diff --git a/.gitignore b/.gitignore index b3bca858c..a3e375bd7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,12 +5,17 @@ *.obj *.a *.o +*.output +*.slc +*.dat +*.sym *~ __pycache__/ autom4te.cache/ .deps/ .libs/ .vimrc +.dirstamp ChangeLog Makefile Makefile.in @@ -35,6 +40,7 @@ core /ltmain.sh /missing /mkinstalldirs +/py-compile /quakeforge-config /quakeforge.lsm /test-driver @@ -104,7 +110,7 @@ core /hw/source/*.d /hw/source/*.i /hw/source/*.s -/hw/source/hw-master +/hw-master # /include/ /include/config.h.in @@ -219,16 +225,9 @@ core /nq/source/fbset_modes_l.c /nq/source/fbset_modes_y.c /nq/source/fbset_modes_y.h -/nq/source/nq-3dfx -/nq/source/nq-fbdev -/nq/source/nq-glslx -/nq/source/nq-glx -/nq/source/nq-sdl -/nq/source/nq-sdl32 -/nq/source/nq-sgl -/nq/source/nq-svga -/nq/source/nq-x11 -/nq/source/nq-server +/nq-sdl +/nq-x11 +/nq-server # /pkg-config/ /pkg-config/*.pc @@ -245,7 +244,7 @@ core /qtv/source/*.d /qtv/source/*.i /qtv/source/*.s -/qtv/source/qtv +/qtv-server # /qw/ @@ -262,17 +261,10 @@ core /qw/source/fbset_modes_l.c /qw/source/fbset_modes_y.c /qw/source/fbset_modes_y.h -/qw/source/qw-client-3dfx -/qw/source/qw-client-fbdev -/qw/source/qw-client-glx -/qw/source/qw-client-glslx -/qw/source/qw-client-sdl -/qw/source/qw-client-sdl32 -/qw/source/qw-client-sgl -/qw/source/qw-client-svga -/qw/source/qw-client-x11 -/qw/source/qw-master -/qw/source/qw-server +/qw-client-sdl +/qw-client-x11 +/qw-master +/qw-server # /ruamoko/ /ruamoko/*.d @@ -281,6 +273,9 @@ core /ruamoko/*.sym /ruamoko/Doxyfile /ruamoko/doxygen/ +/ruamoko/qwaq/qwaq-curses +/ruamoko/qwaq/qwaq-x11 + # /ruamoko/cl_menu/ /ruamoko/cl_menu/*.dat @@ -351,10 +346,10 @@ core /tools/Forge/Bundles/MapEdit/*.app # /tools/bsp2img/ -/tools/bsp2img/bsp2img +/bsp2img # /tools/carne/ -/tools/carne/carne +/carne # /tools/cross/ /tools/cross/binutils-i386-mingw32msvc @@ -370,14 +365,14 @@ core # /tools/pak/ /tools/pak/*.d -/tools/pak/pak +/pak # /tools/qfbsp/ # /tools/qfbsp/include/ # /tools/qfbsp/source/ -/tools/qfbsp/source/qfbsp +/qfbsp # /tools/qfcc/ /tools/qfcc/aclocal.m4 @@ -403,14 +398,11 @@ core /tools/qfcc/source/qc-lex.c /tools/qfcc/source/qc-parse.c /tools/qfcc/source/qc-parse.h -/tools/qfcc/source/qc-parse.output -/tools/qfcc/source/qfcc -/tools/qfcc/source/qfpc -/tools/qfcc/source/qfprogs +/qfcc +/qfprogs /tools/qfcc/source/qp-lex.c /tools/qfcc/source/qp-parse.c /tools/qfcc/source/qp-parse.h -/tools/qfcc/source/qp-parse.output # /tools/qfcc/test/ /tools/qfcc/test/*.dat @@ -421,27 +413,27 @@ core # /tools/qflight/include/ # /tools/qflight/source/ -/tools/qflight/source/qflight +/qflight # /tools/qflmp/ -/tools/qflmp/qflmp +/qflmp # /tools/qfmodelgen/ # /tools/qfmodelgen/include/ # /tools/qfmodelgen/source/ -/tools/qfmodelgen/source/qfmodelgen +/qfmodelgen # /tools/qfspritegen/qfspritegen -/tools/qfspritegen/qfspritegen +/qfspritegen # /tools/qfvis/ # /tools/qfvis/include/ # /tools/qfvis/source/ -/tools/qfvis/source/qfvis +/qfvis # /tools/quaketoascii/ /tools/quaketoascii/quaketoascii @@ -466,8 +458,8 @@ core # /tools/wad/ /tools/wad/*.d -/tools/wad/wad +/wad # /tools/wav/ /tools/wav/*.d -/tools/wav/qfwavinfo +/qfwavinfo diff --git a/INSTALL b/INSTALL index 8b747944a..a2a11be9a 100644 --- a/INSTALL +++ b/INSTALL @@ -20,7 +20,7 @@ necessary packages installed. \section req-pack Required Packages The following packages are required to build QuakeForge: -\li GNU autoconf 2.50 or later +\li GNU autoconf 2.71 or later \li GNU automake 1.6 or later \li GNU libtool 1.4 or later \li GNU bison 2.6 or later @@ -50,14 +50,14 @@ The following is the list of relevant package names in debian. \li git \li libasound2-dev -\li libjack-dev +\li libjack-jackd2-dev \li libsamplerate0-dev \li libflac-dev \li libogg-dev \li libvorbis-dev -\li libpng12-dev +\li libpng-dev \li zlib1g-dev \li libcurl4-openssl-dev (or libcurl4-gnutls-dev) @@ -69,6 +69,11 @@ The following is the list of relevant package names in debian. \li libxxf86dga-dev \li libxxf86vm-dev +\li libvulkan-dev +\li vulkan-tools +\li vulkan-validationlayers +\li glslang-tools + \section building-and-installing Build and Install Procedure Basic process to build and install QuakeForge: \verbatim diff --git a/Makefile.am b/Makefile.am index 1b33ee233..b0f5d2f65 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,15 +1,9 @@ ## Process this file with automake to produce Makefile.in -AUTOMAKE_OPTIONS= foreign ACLOCAL_AMFLAGS = -I m4 -SUBDIRS=desktop pkg-config include @top_dirs@ -DIST_SUBDIRS=desktop pkg-config include \ - libs hw nq qtv qw tools ruamoko\ - RPM debian doc vc2005 vc2008 - -## configure.ac needs to be listed here for older autoconfs -EXTRA_DIST= ChangeLog configure.ac bootstrap \ +EXTRA_DIST = \ + ChangeLog configure.ac bootstrap \ $(top_srcdir)/.version \ config.d/git-version-gen \ tools/cross/droid/cross-configure.sh \ @@ -26,9 +20,161 @@ EXTRA_DIST= ChangeLog configure.ac bootstrap \ tools/gas2masm/gas2masm.dsp tools/gas2masm/gas2masm.dsw \ tools/gas2masm/gas2masm.mak tools/gas2masm/gas2masm.mdp -NOCONV_DIST= $(distdir)/include/win32/resources/icon1Vista.ico \ +NOCONV_DIST= \ + $(distdir)/include/win32/resources/icon1Vista.ico \ $(distdir)/include/win32/resources/icon1XP.ico +BUILT_SOURCES = $(top_srcdir)/.version +#AM_CFLAGS= @PREFER_NON_PIC@ +AM_CPPFLAGS= -I$(top_srcdir)/include $(PTHREAD_CFLAGS) $(FNM_FLAGS) $(NCURSES_CFLAGS) $(FREETYPE_CFLAGS) $(HARFBUZZ_CFLAGS) $(VULKAN_CPPFLAGS) + +common_ldflags= -export-dynamic @STATIC@ @PTHREAD_LDFLAGS@ + +SUFFICES = +TESTS = +XFAIL_TESTS = +bin_PROGRAMS = +bin_SCRIPTS = +check_PROGRAMS = +lib_LTLIBRARIES = +man_MANS = +noinst_LTLIBRARIES = +noinst_LIBRARIES = +noinst_PROGRAMS = +noinst_HEADERS = +noinst_PYTHON = +plugin_LTLIBRARIES = + +RANLIB=touch +ARFLAGS=cr + +EXTRA_HEADERS = +EXTRA_LTLIBRARIES = +EXTRA_PROGRAMS = +EXTRA_LIBRARIES = + +CLEANFILES = +DISTCLEANFILES = + +YFLAGS = -v -d -Wno-yacc -Werror +PTHREAD_LDFLAGS=@PTHREAD_LDFLAGS@ +PTHREAD_CFLAGS=@PTHREAD_CFLAGS@ + +lib_ldflags=-version-info $(QUAKE_LIBRARY_VERSION_INFO) \ + -rpath $(libdir) -no-undefined +plugin_ldflags= @plugin_ldflags@ -avoid-version -module -rpath $(plugindir) +plugin_libadd= @plugin_libadd@ + +SDL_LIBS= @SDL_LIBS@ +XMMS_LIBS= @XMMS_LIBS@ + +PAK=$(top_builddir)/pak$(EXEEXT) +QFCC_DEP=qfcc$(EXEEXT) +QFCC=$(top_builddir)/$(QFCC_DEP) +QWAQ=$(top_builddir)/ruamoko/qwaq/qwaq-cmd$(EXEEXT) + +GZ=@progs_gz@ + +V_QFCC = $(V_QFCC_@AM_V@) +V_QFCC_ = $(V_QFCC_@AM_DEFAULT_V@) +V_QFCC_0 = @echo " QFCC " $@; +V_QFCC_1 = + +V_QFCCLD = $(V_QFCCLD_@AM_V@) +V_QFCCLD_ = $(V_QFCCLD_@AM_DEFAULT_V@) +V_QFCCLD_0 = @echo " QFCCLD " $@; +V_QFCCLD_1 = + +QCSYSTEM=--no-default-paths -I$(top_srcdir) -I$(top_srcdir)/ruamoko/include -I$(top_srcdir)/include +QCFLAGS=-qq -O -g -Werror -Wall -Wno-integer-divide +QCPPFLAGS=$(QCSYSTEM) +QCLINKFLAGS=--no-default-paths -Lruamoko/lib +QCOMPILE=$(QFCC) $(QCFLAGS) $(QCPPFLAGS) +QLINK=$(QFCC) $(QCFLAGS) $(QCLINKFLAGS) +MKDIR_P = @MKDIR_P@ + +am__mv = mv -f + +SUFFIXES=.o .r .pas +.r.o: + $(V_QFCC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ + $(QCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tqo -c -o $@ $< &&\ + sed -i -e '1s@:@: $(QFCC_DEP)@' $$depbase.Tqo &&\ + $(am__mv) $$depbase.Tqo $$depbase.Qo +.pas.o: + $(V_QFCC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ + $(QCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tqo -c -o $@ $< &&\ + sed -i -e '1s@:@: $(QFCC_DEP)@' $$depbase.Tqo &&\ + $(am__mv) $$depbase.Tqo $$depbase.Qo + +qcautodep = $(join $(addsuffix $(DEPDIR)/,$(dir $(basename $(1)))),$(addsuffix .Qo,$(notdir $(basename $(basename $(1)))))) +r_depfiles_remade= +pas_depfiles_remade= + +V_PY = $(V_PY_@AM_V@) +V_PY_ = $(V_PY_@AM_DEFAULT_V@) +V_PY_0 = @echo " PY " $@; +V_PY_1 = + +V_GLSLANG = $(V_GLSLANG_@AM_V@) +V_GLSLANG_ = $(V_GLSLANG_@AM_DEFAULT_V@) +V_GLSLANG_0 = @echo " GLSLANG " $@; +V_GLSLANG_1 = + +V_XXD = $(V_XXD_@AM_V@) +V_XXD_ = $(V_XXD_@AM_DEFAULT_V@) +V_XXD_0 = @echo " XXD " $@; +V_XXD_1 = + +V_SED = $(V_SED_@AM_V@) +V_SED_ = $(V_SED_@AM_DEFAULT_V@) +V_SED_0 = @echo " SED " $@; +V_SED_1 = + +SUFFICES += .plist .plc +.plist.plc: + $(V_SED)sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/\\n"/' $< > $@.t &&\ + $(am__mv) $@.t $@ + +%.spv: % + @$(mkdir_p) $(builddir)/`dirname $@` + $(V_GLSLANG)(((($(GLSLANGVALIDATOR) -V $< -o $@; echo $$? >&3) | sed -e '1d' 1>&2) 3>&1) | (read xs; exit $$xs)) + +%.spvc: % + @$(mkdir_p) $(builddir)/`dirname $@` + $(V_GLSLANG)(((($(GLSLANGVALIDATOR) --vn `basename $< | tr . _` -V $< -o $@; echo $$? >&3) | sed -e '1d' 1>&2) 3>&1) | (read xs; exit $$xs)) + +shaderdir = @shaderdir@ +shader_DATA = + +include doc/Makemodule.am +include RPM/Makemodule.am +include debian/Makemodule.am +include desktop/Makemodule.am +include pkg-config/Makemodule.am +include include/Makemodule.am +include libs/Makemodule.am +include hw/Makemodule.am +include nq/Makemodule.am +include qtv/Makemodule.am +include qw/Makemodule.am +include tools/Makemodule.am +include ruamoko/Makemodule.am + +DISTCLEANFILES += $(r_depfiles_remade) $(pas_depfiles_remade) +CLEANFILES += $(shader_DATA) + +$(r_depfiles_remade): + $(MKDIR_P) $(@D) + echo '$@' | sed -e 's@\$(DEPDIR)/@@' -e 's@\(.*\)\.Qo$$@\1.o: $(top_srcdir)/\1.r $(QFCC)@' >$@-t && $(am__mv) $@-t $@ + +$(pas_depfiles_remade): + $(MKDIR_P) $(@D) + echo '$@' | sed -e 's@\$(DEPDIR)/@@' -e 's@\(.*\)\.Qo$$@\1.o: $(top_srcdir)/\1.pas $(QFCC)@' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) $(r_depfiles_remade) $(pas_depfiles_remade) + echo findme $(ruamoko_gui_libgui_a_dep) + changelog: ChangeLog ChangeLog: FORCE @if test -d "$(top_srcdir)/.git"; then \ @@ -63,7 +209,6 @@ dist-all-local: distdir ZIP="-r9ql" zip $(distdir).zip $(distdir) -rm -rf $(distdir) -BUILT_SOURCES = $(top_srcdir)/.version $(top_srcdir)/.version: echo $(VERSION) > $@-t && mv $@-t $@ dist-hook: diff --git a/README.cygwin b/README.cygwin deleted file mode 100644 index 146988522..000000000 --- a/README.cygwin +++ /dev/null @@ -1,40 +0,0 @@ -This file gives instruction for compiling QF with cygwin to run in MS Windows(R). - -DISCLAIMER: Use at your own risk, NO WARRANTY of any kind. NO GUARANTEE of correctness or usability. - -If you have problems with these instructions please let me know. - -email: wildcode@users.sourceforge.net -IRC: irc.xiph.org #quakeforge -========================================================================== - -WARNING: This is not intended for inexperienced users, binaries are available on request. - - QuakeForge may take several hours from bootstrap to the completion - of make install on some systems - -To build win32(mingw) QuakeForge bins you will need to download and install cygwin, and the Microsoft DirectX SDK. Cygwin can be found at http://www.cygwin.org - -Once cygwin is installed along with the appropriate developement modules, copy dinput.h from the DirectX SDK and place it in /usr/include/w32api/ - -If you want to use DirectSound also copy dsound.h to the same spot. - -in the quakeforge source dir, within cygwin, type -./bootstrap -./configure --host=i386-mingw32 --build=i386-cygwin --target=i386-mingw32 --program-prefix= --with-static-plugins --disable-shared --disable-oss --disable-vorbis --disable-Werror --enable-optimize --enable-asmopt --disable-debug --enable-zlib -make -make install - ---disable-Werror is needed as some versions of the DX SDK are buggy. - -Go to the /usr/local/bin directory and copy the qw-* and nq-* files to your dos -quake dir - -Let us know how it works for you. - -NOTE: At the time of writing qw-server.exe was not working correctly. Please refrain from using it unless you are willing to fix it. - -Special Note: QuakeForge supplies only our own version of the programs for Quake. To get the game data, you still need to have purchased Quake or get the shareware version from Id Software. - -Chris Ison (WildCode) for QuakeForge -March 11, 2003 diff --git a/README.md b/README.md new file mode 100644 index 000000000..3f2bc295d --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# QuakeForge + +QuakeForge is descended from the original Quake engine as released by Id +Software in December 1999, and can be used to play original Quake and +QuakeWorld games and mods (including many modern mods). While this will +always be the case, development continues. + +However, QuakeForge is not just a Quake engine, but includes a +collection of tools for creating Quake mods, and is progressing towards +being a more general game engine. + +## Quake and QuakeWorld + +Support for Quake and QuakeWorld is split into two program sets: nq for +Quake and qw-client for QuakeWorld, with the target system as an +additional suffix: -x11 For the X Window system (Linux, BSD, etc), -win +for MS Windows (plus others that are not currently maintained). + +Both nq and qw-client support multiple renderers: 8-bit software, 32-bit +software, OpenGL 2, EGL (mostly, one non-EGL function is used), and +Vulkan (very WIP), all within the one executable. + +Dedicated servers for both Quake (nq-server) and QuakeWorld (qw-server) +are included, as well as a master server for QuakeWorld (qw-master). + +## Tools + +QuakeForge includes several tools for working with Quake data: +- bsp2image produces wireframe images from Quake maps (bsp files) +- io_mesh_qfmdl for importing and exporting Quake mdl files to/from + Blender +- io_qfmap for Quake map source files (WIP Blender addon) +- pak create, list and extract Quake pak files. There's also zpak which + can be used to compress the contents of pak files using gzip + (QuakeForge has transparent support for gzip compressed files) +- qfbsp for compiling map files to bsp files, includes support for + vis clusters, and can be used to extract data and information from bsp + files. +- qfcc is QuakeForge's version of qcc, but is significantly more + advanced, with support for standard C syntax, including most C types, + as well as Objective-C object oriented programming (Ruamoko). Most of + the advanced features require the QuakeForge engine, but qfcc can + produce progs files compatible with the original Quake engine with + limited support for some of the advanced featuers (C syntax, reduced + global usage, some additional operators (eg, better bit operators, + remainder (%)). Includes qfprogs for inspecting progs files. +- qflight creates lightmaps for Quake maps +- qfvis for compiling PVS data for Quake maps. One of the faster + implementations available. +- Plus a few others in various stages of usefulness: qflmp, qfmodelgen, + qfspritegen, wad, qfwavinfo + +## Building + +For now, please refer to INSTALL for information on building on Linux. +Building for windows is done by cross-compiling using MXE. There are +scripts in tools/mingw and tools/mingw64 that automate the process of +configuring and building both the tools run on the build-host and the +windows targets. diff --git a/RPM/Makefile.am b/RPM/Makefile.am deleted file mode 100644 index 13616e183..000000000 --- a/RPM/Makefile.am +++ /dev/null @@ -1,9 +0,0 @@ -## Process this file with automake to produce Makefile.in -AUTOMAKE_OPTIONS= foreign - -EXTRA_DIST= build_rpm.in quakeforge.conf.in quakeforge.spec.in - -rpm: build_rpm quakeforge.conf quakeforge.spec - ./build_rpm - -CLEANFILES = *.rpm diff --git a/RPM/Makemodule.am b/RPM/Makemodule.am new file mode 100644 index 000000000..583cc8506 --- /dev/null +++ b/RPM/Makemodule.am @@ -0,0 +1,6 @@ +EXTRA_DIST += RPM/build_rpm.in RPM/quakeforge.conf.in RPM/quakeforge.spec.in + +rpm: RPM/build_rpm RPM/quakeforge.conf RPM/quakeforge.spec + cd RPM && ./build_rpm + +CLEANFILES += rpm/*.rpm diff --git a/bootstrap b/bootstrap index 4986a6ca4..902b8a69d 100755 --- a/bootstrap +++ b/bootstrap @@ -18,7 +18,7 @@ if test "$1" = "clean"; then find . -name '*.orig' -type f -print0 | xargs $ARGS rm -f rm -f aclocal.m4 build-stamp changelog-stamp config.cache config.log \ config.status configure configure-stamp install-sh libtool missing \ - mkinstalldirs quakeforge-config quakeforge.lsm + mkinstalldirs quakeforge-config quakeforge.lsm test-driver rm -f compile config.guess config.sub depcomp ltmain.sh ylwrap rm -rf autom4te.cache rm -f m4/libtool.m4 m4/ltoptions.m4 m4/ltsugar.m4 m4/ltversion.m4 \ @@ -44,7 +44,7 @@ if test x`uname` = xDarwin ; then else libtoolize=libtoolize fi -lt=`which $libtoolize` +lt=`command -v $libtoolize --version` if test -n "$lt" ; then if test -x "$lt" ; then LTIZE_VER=`$libtoolize --version | head -1 | sed 's/.* \([1-9][0-9]*\(\.[1-9][0-9]*\)\(\.[1-9][0-9]*\)*\).*/\1/'` @@ -63,7 +63,7 @@ else fi # Check Autoconf version -ac=`which autoconf` +ac=`command -v autoconf` if test -n "$ac" ; then if test -x "$ac" ; then AC_VER=`autoconf --version | head -1 | sed 's/^[^0-9]*//'` @@ -71,18 +71,18 @@ if test -n "$ac" ; then AC_VER_MINOR=`echo $AC_VER | cut -f2 -d'.' | sed 's/[^0-9]*$//'` if test "$AC_VER_MAJOR" -lt "2" ; then - errors="Autoconf 2.61 or greater needed to build configure.\n$errors" + errors="Autoconf 2.69 or greater needed to build configure.\n$errors" fi - if test "$AC_VER_MAJOR" -eq "2" -a "$AC_VER_MINOR" -lt "61" ; then - errors="Autoconf 2.61 or greater needed to build configure.\n$errors" + if test "$AC_VER_MAJOR" -eq "2" -a "$AC_VER_MINOR" -lt "69" ; then + errors="Autoconf 2.69 or greater needed to build configure.\n$errors" fi fi else errors="Autoconf not found. QuakeForge git requires autoconf to bootstrap itself.\n$errors" fi -am=`which automake` +am=`command -v automake` if test -n "$am" ; then if test -x "$am" ; then AM_VER=`automake --version | head -1 | sed -e 's/automake (GNU automake) //' -e 's/\-p.*$//'` diff --git a/config.d/ac_config_files.m4 b/config.d/ac_config_files.m4 index 587a515d9..d84505483 100644 --- a/config.d/ac_config_files.m4 +++ b/config.d/ac_config_files.m4 @@ -1,103 +1,4 @@ Makefile - - include/Makefile - include/QF/Makefile - - libs/Makefile - libs/audio/Makefile - libs/audio/targets/Makefile - libs/audio/renderer/Makefile - libs/audio/test/Makefile - libs/console/Makefile - libs/client/Makefile - libs/gamecode/Makefile - libs/gib/Makefile - libs/image/Makefile - libs/models/Makefile - libs/models/alias/Makefile - libs/models/brush/Makefile - libs/models/iqm/Makefile - libs/models/sprite/Makefile - libs/models/test/Makefile - libs/net/Makefile - libs/net/nc/Makefile - libs/net/nm/Makefile - libs/qw/Makefile - libs/ruamoko/Makefile - libs/util/Makefile - libs/util/test/Makefile - libs/video/Makefile - libs/video/renderer/Makefile - libs/video/renderer/gl/Makefile - libs/video/renderer/glsl/Makefile - libs/video/renderer/sw/Makefile - libs/video/renderer/sw32/Makefile - libs/video/targets/Makefile - - hw/Makefile - hw/include/Makefile - hw/source/Makefile - - nq/Makefile - nq/include/Makefile - nq/source/Makefile - - qtv/Makefile - qtv/include/Makefile - qtv/source/Makefile - - qw/Makefile - qw/include/Makefile - qw/source/Makefile - - tools/Makefile - tools/bsp2img/Makefile - tools/carne/Makefile - tools/pak/Makefile - tools/qfbsp/Makefile - tools/qfbsp/include/Makefile - tools/qfbsp/source/Makefile - tools/qfcc/Makefile - tools/qfcc/doc/Makefile - tools/qfcc/doc/man/Makefile - tools/qfcc/include/Makefile - tools/qfcc/source/Makefile - tools/qfcc/test/Makefile - tools/qflight/Makefile - tools/qflight/include/Makefile - tools/qflight/source/Makefile - tools/qflmp/Makefile - tools/qfmodelgen/Makefile - tools/qfmodelgen/include/Makefile - tools/qfmodelgen/source/Makefile - tools/qfspritegen/Makefile - tools/qfvis/Makefile - tools/qfvis/include/Makefile - tools/qfvis/source/Makefile - tools/qwaq/Makefile - tools/qwaq/progs.src - tools/wad/Makefile - tools/wav/Makefile - - ruamoko/Makefile - ruamoko/Doxyfile - ruamoko/include/Makefile - ruamoko/lib/Makefile - ruamoko/game/Makefile - ruamoko/gui/Makefile - ruamoko/cl_menu/Makefile - ruamoko/scheme/Makefile - - pkg-config/Makefile pkg-config/qfcc.pc pkg-config/quakeforge.pc - - doc/Makefile doc/quakeforge.dox.conf - doc/man/Makefile - - debian/Makefile - desktop/Makefile - - vc2005/Makefile - vc2008/Makefile diff --git a/config.d/asm.m4 b/config.d/asm.m4 index 79e5ceea8..2a9e1d134 100644 --- a/config.d/asm.m4 +++ b/config.d/asm.m4 @@ -5,9 +5,11 @@ case "${host}" in AC_MSG_RESULT(yes) AC_MSG_CHECKING(to see if we should disable asm optimizations) AC_ARG_ENABLE(asmopt, - [ --disable-asmopt disable assembler optimization], + AS_HELP_STRING([--disable-asmopt], + [disable assembler optimization]), AC_MSG_RESULT(yes), - AC_DEFINE(USE_INTEL_ASM, 1, [Define this if you want to use Intel assembly optimizations]) + AC_DEFINE(USE_INTEL_ASM, 1, + [Define this if you want to use Intel assembly optimizations]) ASM_ARCH=yes AC_MSG_RESULT(no) ) @@ -17,10 +19,10 @@ esac AM_CONDITIONAL(ASM_ARCH, test "x$ASM_ARCH" = "xyes") AC_MSG_CHECKING(for underscore prefix in names) -AC_TRY_LINK( - [asm(".long _bar"); - int bar;], - [], - AC_DEFINE(HAVE_SYM_PREFIX_UNDERSCORE, 1, [Define this if C symbols are prefixed with an underscore]) AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) -) +AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[asm(".long _bar"); int bar;]], [[]])], + [AC_DEFINE(HAVE_SYM_PREFIX_UNDERSCORE, 1, + [Define this if C symbols are prefixed with an underscore]) + AC_MSG_RESULT(yes)], + [AC_MSG_RESULT(no) +]) diff --git a/config.d/build_control.m4 b/config.d/build_control.m4 index abc60cd75..db8efb6e8 100644 --- a/config.d/build_control.m4 +++ b/config.d/build_control.m4 @@ -4,17 +4,17 @@ dnl ================================================================== QF_WITH_TARGETS( clients, - [ --with-clients= compile clients in :], - [fbdev,sdl,svga,x11],dummy + [compile clients], + [fbdev,sdl,svga,win,x11],dummy ) QF_WITH_TARGETS( servers, - [ --with-servers= compile dedicated server:], + [compile dedicated servers], [master,nq,qw,qtv],dummy ) QF_WITH_TARGETS( tools, - [ --with-tools= compile qf tools:], + [compile qf tools], [bsp2img,carne,gsc,pak,qfbsp,qfcc,qflight,qflmp,qfmodelgen,qfspritegen,qfvis,qwaq,wad,wav],dummy ) @@ -27,6 +27,20 @@ QWAQ_TARGETS="" QW_DESKTOP_DATA="" NQ_DESKTOP_DATA="" +BSP2IMG_TARGETS="" +CARNE_TARGETS="" +PAK_TARGETS="" +QFBSP_TARGETS="" +QFCC_TARGETS="" +QFLIGHT_TARGETS="" +QFLMP_TARGETS="" +QFMODELGEN_TARGETS="" +QFSPRITEGEN_TARGETS="" +QFVIS_TARGETS="" +WAD_TARGETS="" +WAV_TARGETS="" +VKGEN_TARGETS="" + CD_TARGETS="" SND_TARGETS="" VID_MODEL_TARGETS="" @@ -39,8 +53,9 @@ if test "x$HAVE_FBDEV" = xyes; then QW_TARGETS="$QW_TARGETS qw-client-fbdev\$(EXEEXT)" NQ_TARGETS="$NQ_TARGETS nq-fbdev\$(EXEEXT)" CL_TARGETS="$CL_TARGETS FBDEV" - VID_TARGETS="$VID_TARGETS libQFfbdev.la" + VID_TARGETS="$VID_TARGETS libs/video/targets/libQFfbdev.la" QF_NEED(vid_render, [sw]) + QF_NEED(render, [sw]) QF_NEED(models, [sw]) QF_NEED(alias, [sw]) QF_NEED(brush, [sw]) @@ -66,12 +81,22 @@ if test "x$HAVE_X" = xyes; then if test "x$ENABLE_clients_x11" = xyes; then QW_TARGETS="$QW_TARGETS qw-client-x11\$(EXEEXT)" NQ_TARGETS="$NQ_TARGETS nq-x11\$(EXEEXT)" - QWAQ_TARGETS="$QWAQ_TARGETS qwaq-x11\$(EXEEXT)" - QW_DESKTOP_DATA="$QW_DESKTOP_DATA quakeforge-qw-x11.desktop" - NQ_DESKTOP_DATA="$NQ_DESKTOP_DATA quakeforge-nq-x11.desktop" + QWAQ_TARGETS="$QWAQ_TARGETS ruamoko/qwaq/qwaq-x11\$(EXEEXT)" + QW_DESKTOP_DATA="$QW_DESKTOP_DATA desktop/quakeforge-qw-x11.desktop" + NQ_DESKTOP_DATA="$NQ_DESKTOP_DATA desktop/quakeforge-nq-x11.desktop" CL_TARGETS="$CL_TARGETS X11" - VID_TARGETS="$VID_TARGETS libQFx11.la" - QF_NEED(vid_render, [sw sw32 gl glsl]) + VID_TARGETS="$VID_TARGETS libs/video/targets/libQFx11.la" + if test "$HAVE_VULKAN" = "yes"; then + QF_NEED(vid_render, [vulkan]) + QF_NEED(render, [vulkan]) + QF_NEED(models, [vulkan]) + QF_NEED(alias, [vulkan]) + QF_NEED(brush, [vulkan]) + QF_NEED(iqm, [vulkan]) + QF_NEED(sprite, [vulkan]) + fi + QF_NEED(vid_render, [sw gl glsl]) + QF_NEED(render, [sw gl glsl]) QF_NEED(models, [sw gl glsl]) QF_NEED(alias, [sw gl glsl]) QF_NEED(brush, [sw gl glsl]) @@ -91,11 +116,12 @@ if test "x$HAVE_SDL" = xyes; then if test "x$ENABLE_clients_sdl" = xyes; then QW_TARGETS="$QW_TARGETS qw-client-sdl\$(EXEEXT)" NQ_TARGETS="$NQ_TARGETS nq-sdl\$(EXEEXT)" - QW_DESKTOP_DATA="$QW_DESKTOP_DATA quakeforge-qw-sdl.desktop" - NQ_DESKTOP_DATA="$NQ_DESKTOP_DATA quakeforge-nq-sdl.desktop" + QW_DESKTOP_DATA="$QW_DESKTOP_DATA desktop/quakeforge-qw-sdl.desktop" + NQ_DESKTOP_DATA="$NQ_DESKTOP_DATA desktop/quakeforge-nq-sdl.desktop" CL_TARGETS="$CL_TARGETS SDL" - VID_TARGETS="$VID_TARGETS libQFsdl.la" - QF_NEED(vid_render, [sw sw32 gl glsl]) + VID_TARGETS="$VID_TARGETS libs/video/targets/libQFsdl.la" + QF_NEED(vid_render, [sw gl glsl]) + QF_NEED(render, [sw gl glsl]) QF_NEED(models, [sw gl glsl]) QF_NEED(alias, [sw gl glsl]) QF_NEED(brush, [sw gl glsl]) @@ -116,8 +142,9 @@ if test "x$HAVE_SVGA" = xyes; then QW_TARGETS="$QW_TARGETS qw-client-svga\$(EXEEXT)" NQ_TARGETS="$NQ_TARGETS nq-svga\$(EXEEXT)" CL_TARGETS="$CL_TARGETS SVGAlib" - VID_TARGETS="$VID_TARGETS libQFsvga.la" + VID_TARGETS="$VID_TARGETS libs/video/targets/libQFsvga.la" QF_NEED(vid_render, [sw]) + QF_NEED(render, [sw]) QF_NEED(models, [sw]) QF_NEED(alias, [sw]) QF_NEED(brush, [sw]) @@ -133,25 +160,37 @@ if test "x$HAVE_SVGA" = xyes; then QF_NEED(libs,[util gamecode ruamoko gib audio image models video console net qw client]) fi fi -#if test "x$mingw" = xyes; then -# if test "x$ENABLE_clients_wgl" = xyes; then -# QW_TARGETS="$QW_TARGETS qw-client-wgl\$(EXEEXT)" -# NQ_TARGETS="$NQ_TARGETS nq-wgl\$(EXEEXT)" -# CL_TARGETS="$CL_TARGETS WGL" -# VID_TARGETS="$VID_TARGETS libQFwgl.la" -# QF_NEED(vid_render, [gl]) -# QF_NEED(models, [gl]) -# QF_NEED(alias, [gl]) -# QF_NEED(brush, [gl]) -# QF_NEED(iqm, [gl]) -# QF_NEED(sprite, [gl]) -# QF_NEED(vid, [common]) -# QF_NEED(qw, [client common]) -# QF_NEED(nq, [client common]) -# QF_NEED(console, [client]) -# QF_NEED(libs,[util gamecode ruamoko gib audio image models video console net qw client]) -# fi -#fi +if test "x$mingw" = xyes; then + if test "x$ENABLE_clients_win" = xyes; then + QW_TARGETS="$QW_TARGETS qw-client-win\$(EXEEXT)" + NQ_TARGETS="$NQ_TARGETS nq-win\$(EXEEXT)" + CL_TARGETS="$CL_TARGETS WIN" + VID_TARGETS="$VID_TARGETS libs/video/targets/libQFwin.la" + if test "$HAVE_VULKAN" = "yes"; then + QF_NEED(vid_render, [vulkan]) + QF_NEED(render, [vulkan]) + QF_NEED(models, [vulkan]) + QF_NEED(alias, [vulkan]) + QF_NEED(brush, [vulkan]) + QF_NEED(iqm, [vulkan]) + QF_NEED(sprite, [vulkan]) + fi + QF_NEED(vid_render, [sw gl glsl]) + QF_NEED(models, [sw gl glsl]) + QF_NEED(alias, [sw gl glsl]) + QF_NEED(brush, [sw gl glsl]) + QF_NEED(iqm, [sw gl glsl]) + QF_NEED(sprite, [sw gl glsl]) + if test "x$ASM_ARCH" = "xyes"; then + QF_NEED(swrend, [asm]) + fi + QF_NEED(vid, [common win]) + QF_NEED(qw, [client common]) + QF_NEED(nq, [client common]) + QF_NEED(console, [client]) + QF_NEED(libs,[util gamecode ruamoko gib audio image models video console net qw client]) + fi +fi unset SV_TARGETS if test "x$ENABLE_servers_nq" = xyes; then @@ -163,8 +202,8 @@ if test "x$ENABLE_servers_nq" = xyes; then QF_NEED(libs,[util gamecode ruamoko gib image models console net]) fi if test "x$ENABLE_servers_qtv" = xyes; then - QTV_TARGETS="qtv\$(EXEEXT) $QTV_TARGETS" - SV_TARGETS="$SV_TARGETS qtv" + QTV_TARGETS="qtv-server\$(EXEEXT) $QTV_TARGETS" + SV_TARGETS="$SV_TARGETS qtv-server" # QF_NEED(qtv, [common server]) QF_NEED(console, [server]) QF_NEED(top, [qtv]) @@ -187,98 +226,157 @@ if test "x$ENABLE_servers_qw" = xyes; then fi if test "x$ENABLE_tools_bsp2img" = xyes; then + BSP2IMG_TARGETS="bsp2img\$(EXEEXT)" QF_NEED(tools,[bsp2img]) QF_NEED(libs,[image util]) fi if test "x$ENABLE_tools_carne" = xyes; then + CARNE_TARGETS="carne\$(EXEEXT)" QF_NEED(tools,[carne]) QF_NEED(libs,[gib ruamoko gamecode util]) fi if test "x$ENABLE_tools_pak" = xyes; then + PAK_TARGETS="pak\$(EXEEXT)" QF_NEED(tools,[pak]) QF_NEED(libs,[util]) fi if test "x$ENABLE_tools_qfbsp" = xyes; then + QFBSP_TARGETS="qfbsp\$(EXEEXT)" QF_NEED(tools,[qfbsp]) QF_NEED(libs,[models image util]) fi if test "x$ENABLE_tools_qfcc" = xyes; then + QFCC_TARGETS="qfcc\$(EXEEXT) qfprogs\$(EXEEXT)" QF_NEED(tools,[qfcc]) QF_NEED(libs,[gamecode util]) fi if test "x$ENABLE_tools_qflight" = xyes; then + QFLIGHT_TARGETS="qflight\$(EXEEXT)" QF_NEED(tools,[qflight]) QF_NEED(libs,[util]) fi if test "x$ENABLE_tools_qflmp" = xyes; then + QFLMP_TARGETS="qflmp\$(EXEEXT)" QF_NEED(tools,[qflmp]) QF_NEED(libs,[util]) fi if test "x$ENABLE_tools_qfmodelgen" = xyes; then + QFMODELGEN_TARGETS="qfmodelgen\$(EXEEXT)" QF_NEED(tools,[qfmodelgen]) QF_NEED(libs,[util]) fi if test "x$ENABLE_tools_qfspritegen" = xyes; then + QFSPRITEGEN_TARGETS="qfspritegen\$(EXEEXT)" QF_NEED(tools,[qfspritegen]) QF_NEED(libs,[util]) fi if test "x$ENABLE_tools_qfvis" = xyes; then + QFVIS_TARGETS="qfvis\$(EXEEXT)" QF_NEED(tools,[qfvis]) QF_NEED(libs,[util]) fi if test "x$ENABLE_tools_qwaq" = xyes; then - QF_NEED(tools,[qwaq]) + if test "x$HAVE_NCURSES" == "xyes" -a "x$HAVE_PANEL" = xyes -a "x$HAVE_PTHREAD" = xyes; then + QWAQ_TARGETS="$QWAQ_TARGETS ruamoko/qwaq/qwaq-curses\$(EXEEXT)" + dnl FIXME move key code (maybe to ui?) + QF_NEED(vid, [common]) + fi + if test "x$HAVE_PTHREAD" = xyes; then + QWAQ_TARGETS="$QWAQ_TARGETS ruamoko/qwaq/qwaq-cmd\$(EXEEXT)" + fi + QF_NEED(tools,[qfcc]) + QF_NEED(ruamoko,[qwaq]) QF_NEED(libs,[ruamoko gamecode util]) fi if test "x$ENABLE_tools_wad" = xyes; then + WAD_TARGETS="wad\$(EXEEXT)" QF_NEED(tools,[wad]) QF_NEED(libs,[image util]) fi if test "x$ENABLE_tools_wav" = xyes; then + WAV_TARGETS="qfwavinfo\$(EXEEXT)" QF_NEED(tools,[wav]) QF_NEED(libs,[util]) fi +if test "x$render_need_vulkan" = xyes; then + VKGEN_TARGETS="vkgen.dat\$(EXEEXT)" + QF_NEED(tools,[qfcc pak qwaq]) +fi QF_NEED(top, [libs hw nq qtv qw]) -QF_PROCESS_NEED_DIRS(tools,[bsp2img carne pak qfbsp qfcc qflight qflmp qfmodelgen qfspritegen qfvis qwaq wad wav]) -QF_PROCESS_NEED_FUNC(tools,[bsp2img carne pak qfbsp qfcc qflight qflmp qfmodelgen qfspritegen qfvis qwaq wad wav], QF_NEED(top,tools)) +QF_PROCESS_NEED_LIST(tools,[bsp2img carne pak qfbsp qfcc qflight qflmp qfmodelgen qfspritegen qfvis wad wav]) +QF_PROCESS_NEED_FUNC(tools,[bsp2img carne pak qfbsp qfcc qflight qflmp qfmodelgen qfspritegen qfvis wad wav], QF_NEED(top,tools)) -QF_PROCESS_NEED_DIRS(libs,[util gamecode ruamoko gib audio image models video console net qw client]) +QF_PROCESS_NEED_LIST(libs,[util gamecode ruamoko gib audio image models video console net qw client]) + +QF_PROCESS_NEED_LIST(ruamoko,[qwaq]) if test "$ENABLE_tools_qfcc" = "yes" -a "$ENABLE_tools_pak" = "yes"; then QF_NEED(top, [ruamoko]) + qfac_qfcc_include_qf="\$(qfcc_include_qf)" + qfac_qfcc_include_qf_input="\$(qfcc_include_qf_input)" + qfac_qfcc_include_qf_progs="\$(qfcc_include_qf_progs)" fi +QF_SUBST(qfac_qfcc_include_qf) +QF_SUBST(qfac_qfcc_include_qf_input) +QF_SUBST(qfac_qfcc_include_qf_progs) + +if test x"${top_need_libs}" = xyes; then + qfac_include_qf="\$(include_qf)" + qfac_include_qf_gl="\$(include_qf_gl)" + qfac_include_qf_glsl="\$(include_qf_glsl)" + qfac_include_qf_input="\$(include_qf_input)" + qfac_include_qf_math="\$(include_qf_math)" + qfac_include_qf_plugin="\$(include_qf_plugin)" + qfac_include_qf_progs="\$(include_qf_progs)" + qfac_include_qf_scene="\$(include_qf_scene)" + qfac_include_qf_simd="\$(include_qf_simd)" + qfac_include_qf_ui="\$(include_qf_ui)" + qfac_include_qf_vulkan="\$(include_qf_vulkan)" +fi +QF_SUBST(qfac_include_qf) +QF_SUBST(qfac_include_qf_gl) +QF_SUBST(qfac_include_qf_glsl) +QF_SUBST(qfac_include_qf_input) +QF_SUBST(qfac_include_qf_math) +QF_SUBST(qfac_include_qf_plugin) +QF_SUBST(qfac_include_qf_progs) +QF_SUBST(qfac_include_qf_scene) +QF_SUBST(qfac_include_qf_simd) +QF_SUBST(qfac_include_qf_ui) +QF_SUBST(qfac_include_qf_vulkan) + progs_gz= if test "$HAVE_ZLIB" = "yes"; then progs_gz=".gz" fi QF_SUBST(progs_gz) -QF_PROCESS_NEED_DIRS(top, [libs hw nq qtv qw tools ruamoko]) +QF_PROCESS_NEED_LIST(top, [libs hw nq qtv qw tools ruamoko]) QF_PROCESS_NEED_LIBS(swrend, [asm]) -QF_PROCESS_NEED_DIRS(vid_render, [gl glsl sw sw32]) -QF_PROCESS_NEED_LIBS(models, [gl glsl sw]) -QF_PROCESS_NEED_LIBS(alias, [gl glsl sw]) -QF_PROCESS_NEED_LIBS(brush, [gl glsl sw]) -QF_PROCESS_NEED_LIBS(iqm, [gl glsl sw]) -QF_PROCESS_NEED_LIBS(sprite, [gl glsl sw]) +QF_PROCESS_NEED_LIBS(render, [gl glsl sw vulkan], [libs/video/renderer]) +QF_PROCESS_NEED_LIST(vid_render, [gl glsl sw vulkan]) +QF_PROCESS_NEED_LIBS(models, [gl glsl sw vulkan], [libs/models]) +QF_PROCESS_NEED_LIBS(alias, [gl glsl sw vulkan], [libs/models/alias]) +QF_PROCESS_NEED_LIBS(brush, [gl glsl sw vulkan], [libs/models/brush]) +QF_PROCESS_NEED_LIBS(iqm, [gl glsl sw vulkan], [libs/models/iqm]) +QF_PROCESS_NEED_LIBS(sprite, [gl glsl sw vulkan], [libs/models/sprite]) -QF_PROCESS_NEED_LIBS(vid, [common sdl svga x11]) -QF_PROCESS_NEED_LIBS(qw, [client common sdl server], a) -QF_PROCESS_NEED_LIBS(nq, [client common sdl server], a) +QF_PROCESS_NEED_LIBS(input, [evdev], [libs/input]) +QF_PROCESS_NEED_LIBS(vid, [common sdl svga win x11], [libs/video/targets]) +QF_PROCESS_NEED_LIBS(qw, [client common sdl win server], [qw/source], a) +QF_PROCESS_NEED_LIBS(nq, [client common sdl win server], [nq/source], a) if test -n "$CL_TARGETS"; then - CD_TARGETS="libQFcd.la" - SND_TARGETS="libQFsound.la" - AUDIO_TARGETS="testsound\$(EXEEXT)" - JOY_TARGETS="libQFjs.la" + CD_TARGETS="libs/audio/libQFcd.la" + SND_TARGETS="libs/audio/libQFsound.la" + AUDIO_TARGETS="libs/audio/test/testsound\$(EXEEXT)" else unset CDTYPE unset SOUND_TYPES CD_TARGETS="" - JOY_TARGETS="" SND_TARGETS="" AUDIO_TARGETS="" fi @@ -300,7 +398,8 @@ QF_SUBST(PREFER_NON_PIC) QF_SUBST(STATIC) AC_ARG_WITH(static-plugins, -[ --with-static-plugins build plugins into executable rather than separate], + AS_HELP_STRING([--with-static-plugins], + [build plugins into executable rather than separate]), static_plugins="$withval", static_plugins=auto) if test "x$static_plugins" = xauto; then if test "x$enable_shared" = xno -o "x$SYSTYPE" = xWIN32; then @@ -308,13 +407,13 @@ if test "x$static_plugins" = xauto; then fi fi if test "x$static_plugins" = xyes; then - QF_PROCESS_NEED_STATIC_PLUGINS(vid_render, [sw sw32 glsl gl]) - QF_PROCESS_NEED_STATIC_PLUGINS(console, [server], [\$(top_builddir)/libs/console], [server]) - QF_PROCESS_NEED_STATIC_PLUGINS(console, [client], [\$(top_builddir)/libs/console], [client]) + QF_PROCESS_NEED_STATIC_PLUGINS(vid_render, [sw glsl gl vulkan], [libs/video/renderer]) + QF_PROCESS_NEED_STATIC_PLUGINS(console, [server], [libs/console], [server]) + QF_PROCESS_NEED_STATIC_PLUGINS(console, [client], [libs/console], [client]) - QF_PROCESS_NEED_STATIC_PLUGINS(snd_output, [sdl mme sgi sun win dx oss alsa], [targets]) - QF_PROCESS_NEED_STATIC_PLUGINS(snd_render, [jack default], [renderer]) - QF_PROCESS_NEED_STATIC_PLUGINS(cd, [xmms sdl sgi win linux file]) + QF_PROCESS_NEED_STATIC_PLUGINS(snd_output, [sdl mme sgi sun win dx oss jack alsa], [libs/audio/targets]) + QF_PROCESS_NEED_STATIC_PLUGINS(snd_render, [default], [libs/audio/renderer]) + QF_PROCESS_NEED_STATIC_PLUGINS(cd, [xmms sdl sgi win linux file], [libs/audio]) AC_DEFINE(STATIC_PLUGINS, 1, [Define this if you are building static plugins]) if test -n "$SOUND_TYPES"; then SOUND_TYPES="$SOUND_TYPES (static)" @@ -323,12 +422,12 @@ if test "x$static_plugins" = xyes; then CDTYPE="$CDTYPE (static)" fi else - QF_PROCESS_NEED_PLUGINS(vid_render, [sw sw32 glsl gl]) - QF_PROCESS_NEED_PLUGINS(console, [server], [server]) - QF_PROCESS_NEED_PLUGINS(console, [client], [client]) - QF_PROCESS_NEED_PLUGINS(snd_output, [sdl mme sgi sun win dx oss alsa]) - QF_PROCESS_NEED_PLUGINS(snd_render, [jack default]) - QF_PROCESS_NEED_PLUGINS(cd, [xmms sdl sgi win linux file]) + QF_PROCESS_NEED_PLUGINS(vid_render, [sw glsl gl vulkan], [libs/video/renderer]) + QF_PROCESS_NEED_PLUGINS(console, [server], [libs/console], [server]) + QF_PROCESS_NEED_PLUGINS(console, [client], [libs/console], [client]) + QF_PROCESS_NEED_PLUGINS(snd_output, [sdl mme sgi sun win dx oss jack alsa], [libs/audio/targets]) + QF_PROCESS_NEED_PLUGINS(snd_render, [default], [libs/audio/renderer]) + QF_PROCESS_NEED_PLUGINS(cd, [xmms sdl sgi win linux file], [libs/audio]) fi dnl Do not use -module here, it belongs in makefile.am due to automake @@ -342,7 +441,6 @@ QF_SUBST(QWAQ_TARGETS) QF_SUBST(QW_TARGETS) QF_SUBST(QW_DESKTOP_DATA) QF_SUBST(CD_TARGETS) -QF_SUBST(JOY_TARGETS) QF_SUBST(SND_TARGETS) QF_SUBST(AUDIO_TARGETS) QF_SUBST(VID_MODEL_TARGETS) @@ -350,6 +448,21 @@ QF_SUBST(VID_REND_TARGETS) QF_SUBST(VID_REND_NOINST_TARGETS) QF_SUBST(VID_TARGETS) +QF_SUBST(BSP2IMG_TARGETS) +QF_SUBST(CARNE_TARGETS) +QF_SUBST(PAK_TARGETS) +QF_SUBST(QFBSP_TARGETS) +QF_SUBST(QFCC_TARGETS) +QF_SUBST(QFLIGHT_TARGETS) +QF_SUBST(QFLMP_TARGETS) +QF_SUBST(QFMODELGEN_TARGETS) +QF_SUBST(QFSPRITEGEN_TARGETS) +QF_SUBST(QFVIS_TARGETS) +QF_SUBST(WAD_TARGETS) +QF_SUBST(WAV_TARGETS) + +QF_SUBST(VKGEN_TARGETS) + QF_DEPS(BSP2IMG, [], [$(top_builddir)/libs/image/libQFimage.la @@ -372,6 +485,7 @@ QF_DEPS(QFCC, QF_DEPS(QFCC_TEST, [], [$(top_builddir)/libs/ruamoko/libQFruamoko.la + $(top_builddir)/libs/gamecode/libQFgamecode.la $(top_builddir)/libs/util/libQFutil.la], [$(WIN32_LIBS)], ) @@ -404,8 +518,9 @@ QF_DEPS(QFVIS, [$(WIN32_LIBS)], ) QF_DEPS(QWAQ, - [], + [-I$(top_srcdir)/ruamoko/qwaq], [$(top_builddir)/libs/ruamoko/libQFruamoko.la + $(top_builddir)/libs/gamecode/libQFgamecode.la $(top_builddir)/libs/util/libQFutil.la], [$(WIN32_LIBS)], ) @@ -413,6 +528,7 @@ QF_DEPS(CARNE, [], [$(top_builddir)/libs/gib/libQFgib.la $(top_builddir)/libs/ruamoko/libQFruamoko.la + $(top_builddir)/libs/gamecode/libQFgamecode.la $(top_builddir)/libs/util/libQFutil.la], [$(WIN32_LIBS)], ) @@ -432,3 +548,8 @@ QF_DEPS(WAV, [$(top_builddir)/libs/util/libQFutil.la], [$(WIN32_LIBS)], ) +QF_DEPS(VKGEN, + [], + [], + [], +) diff --git a/config.d/cdrom.m4 b/config.d/cdrom.m4 index 38e407669..e7c66f80e 100644 --- a/config.d/cdrom.m4 +++ b/config.d/cdrom.m4 @@ -4,7 +4,7 @@ dnl ================================================================== dnl XMMS Checks AC_ARG_ENABLE(xmms, -[ --enable-xmms enable checking for XMMS], + AS_HELP_STRING([--enable-xmms], [enable checking for XMMS]) ) if test "x$mingw" != xyes -a "x$enable_xmms" == xyes; then AM_PATH_XMMS(0.9.5.1, HAVE_XMMS=yes, HAVE_XMMS=no) @@ -59,10 +59,12 @@ AC_SUBST(SGI_CD_LIBS) AC_EGREP_CPP([QF_maGiC_VALUE], [ +#ifdef _WIN32 #include #include #if defined(MCI_SET_DOOR_OPEN) QF_maGiC_VALUE +#endif #endif ], CDTYPE="$CDTYPE WIN32" @@ -78,8 +80,8 @@ AC_SUBST(CD_LIBS) AC_ARG_WITH([cd-default], AS_HELP_STRING([--with-cd-default=...], - [Plugin to use for the default cd driver.] - [Defaults to File.] + [plugin to use for the default cd driver.] + [Defaults to file.] [[file linux xmms sdl sgi win32]]), [cd_default="$withval"] ) diff --git a/config.d/compiling.m4 b/config.d/compiling.m4 index 457882d09..81cca85bf 100644 --- a/config.d/compiling.m4 +++ b/config.d/compiling.m4 @@ -16,19 +16,19 @@ AC_MSG_RESULT([$leave_cflags_alone]) AC_MSG_CHECKING(for C99 inline) c99_inline=no -AC_TRY_LINK( - [inline int foo (int x) { return x * x; } - int (*bar) (int) = foo;], - [], - c99_inline=no - AC_MSG_RESULT(no), - c99_inline=yes - AC_DEFINE(HAVE_C99INLINE, extern, [define this if using c99 inline]) - AC_MSG_RESULT(yes) +AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[inline int foo (int x) { return x * x; } + int (*bar) (int) = foo;]], + [[]])], + [c99_inline=no + AC_MSG_RESULT(no)], + [c99_inline=yes + AC_DEFINE(HAVE_C99INLINE, extern, define this if using c99 inline) + AC_MSG_RESULT(yes)] ) AH_VERBATIM([HAVE_C99INLINE], -[/* Define this if the GCC __attribute__ keyword is available */ -#undef HAVE_C99INLINE +[#undef HAVE_C99INLINE #ifdef HAVE_C99INLINE # define GNU89INLINE #else @@ -53,7 +53,7 @@ if test "x$GCC" = xyes; then fi AC_ARG_ENABLE(debug, - [ --disable-debug compile without debugging], + AS_HELP_STRING([--disable-debug], [compile without debugging]), debug=$enable_debug ) @@ -76,28 +76,83 @@ else AC_MSG_RESULT(no) fi +AC_ARG_ENABLE(Werror, + AS_HELP_STRING([--disable-Werror], [do not treat warnings as errors])) +dnl We want warnings, lots of warnings... +dnl The help text should be INVERTED before release! +dnl when in git, this test defaults to ENABLED. +dnl In a release, this test defaults to DISABLED. +if test "x$GCC" = "xyes"; then + if test "x$enable_Werror" $cvs_def_enabled; then + CFLAGS="$CFLAGS -Wall -Werror -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations" + else + CFLAGS="$CFLAGS -Wall" + fi + CFLAGS="$CFLAGS -fno-common" +fi + AC_ARG_ENABLE(optimize, - [ --disable-optimize compile without optimizations (for development)], + AS_HELP_STRING([--disable-optimize], + [compile without optimizations (for development)]), optimize=$enable_optimize, optimize=yes ) +if test "x$host_cpu" = xaarch64; then + simd=neon +else + AC_ARG_ENABLE(simd, + AS_HELP_STRING([--enable-simd@<:@=arg@:.@], + [enable SIMD support (default auto)]), + [], + [enable_simd=yes] + ) + + case "$enable_simd" in + no) + simd=no + ;; + sse|sse2|avx|avx2) + QF_CC_OPTION(-m$enable_simd) + simd=$enable_simd + ;; + yes) + for simd in avx2 avx sse2 sse; do + if lscpu | grep -q -w $simd; then + QF_CC_OPTION(-m$simd) + break + fi + done + ;; + esac + case "$simd" in + avx*) + ;; + *) + QF_CC_OPTION(-Wno-psabi) + ;; + esac +fi + AC_MSG_CHECKING(for optimization) if test "x$optimize" = xyes -a "x$leave_cflags_alone" != "xyes"; then AC_MSG_RESULT(yes) BUILD_TYPE="$BUILD_TYPE Optimize" if test "x$GCC" = xyes; then saved_cflags="$CFLAGS" - CFLAGS="" + dnl CFLAGS="" QF_CC_OPTION(-frename-registers) - if test "$CC_MAJ" -ge 4; then - QF_CC_OPTION(-finline-limit=32000 -Winline) - fi - heavy="-O2 $CFLAGS -ffast-math -fno-unsafe-math-optimizations -funroll-loops -fomit-frame-pointer -fexpensive-optimizations" + QF_CC_OPTION(-fexpensive-optimizations) + dnl if test "$CC_MAJ" -ge 4; then + dnl QF_CC_OPTION(-finline-limit=32000 -Winline) + dnl fi + dnl heavy="-O2 $CFLAGS -ffast-math -fno-unsafe-math-optimizations -funroll-loops -fomit-frame-pointer" + heavy="-O2 $CFLAGS -fno-fast-math -funroll-loops -fomit-frame-pointer " CFLAGS="$saved_cflags" light="-O2" AC_ARG_ENABLE(strict-aliasing, - [ --enable-strict-aliasing enable the -fstrict-aliasing option of gcc]) + AS_HELP_STRING([--enable-strict-aliasing], + [enable the -fstrict-aliasing option of gcc])) if test "x$enable_strict_aliasing" = "xyes"; then heavy="$heavy -fstrict-aliasing" light="$light -fstrict-aliasing" @@ -118,7 +173,8 @@ if test "x$optimize" = xyes -a "x$leave_cflags_alone" != "xyes"; then fi AC_MSG_CHECKING(for special compiler settings) AC_ARG_WITH(arch, - [ --with-arch=ARCH control compiler architecture directly], + AS_HELP_STRING([--with-arch=ARCH], + [control compiler architecture directly]), arch="$withval", arch=auto ) case "$arch" in @@ -144,25 +200,17 @@ if test "x$optimize" = xyes -a "x$leave_cflags_alone" != "xyes"; then else save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $MORE_CFLAGS" - AC_TRY_COMPILE( - [], - [], - AC_MSG_RESULT(yes), - CFLAGS="$save_CFLAGS" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[AC_MSG_RESULT(yes)],[CFLAGS="$save_CFLAGS" AC_MSG_RESULT(no) - ) + ]) fi if test $CC_MAJ = 2 -a $CC_MIN = 96; then AC_MSG_CHECKING(if align options work) save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -malign-loops=2 -malign-jumps=2 -malign-functions=2" - AC_TRY_COMPILE( - [], - [], - light="$light -malign-loops=2 -malign-jumps=2 -malign-functions=2" - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) - ) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[light="$light -malign-loops=2 -malign-jumps=2 -malign-functions=2" + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) + ]) CFLAGS="$save_CFLAGS" CFLAGS="$CFLAGS $light" else @@ -177,7 +225,8 @@ fi dnl CFLAGS for release and devel versions AC_ARG_ENABLE(profile, - [ --enable-profile compile with profiling (for development)], + AS_HELP_STRING([--enable-profile], + [compile with profiling (for development)]), profile=$enable_profile ) if test "x$profile" = xyes; then @@ -195,7 +244,7 @@ if test "x$GCC" = xyes; then dnl Check for -pipe vs -save-temps. AC_MSG_CHECKING(for -pipe vs -save-temps) AC_ARG_ENABLE(save-temps, - [ --enable-save-temps save temporary files], + AS_HELP_STRING([--enable-save-temps], [save temporary files]), AC_MSG_RESULT(-save-temps) CFLAGS="$CFLAGS -save-temps" BUILD_TYPE="$BUILD_TYPE Save-temps" @@ -213,6 +262,17 @@ if test $CC_MAJ -gt 4 -o $CC_MAJ -eq 4 -a $CC_MIN -ge 5; then fi QF_CC_OPTION(-Wtype-limits) QF_CC_OPTION_TEST([-fvisibility=hidden], [VISIBILITY=-fvisibility=hidden]) +QF_CC_OPTION(-Wsuggest-attribute=pure) +QF_CC_OPTION(-Wsuggest-attribute=const) +QF_CC_OPTION(-Wsuggest-attribute=noreturn) +QF_CC_OPTION(-Wsuggest-attribute=format) +QF_CC_OPTION(-Wformat-nonliteral) + +AC_ARG_ENABLE(coverage, + AS_HELP_STRING([--enable-coverage], [enable generation of data for gcov])) +if test "x$enable_coverage" = xyes; then + QF_CC_OPTION(-fprofile-arcs -ftest-coverage) +fi dnl QuakeForge uses lots of BCPL-style (//) comments, which can cause problems dnl with many compilers that do not support the latest ISO standards. Well, @@ -240,22 +300,6 @@ if test "x$GCC" != xyes; then esac fi -AC_ARG_ENABLE(Werror, -[ --disable-Werror Do not treat warnings as errors] -) -dnl We want warnings, lots of warnings... -dnl The help text should be INVERTED before release! -dnl when in git, this test defaults to ENABLED. -dnl In a release, this test defaults to DISABLED. -if test "x$GCC" = "xyes"; then - if test "x$enable_Werror" $cvs_def_enabled; then - CFLAGS="$CFLAGS -Wall -Werror -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations" - else - CFLAGS="$CFLAGS -Wall" - fi - CFLAGS="$CFLAGS -fno-common" -fi - AS="$CC" if test "x$SYSTYPE" = "xWIN32"; then ASFLAGS="\$(DEFS) \$(CFLAGS) \$(CPPFLAGS) \$(DEFAULT_INCLUDES) \$(INCLUDES) -D_WIN32" diff --git a/config.d/compression.m4 b/config.d/compression.m4 index bdc0309f2..2c3f8d4fe 100644 --- a/config.d/compression.m4 +++ b/config.d/compression.m4 @@ -1,6 +1,4 @@ -AC_ARG_ENABLE(flac, -[ --disable-flac disable flac support], -) +AC_ARG_ENABLE(flac, AS_HELP_STRING([--disable-flac], [disable flac support])) HAVE_FLAC=no if test "x$enable_flac" != "xno"; then if test "x$PKG_CONFIG" != "x"; then @@ -15,8 +13,7 @@ fi AM_CONDITIONAL(HAVE_FLAC, test "x$HAVE_FLAC" = "xyes") AC_ARG_ENABLE(wildmidi, -[ --disable-wildmidi disable libWildMidi support], -) + AS_HELP_STRING([--disable-wildmidi], disable libWildMidi support])) HAVE_WILDMIDI=no WM_LIBS= if test "x$enable_wildmidi" != "xno"; then @@ -33,8 +30,7 @@ AC_SUBST(WM_LIBS) AM_CONDITIONAL(HAVE_MIDI, test "x$HAVE_WILDMIDI" = "xyes") AC_ARG_ENABLE(vorbis, -[ --disable-vorbis disable ogg vorbis support], -) + AS_HELP_STRING([--disable-vorbis], [disable ogg vorbis support])) HAVE_VORBIS=no if test "x$enable_vorbis" != "xno"; then if test "x$PKG_CONFIG" != "x"; then @@ -59,7 +55,7 @@ fi AM_CONDITIONAL(HAVE_VORBIS, test "x$HAVE_VORBIS" = "xyes") -AC_ARG_ENABLE(zlib, [ --disable-zlib disable zlib support]) +AC_ARG_ENABLE(zlib, AS_HELP_STRING([--disable-zlib], [disable zlib support])) HAVE_ZLIB=no Z_LIBS="" if test "x$enable_zlib" != "xno"; then @@ -83,9 +79,7 @@ if test "x$HAVE_ZLIB" = "xyes"; then AC_DEFINE(HAVE_ZLIB, 1, [Define if you have zlib]) fi -AC_ARG_ENABLE(png, -[ --disable-png disable png support], -) +AC_ARG_ENABLE(png, AS_HELP_STRING([--disable-png], [disable png support])) HAVE_PNG=no PNG_LIBS="" if test "x$enable_png" != "xno"; then diff --git a/config.d/cross_compile.m4 b/config.d/cross_compile.m4 index 66ca372a3..6992a6f90 100644 --- a/config.d/cross_compile.m4 +++ b/config.d/cross_compile.m4 @@ -1,8 +1,9 @@ if test "x$cross_compiling" = xyes; then AC_MSG_CHECKING(whether byte ordering is bigendian) AC_ARG_WITH(endian, -[ --with-endian=TYPE set endian of target system for - cross-compiling. TYPE = little or big.], + AS_HELP_STRING([--with-endian=TYPE], + [set endian of target system for cross-compiling.] + [TYPE = little or big.]), endian="$withval", ) case "x$endian" in diff --git a/config.d/curses.m4 b/config.d/curses.m4 index 80a2b0415..ea4b5e3b0 100644 --- a/config.d/curses.m4 +++ b/config.d/curses.m4 @@ -1,19 +1,53 @@ AC_ARG_ENABLE(curses, -[ --disable-curses disable curses support] -) + AS_HELP_STRING([--disable-curses], [disable curses support])) if test "x$enable_curses" != "xno"; then - AC_CHECK_HEADERS(curses.h) + if test "x$PKG_CONFIG" != "x"; then + PKG_CHECK_MODULES([NCURSES], [ncurses], HAVE_NCURSES=yes, HAVE_NCURSES=no) + else + AC_CHECK_HEADER([curses.h], [], + [AC_CHECK_HEADER([ncurses/curses.h], + [NCURSES_CFLAGS=-I${prefix}/include/ncurses])]) AC_CHECK_LIB(ncurses, initscr, - CURSES_LIBS=-lncurses, + NCURSES_LIBS=-lncurses, AC_CHECK_LIB(pdcurses, initscr, - CURSES_LIBS=-lpdcurses, + NCURSES_LIBS=-lpdcurses, AC_CHECK_LIB(curses, initscr, - CURSES_LIBS=-lcurses, - CURSES_LIBS= + NCURSES_LIBS=-lcurses, + NCURSES_LIBS= ) ) ) + if test "x$NCURSES_LIBS" != "x"; then + HAVE_NCURSES=yes + else + HAVE_NCURSES=no + fi + fi else - CURSES_LIBS= + HAVE_NCURSES=no + NCURSES_LIBS= fi -AC_SUBST(CURSES_LIBS) +AC_SUBST(NCURSES_LIBS) + +if test "x$HAVE_NCURSES" == "xyes"; then + AC_DEFINE(HAVE_NCURSES, 1, [Define if you have the ncurses library]) + if test "x$PKG_CONFIG" != "x"; then + PKG_CHECK_MODULES([PANEL], [panel], HAVE_PANEL=yes, HAVE_PANEL=no) + else + AC_CHECK_HEADER(panel.h, + [AC_CHECK_LIB(panel, new_panel, + [AC_DEFINE(HAVE_PANEL, 1, + [Define if you have the ncurses panel library]) + PANEL_LIBS=-lpanel + HAVE_PANEL=yes], + [HAVE_PANEL=no], + $NCURSES_LIBS + )], + [HAVE_PANEL=no], + [$NCURSES_CFLAGS] + ) + fi +else + PANEL_LIBS= +fi +AC_SUBST(PANEL_LIBS) diff --git a/config.d/evdev.m4 b/config.d/evdev.m4 new file mode 100644 index 000000000..643fb9e76 --- /dev/null +++ b/config.d/evdev.m4 @@ -0,0 +1,7 @@ +dnl ================================================================== +dnl Checks for evdev +dnl ================================================================== + +AC_CHECK_HEADER(linux/input.h, + [QF_NEED(input,[evdev]) + AC_DEFINE([HAVE_EVDEV], [1], [Define if you have evdev])]) diff --git a/config.d/fbdev.m4 b/config.d/fbdev.m4 index afc519003..335658eb4 100644 --- a/config.d/fbdev.m4 +++ b/config.d/fbdev.m4 @@ -1,7 +1,8 @@ dnl Checks for Linux FBDev support AC_ARG_WITH(fbdev, -[ --with-fbdev use Linux framebuffer device], -HAVE_FBDEV=$withval, HAVE_FBDEV=no) + AS_HELP_STRING([--with-fbdev], [use Linux framebuffer device]), + HAVE_FBDEV=$withval, + HAVE_FBDEV=no) if test "x$HAVE_FBDEV" != xno; then dnl We should still be able to compile it even if dnl there is no fbdev support in the running kernel @@ -10,27 +11,15 @@ fi if test "x$HAVE_FBDEV" = xyes; then AC_MSG_CHECKING(for FB_AUX_VGA_PLANES_VGA4) - AC_TRY_COMPILE( - [#include "linux/fb.h"], - [int foo = FB_AUX_VGA_PLANES_VGA4;], - AC_DEFINE(HAVE_FB_AUX_VGA_PLANES_VGA4, 1, [Define this if you have FB_AUX_VGA_PLANES_VGA4]) - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) - ) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include "linux/fb.h"]], [[int foo = FB_AUX_VGA_PLANES_VGA4;]])],[AC_DEFINE(HAVE_FB_AUX_VGA_PLANES_VGA4, 1, Define this if you have FB_AUX_VGA_PLANES_VGA4) + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) + ]) AC_MSG_CHECKING(for FB_AUX_VGA_PLANES_CFB4) - AC_TRY_COMPILE( - [#include "linux/fb.h"], - [int foo = FB_AUX_VGA_PLANES_CFB4;], - AC_DEFINE(HAVE_FB_AUX_VGA_PLANES_CFB4, 1, [Define this if you have FB_AUX_VGA_PLANES_CFB4]) - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) - ) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include "linux/fb.h"]], [[int foo = FB_AUX_VGA_PLANES_CFB4;]])],[AC_DEFINE(HAVE_FB_AUX_VGA_PLANES_CFB4, 1, Define this if you have FB_AUX_VGA_PLANES_CFB4) + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) + ]) AC_MSG_CHECKING(for FB_AUX_VGA_PLANES_CFB8) - AC_TRY_COMPILE( - [#include "linux/fb.h"], - [int foo = FB_AUX_VGA_PLANES_CFB8;], - AC_DEFINE(HAVE_FB_AUX_VGA_PLANES_CFB8, 1, [Define this if you have FB_AUX_VGA_PLANES_CFB4]) - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) - ) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include "linux/fb.h"]], [[int foo = FB_AUX_VGA_PLANES_CFB8;]])],[AC_DEFINE(HAVE_FB_AUX_VGA_PLANES_CFB8, 1, Define this if you have FB_AUX_VGA_PLANES_CFB4) + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) + ]) fi diff --git a/config.d/freetype.m4 b/config.d/freetype.m4 new file mode 100644 index 000000000..38618e5d0 --- /dev/null +++ b/config.d/freetype.m4 @@ -0,0 +1,32 @@ +AC_ARG_ENABLE(freetype, + AS_HELP_STRING([--disable-freetype], [disable freetype support])) +if test "x$enable_freetype" != "xno"; then + if test "x$PKG_CONFIG" != "x"; then + PKG_CHECK_MODULES([FREETYPE], [freetype2], HAVE_FREETYPE=yes, HAVE_FREETYPE=no) + fi +else + HAVE_FREETYPE=no + FREETYPE_LIBS= +fi +AC_SUBST(FREETYPE_LIBS) + +if test "x$HAVE_FREETYPE" == "xyes"; then + AC_DEFINE(HAVE_FREETYPE, 1, [Define if you have the freetype library]) +fi + + +AC_ARG_ENABLE(harfbuzz, + AS_HELP_STRING([--disable-harfbuzz], [disable HarfBuzz support])) +if test "x$enable_harfbuzz" != "xno"; then + if test "x$PKG_CONFIG" != "x"; then + PKG_CHECK_MODULES([HARFBUZZ], [harfbuzz], HAVE_HARFBUZZ=yes, HAVE_HARFBUZZ=no) + fi +else + HAVE_HARFBUZZ=no + HARFBUZZ_LIBS= +fi +AC_SUBST(HARFBUZZ_LIBS) + +if test "x$HAVE_HARFBUZZ" == "xyes"; then + AC_DEFINE(HAVE_HARFBUZZ, 1, [Define if you have the HarfBuzz library]) +fi diff --git a/config.d/header_files.m4 b/config.d/header_files.m4 index afb4cd37f..93513dc8c 100644 --- a/config.d/header_files.m4 +++ b/config.d/header_files.m4 @@ -3,7 +3,6 @@ dnl Checks for header files. dnl ================================================================== AC_HEADER_DIRENT -AC_HEADER_STDC AC_HEADER_MAJOR AC_HEADER_SYS_WAIT AC_CHECK_HEADERS( @@ -12,7 +11,7 @@ AC_CHECK_HEADERS( dmedia/cdaudio.h dpmi.h dsound.h errno.h fcntl.h io.h \ ifaddrs.h libc.h limits.h linux/cdrom.h linux/joystick.h \ linux/soundcard.h machine/soundcard.h malloc.h math.h mgraph.h _mingw.h \ - netdb.h net/if.h netinet/in.h process.h pthread.h pwd.h rpc/types.h \ + netdb.h net/if.h netinet/in.h process.h pthread.h pwd.h \ setjmp.h signal.h stdarg.h stdio.h stdlib.h string.h strings.h \ sys/asoundlib.h sys/audioio.h sys/filio.h sys/ioctl.h sys/io.h sys/ipc.h \ sys/mman.h sys/param.h sys/poll.h sys/select.h sys/shm.h sys/signal.h \ @@ -33,28 +32,21 @@ fi AC_FUNC_ALLOCA AC_MSG_CHECKING(for fnmatch in fnmatch.h) -AC_TRY_COMPILE( - [#include "fnmatch.h"], - [int (*foo)() = fnmatch;], - AC_DEFINE(HAVE_FNMATCH_PROTO, 1, [Define this if fnmatch is prototyped in fnmatch.h]) - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) -) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include "fnmatch.h"]], [[int (*foo)() = fnmatch;]])],[AC_DEFINE(HAVE_FNMATCH_PROTO, 1, Define this if fnmatch is prototyped in fnmatch.h) + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) +]) AC_MSG_CHECKING(for strnlen in string.h) -AC_TRY_COMPILE( - [#include "string.h"], - [int (*foo)() = strnlen;], - AC_DEFINE(HAVE_STRNLEN_PROTO, 1, [Define this if strnlen is prototyped in string.h]) - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) -) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include "string.h"]], [[int (*foo)() = strnlen;]])],[AC_DEFINE(HAVE_STRNLEN_PROTO, 1, Define this if strnlen is prototyped in string.h) + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) +]) + +AC_MSG_CHECKING(for strndup in string.h) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include "string.h"]], [[int (*foo)() = strndup;]])],[AC_DEFINE(HAVE_STRNDUP_PROTO, 1, Define this if strndup is prototyped in string.h) + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) +]) AC_MSG_CHECKING(for strcasestr in string.h) -AC_TRY_COMPILE( - [#include "string.h"], - [int (*foo)() = strcasestr;], - AC_DEFINE(HAVE_STRCASESTR_PROTO, 1, [Define this if strcasestr is prototyped in string.h]) - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) -) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include "string.h"]], [[int (*foo)() = strcasestr;]])],[AC_DEFINE(HAVE_STRCASESTR_PROTO, 1, Define this if strcasestr is prototyped in string.h) + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) +]) diff --git a/config.d/library_functions.m4 b/config.d/library_functions.m4 index 66ff31edc..65c8a841f 100644 --- a/config.d/library_functions.m4 +++ b/config.d/library_functions.m4 @@ -5,19 +5,21 @@ dnl ================================================================== AC_FUNC_ALLOCA AC_FUNC_MEMCMP AC_FUNC_MMAP -AC_TYPE_SIGNAL + AC_FUNC_VPRINTF AC_CHECK_FUNCS( - access _access connect dlopen execvp fcntl ftime _ftime getaddrinfo \ + access _access bsearch_r connect dlopen execvp fcntl ftime _ftime \ + getaddrinfo \ gethostbyname gethostname getnameinfo getpagesize gettimeofday getuid \ - getwd ioctl mkdir _mkdir mprotect putenv select snprintf _snprintf \ - socket stat strcasestr strerror strnlen strsep strstr vsnprintf \ - _vsnprintf wait + getwd ioctl mkdir _mkdir mprotect putenv qsort_r select sigaction \ + snprintf _snprintf socket stat strcasestr strerror strerror_r strndup \ + strnlen \ + strsep strstr vsnprintf _vsnprintf wait ) AC_FUNC_VA_COPY AC_FUNC__VA_COPY -AH_VERBATIM([DEFINE_VA_COPY], +AH_VERBATIM([HAVE__VA_COPY_], [#ifndef HAVE_VA_COPY # ifdef HAVE__VA_COPY # define va_copy(d,s) __va_copy ((d), (s)) @@ -35,6 +37,17 @@ if test "x$ac_cv_func_dlopen" != "xyes"; then fi AC_SUBST(DL_LIBS) +if test "x$DL_LIBS" != "x"; then +AC_MSG_CHECKING([for RTLD_NOW]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int foo = RTLD_NOW]])],[AC_DEFINE(HAVE_RTLD_NOW, 1, Define if you have RTLD_NOW.) + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) +]) +AC_MSG_CHECKING([for RTLD_DEEPBIND]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int foo = RTLD_DEEPBIND]])],[AC_DEFINE(HAVE_RTLD_DEEPBIND, 1, Define if you have RTLD_DEEPBIND.) + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) +]) +fi + dnl Checks for stricmp/strcasecmp #AC_CHECK_FUNC(strcasecmp, # , @@ -60,36 +73,24 @@ fi AC_CHECK_FUNCS(usleep) AC_MSG_CHECKING(for fnmatch) -AC_TRY_LINK( - [], - [fnmatch();], - BUILD_FNMATCH=no - AC_MSG_RESULT(yes), - BUILD_FNMATCH=yes +AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[fnmatch();]])],[BUILD_FNMATCH=no + AC_MSG_RESULT(yes)],[BUILD_FNMATCH=yes AC_MSG_RESULT(no) -) +]) AM_CONDITIONAL(BUILD_FNMATCH, test "x$BUILD_FNMATCH" = "xyes") AC_MSG_CHECKING(for opendir) -AC_TRY_LINK( - [], - [opendir();], - BUILD_DIRENT=no - AC_MSG_RESULT(yes), - BUILD_DIRENT=yes +AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[opendir();]])],[BUILD_DIRENT=no + AC_MSG_RESULT(yes)],[BUILD_DIRENT=yes AC_MSG_RESULT(no) -) +]) AM_CONDITIONAL(BUILD_DIRENT, test "x$BUILD_DIRENT" = "xyes") AC_MSG_CHECKING(for getopt_long) -AC_TRY_LINK( - [], - [getopt_long();], - BUILD_GETOPT=no - AC_MSG_RESULT(yes), - BUILD_GETOPT=yes +AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[getopt_long();]])],[BUILD_GETOPT=no + AC_MSG_RESULT(yes)],[BUILD_GETOPT=yes AC_MSG_RESULT(no) -) +]) AM_CONDITIONAL(BUILD_GETOPT, test "x$BUILD_GETOPT" = "xyes") AC_MSG_CHECKING(for log2f) diff --git a/config.d/networking.m4 b/config.d/networking.m4 index a7fb22783..443f663f9 100644 --- a/config.d/networking.m4 +++ b/config.d/networking.m4 @@ -16,8 +16,8 @@ else fi AC_ARG_WITH(ipv6, - AS_HELP_STRING([--with-ipv6=DIR], - [Enable IPv6 support.] + AS_HELP_STRING([--with-ipv6@<:@=DIR@:>@], + [enable IPv6 support.] [Optional argument specifies location of inet6 libraries.]), [ if test "x$withval" = xno ; then @@ -60,19 +60,16 @@ fi AC_MSG_CHECKING([for connect in -lwsock32]) SAVELIBS="$LIBS" LIBS="$LIBS -lwsock32" -AC_TRY_LINK([ +AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include -], -[ +]], [[ connect(0, NULL, 42); -], - NET_LIBS="$NET_LIBS -lwsock32 -lwinmm" +]])],[NET_LIBS="$NET_LIBS -lwsock32 -lwinmm" ac_cv_func_connect=yes ac_cv_func_gethostbyname=yes HAVE_WSOCK=yes - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) -) + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) +]) LIBS="$SAVELIBS" AC_MSG_CHECKING(for UDP support) @@ -87,16 +84,13 @@ if test "x$ac_cv_func_connect" != "xyes"; then AC_MSG_CHECKING([for connect in -lwsock32]) SAVELIBS="$LIBS" LIBS="$LIBS -lwsock32" - AC_TRY_LINK([ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include - ], - [ + ]], [[ connect (0, NULL, 42); - ], - NET_LIBS="$NET_LIBS -lwsock32 -lwinmm" - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) - ) + ]])],[NET_LIBS="$NET_LIBS -lwsock32 -lwinmm" + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) + ]) LIBS="$SAVELIBS" fi AC_SUBST(NET_LIBS) @@ -104,12 +98,9 @@ AC_SUBST(NET_LIBS) AC_MSG_CHECKING([for getifaddrs]) SAVELIBS="$LIBS" LIBS="$LIBS $NET_LIBS" -AC_TRY_LINK([], - [ +AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[ getifaddrs (0); - ], - AC_DEFINE(HAVE_GETIFADDRS, 1, [Define this if you have getifaddrs()]) - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) -) + ]])],[AC_DEFINE(HAVE_GETIFADDRS, 1, Define this if you have getifaddrs()) + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) +]) LIBS="$SAVELIBS" diff --git a/config.d/paths.m4 b/config.d/paths.m4 index f340eda78..b71607a7a 100644 --- a/config.d/paths.m4 +++ b/config.d/paths.m4 @@ -1,9 +1,7 @@ dnl Whether to enable XDG support or not -AC_ARG_ENABLE(xdg, -[ --enable-xdg enable XDG support], +AC_ARG_ENABLE(xdg, AS_HELP_STRING([--enable-xdg], [enable XDG support]), xdg=$enable_xdg, - xdg=no -) + xdg=no) if test "x$xdg" != xno; then HAVE_XDG=yes else @@ -36,10 +34,10 @@ else fi AC_ARG_WITH(global-cfg, -[ --with-global-cfg=FILE If set will change the name and location of the - global config file used by QuakeForge. Defaults to - /etc/quakeforge.conf.], -globalconf="$withval", globalconf="auto") + AS_HELP_STRING([--with-global-cfg=FILE], + [if set will change the name and location of the global config file] + [used by QuakeForge. Defaults to /etc/quakeforge.conf.]), + globalconf="$withval", globalconf="auto") if test "x$globalconf" = "xauto" || test "x$globalconf" = "xyes" || \ test "x$globalconf" = "xno"; then dnl yes/no sanity checks globalconf="$default_globalconf" @@ -47,10 +45,10 @@ fi AC_DEFINE_UNQUOTED(FS_GLOBALCFG, "$globalconf", [Define this to the location of the global config file]) AC_ARG_WITH(user-cfg, -[ --with-user-cfg=FILE If set will change the name and location of the - user-specific config file used by QuakeForge. - Defaults to ~/.quakeforgerc.], -userconf="$withval", userconf="auto") + AS_HELP_STRING([--with-user-cfg=FILE], + [if set will change the name and location of the user-specific config] + [file used by QuakeForge. Defaults to ~/.quakeforgerc.]), + userconf="$withval", userconf="auto") if test "x$userconf" = "xauto" || test "x$userconf" = "xyes" || \ test "x$userconf" = "xno"; then dnl yes/no sanity checks userconf="$default_userconf" @@ -58,9 +56,10 @@ fi AC_DEFINE_UNQUOTED(FS_USERCFG, "$userconf", [Define this to the location of the user config file]) AC_ARG_WITH(sharepath, -[ --with-sharepath=DIR Use DIR for shared game data, defaults to - '.' or \${datarootdir}/games/quakeforge (if new style)], -sharepath=$withval, sharepath="auto") + AS_HELP_STRING([--with-sharepath=DIR], + [use DIR for shared game data, defaults to '.' or] + [${datarootdir}/games/quakeforge (if new style)]), + sharepath=$withval, sharepath="auto") if test "x$sharepath" = "xauto" -o "x$sharepath" = "xyes" -o "x$sharepath" = "x"; then sharepath="$default_sharepath" elif test "x$sharepath" = xno; then @@ -70,9 +69,10 @@ AC_DEFINE_UNQUOTED(FS_SHAREPATH, "$sharepath", [Define this to the shared game d QF_SUBST(sharepath) AC_ARG_WITH(userpath, -[ --with-userpath=DIR Use DIR for unshared game data, defaults to - '.' or ~/.quakeforge (if new style)], -userpath=$withval, userpath="auto") + AS_HELP_STRING([--with-userpath=DIR], + [use DIR for unshared game data, defaults to '.' or ~/.quakeforge] + [(if new style)]), + userpath=$withval, userpath="auto") if test "x$userpath" = "xauto" -o "x$userpath" = "xyes" -o "x$userpath" = "x"; then userpath="$default_userpath" elif test "x$userpath" = xno; then @@ -81,9 +81,9 @@ fi AC_DEFINE_UNQUOTED(FS_USERPATH, "$userpath", [Define this to the unshared game directory root]) AC_ARG_WITH(plugin-path, -[ --with-plugin-path=DIR Use DIR for loading plugins, defaults to - \${libdir}/quakeforge], -plugindir=$withval, plugindir="auto") + AS_HELP_STRING([--with-plugin-path=DIR], + [use DIR for loading plugins, defaults to ${libdir}/quakeforge]), + plugindir=$withval, plugindir="auto") PLUGINDIR="\${libdir}/quakeforge/plugins" if test "x$plugindir" = "xauto" -o "x$plugindir" = "xyes" -o "x$plugindir" = "x"; then @@ -98,8 +98,21 @@ eval expanded_plugindir="$expanded_plugindir" AC_DEFINE_UNQUOTED(FS_PLUGINPATH, "$expanded_plugindir", [Define this to the path from which to load plugins]) AC_SUBST(plugindir) +SHADERDIR="\${libdir}/quakeforge/shaders" +if test "x$shaderdir" = "xauto" -o "x$shaderdir" = "xyes" -o "x$shaderdir" = "x"; then + shaderdir="$SHADERDIR" +elif test "x$shaderdir" = xno; then + shaderdir="." +else + SHADERDIR="$shaderdir" +fi +eval expanded_shaderdir="$shaderdir" +eval expanded_shaderdir="$expanded_shaderdir" +AC_DEFINE_UNQUOTED(FS_SHADERPATH, "$expanded_shaderdir", [Define this to the path from which to load shaders]) +AC_SUBST(plugindir) + AC_ARG_WITH(gl-driver, - [ --with-gl-driver=NAME Name of OpenGL driver DLL/DSO], + AS_HELP_STRING([--with-gl-driver=NAME], [name of OpenGL driver DLL/DSO]), gl_driver=$withval, gl_driver=auto ) diff --git a/config.d/programs.m4 b/config.d/programs.m4 index 439fb7c93..bb8ac5e9d 100644 --- a/config.d/programs.m4 +++ b/config.d/programs.m4 @@ -8,6 +8,7 @@ AC_PROG_CPP AC_PROG_LN_S AC_PROG_RANLIB AM_PROG_AS +AM_PATH_PYTHON([3]) PKG_PROG_PKG_CONFIG @@ -32,6 +33,7 @@ AS_VERSION_COMPARE([$BISON_VER], [2.6], AC_MSG_RESULT([yes]) ) +AC_PROG_LEX(noyywrap) AM_PROG_LEX if echo $LEX | grep -v flex > /dev/null; then AC_MSG_ERROR(GNU flex is required but was not found) diff --git a/config.d/pthread.m4 b/config.d/pthread.m4 index 6fc05c13c..6ed5626a9 100644 --- a/config.d/pthread.m4 +++ b/config.d/pthread.m4 @@ -11,22 +11,14 @@ if test "x$ac_cv_header_pthread_h" = "xyes"; then ;; *openbsd*) LIBS="$LIBS -pthread" - AC_TRY_LINK( - [#include ], - [pthread_attr_t type; - pthread_attr_setstacksize(&type, 0x100000);], - [PTHREAD_LDFLAGS=-pthread], - [PTHREAD_LDFLAGS=-lpthread] - ) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[pthread_attr_t type; + pthread_attr_setstacksize(&type, 0x100000);]])],[PTHREAD_LDFLAGS=-pthread],[PTHREAD_LDFLAGS=-lpthread + ]) ;; *) LIBS="$LIBS -lpthread" - AC_TRY_LINK( - [#include ], - [pthread_attr_t type; - pthread_attr_setstacksize(&type, 0x100000);], - [PTHREAD_LDFLAGS=-lpthread], - [PTHREAD_LDFLAGS=-pthread] - ) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[pthread_attr_t type; + pthread_attr_setstacksize(&type, 0x100000);]])],[PTHREAD_LDFLAGS=-lpthread],[PTHREAD_LDFLAGS=-pthread + ]) ;; esac LIBS="$save_LIBS" diff --git a/config.d/qfcc.m4 b/config.d/qfcc.m4 index 3e1d87ae7..55477fe81 100644 --- a/config.d/qfcc.m4 +++ b/config.d/qfcc.m4 @@ -3,13 +3,12 @@ F77=touch AC_SUBST(F77) AC_ARG_WITH(cpp, -[ --with-cpp=CPP how qfcc should invoke cpp], - cpp_name="$withval", cpp_name=auto -) + AS_HELP_STRING([--with-cpp=CPP], [how qfcc should invoke cpp]), + cpp_name="$withval", cpp_name=auto) if test "x$cpp_name" != xauto; then CPP_NAME="$cpp_name" else - CPP_NAME="cpp %d -o %o %i" + CPP_NAME="cpp %u %d %s -o %o %i" case "$host_os" in *freebsd*) CPP_NAME="cpp %d %i %o" diff --git a/config.d/result.m4 b/config.d/result.m4 index 87ded6b36..7bb199947 100644 --- a/config.d/result.m4 +++ b/config.d/result.m4 @@ -4,7 +4,7 @@ AC_MSG_RESULT([ Build type :$BUILD_TYPE Server support :${SV_TARGETS:- no} Client support :${CL_TARGETS:- no} - Tools support :${tools_dirs:- no} + Tools support :${tools_list:- no} Sound support :${SOUND_TYPES:- no} ${snd_output_default} CD Audio system :${CDTYPE:- no} ${cd_default} IPv6 networking : $NETTYPE_IPV6 @@ -12,6 +12,7 @@ AC_MSG_RESULT([ HTTP support : ${CURL:-no} Compiler version : $CCVER Compiler flags : $CFLAGS + SIMD Support : $simd qfcc cpp invocation: $CPP_NAME Shared game data directory : $sharepath diff --git a/config.d/rpm.m4 b/config.d/rpm.m4 index 95ac34cf7..d4406cf2a 100644 --- a/config.d/rpm.m4 +++ b/config.d/rpm.m4 @@ -1,2 +1,2 @@ -AC_CONFIG_FILES(RPM/Makefile RPM/quakeforge.conf RPM/quakeforge.spec) +AC_CONFIG_FILES(RPM/quakeforge.conf RPM/quakeforge.spec) AC_CONFIG_FILES(RPM/build_rpm, [chmod +x RPM/build_rpm]) diff --git a/config.d/sdl.m4 b/config.d/sdl.m4 index 09b49e157..2b32572a2 100644 --- a/config.d/sdl.m4 +++ b/config.d/sdl.m4 @@ -1,9 +1,7 @@ dnl SDL/SDL-GL checks -AC_ARG_ENABLE(sdl, -[ --disable-sdl disable checking for SDL], -) +AC_ARG_ENABLE(sdl, AS_HELP_STRING([--enable-sdl], [enable checking for SDL])) -if test "x$enable_sdl" != xno; then +if test "x$enable_sdl" = xyes; then if test "x$PKG_CONFIG" != "x"; then PKG_CHECK_MODULES([SDL], [sdl >= 1.2.0], HAVE_SDL=yes, HAVE_SDL=no) else @@ -24,8 +22,7 @@ fi dnl SDL-AUDIO checks AC_ARG_ENABLE(sdl-audio, -[ --disable-sdl-audio disable checking for SDL-AUDIO], -) + AS_HELP_STRING([ --disable-sdl-audio], [disable checking for SDL-AUDIO])) if test "x$enable_sdl_audio" != xno; then if test "x$HAVE_SDL" = "xyes"; then @@ -35,8 +32,7 @@ fi dnl SDL-CD checks AC_ARG_ENABLE(sdl-cd, -[ --disable-sdl-cd disable checking for SDL-CD], -) + AS_HELP_STRING([--disable-sdl-cd], [disable checking for SDL-CD])) if test "x$enable_sdl_cd" != xno; then if test "x$HAVE_SDL" = "xyes"; then HAVE_SDL_CD=yes diff --git a/config.d/sound.m4 b/config.d/sound.m4 index c0fd6afce..1abc1c311 100644 --- a/config.d/sound.m4 +++ b/config.d/sound.m4 @@ -2,9 +2,6 @@ dnl ================================================================== dnl Checks for sound dnl ================================================================== -dnl AC_ARG_ENABLE(samplerate, -dnl [ --disable-samplerate disable libsamplerate support], -dnl ) HAVE_SAMPLERATE=no SAMPLERATE_LIBS="" if test "x$enable_samplerate" != "xno"; then @@ -40,16 +37,16 @@ if test "x$enable_sound" != "xno"; then AC_CHECK_LIB(mme, waveOutOpen, HAVE_LIBMME=yes) AC_ARG_ENABLE(alsa, - [ --disable-alsa disable checks for ALSA support]) + AS_HELP_STRING([--disable-alsa], [disable checks for ALSA support])) AC_ARG_ENABLE(oss, - [ --disable-oss disable checks for OSS support]) + AS_HELP_STRING([--disable-oss], [disable checks for OSS support])) AC_ARG_ENABLE(sun, - [ --disable-sun disable checks for Sun audio support]) + AS_HELP_STRING([--disable-sun], [disable checks for Sun audio support])) AC_ARG_ENABLE(sound, - [ --disable-sound disable sound outright]) + AS_HELP_STRING([--disable-sound], [disable sound outright])) unset SOUND_TYPES @@ -96,8 +93,7 @@ QF_maGiC_VALUE AC_SUBST(ALSA_LIBS) AC_ARG_ENABLE(jack, - [ --disable-jack disable jack support], - ) + AS_HELP_STRING([--disable-jack], [disable jack support])) HAVE_JACK=no JACK_LIBS="" if test "x$enable_jack" != "xno"; then @@ -106,7 +102,8 @@ QF_maGiC_VALUE if test "x$HAVE_JACK" = "xyes"; then AC_DEFINE(HAVE_JACK, 1, [Define if you have libjack]) SOUND_TYPES="$SOUND_TYPES JACK" - QF_NEED(snd_render, [jack]) + QF_NEED(snd_output, [jack]) + QF_NEED(snd_render, [default]) fi else AC_CHECK_LIB(jack, jack_client_open, HAVE_JACK=yes, HAVE_JACK=no, @@ -117,7 +114,8 @@ QF_maGiC_VALUE JACK_LIBS="-ljack" AC_DEFINE(HAVE_JACK, 1, [Define if you have libjack]) SOUND_TYPES="$SOUND_TYPES JACK" - QF_NEED(snd_render, [jack]) + QF_NEED(snd_output, [jack]) + QF_NEED(snd_render, [default]) fi fi fi @@ -240,26 +238,28 @@ QF_maGiC_VALUE fi dnl Win32 - if test "x$ac_cv_header_windows_h" = "xyes"; then - SOUND_TYPES="$SOUND_TYPES Win32" - QF_NEED(snd_output, [win]) - QF_NEED(snd_render, [default]) - WINSND_LIBS="-lwinmm" - if test "x$ac_cv_header_dsound_h" = "xyes"; then - AC_EGREP_CPP([QF_maGiC_VALUE], - [ -#include -#include -#ifdef GMEM_MOVEABLE -# ifdef DirectSoundEnumerate -QF_maGiC_VALUE -# endif -#endif - ], - SOUND_TYPES="$SOUND_TYPES DirectX" - QF_NEED(snd_output, [dx]) + if test "x$SYSTYPE" = xWIN32; then + if test "x$ac_cv_header_windows_h" = "xyes"; then + SOUND_TYPES="$SOUND_TYPES Win32" + QF_NEED(snd_output, [win]) QF_NEED(snd_render, [default]) - ) + WINSND_LIBS="-lwinmm" + if test "x$ac_cv_header_dsound_h" = "xyes"; then + AC_EGREP_CPP([QF_maGiC_VALUE], + [ + #include + #include + #ifdef GMEM_MOVEABLE + # ifdef DirectSoundEnumerate + QF_maGiC_VALUE + # endif + #endif + ], + SOUND_TYPES="$SOUND_TYPES DirectX" + QF_NEED(snd_output, [dx]) + QF_NEED(snd_render, [default]) + ) + fi fi fi AC_SUBST(WINSND_LIBS) diff --git a/config.d/svga.m4 b/config.d/svga.m4 index 7fc66aa90..fd52fda3f 100644 --- a/config.d/svga.m4 +++ b/config.d/svga.m4 @@ -1,7 +1,7 @@ dnl Checks for SVGALib support AC_ARG_WITH(svga, -[ --with-svga=DIR use SVGALib found in DIR], -HAVE_SVGA=$withval, HAVE_SVGA=auto) + AS_HELP_STRING([--with-svga@<:@=DIR@:>@], [use SVGALib found in DIR]), + HAVE_SVGA=$withval, HAVE_SVGA=auto) if test "x$HAVE_SVGA" != xno -a "x$HAVE_SVGA" != xauto; then if test "x$HAVE_SVGA" != xauto; then SVGA_CFLAGS="$SVGA_CFLAGS -I$withval/include" diff --git a/config.d/typedefs_structs_compiler.m4 b/config.d/typedefs_structs_compiler.m4 index 3ba73b7d6..c38e7c56c 100644 --- a/config.d/typedefs_structs_compiler.m4 +++ b/config.d/typedefs_structs_compiler.m4 @@ -5,29 +5,19 @@ dnl ================================================================== AC_C_CONST AC_C_INLINE AC_TYPE_SIZE_T -AC_STRUCT_ST_BLKSIZE -AC_HEADER_TIME AC_STRUCT_TM if test "x$ac_cv_header_unistd_h" = xyes; then AC_MSG_CHECKING(for _SC_PAGESIZE) -AC_TRY_COMPILE( - [#include ], - [int foo = _SC_PAGESIZE;], - AC_DEFINE(HAVE__SC_PAGESIZE,1,[Define this if you have _SC_PAGESIZE]) - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) -) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int foo = _SC_PAGESIZE;]])],[AC_DEFINE(HAVE__SC_PAGESIZE,1,Define this if you have _SC_PAGESIZE) + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) +]) fi AC_MSG_CHECKING(for __attribute__) -AC_TRY_COMPILE( - [static __attribute__ ((unused)) const char *foo = "bar";], - [], - AC_DEFINE(HAVE___ATTRIBUTE__) - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) -) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[static __attribute__ ((unused)) const char *foo = "bar";]], [[]])],[AC_DEFINE(HAVE___ATTRIBUTE__) + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) +]) AH_VERBATIM([HAVE___ATTRIBUTE__], [/* Define this if the GCC __attribute__ keyword is available */ #undef HAVE___ATTRIBUTE__ @@ -36,14 +26,10 @@ AH_VERBATIM([HAVE___ATTRIBUTE__], #endif]) AC_MSG_CHECKING(for __attribute__ ((visibility))) -AC_TRY_COMPILE( - [void foo (void); - __attribute__ ((sivibility ("default"))) void foo (void) {}], - [], - AC_DEFINE(HAVE___ATTRIBUTE__VISIBILITY) - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) -) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[void foo (void); + __attribute__ ((sivibility ("default"))) void foo (void) {}]], [[]])],[AC_DEFINE(HAVE___ATTRIBUTE__VISIBILITY) + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) +]) AH_VERBATIM([HAVE___ATTRIBUTE__VISIBILITY], [/* Define this if the GCC visibility __attribute__ is available */ #undef HAVE___ATTRIBUTE__VISIBILITY @@ -55,14 +41,10 @@ AH_VERBATIM([HAVE___ATTRIBUTE__VISIBILITY], if test "x$SYSTYPE" = "xWIN32"; then AC_MSG_CHECKING(for __attribute__ ((gcc_struct))) - AC_TRY_COMPILE( - [typedef struct { int foo; } - __attribute__ ((gcc_struct)) gcc_struct_test;], - [], - AC_DEFINE(HAVE___ATTRIBUTE__GCC_STRUCT) - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) - ) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[typedef struct { int foo; } + __attribute__ ((gcc_struct)) gcc_struct_test;]], [[]])],[AC_DEFINE(HAVE___ATTRIBUTE__GCC_STRUCT) + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) + ]) fi AH_VERBATIM([HAVE___ATTRIBUTE__GCC_STRUCT], [/* Define this if the GCC gcc_struct __attribute__ is available */ @@ -74,13 +56,9 @@ AH_VERBATIM([HAVE___ATTRIBUTE__GCC_STRUCT], #endif]) AC_MSG_CHECKING(for __builtin_expect) -AC_TRY_COMPILE( - [long (*foo) (long, long) = __builtin_expect;], - [], - AC_DEFINE(HAVE___BUILTIN_EXPECT) - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) -) +AC_LINK_IFELSE([AC_LANG_PROGRAM([[int x;]], [[if (__builtin_expect(!x, 1)) {}]])],[AC_DEFINE(HAVE___BUILTIN_EXPECT) + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) +]) AH_VERBATIM([HAVE___BUILTIN_EXPECT], [/* Define this if the GCC __builtin_expect keyword is available */ #undef HAVE___BUILTIN_EXPECT @@ -89,78 +67,47 @@ AH_VERBATIM([HAVE___BUILTIN_EXPECT], #endif]) AC_MSG_CHECKING(for type of fpos_t) -AC_TRY_COMPILE( - [#include ], - [fpos_t x = 0], - AC_MSG_RESULT(off_t), - AC_DEFINE(HAVE_FPOS_T_STRUCT, 1, [Define this if FPOS_T is a struct]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[fpos_t x = 0]])],[AC_MSG_RESULT(off_t)],[AC_DEFINE(HAVE_FPOS_T_STRUCT, 1, Define this if FPOS_T is a struct) AC_MSG_RESULT(struct) -) +]) AC_MSG_CHECKING(for socklen_t in sys/types.h) -AC_TRY_COMPILE( - [#include ], - [ socklen_t x = 0;], - AC_DEFINE(HAVE_SOCKLEN_T, 1, [Define this if your system has socklen_t]) - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[socklen_t x = 0;]])],[AC_DEFINE(HAVE_SOCKLEN_T, 1, Define this if your system has socklen_t) + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) dnl FreeBSD 4.0 has it in sys/socket.h AC_MSG_CHECKING(for socklen_t in sys/socket.h) - AC_TRY_COMPILE( - [#include - #include ], - [ socklen_t x = 0;], - AC_DEFINE(HAVE_SOCKLEN_T, 1, [Define this if your system has socklen_t]) AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) - ) -) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include + #include ]], [[socklen_t x = 0;]])],[AC_DEFINE(HAVE_SOCKLEN_T, 1, Define this if your system has socklen_t) + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) + ]) +]) if test "x$ac_cv_header_sys_uio_h" = xyes; then AC_MSG_CHECKING(for struct in_pktinfo) - AC_TRY_COMPILE( - [#include + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include #include #include - #include ], - [struct in_pktinfo x;], - AC_DEFINE(HAVE_IN_PKTINFO, 1, [Define this if your system has struct in_pktinfo]) - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) - ) + #include ]], [[struct in_pktinfo x;]])],[AC_DEFINE(HAVE_IN_PKTINFO, 1, Define this if your system has struct in_pktinfo) + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) + ]) fi AC_MSG_CHECKING(for size_t in sys/types.h) -AC_TRY_COMPILE( - [#include ], - [ size_t x = 0;], - AC_DEFINE(HAVE_SIZE_T, 1, [Define this if your system has size_t]) AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) -) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ size_t x = 0;]])],[AC_DEFINE(HAVE_SIZE_T, 1, Define this if your system has size_t) AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) +]) dnl maybe these two (at least the 2nd) should be checked only if ipv6 is enabled? AC_MSG_CHECKING(for ss_len in struct sockaddr_storage) -AC_TRY_COMPILE( - [#include - #include ], - [ void f(void) { struct sockaddr_storage ss; ss.ss_len=0; }], - AC_DEFINE(HAVE_SS_LEN, 1, [Define this if you have ss_len member in struct sockaddr_storage (BSD)]) AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) -) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include + #include ]], [[ void f(void) { struct sockaddr_storage ss; ss.ss_len=0; }]])],[AC_DEFINE(HAVE_SS_LEN, 1, Define this if you have ss_len member in struct sockaddr_storage (BSD)) AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) +]) AC_MSG_CHECKING(for sin6_len in struct sockaddr_in6) -AC_TRY_COMPILE( - [#include - #include ], - [ void f(void) { struct sockaddr_in6 s6; s6.sin6_len=0; }], - AC_DEFINE(HAVE_SIN6_LEN, 1, [Define this if you have sin6_len member in struct sockaddr_in6 (BSD)]) AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) -) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include + #include ]], [[ void f(void) { struct sockaddr_in6 s6; s6.sin6_len=0; }]])],[AC_DEFINE(HAVE_SIN6_LEN, 1, Define this if you have sin6_len member in struct sockaddr_in6 (BSD)) AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) +]) AC_MSG_CHECKING(for sa_len in struct sockaddr) -AC_TRY_COMPILE( - [#include - #include ], - [ void f(void) { struct sockaddr sa; sa.sa_len=0; }], - AC_DEFINE(HAVE_SA_LEN, 1, [Define this if you have sa_len member in struct sockaddr (BSD)]) AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) -) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include + #include ]], [[ void f(void) { struct sockaddr sa; sa.sa_len=0; }]])],[AC_DEFINE(HAVE_SA_LEN, 1, Define this if you have sa_len member in struct sockaddr (BSD)) AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) +]) diff --git a/config.d/versions.m4 b/config.d/versions.m4 index 7550013d6..91297048f 100644 --- a/config.d/versions.m4 +++ b/config.d/versions.m4 @@ -15,8 +15,9 @@ AC_DEFINE_UNQUOTED(QW_VERSION, "$QW_VERSION", AC_DEFINE_UNQUOTED(QW_QSG_VERSION, "$QW_QSG_VERSION", [Define this to the QSG standard version you support in QuakeWorld]) -AC_ARG_ENABLE([version-info], AC_HELP_STRING([--enable-version-info=CURRENT:REVISION:AGE], - [Override the value passed to libtool -version-info.]), +AC_ARG_ENABLE([version-info], + AS_HELP_STRING([--enable-version-info=CURRENT:REVISION:AGE], + [override the value passed to libtool -version-info.]), [], [enable_version_info=1:0:0]) QUAKE_LIBRARY_VERSION_INFO=$enable_version_info AC_SUBST([QUAKE_LIBRARY_VERSION_INFO]) diff --git a/config.d/vulkan.m4 b/config.d/vulkan.m4 new file mode 100644 index 000000000..c5baba273 --- /dev/null +++ b/config.d/vulkan.m4 @@ -0,0 +1,34 @@ +dnl Check for vulkan support +AC_ARG_ENABLE(vulkan, + AS_HELP_STRING([--disable-vulkan], [do not use Vulkan]), + HAVE_VULKAN=$enable_vulkan, HAVE_VULKAN=auto) +if test "x$HAVE_VULKAN" != xno; then + save_CPPFLAGS="$CPPFLAGS" + AS_IF([test x"$VULKAN_SDK" != x], [ + CPPFLAGS="$CPPFLAGS -I$VULKAN_SDK/include" + LDFLAGS="$LDFLAGS -L$VULKAN_SDK/lib" + glslangvalidator="$VULKAN_SDK/bin/glslangValidator" + ], [glslangvalidator="glslangValidator"]) + AC_CHECK_HEADER([vulkan/vulkan.h], [ + HAVE_VULKAN=yes + AS_IF([test x"$VULKAN_SDK" != x], [ + VULKAN_CPPFLAGS="-I$VULKAN_SDK/include" + VULKAN_QCFLAGS="-I$VULKAN_SDK/include" + VULKAN_LDFLAGS="-L$VULKAN_SDK/lib" + ],[ + VULKAN_QCFLAGS="-I${QCSYSPREFIX-/usr}/include" + ]) + ], [HAVE_VULKAN=no]) + CPPFLAGS="$save_CPPFLAGS" +fi +if test "x$HAVE_VULKAN" = xyes; then + AC_DEFINE([HAVE_VULKAN], [1], [Define if yhou have the Vulkan libs]) +fi +AC_SUBST(VULKAN_LIBS) +AC_SUBST(VULKAN_CPPFLAGS) +AC_SUBST(VULKAN_QCFLAGS) +AC_SUBST(GLSLANGVALIDATOR, [$glslangvalidator]) + +AM_CONDITIONAL(X11_VULKAN, test "x$HAVE_VULKAN" = "xyes") +AM_CONDITIONAL(WIN_VULKAN, test "x$HAVE_VULKAN" = "xyes") +AM_CONDITIONAL(TEST_VULKAN, test "x$HAVE_VULKAN" = "xyes") diff --git a/config.d/windows.m4 b/config.d/windows.m4 index cc79255c0..d1e0b32a1 100644 --- a/config.d/windows.m4 +++ b/config.d/windows.m4 @@ -1,5 +1,6 @@ endian="" FNM_FLAGS="" + case "$host_os" in mingw32*) mingw=yes @@ -7,15 +8,18 @@ case "$host_os" in if test "x$host" != "x$build"; then case "$build_os" in cygwin*) - CFLAGS="$CFLAGS -mno-cygwin -mconsole" + CFLAGS="$CFLAGS -mconsole -D__USE_MINGW_ANSI_STDIO" CPPFLAGS="$CPPFLAGS $CFLAGS" ;; esac fi + SYSTYPE=WIN32 + AC_DEFINE(NEED_GNUPRINTF) endian="little" ;; cygwin*) cygwin=yes + AC_DEFINE(NEED_GNUPRINTF) if test "x$host" != "x$build"; then CC="$host_cpu-$host_os-gcc" AS="$CC" @@ -26,3 +30,12 @@ case "$host_os" in ;; esac AC_SUBST(FNM_FLAGS) + +AH_VERBATIM([NEED_GNUPRINTF], +[/* Define this if gnu_prinf is needed instead of printf for format attributes*/ +#undef NEED_GNUPRINTF +#ifdef NEED_GNUPRINTF +# define PRINTF gnu_printf +#else +# define PRINTF printf +#endif]) diff --git a/config.d/x11.m4 b/config.d/x11.m4 index 8a9d24fda..b8a780ee4 100644 --- a/config.d/x11.m4 +++ b/config.d/x11.m4 @@ -14,7 +14,8 @@ fi dnl Check for XFree86-VidMode support AC_ARG_ENABLE(vidmode, -[ --disable-vidmode do not use XFree86 VidMode extension], + AS_HELP_STRING([--disable-vidmode], + [do not use XFree86 VidMode extension]), HAVE_VIDMODE=$enable_vidmode, HAVE_VIDMODE=auto) if test "x$HAVE_VIDMODE" != xno; then save_CPPFLAGS="$CPPFLAGS" @@ -33,10 +34,50 @@ if test "x$HAVE_VIDMODE" != xno; then fi AC_SUBST(VIDMODE_LIBS) +dnl Check for XInput2 support +AC_ARG_ENABLE(xi2, + AS_HELP_STRING([--disable-xi2], [do not use Xorg XInput2 extension]), + HAVE_XI2=$enable_xi2, HAVE_XI2=auto) +if test "x$HAVE_XI2" != xno; then + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$X_CFLAGS $CPPFLAGS" + AC_CHECK_HEADER(X11/extensions/XInput2.h, + dnl Make sure the library works + [AC_CHECK_LIB(Xi, XIQueryVersion, + AC_DEFINE(HAVE_XI2, 1, [Define if you have the Xorg XInput2 extension]) + XI2_LIBS="-lXi",, + [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS] + )],, + [#include ] + ) + CPPFLAGS="$save_CPPFLAGS" +fi +AC_SUBST(XI2_LIBS) + +dnl Check for XInput2 support +AC_ARG_ENABLE(xfixes, + AS_HELP_STRING([--disable-xfixes], [do not use Xorg Xfixes extension]), + HAVE_XFIXES=$enable_xfixes, HAVE_XFIXES=auto) +if test "x$HAVE_XFIXES" != xno; then + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$X_CFLAGS $CPPFLAGS" + AC_CHECK_HEADER(X11/extensions/Xfixes.h, + dnl Make sure the library works + [AC_CHECK_LIB(Xi, XIQueryVersion, + AC_DEFINE(HAVE_XFIXES, 1, [Define if you have the Xorg Xfixes extension]) + XFIXES_LIBS="-lXfixes",, + [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS] + )],, + [#include ] + ) + CPPFLAGS="$save_CPPFLAGS" +fi +AC_SUBST(XFIXES_LIBS) + dnl Check for DGA support AC_ARG_ENABLE(dga, -[ --disable-dga do not use XFree86 DGA extension], -HAVE_DGA=$enable_dga, HAVE_DGA=auto) + AS_HELP_STRING([--disable-dga], [do not use XFree86 DGA extension]), + HAVE_DGA=$enable_dga, HAVE_DGA=auto) if test "x$HAVE_DGA" != xno; then save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$X_CFLAGS $CPPFLAGS" diff --git a/configure.ac b/configure.ac index ea203472c..886b2675f 100644 --- a/configure.ac +++ b/configure.ac @@ -1,9 +1,9 @@ dnl Process this file with autoconf to produce a configure script. -AC_PREREQ(2.61) +AC_PREREQ([2.69]) dnl This is the only place where the package name and version appear -AC_INIT([QuakeForge], m4_esyscmd([config.d/git-version-gen --prefix '' .tarball-version])) -AM_INIT_AUTOMAKE([foreign]) +AC_INIT([QuakeForge],m4_esyscmd(config.d/git-version-gen --prefix '' .tarball-version)) +AM_INIT_AUTOMAKE([foreign subdir-objects]) dnl LT_INIT messes with CFLAGS (evil bastard) if test "x${CFLAGS-unset}" = xunset; then @@ -11,7 +11,7 @@ if test "x${CFLAGS-unset}" = xunset; then fi saved_CFLAGS="$CFLAGS" dnl LT_INIT([win32-dll]) -AM_PROG_LIBTOOL +LT_INIT CFLAGS="$saved_CFLAGS" AC_REVISION([$Revision$]) dnl @@ -23,7 +23,7 @@ AC_CANONICAL_HOST m4_include(config.d/versions.m4) -AC_LANG_C +AC_LANG([C]) if test "$x{AR-unset}" = xunset; then AR="ar" @@ -63,19 +63,22 @@ m4_include(config.d/compression.m4) m4_include(config.d/mgl.m4) m4_include(config.d/fbdev.m4) m4_include(config.d/svga.m4) +m4_include(config.d/vulkan.m4) m4_include(config.d/x11.m4) m4_include(config.d/sdl.m4) m4_include(config.d/curses.m4) +m4_include(config.d/freetype.m4) dnl ================================================================== dnl Checks for system type dnl ================================================================== dnl Checks for which system driver to use + AC_MSG_CHECKING(for system driver) case "${host}" in - i?86-*-mingw32*|x86_64-w64-mingw32) + i?86-*-mingw32*|x86_64-w64-mingw32*) SYSTYPE=WIN32 AC_MSG_RESULT([Win32 driver]) WIN32_LIBS=' $(NET_LIBS)' @@ -88,8 +91,8 @@ case "${host}" in fi ;; - i?86-*-cygwin*) - SYSTYPE=WIN32 + i?86-*-cygwin*|x86_64-*-cygwin*) + #SYSTYPE=WIN32 AC_MSG_RESULT([Win32 driver]) WIN32_LIBS=' $(NET_LIBS)' ;; @@ -109,6 +112,7 @@ AC_SUBST(WIN32_LIBS) m4_include(config.d/sound.m4) m4_include(config.d/joystick.m4) +m4_include(config.d/evdev.m4) m4_include(config.d/cdrom.m4) m4_include(config.d/networking.m4) @@ -120,8 +124,8 @@ m4_include(config.d/qfcc.m4) m4_include(config.d/compiling.m4) AC_ARG_ENABLE(static-doc, - [ --enable-static-doc Enable generation of doxygen docs for static] - [ functions.]) + AS_HELP_STRING([--enable-static-doc], + [enable generation of doxygen docs for static functions.])) STATIC_DOC=NO if test "x$enable_static_doc" = xyes; then STATIC_DOC=YES @@ -129,7 +133,8 @@ fi AC_SUBST(STATIC_DOC) AC_ARG_ENABLE(typecheck-progs, - [ --enable-typecheck-progs Enable type checking on progs field access]) + AS_HELP_STRING([--enable-typecheck-progs], + [enable type checking on progs field access])) if test "x$enable_typecheck_progs" = xyes; then AC_DEFINE(TYPECHECK_PROGS, 1, [Define this if you want progs typechecking]) fi diff --git a/debian/Makefile.am b/debian/Makefile.am deleted file mode 100644 index ec13849c4..000000000 --- a/debian/Makefile.am +++ /dev/null @@ -1,23 +0,0 @@ -## Process this file with automake to produce Makefile.in -AUTOMAKE_OPTIONS= foreign - -# find -type f | sed -e 's:^\./::g' | grep -v Makefile | sort >> Makefile.am - -EXTRA_DIST= \ - changelog control copyright quakeforge.conf rules source/format \ - qfcc.install \ - quakeforge-alsa.install quakeforge-alsa.postinst quakeforge-alsa.prerm \ - quakeforge-common.install quakeforge-common.postinst \ - quakeforge-dev.install \ - quakeforge-gl.install \ - quakeforge-glsl.install \ - quakeforge-jack.install \ - quakeforge-maptools.install \ - quakeforge-oss.install quakeforge-oss.postinst quakeforge-oss.prerm \ - quakeforge-sdl.install \ - quakeforge-servers.install \ - quakeforge-stub.postinst \ - quakeforge-sw32.install \ - quakeforge-sw.install \ - quakeforge-utils.install \ - quakeforge-x11.install diff --git a/debian/Makemodule.am b/debian/Makemodule.am new file mode 100644 index 000000000..9ef80d06a --- /dev/null +++ b/debian/Makemodule.am @@ -0,0 +1,28 @@ +EXTRA_DIST += \ + debian/changelog \ + debian/control \ + debian/copyright \ + debian/quakeforge.conf \ + debian/rules \ + debian/source/format \ + debian/qfcc.install \ + debian/quakeforge-alsa.install \ + debian/quakeforge-alsa.postinst \ + debian/quakeforge-alsa.prerm \ + debian/quakeforge-common.install \ + debian/quakeforge-common.postinst \ + debian/quakeforge-dev.install \ + debian/quakeforge-gl.install \ + debian/quakeforge-glsl.install \ + debian/quakeforge-jack.install \ + debian/quakeforge-maptools.install \ + debian/quakeforge-oss.install \ + debian/quakeforge-oss.postinst \ + debian/quakeforge-oss.prerm \ + debian/quakeforge-sdl.install \ + debian/quakeforge-servers.install \ + debian/quakeforge-stub.postinst \ + debian/quakeforge-sw32.install \ + debian/quakeforge-sw.install \ + debian/quakeforge-utils.install \ + debian/quakeforge-x11.install diff --git a/desktop/Makefile.am b/desktop/Makefile.am deleted file mode 100644 index 5aa61578b..000000000 --- a/desktop/Makefile.am +++ /dev/null @@ -1,49 +0,0 @@ -## Process this file with automake to produce Makefile.in -# -# Makefile.am -# -# Automake-using build system for QuakeForge -# -# Copyright (C) 2000 Jeff Teunissen -# -# This Makefile is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# -# See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to: -# -# Free Software Foundation, Inc. -# 59 Temple Place - Suite 330 -# Boston, MA 02111-1307, USA -# -# $Id$ -# - -AUTOMAKE_OPTIONS= foreign - -# Stuff that is common to both client and server -# Desktop files -if HAVE_XDG -desktopdir=$(datarootdir)/applications -desktop_DATA= @NQ_DESKTOP_DATA@ @QW_DESKTOP_DATA@ -endif - -# Stuff that doesn't get linked into an executable NEEDS to be mentioned here, -# or it won't be distributed with 'make dist' - -EXTRA_DIST= quakeforge-nq-glx.desktop quakeforge-nq-sdl.desktop \ - quakeforge-nq-sdl32.desktop quakeforge-nq-sgl.desktop \ - quakeforge-nq-x11.desktop quakeforge-qw-glx.desktop \ - quakeforge-qw-sdl.desktop quakeforge-qw-sdl32.desktop \ - quakeforge-qw-sgl.desktop quakeforge-qw-x11.desktop - -# Kill the temp files, hopefully. -CLEANFILES = *.i *.s $(YACCLEX_CLEANFILES) diff --git a/desktop/Makemodule.am b/desktop/Makemodule.am new file mode 100644 index 000000000..bb0acd969 --- /dev/null +++ b/desktop/Makemodule.am @@ -0,0 +1,10 @@ +if HAVE_XDG +desktopdir=$(datarootdir)/applications +desktop_DATA = @NQ_DESKTOP_DATA@ @QW_DESKTOP_DATA@ +endif + +EXTRA_DIST += \ + desktop/quakeforge-nq-sdl.desktop \ + desktop/quakeforge-nq-x11.desktop \ + desktop/quakeforge-qw-sdl.desktop \ + desktop/quakeforge-qw-x11.desktop diff --git a/desktop/quakeforge-nq-glx.desktop b/desktop/quakeforge-nq-glx.desktop deleted file mode 100644 index 67c4ca509..000000000 --- a/desktop/quakeforge-nq-glx.desktop +++ /dev/null @@ -1,18 +0,0 @@ -[Desktop Entry] -Version=1.0 - -Type=Application - -Name=QuakeForge (GLX) -GenericName=Quake (GLX) -Comment=A first person shooter game -Comment[fr]=Un jeu de tir subjectif -Comment[it]=Sparatutto in prima persona -Icon=quake - -Categories=Game;ActionGame; - -Exec=nq-glx -TryExec=nq-glx -Terminal=false -StartupNotify=false diff --git a/desktop/quakeforge-nq-sdl32.desktop b/desktop/quakeforge-nq-sdl32.desktop deleted file mode 100644 index f39050155..000000000 --- a/desktop/quakeforge-nq-sdl32.desktop +++ /dev/null @@ -1,18 +0,0 @@ -[Desktop Entry] -Version=1.0 - -Type=Application - -Name=QuakeForge (SDL32) -GenericName=Quake (SDL32) -Comment=A first person shooter game -Comment[fr]=Un jeu de tir subjectif -Comment[it]=Sparatutto in prima persona -Icon=quake - -Categories=SDL;Game;ActionGame; - -Exec=nq-sdl32 -TryExec=nq-sdl32 -Terminal=false -StartupNotify=false diff --git a/desktop/quakeforge-nq-sgl.desktop b/desktop/quakeforge-nq-sgl.desktop deleted file mode 100644 index 82645eeb6..000000000 --- a/desktop/quakeforge-nq-sgl.desktop +++ /dev/null @@ -1,18 +0,0 @@ -[Desktop Entry] -Version=1.0 - -Type=Application - -Name=QuakeForge (SDL GL) -GenericName=Quake (SDL GL) -Comment=A first person shooter game -Comment[fr]=Un jeu de tir subjectif -Comment[it]=Sparatutto in prima persona -Icon=quake - -Categories=SDL;Game;ActionGame; - -Exec=nq-sgl -TryExec=nq-sgl -Terminal=false -StartupNotify=false diff --git a/desktop/quakeforge-qw-glx.desktop b/desktop/quakeforge-qw-glx.desktop deleted file mode 100644 index fab7cab4e..000000000 --- a/desktop/quakeforge-qw-glx.desktop +++ /dev/null @@ -1,18 +0,0 @@ -[Desktop Entry] -Version=1.0 - -Type=Application - -Name=QuakeForge QuakeWorld (GLX) -GenericName=QuakeWorld (GLX) -Comment=A first person shooter game -Comment[fr]=Un jeu de tir subjectif -Comment[it]=Sparatutto in prima persona -Icon=quake - -Categories=Game;ActionGame; - -Exec=qw-client-glx -TryExec=qw-client-glx -Terminal=false -StartupNotify=false diff --git a/desktop/quakeforge-qw-sdl32.desktop b/desktop/quakeforge-qw-sdl32.desktop deleted file mode 100644 index d2862ecb6..000000000 --- a/desktop/quakeforge-qw-sdl32.desktop +++ /dev/null @@ -1,18 +0,0 @@ -[Desktop Entry] -Version=1.0 - -Type=Application - -Name=QuakeForge QuakeWorld (SDL32) -GenericName=QuakeWorld (SDL32) -Comment=A first person shooter game -Comment[fr]=Un jeu de tir subjectif -Comment[it]=Sparatutto in prima persona -Icon=quake - -Categories=SDL;Game;ActionGame; - -Exec=qw-client-sdl32 -TryExec=qw-client-sdl32 -Terminal=false -StartupNotify=false diff --git a/desktop/quakeforge-qw-sgl.desktop b/desktop/quakeforge-qw-sgl.desktop deleted file mode 100644 index 36ae08f24..000000000 --- a/desktop/quakeforge-qw-sgl.desktop +++ /dev/null @@ -1,18 +0,0 @@ -[Desktop Entry] -Version=1.0 - -Type=Application - -Name=QuakeForge QuakeWorld (SDL GL) -GenericName=QuakeWorld (SDL GL) -Comment=A first person shooter game -Comment[fr]=Un jeu de tir subjectif -Comment[it]=Sparatutto in prima persona -Icon=quake - -Categories=SDL;Game;ActionGame; - -Exec=qw-client-sgl -TryExec=qw-client-sgl -Terminal=false -StartupNotify=false diff --git a/doc/Makefile.am b/doc/Makefile.am deleted file mode 100644 index 8511d72f3..000000000 --- a/doc/Makefile.am +++ /dev/null @@ -1,55 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -SUBDIRS= man - -DOX= \ - bind.dox config.dox connect.dox cshifts.dox dirconf.dox faq.dox \ - filesystem.dox mapformat.dox property-list.dox qtv.dox quakeforge.dox \ - qw-cap-spec.dox qw-download-spec.dox sound.dox specifications.dox \ - surround-sound.dox timestamps.dox - -GIB= \ - gib/GIB.lyx gib/break.gib gib/continue.gib gib/curly.gib gib/examples.sh \ - gib/for.gib gib/gib_head.eps gib/if-chain.gib gib/if-else.gib \ - gib/if-simple.gib gib/while.gib - -PROGS= \ - progs/vm-exec.c progs/vm-mem.fig - -EXTRA_DIST= qf.ico \ - \ - skybox.fig template.c template.h \ - quakeforge.dox.conf.in \ - \ - ${DOX} ${GIB} ${PROGS} \ - \ - config/glspeed-v1.cfg config/glspeed-v3.cfg config/swspeed.cfg \ - \ - config/gib/adjustvolume.gib config/gib/infobot.gib config/gib/ln.gib \ - config/gib/qfadmin.gib config/gib/sshot.gib config/gib/zoom.gib \ - \ - qtv/qwtv.fig - -SUFFIXES=.eps .fig .png -.fig.png: - @mkdir -p `dirname $@` - fig2dev -L png $< $@ - -.fig.eps: - @mkdir -p `dirname $@` - fig2dev -L ps $< $@ - -.fig.svg: - @mkdir -p `dirname $@` - fig2dev -L svg $< $@ - -clean-local: - -rm -fr doxygen - -progs/vm-mem.svg: progs/vm-mem.fig -progs/vm-mem.eps: progs/vm-mem.fig -qtv/qwtv.svg: qtv/qwtv.fig -qtv/qwtv.eps: qtv/qwtv.fig - -doc: quakeforge.dox.conf progs/vm-mem.svg progs/vm-mem.eps qtv/qwtv.svg qtv/qwtv.eps ${DOX} - doxygen quakeforge.dox.conf diff --git a/doc/Makemodule.am b/doc/Makemodule.am new file mode 100644 index 000000000..91d9270b7 --- /dev/null +++ b/doc/Makemodule.am @@ -0,0 +1,81 @@ +man_MANS += doc/man/quakeforge.1 + +DOX= \ + doc/bind.dox \ + doc/config.dox \ + doc/connect.dox \ + doc/cshifts.dox \ + doc/dirconf.dox \ + doc/faq.dox \ + doc/filesystem.dox \ + doc/mapformat.dox \ + doc/property-list.dox \ + doc/qtv.dox \ + doc/quakeforge.dox \ + doc/qw-cap-spec.dox \ + doc/qw-download-spec.dox \ + doc/sound.dox \ + doc/specifications.dox \ + doc/surround-sound.dox \ + doc/timestamps.dox + +GIB= \ + doc/gib/GIB.lyx \ + doc/gib/break.gib \ + doc/gib/continue.gib \ + doc/gib/curly.gib \ + doc/gib/examples.sh \ + doc/gib/for.gib \ + doc/gib/gib_head.eps \ + doc/gib/if-chain.gib \ + doc/gib/if-else.gib \ + doc/gib/if-simple.gib \ + doc/gib/while.gib + +PROGS= \ + doc/progs/vm-exec.c \ + doc/progs/vm-mem.fig + +EXTRA_DIST += \ + ${DOX} ${GIB} ${PROGS} \ + doc/qf.ico \ + doc/skybox.fig \ + doc/template.c \ + doc/template.h \ + doc/quakeforge.dox.conf.in \ + doc/config/glspeed-v1.cfg \ + doc/config/glspeed-v3.cfg \ + doc/config/swspeed.cfg \ + doc/config/gib/adjustvolume.gib \ + doc/config/gib/infobot.gib \ + doc/config/gib/ln.gib \ + doc/config/gib/qfadmin.gib \ + doc/config/gib/sshot.gib \ + doc/config/gib/zoom.gib \ + doc/man/quakeforge.1 \ + doc/qtv/qwtv.fig + +SUFFIXES += .eps .fig .png +.fig.png: + @mkdir -p `dirname $@` + fig2dev -L png $< $@ + +.fig.eps: + @mkdir -p `dirname $@` + fig2dev -L ps $< $@ + +.fig.svg: + @mkdir -p `dirname $@` + fig2dev -L svg $< $@ + +clean-local: + -rm -fr doxygen + +doc/progs/vm-mem.svg: doc/progs/vm-mem.fig +doc/progs/vm-mem.eps: doc/progs/vm-mem.fig +doc/qtv/qwtv.svg: doc/qtv/qwtv.fig +doc/qtv/qwtv.eps: doc/qtv/qwtv.fig + +.PHONY: doc +doc: doc/quakeforge.dox.conf doc/progs/vm-mem.svg doc/progs/vm-mem.eps doc/qtv/qwtv.svg doc/qtv/qwtv.eps ${DOX} + doxygen doc/quakeforge.dox.conf diff --git a/doc/config.dox b/doc/config.dox index 33b8a5524..14d860694 100644 --- a/doc/config.dox +++ b/doc/config.dox @@ -28,7 +28,7 @@ whitespace. This allows commands and arguments to contain \c + and \c - so long as the preceeding character is not whitespace, but also prevents command line switches (eg \c -nosound) from becoming part of the console command. Using careful quoting, it is even possible for a command or -argument to beging with \c + or \c -. +argument to begin with \c + or \c -. The following command line will fail to set m_pitch because the -0.022 will not be part of the console command. usage: setrom \ diff --git a/doc/connect.dox b/doc/connect.dox index 44d83586a..14295294e 100644 --- a/doc/connect.dox +++ b/doc/connect.dox @@ -3,38 +3,39 @@ /** \page connection_sequence QW Connection Sequence \msc -Client,Server; -Client=>Server [label = "getchallenge\n"]; -Server=>Client [label = "c[challenge][ext]"]; -Client=>Server [label = "connect [protover] [qport] [challenge] [userinfo]"]; -Server=>Client [label = "j"]; -Client->Server [label = "[clc_stringcmd]new"]; -Server->Client [label = "[svc_serverdata][data]"]; -|||; -Client->Server [label = "[clc_stringcmd]soundlist [svcount] 0"]; ---- [label = "soundlist loop start"]; -Server->Client [label = "[svc_soundlist][data][next]"]; -Client->Server [label = "[clc_stringcmd]soundlist [svcount] [next]"]; ---- [label = "soundlist loop end"]; -Server->Client [label = "[svc_soundlist][data]0"]; -|||; -Client->Server [label = "[clc_stringcmd]modellist [svcount] 0"]; ---- [label = "modellist list loop start"]; -Server->Client [label = "[svc_modellist][data][next]"]; -Client->Server [label = "[clc_stringcmd]modellist [svcount] [next]"]; ---- [label = "modellist list loop end"]; -Server->Client [label = "[svc_modellist][data]0"]; -|||; -Client->Server [label = "[clc_stringcmd]prespawn [svcount] [n=0] [wcsum]"]; ---- [label = "prespawn loop start"]; -Server->Client [label = "[signon buffer n][stuffcmd]prespawn..."]; -Client->Server [label = "[clc_stringcmd]prespawn [svcount] [n]"]; ---- [label = "prespawn loop end"]; -Server->Client [label = "[signon buffer n][stuffcmd]spawn..."]; -|||; -Client->Server [label = "[clc_stringcmd]spawn [svcount] 0"]; -Server->Client [label = "[spawn info][stuffcmd]skins"]; -Client->Server [label = "[clc_stringcmd]begin [svcount]"]; -#... [label = "in game message sequence"]; +Client [linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black], +Server [linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Client=>Server [label = "getchallenge\n", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Server=>Client [label = "c[challenge][ext]", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Client=>Server [label = "connect [protover] [qport] [challenge] [userinfo]", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Server=>Client [label = "j", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Client->Server [label = "[clc_stringcmd]new", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Server->Client [label = "[svc_serverdata][data]", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +||| [linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Client->Server [label = "[clc_stringcmd]soundlist [svcount] 0", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +--- [label = "soundlist loop start", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Server->Client [label = "[svc_soundlist][data][next]", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Client->Server [label = "[clc_stringcmd]soundlist [svcount] [next]", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +--- [label = "soundlist loop end", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Server->Client [label = "[svc_soundlist][data]0", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +||| [linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Client->Server [label = "[clc_stringcmd]modellist [svcount] 0", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +--- [label = "modellist list loop start", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Server->Client [label = "[svc_modellist][data][next]", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Client->Server [label = "[clc_stringcmd]modellist [svcount] [next]", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +--- [label = "modellist list loop end", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Server->Client [label = "[svc_modellist][data]0", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +||| [linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Client->Server [label = "[clc_stringcmd]prespawn [svcount] [n=0] [wcsum]", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +--- [label = "prespawn loop start", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Server->Client [label = "[signon buffer n][stuffcmd]prespawn...", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Client->Server [label = "[clc_stringcmd]prespawn [svcount] [n]", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +--- [label = "prespawn loop end", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Server->Client [label = "[signon buffer n][stuffcmd]spawn...", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +||| [linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Client->Server [label = "[clc_stringcmd]spawn [svcount] 0", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Server->Client [label = "[spawn info][stuffcmd]skins", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +Client->Server [label = "[clc_stringcmd]begin [svcount]", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; +... [label = "in game message sequence", linecolor=white, textcolor=white, arclinecolor=white, arctextcolor=white, textbgcolor=black, arctextbgcolor=black]; \endmsc */ diff --git a/doc/dirconf.dox b/doc/dirconf.dox index 4b23454e6..927aa3099 100644 --- a/doc/dirconf.dox +++ b/doc/dirconf.dox @@ -98,7 +98,7 @@ Supported attributes are: QF. -Attribute allow variable substitution. Variables can take the form of +Attributes allow variable substitution. Variables can take the form of either \$NAME or \${NAME}, where the latter is useful when the end of \c NAME is ambiguous (eg, \${NAME}STUFF vs \$NAME/STUFF). diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am deleted file mode 100644 index 5249e3a26..000000000 --- a/doc/man/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -## Process this file with automake to produce Makefile.in -AUTOMAKE_OPTIONS= foreign - -EXTRA_DIST= quakeforge.1 diff --git a/doc/progs/vm-exec.c b/doc/progs/vm-exec.c index 0234c2158..b250848cd 100644 --- a/doc/progs/vm-exec.c +++ b/doc/progs/vm-exec.c @@ -5,7 +5,7 @@ call_progs_main (progs_t *pr, int argc, const char **argv) { int i; dfunction_t *dfunc; - func_t progs_main = 0; + pr_func_t progs_main = 0; string_t *pr_argv; if ((dfunc = PR_FindFunction (pr, "main"))) { @@ -23,6 +23,7 @@ call_progs_main (progs_t *pr, int argc, const char **argv) PR_RESET_PARAMS (pr); P_INT (pr, 0) = argc; P_POINTER (pr, 1) = PR_SetPointer (pr, pr_argv); + pr->pr_argc = 2; PR_ExecuteProgram (pr, progs_main); PR_PopFrame (pr); PR_Zone_Free (pr, pr_argv); diff --git a/doc/qtv.dox b/doc/qtv.dox index 90aba3bc8..9327891be 100644 --- a/doc/qtv.dox +++ b/doc/qtv.dox @@ -10,7 +10,7 @@ The server is "reserved" for playing clients. Any spectators would be referees and/or admins (however, this is up to the server admin). A qtv proxy connects to the server. The proxy may have spectating clients -and other even other proxies connecting to it. The proxies can chain to any +and even other proxies connecting to it. The proxies can chain to any depth as an upstream proxy will pass on its information to any downstream proxies. With just a few levels of proxies, it should be possible to have very large spectator groups. diff --git a/doc/quakeforge.dox.conf.in b/doc/quakeforge.dox.conf.in index 9db22207d..76b963cb4 100644 --- a/doc/quakeforge.dox.conf.in +++ b/doc/quakeforge.dox.conf.in @@ -1,4 +1,4 @@ -# Doxyfile 1.8.9.1 +# Doxyfile 1.8.16 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -17,11 +17,11 @@ # Project related configuration options #--------------------------------------------------------------------------- -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 @@ -93,6 +93,14 @@ ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. @@ -179,6 +187,16 @@ SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus @@ -226,16 +244,15 @@ TAB_SIZE = 4 # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) ALIASES = QF=QuakeForge -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all @@ -264,17 +281,26 @@ OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: -# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: -# Fortran. In the later case the parser tries to guess whether the code is fixed -# or free formatted code, this is the default for Fortran type files), VHDL. For -# instance to make doxygen treat .inc files as Fortran files (default is PHP), -# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is +# Fortran), use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # @@ -285,7 +311,7 @@ EXTENSION_MAPPING = no_extension=C # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. +# documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. @@ -293,6 +319,15 @@ EXTENSION_MAPPING = no_extension=C MARKDOWN_SUPPORT = YES +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or @@ -318,7 +353,7 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. @@ -343,6 +378,13 @@ IDL_PROPERTY_SUPPORT = YES DISTRIBUTE_GROUP_DOC = NO +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = YES + # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent @@ -417,6 +459,12 @@ EXTRACT_ALL = YES EXTRACT_PRIVATE = NO +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. @@ -437,7 +485,7 @@ EXTRACT_STATIC = @STATIC_DOC@ EXTRACT_LOCAL_CLASSES = NO -# This flag is only useful for Objective-C code. When set to YES, local methods, +# This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. @@ -495,7 +543,7 @@ INTERNAL_DOCS = NO # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. +# (including Cygwin) ands Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = NO @@ -682,7 +730,7 @@ LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. @@ -727,11 +775,18 @@ WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. WARN_NO_PARAMDOC = NO +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated @@ -755,7 +810,7 @@ WARN_LOGFILE = # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with -# spaces. +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = @TOPSRC@/include \ @@ -770,7 +825,7 @@ INPUT = @TOPSRC@/include \ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of # possible encodings. # The default value is: UTF-8. @@ -778,12 +833,17 @@ INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank the -# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, -# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, -# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, -# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, -# *.qsf, *.as and *.js. +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.c \ *.h \ @@ -871,9 +931,8 @@ EXAMPLE_RECURSIVE = NO # \image command). IMAGE_PATH = @TOPSRC@/doc \ - @builddir@ \ - @builddir@/progs \ - @builddir@/qtv + @builddir@/doc/progs \ + @builddir@/doc/qtv # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program @@ -889,6 +948,10 @@ IMAGE_PATH = @TOPSRC@/doc \ # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. INPUT_FILTER = @@ -898,6 +961,10 @@ INPUT_FILTER = # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. FILTER_PATTERNS = @@ -950,7 +1017,7 @@ INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. +# entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO @@ -982,12 +1049,12 @@ SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version +# (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # @@ -1015,7 +1082,7 @@ VERBATIM_HEADERS = NO # rich C++ code for which doxygen's built-in parser lacks the necessary type # information. # Note: The availability of this option depends on whether or not doxygen was -# compiled with the --with-libclang option. +# generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO @@ -1028,6 +1095,16 @@ CLANG_ASSISTED_PARSING = NO CLANG_OPTIONS = +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files +# were built. This is equivalent to specifying the "-p" option to a clang tool, +# such as clang-check. These options will then be passed to the parser. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -1039,13 +1116,6 @@ CLANG_OPTIONS = ALPHABETICAL_INDEX = YES -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 4 - # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored @@ -1146,7 +1216,7 @@ HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. @@ -1182,6 +1252,17 @@ HTML_COLORSTYLE_GAMMA = 100 HTML_TIMESTAMP = NO +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via Javascript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have Javascript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. @@ -1205,13 +1286,13 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# environment (see: https://developer.apple.com/xcode/), introduced with OSX +# 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1250,7 +1331,7 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output @@ -1326,7 +1407,7 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1334,7 +1415,7 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1343,7 +1424,7 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1351,7 +1432,7 @@ QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1359,7 +1440,7 @@ QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = @@ -1452,7 +1533,7 @@ EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # @@ -1464,7 +1545,7 @@ FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering +# https://www.mathjax.org) which uses client side Javascript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path @@ -1472,7 +1553,7 @@ FORMULA_TRANSPARENT = YES # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -USE_MATHJAX = NO +USE_MATHJAX = YES # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: @@ -1491,8 +1572,8 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest @@ -1553,7 +1634,7 @@ SERVER_BASED_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). +# Xapian (see: https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1566,7 +1647,7 @@ EXTERNAL_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). See the section "External Indexing and +# Xapian (see: https://xapian.org/). See the section "External Indexing and # Searching" for details. # This tag requires that the tag SEARCHENGINE is set to YES. @@ -1618,21 +1699,35 @@ LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. # -# Note that when enabling USE_PDFLATEX this option is only used for generating -# bitmaps for formulas in the HTML output, but not in the Makefile that is -# written to the output directory. -# The default file is: latex. +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate # index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). # The default file is: makeindex. # This tag requires that the tag GENERATE_LATEX is set to YES. MAKEINDEX_CMD_NAME = makeindex +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. In case there is no backslash (\) as first character +# it will be automatically added in the LaTeX code. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = makeindex + # If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. @@ -1648,12 +1743,15 @@ COMPACT_LATEX = YES # The default value is: a4. # This tag requires that the tag GENERATE_LATEX is set to YES. -PAPER_TYPE = a4wide +PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names -# that should be included in the LaTeX output. To get the times font for -# instance you can specify -# EXTRA_PACKAGES=times +# that should be included in the LaTeX output. The package can be specified just +# by its name or with the correct syntax as to be used with the LaTeX +# \usepackage command. To get the times font for instance you can specify : +# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} +# To use the option intlimits with the amsmath package you can specify: +# EXTRA_PACKAGES=[intlimits]{amsmath} # If left blank no extra packages will be included. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1750,12 +1848,28 @@ LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See -# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. # The default value is: plain. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_BIB_STYLE = plain +# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_TIMESTAMP = NO + +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EMOJI_DIRECTORY = + #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- @@ -1795,9 +1909,9 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's config -# file, i.e. a series of assignments. You only have to provide replacements, -# missing definitions are set to their default value. +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. # # See also section "Doxygen usage" for information on how to generate the # default style sheet that doxygen normally uses. @@ -1806,8 +1920,8 @@ RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is -# similar to doxygen's config file. A template extensions file can be generated -# using doxygen -e rtf extensionFile. +# similar to doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_EXTENSIONS_FILE = @@ -1893,6 +2007,13 @@ XML_OUTPUT = xml XML_PROGRAMLISTING = YES +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_NS_MEMB_FILE_SCOPE = NO + #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- @@ -1925,9 +2046,9 @@ DOCBOOK_PROGRAMLISTING = NO #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an -# AutoGen Definitions (see http://autogen.sf.net) file that captures the -# structure of the code including all documentation. Note that this feature is -# still experimental and incomplete at the moment. +# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# the structure of the code including all documentation. Note that this feature +# is still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO @@ -2010,10 +2131,10 @@ SEARCH_INCLUDES = YES # This tag requires that the tag SEARCH_INCLUDES is set to YES. INCLUDE_PATH = @TOPSRC@/include \ - @TOPSRC@/hw/include \ @TOPSRC@/nq/include \ @TOPSRC@/qw/include \ - @TOPSRC@/qtv/include + @TOPSRC@/qtv/include \ + @TOPSRC@ # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the @@ -2032,6 +2153,7 @@ INCLUDE_FILE_PATTERNS = *.h # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. PREDEFINED = "__attribute__(x)=" \ + IN_DOXYGEN=1 \ VISIBLE= # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this @@ -2101,12 +2223,6 @@ EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of 'which perl'). -# The default file (with absolute path) is: /usr/bin/perl. - -PERL_PATH = /usr/bin/perl - #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- @@ -2120,15 +2236,6 @@ PERL_PATH = /usr/bin/perl CLASS_DIAGRAMS = YES -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see: -# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. @@ -2207,7 +2314,7 @@ COLLABORATION_GRAPH = YES # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -GROUP_GRAPHS = NO +GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES, doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling @@ -2261,7 +2368,8 @@ INCLUDED_BY_GRAPH = YES # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected -# functions only using the \callgraph command. +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2272,7 +2380,8 @@ CALL_GRAPH = NO # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected -# functions only using the \callergraph command. +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2295,13 +2404,17 @@ GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = NO # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# http://www.graphviz.org/)). # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). # Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd, # png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo, -# gif:cairo:gd, gif:gd, gif:gd:gd and svg. +# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo, +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# png:gdiplus:gdiplus. # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2352,6 +2465,11 @@ DIAFILE_DIRS = PLANTUML_JAR_PATH = +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + # When using plantuml, the specified paths are searched for files specified by # the !include statement in a plantuml block. diff --git a/hw/Makefile.am b/hw/Makefile.am deleted file mode 100644 index 77e3cbf5b..000000000 --- a/hw/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -## Process this file with automake to produce Makefile.in -AUTOMAKE_OPTIONS= foreign - -SUBDIRS= include source diff --git a/hw/Makemodule.am b/hw/Makemodule.am new file mode 100644 index 000000000..cfd2f1e31 --- /dev/null +++ b/hw/Makemodule.am @@ -0,0 +1 @@ +include hw/source/Makemodule.am diff --git a/hw/include/Makefile.am b/hw/include/Makefile.am deleted file mode 100644 index b36df17ad..000000000 --- a/hw/include/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -## Process this file with automake to produce Makefile.in -AUTOMAKE_OPTIONS= foreign - -EXTRA_DIST = diff --git a/hw/source/Makefile.am b/hw/source/Makemodule.am similarity index 69% rename from hw/source/Makefile.am rename to hw/source/Makemodule.am index d5ad0658e..9f86d3283 100644 --- a/hw/source/Makefile.am +++ b/hw/source/Makemodule.am @@ -27,27 +27,17 @@ # $Id$ # -AUTOMAKE_OPTIONS= foreign +bin_PROGRAMS += @HW_TARGETS@ -# Stuff that is common to both client and server -AM_CPPFLAGS= -I$(top_srcdir)/include -I$(top_srcdir)/hw/include -SDL_LIBS = @SDL_LIBS@ +EXTRA_PROGRAMS += hw-master -bin_PROGRAMS= @HW_TARGETS@ - -EXTRA_PROGRAMS= hw-master - -common_ldflags= -export-dynamic hw_master_libs= \ - $(top_builddir)/libs/net/libnet_chan.la \ + libs/net/libnet_chan.la \ @server_static_plugin_libs@ \ - $(top_builddir)/libs/console/libQFconsole.la \ - $(top_builddir)/libs/util/libQFutil.la + libs/console/libQFconsole.la \ + libs/util/libQFutil.la hw_master_LDFLAGS= $(common_ldflags) hw_master_LDADD= $(hw_master_libs) $(NET_LIBS) hw_master_DEPENDENCIES= $(hw_master_libs) -hw_master_SOURCES= master.c - -# Kill the temp files, hopefully. -CLEANFILES = *.i *.s +hw_master_SOURCES= hw/source/master.c diff --git a/hw/source/master.c b/hw/source/master.c index 4617e3c7d..83e91332c 100644 --- a/hw/source/master.c +++ b/hw/source/master.c @@ -55,13 +55,21 @@ typedef struct server_s { double timeout; } server_t; -static cvar_t *sv_console_plugin; +static char *sv_console_plugin; +static cvar_t sv_console_plugin_cvar = { + .name = "sv_console_plugin", + .description = + "Plugin used for the console", + .default_value = "server", + .flags = CVAR_ROM, + .value = { .type = 0, .value = &sv_console_plugin }, +}; SERVER_PLUGIN_PROTOS static plugin_list_t server_plugin_list[] = { SERVER_PLUGIN_LIST }; -qboolean is_server = true; +bool is_server = true; static cbuf_t *mst_cbuf; @@ -121,7 +129,7 @@ FL_Add (filter_t * filter) filter_list = filter; } -static filter_t * +static __attribute__((pure)) filter_t * FL_Find (netadr_t adr) { filter_t *filter; @@ -209,7 +217,7 @@ SVL_Add (server_t *sv) sv_list = sv; } -static server_t * +static __attribute__((pure)) server_t * SVL_Find (netadr_t adr) { server_t *sv; @@ -469,13 +477,10 @@ SV_WriteFilterList (void) } static void -SV_Shutdown (void) +SV_Shutdown (void *data) { - NET_Shutdown (); - // write filter list SV_WriteFilterList (); - Con_Shutdown (); } static void @@ -525,7 +530,7 @@ main (int argc, const char **argv) mst_cbuf = Cbuf_New (&id_interp); - Sys_RegisterShutdown (SV_Shutdown); + Sys_RegisterShutdown (SV_Shutdown, 0); Sys_Init (); @@ -538,12 +543,12 @@ main (int argc, const char **argv) PI_Init (); - sv_console_plugin = Cvar_Get ("sv_console_plugin", "server", - CVAR_ROM, 0, "Plugin used for the console"); + Cvar_Register (&sv_console_plugin_cvar, 0, 0); PI_RegisterPlugins (server_plugin_list); - Con_Init (sv_console_plugin->string); + Con_Load (sv_console_plugin); if (con_module) con_module->data->console->cbuf = mst_cbuf; + Con_Init (); con_list_print = Sys_Printf; SV_InitNet (); diff --git a/include/Makefile.am b/include/Makefile.am deleted file mode 100644 index 56b084ae1..000000000 --- a/include/Makefile.am +++ /dev/null @@ -1,26 +0,0 @@ -## Process this file with automake to produce Makefile.in -AUTOMAKE_OPTIONS= foreign -SUBDIRS = QF -EXTRA_DIST = \ - adivtab.h alsa_funcs_list.h anorm_dots.h anorms.h asm_draw.h asm_i386.h \ - block16.h block8.h buildnum.h clview.h compat.h context_sdl.h \ - context_x11.h d_iface.h d_ifacea.h d_local.h dga_check.h exp.h fbset.h \ - garbage.h getopt.h gib_buffer.h gib_builtin.h gib_classes.h \ - gib_execute.h gib_function.h gib_handle.h gib_object.h gib_parse.h \ - gib_process.h gib_regex.h gib_semantics.h gib_thread.h gib_tree.h \ - gib_vars.h gl_warp_sin.h in_win.h logos.h mod_internal.h net_dgrm.h \ - net_loop.h net_udp.h net_vcr.h net_wins.h netchan.h netmain.h \ - noisetextures.h old_keys.h ops.h pstdint.h qfalloca.h qstring.h \ - quakeasm.h r_cvar.h r_dynamic.h r_internal.h r_local.h r_screen.h \ - r_shared.h regex.h rua_internal.h sbar.h skin_stencil.h snd_internal.h \ - sv_console.h varrays.h vgamodes.h vid_internal.h vregset.h winquake.h \ - world.h \ - \ - client/entities.h \ - \ - qw/bothdefs.h qw/msg_backbuf.h qw/msg_ucmd.h qw/pmove.h qw/protocol.h \ - \ - win32/dirent.h win32/fnmatch.h win32/stdint.h \ - \ - win32/resources/icon1Vista.ico win32/resources/icon1XP.ico \ - win32/resources/quakeforge.rc win32/resources/resource.h diff --git a/include/Makemodule.am b/include/Makemodule.am new file mode 100644 index 000000000..30d6f71e9 --- /dev/null +++ b/include/Makemodule.am @@ -0,0 +1,110 @@ +## Process this file with automake to produce Makefile.in +include include/QF/Makemodule.am + +EXTRA_DIST += \ + include/adivtab.h \ + include/alsa_funcs_list.h \ + include/anorm_dots.h \ + include/anorms.h \ + include/asm_draw.h \ + include/asm_i386.h \ + include/block16.h \ + include/block8.h \ + include/bsearch.h \ + include/buildnum.h \ + include/compat.h \ + include/context_sdl.h \ + include/context_win.h \ + include/context_x11.h \ + include/d_iface.h \ + include/d_ifacea.h \ + include/d_local.h \ + include/dga_check.h \ + include/exp.h \ + include/fbset.h \ + include/gamedefs.h \ + include/garbage.h \ + include/getopt.h \ + include/gib_buffer.h \ + include/gib_builtin.h \ + include/gib_classes.h \ + include/gib_execute.h \ + include/gib_function.h \ + include/gib_handle.h \ + include/gib_object.h \ + include/gib_parse.h \ + include/gib_process.h \ + include/gib_regex.h \ + include/gib_semantics.h \ + include/gib_thread.h \ + include/gib_tree.h \ + include/gib_vars.h \ + include/gl_warp_sin.h \ + include/in_win.h \ + include/in_x11.h \ + include/logos.h \ + include/mod_internal.h \ + include/net_dgrm.h \ + include/net_loop.h \ + include/net_udp.h \ + include/net_vcr.h \ + include/net_wins.h \ + include/netchan.h \ + include/netmain.h \ + include/noisetextures.h \ + include/old_keys.h \ + include/ops.h \ + include/pstdint.h \ + include/qfalloca.h \ + include/qfselect.h \ + include/qstring.h \ + include/quakeasm.h \ + include/quicksort.h \ + include/r_cvar.h \ + include/r_dynamic.h \ + include/r_font.h \ + include/r_internal.h \ + include/r_local.h \ + include/r_scrap.h \ + include/r_shared.h \ + include/r_text.h \ + include/regex.h \ + include/rua_internal.h \ + include/skin_stencil.h \ + include/snd_internal.h \ + include/sv_console.h \ + include/varrays.h \ + include/vgamodes.h \ + include/vid_gl.h \ + include/vid_internal.h \ + include/vid_sw.h \ + include/vid_vulkan.h \ + include/vregset.h \ + include/winquake.h \ + include/world.h \ + include/client/chase.h \ + include/client/effects.h \ + include/client/entities.h \ + include/client/hud.h \ + include/client/input.h \ + include/client/locs.h \ + include/client/particles.h \ + include/client/sbar.h \ + include/client/screen.h \ + include/client/state.h \ + include/client/temp_entities.h \ + include/client/view.h \ + include/client/world.h \ + include/evdev/hotplug.h \ + include/evdev/inputlib.h \ + include/qw/bothdefs.h \ + include/qw/msg_backbuf.h \ + include/qw/msg_ucmd.h \ + include/qw/pmove.h \ + include/qw/protocol.h \ + include/win32/dirent.h \ + include/win32/fnmatch.h \ + include/win32/resources/icon1Vista.ico \ + include/win32/resources/icon1XP.ico \ + include/win32/resources/quakeforge.rc \ + include/win32/resources/resource.h diff --git a/include/QF/GL/defines.h b/include/QF/GL/defines.h index d690559e1..522e289c3 100644 --- a/include/QF/GL/defines.h +++ b/include/QF/GL/defines.h @@ -830,4 +830,43 @@ # define GL_OPERAND1_ALPHA 0x8599 # define GL_OPERAND2_ALPHA 0x859A +/* framebuffer */ +#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#define GL_COLOR_ATTACHMENT2 0x8CE2 +#define GL_COLOR_ATTACHMENT3 0x8CE3 +#define GL_COLOR_ATTACHMENT4 0x8CE4 +#define GL_COLOR_ATTACHMENT5 0x8CE5 +#define GL_COLOR_ATTACHMENT6 0x8CE6 +#define GL_COLOR_ATTACHMENT7 0x8CE7 +#define GL_COLOR_ATTACHMENT8 0x8CE8 +#define GL_COLOR_ATTACHMENT9 0x8CE9 +#define GL_COLOR_ATTACHMENT10 0x8CEA +#define GL_COLOR_ATTACHMENT11 0x8CEB +#define GL_COLOR_ATTACHMENT12 0x8CEC +#define GL_COLOR_ATTACHMENT13 0x8CED +#define GL_COLOR_ATTACHMENT14 0x8CEE +#define GL_COLOR_ATTACHMENT15 0x8CEF +#define GL_COLOR_ATTACHMENT16 0x8CF0 +#define GL_COLOR_ATTACHMENT17 0x8CF1 +#define GL_COLOR_ATTACHMENT18 0x8CF2 +#define GL_COLOR_ATTACHMENT19 0x8CF3 +#define GL_COLOR_ATTACHMENT20 0x8CF4 +#define GL_COLOR_ATTACHMENT21 0x8CF5 +#define GL_COLOR_ATTACHMENT22 0x8CF6 +#define GL_COLOR_ATTACHMENT23 0x8CF7 +#define GL_COLOR_ATTACHMENT24 0x8CF8 +#define GL_COLOR_ATTACHMENT25 0x8CF9 +#define GL_COLOR_ATTACHMENT26 0x8CFA +#define GL_COLOR_ATTACHMENT27 0x8CFB +#define GL_COLOR_ATTACHMENT28 0x8CFC +#define GL_COLOR_ATTACHMENT29 0x8CFD +#define GL_COLOR_ATTACHMENT30 0x8CFE +#define GL_COLOR_ATTACHMENT31 0x8CFF +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_FRAMEBUFFER 0x8D40 + + #endif // __gl_defines_h diff --git a/include/QF/GL/extensions.h b/include/QF/GL/extensions.h index be9fe5dfb..5e76c1e35 100644 --- a/include/QF/GL/extensions.h +++ b/include/QF/GL/extensions.h @@ -38,6 +38,7 @@ # include # define GLAPIENTRY WINAPI # undef LoadImage +# undef E_POINTER # else # ifdef APIENTRY # define GLAPIENTRY APIENTRY @@ -325,7 +326,7 @@ typedef void *(GLAPIENTRY *QF_glXGetProcAddressARB) (const GLubyte *procName); typedef const GLubyte *(GLAPIENTRY *QF_wglGetExtensionsStringEXT) (void); /* QuakeForge extension functions */ -qboolean QFGL_ExtensionPresent (const char *); +bool QFGL_ExtensionPresent (const char *); void *QFGL_ExtensionAddress (const char *); #endif // __qfgl_ext_h_ diff --git a/include/QF/GL/funcs.h b/include/QF/GL/funcs.h index dbbb2c679..b02710ef1 100644 --- a/include/QF/GL/funcs.h +++ b/include/QF/GL/funcs.h @@ -42,8 +42,6 @@ #undef QFGL_NEED #undef QFGL_WANT -qboolean GLF_Init (void); -qboolean GLF_FindFunctions (void); -void *QFGL_ProcAddress (void *handle, const char *name, qboolean); +bool GLF_FindFunctions (void); #endif // __QF_GL_funcs_h_ diff --git a/include/QF/hl.h b/include/QF/GL/qf_alias.h similarity index 60% rename from include/QF/hl.h rename to include/QF/GL/qf_alias.h index 286768cc0..1fbfbbb94 100644 --- a/include/QF/hl.h +++ b/include/QF/GL/qf_alias.h @@ -1,9 +1,12 @@ /* - hl_bsp.h + qf_alias.h - Half Life file definitions + GL specific alias model stuff - Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2012 Bill Currie + + Author: Bill Currie + Date: 2012/1/1 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -24,17 +27,18 @@ Boston, MA 02111-1307, USA */ +#ifndef __QF_GL_qf_alias_h +#define __QF_GL_qf_alias_h +#include "QF/GL/types.h" -#ifndef _HL_BSP_H -#define _HL_BSP_H +typedef struct aliasvrt_s { + GLshort st[2]; + GLshort normal[3]; + GLushort vertex[3]; +} aliasvrt_t; -#include "QF/qtypes.h" +struct entity_s; +void gl_R_DrawAliasModel (struct entity_s ent); -extern void CL_ParseEntityLump(const char *entdata); -extern void HL_Mod_LoadLighting (lump_t *l); -extern void HL_Mod_LoadTextures (lump_t *l); -extern byte *W_GetTexture(const char *name, int matchwidth, int matchheight); -extern void W_LoadTextureWadFile (const char *filename, int complain); - -#endif // _HL_BSP_H +#endif//__QF_GL_qf_alias_h diff --git a/include/QF/GL/qf_draw.h b/include/QF/GL/qf_draw.h index 5644e6a74..a34bf6694 100644 --- a/include/QF/GL/qf_draw.h +++ b/include/QF/GL/qf_draw.h @@ -28,8 +28,43 @@ #ifndef __gl_draw_h #define __gl_draw_h +struct qpic_s; +struct font_s; +struct draw_charbuffer_s; + +void gl_Draw_Init (void); +void gl_Draw_Shutdown (void); +void gl_Draw_CharBuffer (int x, int y, struct draw_charbuffer_s *buffer); +void gl_Draw_SetScale (int scale); +void gl_Draw_Character (int x, int y, unsigned ch); +void gl_Draw_String (int x, int y, const char *str); +void gl_Draw_nString (int x, int y, const char *str, int count); +void gl_Draw_AltString (int x, int y, const char *str); +void gl_Draw_ConsoleBackground (int lines, byte alpha); +void gl_Draw_Crosshair (void); +void gl_Draw_CrosshairAt (int ch, int x, int y); +void gl_Draw_TileClear (int x, int y, int w, int h); +void gl_Draw_Fill (int x, int y, int w, int h, int c); +void gl_Draw_Line (int x0, int y0, int x1, int y1, int c); +void gl_Draw_TextBox (int x, int y, int width, int lines, byte alpha); +void gl_Draw_FadeScreen (void); +void gl_Draw_BlendScreen (quat_t color); +struct qpic_s *gl_Draw_CachePic (const char *path, bool alpha); +void gl_Draw_UncachePic (const char *path); +struct qpic_s *gl_Draw_MakePic (int width, int height, const byte *data); +void gl_Draw_DestroyPic (struct qpic_s *pic); +struct qpic_s *gl_Draw_PicFromWad (const char *name); +void gl_Draw_Pic (int x, int y, struct qpic_s *pic); +void gl_Draw_FitPic (int x, int y, int width, int height, struct qpic_s *pic); +void gl_Draw_Picf (float x, float y, struct qpic_s *pic); +void gl_Draw_SubPic(int x, int y, struct qpic_s *pic, + int srcx, int srcy, int width, int height); +int gl_Draw_AddFont (struct font_s *font); +void gl_Draw_Glyph (int x, int y, int fontid, int glyphid, int c); + void GL_Set2D (void); void GL_Set2DScaled (void); +void GL_End2D (void); void GL_DrawReset (void); void GL_FlushText (void); diff --git a/include/QF/GL/qf_fisheye.h b/include/QF/GL/qf_fisheye.h new file mode 100644 index 000000000..44a6f7234 --- /dev/null +++ b/include/QF/GL/qf_fisheye.h @@ -0,0 +1,37 @@ +/* + qf_fisheye.h + + GL screen fisheye + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/3/25 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __QF_GL_qf_fisheye_h +#define __QF_GL_qf_fisheye_h + +void gl_InitFisheye (void); +struct framebuffer_s; +void gl_FisheyeScreen (struct framebuffer_s *fb); + +#endif//__QF_GL_qf_fisheye_h diff --git a/include/QF/GL/qf_funcs_list.h b/include/QF/GL/qf_funcs_list.h index ebce38899..d30b5c873 100644 --- a/include/QF/GL/qf_funcs_list.h +++ b/include/QF/GL/qf_funcs_list.h @@ -154,6 +154,7 @@ QFGL_NEED (void, glFogfv, (GLenum pname, const GLfloat * params)) QFGL_NEED (void, glFogi, (GLenum pname, GLint param)) QFGL_DONT_NEED (void, glFogiv, (GLenum pname, const GLint * params)) QFGL_NEED (void, glFrontFace, (GLenum mode)) +QFGL_NEED (void, glGenerateMipmap, (GLenum target)) QFGL_NEED (void, glFrustum, (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near_val, GLdouble far_val)) QFGL_NEED (GLuint, glGenLists, (GLsizei range)) QFGL_NEED (void, glGenTextures, (GLsizei n, GLuint * textures)) @@ -416,7 +417,20 @@ QFGL_NEED (void, glVertexPointer, (GLint size, GLenum type, GLsizei stride, cons QFGL_NEED (void, glViewport, (GLint x, GLint y, GLsizei width, GLsizei height)) // ATI Extensions -QFGL_WANT (void, glPNTrianglesiATI, (GLenum pname, GLint param)) +QFGL_WANT (void, glPNTrianglesiATI, (GLint x, GLint y)) + +// frame buffers +QFGL_DONT_NEED (GLboolean, glIsFramebuffer, (GLuint framebuffer)) +QFGL_NEED (void, glBindFramebuffer, (GLenum target, GLuint framebuffer)) +QFGL_NEED (void, glDeleteFramebuffers, (GLsizei n, const GLuint *framebuffers)) +QFGL_NEED (void, glGenFramebuffers, (GLsizei n, GLuint *framebuffers)) +QFGL_DONT_NEED (GLenum, glCheckFramebufferStatus, (GLenum target)) +QFGL_DONT_NEED (void, glFramebufferTexture1D, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) +QFGL_NEED (void, glFramebufferTexture2D, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) +QFGL_DONT_NEED (void, glFramebufferTexture3D, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset)) +QFGL_DONT_NEED (void, glFramebufferRenderbuffer, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)) +QFGL_DONT_NEED (void, glGetFramebufferAttachmentParameteriv, (GLenum target, GLenum attachment, GLenum pname, GLint *params)) + #ifdef UNDEF_QFGL_DONT_NEED #undef QFGL_DONT_NEED diff --git a/include/QF/GL/qf_iqm.h b/include/QF/GL/qf_iqm.h index 47a77d6da..a65188670 100644 --- a/include/QF/GL/qf_iqm.h +++ b/include/QF/GL/qf_iqm.h @@ -44,6 +44,7 @@ typedef struct glsliqm_s { iqmvertexarray *color; } gliqm_t; -void gl_R_DrawIQMModel (entity_t *ent); +struct entity_s; +void gl_R_DrawIQMModel (struct entity_s ent); #endif//__QF_GL_qf_iqm_h diff --git a/include/QF/GL/qf_lightmap.h b/include/QF/GL/qf_lightmap.h index c048b181a..d5fa22586 100644 --- a/include/QF/GL/qf_lightmap.h +++ b/include/QF/GL/qf_lightmap.h @@ -28,6 +28,8 @@ #ifndef __QF_GL_lightmap_h #define __QF_GL_lightmap_h +#include "QF/GL/types.h" + // LordHavoc: since lightmaps are now allocated only as needed, allow a ridiculous number :) #define MAX_LIGHTMAPS 1024 #define BLOCK_WIDTH 64 @@ -40,16 +42,20 @@ typedef struct glRect_s { extern int lm_src_blend, lm_dest_blend; extern model_t *gl_currentmodel; -extern int gl_lightmap_textures; -extern qboolean gl_lightmap_modified[MAX_LIGHTMAPS]; -extern instsurf_t *gl_lightmap_polys[MAX_LIGHTMAPS]; +extern GLuint gl_lightmap_textures[MAX_LIGHTMAPS]; +extern bool gl_lightmap_modified[MAX_LIGHTMAPS]; +extern instsurf_t *gl_lightmap_polys; extern glRect_t gl_lightmap_rectchange[MAX_LIGHTMAPS]; -void GL_BuildSurfaceDisplayList (msurface_t *fa); void gl_lightmap_init (void); void GL_BuildLightmaps (struct model_s **models, int num_models); -void R_BlendLightmaps (void); -void R_CalcLightmaps (void); -extern void (*R_BuildLightMap) (msurface_t *surf); +void gl_R_BlendLightmaps (void); +void gl_R_CalcLightmaps (void); +struct transform_s; +extern void (*gl_R_BuildLightMap) (const vec4f_t *transform, + mod_brush_t *brush, msurface_t *surf); +int gl_R_LightmapTexture (void) __attribute__((pure)); +void gl_R_FlushLightmaps (void); + #endif // __QF_GL_lightmap_h diff --git a/include/QF/GL/qf_particles.h b/include/QF/GL/qf_particles.h new file mode 100644 index 000000000..9452fe454 --- /dev/null +++ b/include/QF/GL/qf_particles.h @@ -0,0 +1,46 @@ +/* + qf_particles.h + + GL specific particles stuff + + Copyright (C) 2012 Bill Currie + + Author: Bill Currie + Date: 2012/1/15 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __QF_GL_qf_particles_h +#define __QF_GL_qf_particles_h + +#include "QF/GL/types.h" + +typedef struct { + float texcoord[2]; + float vertex[3]; + byte color[4]; +} partvert_t; + +struct psystem_s; +void gl_R_DrawParticles (struct psystem_s *pssystem); +void gl_R_Particles_Init_Cvars (void); +void gl_R_InitParticles (void); + +#endif//__QF_GL_qf_particles_h diff --git a/include/QF/GL/qf_rlight.h b/include/QF/GL/qf_rlight.h index 0fdeaf2d8..66e719f78 100644 --- a/include/QF/GL/qf_rlight.h +++ b/include/QF/GL/qf_rlight.h @@ -29,6 +29,7 @@ #define __QF_GL_rlight_h extern float gl_bubble_sintable[], gl_bubble_costable[]; -void R_RenderDlights (void); +void gl_R_RenderDlights (void); +void gl_R_InitBubble (void); #endif // __QF_GL_rlight_h diff --git a/include/QF/GL/qf_rmain.h b/include/QF/GL/qf_rmain.h index 63735a926..6f3e7ce96 100644 --- a/include/QF/GL/qf_rmain.h +++ b/include/QF/GL/qf_rmain.h @@ -29,18 +29,30 @@ #define __QF_GL_rmain_h #include "QF/qtypes.h" -#include "QF/cvar.h" +#include "QF/simd/types.h" + +struct cvar_s; +struct entity_s; -extern qboolean gl_envmap; extern int c_brush_polys, c_alias_polys; -extern float r_world_matrix[16]; extern float gl_modelalpha; //extern vec3_t shadecolor; -extern void gl_multitexture_f (cvar_t *var); +extern void gl_multitexture_f (void *data, const struct cvar_s *var); void glrmain_init (void); -void R_RotateForEntity (struct entity_s *e); +void gl_R_RotateForEntity (const vec4f_t *mat); + +struct model_s; +struct entqueue_s; +struct scene_s; +void gl_R_NewScene (struct scene_s *scene); +void gl_R_RenderView (void); +void gl_R_RenderEntities (struct entqueue_s *queue); +void gl_R_ClearState (void); +void gl_R_ViewChanged (void); +void gl_R_LineGraph (int x, int y, int *h_vals, int count, int height); +void gl_R_InitGraphTextures (void); #endif // __QF_GL_rmain_h diff --git a/include/QF/GL/qf_rsurf.h b/include/QF/GL/qf_rsurf.h index 569fcdaf1..e42cdd56f 100644 --- a/include/QF/GL/qf_rsurf.h +++ b/include/QF/GL/qf_rsurf.h @@ -28,14 +28,35 @@ #ifndef __QF_GL_rsurf_h #define __QF_GL_rsurf_h -extern int gl_mirrortexturenum; // quake texturenum, not gltexturenum +typedef struct gltex_s { + struct texture_s *texture; + int gl_texturenum; + int gl_fb_texturenum; + struct instsurf_s *tex_chain; + struct instsurf_s **tex_chain_tail; +} gltex_t; -void gl_lightmap_init (void); -void GL_BuildLightmaps (struct model_s **models, int num_models); +struct model_s; +struct entity_s; +struct msurface_s; +struct mod_brush_s; -void R_DrawBrushModel (struct entity_s *e); -void R_DrawWorld (void); +void GL_BuildSurfaceDisplayList (struct mod_brush_s *brush, + struct msurface_s *fa); -void GL_EmitWaterPolys (msurface_t *fa); +void gl_R_DrawBrushModel (struct entity_s e); +void gl_R_DrawWorld (void); +void gl_R_DrawWaterSurfaces (void); + +void GL_EmitWaterPolys (struct msurface_s *fa); +void gl_R_LoadSkys (const char *sky); + +struct texture_s; +void gl_R_AddTexture (struct texture_s *tx); +void gl_R_ClearTextures (void); +void gl_R_InitSurfaceChains (struct mod_brush_s *brush); + +struct framebuffer_s; +void gl_WarpScreen (struct framebuffer_s *fb); #endif // __QF_GL_rsurf_h diff --git a/include/QF/GL/qf_sky.h b/include/QF/GL/qf_sky.h index 2524016b9..1aea4ea6f 100644 --- a/include/QF/GL/qf_sky.h +++ b/include/QF/GL/qf_sky.h @@ -30,13 +30,20 @@ #include "QF/qtypes.h" #include "QF/model.h" +#include "QF/GL/types.h" #define SKY_TEX 2000 // Quake 2 environment sky -extern qboolean gl_skyloaded; +extern bool gl_skyloaded; extern vec5_t gl_skyvec[6][4]; +extern GLuint gl_solidskytexture; +extern GLuint gl_alphaskytexture; -void R_DrawSky (void); -void R_DrawSkyChain (const instsurf_t *s); +struct texture_s; + +void gl_R_InitSky (struct texture_s *mt); +void gl_R_DrawSky (void); +void gl_R_DrawSkyChain (const instsurf_t *s); +void gl_R_LoadSkys (const char *skyname); #endif // __QF_GL_sky_h diff --git a/include/QF/GL/qf_sprite.h b/include/QF/GL/qf_sprite.h new file mode 100644 index 000000000..8050f9369 --- /dev/null +++ b/include/QF/GL/qf_sprite.h @@ -0,0 +1,37 @@ +/* + qf_sprite.h + + GL specific sprite model stuff + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/7/22 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __QF_GL_qf_sprite_h +#define __QF_GL_qf_sprite_h + +struct entity_s; +void gl_R_InitSprites (void); +extern void (*gl_R_DrawSpriteModel) (struct entity_s ent); + +#endif//__QF_GL_qf_sprite_h diff --git a/include/QF/GL/qf_textures.h b/include/QF/GL/qf_textures.h index aae32fb50..8ca16ebb5 100644 --- a/include/QF/GL/qf_textures.h +++ b/include/QF/GL/qf_textures.h @@ -29,6 +29,7 @@ #define __gl_textures_h #include "QF/qtypes.h" +#include "QF/GL/types.h" #define MAX_GLTEXTURES 2048 @@ -37,17 +38,32 @@ extern int gl_solid_format; extern int gl_lightmap_format; extern int gl_filter_min; extern int gl_filter_max; -extern qboolean gl_Anisotropy; +extern bool gl_Anisotropy; extern float gl_aniso; -extern int gl_part_tex; +extern GLuint gl_part_tex; +struct tex_s; -void GL_Upload8 (const byte *data, int width, int height, qboolean mipmap, qboolean alpha); -void GL_Upload8_EXT (const byte *data, int width, int height, qboolean mipmap, qboolean alpha); -int GL_LoadTexture (const char *identifier, int width, int height, const byte *data, qboolean mipmap, qboolean alpha, int bytesperpixel); +void GL_Upload8 (const byte *data, int width, int height, bool mipmap, bool alpha); +void GL_Upload8_EXT (const byte *data, int width, int height, bool mipmap, bool alpha); +int GL_LoadTexture (const char *identifier, int width, int height, const byte *data, bool mipmap, bool alpha, int bytesperpixel); +int GL_LoadTex (const char *identifier, int mips, struct tex_s *tex); int GL_FindTexture (const char *identifier); void GL_TextureMode_f (void); - +void GL_ReleaseTexture (int tex); void GDT_Init (void); +void GL_TextureInit (void); + +typedef struct scrap_s scrap_t; + +scrap_t *GL_CreateScrap (int size, int format, int linear); +void GL_DestroyScrap (scrap_t *scrap); +void GL_ScrapClear (scrap_t *scrap); +int GL_ScrapTexture (scrap_t *scrap) __attribute__((pure)); +struct subpic_s *GL_ScrapSubpic (scrap_t *scrap, int width, int height); +void GL_SubpicDelete (struct subpic_s *subpic); +void GL_SubpicUpdate (struct subpic_s *subpic, byte *data, int batch); +void GL_ScrapFlush (scrap_t *scrap); + #endif // __gl_textures_h diff --git a/include/QF/GL/qf_vid.h b/include/QF/GL/qf_vid.h index bb6291aa1..ea86acc2a 100644 --- a/include/QF/GL/qf_vid.h +++ b/include/QF/GL/qf_vid.h @@ -39,14 +39,13 @@ extern QF_glActiveTexture qglActiveTexture; extern QF_glMultiTexCoord2f qglMultiTexCoord2f; extern QF_glMultiTexCoord2fv qglMultiTexCoord2fv; extern int gl_mtex_active_tmus; -extern qboolean gl_mtex_capable; -extern qboolean gl_mtex_fullbright; +extern bool gl_mtex_capable; +extern bool gl_mtex_fullbright; extern GLenum gl_mtex_enum; -extern qboolean gl_combine_capable; +extern bool gl_combine_capable; extern float gl_rgb_scale; -extern int gl_texture_number; -extern qboolean gl_feature_mach64; +extern bool gl_feature_mach64; extern float gldepthmin, gldepthmax; extern int gl_use_bgra; extern int gl_tess; diff --git a/include/QF/GLSL/funcs.h b/include/QF/GLSL/funcs.h index 28ddf90a5..0f8a31092 100644 --- a/include/QF/GLSL/funcs.h +++ b/include/QF/GLSL/funcs.h @@ -43,8 +43,7 @@ #undef QFGL_NEED #undef QFGL_WANT -qboolean EGLF_Init (void); -qboolean EGLF_FindFunctions (void); -void *QFEGL_ProcAddress (void *handle, const char *name, qboolean); +bool EGLF_FindFunctions (void); +void *QFEGL_ProcAddress (void *handle, const char *name, bool); #endif // __QF_GLSL_funcs_h_ diff --git a/include/QF/GLSL/qf_alias.h b/include/QF/GLSL/qf_alias.h index 600d6be16..a7129547d 100644 --- a/include/QF/GLSL/qf_alias.h +++ b/include/QF/GLSL/qf_alias.h @@ -39,7 +39,8 @@ typedef struct aliasvrt_s { } aliasvrt_t; void glsl_R_InitAlias (void); -void glsl_R_DrawAlias (void); +struct entity_s; +void glsl_R_DrawAlias (struct entity_s ent); void glsl_R_AliasBegin (void); void glsl_R_AliasEnd (void); diff --git a/include/QF/GLSL/qf_bsp.h b/include/QF/GLSL/qf_bsp.h index 1ed5bba33..c9648962c 100644 --- a/include/QF/GLSL/qf_bsp.h +++ b/include/QF/GLSL/qf_bsp.h @@ -30,8 +30,19 @@ #ifndef __QF_GLSL_qf_bsp_h #define __QF_GLSL_qf_bsp_h +#include "QF/simd/types.h" #include "QF/GLSL/types.h" +typedef struct glsltex_s { + struct texture_s *texture; + int gl_texturenum; + int sky_tex[2]; + struct instsurf_s *tex_chain; + struct instsurf_s **tex_chain_tail; + struct elechain_s *elechain; + struct elechain_s **elechain_tail; +} glsltex_t; + typedef struct bspvert_s { quat_t vertex; quat_t tlst; @@ -47,17 +58,22 @@ typedef struct elements_s { typedef struct elechain_s { struct elechain_s *_next; struct elechain_s *next; - int index; + int model_index; elements_t *elements; - vec_t *transform; + vec4f_t *transform; float *color; } elechain_t; +struct model_s; + void glsl_R_ClearElements (void); void glsl_R_DrawWorld (void); void glsl_R_DrawSky (void); -void glsl_R_RegisterTextures (model_t **models, int num_models); -void glsl_R_BuildDisplayLists (model_t **models, int num_models); +void glsl_R_DrawWaterSurfaces (void); +void glsl_R_RegisterTextures (struct model_s **models, int num_models); +void glsl_R_BuildDisplayLists (struct model_s **models, int num_models); void glsl_R_InitBsp (void); +void glsl_R_ShutdownBsp (void); +void glsl_R_LoadSkys (const char *sky); #endif//__QF_GLSL_qf_bsp_h diff --git a/include/QF/GLSL/qf_draw.h b/include/QF/GLSL/qf_draw.h index aa708a96a..1868dbccc 100644 --- a/include/QF/GLSL/qf_draw.h +++ b/include/QF/GLSL/qf_draw.h @@ -25,8 +25,43 @@ */ -#ifndef __gl_draw_h -#define __gl_draw_h +#ifndef __QF_GLSL_qf_draw_h +#define __QF_GLSL_qf_draw_h + +struct qpic_s; +struct font_s; +struct draw_charbuffer_s; + +void glsl_Draw_Init (void); +void glsl_Draw_Shutdown (void); +void glsl_Draw_CharBuffer (int x, int y, struct draw_charbuffer_s *buffer); +void glsl_Draw_SetScale (int scale); +void glsl_Draw_Character (int x, int y, unsigned ch); +void glsl_Draw_String (int x, int y, const char *str); +void glsl_Draw_nString (int x, int y, const char *str, int count); +void glsl_Draw_AltString (int x, int y, const char *str); +void glsl_Draw_ConsoleBackground (int lines, byte alpha); +void glsl_Draw_Crosshair (void); +void glsl_Draw_CrosshairAt (int ch, int x, int y); +void glsl_Draw_TileClear (int x, int y, int w, int h); +void glsl_Draw_Fill (int x, int y, int w, int h, int c); +void glsl_Draw_Line (int x0, int y0, int x1, int y1, int c); +void glsl_LineGraph (int x, int y, int *h_vals, int count, int height); +void glsl_Draw_TextBox (int x, int y, int width, int lines, byte alpha); +void glsl_Draw_FadeScreen (void); +void glsl_Draw_BlendScreen (quat_t color); +struct qpic_s *glsl_Draw_CachePic (const char *path, bool alpha); +void glsl_Draw_UncachePic (const char *path); +struct qpic_s *glsl_Draw_MakePic (int width, int height, const byte *data); +void glsl_Draw_DestroyPic (struct qpic_s *pic); +struct qpic_s *glsl_Draw_PicFromWad (const char *name); +void glsl_Draw_Pic (int x, int y, struct qpic_s *pic); +void glsl_Draw_FitPic (int x, int y, int width, int height, struct qpic_s *pic); +void glsl_Draw_Picf (float x, float y, struct qpic_s *pic); +void glsl_Draw_SubPic(int x, int y, struct qpic_s *pic, + int srcx, int srcy, int width, int height); +int glsl_Draw_AddFont (struct font_s *font); +void glsl_Draw_Glyph (int x, int y, int fontid, int glyphid, int c); void GLSL_Set2D (void); void GLSL_Set2DScaled (void); @@ -34,4 +69,4 @@ void GLSL_End2D (void); void GLSL_DrawReset (void); void GLSL_FlushText (void); -#endif//__gl_draw_h +#endif//__QF_GLSL_qf_draw_h diff --git a/include/QF/GLSL/qf_fisheye.h b/include/QF/GLSL/qf_fisheye.h new file mode 100644 index 000000000..359606f25 --- /dev/null +++ b/include/QF/GLSL/qf_fisheye.h @@ -0,0 +1,37 @@ +/* + qf_fisheye.h + + GLSL screen fisheye + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/3/25 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __QF_GLSL_qf_fisheye_h +#define __QF_GLSL_qf_fisheye_h + +void glsl_InitFisheye (void); +struct framebuffer_s; +void glsl_FisheyeScreen (struct framebuffer_s *fb); + +#endif//__QF_GLSL_qf_fisheye_h diff --git a/include/QF/GLSL/qf_funcs_list.h b/include/QF/GLSL/qf_funcs_list.h index 7eab3ecdf..26adfea69 100644 --- a/include/QF/GLSL/qf_funcs_list.h +++ b/include/QF/GLSL/qf_funcs_list.h @@ -57,6 +57,8 @@ QFGL_NEED (void, glDepthMask, (GLboolean flag)) QFGL_NEED (void, glDepthRangef, (GLclampf zNear, GLclampf zFar)) QFGL_NEED (void, glDetachShader, (GLuint program, GLuint shader)) QFGL_NEED (void, glDisable, (GLenum cap)) +QFGL_NEED (void, glCreateVertexArrays, (GLsizei n, GLuint *arrays)) +QFGL_NEED (void, glBindVertexArray, (GLuint array)) QFGL_NEED (void, glDisableVertexAttribArray, (GLuint index)) QFGL_NEED (void, glDrawArrays, (GLenum mode, GLint first, GLsizei count)) QFGL_NEED (void, glDrawElements, (GLenum mode, GLsizei count, GLenum type, const GLvoid* indices)) diff --git a/include/QF/GLSL/qf_iqm.h b/include/QF/GLSL/qf_iqm.h index f37ac5f21..41ff0f1b8 100644 --- a/include/QF/GLSL/qf_iqm.h +++ b/include/QF/GLSL/qf_iqm.h @@ -40,7 +40,8 @@ typedef struct glsliqm_s { } glsliqm_t; void glsl_R_InitIQM (void); -void glsl_R_DrawIQM (void); +struct entity_s; +void glsl_R_DrawIQM (struct entity_s ent); void glsl_R_IQMBegin (void); void glsl_R_IQMEnd (void); diff --git a/include/QF/GLSL/qf_lightmap.h b/include/QF/GLSL/qf_lightmap.h index ef828d6d8..0fbc62bd7 100644 --- a/include/QF/GLSL/qf_lightmap.h +++ b/include/QF/GLSL/qf_lightmap.h @@ -33,11 +33,13 @@ #define BLOCK_WIDTH 64 #define BLOCK_HEIGHT 64 -void glsl_lightmap_init (void); +void glsl_Lightmap_Shutdown (void); +struct transform_s; void glsl_R_BuildLightmaps (struct model_s **models, int num_models); void glsl_R_CalcLightmaps (void); -extern void (*glsl_R_BuildLightMap) (msurface_t *surf); -int glsl_R_LightmapTexture (void); +extern void (*glsl_R_BuildLightMap) (const vec4f_t *transform, + mod_brush_t *brush, msurface_t *surf); +int glsl_R_LightmapTexture (void) __attribute__((pure)); void glsl_R_FlushLightmaps (void); #endif//__QF_GLSL_lightmap_h diff --git a/include/QF/GLSL/qf_main.h b/include/QF/GLSL/qf_main.h new file mode 100644 index 000000000..b51b64103 --- /dev/null +++ b/include/QF/GLSL/qf_main.h @@ -0,0 +1,40 @@ +/* + qf_main.h + + glsl main stuff from the renderer. + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_GLSL_qf_main_h +#define __QF_GLSL_qf_main_h + +struct entqueue_s; +struct scene_s; + +void glsl_R_NewScene (struct scene_s *scene); +void glsl_R_RenderEntities (struct entqueue_s *queue); +void glsl_R_RenderView (void); +void glsl_R_ClearState (void); +void glsl_R_ViewChanged (void); + +#endif//__QF_GLSL_qf_main_h diff --git a/include/QF/GLSL/qf_particles.h b/include/QF/GLSL/qf_particles.h index 0c42d218e..a6a4111f8 100644 --- a/include/QF/GLSL/qf_particles.h +++ b/include/QF/GLSL/qf_particles.h @@ -27,8 +27,8 @@ Boston, MA 02111-1307, USA */ -#ifndef __QF_GLSL_qf_bsp_h -#define __QF_GLSL_qf_bsp_h +#ifndef __QF_GLSL_qf_particles_h +#define __QF_GLSL_qf_particles_h #include "QF/GLSL/types.h" @@ -38,4 +38,10 @@ typedef struct { byte color[4]; } partvert_t; -#endif//__QF_GLSL_qf_bsp_h +struct psystem_s; +void glsl_R_DrawParticles (struct psystem_s *psystem); +void glsl_R_Particles_Init_Cvars (void); +void glsl_R_InitParticles (void); +void glsl_R_ShutdownParticles (void); + +#endif//__QF_GLSL_qf_particles_h diff --git a/include/QF/GLSL/qf_sprite.h b/include/QF/GLSL/qf_sprite.h new file mode 100644 index 000000000..82ed43d6b --- /dev/null +++ b/include/QF/GLSL/qf_sprite.h @@ -0,0 +1,39 @@ +/* + qf_sprite.h + + GLSL specific sprite model stuff + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/7/22 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __QF_GLSL_qf_sprite_h +#define __QF_GLSL_qf_sprite_h + +struct entity_s; +void glsl_R_DrawSprite (struct entity_s ent); +void glsl_R_SpriteBegin (void); +void glsl_R_SpriteEnd (void); +void glsl_R_InitSprites (void); + +#endif//__QF_GLSL_qf_sprite_h diff --git a/include/QF/GLSL/qf_textures.h b/include/QF/GLSL/qf_textures.h index 1447d797a..7e28acff3 100644 --- a/include/QF/GLSL/qf_textures.h +++ b/include/QF/GLSL/qf_textures.h @@ -31,34 +31,27 @@ #include "QF/qtypes.h" typedef struct scrap_s scrap_t; -typedef struct subpic_s { - const struct subpic_s * const next; - const scrap_t * const scrap; - const struct vrect_s * const rect; - const int tnum; ///< texture number - const int width; ///< requested width - const int height; ///< requested height - const float size; ///< size factor for tex coords (mult) -} subpic_t; +struct tex_s; int GLSL_LoadQuakeTexture (const char *identifier, int width, int height, - byte *data); + const byte *data); struct texture_s; int GLSL_LoadQuakeMipTex (const struct texture_s *tex); int GLSL_LoadRGBTexture (const char *identifier, int width, int height, - byte *data); + const byte *data); int GLSL_LoadRGBATexture (const char *identifier, int width, int height, - byte *data); + const byte *data); +int GLSL_LoadTex (const char *identifier, int mips, struct tex_s *tex); void GLSL_ReleaseTexture (int tex); void GLSL_TextureInit (void); scrap_t *GLSL_CreateScrap (int size, int format, int linear); void GLSL_DestroyScrap (scrap_t *scrap); void GLSL_ScrapClear (scrap_t *scrap); -int GLSL_ScrapTexture (scrap_t *scrap); -subpic_t *GLSL_ScrapSubpic (scrap_t *scrap, int width, int height); //XXX slow! -void GLSL_SubpicDelete (subpic_t *subpic); //XXX slow! -void GLSL_SubpicUpdate (subpic_t *subpic, byte *data, int batch); +int GLSL_ScrapTexture (scrap_t *scrap) __attribute__((pure)); +struct subpic_s *GLSL_ScrapSubpic (scrap_t *scrap, int width, int height); +void GLSL_SubpicDelete (struct subpic_s *subpic); +void GLSL_SubpicUpdate (struct subpic_s *subpic, byte *data, int batch); void GLSL_ScrapFlush (scrap_t *scrap); #endif//__QF_GLSL_textures_h diff --git a/include/QF/GLSL/qf_vid.h b/include/QF/GLSL/qf_vid.h index 57f6e5313..b3578eb34 100644 --- a/include/QF/GLSL/qf_vid.h +++ b/include/QF/GLSL/qf_vid.h @@ -38,7 +38,7 @@ typedef struct shader_s { typedef struct shaderparam_s { const char *name; - qboolean uniform; + bool uniform; int location; } shaderparam_t; @@ -46,6 +46,7 @@ extern int glsl_palette; extern int glsl_colormap; void GLSL_Init_Common (void); +void GLSL_Shutdown_Common (void); int GLSL_CompileShader (const char *name, const shader_t *shader, int type); int GLSL_LinkProgram (const char *name, int vert, int frag); @@ -62,6 +63,7 @@ void GLSL_DumpAttribArrays (void); \return 0 for failure, 1 for success. */ int GLSL_RegisterEffect (const char *name, const char *src); +void GLSL_ShaderShutdown (void); /* Build a shader program script from a list of effect keys. diff --git a/qw/include/cl_tent.h b/include/QF/GLSL/qf_warp.h similarity index 68% rename from qw/include/cl_tent.h rename to include/QF/GLSL/qf_warp.h index 71af54705..3b91b7183 100644 --- a/qw/include/cl_tent.h +++ b/include/QF/GLSL/qf_warp.h @@ -1,9 +1,12 @@ /* - client.h + qf_warp.h - Client definitions + GLSL screen warp - Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/3/24 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -24,15 +27,11 @@ Boston, MA 02111-1307, USA */ +#ifndef __QF_GLSL_qf_warp_h +#define __QF_GLSL_qf_warp_h -#ifndef _CL_TENT_H -#define _CL_TENT_H +void glsl_InitWarp (void); +struct framebuffer_s; +void glsl_WarpScreen (struct framebuffer_s *fb); -void CL_TEnts_Init (void); -void CL_ClearEnts (void); -void CL_ClearTEnts (void); -void CL_Init_Entity (struct entity_s *ent); -void CL_ParseTEnt (void); -void CL_UpdateTEnts (void); - -#endif +#endif//__QF_GLSL_qf_warp_h diff --git a/include/QF/GLSL/types.h b/include/QF/GLSL/types.h index 62c50780b..3e0819fa9 100644 --- a/include/QF/GLSL/types.h +++ b/include/QF/GLSL/types.h @@ -3,8 +3,8 @@ * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ . */ -#ifndef __QFGL_types_h -#define __QFGL_types_h +#ifndef __QF_GLSL_types_h +#define __QF_GLSL_types_h #include "QF/qtypes.h" @@ -14,6 +14,7 @@ # include # define GLAPIENTRY WINAPI # undef LoadImage +# undef E_POINTER # else # ifdef APIENTRY # define GLAPIENTRY APIENTRY diff --git a/include/QF/Makefile.am b/include/QF/Makefile.am deleted file mode 100644 index ffb0ff56b..000000000 --- a/include/QF/Makefile.am +++ /dev/null @@ -1,27 +0,0 @@ -AUTOMAKE_OPTIONS = foreign -pkgincludedir = $(includedir)/QF -nobase_pkginclude_HEADERS = \ - alloc.h bspfile.h cbuf.h cdaudio.h checksum.h clip_hull.h cmd.h \ - console.h crc.h csqc.h cvar.h dstring.h draw.h gib.h hash.h hl.h \ - idparse.h image.h in_event.h info.h input.h iqm.h joystick.h keys.h \ - link.h llist.h locs.h mathlib.h mdfour.h mersenne.h model.h modelgen.h \ - msg.h object.h pak.h pakfile.h pcx.h png.h plugin.h pr_comp.h pr_debug.h \ - pr_obj.h progs.h qargs.h qdefs.h qendian.h qfplist.h qtypes.h quakefs.h \ - quakeio.h render.h riff.h ruamoko.h screen.h script.h segtext.h set.h \ - sizebuf.h skin.h sound.h spritegn.h sys.h teamplay.h tga.h uint32.h va.h \ - ver_check.h vid.h vrect.h view.h wad.h wadfile.h winding.h zone.h \ - \ - GL/ati.h GL/defines.h GL/extensions.h GL/funcs.h GL/qf_draw.h \ - GL/qf_explosions.h GL/qf_funcs_list.h GL/qf_iqm.h GL/qf_lightmap.h \ - GL/qf_rlight.h GL/qf_rmain.h GL/qf_rsurf.h GL/qf_sky.h GL/qf_textures.h \ - GL/qf_vid.h GL/types.h \ - \ - GLSL/defines.h GLSL/funcs.h GLSL/qf_alias.h GLSL/qf_bsp.h GLSL/qf_draw.h \ - GLSL/qf_funcs_list.h GLSL/qf_iqm.h GLSL/qf_lightmap.h GLSL/qf_particles.h \ - GLSL/qf_textures.h GLSL/qf_vid.h GLSL/types.h \ - \ - math/dual.h math/half.h math/matrix3.h math/matrix4.h math/quaternion.h \ - math/vector.h \ - \ - plugin/cd.h plugin/console.h plugin/general.h plugin/input.h \ - plugin/snd_output.h plugin/snd_render.h plugin/vid_render.h diff --git a/include/QF/Makemodule.am b/include/QF/Makemodule.am new file mode 100644 index 000000000..bfeeaa4dd --- /dev/null +++ b/include/QF/Makemodule.am @@ -0,0 +1,272 @@ +include_qf = \ + include/QF/alloc.h \ + include/QF/bspfile.h \ + include/QF/cbuf.h \ + include/QF/cdaudio.h \ + include/QF/cexpr.h \ + include/QF/checksum.h \ + include/QF/clip_hull.h \ + include/QF/cmd.h \ + include/QF/cmem.h \ + include/QF/console.h \ + include/QF/crc.h \ + include/QF/csqc.h \ + include/QF/cvar.h \ + include/QF/darray.h \ + include/QF/dstring.h \ + include/QF/draw.h \ + include/QF/ecs.h \ + include/QF/fbsearch.h \ + include/QF/gib.h \ + include/QF/hash.h \ + include/QF/heapsort.h \ + include/QF/idparse.h \ + include/QF/image.h \ + include/QF/info.h \ + include/QF/input.h \ + include/QF/iqm.h \ + include/QF/joystick.h \ + include/QF/keys.h \ + include/QF/link.h \ + include/QF/listener.h \ + include/QF/llist.h \ + include/QF/mathlib.h \ + include/QF/mdfour.h \ + include/QF/mersenne.h \ + include/QF/model.h \ + include/QF/modelgen.h \ + include/QF/msg.h \ + include/QF/object.h \ + include/QF/pak.h \ + include/QF/pakfile.h \ + include/QF/pcx.h \ + include/QF/png.h \ + include/QF/plist.h \ + include/QF/plugin.h \ + include/QF/pqueue.h \ + include/QF/progs.h \ + include/QF/pvsfile.h \ + include/QF/qargs.h \ + include/QF/qdefs.h \ + include/QF/qendian.h \ + include/QF/qtypes.h \ + include/QF/quakefs.h \ + include/QF/quakeio.h \ + include/QF/render.h \ + include/QF/riff.h \ + include/QF/ringbuffer.h \ + include/QF/ruamoko.h \ + include/QF/screen.h \ + include/QF/script.h \ + include/QF/segtext.h \ + include/QF/set.h \ + include/QF/sizebuf.h \ + include/QF/skin.h \ + include/QF/sound.h \ + include/QF/spritegn.h \ + include/QF/sys.h \ + include/QF/sys_developer.h \ + include/QF/teamplay.h \ + include/QF/tga.h \ + include/QF/va.h \ + include/QF/ver_check.h \ + include/QF/vid.h \ + include/QF/wad.h \ + include/QF/wadfile.h \ + include/QF/winding.h \ + include/QF/zone.h + +include_qf_ecs = \ + include/QF/ecs/component.h \ + include/QF/ecs/entity.h \ + include/QF/ecs/hierarchy.h + +include_qf_gl = \ + include/QF/GL/ati.h \ + include/QF/GL/defines.h \ + include/QF/GL/extensions.h \ + include/QF/GL/funcs.h \ + include/QF/GL/qf_alias.h \ + include/QF/GL/qf_draw.h \ + include/QF/GL/qf_fisheye.h \ + include/QF/GL/qf_funcs_list.h \ + include/QF/GL/qf_iqm.h \ + include/QF/GL/qf_lightmap.h \ + include/QF/GL/qf_particles.h \ + include/QF/GL/qf_rlight.h \ + include/QF/GL/qf_rmain.h \ + include/QF/GL/qf_rsurf.h \ + include/QF/GL/qf_sky.h \ + include/QF/GL/qf_sprite.h \ + include/QF/GL/qf_textures.h \ + include/QF/GL/qf_vid.h \ + include/QF/GL/types.h + +include_qf_glsl = \ + include/QF/GLSL/defines.h \ + include/QF/GLSL/funcs.h \ + include/QF/GLSL/qf_alias.h \ + include/QF/GLSL/qf_bsp.h \ + include/QF/GLSL/qf_draw.h \ + include/QF/GLSL/qf_fisheye.h \ + include/QF/GLSL/qf_funcs_list.h \ + include/QF/GLSL/qf_iqm.h \ + include/QF/GLSL/qf_lightmap.h \ + include/QF/GLSL/qf_main.h \ + include/QF/GLSL/qf_particles.h \ + include/QF/GLSL/qf_sprite.h \ + include/QF/GLSL/qf_textures.h \ + include/QF/GLSL/qf_vid.h \ + include/QF/GLSL/qf_warp.h \ + include/QF/GLSL/types.h + +include_qf_input = \ + include/QF/input/binding.h \ + include/QF/input/event.h \ + include/QF/input/event_names.h \ + include/QF/input/imt.h + +include_qf_math = \ + include/QF/math/bitop.h \ + include/QF/math/dual.h \ + include/QF/math/half.h \ + include/QF/math/matrix3.h \ + include/QF/math/matrix4.h \ + include/QF/math/quaternion.h \ + include/QF/math/vector.h + +include_qf_plugin = \ + include/QF/plugin/cd.h \ + include/QF/plugin/console.h \ + include/QF/plugin/general.h \ + include/QF/plugin/input.h \ + include/QF/plugin/snd_output.h \ + include/QF/plugin/snd_render.h \ + include/QF/plugin/vid_render.h + +include_qf_progs = \ + include/QF/progs/pr_comp.h \ + include/QF/progs/pr_debug.h \ + include/QF/progs/pr_obj.h \ + include/QF/progs/pr_type.h \ + include/QF/progs/pr_type_names.h + +include_qf_scene = \ + include/QF/scene/camera.h \ + include/QF/scene/entity.h \ + include/QF/scene/light.h \ + include/QF/scene/transform.h \ + include/QF/scene/scene.h \ + include/QF/scene/types.h + +include_qf_simd = \ + include/QF/simd/mat4f.h \ + include/QF/simd/types.h \ + include/QF/simd/vec2d.h \ + include/QF/simd/vec2f.h \ + include/QF/simd/vec2i.h \ + include/QF/simd/vec4d.h \ + include/QF/simd/vec4f.h \ + include/QF/simd/vec4i.h + +include_qf_ui = \ + include/QF/ui/canvas.h \ + include/QF/ui/font.h \ + include/QF/ui/inputline.h \ + include/QF/ui/passage.h \ + include/QF/ui/text.h \ + include/QF/ui/txtbuffer.h \ + include/QF/ui/view.h \ + include/QF/ui/vrect.h + +include_qf_vulkan = \ + include/QF/Vulkan/barrier.h \ + include/QF/Vulkan/buffer.h \ + include/QF/Vulkan/capture.h \ + include/QF/Vulkan/command.h \ + include/QF/Vulkan/cvars.h \ + include/QF/Vulkan/debug.h \ + include/QF/Vulkan/descriptor.h \ + include/QF/Vulkan/device.h \ + include/QF/Vulkan/funclist.h \ + include/QF/Vulkan/image.h \ + include/QF/Vulkan/instance.h \ + include/QF/Vulkan/memory.h \ + include/QF/Vulkan/pipeline.h \ + include/QF/Vulkan/projection.h \ + include/QF/Vulkan/resource.h \ + include/QF/Vulkan/qf_alias.h \ + include/QF/Vulkan/qf_bsp.h \ + include/QF/Vulkan/qf_compose.h \ + include/QF/Vulkan/qf_draw.h \ + include/QF/Vulkan/qf_iqm.h \ + include/QF/Vulkan/qf_lighting.h \ + include/QF/Vulkan/qf_lightmap.h \ + include/QF/Vulkan/qf_main.h \ + include/QF/Vulkan/qf_matrices.h \ + include/QF/Vulkan/qf_model.h \ + include/QF/Vulkan/qf_output.h \ + include/QF/Vulkan/qf_palette.h \ + include/QF/Vulkan/qf_particles.h \ + include/QF/Vulkan/qf_scene.h \ + include/QF/Vulkan/qf_sprite.h \ + include/QF/Vulkan/qf_texture.h \ + include/QF/Vulkan/qf_translucent.h \ + include/QF/Vulkan/qf_vid.h \ + include/QF/Vulkan/scrap.h \ + include/QF/Vulkan/shader.h \ + include/QF/Vulkan/staging.h \ + include/QF/Vulkan/swapchain.h + +# headers shared with ruamoko +qfcc_include_qf = include/QF/input.h +qfcc_include_qf_progs = include/QF/progs/pr_type_names.h +qfcc_include_qf_input = \ + include/QF/input/binding.h \ + include/QF/input/imt.h + +qf_includedir = $(includedir)/QF +qf_gl_includedir = $(includedir)/QF/GL +qf_glsl_includedir = $(includedir)/QF/GLSL +qf_input_includedir = $(includedir)/QF/input +qf_math_includedir = $(includedir)/QF/math +qf_plugin_includedir = $(includedir)/QF/plugin +qf_progs_includedir = $(includedir)/QF/progs +qf_scene_includedir = $(includedir)/QF/scene +qf_simd_includedir = $(includedir)/QF/simd +qf_ui_includedir = $(includedir)/QF/ui +qf_vulkan_includedir = $(includedir)/QF/Vulkan +qf_include_HEADERS = @qfac_include_qf@ +qf_gl_include_HEADERS = @qfac_include_qf_gl@ +qf_glsl_include_HEADERS = @qfac_include_qf_glsl@ +qf_input_include_HEADERS = @qfac_include_qf_input@ +qf_math_include_HEADERS = @qfac_include_qf_math@ +qf_plugin_include_HEADERS = @qfac_include_qf_plugin@ +qf_progs_include_HEADERS = @qfac_include_qf_progs@ +qf_scene_include_HEADERS = @qfac_include_qf_scene@ +qf_simd_include_HEADERS = @qfac_include_qf_simd@ +qf_ui_include_HEADERS = @qfac_include_qf_ui@ +qf_vulkan_include_HEADERS = @qfac_include_qf_vulkan@ + +ruamoko_qf_includedir = $(ruamoko_includedir)/QF +ruamoko_qf_input_includedir = $(ruamoko_includedir)/QF/input +ruamoko_qf_progs_includedir = $(ruamoko_includedir)/QF/progs +ruamoko_qf_include_HEADERS = @qfac_qfcc_include_qf@ +ruamoko_qf_input_include_HEADERS = @qfac_qfcc_include_qf_input@ +ruamoko_qf_progs_include_HEADERS = @qfac_qfcc_include_qf_progs@ + +EXTRA_DIST += \ + $(include_qf) \ + $(include_qf_ecs) \ + $(include_qf_gl) \ + $(include_qf_glsl) \ + $(include_qf_input) \ + $(include_qf_math) \ + $(include_qf_plugin) \ + $(include_qf_progs) \ + $(include_qf_scene) \ + $(include_qf_simd) \ + $(include_qf_ui) \ + $(include_qf_vulkan) \ + $(qfcc_include_qf) \ + $(qfcc_include_qf_input) diff --git a/include/QF/Vulkan/barrier.h b/include/QF/Vulkan/barrier.h new file mode 100644 index 000000000..6288241e2 --- /dev/null +++ b/include/QF/Vulkan/barrier.h @@ -0,0 +1,52 @@ +#ifndef __QF_Vulkan_barrier_h +#define __QF_Vulkan_barrier_h + +#ifndef VK_NO_PROTOTYPES +#define VK_NO_PROTOTYPES +#endif +#include + +typedef struct qfv_imagebarrier_s { + VkPipelineStageFlags srcStages; + VkPipelineStageFlags dstStages; + VkImageMemoryBarrier barrier; +} qfv_imagebarrier_t; + +typedef struct qfv_bufferbarrier_s { + VkPipelineStageFlags srcStages; + VkPipelineStageFlags dstStages; + VkBufferMemoryBarrier barrier; +} qfv_bufferbarrier_t; + +// image layout transitions +enum { + qfv_LT_Undefined_to_TransferDst, + qfv_LT_Undefined_to_General, + qfv_LT_TransferDst_to_TransferSrc, + qfv_LT_TransferDst_to_General, + qfv_LT_TransferDst_to_ShaderReadOnly, + qfv_LT_TransferSrc_to_ShaderReadOnly, + qfv_LT_ShaderReadOnly_to_TransferDst, + qfv_LT_Undefined_to_DepthStencil, + qfv_LT_Undefined_to_Color, +}; + +// buffer barriers +enum { + qfv_BB_Unknown_to_TransferWrite, + qfv_BB_TransferWrite_to_VertexAttrRead, + qfv_BB_TransferWrite_to_IndexRead, + qfv_BB_TransferWrite_to_UniformRead, + qfv_BB_TransferWrite_to_ShaderRW, + qfv_BB_ShaderRW_to_ShaderRO, + qfv_BB_ShaderRW_to_ShaderRO_VA, + qfv_BB_ShaderRO_to_ShaderWrite, + qfv_BB_ShaderRO_VA_to_ShaderWrite, + qfv_BB_ShaderWrite_to_ShaderRO, + qfv_BB_ShaderWrite_to_ShaderRW, +}; + +extern const qfv_imagebarrier_t imageBarriers[]; +extern const qfv_bufferbarrier_t bufferBarriers[]; + +#endif//__QF_Vulkan_barrier_h diff --git a/include/QF/Vulkan/buffer.h b/include/QF/Vulkan/buffer.h new file mode 100644 index 000000000..2edff6a1d --- /dev/null +++ b/include/QF/Vulkan/buffer.h @@ -0,0 +1,57 @@ +#ifndef __QF_Vulkan_buffer_h +#define __QF_Vulkan_buffer_h + +#ifndef VK_NO_PROTOTYPES +#define VK_NO_PROTOTYPES +#endif +#include + +#include "QF/darray.h" + +typedef struct qfv_buffertransition_s { + VkBuffer buffer; + VkAccessFlags srcAccess; + VkAccessFlags dstAccess; + uint32_t srcQueueFamily; + uint32_t dstQueueFamily; + VkDeviceSize offset; + VkDeviceSize size; +} qfv_buffertransition_t; + +typedef struct qfv_buffertransitionset_s + DARRAY_TYPE (qfv_buffertransition_t) qfv_buffertransitionset_t; +typedef struct qfv_bufferbarrierset_s + DARRAY_TYPE (VkBufferMemoryBarrier) qfv_bufferbarrierset_t; + +typedef struct qfv_bufferset_s + DARRAY_TYPE (VkBuffer) qfv_bufferset_t; +#define QFV_AllocBufferSet(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_bufferset_t, num, allocator) + +struct qfv_device_s; +VkBuffer QFV_CreateBuffer (struct qfv_device_s *device, + VkDeviceSize size, + VkBufferUsageFlags usage); + +VkDeviceMemory QFV_AllocBufferMemory (struct qfv_device_s *device, + VkBuffer buffer, + VkMemoryPropertyFlags properties, + VkDeviceSize size, VkDeviceSize offset); + +int QFV_BindBufferMemory (struct qfv_device_s *device, + VkBuffer buffer, VkDeviceMemory object, + VkDeviceSize offset); + +qfv_bufferbarrierset_t * +QFV_CreateBufferTransitions (qfv_buffertransition_t *transitions, + int numTransitions); + +VkBufferView QFV_CreateBufferView (struct qfv_device_s *device, + VkBuffer buffer, VkFormat format, + VkDeviceSize offset, VkDeviceSize size); + +VkDeviceSize QFV_NextOffset (VkDeviceSize current, + const VkMemoryRequirements *requirements) + __attribute__((pure)); + +#endif//__QF_Vulkan_buffer_h diff --git a/include/QF/Vulkan/capture.h b/include/QF/Vulkan/capture.h new file mode 100644 index 000000000..605a940b3 --- /dev/null +++ b/include/QF/Vulkan/capture.h @@ -0,0 +1,46 @@ +#ifndef __QF_Vulkan_capture_h +#define __QF_Vulkan_capture_h + +#ifndef VK_NO_PROTOTYPES +#define VK_NO_PROTOTYPES +#endif +#include + +#include "QF/darray.h" +#include "QF/qtypes.h" + +struct vulkan_ctx_s; +struct tex_s; +typedef void (*capfunc_t) (struct tex_s *screencap, void *data); + +typedef struct qfv_capture_frame_s { + VkBuffer buffer; + byte *data; + + bool initiated; + capfunc_t callback; + void *callback_data; +} qfv_capture_frame_t; + +typedef struct qfv_capture_frame_set_s + DARRAY_TYPE (qfv_capture_frame_t) qfv_capture_frame_set_t; + +typedef struct qfv_capturectx_s { + qfv_capture_frame_set_t frames; + struct qfv_device_s *device; + VkExtent2D extent; + size_t imgsize; + size_t memsize; + byte *data; + VkDeviceMemory memory; +} qfv_capturectx_t; + +struct vulkan_ctx_s; + +void QFV_Capture_Init (struct vulkan_ctx_s *ctx); +void QFV_Capture_Renew (struct vulkan_ctx_s *ctx); +void QFV_Capture_Shutdown (struct vulkan_ctx_s *ctx); +void QFV_Capture_Screen (struct vulkan_ctx_s *ctx, + capfunc_t callback, void *data); + +#endif//__QF_Vulkan_capture_h diff --git a/include/QF/Vulkan/command.h b/include/QF/Vulkan/command.h new file mode 100644 index 000000000..d65d2ab74 --- /dev/null +++ b/include/QF/Vulkan/command.h @@ -0,0 +1,84 @@ +#ifndef __QF_Vulkan_command_h +#define __QF_Vulkan_command_h + +#ifndef VK_NO_PROTOTYPES +#define VK_NO_PROTOTYPES +#endif +#include + +#include "QF/darray.h" +#include "QF/qtypes.h" + +typedef struct qfv_cmdbufferset_s + DARRAY_TYPE (VkCommandBuffer) qfv_cmdbufferset_t; + +#define QFV_AllocCommandBufferSet(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_cmdbufferset_t, num, allocator) + +typedef struct qfv_semaphoreset_s + DARRAY_TYPE (VkSemaphore) qfv_semaphoreset_t; + +typedef struct qfv_fenceset_s + DARRAY_TYPE (VkFence) qfv_fenceset_t; + +#define QFV_AllocFenceSet(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_fenceset_t, num, allocator) + +typedef struct qfv_bufferimagecopy_s + DARRAY_TYPE (VkBufferImageCopy) qfv_bufferimagecopy_t; + +#define QFV_AllocBufferImageCopy(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_bufferimagecopy_t, num, allocator) + +typedef struct qfv_cmdpoolmgr_s { + qfv_cmdbufferset_t primary; + qfv_cmdbufferset_t secondary; + size_t active_primary; + size_t active_secondary; + struct qfv_device_s *device; + VkCommandPool pool; +} qfv_cmdpoolmgr_t; + +struct qfv_queue_s; + +qfv_cmdpoolmgr_t *QFV_CmdPoolManager_Init (qfv_cmdpoolmgr_t *manager, + struct qfv_device_s *device, + const char *name); +qfv_cmdpoolmgr_t *QFV_CmdPoolManager_New (struct qfv_device_s *device, + const char *name); +void QFV_CmdPoolManager_Shutdown (qfv_cmdpoolmgr_t *manager); +void QFV_CmdPoolManager_Delete (qfv_cmdpoolmgr_t *manager); +void QFV_CmdPoolManager_Reset (qfv_cmdpoolmgr_t *manager); +VkCommandBuffer QFV_CmdPoolManager_CmdBuffer (qfv_cmdpoolmgr_t *manager, + bool secondary); + +VkCommandPool QFV_CreateCommandPool (struct qfv_device_s *device, + uint32_t queueFamily, + int transient, int reset); +/** Allocate bufferset->size command buffers + */ +int QFV_AllocateCommandBuffers (struct qfv_device_s *device, + VkCommandPool pool, int secondary, + qfv_cmdbufferset_t *bufferset); + +VkSemaphore QFV_CreateSemaphore (struct qfv_device_s *device); +VkFence QFV_CreateFence (struct qfv_device_s *device, int signaled); +int QFV_QueueSubmit (struct qfv_queue_s *queue, + qfv_semaphoreset_t *waitSemaphores, + VkPipelineStageFlags *stages, + qfv_cmdbufferset_t *buffers, + qfv_semaphoreset_t *signalSemaphores, VkFence fence); +int QFV_QueueWaitIdle (struct qfv_queue_s *queue); + +typedef struct qfv_push_constants_s { + VkShaderStageFlags stageFlags; + uint32_t offset; + uint32_t size; + const void *data; +} qfv_push_constants_t; + +void QFV_PushConstants (struct qfv_device_s *device, VkCommandBuffer cmd, + VkPipelineLayout layout, uint32_t numPC, + const qfv_push_constants_t *constants); + +#endif//__QF_Vulkan_command_h diff --git a/include/QF/Vulkan/cvars.h b/include/QF/Vulkan/cvars.h new file mode 100644 index 000000000..c9869d1c2 --- /dev/null +++ b/include/QF/Vulkan/cvars.h @@ -0,0 +1,8 @@ +#ifndef __QF_Vulkan_cvars_h +#define __QF_Vulkan_cvars_h + +extern int vulkan_use_validation; +extern int vulkan_presentation_mode; +extern int vulkan_frame_count; + +#endif//__QF_Vulkan_cvars_h diff --git a/include/QF/Vulkan/debug.h b/include/QF/Vulkan/debug.h new file mode 100644 index 000000000..d9029eab6 --- /dev/null +++ b/include/QF/Vulkan/debug.h @@ -0,0 +1,144 @@ +#ifndef __QF_Vulkan_debug_h +#define __QF_Vulkan_debug_h + +#ifndef VK_NO_PROTOTYPES +#define VK_NO_PROTOTYPES +#endif +#include + +#include "QF/simd/types.h" + +#if (defined(_WIN32) && !defined(_WIN64)) || (__WORDSIZE < 64) +#define QFV_duCmdBeginLabel(device, cmd, name...) +#define QFV_duCmdEndLabel(device, cmd) +#define QFV_duCmdInsertLabel(device, cmd, name...) +#define QFV_duCreateMessenger(inst, severity, type, callback, data, messenger) +#define QFV_duDestroyMessenger(inst, messenger) +#define QFV_duQueueBeginLabel(device, queue, name...) +#define QFV_duQueueEndLabel(device, queue) +#define QFV_duQueueInsertLabel(device, queue, name...) +#define QFV_duSetObjectName(device, type, handle, name) +#define QFV_duSetObjectTag(device, type, handle, name, size, tag) +#define QFV_duSubmitMessage(inst, severity, types, data) +#else +#define QFV_duCmdBeginLabel(device, cmd, name...)\ + do { \ + qfv_devfuncs_t *dfunc = device->funcs; \ + if (dfunc->vkCmdBeginDebugUtilsLabelEXT) { \ + VkDebugUtilsLabelEXT qfv_du_label = { \ + VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, 0, name \ + }; \ + dfunc->vkCmdBeginDebugUtilsLabelEXT (cmd, &qfv_du_label); \ + } \ + } while (0) + +#define QFV_duCmdEndLabel(device, cmd) \ + do { \ + qfv_devfuncs_t *dfunc = device->funcs; \ + if (dfunc->vkCmdEndDebugUtilsLabelEXT) { \ + dfunc->vkCmdEndDebugUtilsLabelEXT (cmd); \ + } \ + } while (0) + +#define QFV_duCmdInsertLabel(device, cmd, name...) \ + do { \ + qfv_devfuncs_t *dfunc = device->funcs; \ + if (dfunc->vkCmdInsertDebugUtilsLabelEXT) { \ + VkDebugUtilsLabelEXT qfv_du_label = { \ + VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, 0, name \ + }; \ + dfunc->vkCmdInsertDebugUtilsLabelEXT (cmd, &qfv_du_label); \ + } \ + } while (0) + +#define QFV_duCreateMessenger(inst, severity, type, callback, data, messenger)\ + do { \ + if (inst->funcs->vkCreateDebugUtilsMessengerEXT) { \ + VkDebugUtilsMessengerCreateInfoEXT createInfo = { \ + VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, 0, 0,\ + severity, type, callback, data \ + }; \ + inst->funcs->vkCreateDebugUtilsMessengerEXT (inst, &createInfo, 0,\ + messenger); \ + } \ + } while (0) + +#define QFV_duDestroyMessenger(inst, messenger) \ + do { \ + if (inst->funcs->vkDestroyDebugUtilsMessengerEXT) { \ + inst->funcs->vkDestroyDebugUtilsMessengerEXT (inst, messenger, 0);\ + } \ + } while (0) + +#define QFV_duQueueBeginLabel(device, queue, name...) \ + do { \ + qfv_devfuncs_t *dfunc = device->funcs; \ + if (dfunc->vkQueueBeginDebugUtilsLabelEXT) { \ + VkDebugUtilsLabelEXT qfv_du_label = { \ + VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, 0, name \ + }; \ + dfunc->vkQueueBeginDebugUtilsLabelEXT (queue, &qfv_du_label); \ + } \ + } while (0) + +#define QFV_duQueueEndLabel(device, queue) \ + do { \ + qfv_devfuncs_t *dfunc = device->funcs; \ + if (dfunc->vkQueueEndDebugUtilsLabelEXT) { \ + dfunc->vkQueueEndDebugUtilsLabelEXT (queue); \ + } \ + } while (0) + +#define QFV_duQueueInsertLabel(device, queue, name...) \ + do { \ + qfv_devfuncs_t *dfunc = device->funcs; \ + if (dfunc->vkQueueInsertDebugUtilsLabelEXT) { \ + VkDebugUtilsLabelEXT qfv_du_label = { \ + VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, 0, name \ + }; \ + dfunc->vkQueueInsertDebugUtilsLabelEXT (queue, &qfv_du_label); \ + } \ + } while (0) + +#define QFV_duSetObjectName(device, type, handle, name) \ + do { \ + qfv_devfuncs_t *dfunc = device->funcs; \ + if (dfunc->vkSetDebugUtilsObjectNameEXT) { \ + VkDebugUtilsObjectNameInfoEXT nameInfo = { \ + VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, 0, \ + type, (uint64_t) handle, name \ + }; \ + dfunc->vkSetDebugUtilsObjectNameEXT (device->dev, &nameInfo); \ + } \ + } while (0) + +#define QFV_duSetObjectTag(device, type, handle, name, size, tag) \ + do { \ + qfv_devfuncs_t *dfunc = device->funcs; \ + if (dfunc->vkSetDebugUtilsObjectTagEXT) { \ + VkDebugUtilsObjectTagInfoEXT tagInfo = { \ + VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_TAG_INFO_EXT, 0, \ + type, handle, name, size, tag \ + }; \ + dfunc->vkSetDebugUtilsObjectTagEXT (device->dev, &tagInfo); \ + } \ + } while (0) + +#define QFV_duSubmitMessage(inst, severity, types, data) \ + do { \ + if (inst->funcs->vkSubmitDebugUtilsMessageEXT) { \ + inst->funcs->vkSubmitDebugUtilsMessageEXT (inst, severity, types, \ + data); \ + } \ + } while (0) +#endif + +struct qfv_device_s; + +void QFV_CmdBeginLabel (struct qfv_device_s *device, VkCommandBuffer cmd, + const char *name, vec4f_t color); +void QFV_CmdEndLabel (struct qfv_device_s *device, VkCommandBuffer cmd); +void QFV_CmdInsertLabel (struct qfv_device_s *device, VkCommandBuffer cmd, + const char *name, vec4f_t color); + +#endif//__QF_Vulkan_debug_h diff --git a/include/QF/Vulkan/descriptor.h b/include/QF/Vulkan/descriptor.h new file mode 100644 index 000000000..14d6be987 --- /dev/null +++ b/include/QF/Vulkan/descriptor.h @@ -0,0 +1,67 @@ +#ifndef __QF_Vulkan_descriptor_h +#define __QF_Vulkan_descriptor_h + +#ifndef VK_NO_PROTOTYPES +#define VK_NO_PROTOTYPES +#endif +#include + +#include "QF/darray.h" + +typedef struct qfv_bindingset_s + DARRAY_TYPE (VkDescriptorSetLayoutBinding) qfv_bindingset_t; + +typedef struct qfv_descriptorsetlayoutset_s + DARRAY_TYPE (VkDescriptorSetLayout) qfv_descriptorsetlayoutset_t; + +#define QFV_AllocDescriptorSetLayoutSet(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_descriptorsetlayoutset_t, num, allocator) + +typedef struct qfv_descriptorsets_s + DARRAY_TYPE (VkDescriptorSet) qfv_descriptorsets_t; + +#define QFV_AllocDescriptorSets(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_descriptorsets_t, num, allocator) + +typedef struct qfv_writedescriptorsets_s + DARRAY_TYPE (VkWriteDescriptorSet) qfv_writedescriptorsets_t; + +#define QFV_AllocWriteDescriptorSets(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_writedescriptorsets_t, num, allocator) + +typedef struct qfv_copydescriptorsets_s + DARRAY_TYPE (VkCopyDescriptorSet) qfv_copydescriptorsets_t; + +#define QFV_AllocCopyDescriptorSets(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_descriptorsetlayoutset_t, num, allocator) + +struct qfv_device_s; + +VkSampler QFV_CreateSampler (struct qfv_device_s *device, + VkFilter magFilter, VkFilter minFilter, + VkSamplerMipmapMode mipmapMode, + VkSamplerAddressMode addressModeU, + VkSamplerAddressMode addressModeV, + VkSamplerAddressMode addressModeW, + float mipLodBias, + VkBool32 anisotryEnable, float maxAnisotropy, + VkBool32 compareEnable, VkCompareOp compareOp, + float minLod, float maxLod, + VkBorderColor borderColor, + VkBool32 unnormalizedCoordinates); + +VkDescriptorSetLayout +QFV_CreateDescriptorSetLayout (struct qfv_device_s *device, + qfv_bindingset_t *bindings); + +VkDescriptorPool +QFV_CreateDescriptorPool (struct qfv_device_s *device, + VkDescriptorPoolCreateFlags flags, uint32_t maxSets, + qfv_bindingset_t *bindings); + +qfv_descriptorsets_t * +QFV_AllocateDescriptorSet (struct qfv_device_s *device, + VkDescriptorPool pool, + qfv_descriptorsetlayoutset_t *layouts); + +#endif//__QF_Vulkan_descriptor_h diff --git a/include/QF/Vulkan/device.h b/include/QF/Vulkan/device.h new file mode 100644 index 000000000..8d10971dc --- /dev/null +++ b/include/QF/Vulkan/device.h @@ -0,0 +1,41 @@ +#ifndef __QF_Vulkan_device_h +#define __QF_Vulkan_device_h + +#include "QF/Vulkan/qf_vid.h" + +typedef struct qfv_devfuncs_s { +#define DEVICE_LEVEL_VULKAN_FUNCTION(name) PFN_##name name; +#define DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION(name,ext) PFN_##name name; +#include "QF/Vulkan/funclist.h" +} qfv_devfuncs_t; + +typedef struct qfv_queue_s { + struct qfv_device_s *device; + + uint32_t queueFamily; + VkQueue queue; +} qfv_queue_t; + +struct qfv_instance_s; +typedef struct qfv_device_s { + VkDevice dev; + struct qfv_physdev_s *physDev; + qfv_devfuncs_t *funcs; + qfv_queue_t queue; + struct strset_s *enabled_extensions; + int (*extension_enabled) (struct qfv_device_s *inst, + const char *ext); +} qfv_device_t; + +struct vulkan_ctx_s; +qfv_device_t *QFV_CreateDevice (struct vulkan_ctx_s *ctx, + const char **extensions); +void QFV_DestroyDevice (qfv_device_t *device); +int QFV_DeviceWaitIdle (qfv_device_t *device); + +VkFormat QFV_FindSupportedFormat (qfv_device_t *device, VkImageTiling tiling, + VkFormatFeatureFlags features, + int numCandidates, + const VkFormat *candidates); + +#endif//__QF_Vulkan_device_h diff --git a/include/QF/Vulkan/dsmanager.h b/include/QF/Vulkan/dsmanager.h new file mode 100644 index 000000000..e152c5236 --- /dev/null +++ b/include/QF/Vulkan/dsmanager.h @@ -0,0 +1,37 @@ +#ifndef __QF_Vulkan_dsmanager_h +#define __QF_Vulkan_dsmanager_h + +#ifndef VK_NO_PROTOTYPES +#define VK_NO_PROTOTYPES +#endif +#include + +#include "QF/darray.h" +#include "QF/darray.h" + +typedef struct qfv_descriptorpoolset_s + DARRAY_TYPE (VkDescriptorPool) qfv_descriptorpoolset_t; +typedef struct qfv_descriptorsetset_s + DARRAY_TYPE (VkDescriptorSet) qfv_descriptorsetset_t; + +typedef struct qfv_dsmanager_s { + const char *name; + struct qfv_device_s *device; + VkDescriptorPoolCreateInfo poolCreateInfo; + VkDescriptorPool activePool; + qfv_descriptorpoolset_t freePools; + qfv_descriptorpoolset_t usedPools; + qfv_descriptorsetset_t freeSets; + VkDescriptorSetLayout layout; +} qfv_dsmanager_t; + +struct qfv_descriptorsetlayoutinfo_s; + +qfv_dsmanager_t * +QFV_DSManager_Create (const struct qfv_descriptorsetlayoutinfo_s *setLayoutInfo, + uint32_t maxSets, struct vulkan_ctx_s *ctx); +void QFV_DSManager_Destroy (qfv_dsmanager_t *setManager); +VkDescriptorSet QFV_DSManager_AllocSet (qfv_dsmanager_t *setManager); +void QFV_DSManager_FreeSet (qfv_dsmanager_t *setManager, VkDescriptorSet set); + +#endif//__QF_Vulkan_dsmanager_h diff --git a/include/QF/Vulkan/funclist.h b/include/QF/Vulkan/funclist.h new file mode 100644 index 000000000..6eb8d86bb --- /dev/null +++ b/include/QF/Vulkan/funclist.h @@ -0,0 +1,235 @@ +#ifndef EXPORTED_VULKAN_FUNCTION +#define EXPORTED_VULKAN_FUNCTION(function) +#endif + +EXPORTED_VULKAN_FUNCTION (vkGetInstanceProcAddr) + +#undef EXPORTED_VULKAN_FUNCTION + +#ifndef GLOBAL_LEVEL_VULKAN_FUNCTION +#define GLOBAL_LEVEL_VULKAN_FUNCTION(function) +#endif + +GLOBAL_LEVEL_VULKAN_FUNCTION (vkEnumerateInstanceVersion) +GLOBAL_LEVEL_VULKAN_FUNCTION (vkEnumerateInstanceExtensionProperties) +GLOBAL_LEVEL_VULKAN_FUNCTION (vkEnumerateInstanceLayerProperties) +GLOBAL_LEVEL_VULKAN_FUNCTION (vkCreateInstance) + +#undef GLOBAL_LEVEL_VULKAN_FUNCTION + +#ifndef INSTANCE_LEVEL_VULKAN_FUNCTION +#define INSTANCE_LEVEL_VULKAN_FUNCTION(function) +#endif + +INSTANCE_LEVEL_VULKAN_FUNCTION (vkEnumeratePhysicalDevices) +INSTANCE_LEVEL_VULKAN_FUNCTION (vkGetPhysicalDeviceProperties2) +INSTANCE_LEVEL_VULKAN_FUNCTION (vkGetPhysicalDeviceFeatures) +INSTANCE_LEVEL_VULKAN_FUNCTION (vkGetPhysicalDeviceQueueFamilyProperties) +INSTANCE_LEVEL_VULKAN_FUNCTION (vkCreateDevice) +INSTANCE_LEVEL_VULKAN_FUNCTION (vkGetDeviceProcAddr) +INSTANCE_LEVEL_VULKAN_FUNCTION (vkDestroyInstance) +INSTANCE_LEVEL_VULKAN_FUNCTION (vkEnumerateDeviceLayerProperties) +INSTANCE_LEVEL_VULKAN_FUNCTION (vkEnumerateDeviceExtensionProperties) +INSTANCE_LEVEL_VULKAN_FUNCTION (vkGetPhysicalDeviceMemoryProperties) +INSTANCE_LEVEL_VULKAN_FUNCTION (vkGetPhysicalDeviceFormatProperties) + +#undef INSTANCE_LEVEL_VULKAN_FUNCTION + +#ifndef INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION +#define INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION(function, extension) +#endif + +INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkCreateDebugUtilsMessengerEXT, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) +INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkDestroyDebugUtilsMessengerEXT, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) +INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkSubmitDebugUtilsMessageEXT, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) +INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkGetPhysicalDeviceSurfaceSupportKHR, VK_KHR_SURFACE_EXTENSION_NAME) +INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkGetPhysicalDeviceSurfacePresentModesKHR, VK_KHR_SURFACE_EXTENSION_NAME) +INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkGetPhysicalDeviceSurfaceCapabilitiesKHR, VK_KHR_SURFACE_EXTENSION_NAME) +INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkGetPhysicalDeviceSurfaceFormatsKHR, VK_KHR_SURFACE_EXTENSION_NAME) +INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkDestroySurfaceKHR, VK_KHR_SURFACE_EXTENSION_NAME) + +#undef INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + +#ifndef PRESENTATION_VULKAN_FUNCTION_FROM_EXTENSION +#define PRESENTATION_VULKAN_FUNCTION_FROM_EXTENSION(function, extension) +#endif + +#if defined(VK_USE_PLATFORM_XLIB_KHR) +PRESENTATION_VULKAN_FUNCTION_FROM_EXTENSION + (vkGetPhysicalDeviceXlibPresentationSupportKHR, + VK_KHR_XLIB_SURFACE_EXTENSION_NAME) +PRESENTATION_VULKAN_FUNCTION_FROM_EXTENSION + (vkCreateXlibSurfaceKHR, + VK_KHR_XLIB_SURFACE_EXTENSION_NAME) +#elif defined(VK_USE_PLATFORM_WIN32_KHR) +PRESENTATION_VULKAN_FUNCTION_FROM_EXTENSION + (vkGetPhysicalDeviceWin32PresentationSupportKHR, + VK_KHR_WIN32_SURFACE_EXTENSION_NAME) +PRESENTATION_VULKAN_FUNCTION_FROM_EXTENSION + (vkCreateWin32SurfaceKHR, + VK_KHR_WIN32_SURFACE_EXTENSION_NAME) +#elif defined(VK_USE_PLATFORM_XCB_KHR) +PRESENTATION_VULKAN_FUNCTION_FROM_EXTENSION + (vkGetPhysicalDeviceXcbPresentationSupportKHR, + VK_KHR_XCB_SURFACE_EXTENSION_NAME) +PRESENTATION_VULKAN_FUNCTION_FROM_EXTENSION + (vkCreateXcbSurfaceKHR, + VK_KHR_XCB_SURFACE_EXTENSION_NAME) +#endif + +#undef PRESENTATION_VULKAN_FUNCTION_FROM_EXTENSION + +#ifndef DEVICE_LEVEL_VULKAN_FUNCTION +#define DEVICE_LEVEL_VULKAN_FUNCTION(function) +#endif + +DEVICE_LEVEL_VULKAN_FUNCTION (vkDestroyDevice) +DEVICE_LEVEL_VULKAN_FUNCTION (vkGetDeviceQueue) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCreateCommandPool) +DEVICE_LEVEL_VULKAN_FUNCTION (vkResetCommandPool) +DEVICE_LEVEL_VULKAN_FUNCTION (vkAllocateCommandBuffers) +DEVICE_LEVEL_VULKAN_FUNCTION (vkBeginCommandBuffer) +DEVICE_LEVEL_VULKAN_FUNCTION (vkEndCommandBuffer) +DEVICE_LEVEL_VULKAN_FUNCTION (vkResetCommandBuffer) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCreateSemaphore) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCreateFence) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCreateEvent) +DEVICE_LEVEL_VULKAN_FUNCTION (vkWaitForFences) +DEVICE_LEVEL_VULKAN_FUNCTION (vkResetFences) +DEVICE_LEVEL_VULKAN_FUNCTION (vkGetFenceStatus) +DEVICE_LEVEL_VULKAN_FUNCTION (vkResetEvent) +DEVICE_LEVEL_VULKAN_FUNCTION (vkSetEvent) +DEVICE_LEVEL_VULKAN_FUNCTION (vkGetEventStatus) +DEVICE_LEVEL_VULKAN_FUNCTION (vkQueueSubmit) +DEVICE_LEVEL_VULKAN_FUNCTION (vkQueueWaitIdle) +DEVICE_LEVEL_VULKAN_FUNCTION (vkDeviceWaitIdle) +DEVICE_LEVEL_VULKAN_FUNCTION (vkDestroyEvent) +DEVICE_LEVEL_VULKAN_FUNCTION (vkDestroyFence) +DEVICE_LEVEL_VULKAN_FUNCTION (vkDestroySemaphore) +DEVICE_LEVEL_VULKAN_FUNCTION (vkFreeCommandBuffers) +DEVICE_LEVEL_VULKAN_FUNCTION (vkDestroyCommandPool) + +DEVICE_LEVEL_VULKAN_FUNCTION (vkCreateBuffer) +DEVICE_LEVEL_VULKAN_FUNCTION (vkGetBufferMemoryRequirements) +DEVICE_LEVEL_VULKAN_FUNCTION (vkBindBufferMemory) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCreateBufferView) +DEVICE_LEVEL_VULKAN_FUNCTION (vkDestroyBufferView) +DEVICE_LEVEL_VULKAN_FUNCTION (vkDestroyBuffer) + +DEVICE_LEVEL_VULKAN_FUNCTION (vkCreateImage) +DEVICE_LEVEL_VULKAN_FUNCTION (vkGetImageMemoryRequirements) +DEVICE_LEVEL_VULKAN_FUNCTION (vkBindImageMemory) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCreateImageView) +DEVICE_LEVEL_VULKAN_FUNCTION (vkDestroyImageView) +DEVICE_LEVEL_VULKAN_FUNCTION (vkDestroyImage) + +DEVICE_LEVEL_VULKAN_FUNCTION (vkAllocateMemory) +DEVICE_LEVEL_VULKAN_FUNCTION (vkFreeMemory) +DEVICE_LEVEL_VULKAN_FUNCTION (vkMapMemory) +DEVICE_LEVEL_VULKAN_FUNCTION (vkUnmapMemory) +DEVICE_LEVEL_VULKAN_FUNCTION (vkFlushMappedMemoryRanges) +DEVICE_LEVEL_VULKAN_FUNCTION (vkInvalidateMappedMemoryRanges) + +DEVICE_LEVEL_VULKAN_FUNCTION (vkCreateSampler) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCreateDescriptorSetLayout) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCreateDescriptorPool) +DEVICE_LEVEL_VULKAN_FUNCTION (vkAllocateDescriptorSets) +DEVICE_LEVEL_VULKAN_FUNCTION (vkUpdateDescriptorSets) +DEVICE_LEVEL_VULKAN_FUNCTION (vkFreeDescriptorSets) +DEVICE_LEVEL_VULKAN_FUNCTION (vkResetDescriptorPool) +DEVICE_LEVEL_VULKAN_FUNCTION (vkDestroyDescriptorPool) +DEVICE_LEVEL_VULKAN_FUNCTION (vkDestroyDescriptorSetLayout) +DEVICE_LEVEL_VULKAN_FUNCTION (vkDestroySampler) + +DEVICE_LEVEL_VULKAN_FUNCTION (vkCreateRenderPass) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCreateFramebuffer) +DEVICE_LEVEL_VULKAN_FUNCTION (vkDestroyRenderPass) +DEVICE_LEVEL_VULKAN_FUNCTION (vkDestroyFramebuffer) + +DEVICE_LEVEL_VULKAN_FUNCTION (vkCreateShaderModule) +DEVICE_LEVEL_VULKAN_FUNCTION (vkDestroyShaderModule) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCreatePipelineCache) +DEVICE_LEVEL_VULKAN_FUNCTION (vkGetPipelineCacheData) +DEVICE_LEVEL_VULKAN_FUNCTION (vkMergePipelineCaches) +DEVICE_LEVEL_VULKAN_FUNCTION (vkDestroyPipelineCache) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCreatePipelineLayout) +DEVICE_LEVEL_VULKAN_FUNCTION (vkDestroyPipelineLayout) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCreateGraphicsPipelines) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCreateComputePipelines) +DEVICE_LEVEL_VULKAN_FUNCTION (vkDestroyPipeline) + +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdPipelineBarrier) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdCopyBuffer) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdCopyBufferToImage) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdCopyImageToBuffer) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdCopyImage) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdBlitImage) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdBeginRenderPass) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdNextSubpass) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdEndRenderPass) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdBindPipeline) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdDispatch) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdClearColorImage) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdExecuteCommands) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdPushConstants) + +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdResetEvent) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdSetEvent) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdWaitEvents) + +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdSetViewport) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdSetScissor) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdBindVertexBuffers) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdBindIndexBuffer) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdBindDescriptorSets) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdDraw) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdDrawIndexed) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdDrawIndexedIndirect) +DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdDrawIndirect) + +#undef DEVICE_LEVEL_VULKAN_FUNCTION + +#ifndef DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION +#define DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION(function, extension) +#endif + +DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkCreateSwapchainKHR, VK_KHR_SWAPCHAIN_EXTENSION_NAME) +DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkDestroySwapchainKHR, VK_KHR_SWAPCHAIN_EXTENSION_NAME) +DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkGetSwapchainImagesKHR, VK_KHR_SWAPCHAIN_EXTENSION_NAME) +DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkAcquireNextImageKHR, VK_KHR_SWAPCHAIN_EXTENSION_NAME) +DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkQueuePresentKHR, VK_KHR_SWAPCHAIN_EXTENSION_NAME) + +DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkCmdBeginDebugUtilsLabelEXT, 0) +DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkCmdEndDebugUtilsLabelEXT, 0) +DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkCmdInsertDebugUtilsLabelEXT, 0) +DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkQueueBeginDebugUtilsLabelEXT, 0) +DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkQueueEndDebugUtilsLabelEXT, 0) +DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkQueueInsertDebugUtilsLabelEXT, 0) +DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkSetDebugUtilsObjectNameEXT, 0) +DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkSetDebugUtilsObjectTagEXT, 0) + +DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION + (vkCmdPushDescriptorSetKHR, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME) + +#undef DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION diff --git a/include/QF/Vulkan/image.h b/include/QF/Vulkan/image.h new file mode 100644 index 000000000..0ae5217e9 --- /dev/null +++ b/include/QF/Vulkan/image.h @@ -0,0 +1,115 @@ +#ifndef __QF_Vulkan_image_h +#define __QF_Vulkan_image_h + +#ifndef VK_NO_PROTOTYPES +#define VK_NO_PROTOTYPES +#endif +#include + +#include "QF/darray.h" +#include "QF/image.h" + +typedef struct qfv_imageset_s + DARRAY_TYPE (VkImage) qfv_imageset_t; + +#define QFV_AllocImages(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_imageset_t, num, allocator) + +typedef struct qfv_imageviewset_s + DARRAY_TYPE (VkImageView) qfv_imageviewset_t; + +#define QFV_AllocImageViews(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_imageviewset_t, num, allocator) + +typedef struct qfv_imageresource_s { + struct qfv_device_s *device; + VkImage image; + VkDeviceMemory object; + VkImageView view; +} qfv_imageresource_t; + +typedef struct qfv_imagetransition_s { + VkImage image; + VkAccessFlags srcAccess; + VkAccessFlags dstAccess; + VkImageLayout oldLayout; + VkImageLayout newLayout; + uint32_t srcQueueFamily; + uint32_t dstQueueFamily; + VkImageAspectFlags aspect; +} qfv_imagetransition_t; + +typedef struct qfv_imagetransitionset_s + DARRAY_TYPE (qfv_imagetransition_t) qfv_imagetransitionset_t; +typedef struct qfv_imagebarrierset_s + DARRAY_TYPE (VkImageMemoryBarrier) qfv_imagebarrierset_t; +#define QFV_AllocImageBarrierSet(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_imagebarrierset_t, num, allocator) + +struct qfv_device_s; +VkImage QFV_CreateImage (struct qfv_device_s *device, int cubemap, + VkImageType type, + VkFormat format, + VkExtent3D size, + uint32_t num_mipmaps, + uint32_t num_layers, + VkSampleCountFlags samples, + VkImageUsageFlags usage_scenarios); + +VkDeviceMemory QFV_AllocImageMemory (struct qfv_device_s *device, + VkImage image, + VkMemoryPropertyFlags properties, + VkDeviceSize size, VkDeviceSize offset); + +int QFV_BindImageMemory (struct qfv_device_s *device, VkImage image, + VkDeviceMemory object, VkDeviceSize offset); + +qfv_imagebarrierset_t * +QFV_CreateImageTransitionSet (qfv_imagetransition_t *transitions, + int numTransitions); + +VkImageView QFV_CreateImageView (struct qfv_device_s *device, + VkImage image, VkImageViewType type, + VkFormat format, VkImageAspectFlags aspect); + +size_t QFV_GetImageSize (struct qfv_device_s *device, VkImage image); + +/** Generate all mipmaps for a given texture down to a 1x1 pixel. + * + * Uses the GPU blit command from one mip level to the next, thus the base mip + * level data must have already been transfered to the image and the image is + * expected to be in VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL. This includes any + * array levels. + * + * \param device The device owning the command buffer. + * \param cmd The command buffer to which the barrier and blit commands + * will be written. + * \param image The image to be processed. All array layers of the base mip + * level must be initialized and in "transfer dst optimal" + * layout. All remaining mip levels must be in "undefined" + * oayout. + * \param mips The total number of mip levels of the processed image. + * \param width The pixel width of the base image. + * \param height The pixel height of the base image. + * \param layers The number of array layers in the base image. + * + * \note The processed image will be in "shader read only optimal" layout on + * completion. + */ +void QFV_GenerateMipMaps (struct qfv_device_s *device, VkCommandBuffer cmd, + VkImage image, unsigned mips, + unsigned width, unsigned height, unsigned layers); +int QFV_MipLevels (int width, int height) __attribute__((const)); + +/** Convert QFFormat to VkFormat + * + * \param format The format to convert. + * \param srgb Select SRGB formats (non-zero) or UNORM (0). + * \return The corresponding VkFormat. + * + * \note For tex_palette, VK_FORMAT_R8_UINT is returned. If \a format is + * not a valid QFFormat, then VK_FORMAT_R8_SRGB is returned. + */ +VkFormat QFV_ImageFormat (QFFormat format, int srgb) __attribute__((const)); + +#endif//__QF_Vulkan_image_h diff --git a/include/QF/Vulkan/instance.h b/include/QF/Vulkan/instance.h new file mode 100644 index 000000000..c4b18a73a --- /dev/null +++ b/include/QF/Vulkan/instance.h @@ -0,0 +1,73 @@ +/* + Copyright (C) 2019 Bill Currie + + Author: Bill Currie + Date: 2019/6/29 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __QF_Vulkan_instance_h +#define __QF_Vulkan_instance_h + +#include + +#include "QF/darray.h" +#include "QF/qtypes.h" + +typedef struct qfv_instfuncs_s { +#define INSTANCE_LEVEL_VULKAN_FUNCTION(name) PFN_##name name; +#define INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION(name,ext) PFN_##name name; +#include "QF/Vulkan/funclist.h" +} qfv_instfuncs_t; + +typedef struct DARRAY_TYPE(const char *) qfv_debugstack_t; + +typedef struct qfv_physdev_s { + struct qfv_instance_s *instance; + VkPhysicalDevice dev; + VkPhysicalDeviceProperties2 properties2; + VkPhysicalDeviceProperties *properties; + VkPhysicalDeviceMultiviewProperties multiViewProperties; + VkPhysicalDeviceMemoryProperties memory_properties; +} qfv_physdev_t; + +typedef struct qfv_instance_s { + VkInstance instance; + qfv_instfuncs_t *funcs; + struct strset_s *enabled_extensions; + int (*extension_enabled) (struct qfv_instance_s *inst, + const char *ext); + VkDebugUtilsMessengerEXT debug_handle; + uint32_t numDevices; + qfv_physdev_t *devices; + qfv_debugstack_t debug_stack; +} qfv_instance_t; + +struct vulkan_ctx_s; +qfv_instance_t *QFV_CreateInstance (struct vulkan_ctx_s *ctx, + const char *appName, + uint32_t appVersion, + const char **layers, + const char **extensions); +void QFV_DestroyInstance (qfv_instance_t *instance); + +VkSampleCountFlagBits QFV_GetMaxSampleCount (qfv_physdev_t *physdev); + +#endif // __QF_Vulkan_instance_h diff --git a/include/QF/Vulkan/memory.h b/include/QF/Vulkan/memory.h new file mode 100644 index 000000000..91885d31b --- /dev/null +++ b/include/QF/Vulkan/memory.h @@ -0,0 +1,22 @@ +#ifndef __QF_Vulkan_memory_h +#define __QF_Vulkan_memory_h + +#include "QF/darray.h" + +typedef struct qfv_mappedmemrange_s { + VkDeviceMemory object; + VkDeviceSize offset; + VkDeviceSize size; +} qfv_mappedmemrange_t; + +typedef struct qfv_mappedmemrangeset_s + DARRAY_TYPE (qfv_mappedmemrange_t) qfv_mappedmemrangeset_t; + +struct qfv_device_s; + +void *QFV_MapMemory (struct qfv_device_s *device, VkDeviceMemory object, + VkDeviceSize offset, VkDeviceSize size); +void QFV_FlushMemory (struct qfv_device_s *device, + qfv_mappedmemrangeset_t *ranges); + +#endif//__QF_Vulkan_memory_h diff --git a/include/QF/Vulkan/pipeline.h b/include/QF/Vulkan/pipeline.h new file mode 100644 index 000000000..bd6e292a9 --- /dev/null +++ b/include/QF/Vulkan/pipeline.h @@ -0,0 +1,156 @@ +#ifndef __QF_Vulkan_pipeline_h +#define __QF_Vulkan_pipeline_h + +#include "QF/darray.h" + +typedef struct qfv_pipelineshaderstateset_s + DARRAY_TYPE (VkPipelineShaderStageCreateInfo) qfv_pipelineshaderstateset_s; + +#define QFV_AllocPipelineShaderStageSet(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_pipelineshaderstateset_s, num, allocator) + +typedef struct qfv_vertexinputbindingset_s + DARRAY_TYPE (VkVertexInputBindingDescription) qfv_vertexinputbindingset_t; + +#define QFV_AllocVertexInputBindingSet(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_vertexinputbindingset_t, num, allocator) + +typedef struct qfv_vertexinputattributeset_s + DARRAY_TYPE (VkVertexInputAttributeDescription) + qfv_vertexinputattributeset_t; + +#define QFV_AllocVertexInputAttributeSet(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_vertexinputattributeset_t, num, allocator) + +typedef struct qfv_vertexinputstate_s { + qfv_vertexinputbindingset_t *bindings; + qfv_vertexinputattributeset_t *attributes; +} qfv_vertexinputstate_t; + +typedef struct qfv_pipelineinputassembly_s { + VkPrimitiveTopology topology; + VkBool32 primativeRestartEnable; +} qfv_pipelineinputassembly_t; + +typedef struct qfv_pipelinetessellation_s { + uint32_t patchControlPoints; +} qfv_pipelinetessellation_t; + +typedef struct qfv_viewportset_s DARRAY_TYPE (VkViewport) qfv_viewportset_t; + +#define QFV_AllocViewportSet(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_viewportset_t, num, allocator) + +typedef struct qfv_scissorsset_s DARRAY_TYPE (VkRect2D) qfv_scissorsset_t; + +#define QFV_AllocScissorsSet(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_scissorsset_t, num, allocator) + +typedef struct qfv_viewportinfo_s { + qfv_viewportset_t *viewportset; + qfv_scissorsset_t *scissorsset; +} qfv_viewportinfo_t; + +typedef struct qfv_pipelinerasterization_s { + VkBool32 depthClampEnable; + VkBool32 rasterizerDiscardEnable; + VkPolygonMode polygonMode; + VkCullModeFlags cullMode; + VkFrontFace frontFace; + VkBool32 depthBiasEnable; + float depthBiasConstantFactor; + float depthBiasClamp; + float depthBiasSlopeFactor; + float lineWidth; +} qfv_pipelinerasterization_t; + +typedef struct qfv_pipelinemultisample_s { + VkSampleCountFlagBits rasterizationSamples; + VkBool32 sampleShadingEnable; + float minSampleShading; + const VkSampleMask *sampleMask; + VkBool32 alphaToCoverageEnable; + VkBool32 alphaToOneEnable; +} qfv_pipelinemultisample_t; + +typedef struct qfv_pipelinedepthandstencil_s { + VkBool32 depthTestEnable; + VkBool32 depthWriteEnable; + VkCompareOp depthCompareOp; + VkBool32 depthBoundsTestEnable; + VkBool32 stencilTestEnable; + VkStencilOpState front; + VkStencilOpState back; + float minDepthBounds; + float maxDepthBounds; +} qfv_pipelinedepthandstencil_t; + +typedef struct qfv_blendattachmentset_s + DARRAY_TYPE (VkPipelineColorBlendAttachmentState) qfv_blendattachmentset_t; + +#define QFV_AllocBlendAttachmentSet(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_blendattachmentset_t, num, allocator) + +typedef struct qfv_dynamicstateset_s + DARRAY_TYPE (VkDynamicState) qfv_dynamicstateset_t; + +#define QFV_AllocDynamicStateSet(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_dynamicstateset_t, num, allocator) + +typedef struct qfv_pushconstantrangeset_s + DARRAY_TYPE (VkPushConstantRange) qfv_pushconstantrangeset_t; + +#define QFV_AllocPushConstantRangeSet(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_pushconstantrangeset_t, num, allocator) + +typedef struct qfv_pipelineset_s DARRAY_TYPE (VkPipeline) qfv_pipelineset_t; + +#define QFV_AllocPipelineSet(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_pipelineset_t, num, allocator) + +typedef struct qfv_graphicspipelinecreateinfoset_s + DARRAY_TYPE (VkGraphicsPipelineCreateInfo) + qfv_graphicspipelinecreateinfoset_t; + +#define QFV_AllocGraphicsPipelineCreateInfoSet(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_graphicspipelinecreateinfoset_t, num, allocator) + +typedef struct qfv_computepipelinecreateinfoset_s + DARRAY_TYPE (VkComputePipelineCreateInfo) + qfv_computepipelinecreateinfoset_t; + +#define QFV_AllocComputePipelineCreateInfoSet(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_computepipelinecreateinfoset_t, num, allocator) + +typedef struct qfv_pipelinecacheset_s + DARRAY_TYPE (VkPipelineCache) qfv_pipelinecacheset_t; + +#define QFV_AllocPipelineCacheSet(num, allocator) \ + DARRAY_ALLOCFIXED (qfv_pipelinecacheset_t, num, allocator) + +struct dstring_s; +VkPipelineCache QFV_CreatePipelineCache (struct qfv_device_s *device, + struct dstring_s *cacheData); +struct dstring_s *QFV_GetPipelineCacheData (struct qfv_device_s *device, + VkPipelineCache cache); +void QFV_MergePipelineCaches (struct qfv_device_s *device, + VkPipelineCache targetCache, + qfv_pipelinecacheset_t *sourceCaches); + +struct qfv_descriptorsetlayoutset_s; +VkPipelineLayout +QFV_CreatePipelineLayout (struct qfv_device_s *device, + struct qfv_descriptorsetlayoutset_s *layouts, + qfv_pushconstantrangeset_t *pushConstants); +qfv_pipelineset_t * +QFV_CreateGraphicsPipelines (struct qfv_device_s *device, + VkPipelineCache cache, + qfv_graphicspipelinecreateinfoset_t *gpciSet); +qfv_pipelineset_t * +QFV_CreateComputePipelines (struct qfv_device_s *device, + VkPipelineCache cache, + qfv_computepipelinecreateinfoset_t *cpciSet); +void +QFV_DestroyPipeline (struct qfv_device_s *device, VkPipeline pipeline); + +#endif//__QF_Vulkan_pipeline_h diff --git a/include/QF/Vulkan/projection.h b/include/QF/Vulkan/projection.h new file mode 100644 index 000000000..7936008aa --- /dev/null +++ b/include/QF/Vulkan/projection.h @@ -0,0 +1,12 @@ +#ifndef __QF_Vulkan_projection_h +#define __QF_Vulkan_projection_h + +#include "QF/simd/types.h" + +void QFV_Orthographic (mat4f_t proj, float xmin, float xmax, + float ymin, float ymax, float znear, float zfar); +// fov_x and fov_y are tan(fov/2) for x and y respectively +void QFV_PerspectiveTan (mat4f_t proj, float fov_x, float fov_y); +void QFV_PerspectiveCos (mat4f_t proj, float fov); + +#endif//__QF_Vulkan_projection_h diff --git a/include/QF/Vulkan/qf_alias.h b/include/QF/Vulkan/qf_alias.h new file mode 100644 index 000000000..8893bd3a0 --- /dev/null +++ b/include/QF/Vulkan/qf_alias.h @@ -0,0 +1,101 @@ +/* + qf_alias.h + + Vulkan specific alias model stuff + + Copyright (C) 2012 Bill Currie + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2012/1/7 + Date: 2021/1/18 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __QF_Vulkan_qf_alias_h +#define __QF_Vulkan_qf_alias_h + +#include "QF/darray.h" +#include "QF/model.h" +#include "QF/modelgen.h" +#include "QF/Vulkan/qf_vid.h" +#include "QF/Vulkan/command.h" + +typedef struct aliasvrt_s { + float vertex[4]; + float normal[4]; +} aliasvrt_t; + +typedef struct aliasuv_s { + float u, v; +} aliasuv_t; + +typedef struct qfv_alias_mesh_s { + VkBuffer vertex_buffer; + VkBuffer uv_buffer; + VkBuffer index_buffer; + VkDeviceMemory memory; +} qfv_alias_mesh_t; + +typedef struct qfv_alias_skin_s { + VkDeviceMemory memory; + VkImage image; + VkImageView view; + byte colors[4]; + VkDescriptorSet descriptor; +} qfv_alias_skin_t; + +typedef enum { + QFV_aliasDepth, + QFV_aliasGBuffer, + QFV_aliasTranslucent, + + QFV_aliasNumPasses +} QFV_AliasSubpass; + +typedef struct aliasindset_s + DARRAY_TYPE (unsigned) aliasindset_t; + +typedef struct aliasctx_s { + VkSampler sampler; +} aliasctx_t; + +struct vulkan_ctx_s; +struct entity_s; +struct mod_alias_ctx_s; + +void Vulkan_Mod_LoadAllSkins (struct mod_alias_ctx_s *alias_ctx, + struct vulkan_ctx_s *ctx); +void Vulkan_Mod_FinalizeAliasModel (struct mod_alias_ctx_s *alias_ctx, + struct vulkan_ctx_s *ctx); +void Vulkan_Mod_LoadExternalSkins (struct mod_alias_ctx_s *alias_ctx, + struct vulkan_ctx_s *ctx); +void Vulkan_Mod_MakeAliasModelDisplayLists (struct mod_alias_ctx_s *alias_ctx, + void *_m, int _s, int extra, + struct vulkan_ctx_s *ctx); + +void Vulkan_AliasAddSkin (struct vulkan_ctx_s *ctx, qfv_alias_skin_t *skin); +void Vulkan_AliasRemoveSkin (struct vulkan_ctx_s *ctx, qfv_alias_skin_t *skin); + +void Vulkan_Alias_Init (struct vulkan_ctx_s *ctx); +void Vulkan_Alias_Setup (struct vulkan_ctx_s *ctx); +void Vulkan_Alias_Shutdown (struct vulkan_ctx_s *ctx); + +#endif//__QF_Vulkan_qf_alias_h diff --git a/include/QF/Vulkan/qf_bsp.h b/include/QF/Vulkan/qf_bsp.h new file mode 100644 index 000000000..8f7df82db --- /dev/null +++ b/include/QF/Vulkan/qf_bsp.h @@ -0,0 +1,393 @@ +/* + qf_bsp.h + + Vulkan specific brush model stuff + + Copyright (C) 2012 Bill Currie + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2012/1/7 + Date: 2021/1/18 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __QF_Vulkan_qf_bsp_h +#define __QF_Vulkan_qf_bsp_h + +#include "QF/darray.h" +#include "QF/model.h" +#include "QF/Vulkan/qf_vid.h" +#include "QF/Vulkan/command.h" + +#include "QF/simd/types.h" + +/** \defgroup vulkan_bsp Brush model rendering + \ingroup vulkan +*/ + +/** Represent a single face (polygon) of a brush model. + * + * There is one of these for each face in the bsp (brush) model, built at run + * time when the model is loaded (actually, after all models are loaded but + * before rendering begins). + */ +typedef struct bsp_face_s { + uint32_t first_index; ///< index of first index in poly_indices + uint32_t index_count; ///< includes primitive restart + uint32_t tex_id; ///< texture bound to this face (maybe animated) + uint32_t flags; ///< face drawing (alpha, side, sky, turb) +} bsp_face_t; + +/** Represent a brush model, both main and sub-model. + * + * Used for rendering non-world models. + */ +typedef struct bsp_model_s { + uint32_t first_face; + uint32_t face_count; +} bsp_model_t; + +#if 0 +typedef struct texname_s { + char name[MIPTEXNAME]; +} texname_t; + +typedef struct texmip_s { + uint32_t width; + uint32_t height; + uint32_t offsets[MIPLEVELS]; +} texmip_t; +#endif +/** \defgroup vulkan_bsp_texanim Animated Textures + * \ingroup vulkan_bsp + * + * Brush models support texture animations. For general details, see + * \ref bsp_texture_animation. These structures allow for quick lookup + * of the correct texture to use in an animation cycle, or even whether there + * is an animation cycle. + */ +///@{ +/** Represent a texture's animation group. + * + * Every texture is in an animation group, even when not animated. When the + * texture is not animated, `count` is 1, otherwise `count` is the number of + * frames in the group, thus every texture has at least one frame. + * + * Each texture in a particular groupp shares the same `base` frame, with + * `offset` giving the texture's relative frame number within the group. + * The current frame is given by `base + (anim_index + offset) % count` where + * `anim_index` is the global time-based texture animation frame. + */ +typedef struct texanim_s { + uint16_t base; ///< first frame in group + byte offset; ///< relative frame in group + byte count; ///< number of frames in group +} texanim_t; + +/** Holds texture animation data for brush models. + * + * Brush models support one or two texture animation groups, based on the + * entity's frame (0 or non-0). When the entity's frame is 0, group 0 is used, + * otherwise group 1 is used. If there is no alternate (group 1) animation + * data for the texture, then the texture's group 0 data is copied to group 1 + * in order to avoid coplications in selecting which texture a face is to use. + * + * As all of a group's frames are together, `frame_map` is used to get the + * actual texture id for the frame. + */ +typedef struct texdata_s { +// texname_t *names; +// texmip_t **mips; + texanim_t *anim_main; ///< group 0 animations + texanim_t *anim_alt; ///< group 1 animations + uint16_t *frame_map; ///< map from texture frame to texture id +// int num_tex; +} texdata_t; +///@} + +/** \defgroup vulkan_bsp_draw Brush model drawing + * \ingroup vulkan_bsp + */ +///@{ +typedef struct vulktex_s { + struct qfv_tex_s *tex; + VkDescriptorSet descriptor; + int tex_id; +} vulktex_t; + +typedef struct regtexset_s + DARRAY_TYPE (vulktex_t *) regtexset_t; + +/** Represent a single draw call. + * + * For each texture that has faces to be rendered, one or more draw calls is + * made. Normally, only one call per texture is made, but if different models + * use the same texture, then a separate draw call is made for each model. + * When multiple entities use the same model, instanced rendering is used to + * draw all the faces sharing a texture for all the entities using that model. + * Thus when there are multiple draw calls for a single texture, they are + * grouped together so there is only one bind per texture. + * + * The index buffer is populated every frame with the vertex indices of the + * faces to be rendered for the current frame, grouped by texture and instance + * id (model render id). + * + * The model render id is assigned after models are loaded but before rendering + * begins and remains constant until the next time models are loaded (level + * change). + * + * The entid buffer is also populated every frame with the render id of the + * entities to be drawn that frame, It is used to map gl_InstanceIndex to + * entity id so as to look up the entity's transform and color (and any other + * data in the future). + * + * \dot + * digraph vulkan_bsp_draw_call { + * layout=dot; rankdir=LR; compound=true; nodesep=1.0; + * vertices [shape=none,label=< + * + * + * + * + * + *
vertex
vertex
...
vertex
vertex
>]; + * indices [shape=none,label=< + * + * + * + * + * + *
index
index
...
index
index
>]; + * entids [shape=none,label=< + * + * + * + * + * + * + *
entid
...
entid
entid
...
entid
>]; + * entdata [shape=none,label=< + * + * + * + * + * + * + *
transformcolor
transformcolor
...
transformcolor
...
transformcolor
>]; + * drawcall [shape=none,label=< + * + * + * + * + * + * + *
tex_id
inst_id
first_index
index_count
first_instance
instance_count
>]; + * textures [shape=none,label=< + * + * + * + * + * + *
texture
texture
texture
...
texture
>]; + * vertex [label="vertex shader"]; + * fragment [label="fragment shader"]; + * drawcall:tex -> textures:p; + * drawcall:ind -> indices:p; + * drawcall:inst -> entids:p; + * entids:p -> entdata:p; + * indices:p -> vertices:p; + * vertex -> entdata [label="storage buffer"]; + * vertex -> entids [label="per instance"]; + * vertex -> indices [label="index buffer"]; + * vertex -> vertices [label="per vertex"]; + * fragment -> textures [label="per call"]; + * } + * \enddot + */ +///@{ +typedef struct bsp_draw_s { + uint32_t tex_id; ///< texture to bind for this draw call + uint32_t inst_id; ///< model render id owning this draw call + uint32_t index_count; ///< number of indices for this draw call + uint32_t instance_count; ///< number of instances to draw + uint32_t first_index; ///< index into index buffer + uint32_t first_instance; ///< index into entid buffer +} bsp_draw_t; + +typedef struct bsp_drawset_s + DARRAY_TYPE (bsp_draw_t) bsp_drawset_t; +///@} + +/** Tag models that are to be queued for translucent drawing. + */ +#define INST_ALPHA (1u<<31) + +/** Representation of a single face queued for drawing. + */ +///@{ +typedef struct instface_s { + uint32_t inst_id; ///< model render id owning this face + uint32_t face; ///< index of face in context array +} instface_t; + +typedef struct bsp_instfaceset_s + DARRAY_TYPE (instface_t) bsp_instfaceset_t; +///@} + +/** Track entities using a model. + */ +///@{ +typedef struct bsp_modelentset_s + DARRAY_TYPE (uint32_t) bsp_modelentset_t; + +/** Represent a single model and the entities using it. + */ +typedef struct bsp_instance_s { + int first_instance; ///< index into entid buffer + bsp_modelentset_t entities; ///< list of entity render ids using this model +} bsp_instance_t; +///@} + +typedef struct bsp_pass_s { + vec4f_t position; ///< view position + const struct mod_brush_s *brush;///< data for current model + struct bspctx_s *bsp_context; ///< owning bsp context + /** \name GPU data + * + * The indices to be drawn and the entity ids associated with each draw + * instance are updated each frame. The pointers are to the per-frame + * mapped buffers for the respective data. + */ + ///@{ + uint32_t *indices; ///< polygon vertex indices + uint32_t index_count; ///< number of indices written to buffer + uint32_t *entid_data; ///< instance id to entity id map + uint32_t entid_count; ///< numer of entids written to buffer + ///@} + /** \name Potentially Visible Sets + * + * For an object to be in the PVS, its frame id must match the current + * visibility frame id, thus clearing all sets is done by incrementing + * `vis_frame`, and adding an object to the PVS is done by setting its + * current frame id to the current visibility frame id. + */ + ///@{ + int vis_frame; ///< current visibility frame id + int *face_frames; ///< per-face visibility frame ids + int *leaf_frames; ///< per-leaf visibility frame ids + int *node_frames; ///< per-node visibility frame ids + ///@} + bsp_instfaceset_t *face_queue; ///< per-texture face queues + regtexset_t *textures; ///< textures to bind when emitting calls + int num_queues; ///< number of pipeline queues + bsp_drawset_t *draw_queues; ///< per-pipeline draw queues + uint32_t inst_id; ///< render id of current model + bsp_instance_t *instances; ///< per-model entid lists + // FIXME There are several potential optimizations here: + // 1) ent_frame could be forced to be 0 or 1 and then used to index a + // two-element array of texanim pointers + // 2) ent_frame could be a pointer to the correct texanim array + // 3) could update a tex_id map each frame and unconditionally index that + // + // As the texture id is used for selecting the face queue, 3 could be used + // for mapping all textures to 1 or two queues for shadow rendering + int ent_frame; ///< animation frame of current entity +} bsp_pass_t; +///@} + +/// \ingroup vulkan_bsp +///@{ +typedef enum { + QFV_bspSolid, + QFV_bspSky, + QFV_bspTrans, // texture translucency + QFV_bspTurb, // also translucent via r_wateralpha + + QFV_bspNumPasses +} QFV_BspQueue; + +typedef struct bspframe_s { + uint32_t *index_data; // pointer into mega-buffer for this frame (c) + uint32_t index_offset; // offset of index_data within mega-buffer (c) + uint32_t index_count; // number if indices queued (d) + uint32_t *entid_data; + uint32_t entid_offset; + uint32_t entid_count; + qfv_cmdbufferset_t cmdSet; +} bspframe_t; + +typedef struct bspframeset_s + DARRAY_TYPE (bspframe_t) bspframeset_t; + +/** Main BSP context structure + * + * This holds all the state and resources needed for rendering brush models. + */ +typedef struct bspctx_s { + + vulktex_t notexture; ///< replacement for invalid textures + + struct scrap_s *light_scrap; + struct qfv_stagebuf_s *light_stage; + + int num_models; ///< number of loaded brush models + bsp_model_t *models; ///< all loaded brush models + bsp_face_t *faces; ///< all faces from all loaded brush models + uint32_t *poly_indices; ///< face indices from all loaded brush models + + regtexset_t registered_textures;///< textures for all loaded brush models + texdata_t texdata; ///< texture animation data + int anim_index; ///< texture animation frame (5fps) + struct qfv_tex_s *default_skysheet; + struct qfv_tex_s *skysheet_tex; ///< scrolling sky texture for current map + + struct qfv_tex_s *default_skybox; + struct qfv_tex_s *skybox_tex; ///< sky box texture for current map + VkDescriptorSet skybox_descriptor; + + bsp_pass_t main_pass; ///< camera view depth, gbuffer, etc + + VkSampler sampler; + + VkDeviceMemory texture_memory; + size_t vertex_buffer_size; + size_t index_buffer_size; + VkBuffer vertex_buffer; + VkDeviceMemory vertex_memory; + VkBuffer index_buffer; + VkDeviceMemory index_memory; + VkBuffer entid_buffer; + VkDeviceMemory entid_memory; + bspframeset_t frames; +} bspctx_t; + +struct vulkan_ctx_s; +void Vulkan_LoadSkys (const char *sky, struct vulkan_ctx_s *ctx); +void Vulkan_RegisterTextures (model_t **models, int num_models, + struct vulkan_ctx_s *ctx); +void Vulkan_BuildDisplayLists (model_t **models, int num_models, + struct vulkan_ctx_s *ctx); +void Vulkan_Bsp_Init (struct vulkan_ctx_s *ctx); +void Vulkan_Bsp_Setup (struct vulkan_ctx_s *ctx); +void Vulkan_Bsp_Shutdown (struct vulkan_ctx_s *ctx); +///@} + +#endif//__QF_Vulkan_qf_bsp_h diff --git a/include/QF/Vulkan/qf_compose.h b/include/QF/Vulkan/qf_compose.h new file mode 100644 index 000000000..cc59f8b63 --- /dev/null +++ b/include/QF/Vulkan/qf_compose.h @@ -0,0 +1,59 @@ +/* + qf_compose.h + + Vulkan compose pass + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/2/24 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __QF_Vulkan_qf_compose_h +#define __QF_Vulkan_qf_compose_h + +#include "QF/darray.h" +#include "QF/model.h" +#include "QF/modelgen.h" +#include "QF/Vulkan/qf_vid.h" +#include "QF/Vulkan/command.h" + +#define COMPOSE_IMAGE_INFOS 1 + +typedef struct composeframe_s { + VkDescriptorImageInfo imageInfo[COMPOSE_IMAGE_INFOS]; + VkWriteDescriptorSet descriptors[COMPOSE_IMAGE_INFOS]; +} composeframe_t; + +typedef struct composeframeset_s + DARRAY_TYPE (composeframe_t) composeframeset_t; + +typedef struct composectx_s { + composeframeset_t frames; +} composectx_t; + +struct vulkan_ctx_s; + +void Vulkan_Compose_Init (struct vulkan_ctx_s *ctx); +void Vulkan_Compose_Setup (struct vulkan_ctx_s *ctx); +void Vulkan_Compose_Shutdown (struct vulkan_ctx_s *ctx); + +#endif//__QF_Vulkan_qf_compose_h diff --git a/include/QF/Vulkan/qf_draw.h b/include/QF/Vulkan/qf_draw.h new file mode 100644 index 000000000..c7ba2b1d6 --- /dev/null +++ b/include/QF/Vulkan/qf_draw.h @@ -0,0 +1,90 @@ +/* + qf_draw.h + + vulkan specific draw function + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_Vulkan_qf_draw_h +#define __QF_Vulkan_qf_draw_h + +#include "QF/screen.h" + +struct vulkan_ctx_s; +struct qpic_s; +struct font_s; +struct draw_charbuffer_s; + +void Vulkan_Draw_CharBuffer (int x, int y, struct draw_charbuffer_s *buffer, + struct vulkan_ctx_s *ctx); +void Vulkan_Draw_Init (struct vulkan_ctx_s *ctx); +void Vulkan_Draw_Setup (struct vulkan_ctx_s *ctx); +void Vulkan_Draw_Shutdown (struct vulkan_ctx_s *ctx); +void Vulkan_Draw_Character (int x, int y, unsigned ch, + struct vulkan_ctx_s *ctx); +void Vulkan_Draw_String (int x, int y, const char *str, + struct vulkan_ctx_s *ctx); +void Vulkan_Draw_nString (int x, int y, const char *str, int count, + struct vulkan_ctx_s *ctx); +void Vulkan_Draw_AltString (int x, int y, const char *str, + struct vulkan_ctx_s *ctx); +void Vulkan_Draw_ConsoleBackground (int lines, byte alpha, + struct vulkan_ctx_s *ctx); +void Vulkan_Draw_Crosshair (struct vulkan_ctx_s *ctx); +void Vulkan_Draw_CrosshairAt (int ch, int x, int y, struct vulkan_ctx_s *ctx); +void Vulkan_Draw_TileClear (int x, int y, int w, int h, + struct vulkan_ctx_s *ctx); +void Vulkan_Draw_Fill (int x, int y, int w, int h, int c, + struct vulkan_ctx_s *ctx); +void Vulkan_Draw_Line (int x0, int y0, int x1, int y1, int c, + struct vulkan_ctx_s *ctx); +void Vulkan_Draw_TextBox (int x, int y, int width, int lines, byte alpha, + struct vulkan_ctx_s *ctx); +void Vulkan_Draw_FadeScreen (struct vulkan_ctx_s *ctx); +void Vulkan_Draw_BlendScreen (quat_t color, struct vulkan_ctx_s *ctx); +struct qpic_s *Vulkan_Draw_CachePic (const char *path, bool alpha, + struct vulkan_ctx_s *ctx); +void Vulkan_Draw_UncachePic (const char *path, struct vulkan_ctx_s *ctx); +struct qpic_s *Vulkan_Draw_MakePic (int width, int height, const byte *data, + struct vulkan_ctx_s *ctx); +void Vulkan_Draw_DestroyPic (struct qpic_s *pic, struct vulkan_ctx_s *ctx); +struct qpic_s *Vulkan_Draw_PicFromWad (const char *name, + struct vulkan_ctx_s *ctx); +void Vulkan_Draw_Pic (int x, int y, struct qpic_s *pic, + struct vulkan_ctx_s *ctx); +void Vulkan_Draw_FitPic (int x, int y, int width, int height, + struct qpic_s *pic, struct vulkan_ctx_s *ctx); +void Vulkan_Draw_Picf (float x, float y, struct qpic_s *pic, + struct vulkan_ctx_s *ctx); +void Vulkan_Draw_SubPic(int x, int y, struct qpic_s *pic, + int srcx, int srcy, int width, int height, + struct vulkan_ctx_s *ctx); +int Vulkan_Draw_AddFont (struct font_s *font, struct vulkan_ctx_s *ctx); +void Vulkan_Draw_Glyph (int x, int y, int fontid, int glyphid, int c, + struct vulkan_ctx_s *ctx); + +void Vulkan_LineGraph (int x, int y, int *h_vals, int count, int height, + struct vulkan_ctx_s *ctx); +void Vulkan_SetScrFuncs (SCR_Func *scr_funcs, struct vulkan_ctx_s *ctx); + +#endif//__QF_Vulkan_qf_draw_h diff --git a/include/QF/Vulkan/qf_iqm.h b/include/QF/Vulkan/qf_iqm.h new file mode 100644 index 000000000..d39051882 --- /dev/null +++ b/include/QF/Vulkan/qf_iqm.h @@ -0,0 +1,113 @@ +/* + qf_iqm.h + + Vulkan specific iqm model stuff + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/5/3 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __QF_Vulkan_qf_iqm_h +#define __QF_Vulkan_qf_iqm_h + +#include "QF/darray.h" +#include "QF/model.h" +#include "QF/modelgen.h" +#include "QF/Vulkan/qf_vid.h" +#include "QF/Vulkan/command.h" + +// geometry attributes +typedef struct iqmgvert_s { + float vertex[3]; + byte bones[4]; // uint + byte weights[4]; // unorm +} iqmgvert_t; + +// rendering attributes +typedef struct iqmrvert_s { + float uv[2]; + float normal[3]; + float tangent[4]; + byte color[4]; // unorm +} iqmrvert_t; + +typedef struct qfv_iqm_skin_s { + VkImageView view; + byte colora[4]; + byte colorb[4]; + VkDescriptorSet descriptor; +} qfv_iqm_skin_t; + +typedef struct qfv_iqm_s { + VkBuffer geom_buffer; + VkBuffer rend_buffer; + VkBuffer index_buffer; + qfv_iqm_skin_t *skins; + struct qfv_resource_s *mesh; + struct qfv_resource_s *bones; + VkBuffer bones_buffer; + VkDescriptorSet *bones_descriptors; // one per frame FIXME per instance!!! +} qfv_iqm_t; + +typedef enum { + QFV_iqmDepth, + QFV_iqmGBuffer, + QFV_iqmTranslucent, + + QFV_iqmNumPasses +} QFV_IQMSubpass; + +typedef struct iqm_frame_s { + qfv_cmdbufferset_t cmdSet; +} iqm_frame_t; + +typedef struct iqm_frameset_s + DARRAY_TYPE (iqm_frame_t) iqm_frameset_t; + +typedef struct iqmindset_s + DARRAY_TYPE (unsigned) iqmindset_t; + +typedef struct iqmctx_s { + iqm_frameset_t frames; + VkSampler sampler; + struct qfv_dsmanager_s *dsmanager; +} iqmctx_t; + +struct vulkan_ctx_s; +struct entity_s; +struct mod_iqm_ctx_s; +struct iqm_s; + +void Vulkan_Mod_IQMFinish (struct model_s *mod, struct vulkan_ctx_s *ctx); + +void Vulkan_IQMAddBones (struct vulkan_ctx_s *ctx, struct iqm_s *iqm); +void Vulkan_IQMRemoveBones (struct vulkan_ctx_s *ctx, struct iqm_s *iqm); + +void Vulkan_IQMAddSkin (struct vulkan_ctx_s *ctx, qfv_iqm_skin_t *skin); +void Vulkan_IQMRemoveSkin (struct vulkan_ctx_s *ctx, qfv_iqm_skin_t *skin); + +void Vulkan_IQM_Init (struct vulkan_ctx_s *ctx); +void Vulkan_IQM_Setup (struct vulkan_ctx_s *ctx); +void Vulkan_IQM_Shutdown (struct vulkan_ctx_s *ctx); + +#endif//__QF_Vulkan_qf_iqm_h diff --git a/include/QF/Vulkan/qf_lighting.h b/include/QF/Vulkan/qf_lighting.h new file mode 100644 index 000000000..54d4c3856 --- /dev/null +++ b/include/QF/Vulkan/qf_lighting.h @@ -0,0 +1,120 @@ +/* + qf_lighting.h + + Vulkan lighting pass + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/2/23 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __QF_Vulkan_qf_lighting_h +#define __QF_Vulkan_qf_lighting_h + +#include "QF/darray.h" +#include "QF/model.h" +#include "QF/modelgen.h" +#include "QF/scene/light.h" +#include "QF/Vulkan/qf_vid.h" +#include "QF/Vulkan/command.h" +#include "QF/Vulkan/image.h" +#include "QF/simd/types.h" + +typedef struct qfv_lightmatset_s DARRAY_TYPE (mat4f_t) qfv_lightmatset_t; + +#define MaxLights 768 + +#define ST_NONE 0 // no shadows +#define ST_PLANE 1 // single plane shadow map (small spotlight) +#define ST_CASCADE 2 // cascaded shadow maps +#define ST_CUBE 3 // cubemap (omni, large spotlight) + +typedef struct qfv_light_buffer_s { + light_t lights[MaxLights] __attribute__((aligned(16))); + int lightCount; + //mat4f_t shadowMat[MaxLights]; + //vec4f_t shadowCascade[MaxLights]; +} qfv_light_buffer_t; + +#define LIGHTING_BUFFER_INFOS 1 +#define LIGHTING_ATTACH_INFOS 5 +#define LIGHTING_SHADOW_INFOS 32 +#define LIGHTING_DESCRIPTORS (LIGHTING_BUFFER_INFOS + LIGHTING_ATTACH_INFOS + 1) + +typedef struct lightingframe_s { + VkBuffer light_buffer; + VkDescriptorBufferInfo bufferInfo[LIGHTING_BUFFER_INFOS]; + VkDescriptorImageInfo attachInfo[LIGHTING_ATTACH_INFOS]; + VkDescriptorImageInfo shadowInfo[LIGHTING_SHADOW_INFOS]; + union { + VkWriteDescriptorSet descriptors[LIGHTING_DESCRIPTORS]; + struct { + VkWriteDescriptorSet bufferWrite[LIGHTING_BUFFER_INFOS]; + VkWriteDescriptorSet attachWrite[LIGHTING_ATTACH_INFOS]; + VkWriteDescriptorSet shadowWrite; + }; + }; +} lightingframe_t; + +typedef struct lightingframeset_s + DARRAY_TYPE (lightingframe_t) lightingframeset_t; + +typedef struct light_renderer_s { + VkRenderPass renderPass; // shared + VkFramebuffer framebuffer; + VkImage image; // shared + VkImageView view; + uint32_t size; + uint32_t layer; + uint32_t numLayers; + int mode; +} light_renderer_t; + +typedef struct light_renderer_set_s + DARRAY_TYPE (light_renderer_t) light_renderer_set_t; + +typedef struct lightingctx_s { + lightingframeset_t frames; + VkPipeline pipeline; + VkSampler sampler; + VkDeviceMemory light_memory; + struct qfv_resource_s *shadow_resources; + qfv_lightmatset_t light_mats; + qfv_imageset_t light_images; + light_renderer_set_t light_renderers; + + VkRenderPass renderpass_6; + VkRenderPass renderpass_4; + VkRenderPass renderpass_1; + + struct lightingdata_s *ldata; + struct scene_s *scene; +} lightingctx_t; + +struct vulkan_ctx_s; + +void Vulkan_Lighting_Init (struct vulkan_ctx_s *ctx); +void Vulkan_Lighting_Setup (struct vulkan_ctx_s *ctx); +void Vulkan_Lighting_Shutdown (struct vulkan_ctx_s *ctx); +void Vulkan_LoadLights (struct scene_s *scene, struct vulkan_ctx_s *ctx); + +#endif//__QF_Vulkan_qf_lighting_h diff --git a/include/QF/Vulkan/qf_lightmap.h b/include/QF/Vulkan/qf_lightmap.h new file mode 100644 index 000000000..d9371dbaa --- /dev/null +++ b/include/QF/Vulkan/qf_lightmap.h @@ -0,0 +1,50 @@ +/* + qf_lightmap.h + + Vulkan lightmap stuff from the renderer. + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_Vulkan_qf_lightmap_h +#define __QF_Vulkan_qf_lightmap_h + +#define MAX_LIGHTMAPS 1024 +#define BLOCK_WIDTH 64 +#define BLOCK_HEIGHT 64 + +#include "QF/Vulkan/qf_vid.h" + +struct vulkan_ctx_s; +struct model_s; +struct mod_brush_s; +struct msurface_s; +struct transform_s; + +void Vulkan_lightmap_init (struct vulkan_ctx_s *ctx); +void Vulkan_BuildLightmaps (struct model_s **models, int num_models, struct vulkan_ctx_s *ctx); +void Vulkan_CalcLightmaps (struct vulkan_ctx_s *ctx); +void Vulkan_BuildLightMap (const struct transform_s *transform, struct mod_brush_s *brush, struct msurface_s *surf, struct vulkan_ctx_s *ctx); +VkImageView Vulkan_LightmapImageView (struct vulkan_ctx_s *ctx) __attribute__((pure)); +void Vulkan_FlushLightmaps (struct vulkan_ctx_s *ctx); + +#endif//__QF_Vulkan_qf_lightmap_h diff --git a/include/QF/Vulkan/qf_main.h b/include/QF/Vulkan/qf_main.h new file mode 100644 index 000000000..d17053dca --- /dev/null +++ b/include/QF/Vulkan/qf_main.h @@ -0,0 +1,37 @@ +/* + qf_main.h + + Vulkan main stuff from the renderer. + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_Vulkan_qf_main_h +#define __QF_Vulkan_qf_main_h + +struct vulkan_ctx_s; +struct entqueue_s; +struct scene_s; + +void Vulkan_NewScene (struct scene_s *scene, struct vulkan_ctx_s *ctx); + +#endif//__QF_Vulkan_qf_main_h diff --git a/include/QF/Vulkan/qf_matrices.h b/include/QF/Vulkan/qf_matrices.h new file mode 100644 index 000000000..e1c41991f --- /dev/null +++ b/include/QF/Vulkan/qf_matrices.h @@ -0,0 +1,84 @@ +/* + qf_matrices.h + + Vulkan matrix "pass" + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/12/8 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __QF_Vulkan_qf_matrices_h +#define __QF_Vulkan_qf_matrices_h + +#include "QF/darray.h" +#include "QF/Vulkan/qf_vid.h" +#include "QF/Vulkan/command.h" +#include "QF/Vulkan/image.h" +#include "QF/simd/types.h" + +typedef __attribute__((aligned(64))) struct qfv_matrix_buffer_s { + // projection and view matrices (model is push constant) + mat4f_t Projection3d; + mat4f_t View[6]; + mat4f_t Sky; + mat4f_t Projection2d; + vec2f_t ScreenSize; +} qfv_matrix_buffer_t; + +typedef struct matrixframe_s { + VkBuffer buffer; + VkDescriptorSet descriptors; +} matrixframe_t; + +typedef struct matrixframeset_s + DARRAY_TYPE (matrixframe_t) matrixframeset_t; + +typedef struct matrixctx_s { + qfv_matrix_buffer_t matrices; + vec4f_t sky_rotation[2]; + vec4f_t sky_velocity; + vec4f_t sky_fix; + double sky_time; + int dirty; + + matrixframeset_t frames; + + struct qfv_resource_s *resource; + struct qfv_stagebuf_s *stage; +} matrixctx_t; + +struct vulkan_ctx_s; + +void Vulkan_CalcViewMatrix (struct vulkan_ctx_s *ctx); +void Vulkan_SetViewMatrices (struct vulkan_ctx_s *ctx, mat4f_t views[], + int count); +void Vulkan_SetSkyMatrix (struct vulkan_ctx_s *ctx, mat4f_t sky); +void Vulkan_SetSkyMatrix (struct vulkan_ctx_s *ctx, mat4f_t sky); + +void Vulkan_Matrix_Init (struct vulkan_ctx_s *ctx); +void Vulkan_Matrix_Setup (struct vulkan_ctx_s *ctx); +void Vulkan_Matrix_Shutdown (struct vulkan_ctx_s *ctx); +VkDescriptorSet Vulkan_Matrix_Descriptors (struct vulkan_ctx_s *ctx, int frame) + __attribute__((pure)); + +#endif//__QF_Vulkan_qf_matrices_h diff --git a/include/QF/Vulkan/qf_model.h b/include/QF/Vulkan/qf_model.h new file mode 100644 index 000000000..3a42e131d --- /dev/null +++ b/include/QF/Vulkan/qf_model.h @@ -0,0 +1,44 @@ +/* + qf_model.h + + Vulkan specific model stuff + + Copyright (C) 2012 Bill Currie + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2012/1/7 + Date: 2021/1/19 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __QF_Vulkan_qf_model_h +#define __QF_Vulkan_qf_model_h + +#include "QF/darray.h" +#include "QF/model.h" +#include "QF/Vulkan/qf_vid.h" + +typedef struct modelctx_s { + struct vulkan_ctx_s *ctx; + VkDeviceMemory texture_memory; +} modelctx_t; + +#endif//__QF_Vulkan_qf_model_h diff --git a/include/QF/Vulkan/qf_output.h b/include/QF/Vulkan/qf_output.h new file mode 100644 index 000000000..7ddfaddbb --- /dev/null +++ b/include/QF/Vulkan/qf_output.h @@ -0,0 +1,58 @@ +/* + qf_output.h + + Vulkan output pass + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/11/21 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __QF_Vulkan_qf_output_h +#define __QF_Vulkan_qf_output_h + +#include "QF/darray.h" +#include "QF/Vulkan/qf_vid.h" +#include "QF/Vulkan/command.h" + +typedef struct outputframe_s { + VkImageView input; + VkDescriptorSet set; +} outputframe_t; + +typedef struct outputframeset_s + DARRAY_TYPE (outputframe_t) outputframeset_t; + +typedef struct outputctx_s { + outputframeset_t frames; + VkSampler sampler; + VkImageView input; + VkFramebuffer *framebuffers; // one per swapchain image +} outputctx_t; + +struct vulkan_ctx_s; + +void Vulkan_Output_Init (struct vulkan_ctx_s *ctx); +void Vulkan_Output_Setup (struct vulkan_ctx_s *ctx); +void Vulkan_Output_Shutdown (struct vulkan_ctx_s *ctx); + +#endif//__QF_Vulkan_qf_output_h diff --git a/include/QF/Vulkan/qf_palette.h b/include/QF/Vulkan/qf_palette.h new file mode 100644 index 000000000..c1c1a4b33 --- /dev/null +++ b/include/QF/Vulkan/qf_palette.h @@ -0,0 +1,51 @@ +/* + qf_palette.h + + Vulkan matrix "pass" + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/12/8 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __QF_Vulkan_qf_palette_h +#define __QF_Vulkan_qf_palette_h + +#include "QF/darray.h" +#include "QF/Vulkan/qf_vid.h" +#include "QF/Vulkan/command.h" +#include "QF/Vulkan/image.h" + +typedef struct palettectx_s { + struct qfv_tex_s *palette; + VkSampler sampler; + VkDescriptorSet descriptor; +} palettectx_t; + +struct vulkan_ctx_s; + +void Vulkan_Palette_Update (struct vulkan_ctx_s *ctx, const byte *palette); +void Vulkan_Palette_Init (struct vulkan_ctx_s *ctx, const byte *palette); +void Vulkan_Palette_Shutdown (struct vulkan_ctx_s *ctx); +VkDescriptorSet Vulkan_Palette_Descriptor (struct vulkan_ctx_s *ctx) __attribute__((pure)); + +#endif//__QF_Vulkan_qf_palette_h diff --git a/include/QF/Vulkan/qf_particles.h b/include/QF/Vulkan/qf_particles.h new file mode 100644 index 000000000..72d07eee5 --- /dev/null +++ b/include/QF/Vulkan/qf_particles.h @@ -0,0 +1,64 @@ +#ifndef __QF_Vulkan_qf_particles_h +#define __QF_Vulkan_qf_particles_h + +#include "QF/darray.h" +#include "QF/image.h" +#include "QF/render.h" + +#include "QF/Vulkan/command.h" + +typedef struct qfv_particle_s { + vec4f_t pos; + vec4f_t vel; + vec4f_t color; + float texture; + float ramp; + float scale; + float live; +} qfv_particle_t; + +typedef struct qfv_parameters_s { + vec4f_t drag; + vec4f_t ramp; +} qfv_parameters_t; + +// Doubles as VkDrawIndirectCommand +typedef struct qfv_particle_system_s { + uint32_t vertexCount; // always 1 + uint32_t particleCount; + uint32_t firstVertex; // always 0 + uint32_t firstInstance; // always 0 +} qfv_particle_system_t; + +typedef struct particleframe_s { + VkEvent physicsEvent; + VkEvent updateEvent; + VkBuffer states; + VkBuffer params; + VkBuffer system; + + VkDescriptorSet curDescriptors; + VkDescriptorSet inDescriptors; + VkDescriptorSet newDescriptors; +} particleframe_t; + +typedef struct particleframeset_s + DARRAY_TYPE (particleframe_t) particleframeset_t; + +typedef struct particlectx_s { + particleframeset_t frames; + + struct qfv_resource_s *resources; + struct qfv_stagebuf_s *stage; + + psystem_t *psystem; +} particlectx_t; + +struct vulkan_ctx_s; + +struct psystem_s *Vulkan_ParticleSystem (struct vulkan_ctx_s *ctx); +void Vulkan_Particles_Init (struct vulkan_ctx_s *ctx); +void Vulkan_Particles_Setup (struct vulkan_ctx_s *ctx); +void Vulkan_Particles_Shutdown (struct vulkan_ctx_s *ctx); + +#endif//__QF_Vulkan_qf_particles_h diff --git a/include/QF/Vulkan/qf_scene.h b/include/QF/Vulkan/qf_scene.h new file mode 100644 index 000000000..cd35abd1e --- /dev/null +++ b/include/QF/Vulkan/qf_scene.h @@ -0,0 +1,80 @@ +/* + qf_scene.h + + Vulkan specific scene stuff + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/5/24 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __QF_Vulkan_qf_scene_h +#define __QF_Vulkan_qf_scene_h + +#ifndef VK_NO_PROTOTYPES +#define VK_NO_PROTOTYPES +#endif +#include + +#include "QF/darray.h" +#include "QF/simd/types.h" + +typedef struct entdata_s { + // transpose of entity transform matrix without row 4 + vec4f_t xform[3]; + vec4f_t color; +} entdata_t; + +typedef struct entdataset_s + DARRAY_TYPE (entdata_t) entdataset_t; + +typedef struct scnframe_s { + // used to check if the entity has been pooled this frame (cleared + // every frame) + struct set_s *pooled_entities; + // data for entities pooled this frame (cleared every frame). transfered + // to gpu + entdataset_t entity_pool; + VkDescriptorSet descriptors; +} scnframe_t; + +typedef struct scnframeset_s + DARRAY_TYPE (scnframe_t) scnframeset_t; + +typedef struct scenectx_s { + struct qfv_resource_s *entities; + scnframeset_t frames; + int max_entities; +} scenectx_t; + +struct vulkan_ctx_s; +struct entity_s; + +void Vulkan_Scene_Init (struct vulkan_ctx_s *ctx); +void Vulkan_Scene_Setup (struct vulkan_ctx_s *ctx); +void Vulkan_Scene_Shutdown (struct vulkan_ctx_s *ctx); +int Vulkan_Scene_MaxEntities (struct vulkan_ctx_s *ctx) __attribute__((pure)); +VkDescriptorSet Vulkan_Scene_Descriptors (struct vulkan_ctx_s *ctx) __attribute__((pure)); +int Vulkan_Scene_AddEntity (struct vulkan_ctx_s *ctx, struct entity_s entity); +void Vulkan_Scene_Flush (struct vulkan_ctx_s *ctx); + +#endif//__QF_Vulkan_qf_scene_h diff --git a/include/QF/Vulkan/qf_sprite.h b/include/QF/Vulkan/qf_sprite.h new file mode 100644 index 000000000..57520dbd6 --- /dev/null +++ b/include/QF/Vulkan/qf_sprite.h @@ -0,0 +1,82 @@ +/* + qf_sprite.h + + Vulkan specific sprite model stuff + + Copyright (C) 2012 Bill Currie + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2012/1/7 + Date: 2021/1/18 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __QF_Vulkan_qf_sprite_h +#define __QF_Vulkan_qf_sprite_h + +#include "QF/darray.h" +#include "QF/model.h" +#include "QF/modelgen.h" +#include "QF/Vulkan/qf_vid.h" +#include "QF/Vulkan/command.h" + +typedef struct spritevrt_s { + float x, y, u, v; +} spritevrt_t; + +typedef struct qfv_sprite_s { + VkDeviceMemory memory; + VkBuffer verts; + VkImage image; + VkImageView view; + VkDescriptorSet descriptors; +} qfv_sprite_t; + +typedef enum { + QFV_spriteDepth, + QFV_spriteGBuffer, + QFV_spriteTranslucent, + + QFV_spriteNumPasses +} QFV_SpriteSubpass; + +typedef struct spritectx_s { + struct qfv_dsmanager_s *dsmanager; + unsigned maxImages; + VkSampler sampler; +} spritectx_t; + +struct vulkan_ctx_s; +struct entity_s; +struct mod_sprite_ctx_s; + +void Vulkan_Sprite_FreeDescriptors (struct vulkan_ctx_s *ctx, + qfv_sprite_t *sprite); +void Vulkan_Sprite_DescriptorSet (struct vulkan_ctx_s *ctx, + qfv_sprite_t *sprite); +void Vulkan_Mod_SpriteLoadFrames (struct mod_sprite_ctx_s *sprite_ctx, + struct vulkan_ctx_s *ctx); + +void Vulkan_Sprite_Init (struct vulkan_ctx_s *ctx); +void Vulkan_Sprite_Setup (struct vulkan_ctx_s *ctx); +void Vulkan_Sprite_Shutdown (struct vulkan_ctx_s *ctx); + +#endif//__QF_Vulkan_qf_sprite_h diff --git a/include/QF/Vulkan/qf_texture.h b/include/QF/Vulkan/qf_texture.h new file mode 100644 index 000000000..835ee624c --- /dev/null +++ b/include/QF/Vulkan/qf_texture.h @@ -0,0 +1,42 @@ +#ifndef __QF_Vulkan_qf_texture_h +#define __QF_Vulkan_qf_texture_h + +#include "QF/image.h" +#include "QF/Vulkan/qf_vid.h" + +typedef struct qfv_tex_s { + VkDeviceMemory memory; + VkImage image; + VkImageView view; +} qfv_tex_t; + +typedef struct texturectx_s { + struct qfv_dsmanager_s *dsmanager; +} texturectx_t; + +void Vulkan_ExpandPalette (byte *dst, const byte *src, const byte *palette, + int alpha, int count); +qfv_tex_t *Vulkan_LoadTex (struct vulkan_ctx_s *ctx, tex_t *tex, int mip, + const char *name); +qfv_tex_t *Vulkan_LoadTexArray (struct vulkan_ctx_s *ctx, tex_t *tex, + int layers, int mip, const char *name); +qfv_tex_t *Vulkan_LoadEnvMap (struct vulkan_ctx_s *ctx, tex_t *tex, + const char *name); +qfv_tex_t *Vulkan_LoadEnvSides (struct vulkan_ctx_s *ctx, tex_t **tex, + const char *name); +VkImageView Vulkan_TexImageView (qfv_tex_t *tex) __attribute__((pure)); +void Vulkan_UpdateTex (struct vulkan_ctx_s *ctx, qfv_tex_t *tex, tex_t *src, + int x, int y, int layer, int mip); +void Vulkan_UnloadTex (struct vulkan_ctx_s *ctx, qfv_tex_t *tex); +void Vulkan_Texture_Init (struct vulkan_ctx_s *ctx); +void Vulkan_Texture_Setup (struct vulkan_ctx_s *ctx); +void Vulkan_Texture_Shutdown (struct vulkan_ctx_s *ctx); +VkDescriptorSet Vulkan_CreateCombinedImageSampler (struct vulkan_ctx_s *ctx, + VkImageView view, + VkSampler sampler); +VkDescriptorSet Vulkan_CreateTextureDescriptor (struct vulkan_ctx_s *ctx, + qfv_tex_t *tex, + VkSampler sampler); +void Vulkan_FreeTexture (struct vulkan_ctx_s *ctx, VkDescriptorSet texture); + +#endif//__QF_Vulkan_qf_texture_h diff --git a/include/QF/Vulkan/qf_translucent.h b/include/QF/Vulkan/qf_translucent.h new file mode 100644 index 000000000..19003576c --- /dev/null +++ b/include/QF/Vulkan/qf_translucent.h @@ -0,0 +1,49 @@ +#ifndef __QF_Vulkan_qf_translucent_h +#define __QF_Vulkan_qf_translucent_h + +#include "QF/darray.h" + +#include "QF/simd/types.h" + +#include "QF/Vulkan/command.h" + +typedef struct qfv_transfrag_s { + vec4f_t color; + float depth; + int32_t next; +} qfv_transfrag_t; + +typedef struct qfv_transtate_s { + int32_t numFragments; + int32_t maxFragments; +} qfv_transtate_t; + +typedef struct translucentframe_s { + VkDescriptorSet flat; + VkDescriptorSet cube; + VkImage heads; + VkImage cube_heads; + VkBuffer state; +} translucentframe_t; + +typedef struct translucentframeset_s + DARRAY_TYPE (translucentframe_t) translucentframeset_t; + +typedef struct translucentctx_s { + translucentframeset_t frames; + struct qfv_resource_s *resources; + + int maxFragments; +} translucentctx_t; + +struct vulkan_ctx_s; + +void Vulkan_Translucent_Init (struct vulkan_ctx_s *ctx); +void Vulkan_Translucent_Setup (struct vulkan_ctx_s *ctx); +void Vulkan_Translucent_Shutdown (struct vulkan_ctx_s *ctx); +VkDescriptorSet Vulkan_Translucent_Descriptors (struct vulkan_ctx_s *ctx, + int frame)__attribute__((pure)); +void Vulkan_Translucent_CreateBuffers (struct vulkan_ctx_s *ctx, + VkExtent2D extent); + +#endif//__QF_Vulkan_qf_translucent_h diff --git a/include/QF/Vulkan/qf_vid.h b/include/QF/Vulkan/qf_vid.h new file mode 100644 index 000000000..8d6deb9bb --- /dev/null +++ b/include/QF/Vulkan/qf_vid.h @@ -0,0 +1,94 @@ +/* + Vulkan/qf_vid.h + + vulkan vid stuff from the renderer. + + Copyright (C) 2019 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_Vulkan_vid_h +#define __QF_Vulkan_vid_h + +#include "QF/Vulkan/cvars.h" + +#ifndef VK_NO_PROTOTYPES +#define VK_NO_PROTOTYPES +#endif +#include + +/** \defgroup vulkan Vulkan Renderer +*/ + +enum { + QFV_rp_particles, + QFV_rp_shadowmap, + QFV_rp_preoutput, + QFV_rp_translucent, + QFV_rp_main, + QFV_rp_output, +}; + +//FIXME location +typedef enum { + QFV_passDepth, // geometry + QFV_passTranslucentFrag, // geometry + QFV_passGBuffer, // geometry + QFV_passLighting, // single triangle + QFV_passCompose, // single triangle + + QFV_NumPasses +} QFV_Subpass; + +enum { + QFV_attachDepth, + QFV_attachColor, + QFV_attachEmission, + QFV_attachNormal, + QFV_attachPosition, + QFV_attachOpaque, + QFV_attachSwapchain, +}; + +struct vulkan_ctx_s; +void Vulkan_CreateSwapchain (struct vulkan_ctx_s *ctx); +void Vulkan_CreateDevice (struct vulkan_ctx_s *ctx); +void Vulkan_Init_Common (struct vulkan_ctx_s *ctx); +void Vulkan_Shutdown_Common (struct vulkan_ctx_s *ctx); +void Vulkan_CreateStagingBuffers (struct vulkan_ctx_s *ctx); + +struct qfv_output_s; +void Vulkan_ConfigOutput (struct vulkan_ctx_s *ctx, + struct qfv_output_s *output); + +VkSampler Vulkan_CreateSampler (struct vulkan_ctx_s *ctx, const char *name); + +struct entity_s; +void Vulkan_BeginEntityLabel (struct vulkan_ctx_s *ctx, VkCommandBuffer cmd, + struct entity_s ent); + +struct plitem_s *Vulkan_GetConfig (struct vulkan_ctx_s *ctx, const char *name); + +extern int vulkan_frame_width; +extern int vulkan_frame_height; +extern int vulkan_oit_fragments; + +#endif // __QF_Vulkan_vid_h diff --git a/include/QF/Vulkan/render.h b/include/QF/Vulkan/render.h new file mode 100644 index 000000000..adc7b4fdb --- /dev/null +++ b/include/QF/Vulkan/render.h @@ -0,0 +1,455 @@ +#ifndef __QF_Vulkan_render_h +#define __QF_Vulkan_render_h + +#ifndef VK_NO_PROTOTYPES +#define VK_NO_PROTOTYPES +#endif +#include + +#include "QF/cexpr.h" +#include "QF/simd/types.h" + +#ifndef __QFCC__ +#include "QF/darray.h" +#include "QF/Vulkan/command.h" +#endif + +typedef struct qfv_output_s { + VkExtent2D extent; + VkImage image; // only if owned + VkImageView view; + VkFormat format; + uint32_t frames; + VkImageView *view_list; // per frame + VkImageLayout finalLayout; +} qfv_output_t; + +typedef struct qfv_reference_s { + const char *name; + int line; +} qfv_reference_t; + +typedef struct qfv_descriptorsetlayoutinfo_s { + const char *name; + VkDescriptorSetLayoutCreateFlags flags; + uint32_t num_bindings; + VkDescriptorSetLayoutBinding *bindings; + VkDescriptorSetLayout setLayout; +} qfv_descriptorsetlayoutinfo_t; + +typedef enum qfv_type_t { + qfv_float, + qfv_int, + qfv_uint, + qfv_vec3, + qfv_vec4, + qfv_mat4, +} qfv_type_t; + +typedef struct qfv_pushconstantinfo_s { + const char *name; + int line; + qfv_type_t type; + uint32_t offset; + uint32_t size; +} qfv_pushconstantinfo_t; + +typedef struct qfv_pushconstantrangeinfo_s { + VkShaderStageFlags stageFlags; + uint32_t num_pushconstants; + qfv_pushconstantinfo_t *pushconstants; +} qfv_pushconstantrangeinfo_t; + +typedef struct qfv_layoutinfo_s { + const char *name; + uint32_t num_sets; + qfv_reference_t *sets; + uint32_t num_pushconstantranges; + qfv_pushconstantrangeinfo_t *pushconstantranges; + VkPipelineLayout layout; +} qfv_layoutinfo_t; + +typedef struct qfv_imageinfo_s { + const char *name; + VkImageCreateFlags flags; + VkImageType imageType; + VkFormat format; + VkExtent3D extent; + uint32_t mipLevels; + uint32_t arrayLayers; + VkSampleCountFlagBits samples; + VkImageTiling tiling; + VkImageUsageFlags usage; + VkImageLayout initialLayout; +} qfv_imageinfo_t; + +typedef struct qfv_imageviewinfo_s { + const char *name; + VkImageViewCreateFlags flags; + qfv_reference_t image; + VkImageViewType viewType; + VkFormat format; + VkComponentMapping components; + VkImageSubresourceRange subresourceRange; +} qfv_imageviewinfo_t; + +typedef struct qfv_bufferinfo_s { + const char *name; + VkBufferCreateFlags flags; + VkDeviceSize size; + VkBufferUsageFlags usage; + VkSharingMode sharingMode; +} qfv_bufferinfo_t; + +typedef struct qfv_bufferviewinfo_s { + const char *name; + VkBufferViewCreateFlags flags; + qfv_reference_t buffer; + VkFormat format; + VkDeviceSize offset; + VkDeviceSize range; +} qfv_bufferviewinfo_t; + +typedef struct qfv_dependencymask_s { + VkPipelineStageFlags stage; + VkAccessFlags access; +} qfv_dependencymask_t; + +typedef struct qfv_dependencyinfo_s { + const char *name; + qfv_dependencymask_t src; + qfv_dependencymask_t dst; + VkDependencyFlags flags; +} qfv_dependencyinfo_t; + +typedef struct qfv_attachmentinfo_s { + const char *name; + int line; + VkAttachmentDescriptionFlags flags; + VkFormat format; + VkSampleCountFlagBits samples; + VkAttachmentLoadOp loadOp; + VkAttachmentStoreOp storeOp; + VkAttachmentLoadOp stencilLoadOp; + VkAttachmentStoreOp stencilStoreOp; + VkImageLayout initialLayout; + VkImageLayout finalLayout; + VkClearValue clearValue; + qfv_reference_t view; + const char *external; +} qfv_attachmentinfo_t; + +typedef struct qfv_taskinfo_s { + exprfunc_t *func; + const exprval_t **params; + void *param_data; +} qfv_taskinfo_t; + +typedef struct qfv_attachmentrefinfo_s { + const char *name; + int line; + VkImageLayout layout; + VkPipelineColorBlendAttachmentState blend; +} qfv_attachmentrefinfo_t; + +typedef struct qfv_attachmentsetinfo_s { + uint32_t num_input; + qfv_attachmentrefinfo_t *input; + uint32_t num_color; + qfv_attachmentrefinfo_t *color; + qfv_attachmentrefinfo_t *resolve; + qfv_attachmentrefinfo_t *depth; + uint32_t num_preserve; + qfv_reference_t *preserve; +} qfv_attachmentsetinfo_t; + +typedef struct qfv_pipelineinfo_s { + vec4f_t color; + const char *name; + bool disabled; + uint32_t num_tasks; + qfv_taskinfo_t *tasks; + + VkPipelineCreateFlags flags; + VkPipelineShaderStageCreateInfo *compute_stage; + uint32_t dispatch[3]; + + uint32_t num_graph_stages; + const VkPipelineShaderStageCreateInfo *graph_stages; + const VkPipelineVertexInputStateCreateInfo *vertexInput; + const VkPipelineInputAssemblyStateCreateInfo *inputAssembly; + const VkPipelineTessellationStateCreateInfo *tessellation; + const VkPipelineViewportStateCreateInfo *viewport; + const VkPipelineRasterizationStateCreateInfo *rasterization; + const VkPipelineMultisampleStateCreateInfo *multisample; + const VkPipelineDepthStencilStateCreateInfo *depthStencil; + const VkPipelineColorBlendStateCreateInfo *colorBlend; + const VkPipelineDynamicStateCreateInfo *dynamic; + qfv_reference_t layout; +} qfv_pipelineinfo_t; + +typedef struct qfv_subpassinfo_s { + vec4f_t color; + const char *name; + uint32_t num_dependencies; + qfv_dependencyinfo_t *dependencies; + qfv_attachmentsetinfo_t *attachments; + uint32_t num_pipelines; + qfv_pipelineinfo_t *pipelines; + qfv_pipelineinfo_t *base_pipeline; +} qfv_subpassinfo_t; + +typedef struct qfv_framebufferinfo_s { + qfv_attachmentinfo_t *attachments; + uint32_t num_attachments; + uint32_t width; + uint32_t height; + uint32_t layers; +} qfv_framebufferinfo_t; + +typedef struct qfv_renderpassinfo_s { + vec4f_t color; + const char *name; + void *pNext; + qfv_framebufferinfo_t framebuffer; + uint32_t num_subpasses; + qfv_subpassinfo_t *subpasses; + qfv_reference_t output; +} qfv_renderpassinfo_t; + +typedef struct qfv_computeinfo_s { + vec4f_t color; + const char *name; + + uint32_t num_pipelines; + qfv_pipelineinfo_t *pipelines; +} qfv_computeinfo_t; + +typedef struct qfv_renderinfo_s { + vec4f_t color; + const char *name; + + uint32_t num_renderpasses; + qfv_renderpassinfo_t *renderpasses; +} qfv_renderinfo_t; + +typedef struct qfv_processinfo_s { + vec4f_t color; + const char *name; + + uint32_t num_tasks; + qfv_taskinfo_t *tasks; +} qfv_processinfo_t; + +typedef struct qfv_stepinfo_s { + vec4f_t color; + const char *name; + + uint32_t num_dependencies; + qfv_reference_t *dependencies; + + qfv_renderinfo_t *render; + qfv_computeinfo_t *compute; + qfv_processinfo_t *process; +} qfv_stepinfo_t; + +typedef struct qfv_jobinfo_s { + struct memsuper_s *memsuper; + + struct plitem_s *plitem; + uint32_t num_steps; + qfv_stepinfo_t *steps; + + uint32_t num_images; + uint32_t num_imageviews; + qfv_imageinfo_t *images; + qfv_imageviewinfo_t *imageviews; + uint32_t num_buffers; + uint32_t num_bufferviews; + qfv_imageinfo_t *buffers; + qfv_imageviewinfo_t *bufferviews; + + uint32_t num_dslayouts; + qfv_descriptorsetlayoutinfo_t *dslayouts; +} qfv_jobinfo_t; + +typedef struct qfv_samplercreateinfo_s { + const char *name; + VkSamplerCreateFlags flags; + VkFilter magFilter; + VkFilter minFilter; + VkSamplerMipmapMode mipmapMode; + VkSamplerAddressMode addressModeU; + VkSamplerAddressMode addressModeV; + VkSamplerAddressMode addressModeW; + float mipLodBias; + VkBool32 anisotropyEnable; + float maxAnisotropy; + VkBool32 compareEnable; + VkCompareOp compareOp; + float minLod; + float maxLod; + VkBorderColor borderColor; + VkBool32 unnormalizedCoordinates; + VkSampler sampler; +} qfv_samplercreateinfo_t; + +typedef struct qfv_samplerinfo_s { + struct memsuper_s *memsuper; + + struct plitem_s *plitem; + qfv_samplercreateinfo_t *samplers; + uint32_t num_samplers; +} qfv_samplerinfo_t; + +#ifndef __QFCC__ +typedef struct qfv_label_s { + vec4f_t color; + const char *name; +} qfv_label_t; + +typedef struct qfv_pipeline_s { + qfv_label_t label; + bool disabled; + VkPipelineBindPoint bindPoint; + vec4u_t dispatch; + VkPipeline pipeline; + VkPipelineLayout layout; + + VkViewport viewport; + VkRect2D scissor; + uint32_t num_indices; + uint32_t *ds_indices; + + uint32_t task_count; + qfv_taskinfo_t *tasks; +} qfv_pipeline_t; + +typedef struct qfv_subpass_s { + qfv_label_t label; + VkCommandBufferInheritanceInfo inherit; + VkCommandBufferBeginInfo beginInfo; + uint32_t pipeline_count; + qfv_pipeline_t *pipelines; +} qfv_subpass_t; + +typedef struct qfv_framebuffer_s { + uint32_t width; + uint32_t height; + uint32_t layers; + uint32_t num_attachments; + VkImageView *views; +} qfv_framebuffer_t; + +typedef struct qfv_renderpass_s { + struct vulkan_ctx_s *vulkan_ctx; + qfv_label_t label; // for debugging + + VkRenderPassBeginInfo beginInfo; + VkSubpassContents subpassContents; + + uint32_t subpass_count; + qfv_subpass_t *subpasses; + + qfv_framebuffer_t framebuffer; + qfv_framebufferinfo_t *framebufferinfo; + VkImageView output; + qfv_reference_t outputref; +} qfv_renderpass_t; + +typedef struct qfv_render_s { + qfv_label_t label; + qfv_renderpass_t *active; + qfv_renderpass_t *renderpasses; + uint32_t num_renderpasses; + qfv_output_t output; +} qfv_render_t; + +typedef struct qfv_compute_s { + qfv_label_t label; + qfv_pipeline_t *pipelines; + uint32_t pipeline_count; +} qfv_compute_t; + +typedef struct qfv_process_s { + qfv_label_t label; + qfv_taskinfo_t *tasks; + uint32_t task_count; +} qfv_process_t; + +typedef struct qfv_step_s { + qfv_label_t label; + qfv_render_t *render; + qfv_compute_t *compute; + qfv_process_t *process; +} qfv_step_t; + +typedef struct qfv_job_s { + qfv_label_t label; + struct qfv_resource_s *resources; + struct qfv_resobj_s *images; + struct qfv_resobj_s *image_views; + + uint32_t num_renderpasses; + uint32_t num_pipelines; + uint32_t num_layouts; + uint32_t num_steps; + VkRenderPass *renderpasses; + VkPipeline *pipelines; + VkPipelineLayout *layouts; + qfv_step_t *steps; + qfv_cmdbufferset_t commands; + uint32_t num_dsmanagers; + struct qfv_dsmanager_s **dsmanager; +} qfv_job_t; + +typedef struct qfv_renderframe_s { + VkFence fence; + VkSemaphore imageAvailableSemaphore; + VkSemaphore renderDoneSemaphore; + qfv_cmdpoolmgr_t cmdpool; +} qfv_renderframe_t; + +typedef struct qfv_renderframeset_s + DARRAY_TYPE (qfv_renderframe_t) qfv_renderframeset_t; + +typedef struct qfv_renderctx_s { + struct hashctx_s *hashctx; + exprtab_t task_functions; + qfv_jobinfo_t *jobinfo; + qfv_samplerinfo_t *samplerinfo; + qfv_job_t *job; + qfv_renderframeset_t frames; +} qfv_renderctx_t; + +typedef struct qfv_taskctx_s { + struct vulkan_ctx_s *ctx; + qfv_pipeline_t *pipeline; + qfv_renderpass_t *renderpass; + VkCommandBuffer cmd; +} qfv_taskctx_t; + +VkCommandBuffer QFV_GetCmdBuffer (struct vulkan_ctx_s *ctx, bool secondary); +void QFV_AppendCmdBuffer (struct vulkan_ctx_s *ctx, VkCommandBuffer cmd); + +void QFV_RunRenderJob (struct vulkan_ctx_s *ctx); +void QFV_LoadRenderInfo (struct vulkan_ctx_s *ctx, const char *name); +void QFV_LoadSamplerInfo (struct vulkan_ctx_s *ctx, const char *name); +void QFV_BuildRender (struct vulkan_ctx_s *ctx); +void QFV_Render_Init (struct vulkan_ctx_s *ctx); +void QFV_Render_Shutdown (struct vulkan_ctx_s *ctx); +void QFV_Render_AddTasks (struct vulkan_ctx_s *ctx, exprsym_t *task_sys); + +void QFV_CreateFramebuffer (struct vulkan_ctx_s *ctx, qfv_renderpass_t *rp); + +struct qfv_dsmanager_s * +QFV_Render_DSManager (struct vulkan_ctx_s *ctx, + const char *setName) __attribute__((pure)); +VkSampler QFV_Render_Sampler (struct vulkan_ctx_s *ctx, const char *name); + +qfv_step_t *QFV_GetStep (const exprval_t *param, qfv_job_t *job); +qfv_step_t *QFV_FindStep (const char *step, qfv_job_t *job) __attribute__((pure)); + +#endif//__QFCC__ + +#endif//__QF_Vulkan_render_h diff --git a/include/QF/Vulkan/resource.h b/include/QF/Vulkan/resource.h new file mode 100644 index 000000000..74a5deb2e --- /dev/null +++ b/include/QF/Vulkan/resource.h @@ -0,0 +1,82 @@ +#ifndef __QF_Vulkan_resource_h +#define __QF_Vulkan_resource_h + +#ifndef VK_NO_PROTOTYPES +#define VK_NO_PROTOTYPES +#endif +#include + +typedef enum { + qfv_res_buffer = 1, + qfv_res_buffer_view, + qfv_res_image, + qfv_res_image_view, +} qfv_res_type; + +typedef struct qfv_resobj_s { + const char *name; + qfv_res_type type; + union { + struct { + VkDeviceSize size; + VkBufferUsageFlags usage; + VkBuffer buffer; + VkDeviceSize offset; + } buffer; + struct { + unsigned buffer; + VkFormat format; + VkDeviceSize offset; + VkDeviceSize size; + VkBufferView view; + } buffer_view; + struct { + VkImageCreateFlags flags; + VkImageType type; + VkFormat format; + VkExtent3D extent; + uint32_t num_mipmaps; + uint32_t num_layers; + VkSampleCountFlags samples; + VkImageTiling tiling; + VkImageUsageFlags usage; + VkSharingMode sharing; + uint32_t num_queue_inds; + const uint32_t *queue_inds; + VkImageLayout initialLayout; + VkImage image; + VkDeviceSize offset; + } image; + struct { + unsigned image; + VkImage external_image; + VkImageViewCreateFlags flags; + VkImageViewType type; + VkFormat format; + VkComponentMapping components; + VkImageSubresourceRange subresourceRange; + VkImageView view; + } image_view; + }; +} qfv_resobj_t; + +typedef struct qfv_resource_s { + const char *name; + struct va_ctx_s *va_ctx; + VkMemoryPropertyFlags memory_properties; + unsigned num_objects; + qfv_resobj_t *objects; + VkDeviceMemory memory; + VkDeviceSize size; +} qfv_resource_t; + +struct qfv_device_s; + +int QFV_CreateResource (struct qfv_device_s *device, qfv_resource_t *resource); +void QFV_DestroyResource (struct qfv_device_s *device, + qfv_resource_t *resource); +struct tex_s; +void QFV_ResourceInitTexImage (qfv_resobj_t *image, const char *name, + int mips, const struct tex_s *tex); + +#endif//__QF_Vulkan_resource_h diff --git a/include/QF/Vulkan/scrap.h b/include/QF/Vulkan/scrap.h new file mode 100644 index 000000000..760d9cb12 --- /dev/null +++ b/include/QF/Vulkan/scrap.h @@ -0,0 +1,25 @@ +#ifndef __QF_Vulkan_scrap_h +#define __QF_Vulkan_scrap_h + +#include "QF/image.h" + +typedef struct scrap_s scrap_t; + +struct qfv_stagebuf_s; +struct qfv_device_s; + +scrap_t *QFV_CreateScrap (struct qfv_device_s *device, const char *name, + int size, QFFormat format, + struct qfv_stagebuf_s *stage); +size_t QFV_ScrapSize (scrap_t *scrap) __attribute__((pure)); +void QFV_ScrapClear (scrap_t *scrap); +void QFV_DestroyScrap (scrap_t *scrap); +VkImageView QFV_ScrapImageView (scrap_t *scrap) __attribute__((pure)); +struct subpic_s *QFV_ScrapSubpic (scrap_t *scrap, int width, int height); +void QFV_SubpicDelete (struct subpic_s *subpic); + +void *QFV_SubpicBatch (struct subpic_s *subpic, struct qfv_stagebuf_s *stage); + +void QFV_ScrapFlush (scrap_t *scrap); + +#endif//__QF_Vulkan_scrap_h diff --git a/include/QF/Vulkan/shader.h b/include/QF/Vulkan/shader.h new file mode 100644 index 000000000..f44189978 --- /dev/null +++ b/include/QF/Vulkan/shader.h @@ -0,0 +1,14 @@ +#ifndef __QF_Vulkan_shader_h +#define __QF_Vulkan_shader_h + +struct qfv_device_s; +struct vulkan_ctx_s; +struct plitem_s; +struct parsectx_s; + +VkShaderModule QFV_CreateShaderModule (struct qfv_device_s *device, + const char *path); +void QFV_DestroyShaderModule (struct qfv_device_s *device, + VkShaderModule module); + +#endif//__QF_Vulkan_shader_h diff --git a/include/QF/Vulkan/staging.h b/include/QF/Vulkan/staging.h new file mode 100644 index 000000000..9abe5a48c --- /dev/null +++ b/include/QF/Vulkan/staging.h @@ -0,0 +1,46 @@ +#ifndef __QF_Vulkan_staging_h +#define __QF_Vulkan_staging_h + +#include "QF/ringbuffer.h" + +typedef struct qfv_packet_s { + struct qfv_stagebuf_s *stage; ///< staging buffer that owns this packet + VkCommandBuffer cmd; + VkFence fence; + size_t offset; + size_t length; +} qfv_packet_t; + +typedef struct qfv_stagebuf_s { + struct qfv_device_s *device; + VkCommandPool cmdPool; + VkBuffer buffer; + VkDeviceMemory memory; + RING_BUFFER(qfv_packet_t, 4) packets; ///< packets for controlling access + size_t atom_mask; ///< for flush size rounding + size_t size; ///< actual size of the buffer + size_t end; ///< effective end of the buffer due to early wrap + size_t space_start;///< beginning of available space + size_t space_end; ///< end of available space + void *data; +} qfv_stagebuf_t; + + +qfv_stagebuf_t *QFV_CreateStagingBuffer (struct qfv_device_s *device, + const char *name, size_t size, + VkCommandPool cmdPool); +void QFV_DestroyStagingBuffer (qfv_stagebuf_t *stage); +void QFV_FlushStagingBuffer (qfv_stagebuf_t *stage, size_t offset, size_t size); +qfv_packet_t *QFV_PacketAcquire (qfv_stagebuf_t *stage); +void *QFV_PacketExtend (qfv_packet_t *packet, size_t size); +void QFV_PacketSubmit (qfv_packet_t *packet); +struct qfv_bufferbarrier_s; +void QFV_PacketCopyBuffer (qfv_packet_t *packet, + VkBuffer dstBuffer, VkDeviceSize offset, + const struct qfv_bufferbarrier_s *dstBarrier); +struct qfv_imagebarrier_s; +void QFV_PacketCopyImage (qfv_packet_t *packet, VkImage dstImage, + int width, int height, + const struct qfv_imagebarrier_s *dstBarrier); + +#endif//__QF_Vulkan_staging_h diff --git a/include/QF/Vulkan/swapchain.h b/include/QF/Vulkan/swapchain.h new file mode 100644 index 000000000..5c988f90f --- /dev/null +++ b/include/QF/Vulkan/swapchain.h @@ -0,0 +1,25 @@ +#ifndef __QF_Vulkan_swapchain_h +#define __QF_Vulkan_swapchain_h + +typedef struct qfv_swapchain_s { + struct qfv_device_s *device; + VkSurfaceKHR surface; + VkSwapchainKHR swapchain; + VkFormat format; + VkExtent2D extent; + int32_t numImages; + VkImageUsageFlags usage; + struct qfv_imageset_s *images; + struct qfv_imageviewset_s *imageViews; +} qfv_swapchain_t; + +struct vulkan_ctx_s; +qfv_swapchain_t *QFV_CreateSwapchain (struct vulkan_ctx_s *ctx, + VkSwapchainKHR old_swapchain); +void QFV_DestroySwapchain (qfv_swapchain_t *swapchain); +struct qfv_semaphore_s; +struct qfv_fence_s; +int QFV_AcquireNextImage (qfv_swapchain_t *swapchain, VkSemaphore semaphore, + VkFence fence, uint32_t *imageIndex); + +#endif//__QF_Vulkan_swapchain_h diff --git a/include/QF/alloc.h b/include/QF/alloc.h index 59a57bdf1..a22ad78eb 100644 --- a/include/QF/alloc.h +++ b/include/QF/alloc.h @@ -34,16 +34,19 @@ #include #include +#include "QF/darray.h" + /** \defgroup alloc High-tide allocator. \ingroup utils */ -//@{ +///@{ #ifndef DEBUG_QF_MEMORY /** High-tide structure allocator for use in linked lists. Using a free-list with the name of \c NAME_freelist, return a single - element. + element. Also, the allocated blocks are recorded in a DARRAY with the name + of \c NAME_blocks. The type of the element must be a structure with a field named \c next. When the free-list is empty, memory is claimed from the system in blocks. Elements may be returned to the pool by linking them into the free-list. @@ -65,12 +68,25 @@ for (i = 0; i < (s) - 1; i++) \ n##_freelist[i].next = &n##_freelist[i + 1];\ n##_freelist[i].next = 0; \ + DARRAY_APPEND (&n##_blocks, n##_freelist); \ } \ v = n##_freelist; \ n##_freelist = n##_freelist->next; \ memset (v, 0, sizeof (*v)); \ } while (0) +#define ALLOC_STATE(t,n) \ +static t *n##_freelist; \ +static struct DARRAY_TYPE(t *) n##_blocks = DARRAY_STATIC_INIT(8) + +#define ALLOC_FREE_BLOCKS(n) \ + do { \ + for (size_t i = 0; i < n##_blocks.size; i++) { \ + free (n##_blocks.a[i]); \ + } \ + DARRAY_CLEAR (&n##_blocks); \ + } while (0) + /** Free a block allocated by #ALLOC \param n The \c NAME portion of the \c NAME_freelist free-list. @@ -93,6 +109,6 @@ #define FREE(n, p) do { free (p); } while (0) #endif -//@} +///@} #endif//__QF_alloc_h diff --git a/include/QF/bspfile.h b/include/QF/bspfile.h index b7683849f..926c3873a 100644 --- a/include/QF/bspfile.h +++ b/include/QF/bspfile.h @@ -24,252 +24,660 @@ Boston, MA 02111-1307, USA */ -#ifndef __bspfile_h_ -#define __bspfile_h_ +#ifndef __QF_bspfile_h +#define __QF_bspfile_h #include "QF/qtypes.h" #include "QF/quakeio.h" -// upper design bounds +/** \defgroup formats_bsp BSP Files -#define MAX_MAP_HULLS 4 // format limit (array) + BSP files are used for quake's maps and some pick-up items (health and + ammo boxes in particular). The maps can be quite complicated, usually with + many sub-models, while the pick-up items are usually simple boxes. -#define MAX_MAP_PLANES 32767 // format limit (s16) FIXME u16 ok? -#define MAX_MAP_NODES 65520 // because negative shorts are contents -#define MAX_MAP_CLIPNODES 65520 // but contents "max" is -15, so -#define MAX_MAP_LEAFS 65520 // -32768 to -17 are available -#define MAX_MAP_VERTS 65535 // format limit (u16) -#define MAX_MAP_FACES 65535 // format limit (u16) -#define MAX_MAP_MARKSURFACES 65535 // format limit (u16) + QuakeForge supports two formats for BSP files "BSP 29", (the original + format, with many 16-bit fields), and "BSP 2", all fields 32 bits. Both + formats are little-endian. -//============================================================================= + BSP files use a right-handed coordinate system with +Z up but clockwise + winding for face normals. -#define BSPVERSION 29 -#define BSP2VERSION "BSP2" // use memcmp with 4 bytes -#define Q2BSPVERSION 38 -#define TOOLVERSION 2 + The tools used to create a bsp file include qfbsp, qfvis and qflight (or + similar tools from other projects). +*/ +/** \defgroup bsp_limits BSP File limits + \ingroup formats_bsp + + Other than MAX_MAP_HULLS, these apply only to BSP 29 files as the fields + holding the values are all 16 bits. +*/ +///@{ + +#define MAX_MAP_HULLS 4 ///< fixed length array + +#define MAX_MAP_PLANES 32767 +/** \name nodes and leafs (sic) + negative shorts are contents but contents "max" is -15, so -32768 to -17 + are available +*/ +///@{ +#define MAX_MAP_NODES 65520 +#define MAX_MAP_CLIPNODES 65520 +#define MAX_MAP_LEAFS 65520 +///@} +#define MAX_MAP_VERTS 65535 +#define MAX_MAP_FACES 65535 +#define MAX_MAP_MARKSURFACES 65535 + +#define MAP_PVS_BYTES (MAX_MAP_LEAFS / 8) +///@} + +/** \defgroup bsp_lumps BSP File lumps + \ingroup formats_bsp + + All data in a BSP file is separated into lumps. The lump structure gives + the file relative offset of the beginning of the data lump, and the size + of the lump in bytes. +*/ +///@{ + +/** Individual data lump descriptor. +*/ typedef struct lump_s { - int32_t fileofs; - int32_t filelen; + uint32_t fileofs; ///< File relative offset in bytes + uint32_t filelen; ///< Size of lump in bytes } lump_t; -#define LUMP_ENTITIES 0 -#define LUMP_PLANES 1 -#define LUMP_TEXTURES 2 -#define LUMP_VERTEXES 3 -#define LUMP_VISIBILITY 4 -#define LUMP_NODES 5 -#define LUMP_TEXINFO 6 -#define LUMP_FACES 7 -#define LUMP_LIGHTING 8 -#define LUMP_CLIPNODES 9 -#define LUMP_LEAFS 10 -#define LUMP_MARKSURFACES 11 -#define LUMP_EDGES 12 -#define LUMP_SURFEDGES 13 -#define LUMP_MODELS 14 -#define HEADER_LUMPS 15 +#define LUMP_ENTITIES 0 ///< \ref bsp_entities +#define LUMP_PLANES 1 ///< \ref bsp_planes +#define LUMP_TEXTURES 2 ///< \ref bsp_textures +#define LUMP_VERTEXES 3 ///< \ref bsp_vertices +#define LUMP_VISIBILITY 4 ///< \ref bsp_visibility +#define LUMP_NODES 5 ///< \ref bsp_nodes +#define LUMP_TEXINFO 6 ///< \ref bsp_texinfo +#define LUMP_FACES 7 ///< \ref bsp_face +#define LUMP_LIGHTING 8 ///< \ref bsp_lighting +#define LUMP_CLIPNODES 9 ///< \ref bsp_clipnodes +#define LUMP_LEAFS 10 ///< \ref bsp_leafs +#define LUMP_MARKSURFACES 11 ///< \ref bsp_marksurfaces +#define LUMP_EDGES 12 ///< \ref bsp_edges +#define LUMP_SURFEDGES 13 ///< \ref bsp_surfedges +#define LUMP_MODELS 14 ///< \ref bsp_model +#define HEADER_LUMPS 15 ///< Number of lumps +///@} -typedef struct dmodel_s { - float mins[3], maxs[3]; - float origin[3]; - int32_t headnode[MAX_MAP_HULLS]; - int32_t visleafs; // not including the solid leaf 0 - int32_t firstface, numfaces; -} dmodel_t; +/** \defgroup bsp_header BSP File header + \ingroup formats_bsp +*/ +///@{ +/** Holdes version and lump offset information. + Always at offset 0 of the BSP file. +*/ typedef struct dheader_s { - int32_t version; - lump_t lumps[HEADER_LUMPS]; + uint32_t version; ///< little-endian or 4-char string + lump_t lumps[HEADER_LUMPS];///< Identical between BSP 2 and BSP 29 } dheader_t; +#define BSPVERSION 29 ///< little-endian uint32_t +#define BSP2VERSION "BSP2" ///< 4-char unterminated string +#define Q2BSPVERSION 38 ///< Not supported +#define TOOLVERSION 2 ///< Not used +///@} + +/** \defgroup bsp_model BSP File sub-model + \ingroup formats_bsp + + The sub-model lump is an array of sub-model descriptors. Every BSP file + has at least one sub-model, with sub-model index 0 being the main model. +*/ +///@{ +typedef struct dmodel_s { + /// \name Bounding box for the model + ///@{ + float mins[3]; ///< minimum X, Y, Z + float maxs[3]; ///< maximum X, Y, Z + ///@} + float origin[3]; ///< unclear + /// Index of the first node for each hull. The first headnode index + /// is for the draw nodes (\ref bsp_nodes), while subsequent headnodes + /// are for the clipnodes (\ref bsp_clipnodes). + /// The engine builds a clip node tree for headnods[0] from the draw + /// nodes at load time. + uint32_t headnode[MAX_MAP_HULLS]; + /// The number of visible leafs in the model. + /// \note Does *not* include leaf 0 (the infinite solid leaf in most maps) + uint32_t visleafs; + /// \name Visible faces + /// A sub-model's visible faces are all in one contiguous block. + ///@{ + uint32_t firstface; ///< Index of first visible face in model + uint32_t numfaces; ///< Number of visible faces in model + ///@} +} dmodel_t; +///@} + +/** \defgroup bsp_textures BSP File texture data + \ingroup formats_bsp + + The texture data in a BSP file forms a sub-file with its own structure. +*/ +///@{ + +/** Header for the textures lump. + + The size is variable as there is one dataofs element for each miptex + block. +*/ typedef struct dmiptexlump_s { - int32_t nummiptex; - int32_t dataofs[4]; // [nummiptex] + /// Number of miptex blocks in the textures lump. + uint32_t nummiptex; + /// Offsets are relative to the beginning of the dmiptexlump_t structure + /// (ie, the textures lump). One for each miptex block. \ref miptex_s + uint32_t dataofs[4]; } dmiptexlump_t; -#define MIPTEXNAME 16 -#define MIPLEVELS 4 +#define MIPTEXNAME 16 ///< Names have a max length of 15 chars +#define MIPLEVELS 4 ///< Number of mip levels in all miptex +/** Header for individual mip-mapped texture. + + The beginning of the name of the texture specifies some of the texture's + properties: + - sky The texture is used for the dual-layer skies. Expected + to be 256x128. Sky face get special treatment by the + renderer. + - * "Water" face. Texture-warped by the renderer. Usually + unlit (ie, always full-bright). + - + Member of an animation group. The second character of + the texture's name determines to which animation sequence + the texture belongs and the texture's position in that + sequence. The rest of the name specifies the group name. + \ref bsp_texture_animation + + The texture may be rectangular, but the size must be a multiple of 16 + pixels in both directions. +*/ typedef struct miptex_s { - char name[MIPTEXNAME]; - uint32_t width, height; - uint32_t offsets[MIPLEVELS]; // four mip maps stored + char name[MIPTEXNAME]; ///< Texture name. Nul-terminated. + uint32_t width; ///< Width of the full-size texture + uint32_t height; ///< Height of the full-size texture + /// Offsets are relative to the beginning of the individual miptex block. + uint32_t offsets[MIPLEVELS]; } miptex_t; +///@} +/** \defgroup bsp_vertices BSP File vertex lump + \ingroup formats_bsp + Array of unique vertices. + + Vertices are shared between edges and faces. +*/ +///@{ typedef struct dvertex_s { - float point[3]; + float point[3]; ///< X, Y, Z } dvertex_t; +///@} -// 0-2 are axial planes -#define PLANE_X 0 -#define PLANE_Y 1 -#define PLANE_Z 2 -// 3-5 are non-axial planes snapped to the nearest -#define PLANE_ANYX 3 -#define PLANE_ANYY 4 -#define PLANE_ANYZ 5 +/** \defgroup bsp_planes BSP File plane lump + \ingroup formats_bsp + Array of unique planes. + + Planes are shared between nodes and faces. +*/ +///@{ typedef struct dplane_s { - float normal[3]; - float dist; - int32_t type; // PLANE_X - PLANE_ANYZ + float normal[3]; ///< Plane normal + float dist; ///< Distance of the plane from the origin + int32_t type; ///< \ref bsp_plane_definition } dplane_t; +///@} -#define CONTENTS_EMPTY -1 -#define CONTENTS_SOLID -2 -#define CONTENTS_WATER -3 -#define CONTENTS_SLIME -4 -#define CONTENTS_LAVA -5 -#define CONTENTS_SKY -6 -#define CONTENTS_ORIGIN -7 // removed at csg time -#define CONTENTS_CLIP -8 // changed to contents_solid +/** \defgroup bsp_plane_definition Plane definitions + \ingroup bsp_planes + Planes are always canonical in that thier normals always point along + a positive axis for axial planes ((1, 0, 0), (0, 1, 0), or (0, 0, 1)), + or the largest component is positive. +*/ +///@{ +/// \name Axial planes +///@{ +#define PLANE_X 0 ///< Plane normal points along +X axis +#define PLANE_Y 1 ///< Plane normal points along +Y axis +#define PLANE_Z 2 ///< Plane normal points along +Z axis +///@} +/// \name Non-axial planes. Specifies the closest axis. +///@{ +#define PLANE_ANYX 3 ///< Plane normal close to +X +#define PLANE_ANYY 4 ///< Plane normal close to +Y +#define PLANE_ANYZ 5 ///< Plane normal close to +Z +///@} +///@} -#define CONTENTS_CURRENT_0 -9 -#define CONTENTS_CURRENT_90 -10 -#define CONTENTS_CURRENT_180 -11 -#define CONTENTS_CURRENT_270 -12 -#define CONTENTS_CURRENT_UP -13 -#define CONTENTS_CURRENT_DOWN -14 +/** \defgroup bsp_nodes BSP nodes lump + \ingroup formats_bsp -//BSP2 version (bsp 29 version is in bspfile.c) -typedef struct dnode_s { - int32_t planenum; - int32_t children[2]; // negative numbers are -(leafs+1), not nodes - float mins[3]; // for sphere culling - float maxs[3]; - uint32_t firstface; - uint32_t numfaces; // counting both sides + These nodes form the visible BSP. While not all nodes will have faces, + most (or at least many) will, as the nodes are used for depth-sorting the + faces while traversing the BSP tree using front-depth-first-in-order + traversal. + + They can also be used for collision detection as the leaf nodes indicate + the contents of the volume. The engine takes advantage of this to + construct hull 0 (disk space was still very precious in 1996) +*/ +///@{ + +/** Spit a convex region of space into two convex regions. + + The node's plane divides the space into a front side and a back side, + where a point is in front of the plane if its dot product with the plane + normal is positive or 0, and ond behind (in back of) the plane if the + dot product is negative. +*/ +typedef struct dnode_s { //BSP2 version (bsp 29 version is in bspfile.c) + uint32_t planenum; ///< Index of plane defining this node + /// Indices of the child nodes on the front [0] and back [1] side of this + /// node. Negative indicies indicate leaf nodes, where the leaf index is + /// -(child + 1) (or child is -(leaf + 1)). + int32_t children[2]; + /// \name Node bounding box + ///@{ + float mins[3]; ///< minimum X, Y, Z + float maxs[3]; ///< maximum X, Y, Z + ///@} + /// \name Node plane faces + /// List of \ref bsp_face faces on the node's plane. Used for + /// depth-sorting the faces while traversing the visible BSP tree. + ///@{ + uint32_t firstface; ///< index of first face on nonde plane + uint32_t numfaces; ///< numer of faces on node plane (both sides) + ///@} } dnode_t; +///@} -//BSP2 version (bsp 29 version is in bspfile.c) -typedef struct dclipnode_s { - int32_t planenum; - int32_t children[2]; // negative numbers are contents +/** \defgroup bsp_clipnodes BSP clipping nodes lump + \ingroup formats_bsp + + Compact BSP tree for collision detection. +*/ +///@{ +typedef struct dclipnode_s { //BSP2 version (bsp 29 version is in bspfile.c) + uint32_t planenum; ///< Index of plane defining this node + /// Indices of the child nodes on the front [0] and back [1] side of this + /// node. Negative indicies indicate leaf contents. + /// \ref bsp_leaf_contents + int32_t children[2]; } dclipnode_t; +///@} +/** \defgroup bsp_texinfo BSP texture information lump + \ingroup formats_bsp + Texture mapping information for visible faces. An individual texinfo may + be shared by many faces. +*/ +///@{ typedef struct texinfo_s { - float vecs[2][4]; // [s/t][xyz offset] - int32_t miptex; - int32_t flags; + /// The texture plane basis vectors, with the \a s basis vector in index + /// 0 and the \a t basis vector in index 1. + /// For each index, the first three elements are the X, Y, Z components + /// of the basis vector, and the fourth component is the offset. Can be + /// viewed as a 2 row x 4 column matrix (\a M) that is multiplied by + /// a homogeneos vector (\a v) for a face vertex to determine that + /// vertex's UV coordinates (ie, \a M \a v) + float vecs[2][4]; + /// Index of the miptex block in the texture data lump + /// (\ref bsp_textures). If the referenced miptex is a member of + /// an animation group, then the entire group is used by this texinfo. + uint32_t miptex; + uint32_t flags; ///< \ref bsp_texinfo_flags } texinfo_t; -#define TEX_SPECIAL 1 // sky or slime, no lightmap or 256 subdivision -#define TEX_MISSING 2 // this texinfo does not have a texture +///@} -// note that edge 0 is never used, because negative edge nums are used for -// counterclockwise use of the edge in a face -//BSP2 version (bsp 29 version is in bspfile.c) -typedef struct dedge_s { - uint32_t v[2]; // vertex numbers +/** \defgroup bsp_texinfo_flags BSP texture information flags + \ingroup bsp_texinfo +*/ +///@{ +#define TEX_SPECIAL 1 ///< sky or slime, no lightmap, 256 subdivision +#define TEX_MISSING 2 ///< this texinfo does not have a texture (N/U) +///@} + +/** \defgroup bsp_edges BSP edges lump + \ingroup formats_bsp + + \note Edge 0 is never used as negative edge indices are indicate + counterclockwise use of the edge in a face. +*/ +///@{ +typedef struct dedge_s { //BSP2 version (bsp 29 version is in bspfile.c) + uint32_t v[2]; ///< vertex indices in winding order } dedge_t; +///@} -#define MAXLIGHTMAPS 4 -//BSP2 version (bsp 29 version is in bspfile.c) -typedef struct dface_s { - int32_t planenum; +/** \defgroup bsp_face BSP visible faces + \ingroup formats_bsp +*/ +///@{ +typedef struct dface_s { //BSP2 version (bsp 29 version is in bspfile.c) + uint32_t planenum; ///< Index of the plane containing this face + /// Indicates the side of the plane considered to be the front for this + /// face. 0 indicates the postive normal side, 1 indicates the negative + /// normal side (ie, the face is on the back side of the plane). int32_t side; - int32_t firstedge; // we must support > 64k edges - int32_t numedges; - int32_t texinfo; + /// \name List of edge indices in clockwise order + /// \ref bsp_surfedges + /// Negative edge indices indicate that the edge is being used in its + /// reverse direction (ie, \a v[1] to \a v[0] instead of \a v[0] to + /// \a v[1]). + /// \note This is a double-indirection. + ///@{ + uint32_t firstedge; ///< Index of first edge index + uint32_t numedges; ///< Number of edge indices + ///@} + /// Index of texinfo block describing texture mapping and miptex reference + /// for this face. + uint32_t texinfo; -// lighting info + /// \name lighting info + ///@{ +#define MAXLIGHTMAPS 4 + /// List of lighting styles affecting the lightmaps affecting this face. + /// 255 indicates both no style thus no lightmap and the end of the list. byte styles[MAXLIGHTMAPS]; - int32_t lightofs; // start of [numstyles*surfsize] samples + /// Offset into the lighting data (ref bsp_lighting) of the first lightmap + /// affecting this face. The number of lightmaps affecting this face is + /// inferred from the \a styles array. The size of each lightmap is + /// determined from the texture extents of the face as (E / 16 + 1) + /// (integer division). Thus the total lightmap data for this face is + /// given by (nummaps * (Es / 16 + 1) * (Et / 16 + 1)) bytes. + /// The lightmap data is 8-bit, ranging from 0 to 255. + /// + /// -1 indicates no lightmap data. + uint32_t lightofs; + ///@} } dface_t; +///@} +/** \defgroup bsp_leafs BSP leafs lump + \ingroup formats_bsp + leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas + all other leafs need visibility info +*/ +///@{ + +/** A convex volume of space delimited by the surrounding node planes. + + With the exception of the generic solid leaf at index 0, *ALL* leafs are + convex polyhedra. However, they will generally not be manifold as + otherwise it would not be possible to see into or out of a leaf, nor for + an entity to pass in or out of one. +*/ +typedef struct dleaf_s { //BSP2 version (bsp 29 version is in bspfile.c) + /// The contents of the leaf. \ref bsp_leaf_contents + int32_t contents; + /// Offset into the \ref bsp_visibility data block for this leaf's + /// visibility data. -1 if the leaf has no visibility data. + int32_t visofs; + + /// \name Bounding box for frustum culling + ///@{ + float mins[3]; ///< minimum X, Y, Z + float maxs[3]; ///< maximum X, Y, Z + ///@} + + /// \name Visible faces of the leaf + /// List of face indices that form the visible faces of the leaf. + /// \note This is a double-indirection. + ///@{ + uint32_t firstmarksurface; ///< index of first visible face index + uint32_t nummarksurfaces; ///< number of indices + ///@} + +#define NUM_AMBIENTS 4 + /// automatic ambient sounds \ref bsp_leaf_ambient + byte ambient_level[NUM_AMBIENTS]; +} dleaf_t; +///@} + +/** \defgroup bsp_leaf_contents BSP leaf contents + \ingroup bsp_leafs +*/ +///@{ +#define CONTENTS_EMPTY -1 ///< Open, transparent, passable space +#define CONTENTS_SOLID -2 ///< Solid, opaque, impassible space +#define CONTENTS_WATER -3 ///< Buoyant, translucent, passable space +#define CONTENTS_SLIME -4 ///< Like water, but toxic +#define CONTENTS_LAVA -5 ///< Like water, but hot +#define CONTENTS_SKY -6 ///< FIXME sky, but... +#define CONTENTS_ORIGIN -7 ///< removed at csg time +#define CONTENTS_CLIP -8 ///< changed to CONTENTS_SOLID + +#define CONTENTS_CURRENT_0 -9 ///< changed to CONTENTS_WATER +#define CONTENTS_CURRENT_90 -10 ///< changed to CONTENTS_WATER +#define CONTENTS_CURRENT_180 -11 ///< changed to CONTENTS_WATER +#define CONTENTS_CURRENT_270 -12 ///< changed to CONTENTS_WATER +#define CONTENTS_CURRENT_UP -13 ///< changed to CONTENTS_WATER +#define CONTENTS_CURRENT_DOWN -14 ///< changed to CONTENTS_WATER +///@} + +/** \defgroup bsp_leaf_ambient BSP leaf ambient sounds + \ingroup bsp_leafs +*/ +///@{ #define AMBIENT_WATER 0 #define AMBIENT_SKY 1 #define AMBIENT_SLIME 2 #define AMBIENT_LAVA 3 +///@} -#define NUM_AMBIENTS 4 // automatic ambient sounds +/** \defgroup bsp_entities Entity data string + \ingroup formats_bsp -// leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas -// all other leafs need visibility info -//BSP2 version (bsp 29 version is in bspfile.c) -typedef struct dleaf_s { - int32_t contents; - int32_t visofs; // -1 = no visibility info + Single string containing all the entity dictionaries (edicts). - float mins[3]; // for frustum culling - float maxs[3]; + The format is extremely simple in that is is a series of blocks delimited + by { and }. Blocks cannot be nested. A { after a { but before a } will be + treated as either a key or a value. A } after a } but before a { is not + valid. { must be the first non-whitespace character in the string. } must + be the last non-whitespace character in the string if there is a {. - uint32_t firstmarksurface; - uint32_t nummarksurfaces; + The contents of a block consist of a series of key-value strinng pairs. + Unless the string contains spaces or punctuaiont (one of "{}()':") double + quote (") around the string are optional. Keys and values are separated + by whitespace (optional if quotes are used) but must be on the same line, + but it is best to use a space. Key-value pairs may be on separate lines + (prefered for readability). - byte ambient_level[NUM_AMBIENTS]; -} dleaf_t; + Key strings are Quake-C entity field names. Value strings are interpreted + based on the type of the field. -//============================================================================ + Below is a sample edict + \verbatim + { + "origin" "-2528 -384 512" + "angle" "270" + "classname" "info_player_start" + } + \endverbatim + + As an extention, QuakeForge supports \ref property-list entity string data + if the first non-whitespace character is a (. QuakeForge progs can register + a custom parser for the resulting property list, but the default parser + supports only a single array of dictionaries containing only simple + key-value string pairs. +*/ + +/** \defgroup bsp_lighting Lighting data + \ingroup formats_bsp + + Pre-computed lightmap data. Each byte represents a single lightmap + pixel, thus a lightmap is luminance with values ranging from 0 for + full-dark to 255 for full-bright. The pixels for each lightmap (up + to four) for each visible face form a linear stream. + + Computed using tools such as qflight. +*/ + +/** \defgroup bsp_marksurfaces BSP Leaf faces + \ingroup formats_bsp + + Array of face indices representing the faces on the leafs. The face + indices are grouped by leaf. Because whether a face gets split depends + on how the nodes split space, a face may be shared by multiple leafs. + FIXME needs confirmation, but maps with more leaf face indices than + faces in the face array have observed. +*/ + +/** \defgroup bsp_surfedges BSP face edges + \ingroup formats_bsp + + Array of edge indices specifying the edges used in face polyons. + + The edges are grouped by face polygon, and are listed in clockwise order + from that face's perspective (when looking at the face from that face's + front side). + + As actual edges are reused and thus shared by faces, negative edge + indices indicate that the edge is to be reversed. That is, instead of + going from \a v[0] to \a v[1] for edges referenced by positive indices, + go from \a v[1] to \a v[0] for edges referenced by negative indices. + \ref bsp_edges +*/ + +/** \defgroup bsp_visibility BSP visibility data + \ingroup formats_bsp + + The primary culling data in a Quake BSP. Conceptually, it is just an + array of bit-sets representing the leafs visible from each leaf. + However, leaf 0 is not represented in the data at all: there is now row + for leaf 0 (first row is for leaf 1's visible leafs), and the first bit + in each row represents leaf 1. + + As well, the data within a row is compressed: runs of 0-bytes + (representing eight non-visible leafs) are represented as a 0 followed + by a single count byte. For example, a run of eight 0-bytes will appear + as 0x00 0x08, and a single 0 byte will appear as 0x00 0x01. + + Computed using tools such as \a qfvis. +*/ + +/** \defgroup bsp_texture_animation BSP Texture Animation + \ingroup formats_bsp + + Textures in BSP files can be animated. Animated textures are those + with miptex names in a special format: + + \verbatim + +Sname + \endverbatim + + That is, the miptex name starts with a +, followed by a single sequence + id character (0-9 or A-J (or a-j, case-insensitive)) then the name of the + animation group is formed by the remaining characters. Thus an animation + group can have one or two sequences with one to ten frames per sequence. + The two sequences are the main sequence (0-9) and the alternate sequence + (A-J). Gaps in a sequence are not allowed: there must always be a +0 or +A + miptex in the sequence, and there must be a texture for every digit or + letter up to the highest used. However, the miptex blocks can be in any + order within the textures block: the engine searches for them and sorts + them. + + Single-frame sequences are useful for simple static texture switchinng + based on game state (entity frame: 0 for main, non-zero for alternate). + Multi-frame sequences are played back at five frames per second. + + For example, the folowing sets up an animitation with a 4-frame main + sequence and a 2-frame alternate sequence. Perhaps a roaring fire in + the main sequence and glowing coals in the alternate. + + \verbatim + +0fire // main sequence + +1fire // main sequence + +2fire // main sequence + +3fire // main sequence + +Afire // alternate sequence + +Bfire // alternate sequence + \endverbatim +*/ + +/** \defgroup bspfile BSP file manipulation + \ingroup utils +*/ +///@{ +/** Memory representation of the BSP file. + All data is in native endian format. +*/ typedef struct bsp_s { - int own_header; - dheader_t *header; + int own_header; ///< \a header is owned by the structure + dheader_t *header; ///< the bsp header block - int own_models; - int nummodels; - dmodel_t *models; + int own_models; ///< \a models is owned by the structure + size_t nummodels; ///< number of models + dmodel_t *models; ///< Array of sub-models - int own_visdata; - size_t visdatasize; - byte *visdata; + int own_visdata; ///< \a visdata is owned by the structure + size_t visdatasize; ///< number of bytes in visdata + byte *visdata; ///< \ref bsp_visibility - int own_lightdata; - size_t lightdatasize; - byte *lightdata; + int own_lightdata; ///< \a lightdata is owned by the structure + size_t lightdatasize; ///< number of bytes in lightdata + byte *lightdata; ///< \ref bsp_lighting - int own_texdata; - size_t texdatasize; - byte *texdata; // (dmiptexlump_t) + int own_texdata; ///< \a texdata is owned by the structure + size_t texdatasize; ///< number of bytes in texture data + byte *texdata; ///< \ref bsp_textures - int own_entdata; - size_t entdatasize; - char *entdata; + int own_entdata; ///< \a entdata is owned by the structure + size_t entdatasize; ///< number of bytes in entdata string + char *entdata; ///< \ref bsp_entities - int own_leafs; - int numleafs; - dleaf_t *leafs; + int own_leafs; ///< \a leafs is owned by the structure + size_t numleafs; ///< number of leafs + dleaf_t *leafs; ///< array of leafs - int own_planes; - int numplanes; - dplane_t *planes; + int own_planes; ///< \a planes is owned by the structure + size_t numplanes; ///< number of planes + dplane_t *planes; ///< array of planes - int own_vertexes; - int numvertexes; - dvertex_t *vertexes; + int own_vertexes; ///< \a vertexes is owned by the structure + size_t numvertexes; ///< number of vertices + dvertex_t *vertexes; ///< array of vertices - int own_nodes; - int numnodes; - dnode_t *nodes; + int own_nodes; ///< \a nodes is owned by the structure + size_t numnodes; ///< number of nodes + dnode_t *nodes; ///< array of nodes - int own_texinfo; - int numtexinfo; - texinfo_t *texinfo; + int own_texinfo; ///< \a texinfo is owned by the structure + size_t numtexinfo; ///< number of texinfo blocks + texinfo_t *texinfo; ///< array of texinfo blocks - int own_faces; - int numfaces; - dface_t *faces; + int own_faces; ///< \a faces is owned by the structure + size_t numfaces; ///< number of faces + dface_t *faces; ///< array of faces - int own_clipnodes; - int numclipnodes; - dclipnode_t *clipnodes; + int own_clipnodes; ///< \a clipnodes is owned by the structure + size_t numclipnodes; ///< number of clipnodes + dclipnode_t *clipnodes; ///< array of clipnodes - int own_edges; - int numedges; - dedge_t *edges; + int own_edges; ///< \a edges is owned by the structure + size_t numedges; ///< number of edges + dedge_t *edges; ///< array of edges - int own_marksurfaces; - int nummarksurfaces; - uint32_t *marksurfaces; + int own_marksurfaces;///< \a marksurfaces is owned by the structure + size_t nummarksurfaces; ///< number of marksurface indices + uint32_t *marksurfaces; ///< array of marksurfaces indices - int own_surfedges; - int numsurfedges; - int32_t *surfedges; + int own_surfedges; ///< \a surfedge is owned by the structure + size_t numsurfedges; ///< number of surfedge indices + int32_t *surfedges; ///< array of surfedge indices } bsp_t; /** Create a bsp structure from a memory buffer. @@ -290,24 +698,184 @@ typedef struct bsp_s { */ bsp_t *LoadBSPMem (void *mem, size_t size, void (*cb) (const bsp_t *, void *), void *cbdata); +/** Load a bsp file into memory. + The return structre is set up the same as for LoadBSPMem, with the + exception that the BSP data memory is owned by the structure and will be + freed by BSP_Free. + + \param file The open file from which to read the BSP data. + \param size The number of bytes to read. The BSP data may be a section + in a larger file (eg, in a pak file). + \return Initialized bsp structure. +*/ bsp_t *LoadBSPFile (QFile *file, size_t size); +/** Write the bsp data to the given file. + Does any necessary byte-swapping to ensure the written data is in + little-endian format. Automatically selects BSP-2 or BSP-29 based on the + data: if any value is too large for BSP-29, BSP-2 will be written, + otherwise BSP-29 will be written. + + \param bsp The bsp data to be written to the given file. + \param file The file to which the data will be written. + + \note \a file is *not* closes. It is the caller's responsibility to close + \a file. +*/ void WriteBSPFile (const bsp_t *bsp, QFile *file); + +/** Create a fresh BSP struct ready to be populated with data. + Initially, none of the data pointers (which start as null) are owned by + the bsp, but as data is added using the Add functions, the pointers will + become owned. + + \return Pointer to a blank-slate BSP strucutre. + + \note Mixing use of an Add function with a private pointer for the + same data will result in undefined behavior. +*/ bsp_t *BSP_New (void); + +/** Free the BSP data. + + Only memory blocks owned by the BSP structure (and the structure itselve) + will be freed. + + \param bsp Pointer to the BSP structure to be freed. +*/ void BSP_Free (bsp_t *bsp); + +/** Add a single plane to the BSP data. + + \param bsp Pointer to the BSP structure to be modified. + \param plane Pointer to the single plane to be added to the BSP + structure. +*/ void BSP_AddPlane (bsp_t *bsp, const dplane_t *plane); + +/** Add a single leaf to the BSP data. + + \param bsp Pointer to the BSP structure to be modified. + \param leaf Pointer to the single leaf to be added to the BSP + structure. +*/ void BSP_AddLeaf (bsp_t *bsp, const dleaf_t *leaf); + +/** Add a single vertex to the BSP data. + + \param bsp Pointer to the BSP structure to be modified. + \param vertex Pointer to the single vertex to be added to the BSP + structure. +*/ void BSP_AddVertex (bsp_t *bsp, const dvertex_t *vertex); + +/** Add a single node to the BSP data. + + \param bsp Pointer to the BSP structure to be modified. + \param node Pointer to the single node to be added to the BSP + structure. +*/ void BSP_AddNode (bsp_t *bsp, const dnode_t *node); + +/** Add a single texinfo to the BSP data. + + \param bsp Pointer to the BSP structure to be modified. + \param texinfo Pointer to the single texinfo to be added to the BSP + structure. +*/ void BSP_AddTexinfo (bsp_t *bsp, const texinfo_t *texinfo); + +/** Add a single face to the BSP data. + + \param bsp Pointer to the BSP structure to be modified. + \param face Pointer to the single face to be added to the BSP + structure. +*/ void BSP_AddFace (bsp_t *bsp, const dface_t *face); + +/** Add a single clipnode to the BSP data. + + \param bsp Pointer to the BSP structure to be modified. + \param clipnode Pointer to the single clipnode to be added to the BSP + structure. +*/ void BSP_AddClipnode (bsp_t *bsp, const dclipnode_t *clipnode); + +/** Add a single marksurface to the BSP data. + + \param bsp Pointer to the BSP structure to be modified. + \param marksurface The single marksurface to be added to the BSP + structure. +*/ void BSP_AddMarkSurface (bsp_t *bsp, int marksurface); + +/** Add a single surfedge to the BSP data. + + \param bsp Pointer to the BSP structure to be modified. + \param surfedge Pointer to the single surfedge to be added to the BSP + structure. +*/ void BSP_AddSurfEdge (bsp_t *bsp, int surfedge); + +/** Add a single edge to the BSP data. + + \param bsp Pointer to the BSP structure to be modified. + \param edge Pointer to the single edge to be added to the BSP + structure. +*/ void BSP_AddEdge (bsp_t *bsp, const dedge_t *edge); + +/** Add a single model to the BSP data. + + \param bsp Pointer to the BSP structure to be modified. + \param model Pointer to the single model to be added to the BSP + structure. +*/ void BSP_AddModel (bsp_t *bsp, const dmodel_t *model); + +/** Add lighmap data to the BSP data. + + \param bsp Pointer to the BSP structure to be modified. + \param lightdata Pointer to the lightmap data to be added to the BSP + structure. ref bsp_lighting + \param lightdatasize The number of bytes in the lightmap data + + \note Call only once! +*/ void BSP_AddLighting (bsp_t *bsp, const byte *lightdata, size_t lightdatasize); + +/** Add visibilityd data to the BSP data. + + \param bsp Pointer to the BSP structure to be modified. + \param visdata Pointer to the visibility data to be added to the BSP + structure. \ref bsp_visibility + \param visdatasize The number of bytes in the visibility data + + \note Call only once! +*/ void BSP_AddVisibility (bsp_t *bsp, const byte *visdata, size_t visdatasize); + +/** Add an entity data string to the BSP data. + + \param bsp Pointer to the BSP structure to be modified. + \param entdata Pointer to the entity data string to be added to the BSP + structure. \ref bsp_entities + \param entdatasize The number of bytes in the entity data string + + \note Call only once! +*/ void BSP_AddEntities (bsp_t *bsp, const char *entdata, size_t entdatasize); + +/** Add texture data to the BSP data. + + \param bsp Pointer to the BSP structure to be modified. + \param texdata Pointer to the texture data to be added to the BSP + structure. \ref bsp_textures + \param texdatasize The number of bytes in the texture data + + \note Call only once! +*/ void BSP_AddTextures (bsp_t *bsp, const byte *texdata, size_t texdatasize); -#endif//__bspfile_h_ +///@} + +#endif//__QF_bspfile_h diff --git a/include/QF/cbuf.h b/include/QF/cbuf.h index 9018fa6b6..c6d6b01f9 100644 --- a/include/QF/cbuf.h +++ b/include/QF/cbuf.h @@ -34,7 +34,7 @@ /** \defgroup cbuf Command buffer management. \ingroup utils */ -//@{ +///@{ #include @@ -65,7 +65,7 @@ typedef struct cbuf_s { } state; int (*unknown_command)(void); // handle unkown commands. !0 = handled - qboolean strict; // Should we tolerate unknown commands? + bool strict; // Should we tolerate unknown commands? double resumetime; // Time when stack can be executed again void *data; // Pointer to interpreter data @@ -92,6 +92,7 @@ cbuf_t * Cbuf_New (cbuf_interpreter_t *interp); void Cbuf_Delete (cbuf_t *cbuf); void Cbuf_DeleteStack (cbuf_t *stack); +void Cbuf_DeleteStackReverse (cbuf_t *stack); void Cbuf_Reset (cbuf_t *cbuf); cbuf_t *Cbuf_PushStack (cbuf_interpreter_t *interp); void Cbuf_AddText (cbuf_t *cbuf, const char *text); @@ -100,6 +101,6 @@ void Cbuf_Execute (cbuf_t *cbuf); void Cbuf_Execute_Stack (cbuf_t *cbuf); void Cbuf_Execute_Sets (cbuf_t *cbuf); -//@} +///@} #endif//__QF_cbuf_h diff --git a/include/QF/cdaudio.h b/include/QF/cdaudio.h index e7596ad78..0a326f8b3 100644 --- a/include/QF/cdaudio.h +++ b/include/QF/cdaudio.h @@ -25,17 +25,16 @@ */ -#ifndef _CDAUDIO_H -#define _CDAUDIO_H +#ifndef __QF_cdaudio_h +#define __QF_cdaudio_h #include "QF/qtypes.h" int CDAudio_Init(void); -void CDAudio_Play(int track, qboolean looping); +void CDAudio_Play(int track, bool looping); void CDAudio_Stop(void); void CDAudio_Pause(void); void CDAudio_Resume(void); -void CDAudio_Shutdown(void); void CDAudio_Update(void); -#endif // _CDAUDIO_H +#endif//__QF_cdaudio_h diff --git a/include/QF/cexpr.h b/include/QF/cexpr.h new file mode 100644 index 000000000..0d08d5ead --- /dev/null +++ b/include/QF/cexpr.h @@ -0,0 +1,168 @@ +/* + cexpr.h + + Config expression parser. Or concurrent. + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __QF_expr_h +#define __QF_expr_h + +#include + +struct exprval_s; +struct exprctx_s; +struct va_ctx_s; + +typedef struct binop_s { + int op; + struct exprtype_s *other; + struct exprtype_s *result; + void (*func) (const struct exprval_s *val1, + const struct exprval_s *val2, + struct exprval_s *result, + struct exprctx_s *context); +} binop_t; + +typedef struct unop_s { + int op; + struct exprtype_s *result; + void (*func) (const struct exprval_s *val, struct exprval_s *result, + struct exprctx_s *context); +} unop_t; + +typedef struct exprtype_s { + const char *name; + size_t size; + const char *(*get_string) (const struct exprval_s *val, struct va_ctx_s *va_ctx); + binop_t *binops; + unop_t *unops; + void *data; +} __attribute__((designated_init)) exprtype_t; + +typedef struct exprval_s { + exprtype_t *type; + void *value; +} exprval_t; + +typedef struct exprlist_s { + struct exprlist_s *next; + exprval_t *value; +} exprlist_t; + +typedef struct exprsym_s { + const char *name; + exprtype_t *type; + void *value; + struct exprsym_s *next; +} exprsym_t; + +typedef struct exprfunc_s { + exprtype_t *result; + int num_params; + exprtype_t **param_types; + void (*func) (const exprval_t **params, exprval_t *result, + struct exprctx_s *context); +} exprfunc_t; + +typedef struct exprtab_s { + exprsym_t *symbols; + struct hashtab_s *tab; +} exprtab_t; + +typedef struct exprarray_s { + exprtype_t *type; + unsigned size; +} exprarray_t; + +typedef struct exprctx_s { + struct exprctx_s *parent; // for nested symol scopes + exprval_t *result; + exprtab_t *symtab; // directly accessible symbols + exprtab_t *external_variables; // accessible via $id + const char *msg_prefix; // optional prefix for error messages + struct memsuper_s *memsuper; + const struct plitem_s *item; + struct plitem_s *messages; + struct hashctx_s **hashctx; + int errors; +} exprctx_t; + +typedef struct exprenum_s { + exprtype_t *type; + exprtab_t *symtab; +} exprenum_t; + +exprval_t *cexpr_assign_value (exprval_t *dst, const exprval_t *src, + exprctx_t *context); +int cexpr_parse_enum (exprenum_t *enm, const char *str, + const exprctx_t *context, void *data); +binop_t *cexpr_find_cast (exprtype_t *dst_type, exprtype_t *src_type) __attribute__((pure)); +exprval_t *cexpr_value (exprtype_t *type, exprctx_t *ctx); +exprval_t *cexpr_value_reference (exprtype_t *type, void *data, exprctx_t *ctx); +const char *cexpr_enum_get_string (const exprval_t *val, struct va_ctx_s *va_ctx) __attribute__((pure)); +const char *cexpr_flags_get_string (const exprval_t *val, struct va_ctx_s *va_ctx) __attribute__((pure)); +int cexpr_eval_string (const char *str, exprctx_t *context); +void cexpr_error(exprctx_t *ctx, const char *fmt, ...) __attribute__((format(PRINTF,2,3))); + +void cexpr_array_getelement (const exprval_t *a, const exprval_t *b, + exprval_t *c, exprctx_t *ctx); +void cexpr_struct_getfield (const exprval_t *a, const exprval_t *b, + exprval_t *c, exprctx_t *ctx); +void cexpr_struct_pointer_getfield (const exprval_t *a, const exprval_t *b, + exprval_t *c, exprctx_t *ctx); +exprval_t *cexpr_cvar (const char *name, exprctx_t *ctx); +exprval_t *cexpr_cvar_struct (exprctx_t *ctx); + +void cexpr_cast_plitem (const exprval_t *val1, const exprval_t *src, + exprval_t *result, exprctx_t *ctx); + +void cexpr_init_symtab (exprtab_t *symtab, exprctx_t *ctx); + +char *cexpr_yyget_text (void *scanner); + +extern exprenum_t cexpr_bool_enum; +extern exprtype_t cexpr_bool; +extern exprtype_t cexpr_int; +extern exprtype_t cexpr_uint; +extern exprtype_t cexpr_size_t; +extern exprtype_t cexpr_float; +extern exprtype_t cexpr_double; +extern exprtype_t cexpr_vector; +extern exprtype_t cexpr_quaternion; +extern exprtype_t cexpr_exprval; +extern exprtype_t cexpr_field; +extern exprtype_t cexpr_function; +extern exprtype_t cexpr_plitem; +extern exprtype_t cexpr_string; +// generic pointer holder, never generated directly by cexpr +extern exprtype_t cexpr_voidptr; + +extern binop_t cexpr_array_binops[]; +extern binop_t cexpr_struct_binops[]; +extern binop_t cexpr_struct_pointer_binops[]; +extern binop_t cexpr_flag_binops[]; +extern unop_t cexpr_flag_unops[]; + +extern exprsym_t cexpr_lib_symbols[]; + +#endif//__QF_expr_h diff --git a/include/QF/checksum.h b/include/QF/checksum.h index 9a7dfb8e6..12fcaf477 100644 --- a/include/QF/checksum.h +++ b/include/QF/checksum.h @@ -25,12 +25,12 @@ */ -#ifndef __checksum_h -#define __checksum_h +#ifndef __QF_checksum_h +#define __QF_checksum_h /** \addtogroup crc */ -//@{ +///@{ #include "QF/qtypes.h" @@ -38,6 +38,6 @@ unsigned int Com_BlockChecksum (const void *buffer, int length); void Com_BlockFullChecksum (const void *buffer, int len, unsigned char *outbuf); byte COM_BlockSequenceCRCByte (const byte *base, int length, int sequence); -//@} +///@} -#endif // __checksum_h +#endif//__QF_checksum_h diff --git a/include/QF/classes/ArrayList.h b/include/QF/classes/ArrayList.h index 78b4c8f1b..83eb0929f 100644 --- a/include/QF/classes/ArrayList.h +++ b/include/QF/classes/ArrayList.h @@ -44,7 +44,7 @@ classDecl (ArrayListIterator, Iterator, ArrayList *list; unsigned int pos; unsigned int smods; - qboolean alive; + bool alive; ObjRefs_t allrefs; ); #define ARRAYLISTITERATOR(o) ((ArrayListIterator *)(o)) diff --git a/include/QF/classes/Collection.h b/include/QF/classes/Collection.h index b867f9faa..88b2075c8 100644 --- a/include/QF/classes/Collection.h +++ b/include/QF/classes/Collection.h @@ -38,9 +38,9 @@ classDecl (Collection, Object, unsigned int count; Class *type; - qboolean methodDecl (Collection, add, Object *o); + bool methodDecl (Collection, add, Object *o); Object * methodDecl (Collection, remove, Object *o); - qboolean methodDecl (Collection, contains, Object *o); + bool methodDecl (Collection, contains, Object *o); Iterator * methodDecl (Collection, iterator); ); #define COLLECTION(o) ((Collection *)(o)) diff --git a/include/QF/classes/Iterator.h b/include/QF/classes/Iterator.h index bb5169a0d..c393de349 100644 --- a/include/QF/classes/Iterator.h +++ b/include/QF/classes/Iterator.h @@ -35,7 +35,7 @@ classDecl (Iterator, Object, Object * methodDecl (Iterator, next); - qboolean methodDecl (Iterator, hasNext); + bool methodDecl (Iterator, hasNext); ); #define ITERATOR(o) ((Iterator *)(o)) diff --git a/include/QF/classes/List.h b/include/QF/classes/List.h index 1cb2cb8e3..931944942 100644 --- a/include/QF/classes/List.h +++ b/include/QF/classes/List.h @@ -35,9 +35,9 @@ classDecl (List, Collection, unsigned int smods; - qboolean methodDecl (List, set, unsigned int index, Object *o); + bool methodDecl (List, set, unsigned int index, Object *o); Object * methodDecl (List, get, unsigned int index); - qboolean methodDecl (List, insertAt, unsigned int index, Object *o); + bool methodDecl (List, insertAt, unsigned int index, Object *o); Object * methodDecl (List, removeAt, unsigned int index); ); #define LIST(o) ((List *)(o)) diff --git a/include/QF/cmd.h b/include/QF/cmd.h index 85131083e..98199756c 100644 --- a/include/QF/cmd.h +++ b/include/QF/cmd.h @@ -31,12 +31,13 @@ /** \defgroup cmd Command management. \ingroup utils */ -//@{ +///@{ #include "QF/qtypes.h" #include "QF/cbuf.h" typedef void (*xcommand_t) (void); +typedef void (*xdatacmd_t) (void *data); typedef enum { src_client, // came in over a net connection as a clc_stringcmd @@ -48,6 +49,8 @@ typedef struct cmd_function_s { struct cmd_function_s *next; const char *name; xcommand_t function; + xdatacmd_t datafunc; + void *data; const char *description; } cmd_function_t; @@ -56,31 +59,34 @@ extern cmd_source_t cmd_source; void Cmd_Init_Hash (void); void Cmd_Init (void); -int Cmd_AddCommand (const char *cmd_name, xcommand_t function, const char *description); +int Cmd_AddCommand (const char *cmd_name, xcommand_t function, + const char *description); +int Cmd_AddDataCommand (const char *cmd_name, xdatacmd_t function, + void *data, const char *description); int Cmd_RemoveCommand (const char *cmd_name); -qboolean Cmd_Exists (const char *cmd_name); -const char *Cmd_CompleteCommand (const char *partial); -int Cmd_CompleteCountPossible (const char *partial); +bool Cmd_Exists (const char *cmd_name); +const char *Cmd_CompleteCommand (const char *partial) __attribute__((pure)); +int Cmd_CompleteCountPossible (const char *partial) __attribute__((pure)); const char **Cmd_CompleteBuildList (const char *partial); -int Cmd_Argc (void); -const char *Cmd_Argv (int arg); -const char *Cmd_Args (int start); +int Cmd_Argc (void) __attribute__((pure)); +const char *Cmd_Argv (int arg) __attribute__((pure)); +const char *Cmd_Args (int start) __attribute__((pure)); struct cbuf_args_s; int Cmd_Command (struct cbuf_args_s *args); int Cmd_ExecuteString (const char *text, cmd_source_t src); struct cbuf_s; void Cmd_StuffCmds (struct cbuf_s *cbuf); -void Cmd_Exec_File (struct cbuf_s *cbuf, const char *path, int qfs); +int Cmd_Exec_File (struct cbuf_s *cbuf, const char *path, int qfs); void Cmd_AddProvider(const char *name, struct cbuf_interpreter_s *interp); struct cbuf_interpreter_s *Cmd_GetProvider(const char *name); extern struct cbuf_args_s *cmd_args; -extern struct cvar_s *cmd_warncmd; +extern int cmd_warncmd; -//@} +///@} #endif//__QF_cmd_h diff --git a/include/QF/cmem.h b/include/QF/cmem.h new file mode 100644 index 000000000..2641cabf8 --- /dev/null +++ b/include/QF/cmem.h @@ -0,0 +1,153 @@ +/* + cmem.h + + Cache-line aligned memory allocator + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __QF_cmem_h +#define __QF_cmem_h + +#include "QF/qtypes.h" + +#define MEM_LINE_SIZE 64 +#define MAX_CACHE_LINES 9 + +typedef struct memline_s { + /* chain of free line blocks for fast allocation + * chain begins in memsuper_t + */ + struct memline_s *free_next; + struct memline_s **free_prev; + /* chain of free line blocks within a membock for merging + * chain begins in memblock_t + */ + struct memline_s *block_next; + struct memline_s **block_prev; + size_t size; + /* owning block + */ + struct memblock_s *block; + size_t pad[2]; +} __attribute__((aligned (64))) memline_t; + +typedef struct memsline_s { + struct memsline_s *next; + size_t size:2; + size_t list:4; + size_t prev:8 * sizeof (void *) - 6; // memsline_t ** +} memsline_t; + +typedef struct memblock_s { + struct memblock_s *next; + struct memblock_s **prev; + memline_t *free_lines; + /* Size of memory available within the page-sized block. + */ + size_t size; + /* Size of memory region after the page-sized block. + * + * Will be 0 for blocks that were allocated exclusively for small + * allocations, otherwise indicates the size of the allocated block. + */ + size_t post_size; + /* True if the post-header block is free to be reused. + */ + int post_free; +#if __WORDSIZE == 64 + int pad; +#endif + size_t allocated; +} __attribute__((aligned (64))) memblock_t; + +typedef struct memsuper_s { + size_t page_size; + size_t page_mask; + memblock_t *memblocks; + /* Allocated cache lines from which smaller blocks can be allocated. + * + * The index is the base-2 log minus 2 of the size of the elements in the + * cache line from which an element was last freed. Only 4-32 bytes are of + * interest because nothing smaller than 4 bytes (int/float) will be + * allocated, and 64 bytes and up consume entire cache lines. + */ + memsline_t *last_freed[4]; + /* Free chache lines grouped by size. + * + * The index is the base-2 log of the MINIMUM number of cache lines + * available in each block. ie, blocks with 4, 5, 6 and 7 lines will all + * be in the third list (index 2). For 4k page sizes, only 6 lists are + * needed (32-63 lines) because a page can hold only 62 lines (1 for the + * control block and one to avoid a cache-line being on a page boundary). + * Having 9 (MAX_CACHE_LINES) lists allows page sizes up to 16kB. + */ + memline_t *free_lines[MAX_CACHE_LINES]; +} memsuper_t; + +memsuper_t *new_memsuper (void); +void delete_memsuper (memsuper_t *super); +void *cmemalloc (memsuper_t *super, size_t size); +void cmemfree (memsuper_t *super, void *mem); + +/** High-tide structure allocator for use in linked lists. + + Using a free-list with the name of \c NAME_freelist, return a single + element. + The type of the element must be a structure with a field named \c next. + When the free-list is empty, memory is claimed from the system in blocks. + Elements may be returned to the pool by linking them into the free-list. + + \param s The number of structures in the block. + \param t The structure type. + \param n The \c NAME portion of the \c NAME_freelist free-list. + \param super The memsuper_t super block from which to allocate memory. + + \hideinitializer +*/ +#define CMEMALLOC(s, t, n, super) \ + ({ \ + if (!n##_freelist) { \ + int i; \ + n##_freelist = cmemalloc ((super), (s) * sizeof (t)); \ + for (i = 0; i < (s) - 1; i++) \ + n##_freelist[i].next = &n##_freelist[i + 1]; \ + n##_freelist[i].next = 0; \ + } \ + t *v = n##_freelist; \ + n##_freelist = n##_freelist->next; \ + v; \ + }) + +/** Free a block allocated by #ALLOC + + \param n The \c NAME portion of the \c NAME_freelist free-list. + \param p The pointer to the block to be freed. + + \hideinitializer +*/ +#define CMEMFREE(n, p) \ + do { \ + p->next = n##_freelist; \ + n##_freelist = p; \ + } while (0) + +#endif//__QF_cmem_h diff --git a/include/QF/console.h b/include/QF/console.h index eeb9010d6..f2be4a4c6 100644 --- a/include/QF/console.h +++ b/include/QF/console.h @@ -25,105 +25,76 @@ */ -#ifndef __console_h -#define __console_h +#ifndef __QF_console_h +#define __QF_console_h -#include +#include -#include "QF/keys.h" #include "QF/qtypes.h" /** \defgroup console 2d Console Stuff */ -#define CON_TEXTSIZE 32764 -typedef struct -{ - char text[CON_TEXTSIZE]; - int current; // line where next message will be printed - int x; // offset in current line for next print - int display; // bottom of console displays this line - int numlines; // number of non-blank text lines, used for backscroling -} old_console_t; - -typedef struct inputline_s -{ - char **lines; // array of lines for input history - int num_lines; // number of lines in arry. 1 == no history - size_t line_size; // space available in each line. includes \0 - char prompt_char; // char placed at the beginning of the line - int edit_line; // current line being edited - int history_line; // current history line - size_t linepos; // cursor position within the current edit line - size_t scroll; // beginning of displayed line - size_t width; // viewable width for horizontal scrolling - const char *line; - void *user_data; // eg: window pointer - void (*complete)(struct inputline_s *); // tab key pressed - void (*enter)(struct inputline_s *); // enter key pressed - void (*draw)(struct inputline_s *); // draw input line to screen - - int x, y; // coordinates depend on display - int cursor; // is the cursor active (drawn?) -} inputline_t; - typedef struct { - byte *text; - size_t len; + uint32_t text; + uint32_t len; } con_line_t; typedef struct { - byte *buffer; - size_t buffer_size; - con_line_t *lines; - int max_lines; // size of lines array - int num_lines; // number of lines used - int cur_line; // current line + byte *buffer; + con_line_t *lines; + uint32_t buffer_size; + uint32_t max_lines; // size of lines array + uint32_t line_head; // number of lines used + uint32_t line_tail; // current line } con_buffer_t; +typedef enum { + con_inactive, + con_active, + con_fullscreen, + con_message, //FIXME should not be here ? + con_menu, //FIXME should not be here ? +} con_state_t; + extern int con_linewidth; extern struct plugin_s *con_module; extern struct console_data_s con_data; //extern int con_totallines; -//extern qboolean con_initialized; +//extern bool con_initialized; //extern byte *con_chars; -void Con_CheckResize (void); void Con_DrawConsole (void); -void Con_Printf (const char *fmt, ...); -void Con_Print (const char *fmt, va_list args); -void Con_ToggleConsole_f (void); +void Con_Printf (const char *fmt, ...) __attribute__((format(PRINTF, 1, 2))); +void Con_Print (const char *fmt, va_list args) __attribute__((format(PRINTF, 1, 0))); +void Con_SetState (con_state_t state); +struct inputline_s; // wrapper function to attempt to either complete the command line // or to list possible matches grouped by type // (i.e. will display possible variables, aliases, commands // that match what they've typed so far) -void Con_BasicCompleteCommandLine (inputline_t *il); +void Con_BasicCompleteCommandLine (struct inputline_s *il); // Generic libs/util/console.c function to display a list // formatted in columns on the console void Con_DisplayList(const char **list, int con_linewidth); -extern void (*con_list_print)(const char *fmt, ...); - -inputline_t *Con_CreateInputLine (int lines, int lsize, char prompt); -void Con_DestroyInputLine (inputline_t *inputline); -void Con_ClearTyping (inputline_t *il, int save); -void Con_ProcessInputLine (inputline_t *il, int ch); +extern void (*con_list_print)(const char *fmt, ...) __attribute__((format(PRINTF, 1, 2))); con_buffer_t *Con_CreateBuffer (size_t buffer_size, int max_lines); void Con_DestroyBuffer (con_buffer_t *buffer); void Con_BufferAddText (con_buffer_t *buf, const char *text); #define Con_BufferLine(b,l) ((b)->lines + ((l) + (b)->max_lines) % (b)->max_lines) +void Con_ClearBuffer (con_buffer_t *buffer); // init/shutdown functions -void Con_Init (const char *plugin_name); -void Con_Shutdown (void); +void Con_Load (const char *plugin_name); +void Con_Init (void); void Con_ExecLine (const char *line); void Con_ProcessInput (void); -void Con_KeyEvent (knum_t key, short unicode, qboolean down); void Con_SetOrMask (int mask); void Con_NewMap (void); @@ -135,14 +106,16 @@ void Con_Demolist_DEM_f (void); //FIXME need a better way to communicate this (bah, need a better menu system // in general :P) -void C_DrawInputLine (inputline_t *il); +void C_DrawInputLine (struct inputline_s *il); struct view_s; void Menu_Init (void); +void Menu_Shutdown (void); void Menu_Load (void); -void Menu_Draw (struct view_s *view); -void Menu_Draw_Hud (struct view_s *view); -int Menu_KeyEvent (knum_t key, short unicode, qboolean down); +void Menu_Draw (struct view_s view); +void Menu_Draw_Hud (struct view_s view); +struct IE_event_s; +int Menu_EventHandler (const struct IE_event_s *ie_event); void Menu_Enter (void); void Menu_Leave (void); @@ -151,4 +124,4 @@ void Menu_Leave_f (void); void Menu_Prev_f (void); void Menu_Next_f (void); -#endif // __console_h +#endif//__QF_console_h diff --git a/include/QF/crc.h b/include/QF/crc.h index 8c3154137..ad26bacb8 100644 --- a/include/QF/crc.h +++ b/include/QF/crc.h @@ -25,22 +25,22 @@ */ -#ifndef __crc_h -#define __crc_h +#ifndef __QF_crc_h +#define __QF_crc_h /** \defgroup crc Checksum generation. \ingroup utils */ -//@{ +///@{ #include "QF/qtypes.h" void CRC_Init(unsigned short *crcvalue); void CRC_ProcessByte(unsigned short *crcvalue, byte data); void CRC_ProcessBlock (const byte *start, unsigned short *crcvalue, int count); -unsigned short CRC_Value(unsigned short crcvalue); -unsigned short CRC_Block (const byte *start, int count); +unsigned short CRC_Value(unsigned short crcvalue) __attribute__((const)); +unsigned short CRC_Block (const byte *start, int count) __attribute__((pure)); -//@} +///@} -#endif // __crc_h +#endif//__QF_crc_h diff --git a/include/QF/cvar.h b/include/QF/cvar.h index 4f182d285..c023477bb 100644 --- a/include/QF/cvar.h +++ b/include/QF/cvar.h @@ -25,43 +25,38 @@ */ -#ifndef __cvar_h -#define __cvar_h +#ifndef __QF_cvar_h +#define __QF_cvar_h /** \defgroup cvar Configuration variables \ingroup utils */ -//@{ +///@{ +#include "QF/cexpr.h" +#include "QF/listener.h" #include "QF/qtypes.h" #include "QF/quakeio.h" typedef struct cvar_s { - const char *name; ///< The name of the cvar. - const char *string; ///< The current cvar value as a string. - const char *default_string; ///< The default value of the cvar. - int flags; ///< Cvar flags - /** Callback for when the cvar value changes. - - This allows for more flexibility in what happens when a cvar is - nodifed than can be achieved with flags alone. While a similar could - be done using commands, a cvar with a callback and CVAR_ARCHIVE set - allows the setting to be saved automatically. - - \param var This cvar. - */ - void (*callback)(struct cvar_s *var); - const char *description; ///< for "help" command - float value; ///< The current cvar value as a float - int int_val; ///< The current cvar value as an integer - vec3_t vec; ///< The current cvar value as a vector - struct cvar_s *next; ///< \internal Linked list of cvars. + const char *name; + const char *description; + const char *default_value; + unsigned flags; + exprval_t value; + int (*validator) (const struct cvar_s *var); + struct cvar_listener_set_s *listeners; } cvar_t; +typedef struct cvar_listener_set_s LISTENER_SET_TYPE (cvar_t) + cvar_listener_set_t; +typedef void (*cvar_listener_t) (void *data, const cvar_t *cvar); + +typedef int (*cvar_select_t) (const cvar_t *cvar, void *data); + typedef struct cvar_alias_s { char *name; ///< The name of the alias. cvar_t *cvar; ///< The cvar to which this alias refers - struct cvar_alias_s *next; ///< \internal LInked list of aliases. } cvar_alias_t; /** \name cvar_flags @@ -72,7 +67,7 @@ typedef struct cvar_alias_s { code goes "look, the user made fs_basepath already", uses the users value, but sets CVAR_ROM as per the call. */ -//@{ +///@{ #define CVAR_NONE 0 ///< normal cvar #define CVAR_ARCHIVE 1 ///< set to cause it to be saved to ///< config.cfg @@ -82,62 +77,67 @@ typedef struct cvar_alias_s { ///< (not implemented) #define CVAR_ROM 64 ///< display only, cannot be set #define CVAR_USER_CREATED 128 ///< created by a set command +#define CVAR_REGISTERED 256 ///< var has been registered #define CVAR_LATCH 2048 ///< will change only when C code next does ///< a Cvar_Get(), so it can't be changed ///< (not implemented) -//@} +///@} -// Returns the Cvar if found, creates it with value if not. Description and -// flags are always updated. -cvar_t *Cvar_Get (const char *name, const char *value, int cvarflags, - void (*callback)(cvar_t*), const char *description); +void Cvar_Register (cvar_t *var, cvar_listener_t listener, void *data); cvar_t *Cvar_FindAlias (const char *alias_name); cvar_t *Cvar_MakeAlias (const char *name, cvar_t *cvar); cvar_t *Cvar_RemoveAlias (const char *name); +void Cvar_AddListener (cvar_t *cvar, cvar_listener_t listener, void *data); +void Cvar_RemoveListener (cvar_t *cvar, cvar_listener_t listener, void *data); // equivelants to " " typed at the console -void Cvar_Set (cvar_t *var, const char *value); -void Cvar_SetValue (cvar_t *var, float value); +void Cvar_Set (const char *var, const char *value); +void Cvar_SetVar (cvar_t *var, const char *value); // allows you to change a Cvar's flags without a full Cvar_Get void Cvar_SetFlags (cvar_t *var, int cvarflags); // returns 0 if not defined or non numeric -float Cvar_VariableValue (const char *var_name); +float Cvar_Value (const char *var_name); // returns an empty string if not defined -const char *Cvar_VariableString (const char *var_name); +const char *Cvar_String (const char *var_name); +const char *Cvar_VarString (const cvar_t *var); // called by Cmd_ExecuteString when Cmd_Argv(0) doesn't match a known // command. Returns true if the command was a variable reference that // was handled. (print or change) -qboolean Cvar_Command (void); +bool Cvar_Command (void); // Writes lines containing "set variable value" for all variables // with the archive flag set to true. void Cvar_WriteVariables (QFile *f); +struct plitem_s; +void Cvar_SaveConfig (struct plitem_s *config); +void Cvar_LoadConfig (struct plitem_s *config); + // attempts to match a partial variable name for command line completion // returns NULL if nothing fits -const char *Cvar_CompleteVariable (const char *partial); +const char *Cvar_CompleteVariable (const char *partial) __attribute__((pure)); // Added by EvilTypeGuy - functions for tab completion system // Thanks to Fett erich@heintz.com // Thanks to taniwha -int Cvar_CompleteCountPossible (const char *partial); +int Cvar_CompleteCountPossible (const char *partial) __attribute__((pure)); const char **Cvar_CompleteBuildList (const char *partial); // Returns a pointer to the Cvar, NULL if not found cvar_t *Cvar_FindVar (const char *var_name); +const cvar_t **Cvar_Select (cvar_select_t select, void *data); + void Cvar_Init_Hash (void); void Cvar_Init (void); -extern cvar_t *cvar_vars; +///@} -//@} - -#endif // __cvar_h +#endif//__QF_cvar_h diff --git a/include/QF/darray.h b/include/QF/darray.h new file mode 100644 index 000000000..8126be871 --- /dev/null +++ b/include/QF/darray.h @@ -0,0 +1,374 @@ +/* + darray.h + + Dynamic arrays + + Copyright (C) 2020 Bill Currie + + Author: Bill Currie + Date: 2020/02/17 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_darray_h +#define __QF_darray_h + +#include + +#include "QF/sys.h" + +/** \defgroup darray Dynamic Arrays + \ingroup utils + + Dynamic array container object +*/ +///@{ + +/** The structure defs for a dynamic array with elements of the given type. + + This is just the defs of a struct delcaration: it is useless on its own. + The intended usage is something like: + + typedef struct dynamic_array_s DARRAY_TYPE(int) dynamic_array_t; + + This allows full flexibility in just how the actual type is used. + + The \a size field is the number of elements currently in the array, and + the \a maxSize field is the number of elements the array can hold without + being resized. + + The \a grow field specifies the number of elements by which \a maxSize is + to grow when the array needs to be resized. Setting this to 0 prevents + resizing and any attempt to do so is a fatal error. + + \param ele_type The type to use for the element array, which is accessed + by the \a a field. + \hideinitializer +*/ +#define DARRAY_TYPE(ele_type) \ + { \ + size_t size; \ + size_t maxSize; \ + size_t grow; \ + ele_type *a; \ + } + +#define DARRAY_STATIC_INIT(g) { .grow = g } + +/** Allocate a fixed-size array using the given allocator + + The allocated array is initilized to be ungrowable, and with both size + and maxSize set to the given size. + + \param array_type Expression acceptable by typeof for determining the + type of the array. + \param array_size The size of the array. + \param alloc Allocator compatible with malloc (eg, alloca). + \hideinitializer +*/ +#define DARRAY_ALLOCFIXED(array_type, array_size, alloc) \ + ({ \ + __auto_type s = (array_size); \ + typeof (array_type) *ar = alloc (sizeof(*ar) \ + + s * sizeof (*ar->a)); \ + ar->size = ar->maxSize = s; \ + ar->grow = 0; \ + ar->a = (typeof (ar->a)) (ar + 1); \ + ar; \ + }) + +/** Allocate a fixed-size array using the given allocator + + The allocated array is initilized to be ungrowable, and with both size + and maxSize set to the given size. + + \param array_type Expression acceptable by typeof for determining the + type of the array. + \param array_size The size of the array. + \param alloc Allocator taking (obj, size) where obj is allocator + specific data (eg, a memory pool). + \param obj Additional data for the allocator. + \hideinitializer +*/ +#define DARRAY_ALLOCFIXED_OBJ(array_type, array_size, alloc, obj) \ + ({ \ + __auto_type s = (array_size); \ + typeof (array_type) *ar = alloc ((obj), \ + sizeof(*ar) \ + + s * sizeof (*ar->a)); \ + ar->size = ar->maxSize = s; \ + ar->grow = 0; \ + ar->a = (typeof (ar->a)) (ar + 1); \ + ar; \ + }) + +/** Initialized the array. + + The array will be initialized to be empty but with grow set to the + specifed value. + + \param array *Address* of the array to be modified (ie, pointer to the + array struct instance, not the instance itself: use & for + static instances of the array struct). + \param growSize Number of elements by which the array is to grow when + required. + \hideinitializer +*/ +#define DARRAY_INIT(array, growSize) \ + do { \ + __auto_type ar = (array); \ + ar->size = ar->maxSize = 0; \ + ar->grow = (growSize); \ + ar->a = 0; \ + } while (0) + +/** Clear the array. + + If the array can grow, its backing will be freed and maxSize and a reset, + otherwise maxSize and a are left untouched. + + \param array *Address* of the array to be modified (ie, pointer to the + array struct instance, not the instance itself: use & for + static instances of the array struct). + \hideinitializer +*/ +#define DARRAY_CLEAR(array) \ + do { \ + __auto_type ar = (array); \ + ar->size = 0; \ + if (ar->grow) { \ + free (ar->a); \ + ar->maxSize = 0; \ + ar->a = 0; \ + } \ + } while (0) + +/** Set the size of the array. + + If the new size is greater than maxSize, and the array can grow (grow is + non-zero), then maxSize will be increased to the smallest multiple of grow + greater than or equal to size (ie, maxSize >= size, maxSize % grow == 0). + + Attempting to increase maxSize on an array that cannot grow is an error: + it is assumed that the array struct does not own the backing memory. + + \param array *Address* of the array to be modified (ie, pointer to the + array struct instance, not the instance itself: use & for + static instances of the array struct). + \param newSize The new size of the array: newly opened slots are + uninitialized, but old slots retain their values. + \hideinitializer +*/ +#define DARRAY_RESIZE(array, newSize) \ + do { \ + __auto_type ar_r = (array); \ + size_t ns = (newSize); \ + if (__builtin_expect (ns > ar_r->maxSize, 0)) { \ + if (__builtin_expect (!ar_r->grow, 0)) { \ + Sys_Error ("Attempt to grow fixed-size darray: %s:%d", \ + __FILE__, __LINE__); \ + } \ + ar_r->maxSize = ar_r->grow * ((ns + ar_r->grow - 1) / ar_r->grow);\ + ar_r->a = realloc (ar_r->a, ar_r->maxSize * sizeof (*ar_r->a)); \ + if (__builtin_expect (!ar_r->a, 0)) { \ + Sys_Error ("failed to realloc darray: %s:%d", \ + __FILE__, __LINE__); \ + } \ + } \ + ar_r->size = ns; \ + } while (0) + +/** Append a value to the end of the array. + + The array is grown by one and the value written to the newly opened slot. + + If the new array size is greater than maxSize and the array can be grown, + the array backing will be resized to the next multiple of grow. + + Attempting to grow an array that cannot grow is an error: it is assumed + that the array struct does not own the backing memory. + + \param array *Address* of the array to be modified (ie, pointer to the + array struct instance, not the instance itself: use & for + static instances of the array struct). + \param value The value to be appended to the array. Must be of a type + compatible with that used for creating the array struct. + \return The appended value: can be assigned to another compatible + value. + \hideinitializer +*/ +#define DARRAY_APPEND(array, value) \ + ({ \ + __auto_type ar = (array); \ + typeof(ar->a[0]) ob = (value); \ + size_t sz = ar->size; \ + DARRAY_RESIZE (ar, ar->size + 1); \ + ar->a[sz] = ob; \ + }) + +/** Open a hole in the array for bulk copying of data. + + The array is grown by the requested size, opening a hole at the specified + index. Values beyond the index are copied to just after the newly opened + hole. + + If the new array size is greater than maxSize and the array can be grown, + the array backing will be resized to the next multiple of grow. + + Attempting to grow an array that cannot grow is an error: it is assumed + that the array struct does not own the backing memory. + + \param array *Address* of the array to be modified (ie, pointer to the + array struct instance, not the instance itself: use & for + static instances of the array struct). + \param index The index at which the hole will begin. + \param space The sized of the hole to be opened, in array elements. + \return The *address* of the newly opened hole: can be passed to + memcpy and friends. + + memcpy (DARRAY_OPEN_AT(array, index, size), data, + size * sizeof (*data)); + \hideinitializer +*/ +#define DARRAY_OPEN_AT(array, index, space) \ + ({ \ + __auto_type ar_o = (array); \ + size_t po = (index); \ + size_t sp = (space); \ + if (__builtin_expect (po > ar_o->size, 0)) { \ + Sys_Error ("Attempt to insert elements outside darray: " \ + "%s:%d", __FILE__, __LINE__); \ + } \ + DARRAY_RESIZE (ar_o, ar_o->size + sp); \ + memmove (&ar_o->a[po + sp], &ar_o->a[po], \ + (ar_o->size - po - sp) * sizeof (*ar_o->a)); \ + &ar_o->a[po]; \ + }) + +/** Insert a value into the array at the specified index. + + The array is grown by one at the specified index and the value written + to the newly opened slot. Values beyond the index are copied to just + after the newly opened slot. + + If the new array size is greater than maxSize and the array can be grown, + the array backing will be resized to the next multiple of grow. + + Attempting to grow an array that cannot grow is an error: it is assumed + that the array struct does not own the backing memory. + + Attempting to insert a value beyond one past the end of the array is an + error (inserting at index = size is valid). + + \param array *Address* of the array to be modified (ie, pointer to the + array struct instance, not the instance itself: use & for + static instances of the array struct). + \param value The value to be inserted into the array. Must be of a type + compatible with that used for creating the array struct. + \param index The index at which the value will be inserted + \return The inserted value: can be assigned to another compatible + value. + \hideinitializer +*/ +#define DARRAY_INSERT_AT(array, value, index) \ + ({ \ + __auto_type ar = (array); \ + typeof(ar->a[0]) ob = (value); \ + *DARRAY_OPEN_AT (ar, index, 1) = ob; \ + }) + +/** Close a segment of an array. + + The values beyond the segment are copied to the beginning of the segment + and the array size reduced by the size of the segment. All but the first + one of the values previously in the segment are lost and gone forever. + + Attempting to close a segment that extends outside the array is an error. + + \param array *Address* of the array to be modified (ie, pointer to the + array struct instance, not the instance itself: use & for + static instances of the array struct). + \param index The index of the beginning of the segment. + \param count The number of values in the segment. + \return The single value at the beginning of the segment: can be + assigned to another compatible value. + \hideinitializer +*/ +#define DARRAY_CLOSE_AT(array, index, count) \ + ({ \ + __auto_type ar_c = (array); \ + size_t po = (index); \ + size_t co = (count); \ + if (__builtin_expect (po + co > ar_c->size \ + || po >= ar_c->size, 0)) { \ + Sys_Error ("Attempt to remove elements outside darray: " \ + "%s:%d", __FILE__, __LINE__); \ + } \ + __auto_type ob = ar_c->a[po]; \ + memmove (&ar_c->a[po], &ar_c->a[po + co], \ + (ar_c->size - po - co) * sizeof (ob)); \ + ar_c->size -= co; \ + ob; \ + }) + +/** Remove a value from an array at the specified index. + + The values beyond the index are moved down to fill the hole left by the + single value and the array size reduced by one. + + Attempting to remove a value from beyond the array is an error. + + \param array *Address* of the array to be modified (ie, pointer to the + array struct instance, not the instance itself: use & for + static instances of the array struct). + \param index The index of the value to be removed. + \return The removed value: can be assigned to another compatible + value. + \hideinitializer +*/ +#define DARRAY_REMOVE_AT(array, index) \ + ({ \ + __auto_type ar = (array); \ + DARRAY_CLOSE_AT (ar, index, 1); \ + }) + +/** Remove (pop) a value from the end of an array. + + The size of the array size reduced by one. + + Attempting to remove a value from an empty array is an error. + + \param array *Address* of the array to be modified (ie, pointer to the + array struct instance, not the instance itself: use & for + static instances of the array struct). + \return The removed value: can be assigned to another compatible + value. + \hideinitializer +*/ +#define DARRAY_REMOVE(array) \ + ({ \ + __auto_type ar = (array); \ + DARRAY_CLOSE_AT (ar, ar->size - 1, 1); \ + }) + +///@} + +#endif//__QF_darray_h diff --git a/include/QF/draw.h b/include/QF/draw.h index d8067a4c7..9943967a5 100644 --- a/include/QF/draw.h +++ b/include/QF/draw.h @@ -25,8 +25,8 @@ */ -#ifndef _DRAW_H -#define _DRAW_H +#ifndef __QF_draw_h +#define __QF_draw_h /** \defgroup video Video Sub-sytem */ @@ -37,10 +37,33 @@ /** \defgroup video_renderer_draw Generic draw functions \ingroup video_renderer */ -//@{ +///@{ #include "QF/wad.h" +/** Buffer for drawing text using quake conchars or the default 8x8 font. + + Characters are stored with the first character in the upper left, scanning + horizontally to the right. +*/ +typedef struct draw_charbuffer_s { + int width; ///< width in character cells + int height; ///< height in character cells + char *chars; ///< width * height characters + int cursx; ///< horizontal cursor position + int cursy; ///< vertical cursor position +} draw_charbuffer_t; + +draw_charbuffer_t *Draw_CreateBuffer (int width, int height); +void Draw_DestroyBuffer (draw_charbuffer_t *buffer); +void Draw_ClearBuffer (draw_charbuffer_t *buffer); +void Draw_ScrollBuffer (draw_charbuffer_t *buffer, int lines); +void Draw_CharBuffer (int x, int y, draw_charbuffer_t *buffer); +int Draw_PrintBuffer (draw_charbuffer_t *buffer, const char *str); + +void Draw_SetScale (int scale); +int Draw_MaxScale (void) __attribute__((pure)); + extern byte *draw_chars; /** Initialize the draw stuff. @@ -144,6 +167,17 @@ void Draw_TileClear (int x, int y, int w, int h); */ void Draw_Fill (int x, int y, int w, int h, int c); +/** Clear a line with a solid color. + \param x0 horizontal position of the line start point + \param y0 horizontal position of the line start point + \param x1 horizontal position of the line end point + \param y1 horizontal position of the line end point + \param c 8 bit color index. + + The color comes from the quake palette. +*/ +void Draw_Line (int x0, int y0, int x1, int y1, int c); + /** Draw a text box on the screen \param x horizontal location of the upper left corner of the box \param y vertical location of the upper left corner of the box @@ -160,19 +194,19 @@ void Draw_FadeScreen (void); /** Shift the screen colors. */ void Draw_BlendScreen (quat_t color); -//@} +///@} /** \defgroup video_renderer_draw_qpic QPic functions \ingroup video_renderer_draw */ -//@{ +///@{ /** Load a qpic from the filesystem. \param path path of the file within the quake filesystem \param alpha transparency level of the pic. \return pointer qpic data. \note Up to MAX_CACHED_PICS qpics can be loaded at a time this way */ -qpic_t *Draw_CachePic (const char *path, qboolean alpha); +qpic_t *Draw_CachePic (const char *path, bool alpha); /** Remove a qpic from the qpic cache. @@ -211,6 +245,15 @@ qpic_t *Draw_PicFromWad (const char *name); */ void Draw_Pic (int x, int y, qpic_t *pic); +/** Draw a qpic to the screen, scaled to fit the given width and height + \param x horizontal location of the upper left corner of the qpic + \param y vertical location of the upper left corner of the qpic + \param width horizontal size of the output pic + \param height vertical size of the output pic + \param pic qpic to draw +*/ +void Draw_FitPic (int x, int y, int width, int height, qpic_t *pic); + /** Draw a qpic to the screen \param x horizontal location of the upper left corner of the qpic \param y vertical location of the upper left corner of the qpic @@ -232,6 +275,11 @@ void Draw_Picf (float x, float y, qpic_t *pic); \param height vertical size of the sub-region to be drawn */ void Draw_SubPic(int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height); -//@} -#endif // _DRAW_H +struct font_s; +int Draw_AddFont (struct font_s *font); +void Draw_Glyph (int x, int y, int fontid, int glyphid, int c); + +///@} + +#endif//__QF_draw_h diff --git a/include/QF/dstring.h b/include/QF/dstring.h index 85ea7d61f..b3aa32c0a 100644 --- a/include/QF/dstring.h +++ b/include/QF/dstring.h @@ -25,13 +25,13 @@ */ -#ifndef __dstring_h -#define __dstring_h +#ifndef __QF_dstring_h +#define __QF_dstring_h /** \defgroup dstring Dynamic Strings \ingroup utils */ -//@{ +///@{ #include #include @@ -45,20 +45,20 @@ typedef struct dstring_mem_s { typedef struct dstring_s { dstring_mem_t *mem; - unsigned long int size, truesize; - char *str; + size_t size, truesize; + char *str; } dstring_t; extern dstring_mem_t dstring_default_mem; // General buffer functions -//@{ +///@{ /** Create a new dstring. size and truesize start at 0 and no string buffer is allocated. */ dstring_t *_dstring_new (dstring_mem_t *mem); dstring_t *dstring_new (void); -//@} +///@} /** Delete a dstring. Both the string buffer and dstring object are freed. */ void dstring_delete (dstring_t *dstr); @@ -72,21 +72,21 @@ void dstring_adjust (dstring_t *dstr); \param len the size of the hole to open. \return pointer to the beginning of the opened hole. */ -char *dstring_reserve (dstring_t *dstr, unsigned len); +char *dstring_reserve (dstring_t *dstr, size_t len); /** Copy len bytes from data into the dstring, replacing any existing data. */ -void dstring_copy (dstring_t *dstr, const char *data, unsigned int len); +void dstring_copy (dstring_t *dstr, const char *data, size_t len); /** Append len bytes from data onto the end of the dstring. */ -void dstring_append (dstring_t *dstr, const char *data, unsigned int len); +void dstring_append (dstring_t *dstr, const char *data, size_t len); /** Insert len bytes from data int the dstring at pos. If pos is past the end of the dstring, equivalent to dstring_append. */ -void dstring_insert (dstring_t *dstr, unsigned int pos, const char *data, - unsigned int len); +void dstring_insert (dstring_t *dstr, size_t pos, const char *data, + size_t len); /** Remove len bytes from the dstring starting at pos. */ -void dstring_snip (dstring_t *dstr, unsigned int pos, unsigned int len); +void dstring_snip (dstring_t *dstr, size_t pos, size_t len); /** Set the size of the dstring to 0 bytes. Does not free the string buffer anticipating reuse. */ @@ -94,8 +94,8 @@ void dstring_clear (dstring_t *dstr); /** Replace rlen bytes in dstring at pos with len bytes from data. Moves trailing bytes as needed. */ -void dstring_replace (dstring_t *dstr, unsigned int pos, unsigned int rlen, - const char *data, unsigned int len); +void dstring_replace (dstring_t *dstr, size_t pos, size_t rlen, + const char *data, size_t len); /** Delete the dstring object retaining the string buffer. The string buffer will be just big enough to hold the data. Does NOT ensure the string is null terminated. @@ -103,13 +103,13 @@ void dstring_replace (dstring_t *dstr, unsigned int pos, unsigned int rlen, char *dstring_freeze (dstring_t *dstr); // String-specific functions -//@{ +///@{ /** Allocate a new dstring pre-initialized as a null terminated string. size will be 1 and the first byte 0. */ dstring_t *_dstring_newstr (dstring_mem_t *mem); dstring_t *dstring_newstr (void); -//@} +///@} /** Create a new dstring from a string. Similar to strdup(). \param str the string to copy \return inititialized dstring @@ -123,7 +123,7 @@ dstring_t *dstring_strdup (const char *str); \return pointer to the current null terminator or beginning of the opened hole if there was no terminator. */ -char *dstring_reservestr (dstring_t *dstr, unsigned len); +char *dstring_reservestr (dstring_t *dstr, size_t len); /** Copy the null terminated string into the dstring. Replaces any existing data. The dstring does not have to be null terminated but will become so. @@ -133,7 +133,7 @@ void dstring_copystr (dstring_t *dstr, const char *str); existing data. The dstring does not have to be null terminated but will become so. */ -void dstring_copysubstr (dstring_t *dstr, const char *str, unsigned int len); +void dstring_copysubstr (dstring_t *dstr, const char *str, size_t len); /** Append the null terminated string to the end of the dstring. The dstring does not have to be null terminated but will become so. However, any embedded nulls will be treated as the end of the dstring. @@ -143,16 +143,16 @@ void dstring_appendstr (dstring_t *dstr, const char *str); The dstring does not have to be null terminated but will become so. However, any embedded nulls will be treated as the end of the dstring. */ -void dstring_appendsubstr (dstring_t *dstr, const char *str, unsigned int len); +void dstring_appendsubstr (dstring_t *dstr, const char *str, size_t len); /** Insert the null terminated string into the dstring at pos. The dstring is NOT forced to be null terminated. */ -void dstring_insertstr (dstring_t *dstr, unsigned int pos, const char *str); +void dstring_insertstr (dstring_t *dstr, size_t pos, const char *str); /** Insert up to len bytes from the string into the dstring at pos. The dstring is NOT forced to be null terminated. */ -void dstring_insertsubstr (dstring_t *dstr, unsigned int pos, const char *str, - unsigned int len); +void dstring_insertsubstr (dstring_t *dstr, size_t pos, const char *str, + size_t len); /** Clear the dstring to be equivalent to "". Does not resize the string buffer but size is set to 1. dstr = dstring_new (); dstring_clearstr (dstr); is exactly equivalent to @@ -160,21 +160,21 @@ void dstring_insertsubstr (dstring_t *dstr, unsigned int pos, const char *str, */ void dstring_clearstr (dstring_t *dstr); -//@{ +///@{ /** Formatted printing to dstrings. Existing data is replaced by the formatted string. */ -int dvsprintf (dstring_t *dstr, const char *fmt, va_list args); -int dsprintf (dstring_t *dstr, const char *fmt, ...) __attribute__((format(printf,2,3))); -//@} -//@{ +char *dvsprintf (dstring_t *dstr, const char *fmt, va_list args) __attribute__((format(PRINTF,2,0))); +char *dsprintf (dstring_t *dstr, const char *fmt, ...) __attribute__((format(PRINTF,2,3))); +///@} +///@{ /** Formatted printing to dstrings. Formatted string is appened to the dstring. Embedded nulls in the dstring are ignored. */ -int davsprintf (dstring_t *dstr, const char *fmt, va_list args); -int dasprintf (dstring_t *dstr, const char *fmt, ...) __attribute__((format(printf,2,3))); -//@} +char *davsprintf (dstring_t *dstr, const char *fmt, va_list args) __attribute__((format(PRINTF,2,0))); +char *dasprintf (dstring_t *dstr, const char *fmt, ...) __attribute__((format(PRINTF,2,3))); +///@} -//@} +///@} -#endif // __dstring_h +#endif//__QF_dstring_h diff --git a/include/QF/ecs.h b/include/QF/ecs.h new file mode 100644 index 000000000..e321f663e --- /dev/null +++ b/include/QF/ecs.h @@ -0,0 +1,159 @@ +/* + ecs.h + + Entity Component System + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/10/07 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_ecs_h +#define __QF_ecs_h + +#include +#include + +#include "QF/darray.h" +#include "QF/qtypes.h" +#include "QF/progs.h"//FIXME for PR_RESMAP + +#include "QF/ecs/component.h" +#include "QF/ecs/hierarchy.h" + +/** \defgroup ecs Entity Component System + \ingroup utils +*/ +///@{ + +#define ENT_GROW 1024 +#define COMP_GROW 128 +#define RANGE_GROW 32 +#define ENT_IDBITS 20 +#define nullent (~0u) + +typedef struct ecs_pool_s { + uint32_t *sparse; // indexed by entity id, holds index to dense/data + uint32_t *dense; // holds entity id + void *data; // component data + uint32_t count; // number of components/dense entity ids + uint32_t max_count; // current capacity for components/entity ids +} ecs_pool_t; + +typedef struct DARRAY_TYPE(component_t) componentset_t; + +typedef struct ecs_range_s { + uint32_t start; + uint32_t end; +} ecs_range_t; + +typedef struct ecs_subpool_s { + uint32_t *rangeids; + uint32_t *sorted; + uint32_t *ranges; + uint32_t next; + uint32_t available; + uint32_t num_ranges; + uint32_t max_ranges; +} ecs_subpool_t; + +typedef struct ecs_registry_s { + ecs_pool_t *comp_pools; + uint32_t *entities; + ecs_subpool_t *subpools; + uint32_t next; + uint32_t available; + uint32_t num_entities; + uint32_t max_entities; + componentset_t components; + PR_RESMAP (hierarchy_t) hierarchies;//FIXME find a better way + int locked; +} ecs_registry_t; + +/** Tie an ECS system to a registry. + + Keeps the registry using the system and the system's component base + together. +*/ +typedef struct ecs_system_s { + ecs_registry_t *reg; + uint32_t base; +} ecs_system_t; + +#include "QF/ecs/entity.h" + +#define ECSINLINE GNU89INLINE inline + +ecs_registry_t *ECS_NewRegistry (void); +void ECS_DelRegistry (ecs_registry_t *registry); +uint32_t ECS_RegisterComponents (ecs_registry_t *registry, + const component_t *components, + uint32_t count); +void ECS_CreateComponentPools (ecs_registry_t *registry); + + +#ifndef __compar_d_fn_t_defined +#define __compar_d_fn_t_defined +typedef int (*__compar_d_fn_t)(const void *, const void *, void *); +#endif +void ECS_SortComponentPool (ecs_registry_t *registry, uint32_t component, + __compar_d_fn_t cmp, void *arg); +void ECS_SortComponentPoolRange (ecs_registry_t *registry, uint32_t component, + ecs_range_t range, + __compar_d_fn_t cmp, void *arg); + +uint32_t ECS_NewEntity (ecs_registry_t *registry); +void ECS_DelEntity (ecs_registry_t *registry, uint32_t ent); +void ECS_RemoveEntities (ecs_registry_t *registry, uint32_t component); + +uint32_t ECS_NewSubpoolRange (ecs_registry_t *registry, uint32_t component); +void ECS_DelSubpoolRange (ecs_registry_t *registry, uint32_t component, + uint32_t id); +ECSINLINE ecs_range_t ECS_GetSubpoolRange (ecs_registry_t *registry, + uint32_t component, uint32_t id); + +#undef ECSINLINE +#ifndef IMPLEMENT_ECS_Funcs +#define ECSINLINE GNU89INLINE inline +#else +#define ECSINLINE VISIBLE +#endif + +ECSINLINE +ecs_range_t +ECS_GetSubpoolRange (ecs_registry_t *registry, uint32_t component, uint32_t id) +{ + ecs_subpool_t *subpool = ®istry->subpools[component]; + uint32_t ind = subpool->sorted[Ent_Index (id)]; + ecs_range_t range = { + .start = ind ? subpool->ranges[ind - 1] : 0, + .end = subpool->ranges[ind], + }; + return range; +} + +#undef ECSINLINE + +///@} + +#endif//__QF_ecs_h diff --git a/include/QF/ecs/component.h b/include/QF/ecs/component.h new file mode 100644 index 000000000..831418dac --- /dev/null +++ b/include/QF/ecs/component.h @@ -0,0 +1,154 @@ +/* + component.h + + ECS Component management + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/10/07 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_ecs_component_h +#define __QF_ecs_component_h + +#include +#include + +#include "QF/qtypes.h" +#include "QF/progs.h"//FIXME for PR_RESMAP + +/** \defgroup ecs_component Entity Component System components + \ingroup utils +*/ +///@{ + +struct ecs_registry_s; +typedef struct component_s { + size_t size; + void (*create) (void *); + void (*destroy) (void *); + // comp is the registry component id (base + system component id) + uint32_t (*rangeid) (struct ecs_registry_s *reg, uint32_t ent, + uint32_t comp); + const char *name; +} component_t; + +#define COMPINLINE GNU89INLINE inline + +COMPINLINE void Component_ResizeArray (const component_t *component, + void **array, uint32_t count); +COMPINLINE void *Component_MoveElements (const component_t *component, + void *array, uint32_t dstIndex, + uint32_t srcIndex, uint32_t count); +COMPINLINE void Component_SwapElements (const component_t *component, + void *a, void *b); +COMPINLINE void *Component_CopyElements (const component_t *component, + void *dstArray, uint32_t dstIndex, + const void *srcArray, + uint32_t srcIndex, + uint32_t count); +COMPINLINE void *Component_CreateElements (const component_t *component, + void *array, + uint32_t index, uint32_t count); +COMPINLINE void Component_DestroyElements (const component_t *component, + void *array, + uint32_t index, uint32_t count); + +#undef COMPINLINE +#ifndef IMPLEMENT_ECS_COMPONENT_Funcs +#define COMPINLINE GNU89INLINE inline +#else +#define COMPINLINE VISIBLE +#endif + +COMPINLINE void +Component_ResizeArray (const component_t *component, + void **array, uint32_t count) +{ + *array = realloc (*array, count * component->size); +} + +COMPINLINE void * +Component_MoveElements (const component_t *component, + void *array, uint32_t dstIndex, uint32_t srcIndex, + uint32_t count) +{ + __auto_type dst = (byte *) array + dstIndex * component->size; + __auto_type src = (byte *) array + srcIndex * component->size; + return memmove (dst, src, count * component->size); +} + +COMPINLINE void +Component_SwapElements (const component_t *component, void *a, void *b) +{ + size_t size = component->size; + byte tmp[size]; + memcpy (tmp, a, size); + memcpy (a, b, size); + memcpy (b, tmp, size); +} + +COMPINLINE void * +Component_CopyElements (const component_t *component, + void *dstArray, uint32_t dstIndex, + const void *srcArray, uint32_t srcIndex, + uint32_t count) +{ + __auto_type dst = (byte *) dstArray + dstIndex * component->size; + __auto_type src = (byte *) srcArray + srcIndex * component->size; + return memcpy (dst, src, count * component->size); +} + +COMPINLINE void * +Component_CreateElements (const component_t *component, void *array, + uint32_t index, uint32_t count) +{ + if (component->create) { + for (uint32_t i = index; count-- > 0; i++) { + __auto_type dst = (byte *) array + i * component->size; + component->create (dst); + } + } else { + __auto_type dst = (byte *) array + index * component->size; + memset (dst, 0, count * component->size); + } + return (byte *) array + index * component->size; +} + +COMPINLINE void +Component_DestroyElements (const component_t *component, void *array, + uint32_t index, uint32_t count) +{ + if (component->destroy) { + for (uint32_t i = index; count-- > 0; i++) { + __auto_type dst = (byte *) array + i * component->size; + component->destroy (dst); + } + } +} + +#undef COMPINLINE + +///@} + +#endif//__QF_ecs_component_h diff --git a/include/QF/ecs/entity.h b/include/QF/ecs/entity.h new file mode 100644 index 000000000..3ec271b4f --- /dev/null +++ b/include/QF/ecs/entity.h @@ -0,0 +1,128 @@ +/* + entity.h + + ECS entity management + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/10/07 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_ecs_entity_h +#define __QF_ecs_entity_h + +/** \defgroup ecs_entity Entity Component System Entities + \ingroup ecs +*/ +///@{ + +#define ENT_IDBITS 20 +#define nullent (~0u) + +#define ENTINLINE GNU89INLINE inline + +ENTINLINE int ECS_EntValid (uint32_t id, ecs_registry_t *reg); + +ENTINLINE uint32_t Ent_Index (uint32_t id); +ENTINLINE uint32_t Ent_Generation (uint32_t id); +ENTINLINE uint32_t Ent_NextGen (uint32_t id); + +ENTINLINE int Ent_HasComponent (uint32_t ent, uint32_t comp, + ecs_registry_t *reg); +ENTINLINE void *Ent_GetComponent (uint32_t ent, uint32_t comp, + ecs_registry_t *reg); +ENTINLINE void *Ent_SetComponent (uint32_t ent, uint32_t comp, + ecs_registry_t *registry, const void *data); + +#undef ENTINLINE +#ifndef IMPLEMENT_ECS_ENTITY_Funcs +#define ENTINLINE GNU89INLINE inline +#else +#define ENTINLINE VISIBLE +#endif + +ENTINLINE uint32_t +Ent_Index (uint32_t id) +{ + return id & ((1 << ENT_IDBITS) - 1); +} + +ENTINLINE uint32_t +Ent_Generation (uint32_t id) +{ + return id & ~((1 << ENT_IDBITS) - 1); +} + +ENTINLINE uint32_t +Ent_NextGen(uint32_t id) +{ + return id + (1 << ENT_IDBITS); +} + +ENTINLINE int +ECS_EntValid (uint32_t id, ecs_registry_t *reg) +{ + uint32_t ind = Ent_Index (id); + return ind < reg->num_entities && reg->entities[ind] == id; +} + +ENTINLINE int +Ent_HasComponent (uint32_t ent, uint32_t comp, ecs_registry_t *reg) +{ + ecs_pool_t *pool = ®->comp_pools[comp]; + uint32_t ind = pool->sparse[Ent_Index (ent)]; + return ind < pool->count && pool->dense[ind] == ent; +} + +ENTINLINE void * +Ent_GetComponent (uint32_t ent, uint32_t comp, ecs_registry_t *reg) +{ + const component_t *component = ®->components.a[comp]; + uint32_t ind = reg->comp_pools[comp].sparse[Ent_Index (ent)]; + byte *data = reg->comp_pools[comp].data; + return data + ind * component->size; +} + +void *Ent_AddComponent (uint32_t ent, uint32_t comp, ecs_registry_t *registry); +void Ent_RemoveComponent (uint32_t ent, uint32_t comp, + ecs_registry_t *registry); + +ENTINLINE void * +Ent_SetComponent (uint32_t ent, uint32_t comp, ecs_registry_t *registry, + const void *data) +{ + void *dst = Ent_AddComponent (ent, comp, registry); + if (data) { + return Component_CopyElements (®istry->components.a[comp], + dst, 0, data, 0, 1); + } else { + return Component_CreateElements (®istry->components.a[comp], + dst, 0, 1); + } +} + +#undef ENTINLINE + +///@} + +#endif//__QF_ecs_entity_h diff --git a/include/QF/ecs/hierarchy.h b/include/QF/ecs/hierarchy.h new file mode 100644 index 000000000..88e9c5665 --- /dev/null +++ b/include/QF/ecs/hierarchy.h @@ -0,0 +1,84 @@ +/* + hierarchy.h + + ECS Hierarchy management + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/02/26 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_ecs_hierarchy_h +#define __QF_ecs_hierarchy_h + +#include "QF/qtypes.h" + +/** \defgroup entity Hierarchy management + \ingroup utils +*/ +///@{ + +/** Descriptors for components attached to every entity in the hierarchy. +*/ +typedef struct hierarchy_type_s { + uint32_t num_components; + const struct component_s *components; +} hierarchy_type_t; + +typedef struct hierref_s { + struct hierarchy_s *hierarchy; + uint32_t index; ///< index in hierarchy +} hierref_t; + +typedef struct hierarchy_s { + uint32_t num_objects; + uint32_t max_objects; + uint32_t *ent; + uint32_t *childCount; + uint32_t *childIndex; + uint32_t *parentIndex; + const hierarchy_type_t *type; + void **components; + struct ecs_registry_s *reg; + uint32_t href_comp; +} hierarchy_t; + +hierarchy_t *Hierarchy_New (struct ecs_registry_s *reg, uint32_t href_comp, + const hierarchy_type_t *type, int createRoot); +void Hierarchy_Reserve (hierarchy_t *hierarchy, uint32_t count); +hierarchy_t *Hierarchy_Copy (struct ecs_registry_s *reg, uint32_t href_comp, + const hierarchy_t *src); +void Hierarchy_Delete (hierarchy_t *hierarchy); + +uint32_t Hierarchy_InsertHierarchy (hierarchy_t *dst, const hierarchy_t *src, + uint32_t dstParent, uint32_t srcRoot); +void Hierarchy_RemoveHierarchy (hierarchy_t *hierarchy, uint32_t index, + int delEntities); + +hierref_t Hierarchy_SetParent (hierarchy_t *dst, uint32_t dstParent, + hierarchy_t *src, uint32_t srcIndex); +void Hierref_DestroyComponent (void *href); + +///@} + +#endif//__QF_ecs_hierarchy_h diff --git a/include/QF/fbsearch.h b/include/QF/fbsearch.h new file mode 100644 index 000000000..19611f3c5 --- /dev/null +++ b/include/QF/fbsearch.h @@ -0,0 +1,49 @@ +/* + fbsearch.h + + Fuzzy bsearch + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_fbsearch_h +#define __QF_fbsearch_h + +#include + +#ifndef __compar_fn_t_defined +#define __compar_fn_t_defined +typedef int (*__compar_fn_t)(const void *, const void *); +#endif + +#ifndef __compar_d_fn_t_defined +#define __compar_d_fn_t_defined +typedef int (*__compar_d_fn_t)(const void *, const void *, void *); +#endif + +void *fbsearch (const void *key, const void *base, size_t nmemb, size_t size, + __compar_fn_t cmp); + +void *fbsearch_r (const void *key, const void *base, size_t nmemb, size_t size, + __compar_d_fn_t cmp, void *arg); + +#endif//__QF_fbsearch_h diff --git a/include/QF/gib.h b/include/QF/gib.h index aa97e3b05..da0247dfc 100644 --- a/include/QF/gib.h +++ b/include/QF/gib.h @@ -1,12 +1,9 @@ /* - #FILENAME# + gib.h - #DESCRIPTION# + GIB scripting language - Copyright (C) 2003 #AUTHOR# - - Author: #AUTHOR# - Date: #DATE# + Copyright (C) 2003 Brian Koropoff This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -28,15 +25,10 @@ */ -#ifndef __gib_h -#define __gib_h +#ifndef __QF_gib_h +#define __QF_gib_h -// Dependencies - -#include "QF/dstring.h" -#include "QF/cbuf.h" -#include "QF/hash.h" -#include "QF/llist.h" +#include "QF/qtypes.h" // Object interface @@ -56,11 +48,11 @@ typedef struct gib_slot_s { typedef struct gib_object_s { struct gib_class_s *class; - hashtab_t *methods; + struct hashtab_s *methods; void **data; unsigned long int handle, refs; - hashtab_t *signals, *vars; - llist_t *slots; + struct hashtab_s *signals, *vars; + struct llist_s *slots; const char *handstr; } gib_object_t; @@ -87,13 +79,13 @@ typedef void (*gib_obj_destructor) (void *data); typedef struct gib_class_s { const char *name; - hashtab_t *methods, *class_methods; + struct hashtab_s *methods, *class_methods; gib_obj_constructor construct, class_construct; gib_obj_destructor destruct; unsigned int depth; struct gib_object_s *classobj; struct gib_class_s *parent; - llist_t *children; + struct llist_s *children; } gib_class_t; typedef struct gib_methodtab_s { @@ -116,7 +108,7 @@ typedef struct gib_classdesc_s { (obj), (mesg))) void GIB_Class_Create (gib_classdesc_t *desc); -gib_object_t *GIB_Object_Create (const char *classname, qboolean classobj); +gib_object_t *GIB_Object_Create (const char *classname, bool classobj); void GIB_Object_Destroy (gib_object_t *obj); void GIB_Object_Incref (gib_object_t *obj); void GIB_Object_Decref (gib_object_t *obj); @@ -144,11 +136,13 @@ typedef struct gib_script_s { unsigned int refs; } gib_script_t; +struct cbuf_s; + typedef struct gib_buffer_data_s { struct gib_script_s *script; struct gib_tree_s *program, *ip; struct dstring_s *arg_composite; - qboolean waitret; + bool waitret; struct gib_sstack_s { struct gib_dsarray_s { struct dstring_s **dstrs; @@ -163,7 +157,7 @@ typedef struct gib_buffer_data_s { } reply; struct hashtab_s *locals; // Local variables struct hashtab_s *globals; // Current domain - void (*dnotify) (cbuf_t *cbuf, void *data); + void (*dnotify) (struct cbuf_s *cbuf, void *data); void *ddata; } gib_buffer_data_t; @@ -181,11 +175,11 @@ extern char * const gib_null_string; #define GIB_CanReturn() (GIB_DATA(cbuf_active)->waitret) -dstring_t *GIB_Return (const char *str); -void GIB_Error (const char *type, const char *fmt, ...); +struct dstring_s *GIB_Return (const char *str); +void GIB_Error (const char *type, const char *fmt, ...) __attribute__((format(PRINTF, 2, 3))); void GIB_Builtin_Add (const char *name, void (*func) (void)); void GIB_Builtin_Remove (const char *name); -qboolean GIB_Builtin_Exists (const char *name); +bool GIB_Builtin_Exists (const char *name); // Event interface @@ -199,21 +193,21 @@ void GIB_Event_Callback (gib_event_t *event, unsigned int argc, ...); // Interpreter interface (for creating GIB cbufs) -cbuf_interpreter_t *GIB_Interpreter (void); +struct cbuf_interpreter_s *GIB_Interpreter (void) __attribute__((const)); // Thread interface void GIB_Thread_Execute (void); -unsigned int GIB_Thread_Count (void); +unsigned int GIB_Thread_Count (void) __attribute__((pure)); // Init interface -void GIB_Init (qboolean sandbox); +void GIB_Init (bool sandbox); // Handle interface unsigned long int GIB_Handle_New (gib_object_t *data); void GIB_Handle_Free (unsigned long int num); -gib_object_t *GIB_Handle_Get (unsigned long int num); +gib_object_t *GIB_Handle_Get (unsigned long int num) __attribute__((pure)); -#endif +#endif//__QF_gib_h diff --git a/include/QF/hash.h b/include/QF/hash.h index 5cff9d115..df9773042 100644 --- a/include/QF/hash.h +++ b/include/QF/hash.h @@ -25,8 +25,8 @@ */ -#ifndef __hash_h -#define __hash_h +#ifndef __QF_hash_h +#define __QF_hash_h #include #include @@ -34,9 +34,12 @@ /** \defgroup hash Hash tables \ingroup utils */ -//@{ +///@{ typedef struct hashtab_s hashtab_t; +typedef struct hashctx_s hashctx_t; +typedef int (*hash_select_t) (void *ele, void *data); +typedef void (*hash_action_t) (void *ele, void *data); /** create a new hash table. \param tsize table size. larger values will give better distribution, but @@ -45,11 +48,18 @@ typedef struct hashtab_s hashtab_t; inserting or finding the element. First parameter is a pointer to the element from which to extract the key, the second is the user data pointer. - \param f a function to free the element. Only ever called from + \param f a function to free the element. Called from only Hash_FlushTable and Hash_DelTable. The first parameter is the element to be freed and the second is the user data pointer. \param ud user data pointer. set to whatever you want, it will be passed to the get key and free functions as the second parameter. + \param hctx Address of opaque pointer used for per-thread allocation of + internal memory. If null, a local static pointer will be used, + but the hash table will not be thread-safe unless all tables + created with a null \a hctx are used in only the one thread. + However, this applys only to updating a hash table; hash + tables that are not updated can be safely shared between + threads. \return pointer to the hash table (to be passed to the other functions) or 0 on error. @@ -59,7 +69,7 @@ typedef struct hashtab_s hashtab_t; previous ones until the later one is removed (Hash_Del). */ hashtab_t *Hash_NewTable (int tsize, const char *(*gk)(const void*,void*), - void (*f)(void*,void*), void *ud); + void (*f)(void*,void*), void *ud, hashctx_t **hctx); /** change the hash and compare functions used by the Hash_*Element functions. the default hash function just returns the address of the element, and the @@ -83,6 +93,8 @@ void Hash_SetHashCompare (hashtab_t *tab, uintptr_t (*gh)(const void*,void*), */ void Hash_DelTable (hashtab_t *tab); +void Hash_DelContext (hashctx_t *hashctx); + /** clean out all the entries from a hash table, starting over again. \param tab the table to be cleared */ @@ -109,6 +121,14 @@ int Hash_AddElement (hashtab_t *tab, void *ele); */ void *Hash_Find (hashtab_t *tab, const char *key); +/** find an element within a hash table. + \param tab the table to search + \param key the key string identifying the element being searched for + \param sz the maximum length of the key string + \return pointer to the element if found, otherwise 0. +*/ +void *Hash_nFind (hashtab_t *tab, const char *key, size_t sz); + /** find an element within a hash table. \param tab the table to search \param ele element with info identifying the element being searched for @@ -177,20 +197,29 @@ void Hash_Free (hashtab_t *tab, void *ele); this is the same function as used internally. */ -unsigned long Hash_String (const char *str); +uintptr_t Hash_String (const char *str) __attribute__((pure)); + +/** hash a string. + \param str the string to hash + \param sz the maximum length of the string + \return the hash value of the string. + + this is the same function as used internally. +*/ +uintptr_t Hash_nString (const char *str, size_t sz) __attribute__((pure)); /** hash a buffer. \param buf the buffer to hash \param len the size of the buffer \return the hash value of the string. */ -unsigned long Hash_Buffer (const void *buf, int len); +uintptr_t Hash_Buffer (const void *buf, int len) __attribute__((pure)); /** get the size of the table \param tab the table in question \return the number of elements in the table. */ -size_t Hash_NumElements (hashtab_t *tab); +size_t Hash_NumElements (hashtab_t *tab) __attribute__((pure)); /** list of all elements in the table. \param tab the table to list @@ -204,11 +233,37 @@ size_t Hash_NumElements (hashtab_t *tab); */ void **Hash_GetList (hashtab_t *tab); +/** list of all matching elements in the table. + \param tab the table to search + \param select function that tests for a match. The expected return value + is 0 for no match, non-zero for a match. + \param data context data passed to the \a select function + \return a null terminated list of element pointers for all matchin + elements in the table + \note it is the caller's responsibilty to free() the list. + + returned list is guaranteed to be in reverse order of insertion for + elements with the same key. ie, deleting items from the list in list order + will delete the correct items. +*/ +void **Hash_Select (hashtab_t *tab, hash_select_t select, void *data); + + +/** call a function for all elements in the table. + \param tab the table to search + \param action function to call for each elelemnt + \param data context data passed to the \a action function + + call order is guaranteed to be in reverse order of insertion for + elements with the same key +*/ +void Hash_ForEach (hashtab_t *tab, hash_action_t action, void *data); + /** dump statistics about the hash table \param tab the table to dump */ void Hash_Stats (hashtab_t *tab); -//@} +///@} -#endif // __hash_h +#endif//__QF_hash_h diff --git a/include/QF/heapsort.h b/include/QF/heapsort.h new file mode 100644 index 000000000..77ec0ef33 --- /dev/null +++ b/include/QF/heapsort.h @@ -0,0 +1,74 @@ +/* + heapsort.h + + Priority heap related functions + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_heapsort_h +#define __QF_heapsort_h + +#include + +#ifndef __compar_fn_t_defined +#define __compar_fn_t_defined +typedef int (*__compar_fn_t)(const void *, const void *); +#endif + +#ifndef __compar_d_fn_t_defined +#define __compar_d_fn_t_defined +typedef int (*__compar_d_fn_t)(const void *, const void *, void *); +#endif + +#ifndef __swap_d_fn_t_defined +#define __swap_d_fn_t_defined +typedef void (*__swap_d_fn_t)(void *, void *, void *); +#endif + +void heap_sink (void *base, size_t ind, size_t nmemb, size_t size, + __compar_fn_t cmp); +void heap_sink_r (void *base, size_t ind, size_t nmemb, size_t size, + __compar_d_fn_t cmp, void *arg); +void heap_sink_s (void *base, size_t ind, size_t nmemb, size_t size, + __compar_d_fn_t cmp, __swap_d_fn_t swp, void *arg); + +void heap_swim (void *base, size_t ind, size_t nmemb, size_t size, + __compar_fn_t cmp); +void heap_swim_r (void *base, size_t ind, size_t nmemb, size_t size, + __compar_d_fn_t cmp, void *arg); +void heap_swim_s (void *base, size_t ind, size_t nmemb, size_t size, + __compar_d_fn_t cmp, __swap_d_fn_t swp, void *arg); + +void heap_build (void *base, size_t nmemb, size_t size, __compar_fn_t cmp); +void heap_build_r (void *base, size_t nmemb, size_t size, + __compar_d_fn_t cmp, void *arg); +void heap_build_s (void *base, size_t nmemb, size_t size, + __compar_d_fn_t cmp, __swap_d_fn_t swp, void *arg); + +void heapsort (void *base, size_t nmemb, size_t size, __compar_fn_t cmp); +void heapsort_r (void *base, size_t nmemb, size_t size, + __compar_d_fn_t cmp, void *arg); +void heapsort_s (void *base, size_t nmemb, size_t size, + __compar_d_fn_t cmp, __swap_d_fn_t swp, void *arg); + +#endif//__QF_heapsort_h diff --git a/include/QF/idparse.h b/include/QF/idparse.h index 07b5c322e..e48cf30e6 100644 --- a/include/QF/idparse.h +++ b/include/QF/idparse.h @@ -33,7 +33,7 @@ /** \addtogroup cbuf */ -//@{ +///@{ extern const char *com_token; @@ -44,6 +44,6 @@ void COM_TokenizeString (const char *str, struct cbuf_args_s *args); extern struct cbuf_interpreter_s id_interp; -//@} +///@} #endif//__QF_idparse_h diff --git a/include/QF/image.h b/include/QF/image.h index 33c69c120..d93f65cc3 100644 --- a/include/QF/image.h +++ b/include/QF/image.h @@ -29,24 +29,45 @@ #ifndef __QF_image_h #define __QF_image_h -#include "QF/quakeio.h" +#include "QF/qtypes.h" + +typedef enum QFFormat { + tex_palette = 0, + tex_l = 0x1909, //GL_LUMINANCE + tex_a = 0x1906, //GL_ALPHA + tex_la = 2, + tex_rgb = 3, + tex_rgba = 4, + tex_frgba = 5, +} QFFormat; // could not use texture_t as that is used for models. typedef struct tex_s { - int width; - int height; - int format; - unsigned char *palette; // 0 = 32 bit, otherwise 8 - unsigned char data[4]; // variable length + int width; + int height; + QFFormat format; + union { + struct { + int loaded:1; // 0 if size info only, otherwise data loaded + int flipped:1; // 1 if first pixel is bottom instead of top + int bgr:1; // 1 if image is bgr (for tex_rgb) + }; + int flagbits; // for eazy zeroing + }; + const byte *palette; // 0 = 32 bit, otherwise 8 + byte *data; } tex_t; -#define tex_palette 0 -#define tex_l 0x1909 //GL_LUMINANCE -#define tex_a 0x1906 //GL_ALPHA -#define tex_la 2 -#define tex_rgb 3 -#define tex_rgba 4 +tex_t *LoadImage (const char *imageFile, int load); -tex_t *LoadImage (const char *imageFile); +size_t ImageSize (const tex_t *tex, int incl_struct) __attribute__((pure)); -#endif //__QF_image_h +typedef struct colcache_s colcache_t; + +colcache_t *ColorCache_New (void); +void ColorCache_Delete (colcache_t *cache); +void ColorCache_Shutdown (void); +byte ConvertColor (const byte *rgb, const byte *pal, colcache_t *cache); +tex_t *ConvertImage (const tex_t *tex, const byte *pal); + +#endif//__QF_image_h diff --git a/include/QF/in_event.h b/include/QF/in_event.h deleted file mode 100644 index dd1f148cf..000000000 --- a/include/QF/in_event.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - in_event.h - - input event handling - - Copyright (C) 2001 Bill Currie - - Author: Bill Currie - Date: 2001/8/9 - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ - -#ifndef __QF_in_event_h -#define __QF_in_event_h - -#include "QF/qtypes.h" -#include "QF/joystick.h" // needed for JOY_MAX_AXES - -typedef struct { - float x, y; - unsigned int buttons; -} IE_mouse_event_t; - -typedef struct { - int key_code; - qboolean pressed; -} IE_key_event_t; - -typedef struct { - float axis[JOY_MAX_AXES]; - unsigned int buttons; -} IE_joystick_event_t; - -typedef enum { - ie_none, - ie_gain_focus, - ie_lose_focus, - ie_mouse, - ie_key, - ie_joystick, -} IE_event_type; - -typedef struct { - IE_event_type type; - union { - IE_mouse_event_t mouse; - IE_key_event_t key; - IE_joystick_event_t joystick; - } e; -} IE_event_t; - -void IE_Init (void); -void IE_Init_Cvars (void); -void IE_Shutdown (void); -int IE_Send_Event (const IE_event_t *event); -int IE_Add_Handler (int (*event_handler)(const IE_event_t*)); -void IE_Remove_Handler (int handle); -void IE_Set_Focus (int handle); - -#endif//__QF_in_event_h diff --git a/include/QF/info.h b/include/QF/info.h index 518a35c23..9b460917b 100644 --- a/include/QF/info.h +++ b/include/QF/info.h @@ -25,13 +25,13 @@ */ -#ifndef _INFO_H -#define _INFO_H +#ifndef __QF_info_h +#define __QF_info_h /** \defgroup info Info Keys \ingroup utils */ -//@{ +///@{ #include // for size_t. sys/types.h SHOULD be used, but can't :(bc) #include @@ -47,10 +47,10 @@ typedef struct info_key_s { const char *value; } info_key_t; -qboolean Info_FilterForKey (const char *key, const char **filter_list); +bool Info_FilterForKey (const char *key, const char **filter_list) __attribute__((pure)); void Info_Print (info_t *info); -int Info_CurrentSize (info_t *info); +int Info_CurrentSize (info_t *info) __attribute__((pure)); info_key_t *Info_Key (info_t *info, const char *key); info_key_t **Info_KeyList (info_t *info); void Info_RemoveKey (info_t *info, const char *key); @@ -63,6 +63,6 @@ void Info_Destroy (info_t *info); char *Info_MakeString (info_t *info, int (*filter)(const char *)); void Info_AddKeys (info_t *info, info_t *keys); -//@} +///@} -#endif // _INFO_H +#endif//__QF_info_h diff --git a/include/QF/input.h b/include/QF/input.h index 8f0438071..e5ef61f9d 100644 --- a/include/QF/input.h +++ b/include/QF/input.h @@ -25,54 +25,121 @@ */ -#ifndef __QF_input_h_ -#define __QF_input_h_ +#ifndef __QF_input_h +#define __QF_input_h -#include "QF/keys.h" +/** \defgroup input Input Sub-system */ +///@{ -typedef struct { - vec3_t angles; - vec3_t position; -} viewdelta_t; +/// input axis info +typedef struct in_axisinfo_s { + int deviceid; + int axis; + int value; + int min; + int max; +} in_axisinfo_t; -extern viewdelta_t viewdelta; +/// input button info +typedef struct in_buttoninfo_s { + int deviceid; + int button; + int state; +} in_buttoninfo_t; -#define freelook (in_mlook.state & 1 || in_freelook->int_val) +#include "QF/input/binding.h" +#include "QF/input/imt.h" -struct cvar_s; +#ifndef __QFCC__ -void IN_Init (struct cbuf_s *cbuf); +struct qf_fd_set; + +/// driver interface +typedef struct in_driver_s { + void (*init_cvars) (void *data); + void (*init) (void *data); + void (*shutdown) (void *data); + + void (*set_device_event_data) (void *device, void *event_data, void *data); + void *(*get_device_event_data) (void *device, void *data); + + // The driver must provide either both or none of add_select and + // chec_select. + void (*add_select) (struct qf_fd_set *fdset, int *maxfd, void *data); + void (*check_select) (struct qf_fd_set *fdset, void *data); + // Generally musually exclusive with add_select/check_select + void (*process_events) (void *data); + + void (*clear_states) (void *data); + void (*grab_input) (void *data, int grab); + + void (*axis_info) (void *data, void *device, in_axisinfo_t *axes, + int *numaxes); + void (*button_info) (void *data, void *device, in_buttoninfo_t *buttons, + int *numbuttons); + // null means either invalid number or the name is not known + const char *(*get_axis_name) (void *data, void *device, int axis_num); + const char *(*get_button_name) (void *data, void *device, int button_num); + // -1 for invalid name + int (*get_axis_num) (void *data, void *device, const char *axis_name); + int (*get_button_num) (void *data, void *device, const char *button_name); + // null means invalid number + int (*get_axis_info) (void *data, void *device, int axis_num, + in_axisinfo_t *info); + int (*get_button_info) (void *data, void *device, int button_num, + in_buttoninfo_t *info); +} in_driver_t; + +/// device info +typedef struct in_device_s { + int driverid; + void *device; + const char *name; + const char *id; + void *event_data; +} in_device_t; + +int IN_RegisterDriver (in_driver_t *driver, void *data); +void IN_DriverData (int handlle, void *data); +void IN_Init (void); void IN_Init_Cvars (void); +struct plitem_s; +void IN_SaveConfig (struct plitem_s *config); +void IN_LoadConfig (struct plitem_s *config); -void IN_Shutdown (void); +int IN_AddDevice (int driver, void *device, const char *name, const char *id); +void IN_RemoveDevice (int devid); + +void IN_SendConnectedDevices (void); +int IN_FindDeviceId (const char *id) __attribute__((pure)); +const char *IN_GetDeviceName (int devid) __attribute__((pure)); +const char *IN_GetDeviceId (int devid) __attribute__((pure)); +void IN_SetDeviceEventData (int devid, void *data); +void *IN_GetDeviceEventData (int devid); +int IN_AxisInfo (int devid, in_axisinfo_t *axes, int *numaxes); +int IN_ButtonInfo (int devid, in_buttoninfo_t *button, int *numbuttons); +const char *IN_GetAxisName (int devid, int axis_num); +const char *IN_GetButtonName (int devid, int button_num); +int IN_GetAxisNumber (int devid, const char *axis_name); +int IN_GetButtonNumber (int devid, const char *button_name); +int IN_GetAxisInfo (int devid, int axis_num, in_axisinfo_t *info); +int IN_GetButtonInfo (int devid, int button_num, in_buttoninfo_t *info); void IN_ProcessEvents (void); -void IN_UpdateGrab (struct cvar_s *); +void IN_UpdateGrab (int grab); void IN_ClearStates (void); -void IN_Move (void); // FIXME: was cmduser_t? -// add additional movement on top of the keyboard move cmd +extern int in_grab; +extern float in_amp; +extern float in_pre_amp; +extern int in_mouse_accel; +extern int in_freelook; +extern int lookstrafe; -extern struct cvar_s *in_grab; -extern struct cvar_s *in_amp; -extern struct cvar_s *in_pre_amp; -extern struct cvar_s *m_filter; -extern struct cvar_s *in_mouse_accel; -extern struct cvar_s *in_freelook; -extern struct cvar_s *lookstrafe; +#endif -extern qboolean in_mouse_avail; -extern float in_mouse_x, in_mouse_y; +///@} -void IN_LL_Init_Cvars (void); -void IN_LL_Init (void); -void IN_LL_Shutdown (void); -void IN_LL_ProcessEvents (void); -void IN_LL_ClearStates (void); -void IN_LL_Grab_Input (int grab); - -extern kbutton_t in_strafe, in_klook, in_speed, in_mlook; - -#endif // __QF_input_h_ +#endif//__QF_input_h diff --git a/include/QF/input/binding.h b/include/QF/input/binding.h new file mode 100644 index 000000000..a81f8a05c --- /dev/null +++ b/include/QF/input/binding.h @@ -0,0 +1,379 @@ +/* + binding.h + + Input Mapping Table management + + Copyright (C) 2001 Zephaniah E. Hull + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_input_binding_h +#define __QF_input_binding_h + +#ifndef __QFCC__ +#include "QF/listener.h" +#include "QF/mathlib.h" +#endif + +/** \defgroup input_bindings Input Bindings + \ingroup input +*/ +///@{ + +/** Recipe for converting an axis to a floating point value. + + Absolute axes are converted to the 0..1 range for unbalanced axes, and + the -1..1 range for balanced axes, and then scaled. + + Relative axes are simply converted to floating point and scaled as they + have no fixed limits. + + Relative axes should have \a minzone and \a maxzone set to 0, or weird + things will happen. + + \a min and \a max normally come from the system. \a min and \a max being + equal indicates the axis is relative. + + \a deadzone applies only to balanced axes, thus it doubles as a flag + for balanced (>= 0) or unbalanced (< 0). However, relative axes are always + balanced and so \a deadzone < 0 is the same as 0 for relative axes. + + \a curve is applied after the input has been converted to a float, and the + 0..1 or -1..1 ranges for absolute axes. + + \a scale is applied after \a curve for absolute axes, but before \a curve + for relative axes. +*/ +typedef struct in_recipe_s { + int min; ///< Axis minimum value (from system) + int max; ///< Axis maximum value (from system) + int minzone; ///< Size of deadzone near axis minimum + int maxzone; ///< Size of deadzone near axis maximum + int deadzone; ///< Size of deadzone near axis center (balanced) + float curve; ///< Power factor for absolute axes + float scale; ///< Final scale factor +} in_recipe_t; + +typedef enum { + ina_set, ///< write the axis value to the destination + ina_accumulate, ///< add the axis value to the destination +} in_axis_mode; + +/** Logical axis. + + Logical axes are the inputs defined by the game on which axis inputs + (usually "physical" axes) can act. Depending on the mode, the physical + axis value is either written as-is, or added to the existing value. It is + the responsibility of the code using the axis to clear the value for + accumulated inputs. +*/ +typedef struct in_axis_s { + float value; ///< converted value of the axis + in_axis_mode mode; ///< method used for updating the destination + float abs_input; ///< input from an absolute axis (eg, joystick) + float rel_input; ///< input from a relative axis (eg, mouse) +#ifndef __QFCC__ + struct axis_listener_set_s *listeners; + const char *name; + const char *description; +#endif +} in_axis_t; + +#ifndef __QFCC__ +typedef struct axis_listener_set_s LISTENER_SET_TYPE (in_axis_t) + axis_listener_set_t; + +/** Function type for axis listeners. +*/ +typedef void (*axis_listener_t) (void *data, const in_axis_t *axis); +#endif + +/** Current state of the logical button. + + Captures the current state and any transitions during the last frame. + Not all combinations are valid (inb_edge_up|inb_down and inb_edge_down + (no inb_down) are not valid states), but inb_edge_up|inb_edge_down )with + or without inb_down) is valid as it represents a double transition during + the frame. +*/ +typedef enum { + inb_down = 1<<0, ///< button is held + inb_edge_down = 1<<1, ///< button pressed this frame + inb_edge_up = 1<<2, ///< button released this frame +} in_button_state; + +/** Logical button. + + Logical buttons are the inputs defined by the game on which button inputs + (usually "physical" buttons) can act. Up to two button inputs can be + bound to a logical button. The logical button acts as an or gate where + either input will put the logical button in the pressed state, and both + inputs must be inactive for the logical button to be released. +*/ +typedef struct in_button_s { + int down[2]; ///< button ids holding this button down + int state; ///< in_button_state +#ifndef __QFCC__ + struct button_listener_set_s *listeners; + const char *name; + const char *description; +#endif +} in_button_t; + +#ifndef __QFCC__ +typedef struct button_listener_set_s LISTENER_SET_TYPE (in_button_t) + button_listener_set_t; + +/** Function type for button listeners. +*/ +typedef void (*button_listener_t) (void *data, const in_button_t *button); + +typedef struct in_axisbinding_s { + in_recipe_t *recipe; + in_axis_t *axis; +} in_axisbinding_t; + +typedef enum { + inb_button, + inb_command, +} in_button_type; + +typedef struct in_buttonbinding_s { + in_button_type type; + union { + in_button_t *button; + char *command; + }; +} in_buttonbinding_t; + +/** Represent the button's activity in the last frame as a float. + + The detected activity is: + steady off (up) + steady on (down) + off to on (up to down) transition + on to off )down to up) transition + pulse on (off-on-off or up-down-up) + pulse off (on-off-on or down-up-down) + Any additional transitions are treated as a pulse appropriate for the + final state of the button. + + \param button Pointer to the button being tested. + \return Float value between 0 (off/up) and 1 (on/down) + \note The edge transitions are cleared, so for each frame, this + is a one-shot function (ie, it is NOT idempotent). +*/ +GNU89INLINE inline float IN_ButtonState (in_button_t *button); + +/** Test whether a button has been pressed in the last frame. + + Both steady-state on, and brief clicks are detected. + + \param button Pointer to the button being tested. + \return True if the button is currently held or was pulsed on + in the last frame. + \note The edge transitions are cleared, so for each frame, this + is a one-shot function (ie, it is NOT idempotent). +*/ +GNU89INLINE inline int IN_ButtonPressed (in_button_t *button); + +/** Test whether a button was released in the last frame. + + Valid only if the button is still released. A pulsed off does not + count as being released as the button is still held. + + \param button Pointer to the button being tested. + \return True if the button is currently released and the release + was in the last frame. + \note The edge transitions are cleared, so for each frame, this + is a one-shot function (ie, it is NOT idempotent). +*/ +GNU89INLINE inline int IN_ButtonReleased (in_button_t *button); + +/** Update the axis value based on its mode and clear its relative input. + + The absolute and relative inputs are separate because absolute inputs + usually get written when the input actually changes (and thus must not + be cleared each frame), while relative inputs indicate a per-frame delta + and thus must be cleared each frame. + + \param axis Pointer to the axis being updated. + \return The resulting output value of the axis. + \note The relative input (\a rel_input) is zeroed. +*/ +GNU89INLINE inline float IN_UpdateAxis (in_axis_t *axis); + +/** Update and clamp the axis value (see IN_UpdateAxis()) + + Like IN_UpdateAxis(), but clamps the final output to the specified range. + This is most useful for \a ina_accumulate axes, but can be used to ensure + \a ina_set axes never exceed a given range. + + The absolute and relative inputs are separate because absolute inputs + usually get written when the input actually changes (and thus must not + be cleared each frame), while relative inputs indicate a per-frame delta + and thus must be cleared each frame. + + \param axis Pointer to the axis being updated. + \param minval The minimum value to which the axis output will be clamped. + \param maxval The minimum value to which the axis output will be clamped. + \return The resulting output value of the axis. + \note The relative input (\a rel_input) is zeroed. The absolute + input is not affected by the clamping, only the output + \a value. +*/ +GNU89INLINE inline float IN_ClampAxis (in_axis_t *axis, + float minval, float maxval); + +#ifndef IMPLEMENT_INPUT_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +float +IN_ButtonState (in_button_t *button) +{ + static const float state_values[8] = { + // held down for the entire frame + [inb_down] = 1, + // released this frame + [inb_edge_up] = 0, // instant falloff + // pressed this frame + [inb_edge_down|inb_down] = 0.5, + // pressed and released this frame + [inb_edge_down|inb_edge_up] = 0.25, + // released and pressed this frame + [inb_edge_down|inb_edge_up|inb_down] = 0.75, + }; + int state = button->state; + button->state &= inb_down; // clear edges, preserve pressed + return state_values[state & (inb_down|inb_edge_down|inb_edge_up)]; +} + +#ifndef IMPLEMENT_INPUT_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +int +IN_ButtonPressed (in_button_t *button) +{ + int state = button->state; + button->state &= inb_down; // clear edges, preserve pressed + // catch even press and release that occurs between frames + return (state & (inb_down | inb_edge_down)) != 0; +} + +#ifndef IMPLEMENT_INPUT_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +int +IN_ButtonReleased (in_button_t *button) +{ + int state = button->state; + button->state &= inb_down; // clear edges, preserve pressed + // catch only full release (a pulsed on does count as a release) + return (state & (inb_down | inb_edge_up)) == inb_edge_up; +} + +#ifndef IMPLEMENT_INPUT_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +float +IN_UpdateAxis (in_axis_t *axis) +{ + float prev_value = axis->value; + switch (axis->mode) { + case ina_set: + axis->value = axis->abs_input + axis->rel_input; + break; + case ina_accumulate: + axis->value += axis->abs_input + axis->rel_input; + break; + } + axis->rel_input = 0; + if (axis->value != prev_value && axis->listeners) { + LISTENER_INVOKE (axis->listeners, axis); + } + return axis->value; +} + +#ifndef IMPLEMENT_INPUT_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +float +IN_ClampAxis (in_axis_t *axis, float minval, float maxval) +{ + float prev_value = axis->value; + switch (axis->mode) { + case ina_set: + axis->value = axis->abs_input + axis->rel_input; + break; + case ina_accumulate: + axis->value += axis->abs_input + axis->rel_input; + break; + } + axis->rel_input = 0; + axis->value = bound (minval, axis->value, maxval); + if (axis->value != prev_value && axis->listeners) { + LISTENER_INVOKE (axis->listeners, axis); + } + return axis->value; +} + +void IN_ButtonAction (in_button_t *buttin, int id, int pressed); + +int IN_RegisterButton (in_button_t *button); +int IN_RegisterAxis (in_axis_t *axis); +in_button_t *IN_FindButton (const char *name); +void IN_ButtonClearStates (void); +in_axis_t *IN_FindAxis (const char *name); +void IN_AxisClearStates (void); +void IN_ButtonAddListener (in_button_t *button, button_listener_t listener, + void *data); +void IN_ButtonRemoveListener (in_button_t *button, button_listener_t listener, + void *data); +void IN_AxisAddListener (in_axis_t *axis, axis_listener_t listener, + void *data); +void IN_AxisRemoveListener (in_axis_t *axis, axis_listener_t listener, + void *data); + +struct IE_event_s; +int IN_Binding_HandleEvent (const struct IE_event_s *ie_event); +void IN_Binding_Activate (void); +void IN_Binding_Init (void); +void IN_Binding_Shutdown (void); +struct plitem_s; +void IN_Binding_SaveConfig (struct plitem_s *config); +void IN_Binding_LoadConfig (struct plitem_s *config); + +#endif + +///@} + +#endif//__QF_input_binding_h diff --git a/include/QF/input/event.h b/include/QF/input/event.h new file mode 100644 index 000000000..698fd21bb --- /dev/null +++ b/include/QF/input/event.h @@ -0,0 +1,132 @@ +/* + in_event.h + + input event handling + + Copyright (C) 2001 Bill Currie + + Author: Bill Currie + Date: 2001/8/9 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_in_event_h +#define __QF_in_event_h + +#include "QF/qtypes.h" + +/** \defgroup input_events Input Events + \ingroup input +*/ +///@{ + +typedef struct { + int xpos, ypos; + int xlen, ylen; +} IE_app_window_event_t; + +typedef enum { + ies_shift = 1, + ies_capslock = 2, + ies_control = 4, + ies_alt = 8, +} IE_shift; + +typedef enum { + ie_mousedown, + ie_mouseup, + ie_mouseclick, + ie_mousemove, + ie_mouseauto, +} IE_mouse_type; + +typedef struct { + IE_mouse_type type; + unsigned shift; ///< ored bit pattern of IE_shift + int x, y; + unsigned buttons; + int click; +} IE_mouse_event_t; + +typedef struct { + int code; + int unicode; + unsigned shift; ///< ored bit pattern of IE_shift +} IE_key_event_t; + +typedef struct { + void *data; + int devid; + int axis; + int value; +} IE_axis_event_t; + +typedef struct { + void *data; + int devid; + int button; + int state; +} IE_button_event_t; + +typedef struct { + int devid; +} IE_device_event_t; + +#define IE_EVENT(event) ie_##event, +typedef enum { +#include "QF/input/event_names.h" + ie_event_count +} IE_event_type; + +extern const char *ie_event_names[]; + +#define IE_broadcast_events (0 \ + | (1 << ie_add_device) \ + | (1 << ie_remove_device) \ + | (1 << ie_app_gain_focus) \ + | (1 << ie_app_lose_focus) \ + | (1 << ie_app_window) \ + ) + +typedef struct IE_event_s { + IE_event_type type; + uint64_t when; + union { + IE_app_window_event_t app_window; + IE_mouse_event_t mouse; + IE_key_event_t key; + IE_axis_event_t axis; + IE_button_event_t button; + IE_device_event_t device; + }; +} IE_event_t; + +typedef int ie_handler_t (const IE_event_t *, void *data); + +int IE_Send_Event (const IE_event_t *event); +int IE_Add_Handler (ie_handler_t *event_handler, void *data); +void IE_Remove_Handler (int handle); +void IE_Set_Focus (int handle); +int IE_Get_Focus (void) __attribute__ ((pure)); + +///@} + +#endif//__QF_in_event_h diff --git a/include/QF/input/event_names.h b/include/QF/input/event_names.h new file mode 100644 index 000000000..9e2e5f150 --- /dev/null +++ b/include/QF/input/event_names.h @@ -0,0 +1,45 @@ +/* + event_names.h + + Input event enum names + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef IE_EVENT +#define IE_EVENT(event) +#endif + +IE_EVENT (none) +IE_EVENT (gain_focus) +IE_EVENT (lose_focus) +IE_EVENT (app_gain_focus) +IE_EVENT (app_lose_focus) +IE_EVENT (app_window) +IE_EVENT (add_device) +IE_EVENT (remove_device) +IE_EVENT (mouse) +IE_EVENT (key) +IE_EVENT (axis) +IE_EVENT (button) + +#undef IE_EVENT diff --git a/include/QF/input/imt.h b/include/QF/input/imt.h new file mode 100644 index 000000000..642d24f4a --- /dev/null +++ b/include/QF/input/imt.h @@ -0,0 +1,145 @@ +/* + imt.h + + Input Mapping Table management + + Copyright (C) 2001 Zephaniah E. Hull + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_input_imt_h +#define __QF_input_imt_h + +/** \defgroup input_imt Input Mapping Tables + \ingroup input +*/ +///@{ + +#ifndef __QFCC__ + +#include "QF/darray.h" +#include "QF/qtypes.h" + +#include "QF/input/binding.h" + +/** Input Mapping Table +*/ +typedef struct imt_s { + struct imt_s *next; ///< list of tables attached to key_dest + struct imt_s *chain; ///< fallback table if input not bound + const char *name; ///< for user interaction + int written; + struct DARRAY_TYPE (in_axisbinding_t *) axis_bindings; + struct DARRAY_TYPE (in_buttonbinding_t *) button_bindings; +} imt_t; + +typedef enum { + imti_button, + imti_cvar, +} imt_input_type; + +typedef struct imt_input_s { + imt_input_type type; + union { + struct in_button_s *button; + struct cvar_s *cvar; + }; +} imt_input_t; + +typedef struct imt_switcher_s { + struct imt_switcher_s *next; + const char *name; + int num_inputs; + imt_input_t *inputs; // one per input + imt_t **imts; // 2**(num_inputs) + struct in_context_s *context; +} imt_switcher_t; + +typedef struct in_context_s { + const char *name; + imt_t *imts; + imt_t **imt_tail; + imt_t *active_imt; + imt_t *default_imt; + imt_switcher_t *switchers; + imt_switcher_t **switcher_tail; + struct cbuf_s *cbuf; +} in_context_t; + +int IMT_GetAxisBlock (int num_axes); +int IMT_GetButtonBlock (int num_buttons); +int IMT_CreateContext (const char *name); +int IMT_GetContext (void) __attribute__ ((pure)); +void IMT_SetContext (int ctx); +void IMT_SetContextCbuf (int ctx, struct cbuf_s *cbuf); + +/** Find an Input Mapping Table by name. + + Searches through all contexts for the named imt. The search is case + insensitive. + + \param name The name of the imt to find. Case insensitive. + \return The named imt, or null if not found. +*/ +imt_t *IMT_FindIMT (const char *name) __attribute__ ((pure)); +imt_switcher_t *IMT_FindSwitcher (const char *name) __attribute__ ((pure)); + +/** Create a new imt and attach it to the specified context. + + The name of the new imt must be unique (case insensitive) across all + contexts. This is to simplify the in_bind command. + + If \a chain_imt_name is not null, then it species the fallback imt for when + the input is not bound in the new imt. It must be an already existing imt + in the specified context. This is to prevent loops and other weird + behavior. + + \param context The context to which the new imt will be attached. + \param imt_name The name for the new imt. Must be unique (case + insensitive). + \param chain_imt_name The name of the fallback imt if not null. Must + already exist on the specified context. +*/ +int IMT_CreateIMT (int context, const char *imt_name, + const char *chain_imt_name); +int IMT_CreateSwitcher (const char *switcher_name, + int context, imt_t *default_imt, + int num_inputs, const char **input_names); +void IMT_BindAxis (imt_t *imt, int axis_num, in_axis_t *axis, + const in_recipe_t *recipe); +void IMT_BindButton (imt_t *imt, int button, const char *binding); +bool IMT_ProcessAxis (int axis, int value); +bool IMT_ProcessButton (int button, int state); +void IMT_Init (void); +void IMT_Shutdown (void); +struct plitem_s; +void IMT_SaveConfig (struct plitem_s *config); +void IMT_SaveAxisConfig (struct plitem_s *axes, int axis_ind, int dev_axis); +void IMT_SaveButtonConfig (struct plitem_s *buttons, int button_ind, + int dev_button); +void IMT_LoadConfig (struct plitem_s *config); + +#endif + +///@} + +#endif//__QF_input_imt_h diff --git a/include/QF/iqm.h b/include/QF/iqm.h index fb6517c76..e5ab7ce85 100644 --- a/include/QF/iqm.h +++ b/include/QF/iqm.h @@ -1,5 +1,5 @@ -#ifndef __QF_iqm_h__ -#define __QF_iqm_h__ +#ifndef __QF_iqm_h +#define __QF_iqm_h #include "QF/qtypes.h" @@ -127,7 +127,7 @@ typedef struct { byte weights[4]; } iqmblend_t; -typedef struct { +typedef struct iqm_s { char *text; int num_meshes; iqmmesh *meshes; @@ -149,4 +149,4 @@ typedef struct { void *extra_data; } iqm_t; -#endif//__QF_iqm_h__ +#endif//__QF_iqm_h diff --git a/include/QF/joystick.h b/include/QF/joystick.h index 781f783a9..8ec845e20 100644 --- a/include/QF/joystick.h +++ b/include/QF/joystick.h @@ -25,8 +25,8 @@ */ -#ifndef __QF_joystick_h_ -#define __QF_joystick_h_ +#ifndef __QF_joystick_h +#define __QF_joystick_h #include #include "QF/quakeio.h" @@ -34,8 +34,8 @@ #define JOY_MAX_AXES 32 #define JOY_MAX_BUTTONS 64 -extern struct cvar_s *joy_device; // Joystick device name -extern struct cvar_s *joy_enable; // Joystick enabling flag +extern char *joy_device; +extern int joy_enable; struct joy_axis_button { float threshold; @@ -72,8 +72,8 @@ struct joy_axis { struct joy_axis_button *axis_buttons; // if axis button }; -extern qboolean joy_found; // Joystick present? -extern qboolean joy_active; // Joystick in use? +extern bool joy_found; // Joystick present? +extern bool joy_active; // Joystick in use? struct joy_button { int old; @@ -92,7 +92,7 @@ extern struct joy_button joy_buttons[JOY_MAX_BUTTONS]; frame. You should exit this function immediately if either joy_active or - joy_enable->int_val are zero. + joy_enable are zero. */ void JOY_Command (void); void joy_clear_axis (int i); @@ -103,7 +103,7 @@ void joy_clear_axis (int i); Use this function to process joystick movements to move the player around. You should exit this function immediately if either joy_active or - joy_enable->int_val are zero. + joy_enable are zero. */ void JOY_Move (void); @@ -144,14 +144,14 @@ void JOY_Close (void); void JOY_Read (void); -const char *JOY_GetOption_c (int i); -int JOY_GetOption_i (const char *c); +const char *JOY_GetOption_c (int i) __attribute__((pure)); +int JOY_GetOption_i (const char *c) __attribute__((pure)); -const char *JOY_GetDest_c (int i); -int JOY_GetDest_i (const char *c); +const char *JOY_GetDest_c (int i) __attribute__((pure)); +int JOY_GetDest_i (const char *c) __attribute__((pure)); int JOY_GetAxis_i (int dest, const char *c); void Joy_WriteBindings (QFile *f); -#endif // __QF_joystick_h_ +#endif//__QF_joystick_h diff --git a/include/QF/keys.h b/include/QF/keys.h index f79291dc0..4d0387467 100644 --- a/include/QF/keys.h +++ b/include/QF/keys.h @@ -26,22 +26,21 @@ */ -#ifndef _KEYS_H -#define _KEYS_H +#ifndef __QF_keys_h +#define __QF_keys_h #ifndef __QFCC__ # include "QF/qtypes.h" # include "QF/quakeio.h" #endif -/** \defgroup input Input Sub-system */ - /** \defgroup input_keybinding Key Binding Sub-system \ingroup input */ -//@{ +///@{ /// these are the key numbers that should be passed to Key_Event +/// FIXME clashes with actual inicode and OS keys typedef enum { /* The keyboard syms have been cleverly chosen to map to ASCII */ QFK_UNKNOWN = 0, @@ -227,77 +226,110 @@ typedef enum { /* Numeric keypad */ QFK_KP0 = 256, - QFK_KP1 = 257, - QFK_KP2 = 258, - QFK_KP3 = 259, - QFK_KP4 = 260, - QFK_KP5 = 261, - QFK_KP6 = 262, - QFK_KP7 = 263, - QFK_KP8 = 264, - QFK_KP9 = 265, - QFK_KP_PERIOD = 266, - QFK_KP_DIVIDE = 267, - QFK_KP_MULTIPLY = 268, - QFK_KP_MINUS = 269, - QFK_KP_PLUS = 270, - QFK_KP_ENTER = 271, - QFK_KP_EQUALS = 272, + QFK_KP1, + QFK_KP2, + QFK_KP3, + QFK_KP4, + QFK_KP5, + QFK_KP6, + QFK_KP7, + QFK_KP8, + QFK_KP9, + QFK_KP_PERIOD, + QFK_KP_DIVIDE, + QFK_KP_MULTIPLY, + QFK_KP_MINUS, + QFK_KP_PLUS, + QFK_KP_ENTER, + QFK_KP_EQUALS, /* Arrows + Home/End pad */ - QFK_UP = 273, - QFK_DOWN = 274, - QFK_RIGHT = 275, - QFK_LEFT = 276, - QFK_INSERT = 277, - QFK_HOME = 278, - QFK_END = 279, - QFK_PAGEUP = 280, - QFK_PAGEDOWN = 281, + QFK_UP, + QFK_DOWN, + QFK_RIGHT, + QFK_LEFT, + QFK_INSERT, + QFK_HOME, + QFK_END, + QFK_PAGEUP, + QFK_PAGEDOWN, /* Function keys */ - QFK_F1 = 282, - QFK_F2 = 283, - QFK_F3 = 284, - QFK_F4 = 285, - QFK_F5 = 286, - QFK_F6 = 287, - QFK_F7 = 288, - QFK_F8 = 289, - QFK_F9 = 290, - QFK_F10 = 291, - QFK_F11 = 292, - QFK_F12 = 293, - QFK_F13 = 294, - QFK_F14 = 295, - QFK_F15 = 296, + QFK_F1, + QFK_F2, + QFK_F3, + QFK_F4, + QFK_F5, + QFK_F6, + QFK_F7, + QFK_F8, + QFK_F9, + QFK_F10, + QFK_F11, + QFK_F12, + QFK_F13, + QFK_F14, + QFK_F15, + QFK_F16, + QFK_F17, + QFK_F18, + QFK_F19, + QFK_F20, + QFK_F21, + QFK_F22, + QFK_F23, + QFK_F24, + QFK_F25, + QFK_F26, + QFK_F27, + QFK_F28, + QFK_F29, + QFK_F30, + QFK_F31, + QFK_F32, + QFK_F33, + QFK_F34, + QFK_F35, + QFK_F36, + QFK_F37, + QFK_F38, + QFK_F39, + QFK_F40, + QFK_F41, + QFK_F42, + QFK_F43, + QFK_F44, + QFK_F45, + QFK_F46, + QFK_F47, + QFK_F48, /* Key state modifier keys */ - QFK_NUMLOCK = 300, - QFK_CAPSLOCK = 301, - QFK_SCROLLOCK = 302, - QFK_RSHIFT = 303, - QFK_LSHIFT = 304, - QFK_RCTRL = 305, - QFK_LCTRL = 306, - QFK_RALT = 307, - QFK_LALT = 308, - QFK_RMETA = 309, - QFK_LMETA = 310, - QFK_LSUPER = 311, /* Left "Windows" key */ - QFK_RSUPER = 312, /* Right "Windows" key */ - QFK_MODE = 313, /* "Alt Gr" key */ - QFK_COMPOSE = 314, /* Multi-key compose key */ + QFK_NUMLOCK, + QFK_CAPSLOCK, + QFK_SCROLLOCK, + QFK_RSHIFT, + QFK_LSHIFT, + QFK_RCTRL, + QFK_LCTRL, + QFK_RALT, + QFK_LALT, + QFK_RMETA, + QFK_LMETA, + QFK_LSUPER, /* Left "Windows" key */ + QFK_RSUPER, /* Right "Windows" key */ + QFK_MODE, /* "Alt Gr" key */ + QFK_COMPOSE, /* Multi-key compose key */ /* Miscellaneous function keys */ - QFK_HELP = 315, - QFK_PRINT = 316, - QFK_SYSREQ = 317, - QFK_BREAK = 318, - QFK_MENU = 319, - QFK_POWER = 320, /* Power Macintosh power key */ - QFK_EURO = 321, /* Some european keyboards */ - QFK_UNDO = 322, + QFK_HELP, + QFK_PRINT, + QFK_SYSREQ, + QFK_BREAK, + QFK_MENU, + QFK_POWER, /* Power Macintosh power key */ + QFK_EURO, /* Some european keyboards */ + QFK_UNDO, /* Japanese keys */ QFK_KANJI, /* Kanji, Kanji convert */ @@ -342,295 +374,10 @@ typedef enum { QFK_BACK, QFK_FORWARD, - /* Add any other keys here */ - -// -// mouse buttons generate virtual keys -// - QFM_BUTTON1, - QFM_BUTTON2, - QFM_BUTTON3, - QFM_WHEEL_UP, - QFM_WHEEL_DOWN, - QFM_BUTTON6, - QFM_BUTTON7, - QFM_BUTTON8, - QFM_BUTTON9, - QFM_BUTTON10, - QFM_BUTTON11, - QFM_BUTTON12, - QFM_BUTTON13, - QFM_BUTTON14, - QFM_BUTTON15, - QFM_BUTTON16, - QFM_BUTTON17, - QFM_BUTTON18, - QFM_BUTTON19, - QFM_BUTTON20, - QFM_BUTTON21, - QFM_BUTTON22, - QFM_BUTTON23, - QFM_BUTTON24, - QFM_BUTTON25, - QFM_BUTTON26, - QFM_BUTTON27, - QFM_BUTTON28, - QFM_BUTTON29, - QFM_BUTTON30, - QFM_BUTTON31, - QFM_BUTTON32, - -// -// joystick buttons -// - QFJ_BUTTON1, - QFJ_BUTTON2, - QFJ_BUTTON3, - QFJ_BUTTON4, - QFJ_BUTTON5, - QFJ_BUTTON6, - QFJ_BUTTON7, - QFJ_BUTTON8, - QFJ_BUTTON9, - QFJ_BUTTON10, - QFJ_BUTTON11, - QFJ_BUTTON12, - QFJ_BUTTON13, - QFJ_BUTTON14, - QFJ_BUTTON15, - QFJ_BUTTON16, - QFJ_BUTTON17, - QFJ_BUTTON18, - QFJ_BUTTON19, - QFJ_BUTTON20, - QFJ_BUTTON21, - QFJ_BUTTON22, - QFJ_BUTTON23, - QFJ_BUTTON24, - QFJ_BUTTON25, - QFJ_BUTTON26, - QFJ_BUTTON27, - QFJ_BUTTON28, - QFJ_BUTTON29, - QFJ_BUTTON30, - QFJ_BUTTON31, - QFJ_BUTTON32, - QFJ_BUTTON33, - QFJ_BUTTON34, - QFJ_BUTTON35, - QFJ_BUTTON36, - QFJ_BUTTON37, - QFJ_BUTTON38, - QFJ_BUTTON39, - QFJ_BUTTON40, - QFJ_BUTTON41, - QFJ_BUTTON42, - QFJ_BUTTON43, - QFJ_BUTTON44, - QFJ_BUTTON45, - QFJ_BUTTON46, - QFJ_BUTTON47, - QFJ_BUTTON48, - QFJ_BUTTON49, - QFJ_BUTTON50, - QFJ_BUTTON51, - QFJ_BUTTON52, - QFJ_BUTTON53, - QFJ_BUTTON54, - QFJ_BUTTON55, - QFJ_BUTTON56, - QFJ_BUTTON57, - QFJ_BUTTON58, - QFJ_BUTTON59, - QFJ_BUTTON60, - QFJ_BUTTON61, - QFJ_BUTTON62, - QFJ_BUTTON63, - QFJ_BUTTON64, - -// -// joystick axes (for button emulation without consuming buttons) -// - QFJ_AXIS1, - QFJ_AXIS2, - QFJ_AXIS3, - QFJ_AXIS4, - QFJ_AXIS5, - QFJ_AXIS6, - QFJ_AXIS7, - QFJ_AXIS8, - QFJ_AXIS9, - QFJ_AXIS10, - QFJ_AXIS11, - QFJ_AXIS12, - QFJ_AXIS13, - QFJ_AXIS14, - QFJ_AXIS15, - QFJ_AXIS16, - QFJ_AXIS17, - QFJ_AXIS18, - QFJ_AXIS19, - QFJ_AXIS20, - QFJ_AXIS21, - QFJ_AXIS22, - QFJ_AXIS23, - QFJ_AXIS24, - QFJ_AXIS25, - QFJ_AXIS26, - QFJ_AXIS27, - QFJ_AXIS28, - QFJ_AXIS29, - QFJ_AXIS30, - QFJ_AXIS31, - QFJ_AXIS32, - QFK_LAST } knum_t; -typedef enum { - key_unfocused, ///< engine has lost input focus - key_game, ///< Normal in-game key bindings - key_demo, ///< Demo playback key bindings - key_console, ///< Command console key bindings - key_message, ///< Message input line key bindings - key_menu, ///< Menu key bindings. - - key_last ///< enum size -} keydest_t; - #ifndef __QFCC__ -typedef struct { - int down[2]; // key nums holding it down - int state; // low bit is down state -} kbutton_t; - -extern knum_t key_togglemenu; -extern knum_t key_toggleconsole; - -typedef struct keybind_s { - char *str; -} keybind_t; - -/** Input Mapping Table -*/ -typedef struct imt_s { - struct imt_s *next; ///< list of tables attached to key_dest - struct imt_s *chain; ///< fallback table if key not bound - const char *name; ///< for user interaction - keybind_t bindings[QFK_LAST]; - int written; ///< avoid duplicate config file writes -} imt_t; - -/** Chain of input mapping tables ascociated with a keydest sub-system (game, - menu, etc). -*/ -typedef struct keytarget_s { - imt_t *imts; ///< list of tables attached to this target - imt_t *active; ///< currently active table in this target -} keytarget_t; - -extern int keydown[QFK_LAST]; - -struct cbuf_s; - -void Key_Init (struct cbuf_s *cb); -void Key_Init_Cvars (void); - -/** Find an Input Mapping Table by name. - - Searches through all keydest targets for the named imt. The search is case - insensitive. - - \param imt_name The name of the imt to find. Case insensitive. - \return The named imt, or null if not found. -*/ -imt_t *Key_FindIMT (const char *imt_name); - -/** Create a new imt and attach it to the specified keydest target. - - The name of the new imt must be unique (case insensitive) across all - keydest targets. This is to simplify the in_bind command. - - If \a chain_imt_name is not null, then it species the fallback imt for when - the key is not bound in the new imt. It must be an already existing imt in - the specified keydest target. This is to prevent loops and other weird - behavior. - - \param kd The keydest target to which the new imt will be attached. - \param imt_name The name for the new imt. Must be unique (case - insensitive). - \param chain_imt_name The name of the fallback imt if not null. Must - already exist on the specified keydest target. -*/ -void Key_CreateIMT (keydest_t kd, const char *imt_name, - const char *chain_imt_name); - -/** Handle a key press/release event. - - \param key The key that was pressed or released for this event. - \param unicode The unicode value of the key. - \param down True if a press event, false if a release event. -*/ -void Key_Event (knum_t key, short unicode, qboolean down); - -/** Handle loss or gain of input focus (usually in windowed enviroments). - - Sets the keydest target to key_unfocuses when input focus is lost. - - Triggers keydest callbacks. - - \bug Always sets the target to key_game when focus is gained. - - \param gain True if focus is gained, false if focus is lost. -*/ -void Key_FocusEvent (int gain); - -void Key_WriteBindings (QFile *f); - -/** Force all key states to unpressed. - - Sends a key release event for any keys that are seen as down. -*/ -void Key_ClearStates (void); - -/** Return a key binding in the specified input mapping table. - - \param imt The input mapping table from which to get the binding. - \param key The key for which to get the binding. - \return The command string bound to the key, or null if unbound. -*/ -const char *Key_GetBinding (imt_t *imt, knum_t key); - -/** Bind a command string to a key in the specified input mapping table. - - Only one command string can be bound to a key, but the command string may - contain multiple commands. - - \param imt The input mapping table in which the key will be bound. - \param keynum The key to which the command string will be bound. - \param binding The command string that will be bound. -*/ -void Key_SetBinding (imt_t *imt, knum_t keynum, const char *binding); - -/** Set the current keydest target. - - Triggers keydest callbacks. - - \param kd The keydest target to make current. -*/ -void Key_SetKeyDest(keydest_t kd); - -/** keydest callback signature. - - \param kd The new current keydest target. -*/ -typedef void keydest_callback_t (keydest_t kd); - -/** Add a callback for when the keydest target changes. - - \param callback The callback to be added. -*/ -void Key_KeydestCallback (keydest_callback_t *callback); /** Get the string representation of a key. @@ -639,7 +386,7 @@ void Key_KeydestCallback (keydest_callback_t *callback); \param keynum The key for which to get the string. \return The string representation of the key. */ -const char *Key_KeynumToString (knum_t keynum); +const char *Key_KeynumToString (knum_t keynum) __attribute__((pure)); /** Get the keynum for the named key. @@ -650,15 +397,19 @@ const char *Key_KeynumToString (knum_t keynum); \param str The name of the key. \return The named key if valid, otherwise -1 */ -int Key_StringToKeynum (const char *str); +int Key_StringToKeynum (const char *str) __attribute__((pure)); struct progs_s; +//FIXME location /** Add the Key builtins to the specified progs instance. */ -void Key_Progs_Init (struct progs_s *pr); +void RUA_Key_Init (struct progs_s *pr); #endif -//@} +//FIXME location +void GIB_Key_Init (void); -#endif // _KEYS_H +///@} + +#endif//__QF_keys_h diff --git a/include/QF/link.h b/include/QF/link.h index de29bb454..5b6fdcf8c 100644 --- a/include/QF/link.h +++ b/include/QF/link.h @@ -25,8 +25,8 @@ */ -#ifndef _LINK_H -#define _LINK_H +#ifndef __QF_link_h +#define __QF_link_h // (type *)STRUCT_FROM_LINK(link_t *link, type, member) // ent = STRUCT_FROM_LINK(link,entity_t,order) @@ -43,4 +43,4 @@ void RemoveLink (link_t *l); void InsertLinkBefore (link_t *l, link_t *before); void InsertLinkAfter (link_t *l, link_t *after); -#endif // _LINK_H +#endif//__QF_link_h diff --git a/include/QF/listener.h b/include/QF/listener.h new file mode 100644 index 000000000..553c79a3e --- /dev/null +++ b/include/QF/listener.h @@ -0,0 +1,158 @@ +/* + listener.h + + Listener objects + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/11/25 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_listener_h +#define __QF_listener_h + +#include "QF/darray.h" + +/** \defgroup listener Listeners + \ingroup utils + + Listener objects +*/ +///@{ + +/** The structure defs for a listener set for an object of the given type. + + This is just the defs of a struct delcaration: it is useless on its own. + The intended usage is something like: + + typedef struct obj_listenerset_s LISTENER_SET_TYPE(obj_t) + obj_listenerset_t; + + This allows full flexibility in just how the actual type is used. + + The \a lfunc field is the function that will be called whenever the + listener is invoked. \a ldata is an aribtrary pointer that is passed to + the function as its \a data parameter. The function's \a obj parameter + is the \a obj parameter passed to LISTENER_INVOKE(), and is intended to + be the object being listened to. + + There can be any number of \a lfunc, \a ldata pairs, but they should be + unique, though this is not enforced. + + \param type The type of the listened objects. A pointer to an object + being listend to is passed to LISTENER_INVOKE() and that + pointer is passed on to each listener function in the + listener set. + \hideinitializer +*/ +#define LISTENER_SET_TYPE(type) \ + DARRAY_TYPE(struct { \ + void (*lfunc) (void *data, const type *obj); \ + void *ldata; \ + }) + +#define LISTENER_SET_STATIC_INIT(g) DARRAY_STATIC_INIT(g) + +/** Initialize the listener set. + + The set is initialized to be emtpy. + + \param lset The *address* of the listener set being initialized (ie, + a pointer to the listener set). + \param growSice Amount by which the underlying array will grow. + \hideinitializer +*/ +#define LISTENER_SET_INIT(lset, growSice) DARRAY_INIT(lset, growSice) + +/** Add a listener function/data par to the listener set. + + The function's first parameter is the \a data pointer in the \a func + \a data pair. The function's second parameter is the \a obj parameter + of LISTENER_INVOKE(). Thus each added listener function will be called + with its associated data pointer and the \a obj parameter from + LISTENER_INVOKE(). + + Each pair should be unique in order to avoid double-action and so + listeners can be removed properly. + + \param lset The *address* of the listener set being modified (ie, + a pointer to the listener set). + \param func The function to be called when the listener is invoked. + \param data Arbitrary pointer passed on to the function as its first + parameter when when the listener is invoked. + \hideinitializer +*/ +#define LISTENER_ADD(lset, func, data) \ + do { \ + __auto_type ls = (lset); \ + typeof(ls->a[0]) l = {(func), (data)}; \ + DARRAY_APPEND (ls, l); \ + } while (0) + +/** Remove a listener function/data pair from the listener set. + + Each individual listener is identifed by its \a func \a data pair. Only + the first instance of a pair is removed, thus only unique pairs should be + added. + + \param lset The *address* of the listener set being modified (ie, + a pointer to the listener set). + \param func The listener function being removed. + \param data The function's associated data pointer. + \hideinitializer +*/ +#define LISTENER_REMOVE(lset, func, data) \ + do { \ + __auto_type ls = (lset); \ + typeof(ls->a[0]) l = {(func), (data)}; \ + for (size_t i = 0; i < ls->size; i++) { \ + if (ls->a[i].lfunc == l.lfunc && ls->a[i].ldata == l.ldata) { \ + DARRAY_REMOVE_AT (ls, i); \ + break; \ + } \ + } \ + } while (0) + +/** Call all listener functions in the listener set. + + Each listener function is passed its data pointer as the first parameter + and \a obj as the second parameter. + + \param lset The *address* of the listener set being invoked (ie, + a pointer to the listener set). + \param obj Pointer passed to each listener function as its second + parameter. + \hideinitializer +*/ +#define LISTENER_INVOKE(lset, obj) \ + do { \ + __auto_type ls = (lset); \ + __auto_type o = (obj); \ + for (size_t i = 0; i < ls->size; i++) { \ + ls->a[i].lfunc (ls->a[i].ldata, o); \ + } \ + } while (0) + +///@} + +#endif//__QF_listener_h diff --git a/include/QF/llist.h b/include/QF/llist.h index 7253618b2..922dc349b 100644 --- a/include/QF/llist.h +++ b/include/QF/llist.h @@ -25,41 +25,91 @@ */ -#ifndef _LLIST_H -#define _LLIST_H +#ifndef __QF_llist_h +#define __QF_llist_h #include "QF/qtypes.h" +/** \defgroup llist Linked lists + \ingroup utils +*/ +//@{ + typedef struct llist_node_s { - struct llist_s *parent; - struct llist_node_s *prev, *next; - void *data; + struct llist_s *parent; ///< The list owning this node. + struct llist_node_s *prev; ///< The previous node in the list, or null. + struct llist_node_s *next; ///< The flowing node in the list, or null. + void *data; ///< The actual list item. } llist_node_t; typedef struct llist_s { - struct llist_node_s *start, *end, *iter; + struct llist_node_s *start; ///< The first node in the list, or null. + struct llist_node_s *end; ///< The last node in the list, or null. + struct llist_node_s *iter; + /// Function called when deleting a list item. + /// \param element The item being deleted. + /// \param userdata Pointer to user data supplied to llist_new(). void (*freedata)(void *element, void *userdata); - qboolean (*cmpdata)(const void *element, const void *comparison, void *userdata); + bool (*cmpdata)(const void *element, const void *comparison, + void *userdata); void *userdata; } llist_t; -typedef qboolean (*llist_iterator_t)(void *element, llist_node_t *node); +typedef bool (*llist_iterator_t)(void *element, llist_node_t *node); #define LLIST_ICAST(x) (llist_iterator_t)(x) #define LLIST_DATA(node, type) ((type *)((node)->data)) -llist_t *llist_new (void (*freedata)(void *element, void *userdata), qboolean (*cmpdata)(const void *element, const void *comparison, void *userdata), void *userdata); +/** Create a new, empty, linked list. + + \param freedata Function to call when deleting a list item. + \param cmpdata Function to call to compare two list items. It must + return true when the items are the same and false when + they differ. + \param userdata User data pointer. Set to whatever you want, it will be + passed to the \a freedata and \a cmpdata functions as + their final parameter. + \return Pointer to the list's control structure, which is to be + passed to the other functions accessing the list. +*/ +llist_t *llist_new (void (*freedata)(void *element, void *userdata), + bool (*cmpdata)(const void *element, + const void *comparison, + void *userdata), + void *userdata); + +/** Empty a linked list. + All of the items in the list will be deleted via the list's \a freedata + function and the list will become empty. + + \param list Pointer to the list's control structure created by + llist_new(). May be null, in which case no operation is + performed. +*/ void llist_flush (llist_t *list); + +/** Delete a linked list. + All of the items in the list will be deleted via the list's \a freedata + function and the list will be destroyed. Do not attempt to use the list + pointer after destroying the list. + + \param list Pointer to the list's control structure created by + llist_new(). May be null, in which case no operation is + performed. +*/ void llist_delete (llist_t *list); llist_node_t *llist_append (llist_t *list, void *element); llist_node_t *llist_prefix (llist_t *list, void *element); -llist_node_t *llist_getnode (llist_t *list, void *element); +llist_node_t *llist_getnode (llist_t *list, void *element) __attribute__((pure)); llist_node_t *llist_insertafter (llist_node_t *ref, void *element); llist_node_t *llist_insertbefore (llist_node_t *ref, void *element); void *llist_remove (llist_node_t *ref); -unsigned int llist_size (llist_t *llist); +unsigned int llist_size (llist_t *llist) __attribute__((pure)); void llist_iterate (llist_t *list, llist_iterator_t iterate); void *llist_find (llist_t *list, void *comparison); llist_node_t *llist_findnode (llist_t *list, void *comparison); void *llist_createarray (llist_t *list, size_t esize); -#endif + +//@} + +#endif//__QF_llist_h diff --git a/include/QF/math/bitop.h b/include/QF/math/bitop.h new file mode 100644 index 000000000..ee00ac6ca --- /dev/null +++ b/include/QF/math/bitop.h @@ -0,0 +1,73 @@ +/* + bitop.h + + bit-op functions + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/1/23 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_math_bitop_h +#define __QF_math_bitop_h + +/** \defgroup mathlib_bitop Bit-op functions + \ingroup utils +*/ +///@{ + +#include "QF/qtypes.h" + +#define BITOP_RUP1__(x) ( (x) | ( (x) >> 1)) +#define BITOP_RUP2__(x) (BITOP_RUP1__(x) | (BITOP_RUP1__(x) >> 2)) +#define BITOP_RUP4__(x) (BITOP_RUP2__(x) | (BITOP_RUP2__(x) >> 4)) +#define BITOP_RUP8__(x) (BITOP_RUP4__(x) | (BITOP_RUP4__(x) >> 8)) +#define BITOP_RUP16__(x) (BITOP_RUP8__(x) | (BITOP_RUP8__(x) >> 16)) +/** Round x up to the next power of two. + + Rounds x up to the next power of two leaving exact powers of two + untouched. + + \param x The value to round + \return The next higher power of two or x if it already is a power + of two. +*/ +#define BITOP_RUP(x) (BITOP_RUP16__((uint32_t)(x) - 1) + 1) + +#define BITOP_LOG2__(x) (((((x) & 0xffff0000) != 0) << 4) \ + |((((x) & 0xff00ff00) != 0) << 3) \ + |((((x) & 0xf0f0f0f0) != 0) << 2) \ + |((((x) & 0xcccccccc) != 0) << 1) \ + |((((x) & 0xaaaaaaaa) != 0) << 0)) +/** Log base 2 rounded up. + + Finds the base 2 logarithm of x rounded up (ceil(log2(x))). + + \param x The value for which to find the base 2 logarithm. + \return Log base 2 of x, rounded up (2 -> 1, 3 -> 2, 4 -> 2) +*/ +#define BITOP_LOG2(x) BITOP_LOG2__(BITOP_RUP(x)) + +///@} + +#endif // __QF_math_bitop_h diff --git a/include/QF/math/dual.h b/include/QF/math/dual.h index eba9b8bf0..34c58a984 100644 --- a/include/QF/math/dual.h +++ b/include/QF/math/dual.h @@ -34,7 +34,7 @@ /** \defgroup mathlib_dual Dual and dual quaternion functions \ingroup utils */ -//@{ +///@{ #include "QF/qtypes.h" @@ -189,6 +189,6 @@ } while (0) #define DualQuatExpand(dq) QuatExpand ((dq).q0.q), QuatExpand ((dq).qe.q) -//@} +///@} #endif // __QF_math_dual_h diff --git a/include/QF/math/half.h b/include/QF/math/half.h index 61baf8f09..bc9133fc6 100644 --- a/include/QF/math/half.h +++ b/include/QF/math/half.h @@ -34,14 +34,14 @@ /** \defgroup mathlib_half Half-float functions \ingroup utils */ -//@{ +///@{ #include "QF/qtypes.h" -int16_t FloatToHalf (float x); -float HalfToFloat (int16_t x); +int16_t FloatToHalf (float x) __attribute__((const)); +float HalfToFloat (int16_t x) __attribute__((const)); -//@} +///@} #endif // __QF_math_half_h diff --git a/include/QF/math/matrix3.h b/include/QF/math/matrix3.h index 7a54938dd..978ca1868 100644 --- a/include/QF/math/matrix3.h +++ b/include/QF/math/matrix3.h @@ -34,7 +34,7 @@ /** \defgroup mathlib_matrix3 3x3 matrix functions \ingroup utils */ -//@{ +///@{ #include "QF/qtypes.h" @@ -101,7 +101,7 @@ void Mat3Init (const quat_t rot, const vec3_t scale, mat3_t mat); void Mat3Transpose (const mat3_t a, mat3_t b); -vec_t Mat3Determinant (const mat3_t m); +vec_t Mat3Determinant (const mat3_t m) __attribute__((pure)); int Mat3Inverse (const mat3_t a, mat3_t b); void Mat3Mult (const mat3_t a, const mat3_t b, mat3_t c); void Mat3MultVec (const mat3_t a, const vec3_t b, vec3_t c); @@ -116,8 +116,8 @@ void Mat3SymEigen (const mat3_t m, vec3_t e); VectorShear (shear, v, v); VectorCompMult (scale, v, v); */ -int Mat3Decompose (const mat4_t mat, quat_t rot, vec3_t shear, vec3_t scale); +int Mat3Decompose (const mat3_t mat, quat_t rot, vec3_t shear, vec3_t scale); -//@} +///@} #endif // __QF_math_matrix3_h diff --git a/include/QF/math/matrix4.h b/include/QF/math/matrix4.h index 4f0e0dfb5..a55c8ed94 100644 --- a/include/QF/math/matrix4.h +++ b/include/QF/math/matrix4.h @@ -34,7 +34,7 @@ /** \defgroup mathlib_matrix4 4x4 matrix functions \ingroup utils */ -//@{ +///@{ #include "QF/qtypes.h" @@ -130,6 +130,6 @@ void Mat4as3MultVec (const mat4_t a, const vec3_t b, vec3_t c); int Mat4Decompose (const mat4_t mat, quat_t rot, vec3_t shear, vec3_t scale, vec3_t trans); -//@} +///@} #endif // __QF_math_matrix4_h diff --git a/include/QF/math/quaternion.h b/include/QF/math/quaternion.h index c6bec188b..60245ad77 100644 --- a/include/QF/math/quaternion.h +++ b/include/QF/math/quaternion.h @@ -34,7 +34,7 @@ /** \defgroup mathlib_quaternion Quaternion functions \ingroup utils */ -//@{ +///@{ #include "QF/qtypes.h" @@ -58,10 +58,10 @@ extern const vec_t *const quat_origin; } while (0) #define QuatConj(a,b) \ do { \ - (b)[0] = (a)[0]; \ + (b)[0] = -(a)[0]; \ (b)[1] = -(a)[1]; \ (b)[2] = -(a)[2]; \ - (b)[3] = -(a)[3]; \ + (b)[3] = (a)[3]; \ } while (0) #define QuatAdd(a,b,c) \ do { \ @@ -115,10 +115,17 @@ extern const vec_t *const quat_origin; (c)[2] = (a)[2] / (b)[2]; \ (c)[3] = (a)[3] / (b)[3]; \ } while (0) -#define QuatCompCompare(x, op, y) \ +#define QuatCompCompare(m, a, op, b, c) \ + do { \ + (c)[0] = m((a)[0] op (b)[0]); \ + (c)[1] = m((a)[1] op (b)[1]); \ + (c)[2] = m((a)[2] op (b)[2]); \ + (c)[3] = m((a)[3] op (b)[3]); \ + } while (0) +#define QuatCompCompareAll(x, op, y) \ (((x)[0] op (y)[0]) && ((x)[1] op (y)[1]) \ && ((x)[2] op (y)[2]) && ((x)[3] op (y)[3])) -#define QuatCompare(x, y) QuatCompCompare (x, ==, y) +#define QuatCompare(x, y) QuatCompCompareAll (x, ==, y) #define QuatCompMin(a, b, c) \ do { \ (c)[0] = min ((a)[0], (b)[0]); \ @@ -164,10 +171,11 @@ extern const vec_t *const quat_origin; void QuatMult (const quat_t q1, const quat_t q2, quat_t out); void QuatMultVec (const quat_t q, const vec3_t v, vec3_t out); +void QuatRotation (const vec3_t a, const vec3_t b, quat_t out); void QuatInverse (const quat_t in, quat_t out); void QuatExp (const quat_t a, quat_t b); void QuatToMatrix (const quat_t q, vec_t *m, int homogenous, int vertical); -//@} +///@} #endif // __QF_math_quaternion_h diff --git a/include/QF/math/vector.h b/include/QF/math/vector.h index 994ffee62..f45893f50 100644 --- a/include/QF/math/vector.h +++ b/include/QF/math/vector.h @@ -31,31 +31,33 @@ /** \defgroup mathlib_vector Vector functions \ingroup mathlib */ -//@{ +///@{ #include "QF/qtypes.h" extern const vec_t *const vec3_origin; +#define VectorCompUop(b, op, a) \ + do { \ + (b)[0] = op ((a)[0]); \ + (b)[1] = op ((a)[1]); \ + (b)[2] = op ((a)[2]); \ + } while (0) +#define VectorCompOp(c, a, op, b) \ + do { \ + (c)[0] = (a)[0] op (b)[0]; \ + (c)[1] = (a)[1] op (b)[1]; \ + (c)[2] = (a)[2] op (b)[2]; \ + } while (0) +#define VectorCompAdd(c,a,b) VectorCompOp (c, a, +, b) +#define VectorCompSub(c,a,b) VectorCompOp (c, a, -, b) +#define VectorCompMult(c,a,b) VectorCompOp (c, a, *, b) +#define VectorCompDiv(c,a,b) VectorCompOp (c, a, /, b) + #define DotProduct(a,b) ((a)[0] * (b)[0] + (a)[1] * (b)[1] + (a)[2] * (b)[2]) -#define VectorSubtract(a,b,c) \ - do { \ - (c)[0] = (a)[0] - (b)[0]; \ - (c)[1] = (a)[1] - (b)[1]; \ - (c)[2] = (a)[2] - (b)[2]; \ - } while (0) -#define VectorNegate(a,b) \ - do { \ - (b)[0] = -(a)[0]; \ - (b)[1] = -(a)[1]; \ - (b)[2] = -(a)[2]; \ - } while (0) -#define VectorAdd(a,b,c) \ - do { \ - (c)[0] = (a)[0] + (b)[0]; \ - (c)[1] = (a)[1] + (b)[1]; \ - (c)[2] = (a)[2] + (b)[2]; \ - } while (0) +#define VectorSubtract(a,b,c) VectorCompSub (c, a, b) +#define VectorNegate(a,b) VectorCompUop (b, -, a) +#define VectorAdd(a,b,c) VectorCompAdd (c, a, b) #define VectorCopy(a,b) \ do { \ (b)[0] = (a)[0]; \ @@ -74,6 +76,18 @@ extern const vec_t *const vec3_origin; (c)[1] = (a)[1] - (s) * (b)[1]; \ (c)[2] = (a)[2] - (s) * (b)[2]; \ } while (0) +#define VectorCompMultAdd(a,b,c,d) \ + do { \ + (d)[0] = (a)[0] + (b)[0] * (c)[0]; \ + (d)[1] = (a)[1] + (b)[1] * (c)[1]; \ + (d)[2] = (a)[2] + (b)[2] * (c)[2]; \ + } while (0) +#define VectorCompMultSub(a,b,c,d) \ + do { \ + (d)[0] = (a)[0] - (b)[0] * (c)[0]; \ + (d)[1] = (a)[1] - (b)[1] * (c)[1]; \ + (d)[2] = (a)[2] - (b)[2] * (c)[2]; \ + } while (0) #define VectorLength(a) sqrt(DotProduct(a, a)) #define VectorScale(a,b,c) \ @@ -107,21 +121,15 @@ extern const vec_t *const vec3_origin; (c)[1] = (b)[1] - (b)[0] * (a)[0]; \ (c)[0] = (b)[0]; \ } while (0) -#define VectorCompMult(a,b,c) \ +#define VectorCompCompare(c, m, a, op, b) \ do { \ - (c)[0] = (a)[0] * (b)[0]; \ - (c)[1] = (a)[1] * (b)[1]; \ - (c)[2] = (a)[2] * (b)[2]; \ + (c)[0] = m((a)[0] op (b)[0]); \ + (c)[1] = m((a)[1] op (b)[1]); \ + (c)[2] = m((a)[2] op (b)[2]); \ } while (0) -#define VectorCompDiv(a,b,c) \ - do { \ - (c)[0] = (a)[0] / (b)[0]; \ - (c)[1] = (a)[1] / (b)[1]; \ - (c)[2] = (a)[2] / (b)[2]; \ - } while (0) -#define VectorCompCompare(x, op, y) \ +#define VectorCompCompareAll(x, op, y) \ (((x)[0] op (y)[0]) && ((x)[1] op (y)[1]) && ((x)[2] op (y)[2])) -#define VectorCompare(x, y) VectorCompCompare (x, ==, y) +#define VectorCompare(x, y) VectorCompCompareAll (x, ==, y) #define VectorCompMin(a, b, c) \ do { \ (c)[0] = min ((a)[0], (b)[0]); \ @@ -142,7 +150,12 @@ extern const vec_t *const vec3_origin; } while (0) #define VectorIsZero(a) (!(a)[0] && !(a)[1] && !(a)[2]) -#define VectorZero(a) ((a)[2] = (a)[1] = (a)[0] = 0); +#define VectorZero(a) \ + do { \ + (a)[0] = 0; \ + (a)[1] = 0; \ + (a)[2] = 0; \ + } while (0) #define VectorSet(a,b,c,d) \ do { \ (d)[0] = a; \ @@ -159,6 +172,8 @@ extern const vec_t *const vec3_origin; //For printf etc #define VectorExpand(v) (v)[0], (v)[1], (v)[2] +//For scanf etc +#define VectorExpandAddr(v) &(v)[0], &(v)[1], &(v)[2] /* * VectorDistance, the distance between two points. @@ -171,11 +186,11 @@ extern const vec_t *const vec3_origin; (((a)[2] - (b)[2]) * ((a)[2] - (b)[2]))) #define VectorDistance(a, b) sqrt(VectorDistance_fast(a, b)) -vec_t _DotProduct (const vec3_t v1, const vec3_t v2); +vec_t _DotProduct (const vec3_t v1, const vec3_t v2) __attribute__((pure)); void _VectorAdd (const vec3_t veca, const vec3_t vecb, vec3_t out); void _VectorCopy (const vec3_t in, vec3_t out); -int _VectorCompare (const vec3_t v1, const vec3_t v2); // uses EQUAL_EPSILON -vec_t _VectorLength (const vec3_t v); +int _VectorCompare (const vec3_t v1, const vec3_t v2) __attribute__((pure)); // uses EQUAL_EPSILON +vec_t _VectorLength (const vec3_t v) __attribute__((pure)); void _VectorMA (const vec3_t veca, float scale, const vec3_t vecb, vec3_t vecc); void _VectorScale (const vec3_t in, vec_t scale, vec3_t out); @@ -209,6 +224,6 @@ VectorNormalize (vec3_t v) return length; } -//@} +///@} #endif // __QF_math_vector_h diff --git a/include/QF/mathlib.h b/include/QF/mathlib.h index 498dbd2d0..be3244c6a 100644 --- a/include/QF/mathlib.h +++ b/include/QF/mathlib.h @@ -25,13 +25,13 @@ */ -#ifndef __mathlib_h -#define __mathlib_h +#ifndef __QF_mathlib_h +#define __QF_mathlib_h /** \defgroup mathlib Vector and matrix functions \ingroup utils */ -//@{ +///@{ #include #include "QF/qtypes.h" @@ -50,12 +50,10 @@ # define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h #endif -extern int nanmask; - #define EQUAL_EPSILON 0.001 #define RINT(x) (floor ((x) + 0.5)) -#define IS_NAN(x) (((*(int *) (char *) &x) & nanmask) == nanmask) +#define Blend(a,b,blend) ((1 - (blend)) * (a) + (blend) * (b)) #include "QF/math/vector.h" #include "QF/math/quaternion.h" @@ -73,15 +71,15 @@ extern int nanmask; // fall over #define ROLL 2 -int Q_log2(int val); +int Q_log2(int val) __attribute__((const)); void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]); void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4]); void FloorDivMod (double numer, double denom, int *quotient, int *rem); -fixed16_t Invert24To16(fixed16_t val); +fixed16_t Invert24To16(fixed16_t val) __attribute__((const)); fixed16_t Mul16_30(fixed16_t multiplier, fixed16_t multiplicand); -int GreatestCommonDivisor (int i1, int i2); +int GreatestCommonDivisor (int i1, int i2) __attribute__((const)); /** Convert quake angles to basis vectors. @@ -138,21 +136,23 @@ void AngleVectors (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up); void AngleQuat (const vec3_t angles, quat_t q); void VectorVectors (const vec3_t forward, vec3_t right, vec3_t up); +// NOTE expects plane distance is -p.n int BoxOnPlaneSide (const vec3_t emins, const vec3_t emaxs, - struct plane_s *plane); -float anglemod (float a); + const plane_t *plane) __attribute__((pure)); +float anglemod (float a) __attribute__((const)); void RotatePointAroundVector (vec3_t dst, const vec3_t axis, const vec3_t point, float degrees); +// NOTE expects plane distance is -p.n #define BOX_ON_PLANE_SIDE(emins, emaxs, p) \ (((p)->type < 3)? \ ( \ - ((p)->dist <= (emins)[(p)->type])? \ + (-(p)->dist <= (emins)[(p)->type])? \ 1 \ : \ ( \ - ((p)->dist >= (emaxs)[(p)->type])? \ + (-(p)->dist >= (emaxs)[(p)->type])? \ 2 \ : \ 3 \ @@ -173,23 +173,25 @@ void RotatePointAroundVector (vec3_t dst, const vec3_t axis, VectorNegate ((sp)->normal, (dp)->normal); \ } while (0) -extern plane_t * const frustum; -GNU89INLINE inline qboolean R_CullBox (const vec3_t mins, const vec3_t maxs); -GNU89INLINE inline qboolean R_CullSphere (const vec3_t origin, const float radius); +GNU89INLINE inline bool R_CullBox (const plane_t *frustum, const vec3_t mins, const vec3_t maxs) __attribute__((pure)); +GNU89INLINE inline bool R_CullSphere (const plane_t *frustum, const vec3_t origin, const float radius); #ifndef IMPLEMENT_R_Cull GNU89INLINE inline #else VISIBLE #endif -qboolean -R_CullBox (const vec3_t mins, const vec3_t maxs) +bool +R_CullBox (const plane_t *frustum, const vec3_t mins, const vec3_t maxs) { int i; - for (i=0 ; i < 4 ; i++) - if (BoxOnPlaneSide (mins, maxs, &frustum[i]) == 2) + for (i=0 ; i < 4 ; i++) { + // NOTE frustum distance is -p.n + if (BOX_ON_PLANE_SIDE (mins, maxs, &frustum[i]) == 2) { return true; + } + } return false; } @@ -198,15 +200,16 @@ GNU89INLINE inline #else VISIBLE #endif -qboolean -R_CullSphere (const vec3_t origin, const float radius) +bool +R_CullSphere (const plane_t *frustum, const vec3_t origin, const float radius) { int i; float r; for (i = 0; i < 4; i++) { - r = DotProduct (origin, frustum[i].normal) - frustum[i].dist; + // NOTE frustum distance is -p.n + r = DotProduct (origin, frustum[i].normal) + frustum[i].dist; if (r <= -radius) return true; } @@ -218,6 +221,6 @@ int CircumSphere (const vec3_t points[], int num_points, sphere_t *sphere); void BarycentricCoords (const vec_t **points, int num_points, const vec3_t p, vec_t *lambda); -//@} +///@} -#endif // __mathlib_h +#endif//__QF_mathlib_h diff --git a/include/QF/mdfour.h b/include/QF/mdfour.h index 94fe99da0..ec8648cd2 100644 --- a/include/QF/mdfour.h +++ b/include/QF/mdfour.h @@ -26,20 +26,20 @@ */ -#ifndef __mdfour_h -#define __mdfour_h +#ifndef __QF_mdfour_h +#define __QF_mdfour_h + +#include "QF/qtypes.h" /** \addtogroup crc */ -//@{ - -#include "QF/uint32.h" +///@{ #define MDFOUR_DIGEST_BYTES 16 struct mdfour { - uint32 A, B, C, D; - uint32 totalN; + uint32_t A, B, C, D; + uint32_t totalN; }; void mdfour_begin(struct mdfour *md); // old: MD4Init @@ -47,6 +47,6 @@ void mdfour_update(struct mdfour *md, const unsigned char *in, int n); //old: MD void mdfour_result(struct mdfour *md, unsigned char *out); // old: MD4Final void mdfour(unsigned char *out, const unsigned char *in, int n); -//@} +///@} -#endif // __mdfour_h +#endif//__QF_mdfour_h diff --git a/include/QF/mersenne.h b/include/QF/mersenne.h index 197ded28d..d80b53e43 100644 --- a/include/QF/mersenne.h +++ b/include/QF/mersenne.h @@ -42,5 +42,45 @@ typedef struct { void mtwist_seed (mtstate_t *state, uint32_t seed); uint32_t mtwist_rand (mtstate_t *state); +GNU89INLINE inline float mtwist_rand_0_1 (mtstate_t *state); +GNU89INLINE inline float mtwist_rand_m1_1 (mtstate_t *state); + +#ifndef IMPLEMENT_MTWIST_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +float +mtwist_rand_0_1 (mtstate_t *state) +{ + union { + uint32_t u; + float f; + } uf; + + uf.u = mtwist_rand (state) & 0x007fffff; + uf.u |= 0x3f800000; + return uf.f - 1.0; +} + +#ifndef IMPLEMENT_MTWIST_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +float +mtwist_rand_m1_1 (mtstate_t *state) +{ + union { + uint32_t u; + float f; + } uf; + + do { + uf.u = mtwist_rand (state) & 0x007fffff; + } while (!uf.u); + uf.u |= 0x40000000; + return uf.f - 3.0; +} #endif//__QF_mersenne_h diff --git a/include/QF/model.h b/include/QF/model.h index b4d7791cd..e8ad0cf47 100644 --- a/include/QF/model.h +++ b/include/QF/model.h @@ -25,8 +25,8 @@ */ -#ifndef _MODEL_H -#define _MODEL_H +#ifndef __QF_model_h +#define __QF_model_h #include "QF/qtypes.h" #include "QF/bspfile.h" @@ -34,6 +34,8 @@ #include "QF/modelgen.h" #include "QF/zone.h" +#include "QF/simd/types.h" + extern struct vid_model_funcs_s *mod_funcs; /* @@ -54,14 +56,6 @@ extern struct vid_model_funcs_s *mod_funcs; // BRUSH MODELS =============================================================== -typedef struct efrag_s { - struct mleaf_s *leaf; - struct efrag_s *leafnext; - struct entity_s *entity; - struct efrag_s *entnext; -} efrag_t; - - // in memory representation =================================================== // !!! if this is changed, it must be changed in asm_draw.h too !!! @@ -85,39 +79,30 @@ typedef struct instsurf_s { struct instsurf_s *tex_chain; ///< next in texture chain struct instsurf_s *lm_chain; ///< next in lightmap chain struct msurface_s *surface; ///< surface to render - vec_t *transform; + vec4f_t *transform; float *color; } instsurf_t; +#define ANIM_CYCLE 2 typedef struct texture_s { char name[16]; - unsigned int width, height; - int gl_texturenum; - int gl_fb_texturenum; - int sky_tex[2]; - instsurf_t *tex_chain; // for gl_texsort drawing - instsurf_t **tex_chain_tail; - struct elechain_s *elechain; - struct elechain_s **elechain_tail; + unsigned width, height; + void *render; // renderer specific data int anim_total; // total tenths in sequence ( 0 = no) int anim_min, anim_max; // time for this frame min <=time< max struct texture_s *anim_next; // in the animation sequence struct texture_s *alternate_anims; // bmodels in frmae 1 use these - unsigned int offsets[MIPLEVELS]; // four mip maps stored + unsigned offsets[MIPLEVELS]; // four mip maps stored } texture_t; -#define SURF_PLANEBACK 2 -#define SURF_DRAWSKY 4 -#define SURF_DRAWSPRITE 8 -#define SURF_DRAWTURB 0x10 -#define SURF_DRAWTILED 0x20 -#define SURF_DRAWBACKGROUND 0x40 -#define SURF_UNDERWATER 0x80 -#define SURF_DONTWARP 0x100 -#define SURF_DRAWNOALPHA 0x200 -#define SURF_DRAWFULLBRIGHT 0x400 -#define SURF_LIGHTBOTHSIDES 0x800 +#define SURF_DRAWSKY 0x01 +#define SURF_DRAWALPHA 0x02 +#define SURF_DRAWTURB 0x04 +#define SURF_PLANEBACK 0x08 +#define SURF_DRAWTILED 0x10 +#define SURF_DRAWBACKGROUND 0x20 +#define SURF_LIGHTBOTHSIDES 0x40 // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct { @@ -144,86 +129,79 @@ typedef struct glpoly_s { #define MAX_DLIGHTS 128 typedef struct msurface_s { - int visframe; // should be drawn when node is crossed + int _visframe; // should be drawn when node is crossed/ + // no longer used, see r_face_visframes - plane_t *plane; int flags; + plane_t *plane; int firstedge; // look up in model->surfedges[], negative numbers int numedges; // are backwards edges - struct surfcache_s *cachespots[MIPLEVELS]; + union { + // sw-only + struct surfcache_s *cachespots[MIPLEVELS]; + // gl/glsl/vulkan + struct { + glpoly_t *polys; // multiple if warped + instsurf_t *instsurf;///< null if not part of world model/sub-model + struct subpic_s *lightpic;///< light map texture ref (glsl) + byte *base; + }; + }; + mtexinfo_t *texinfo; short texturemins[2]; unsigned short extents[2]; - int light_s, light_t; // gl lightmap coordinates - - glpoly_t *polys; // multiple if warped - instsurf_t *instsurf; ///< null if not part of world model/sub-model - instsurf_t *tinst; ///< for instance models - - mtexinfo_t *texinfo; - int ec_index; - byte *base; - // lighting info - struct subpic_s *lightpic; ///< light map texture ref (glsl) + byte *samples; // [numstyles*surfsize] int dlightframe; uint32_t dlightbits[(MAX_DLIGHTS + 31) >> 5]; - int lightmaptexturenum; byte styles[MAXLIGHTMAPS]; int cached_light[MAXLIGHTMAPS]; // values currently used in lightmap - qboolean cached_dlight; // true if dynamic light in cache - byte *samples; // [numstyles*surfsize] + bool cached_dlight; // true if dynamic light in cache + + int model_index; ///< < 0: instance, 0 main, > 0: sub } msurface_t; typedef struct mnode_s { -// common with leaf - int contents; // 0, to differentiate from leafs - int visframe; // node needs to be traversed if current - - float minmaxs[6]; // for bounding box culling - - struct mnode_s *parent; - -// node specific - plane_t *plane; - struct mnode_s *children[2]; - - unsigned int firstsurface; - unsigned int numsurfaces; + /// Vector representation of the plane. + /// \note the 4th component is the negative of the plane distance. This + /// makes it so a 4d dot product with a point vector (4th component = 1) + /// gives the distance of the point from the plane. + vec4f_t plane; + byte type; + byte signbits; + byte pad[2]; + int32_t children[2]; ///< Negative values = ~(leaf number) + float minmaxs[6]; + uint32_t firstsurface; + uint32_t numsurfaces; } mnode_t; typedef struct mleaf_s { // common with node int contents; // wil be a negative contents number - int visframe; // node needs to be traversed if current + int cluster; // cluster to which this leaf bleongs XXX // for bounding box culling float mins[3]; float maxs[3]; - struct mnode_s *parent; - // leaf specific byte *compressed_vis; - efrag_t *efrags; + struct efrag_s *efrags; - msurface_t **firstmarksurface; + int firstmarksurface; int nummarksurfaces; int key; // BSP sequence number for leaf's contents byte ambient_sound_level[NUM_AMBIENTS]; } mleaf_t; -typedef struct mclipnode_s { - int planenum; - int children[2]; -} mclipnode_t; - typedef struct hull_s { - mclipnode_t *clipnodes; + dclipnode_t *clipnodes; plane_t *planes; int firstclipnode; int lastclipnode; @@ -233,9 +211,65 @@ typedef struct hull_s { int depth; ///< maximum depth of the tree } hull_t; +typedef struct mod_brush_s { + unsigned firstmodelsurface, nummodelsurfaces; + + unsigned numsubmodels; + dmodel_t *submodels; + + unsigned numplanes; + plane_t *planes; + + unsigned modleafs; ///< number of leafs in model, including 0 + unsigned visleafs; ///< number of visible leafs, not counting 0 + mleaf_t *leafs; + + unsigned numvertexes; + mvertex_t *vertexes; + + unsigned numedges; + medge_t *edges; + + unsigned numnodes; + mnode_t *nodes; + int depth; ///< maximum depth of the tree + + unsigned numtexinfo; + mtexinfo_t *texinfo; + + unsigned numsurfaces; + msurface_t *surfaces; + + unsigned numsurfedges; + int *surfedges; + + unsigned numclipnodes; + dclipnode_t *clipnodes; + + unsigned nummarksurfaces; + msurface_t **marksurfaces; + + hull_t hulls[MAX_MAP_HULLS]; + hull_t *hull_list[MAX_MAP_HULLS]; + + unsigned numtextures; + texture_t **textures; + texture_t *skytexture; + + byte *visdata; + byte *lightdata; + char *entities; //FIXME should not be here + + int32_t *node_parents; + int32_t *leaf_parents; + int *leaf_flags; // union of surf flags for surfs in leaf + + unsigned int checksum; + unsigned int checksum2; +} mod_brush_t; + // SPRITE MODELS ============================================================== -// FIXME: shorten these? typedef struct mspriteframe_s { int width; int height; @@ -252,16 +286,17 @@ typedef struct { typedef struct { spriteframetype_t type; - mspriteframe_t *frameptr; + union { + mspriteframe_t *frame; + mspritegroup_t *group; + }; } mspriteframedesc_t; typedef struct { - int type; - int maxwidth; - int maxheight; - int numframes; - float beamlength; // remove? - void *cachespot; // remove? + int type; + float beamlength; + int numframes; + int data; mspriteframedesc_t frames[1]; } msprite_t; @@ -282,10 +317,10 @@ typedef struct { } maliasframedesc_t; typedef struct { - aliasskintype_t type; - int skin; - int texnum; - int fb_texnum; + aliasskintype_t type; + int skin; + int texnum; + int fb_texnum; } maliasskindesc_t; typedef struct { @@ -326,17 +361,16 @@ typedef struct { maliasframedesc_t frames[1]; } aliashdr_t; -#define MAXALIASFRAMES 256 -extern aliashdr_t *pheader; -extern stvert_t *stverts; -extern mtriangle_t *triangles; -extern trivertx_t *poseverts[MAXALIASFRAMES]; -extern int aliasbboxmins[3]; -extern int aliasbboxmaxs[3]; - // Whole model ================================================================ -typedef enum {mod_brush, mod_sprite, mod_alias, mod_iqm} modtype_t; +typedef enum { + mod_brush, + mod_sprite, + mod_alias, + mod_iqm, + + mod_num_types +} modtype_t; #define EF_ROCKET 1 // leave a trail #define EF_GRENADE 2 // leave a trail @@ -349,11 +383,13 @@ typedef enum {mod_brush, mod_sprite, mod_alias, mod_iqm} modtype_t; #define EF_GLOWTRAIL 4096 // glowcolor particle trail typedef struct model_s { + //FIXME use pointers. needs care in bsp submodel loading + char path[MAX_QPATH]; char name[MAX_QPATH]; const struct vpath_s *vpath;// virtual path where this model was found - qboolean needload; // bmodels and sprites don't cache normally + bool needload; // bmodels and sprites don't cache normally aliashdr_t *aliashdr; // if not null, alias model is not cached - qboolean hasfullbrights; + bool hasfullbrights; modtype_t type; int numframes; @@ -372,100 +408,45 @@ typedef struct model_s { vec3_t mins, maxs; // solid volume for clipping - qboolean clipbox; + bool clipbox; vec3_t clipmins, clipmaxs; // brush model - int firstmodelsurface, nummodelsurfaces; - - int numsubmodels; - dmodel_t *submodels; - - int numplanes; - plane_t *planes; - - int numleafs; // number of visible leafs, not counting 0 - mleaf_t *leafs; - - int numvertexes; - mvertex_t *vertexes; - - int numedges; - medge_t *edges; - - int numnodes; - mnode_t *nodes; - int depth; ///< maximum depth of the tree - - int numtexinfo; - mtexinfo_t *texinfo; - - int numsurfaces; - msurface_t *surfaces; - - int numsurfedges; - int *surfedges; - - int numclipnodes; - mclipnode_t *clipnodes; - - int nummarksurfaces; - msurface_t **marksurfaces; - - hull_t hulls[MAX_MAP_HULLS]; - hull_t *hull_list[MAX_MAP_HULLS]; - - int numtextures; - texture_t **textures; - texture_t *skytexture; - - byte *visdata; - byte *lightdata; - char *entities; - - unsigned int checksum; - unsigned int checksum2; + //FIXME should be a pointer (submodels make things tricky) + mod_brush_t brush; // additional model data cache_user_t cache; - void (*clear) (struct model_s *m); + void (*clear) (struct model_s *m, void *data); + void *data; + + int render_id; } model_t; // ============================================================================ -extern float RadiusFromBounds (const vec3_t mins, const vec3_t maxs); -void Mod_Init (void); -void Mod_Init_Cvars (void); -void Mod_ClearAll (void); -model_t *Mod_ForName (const char *name, qboolean crash); -void *Mod_Extradata (model_t *mod); // handles caching -void Mod_TouchModel (const char *name); +void Mod_Init (void); +void Mod_Init_Cvars (void); +void Mod_ClearAll (void); +model_t *Mod_ForName (const char *name, bool crash); +void Mod_TouchModel (const char *name); +void Mod_UnloadModel (model_t *model); +// brush specific +mleaf_t *Mod_PointInLeaf (vec4f_t p, model_t *model) __attribute__((pure)); +struct set_s *Mod_LeafPVS (const mleaf_t *leaf, const model_t *model); -mleaf_t *Mod_PointInLeaf (const vec3_t p, model_t *model); -byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model); -model_t *Mod_FindName (const char *name); -int Mod_CalcFullbright (byte *in, byte *out, int pixels); -int Mod_Fullbright (byte * skin, int width, int height, char *name); +void Mod_LeafPVS_set (const mleaf_t *leaf, const model_t *model, byte defvis, + struct set_s *pvs); +void Mod_LeafPVS_mix (const mleaf_t *leaf, const model_t *model, byte defvis, + struct set_s *pvs); -void *Mod_LoadAliasFrame (void *pin, int *posenum, maliasframedesc_t *frame, - int extra); -void *Mod_LoadAliasGroup (void *pin, int *posenum, maliasframedesc_t *frame, - int extra); +void Mod_Print (void); -void Mod_FindClipDepth (hull_t *hull); -void Mod_LoadBrushModel (model_t *mod, void *buffer); -void Mod_FloodFillSkin (byte * skin, int skinwidth, int skinheight); - -void Mod_Print (void); - -extern struct cvar_s *gl_mesh_cache; -extern struct cvar_s *gl_subdivide_size; -extern struct cvar_s *gl_alias_render_tri; -extern struct cvar_s *gl_textures_external; -extern model_t *loadmodel; -extern char *loadname; -extern byte *mod_base; -extern byte mod_novis[MAX_MAP_LEAFS / 8]; +extern int gl_mesh_cache; +extern float gl_subdivide_size; +extern int gl_alias_render_tri; +extern int gl_textures_external; +extern int mod_sky_divide; extern int mod_lightmap_bytes; -#endif // _MODEL_H +#endif//__QF_model_h diff --git a/include/QF/modelgen.h b/include/QF/modelgen.h index 447a8c864..66cdd1308 100644 --- a/include/QF/modelgen.h +++ b/include/QF/modelgen.h @@ -31,8 +31,8 @@ // * pass data from one to the other via model files. * // ********************************************************* -#ifndef _MODELGEN_H -#define _MODELGEN_H +#ifndef __QF_modelgen_h +#define __QF_modelgen_h #include "QF/mathlib.h" @@ -142,4 +142,4 @@ typedef struct { // little-endian "IDP2" #define IDHEADER_MD2 (('2'<<24)+('P'<<16)+('D'<<8)+'I') -#endif // _MODELGEN_H +#endif//__QF_modelgen_h diff --git a/include/QF/msg.h b/include/QF/msg.h index 63da220e9..44b9122fb 100644 --- a/include/QF/msg.h +++ b/include/QF/msg.h @@ -24,22 +24,24 @@ Boston, MA 02111-1307, USA */ -#ifndef _MSG_H -#define _MSG_H +#ifndef __QF_msg_h +#define __QF_msg_h /** \defgroup msg Message reading and writing \ingroup utils */ -//@{ +///@{ #include "QF/sizebuf.h" void MSG_WriteByte (sizebuf_t *sb, int c); void MSG_WriteShort (sizebuf_t *sb, int c); +void MSG_WriteShortBE (sizebuf_t *sb, int c); void MSG_WriteLong (sizebuf_t *sb, int c); +void MSG_WriteLongBE (sizebuf_t *sb, int c); void MSG_WriteFloat (sizebuf_t *sb, float f); void MSG_WriteString (sizebuf_t *sb, const char *s); -void MSG_WriteBytes (sizebuf_t *sb, const void *buf, int len); +void MSG_WriteBytes (sizebuf_t *sb, const void *buf, unsigned len); void MSG_WriteCoord (sizebuf_t *sb, float coord); void MSG_WriteCoordV (sizebuf_t *sb, const vec3_t coord); void MSG_WriteCoordAngleV (sizebuf_t *sb, const vec3_t coord, @@ -50,12 +52,17 @@ void MSG_WriteAngle16 (sizebuf_t *sb, float angle); void MSG_WriteAngle16V (sizebuf_t *sb, const vec3_t angle); void MSG_WriteUTF8 (sizebuf_t *sb, unsigned utf8); +void MSG_PokeShort (sizebuf_t *sb, unsigned offset, int c); +void MSG_PokeShortBE (sizebuf_t *sb, unsigned offset, int c); +void MSG_PokeLong (sizebuf_t *sb, unsigned offset, int c); +void MSG_PokeLongBE (sizebuf_t *sb, unsigned offset, int c); + typedef struct msg_s { - int readcount; - qboolean badread; // set if a read goes beyond end of message - sizebuf_t *message; - size_t badread_string_size; - char *badread_string; + unsigned readcount; + bool badread; // set if a read goes beyond end of message + sizebuf_t *message; + size_t badread_string_size; + char *badread_string; } qmsg_t; /** Reset the message read status. @@ -73,7 +80,7 @@ void MSG_BeginReading (qmsg_t *msg); \param msg The message to check. \return The number of bytes that have been read. */ -int MSG_GetReadCount(qmsg_t *msg); +unsigned MSG_GetReadCount(qmsg_t *msg) __attribute__((pure)); /** Read a single byte from the message. @@ -95,6 +102,16 @@ int MSG_ReadByte (qmsg_t *msg); */ int MSG_ReadShort (qmsg_t *msg); +/** Read a single big-endian unsigned short from the message. + + Advances the read index. + + \param msg The message from which the short will be read. + \return The short value (0 - 65535), or -1 if already at + the end of the message. +*/ +int MSG_ReadShortBE (qmsg_t *msg); + /** Read a single little-endian long from the message. Advances the read index. @@ -108,6 +125,19 @@ int MSG_ReadShort (qmsg_t *msg); */ int MSG_ReadLong (qmsg_t *msg); +/** Read a single big-endian long from the message. + + Advances the read index. + + \param msg The message from which the long will be read. + \return The signed long value or -1 if already at the end of + the message. + \note -1 may be either an error or a value. Check qmsg_t::badread to + differentiate the two cases (false for a value). + \todo Fix? +*/ +int MSG_ReadLongBE (qmsg_t *msg); + /** Read a single little-endian float from the message. Advances the read index. @@ -150,7 +180,7 @@ const char *MSG_ReadString (qmsg_t *msg); \param len The number of bytes to read. \return The number of bytes read from the message. */ -int MSG_ReadBytes (qmsg_t *msg, void *buf, int len); +int MSG_ReadBytes (qmsg_t *msg, void *buf, unsigned len); /** Read a little-endian 16-bit fixed point (13.3) coordinate value from the message. @@ -249,6 +279,6 @@ void MSG_ReadAngle16V (qmsg_t *msg, vec3_t angles); */ int MSG_ReadUTF8 (qmsg_t *msg); -//@} +///@} -#endif +#endif//__QF_msg_h diff --git a/include/QF/object.h b/include/QF/object.h index 39f06210b..a722c42ee 100644 --- a/include/QF/object.h +++ b/include/QF/object.h @@ -32,8 +32,8 @@ */ -#ifndef __object_h -#define __object_h +#ifndef __QF_object_h +#define __QF_object_h #include "QF/qtypes.h" @@ -81,7 +81,7 @@ typedef struct Object_s { struct Object_s *next; struct String_s * methodDecl(Object, toString); ObjRefs_t * methodDecl(Object, allRefs); - qboolean methodDecl(Object, finalize); + bool methodDecl(Object, finalize); void *data; } Object; @@ -103,11 +103,11 @@ classDecl (Class, Object, ); #define CLASS(o) ((Class *)(o)) -Object *Object_Create (Class *cl, qboolean perm); +Object *Object_Create (Class *cl, bool perm); void Object_Delete (Object *obj); Object *Object_Retain (Object *obj); Object *Object_Release (Object *obj); -qboolean Object_InstanceOf (Object *obj, Class *cl); +bool Object_InstanceOf (Object *obj, Class *cl); void Object_AddToRoot (Object *obj); void Object_RemoveFromRoot (Object *obj); void Object_Init (void); @@ -115,4 +115,4 @@ void Object_Garbage_Collect (void); #include "QF/classes/String.h" -#endif +#endif//__QF_object_h diff --git a/include/QF/pak.h b/include/QF/pak.h index f176e5733..e331fca6e 100644 --- a/include/QF/pak.h +++ b/include/QF/pak.h @@ -28,12 +28,12 @@ */ -#ifndef __qf_pak_h -#define __qf_pak_h +#ifndef __QF_pak_h +#define __QF_pak_h /** \addtogroup pak */ -//@{ +///@{ // little-endian PACK #define IDPAKHEADER (('K'<<24)+('C'<<16)+('A'<<8)+'P') @@ -51,6 +51,6 @@ typedef struct { int dirlen; } dpackheader_t; -//@} +///@} -#endif//__qf_pak_h +#endif//__QF_pak_h diff --git a/include/QF/pakfile.h b/include/QF/pakfile.h index ecb6738f0..7bb4f3a0e 100644 --- a/include/QF/pakfile.h +++ b/include/QF/pakfile.h @@ -33,7 +33,7 @@ /** \defgroup pak pakfile proccessing \ingroup utils */ -//@{ +///@{ #include "QF/hash.h" #include "QF/pak.h" @@ -64,6 +64,6 @@ int pack_add (pack_t *pack, const char *filename); int pack_extract (pack_t *pack, dpackfile_t *pf); dpackfile_t *pack_find_file (pack_t *pack, const char *filename); -//@} +///@} #endif//__QF_pakfile_h diff --git a/include/QF/pcx.h b/include/QF/pcx.h index 6bfeb03e5..f4ce939d1 100644 --- a/include/QF/pcx.h +++ b/include/QF/pcx.h @@ -24,8 +24,8 @@ Boston, MA 02111-1307, USA */ -#ifndef __pcx_h -#define __pcx_h +#ifndef __QF_pcx_h +#define __QF_pcx_h #include "QF/qtypes.h" #include "QF/quakeio.h" @@ -62,8 +62,8 @@ typedef struct \return A pointer to the newly-coded PCX data. \warning Uses Hunk_TempAlloc() to allocate the output PCX content. */ -pcx_t *EncodePCX (byte *data, int width, int height, int rowbytes, - byte *palette, qboolean flip, int *length); +pcx_t *EncodePCX (const byte *data, int width, int height, int rowbytes, + const byte *palette, bool flip, int *length); /** Load a texture from a PCX file. @@ -71,10 +71,11 @@ pcx_t *EncodePCX (byte *data, int width, int height, int rowbytes, \param f The file to read the texture from \param convert If true, the texture is converted to RGB on load \param pal The palette to apply during conversion + \param load If false, only the format and size info is loaded \return A pointer to the texture. \warning Uses Hunk_TempAlloc() to allocate the texture. */ -struct tex_s *LoadPCX (QFile *f, qboolean convert, byte *pal); +struct tex_s *LoadPCX (QFile *f, bool convert, const byte *pal, int load); -#endif // __pcx_h +#endif//__QF_pcx_h diff --git a/include/QF/plist.h b/include/QF/plist.h new file mode 100644 index 000000000..43f6a6f7e --- /dev/null +++ b/include/QF/plist.h @@ -0,0 +1,592 @@ +/* + plist.h + + Property list management types and prototypes + + Copyright (C) 2000 Jeff Teunissen + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_plist_h +#define __QF_plist_h + +struct hashctx_s; + +/** \defgroup plist Property lists + \ingroup utils +*/ +///@{ + +#include "QF/qtypes.h" + +/** The type of the property list item. + + For further details, see \ref property-list. +*/ +typedef enum { + QFDictionary, ///< The property list item represents a dictionary. + QFArray, ///< The property list item represents an array. + QFBinary, ///< The property list item represents arbitrary binary + ///< data. + QFString, ///< The property list item represents a C string. + + QFMultiType = (1 << 31) ///< if bit 31 is set, the type indicates a mask + ///< of allowed types for plfield_t +} pltype_t; + +/** Generic property list item. + + All inspection and manipulation is to be done via the accessor functions. +*/ +typedef struct plitem_s plitem_t; + +struct plfield_s; +/** Custom parser for the field. + + With this, custom parsing of any property list object type is + supported. For example, parsing of strings into numeric values, + converting binary objects to images, and deeper parsing of array and + dictionary objects. + + If null, then the default parser for the object type is used: + * QFString: pointer to the actual string. The string continues to + be owned by the string object. + * QFBinary: pointer to fixed-size DARRAY_TYPE(byte) (so size isn't + lost) + * QFArray: pointer to fixed-size DARRAY_TYPE(plitem_t *) with the + individual objects. The individual objects continue to be owned + by the array object. + * QFDictionary: pointer to the hashtab_t hash table used for the + dictionary object. The hash table continues to be owned by the + dictionary object. + + \param field Pointer to this field item. + \param item The property list item being parsed into the field. + \param data Pointer to the field in the structure being parsed. + \param messages An array object the parser can use to store any + error messages. Messages should be strings, but no + checking is done: it is up to the top-level caller to + parse out the messages. + \param context Additional context data passed to the parser. + \return 0 for error, 1 for success. See \a PL_ParseStruct, + \a PL_ParseArray, \a PL_ParseLabeledArray, and + \a PL_ParseSymtab. +*/ +typedef int (*plparser_t) (const struct plfield_s *field, + const struct plitem_s *item, + void *data, + struct plitem_s *messages, + void *context); + +/** A field to be parsed from a dictionary item. + + \a PL_ParseStruct uses an array (terminated by an element with \a name + set to null) of these to describe the fields in the structure being + parsed. + + \a PL_ParseArray, \a PL_ParseLabeledArray, and \a PL_ParseSymtab use only + a single \a plfield_t object, and then only the \a data field, which must + point to a \a plelement_t object. This allows all the parse functions to + be used directly as either a \a plfield_t or \a plelement_t object's + \a parser. + + \a PL_ParseLabeledArray and \a PL_ParseSymtab pass to the parser a field + with \a name set to the key of the current item and \a offset set to 0. + For \a PL_ParseArray, \a name is set to null and \a offset is set to the + current index. \a PL_ParseLabeledArray also sets \a offset to the current + index. \a type, \a parser, and \a data are taken from the \a plelement_t + passed in to them. + + \a PL_ParseStruct passes the currently parsed field without any + modifications. +*/ +typedef struct plfield_s { + const char *name; ///< matched by dictionary key + size_t offset; ///< the offset of the field within the structure + pltype_t type; ///< the required type of the dictionary object + plparser_t parser; ///< custom parser function + void *data; ///< additional data for \a parser +} plfield_t; + +typedef struct plelement_s { + pltype_t type; ///< the required type of the array elements + size_t stride; ///< the size of each element + void *(*alloc) (void *ctx, size_t size);///< allocator for array memory + plparser_t parser; ///< custom parser function + void *data; ///< additional data for \a parser +} plelement_t; + +/** Create an in-memory representation of the contents of a property list. + + \param string the saved plist, as read from a file. + \param hashctx Hashlink chain to use when creating dictionaries (see + Hash_NewTable()). May be null. + + \return Returns an object equivalent to the passed-in string. + \note You are responsible for freeing the returned object. +*/ +plitem_t *PL_GetPropertyList (const char *string, struct hashctx_s **hashctx); + +/** Create a property list from a bare dictionary list. + + The input is treated as a list of dictionary key-value pairs without the + enclosing { or }. + + \param string dicitionary list string. + \param hashctx Hashlink chain to use when creating dictionaries (see + Hash_NewTable()). May be null. + + \return Returns a dictionary object. + \note You are responsible for freeing the returned object. +*/ +plitem_t *PL_GetDictionary (const char *string, struct hashctx_s **hashctx); + +/** Create a property list from a bare array list. + + The input is treated as a list of array values without the enclosing ( or ). + + \param string array list string. + \param hashctx Hashlink chain to use when creating dictionaries (see + Hash_NewTable()). May be null. + + \return Returns an array object. + \note You are responsible for freeing the returned object. +*/ +plitem_t *PL_GetArray (const char *string, struct hashctx_s **hashctx); + +/** Create a property list string from the in-memory representation. + + \param pl the in-memory representation + \return the text representation of the property list + \note You are responsible for freeing the returned string. +*/ +char *PL_WritePropertyList (const plitem_t *pl); + +/** Retrieve the type of an object. + + \param item The object. Must not be null. + \return the type of the object +*/ +pltype_t PL_Type (const plitem_t *item) __attribute__((pure)); + +/** Retrieve the line number of an object. + + \param item The object. Must not be null. + \return the line number on which the object began, or 0 if not from a + string +*/ +int PL_Line (const plitem_t *item) __attribute__((pure)); + +/** Retrieve the data size from a binary object. + + \param binary The binary object + \return the size in bytes of the binary object 0 if \a binary isn't a + binary object (includes if \a binary is null). +*/ +size_t PL_BinarySize (const plitem_t *binary) __attribute__((pure)); + +/** Retrieve the data from a binary object. + + \param binary The binary object + \return pointer to the actual data or NULL if \b binary isn't a binary + object (includes if \a binary is null). + \note You are NOT responsible for freeing the returned object. It will + be destroyed when its container is. +*/ +const void *PL_BinaryData (const plitem_t *binary) __attribute__((pure)); + +/** Retrieve a string from a string object. + + \param string The string object + \return pointer to the actual string value or NULL if string isn't a + string (includes if \a string is null). + \note You are NOT responsible for freeing the returned object. It will + be destroyed when its container is. +*/ +const char *PL_String (const plitem_t *string) __attribute__((pure)); + +/** Retrieve a value from a dictionary object. + + \param dict The dictionary to retrieve a value from + \param key The unique key associated with the value + \return the value associated with the key, or NULL if not found or \a dict + isn't a dictionary (includes if \a dict is null). + \note You are NOT responsible for freeing the returned object. It will + be destroyed when its container is. +*/ +plitem_t *PL_ObjectForKey (const plitem_t *dict, const char *key); + +/** Remove a value from a dictionary object. + + \param dict The Dictionary to remove the value from + \param key The unique key associated with the value to be removed +*/ +void PL_RemoveObjectForKey (plitem_t *dict, const char *key); + +/** Retrieve a key from a dictionary object. + + \param dict The dictionary to get the key from + \param index The index of the key + \return the key at the specified index, or NULL if index is out of range or + dict is not a dictionary (includes if \a dict is null). + \note You are NOT responsible for freeing the returned object. It will + be destroyed when its container is. +*/ +const char *PL_KeyAtIndex (const plitem_t *dict, int index) __attribute__((pure)); + +/** Retrieve a value from an array object. + + \param array The array to get the value from + \param index The index within the array to retrieve + \return the value at the specified index, or NULL if \a index is out of + range or \a array is not an array (includes if \a array is null). + \note You are NOT responsible for freeing the returned object. It will + be destroyed when its container is. +*/ +plitem_t *PL_ObjectAtIndex (const plitem_t *array, int index) __attribute__((pure)); + +/** Retrieve a list of all keys in a dictionary. + + \param dict The dictionary to list + \return an Array containing Strings or NULL if \a dict isn't a dictionary + (includes if \a dict is null). + \note You are responsible for freeing this array. +*/ +plitem_t *PL_D_AllKeys (const plitem_t *dict); + +/** Retrieve the number of keys in a dictionary. + + \param dict The dictionary to get the number of keys of. + + \return Returns the number of keys in the dictionary or 0 if \a dict isn't + a dictionary (includes if \a dict is null). +*/ +int PL_D_NumKeys (const plitem_t *dict) __attribute__((pure)); + +/** Add a key/value pair to a dictionary. + + \param dict The dictionary to which the key/value pair will be added + \param key The key of the key/value pair to be added to the dictionary + \param value The value of the key/value pair to be added to the dictionary + + \return true on success, false on failure (\a dict is null or not a + dictionary) + + \note the dictionary becomes the owner of the value. +*/ +bool PL_D_AddObject (plitem_t *dict, const char *key, plitem_t *value); + +/** Copy contents of one dictionary into another. + + The contents of \a srcDict are added to \a dstDict without affecting + \a srcDict. Any collisions in \a dstDict result in those values in + \a dstDict being replaced by the the values from \a srcDict: the new + key-value pairs override the old. + + \param dstDict The dictionary to extend + \param srcDict The dictionary from which key-value pairs will be copied + \return true if values were copied, false if nothing was copied (either + dictionary is null, or not a dictionary, or if \a srcDict was empty) +*/ +bool PL_D_Extend (plitem_t *dstDict, plitem_t *srcDict); + +/** Add an item to an array. + + \param array The array to which the item will be added + \param item The item to be added to the array + + \return true on success, false on failure (\a array is null or not an + array) + + \note the array becomes the owner of the added item. +*/ +bool PL_A_AddObject (plitem_t *array, plitem_t *item); + +/** Append contents of one array to another. + + The contents of \a srcArray are added to \a dstArray without affecting + \a srcArray. Those values are appended to the destination array values. + + \param dstArray The array to extend + \param srcArray The array from which values will be copied + \return true if values were copied, false if nothing was copied (either + array is null, or not an array, or if \a srcArray was empty) +*/ +bool PL_A_Extend (plitem_t *dstArray, plitem_t *srcArray); + +/** Retrieve the number of items in an array. + + \param array The array from which to get the number of objects + + \return number of objects in the array or 0 if \a array is null or not + an array. +*/ +int PL_A_NumObjects (const plitem_t *array) __attribute__((pure)); + +/** Insert an item into an array before the specified location. + + \param array The array to which the item will be added + \param item The item to be added to the array + \param index The location at which to insert the item into the array + + \return true on success, false on failure (\a array is null or not an + array). + + \note the array becomes the owner of the added item. +*/ +bool PL_A_InsertObjectAtIndex (plitem_t *array, plitem_t *item, int index); + +/** Remove a value from an array object. + The array items will be shifted to fill the resulting hole. + + \param array The array from which to remove the value + \param index The index within the array to remove +*/ +void PL_RemoveObjectAtIndex (plitem_t *array, int index); + +/** Create a new dictionary object. + The dictionary will be empty. + \param hashctx Hashlink chain to use when creating dictionaries (see + Hash_NewTable()). May be null. + \return the new dictionary object +*/ +plitem_t *PL_NewDictionary (struct hashctx_s **hashctx); + +/** Create a new array object. + The array will be empty. + \return the new array object +*/ +plitem_t *PL_NewArray (void); + +/** Create a new data object from the given data. + Takes ownership of the given data. + \param data pointer to data buffer + \param size number of bytes in the buffer + \return the new dictionary object + \note The data will be freed via free() when the item is freed. +*/ +plitem_t *PL_NewData (void *data, size_t size); + +/** Create a new string object. + Makes a copy of the given string. + \param str C string to copy + \return the new dictionary object +*/ +plitem_t *PL_NewString (const char *str); + +/** Retain ownership of a property list object. + + Use prior to removal to ensure the property list object is not freed when + removed from an array or dictionary. Adding an object to a dictionary or + array automatically retains the object. + + \param item the property list object to be retained + \return the item + \note item may be null, in which case nothing is done but to return null +*/ +plitem_t *PL_Retain (plitem_t *item); + +/** Release ownership of a property list object. + + If the number of owners is reduced to 0 (or is already 0) then the object + will be freed. If the object contains other objects, then those objects + will be released. + + \param item the property list object to be released + \return the item if it is still valid, otherwise null + \note item may be null, in which case nothing is done but to return null +*/ +plitem_t *PL_Release (plitem_t *item); + +void PL_SetUserData (plitem_t *item, void *data); +void *PL_GetUserData (plitem_t *item) __attribute__((pure)); + +int PL_CheckType (pltype_t field_type, pltype_t item_type) __attribute__((const)); +void PL_TypeMismatch (plitem_t *messages, const plitem_t *item, + const char *name, pltype_t field_type, + pltype_t item_type); + +/** Parse a dictionary object into a structure. + + For each key in the dictionary, the corresponding field item is used to + determine how to parse the object associated with that key. Duplicate + field items are ignored: only the first item is used, and no checking is + done. Fields for which there is no key in the dictionary are also ignored, + and the destination is left unmodified. However, keys that have no + corresponding field are treated as errors and a suitable message is added + to the \a messages object. + + When an error occurs (unknown key, incorrect item type (item type does not + match the type specified in the field item) or the field item's \a parser + returns 0), processing continues but the error result is returned. + + Can be used recursively to parse deep hierarchies. + + \param fields Array of field items describing the structure. Terminated + by a field item with a null \a name pointer. + \param dict The dictionary object to parse + \param data Pointer to the structure into which the data will be + parsed. + \param messages Array object supplied by the caller used for storing + messages. The messages may or may not indicate errors (its + contents are not checked). This function itself will add + only string objects. + If there are any errors, suitable messages will be found in + the \a messages object. However, just because there are no + errors doesn't mean that \a messages will remain empty as + a field's \a parser may add other messages. The standard + message format is "[line number]: [message]". If the line + number is 0, then the actual line is unknown (due to the + source item not being parsed from a file or string). + \param context Additional context data passed to the parser. + \return 0 if there are any errors, 1 if there are no errors. +*/ +int PL_ParseStruct (const plfield_t *fields, const plitem_t *dict, + void *data, plitem_t *messages, void *context); + +/** Parse an array object into a dynamic array (see darray.h). + + For each object in the array, the field item is used to determine how to + parse the object. If the array is empty, the destination will be + initialized to an empty array. + + When an error occurs (incorrect item type (item type does not match the + type specified in the element object) or the element object's \a parser + returns 0), processing continues but the error result is returned. + + Can be used recursively to parse deep hierarchies. + + \param field Pointer to a single field that has the field data pointer + set to reference a plelement_t object used to describe + the contents of the array. + \param array The array object to parse + \param data Pointer to the pointer to which the dynamic array will + be written. The dynamic array is allocated using + DARRAY_ALLOCFIXED_OBJ(). + \param messages Array object supplied by the caller used for storing + messages. The messages may or may not indicate errors (its + contents are not checked). This function itself will add + only string objects. + If there are any errors, suitable messages will be found in + the \a messages object. However, just because there are no + errors doesn't mean that \a messages will remain empty as + a field's \a parser may add other messages. The standard + message format is "[line number]: [message]". If the line + number is 0, then the actual line is unknown (due to the + source item not being parsed from a file or string). + \param context Additional context data passed to the parser and allocator. + \return 0 if there are any errors, 1 if there are no errors. +*/ +int PL_ParseArray (const plfield_t *field, const plitem_t *array, + void *data, plitem_t *messages, void *context); + +/** Parse a dictionary object into a dynamic array (see darray.h). + + This is useful when the dictionary object is meant to be a labeled list + rather than a representation of a structure. + + For each object in the array, the field item is used to determine how to + parse the object. If the array is empty, the destination will be + initialized to an empty array. + + When an error occurs (incorrect item type (item type does not match the + type specified in the element object) or the element object's \a parser + returns 0), processing continues but the error result is returned. + + Can be used recursively to parse deep hierarchies. + + \param field Pointer to a single field that has the field data pointer + set to reference a plelement_t object used to describe + the contents of the array. + \param dict The dict object to parse + \param data Pointer to the pointer to which the dynamic array will + be written. The dynamic array is allocated using + DARRAY_ALLOCFIXED_OBJ(). + \param messages Array object supplied by the caller used for storing + messages. The messages may or may not indicate errors (its + contents are not checked). This function itself will add + only string objects. + If there are any errors, suitable messages will be found in + the \a messages object. However, just because there are no + errors doesn't mean that \a messages will remain empty as + a field's \a parser may add other messages. The standard + message format is "[line number]: [message]". If the line + number is 0, then the actual line is unknown (due to the + source item not being parsed from a file or string). + \param context Additional context data passed to the parser and allocator. + \return 0 if there are any errors, 1 if there are no errors. +*/ +int PL_ParseLabeledArray (const plfield_t *field, const plitem_t *dict, + void *data, plitem_t *messages, void *context); + +/** Parse a dictionary object into a hash table. + + For each key in the dictionary, the element object is used to determine + how to parse the object associated with that key. Duplicate keys are an + error: they must be unique. A suitable message is added to the + \a messages object. If the dictionary object is empty, the destination + table is left unmodified. + + When an error occurs (duplicate keys, incorrect type, or the element + object's \a parser returns 0), processing continues but the error + result is returned. + + Can be used recursively to parse deep hierarchies. + + Hash_Add() is used to add objects to the hash table, and Hash_Find() is + used to check for duplicates. Hash_Free() is used to free unused + objects. The means that the hash table is expected to use standard + objects with embedded keys (the parser is expected to put the key in the + object) and to have a free function. + + The parser's data paramenter points to a pre-allocated block of memory + of the sized indicated by the element object's size field, using the + element object's alloc callback. The name field in the field paramenter + is set to the object's key and remains owned by the dictionary. + + \param field Pointer to a single field that has the field data pointer + set to reference a plelement_t object used to describe + the contents of the dictionary. + \param dict The dictionary object to parse + \param data Pointer to the structure into which the data will be + parsed. + \param messages Array object supplied by the caller used for storing + messages. The messages may or may not indicate errors (its + contents are not checked). This function itself will add + only string objects. + If there are any errors, suitable messages will be found in + the \a messages object. However, just because there are no + errors doesn't mean that \a messages will remain empty as + a field's \a parser may add other messages. The standard + message format is "[line number]: [message]". If the line + number is 0, then the actual line is unknown (due to the + source item not being parsed from a file or string). + \param context Additional context data passed to the parser and allocator. + \return 0 if there are any errors, 1 if there are no errors. +*/ +int PL_ParseSymtab (const plfield_t *field, const plitem_t *dict, + void *data, plitem_t *messages, void *context); +void __attribute__((format(PRINTF,3,4))) +PL_Message (plitem_t *messages, const plitem_t *item, const char *fmt, ...); + +///@} + +#endif//__QF_plist_h diff --git a/include/QF/plugin.h b/include/QF/plugin.h index 995b72750..7eb0e8d4d 100644 --- a/include/QF/plugin.h +++ b/include/QF/plugin.h @@ -25,22 +25,22 @@ */ -#ifndef __QF_plugin_h_ -#define __QF_plugin_h_ +#ifndef __QF_plugin_h +#define __QF_plugin_h /** \defgroup plugin Plugins \ingroup utils */ -//@{ +///@{ #define QFPLUGIN_VERSION "1.0" #include #ifdef STATIC_PLUGINS -#define PLUGIN_INFO(type,name) plugin_t *type##_##name##_PluginInfo (void); plugin_t * type##_##name##_PluginInfo (void) +#define PLUGIN_INFO(type,name) plugin_t *type##_##name##_PluginInfo (void); __attribute__((const)) plugin_t * type##_##name##_PluginInfo (void) #else -#define PLUGIN_INFO(type,name) plugin_t *PluginInfo (void); __attribute__((visibility ("default"))) plugin_t *PluginInfo (void) +#define PLUGIN_INFO(type,name) plugin_t *PluginInfo (void); __attribute__((visibility ("default"),const)) plugin_t *PluginInfo (void) #endif typedef enum { @@ -88,29 +88,26 @@ typedef struct plugin_s { /* General plugin info return function type */ -typedef plugin_t * (*P_PluginInfo) (void); +typedef plugin_t *(*plugin_info_t) (void); typedef struct plugin_list_s { - const char *name; - P_PluginInfo info; + const char *name; + plugin_info_t info; } plugin_list_t; /* Plugin system variables */ -extern struct cvar_s *fs_pluginpath; +extern char *fs_pluginpath; /* Function prototypes */ plugin_t *PI_LoadPlugin (const char *, const char *); -qboolean PI_UnloadPlugin (plugin_t *); +bool PI_UnloadPlugin (plugin_t *); void PI_RegisterPlugins (plugin_list_t *); void PI_Init (void); -void PI_Shutdown (void); -// FIXME: we need a generic function to initialize unused fields +///@} -//@} - -#endif // __QF_plugin_h_ +#endif//__QF_plugin_h diff --git a/include/QF/plugin/cd.h b/include/QF/plugin/cd.h index ad7495159..8976b3ecf 100644 --- a/include/QF/plugin/cd.h +++ b/include/QF/plugin/cd.h @@ -24,33 +24,23 @@ Boston, MA 02111-1307, USA */ -#ifndef __QF_plugin_cd_h_ -#define __QF_plugin_cd_h_ +#ifndef __QF_plugin_cd_h +#define __QF_plugin_cd_h #include #include -/* - All CDAudio plugins must export these functions -*/ -typedef void (*P_CDAudio_CD_f) (void); // -typedef void (*P_CDAudio_Pause) (void); -typedef void (*P_CDAudio_Play) (int, qboolean); -typedef void (*P_CDAudio_Resume) (void); -typedef void (*P_CDAudio_Shutdown) (void); -typedef void (*P_CDAudio_Update) (void); -typedef void (*P_CDAudio_Init) (void); - typedef struct cd_funcs_s { - P_CDAudio_CD_f pCD_f; // - P_CDAudio_Pause pCDAudio_Pause; - P_CDAudio_Play pCDAudio_Play; - P_CDAudio_Resume pCDAudio_Resume; - P_CDAudio_Update pCDAudio_Update; + void (*init) (void); + void (*cd_f) (void); // + void (*pause) (void); + void (*play) (int, bool); + void (*resume) (void); + void (*update) (void); } cd_funcs_t; typedef struct cd_data_s { - int unused; /* C requires that a struct or union has at least one member */ + int unused; } cd_data_t; -#endif // __QF_plugin_cd_h_ +#endif // __QF_plugin_cd_h diff --git a/include/QF/plugin/console.h b/include/QF/plugin/console.h index e743d20e7..b5f1ab536 100644 --- a/include/QF/plugin/console.h +++ b/include/QF/plugin/console.h @@ -25,45 +25,40 @@ */ -#ifndef __QF_plugin_console_h_ -#define __QF_plugin_console_h_ +#ifndef __QF_plugin_console_h +#define __QF_plugin_console_h #include -#include +#include #include -#include - -typedef void (*P_C_Print) (const char *fmt, va_list args); -typedef void (*P_C_ProcessInput) (void); -typedef void (*P_C_KeyEvent) (knum_t key, short unicode, qboolean down); -typedef void (*P_C_DrawConsole) (void); -typedef void (*P_C_CheckResize) (void); -typedef void (*P_C_NewMap) (void); typedef struct console_funcs_s { - P_C_Print pC_Print; - P_C_ProcessInput pC_ProcessInput; - P_C_KeyEvent pC_KeyEvent; - P_C_DrawConsole pC_DrawConsole; - P_C_CheckResize pC_CheckResize; - P_C_NewMap pC_NewMap; + void (*init) (void); + void (*print) (const char *fmt, va_list args) __attribute__((format(PRINTF, 1, 0))); + void (*process_input) (void); + void (*draw_console) (void); + void (*new_map) (void); + void (*set_state) (con_state_t state); } console_funcs_t; typedef struct console_data_s { - struct dstring_s *dl_name; - int *dl_percent; - double *realtime; - double *frametime; - int force_commandline; - int ormask; - void (*quit)(void); - struct cbuf_s *cbuf; - struct view_s *view; - struct view_s *status_view; - float lines; - int (*exec_line)(void *data, const char *line); - void *exec_data; + struct dstring_s *dl_name; + int *dl_percent; + double *realtime; + double *frametime; + int force_commandline; + int ormask; + void (*quit) (void); + struct cbuf_s *cbuf; + struct view_s *status_view; + const struct component_s *components; + uint32_t num_components; + uint32_t component_base; + struct canvas_system_s *canvas_sys; + float lines; + int (*exec_line)(void *data, const char *line); + void *exec_data; } console_data_t; -#endif // __QF_plugin_console_h_ +#endif // __QF_plugin_console_h diff --git a/include/QF/plugin/general.h b/include/QF/plugin/general.h index 16141e410..7718e375e 100644 --- a/include/QF/plugin/general.h +++ b/include/QF/plugin/general.h @@ -25,28 +25,21 @@ */ -#ifndef __QF_plugin_general_h_ -#define __QF_plugin_general_h_ +#ifndef __QF_plugin_general_h +#define __QF_plugin_general_h #include #include -/* - All plugins, of all types, must export these functions -*/ - -typedef void (*P_Init) (void); -typedef void (*P_Shutdown) (void); - typedef struct general_funcs_s { - P_Init p_Init; - P_Shutdown p_Shutdown; + void (*init) (void); + void (*shutdown) (void); } general_funcs_t; #define PIF_GLOBAL 1 typedef struct general_data_s { - int flag; + int flag; } general_data_t; -#endif // __QF_plugin_general_h_ +#endif // __QF_plugin_general_h diff --git a/include/QF/plugin/input.h b/include/QF/plugin/input.h index 1077c1d99..297a2953b 100644 --- a/include/QF/plugin/input.h +++ b/include/QF/plugin/input.h @@ -25,29 +25,16 @@ */ -#ifndef __QF_plugin_input_h_ -#define __QF_plugin_input_h_ +#ifndef __QF_plugin_input_h +#define __QF_plugin_input_h #include #include -/* - All input plugins must export these functions -*/ -typedef void (*P_IN_Commands) (void); -typedef void (*P_IN_SendKeyEvents) (void); -typedef void (*P_IN_Move) (void); -typedef void (*P_IN_ModeChanged) (void); - typedef struct input_funcs_s { - P_IN_Commands pIN_Commands; - P_IN_SendKeyEvents pIN_SendKeyEvents; - P_IN_Move pIN_Move; - P_IN_ModeChanged pIN_ModeChanged; } input_funcs_t; typedef struct input_data_s { - int unused; /* C requires that a struct or union has at least one member */ } input_data_t; -#endif // __QF_plugin_input_h_ +#endif // __QF_plugin_input_h diff --git a/include/QF/plugin/snd_output.h b/include/QF/plugin/snd_output.h index 5b6cd02d5..d989a1571 100644 --- a/include/QF/plugin/snd_output.h +++ b/include/QF/plugin/snd_output.h @@ -24,34 +24,33 @@ Boston, MA 02111-1307, USA */ -#ifndef __QF_plugin_snd_output_h_ -#define __QF_plugin_snd_output_h_ +#ifndef __QF_plugin_snd_output_h +#define __QF_plugin_snd_output_h #include #include -/* - All sound plugins must export these functions -*/ -typedef volatile struct dma_s *(*P_S_O_Init) (void); -typedef void (*P_S_O_Shutdown) (void); -typedef int (*P_S_O_GetDMAPos) (void); -typedef void (*P_S_O_Submit) (void); -typedef void (*P_S_O_BlockSound) (void); -typedef void (*P_S_O_UnblockSound) (void); +struct snd_s; typedef struct snd_output_funcs_s { - P_S_O_Init pS_O_Init; - P_S_O_Shutdown pS_O_Shutdown; - P_S_O_GetDMAPos pS_O_GetDMAPos; - P_S_O_Submit pS_O_Submit; - P_S_O_BlockSound pS_O_BlockSound; - P_S_O_UnblockSound pS_O_UnblockSound; + int (*init) (struct snd_s *snd); + void (*shutdown) (struct snd_s *snd); + int (*get_dma_pos) (struct snd_s *snd); + void (*submit) (struct snd_s *snd); + void (*on_update) (struct snd_s *snd); + void (*block_sound) (struct snd_s *snd); + void (*unblock_sound) (struct snd_s *snd); } snd_output_funcs_t; +typedef enum { + som_push, // synchronous io (mixer pushes data to driver) + som_pull, // asynchronous io (driver pulls data from mixer) +} snd_output_model_t; + typedef struct snd_output_data_s { unsigned *soundtime; unsigned *paintedtime; + snd_output_model_t model; } snd_output_data_t; -#endif // __QF_plugin_snd_output_h_ +#endif // __QF_plugin_snd_output_h diff --git a/include/QF/plugin/snd_render.h b/include/QF/plugin/snd_render.h index 437367228..b06c42eb9 100644 --- a/include/QF/plugin/snd_render.h +++ b/include/QF/plugin/snd_render.h @@ -24,61 +24,52 @@ Boston, MA 02111-1307, USA */ -#ifndef __QF_plugin_snd_render_h_ -#define __QF_plugin_snd_render_h_ +#ifndef __QF_plugin_snd_render_h +#define __QF_plugin_snd_render_h #include #include - -/* - All sound plugins must export these functions -*/ +#include struct sfx_s; - -typedef void (*P_S_Init) (void); -typedef void (*P_S_Shutdown) (void); -typedef void (*P_S_AmbientOff) (void); -typedef void (*P_S_AmbientOn) (void); -typedef void (*P_S_StartSound) (int entnum, int entchannel, struct sfx_s *sfx, const vec3_t origin, float fvol, float attenuation); -typedef void (*P_S_StaticSound) (struct sfx_s *sfx, const vec3_t origin, float vol, float attenuation); -typedef void (*P_S_StopSound) (int entnum, int entchannel); -typedef struct sfx_s * (*P_S_PrecacheSound) (const char *sample); -typedef void (*P_S_Update) (const vec3_t origin, const vec3_t v_forward, const vec3_t v_right, const vec3_t v_up, const byte *ambient_sound_level); -typedef void (*P_S_StopAllSounds) (void); -typedef void (*P_S_ExtraUpdate) (void); -typedef void (*P_S_LocalSound) (const char *s); -typedef void (*P_S_BlockSound) (void); -typedef void (*P_S_UnblockSound) (void); -typedef struct sfx_s *(*P_S_LoadSound) (const char *name); -typedef struct channel_s *(*P_S_AllocChannel) (void); -typedef void (*P_S_ChannelStop) (struct channel_s *chan); +struct transform_s; typedef struct snd_render_funcs_s { - P_S_AmbientOff pS_AmbientOff; - P_S_AmbientOn pS_AmbientOn; - P_S_StaticSound pS_StaticSound; - P_S_StartSound pS_StartSound; - P_S_StopSound pS_StopSound; - P_S_PrecacheSound pS_PrecacheSound; - P_S_Update pS_Update; - P_S_StopAllSounds pS_StopAllSounds; - P_S_ExtraUpdate pS_ExtraUpdate; - P_S_LocalSound pS_LocalSound; - P_S_BlockSound pS_BlockSound; - P_S_UnblockSound pS_UnblockSound; - P_S_LoadSound pS_LoadSound; - P_S_AllocChannel pS_AllocChannel; - P_S_ChannelStop pS_ChannelStop; + void (*init) (void); + void (*ambient_off) (void); + void (*ambient_on) (void); + void (*set_ambient) (int amb_channel, struct sfx_s *sfx); + void (*static_sound) (struct sfx_s *sfx, vec4f_t origin, float vol, float attenuation); + void (*start_sound) (int entnum, int entchannel, struct sfx_s *sfx, const vec4f_t, float vol, float attenuation); + void (*local_sound) (const char *s); + void (*stop_sound) (int entnum, int entchannel); + + struct channel_s *(*alloc_channel) (void); + void (*channel_free) (struct channel_s *chan); + int (*channel_set_sfx) (struct channel_s *chan, struct sfx_s *sfx); + void (*channel_set_paused) (struct channel_s *chan, int paused); + void (*channel_set_looping) (struct channel_s *chan, int looping); + enum chan_state_e (*channel_get_state) (struct channel_s *chan); + void (*channel_set_volume) (struct channel_s *chan, float volume); + + struct sfx_s *(*precache_sound) (const char *sample); + struct sfx_s *(*load_sound) (const char *name); + + void (*update) (struct transform_s ear, + const byte *ambient_sound_levels); + void (*stop_all_sounds) (void); + void (*extra_update) (void); + void (*block_sound) (void); + void (*unblock_sound) (void); } snd_render_funcs_t; typedef struct snd_render_data_s { - double *host_frametime; - int *viewentity; + double *host_frametime; + int *viewentity; - unsigned int *soundtime; - unsigned int *paintedtime; + unsigned *soundtime; + unsigned *paintedtime; struct plugin_s *output; } snd_render_data_t; -#endif // __QF_plugin_snd_render_h_ +#endif // __QF_plugin_snd_render_h diff --git a/include/QF/plugin/vid_render.h b/include/QF/plugin/vid_render.h index 1c20ea8f0..9646a1a59 100644 --- a/include/QF/plugin/vid_render.h +++ b/include/QF/plugin/vid_render.h @@ -24,8 +24,8 @@ Boston, MA 02111-1307, USA */ -#ifndef __QF_plugin_vid_render_h_ -#define __QF_plugin_vid_render_h_ +#ifndef __QF_plugin_vid_render_h +#define __QF_plugin_vid_render_h #include #include @@ -33,72 +33,44 @@ #include #include +#include "QF/scene/entity.h"//FIXME + struct plitem_s; struct cvar_s; +struct scene_s; struct skin_s; struct particle_s; +struct mod_alias_ctx_s; +struct mod_sprite_ctx_s; +struct entqueue_s; +struct framebuffer_s; +struct vrect_s; +struct texture_s; + /* All video plugins must export these functions */ -typedef struct vid_particle_funcs_s { - void (*R_RocketTrail) (struct entity_s *ent); - void (*R_GrenadeTrail) (struct entity_s *ent); - void (*R_BloodTrail) (struct entity_s *ent); - void (*R_SlightBloodTrail) (struct entity_s *ent); - void (*R_WizTrail) (struct entity_s *ent); - void (*R_FlameTrail) (struct entity_s *ent); - void (*R_VoorTrail) (struct entity_s *ent); - void (*R_GlowTrail) (struct entity_s *ent, int glow_color); - - void (*R_RunParticleEffect) (const vec3_t org, const vec3_t dir, - int color, int count); - void (*R_BloodPuffEffect) (const vec3_t org, int count); - void (*R_GunshotEffect) (const vec3_t org, int count); - void (*R_LightningBloodEffect) (const vec3_t org); - void (*R_SpikeEffect) (const vec3_t org); - void (*R_KnightSpikeEffect) (const vec3_t org); - void (*R_SuperSpikeEffect) (const vec3_t org); - void (*R_WizSpikeEffect) (const vec3_t org); - - void (*R_BlobExplosion) (const vec3_t org); - void (*R_ParticleExplosion) (const vec3_t org); - void (*R_ParticleExplosion2) (const vec3_t org, int colorStart, - int colorLength); - void (*R_LavaSplash) (const vec3_t org); - void (*R_TeleportSplash) (const vec3_t org); - void (*R_DarkFieldParticles) (struct entity_s *ent); - void (*R_EntityParticles) (struct entity_s *ent); - - void (*R_Particle_New) (const char *type, int texnum, const vec3_t org, - float scale, const vec3_t vel, float die, - int color, float alpha, float ramp); - void (*R_Particle_NewRandom) (const char *type, int texnum, - const vec3_t org, int org_fuzz, float scale, - int vel_fuzz, float die, int color, - float alpha, float ramp); -} vid_particle_funcs_t; - typedef struct vid_model_funcs_s { - void (*Mod_LoadExternalTextures) (model_t *mod); - void (*Mod_LoadLighting) (bsp_t *bsp); - void (*Mod_SubdivideSurface) (msurface_t *fa); - void (*Mod_ProcessTexture) (texture_t *tx); + size_t texture_render_size;// size of renderer specific texture data + void (*Mod_LoadLighting) (model_t *mod, bsp_t *bsp); + void (*Mod_SubdivideSurface) (model_t *mod, msurface_t *fa); + void (*Mod_ProcessTexture) (model_t *mod, struct texture_s *tx); void (*Mod_LoadIQM) (model_t *mod, void *buffer); void (*Mod_LoadAliasModel) (model_t *mod, void *buffer, cache_allocator_t allocator); void (*Mod_LoadSpriteModel) (model_t *mod, void *buffer); - void (*Mod_MakeAliasModelDisplayLists) (model_t *m, aliashdr_t *hdr, + void (*Mod_MakeAliasModelDisplayLists) (struct mod_alias_ctx_s *alias_ctx, void *_m, int _s, int extra); - void *(*Mod_LoadSkin) (byte *skin, int skinsize, int snum, int gnum, - qboolean group, maliasskindesc_t *skindesc); - void (*Mod_FinalizeAliasModel) (model_t *m, aliashdr_t *hdr); - void (*Mod_LoadExternalSkins) (model_t *mod); + void (*Mod_LoadAllSkins) (struct mod_alias_ctx_s *alias_ctx); + void (*Mod_FinalizeAliasModel) (struct mod_alias_ctx_s *alias_ctx); + void (*Mod_LoadExternalSkins) (struct mod_alias_ctx_s *alias_ctx); void (*Mod_IQMFinish) (model_t *mod); int alias_cache; - void (*Mod_SpriteLoadTexture) (mspriteframe_t *pspriteframe, int framenum); + void (*Mod_SpriteLoadFrames) (struct mod_sprite_ctx_s *sprite_ctx); + void (*Skin_Free) (struct skin_s *skin); struct skin_s *(*Skin_SetColormap) (struct skin_s *skin, int cmap); struct skin_s *(*Skin_SetSkin) (struct skin_s *skin, int cmap, const char *skinname); @@ -108,8 +80,18 @@ typedef struct vid_model_funcs_s { void (*Skin_InitTranslations) (void); } vid_model_funcs_t; +struct tex_s; +struct font_s; +struct draw_charbuffer_s; + +typedef void (*capfunc_t) (struct tex_s *screencap, void *data); + typedef struct vid_render_funcs_s { - void (*Draw_Init) (void); + void (*init) (void); + void (*UpdateScreen) (struct transform_s camera, double realtime, + SCR_Func *scr_funcs); + void (*Draw_CharBuffer) (int x, int y, struct draw_charbuffer_s *buffer); + void (*Draw_SetScale) (int scale); void (*Draw_Character) (int x, int y, unsigned ch); void (*Draw_String) (int x, int y, const char *str); void (*Draw_nString) (int x, int y, const char *str, int count); @@ -119,87 +101,69 @@ typedef struct vid_render_funcs_s { void (*Draw_CrosshairAt) (int ch, int x, int y); void (*Draw_TileClear) (int x, int y, int w, int h); void (*Draw_Fill) (int x, int y, int w, int h, int c); + void (*Draw_Line) (int x0, int y0, int x1, int y1, int c); void (*Draw_TextBox) (int x, int y, int width, int lines, byte alpha); void (*Draw_FadeScreen) (void); void (*Draw_BlendScreen) (quat_t color); - qpic_t *(*Draw_CachePic) (const char *path, qboolean alpha); + qpic_t *(*Draw_CachePic) (const char *path, bool alpha); void (*Draw_UncachePic) (const char *path); qpic_t *(*Draw_MakePic) (int width, int height, const byte *data); void (*Draw_DestroyPic) (qpic_t *pic); qpic_t *(*Draw_PicFromWad) (const char *name); void (*Draw_Pic) (int x, int y, qpic_t *pic); + void (*Draw_FitPic) (int x, int y, int width, int height, qpic_t *pic); void (*Draw_Picf) (float x, float y, qpic_t *pic); void (*Draw_SubPic) (int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height); + int (*Draw_AddFont) (struct font_s *font); + void (*Draw_Glyph) (int x, int y, int fontid, int glyphid, int c); - // scr_funcs is a null terminated array - void (*SCR_UpdateScreen) (double realtime, SCR_Func scr_3dfunc, - SCR_Func *scr_funcs); - void (*SCR_DrawRam) (void); - void (*SCR_DrawTurtle) (void); - void (*SCR_DrawPause) (void); - struct tex_s *(*SCR_CaptureBGR) (void); - struct tex_s *(*SCR_ScreenShot) (int width, int height); - void (*SCR_DrawStringToSnap) (const char *s, struct tex_s *tex, - int x, int y); - - void (*Fog_Update) (float density, float red, float green, float blue, - float time); - void (*Fog_ParseWorldspawn) (struct plitem_s *worldspawn); - + struct psystem_s *(*ParticleSystem) (void); void (*R_Init) (void); void (*R_ClearState) (void); void (*R_LoadSkys) (const char *); - void (*R_NewMap) (model_t *worldmodel, model_t **models, int num_models); - void (*R_AddEfrags) (entity_t *ent); - void (*R_RemoveEfrags) (entity_t *ent); - void (*R_EnqueueEntity) (struct entity_s *ent); //FIXME should not be here - void (*R_LineGraph) (int x, int y, int *h_vals, int count); - dlight_t *(*R_AllocDlight) (int key); - entity_t *(*R_AllocEntity) (void); - void (*R_RenderView) (void); - void (*R_DecayLights) (double frametime); + void (*R_NewScene) (struct scene_s *scene); + void (*R_LineGraph) (int x, int y, int *h_vals, int count, int height); - void (*R_ViewChanged) (float aspect); - qboolean (*R_CompileParticlePhysics) (const char *name, const char *code); - qboolean (*R_AddParticlePhysicsFunction) - (const char *name, qboolean (*func) (struct particle_s *, void *), - void *data); - void (*R_ClearParticles) (void); - void (*R_InitParticles) (void); - void (*SCR_ScreenShot_f) (void); - void (*r_easter_eggs_f) (struct cvar_s *var); - void (*r_particles_style_f) (struct cvar_s *var); + void (*begin_frame) (void); + void (*render_view) (void); + void (*draw_particles) (struct psystem_s *psystem); + void (*draw_transparent) (void); + void (*post_process) (struct framebuffer_s *src); + void (*set_2d) (int scaled); + void (*end_frame) (void); + + struct framebuffer_s *(*create_cube_map) (int side); + struct framebuffer_s *(*create_frame_buffer) (int width, int height); + void (*destroy_frame_buffer) (struct framebuffer_s *framebuffer); + void (*bind_framebuffer) (struct framebuffer_s *framebuffer); + void (*set_viewport) (const struct vrect_s *view); + // x and y are tan(f/2) for fov_x and fov_y + void (*set_fov) (float x, float y); + + void (*capture_screen) (capfunc_t callback, void *data); - vid_particle_funcs_t *particles; vid_model_funcs_t *model_funcs; } vid_render_funcs_t; typedef struct vid_render_data_s { viddef_t *vid; refdef_t *refdef; - vrect_t *scr_vrect; int scr_copytop; int scr_copyeverything; int scr_fullupdate; // set to 0 to force full redraw - void (*viewsize_callback) (struct cvar_s *); - struct cvar_s *scr_viewsize; - struct cvar_s *graphheight; + void (*viewsize_callback) (int view_size); + int *scr_viewsize; + int *graphheight; float min_wateralpha; - qboolean force_fullscreen; - qboolean inhibit_viewmodel; - qboolean paused; + bool force_fullscreen; + bool inhibit_viewmodel; + bool paused; int lineadj; - struct entity_s *view_model; - struct entity_s *player_entity; - float gravity; + entity_t view_model; //FIXME still?!? double frametime; double realtime; lightstyle_t *lightstyle; - vec_t *origin; - vec_t *vpn; - vec_t *vright; - vec_t *vup; } vid_render_data_t; -#endif // __QF_plugin_vid_render_h_ +#endif // __QF_plugin_vid_render_h diff --git a/include/QF/png.h b/include/QF/png.h index c3e2ed562..62c69a305 100644 --- a/include/QF/png.h +++ b/include/QF/png.h @@ -33,9 +33,7 @@ #include "QF/quakefs.h" -struct tex_s *LoadPNG (QFile *infile); -void WritePNG (const char *fileName, const byte *data, int width, int height); -void WritePNGqfs (const char *fileName, const byte *data, - int width, int height); +struct tex_s *LoadPNG (QFile *infile, int load); +int WritePNG (QFile *outfile, const struct tex_s *tex); #endif//__QF_png_h diff --git a/include/QF/pqueue.h b/include/QF/pqueue.h new file mode 100644 index 000000000..7ee3c73d8 --- /dev/null +++ b/include/QF/pqueue.h @@ -0,0 +1,124 @@ +/* + pqueue.h + + Priority Queues + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/08/02 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_pqueue_h +#define __QF_pqueue_h + +#include + +#include "QF/heapsort.h" +#include "QF/sys.h" + +/** \defgroup pqueue Priority Queues + \ingroup utils + + Priority queue management +*/ +///@{ + +/** The structure def for a priority queue with elements of the given type. + + This is just the def of a struct delcaration: it is useless on its own. + The intended usage is something like: + + typedef struct priority_queue_s PQUEUE_TYPE (int) priority_queue_t; + + This allows full flexibility in just how the actual type is used. + + The \a size field is the number of elements currently in the queue, and + the \a maxSize field is the number of elements the queue can hold. The + priority queue functions will never resize the array. However, with a + little glue logic, priority queues can be used with dynamic arrays. + + \param T The type to use for the element array, which is accessed + by the \a a field. +*/ +#define PQUEUE_TYPE(T) \ + { \ + size_t size; \ + size_t maxSize; \ + T *a; \ + int (*compare) (const T *a, const T *b, void *data); \ + void *data; \ + } + +#define PQUEUE_IS_EMPTY(queue) ((queue)->size == 0) + +#define PQUEUE_IS_FULL(queue) \ + ({ \ + __auto_type q_f = (queue); \ + q_f->size == q_f->maxSize; \ + }) + +#define PQUEUE_PEEK(queue) ((queue)->a[0]) + +#define PQUEUE_INSERT(queue, value) \ + do { \ + __auto_type q = (queue); \ + if (PQUEUE_IS_FULL (q)) { \ + Sys_Error ("Priority queue full"); \ + } \ + q->a[q->size++] = (value); \ + heap_swim_r (q->a, q->size - 1, q->size, sizeof (q->a[0]), \ + (__compar_d_fn_t) q->compare, q->data); \ + } while (0) + +#define PQUEUE_REMOVE(queue) \ + ({ \ + __auto_type q = (queue); \ + if (PQUEUE_IS_EMPTY (q)) { \ + Sys_Error ("Priority queue empty"); \ + } \ + __auto_type value = q->a[0]; \ + q->a[0] = q->a[--q->size]; \ + heap_sink_r (q->a, 0, q->size, sizeof (q->a[0]), \ + (__compar_d_fn_t) q->compare, q->data); \ + value; \ + }) + +#define PQUEUE_ADJUST(queue, index) \ + do { \ + __auto_type q = (queue); \ + size_t ind = (index); \ + if (ind >= q->size) { \ + Sys_Error ("Priority queue index error"); \ + } \ + if (ind>0 && q->compare(q->a + ind, q->a + (ind-1)/2, q->data)>0) { \ + heap_swim_r (q->a, ind, q->size, sizeof (q->a[0]), \ + (__compar_d_fn_t) q->compare, q->data); \ + } else { \ + heap_sink_r (q->a, ind, q->size, sizeof (q->a[0]), \ + (__compar_d_fn_t) q->compare, q->data); \ + } \ + } while (0) + +///@} + +#endif//__QF_pqueue_h diff --git a/include/QF/pr_comp.h b/include/QF/pr_comp.h deleted file mode 100644 index 4cc274f98..000000000 --- a/include/QF/pr_comp.h +++ /dev/null @@ -1,393 +0,0 @@ -/* Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - See file, 'COPYING', for details. -*/ - -// this file is shared by QuakeForge and qfcc -#ifndef __pr_comp_h -#define __pr_comp_h - -#include "QF/qtypes.h" - -typedef int16_t pr_short_t; -typedef uint16_t pr_ushort_t; -typedef int32_t pr_int_t; -typedef uint32_t pr_uint_t; -typedef pr_int_t func_t; -typedef pr_int_t string_t; -typedef pr_uint_t pointer_t; - -typedef enum { - ev_void, - ev_string, - ev_float, - ev_vector, - ev_entity, - ev_field, - ev_func, - ev_pointer, // end of v6 types - ev_quat, - ev_integer, - ev_uinteger, - ev_short, // value is embedded in the opcode - - ev_invalid, // invalid type. used for instruction checking - ev_type_count // not a type, gives number of types -} etype_t; - -extern int pr_type_size[ev_type_count]; -extern const char *pr_type_name[ev_type_count]; - -#define OFS_NULL 0 -#define OFS_RETURN 1 -#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors -#define OFS_PARM1 7 -#define OFS_PARM2 10 -#define OFS_PARM3 13 -#define OFS_PARM4 16 -#define OFS_PARM5 19 -#define OFS_PARM6 22 -#define OFS_PARM7 25 -#define RESERVED_OFS 28 - - -typedef enum { - OP_DONE, - OP_MUL_F, - OP_MUL_V, - OP_MUL_FV, - OP_MUL_VF, - OP_DIV_F, - OP_ADD_F, - OP_ADD_V, - OP_SUB_F, - OP_SUB_V, - - OP_EQ_F, - OP_EQ_V, - OP_EQ_S, - OP_EQ_E, - OP_EQ_FN, - - OP_NE_F, - OP_NE_V, - OP_NE_S, - OP_NE_E, - OP_NE_FN, - - OP_LE_F, - OP_GE_F, - OP_LT_F, - OP_GT_F, - - OP_LOAD_F, - OP_LOAD_V, - OP_LOAD_S, - OP_LOAD_ENT, - OP_LOAD_FLD, - OP_LOAD_FN, - - OP_ADDRESS, - - OP_STORE_F, - OP_STORE_V, - OP_STORE_S, - OP_STORE_ENT, - OP_STORE_FLD, - OP_STORE_FN, - - OP_STOREP_F, - OP_STOREP_V, - OP_STOREP_S, - OP_STOREP_ENT, - OP_STOREP_FLD, - OP_STOREP_FN, - - OP_RETURN, - OP_NOT_F, - OP_NOT_V, - OP_NOT_S, - OP_NOT_ENT, - OP_NOT_FN, - OP_IF, - OP_IFNOT, - OP_CALL0, - OP_CALL1, - OP_CALL2, - OP_CALL3, - OP_CALL4, - OP_CALL5, - OP_CALL6, - OP_CALL7, - OP_CALL8, - OP_STATE, - OP_GOTO, - OP_AND, - OP_OR, - - OP_BITAND, - OP_BITOR, - - OP_ADD_S, - OP_LE_S, - OP_GE_S, - OP_LT_S, - OP_GT_S, - - OP_ADD_I, - OP_SUB_I, - OP_MUL_I, - OP_DIV_I, - OP_BITAND_I, - OP_BITOR_I, - OP_GE_I, - OP_LE_I, - OP_GT_I, - OP_LT_I, - OP_AND_I, - OP_OR_I, - OP_NOT_I, - OP_EQ_I, - OP_NE_I, - OP_STORE_I, - OP_STOREP_I, - OP_LOAD_I, - - OP_CONV_IF, - OP_CONV_FI, - - OP_BITXOR_F, - OP_BITXOR_I, - OP_BITNOT_F, - OP_BITNOT_I, - - OP_SHL_F, - OP_SHR_F, - OP_SHL_I, - OP_SHR_I, - - OP_MOD_F, - OP_MOD_I, - - OP_LOADB_F, - OP_LOADB_V, - OP_LOADB_S, - OP_LOADB_ENT, - OP_LOADB_FLD, - OP_LOADB_FN, - OP_LOADB_I, - OP_LOADB_P, - - OP_STOREB_F, - OP_STOREB_V, - OP_STOREB_S, - OP_STOREB_ENT, - OP_STOREB_FLD, - OP_STOREB_FN, - OP_STOREB_I, - OP_STOREB_P, - - OP_ADDRESS_VOID, - OP_ADDRESS_F, - OP_ADDRESS_V, - OP_ADDRESS_S, - OP_ADDRESS_ENT, - OP_ADDRESS_FLD, - OP_ADDRESS_FN, - OP_ADDRESS_I, - OP_ADDRESS_P, - - OP_LEA, - - OP_IFBE, - OP_IFB, - OP_IFAE, - OP_IFA, - - OP_JUMP, - OP_JUMPB, - - OP_LT_U, - OP_GT_U, - OP_LE_U, - OP_GE_U, - - OP_LOADBI_F, - OP_LOADBI_V, - OP_LOADBI_S, - OP_LOADBI_ENT, - OP_LOADBI_FLD, - OP_LOADBI_FN, - OP_LOADBI_I, - OP_LOADBI_P, - - OP_STOREBI_F, - OP_STOREBI_V, - OP_STOREBI_S, - OP_STOREBI_ENT, - OP_STOREBI_FLD, - OP_STOREBI_FN, - OP_STOREBI_I, - OP_STOREBI_P, - - OP_LEAI, - - OP_LOAD_P, - OP_STORE_P, - OP_STOREP_P, - OP_NOT_P, - OP_EQ_P, - OP_NE_P, - OP_LE_P, - OP_GE_P, - OP_LT_P, - OP_GT_P, - - OP_MOVEI, - OP_MOVEP, - OP_MOVEPI, - - OP_SHR_U, - - OP_STATE_F, - - OP_ADD_Q, - OP_SUB_Q, - OP_MUL_Q, - OP_MUL_QF, - OP_MUL_FQ, - OP_MUL_QV, - OP_CONJ_Q, - OP_NOT_Q, - OP_EQ_Q, - OP_NE_Q, - OP_STORE_Q, - OP_STOREB_Q, - OP_STOREBI_Q, - OP_STOREP_Q, - OP_LOAD_Q, - OP_LOADB_Q, - OP_LOADBI_Q, - OP_ADDRESS_Q, - - OP_RCALL0, - OP_RCALL1, - OP_RCALL2, - OP_RCALL3, - OP_RCALL4, - OP_RCALL5, - OP_RCALL6, - OP_RCALL7, - OP_RCALL8, - - OP_RETURN_V, -} pr_opcode_e; - -typedef struct opcode_s { - const char *name; - const char *opname; - pr_opcode_e opcode; - qboolean right_associative; - etype_t type_a, type_b, type_c; - unsigned int min_version; - const char *fmt; -} opcode_t; - -extern opcode_t pr_opcodes[]; -opcode_t *PR_Opcode (pr_short_t opcode); -void PR_Opcode_Init (void); - -typedef struct dstatement_s { - pr_opcode_e op:16; - pr_ushort_t a,b,c; -} GCC_STRUCT dstatement_t; - -typedef struct ddef_s { - pr_ushort_t type; // if DEF_SAVEGLOBGAL bit is set - // the variable needs to be saved in savegames - pr_ushort_t ofs; - pr_int_t s_name; -} ddef_t; - -#define DEF_SAVEGLOBAL (1<<15) - -#define MAX_PARMS 8 - -typedef struct dfunction_s { - pr_int_t first_statement; // negative numbers are builtins - pr_int_t parm_start; - pr_int_t locals; // total ints of parms + locals - - pr_int_t profile; // runtime - - pr_int_t s_name; - pr_int_t s_file; // source file defined in - - pr_int_t numparms; - uint8_t parm_size[MAX_PARMS]; -} dfunction_t; - -typedef union pr_type_u { - float float_var; - string_t string_var; - func_t func_var; - pr_int_t entity_var; - float vector_var[1]; // really 3, but this structure must be 32 bits - float quat_var[1]; // really 4, but this structure must be 32 bits - pr_int_t integer_var; - pointer_t pointer_var; - pr_uint_t uinteger_var; -} pr_type_t; - -typedef struct pr_va_list_s { - pr_int_t count; - pointer_t list; // pr_type_t -} pr_va_list_t; - -#define PROG_VERSION_ENCODE(a,b,c) \ - ( (((0x##a) & 0x0ff) << 24) \ - |(((0x##b) & 0xfff) << 12) \ - |(((0x##c) & 0xfff) << 0) ) -#define PROG_ID_VERSION 6 -#define PROG_VERSION PROG_VERSION_ENCODE(0,fff,009) - -typedef struct dprograms_s { - pr_uint_t version; - pr_uint_t crc; // check of header file - - pr_uint_t ofs_statements; - pr_uint_t numstatements; // statement 0 is an error - - pr_uint_t ofs_globaldefs; - pr_uint_t numglobaldefs; - - pr_uint_t ofs_fielddefs; - pr_uint_t numfielddefs; - - pr_uint_t ofs_functions; - pr_int_t numfunctions; // function 0 is an empty - - pr_uint_t ofs_strings; - pr_int_t numstrings; // first string is a null string - - pr_uint_t ofs_globals; - pr_uint_t numglobals; - - pr_uint_t entityfields; -} dprograms_t; - -#endif // __pr_comp_h diff --git a/include/QF/progs.h b/include/QF/progs.h index 7c8bb3341..800e03816 100644 --- a/include/QF/progs.h +++ b/include/QF/progs.h @@ -33,42 +33,56 @@ \image latex vm-mem.eps "VM memory map" */ -#include "QF/pr_comp.h" -#include "QF/pr_debug.h" +#include "QF/math/bitop.h" +#include "QF/progs/pr_comp.h" +#include "QF/progs/pr_debug.h" struct QFile_s; /** \ingroup progs */ -//@{ +///@{ typedef struct progs_s progs_t; typedef struct pr_resource_s pr_resource_t; typedef struct edict_s edict_t; -//@} +///@} //============================================================================ /** \defgroup progs_misc Miscelaneous functions \ingroup progs */ -//@{ +///@{ /** Initialize the progs engine. + + The first call will initialize subsystems common to all progs instances. + + \param pr The progs engine instance to initialize. */ -void PR_Init (void); +void PR_Init (progs_t *pr); + +/** Shut down the progs engine. + + Shuts down only the specified progs engine. Frees resources allocated + during progs init. + + \param pr The progs engine instance to shut down. +*/ +void PR_Shutdown (progs_t *pr); /** Initialize the Cvars for the progs engine. Call before calling PR_Init(). */ void PR_Init_Cvars (void); -void PR_Error (progs_t *pr, const char *error, ...) __attribute__((format(printf,2,3), noreturn)); -void PR_RunError (progs_t *pr, const char *error, ...) __attribute__((format(printf,2,3), noreturn)); +void PR_Error (progs_t *pr, const char *error, ...) __attribute__((format(PRINTF,2,3), noreturn)); +void PR_RunError (progs_t *pr, const char *error, ...) __attribute__((format(PRINTF,2,3), noreturn)); -//@} +///@} /** \defgroup progs_execution Execution \ingroup progs */ -//@{ +///@{ /** Ensure P_* macros point to the right place for passing parameters to progs functions. @@ -77,31 +91,109 @@ void PR_RunError (progs_t *pr, const char *error, ...) __attribute__((format(pri \warning Failure to use this macro before assigning to the P_* macros can cause corruption of the VM data due to "register" based calling. Can be safely ignored for parameterless functions, or forwarding parameters - though a builtin. + though a builtin. However, it is ok (and encouraged) to call + PR_SetupParams instead, as this macro calls PR_SetupParams with + PR_MAX_PARAMS and 1 for the alignment. \hideinitializer */ -#define PR_RESET_PARAMS(pr) \ - do { \ - (pr)->pr_params[0] = (pr)->pr_real_params[0]; \ - (pr)->pr_params[1] = (pr)->pr_real_params[1]; \ - } while (0) +#define PR_RESET_PARAMS(pr) PR_SetupParams (pr, PR_MAX_PARAMS, 1) + +/** \name Detouring Function Calls + + These functions allow a builtin function that uses PR_CallFunction() to + safely insert a call to another VM function. The +initialize diversion + required by Objective-QuakeC uses this. + + PR_PushFrame (pr); + __auto_type params = PR_SaveParams (pr); + ... set up parameters to detour_function + PR_ExecuteProgram (pr, detour_function) + PR_RestoreParams (pr, params); + PR_PopFrame (pr); -/** Save the current parameters. - \param pr pointer to ::progs_t VM struct */ -void PR_SaveParams (progs_t *pr); +///@{ +typedef struct pr_stashed_params_s { + pr_type_t *param_ptrs[2]; + int argc; + pr_type_t params[1]; +} pr_stashed_params_t; + +/** Save the current parameters to the provided stash. + + \warning The memory for the parameter stash is allocated using + alloca(). + + \param pr pointer to ::progs_t VM struct + \return Pointer to a newly allocated and initialized parameter + stash that has the current parameters saved to it. + \hideinitializer +*/ +#define PR_SaveParams(pr) \ + _PR_SaveParams((pr), \ + alloca (field_offset (pr_stashed_params_t, \ + params[(pr)->pr_argc \ + * (pr)->pr_param_size]))) + +/** [INTERNAL] Save the current parameters to the provided stash. + + \warning Requires \a params to be correctly allocated. Use + PR_SaveParams instead as it will create a suitable stash for + saving the parameters. + + \param pr pointer to ::progs_t VM struct + \param params location to save the parameters, must be of adequade size + to hold \a pr_argc * \a pr_param_size words in \a params + \return \a params Allows the likes of: + __auto_type params = PR_SaveParams (pr); +*/ +pr_stashed_params_t *_PR_SaveParams (progs_t *pr, pr_stashed_params_t *params); /** Restore the parameters saved by PR_SaveParams(). \param pr pointer to ::progs_t VM struct + \param params pointer to stash created by PR_SaveParams() */ -void PR_RestoreParams (progs_t *pr); +void PR_RestoreParams (progs_t *pr, pr_stashed_params_t *params); +///@} /** Push an execution frame onto the VM stack. Saves current execution state. \param pr pointer to ::progs_t VM struct */ void PR_PushFrame (progs_t *pr); +/** Reserve space on the data stack and set up the param pointers. + + For v6p progs, this only sets up the param pointers as v6p progs do not + have a data stack. + + For Ruamoko progs, space for at least \a num_params (each being 4 words) + is created on the stack, with a minimum alignment of min_alignment words, + or 4, whichever is larger. + + \param pr pointer to ::progs_t VM struct + \param num_params Number of parameter slots needed for the function call. + Each slot is 4 words. dvec4 and lvec4 parameters require + 8 words and must be 8-word aligned. dvec3 and lvec3 + also require 8 words due to the minimum 4 word alignment, + but have no alignment requirements themselves. Be sure to + take this into account in size calculations. + \param min_alignment Minimum number of words to which the stack will be + aligned. Must be a power of two. Note that when passing + dvec4 or lvec4 parameters, they have a hardware-enforced + requirement of 8 word alignment. This means that for + something like (int, lvec4), there will be an unused + parameter slot between the int and the lvec4. + Ignored for v6p progs. + \return Pointer to the base of the created parameter area. For + v6p progs, this is just .param_0, but for Ruamoko progs + this will be the current top of the the data stack after + adjustment for the parameter space. + \note Attempting to pass more than PR_MAX_PARAMS parameters to v6p progs + is a hard error. +*/ +pr_type_t *PR_SetupParams (progs_t *pr, int num_params, int min_alignment); + /** Pop an execution frame from the VM stack. Restores execution state. Also frees any temporary strings allocated in this frame (via PR_FreeTempStrings()). @@ -114,8 +206,9 @@ void PR_PopFrame (progs_t *pr); return to the caller. Nested calls are fully supported. \param pr pointer to ::progs_t VM struct \param fnum number of the function to call + \note Calls PR_CallFunction() */ -void PR_ExecuteProgram (progs_t *pr, func_t fnum); +void PR_ExecuteProgram (progs_t *pr, pr_func_t fnum); /** Setup to call a function. If \p fnum is a builtin rather than a progs function, then the function is called immediately. When called from a @@ -123,17 +216,22 @@ void PR_ExecuteProgram (progs_t *pr, func_t fnum); execute upon return of control to PR_ExecuteProgram(). \param pr pointer to ::progs_t VM struct \param fnum number of the function to call + \param return_ptr pointer to location in which return values will be + written \return true if \p fnum was a progs function, false if \p fnum was a builtin + \note Called by PR_ExecuteProgram, so the only time this should be called + is in a builtin function that calls a progs function and returns + immediately (eg, to implement `return progsfunc();`). */ -int PR_CallFunction (progs_t *pr, func_t fnum); +int PR_CallFunction (progs_t *pr, pr_func_t fnum, pr_type_t *return_ptr); -//@} +///@} /** \defgroup progs_load Loading \ingroup progs */ -//@{ +///@{ /** Type of functions that are called at progs load. \param pr pointer to ::progs_t VM struct @@ -143,29 +241,24 @@ typedef int pr_load_func_t (progs_t *pr); /** Initialize a ::progs_t VM struct from an already open file. \param pr pointer to ::progs_t VM struct - \param file handle of file to read progs data from + \param file handle of file from which to read progs data \param size bytes of \p file to read - \param max_edicts \e number of entities to allocate space for - \param zone minimum size of dynamic memory to allocate space for - dynamic memory (bytes). \note \e All runtime strings (permanent or temporary) are allocated from - the VM's dynamic memory space, so be sure \p zone is of sufficient size. - So far, 1MB has proven more than sufficient for Quakeword, even when using - Ruamoko objects. + the VM's dynamic memory space, so be sure \p zone is of sufficient size + (by setting pr->zone_size prior to calling). So far, 1MB has proven more + than sufficient for Quakeword, even when using Ruamoko objects. + \note If entities are used, ensure pr->max_edicts is set appropriately + prior to calling. */ -void PR_LoadProgsFile (progs_t *pr, struct QFile_s *file, int size, - int max_edicts, int zone); +void PR_LoadProgsFile (progs_t *pr, struct QFile_s *file, int size); /** Convenience wrapper for PR_LoadProgsFile() and PR_RunLoadFuncs(). Searches for the specified file in the Quake filesystem. \param pr pointer to ::progs_t VM struct \param progsname name of the file to load as progs data - \param max_edicts \e number of entities to allocate space for - \param zone minimum size of dynamic memory to allocate space for */ -void PR_LoadProgs (progs_t *pr, const char *progsname, int max_edicts, - int zone); +void PR_LoadProgs (progs_t *pr, const char *progsname); /** Register a primary function to be called after the progs code has been loaded. These functions are remembered across progs loads. They will be @@ -189,14 +282,24 @@ void PR_AddLoadFinishFunc (progs_t *pr, pr_load_func_t *func); \return true for success, false for failure Calls the first set of internal load functions followed by the supplied - symbol resolution function, if any (progs_t::resolve), the second set of - internal load functions. After that, any primary load functions are called - in order of registration followed by any \c .ctor functions in the progs, - then any secondary load functions the primary load functions registered - in \e reverse order of registration. + symbol resolution function, if any (progs_t::resolve), then the second set + of internal load functions. After that, any primary load functions are + called in order of registration, and if there is no debug handler, + PR_RunPostLoadFuncs() is called. */ int PR_RunLoadFuncs (progs_t *pr); +/** Run any progs-dependent load functions. + \param pr pointer to ::progs_t VM struct + \return true for success, false for failure + This means any \c .ctor functions in the progs, followed by any secondary + load functions registered by either the primary load functions or the + \.c ctor functions in \e reverse order of registration. This is called + automatically by PR_RunLoadFuncs() if there is no debug handler, otherwise + it is up to the host to call this function. +*/ +int PR_RunPostLoadFuncs (progs_t *pr); + /** Validate the opcodes and statement addresses in the progs. This is an internal load function. \param pr pointer to ::progs_t VM struct @@ -205,80 +308,86 @@ int PR_RunLoadFuncs (progs_t *pr); \todo should this be elsewhere? */ int PR_Check_Opcodes (progs_t *pr); +int PR_Check_v6p_Opcodes (progs_t *pr); -void PR_BoundsCheckSize (progs_t *pr, pointer_t addr, unsigned size); +void PR_BoundsCheckSize (progs_t *pr, pr_ptr_t addr, unsigned size); void PR_BoundsCheck (progs_t *pr, int addr, etype_t type); -//@} +///@} /** \defgroup progs_edict Edict management \ingroup progs */ -//@{ +///@{ struct edict_s { - qboolean free; - int entnum; ///< number of this entity + bool free; + progs_t *pr; ///< progs owning this edict + pr_uint_t entnum; ///< number of this entity + pr_uint_t edict; ///< offset of this entity in pr_edict_area float freetime; ///< sv.time when the object was freed void *edata; ///< external per-edict data - pr_type_t v[1]; ///< fields from progs }; // pr_edict.c void ED_ClearEdict (progs_t *pr, edict_t *e, int val); edict_t *ED_Alloc (progs_t *pr); void ED_Free (progs_t *pr, edict_t *ed); -edict_t *ED_EdictNum(progs_t *pr, pr_int_t n); -pr_int_t ED_NumForEdict(progs_t *pr, edict_t *e); +edict_t *ED_EdictNum(progs_t *pr, pr_uint_t n) __attribute__((pure)); +pr_uint_t ED_NumForEdict(progs_t *pr, edict_t *e) __attribute__((pure)); void ED_Count (progs_t *pr); -qboolean PR_EdictValid (progs_t *pr, pr_int_t e); +bool PR_EdictValid (progs_t *pr, pr_uint_t e) __attribute__((pure)); // pr_debug.c -void ED_Print (progs_t *pr, edict_t *ed); +void ED_Print (progs_t *pr, edict_t *ed, const char *fieldname); void ED_PrintEdicts (progs_t *pr, const char *fieldval); -void ED_PrintNum (progs_t *pr, pr_int_t ent); +void ED_PrintNum (progs_t *pr, pr_int_t ent, const char *fieldname); // pr_parse.c struct script_s; struct plitem_s; -qboolean ED_ParseEpair (progs_t *pr, pr_type_t *base, ddef_t *key, +struct hashctx_s; +bool ED_ParseEpair (progs_t *pr, pr_type_t *base, pr_def_t *key, const char *s); struct plitem_s *ED_EntityDict (progs_t *pr, edict_t *ed); struct plitem_s *ED_GlobalsDict (progs_t *pr); void ED_InitGlobals (progs_t *pr, struct plitem_s *globals); void ED_InitEntity (progs_t *pr, struct plitem_s *entity, edict_t *ent); -struct plitem_s *ED_ConvertToPlist (struct script_s *script, int nohack); +struct plitem_s *ED_ConvertToPlist (struct script_s *script, int nohack, + struct hashctx_s **hashctx); struct plitem_s *ED_Parse (progs_t *pr, const char *data); void ED_LoadFromFile (progs_t *pr, const char *data); -void ED_EntityParseFunction (progs_t *pr); +void ED_EntityParseFunction (progs_t *pr, void *data); -#define PR_edicts(p) ((byte *) *(p)->edicts) +#define PR_edicts(p) (*(p)->pr_edicts) -#define NEXT_EDICT(p,e) ((edict_t *) ((byte *) e + (p)->pr_edict_size)) -#define EDICT_TO_PROG(p,e) ((pr_int_t)(intptr_t)((byte *)(e) - PR_edicts (p))) -#define PROG_TO_EDICT(p,e) ((edict_t *) (PR_edicts (p) + (e))) -#define NUM_FOR_BAD_EDICT(p,e) ((e)->entnum) +#define NEXT_EDICT(p,e) ((e) ? (e) + 1 : 0) +#define EDICT_TO_PROG(p,e) ((e) ? (e)->entnum * (p)->pr_edict_size : 0) +#define PROG_TO_EDICT(p,e) (&PR_edicts(p)[(e) / (p)->pr_edict_size]) +#define NUM_FOR_BAD_EDICT(p,e) ((e) ? (e)->entnum : 0) #ifndef PR_PARANOID_PROGS -# define EDICT_NUM(p,n) (PROG_TO_EDICT (p, (n) * (p)->pr_edict_size)) -# define NUM_FOR_EDICT(p,e) NUM_FOR_BAD_EDICT (p, e) +# define EDICT_NUM(p,n) (PR_edicts (p) + (n)) +# define NUM_FOR_EDICT(p,e) NUM_FOR_BAD_EDICT ((p), (e)) #else -# define EDICT_NUM(p,n) ED_EdictNum (p, n) -# define NUM_FOR_EDICT(p,e) ED_NumForEdict (p, e) +# define EDICT_NUM(p,n) ED_EdictNum ((p), (n)) +# define NUM_FOR_EDICT(p,e) ED_NumForEdict ((p), (e)) #endif -//@} +///@} /** \defgroup pr_symbols Symbol Management \ingroup progs Lookup functions for symbol name resolution. */ -//@{ +///@{ -ddef_t *PR_FieldAtOfs (progs_t *pr, pr_int_t ofs); -ddef_t *PR_GlobalAtOfs (progs_t *pr, pr_int_t ofs); +pr_def_t *PR_SearchDefs (pr_def_t *defs, unsigned num_defs, pr_ptr_t offset) + __attribute__((pure)); +pr_def_t *PR_FieldAtOfs (progs_t *pr, pr_ptr_t ofs) __attribute__((pure)); +pr_def_t *PR_GlobalAtOfs (progs_t *pr, pr_ptr_t ofs) __attribute__((pure)); -ddef_t *PR_FindField (progs_t *pr, const char *name); -ddef_t *PR_FindGlobal (progs_t *pr, const char *name); +pr_def_t *PR_FindField (progs_t *pr, const char *name); +pr_def_t *PR_FindGlobal (progs_t *pr, const char *name); dfunction_t *PR_FindFunction (progs_t *pr, const char *name); int PR_ResolveGlobals (progs_t *pr); @@ -287,7 +396,7 @@ int PR_AccessField (progs_t *pr, const char *name, etype_t type, const char *file, int line); void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute__((noreturn)); -//@} +///@} //============================================================================ @@ -301,7 +410,22 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ Typed global access macros. No checking is done against the QC type, but the appropriate C type will be used. */ -//@{ +///@{ + +/** Access a global as an arbitray type. + + More direct than G_STRUCT + \par QC type: + \c struct etc small enough to fit in a single parameter + \param p pointer to ::progs_t VM struct + \param t C type of the structure + \param o offset into global data space + \return structure lvalue. use & to make a pointer of the + appropriate type. + + \hideinitializer +*/ +#define G_PACKED(p,t,o) (*(t *) &(p)->pr_globals[o]) /** \internal \param p pointer to ::progs_t VM struct @@ -311,7 +435,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \hideinitializer */ -#define G_var(p,o,t) ((p)->pr_globals[o].t##_var) +#define G_var(p,o,t) G_PACKED(p, pr_##t##_t, o) /** Access a float global. Can be assigned to. @@ -325,29 +449,65 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ */ #define G_FLOAT(p,o) G_var (p, o, float) -/** Access an integer global. Can be assigned to. +/** Access a double global. Can be assigned to. \par QC type: - \c integer + \c double + \param p pointer to ::progs_t VM struct + \param o offset into global data space + \return double lvalue + + \hideinitializer +*/ +#define G_DOUBLE(p,o) G_var (p, o, double) + +/** Access an int global. Can be assigned to. + + \par QC type: + \c int \param p pointer to ::progs_t VM struct \param o offset into global data space \return int lvalue \hideinitializer */ -#define G_INT(p,o) G_var (p, o, integer) +#define G_INT(p,o) G_var (p, o, int) -/** Access an unsigned integer global. Can be assigned to. +/** Access an unsigned int global. Can be assigned to. \par QC type: - \c uinteger + \c uint \param p pointer to ::progs_t VM struct \param o offset into global data space \return unsigned int lvalue \hideinitializer */ -#define G_UINT(p,o) G_var (p, o, uinteger) +#define G_UINT(p,o) G_var (p, o, uint) + +/** Access a long global. Can be assigned to. + + \par QC type: + \c long + \param p pointer to ::progs_t VM struct + \param o offset into global data space + \return long lvalue + + \hideinitializer +*/ +#define G_LONG(p,o) G_var (p, o, long) + +/** Access an unsigned long global. Can be assigned to. + + \par QC type: + \c ulong + \param p pointer to ::progs_t VM struct + \param o offset into global data space + \return unsigned long lvalue + + \hideinitializer +*/ +#define G_ULONG(p,o) G_var (p, o, ulong) /** Access a vector global. Can be assigned to. @@ -359,7 +519,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \hideinitializer */ -#define G_VECTOR(p,o) G_var (p, o, vector) +#define G_VECTOR(p,o) (&G_var (p, o, float)) /** Access a quaternion global. Can be assigned to. @@ -371,7 +531,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \hideinitializer */ -#define G_QUAT(p,o) G_var (p, o, quat) +#define G_QUAT(p,o) (&G_var (p, o, float)) /** Access a string index global. Can be assigned to. @@ -379,7 +539,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \c string \param p pointer to ::progs_t VM struct \param o offset into global data space - \return string_t lvalue + \return pr_string_t lvalue \hideinitializer */ @@ -391,7 +551,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \c void() \param p pointer to ::progs_t VM struct \param o offset into global data space - \return func_t lvalue + \return pr_func_t lvalue \hideinitializer */ @@ -403,12 +563,35 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \c void * \param p pointer to ::progs_t VM struct \param o offset into global data space - \return pointer_t lvalue + \return pr_ptr_t lvalue \hideinitializer */ -#define G_POINTER(p,o) G_var (p, o, pointer) +#define G_POINTER(p,o) G_var (p, o, ptr) +/** Access a field global. + + \par QC type: + \c field + \param p pointer to ::progs_t VM struct + \param o offset into global data space + \return field offset + + \hideinitializer +*/ +#define G_FIELD(p,o) G_var (p, o, field) + +/** Access an entity global. + + \par QC type: + \c entity + \param p pointer to ::progs_t VM struct + \param o offset into global data space + \return entity "pointer" + + \hideinitializer +*/ +#define G_ENTITY(p,o) G_var (p, o, entity) /** Access an entity global. @@ -420,7 +603,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \hideinitializer */ -#define G_EDICT(p,o) ((edict_t *)(PR_edicts (p) + G_INT (p, o))) +#define G_EDICT(p,o) PROG_TO_EDICT ((p), G_INT (p, o)) /** Access an entity global. @@ -484,14 +667,28 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \hideinitializer */ #define G_STRUCT(p,t,o) (*(t *)G_GPOINTER (p, o)) -//@} +///@} /** \defgroup prda_parameters Parameters \ingroup progs_data_access Typed parameter access macros. No checking is done against the QC type, but the appropriate C type will be used. */ -//@{ +///@{ + +/** Access a parameter as an arbitray type. + + \par QC type: + \c struct etc small enough to fit in a single parameter + \param p pointer to ::progs_t VM struct + \param t C type of the structure + \param n parameter number (0-7) + \return structure lvalue. use & to make a pointer of the + appropriate type. + + \hideinitializer +*/ +#define P_PACKED(p,t,n) (*(t *) (p)->pr_params[n]) /** \internal \param p pointer to ::progs_t VM struct @@ -501,7 +698,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \hideinitializer */ -#define P_var(p,n,t) ((p)->pr_params[n]->t##_var) +#define P_var(p,n,t) P_PACKED(p, pr_##t##_t, n) /** Access a float parameter. Can be assigned to. @@ -515,29 +712,65 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ */ #define P_FLOAT(p,n) P_var (p, n, float) -/** Access an integer parameter. Can be assigned to. +/** Access a double parameter. Can be assigned to. \par QC type: - \c integer + \c double + \param p pointer to ::progs_t VM struct + \param n parameter number (0-7) + \return double lvalue + + \hideinitializer +*/ +#define P_DOUBLE(p,n) P_var (p, n, double) + +/** Access an int parameter. Can be assigned to. + + \par QC type: + \c int \param p pointer to ::progs_t VM struct \param n parameter number (0-7) \return int lvalue \hideinitializer */ -#define P_INT(p,n) P_var (p, n, integer) +#define P_INT(p,n) P_var (p, n, int) -/** Access an unsigned integer parameter. Can be assigned to. +/** Access an unsigned int parameter. Can be assigned to. \par QC type: - \c uinteger + \c uint \param p pointer to ::progs_t VM struct \param n parameter number (0-7) \return unsigned int lvalue \hideinitializer */ -#define P_UINT(p,n) P_var (p, n, uinteger) +#define P_UINT(p,n) P_var (p, n, uint) + +/** Access a long parameter. Can be assigned to. + + \par QC type: + \c long + \param p pointer to ::progs_t VM struct + \param n parameter number (0-7) + \return long lvalue + + \hideinitializer +*/ +#define P_LONG(p,n) P_var (p, n, long) + +/** Access an unsigned long parameter. Can be assigned to. + + \par QC type: + \c ulong + \param p pointer to ::progs_t VM struct + \param n parameter number (0-7) + \return unsigned long lvalue + + \hideinitializer +*/ +#define P_ULONG(p,n) P_var (p, n, ulong) /** Access a vector parameter. Can be used any way a vec3_t variable can. @@ -549,7 +782,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \hideinitializer */ -#define P_VECTOR(p,n) P_var (p, n, vector) +#define P_VECTOR(p,n) (&P_var (p, n, float)) /** Access a quaterion parameter. Can be used any way a quat_t variable can. @@ -561,7 +794,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \hideinitializer */ -#define P_QUAT(p,n) P_var (p, n, quat) +#define P_QUAT(p,n) (&P_var (p, n, float)) /** Access a string index parameter. Can be assigned to. @@ -569,7 +802,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \c string \param p pointer to ::progs_t VM struct \param n parameter number (0-7) - \return string_t lvalue + \return pr_string_t lvalue \hideinitializer */ @@ -581,7 +814,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \c void() \param p pointer to ::progs_t VM struct \param n parameter number (0-7) - \return func_t lvalue + \return pr_func_t lvalue \hideinitializer */ @@ -593,12 +826,11 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \c void * \param p pointer to ::progs_t VM struct \param n parameter number (0-7) - \return pointer_t lvalue + \return pr_ptr_t lvalue \hideinitializer */ -#define P_POINTER(p,n) P_var (p, n, pointer) - +#define P_POINTER(p,n) P_var (p, n, ptr) /** Access an entity parameter. @@ -610,7 +842,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \hideinitializer */ -#define P_EDICT(p,n) ((edict_t *)(PR_edicts (p) + P_INT (p, n))) +#define P_EDICT(p,n) PROG_TO_EDICT ((p), P_INT (p, n)) /** Access an entity parameter. @@ -674,18 +906,31 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \hideinitializer */ #define P_STRUCT(p,t,n) (*(t *)P_GPOINTER (p, n)) -//@} +///@} /** \defgroup prda_return Return Values \ingroup progs_data_access - These macros are used to access the value returned by an interpreted VM - function, and to return values from engine functions into progs space - (that is, builtins). + Typed return value access. These macros are used to access the value + returned by an interpreted VM function, and to return values from engine + functions into progs space (that is, builtins). \warning No checking is performed against progs types; for example, if you ask for an \c int from a function that returned a \c float, you're asking for trouble. */ -//@{ +///@{ + +/** Access the VM function return value as an arbitray type. + + \par QC type: + \c struct etc small enough to fit in the return slot + \param p pointer to ::progs_t VM struct + \param t C type of the structure + \return structure lvalue. use & to make a pointer of the + appropriate type. + + \hideinitializer +*/ +#define R_PACKED(p,t) (*(t *) (p)->pr_return) /** \internal \param p pointer to ::progs_t VM struct @@ -694,7 +939,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \hideinitializer */ -#define R_var(p,t) ((p)->pr_return->t##_var) +#define R_var(p,t) R_PACKED(p, pr_##t##_t) /** Access the VM function return value as a \c float @@ -707,27 +952,60 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ */ #define R_FLOAT(p) R_var (p, float) +/** Access the VM function return value as a \c double + + \par QC type: + \c double + \param p pointer to ::progs_t VM struct + \return double lvalue + + \hideinitializer +*/ +#define R_DOUBLE(p) R_var (p, double) + /** Access the VM function return value as a \c ::pr_int_t (AKA int32_t) \par QC type: - \c integer + \c int \param p pointer to ::progs_t VM struct \return ::pr_int_t lvalue \hideinitializer */ -#define R_INT(p) R_var (p, integer) +#define R_INT(p) R_var (p, int) /** Access the VM function return value as a \c ::pr_uint_t (AKA uint32_t) \par QC type: - \c uinteger + \c uint \param p pointer to ::progs_t VM struct \return ::pr_int_t lvalue \hideinitializer */ -#define R_UINT(p) R_var (p, uinteger) +#define R_UINT(p) R_var (p, uint) + +/** Access the VM function return value as a \c ::pr_long_t (AKA int32_t) + + \par QC type: + \c long + \param p pointer to ::progs_t VM struct + \return ::pr_long_t lvalue + + \hideinitializer +*/ +#define R_LONG(p) R_var (p, long) + +/** Access the VM function return value as a \c ::pr_ulong_t (AKA uint32_t) + + \par QC type: + \c ulong + \param p pointer to ::progs_t VM struct + \return ::pr_long_t lvalue + + \hideinitializer +*/ +#define R_ULONG(p) R_var (p, ulong) /** Access the VM function return value as a \c ::vec3_t vector. @@ -738,7 +1016,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \hideinitializer */ -#define R_VECTOR(p) R_var (p, vector) +#define R_VECTOR(p) (&R_var (p, float)) /** Access the VM function return value as a \c ::quat_t quaternion. @@ -749,40 +1027,40 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \hideinitializer */ -#define R_QUAT(p) R_var (p, quat) +#define R_QUAT(p) (&R_var (p, float)) -/** Access the VM function return value as a ::string_t (a VM string reference). +/** Access the VM function return value as a ::pr_string_t (a VM string reference). \par QC type: \c string \param p pointer to ::progs_t VM struct - \return ::string_t lvalue + \return ::pr_string_t lvalue \hideinitializer */ #define R_STRING(p) R_var (p, string) -/** Access the VM function return value as a ::func_t (a VM function reference) +/** Access the VM function return value as a ::pr_func_t (a VM function reference) \par QC type: \c void() \param p pointer to ::progs_t VM struct - \return ::func_t lvalue + \return ::pr_func_t lvalue \hideinitializer */ #define R_FUNCTION(p) R_var (p, func) -/** Access the VM function return value as a ::pointer_t (a VM "pointer") +/** Access the VM function return value as a ::pr_ptr_t (a VM "pointer") \par QC type: \c void * \param p pointer to ::progs_t VM struct - \return ::pointer_t lvalue + \return ::pr_ptr_t lvalue \hideinitializer */ -#define R_POINTER(p) R_var (p, pointer) +#define R_POINTER(p) R_var (p, ptr) /** Set the return value to the given C string. The returned string will @@ -807,7 +1085,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \hideinitializer */ -#define RETURN_EDICT(p,e) (R_STRING (p) = EDICT_TO_PROG(p, e)) +#define RETURN_EDICT(p,e) (R_INT (p) = EDICT_TO_PROG(p, e)) /** Set the return value to the given C pointer. NULL is converted to 0. @@ -840,15 +1118,38 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \hideinitializer */ -#define RETURN_QUAT(p,q) VectorCopy (q, R_QUAT (p)) -//@} +#define RETURN_QUAT(p,q) QuatCopy (q, R_QUAT (p)) +///@} /** \defgroup prda_entity_fields Entity Fields \ingroup progs_data_access Typed entity field access macros. No checking is done against the QC type, but the appropriate C type will be used. */ -//@{ +///@{ + +/** \internal + \param e pointer to the entity + \param o field offset into entity data space + \return lvalue of the appropriate type + + \hideinitializer +*/ +#define E_fld(e,o) ((e)->pr->pr_edict_area[(e)->edict + (o)]) + +/** Access an entity field as an arbitray type. + + \par QC type: + \c struct etc small enough to fit in a single parameter + \param e pointer to the entity + \param t C type of the structure + \param o field offset into entity data space + \return structure lvalue. use & to make a pointer of the + appropriate type. + + \hideinitializer +*/ +#define E_PACKED(e,t,o) (*(t *) &E_fld (e, o)) /** \internal \param e pointer to the entity @@ -858,7 +1159,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \hideinitializer */ -#define E_var(e,o,t) ((e)->v[o].t##_var) +#define E_var(e,o,t) E_PACKED (e, pr_##t##_t,o) /** Access a float entity field. Can be assigned to. @@ -873,29 +1174,65 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ */ #define E_FLOAT(e,o) E_var (e, o, float) -/** Access an integer entity field. Can be assigned to. +/** Access a double entity field. Can be assigned to. \par QC type: - \c integer + \c double + \param e pointer to the entity + \param o field offset into entity data space + \return double lvalue + + \hideinitializer +*/ +#define E_DOUBLE(e,o) E_var (e, o, double) + +/** Access an int entity field. Can be assigned to. + + \par QC type: + \c int \param e pointer to the entity \param o field offset into entity data space \return int lvalue \hideinitializer */ -#define E_INT(e,o) E_var (e, o, integer) +#define E_INT(e,o) E_var (e, o, int) -/** Access an unsigned integer entity field. Can be assigned to. +/** Access an unsigned int entity field. Can be assigned to. \par QC type: - \c uinteger + \c uint \param e pointer to the entity \param o field offset into entity data space \return unsigned int lvalue \hideinitializer */ -#define E_UINT(e,o) E_var (e, o, uinteger) +#define E_UINT(e,o) E_var (e, o, uint) + +/** Access a long entity field. Can be assigned to. + + \par QC type: + \c long + \param e pointer to the entity + \param o field offset into entity data space + \return long lvalue + + \hideinitializer +*/ +#define E_LONG(e,o) E_var (e, o, long) + +/** Access an unsigned long entity field. Can be assigned to. + + \par QC type: + \c ulong + \param e pointer to the entity + \param o field offset into entity data space + \return unsigned long lvalue + + \hideinitializer +*/ +#define E_ULONG(e,o) E_var (e, o, ulong) /** Access a vector entity field. Can be used any way a vec3_t variable can. @@ -907,7 +1244,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \hideinitializer */ -#define E_VECTOR(e,o) E_var (e, o, vector) +#define E_VECTOR(e,o) (&E_var (e, o, float)) /** Access a quaternion entity field. Can be used any way a quat_t variable can. @@ -920,7 +1257,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \hideinitializer */ -#define E_QUAT(e,o) E_var (e, o, quat) +#define E_QUAT(e,o) E_var (e, o, float) /** Access a string index entity field. Can be assigned to. @@ -928,7 +1265,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \c string \param e pointer to the entity \param o field offset into entity data space - \return string_t lvalue + \return pr_string_t lvalue \hideinitializer */ @@ -940,7 +1277,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \c void() \param e pointer to the entity \param o field offset into entity data space - \return func_t lvalue + \return pr_func_t lvalue \hideinitializer */ @@ -952,11 +1289,11 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \c void * \param e pointer to the entity \param o field offset into entity data space - \return pointer_t lvalue + \return pr_ptr_t lvalue \hideinitializer */ -#define E_POINTER(e,o) E_var (e, o, pointer) +#define E_POINTER(e,o) E_var (e, o, ptr) /** Access a string entity field, converting it to a C string. Kills the @@ -985,7 +1322,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ \hideinitializer */ #define E_DSTRING(p,e,o) (PR_GetMutableString (p, E_STRING (e, o))) -//@} +///@} /** \defgroup pr_builtins VM Builtin functions \ingroup progs @@ -998,7 +1335,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ 0x8000000 to 0xffffffff is unavailable due to the builtin number being a negative statement address. */ -//@{ +///@{ #define PR_RANGE_SHIFT 16 #define PR_RANGE_MASK 0xffff0000 @@ -1010,7 +1347,7 @@ void PR_Undefined (progs_t *pr, const char *type, const char *name) __attribute_ #define PR_RANGE_NONE 0xffff #define PR_AUTOBUILTIN (PR_RANGE_AUTO << PR_RANGE_SHIFT) -typedef void (*builtin_proc) (progs_t *pr); +typedef void (*builtin_proc) (progs_t *pr, void *data); /** Create a static array of these and pass the array to PR_RegisterBuiltins() to register the module's QC builtin functions. @@ -1025,21 +1362,56 @@ typedef struct { /// The number of the builtin for \#N in QC. -1 for automatic allocation. /// 0 or >= ::PR_AUTOBUILTIN is invalid. pr_int_t binum; + /// The number of parameters the builtin takes. Negative numbers mean + /// varargs with ~num_params (-num_params - 1) being the number of real + /// parameters. + pr_int_t num_params; + /// Parameter size specificiation. + /// + /// Up to 8 parameters are supported for automatic parameter setup because + /// that's all that v6p progs can pass. Builtins taking more than 8 + /// parameters are already Ruamoko-only and thus know how to deal with the + /// parameters being on the data stack. + /// + /// The encoding is the same as for progs functions, with 3:5 for + /// alignment:size (size 0 means 32 words). + dparmsize_t params[PR_MAX_PARAMS]; + + /// Data passed to builtin functions. Set by PR_RegisterBuiltins + void *data; } builtin_t; +#define PR_PARAM(type) { \ + .size = PR_SIZEOF(type) & 0x1f, \ + .alignment = BITOP_LOG2(PR_ALIGNOF(type)), \ +} + /** Duplicate the dfunction_t descriptor with the addition of a pointer to the builtin function. Avoids a level of indirection when calling a builtin function. */ typedef struct { pr_int_t first_statement; - pr_int_t parm_start; - pr_int_t locals; - pr_int_t profile; - pr_int_t numparms; - uint8_t parm_size[MAX_PARMS]; - dfunction_t *descriptor; - builtin_proc func; + pr_int_t numparams; + pr_ulong_t profile; + union { + struct { + dparmsize_t param_size[PR_MAX_PARAMS]; + dfunction_t *descriptor; + pr_uint_t params_start; + pr_uint_t locals; + }; + struct { + // although Ruamoko progs support more than PR_MAX_PARAMS + // arguments, only the first PR_MAX_PARAMS parameter pointers + // are initialized. This keeps builtins meant for both ISAs + // simple as they either will never accept more tha PR_MAX_PARAMS + // arugments, or they'll be modified to do the right thing. + pr_ushort_t param_offsets[PR_MAX_PARAMS]; + builtin_proc func; + void *data; ///< extra data passed to the builtin + }; + }; } bfunction_t; /** Register a set of builtin functions with the VM. Different VMs within the @@ -1047,8 +1419,10 @@ typedef struct { for the same VM, but redefining a builtin is an error. \param pr pointer to ::progs_t VM struct \param builtins array of builtin_t builtins + \param data pointer to builtin-specific data. Usually the resources + struct registered with PR_Resources_Register */ -void PR_RegisterBuiltins (progs_t *pr, builtin_t *builtins); +void PR_RegisterBuiltins (progs_t *pr, builtin_t *builtins, void *data); /** Lookup a builtin function referred by name. \param pr pointer to ::progs_t VM struct @@ -1072,7 +1446,7 @@ builtin_t *PR_FindBuiltinNum (progs_t *pr, pr_int_t num); */ int PR_RelocateBuiltins (progs_t *pr); -//@} +///@} /** \defgroup pr_strings String Management \ingroup progs @@ -1098,9 +1472,16 @@ int PR_RelocateBuiltins (progs_t *pr); They can be created, altered, and destroyed at any time by the main program (or the progs code via an appropriate builtin function). */ -//@{ +///@{ -/** Initialize the string tables using the strings supplied by the progs. +/** Initialize the string management subsystem. + + \param pr The VM of which the string management subsystem will be + initialized; +*/ +void PR_Strings_Init (progs_t *pr); + +/** Initialize the string tables using the strings supplied by the progs. Called automatically during progs load. \param pr pointer to ::progs_t VM struct \return true for success, false for failure @@ -1112,21 +1493,28 @@ int PR_LoadStrings (progs_t *pr); \param num string index to be validated \return true if the index is valid, false otherwise */ -qboolean PR_StringValid (progs_t *pr, string_t num); +bool PR_StringValid (progs_t *pr, pr_string_t num) __attribute__((pure)); + +/** Check if a string is valid and mutable. + \param pr pointer to ::progs_t VM struct + \param num string index to be checked + \return true if the string is valid and mutable, false otherwise +*/ +bool PR_StringMutable (progs_t *pr, pr_string_t num) __attribute__((pure)); /** Convert a string index to a C string. \param pr pointer to ::progs_t VM struct \param num string index to be converted \return C pointer to the string. */ -const char *PR_GetString(progs_t *pr, string_t num); +const char *PR_GetString(progs_t *pr, pr_string_t num) __attribute__((pure)); /** Retrieve the dstring_t associated with a mutable string. \param pr pointer to ::progs_t VM struct \param num string index of the mutable string \return the dstring implementing the mutable string */ -struct dstring_s *PR_GetMutableString(progs_t *pr, string_t num); +struct dstring_s *PR_GetMutableString(progs_t *pr, pr_string_t num) __attribute__((pure)); /** Make a permanent progs string from the given C string. Will not create a duplicate permanent string (temporary and mutable strings are not checked). @@ -1134,7 +1522,16 @@ struct dstring_s *PR_GetMutableString(progs_t *pr, string_t num); \param s C string to be made into a permanent progs string \return string index of the progs string */ -string_t PR_SetString(progs_t *pr, const char *s); +pr_string_t PR_SetString(progs_t *pr, const char *s); + +/** Get the progs string if it exists. + Only static strings are searched. + \param pr pointer to ::progs_t VM struct + \param s C string to be found + \return string index of the progs string if it exists, otherwise + 0 (ambiguous with ""). +*/ +pr_string_t PR_FindString(progs_t *pr, const char *s); /** Make a temporary progs string that will survive across function returns. Will not duplicate a permanent string. If a new progs string is created, @@ -1144,7 +1541,7 @@ string_t PR_SetString(progs_t *pr, const char *s); \param s C string to be returned to the progs code \return string index of the progs string */ -string_t PR_SetReturnString(progs_t *pr, const char *s); +pr_string_t PR_SetReturnString(progs_t *pr, const char *s); /** Make a temporary progs string that will be freed when the current progs stack frame is exited. Will not duplicate a permantent string. @@ -1152,7 +1549,30 @@ string_t PR_SetReturnString(progs_t *pr, const char *s); \param s C string \return string index of the progs string */ -string_t PR_SetTempString(progs_t *pr, const char *s); +pr_string_t PR_SetTempString(progs_t *pr, const char *s); + +/** Make a temporary memory block that will be freed when the current progs + stack frame is exited. The contents may be anything and a new block is + returned every time, and the block is in VM addressible space. To access + the contents of the block (for reading, writing, etc), use PR_GetString() + and cast the pointer as necessary. + + \param pr pointer to ::progs_t VM struct + \param size size of block in bytes + \return string index of the block +*/ +pr_string_t PR_AllocTempBlock (progs_t *pr, size_t size); + +/** Push a temporary string to the callee stack frame + + This is for when the temp string needs to be freed when the called function + returns rather than the calling function. It is an error to push a non-temp + string. + + \param pr pointer to ::progs_t VM struct + \param num string index of the temp string +*/ +void PR_PushTempString (progs_t *pr, pr_string_t num); /** Make a temporary progs string that is the concatenation of two C strings. \param pr pointer to ::progs_t VM struct @@ -1161,19 +1581,19 @@ string_t PR_SetTempString(progs_t *pr, const char *s); \return string index of the progs string that represents the concatenation of strings a and b */ -string_t PR_CatStrings (progs_t *pr, const char *a, const char *b); +pr_string_t PR_CatStrings (progs_t *pr, const char *a, const char *b); /** Convert a mutable string to a temporary string. \param pr pointer to ::progs_t VM struct \param str string index of the mutable string to be converted */ -void PR_MakeTempString(progs_t *pr, string_t str); +void PR_MakeTempString(progs_t *pr, pr_string_t str); /** Create a new mutable string. \param pr pointer to ::progs_t VM struct \return string index of the newly created mutable string */ -string_t PR_NewMutableString (progs_t *pr); +pr_string_t PR_NewMutableString (progs_t *pr); /** Make a dynamic progs string from the given C string. Will not create a duplicate permanent string (temporary, dynamic and mutable strings are @@ -1182,24 +1602,57 @@ string_t PR_NewMutableString (progs_t *pr); \param s C string to be made into a permanent progs string \return string index of the progs string */ -string_t PR_SetDynamicString (progs_t *pr, const char *s); +pr_string_t PR_SetDynamicString (progs_t *pr, const char *s); + +/** Convert an ephemeral string to a dynamic string. + + Valid strings that are not ephemeral (static, dynamic, mutable) will not + be affected, but temp and return strings will be marked dynamic, requiring + a call to PR_FreeString to return their memory. -/** Clear all of the return string slots. Called at progs load. \param pr pointer to ::progs_t VM struct + \param str The string to be "held" (made non-ephemeral). Safe to call + on any valid string, but affects only ephemeral strings. */ -void PR_ClearReturnStrings (progs_t *pr); +void PR_HoldString (progs_t *pr, pr_string_t str); /** Destroy a mutable, dynamic or temporary string. \param pr pointer to ::progs_t VM struct \param str string index of the string to be destroyed */ -void PR_FreeString (progs_t *pr, string_t str); +void PR_FreeString (progs_t *pr, pr_string_t str); /** Free all the temporary strings allocated in the current stack frame. \param pr pointer to ::progs_t VM struct */ void PR_FreeTempStrings (progs_t *pr); +/** Callback for handling %@ in PR_Sprintf format strings. + + \param pr pointer to ::progs_t VM struct + \param at_param Parameter to be converted to a string. Usually a progs + pointer value. It is the callee's responsibility to + interpret the value as a pointer. + \param data User data pointer as passed to PR_Sprintf_SetAtHandler + \return String to be printed in place of %@ +*/ +typedef const char *(*prstr_at_handler_t) (progs_t *pr, pr_ptr_t at_param, + void *data); + +/** Set the at_handler callback and its user data pointer. + + The at_handler callback defaults to null resulting in [ptr] in the output + string. Setting the callback allows the at_handler to interpret the value + (nominally pr_ptr_t, but always a 32-bit value) as it chooses. + + \param pr pointer to ::progs_t VM struct + \param at_handler Function pointer for callback to interpret the parameter + corresponding to the %@ format specifier. + \param data User data pointer, passed to \a at_handler. +*/ +void PR_Sprintf_SetAtHandler (progs_t *pr, prstr_at_handler_t at_handler, + void *data); + /** Formatted printing similar to C's vsprintf, but using QC types. The format string is a string of characters (other than \c \%) to be printed that includes optional format specifiers, one for each arg to be @@ -1236,14 +1689,14 @@ void PR_FreeTempStrings (progs_t *pr);
  • \c '@' \c id Not yet implemented. Silently ignored.
  • \c 'e' \c entity Prints the edict number of the given entity ("%i") -
  • \c 'i' \c integer Print a integer value. ("%i") +
  • \c 'i' \c int Print a int value. ("%i")
  • \c 'f' \c float Print a float value. ("%f")
  • \c 'g' \c float Print a float value. ("%f")
  • \c 'p' \c void * Print a pointer value. ("%#x")
  • \c 's' \c string Print a string value. ("%s")
  • \c 'v' \c vector Print a vector value. ("'%g %g %g'")
  • \c 'q' \c quaternion Print a quaternion value. ("'%g %g %g %g'") -
  • \c 'x' \c uinteger Print an unsigned integer value. ("%x") +
  • \c 'x' \c uint Print an unsigned int value. ("%x")
@@ -1259,13 +1712,13 @@ void PR_FreeTempStrings (progs_t *pr); void PR_Sprintf (progs_t *pr, struct dstring_s *result, const char *name, const char *format, int count, pr_type_t **args); -//@} +///@} /** \defgroup pr_resources Resource Management \ingroup progs Builtin module private data management. */ -//@{ +///@{ /** Initialize the resource management fields. @@ -1273,6 +1726,9 @@ void PR_Sprintf (progs_t *pr, struct dstring_s *result, const char *name, */ void PR_Resources_Init (progs_t *pr); +void PR_Resources_Shutdown (progs_t *pr); +void PR_Builtins_Shutdown (progs_t *pr); + /** Clear all resources before loading a new progs. Calls the clear() callback of all registered resources. @@ -1287,18 +1743,23 @@ void PR_Resources_Clear (progs_t *pr); \param name The name of the resource. Used for retrieving the resource. \param data The resource data. - callback. - \param clear Callback for performing any necessary cleanup. Called - by PR_Resources_Clear(). The parameters are the current + \param clear Callback for performing any necessary cleanup before + VM reuse. Called by PR_Resources_Clear(). The parameters + are the current VM (\p pr) and \p data. + \param destroy Callback for when the resource is about to be destryed + due to final VM shutdown. The parameters are the current VM (\p pr) and \p data. \note The name should be unique to the VM, but no checking is done. If the name is not unique, the previous registered resource will break. The name of the sub-system registering the resource is a suitable name, and will probably be unique. + \note During VM shutdown, \a clear is called (for all resources) + before \a destroy is called. */ void PR_Resources_Register (progs_t *pr, const char *name, void *data, - void (*clear)(progs_t *, void *)); + void (*clear)(progs_t *, void *), + void (*destroy)(progs_t *, void *)); /** Retrieve a resource registered with the VM. @@ -1312,100 +1773,141 @@ void *PR_Resources_Find (progs_t *pr, const char *name); /** \name Resource Map support These macros can be used to create functions for mapping C resources - to QuakeC integer handles. + to QuakeC int handles. Valid handles are always negative. \note \p map is the resource map itself, not a pointer to the resource map. */ -//@{ +///@{ /** Type delcaration for the resource map. \param type The type of the resource. The size must be at least as large as \c sizeof(type *). + \note \a _size is NOT the number of objects in the + map. It is the number of rows in the map array (each row + has multiple objects). */ #define PR_RESMAP(type) struct { type *_free; type **_map; unsigned _size; } +#define PR_RESDELMAP(map) \ + do { \ + for (unsigned i = 0; i < (map)._size; i++) { \ + free ((map)._map[i]); \ + } \ + free ((map)._map); \ + } while (0) + /** Allocate a new resource from the resource map. - \param type The type of the resource. Must match the \c type parameter - used for PR_RESMAP. \param map The resource map. \return A pointer to the new resource, or null if no more could be allocated. */ -#define PR_RESNEW(type,map) \ - type *t; \ - \ - if (!map._free) { \ - int i, size; \ - map._size++; \ - size = map._size * sizeof (type *); \ - map._map = realloc (map._map, size); \ - if (!map._map) \ - return 0; \ - map._free = calloc (1024, sizeof (type)); \ - if (!map._free) \ - return 0; \ - map._map[map._size - 1] = map._free; \ - for (i = 0; i < 1023; i++) \ - *(type **) &map._free[i] = &map._free[i + 1]; \ - *(type **) &map._free[i] = 0; \ - } \ - t = map._free; \ - map._free = *(type **) t; \ - memset (t, 0, sizeof (type)); \ - return t +#define PR_RESNEW_NC(map) \ + ({ \ + __auto_type ret = (map)._free; \ + do { \ + if (!(map)._free) { \ + int i, size; \ + (map)._size++; \ + size = (map)._size * sizeof ((map)._free); \ + (map)._map = realloc ((map)._map, size); \ + if (!(map)._map) { \ + ret = 0; \ + break; \ + } \ + (map)._free = malloc (1024 * sizeof (*(map)._free)); \ + if (!(map)._free) { \ + ret = 0; \ + break; \ + } \ + (map)._map[(map)._size - 1] = (map)._free; \ + for (i = 0; i < 1023; i++) { \ + *(typeof ((map)._free) *) &(map)._free[i] \ + = &(map)._free[i + 1]; \ + } \ + *(typeof ((map)._free) *) &(map)._free[i] = 0; \ + } \ + ret = (map)._free; \ + (map)._free = *(typeof ((map)._free) *) ret; \ + } while (0); \ + ret; \ + }) + +#define PR_RESNEW(map) \ + ({ \ + __auto_type t = PR_RESNEW_NC (map); \ + if (t) { \ + memset (t, 0, sizeof (*(map)._free)); \ + } \ + t; \ + }) /** Free a resource returning it to the resource map. - \param type The type of the resource. Must match the \c type parameter - used for PR_RESMAP. \param map The resource map. \param t Pointer to the resource to be freed. */ -#define PR_RESFREE(type,map,t) \ - memset (t, 0, sizeof (type)); \ - *(type **) t = map._free; \ - map._free = t +#define PR_RESFREE(map,t) \ + do { \ + memset (t, 0, sizeof (*(map)._free)); \ + *(typeof ((map)._free) *) t = (map)._free; \ + (map)._free = t; \ + } while (0) /** Free all resources in the resource map. Any memory allocated to the resource must be freed before freeing the resource. - \param type The type of the resource. Must match the \c type parameter - used for PR_RESMAP. + A reset resource map is guaranteed to allocate elements sequentially. + \param map The resource map. */ -#define PR_RESRESET(type,map) \ - unsigned i, j; \ - if (!map._size) \ - return; \ - for (i = 0; i < map._size; i++) { \ - map._free = map._map[i]; \ - for (j = 0; j < 1023; j++) \ - *(type **) &map._free[j] = &map._free[j + 1]; \ - if (i < map._size - 1) \ - *(type **) &map._free[j] = &map._map[i + 1][0]; \ - } \ - map._free = map._map[0]; +#define PR_RESRESET(map) \ + do { \ + unsigned i, j; \ + if (!(map)._size) { \ + break; \ + } \ + for (i = 0; i < (map)._size; i++) { \ + (map)._free = (map)._map[i]; \ + for (j = 0; j < 1023; j++) { \ + *(typeof ((map)._free) *) &(map)._free[j] \ + = &(map)._free[j + 1]; \ + } \ + if (i < (map)._size - 1) { \ + *(typeof ((map)._free) *) &(map)._free[j] \ + = &(map)._map[i + 1][0]; \ + } else { \ + *(typeof ((map)._free) *) &(map)._free[j] = 0; \ + } \ + } \ + (map)._free = (map)._map[0]; \ + } while (0) /** Retrieve a resource from the resource map using a handle. \param map The resource map. - \param col The handle. + \param ind The handle. \return A pointer to the resource, or NULL if the handle is invalid. */ -#define PR_RESGET(map,col) \ - unsigned row = ~col / 1024; \ - col = ~col % 1024; \ - if (row >= map._size) \ - return 0; \ - return &map._map[row][col] +#define PR_RESGET(map,ind) \ + ({ \ + __auto_type ret = (map)._free; \ + unsigned row = ~ind / 1024; \ + unsigned col = ~ind % 1024; \ + if (row >= (map)._size) { \ + ret = 0; \ + } else { \ + ret = &(map)._map[row][col]; \ + } \ + ret; \ + }) /** Convert a resource pointer to a handle. @@ -1413,101 +1915,154 @@ void *PR_Resources_Find (progs_t *pr, const char *name); \param ptr The resource pointer. \return The handle or 0 if the pointer is invalid. */ -#define PR_RESINDEX(map,ptr) \ - unsigned i; \ - for (i = 0; i < map._size; i++) { \ - long d = ptr - map._map[i]; \ - if (d >= 0 && d < 1024) \ - return ~(i * 1024 + d); \ - } \ - return 0 -//@} +#define PR_RESINDEX(map,ptr) \ + ({ \ + unsigned i; \ + unsigned index = 0; \ + for (i = 0; i < (map)._size; i++) { \ + long d = ptr - (map)._map[i]; \ + if (d >= 0 && d < 1024) { \ + index = ~(i * 1024 + d); \ + break; \ + } \ + } \ + index; \ + }) +///@} -//@} +///@} /** \defgroup pr_zone VM memory management. \ingroup progs Used to allocate and free memory in the VM address space. */ -//@{ +///@{ void PR_Zone_Init (progs_t *pr); void PR_Zone_Free (progs_t *pr, void *ptr); void *PR_Zone_Malloc (progs_t *pr, pr_int_t size); void *PR_Zone_Realloc (progs_t *pr, void *ptr, pr_int_t size); +void *PR_Zone_TagMalloc (progs_t *pr, int size, int tag); -//@} +///@} /** \defgroup debug VM Debugging \ingroup progs Progs debugging support. */ -//@{ +/// \addtogroup debug +///@{ -void PR_Debug_Init (void); +struct qfot_type_s; + +/** Callback for viewing progs data + + \param type C pointer to the type definition by which to view the + data. + \param value C pointer to the data to be viewed. + \param data User data. +*/ +typedef void (*type_view_func) (struct qfot_type_s *type, pr_type_t *value, + void *data); + +/** Set of callbacks for viewing progs data + + Each possible type has its own callback. Basic types (those for which the + VM has specific instructions) all have separate callbacks, one for each + type, but the callbacks for compound types are expected to some + interpretation on their own, such as displaying a simple identifier or + the entire contents of the data. +*/ +typedef struct type_view_s { + type_view_func struct_view; + type_view_func union_view; + type_view_func enum_view; + type_view_func array_view; + type_view_func class_view; +#define EV_TYPE(type) type_view_func type##_view; +#include "QF/progs/pr_type_names.h" +} type_view_t; + +void PR_Debug_Init (progs_t *pr); void PR_Debug_Init_Cvars (void); +void PR_DebugSetSym (progs_t *pr, pr_debug_header_t *debug); int PR_LoadDebug (progs_t *pr); +const char *PR_Debug_GetBaseDirectory (progs_t *pr, const char *file); void PR_Debug_Watch (progs_t *pr, const char *expr); void PR_Debug_Print (progs_t *pr, const char *expr); -pr_auxfunction_t *PR_Get_Lineno_Func (progs_t *pr, pr_lineno_t *lineno); -pr_uint_t PR_Get_Lineno_Addr (progs_t *pr, pr_lineno_t *lineno); -pr_uint_t PR_Get_Lineno_Line (progs_t *pr, pr_lineno_t *lineno); -pr_lineno_t *PR_Find_Lineno (progs_t *pr, pr_uint_t addr); -const char *PR_Get_Source_File (progs_t *pr, pr_lineno_t *lineno); +const char *PR_Debug_ValueString (progs_t *pr, pr_ptr_t offset, + struct qfot_type_s *type, + struct dstring_s *dstr); +pr_auxfunction_t *PR_Debug_AuxFunction (progs_t *pr, pr_uint_t func) __attribute__((pure)); +pr_auxfunction_t *PR_Debug_MappedAuxFunction (progs_t *pr, pr_uint_t func) __attribute__((pure)); +pr_def_t *PR_Debug_LocalDefs (progs_t *pr, pr_auxfunction_t *aux_function) __attribute__((pure)); +pr_lineno_t *PR_Debug_Linenos (progs_t *pr, pr_auxfunction_t *aux_function, + pr_uint_t *num_linenos); +pr_auxfunction_t *PR_Get_Lineno_Func (progs_t *pr, pr_lineno_t *lineno) __attribute__((pure)); +pr_uint_t PR_Get_Lineno_Addr (progs_t *pr, pr_lineno_t *lineno) __attribute__((pure)); +pr_uint_t PR_Get_Lineno_Line (progs_t *pr, pr_lineno_t *lineno) __attribute__((pure)); +pr_lineno_t *PR_Find_Lineno (progs_t *pr, pr_uint_t addr) __attribute__((pure)); +pr_uint_t PR_FindSourceLineAddr (progs_t *pr, const char *file, pr_uint_t line) __attribute__((pure)); +const char *PR_Get_Source_File (progs_t *pr, pr_lineno_t *lineno) __attribute__((pure)); const char *PR_Get_Source_Line (progs_t *pr, pr_uint_t addr); -ddef_t *PR_Get_Param_Def (progs_t *pr, dfunction_t *func, unsigned parm); -ddef_t *PR_Get_Local_Def (progs_t *pr, pr_int_t offs); +pr_def_t *PR_Get_Param_Def (progs_t *pr, dfunction_t *func, unsigned parm) __attribute__((pure)); +pr_def_t *PR_Get_Local_Def (progs_t *pr, pr_ptr_t *offs) __attribute__((pure)); void PR_PrintStatement (progs_t *pr, dstatement_t *s, int contents); void PR_DumpState (progs_t *pr); void PR_StackTrace (progs_t *pr); void PR_Profile (progs_t *pr); -extern struct cvar_s *pr_debug; -extern struct cvar_s *pr_deadbeef_ents; -extern struct cvar_s *pr_deadbeef_locals; -extern struct cvar_s *pr_boundscheck; -extern struct cvar_s *pr_faultchecks; +extern int pr_debug; +extern char *pr_source_path; +extern int pr_deadbeef_ents; +extern int pr_deadbeef_locals; +extern int pr_boundscheck; +extern int pr_faultchecks; -//@} +///@} /** \defgroup pr_cmds Quake and Quakeworld common builtins \ingroup progs \todo This really doesn't belong in progs. */ -//@{ +///@{ -char *PF_VarString (progs_t *pr, int first); +char *PF_VarString (progs_t *pr, int first, int count); void PR_Cmds_Init (progs_t *pr); extern const char *pr_gametype; -//@} +///@} //============================================================================ -#define MAX_STACK_DEPTH 64 -#define LOCALSTACK_SIZE 4096 +#define PR_MAX_STACK_DEPTH 64 +#define PR_LOCAL_STACK_SIZE 4096 #define PR_RS_SLOTS 16 +#define PR_BASE_IND(o, b) (((o) & OP_##b##_BASE) >> OP_##b##_SHIFT) +#define PR_BASE(p, s, b) (p->pr_bases[PR_BASE_IND(s->op, b)]) typedef struct strref_s strref_t; typedef struct { - pr_int_t s; ///< Return statement. - bfunction_t *f; ///< Calling function. + pr_uivec4_t bases; ///< base registers on entry to function + pr_uint_t staddr; ///< Return statement. + pr_uint_t stack_ptr; ///< data stack on entry to function + bfunction_t *func; ///< Calling function. strref_t *tstr; ///< Linked list of temporary strings. + pr_type_t *return_ptr; ///< Saved return address } prstack_t; -struct obj_list_s; - struct progs_s { int (*parse_field) (progs_t *pr, const char *key, const char *value); int null_bad; int no_exec_limit; + struct hashctx_s **hashctx; void (*file_error) (progs_t *pr, const char *path); - void *(*load_file) (progs_t *pr, const char *path); + void *(*load_file) (progs_t *pr, const char *path, off_t *size); void *(*allocate_progs_mem) (progs_t *pr, int size); void (*free_progs_mem) (progs_t *pr, void *mem); @@ -1521,146 +2076,153 @@ struct progs_s { int denorm_found; struct memzone_s *zone; - int zone_size; + int zone_size; ///< set by user /// \name builtin functions - //@{ + ///@{ struct hashtab_s *builtin_hash; struct hashtab_s *builtin_num_hash; + struct biblock_s *builtin_blocks; + builtin_t *bi_no_function; unsigned bi_next; unsigned (*bi_map) (progs_t *pr, unsigned binum); - //@} + ///@} /// \name symbol management - //@{ + ///@{ struct hashtab_s *function_hash; struct hashtab_s *global_hash; struct hashtab_s *field_hash; - //@} + ///@} + + /// \name type encodings + ///@{ + struct hashtab_s *type_hash; + pr_ptr_t type_encodings; + ///@} /// \name load hooks - //@{ + ///@{ int num_load_funcs; int max_load_funcs; pr_load_func_t **load_funcs; /// cleared each load - //@{ + ///@{ int num_load_finish_funcs; int max_load_finish_funcs; pr_load_func_t **load_finish_funcs; - //@} - //@} + ///@} + ///@} /// \name string management - //@{ - struct dstring_mem_s *ds_mem; - strref_t *free_string_refs; - strref_t *static_strings; - strref_t **string_map; - strref_t *return_strings[PR_RS_SLOTS]; - int rs_slot; - unsigned dyn_str_size; - struct hashtab_s *strref_hash; - int num_strings; + ///@{ + struct prstr_resources_s *pr_string_resources; strref_t *pr_xtstr; - //@} + strref_t *pr_pushtstr; + int float_promoted; ///< for PR_Sprintf + ///@} /// \name memory map - //@{ + ///@{ dfunction_t *pr_functions; bfunction_t *function_table; char *pr_strings; int pr_stringsize; - ddef_t *pr_globaldefs; - ddef_t *pr_fielddefs; + pr_def_t *pr_globaldefs; + pr_def_t *pr_fielddefs; dstatement_t *pr_statements; pr_type_t *pr_globals; - unsigned globals_size; - //@} + pr_uint_t globals_size; + pr_uint_t null_size; ///< size of block considered null page + pr_uivec4_t pr_bases; ///< base registers (index in opcode) + ///@} /// \name parameter block - //@{ + ///@{ pr_type_t *pr_return; - pr_type_t *pr_params[MAX_PARMS]; - pr_type_t *pr_real_params[MAX_PARMS]; - pr_type_t *pr_param_ptrs[2]; - pr_type_t *pr_saved_params; - int pr_saved_argc; + pr_type_t *pr_params[PR_MAX_PARAMS]; + pr_type_t *pr_real_params[PR_MAX_PARAMS]; int pr_param_size; ///< covers both params and return - //@} + int pr_param_alignment; ///< covers both params and return + pr_type_t *pr_return_buffer; ///< for discarded return values + ///< or returning values to C + ///@} /// \name edicts - //@{ - edict_t **edicts; - int max_edicts; - int *num_edicts; - int *reserved_edicts; ///< alloc will start at reserved_edicts+1 + /// \todo FIXME should this be outside the VM? + ///@{ + edict_t **pr_edicts; + pr_uint_t max_edicts; ///< set by user + pr_uint_t *num_edicts; + pr_uint_t *reserved_edicts; ///< alloc will start at reserved_edicts+1 void (*unlink) (edict_t *ent); void (*flush) (void); int (*prune_edict) (progs_t *pr, edict_t *ent); void (*free_edict) (progs_t *pr, edict_t *ent); - int pr_edict_size; ///< in bytes - int pr_edictareasize; ///< for bounds checking, starts at 0 - func_t edict_parse; - //@} + pr_type_t *pr_edict_area; + int pr_edict_size; ///< # of pr_type_t slots + pr_uint_t pr_edict_area_size; ///< for bounds checking, starts at 0 + pr_func_t edict_parse; + ///@} /// \name execution state - //@{ - int pr_argc; + ///@{ + int pr_argc; //FIXME need a good way to ensure it is correct - qboolean pr_trace; + bool pr_trace; int pr_trace_depth; bfunction_t *pr_xfunction; int pr_xstatement; - prstack_t pr_stack[MAX_STACK_DEPTH]; + prstack_t pr_stack[PR_MAX_STACK_DEPTH]; int pr_depth; - int localstack[LOCALSTACK_SIZE]; + /// \name progs visible stack + /// Usable by the progs for any purpose. Will not be accessible unless + /// a .stack global is found. Space is allocated from the top of the stack + /// (as is common for hardware). The push and pop instructions will not + /// be considered valid if there is no .stack global. + /// \note The return address and saved locals will not ever be on this + /// stack. + ///@{ + pr_type_t *stack; + pr_ptr_t stack_bottom; + int stack_size; ///< set by user + ///@} + + int localstack[PR_LOCAL_STACK_SIZE]; int localstack_used; - //@} + ///@} /// \name resources - //@{ + ///@{ pr_resource_t *resources; struct hashtab_s *resource_hash; - //@} + ///@} /// \name obj info - //@{ - unsigned selector_index; - unsigned selector_index_max; - struct obj_list_s **selector_sels; - string_t *selector_names; - struct hashtab_s *selector_hash; - struct hashtab_s *classes; - struct hashtab_s *load_methods; - struct obj_list_s *unresolved_classes; - struct obj_list_s *unclaimed_categories; - struct obj_list_s *unclaimed_proto_list; - struct obj_list_s *module_list; - struct obj_list_s *class_tree_list; - //@} + ///@{ + struct probj_resources_s *pr_objective_resources; + ///@} - /// \name debug info - //@{ - const char *debugfile; - struct pr_debug_header_s *debug; - struct pr_auxfunction_s *auxfunctions; - struct pr_auxfunction_s **auxfunction_map; - struct pr_lineno_s *linenos; - ddef_t *local_defs; + /// \name debugging + ///@{ + struct prdeb_resources_s *pr_debug_resources; + void (*debug_handler) (prdebug_t event, void *param, void *data); + void *debug_data; pr_type_t *watch; int wp_conditional; pr_type_t wp_val; - //@} + ///@} /// \name globals and fields needed by the VM - //@{ + ///@{ struct { - float *time; ///< required for OP_STATE - pr_int_t *self; ///< required for OP_STATE + double *dtime; ///< required for OP_STATE d + float *ftime; ///< required for OP_STATE f + pr_uint_t *self; ///< required for OP_STATE + pr_ptr_t *stack; ///< required for OP_(PUSH|POP)* } globals; struct { pr_int_t nextthink; ///< required for OP_STATE @@ -1668,12 +2230,12 @@ struct progs_s { pr_int_t think; ///< required for OP_STATE pr_int_t this; ///< optional for entity<->object linking } fields; - //@} + ///@} }; /** \addtogroup progs_data_access */ -//@{ +///@{ /** Convert a progs offset/pointer to a C pointer. \param pr pointer to ::progs_t VM struct @@ -1681,7 +2243,7 @@ struct progs_s { \return C pointer represented by the parameter. 0 offset -> NULL */ static inline pr_type_t * -PR_GetPointer (progs_t *pr, pointer_t o) +PR_GetPointer (const progs_t *pr, pr_ptr_t o) { return o ? pr->pr_globals + o : 0; } @@ -1691,13 +2253,13 @@ PR_GetPointer (progs_t *pr, pointer_t o) \param p C pointer to be converted. \return Progs offset/pointer represented by \c p. NULL -> 0 offset */ -static inline pointer_t -PR_SetPointer (progs_t *pr, void *p) +static inline pr_ptr_t +PR_SetPointer (const progs_t *pr, const void *p) { - return p ? (pr_type_t *) p - pr->pr_globals : 0; + return p ? (const pr_type_t *) p - pr->pr_globals : 0; } -//@} +///@} /** \example vm-exec.c */ diff --git a/include/QF/progs/pr_comp.h b/include/QF/progs/pr_comp.h new file mode 100644 index 000000000..696de6218 --- /dev/null +++ b/include/QF/progs/pr_comp.h @@ -0,0 +1,587 @@ +/* Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + See file, 'COPYING', for details. +*/ + +// this file is shared by QuakeForge and qfcc +#ifndef __QF_pr_comp_h +#define __QF_pr_comp_h + +#include "QF/qtypes.h" + +typedef int32_t pr_string_t __attribute__((aligned(4))); +typedef float pr_float_t __attribute__((aligned(4))); +typedef float pr_vector_t[3] __attribute__((aligned(4))); +typedef uint32_t pr_entity_t __attribute__((aligned(4))); +typedef uint32_t pr_field_t __attribute__((aligned(4))); +typedef uint32_t pr_func_t __attribute__((aligned(4))); +typedef uint32_t pr_ptr_t __attribute__((aligned(4))); +typedef float pr_quaternion_t[4] __attribute__((aligned(4))); +typedef int32_t pr_int_t __attribute__((aligned(4))); +typedef uint32_t pr_uint_t __attribute__((aligned(4))); +typedef int16_t pr_short_t __attribute__((aligned(2))); +typedef double pr_double_t __attribute__((aligned(8))); +typedef int64_t pr_long_t __attribute__((aligned(8))); +typedef uint64_t pr_ulong_t __attribute__((aligned(8))); +typedef uint16_t pr_ushort_t __attribute__((aligned(2)));; + +#define PR_PTR(t, p) (*(pr_##t##_t *) (p)) + +#define PR_VEC_TYPE(t,n,s) \ + typedef t n __attribute__ ((vector_size (s*sizeof (t)))) + +PR_VEC_TYPE (pr_int_t, pr_ivec2_t, 2); +typedef pr_int_t pr_ivec3_t[3]; +PR_VEC_TYPE (pr_int_t, pr_ivec4_t, 4); + +PR_VEC_TYPE (pr_uint_t, pr_uivec2_t, 2); +typedef pr_uint_t pr_uivec3_t[3]; +PR_VEC_TYPE (pr_uint_t, pr_uivec4_t, 4); + +PR_VEC_TYPE (float, pr_vec2_t, 2); +typedef pr_float_t pr_vec3_t[3]; +PR_VEC_TYPE (float, pr_vec4_t, 4); + +PR_VEC_TYPE (pr_long_t, pr_lvec2_t, 2); +typedef pr_long_t pr_lvec3_t[3]; +PR_VEC_TYPE (pr_long_t, pr_lvec4_t, 4); + +PR_VEC_TYPE (pr_ulong_t, pr_ulvec2_t, 2); +typedef pr_ulong_t pr_ulvec3_t[3]; +PR_VEC_TYPE (pr_ulong_t, pr_ulvec4_t, 4); + +PR_VEC_TYPE (double, pr_dvec2_t, 2); +typedef pr_double_t pr_dvec3_t[3]; +PR_VEC_TYPE (double, pr_dvec4_t, 4); + + +#define EV_TYPE(type) ev_##type, +typedef enum { +#include "QF/progs/pr_type_names.h" + ev_invalid, // invalid type. used for instruction checking + ev_type_count // not a type, gives number of types +} etype_t; + +#define PR_SIZEOF(type) (sizeof (pr_##type##_t) / (sizeof (pr_int_t))) +#define PR_ALIGNOF(type) (__alignof__ (pr_##type##_t) / __alignof__ (pr_int_t)) + +extern const pr_ushort_t pr_type_size[ev_type_count]; +extern const pr_ushort_t pr_type_alignment[ev_type_count]; +extern const char * const pr_type_name[ev_type_count]; + +#define OFS_NULL 0 +#define OFS_RETURN 1 +#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors +#define OFS_PARM1 7 +#define OFS_PARM2 10 +#define OFS_PARM3 13 +#define OFS_PARM4 16 +#define OFS_PARM5 19 +#define OFS_PARM6 22 +#define OFS_PARM7 25 +#define RESERVED_OFS 28 + + +typedef enum { + OP_DONE_v6p, + OP_MUL_F_v6p, + OP_MUL_V_v6p, + OP_MUL_FV_v6p, + OP_MUL_VF_v6p, + OP_DIV_F_v6p, + OP_ADD_F_v6p, + OP_ADD_V_v6p, + OP_SUB_F_v6p, + OP_SUB_V_v6p, + + OP_EQ_F_v6p, + OP_EQ_V_v6p, + OP_EQ_S_v6p, + OP_EQ_E_v6p, + OP_EQ_FN_v6p, + + OP_NE_F_v6p, + OP_NE_V_v6p, + OP_NE_S_v6p, + OP_NE_E_v6p, + OP_NE_FN_v6p, + + OP_LE_F_v6p, + OP_GE_F_v6p, + OP_LT_F_v6p, + OP_GT_F_v6p, + + OP_LOAD_F_v6p, + OP_LOAD_V_v6p, + OP_LOAD_S_v6p, + OP_LOAD_ENT_v6p, + OP_LOAD_FLD_v6p, + OP_LOAD_FN_v6p, + + OP_ADDRESS_v6p, + + OP_STORE_F_v6p, + OP_STORE_V_v6p, + OP_STORE_S_v6p, + OP_STORE_ENT_v6p, + OP_STORE_FLD_v6p, + OP_STORE_FN_v6p, + + OP_STOREP_F_v6p, + OP_STOREP_V_v6p, + OP_STOREP_S_v6p, + OP_STOREP_ENT_v6p, + OP_STOREP_FLD_v6p, + OP_STOREP_FN_v6p, + + OP_RETURN_v6p, + OP_NOT_F_v6p, + OP_NOT_V_v6p, + OP_NOT_S_v6p, + OP_NOT_ENT_v6p, + OP_NOT_FN_v6p, + OP_IF_v6p, + OP_IFNOT_v6p, + OP_CALL0_v6p, + OP_CALL1_v6p, + OP_CALL2_v6p, + OP_CALL3_v6p, + OP_CALL4_v6p, + OP_CALL5_v6p, + OP_CALL6_v6p, + OP_CALL7_v6p, + OP_CALL8_v6p, + OP_STATE_v6p, + OP_GOTO_v6p, + OP_AND_v6p, + OP_OR_v6p, + + OP_BITAND_v6p, + OP_BITOR_v6p, // end of v6 opcodes + + OP_ADD_S_v6p, + OP_LE_S_v6p, + OP_GE_S_v6p, + OP_LT_S_v6p, + OP_GT_S_v6p, + + OP_ADD_I_v6p, + OP_SUB_I_v6p, + OP_MUL_I_v6p, + OP_DIV_I_v6p, + OP_BITAND_I_v6p, + OP_BITOR_I_v6p, + OP_GE_I_v6p, + OP_LE_I_v6p, + OP_GT_I_v6p, + OP_LT_I_v6p, + OP_AND_I_v6p, + OP_OR_I_v6p, + OP_NOT_I_v6p, + OP_EQ_I_v6p, + OP_NE_I_v6p, + OP_STORE_I_v6p, + OP_STOREP_I_v6p, + OP_LOAD_I_v6p, + + OP_CONV_IF_v6p, + OP_CONV_FI_v6p, + + OP_BITXOR_F_v6p, + OP_BITXOR_I_v6p, + OP_BITNOT_F_v6p, + OP_BITNOT_I_v6p, + + OP_SHL_F_v6p, + OP_SHR_F_v6p, + OP_SHL_I_v6p, + OP_SHR_I_v6p, + + OP_REM_F_v6p, + OP_REM_I_v6p, + + OP_LOADB_F_v6p, + OP_LOADB_V_v6p, + OP_LOADB_S_v6p, + OP_LOADB_ENT_v6p, + OP_LOADB_FLD_v6p, + OP_LOADB_FN_v6p, + OP_LOADB_I_v6p, + OP_LOADB_P_v6p, + + OP_STOREB_F_v6p, + OP_STOREB_V_v6p, + OP_STOREB_S_v6p, + OP_STOREB_ENT_v6p, + OP_STOREB_FLD_v6p, + OP_STOREB_FN_v6p, + OP_STOREB_I_v6p, + OP_STOREB_P_v6p, + + OP_ADDRESS_VOID_v6p, + OP_ADDRESS_F_v6p, + OP_ADDRESS_V_v6p, + OP_ADDRESS_S_v6p, + OP_ADDRESS_ENT_v6p, + OP_ADDRESS_FLD_v6p, + OP_ADDRESS_FN_v6p, + OP_ADDRESS_I_v6p, + OP_ADDRESS_P_v6p, + + OP_LEA_v6p, + + OP_IFBE_v6p, + OP_IFB_v6p, + OP_IFAE_v6p, + OP_IFA_v6p, + + OP_JUMP_v6p, + OP_JUMPB_v6p, + + OP_LT_U_v6p, + OP_GT_U_v6p, + OP_LE_U_v6p, + OP_GE_U_v6p, + + OP_LOADBI_F_v6p, + OP_LOADBI_V_v6p, + OP_LOADBI_S_v6p, + OP_LOADBI_ENT_v6p, + OP_LOADBI_FLD_v6p, + OP_LOADBI_FN_v6p, + OP_LOADBI_I_v6p, + OP_LOADBI_P_v6p, + + OP_STOREBI_F_v6p, + OP_STOREBI_V_v6p, + OP_STOREBI_S_v6p, + OP_STOREBI_ENT_v6p, + OP_STOREBI_FLD_v6p, + OP_STOREBI_FN_v6p, + OP_STOREBI_I_v6p, + OP_STOREBI_P_v6p, + + OP_LEAI_v6p, + + OP_LOAD_P_v6p, + OP_STORE_P_v6p, + OP_STOREP_P_v6p, + OP_NOT_P_v6p, + OP_EQ_P_v6p, + OP_NE_P_v6p, + OP_LE_P_v6p, + OP_GE_P_v6p, + OP_LT_P_v6p, + OP_GT_P_v6p, + + OP_MOVEI_v6p, + OP_MOVEP_v6p, + OP_MOVEPI_v6p, + + OP_SHR_U_v6p, + + OP_STATE_F_v6p, + + OP_ADD_Q_v6p, + OP_SUB_Q_v6p, + OP_MUL_Q_v6p, + OP_MUL_QF_v6p, + OP_MUL_FQ_v6p, + OP_MUL_QV_v6p, + OP_CONJ_Q_v6p, + OP_NOT_Q_v6p, + OP_EQ_Q_v6p, + OP_NE_Q_v6p, + OP_STORE_Q_v6p, + OP_STOREB_Q_v6p, + OP_STOREBI_Q_v6p, + OP_STOREP_Q_v6p, + OP_LOAD_Q_v6p, + OP_LOADB_Q_v6p, + OP_LOADBI_Q_v6p, + OP_ADDRESS_Q_v6p, + + OP_RCALL0_v6p, + OP_RCALL1_v6p, + OP_RCALL2_v6p, + OP_RCALL3_v6p, + OP_RCALL4_v6p, + OP_RCALL5_v6p, + OP_RCALL6_v6p, + OP_RCALL7_v6p, + OP_RCALL8_v6p, + + OP_RETURN_V_v6p, + + OP_PUSH_S_v6p, + OP_PUSH_F_v6p, + OP_PUSH_V_v6p, + OP_PUSH_ENT_v6p, + OP_PUSH_FLD_v6p, + OP_PUSH_FN_v6p, + OP_PUSH_P_v6p, + OP_PUSH_Q_v6p, + OP_PUSH_I_v6p, + OP_PUSH_D_v6p, + + OP_PUSHB_S_v6p, + OP_PUSHB_F_v6p, + OP_PUSHB_V_v6p, + OP_PUSHB_ENT_v6p, + OP_PUSHB_FLD_v6p, + OP_PUSHB_FN_v6p, + OP_PUSHB_P_v6p, + OP_PUSHB_Q_v6p, + OP_PUSHB_I_v6p, + OP_PUSHB_D_v6p, + + OP_PUSHBI_S_v6p, + OP_PUSHBI_F_v6p, + OP_PUSHBI_V_v6p, + OP_PUSHBI_ENT_v6p, + OP_PUSHBI_FLD_v6p, + OP_PUSHBI_FN_v6p, + OP_PUSHBI_P_v6p, + OP_PUSHBI_Q_v6p, + OP_PUSHBI_I_v6p, + OP_PUSHBI_D_v6p, + + OP_POP_S_v6p, + OP_POP_F_v6p, + OP_POP_V_v6p, + OP_POP_ENT_v6p, + OP_POP_FLD_v6p, + OP_POP_FN_v6p, + OP_POP_P_v6p, + OP_POP_Q_v6p, + OP_POP_I_v6p, + OP_POP_D_v6p, + + OP_POPB_S_v6p, + OP_POPB_F_v6p, + OP_POPB_V_v6p, + OP_POPB_ENT_v6p, + OP_POPB_FLD_v6p, + OP_POPB_FN_v6p, + OP_POPB_P_v6p, + OP_POPB_Q_v6p, + OP_POPB_I_v6p, + OP_POPB_D_v6p, + + OP_POPBI_S_v6p, + OP_POPBI_F_v6p, + OP_POPBI_V_v6p, + OP_POPBI_ENT_v6p, + OP_POPBI_FLD_v6p, + OP_POPBI_FN_v6p, + OP_POPBI_P_v6p, + OP_POPBI_Q_v6p, + OP_POPBI_I_v6p, + OP_POPBI_D_v6p, + + OP_ADD_D_v6p, + OP_SUB_D_v6p, + OP_MUL_D_v6p, + OP_MUL_QD_v6p, + OP_MUL_DQ_v6p, + OP_MUL_VD_v6p, + OP_MUL_DV_v6p, + OP_DIV_D_v6p, + OP_REM_D_v6p, + OP_GE_D_v6p, + OP_LE_D_v6p, + OP_GT_D_v6p, + OP_LT_D_v6p, + OP_NOT_D_v6p, + OP_EQ_D_v6p, + OP_NE_D_v6p, + OP_CONV_FD_v6p, + OP_CONV_DF_v6p, + OP_CONV_ID_v6p, + OP_CONV_DI_v6p, + OP_STORE_D_v6p, + OP_STOREB_D_v6p, + OP_STOREBI_D_v6p, + OP_STOREP_D_v6p, + OP_LOAD_D_v6p, + OP_LOADB_D_v6p, + OP_LOADBI_D_v6p, + OP_ADDRESS_D_v6p, + + OP_MOD_I_v6p, + OP_MOD_F_v6p, + OP_MOD_D_v6p, + + OP_MEMSETI_v6p, + OP_MEMSETP_v6p, + OP_MEMSETPI_v6p, +} pr_opcode_v6p_e; +#define OP_BREAK 0x8000 + +typedef enum { +#ifndef IN_DOXYGEN +#include "QF/progs/pr_opcode.hinc" +#endif +} pr_opcode_e; + +// Used for both branch and comparison, with jump and call being ignored for +// comparison. For branches, the test is against zero, while for comparison, +// it's a cmp b (where cmp takes the place of "branch" in the enum names). +typedef enum { + pr_branch_eq, + pr_branch_lt, + pr_branch_gt, + pr_branch_jump, + pr_branch_ne, + pr_branch_ge, + pr_branch_le, + pr_branch_call, +} pr_branch_e; + +#define OP_A_SHIFT (9) +#define OP_B_SHIFT (11) +#define OP_C_SHIFT (13) +#define OP_A_BASE (3 << OP_A_SHIFT) +#define OP_B_BASE (3 << OP_B_SHIFT) +#define OP_C_BASE (3 << OP_C_SHIFT) +#define OP_MASK (~(OP_BREAK|OP_A_BASE|OP_B_BASE|OP_C_BASE)) + +typedef enum { + OP_with_zero, + OP_with_base, + OP_with_stack, + OP_with_entity, +} pr_with_e; + +typedef struct v6p_opcode_s { + const char *name; + const char *opname; + etype_t type_a, type_b, type_c; + unsigned int min_version; + const char *fmt; +} v6p_opcode_t; + +extern const v6p_opcode_t pr_v6p_opcodes[]; +const v6p_opcode_t *PR_v6p_Opcode (pr_ushort_t opcode) __attribute__((const)); + +typedef struct opcode_s { + const char *opname; + const char *mnemonic; + int widths[3]; ///< component count for each argument (1-4) + etype_t types[3]; ///< component type for each argument + const char *fmt; +} opcode_t; +extern const opcode_t pr_opcodes[512]; +const opcode_t *PR_Opcode (pr_ushort_t opcode) __attribute__((const)); + +typedef struct dstatement_s { + pr_opcode_e op:16; // will be pr_opcode_v6p_e for older progs + pr_ushort_t a,b,c; +} GCC_STRUCT dstatement_t; + +typedef struct ddef_s { + pr_ushort_t type; // if DEF_SAVEGLOBAL bit is set + // the variable needs to be saved in savegames + pr_ushort_t ofs; + pr_string_t name; +} ddef_t; + +typedef struct xdef_s { + pr_ptr_t type; ///< pointer to type definition + pr_ptr_t ofs; ///< 32-bit version of ddef_t.ofs +} xdef_t; + +typedef struct pr_xdefs_s { + pr_ptr_t xdefs; + pr_int_t num_xdefs; +} pr_xdefs_t; + +typedef struct pr_def_s { + pr_ushort_t type; + pr_ushort_t size; ///< may not be correct + pr_ptr_t ofs; + pr_string_t name; + pr_ptr_t type_encoding; +} pr_def_t; + +typedef struct dparmsize_s { + uint8_t size:5; + uint8_t alignment:3; +} dparmsize_t; + +#define DEF_SAVEGLOBAL (1<<15) + +#define PR_MAX_PARAMS 8 +#define PR_MAX_RETURN 32 // maximum size of return value + +typedef struct dfunction_s { + pr_int_t first_statement; // negative numbers are builtins + pr_uint_t params_start; // beginning of locals data space + pr_uint_t locals; // total ints of params + locals + + pr_uint_t profile; // runtime + + pr_string_t name; // source function name + pr_string_t file; // source file defined in + + pr_int_t numparams; // -ve is varargs (1s comp of real count) + dparmsize_t param_size[PR_MAX_PARAMS]; +} dfunction_t; + +typedef struct pr_type_s { + union { + pr_int_t value; + pr_uint_t uint_value; + pr_float_t float_value; + }; +} pr_type_t; + +typedef pr_type_t pr_void_t; // so size of void is 1 + +typedef struct pr_va_list_s { + pr_int_t count; + pr_ptr_t list; // pr_type_t +} pr_va_list_t; + +#define PROG_VERSION_ENCODE(a,b,c) \ + ( (((0x##a) & 0x0ff) << 24) \ + |(((0x##b) & 0xfff) << 12) \ + |(((0x##c) & 0xfff) << 0) ) +#define PROG_ID_VERSION 6 +#define PROG_V6P_VERSION PROG_VERSION_ENCODE(0,fff,00a) +#define PROG_VERSION PROG_VERSION_ENCODE(0,fff,010) + +typedef struct pr_chunk_s { + pr_uint_t offset; + pr_uint_t count; +} pr_chunk_t; + +typedef struct dprograms_s { + pr_uint_t version; + pr_uint_t crc; // checksum of header file + + pr_chunk_t statements; // statement 0 is an error + pr_chunk_t globaldefs; + pr_chunk_t fielddefs; + pr_chunk_t functions; // function 0 is an empty + pr_chunk_t strings; // first string is a null string, count is bytes + pr_chunk_t globals; + + pr_uint_t entityfields; +} dprograms_t; + +#endif//__QF_pr_comp_h diff --git a/include/QF/pr_debug.h b/include/QF/progs/pr_debug.h similarity index 67% rename from include/QF/pr_debug.h rename to include/QF/progs/pr_debug.h index 65b953381..20b65c25c 100644 --- a/include/QF/pr_debug.h +++ b/include/QF/progs/pr_debug.h @@ -28,10 +28,18 @@ */ -#ifndef __pr_debug_h -#define __pr_debug_h +#ifndef __QF_pr_debug_h +#define __QF_pr_debug_h -#include "QF/pr_comp.h" +#ifndef __QFCC__ +#include "QF/progs/pr_comp.h" + +typedef struct pr_compunit_s { + pr_uint_t unit_name; + pr_uint_t basedir; + pr_uint_t num_files; + pr_uint_t files[1]; +} pr_compunit_t; typedef struct pr_auxfunction_s { pr_uint_t function; // function def this aux info is for @@ -39,8 +47,7 @@ typedef struct pr_auxfunction_s { pr_uint_t line_info; // index to first lineno entry pr_uint_t local_defs; // index to the first local def pr_uint_t num_locals; // number of local defs - pr_short_t return_type; // return type of this function - pr_short_t reserved; + pr_uint_t return_type; // return type of this function } pr_auxfunction_t; typedef struct pr_lineno_s { @@ -51,7 +58,7 @@ typedef struct pr_lineno_s { pr_uint_t line; } pr_lineno_t; -#define PROG_DEBUG_VERSION 0x00001002 // MMmmmRRR 0.001.002 (hex) +#define PROG_DEBUG_VERSION 0x00001004 // MMmmmRRR 0.001.004 (hex) typedef struct pr_debug_header_s { pr_int_t version; @@ -63,6 +70,26 @@ typedef struct pr_debug_header_s { pr_uint_t num_linenos; pr_uint_t locals; pr_uint_t num_locals; + pr_uint_t debug_defs; + pr_uint_t num_debug_defs; + pr_uint_t debug_data; + pr_uint_t debug_data_size; } pr_debug_header_t; -#endif//__pr_debug_h +extern const char *prdebug_names[]; +#endif + +typedef enum prdebug_e { + prd_none, + prd_trace, + prd_breakpoint, + prd_watchpoint, + prd_subenter, + prd_subexit, // current invocation of PR_ExecuteProgram finished + prd_begin, // not sent by VM + prd_terminate, // not sent by VM + prd_runerror, + prd_error, // lower level error thann prd_runerror +} prdebug_t; + +#endif//__QF_pr_debug_h diff --git a/include/QF/pr_obj.h b/include/QF/progs/pr_obj.h similarity index 69% rename from include/QF/pr_obj.h rename to include/QF/progs/pr_obj.h index 5e4f8463c..2841f6064 100644 --- a/include/QF/pr_obj.h +++ b/include/QF/progs/pr_obj.h @@ -28,10 +28,10 @@ */ -#ifndef __pr_obj_h -#define __pr_obj_h +#ifndef __QF_pr_obj_h +#define __QF_pr_obj_h -#include "QF/pr_comp.h" +#include "QF/progs/pr_comp.h" #define PR_BITS_PER_INT (sizeof (pr_int_t) * 8) @@ -74,62 +74,62 @@ #define PR_CLS_GETNUMBER(cls) (__CLS_INFO (cls) >> (PR_BITS_PER_INT / 2)) #define PR_CLS_SETNUMBER(cls, num) \ (__PR_CLS_INFO (cls) = __PR_CLS_INFO (cls) & (~0U >> (PR_BITS_PER_INT / 2)) \ - | (num) << (PR_BITS_PER_INT / 2)) + | (num) << (PR_BITS_PER_INT / 2)) typedef struct pr_sel_s { - pointer_t sel_id; - string_t sel_types; + pr_ptr_t sel_id; + pr_string_t sel_types; } pr_sel_t; typedef struct pr_id_s { - pointer_t class_pointer; // pr_class_t + pr_ptr_t class_pointer; // pr_class_t } pr_id_t; typedef struct pr_class_s { - pointer_t class_pointer; // pr_class_t - pointer_t super_class; // pr_class_t - string_t name; + pr_ptr_t class_pointer; // pr_class_t + pr_ptr_t super_class; // pr_class_t + pr_string_t name; pr_int_t version; pr_uint_t info; pr_int_t instance_size; - pointer_t ivars; // pr_ivar_list_t - pointer_t methods; // pr_method_list_t - pointer_t dtable; - pointer_t subclass_list; // pr_class_t - pointer_t sibling_class; // pr_class_t - pointer_t protocols; // pr_protocol_list_t - pointer_t gc_object_type; + pr_ptr_t ivars; // pr_ivar_list_t + pr_ptr_t methods; // pr_method_list_t + pr_ptr_t dtable; // resource index + pr_ptr_t subclass_list; // pr_class_t + pr_ptr_t sibling_class; // pr_class_t + pr_ptr_t protocols; // pr_protocol_list_t + pr_ptr_t gc_object_type; } pr_class_t; typedef struct pr_protocol_s { - pointer_t class_pointer; // pr_class_t - string_t protocol_name; - pointer_t protocol_list; // pr_protocol_list_t - pointer_t instance_methods; // pr_method_description_list_t - pointer_t class_methods; // pr_method_description_list_t + pr_ptr_t class_pointer; // pr_class_t + pr_string_t protocol_name; + pr_ptr_t protocol_list; // pr_protocol_list_t + pr_ptr_t instance_methods; // pr_method_description_list_t + pr_ptr_t class_methods; // pr_method_description_list_t } pr_protocol_t; typedef struct pr_category_s { - string_t category_name; - string_t class_name; - pointer_t instance_methods; // pr_method_list_t - pointer_t class_methods; // pr_method_list_t - pointer_t protocols; // pr_protocol_list_t + pr_string_t category_name; + pr_string_t class_name; + pr_ptr_t instance_methods; // pr_method_list_t + pr_ptr_t class_methods; // pr_method_list_t + pr_ptr_t protocols; // pr_protocol_list_t } pr_category_t; typedef struct pr_protocol_list_s { - pointer_t next; + pr_ptr_t next; pr_int_t count; - pointer_t list[1]; // pr_protocol_t + pr_ptr_t list[1]; // pr_protocol_t } pr_protocol_list_t; typedef struct pr_method_list_s { - pointer_t method_next; + pr_ptr_t method_next; pr_int_t method_count; struct pr_method_s { - pointer_t method_name; // pr_sel_t - string_t method_types; - func_t method_imp; // typedef id (id, SEL, ...) IMP + pr_ptr_t method_name; // pr_sel_t + pr_string_t method_types; + pr_func_t method_imp; // typedef id (id, SEL, ...) IMP } method_list[1]; } pr_method_list_t; typedef struct pr_method_s pr_method_t; @@ -137,8 +137,8 @@ typedef struct pr_method_s pr_method_t; typedef struct pr_method_description_list_s { pr_int_t count; struct pr_method_description_s { - pointer_t name; // pr_sel_t - string_t types; + pr_ptr_t name; // pr_sel_t + pr_string_t types; } list[1]; } pr_method_description_list_t; typedef struct pr_method_description_s pr_method_description_t; @@ -146,8 +146,8 @@ typedef struct pr_method_description_s pr_method_description_t; typedef struct pr_ivar_list_s { pr_int_t ivar_count; struct pr_ivar_s { - string_t ivar_name; - string_t ivar_type; + pr_string_t ivar_name; + pr_string_t ivar_type; pr_int_t ivar_offset; } ivar_list[1]; } pr_ivar_list_t; @@ -157,16 +157,16 @@ typedef struct pr_static_instances_s { // one per staticly instanced class per module (eg, 3 instances of Object // will produce one of these structs with 3 pointers to those instances in // instances[] - string_t class_name; - pointer_t instances[1]; // null terminated array of pr_id_t + pr_string_t class_name; + pr_ptr_t instances[1]; // null terminated array of pr_id_t } pr_static_instances_t; typedef struct pr_symtab_s { pr_int_t sel_ref_cnt; - pointer_t refs; // pr_sel_t + pr_ptr_t refs; // pr_sel_t pr_int_t cls_def_cnt; pr_int_t cat_def_cnt; - pointer_t defs[1]; // variable array of cls_def_cnt class + pr_ptr_t defs[1]; // variable array of cls_def_cnt class // pointers then cat_def_cnt category // pointers followed by a null terminated // array of pr_static_instances (not yet @@ -176,13 +176,13 @@ typedef struct pr_symtab_s { typedef struct pr_module_s { pr_int_t version; pr_int_t size; - string_t name; - pointer_t symtab; // pr_symtab_t + pr_string_t name; + pr_ptr_t symtab; // pr_symtab_t } pr_module_t; typedef struct pr_super_s { - pointer_t self; - pointer_t class; + pr_ptr_t self; + pr_ptr_t class; } pr_super_t; -#endif//__pr_obj_h +#endif//__QF_pr_obj_h diff --git a/include/QF/progs/pr_type.h b/include/QF/progs/pr_type.h new file mode 100644 index 000000000..a3c1528b1 --- /dev/null +++ b/include/QF/progs/pr_type.h @@ -0,0 +1,139 @@ +/* + pr_type.h + + object file type encoding support + + Copyright (C) 2011 Bill Currie + + Author: Bill Currie + Date: 2011/02/18 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_pr_type_h +#define __QF_pr_type_h + +/** \defgroup qfcc_qfo_type Object file type encoding + \ingroup progs + + All \c pr_ptr_t \c type fields are pointers within the type qfo_space. +*/ +///@{ + +#include "QF/progs/pr_comp.h" + +typedef enum { + ty_basic, ///< VM type (float, int, pointer, field, etc) + ty_struct, + ty_union, + ty_enum, + ty_array, + ty_class, + ty_alias, + ty_handle, +} ty_meta_e; + +typedef struct qfot_alias_s { + etype_t type; ///< type at end of alias chain + pr_ptr_t aux_type; ///< referenced type: stripped of aliases + pr_ptr_t full_type; ///< includes full alias info + pr_string_t name; ///< alias name, may be null +} qfot_alias_t; + +typedef struct qfot_handle_s { + etype_t type; + pr_string_t tag; +} qfot_handle_t; + +typedef struct qfot_fldptr_s { + etype_t type; ///< ev_field or ev_ptr + pr_ptr_t aux_type; ///< referenced type +} qfot_fldptr_t; + +typedef struct qfot_basic_s { + etype_t type; ///< integral and fp scalar types + pr_int_t width; ///< components in vector (1 for vector or + ///< quaternion) +} qfot_basic_t; + +typedef struct qfot_func_s { + etype_t type; ///< always ev_func + pr_ptr_t return_type; ///< return type of the function + pr_int_t num_params; ///< ones compliment count of the + ///< parameters. -ve values indicate the + ///< number of real parameters before the + ///< ellipsis + pr_ptr_t param_types[1]; ///< variable length list of parameter + ///< types +} qfot_func_t; + +typedef struct qfot_var_s { + pr_ptr_t type; ///< type of field or self reference for + ///< enum + pr_string_t name; ///< name of field/enumerator + pr_int_t offset; ///< value for enum, 0 for union +} qfot_var_t; + +typedef struct qfot_struct_s { + pr_string_t tag; ///< struct/union/enum tag + pr_int_t num_fields; ///< number of fields/enumerators + qfot_var_t fields[1]; ///< variable length list of + ///< fields/enumerators +} qfot_struct_t; + +typedef struct qfot_array_s { + pr_ptr_t type; ///< element type + pr_int_t base; ///< start index of array + pr_int_t size; ///< number of elements in array +} qfot_array_t; + +/** QFO type encoding. + + \note As this holds a union of all type representations, and those + representations may contain variable arrays, sizeof() will return only + one, rather useless, value. It is also not suitable for direct use in + arrays. +*/ +typedef struct qfot_type_s { + ty_meta_e meta; ///< meta type + pr_uint_t size; ///< total word size of this encoding + pr_string_t encoding; ///< Objective-QC encoding + union { + etype_t type; ///< ty_basic: etype_t + qfot_basic_t basic; ///< ty_basic: int/float/double/long etc + qfot_fldptr_t fldptr; ///< ty_basic, ev_ptr/ev_field + qfot_func_t func; ///< ty_basic, ev_func + qfot_struct_t strct; ///< ty_struct/ty_union/ty_enum + qfot_array_t array; ///< ty_array + pr_string_t class; ///< ty_class + qfot_alias_t alias; ///< ty_alias + qfot_handle_t handle; ///< ty_handle + }; +} qfot_type_t; + +typedef struct qfot_type_encodings_s { + pr_ptr_t types; + pr_uint_t size; +} qfot_type_encodings_t; + +///@} + +#endif//__QF_pr_type_h diff --git a/include/QF/progs/pr_type_names.h b/include/QF/progs/pr_type_names.h new file mode 100644 index 000000000..3e0daf10a --- /dev/null +++ b/include/QF/progs/pr_type_names.h @@ -0,0 +1,50 @@ +/* + pr_type_names.h + + Progs type names + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2022 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef EV_TYPE +#define EV_TYPE(event) +#endif + +EV_TYPE(void) +EV_TYPE(string) +EV_TYPE(float) +EV_TYPE(vector) +EV_TYPE(entity) +EV_TYPE(field) +EV_TYPE(func) +EV_TYPE(ptr) // end of v6 types +EV_TYPE(quaternion) +EV_TYPE(int) +EV_TYPE(uint) +EV_TYPE(short) // value is embedded in the opcode +EV_TYPE(double) +EV_TYPE(long) +EV_TYPE(ulong) +EV_TYPE(ushort) // value is embedded in the opcode + +#undef EV_TYPE diff --git a/include/QF/pvsfile.h b/include/QF/pvsfile.h new file mode 100644 index 000000000..5b57e447c --- /dev/null +++ b/include/QF/pvsfile.h @@ -0,0 +1,64 @@ +/* + pvsfile.h + + External pvs file support + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/07/27 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_pvsfile_h +#define __QF_pvsfile_h + +#include + +#include "QF/qtypes.h" + +/** \defgroup pvsfile External PVS + \ingroup utils + + External PVS data. +*/ +///@{ + +#define PVS_MAGIC "QF PVS\r\n\x1a\x04" +#define PVS_VERSION 1 + +#define PVS_IS_FATPVS 1 ///< data is for fat pvs (phs, lighting, etc) + ///< otherwise it's the base pvs +#define PVS_UTF8_RLE 2 ///< 0-byte run length encoding uses utf-8 + +typedef struct pvsfile_s { + char magic[16]; + + uint32_t version; + uint32_t md4_offset; ///< offset of md4 sum, or 0 if not present + uint32_t flags; + uint32_t visleafs; ///< does NOT include leaf-0 + uint32_t visoffsets[]; ///< does NOT include leaf-0 +} pvsfile_t; + +///@} + +#endif//__QF_pvsfile_h diff --git a/include/QF/qargs.h b/include/QF/qargs.h index 5b4252075..689b5b5a1 100644 --- a/include/QF/qargs.h +++ b/include/QF/qargs.h @@ -27,29 +27,32 @@ */ -#ifndef __qargs_h -#define __qargs_h +#ifndef __QF_qargs_h +#define __QF_qargs_h /** \addtogroup misc */ -//@{ +///@{ #include "QF/qtypes.h" extern int com_argc; extern const char **com_argv; extern const char *com_cmdline; -extern struct cvar_s *fs_globalcfg; -extern struct cvar_s *fs_usercfg; +extern char *fs_globalcfg; +extern char *fs_usercfg; -int COM_CheckParm (const char *parm); +int COM_CheckParm (const char *parm) __attribute__((pure)); void COM_AddParm (const char *parm); void COM_Init (void); void COM_Init_Cvars (void); void COM_InitArgv (int argc, const char **argv); -void COM_ParseConfig (void); +struct cbuf_s; +void COM_ParseConfig (struct cbuf_s *cbuf); +void COM_ExecConfig (struct cbuf_s *cbuf, int skip_quakerc); +int COM_Check_quakerc (const char *cmd, struct cbuf_s *cbuf); -//@} +///@} -#endif // __qargs_h +#endif//__QF_qargs_h diff --git a/include/QF/qdefs.h b/include/QF/qdefs.h index 1d00abee0..a0dbbfe5c 100644 --- a/include/QF/qdefs.h +++ b/include/QF/qdefs.h @@ -27,8 +27,8 @@ */ -#ifndef __qdefs_h -#define __qdefs_h +#ifndef __QF_qdefs_h +#define __QF_qdefs_h #define MAX_QPATH 64 #define MAX_CL_STATS 32 @@ -40,4 +40,4 @@ #define clc_stringcmd 4 -#endif // __qdefs_h +#endif//__QF_qdefs_h diff --git a/include/QF/qendian.h b/include/QF/qendian.h index 49e60c2aa..147ec4c75 100644 --- a/include/QF/qendian.h +++ b/include/QF/qendian.h @@ -27,13 +27,13 @@ */ -#ifndef __qendian_h -#define __qendian_h +#ifndef __QF_qendian_h +#define __QF_qendian_h /** \defgroup qendian Endian handling functions \ingroup utils */ -//@{ +///@{ #include "QF/qtypes.h" @@ -73,14 +73,14 @@ #define LittleFloat FloatSwap #endif -extern qboolean bigendien; +extern bool bigendien; -uint16_t _ShortSwap (uint16_t l); -uint16_t _ShortNoSwap (uint16_t l); -uint32_t _LongSwap (uint32_t l); -uint32_t _LongNoSwap (uint32_t l); -float _FloatSwap (float f); -float _FloatNoSwap (float f); +uint16_t _ShortSwap (uint16_t l) __attribute__((const)); +uint16_t _ShortNoSwap (uint16_t l) __attribute__((const)); +uint32_t _LongSwap (uint32_t l) __attribute__((const)); +uint32_t _LongNoSwap (uint32_t l) __attribute__((const)); +float _FloatSwap (float f) __attribute__((const)); +float _FloatNoSwap (float f) __attribute__((const)); #ifdef __GNUC__ @@ -118,6 +118,6 @@ byte ReadByte (struct QFile_s *file); unsigned short ReadShort (struct QFile_s *file); unsigned int ReadLong (struct QFile_s *file); -//@} +///@} -#endif // __qendian_h +#endif//__QF_qendian_h diff --git a/include/QF/qfplist.h b/include/QF/qfplist.h deleted file mode 100644 index 713eb6dbb..000000000 --- a/include/QF/qfplist.h +++ /dev/null @@ -1,231 +0,0 @@ -/* - qfplist.h - - Property list management types and prototypes - - Copyright (C) 2000 Jeff Teunissen - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ - -#ifndef __QF_qfplist_h_ -#define __QF_qfplist_h_ - -/** \defgroup qfplist Property lists - \ingroup utils -*/ -//@{ - -#include "QF/qtypes.h" - -/** The type of the property list item. - - For further details, see \ref property-list. -*/ -typedef enum { - QFDictionary, ///< The property list item represents a dictionary. - QFArray, ///< The property list item represents an array. - QFBinary, ///< The property list item represents arbitrary binary - ///< data. - QFString ///< The property list item represents a C string. -} pltype_t; - -/** Generic property list item. - - All inspection and manipulation is to be done via the accessor functions. -*/ -typedef struct plitem_s plitem_t; - -/** Create an in-memory representation of the contents of a property list. - - \param string the saved plist, as read from a file. - - \return Returns an object equivalent to the passed-in string. - \note You are responsible for freeing the returned object. -*/ -plitem_t *PL_GetPropertyList (const char *string); - -/** Create a property list string from the in-memory representation. - - \param pl the in-memory representation - \return the text representation of the property list - \note You are responsible for freeing the returned string. -*/ -char *PL_WritePropertyList (plitem_t *pl); - -/** Retrieve the type of an object. - - \param item The object - \return the type of the object -*/ -pltype_t PL_Type (plitem_t *item); - -/** Retrieve a string from a string object. - - \param string The string object - \return pointer to the actual string value or NULL if string isn't a - string. - \note You are NOT responsible for freeing the returned object. It will - be destroyed when its container is. -*/ -const char *PL_String (plitem_t *string); - -/** Retrieve a value from a dictionary object. - - \param dict The dictionary to retrieve a value from - \param key The unique key associated with the value - \return the value associated with the key, or NULL if not found or dict - isn't a dictionary. - \note You are NOT responsible for freeing the returned object. It will - be destroyed when its container is. -*/ -plitem_t *PL_ObjectForKey (plitem_t *dict, const char *key); - -/** Remove a value from a dictionary object. - - \param dict The Dictionary to remove the value from - \param key The unique key associated with the value to be removed - \return the value associated with the key, or NULL if not found or dict - isn't a dictionary. - \note You are responsible for freeing the returned object. -*/ -plitem_t *PL_RemoveObjectForKey (plitem_t *dict, const char *key); - -/** Retrieve a value from an array object. - - \param array The array to get the value from - \param index The index within the array to retrieve - \return the value associated with the key, or NULL if not found or array - isn't an array. - \note You are NOT responsible for freeing the returned object. It will - be destroyed when its container is. -*/ -plitem_t *PL_ObjectAtIndex (plitem_t *array, int index); - -/** Retrieve a list of all keys in a dictionary. - - \param dict The dictionary to list - \return an Array containing Strings or NULL if dict isn't a dictionary - \note You are responsible for freeing this array. -*/ -plitem_t *PL_D_AllKeys (plitem_t *dict); - -/** Retrieve the number of keys in a dictionary. - - \param dict The dictionary to get the number of keys of. - - \return Returns the number of keys in the dictionary. -*/ -int PL_D_NumKeys (plitem_t *dict); - -/** Add a key/value pair to a dictionary. - - \param dict The dictionary to which the key/value pair will be added - \param key The key of the key/value pair to be added to the dictionary - \param value The value of the key/value pair to be added to the dictionary - - \return true on success, false on failure - - \note the dictionary becomes the owner of the value. -*/ -qboolean PL_D_AddObject (plitem_t *dict, const char *key, plitem_t *value); - -/** Add an item to an array. - - \param array The array to which the item will be added - \param item The item to be added to the array - - \return true on success, false on failure - - \note the array becomes the owner of the added item. -*/ -qboolean PL_A_AddObject (plitem_t *array, plitem_t *item); - -/** Retrieve the number of items in an array. - - \param array The array from which to get the number of objects - - \return number of objects in the array -*/ -int PL_A_NumObjects (plitem_t *array); - -/** Insert an item into an array before the specified location. - - \param array The array to which the item will be added - \param item The item to be added to the array - \param index The location at which to insert the item into the array - - \return true on success, false on failure - - \note the array becomes the owner of the added item. -*/ -qboolean PL_A_InsertObjectAtIndex (plitem_t *array, plitem_t *item, int index); - -/** Remove a value from an array object. - The array items will be shuffled to fill the resulting hole. - - \param array The array from which to remove the value - \param index The index within the array to remove - \return the value associated with the index, or NULL if not found or array - isn't an array. - \note You are responsible for freeing the returned object. -*/ -plitem_t *PL_RemoveObjectAtIndex (plitem_t *array, int index); - -/** Create a new dictionary object. - The dictionary will be empty. - \return the new dictionary object -*/ -plitem_t *PL_NewDictionary (void); - -/** Create a new array object. - The array will be empty. - \return the new array object -*/ -plitem_t *PL_NewArray (void); - -/** Create a new data object from the given data. - Takes ownership of the given data. - \param data pointer to data buffer - \param size number of bytes in the buffer - \return the new dictionary object - \note The data will be freed via free() when the item is freed. -*/ -plitem_t *PL_NewData (void *data, size_t size); - -/** Create a new string object. - Makes a copy of the given string. - \param str C string to copy - \return the new dictionary object -*/ -plitem_t *PL_NewString (const char *str); - -/** Free a property list object. - - This function takes care of freeing any referenced property list data, so - call it only on top-level objects. - - \param item the property list object to be freed -*/ -void PL_Free (plitem_t *item); - -//@} - -#endif // __QF_qfplist_h_ diff --git a/include/QF/qtypes.h b/include/QF/qtypes.h index 05c93af59..5647f685b 100644 --- a/include/QF/qtypes.h +++ b/include/QF/qtypes.h @@ -27,11 +27,12 @@ */ -#ifndef __qtypes_h -#define __qtypes_h +#ifndef __QF_qtypes_h +#define __QF_qtypes_h #include #include +#include #ifdef HAVE_SYSTEM_MSG_T # define msg_t sys_msg_t @@ -51,21 +52,20 @@ typedef uint8_t byte; #endif +#if __STDC_VERSION__ < 202000 +#define auto __auto_type #ifndef _DEF_BOOL_ # define _DEF_BOOL_ // KJB Undefined true and false defined in SciTech's DEBUG.H header -#ifdef __cplusplus -# define __bool_true_false_are_defined -#endif -# ifdef __bool_true_false_are_defined -typedef enum {q_false = false, q_true = true} qboolean; -//#define true q_true; -//#define false q_false; -# else -#undef true -#undef false -typedef enum {false, true} qboolean; +# ifdef __cplusplus +# define __bool_true_false_are_defined # endif +# ifndef __bool_true_false_are_defined +# undef true +# undef false +typedef enum {false, true} bool; +# endif +#endif #endif // From mathlib... @@ -76,8 +76,8 @@ typedef vec_t quat_t[4]; ///< A quaternion. typedef vec_t vec5_t[5]; typedef union { struct { - vec_t s; vec3_t v; + vec_t s; } sv; quat_t q; } Quat_t; @@ -115,4 +115,4 @@ typedef struct sphere_s { vec_t radius; } sphere_t; -#endif // __qtypes_h +#endif//__QF_qtypes_h diff --git a/include/QF/quakefs.h b/include/QF/quakefs.h index aab034381..91f7cb440 100644 --- a/include/QF/quakefs.h +++ b/include/QF/quakefs.h @@ -27,13 +27,13 @@ */ -#ifndef __quakefs_h -#define __quakefs_h +#ifndef __QF_quakefs_h +#define __QF_quakefs_h /** \defgroup quakefs Quake Filesystem \ingroup utils */ -//@{ +///@{ #include "QF/qtypes.h" #include "QF/quakeio.h" @@ -71,11 +71,11 @@ typedef struct vpath_s vpath_t; typedef struct findfile_s { const vpath_t *vpath; ///< vpath in which file was found - qboolean in_pak; ///< if true, path refers to a pak file rather - ///< than a directory const char *realname; ///< the name of the file as found (may have ///< .gz appended, or .ogg substituded for ///< .wav) does not include the path + bool in_pak; ///< if true, path refers to a pak file rather + ///< than a directory } findfile_t; /** Cached information about the current game directory. \see \ref dirconf. @@ -84,8 +84,9 @@ extern gamedir_t *qfs_gamedir; /** Function type of callback called on gamedir change. \param phase 0 = before Cache_Flush(), 1 = after Cache_Flush() + \param data data pointer passed on to the callback */ -typedef void gamedir_callback_t (int phase); +typedef void gamedir_callback_t (int phase, void *data); /** Base of the QFS user directory tree. The QFS functions, except for QFS_FOpenFile() and _QFS_FOpenFile(), will never access a file outside of @@ -107,6 +108,7 @@ extern int qfs_filesize; struct cache_user_s; struct dstring_s; +struct memhunk_s; /** Initialize the Quake Filesystem. @@ -114,13 +116,14 @@ struct dstring_s; \c fs_dirconf Cvars. It then loads the \ref dirconf and parses the \c -game command line option. + \param hunk Memory pool to use for hunk-based allocations. \param game The game type used for searching the directory configuration. Currently, this is \"qw\" for quakeworld clients and servers, and one of \"nq\", \"hexen\", \"rogue\" or \"abyss\" for the netquake clients and servers. */ -void QFS_Init (const char *game); +void QFS_Init (struct memhunk_s *hunk, const char *game); /** Change the current game directory. @@ -288,17 +291,20 @@ int QFS_Remove (const char *path); /** Find available filename. The filename will be of the form \c prefixXXXX.ext where \c XXXX - is a zero padded number from 0 to 9999. + is a zero padded number from 0 to 9999. Should there already be 10000 + files of such a pattern, then extra digits will be added. \param filename This will be set to the available filename. \param prefix The part of the filename preceeding the numers. \param ext The extension to add to the filename. - \return 1 for success, 0 for failure. + \return NULL for failure (with an error message in \a filename) + or a quakeio file handle. - \note \a prefix is relative to \c qfc_userpath. + \note \a prefix is relative to \c qfc_userpath, as is the generated + filename. */ -int QFS_NextFilename (struct dstring_s *filename, const char *prefix, - const char *ext); +QFile *QFS_NextFile (struct dstring_s *filename, const char *prefix, + const char *ext); /** Extract the non-extension part of the file name from the path. @@ -356,7 +362,7 @@ char *QFS_CompressPath (const char *pth); \return Pointer to the beginning of the filename. This points inside \a pathname. */ -const char *QFS_SkipPath (const char *pathname); +const char *QFS_SkipPath (const char *pathname) __attribute__((pure)); /** Return a pointer to the start of the extention part of the path. @@ -366,7 +372,7 @@ const char *QFS_SkipPath (const char *pathname); the returned pointer will point to the terminating nul of the path. */ -const char *QFS_FileExtension (const char *in); +const char *QFS_FileExtension (const char *in) __attribute__((pure)); /** Register a callback function for when the gamedir changes. @@ -376,8 +382,9 @@ const char *QFS_FileExtension (const char *in); \param func The function to call every time the gamedir changes via QFS_Gamedir(). + \param data Opaque data pointer passed to the callback. */ -void QFS_GamedirCallback (gamedir_callback_t *func); +void QFS_GamedirCallback (gamedir_callback_t *func, void *data); /** Create a new file list. @@ -418,6 +425,6 @@ void QFS_FilelistFill (filelist_t *list, const char *path, const char *ext, */ void QFS_FilelistFree (filelist_t *list); -//@} +///@} -#endif // __quakefs_h +#endif//__QF_quakefs_h diff --git a/include/QF/quakeio.h b/include/QF/quakeio.h index 0664bd0b6..788b7ee7b 100644 --- a/include/QF/quakeio.h +++ b/include/QF/quakeio.h @@ -26,21 +26,21 @@ Boston, MA 02111-1307, USA */ -#ifndef __quakeio_h -#define __quakeio_h +#ifndef __QF_quakeio_h +#define __QF_quakeio_h #include /** \defgroup quakeio File IO \ingroup utils */ -//@{ +///@{ typedef struct QFile_s QFile; int Qrename(const char *old_path, const char *new_path); int Qremove(const char *path); -int Qfilesize (QFile *file); +int Qfilesize (QFile *file) __attribute__((pure)); QFile *Qopen(const char *path, const char *mode); QFile *Qdopen(int fd, const char *mode); QFile *Qfopen (FILE *file, const char *mode); @@ -48,7 +48,7 @@ QFile *Qsubopen (const char *path, int offs, int len, int zip); void Qclose(QFile *file); int Qread(QFile *file, void *buf, int count); int Qwrite(QFile *file, const void *buf, int count); -int Qprintf(QFile *file, const char *fmt, ...) __attribute__((format(printf,2,3))); +int Qprintf(QFile *file, const char *fmt, ...) __attribute__((format(PRINTF,2,3))); int Qputs(QFile *file, const char *buf); char *Qgets(QFile *file, char *buf, int count); int Qgetc(QFile *file); @@ -60,6 +60,6 @@ int Qflush(QFile *file); int Qeof(QFile *file); const char *Qgetline(QFile *file); -//@} +///@} -#endif /*__quakeio_h*/ +#endif//__QF_quakeio_h diff --git a/include/QF/render.h b/include/QF/render.h index 3ff4af2ac..cd06fb44f 100644 --- a/include/QF/render.h +++ b/include/QF/render.h @@ -25,17 +25,75 @@ */ -#ifndef __render_h -#define __render_h +#ifndef __QF_render_h +#define __QF_render_h #include "QF/mathlib.h" #include "QF/model.h" -#include "QF/qdefs.h" // FIXME +#include "QF/qdefs.h" // FIXME for MAX_STYLESTRING #include "QF/vid.h" +#include "QF/simd/types.h" +#include "QF/ui/vrect.h" + +typedef enum { + part_tex_dot, + part_tex_spark, + part_tex_smoke, +} ptextype_t; + +typedef struct particle_s particle_t; + +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +struct particle_s { + vec4f_t pos; + vec4f_t vel; + + union { + struct { + int icolor; + int pad[2]; + float alpha; + }; + vec4f_t color; + }; + + ptextype_t tex; + float ramp; + float scale; + float live; +}; + +typedef struct partparm_s { + vec4f_t drag; // drag[3] is grav scale + float ramp; + float ramp_max; + float scale_rate; + float alpha_rate; +} partparm_t; + +typedef struct psystem_s { + vec4f_t gravity; + uint32_t maxparticles; + uint32_t numparticles; + particle_t *particles; + partparm_t *partparams; + const int **partramps; + + int points_only; +} psystem_t; extern struct vid_render_funcs_s *r_funcs; extern struct vid_render_data_s *r_data; +typedef struct subpic_s { + const struct subpic_s *const next; + const struct scrap_s *const scrap; ///< renderer specific type + const struct vrect_s *const rect; + const int width; ///< requested width + const int height; ///< requested height + const float size; ///< size factor for tex coords (mult) +} subpic_t; + // dynamic lights =========================================================== typedef struct dlight_s @@ -62,102 +120,78 @@ typedef struct //=============== -typedef struct entity_s { - struct entity_s *next; - struct entity_s *unext; //FIXME this shouldn't be here. for qw demos +typedef union refframe_s { + mat4f_t mat; + struct { + vec4f_t right; + vec4f_t forward; + vec4f_t up; + vec4f_t position; + }; +} refframe_t; - vec3_t origin; - vec3_t old_origin; - vec3_t angles; - vec_t transform[4 * 4]; - vec_t full_transform[4 * 4]; - struct model_s *model; // NULL = no model - int frame; - int skinnum; // for Alias models - struct skin_s *skin; - - float syncbase; // for client-side animations - - struct efrag_s *efrag; // linked list of efrags - int visframe; // last frame this entity was - // found in an active leaf - float colormod[4]; // color tint and alpha for model - float scale; // size scaler of the model - - int fullbright; - float min_light; - - struct trail_s *trail; - - // FIXME: could turn these into a union - int trivial_accept; - struct mnode_s *topnode; // for bmodels, first world node that - // splits bmodel, or NULL if not split - - // Animation interpolation - float frame_start_time; - float frame_interval; - int pose1; - int pose2; - struct model_s *pose_model; // no lerp if not the same as model -} entity_t; +/** Generic frame buffer object. + * + * For attaching scene cameras to render targets. + */ +typedef struct framebuffer_s { + unsigned width; + unsigned height; + void *buffer; ///< renderer-specific frame buffer data +} framebuffer_t; // !!! if this is changed, it must be changed in asm_draw.h too !!! -typedef struct -{ - vrect_t vrect; // subwindow in video for refresh - // FIXME: not need vrect next field here? - vrect_t aliasvrect; // scaled Alias version - int vrectright, vrectbottom; // right & bottom screen coords - int aliasvrectright, aliasvrectbottom; // scaled Alias versions - float vrectrightedge; // rightmost right edge we care about, - // for use in edge list - float fvrectx, fvrecty; // for floating-point compares +typedef struct { float fvrectx_adj, fvrecty_adj; // left and top edges, for clamping - int vrect_x_adj_shift20; // (vrect.x + 0.5 - epsilon) << 20 - int vrectright_adj_shift20; // (vrectright + 0.5 - epsilon) << 20 float fvrectright_adj, fvrectbottom_adj; + int aliasvrectleft, aliasvrecttop; // scaled Alias versions + int aliasvrectright, aliasvrectbottom; // scaled Alias versions + int vrectright, vrectbottom; // right & bottom screen coords + int vrectx_adj_shift20; // (vrect.x + 0.5 - epsilon) << 20 + int vrectright_adj_shift20; // (vrectright + 0.5 - epsilon) << 20 + float fvrectx, fvrecty; // for floating-point compares // right and bottom edges, for clamping float fvrectright; // rightmost edge, for Alias clamping float fvrectbottom; // bottommost edge, for Alias clamping - float horizontalFieldOfView; // at Z = 1.0, this many X is visible - // 2.0 = 90 degrees - float xOrigin; // should probably always be 0.5 - float yOrigin; // between be around 0.3 to 0.5 + // end of asm refs - vec3_t vieworg; - vec3_t viewangles; + //FIXME move all of below elsewhere + vrect_t vrect; // subwindow in video for refresh - float fov_x, fov_y; + refframe_t frame; + plane_t frustum[4]; + mat4f_t camera; + mat4f_t camera_inverse; int ambientlight; + int drawflat; + + struct ecs_registry_s *registry; + struct model_s *worldmodel; } refdef_t; -// color shifts ============================================================= - -typedef struct { - int destcolor[3]; - int percent; // 0-255 - double time; - int initialpct; -} cshift_t; - -#define CSHIFT_CONTENTS 0 -#define CSHIFT_DAMAGE 1 -#define CSHIFT_BONUS 2 -#define CSHIFT_POWERUP 3 -#define NUM_CSHIFTS 4 - // REFRESH ==================================================================== extern struct texture_s *r_notexture_mip; -extern entity_t r_worldentity; - void R_Init (void); -void R_LoadModule (void (*load_gl)(void), - void (*set_palette) (const byte *palette)); +struct vid_internal_s; +void R_LoadModule (struct vid_internal_s *vid_internal); struct progs_s; void R_Progs_Init (struct progs_s *pr); -#endif // __render_h +dlight_t *R_AllocDlight (int key); +void R_DecayLights (double frametime); +void Fog_Update (float density, float red, float green, float blue, + float time); +struct plitem_s; +void Fog_ParseWorldspawn (struct plitem_s *worldspawn); + +void Fog_GetColor (quat_t fogcolor); +float Fog_GetDensity (void) __attribute__((pure)); +void Fog_SetupFrame (void); +void Fog_StartAdditive (void); +void Fog_StopAdditive (void); +void Fog_Init (void); + +#endif//__QF_render_h diff --git a/include/QF/ringbuffer.h b/include/QF/ringbuffer.h new file mode 100644 index 000000000..d6ffa4a9b --- /dev/null +++ b/include/QF/ringbuffer.h @@ -0,0 +1,238 @@ +/* + ringbuffer.h + + ring buffer + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_ringbuffer_h +#define __QF_ringbuffer_h + +/** Type declaration for a type-safe ring buffer. + * + * While not in itself thread-safe, the buffer is designed (and tested) to be + * used in a threaded environment using suitable locking mechanisms. + * + * \param type The type of data element stored in the ring buffer. + * \param size The number of objects in the ring buffer. Note that the + * actual capacity of the buffer is `size - 1` due to the + * way ring buffers work. + * + * \note On its own, this is not thread-safe. Suitable locking is required. + */ +#define RING_BUFFER(type, size) \ + struct { \ + type buffer[size]; \ + unsigned head; \ + unsigned tail; \ + } + +/** Type declaration for a type-safe ring buffer with atomic head and tail. + * + * \param type The type of data element stored in the ring buffer. + * \param size The number of objects in the ring buffer. Note that the + * actual capacity of the buffer is `size - 1` due to the + * way ring buffers work. + * + * \note This is suitable only for single-reader/single-writer unless + * additional locking is provided for multi-user side. This means `head` must + * have an associated lock for multiple writers, and `tail` must have an + * associated lock for multiple readers. For multi-reader + multi-writer, it + * may be better to use RING_BUFFER with suitable locking. + */ +#define RING_BUFFER_ATOMIC(type, size) \ + struct { \ + type buffer[size]; \ + _Atomic unsigned head; \ + _Atomic unsigned tail; \ + } + +#define RB_buffer_size(ring_buffer) \ + ({ __auto_type rb_s = (ring_buffer); \ + sizeof (rb_s->buffer) / sizeof (rb_s->buffer[0]); \ + }) + +/** Return the amount of space available for writing in the ring buffer. + * + * Use this to know how much data can be written (RB_WRITE_DATA()) or acquired + * (RB_ACQUIRE()), or just to see if any space is available. + * + * \note Does NOT affect buffer state. + * + * \param ring_buffer The ring buffer to check for available space. + */ +#define RB_SPACE_AVAILABLE(ring_buffer) \ + ({ __auto_type rb = &(ring_buffer); \ + (rb->tail + RB_buffer_size(rb) - rb->head - 1) % RB_buffer_size(rb);\ + }) + +/** Return the number of objects available in the ring buffer. + * + * Use this to know how much data can be read (RB_READ_DATA()) or discarded + * (RB_RELEASE()), or just to see if any data is available. + * + * \note Does NOT affect buffer state. + * + * \param ring_buffer The ring buffer to check for available data. + */ +#define RB_DATA_AVAILABLE(ring_buffer) \ + ({ __auto_type rb = &(ring_buffer); \ + (rb->head + RB_buffer_size (rb) - rb->tail) % RB_buffer_size (rb); \ + }) + +/** Write \a count objects from \a data to the buffer, wrapping if necessary. + * + * \note Affects buffer state (advances the head). + * + * \note Does NOT check that the space is available. It is the caller's + * responsitiblity to do so using RB_SPACE_AVAILABLE(). + * + * \param ring_buffer The ring buffer to which the data will be written. + * \param data Pointer to the data to be copied into the ring buffer. + * \param count unsigned int. The number of objects to copy from + * \a data + */ +#define RB_WRITE_DATA(ring_buffer, data, count) \ + ({ __auto_type rb = &(ring_buffer); \ + const typeof (rb->buffer[0]) *d = (data); \ + unsigned c = (count); \ + unsigned h = rb->head; \ + unsigned new_head = (h + c) % RB_buffer_size (rb); \ + if (c > RB_buffer_size (rb) - h) { \ + memcpy (rb->buffer + h, d, \ + (RB_buffer_size (rb) - h) * sizeof (rb->buffer[0])); \ + c -= RB_buffer_size (rb) - h; \ + d += RB_buffer_size (rb) - h; \ + h = 0; \ + } \ + memcpy (rb->buffer + h, d, c * sizeof (rb->buffer[0])); \ + rb->head = new_head; \ + }) + +/** Acquire \a count objects from the buffer, wrapping if necessary. + * + * \note Affects buffer state (advances the head). + * + * \note Does NOT check that the space is available. It is the caller's + * responsitiblity to do so using RB_SPACE_AVAILABLE(). + * + * \param ring_buffer The ring buffer to which the data will be written. + * \param count unsigned int. The number of objects to copy from + * \a data. + * \return Address of the first object acquired. + */ +#define RB_ACQUIRE(ring_buffer, count) \ + ({ __auto_type rb = &(ring_buffer); \ + unsigned c = (count); \ + unsigned h = rb->head; \ + unsigned new_head = (h + c) % RB_buffer_size (rb); \ + __auto_type head_addr = &rb->buffer[h]; \ + rb->head = new_head; \ + head_addr; \ + }) + +/** Read \a count objects from the buffer to \a data, wrapping if necessary. + * + * \note Affects buffer state (advances the tail). + * + * \note Does NOT check that the data is available. It is the caller's + * responsitiblity to do so using RB_DATA_AVAILABLE(). + * + * \param ring_buffer The ring buffer from which the data will be read. + * \param data Pointer to the location into which data will be copied + * from the ring buffer. + * \param count unsigned int. The number of objects to copy from + * the buffer into \a data + */ +#define RB_READ_DATA(ring_buffer, data, count) \ + ({ __auto_type rb = &(ring_buffer); \ + typeof (&rb->buffer[0]) d = (data); \ + unsigned c = (count); \ + unsigned oc = c; \ + unsigned t = rb->tail; \ + unsigned new_tail = (t + oc) % RB_buffer_size (rb); \ + if (c > RB_buffer_size (rb) - t) { \ + memcpy (d, rb->buffer + t, \ + (RB_buffer_size (rb) - t) * sizeof (rb->buffer[0])); \ + c -= RB_buffer_size (rb) - t; \ + d += RB_buffer_size (rb) - t; \ + t = 0; \ + } \ + memcpy (d, rb->buffer + t, c * sizeof (rb->buffer[0])); \ + rb->tail = new_tail; \ + }) + +/** Discard \a count objects from the ring buffer. + * + * \note Affects buffer state (advances the tail). + * + * \note Does NOT check that the data is available. It is the caller's + * responsitiblity to do so using RB_DATA_AVAILABLE(). + * + * \param ring_buffer The ring buffer from which the objects will be + * discarded. + * \param count The number of objects to discard. + */ +#define RB_RELEASE(ring_buffer, count) \ + ({ __auto_type rb = &(ring_buffer); \ + unsigned c = (count); \ + unsigned t = rb->tail; \ + rb->tail = (t + c) % RB_buffer_size (rb); \ + }) + +/** Access a single item from the buffer without affecting buffer state. + * + * \note Does NOT affect buffer state. + * + * \note Does NOT check that the data is available. It is the caller's + * responsitiblity to do so using RB_DATA_AVAILABLE(). + * + * \param ring_buffer The ring buffer from which to access the object. + * \param ahead The tail-relative index of the object to access from + * the buffer. Valid range is 0 to + * `RB_DATA_AVAILABLE() - 1` + * \return Address of the accessed element + */ +#define RB_PEEK_DATA(ring_buffer, ahead) \ + ({ __auto_type rb = &(ring_buffer); \ + &rb->buffer[(rb->tail + ahead) % RB_buffer_size (rb)]; \ + }) + +/** WRite a single item to the buffer without affecting buffer state. + * + * \note Does NOT affect buffer state. + * + * \note Does NOT check that the data is available. It is the caller's + * responsitiblity to do so using RB_DATA_AVAILABLE(). + * + * \param ring_buffer The ring buffer from which to read the object. + * \param ahead The tail-relative index of the buffer object to write. + * Valid range is 0 to `RB_DATA_AVAILABLE() - 1` + * \param data The data to write to the buffer. + */ +#define RB_POKE_DATA(ring_buffer, ahead, data) \ + ({ __auto_type rb = &(ring_buffer); \ + rb->buffer[(rb->tail + ahead) % RB_buffer_size (rb)] = (data); \ + }) + +#endif//__QF_ringbuffer_h diff --git a/include/QF/ruamoko.h b/include/QF/ruamoko.h index a2dcd6366..7af3ff57f 100644 --- a/include/QF/ruamoko.h +++ b/include/QF/ruamoko.h @@ -31,13 +31,21 @@ #ifndef __QF_ruamoko_h #define __QF_ruamoko_h -#include "QF/pr_comp.h" +#include "QF/progs/pr_obj.h" struct progs_s; struct cbuf_s; void RUA_Init (struct progs_s *pr, int secure); void RUA_Cbuf_SetCbuf (struct progs_s *pr, struct cbuf_s *cbuf); -func_t RUA_Obj_msg_lookup (struct progs_s *pr, pointer_t _self, pointer_t __cmd); +pr_func_t RUA_Obj_msg_lookup (struct progs_s *pr, pr_ptr_t _self, + pr_ptr_t __cmd); + +void RUA_Game_Init (struct progs_s *pr, int secure); + +// self is expected in param 0 +int RUA_obj_increment_retaincount (struct progs_s *pr, void *data); +// self is expected in param 0 +int RUA_obj_decrement_retaincount (struct progs_s *pr, void *data); #endif//__QF_ruamoko_h diff --git a/include/QF/scene/camera.h b/include/QF/scene/camera.h new file mode 100644 index 000000000..8593896ea --- /dev/null +++ b/include/QF/scene/camera.h @@ -0,0 +1,59 @@ +/* + camera.h + + Scene camera data + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/02/18 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_scene_camera_h +#define __QF_scene_camera_h + +#include "QF/qtypes.h" +#include "QF/simd/mat4f.h" +#include "QF/scene/transform.h" + +/** \defgroup scene_camera Camera data + \ingroup scene +*/ +///@{ + +typedef struct camera_s { + struct scene_s *scene; ///< owning scene + struct framebuffer_s *framebuffer; + int32_t id; ///< id in scene + transform_t transform; + float field_of_view; + float aspect; + float near_clip; + float far_clip; +} camera_t; + +void Camera_GetViewMatrix (const camera_t *camera, mat4f_t mat); +void Camera_GetProjectionMatrix (const camera_t *camera, mat4f_t mat); + +///@} + +#endif//__QF_scene_camera_h diff --git a/include/QF/scene/entity.h b/include/QF/scene/entity.h new file mode 100644 index 000000000..e2bc00ffa --- /dev/null +++ b/include/QF/scene/entity.h @@ -0,0 +1,159 @@ +/* + entity.h + + Entity management + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/02/26 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_scene_entity_h +#define __QF_scene_entity_h + +#include "QF/darray.h" +#include "QF/qtypes.h" +#include "QF/set.h" +#include "QF/simd/vec4f.h" +#include "QF/simd/mat4f.h" + +/** \defgroup scene_entity Entity management + \ingroup scene +*/ +///@{ + +#include "QF/scene/scene.h" +#include "QF/scene/transform.h" + +typedef struct entity_s { + ecs_registry_t *reg; + uint32_t id; +} entity_t; + +#define nullentity ((entity_t) { .id = nullent }) + +typedef struct animation_s { + int frame; + float syncbase; // randomize time base for local animations + float frame_start_time; + float frame_interval; + int pose1; + int pose2; + float blend; + int nolerp; // don't lerp this frame (pose data invalid) +} animation_t; + +typedef struct visibility_s { + struct efrag_s *efrag; // linked list of efrags + int topnode_id; // bmodels, first world node that + // splits bmodel, or NULL if not split + // applies to other models, too + // found in an active leaf + int trivial_accept; // view clipping (frustum and depth) +} visibility_t; + +typedef struct renderer_s { + struct model_s *model; // NULL = no model + struct skin_s *skin; + float colormod[4]; // color tint and alpha for model + int skinnum; // for Alias models + int fullbright; + float min_light; + int render_id; + mat4f_t full_transform; +} renderer_t; + +typedef struct entityset_s DARRAY_TYPE (entity_t) entityset_t; + +typedef struct efrag_s { + struct mleaf_s *leaf; + struct efrag_s *leafnext; + entity_t entity; + uint32_t queue_num; + struct efrag_s *entnext; +} efrag_t; + +typedef struct entqueue_s { + set_t *queued_ents; + entityset_t *ent_queues; + int num_queues; +} entqueue_t; + +typedef struct colormap_s { + byte top; + byte bottom; +} colormap_t; + +#define ENTINLINE GNU89INLINE inline + +entqueue_t *EntQueue_New (int num_queues); +void EntQueue_Delete (entqueue_t *queue); +ENTINLINE void EntQueue_AddEntity (entqueue_t *queue, entity_t ent, + int queue_num); +void EntQueue_Clear (entqueue_t *queue); +ENTINLINE int Entity_Valid (entity_t ent); +ENTINLINE transform_t Entity_Transform (entity_t ent); + +#undef ENTINLINE +#ifndef IMPLEMENT_ENTITY_Funcs +#define ENTINLINE GNU89INLINE inline +#else +#define ENTINLINE VISIBLE +#endif + +ENTINLINE +void +EntQueue_AddEntity (entqueue_t *queue, entity_t ent, int queue_num) +{ + int id = Ent_Index (ent.id); + if (!set_is_member (queue->queued_ents, id)) { + // entity ids are negative (ones-complement) + set_add (queue->queued_ents, id); + DARRAY_APPEND (&queue->ent_queues[queue_num], ent); + } +} + +ENTINLINE +int +Entity_Valid (entity_t ent) +{ + return ent.reg && ECS_EntValid (ent.id, ent.reg); +} + +ENTINLINE +transform_t +Entity_Transform (entity_t ent) +{ + // The transform hierarchy reference is a component on the entity thus + // the entity id is the transform id. + return (transform_t) { .reg = ent.reg, .id = ent.id, .comp = scene_href }; +} + +struct mod_brush_s; +void R_AddEfrags (struct mod_brush_s *, entity_t ent); +void R_ShutdownEfrags (void); +void R_ClearEfragChain (efrag_t *ef); + +///@} + +#endif//__QF_scene_entity_h diff --git a/include/QF/scene/light.h b/include/QF/scene/light.h new file mode 100644 index 000000000..542431bf3 --- /dev/null +++ b/include/QF/scene/light.h @@ -0,0 +1,80 @@ +/* + light.h + + Entity management + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/02/26 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_scene_light_h +#define __QF_scene_light_h + +#include "QF/darray.h" +#include "QF/qtypes.h" +#include "QF/simd/types.h" + +struct mod_brush_s; + +#define NumStyles 64 + +#define LM_LINEAR 0 // light - dist (or radius + dist if -ve) +#define LM_INVERSE 1 // distFactor1 * light / dist +#define LM_INVERSE2 2 // distFactor2 * light / (dist * dist) +#define LM_INFINITE 3 // light +#define LM_AMBIENT 4 // light +#define LM_INVERSE3 5 // distFactor2 * light / (dist + distFactor2)**2 + +typedef struct light_s { + vec4f_t color; + vec4f_t position; + vec4f_t direction; + vec4f_t attenuation; +} light_t; + +typedef struct lightset_s DARRAY_TYPE (light_t) lightset_t; +typedef struct lightleafset_s DARRAY_TYPE (int) lightintset_t; +typedef struct lightvisset_s DARRAY_TYPE (byte) lightvisset_t; + +typedef struct lightingdata_s { + lightset_t lights; + lightintset_t lightstyles; + lightintset_t lightleafs; + lightvisset_t lightvis; + struct set_s *sun_pvs; + // A fat PVS of leafs visible from visible leafs so hidden lights can + // illuminate the leafs visible to the player + struct set_s *pvs; + struct mleaf_s *leaf; // the last leaf used to generate the pvs + struct scene_s *scene; +} lightingdata_t; + +lightingdata_t *Light_CreateLightingData (struct scene_s *scene); +void Light_DestroyLightingData (lightingdata_t *ldata); +void Light_ClearLights (lightingdata_t *ldata); +void Light_AddLight (lightingdata_t *ldata, const light_t *light, int style); +void Light_EnableSun (lightingdata_t *ldata); +void Light_FindVisibleLights (lightingdata_t *ldata); + +#endif//__QF_scene_light_h diff --git a/include/QF/scene/scene.h b/include/QF/scene/scene.h new file mode 100644 index 000000000..3a1d281e0 --- /dev/null +++ b/include/QF/scene/scene.h @@ -0,0 +1,81 @@ +/* + scene.h + + Entity management + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/02/26 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_scene_scene_h +#define __QF_scene_scene_h + +#include "QF/darray.h" + +#include "QF/scene/types.h" + +/** \defgroup scene Scene management + \ingroup utils +*/ +///@{ + +enum scene_components { + scene_href, //hierarchical transform + scene_animation, + scene_visibility, + scene_renderer, + scene_active, + scene_old_origin, //XXX FIXME XXX should not be here + scene_colormap, + + //FIXME these should probably be private to the sw renderer (and in a + //group, which needs to be implemented), but need to sort out a good + //scheme for semi-dynamic components + scene_sw_matrix, // world transform matrix + scene_sw_frame, // animation frame + scene_sw_brush, // brush model data pointer + + scene_comp_count +}; + +typedef struct scene_s { + struct ecs_registry_s *reg; + + struct model_s *worldmodel; + int num_models; + struct model_s **models; + struct mleaf_s *viewleaf; + struct lightingdata_s *lights; +} scene_t; + +scene_t *Scene_NewScene (void); +void Scene_DeleteScene (scene_t *scene); +struct entity_s Scene_CreateEntity (scene_t *scene); +void Scene_DestroyEntity (scene_t *scene, struct entity_s entity); +void Scene_FreeAllEntities (scene_t *scene); + + +///@} + +#endif//__QF_scene_scene_h diff --git a/include/QF/scene/transform.h b/include/QF/scene/transform.h new file mode 100644 index 000000000..46468e1d3 --- /dev/null +++ b/include/QF/scene/transform.h @@ -0,0 +1,364 @@ +/* + transform.h + + Transform management + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/02/26 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_scene_transform_h +#define __QF_scene_transform_h + +#include "QF/darray.h" +#include "QF/ecs.h" +#include "QF/qtypes.h" +#include "QF/simd/vec4f.h" +#include "QF/simd/mat4f.h" + +/** \defgroup transform Transform management + \ingroup utils +*/ +///@{ + +enum { + transform_type_name, + transform_type_tag, + transform_type_modified, + transform_type_localMatrix, + transform_type_localInverse, + transform_type_worldMatrix, + transform_type_worldInverse, + transform_type_localRotation, + transform_type_localScale, + transform_type_worldRotation, + + transform_type_count +}; + +typedef struct transform_s { + ecs_registry_t *reg; + uint32_t id; + uint32_t comp; +} transform_t; + +#define nulltransform ((transform_t) {}) + +#define XFORMINLINE GNU89INLINE inline __attribute__((pure)) + +XFORMINLINE int Transform_Valid (transform_t transform); + +transform_t Transform_New (ecs_registry_t *reg, transform_t parent); +/* Deletes all child transforms, and transform names */ +void Transform_Delete (transform_t transform); +transform_t Transform_NewNamed (ecs_registry_t *reg, transform_t parent, + const char *name); +XFORMINLINE hierref_t *Transform_GetRef (transform_t transform); +XFORMINLINE uint32_t Transform_ChildCount (transform_t transform); +XFORMINLINE transform_t Transform_GetChild (transform_t transform, + uint32_t childIndex); +void Transform_SetParent (transform_t transform, transform_t parent); +XFORMINLINE transform_t Transform_GetParent (transform_t transform); +void Transform_SetName (transform_t transform, const char *name); +XFORMINLINE const char *Transform_GetName (transform_t transform); +void Transform_SetTag (transform_t transform, uint32_t tag); +XFORMINLINE uint32_t Transform_GetTag (transform_t transform); +GNU89INLINE inline void Transform_GetLocalMatrix (transform_t transform, + mat4f_t mat); +GNU89INLINE inline void Transform_GetLocalInverse (transform_t transform, + mat4f_t mat); +GNU89INLINE inline void Transform_GetWorldMatrix (transform_t transform, + mat4f_t mat); +// XXX the pointer may be invalidated by hierarchy updates +XFORMINLINE const vec4f_t *Transform_GetWorldMatrixPtr (transform_t transform); +GNU89INLINE inline void Transform_GetWorldInverse (transform_t transform, + mat4f_t mat); +XFORMINLINE vec4f_t Transform_GetLocalPosition (transform_t transform); +void Transform_SetLocalPosition (transform_t transform, vec4f_t position); +XFORMINLINE vec4f_t Transform_GetLocalRotation (transform_t transform); +void Transform_SetLocalRotation (transform_t transform, vec4f_t rotation); +XFORMINLINE vec4f_t Transform_GetLocalScale (transform_t transform); +void Transform_SetLocalScale (transform_t transform, vec4f_t scale); +XFORMINLINE vec4f_t Transform_GetWorldPosition (transform_t transform); +void Transform_SetWorldPosition (transform_t transform, vec4f_t position); +XFORMINLINE vec4f_t Transform_GetWorldRotation (transform_t transform); +void Transform_SetWorldRotation (transform_t transform, vec4f_t rotation); +XFORMINLINE vec4f_t Transform_GetWorldScale (transform_t transform); +void Transform_SetLocalTransform (transform_t transform, vec4f_t scale, + vec4f_t rotation, vec4f_t position); +// NOTE: these use X: forward, -Y: right, Z:up +// aslo, not guaranteed to be normalized or even orthogonal +XFORMINLINE vec4f_t Transform_Forward (transform_t transform); +XFORMINLINE vec4f_t Transform_Right (transform_t transform); +XFORMINLINE vec4f_t Transform_Up (transform_t transform); +// no SetWorldScale because after rotations, non uniform scale becomes shear + +#undef XFORMINLINE +#ifndef IMPLEMENT_TRANSFORM_Funcs +#define XFORMINLINE GNU89INLINE inline +#else +#define XFORMINLINE VISIBLE +#endif + +XFORMINLINE +int +Transform_Valid (transform_t transform) +{ + return transform.reg && ECS_EntValid (transform.id, transform.reg); +} + +XFORMINLINE +hierref_t * +Transform_GetRef (transform_t transform) +{ + return Ent_GetComponent (transform.id, transform.comp, transform.reg); +} + +XFORMINLINE +uint32_t +Transform_ChildCount (transform_t transform) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + return h->childCount[ref->index]; +} + +XFORMINLINE +transform_t +Transform_GetChild (transform_t transform, uint32_t childIndex) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + if (childIndex >= h->childCount[ref->index]) { + return nulltransform; + } + return (transform_t) { + .reg = transform.reg, + .id = h->ent[h->childIndex[ref->index] + childIndex], + .comp = transform.comp, + }; +} + +XFORMINLINE +transform_t +Transform_GetParent (transform_t transform) +{ + __auto_type ref = Transform_GetRef (transform); + if (ref->index == 0) { + return nulltransform; + } + hierarchy_t *h = ref->hierarchy; + return (transform_t) { + .reg = transform.reg, + .id = h->ent[h->parentIndex[ref->index]], + .comp = transform.comp, + }; +} + +XFORMINLINE +const char * +Transform_GetName (transform_t transform) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + char **name = h->components[transform_type_name]; + return name[ref->index]; +} + +XFORMINLINE +uint32_t +Transform_GetTag (transform_t transform) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + uint32_t *tag = h->components[transform_type_tag]; + return tag[ref->index]; +} + +XFORMINLINE +void +Transform_GetLocalMatrix (transform_t transform, mat4f_t mat) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + mat4f_t *localMatrix = h->components[transform_type_localMatrix]; + vec4f_t *src = localMatrix[ref->index]; + mat[0] = src[0]; + mat[1] = src[1]; + mat[2] = src[2]; + mat[3] = src[3]; +} + +XFORMINLINE +void +Transform_GetLocalInverse (transform_t transform, mat4f_t mat) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + mat4f_t *localInverse = h->components[transform_type_localInverse]; + vec4f_t *src = localInverse[ref->index]; + mat[0] = src[0]; + mat[1] = src[1]; + mat[2] = src[2]; + mat[3] = src[3]; +} + +XFORMINLINE +void +Transform_GetWorldMatrix (transform_t transform, mat4f_t mat) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + mat4f_t *worldMatrix = h->components[transform_type_worldMatrix]; + vec4f_t *src = worldMatrix[ref->index]; + mat[0] = src[0]; + mat[1] = src[1]; + mat[2] = src[2]; + mat[3] = src[3]; +} + +XFORMINLINE +const vec4f_t * +Transform_GetWorldMatrixPtr (transform_t transform) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + mat4f_t *worldMatrix = h->components[transform_type_worldMatrix]; + return worldMatrix[ref->index]; +} + +XFORMINLINE +void +Transform_GetWorldInverse (transform_t transform, mat4f_t mat) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + mat4f_t *worldInverse = h->components[transform_type_worldInverse]; + vec4f_t *src = worldInverse[ref->index]; + mat[0] = src[0]; + mat[1] = src[1]; + mat[2] = src[2]; + mat[3] = src[3]; +} + +XFORMINLINE +vec4f_t +Transform_GetLocalPosition (transform_t transform) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + mat4f_t *localMatrix = h->components[transform_type_localMatrix]; + return localMatrix[ref->index][3]; +} + +XFORMINLINE +vec4f_t +Transform_GetLocalRotation (transform_t transform) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + vec4f_t *localRotation = h->components[transform_type_localRotation]; + return localRotation[ref->index]; +} + +XFORMINLINE +vec4f_t +Transform_GetLocalScale (transform_t transform) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + vec4f_t *localScale = h->components[transform_type_localScale]; + return localScale[ref->index]; +} + +XFORMINLINE +vec4f_t +Transform_GetWorldPosition (transform_t transform) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + mat4f_t *worldMatrix = h->components[transform_type_worldMatrix]; + return worldMatrix[ref->index][3]; +} + +XFORMINLINE +vec4f_t +Transform_GetWorldRotation (transform_t transform) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + vec4f_t *worldRotation = h->components[transform_type_worldRotation]; + return worldRotation[ref->index]; +} + +XFORMINLINE +vec4f_t +Transform_GetWorldScale (transform_t transform) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + mat4f_t *worldMatrix = h->components[transform_type_worldMatrix]; + vec4f_t *m = worldMatrix[ref->index]; + vec4f_t s = { + dotf (m[0], m[0])[0], + dotf (m[1], m[1])[0], + dotf (m[2], m[2])[0], + 0, + }; + return vsqrt4f (s); +} + +XFORMINLINE +vec4f_t +Transform_Forward (transform_t transform) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + mat4f_t *worldMatrix = h->components[transform_type_worldMatrix]; + return worldMatrix[ref->index][0]; +} + +XFORMINLINE +vec4f_t +Transform_Right (transform_t transform) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + mat4f_t *worldMatrix = h->components[transform_type_worldMatrix]; + return -worldMatrix[ref->index][1]; +} + +XFORMINLINE +vec4f_t +Transform_Up (transform_t transform) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + mat4f_t *worldMatrix = h->components[transform_type_worldMatrix]; + return worldMatrix[ref->index][2]; +} + +///@} + +#endif//__QF_scene_transform_h diff --git a/include/QF/scene/types.h b/include/QF/scene/types.h new file mode 100644 index 000000000..fa371a65a --- /dev/null +++ b/include/QF/scene/types.h @@ -0,0 +1,53 @@ +/* + types.h + + Entity management + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/02/26 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_scene_types_h +#define __QF_scene_types_h + +#include "QF/darray.h" +#include "QF/qtypes.h" +#include "QF/simd/vec4f.h" +#include "QF/simd/mat4f.h" + +/** \defgroup scene_types Scene type definitions + \ingroup scene +*/ +///@{ + +typedef struct mat4fset_s DARRAY_TYPE (mat4f_t) mat4fset_t; +typedef struct vec4fset_s DARRAY_TYPE (vec4f_t) vec4fset_t; +typedef struct uint32set_s DARRAY_TYPE (uint32_t) uint32set_t; +typedef struct byteset_s DARRAY_TYPE (byte) byteset_t; +typedef struct stringset_s DARRAY_TYPE (char *) stringset_t; +typedef struct xformset_s DARRAY_TYPE (struct transform_s *) xformset_t; + +///@} + +#endif//__QF_scene_types_h diff --git a/include/QF/screen.h b/include/QF/screen.h index 9586e20a1..7608c3571 100644 --- a/include/QF/screen.h +++ b/include/QF/screen.h @@ -26,45 +26,36 @@ */ // screen.h -#ifndef __screen_h -#define __screen_h +#ifndef __QF_screen_h +#define __QF_screen_h -#include "QF/qtypes.h" +struct scene_s; +struct transform_s; +struct tex_s; -void SCR_Init_Cvars (void); void SCR_Init (void); +void SCR_Shutdown (void); typedef void (*SCR_Func)(void); +// scr_funcs is a null terminated array +void SCR_UpdateScreen (struct transform_s camera, double realtime, + SCR_Func *scr_funcs); +void SCR_UpdateScreen_legacy (struct transform_s camera, double realtime, + SCR_Func *scr_funcs); +void SCR_SetFOV (float fov); +// control whether the 3d viewport is user-controlled or always fullscreen +void SCR_SetFullscreen (bool fullscreen); +void SCR_SetBottomMargin (int lines); -void SCR_SizeUp (void); -void SCR_SizeDown (void); -void SCR_BringDownConsole (void); -void SCR_CalcRefdef (void); +void SCR_NewScene (struct scene_s *scene); -void SCR_BeginLoadingPlaque (void); -void SCR_EndLoadingPlaque (void); +extern int r_timegraph; +extern int r_zgraph; +extern int scr_copytop; +extern bool scr_skipupdate; -struct view_s; +struct view_pos_s; +void R_TimeGraph (struct view_pos_s abs, struct view_pos_s len); +void R_ZGraph (struct view_pos_s abs, struct view_pos_s len); -int MipColor (int r, int g, int b); -int SCR_ModalMessage (const char *text); - -extern float scr_con_current; - -extern int sb_lines; - -extern qboolean scr_disabled_for_loading; -extern qboolean scr_skipupdate; -extern qboolean hudswap; - -extern struct cvar_s *scr_fov; -extern struct cvar_s *scr_viewsize; - -// only the refresh window will be updated unless these variables are flagged - -extern struct qpic_s *scr_ram; -extern struct qpic_s *scr_turtle; - -extern struct cvar_s *hud_fps, *hud_time; - -#endif // __screen_h +#endif//__QF_screen_h diff --git a/include/QF/script.h b/include/QF/script.h index bc11b0e2a..540fb8db2 100644 --- a/include/QF/script.h +++ b/include/QF/script.h @@ -25,7 +25,7 @@ Line oriented script parsing. Multiple scripts being parsed at the same time is supported. */ -//@{ +///@{ #include "QF/qtypes.h" @@ -33,7 +33,7 @@ typedef struct script_s { /// The current (or next when unget is true) token struct dstring_s *token; /// True if the last token has been pushed back. - qboolean unget; + bool unget; /// current position within the script const char *p; /// name of the file being processed. used only for error reporting @@ -41,8 +41,9 @@ typedef struct script_s { /// line number of the file being processed. used only for error reporting /// but updated internally. int line; - /// if set, will be called instead of the internal error handler - void (*error)(struct script_s *script, const char *msg); + /// contains last error message or null if no error + /// if set, no tokens will be parsed. + const char *error; /// if set, multi line quoted tokens will be treated as errors int no_quote_lines; /// if set, characters in this string will always be lexed as single @@ -65,6 +66,7 @@ void Script_Delete (script_t *script); /** Prepare a script_t object for parsing. The caller is responsible for freeing the memory associated with file and data when parsing is complete. + Resets \a script->error \param script The script_t object being parsed \param file Name of the file being parsed. used only for error reporting \param data The script to be parsed @@ -77,7 +79,7 @@ void Script_Start (script_t *script, const char *file, const char *data); \return True if a token is available, false if end of file or end of line (if crossline is false) has been hit */ -qboolean Script_TokenAvailable (script_t *script, qboolean crossline); +bool Script_TokenAvailable (script_t *script, bool crossline); /** Get the next token. Generates an error and exits the program if no token is available and crossline is false. @@ -85,7 +87,7 @@ qboolean Script_TokenAvailable (script_t *script, qboolean crossline); \param crossline True to allow passing \n \return True on success, false on failure (no token available) */ -qboolean Script_GetToken (script_t *script, qboolean crossline); +bool Script_GetToken (script_t *script, bool crossline); /** Unget the current token. Only one level of unget is supported. \param script The script_t object being parsed @@ -95,8 +97,8 @@ void Script_UngetToken (script_t *script); /** Return a pointer to the current token. \param script The script_t object being parsed */ -const char *Script_Token (script_t *script); +const char *Script_Token (script_t *script) __attribute__((pure)); -//@} +///@} #endif//__QF_script_h diff --git a/include/QF/segtext.h b/include/QF/segtext.h index 1c781fcb9..a8b0be896 100644 --- a/include/QF/segtext.h +++ b/include/QF/segtext.h @@ -31,28 +31,111 @@ #ifndef __QF_segtext_h #define __QF_segtext_h -/** \defgroup segtext Segmented text files. +/** \defgroup segtext Segmented text files \ingroup utils + Access named sections of a text file. - Based on The OpenGL Shader Wrangler: http://prideout.net/blog/?p=11 + Based on The OpenGL Shader Wrangler: + https://prideout.net/blog/old/blog/index.html@p=11.html + However, nothing is assumed about any of the segments: they are all nothing + more than chunks of mostly random (lines cannot start with --) text with + identifying tags. Also, the identifying tag represents only the shader_key + of OpenGL Shader Wrangler's Effect.shader_key identifier. */ +///@{ +/** Represent a single text segment + + Segments are separated by lines beginning with "--" (without the quotes). + + The first word ([A-Za-z0-9_.]+) following the double-hyphen is used as an + identifying tag for the segment. Whitespace and any additional characters + are ignored. + + The segment itself starts on the following line. +*/ typedef struct segchunk_s { struct segchunk_s *next; - const char *tag; - const char *text; - int start_line; + const char *tag; ///< identifying tag or null if no tag + const char *text; ///< nul-terminated string holding the segment + int start_line; ///< line number for first line of the segment } segchunk_t; +/** Container for all the segments extracted from the provided text + + The first segment has no tag and may or may not be empty. + Segments are stored sequentially in \a chunk_list are indexed by + identifying tag (if present) in \a tab. + Segments that have no identifying tag are not in \a tab, but can + be accessed by walking \a chunk_list 's \a next field. +*/ typedef struct segtext_s { struct segtext_s *next; segchunk_t *chunk_list; struct hashtab_s *tab; } segtext_t; +/** Parse a nul-terminated string into (optionally) tagged segments + + The segments are separated by lines beginning with a double-hyphen ("--"), + with an optional tag following the double-hyphen on the same line. Valid + tag characters are ASCII alpha-numerics, "." and "_". Whitespace is ignored + and invalid characters are treated as whitespace. Only the first tag on the + line is significant. + + \param src nul-terminated string to be parsed into segments + \return Pointer to container of parsed segments. Can be freed using + Segtext_delete(). +*/ segtext_t *Segtext_new (const char *src); +/** Free a segmented text container created by Segtext_new() + + \param st Pointer to segmented text container to be freed. Must have been + created by Segtext_new(). +*/ void Segtext_delete (segtext_t *st); +/** Find a text segment via its identifying tag + + Like OpenGL Shader Wrangler, the segment tag does not have to be a perfect + match with the specified tag: the segment with the longest string of + sub-tags matching the leading subtags of the specified tag is selected. + Tags are logically broken into sub-tag by the "." character. Thus in a + collection of segments with tags "foo" and "foo.b", "foo.bar" will find the + segment tagged "foo". However, if a segment tagged "foo.bar" is present, it + will be returned. A segment tagged "foo.bar.baz" will not. + + \param st Pointer to segmented text container + \param tag String specifying the tag identifying the segment to be + returned. Valid tag characters are alphanumerics, "_" and "." + (ie, [A-Za-z0-9._]). "." is used as a sub-tag separator for + partial matches. + \return Pointer to segment data block with the best matching tag, + providing access to the segment's tag and starting line number, + as well as the segment contents. Null if no matching tag was + found. +*/ const segchunk_t *Segtext_FindChunk (const segtext_t *st, const char *tag); + +/** Find a text segment via its identifying tag + + Like OpenGL Shader Wrangler, the segment tag does not have to be a perfect + match with the specified tag: the segment with the longest string of + sub-tags matching the leading subtags of the specified tag is selected. + Tags are logically broken into sub-tag by the "." character. Thus in a + collection of segments with tags "foo" and "foo.b", "foo.bar" will find the + segment tagged "foo". However, if a segment tagged "foo.bar" is present, it + will be returned. A segment tagged "foo.bar.baz" will not. + + \param st Pointer to segmented text container + \param tag String specifying the tag identifying the segment to be + returned. Valid tag characters are alphanumerics, "_" and "." + (ie, [A-Za-z0-9._]). "." is used as a sub-tag separator for + partial matches. + \return Contents of the segment with the best matching tag, or null if + no matching tag was found. +*/ const char *Segtext_Find (const segtext_t *st, const char *tag); +///@} + #endif//__QF_segtext_h diff --git a/include/QF/set.h b/include/QF/set.h index a30840b18..0fc200cb5 100644 --- a/include/QF/set.h +++ b/include/QF/set.h @@ -32,14 +32,15 @@ #define __QF_set_h #include "QF/qtypes.h" +#include "QF/darray.h" /** \defgroup set Set handling \ingroup utils */ -//@{ +///@{ //FIXME other archs -#ifdef __x86_64__ +#if defined(__x86_64__) || defined(__aarch64__) typedef uint64_t set_bits_t; #else typedef uint32_t set_bits_t; @@ -50,13 +51,33 @@ typedef uint32_t set_bits_t; - sizeof (int) - sizeof (unsigned))\ / sizeof (set_bits_t)) #define SET_BITS (sizeof (set_bits_t) * 8) +#define SET_DEFMAP_BITS (SET_DEFMAP_SIZE * SET_BITS) //NOTE: x is the element number, so size is x + 1 #define SET_SIZE(x) (((x) + SET_BITS) & ~(SET_BITS - 1)) +#define SET_WORDS_STATIC(x) (SET_SIZE (x) / SET_BITS) #define SET_WORDS(s) ((s)->size / SET_BITS) #define SET_ZERO ((set_bits_t) 0) #define SET_ONE ((set_bits_t) 1) #define SET_TEST_MEMBER(s, x) \ - ((s)->map[(x) / SET_BITS] & (SET_ONE << ((x) % SET_BITS))) + (((const byte *)(s)->map)[(x) / 8] & (SET_ONE << ((x) % 8))) +#define SET_ADD(s, x) \ + (((byte *)(s)->map)[(x) / 8] |= (SET_ONE << ((x) % 8))) +#define SET_REMOVE(s, x) \ + (((byte *)(s)->map)[(x) / 8] &= ~(SET_ONE << ((x) % 8))) +#define SET_LARGE_SET(x) (SET_SIZE (x) > SET_DEFMAP_BITS) +#define SET_SAFE_SIZE(x) (SET_LARGE_SET (x) ? SET_SIZE (x) : SET_DEFMAP_BITS) +#define SET_STATIC_INIT(x, alloc) { \ + .size = SET_SAFE_SIZE (x), \ + .map = alloc (SET_SAFE_SIZE (x) / 8), \ +} +#define SET_STATIC_ARRAY(array) { \ + .size = 8 * __builtin_choose_expr ( \ + sizeof (array[0]) == sizeof (set_bits_t), \ + sizeof (array), \ + (void) 0 \ + ), \ + .map = array, \ +} /** Represent a set using a bitmap. @@ -99,6 +120,8 @@ typedef struct set_iter_s { typedef struct set_pool_s { set_t *set_freelist; set_iter_t *set_iter_freelist; + struct DARRAY_TYPE (set_t *) set_blocks; + struct DARRAY_TYPE (set_iter_t *) set_iter_blocks; } set_pool_t; void set_pool_init (set_pool_t *set_pool); @@ -129,9 +152,11 @@ set_t *set_new_r (set_pool_t *set_pool); \param size The number of elements for which space is to be allocated. \return The newly created, empty set. + \note \a size is the actual amount of elements, not the number of the + highest element (ie, for values 0..n, size is n + 1). */ -set_t *set_new_size (int size); -set_t *set_new_size_r (set_pool_t *set_pool, int size); +set_t *set_new_size (unsigned size); +set_t *set_new_size_r (set_pool_t *set_pool, unsigned size); /** Delete a set that is no longer needed. @@ -140,6 +165,27 @@ set_t *set_new_size_r (set_pool_t *set_pool, int size); void set_delete (set_t *set); void set_delete_r (set_pool_t *set_pool, set_t *set); +/** Pre-expand a set with space for the specified element + + Has no effect if the set is already large enough to hold the specified + element. + + \param set The set to be expanded + \param size The minimum number of elements representable by the set + \note \a size is the actual amount of elements, not the number of the + highest element (ie, for values 0..n, size is n + 1). +*/ +void set_expand (set_t *set, unsigned size); + +/** Shrink the set's backing memory to the minimum required to hold the set. + + This does not affect (nor is affected by) whether the set is an infinite + set. + + \param set The set to trim +*/ +void set_trim (set_t *set); + /** Add an element to a set. It is not an error to add an element that is already a member of the set. @@ -152,6 +198,19 @@ void set_delete_r (set_pool_t *set_pool, set_t *set); */ set_t *set_add (set_t *set, unsigned x); +/** Add a range of elements to a set. + + It is not an error to add elements that are already members of the set. + + \note \a set is modified. + + \param set The set to which the element will be added. + \param start The first element to be added. + \param count The number of elements to be added. + \return The modified set. +*/ +set_t *set_add_range (set_t *set, unsigned start, unsigned count); + /** Remove an element from a set. It is not an error to remove an element that is not a member of the set. @@ -164,6 +223,19 @@ set_t *set_add (set_t *set, unsigned x); */ set_t *set_remove (set_t *set, unsigned x); +/** Remove a range of elements from a set. + + It is not an error to remove elements that not members of the set. + + \note \a set is modified. + + \param set The set from which the element will be removed. + \param start The first element to be removed. + \param count The number of elements to be removed. + \return The modified set. +*/ +set_t *set_remove_range (set_t *set, unsigned start, unsigned count); + /** Compute the inverse of a set. The computation is done as \a set = ~\a set. @@ -251,19 +323,19 @@ set_t *set_empty (set_t *set); */ set_t *set_everything (set_t *set); -/** Test if a set is the set of everything. +/** Test if a set is the empty set. \param set The set to test. \return 1 if \a set is empty (non-inverted). */ -int set_is_empty (const set_t *set); +int set_is_empty (const set_t *set) __attribute__((pure)); /** Test if a set is the set of everything. \param set The set to test. \return 1 if \a set is the set of everything (empty inverted set). */ -int set_is_everything (const set_t *set); +int set_is_everything (const set_t *set) __attribute__((pure)); /** Test if two sets are disjoint. @@ -273,7 +345,7 @@ int set_is_everything (const set_t *set); \note The emtpy set is disjoint with itself. */ -int set_is_disjoint (const set_t *s1, const set_t *s2); +int set_is_disjoint (const set_t *s1, const set_t *s2) __attribute__((pure)); /** Test if two sets intersect. @@ -283,7 +355,7 @@ int set_is_disjoint (const set_t *s1, const set_t *s2); \note Equivalent non-empty sets are treated as intersecting. */ -int set_is_intersecting (const set_t *s1, const set_t *s2); +int set_is_intersecting (const set_t *s1, const set_t *s2) __attribute__((pure)); /** Test if two sets are equivalent. @@ -291,7 +363,7 @@ int set_is_intersecting (const set_t *s1, const set_t *s2); \param s2 The second set to test. \return 1 if \a s2 is equivalent to \a s1, 0 if not. */ -int set_is_equivalent (const set_t *s1, const set_t *s2); +int set_is_equivalent (const set_t *s1, const set_t *s2) __attribute__((pure)); /** Test if a set is a subset of another set. @@ -302,7 +374,7 @@ int set_is_equivalent (const set_t *s1, const set_t *s2); \return 1 if \a sub is a subset of \a set, or if the sets are equivalent. */ -int set_is_subset (const set_t *set, const set_t *sub); +int set_is_subset (const set_t *set, const set_t *sub) __attribute__((pure)); /** Test an element for membership in a set. @@ -310,7 +382,7 @@ int set_is_subset (const set_t *set, const set_t *sub); \param x The element to test. \return 1 if the element is a member of the set, otherwise 0. */ -int set_is_member (const set_t *set, unsigned x); +int set_is_member (const set_t *set, unsigned x) __attribute__((pure)); /** Obtain the number of members (or non-members) of a set. @@ -322,7 +394,7 @@ int set_is_member (const set_t *set, unsigned x); \return The number of (non-)members. Both empty sets and sets of evertything will return 0. */ -unsigned set_size (const set_t *set); +unsigned set_count (const set_t *set) __attribute__((pure)); /** Find the first "member" of the set. @@ -356,10 +428,14 @@ set_iter_t *set_first_r (set_pool_t *set_pool, const set_t *set); set_iter_t *set_next (set_iter_t *set_iter); set_iter_t *set_next_r (set_pool_t *set_pool, set_iter_t *set_iter); +set_iter_t *set_while (set_iter_t *set_iter); +set_iter_t *set_while_r (set_pool_t *set_pool, set_iter_t *set_iter); + +struct dstring_s; /** Return a human-readable string representing the set. - Empty sets will be represented by the string "[empty]". Sets of everything - will be represented by the string "[everything]". Inverted sets will have + Empty sets will be represented by the string "{}". Sets of everything + will be represented by the string "{...}". Inverted sets will have the first implicit member followed by "..." (eg, "256 ..."). \param set The set to be converted to a string. @@ -370,5 +446,23 @@ set_iter_t *set_next_r (set_pool_t *set_pool, set_iter_t *set_iter); */ const char *set_as_string (const set_t *set); -//@} +/** Return a human-readable string representing the set. + + Empty sets will be represented by the string "{}". Sets of everything + will be represented by the string "{...}". Inverted sets will have + the first implicit member followed by "..." (eg, "256 ..."). + + \param str dstring to which the representation will be written + \param set The set to be converted to a string. + \return The string held in str + + \warning The string is NOT cleared, but rather the set representation + is appeneded to the string. This makes it more useful when + constructing strings in a threaded environment. +*/ +const char *set_to_dstring (struct dstring_s *str, const set_t *set); +const char *set_to_dstring_r (set_pool_t *set_pool, struct dstring_s *str, + const set_t *set); + +///@} #endif//__QF_set_h diff --git a/include/QF/simd/mat4f.h b/include/QF/simd/mat4f.h new file mode 100644 index 000000000..ce061edd0 --- /dev/null +++ b/include/QF/simd/mat4f.h @@ -0,0 +1,198 @@ +/* + QF/simd/mat4f.h + + Matrix functions for mat4f_t (ie, float precision) + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_simd_mat4f_h +#define __QF_simd_mat4f_h + +#include "QF/simd/types.h" + +GNU89INLINE inline void maddf (mat4f_t c, const mat4f_t a, const mat4f_t b); +GNU89INLINE inline void msubf (mat4f_t c, const mat4f_t a, const mat4f_t b); +GNU89INLINE inline void mmulf (mat4f_t c, const mat4f_t a, const mat4f_t b); +GNU89INLINE inline vec4f_t mvmulf (const mat4f_t m, vec4f_t v) __attribute__((const)); +GNU89INLINE inline vec4f_t m3vmulf (const mat4f_t m, vec4f_t v) __attribute__((const)); +GNU89INLINE inline void mat4fidentity (mat4f_t m); +GNU89INLINE inline void mat4ftranspose (mat4f_t t, const mat4f_t m); +GNU89INLINE inline void mat4fquat (mat4f_t m, vec4f_t q); + +#ifndef IMPLEMENT_MAT4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +void +maddf (mat4f_t c, const mat4f_t a, const mat4f_t b) +{ + c[0] = a[0] + b[0]; + c[1] = a[1] + b[1]; + c[2] = a[2] + b[2]; + c[3] = a[3] + b[3]; +} + +#ifndef IMPLEMENT_MAT4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +void +msubf (mat4f_t c, const mat4f_t a, const mat4f_t b) +{ + c[0] = a[0] - b[0]; + c[1] = a[1] - b[1]; + c[2] = a[2] - b[2]; + c[3] = a[3] - b[3]; +} + +#ifndef IMPLEMENT_MAT4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +void +mmulf (mat4f_t c, const mat4f_t a, const mat4f_t b) +{ + mat4f_t t; + t[0] = a[0] * b[0][0] + a[1] * b[0][1] + a[2] * b[0][2] + a[3] * b[0][3]; + t[1] = a[0] * b[1][0] + a[1] * b[1][1] + a[2] * b[1][2] + a[3] * b[1][3]; + t[2] = a[0] * b[2][0] + a[1] * b[2][1] + a[2] * b[2][2] + a[3] * b[2][3]; + t[3] = a[0] * b[3][0] + a[1] * b[3][1] + a[2] * b[3][2] + a[3] * b[3][3]; + + c[0] = t[0]; + c[1] = t[1]; + c[2] = t[2]; + c[3] = t[3]; +} + +#ifndef IMPLEMENT_MAT4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4f_t +mvmulf (const mat4f_t m, vec4f_t v) +{ + return m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3] * v[3]; +} + +#ifndef IMPLEMENT_MAT4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4f_t +m3vmulf (const mat4f_t m, vec4f_t v) +{ + vec4f_t w; + w = m[0] * v[0] + m[1] * v[1] + m[2] * v[2]; + w[3] = v[3]; + return w; +} + +#ifndef IMPLEMENT_MAT4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +void +mat4fidentity (mat4f_t m) +{ + m[0] = (vec4f_t) { 1, 0, 0, 0 }; + m[1] = (vec4f_t) { 0, 1, 0, 0 }; + m[2] = (vec4f_t) { 0, 0, 1, 0 }; + m[3] = (vec4f_t) { 0, 0, 0, 1 }; +} + +#ifndef IMPLEMENT_MAT4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +void +mat4ftranspose (mat4f_t t, const mat4f_t m) +{ + vec4f_t a = m[0]; + vec4f_t b = m[1]; + vec4f_t c = m[2]; + vec4f_t d = m[3]; + t[0] = (vec4f_t) { a[0], b[0], c[0], d[0] }; + t[1] = (vec4f_t) { a[1], b[1], c[1], d[1] }; + t[2] = (vec4f_t) { a[2], b[2], c[2], d[2] }; + t[3] = (vec4f_t) { a[3], b[3], c[3], d[3] }; +} + +#ifndef IMPLEMENT_MAT4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +void +mat4fquat (mat4f_t m, vec4f_t q) +{ + vec4f_t xq = q[0] * q; + vec4f_t yq = q[1] * q; + vec4f_t zq = q[2] * q; + vec4f_t wq = q[3] * q; + +#define shuff103(v) (vec4f_t) {v[1], v[0], v[3], v[2]} +#define shuff230(v) (vec4f_t) {v[2], v[3], v[0], v[1]} +#define shuff321(v) (vec4f_t) {v[3], v[2], v[1], v[0]} +#define p (0) +#define m (1u << 31) + static const vec4i_t mpm = { m, p, m, 0 }; + static const vec4i_t pmm = { p, m, m, 0 }; + static const vec4i_t mmp = { m, m, p, 0 }; + static const vec4i_t mask = { ~0u, ~0u, ~0u, 0 }; +#undef p +#undef m + { + vec4f_t a = xq; + vec4f_t b = (vec4f_t) ((vec4i_t) shuff103 (yq) ^ mpm); + vec4f_t c = (vec4f_t) ((vec4i_t) shuff230 (zq) ^ pmm); + vec4f_t d = (vec4f_t) ((vec4i_t) shuff321 (wq) ^ mmp); + // column: ww + xx - yy - zz // 2xy + 2wz // 2zx - 2wy // 0 + m[0] = (vec4f_t) ((vec4i_t) (a + b - c - d) & mask); + } + { + vec4f_t a = (vec4f_t) ((vec4i_t) shuff103 (xq) ^ mpm); + vec4f_t b = yq; + vec4f_t c = (vec4f_t) ((vec4i_t) shuff321 (zq) ^ mmp); + vec4f_t d = (vec4f_t) ((vec4i_t) shuff230 (wq) ^ pmm); + // column: 2xy - 2wz // ww - xx + yy - zz // 2yz + 2wx // 0 + m[1] = (vec4f_t) ((vec4i_t) (b + c - a - d) & mask); + } + { + vec4f_t a = (vec4f_t) ((vec4i_t) shuff230 (xq) ^ pmm); + vec4f_t b = (vec4f_t) ((vec4i_t) shuff321 (yq) ^ mmp); + vec4f_t c = zq; + vec4f_t d = (vec4f_t) ((vec4i_t) shuff103 (wq) ^ mpm); + // column: 2xz + 2wy // 2yz - 2wx // ww - xx - yy + zz // 0 + m[2] = (vec4f_t) ((vec4i_t) (a - b + c - d) & mask); + } + m[3] = (vec4f_t) { 0, 0, 0, 1 }; +} + +#endif//__QF_simd_mat4f_h diff --git a/include/QF/simd/types.h b/include/QF/simd/types.h new file mode 100644 index 000000000..bb04d2182 --- /dev/null +++ b/include/QF/simd/types.h @@ -0,0 +1,113 @@ +/* + QF/simd/types.h + + Type definitions for QF SIMD values + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_simd_types_h +#define __QF_simd_types_h + +#include +#include + +#define QF_VEC_TYPE(t,n,s) \ + typedef t n __attribute__ ((vector_size (s*sizeof (t)))) + +/** Three element vector type for interfacing with compact data. + * + * This cannot be used directly by SIMD code and must be loaded and stored + * using load_vec3d() and store_vec3d() respectively. + */ +typedef double vec3d_t[3]; + +QF_VEC_TYPE (double, vec2d_t, 2); +QF_VEC_TYPE (int64_t, vec2l_t, 2); + +/** Four element vector type for horizontal (AOS) vector data. + * + * This is used for both vectors (3D and 4D) and quaternions. 3D vectors + * are simply vec4d_t values with 0 in the 4th element. + * + * Also usable with vertical (SOA) code, in which case each vec4d_t represents + * a single component from four vectors, or a single row/column (depending on + * context) of an Nx4 or 4xN matrix. + */ +QF_VEC_TYPE (double, vec4d_t, 4); + +/** Used mostly for __builtin_shuffle. + */ +QF_VEC_TYPE (int64_t, vec4l_t, 4); + +/** Three element vector type for interfacing with compact data. + * + * This cannot be used directly by SIMD code and must be loaded and stored + * using load_vec3f() and store_vec3f() respectively. + */ +typedef float vec3f_t[3]; + +QF_VEC_TYPE (float, vec2f_t, 2); +QF_VEC_TYPE (int, vec2i_t, 2); +QF_VEC_TYPE (unsigned, vec2u_t, 2); + +/** Four element vector type for horizontal (AOS) vector data. + * + * This is used for both vectors (3D and 4D) and quaternions. 3D vectors + * are simply vec4f_t values with 0 in the 4th element. + * + * Also usable with vertical (SOA) code, in which case each vec4f_t represents + * a single component from four vectors, or a single row/column (depending on + * context) of an Nx4 or 4xN matrix. + */ +QF_VEC_TYPE (float, vec4f_t, 4); + +/** Used mostly for __builtin_shuffle. + */ +QF_VEC_TYPE (int, vec4i_t, 4); + +QF_VEC_TYPE (unsigned, vec4u_t, 4); + +#define VEC2D_FMT "[%.17g, %.17g]" +#define VEC2L_FMT "[%"PRIi64", %"PRIi64"]" +#define VEC4D_FMT "[%.17g, %.17g, %.17g, %.17g]" +#define VEC4L_FMT "[%"PRIi64", %"PRIi64", %"PRIi64", %"PRIi64"]" +#define VEC2F_FMT "[%.9g, %.9g]" +#define VEC2I_FMT "[%d, %d]" +#define VEC2U_FMT "[%u, %u]" +#define VEC4F_FMT "[%.9g, %.9g, %.9g, %.9g]" +#define VEC4I_FMT "[%d, %d, %d, %d]" +#define VEC4U_FMT "[%u, %u, %u, %u]" +#define VEC2_EXP(v) (v)[0], (v)[1] +#define VEC4_EXP(v) (v)[0], (v)[1], (v)[2], (v)[3] + +#define MAT4_ROW(m, r) (m)[0][r], (m)[1][r], (m)[2][r], (m)[3][r] + +typedef vec4f_t mat4f_t[4]; +typedef vec4i_t mat4i_t[4]; + +typedef struct vspheref_s { + vec4f_t center; // w set to 1 + float radius; +} vspheref_t; + +#endif//__QF_simd_types_h diff --git a/include/QF/simd/vec2d.h b/include/QF/simd/vec2d.h new file mode 100644 index 000000000..25eb263bf --- /dev/null +++ b/include/QF/simd/vec2d.h @@ -0,0 +1,149 @@ +/* + QF/simd/vec2d.h + + Vector functions for vec2d_t (ie, double precision) + + Copyright (C) 2020 Bill Currie + Copyright (C) 2022 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_simd_vec2d_h +#define __QF_simd_vec2d_h + +#ifdef __aarch64__ +#include +#else +#include +#endif + +#include "QF/simd/types.h" + +GNU89INLINE inline vec2d_t vsqrt2d (vec2d_t v) __attribute__((const)); +GNU89INLINE inline vec2d_t vceil2d (vec2d_t v) __attribute__((const)); +GNU89INLINE inline vec2d_t vfloor2d (vec2d_t v) __attribute__((const)); +GNU89INLINE inline vec2d_t vtrunc2d (vec2d_t v) __attribute__((const)); +/** 2D vector dot product. + */ +GNU89INLINE inline vec2d_t dot2d (vec2d_t a, vec2d_t b) __attribute__((const)); +GNU89INLINE inline vec2d_t cmuld (vec2d_t a, vec2d_t b) __attribute__((const)); + +#ifndef IMPLEMENT_VEC2D_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec2d_t +vsqrt2d (vec2d_t v) +{ +#ifdef __aarch64__ + return vsqrtq_f64 (v); +#else + return _mm_sqrt_pd (v); +#endif +} + +#ifndef IMPLEMENT_VEC2D_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec2d_t +vceil2d (vec2d_t v) +{ +#ifndef __SSE4_1__ + return (vec2d_t) { + ceil (v[0]), + ceil (v[1]), + }; +#else + return _mm_ceil_pd (v); +#endif +} + +#ifndef IMPLEMENT_VEC2D_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec2d_t +vfloor2d (vec2d_t v) +{ +#ifndef __SSE4_1__ + return (vec2d_t) { + floor (v[0]), + floor (v[1]), + }; +#else + return _mm_floor_pd (v); +#endif +} + +#ifndef IMPLEMENT_VEC2D_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec2d_t +vtrunc2d (vec2d_t v) +{ +#ifndef __SSE4_1__ + return (vec2d_t) { + trunc (v[0]), + trunc (v[1]), + }; +#else + return _mm_round_pd (v, _MM_FROUND_TRUNC); +#endif +} + +#ifndef IMPLEMENT_VEC2D_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec2d_t +dot2d (vec2d_t a, vec2d_t b) +{ + vec2d_t c = a * b; + // gcc-11 does a good job with hadd + c = (vec2d_t) { c[0] + c[1], c[0] + c[1] }; + return c; +} + +#ifndef IMPLEMENT_VEC2F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec2d_t +cmuld (vec2d_t a, vec2d_t b) +{ + vec2d_t c1 = a * b[0]; + vec2d_t c2 = a * b[1]; +#ifndef __SSE3__ + return (vec2d_t) { c1[0] - c2[1], c1[1] + c2[0] }; +#else + return _mm_addsub_pd (c1, (vec2d_t) { c2[1], c2[0] }); +#endif +} + +#endif//__QF_simd_vec2d_h diff --git a/include/QF/simd/vec2f.h b/include/QF/simd/vec2f.h new file mode 100644 index 000000000..3dcc97ecc --- /dev/null +++ b/include/QF/simd/vec2f.h @@ -0,0 +1,190 @@ +/* + QF/simd/vec2f.h + + Vector functions for vec2f_t (ie, float precision) + + Copyright (C) 2020 Bill Currie + Copyright (C) 2022 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_simd_vec2f_h +#define __QF_simd_vec2f_h + +#ifdef __aarch64__ +#include +#else +#include +#endif +#include + +#include "QF/simd/types.h" + +GNU89INLINE inline vec2f_t vabs2f (vec2f_t v) __attribute__((const)); +GNU89INLINE inline vec2f_t vsqrt2f (vec2f_t v) __attribute__((const)); +GNU89INLINE inline vec2f_t vceil2f (vec2f_t v) __attribute__((const)); +GNU89INLINE inline vec2f_t vfloor2f (vec2f_t v) __attribute__((const)); +GNU89INLINE inline vec2f_t vtrunc2f (vec2f_t v) __attribute__((const)); +/** 2D vector dot product. + */ +GNU89INLINE inline vec2f_t dot2f (vec2f_t a, vec2f_t b) __attribute__((const)); +GNU89INLINE inline vec2f_t cmulf (vec2f_t a, vec2f_t b) __attribute__((const)); +GNU89INLINE inline vec2f_t normal2f (vec2f_t v) __attribute__((pure)); +GNU89INLINE inline vec2f_t magnitude2f (vec2f_t v) __attribute__((pure)); + +#ifndef IMPLEMENT_VEC2F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec2f_t +vabs2f (vec2f_t v) +{ + const uint32_t nan = ~0u >> 1; + const vec2i_t abs = { nan, nan }; + return (vec2f_t) ((vec2i_t) v & abs); +} + +#ifndef IMPLEMENT_VEC2F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec2f_t +vsqrt2f (vec2f_t v) +{ +#ifdef __aarch64__ + return vsqrt_f32 (v); +#else + vec4f_t t = { v[0], v[1], 0, 0 }; + t = _mm_sqrt_ps (t); + return (vec2f_t) { t[0], t[1] }; +#endif +} + +#ifndef IMPLEMENT_VEC2F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec2f_t +vceil2f (vec2f_t v) +{ +#ifndef __SSE4_1__ + return (vec2f_t) { + ceilf (v[0]), + ceilf (v[1]), + }; +#else + vec4f_t t = { v[0], v[1], 0, 0 }; + t = _mm_ceil_ps (t); + return (vec2f_t) { t[0], t[1] }; +#endif +} + +#ifndef IMPLEMENT_VEC2F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec2f_t +vfloor2f (vec2f_t v) +{ +#ifndef __SSE4_1__ + return (vec2f_t) { + floorf (v[0]), + floorf (v[1]), + }; +#else + vec4f_t t = { v[0], v[1], 0, 0 }; + t = _mm_floor_ps (t); + return (vec2f_t) { t[0], t[1] }; +#endif +} + +#ifndef IMPLEMENT_VEC2F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec2f_t +vtrunc2f (vec2f_t v) +{ +#ifndef __SSE4_1__ + return (vec2f_t) { + truncf (v[0]), + truncf (v[1]), + }; +#else + vec4f_t t = { v[0], v[1], 0, 0 }; + t = _mm_round_ps (t, _MM_FROUND_TRUNC); + return (vec2f_t) { t[0], t[1] }; +#endif +} + +#ifndef IMPLEMENT_VEC2F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec2f_t +dot2f (vec2f_t a, vec2f_t b) +{ + vec2f_t c = a * b; + return (vec2f_t) { c[0] + c[1], c[0] + c[1] }; +} + +#ifndef IMPLEMENT_VEC2F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec2f_t +cmulf (vec2f_t a, vec2f_t b) +{ + vec2f_t c1 = a * b[0]; + vec2f_t c2 = a * b[1]; + return (vec2f_t) { c1[0] - c2[1], c1[1] + c2[0] }; +} + +#ifndef IMPLEMENT_VEC2F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec2f_t +normal2f (vec2f_t v) +{ + return v / vsqrt2f (dot2f (v, v)); +} + +#ifndef IMPLEMENT_VEC2F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec2f_t +magnitude2f (vec2f_t v) +{ + return vsqrt2f (dot2f (v, v)); +} + +#endif//__QF_simd_vec2f_h diff --git a/include/QF/simd/vec2i.h b/include/QF/simd/vec2i.h new file mode 100644 index 000000000..0032e3e33 --- /dev/null +++ b/include/QF/simd/vec2i.h @@ -0,0 +1,100 @@ +/* + QF/simd/vec2i.h + + Vector functions for vec2i_t (ie, int) + + Copyright (C) 2022 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_simd_vec2i_h +#define __QF_simd_vec2i_h + + +#include "QF/simd/types.h" + +GNU89INLINE inline vec2i_t vabs2i (vec2i_t v) __attribute__((const)); +GNU89INLINE inline int any2i (vec2i_t v) __attribute__((const)); +GNU89INLINE inline int all2i (vec2i_t v) __attribute__((const)); +GNU89INLINE inline int none2i (vec2i_t v) __attribute__((const)); + +#ifndef IMPLEMENT_VEC2I_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec2i_t +vabs2i (vec2i_t v) +{ + const uint32_t nan = ~0u >> 1; + const vec2i_t abs = { nan, nan }; + return (vec2i_t) ((vec2i_t) v & abs); +} + +#ifndef IMPLEMENT_VEC2I_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +int +any2i (vec2i_t v) +{ + vec2i_t t = v == (vec2i_t) {0, 0}; +#ifndef __SSSE3__ + return (t[0] + t[1]) > -2; +#else + return _mm_hadd_pi32 (t, t)[0] > -2; +#endif +} + +#ifndef IMPLEMENT_VEC2I_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +int +all2i (vec2i_t v) +{ + vec2i_t t = v == (vec2i_t) {0, 0}; +#ifndef __SSSE3__ + return (t[0] + t[1]) == 0; +#else + return _mm_hadd_pi32 (t, t)[0] == 0; +#endif +} + +#ifndef IMPLEMENT_VEC2I_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +int +none2i (vec2i_t v) +{ + vec2i_t t = v == (vec2i_t) {0, 0}; +#ifndef __SSSE3__ + return (t[0] + t[1]) == -2; +#else + return _mm_hadd_pi32 (t, t)[0] == -2; +#endif +} + +#endif//__QF_simd_vec2i_h diff --git a/include/QF/simd/vec4d.h b/include/QF/simd/vec4d.h new file mode 100644 index 000000000..a56c70ab7 --- /dev/null +++ b/include/QF/simd/vec4d.h @@ -0,0 +1,374 @@ +/* + QF/simd/vec4d.h + + Vector functions for vec4d_t (ie, double precision) + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_simd_vec4d_h +#define __QF_simd_vec4d_h + +#ifdef __aarch64__ +#include +#else +#include +#endif + +#include "QF/simd/types.h" +#include "QF/simd/vec2d.h" + +GNU89INLINE inline vec4d_t vsqrt4d (vec4d_t v) __attribute__((const)); +GNU89INLINE inline vec4d_t vceil4d (vec4d_t v) __attribute__((const)); +GNU89INLINE inline vec4d_t vfloor4d (vec4d_t v) __attribute__((const)); +GNU89INLINE inline vec4d_t vtrunc4d (vec4d_t v) __attribute__((const)); +/** 3D vector cross product. + * + * The w (4th) component can be any value on input, and is guaranteed to be 0 + * in the result. The result is not affected in any way by either vector's w + * componemnt + */ +GNU89INLINE inline vec4d_t crossd (vec4d_t a, vec4d_t b) __attribute__((const)); +/** 4D vector dot product. + * + * The w component *IS* significant, but if it is 0 in either vector, then + * the result will be as for a 3D dot product. + * + * Note that the dot product is in all 4 of the return value's elements. This + * helps optimize vector math as the scalar is already pre-spread. If just the + * scalar is required, use result[0]. + */ +GNU89INLINE inline vec4d_t dotd (vec4d_t a, vec4d_t b) __attribute__((const)); +/** Quaternion product. + * + * The vector is interpreted as a quaternion instead of a regular 4D vector. + * The quaternion may be of any magnitude, so this is more generally useful. + * than if the quaternion was required to be unit length. + */ +GNU89INLINE inline vec4d_t qmuld (vec4d_t a, vec4d_t b) __attribute__((const)); +/** Optimized quaterion-vector multiplication for vector rotation. + * + * \note This is the inverse of vqmulf + * + * If the vector's w component is not zero, then the result's w component + * is the cosine of the full rotation angle scaled by the vector's w component. + * The quaternion is assumed to be unit. If the quaternion is not unit, the + * vector will be scaled by the square of the quaternion's magnitude. + */ +GNU89INLINE inline vec4d_t qvmuld (vec4d_t q, vec4d_t v) __attribute__((const)); +/** Optimized vector-quaterion multiplication for vector rotation. + * + * \note This is the inverse of qvmulf + * + * If the vector's w component is not zero, then the result's w component + * is the cosine of the full rotation angle scaled by the vector's w component. + * The quaternion is assumed to be unit. If the quaternion is not unit, the + * vector will be scaled by the square of the quaternion's magnitude. + */ +GNU89INLINE inline vec4d_t vqmuld (vec4d_t v, vec4d_t q) __attribute__((const)); +/** Create the quaternion representing the shortest rotation from a to b. + * + * Both a and b are assumed to be 3D vectors (w components 0), but a resonable + * (but incorrect) result will still be produced if either a or b is a 4D + * vector. The rotation axis will be the same as if both vectors were 3D, but + * the magnitude of the rotation will be different. + */ +GNU89INLINE inline vec4d_t qrotd (vec4d_t a, vec4d_t b) __attribute__((const)); +/** Return the conjugate of the quaternion. + * + * That is, [-x, -y, -z, w]. + */ +GNU89INLINE inline vec4d_t qconjd (vec4d_t q) __attribute__((const)); +GNU89INLINE inline vec4d_t loadvec3d (const double v3[]) __attribute__((pure)); +GNU89INLINE inline void storevec3d (double v3[3], vec4d_t v4); +GNU89INLINE inline vec4l_t loadvec3l (const int64_t *v3) __attribute__((pure)); +GNU89INLINE inline vec4l_t loadvec3l1 (const int64_t *v3) __attribute__((pure)); +GNU89INLINE inline void storevec3l (int64_t *v3, vec4l_t v4); + +#ifndef IMPLEMENT_VEC4D_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4d_t +vsqrt4d (vec4d_t v) +{ +#ifndef __AVX__ + vec2d_t xy = { v[0], v[1] }; + vec2d_t zw = { v[2], v[3] }; + xy = vsqrt2d (xy); + zw = vsqrt2d (zw); + return (vec4d_t) { xy[0], xy[1], zw[0], zw[1] }; +#else + return _mm256_sqrt_pd (v); +#endif +} + +#ifndef IMPLEMENT_VEC4D_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4d_t +vceil4d (vec4d_t v) +{ +#ifndef __AVX__ + vec2d_t xy = { v[0], v[1] }; + vec2d_t zw = { v[2], v[3] }; + xy = vceil2d (xy); + zw = vceil2d (zw); + return (vec4d_t) { xy[0], xy[1], zw[0], zw[1] }; +#else + return _mm256_ceil_pd (v); +#endif +} + +#ifndef IMPLEMENT_VEC4D_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4d_t +vfloor4d (vec4d_t v) +{ +#ifndef __AVX__ + vec2d_t xy = { v[0], v[1] }; + vec2d_t zw = { v[2], v[3] }; + xy = vfloor2d (xy); + zw = vfloor2d (zw); + return (vec4d_t) { xy[0], xy[1], zw[0], zw[1] }; +#else + return _mm256_floor_pd (v); +#endif +} + +#ifndef IMPLEMENT_VEC4D_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4d_t +vtrunc4d (vec4d_t v) +{ +#ifndef __AVX__ + vec2d_t xy = { v[0], v[1] }; + vec2d_t zw = { v[2], v[3] }; + xy = vtrunc2d (xy); + zw = vtrunc2d (zw); + return (vec4d_t) { xy[0], xy[1], zw[0], zw[1] }; +#else + return _mm256_round_pd (v, _MM_FROUND_TRUNC); +#endif +} + +#ifndef IMPLEMENT_VEC4D_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4d_t +crossd (vec4d_t a, vec4d_t b) +{ + vec4d_t c = a * (vec4d_t) {b[1], b[2], b[0], b[3]} + - b * (vec4d_t) {a[1], a[2], a[0], a[3]}; + return (vec4d_t) {c[1], c[2], c[0], c[3]}; +} + +#ifndef IMPLEMENT_VEC4D_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4d_t +dotd (vec4d_t a, vec4d_t b) +{ + vec4d_t c = a * b; + c += (vec4d_t) { c[3], c[0], c[1], c[2] }; + c += (vec4d_t) { c[2], c[3], c[0], c[1] }; + return c; +} + +#ifndef IMPLEMENT_VEC4D_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4d_t +qmuld (vec4d_t a, vec4d_t b) +{ + // results in [2*as*bs, as*b + bs*a + a x b] ([scalar, vector] notation) + // doesn't seem to adversly affect precision + vec4d_t c = crossd (a, b) + a * b[3] + a[3] * b; + vec4d_t d = dotd (a, b); + // zero out the vector component of dot product so only the scalar remains + d = (vec4d_t) { 0, 0, 0, d[3] }; + return c - d; +} + +#ifndef IMPLEMENT_VEC4D_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4d_t +qvmuld (vec4d_t q, vec4d_t v) +// ^^^ ^^^ +{ + double s = q[3]; + // zero the scalar of the quaternion. Results in an extra operation, but + // avoids adding precision issues. +#ifndef __AVX__ + q = (vec4d_t) { q[0], q[1], q[2], 0 }; +#else + vec4d_t z = {}; + q = _mm256_blend_pd (q, z, 0x08); +#endif + vec4d_t c = crossd (q, v); + vec4d_t qv = dotd (q, v); // q.w is 0 so v.w is irrelevant + vec4d_t qq = dotd (q, q); + + // vvv + return (s * s - qq) * v + 2 * (qv * q + s * c); +} + +#ifndef IMPLEMENT_VEC4D_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4d_t +vqmuld (vec4d_t v, vec4d_t q) +// ^^^ ^^^ +{ + double s = q[3]; + // zero the scalar of the quaternion. Results in an extra operation, but + // avoids adding precision issues. +#ifndef __AVX__ + q = (vec4d_t) { q[0], q[1], q[2], 0 }; +#else + vec4d_t z = {}; + q = _mm256_blend_pd (q, z, 0x08); +#endif + vec4d_t c = crossd (q, v); + vec4d_t qv = dotd (q, v); // q.w is 0 so v.w is irrelevant + vec4d_t qq = dotd (q, q); + + // vvv + return (s * s - qq) * v + 2 * (qv * q - s * c); +} + +#ifndef IMPLEMENT_VEC4D_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4d_t +qrotd (vec4d_t a, vec4d_t b) +{ + vec4d_t ma = vsqrt4d (dotd (a, a)); + vec4d_t mb = vsqrt4d (dotd (b, b)); + vec4d_t den = 2 * ma * mb; + vec4d_t t = mb * a + ma * b; + vec4d_t mba_mab = vsqrt4d (dotd (t, t)); + vec4d_t q = crossd (a, b) / mba_mab; + q[3] = (mba_mab / den)[0]; + return q; +} + +#ifndef IMPLEMENT_VEC4D_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4d_t +qconjd (vec4d_t q) +{ + return (vec4d_t) { -q[0], -q[1], -q[2], q[3] }; +} + +#ifndef IMPLEMENT_VEC4D_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4d_t +loadvec3d (const double v3[]) +{ + vec4d_t v4 = {}; + + v4[0] = v3[0]; + v4[1] = v3[1]; + v4[2] = v3[2]; + return v4; +} + +#ifndef IMPLEMENT_VEC4D_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +void +storevec3d (double v3[3], vec4d_t v4) +{ + v3[0] = v4[0]; + v3[1] = v4[1]; + v3[2] = v4[2]; +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4l_t +loadvec3l (const int64_t *v3) +{ + vec4l_t v4 = { v3[0], v3[1], v3[2], 0 }; + return v4; +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4l_t +loadvec3l1 (const int64_t *v3) +{ + vec4l_t v4 = { v3[0], v3[1], v3[2], 1 }; + return v4; +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +void +storevec3l (int64_t *v3, vec4l_t v4) +{ + v3[0] = v4[0]; + v3[1] = v4[1]; + v3[2] = v4[2]; +} + +#endif//__QF_simd_vec4d_h diff --git a/include/QF/simd/vec4f.h b/include/QF/simd/vec4f.h new file mode 100644 index 000000000..effb32864 --- /dev/null +++ b/include/QF/simd/vec4f.h @@ -0,0 +1,408 @@ +/* + QF/simd/vec4f.h + + Vector functions for vec4f_t (ie, float precision) + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_simd_vec4f_h +#define __QF_simd_vec4f_h + +#ifdef __aarch64__ +#include +#else +#include +#endif +#include + +#include "QF/simd/types.h" + +GNU89INLINE inline vec4f_t vabs4f (vec4f_t v) __attribute__((const)); +GNU89INLINE inline vec4f_t vsqrt4f (vec4f_t v) __attribute__((const)); +GNU89INLINE inline vec4f_t vceil4f (vec4f_t v) __attribute__((const)); +GNU89INLINE inline vec4f_t vfloor4f (vec4f_t v) __attribute__((const)); +GNU89INLINE inline vec4f_t vtrunc4f (vec4f_t v) __attribute__((const)); +/** 3D vector cross product. + * + * The w (4th) component can be any value on input, and is guaranteed to be 0 + * in the result. The result is not affected in any way by either vector's w + * componemnt + */ +GNU89INLINE inline vec4f_t crossf (vec4f_t a, vec4f_t b) __attribute__((const)); +/** 4D vector dot product. + * + * The w component *IS* significant, but if it is 0 in either vector, then + * the result will be as for a 3D dot product. + * + * Note that the dot product is in all 4 of the return value's elements. This + * helps optimize vector math as the scalar is already pre-spread. If just the + * scalar is required, use result[0]. + */ +GNU89INLINE inline vec4f_t dotf (vec4f_t a, vec4f_t b) __attribute__((const)); +/** Quaternion product. + * + * The vector is interpreted as a quaternion instead of a regular 4D vector. + * The quaternion may be of any magnitude, so this is more generally useful. + * than if the quaternion was required to be unit length. + */ +GNU89INLINE inline vec4f_t qmulf (vec4f_t a, vec4f_t b) __attribute__((const)); +/** Optimized quaterion-vector multiplication for vector rotation. + * + * \note This is the inverse of vqmulf + * + * If the vector's w component is not zero, then the result's w component + * is the cosine of the full rotation angle scaled by the vector's w component. + * The quaternion is assumed to be unit. + */ +GNU89INLINE inline vec4f_t qvmulf (vec4f_t q, vec4f_t v) __attribute__((const)); +/** Optimized vector-quaterion multiplication for vector rotation. + * + * \note This is the inverse of qvmulf + * + * If the vector's w component is not zero, then the result's w component + * is the cosine of the full rotation angle scaled by the vector's w component. + * The quaternion is assumed to be unit. + */ +GNU89INLINE inline vec4f_t vqmulf (vec4f_t v, vec4f_t q) __attribute__((const)); +/** Create the quaternion representing the shortest rotation from a to b. + * + * Both a and b are assumed to be 3D vectors (w components 0), but a resonable + * (but incorrect) result will still be produced if either a or b is a 4D + * vector. The rotation axis will be the same as if both vectors were 3D, but + * the magnitude of the rotation will be different. + */ +GNU89INLINE inline vec4f_t qrotf (vec4f_t a, vec4f_t b) __attribute__((const)); +/** Return the conjugate of the quaternion. + * + * That is, [-x, -y, -z, w]. + */ +GNU89INLINE inline vec4f_t qconjf (vec4f_t q) __attribute__((const)); +GNU89INLINE inline vec4f_t qexpf (vec4f_t q) __attribute__((const)); +GNU89INLINE inline vec4f_t loadvec3f (const float *v3) __attribute__((pure)); +GNU89INLINE inline void storevec3f (float *v3, vec4f_t v4); +GNU89INLINE inline vec4f_t normalf (vec4f_t v) __attribute__((pure)); +GNU89INLINE inline vec4f_t magnitudef (vec4f_t v) __attribute__((pure)); +GNU89INLINE inline vec4f_t magnitude3f (vec4f_t v) __attribute__((pure)); + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4f_t +vabs4f (vec4f_t v) +{ + const uint32_t nan = ~0u >> 1; + const vec4i_t abs = { nan, nan, nan, nan }; + return (vec4f_t) ((vec4i_t) v & abs); +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4f_t +vsqrt4f (vec4f_t v) +{ +#ifdef __aarch64__ + return vsqrtq_f32 (v); +#else +#ifndef __SSE__ + vec4f_t r = { sqrtf (v[0]), sqrtf (v[1]), sqrtf (v[2]), sqrtf (v[3]) }; + return r; +#else + return _mm_sqrt_ps (v); +#endif +#endif +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4f_t +vceil4f (vec4f_t v) +{ +#ifndef __SSE4_1__ + return (vec4f_t) { + ceilf (v[0]), + ceilf (v[1]), + ceilf (v[2]), + ceilf (v[3]) + }; +#else + return _mm_ceil_ps (v); +#endif +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4f_t +vfloor4f (vec4f_t v) +{ +#ifndef __SSE4_1__ + return (vec4f_t) { + floorf (v[0]), + floorf (v[1]), + floorf (v[2]), + floorf (v[3]) + }; +#else + return _mm_floor_ps (v); +#endif +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4f_t +vtrunc4f (vec4f_t v) +{ +#ifndef __SSE4_1__ + return (vec4f_t) { + truncf (v[0]), + truncf (v[1]), + truncf (v[2]), + truncf (v[3]) + }; +#else + return _mm_round_ps (v, _MM_FROUND_TRUNC); +#endif +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4f_t +crossf (vec4f_t a, vec4f_t b) +{ + vec4f_t c = a * (vec4f_t) {b[1], b[2], b[0], b[3]} + - b * (vec4f_t) {a[1], a[2], a[0], a[3]}; + return (vec4f_t) {c[1], c[2], c[0], c[3]}; +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4f_t +dotf (vec4f_t a, vec4f_t b) +{ + vec4f_t c = a * b; + c += (vec4f_t) { c[3], c[0], c[1], c[2] }; + c += (vec4f_t) { c[2], c[3], c[0], c[1] }; + return c; +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4f_t +qmulf (vec4f_t a, vec4f_t b) +{ + // results in [2*as*bs, as*b + bs*a + a x b] ([scalar, vector] notation) + // doesn't seem to adversly affect precision + vec4f_t c = crossf (a, b) + a * b[3] + a[3] * b; + vec4f_t d = dotf (a, b); + // zero out the vector component of dot product so only the scalar remains + d = (vec4f_t) { 0, 0, 0, d[3] }; + return c - d; +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4f_t +qvmulf (vec4f_t q, vec4f_t v) +{ + float s = q[3]; + // zero the scalar of the quaternion. Results in an extra operation, but + // avoids adding precision issues. +#ifndef __SSE4_1__ + q[3] = 0; +#else + q = _mm_insert_ps (q, q, 0xf8); +#endif + vec4f_t c = crossf (q, v); + vec4f_t qv = dotf (q, v); // q.w is 0 so v.w is irrelevant + vec4f_t qq = dotf (q, q); + + return (s * s - qq) * v + 2 * (qv * q + s * c); +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4f_t +vqmulf (vec4f_t v, vec4f_t q) +{ + float s = q[3]; + // zero the scalar of the quaternion. Results in an extra operation, but + // avoids adding precision issues. +#ifndef __SSE4_1__ + q[3] = 0; +#else + q = _mm_insert_ps (q, q, 0xf8); +#endif + vec4f_t c = crossf (q, v); + vec4f_t qv = dotf (q, v); // q.w is 0 so v.w is irrelevant + vec4f_t qq = dotf (q, q); + + return (s * s - qq) * v + 2 * (qv * q - s * c); +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4f_t +qrotf (vec4f_t a, vec4f_t b) +{ + vec4f_t ma = vsqrt4f (dotf (a, a)); + vec4f_t mb = vsqrt4f (dotf (b, b)); + vec4f_t den = 2 * ma * mb; + vec4f_t t = mb * a + ma * b; + vec4f_t mba_mab = vsqrt4f (dotf (t, t)); + vec4f_t q = crossf (a, b) / mba_mab; + q[3] = (mba_mab / den)[0]; + return q; +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4f_t +qconjf (vec4f_t q) +{ + return (vec4f_t) { -q[0], -q[1], -q[2], q[3] }; +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4f_t +qexpf (vec4f_t q) +{ + vec4f_t th = magnitude3f (q); + float r = expf (q[3]); + if (!th[0]) { + return (vec4f_t) { 0, 0, 0, r }; + } + float c = cosf (th[0]); + float s = sinf (th[0]); + vec4f_t n = (r * s) * (q / th); + n[3] = r * c; + return n; +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4f_t +loadvec3f (const float *v3) +{ + vec4f_t v4; + + v4 = (vec4f_t) { v3[0], v3[1], v3[2], 0 }; + return v4; +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +void +storevec3f (float *v3, vec4f_t v4) +{ + v3[0] = v4[0]; + v3[1] = v4[1]; + v3[2] = v4[2]; +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4f_t +normalf (vec4f_t v) +{ + return v / vsqrt4f (dotf (v, v)); +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4f_t +magnitudef (vec4f_t v) +{ + return vsqrt4f (dotf (v, v)); +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4f_t +magnitude3f (vec4f_t v) +{ + v[3] = 0; + return vsqrt4f (dotf (v, v)); +} + +vec4f_t __attribute__((pure)) +BarycentricCoords_vf (const vec4f_t **points, int num_points, vec4f_t p); + +vspheref_t __attribute__((pure)) +CircumSphere_vf (const vec4f_t *points, int num_points); + +vspheref_t SmallestEnclosingBall_vf (const vec4f_t *points, int num_points); + +#endif//__QF_simd_vec4f_h diff --git a/include/QF/simd/vec4i.h b/include/QF/simd/vec4i.h new file mode 100644 index 000000000..0e947a06a --- /dev/null +++ b/include/QF/simd/vec4i.h @@ -0,0 +1,141 @@ +/* + QF/simd/vec4i.h + + Vector functions for vec4i_t (ie, int) + + Copyright (C) 2022 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_simd_vec4i_h +#define __QF_simd_vec4i_h + +#include + +#include "QF/simd/types.h" + +GNU89INLINE inline vec4i_t vabs4i (vec4i_t v) __attribute__((const)); +GNU89INLINE inline int any4i (vec4i_t v) __attribute__((const)); +GNU89INLINE inline int all4i (vec4i_t v) __attribute__((const)); +GNU89INLINE inline int none4i (vec4i_t v) __attribute__((const)); +GNU89INLINE inline vec4i_t loadvec3i (const int *v3) __attribute__((pure)); +GNU89INLINE inline vec4i_t loadvec3i1 (const int *v3) __attribute__((pure)); +GNU89INLINE inline void storevec3i (int *v3, vec4i_t v4); + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4i_t +vabs4i (vec4i_t v) +{ + const uint32_t nan = ~0u >> 1; + const vec4i_t abs = { nan, nan, nan, nan }; + return (vec4i_t) ((vec4i_t) v & abs); +} + +#ifndef IMPLEMENT_VEC2I_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +int +any4i (vec4i_t v) +{ +#ifndef __SSE4_1__ + vec4i_t t = (v != (vec4i_t) {}); + return (t[0] + t[1] + t[2] + t[3]) != 0; +#else + return !__builtin_ia32_ptestz128 ((__v2di)v, (__v2di)v); +#endif +} + +#ifndef IMPLEMENT_VEC2I_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +int +all4i (vec4i_t v) +{ + vec4i_t t = (v == (vec4i_t) {}); +#ifndef __SSE4_1__ + return (t[0] + t[1] + t[2] + t[3]) == 0; +#else + return __builtin_ia32_ptestz128 ((__v2di)t, (__v2di)t); +#endif +} + +#ifndef IMPLEMENT_VEC2I_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +int +none4i (vec4i_t v) +{ +#ifndef __SSE4_1__ + vec4i_t t = (v != (vec4i_t) {}); + return (t[0] + t[1] + t[2] + t[3]) == 0; +#else + return __builtin_ia32_ptestz128 ((__v2di)v, (__v2di)v); +#endif +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4i_t +loadvec3i (const int *v3) +{ + vec4i_t v4 = { v3[0], v3[1], v3[2], 0 }; + return v4; +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +vec4i_t +loadvec3i1 (const int *v3) +{ + vec4i_t v4 = { v3[0], v3[1], v3[2], 1 }; + return v4; +} + +#ifndef IMPLEMENT_VEC4F_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +void +storevec3i (int *v3, vec4i_t v4) +{ + v3[0] = v4[0]; + v3[1] = v4[1]; + v3[2] = v4[2]; +} + +#endif//__QF_simd_vec4i_h diff --git a/include/QF/sizebuf.h b/include/QF/sizebuf.h index 8605f803d..752ab2b26 100644 --- a/include/QF/sizebuf.h +++ b/include/QF/sizebuf.h @@ -24,33 +24,34 @@ Boston, MA 02111-1307, USA */ -#ifndef __sizebuf_h -#define __sizebuf_h +#ifndef __QF_sizebuf_h +#define __QF_sizebuf_h /** \defgroup sizebuf Fixed Size Buffers \ingroup utils Fixed size buffer management */ -//@{ +///@{ #include "QF/qtypes.h" typedef struct sizebuf_s { - qboolean allowoverflow; // if false, do a Sys_Error - qboolean overflowed; // set to true if the buffer size failed - byte *data; - int maxsize; - int cursize; + bool allowoverflow; // if false, do a Sys_Error + bool overflowed; // set to true if the buffer size failed + byte *data; + unsigned maxsize; + unsigned cursize; } sizebuf_t; -void SZ_Alloc (sizebuf_t *buf, int startsize); +void SZ_Alloc (sizebuf_t *buf, unsigned maxsize); void SZ_Free (sizebuf_t *buf); void SZ_Clear (sizebuf_t *buf); -void *SZ_GetSpace (sizebuf_t *buf, int length); -void SZ_Write (sizebuf_t *buf, const void *data, int length); +void *SZ_GetSpace (sizebuf_t *buf, unsigned length); +void SZ_Write (sizebuf_t *buf, const void *data, unsigned length); void SZ_Print (sizebuf_t *buf, const char *data); // strcats onto the sizebuf +void SZ_Dump (sizebuf_t *buf); -//@} +///@} -#endif // __sizebuf_h +#endif//__QF_sizebuf_h diff --git a/include/QF/skin.h b/include/QF/skin.h index b9c8a3dcb..75272a26a 100644 --- a/include/QF/skin.h +++ b/include/QF/skin.h @@ -25,8 +25,8 @@ */ -#ifndef _SKIN_H -#define _SKIN_H +#ifndef __QF_skin_h +#define __QF_skin_h #include "QF/qtypes.h" #include "QF/vid.h" @@ -50,11 +50,11 @@ typedef struct skin_s { const char *name; - qboolean valid; // the skin was found + bool valid; // the skin was found struct tex_s *texels; byte *colormap; int texnum; int auxtex; } skin_t; -#endif +#endif//__QF_skin_h diff --git a/include/QF/sound.h b/include/QF/sound.h index 874469ea4..8bfad49e0 100644 --- a/include/QF/sound.h +++ b/include/QF/sound.h @@ -26,50 +26,37 @@ */ // sound.h -- client sound i/o functions -#ifndef _SOUND_H -#define _SOUND_H +#ifndef __QF_sound_h +#define __QF_sound_h /** \defgroup sound QuakeForge sound engine */ #include "QF/mathlib.h" +#include "QF/simd/types.h" + +struct transform_s; /** \ingroup sound */ -//@{ +///@{ typedef struct sfx_s sfx_t; -struct sfx_s -{ - const char *name; - sfx_t *owner; +typedef struct channel_s channel_t; - unsigned int length; - unsigned int loopstart; - - union { - struct sfxstream_s *stream; - struct sfxblock_s *block; - } data; - - struct sfxbuffer_s *(*touch) (sfx_t *sfx); - struct sfxbuffer_s *(*retain) (sfx_t *sfx); - void (*release) (sfx_t *sfx); - - struct sfxbuffer_s *(*getbuffer) (sfx_t *sfx); - struct wavinfo_s *(*wavinfo) (sfx_t *sfx); - - sfx_t *(*open) (sfx_t *sfx); - void (*close) (sfx_t *sfx); -}; -//@} - -struct model_s; +typedef enum chan_state_e { + chan_invalid, //!< Channel is in an invalid state and must not be used + chan_pending, //!< Channel is waiting to be initialized + chan_done, //!< Channel is done and should be freed + chan_paused, //!< Channel is paused but can be resumed at any time + chan_playing, //!< Channel is currently playing +} chan_state; +///@} /** \defgroup sound_init Initialization functions \ingroup sound */ -//@{ +///@{ /** Initialize the sound engine. \param viewentity pointer to view entity index @@ -85,12 +72,12 @@ void S_Init_Cvars (void); gracefully. */ void S_Shutdown (void); -//@} +///@} /** \defgroup sound_stuff Unclassified \ingroup sound */ -//@{ +///@{ /** Start a sound playing. \param entnum index of entity the sound is associated with. @@ -102,11 +89,11 @@ void S_Shutdown (void); - 4 body \param sfx sound to play \param origin 3d coords of where the sound originates - \param fvol absolute volume of the sound + \param vol absolute volume of the sound \param attenuation rate of volume dropoff vs distance */ -void S_StartSound (int entnum, int entchannel, sfx_t *sfx, const vec3_t origin, - float fvol, float attenuation); +void S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec4f_t origin, + float vol, float attenuation); /** Create a sound generated by the world. \param sfx sound to play @@ -114,8 +101,7 @@ void S_StartSound (int entnum, int entchannel, sfx_t *sfx, const vec3_t origin, \param vol absolute volume of the sound \param attenuation rate of volume dropoff vs distance */ -void S_StaticSound (sfx_t *sfx, const vec3_t origin, float vol, - float attenuation); +void S_StaticSound (sfx_t *sfx, vec4f_t origin, float vol, float attenuation); /** Stop an entity's sound. \param entnum index of entity the sound is associated with. \param entchannel channel to silence @@ -128,16 +114,12 @@ void S_StopAllSounds(void); /** Update the sound engine with the client's position and orientation and render some sound. - \param origin 3d coords of the client - \param v_forward 3d vector of the client's facing direction - \param v_right 3d vector of the client's rightward direction - \param v_up 3d vector of the client's upward direction + \param ear Transform for the position and orientation of the stereo + sound pickup. \param ambient_sound_level NUM_AMBIENTS bytes indicating current ambient sound levels */ -void S_Update (const vec3_t origin, const vec3_t v_forward, - const vec3_t v_right, const vec3_t v_up, - const byte *ambient_sound_level); +void S_Update (struct transform_s ear, const byte *ambient_sound_level); /** Render some more sound without updating the client's position/orientation. */ @@ -165,12 +147,24 @@ sfx_t *S_LoadSound (const char *name); /** Allocate a sound channel that can be used for playing sounds. */ -struct channel_s *S_AllocChannel (void); +channel_t *S_AllocChannel (void); /** Stop and safely free a channel. \param chan channel to stop */ -void S_ChannelStop (struct channel_s *chan); +void S_ChannelFree (channel_t *chan); + +int S_ChannelSetSfx (channel_t *chan, sfx_t *sfx); +void S_ChannelSetPaused (channel_t *chan, int paused); +void S_ChannelSetLooping (channel_t *chan, int looping); +chan_state S_ChannelGetState (channel_t *chan); + +/** Set a channel's volume. + \param chan The channel for which the volume will be set. + \param volume The overall playback volume of the channel: set to 0 for + silence, 1 for full volume. +*/ +void S_ChannelSetVolume (channel_t *chan, float volume); /** Start a sound local to the client view. \param s name of sound to play @@ -187,12 +181,14 @@ void S_AmbientOff (void); */ void S_AmbientOn (void); +void S_SetAmbient (int amb_channel, sfx_t *sfx); + /** Link sound engine builtins into the specified progs vm \param pr the vm to link the builtins into. */ struct progs_s; void S_Progs_Init (struct progs_s *pr); -//@} +///@} -#endif // _SOUND_H +#endif//__QF_sound_h diff --git a/include/QF/spritegn.h b/include/QF/spritegn.h index 4505ad59f..de490f28d 100644 --- a/include/QF/spritegn.h +++ b/include/QF/spritegn.h @@ -45,8 +45,8 @@ // //------------------------------------------------------- -#ifndef _SPRITEGN_H -#define _SPRITEGN_H +#ifndef __QF_sprintgn_h +#define __QF_sprintgn_h #define SPR_VERSION 1 #define SP2_VERSION 2 @@ -102,4 +102,4 @@ typedef struct { // little-endian "IDS2" #define IDHEADER_SP2 (('2'<<24)+('S'<<16)+('D'<<8)+'I') -#endif // _SPRITEGN_H +#endif//__QF_sprintgn_h diff --git a/include/QF/sys.h b/include/QF/sys.h index 8bbb08781..dd8c53e0c 100644 --- a/include/QF/sys.h +++ b/include/QF/sys.h @@ -25,25 +25,27 @@ */ -#ifndef __sys_h -#define __sys_h +#ifndef __QF_sys_h +#define __QF_sys_h -/** \defgroup sys Portability +/** \defgroup sys System Portability \ingroup utils Non-portable functions */ -//@{ +///@{ #include #include #include -extern struct cvar_s *sys_nostdout; -extern struct cvar_s *sys_extrasleep; -extern struct cvar_s *sys_dead_sleep; -extern struct cvar_s *sys_sleep; +struct dstring_s; -extern struct cvar_s *developer; +extern int sys_nostdout; +extern int sys_extrasleep; +extern int sys_dead_sleep; +extern int sys_sleep; + +extern int developer; extern const char sys_char_map[256]; @@ -62,42 +64,46 @@ int Sys_FileExists (const char *path); int Sys_isdir (const char *path); int Sys_mkdir (const char *path); -typedef void (*sys_printf_t) (const char *fmt, va_list args); +typedef void (*sys_printf_t) (const char *fmt, va_list args) __attribute__((format(PRINTF, 1, 0))); typedef void (*sys_error_t) (void *data); -void Sys_SetStdPrintf (sys_printf_t func); -void Sys_SetErrPrintf (sys_printf_t func); +sys_printf_t Sys_SetStdPrintf (sys_printf_t func); +sys_printf_t Sys_SetErrPrintf (sys_printf_t func); void Sys_PushErrorHandler (sys_error_t func, void *data); void Sys_PopErrorHandler (void); -void Sys_Print (FILE *stream, const char *fmt, va_list args); -void Sys_Printf (const char *fmt, ...) __attribute__((format(printf,1,2))); -void Sys_Error (const char *error, ...) __attribute__((format(printf,1,2), noreturn)); -void Sys_FatalError (const char *error, ...) __attribute__((format(printf,1,2), noreturn)); +void Sys_Print (FILE *stream, const char *fmt, va_list args) __attribute__((format(PRINTF, 2, 0))); +void Sys_Printf (const char *fmt, ...) __attribute__((format(PRINTF,1,2))); +void Sys_Error (const char *error, ...) __attribute__((format(PRINTF,1,2), noreturn)); +void Sys_FatalError (const char *error, ...) __attribute__((format(PRINTF,1,2), noreturn)); void Sys_Quit (void) __attribute__((noreturn)); void Sys_Shutdown (void); -void Sys_RegisterShutdown (void (*func) (void)); +void Sys_RegisterShutdown (void (*func) (void *), void *data); +int64_t Sys_StartTime (void) __attribute__ ((pure)); int64_t Sys_LongTime (void); double Sys_DoubleTime (void); +int64_t Sys_TimeBase (void) __attribute__ ((const)); +double Sys_DoubleTimeBase (void) __attribute__ ((const)); void Sys_TimeOfDay(date_t *date); -void Sys_MaskPrintf (int mask, const char *fmt, ...) __attribute__((format(printf,2,3))); -#define SYS_DEV (1|0) -#define SYS_WARN (1|2) // bit 0 so developer 1 will pick it up -#define SYS_VID (1|4) -#define SYS_FS_NF (1|8) -#define SYS_FS_F (1|16) -#define SYS_FS (1|32) -#define SYS_NET (1|64) -#define SYS_RUA_OBJ (1|128) -#define SYS_RUA_MSG (1|256) -#define SYS_SND (1|512) -#define SYS_GLT (1|1024) -#define SYS_GLSL (1|2048) -#define SYS_SKIN (1|4096) -#define SYS_MODEL (1|8192) +#define SYS_DEVELOPER(developer) SYS_DeveloperID_##developer, +enum { +#include "QF/sys_developer.h" +}; +// bit 0 so developer 1 will pick it up +#define SYS_DEVELOPER(developer) \ + SYS_##developer = (SYS_dev | (1 << (SYS_DeveloperID_##developer + 1))), +typedef enum { + SYS_dev = 1, +#include "QF/sys_developer.h" +} sys_developer_e; + +void Sys_MaskPrintf (sys_developer_e mask, const char *fmt, ...) __attribute__((format(PRINTF,2,3))); + +struct qf_fd_set; +int Sys_Select (int maxfd, struct qf_fd_set *fdset, int64_t usec); int Sys_CheckInput (int idle, int net_socket); const char *Sys_ConsoleInput (void); @@ -120,12 +126,18 @@ void Sys_Init_Cvars (void); // memory protection // void Sys_MakeCodeWriteable (uintptr_t startaddr, size_t length); -void Sys_PageIn (void *ptr, int size); +void Sys_PageIn (void *ptr, size_t size); +size_t Sys_PageSize (void); +void *Sys_Alloc (size_t size); +void Sys_Free (void *mem, size_t size); +int Sys_LockMemory (void *mem, size_t size); + +int Sys_ProcessorCount (void); // // system IO // -void Sys_DebugLog(const char *file, const char *fmt, ...) __attribute__((format(printf,2,3))); +void Sys_DebugLog(const char *file, const char *fmt, ...) __attribute__((format(PRINTF,2,3))); #define SYS_CHECKMEM(x) \ do { \ @@ -157,6 +169,37 @@ int Sys_CreatePath (const char *path); */ char *Sys_ExpandSquiggle (const char *path); -//@} +/** Open a newly created file with a guaranteed unique name. -#endif // __sys_h + Uniqueness is guaranteed by adding a numeric sequence between the \a + prefix and \a suffix, with a minium of \a mindigits numeric characters + (with any required leading 0s to expand the number to \a mindigits). + + The created file has read and write permissions as modified by the OS, + and the handle can be bothe written and read. + + \param name dstring into which the name will be generated. Any + existing contents will be lost. If an error occurs, + \a name will be set to the error string. + \param prefix This includes the path to the file and any file name + prefix. The numeric sequence will be appended directly + to the prefix with no directory separator. + \param suffix Optional tail to be appended after the numeric sequence, + usually the file extension. A dot is not added + automatically, it is up to the caller to supply one. NULL + and an empty string are equivalent. + \param mindigits The minimum number of digits to include in the + generated file name. The sequence number will be padded + with 0s in order to meet this menimum. Overflow will + simply produce longer numeric sequence sub-strings. + \return File handle to the newly created file, or a negative + value if an error occured (the negative error code). + Suitable for use with read, write, fdopen, Qdopen, etc. + \note It is the caller's responsibility to close the file. +*/ +int Sys_UniqueFile (struct dstring_s *name, const char *prefix, + const char *suffix, int mindigits); + +///@} + +#endif//__QF_sys_h diff --git a/include/QF/sys_developer.h b/include/QF/sys_developer.h new file mode 100644 index 000000000..073f753d8 --- /dev/null +++ b/include/QF/sys_developer.h @@ -0,0 +1,54 @@ +/* + sys_developer.h + + Developer flags + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef SYS_DEVELOPER +#define SYS_DEVELOPER(developer) +#endif + +SYS_DEVELOPER (warn) +SYS_DEVELOPER (cache) +SYS_DEVELOPER (hunk) +SYS_DEVELOPER (zone) +SYS_DEVELOPER (vid) +SYS_DEVELOPER (input) +SYS_DEVELOPER (fs_nf) +SYS_DEVELOPER (fs_f) +SYS_DEVELOPER (fs) +SYS_DEVELOPER (net) +SYS_DEVELOPER (rua_resolve) +SYS_DEVELOPER (rua_obj) +SYS_DEVELOPER (rua_msg) +SYS_DEVELOPER (snd) +SYS_DEVELOPER (glt) +SYS_DEVELOPER (glsl) +SYS_DEVELOPER (skin) +SYS_DEVELOPER (model) +SYS_DEVELOPER (lighting) +SYS_DEVELOPER (vulkan) +SYS_DEVELOPER (vulkan_parse) + +#undef SYS_DEVELOPER diff --git a/include/QF/teamplay.h b/include/QF/teamplay.h index b0b1c01da..0f03ff5e9 100644 --- a/include/QF/teamplay.h +++ b/include/QF/teamplay.h @@ -25,12 +25,12 @@ */ -#ifndef __teamplay_h -#define __teamplay_h +#ifndef __QF_teamplay_h +#define __QF_teamplay_h -extern struct cvar_s *cl_parsesay; -extern struct cvar_s *cl_nofake; -extern struct cvar_s *cl_freply; +extern int cl_parsesay; +extern int cl_nofake; +extern float cl_freply; typedef const char *(*ffunc_t) (char *args); typedef struct freply_s { @@ -43,8 +43,8 @@ void Team_Init_Cvars (void); void Team_BestWeaponImpulse (void); void Team_Dead (void); void Team_NewMap (void); -const char *Team_ParseSay (const char *); +const char *Team_ParseSay (struct dstring_s *buf, const char *); void Locs_Init (void); void Team_ParseChat (const char *string); void Team_ResetTimers (void); -#endif // __teamplay_h +#endif//__QF_teamplay_h diff --git a/include/QF/tga.h b/include/QF/tga.h index 1d0bc8c25..a5a133da3 100644 --- a/include/QF/tga.h +++ b/include/QF/tga.h @@ -25,8 +25,8 @@ */ -#ifndef __tga_h -#define __tga_h +#ifndef __QF_tga_h +#define __QF_tga_h #include "QF/qtypes.h" #include "QF/quakeio.h" @@ -44,6 +44,17 @@ # endif #endif +typedef enum { + targa_colormap = 1, + targa_truecolor = 2, + targa_greyscale = 3, + targa_colormap_rle = 9, + targa_truecolor_rle = 10, + targa_greyscale_rle = 11, + + targa_max_image_type = 15 +} TargaImageType; + typedef struct _TargaHeader { unsigned char id_length; // __attribute__((packed)); unsigned char colormap_type; // __attribute__((packed)); @@ -67,7 +78,7 @@ typedef struct _TargaHeader { # endif #endif -struct tex_s *LoadTGA (QFile *fin); +struct tex_s *LoadTGA (QFile *fin, int load); void WriteTGAfile (const char *tganame, byte *data, int width, int height); -#endif // __tga_h +#endif//__QF_tga_h diff --git a/include/QF/ui/canvas.h b/include/QF/ui/canvas.h new file mode 100644 index 000000000..a4d8f9552 --- /dev/null +++ b/include/QF/ui/canvas.h @@ -0,0 +1,111 @@ +/* + canvas.h + + Integration of 2d and 3d objects + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/12/12 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_scene_canvas_h +#define __QF_scene_canvas_h + +#include "QF/ecs.h" +#include "QF/ui/view.h" + +enum { + canvas_update, + canvas_updateonce, + canvas_tile, + canvas_pic, + canvas_fitpic, // pic is scaled to fit the view + canvas_subpic, + canvas_cachepic, + canvas_fill, + canvas_charbuff, + canvas_func, + canvas_outline, + canvas_lateupdate, + + // last so deleting an entity removes the grouped components first + canvas_canvas, + + canvas_comp_count +}; + +typedef struct canvas_s { + uint32_t range[canvas_comp_count]; +} canvas_t; + +extern const struct component_s canvas_components[canvas_comp_count]; + +typedef struct canvas_system_s { + ecs_registry_t *reg; + uint32_t base; + uint32_t view_base; + uint32_t text_base; +} canvas_system_t; + +struct view_s; +struct view_pos_s; + +typedef void (*canvas_update_f) (struct view_s); +typedef void (*canvas_func_f) (struct view_pos_s, struct view_pos_s); + +typedef struct canvas_subpic_s { + struct qpic_s *pic; + uint32_t x, y; + uint32_t w, h; +} canvas_subpic_t; + +#define CANVASINLINE GNU89INLINE inline + +void Canvas_InitSys (canvas_system_t *canvas_sys, ecs_registry_t *reg); +void Canvas_AddToEntity (canvas_system_t canvas_sys, uint32_t ent); +uint32_t Canvas_New (canvas_system_t canvas_sys); +void Canvas_Draw (canvas_system_t canvas_sys); +void Canvas_SortComponentPool (canvas_system_t canvas_sys, uint32_t ent, + uint32_t component); +void Canvas_SetLen (canvas_system_t canvas_sys, view_pos_t len); +CANVASINLINE view_t Canvas_GetRootView (canvas_system_t canvas_sys, + uint32_t ent); + +#undef CANVASINLINE +#ifndef IMPLEMENT_CANVAS_Funcs +#define CANVASINLINE GNU89INLINE inline +#else +#define CANVASINLINE VISIBLE +#endif + +CANVASINLINE +view_t +Canvas_GetRootView (canvas_system_t canvas_sys, uint32_t ent) +{ + ecs_system_t viewsys = { canvas_sys.reg, canvas_sys.view_base }; + return View_FromEntity (viewsys, ent); +} + +#undef CANVASINLINE + +#endif//__QF_scene_canvas_h diff --git a/include/QF/ui/font.h b/include/QF/ui/font.h new file mode 100644 index 000000000..6ea183d6d --- /dev/null +++ b/include/QF/ui/font.h @@ -0,0 +1,61 @@ +/* + font.h + + Font management + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/8/26 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_ui_font_h +#define __QF_ui_font_h + +#include +#include FT_FREETYPE_H + +#include "QF/ui/vrect.h" +#include "QF/simd/types.h" + +#include "r_scrap.h" + +typedef struct fontent_s { + uint32_t id; +} fontent_t; + +typedef struct font_s { + void *font_resource; + FT_Face face; + rscrap_t scrap; + byte *scrap_bitmap; + FT_Long num_glyphs; + vrect_t *glyph_rects; + vec2i_t *glyph_bearings; + uint32_t fontid; +} font_t; + +void Font_Init (void); +void Font_Free (font_t *font); +font_t *Font_Load (QFile *font_file, int size); + +#endif//__QF_ui_font_h diff --git a/include/QF/ui/inputline.h b/include/QF/ui/inputline.h new file mode 100644 index 000000000..c98774501 --- /dev/null +++ b/include/QF/ui/inputline.h @@ -0,0 +1,60 @@ +/* + inputline.h + + Input line support + + Copyright (C) 2001 Bill Currie + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_ui_inputline_h +#define __QF_ui_inputline_h + +#include + +typedef struct inputline_s +{ + char **lines; // array of lines for input history + int num_lines; // number of lines in arry. 1 == no history + size_t line_size; // space available in each line. includes \0 + char prompt_char; // char placed at the beginning of the line + int edit_line; // current line being edited + int history_line; // current history line + size_t linepos; // cursor position within the current edit line + size_t scroll; // beginning of displayed line + size_t width; // viewable width for horizontal scrolling + const char *line; + void *user_data; // eg: window pointer + void (*complete)(struct inputline_s *); // tab key pressed + void (*enter)(struct inputline_s *); // enter key pressed + void (*draw)(struct inputline_s *); // draw input line to screen + + int x, y; // coordinates depend on display + int cursor; // is the cursor active (drawn?) +} inputline_t; + +inputline_t *Con_CreateInputLine (int lines, int lsize, char prompt); +void Con_DestroyInputLine (inputline_t *inputline); +void Con_ClearTyping (inputline_t *il, int save); +void Con_ProcessInputLine (inputline_t *il, int ch); + +#endif//__QF_ui_inputline_h diff --git a/include/QF/ui/passage.h b/include/QF/ui/passage.h new file mode 100644 index 000000000..ef23dd2f4 --- /dev/null +++ b/include/QF/ui/passage.h @@ -0,0 +1,76 @@ +/* + passage.h + + Text passage formatting. + + Copyright (C) 2022 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_ui_passage_h +#define __QF_ui_passage_h + +#include + +#include "QF/ecs.h" + +/** \defgroup passage Text passages + \ingroup utils +*/ +///@{ + +enum { + passage_href, + + passage_comp_count +}; + +extern const component_t passage_components[passage_comp_count]; + +enum { + passage_type_text_obj, + + passage_type_count +}; + +typedef struct psg_text_s { + /// beginning of text for this segment relative to passage_t.text + uint32_t text; + /// length of text segment in bytes rather than chars as text may be utf-8 + uint32_t size; +} psg_text_t; + +typedef struct passage_s { + const char *text; ///< Not owned by passage + + struct ecs_registry_s *reg; ///< Owning ECS registry + uint32_t comp_base; ///< Passage base component + struct hierarchy_s *hierarchy; ///< hierarchy of text objects +} passage_t; + +void Passage_ParseText (passage_t *passage, const char *text); +passage_t *Passage_New (ecs_system_t passage_sys); +void Passage_Delete (passage_t *passage); +int Passage_IsSpace (const char *text) __attribute__((pure)); + +///@} + +#endif//__QF_ui_passage_h diff --git a/include/QF/ui/text.h b/include/QF/ui/text.h new file mode 100644 index 000000000..33ea226e1 --- /dev/null +++ b/include/QF/ui/text.h @@ -0,0 +1,119 @@ +/* + text.h + + Text management + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/9/4 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_ui_text_h +#define __QF_ui_text_h + +#include +#include + +#include "QF/darray.h" + +#include "QF/ecs.h" + +// These can be converted to hb_direction_t simply by oring with 4 +typedef enum { + text_right_down, // left to right, then down horizontal text + text_left_down, // right to left, then down horizontal text + text_down_right, // top to bottom, then right vertical text + text_up_right, // bottom to top, then right vertical text + text_right_up, // left to right, then up horizontal text + text_left_up, // right to left, then up horizontal text + text_down_left, // top to bottom, then left vertical text + text_up_left, // bottom to top, then left vertical text +} text_dir_e; + +typedef struct script_component_s { + hb_language_t language; + hb_script_t script; + text_dir_e direction; +} script_component_t; + +typedef struct glyphobj_s { + int glyphid; + int x, y; + int fontid; +} glyphobj_t; + +typedef struct glyphref_s { + uint32_t start; + uint32_t count; +} glyphref_t; + +typedef struct glyphset_s { + glyphobj_t *glyphs; + uint32_t count; +} glyphset_t; + +enum { + // all the glyphs in a passage. Always on only the root view of the passage. + text_passage_glyphs, + // glyphs for a single text object + text_glyphs, + // text_script, text_font and text_features on the passage root object set + // the defaults for all text objects in the passage. The settings can be + // overridden at the paragraph level or individiual text object level by + // adding the appropriate component to that text object. + // script settings for the text object + text_script, + // font id for the text object + text_font, + // harfbuzz font features for the text object + text_features, + + text_comp_count +}; + +extern const component_t text_components[text_comp_count]; + +typedef struct featureset_s DARRAY_TYPE (hb_feature_t) featureset_t; + +extern hb_feature_t LigatureOff; +extern hb_feature_t LigatureOn; +extern hb_feature_t KerningOff; +extern hb_feature_t KerningOn; +extern hb_feature_t CligOff; +extern hb_feature_t CligOn; + +struct font_s; +struct passage_s; + +struct view_s Text_View (ecs_system_t viewsys, + struct font_s *font, struct passage_s *passage); +void Text_SetScript (ecs_system_t textsys, uint32_t textid, + const char *lang, hb_script_t script, text_dir_e dir); +void Text_SetFont (ecs_system_t textsys, uint32_t textid, + struct font_s *font); +void Text_SetFeatures (ecs_system_t textsys, uint32_t textid, + featureset_t *features); +void Text_AddFeature (ecs_system_t textsys, uint32_t textid, + hb_feature_t feature); + +#endif//__QF_ui_text_h diff --git a/include/QF/ui/txtbuffer.h b/include/QF/ui/txtbuffer.h new file mode 100644 index 000000000..ee3f66d61 --- /dev/null +++ b/include/QF/ui/txtbuffer.h @@ -0,0 +1,112 @@ +/* + txtbuffer.h + + Text buffer for edit or scrollback + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_ui_txtbuffer_h +#define __QF_ui_txtbuffer_h + +#include +#include + +/** \defgroup txtbuffer Text buffers + \ingroup utils +*/ +///@{ + +/// must be a power of 2 +#define TXTBUFFER_GROW 0x4000 + +/** Text buffer implementing efficient editing. +*/ +typedef struct txtbuffer_s { + struct txtbuffer_s *next; + char *text; + size_t textSize; ///< amount of text in the buffer + size_t bufferSize; ///< current capacity of the buffer + size_t gapOffset; ///< beginning of the gap + size_t gapSize; ///< size of gap. gapSize + textSize == bufferSize +} txtbuffer_t; + +/** Create a new, empty buffer. +*/ +txtbuffer_t *TextBuffer_Create (void); + +/** Destroy a buffer, freeing all resources connected to it. + \param buffer The buffer to destroy. +*/ +void TextBuffer_Destroy (txtbuffer_t *buffer); + +/** Open a gap for writing at the specified offset. + + Text after the offset is moved to be after the opened gap. + The buffer is resized as necessary. + + \param buffer The buffer to be updated + \param offset The offset in the buffer at which to insert the text block + \param text_len The size of the gap to be opened + \return Pointr to beginning of gap if successful, 0 if failure + (offset not valid or out of memory) +*/ +char *TextBuffer_OpenGap (txtbuffer_t *buffer, size_t offset, + size_t text_len); + +/** Insert a block of text at the specified offset. + + Text after the offset is moved to be after the inserted block of text. + The buffer is resized as necessary. + nul characters are not significant in that they do not mark the end of + the text to be inserted. + + \param buffer The buffer to be updated + \param offset The offset in the buffer at which to insert the text block + \param text The text block to be inserted. May contain nul ('\0') + characters. + \param text_len The number of characters to insert. + \return 1 for success, 0 for failure (offset not valid or out + of memory) +*/ +int TextBuffer_InsertAt (txtbuffer_t *buffer, size_t offset, + const char *text, size_t text_len); + +/** Delete a block of text from the buffer. + + The buffer is not resized. Rather, its capacity before resizing is require + is increased. + + \param buffer The buffer to be updated + \param offset The offset of the beginning of the text block to be deleted + \param len The amount of characters to be deleted. Values larger than + the amount of text in the buffer beyond the beginning of + block are truncated to the amount of remaining text. Thus + using excessivly large values sets the offset to be the + end of the buffer. + \return 1 for success, 0 for failure (offset not valid) +*/ +int TextBuffer_DeleteAt (txtbuffer_t *buffer, size_t offset, size_t len); + +///@} + +#endif//__QF_ui_txtbuffer_h diff --git a/include/QF/ui/view.h b/include/QF/ui/view.h new file mode 100644 index 000000000..3a39ee95b --- /dev/null +++ b/include/QF/ui/view.h @@ -0,0 +1,410 @@ +/* + view.h + + console view object + + Copyright (C) 2003 Bill Currie + + Author: Bill Currie + Date: 2003/5/5 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __QF_ui_view_h +#define __QF_ui_view_h + +#include "QF/ecs.h" + +/** \defgroup console_view Console View Objects + \ingroup console +*/ +///@{ + +/** Control the positioning of a view within its parent. The directions are + the standard compass rose (north, east, south, west in clockwise order) + with north at the top. + + The origin of the view is taken to be the corresponding point on the edge + of the view (eg, southeast is bottom right), or the view's center for + center gravity. When the relative coordinates of the view are (0,0), the + view's origin is placed on the parent view's gravity point using the view's + gravity (\em not the parent view's gravity). That is, the parent view's + gravity has no effect on the view's position within the parent view. + + The gravity also affects the direction the view moves as the relative + coordinates of the view change. + + No checking is done to ensure the view stays within the parent, or that the + view is smaller than the parent. This is by design. It is up to the drawing + callbacks to do any necessary clipping. +*/ +typedef enum { + grav_center, ///< +ve X right, +ve Y down, -X left, -ve Y up + grav_north, ///< +ve X right, +ve Y down, -X left, -ve Y up + grav_northeast, ///< +ve X left, +ve Y down, -X right, -ve Y up + grav_east, ///< +ve X left, +ve Y down, -X right, -ve Y up + grav_southeast, ///< +ve X left, +ve Y up, -X right, -ve Y down + grav_south, ///< +ve X right, +ve Y up, -X left, -ve Y down + grav_southwest, ///< +ve X right, +ve Y up, -X left, -ve Y down + grav_west, ///< +ve X right, +ve Y down, -X left, -ve Y up + grav_northwest, ///< +ve X right, +ve Y down, -X left, -ve Y up + grav_flow, ///< controlled by view_flow +} grav_t; + +extern struct exprtype_s grav_t_type; + +typedef struct view_pos_s { + int x; + int y; +} view_pos_t; + +typedef struct viewcont_s { + grav_t gravity; ///< The gravity of the view. + unsigned visible:1; ///< If false, view_draw() skips this view. + unsigned resize_x:1; ///< If true, view's width follows parent's. + unsigned resize_y:1; ///< If true, view's height follows parent's. + unsigned bol_suppress:1; ///< If true, view_flow skips at start of line. + unsigned flow_size:1; ///< If true, view's size is adjusted to flow. +} viewcont_t; + +enum { + view_href, + + view_comp_count +}; + +extern const struct component_s view_components[view_comp_count]; + +// components in the view hierarchy +enum { + /// Coordinates of view's origin relative to parent's gravity point. + view_pos, + /// Size of the view. + view_len, + /** Absolute coordinates of the top left (northwest) corner of the view. + Set interally. + */ + view_abs, + /** Coordinates of the top left (northwest) corner of the view relative to + the parent view's top left corner. Set internally. + */ + view_rel, + view_oldlen, + view_control, + view_modified, + view_onresize, + view_onmove, + + view_type_count +}; + +/** The view object. +*/ +typedef struct view_s { + ecs_registry_t *reg; + uint32_t id; + uint32_t comp; +} view_t; + +#define nullview ((view_t) {}) + +typedef void (*view_resize_f) (view_t view, view_pos_t len); +typedef void (*view_move_f) (view_t view, view_pos_t abs); + +#define VIEWINLINE GNU89INLINE inline + +VIEWINLINE view_t View_FromEntity (ecs_system_t viewsys, uint32_t ent); +view_t View_New (ecs_system_t viewsys, view_t parent); +view_t View_AddToEntity (uint32_t ent, ecs_system_t viewsys, view_t parent); +VIEWINLINE void View_Delete (view_t view); +void View_SetParent (view_t view, view_t parent); +void View_UpdateHierarchy (view_t view); + +void view_flow_right_down (view_t view, view_pos_t len); +void view_flow_right_up (view_t view, view_pos_t len); +void view_flow_left_down (view_t view, view_pos_t len); +void view_flow_left_up (view_t view, view_pos_t len); +void view_flow_down_right (view_t view, view_pos_t len); +void view_flow_up_right (view_t view, view_pos_t len); +void view_flow_down_left (view_t view, view_pos_t len); +void view_flow_up_left (view_t view, view_pos_t len); + +VIEWINLINE hierref_t *View_GetRef (view_t view); +VIEWINLINE int View_Valid (view_t view); + +VIEWINLINE view_t View_GetParent (view_t view); +VIEWINLINE uint32_t View_ChildCount (view_t view); +VIEWINLINE view_t View_GetChild (view_t view, uint32_t index); + +VIEWINLINE void View_SetPos (view_t view, int x, int y); +VIEWINLINE view_pos_t View_GetPos (view_t view); +VIEWINLINE view_pos_t View_GetAbs (view_t view); +VIEWINLINE view_pos_t View_GetRel (view_t view); +VIEWINLINE void View_SetLen (view_t view, int x, int y); +VIEWINLINE view_pos_t View_GetLen (view_t view); +VIEWINLINE viewcont_t* View_Control (view_t view); +VIEWINLINE void View_SetGravity (view_t view, grav_t grav); +VIEWINLINE grav_t View_GetGravity (view_t view); +VIEWINLINE void View_SetVisible (view_t view, int visible); +VIEWINLINE int View_GetVisible (view_t view); +VIEWINLINE void View_SetResize (view_t view, int resize_x, int resize_y); +VIEWINLINE void View_SetOnResize (view_t view, view_resize_f onresize); +VIEWINLINE void View_SetOnMove (view_t view, view_move_f onmove); + +#undef VIEWINLINE +#ifndef IMPLEMENT_VIEW_Funcs +#define VIEWINLINE GNU89INLINE inline +#else +#define VIEWINLINE VISIBLE +#endif + +VIEWINLINE +view_t +View_FromEntity (ecs_system_t viewsys, uint32_t ent) +{ + return (view_t) { + .id = ent, + .reg = viewsys.reg, + .comp = viewsys.base + view_href, + }; +} + +VIEWINLINE +hierref_t * +View_GetRef (view_t view) +{ + return Ent_GetComponent (view.id, view.comp, view.reg); +} + +VIEWINLINE +int +View_Valid (view_t view) +{ + return view.reg && ECS_EntValid (view.id, view.reg); +} + +VIEWINLINE +void +View_Delete (view_t view) +{ + __auto_type ref = *View_GetRef (view); + Hierarchy_RemoveHierarchy (ref.hierarchy, ref.index, 1); + if (!ref.hierarchy->num_objects) { + Hierarchy_Delete (ref.hierarchy); + } +} + +VIEWINLINE +view_t +View_GetParent (view_t view) +{ + __auto_type ref = View_GetRef (view); + if (ref->index == 0) { + return nullview; + } + hierarchy_t *h = ref->hierarchy; + return (view_t) { + .reg = view.reg, + .id = h->ent[h->parentIndex[ref->index]], + .comp = view.comp, + }; +} + +VIEWINLINE +uint32_t +View_ChildCount (view_t view) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + return h->childCount[ref->index]; +} + +VIEWINLINE +view_t +View_GetChild (view_t view, uint32_t childIndex) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + if (childIndex >= h->childCount[ref->index]) { + return nullview; + } + return (view_t) { + .reg = view.reg, + .id = h->ent[h->childIndex[ref->index] + childIndex], + .comp = view.comp, + }; +} + + +VIEWINLINE +void +View_SetPos (view_t view, int x, int y) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + view_pos_t *pos = h->components[view_pos]; + byte *modified = h->components[view_modified]; + pos[ref->index] = (view_pos_t) { x, y }; + modified[ref->index] |= 1; +} + +VIEWINLINE +view_pos_t +View_GetPos (view_t view) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + view_pos_t *pos = h->components[view_pos]; + return pos[ref->index]; +} + +VIEWINLINE +view_pos_t +View_GetAbs (view_t view) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + view_pos_t *abs = h->components[view_abs]; + return abs[ref->index]; +} + +VIEWINLINE +view_pos_t +View_GetRel (view_t view) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + view_pos_t *rel = h->components[view_rel]; + return rel[ref->index]; +} + +VIEWINLINE +void +View_SetLen (view_t view, int x, int y) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + view_pos_t *len = h->components[view_len]; + view_pos_t *oldlen = h->components[view_oldlen]; + byte *modified = h->components[view_modified]; + if (!(modified[ref->index] & 2)) { + oldlen[ref->index] = len[ref->index]; + } + len[ref->index] = (view_pos_t) { x, y }; + modified[ref->index] |= 2; +} + +VIEWINLINE +view_pos_t +View_GetLen (view_t view) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + view_pos_t *len = h->components[view_len]; + return len[ref->index]; +} + +VIEWINLINE +viewcont_t * +View_Control (view_t view) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + viewcont_t *cont = h->components[view_control]; + return &cont[ref->index]; +} + +VIEWINLINE +void +View_SetGravity (view_t view, grav_t grav) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + viewcont_t *cont = h->components[view_control]; + byte *modified = h->components[view_modified]; + cont[ref->index].gravity = grav; + modified[ref->index] |= 1; +} + +VIEWINLINE +grav_t +View_GetGravity (view_t view) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + viewcont_t *cont = h->components[view_control]; + return cont[ref->index].gravity; +} + +VIEWINLINE +void +View_SetVisible (view_t view, int visible) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + viewcont_t *cont = h->components[view_control]; + cont[ref->index].visible = !!visible; +} + +VIEWINLINE +int +View_GetVisible (view_t view) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + viewcont_t *cont = h->components[view_control]; + return cont[ref->index].visible; +} + +VIEWINLINE +void +View_SetResize (view_t view, int resize_x, int resize_y) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + viewcont_t *cont = h->components[view_control]; + cont[ref->index].resize_x = resize_x; + cont[ref->index].resize_y = resize_y; +} + +VIEWINLINE +void +View_SetOnResize (view_t view, view_resize_f onresize) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + view_resize_f *resize = h->components[view_onresize]; + resize[ref->index] = onresize; +} + +VIEWINLINE +void +View_SetOnMove (view_t view, view_move_f onmove) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + view_move_f *move = h->components[view_onmove]; + move[ref->index] = onmove; +} + +///@} + +#endif//__QF_ui_view_h diff --git a/include/QF/vrect.h b/include/QF/ui/vrect.h similarity index 81% rename from include/QF/vrect.h rename to include/QF/ui/vrect.h index 4c0cba107..c05bc51f9 100644 --- a/include/QF/vrect.h +++ b/include/QF/ui/vrect.h @@ -28,8 +28,8 @@ */ -#ifndef __QF_vrect_h -#define __QF_vrect_h +#ifndef __QF_ui_vrect_h +#define __QF_ui_vrect_h typedef struct vrect_s { int x; @@ -100,7 +100,7 @@ vrect_t *VRect_Intersect (const vrect_t *r1, const vrect_t *r2); vrect_t *VRect_HSplit (const vrect_t *r, int y); /** Return two rectangles representing the portions of \a r to the left and - right of \a y. + right of \a x. One of the returned rectangles may be empty. Use VRect_IsEmpty to check. The first rectangle represents the portion of \a r that is to the left of @@ -111,7 +111,7 @@ vrect_t *VRect_HSplit (const vrect_t *r, int y); \param r The rectangle to split. \param x The vertical line by which \a r will be split. \return The two linked rectangles representing the portions to the - left and right of \a y. The returned pointer points to the + left and right of \a x. The returned pointer points to the first (left) rectangle, which links to the second (right) rectangle. \note It is the caller's responsibility to delete the returned @@ -165,4 +165,27 @@ vrect_t *VRect_Union (const vrect_t *r1, const vrect_t *r2); */ vrect_t *VRect_Merge (const vrect_t *r1, const vrect_t *r2); -#endif//__QF_vrect_h +/** Return up to three rectangles resulting from carving out the upper-left + rectangle of the specified width and height. + + The first rectangle in the list will be the part of the supplied rectangle + that is covered by \a width and \a height (if either parameter is larger + than the supplied rectangle, then the returned rectangle will have the same + size as the supplied rectangle in that dimension). The other rectangles, + if there are any, will be the remainder of the supplied rectangle such that + the areas are min-max (to avoid creating long narrow rectangles). + + \param rect The rectangle to carve. + \param width Width of the sub-rectangle carved from \a rect. + \param height Height of the sub-rectangle carved from \a rect. + \return The carved-out sub-rectangle, plus up to two rectangles + representing the remainder of \a rect, with splits such + that the two rectangles are both minimum and maximum area. + The larger of the two remainder rectangles will allways be + last. + \note It is the caller's responsibility to delete the returned + rectangles. +*/ +vrect_t *VRect_SubRect (const vrect_t *rect, int width, int height); + +#endif//__QF_ui_vrect_h diff --git a/include/QF/va.h b/include/QF/va.h index 6a5e54465..2d2974e05 100644 --- a/include/QF/va.h +++ b/include/QF/va.h @@ -26,19 +26,70 @@ */ -#ifndef __va_h -#define __va_h +#ifndef __QF_va_h +#define __QF_va_h /** \addtogroup misc Formatted printing. */ -//@{ +///@{ -// does a varargs printf into a temp buffer -char *va(const char *format, ...) __attribute__((format(printf,1,2))); -// does a varargs printf into a malloced buffer -char *nva(const char *format, ...) __attribute__((format(printf,1,2))); +/** Opaque context for va so it can have per-thread data. + */ +typedef struct va_ctx_s va_ctx_t; -//@} +/** Create a va context with the specified number of buffers. + * + * Having multiple buffers allows va to be used in short chains. + * + * \param buffers The number of buffers to create in the context. va() will + * cycle through the buffers for each call. + * \return Pointer to the context. Pass to va() for thread-safe usage. + */ +va_ctx_t *va_create_context (int buffers); -#endif // __va_h +/** Destroy a va context. + * + * \param ctx The context to be destroyed. Must have been created by + * va_create_context(). + * \note Any pointers to strings returned by va() using the context + * referenced by \a ctx will become invalid as the backing + * memory for the strings will have been freed. + */ +void va_destroy_context (va_ctx_t *ctx); + +/** Does a varargs printf into a private buffer. + * + * \param ctx Context used for storing the private buffer such that va + * can be used in a multi-threaded environment. If null then + * a static context is used, in which case va is NOT + * thread-safe. + * \param format Standard printf() format string. + * \return Pointer to the beginning of the output string. The memory + * for the returned string is owned by the context pointed to + * by \a ctx. + * \note The static context is created with 4 buffers, so va (0,...) + * can be used to produce mildly complex output (eg, 3 calls + * to va sent to a 4th) with a reduced risk of strings being + * trampled. + */ +const char *va(va_ctx_t *ctx, const char *format, ...) __attribute__((format(PRINTF,2,3))); + +/** Does a varargs printf into a malloced buffer. + * + * Combines the effect of strdup and sprintf, but in a safe manner. Essentially + * the equivalent of strdup (va (ctx, format, ...)); + * + * \param format Standard printf() format string. + * \return Pointer to the beginning of the output string. The caller + * is responsible for freeing the memory holding the string. + * \note As nva() creates a new buffer every time it is called, it + * is thread-safe and there is no risk of the string being + * trampled. In addition, it does not use va(), so combining + * nva() with va() is safe. + */ +char *nva(const char *format, ...) __attribute__((format(PRINTF,1,2))); + +///@} + +#endif//__QF_va_h diff --git a/include/QF/ver_check.h b/include/QF/ver_check.h index 5d8c1061c..eaa4d9391 100644 --- a/include/QF/ver_check.h +++ b/include/QF/ver_check.h @@ -25,8 +25,8 @@ */ -#ifndef __ver_check_h_ -#define __ver_check_h_ +#ifndef __QF_ver_check_h +#define __QF_ver_check_h /** \defgroup utils Utils */ @@ -34,7 +34,7 @@ /** \defgroup misc Miscellaneous functions \ingroup utils */ -//@{ +///@{ /* ver_compare @@ -45,6 +45,6 @@ */ int ver_compare (const char *, const char *); -//@} +///@} -#endif // __ver_check_h_ +#endif//__QF_ver_check_h diff --git a/include/QF/vid.h b/include/QF/vid.h index 70298fa8b..7f15de0e8 100644 --- a/include/QF/vid.h +++ b/include/QF/vid.h @@ -25,60 +25,45 @@ */ -#ifndef __vid_h_ -#define __vid_h_ +#ifndef __QF_vid_h +#define __QF_vid_h +#include "QF/listener.h" #include "QF/qtypes.h" -#include "QF/vrect.h" #define VID_CBITS 6 #define VID_GRADES (1 << VID_CBITS) typedef struct { - qboolean initialized; - qboolean is8bit; - void *buffer; // invisible buffer - short *zbuffer; - void *surfcache; + bool initialized; + bool is8bit; byte *gammatable; // 256 - byte *basepal; // 256 * 3 + const byte *basepal; // 256 * 3 byte *palette; // 256 * 3 + byte *palette32; // 256 * 4 includes alpha byte *colormap8; // 256 * VID_GRADES size unsigned short *colormap16; // 256 * VID_GRADES size unsigned int *colormap32; // 256 * VID_GRADES size int fullbright; // index of first fullbright color - int rowbytes; // may be > width if displayed in a window - int width; - int height; - float aspect; // width / height -- < 1 is taller than wide + unsigned width; + unsigned height; int numpages; - qboolean recalc_refdef; // if true, recalc vid-based stuff - qboolean cshift_changed; - quat_t cshift_color; - void *conbuffer; - int conrowbytes; - int conwidth; - int conheight; - byte *direct; // direct drawing to framebuffer, if not - // NULL - int (*surf_cache_size)(int width, int height); - void (*flush_caches)(void); - void (*init_caches)(void *cache, int size); - void (*do_screen_buffer)(void); - void (*set_palette)(const byte *palette); + bool recalc_refdef; // if true, recalc vid-based stuff + struct vid_internal_s *vid_internal; - // gl stuff - void (*load_gl)(void); - void (*init_gl)(void); - void *(*get_proc_address)(const char *name, qboolean crit); - void (*end_rendering)(void); + struct viddef_listener_set_s *onPaletteChanged; + struct viddef_listener_set_s *onVidResize; } viddef_t; +typedef struct viddef_listener_set_s LISTENER_SET_TYPE (viddef_t) + viddef_listener_set_t; +typedef void (*viddef_listener_t) (void *data, const viddef_t *viddef); + #define viddef (*r_data->vid) extern unsigned int d_8to24table[256]; //FIXME nq/qw uses -extern qboolean vid_gamma_avail; +extern bool vid_gamma_avail; void VID_Init_Cvars (void); @@ -86,7 +71,14 @@ void VID_Init_Cvars (void); // the palette data will go away after the call, so it must be copied off if // the video driver will need it again void VID_Init (byte *palette, byte *colormap); -void VID_Shutdown (void); +void VID_SetPalette (byte *palette, byte *colormap); void VID_SetCaption (const char *text); +void VID_ClearMemory (void); -#endif // __vid_h_ +void VID_OnPaletteChange_AddListener (viddef_listener_t listener, void *data); +void VID_OnPaletteChange_RemoveListener (viddef_listener_t listener, + void *data); +void VID_OnVidResize_AddListener (viddef_listener_t listener, void *data); +void VID_OnVidResize_RemoveListener (viddef_listener_t listener, void *data); + +#endif//__QF_vid_h diff --git a/include/QF/view.h b/include/QF/view.h deleted file mode 100644 index ecb2ea920..000000000 --- a/include/QF/view.h +++ /dev/null @@ -1,220 +0,0 @@ -/* - view.h - - console view object - - Copyright (C) 2003 Bill Currie - - Author: Bill Currie - Date: 2003/5/5 - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ - -#ifndef __qf_view_h -#define __qf_view_h - -/** \defgroup console_view Console View Objects - \ingroup console -*/ -//@{ - -/** Control the positioning of a view within its parent. The directions are - the standard compass rose (north, east, south, west in clockwise order) - with north at the top. - - The origin of the view is taken to be the point corresponding point on the - edge of the view (eg, southeast is bottom right), or the view's center for - center gravity. When the relative coordinates of the view are (0,0), the - view's origin is placed on the parent view's gravity point using the view's - gravity (\em not the parent view's gravity). That is, the parent view's - gravity has no effect on the view's position within the parent view. - - The gravity also affects the direction the view moves as the relative - coordinates of the view change. - - No checking is done to ensure the view stays within the parent, or that the - view is smaller than the parent. This is by design. It is up to the drawing - callbacks to do any necessary clipping. -*/ -typedef enum { - grav_center, ///< +ve X right, +ve Y down, -X left, -ve Y up - grav_north, ///< +ve X right, +ve Y down, -X left, -ve Y up - grav_northeast, ///< +ve X left, +ve Y down, -X right, -ve Y up - grav_east, ///< +ve X left, +ve Y down, -X right, -ve Y up - grav_southeast, ///< +ve X left, +ve Y up, -X right, -ve Y down - grav_south, ///< +ve X right, +ve Y up, -X left, -ve Y down - grav_southwest, ///< +ve X right, +ve Y up, -X left, -ve Y down - grav_west, ///< +ve X right, +ve Y down, -X left, -ve Y up - grav_northwest, ///< +ve X right, +ve Y down, -X left, -ve Y up -} grav_t; - -/** The view object. -*/ -typedef struct view_s view_t; -struct view_s { - /// Coordinates of view's origin relative to parent's gravity point. - //@{ - int xpos, ypos; - //@} - /// Size of the view. - //@{ - int xlen, ylen; - //@} - /** Absolute coordinates of the top left (northwest) corner of the view. - Set interally. - */ - //@{ - int xabs, yabs; - //@} - /** Coordinates of the top left (northwest) corner of the view relative to - the parent view's top left corner. Set internally. - */ - //@{ - int xrel, yrel; - //@} - grav_t gravity; ///< The gravity of the view. - view_t *parent; ///< The parent view. - view_t **children; ///< The child views. - int num_children; ///< Number of child views in view. - int max_children; ///< Size of children array. - /** Callback for drawing the view. defaults to view_draw(). if overridden, - the supplied callback should call view_draw() to draw any child views - unless the view is a leaf view. - - \note All coordinates are set appropriately before this is called. - - \param view This view. - */ - void (*draw)(view_t *view); - /** Callback for when the position and/or size of the view changes. Set - this if the underlying drawing system needs to take any action when - the view's geometry changes (eg, moving/resizing the window in curses). - - \note All coordinates are set appropriately before this is called. - - \param view This view. - */ - void (*setgeometry)(view_t *view); - /** User supplied data. Purely for external use. The view functions do not - touch this at all except view_new(), which just sets it to 0. - */ - void *data; - unsigned visible:1; ///< If false, view_draw() skips this view. - unsigned resize_x:1; ///< If true, view's width follows parent's. - unsigned resize_y:1; ///< If true, view's height follows parent's. -}; - -/** Create a new view. view_t::draw is set to view_draw() and the view is made - visible. All coordinates are set appropriately for the new view being a - root view. All other fields not set by the parameters are 0. - - \param xp The X coordinate of the view's origin. - \param yp The Y coordinate of the view's origin. - \param xl The width of the view. - \param yl The height of the view. - \param grav The gravity of the view. determines the view's origin and - its positioning within the view's parent. -*/ -view_t *view_new (int xp, int yp, int xl, int yl, grav_t grav); - -/** Insert a view into a parent view at the specified location. If \c pos is - negative, it is taken to be relative to the end of the parent's list of - views (view_insert (par, view, -1) is equivalent to view_add (par, view)). - \c pos is clipped to be within the correct range. - - The absolute X and Y coordianates of the inserted view and its child views - are updated based on the view's gravity. - - The position of the view within the parent view's child view list - determines the draw order (and thus Z order) of the view, with position 0 - being drawn first. - - \param par The parent view into which the view is to be inserted. - \param view The view to insert. - \param pos The position at which to insert the view into the parent. -*/ -void view_insert (view_t *par, view_t *view, int pos); - -/** Add a view to a parent view at the end of the parents view list. - - The absolute X and Y coordianates of the inserted view and its child views - are updated based on the view's gravity. - - The added view will be drawn last (and thus on top of earlier views). - - \param par The parent view to which the view is to be added. - \param view The view to add. -*/ -void view_add (view_t *par, view_t *view); - -/** Remove a view from its parent. - - \param par The parent view from which the view is to be removed. - \param view The view to remove. -*/ -void view_remove (view_t *par, view_t *view); - -/** Delete a view and all its child views. If the view has a parent, the view - will be removed from its parent. - - \param view The view to delete. -*/ -void view_delete (view_t *view); - -/** Draw the child views of a view. If a child view is not visible - (view_t::visible is 0), the child will be skipped. - - \note It is best to always use view_t::draw() to draw a view rather than - calling this directly. This function is really for the view's draw - callback to call to draw its sub-views. - - \param view The view of which to draw the children. -*/ -void view_draw (view_t *view); - -/** Change the size of a view. The view's children are also resized based on - their view_t::resize_x and view_t::resize_y flags. - - The absolute X and Y coorinates of the view are updated as necessary to - keep the coordinates of the view's origin correct relative to the view's - geometry. - - \param view The view to resize. - \param xl The new width of the view. - \param yl The new height of the view. -*/ -void view_resize (view_t *view, int xl, int yl); - -/** Chage the location of a view. - - The absolute X and Y coorinates of the view are updated as necessary to - keep the coordinates of the view's origin correct relative to the view's - geometry. - - \param view The view to move. - \param xp The new X coordinate of the view relative to its gravity. - \param yp The new Y coordinate of the view relative to its gravity. -*/ -void view_move (view_t *view, int xp, int yp); - -//@} - -#endif//__qf_view_h diff --git a/include/QF/wad.h b/include/QF/wad.h index 805a69925..2e6bb9c67 100644 --- a/include/QF/wad.h +++ b/include/QF/wad.h @@ -26,13 +26,13 @@ */ // wad.h -#ifndef _WAD_H -#define _WAD_H +#ifndef __QF_wad_h +#define __QF_wad_h /** \addtogroup wad Wad Files */ -//@{ +///@{ #include "QF/wadfile.h" @@ -46,6 +46,6 @@ void *W_GetLumpName (const char *name); void SwapPic (qpic_t *pic); -//@} +///@} -#endif // _WAD_H +#endif//__QF_wad_h diff --git a/include/QF/wadfile.h b/include/QF/wadfile.h index 79e5e2055..9e6c220f6 100644 --- a/include/QF/wadfile.h +++ b/include/QF/wadfile.h @@ -29,7 +29,7 @@ \ingroup utils Wadfile processing */ -//@{ +///@{ #ifndef __QF_wadfile_h #define __QF_wadfile_h @@ -102,6 +102,6 @@ int wad_add_data (wad_t *wad, const char *lumpname, byte type, const void *data, int bytes); lumpinfo_t *wad_find_lump (wad_t *wad, const char *filename); -//@} +///@} #endif//__QF_wadfile_h diff --git a/include/QF/winding.h b/include/QF/winding.h index 5ef3d8094..52058b6fd 100644 --- a/include/QF/winding.h +++ b/include/QF/winding.h @@ -26,7 +26,7 @@ /** \defgroup winding Winding Manipulation */ -//@{ +///@{ struct plane_s; @@ -114,7 +114,7 @@ winding_t *WindingVectors (const winding_t *w, int unit); on the font side of the plane, or NULL if the winding has been clipped away. */ -winding_t *ClipWinding (winding_t *in, struct plane_s *split, qboolean keepon); +winding_t *ClipWinding (winding_t *in, struct plane_s *split, bool keepon); /** Divide a winding by a plane, producing one or two windings. @@ -138,6 +138,6 @@ winding_t *ClipWinding (winding_t *in, struct plane_s *split, qboolean keepon); void DivideWinding (winding_t *in, struct plane_s *split, winding_t **front, winding_t **back); -//@} +///@} #endif//__QF_winding_h diff --git a/include/QF/zone.h b/include/QF/zone.h index 22c7bc19f..11c95b7f0 100644 --- a/include/QF/zone.h +++ b/include/QF/zone.h @@ -24,8 +24,10 @@ Boston, MA 02111-1307, USA */ -#ifndef __zone_h -#define __zone_h +#ifndef __QF_zone_h +#define __QF_zone_h + +#include "QF/qtypes.h" /** \defgroup zone Memory Management \ingroup utils @@ -88,34 +90,50 @@ ----- Bottom of Memory ----- */ -//@{ +///@{ typedef struct memzone_s memzone_t; +typedef struct memhunk_s memhunk_t; -void Memory_Init (void *buf, int size); +memhunk_t *Memory_Init (void *buf, size_t size); -void Z_ClearZone (memzone_t *zone, int size, int zone_offset, int ele_size); +void Z_ClearZone (memzone_t *zone, size_t size, size_t zone_offset, + size_t ele_size); +void Z_MemInfo (const memzone_t *zone, size_t *used, size_t *size); void Z_Free (memzone_t *zone, void *ptr); -void *Z_Malloc (memzone_t *zone, int size); // returns 0 filled memory -void *Z_TagMalloc (memzone_t *zone, int size, int tag); -void *Z_Realloc (memzone_t *zone, void *ptr, int size); +void *Z_Malloc (memzone_t *zone, size_t size); // returns 0 filled memory +void *Z_TagMalloc (memzone_t *zone, size_t size, int tag); +void *Z_Realloc (memzone_t *zone, void *ptr, size_t size); void Z_Print (memzone_t *zone); void Z_CheckHeap (memzone_t *zone); void Z_SetError (memzone_t *zone, void (*err) (void *data, const char *msg), void *data); -void Z_CheckPointer (const memzone_t *zone, const void *ptr, int size); +void Z_CheckPointer (const memzone_t *zone, const void *ptr, size_t size); +int Z_IncRetainCount (memzone_t *zone, void *ptr); +int Z_DecRetainCount (memzone_t *zone, void *ptr); +int Z_GetRetainCount (memzone_t *zone, void *ptr) __attribute__((pure)); +int Z_GetTag (memzone_t *zone, void *ptr) __attribute__((pure)); +void Z_SetTag (memzone_t *zone, void *ptr, int tag); -void *Hunk_Alloc (int size); // returns 0 filled memory -void *Hunk_AllocName (int size, const char *name); -int Hunk_LowMark (void); -void Hunk_FreeToLowMark (int mark); -void *Hunk_TempAlloc (int size); -void Hunk_Check (void); + + +memhunk_t *Hunk_Init (void *buf, size_t size); +void Hunk_Print (memhunk_t *hunk, bool all); +void Hunk_Check (memhunk_t *hunk); +void *Hunk_RawAlloc (memhunk_t *hunk, size_t size) __attribute__((nonnull(1))); +void *Hunk_RawAllocName (memhunk_t *hunk, size_t size, const char *name) __attribute__((nonnull(1))); +void *Hunk_Alloc (memhunk_t *hunk, size_t size); // returns 0 filled memory +void *Hunk_AllocName (memhunk_t *hunk, size_t size, const char *name); +size_t Hunk_LowMark (memhunk_t *hunk) __attribute__((pure)); +void Hunk_RawFreeToLowMark (memhunk_t *hunk, size_t mark) __attribute__((nonnull(1))); +void Hunk_FreeToLowMark (memhunk_t *hunk, size_t mark); +void *Hunk_TempAlloc (memhunk_t *hunk, size_t size); +int Hunk_PointerIsValid (memhunk_t *hunk, void *ptr) __attribute__((pure)); struct cache_user_s; -typedef void *(*cache_allocator_t) (struct cache_user_s *c, int size, const char *name); +typedef void *(*cache_allocator_t) (struct cache_user_s *c, size_t size, const char *name); typedef void (*cache_loader_t) (void *object, cache_allocator_t allocator); typedef struct cache_user_s { void *data; @@ -129,7 +147,7 @@ void *Cache_Check (cache_user_t *c); // if present, otherwise returns NULL void Cache_Free (cache_user_t *c); -void *Cache_Alloc (cache_user_t *c, int size, const char *name); +void *Cache_Alloc (cache_user_t *c, size_t size, const char *name); // Returns NULL if all purgable data was tossed and there still // wasn't enough room. void Cache_Report (void); @@ -138,8 +156,8 @@ void Cache_Remove (cache_user_t *c); void *Cache_TryGet (cache_user_t *c); void *Cache_Get (cache_user_t *c); void Cache_Release (cache_user_t *c); -int Cache_ReadLock (cache_user_t *c); +int Cache_ReadLock (cache_user_t *c) __attribute__((pure)); -//@} +///@} -#endif // __zone_h +#endif//__QF_zone_h diff --git a/include/alsa_funcs_list.h b/include/alsa_funcs_list.h index fadedad4b..af7fcb4be 100644 --- a/include/alsa_funcs_list.h +++ b/include/alsa_funcs_list.h @@ -49,7 +49,11 @@ QF_ALSA_NEED (int, snd_pcm_hw_params_get_period_size, (const snd_pcm_hw_params_t QF_ALSA_NEED (int, snd_pcm_hw_params_set_access, (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t access)) QF_ALSA_NEED (int, snd_pcm_hw_params_set_period_size_near, (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir)) QF_ALSA_NEED (int, snd_pcm_hw_params_set_rate_near, (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir)) +QF_ALSA_NEED (int, snd_pcm_hw_params_set_rate, (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir)) +QF_ALSA_NEED (int, snd_pcm_hw_params_get_rate, (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir)) #endif +QF_ALSA_NEED (int, snd_pcm_prepare, (snd_pcm_t *pcm)) +QF_ALSA_NEED (int, snd_pcm_resume, (snd_pcm_t *pcm)) QF_ALSA_NEED (int, snd_pcm_hw_params_set_channels, (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val)) QF_ALSA_NEED (int, snd_pcm_hw_params_set_format, (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val)) QF_ALSA_NEED (size_t, snd_pcm_hw_params_sizeof, (void)) @@ -67,6 +71,11 @@ QF_ALSA_NEED (int, snd_pcm_sw_params_set_stop_threshold, (snd_pcm_t *pcm, snd_pc QF_ALSA_NEED (size_t, snd_pcm_sw_params_sizeof, (void)) QF_ALSA_NEED (const char *, snd_strerror, (int errnum)) +QF_ALSA_NEED (int, snd_async_add_pcm_handler, (snd_async_handler_t **handler, snd_pcm_t *pcm, snd_async_callback_t callback, void *private_data)) +QF_ALSA_NEED (snd_pcm_t *, snd_async_handler_get_pcm, (snd_async_handler_t *handler)) +QF_ALSA_NEED (void *, snd_async_handler_get_callback_private, (snd_async_handler_t *handler)) +QF_ALSA_NEED (int, snd_async_del_handler, (snd_async_handler_t *handler)) + #ifdef UNDEF_QF_ALSA_NEED #undef QF_ALSA_NEED #undef UNDEF_QF_ALSA_NEED diff --git a/include/asm_draw.h b/include/asm_draw.h index ec6d99283..50f720c63 100644 --- a/include/asm_draw.h +++ b/include/asm_draw.h @@ -42,14 +42,14 @@ #define espan_t_v 4 #define espan_t_count 8 #define espan_t_pnext 12 -#define espan_t_size 16 +#define espan_t_size 16 // sizeof(espan_t) // sspan_t structure // !!! if this is changed, it must be changed in d_local.h too !!! #define sspan_t_u 0 #define sspan_t_v 4 #define sspan_t_count 8 -#define sspan_t_size 12 +#define sspan_t_size 12 // sizeof(sspan_t) // spanpackage_t structure // !!! if this is changed, it must be changed in d_polyset.c too !!! @@ -61,7 +61,7 @@ #define spanpackage_t_tfrac 20 #define spanpackage_t_light 24 #define spanpackage_t_zi 28 -#define spanpackage_t_size 32 +#define spanpackage_t_size 32 // sizeof(spanpackage_t) // edge_t structure // !!! if this is changed, it must be changed in r_shared.h too !!! @@ -73,7 +73,7 @@ #define et_nextremove 20 #define et_nearzi 24 #define et_owner 28 -#define et_size 32 +#define et_size 32 // sizeof(edge_t) // surf_t structure // !!! if this is changed, it must be changed in r_shared.h too !!! @@ -93,7 +93,7 @@ #define st_d_zistepu 48 #define st_d_zistepv 52 #define st_pad 56 -#define st_size 64 +#define st_size 64 // sizeof (surf_t) // clipplane_t structure // !!! if this is changed, it must be changed in r_local.h too !!! @@ -103,45 +103,38 @@ #define cp_leftedge 20 #define cp_rightedge 21 #define cp_reserved 22 -#define cp_size 24 +#define cp_size 24 // sizeof (clipplane_t) // medge_t structure // !!! if this is changed, it must be changed in model.h too !!! #define me_v 0 #define me_cachededgeoffset 8 -#define me_size 12 +#define me_size 12 // sizeof (medge_t) // mvertex_t structure // !!! if this is changed, it must be changed in model.h too !!! #define mv_position 0 -#define mv_size 12 +#define mv_size 12 // sizeof (mvertex_t) // refdef_t structure // !!! if this is changed, it must be changed in render.h too !!! -#define rd_vrect 0 -#define rd_aliasvrect 20 -#define rd_vrectright 40 -#define rd_vrectbottom 44 -#define rd_aliasvrectright 48 -#define rd_aliasvrectbottom 52 -#define rd_vrectrightedge 56 -#define rd_fvrectx 60 -#define rd_fvrecty 64 -#define rd_fvrectx_adj 68 -#define rd_fvrecty_adj 72 -#define rd_vrect_x_adj_shift20 76 -#define rd_vrectright_adj_shift20 80 -#define rd_fvrectright_adj 84 -#define rd_fvrectbottom_adj 88 -#define rd_fvrectright 92 -#define rd_fvrectbottom 96 -#define rd_horizontalFieldOfView 100 -#define rd_xOrigin 104 -#define rd_yOrigin 108 -#define rd_vieworg 112 -#define rd_viewangles 124 -#define rd_ambientlight 136 -#define rd_size 140 +#define rd_fvrectx_adj 0 +#define rd_fvrecty_adj 4 +#define rd_fvrectright_adj 8 +#define rd_fvrectbottom_adj 12 +#define rd_aliasvrectleft 16 +#define rd_aliasvrecttop 20 +#define rd_aliasvrectright 24 +#define rd_aliasvrectbottom 28 +#define rd_vrectright 32 +#define rd_vrectbottom 36 +#define rd_vrectx_adj_shift20 40 +#define rd_vrectright_adj_shift20 44 +#define rd_fvrectx 48 +#define rd_fvrecty 52 +#define rd_fvrectright 56 +#define rd_fvrectbottom 60 +#define rd_size 64 // sizeof (refdef_t) FIXME make true // mtriangle_t structure // !!! if this is changed, it must be changed in model.h too !!! diff --git a/include/bsearch.h b/include/bsearch.h new file mode 100644 index 000000000..db03d85ea --- /dev/null +++ b/include/bsearch.h @@ -0,0 +1,41 @@ +/* + bsearch.h + + Reentrant bsearch for systems that don't have it + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef bsearch_h +#define bsearch_h + +#include + +#ifndef __compar_d_fn_t_defined +#define __compar_d_fn_t_defined +typedef int (*__compar_d_fn_t)(const void *, const void *, void *); +#endif + +void *QF_bsearch_r(const void *key, const void *base, size_t nmemb, size_t size, + __compar_d_fn_t cmp, void *arg); + +#endif// quicksort_h diff --git a/include/client/chase.h b/include/client/chase.h new file mode 100644 index 000000000..91e12cd5a --- /dev/null +++ b/include/client/chase.h @@ -0,0 +1,49 @@ +/* + chase.h + + @description@ + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __client_chase_h +#define __client_chase_h + +#include "QF/math/vector.h" +#include "QF/simd/vec4f.h" + +typedef struct chasestate_s { + struct model_s *worldmodel; + struct viewstate_s *viewstate; + vec4f_t camera_origin; + vec4f_t player_origin; + vec3_t camera_angles; + vec3_t player_angles; +} chasestate_t; + +extern int chase_active; + +void Chase_Init_Cvars (void); +void Chase_Reset (void); +void Chase_Update (chasestate_t *cs); + +#endif // __client_chase_h diff --git a/include/client/effects.h b/include/client/effects.h new file mode 100644 index 000000000..45e103410 --- /dev/null +++ b/include/client/effects.h @@ -0,0 +1,47 @@ +/* + effects.h + + Effect management + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/3/11 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __client_effects_h +#define __client_effects_h + +#include "QF/simd/types.h" + +struct entity_s; +struct entity_state_s; + +void CL_NewDlight (int key, vec4f_t org, int effects, byte glow_size, + byte glow_color, double time); +void CL_ModelEffects (struct entity_s ent, int num, int glow_color, + double time); +void CL_EntityEffects (int num, struct entity_s ent, + struct entity_state_s *state, double time); +void CL_MuzzleFlash (vec4f_t position, vec4f_t fv, float zoffset, int num, + double time); + +#endif//__client_effects_h diff --git a/include/client/entities.h b/include/client/entities.h index ae046b8c4..c0c2912dd 100644 --- a/include/client/entities.h +++ b/include/client/entities.h @@ -31,18 +31,28 @@ #ifndef __client_entities_h #define __client_entities_h +#include "QF/darray.h" +#include "QF/msg.h" #include "QF/qtypes.h" +#include "QF/simd/types.h" + +typedef struct entitystateset_s DARRAY_TYPE (struct entity_state_s) + entitystateset_t; +extern entitystateset_t cl_static_entities; + // entity_state_t is the information conveyed from the server // in an update message typedef struct entity_state_s { int number; // edict index unsigned flags; // nolerp, etc - vec3_t origin; + vec4f_t origin; + vec4f_t velocity; vec3_t angles; - unsigned short modelindex; - unsigned short frame; + uint16_t modelindex; + uint16_t frame; + int weaponframe; int effects; byte colormap; byte skinnum; @@ -67,4 +77,8 @@ extern entstates_t qw_entstates; extern vec3_t ent_colormod[256]; +struct entity_s; +void CL_TransformEntity (struct entity_s ent, float scale, + const vec3_t angles, vec4f_t position); + #endif//__client_entities_h diff --git a/include/clview.h b/include/client/hud.h similarity index 53% rename from include/clview.h rename to include/client/hud.h index 7568ab23b..fdb8e8862 100644 --- a/include/clview.h +++ b/include/client/hud.h @@ -1,9 +1,10 @@ /* - clview.h + hud.h - (description) + Heads-up display Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2022 Bill Currie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -24,30 +25,32 @@ Boston, MA 02111-1307, USA */ -// view.h +#ifndef __client_hud_h +#define __client_hud_h -#ifndef __clview_h_ -#define __clview_h_ +struct view_s; -#include "QF/mathlib.h" +extern struct ecs_system_s hud_psgsys; -#define INFO_CSHIFT_BONUS (1 << 0) -#define INFO_CSHIFT_CONTENTS (1 << 1) -#define INFO_CSHIFT_DAMAGE (1 << 2) -#define INFO_CSHIFT_POWERUP (1 << 3) +extern uint32_t hud_canvas; +extern int hud_sb_lines; -void V_Init (void); -void V_Init_Cvars (void); -void V_RenderView (void); -float V_CalcRoll (const vec3_t angles, const vec3_t velocity); -void V_UpdatePalette (void); -void V_StartPitchDrift (void); -void V_StopPitchDrift (void); +extern int hud_sbar; +extern int hud_swap; +extern int hud_fps; +extern int hud_pl; +extern int hud_ping; +extern int hud_time; -void V_RenderView (void); -void V_UpdatePalette (void); -void V_Register (void); -void V_SetContentsColor (int contents); -void V_CalcBlend (void); +//root view of the hud canvas +extern struct view_s hud_canvas_view; -#endif // __clview_h_ +struct ecs_registry_s; +struct canvas_system_s; + +void HUD_Init (struct ecs_registry_s *reg); +void HUD_Init_Cvars (void); +void HUD_CreateCanvas (struct canvas_system_s canvas_sys); +void HUD_Calc_sb_lines (int view_size); + +#endif//__client_hud_h diff --git a/include/client/input.h b/include/client/input.h new file mode 100644 index 000000000..3cf9cee24 --- /dev/null +++ b/include/client/input.h @@ -0,0 +1,82 @@ +/* + input.h + + Client input handling + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __client_input_h_ +#define __client_input_h_ + +#include "QF/simd/vec4f.h" + +#include "QF/input.h" + +struct cbuf_s; + +extern float cl_upspeed; +extern float cl_forwardspeed; +extern float cl_backspeed; +extern float cl_sidespeed; + +extern float cl_movespeedkey; + +extern float cl_yawspeed; +extern float cl_pitchspeed; + +extern float cl_anglespeedkey; + +extern float m_pitch; +extern float m_yaw; +extern float m_forward; +extern float m_side; + +#define FORWARD 0 +#define SIDE 1 +#define UP 2 + +typedef struct movestate_s { + vec4f_t move; + vec4f_t angles; +} movestate_t; + +#define freelook (in_mlook.state & 1 || in_freelook) + +struct viewstate_s; + +void CL_OnFocusChange (void (*func) (int game)); +void CL_Input_BuildMove (float frametime, movestate_t *state, + struct viewstate_s *vs); +void CL_Input_Init (struct cbuf_s *cbuf); +void CL_Input_Init_Cvars (void); +void CL_Input_Activate (int in_game); + +extern in_axis_t in_move_forward, in_move_side, in_move_up; +extern in_axis_t in_move_pitch, in_move_yaw, in_move_roll; +extern in_axis_t in_cam_forward, in_cam_side, in_cam_up; +extern in_button_t in_left, in_right, in_forward, in_back; +extern in_button_t in_lookup, in_lookdown, in_moveleft, in_moveright; +extern in_button_t in_use, in_jump, in_attack; +extern in_button_t in_up, in_down; +extern in_button_t in_strafe, in_klook, in_speed, in_mlook; + +#endif // __client_input_h_ diff --git a/include/QF/locs.h b/include/client/locs.h similarity index 65% rename from include/QF/locs.h rename to include/client/locs.h index 32cb4ea0d..d18e2b0dd 100644 --- a/include/QF/locs.h +++ b/include/client/locs.h @@ -25,26 +25,28 @@ */ -#ifndef __locs_h -#define __locs_h +#ifndef __QF_locs_h +#define __QF_locs_h #include "QF/qtypes.h" +#include "QF/simd/types.h" typedef struct { - vec3_t loc; - char *name; + vec4f_t loc; + char *name; } location_t; -location_t *locs_find(const vec3_t target); -void locs_add (const vec3_t location, const char *name); -void locs_del (const vec3_t loc); -void locs_edit (const vec3_t loc, const char *desc); +location_t *locs_find(vec4f_t target) __attribute__((pure)); +void locs_add (vec4f_t location, const char *name); +void locs_del (vec4f_t loc); +void locs_edit (vec4f_t loc, const char *desc); void locs_load(const char *filename); -void locs_mark (const vec3_t loc, const char *desc); -int locs_nearest (const vec3_t loc); +void locs_mark (vec4f_t loc, const char *desc); +int locs_nearest (vec4f_t loc) __attribute__((pure)); void locs_reset (void); -void locs_save (const char *filename, qboolean gz); +void locs_save (const char *filename, bool gz); void map_to_loc (const char *mapname, char *filename); +void locs_draw (double time, vec4f_t simorg); -#endif // __locs_h +#endif//__QF_locs_h diff --git a/include/client/particles.h b/include/client/particles.h new file mode 100644 index 000000000..0b91f9daf --- /dev/null +++ b/include/client/particles.h @@ -0,0 +1,97 @@ +/* + particles.h + + Client particles handling + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __client_particles_h_ +#define __client_particles_h_ + +#include "QF/render.h" + +typedef enum { + pt_static, + pt_grav, + pt_slowgrav, + pt_fire, + pt_explode, + pt_explode2, + pt_blob, + pt_blob2, + pt_smoke, + pt_smokecloud, + pt_bloodcloud, + pt_fadespark, + pt_fadespark2, + pt_fallfade, + pt_fallfadespark, + pt_flame +} ptype_t; + +typedef struct cl_particle_funcs_s { + void (*RocketTrail) (vec4f_t start, vec4f_t end); + void (*GrenadeTrail) (vec4f_t start, vec4f_t end); + void (*BloodTrail) (vec4f_t start, vec4f_t end); + void (*SlightBloodTrail) (vec4f_t start, vec4f_t end); + void (*WizTrail) (vec4f_t start, vec4f_t end); + void (*FlameTrail) (vec4f_t start, vec4f_t end); + void (*VoorTrail) (vec4f_t start, vec4f_t end); + void (*GlowTrail) (vec4f_t start, vec4f_t end, int glow_color); + + void (*RunParticleEffect) (vec4f_t org, vec4f_t dir, int color, int count); + void (*BloodPuffEffect) (vec4f_t org, int count); + void (*GunshotEffect) (vec4f_t org, int count); + void (*LightningBloodEffect) (vec4f_t org); + void (*SpikeEffect) (vec4f_t org); + void (*KnightSpikeEffect) (vec4f_t org); + void (*SuperSpikeEffect) (vec4f_t org); + void (*WizSpikeEffect) (vec4f_t org); + + void (*BlobExplosion) (vec4f_t org); + void (*ParticleExplosion) (vec4f_t org); + void (*ParticleExplosion2) (vec4f_t org, int colorStart, int colorLength); + void (*LavaSplash) (vec4f_t org); + void (*TeleportSplash) (vec4f_t org); + void (*DarkFieldParticles) (vec4f_t org); + void (*EntityParticles) (vec4f_t org); + + void (*Particle_New) (ptype_t type, int texnum, vec4f_t org, + float scale, vec4f_t vel, float die, + int color, float alpha, float ramp); + void (*Particle_NewRandom) (ptype_t type, int texnum, vec4f_t org, + int org_fuzz, float scale, int vel_fuzz, + float die, int color, float alpha, + float ramp); +} cl_particle_funcs_t; + +extern cl_particle_funcs_t *clp_funcs; +extern float cl_frametime; +extern float cl_realtime; + +void CL_Particles_Init (void); +void CL_ParticlesGravity (float gravity); + +struct model_s; +void CL_LoadPointFile (const struct model_s *model); + +#endif // __client_particles_h_ diff --git a/include/sbar.h b/include/client/sbar.h similarity index 53% rename from include/sbar.h rename to include/client/sbar.h index 7fa9fbdd3..ba10c8872 100644 --- a/include/sbar.h +++ b/include/client/sbar.h @@ -28,31 +28,35 @@ // the status bar is redrawn only if something has changed, but if anything // does, the entire thing will be redrawn for the next vid.numpages frames. -#ifndef _SBAR_H -#define _SBAR_H +#ifndef __client_sbar_h +#define __client_sbar_h -#define SBAR_HEIGHT 24 +extern bool sbar_showscores; -extern int sb_lines; // scan lines to draw +struct player_info_s; +void Sbar_Init (int *stats, float *item_gettime); +void Sbar_SetPlayers (struct player_info_s *players, int maxplayers); +void Sbar_SetLevelName (const char *levelname, const char *servername); +void Sbar_SetPlayerNum (int playernum, int spectator); +void Sbar_SetAutotrack (int autotrack); +void Sbar_SetViewEntity (int viewentity); +void Sbar_SetTeamplay (int teamplay); +void Sbar_SetGameType (int gametype); +void Sbar_SetActive (int active); -void Sbar_Init (void); +void Sbar_Update (double time); +void Sbar_UpdatePings (void); +void Sbar_UpdatePL (int pl); +void Sbar_UpdateFrags (int playernum); +void Sbar_UpdateInfo (int playernum); +void Sbar_UpdateStats (int stat); +void Sbar_Damage (double time); -struct cvar_s; -void Sbar_DMO_Init_f (struct cvar_s *var); +void Sbar_Intermission (int mode, double completed_time); -void Sbar_Changed (void); -// call whenever any of the client stats represented on the sbar changes - -void Sbar_Draw (void); -// called every frame by screen - -void Sbar_IntermissionOverlay (void); -// called each frame after the level has been completed - -void Sbar_FinaleOverlay (void); void Sbar_DrawCenterPrint (void); void Sbar_CenterPrint (const char *str); -void Sbar_LogFrags (void); +void Sbar_LogFrags (double time); -#endif +#endif//__client_sbar_h diff --git a/include/client/screen.h b/include/client/screen.h new file mode 100644 index 000000000..1695834cb --- /dev/null +++ b/include/client/screen.h @@ -0,0 +1,11 @@ +#ifndef __client_screen_h +#define __client_screen_h + +extern struct view_s cl_screen_view; +extern struct canvas_system_s cl_canvas_sys; + +struct viewstate_s; +void CL_Init_Screen (void); +void CL_UpdateScreen (struct viewstate_s *vs); + +#endif//__client_screen_h diff --git a/include/client/state.h b/include/client/state.h new file mode 100644 index 000000000..46f0f8d88 --- /dev/null +++ b/include/client/state.h @@ -0,0 +1,62 @@ +/* + state.h + + client state + + Copyright (C) 2013 Bill Currie + + Author: Bill Currie + Date: 2013/01/31 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __client_state_h +#define __client_state_h + +#include "QF/qdefs.h" // FIXME for MAX_CL_STATS + +typedef struct player_info_s { + int userid; + struct info_s *userinfo; + + // scoreboard information + struct info_key_s *name; + struct info_key_s *team; + struct info_key_s *chat; + float entertime; + int frags; + int ping; + byte pl; + + // skin information + int topcolor; + int bottomcolor; + struct info_key_s *skinname; + struct skin_s *skin; + + entity_t flag_ent; + + int spectator; + int stats[MAX_CL_STATS]; // health, etc + int prevcount; +} player_info_t; + +#endif//__client_state_h diff --git a/include/client/temp_entities.h b/include/client/temp_entities.h new file mode 100644 index 000000000..62e828d88 --- /dev/null +++ b/include/client/temp_entities.h @@ -0,0 +1,120 @@ +/* + temp_entities.h + + Temporary entity management + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/3/10 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifndef __client_temp_entities_h +#define __client_temp_entities_h + +#include "QF/simd/vec4f.h" + +typedef enum TE_Effect { + TE_NoEffect, // for invalid nq/qw -> qf mapping + TE_Beam, // grappling hook beam + TE_Blood, // bullet hitting body + TE_Explosion, // rocket explosion (nq) + TE_Explosion1, // rocket explosion (qw) + TE_Explosion2, // color mapped explosion + TE_Explosion3, // Nehahra colored light explosion + TE_Gunshot1, // NQ gunshot (20 particles) + TE_Gunshot2, // QW gunshot (has particle count) + TE_KnightSpike, // spike hitting wall + TE_LavaSplash, + TE_Lightning1, // lightning bolts + TE_Lightning2, // lightning bolts + TE_Lightning3, // lightning bolts + TE_Lightning4, // Nehahra lightning + TE_LightningBlood, // lightning hitting body + TE_Spike, // spike hitting wall + TE_SuperSpike, // super spike hitting wall + TE_TarExplosion, // tarbaby explosion + TE_Teleport, + TE_WizSpike, // spike hitting wall +} TE_Effect; + +typedef enum TE_nqEffect { + TE_nqSpike, + TE_nqSuperSpike, + TE_nqGunshot, + TE_nqExplosion, + TE_nqTarExplosion, + TE_nqLightning1, + TE_nqLightning2, + TE_nqWizSpike, + TE_nqKnightSpike, + TE_nqLightning3, + TE_nqLavaSplash, + TE_nqTeleport, + TE_nqExplosion2, + TE_nqBeam, + TE_nqExplosion3 = 16, + TE_nqLightning4, +} TE_nqEffect; + +typedef enum TE_qwEffect { + TE_qwSpike, + TE_qwSuperSpike, + TE_qwGunshot, + TE_qwExplosion, + TE_qwTarExplosion, + TE_qwLightning1, + TE_qwLightning2, + TE_qwWizSpike, + TE_qwKnightSpike, + TE_qwLightning3, + TE_qwLavaSplash, + TE_qwTeleport, + TE_qwBlood, + TE_qwLightningBlood, + TE_qwExplosion2 = 16, + TE_qwBeam, +} TE_qwEffect; + +//FIXME find a better way to get this info from the parser +typedef struct TEntContext_s { + vec4f_t simorg; + int playerEntity; +} TEntContext_t; + +struct msg_s; +struct entity_s; + +void CL_TEnts_Init (void); +void CL_TEnts_Precache (void); +void CL_Init_Entity (struct entity_s ent); +void CL_ClearTEnts (void); +void CL_UpdateTEnts (double time, TEntContext_t *ctx); +void CL_ParseTEnt_nq (struct msg_s *net_message, double time, + TEntContext_t *ctx); +void CL_ParseTEnt_qw (struct msg_s *net_message, double time, + TEntContext_t *ctx); +void CL_ParseParticleEffect (struct msg_s *net_message); +void CL_ClearProjectiles (void); +void CL_ParseProjectiles (struct msg_s *net_message, bool nail2, + TEntContext_t *ctx); + +#endif//__client_temp_entities_h diff --git a/include/client/view.h b/include/client/view.h new file mode 100644 index 000000000..4915b77cc --- /dev/null +++ b/include/client/view.h @@ -0,0 +1,117 @@ +/* + view.h + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +// view.h + +#ifndef __client_view_h +#define __client_view_h + +#include "QF/mathlib.h" +#include "QF/simd/types.h" +#include "QF/scene/entity.h" + +typedef struct { + int destcolor[3]; + int percent; // 0-255 + double time; + int initialpct; +} cshift_t; + +#define CSHIFT_CONTENTS 0 +#define CSHIFT_DAMAGE 1 +#define CSHIFT_BONUS 2 +#define CSHIFT_POWERUP 3 +#define NUM_CSHIFTS 4 + +#define INFO_CSHIFT_BONUS (1 << 0) +#define INFO_CSHIFT_CONTENTS (1 << 1) +#define INFO_CSHIFT_DAMAGE (1 << 2) +#define INFO_CSHIFT_POWERUP (1 << 3) + +typedef struct viewstate_s { + vec4f_t player_origin; + vec3_t player_angles; + int chase; + vec4f_t movecmd; + vec4f_t velocity; + vec4f_t punchangle; + transform_t camera_transform; + double time; + double realtime; + double last_servermessage; + float frametime; + float height; + int weaponframe; + int onground; // -1 when in air + unsigned active:1; + unsigned loading:1; + unsigned watervis:1; + unsigned demoplayback:1; + unsigned drift_enabled:1; + unsigned voffs_enabled:1; + unsigned bob_enabled:1; + unsigned intermission:1; + unsigned decay_punchangle:1; + int force_cshifts; // bitfield of server enforced cshifts + uint32_t flags; + + int powerup_index; + cshift_t cshifts[NUM_CSHIFTS]; // Color shifts for damage, powerups + cshift_t prev_cshifts[NUM_CSHIFTS]; // and content types + quat_t cshift_color; + +// pitch drifting vars + float idealpitch; + float pitchvel; + bool nodrift; + float driftmove; + double laststop; + + struct model_s *weapon_model; + entity_t weapon_entity; + entity_t player_entity; + + struct chasestate_s *chasestate; +} viewstate_t; + +#define VF_DEAD 1 +#define VF_GIB 2 + +struct msg_s; + +void V_Init (viewstate_t *vs); +void V_Init_Cvars (void); +void V_RenderView (viewstate_t *vs); +float V_CalcRoll (const vec3_t angles, vec4f_t velocity); +void V_StartPitchDrift (viewstate_t *vs); +void V_StopPitchDrift (viewstate_t *vs); +void V_SetContentsColor (viewstate_t *vs, int contents); +void V_ParseDamage (struct msg_s *net_message, viewstate_t *vs); +void V_PrepBlend (viewstate_t *vs); + +extern bool noclip_anglehack; + +#endif // __client_view_h diff --git a/include/client/world.h b/include/client/world.h new file mode 100644 index 000000000..2e1f79107 --- /dev/null +++ b/include/client/world.h @@ -0,0 +1,71 @@ +/* + world.h + + Client world scene management + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/3/4 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __client_world_h +#define __client_world_h + +#include "QF/darray.h" +#include "QF/msg.h" +#include "QF/qtypes.h" + +#include "QF/simd/types.h" + +typedef struct modelset_s DARRAY_TYPE (struct model_s *) modelset_t; + +typedef struct worldscene_s { + struct scene_s *scene; + struct plitem_s *edicts; + struct plitem_s *worldspawn; + modelset_t models; +} worldscene_t; + +extern worldscene_t cl_world; + +struct msg_s; +struct entity_state_s; +struct lightingdata_s; + +void CL_World_Init (void); + +// PROTOCOL_FITZQUAKE -- flags for entity baseline messages +#define B_LARGEMODEL (1<<0) // modelindex is short instead of byte +#define B_LARGEFRAME (1<<1) // frame is short instead of byte +#define B_ALPHA (1<<2) // 1 byte, uses ENTALPHA_ENCODE, not sent if ENTALPHA_DEFAULT +void CL_ParseBaseline (struct msg_s *msg, struct entity_state_s *baseline, + int version); +/* + Static entities are non-interactive world objects like torches +*/ +void CL_ParseStatic (struct msg_s *msg, int version); +void CL_MapCfg (const char *mapname); +void CL_World_NewMap (const char *mapname, const char *skyname); +void CL_LoadLights (struct plitem_s *entities, struct scene_s *scene); + +#endif//__client_world_h diff --git a/include/compat.h b/include/compat.h index a3b8cab92..07c871667 100644 --- a/include/compat.h +++ b/include/compat.h @@ -94,9 +94,31 @@ size_t strnlen (const char *str, size_t len); # define strnlen Q_strnlen # define need_qstring_h #endif +#ifdef HAVE_STRNDUP +# ifndef HAVE_STRNDUP_PROTO +size_t strndup (const char *str, size_t len); +# endif +#else +# define strndup Q_strndup +# define need_qstring_h +#endif #ifdef need_qstring_h # include "qstring.h" #endif +#ifndef HAVE_BSEARCH_R +# include "bsearch.h" +# define bsearch_r QF_bsearch_r +#endif + +#ifndef HAVE_QSORT_R +# include "quicksort.h" +# define qsort_r _quicksort +#endif + +#ifndef HAVE_STRERROR_R +#define strerror_r(err,buf,len) strerror_s (buf, len, err) +#endif + #endif // __compat_h diff --git a/include/context_sdl.h b/include/context_sdl.h index b097587f1..f6f1dbbe7 100644 --- a/include/context_sdl.h +++ b/include/context_sdl.h @@ -30,7 +30,19 @@ #ifndef __context_sdl_h_ #define __context_sdl_h_ +#include + +extern SDL_Surface *sdl_screen; + void VID_SDL_GammaCheck (void); void SDL_Init_Cvars (void); +struct gl_ctx_s *SDL_GL_Context (void); +void SDL_GL_Init_Cvars (void); + +struct sw_ctx_s *SDL_SW_Context (void); +void SDL_SW_Init_Cvars (void); + +extern uint32_t sdl_flags; + #endif // __context_sdl_h_ diff --git a/include/context_win.h b/include/context_win.h new file mode 100644 index 000000000..513b0478a --- /dev/null +++ b/include/context_win.h @@ -0,0 +1,76 @@ +/* + context_win.h + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __context_win_h +#define __context_win_h + +#include "QF/qtypes.h" +#include "winquake.h" + +extern HWND win_mainwindow; +extern HDC win_maindc; +extern int win_palettized; +extern int win_canalttab; +extern DEVMODE win_gdevmode; +extern struct sw_ctx_s *win_sw_context; +extern int win_minimized; +extern int vid_ddraw; +extern int win_center_x, win_center_y; +extern RECT win_rect; + +LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +void Win_Activate (BOOL fActive, BOOL minimize); + +void Win_UnloadAllDrivers (void); +void Win_OpenDisplay (void); +void Win_CloseDisplay (void); +void Win_SetVidMode (int width, int height); +void Win_CreateWindow (int width, int height); +void Win_Init_Cvars (void); +void Win_UpdateWindowStatus (int x, int y); +void Win_SetCaption (const char *text); +bool Win_SetGamma (double gamma); + +struct vid_internal_s; +struct gl_ctx_s *Win_GL_Context (struct vid_internal_s *); +void Win_GL_Init_Cvars (void); + +struct sw_ctx_s *Win_SW_Context (struct vid_internal_s *); +void Win_SW_Init_Cvars (void); + +struct vulkan_ctx_s *Win_Vulkan_Context (struct vid_internal_s *); +void Win_Vulkan_Init_Cvars (void); + +void IN_UpdateClipCursor (void); +void IN_ShowMouse (void); +void IN_HideMouse (void); +void IN_ActivateMouse (void); +void IN_DeactivateMouse (void); + +#endif // __context_win_h diff --git a/include/context_x11.h b/include/context_x11.h index b513e587a..59e1e63f0 100644 --- a/include/context_x11.h +++ b/include/context_x11.h @@ -41,7 +41,7 @@ #define X11_KEY_MASK (KeyPressMask | KeyReleaseMask) #define X11_MOUSE_MASK (ButtonPressMask | ButtonReleaseMask \ | PointerMotionMask) -#define X11_FOCUS_MASK (FocusChangeMask | EnterWindowMask) +#define X11_FOCUS_MASK (FocusChangeMask | EnterWindowMask | LeaveWindowMask) #define X11_INPUT_MASK (X11_KEY_MASK | X11_MOUSE_MASK | X11_FOCUS_MASK) #define X11_MASK (X11_WINDOW_MASK | X11_FOCUS_MASK | PointerMotionMask) @@ -52,19 +52,22 @@ extern Window x_win; extern Colormap x_cmap; extern XVisualInfo *x_visinfo; extern int x_screen; +extern int x_width; +extern int x_height; extern int x_shmeventtype; extern Time x_time; extern Time x_mouse_time; -extern qboolean oktodraw; -extern qboolean x_have_focus; +extern bool oktodraw; +extern bool x_have_focus; -qboolean X11_AddEvent (int event, void (*event_handler)(XEvent *)); -qboolean X11_RemoveEvent (int event, void (*event_handler)(XEvent *)); -qboolean X11_SetGamma (double); +bool X11_AddEvent (int event, void (*event_handler)(XEvent *)); +bool X11_RemoveEvent (int event, void (*event_handler)(XEvent *)); +bool X11_SetGamma (double); void X11_CloseDisplay (void); void X11_CreateNullCursor (void); void X11_CreateWindow (int, int); void X11_ForceViewPort (void); +void X11_UpdateFullscreen (int fullscreen); void X11_Init_Cvars (void); void X11_OpenDisplay (void); void X11_ProcessEvent (void); @@ -77,4 +80,14 @@ void X11_SaveMouseAcceleration (void); void X11_RemoveMouseAcceleration (void); void X11_RestoreMouseAcceleration (void); +struct vid_internal_s; +struct gl_ctx_s *X11_GL_Context (struct vid_internal_s *); +void X11_GL_Init_Cvars (void); + +struct sw_ctx_s *X11_SW_Context (struct vid_internal_s *); +void X11_SW_Init_Cvars (void); + +struct vulkan_ctx_s *X11_Vulkan_Context (struct vid_internal_s *); +void X11_Vulkan_Init_Cvars (void); + #endif // __context_x11_h_ diff --git a/include/d_iface.h b/include/d_iface.h index df6336a40..d1fce5f53 100644 --- a/include/d_iface.h +++ b/include/d_iface.h @@ -56,41 +56,17 @@ typedef struct float zi; } emitpoint_t; -typedef enum { - part_tex_dot, - part_tex_spark, - part_tex_smoke, -} ptextype_t; - typedef struct particle_s particle_t; void R_LoadParticles (void); -qboolean R_CompileParticlePhysics (const char *name, const char *code); +bool R_CompileParticlePhysics (const char *name, const char *code); void R_RunParticlePhysics (particle_t *part); const union pt_phys_op_s *R_ParticlePhysics (const char *type); -qboolean R_AddParticlePhysicsFunction (const char *name, - qboolean (*func) (struct particle_s *, - void *), - void *data); +bool R_AddParticlePhysicsFunction (const char *name, + bool (*func) (struct particle_s *, void *), + void *data); extern const char particle_types[]; -// !!! if this is changed, it must be changed in d_ifacea.h too !!! -struct particle_s -{ -// driver-usable fields - vec3_t org; - int color; - float alpha; - ptextype_t tex; - float scale; -// drivers never touch the following fields - vec3_t vel; - float die; - float ramp; - const union pt_phys_op_s *physics; - particle_t *next; -}; - #define PARTICLE_Z_CLIP 8.0 typedef struct polyvert_s { @@ -136,7 +112,7 @@ typedef struct // if the driver wants to duplicate element [0] at // element [nump] to avoid dealing with wrapping mspriteframe_t *pspriteframe; - vec3_t vup, vright, vpn; // in worldspace + vec3_t vup, vright, vfwd; // in worldspace float nearzi; } spritedesc_t; @@ -147,25 +123,24 @@ typedef struct int color; } zpointdesc_t; -extern struct cvar_s *r_drawflat; -extern int r_framecount; // sequence # of current frame since Quake - // started -extern qboolean r_drawpolys; // 1 if driver wants clipped polygons - // rather than a span list -extern qboolean r_drawculledpolys; // 1 if driver wants clipped polygons that - // have been culled by the edge list -extern qboolean r_worldpolysbacktofront; // 1 if driver wants polygons - // delivered back to front rather - // than front to back -extern qboolean r_recursiveaffinetriangles; // true if a driver wants to use - // recursive triangular subdivison - // and vertex drawing via - // D_PolysetDrawFinalVerts() past - // a certain distance (normally - // used only by the software - // driver) -extern int r_pixbytes; -extern qboolean r_dowarp; +extern int r_drawflat; +extern int r_framecount; // sequence # of current frame since Quake + // started +extern bool r_drawpolys; // 1 if driver wants clipped polygons + // rather than a span list +extern bool r_drawculledpolys; // 1 if driver wants clipped polygons that + // have been culled by the edge list +extern bool r_worldpolysbacktofront; // 1 if driver wants polygons + // delivered back to front rather + // than front to back +extern bool r_recursiveaffinetriangles; // true if a driver wants to use + // recursive triangular subdivison + // and vertex drawing via + // D_PolysetDrawFinalVerts() past + // a certain distance (normally + // used only by the software + // driver) +extern bool r_dowarp; extern affinetridesc_t r_affinetridesc; extern spritedesc_t r_spritedesc; @@ -176,28 +151,26 @@ extern int d_con_indirect; // if 0, Quake will draw console directly // draw console via D_DrawRect. Must be // defined by driver -extern vec3_t r_pright, r_pup, r_ppn; +extern vec3_t r_pright, r_pup, r_ppn, r_porigin; -void D_Aff8Patch (void *pcolormap); +void D_Aff8Patch (const byte *pcolormap); void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height); -void D_DisableBackBufferAccess (void); void D_EndDirectRect (int x, int y, int width, int height); void D_PolysetDraw (void); void D_PolysetDrawFinalVerts (finalvert_t *fv, int numverts); void D_PolysetSetEdgeTable (void); void D_DrawParticle (particle_t *pparticle); void D_DrawPoly (void); -void D_DrawSprite (void); +void D_DrawSprite (const vec3_t relvieworg); void D_DrawSurfaces (void); void D_DrawZPoint (void); -void D_EnableBackBufferAccess (void); void D_Init (void); void D_Init_Cvars (void); void D_ViewChanged (void); void D_SetupFrame (void); void D_TurnZOn (void); -void D_WarpScreen (void); +void D_WarpScreen (framebuffer_t *src); void D_FillRect (vrect_t *vrect, int color); void D_DrawRect (void); @@ -218,7 +191,7 @@ extern byte *r_skysource; // !!! must be kept the same as in quakeasm.h !!! #define TRANSPARENT_COLOR 0xFF -extern void *acolormap; // FIXME: should go away +extern const byte *acolormap; // FIXME: should go away //=======================================================================// @@ -231,15 +204,15 @@ typedef struct msurface_t *surf; // description for surface to generate fixed8_t lightadj[MAXLIGHTMAPS]; // adjust for lightmap levels for dynamic lighting - texture_t *texture; // corrected for animating textures + struct texture_s *texture; // corrected for animating textures int surfmip; // mipmapped ratio of surface texels / world pixels int surfwidth; // in mipmapped texels int surfheight; // in mipmapped texels } drawsurf_t; extern drawsurf_t r_drawsurf; - -void R_DrawSurface (void); +struct transform_s; +void R_DrawSurface (uint32_t render_id); void R_GenTile (msurface_t *psurf, void *pdest); // !!! if this is changed, it must be changed in d_iface.h too !!! @@ -263,8 +236,8 @@ extern float r_skyspeed; extern float r_skytime; extern int c_surf; -extern vrect_t scr_vrect; -extern byte *r_warpbuffer; +struct draw_charbuffer_s; +void sw_Draw_CharBuffer (int x, int y, struct draw_charbuffer_s *buffer); #endif // _D_IFACE_H diff --git a/include/d_ifacea.h b/include/d_ifacea.h index fa61655e0..4f6118b76 100644 --- a/include/d_ifacea.h +++ b/include/d_ifacea.h @@ -50,19 +50,15 @@ // particle_t structure // !!! if this is changed, it must be changed in d_iface.h too !!! -// driver-usable fields -#define pt_org 0 -#define pt_color 12 -#define pt_alpha 16 -#define pt_tex 20 -#define pt_scale 24 -// drivers never touch the following fields -#define pt_vel 28 -#define pt_type 40 -#define pt_die 44 -#define pt_ramp 48 -#define pt_next 52 -#define pt_size 56 +#define pt_pos 0 +#define pt_vel 16 +#define pt_color 32 +#define pt_alpha 44 +#define pt_tex 48 +#define pt_ramp 52 +#define pt_scale 56 +#define pt_live 60 +#define pt_size 64 #define PARTICLE_Z_CLIP 8.0 diff --git a/include/d_local.h b/include/d_local.h index cc7c4591c..3a3594db2 100644 --- a/include/d_local.h +++ b/include/d_local.h @@ -59,16 +59,13 @@ typedef struct surfcache_s } surfcache_t; // !!! if this is changed, it must be changed in asm_draw.h too !!! -typedef struct sspan_s -{ +typedef struct sspan_s { int u, v, count; } sspan_t; -extern struct cvar_s *d_subdiv16; - extern float scale_for_mip; -extern qboolean d_roverwrapped; +extern bool d_roverwrapped; extern surfcache_t *sc_rover; extern surfcache_t *d_initial_rover; @@ -76,6 +73,8 @@ extern float d_sdivzstepu, d_tdivzstepu, d_zistepu; extern float d_sdivzstepv, d_tdivzstepv, d_zistepv; extern float d_sdivzorigin, d_tdivzorigin, d_ziorigin; +extern float d_skyoffs; + extern fixed16_t sadjust, tadjust; extern fixed16_t bbextents, bbextentt; @@ -92,17 +91,24 @@ void D_DrawSkyScans (struct espan_s *pspan); void R_ShowSubDiv (void); extern void (*prealspandrawer)(void); -surfcache_t *D_CacheSurface (msurface_t *surface, int miplevel); +struct entity_s; +surfcache_t *D_CacheSurface (uint32_t render_id, + msurface_t *surface, int miplevel); -extern int D_MipLevelForScale (float scale); +int D_MipLevelForScale (float scale) __attribute__((pure)); #ifdef USE_INTEL_ASM -extern void D_PolysetAff8Start (void); -extern void D_PolysetAff8End (void); +void D_PolysetAff8Start (void); +void D_PolysetAff8End (void); #endif -extern short *d_pzbuffer; -extern int d_zrowbytes, d_zwidth; +extern byte *d_viewbuffer; +extern int d_rowbytes; +extern unsigned d_height; + +extern short *d_zbuffer; +extern int d_zrowbytes; +extern unsigned d_zwidth; extern int *d_pscantable; extern int d_scantable[]; @@ -111,8 +117,6 @@ extern int d_vrectx, d_vrecty, d_vrectright_particle, d_vrectbottom_particle; extern int d_y_aspect_shift, d_pix_min, d_pix_max, d_pix_shift; -extern byte *d_viewbuffer; - extern short *zspantable[]; extern int d_minmip; diff --git a/include/dga_check.h b/include/dga_check.h index 53ab16fe1..26f5a7dfb 100644 --- a/include/dga_check.h +++ b/include/dga_check.h @@ -38,7 +38,11 @@ Check for the presence of the XFree86-DGA support in the X server */ -qboolean VID_CheckDGA (Display *, int *, int *, int *); +bool VID_CheckDGA (Display *, int *, int *, int *) +#ifndef HAVE_DGA // FIXME + __attribute__((const)) +#endif + ; /* @@ -46,6 +50,10 @@ qboolean VID_CheckDGA (Display *, int *, int *, int *); Check for the presence of the XFree86-VMode X server extension */ -qboolean VID_CheckVMode (Display *, int *, int *); +bool VID_CheckVMode (Display *, int *, int *) +#ifndef HAVE_DGA // FIXME + __attribute__((const)) +#endif + ; #endif // __dga_check_h_ diff --git a/include/evdev/hotplug.h b/include/evdev/hotplug.h new file mode 100644 index 000000000..d24b8cabd --- /dev/null +++ b/include/evdev/hotplug.h @@ -0,0 +1,14 @@ +#ifndef evdev_hotplug_h +#define evdev_hotplug_h + +int inputlib_hotplug_init(const char *path, + void (*created) (const char*), + void (*deleted) (const char *)); + +void inputlib_hotplug_close (void); + +int inputlib_hotplug_add_select (fd_set *fdset, int *maxfd); + +int inputlib_hotplug_check_select (fd_set *fdset); + +#endif//evdev_hotplug_h diff --git a/include/evdev/inputlib.h b/include/evdev/inputlib.h new file mode 100644 index 000000000..16f647311 --- /dev/null +++ b/include/evdev/inputlib.h @@ -0,0 +1,53 @@ +#ifndef evdev_inputlib_h +#define evdev_inputlib_h + +typedef struct { + int num; ///< The high-level index of the button. Always 0-N + int evnum; ///< The low-level index of the button. May be sparse + int state; ///< Current state of the button. +} button_t; + +typedef struct { + int num; ///< The high-level index of the axis. Always 0-N + int evnum; ///< The low-level index of the axis. May be sparse + int value; ///< Current value of the input axis. + // relative axes set these to 0 + int min; ///< Minimum value for the axis (usually constant). + int max; ///< Maximum value for the axis (usually constant). +} axis_t; + +typedef struct device_s { + struct device_s *next; + struct device_s **prev; + char *path; + char *name; + char *phys; + char *uniq; + int fd; + int max_button; + int *button_map; + int num_buttons; + button_t *buttons; + int max_abs_axis; + int *abs_axis_map; + int max_rel_axis; + int *rel_axis_map; + int num_abs_axes; + int num_rel_axes; + // includes both abs and rel axes, with abs first + int num_axes; + axis_t *axes; + int event_count; + + void *data; + void (*axis_event) (axis_t *axis, void *data); + void (*button_event) (button_t *button, void *data); +} device_t; + +void inputlib_add_select (fd_set *fdset, int *maxfd); +void inputlib_check_select (fd_set *fdset); +int inputlib_check_input (void); +void inputlib_close (void); +int inputlib_init (void (*dev_add) (device_t *), void (*dev_rem) (device_t *)); + +#endif//evdev_inputlib_h diff --git a/include/exp.h b/include/exp.h index a3c40a7ca..f4a46514e 100644 --- a/include/exp.h +++ b/include/exp.h @@ -80,7 +80,7 @@ typedef union token_u extern exp_error_t EXP_ERROR; -const char *EXP_GetErrorMsg (void); +const char *EXP_GetErrorMsg (void) __attribute__((pure)); token *EXP_ParseString (char *str); exp_error_t EXP_SimplifyTokens (token *chain); void EXP_RemoveToken (token *tok); diff --git a/include/fbset.h b/include/fbset.h index 82f2f9bd3..8dc8cbb87 100644 --- a/include/fbset.h +++ b/include/fbset.h @@ -1,7 +1,7 @@ /* * Linux Frame Buffer Device Configuration * - * © Copyright 1995-1998 by Geert Uytterhoeven + * © Copyright 1995-1998 by Geert Uytterhoeven * (Geert.Uytterhoeven@cs.kuleuven.ac.be) * * -------------------------------------------------------------------------- diff --git a/include/gamedefs.h b/include/gamedefs.h new file mode 100644 index 000000000..7659c349c --- /dev/null +++ b/include/gamedefs.h @@ -0,0 +1,117 @@ +/* + gamedefs.h + + Game specific definitions + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __gamedefs_h +#define __gamedefs_h + +#define MAX_PLAYERS 32 // maximum players supported by the engine (esp sbar) + +// stats are integers communicated to the client by the server +#define MAX_CL_STATS 32 +#define STAT_HEALTH 0 +#define STAT_FRAGS 1 +#define STAT_WEAPON 2 +#define STAT_AMMO 3 +#define STAT_ARMOR 4 +#define STAT_WEAPONFRAME 5 +#define STAT_SHELLS 6 +#define STAT_NAILS 7 +#define STAT_ROCKETS 8 +#define STAT_CELLS 9 +#define STAT_ACTIVEWEAPON 10 +#define STAT_TOTALSECRETS 11 +#define STAT_TOTALMONSTERS 12 +#define STAT_SECRETS 13 // bumped on client side by svc_foundsecret +#define STAT_MONSTERS 14 // bumped by svc_killedmonster +#define STAT_ITEMS 15 +#define STAT_VIEWHEIGHT 16 +#define STAT_FLYMODE 17 + +// stock defines +#define IT_SHOTGUN (1<<0) +#define IT_SUPER_SHOTGUN (1<<1) +#define IT_NAILGUN (1<<2) +#define IT_SUPER_NAILGUN (1<<3) +#define IT_GRENADE_LAUNCHER (1<<4) +#define IT_ROCKET_LAUNCHER (1<<5) +#define IT_LIGHTNING (1<<6) +#define IT_SUPER_LIGHTNING (1<<7) +#define IT_SHELLS (1<<8) +#define IT_NAILS (1<<9) +#define IT_ROCKETS (1<<10) +#define IT_CELLS (1<<11) +#define IT_AXE (1<<12) +#define IT_ARMOR1 (1<<13) +#define IT_ARMOR2 (1<<14) +#define IT_ARMOR3 (1<<15) +#define IT_SUPERHEALTH (1<<16) +#define IT_KEY1 (1<<17) +#define IT_KEY2 (1<<18) +#define IT_INVISIBILITY (1<<19) +#define IT_INVULNERABILITY (1<<20) +#define IT_SUIT (1<<21) +#define IT_QUAD (1<<22) +#define IT_SIGIL1 (1<<28) +#define IT_SIGIL2 (1<<29) +#define IT_SIGIL3 (1<<30) +#define IT_SIGIL4 (1<<31) + +//rogue changed and added defines +#define RIT_SHELLS (1<<7) +#define RIT_NAILS (1<<8) +#define RIT_ROCKETS (1<<9) +#define RIT_CELLS (1<<10) +#define RIT_AXE (1<<11) +#define RIT_LAVA_NAILGUN (1<<12) +#define RIT_LAVA_SUPER_NAILGUN (1<<13) +#define RIT_MULTI_GRENADE (1<<14) +#define RIT_MULTI_ROCKET (1<<15) +#define RIT_PLASMA_GUN (1<<16) +#define RIT_ARMOR1 (1<<23) +#define RIT_ARMOR2 (1<<24) +#define RIT_ARMOR3 (1<<25) +#define RIT_LAVA_NAILS (1<<26) +#define RIT_PLASMA_AMMO (1<<27) +#define RIT_MULTI_ROCKETS (1<<28) +#define RIT_SHIELD (1<<29) +#define RIT_ANTIGRAV (1<<30) +#define RIT_SUPERHEALTH (1<<31) + +//MED 01/04/97 added hipnotic defines +//hipnotic added defines +#define HIT_PROXIMITY_GUN_BIT 16 +#define HIT_MJOLNIR_BIT 7 +#define HIT_LASER_CANNON_BIT 23 +#define HIT_PROXIMITY_GUN (1< + + Author: Bill Currie + Date: 2021/11/21 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -25,13 +28,10 @@ */ -#ifndef __chase_h -#define __chase_h +#ifndef __in_x11_h +#define __in_x11_h -extern struct cvar_s *chase_active; +long IN_X11_Preinit (void); +void IN_X11_Postinit (void); -void Chase_Init_Cvars (void); -void Chase_Reset (void); -void Chase_Update (void); - -#endif // __chase_h +#endif//__in_x11_h diff --git a/include/mod_internal.h b/include/mod_internal.h index 7f6be3e4e..d643a31d8 100644 --- a/include/mod_internal.h +++ b/include/mod_internal.h @@ -1,55 +1,102 @@ #ifndef __mod_internal_h #define __mod_internal_h +#include "QF/darray.h" #include "QF/iqm.h" #include "QF/model.h" #include "QF/skin.h" #include "QF/plugin/vid_render.h" +typedef struct mod_alias_skin_s { + int skin_num; + int group_num; // -1 if not in an animated group + byte *texels; + maliasskindesc_t *skindesc; +} mod_alias_skin_t; + +typedef struct stvertset_s DARRAY_TYPE (stvert_t) stvertset_t; +typedef struct mtriangleset_s DARRAY_TYPE (mtriangle_t) mtriangleset_t; +typedef struct trivertxset_s DARRAY_TYPE (trivertx_t *) trivertxset_t; +typedef struct askinset_s DARRAY_TYPE (mod_alias_skin_t) askinset_t; + +typedef struct mod_alias_ctx_s { + aliashdr_t *header; + model_t *mod; + stvertset_t stverts; + mtriangleset_t triangles; + trivertxset_t poseverts; + askinset_t skins; + int aliasbboxmins[3]; + int aliasbboxmaxs[3]; +} mod_alias_ctx_t; + +typedef struct mod_sprite_ctx_s { + model_t *mod; + dsprite_t *dsprite; + msprite_t *sprite; + int numframes; + int *frame_numbers; + dspriteframe_t **dframes; ///< array of pointers to dframes (in) + mspriteframe_t ***frames; ///< array of pointers to mframe pointers (out) +} mod_sprite_ctx_t; + +int Mod_CalcFullbright (byte *out, const byte *in, size_t pixels); +int Mod_ClearFullbright (byte *out, const byte *in, size_t pixels); +void Mod_FloodFillSkin (byte *skin, int skinwidth, int skinheight); +//FIXME gl specific. rewrite to use above +int Mod_Fullbright (byte * skin, int width, int height, const char *name); + +void Mod_LoadBrushModel (model_t *mod, void *buffer); +void Mod_FindClipDepth (hull_t *hull); + +model_t *Mod_FindName (const char *name); +float RadiusFromBounds (const vec3_t mins, const vec3_t maxs) __attribute__((pure)); + +struct vulkan_ctx_s; + extern vid_model_funcs_t *m_funcs; -void gl_Mod_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, void *_m, +void gl_Mod_MakeAliasModelDisplayLists (mod_alias_ctx_t *alias_ctx, void *_m, int _s, int extra); -void *gl_Mod_LoadSkin (byte *skin, int skinsize, int snum, int gnum, - qboolean group, maliasskindesc_t *skindesc); -void gl_Mod_FinalizeAliasModel (model_t *m, aliashdr_t *hdr); -void gl_Mod_LoadExternalSkins (model_t *mod); +void gl_Mod_LoadAllSkins (mod_alias_ctx_t *alias_ctx); +void gl_Mod_FinalizeAliasModel (mod_alias_ctx_t *alias_ctx); +void gl_Mod_LoadExternalSkins (mod_alias_ctx_t *alias_ctx); void gl_Mod_IQMFinish (model_t *mod); -void glsl_Mod_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, +void glsl_Mod_MakeAliasModelDisplayLists (mod_alias_ctx_t *alias_ctx, void *_m, int _s, int extra); -void *glsl_Mod_LoadSkin (byte *skin, int skinsize, int snum, int gnum, - qboolean group, maliasskindesc_t *skindesc); -void glsl_Mod_FinalizeAliasModel (model_t *m, aliashdr_t *hdr); -void glsl_Mod_LoadExternalSkins (model_t *mod); +void glsl_Mod_LoadAllSkins (mod_alias_ctx_t *alias_ctx); +void glsl_Mod_FinalizeAliasModel (mod_alias_ctx_t *alias_ctx); +void glsl_Mod_LoadExternalSkins (mod_alias_ctx_t *alias_ctx); void glsl_Mod_IQMFinish (model_t *mod); -void sw_Mod_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, void *_m, +void sw_Mod_MakeAliasModelDisplayLists (mod_alias_ctx_t *alias_ctx, void *_m, int _s, int extra); -void *sw_Mod_LoadSkin (byte *skin, int skinsize, int snum, int gnum, - qboolean group, maliasskindesc_t *skindesc); -void sw_Mod_FinalizeAliasModel (model_t *m, aliashdr_t *hdr); -void sw_Mod_LoadExternalSkins (model_t *mod); +void sw_Mod_LoadAllSkins (mod_alias_ctx_t *alias_ctx); +void sw_Mod_FinalizeAliasModel (mod_alias_ctx_t *alias_ctx); void sw_Mod_IQMFinish (model_t *mod); -void gl_Mod_LoadExternalTextures (model_t *mod); -void gl_Mod_LoadLighting (bsp_t *bsp); -void gl_Mod_SubdivideSurface (msurface_t *fa); -void gl_Mod_ProcessTexture(texture_t *tx); +void gl_Mod_LoadLighting (model_t *mod, bsp_t *bsp); +void gl_Mod_SubdivideSurface (model_t *mod, msurface_t *fa); +struct texture_s; +void gl_Mod_ProcessTexture (model_t *mod, struct texture_s *tx); -void glsl_Mod_LoadExternalTextures (model_t *mod); -void glsl_Mod_LoadLighting (bsp_t *bsp); -void glsl_Mod_SubdivideSurface (msurface_t *fa); -void glsl_Mod_ProcessTexture(texture_t *tx); +void glsl_Mod_LoadLighting (model_t *mod, bsp_t *bsp); +void glsl_Mod_ProcessTexture (model_t *mod, struct texture_s *tx); -void sw_Mod_LoadExternalTextures (model_t *mod); -void sw_Mod_LoadLighting (bsp_t *bsp); -void sw_Mod_SubdivideSurface (msurface_t *fa); -void sw_Mod_ProcessTexture(texture_t *tx); +void sw_Mod_LoadLighting (model_t *mod, bsp_t *bsp); -void gl_Mod_SpriteLoadTexture (mspriteframe_t *pspriteframe, int framenum); -void glsl_Mod_SpriteLoadTexture (mspriteframe_t *pspriteframe, int framenum); -void sw_Mod_SpriteLoadTexture (mspriteframe_t *pspriteframe, int framenum); +void Vulkan_Mod_LoadLighting (model_t *mod, bsp_t *bsp, + struct vulkan_ctx_s *ctx); +void Vulkan_Mod_SubdivideSurface (model_t *mod, msurface_t *fa, + struct vulkan_ctx_s *ctx); +void Vulkan_Mod_ProcessTexture (model_t *mod, struct texture_s *tx, + struct vulkan_ctx_s *ctx); + +void Mod_LoadSpriteFrame (mspriteframe_t *frame, const dspriteframe_t *dframe); +void gl_Mod_SpriteLoadFrames (mod_sprite_ctx_t *sprite_ctx); +void glsl_Mod_SpriteLoadFrames (mod_sprite_ctx_t *sprite_ctx); +void sw_Mod_SpriteLoadFrames (mod_sprite_ctx_t *sprite_ctx); void Mod_LoadIQM (model_t *mod, void *buffer); void Mod_FreeIQM (iqm_t *iqm); @@ -59,9 +106,17 @@ void Mod_LoadAliasModel (model_t *mod, void *buffer, void Mod_LoadSpriteModel (model_t *mod, void *buffer); void Skin_Init (void); +void Skin_Shutdown (void); +void Skin_Free (skin_t *skin); skin_t *Skin_SetColormap (skin_t *skin, int cmap); skin_t *Skin_SetSkin (skin_t *skin, int cmap, const char *skinname); void Skin_SetTranslation (int cmap, int top, int bottom); +int Skin_CalcTopColors (byte *out, const byte *in, size_t pixels, int stride); +int Skin_CalcTopMask (byte *out, const byte *in, size_t pixels, int stride); +int Skin_CalcBottomColors(byte *out, const byte *in, size_t pixels, int stride); +int Skin_CalcBottomMask (byte *out, const byte *in, size_t pixels, int stride); +int Skin_ClearTopColors (byte *out, const byte *in, size_t pixels); +int Skin_ClearBottomColors (byte *out, const byte *in, size_t pixels); void sw_Skin_SetupSkin (skin_t *skin, int cmap); void sw_Skin_ProcessTranslation (int cmap, const byte *translation); @@ -74,6 +129,6 @@ void glsl_Skin_InitTranslations (void); void gl_Skin_SetupSkin (skin_t *skin, int cmap); void gl_Skin_ProcessTranslation (int cmap, const byte *translation); void gl_Skin_InitTranslations (void); -int gl_Skin_Init_Textures (int base); +void gl_Skin_Init_Textures (void); void gl_Skin_SetPlayerSkin (int width, int height, const byte *data); #endif// __mod_internal_h diff --git a/include/net_dgrm.h b/include/net_dgrm.h index 712864dd3..d9bab8c1c 100644 --- a/include/net_dgrm.h +++ b/include/net_dgrm.h @@ -29,7 +29,7 @@ /** \defgroup nq-dgrm NetQuake Datagram network driver. \ingroup nq-nd */ -//@{ +///@{ /** Initialize the Datagram net driver. @@ -43,7 +43,7 @@ int Datagram_Init (void); \param state True to enable, false to disable. */ -void Datagram_Listen (qboolean state); +void Datagram_Listen (bool state); /** Search for hosts (servers) on the local network. @@ -54,7 +54,7 @@ void Datagram_Listen (qboolean state); \param xmit True to send the broadcast, falst to only listen. */ -void Datagram_SearchForHosts (qboolean xmit); +void Datagram_SearchForHosts (bool xmit); /** Connect to the specified host. @@ -104,7 +104,7 @@ int Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data); \param sock The socket to check. \return True if the packet can be sent. */ -qboolean Datagram_CanSendMessage (qsocket_t *sock); +bool Datagram_CanSendMessage (qsocket_t *sock); /** Check if an unreliable message can be sent to the socket. @@ -113,7 +113,7 @@ qboolean Datagram_CanSendMessage (qsocket_t *sock); \param sock The socket to check. \return True if the packet can be sent. */ -qboolean Datagram_CanSendUnreliableMessage (qsocket_t *sock); +bool Datagram_CanSendUnreliableMessage (qsocket_t *sock); /** Close the socket. @@ -125,4 +125,4 @@ void Datagram_Close (qsocket_t *sock); */ void Datagram_Shutdown (void); -//@} +///@} diff --git a/include/net_loop.h b/include/net_loop.h index a0f58a34a..c74683be8 100644 --- a/include/net_loop.h +++ b/include/net_loop.h @@ -34,21 +34,21 @@ /** \defgroup nq-loop NetQuake loopback network driver. \ingroup nq-nd */ -//@{ +///@{ int Loop_Init (void); -void Loop_Listen (qboolean state); -void Loop_SearchForHosts (qboolean xmit); +void Loop_Listen (bool state); +void Loop_SearchForHosts (bool xmit); qsocket_t *Loop_Connect (const char *host); qsocket_t *Loop_CheckNewConnections (void); int Loop_GetMessage (qsocket_t *sock); int Loop_SendMessage (qsocket_t *sock, sizebuf_t *data); int Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data); -qboolean Loop_CanSendMessage (qsocket_t *sock); -qboolean Loop_CanSendUnreliableMessage (qsocket_t *sock); +bool Loop_CanSendMessage (qsocket_t *sock); +bool Loop_CanSendUnreliableMessage (qsocket_t *sock); void Loop_Close (qsocket_t *sock); void Loop_Shutdown (void); -//@} +///@} #endif//__net_loop_h diff --git a/include/net_udp.h b/include/net_udp.h index 78f26a6ec..201342b12 100644 --- a/include/net_udp.h +++ b/include/net_udp.h @@ -33,7 +33,7 @@ /** \defgroup nq-udp NetQuake UDP lan driver. \ingroup nq-ld */ -//@{ +///@{ /** Initialize the UDP network interface. @@ -55,7 +55,7 @@ void UDP_Shutdown (void); \param state True to open the socket, false to close it. */ -void UDP_Listen (qboolean state); +void UDP_Listen (bool state); /** Open a single socket on the specified port. @@ -193,6 +193,6 @@ int UDP_GetSocketPort (netadr_t *addr); */ int UDP_SetSocketPort (netadr_t *addr, int port); -//@} +///@} #endif // __net_udp_h diff --git a/include/net_vcr.h b/include/net_vcr.h index f5383b572..527b2893b 100644 --- a/include/net_vcr.h +++ b/include/net_vcr.h @@ -29,7 +29,7 @@ /** \defgroup nq-vcr NetQuake VCR network driver. \ingroup nq-nd */ -//@{ +///@{ #define VCR_OP_CONNECT 1 #define VCR_OP_GETMESSAGE 2 @@ -38,14 +38,14 @@ #define VCR_MAX_MESSAGE 4 int VCR_Init (void); -void VCR_Listen (qboolean state); -void VCR_SearchForHosts (qboolean xmit); +void VCR_Listen (bool state); +void VCR_SearchForHosts (bool xmit); qsocket_t *VCR_Connect (const char *host); qsocket_t *VCR_CheckNewConnections (void); int VCR_GetMessage (qsocket_t *sock); int VCR_SendMessage (qsocket_t *sock, sizebuf_t *data); -qboolean VCR_CanSendMessage (qsocket_t *sock); +bool VCR_CanSendMessage (qsocket_t *sock); void VCR_Close (qsocket_t *sock); void VCR_Shutdown (void); -//@} +///@} diff --git a/include/net_wins.h b/include/net_wins.h index fe4133f50..a73a8b0fd 100644 --- a/include/net_wins.h +++ b/include/net_wins.h @@ -35,14 +35,14 @@ /** \defgroup nq-wins NetQuake Winsock lan driver. \ingroup nq-ld */ -//@{ +///@{ extern int winsock_initialized; extern WSADATA winsockdata; int WINS_Init (void); void WINS_Shutdown (void); -void WINS_Listen (qboolean state); +void WINS_Listen (bool state); int WINS_OpenSocket (int port); int WINS_CloseSocket (int socket); int WINS_Connect (int socket, netadr_t *addr); @@ -54,10 +54,10 @@ const char *WINS_AddrToString (netadr_t *addr); int WINS_GetSocketAddr (int socket, netadr_t *addr); int WINS_GetNameFromAddr (netadr_t *addr, char *name); int WINS_GetAddrFromName (const char *name, netadr_t *addr); -int WINS_AddrCompare (netadr_t *addr1, netadr_t *addr2); +int WINS_AddrCompare (netadr_t *addr1, netadr_t *addr2) __attribute__((pure)); int WINS_GetSocketPort (netadr_t *addr); int WINS_SetSocketPort (netadr_t *addr, int port); -//@} +///@} #endif//__net_wins_h diff --git a/include/netchan.h b/include/netchan.h index 5b361009e..ce1178540 100644 --- a/include/netchan.h +++ b/include/netchan.h @@ -39,7 +39,7 @@ /** \defgroup qw-net QuakeWorld network support. \ingroup network */ -//{ +///@{ #define MAX_MSGLEN 1450 ///< max length of a reliable message #define MAX_DATAGRAM 1450 ///< max length of unreliable message @@ -62,25 +62,23 @@ extern netadr_t net_loopback_adr; extern netadr_t net_from; // address of who sent the packet extern struct msg_s *net_message; -extern struct cvar_s *qport; +extern int qport; -int Net_Log_Init (const char **sound_precache); -void Net_LogPrintf (const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); -void Log_Incoming_Packet (const byte *p, int len, int has_sequence, - int is_server); -void Log_Outgoing_Packet (const byte *p, int len, int has_sequence, - int is_server); -void Net_LogStop (void); +int Net_Log_Init (const char **sound_precache, int server); +void Net_LogPrintf (const char *fmt, ...) __attribute__ ((format (PRINTF, 1, 2))); +void Log_Incoming_Packet (const byte *p, int len, int has_sequence); +void Log_Outgoing_Packet (const byte *p, int len, int has_sequence); +void Net_LogStop (void *data); void Analyze_Client_Packet (const byte * data, int len, int has_sequence); void Analyze_Server_Packet (const byte * data, int len, int has_sequence); -extern struct cvar_s *net_packetlog; -//@} +extern int net_packetlog; +///@} /** \defgroup qw-udp QuakeWorld udp support. \ingroup qw-net */ -//@{ +///@{ /** Initialize the UDP network interface. @@ -90,15 +88,11 @@ extern struct cvar_s *net_packetlog; */ void NET_Init (int port); -/** Shutdown the UDP network interface. -*/ -void NET_Shutdown (void); - /** Read a single packet from the network into net_message. \return True if successfully read, otherwise false. */ -qboolean NET_GetPacket (void); +bool NET_GetPacket (void); /** Send a data packet out to the network. @@ -116,7 +110,7 @@ void NET_SendPacket (int length, const void *data, netadr_t to); \param b The second address to compare. \return True of the addresses match, otherwise false. */ -qboolean NET_CompareAdr (netadr_t a, netadr_t b); +bool NET_CompareAdr (netadr_t a, netadr_t b) __attribute__((const)); /** Compare two network addresses. @@ -126,7 +120,7 @@ qboolean NET_CompareAdr (netadr_t a, netadr_t b); \param b The second address to compare. \return True of the addresses match, otherwise false. */ -qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b); +bool NET_CompareBaseAdr (netadr_t a, netadr_t b) __attribute__((const)); /** Convert an address to a string. @@ -163,9 +157,9 @@ const char *NET_BaseAdrToString (netadr_t a); \param[out] a The resulting address of the conversion. \return True if the conversion is successful, otherwise false. */ -qboolean NET_StringToAdr (const char *s, netadr_t *a); +bool NET_StringToAdr (const char *s, netadr_t *a); -//@} +///@} /** \defgroup netchan Netchan \ingroup qw-net @@ -220,7 +214,7 @@ qboolean NET_StringToAdr (const char *s, netadr_t *a); the channel matches even if the IP port differs. The IP port should be updated to the new value before sending out any replies. */ -//@{ +///@{ #define OLD_AVG 0.99 // total = oldtotal*OLD_AVG + new*(1-OLD_AVG) #define MAX_LATENT 32 @@ -231,9 +225,9 @@ typedef enum { } ncqport_e; typedef struct netchan_s { - qboolean fatal_error; ///< True if the message overflowed + bool fatal_error; ///< True if the message overflowed - float last_received; ///< Time the last packet was received. + double last_received; ///< Time the last packet was received. /// \name statistics /// the statistics are cleared at each client begin, because @@ -301,6 +295,10 @@ extern int net_blocksend; */ extern double *net_realtime; +/** Callback to log outgoing packets +*/ +extern void (*net_log_packet) (int length, const void *data, netadr_t to); + /** Initialize the netchan system. Currently only sets the qport cvar default to a random value. @@ -321,7 +319,7 @@ void Netchan_Init_Cvars (void); \param length The size of the unreliable packet. \param data The data of the unreliable packet. */ -void Netchan_Transmit (netchan_t *chan, int length, byte *data); +void Netchan_Transmit (netchan_t *chan, unsigned length, byte *data); /** Send an out-of-band packet. @@ -329,14 +327,14 @@ void Netchan_Transmit (netchan_t *chan, int length, byte *data); \param length The length of the data to be sent. \param data The data to be sent. */ -void Netchan_OutOfBand (netadr_t adr, int length, byte *data); +void Netchan_OutOfBand (netadr_t adr, unsigned length, byte *data); /** Send a formatted string as an out-of-band packet. \param adr The address to which the data will be sent. \param format The printf style format string. */ void Netchan_OutOfBandPrint (netadr_t adr, const char *format, ...) - __attribute__ ((format (printf,2,3))); + __attribute__ ((format (PRINTF,2,3))); /** Process a packet for the specifiied connection. @@ -345,7 +343,7 @@ void Netchan_OutOfBandPrint (netadr_t adr, const char *format, ...) \param chan The netchan representing the connection. */ -qboolean Netchan_Process (netchan_t *chan); +bool Netchan_Process (netchan_t *chan); /** Initialize a new connection. @@ -362,7 +360,7 @@ void Netchan_Setup (netchan_t *chan, netadr_t adr, int qport, ncqport_e flags); \param chan The netchan representing the connection. \return True if the connection isn't chocked. */ -qboolean Netchan_CanPacket (netchan_t *chan); +bool Netchan_CanPacket (netchan_t *chan) __attribute__((pure)); /** Check if a reliable packet can be sent to the connection. @@ -370,7 +368,7 @@ qboolean Netchan_CanPacket (netchan_t *chan); \return True if there is no outstanding reliable packet and the connection isn't chocked. */ -qboolean Netchan_CanReliable (netchan_t *chan); +bool Netchan_CanReliable (netchan_t *chan) __attribute__((pure)); /** Send a packet. @@ -381,6 +379,6 @@ qboolean Netchan_CanReliable (netchan_t *chan); */ void Netchan_SendPacket (int length, const void *data, netadr_t to); -//@} +///@} #endif // _NET_H diff --git a/include/netmain.h b/include/netmain.h index a35171415..e876f18b4 100644 --- a/include/netmain.h +++ b/include/netmain.h @@ -34,7 +34,7 @@ /** \defgroup nq-net NetQuake network support. \ingroup network */ -//@{ +///@{ typedef struct { @@ -56,7 +56,7 @@ typedef struct /** \name NetHeader flags */ -//@{ +///@{ #define NETFLAG_LENGTH_MASK 0x0000ffff #define NETFLAG_DATA 0x00010000 #define NETFLAG_ACK 0x00020000 @@ -64,7 +64,7 @@ typedef struct #define NETFLAG_EOM 0x00080000 #define NETFLAG_UNRELIABLE 0x00100000 #define NETFLAG_CTL 0x80000000 -//@} +///@} #define NET_PROTOCOL_VERSION 3 @@ -86,7 +86,7 @@ typedef struct a full address and port in a string. It is used for returning the address of a server that is not running locally. */ -//@{ +///@{ /** Connect Request: \arg \b string \c game_name \em "QUAKE" @@ -153,7 +153,7 @@ typedef struct \arg \b string \c value */ #define CCREP_RULE_INFO 0x85 -//@} +///@} typedef struct qsocket_s { struct qsocket_s *next; @@ -166,9 +166,9 @@ typedef struct qsocket_s { /// \name socket status //@{ - qboolean disconnected; ///< Socket is not in use. - qboolean canSend; ///< Socket can send a message. - qboolean sendNext; + bool disconnected; ///< Socket is not in use. + bool canSend; ///< Socket can send a message. + bool sendNext; //@} /// \name socket drivers @@ -205,11 +205,11 @@ typedef struct qsocket_s { /** \name socket management */ -//@{ +///@{ extern qsocket_t *net_activeSockets; extern qsocket_t *net_freeSockets; extern int net_numsockets; -//@} +///@} #define MAX_NET_DRIVERS 8 @@ -220,12 +220,12 @@ extern int net_driverlevel; /** \name message statistics */ -//@{ +///@{ extern int messagesSent; extern int messagesReceived; extern int unreliableMessagesSent; extern int unreliableMessagesReceived; -//@} +///@} /** Create and initialize a new qsocket. @@ -262,20 +262,19 @@ typedef struct { netadr_t addr; } hostcache_t; -extern int hostCacheCount; -extern hostcache_t hostcache[HOSTCACHESIZE]; +void NET_AddCachedHost (const char *name, const char *map, const char *cname, + int users, int maxusers, int driver, int ldriver, + const netadr_t *addr); extern double net_time; extern struct msg_s *net_message; -extern int net_activeconnections; +extern unsigned net_activeconnections; + +struct cbuf_s; /** Initialize the networking sub-system. */ -void NET_Init (void); - -/** Shutdown the networking sub-system. -*/ -void NET_Shutdown (void); +void NET_Init (struct cbuf_s *cbuf); /** Check for new connections. @@ -297,7 +296,7 @@ struct qsocket_s *NET_Connect (const char *host); \param sock The qsocket representing the connection. \return True if the message can be sent. */ -qboolean NET_CanSendMessage (qsocket_t *sock); +bool NET_CanSendMessage (qsocket_t *sock); /** Read a single message from the connection into net_message. @@ -379,31 +378,31 @@ typedef struct _PollProcedure { */ void SchedulePollProcedure(PollProcedure *pp, double timeOffset); -extern qboolean tcpipAvailable; -extern char my_tcpip_address[NET_NAMELEN]; +extern bool tcpipAvailable; +extern char my_tcpip_address[NET_NAMELEN]; -extern qboolean slistInProgress; -extern qboolean slistSilent; -extern qboolean slistLocal; +extern bool slistInProgress; +extern bool slistSilent; +extern bool slistLocal; -extern struct cvar_s *hostname; +extern char *hostname; extern QFile *vcrFile; -//@} +///@} /** \defgroup nq-ld NetQuake lan drivers. \ingroup nq-net */ -//@{ +///@{ typedef struct { const char *name; - qboolean initialized; + bool initialized; int controlSock; int (*Init) (void); void (*Shutdown) (void); - void (*Listen) (qboolean state); + void (*Listen) (bool state); int (*OpenSocket) (int port); int (*CloseSocket) (int socket); int (*Connect) (int socket, netadr_t *addr); @@ -423,26 +422,26 @@ typedef struct { extern int net_numlandrivers; extern net_landriver_t net_landrivers[MAX_NET_DRIVERS]; -//@} +///@} /** \defgroup nq-nd NetQuake network drivers. \ingroup nq-net */ -//@{ +///@{ typedef struct { const char *name; - qboolean initialized; + bool initialized; int (*Init) (void); - void (*Listen) (qboolean state); - void (*SearchForHosts) (qboolean xmit); + void (*Listen) (bool state); + void (*SearchForHosts) (bool xmit); qsocket_t *(*Connect) (const char *host); qsocket_t *(*CheckNewConnections) (void); int (*QGetMessage) (qsocket_t *sock); int (*QSendMessage) (qsocket_t *sock, sizebuf_t *data); int (*SendUnreliableMessage) (qsocket_t *sock, sizebuf_t *data); - qboolean (*CanSendMessage) (qsocket_t *sock); - qboolean (*CanSendUnreliableMessage) (qsocket_t *sock); + bool (*CanSendMessage) (qsocket_t *sock); + bool (*CanSendUnreliableMessage) (qsocket_t *sock); void (*Close) (qsocket_t *sock); void (*Shutdown) (void); int controlSock; @@ -450,7 +449,8 @@ typedef struct { extern int net_numdrivers; extern net_driver_t net_drivers[MAX_NET_DRIVERS]; +extern int net_is_dedicated; -//@} +///@} #endif // __net_h diff --git a/include/ops.h b/include/ops.h index cc988e56a..8793d5663 100644 --- a/include/ops.h +++ b/include/ops.h @@ -22,34 +22,34 @@ #ifndef __ops_h #define __ops_h -double OP_Not (double op1, double op2); -double OP_Negate (double op1, double op2); -double OP_Add (double op1, double op2); -double OP_Sub (double op1, double op2); -double OP_Mult (double op1, double op2); -double OP_Div (double op1, double op2); -double OP_Exp (double op1, double op2); -double OP_Eq (double op1, double op2); -double OP_Neq (double op1, double op2); -double OP_Or (double op1, double op2); -double OP_And (double op1, double op2); -double OP_GreaterThan (double op1, double op2); -double OP_LessThan (double op1, double op2); -double OP_GreaterThanEqual (double op1, double op2); -double OP_LessThanEqual (double op1, double op2); -double OP_BitAnd (double op1, double op2); -double OP_BitOr (double op1, double op2); -double OP_BitXor (double op1, double op2); -double OP_BitInv (double op1, double op2); +double OP_Not (double op1, double op2) __attribute__((const)); +double OP_Negate (double op1, double op2) __attribute__((const)); +double OP_Add (double op1, double op2) __attribute__((const)); +double OP_Sub (double op1, double op2) __attribute__((const)); +double OP_Mult (double op1, double op2) __attribute__((const)); +double OP_Div (double op1, double op2) __attribute__((const)); +double OP_Exp (double op1, double op2) __attribute__((const)); +double OP_Eq (double op1, double op2) __attribute__((const)); +double OP_Neq (double op1, double op2) __attribute__((const)); +double OP_Or (double op1, double op2) __attribute__((const)); +double OP_And (double op1, double op2) __attribute__((const)); +double OP_GreaterThan (double op1, double op2) __attribute__((const)); +double OP_LessThan (double op1, double op2) __attribute__((const)); +double OP_GreaterThanEqual (double op1, double op2) __attribute__((const)); +double OP_LessThanEqual (double op1, double op2) __attribute__((const)); +double OP_BitAnd (double op1, double op2) __attribute__((const)); +double OP_BitOr (double op1, double op2) __attribute__((const)); +double OP_BitXor (double op1, double op2) __attribute__((const)); +double OP_BitInv (double op1, double op2) __attribute__((const)); -double Func_Sin (double *oplist, unsigned int numops); -double Func_Cos (double *oplist, unsigned int numops); -double Func_Tan (double *oplist, unsigned int numops); -double Func_Asin (double *oplist, unsigned int numops); -double Func_Acos (double *oplist, unsigned int numops); -double Func_Atan (double *oplist, unsigned int numops); -double Func_Sqrt (double *oplist, unsigned int numops); -double Func_Abs (double *oplist, unsigned int numops); +double Func_Sin (double *oplist, unsigned int numops) __attribute__((pure)); +double Func_Cos (double *oplist, unsigned int numops) __attribute__((pure)); +double Func_Tan (double *oplist, unsigned int numops) __attribute__((pure)); +double Func_Asin (double *oplist, unsigned int numops) __attribute__((pure)); +double Func_Acos (double *oplist, unsigned int numops) __attribute__((pure)); +double Func_Atan (double *oplist, unsigned int numops) __attribute__((pure)); +double Func_Sqrt (double *oplist, unsigned int numops) __attribute__((pure)); +double Func_Abs (double *oplist, unsigned int numops) __attribute__((pure)); double Func_Rand (double *oplist, unsigned int numops); -double Func_Trunc (double *oplist, unsigned int numops); +double Func_Trunc (double *oplist, unsigned int numops) __attribute__((pure)); #endif // __ops_h diff --git a/include/qfselect.h b/include/qfselect.h new file mode 100644 index 000000000..14e282d3c --- /dev/null +++ b/include/qfselect.h @@ -0,0 +1,98 @@ +/* + qfselect.h + + select fd_set wrapper + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/09/28 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __qfselect_h +#define __qfselect_h + +#include "config.h" + +#ifdef HAVE_WINDOWS_H +# define mouse_event __hide_mouse_event +# include "winquake.h" +# undef mouse_event +#endif +#ifdef HAVE_SYS_SELECT_H +# include +#endif + +typedef struct qf_fd_set { + fd_set fdset; +} qf_fd_set; + +GNU89INLINE inline void QF_FD_CLR (int fd, qf_fd_set *set); +GNU89INLINE inline int QF_FD_ISSET (int fd, qf_fd_set *set); +GNU89INLINE inline void QF_FD_SET (int fd, qf_fd_set *set); +GNU89INLINE inline void QF_FD_ZERO (qf_fd_set *set); + +#ifndef IMPLEMENT_QFSELECT_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +void +QF_FD_CLR (int fd, qf_fd_set *set) +{ + FD_CLR (fd, &set->fdset); +} + +#ifndef IMPLEMENT_QFSELECT_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +int +QF_FD_ISSET(int fd, qf_fd_set *set) +{ + return FD_ISSET(fd, &set->fdset); +} + +#ifndef IMPLEMENT_QFSELECT_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +void +QF_FD_SET(int fd, qf_fd_set *set) +{ + FD_SET(fd, &set->fdset); +} + +#ifndef IMPLEMENT_QFSELECT_Funcs +GNU89INLINE inline +#else +VISIBLE +#endif +void +QF_FD_ZERO(qf_fd_set *set) +{ + FD_ZERO(&set->fdset); +} + +#endif//__qfselect_h diff --git a/include/qstring.h b/include/qstring.h index acea2f2e2..d91503075 100644 --- a/include/qstring.h +++ b/include/qstring.h @@ -31,9 +31,10 @@ #include #include -const char * Q_strcasestr (const char *haystack, const char *needle); -size_t Q_strnlen (const char *s, size_t maxlen); -size_t Q_snprintfz (char *dest, size_t size, const char *fmt, ...) __attribute__((format(printf,3,4))); +const char * Q_strcasestr (const char *haystack, const char *needle) __attribute__((pure)); +size_t Q_strnlen (const char *s, size_t maxlen) __attribute__((pure)); +char *Q_strndup (const char *s, size_t maxlen); +size_t Q_snprintfz (char *dest, size_t size, const char *fmt, ...) __attribute__((format(PRINTF,3,4))); size_t Q_vsnprintfz (char *dest, size_t size, const char *fmt, va_list argptr); #endif // string_h diff --git a/include/quakeasm.h b/include/quakeasm.h index 8192e05f5..6a4342f2f 100644 --- a/include/quakeasm.h +++ b/include/quakeasm.h @@ -32,7 +32,7 @@ #define TRANSPARENT_COLOR 255 .extern C(d_zistepu) - .extern C(d_pzbuffer) + .extern C(d_zbuffer) .extern C(d_zistepv) .extern C(d_zrowbytes) .extern C(d_ziorigin) @@ -58,8 +58,6 @@ .extern C(cacheblock) .extern C(d_viewbuffer) .extern C(cachewidth) - .extern C(d_pzbuffer) - .extern C(d_zrowbytes) .extern C(d_zwidth) .extern C(d_scantable) .extern C(r_lightptr) @@ -71,24 +69,20 @@ .extern C(lightrightstep) .extern C(lightdeltastep) .extern C(lightdelta) - .extern C(lightright) - .extern C(lightdelta) .extern C(sourcetstep) .extern C(surfrowbytes) - .extern C(lightrightstep) - .extern C(lightdeltastep) .extern C(r_sourcemax) .extern C(r_stepback) - .extern C(colormap) + .extern C(r_colormap) .extern C(blocksize) .extern C(sourcesstep) .extern C(lightleft) .extern C(blockdivshift) .extern C(lightleftstep) - .extern C(r_origin) .extern C(r_ppn) .extern C(r_pup) .extern C(r_pright) + .extern C(r_porigin) .extern C(ycenter) .extern C(xcenter) .extern C(d_vrectbottom_particle) @@ -99,7 +93,7 @@ .extern C(d_pix_min) .extern C(d_pix_max) .extern C(d_y_aspect_shift) - .extern C(screenwidth) + .extern C(d_rowbytes) .extern C(r_leftclipped) .extern C(r_leftenter) .extern C(r_rightclipped) @@ -138,7 +132,7 @@ .extern C(r_anumverts) .extern C(aliastransform) .extern C(r_avertexnormals) - .extern C(r_plightvec) + .extern C(r_lightvec) .extern C(r_ambientlight) .extern C(r_shadelight) .extern C(aliasxcenter) @@ -147,7 +141,6 @@ .extern C(r_affinetridesc) .extern C(acolormap) .extern C(d_pcolormap) - .extern C(r_affinetridesc) .extern C(d_sfrac) .extern C(d_ptex) .extern C(d_pedgespanpackage) @@ -173,16 +166,12 @@ .extern C(erroradjustdown) .extern C(d_countextrastep) .extern C(ubasestep) - .extern C(a_ststepxwhole) - .extern C(a_tstepxfrac) .extern C(r_lstepx) .extern C(a_spans) - .extern C(erroradjustdown) .extern C(d_pdestextrastep) .extern C(d_pzextrastep) .extern C(d_sfracextrastep) .extern C(d_ptexextrastep) - .extern C(d_countextrastep) .extern C(d_tfracextrastep) .extern C(d_lightextrastep) .extern C(d_ziextrastep) @@ -190,11 +179,9 @@ .extern C(d_pzbasestep) .extern C(d_sfracbasestep) .extern C(d_ptexbasestep) - .extern C(ubasestep) .extern C(d_tfracbasestep) .extern C(d_lightbasestep) .extern C(d_zibasestep) - .extern C(zspantable) .extern C(r_lstepy) .extern C(r_sstepy) .extern C(r_tstepy) @@ -255,7 +242,7 @@ .extern C(vright) .extern C(vup) - .extern C(vpn) + .extern C(vfwd) #endif diff --git a/include/quicksort.h b/include/quicksort.h new file mode 100644 index 000000000..75d7c43ac --- /dev/null +++ b/include/quicksort.h @@ -0,0 +1,41 @@ +/* + quicksort.h + + Reentrant qsort for systems that don't have it + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef quicksort_h +#define quicksort_h + +#include + +#ifndef __compar_d_fn_t_defined +#define __compar_d_fn_t_defined +typedef int (*__compar_d_fn_t)(const void *, const void *, void *); +#endif + +void _quicksort(void *base, size_t nmemb, size_t size, __compar_d_fn_t cmp, + void *arg); + +#endif// quicksort_h diff --git a/include/qw/bothdefs.h b/include/qw/bothdefs.h index 3f9d8590b..59bdb707c 100644 --- a/include/qw/bothdefs.h +++ b/include/qw/bothdefs.h @@ -55,69 +55,7 @@ #define MAX_STYLESTRING 64 -// -// stats are integers communicated to the client by the server -// -#define MAX_CL_STATS 32 -#define STAT_HEALTH 0 -//define STAT_FRAGS 1 -#define STAT_WEAPON 2 -#define STAT_AMMO 3 -#define STAT_ARMOR 4 -//define STAT_WEAPONFRAME 5 -#define STAT_SHELLS 6 -#define STAT_NAILS 7 -#define STAT_ROCKETS 8 -#define STAT_CELLS 9 -#define STAT_ACTIVEWEAPON 10 -#define STAT_TOTALSECRETS 11 -#define STAT_TOTALMONSTERS 12 -#define STAT_SECRETS 13 // bumped on client side by svc_foundsecret -#define STAT_MONSTERS 14 // bumped by svc_killedmonster -#define STAT_ITEMS 15 -#define STAT_VIEWHEIGHT 16 -#define STAT_FLYMODE 17 - -// -// item flags -// -#define IT_SHOTGUN 1 -#define IT_SUPER_SHOTGUN 2 -#define IT_NAILGUN 4 -#define IT_SUPER_NAILGUN 8 - -#define IT_GRENADE_LAUNCHER 16 -#define IT_ROCKET_LAUNCHER 32 -#define IT_LIGHTNING 64 -#define IT_SUPER_LIGHTNING 128 - -#define IT_SHELLS 256 -#define IT_NAILS 512 -#define IT_ROCKETS 1024 -#define IT_CELLS 2048 - -#define IT_AXE 4096 - -#define IT_ARMOR1 8192 -#define IT_ARMOR2 16384 -#define IT_ARMOR3 32768 - -#define IT_SUPERHEALTH 65536 - -#define IT_KEY1 131072 -#define IT_KEY2 262144 - -#define IT_INVISIBILITY 524288 - -#define IT_INVULNERABILITY 1048576 -#define IT_SUIT 2097152 -#define IT_QUAD 4194304 - -#define IT_SIGIL1 (1<<28) - -#define IT_SIGIL2 (1<<29) -#define IT_SIGIL3 (1<<30) -#define IT_SIGIL4 (1<<31) +#include "gamedefs.h" // // print flags diff --git a/include/qw/msg_backbuf.h b/include/qw/msg_backbuf.h index 787612d38..0a6cea2db 100644 --- a/include/qw/msg_backbuf.h +++ b/include/qw/msg_backbuf.h @@ -43,10 +43,10 @@ typedef struct backbuf_s { const char *name; } backbuf_t; -int MSG_ReliableCheckSize (backbuf_t *rel, int maxsize, int minsize); -sizebuf_t *MSG_ReliableCheckBlock(backbuf_t *rel, int maxsize); +int MSG_ReliableCheckSize (backbuf_t *rel, unsigned maxsize, unsigned minsize) __attribute__((pure)); +sizebuf_t *MSG_ReliableCheckBlock(backbuf_t *rel, unsigned maxsize); void MSG_Reliable_FinishWrite(backbuf_t *rel); -sizebuf_t *MSG_ReliableWrite_Begin(backbuf_t *rel, int c, int maxsize); +sizebuf_t *MSG_ReliableWrite_Begin(backbuf_t *rel, int c, unsigned maxsize); void MSG_ReliableWrite_Angle(backbuf_t *rel, float f); void MSG_ReliableWrite_Angle16(backbuf_t *rel, float f); void MSG_ReliableWrite_Byte(backbuf_t *rel, int c); @@ -56,7 +56,7 @@ void MSG_ReliableWrite_Coord(backbuf_t *rel, float f); void MSG_ReliableWrite_Long(backbuf_t *rel, int c); void MSG_ReliableWrite_Short(backbuf_t *rel, int c); void MSG_ReliableWrite_String(backbuf_t *rel, const char *s); -void MSG_ReliableWrite_SZ(backbuf_t *rel, const void *data, int len); +void MSG_ReliableWrite_SZ(backbuf_t *rel, const void *data, unsigned len); void MSG_ReliableWrite_AngleV(backbuf_t *rel, const vec3_t v); void MSG_ReliableWrite_CoordV(backbuf_t *rel, const vec3_t v); void MSG_Reliable_Send (backbuf_t *rel); diff --git a/include/qw/pmove.h b/include/qw/pmove.h index 285513a75..f30260d19 100644 --- a/include/qw/pmove.h +++ b/include/qw/pmove.h @@ -58,9 +58,9 @@ typedef struct { int oldbuttons; int oldonground; float waterjumptime; - qboolean dead; - qboolean flying; - qboolean add_grav; + bool dead; + bool flying; + bool add_grav; int spectator; // world state @@ -88,7 +88,7 @@ typedef struct { float entgravity; } movevars_t; -extern struct cvar_s *no_pogo_stick; +extern int no_pogo_stick; extern movevars_t movevars; extern playermove_t pmove; extern int onground; @@ -106,10 +106,10 @@ void PlayerMove (void); void Pmove_Init (void); void Pmove_Init_Cvars (void); -int PM_HullPointContents (hull_t *hull, int num, const vec3_t p); +int PM_HullPointContents (hull_t *hull, int num, const vec3_t p) __attribute__((pure)); -int PM_PointContents (const vec3_t point); -qboolean PM_TestPlayerPosition (const vec3_t point); +int PM_PointContents (const vec3_t point) __attribute__((pure)); +bool PM_TestPlayerPosition (const vec3_t point); trace_t PM_PlayerMove (const vec3_t start, const vec3_t stop); #endif // _PMOVE_H diff --git a/include/qw/protocol.h b/include/qw/protocol.h index 76f2c625d..ae816d13d 100644 --- a/include/qw/protocol.h +++ b/include/qw/protocol.h @@ -31,6 +31,8 @@ #include "QF/mathlib.h" +#include "gamedefs.h" + #define PROTOCOL_VERSION 28 #define QW_CHECK_HASH 0x5157 @@ -294,7 +296,7 @@ // ELEMENTS COMMUNICATED ACROSS THE NET ======================================= -#define MAX_CLIENTS 32 +#define MAX_CLIENTS MAX_PLAYERS #define UPDATE_BACKUP 64 // copies of entity_state_t to keep buffered // must be power of two @@ -320,26 +322,9 @@ typedef struct usercmd_s { } usercmd_t; typedef struct plent_state_s { - int number; - - unsigned int flags; - vec3_t origin; + entity_state_t es; usercmd_t cmd; - vec3_t velocity; - int modelindex; - int frame; - int skinnum; - int effects; - int weaponframe; - byte msec; - - // QSG 2 - byte alpha; - byte scale; - byte glow_size; - byte glow_color; - byte colormod; } plent_state_t; typedef struct { diff --git a/include/r_cvar.h b/include/r_cvar.h index 8383f4705..3aac1e6db 100644 --- a/include/r_cvar.h +++ b/include/r_cvar.h @@ -1,105 +1,92 @@ #include "QF/mathlib.h" -extern struct cvar_s *easter_eggs; -extern void r_easter_eggs_f (struct cvar_s *var); -extern void r_particles_style_f (struct cvar_s *var); +extern void gl_overbright_f (void *data, const struct cvar_s *cvar); -extern void gl_overbright_f (struct cvar_s *cvar); - -extern struct cvar_s *cl_crossx; -extern struct cvar_s *cl_crossy; -extern struct cvar_s *cl_verstring; -extern struct cvar_s *crosshair; -extern struct cvar_s *crosshaircolor; +extern int cl_crossx; +extern int cl_crossy; +extern char *cl_verstring; +extern int crosshair; +extern int crosshaircolor; extern quat_t crosshair_color; -extern struct cvar_s *d_mipcap; -extern struct cvar_s *d_mipscale; +extern float d_mipcap; +extern float d_mipscale; -extern struct cvar_s *gl_driver; -extern struct cvar_s *gl_affinemodels; -extern struct cvar_s *gl_anisotropy; -extern struct cvar_s *gl_clear; -extern struct cvar_s *gl_conspin; -extern struct cvar_s *gl_constretch; -extern struct cvar_s *gl_dlight_polyblend; -extern struct cvar_s *gl_dlight_smooth; -extern struct cvar_s *gl_fb_bmodels; -extern struct cvar_s *gl_fb_models; -extern struct cvar_s *gl_finish; -extern struct cvar_s *gl_keeptjunctions; -extern struct cvar_s *gl_lerp_anim; -extern struct cvar_s *gl_lightmap_align; -extern struct cvar_s *gl_lightmap_subimage; -extern struct cvar_s *gl_max_size; -extern struct cvar_s *gl_multitexture; -extern struct cvar_s *gl_nocolors; -extern struct cvar_s *gl_overbright; -extern struct cvar_s *gl_particle_mip; -extern struct cvar_s *gl_particle_size; -extern struct cvar_s *gl_picmip; -extern struct cvar_s *gl_playermip; -extern struct cvar_s *gl_reporttjunctions; -extern struct cvar_s *gl_sky_clip; -extern struct cvar_s *gl_sky_debug; -extern struct cvar_s *gl_sky_divide; -extern struct cvar_s *gl_sky_multipass; -extern struct cvar_s *gl_tessellate; -extern struct cvar_s *gl_texsort; -extern struct cvar_s *gl_textures_bgra; -extern struct cvar_s *gl_triplebuffer; -extern struct cvar_s *gl_vector_light; +extern int gl_affinemodels; +extern float gl_anisotropy; +extern int gl_clear; +extern float gl_conspin; +extern int gl_constretch; +extern int gl_dlight_polyblend; +extern int gl_dlight_smooth; +extern int gl_fb_bmodels; +extern int gl_fb_models; +extern int gl_finish; +extern int gl_keeptjunctions; +extern int gl_lerp_anim; +extern int gl_lightmap_align; +extern int gl_lightmap_subimage; +extern int gl_max_size; +extern int gl_multitexture; +extern int gl_nocolors; +extern int gl_overbright; +extern int gl_particle_mip; +extern int gl_particle_size; +extern int gl_picmip; +extern int gl_playermip; +extern int gl_reporttjunctions; +extern int gl_sky_clip; +extern int gl_sky_debug; +extern int gl_sky_multipass; +extern int gl_tessellate; +extern int gl_texsort; +extern int gl_textures_bgra; +extern int gl_triplebuffer; +extern int gl_vector_light; -extern struct cvar_s *r_aliasstats; -extern struct cvar_s *r_aliastransadj; -extern struct cvar_s *r_aliastransbase; -extern struct cvar_s *r_ambient; -extern struct cvar_s *r_clearcolor; -extern struct cvar_s *r_dlight_lightmap; -extern struct cvar_s *r_drawentities; -extern struct cvar_s *r_drawexplosions; -extern struct cvar_s *r_drawflat; -extern struct cvar_s *r_drawviewmodel; -extern struct cvar_s *r_dspeeds; -extern struct cvar_s *r_dynamic; -extern struct cvar_s *r_explosionclip; -extern struct cvar_s *r_farclip; -extern struct cvar_s *r_firecolor; -extern struct cvar_s *r_flatlightstyles; -extern struct cvar_s *r_fullbright; -extern struct cvar_s *r_graphheight; -extern struct cvar_s *r_lightmap; -extern struct cvar_s *r_lightmap_components; -extern struct cvar_s *r_maxedges; -extern struct cvar_s *r_maxsurfs; -extern struct cvar_s *r_mirroralpha; -extern struct cvar_s *r_nearclip; -extern struct cvar_s *r_norefresh; -extern struct cvar_s *r_novis; -extern struct cvar_s *r_numedges; -extern struct cvar_s *r_numsurfs; -extern struct cvar_s *r_particles; -extern struct cvar_s *r_particles_max; -extern struct cvar_s *r_particles_nearclip; -extern struct cvar_s *r_particles_style; -extern struct cvar_s *r_reportedgeout; -extern struct cvar_s *r_reportsurfout; -extern struct cvar_s *r_shadows; -extern struct cvar_s *r_skyname; -extern struct cvar_s *r_speeds; -extern struct cvar_s *r_timegraph; -extern struct cvar_s *r_wateralpha; -extern struct cvar_s *r_waterripple; -extern struct cvar_s *r_waterwarp; -extern struct cvar_s *r_zgraph; +extern int r_aliasstats; +extern float r_aliastransadj; +extern float r_aliastransbase; +extern int r_clearcolor; +extern int r_dlight_lightmap; +extern int r_drawentities; +extern int r_drawexplosions; +extern int r_drawviewmodel; +extern int r_dspeeds; +extern int r_dynamic; +extern int r_explosionclip; +extern float r_farclip; +extern vec4f_t r_firecolor; +extern int r_flatlightstyles; +extern int r_graphheight; +extern int r_lightmap_components; +extern int r_maxedges; +extern int r_maxsurfs; +extern float r_mirroralpha; +extern float r_nearclip; +extern int r_norefresh; +extern int r_novis; +extern int r_numedges; +extern int r_numsurfs; +extern int r_particles; +extern int r_particles_max; +extern float r_particles_nearclip; +extern int r_reportedgeout; +extern int r_reportsurfout; +extern int r_shadows; +extern char *r_skyname; +extern int r_speeds; +extern int r_timegraph; +extern float r_wateralpha; +extern float r_waterripple; +extern int r_waterwarp; +extern int r_zgraph; -extern struct cvar_s *scr_consize; -extern struct cvar_s *scr_conspeed; -extern struct cvar_s *scr_fov; -extern struct cvar_s *scr_fisheye; -extern struct cvar_s *scr_fviews; -extern struct cvar_s *scr_ffov; -extern struct cvar_s *scr_showpause; -extern struct cvar_s *scr_showram; -extern struct cvar_s *scr_showturtle; -extern struct cvar_s *scr_viewsize; +extern float scr_fov; +extern int scr_fisheye; +extern int scr_fviews; +extern float scr_ffov; +extern int scr_showpause; +extern int scr_showram; +extern int scr_showturtle; +extern int scr_viewsize; diff --git a/include/r_dynamic.h b/include/r_dynamic.h index 0adeb4839..0bb576756 100644 --- a/include/r_dynamic.h +++ b/include/r_dynamic.h @@ -46,8 +46,7 @@ typedef enum { struct entity_s; void R_PushDlights (const vec3_t entorigin); -struct cvar_s; -void R_MaxDlightsCheck (struct cvar_s *var); +void R_MaxDlightsCheck (int max_dlights); void R_Particles_Init_Cvars (void); void R_Particle_New (const char *type, int texnum, const vec3_t org, float scale, const vec3_t vel, float die, int color, @@ -59,17 +58,6 @@ void R_InitBubble (void); void R_InitParticles (void); void R_ClearParticles (void); -void R_DrawParticles (void); -struct cvar_s; -void R_MaxParticlesCheck (struct cvar_s *r_particles, - struct cvar_s *r_particles_max); void R_InitSprites (void); -extern unsigned int r_maxparticles; -extern unsigned int numparticles; -extern struct particle_s *active_particles; -extern struct particle_s *free_particles; -extern struct particle_s *particles; -extern struct particle_s **freeparticles; - #endif // _R_DYNAMIC_H diff --git a/include/r_internal.h b/include/r_internal.h index 3d5e69206..1c2c47f5a 100644 --- a/include/r_internal.h +++ b/include/r_internal.h @@ -6,7 +6,6 @@ #include "r_cvar.h" #include "r_dynamic.h" #include "r_local.h" -#include "r_screen.h" #include "r_shared.h" extern viddef_t vid; // global video state @@ -15,81 +14,93 @@ extern vid_render_data_t vid_render_data; extern vid_render_funcs_t gl_vid_render_funcs; extern vid_render_funcs_t glsl_vid_render_funcs; extern vid_render_funcs_t sw_vid_render_funcs; -extern vid_render_funcs_t sw32_vid_render_funcs; +extern vid_render_funcs_t vulkan_vid_render_funcs; extern vid_render_funcs_t *vid_render_funcs; #define vr_data vid_render_data #define vr_funcs vid_render_funcs extern refdef_t r_refdef; +#define SW_COMP(comp, id) ((void *)((byte *)r_refdef.registry->comp_pools[comp].data + (id) * r_refdef.registry->components.a[comp].size)) extern int r_viewsize; -void R_LineGraph (int x, int y, int *h_vals, int count); +void R_LineGraph (int x, int y, int *h_vals, int count, int height); -void Fog_Update (float density, float red, float green, float blue, - float time); -struct plitem_s; -void Fog_ParseWorldspawn (struct plitem_s *worldspawn); - -float *Fog_GetColor (void); -float Fog_GetDensity (void); -void Fog_SetupFrame (void); -void Fog_EnableGFog (void); -void Fog_DisableGFog (void); -void Fog_StartAdditive (void); -void Fog_StopAdditive (void); -void Fog_Init (void); void gl_R_Init (void); void glsl_R_Init (void); +void glsl_R_Shutdown (void); void sw_R_Init (void); -void sw32_R_Init (void); - +void R_RenderFrame (SCR_Func *scr_funcs); void R_Init_Cvars (void); void R_InitEfrags (void); void R_ClearState (void); void R_InitSky (struct texture_s *mt); // called at level load void R_Textures_Init (void); void R_RenderView (void); // must set r_refdef first -void R_ViewChanged (float aspect); // must set r_refdef first +void R_ViewChanged (void); // must set r_refdef first // called whenever r_refdef or vid change -void R_AddEfrags (entity_t *ent); -void R_RemoveEfrags (entity_t *ent); +extern struct psystem_s r_psystem; +struct psystem_s *gl_ParticleSystem (void); +struct psystem_s *glsl_ParticleSystem (void); +struct psystem_s *sw_ParticleSystem (void); +void R_RunParticles (float dT); -void R_NewMap (model_t *worldmodel, model_t **models, int num_models); +struct scene_s; +void R_NewScene (struct scene_s *scene); // LordHavoc: relative bmodel lighting void R_PushDlights (const vec3_t entorigin); void R_DrawWaterSurfaces (void); -void *D_SurfaceCacheAddress (void); +void *D_SurfaceCacheAddress (void) __attribute__((pure)); int D_SurfaceCacheForRes (int width, int height); -void D_FlushCaches (void); +void D_FlushCaches (void *data); void D_DeleteSurfaceCache (void); void D_InitCaches (void *buffer, int size); -void R_SetVrect (vrect_t *pvrect, vrect_t *pvrectin, int lineadj); +void R_SetVrect (const vrect_t *pvrect, vrect_t *pvrectin, int lineadj); void R_LoadSkys (const char *); void R_ClearEfrags (void); -void R_ClearEnts (void); -void R_EnqueueEntity (struct entity_s *ent); -entity_t *R_AllocEntity (void); -void R_FreeAllEntities (void); -void R_FindNearLights (const vec3_t pos, int count, dlight_t **lights); +void R_FindNearLights (vec4f_t pos, int count, dlight_t **lights); dlight_t *R_AllocDlight (int key); void R_DecayLights (double frametime); void R_ClearDlights (void); int R_InitGraphTextures (int base); -void R_DrawAliasModel (entity_t *e); +struct entity_s; +struct animation_s; +struct transform_s; +void R_DrawAliasModel (struct entity_s *e); -void R_MarkLeaves (void); +void R_MarkLeaves (struct mleaf_s *viewleaf, int *node_visframes, + int *leaf_visframes, int *face_visframes); +extern int *r_node_visframes; +extern int *r_leaf_visframes; +extern int *r_face_visframes; -void GL_SetPalette (const byte *palette); -void GLSL_SetPalette (const byte *palette); +void GL_SetPalette (void *data, const byte *palette); +void GLSL_SetPalette (void *data, const byte *palette); + +int R_BillboardFrame (struct transform_s transform, int orientation, + vec4f_t cameravec, + vec4f_t *bbup, vec4f_t *bbright, vec4f_t *bbfwd); +mspriteframe_t *R_GetSpriteFrame (const msprite_t *sprite, + const struct animation_s *animation); + +// These correspond to the standard box sides for OpenGL cube maps but with +// TOP and BOTTOM swapped due to lelt/right handed systems (quake/gl are right, +// cube maps are left) +#define BOX_FRONT 4 +#define BOX_RIGHT 0 +#define BOX_BEHIND 5 +#define BOX_LEFT 1 +#define BOX_TOP 3 +#define BOX_BOTTOM 2 +void R_RenderFisheye (framebuffer_t *cube); #endif//__r_internal_h diff --git a/include/r_local.h b/include/r_local.h index 5f9eb2ef0..c8bb871a7 100644 --- a/include/r_local.h +++ b/include/r_local.h @@ -33,6 +33,8 @@ #include "QF/model.h" #include "QF/render.h" #include "QF/vid.h" +#include "QF/simd/mat4f.h" +#include "QF/simd/vec4f.h" #include "r_shared.h" #define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0) @@ -45,9 +47,9 @@ // viewmodel lighting ======================================================= typedef struct { - int ambientlight; - int shadelight; - float *plightvec; + int ambientlight; + int shadelight; + vec3_t lightvec; } alight_t; // clipped bmodel edges ===================================================== @@ -64,23 +66,22 @@ typedef struct { //=========================================================================== -extern struct cvar_s *r_speeds; -extern struct cvar_s *r_timegraph; -extern struct cvar_s *r_graphheight; -extern struct cvar_s *r_clearcolor; -extern struct cvar_s *r_waterwarp; -extern struct cvar_s *r_fullbright; -extern struct cvar_s *r_drawentities; -extern struct cvar_s *r_aliasstats; -extern struct cvar_s *r_dspeeds; -extern struct cvar_s *r_drawflat; -extern struct cvar_s *r_ambient; -extern struct cvar_s *r_reportsurfout; -extern struct cvar_s *r_maxsurfs; -extern struct cvar_s *r_numsurfs; -extern struct cvar_s *r_reportedgeout; -extern struct cvar_s *r_maxedges; -extern struct cvar_s *r_numedges; +extern int r_speeds; +extern int r_timegraph; +extern int r_graphheight; +extern int r_clearcolor; +extern int r_waterwarp; +extern int r_drawentities; +extern int r_aliasstats; +extern int r_dspeeds; +extern int r_drawflat; +extern int r_ambient; +extern int r_reportsurfout; +extern int r_maxsurfs; +extern int r_numsurfs; +extern int r_reportedgeout; +extern int r_maxedges; +extern int r_numedges; extern float cl_wateralpha; @@ -96,8 +97,7 @@ extern float cl_wateralpha; #define DIST_NOT_SET 98765 // !!! if this is changed, it must be changed in asm_draw.h too !!! -typedef struct clipplane_s -{ +typedef struct clipplane_s { vec3_t normal; float dist; struct clipplane_s *next; @@ -111,13 +111,14 @@ extern clipplane_t view_clipplanes[4]; //============================================================================= void R_RenderWorld (void); +struct entqueue_s; +void R_DrawEntitiesOnList (struct entqueue_s *queue); //============================================================================= extern plane_t screenedge[4]; -extern vec3_t r_origin; -extern vec3_t r_entorigin; +extern vec4f_t r_entorigin; extern int r_visframecount; @@ -130,71 +131,78 @@ void R_ClearPolyList (void); void R_DrawPolyList (void); // Surface cache related ========== -extern qboolean r_cache_thrash; // set if thrashing the surface cache +extern bool r_cache_thrash; // set if thrashing the surface cache // current entity info -extern qboolean insubmodel; -extern vec3_t r_worldmodelorg; +extern bool insubmodel; +extern vec3_t r_worldmodelorg; -extern mat4_t glsl_projection; -extern mat4_t glsl_view; +extern mat4f_t glsl_projection; +extern mat4f_t glsl_view; -void R_SetFrustum (void); +union refframe_s; +void R_SetFrustum (plane_t *frustum, const union refframe_s *frame, + float fov_x, float fov_y); + +struct entity_s; +struct animation_s; void R_SpriteBegin (void); void R_SpriteEnd (void); -void R_DrawSprite (void); -void R_RenderFace (msurface_t *fa, int clipflags); -void R_RenderPoly (msurface_t *fa, int clipflags); -void R_RenderBmodelFace (bedge_t *pedges, msurface_t *psurf); -void R_TransformPlane (plane_t *p, float *normal, float *dist); +void R_DrawSprite (struct entity_s ent); +void R_RenderFace (uint32_t render_id, msurface_t *fa, int clipflags); +void R_RenderPoly (uint32_t render_id, msurface_t *fa, int clipflags); +void R_RenderBmodelFace (uint32_t render_id, bedge_t *pedges, msurface_t *psurf); void R_TransformFrustum (void); void R_SetSkyFrame (void); void R_DrawSurfaceBlock (void); -texture_t *R_TextureAnimation (msurface_t *surf); +struct texture_s *R_TextureAnimation (int frame, msurface_t *surf) __attribute__((pure)); void R_GenSkyTile (void *pdest); void R_SurfPatch (void); -void R_DrawSubmodelPolygons (model_t *pmodel, int clipflags); -void R_DrawSolidClippedSubmodelPolygons (model_t *pmodel); +void R_DrawSubmodelPolygons (uint32_t render_id, mod_brush_t *brush, int clipflags, struct mleaf_s *topleaf); +void R_DrawSolidClippedSubmodelPolygons (uint32_t render_id, mod_brush_t *brush, struct mnode_s *topnode); void R_AddPolygonEdges (emitpoint_t *pverts, int numverts, int miplevel); surf_t *R_GetSurf (void); void R_AliasClipAndProjectFinalVert (finalvert_t *fv, auxvert_t *av); -void R_AliasDrawModel (alight_t *plighting); -void R_IQMDrawModel (alight_t *plighting); -maliasskindesc_t *R_AliasGetSkindesc (int skinnum, aliashdr_t *hdr); -maliasframedesc_t *R_AliasGetFramedesc (int framenum, aliashdr_t *hdr); -float R_AliasGetLerpedFrames (entity_t *ent, aliashdr_t *hdr); -float R_IQMGetLerpedFrames (entity_t *ent, iqm_t *hdr); +void R_AliasDrawModel (struct entity_s ent, alight_t *plighting); +void R_IQMDrawModel (struct entity_s ent, alight_t *plighting); +struct animation_s; +maliasskindesc_t *R_AliasGetSkindesc (struct animation_s *animation, int skinnum, aliashdr_t *hdr); +maliasframedesc_t *R_AliasGetFramedesc (struct animation_s *animation, aliashdr_t *hdr); +float R_AliasGetLerpedFrames (struct animation_s *animation, aliashdr_t *hdr); +float R_IQMGetLerpedFrames (struct animation_s *animation, iqm_t *hdr); iqmframe_t *R_IQMBlendFrames (const iqm_t *iqm, int frame1, int frame2, float blend, int extra); iqmframe_t *R_IQMBlendPalette (const iqm_t *iqm, int frame1, int frame2, float blend, int extra, iqmblend_t *blend_palette, int palette_size); -float R_EntityBlend (entity_t *ent, int pose, float interval); +float R_EntityBlend (struct animation_s *animation, int pose, float interval); void R_BeginEdgeFrame (void); void R_ScanEdges (void); void D_DrawSurfaces (void); void R_InsertNewEdges (edge_t *edgestoadd, edge_t *edgelist); void R_StepActiveU (edge_t *pedge); void R_RemoveEdges (edge_t *pedge); -void R_AddTexture (texture_t *tex); +void R_AddTexture (struct texture_s *tex); +struct vulkan_ctx_s; void R_ClearTextures (void); -void R_InitSurfaceChains (model_t *model); +void R_InitSurfaceChains (mod_brush_t *brush); +extern const byte *r_colormap; +void R_SetColormap (const byte *cmap); extern void R_Surf8Start (void); extern void R_Surf8End (void); extern void R_EdgeCodeStart (void); extern void R_EdgeCodeEnd (void); -extern void R_RotateBmodel (void); +struct transform_s; +extern void R_RotateBmodel (vec4f_t *mat); extern int c_faceclip; extern int r_polycount; -extern model_t *cl_worldmodel; - extern int *pfrustum_indexes[4]; // !!! if this is changed, it must be changed in asm_draw.h too !!! @@ -222,7 +230,7 @@ typedef struct btofpoly_s { extern int numbtofpolys; void R_InitTurb (void); -void R_ZDrawSubmodelPolys (model_t *clmodel); +void R_ZDrawSubmodelPolys (uint32_t render_id, mod_brush_t *brush); // Alias models =========================================== @@ -234,10 +242,10 @@ extern float leftclip, topclip, rightclip, bottomclip; extern int r_acliptype; extern finalvert_t *pfinalverts; extern auxvert_t *pauxverts; -extern float ziscale, sw32_ziscale; +extern float ziscale; extern float aliastransform[3][4]; -qboolean R_AliasCheckBBox (void); +bool R_AliasCheckBBox (struct entity_s ent); // turbulence stuff ======================================= @@ -247,7 +255,8 @@ qboolean R_AliasCheckBBox (void); // particle stuff ========================================= -void R_DrawParticles (void); +struct psystem_s; +void R_DrawParticles (struct psystem_s *psystem); void R_InitParticles (void); void R_ClearParticles (void); void R_ReadPointFile_f (void); @@ -264,8 +273,6 @@ extern edge_t *r_edges, *edge_p, *edge_max; extern edge_t *newedges[MAXHEIGHT]; extern edge_t *removeedges[MAXHEIGHT]; -extern int screenwidth; - extern int r_bmodelactive; extern vrect_t *pconupdate; @@ -280,39 +287,34 @@ extern int r_maxvalidedgeoffset; void R_AliasClipTriangle (mtriangle_t *ptri); -extern float r_time1; +extern double r_time1; extern int r_frustum_indexes[4*6]; extern int r_maxsurfsseen, r_maxedgesseen; -extern qboolean r_dowarpold, r_viewchanged; - -extern mleaf_t *r_viewleaf; +extern bool r_dowarpold, r_viewchanged; extern int r_clipflags; -extern int r_dlightframecount; -extern struct entity_s *r_ent_queue; +extern struct entqueue_s *r_ent_queue; struct dlight_s; extern vec3_t lightspot; -void R_StoreEfrags (const efrag_t *ppefrag); +struct efrag_s; +void R_StoreEfrags (const struct efrag_s *efrag); void R_TimeRefresh_f (void); -void R_TimeGraph (void); -void R_ZGraph (void); void R_PrintAliasStats (void); void R_PrintTimes (void); void R_AnimateLight (void); -int R_LightPoint (const vec3_t p); +int R_LightPoint (mod_brush_t *brush, vec4f_t p); void R_SetupFrame (void); void R_cshift_f (void); void R_EmitEdge (mvertex_t *pv0, mvertex_t *pv1); void R_ClipEdge (mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip); -void R_RecursiveMarkLights (const vec3_t lightorigin, struct dlight_s *light, - int bit, mnode_t *node); -void R_MarkLights (const vec3_t lightorigin, struct dlight_s *light, int bit, - model_t *model); +void R_RecursiveMarkLights (mod_brush_t *brush, vec4f_t lightorigin, + struct dlight_s *light, int bit, int node_id); void R_LoadSkys (const char *); +//void Vulkan_R_LoadSkys (const char *, struct vulkan_ctx_s *ctx); void R_LowFPPrecision (void); void R_HighFPPrecision (void); @@ -327,7 +329,6 @@ void R_Alias_clip_bottom (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out); void R_Alias_clip_top (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out); -void R_AliasSetUpTransform (int trivial_accept); void R_AliasTransformVector (vec3_t in, vec3_t out); void R_AliasTransformFinalVert (finalvert_t *fv, trivertx_t *pverts, stvert_t *pstverts); @@ -346,7 +347,12 @@ extern byte crosshair_data[]; #define CROSSHAIR_TILEY 2 #define CROSSHAIR_COUNT (CROSSHAIR_TILEX * CROSSHAIR_TILEY) +//NOTE: This is packed 8x8 bitmap data, one byte per scanline, 8 scanlines +////per character. Also, it is NOT the quake font, but the IBM charset. +extern byte font8x8_data[]; + struct qpic_s *Draw_CrosshairPic (void); +struct qpic_s *Draw_Font8x8Pic (void); struct tex_s *R_DotParticleTexture (void); struct tex_s *R_SparkParticleTexture (void); diff --git a/include/r_scrap.h b/include/r_scrap.h new file mode 100644 index 000000000..7648f2b39 --- /dev/null +++ b/include/r_scrap.h @@ -0,0 +1,49 @@ +/* + r_scrap.h + + Renderer agnostic scrap management + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/1/12 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __r_scrap_h +#define __r_scrap_h + +typedef struct rscrap_s { + struct vrect_s *free_rects; ///< linked list of free areas + struct vrect_s *rects; ///< linked list of allocated rects + int width; ///< overall width of scrap + int height; ///< overall height of scrap +} rscrap_t; + +void R_ScrapInit (rscrap_t *scrap, int width, int height); +void R_ScrapDelete (rscrap_t *scrap); +struct vrect_s *R_ScrapAlloc (rscrap_t *scrap, int width, int height); +void R_ScrapFree (rscrap_t *scrap, struct vrect_s *rect); +void R_ScrapClear (rscrap_t *scrap); +size_t R_ScrapArea (rscrap_t *scrap, int *count) __attribute__((pure)); +void R_ScrapDump (rscrap_t *scrap); + +#endif//__r_scrap_h diff --git a/include/r_screen.h b/include/r_screen.h deleted file mode 100644 index 8d3b08e91..000000000 --- a/include/r_screen.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - r_screen.h - - shared screen declarations - - Copyright (C) 2002 Bill Currie - - Author: Bill Currie - Date: 2002/12/11 - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ - -#ifndef __r_screen_h -#define __r_screen_h - -#include "QF/screen.h" -#include "QF/vid.h" - -void SCR_UpdateScreen (double realtime, SCR_Func scr_3dfunc, - SCR_Func *scr_funcs); -void SCR_DrawRam (void); -void SCR_DrawFPS (void); -void SCR_DrawTime (void); -void SCR_DrawTurtle (void); -void SCR_DrawPause (void); -struct tex_s *SCR_CaptureBGR (void); -struct tex_s *SCR_ScreenShot (int width, int height); -void SCR_DrawStringToSnap (const char *s, struct tex_s *tex, int x, int y); - - -extern int scr_copytop; - -extern float scr_con_current; -extern float scr_conlines; // lines of console to display - -extern int oldscreensize; -extern float oldfov; -extern int oldsbar; - -extern qboolean scr_initialized; // ready to draw - -extern struct qpic_s *scr_ram; -extern struct qpic_s *scr_net; -extern struct qpic_s *scr_turtle; - -extern int clearconsole; -extern int clearnotify; - -extern viddef_t vid; // global video state - -extern vrect_t *pconupdate; -extern vrect_t scr_vrect; - -extern qboolean scr_skipupdate; - -float CalcFov (float fov_x, float width, float height); -void SCR_SetUpToDrawConsole (void); -void SCR_ScreenShot_f (void); - -#endif//__r_screen_h diff --git a/include/r_shared.h b/include/r_shared.h index 521024d68..358ca1465 100644 --- a/include/r_shared.h +++ b/include/r_shared.h @@ -54,14 +54,13 @@ extern void R_DrawLine (polyvert_t *polyvert0, polyvert_t *polyvert1); extern int cachewidth; extern byte *cacheblock; -extern int screenwidth; extern int r_init; extern float pixelAspect; extern int r_drawnpolycount; -extern struct cvar_s *r_clearcolor; +extern int r_clearcolor; extern int sintable[SIN_BUFFER_SIZE]; extern int intsintable[SIN_BUFFER_SIZE]; @@ -70,9 +69,9 @@ extern byte color_white[4]; extern byte color_black[4]; extern vec3_t vup, base_vup; -extern vec3_t vpn, base_vpn; +extern vec3_t vfwd, base_vfwd; extern vec3_t vright, base_vright; -extern struct entity_s *currententity; +extern float r_viewmatrix[3][4]; #define NUMSTACKEDGES 2400 //2000 #define MINEDGES NUMSTACKEDGES @@ -81,16 +80,14 @@ extern struct entity_s *currententity; #define MAXSPANS 3000 // !!! if this is changed, it must be changed in asm_draw.h too !!! -typedef struct espan_s -{ +typedef struct espan_s { int u, v, count; struct espan_s *pnext; } espan_t; // FIXME: compress, make a union if that will help // insubmodel is only 1, flags is fewer than 32, spanstate could be a byte -typedef struct surf_s -{ +typedef struct surf_s { struct surf_s *next; // active surface stack in r_edge.c struct surf_s *prev; // used in r_edge.c for active surf stack struct espan_s *spans; // pointer to linked list of spans to draw @@ -102,12 +99,12 @@ typedef struct surf_s // start) int flags; // currentface flags void *data; // associated data like msurface_t - struct entity_s *entity; + uint32_t render_id; float nearzi; // nearest 1/z on surface, for mipmapping - qboolean insubmodel; + bool insubmodel; float d_ziorigin, d_zistepu, d_zistepv; - int pad[2]; // to 64 bytes + int pad[2]; // to 64 bytes (FIXME not for 64-bit) } surf_t; extern surf_t *surfaces, *surface_p, *surf_max; @@ -139,9 +136,6 @@ extern void SetUpForLineScan(fixed8_t startvertu, fixed8_t startvertv, extern int r_skymade; extern void R_MakeSky (void); -extern int gl_solidskytexture; -extern int gl_alphaskytexture; - // flags in finalvert_t.flags #define ALIAS_LEFT_CLIP 0x0001 #define ALIAS_TOP_CLIP 0x0002 @@ -169,4 +163,7 @@ typedef struct edge_s extern float r_avertexnormals[NUMVERTEXNORMALS][3]; extern vec3_t ambientcolor; +struct entity_s; +uint32_t SW_AddEntity (struct entity_s ent); + #endif // _R_SHARED_H diff --git a/include/rua_internal.h b/include/rua_internal.h index 0b6b34801..c646c1118 100644 --- a/include/rua_internal.h +++ b/include/rua_internal.h @@ -33,34 +33,39 @@ #include "QF/quakeio.h" +struct progs_s; +struct dstring_s; + void RUA_Cbuf_Init (struct progs_s *pr, int secure); - void RUA_Cmd_Init (struct progs_s *pr, int secure); - void RUA_Cvar_Init (struct progs_s *pr, int secure); - -void RUA_File_Init (struct progs_s *pr, int secure); - void RUA_Hash_Init (struct progs_s *pr, int secure); - void RUA_Math_Init (struct progs_s *pr, int secure); - void RUA_MsgBuf_Init (struct progs_s *pr, int secure); - void RUA_Obj_Init (struct progs_s *pr, int secure); - void RUA_Plist_Init (struct progs_s *pr, int secure); - +void RUA_Runtime_Init (struct progs_s *pr, int secure); void RUA_Script_Init (progs_t *pr, int secure); - void RUA_Set_Init (progs_t *pr, int secure); - +void RUA_Stdlib_Init (struct progs_s *pr, int secure); void RUA_String_Init (struct progs_s *pr, int secure); - void RUA_QFile_Init (struct progs_s *pr, int secure); -int QFile_AllocHandle (struct progs_s *pr, QFile *file); -QFile *QFile_GetFile (struct progs_s *pr, int handle); - void RUA_QFS_Init (struct progs_s *pr, int secure); +// the variable args are assumed to come immediately after fmt_arg +void RUA_Sprintf (struct progs_s *pr, struct dstring_s *dstr, const char *func, + int fmt_arg); + +int QFile_AllocHandle (struct progs_s *pr, QFile *file); +QFile *QFile_GetFile (struct progs_s *pr, int handle); +struct plitem_s *Plist_GetItem (struct progs_s *pr, int handle); + +void RUA_GUI_Init (struct progs_s *pr, int secure); +void RUA_Input_Init (struct progs_s *pr, int secure); +void RUA_Mersenne_Init (struct progs_s *pr, int secure); +void RUA_Model_Init (struct progs_s *pr, int secure); +struct model_s *Model_GetModel (progs_t *pr, int handle); +void RUA_Scene_Init (struct progs_s *pr, int secure); +struct scene_s *Scene_GetScene (struct progs_s *pr, pr_ulong_t handle); + #endif//__rua_internal_h diff --git a/include/snd_internal.h b/include/snd_internal.h index 0d8f5ba3e..61273211b 100644 --- a/include/snd_internal.h +++ b/include/snd_internal.h @@ -31,11 +31,12 @@ #ifndef __snd_internal_h #define __snd_internal_h +#include /** \defgroup sound_render Sound rendering sub-system. \ingroup sound */ -//@{ +///@{ #include "QF/plugin/general.h" #include "QF/plugin/snd_render.h" @@ -44,50 +45,78 @@ #include "QF/sound.h" #include "QF/zone.h" +struct transform_s; typedef struct portable_samplepair_s portable_samplepair_t; -typedef struct dma_s dma_t; +typedef struct snd_s snd_t; typedef struct wavinfo_s wavinfo_t; -typedef struct channel_s channel_t; typedef struct sfxbuffer_s sfxbuffer_t; typedef struct sfxblock_s sfxblock_t; typedef struct sfxstream_s sfxstream_t; + +struct sfx_s +{ + struct snd_s *snd; //!< ownding snd_t instance + const char *name; + + unsigned length; + unsigned loopstart; + + union { + sfxstream_t *stream; + sfxblock_t *block; + }; + + struct wavinfo_s *(*wavinfo) (const sfx_t *sfx); + + sfxbuffer_t *(*open) (sfx_t *sfx); +}; + /** paint samples into the mix buffer + + This is currently used for channel-count specific mixing + \param offset offset into the mix buffer at which to start mixing the channel \param ch sound channel \param buffer sound data \param count number of frames to paint */ -typedef void sfxpaint_t (int, channel_t *, float *, unsigned); +typedef void sfxpaint_t (int offset, channel_t *ch, float *buffer, + unsigned count); -/** Represent a sound sample in the mixer. +/** Represent a single output frame in the mixer. */ struct portable_samplepair_s { - float left; //!< left sample - float right; //!< right sample + float left; //!< left sample + float right; //!< right sample }; -/** communication structure between output drivers and renderer +/** Sound system state */ -struct dma_s { - int speed; //!< sample rate - int samplebits; //!< bits per sample - int channels; //!< number of output channels - int frames; //!< frames in buffer - //!< 1 frame = channels samples - int submission_chunk; //!< don't mix less than this # - int framepos; //!< position of dma cursor - unsigned char *buffer; //!< destination for mixed sound +struct snd_s { + int speed; //!< sample rate + int samplebits; //!< bits per sample + int channels; //!< number of output channels + int frames; //!< frames in buffer + //!< 1 frame = channels samples + int submission_chunk; //!< don't mix less than this # + unsigned paintedtime; //!< sound clock in samples + int framepos; //!< position of dma cursor + int threaded; //!< output+mixer run asynchronously + byte *buffer; //!< destination for mixed sound /** Transfer mixed samples to the output. \param paintbuffer The buffer of mixed samples to be transferred. \param count The number of sample to transfer. \param volume The gain for the samples. */ - void (*xfer) (portable_samplepair_t *paintbuffer, int count, - float volume); + void (*xfer) (struct snd_s *snd, portable_samplepair_t *paintbuffer, + int count, float volume); /** Optional data for the xfer function. */ - void *xfer_data; + void *xfer_data; + + void (*finish_channels) (void); + void (*paint_channels) (struct snd_s *snd, unsigned endtime); }; /** Describes the sound data. @@ -105,15 +134,25 @@ struct wavinfo_s { unsigned datalen; //!< chunk bytes }; -/** Buffer for storing sound samples in memory. For cached sounds, acts as - an ordinary buffer. For streamed sounds, acts as a ring buffer. +/** Buffer for storing sound samples in memory. For block-loaded sounds, acts + as an ordinary buffer. For streamed sounds, acts as a ring buffer. */ struct sfxbuffer_s { - unsigned head; //!< ring buffer head position in sampels - unsigned tail; //!< ring buffer tail position in sampels - unsigned length; //!< length of buffer in frames + _Atomic unsigned head; //!< ring buffer head position in sampels + _Atomic unsigned tail; //!< ring buffer tail position in sampels + // FIXME should pos be atomic? it's primary use is in the mixer but can + // be used for checking the channel's "time" unsigned pos; //!< position of tail within full stream + unsigned size; //!< size of buffer in frames unsigned channels; //!< number of channels per frame + unsigned sfx_length; //!< total length of sfx + union { // owning instance + // the first field of both sfxstream_t and sfxblock_t is a pointer + // to sfx_t + sfx_t const * const * const sfx; + sfxstream_t *stream; + sfxblock_t *block; + }; sfxpaint_t *paint; //!< channel count specific paint function /** Advance the position with the stream, updating the ring buffer as necessary. Null for chached sounds. @@ -121,24 +160,24 @@ struct sfxbuffer_s { \param count number of frames to advance \return true for success, false if an error occured */ - int (*advance) (sfxbuffer_t *buffer, unsigned int count); + int (*advance) (sfxbuffer_t *buffer, unsigned int count); /** Seek to an absolute position within the stream, resetting the ring buffer. \param buffer "this" \param pos frame position with the stream */ - void (*setpos) (sfxbuffer_t *buffer, unsigned int pos); - sfx_t *sfx; //!< owning sfx_t instance + void (*setpos) (sfxbuffer_t *buffer, unsigned int pos); + void (*close) (sfxbuffer_t *buffer); /** Sample data. The block at the beginning of the buffer (size depends on sample size) */ - float data[1]; + float data[]; }; /** Representation of sound loaded that is streamed in as needed. */ struct sfxstream_s { - sfx_t *sfx; //!< owning sfx_t instance + const sfx_t *sfx; //!< owning sfx_t instance void *file; //!< handle for "file" representing the stream wavinfo_t wavinfo; //!< description of sound data unsigned pos; //!< position of next frame full stream @@ -154,12 +193,12 @@ struct sfxstream_s { \param data address of pointer to be set to point to the buffer holding the samples */ - long (*ll_read)(void *cb_data, float **data); + long (*ll_read)(void *cb_data, float **data); /** Seek to an absolute position within the stream (low level). \param stream "this" \param pos frame position with the stream */ - int (*ll_seek)(sfxstream_t *stream, int pos); + int (*ll_seek)(sfxstream_t *stream, int pos); /** Read data from the stream. This is a high-level function for use in the mixer. The read samples will always at be the correct sample rate for the mixer, reguardless @@ -169,43 +208,36 @@ struct sfxstream_s { \param frames maximum number of frames to read from stream \return number of frames read from stream */ - int (*read)(sfxstream_t *stream, float *data, int frames); + int (*read)(sfxstream_t *stream, float *data, int frames); /** Seek to an absolute position within the stream. \param stream "this" \param pos frame position with the stream */ - int (*seek)(sfxstream_t *stream, int pos); - sfxbuffer_t buffer; // static sounds */ -//@{ +///@{ #define MAX_CHANNELS 512 //!< number of available mixing channels #define MAX_DYNAMIC_CHANNELS 128 //!< number of dynamic channels #define MAX_STATIC_CHANNELS 256 //!< number of static channels @@ -310,53 +349,69 @@ extern channel_t snd_channels[MAX_CHANNELS]; //!< pool of available channels extern int snd_total_channels; //!< number of active channels /** Allocate a sound channel that can be used for playing sounds. + \param snd sound system state */ -struct channel_s *SND_AllocChannel (void); +channel_t *SND_AllocChannel (snd_t *snd); + +void SND_ChannelSetVolume (channel_t *chan, float volume); /** Stop a channel from playing. + \param snd sound system state \param chan the channel to stop */ -void SND_ChannelStop (channel_t *chan); +void SND_ChannelStop (snd_t *snd, channel_t *chan); + +/** Mark all channels as no longer in use. + + For use by asynchronous output drivers. +*/ +void SND_FinishChannels (void); /** Scan channels looking for stopped channels. + \param snd sound system state \param wait if true, wait for the channels to be done. if false, force the channels to be done. true is for threaded, false for non-threaded. */ -void SND_ScanChannels (int wait); +void SND_ScanChannels (snd_t *snd, int wait); /** Disable ambient sounds. + \param snd sound system state \todo not used, remove? */ -void SND_AmbientOff (void); +void SND_AmbientOff (snd_t *snd); /** Enable ambient sounds. + \param snd sound system state \todo not used, remove? */ -void SND_AmbientOn (void); +void SND_AmbientOn (snd_t *snd); + +void SND_SetAmbient (snd_t *snd, int amb_channel, sfx_t *sfx); /** Update the sound engine with the client's position and orientation and render some sound. - \param origin 3d coords of the client - \param v_forward 3d vector of the client's facing direction - \param v_right 3d vector of the client's rightward direction - \param v_up 3d vector of the client's upward direction + \param snd sound system state + \param ear Transform for the position and orientation of the stereo + sound pickup. \param ambient_sound_level Pointer to 4 bytes indicating the levels at which to play the ambient sounds. */ -void SND_SetListener (const vec3_t origin, const vec3_t v_forward, - const vec3_t v_right, const vec3_t v_up, +void SND_SetListener (snd_t *snd, struct transform_s ear, const byte *ambient_sound_level); /** Stop all sounds from playing. + \param snd sound system state */ -void SND_StopAllSounds(void); +void SND_StopAllSounds (snd_t *snd); /** Initialize the channels sub-subsystem + \param snd sound system state */ -void SND_Channels_Init (void); +void SND_Channels_Init (snd_t *snd); /** Start a sound playing. + \param snd sound system state \param entnum index of entity the sound is associated with. \param entchannel 0-7 - 0 auto (never willingly overrides) @@ -369,63 +424,68 @@ void SND_Channels_Init (void); \param fvol absolute volume of the sound \param attenuation rate of volume dropoff vs distance */ -void SND_StartSound (int entnum, int entchannel, sfx_t *sfx, const vec3_t origin, - float fvol, float attenuation); +void SND_StartSound (snd_t *snd, int entnum, int entchannel, sfx_t *sfx, + vec4f_t origin, float fvol, float attenuation); /** Create a sound generated by the world. + \param snd sound system state \param sfx sound to play \param origin 3d coords of where the sound originates \param vol absolute volume of the sound \param attenuation rate of volume dropoff vs distance */ -void SND_StaticSound (sfx_t *sfx, const vec3_t origin, float vol, - float attenuation); +void SND_StaticSound (snd_t *snd, sfx_t *sfx, vec4f_t origin, float vol, + float attenuation); /** Stop an entity's sound. + \param snd sound system state \param entnum index of entity the sound is associated with. \param entchannel channel to silence */ -void SND_StopSound (int entnum, int entchannel); +void SND_StopSound (snd_t *snd, int entnum, int entchannel); /** Start a sound local to the client view. + \param snd sound system state \param s name of sound to play */ -void SND_LocalSound (const char *s); -//@} +void SND_LocalSound (snd_t *snd, const char *s); +///@} /** \defgroup sound_render_mix Mixer engine. \ingroup sound_render */ -//@{ -/** sound clock in samples -*/ -extern unsigned snd_paintedtime; +///@{ /** Mix all active channels into the output buffer. + \param snd sound system state \param endtime sample time until when to mix */ -void SND_PaintChannels(unsigned int endtime); +void SND_PaintChannels(snd_t *snd, unsigned int endtime); /** Initialize the scale table for painting of 8 bit samples. + \param snd sound system state */ -void SND_InitScaletable (void); +void SND_InitScaletable (snd_t *snd); /** Set the paint function of the sfxbuffer - \param sc sfxbuffer to set. + \param sb sfxbuffer to set. */ -void SND_SetPaint (sfxbuffer_t *sc); -//@} +void SND_SetPaint (sfxbuffer_t *sb); +///@} /** \defgroup sound_render_resample Resampling functions \ingroup sound_render */ -//@{ +///@{ + +unsigned SND_ResamplerFrames (const sfx_t *sfx, unsigned frames); + /** Set up the various parameters that depend on the actual sample rate. - \param sc buffer to setup + \param sb buffer to setup \param streamed non-zero if this is for a stream. */ -void SND_SetupResampler (sfxbuffer_t *sc, int streamed); +void SND_SetupResampler (sfxbuffer_t *sb, int streamed); /** Free memory allocated for the resampler. \param stream stream to pulldown @@ -433,11 +493,11 @@ void SND_SetupResampler (sfxbuffer_t *sc, int streamed); void SND_PulldownResampler (sfxstream_t *stream); /** Copy/resample data into buffer, resampling as necessary. - \param sc buffer to write resampled sound + \param sb buffer to write resampled sound \param data raw sample data \param length number of frames to resample */ -void SND_Resample (sfxbuffer_t *sc, float *data, int length); +void SND_Resample (sfxbuffer_t *sb, float *data, int length); /** Convert integer sample data to float sample data. \param idata integer data buffer @@ -446,15 +506,15 @@ void SND_Resample (sfxbuffer_t *sc, float *data, int length); \param channels number of channels per frame \param width bytes per channel */ -void SND_Convert (byte *idata, float *fdata, int frames, int channels, - int width); -//@} +void SND_Convert (byte *idata, float *fdata, int frames, + int channels, int width); +///@} /** \defgroup sound_render_load Sound loading functions \ingroup sound_render */ -//@{ +///@{ /** Load the referenced sound. \param sfx sound reference \return 0 if ok, -1 on error @@ -492,68 +552,23 @@ int SND_LoadWav (QFile *file, sfx_t *sfx, char *realname); \return 0 if ok, -1 on error */ int SND_LoadMidi (QFile *file, sfx_t *sfx, char *realname); -//@} +///@} -/** \defgroup sound_render_cache_stream Cache/Stream Functions. +/** \defgroup sound_render_block_stream Block/Stream Functions. \ingroup sound_render */ -//@{ -/** Retrieve wavinfo from a cached sound. +///@{ +/** Retrieve wavinfo from a block-loaded sound. \param sfx sound reference \return pointer to sound's wavinfo */ -wavinfo_t *SND_CacheWavinfo (sfx_t *sfx); +wavinfo_t *SND_BlockWavinfo (const sfx_t *sfx) __attribute__((pure)); /** Retrieve wavinfo from a streamed sound. \param sfx sound reference \return pointer to sound's wavinfo */ -wavinfo_t *SND_StreamWavinfo (sfx_t *sfx); - -/** Ensure a cached sound is in memory. - \param sfx sound reference - \return poitner to sound buffer -*/ -sfxbuffer_t *SND_CacheTouch (sfx_t *sfx); - -/** Get the pointer to the sound buffer. - \param sfx sound reference - \return sound buffer or null - \note The sound must be retained with SND_CacheRetain() for the returned - buffer to be valid. -*/ -sfxbuffer_t *SND_CacheGetBuffer (sfx_t *sfx); - -/** Lock a cached sound into memory. After calling this, SND_CacheGetBffer() - will return a valid buffer. - \param sfx sound reference - \return poitner to sound buffer -*/ -sfxbuffer_t *SND_CacheRetain (sfx_t *sfx); - -/** Unlock a cached sound from memory. After calling this, SND_CacheGetBffer() - will return a null buffer. - \param sfx sound reference -*/ -void SND_CacheRelease (sfx_t *sfx); - -/** Get the pointer to the sound buffer. - \param sfx sound reference - \return poitner to sound buffer -*/ -sfxbuffer_t *SND_StreamGetBuffer (sfx_t *sfx); - -/** Lock a streamed sound into memory. Doesn't actually do anything other than - return a pointer to the buffer. - \param sfx sound reference - \return poitner to sound buffer -*/ -sfxbuffer_t *SND_StreamRetain (sfx_t *sfx); - -/** Unlock a streamed sound from memory. Doesn't actually do anything. - \param sfx sound reference -*/ -void SND_StreamRelease (sfx_t *sfx); +wavinfo_t *SND_StreamWavinfo (const sfx_t *sfx) __attribute__((pure)); /** Advance the position with the stream, updating the ring buffer as necessary. Null for chached sounds. @@ -568,17 +583,6 @@ int SND_StreamAdvance (sfxbuffer_t *buffer, unsigned int count); \param pos sample position with the stream */ void SND_StreamSetPos (sfxbuffer_t *buffer, unsigned int pos); - -/** Allocate a sound buffer from cache for cached sounds. - \param samples size in samples - \param rate sample rate - \param channels number of channels in input data - \param block cached sound descriptor to initialize - \param allocator cache allocator function - \return pointer to sound sample buffer (setup for block mode) -*/ -sfxbuffer_t *SND_GetCache (long samples, int rate, int channels, - sfxblock_t *block, cache_allocator_t allocator); -//@} +///@} #endif//__snd_internal_h diff --git a/include/sv_console.h b/include/sv_console.h index c6584a90b..28c616dd0 100644 --- a/include/sv_console.h +++ b/include/sv_console.h @@ -1,13 +1,21 @@ #ifndef __sv_console_h #define __sv_console_h +enum { + server_href, + server_view, + server_window, + + server_comp_count +}; + struct view_s; typedef struct sv_view_s { void *win; void *obj; - void (*draw) (struct view_s *view); - void (*setgeometry) (struct view_s *view); + void (*draw) (struct view_s view); + void (*setgeometry) (struct view_s view); } sv_view_t; typedef struct sv_sbar_s { diff --git a/include/varrays.h b/include/varrays.h index 3116e3781..2e67a3167 100644 --- a/include/varrays.h +++ b/include/varrays.h @@ -47,7 +47,7 @@ typedef struct varray_t2f_c4f_n3f_v3f_s { GLfloat vertex[3]; } varray_t2f_c4f_n3f_v3f_t; -extern qboolean gl_va_capable; +extern bool gl_va_capable; extern int vaelements; extern varray_t2f_c4ub_v3f_t *gl_spriteVertexArray; diff --git a/include/vid_gl.h b/include/vid_gl.h new file mode 100644 index 000000000..d23114a48 --- /dev/null +++ b/include/vid_gl.h @@ -0,0 +1,43 @@ +#ifndef __vid_gl_h +#define __vid_gl_h + +#include "QF/simd/types.h" + +// GL_context is a pointer to opaque data +typedef struct GL_context *GL_context; + +typedef struct gl_ctx_s { + GL_context context; + void (*load_gl) (struct gl_ctx_s *ctx); + void (*choose_visual) (struct gl_ctx_s *ctx); + void (*create_context) (struct gl_ctx_s *ctx, int core); + void (*init_gl) (void); + void *(*get_proc_address) (const char *name, bool crit); + void (*end_rendering) (void); + + mat4f_t projection; + + int begun; + double start_time; + int brush_polys; + int alias_polys; +} gl_ctx_t; + +typedef struct gl_framebuffer_s { + unsigned handle; + unsigned color; + unsigned depth; +} gl_framebuffer_t; + +extern gl_ctx_t *gl_ctx; +extern gl_ctx_t *glsl_ctx; + +void gl_Fog_SetupFrame (void); +void gl_Fog_EnableGFog (void); +void gl_Fog_DisableGFog (void); +void gl_Fog_StartAdditive (void); +void gl_Fog_StopAdditive (void); + +void gl_errors (const char *msg); + +#endif//__vid_gl_h diff --git a/include/vid_internal.h b/include/vid_internal.h index bf1803270..ea45d2a13 100644 --- a/include/vid_internal.h +++ b/include/vid_internal.h @@ -4,23 +4,45 @@ #include "QF/vid.h" #include "QF/plugin/vid_render.h" -extern struct cvar_s *vid_fullscreen; -extern struct cvar_s *vid_system_gamma; -extern struct cvar_s *vid_gamma; +typedef struct vid_system_s { + void (*init) (byte *palette, byte *colormap); + void (*shutdown) (void); + void (*set_palette) (byte *palette, byte *colormap); + void (*init_cvars) (void); + void (*update_fullscreen) (int fullscreen); +} vid_system_t; -extern unsigned short sw32_8to16table[256]; +extern vid_system_t vid_system; + +typedef struct vid_internal_s { + void (*flush_caches) (void *ctx); + void (*init_buffers) (void *ctx); + void (*set_palette) (void *ctx, const byte *palette); + void (*set_colormap) (void *ctx, const byte *colormap); + + void (*choose_visual) (void *ctx); + void (*create_context) (void *ctx); + + void *ctx; + + struct gl_ctx_s *(*gl_context) (struct vid_internal_s *); + struct sw_ctx_s *(*sw_context) (struct vid_internal_s *); + struct vulkan_ctx_s *(*vulkan_context) (struct vid_internal_s *); + + void (*unload) (void *data); +} vid_internal_t; + +extern int vid_fullscreen; +extern int vid_system_gamma; +extern float vid_gamma; void VID_GetWindowSize (int def_w, int def_h); +void VID_SetWindowSize (int width, int height); -void VID_InitGamma (unsigned char *); -qboolean VID_SetGamma (double); -void VID_UpdateGamma (struct cvar_s *); +void VID_InitGamma (const byte *); +bool VID_SetGamma (double); +void VID_UpdateGamma (void); -void VID_Update (vrect_t *rects); -void VID_LockBuffer (void); -void VID_UnlockBuffer (void); -void VID_InitBuffers (void); void VID_MakeColormaps (void); - #endif//__vid_internal_h diff --git a/include/vid_sw.h b/include/vid_sw.h new file mode 100644 index 000000000..913052242 --- /dev/null +++ b/include/vid_sw.h @@ -0,0 +1,23 @@ +#ifndef __vid_sw_h +#define __vid_sw_h + +#include "QF/qtypes.h" + +struct vrect_s; +typedef struct sw_ctx_s { + void (*choose_visual) (struct sw_ctx_s *ctx); + void (*create_context) (struct sw_ctx_s *ctx); + void (*set_palette) (struct sw_ctx_s *ctx, const byte *palette); + void (*update) (struct sw_ctx_s *ctx, struct vrect_s *rects); + struct framebuffer_s *framebuffer; +} sw_ctx_t; + +typedef struct sw_framebuffer_s { + byte *color; + short *depth; + int rowbytes; +} sw_framebuffer_t; + +extern sw_ctx_t *sw_ctx; + +#endif//__vid_sw_h diff --git a/include/vid_vulkan.h b/include/vid_vulkan.h new file mode 100644 index 000000000..fc6324162 --- /dev/null +++ b/include/vid_vulkan.h @@ -0,0 +1,91 @@ +#ifndef __vid_vulkan_h +#define __vid_vulkan_h + +#ifndef VK_NO_PROTOTYPES +#define VK_NO_PROTOTYPES +#endif +#include + +#include "QF/darray.h" +#include "QF/qtypes.h" +#include "QF/simd/types.h" + +typedef struct qfv_renderpassset_s + DARRAY_TYPE (struct qfv_orenderpass_s *) qfv_renderpassset_t; + +typedef struct vulkan_ctx_s { + void (*load_vulkan) (struct vulkan_ctx_s *ctx); + void (*unload_vulkan) (struct vulkan_ctx_s *ctx); + + const char **required_extensions; + struct vulkan_presentation_s *presentation; + int (*get_presentation_support) (struct vulkan_ctx_s *ctx, + VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex); + void (*choose_visual) (struct vulkan_ctx_s *ctx); + void (*create_window) (struct vulkan_ctx_s *ctx); + VkSurfaceKHR (*create_surface) (struct vulkan_ctx_s *ctx); + + struct va_ctx_s *va_ctx; + struct qfv_instance_s *instance; + struct qfv_device_s *device; + struct qfv_swapchain_s *swapchain; + VkSampleCountFlagBits msaaSamples; // FIXME not here? + VkSurfaceKHR surface; //FIXME surface = window, so "contains" swapchain + + uint32_t swapImageIndex; + + struct scriptctx_s *script_context; + struct qfv_renderctx_s *render_context; + struct qfv_capturectx_s *capture_context; + struct texturectx_s *texture_context; + struct matrixctx_s *matrix_context; + struct translucentctx_s *translucent_context; + struct aliasctx_s *alias_context; + struct bspctx_s *bsp_context; + struct iqmctx_s *iqm_context; + struct scenectx_s *scene_context; + struct palettectx_s *palette_context; + struct particlectx_s *particle_context; + struct spritectx_s *sprite_context; + struct drawctx_s *draw_context; + struct lightingctx_s *lighting_context; + struct composectx_s *compose_context; + struct outputctx_s *output_context; + + VkCommandPool cmdpool; + struct qfv_stagebuf_s *staging; + uint32_t curFrame; + qfv_renderpassset_t renderPasses; + + + struct qfv_tex_s *default_black; + struct qfv_tex_s *default_white; + struct qfv_tex_s *default_magenta; + struct qfv_tex_s *default_magenta_array; + + // size of window + int window_width; + int window_height; + int twod_scale; + +#define EXPORTED_VULKAN_FUNCTION(fname) PFN_##fname fname; +#define GLOBAL_LEVEL_VULKAN_FUNCTION(fname) PFN_##fname fname; +#include "QF/Vulkan/funclist.h" +} vulkan_ctx_t; + +#define qfvPushDebug(ctx, x) \ + do { \ + if (developer & SYS_vulkan) { \ + DARRAY_APPEND(&(ctx)->instance->debug_stack, (x)); \ + } \ + } while (0) +#define qfvPopDebug(ctx) \ + do { \ + if (developer & SYS_vulkan) { \ + __auto_type ds = &(ctx)->instance->debug_stack; \ + DARRAY_REMOVE_AT(ds, ds->size - 1); \ + } \ + } while (0) + +#endif//__vid_vulkan_h diff --git a/include/win32/fnmatch.h b/include/win32/fnmatch.h index 7dd238d67..af8cd3670 100644 --- a/include/win32/fnmatch.h +++ b/include/win32/fnmatch.h @@ -61,7 +61,7 @@ extern "C" { /* Match STRING against the filename pattern PATTERN, returning zero if it matches, FNM_NOMATCH if not. */ extern int fnmatch __P ((const char *__pattern, const char *__string, - int __flags)); + int __flags)) __attribute__((pure)); #ifdef __cplusplus } diff --git a/include/win32/stdint.h b/include/win32/stdint.h deleted file mode 100644 index 495cadcba..000000000 --- a/include/win32/stdint.h +++ /dev/null @@ -1,2 +0,0 @@ -/* stub for compilers not supporting stdint.h */ -#include "pstdint.h" diff --git a/include/winquake.h b/include/winquake.h index 82d73f083..ccb3d2db9 100644 --- a/include/winquake.h +++ b/include/winquake.h @@ -30,6 +30,8 @@ #ifdef _WIN32 +#include + #ifndef __GNUC__ # pragma warning( disable : 4229 ) /* mgraph gets this */ #endif @@ -50,6 +52,10 @@ # endif #endif +#ifdef HAVE_DDRAW_H +# include +#endif + #ifdef HAVE_MGRAPH_H # include #endif @@ -65,68 +71,19 @@ #endif extern HINSTANCE global_hInstance; -extern int global_nCmdShow; - -#ifdef HAVE_DDRAW_H -# include -extern LPDIRECTDRAW lpDD; -extern LPDIRECTDRAWSURFACE lpPrimary; -extern LPDIRECTDRAWSURFACE lpFrontBuffer; -extern LPDIRECTDRAWSURFACE lpBackBuffer; -extern LPDIRECTDRAWPALETTE lpDDPal; -#endif - -void VID_LockBuffer (void); -void VID_UnlockBuffer (void); -void VID_UpdateWindowStatus (int window_x, int window_y); typedef enum {MS_WINDOWED, MS_FULLSCREEN, MS_FULLDIB, MS_UNINIT} modestate_t; extern modestate_t modestate; -extern HWND mainwindow; -extern qboolean ActiveApp, Minimized; +extern bool WinNT; -extern qboolean WinNT; - -void VID_ForceLockState (int lk); -extern qboolean winsock_lib_initialized; - -extern int window_center_x, window_center_y; -extern RECT window_rect; +extern bool winsock_lib_initialized; #ifdef SPLASH_SCREEN extern HWND hwnd_dialog; #endif -extern HANDLE hinput, houtput; - -void S_BlockSound (void); -void S_UnblockSound (void); - -DWORD *DSOUND_LockBuffer(qboolean lockit); -void DSOUND_ClearBuffer(int clear); -void DSOUND_Restore(void); - -extern int (PASCAL FAR *pWSAStartup)(WORD wVersionRequired, LPWSADATA lpWSAData); -extern int (PASCAL FAR *pWSACleanup)(void); -extern int (PASCAL FAR *pWSAGetLastError)(void); -extern SOCKET (PASCAL FAR *psocket)(int af, int type, int protocol); -extern int (PASCAL FAR *pioctlsocket)(SOCKET s, long cmd, u_long FAR *argp); -extern int (PASCAL FAR *psetsockopt)(SOCKET s, int level, int optname, const char FAR * optval, int optlen); -extern int (PASCAL FAR *precvfrom)(SOCKET s, char FAR * buf, int len, int flags, struct sockaddr FAR *from, int FAR * fromlen); -extern int (PASCAL FAR *psendto)(SOCKET s, const char FAR * buf, int len, int flags, const struct sockaddr FAR *to, int tolen); -extern int (PASCAL FAR *pclosesocket)(SOCKET s); -extern int (PASCAL FAR *pgethostname)(char FAR * name, int namelen); -extern struct hostent FAR * (PASCAL FAR *pgethostbyname)(const char FAR * name); -extern struct hostent FAR * (PASCAL FAR *pgethostbyaddr)(const char FAR * addr, int len, int type); -extern int (PASCAL FAR *pgetsockname)(SOCKET s, struct sockaddr FAR *name, int FAR * namelen); - -HWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow); -LONG CDAudio_MessageHandler (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); -LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); -void AppActivate (BOOL fActive, BOOL minimize); - #undef E_POINTER #endif /* _WIN32 */ diff --git a/include/world.h b/include/world.h index e6ebc90ee..69b7664b6 100644 --- a/include/world.h +++ b/include/world.h @@ -33,7 +33,7 @@ #include "QF/model.h" typedef struct { - qboolean present; + bool present; vec3_t laggedpos; } laggedentinfo_t; @@ -44,9 +44,9 @@ typedef enum { } trace_e; typedef struct trace_s { - qboolean allsolid; // if true, plane is not valid - qboolean startsolid; // if true, the initial point was in a solid area - qboolean inopen, inwater; + bool allsolid; // if true, plane is not valid + bool startsolid; // if true, the initial point was in a solid area + bool inopen, inwater; float fraction; // time completed, 1.0 = didn't hit anything vec3_t extents; // 1/2 size of traced box trace_e type; // type of trace to perform @@ -82,7 +82,7 @@ extern areanode_t sv_areanodes[AREA_NODES]; void SV_FreeAllEdictLeafs (void); -void SV_InitHull (hull_t *hull, mclipnode_t *clipnodes, plane_t *planes); +void SV_InitHull (hull_t *hull, dclipnode_t *clipnodes, plane_t *planes); void SV_ClearWorld (void); // called after the world model has been loaded, before linking any entities @@ -92,14 +92,14 @@ void SV_UnlinkEdict (struct edict_s *ent); // so it doesn't clip against itself // flags ent->v.modified -void SV_LinkEdict (struct edict_s *ent, qboolean touch_triggers); +void SV_LinkEdict (struct edict_s *ent, bool touch_triggers); // Needs to be called any time an entity changes origin, mins, maxs, or solid // flags ent->v.modified // sets ent->v.absmin and ent->v.absmax // if touchtriggers, calls prog functions for the intersected triggers -int SV_PointContents (const vec3_t p); -int SV_TruePointContents (const vec3_t p); +int SV_PointContents (const vec3_t p) __attribute__((pure)); +int SV_TruePointContents (const vec3_t p) __attribute__((pure)); // returns the CONTENTS_* value from the world at the given point. // does not check any entities at all // the non-true version remaps the water current contents to content_water @@ -123,7 +123,7 @@ trace_t SV_Move (const vec3_t start, const vec3_t mins, const vec3_t maxs, struct edict_s *SV_TestPlayerPosition (struct edict_s *ent, const vec3_t origin); -int SV_HullPointContents (hull_t *hull, int num, const vec3_t p); +int SV_HullPointContents (hull_t *hull, int num, const vec3_t p) __attribute__((pure)); hull_t *SV_HullForEntity (struct edict_s *ent, const vec3_t mins, const vec3_t maxs, vec3_t extents, vec3_t offset); void MOD_TraceLine (hull_t *hull, int num, diff --git a/libs/Makefile.am b/libs/Makefile.am deleted file mode 100644 index 5c14c7d00..000000000 --- a/libs/Makefile.am +++ /dev/null @@ -1,12 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -# everything depends on util -# ruamoko depends on gamecode -# gib depends on ruamoko -# audio depends on gamecode -# models depends on image -# video depends on models(?), image(?) and ruamoko -# console depends on video, ruamoko and audio -SUBDIRS=@libs_dirs@ -DIST_SUBDIRS=util gamecode ruamoko gib audio image models video console \ - net qw client diff --git a/libs/Makemodule.am b/libs/Makemodule.am new file mode 100644 index 000000000..92eda626e --- /dev/null +++ b/libs/Makemodule.am @@ -0,0 +1,17 @@ +include libs/util/Makemodule.am +include libs/ecs/Makemodule.am +include libs/ui/Makemodule.am +include libs/gamecode/Makemodule.am +include libs/ruamoko/Makemodule.am +include libs/gib/Makemodule.am +include libs/audio/Makemodule.am +include libs/image/Makemodule.am +include libs/models/Makemodule.am +include libs/input/Makemodule.am +include libs/video/Makemodule.am +include libs/console/Makemodule.am +include libs/scene/Makemodule.am + +include libs/net/Makemodule.am +include libs/client/Makemodule.am +include libs/qw/Makemodule.am diff --git a/libs/audio/Makefile.am b/libs/audio/Makefile.am deleted file mode 100644 index 5521b3e2f..000000000 --- a/libs/audio/Makefile.am +++ /dev/null @@ -1,77 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -SUBDIRS= targets renderer . test -AM_CFLAGS= @PREFER_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include -SDL_LIBS= @SDL_LIBS@ -XMMS_LIBS= @XMMS_LIBS@ -plugin_ldflags= @plugin_ldflags@ -avoid-version -module -rpath $(plugindir) -plugin_libadd= @plugin_libadd@ -lib_ldflags=-version-info $(QUAKE_LIBRARY_VERSION_INFO) \ - -rpath $(libdir) -no-undefined -EXEEXT= - -plugin_LTLIBRARIES= @cd_plugins@ -noinst_LTLIBRARIES= @cd_static_plugins@ -lib_LTLIBRARIES= @CD_TARGETS@ @SND_TARGETS@ -EXTRA_LTLIBRARIES= libQFsound.la libQFcd.la \ - cd_file.la cd_linux.la cd_sdl.la cd_sgi.la cd_win.la \ - cd_xmms.la - -cd_plug_libs=$(top_builddir)/libs/util/libQFutil.la - -cd_file_libs= \ - libQFsound.la \ - $(cd_plug_libs) - -cd_file_la_LDFLAGS= $(plugin_ldflags) -cd_file_la_LIBADD= $(cd_file_libs) -cd_file_la_DEPENDENCIES= $(cd_file_libs) -cd_file_la_SOURCES= cd_file.c - -cd_linux_la_LDFLAGS= $(plugin_ldflags) -cd_linux_la_LIBADD= $(cd_plug_libs) -cd_linux_la_DEPENDENCIES= $(cd_plug_libs) -cd_linux_la_SOURCES= cd_linux.c - -cd_sdl_la_LDFLAGS= $(plugin_ldflags) -cd_sdl_la_LIBADD= $(cd_plug_libs) $(SDL_LIBS) $(plugin_libadd) -cd_sdl_la_DEPENDENCIES= $(cd_plug_libs) -cd_sdl_la_CFLAGS= $(SDL_CFLAGS) -cd_sdl_la_SOURCES= cd_sdl.c - -cd_sgi_la_LDFLAGS= $(plugin_ldflags) -cd_sgi_la_LIBADD= $(cd_plug_libs) $(SGI_CD_LIBS) -cd_sgi_la_DEPENDENCIES= $(cd_plug_libs) -cd_sgi_la_SOURCES= cd_sgi.c - -cd_win_la_LDFLAGS= $(plugin_ldflags) -cd_win_la_LIBADD= $(cd_plug_libs) $(plugin_libadd) -cd_win_la_DEPENDENCIES= $(cd_plug_libs) -cd_win_la_SOURCES= cd_win.c - -cd_xmms_la_LDFLAGS= $(plugin_ldflags) -cd_xmms_la_LIBADD= $(cd_plug_libs) $(XMMS_LIBS) -cd_xmms_la_DEPENDENCIES= $(cd_plug_libs) -cd_xmms_la_CFLAGS= $(XMMS_CFLAGS) -cd_xmms_la_SOURCES= cd_xmms.c - -sound_libs= \ - @snd_output_static_plugin_libs@ \ - @snd_render_static_plugin_libs@ \ - $(top_builddir)/libs/ruamoko/libQFruamoko.la \ - $(top_builddir)/libs/util/libQFutil.la - -cd_libs= \ - @cd_static_plugin_libs@ \ - $(top_builddir)/libs/util/libQFutil.la - -libQFsound_la_LDFLAGS= $(lib_ldflags) -libQFsound_la_LIBADD= $(sound_libs) -libQFsound_la_DEPENDENCIES= $(sound_libs) -libQFsound_la_SOURCES= snd.c snd_progs.c - -libQFcd_la_LDFLAGS= $(lib_ldflags) -libQFcd_la_LIBADD= $(cd_libs) -libQFcd_la_DEPENDENCIES= $(cd_libs) -libQFcd_la_SOURCES= cd.c diff --git a/libs/audio/Makemodule.am b/libs/audio/Makemodule.am new file mode 100644 index 000000000..8c371c680 --- /dev/null +++ b/libs/audio/Makemodule.am @@ -0,0 +1,68 @@ +include libs/audio/targets/Makemodule.am +include libs/audio/renderer/Makemodule.am +include libs/audio/test/Makemodule.am + +plugin_LTLIBRARIES += @cd_plugins@ +noinst_LTLIBRARIES += @cd_static_plugins@ +lib_LTLIBRARIES += @CD_TARGETS@ @SND_TARGETS@ +EXTRA_LTLIBRARIES += \ + libs/audio/libQFsound.la \ + libs/audio/libQFcd.la \ + libs/audio/cd_file.la \ + libs/audio/cd_linux.la \ + libs/audio/cd_sdl.la \ + libs/audio/cd_sgi.la \ + libs/audio/cd_win.la \ + libs/audio/cd_xmms.la + +cd_plug_libs=libs/util/libQFutil.la + +cd_file_libs= \ + libs/audio/libQFsound.la \ + $(cd_plug_libs) + +libs_audio_cd_file_la_LDFLAGS= $(plugin_ldflags) +libs_audio_cd_file_la_LIBADD= $(cd_file_libs) +libs_audio_cd_file_la_SOURCES= libs/audio/cd_file.c + +libs_audio_cd_linux_la_LDFLAGS= $(plugin_ldflags) +libs_audio_cd_linux_la_LIBADD= $(cd_plug_libs) +libs_audio_cd_linux_la_SOURCES= libs/audio/cd_linux.c + +libs_audio_cd_sdl_la_LDFLAGS= $(plugin_ldflags) +libs_audio_cd_sdl_la_LIBADD= $(cd_plug_libs) $(SDL_LIBS) $(plugin_libadd) +libs_audio_cd_sdl_la_CFLAGS= $(SDL_CFLAGS) +libs_audio_cd_sdl_la_SOURCES= libs/audio/cd_sdl.c + +libs_audio_cd_sgi_la_LDFLAGS= $(plugin_ldflags) +libs_audio_cd_sgi_la_LIBADD= $(cd_plug_libs) $(SGI_CD_LIBS) +libs_audio_cd_sgi_la_SOURCES= libs/audio/cd_sgi.c + +libs_audio_cd_win_la_LDFLAGS= $(plugin_ldflags) +libs_audio_cd_win_la_LIBADD= $(cd_plug_libs) $(plugin_libadd) +libs_audio_cd_win_la_SOURCES= libs/audio/cd_win.c + +libs_audio_cd_xmms_la_LDFLAGS= $(plugin_ldflags) +libs_audio_cd_xmms_la_LIBADD= $(cd_plug_libs) $(XMMS_LIBS) +libs_audio_cd_xmms_la_CFLAGS= $(XMMS_CFLAGS) +libs_audio_cd_xmms_la_SOURCES= libs/audio/cd_xmms.c + +sound_libs= \ + @snd_output_static_plugin_libs@ \ + @snd_render_static_plugin_libs@ \ + libs/ruamoko/libQFruamoko.la \ + libs/util/libQFutil.la + +cd_libs= \ + @cd_static_plugin_libs@ \ + libs/util/libQFutil.la + +libs_audio_libQFsound_la_LDFLAGS= $(lib_ldflags) +libs_audio_libQFsound_la_DEPENDENCIES= $(sound_libs) +libs_audio_libQFsound_la_LIBADD= $(sound_libs) +libs_audio_libQFsound_la_SOURCES= libs/audio/snd.c libs/audio/snd_progs.c + +libs_audio_libQFcd_la_LDFLAGS= $(lib_ldflags) +libs_audio_libQFcd_la_DEPENDENCIES= $(cd_libs) +libs_audio_libQFcd_la_LIBADD= $(cd_libs) +libs_audio_libQFcd_la_SOURCES= libs/audio/cd.c diff --git a/libs/audio/cd.c b/libs/audio/cd.c index bf4245ba6..ea28429d6 100644 --- a/libs/audio/cd.c +++ b/libs/audio/cd.c @@ -40,7 +40,15 @@ #include "QF/plugin/general.h" #include "QF/plugin/cd.h" -cvar_t *cd_plugin; +char *cd_plugin; +static cvar_t cd_plugin_cvar = { + .name = "cd_plugin", + .description = + "CD Plugin to use", + .default_value = CD_DEFAULT, + .flags = CVAR_ROM, + .value = { .type = 0, .value = &cd_plugin }, +}; plugin_t *cdmodule = NULL; CD_PLUGIN_PROTOS @@ -52,64 +60,64 @@ VISIBLE void CDAudio_Pause (void) { if (cdmodule) - cdmodule->functions->cd->pCDAudio_Pause (); + cdmodule->functions->cd->pause (); } VISIBLE void -CDAudio_Play (int track, qboolean looping) +CDAudio_Play (int track, bool looping) { if (cdmodule) - cdmodule->functions->cd->pCDAudio_Play (track, looping); + cdmodule->functions->cd->play (track, looping); } VISIBLE void CDAudio_Resume (void) { if (cdmodule) - cdmodule->functions->cd->pCDAudio_Resume (); + cdmodule->functions->cd->resume (); } -VISIBLE void -CDAudio_Shutdown (void) +static void +CDAudio_shutdown (void *data) { if (cdmodule) - cdmodule->functions->general->p_Shutdown (); + cdmodule->functions->general->shutdown (); } VISIBLE void CDAudio_Update (void) { if (cdmodule) - cdmodule->functions->cd->pCDAudio_Update (); + cdmodule->functions->cd->update (); } static void CD_f (void) { if (cdmodule) - cdmodule->functions->cd->pCD_f (); + cdmodule->functions->cd->cd_f (); } VISIBLE int CDAudio_Init (void) { + Sys_RegisterShutdown (CDAudio_shutdown, 0); + PI_RegisterPlugins (cd_plugin_list); - cd_plugin = Cvar_Get ("cd_plugin", CD_DEFAULT, CVAR_ROM, NULL, - "CD Plugin to use"); + Cvar_Register (&cd_plugin_cvar, 0, 0); if (COM_CheckParm ("-nocdaudio")) return 0; - if (!*cd_plugin->string) { + if (!*cd_plugin) { Sys_Printf ("Not loading CD due to no driver\n"); return 0; } - cdmodule = PI_LoadPlugin ("cd", cd_plugin->string); + cdmodule = PI_LoadPlugin ("cd", cd_plugin); if (!cdmodule) { - Sys_Printf ("Loading of cd module: %s failed!\n", cd_plugin->string); + Sys_Printf ("Loading of cd module: %s failed!\n", cd_plugin); return -1; } - cdmodule->functions->general->p_Init (); Cmd_AddCommand ( "cd", CD_f, "Control the CD player.\n" "Commands:\n" diff --git a/libs/audio/cd_file.c b/libs/audio/cd_file.c index ce0f2b94f..530872376 100644 --- a/libs/audio/cd_file.c +++ b/libs/audio/cd_file.c @@ -53,8 +53,8 @@ #include "QF/cmd.h" #include "QF/cvar.h" #include "QF/dstring.h" +#include "QF/plist.h" #include "QF/qargs.h" -#include "QF/qfplist.h" #include "QF/quakefs.h" #include "QF/quakeio.h" #include "QF/sound.h" @@ -65,17 +65,16 @@ #include "QF/plugin/cd.h" #include "compat.h" -#include "snd_internal.h" /* Generic plugin structures */ static general_data_t plugin_info_general_data; static general_funcs_t plugin_info_general_funcs; /* global status variables. */ -static qboolean playing = false; -static qboolean wasPlaying = false; -static qboolean mus_enabled = false; -static qboolean ogglistvalid = false; +static bool playing = false; +static bool wasPlaying = false; +static bool mus_enabled = false; +static bool ogglistvalid = false; /* sound resources */ static channel_t *cd_channel; @@ -84,18 +83,31 @@ static plitem_t *play_list; // string or array of strings static int play_pos = -1; // position in play_list (0 for string) // -1 = invalid (both) -static cvar_t *bgmvolume; // volume cvar -static cvar_t *mus_ogglist; // tracklist cvar +static float bgmvolume; +static cvar_t bgmvolume_cvar = { + .name = "bgmvolume", + .description = + "Volume of CD music", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &bgmvolume }, +}; +static char *mus_ogglist; +static cvar_t mus_ogglist_cvar = { + .name = "mus_ogglist", + .description = + "filename of track to music file map", + .default_value = "tracklist.cfg", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &mus_ogglist }, +}; static void set_volume (void) { - if (cd_channel && cd_channel->sfx) { - int vol = bgmvolume->value * 255; - - cd_channel->master_vol = vol; - cd_channel->leftvol = cd_channel->rightvol = cd_channel->master_vol; + if (cd_channel) { + S_ChannelSetVolume (cd_channel, bgmvolume); } } @@ -120,7 +132,7 @@ I_OGGMus_Stop (void) wasPlaying = false; if (cd_channel) { - S_ChannelStop (cd_channel); + S_ChannelFree (cd_channel); cd_channel = NULL; } } @@ -130,7 +142,7 @@ I_OGGMus_Shutdown (void) { if (tracklist) { I_OGGMus_Stop (); - PL_Free (tracklist); + PL_Release (tracklist); tracklist = NULL; } mus_enabled = false; @@ -151,14 +163,14 @@ Load_Tracklist (void) ogglistvalid = false; mus_enabled = false; - if (!mus_ogglist || strequal (mus_ogglist->string, "none")) { + if (!mus_ogglist || strequal (mus_ogglist, "none")) { return -1; // bail if we don't have a valid filename } - oggfile = QFS_FOpenFile (mus_ogglist->string); + oggfile = QFS_FOpenFile (mus_ogglist); if (!oggfile) { Sys_Printf ("Mus_OggInit: open of file \"%s\" failed\n", - mus_ogglist->string); + mus_ogglist); return -1; } @@ -172,7 +184,8 @@ Load_Tracklist (void) buffile = calloc (size+10, sizeof (char)); Qread (oggfile, buffile, size); - tracklist = PL_GetPropertyList (buffile); + PL_Release (tracklist); + tracklist = PL_GetPropertyList (buffile, 0); if (!tracklist || PL_Type (tracklist) != QFDictionary) { Sys_Printf ("Malformed or empty tracklist file. check mus_ogglist\n"); return -1; @@ -189,7 +202,7 @@ Load_Tracklist (void) static void I_OGGMus_SetPlayList (int track) { - const char *trackstring = va ("%i", track); + const char *trackstring = va (0, "%i", track); int i; play_list = PL_ObjectForKey (tracklist, trackstring); @@ -218,8 +231,7 @@ static void I_OGGMus_PlayNext (int looping) { const char *track; - sfx_t *cd_sfx, *sfx; - wavinfo_t *info = 0; + sfx_t *sfx; if (!play_list) return; @@ -235,29 +247,21 @@ I_OGGMus_PlayNext (int looping) } if (cd_channel) { - S_ChannelStop (cd_channel); + S_ChannelFree (cd_channel); cd_channel = 0; } if (!(cd_channel = S_AllocChannel ())) return; - if (!(cd_sfx = S_LoadSound (track)) || !(sfx = cd_sfx->open (cd_sfx))) { - S_ChannelStop (cd_channel); + if (!(sfx = S_LoadSound (track)) || !S_ChannelSetSfx (cd_channel, sfx)) { + S_ChannelFree (cd_channel); cd_channel = 0; return; } - Sys_Printf ("Playing: %s.\n", track); - if (sfx->wavinfo) - info = sfx->wavinfo (sfx); - if (info) { - if (looping == true) - info->loopstart = 0; - else - info->loopstart = -1; - } - cd_channel->sfx = sfx; + S_ChannelSetLooping (cd_channel, looping ? 1 : -1); set_volume (); + Sys_Printf ("Playing: %s.\n", track); playing = true; } @@ -269,7 +273,7 @@ I_OGGMus_Pause (void) return; if (cd_channel) - cd_channel->pause = 1; + S_ChannelSetPaused (cd_channel, 1); wasPlaying = playing; playing = false; @@ -282,7 +286,7 @@ I_OGGMus_Resume (void) return; set_volume (); - cd_channel->pause = 0; + S_ChannelSetPaused (cd_channel, 0); wasPlaying = false; playing = true; } @@ -290,7 +294,7 @@ I_OGGMus_Resume (void) /* start playing, if we've got a play_list. * cry if we can't find a file to play */ static void -I_OGGMus_Play (int track, qboolean looping) +I_OGGMus_Play (int track, bool looping) { /* alrighty. grab the list, map track to filename. grab filename from data resources, attach sound to play, loop. */ @@ -323,11 +327,11 @@ I_OGGMus_Info (void) return; Sys_Printf ("\n" "Tracklist loaded from file:\n%s\n" - "---------------------------\n", mus_ogglist->string); + "---------------------------\n", mus_ogglist); /* loop, and count up the Highest key number. */ for (iter = 1, count = 0; count < keycount && iter <= 99 ; iter++) { - trackstring = va ("%i", iter); + trackstring = va (0, "%i", iter); if (!(currenttrack = PL_ObjectForKey (tracklist, trackstring))) { continue; } @@ -429,7 +433,7 @@ I_OGG_f (void) static void I_OGGMus_Update (void) { - if (!cd_channel || !cd_channel->done) + if (!cd_channel || S_ChannelGetState (cd_channel) > chan_done) return; // will get here only when multi-tracked I_OGGMus_Stop (); @@ -438,36 +442,32 @@ I_OGGMus_Update (void) /* called when the mus_ogglist cvar is changed */ static void -Mus_OggChange (cvar_t *ogglist) +Mus_OggChange (void *data, const cvar_t *cvar) { - mus_ogglist = ogglist; Load_Tracklist (); } /* change volume on sound object */ static void -Mus_VolChange (cvar_t *bgmvolume) +Mus_VolChange (void *data, const cvar_t *bgmvolume) { set_volume (); } static void -Mus_gamedir (int phase) +Mus_gamedir (int phase, void *data) { - if (phase); - Mus_OggChange (mus_ogglist); + if (phase) + Load_Tracklist (); } static void I_OGGMus_Init (void) { /* check list file cvar, open list file, create map, close file. */ - mus_ogglist = Cvar_Get ("mus_ogglist", "tracklist.cfg", CVAR_NONE, - Mus_OggChange, - "filename of track to music file map"); - bgmvolume = Cvar_Get ("bgmvolume", "1.0", CVAR_ARCHIVE, Mus_VolChange, - "Volume of CD music"); - QFS_GamedirCallback (Mus_gamedir); + Cvar_Register (&mus_ogglist_cvar, Mus_OggChange, 0); + Cvar_Register (&bgmvolume_cvar, Mus_VolChange, 0); + QFS_GamedirCallback (Mus_gamedir, 0); } static general_funcs_t plugin_info_general_funcs = { @@ -476,6 +476,7 @@ static general_funcs_t plugin_info_general_funcs = { }; static cd_funcs_t plugin_info_cd_funcs = { + 0, I_OGG_f, I_OGGMus_Pause, I_OGGMus_Play, diff --git a/libs/audio/cd_linux.c b/libs/audio/cd_linux.c index ff66356df..23c93b01e 100644 --- a/libs/audio/cd_linux.c +++ b/libs/audio/cd_linux.c @@ -66,19 +66,35 @@ static general_data_t plugin_info_general_data; static general_funcs_t plugin_info_general_funcs; static cd_funcs_t plugin_info_cd_funcs; -static qboolean cdValid = false; -static qboolean playing = false; -static qboolean wasPlaying = false; -static qboolean mus_enabled = false; -static qboolean playLooping = false; +static bool cdValid = false; +static bool playing = false; +static bool wasPlaying = false; +static bool mus_enabled = false; +static bool playLooping = false; static float cdvolume; static byte remap[100]; static byte playTrack; static byte maxTrack; static int cdfile = -1; -static cvar_t *mus_cddevice; -static cvar_t *bgmvolume; +static char *mus_cddevice; +static cvar_t mus_cddevice_cvar = { + .name = "mus_cddevice", + .description = + "device to use for CD music", + .default_value = "/dev/cdrom", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &mus_cddevice }, +}; +static float bgmvolume; +static cvar_t bgmvolume_cvar = { + .name = "bgmvolume", + .description = + "Volume of CD music", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &bgmvolume }, +}; static void @@ -88,7 +104,7 @@ I_CDAudio_CloseDoor (void) return; // no cd init'd if (ioctl (cdfile, CDROMCLOSETRAY) == -1) - Sys_MaskPrintf (SYS_SND, "CDAudio: ioctl cdromclosetray failed\n"); + Sys_MaskPrintf (SYS_snd, "CDAudio: ioctl cdromclosetray failed\n"); } static void @@ -98,7 +114,7 @@ I_CDAudio_Eject (void) return; // no cd init'd if (ioctl (cdfile, CDROMEJECT) == -1) - Sys_MaskPrintf (SYS_SND, "CDAudio: ioctl cdromeject failed\n"); + Sys_MaskPrintf (SYS_snd, "CDAudio: ioctl cdromeject failed\n"); } static int @@ -109,12 +125,12 @@ I_CDAudio_GetAudioDiskInfo (void) cdValid = false; if (ioctl (cdfile, CDROMREADTOCHDR, &tochdr) == -1) { - Sys_MaskPrintf (SYS_SND, "CDAudio: ioctl cdromreadtochdr failed\n"); + Sys_MaskPrintf (SYS_snd, "CDAudio: ioctl cdromreadtochdr failed\n"); return -1; } if (tochdr.cdth_trk0 < 1) { - Sys_MaskPrintf (SYS_SND, "CDAudio: no music tracks\n"); + Sys_MaskPrintf (SYS_snd, "CDAudio: no music tracks\n"); return -1; } @@ -134,7 +150,7 @@ I_CDAudio_Pause (void) return; if (ioctl (cdfile, CDROMPAUSE) == -1) - Sys_MaskPrintf (SYS_SND, "CDAudio: ioctl cdrompause failed\n"); + Sys_MaskPrintf (SYS_snd, "CDAudio: ioctl cdrompause failed\n"); wasPlaying = playing; playing = false; @@ -150,7 +166,7 @@ I_CDAudio_Stop (void) return; if (ioctl (cdfile, CDROMSTOP) == -1) - Sys_MaskPrintf (SYS_SND, "CDAudio: ioctl cdromstop failed (%d)\n", + Sys_MaskPrintf (SYS_snd, "CDAudio: ioctl cdromstop failed (%d)\n", errno); wasPlaying = false; @@ -158,7 +174,7 @@ I_CDAudio_Stop (void) } static void -I_CDAudio_Play (int track, qboolean looping) +I_CDAudio_Play (int track, bool looping) { struct cdrom_tocentry entry0; struct cdrom_tocentry entry1; @@ -188,7 +204,7 @@ I_CDAudio_Play (int track, qboolean looping) entry0.cdte_track = track; entry0.cdte_format = CDROM_MSF; if (ioctl (cdfile, CDROMREADTOCENTRY, &entry0) == -1) { - Sys_MaskPrintf (SYS_SND, "CDAudio: ioctl cdromreadtocentry failed\n"); + Sys_MaskPrintf (SYS_snd, "CDAudio: ioctl cdromreadtocentry failed\n"); return; } entry1.cdte_track = track + 1; @@ -197,7 +213,7 @@ I_CDAudio_Play (int track, qboolean looping) entry1.cdte_track = CDROM_LEADOUT; } if (ioctl (cdfile, CDROMREADTOCENTRY, &entry1) == -1) { - Sys_MaskPrintf (SYS_SND, "CDAudio: ioctl cdromreadtocentry failed\n"); + Sys_MaskPrintf (SYS_snd, "CDAudio: ioctl cdromreadtocentry failed\n"); return; } if (entry0.cdte_ctrl == CDROM_DATA_TRACK) { @@ -219,14 +235,14 @@ I_CDAudio_Play (int track, qboolean looping) msf.cdmsf_sec1 = entry1.cdte_addr.msf.second; msf.cdmsf_frame1 = entry1.cdte_addr.msf.frame; - Sys_MaskPrintf (SYS_SND, "%2d:%02d:%02d %2d:%02d:%02d\n", + Sys_MaskPrintf (SYS_snd, "%2d:%02d:%02d %2d:%02d:%02d\n", msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0, msf.cdmsf_min1, msf.cdmsf_sec1, msf.cdmsf_frame1); if (ioctl (cdfile, CDROMPLAYMSF, &msf) == -1) { - Sys_MaskPrintf (SYS_SND, + Sys_MaskPrintf (SYS_snd, "CDAudio: ioctl cdromplaytrkind failed (%s)\n", strerror (errno)); return; @@ -253,7 +269,7 @@ I_CDAudio_Resume (void) return; if (ioctl (cdfile, CDROMRESUME) == -1) - Sys_MaskPrintf (SYS_SND, "CDAudio: ioctl cdromresume failed\n"); + Sys_MaskPrintf (SYS_snd, "CDAudio: ioctl cdromresume failed\n"); playing = true; } @@ -382,14 +398,14 @@ I_CDAudio_Update (void) if (!mus_enabled) return; - if (bgmvolume->value != cdvolume) { + if (bgmvolume != cdvolume) { if (cdvolume) { - Cvar_SetValue (bgmvolume, 0.0); - cdvolume = bgmvolume->value; + bgmvolume = 0.0; + cdvolume = bgmvolume; I_CDAudio_Pause (); } else { - Cvar_SetValue (bgmvolume, 1.0); - cdvolume = bgmvolume->value; + bgmvolume = 1.0; + cdvolume = bgmvolume; I_CDAudio_Resume (); } } @@ -398,7 +414,7 @@ I_CDAudio_Update (void) lastchk = time (NULL) + 2; // two seconds between chks subchnl.cdsc_format = CDROM_MSF; if (ioctl (cdfile, CDROMSUBCHNL, &subchnl) == -1) { - Sys_MaskPrintf (SYS_SND, "CDAudio: ioctl cdromsubchnl failed\n"); + Sys_MaskPrintf (SYS_snd, "CDAudio: ioctl cdromsubchnl failed\n"); playing = false; return; } @@ -412,20 +428,20 @@ I_CDAudio_Update (void) } static void -Mus_CDChange (cvar_t *mus_cdaudio) +Mus_CDChange (void *data, const cvar_t *cvar) { int i; I_CDAudio_Shutdown (); - if (strequal (mus_cdaudio->string, "none")) { + if (strequal (mus_cddevice, "none")) { return; } - cdfile = open (mus_cdaudio->string, O_RDONLY | O_NONBLOCK); + cdfile = open (mus_cddevice, O_RDONLY | O_NONBLOCK); if (cdfile == -1) { - Sys_MaskPrintf (SYS_SND, + Sys_MaskPrintf (SYS_snd, "Mus_CDInit: open device \"%s\" failed (error %i)\n", - mus_cdaudio->string, errno); + mus_cddevice, errno); return; } @@ -443,10 +459,8 @@ Mus_CDChange (cvar_t *mus_cdaudio) static void I_CDAudio_Init (void) { - mus_cddevice = Cvar_Get ("mus_cddevice", "/dev/cdrom", CVAR_NONE, - Mus_CDChange, "device to use for CD music"); - bgmvolume = Cvar_Get ("bgmvolume", "1", CVAR_ARCHIVE, NULL, - "Volume of CD music"); + Cvar_Register (&mus_cddevice_cvar, Mus_CDChange, 0); + Cvar_Register (&bgmvolume_cvar, 0, 0); } static general_funcs_t plugin_info_general_funcs = { @@ -455,6 +469,7 @@ static general_funcs_t plugin_info_general_funcs = { }; static cd_funcs_t plugin_info_cd_funcs = { + 0, I_CD_f, I_CDAudio_Pause, I_CDAudio_Play, diff --git a/libs/audio/cd_sdl.c b/libs/audio/cd_sdl.c index 9a0da6c3d..44fe6f01b 100644 --- a/libs/audio/cd_sdl.c +++ b/libs/audio/cd_sdl.c @@ -56,15 +56,23 @@ static general_data_t plugin_info_general_data; static general_funcs_t plugin_info_general_funcs; static cd_funcs_t plugin_info_cd_funcs; -static qboolean cdValid = false; -static qboolean initialized = false; -static qboolean enabled = true; -static qboolean playLooping = false; +static bool cdValid = false; +static bool initialized = false; +static bool enabled = true; +static bool playLooping = false; static SDL_CD *cd_id; static float cdvolume = 1.0; -static cvar_t *bgmvolume; +static float bgmvolume; +static cvar_t bgmvolume_cvar = { + .name = "bgmvolume", + .description = + "Volume of CD music", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &bgmvolume }, +}; static void @@ -74,7 +82,7 @@ I_CDAudio_Eject (void) return; if (SDL_CDEject (cd_id)) - Sys_MaskPrintf (SYS_SND, "Unable to eject CD-ROM tray.\n"); + Sys_MaskPrintf (SYS_snd, "Unable to eject CD-ROM tray.\n"); } static void @@ -86,7 +94,7 @@ I_CDAudio_Pause (void) return; if (SDL_CDPause (cd_id)) - Sys_MaskPrintf (SYS_SND, "CDAudio_Pause: Failed to pause track.\n"); + Sys_MaskPrintf (SYS_snd, "CDAudio_Pause: Failed to pause track.\n"); } static void @@ -101,11 +109,11 @@ I_CDAudio_Stop (void) return; if (SDL_CDStop (cd_id)) - Sys_MaskPrintf (SYS_SND, "CDAudio_Stop: Failed to stop track.\n"); + Sys_MaskPrintf (SYS_snd, "CDAudio_Stop: Failed to stop track.\n"); } static void -I_CDAudio_Play (int track, qboolean looping) +I_CDAudio_Play (int track, bool looping) { /* Initialize cd_stat to avoid warning */ /* XXX - Does this default value make sense? */ @@ -134,7 +142,7 @@ I_CDAudio_Play (int track, qboolean looping) if (SDL_CDPlay (cd_id, cd_id->track[track].offset, cd_id->track[track].length)) { - Sys_MaskPrintf (SYS_SND, "CDAudio_Play: Unable to play track: %d\n", + Sys_MaskPrintf (SYS_snd, "CDAudio_Play: Unable to play track: %d\n", track + 1); return; } @@ -150,7 +158,7 @@ I_CDAudio_Resume (void) return; if (SDL_CDResume (cd_id)) - Sys_MaskPrintf (SYS_SND, "CDAudio_Resume: Failed to resume track.\n"); + Sys_MaskPrintf (SYS_snd, "CDAudio_Resume: Failed to resume track.\n"); } static void @@ -168,15 +176,15 @@ I_CDAudio_Update (void) { if (!cd_id || !enabled) return; - if (bgmvolume->value != cdvolume) { + if (bgmvolume != cdvolume) { if (cdvolume) { - Cvar_SetValue (bgmvolume, 0.0); + bgmvolume = 0.0; I_CDAudio_Pause (); } else { - Cvar_SetValue (bgmvolume, 1.0); + bgmvolume = 1.0; I_CDAudio_Resume (); } - cdvolume = bgmvolume->value; + cdvolume = bgmvolume; return; } if (playLooping && (SDL_CDStatus (cd_id) != CD_PLAYING) @@ -276,8 +284,7 @@ I_CDAudio_Init (void) cdValid = false; } - bgmvolume = Cvar_Get ("bgmvolume", "1", CVAR_ARCHIVE, NULL, - "Volume of CD music"); + Cvar_Register (&bgmvolume_cvar, 0, 0); Sys_Printf ("CD Audio Initialized.\n"); } @@ -288,6 +295,7 @@ static general_funcs_t plugin_info_general_funcs = { }; static cd_funcs_t plugin_info_cd_funcs = { + 0, I_CD_f, I_CDAudio_Pause, I_CDAudio_Play, diff --git a/libs/audio/cd_sgi.c b/libs/audio/cd_sgi.c index 1b9631aab..c8e0f4065 100644 --- a/libs/audio/cd_sgi.c +++ b/libs/audio/cd_sgi.c @@ -53,9 +53,9 @@ static general_funcs_t plugin_info_general_funcs; static cd_funcs_t plugin_info_cd_funcs; -static qboolean initialized = false; -static qboolean enabled = true; -static qboolean playLooping = false; +static bool initialized = false; +static bool enabled = true; +static bool playLooping = false; static float cdvolume; static byte remap[100]; static byte playTrack; @@ -63,7 +63,15 @@ static byte playTrack; static char cd_dev[] = "/dev/cdrom"; static CDPLAYER *cdp = NULL; -static cvar_t *bgmvolume; +static float bgmvolume; +static cvar_t bgmvolume_cvar = { + .name = "bgmvolume", + .description = + "Volume of CD music", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &bgmvolume }, +}; static void I_SGI_Eject (void) @@ -72,7 +80,7 @@ I_SGI_Eject (void) return; // no cd init'd if (CDeject (cdp) == 0) - Sys_MaskPrintf (SYS_SND, "I_SGI_Eject: CDeject failed\n"); + Sys_MaskPrintf (SYS_snd, "I_SGI_Eject: CDeject failed\n"); } static int @@ -84,7 +92,7 @@ I_SGI_GetState (void) return -1; // no cd init'd if (CDgetstatus (cdp, &cds) == 0) { - Sys_MaskPrintf (SYS_SND, "CDAudio_GetStatus: CDgetstatus failed\n"); + Sys_MaskPrintf (SYS_snd, "CDAudio_GetStatus: CDgetstatus failed\n"); return -1; } @@ -100,7 +108,7 @@ I_SGI_MaxTrack (void) return -1; // no cd init'd if (CDgetstatus (cdp, &cds) == 0) { - Sys_MaskPrintf (SYS_SND, "I_SGI_MaxTrack: CDgetstatus failed\n"); + Sys_MaskPrintf (SYS_snd, "I_SGI_MaxTrack: CDgetstatus failed\n"); return -1; } @@ -114,11 +122,11 @@ I_SGI_Pause (void) return; if (CDtogglepause (cdp) == 0) - Sys_MaskPrintf (SYS_SND, "CDAudio_PAUSE: CDtogglepause failed (%d)\n", errno); + Sys_MaskPrintf (SYS_snd, "CDAudio_PAUSE: CDtogglepause failed (%d)\n", errno); } void -I_SGI_Play (int track, qboolean looping) +I_SGI_Play (int track, bool looping) { int maxtrack = I_SGI_MaxTrack (); @@ -132,7 +140,7 @@ I_SGI_Play (int track, qboolean looping) } if (maxtrack < 0) { - Sys_MaskPrintf (SYS_SND, + Sys_MaskPrintf (SYS_snd, "CDAudio_Play: Error getting maximum track number\n"); return; } @@ -172,7 +180,7 @@ I_SGI_Play (int track, qboolean looping) } if (CDplaytrack (cdp, track, cdvolume == 0.0 ? 0 : 1) == 0) { - Sys_MaskPrintf (SYS_SND, "CDAudio_Play: CDplay failed (%d)\n", errno); + Sys_MaskPrintf (SYS_snd, "CDAudio_Play: CDplay failed (%d)\n", errno); return; } @@ -187,7 +195,7 @@ I_SGI_Resume (void) return; if (CDtogglepause (cdp) == 0) - Sys_MaskPrintf (SYS_SND, "CDAudio_Resume: CDtogglepause failed (%d)\n", + Sys_MaskPrintf (SYS_snd, "CDAudio_Resume: CDtogglepause failed (%d)\n", errno); } @@ -210,7 +218,7 @@ I_SGI_Stop (void) return; if (CDstop (cdp) == 0) - Sys_MaskPrintf (SYS_SND, "I_SGI_Stop: CDStop failed (%d)\n", errno); + Sys_MaskPrintf (SYS_snd, "I_SGI_Stop: CDStop failed (%d)\n", errno); } void @@ -219,14 +227,14 @@ I_SGI_Update (void) if (!initialized || !enabled) return; - if (bgmvolume->value != cdvolume) { + if (bgmvolume != cdvolume) { if (cdvolume) { - Cvar_SetValue (bgmvolume, 0.0); - cdvolume = bgmvolume->value; + bgmvolume = 0.0; + cdvolume = bgmvolume; CDAudio_Pause (); } else { - Cvar_SetValue (bgmvolume, 1.0); - cdvolume = bgmvolume->value; + bgmvolume = 1.0; + cdvolume = bgmvolume; CDAudio_Resume (); } } @@ -334,8 +342,7 @@ I_SGI_Init (void) { int i; - bgmvolume = Cvar_Get ("bgmvolume", "1", CVAR_ARCHIVE, NULL, - "Volume of CD music"); + Cvar_Register (&bgmvolume_cvar, 0, 0); if ((i = COM_CheckParm ("-cddev")) != 0 && i < com_argc - 1) { strncpy (cd_dev, com_argv[i + 1], sizeof (cd_dev)); cd_dev[sizeof (cd_dev) - 1] = 0; @@ -364,6 +371,7 @@ static general_funcs_t plugin_info_general_funcs = { }; static cd_funcs_t plugin_info_cd_funcs = { + 0, I_SGI_f, I_SGI_Pause, I_SGI_Play, diff --git a/libs/audio/cd_win.c b/libs/audio/cd_win.c index 93f29e624..1be03d248 100644 --- a/libs/audio/cd_win.c +++ b/libs/audio/cd_win.c @@ -28,8 +28,6 @@ # include "config.h" #endif -#include "winquake.h" - #include "QF/cdaudio.h" #include "QF/cmd.h" #include "QF/cvar.h" @@ -41,6 +39,7 @@ #include "QF/plugin/cd.h" #include "compat.h" +#include "context_win.h" static plugin_t plugin_info; static plugin_data_t plugin_info_data; @@ -49,12 +48,12 @@ static general_data_t plugin_info_general_data; static general_funcs_t plugin_info_general_funcs; static cd_funcs_t plugin_info_cd_funcs; -static qboolean cdValid = false; -static qboolean playing = false; -static qboolean wasPlaying = false; -static qboolean initialized = false; -static qboolean enabled = false; -static qboolean playLooping = false; +static bool cdValid = false; +static bool playing = false; +static bool wasPlaying = false; +static bool initialized = false; +static bool enabled = false; +static bool playLooping = false; static float cdvolume; static byte remap[100]; static byte playTrack; @@ -62,10 +61,18 @@ static byte maxTrack; static UINT wDeviceID; -static void I_CDAudio_Play (int track, qboolean looping); +static void I_CDAudio_Play (int track, bool looping); static void I_CDAudio_Stop (void); -static cvar_t *bgmvolume; +static float bgmvolume; +static cvar_t bgmvolume_cvar = { + .name = "bgmvolume", + .description = + "Volume of CD music", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &bgmvolume }, +}; static void @@ -76,7 +83,7 @@ I_CDAudio_CloseDoor (void) dwReturn = mciSendCommand (wDeviceID, MCI_SET, MCI_SET_DOOR_CLOSED, (DWORD_PTR) NULL); if (dwReturn) { - Sys_MaskPrintf (SYS_SND, "MCI_SET_DOOR_CLOSED failed (%li)\n", + Sys_MaskPrintf (SYS_snd, "MCI_SET_DOOR_CLOSED failed (%li)\n", dwReturn); } } @@ -89,7 +96,7 @@ I_CDAudio_Eject (void) dwReturn = mciSendCommand (wDeviceID, MCI_SET, MCI_SET_DOOR_OPEN, (DWORD_PTR) NULL); if (dwReturn) { - Sys_MaskPrintf (SYS_SND, "MCI_SET_DOOR_OPEN failed (%li)\n", dwReturn); + Sys_MaskPrintf (SYS_snd, "MCI_SET_DOOR_OPEN failed (%li)\n", dwReturn); } } @@ -106,12 +113,12 @@ I_CDAudio_GetAudioDiskInfo (void) mciSendCommand (wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR) (LPVOID) & mciStatusParms); if (dwReturn) { - Sys_MaskPrintf (SYS_SND, + Sys_MaskPrintf (SYS_snd, "CDAudio: drive ready test - get status failed\n"); return -1; } if (!mciStatusParms.dwReturn) { - Sys_MaskPrintf (SYS_SND, "CDAudio: drive not ready\n"); + Sys_MaskPrintf (SYS_snd, "CDAudio: drive not ready\n"); return -1; } @@ -120,11 +127,11 @@ I_CDAudio_GetAudioDiskInfo (void) mciSendCommand (wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR) (LPVOID) & mciStatusParms); if (dwReturn) { - Sys_MaskPrintf (SYS_SND, "CDAudio: get tracks - status failed\n"); + Sys_MaskPrintf (SYS_snd, "CDAudio: get tracks - status failed\n"); return -1; } if (mciStatusParms.dwReturn < 1) { - Sys_MaskPrintf (SYS_SND, "CDAudio: no music tracks\n"); + Sys_MaskPrintf (SYS_snd, "CDAudio: no music tracks\n"); return -1; } @@ -155,13 +162,13 @@ static I_CDAudio_MessageHandler (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPa break; case MCI_NOTIFY_FAILURE: - Sys_MaskPrintf (SYS_SND, "MCI_NOTIFY_FAILURE\n"); + Sys_MaskPrintf (SYS_snd, "MCI_NOTIFY_FAILURE\n"); I_CDAudio_Stop (); cdValid = false; break; default: - Sys_MaskPrintf (SYS_SND, "Unexpected MM_MCINOTIFY type (%i)\n", + Sys_MaskPrintf (SYS_snd, "Unexpected MM_MCINOTIFY type (%i)\n", wParam); return 1; } @@ -181,12 +188,12 @@ I_CDAudio_Pause (void) if (!playing) return; - mciGenericParms.dwCallback = (DWORD_PTR) mainwindow; + mciGenericParms.dwCallback = (DWORD_PTR) win_mainwindow; dwReturn = mciSendCommand (wDeviceID, MCI_PAUSE, 0, (DWORD_PTR) (LPVOID) & mciGenericParms); if (dwReturn) { - Sys_MaskPrintf (SYS_SND, "MCI_PAUSE failed (%li)", dwReturn); + Sys_MaskPrintf (SYS_snd, "MCI_PAUSE failed (%li)", dwReturn); } wasPlaying = playing; @@ -194,7 +201,7 @@ I_CDAudio_Pause (void) } static void -I_CDAudio_Play (int track, qboolean looping) +I_CDAudio_Play (int track, bool looping) { DWORD dwReturn; MCI_PLAY_PARMS mciPlayParms; @@ -227,7 +234,7 @@ I_CDAudio_Play (int track, qboolean looping) MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD_PTR) (LPVOID) & mciStatusParms); if (dwReturn) { - Sys_MaskPrintf (SYS_SND, "MCI_STATUS failed (%li)\n", dwReturn); + Sys_MaskPrintf (SYS_snd, "MCI_STATUS failed (%li)\n", dwReturn); return; } if (mciStatusParms.dwReturn != MCI_CDA_TRACK_AUDIO) { @@ -242,7 +249,7 @@ I_CDAudio_Play (int track, qboolean looping) MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD_PTR) (LPVOID) & mciStatusParms); if (dwReturn) { - Sys_MaskPrintf (SYS_SND, "MCI_STATUS failed (%li)\n", dwReturn); + Sys_MaskPrintf (SYS_snd, "MCI_STATUS failed (%li)\n", dwReturn); return; } @@ -254,12 +261,12 @@ I_CDAudio_Play (int track, qboolean looping) mciPlayParms.dwFrom = MCI_MAKE_TMSF (track, 0, 0, 0); mciPlayParms.dwTo = (mciStatusParms.dwReturn << 8) | track; - mciPlayParms.dwCallback = (DWORD_PTR) mainwindow; + mciPlayParms.dwCallback = (DWORD_PTR) win_mainwindow; dwReturn = mciSendCommand (wDeviceID, MCI_PLAY, MCI_NOTIFY | MCI_FROM | MCI_TO, (DWORD_PTR) (LPVOID) & mciPlayParms); if (dwReturn) { - Sys_MaskPrintf (SYS_SND, "CDAudio: MCI_PLAY failed (%li)\n", dwReturn); + Sys_MaskPrintf (SYS_snd, "CDAudio: MCI_PLAY failed (%li)\n", dwReturn); return; } @@ -286,12 +293,12 @@ I_CDAudio_Resume (void) mciPlayParms.dwFrom = MCI_MAKE_TMSF (playTrack, 0, 0, 0); mciPlayParms.dwTo = MCI_MAKE_TMSF (playTrack + 1, 0, 0, 0); - mciPlayParms.dwCallback = (DWORD_PTR) mainwindow; + mciPlayParms.dwCallback = (DWORD_PTR) win_mainwindow; dwReturn = mciSendCommand (wDeviceID, MCI_PLAY, MCI_TO | MCI_NOTIFY, (DWORD_PTR) (LPVOID) & mciPlayParms); if (dwReturn) { - Sys_MaskPrintf (SYS_SND, "CDAudio: MCI_PLAY failed (%li)\n", dwReturn); + Sys_MaskPrintf (SYS_snd, "CDAudio: MCI_PLAY failed (%li)\n", dwReturn); return; } playing = true; @@ -304,7 +311,7 @@ I_CDAudio_Shutdown (void) return; I_CDAudio_Stop (); if (mciSendCommand (wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD_PTR) NULL)) - Sys_MaskPrintf (SYS_SND, "CDAudio_Shutdown: MCI_CLOSE failed\n"); + Sys_MaskPrintf (SYS_snd, "CDAudio_Shutdown: MCI_CLOSE failed\n"); } static void @@ -319,7 +326,7 @@ I_CDAudio_Stop (void) dwReturn = mciSendCommand (wDeviceID, MCI_STOP, 0, (DWORD_PTR) NULL); if (dwReturn) { - Sys_MaskPrintf (SYS_SND, "MCI_STOP failed (%li)", dwReturn); + Sys_MaskPrintf (SYS_snd, "MCI_STOP failed (%li)", dwReturn); } wasPlaying = false; @@ -332,14 +339,14 @@ I_CDAudio_Update (void) if (!enabled) return; - if (bgmvolume->value != cdvolume) { + if (bgmvolume != cdvolume) { if (cdvolume) { - Cvar_SetValue (bgmvolume, 0.0); - cdvolume = bgmvolume->value; + bgmvolume = 0.0; + cdvolume = bgmvolume; I_CDAudio_Pause (); } else { - Cvar_SetValue (bgmvolume, 1.0); - cdvolume = bgmvolume->value; + bgmvolume = 1.0; + cdvolume = bgmvolume; I_CDAudio_Resume (); } } @@ -484,8 +491,7 @@ I_CDAudio_Init (void) initialized = true; enabled = true; - bgmvolume = Cvar_Get ("bgmvolume", "1", CVAR_ARCHIVE, NULL, - "Volume of CD music"); + Cvar_Register (&bgmvolume_cvar, 0, 0); if (I_CDAudio_GetAudioDiskInfo ()) { Sys_Printf ("CDAudio_Init: No CD in player.\n"); @@ -500,6 +506,7 @@ static general_funcs_t plugin_info_general_funcs = { }; static cd_funcs_t plugin_info_cd_funcs = { + 0, I_CD_f, I_CDAudio_Pause, I_CDAudio_Play, diff --git a/libs/audio/cd_xmms.c b/libs/audio/cd_xmms.c index 6ed35d2cf..3fa30a04e 100644 --- a/libs/audio/cd_xmms.c +++ b/libs/audio/cd_xmms.c @@ -43,9 +43,6 @@ #ifdef HAVE_SYS_IOCTL_H # include #endif -#ifdef HAVE_SIGNAL_H -# include -#endif #include #include @@ -82,15 +79,15 @@ static int sessionNo; //static int xmmsPid = '0'; //static int sigNo = '2'; -static qboolean playing = false; +static bool playing = false; // no idea why I have wasPlaying, pbly this code was based on cd_linux.c :/ -static qboolean wasPlaying = false; -static qboolean musEnabled = true; +static bool wasPlaying = false; +static bool musEnabled = true; static void I_XMMS_Running(void); static void I_XMMS_Stop(void); -static void I_XMMS_Play(int, qboolean); +static void I_XMMS_Play(int, bool); static void I_XMMS_Pause(void); static void I_XMMS_Resume(void); static void I_XMMS_Next(void); @@ -151,7 +148,7 @@ I_XMMS_Running (void) break; case -1: // ICH! // inform user - Sys_MaskPrintf (SYS_SND, "XMMSAudio: error, can't fork!?\n"); + Sys_MaskPrintf (SYS_snd, "XMMSAudio: error, can't fork!?\n"); break; default: // Parent // don't need now :/ @@ -181,7 +178,7 @@ I_XMMS_Stop (void) // stop playing // Play // start it playing, (unless disabled) static void -I_XMMS_Play (int track, qboolean looping) // looping for compatability +I_XMMS_Play (int track, bool looping) // looping for compatability { if (!musEnabled) return; @@ -492,6 +489,7 @@ static general_funcs_t plugin_info_general_funcs = { }; static cd_funcs_t plugin_info_cd_funcs = { + 0, I_XMMS_f, I_XMMS_Pause, I_XMMS_Play, diff --git a/libs/audio/renderer/Makefile.am b/libs/audio/renderer/Makefile.am deleted file mode 100644 index 0c3bcd719..000000000 --- a/libs/audio/renderer/Makefile.am +++ /dev/null @@ -1,55 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CFLAGS= @PREFER_PIC@ \ - $(VORBIS_CFLAGS) $(OGG_CFLAGS) $(SAMPLERATE_CFLAGS) $(JACK_CFLAGS) -AM_CPPFLAGS= -I$(top_srcdir)/include -plugin_ldflags= @plugin_ldflags@ -avoid-version -module -rpath $(plugindir) -plugin_libadd= @plugin_libadd@ -EXEEXT= - -plugin_LTLIBRARIES= @snd_render_plugins@ -noinst_LTLIBRARIES= @snd_render_static_plugins@ -EXTRA_LTLIBRARIES= snd_render_default.la snd_render_jack.la - -flac_src=flac.c -midi_src=midi.c -vorbis_src=vorbis.c -wav_src=wav.c - -if HAVE_FLAC -have_flac_src=$(flac_src) -else -have_flac_src= -endif -if HAVE_MIDI -have_midi_src=$(midi_src) -else -have_midi_src= -endif -if HAVE_VORBIS -have_vorbis_src=$(vorbis_src) -else -have_vorbis_src= -endif -have_wav_src=$(wav_src) - -format_src=$(have_flac_src) $(have_midi_src) $(have_vorbis_src) $(have_wav_src) -format_libs= \ - $(SAMPLERATE_LIBS) $(VORBISFILE_LIBS) $(VORBIS_LIBS) $(FLAC_LIBS) \ - $(OGG_LIBS) $(WM_LIBS) -extra_format_src=flac.c midi.c vorbis.c wav.c -snd_common=snd_channels.c snd_mem.c snd_mix.c snd_resample.c snd_sfx.c -snd_libs= \ - $(top_builddir)/libs/util/libQFutil.la - -snd_render_default_la_LDFLAGS= $(plugin_ldflags) -snd_render_default_la_SOURCES= snd_dma.c $(snd_common) $(format_src) -snd_render_default_la_LIBADD= $(snd_libs) $(format_libs) -snd_render_default_la_DEPENDENCIES= $(snd_libs) -EXTRA_snd_render_default_la_SOURCES=$(extra_format_src) - -snd_render_jack_la_LDFLAGS= $(plugin_ldflags) -snd_render_jack_la_SOURCES= snd_jack.c $(snd_common) $(format_src) -snd_render_jack_la_LIBADD= $(snd_libs) $(format_libs) $(JACK_LIBS) -snd_render_jack_la_DEPENDENCIES= $(snd_libs) -EXTRA_snd_render_jack_la_SOURCES= $(extra_format_src) diff --git a/libs/audio/renderer/Makemodule.am b/libs/audio/renderer/Makemodule.am new file mode 100644 index 000000000..0e726fe36 --- /dev/null +++ b/libs/audio/renderer/Makemodule.am @@ -0,0 +1,58 @@ +plugin_LTLIBRARIES += @snd_render_plugins@ +noinst_LTLIBRARIES += @snd_render_static_plugins@ +EXTRA_LTLIBRARIES += libs/audio/renderer/snd_render_default.la + +flac_src = libs/audio/renderer/flac.c +midi_src = libs/audio/renderer/midi.c +vorbis_src = libs/audio/renderer/vorbis.c +wav_src = libs/audio/renderer/wav.c + +if HAVE_FLAC +have_flac_src = $(flac_src) +else +have_flac_src = +endif +if HAVE_MIDI +have_midi_src = $(midi_src) +else +have_midi_src = +endif +if HAVE_VORBIS +have_vorbis_src = $(vorbis_src) +else +have_vorbis_src = +endif +have_wav_src = $(wav_src) + +format_src=$(have_flac_src) $(have_midi_src) $(have_vorbis_src) $(have_wav_src) +format_libs= \ + $(SAMPLERATE_LIBS) \ + $(VORBISFILE_LIBS) \ + $(VORBIS_LIBS) \ + $(FLAC_LIBS) \ + $(OGG_LIBS) \ + $(WM_LIBS) +extra_format_src = \ + libs/audio/renderer/flac.c \ + libs/audio/renderer/midi.c \ + libs/audio/renderer/vorbis.c \ + libs/audio/renderer/wav.c +snd_common = \ + libs/audio/renderer/snd_channels.c \ + libs/audio/renderer/snd_mem.c \ + libs/audio/renderer/snd_mix.c \ + libs/audio/renderer/snd_resample.c \ + libs/audio/renderer/snd_sfx.c +snd_libs = \ + libs/util/libQFutil.la + +libs_audio_renderer_snd_render_default_la_LDFLAGS = $(plugin_ldflags) +libs_audio_renderer_snd_render_default_la_SOURCES = \ + libs/audio/renderer/snd_dma.c \ + $(snd_common) \ + $(format_src) +libs_audio_renderer_snd_render_default_la_LIBADD = \ + $(snd_libs) \ + $(format_libs) +libs_audio_renderer_snd_render_default_la_DEPENDENCIES = $(snd_libs) +EXTRA_libs_audio_renderer_snd_render_default_la_SOURCES = $(extra_format_src) diff --git a/libs/audio/renderer/flac.c b/libs/audio/renderer/flac.c index be7962522..000ec4867 100644 --- a/libs/audio/renderer/flac.c +++ b/libs/audio/renderer/flac.c @@ -39,6 +39,7 @@ #endif #include +#define FLAC__NO_DLL #include #include "qfalloca.h" @@ -279,59 +280,60 @@ flac_read (flacfile_t *ff, float *buf, int len) } static sfxbuffer_t * -flac_load (flacfile_t *ff, sfxblock_t *block, cache_allocator_t allocator) +flac_load (flacfile_t *ff, sfxblock_t *block) { float *data; - sfxbuffer_t *sc = 0; - sfx_t *sfx = block->sfx; + sfxbuffer_t *sb = 0; + const sfx_t *sfx = block->sfx; wavinfo_t *info = &block->wavinfo; data = malloc (info->datalen); if (!data) goto bail; - sc = SND_GetCache (info->frames, info->rate, info->channels, - block, allocator); - if (!sc) + unsigned buffer_frames = SND_ResamplerFrames (sfx, info->frames); + sb = SND_Memory_AllocBuffer (buffer_frames * info->channels); + if (!sb) goto bail; - sc->sfx = sfx; + sb->size = buffer_frames * info->channels; + sb->channels = info->channels; + sb->sfx_length = info->frames; + sb->block = sfx->block; if (flac_read (ff, data, info->frames) < 0) goto bail; - SND_SetPaint (sc); - SND_SetupResampler (sc, 0); - SND_Resample (sc, data, info->frames); - sc->head = sc->length; + SND_SetPaint (sb); + SND_SetupResampler (sb, 0); + SND_Resample (sb, data, info->frames); + sb->head = sb->size; bail: if (data) free (data); flac_close (ff); - return sc; + return sb; } -static void -flac_callback_load (void *object, cache_allocator_t allocator) +static sfxbuffer_t * +flac_callback_load (sfxblock_t *block) { QFile *file; flacfile_t *ff; - sfxblock_t *block = (sfxblock_t *) object; - file = QFS_FOpenFile (block->file); if (!file) - return; //FIXME Sys_Error? + return 0; if (!(ff = flac_open (file))) { Sys_Printf ("Input does not appear to be an Ogg bitstream.\n"); Qclose (file); - return; //FIXME Sys_Error? + return 0; } - flac_load (ff, block, allocator); + return flac_load (ff, block); } static void -flac_cache (sfx_t *sfx, char *realname, flacfile_t *ff, wavinfo_t info) +flac_block (sfx_t *sfx, char *realname, flacfile_t *ff, wavinfo_t info) { flac_close (ff); - SND_SFX_Cache (sfx, realname, info, flac_callback_load); + SND_SFX_Block (sfx, realname, info, flac_callback_load); } static long @@ -359,18 +361,18 @@ flac_stream_seek (sfxstream_t *stream, int pos) } static void -flac_stream_close (sfx_t *sfx) +flac_stream_close (sfxbuffer_t *buffer) { - sfxstream_t *stream = sfx->data.stream; + sfxstream_t *stream = buffer->stream; flac_close (stream->file); - SND_SFX_StreamClose (sfx); + SND_SFX_StreamClose (stream); } -static sfx_t * +static sfxbuffer_t * flac_stream_open (sfx_t *sfx) { - sfxstream_t *stream = sfx->data.stream; + sfxstream_t *stream = sfx->stream; QFile *file; void *f; @@ -411,7 +413,7 @@ flac_get_info (flacfile_t *ff) vc = &ff->vorbis_info->data.vorbis_comment; for (i = 0, ve = vc->comments; i < vc->num_comments; ve++, i++) { - Sys_MaskPrintf (SYS_DEV, "%.*s\n", ve->length, ve->entry); + Sys_MaskPrintf (SYS_snd, "%.*s\n", ve->length, ve->entry); if (strncmp ("CUEPOINT=", (char *) ve->entry, 9) == 0) { char *str = alloca (ve->length + 1); strncpy (str, (char *) ve->entry, ve->length); @@ -432,12 +434,12 @@ flac_get_info (flacfile_t *ff) info.dataofs = 0; info.datalen = samples * info.channels * sizeof (float); - Sys_MaskPrintf (SYS_DEV, "\nBitstream is %d channel, %dHz\n", + Sys_MaskPrintf (SYS_snd, "\nBitstream is %d channel, %dHz\n", info.channels, info.rate); - Sys_MaskPrintf (SYS_DEV, "\nDecoded length: %d samples (%d bytes)\n", + Sys_MaskPrintf (SYS_snd, "\nDecoded length: %d samples (%d bytes)\n", info.frames, info.width); if (vc) { - Sys_MaskPrintf (SYS_DEV, "Encoded by: %.*s\n\n", + Sys_MaskPrintf (SYS_snd, "Encoded by: %.*s\n\n", vc->vendor_string.length, vc->vendor_string.entry); } @@ -460,10 +462,10 @@ SND_LoadFLAC (QFile *file, sfx_t *sfx, char *realname) return -1; } if (info.frames / info.rate < 3) { - Sys_MaskPrintf (SYS_DEV, "cache %s\n", realname); - flac_cache (sfx, realname, ff, info); + Sys_MaskPrintf (SYS_snd, "block %s\n", realname); + flac_block (sfx, realname, ff, info); } else { - Sys_MaskPrintf (SYS_DEV, "stream %s\n", realname); + Sys_MaskPrintf (SYS_snd, "stream %s\n", realname); flac_stream (sfx, realname, ff, info); } return 0; diff --git a/libs/audio/renderer/midi.c b/libs/audio/renderer/midi.c index 86898da65..6df371b0c 100644 --- a/libs/audio/renderer/midi.c +++ b/libs/audio/renderer/midi.c @@ -57,25 +57,40 @@ typedef struct { static int midi_intiialized = 0; -static cvar_t *wildmidi_volume; -static cvar_t *wildmidi_config; +static float wildmidi_volume; +static cvar_t wildmidi_volume_cvar = { + .name = "wildmidi_volume", + .description = + "Set the Master Volume", + .default_value = "100", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &wildmidi_volume }, +}; +static char *wildmidi_config; +static cvar_t wildmidi_config_cvar = { + .name = "wildmidi_config", + .description = + "path/filename of wildmidi.cfg", + .default_value = "/etc/wildmidi/wildmidi.cfg", + .flags = CVAR_ROM, + .value = { .type = 0, .value = &wildmidi_config }, +}; static int -midi_init ( void ) { - wildmidi_volume = Cvar_Get ("wildmidi_volume", "100", CVAR_ARCHIVE, NULL, - "Set the Master Volume"); - wildmidi_config = Cvar_Get ("wildmidi_config", "/etc/timidity.cfg", - CVAR_ROM, NULL, - "path/filename of timidity.cfg"); +midi_init (snd_t *snd) +{ + Cvar_Register (&wildmidi_volume_cvar, 0, 0); + Cvar_Register (&wildmidi_config_cvar, 0, 0); - if (WildMidi_Init (wildmidi_config->string, snd_shm->speed, 0) == -1) + if (WildMidi_Init (wildmidi_config, snd->speed, 0) == -1) return 1; midi_intiialized = 1; return 0; } static wavinfo_t -midi_get_info (void * handle) { +midi_get_info (snd_t *snd, void *handle) +{ wavinfo_t info; struct _WM_Info *wm_info; @@ -86,7 +101,7 @@ midi_get_info (void * handle) { return info; } - info.rate = snd_shm->speed; + info.rate = snd->speed; info.width = 2; info.channels = 2; info.loopstart = -1; @@ -105,7 +120,7 @@ midi_stream_read (void *file, float **buf) int res; byte *data = alloca (size); - res = WildMidi_GetOutput (mf->handle, (char *)data, size); + res = WildMidi_GetOutput (mf->handle, (int8_t *)data, size); if (res <= 0) { stream->error = 1; return 0; @@ -128,20 +143,20 @@ midi_stream_seek (sfxstream_t *stream, int pos) } static void -midi_stream_close (sfx_t *sfx) +midi_stream_close (sfxbuffer_t *buffer) { - sfxstream_t *stream = sfx->data.stream; + sfxstream_t *stream = buffer->stream; midi_file_t *mf = (midi_file_t *) stream->file; WildMidi_Close (mf->handle); free (mf); - SND_SFX_StreamClose (sfx); + SND_SFX_StreamClose (stream); } -static sfx_t * +static sfxbuffer_t * midi_stream_open (sfx_t *sfx) { - sfxstream_t *stream = sfx->data.stream; + sfxstream_t *stream = sfx->stream; QFile *file; midi *handle; unsigned char *local_buffer; @@ -171,13 +186,14 @@ midi_stream_open (sfx_t *sfx) int SND_LoadMidi (QFile *file, sfx_t *sfx, char *realname) { + snd_t *snd = sfx->snd; wavinfo_t info; midi *handle; unsigned char *local_buffer; unsigned long int local_buffer_size = Qfilesize (file); if (!midi_intiialized) { - if (midi_init ()) { + if (midi_init (snd)) { return -1; } } @@ -193,11 +209,11 @@ SND_LoadMidi (QFile *file, sfx_t *sfx, char *realname) if (handle == NULL) return -1; - info = midi_get_info (handle); + info = midi_get_info (snd, handle); WildMidi_Close (handle); - Sys_MaskPrintf (SYS_DEV, "stream %s\n", realname); + Sys_MaskPrintf (SYS_snd, "stream %s\n", realname); // we init stream here cause we will only ever stream SND_SFX_Stream (sfx, realname, info, midi_stream_open); diff --git a/libs/audio/renderer/snd_channels.c b/libs/audio/renderer/snd_channels.c index cbf337496..01e8d0c8a 100644 --- a/libs/audio/renderer/snd_channels.c +++ b/libs/audio/renderer/snd_channels.c @@ -4,6 +4,7 @@ main control for any streaming sound output device Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2003-2022 Bill Currie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -45,127 +46,187 @@ #include "QF/dstring.h" #include "QF/model.h" #include "QF/quakefs.h" +#include "QF/set.h" #include "QF/sys.h" +#include "QF/scene/transform.h" + #include "snd_internal.h" -static channel_t *free_channels; -channel_t snd_channels[MAX_CHANNELS]; +#define SND_STATIC_ID -1 + +typedef struct entchan_s { + int id; // entity id + int channel; // per-entity sound channel +} entchan_t; + +typedef struct spacial_s { + vec3_t origin; //!< origin of sound effect + vec_t dist_mult; //!< distance multiplier (attenuation/clip) + float volume; //!< 0-1 overall channel volume +} spacial_t; + int snd_total_channels; +channel_t snd_channels[MAX_CHANNELS]; +static entchan_t snd_entity_channels[MAX_CHANNELS]; +static spacial_t snd_spacialization[MAX_CHANNELS]; +static int snd_free_channels[MAX_CHANNELS]; +static int snd_num_free_channels; +/* Dynamic channels are (usually) short sound bytes, never looped. They do not + * override other dynamic channels even for the same entity channel. However, + * they DO override (stop) looped channels on the same entity channel. + */ +static set_bits_t dynamic_channel_bits[SET_WORDS_STATIC (MAX_CHANNELS)]; +static set_t dynamic_channels = SET_STATIC_ARRAY (dynamic_channel_bits); +/* Looped channels are sounds that automatically repeat until they are stopped. + * They can be stopped via SND_ChannelStop or by starting another sound + * (dynamic or looped) on the same entity channel. + */ +static set_bits_t looped_channel_bits[SET_WORDS_STATIC (MAX_CHANNELS)]; +static set_t looped_channels = SET_STATIC_ARRAY (looped_channel_bits); +static set_bits_t static_channel_bits[SET_WORDS_STATIC (MAX_CHANNELS)]; +static set_t static_channels = SET_STATIC_ARRAY (static_channel_bits); static channel_t *ambient_channels[NUM_AMBIENTS]; -static channel_t *dynamic_channels; -static channel_t *looped_dynamic_channels; -static channel_t *static_channels[MAX_STATIC_CHANNELS]; -static int snd_num_statics; -static qboolean snd_ambient = 1; +static bool snd_ambient = 1; static sfx_t *ambient_sfx[NUM_AMBIENTS]; static vec_t sound_nominal_clip_dist = 1000.0; -static vec3_t listener_origin; -static vec3_t listener_forward; -static vec3_t listener_right; -static vec3_t listener_up; +static vec4f_t listener_origin; +static vec4f_t listener_forward; +static vec4f_t listener_right; +static vec4f_t listener_up; -static cvar_t *snd_phasesep; -static cvar_t *snd_volumesep; -static cvar_t *snd_swapchannelside; -static cvar_t *ambient_fade; -static cvar_t *ambient_level; +static float snd_phasesep; +static cvar_t snd_phasesep_cvar = { + .name = "snd_phasesep", + .description = + "max stereo phase separation in ms. 0.6 is for 20cm head", + .default_value = "0.0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &snd_phasesep }, +}; +static float snd_volumesep; +static cvar_t snd_volumesep_cvar = { + .name = "snd_volumesep", + .description = + "max stereo volume separation. 1.0 is max", + .default_value = "1.0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &snd_volumesep }, +}; +static int snd_swapchannelside; +static cvar_t snd_swapchannelside_cvar = { + .name = "snd_swapchannelside", + .description = + "Toggle swapping of left and right channels", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &snd_swapchannelside }, +}; +static float ambient_fade; +static cvar_t ambient_fade_cvar = { + .name = "ambient_fade", + .description = + "How quickly ambient sounds fade in or out", + .default_value = "100", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &ambient_fade }, +}; +static float ambient_level; +static cvar_t ambient_level_cvar = { + .name = "ambient_level", + .description = + "Ambient sounds' volume", + .default_value = "0.3", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &ambient_level }, +}; -static inline channel_t * -unlink_channel (channel_t **_ch) +static void +snd_free_channel (channel_t *ch) { - channel_t *ch = *_ch; - *_ch = ch->next; - ch->next = 0; - return ch; + sfxbuffer_t *buffer = ch->buffer; + ch->buffer = (sfxbuffer_t *) 0; + ch->stop = 0; + ch->done = 0; + int chan_ind = ch - snd_channels; + if (snd_num_free_channels >= MAX_CHANNELS) { + Sys_Error ("snd_num_free_channels: free channel list overflow"); + } + snd_free_channels[snd_num_free_channels++] = chan_ind; + snd_entity_channels[chan_ind] = (entchan_t) {}; + + if (buffer) { + buffer->close (buffer); + } } channel_t * -SND_AllocChannel (void) +SND_AllocChannel (snd_t *snd) { - channel_t **free = &free_channels; channel_t *chan; - while (*free) { - if (!(*free)->sfx) // free channel - break; - if ((*free)->done) // mixer is finished with this channel - break; - if (!(*free)->stop) - Sys_Error ("SND_AllocChannel: bogus channel free list"); - free = &(*free)->next; - } - if (!*free) { - int num_free = 0; - for (free = &free_channels; *free; free = &(*free)->next) { - num_free++; - } - Sys_Printf ("SND_AllocChannel: out of channels. %d\n", num_free); + Sys_MaskPrintf (SYS_snd, "SND_AllocChannel: free channels: %d\n", + snd_num_free_channels); + if (!snd_num_free_channels) { + Sys_MaskPrintf (SYS_warn, "SND_AllocChannel: out of channels.\n"); return 0; } - chan = *free; - *free = chan->next; - if (chan->sfx) { - chan->sfx->release (chan->sfx); - chan->sfx->close (chan->sfx); - chan->sfx = 0; // make sure mixer doesn't use channel during memset - } + int chan_ind = snd_free_channels[--snd_num_free_channels]; + chan = &snd_channels[chan_ind]; memset (chan, 0, sizeof (*chan)); - chan->next = 0; - chan->sfx = 0; return chan; } void -SND_ChannelStop (channel_t *chan) +SND_ChannelStop (snd_t *snd, channel_t *chan) { - /* if chan->next is set, then this channel may have already been freed. - a rather serious bug as it will create a loop in the free list - */ - if (chan->next) - Sys_Error ("Stopping a freed channel"); + if (!chan->buffer) { + Sys_MaskPrintf (SYS_warn, "Sound: stop called on invalid channel\n"); + } chan->stop = 1; - chan->next = free_channels; - free_channels = chan; + int chan_ind = chan - snd_channels; + set_remove (&dynamic_channels, chan_ind); + set_remove (&looped_channels, chan_ind); + set_remove (&static_channels, chan_ind); } void -SND_ScanChannels (int wait) +SND_ScanChannels (snd_t *snd, int wait) { int i; channel_t *ch; int count = 0; - if (!snd_shm || !snd_shm->speed) + if (!snd || !snd->speed) return; if (wait) { - Sys_MaskPrintf (SYS_DEV, "scanning channels...\n"); + Sys_MaskPrintf (SYS_snd, "scanning channels...\n"); do { count = 0; for (i = 0; i < MAX_CHANNELS; i++) { ch = &snd_channels[i]; - if (!ch->sfx || ch->done) + if (!ch->buffer || ch->done) continue; ch->stop = 1; count++; } - Sys_MaskPrintf (SYS_DEV, "count = %d\n", count); + Sys_MaskPrintf (SYS_snd, "count = %d\n", count); #ifdef HAVE_USLEEP usleep (1000); #endif } while (count); - Sys_MaskPrintf (SYS_DEV, "scanning done.\n"); + Sys_MaskPrintf (SYS_snd, "scanning done.\n"); } else { for (i = 0; i < MAX_CHANNELS; i++) { ch = &snd_channels[i]; - if (ch->sfx && ch->stop && !ch->done) { + if (ch->buffer && ch->stop && !ch->done) { ch->done = 1; count++; } @@ -174,50 +235,50 @@ SND_ScanChannels (int wait) } for (i = 0; i < MAX_CHANNELS; i++) { ch = &snd_channels[i]; - if (!ch->sfx || !ch->done) + if (!ch->buffer || !ch->done) continue; - ch->sfx->release (ch->sfx); - ch->sfx->close (ch->sfx); - ch->sfx = 0; + snd_free_channel (ch); + } + Sys_MaskPrintf (SYS_snd, "SND_ScanChannels: free channels: %d\n", + snd_num_free_channels); +} + +void +SND_FinishChannels (void) +{ + int i; + channel_t *ch; + + for (i = 0; i < MAX_CHANNELS; i++) { + ch = &snd_channels[i]; + ch->done = ch->stop = 1; } } void -SND_StopAllSounds (void) +SND_StopAllSounds (snd_t *snd) { - int i; - - snd_num_statics = 0; - while (dynamic_channels) - SND_ChannelStop (unlink_channel (&dynamic_channels)); - while (looped_dynamic_channels) - SND_ChannelStop (unlink_channel (&looped_dynamic_channels)); - for (i = 0; i < NUM_AMBIENTS; i++) { + for (int i = 0; i < MAX_CHANNELS; i++) { + if (set_is_member (&dynamic_channels, i) + || set_is_member (&looped_channels, i) + || set_is_member (&static_channels, i)) { + SND_ChannelStop (snd, &snd_channels[i]); + } + } + set_empty (&dynamic_channels); + set_empty (&looped_channels); + set_empty (&static_channels); + for (int i = 0; i < NUM_AMBIENTS; i++) { if (ambient_channels[i]) - SND_ChannelStop (ambient_channels[i]); + SND_ChannelStop (snd, ambient_channels[i]); ambient_channels[i] = 0; } - for (i = 0; i < MAX_STATIC_CHANNELS; i++) { - if (static_channels[i]) - SND_ChannelStop (static_channels[i]); - static_channels[i] = 0; - } - if (0) { - channel_t *ch; - Sys_Printf ("SND_StopAllSounds\n"); - for (i = 0, ch = free_channels; ch; ch = ch->next) - i++; - Sys_Printf (" free channels:%d\n", i); - for (i = 0, ch = free_channels; ch; ch = ch->next) - if (!ch->sfx || ch->done) - i++; - Sys_Printf (" truely free channels:%d\n", i); - } } static void -s_play_f (void) +s_play_f (void *_snd) { + snd_t *snd = _snd; dstring_t *name = dstring_new (); int i; static int hash = 345; @@ -230,16 +291,17 @@ s_play_f (void) } else { dsprintf (name, "%s", Cmd_Argv (i)); } - sfx = SND_PrecacheSound (name->str); - SND_StartSound (hash++, 0, sfx, listener_origin, 1.0, 1.0); + sfx = SND_PrecacheSound (snd, name->str); + SND_StartSound (snd, hash++, 0, sfx, listener_origin, 1.0, 1.0); i++; } dstring_delete (name); } static void -s_playcenter_f (void) +s_playcenter_f (void *_snd) { + snd_t *snd = _snd; dstring_t *name = dstring_new (); int i; sfx_t *sfx; @@ -254,15 +316,16 @@ s_playcenter_f (void) } else { dsprintf (name, "%s", Cmd_Argv (i)); } - sfx = SND_PrecacheSound (name->str); - SND_StartSound (viewent, 0, sfx, listener_origin, 1.0, 1.0); + sfx = SND_PrecacheSound (snd, name->str); + SND_StartSound (snd, viewent, 0, sfx, listener_origin, 1.0, 1.0); } dstring_delete (name); } static void -s_playvol_f (void) +s_playvol_f (void *_snd) { + snd_t *snd = _snd; dstring_t *name = dstring_new (); float vol; int i; @@ -276,126 +339,115 @@ s_playvol_f (void) } else { dsprintf (name, "%s", Cmd_Argv (i)); } - sfx = SND_PrecacheSound (name->str); + sfx = SND_PrecacheSound (snd, name->str); vol = atof (Cmd_Argv (i + 1)); - SND_StartSound (hash++, 0, sfx, listener_origin, vol, 1.0); + SND_StartSound (snd, hash++, 0, sfx, listener_origin, vol, 1.0); i += 2; } dstring_delete (name); } static void -s_channels_gamedir (int phase) +s_channels_gamedir (int phase, void *_snd) { - //FIXME for some reason, a gamedir change causes semi-random - //"already released" cache errors. fortunatly, servers don't change - //gamedir often, so I'll put this in the too-hard basket for now. - if (phase) { - ambient_sfx[AMBIENT_WATER] = SND_PrecacheSound ("ambience/water1.wav"); - ambient_sfx[AMBIENT_SKY] = SND_PrecacheSound ("ambience/wind2.wav"); - } } void -SND_Channels_Init (void) +SND_Channels_Init (snd_t *snd) { - int i; + Cvar_Register (&snd_phasesep_cvar, 0, 0); + Cvar_Register (&snd_volumesep_cvar, 0, 0); + Cvar_Register (&snd_swapchannelside_cvar, 0, 0); + Cvar_Register (&ambient_fade_cvar, 0, 0); + Cvar_Register (&ambient_level_cvar, 0, 0); - snd_phasesep = Cvar_Get ("snd_phasesep", "0.0", CVAR_ARCHIVE, NULL, - "max stereo phase separation in ms. 0.6 is for " - "20cm head"); - snd_volumesep = Cvar_Get ("snd_volumesep", "1.0", CVAR_ARCHIVE, NULL, - "max stereo volume separation. 1.0 is max"); - snd_swapchannelside = Cvar_Get ("snd_swapchannelside", "0", CVAR_ARCHIVE, - NULL, "Toggle swapping of left and right " - "channels"); - ambient_fade = Cvar_Get ("ambient_fade", "100", CVAR_NONE, NULL, - "How quickly ambient sounds fade in or out"); - ambient_level = Cvar_Get ("ambient_level", "0.3", CVAR_NONE, NULL, - "Ambient sounds' volume"); + Cmd_AddDataCommand ("play", s_play_f, snd, + "Play selected sound effect (play pathto/sound.wav)"); + Cmd_AddDataCommand ("playcenter", s_playcenter_f, snd, + "Play selected sound effect without 3D " + "spatialization."); + Cmd_AddDataCommand ("playvol", s_playvol_f, snd, + "Play selected sound effect at selected volume " + "(playvol pathto/sound.wav num"); - Cmd_AddCommand ("play", s_play_f, - "Play selected sound effect (play pathto/sound.wav)"); - Cmd_AddCommand ("playcenter", s_playcenter_f, - "Play selected sound effect without 3D spatialization."); - Cmd_AddCommand ("playvol", s_playvol_f, "Play selected sound effect at " - "selected volume (playvol pathto/sound.wav num"); - - for (i = 0; i < MAX_CHANNELS - 1; i++) - snd_channels[i].next = &snd_channels[i + 1]; - free_channels = &snd_channels[0]; + for (int i = 0; i < MAX_CHANNELS; i++) { + snd_free_channels[i] = MAX_CHANNELS - 1 - i; + } + snd_num_free_channels = MAX_CHANNELS; snd_total_channels = MAX_CHANNELS; - snd_num_statics = 0; - - QFS_GamedirCallback (s_channels_gamedir); + QFS_GamedirCallback (s_channels_gamedir, snd); } static channel_t * -s_pick_channel (int entnum, int entchannel, int looped) +s_pick_channel (snd_t *snd, int entnum, int entchannel, int looped) { - channel_t *ch, **_ch; - // check for finished non-looped sounds - for (_ch = &dynamic_channels; *_ch; ) { - if (!(*_ch)->sfx || (*_ch)->done) { - SND_ChannelStop (unlink_channel (_ch)); - continue; + for (int i = 0; i < MAX_CHANNELS; i++) { + channel_t *ch = &snd_channels[i]; + if (set_is_member (&dynamic_channels, i)) { + if (ch->done) { + // mixer is done with the channel, it can be freed + snd_free_channel (ch); + set_remove (&dynamic_channels, i); + } + } else if (set_is_member (&looped_channels, i)) { + // non-looped sounds are used to stop looped sounds on an entity + // channel also clean out any caught by SND_ScanChannels + entchan_t *entchan = &snd_entity_channels[i]; + if (entchan->id == entnum + && (entchan->channel == entchannel || entchannel == -1)) { + // the mixer is still using the channel, so send a request + // for it to stopp + SND_ChannelStop (snd, ch); + } } - _ch = &(*_ch)->next; } - // non-looped sounds are used to stop looped sounds on an ent channel - // also clean out any caught by SND_ScanChannels - for (_ch = &looped_dynamic_channels; *_ch; ) { - if (!(*_ch)->sfx || (*_ch)->done - || ((*_ch)->entnum == entnum - && ((*_ch)->entchannel == entchannel || entchannel == -1))) { - SND_ChannelStop (unlink_channel (_ch)); - continue; - } - _ch = &(*_ch)->next; - } - _ch = looped ? &looped_dynamic_channels : &dynamic_channels; - if ((ch = SND_AllocChannel ())) { - ch->next = *_ch; - *_ch = ch; - } - return ch; + return SND_AllocChannel (snd); } void -SND_AmbientOff (void) +SND_AmbientOff (snd_t *snd) { snd_ambient = false; } void -SND_AmbientOn (void) +SND_AmbientOn (snd_t *snd) { snd_ambient = true; } +void +SND_SetAmbient (snd_t *snd, int amb_channel, sfx_t *sfx) +{ + if (amb_channel < 0 || amb_channel > NUM_AMBIENTS) { + return; + } + ambient_sfx[amb_channel] = sfx; +} + static void -s_updateAmbientSounds (const byte *ambient_sound_level) +s_updateAmbientSounds (snd_t *snd, const byte *ambient_sound_level) { float vol; int ambient_channel; - channel_t *chan; - sfx_t *sfx; if (!snd_ambient) return; // calc ambient sound levels - if (!ambient_sound_level || !ambient_level->value) { + if (!ambient_sound_level || !ambient_level) { // if we're not in a leaf (huh?) or ambients have been turned off, // stop all ambient channels. for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS; ambient_channel++) { - chan = ambient_channels[ambient_channel]; + channel_t *chan = ambient_channels[ambient_channel]; if (chan) { - chan->master_vol = 0; - chan->leftvol = chan->rightvol = chan->master_vol; + int chan_ind = chan - snd_channels; + spacial_t *spacial = &snd_spacialization[chan_ind]; + spacial->volume = 0; + chan->leftvol = chan->rightvol = spacial->volume; } } return; @@ -403,97 +455,103 @@ s_updateAmbientSounds (const byte *ambient_sound_level) for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS; ambient_channel++) { - sfx = ambient_sfx[ambient_channel]; + sfx_t *sfx = ambient_sfx[ambient_channel]; if (!sfx) continue; - chan = ambient_channels[ambient_channel]; + channel_t *chan = ambient_channels[ambient_channel]; if (!chan) { - chan = ambient_channels[ambient_channel] = SND_AllocChannel (); + chan = ambient_channels[ambient_channel] = SND_AllocChannel (snd); if (!chan) continue; } - if (!chan->sfx) { - sfx = sfx->open (sfx); - if (!sfx) + sfxbuffer_t *buffer; + if (!chan->buffer) { + buffer = sfx->open (sfx); + if (!buffer) continue; - sfx->retain (sfx); } else { - sfx = chan->sfx; + buffer = chan->buffer; //sfx->retain (sfx); //FIXME why is this necessary? } - // sfx will be written to chan->sfx later to ensure mixer doesn't use - // channel prematurely. + // buffer will be written to chan->buffer later to ensure mixer + // doesn't use channel prematurely. - vol = ambient_level->value * ambient_sound_level[ambient_channel]; - if (vol < 8) + int chan_ind = chan - snd_channels; + spacial_t *spacial = &snd_spacialization[chan_ind]; + + vol = ambient_level * ambient_sound_level[ambient_channel] * (1/255.0); + if (vol < 8/255.0) vol = 0; // don't adjust volume too fast - if (chan->master_vol < vol) { - chan->master_vol += *snd_render_data.host_frametime - * ambient_fade->value; - if (chan->master_vol > vol) - chan->master_vol = vol; - } else if (chan->master_vol > vol) { - chan->master_vol -= *snd_render_data.host_frametime - * ambient_fade->value; - if (chan->master_vol < vol) - chan->master_vol = vol; + float fade = ambient_fade * (1/255.0); + if (spacial->volume < vol) { + spacial->volume += *snd_render_data.host_frametime * fade; + if (spacial->volume > vol) + spacial->volume = vol; + } else if (spacial->volume > vol) { + spacial->volume -= *snd_render_data.host_frametime * fade; + if (spacial->volume < vol) + spacial->volume = vol; } - chan->leftvol = chan->rightvol = chan->master_vol; - chan->sfx = sfx; + chan->leftvol = chan->rightvol = spacial->volume; + chan->loopstart = sfx->loopstart; + chan->buffer = buffer; } } static void -s_spatialize (channel_t *ch) +s_spatialize (snd_t *snd, channel_t *ch) { int phase; // in samples vec_t dist, dot, lscale, rscale, scale; vec3_t source_vec; + int chan_ind = ch - snd_channels; // prepare to lerp from prev to next phase ch->oldphase = ch->phase; + spacial_t *spacial = &snd_spacialization[chan_ind]; + // anything coming from the view entity will always be full volume if (!snd_render_data.viewentity - || ch->entnum == *snd_render_data.viewentity) { - ch->leftvol = ch->master_vol; - ch->rightvol = ch->master_vol; + || snd_entity_channels[chan_ind].id == *snd_render_data.viewentity) { + ch->leftvol = spacial->volume; + ch->rightvol = spacial->volume; ch->phase = 0; return; } // calculate stereo seperation and distance attenuation - VectorSubtract (ch->origin, listener_origin, source_vec); + VectorSubtract (spacial->origin, listener_origin, source_vec); - dist = VectorNormalize (source_vec) * ch->dist_mult; + dist = VectorNormalize (source_vec) * spacial->dist_mult; dot = DotProduct (listener_right, source_vec); - if (snd_swapchannelside->int_val) + if (snd_swapchannelside) dot = -dot; - if (snd_shm->channels == 1) { + if (snd->channels == 1) { rscale = 1.0; lscale = 1.0; phase = 0; } else { - rscale = 1.0 + dot * snd_volumesep->value; - lscale = 1.0 - dot * snd_volumesep->value; - phase = snd_phasesep->value * 0.001 * snd_shm->speed * dot; + rscale = 1.0 + dot * snd_volumesep; + lscale = 1.0 - dot * snd_volumesep; + phase = snd_phasesep * 0.001 * snd->speed * dot; } // add in distance effect scale = (1.0 - dist) * rscale; - ch->rightvol = (int) (ch->master_vol * scale); + ch->rightvol = spacial->volume * scale; if (ch->rightvol < 0) ch->rightvol = 0; scale = (1.0 - dist) * lscale; - ch->leftvol = (int) (ch->master_vol * scale); + ch->leftvol = spacial->volume * scale; if (ch->leftvol < 0) ch->leftvol = 0; @@ -501,11 +559,11 @@ s_spatialize (channel_t *ch) } static inline int -s_update_channel (channel_t *ch) +s_update_channel (snd_t *snd, channel_t *ch) { - if (!ch->sfx) + if (!ch->buffer) return 0; - s_spatialize (ch); // respatialize channel + s_spatialize (snd, ch); if (!ch->leftvol && !ch->rightvol) return 0; return 1; @@ -520,65 +578,73 @@ s_combine_channel (channel_t *combine, channel_t *ch) } void -SND_SetListener (const vec3_t origin, const vec3_t forward, const vec3_t right, - const vec3_t up, const byte *ambient_sound_level) +SND_SetListener (snd_t *snd, transform_t ear, const byte *ambient_sound_level) { - int i, j; - channel_t *combine, *ch; - - VectorCopy (origin, listener_origin); - VectorCopy (forward, listener_forward); - VectorCopy (right, listener_right); - VectorCopy (up, listener_up); + if (Transform_Valid (ear)) { + listener_origin = Transform_GetWorldPosition (ear); + listener_forward = Transform_Forward (ear); + listener_right = Transform_Right (ear); + listener_up = Transform_Up (ear); + } else { + listener_origin = (vec4f_t) {0, 0, 0, 1}; + listener_forward = (vec4f_t) {1, 0, 0, 0}; + listener_right = (vec4f_t) {0, -1, 0, 0}; + listener_up = (vec4f_t) {0, 0, 1, 0}; + } // update general area ambient sound sources - s_updateAmbientSounds (ambient_sound_level); + s_updateAmbientSounds (snd, ambient_sound_level); - // update spatialization for dynamic sounds - for (ch = dynamic_channels; ch; ch = ch->next) - s_update_channel (ch); - for (ch = looped_dynamic_channels; ch; ch = ch->next) - s_update_channel (ch); - - // update spatialization for static sounds - combine = 0; - for (i = 0; i < snd_num_statics; i++) { - ch = static_channels[i]; - if (!s_update_channel (ch)) - continue; - - // try to combine static sounds with a previous channel of the same - // sound effect so we don't mix five torches every frame - // see if it can just use the last one - if (combine && combine->sfx == ch->sfx) { - s_combine_channel (combine, ch); + channel_t *combine = 0; + for (int i = 0; i < MAX_CHANNELS; i++) { + channel_t *ch = &snd_channels[i]; + if (!ch->buffer || ch->done) { continue; } - // search for one - for (j = 0; j < i; j++) { - combine = static_channels[j]; - if (combine->sfx == ch->sfx) - break; - } - - if (j == i) { - combine = 0; - } else { - if (combine != ch) + if (set_is_member (&dynamic_channels, i) + || set_is_member (&looped_channels, i)) { + // update spatialization for dynamic and looped sounds + s_update_channel (snd, ch); + } else if (set_is_member (&static_channels, i)) { + if (!s_update_channel (snd, ch)) { + // too quiet + continue; + } + //FIXME does this even work? probably better just to give + //static sounds random offsets (I suspect it worked just fine + //before streams were implemented) + // try to combine static sounds with a previous channel of + // the same sound effect so we don't mix five torches every + // frame see if it can just use the last one + if (combine && combine->buffer == ch->buffer) { s_combine_channel (combine, ch); - continue; + continue; + } + // search for one + channel_t *c = 0; + for (int j = 0; j < i; j++) { + if (set_is_member (&static_channels, j)) { + if (snd_channels[j].buffer == ch->buffer) { + c = &snd_channels[j]; + break; + } + } + } + if ((combine = c)) { + s_combine_channel (combine, ch); + } } } } static int -snd_check_channels (channel_t *target_chan, const channel_t *check, - const sfx_t *osfx) +snd_check_channels (snd_t *snd, channel_t *target_chan, const channel_t *check, + const sfx_t *sfx) { - if (!check || check == target_chan) + if (!check || !check->buffer || check == target_chan) return 0; - if (check->sfx->owner == osfx->owner && !check->pos) { - int skip = rand () % (int) (0.01 * snd_shm->speed); + if (*check->buffer->sfx == sfx && !check->pos) { + int skip = rand () % (int) (0.01 * snd->speed); target_chan->pos = -skip; return 1; } @@ -586,36 +652,33 @@ snd_check_channels (channel_t *target_chan, const channel_t *check, } void -SND_StartSound (int entnum, int entchannel, sfx_t *sfx, const vec3_t origin, - float fvol, float attenuation) +SND_StartSound (snd_t *snd, int entnum, int entchannel, sfx_t *sfx, + vec4f_t origin, float vol, float attenuation) { - int vol; - int looped; - channel_t *target_chan, *check; - sfx_t *osfx; - - if (!sfx || !snd_shm->speed) + if (!sfx || !snd->speed) return; // pick a channel to play on - looped = sfx->loopstart != (unsigned) -1; - target_chan = s_pick_channel (entnum, entchannel, looped); + int looped = sfx->loopstart != (unsigned) -1; + channel_t *target_chan = s_pick_channel (snd, entnum, entchannel, looped); if (!target_chan) return; - vol = fvol * 255; - + int chan_ind = target_chan - snd_channels; // spatialize - VectorCopy (origin, target_chan->origin); - target_chan->dist_mult = attenuation / sound_nominal_clip_dist; - target_chan->master_vol = vol; - target_chan->entnum = entnum; - target_chan->entchannel = entchannel; - s_spatialize (target_chan); + spacial_t *spacial = &snd_spacialization[chan_ind]; + VectorCopy (origin, spacial->origin); + spacial->dist_mult = attenuation / sound_nominal_clip_dist; + spacial->volume = vol; + snd_entity_channels[chan_ind] = (entchan_t) { + .id = entnum, + .channel = entchannel, + }; + s_spatialize (snd, target_chan); - // new channel - if (!(osfx = sfx->open (sfx))) { - SND_ChannelStop (unlink_channel (looped ? &looped_dynamic_channels - : &dynamic_channels)); + sfxbuffer_t *buffer; + if (!(buffer = sfx->open (sfx))) { + // because the channel was never started, it's safe to directly free it + snd_free_channel (target_chan); return; } target_chan->pos = 0; @@ -623,50 +686,45 @@ SND_StartSound (int entnum, int entchannel, sfx_t *sfx, const vec3_t origin, // if an identical sound has also been started this frame, offset the pos // a bit to keep it from just making the first one louder - for (check = dynamic_channels; check; check = check->next) - if (snd_check_channels (target_chan, check, osfx)) - break; - for (check = looped_dynamic_channels; check; check = check->next) - if (snd_check_channels (target_chan, check, osfx)) - break; - if (!osfx->retain (osfx)) { - SND_ChannelStop (unlink_channel (looped ? &looped_dynamic_channels - : &dynamic_channels)); - return; // couldn't load the sound's data + for (int i = 0; i < MAX_CHANNELS; i++) { + if (set_is_member (&dynamic_channels, i) + || set_is_member (&looped_channels, i)) { + channel_t *check = &snd_channels[i]; + if (snd_check_channels (snd, target_chan, check, sfx)) + break; + } } - target_chan->sfx = osfx; + target_chan->loopstart = sfx->loopstart; + target_chan->buffer = buffer; + set_add (looped ? &looped_channels : &dynamic_channels, chan_ind); } static int -s_check_stop (channel_t **_ch, int entnum, int entchannel) +s_check_stop (snd_t *snd, int chan_ind, int entnum, int entchannel) { - if ((*_ch)->entnum == entnum && (*_ch)->entchannel == entchannel) { - SND_ChannelStop (unlink_channel (_ch)); + entchan_t *entchan = &snd_entity_channels[chan_ind]; + if (entchan->id == entnum && entchan->channel == entchannel) { + SND_ChannelStop (snd, &snd_channels[chan_ind]); return 1; } return 0; } void -SND_StopSound (int entnum, int entchannel) +SND_StopSound (snd_t *snd, int entnum, int entchannel) { - channel_t **_ch; - - for (_ch = &dynamic_channels; *_ch; ) - if (!s_check_stop (_ch, entnum, entchannel)) - _ch = &(*_ch)->next; - for (_ch = &looped_dynamic_channels; *_ch; ) - if (!s_check_stop (_ch, entnum, entchannel)) - _ch = &(*_ch)->next; + for (int i = 0; i < MAX_CHANNELS; i++) { + if (set_is_member (&dynamic_channels, i) + || set_is_member (&looped_channels, i)) { + s_check_stop (snd, i, entnum, entchannel); + } + } } void -SND_StaticSound (sfx_t *sfx, const vec3_t origin, float vol, +SND_StaticSound (snd_t *snd, sfx_t *sfx, vec4f_t origin, float vol, float attenuation) { - channel_t *ss; - sfx_t *osfx; - if (!sfx) return; if (sfx->loopstart == (unsigned int) -1) { @@ -674,44 +732,55 @@ SND_StaticSound (sfx_t *sfx, const vec3_t origin, float vol, return; } - if (!static_channels[snd_num_statics]) { - if (!(static_channels[snd_num_statics] = SND_AllocChannel ())) { - Sys_Printf ("ran out of channels\n"); - return; - } + channel_t *ss; + if (!(ss = SND_AllocChannel (snd))) { + Sys_Printf ("ran out of channels\n"); + return; } + int ss_ind = ss - snd_channels; - ss = static_channels[snd_num_statics]; - - if (!(osfx = sfx->open (sfx))) + sfxbuffer_t *buffer; + if (!(buffer = sfx->open (sfx))) return; - VectorCopy (origin, ss->origin); - ss->master_vol = vol; - ss->dist_mult = (attenuation / 64) / sound_nominal_clip_dist; + spacial_t *spacial = &snd_spacialization[ss_ind]; + VectorCopy (origin, spacial->origin); + spacial->volume = vol; + spacial->dist_mult = attenuation / sound_nominal_clip_dist; ss->end = 0; - s_spatialize (ss); + s_spatialize (snd, ss); ss->oldphase = ss->phase; - if (!osfx->retain (osfx)) - return; - snd_num_statics++; - ss->sfx = osfx; + set_add (&static_channels, ss_ind); + snd_entity_channels[ss_ind] = (entchan_t) { + .id = SND_STATIC_ID, + .channel = 0, + }; + ss->loopstart = sfx->loopstart; + ss->buffer = buffer; } void -SND_LocalSound (const char *sound) +SND_LocalSound (snd_t *snd, const char *sound) { sfx_t *sfx; int viewent = 0; - sfx = SND_PrecacheSound (sound); + sfx = SND_PrecacheSound (snd, sound); if (!sfx) { - Sys_Printf ("S_LocalSound: can't cache %s\n", sound); + Sys_Printf ("S_LocalSound: can't load %s\n", sound); return; } if (snd_render_data.viewentity) viewent = *snd_render_data.viewentity; - SND_StartSound (viewent, -1, sfx, vec3_origin, 1, 1); + SND_StartSound (snd, viewent, -1, sfx, (vec4f_t) {0, 0, 0, 1}, 1, 1); +} + +void +SND_ChannelSetVolume (channel_t *chan, float volume) +{ + int chan_ind = chan - snd_channels; + snd_spacialization[chan_ind].volume = volume; + chan->leftvol = chan->rightvol = volume; } diff --git a/libs/audio/renderer/snd_dma.c b/libs/audio/renderer/snd_dma.c index 0d3d883f0..91207a275 100644 --- a/libs/audio/renderer/snd_dma.c +++ b/libs/audio/renderer/snd_dma.c @@ -1,5 +1,5 @@ /* - snd_dma.c + snd_default.c main control for any streaming sound output device @@ -50,9 +50,10 @@ #include "QF/va.h" #include "QF/quakefs.h" +#include "QF/scene/transform.h" + #include "snd_internal.h" -static qboolean snd_initialized = false; static int snd_blocked = 0; static unsigned soundtime; // sample PAIRS @@ -60,32 +61,81 @@ static unsigned soundtime; // sample PAIRS static int sound_started = 0; -static cvar_t *nosound; -static cvar_t *snd_mixahead; -static cvar_t *snd_noextraupdate; -static cvar_t *snd_show; +float snd_volume; +static cvar_t snd_volume_cvar = { + .name = "volume", + .description = + "Set the volume for sound playback", + .default_value = "0.7", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &snd_volume }, +}; +static int nosound; +static cvar_t nosound_cvar = { + .name = "nosound", + .description = + "Set to turn sound off", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &nosound }, +}; +static float snd_mixahead; +static cvar_t snd_mixahead_cvar = { + .name = "snd_mixahead", + .description = + "Delay time for sounds", + .default_value = "0.1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &snd_mixahead }, +}; +static int snd_noextraupdate; +static cvar_t snd_noextraupdate_cvar = { + .name = "snd_noextraupdate", + .description = + "Toggles the correct value display in host_speeds. Usually messes up " + "sound playback when in effect", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &snd_noextraupdate }, +}; +static int snd_show; +static cvar_t snd_show_cvar = { + .name = "snd_show", + .description = + "Toggles display of sounds currently being played", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &snd_show }, +}; static general_data_t plugin_info_general_data; static snd_output_funcs_t *snd_output_funcs; +static snd_output_data_t *snd_output_data; + +static snd_t snd = { + .finish_channels = SND_FinishChannels, + .paint_channels = SND_PaintChannels, +}; +static int snd_shutdown = 0; static void -s_xfer_paint_buffer (portable_samplepair_t *paintbuffer, int count, +s_xfer_paint_buffer (snd_t *snd, portable_samplepair_t *paintbuffer, int count, float volume) { int out_idx, out_max, step, val; float *p; p = (float *) paintbuffer; - count *= snd_shm->channels; - out_max = (snd_shm->frames * snd_shm->channels) - 1; - out_idx = snd_paintedtime * snd_shm->channels; + count *= snd->channels; + out_max = (snd->frames * snd->channels) - 1; + out_idx = snd->paintedtime * snd->channels; while (out_idx > out_max) out_idx -= out_max + 1; - step = 3 - snd_shm->channels; + step = 3 - snd->channels; - if (snd_shm->samplebits == 16) { - short *out = (short *) snd_shm->buffer; + if (snd->samplebits == 16) { + short *out = (short *) snd->buffer; while (count--) { val = (*p * volume) * 0x8000; @@ -98,8 +148,8 @@ s_xfer_paint_buffer (portable_samplepair_t *paintbuffer, int count, if (out_idx > out_max) out_idx = 0; } - } else if (snd_shm->samplebits == 8) { - unsigned char *out = (unsigned char *) snd_shm->buffer; + } else if (snd->samplebits == 8) { + unsigned char *out = (unsigned char *) snd->buffer; while (count--) { val = (*p * volume) * 128; @@ -116,30 +166,30 @@ s_xfer_paint_buffer (portable_samplepair_t *paintbuffer, int count, } static void -s_clear_buffer (void) +s_clear_buffer (snd_t *snd) { int clear, i; int count; - if (!sound_started || !snd_shm || !snd_shm->buffer) + if (!sound_started || !snd || !snd->buffer) return; - if (snd_shm->samplebits == 8) + if (snd->samplebits == 8) clear = 0x80; else clear = 0; - count = snd_shm->frames * snd_shm->channels * snd_shm->samplebits / 8; + count = snd->frames * snd->channels * snd->samplebits / 8; for (i = 0; i < count; i++) - snd_shm->buffer[i] = clear; + snd->buffer[i] = clear; } static void s_stop_all_sounds (void) { - SND_StopAllSounds (); - SND_ScanChannels (0); - s_clear_buffer (); + SND_StopAllSounds (&snd); + SND_ScanChannels (&snd, snd.threaded); + s_clear_buffer (&snd); } //============================================================================= @@ -150,20 +200,20 @@ s_get_soundtime (void) int frames, framepos; static int buffers, oldframepos; - frames = snd_shm->frames; + frames = snd.frames; // it is possible to miscount buffers if it has wrapped twice between // calls to s_update. Oh well. - if ((framepos = snd_output_funcs->pS_O_GetDMAPos ()) == -1) + if ((framepos = snd_output_funcs->get_dma_pos (&snd)) == -1) return; if (framepos < oldframepos) { buffers++; // buffer wrapped - if (snd_paintedtime > 0x40000000) { // time to chop things off to avoid + if (snd.paintedtime > 0x40000000) { // time to chop things off to avoid // 32 bit limits buffers = 0; - snd_paintedtime = frames; + snd.paintedtime = frames; s_stop_all_sounds (); } } @@ -184,18 +234,18 @@ s_update_ (void) s_get_soundtime (); // check to make sure that we haven't overshot - if (snd_paintedtime < soundtime) { + if (snd.paintedtime < soundtime) { // Sys_Printf ("S_Update_ : overflow\n"); - snd_paintedtime = soundtime; + snd.paintedtime = soundtime; } // mix ahead of current position - endtime = soundtime + snd_mixahead->value * snd_shm->speed; - samps = snd_shm->frames; + endtime = soundtime + snd_mixahead * snd.speed; + samps = snd.frames; if (endtime - soundtime > samps) endtime = soundtime + samps; - SND_PaintChannels (endtime); - snd_output_funcs->pS_O_Submit (); + SND_PaintChannels (&snd, endtime); + snd_output_funcs->submit (&snd); } /* @@ -204,33 +254,40 @@ s_update_ (void) Called once each time through the main loop */ static void -s_update (const vec3_t origin, const vec3_t forward, const vec3_t right, - const vec3_t up, const byte *ambient_sound_level) +s_update (transform_t ear, const byte *ambient_sound_level) { if (!sound_started || (snd_blocked > 0)) return; - SND_SetListener (origin, forward, right, up, ambient_sound_level); + if (snd_output_funcs->on_update) { + snd_output_funcs->on_update (&snd); + } - // mix some sound - s_update_ (); - SND_ScanChannels (0); + SND_SetListener (&snd, ear, ambient_sound_level); + + if (snd_output_data->model == som_push) { + // mix some sound + s_update_ (); + SND_ScanChannels (&snd, 0); + } } static void s_extra_update (void) { - if (!sound_started || snd_noextraupdate->int_val) - return; // don't pollute timings - s_update_ (); + if (snd_output_data->model == som_push) { + if (!sound_started || snd_noextraupdate) + return; // don't pollute timings + s_update_ (); + } } static void s_block_sound (void) { if (++snd_blocked == 1) { - snd_output_funcs->pS_O_BlockSound (); - s_clear_buffer (); + snd_output_funcs->block_sound (&snd); + s_clear_buffer (&snd); } } @@ -241,8 +298,8 @@ s_unblock_sound (void) return; if (!--snd_blocked) { - s_clear_buffer (); - snd_output_funcs->pS_O_UnblockSound (); + s_clear_buffer (&snd); + snd_output_funcs->unblock_sound (&snd); } } @@ -251,18 +308,20 @@ s_unblock_sound (void) static void s_soundinfo_f (void) { - if (!sound_started || !snd_shm) { + if (!sound_started) { Sys_Printf ("sound system not started\n"); return; } - Sys_Printf ("%5d channels\n", snd_shm->channels); - Sys_Printf ("%5d frames\n", snd_shm->frames); - Sys_Printf ("%5d framepos\n", snd_shm->framepos); - Sys_Printf ("%5d samplebits\n", snd_shm->samplebits); - Sys_Printf ("%5d submission_chunk\n", snd_shm->submission_chunk); - Sys_Printf ("%5d speed\n", snd_shm->speed); - Sys_Printf ("0x%"PRIxPTR" dma buffer\n", (intptr_t) snd_shm->buffer); + Sys_Printf ("%5d channels\n", snd.channels); + Sys_Printf ("%5d frames (%.1fms)\n", snd.frames, + 1000.0 * snd.frames / snd.speed); + Sys_Printf ("%5d framepos\n", snd.framepos); + Sys_Printf ("%5d samplebits\n", snd.samplebits); + Sys_Printf ("%5d submission_chunk (%.1fms)\n", snd.submission_chunk, + 1000.0 * snd.submission_chunk / snd.speed); + Sys_Printf ("%5d speed\n", snd.speed); + Sys_Printf ("0x%"PRIxPTR" dma buffer\n", (intptr_t) snd.buffer); Sys_Printf ("%5d total_channels\n", snd_total_channels); } @@ -275,18 +334,15 @@ s_stop_all_sounds_f (void) static void s_startup (void) { - if (!snd_initialized) - return; - - snd_shm = snd_output_funcs->pS_O_Init (); - - if (!snd_shm) { - Sys_Printf ("S_Startup: S_O_Init failed.\n"); - sound_started = 0; + if (!SND_Memory_Init ()) { return; } - if (!snd_shm->xfer) - snd_shm->xfer = s_xfer_paint_buffer; + if (!snd_output_funcs->init (&snd)) { + Sys_Printf ("S_Startup: output init failed.\n"); + return; + } + if (!snd.xfer) + snd.xfer = s_xfer_paint_buffer; sound_started = 1; } @@ -298,10 +354,23 @@ s_snd_force_unblock (void) s_unblock_sound (); } +static void +s_init_cvars (void) +{ + Cvar_Register (&nosound_cvar, 0, 0); + Cvar_Register (&snd_volume_cvar, 0, 0); + Cvar_Register (&snd_mixahead_cvar, 0, 0); + Cvar_Register (&snd_noextraupdate_cvar, 0, 0); + Cvar_Register (&snd_show_cvar, 0, 0); + + SND_Memory_Init_Cvars (); +} + static void s_init (void) { snd_output_funcs = snd_render_data.output->functions->snd_output; + snd_output_data = snd_render_data.output->data->snd_output; snd_render_data.soundtime = &soundtime; Sys_Printf ("\nSound Initialization\n"); @@ -312,33 +381,13 @@ s_init (void) Cmd_AddCommand ("snd_force_unblock", s_snd_force_unblock, "fix permanently blocked sound"); - nosound = Cvar_Get ("nosound", "0", CVAR_NONE, NULL, - "Set to turn sound off"); - snd_volume = Cvar_Get ("volume", "0.7", CVAR_ARCHIVE, NULL, - "Set the volume for sound playback"); - snd_mixahead = Cvar_Get ("snd_mixahead", "0.1", CVAR_ARCHIVE, NULL, - "Delay time for sounds"); - snd_noextraupdate = Cvar_Get ("snd_noextraupdate", "0", CVAR_NONE, NULL, - "Toggles the correct value display in " - "host_speeds. Usually messes up sound " - "playback when in effect"); - snd_show = Cvar_Get ("snd_show", "0", CVAR_NONE, NULL, - "Toggles display of sounds currently being played"); -// FIXME -// if (host_parms.memsize < 0x800000) { -// Cvar_Set (snd_loadas8bit, "1"); -// Sys_Printf ("loading all sounds as 8bit\n"); -// } - - snd_initialized = true; - s_startup (); if (sound_started == 0) // sound startup failed? Bail out. return; - SND_SFX_Init (); - SND_Channels_Init (); + SND_SFX_Init (&snd); + SND_Channels_Init (&snd); s_stop_all_sounds (); } @@ -350,51 +399,205 @@ s_shutdown (void) return; sound_started = 0; + snd_shutdown = 1; - snd_output_funcs->pS_O_Shutdown (); + SND_SFX_Shutdown (&snd); + snd_output_funcs->shutdown (&snd); +} - snd_shm = 0; +static void +s_ambient_off (void) +{ + if (!sound_started) + return; + SND_AmbientOff (&snd); +} + +static void +s_ambient_on (void) +{ + if (!sound_started) + return; + SND_AmbientOn (&snd); +} + +static void +s_set_ambient (int amb_channel, sfx_t *sfx) +{ + if (!sound_started) + return; + SND_SetAmbient (&snd, amb_channel, sfx); +} + +static void +s_static_sound (sfx_t *sfx, vec4f_t origin, float vol, + float attenuation) +{ + if (!sound_started) + return; + SND_StaticSound (&snd, sfx, origin, vol, attenuation); +} + +static void +s_start_sound (int entnum, int entchannel, sfx_t *sfx, vec4f_t origin, + float vol, float attenuation) +{ + if (!sound_started) + return; + if (!snd_shutdown) + SND_StartSound (&snd, entnum, entchannel, sfx, origin, vol, + attenuation); +} + +static void +s_stop_sound (int entnum, int entchannel) +{ + if (!sound_started) + return; + SND_StopSound (&snd, entnum, entchannel); +} + +static sfx_t * +s_precache_sound (const char *name) +{ + if (!sound_started) + return 0; + return SND_PrecacheSound (&snd, name); +} + +static sfx_t * +s_load_sound (const char *name) +{ + if (!sound_started) + return 0; + return SND_LoadSound (&snd, name); +} + +static void +s_channel_free (channel_t *chan) +{ + if (!sound_started) + return; + SND_ChannelStop (&snd, chan); +} + +static int +s_channel_set_sfx (channel_t *chan, sfx_t *sfx) +{ + sfxbuffer_t *buffer = sfx->open (sfx); + if (!buffer) { + return 0; + } + chan->buffer = buffer; + return 1; +} + +static void +s_channel_set_paused (channel_t *chan, int paused) +{ + chan->pause = paused != 0; +} + +static void +s_channel_set_looping (channel_t *chan, int looping) +{ + // FIXME implement +} + +static chan_state +s_channel_get_state (channel_t *chan) +{ + // stop means the channel has been "freed" and is waiting for the mixer + // thread to be done with it, thus putting the channel in an invalid state + // from the user's point of view. ie, don't touch (user should set channel + // pointer to null). + if (!chan->stop) { + if (chan->done) { + // The mixer has finished mixing the channel (come to the end). + return chan_done; + } + if (!chan->buffer) { + // channel has not been started yet + return chan_pending; + } + if (chan->pause) { + return chan_paused; + } + return chan_playing; + } + return chan_invalid; +} + +static void +s_channel_set_volume (channel_t *chan, float volume) +{ + SND_ChannelSetVolume (chan, volume); +} + +static void +s_local_sound (const char *sound) +{ + if (!sound_started) + return; + if (!snd_shutdown) + SND_LocalSound (&snd, sound); +} + +static channel_t * +s_alloc_channel (void) +{ + if (!sound_started) + return 0; + if (!snd_shutdown) + return SND_AllocChannel (&snd); + return 0; } static general_funcs_t plugin_info_general_funcs = { - s_init, - s_shutdown, + .init = s_init_cvars, + .shutdown = s_shutdown, }; static snd_render_funcs_t plugin_info_render_funcs = { - SND_AmbientOff, - SND_AmbientOn, - SND_StaticSound, - SND_StartSound, - SND_StopSound, - SND_PrecacheSound, - s_update, - s_stop_all_sounds, - s_extra_update, - SND_LocalSound, - s_block_sound, - s_unblock_sound, - SND_LoadSound, - SND_AllocChannel, - SND_ChannelStop, + .init = s_init, + .ambient_off = s_ambient_off, + .ambient_on = s_ambient_on, + .set_ambient = s_set_ambient, + .static_sound = s_static_sound, + .start_sound = s_start_sound, + .local_sound = s_local_sound, + .stop_sound = s_stop_sound, + + .alloc_channel = s_alloc_channel, + .channel_free = s_channel_free, + .channel_set_sfx = s_channel_set_sfx, + .channel_set_paused = s_channel_set_paused, + .channel_set_looping = s_channel_set_looping, + .channel_get_state = s_channel_get_state, + .channel_set_volume = s_channel_set_volume, + + .precache_sound = s_precache_sound, + .load_sound = s_load_sound, + + .update = s_update, + .stop_all_sounds = s_stop_all_sounds, + .extra_update = s_extra_update, + .block_sound = s_block_sound, + .unblock_sound = s_unblock_sound, }; static plugin_funcs_t plugin_info_funcs = { - &plugin_info_general_funcs, - 0, - 0, - 0, - 0, - &plugin_info_render_funcs, + .general = &plugin_info_general_funcs, + .snd_render = &plugin_info_render_funcs, +}; + +snd_render_data_t snd_render_data = { + .paintedtime = &snd.paintedtime, }; static plugin_data_t plugin_info_data = { - &plugin_info_general_data, - 0, - 0, - 0, - 0, - &snd_render_data, + .general = &plugin_info_general_data, + .snd_render = &snd_render_data, }; static plugin_t plugin_info = { diff --git a/libs/audio/renderer/snd_mem.c b/libs/audio/renderer/snd_mem.c index 154618899..86f248be5 100644 --- a/libs/audio/renderer/snd_mem.c +++ b/libs/audio/renderer/snd_mem.c @@ -1,9 +1,10 @@ /* snd_mem.c - sound caching + sound memory management Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2003 Bill Currie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -35,10 +36,6 @@ # include #endif -#if defined(_WIN32) && defined(HAVE_MALLOC_H) -#include -#endif - #include "QF/cvar.h" #include "QF/dstring.h" #include "QF/sound.h" @@ -51,118 +48,154 @@ #define SAMPLE_GAP 4 -volatile dma_t *snd_shm; -snd_render_data_t snd_render_data = { - 0, - 0, - 0, - &snd_paintedtime, - 0, +static uint32_t snd_mem_size; +static cvar_t snd_mem_size_cvar = { + .name = "snd_mem_size", + .description = + "Amount of LOCKED memory to allocate to the sound system in MB. " + "Defaults to 32MB.", + .default_value = "32", + .flags = CVAR_ROM, + .value = { .type = &cexpr_uint, .value = &snd_mem_size }, }; -static sfxbuffer_t * -snd_fail (sfx_t *sfx) +static memzone_t *snd_zone; + +void +SND_Memory_Init_Cvars (void) { - return 0; + Cvar_Register (&snd_mem_size_cvar, 0, 0); } static void -snd_noop (sfx_t *sfx) +snd_zone_error (void *data, const char *msg) { + Sys_Error ("Sound: %s", msg); } -static sfx_t * +static void +snd_memory_shutdown (void *data) +{ + if (snd_zone) { + size_t size = snd_mem_size * 1024 * 1024; + Sys_Free (snd_zone, size); + } +} + +int +SND_Memory_Init (void) +{ + size_t size = snd_mem_size * 1024 * 1024; + + snd_zone = Sys_Alloc (size); + if (!snd_zone) { + Sys_Printf ("Sound: Unable to allocate %uMB buffer\n", snd_mem_size); + return 0; + } + if (!Sys_LockMemory (snd_zone, size)) { + Sys_Printf ("Sound: Unable to lock %uMB buffer\n", snd_mem_size); + Sys_Free (snd_zone, size); + return 0; + } + Z_ClearZone (snd_zone, size, 0, 1); + Z_SetError (snd_zone, snd_zone_error, 0); + + Sys_MaskPrintf (SYS_snd, "Sound: Initialized %uMB buffer\n", snd_mem_size); + + Sys_RegisterShutdown (snd_memory_shutdown, 0); + return 1; +} + +sfxbuffer_t * +SND_Memory_AllocBuffer (unsigned samples) +{ + size_t size = field_offset (sfxbuffer_t, data[samples]); + // Z_Malloc (currently) clears memory, don't need that for the whole + // buffer (just the header), but Z_TagMalloc // does not + // +4 for sentinel + sfxbuffer_t *buffer = Z_TagMalloc (snd_zone, size + 4, 1); + if (buffer) { + // place a sentinel at the end of the buffer for added safety + memcpy (&buffer->data[samples], "\xde\xad\xbe\xef", 4); + // clear buffer header + memset (buffer, 0, sizeof (sfxbuffer_t)); + } else { + Sys_Printf ("Sound: out of memory: %uMB exhausted\n", snd_mem_size); + } + return buffer; +} + +void +SND_Memory_Free (void *ptr) +{ + Z_Free (snd_zone, ptr); +} + +void +SND_Memory_SetTag (void *ptr, int tag) +{ + Z_SetTag (snd_zone, ptr, tag); +} + +int +SND_Memory_Retain (void *ptr) +{ + return Z_IncRetainCount (snd_zone, ptr); +} + +int +SND_Memory_Release (void *ptr) +{ + int retain = Z_DecRetainCount (snd_zone, ptr); + if (!retain) { + Z_Free (snd_zone, ptr); + } + return retain; +} + +int +SND_Memory_GetRetainCount (void *ptr) +{ + return Z_GetRetainCount (snd_zone, ptr); +} + +static sfxbuffer_t * snd_open (sfx_t *sfx) { - return sfx; + sfxbuffer_t *buffer = sfx->block->buffer; + SND_Memory_Retain (buffer); + return buffer; } -static sfx_t * +static sfxbuffer_t * snd_open_fail (sfx_t *sfx) { return 0; } -sfxbuffer_t * -SND_CacheTouch (sfx_t *sfx) -{ - return Cache_Check (&sfx->data.block->cache); -} - -sfxbuffer_t * -SND_CacheGetBuffer (sfx_t *sfx) -{ - return sfx->data.block->buffer; -} - -sfxbuffer_t * -SND_CacheRetain (sfx_t *sfx) -{ - sfxblock_t *block = sfx->data.block; - block->buffer = Cache_TryGet (&block->cache); - if (!block->buffer) - Sys_Printf ("failed to cache sound!\n"); - return block->buffer; -} - -void -SND_CacheRelease (sfx_t *sfx) -{ - sfxblock_t *block = sfx->data.block; - // due to the possibly asynchronous nature of the mixer, the cache - // may have been flushed behind our backs - if (block->cache.data) { - if (!Cache_ReadLock (&block->cache)) { - Sys_Printf ("WARNING: taniwha screwed up in the sound engine: %s\n", - sfx->name); - return; - } - Cache_Release (&block->cache); - if (!Cache_ReadLock (&block->cache)) - block->buffer = 0; - } -} - -sfxbuffer_t * -SND_StreamGetBuffer (sfx_t *sfx) -{ - return &sfx->data.stream->buffer; -} - -sfxbuffer_t * -SND_StreamRetain (sfx_t *sfx) -{ - return &sfx->data.stream->buffer; -} - -void -SND_StreamRelease (sfx_t *sfx) +wavinfo_t * +SND_BlockWavinfo (const sfx_t *sfx) { + return &sfx->block->wavinfo; } wavinfo_t * -SND_CacheWavinfo (sfx_t *sfx) +SND_StreamWavinfo (const sfx_t *sfx) { - return &sfx->data.stream->wavinfo; -} - -wavinfo_t * -SND_StreamWavinfo (sfx_t *sfx) -{ - return &sfx->data.stream->wavinfo; + return &sfx->stream->wavinfo; } static void read_samples (sfxbuffer_t *buffer, int count) { - if (buffer->head + count > buffer->length) { - count -= buffer->length - buffer->head; - read_samples (buffer, buffer->length - buffer->head); + if (buffer->head + count > buffer->size) { + count -= buffer->size - buffer->head; + read_samples (buffer, buffer->size - buffer->head); read_samples (buffer, count); } else { - sfx_t *sfx = buffer->sfx; - sfxstream_t *stream = sfx->data.stream; + sfxstream_t *stream = buffer->stream; + const sfx_t *sfx = stream->sfx; wavinfo_t *info = &stream->wavinfo; float *data = buffer->data + buffer->head * info->channels; int c; @@ -170,14 +203,16 @@ read_samples (sfxbuffer_t *buffer, int count) if ((c = stream->read (stream, data, count)) != count) Sys_Printf ("%s nr %d %d\n", sfx->name, count, c); - buffer->head += count; - if (buffer->head >= buffer->length) - buffer->head -= buffer->length; + if (c > 0) { + buffer->head += count; + if (buffer->head >= buffer->size) + buffer->head -= buffer->size; + } } } static void -fill_buffer (sfx_t *sfx, sfxstream_t *stream, sfxbuffer_t *buffer, +fill_buffer (const sfx_t *sfx, sfxstream_t *stream, sfxbuffer_t *buffer, wavinfo_t *info, unsigned int headpos) { unsigned int samples; @@ -186,13 +221,13 @@ fill_buffer (sfx_t *sfx, sfxstream_t *stream, sfxbuffer_t *buffer, // find out how many samples can be read into the buffer samples = buffer->tail - buffer->head - SAMPLE_GAP; if (buffer->tail <= buffer->head) - samples += buffer->length; + samples += buffer->size; - if (headpos + samples > sfx->length) { + if (headpos + samples > buffer->sfx_length) { if (sfx->loopstart == (unsigned int)-1) { - samples = sfx->length - headpos; + samples = buffer->sfx_length - headpos; } else { - loop_samples = headpos + samples - sfx->length; + loop_samples = headpos + samples - buffer->sfx_length; samples -= loop_samples; } } @@ -208,11 +243,11 @@ void SND_StreamSetPos (sfxbuffer_t *buffer, unsigned int pos) { float stepscale; - sfx_t *sfx = buffer->sfx; - sfxstream_t *stream = sfx->data.stream; + sfxstream_t *stream = buffer->stream; + const sfx_t *sfx = stream->sfx; wavinfo_t *info = &stream->wavinfo; - stepscale = (float) info->rate / snd_shm->speed; + stepscale = (float) info->rate / sfx->snd->speed; buffer->head = buffer->tail = 0; buffer->pos = pos; @@ -226,8 +261,8 @@ SND_StreamAdvance (sfxbuffer_t *buffer, unsigned int count) { float stepscale; unsigned int headpos, samples; - sfx_t *sfx = buffer->sfx; - sfxstream_t *stream = sfx->data.stream; + sfxstream_t *stream = buffer->stream; + const sfx_t *sfx = stream->sfx; wavinfo_t *info = &stream->wavinfo; stream->pos += count; @@ -235,32 +270,32 @@ SND_StreamAdvance (sfxbuffer_t *buffer, unsigned int count) if (!count) return 1; - stepscale = (float) info->rate / snd_shm->speed; + stepscale = (float) info->rate / sfx->snd->speed; // find out how many samples the buffer currently holds samples = buffer->head - buffer->tail; if (buffer->head < buffer->tail) - samples += buffer->length; + samples += buffer->size; // find out where head points to in the stream headpos = buffer->pos + samples; - if (headpos >= sfx->length) { + if (headpos >= buffer->sfx_length) { if (sfx->loopstart == (unsigned int)-1) - headpos = sfx->length; + headpos = buffer->sfx_length; else - headpos -= sfx->length - sfx->loopstart; + headpos -= buffer->sfx_length - sfx->loopstart; } if (samples < count) { buffer->head = buffer->tail = 0; buffer->pos += count; - if (buffer->pos > sfx->length) { + if (buffer->pos > buffer->sfx_length) { if (sfx->loopstart == (unsigned int)-1) { // reset the buffer and fill it incase it's needed again buffer->pos = 0; } else { buffer->pos -= sfx->loopstart; - buffer->pos %= sfx->length - sfx->loopstart; + buffer->pos %= buffer->sfx_length - sfx->loopstart; buffer->pos += sfx->loopstart; } stream->pos = buffer->pos; @@ -269,7 +304,7 @@ SND_StreamAdvance (sfxbuffer_t *buffer, unsigned int count) stream->seek (stream, buffer->pos * stepscale); } else { buffer->pos += count; - if (buffer->pos >= sfx->length) { + if (buffer->pos >= buffer->sfx_length) { if (sfx->loopstart == (unsigned int)-1) { // reset the buffer and fill it in case it's needed again headpos = buffer->pos = 0; @@ -277,14 +312,14 @@ SND_StreamAdvance (sfxbuffer_t *buffer, unsigned int count) count = 0; stream->seek (stream, buffer->pos * stepscale); } else { - buffer->pos -= sfx->length - sfx->loopstart; + buffer->pos -= buffer->sfx_length - sfx->loopstart; } stream->pos = buffer->pos; } buffer->tail += count; - if (buffer->tail >= buffer->length) - buffer->tail -= buffer->length; + if (buffer->tail >= buffer->size) + buffer->tail -= buffer->size; } fill_buffer (sfx, stream, buffer, info, headpos); return !stream->error; @@ -297,9 +332,6 @@ SND_Load (sfx_t *sfx) char buf[4]; QFile *file; - sfx->touch = sfx->retain = snd_fail; - sfx->release = snd_noop; - sfx->close = snd_noop; sfx->open = snd_open_fail; file = QFS_FOpenFile (sfx->name); @@ -317,7 +349,7 @@ SND_Load (sfx_t *sfx) Qseek (file, 0, SEEK_SET); #ifdef HAVE_VORBIS if (strnequal ("OggS", buf, 4)) { - Sys_MaskPrintf (SYS_DEV, "SND_Load: ogg file\n"); + Sys_MaskPrintf (SYS_snd, "SND_Load: ogg file\n"); if (SND_LoadOgg (file, sfx, realname) == -1) goto bail; return 0; @@ -325,7 +357,7 @@ SND_Load (sfx_t *sfx) #endif #ifdef HAVE_FLAC if (strnequal ("fLaC", buf, 4)) { - Sys_MaskPrintf (SYS_DEV, "SND_Load: flac file\n"); + Sys_MaskPrintf (SYS_snd, "SND_Load: flac file\n"); if (SND_LoadFLAC (file, sfx, realname) == -1) goto bail; return 0; @@ -333,14 +365,14 @@ SND_Load (sfx_t *sfx) #endif #ifdef HAVE_WILDMIDI if (strnequal ("MThd", buf, 4)) { - Sys_MaskPrintf (SYS_DEV, "SND_Load: midi file\n"); + Sys_MaskPrintf (SYS_snd, "SND_Load: midi file\n"); if (SND_LoadMidi (file, sfx, realname) == -1) goto bail; return 0; } #endif if (strnequal ("RIFF", buf, 4)) { - Sys_MaskPrintf (SYS_DEV, "SND_Load: wav file\n"); + Sys_MaskPrintf (SYS_snd, "SND_Load: wav file\n"); if (SND_LoadWav (file, sfx, realname) == -1) goto bail; return 0; @@ -351,24 +383,3 @@ bail: free (realname); return -1; } - -sfxbuffer_t * -SND_GetCache (long frames, int rate, int channels, - sfxblock_t *block, cache_allocator_t allocator) -{ - int len, size; - float stepscale; - sfxbuffer_t *sc; - sfx_t *sfx = block->sfx; - - stepscale = (float) rate / snd_shm->speed; - len = size = frames / stepscale; - size *= sizeof (float) * channels; - sc = allocator (&block->cache, sizeof (sfxbuffer_t) + size, sfx->name); - if (!sc) - return 0; - memset (sc, 0, sizeof (sfxbuffer_t) + size); - sc->length = len; - memcpy (sc->data + len * channels, "\xde\xad\xbe\xef", 4); - return sc; -} diff --git a/libs/audio/renderer/snd_mix.c b/libs/audio/renderer/snd_mix.c index 2c54836d1..0aa8c220b 100644 --- a/libs/audio/renderer/snd_mix.c +++ b/libs/audio/renderer/snd_mix.c @@ -44,13 +44,7 @@ #include "compat.h" #include "snd_internal.h" -#define VOLSCALE 512.0 // so mixing is less likely to overflow - // note: must be >= 255 due to the channel - // volumes being 0-255. - -cvar_t *snd_volume; - -unsigned snd_paintedtime; // sample PAIRS +#define VOLSCALE 0.5 // so mixing is less likely to overflow portable_samplepair_t snd_paintbuffer[PAINTBUFFER_SIZE * 2]; static int max_overpaint; // number of extra samples painted @@ -59,12 +53,12 @@ static int max_overpaint; // number of extra samples painted /* CHANNEL MIXING */ static inline int -check_channel_end (channel_t *ch, sfx_t *sfx, int count, unsigned ltime) +check_channel_end (channel_t *ch, sfxbuffer_t *sb, int count, unsigned ltime) { if (count <= 0 || ltime >= ch->end) { - if (sfx->loopstart != (unsigned) -1) { - ch->pos = sfx->loopstart; - ch->end = ltime + sfx->length - ch->pos; + if (ch->loopstart != (unsigned) -1) { + ch->pos = ch->loopstart; + ch->end = ltime + sb->sfx_length - ch->pos; } else { // channel just stopped ch->done = 1; return 1; @@ -74,7 +68,7 @@ check_channel_end (channel_t *ch, sfx_t *sfx, int count, unsigned ltime) } static inline void -snd_paint_channel (channel_t *ch, sfxbuffer_t *sc, int count) +snd_paint_channel (channel_t *ch, sfxbuffer_t *sb, int count) { unsigned pos; int offs = 0; @@ -88,29 +82,28 @@ snd_paint_channel (channel_t *ch, sfxbuffer_t *sc, int count) count -= offs; ch->pos = 0; } - if (ch->pos < sc->pos || ch->pos - sc->pos >= sc->length) - sc->setpos (sc, ch->pos); - pos = (ch->pos - sc->pos + sc->tail) % sc->length; - samps = sc->data + pos * sc->channels; + if (ch->pos < sb->pos || ch->pos - sb->pos >= sb->size) + sb->setpos (sb, ch->pos); + pos = (ch->pos - sb->pos + sb->tail) % sb->size; + samps = sb->data + pos * sb->channels; - if (pos + count > sc->length) { - unsigned sub = sc->length - pos; - sc->paint (offs, ch, samps, sub); - sc->paint (offs + sub, ch, sc->data, count - sub); + if (pos + count > sb->size) { + unsigned sub = sb->size - pos; + sb->paint (offs, ch, samps, sub); + sb->paint (offs + sub, ch, sb->data, count - sub); } else { - sc->paint (offs, ch, samps, count); + sb->paint (offs, ch, samps, count); } ch->pos += count; } void -SND_PaintChannels (unsigned endtime) +SND_PaintChannels (snd_t *snd, unsigned endtime) { unsigned end, ltime; int i, count; channel_t *ch; - sfx_t *sfx; - sfxbuffer_t *sc; + sfxbuffer_t *sb; // clear the paint buffer for (i = 0; i < PAINTBUFFER_SIZE * 2; i++) { @@ -118,43 +111,40 @@ SND_PaintChannels (unsigned endtime) snd_paintbuffer[i].right = 0; } - while (snd_paintedtime < endtime) { + while (snd->paintedtime < endtime) { // if snd_paintbuffer is smaller than DMA buffer end = endtime; - if (end - snd_paintedtime > PAINTBUFFER_SIZE) - end = snd_paintedtime + PAINTBUFFER_SIZE; + if (end - snd->paintedtime > PAINTBUFFER_SIZE) + end = snd->paintedtime + PAINTBUFFER_SIZE; max_overpaint = 0; // paint in the channels. ch = snd_channels; for (i = 0; i < snd_total_channels; i++, ch++) { - if (!(sfx = ch->sfx)) + if (!(sb = ch->buffer) || ch->done) { + // channel is inactive continue; - if (ch->stop || ch->done) { + } + if (ch->stop) { ch->done = 1; // acknowledge stopped signal continue; } if (ch->pause) continue; - sc = sfx->getbuffer (sfx); - if (!sc) { // something went wrong with the sfx - printf ("XXXX sfx blew up!!!!\n"); - continue; - } if (!ch->end) - ch->end = snd_paintedtime + sfx->length - ch->pos; + ch->end = snd->paintedtime + sb->sfx_length - ch->pos; - ltime = snd_paintedtime; + ltime = snd->paintedtime; while (ltime < end) { // paint up to end count = ((ch->end < end) ? ch->end : end) - ltime; if (count > 0) { if (ch->leftvol || ch->rightvol) { - snd_paint_channel (ch, sc, count); - if (sc->advance) { - if (!sc->advance (sc, count)) { + snd_paint_channel (ch, sb, count); + if (sb->advance) { + if (!sb->advance (sb, count)) { // this channel can no longer be used as its // source has died. ch->done = 1; @@ -165,21 +155,21 @@ SND_PaintChannels (unsigned endtime) ltime += count; } - if (check_channel_end (ch, sfx, count, ltime)) + if (check_channel_end (ch, sb, count, ltime)) break; } } // transfer out according to DMA format - snd_shm->xfer (snd_paintbuffer, end - snd_paintedtime, - snd_volume->value); + snd->xfer (snd, snd_paintbuffer, end - snd->paintedtime, + snd_volume); - memmove (snd_paintbuffer, snd_paintbuffer + end - snd_paintedtime, + memmove (snd_paintbuffer, snd_paintbuffer + end - snd->paintedtime, max_overpaint * sizeof (snd_paintbuffer[0])); memset (snd_paintbuffer + max_overpaint, 0, sizeof (snd_paintbuffer) - max_overpaint * sizeof (snd_paintbuffer[0])); - snd_paintedtime = end; + snd->paintedtime = end; } } @@ -205,7 +195,7 @@ snd_mix_pair (portable_samplepair_t *pair, float **samp, } static inline void -snd_mix_tripple (portable_samplepair_t *pair, float **samp, +snd_mix_triple (portable_samplepair_t *pair, float **samp, float lvol, float rvol) { float left = *(*samp)++; @@ -230,8 +220,8 @@ snd_paint_mono (int offs, channel_t *ch, float *sfx, unsigned count) unsigned i = 0; portable_samplepair_t *pair; - leftvol = ch->leftvol / VOLSCALE; - rightvol = ch->rightvol / VOLSCALE; + leftvol = ch->leftvol * VOLSCALE; + rightvol = ch->rightvol * VOLSCALE; max_overpaint = max (abs (ch->phase), max (abs (ch->oldphase), max_overpaint)); @@ -324,8 +314,8 @@ static void snd_paint_stereo (int offs, channel_t *ch, float *samp, unsigned count) { portable_samplepair_t *pair; - float leftvol = ch->leftvol / VOLSCALE; - float rightvol = ch->rightvol / VOLSCALE; + float leftvol = ch->leftvol * VOLSCALE; + float rightvol = ch->rightvol * VOLSCALE; pair = snd_paintbuffer + offs; while (count-- > 0) { @@ -342,12 +332,12 @@ static void snd_paint_3 (int offs, channel_t *ch, float *samp, unsigned count) { portable_samplepair_t *pair; - float leftvol = ch->leftvol / VOLSCALE; - float rightvol = ch->rightvol / VOLSCALE; + float leftvol = ch->leftvol * VOLSCALE; + float rightvol = ch->rightvol * VOLSCALE; pair = snd_paintbuffer + offs; while (count-- > 0) { - snd_mix_tripple (pair, &samp, leftvol, rightvol); + snd_mix_triple (pair, &samp, leftvol, rightvol); pair++; } } @@ -361,8 +351,8 @@ static void snd_paint_4 (int offs, channel_t *ch, float *samp, unsigned count) { portable_samplepair_t *pair; - float leftvol = ch->leftvol / VOLSCALE; - float rightvol = ch->rightvol / VOLSCALE; + float leftvol = ch->leftvol * VOLSCALE; + float rightvol = ch->rightvol * VOLSCALE; pair = snd_paintbuffer + offs; while (count-- > 0) { @@ -381,12 +371,12 @@ static void snd_paint_5 (int offs, channel_t *ch, float *samp, unsigned count) { portable_samplepair_t *pair; - float leftvol = ch->leftvol / VOLSCALE; - float rightvol = ch->rightvol / VOLSCALE; + float leftvol = ch->leftvol * VOLSCALE; + float rightvol = ch->rightvol * VOLSCALE; pair = snd_paintbuffer + offs; while (count-- > 0) { - snd_mix_tripple (pair, &samp, leftvol, rightvol); + snd_mix_triple (pair, &samp, leftvol, rightvol); snd_mix_pair (pair, &samp, leftvol, rightvol); pair++; } @@ -402,12 +392,12 @@ static void snd_paint_6 (int offs, channel_t *ch, float *samp, unsigned count) { portable_samplepair_t *pair; - float leftvol = ch->leftvol / VOLSCALE; - float rightvol = ch->rightvol / VOLSCALE; + float leftvol = ch->leftvol * VOLSCALE; + float rightvol = ch->rightvol * VOLSCALE; pair = snd_paintbuffer + offs; while (count-- > 0) { - snd_mix_tripple (pair, &samp, leftvol, rightvol); + snd_mix_triple (pair, &samp, leftvol, rightvol); snd_mix_pair (pair, &samp, leftvol, rightvol); snd_mix_single (pair, &samp, leftvol, rightvol); pair++; @@ -425,12 +415,12 @@ static void snd_paint_7 (int offs, channel_t *ch, float *samp, unsigned count) { portable_samplepair_t *pair; - float leftvol = ch->leftvol / VOLSCALE; - float rightvol = ch->rightvol / VOLSCALE; + float leftvol = ch->leftvol * VOLSCALE; + float rightvol = ch->rightvol * VOLSCALE; pair = snd_paintbuffer + offs; while (count-- > 0) { - snd_mix_tripple (pair, &samp, leftvol, rightvol); + snd_mix_triple (pair, &samp, leftvol, rightvol); snd_mix_pair (pair, &samp, leftvol, rightvol); snd_mix_single (pair, &samp, leftvol, rightvol); snd_mix_single (pair, &samp, leftvol, rightvol); @@ -449,12 +439,12 @@ static void snd_paint_8 (int offs, channel_t *ch, float *samp, unsigned count) { portable_samplepair_t *pair; - float leftvol = ch->leftvol / VOLSCALE; - float rightvol = ch->rightvol / VOLSCALE; + float leftvol = ch->leftvol * VOLSCALE; + float rightvol = ch->rightvol * VOLSCALE; pair = snd_paintbuffer + offs; while (count-- > 0) { - snd_mix_tripple (pair, &samp, leftvol, rightvol); + snd_mix_triple (pair, &samp, leftvol, rightvol); snd_mix_pair (pair, &samp, leftvol, rightvol); snd_mix_pair (pair, &samp, leftvol, rightvol); snd_mix_single (pair, &samp, leftvol, rightvol); @@ -463,7 +453,7 @@ snd_paint_8 (int offs, channel_t *ch, float *samp, unsigned count) } void -SND_SetPaint (sfxbuffer_t *sc) +SND_SetPaint (sfxbuffer_t *sb) { static sfxpaint_t *painters[] = { 0, @@ -477,8 +467,7 @@ SND_SetPaint (sfxbuffer_t *sc) snd_paint_8, }; - wavinfo_t *info = sc->sfx->wavinfo (sc->sfx); - if (info->channels > 8) - Sys_Error ("illegal channel count %d", info->channels); - sc->paint = painters[info->channels]; + if (sb->channels > 8 || !sb->channels) + Sys_Error ("invaliid channel count %d", sb->channels); + sb->paint = painters[sb->channels]; } diff --git a/libs/audio/renderer/snd_resample.c b/libs/audio/renderer/snd_resample.c index a3037561d..c3cbfd185 100644 --- a/libs/audio/renderer/snd_resample.c +++ b/libs/audio/renderer/snd_resample.c @@ -1,9 +1,10 @@ /* - snd_mem.c + snd_resample.c - sound caching + sound resampling Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2010 Bill Currie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -54,44 +55,58 @@ typedef struct { } snd_null_state_t; static void -check_buffer_integrity (sfxbuffer_t *sc, int width, const char *func) +check_buffer_integrity (sfxbuffer_t *sb, int width, const char *func) { - byte *x = (byte *) sc->data + sc->length * width; + byte *x = (byte *) sb->data + sb->size * width; if (memcmp (x, "\xde\xad\xbe\xef", 4)) Sys_Error ("%s screwed the pooch %02x%02x%02x%02x", func, x[0], x[1], x[2], x[3]); } +unsigned +SND_ResamplerFrames (const sfx_t *sfx, unsigned frames) +{ + if (frames == ~0u) { + return frames; + } + wavinfo_t *info = sfx->wavinfo (sfx); + snd_t *snd = sfx->snd; + int inrate = info->rate; + double stepscale = (double) snd->speed / inrate; + return frames * stepscale; +} + void -SND_Resample (sfxbuffer_t *sc, float *data, int length) +SND_Resample (sfxbuffer_t *sb, float *data, int length) { int outcount; double stepscale; - wavinfo_t *info = sc->sfx->wavinfo (sc->sfx); + wavinfo_t *info = (*sb->sfx)->wavinfo (*sb->sfx); + snd_t *snd = (*sb->sfx)->snd; int inrate = info->rate; int outwidth; SRC_DATA src_data; - stepscale = (double) snd_shm->speed / inrate; + stepscale = (double) snd->speed / inrate; outcount = length * stepscale; src_data.data_in = data; - src_data.data_out = sc->data + sc->head * sc->channels; + src_data.data_out = sb->data + sb->head * sb->channels; src_data.input_frames = length; src_data.output_frames = outcount; src_data.src_ratio = stepscale; - src_simple (&src_data, SRC_LINEAR, sc->channels); + src_simple (&src_data, SRC_LINEAR, sb->channels); outwidth = info->channels * sizeof (float); - check_buffer_integrity (sc, outwidth, __FUNCTION__); + check_buffer_integrity (sb, outwidth, __FUNCTION__); } static int snd_read (sfxstream_t *stream, float *data, int frames) { snd_null_state_t *state = (snd_null_state_t *) stream->state; - int channels = stream->buffer.channels; + int channels = stream->buffer->channels; int framesize = channels * sizeof (float); int count; int read = 0; @@ -119,7 +134,7 @@ static int snd_resample_read (sfxstream_t *stream, float *data, int frames) { int inrate = stream->wavinfo.rate; - double ratio = (double) snd_shm->speed / inrate; + double ratio = (double) stream->sfx->snd->speed / inrate; return src_callback_read (stream->state, ratio, frames, data); } @@ -139,27 +154,24 @@ snd_seek (sfxstream_t *stream, int pos) } void -SND_SetupResampler (sfxbuffer_t *sc, int streamed) +SND_SetupResampler (sfxbuffer_t *sb, int streamed) { double stepscale; - wavinfo_t *info = sc->sfx->wavinfo (sc->sfx); + wavinfo_t *info = (*sb->sfx)->wavinfo (*sb->sfx); + snd_t *snd = (*sb->sfx)->snd; int inrate = info->rate; - stepscale = (double) snd_shm->speed / inrate; + stepscale = (double) snd->speed / inrate; - sc->sfx->length = info->frames * stepscale; - if (info->loopstart != (unsigned int)-1) - sc->sfx->loopstart = info->loopstart * stepscale; - else - sc->sfx->loopstart = (unsigned int)-1; + sb->sfx_length = info->frames * stepscale; - sc->channels = info->channels; + sb->channels = info->channels; if (streamed) { int err; - sfxstream_t *stream = sc->sfx->data.stream; + sfxstream_t *stream = sb->stream; - if (snd_shm->speed == inrate) { + if (snd->speed == inrate) { stream->state = calloc (sizeof (snd_null_state_t), 1); stream->read = snd_read; } else { diff --git a/libs/audio/renderer/snd_sfx.c b/libs/audio/renderer/snd_sfx.c index 25d023de8..2082db1e0 100644 --- a/libs/audio/renderer/snd_sfx.c +++ b/libs/audio/renderer/snd_sfx.c @@ -53,8 +53,6 @@ static sfx_t snd_sfx[MAX_SFX]; static int snd_num_sfx; static hashtab_t *snd_sfx_hash; -static cvar_t *precache; - static const char * snd_sfx_getkey (const void *sfx, void *unused) { @@ -67,108 +65,106 @@ snd_sfx_free (void *_sfx, void *unused) sfx_t *sfx = (sfx_t *) _sfx; free ((char *) sfx->name); sfx->name = 0; - sfx->owner = 0; +} + +static void +snd_block_close (sfxbuffer_t *buffer) +{ + SND_Memory_Release (buffer); } void -SND_SFX_Cache (sfx_t *sfx, char *realname, wavinfo_t info, - cache_loader_t loader) +SND_SFX_Block (sfx_t *sfx, char *realname, wavinfo_t info, + sfxbuffer_t *(*load) (sfxblock_t *block)) { sfxblock_t *block = calloc (1, sizeof (sfxblock_t)); - sfx->data.block = block; - sfx->wavinfo = SND_CacheWavinfo; - sfx->touch = SND_CacheTouch; - sfx->retain = SND_CacheRetain; - sfx->release = SND_CacheRelease; - sfx->getbuffer = SND_CacheGetBuffer; + sfx->block = block; + sfx->wavinfo = SND_BlockWavinfo; + sfx->loopstart = SND_ResamplerFrames (sfx, info.loopstart); + sfx->length = SND_ResamplerFrames (sfx, info.frames); block->sfx = sfx; block->file = realname; block->wavinfo = info; - - Cache_Add (&block->cache, block, loader); + block->buffer = load (block); + SND_Memory_Retain (block->buffer); + block->buffer->close = snd_block_close; } void SND_SFX_Stream (sfx_t *sfx, char *realname, wavinfo_t info, - sfx_t *(*open) (sfx_t *sfx)) + sfxbuffer_t *(*open) (sfx_t *sfx)) { sfxstream_t *stream = calloc (1, sizeof (sfxstream_t)); sfx->open = open; - sfx->wavinfo = SND_CacheWavinfo; - sfx->touch = sfx->retain = SND_StreamRetain; - sfx->release = SND_StreamRelease; - sfx->getbuffer = SND_StreamGetBuffer; - sfx->data.stream = stream; + sfx->wavinfo = SND_StreamWavinfo; + sfx->stream = stream; + sfx->loopstart = SND_ResamplerFrames (sfx, info.loopstart); + sfx->length = SND_ResamplerFrames (sfx, info.frames); stream->file = realname; stream->wavinfo = info; } -sfx_t * +sfxbuffer_t * SND_SFX_StreamOpen (sfx_t *sfx, void *file, long (*read)(void *, float **), int (*seek)(sfxstream_t *, int), - void (*close) (sfx_t *)) + void (*close) (sfxbuffer_t *)) { - sfxstream_t *stream = sfx->data.stream; + snd_t *snd = sfx->snd; + sfxstream_t *stream = sfx->stream; wavinfo_t *info = &stream->wavinfo; int frames; - int size; // if the speed is 0, there is no sound driver (probably failed to connect // to jackd) - if (!snd_shm->speed) + if (!snd->speed) return 0; - sfx_t *new_sfx = calloc (1, sizeof (sfx_t)); - - new_sfx->name = sfx->name; - new_sfx->owner = sfx; - new_sfx->wavinfo = SND_CacheWavinfo; - new_sfx->touch = new_sfx->retain = SND_StreamRetain; - new_sfx->release = SND_StreamRelease; - new_sfx->getbuffer = SND_StreamGetBuffer; - new_sfx->close = close; - - frames = snd_shm->speed * 0.3; + frames = snd->speed * 0.3; frames = (frames + 255) & ~255; - size = frames * info->channels * sizeof (float); - stream = calloc (1, sizeof (sfxstream_t) + size); - new_sfx->data.stream = stream; - memcpy ((byte *) stream->buffer.data + size, "\xde\xad\xbe\xef", 4); + stream = calloc (1, sizeof (sfxstream_t)); + stream->buffer = SND_Memory_AllocBuffer (frames * info->channels); + if (!stream->buffer) { + free (stream); + return 0; + } + stream->file = file; - stream->sfx = new_sfx; + stream->sfx = sfx; stream->ll_read = read; stream->ll_seek = seek; stream->wavinfo = *sfx->wavinfo (sfx); - stream->buffer.length = frames; - stream->buffer.advance = SND_StreamAdvance; - stream->buffer.setpos = SND_StreamSetPos; - stream->buffer.sfx = new_sfx; - SND_SetPaint (&stream->buffer); + stream->buffer->stream = stream; + stream->buffer->size = frames; + stream->buffer->advance = SND_StreamAdvance; + stream->buffer->setpos = SND_StreamSetPos; + stream->buffer->sfx_length = info->frames; + stream->buffer->channels = info->channels; + stream->buffer->close = close; + SND_SetPaint (stream->buffer); - SND_SetupResampler (&stream->buffer, 1); // get sfx setup properly - stream->buffer.setpos (&stream->buffer, 0); // pre-fill the buffer + SND_SetupResampler (stream->buffer, 1); // get sfx setup properly + stream->buffer->setpos (stream->buffer, 0); // pre-fill the buffer - return new_sfx; + return stream->buffer; } void -SND_SFX_StreamClose (sfx_t *sfx) +SND_SFX_StreamClose (sfxstream_t *stream) { - sfxstream_t *stream = sfx->data.stream; SND_PulldownResampler (stream); + SND_Memory_Free (stream->buffer); free (stream); - free (sfx); } sfx_t * -SND_LoadSound (const char *name) +SND_LoadSound (snd_t *snd, const char *name) { sfx_t *sfx; @@ -181,8 +177,8 @@ SND_LoadSound (const char *name) Sys_Error ("s_load_sound: out of sfx_t"); sfx = &snd_sfx[snd_num_sfx++]; + sfx->snd = snd; sfx->name = strdup (name); - sfx->owner = sfx; if (SND_Load (sfx) == -1) { snd_num_sfx--; return 0; @@ -192,23 +188,19 @@ SND_LoadSound (const char *name) } sfx_t * -SND_PrecacheSound (const char *name) +SND_PrecacheSound (snd_t *snd, const char *name) { sfx_t *sfx; if (!name) Sys_Error ("SND_PrecacheSound: NULL"); - sfx = SND_LoadSound (va ("sound/%s", name)); - if (sfx && precache->int_val) { - if (sfx->retain (sfx)) - sfx->release (sfx); - } + sfx = SND_LoadSound (snd, va (0, "sound/%s", name)); return sfx; } static void -s_gamedir (int phase) +s_gamedir (int phase, void *data) { snd_num_sfx = 0; } @@ -216,41 +208,30 @@ s_gamedir (int phase) static void s_soundlist_f (void) { - int load, total, i; + int total, i; sfx_t *sfx; - if (Cmd_Argc() >= 2 && Cmd_Argv (1)[0]) - load = 1; - else - load = 0; - total = 0; for (sfx = snd_sfx, i = 0; i < snd_num_sfx; i++, sfx++) { - if (load) { - if (!sfx->retain (sfx)) - continue; - } else { - if (!sfx->touch (sfx)) - continue; - } total += sfx->length; Sys_Printf ("%6d %6d %s\n", sfx->loopstart, sfx->length, sfx->name); - - if (load) - sfx->release (sfx); } Sys_Printf ("Total resident: %i\n", total); } void -SND_SFX_Init (void) +SND_SFX_Init (snd_t *snd) { - snd_sfx_hash = Hash_NewTable (511, snd_sfx_getkey, snd_sfx_free, 0); - precache = Cvar_Get ("precache", "1", CVAR_NONE, NULL, - "Toggle the use of a precache"); + snd_sfx_hash = Hash_NewTable (511, snd_sfx_getkey, snd_sfx_free, 0, 0); - QFS_GamedirCallback (s_gamedir); + QFS_GamedirCallback (s_gamedir, 0); Cmd_AddCommand ("soundlist", s_soundlist_f, - "Reports a list of sounds in the cache"); + "Reports a list of loaded sounds"); +} + +void +SND_SFX_Shutdown (snd_t *snd) +{ + Hash_DelTable (snd_sfx_hash); } diff --git a/libs/audio/renderer/vorbis.c b/libs/audio/renderer/vorbis.c index fd14ec965..436fdd5c7 100644 --- a/libs/audio/renderer/vorbis.c +++ b/libs/audio/renderer/vorbis.c @@ -101,7 +101,7 @@ vorbis_get_info (OggVorbis_File *vf) samples = ov_pcm_total (vf, -1); for (ptr = ov_comment (vf, -1)->user_comments; *ptr; ptr++) { - Sys_MaskPrintf (SYS_DEV, "%s\n", *ptr); + Sys_MaskPrintf (SYS_snd, "%s\n", *ptr); if (strncmp ("CUEPOINT=", *ptr, 9) == 0) { sscanf (*ptr + 9, "%d %d", &sample_start, &sample_count); } @@ -118,11 +118,11 @@ vorbis_get_info (OggVorbis_File *vf) info.dataofs = 0; info.datalen = samples * info.channels * info.width; - Sys_MaskPrintf (SYS_DEV, "\nBitstream is %d channel, %dHz\n", + Sys_MaskPrintf (SYS_snd, "\nBitstream is %d channel, %dHz\n", info.channels, info.rate); - Sys_MaskPrintf (SYS_DEV, "\nDecoded length: %d samples (%d bytes)\n", + Sys_MaskPrintf (SYS_snd, "\nDecoded length: %d samples (%d bytes)\n", info.frames, info.width); - Sys_MaskPrintf (SYS_DEV, "Encoded by: %s\n\n", + Sys_MaskPrintf (SYS_snd, "Encoded by: %s\n\n", ov_comment (vf, -1)->vendor); return info; @@ -160,59 +160,60 @@ vorbis_read (OggVorbis_File *vf, float *buf, int len, wavinfo_t *info) } static sfxbuffer_t * -vorbis_load (OggVorbis_File *vf, sfxblock_t *block, cache_allocator_t allocator) +vorbis_load (OggVorbis_File *vf, sfxblock_t *block) { float *data; - sfxbuffer_t *sc = 0; - sfx_t *sfx = block->sfx; + sfxbuffer_t *sb = 0; + const sfx_t *sfx = block->sfx; wavinfo_t *info = &block->wavinfo; data = malloc (info->datalen); if (!data) goto bail; - sc = SND_GetCache (info->frames, info->rate, info->channels, - block, allocator); - if (!sc) + unsigned buffer_frames = SND_ResamplerFrames (sfx, info->frames); + sb = SND_Memory_AllocBuffer (buffer_frames * info->channels); + if (!sb) goto bail; - sc->sfx = sfx; + sb->size = buffer_frames * info->channels; + sb->channels = info->channels; + sb->sfx_length = info->frames; + sb->block = block; if (vorbis_read (vf, data, info->frames, info) < 0) goto bail; - SND_SetPaint (sc); - SND_SetupResampler (sc, 0); - SND_Resample (sc, data, info->frames); - sc->head = sc->length; + SND_SetPaint (sb); + SND_SetupResampler (sb, 0); + SND_Resample (sb, data, info->frames); + sb->head = sb->size; bail: if (data) free (data); ov_clear (vf); - return sc; + return sb; } -static void -vorbis_callback_load (void *object, cache_allocator_t allocator) +static sfxbuffer_t * +vorbis_callback_load (sfxblock_t *block) { QFile *file; OggVorbis_File vf; - sfxblock_t *block = (sfxblock_t *) object; - file = QFS_FOpenFile (block->file); if (!file) - return; //FIXME Sys_Error? + return 0; if (ov_open_callbacks (file, &vf, 0, 0, callbacks) < 0) { Sys_Printf ("Input does not appear to be an Ogg bitstream.\n"); Qclose (file); - return; //FIXME Sys_Error? + return 0; } - vorbis_load (&vf, block, allocator); + return vorbis_load (&vf, block); } -static void -vorbis_cache (sfx_t *sfx, char *realname, OggVorbis_File *vf, wavinfo_t info) +static void//extra _ in name because vorbis_block is a vorbis type +vorbis__block (sfx_t *sfx, char *realname, OggVorbis_File *vf, wavinfo_t info) { ov_clear (vf); - SND_SFX_Cache (sfx, realname, info, vorbis_callback_load); + SND_SFX_Block (sfx, realname, info, vorbis_callback_load); } static long @@ -241,22 +242,23 @@ vorbis_stream_seek (sfxstream_t *stream, int pos) } static void -vorbis_stream_close (sfx_t *sfx) +vorbis_stream_close (sfxbuffer_t *buffer) { - sfxstream_t *stream = sfx->data.stream; + sfxstream_t *stream = buffer->stream; vorbis_file_t *vf = (vorbis_file_t *) stream->file; if (vf->data) free (vf->data); ov_clear (vf->vf); + free (vf->vf); free (vf); - SND_SFX_StreamClose (sfx); + SND_SFX_StreamClose (stream); } -static sfx_t * +static sfxbuffer_t * vorbis_stream_open (sfx_t *sfx) { - sfxstream_t *stream = sfx->data.stream; + sfxstream_t *stream = sfx->stream; QFile *file; vorbis_file_t *f; @@ -292,7 +294,6 @@ SND_LoadOgg (QFile *file, sfx_t *sfx, char *realname) if (ov_open_callbacks (file, &vf, 0, 0, callbacks) < 0) { Sys_Printf ("Input does not appear to be an Ogg bitstream.\n"); - free (realname); return -1; } info = vorbis_get_info (&vf); @@ -301,10 +302,10 @@ SND_LoadOgg (QFile *file, sfx_t *sfx, char *realname) return -1; } if (info.frames / info.rate < 3) { - Sys_MaskPrintf (SYS_DEV, "cache %s\n", realname); - vorbis_cache (sfx, realname, &vf, info); + Sys_MaskPrintf (SYS_snd, "block %s\n", realname); + vorbis__block (sfx, realname, &vf, info); } else { - Sys_MaskPrintf (SYS_DEV, "stream %s\n", realname); + Sys_MaskPrintf (SYS_snd, "stream %s\n", realname); vorbis_stream (sfx, realname, &vf, info); } return 0; diff --git a/libs/audio/renderer/wav.c b/libs/audio/renderer/wav.c index b3d59697a..4d2131f85 100644 --- a/libs/audio/renderer/wav.c +++ b/libs/audio/renderer/wav.c @@ -54,48 +54,56 @@ typedef struct { QFile *file; } wav_file_t; -static void -wav_callback_load (void *object, cache_allocator_t allocator) +static sfxbuffer_t * +wav_callback_load (sfxblock_t *block) { - sfxblock_t *block = (sfxblock_t *) object; - sfx_t *sfx = block->sfx; + const sfx_t *sfx = block->sfx; const char *name = (const char *) block->file; QFile *file; int len, fdata_ofs; byte *data; float *fdata; - sfxbuffer_t *buffer; + sfxbuffer_t *buffer = 0; wavinfo_t *info = &block->wavinfo; file = QFS_FOpenFile (name); if (!file) - return; //FIXME Sys_Error? + return 0; Qseek (file, info->dataofs, SEEK_SET); fdata_ofs = (info->datalen + sizeof (float) - 1) & ~(sizeof (float) - 1); len = fdata_ofs + info->frames * info->channels * sizeof (float); data = malloc (len); + if (!data) + goto bail; fdata = (float *) (data + fdata_ofs); Qread (file, data, info->datalen); - Qclose (file); SND_Convert (data, fdata, info->frames, info->channels, info->width); - buffer = SND_GetCache (info->frames, info->rate, - info->channels, block, allocator); - buffer->sfx = sfx; + unsigned buffer_frames = SND_ResamplerFrames (sfx, info->frames); + buffer = SND_Memory_AllocBuffer (buffer_frames * info->channels); + if (!buffer) + goto bail; + buffer->size = buffer_frames * info->channels; + buffer->channels = info->channels; + buffer->sfx_length = info->frames; + buffer->block = block; SND_SetPaint (buffer); SND_SetupResampler (buffer, 0); SND_Resample (buffer, fdata, info->frames); - buffer->head = buffer->length; + buffer->head = buffer->size; +bail: free (data); + Qclose (file); + return buffer; } static void -wav_cache (sfx_t *sfx, char *realname, void *file, wavinfo_t info) +wav_block (sfx_t *sfx, char *realname, void *file, wavinfo_t info) { Qclose (file); - SND_SFX_Cache (sfx, realname, info, wav_callback_load); + SND_SFX_Block (sfx, realname, info, wav_callback_load); } static long @@ -133,22 +141,22 @@ wav_stream_seek (sfxstream_t *stream, int pos) } static void -wav_stream_close (sfx_t *sfx) +wav_stream_close (sfxbuffer_t *buffer) { - sfxstream_t *stream = sfx->data.stream; + sfxstream_t *stream = buffer->stream; wav_file_t *wf = (wav_file_t *) stream->file; Qclose (wf->file); if (wf->data) free (wf->data); free (wf); - SND_SFX_StreamClose (sfx); + SND_SFX_StreamClose (stream); } -static sfx_t * +static sfxbuffer_t * wav_stream_open (sfx_t *sfx) { - sfxstream_t *stream = sfx->data.stream; + sfxstream_t *stream = sfx->stream; QFile *file; wav_file_t *wf; @@ -181,14 +189,13 @@ wav_get_info (QFile *file) riff_data_t *data = 0; riff_cue_t *cue; - riff_d_cue_t *dcue; - riff_d_cue_point_t *cp = 0; + riff_d_cue_t *dcue = 0; riff_list_t *list; riff_d_chunk_t **lck; - riff_ltxt_t *ltxt; - riff_d_ltxt_t *dltxt = 0; + //riff_ltxt_t *ltxt; + //riff_d_ltxt_t *dltxt = 0; wavinfo_t info; @@ -211,8 +218,6 @@ wav_get_info (QFile *file) case RIFF_CASE ('c','u','e',' '): cue = (riff_cue_t *) *ck; dcue = cue->cue; - if (dcue->count) - cp = &dcue->cue_points[dcue->count - 1]; break; case RIFF_CASE ('L','I','S','T'): list = (riff_list_t *) *ck; @@ -221,8 +226,8 @@ wav_get_info (QFile *file) for (lck = list->chunks; *lck; lck++) { RIFF_SWITCH ((*lck)->name) { case RIFF_CASE ('l','t','x','t'): - ltxt = (riff_ltxt_t *) *lck; - dltxt = <xt->ltxt; + //ltxt = (riff_ltxt_t *) *lck; + //dltxt = <xt->ltxt; break; } } @@ -252,15 +257,19 @@ wav_get_info (QFile *file) info.width = dfmt->bits_per_sample / 8; info.channels = dfmt->channels; info.frames = 0; - if (cp) { - info.loopstart = cp->sample_offset; - if (dltxt) - info.frames = info.loopstart + dltxt->len; + info.frames = data->ck.len / (info.width * info.channels); + if (dcue && dcue->count) { + if (dcue->cue_points[0].sample_offset < info.frames) { + info.loopstart = dcue->cue_points[0].sample_offset; + } + if (dcue->count > 1) { + info.frames = min (info.frames, dcue->cue_points[1].sample_offset); + } + //if (dltxt) + // info.frames = info.loopstart + dltxt->len; } else { info.loopstart = -1; } - if (!info.frames) - info.frames = data->ck.len / (info.width * info.channels); info.dataofs = *(int *)data->data; info.datalen = data->ck.len; @@ -280,10 +289,10 @@ SND_LoadWav (QFile *file, sfx_t *sfx, char *realname) } if (info.frames / info.rate < 3) { - Sys_MaskPrintf (SYS_DEV, "cache %s\n", realname); - wav_cache (sfx, realname, file, info); + Sys_MaskPrintf (SYS_snd, "block %s\n", realname); + wav_block (sfx, realname, file, info); } else { - Sys_MaskPrintf (SYS_DEV, "stream %s\n", realname); + Sys_MaskPrintf (SYS_snd, "stream %s\n", realname); wav_stream (sfx, realname, file, info); } return 0; diff --git a/libs/audio/snd.c b/libs/audio/snd.c index 6f4ecba05..c665e7f72 100644 --- a/libs/audio/snd.c +++ b/libs/audio/snd.c @@ -35,10 +35,28 @@ #include "QF/qargs.h" #include "QF/sys.h" +#include "QF/scene/transform.h" + #include "snd_internal.h" -static cvar_t *snd_output; -static cvar_t *snd_render; +static char *snd_output; +static cvar_t snd_output_cvar = { + .name = "snd_output", + .description = + "Sound Output Plugin to use", + .default_value = SND_OUTPUT_DEFAULT, + .flags = CVAR_ROM, + .value = { .type = 0, .value = &snd_output }, +}; +static char *snd_render; +static cvar_t snd_render_cvar = { + .name = "snd_render", + .description = + "Sound Renderer Plugin to use", + .default_value = "default", + .flags = CVAR_ROM, + .value = { .type = 0, .value = &snd_render }, +}; static plugin_t *snd_render_module = NULL; static plugin_t *snd_output_module = NULL; static snd_render_funcs_t *snd_render_funcs = NULL; @@ -53,6 +71,19 @@ static plugin_list_t snd_render_list[] = { SND_RENDER_PLUGIN_LIST }; +static void +S_shutdown (void *data) +{ + if (snd_render_module) { + PI_UnloadPlugin (snd_render_module); + snd_render_module = NULL; + snd_render_funcs = NULL; + } + if (snd_output_module) { + PI_UnloadPlugin (snd_output_module); + snd_output_module = NULL; + } +} VISIBLE void S_Init (int *viewentity, double *host_frametime) @@ -60,22 +91,24 @@ S_Init (int *viewentity, double *host_frametime) if (COM_CheckParm ("-nosound")) return; - if (!*snd_output->string || !*snd_render->string) { + if (!*snd_output || !*snd_render) { Sys_Printf ("Not loading sound due to no renderer/output\n"); return; } + Sys_RegisterShutdown (S_shutdown, 0); + PI_RegisterPlugins (snd_output_list); PI_RegisterPlugins (snd_render_list); - snd_output_module = PI_LoadPlugin ("snd_output", snd_output->string); + snd_output_module = PI_LoadPlugin ("snd_output", snd_output); if (!snd_output_module) { Sys_Printf ("Loading of sound output module: %s failed!\n", - snd_output->string); + snd_output); } else { - snd_render_module = PI_LoadPlugin ("snd_render", snd_render->string); + snd_render_module = PI_LoadPlugin ("snd_render", snd_render); if (!snd_render_module) { Sys_Printf ("Loading of sound render module: %s failed!\n", - snd_render->string); + snd_render); PI_UnloadPlugin (snd_output_module); snd_output_module = NULL; } else { @@ -84,8 +117,7 @@ S_Init (int *viewentity, double *host_frametime) host_frametime; snd_render_module->data->snd_render->output = snd_output_module; - snd_output_module->functions->general->p_Init (); - snd_render_module->functions->general->p_Init (); + snd_render_module->functions->snd_render->init (); snd_output_module->data->snd_output->soundtime = snd_render_module->data->snd_render->soundtime; @@ -100,53 +132,44 @@ S_Init (int *viewentity, double *host_frametime) VISIBLE void S_Init_Cvars (void) { - snd_output = Cvar_Get ("snd_output", SND_OUTPUT_DEFAULT, CVAR_ROM, NULL, - "Sound Output Plugin to use"); - snd_render = Cvar_Get ("snd_render", "default", CVAR_ROM, NULL, - "Sound Renderer Plugin to use"); + Cvar_Register (&snd_output_cvar, 0, 0); + Cvar_Register (&snd_render_cvar, 0, 0); } VISIBLE void S_AmbientOff (void) { if (snd_render_funcs) - snd_render_funcs->pS_AmbientOff (); + snd_render_funcs->ambient_off (); } VISIBLE void S_AmbientOn (void) { if (snd_render_funcs) - snd_render_funcs->pS_AmbientOn (); + snd_render_funcs->ambient_on (); } VISIBLE void -S_Shutdown (void) -{ - if (snd_render_module) { - PI_UnloadPlugin (snd_render_module); - snd_render_module = NULL; - snd_render_funcs = NULL; - } - if (snd_output_module) { - PI_UnloadPlugin (snd_output_module); - snd_output_module = NULL; - } -} - -VISIBLE void -S_StaticSound (sfx_t *sfx, const vec3_t origin, float vol, float attenuation) +S_SetAmbient (int amb_channel, sfx_t *sfx) { if (snd_render_funcs) - snd_render_funcs->pS_StaticSound (sfx, origin, vol, attenuation); + snd_render_funcs->set_ambient (amb_channel, sfx); } VISIBLE void -S_StartSound (int entnum, int entchannel, sfx_t *sfx, const vec3_t origin, - float fvol, float attenuation) +S_StaticSound (sfx_t *sfx, vec4f_t origin, float vol, float attenuation) { if (snd_render_funcs) - snd_render_funcs->pS_StartSound (entnum, entchannel, sfx, origin, fvol, + snd_render_funcs->static_sound (sfx, origin, vol, attenuation); +} + +VISIBLE void +S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec4f_t origin, + float vol, float attenuation) +{ + if (snd_render_funcs) + snd_render_funcs->start_sound (entnum, entchannel, sfx, origin, vol, attenuation); } @@ -154,80 +177,121 @@ VISIBLE void S_StopSound (int entnum, int entchannel) { if (snd_render_funcs) - snd_render_funcs->pS_StopSound (entnum, entchannel); + snd_render_funcs->stop_sound (entnum, entchannel); } VISIBLE sfx_t * S_PrecacheSound (const char *sample) { if (snd_render_funcs) - return snd_render_funcs->pS_PrecacheSound (sample); + return snd_render_funcs->precache_sound (sample); return NULL; } VISIBLE void -S_Update (const vec3_t origin, const vec3_t v_forward, const vec3_t v_right, - const vec3_t v_up, const byte *ambient_sound_level) +S_Update (transform_t ear, const byte *ambient_sound_level) { if (snd_render_funcs) - snd_render_funcs->pS_Update (origin, v_forward, v_right, v_up, - ambient_sound_level); + snd_render_funcs->update (ear, ambient_sound_level); } VISIBLE void S_StopAllSounds (void) { if (snd_render_funcs) - snd_render_funcs->pS_StopAllSounds (); + snd_render_funcs->stop_all_sounds (); } VISIBLE void S_ExtraUpdate (void) { -// if (snd_render_funcs) -// snd_render_funcs->pS_ExtraUpdate (); + if (snd_render_funcs && snd_render_funcs->extra_update) + snd_render_funcs->extra_update (); } VISIBLE void S_LocalSound (const char *s) { if (snd_render_funcs) - snd_render_funcs->pS_LocalSound (s); + snd_render_funcs->local_sound (s); } VISIBLE void S_BlockSound (void) { if (snd_render_funcs) - snd_render_funcs->pS_BlockSound (); + snd_render_funcs->block_sound (); } VISIBLE void S_UnblockSound (void) { if (snd_render_funcs) - snd_render_funcs->pS_UnblockSound (); + snd_render_funcs->unblock_sound (); } VISIBLE sfx_t * S_LoadSound (const char *name) { if (snd_render_funcs) - return snd_render_funcs->pS_LoadSound (name); + return snd_render_funcs->load_sound (name); return 0; } -VISIBLE struct channel_s * +VISIBLE channel_t * S_AllocChannel (void) { if (snd_render_funcs) - return snd_render_funcs->pS_AllocChannel (); + return snd_render_funcs->alloc_channel (); return 0; } VISIBLE void -S_ChannelStop (struct channel_s *chan) +S_ChannelFree (channel_t *chan) { - if (snd_render_funcs) - snd_render_funcs->pS_ChannelStop (chan); + if (snd_render_funcs) { + snd_render_funcs->channel_free (chan); + } +} + +VISIBLE int +S_ChannelSetSfx (channel_t *chan, sfx_t *sfx) +{ + if (snd_render_funcs) { + return snd_render_funcs->channel_set_sfx (chan, sfx); + } + return 0; +} + +VISIBLE void +S_ChannelSetPaused (channel_t *chan, int paused) +{ + if (snd_render_funcs) { + snd_render_funcs->channel_set_paused (chan, paused); + } +} + +VISIBLE void +S_ChannelSetLooping (channel_t *chan, int looping) +{ + if (snd_render_funcs) { + snd_render_funcs->channel_set_looping (chan, looping); + } +} + +VISIBLE chan_state +S_ChannelGetState (channel_t *chan) +{ + if (snd_render_funcs) { + return snd_render_funcs->channel_get_state (chan); + } + return 0; +} + +VISIBLE void +S_ChannelSetVolume (channel_t *chan, float volume) +{ + if (snd_render_funcs) { + snd_render_funcs->channel_set_volume (chan, volume); + } } diff --git a/libs/audio/snd_progs.c b/libs/audio/snd_progs.c index a130fd44c..52eeb945d 100644 --- a/libs/audio/snd_progs.c +++ b/libs/audio/snd_progs.c @@ -40,20 +40,22 @@ #include "QF/sound.h" static void -bi_S_LocalSound (progs_t *pr) +bi_S_LocalSound (progs_t *pr, void *data) { const char *sound = P_GSTRING (pr, 0); S_LocalSound (sound); } +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) static builtin_t builtins[] = { - {"S_LocalSound", bi_S_LocalSound, -1}, + bi(S_LocalSound, 1, p(string)), {0} }; VISIBLE void S_Progs_Init (progs_t *pr) { - PR_RegisterBuiltins (pr, builtins); + PR_RegisterBuiltins (pr, builtins, 0); } diff --git a/libs/audio/targets/Makefile.am b/libs/audio/targets/Makefile.am deleted file mode 100644 index ff0411354..000000000 --- a/libs/audio/targets/Makefile.am +++ /dev/null @@ -1,62 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CFLAGS= @PREFER_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include -SDL_LIBS = @SDL_LIBS@ -plugin_ldflags= @plugin_ldflags@ -avoid-version -module -rpath $(plugindir) -plugin_libadd= @plugin_libadd@ -EXEEXT= -snd_deps=$(top_builddir)/libs/util/libQFutil.la - -plugin_LTLIBRARIES= @snd_output_plugins@ -noinst_LTLIBRARIES= @snd_output_static_plugins@ -EXTRA_LTLIBRARIES= \ - snd_output_sdl.la snd_output_alsa.la snd_output_oss.la snd_output_sgi.la \ - snd_output_sun.la snd_output_win.la snd_output_dx.la snd_output_disk.la - -snd_output_sdl_la_LDFLAGS= $(plugin_ldflags) -snd_output_sdl_la_LIBADD= $(snd_deps) $(SDL_LIBS) $(plugin_libadd) -snd_output_sdl_la_DEPENDENCIES= $(snd_deps) -snd_output_sdl_la_CFLAGS= $(SDL_CFLAGS) -snd_output_sdl_la_SOURCES= snd_sdl.c - -snd_output_alsa_la_LDFLAGS= $(plugin_ldflags) -snd_output_alsa_la_LIBADD= $(snd_deps) -snd_output_alsa_la_DEPENDENCIES=$(snd_deps) -snd_output_alsa_la_CFLAGS= $(ALSA_CFLAGS) -snd_output_alsa_la_SOURCES= snd_alsa.c - -snd_output_oss_la_LDFLAGS= $(plugin_ldflags) -snd_output_oss_la_LIBADD= $(snd_deps) $(OSS_LIBS) -snd_output_oss_la_DEPENDENCIES= $(snd_deps) -snd_output_oss_la_CFLAGS= $(OSS_CFLAGS) -snd_output_oss_la_SOURCES= snd_oss.c - -snd_output_sgi_la_LDFLAGS= $(plugin_ldflags) -snd_output_sgi_la_LIBADD= $(snd_deps) $(SGISND_LIBS) -snd_output_sgi_la_DEPENDENCIES= $(snd_deps) -snd_output_sgi_la_CFLAGS= $(SGISND_CFLAGS) -snd_output_sgi_la_SOURCES= snd_sgi.c - -snd_output_sun_la_LDFLAGS= $(plugin_ldflags) -snd_output_sun_la_DEPENDENCIES= $(snd_deps) -snd_output_sun_la_CFLAGS= $(SUNSND_CFLAGS) -snd_output_sun_la_SOURCES= snd_sun.c - -snd_output_win_la_LDFLAGS= $(plugin_ldflags) -snd_output_win_la_LIBADD= $(snd_deps) $(WINSND_LIBS) $(plugin_libadd) -snd_output_win_la_DEPENDENCIES= $(snd_deps) -snd_output_win_la_CFLAGS= $(WIN32SND_CFLAGS) -snd_output_win_la_SOURCES= snd_win.c - -snd_output_dx_la_LDFLAGS= $(plugin_ldflags) -snd_output_dx_la_LIBADD= $(snd_deps) $(WINSND_LIBS) $(plugin_libadd) -snd_output_dx_la_DEPENDENCIES= $(snd_deps) -snd_output_dx_la_CFLAGS= $(WIN32SND_CFLAGS) -snd_output_dx_la_SOURCES= snd_dx.c - -snd_output_disk_la_LDFLAGS= $(plugin_ldflags) -snd_output_disk_la_LIBADD= $(snd_deps) $(plugin_libadd) -snd_output_disk_la_DEPENDENCIES=$(snd_deps) -snd_output_disk_la_CFLAGS= -snd_output_disk_la_SOURCES= snd_disk.c diff --git a/libs/audio/targets/Makemodule.am b/libs/audio/targets/Makemodule.am new file mode 100644 index 000000000..8997eb847 --- /dev/null +++ b/libs/audio/targets/Makemodule.am @@ -0,0 +1,67 @@ +plugin_LTLIBRARIES += @snd_output_plugins@ +noinst_LTLIBRARIES += @snd_output_static_plugins@ +EXTRA_LTLIBRARIES += \ + libs/audio/targets/snd_output_sdl.la \ + libs/audio/targets/snd_output_alsa.la \ + libs/audio/targets/snd_output_jack.la \ + libs/audio/targets/snd_output_oss.la \ + libs/audio/targets/snd_output_sgi.la \ + libs/audio/targets/snd_output_sun.la \ + libs/audio/targets/snd_output_win.la \ + libs/audio/targets/snd_output_dx.la \ + libs/audio/targets/snd_output_disk.la + +snd_deps=libs/util/libQFutil.la + +libs_audio_targets_snd_output_sdl_la_LDFLAGS= $(plugin_ldflags) +libs_audio_targets_snd_output_sdl_la_LIBADD= $(snd_deps) $(SDL_LIBS) $(plugin_libadd) +libs_audio_targets_snd_output_sdl_la_DEPENDENCIES= $(snd_deps) +libs_audio_targets_snd_output_sdl_la_CFLAGS= $(SDL_CFLAGS) +libs_audio_targets_snd_output_sdl_la_SOURCES= libs/audio/targets/snd_sdl.c + +libs_audio_targets_snd_output_alsa_la_LDFLAGS= $(plugin_ldflags) +libs_audio_targets_snd_output_alsa_la_LIBADD= $(snd_deps) +libs_audio_targets_snd_output_alsa_la_DEPENDENCIES= $(snd_deps) +libs_audio_targets_snd_output_alsa_la_CFLAGS= $(ALSA_CFLAGS) +libs_audio_targets_snd_output_alsa_la_SOURCES= libs/audio/targets/snd_alsa.c + +libs_audio_targets_snd_output_jack_la_LDFLAGS= $(plugin_ldflags) +libs_audio_targets_snd_output_jack_la_LIBADD= $(snd_deps) $(JACK_LIBS) +libs_audio_targets_snd_output_jack_la_DEPENDENCIES= $(snd_libs) +libs_audio_targets_snd_output_jack_la_CFLAGS= $(JACK_CFLAGS) +libs_audio_targets_snd_output_jack_la_SOURCES= libs/audio/targets/snd_jack.c + +libs_audio_targets_snd_output_oss_la_LDFLAGS= $(plugin_ldflags) +libs_audio_targets_snd_output_oss_la_LIBADD= $(snd_deps) $(OSS_LIBS) +libs_audio_targets_snd_output_oss_la_DEPENDENCIES= $(snd_deps) +libs_audio_targets_snd_output_oss_la_CFLAGS= $(OSS_CFLAGS) +libs_audio_targets_snd_output_oss_la_SOURCES= libs/audio/targets/snd_oss.c + +libs_audio_targets_snd_output_sgi_la_LDFLAGS= $(plugin_ldflags) +libs_audio_targets_snd_output_sgi_la_LIBADD= $(snd_deps) $(SGISND_LIBS) +libs_audio_targets_snd_output_sgi_la_DEPENDENCIES= $(snd_deps) +libs_audio_targets_snd_output_sgi_la_CFLAGS= $(SGISND_CFLAGS) +libs_audio_targets_snd_output_sgi_la_SOURCES= libs/audio/targets/snd_sgi.c + +libs_audio_targets_snd_output_sun_la_LDFLAGS= $(plugin_ldflags) +libs_audio_targets_snd_output_sun_la_DEPENDENCIES= $(snd_deps) +libs_audio_targets_snd_output_sun_la_CFLAGS= $(SUNSND_CFLAGS) +libs_audio_targets_snd_output_sun_la_SOURCES= libs/audio/targets/snd_sun.c + +libs_audio_targets_snd_output_win_la_LDFLAGS= $(plugin_ldflags) +libs_audio_targets_snd_output_win_la_LIBADD= $(snd_deps) $(WINSND_LIBS) $(plugin_libadd) +libs_audio_targets_snd_output_win_la_DEPENDENCIES= $(snd_deps) +libs_audio_targets_snd_output_win_la_CFLAGS= $(WIN32SND_CFLAGS) +libs_audio_targets_snd_output_win_la_SOURCES= libs/audio/targets/snd_win.c + +libs_audio_targets_snd_output_dx_la_LDFLAGS= $(plugin_ldflags) +libs_audio_targets_snd_output_dx_la_LIBADD= $(snd_deps) $(WINSND_LIBS) $(plugin_libadd) +libs_audio_targets_snd_output_dx_la_DEPENDENCIES= $(snd_deps) +libs_audio_targets_snd_output_dx_la_CFLAGS= $(WIN32SND_CFLAGS) +libs_audio_targets_snd_output_dx_la_SOURCES= libs/audio/targets/snd_dx.c + +libs_audio_targets_snd_output_disk_la_LDFLAGS= $(plugin_ldflags) +libs_audio_targets_snd_output_disk_la_LIBADD= $(snd_deps) $(plugin_libadd) +libs_audio_targets_snd_output_disk_la_DEPENDENCIES=$(snd_deps) +libs_audio_targets_snd_output_disk_la_CFLAGS= +libs_audio_targets_snd_output_disk_la_SOURCES= libs/audio/targets/snd_disk.c diff --git a/libs/audio/targets/snd_alsa.c b/libs/audio/targets/snd_alsa.c index 864055ccc..8c55c6005 100644 --- a/libs/audio/targets/snd_alsa.c +++ b/libs/audio/targets/snd_alsa.c @@ -39,34 +39,67 @@ #include "snd_internal.h" +typedef struct { + const snd_pcm_channel_area_t *areas; + snd_pcm_uframes_t offset; + snd_pcm_uframes_t nframes; +} alsa_pkt_t; + static int snd_inited; static int snd_blocked = 0; -static volatile dma_t sn; static snd_pcm_uframes_t buffer_size; static void *alsa_handle; -static const char *pcmname = NULL; static snd_pcm_t *pcm; +static snd_async_handler_t *async_handler; -static plugin_t plugin_info; -static plugin_data_t plugin_info_data; -static plugin_funcs_t plugin_info_funcs; -static general_data_t plugin_info_general_data; -static general_funcs_t plugin_info_general_funcs; -static snd_output_data_t plugin_info_snd_output_data; -static snd_output_funcs_t plugin_info_snd_output_funcs; +static int snd_bits; +static cvar_t snd_bits_cvar = { + .name = "snd_bits", + .description = + "sound sample depth. 0 is system default", + .default_value = "0", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &snd_bits }, +}; +static char *snd_device; +static cvar_t snd_device_cvar = { + .name = "snd_device", + .description = + "sound device. \"\" is system default", + .default_value = "", + .flags = CVAR_ROM, + .value = { .type = 0, .value = &snd_device }, +}; +static int snd_rate; +static cvar_t snd_rate_cvar = { + .name = "snd_rate", + .description = + "sound playback rate. 0 is system default", + .default_value = "0", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &snd_rate }, +}; +static int snd_stereo; +static cvar_t snd_stereo_cvar = { + .name = "snd_stereo", + .description = + "sound stereo output", + .default_value = "1", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &snd_stereo }, +}; -static cvar_t *snd_bits; -static cvar_t *snd_device; -static cvar_t *snd_rate; -static cvar_t *snd_stereo; +//FIXME xfer probably should not be touching this (such data should probably +//come through snd_t) +static snd_output_data_t plugin_info_snd_output_data; #define QF_ALSA_NEED(ret, func, params) \ static ret (*qf##func) params; #include "alsa_funcs_list.h" #undef QF_ALSA_NEED -static qboolean +static bool load_libasound (void) { if (!(alsa_handle = dlopen ("libasound.so.2", RTLD_GLOBAL | RTLD_NOW))) { @@ -91,19 +124,13 @@ load_libasound (void) static void SNDDMA_Init_Cvars (void) { - snd_stereo = Cvar_Get ("snd_stereo", "1", CVAR_ROM, NULL, - "sound stereo output"); - snd_rate = Cvar_Get ("snd_rate", "0", CVAR_ROM, NULL, - "sound playback rate. 0 is system default"); - snd_device = Cvar_Get ("snd_device", "", CVAR_ROM, NULL, - "sound device. \"\" is system default"); - snd_bits = Cvar_Get ("snd_bits", "0", CVAR_ROM, NULL, - "sound sample depth. 0 is system default"); + Cvar_Register (&snd_stereo_cvar, 0, 0); + Cvar_Register (&snd_rate_cvar, 0, 0); + Cvar_Register (&snd_device_cvar, 0, 0); + Cvar_Register (&snd_bits_cvar, 0, 0); } -static int SNDDMA_GetDMAPos (void); - -static snd_pcm_uframes_t +static __attribute__((const)) snd_pcm_uframes_t round_buffer_size (snd_pcm_uframes_t sz) { snd_pcm_uframes_t mask = ~0; @@ -136,25 +163,26 @@ clamp_8 (int val) } static void -SNDDMA_ni_xfer (portable_samplepair_t *paintbuffer, int count, float volume) +alsa_ni_xfer (snd_t *snd, portable_samplepair_t *paintbuffer, int count, + float volume) { const snd_pcm_channel_area_t *areas; int out_idx, out_max; float *p; - areas = sn.xfer_data; + areas = snd->xfer_data; p = (float *) paintbuffer; - out_max = sn.frames - 1; + out_max = snd->frames - 1; out_idx = *plugin_info_snd_output_data.paintedtime; while (out_idx > out_max) out_idx -= out_max + 1; - if (sn.samplebits == 16) { + if (snd->samplebits == 16) { short *out_0 = (short *) areas[0].addr; short *out_1 = (short *) areas[1].addr; - if (sn.channels == 2) { + if (snd->channels == 2) { while (count--) { out_0[out_idx] = clamp_16 ((*p++ * volume) * 0x8000); out_1[out_idx] = clamp_16 ((*p++ * volume) * 0x8000); @@ -169,11 +197,11 @@ SNDDMA_ni_xfer (portable_samplepair_t *paintbuffer, int count, float volume) out_idx = 0; } } - } else if (sn.samplebits == 8) { + } else if (snd->samplebits == 8) { byte *out_0 = (byte *) areas[0].addr; byte *out_1 = (byte *) areas[1].addr; - if (sn.channels == 2) { + if (snd->channels == 2) { while (count--) { out_0[out_idx] = clamp_8 ((*p++ * volume) * 0x80); out_1[out_idx] = clamp_8 ((*p++ * volume) * 0x80); @@ -191,333 +219,499 @@ SNDDMA_ni_xfer (portable_samplepair_t *paintbuffer, int count, float volume) } } -static volatile dma_t * -SNDDMA_Init (void) +static void +alsa_xfer (snd_t *snd, portable_samplepair_t *paintbuffer, int count, + float volume) { - int err; - int bps = -1, stereo = -1; - unsigned int rate = 0; - snd_pcm_hw_params_t *hw; - snd_pcm_hw_params_t **_hw = &hw; - snd_pcm_sw_params_t *sw; - snd_pcm_sw_params_t **_sw = &sw; - snd_pcm_uframes_t frag_size; + int out_idx, out_max, step, val; + float *p; + alsa_pkt_t *packet = snd->xfer_data;; - if (!load_libasound ()) - return false; + p = (float *) paintbuffer; + count *= snd->channels; + out_max = (snd->frames * snd->channels) - 1; + out_idx = snd->paintedtime * snd->channels; + while (out_idx > out_max) + out_idx -= out_max + 1; + step = 3 - snd->channels; - snd_pcm_hw_params_alloca (_hw); - snd_pcm_sw_params_alloca (_sw); + if (snd->samplebits == 16) { + short *out = (short *) packet->areas[0].addr; - if (snd_device->string[0]) - pcmname = snd_device->string; - if (snd_bits->int_val) { - bps = snd_bits->int_val; - if (bps != 16 && bps != 8) { - Sys_Printf ("Error: invalid sample bits: %d\n", bps); - return 0; + while (count--) { + val = (*p * volume) * 0x8000; + p += step; + if (val > 0x7fff) + val = 0x7fff; + else if (val < -0x8000) + val = -0x8000; + out[out_idx++] = val; + if (out_idx > out_max) + out_idx = 0; + } + } else if (snd->samplebits == 8) { + unsigned char *out = (unsigned char *) packet->areas[0].addr; + + while (count--) { + val = (*p * volume) * 128; + p += step; + if (val > 0x7f) + val = 0x7f; + else if (val < -0x80) + val = -0x80; + out[out_idx++] = val + 0x80; + if (out_idx > out_max) + out_idx = 0; } } - if (snd_rate->int_val) - rate = snd_rate->int_val; - stereo = snd_stereo->int_val; - if (!pcmname) - pcmname = "default"; -retry_open: - err = qfsnd_pcm_open (&pcm, pcmname, SND_PCM_STREAM_PLAYBACK, - SND_PCM_NONBLOCK); - if (0 > err) { - Sys_Printf ("Error: audio open error: %s\n", qfsnd_strerror (err)); +} + +static int +alsa_recover (snd_pcm_t *pcm, int err) +{ + if (err == -EPIPE) { + Sys_Printf ("snd_alsa: xrun\n"); + // xrun + if ((err = qfsnd_pcm_prepare (pcm)) < 0) { + Sys_MaskPrintf (SYS_snd, "snd_alsa: recover from xrun failed: %s\n", + qfsnd_strerror (err)); + return err; + } + return 0; + } else if (err == -ESTRPIPE) { + Sys_Printf ("snd_alsa: suspend\n"); + // suspend + while ((err = qfsnd_pcm_resume(pcm)) == -EAGAIN) { + usleep (20 * 1000); + } + if (err < 0 && (err = qfsnd_pcm_prepare (pcm)) < 0) { + Sys_MaskPrintf (SYS_snd, + "snd_alsa: recover from suspend failed: %s\n", + qfsnd_strerror (err)); + return err; + } + return 0; + } + return err; +} + +static int +alsa_process (snd_pcm_t *pcm, snd_t *snd) +{ + alsa_pkt_t packet; + int res; + int ret = 1; + snd_pcm_uframes_t size = snd->submission_chunk; + + snd->xfer_data = &packet; + while (size > 0) { + packet.nframes = size; + if ((res = qfsnd_pcm_mmap_begin (pcm, &packet.areas, &packet.offset, + &packet.nframes)) < 0) { + if ((res = alsa_recover (pcm, -EPIPE)) < 0) { + Sys_Printf ("snd_alsa: XRUN recovery failed: %s\n", + qfsnd_strerror (res)); + snd->xfer_data = 0; + return res; + } + ret = 0; + } + snd->buffer = packet.areas[0].addr; + snd->paint_channels (snd, snd->paintedtime + packet.nframes); + if ((res = qfsnd_pcm_mmap_commit (pcm, packet.offset, + packet.nframes)) < 0 + || (snd_pcm_uframes_t) res != packet.nframes) { + if ((res = alsa_recover (pcm, res >= 0 ? -EPIPE : res)) < 0) { + Sys_Printf ("snd_alsa: XRUN recovery failed: %s\n", + qfsnd_strerror (res)); + snd->xfer_data = 0; + return res; + } + ret = 0; + } + size -= packet.nframes; + } + + snd->xfer_data = 0; + return ret; +} + +static void +alsa_callback (snd_async_handler_t *handler) +{ + snd_pcm_t *pcm = qfsnd_async_handler_get_pcm (handler); + snd_t *snd = qfsnd_async_handler_get_callback_private (handler); + int res; + int avail; + int first = 0; + + while (1) { + snd_pcm_state_t state = qfsnd_pcm_state (pcm); + if (state == SND_PCM_STATE_XRUN) { + if ((res = alsa_recover (pcm, -EPIPE)) < 0) { + Sys_Printf ("snd_alsa: XRUN recovery failed: %s\n", + qfsnd_strerror (res)); + //FIXME disable/restart sound + return; + } + } else if (state == SND_PCM_STATE_SUSPENDED) { + if ((res = alsa_recover (pcm, -EPIPE)) < 0) { + Sys_Printf ("snd_alsa: suspend recovery failed: %s\n", + qfsnd_strerror (res)); + //FIXME disable/restart sound + return; + } + } + if ((avail = qfsnd_pcm_avail_update (pcm)) < 0) { + if ((res = alsa_recover (pcm, -EPIPE)) < 0) { + Sys_Printf ("snd_alsa: avail update failed: %s\n", + qfsnd_strerror (res)); + //FIXME disable/restart sound + return; + } + first = 1; + continue; + } + if (avail < snd->submission_chunk) { + if (first) { + first = 0; + if ((res = qfsnd_pcm_start (pcm)) < 0) { + Sys_Printf ("snd_alsa: start failed: %s\n", + qfsnd_strerror (res)); + return; + } + continue; + } + break; + } + + if ((res = alsa_process (pcm, snd))) { + if (res < 0) { + //FIXME disable/restart sound + return; + } + break; + } + first = 1; + } +} + +static int +alsa_open_playback (snd_t *snd, const char *device) +{ + if (!*device) { + device = "default"; + } + Sys_Printf ("Using PCM %s.\n", device); + + int res = qfsnd_pcm_open (&pcm, device, SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK); + if (res < 0) { + Sys_Printf ("snd_alsa: audio open error: %s\n", qfsnd_strerror (res)); return 0; } - err = qfsnd_pcm_hw_params_any (pcm, hw); - if (0 > err) { - Sys_Printf ("ALSA: error setting hw_params_any. %s\n", - qfsnd_strerror (err)); - goto error; - } + return 1; +} - err = qfsnd_pcm_hw_params_set_access (pcm, hw, +static int +alsa_playback_set_mmap (snd_t *snd, snd_pcm_hw_params_t *hw) +{ + int res; + + res = qfsnd_pcm_hw_params_set_access (pcm, hw, SND_PCM_ACCESS_MMAP_INTERLEAVED); - if (0 > err) { - Sys_MaskPrintf (SYS_SND, "ALSA: Failure to set interleaved PCM " - "access. %s\n", qfsnd_strerror (err)); - err = qfsnd_pcm_hw_params_set_access (pcm, hw, + if (res == 0) { + snd->xfer = alsa_xfer; + return 1; + } + Sys_MaskPrintf (SYS_snd, "snd_alsa: Failure to set interleaved PCM " + "access. (%d) %s\n", res, qfsnd_strerror (res)); + res = qfsnd_pcm_hw_params_set_access (pcm, hw, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); - if (0 > err) { - Sys_MaskPrintf (SYS_SND, "ALSA: Failure to set noninterleaved PCM " - "access. %s\n", qfsnd_strerror (err)); - // "default" did not work, so retry with "plughw". However do not - // second guess the user, even if the user specified "default". - if (!snd_device->string[0] && !strcmp (pcmname, "default")) { - pcmname = "plughw"; - goto retry_open; - } - Sys_Printf ("ALSA: could not set mmap access\n"); - goto error; - } - sn.xfer = SNDDMA_ni_xfer; + if (res == 0) { + snd->xfer = alsa_ni_xfer; + return 1; } - Sys_Printf ("Using PCM %s.\n", pcmname); + Sys_MaskPrintf (SYS_snd, "snd_alsa: Failure to set noninterleaved PCM " + "access. (%d) %s\n", res, qfsnd_strerror (res)); + Sys_Printf ("snd_alsa: could not set mmap access\n"); - - switch (bps) { - case -1: - err = qfsnd_pcm_hw_params_set_format (pcm, hw, - SND_PCM_FORMAT_S16_LE); - if (0 <= err) { - bps = 16; - } else if (0 <= (err = qfsnd_pcm_hw_params_set_format (pcm, hw, - SND_PCM_FORMAT_U8))) { - bps = 8; - } else { - Sys_Printf ("ALSA: no useable formats. %s\n", - qfsnd_strerror (err)); - goto error; - } - break; - case 8: - case 16: - err = qfsnd_pcm_hw_params_set_format (pcm, hw, bps == 8 ? - SND_PCM_FORMAT_U8 : - SND_PCM_FORMAT_S16); - if (0 > err) { - Sys_Printf ("ALSA: no usable formats. %s\n", - qfsnd_strerror (err)); - goto error; - } - break; - default: - Sys_Printf ("ALSA: desired format not supported\n"); - goto error; - } - - switch (stereo) { - case -1: - err = qfsnd_pcm_hw_params_set_channels (pcm, hw, 2); - if (0 <= err) { - stereo = 1; - } else if (0 <= (err = qfsnd_pcm_hw_params_set_channels (pcm, hw, - 1))) { - stereo = 0; - } else { - Sys_Printf ("ALSA: no usable channels. %s\n", - qfsnd_strerror (err)); - goto error; - } - break; - case 0: - case 1: - err = qfsnd_pcm_hw_params_set_channels (pcm, hw, stereo ? 2 : 1); - if (0 > err) { - Sys_Printf ("ALSA: no usable channels. %s\n", - qfsnd_strerror (err)); - goto error; - } - break; - default: - Sys_Printf ("ALSA: desired channels not supported\n"); - goto error; - } - - switch (rate) { - case 0: - { - int rates[] = { - 48000, - 44100, - 22050, - 11025, - 0 - }; - int i; - - for (i = 0; rates[i]; i++) { - rate = rates[i]; - Sys_MaskPrintf (SYS_SND, "ALSA: trying %dHz\n", rate); - err = qfsnd_pcm_hw_params_set_rate_near (pcm, hw, - &rate, 0); - if (0 <= err) { - frag_size = 8 * bps * (rate / 11025); - break; - } - } - if (!rates[i]) { - Sys_Printf ("ALSA: no usable rates. %s\n", - qfsnd_strerror (err)); - goto error; - } - } break; - case 11025: - case 22050: - case 44100: - case 48000: - default: - err = qfsnd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0); - if (0 > err) { - Sys_Printf ("ALSA: desired rate %i not supported. %s\n", rate, - qfsnd_strerror (err)); - goto error; - } - frag_size = 8 * bps * (rate / 11025); - break; - } - - err = qfsnd_pcm_hw_params_set_period_size_near (pcm, hw, &frag_size, 0); - if (0 > err) { - Sys_Printf ("ALSA: unable to set period size near %i. %s\n", - (int) frag_size, qfsnd_strerror (err)); - goto error; - } - err = qfsnd_pcm_hw_params (pcm, hw); - if (0 > err) { - Sys_Printf ("ALSA: unable to install hw params: %s\n", - qfsnd_strerror (err)); - goto error; - } - err = qfsnd_pcm_sw_params_current (pcm, sw); - if (0 > err) { - Sys_Printf ("ALSA: unable to determine current sw params. %s\n", - qfsnd_strerror (err)); - goto error; - } - err = qfsnd_pcm_sw_params_set_start_threshold (pcm, sw, ~0U); - if (0 > err) { - Sys_Printf ("ALSA: unable to set playback threshold. %s\n", - qfsnd_strerror (err)); - goto error; - } - err = qfsnd_pcm_sw_params_set_stop_threshold (pcm, sw, ~0U); - if (0 > err) { - Sys_Printf ("ALSA: unable to set playback stop threshold. %s\n", - qfsnd_strerror (err)); - goto error; - } - err = qfsnd_pcm_sw_params (pcm, sw); - if (0 > err) { - Sys_Printf ("ALSA: unable to install sw params. %s\n", - qfsnd_strerror (err)); - goto error; - } - - memset ((dma_t *) &sn, 0, sizeof (sn)); - sn.channels = stereo + 1; - - // don't mix less than this in frames: - err = qfsnd_pcm_hw_params_get_period_size (hw, (snd_pcm_uframes_t *) - (char *) - &sn.submission_chunk, 0); - if (0 > err) { - Sys_Printf ("ALSA: unable to get period size. %s\n", - qfsnd_strerror (err)); - goto error; - } - - sn.framepos = 0; - sn.samplebits = bps; - - err = qfsnd_pcm_hw_params_get_buffer_size (hw, &buffer_size); - if (0 > err) { - Sys_Printf ("ALSA: unable to get buffer size. %s\n", - qfsnd_strerror (err)); - goto error; - } - if (buffer_size != round_buffer_size (buffer_size)) { - Sys_Printf ("ALSA: WARNING: non-power of 2 buffer size. sound may be unsatisfactory\n"); - Sys_Printf ("recommend using either the plughw, or hw devices or adjusting dmix\n"); - Sys_Printf ("to have a power of 2 buffer size\n"); - } - - sn.frames = buffer_size; - sn.speed = rate; - SNDDMA_GetDMAPos (); //XXX sets sn.buffer - Sys_Printf ("%5d channels\n", sn.channels); - Sys_Printf ("%5d samples\n", sn.frames); - Sys_Printf ("%5d samplepos\n", sn.framepos); - Sys_Printf ("%5d samplebits\n", sn.samplebits); - Sys_Printf ("%5d submission_chunk\n", sn.submission_chunk); - Sys_Printf ("%5d speed\n", sn.speed); - Sys_Printf ("0x%lx dma buffer\n", (long) sn.buffer); - - snd_inited = 1; - return &sn; -error: - qfsnd_pcm_close (pcm); return 0; } static int -SNDDMA_GetDMAPos (void) +alsa_playback_set_bps (snd_t *snd, snd_pcm_hw_params_t *hw) { - const snd_pcm_channel_area_t *areas; - snd_pcm_uframes_t offset; - snd_pcm_uframes_t nframes = sn.frames; + int res; + int bps = 0; - qfsnd_pcm_avail_update (pcm); - qfsnd_pcm_mmap_begin (pcm, &areas, &offset, &nframes); - sn.framepos = offset; - sn.buffer = areas->addr; - sn.xfer_data = (void *) areas; - return sn.framepos; + if (snd_bits == 16) { + bps = SND_PCM_FORMAT_S16_LE; + snd->samplebits = 16; + } else if (snd_bits == 8) { + bps = SND_PCM_FORMAT_U8; + snd->samplebits = 8; + } else if (snd_bits) { + Sys_Printf ("snd_alsa: invalid sample bits: %d\n", bps); + return 0; + } + + if (bps) { + if ((res = qfsnd_pcm_hw_params_set_format (pcm, hw, bps)) == 0) { + return 1; + } + } else { + bps = SND_PCM_FORMAT_S16_LE; + if ((res = qfsnd_pcm_hw_params_set_format (pcm, hw, bps)) == 0) { + snd->samplebits = 16; + return 1; + } + bps = SND_PCM_FORMAT_U8; + if ((res = qfsnd_pcm_hw_params_set_format (pcm, hw, bps)) == 0) { + snd->samplebits = 8; + return 1; + } + Sys_Printf ("snd_alsa: no usable formats. %s\n", qfsnd_strerror (res)); + } + snd->samplebits = -1; + Sys_Printf ("snd_alsa: desired format not supported\n"); + return 0; +} + +static int +alsa_playback_set_channels (snd_t *snd, snd_pcm_hw_params_t *hw) +{ + int res; + int channels = 1; + + if (snd_stereo) { + channels = 2; + } + if ((res = qfsnd_pcm_hw_params_set_channels (pcm, hw, channels)) == 0) { + snd->channels = channels; + return 1; + } + + Sys_Printf ("snd_alsa: desired channels not supported\n"); + return 0; +} + +static int +alsa_playback_set_rate (snd_t *snd, snd_pcm_hw_params_t *hw) +{ + int res; + unsigned rate = 0; + static int default_rates[] = { 48000, 44100, 22050, 11025, 0 }; + + if (snd_rate) { + rate = snd_rate; + } + + if (rate) { + if ((res = qfsnd_pcm_hw_params_set_rate (pcm, hw, rate, 0)) == 0) { + snd->speed = rate; + return 1; + } + Sys_Printf ("snd_alsa: desired rate %i not supported. %s\n", rate, + qfsnd_strerror (res)); + } else { + // use default rate + int dir = 0; + for (int *def_rate = default_rates; *def_rate; def_rate++) { + rate = *def_rate; + res = qfsnd_pcm_hw_params_set_rate_near (pcm, hw, &rate, &dir); + if (res == 0) { + snd->speed = rate; + return 1; + } + } + Sys_Printf ("snd_alsa: no usable rate\n"); + } + + return 0; +} + +static int +alsa_playback_set_period_size (snd_t *snd, snd_pcm_hw_params_t *hw) +{ + int res; + snd_pcm_uframes_t period_size; + + // works out to about 5.5ms (5.3 for 48k, 5.8 for 44k1) but consistent for + // different sample rates give or take rounding + period_size = 64 * (snd->speed / 11025); + res = qfsnd_pcm_hw_params_set_period_size_near (pcm, hw, &period_size, 0); + if (res == 0) { + // don't mix less than this in frames: + res = qfsnd_pcm_hw_params_get_period_size (hw, &period_size, 0); + if (res == 0) { + snd->submission_chunk = period_size; + return 1; + } + Sys_Printf ("snd_alsa: unable to get period size. %s\n", + qfsnd_strerror (res)); + } else { + Sys_Printf ("snd_alsa: unable to set period size near %i. %s\n", + (int) period_size, qfsnd_strerror (res)); + } + return 0; +} + +static int +SNDDMA_Init (snd_t *snd) +{ + int res; + const char *device = snd_device; + + snd_pcm_hw_params_t *hw; + snd_pcm_sw_params_t *sw; + + if (!load_libasound ()) + return false; + + snd_pcm_hw_params_alloca (&hw); + snd_pcm_sw_params_alloca (&sw); + + + while (1) { + if (!alsa_open_playback (snd, device)) { + return 0; + } + if ((res = qfsnd_pcm_hw_params_any (pcm, hw)) < 0) { + Sys_Printf ("snd_alsa: error setting hw_params_any. %s\n", + qfsnd_strerror (res)); + goto error; + } + if (alsa_playback_set_mmap (snd, hw)) { + break; + } + if (*device) { + goto error; + } + qfsnd_pcm_close (pcm); + device = "plughw"; + } + + if (!alsa_playback_set_bps (snd, hw)) { + goto error; + } + + if (!alsa_playback_set_channels (snd, hw)) { + goto error; + } + + if (!alsa_playback_set_rate (snd, hw)) { + goto error; + } + + if (!alsa_playback_set_period_size (snd, hw)) { + goto error; + } + + if ((res = qfsnd_pcm_hw_params (pcm, hw)) < 0) { + Sys_Printf ("snd_alsa: unable to install hw params: %s\n", + qfsnd_strerror (res)); + goto error; + } + + if ((res = qfsnd_pcm_sw_params_current (pcm, sw)) < 0) { + Sys_Printf ("snd_alsa: unable to determine current sw params. %s\n", + qfsnd_strerror (res)); + goto error; + } + if ((res = qfsnd_pcm_sw_params_set_start_threshold (pcm, sw, ~0U)) < 0) { + Sys_Printf ("snd_alsa: unable to set playback threshold. %s\n", + qfsnd_strerror (res)); + goto error; + } + if ((res = qfsnd_pcm_sw_params_set_stop_threshold (pcm, sw, ~0U)) < 0) { + Sys_Printf ("snd_alsa: unable to set playback stop threshold. %s\n", + qfsnd_strerror (res)); + goto error; + } + if ((res = qfsnd_pcm_sw_params (pcm, sw)) < 0) { + Sys_Printf ("snd_alsa: unable to install sw params. %s\n", + qfsnd_strerror (res)); + goto error; + } + + snd->framepos = 0; + + if ((res = qfsnd_pcm_hw_params_get_buffer_size (hw, &buffer_size)) < 0) { + Sys_Printf ("snd_alsa: unable to get buffer size. %s\n", + qfsnd_strerror (res)); + goto error; + } + if (buffer_size != round_buffer_size (buffer_size)) { + Sys_Printf ("snd_alsa: WARNING: non-power of 2 buffer size. sound may be unsatisfactory\n"); + Sys_Printf ("recommend using either the plughw, or hw devices or adjusting dmix\n"); + Sys_Printf ("to have a power of 2 buffer size\n"); + } + + if ((res = qfsnd_async_add_pcm_handler (&async_handler, pcm, + alsa_callback, snd)) < 0) { + Sys_Printf ("snd_alsa: unable to register async handler: %s", + qfsnd_strerror (res)); + goto error; + } + + snd->frames = buffer_size; + + snd->threaded = 1;//XXX FIXME double check whether it's always true + + // send the first period to fill the buffer + // also sets snd->buffer + if (alsa_process (pcm, snd) < 0) { + goto error; + } + + qfsnd_pcm_start (pcm); + + Sys_Printf ("%5d channels %sinterleaved\n", snd->channels, + snd->xfer ? "non-" : ""); + Sys_Printf ("%5d samples (%.1fms)\n", snd->frames, + 1000.0 * snd->frames / snd->speed); + Sys_Printf ("%5d samplepos\n", snd->framepos); + Sys_Printf ("%5d samplebits\n", snd->samplebits); + Sys_Printf ("%5d submission_chunk (%.1fms)\n", snd->submission_chunk, + 1000.0 * snd->submission_chunk / snd->speed); + Sys_Printf ("%5d speed\n", snd->speed); + Sys_Printf ("0x%lx dma buffer\n", (long) snd->buffer); + + snd_inited = 1; + + return 1; +error: + qfsnd_pcm_close (pcm); + snd->channels = 0; + snd->frames = 0; + snd->samplebits = 0; + snd->submission_chunk = 0; + snd->speed = 0; + return 0; } static void -SNDDMA_Shutdown (void) +SNDDMA_shutdown (snd_t *snd) { if (snd_inited) { + qfsnd_async_del_handler (async_handler); + async_handler = 0; qfsnd_pcm_close (pcm); snd_inited = 0; } } -/* - SNDDMA_Submit - - Send sound to device if buffer isn't really the dma buffer -*/ static void -SNDDMA_Submit (void) -{ - int state; - int count = (*plugin_info_snd_output_data.paintedtime - - *plugin_info_snd_output_data.soundtime); - const snd_pcm_channel_area_t *areas; - snd_pcm_uframes_t nframes; - snd_pcm_uframes_t offset; - - if (snd_blocked) - return; - - nframes = count; - - qfsnd_pcm_avail_update (pcm); - qfsnd_pcm_mmap_begin (pcm, &areas, &offset, &nframes); - - state = qfsnd_pcm_state (pcm); - - switch (state) { - case SND_PCM_STATE_PREPARED: - qfsnd_pcm_mmap_commit (pcm, offset, nframes); - qfsnd_pcm_start (pcm); - break; - case SND_PCM_STATE_RUNNING: - qfsnd_pcm_mmap_commit (pcm, offset, nframes); - break; - default: - break; - } -} - -static void -SNDDMA_BlockSound (void) +SNDDMA_BlockSound (snd_t *snd) { if (snd_inited && ++snd_blocked == 1) qfsnd_pcm_pause (pcm, 1); } static void -SNDDMA_UnblockSound (void) +SNDDMA_UnblockSound (snd_t *snd) { if (!snd_inited || !snd_blocked) return; @@ -525,35 +719,49 @@ SNDDMA_UnblockSound (void) qfsnd_pcm_pause (pcm, 0); } -PLUGIN_INFO(snd_output, alsa) -{ - plugin_info.type = qfp_snd_output; - plugin_info.api_version = QFPLUGIN_VERSION; - plugin_info.plugin_version = "0.1"; - plugin_info.description = "ALSA digital output"; - plugin_info.copyright = "Copyright (C) 1996-1997 id Software, Inc.\n" +static general_data_t plugin_info_general_data = { +}; + +static general_funcs_t plugin_info_general_funcs = { + .init = SNDDMA_Init_Cvars, + .shutdown = NULL, +}; + +static snd_output_data_t plugin_info_snd_output_data = { + .model = som_pull, +}; + +static snd_output_funcs_t plugin_info_snd_output_funcs = { + .init = SNDDMA_Init, + .shutdown = SNDDMA_shutdown, + .block_sound = SNDDMA_BlockSound, + .unblock_sound = SNDDMA_UnblockSound, +}; + +static plugin_data_t plugin_info_data = { + .general = &plugin_info_general_data, + .snd_output = &plugin_info_snd_output_data, +}; + +static plugin_funcs_t plugin_info_funcs = { + .general = &plugin_info_general_funcs, + .snd_output = &plugin_info_snd_output_funcs, +}; + +static plugin_t plugin_info = { + .type = qfp_snd_output, + .api_version = QFPLUGIN_VERSION, + .plugin_version = "0.1", + .description = "ALSA digital output", + .copyright = "Copyright (C) 1996-1997 id Software, Inc.\n" "Copyright (C) 1999,2000,2001 contributors of the QuakeForge " "project\n" - "Please see the file \"AUTHORS\" for a list of contributors"; - plugin_info.functions = &plugin_info_funcs; - plugin_info.data = &plugin_info_data; - - plugin_info_data.general = &plugin_info_general_data; - plugin_info_data.input = NULL; - plugin_info_data.snd_output = &plugin_info_snd_output_data; - - plugin_info_funcs.general = &plugin_info_general_funcs; - plugin_info_funcs.input = NULL; - plugin_info_funcs.snd_output = &plugin_info_snd_output_funcs; - - plugin_info_general_funcs.p_Init = SNDDMA_Init_Cvars; - plugin_info_general_funcs.p_Shutdown = NULL; - plugin_info_snd_output_funcs.pS_O_Init = SNDDMA_Init; - plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_Shutdown; - plugin_info_snd_output_funcs.pS_O_GetDMAPos = SNDDMA_GetDMAPos; - plugin_info_snd_output_funcs.pS_O_Submit = SNDDMA_Submit; - plugin_info_snd_output_funcs.pS_O_BlockSound = SNDDMA_BlockSound; - plugin_info_snd_output_funcs.pS_O_UnblockSound = SNDDMA_UnblockSound; + "Please see the file \"AUTHORS\" for a list of contributors", + .functions = &plugin_info_funcs, + .data = &plugin_info_data, +}; +PLUGIN_INFO(snd_output, alsa) +{ return &plugin_info; } diff --git a/libs/audio/targets/snd_disk.c b/libs/audio/targets/snd_disk.c index e24bc0e9f..878ca90ca 100644 --- a/libs/audio/targets/snd_disk.c +++ b/libs/audio/targets/snd_disk.c @@ -108,7 +108,7 @@ SNDDMA_GetDMAPos (void) } static void -SNDDMA_Shutdown (void) +SNDDMA_shutdown (void) { if (snd_inited) { Qclose (snd_file); @@ -174,7 +174,7 @@ PLUGIN_INFO(snd_output, disk) plugin_info_general_funcs.p_Init = SNDDMA_Init_Cvars; plugin_info_general_funcs.p_Shutdown = NULL; plugin_info_snd_output_funcs.pS_O_Init = SNDDMA_Init; - plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_Shutdown; + plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_shutdown; plugin_info_snd_output_funcs.pS_O_GetDMAPos = SNDDMA_GetDMAPos; plugin_info_snd_output_funcs.pS_O_Submit = SNDDMA_Submit; plugin_info_snd_output_funcs.pS_O_BlockSound = SNDDMA_BlockSound; diff --git a/libs/audio/targets/snd_dx.c b/libs/audio/targets/snd_dx.c index 1bae2c3d0..291232720 100644 --- a/libs/audio/targets/snd_dx.c +++ b/libs/audio/targets/snd_dx.c @@ -30,12 +30,12 @@ #define CINTERFACE -#include "winquake.h" #include "QF/cvar.h" #include "QF/qargs.h" #include "QF/sys.h" #include "snd_internal.h" +#include "context_win.h" #define iDirectSoundCreate(a,b,c) pDirectSoundCreate(a,b,c) @@ -51,12 +51,11 @@ HRESULT (WINAPI * pDirectSoundCreate) (GUID FAR * lpGUID, typedef enum { SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL } sndinitstat; -static qboolean dsound_init; -static qboolean snd_firsttime = true; -static qboolean primary_format_set; +static bool dsound_init; +static bool snd_firsttime = true; +static bool primary_format_set; static int sample16; -static volatile dma_t sn; /* Global variables. Must be visible to window-procedure function @@ -82,39 +81,53 @@ static LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf; static HINSTANCE hInstDS; -static sndinitstat SNDDMA_InitDirect (void); +static sndinitstat SNDDMA_InitDirect (snd_t *snd); -static cvar_t *snd_stereo; -static cvar_t *snd_rate; -static cvar_t *snd_bits; - -static plugin_t plugin_info; -static plugin_data_t plugin_info_data; -static plugin_funcs_t plugin_info_funcs; -static general_data_t plugin_info_general_data; -static general_funcs_t plugin_info_general_funcs; -static snd_output_data_t plugin_info_snd_output_data; -static snd_output_funcs_t plugin_info_snd_output_funcs; +static int snd_stereo; +static cvar_t snd_stereo_cvar = { + .name = "snd_stereo", + .description = + "sound stereo output", + .default_value = "1", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &snd_stereo }, +}; +static int snd_rate; +static cvar_t snd_rate_cvar = { + .name = "snd_rate", + .description = + "sound playback rate. 0 is system default", + .default_value = "0", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &snd_rate }, +}; +static int snd_bits; +static cvar_t snd_bits_cvar = { + .name = "snd_bits", + .description = + "sound sample depth. 0 is system default", + .default_value = "0", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &snd_bits }, +}; +static DWORD *DSOUND_LockBuffer (snd_t *snd, bool lockit); static void SNDDMA_Init_Cvars (void) { - snd_stereo = Cvar_Get ("snd_stereo", "1", CVAR_ROM, NULL, - "sound stereo output"); - snd_rate = Cvar_Get ("snd_rate", "11025", CVAR_ROM, NULL, - "sound playback rate. 0 is system default"); - snd_bits = Cvar_Get ("snd_bits", "16", CVAR_ROM, NULL, - "sound sample depth. 0 is system default"); + Cvar_Register (&snd_stereo_cvar, 0, 0); + Cvar_Register (&snd_rate_cvar, 0, 0); + Cvar_Register (&snd_bits_cvar, 0, 0); } static void -SNDDMA_BlockSound (void) +SNDDMA_BlockSound (snd_t *snd) { } static void -SNDDMA_UnblockSound (void) +SNDDMA_UnblockSound (snd_t *snd) { } @@ -131,7 +144,7 @@ FreeSound (void) } if (pDS) { - IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL); + IDirectSound_SetCooperativeLevel (pDS, win_mainwindow, DSSCL_NORMAL); IDirectSound_Release (pDS); } pDS = NULL; @@ -150,7 +163,7 @@ FreeSound (void) Direct-Sound support */ static sndinitstat -SNDDMA_InitDirect (void) +SNDDMA_InitDirect (snd_t *snd) { int reps; DSBUFFERDESC dsbuf; @@ -160,22 +173,20 @@ SNDDMA_InitDirect (void) HRESULT hresult; WAVEFORMATEX format, pformat; - memset ((void *) &sn, 0, sizeof (sn)); - - if (!snd_stereo->int_val) { - sn.channels = 1; + if (!snd_stereo) { + snd->channels = 1; } else { - sn.channels = 2; + snd->channels = 2; } - sn.samplebits = snd_bits->int_val; - sn.speed = snd_rate->int_val; + snd->samplebits = snd_bits; + snd->speed = snd_rate; memset (&format, 0, sizeof (format)); format.wFormatTag = WAVE_FORMAT_PCM; - format.nChannels = sn.channels; - format.wBitsPerSample = sn.samplebits; - format.nSamplesPerSec = sn.speed; + format.nChannels = snd->channels; + format.wBitsPerSample = snd->samplebits; + format.nSamplesPerSec = snd->speed; format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; format.cbSize = 0; format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; @@ -219,7 +230,8 @@ SNDDMA_InitDirect (void) } if (DS_OK != - IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE)) { + IDirectSound_SetCooperativeLevel (pDS, win_mainwindow, + DSSCL_EXCLUSIVE)) { Sys_Printf ("Set coop level failed\n"); FreeSound (); return SIS_FAILURE; @@ -265,9 +277,9 @@ SNDDMA_InitDirect (void) return SIS_FAILURE; } - sn.channels = format.nChannels; - sn.samplebits = format.wBitsPerSample; - sn.speed = format.nSamplesPerSec; + snd->channels = format.nChannels; + snd->samplebits = format.wBitsPerSample; + snd->speed = format.nSamplesPerSec; if (DS_OK != IDirectSound_GetCaps (pDSBuf, &dsbcaps)) { Sys_Printf ("DS:GetCaps failed\n"); @@ -276,7 +288,7 @@ SNDDMA_InitDirect (void) } } else { if (DS_OK != - IDirectSound_SetCooperativeLevel (pDS, mainwindow, + IDirectSound_SetCooperativeLevel (pDS, win_mainwindow, DSSCL_WRITEPRIMARY)) { Sys_Printf ("Set coop level failed\n"); FreeSound (); @@ -329,11 +341,11 @@ SNDDMA_InitDirect (void) &dwWrite); IDirectSoundBuffer_Play (pDSBuf, 0, 0, DSBPLAY_LOOPING); - sn.frames = gSndBufSize / (sn.samplebits / 8) / sn.channels; - sn.framepos = 0; - sn.submission_chunk = 1; - sn.buffer = (byte *) lpData; - sample16 = (sn.samplebits / 8) - 1; + snd->frames = gSndBufSize / (snd->samplebits / 8) / snd->channels; + snd->framepos = 0; + snd->submission_chunk = 1; + snd->buffer = (byte *) lpData; + sample16 = (snd->samplebits / 8) - 1; dsound_init = true; @@ -347,10 +359,11 @@ SNDDMA_InitDirect (void) Try to find a sound device to mix for. Returns false if nothing is found. */ -static volatile dma_t * -SNDDMA_Init (void) +static int +SNDDMA_Init (snd_t *snd) { sndinitstat stat; + int ret = 0; stat = SIS_FAILURE; // assume DirectSound won't // initialize @@ -358,17 +371,16 @@ SNDDMA_Init (void) /* Init DirectSound */ if (snd_firsttime) { snd_firsttime = false; - stat = SNDDMA_InitDirect (); + stat = SNDDMA_InitDirect (snd); - if (stat == SIS_SUCCESS) { + if ((ret = (stat == SIS_SUCCESS))) { Sys_Printf ("DirectSound initialized\n"); } else { Sys_Printf ("DirectSound failed to init\n"); - return 0; } } - return &sn; + return ret; } /* @@ -379,31 +391,31 @@ SNDDMA_Init (void) how many sample are required to fill it up. */ static int -SNDDMA_GetDMAPos (void) +SNDDMA_GetDMAPos (snd_t *snd) { int s = 0; DWORD dwWrite; MMTIME mmtime; unsigned long *pbuf; - pbuf = DSOUND_LockBuffer (true); + pbuf = DSOUND_LockBuffer (snd, true); if (!pbuf) { Sys_Printf ("DSOUND_LockBuffer fails!\n"); return -1; } - sn.buffer = (unsigned char *) pbuf; + snd->buffer = (unsigned char *) pbuf; mmtime.wType = TIME_SAMPLES; IDirectSoundBuffer_GetCurrentPosition (pDSBuf, &mmtime.u.sample, &dwWrite); s = mmtime.u.sample - mmstarttime.u.sample; s >>= sample16; - s /= sn.channels; + s /= snd->channels; - s %= sn.frames; - sn.framepos = s; + s %= snd->frames; + snd->framepos = s; - return sn.framepos; + return snd->framepos; } /* @@ -412,24 +424,19 @@ SNDDMA_GetDMAPos (void) Send sound to device if buffer isn't really the dma buffer */ static void -SNDDMA_Submit (void) +SNDDMA_Submit (snd_t *snd) { - DSOUND_LockBuffer (false); + DSOUND_LockBuffer (snd, false); } -/* - SNDDMA_Shutdown - - Reset the sound device for exiting -*/ static void -SNDDMA_Shutdown (void) +SNDDMA_shutdown (snd_t *snd) { FreeSound (); } -DWORD * -DSOUND_LockBuffer (qboolean lockit) +static DWORD * +DSOUND_LockBuffer (snd_t *snd, bool lockit) { int reps; @@ -450,16 +457,16 @@ DSOUND_LockBuffer (qboolean lockit) if (hresult != DSERR_BUFFERLOST) { Sys_Printf ("S_TransferStereo16: DS::Lock Sound Buffer Failed\n"); - SNDDMA_Shutdown (); - SNDDMA_Init (); + SNDDMA_shutdown (snd); + SNDDMA_Init (snd); return NULL; } if (++reps > 10000) { Sys_Printf ("S_TransferStereo16: DS: couldn't restore buffer\n"); - SNDDMA_Shutdown (); - SNDDMA_Init (); + SNDDMA_shutdown (snd); + SNDDMA_Init (snd); return NULL; } } @@ -473,18 +480,18 @@ DSOUND_LockBuffer (qboolean lockit) return (pbuf1); } -void -DSOUND_ClearBuffer (int clear) +static void __attribute__((used)) //FIXME make it true +DSOUND_ClearBuffer (snd_t *snd, int clear) { DWORD *pData; // FIXME: this should be called with 2nd pbuf2 = NULL, dwsize =0 - pData = DSOUND_LockBuffer (true); - memset (pData, clear, sn.frames * sn.channels * sn.samplebits / 8); - DSOUND_LockBuffer (false); + pData = DSOUND_LockBuffer (snd, true); + memset (pData, clear, snd->frames * snd->channels * snd->samplebits / 8); + DSOUND_LockBuffer (snd, false); } -void +static void __attribute__((used)) //FIXME make it true DSOUND_Restore (void) { // if the buffer was lost or stopped, restore it and/or restart it @@ -505,35 +512,49 @@ DSOUND_Restore (void) return; } +static snd_output_data_t plugin_info_snd_output_data = { +}; + +static snd_output_funcs_t plugin_info_snd_output_funcs = { + .init = SNDDMA_Init, + .shutdown = SNDDMA_shutdown, + .get_dma_pos = SNDDMA_GetDMAPos, + .submit = SNDDMA_Submit, + .block_sound = SNDDMA_BlockSound, + .unblock_sound = SNDDMA_UnblockSound, +}; + +static general_data_t plugin_info_general_data = { +}; + +static general_funcs_t plugin_info_general_funcs = { + .init = SNDDMA_Init_Cvars, +}; + +static plugin_data_t plugin_info_data = { + .general = &plugin_info_general_data, + .snd_output = &plugin_info_snd_output_data, +}; + +static plugin_funcs_t plugin_info_funcs = { + .general = &plugin_info_general_funcs, + .snd_output = &plugin_info_snd_output_funcs, +}; + +static plugin_t plugin_info = { + .type = qfp_snd_output, + .api_version = QFPLUGIN_VERSION, + .plugin_version = "0.1", + .description = "Windows DirectX output", + .copyright = "Copyright (C) 1996-1997 id Software, Inc.\n" + "Copyright (C) 1999,2000,2001,2002,2003 contributors of the " + "QuakeForge project\n" + "Please see the file \"AUTHORS\" for a list of contributors", + .functions = &plugin_info_funcs, + .data = &plugin_info_data, +}; + PLUGIN_INFO(snd_output, dx) { - plugin_info.type = qfp_snd_output; - plugin_info.api_version = QFPLUGIN_VERSION; - plugin_info.plugin_version = "0.1"; - plugin_info.description = "Windows DirectX output"; - plugin_info.copyright = "Copyright (C) 1996-1997 id Software, Inc.\n" - "Copyright (C) 1999,2000,2001,2002,2003 contributors of the QuakeForge " - "project\n" - "Please see the file \"AUTHORS\" for a list of contributors"; - plugin_info.functions = &plugin_info_funcs; - plugin_info.data = &plugin_info_data; - - plugin_info_data.general = &plugin_info_general_data; - plugin_info_data.input = NULL; - plugin_info_data.snd_output = &plugin_info_snd_output_data; - - plugin_info_funcs.general = &plugin_info_general_funcs; - plugin_info_funcs.input = NULL; - plugin_info_funcs.snd_output = &plugin_info_snd_output_funcs; - - plugin_info_general_funcs.p_Init = SNDDMA_Init_Cvars; - plugin_info_general_funcs.p_Shutdown = NULL; - plugin_info_snd_output_funcs.pS_O_Init = SNDDMA_Init; - plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_Shutdown; - plugin_info_snd_output_funcs.pS_O_GetDMAPos = SNDDMA_GetDMAPos; - plugin_info_snd_output_funcs.pS_O_Submit = SNDDMA_Submit; - plugin_info_snd_output_funcs.pS_O_BlockSound = SNDDMA_BlockSound; - plugin_info_snd_output_funcs.pS_O_UnblockSound = SNDDMA_UnblockSound; - return &plugin_info; } diff --git a/libs/audio/renderer/snd_jack.c b/libs/audio/targets/snd_jack.c similarity index 50% rename from libs/audio/renderer/snd_jack.c rename to libs/audio/targets/snd_jack.c index 36c5d0e6a..836792c98 100644 --- a/libs/audio/renderer/snd_jack.c +++ b/libs/audio/targets/snd_jack.c @@ -1,9 +1,9 @@ /* snd_jack.c - JACK version of the renderer + JACK output driver - Copyright (C) 2007 Bill Currie + Copyright (C) 2007-2021 Bill Currie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -54,46 +54,31 @@ static int snd_alive = 0; static double snd_alive_time = 0; static jack_client_t *jack_handle; static jack_port_t *jack_out[2]; -static dma_t _snd_shm; static float *output[2]; -static cvar_t *snd_jack_server; +static char *snd_jack_server; +static cvar_t snd_jack_server_cvar = { + .name = "snd_jack_server", + .description = + "The name of the JACK server to connect to", + .default_value = "default", + .flags = CVAR_ROM, + .value = { .type = 0, .value = &snd_jack_server }, +}; +static char *snd_jack_ports; +static cvar_t snd_jack_ports_cvar = { + .name = "snd_jack_ports", + .description = + "; separated list of port names to which QF will connect. Defaults to " + "the first two physical ports. Currently only two ports are supported.", + .default_value = "", + .flags = CVAR_ROM, + .value = { .type = 0, .value = &snd_jack_ports }, +}; -static void s_jack_connect (void); +static int s_jack_connect (snd_t *snd); static void -s_stop_all_sounds (void) -{ - if (!sound_started) - return; - SND_StopAllSounds (); - SND_ScanChannels (1); -} - -static void -s_start_sound (int entnum, int entchannel, sfx_t *sfx, const vec3_t origin, - float fvol, float attenuation) -{ - if (!sound_started) - return; - if (!snd_shutdown) - SND_StartSound (entnum, entchannel, sfx, origin, fvol, attenuation); -} - -static void -s_finish_channels (void) -{ - int i; - channel_t *ch; - - for (i = 0; i < MAX_CHANNELS; i++) { - ch = &snd_channels[i]; - ch->done = ch->stop = 1; - } -} - -static void -s_update (const vec3_t origin, const vec3_t forward, const vec3_t right, - const vec3_t up, const byte *ambient_sound_level) +s_update (snd_t *snd) { double now = Sys_DoubleTime (); @@ -106,7 +91,7 @@ s_update (const vec3_t origin, const vec3_t forward, const vec3_t right, if (!snd_shutdown) { if (now - snd_alive_time > 1.0) { Sys_Printf ("jackd client thread seems to have died\n"); - s_finish_channels (); + snd->finish_channels (); snd_shutdown = 1; } } @@ -115,26 +100,40 @@ s_update (const vec3_t origin, const vec3_t forward, const vec3_t right, if (snd_shutdown == 1) { snd_shutdown++; Sys_Printf ("Lost connection to jackd\n"); - s_jack_connect (); + s_jack_connect (snd); } if (snd_shutdown) return; } - SND_SetListener (origin, forward, right, up, ambient_sound_level); } -static void -s_extra_update (void) +static const char ** +s_jack_parse_ports (const char *ports_str) { -} + int num_ports = 1; + int ind = 0; + char **ports; -static void -s_local_sound (const char *sound) -{ - if (!sound_started) - return; - if (!snd_shutdown) - SND_LocalSound (sound); + for (const char *s = ports_str; *s; s++) { + num_ports += *s == ';'; + } + + size_t len = strlen (ports_str) + 1; + ports = malloc ((num_ports + 1) * sizeof (*ports) + len + 1); + ports[num_ports] = 0; + ports[0] = (char *) &ports[num_ports + 1]; + ports[0][len] = 0; + strcpy (ports[0], ports_str); + + for (char *s = ports[0]; *s; s++) { + if (s > ports[0]) { + s[-1] = 0; + } + for (ports[ind++] = s; *s && *s != ';'; s++) { + } + } + + return (const char **) ports; } static void @@ -143,24 +142,30 @@ s_jack_activate (void) const char **ports; int i; + snd_shutdown = 0; + if (!*snd_jack_ports) { + ports = jack_get_ports (jack_handle, 0, 0, + JackPortIsPhysical | JackPortIsInput); + } else { + ports = s_jack_parse_ports (snd_jack_ports); + } + if (developer & SYS_snd) { + for (i = 0; ports[i]; i++) { + Sys_Printf ("%s\n", ports[i]); + } + } if (jack_activate (jack_handle)) { Sys_Printf ("Could not activate JACK\n"); return; } - snd_shutdown = 0; - ports = jack_get_ports (jack_handle, 0, 0, - JackPortIsPhysical | JackPortIsInput); - if (developer->int_val & SYS_SND) { - for (i = 0; ports[i]; i++) - Sys_Printf ("%s\n", ports[i]); - } - for (i = 0; i < 2; i++) + for (i = 0; i < 2; i++) { jack_connect (jack_handle, jack_port_name (jack_out[i]), ports[i]); + } free (ports); } static void -s_block_sound (void) +s_block_sound (snd_t *t) { if (!sound_started) return; @@ -170,7 +175,7 @@ s_block_sound (void) } static void -s_unblock_sound (void) +s_unblock_sound (snd_t *t) { if (!sound_started) return; @@ -183,84 +188,9 @@ s_unblock_sound (void) } } -static channel_t * -s_alloc_channel (void) -{ - if (!sound_started) - return 0; - if (!snd_shutdown) - return SND_AllocChannel (); - return 0; -} - static void -s_snd_force_unblock (void) -{ - if (!sound_started) - return; - snd_blocked = 1; - s_unblock_sound (); -} - -static void -s_ambient_off (void) -{ - if (!sound_started) - return; - SND_AmbientOff (); -} - -static void -s_ambient_on (void) -{ - if (!sound_started) - return; - SND_AmbientOn (); -} - -static void -s_static_sound (sfx_t *sfx, const vec3_t origin, float vol, - float attenuation) -{ - if (!sound_started) - return; - SND_StaticSound (sfx, origin, vol, attenuation); -} - -static void -s_stop_sound (int entnum, int entchannel) -{ - if (!sound_started) - return; - SND_StopSound (entnum, entchannel); -} - -static sfx_t * -s_precache_sound (const char *name) -{ - if (!sound_started) - return 0; - return SND_PrecacheSound (name); -} - -static sfx_t * -s_load_sound (const char *name) -{ - if (!sound_started) - return 0; - return SND_LoadSound (name); -} - -static void -s_channel_stop (channel_t *chan) -{ - if (!sound_started) - return; - SND_ChannelStop (chan); -} - -static void -snd_jack_xfer (portable_samplepair_t *paintbuffer, int count, float volume) +snd_jack_xfer (snd_t *snd, portable_samplepair_t *paintbuffer, int count, + float volume) { int i; @@ -273,28 +203,31 @@ snd_jack_xfer (portable_samplepair_t *paintbuffer, int count, float volume) } for (i = 0; i < count; i++) { /* max is +/- 1.0. need to implement clamping. */ - *output[0]++ = volume * snd_paintbuffer[i].left; - *output[1]++ = volume * snd_paintbuffer[i].right; + *output[0]++ = volume * paintbuffer[i].left; + *output[1]++ = volume * paintbuffer[i].right; } } static int snd_jack_process (jack_nframes_t nframes, void *arg) { + snd_t *snd = arg; int i; snd_alive = 1; for (i = 0; i < 2; i++) output[i] = (float *) jack_port_get_buffer (jack_out[i], nframes); - SND_PaintChannels (snd_paintedtime + nframes); + snd->paint_channels (snd, snd->paintedtime + nframes); return 0; } static void snd_jack_shutdown (void *arg) { + snd_t *snd = arg; + snd_shutdown = 1; - s_finish_channels (); + snd->finish_channels (); } static void @@ -306,58 +239,51 @@ snd_jack_error (const char *desc) static int snd_jack_xrun (void *arg) { - fprintf (stderr, "snd_jack: xrun\n"); + if (developer & SYS_snd) { + fprintf (stderr, "snd_jack: xrun\n"); + } return 0; } -static void -s_jack_connect (void) +static int +s_jack_connect (snd_t *snd) { int i; jack_set_error_function (snd_jack_error); if ((jack_handle = jack_client_open ("QuakeForge", JackServerName | JackNoStartServer, 0, - snd_jack_server->string)) == 0) { + snd_jack_server)) == 0) { Sys_Printf ("Could not connect to JACK\n"); - return; + return 0; } if (jack_set_xrun_callback (jack_handle, snd_jack_xrun, 0)) Sys_Printf ("Could not set xrun callback\n"); - jack_set_process_callback (jack_handle, snd_jack_process, 0); - jack_on_shutdown (jack_handle, snd_jack_shutdown, 0); + jack_set_process_callback (jack_handle, snd_jack_process, snd); + jack_on_shutdown (jack_handle, snd_jack_shutdown, snd); for (i = 0; i < 2; i++) - jack_out[i] = jack_port_register (jack_handle, va ("out_%d", i + 1), + jack_out[i] = jack_port_register (jack_handle, va (0, "out_%d", i + 1), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); - snd_shm->speed = jack_get_sample_rate (jack_handle); + snd->speed = jack_get_sample_rate (jack_handle); + snd->channels = 2; s_jack_activate (); sound_started = 1; - Sys_Printf ("Connected to JACK: %d Sps\n", snd_shm->speed); + snd_alive_time = Sys_DoubleTime (); + Sys_Printf ("Connected to JACK: %d Sps\n", snd->speed); + return 1; } -static void -s_init (void) +static int +s_init (snd_t *snd) { - snd_shm = &_snd_shm; - snd_shm->xfer = snd_jack_xfer; - - Cmd_AddCommand ("snd_force_unblock", s_snd_force_unblock, - "fix permanently blocked sound"); - - snd_volume = Cvar_Get ("volume", "0.7", CVAR_ARCHIVE, NULL, - "Set the volume for sound playback"); - snd_jack_server = Cvar_Get ("snd_jack_server", "default", CVAR_ROM, NULL, - "The name of the JACK server to connect to"); - - SND_SFX_Init (); - SND_Channels_Init (); - - s_jack_connect (); + snd->xfer = snd_jack_xfer; + snd->threaded = 1; + return s_jack_connect (snd); } static void -s_shutdown (void) +s_shutdown (snd_t *snd) { int i; if (jack_handle) { @@ -370,63 +296,55 @@ s_shutdown (void) } } +static void +s_init_cvars (void) +{ + Cvar_Register (&snd_jack_server_cvar, 0, 0); + Cvar_Register (&snd_jack_ports_cvar, 0, 0); +} + static general_funcs_t plugin_info_general_funcs = { - s_init, - s_shutdown, + .init = s_init_cvars, }; -static snd_render_funcs_t plugin_info_render_funcs = { - s_ambient_off, - s_ambient_on, - s_static_sound, - s_start_sound, - s_stop_sound, - s_precache_sound, - s_update, - s_stop_all_sounds, - s_extra_update, - s_local_sound, - s_block_sound, - s_unblock_sound, - s_load_sound, - s_alloc_channel, - s_channel_stop, +static snd_output_funcs_t plugin_info_output_funcs = { + .init = s_init, + .shutdown = s_shutdown, + .on_update = s_update, + .block_sound = s_block_sound, + .unblock_sound = s_unblock_sound, +}; + +static snd_output_data_t plugin_info_output_data = { + .model = som_pull, }; static general_data_t plugin_info_general_data; static plugin_funcs_t plugin_info_funcs = { - &plugin_info_general_funcs, - 0, - 0, - 0, - 0, - &plugin_info_render_funcs, + .general = &plugin_info_general_funcs, + .snd_output = &plugin_info_output_funcs, }; static plugin_data_t plugin_info_data = { - &plugin_info_general_data, - 0, - 0, - 0, - 0, - &snd_render_data, + .general = &plugin_info_general_data, + .snd_output = &plugin_info_output_data, }; static plugin_t plugin_info = { - qfp_snd_render, + qfp_snd_output, 0, QFPLUGIN_VERSION, "0.1", "Sound Renderer", - "Copyright (C) 2007 contributors of the QuakeForge " + "Copyright (C) 2007-2021 contributors of the QuakeForge " "project\n" "Please see the file \"AUTHORS\" for a list of contributors", &plugin_info_funcs, &plugin_info_data, }; -PLUGIN_INFO(snd_render, jack) +PLUGIN_INFO(snd_output, jack) { return &plugin_info; } diff --git a/libs/audio/targets/snd_oss.c b/libs/audio/targets/snd_oss.c index 33349dc98..e938e3965 100644 --- a/libs/audio/targets/snd_oss.c +++ b/libs/audio/targets/snd_oss.c @@ -78,15 +78,54 @@ static int audio_fd; static int snd_inited; static int mmaped_io = 0; static const char *snd_dev = "/dev/dsp"; -static volatile dma_t sn; static int tryrates[] = { 44100, 48000, 11025, 22050, 22051, 44100, 8000 }; -static cvar_t *snd_stereo; -static cvar_t *snd_rate; -static cvar_t *snd_device; -static cvar_t *snd_bits; -static cvar_t *snd_oss_mmaped; +static int snd_stereo; +static cvar_t snd_stereo_cvar = { + .name = "snd_stereo", + .description = + "sound stereo output", + .default_value = "1", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &snd_stereo }, +}; +static int snd_rate; +static cvar_t snd_rate_cvar = { + .name = "snd_rate", + .description = + "sound playback rate. 0 is system default", + .default_value = "0", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &snd_rate }, +}; +static char *snd_device; +static cvar_t snd_device_cvar = { + .name = "snd_device", + .description = + "sound device. \"\" is system default", + .default_value = "", + .flags = CVAR_ROM, + .value = { .type = 0, .value = &snd_device }, +}; +static int snd_bits; +static cvar_t snd_bits_cvar = { + .name = "snd_bits", + .description = + "sound sample depth. 0 is system default", + .default_value = "0", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &snd_bits }, +}; +static int snd_oss_mmaped; +static cvar_t snd_oss_mmaped_cvar = { + .name = "snd_oss_mmaped", + .description = + "mmaped io", + .default_value = "1", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &snd_oss_mmaped }, +}; static plugin_t plugin_info; static plugin_data_t plugin_info_data; @@ -100,20 +139,15 @@ static snd_output_funcs_t plugin_info_snd_output_funcs; static void SNDDMA_Init_Cvars (void) { - snd_stereo = Cvar_Get ("snd_stereo", "1", CVAR_ROM, NULL, - "sound stereo output"); - snd_rate = Cvar_Get ("snd_rate", "0", CVAR_ROM, NULL, - "sound playback rate. 0 is system default"); - snd_device = Cvar_Get ("snd_device", "", CVAR_ROM, NULL, - "sound device. \"\" is system default"); - snd_bits = Cvar_Get ("snd_bits", "0", CVAR_ROM, NULL, - "sound sample depth. 0 is system default"); - snd_oss_mmaped = Cvar_Get ("snd_oss_mmaped", "1", CVAR_ROM, NULL, - "mmaped io"); + Cvar_Register (&snd_stereo_cvar, 0, 0); + Cvar_Register (&snd_rate_cvar, 0, 0); + Cvar_Register (&snd_device_cvar, 0, 0); + Cvar_Register (&snd_bits_cvar, 0, 0); + Cvar_Register (&snd_oss_mmaped_cvar, 0, 0); } -static volatile dma_t * -try_open (int rw) +static int +try_open (snd_t *snd, int rw) { int caps, fmt, rc, tmp, i; int retries = 3; @@ -123,11 +157,11 @@ try_open (int rw) struct audio_buf_info info; snd_inited = 0; - mmaped_io = snd_oss_mmaped->int_val; + mmaped_io = snd_oss_mmaped; // open snd_dev, confirm capability to mmap, and get size of dma buffer - if (snd_device->string[0]) - snd_dev = snd_device->string; + if (snd_device[0]) + snd_dev = snd_device; if (rw) { omode = O_RDWR; @@ -169,21 +203,21 @@ try_open (int rw) } // set sample bits & speed - sn.samplebits = snd_bits->int_val; + snd->samplebits = snd_bits; - if (sn.samplebits != 16 && sn.samplebits != 8) { + if (snd->samplebits != 16 && snd->samplebits != 8) { ioctl (audio_fd, SNDCTL_DSP_GETFMTS, &fmt); if (fmt & AFMT_S16_LE) { // little-endian 16-bit signed - sn.samplebits = 16; + snd->samplebits = 16; } else { if (fmt & AFMT_U8) { // unsigned 8-bit ulaw - sn.samplebits = 8; + snd->samplebits = 8; } } } - if (sn.samplebits == 16) { + if (snd->samplebits == 16) { rc = AFMT_S16_LE; rc = ioctl (audio_fd, SNDCTL_DSP_SETFMT, &rc); if (rc < 0) { @@ -192,7 +226,7 @@ try_open (int rw) close (audio_fd); return 0; } - } else if (sn.samplebits == 8) { + } else if (snd->samplebits == 8) { rc = AFMT_U8; rc = ioctl (audio_fd, SNDCTL_DSP_SETFMT, &rc); if (rc < 0) { @@ -202,39 +236,39 @@ try_open (int rw) return 0; } } else { - Sys_Printf ("%d-bit sound not supported. %s", sn.samplebits, + Sys_Printf ("%d-bit sound not supported. %s", snd->samplebits, strerror (errno)); close (audio_fd); return 0; } - tmp = sn.channels; + tmp = snd->channels; rc = ioctl (audio_fd, SNDCTL_DSP_CHANNELS, &tmp); if (rc < 0) { Sys_Printf ("Could not set %s to stereo=%d: %s\n", snd_dev, - sn.channels, strerror (errno)); + snd->channels, strerror (errno)); close (audio_fd); return 0; } - if (snd_rate->int_val) { - sn.speed = snd_rate->int_val; + if (snd_rate) { + snd->speed = snd_rate; } else { for (i = 0; i < ((int) sizeof (tryrates) / 4); i++) if (!ioctl (audio_fd, SNDCTL_DSP_SPEED, &tryrates[i])) break; - sn.speed = tryrates[i]; + snd->speed = tryrates[i]; } - if (!snd_stereo->int_val) { - sn.channels = 1; + if (!snd_stereo) { + snd->channels = 1; } else { - sn.channels = 2; + snd->channels = 2; } - rc = ioctl (audio_fd, SNDCTL_DSP_SPEED, &sn.speed); + rc = ioctl (audio_fd, SNDCTL_DSP_SPEED, &snd->speed); if (rc < 0) { - Sys_Printf ("Could not set %s speed to %d: %s\n", snd_dev, sn.speed, + Sys_Printf ("Could not set %s speed to %d: %s\n", snd_dev, snd->speed, strerror (errno)); close (audio_fd); return 0; @@ -246,25 +280,25 @@ try_open (int rw) return 0; } - sn.frames = info.fragstotal * info.fragsize; - sn.frames /= sn.channels * sn.samplebits / 8; - sn.submission_chunk = 1; + snd->frames = info.fragstotal * info.fragsize; + snd->frames /= snd->channels * snd->samplebits / 8; + snd->submission_chunk = 1; if (mmaped_io) { // memory map the dma buffer unsigned long sz = sysconf (_SC_PAGESIZE); unsigned long len = info.fragstotal * info.fragsize; len = (len + sz - 1) & ~(sz - 1); - sn.buffer = (byte *) mmap (NULL, len, mmmode, mmflags, audio_fd, 0); - if (sn.buffer == MAP_FAILED) { - Sys_MaskPrintf (SYS_SND, "Could not mmap %s: %s\n", snd_dev, + snd->buffer = (byte *) mmap (NULL, len, mmmode, mmflags, audio_fd, 0); + if (snd->buffer == MAP_FAILED) { + Sys_MaskPrintf (SYS_snd, "Could not mmap %s: %s\n", snd_dev, strerror (errno)); close (audio_fd); return 0; } } else { - sn.buffer = malloc (sn.frames * sn.channels * (sn.samplebits / 8)); - if (!sn.buffer) { + snd->buffer = malloc (snd->frames * snd->channels * (snd->samplebits / 8)); + if (!snd->buffer) { Sys_Printf ("SNDDMA_Init: memory allocation failure\n"); close (audio_fd); return 0; @@ -277,7 +311,7 @@ try_open (int rw) if (rc < 0) { Sys_Printf ("Could not toggle.: %s\n", strerror (errno)); if (mmaped_io) - munmap (sn.buffer, sn.frames * sn.channels * sn.samplebits / 8); + munmap (snd->buffer, snd->frames * snd->channels * snd->samplebits / 8); close (audio_fd); return 0; } @@ -286,28 +320,25 @@ try_open (int rw) if (rc < 0) { Sys_Printf ("Could not toggle.: %s\n", strerror (errno)); if (mmaped_io) - munmap (sn.buffer, sn.frames * sn.channels * sn.samplebits / 8); + munmap (snd->buffer, snd->frames * snd->channels * snd->samplebits / 8); close (audio_fd); return 0; } - sn.framepos = 0; + snd->framepos = 0; snd_inited = 1; - return &sn; -} - -static volatile dma_t * -SNDDMA_Init (void) -{ - volatile dma_t *shm; - if ((shm = try_open (0))) - return shm; - return try_open (1); + return 1; } static int -SNDDMA_GetDMAPos (void) +SNDDMA_Init (snd_t *snd) +{ + return try_open (snd, 0) || try_open (snd, 1); +} + +static int +SNDDMA_GetDMAPos (snd_t *snd) { struct count_info count; @@ -317,34 +348,32 @@ SNDDMA_GetDMAPos (void) if (ioctl (audio_fd, SNDCTL_DSP_GETOPTR, &count) == -1) { Sys_Printf ("Uh, %s dead: %s\n", snd_dev, strerror (errno)); if (mmaped_io) - munmap (sn.buffer, sn.frames * sn.channels * sn.samplebits / 8); + munmap (snd->buffer, snd->frames * snd->channels * snd->samplebits / 8); close (audio_fd); snd_inited = 0; return 0; } -// sn.samplepos = (count.bytes / (sn.samplebits / 8)) & (sn.samples-1); -// fprintf(stderr, "%d \r", count.ptr); - sn.framepos = count.ptr / (sn.channels * sn.samplebits / 8); + snd->framepos = count.ptr / (snd->channels * snd->samplebits / 8); - return sn.framepos; + return snd->framepos; } static void -SNDDMA_Shutdown (void) +SNDDMA_shutdown (snd_t *snd) { if (snd_inited) { if (mmaped_io) - munmap (sn.buffer, sn.frames * sn.channels * sn.samplebits / 8); + munmap (snd->buffer, snd->frames * snd->channels * snd->samplebits / 8); close (audio_fd); snd_inited = 0; } } static int -sample_bytes (int frames) +sample_bytes (snd_t *snd, int frames) { - return frames * sn.channels * sn.samplebits / 8; + return frames * snd->channels * snd->samplebits / 8; } /* @@ -353,7 +382,7 @@ sample_bytes (int frames) Send sound to device if buffer isn't really the dma buffer */ static void -SNDDMA_Submit (void) +SNDDMA_Submit (snd_t *snd) { int frames; int len; @@ -362,17 +391,17 @@ SNDDMA_Submit (void) if (snd_inited && !mmaped_io) { frames = *plugin_info_snd_output_data.paintedtime - *plugin_info_snd_output_data.soundtime; - offset = frames * sn.channels * sn.samplebits / 8; + offset = frames * snd->channels * snd->samplebits / 8; - if (sn.framepos + frames <= sn.frames) { - len = sample_bytes (frames); - if (write (audio_fd, sn.buffer + offset, len) != len) + if (snd->framepos + frames <= snd->frames) { + len = sample_bytes (snd, frames); + if (write (audio_fd, snd->buffer + offset, len) != len) Sys_Printf ("SNDDMA_Submit(): %s\n", strerror (errno)); } else { - int len = sample_bytes (sn.frames - sn.framepos); - if (write (audio_fd, sn.buffer + offset, len) != len) + int len = sample_bytes (snd, snd->frames - snd->framepos); + if (write (audio_fd, snd->buffer + offset, len) != len) Sys_Printf ("SNDDMA_Submit(): %s\n", strerror (errno)); - if (write (audio_fd, sn.buffer, offset - len) != offset - len) + if (write (audio_fd, snd->buffer, offset - len) != offset - len) Sys_Printf ("SNDDMA_Submit(): %s\n", strerror (errno)); } *plugin_info_snd_output_data.soundtime += frames; @@ -380,12 +409,12 @@ SNDDMA_Submit (void) } static void -SNDDMA_BlockSound (void) +SNDDMA_BlockSound (snd_t *snd) { } static void -SNDDMA_UnblockSound (void) +SNDDMA_UnblockSound (snd_t *snd) { } @@ -410,14 +439,14 @@ PLUGIN_INFO(snd_output, oss) plugin_info_funcs.input = NULL; plugin_info_funcs.snd_output = &plugin_info_snd_output_funcs; - plugin_info_general_funcs.p_Init = SNDDMA_Init_Cvars; - plugin_info_general_funcs.p_Shutdown = NULL; - plugin_info_snd_output_funcs.pS_O_Init = SNDDMA_Init; - plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_Shutdown; - plugin_info_snd_output_funcs.pS_O_GetDMAPos = SNDDMA_GetDMAPos; - plugin_info_snd_output_funcs.pS_O_Submit = SNDDMA_Submit; - plugin_info_snd_output_funcs.pS_O_BlockSound = SNDDMA_BlockSound; - plugin_info_snd_output_funcs.pS_O_UnblockSound = SNDDMA_UnblockSound; + plugin_info_general_funcs.init = SNDDMA_Init_Cvars; + plugin_info_general_funcs.shutdown = NULL; + plugin_info_snd_output_funcs.init = SNDDMA_Init; + plugin_info_snd_output_funcs.shutdown = SNDDMA_shutdown; + plugin_info_snd_output_funcs.get_dma_pos = SNDDMA_GetDMAPos; + plugin_info_snd_output_funcs.submit = SNDDMA_Submit; + plugin_info_snd_output_funcs.block_sound = SNDDMA_BlockSound; + plugin_info_snd_output_funcs.unblock_sound = SNDDMA_UnblockSound; return &plugin_info; } diff --git a/libs/audio/targets/snd_sdl.c b/libs/audio/targets/snd_sdl.c index 91d5500f5..f77f17a61 100644 --- a/libs/audio/targets/snd_sdl.c +++ b/libs/audio/targets/snd_sdl.c @@ -46,7 +46,6 @@ #include "snd_internal.h" -static dma_t sn; static int snd_inited; static int snd_blocked; @@ -56,9 +55,33 @@ static unsigned shm_rpos; static unsigned wpos; -static cvar_t *snd_bits; -static cvar_t *snd_rate; -static cvar_t *snd_stereo; +static int snd_bits; +static cvar_t snd_bits_cvar = { + .name = "snd_bits", + .description = + "sound sample depth. 0 is system default", + .default_value = "0", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &snd_bits }, +}; +static int snd_rate; +static cvar_t snd_rate_cvar = { + .name = "snd_rate", + .description = + "sound playback rate. 0 is system default", + .default_value = "0", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &snd_rate }, +}; +static int snd_stereo; +static cvar_t snd_stereo_cvar = { + .name = "snd_stereo", + .description = + "sound stereo output", + .default_value = "1", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &snd_stereo }, +}; static plugin_t plugin_info; static plugin_data_t plugin_info_data; @@ -87,21 +110,18 @@ paint_audio (void *unused, Uint8 * stream, int len) static void SNDDMA_Init_Cvars (void) { - snd_stereo = Cvar_Get ("snd_stereo", "1", CVAR_ROM, NULL, - "sound stereo output"); - snd_rate = Cvar_Get ("snd_rate", "0", CVAR_ROM, NULL, - "sound playback rate. 0 is system default"); - snd_bits = Cvar_Get ("snd_bits", "0", CVAR_ROM, NULL, - "sound sample depth. 0 is system default"); + Cvar_Register (&snd_stereo_cvar, 0, 0); + Cvar_Register (&snd_rate_cvar, 0, 0); + Cvar_Register (&snd_bits_cvar, 0, 0); } -static volatile dma_t * -SNDDMA_Init (void) +static int +SNDDMA_Init (snd_t *snd) { SDL_AudioSpec desired, obtained; if (snd_inited) - return &sn; + return 1; if (SDL_Init (SDL_INIT_AUDIO) < 0) { Sys_Printf ("Couldn't initialize SDL AUDIO: %s\n", SDL_GetError ()); @@ -110,9 +130,9 @@ SNDDMA_Init (void) /* Set up the desired format */ desired.freq = 22050; - if (snd_rate->int_val) - desired.freq = snd_rate->int_val; - switch (snd_bits->int_val) { + if (snd_rate) + desired.freq = snd_rate; + switch (snd_bits) { case 8: desired.format = AUDIO_U8; break; @@ -125,10 +145,10 @@ SNDDMA_Init (void) break; default: Sys_Printf ("Unknown number of audio bits: %d\n", - snd_bits->int_val); + snd_bits); return 0; } - desired.channels = snd_stereo->int_val ? 2 : 1; + desired.channels = snd_stereo ? 2 : 1; desired.samples = 1024; desired.callback = paint_audio; @@ -165,17 +185,17 @@ SNDDMA_Init (void) } /* Fill the audio DMA information block */ - sn.samplebits = (obtained.format & 0xFF); - sn.speed = obtained.freq; - sn.channels = obtained.channels; - sn.frames = obtained.samples * 8; // 8 chunks in the buffer - sn.framepos = 0; - sn.submission_chunk = 1; + snd->samplebits = (obtained.format & 0xFF); + snd->speed = obtained.freq; + snd->channels = obtained.channels; + snd->frames = obtained.samples * 8; // 8 chunks in the buffer + snd->framepos = 0; + snd->submission_chunk = 1; - shm_buflen = sn.frames * sn.channels * (sn.samplebits / 8); + shm_buflen = snd->frames * snd->channels * (snd->samplebits / 8); - sn.buffer = calloc (shm_buflen, 1); - if (!sn.buffer) + snd->buffer = calloc (shm_buflen, 1); + if (!snd->buffer) Sys_Error ("Failed to allocate buffer for sound!"); shm_buf = calloc (shm_buflen, 1); @@ -189,28 +209,28 @@ SNDDMA_Init (void) snd_inited = 1; - return &sn; + return 1; } static int -SNDDMA_GetDMAPos (void) +SNDDMA_GetDMAPos (snd_t *snd) { if (!snd_inited) return 0; SDL_LockAudio (); - sn.framepos = shm_rpos / (sn.channels * (sn.samplebits / 8)); + snd->framepos = shm_rpos / (snd->channels * (snd->samplebits / 8)); SDL_UnlockAudio (); - return sn.framepos; + return snd->framepos; } static void -SNDDMA_Shutdown (void) +SNDDMA_shutdown (snd_t *snd) { if (snd_inited) { SDL_CloseAudio (); - free (sn.buffer); + free (snd->buffer); free (shm_buf); snd_inited = 0; } @@ -222,7 +242,7 @@ SNDDMA_Shutdown (void) Send sound to device if buffer isn't really the dma buffer */ static void -SNDDMA_Submit (void) +SNDDMA_Submit (snd_t *snd) { static unsigned old_paintedtime; unsigned len; @@ -236,17 +256,17 @@ SNDDMA_Submit (void) old_paintedtime = 0; len = (*plugin_info_snd_output_data.paintedtime - old_paintedtime) * - sn.channels * (sn.samplebits / 8); + snd->channels * (snd->samplebits / 8); old_paintedtime = *plugin_info_snd_output_data.paintedtime; while (wpos + len > shm_buflen) { - memcpy (shm_buf + wpos, sn.buffer + wpos, shm_buflen - wpos); + memcpy (shm_buf + wpos, snd->buffer + wpos, shm_buflen - wpos); len -= shm_buflen - wpos; wpos = 0; } if (len) { - memcpy (shm_buf + wpos, sn.buffer + wpos, len); + memcpy (shm_buf + wpos, snd->buffer + wpos, len); wpos += len; } @@ -254,7 +274,7 @@ SNDDMA_Submit (void) } static void -SNDDMA_BlockSound (void) +SNDDMA_BlockSound (snd_t *snd) { if (!snd_inited) return; @@ -264,7 +284,7 @@ SNDDMA_BlockSound (void) } static void -SNDDMA_UnblockSound (void) +SNDDMA_UnblockSound (snd_t *snd) { if (!snd_inited || !snd_blocked) return; @@ -294,14 +314,14 @@ PLUGIN_INFO(snd_output, sdl) plugin_info_funcs.input = NULL; plugin_info_funcs.snd_output = &plugin_info_snd_output_funcs; - plugin_info_general_funcs.p_Init = SNDDMA_Init_Cvars; - plugin_info_general_funcs.p_Shutdown = NULL; - plugin_info_snd_output_funcs.pS_O_Init = SNDDMA_Init; - plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_Shutdown; - plugin_info_snd_output_funcs.pS_O_GetDMAPos = SNDDMA_GetDMAPos; - plugin_info_snd_output_funcs.pS_O_Submit = SNDDMA_Submit; - plugin_info_snd_output_funcs.pS_O_BlockSound = SNDDMA_BlockSound; - plugin_info_snd_output_funcs.pS_O_UnblockSound = SNDDMA_UnblockSound; + plugin_info_general_funcs.init = SNDDMA_Init_Cvars; + plugin_info_general_funcs.shutdown = NULL; + plugin_info_snd_output_funcs.init = SNDDMA_Init; + plugin_info_snd_output_funcs.shutdown = SNDDMA_shutdown; + plugin_info_snd_output_funcs.get_dma_pos = SNDDMA_GetDMAPos; + plugin_info_snd_output_funcs.submit = SNDDMA_Submit; + plugin_info_snd_output_funcs.block_sound = SNDDMA_BlockSound; + plugin_info_snd_output_funcs.unblock_sound = SNDDMA_UnblockSound; return &plugin_info; } diff --git a/libs/audio/targets/snd_sgi.c b/libs/audio/targets/snd_sgi.c index 4b431dbaf..27ae12d64 100644 --- a/libs/audio/targets/snd_sgi.c +++ b/libs/audio/targets/snd_sgi.c @@ -269,7 +269,7 @@ SNDDMA_GetDMAPos (void) } static void -SNDDMA_Shutdown (void) +SNDDMA_shutdown (void) { if (snd_inited) { free (write_buffer); @@ -357,7 +357,7 @@ PLUGIN_INFO(snd_output, sgi) plugin_info_general_funcs.p_Init = SNDDMA_Init_Cvars; plugin_info_general_funcs.p_Shutdown = NULL; plugin_info_snd_output_funcs.pS_O_Init = SNDDMA_Init; - plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_Shutdown; + plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_shutdown; plugin_info_snd_output_funcs.pS_O_GetDMAPos = SNDDMA_GetDMAPos; plugin_info_snd_output_funcs.pS_O_Submit = SNDDMA_Submit; plugin_info_snd_output_funcs.pS_O_BlockSound = SNDDMA_BlockSound; diff --git a/libs/audio/targets/snd_sun.c b/libs/audio/targets/snd_sun.c index 89c649416..702757a34 100644 --- a/libs/audio/targets/snd_sun.c +++ b/libs/audio/targets/snd_sun.c @@ -189,7 +189,7 @@ SNDDMA_GetSamples (void) } #endif static void -SNDDMA_Shutdown (void) +SNDDMA_shutdown (void) { if (snd_inited) { close (audio_fd); @@ -281,7 +281,7 @@ PLUGIN_INFO(snd_output, sun) plugin_info_general_funcs.p_Shutdown = NULL; plugin_info_sound_funcs.pS_O_Init = SNDDMA_Init; - plugin_info_sound_funcs.pS_O_Shutdown = SNDDMA_Shutdown; + plugin_info_sound_funcs.pS_O_Shutdown = SNDDMA_shutdown; plugin_info_sound_funcs.pS_O_GetDMAPos = SNDDMA_GetDMAPos; plugin_info_sound_funcs.pS_O_Submit = SNDDMA_Submit; plugin_info_sound_funcs.pS_O_BlockSound = SNDDMA_BlockSound; diff --git a/libs/audio/targets/snd_win.c b/libs/audio/targets/snd_win.c index 91d4ce298..3cc99aa83 100644 --- a/libs/audio/targets/snd_win.c +++ b/libs/audio/targets/snd_win.c @@ -45,12 +45,11 @@ typedef enum { SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL } sndinitstat; -static qboolean snd_firsttime = true; +static bool snd_firsttime = true; static int sample16; static int snd_sent, snd_completed; static int snd_blocked = 0; -static volatile dma_t sn; /* Global variables. Must be visible to window-procedure function @@ -69,41 +68,54 @@ static HWAVEOUT hWaveOut; static DWORD gSndBufSize; -static qboolean SNDDMA_InitWav (void); +static bool SNDDMA_InitWav (snd_t *snd); -static cvar_t *snd_stereo; -static cvar_t *snd_rate; -static cvar_t *snd_bits; - -static plugin_t plugin_info; -static plugin_data_t plugin_info_data; -static plugin_funcs_t plugin_info_funcs; -static general_data_t plugin_info_general_data; -static general_funcs_t plugin_info_general_funcs; -static snd_output_data_t plugin_info_snd_output_data; -static snd_output_funcs_t plugin_info_snd_output_funcs; +static int snd_stereo; +static cvar_t snd_stereo_cvar = { + .name = "snd_stereo", + .description = + "sound stereo output", + .default_value = "1", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &snd_stereo }, +}; +static int snd_rate; +static cvar_t snd_rate_cvar = { + .name = "snd_rate", + .description = + "sound playback rate. 0 is system default", + .default_value = "0", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &snd_rate }, +}; +static int snd_bits; +static cvar_t snd_bits_cvar = { + .name = "snd_bits", + .description = + "sound sample depth. 0 is system default", + .default_value = "0", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &snd_bits }, +}; static void SNDDMA_Init_Cvars (void) { - snd_stereo = Cvar_Get ("snd_stereo", "1", CVAR_ROM, NULL, - "sound stereo output"); - snd_rate = Cvar_Get ("snd_rate", "11025", CVAR_ROM, NULL, - "sound playback rate. 0 is system default"); - snd_bits = Cvar_Get ("snd_bits", "16", CVAR_ROM, NULL, - "sound sample depth. 0 is system default"); + Cvar_Register (&snd_stereo_cvar, 0, 0); + Cvar_Register (&snd_rate_cvar, 0, 0); + Cvar_Register (&snd_bits_cvar, 0, 0); } static void -SNDDMA_BlockSound (void) +SNDDMA_BlockSound (snd_t *snd) { if (++snd_blocked == 1) waveOutReset (hWaveOut); } static void -SNDDMA_UnblockSound (void) +SNDDMA_UnblockSound (snd_t *snd) { if (snd_blocked) --snd_blocked; @@ -149,8 +161,8 @@ FreeSound (void) Crappy windows multimedia base */ -static qboolean -SNDDMA_InitWav (void) +static bool +SNDDMA_InitWav (snd_t *snd) { int i; HRESULT hr; @@ -159,20 +171,20 @@ SNDDMA_InitWav (void) snd_sent = 0; snd_completed = 0; - if (!snd_stereo->int_val) { - sn.channels = 1; + if (!snd_stereo) { + snd->channels = 1; } else { - sn.channels = 2; + snd->channels = 2; } - sn.samplebits = snd_bits->int_val; - sn.speed = snd_rate->int_val; + snd->samplebits = snd_bits; + snd->speed = snd_rate; memset (&format, 0, sizeof (format)); format.wFormatTag = WAVE_FORMAT_PCM; - format.nChannels = sn.channels; - format.wBitsPerSample = sn.samplebits; - format.nSamplesPerSec = sn.speed; + format.nChannels = snd->channels; + format.wBitsPerSample = snd->samplebits; + format.nSamplesPerSec = snd->speed; format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; format.cbSize = 0; format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; @@ -246,11 +258,11 @@ SNDDMA_InitWav (void) } } - sn.frames = gSndBufSize / (sn.samplebits / 8) / sn.channels; - sn.framepos = 0; - sn.submission_chunk = 1; - sn.buffer = (unsigned char *) lpData; - sample16 = (sn.samplebits / 8) - 1; + snd->frames = gSndBufSize / (snd->samplebits / 8) / snd->channels; + snd->framepos = 0; + snd->submission_chunk = 1; + snd->buffer = (unsigned char *) lpData; + sample16 = (snd->samplebits / 8) - 1; return true; } @@ -261,11 +273,12 @@ SNDDMA_InitWav (void) Try to find a sound device to mix for. Returns false if nothing is found. */ -static volatile dma_t * -SNDDMA_Init (void) +static int +SNDDMA_Init (snd_t *snd) { + int ret = 0; if (snd_firsttime) { - if (SNDDMA_InitWav ()) { + if ((ret = SNDDMA_InitWav (snd))) { Sys_Printf ("Wave sound initialized\n"); } else { Sys_Printf ("Wave sound failed to init\n"); @@ -273,7 +286,7 @@ SNDDMA_Init (void) } snd_firsttime = false; - return &sn; + return ret; } /* @@ -284,19 +297,19 @@ SNDDMA_Init (void) how many sample are required to fill it up. */ static int -SNDDMA_GetDMAPos (void) +SNDDMA_GetDMAPos (snd_t *snd) { int s = 0; s = snd_sent * WAV_BUFFER_SIZE; s >>= sample16; - s /= sn.channels; + s /= snd->channels; - s %= sn.frames; - sn.framepos = s; + s %= snd->frames; + snd->framepos = s; - return sn.framepos; + return snd->framepos; } /* @@ -305,7 +318,7 @@ SNDDMA_GetDMAPos (void) Send sound to device if buffer isn't really the dma buffer */ static void -SNDDMA_Submit (void) +SNDDMA_Submit (snd_t *snd) { int wResult; LPWAVEHDR h; @@ -313,7 +326,7 @@ SNDDMA_Submit (void) // find which sound blocks have completed while (1) { if (snd_completed == snd_sent) { - Sys_MaskPrintf (SYS_SND, "Sound overrun\n"); + Sys_MaskPrintf (SYS_snd, "Sound overrun\n"); break; } @@ -344,46 +357,55 @@ SNDDMA_Submit (void) } } -/* - SNDDMA_Shutdown - - Reset the sound device for exiting -*/ static void -SNDDMA_Shutdown (void) +SNDDMA_shutdown (snd_t *snd) { FreeSound (); } -PLUGIN_INFO(snd_output, win) -{ - plugin_info.type = qfp_snd_output; - plugin_info.api_version = QFPLUGIN_VERSION; - plugin_info.plugin_version = "0.1"; - plugin_info.description = "Windows digital output"; - plugin_info.copyright = "Copyright (C) 1996-1997 id Software, Inc.\n" +static snd_output_data_t plugin_info_snd_output_data = { +}; + +static snd_output_funcs_t plugin_info_snd_output_funcs = { + .init = SNDDMA_Init, + .shutdown = SNDDMA_shutdown, + .get_dma_pos = SNDDMA_GetDMAPos, + .submit = SNDDMA_Submit, + .block_sound = SNDDMA_BlockSound, + .unblock_sound = SNDDMA_UnblockSound, +}; + +static general_data_t plugin_info_general_data = { +}; + +static general_funcs_t plugin_info_general_funcs = { + .init = SNDDMA_Init_Cvars, +}; + +static plugin_data_t plugin_info_data = { + .general = &plugin_info_general_data, + .snd_output = &plugin_info_snd_output_data, +}; + +static plugin_funcs_t plugin_info_funcs = { + .general = &plugin_info_general_funcs, + .snd_output = &plugin_info_snd_output_funcs, +}; + +static plugin_t plugin_info = { + .type = qfp_snd_output, + .api_version = QFPLUGIN_VERSION, + .plugin_version = "0.1", + .description = "Windows digital output", + .copyright = "Copyright (C) 1996-1997 id Software, Inc.\n" "Copyright (C) 1999,2000,2001 contributors of the QuakeForge " "project\n" - "Please see the file \"AUTHORS\" for a list of contributors"; - plugin_info.functions = &plugin_info_funcs; - plugin_info.data = &plugin_info_data; - - plugin_info_data.general = &plugin_info_general_data; - plugin_info_data.input = NULL; - plugin_info_data.snd_output = &plugin_info_snd_output_data; - - plugin_info_funcs.general = &plugin_info_general_funcs; - plugin_info_funcs.input = NULL; - plugin_info_funcs.snd_output = &plugin_info_snd_output_funcs; - - plugin_info_general_funcs.p_Init = SNDDMA_Init_Cvars; - plugin_info_general_funcs.p_Shutdown = NULL; - plugin_info_snd_output_funcs.pS_O_Init = SNDDMA_Init; - plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_Shutdown; - plugin_info_snd_output_funcs.pS_O_GetDMAPos = SNDDMA_GetDMAPos; - plugin_info_snd_output_funcs.pS_O_Submit = SNDDMA_Submit; - plugin_info_snd_output_funcs.pS_O_BlockSound = SNDDMA_BlockSound; - plugin_info_snd_output_funcs.pS_O_UnblockSound = SNDDMA_UnblockSound; + "Please see the file \"AUTHORS\" for a list of contributors", + .functions = &plugin_info_funcs, + .data = &plugin_info_data, +}; +PLUGIN_INFO(snd_output, win) +{ return &plugin_info; } diff --git a/libs/audio/test/Makefile.am b/libs/audio/test/Makefile.am deleted file mode 100644 index 410be4f4e..000000000 --- a/libs/audio/test/Makefile.am +++ /dev/null @@ -1,16 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CPPFLAGS= -I$(top_srcdir)/include - -noinst_PROGRAMS= @AUDIO_TARGETS@ - -EXTRA_PROGRAMS= testsound - -test_libs= \ - $(top_builddir)/libs/audio/libQFsound.la \ - $(top_builddir)/libs/ruamoko/libQFruamoko.la \ - $(top_builddir)/libs/util/libQFutil.la - -testsound_SOURCES= testsound.c -testsound_LDADD= $(test_libs) -testsound_DEPENDENCIES= $(test_libs) diff --git a/libs/audio/test/Makemodule.am b/libs/audio/test/Makemodule.am new file mode 100644 index 000000000..6b97b2e53 --- /dev/null +++ b/libs/audio/test/Makemodule.am @@ -0,0 +1,13 @@ +noinst_PROGRAMS += @AUDIO_TARGETS@ + +EXTRA_PROGRAMS += libs/audio/test/testsound + +testaudio_libs= \ + libs/audio/libQFsound.la \ + libs/scene/libQFscene.la \ + libs/ruamoko/libQFruamoko.la \ + libs/util/libQFutil.la + +libs_audio_test_testsound_SOURCES= libs/audio/test/testsound.c +libs_audio_test_testsound_LDADD= $(testaudio_libs) +libs_audio_test_testsound_DEPENDENCIES= $(testaudio_libs) diff --git a/libs/audio/test/testsound.c b/libs/audio/test/testsound.c index f4518d7ac..8e52bc6b3 100644 --- a/libs/audio/test/testsound.c +++ b/libs/audio/test/testsound.c @@ -46,10 +46,11 @@ #include "QF/zone.h" #include "QF/sound.h" +#include "QF/scene/transform.h" #ifdef _WIN32 # include "winquake.h" -HWND mainwindow; +HWND win_mainwindow; #endif #define MEMSIZE (32 * 1024 * 1024) @@ -65,12 +66,12 @@ init (void) testsound_args = Cbuf_ArgsNew (); Sys_Init (); - COM_ParseConfig (); - Cvar_Get ("cmd_warncmd", "1", CVAR_NONE, NULL, NULL); + COM_ParseConfig (testsound_cbuf); + cmd_warncmd = 1; - Memory_Init (malloc (MEMSIZE), MEMSIZE); + memhunk_t *hunk = Memory_Init (Sys_Alloc (MEMSIZE), MEMSIZE); - QFS_Init ("qw"); + QFS_Init (hunk, "qw"); PI_Init (); S_Init_Cvars (); @@ -88,7 +89,7 @@ main (int argc, const char *argv[]) while (1) { Cbuf_Execute_Stack (testsound_cbuf); - S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin, 0); + S_Update (nulltransform, 0); usleep(20 * 1000); } Sys_Quit (); diff --git a/libs/client/Makefile.am b/libs/client/Makefile.am deleted file mode 100644 index d9fc98a77..000000000 --- a/libs/client/Makefile.am +++ /dev/null @@ -1,11 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CFLAGS= @PREFER_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include - -noinst_LTLIBRARIES= libQFclient.la - -libQFclient_la_LDFLAGS= @STATIC@ -libQFclient_la_LIBADD= $(top_builddir)/libs/gamecode/libQFgamecode.la $(top_builddir)/libs/util/libQFutil.la -libQFclient_la_SOURCES= \ - cl_entities.c diff --git a/libs/client/Makemodule.am b/libs/client/Makemodule.am new file mode 100644 index 000000000..cddd028e8 --- /dev/null +++ b/libs/client/Makemodule.am @@ -0,0 +1,44 @@ +noinst_LTLIBRARIES += libs/client/libQFclient.la + +particles_src= libs/client/particles.part +particles_gen= libs/client/particles.pc + +SUFFICES += .part .pc +.part.pc: + $(V_SED)sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/\\n"/' $< > $@.t &&\ + $(am__mv) $@.t $@ + +libs_client_libQFclient_la_LDFLAGS= @STATIC@ +libs_client_libQFclient_la_LIBADD= \ + libs/ui/libQFgui.la \ + libs/gamecode/libQFgamecode.la \ + libs/util/libQFutil.la +libs_client_libQFclient_la_SOURCES= \ + libs/client/cl_chase.c \ + libs/client/cl_effects.c \ + libs/client/cl_entities.c \ + libs/client/cl_input.c \ + libs/client/cl_light.c \ + libs/client/cl_particles.c \ + libs/client/cl_screen.c \ + libs/client/cl_temp_entities.c \ + libs/client/cl_view.c \ + libs/client/cl_world.c \ + libs/client/hud.c \ + libs/client/locs.c \ + libs/client/old_keys.c \ + libs/client/sbar.c + +default_input_src = libs/client/default_input.plist +default_input_gen = libs/client/default_input.plc + +EXTRA_DIST += \ + $(default_input_src) \ + $(particles_src) +CLEANFILES += \ + libs/client/*.plc \ + libs/client/*.pc + +BUILT_SOURCES += \ + $(default_input_gen) \ + $(particles_gen) diff --git a/libs/client/cl_chase.c b/libs/client/cl_chase.c new file mode 100644 index 000000000..7712875ba --- /dev/null +++ b/libs/client/cl_chase.c @@ -0,0 +1,323 @@ +/* + cl_chase.c + + chase camera support + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/cvar.h" +#include "QF/keys.h" +#include "QF/input.h" +#include "QF/mathlib.h" + +#include "QF/plugin/vid_render.h" +#include "QF/scene/transform.h" + +#include "world.h" + +#include "client/chase.h" +#include "client/input.h" +#include "client/view.h" + + +float chase_back; +static cvar_t chase_back_cvar = { + .name = "chase_back", + .description = + "None", + .default_value = "100", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &chase_back }, +}; +float chase_up; +static cvar_t chase_up_cvar = { + .name = "chase_up", + .description = + "None", + .default_value = "16", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &chase_up }, +}; +float chase_right; +static cvar_t chase_right_cvar = { + .name = "chase_right", + .description = + "None", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &chase_right }, +}; +int chase_active; +static cvar_t chase_active_cvar = { + .name = "chase_active", + .description = + "None", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &chase_active }, +}; + + +void +Chase_Init_Cvars (void) +{ + Cvar_Register (&chase_back_cvar, 0, 0); + Cvar_Register (&chase_up_cvar, 0, 0); + Cvar_Register (&chase_right_cvar, 0, 0); + Cvar_Register (&chase_active_cvar, 0, 0); +} + +void +Chase_Reset (void) +{ + // for respawning and teleporting + // start position 12 units behind head +} + +static inline vec4f_t +TraceLine (chasestate_t *cs, vec4f_t start, vec4f_t end) +{ + trace_t trace; + + memset (&trace, 0, sizeof (trace)); + trace.fraction = 1; + MOD_TraceLine (cs->worldmodel->brush.hulls, 0, (vec_t*)&start, (vec_t*)&end, &trace);//FIXME + + return (vec4f_t) {trace.endpos[0], trace.endpos[1], trace.endpos[2], 1}; +} + +static void +check_for_walls (chasestate_t *cs, vec4f_t forward) +{ + // check for walls between player and camera + cs->camera_origin += 8 * forward; + vec4f_t stop = TraceLine (cs, cs->player_origin, cs->camera_origin); + if (magnitude3f (stop)[0] != 0) { + cs->camera_origin = stop - forward; + } +} + + +static void +limit_distance (chasestate_t *cs) +{ + // don't let camera get too far from player + + vec4f_t dir = cs->camera_origin - cs->player_origin; + vec4f_t forward = normalf (dir); + + if (magnitudef (dir)[0] > chase_back) { + cs->camera_origin = cs->player_origin + forward * chase_back; + } +} + +static void +set_camera (chasestate_t *cs, viewstate_t *vs) +{ + vec4f_t rotation; + AngleQuat (cs->camera_angles, (vec_t*)&rotation);//FIXME + Transform_SetWorldRotation (vs->camera_transform, rotation); + Transform_SetWorldPosition (vs->camera_transform, cs->camera_origin); +} + +static void +cam_controls (chasestate_t *cs, viewstate_t *vs) +{ + // FIXME this doesn't actually control the camera, but rather makes the + // player face the direction of motion. It probably should not access + // movement input buttons and axes directly. + // get basic movement from keyboard + vec4f_t move = { }; + vec4f_t forward = { }; + vec4f_t right = { }; + vec4f_t up = { }; + vec4f_t dir = { }; + + if (in_strafe.state & 1) { + move[SIDE] += cl_sidespeed * IN_ButtonState (&in_right); + move[SIDE] -= cl_sidespeed * IN_ButtonState (&in_left); + } + move[SIDE] += cl_sidespeed * IN_ButtonState (&in_moveright); + move[SIDE] -= cl_sidespeed * IN_ButtonState (&in_moveleft); + + if (!(in_klook.state & 1)) { + move[FORWARD] += cl_forwardspeed + * IN_ButtonState (&in_forward); + move[FORWARD] -= cl_backspeed * IN_ButtonState (&in_back); + } + if (in_speed.state & 1) { + move *= cl_movespeedkey; + } + + // mouse and joystick controllers add to movement + VectorSet (0, vs->player_angles[1] - cs->camera_angles[1], 0, dir); + AngleVectors ((vec_t*)&dir, (vec_t*)&forward, (vec_t*)&right, (vec_t*)&up); //FIXME + forward *= IN_UpdateAxis (&in_cam_forward) * m_forward; + right *= IN_UpdateAxis (&in_cam_side) * m_side; + dir = forward + right; + move[FORWARD] += dir[0]; + move[SIDE] -= dir[1]; + + VectorSet (0, cs->camera_angles[1], 0, dir); + AngleVectors ((vec_t*)&dir, (vec_t*)&forward, (vec_t*)&right, (vec_t*)&up); //FIXME + + dir = forward * move[FORWARD] + right * move[SIDE]; + + if (dir[1] || dir[0]) { + vs->player_angles[YAW] = (atan2 (dir[1], dir[0]) * 180 / M_PI); + } + + //vs->player_angles[PITCH] = 0; + VectorCopy (vs->player_angles, cs->player_angles); +} + +static void +update_cam_frame (chasestate_t *cs, viewstate_t *vs) +{ + vec3_t d; + VectorSubtract (vs->player_angles, cs->player_angles, d); + VectorAdd (cs->camera_angles, d, cs->camera_angles); + // remember the new angle to calculate the difference next frame + VectorCopy (vs->player_angles, cs->player_angles); +} + +static void +chase_mode_1 (chasestate_t *cs) +{ + // regular camera, faces same direction as player + viewstate_t *vs = cs->viewstate; + vec4f_t forward = {}, up = {}, right = {}, stop = {}; + + AngleVectors (vs->player_angles, (vec_t*)&forward, (vec_t*)&right, (vec_t*)&up);//FIXME + VectorCopy (vs->player_angles, cs->camera_angles); + + // calc exact destination + cs->camera_origin = vs->player_origin + - forward * chase_back - right * chase_right; + // chase_up is world up + cs->camera_origin[2] += chase_up; + + // check for walls between player and camera + stop = TraceLine (cs, vs->player_origin, cs->camera_origin); + if (magnitude3f (stop)[0] != 0) { + cs->camera_origin = stop + forward * 8; + } + + set_camera (cs, vs); +} + +static void +chase_mode_2 (chasestate_t *cs) +{ + viewstate_t *vs = cs->viewstate; + vec4f_t forward = {}, up = {}, right = {}, dir = {}; + + // lazy camera, look toward player entity + + update_cam_frame (cs, vs); + + cs->camera_angles[PITCH] = bound (-60, cs->camera_angles[PITCH], 60); + + // move camera, it's not enough to just change the angles because + // the angles are automatically changed to look toward the player + AngleVectors (cs->camera_angles, (vec_t*)&forward, (vec_t*)&right, (vec_t*)&up);//FIXME + cs->camera_origin = cs->player_origin - chase_back * forward; + + cs->player_origin = vs->player_origin; + + // don't let camera get too low + if (cs->camera_origin[2] < cs->player_origin[2] + chase_up) { + cs->camera_origin[2] = cs->player_origin[2] + chase_up; + } + + limit_distance (cs); + check_for_walls (cs, forward); + + dir = vs->player_origin - cs->camera_origin; + + if (dir[1] == 0 && dir[0] == 0) { + // look straight up or down + cs->camera_angles[YAW] = vs->player_angles[YAW]; + if (dir[2] > 0) + cs->camera_angles[PITCH] = 90; + else + cs->camera_angles[PITCH] = -90; + } else { + float pitch, yaw, fwd; + yaw = (atan2 (dir[1], dir[0]) * 180 / M_PI); + cs->camera_angles[YAW] = yaw; + + fwd = sqrt (dir[0] * dir[0] + dir[1] * dir[1]); + pitch = -(atan2 (dir[2], fwd) * 180 / M_PI); + cs->camera_angles[PITCH] = pitch; + } + set_camera (cs, vs); + cam_controls (cs, vs); +} + +static void +chase_mode_3 (chasestate_t *cs) +{ + viewstate_t *vs = cs->viewstate; + vec4f_t forward = {}, up = {}, right = {}; + + // lazy camera, look toward player entity + update_cam_frame (cs, vs); + + // move camera, it's not enough to just change the angles because + // the angles are automatically changed to look toward the player + + cs->player_origin = vs->player_origin; + AngleVectors (cs->camera_angles, (vec_t*)&forward, (vec_t*)&right, (vec_t*)&up);//FIXME + cs->camera_origin = cs->player_origin - chase_back * forward; + limit_distance (cs); + check_for_walls (cs, forward); + set_camera (cs, vs); + cam_controls (cs, vs); +} + +void +Chase_Update (chasestate_t *cs) +{ + switch (chase_active) { + case 1: + chase_mode_1 (cs); + return; + case 2: + chase_mode_2 (cs); + return; + case 3: + chase_mode_3 (cs); + return; + } +} diff --git a/libs/client/cl_effects.c b/libs/client/cl_effects.c new file mode 100644 index 000000000..982d381fb --- /dev/null +++ b/libs/client/cl_effects.c @@ -0,0 +1,187 @@ +/* + cl_effect.c + + Client side effect management + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/3/11 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/render.h" + +#include "QF/plugin/vid_render.h" //FIXME +#include "QF/scene/entity.h" +#include "QF/scene/scene.h" + +#include "client/entities.h" +#include "client/effects.h" +#include "client/particles.h" +#include "client/world.h" + +void +CL_NewDlight (int key, vec4f_t org, int effects, byte glow_size, + byte glow_color, double time) +{ + float radius; + dlight_t *dl; + static quat_t normal = {0.4, 0.2, 0.05, 0.7}; + static quat_t red = {0.5, 0.05, 0.05, 0.7}; + static quat_t blue = {0.05, 0.05, 0.5, 0.7}; + static quat_t purple = {0.5, 0.05, 0.5, 0.7}; + + effects &= EF_BLUE | EF_RED | EF_BRIGHTLIGHT | EF_DIMLIGHT; + if (!effects) { + if (!glow_size) + return; + } + + dl = R_AllocDlight (key); + if (!dl) + return; + VectorCopy (org, dl->origin); + + if (effects & (EF_BLUE | EF_RED | EF_BRIGHTLIGHT | EF_DIMLIGHT)) { + radius = 200 + (rand () & 31); + if (effects & EF_BRIGHTLIGHT) { + radius += 200; + dl->origin[2] += 16; + } + if (effects & EF_DIMLIGHT) + if (effects & ~EF_DIMLIGHT) + radius -= 100; + dl->radius = radius; + dl->die = time + 0.1; + + switch (effects & (EF_RED | EF_BLUE)) { + case EF_RED | EF_BLUE: + QuatCopy (purple, dl->color); + break; + case EF_RED: + QuatCopy (red, dl->color); + break; + case EF_BLUE: + QuatCopy (blue, dl->color); + break; + default: + QuatCopy (normal, dl->color); + break; + } + } + + if (glow_size) { + dl->radius += glow_size < 128 ? glow_size * 8.0 : + (glow_size - 256) * 8.0; + dl->die = time + 0.1; + if (glow_color) { + if (glow_color == 255) { + dl->color[0] = dl->color[1] = dl->color[2] = 1.0; + } else { + byte *tempcolor; + + tempcolor = (byte *) &d_8to24table[glow_color]; + VectorScale (tempcolor, 1 / 255.0, dl->color); + } + } + } +} + +void +CL_ModelEffects (entity_t ent, int num, int glow_color, double time) +{ + dlight_t *dl; + transform_t transform = Entity_Transform (ent); + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, cl_world.scene->reg); + model_t *model = renderer->model; + vec4f_t *old_origin = Ent_GetComponent (ent.id, scene_old_origin, cl_world.scene->reg); + vec4f_t ent_origin = Transform_GetWorldPosition (transform); + + // add automatic particle trails + if (model->flags & EF_ROCKET) { + dl = R_AllocDlight (num); + if (dl) { + VectorCopy (ent_origin, dl->origin); + dl->radius = 200.0; + dl->die = time + 0.1; + //FIXME VectorCopy (r_firecolor, dl->color); + VectorSet (0.9, 0.7, 0.0, dl->color); + dl->color[3] = 0.7; + } + clp_funcs->RocketTrail (*old_origin, ent_origin); + } else if (model->flags & EF_GRENADE) + clp_funcs->GrenadeTrail (*old_origin, ent_origin); + else if (model->flags & EF_GIB) + clp_funcs->BloodTrail (*old_origin, ent_origin); + else if (model->flags & EF_ZOMGIB) + clp_funcs->SlightBloodTrail (*old_origin, ent_origin); + else if (model->flags & EF_TRACER) + clp_funcs->WizTrail (*old_origin, ent_origin); + else if (model->flags & EF_TRACER2) + clp_funcs->FlameTrail (*old_origin, ent_origin); + else if (model->flags & EF_TRACER3) + clp_funcs->VoorTrail (*old_origin, ent_origin); + else if (model->flags & EF_GLOWTRAIL) + clp_funcs->GlowTrail (*old_origin, ent_origin, glow_color); +} + +void +CL_MuzzleFlash (vec4f_t position, vec4f_t fv, float zoffset, int num, + double time) +{ + dlight_t *dl = R_AllocDlight (num); + if (dl) { + position += 18 * fv; + VectorCopy (position, dl->origin); + dl->origin[2] += zoffset; + dl->radius = 200 + (rand () & 31); + dl->die = time + 0.1; + dl->minlight = 32; + dl->color[0] = 0.2; + dl->color[1] = 0.1; + dl->color[2] = 0.05; + dl->color[3] = 0.7; + } +} + +void +CL_EntityEffects (int num, entity_t ent, entity_state_t *state, double time) +{ + transform_t transform = Entity_Transform (ent); + vec4f_t position = Transform_GetWorldPosition (transform); + if (state->effects & EF_BRIGHTFIELD) + clp_funcs->EntityParticles (position); + if (state->effects & EF_MUZZLEFLASH) { + vec4f_t fv = Transform_Forward (transform); + CL_MuzzleFlash (position, fv, 16, num, time); + } +} diff --git a/libs/client/cl_entities.c b/libs/client/cl_entities.c index 01397321a..324c1e66d 100644 --- a/libs/client/cl_entities.c +++ b/libs/client/cl_entities.c @@ -31,7 +31,22 @@ # include "config.h" #endif +#include "QF/msg.h" + +#include "QF/scene/entity.h" +#include "QF/scene/scene.h" +#include "QF/simd/vec4f.h" + +#include "QF/plugin/vid_render.h" //FIXME + +#include "QF/scene/entity.h" +#include "QF/scene/scene.h" + #include "client/entities.h" +#include "client/temp_entities.h" +#include "client/world.h" + +entitystateset_t cl_static_entities = DARRAY_STATIC_INIT (32); /* QW has a max of 512 entities and wants 64 frames of data per entity, plus the baseline data (512 * (64 + 1) = 33280), but NQ has a max of 32000 @@ -340,3 +355,29 @@ vec3_t ent_colormod[256] = { {1, 1, 0.666667}, {1, 1, 1} }; + +void +CL_TransformEntity (entity_t ent, float scale, const vec3_t angles, + vec4f_t position) +{ + vec4f_t rotation; + vec4f_t scalevec = { scale, scale, scale, 1}; + + if (VectorIsZero (angles)) { + rotation = (vec4f_t) { 0, 0, 0, 1 }; + } else { + vec3_t ang; + VectorCopy (angles, ang); + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, + ent.reg); + if (renderer->model && renderer->model->type == mod_alias) { + // stupid quake bug + // why, oh, why, do alias models pitch in the opposite direction + // to everything else? + ang[PITCH] = -ang[PITCH]; + } + AngleQuat (ang, (vec_t*)&rotation);//FIXME + } + transform_t transform = Entity_Transform (ent); + Transform_SetLocalTransform (transform, scalevec, rotation, position); +} diff --git a/libs/client/cl_input.c b/libs/client/cl_input.c new file mode 100644 index 000000000..1f288b6bd --- /dev/null +++ b/libs/client/cl_input.c @@ -0,0 +1,650 @@ +/* + cl_input.c + + Client input commands + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/11/24 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/cmd.h" +#include "QF/console.h" +#include "QF/cvar.h" +#include "QF/dstring.h" +#include "QF/input.h" +#include "QF/keys.h" +#include "QF/plist.h" +#include "QF/sys.h" + +#include "QF/input/event.h" + +#include "old_keys.h" + +#include "client/chase.h" +#include "client/input.h" +#include "client/view.h" + +int cl_game_context; +int cl_demo_context; +static int cl_event_id; + +static struct LISTENER_SET_TYPE(int) cl_on_focus_change + = LISTENER_SET_STATIC_INIT(4); + +in_axis_t in_move_forward = { + .mode = ina_set, + .name = "move.forward", + .description = "Move forward (negative) or backward (positive)", +}; +in_axis_t in_move_side = { + .mode = ina_set, + .name = "move.side", + .description = "Move right (positive) or left (negative)", +}; +in_axis_t in_move_up = { + .mode = ina_set, + .name = "move.up", + .description = "Move up (positive) or down (negative)", +}; + +in_axis_t in_cam_forward = { + .mode = ina_set, + .name = "cam.forward", + .description = "Move camera forward (negative) or backward (positive)", +}; +in_axis_t in_cam_side = { + .mode = ina_set, + .name = "cam.side", + .description = "Move camera right (positive) or left (negative)", +}; +in_axis_t in_cam_up = { + .mode = ina_set, + .name = "cam.up", + .description = "Move camera up (positive) or down (negative)", +}; + +in_axis_t in_move_pitch = { + .mode = ina_set, + .name = "move.pitch", + .description = "Pitch axis", +}; +in_axis_t in_move_yaw = { + .mode = ina_set, + .name = "move.yaw", + .description = "Yaw axis", +}; +in_axis_t in_move_roll = { + .mode = ina_set, + .name = "move.roll", + .description = "Roll axis", +}; + +in_button_t in_left = { + .name = "left", + .description = "When active the player is turning left" +}; +in_button_t in_right = { + .name = "right", + .description = "When active the player is turning right" +}; +in_button_t in_forward = { + .name = "forward", + .description = "When active the player is moving forward" +}; +in_button_t in_back = { + .name = "back", + .description = "When active the player is moving backwards" +}; +in_button_t in_lookup = { + .name = "lookup", + .description = "When active the player's view is looking up" +}; +in_button_t in_lookdown = { + .name = "lookdown", + .description = "When active the player's view is looking down" +}; +in_button_t in_moveleft = { + .name = "moveleft", + .description = "When active the player is strafing left" +}; +in_button_t in_moveright = { + .name = "moveright", + .description = "When active the player is strafing right" +}; +in_button_t in_use = { + .name = "use", + .description = "Left over command for opening doors and triggering" + " switches" +}; +in_button_t in_jump = { + .name = "jump", + .description = "When active the player is jumping" +}; +in_button_t in_attack = { + .name = "attack", + .description = "When active player is firing/using current weapon" +}; +in_button_t in_up = { + .name = "moveup", + .description = "When active the player is swimming up in a liquid" +}; +in_button_t in_down = { + .name = "movedown", + .description = "When active the player is swimming down in a liquid" +}; +in_button_t in_strafe = { + .name = "strafe", + .description = "When active, +left and +right function like +moveleft and" + " +moveright" +}; +in_button_t in_klook = { + .name = "klook", + .description = "When active, +forward and +back perform +lookup and" + " +lookdown" +}; +in_button_t in_speed = { + .name = "speed", + .description = "When active the player is running" +}; +in_button_t in_mlook = { + .name = "mlook", + .description = "When active moving the mouse or joystick forwards " + "and backwards performs +lookup and " + "+lookdown" +}; + +static in_axis_t *cl_in_axes[] = { + &in_move_forward, + &in_move_side, + &in_move_up, + &in_move_pitch, + &in_move_yaw, + &in_move_roll, + 0, +}; + +static in_button_t *cl_in_buttons[] = { + &in_left, + &in_right, + &in_forward, + &in_back, + &in_lookup, + &in_lookdown, + &in_moveleft, + &in_moveright, + &in_use, + &in_jump, + &in_attack, + &in_up, + &in_down, + &in_strafe, + &in_klook, + &in_speed, + &in_mlook, + 0 +}; + +float cl_anglespeedkey; +static cvar_t cl_anglespeedkey_cvar = { + .name = "cl_anglespeedkey", + .description = + "turn `run' speed multiplier", + .default_value = "1.5", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &cl_anglespeedkey }, +}; +float cl_backspeed; +static cvar_t cl_backspeed_cvar = { + .name = "cl_backspeed", + .description = + "backward speed", + .default_value = "200", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &cl_backspeed }, +}; +float cl_forwardspeed; +static cvar_t cl_forwardspeed_cvar = { + .name = "cl_forwardspeed", + .description = + "forward speed", + .default_value = "200", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &cl_forwardspeed }, +}; +float cl_movespeedkey; +static cvar_t cl_movespeedkey_cvar = { + .name = "cl_movespeedkey", + .description = + "move `run' speed multiplier", + .default_value = "2.0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &cl_movespeedkey }, +}; +float cl_pitchspeed; +static cvar_t cl_pitchspeed_cvar = { + .name = "cl_pitchspeed", + .description = + "look up/down speed", + .default_value = "150", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &cl_pitchspeed }, +}; +float cl_sidespeed; +static cvar_t cl_sidespeed_cvar = { + .name = "cl_sidespeed", + .description = + "strafe speed", + .default_value = "350", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &cl_sidespeed }, +}; +float cl_upspeed; +static cvar_t cl_upspeed_cvar = { + .name = "cl_upspeed", + .description = + "swim/fly up/down speed", + .default_value = "200", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &cl_upspeed }, +}; +float cl_yawspeed; +static cvar_t cl_yawspeed_cvar = { + .name = "cl_yawspeed", + .description = + "turning speed", + .default_value = "140", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &cl_yawspeed }, +}; +float cl_maxpitch; +static cvar_t cl_maxpitch_cvar = { + .name = "cl_maxpitch", + .description = + "turning speed", + .default_value = "90", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &cl_maxpitch }, +}; +float cl_minpitch; +static cvar_t cl_minpitch_cvar = { + .name = "cl_minpitch", + .description = + "turning speed", + .default_value = "-90", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &cl_minpitch }, +}; +float cl_maxroll; +static cvar_t cl_maxroll_cvar = { + .name = "cl_maxroll", + .description = + "turning speed", + .default_value = "50", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &cl_maxroll }, +}; +float cl_minroll; +static cvar_t cl_minroll_cvar = { + .name = "cl_minroll", + .description = + "turning speed", + .default_value = "-50", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &cl_minroll }, +}; + +int lookspring; +static cvar_t lookspring_cvar = { + .name = "lookspring", + .description = + "Snap view to center when moving and no mlook/klook", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &lookspring }, +}; +float m_pitch; +static cvar_t m_pitch_cvar = { + .name = "m_pitch", + .description = + "mouse pitch (up/down) multipier", + .default_value = "0.022", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &m_pitch }, +}; +float m_yaw; +static cvar_t m_yaw_cvar = { + .name = "m_yaw", + .description = + "mouse yaw (left/right) multipiler", + .default_value = "0.022", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &m_yaw }, +}; +float m_forward; +static cvar_t m_forward_cvar = { + .name = "m_forward", + .description = + "mouse forward/back speed", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &m_forward }, +}; +float m_side; +static cvar_t m_side_cvar = { + .name = "m_side", + .description = + "mouse strafe speed", + .default_value = "0.8", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &m_side }, +}; + +static void +CL_AdjustAngles (float frametime, movestate_t *ms, viewstate_t *vs) +{ + float down, up; + float pitchspeed, yawspeed; + vec4f_t delta = {}; + + pitchspeed = cl_pitchspeed; + yawspeed = cl_yawspeed; + + if (in_speed.state & inb_down) { + pitchspeed *= cl_anglespeedkey; + yawspeed *= cl_anglespeedkey; + } + + pitchspeed *= frametime; + yawspeed *= frametime; + + if (!(in_strafe.state & inb_down)) { + delta[YAW] -= yawspeed * IN_ButtonState (&in_right); + delta[YAW] += yawspeed * IN_ButtonState (&in_left); + } + if (in_klook.state & inb_down) { + V_StopPitchDrift (vs); + delta[PITCH] -= pitchspeed * IN_ButtonState (&in_forward); + delta[PITCH] += pitchspeed * IN_ButtonState (&in_back); + } + + up = IN_ButtonState (&in_lookup); + down = IN_ButtonState (&in_lookdown); + + delta[PITCH] -= pitchspeed * up; + delta[PITCH] += pitchspeed * down; + + delta[PITCH] -= IN_UpdateAxis (&in_move_pitch) * m_pitch; + delta[YAW] -= IN_UpdateAxis (&in_move_yaw) * m_yaw; + delta[ROLL] -= IN_UpdateAxis (&in_move_roll) * m_pitch; + + ms->angles += delta; + + if (delta[PITCH]) { + V_StopPitchDrift (vs); + ms->angles[PITCH] = bound (cl_minpitch, ms->angles[PITCH], cl_maxpitch); + } + if (delta[ROLL]) { + ms->angles[ROLL] = bound (cl_minroll, ms->angles[ROLL], cl_maxroll); + } + if (delta[YAW]) { + ms->angles[YAW] = anglemod (ms->angles[YAW]); + } +} + +static const char default_input_config[] = { +#include "libs/client/default_input.plc" +}; + +static void +cl_bind_f (void) +{ + int c, i; + const char *key; + static dstring_t *cmd_buf; + + if (!cmd_buf) { + cmd_buf = dstring_newstr (); + } + + c = Cmd_Argc (); + + if (c < 2) { + Sys_Printf ("bind [command] : attach a command to a key\n"); + return; + } + + if (strcasecmp (Cmd_Argv (1), "ESCAPE") == 0) { + return; + } + + if (!(key = OK_TranslateKeyName (Cmd_Argv (1)))) { + return; + } + + dsprintf (cmd_buf, "in_bind imt_mod %s", key); + if (c >= 3) { + for (i = 2; i < c; i++) { + dasprintf (cmd_buf, " \"%s\"", Cmd_Argv (i)); + } + } + + Cmd_ExecuteString (cmd_buf->str, src_command); +} + +static void +cl_unbind_f (void) +{ + int c; + const char *key; + static dstring_t *cmd_buf; + + if (!cmd_buf) { + cmd_buf = dstring_newstr (); + } + + c = Cmd_Argc (); + + if (c < 2) { + Sys_Printf ("bind [command] : attach a command to a key\n"); + return; + } + + if (!(key = OK_TranslateKeyName (Cmd_Argv (1)))) { + return; + } + dsprintf (cmd_buf, "in_unbind imt_mod %s", key); + Cmd_ExecuteString (cmd_buf->str, src_command); +} + +static void +CL_Legacy_Init (void) +{ + OK_Init (); + Cmd_AddCommand ("bind", cl_bind_f, "compatibility wrapper for in_bind"); + Cmd_AddCommand ("unbind", cl_unbind_f, "compatibility wrapper for in_bind"); + plitem_t *cfg = PL_GetPropertyList (default_input_config, 0); + IN_LoadConfig (cfg); + PL_Release (cfg); +} + +void +CL_Input_BuildMove (float frametime, movestate_t *state, viewstate_t *vs) +{ + if (IN_ButtonReleased (&in_mlook) && !freelook && lookspring) { + V_StartPitchDrift (vs); + } + + CL_AdjustAngles (frametime, state, vs); + + vec4f_t move = {}; + + if (in_strafe.state & inb_down) { + move[SIDE] += cl_sidespeed * IN_ButtonState (&in_right); + move[SIDE] -= cl_sidespeed * IN_ButtonState (&in_left); + } + + move[SIDE] += cl_sidespeed * IN_ButtonState (&in_moveright); + move[SIDE] -= cl_sidespeed * IN_ButtonState (&in_moveleft); + + move[UP] += cl_upspeed * IN_ButtonState (&in_up); + move[UP] -= cl_upspeed * IN_ButtonState (&in_down); + + if (!(in_klook.state & inb_down)) { + move[FORWARD] += cl_forwardspeed * IN_ButtonState (&in_forward); + move[FORWARD] -= cl_backspeed * IN_ButtonState (&in_back); + } + + // adjust for speed key + if (in_speed.state & inb_down) { + move *= cl_movespeedkey; + } + + move[FORWARD] -= IN_UpdateAxis (&in_move_forward) * m_forward; + move[SIDE] += IN_UpdateAxis (&in_move_side) * m_side; + move[UP] -= IN_UpdateAxis (&in_move_up); + + if (freelook) + V_StopPitchDrift (vs); + + if (vs->chase + && (chase_active == 2 || chase_active == 3)) { + /* adjust for chase camera angles + * makes the player move relative to the chase camera frame rather + * than the player's frame + */ + chasestate_t *cs = vs->chasestate; + vec3_t forward, right, up, f, r; + vec3_t dir = {0, 0, 0}; + dir[1] = cs->camera_angles[1] - vs->player_angles[1]; + AngleVectors (dir, forward, right, up); + VectorScale (forward, move[FORWARD], f); + VectorScale (right, move[SIDE], r); + move[FORWARD] = f[0] + r[0]; + move[SIDE] = -f[1] - r[1]; + } + state->move = move; +} + +static void +cl_on_focus_change_redirect (void *_func, const int *game) +{ + void (*func) (int game) = _func; + func (*game); +} + +void +CL_OnFocusChange (void (*func) (int game)) +{ + LISTENER_ADD (&cl_on_focus_change, cl_on_focus_change_redirect, func); +} + +static int +cl_focus_event (const IE_event_t *ie_event) +{ + int game = ie_event->type == ie_gain_focus; + LISTENER_INVOKE (&cl_on_focus_change, &game); + return 1; +} + +static int +cl_key_event (const IE_event_t *ie_event) +{ + if (ie_event->key.code == QFK_ESCAPE) { + Con_SetState (con_menu); + return 1; + } + return 0; +} + +static int +cl_event_handler (const IE_event_t *ie_event, void *unused) +{ + static int (*handlers[ie_event_count]) (const IE_event_t *ie_event) = { + [ie_key] = cl_key_event, + [ie_gain_focus] = cl_focus_event, + [ie_lose_focus] = cl_focus_event, + }; + if ((unsigned) ie_event->type >= ie_event_count + || !handlers[ie_event->type]) { + return IN_Binding_HandleEvent (ie_event); + } + return handlers[ie_event->type] (ie_event); +} + +void +CL_Input_Init (cbuf_t *cbuf) +{ + cl_event_id = IE_Add_Handler (cl_event_handler, 0); + + for (int i = 0; cl_in_axes[i]; i++) { + IN_RegisterAxis (cl_in_axes[i]); + } + for (int i = 0; cl_in_buttons[i]; i++) { + IN_RegisterButton (cl_in_buttons[i]); + } + cl_game_context = IMT_CreateContext ("key_game"); + IMT_SetContextCbuf (cl_game_context, cbuf); + cl_demo_context = IMT_CreateContext ("key_demo"); + IMT_SetContextCbuf (cl_demo_context, cbuf); + CL_Legacy_Init (); +} + +void +CL_Input_Init_Cvars (void) +{ + Cvar_Register (&lookspring_cvar, 0, 0); + Cvar_Register (&m_pitch_cvar, 0, 0); + Cvar_Register (&m_yaw_cvar, 0, 0); + Cvar_Register (&m_forward_cvar, 0, 0); + Cvar_Register (&m_side_cvar, 0, 0); + Cvar_Register (&cl_anglespeedkey_cvar, 0, 0); + Cvar_Register (&cl_backspeed_cvar, 0, 0); + Cvar_Register (&cl_forwardspeed_cvar, 0, 0); + Cvar_Register (&cl_movespeedkey_cvar, 0, 0); + Cvar_Register (&cl_pitchspeed_cvar, 0, 0); + Cvar_Register (&cl_sidespeed_cvar, 0, 0); + Cvar_Register (&cl_upspeed_cvar, 0, 0); + Cvar_Register (&cl_yawspeed_cvar, 0, 0); + Cvar_Register (&cl_maxpitch_cvar, 0, 0); + Cvar_Register (&cl_minpitch_cvar, 0, 0); + Cvar_Register (&cl_maxroll_cvar, 0, 0); + Cvar_Register (&cl_minroll_cvar, 0, 0); +} + +void +CL_Input_Activate (int in_game) +{ + IMT_SetContext (!in_game ? cl_demo_context : cl_game_context); + IE_Set_Focus (cl_event_id); +} diff --git a/libs/client/cl_light.c b/libs/client/cl_light.c new file mode 100644 index 000000000..61c4df6b6 --- /dev/null +++ b/libs/client/cl_light.c @@ -0,0 +1,330 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "QF/dstring.h" +#include "QF/mathlib.h" +#include "QF/model.h" +#include "QF/plist.h" +#include "QF/progs.h" //for ED_ConvertToPlist +#include "QF/set.h" +#include "QF/scene/light.h" +#include "QF/scene/scene.h" +#include "QF/simd/vec4f.h" + +#include "client/world.h" + +static void +dump_light (light_t *light, int leaf) +{ + Sys_MaskPrintf (SYS_lighting, + "[%g, %g, %g] %g, " + "[%g, %g, %g, %g], [%g %g %g] %g, [%g, %g, %g, %g] %d\n", + VEC4_EXP (light->color), + VEC4_EXP (light->position), + VEC4_EXP (light->direction), + VEC4_EXP (light->attenuation), + leaf); +} + +static float +parse_float (const char *str, float defval) +{ + float val = defval; + if (str) { + char *end; + val = strtof (str, &end); + if (end == str) { + val = defval; + } + } + return val; +} + +static void +parse_vector (const char *str, vec_t *val) +{ + if (str) { + int num = sscanf (str, "%f %f %f", VectorExpandAddr (val)); + while (num < 3) { + val[num++] = 0; + } + } +} + +static float +ecos (float ang) +{ + if (ang == 90 || ang == -90) { + return 0; + } + if (ang == 180 || ang == -180) { + return -1; + } + if (ang == 0 || ang == 360) { + return 1; + } + return cos (ang * M_PI / 180); +} + +static float +esin (float ang) +{ + if (ang == 90) { + return 1; + } + if (ang == -90) { + return -1; + } + if (ang == 180 || ang == -180) { + return 0; + } + if (ang == 0 || ang == 360) { + return 0; + } + return sin (ang * M_PI / 180); +} + +static vec4f_t +sun_vector (const vec_t *ang) +{ + // ang is yaw, pitch (maybe roll, but ignored + // negative as the vector points *to* the sun, but ang specifies the + // direction from the sun + vec4f_t vec = { + -ecos (ang[1]) * ecos (ang[0]), + -ecos (ang[1]) * esin (ang[0]), + -esin (ang[1]), + 0, + }; + return vec; +} + +static void +parse_sun (lightingdata_t *ldata, plitem_t *entity) +{ + light_t light = {}; + float sunlight; + //float sunlight2; + vec3_t sunangle = { 0, -90, 0 }; + + Light_EnableSun (ldata); + sunlight = parse_float (PL_String (PL_ObjectForKey (entity, + "_sunlight")), 0); + //sunlight2 = parse_float (PL_String (PL_ObjectForKey (entity, + // "_sunlight2")), 0); + parse_vector (PL_String (PL_ObjectForKey (entity, "_sun_mangle")), + sunangle); + if (sunlight <= 0) { + return; + } + VectorSet (1, 1, 1, light.color); + light.color[3] = sunlight; + light.position = sun_vector (sunangle); + light.direction = light.position; + light.direction[3] = 1; + light.attenuation = (vec4f_t) { 0, 0, 1, 0 }; + Light_AddLight (ldata, &light, 0); +} + +static vec4f_t +parse_position (const char *str) +{ + vec3_t vec = {}; + sscanf (str, "%f %f %f", VectorExpandAddr (vec)); + return (vec4f_t) {vec[0], vec[1], vec[2], 1}; +} + +static void +parse_light (light_t *light, int *style, const plitem_t *entity, + const plitem_t *targets) +{ + const char *str; + int model = 0; + float atten = 1; + + /*Sys_Printf ("{\n"); + for (int i = PL_D_NumKeys (entity); i-- > 0; ) { + const char *field = PL_KeyAtIndex (entity, i); + const char *value = PL_String (PL_ObjectForKey (entity, field)); + Sys_Printf ("\t%s = %s\n", field, value); + } + Sys_Printf ("}\n");*/ + + // omnidirectional light (unit length xyz so not treated as ambient) + light->direction = (vec4f_t) { 0, 0, 1, 1 }; + // bright white + light->color = (vec4f_t) { 1, 1, 1, 300 }; + + if ((str = PL_String (PL_ObjectForKey (entity, "origin")))) { + light->position = parse_position (str); + } + + if ((str = PL_String (PL_ObjectForKey (entity, "target")))) { + plitem_t *target = PL_ObjectForKey (targets, str); + vec4f_t dir = { 1, 0, 0, 0 }; + if (target) { + if ((str = PL_String (PL_ObjectForKey (target, "origin")))) { + dir = parse_position (str); + dir = normalf (dir - light->position); + } + } + + float angle = 40; + if ((str = PL_String (PL_ObjectForKey (entity, "angle")))) { + angle = atof (str); + } + dir[3] = -cos (angle * M_PI / 360); // half angle + light->direction = dir; + } + + if ((str = PL_String (PL_ObjectForKey (entity, "light_lev"))) + || (str = PL_String (PL_ObjectForKey (entity, "_light")))) { + light->color[3] = atof (str); + } + + if ((str = PL_String (PL_ObjectForKey (entity, "style")))) { + *style = atoi (str) & 0x3f; + } + + if ((str = PL_String (PL_ObjectForKey (entity, "delay")))) { + model = atoi (str) & 0x7; + if (model == LM_INVERSE2) { + model = LM_INVERSE3; //FIXME for marcher (need a map) + } + } + + if ((str = PL_String (PL_ObjectForKey (entity, "color"))) + || (str = PL_String (PL_ObjectForKey (entity, "_color")))) { + union { + float a[4]; + vec4f_t v; + } color = { .v = light->color }; + sscanf (str, "%f %f %f", VectorExpandAddr (color.a)); + light->color = color.v; + if (light->color[0] > 1 || light->color[1] > 1 || light->color[2] > 1) { + VectorScale (light->color, 1/255.0, light->color); + } + } + + if ((str = PL_String (PL_ObjectForKey (entity, "wait")))) { + atten = atof (str); + if (atten <= 0) { + atten = 1; + } + } + + // The light's intensity is calculated as + // I = (1 - a.w * r.y) / dot (a, r) + // where a is attenuation and r = vec4 (d*d, d, 1, 0) + // thus giving linear falloff for a = vec4 (0, 0, 1, 1/maxdist) + // and 1/(A*d*d + B*d + C) for a = vec4 (A, B, C, 0) + // Other factors contribute to the final intensity (cone angle etc) + vec4f_t attenuation = { // inverse square + 1, 0, 0, + 0, + }; + switch (model) { + case LM_LINEAR: + attenuation = (vec4f_t) { + 0, 0, 1, + atten / fabsf (light->color[3]), + }; + break; + case LM_INVERSE: + attenuation = (vec4f_t) { + 0, atten / 128, 0, + 0, + }; + break; + case LM_INVERSE2: + attenuation = (vec4f_t) { + atten * atten / 16384, 0, 0, + 0, + }; + break; + case LM_INFINITE: + attenuation = (vec4f_t) { + 0, 0, 1, + 0, + }; + break; + case LM_AMBIENT: + attenuation = (vec4f_t) { + 0, 0, 1, + 0, + }; + light->direction = (vec4f_t) { 0, 0, 0, 1 }; + break; + case LM_INVERSE3: + attenuation = (vec4f_t) { + atten * atten / 16384, 2 * atten / 128, 1, + 0, + }; + break; + } + light->attenuation = attenuation; +} + +void +CL_LoadLights (plitem_t *entities, scene_t *scene) +{ + lightingdata_t *ldata = scene->lights; + model_t *model = scene->worldmodel; + + Light_ClearLights (ldata); + ldata->sun_pvs = set_new_size (model->brush.visleafs); + if (!entities) { + return; + } + + plitem_t *targets = PL_NewDictionary (0); + + // find all the targets so spotlights can be aimed + for (int i = 1; i < PL_A_NumObjects (entities); i++) { + plitem_t *entity = PL_ObjectAtIndex (entities, i); + const char *targetname = PL_String (PL_ObjectForKey (entity, + "targetname")); + if (targetname && !PL_ObjectForKey (targets, targetname)) { + PL_D_AddObject (targets, targetname, entity); + } + } + + for (int i = 0; i < PL_A_NumObjects (entities); i++) { + plitem_t *entity = PL_ObjectAtIndex (entities, i); + const char *classname = PL_String (PL_ObjectForKey (entity, + "classname")); + if (!classname) { + continue; + } + if (!strcmp (classname, "worldspawn")) { + // parse_sun can add many lights + parse_sun (ldata, entity); + const char *str; + if ((str = PL_String (PL_ObjectForKey (entity, "light_lev")))) { + light_t light = {}; + light.color = (vec4f_t) { 1, 1, 1, atof (str) }; + light.attenuation = (vec4f_t) { 0, 0, 1, 0 }; + light.direction = (vec4f_t) { 0, 0, 0, 1 }; + Light_AddLight (ldata, &light, 0); + } + } else if (!strncmp (classname, "light", 5)) { + light_t light = {}; + int style = 0; + + parse_light (&light, &style, entity, targets); + // some lights have 0 output, so drop them + if (light.color[3]) { + Light_AddLight (ldata, &light, style); + } + } + } + PL_Release (targets); + + for (size_t i = 0; i < ldata->lights.size; i++) { + dump_light (&ldata->lights.a[i], ldata->lightleafs.a[i]); + } + Sys_MaskPrintf (SYS_lighting, "loaded %zd lights\n", ldata->lights.size); +} diff --git a/libs/client/cl_particles.c b/libs/client/cl_particles.c new file mode 100644 index 000000000..249a14aaf --- /dev/null +++ b/libs/client/cl_particles.c @@ -0,0 +1,1301 @@ +/* + cl_particles.c + + OpenGL particle system. + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "QF/cmd.h" +#include "QF/cvar.h" +#include "QF/mersenne.h" +#include "QF/qargs.h" +#include "QF/quakefs.h" +#include "QF/render.h" +#include "QF/sys.h" +#include "QF/va.h" + +#include "QF/plugin/vid_render.h" //FIXME +#include "QF/scene/entity.h" + +#include "compat.h" + +#include "client/particles.h" + +float cl_frametime; +float cl_realtime; +cl_particle_funcs_t *clp_funcs; + +static mtstate_t mt; // private PRNG state +static psystem_t *cl_psystem; + +static int easter_eggs; +static cvar_t easter_eggs_cvar = { + .name = "easter_eggs", + .description = + "Enables easter eggs.", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &easter_eggs }, +}; +static int particles_style; +static cvar_t particles_style_cvar = { + .name = "particles_style", + .description = + "Sets particle style. 0 for Id, 1 for QF.", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &particles_style }, +}; + +static int ramp[] = { + /*ramp1*/ 0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61, + /*ramp2*/ 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66, + /*ramp3*/ 0x6d, 0x6b, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, +}; + +static partparm_t part_params[] = { + [pt_static] = {{0, 0, 0, 0}, 0, 1, 0, 0}, + [pt_grav] = {{0, 0, 0, 0.05}, 0, 1, 0, 0}, + [pt_slowgrav] = {{0, 0, 0, 0.05}, 0, 1, 0, 0}, + [pt_fire] = {{0, 0, 0, 0.05}, 5, 6, 0, 5./6}, + [pt_explode] = {{4, 4, 4, 0.05}, 10, 8, 0, 0}, + [pt_explode2] = {{1, 1, 1, 0.05}, 15, 8, 0, 0}, + [pt_blob] = {{4, 4, 4, 0.05}, 0, 1, 0, 0}, + [pt_blob2] = {{4, 4, 0, 0.05}, 0, 1, 0, 0}, + [pt_smoke] = {{0, 0, 0, 0}, 0, 1, 4, 0.4}, + [pt_smokecloud] = {{0, 0, 0, 0.0375}, 0, 1, 50, 0.55}, + [pt_bloodcloud] = {{0, 0, 0, 0.05}, 0, 1, 4, 0.25}, + [pt_fadespark] = {{0, 0, 0, 0}, 0, 1, 0, 0}, + [pt_fadespark2] = {{0, 0, 0, 0}, 0, 1, 0, 0}, + [pt_fallfade] = {{0, 0, 0, 1}, 0, 1, 0, 1}, + [pt_fallfadespark] = {{0, 0, 0, 1}, 15, 8, 0, 1}, + [pt_flame] = {{0, 0, 0, 0}, 0, 1, -2, 0.125}, +}; + +static const int *part_ramps[] = { + [pt_fire] = ramp + 2 * 8, // ramp3 + [pt_explode] = ramp + 0 * 8, // ramp1 + [pt_explode2] = ramp + 1 * 8, // ramp2 + [pt_fallfadespark] = ramp + 0 * 8, // ramp1 + [pt_flame] = 0, +}; + +static partparm_t __attribute__((pure)) +particle_params (ptype_t type) +{ + if (type > pt_flame) { + Sys_Error ("particle_params: invalid particle type"); + } + return part_params[type]; +} + +static const int * __attribute__((pure)) +particle_ramp (ptype_t type) +{ + if (type > pt_flame) { + Sys_Error ("particle_ramp: invalid particle type"); + } + return part_ramps[type]; +} + +inline static int +particle_new (ptype_t type, int texnum, vec4f_t pos, float scale, + vec4f_t vel, float live, int color, float alpha, float ramp) +{ + if (cl_psystem->numparticles >= cl_psystem->maxparticles) { + return 0; + } + + __auto_type ps = cl_psystem; + particle_t *p = &ps->particles[ps->numparticles]; + partparm_t *parm = &ps->partparams[ps->numparticles]; + const int **rampptr = &ps->partramps[ps->numparticles]; + ps->numparticles += 1; + + p->pos = pos; + p->vel = vel; + p->icolor = color; + p->alpha = alpha; + p->tex = texnum; + p->ramp = ramp; + p->scale = scale; + p->live = live; + + *parm = particle_params (type); + *rampptr = particle_ramp (type); + if (*rampptr) { + p->icolor = (*rampptr) [(int) p->ramp]; + } + return 1; +} + +/* + particle_new_random + + note that org_fuzz & vel_fuzz should be ints greater than 0 if you are + going to bother using this function. +*/ +inline static int +particle_new_random (ptype_t type, int texnum, vec4f_t org, int org_fuzz, + float scale, int vel_fuzz, float live, int color, + float alpha, float ramp) +{ + float o_fuzz = org_fuzz, v_fuzz = vel_fuzz; + int rnd; + vec4f_t porg, pvel; + + rnd = mtwist_rand (&mt); + porg[0] = o_fuzz * ((rnd & 63) - 31.5) / 63.0 + org[0]; + porg[1] = o_fuzz * (((rnd >> 6) & 63) - 31.5) / 63.0 + org[1]; + porg[2] = o_fuzz * (((rnd >> 10) & 63) - 31.5) / 63.0 + org[2]; + porg[3] = 1; + rnd = mtwist_rand (&mt); + pvel[0] = v_fuzz * ((rnd & 63) - 31.5) / 63.0; + pvel[1] = v_fuzz * (((rnd >> 6) & 63) - 31.5) / 63.0; + pvel[2] = v_fuzz * (((rnd >> 10) & 63) - 31.5) / 63.0; + pvel[3] = 0; + + return particle_new (type, texnum, porg, scale, pvel, live, color, alpha, + ramp); +} + +/* +inline static void +particle_new_veryrandom (ptype_t type, int texnum, vec4f_t org, + int org_fuzz, float scale, int vel_fuzz, float live, + int color, float alpha, float ramp) +{ + vec3_t porg, pvel; + + porg[0] = qfrandom (org_fuzz * 2) - org_fuzz + org[0]; + porg[1] = qfrandom (org_fuzz * 2) - org_fuzz + org[1]; + porg[2] = qfrandom (org_fuzz * 2) - org_fuzz + org[2]; + pvel[0] = qfrandom (vel_fuzz * 2) - vel_fuzz; + pvel[1] = qfrandom (vel_fuzz * 2) - vel_fuzz; + pvel[2] = qfrandom (vel_fuzz * 2) - vel_fuzz; + particle_new (type, texnum, porg, scale, pvel, live, color, alpha, ramp); +} +*/ + +static vec4f_t +roffs (int mod) +{ + vec4f_t offs = { + (mtwist_rand (&mt) % mod) - 0.5 * (mod - 1), + (mtwist_rand (&mt) % mod) - 0.5 * (mod - 1), + (mtwist_rand (&mt) % mod) - 0.5 * (mod - 1), + 0 + }; + return offs; +} + +static vec4f_t +tracer_vel (int tracercount, vec4f_t vec) +{ + if (tracercount & 1) { + return (vec4f_t) { vec[1], -vec[0], 0, 0 }; + } else { + return (vec4f_t) { -vec[1], vec[0], 0, 0 }; + } +} + +static void +add_particle (ptype_t type, vec4f_t pos, vec4f_t vel, float live, int color, + float ramp) +{ + particle_new (type, part_tex_dot, pos, 1, vel, live, color, 1, ramp); +} + +void +CL_LoadPointFile (const model_t *model) +{ + const char *name; + char *mapname; + int c; + QFile *f; + + mapname = strdup (model->path); + if (!mapname) + Sys_Error ("Can't duplicate mapname!"); + QFS_StripExtension (mapname, mapname); + + name = va (0, "%s.pts", mapname); + free (mapname); + + f = QFS_FOpenFile (name); + if (!f) { + Sys_Printf ("couldn't open %s\n", name); + return; + } + + Sys_MaskPrintf (SYS_dev, "Reading %s...\n", name); + c = 0; + vec4f_t zero = {}; + for (;;) { + char buf[64]; + union { + vec4f_t org; + vec3_t org3; + } o = { .org = { 0, 0, 0, 1 }}; + + Qgets (f, buf, sizeof (buf)); + int r = sscanf (buf, "%f %f %f\n", + &o.org3[0], &o.org3[1], &o.org3[2]); + if (r != 3) + break; + c++; + + if (!particle_new (pt_static, part_tex_dot, o.org, 1.5, zero, + 99999, (-c) & 15, 1.0, 0.0)) { + Sys_MaskPrintf (SYS_dev, "Not enough free particles\n"); + break; + } + } + Qclose (f); + Sys_MaskPrintf (SYS_dev, "%i points read\n", c); +} + +static void +CL_ParticleExplosion_QF (vec4f_t org) +{ +// CL_NewExplosion (org); + particle_new_random (pt_smokecloud, part_tex_smoke, org, 4, 30, 8, + 5.0, (mtwist_rand (&mt) & 7) + 8, + 0.5 + qfrandom (0.25), 0.0); +} + +static void +CL_ParticleExplosion2_QF (vec4f_t org, int colorStart, int colorLength) +{ + unsigned int i, j = 512; + + for (i = 0; i < j; i++) { + particle_new_random (pt_blob, part_tex_dot, org, 16, 2, 256, + 0.3, + colorStart + (i % colorLength), 1.0, 0.0); + } +} + +static void +CL_BlobExplosion_QF (vec4f_t org) +{ + unsigned int i; + unsigned int j = 1024; + + for (i = 0; i < j >> 1; i++) { + particle_new_random (pt_blob, part_tex_dot, org, 12, 2, 256, + 1.0 + (mtwist_rand (&mt) & 7) * 0.05, + 66 + i % 6, 1.0, 0.0); + } + for (i = 0; i < j / 2; i++) { + particle_new_random (pt_blob2, part_tex_dot, org, 12, 2, 256, + 1.0 + (mtwist_rand (&mt) & 7) * 0.05, + 150 + i % 6, 1.0, 0.0); + } +} + +static inline void +CL_RunSparkEffect_QF (vec4f_t org, int count, int ofuzz) +{ + vec4f_t zero = {}; + particle_new (pt_smokecloud, part_tex_smoke, org, ofuzz * 0.08, + zero, 9, 12 + (mtwist_rand (&mt) & 3), + 0.25 + qfrandom (0.125), 0.0); + + if (count > 0) { + int orgfuzz = ofuzz * 3 / 4; + if (orgfuzz < 1) + orgfuzz = 1; + + while (count--) { + int color = mtwist_rand (&mt) & 7; + + particle_new_random (pt_fallfadespark, part_tex_dot, org, orgfuzz, + 0.7, 96, 5.0, 0, 1.0, color); + } + } +} + +static inline void +CL_BloodPuff_QF (vec4f_t org, int count) +{ + vec4f_t zero = {}; + particle_new (pt_bloodcloud, part_tex_smoke, org, count / 5, zero, + 99.0, 70 + (mtwist_rand (&mt) & 3), 0.5, 0.0); +} + +static void +CL_BloodPuffEffect_QF (vec4f_t org, int count) +{ + CL_BloodPuff_QF (org, count); +} + +static void +CL_GunshotEffect_QF (vec4f_t org, int count) +{ + int scale = 16; + + scale += count / 15; + CL_RunSparkEffect_QF (org, count >> 1, scale); +} + +static void +CL_LightningBloodEffect_QF (vec4f_t org) +{ + CL_BloodPuff_QF (org, 50); + + vec4f_t zero = {}; + particle_new (pt_smokecloud, part_tex_smoke, org, 3.0, zero, + 9.0, 12 + (mtwist_rand (&mt) & 3), + 0.25 + qfrandom (0.125), 0.0); + + for (int count = 7; count-- > 0; ) { + particle_new_random (pt_fallfade, part_tex_spark, org, 12, 2.0, 128, + 5.0, 244 + (count % 3), 1.0, 0.0); + } +} + +static void +CL_RunParticleEffect_QF (vec4f_t org, vec4f_t dir, int color, int count) +{ + float scale = pow (count, 0.23); + + for (int i = 0; i < count; i++) { + int rnd = mtwist_rand (&mt); + + // Note that ParseParticleEffect handles (dir * 15) + particle_new (pt_grav, part_tex_dot, org + scale * roffs (16), 1.5, + dir, 0.1 * (i % 5), + (color & ~7) + (rnd & 7), 1.0, 0.0); + } +} + +static void +CL_SpikeEffect_QF (vec4f_t org) +{ + CL_RunSparkEffect_QF (org, 5, 8); +} + +static void +CL_SuperSpikeEffect_QF (vec4f_t org) +{ + CL_RunSparkEffect_QF (org, 10, 8); +} + +static void +CL_KnightSpikeEffect_QF (vec4f_t org) +{ + vec4f_t zero = {}; + particle_new (pt_smokecloud, part_tex_smoke, org, 1.0, zero, + 9.0, 234, 0.25 + qfrandom (0.125), 0.0); + + for (int count = 10; count-- > 0; ) { + particle_new_random (pt_fallfade, part_tex_dot, org, 6, 0.7, 96, + 5.0, 234, 1.0, 0.0); + } +} + +static void +CL_WizSpikeEffect_QF (vec4f_t org) +{ + vec4f_t zero = {}; + particle_new (pt_smokecloud, part_tex_smoke, org, 2.0, zero, + 9.0, 63, 0.25 + qfrandom (0.125), 0.0); + + for (int count = 15; count-- > 0; ) { + particle_new_random (pt_fallfade, part_tex_dot, org, 12, 0.7, 96, + 5.0, 63, 1.0, 0.0); + } +} + +static void +CL_LavaSplash_QF (vec4f_t org) +{ + for (int i = -16; i < 16; i++) { + for (int j = -16; j < 16; j++) { + uint32_t rnd = mtwist_rand (&mt); + float vel = 50.0 + 0.5 * (mtwist_rand (&mt) & 127); + vec4f_t dir = { + j * 8 + (rnd & 7), + i * 8 + ((rnd >> 6) & 7), + 256, + 0 + }; + vec4f_t offs = { dir[0], dir[1], ((rnd >> 9) & 63), 0 }; + dir = normalf (dir); + particle_new (pt_grav, part_tex_dot, org + offs, 3, vel * dir, + 2.0 + ((rnd >> 7) & 31) * 0.02, + 224 + ((rnd >> 12) & 7), 0.75, 0.0); + } + } +} + +static void +CL_TeleportSplash_QF (vec4f_t org) +{ + for (int k = -24; k < 32; k += 4) { + for (int i = -16; i < 16; i += 4) { + for (int j = -16; j < 16; j += 4) { + uint32_t rnd = mtwist_rand (&mt); + float vel = 50 + ((rnd >> 6) & 63); + vec4f_t dir = normalf ((vec4f_t) { j, i, k, 0 } * 8); + vec4f_t offs = { + i + (rnd & 3), + j + ((rnd >> 2) & 3), + k + ((rnd >> 4) & 3), + 0 + }; + particle_new (pt_grav, part_tex_spark, org + offs, 0.6, + vel * dir, + (0.2 + (mtwist_rand (&mt) & 15) * 0.01), + (7 + ((rnd >> 12) & 7)), 1.0, 0.0); + } + } + } +} + +static void +CL_RocketTrail_QF (vec4f_t start, vec4f_t end) +{ + vec4f_t vec = end - start; + float maxlen = magnitudef (vec)[0]; + vec = normalf (vec); + + float origlen = cl_frametime / maxlen; + vec4f_t step = (maxlen - 3) * vec; + + float len = 0; + vec4f_t zero = {}; + vec4f_t pos = start; + float pscale = 1.5 + qfrandom (1.5); + + while (len < maxlen) { + float pscalenext = 1.5 + qfrandom (1.5); + float dist = (pscale + pscalenext) * 3.0; + float percent = len * origlen; + + particle_new (pt_smoke, part_tex_smoke, pos, + pscale + percent * 4.0, zero, + 2.0 - percent * 2.0, + 12 + (mtwist_rand (&mt) & 3), + 0.5 + qfrandom (0.125) - percent * 0.40, 0.0); + len += dist; + pos += step; + pscale = pscalenext; + } +} + +static void +CL_GrenadeTrail_QF (vec4f_t start, vec4f_t end) +{ + vec4f_t vec = end - start; + float maxlen = magnitudef (vec)[0]; + vec = normalf (vec); + + float origlen = cl_frametime / maxlen; + vec4f_t step = (maxlen - 3) * vec; + + float len = 0; + vec4f_t zero = {}; + vec4f_t pos = start; + float pscale = 6.0 + qfrandom (7.0); + + while (len < maxlen) { + float pscalenext = 6.0 + qfrandom (7.0); + float dist = (pscale + pscalenext) * 2.0; + float percent = len * origlen; + + particle_new (pt_smoke, part_tex_smoke, pos, + pscale + percent * 4.0, zero, + 2.0 - percent * 2.0, + 1 + (mtwist_rand (&mt) & 3), + 0.625 + qfrandom (0.125) - percent * 0.40, 0.0); + len += dist; + pos += step; + pscale = pscalenext; + } +} + +static void +CL_BloodTrail_QF (vec4f_t start, vec4f_t end) +{ + vec4f_t vec = end - start; + float maxlen = magnitudef (vec)[0]; + vec = normalf (vec); + + float origlen = cl_frametime / maxlen; + vec4f_t step = (maxlen - 3) * vec; + + float len = 0; + vec4f_t pos = start; + float pscale = 5.0 + qfrandom (10.0); + + while (len < maxlen) { + float pscalenext = 5.0 + qfrandom (10.0); + float dist = (pscale + pscalenext) * 1.5; + float percent = len * origlen; + vec4f_t vel = roffs (24); + vel[2] -= percent * 40; + + particle_new (pt_grav, part_tex_smoke, pos + roffs (4), pscale, vel, + 2.0 - percent * 2.0, + 68 + (mtwist_rand (&mt) & 3), 1.0, 0.0); + len += dist; + pos += step; + pscale = pscalenext; + } +} + +static void +CL_SlightBloodTrail_QF (vec4f_t start, vec4f_t end) +{ + vec4f_t vec = end - start; + float maxlen = magnitudef (vec)[0]; + vec = normalf (vec); + + float origlen = cl_frametime / maxlen; + vec4f_t step = (maxlen - 3) * vec; + + float len = 0; + vec4f_t pos = start; + float pscale = 1.5 + qfrandom (7.5); + while (len < maxlen) { + float pscalenext = 1.5 + qfrandom (7.5); + float dist = (pscale + pscalenext) * 1.5; + float percent = len * origlen; + vec4f_t vel = roffs (12); + vel[2] -= percent * 40; + + particle_new (pt_grav, part_tex_smoke, pos + roffs (4), pscale, vel, + 1.5 - percent * 1.5, + 68 + (mtwist_rand (&mt) & 3), 0.75, 0.0); + len += dist; + pos += step; + pscale = pscalenext; + } +} + +static void +CL_WizTrail_QF (vec4f_t start, vec4f_t end) +{ + float dist = 3.0; + + vec4f_t vec = end - start; + float maxlen = magnitudef (vec)[0]; + vec = normalf (vec); + + float origlen = cl_frametime / maxlen; + vec4f_t step = (maxlen - dist) * vec; + + float len = 0; + vec4f_t pos = start; + while (len < maxlen) { + static int tracercount; + float percent = len * origlen; + + particle_new (pt_flame, part_tex_smoke, pos, + 2.0 + qfrandom (1.0) - percent * 2.0, + 30 * tracer_vel (tracercount++, vec), + 0.5 - percent * 0.5, + 52 + (mtwist_rand (&mt) & 4), 1.0 - percent * 0.125, 0.0); + len += dist; + pos += step; + } +} + +static void +CL_FlameTrail_QF (vec4f_t start, vec4f_t end) +{ + float dist = 3.0; + + vec4f_t vec = end - start; + float maxlen = magnitudef (vec)[0]; + vec = normalf (vec); + + float origlen = cl_frametime / maxlen; + vec4f_t step = (maxlen - dist) * vec; + + float len = 0; + vec4f_t pos = start; + while (len < maxlen) { + static int tracercount; + float percent = len * origlen; + + particle_new (pt_flame, part_tex_smoke, pos, + 2.0 + qfrandom (1.0) - percent * 2.0, + 30 * tracer_vel (tracercount++, vec), + 0.5 - percent * 0.5, 234, + 1.0 - percent * 0.125, 0.0); + len += dist; + pos += step; + } +} + +static void +CL_VoorTrail_QF (vec4f_t start, vec4f_t end) +{ + float dist = 3.0; + + vec4f_t vec = end - start; + float maxlen = magnitudef (vec)[0]; + vec = normalf (vec); + + float origlen = cl_frametime / maxlen; + vec4f_t step = (maxlen - dist) * vec; + + float len = 0; + vec4f_t zero = {}; + vec4f_t pos = start; + while (len < maxlen) { + float percent = len * origlen; + + particle_new (pt_static, part_tex_dot, pos + roffs (16), + 1.0 + qfrandom (1.0), + zero, 0.3 - percent * 0.3, + 9 * 16 + 8 + (mtwist_rand (&mt) & 3), 1.0, 0.0); + len += dist; + pos += step; + } +} + +static void +CL_GlowTrail_QF (vec4f_t start, vec4f_t end, int glow_color) +{ + float dist = 3.0; + + vec4f_t vec = end - start; + float maxlen = magnitudef (vec)[0]; + vec = normalf (vec); + + float origlen = cl_frametime / maxlen; + vec4f_t step = (maxlen - dist) * vec; + + float len = 0; + vec4f_t zero = {}; + vec4f_t pos = start; + while (len < maxlen) { + float percent = len * origlen; + + particle_new (pt_smoke, part_tex_dot, pos + roffs (5), 1.0, zero, + 2.0 - percent * 0.2, glow_color, 1.0, 0.0); + len += dist; + pos += step; + } +} + +static void +CL_ParticleExplosion_EE (vec4f_t org) +{ +/* + CL_NewExplosion (org); +*/ + particle_new_random (pt_smokecloud, part_tex_smoke, org, 4, 30, 8, + 5.0, mtwist_rand (&mt) & 255, + 0.5 + qfrandom (0.25), 0.0); +} + +static void +CL_TeleportSplash_EE (vec4f_t org) +{ + for (int k = -24; k < 32; k += 4) { + for (int i = -16; i < 16; i += 4) { + for (int j = -16; j < 16; j += 4) { + uint32_t rnd = mtwist_rand (&mt); + float vel = 50 + ((rnd >> 6) & 63); + vec4f_t dir = normalf ((vec4f_t) { j, i, k, 0 } * 8); + vec4f_t offs = { + i + (rnd & 3), + j + ((rnd >> 2) & 3), + k + ((rnd >> 4) & 3), + 0 + }; + particle_new (pt_grav, part_tex_spark, org + offs, 0.6, + vel * dir, + (0.2 + (mtwist_rand (&mt) & 15) * 0.01), + qfrandom (1.0), 1.0, 0.0); + } + } + } +} + +static void +CL_RocketTrail_EE (vec4f_t start, vec4f_t end) +{ + vec4f_t vec = end - start; + float maxlen = magnitudef (vec)[0]; + vec = normalf (vec); + + float origlen = cl_frametime / maxlen; + float pscale = 1.5 + qfrandom (1.5); + + float len = 0; + vec4f_t zero = {}; + vec4f_t pos = start; + while (len < maxlen) { + float pscalenext = 1.5 + qfrandom (1.5); + float dist = (pscale + pscalenext) * 3.0; + float percent = len * origlen; + + particle_new (pt_smoke, part_tex_smoke, pos, + pscale + percent * 4.0, zero, + 2.0 - percent * 2.0, + mtwist_rand (&mt) & 255, + 0.5 + qfrandom (0.125) - percent * 0.40, 0.0); + len += dist; + pos += len * vec; + pscale = pscalenext; + } +} + +static void +CL_GrenadeTrail_EE (vec4f_t start, vec4f_t end) +{ + vec4f_t vec = end - start; + float maxlen = magnitudef (vec)[0]; + vec = normalf (vec); + + float origlen = cl_frametime / maxlen; + float pscale = 6.0 + qfrandom (7.0); + + float len = 0; + vec4f_t zero = {}; + vec4f_t pos = start; + while (len < maxlen) { + float pscalenext = 6.0 + qfrandom (7.0); + float dist = (pscale + pscalenext) * 2.0; + float percent = len * origlen; + + particle_new (pt_smoke, part_tex_smoke, pos, + pscale + percent * 4.0, zero, + 2.0 - percent * 2.0, + mtwist_rand (&mt) & 255, + 0.625 + qfrandom (0.125) - percent * 0.40, 0.0); + len += dist; + pos += len * vec; + pscale = pscalenext; + } +} + +static void +CL_ParticleExplosion_ID (vec4f_t org) +{ + for (int i = 0; i < 1024; i++) { + ptype_t type = i & 1 ? pt_explode2 : pt_explode; + add_particle (type, org + roffs (32), roffs (512), 5, + 0, mtwist_rand (&mt) & 3); + } +} + +static void +CL_BlobExplosion_ID (vec4f_t org) +{ + for (int i = 0; i < 1024; i++) { + ptype_t type = i & 1 ? pt_blob : pt_blob2; + int color = i & 1 ? 66 : 150; + add_particle (type, org + roffs (32), roffs (512), + color + mtwist_rand (&mt) % 6, + (color & ~7) + (mtwist_rand (&mt) & 7), 0); + } +} + +static inline void // FIXME: inline? +CL_RunParticleEffect_ID (vec4f_t org, vec4f_t dir, int color, int count) +{ + for (int i = 0; i < count; i++) { + add_particle (pt_slowgrav, org + roffs (16), + dir/* + roffs (300)*/, + 0.1 * (mtwist_rand (&mt) % 5), + (color & ~7) + (mtwist_rand (&mt) & 7), 0); + } +} + +static void +CL_BloodPuffEffect_ID (vec4f_t org, int count) +{ + vec4f_t zero = {}; + CL_RunParticleEffect_ID (org, zero, 73, count); +} + +static void +CL_GunshotEffect_ID (vec4f_t org, int count) +{ + vec4f_t zero = {}; + CL_RunParticleEffect_ID (org, zero, 0, count); +} + +static void +CL_LightningBloodEffect_ID (vec4f_t org) +{ + vec4f_t zero = {}; + CL_RunParticleEffect_ID (org, zero, 225, 50); +} + +static void +CL_SpikeEffect_ID (vec4f_t org) +{ + vec4f_t zero = {}; + CL_RunParticleEffect_ID (org, zero, 0, 10); +} + +static void +CL_SuperSpikeEffect_ID (vec4f_t org) +{ + vec4f_t zero = {}; + CL_RunParticleEffect_ID (org, zero, 0, 20); +} + +static void +CL_KnightSpikeEffect_ID (vec4f_t org) +{ + vec4f_t zero = {}; + CL_RunParticleEffect_ID (org, zero, 226, 20); +} + +static void +CL_WizSpikeEffect_ID (vec4f_t org) +{ + vec4f_t zero = {}; + CL_RunParticleEffect_ID (org, zero, 20, 30); +} + +static void +CL_LavaSplash_ID (vec4f_t org) +{ + for (int i = -16; i < 16; i++) { + for (int j = -16; j < 16; j++) { + for (int k = 0; k < 1; k++) { + float vel = 50 + (mtwist_rand (&mt) & 63); + vec4f_t dir = { + j * 8 + (mtwist_rand (&mt) & 7), + i * 8 + (mtwist_rand (&mt) & 7), + 256, + 0 + }; + vec4f_t offs = { + dir[0], + dir[1], + (mtwist_rand (&mt) & 63), + 0 + }; + dir = normalf (dir); + add_particle (pt_grav, org + offs, vel * dir, + 2 + (mtwist_rand (&mt) & 31) * 0.02, + 224 + (mtwist_rand (&mt) & 7), 0); + } + } + } +} + +static void +CL_TeleportSplash_ID (vec4f_t org) +{ + for (int i = -16; i < 16; i += 4) { + for (int j = -16; j < 16; j += 4) { + for (int k = -24; k < 32; k += 4) { + float vel = 50 + (mtwist_rand (&mt) & 63); + vec4f_t dir = normalf ((vec4f_t) { j, i, k, 0 } * 8); + vec4f_t offs = { + i + (mtwist_rand (&mt) & 3), + j + (mtwist_rand (&mt) & 3), + k + (mtwist_rand (&mt) & 3), + 0 + }; + add_particle (pt_grav, org + offs, vel * dir, + 0.2 + (mtwist_rand (&mt) & 7) * 0.02, + 7 + (mtwist_rand (&mt) & 7), 0); + } + } + } +} + +static void +CL_DarkFieldParticles_ID (vec4f_t org) +{ + for (int i = -16; i < 16; i += 8) { + for (int j = -16; j < 16; j += 8) { + for (int k = 0; k < 32; k += 8) { + uint32_t rnd = mtwist_rand (&mt); + float vel = 50 + ((rnd >> 9) & 63); + vec4f_t dir = normalf ((vec4f_t) { j, i, k, 0 } * 8); + vec4f_t offs = { + i + ((rnd >> 3) & 3), + j + ((rnd >> 5) & 3), + k + ((rnd >> 7) & 3), + 0 + }; + + add_particle (pt_slowgrav, org + offs, vel * dir, + 0.2 + (rnd & 7) * 0.02, + 150 + mtwist_rand (&mt) % 6, 0); + } + } + } +} + +#define num_normals (int)(sizeof (normals) / sizeof (normals[0])) +static vec4f_t normals[] = { +#include "anorms.h" +}; +static vec4f_t velocities[num_normals]; + +static void +CL_EntityParticles_ID (vec4f_t org) +{ + float angle, sp, sy, cp, cy; // cr, sr + float beamlength = 16.0, dist = 64.0; + + for (int i = 0; i < num_normals; i++) { + int k; + for (k = 0; k < 3; k++) { + velocities[i][k] = (mtwist_rand (&mt) & 255) * 0.01; + } + } + + vec4f_t zero = {}; + for (int i = 0; i < num_normals; i++) { + angle = cl_realtime * velocities[i][0]; + cy = cos (angle); + sy = sin (angle); + angle = cl_realtime * velocities[i][1]; + cp = cos (angle); + sp = sin (angle); +// Next 3 lines results aren't currently used, may be in future. --Despair +// angle = cl_realtime * avelocities[i][2]; +// sr = sin (angle); +// cr = cos (angle); + + vec4f_t forward = { cp * cy, cp * sy, -sp, 0 }; + vec4f_t pos = org + normals[i] * dist + forward * beamlength; + //FIXME 0 velocity? + add_particle (pt_explode, pos, zero, 0.01, 0x6f, 0); + } +} + +static void +CL_RocketTrail_ID (vec4f_t start, vec4f_t end) +{ + vec4f_t vec = end - start; + float len = magnitudef (vec)[0]; + vec = normalf (vec); + + vec4f_t zero = {}; + vec4f_t pos = start; + while (len > 0) { + len -= 3; + add_particle (pt_fire, pos + roffs (6), zero, 2, + 0, (mtwist_rand (&mt) & 3)); + pos += vec; + } +} + +static void +CL_GrenadeTrail_ID (vec4f_t start, vec4f_t end) +{ + vec4f_t vec = end - start; + float len = magnitudef (vec)[0]; + vec = normalf (vec); + + vec4f_t zero = {}; + vec4f_t pos = start; + while (len > 0) { + len -= 3; + add_particle (pt_fire, pos + roffs (6), zero, 2, + 0, (mtwist_rand (&mt) & 3) + 2); + pos += vec; + } +} + +static void +CL_BloodTrail_ID (vec4f_t start, vec4f_t end) +{ + vec4f_t vec = end - start; + float len = magnitudef (vec)[0]; + vec = normalf (vec); + + vec4f_t zero = {}; + vec4f_t pos = start; + while (len > 0) { + len -= 3; + add_particle (pt_slowgrav, pos + roffs (6), zero, 2, + 67 + (mtwist_rand (&mt) & 3), 0); + pos += vec; + } +} + +static void +CL_SlightBloodTrail_ID (vec4f_t start, vec4f_t end) +{ + vec4f_t vec = end - start; + float len = magnitudef (vec)[0]; + vec = normalf (vec); + + vec4f_t zero = {}; + vec4f_t pos = start; + while (len > 0) { + len -= 6; + add_particle (pt_slowgrav, pos + roffs (6), zero, 2, + 67 + (mtwist_rand (&mt) & 3), 0); + pos += vec; + } +} + +static void +CL_WizTrail_ID (vec4f_t start, vec4f_t end) +{ + vec4f_t vec = end - start; + float len = magnitudef (vec)[0]; + vec = normalf (vec); + + vec4f_t pos = start; + while (len > 0) { + static int tracercount; + len -= 3; + add_particle (pt_static, pos, 30 * tracer_vel (tracercount, vec), 0.5, + 52 + ((tracercount & 4) << 1), 0); + tracercount++; + pos += vec; + } +} + +static void +CL_FlameTrail_ID (vec4f_t start, vec4f_t end) +{ + vec4f_t vec = end - start; + float len = magnitudef (vec)[0]; + vec = normalf (vec); + + vec4f_t pos = start; + while (len > 0) { + static int tracercount; + len -= 3; + add_particle (pt_static, pos, 30 * tracer_vel (tracercount, vec), 0.5, + 230 + ((tracercount & 4) << 1), 0); + tracercount++; + pos += vec; + } +} + +static void +CL_VoorTrail_ID (vec4f_t start, vec4f_t end) +{ + vec4f_t vec = end - start; + float len = magnitudef (vec)[0]; + vec = normalf (vec); + + vec4f_t zero = {}; + vec4f_t pos = start; + while (len > 0) { + len -= 3; + add_particle (pt_static, pos + roffs (16), zero, 0.3, + 9 * 16 + 8 + (mtwist_rand (&mt) & 3), 0); + pos += vec; + } +} + +static void +CL_Particle_New (ptype_t type, int texnum, vec4f_t org, float scale, + vec4f_t vel, float live, int color, float alpha, + float ramp) +{ + particle_new (type, texnum, org, scale, vel, live, color, alpha, ramp); +} + +static void +CL_Particle_NewRandom (ptype_t type, int texnum, vec4f_t org, + int org_fuzz, float scale, int vel_fuzz, float live, + int color, float alpha, float ramp) +{ + particle_new_random (type, texnum, org, org_fuzz, scale, vel_fuzz, live, + color, alpha, ramp); +} + +static cl_particle_funcs_t particles_QF = { + CL_RocketTrail_QF, + CL_GrenadeTrail_QF, + CL_BloodTrail_QF, + CL_SlightBloodTrail_QF, + CL_WizTrail_QF, + CL_FlameTrail_QF, + CL_VoorTrail_QF, + CL_GlowTrail_QF, + CL_RunParticleEffect_QF, + CL_BloodPuffEffect_QF, + CL_GunshotEffect_QF, + CL_LightningBloodEffect_QF, + CL_SpikeEffect_QF, + CL_KnightSpikeEffect_QF, + CL_SuperSpikeEffect_QF, + CL_WizSpikeEffect_QF, + CL_BlobExplosion_QF, + CL_ParticleExplosion_QF, + CL_ParticleExplosion2_QF, + CL_LavaSplash_QF, + CL_TeleportSplash_QF, + CL_DarkFieldParticles_ID, + CL_EntityParticles_ID, + CL_Particle_New, + CL_Particle_NewRandom, +}; + +static cl_particle_funcs_t particles_ID = { + CL_RocketTrail_ID, + CL_GrenadeTrail_ID, + CL_BloodTrail_ID, + CL_SlightBloodTrail_ID, + CL_WizTrail_ID, + CL_FlameTrail_ID, + CL_VoorTrail_ID, + CL_GlowTrail_QF, + CL_RunParticleEffect_ID, + CL_BloodPuffEffect_ID, + CL_GunshotEffect_ID, + CL_LightningBloodEffect_ID, + CL_SpikeEffect_ID, + CL_KnightSpikeEffect_ID, + CL_SuperSpikeEffect_ID, + CL_WizSpikeEffect_ID, + CL_BlobExplosion_ID, + CL_ParticleExplosion_ID, + CL_ParticleExplosion2_QF, + CL_LavaSplash_ID, + CL_TeleportSplash_ID, + CL_DarkFieldParticles_ID, + CL_EntityParticles_ID, + CL_Particle_New, + CL_Particle_NewRandom, +}; + +static cl_particle_funcs_t particles_QF_egg = { + CL_RocketTrail_EE, + CL_GrenadeTrail_EE, + CL_BloodTrail_QF, + CL_SlightBloodTrail_QF, + CL_WizTrail_QF, + CL_FlameTrail_QF, + CL_VoorTrail_QF, + CL_GlowTrail_QF, + CL_RunParticleEffect_QF, + CL_BloodPuffEffect_QF, + CL_GunshotEffect_QF, + CL_LightningBloodEffect_QF, + CL_SpikeEffect_QF, + CL_KnightSpikeEffect_QF, + CL_SuperSpikeEffect_QF, + CL_WizSpikeEffect_QF, + CL_BlobExplosion_QF, + CL_ParticleExplosion_EE, + CL_ParticleExplosion2_QF, + CL_LavaSplash_QF, + CL_TeleportSplash_EE, + CL_DarkFieldParticles_ID, + CL_EntityParticles_ID, + CL_Particle_New, + CL_Particle_NewRandom, +}; + +static cl_particle_funcs_t particles_ID_egg = { + CL_RocketTrail_EE, + CL_GrenadeTrail_EE, + CL_BloodTrail_ID, + CL_SlightBloodTrail_ID, + CL_WizTrail_ID, + CL_FlameTrail_ID, + CL_VoorTrail_ID, + CL_GlowTrail_QF, + CL_RunParticleEffect_ID, + CL_BloodPuffEffect_ID, + CL_GunshotEffect_ID, + CL_LightningBloodEffect_ID, + CL_SpikeEffect_ID, + CL_KnightSpikeEffect_ID, + CL_SuperSpikeEffect_ID, + CL_WizSpikeEffect_ID, + CL_BlobExplosion_ID, + CL_ParticleExplosion_EE, + CL_ParticleExplosion2_QF, + CL_LavaSplash_ID, + CL_TeleportSplash_EE, + CL_DarkFieldParticles_ID, + CL_EntityParticles_ID, + CL_Particle_New, + CL_Particle_NewRandom, +}; + +static void +set_particle_funcs (void) +{ + if (easter_eggs) { + if (particles_style) { + clp_funcs = &particles_QF_egg; + } else { + clp_funcs = &particles_ID_egg; + } + } else { + if (particles_style) { + clp_funcs = &particles_QF; + } else { + clp_funcs = &particles_ID; + } + } +} + +static void +easter_eggs_f (void *data, const cvar_t *cvar) +{ + set_particle_funcs (); +} + +static void +particles_style_f (void *data, const cvar_t *cvar) +{ + set_particle_funcs (); + cl_psystem->points_only = !particles_style; +} + +void +CL_Particles_Init (void) +{ + mtwist_seed (&mt, 0xdeadbeef); + cl_psystem = r_funcs->ParticleSystem (); + Cvar_Register (&easter_eggs_cvar, easter_eggs_f, 0); + Cvar_Register (&particles_style_cvar, particles_style_f, 0); + set_particle_funcs (); +} + +void +CL_ParticlesGravity (float gravity) +{ + cl_psystem->gravity = (vec4f_t) { 0, 0, -gravity, 0 }; +} diff --git a/libs/client/cl_screen.c b/libs/client/cl_screen.c new file mode 100644 index 000000000..4ae6e5296 --- /dev/null +++ b/libs/client/cl_screen.c @@ -0,0 +1,310 @@ +/* + cl_screen.c + + master for refresh, status bar, console, chat, notify, etc + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "QF/console.h" +#include "QF/cvar.h" +#include "QF/draw.h" +#include "QF/image.h" +#include "QF/pcx.h" +#include "QF/screen.h" + +#include "QF/plugin/console.h" +#include "QF/plugin/general.h" +#include "QF/plugin/vid_render.h" + +#include "QF/scene/scene.h" +#include "QF/scene/transform.h" +#include "QF/ui/canvas.h" + +#include "r_local.h" //FIXME for r_cache_thrash + +#include "client/hud.h" +#include "client/sbar.h" +#include "client/screen.h" +#include "client/view.h" +#include "client/world.h" + +//#include "nq/include/client.h" + +int scr_showpause; +static cvar_t scr_showpause_cvar = { + .name = "showpause", + .description = + "Toggles display of pause graphic", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &scr_showpause }, +}; +int scr_showram; +static cvar_t scr_showram_cvar = { + .name = "showram", + .description = + "Show RAM icon if game is running low on memory", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &scr_showram }, +}; +int scr_showturtle; +static cvar_t scr_showturtle_cvar = { + .name = "showturtle", + .description = + "Show a turtle icon if your fps is below 10", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &scr_showturtle }, +}; + +view_t cl_screen_view; +uint32_t cl_canvas; +static view_t net_view; +static view_t timegraph_view; +static view_t zgraph_view; +static view_t loading_view; +static view_t ram_view; +static view_t turtle_view; +static view_t pause_view; + +static viewstate_t *_vs;//FIXME ick + +canvas_system_t cl_canvas_sys; + +static void +SCR_CShift (void) +{ + mleaf_t *leaf; + int contents = CONTENTS_EMPTY; + + if (_vs->active && cl_world.scene->worldmodel) { + vec4f_t origin; + origin = Transform_GetWorldPosition (_vs->camera_transform); + leaf = Mod_PointInLeaf (origin, cl_world.scene->worldmodel); + contents = leaf->contents; + } + V_SetContentsColor (_vs, contents); + r_funcs->Draw_BlendScreen (_vs->cshift_color); +} + +static void +scr_draw_views (void) +{ + if (scr_showturtle) { + static int count; + if (r_data->frametime < 0.1) { + count = 0; + } else { + count++; + } + View_SetVisible (turtle_view, count > 2); + } + + // turn off for screenshots + View_SetVisible (pause_view, scr_showpause && r_data->paused); + + View_SetVisible (ram_view, scr_showram && r_cache_thrash); + double msg_time = _vs->realtime - _vs->last_servermessage; + View_SetVisible (net_view, (!_vs->demoplayback && msg_time >= 0.3)); + View_SetVisible (loading_view, _vs->loading); + // FIXME cvar callbacks + View_SetVisible (timegraph_view, r_timegraph); + View_SetVisible (zgraph_view, r_zgraph); + + if (!_vs->intermission) { + r_funcs->Draw_Crosshair ();//FIXME canvas_func + } + Con_DrawConsole (); + Canvas_Draw (cl_canvas_sys); + SCR_CShift ();//FIXME canvas_func + Sbar_DrawCenterPrint ();//FIXME canvas_func +} + +static SCR_Func scr_funcs[] = { + scr_draw_views, + 0 +}; + +static int cl_scale; +static int cl_xlen; +static int cl_ylen; + +static void +cl_set_size (void) +{ + int xlen = cl_xlen / cl_scale; + int ylen = cl_ylen / cl_scale; + printf ("cl_set_size: %d %d %d\n", cl_scale, xlen, ylen); + //View_SetLen (cl_screen_view, xlen, ylen); + //View_UpdateHierarchy (cl_screen_view); + Canvas_SetLen (cl_canvas_sys, (view_pos_t) { xlen, ylen }); +} + +static void +cl_scale_listener (void *data, const cvar_t *cvar) +{ + cl_scale = *(int *) cvar->value.value; + cl_set_size (); +} + +static void +cl_vidsize_listener (void *data, const viddef_t *vdef) +{ + cl_xlen = vdef->width; + cl_ylen = vdef->height; + cl_set_size (); +} + +void +CL_Init_Screen (void) +{ + qpic_t *pic; + + __auto_type reg = ECS_NewRegistry (); + Canvas_InitSys (&cl_canvas_sys, reg); + if (con_module) { + __auto_type cd = con_module->data->console; + cd->component_base = ECS_RegisterComponents (reg, cd->components, + cd->num_components); + cd->canvas_sys = &cl_canvas_sys; + } + HUD_Init (reg); + ECS_CreateComponentPools (reg); + + cl_xlen = viddef.width; + cl_ylen = viddef.height; + + ecs_system_t vsys = { + .reg = reg, + .base = cl_canvas_sys.view_base, + }; + + HUD_CreateCanvas (cl_canvas_sys); + + cl_canvas = Canvas_New (cl_canvas_sys); + cl_screen_view = Canvas_GetRootView (cl_canvas_sys, cl_canvas); + + View_SetPos (cl_screen_view, 0, 0); + View_SetLen (cl_screen_view, cl_xlen, cl_ylen); + View_SetGravity (cl_screen_view, grav_northwest); + View_SetVisible (cl_screen_view, 1); + + pic = r_funcs->Draw_PicFromWad ("ram"); + ram_view = View_New (vsys, cl_screen_view); + View_SetPos (ram_view, 32, 0); + View_SetLen (ram_view, pic->width, pic->height); + View_SetGravity (ram_view, grav_northwest); + Ent_SetComponent (ram_view.id, canvas_pic, ram_view.reg, &pic); + View_SetVisible (ram_view, 0); + + pic = r_funcs->Draw_PicFromWad ("turtle"); + turtle_view = View_New (vsys, cl_screen_view); + View_SetPos (turtle_view, 32, 0); + View_SetLen (turtle_view, pic->width, pic->height); + View_SetGravity (turtle_view, grav_northwest); + Ent_SetComponent (turtle_view.id, canvas_pic, turtle_view.reg, &pic); + View_SetVisible (turtle_view, 0); + + Cvar_Register (&scr_showpause_cvar, 0, 0); + Cvar_Register (&scr_showram_cvar, 0, 0); + Cvar_Register (&scr_showturtle_cvar, 0, 0); + + pic = r_funcs->Draw_PicFromWad ("net"); + net_view = View_New (vsys, cl_screen_view); + View_SetPos (net_view, 64, 0); + View_SetLen (net_view, pic->width, pic->height); + View_SetGravity (net_view, grav_northwest); + Ent_SetComponent (net_view.id, canvas_pic, net_view.reg, &pic); + View_SetVisible (net_view, 0); + + timegraph_view = View_New (vsys, cl_screen_view); + View_SetPos (timegraph_view, 0, 0); + View_SetLen (timegraph_view, r_data->vid->width, 100); + View_SetGravity (timegraph_view, grav_southwest); + void *rtg = R_TimeGraph; + Ent_SetComponent (timegraph_view.id, canvas_func, timegraph_view.reg, &rtg); + View_SetVisible (timegraph_view, r_timegraph); + + zgraph_view = View_New (vsys, cl_screen_view); + View_SetPos (zgraph_view, 0, 0); + View_SetLen (zgraph_view, r_data->vid->width, 100); + View_SetGravity (zgraph_view, grav_southwest); + void *rzg = R_ZGraph; + Ent_SetComponent (zgraph_view.id, canvas_func, zgraph_view.reg, &rzg); + View_SetVisible (zgraph_view, r_zgraph); + + const char *name = "gfx/loading.lmp"; + pic = r_funcs->Draw_CachePic (name, 1); + loading_view = View_New (vsys, cl_screen_view); + View_SetPos (loading_view, 0, -24); + View_SetLen (loading_view, pic->width, pic->height); + View_SetGravity (loading_view, grav_center); + Ent_SetComponent (loading_view.id, canvas_cachepic, + loading_view.reg, &name); + View_SetVisible (loading_view, 0); + + name = "gfx/pause.lmp"; + pic = r_funcs->Draw_CachePic (name, 1); + pause_view = View_New (vsys, cl_screen_view); + View_SetPos (pause_view, 0, -24); + View_SetLen (pause_view, pic->width, pic->height); + View_SetGravity (pause_view, grav_center); + Ent_SetComponent (pause_view.id, canvas_cachepic, pause_view.reg, &name); + View_SetVisible (pause_view, 0); + + cvar_t *con_scale = Cvar_FindVar ("con_scale"); + Cvar_AddListener (con_scale, cl_scale_listener, 0); + cl_scale_listener (0, con_scale); + VID_OnVidResize_AddListener (cl_vidsize_listener, 0); +} + +void +CL_UpdateScreen (viewstate_t *vs) +{ + _vs = vs; + + //FIXME not every time + if (vs->active) { + if (vs->watervis) + r_data->min_wateralpha = 0.0; + else + r_data->min_wateralpha = 1.0; + } + + V_PrepBlend (vs); + V_RenderView (vs); + SCR_UpdateScreen (vs->camera_transform, vs->time, scr_funcs); +} diff --git a/libs/client/cl_temp_entities.c b/libs/client/cl_temp_entities.c new file mode 100644 index 000000000..af70f6f82 --- /dev/null +++ b/libs/client/cl_temp_entities.c @@ -0,0 +1,734 @@ +/* + cl_temp_entities.c + + Client side temporary entity management + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/3/10 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/msg.h" +#include "QF/progs.h" // for PR_RESMAP +#include "QF/quakefs.h" +#include "QF/render.h" +#include "QF/sound.h" + +#include "QF/plugin/vid_render.h" //FIXME + // +#include "QF/scene/entity.h" +#include "QF/scene/scene.h" + +#include "client/effects.h" +#include "client/entities.h" +#include "client/particles.h" +#include "client/temp_entities.h" +#include "client/world.h" + +typedef struct tent_s { + struct tent_s *next; + entity_t ent; +} tent_t; + +typedef struct { + int entity; + struct model_s *model; + float endtime; + vec4f_t start, end; + vec4f_t rotation; + tent_t *tents; + int seed; +} beam_t; + +#define BEAM_SEED_INTERVAL 72 +#define BEAM_SEED_PRIME 3191 + +typedef struct { + float start; + tent_t *tent; +} explosion_t; + +typedef struct tent_obj_s { + struct tent_obj_s *next; + union { + beam_t beam; + explosion_t ex; + } to; +} tent_obj_t; + +static PR_RESMAP (tent_t) temp_entities; +static PR_RESMAP (tent_obj_t) tent_objects; +static tent_obj_t *cl_beams; +static tent_obj_t *cl_explosions; + +static tent_t *cl_projectiles; + +static sfx_t *cl_sfx_wizhit; +static sfx_t *cl_sfx_knighthit; +static sfx_t *cl_sfx_tink1; +static sfx_t *cl_sfx_r_exp3; +static sfx_t *cl_sfx_ric[4]; + +static model_t *cl_mod_beam; +static model_t *cl_mod_bolt; +static model_t *cl_mod_bolt2; +static model_t *cl_mod_bolt3; +static model_t *cl_spr_explod; +static model_t *cl_spike; + +static vec4f_t beam_rolls[360]; + +void +CL_TEnts_Precache (void) +{ + cl_sfx_wizhit = S_PrecacheSound ("wizard/hit.wav"); + cl_sfx_knighthit = S_PrecacheSound ("hknight/hit.wav"); + cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav"); + cl_sfx_ric[3] = S_PrecacheSound ("weapons/ric1.wav"); + cl_sfx_ric[2] = S_PrecacheSound ("weapons/ric2.wav"); + cl_sfx_ric[1] = S_PrecacheSound ("weapons/ric3.wav"); + cl_sfx_ric[0] = cl_sfx_ric[1]; + cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav"); + + cl_mod_bolt = Mod_ForName ("progs/bolt.mdl", true); + cl_mod_bolt2 = Mod_ForName ("progs/bolt2.mdl", true); + cl_mod_bolt3 = Mod_ForName ("progs/bolt3.mdl", true); + cl_spr_explod = Mod_ForName ("progs/s_explod.spr", true); + cl_mod_beam = Mod_ForName ("progs/beam.mdl", false); + cl_spike = Mod_ForName ("progs/spike.mdl", false); + + if (!cl_mod_beam) { + cl_mod_beam = cl_mod_bolt; + } + S_SetAmbient (AMBIENT_WATER, S_PrecacheSound ("ambience/water1.wav")); + S_SetAmbient (AMBIENT_SKY, S_PrecacheSound ("ambience/wind2.wav")); +} + +static void +cl_tents_precache (int phase, void *data) +{ + if (!phase) { + return; + } + CL_TEnts_Precache (); +} + +void +CL_TEnts_Init (void) +{ + QFS_GamedirCallback (cl_tents_precache, 0); + for (int i = 0; i < 360; i++) { + float ang = i * M_PI / 360; + beam_rolls[i] = (vec4f_t) { sin (ang), 0, 0, cos (ang) }; + } +} + +void +CL_Init_Entity (entity_t ent) +{ + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, cl_world.scene->reg); + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, cl_world.scene->reg); + byte *active = Ent_GetComponent (ent.id, scene_active, cl_world.scene->reg); + vec4f_t *old_origin = Ent_GetComponent (ent.id, scene_old_origin, cl_world.scene->reg); + memset (animation, 0, sizeof (*animation)); + memset (renderer, 0, sizeof (*renderer)); + *active = 1; + *old_origin = (vec4f_t) {0, 0, 0, 1}; + + renderer->skin = 0; + QuatSet (1.0, 1.0, 1.0, 1.0, renderer->colormod); + animation->pose1 = animation->pose2 = -1; +} + +static tent_t * +new_temp_entity (void) +{ + tent_t *tent = PR_RESNEW_NC (temp_entities); + tent->ent = Scene_CreateEntity (cl_world.scene); + tent->next = 0; + CL_Init_Entity (tent->ent); + return tent; +} + +static void +free_temp_entities (tent_t *tents) +{ + tent_t **t = &tents; + + while (*t) { + Scene_DestroyEntity (cl_world.scene, (*t)->ent);//FIXME reuse? + t = &(*t)->next; + } + *t = temp_entities._free; + temp_entities._free = tents; +} + +static tent_obj_t * +new_tent_object (void) +{ + tent_obj_t *tobj = PR_RESNEW_NC (tent_objects); + tobj->next = 0; + return tobj; +} + +static void +free_tent_object (tent_obj_t *tobj) +{ + tobj->next = tent_objects._free; + tent_objects._free = tobj; +} + +void +CL_ClearTEnts (void) +{ + PR_RESRESET (temp_entities); + PR_RESRESET (tent_objects); + cl_beams = 0; + cl_explosions = 0; +} + +static inline void +beam_clear (beam_t *b) +{ + if (b->tents) { + free_temp_entities (b->tents); + b->tents = 0; + } +} + +static inline void +beam_setup (beam_t *b, bool settransform, double time, TEntContext_t *ctx) +{ + tent_t *tent; + float d; + int ent_count; + vec4f_t dist, org; + vec4f_t rotation; + vec4f_t scale = { 1, 1, 1, 1 }; + unsigned seed; + + // calculate pitch and yaw + dist = b->end - b->start; + + // FIXME interpolation may be off when passing through the -x axis + if (dist[0] < 0 && dist[1] == 0 && dist[2] == 0) { + // anti-parallel with the +x axis, so assome 180 degree rotation around + // the z-axis + rotation = (vec4f_t) { 0, 0, 1, 0 }; + } else { + rotation = qrotf ((vec4f_t) { 1, 0, 0, 0 }, dist); + } + b->rotation = rotation; + + // add new entities for the lightning + org = b->start; + d = magnitudef (dist)[0]; + dist = normalf (dist) * 30; + ent_count = ceil (d / 30); + d = 0; + + seed = b->seed + ((int) (time * BEAM_SEED_INTERVAL) % BEAM_SEED_INTERVAL); + + while (ent_count--) { + tent = new_temp_entity (); + tent->next = b->tents; + b->tents = tent; + + vec4f_t position = org + d * dist; + d += 1.0; + transform_t transform = Entity_Transform (tent->ent); + renderer_t *renderer = Ent_GetComponent (tent->ent.id, scene_renderer, cl_world.scene->reg); + renderer->model = b->model; + if (settransform) { + seed = seed * BEAM_SEED_PRIME; + Transform_SetLocalTransform (transform, scale, + qmulf (rotation, + beam_rolls[seed % 360]), + position); + } else { + Transform_SetLocalPosition (transform, position); + } + R_AddEfrags (&cl_world.scene->worldmodel->brush, tent->ent); + } +} + +static void +CL_ParseBeam (qmsg_t *net_message, model_t *m, double time, TEntContext_t *ctx) +{ + tent_obj_t *to; + beam_t *b; + int ent; + vec4f_t start, end; + + ent = MSG_ReadShort (net_message); + + MSG_ReadCoordV (net_message, (vec_t*)&start);//FIXME + MSG_ReadCoordV (net_message, (vec_t*)&end);//FIXME + start[3] = end[3] = 1;//FIXME + + to = 0; + if (ent) { + for (to = cl_beams; to; to = to->next) { + if (to->to.beam.entity == ent) { + break; + } + } + } + if (!to) { + to = new_tent_object (); + to->next = cl_beams; + cl_beams = to; + to->to.beam.tents = 0; + to->to.beam.entity = ent; + } + b = &to->to.beam; + + beam_clear (b); + b->model = m; + b->endtime = time + 0.2; + b->seed = rand (); + b->end = end; + if (b->entity != ctx->playerEntity) { + // this will be done in CL_UpdateBeams + b->start = start; + beam_setup (b, true, time, ctx); + } +} + +static void +parse_tent (qmsg_t *net_message, double time, TEntContext_t *ctx, + TE_Effect type) +{ + dlight_t *dl; + tent_obj_t *to; + explosion_t *ex; + int colorStart, colorLength; + quat_t color; + vec4f_t position = {0, 0, 0, 1}; + int count; + const char *name; + + switch (type) { + case TE_NoEffect: + // invalid mapping, can't do anything + break; + case TE_Beam: + CL_ParseBeam (net_message, cl_mod_beam, time, ctx); + break; + case TE_Blood: + count = MSG_ReadByte (net_message) * 20; + MSG_ReadCoordV (net_message, (vec_t*)&position);//FIXME + clp_funcs->BloodPuffEffect (position, count); + break; + case TE_Explosion1: + MSG_ReadCoordV (net_message, (vec_t*)&position);//FIXME + + // sprite + to = new_tent_object (); + to->next = cl_explosions; + cl_explosions = to; + ex = &to->to.ex; + ex->tent = new_temp_entity (); + + ex->start = time; + //FIXME need better model management + if (!cl_spr_explod->cache.data) { + cl_spr_explod = Mod_ForName ("progs/s_explod.spr", true); + } + transform_t transform = Entity_Transform (ex->tent->ent); + renderer_t *renderer = Ent_GetComponent (ex->tent->ent.id, scene_renderer, cl_world.scene->reg); + renderer->model = cl_spr_explod; + Transform_SetLocalPosition (transform, position); + goto TE_Explosion_no_sprite; + case TE_Explosion: + MSG_ReadCoordV (net_message, (vec_t*)&position);//FIXME +TE_Explosion_no_sprite: + // particles + clp_funcs->ParticleExplosion (position); + + // light + dl = R_AllocDlight (0); + if (dl) { + VectorCopy (position, dl->origin); + dl->radius = 350; + dl->die = time + 0.5; + dl->decay = 300; + QuatSet (0.86, 0.31, 0.24, 0.7, dl->color); + //FIXME? nq: QuatSet (1.0, 0.5, 0.25, 0.7, dl->color); + } + + // sound + S_StartSound (-1, 0, cl_sfx_r_exp3, position, 1, 1); + break; + case TE_Explosion2: + MSG_ReadCoordV (net_message, (vec_t*)&position);//FIXME + colorStart = MSG_ReadByte (net_message); + colorLength = MSG_ReadByte (net_message); + S_StartSound (-1, 0, cl_sfx_r_exp3, position, 1, 1); + clp_funcs->ParticleExplosion2 (position, colorStart, colorLength); + dl = R_AllocDlight (0); + if (!dl) + break; + VectorCopy (position, dl->origin); + dl->radius = 350; + dl->die = time + 0.5; + dl->decay = 300; + colorStart = (colorStart + (rand () % colorLength)) * 3; + VectorScale (&r_data->vid->palette[colorStart], 1.0 / 255.0, + dl->color); + dl->color[3] = 0.7; + break; + case TE_Explosion3: + MSG_ReadCoordV (net_message, (vec_t*)&position);//FIXME + MSG_ReadCoordV (net_message, color); // OUCH! + color[3] = 0.7; + clp_funcs->ParticleExplosion (position); + S_StartSound (-1, 0, cl_sfx_r_exp3, position, 1, 1); + dl = R_AllocDlight (0); + if (dl) { + VectorCopy (position, dl->origin); + dl->radius = 350; + dl->die = time + 0.5; + dl->decay = 300; + QuatCopy (color, dl->color); + } + break; + case TE_Gunshot1: + MSG_ReadCoordV (net_message, (vec_t*)&position);//FIXME + clp_funcs->GunshotEffect (position, 20); + break; + case TE_Gunshot2: + count = MSG_ReadByte (net_message) * 20; + MSG_ReadCoordV (net_message, (vec_t*)&position);//FIXME + clp_funcs->GunshotEffect (position, count); + break; + case TE_KnightSpike: + MSG_ReadCoordV (net_message, (vec_t*)&position);//FIXME + clp_funcs->KnightSpikeEffect (position); + S_StartSound (-1, 0, cl_sfx_knighthit, position, 1, 1); + break; + case TE_LavaSplash: + MSG_ReadCoordV (net_message, (vec_t*)&position);//FIXME + clp_funcs->LavaSplash (position); + break; + case TE_Lightning1: + CL_ParseBeam (net_message, cl_mod_bolt, time, ctx); + break; + case TE_Lightning2: + CL_ParseBeam (net_message, cl_mod_bolt2, time, ctx); + break; + case TE_Lightning3: + CL_ParseBeam (net_message, cl_mod_bolt3, time, ctx); + break; + case TE_Lightning4: + name = MSG_ReadString (net_message); + CL_ParseBeam (net_message, Mod_ForName (name, true), time, ctx); + break; + case TE_LightningBlood: + MSG_ReadCoordV (net_message, (vec_t*)&position);//FIXME + + // light + dl = R_AllocDlight (0); + if (dl) { + VectorCopy (position, dl->origin); + dl->radius = 150; + dl->die = time + 0.1; + dl->decay = 200; + QuatSet (0.25, 0.40, 0.65, 1, dl->color); + } + + clp_funcs->LightningBloodEffect (position); + break; + case TE_Spike: + MSG_ReadCoordV (net_message, (vec_t*)&position);//FIXME + clp_funcs->SpikeEffect (position); + { + int i; + sfx_t *sound; + + i = (rand () % 20) - 16; + if (i >= 0) { + sound = cl_sfx_ric[i]; + } else { + sound = cl_sfx_tink1; + } + S_StartSound (-1, 0, sound, position, 1, 1); + } + break; + case TE_SuperSpike: + MSG_ReadCoordV (net_message, (vec_t*)&position);//FIXME + clp_funcs->SuperSpikeEffect (position); + { + int i; + sfx_t *sound; + + i = (rand () % 20) - 16; + if (i >= 0) { + sound = cl_sfx_ric[i]; + } else { + sound = cl_sfx_tink1; + } + S_StartSound (-1, 0, sound, position, 1, 1); + } + break; + case TE_TarExplosion: + MSG_ReadCoordV (net_message, (vec_t*)&position);//FIXME + clp_funcs->BlobExplosion (position); + + S_StartSound (-1, 0, cl_sfx_r_exp3, position, 1, 1); + break; + case TE_Teleport: + MSG_ReadCoordV (net_message, (vec_t*)&position);//FIXME + clp_funcs->TeleportSplash (position); + break; + case TE_WizSpike: + MSG_ReadCoordV (net_message, (vec_t*)&position);//FIXME + clp_funcs->WizSpikeEffect (position); + S_StartSound (-1, 0, cl_sfx_wizhit, position, 1, 1); + break; + } +} + +// the effect type is a byte so a max of 256 values +static const TE_Effect nqEffects[256] = { + [TE_nqSpike] = TE_Spike, + [TE_nqSuperSpike] = TE_SuperSpike, + [TE_nqGunshot] = TE_Gunshot1, + [TE_nqExplosion] = TE_Explosion, + [TE_nqTarExplosion] = TE_TarExplosion, + [TE_nqLightning1] = TE_Lightning1, + [TE_nqLightning2] = TE_Lightning2, + [TE_nqWizSpike] = TE_WizSpike, + [TE_nqKnightSpike] = TE_KnightSpike, + [TE_nqLightning3] = TE_Lightning3, + [TE_nqLavaSplash] = TE_LavaSplash, + [TE_nqTeleport] = TE_Teleport, + [TE_nqExplosion2] = TE_Explosion2, + [TE_nqBeam] = TE_Beam, + [TE_nqExplosion3] = TE_Explosion3, + [TE_nqLightning4] = TE_Lightning4, +}; + +void +CL_ParseTEnt_nq (qmsg_t *net_message, double time, TEntContext_t *ctx) +{ + byte type = MSG_ReadByte (net_message); + parse_tent (net_message, time, ctx, nqEffects[type]); +} + +// the effect type is a byte so a max of 256 values +static const TE_Effect qwEffects[256] = { + [TE_qwSpike] = TE_Spike, + [TE_qwSuperSpike] = TE_SuperSpike, + [TE_qwGunshot] = TE_Gunshot2, + [TE_qwExplosion] = TE_Explosion1, + [TE_qwTarExplosion] = TE_TarExplosion, + [TE_qwLightning1] = TE_Lightning1, + [TE_qwLightning2] = TE_Lightning2, + [TE_qwWizSpike] = TE_WizSpike, + [TE_qwKnightSpike] = TE_KnightSpike, + [TE_qwLightning3] = TE_Lightning3, + [TE_qwLavaSplash] = TE_LavaSplash, + [TE_qwTeleport] = TE_Teleport, + [TE_qwBlood] = TE_Blood, + [TE_qwLightningBlood] = TE_LightningBlood, + [TE_qwExplosion2] = TE_Explosion2, + [TE_qwBeam] = TE_Beam, +}; + +void +CL_ParseTEnt_qw (qmsg_t *net_message, double time, TEntContext_t *ctx) +{ + byte type = MSG_ReadByte (net_message); + parse_tent (net_message, time, ctx, qwEffects[type]); +} + +static void +CL_UpdateBeams (double time, TEntContext_t *ctx) +{ + tent_obj_t **to; + beam_t *b; + unsigned seed; + tent_t *t; + + // update lightning + for (to = &cl_beams; *to; ) { + b = &(*to)->to.beam; + if (!b->endtime) + continue; + if (!b->model || b->endtime < time) { + tent_obj_t *_to; + b->endtime = 0; + beam_clear (b); + _to = *to; + *to = _to->next; + free_tent_object (_to); + continue; + } + to = &(*to)->next; + + // if coming from the player, update the start position + if (b->entity == ctx->playerEntity) { + beam_clear (b); + b->start = ctx->simorg; + beam_setup (b, false, time, ctx); + } + + seed = b->seed + ((int) (time * BEAM_SEED_INTERVAL) % + BEAM_SEED_INTERVAL); + + // add new entities for the lightning + for (t = b->tents; t; t = t->next) { + seed = seed * BEAM_SEED_PRIME; + transform_t transform = Entity_Transform (t->ent); + Transform_SetLocalRotation (transform, + qmulf (b->rotation, + beam_rolls[seed % 360])); + } + } +} + +static void +CL_UpdateExplosions (double time, TEntContext_t *ctx) +{ + int f; + tent_obj_t **to; + explosion_t *ex; + entity_t ent; + + for (to = &cl_explosions; *to; ) { + ex = &(*to)->to.ex; + ent = ex->tent->ent; + f = 10 * (time - ex->start); + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, cl_world.scene->reg); + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, cl_world.scene->reg); + if (f >= renderer->model->numframes) { + tent_obj_t *_to; + free_temp_entities (ex->tent); + _to = *to; + *to = _to->next; + free_tent_object (_to); + continue; + } + to = &(*to)->next; + + animation->frame = f; + R_AddEfrags (&cl_world.scene->worldmodel->brush, ent); + } +} + +void +CL_UpdateTEnts (double time, TEntContext_t *ctx) +{ + CL_UpdateBeams (time, ctx); + CL_UpdateExplosions (time, ctx); +} + +/* + CL_ParseParticleEffect + + Parse an effect out of the server message +*/ +void +CL_ParseParticleEffect (qmsg_t *net_message) +{ + int i, count, color; + vec4f_t org = {0, 0, 0, 1}, dir = {}; + + MSG_ReadCoordV (net_message, (vec_t*)&org);//FIXME + for (i = 0; i < 3; i++) + dir[i] = ((signed char) MSG_ReadByte (net_message)) * (15.0 / 16.0); + count = MSG_ReadByte (net_message); + color = MSG_ReadByte (net_message); + + if (count == 255) + clp_funcs->ParticleExplosion (org); + else + clp_funcs->RunParticleEffect (org, dir, color, count); +} + +void +CL_ClearProjectiles (void) +{ + free_temp_entities (cl_projectiles); + cl_projectiles = 0; +} + +/* + Nails are passed as efficient temporary entities +*/ +void +CL_ParseProjectiles (qmsg_t *net_message, bool nail2, TEntContext_t *ctx) +{ + tent_t *tent; + tent_t *head = 0, **tail = &head; + byte bits[6]; + int i, c, j, num; + entity_t pr; + vec4f_t position = { 0, 0, 0, 1 }; + vec3_t angles; + + c = MSG_ReadByte (net_message); + + for (i = 0; i < c; i++) { + if (nail2) + num = MSG_ReadByte (net_message); + else + num = 0; + (void) num; //FIXME + + for (j = 0; j < 6; j++) + bits[j] = MSG_ReadByte (net_message); + + tent = new_temp_entity (); + *tail = tent; + tail = &tent->next; + + pr = tent->ent; + renderer_t *renderer = Ent_GetComponent (pr.id, scene_renderer, cl_world.scene->reg); + renderer->model = cl_spike; + renderer->skin = 0; + position[0] = ((bits[0] + ((bits[1] & 15) << 8)) << 1) - 4096; + position[1] = (((bits[1] >> 4) + (bits[2] << 4)) << 1) - 4096; + position[2] = ((bits[3] + ((bits[4] & 15) << 8)) << 1) - 4096; + angles[0] = (bits[4] >> 4) * (360.0 / 16.0); + angles[1] = bits[5] * (360.0 / 256.0); + angles[2] = 0; + CL_TransformEntity (tent->ent, 1, angles, position); + + R_AddEfrags (&cl_world.scene->worldmodel->brush, pr); + } + + *tail = cl_projectiles; + cl_projectiles = head; +} diff --git a/libs/client/cl_view.c b/libs/client/cl_view.c new file mode 100644 index 000000000..2774f3ae3 --- /dev/null +++ b/libs/client/cl_view.c @@ -0,0 +1,1001 @@ +/* + cl_view.c + + player eye positioning + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/cmd.h" +#include "QF/cvar.h" +#include "QF/msg.h" +#include "QF/screen.h" + +#include "QF/plugin/vid_render.h" +#include "QF/scene/entity.h" +#include "QF/scene/scene.h" +#include "QF/simd/vec4f.h" + +#include "compat.h" + +#include "client/chase.h" +#include "client/entities.h" +#include "client/hud.h" +#include "client/input.h" +#include "client/view.h" +#include "client/world.h" + +/* + The view is allowed to move slightly from it's true position for bobbing, + but if it exceeds 8 pixels linear distance (spherical, not box), the list + of entities sent from the server may not include everything in the pvs, + especially when crossing a water boudnary. +*/ + +float scr_ofsx; +static cvar_t scr_ofsx_cvar = { + .name = "scr_ofsx", + .description = + "None", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &scr_ofsx }, +}; +float scr_ofsy; +static cvar_t scr_ofsy_cvar = { + .name = "scr_ofsy", + .description = + "None", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &scr_ofsy }, +}; +float scr_ofsz; +static cvar_t scr_ofsz_cvar = { + .name = "scr_ofsz", + .description = + "None", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &scr_ofsz }, +}; + +float cl_rollspeed; +static cvar_t cl_rollspeed_cvar = { + .name = "cl_rollspeed", + .description = + "How quickly you straighten out after strafing", + .default_value = "200", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &cl_rollspeed }, +}; +float cl_rollangle; +static cvar_t cl_rollangle_cvar = { + .name = "cl_rollangle", + .description = + "How much your screen tilts when strafing", + .default_value = "2.0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &cl_rollangle }, +}; + +float cl_bob; +static cvar_t cl_bob_cvar = { + .name = "cl_bob", + .description = + "How much your weapon moves up and down when walking", + .default_value = "0.02", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &cl_bob }, +}; +float cl_bobcycle; +static cvar_t cl_bobcycle_cvar = { + .name = "cl_bobcycle", + .description = + "How quickly your weapon moves up and down when walking", + .default_value = "0.6", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &cl_bobcycle }, +}; +float cl_bobup; +static cvar_t cl_bobup_cvar = { + .name = "cl_bobup", + .description = + "How long your weapon stays up before cycling when walking", + .default_value = "0.5", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &cl_bobup }, +}; + +float v_centermove; +static cvar_t v_centermove_cvar = { + .name = "v_centermove", + .description = + "How far the player must move forward before the view re-centers", + .default_value = "0.15", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &v_centermove }, +}; +float v_centerspeed; +static cvar_t v_centerspeed_cvar = { + .name = "v_centerspeed", + .description = + "How quickly you return to a center view after a lookup or lookdown", + .default_value = "500", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &v_centerspeed }, +}; + +float v_kicktime; +static cvar_t v_kicktime_cvar = { + .name = "v_kicktime", + .description = + "How long the kick from an attack lasts", + .default_value = "0.5", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &v_kicktime }, +}; +float v_kickroll; +static cvar_t v_kickroll_cvar = { + .name = "v_kickroll", + .description = + "How much you lean when hit", + .default_value = "0.6", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &v_kickroll }, +}; +float v_kickpitch; +static cvar_t v_kickpitch_cvar = { + .name = "v_kickpitch", + .description = + "How much you look up when hit", + .default_value = "0.6", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &v_kickpitch }, +}; + +int cl_cshift_bonus; +static cvar_t cl_cshift_bonus_cvar = { + .name = "cl_cshift_bonus", + .description = + "Show bonus flash on item pickup", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &cl_cshift_bonus }, +}; +int cl_cshift_contents; +static cvar_t cl_cshift_contents_cvar = { + .name = "cl_cshift_content", + .description = + "Shift view colors for contents (water, slime, etc)", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &cl_cshift_contents }, +}; +int cl_cshift_damage; +static cvar_t cl_cshift_damage_cvar = { + .name = "cl_cshift_damage", + .description = + "Shift view colors on damage", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &cl_cshift_damage }, +}; +int cl_cshift_powerup; +static cvar_t cl_cshift_powerup_cvar = { + .name = "cl_cshift_powerup", + .description = + "Shift view colors for powerups", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &cl_cshift_powerup }, +}; + +float v_iyaw_cycle; +static cvar_t v_iyaw_cycle_cvar = { + .name = "v_iyaw_cycle", + .description = + "How far you tilt right and left when v_idlescale is enabled", + .default_value = "2", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &v_iyaw_cycle }, +}; +float v_iroll_cycle; +static cvar_t v_iroll_cycle_cvar = { + .name = "v_iroll_cycle", + .description = + "How quickly you tilt right and left when v_idlescale is enabled", + .default_value = "0.5", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &v_iroll_cycle }, +}; +float v_ipitch_cycle; +static cvar_t v_ipitch_cycle_cvar = { + .name = "v_ipitch_cycle", + .description = + "How quickly you lean forwards and backwards when v_idlescale is " + "enabled", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &v_ipitch_cycle }, +}; +float v_iyaw_level; +static cvar_t v_iyaw_level_cvar = { + .name = "v_iyaw_level", + .description = + "How far you tilt right and left when v_idlescale is enabled", + .default_value = "0.3", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &v_iyaw_level }, +}; +float v_iroll_level; +static cvar_t v_iroll_level_cvar = { + .name = "v_iroll_level", + .description = + "How far you tilt right and left when v_idlescale is enabled", + .default_value = "0.1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &v_iroll_level }, +}; +float v_ipitch_level; +static cvar_t v_ipitch_level_cvar = { + .name = "v_ipitch_level", + .description = + "How far you lean forwards and backwards when v_idlescale is enabled", + .default_value = "0.3", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &v_ipitch_level }, +}; + +float v_idlescale; +static cvar_t v_idlescale_cvar = { + .name = "v_idlescale", + .description = + "Toggles whether the view remains idle", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &v_idlescale }, +}; + +float v_dmg_time, v_dmg_roll, v_dmg_pitch; + +vec4f_t v_idle_yaw; +vec4f_t v_idle_roll; +vec4f_t v_idle_pitch; + +static cshift_t cshift_empty = { {130, 80, 50}, 0}; +static cshift_t cshift_water = { {130, 80, 50}, 128}; +static cshift_t cshift_slime = { { 0, 25, 5}, 150}; +static cshift_t cshift_lava = { {255, 80, 0}, 150}; +static cshift_t cshift_bonus = { {215, 186, 60}, 50}; + +static cshift_t armor_blood[] = { + { {255, 0, 0} }, // blood + { {220, 50, 50} }, // armor + blood + { {200, 100, 100} }, // armor > blood need two for logic + { {200, 100, 100} }, // armor > blood need two for logic +}; + +static cshift_t powerup[] = { + { { 0, 0, 0}, 0}, + { {100, 100, 100}, 100}, // IT_INVISIBILITY + { {255, 255, 0}, 30}, // IT_INVULNERABILITY + { {255, 255, 0}, 30}, // IT_INVULNERABILITY + { { 0, 255, 0}, 20}, // IT_SUIT + { { 0, 255, 0}, 20}, // IT_SUIT + { { 0, 255, 0}, 20}, // IT_SUIT + { { 0, 255, 0}, 20}, // IT_SUIT + { { 0, 0, 255}, 30}, // IT_QUAD + { { 0, 0, 255}, 30}, // IT_QUAD + { {255, 0, 255}, 30}, // IT_INVULNERABILITY | IT_QUAD + { {255, 0, 255}, 30}, // IT_INVULNERABILITY | IT_QUAD + { {255, 0, 255}, 30}, // IT_INVULNERABILITY | IT_QUAD + { {255, 0, 255}, 30}, // IT_INVULNERABILITY | IT_QUAD + { {255, 0, 255}, 30}, // IT_INVULNERABILITY | IT_QUAD + { {255, 0, 255}, 30}, // IT_INVULNERABILITY | IT_QUAD +}; + +#define sqr(x) ((x) * (x)) + +float +V_CalcRoll (const vec3_t angles, vec4f_t velocity) +{ + float side, sign, value; + vec3_t forward, right, up; + + AngleVectors (angles, forward, right, up); + side = DotProduct (velocity, right); + sign = side < 0 ? -1 : 1; + side = fabs (side); + + value = cl_rollangle; + + if (side < cl_rollspeed) + side = side * value / cl_rollspeed; + else + side = value; + + return side * sign; +} + +static float +V_CalcBob (viewstate_t *vs) +{ + vec4f_t velocity = vs->velocity; + float cycle; + static double bobtime; + static float bob; + + if (!vs->bob_enabled) + return 0; + + if (vs->onground == -1) + return bob; // just use old value + + bobtime += vs->frametime; + cycle = bobtime - (int) (bobtime / cl_bobcycle) * + cl_bobcycle; + cycle /= cl_bobcycle; + if (cycle < cl_bobup) + cycle = cycle / cl_bobup; + else + cycle = 1 + (cycle - cl_bobup) / (1.0 - cl_bobup); + + // bob is proportional to velocity in the xy plane + // (don't count Z, or jumping messes it up) + velocity[2] = 0; + bob = sqrt (dotf (velocity, velocity)[0]) * cl_bob; + bob = bob * 0.3 + bob * 0.7 * sin (cycle * M_PI); + if (bob > 4) + bob = 4; + else if (bob < -7) + bob = -7; + return bob; +} + +void +V_StartPitchDrift (viewstate_t *vs) +{ + if (vs->laststop == vs->time) { + return; // something else is keeping it from drifting + } + + if (vs->nodrift || !vs->pitchvel) { + vs->pitchvel = v_centerspeed; + vs->nodrift = false; + vs->driftmove = 0; + } +} + +static void +V_StartPitchDrift_f (void *data) +{ + V_StartPitchDrift (data); +} + +void +V_StopPitchDrift (viewstate_t *vs) +{ + vs->laststop = vs->time; + vs->nodrift = true; + vs->pitchvel = 0; +} + +/* + V_DriftPitch + + Moves the client pitch angle towards vs->idealpitch sent by the server. + + If the user is adjusting pitch manually, either with lookup/lookdown, + mlook and mouse, or klook and keyboard, pitch drifting is constantly + stopped. + + Drifting is enabled when the center view key is hit, mlook is released + and lookspring is non 0, or when +*/ +static void +V_DriftPitch (viewstate_t *vs) +{ + float delta, move; + float forwardmove = vs->movecmd[0]; + + if (noclip_anglehack || vs->onground == -1 || !vs->drift_enabled) { + vs->driftmove = 0; + vs->pitchvel = 0; + return; + } + + // don't count small mouse motion + if (vs->nodrift) { + if (fabs (forwardmove) < cl_forwardspeed) + vs->driftmove = 0; + else + vs->driftmove += vs->frametime; + + if (vs->driftmove > v_centermove) { + V_StartPitchDrift (vs); + } + return; + } + + delta = vs->idealpitch - vs->player_angles[PITCH]; + + if (!delta) { + vs->pitchvel = 0; + return; + } + + move = vs->frametime * vs->pitchvel; + vs->pitchvel += vs->frametime * v_centerspeed; + + if (delta > 0) { + if (move > delta) { + vs->pitchvel = 0; + move = delta; + } + vs->player_angles[PITCH] += move; + } else if (delta < 0) { + if (move > -delta) { + vs->pitchvel = 0; + move = -delta; + } + vs->player_angles[PITCH] -= move; + } +} + +/* PALETTE FLASHES */ + +void +V_ParseDamage (qmsg_t *net_message, viewstate_t *vs) +{ + float count, side; + int armor, blood; + vec4f_t origin = vs->player_origin; + vec_t *angles = vs->player_angles; + vec3_t from, forward, right, up; + + armor = MSG_ReadByte (net_message); + blood = MSG_ReadByte (net_message); + MSG_ReadCoordV (net_message, from); + + count = blood * 0.5 + armor * 0.5; + if (count < 10) + count = 10; + + if (cl_cshift_damage + || (vs->force_cshifts & INFO_CSHIFT_DAMAGE)) { + cshift_t *cshift = &vs->cshifts[CSHIFT_DAMAGE]; + int percent = cshift->percent; + *cshift = armor_blood[(2 * (armor > blood)) + (armor > 0)]; + cshift->percent = percent + 3 * count; + cshift->percent = bound (0, cshift->percent, 150); + cshift->initialpct = cshift->percent; + cshift->time = vs->time; + } + + // calculate view angle kicks + VectorSubtract (from, origin, from); + VectorNormalize (from); + + AngleVectors (angles, forward, right, up); + + side = DotProduct (from, right); + v_dmg_roll = count * side * v_kickroll; + + side = DotProduct (from, forward); + v_dmg_pitch = count * side * v_kickpitch; + + v_dmg_time = v_kicktime; +} + +static void +V_cshift_f (void) +{ + cshift_empty.destcolor[0] = atoi (Cmd_Argv (1)); + cshift_empty.destcolor[1] = atoi (Cmd_Argv (2)); + cshift_empty.destcolor[2] = atoi (Cmd_Argv (3)); + cshift_empty.percent = atoi (Cmd_Argv (4)); +} + +/* + V_BonusFlash_f + + When you run over an item, the server sends this command +*/ +static void +V_BonusFlash_f (void *data) +{ + viewstate_t *vs = data; + if (!cl_cshift_bonus + && !(vs->force_cshifts & INFO_CSHIFT_BONUS)) + return; + + vs->cshifts[CSHIFT_BONUS] = cshift_bonus; + vs->cshifts[CSHIFT_BONUS].initialpct = vs->cshifts[CSHIFT_BONUS].percent; + vs->cshifts[CSHIFT_BONUS].time = vs->time; +} + +/* + V_SetContentsColor + + Underwater, lava, etc each has a color shift +*/ +void +V_SetContentsColor (viewstate_t *vs, int contents) +{ + if (!cl_cshift_contents + && !(vs->force_cshifts & INFO_CSHIFT_CONTENTS)) { + vs->cshifts[CSHIFT_CONTENTS] = cshift_empty; + return; + } + + switch (contents) { + case CONTENTS_EMPTY: + vs->cshifts[CSHIFT_CONTENTS] = cshift_empty; + break; + case CONTENTS_LAVA: + vs->cshifts[CSHIFT_CONTENTS] = cshift_lava; + break; + case CONTENTS_SOLID: + case CONTENTS_SLIME: + vs->cshifts[CSHIFT_CONTENTS] = cshift_slime; + break; + default: + vs->cshifts[CSHIFT_CONTENTS] = cshift_water; + } +} + +static void +V_CalcPowerupCshift (viewstate_t *vs) +{ + vs->cshifts[CSHIFT_POWERUP] = powerup[vs->powerup_index]; +} + +/* + V_CalcBlend + + LordHavoc made this a real, true alpha blend. Cleaned it up + a bit, but otherwise this is his code. --KB +*/ +static void +V_CalcBlend (viewstate_t *vs) +{ + float a2, a3; + float r = 0, g = 0, b = 0, a = 0; + int i; + + for (i = 0; i < NUM_CSHIFTS; i++) { + a2 = vs->cshifts[i].percent / 255.0; + + if (!a2) + continue; + + a2 = min (a2, 1.0); + r += (vs->cshifts[i].destcolor[0] - r) * a2; + g += (vs->cshifts[i].destcolor[1] - g) * a2; + b += (vs->cshifts[i].destcolor[2] - b) * a2; + + a3 = (1.0 - a) * (1.0 - a2); + a = 1.0 - a3; + } + + // LordHavoc: saturate color + if (a) { + a2 = 1.0 / a; + r *= a2; + g *= a2; + b *= a2; + } + + vs->cshift_color[0] = min (r, 255.0) / 255.0; + vs->cshift_color[1] = min (g, 255.0) / 255.0; + vs->cshift_color[2] = min (b, 255.0) / 255.0; + vs->cshift_color[3] = bound (0.0, a, 1.0); +} + +static void +V_DropCShift (cshift_t *cs, double time, float droprate) +{ + if (cs->time < 0) { + cs->percent = 0; + } else { + cs->percent = cs->initialpct - (time - cs->time) * droprate; + if (cs->percent <= 0) { + cs->percent = 0; + cs->time = -1; + } + } +} + +void +V_PrepBlend (viewstate_t *vs) +{ + int i, j; + + if (cl_cshift_powerup + || (vs->force_cshifts & INFO_CSHIFT_POWERUP)) + V_CalcPowerupCshift (vs); + + bool cshift_changed = false; + + for (i = 0; i < NUM_CSHIFTS; i++) { + if (vs->cshifts[i].percent != vs->prev_cshifts[i].percent) { + cshift_changed = true; + vs->prev_cshifts[i].percent = vs->cshifts[i].percent; + } + for (j = 0; j < 3; j++) { + if (vs->cshifts[i].destcolor[j] != vs->prev_cshifts[i].destcolor[j]) + { + cshift_changed = true; + vs->prev_cshifts[i].destcolor[j] = vs->cshifts[i].destcolor[j]; + } + } + } + + // drop the damage value + V_DropCShift (&vs->cshifts[CSHIFT_DAMAGE], vs->time, 150); + // drop the bonus value + V_DropCShift (&vs->cshifts[CSHIFT_BONUS], vs->time, 100); + + if (!cshift_changed) + return; + + V_CalcBlend (vs); +} + +/* VIEW RENDERING */ + +static void +CalcGunAngle (viewstate_t *vs) +{ + vec4f_t rotation = Transform_GetWorldRotation (vs->camera_transform); + //FIXME make child of camera + transform_t wep_form = Entity_Transform (vs->weapon_entity); + Transform_SetWorldRotation (wep_form, rotation); +} + +static void +V_BoundOffsets (viewstate_t *vs) +{ + vec4f_t offset = Transform_GetWorldPosition (vs->camera_transform); + offset -= vs->player_origin; + + // absolutely bound refresh reletive to entity clipping hull + // so the view can never be inside a solid wall + + offset[0] = bound (-14, offset[0], 14); + offset[1] = bound (-14, offset[1], 14); + offset[2] = bound (-22, offset[2], 30); + Transform_SetWorldPosition (vs->camera_transform, + vs->player_origin + offset); +} + +static vec4f_t +idle_quat (vec4f_t axis, float cycle, float level, double time) +{ + vec4f_t identity = { 0, 0, 0, 1 }; + if (!level || !cycle) { + return identity; + } + float scale = sin (time * cycle); + float ang = scale * level * v_idlescale; + float c = cos (ang * M_PI / 360); + float s = sin (ang * M_PI / 360); + return axis * s + identity * c; +} + +/* + V_AddIdle + + Idle swaying +*/ +static void +V_AddIdle (viewstate_t *vs) +{ + vec4f_t roll = idle_quat ((vec4f_t) { 1, 0, 0, 0}, + v_iroll_cycle, v_iroll_level, vs->time); + vec4f_t pitch = idle_quat ((vec4f_t) { 0, 1, 0, 0}, + v_ipitch_cycle, v_ipitch_level, vs->time); + vec4f_t yaw = idle_quat ((vec4f_t) { 0, 0, 1, 0}, + v_iyaw_cycle, v_iyaw_level, vs->time); + vec4f_t rot = normalf (qmulf (yaw, qmulf (pitch, roll))); + + // rotate the view + vec4f_t rotation = Transform_GetWorldRotation (vs->camera_transform); + Transform_SetWorldRotation (vs->camera_transform, qmulf (rot, rotation)); + + // counter-rotate the weapon + transform_t wep_form = Entity_Transform (vs->weapon_entity); + rot = qmulf (qconjf (rot), Transform_GetWorldRotation (wep_form)); + Transform_SetWorldRotation (wep_form, rot); +} + +/* + V_CalcViewRoll + + Roll is induced by movement and damage +*/ +static void +V_CalcViewRoll (viewstate_t *vs) +{ + vec_t *angles = vs->player_angles; + vec4f_t velocity = vs->velocity; + vec3_t ang = { }; + + ang[ROLL] = V_CalcRoll (angles, velocity); + + if (v_dmg_time > 0) { + ang[ROLL] += v_dmg_time / v_kicktime * v_dmg_roll; + ang[PITCH] += v_dmg_time / v_kicktime * v_dmg_pitch; + v_dmg_time -= vs->frametime; + } + + if (vs->flags & VF_DEAD) { // VF_GIB will also set VF_DEAD + ang[ROLL] = 80; // dead view angle + } + + vec4f_t rot; + AngleQuat (ang, (vec_t*)&rot);//FIXME + vec4f_t rotation = Transform_GetWorldRotation (vs->camera_transform); + Transform_SetWorldRotation (vs->camera_transform, qmulf (rotation, rot)); +} + +static void +V_CalcIntermissionRefdef (viewstate_t *vs) +{ + // vs->player_entity is the player model (visible when out of body) + entity_t ent = vs->player_entity; + entity_t view; + float old; + transform_t transform = Entity_Transform (ent); + vec4f_t origin = Transform_GetWorldPosition (transform); + vec4f_t rotation = Transform_GetWorldRotation (transform); + + // view is the weapon model (visible only from inside body) + view = vs->weapon_entity; + + Transform_SetWorldPosition (vs->camera_transform, origin); + Transform_SetWorldRotation (vs->camera_transform, rotation); + renderer_t *renderer = Ent_GetComponent (view.id, scene_renderer, view.reg); + renderer->model = NULL; + + // always idle in intermission + old = v_idlescale; + v_idlescale = 1; + V_AddIdle (vs); + v_idlescale = old; +} + +static void +V_CalcRefdef (viewstate_t *vs) +{ + // view is the weapon model (visible only from inside body) + entity_t view = vs->weapon_entity; + float bob; + static float oldz = 0; + vec4f_t forward = {}, right = {}, up = {}; + vec4f_t origin = vs->player_origin; + vec_t *viewangles = vs->player_angles; + + renderer_t *renderer = Ent_GetComponent (view.id, scene_renderer, view.reg); + animation_t *animation = Ent_GetComponent (view.id, scene_animation, view.reg); + + V_DriftPitch (vs); + + bob = V_CalcBob (vs); + + // refresh position + if (vs->flags & VF_GIB) { + origin[2] += 8; // gib view height + } else if (vs->flags & VF_DEAD) { + origin[2] += -16; // corpse view height + } else { + origin[2] += vs->height + bob; + } + + // never let it sit exactly on a node line, because a water plane can + // disappear when viewed with the eye exactly on it. + // server protocol specifies to only 1/8 pixel, so add 1/16 in each axis + origin += (vec4f_t) { 1.0/16, 1.0/16, 1.0/16, 0}; + + vec4f_t rotation; + AngleQuat (vs->player_angles, (vec_t*)&rotation);//FIXME + Transform_SetWorldRotation (vs->camera_transform, rotation); + V_CalcViewRoll (vs); + V_AddIdle (vs); + + // offsets + //FIXME semi-duplicates AngleQuat (also, vec3_t vs vec4f_t) + AngleVectors (viewangles, (vec_t*)&forward, (vec_t*)&right, (vec_t*)&up);//FIXME + + // don't allow cheats in multiplayer + // FIXME check for dead + if (vs->voffs_enabled) { + origin += scr_ofsx * forward + scr_ofsy * right + scr_ofsz * up; + } + + V_BoundOffsets (vs); + + // set up gun position + vec4f_t gun_origin = vs->player_origin; + CalcGunAngle (vs); + + gun_origin += (vec4f_t) { 0, 0, vs->height, 0 }; + gun_origin += forward * bob * 0.4f + (vec4f_t) { 0, 0, bob, 0 }; + + // fudge position around to keep amount of weapon visible + // roughly equal with different FOV + if (hud_sbar == 0 && *r_data->scr_viewsize >= 100) { + ; + } else if (*r_data->scr_viewsize == 110) { + gun_origin += (vec4f_t) { 0, 0, 1, 0}; + } else if (*r_data->scr_viewsize == 100) { + gun_origin += (vec4f_t) { 0, 0, 2, 0}; + } else if (*r_data->scr_viewsize == 90) { + gun_origin += (vec4f_t) { 0, 0, 1, 0}; + } else if (*r_data->scr_viewsize == 80) { + gun_origin += (vec4f_t) { 0, 0, 0.5, 0}; + } + + model_t *model = vs->weapon_model; + if (vs->flags & (VF_GIB | VF_DEAD)) { + model = NULL; + } + if (renderer->model != model) { + animation->pose2 = -1; + } + renderer->model = model; + animation->frame = vs->weaponframe; + renderer->skin = 0; + + // set up the refresh position + Transform_SetWorldRotation (vs->camera_transform, + qmulf (vs->punchangle, rotation)); + + // smooth out stair step ups + if ((vs->onground != -1) && (gun_origin[2] - oldz > 0)) { + float steptime; + + steptime = vs->frametime; + + oldz += steptime * 80; + if (oldz > gun_origin[2]) + oldz = gun_origin[2]; + if (gun_origin[2] - oldz > 12) + oldz = gun_origin[2] - 12; + origin[2] += oldz - gun_origin[2]; + gun_origin[2] += oldz - gun_origin[2]; + } else { + oldz = gun_origin[2]; + } + Transform_SetWorldPosition (vs->camera_transform, origin); + { + // FIXME sort out the alias model specific negation + vec3_t ang = {-viewangles[0], viewangles[1], viewangles[2]}; + CL_TransformEntity (view, 1, ang, gun_origin); + } +} + +static void +DropPunchAngle (viewstate_t *vs) +{ + vec4f_t punch = vs->punchangle; + float ps = magnitude3f (punch)[0]; + if (ps < 1e-3) { + // < 0.2 degree rotation, not worth worrying about + //ensure the quaternion is normalized + vs->punchangle = (vec4f_t) { 0, 0, 0, 1 }; + return; + } + float pc = punch[3]; + float ds = 0.0871557427 * vs->frametime; + float dc = sqrt (1 - ds * ds); + float s = ps * dc - pc * ds; + float c = pc * dc + ps * ds; + if (s <= 0 || c >= 1) { + vs->punchangle = (vec4f_t) { 0, 0, 0, 1 }; + } else { + punch *= s / ps; + punch[3] = c; + } +} + +/* + V_RenderView + + The player's clipping box goes from (-16 -16 -24) to (16 16 32) from + the entity origin, so any view position inside that will be valid +*/ +void +V_RenderView (viewstate_t *vs) +{ + if (!vs->active) { + vec4f_t base = { 0, 0, 0, 1 }; + Transform_SetWorldPosition (vs->camera_transform, base); + Transform_SetWorldRotation (vs->camera_transform, base); + return; + } + + if (vs->decay_punchangle) { + DropPunchAngle (vs); + } + if (vs->intermission) { // intermission / finale rendering + V_CalcIntermissionRefdef (vs); + } else { + if (vs->chase && chase_active) { + Chase_Update (vs->chasestate); + } else { + V_CalcRefdef (vs); + } + } +} + +void +V_Init (viewstate_t *viewstate) +{ + Cmd_AddDataCommand ("bf", V_BonusFlash_f, viewstate, + "Background flash, used when you pick up an item"); + Cmd_AddDataCommand ("centerview", V_StartPitchDrift_f, viewstate, + "Centers the player's " + "view ahead after +lookup or +lookdown\n" + "Will not work while mlook is active or freelook is 1."); + Cmd_AddCommand ("v_cshift", V_cshift_f, "This adjusts all of the colors " + "currently being displayed.\n" + "Used when you are underwater, hit, have the Ring of " + "Shadows, or Quad Damage. (v_cshift r g b intensity)"); + + viewstate->camera_transform = Transform_New (cl_world.scene->reg, + nulltransform); +} + +void +V_Init_Cvars (void) +{ + Cvar_Register (&v_centermove_cvar, 0, 0); + Cvar_Register (&v_centerspeed_cvar, 0, 0); + Cvar_Register (&v_iyaw_cycle_cvar, 0, 0); + Cvar_Register (&v_iroll_cycle_cvar, 0, 0); + Cvar_Register (&v_ipitch_cycle_cvar, 0, 0); + Cvar_Register (&v_iyaw_level_cvar, 0, 0); + Cvar_Register (&v_iroll_level_cvar, 0, 0); + Cvar_Register (&v_ipitch_level_cvar, 0, 0); + Cvar_Register (&v_idlescale_cvar, 0, 0); + + Cvar_Register (&scr_ofsx_cvar, 0, 0); + Cvar_Register (&scr_ofsy_cvar, 0, 0); + Cvar_Register (&scr_ofsz_cvar, 0, 0); + Cvar_Register (&cl_rollspeed_cvar, 0, 0); + Cvar_Register (&cl_rollangle_cvar, 0, 0); + Cvar_Register (&cl_bob_cvar, 0, 0); + Cvar_Register (&cl_bobcycle_cvar, 0, 0); + Cvar_Register (&cl_bobup_cvar, 0, 0); + Cvar_Register (&v_kicktime_cvar, 0, 0); + Cvar_Register (&v_kickroll_cvar, 0, 0); + Cvar_Register (&v_kickpitch_cvar, 0, 0); + Cvar_Register (&cl_cshift_bonus_cvar, 0, 0); + Cvar_Register (&cl_cshift_contents_cvar, 0, 0); + Cvar_Register (&cl_cshift_damage_cvar, 0, 0); + Cvar_Register (&cl_cshift_powerup_cvar, 0, 0); +} diff --git a/libs/client/cl_world.c b/libs/client/cl_world.c new file mode 100644 index 000000000..8b8303136 --- /dev/null +++ b/libs/client/cl_world.c @@ -0,0 +1,235 @@ +/* + cl_entities.c + + Client side entity management + + Copyright (C) 2012 Bill Currie + + Author: Bill Currie + Date: 2012/6/28 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/cbuf.h" +#include "QF/cmd.h" +#include "QF/idparse.h" +#include "QF/quakefs.h" +#include "QF/plist.h" +#include "QF/progs.h" +#include "QF/msg.h" + +#include "QF/scene/entity.h" +#include "QF/scene/light.h" +#include "QF/scene/scene.h" +#include "QF/simd/vec4f.h" + +#include "QF/plugin/vid_render.h" //FIXME + +#include "client/entities.h" +#include "client/temp_entities.h" +#include "client/world.h" + +worldscene_t cl_world = { + .models = DARRAY_STATIC_INIT (32), +}; + +void +CL_World_Init (void) +{ + cl_world.scene = Scene_NewScene (); + cl_world.scene->lights = Light_CreateLightingData (cl_world.scene); +} + +void +CL_ParseBaseline (qmsg_t *msg, entity_state_t *baseline, int version) +{ + int bits = 0; + + if (version == 2) + bits = MSG_ReadByte (msg); + + if (bits & B_LARGEMODEL) + baseline->modelindex = MSG_ReadShort (msg); + else + baseline->modelindex = MSG_ReadByte (msg); + + if (bits & B_LARGEFRAME) + baseline->frame = MSG_ReadShort (msg); + else + baseline->frame = MSG_ReadByte (msg); + + baseline->colormap = MSG_ReadByte (msg); + baseline->skinnum = MSG_ReadByte (msg); + + MSG_ReadCoordAngleV (msg, (vec_t*)&baseline->origin, baseline->angles);//FIXME + baseline->origin[3] = 1;//FIXME + + if (bits & B_ALPHA) + baseline->alpha = MSG_ReadByte (msg); + else + baseline->alpha = 255;//FIXME alpha + baseline->scale = 16; + baseline->glow_size = 0; + baseline->glow_color = 254; + baseline->colormod = 255; +} + +void +CL_ParseStatic (qmsg_t *msg, int version) +{ + entity_t ent; + entity_state_t es; + + ent = Scene_CreateEntity (cl_world.scene); + CL_Init_Entity (ent); + + CL_ParseBaseline (msg, &es, version); + DARRAY_APPEND (&cl_static_entities, es); + + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, cl_world.scene->reg); + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, cl_world.scene->reg); + + // copy it to the current state + renderer->model = cl_world.models.a[es.modelindex]; + animation->frame = es.frame; + renderer->skinnum = es.skinnum; + + CL_TransformEntity (ent, es.scale / 16.0, es.angles, es.origin); + + R_AddEfrags (&cl_world.scene->worldmodel->brush, ent); +} + +static void +map_cfg (const char *mapname, int all) +{ + char *name = malloc (strlen (mapname) + 4 + 1); + cbuf_t *cbuf = Cbuf_New (&id_interp); + QFile *f; + + QFS_StripExtension (mapname, name); + strcat (name, ".cfg"); + f = QFS_FOpenFile (name); + if (f) { + Qclose (f); + Cmd_Exec_File (cbuf, name, 1); + } else { + Cmd_Exec_File (cbuf, "maps_default.cfg", 1); + } + if (all) { + Cbuf_Execute_Stack (cbuf); + } else { + Cbuf_Execute_Sets (cbuf); + } + free (name); + Cbuf_Delete (cbuf); +} + +void +CL_MapCfg (const char *mapname) +{ + map_cfg (mapname, 0); +} + +static plitem_t * +map_ent (const char *mapname) +{ + static progs_t edpr; + char *name = malloc (strlen (mapname) + 4 + 1); + char *buf; + plitem_t *edicts = 0; + QFile *ent_file; + + QFS_StripExtension (mapname, name); + strcat (name, ".ent"); + ent_file = QFS_VOpenFile (name, 0, cl_world.models.a[1]->vpath); + if ((buf = (char *) QFS_LoadFile (ent_file, 0))) { + edicts = ED_Parse (&edpr, buf); + free (buf); + } else { + edicts = ED_Parse (&edpr, cl_world.models.a[1]->brush.entities); + } + free (name); + return edicts; +} + +static void +CL_LoadSky (const char *name) +{ + plitem_t *worldspawn = cl_world.worldspawn; + plitem_t *item; + static const char *sky_keys[] = { + "sky", // Q2/DarkPlaces + "skyname", // old QF + "qlsky", // QuakeLives + 0 + }; + + if (!name) { + if (!worldspawn) { + r_funcs->R_LoadSkys (0); + return; + } + for (const char **key = sky_keys; *key; key++) { + if ((item = PL_ObjectForKey (cl_world.worldspawn, *key))) { + name = PL_String (item); + break; + } + } + } + r_funcs->R_LoadSkys (name); +} + +void +CL_World_NewMap (const char *mapname, const char *skyname) +{ + model_t *worldmodel = cl_world.models.a[1]; + cl_world.scene->worldmodel = worldmodel; + + cl_static_entities.size = 0; + + if (cl_world.models.a[1] && cl_world.models.a[1]->brush.entities) { + if (cl_world.edicts) { + PL_Release (cl_world.edicts); + } + cl_world.edicts = map_ent (mapname); + if (cl_world.edicts) { + cl_world.worldspawn = PL_ObjectAtIndex (cl_world.edicts, 0); + CL_LoadSky (skyname); + Fog_ParseWorldspawn (cl_world.worldspawn); + } + } + CL_LoadLights (cl_world.edicts, cl_world.scene); + + cl_world.scene->models = cl_world.models.a; + cl_world.scene->num_models = cl_world.models.size; + SCR_NewScene (cl_world.scene); + map_cfg (mapname, 1); +} diff --git a/libs/client/default_input.plist b/libs/client/default_input.plist new file mode 100644 index 000000000..765ec9e2c --- /dev/null +++ b/libs/client/default_input.plist @@ -0,0 +1,162 @@ +{ + input = { + contexts = ( + { + name = key_game; + imts = ( + { + name = imt_mod; + }, + { + name = imt_mod_strafe; + chain = imt_mod; + }, + { + name = imt_mod_freelook; + chain = imt_mod; + }, + { + name = imt_mod_lookstrafe; + chain = imt_mod_freelook; + } + ); + default_imt = imt_mod; + switchers = ( + { + name = mouse; + inputs = ( + +strafe, + lookstrafe, + +mlook, + freelook + ); + imts = ( + imt_mod, + imt_mod_strafe, + imt_mod, + imt_mod_strafe, + imt_mod_freelook, + imt_mod_strafe, + imt_mod_lookstrafe, + imt_mod_strafe, + imt_mod_freelook, + imt_mod_strafe, + imt_mod_lookstrafe, + imt_mod_strafe, + imt_mod_freelook, + imt_mod_strafe, + imt_mod_lookstrafe, + imt_mod_strafe + ); + } + ); + }, + { + name = key_demo; + imts = ( + { + name = imt_demo; + }, + ); + } + ); + devices = ( + { + name = mouse; + devname = core:mouse; + num_axes = 2; + num_buttons = 32; + axes = ( + { + imt = imt_mod; + num = 0; + axis = move.yaw; + min = 0; + max = 0; + minzone = 0; + maxzone = 0; + deadzone = 0; + curve = 1; + scale = 1; + }, + { + imt = imt_mod_strafe; + num = 0; + axis = move.side; + min = 0; + max = 0; + minzone = 0; + maxzone = 0; + deadzone = 0; + curve = 1; + scale = 1; + }, + { + imt = imt_mod_lookstrafe; + num = 0; + axis = move.side; + min = 0; + max = 0; + minzone = 0; + maxzone = 0; + deadzone = 0; + curve = 1; + scale = 1; + }, + { + imt = imt_mod; + num = 1; + axis = move.forward; + min = 0; + max = 0; + minzone = 0; + maxzone = 0; + deadzone = 0; + curve = 1; + scale = 1; + }, + { + imt = imt_mod_freelook; + num = 1; + axis = move.pitch; + min = 0; + max = 0; + minzone = 0; + maxzone = 0; + deadzone = 0; + curve = 1; + scale = 1; + } + ); + }, + { + name = key; + devname = core:keyboard; + num_axes = 0; + num_buttons = 256; + buttons = ( + { + imt = imt_demo; + num = 41; + binding = toggleconsole; + }, + { + imt = imt_demo; + num = 61; + binding = menu_load; + }, + { + imt = imt_demo; + num = 67; + binding = "echo Quickloading...; wait; load quick"; + }, + { + imt = imt_demo; + num = 68; + binding = quit; + }, + ); + } + ); + }; +} diff --git a/libs/client/hud.c b/libs/client/hud.c new file mode 100644 index 000000000..52b6c4048 --- /dev/null +++ b/libs/client/hud.c @@ -0,0 +1,291 @@ +/* + hud.c + + Heads-up display bar + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2022 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "QF/cvar.h" +#include "QF/gib.h" +#include "QF/screen.h" +#include "QF/render.h" +#include "QF/plugin/vid_render.h" +#include "QF/ui/canvas.h" +#include "QF/ui/passage.h" +#include "QF/ui/view.h" + +#include "compat.h" + +#include "client/hud.h" +#include "client/screen.h" + +ecs_system_t hud_psgsys; +uint32_t hud_canvas; +int hud_sb_lines; + +int hud_sbar; +static cvar_t hud_sbar_cvar = { + .name = "hud_sbar", + .description = + "status bar mode: 0 = hud, 1 = oldstyle", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &hud_sbar }, +}; +grav_t hud_scoreboard_gravity = grav_center; +#if 0 +static cvar_t hud_scoreboard_gravity_cvar = { + .name = "hud_scoreboard_gravity", + .description = + "control placement of scoreboard overlay: center, northwest, north, " + "northeast, west, east, southwest, south, southeast", + .default_value = "center", + .flags = CVAR_ARCHIVE, + .value = { .type = &grav_t_type, .value = &hud_scoreboard_gravity }, +}; +#endif +int hud_swap; +static cvar_t hud_swap_cvar = { + .name = "hud_swap", + .description = + "new HUD on left side?", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &hud_swap }, +}; +int hud_fps; +static cvar_t hud_fps_cvar = { + .name = "hud_fps", + .description = + "display realtime frames per second", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &hud_fps }, +}; +int hud_ping; +static cvar_t hud_ping_cvar = { + .name = "hud_ping", + .description = + "display current ping to server", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &hud_ping }, +}; +int hud_pl; +static cvar_t hud_pl_cvar = { + .name = "hud_pl", + .description = + "display current packet loss to server", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &hud_pl }, +}; +int hud_time; +static cvar_t hud_time_cvar = { + .name = "hud_time", + .description = + "display the current time", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &hud_time }, +}; +int hud_debug; +static cvar_t hud_debug_cvar = { + .name = "hud_debug", + .description = + "display hud view outlines for debugging", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &hud_debug }, +}; + +view_t hud_canvas_view; + +static void +hud_add_outlines (view_t view, byte color) +{ + Ent_SetComponent (view.id, canvas_outline, view.reg, &color); + uint32_t count = View_ChildCount (view); + for (uint32_t i = 0; i < count; i++) { + hud_add_outlines (View_GetChild (view, i), color); + } +} + +static void +hud_remove_outlines (view_t view) +{ + Ent_RemoveComponent (view.id, canvas_outline, view.reg); + uint32_t count = View_ChildCount (view); + for (uint32_t i = 0; i < count; i++) { + hud_remove_outlines (View_GetChild (view, i)); + } +} + +static void +hud_debug_f (void *data, const cvar_t *cvar) +{ + if (!View_Valid (hud_canvas_view)) { + return; + } + if (hud_debug) { + hud_add_outlines (hud_canvas_view, 0x6f); + } else { + hud_remove_outlines (hud_canvas_view); + } +} + +static void +hud_sbar_f (void *data, const cvar_t *cvar) +{ + HUD_Calc_sb_lines (*r_data->scr_viewsize); + SCR_SetBottomMargin (hud_sbar ? hud_sb_lines : 0); +#if 0//XXX + if (hud_sbar) { + view_remove (hud_main_view, hud_main_view->children[0]); + } else { + view_remove (hud_main_view, hud_main_view->children[0]); + view_insert (hud_main_view, hud_canvas_view, 0); + } +#endif +} + +static void +hud_swap_f (void *data, const cvar_t *cvar) +{ +#if 0//XXX + if (hud_swap) { + //FIXME why is this needed for nq but not for qw? + hud_armament_view->children[0]->gravity = grav_northwest; + hud_armament_view->children[1]->gravity = grav_southeast; + view_setgravity (hud_armament_view, grav_southwest); + view_setgravity (hud_stuff_view, grav_southeast); + } else { + //FIXME why is this needed for nq but not for qw? + hud_armament_view->children[0]->gravity = grav_northeast; + hud_armament_view->children[1]->gravity = grav_southwest; + view_setgravity (hud_armament_view, grav_southeast); + view_setgravity (hud_stuff_view, grav_southwest); + } + view_move (hud_armament_view, hud_armament_view->xpos, + hud_armament_view->ypos); + view_move (hud_stuff_view, hud_stuff_view->xpos, hud_stuff_view->ypos); +#endif +} +#if 0 +static void +hud_scoreboard_gravity_f (void *data, const cvar_t *cvar) +{ + if (View_Valid (hud_overlay_view)) { + View_SetGravity (hud_overlay_view, hud_scoreboard_gravity); + } +} +#endif +static void +C_GIB_HUD_Enable_f (void) +{ + //hud_canvas_view->visible = 1; +} + +static void +C_GIB_HUD_Disable_f (void) +{ + //hud_canvas_view->visible = 0; +} + +void +HUD_Init (ecs_registry_t *reg) +{ + hud_psgsys = (ecs_system_t) { + .reg = reg, + .base = ECS_RegisterComponents (reg, passage_components, + passage_comp_count), + }; + + // register GIB builtins + GIB_Builtin_Add ("HUD::enable", C_GIB_HUD_Enable_f); + GIB_Builtin_Add ("HUD::disable", C_GIB_HUD_Disable_f); +} + +void +HUD_Init_Cvars (void) +{ + Cvar_Register (&hud_fps_cvar, 0, 0); + Cvar_MakeAlias ("show_fps", &hud_fps_cvar); + Cvar_Register (&hud_ping_cvar, 0, 0); + Cvar_Register (&hud_pl_cvar, 0, 0); + Cvar_Register (&hud_time_cvar, 0, 0); + Cvar_Register (&hud_debug_cvar, hud_debug_f, 0); + + Cvar_Register (&hud_sbar_cvar, hud_sbar_f, 0); + Cvar_Register (&hud_swap_cvar, hud_swap_f, 0); +#if 0 + Cvar_Register (&hud_scoreboard_gravity_cvar, hud_scoreboard_gravity_f, 0); +#endif +} + +void +HUD_CreateCanvas (canvas_system_t canvas_sys) +{ + hud_canvas = Canvas_New (canvas_sys); + hud_canvas_view = Canvas_GetRootView (canvas_sys, hud_canvas); + View_SetPos (hud_canvas_view, 0, 0); + View_SetLen (hud_canvas_view, viddef.width, viddef.height); + View_SetGravity (hud_canvas_view, grav_northwest); + View_SetVisible (hud_canvas_view, 1); +} + +void +HUD_Calc_sb_lines (int view_size) +{ +#if 0//XXX + int stuff_y; + + if (view_size >= 120) { + hud_sb_lines = 0; + stuff_y = 0; + } else if (view_size >= 110) { + hud_sb_lines = 24; + hud_inventory_view->visible = 0; + hud_armament_view->visible = 0; + stuff_y = 32; + } else { + hud_sb_lines = 48; + hud_inventory_view->visible = 1; + hud_armament_view->visible = 1; + stuff_y = 48; + } + if (hud_sb_lines) { + hud_canvas_view->visible = 1; + view_resize (hud_canvas_view, hud_canvas_view->xlen, hud_sb_lines); + } else { + hud_canvas_view->visible = 0; + } + view_move (hud_stuff_view, hud_stuff_view->xpos, stuff_y); +#endif +} diff --git a/qw/source/locs.c b/libs/client/locs.c similarity index 77% rename from qw/source/locs.c rename to libs/client/locs.c index 2f94da0f8..3ffd5f4ca 100644 --- a/qw/source/locs.c +++ b/libs/client/locs.c @@ -41,14 +41,23 @@ #include -#include "QF/locs.h" +#include "QF/mathlib.h" +#include "QF/render.h" #include "QF/qtypes.h" #include "QF/quakefs.h" #include "QF/sys.h" #include "QF/va.h" -#include "client.h" +#include "QF/simd/vec4f.h" + +#include "QF/plugin/vid_render.h" //FIXME + #include "compat.h" +#include "d_iface.h" //FIXME part_tex_smoke and part_tex_dot + +#include "client/effects.h" +#include "client/locs.h" +#include "client/particles.h" #define LOCATION_BLOCK 128 // 128 locations per block. @@ -58,7 +67,7 @@ int locations_count = 0; int location_blocks = 0; int -locs_nearest (const vec3_t loc) +locs_nearest (vec4f_t loc) { float best_distance = 9999999, distance; int i, j = -1; @@ -66,7 +75,8 @@ locs_nearest (const vec3_t loc) for (i = 0; i < locations_count; i++) { cur = locations[i]; - distance = VectorDistance_fast (loc, cur->loc); + vec4f_t d = loc - cur->loc; + distance = dotf (d, d)[0]; if ((distance < best_distance)) { best_distance = distance; j = i; @@ -76,7 +86,7 @@ locs_nearest (const vec3_t loc) } location_t * -locs_find (const vec3_t target) +locs_find (vec4f_t target) { int i; @@ -105,7 +115,7 @@ locs_more (void) } void -locs_add (const vec3_t location, const char *name) +locs_add (vec4f_t location, const char *name) { int num; @@ -132,10 +142,10 @@ locs_load (const char *filename) const char *tmp; char *t1, *t2; const char *line; - vec3_t loc; + vec4f_t loc = { 0, 0, 0, 1 }; QFile *file; - tmp = va ("maps/%s", filename); + tmp = va (0, "maps/%s", filename); file = QFS_FOpenFile (tmp); if (!file) { Sys_Printf ("Couldn't load %s\n", tmp); @@ -188,14 +198,14 @@ locs_reset (void) } void -locs_save (const char *filename, qboolean gz) +locs_save (const char *filename, bool gz) { int i; QFile *locfd; if (gz) { if (strcmp (QFS_FileExtension (filename), ".gz") != 0) - filename = va ("%s.gz", filename); + filename = va (0, "%s.gz", filename); locfd = QFS_Open (filename, "z9w+"); } else locfd = QFS_Open (filename, "w+"); @@ -211,7 +221,7 @@ locs_save (const char *filename, qboolean gz) } void -locs_mark (const vec3_t loc, const char *desc) +locs_mark (vec4f_t loc, const char *desc) { locs_add (loc, desc); Sys_Printf ("Marked current location: %s\n", desc); @@ -224,14 +234,14 @@ locs_mark (const vec3_t loc, const char *desc) call with NULL description to modify location vectors */ void -locs_edit (const vec3_t loc, const char *desc) +locs_edit (vec4f_t loc, const char *desc) { int i; if (locations_count) { i = locs_nearest (loc); if (!desc) { - VectorCopy (loc, locations[i]->loc); + locations[i]->loc = loc; Sys_Printf ("Moving location marker for %s\n", locations[i]->name); } else { @@ -245,7 +255,7 @@ locs_edit (const vec3_t loc, const char *desc) } void -locs_del (const vec3_t loc) +locs_del (vec4f_t loc) { int i; @@ -275,3 +285,36 @@ map_to_loc (const char *mapname, char *filename) t1++; strcpy (t1, "loc"); } + +void +locs_draw (double time, vec4f_t simorg) +{ + //FIXME custom ent rendering code would be nice + dlight_t *dl; + location_t *nearloc; + vec4f_t trueloc; + vec4f_t zero = {}; + int i; + + nearloc = locs_find (simorg); + if (nearloc) { + dl = R_AllocDlight (4096); + if (dl) { + VectorCopy (nearloc->loc, dl->origin); + dl->radius = 200; + dl->die = time + 0.1; + dl->color[0] = 0; + dl->color[1] = 1; + dl->color[2] = 0; + dl->color[3] = 0.7; + } + trueloc = nearloc->loc; + clp_funcs->Particle_New (pt_smokecloud, part_tex_smoke, trueloc, 2.0, + zero, time + 9.0, 254, + 0.25 + qfrandom (0.125), 0.0); + for (i = 0; i < 15; i++) + clp_funcs->Particle_NewRandom (pt_fallfade, part_tex_dot, trueloc, + 12, 0.7, 96, time + 5.0, + 104 + (rand () & 7), 1.0, 0.0); + } +} diff --git a/libs/client/old_keys.c b/libs/client/old_keys.c new file mode 100644 index 000000000..2fea7d733 --- /dev/null +++ b/libs/client/old_keys.c @@ -0,0 +1,205 @@ +/* + old_keys.c + + translations from old to new keynames + + Copyright (C) 2001 Bill Currie + + Author: Bill Currie + Date: 2001/8/16 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#if defined(_WIN32) && defined(HAVE_MALLOC_H) +#include +#endif + +#include +#include + +#include "qfalloca.h" + +#include "QF/hash.h" +#include "QF/qtypes.h" +#include "QF/sys.h" + +#include "old_keys.h" + +typedef struct { + const char *old_name; + const char *new_name; +} old_keyname_t; + +old_keyname_t old_keynames[] = { + //{"ESCAPE", "key button 0x01"}, binding not allowed + {"1", "key button 0x02"}, + {"2", "key button 0x03"}, + {"3", "key button 0x04"}, + {"4", "key button 0x05"}, + {"5", "key button 0x06"}, + {"6", "key button 0x07"}, + {"7", "key button 0x08"}, + {"8", "key button 0x09"}, + {"9", "key button 0x0a"}, + {"0", "key button 0x0b"}, + {"-", "key button 0x0c"}, + {"=", "key button 0x0d"}, + {"BACKSPACE", "key button 0x0e"}, + {"TAB", "key button 0x0f"}, + + {"Q", "key button 0x10"}, + {"W", "key button 0x11"}, + {"E", "key button 0x12"}, + {"R", "key button 0x13"}, + {"T", "key button 0x14"}, + {"Y", "key button 0x15"}, + {"U", "key button 0x16"}, + {"I", "key button 0x17"}, + {"O", "key button 0x18"}, + {"P", "key button 0x19"}, + {"[", "key button 0x1a"}, + {"]", "key button 0x1b"}, + {"ENTER", "key button 0x1c"}, + {"CTRL", "key button 0x1d"}, + {"A", "key button 0x1e"}, + {"S", "key button 0x1f"}, + + {"D", "key button 0x20"}, + {"F", "key button 0x21"}, + {"G", "key button 0x22"}, + {"H", "key button 0x23"}, + {"J", "key button 0x24"}, + {"K", "key button 0x25"}, + {"L", "key button 0x26"}, + {"SEMICOLON", "key button 0x27"}, + {"'", "key button 0x28"}, + {"`", "key button 0x29"}, + {"SHIFT", "key button 0x2a"}, + {"\\", "key button 0x2b"}, + {"Z", "key button 0x2c"}, + {"X", "key button 0x2d"}, + {"C", "key button 0x2e"}, + {"V", "key button 0x2f"}, + + {"B", "key button 0x30"}, + {"N", "key button 0x31"}, + {"M", "key button 0x32"}, + {",", "key button 0x33"}, + {".", "key button 0x34"}, + {"/", "key button 0x35"}, + // quake had no right shift + {"*", "key button 0x37"}, + {"ALT", "key button 0x38"}, + {"SPACE", "key button 0x39"}, + //{"CAPSLOCK", "key button 0x3a"}, quake had no way of binding capslock + {"F1", "key button 0x3b"}, + {"F2", "key button 0x3c"}, + {"F3", "key button 0x3d"}, + {"F4", "key button 0x3e"}, + {"F5", "key button 0x3f"}, + + {"F6", "key button 0x40"}, + {"F7", "key button 0x41"}, + {"F8", "key button 0x42"}, + {"F9", "key button 0x43"}, + {"F10", "key button 0x44"}, + {"PAUSE", "key button 0x45"}, + {"SCRLCK", "key button 0x46"}, + {"HOME", "key button 0x47"}, + {"UPARROW", "key button 0x48"}, + {"PGUP", "key button 0x49"}, + //{"-", "key button 0x4a"}, quake had no way of binding kp - + {"LEFTARROW", "key button 0x4b"}, + //{"5", "key button 0x4c"}, quake had no way of binding kp 5 + {"RIGHTARROW", "key button 0x4d"}, + {"+", "key button 0x4e"}, + {"END", "key button 0x4f"}, + {"DOWNARROW", "key button 0x50"}, + {"PGDN", "key button 0x51"}, + {"INS", "key button 0x52"}, + {"DEL", "key button 0x53"}, + // nothing 0x54 + // nothing 0x55 + // nothing 0x56 + {"F11", "key button 0x57"}, + {"F12", "key button 0x58"}, + + {";", "key button 0x27"}, + {":", "key button 0x27"}, + + {"MOUSE1", "mouse button 0"}, + {"MOUSE2", "mouse button 2"}, + {"MOUSE3", "mouse button 1"}, + {"MOUSE6", "mouse button 5"}, + {"MOUSE7", "mouse button 6"}, + + {"MWHEELUP", "mouse button 3"}, + {"MWHEELDOWN", "mouse button 4"}, + + {0, 0} +}; + +hashtab_t *old_key_table; + +static const char * +ok_get_key (const void *_ok, void *unused) +{ + old_keyname_t *ok = (old_keyname_t *)_ok; + return ok->old_name; +} + +void +OK_Init (void) +{ + old_keyname_t *ok; + + old_key_table = Hash_NewTable (1021, ok_get_key, 0, 0, 0); + for (ok = old_keynames; ok->old_name; ok++) + Hash_Add (old_key_table, ok); +} + +const char * +OK_TranslateKeyName (const char *name) +{ + old_keyname_t *ok; + char *uname = alloca (strlen (name) + 1); + const char *s = name; + char *d = uname; + + while ((*d++ = toupper ((byte) *s))) + s++; + ok = Hash_Find (old_key_table, uname); + if (!ok) { + Sys_Printf ("unknown old keyname: %s\n", uname); + return 0; + } + return ok->new_name; +} diff --git a/libs/video/renderer/particles.part b/libs/client/particles.part similarity index 100% rename from libs/video/renderer/particles.part rename to libs/client/particles.part diff --git a/libs/client/sbar.c b/libs/client/sbar.c new file mode 100644 index 000000000..4e90f6072 --- /dev/null +++ b/libs/client/sbar.c @@ -0,0 +1,2596 @@ +/* + sbar.c + + Status bar + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "QF/cmd.h" +#include "QF/console.h" +#include "QF/cvar.h" +#include "QF/draw.h" +#include "QF/dstring.h" +#include "QF/gib.h" +#include "QF/info.h" +#include "QF/quakefs.h" +#include "QF/screen.h" +#include "QF/sys.h" +#include "QF/va.h" +#include "QF/vid.h" +#include "QF/wad.h" + +#include "QF/plugin/console.h" +#include "QF/plugin/vid_render.h" + +#include "QF/ui/canvas.h" +#include "QF/ui/passage.h" +#include "QF/ui/view.h" + +#include "compat.h" + +#include "client/hud.h" +#include "client/sbar.h" +#include "client/screen.h" +#include "client/state.h" +#include "client/world.h" + +#include "gamedefs.h" + +int sb_updates; // if >= vid.numpages, no update needed +static int sb_view_size; +static int fps_count; + +static const char *sbar_levelname; +static const char *sbar_servername; +static player_info_t *sbar_players; +static int *sbar_stats; +static float *sbar_item_gettime; +static double sbar_time; +static double sbar_completed_time; +static double sbar_faceanimtime; +static int sbar_maxplayers; +static int sbar_playernum; +static int sbar_viewplayer; +static int sbar_spectator; +static int sbar_autotrack = -1; +static int sbar_teamplay; +static int sbar_gametype; +static int sbar_active; +static int sbar_intermission; + +static view_t hud_overlay_view; +static view_t hud_stuff_view; +static view_t hud_time_view; +static view_t hud_fps_view; +static view_t hud_ping_view; +static view_t hud_pl_view; + +static view_t intermission_view; +static view_t intermission_time; +static view_t intermission_secr; +static view_t intermission_kill; +static view_t hud_view; +static view_t hud_miniteam; +static view_t sbar_main; +static view_t hud_minifrags; // child of sbar_main for positioning +static view_t sbar_inventory; +static view_t sbar_frags; +static view_t sbar_sigils; +static view_t sbar_items; +static view_t sbar_armament; +static view_t sbar_weapons; +static view_t sbar_miniammo; +static view_t sbar_statusbar; +static view_t sbar_armor; +static view_t sbar_face; +static view_t sbar_health; +static view_t sbar_ammo; +static view_t sbar_solo; +static view_t sbar_solo_monsters; +static view_t sbar_solo_secrets; +static view_t sbar_solo_time; +static view_t sbar_solo_anchor; +static view_t sbar_solo_name; +static view_t sbar_tile[2]; +static view_t sbar_tile[2]; +static view_t spectator_view; +static view_t deathmatch_view; + +typedef struct view_def_s { + view_t *view; + struct { + int x; + int y; + int w; + int h; + } rect; + grav_t gravity; + view_t *parent; + int count; + int xstep; + int ystep; + struct view_def_s *subviews; +} view_def_t; + +// used for "current view" +static view_t pseudo_parent = nullview; +static view_def_t frags_defs[] = { + {0, {4, 1, 28, 4}, grav_northwest, &pseudo_parent, 1, 0, 0}, + {0, {4, 5, 28, 3}, grav_northwest, &pseudo_parent, 1, 0, 0}, + {0, {6, 0, 24, 8}, grav_northwest, &pseudo_parent, 1, 0, 0}, + {0, {0, 0, 34, 8}, grav_northwest, &pseudo_parent, 1, 0, 0}, + {} +}; +static view_def_t minifrags_defs[] = { + {0, { 2, 1, 37, 3}, grav_northwest, &pseudo_parent, 1, 0, 0}, + {0, { 2, 4, 37, 4}, grav_northwest, &pseudo_parent, 1, 0, 0}, + {0, { 8, 0, 24, 8}, grav_northwest, &pseudo_parent, 1, 0, 0}, + {0, { 0, 0, 40, 8}, grav_northwest, &pseudo_parent, 1, 0, 0}, + // teamplay team, name + {0, {48, 0, 32, 8}, grav_northwest, &pseudo_parent, 1, 0, 0}, + {0, {88, 0,104, 8}, grav_northwest, &pseudo_parent, 1, 0, 0}, + // name + {0, {48, 0,128, 8}, grav_northwest, &pseudo_parent, 1, 0, 0}, + {} +}; +static view_def_t miniteam_defs[] = { + {0, { 0, 0, 32, 8}, grav_northwest, &pseudo_parent, 1, 0, 0}, + {0, {40, 0, 40, 8}, grav_northwest, &pseudo_parent, 1, 0, 0}, + {0, {-8, 0, 48, 8}, grav_northwest, &pseudo_parent, 1, 0, 0}, + {} +}; + +static view_def_t sbar_defs[] = { + {&hud_overlay_view, { 0, 0,320,200}, grav_center, &hud_canvas_view}, + {&intermission_view, { 0, 0,320,200}, grav_northwest, &hud_overlay_view}, + {0, {0, 24, 192, 24}, grav_north, &intermission_view, 1, 0, 0}, + {0, {0, 56, 160, 144}, grav_northwest, &intermission_view, 1, 0, 0}, + {0, {0, 16, 288, 24}, grav_north, &intermission_view, 1, 0, 0}, + {&intermission_time, {160,64,134,24}, grav_northwest, &intermission_view}, + {0, {0, 0, 24, 24}, grav_northwest, &intermission_time, 3, 24, 0}, + {0, {74, 0, 16, 24}, grav_northwest, &intermission_time, 1, 0, 0}, + {0, {86, 0, 24, 24}, grav_northwest, &intermission_time, 2, 24, 0}, + {&intermission_secr, {160,104,152,24}, grav_northwest, &intermission_view}, + {0, {0, 0, 24, 24}, grav_northwest, &intermission_secr, 3, 24, 0}, + {0, {72, 0, 16, 24}, grav_northwest, &intermission_secr, 1, 0, 0}, + {0, {80, 0, 24, 24}, grav_northwest, &intermission_secr, 3, 24, 0}, + {&intermission_kill, {160,144,152,24}, grav_northwest, &intermission_view}, + {0, {0, 0, 24, 24}, grav_northwest, &intermission_kill, 3, 24, 0}, + {0, {72, 0, 16, 24}, grav_northwest, &intermission_kill, 1, 0, 0}, + {0, {80, 0, 24, 24}, grav_northwest, &intermission_kill, 3, 24, 0}, + + {&hud_view, { 0, 0,320, 48}, grav_south, &hud_canvas_view}, + {&hud_miniteam, { 0, 0, 96, 48}, grav_southeast, &hud_view}, + {0, {0,0,96,8}, grav_northwest, &hud_miniteam, 6, 0, 8, miniteam_defs}, + {&sbar_main, { 0, 0,320, 48}, grav_south, &hud_view}, + {&hud_minifrags, {-192, 0,192, 48}, grav_southeast, &sbar_main}, + {0, {0,0,192,8}, grav_northwest, &hud_minifrags, 6, 0, 8, minifrags_defs}, + + {&sbar_inventory, { 0, 0,320, 24}, grav_northwest, &sbar_main}, + {&sbar_frags, { 0, 0,130, 8}, grav_northeast, &sbar_inventory}, + {&sbar_sigils, { 0, 0, 32, 16}, grav_southeast, &sbar_inventory}, + {&sbar_items, { 32, 0, 96, 16}, grav_southeast, &sbar_inventory}, + //NOTE sbar_armament moves and gets layed out again on hud_sbar change + {&sbar_armament, { 0, 0,202, 24}, grav_northwest, &sbar_inventory}, + {&sbar_weapons, { 0, 0,192, 16}, grav_southwest, &sbar_armament}, + {&sbar_miniammo, { 0, 0, 32, 8}, grav_northwest, &sbar_armament}, + + {&sbar_statusbar, { 0, 0,320, 24}, grav_southwest, &sbar_main}, + {&sbar_armor, { 0, 0, 96, 24}, grav_northwest, &sbar_statusbar}, + {&sbar_face, {112, 0, 24, 24}, grav_northwest, &sbar_statusbar}, + {&sbar_health, {136, 0, 72, 24}, grav_northwest, &sbar_statusbar}, + {&sbar_ammo, {224, 0, 96, 24}, grav_northwest, &sbar_statusbar}, + {&sbar_tile[0], { 0, 0, 0, 48}, grav_southwest, &sbar_main}, + {&sbar_tile[1], { 0, 0, 0, 48}, grav_southeast, &sbar_main}, + {&sbar_solo, { 0, 0,320, 24}, grav_southwest, &sbar_main}, + {&sbar_solo_monsters, { 8, 4,136, 8}, grav_northwest, &sbar_solo}, + {&sbar_solo_secrets, { 8,12,136, 8}, grav_northwest, &sbar_solo}, + {&sbar_solo_time, {184, 4, 96, 8}, grav_northwest, &sbar_solo}, + {&sbar_solo_anchor, {232,12, 0, 8}, grav_northwest, &sbar_solo}, + {&sbar_solo_name, { 0, 0, 0, 8}, grav_center, &sbar_solo_anchor}, + {0, { 0, 0, 32, 8}, grav_northwest, &sbar_frags, 4, 32, 0, frags_defs}, + {0, { 0, 0, 8, 16}, grav_northwest, &sbar_sigils, 4, 8, 0}, + // for rogue ctf "face" + {0, { 1, 3, 22, 9}, grav_northwest, &sbar_face, 2, 0, 9}, + {0, { 0, 3, 24, 8}, grav_northwest, &sbar_face, 1, 0, 0}, + {0, { 0, 0, 24, 24}, grav_northwest, &sbar_armor, 4, 24, 0}, + {0, { 0, 0, 24, 24}, grav_northwest, &sbar_ammo, 4, 24, 0}, + // hipnotic and rogue have 8 item slots and no sigils, so the two extra + // items overlap the sigils view + {0, { 0, 0, 16, 16}, grav_northwest, &sbar_items, 8, 16, 0}, + {0, { 0, 0, 24, 16}, grav_northwest, &sbar_weapons, 7, 24, 0}, + // hipnotic adds two extra weapons that overlap the keys views (which + // get moved for hipnotic). + {0, { 0, 0,176, 16}, grav_northwest, &sbar_weapons, 2, 24, 0}, + {0, { 0, 0, 24, 24}, grav_northwest, &sbar_health, 3, 24, 0}, + {0, {10, 0, 24, 8}, grav_northwest, &sbar_miniammo, 4, 48, 0}, + + {&spectator_view, { 0, 0,320, 32}, grav_south, &hud_canvas_view}, + {0, { 0, 0, 312, 8}, grav_north, &spectator_view, 1, 0, 0}, + {0, { 0, 8, 320, 24}, grav_northwest, &spectator_view, 1, 0, 0}, + {0, { 0, 12, 112, 8}, grav_north, &spectator_view, 1, 0, 0}, + {0, { 0, 20, 232, 8}, grav_north, &spectator_view, 1, 0, 0}, + {&deathmatch_view, { 0, 0,320, 200}, grav_center, &hud_canvas_view}, + + {} +}; + +static draw_charbuffer_t *time_buff; +static draw_charbuffer_t *fps_buff; +static draw_charbuffer_t *ping_buff; +static draw_charbuffer_t *pl_buff; + +static draw_charbuffer_t *spec_buff[4];//0,1 no track, 2 lost track, 3 tracking + +static draw_charbuffer_t *solo_monsters; +static draw_charbuffer_t *solo_secrets; +static draw_charbuffer_t *solo_time; +static draw_charbuffer_t *solo_name; + +static ecs_system_t sbar_viewsys; + +static view_t +sbar_view (int x, int y, int w, int h, grav_t gravity, view_t parent) +{ + view_t view = View_New (sbar_viewsys, parent); + View_SetPos (view, x, y); + View_SetLen (view, w, h); + View_SetGravity (view, gravity); + View_SetVisible (view, 1); + return view; +} + +static inline void +sbar_setcomponent (view_t view, uint32_t comp, const void *data) +{ + Ent_SetComponent (view.id, comp, view.reg, data); +} + +static inline int +sbar_hascomponent (view_t view, uint32_t comp) +{ + return Ent_HasComponent (view.id, comp, view.reg); +} + +static inline void * +sbar_getcomponent (view_t view, uint32_t comp) +{ + return Ent_GetComponent (view.id, comp, view.reg); +} + +static inline void +sbar_remcomponent (view_t view, uint32_t comp) +{ + Ent_RemoveComponent (view.id, comp, view.reg); +} + +static inline void +set_update (view_t view, canvas_update_f func) +{ + sbar_setcomponent (view, canvas_updateonce, &func); +} + +#define STAT_MINUS 10 // num frame for '-' stats digit + +static qpic_t *sb_nums[2][11]; +static qpic_t *sb_colon, *sb_slash; +static qpic_t *sb_ibar[2]; +static int sb_ibar_index; +static canvas_subpic_t sb_miniammo[4]; +static qpic_t *sb_sbar; +static qpic_t *sb_scorebar; + +// 0 is active, 1 is owned, 2-6 are flashes +// 0-6 id, 7-9 hip (laser, mjolnir, prox), 7-11 rogue powerups +static int sb_weapon_count; +static int sb_weapon_view_count; +static int sb_game; +static canvas_subpic_t sb_weapons[7][12]; +static qpic_t *sb_ammo[7]; // rogue adds 3 ammo types +static qpic_t *sb_sigil[4]; +static qpic_t *sb_armor[3]; +// 0 is owned, 1-5 are flashes +static int sb_item_count; +static qpic_t *sb_items[8][32]; + +static qpic_t *sb_faces[7][2]; // 0 is gibbed, 1 is dead, 2-6 are alive + // 0 is static, 1 is temporary animation +static qpic_t *sb_face_invis; +static qpic_t *sb_face_quad; +static qpic_t *sb_face_invuln; +static qpic_t *sb_face_invis_invuln; + +bool sbar_showscores; +static bool sbar_showteamscores; + +static int sb_lines; // scan lines to draw + +static qpic_t *rsb_teambord; // PGM 01/19/97 - team color border + +//static bool largegame = false; + +char *fs_fraglog; +static cvar_t fs_fraglog_cvar = { + .name = "fs_fraglog", + .description = + "Filename of the automatic frag-log.", + .default_value = "qw-scores.log", + .flags = CVAR_ARCHIVE, + .value = { .type = 0, .value = &fs_fraglog }, +}; +int cl_fraglog; +static cvar_t cl_fraglog_cvar = { + .name = "cl_fraglog", + .description = + "Automatic fraglogging, non-zero value will switch it on.", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &cl_fraglog }, +}; +int hud_scoreboard_uid; +static cvar_t hud_scoreboard_uid_cvar = { + .name = "hud_scoreboard_uid", + .description = + "Set to 1 to show uid instead of ping. Set to 2 to show both.", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &hud_scoreboard_uid }, +}; +float scr_centertime; +static cvar_t scr_centertime_cvar = { + .name = "scr_centertime", + .description = + "How long in seconds screen hints are displayed", + .default_value = "2", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &scr_centertime }, +}; +float scr_printspeed; +static cvar_t scr_printspeed_cvar = { + .name = "scr_printspeed", + .description = + "How fast the text is displayed at the end of the single player " + "episodes", + .default_value = "8", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &scr_printspeed }, +}; + +static void +viewsize_f (int view_size) +{ + sb_view_size = view_size; + HUD_Calc_sb_lines (view_size); + if (hud_sbar) { + SCR_SetBottomMargin (hud_sbar ? sb_lines : 0); + } +} + +static int +Sbar_ColorForMap (int m) +{ + return (bound (0, m, 13) * 16) + 8; +} + +static int +write_charbuff_cl (draw_charbuffer_t *buffer, int x, int y, const char *str) +{ + char *dst = buffer->chars; + int count = buffer->width - x; + int chars = 0; + dst += y * buffer->width + x; + while (*str && count-- > 0) { + *dst++ = *str++; + chars++; + } + while (count-- > 0) { + *dst++ = ' '; + } + return chars; +} + +static int +write_charbuff (draw_charbuffer_t *buffer, int x, int y, const char *str) +{ + char *dst = buffer->chars; + int count = buffer->width - x; + int chars = 0; + dst += y * buffer->width + x; + while (*str && count-- > 0) { + *dst++ = *str++; + chars++; + } + return chars; +} + +static void +draw_num (view_t *view, int num, int digits, int color) +{ + char str[12]; + char *ptr; + int l, frame, x = 0; + + if (num > 999999999) + num = 999999999; + + l = snprintf (str, sizeof (str), "%d", num); + ptr = str; + if (l > digits) + ptr += (l - digits); + while (digits > l) { + sbar_remcomponent (view[x++], canvas_pic); + digits--; + } + + while (*ptr) { + if (*ptr == '-') + frame = STAT_MINUS; + else + frame = *ptr - '0'; + + sbar_setcomponent (view[x++], canvas_pic, &sb_nums[color][frame]); + ptr++; + } +} + +static void +draw_smallnum (view_t view, int n, int packed, int colored) +{ + void *comp = sbar_getcomponent (view, canvas_charbuff); + __auto_type charbuff = *(draw_charbuffer_t **) comp; + char num[4]; + + packed = packed != 0; // ensure 0 or 1 + + n = bound (-99, n, 999); + snprintf (num, sizeof (num), "%3d", n); + for (int i = 0; i < 3; i++) { + charbuff->chars[i] = num[i] - (colored && num[i] != ' ' ? '0' - 18 : 0); + } +} + +static void +draw_miniammo (view_t view) +{ + int i, count; + + // ammo counts + for (i = 0; i < 4; i++) { + view_t v = View_GetChild (view, i); + count = sbar_stats[STAT_SHELLS + i]; + draw_smallnum (v, count, 0, 1); + } +} + +static void +draw_ammo (view_t view) +{ + qpic_t *pic = 0; + if (sb_game) { + if (sbar_stats[STAT_ITEMS] & RIT_SHELLS) + pic = sb_ammo[0]; + else if (sbar_stats[STAT_ITEMS] & RIT_NAILS) + pic = sb_ammo[1]; + else if (sbar_stats[STAT_ITEMS] & RIT_ROCKETS) + pic = sb_ammo[2]; + else if (sbar_stats[STAT_ITEMS] & RIT_CELLS) + pic = sb_ammo[3]; + else if (sbar_stats[STAT_ITEMS] & RIT_LAVA_NAILS) + pic = sb_ammo[4]; + else if (sbar_stats[STAT_ITEMS] & RIT_MULTI_ROCKETS) + pic = sb_ammo[5]; + else if (sbar_stats[STAT_ITEMS] & RIT_PLASMA_AMMO) + pic = sb_ammo[6]; + } else { + if (sbar_stats[STAT_ITEMS] & IT_SHELLS) + pic = sb_ammo[0]; + else if (sbar_stats[STAT_ITEMS] & IT_NAILS) + pic = sb_ammo[1]; + else if (sbar_stats[STAT_ITEMS] & IT_ROCKETS) + pic = sb_ammo[2]; + else if (sbar_stats[STAT_ITEMS] & IT_CELLS) + pic = sb_ammo[3]; + } + + view_t ammo = View_GetChild (view, 0); + if (pic) { + sbar_setcomponent (ammo, canvas_pic, &pic); + } else { + sbar_remcomponent (ammo, canvas_pic); + } + + view_t num[3] = { + View_GetChild (view, 1), + View_GetChild (view, 2), + View_GetChild (view, 3), + }; + draw_num (num, sbar_stats[STAT_AMMO], 3, sbar_stats[STAT_AMMO] <= 10); +} + +static int +calc_flashon (float time, int mask, int base) +{ + int flashon; + + flashon = (int) ((sbar_time - time) * 10); + if (flashon < 0) + flashon = 0; + if (flashon >= 10) { + if (sbar_stats[STAT_ACTIVEWEAPON] == mask) + flashon = 1; + else + flashon = 0; + } else { + flashon = (flashon % 5) + base; + } + return flashon; +} + +static void +draw_weapons (view_t view) +{ + int flashon, i; + static byte view_map[2][12] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 4 }, // id/hipnotic + { 0, 1, 2, 3, 4, 5, 6, 2, 3, 4, 5, 6 }, // rogue + }; + static byte item_map[2][12] = { + { 0, 1, 2, 3, 4, 5, 6, // id/hipnotic + HIT_LASER_CANNON_BIT, HIT_MJOLNIR_BIT, HIT_PROXIMITY_GUN_BIT }, + { 0, 1, 2, 3, 4, 5, 6, 12, 13, 14, 15, 16 }, // rogue + }; + static int active_map[2][12] = { + { [9] = HIT_PROXIMITY_GUN }, // id/hipnotic + { [7] = RIT_LAVA_NAILGUN, + [8] = RIT_LAVA_SUPER_NAILGUN, + [9] = RIT_MULTI_GRENADE, + [10] = RIT_MULTI_ROCKET, + [11] = RIT_PLASMA_GUN, + }, // rogue + }; + + for (i = 0; i < sb_weapon_count; i++) { + view_t weap = View_GetChild (view, view_map[sb_game][i]); + int mask = 1 << item_map[sb_game][i]; + float time = sbar_item_gettime[item_map[sb_game][i]]; + int active = active_map[sb_game][i]; + + if (sbar_stats[STAT_ITEMS] & mask) { + if ((sbar_stats[STAT_ACTIVEWEAPON] & active) == active) { + flashon = calc_flashon (time, mask, 2); + if (flashon > 1) + sb_updates = 0; // force update to remove flash + sbar_setcomponent (weap, canvas_subpic, + &sb_weapons[flashon][i]); + } + } else { + sbar_remcomponent (weap, canvas_subpic); + } + } +} + +static void +draw_items (view_t view) +{ + static byte ind_map[2][8] = { + { 17, 18, 19, 20, 21, 22, 25, 26 }, // id/hipnotic + { 17, 18, 19, 20, 21, 22, 29, 30 }, // rogue + }; + for (int i = 0; i < sb_item_count; i++) { + view_t item = View_GetChild (view, i); + int item_ind = ind_map[sb_game][i]; + if (sbar_stats[STAT_ITEMS] & (1 << item_ind)) { + int flashon = calc_flashon (sbar_item_gettime[item_ind], + -1, 1); + if (flashon > 1) + sb_updates = 0; // force update to remove flash + sbar_setcomponent (item, canvas_pic, &sb_items[flashon][i]); + } else { + sbar_remcomponent (item, canvas_pic); + } + } +} + +static void +draw_sigils (view_t view) +{ + for (int i = 0; i < 4; i++) { + view_t sigil = View_GetChild (view, i); + if (sbar_stats[STAT_ITEMS] & (1 << (28 + i))) { + sbar_setcomponent (sigil, canvas_pic, &sb_sigil[i]); + } else { + sbar_remcomponent (sigil, canvas_pic); + } + } +} + +typedef struct { + char team[16 + 1]; + int frags; + int players; + int plow, phigh, ptotal; +} team_t; + +team_t teams[MAX_PLAYERS]; +int teamsort[MAX_PLAYERS]; +int fragsort[MAX_PLAYERS]; +static view_t sb_views[MAX_PLAYERS]; +static draw_charbuffer_t *sb_fph[MAX_PLAYERS]; +static draw_charbuffer_t *sb_time[MAX_PLAYERS]; +static draw_charbuffer_t *sb_frags[MAX_PLAYERS]; +static draw_charbuffer_t *sb_team[MAX_PLAYERS]; +static draw_charbuffer_t *sb_ping[MAX_PLAYERS]; +static draw_charbuffer_t *sb_pl[MAX_PLAYERS]; +static draw_charbuffer_t *sb_uid[MAX_PLAYERS]; +static draw_charbuffer_t *sb_name[MAX_PLAYERS]; +static draw_charbuffer_t *sb_team_frags[MAX_PLAYERS]; +static draw_charbuffer_t *sb_team_players[MAX_PLAYERS]; +static draw_charbuffer_t *sb_team_stats[MAX_PLAYERS]; +static draw_charbuffer_t *sb_spectator; +int scoreboardlines, scoreboardteams; + +static void +Sbar_SortFrags (bool includespec) +{ + int i, j, k; + + // sort by frags + scoreboardlines = 0; + for (i = 0; i < sbar_maxplayers; i++) { + if (sbar_players[i].name && sbar_players[i].name->value[0] + && (!sbar_players[i].spectator || includespec)) { + fragsort[scoreboardlines] = i; + scoreboardlines++; + if (sbar_players[i].spectator) + sbar_players[i].frags = -999; + } + } + for (i = scoreboardlines; i < sbar_maxplayers; i++) { + fragsort[i] = -1; + } + + player_info_t *p = sbar_players; + for (i = 0; i < scoreboardlines; i++) { + for (j = 0; j < scoreboardlines - 1 - i; j++) { + if (p[fragsort[j]].frags < p[fragsort[j + 1]].frags) { + k = fragsort[j]; + fragsort[j] = fragsort[j + 1]; + fragsort[j + 1] = k; + } + } + } +} + +static void +Sbar_SortTeams (void) +{ + char t[16 + 1]; + int i, j, k; + player_info_t *s; + + // request new ping times every two second + scoreboardteams = 0; + + + // sort the teams + memset (teams, 0, sizeof (teams)); + for (i = 0; i < sbar_maxplayers; i++) + teams[i].plow = 999; + + for (i = 0; i < sbar_maxplayers; i++) { + s = &sbar_players[i]; + if (!s->name || !s->name->value[0]) + continue; + if (s->spectator) + continue; + + // find his team in the list + t[16] = 0; + if (s->team) + strncpy (t, s->team->value, 16); + if (!t[0]) + continue; // not on team + for (j = 0; j < scoreboardteams; j++) + if (!strcmp (teams[j].team, t)) { + teams[j].frags += s->frags; + teams[j].players++; + goto addpinginfo; + } + if (j == scoreboardteams) { // must add him + j = scoreboardteams++; + strcpy (teams[j].team, t); + teams[j].frags = s->frags; + teams[j].players = 1; + addpinginfo: + if (teams[j].plow > s->ping) + teams[j].plow = s->ping; + if (teams[j].phigh < s->ping) + teams[j].phigh = s->ping; + teams[j].ptotal += s->ping; + } + } + for (i = 0; i < sbar_maxplayers; i++) { + team_t *tm = teams + i; + int plow = tm->plow; + if (plow < 0 || plow > 999) + plow = 999; + int phigh = tm->phigh; + if (phigh < 0 || phigh > 999) + phigh = 999; + int pavg = !tm->players ? 999 : tm->ptotal / tm->players; + if (pavg < 0 || pavg > 999) + pavg = 999; + + write_charbuff (sb_team_stats[i], 0, 0, + va (0, "%3i/%3i/%3i", plow, pavg, phigh)); + write_charbuff (sb_team[i], 0, 0, tm->team); + write_charbuff (sb_team_frags[i], 0, 0, va (0, "%5d", tm->frags)); + write_charbuff (sb_team_players[i], 0, 0, va (0, "%5d", tm->players)); + } + + // sort + for (i = 0; i < scoreboardteams; i++) + teamsort[i] = i; + + // good 'ol bubble sort + for (i = 0; i < scoreboardteams - 1; i++) { + for (j = i + 1; j < scoreboardteams; j++) { + if (teams[teamsort[i]].frags < teams[teamsort[j]].frags) { + k = teamsort[i]; + teamsort[i] = teamsort[j]; + teamsort[j] = k; + } + } + } +} + +static void +draw_solo_time (void) +{ + int minutes = sbar_time / 60; + int seconds = sbar_time - 60 * minutes; + write_charbuff (solo_time, 0, 0, + va (0, "Time :%3i:%02i", minutes, seconds)); +} + +static void +draw_solo (view_t view) +{ + sbar_setcomponent (sbar_solo, canvas_pic, &sb_scorebar); + sbar_setcomponent (sbar_solo_monsters, canvas_charbuff, &solo_monsters); + sbar_setcomponent (sbar_solo_secrets, canvas_charbuff, &solo_secrets); + sbar_setcomponent (sbar_solo_time, canvas_charbuff, &solo_time); + sbar_setcomponent (sbar_solo_name, canvas_charbuff, &solo_name); +} + +static void +hide_solo (view_t view) +{ + sbar_remcomponent (sbar_solo, canvas_pic); + sbar_remcomponent (sbar_solo_monsters, canvas_charbuff); + sbar_remcomponent (sbar_solo_secrets, canvas_charbuff); + sbar_remcomponent (sbar_solo_time, canvas_charbuff); + sbar_remcomponent (sbar_solo_name, canvas_charbuff); +} + +static void +clear_frags_bar (view_t view) +{ + sbar_remcomponent (View_GetChild (view, 0), canvas_fill); + sbar_remcomponent (View_GetChild (view, 1), canvas_fill); + sbar_remcomponent (View_GetChild (view, 2), canvas_charbuff); + sbar_remcomponent (View_GetChild (view, 3), canvas_func); +} + +static void +clear_minifrags_bar (view_t view) +{ + clear_frags_bar (view); + sbar_remcomponent (View_GetChild (view, 4), canvas_charbuff); + sbar_remcomponent (View_GetChild (view, 5), canvas_charbuff); + sbar_remcomponent (View_GetChild (view, 6), canvas_charbuff); +} + +static void +set_frags_bar (view_t view, byte top, byte bottom, draw_charbuffer_t *buff, + canvas_func_f func) +{ + sbar_setcomponent (View_GetChild (view, 0), canvas_fill, &top); + sbar_setcomponent (View_GetChild (view, 1), canvas_fill, &bottom); + sbar_setcomponent (View_GetChild (view, 2), canvas_charbuff, &buff); + if (func) { + sbar_setcomponent (View_GetChild (view, 3), canvas_func, &func); + } else { + sbar_remcomponent (View_GetChild (view, 3), canvas_func); + } +} + +static void +set_minifrags_bar (view_t view, byte top, byte bottom, draw_charbuffer_t *buff, + canvas_func_f func, draw_charbuffer_t *team, + draw_charbuffer_t *name) +{ + set_frags_bar (view, top, bottom, buff, func); + if (team) { + sbar_setcomponent (View_GetChild (view, 4), canvas_charbuff, &team); + sbar_setcomponent (View_GetChild (view, 5), canvas_charbuff, &name); + sbar_remcomponent (View_GetChild (view, 6), canvas_charbuff); + } else { + sbar_remcomponent (View_GetChild (view, 4), canvas_charbuff); + sbar_remcomponent (View_GetChild (view, 5), canvas_charbuff); + sbar_setcomponent (View_GetChild (view, 6), canvas_charbuff, &name); + } +} + +static void +frags_marker (view_pos_t pos, view_pos_t len) +{ + r_funcs->Draw_Character (pos.x, pos.y, 16); + r_funcs->Draw_Character (pos.x + len.x - 8, pos.y, 17); +} + +static void +draw_frags (view_t view) +{ + if (sbar_maxplayers == 1) { + return; + } + Sbar_SortFrags (0); + + int numbars = 4; + int count = min (scoreboardlines, numbars); + int i; + + for (i = 0; i < count; i++) { + int k = fragsort[i]; + __auto_type s = &sbar_players[k]; + view_t bar = View_GetChild (view, i); + set_frags_bar (bar, + Sbar_ColorForMap (s->topcolor), + Sbar_ColorForMap (s->bottomcolor), + sb_frags[k], + (k == sbar_viewplayer) ? frags_marker : 0); + draw_smallnum (View_GetChild (bar, 2), s->frags, 0, 0); + } + for (; i < numbars; i++) { + clear_frags_bar (View_GetChild (view, i)); + } +} + +static void +draw_minifrags (view_t view) +{ + if (sbar_maxplayers == 1) { + return; + } + Sbar_SortFrags (0); + + // find us + view_pos_t len = View_GetLen (view); + int numbars = len.y / 8; + int start = 0; + int i; + + for (i = 0; i < scoreboardlines; i++) { + if (fragsort[i] == sbar_playernum) { + start = min (i - numbars / 2, scoreboardlines - numbars); + start = max (0, start); + } + } + + int count = min (scoreboardlines - start, numbars); + + for (i = 0; i < count; i++) { + int k = fragsort[i + start]; + __auto_type s = &sbar_players[k]; + view_t bar = View_GetChild (view, i); + set_minifrags_bar (bar, + Sbar_ColorForMap (s->topcolor), + Sbar_ColorForMap (s->bottomcolor), + sb_frags[k], + (k == sbar_viewplayer) ? frags_marker : 0, + sbar_teamplay ? sb_team[k] : 0, + sb_name[k]); + if (sbar_teamplay) { + write_charbuff_cl (sb_team[k], 0, 0, s->team->value); + } + write_charbuff_cl (sb_name[k], 0, 0, s->name->value); + draw_smallnum (View_GetChild (bar, 2), s->frags, 0, 0); + } + for (; i < numbars; i++) { + clear_minifrags_bar (View_GetChild (view, i)); + } +} + +static void +clear_miniteam_bar (view_t view) +{ + sbar_remcomponent (View_GetChild (view, 0), canvas_charbuff); + sbar_remcomponent (View_GetChild (view, 1), canvas_charbuff); + sbar_remcomponent (View_GetChild (view, 2), canvas_func); +} + +static void +set_miniteam_bar (view_t view, draw_charbuffer_t *team, + draw_charbuffer_t *frags, canvas_func_f func) +{ + sbar_setcomponent (View_GetChild (view, 0), canvas_charbuff, &team); + sbar_setcomponent (View_GetChild (view, 1), canvas_charbuff, &frags); + if (func) { + sbar_setcomponent (View_GetChild (view, 2), canvas_func, &func); + } else { + sbar_remcomponent (View_GetChild (view, 2), canvas_func); + } +} + +static void +draw_miniteam (view_t view) +{ + if (!sbar_teamplay) { + return; + } + Sbar_SortTeams (); + + view_pos_t len = View_GetLen (view); + int numbars = len.y / 8; + int count = min (scoreboardteams, numbars); + int i; + info_key_t *player_team = sbar_players[sbar_playernum].team; + + for (i = 0; i < count; i++) { + int k = teamsort[i]; + team_t *tm = teams + k; + __auto_type s = &sbar_players[k]; + view_t bar = View_GetChild (view, i); + canvas_func_f func = 0; + if (player_team && strnequal (player_team->value, tm->team, 16)) { + func = frags_marker; + } + set_miniteam_bar (bar, sb_team[k], sb_team_frags[k], func); + write_charbuff_cl (sb_team[k], 0, 0, s->team->value); + write_charbuff (sb_team_frags[k], 0, 0, va (0, "%5d", tm->frags)); + } + for (; i < numbars; i++) { + clear_miniteam_bar (View_GetChild (view, i)); + } +} + +static void +draw_face (view_t view) +{ + qpic_t *face; + + if (sb_game && sbar_maxplayers > 1 + && sbar_teamplay > 3 && sbar_teamplay < 7) { + return; + } + if (sbar_stats[STAT_HEALTH] <= 0) {//FIXME hide_Face or hide_sbar + sbar_remcomponent (sbar_face, canvas_pic); + return; + } + if ((sbar_stats[STAT_ITEMS] & (IT_INVISIBILITY | IT_INVULNERABILITY)) + == (IT_INVISIBILITY | IT_INVULNERABILITY)) { + face = sb_face_invis_invuln; + } else if (sbar_stats[STAT_ITEMS] & IT_QUAD) { + face = sb_face_quad; + } else if (sbar_stats[STAT_ITEMS] & IT_INVISIBILITY) { + face = sb_face_invis; + } else if (sbar_stats[STAT_ITEMS] & IT_INVULNERABILITY) { + face = sb_face_invuln; + } else { + int f, anim; + if (sbar_stats[STAT_HEALTH] >= 100) { + f = 4; + } else { + f = sbar_stats[STAT_HEALTH] / 20; + } + + if (sbar_time <= sbar_faceanimtime) { + anim = 1; + sb_updates = 0; // make sure the anim gets drawn over + } else { + anim = 0; + } + face = sb_faces[f][anim]; + } + sbar_setcomponent (view, canvas_pic, &face); +} + +static void +draw_spectator (view_t view) +{ + view_t tracking = View_GetChild (view, 0); + view_t back = View_GetChild (view, 1); + view_t notrack[] = { + View_GetChild (view, 2), + View_GetChild (view, 3), + }; + + if (sbar_autotrack < 0) { + sbar_setcomponent (back, canvas_pic, &sb_scorebar); + sbar_setcomponent (notrack[0], canvas_charbuff, &spec_buff[0]); + sbar_setcomponent (notrack[1], canvas_charbuff, &spec_buff[1]); + sbar_remcomponent (tracking, canvas_charbuff); + } else { + sbar_remcomponent (back, canvas_pic); + sbar_remcomponent (notrack[0], canvas_charbuff); + sbar_remcomponent (notrack[1], canvas_charbuff); + if (sbar_players[sbar_autotrack].name) { + write_charbuff_cl (spec_buff[3], 0, 0, + va (0, "Tracking %.13s, [JUMP] for next", + sbar_players[sbar_autotrack].name->value)); + sbar_setcomponent (tracking, canvas_charbuff, &spec_buff[3]); + } else { + sbar_setcomponent (tracking, canvas_charbuff, &spec_buff[2]); + } + } +} + +static void +hide_spectator (view_t view) +{ + for (int i = 0; i < 4; i++) { + sbar_remcomponent (View_GetChild (view, i), canvas_charbuff); + sbar_remcomponent (View_GetChild (view, i), canvas_pic); + } +} + +static void +draw_armor (view_t view) +{ + view_t armor = View_GetChild (view, 0); + view_t num[3] = { + View_GetChild (view, 1), + View_GetChild (view, 2), + View_GetChild (view, 3), + }; + if (sbar_stats[STAT_ITEMS] & IT_INVULNERABILITY) { + draw_num (num, 666, 3, 1); + } else { + draw_num (num, sbar_stats[STAT_ARMOR], 3, sbar_stats[STAT_ARMOR] <= 25); + if (sbar_stats[STAT_ITEMS] & IT_ARMOR3) + sbar_setcomponent (armor, canvas_pic, &sb_armor[2]); + else if (sbar_stats[STAT_ITEMS] & IT_ARMOR2) + sbar_setcomponent (armor, canvas_pic, &sb_armor[1]); + else if (sbar_stats[STAT_ITEMS] & IT_ARMOR1) + sbar_setcomponent (armor, canvas_pic, &sb_armor[0]); + else + sbar_remcomponent (armor, canvas_pic); + } +} + +static void +draw_health (view_t view) +{ + view_t num[3] = { + View_GetChild (view, 0), + View_GetChild (view, 1), + View_GetChild (view, 2), + }; + draw_num (num, sbar_stats[STAT_HEALTH], 3, sbar_stats[STAT_HEALTH] <= 25); +} + +static void +draw_rogue_ctf_face (view_t view) +{ + __auto_type p = &sbar_players[sbar_viewplayer]; + byte top = Sbar_ColorForMap (p->topcolor); + byte bottom = Sbar_ColorForMap (p->bottomcolor); + sbar_setcomponent (View_GetChild (view, 0), canvas_fill, &top); + sbar_setcomponent (View_GetChild (view, 1), canvas_fill, &bottom); + sbar_setcomponent (View_GetChild (view, 2), canvas_charbuff, + &sb_frags[sbar_viewplayer]); + sbar_setcomponent (view, canvas_pic, &rsb_teambord); +} + +static void +setup_frags (view_t frags, int player) +{ + sbar_view (0, 0, 40, 4, grav_northwest, frags); + sbar_view (0, 4, 40, 4, grav_northwest, frags); + sbar_view (8, 0, 24, 8, grav_northwest, frags); + sbar_view (0, 0, 40, 8, grav_northwest, frags); + + player_info_t *p = &sbar_players[player]; + set_frags_bar (frags, + Sbar_ColorForMap (p->topcolor), + Sbar_ColorForMap (p->bottomcolor), + sb_frags[player], + (player == sbar_viewplayer) ? frags_marker : 0); +} + +static void +setup_spect (view_t spect, int player) +{ + view_t v = sbar_view (0, 0, 88, 4, grav_north, spect); + sbar_setcomponent (v, canvas_charbuff, &sb_spectator); +} + +typedef struct dmo_def_s { + int width; // in pixels + draw_charbuffer_t **buffer; + void (*setup) (view_t, int); +} dmo_def_t; + +static dmo_def_t ping_def = { .width = 24, .buffer = sb_ping }; +static dmo_def_t pl_def = { .width = 24, .buffer = sb_pl }; +static dmo_def_t fph_def = { .width = 24, .buffer = sb_fph }; +static dmo_def_t time_def = { .width = 32, .buffer = sb_time }; +static dmo_def_t frags_def = { .width = 40, .setup = setup_frags }; +static dmo_def_t team_def = { .width = 32, .buffer = sb_uid }; +static dmo_def_t uid_def = { .width = 32, .buffer = sb_uid }; +static dmo_def_t name_def = { .width = 128, .buffer = sb_name }; +static dmo_def_t spectator_def = { .width = 112, .setup = setup_spect }; +static dmo_def_t spec_team_def = { .width = 32, }; +static dmo_def_t team_frags_def = { .width = 40, .buffer = sb_team_frags }; +static dmo_def_t team_stats_def = { .width = 88, .buffer = sb_team_stats }; +static dmo_def_t team_players_def = { .width = 40, .buffer = sb_team_players }; + +static dmo_def_t *nq_dmo_defs[] = { + &frags_def, + &name_def, + 0 +}; +static dmo_def_t *qw_dmo_team_uid_ping_defs[] = { + &ping_def, + &pl_def, + &fph_def, + &time_def, + &frags_def, + &team_def, + &uid_def, + &name_def, + 0 +}; +static dmo_def_t *qw_dmo_team_uid_defs[] = { + &uid_def, + &pl_def, + &fph_def, + &time_def, + &frags_def, + &team_def, + &name_def, + 0 +}; +static dmo_def_t *qw_dmo_team_ping_defs[] = { + &ping_def, + &pl_def, + &fph_def, + &time_def, + &frags_def, + &team_def, + &name_def, + 0 +}; +static dmo_def_t *qw_dmo_uid_ping_defs[] = { + &ping_def, + &pl_def, + &fph_def, + &time_def, + &frags_def, + &uid_def, + &name_def, + 0 +}; +static dmo_def_t *qw_dmo_uid_defs[] = { + &uid_def, + &pl_def, + &fph_def, + &time_def, + &frags_def, + &name_def, + 0 +}; +static dmo_def_t *qw_dmo_ping_defs[] = { + &ping_def, + &pl_def, + &fph_def, + &time_def, + &frags_def, + &name_def, + 0 +}; +static dmo_def_t *qw_dmo_spect_team_uid_ping_defs[] = { + &uid_def, + &pl_def, + &spectator_def, + &spec_team_def, + &ping_def, + &name_def, + 0 +}; +static dmo_def_t *qw_dmo_spect_team_uid_defs[] = { + &uid_def, + &pl_def, + &spectator_def, + &spec_team_def, + &name_def, + 0 +}; +static dmo_def_t *qw_dmo_spect_team_ping_defs[] = { + &ping_def, + &pl_def, + &spectator_def, + &spec_team_def, + &name_def, + 0 +}; +static dmo_def_t *qw_dmo_spect_uid_ping_defs[] = { + &ping_def, + &pl_def, + &spectator_def, + &uid_def, + &name_def, + 0 +}; +static dmo_def_t *qw_dmo_spect_uid_defs[] = { + &uid_def, + &pl_def, + &spectator_def, + &name_def, + 0 +}; +static dmo_def_t *qw_dmo_spect_ping_defs[] = { + &ping_def, + &pl_def, + &spectator_def, + &name_def, + 0 +}; +static dmo_def_t *team_overlay_defs[] = { + &team_stats_def, + &team_def, + &team_frags_def, + &team_players_def, + 0 +}; +static dmo_def_t **dmo_defs[] = { + nq_dmo_defs, + team_overlay_defs, + + qw_dmo_ping_defs, + qw_dmo_uid_defs, + qw_dmo_uid_ping_defs, + + qw_dmo_team_ping_defs, + qw_dmo_team_uid_defs, + qw_dmo_team_uid_ping_defs, + + qw_dmo_spect_ping_defs, + qw_dmo_spect_uid_defs, + qw_dmo_spect_uid_ping_defs, + + qw_dmo_spect_team_ping_defs, + qw_dmo_spect_team_uid_defs, + qw_dmo_spect_team_uid_ping_defs, +}; + +static view_t +make_dmo_line (view_t parent, int player, int line_type) +{ + dmo_def_t **defs = dmo_defs[line_type]; + int x = -8; + view_t line = sbar_view (0, 0, 0, 0, grav_north, parent); + + while (*defs) { + dmo_def_t *d = *defs++; + x += 8 + d->width; + if (d->buffer || d->setup) { + view_t v = sbar_view (x - d->width, 0, d->width, 8, + grav_northwest, line); + if (d->buffer) { + draw_charbuffer_t *buff = d->buffer[player]; + sbar_setcomponent (v, canvas_charbuff, &buff); + } else if (d->setup) { + d->setup (v, player); + } + } + } + View_SetLen (line, x, 8); + return line; +} + +static inline int +calc_fph (int frags, int total) +{ + int fph; + + if (total) { + fph = (3600 * frags) / total; + fph = bound (-999, fph, 999); + } else { + fph = 0; + } + + return fph; +} + +static int +dmo_line_type (void) +{ + if (!sbar_servername) { + return 0; + } else if (sbar_showteamscores) { + return 1; + } else { + int team = !!sbar_teamplay; + int spect = !!sbar_spectator; + int mode = bound (0, hud_scoreboard_uid, 2); + + return 2 + mode + team * 3 + spect * mode; + } +} + +static void +draw_deathmatch (view_t view) +{ + Sbar_SortFrags (0); + Sbar_SortTeams (); + + int y = 40; + view_pos_t len = View_GetLen (view); + int numbars = (len.y - y) / 10; + int count = min (scoreboardlines, numbars); + int line_type = dmo_line_type (); + int i; + + double cur_time = sbar_intermission ? sbar_completed_time : sbar_time; + for (i = 0; i < count; i++, y += 10) { + int k = fragsort[i]; + player_info_t *p = &sbar_players[k]; + if (!View_Valid (sb_views[k])) { + sb_views[k] = make_dmo_line (view, k, line_type); + } + int total = cur_time - p->entertime; + write_charbuff (sb_fph[k], 0, 0, va (0, "%3d", + calc_fph (p->frags, total))); + write_charbuff (sb_time[k], 0, 0, va (0, "%4d", total / 60)); + View_SetPos (sb_views[k], 0, y); + } + for (; i < MAX_PLAYERS; i++) { + int k = fragsort[i]; + if (k >= 0 && View_Valid (sb_views[k])) { + View_Delete (sb_views[k]); + } + } + View_UpdateHierarchy (view); +} + +static void +hide_deathmatch (view_t view) +{ + uint32_t count = View_ChildCount (view); + while (count-- > 0) { + View_Delete (View_GetChild (view, count)); + } +} + +static void +draw_status (void ) +{ + sb_updates = 0; + + + if (sbar_gametype) { + draw_deathmatch (deathmatch_view); + } else { + if (sbar_spectator) { + if (sbar_autotrack < 0) { + return; + } + } + draw_solo (sbar_solo); + } +} + +static void +hide_status (void) +{ + sb_updates = 0; + hide_solo (sbar_solo); + hide_deathmatch (deathmatch_view); +} + +/* autologging of frags after a match ended + (called by recived network packet with command scv_intermission) + TODO: Find a new and better place for this function + (i am nearly shure this is wrong place) + added by Elmex +*/ +void +Sbar_LogFrags (double completed_time) +{ + char *name; + char *team; + byte *cp = NULL; + QFile *file = NULL; + int minutes, fph, total, d, f, i, k, l, p; + player_info_t *s = NULL; + const char *t = NULL; + time_t tt = time (NULL); + + if (!cl_fraglog) + return; + + if ((file = QFS_Open (fs_fraglog, "a")) == NULL) + return; + + t = ctime (&tt); + if (t) + Qwrite (file, t, strlen (t)); + + Qprintf (file, "%s\n%s %s\n", sbar_servername, + cl_world.scene->worldmodel->path, sbar_levelname); + + // scores + Sbar_SortFrags (true); + + // draw the text + l = scoreboardlines; + + if (sbar_teamplay) { + // TODO: test if the teamplay does correct output + Qwrite (file, "pl fph time frags team name\n", + strlen ("pl fph time frags team name\n")); + } else { + Qwrite (file, "pl fph time frags name\n", + strlen ("pl fph time frags name\n")); + } + + for (i = 0; i < l; i++) { + k = fragsort[i]; + s = &sbar_players[k]; + if (!s->name || !s->name->value[0]) + continue; + + // draw pl + p = s->pl; + (void) p; //FIXME + + // get time + if (sbar_intermission) + total = completed_time - s->entertime; + else + total = sbar_time - s->entertime; + minutes = total / 60; + + // get frags + f = s->frags; + + fph = calc_fph (f, total); + + name = malloc (strlen (s->name->value) + 1); + for (cp = (byte *) s->name->value, d = 0; *cp; cp++, d++) + name[d] = sys_char_map[*cp]; + name[d] = 0; + + if (s->spectator) { + Qprintf (file, "%-3i%% %s (spectator)", s->pl, name); + } else { + if (sbar_teamplay) { + team = malloc (strlen (s->team->value) + 1); + for (cp = (byte *) s->team, d = 0; *cp; cp++, d++) + team[d] = sys_char_map[*cp]; + team[d] = 0; + + Qprintf (file, "%-3i%% %-3i %-4i %-3i %-4s %s", + s->pl, fph, minutes, f, team, name); + free (team); + } else { + Qprintf (file, "%-3i%% %-3i %-4i %-3i %s", + s->pl, fph, minutes, f, name); + } + } + free (name); + Qwrite (file, "\n\n", 1); + } + + Qclose (file); +} + +static void +draw_time (view_t *view) +{ + struct tm *local = 0; + time_t utc = 0; + char st[80]; //FIXME: overflow + + // Get local time + utc = time (0); + local = localtime (&utc); + +#if defined(_WIN32) || defined(_WIN64) +# define HOUR12 "%I" +# define HOUR24 "%H" +# define PM "%p" +#else +# define HOUR12 "%l" +# define HOUR24 "%k" +# define PM "%P" +#endif + if (hud_time == 1) { // Use international format + strftime (st, sizeof (st), HOUR24":%M", local); + } else if (hud_time >= 2) { // US AM/PM display + strftime (st, sizeof (st), HOUR12":%M "PM, local); + } + write_charbuff_cl (time_buff, 0, 0, st); +} + +static void +draw_fps (view_t view) +{ + static char st[80]; + double t; + static double lastframetime; + static double lastfps; + + t = Sys_DoubleTime (); + if ((t - lastframetime) >= 0.2) { + lastfps = fps_count / (t - lastframetime); + fps_count = 0; + lastframetime = t; + int prec = lastfps < 1000 ? 1 : 0; + snprintf (st, sizeof (st), "%6.*f FPS", prec, lastfps); + } + write_charbuff (fps_buff, 0, 0, st); +} + +/* CENTER PRINTING */ +static dstring_t center_string = {&dstring_default_mem}; +static passage_t center_passage; +static float centertime_start; // for slow victory printing +static float centertime_off; +static int center_lines; + +/* + Called for important messages that should stay in the center of the screen + for a few moments +*/ +void +Sbar_CenterPrint (const char *str) +{ + if (!str || !*str) { + centertime_off = 0; + return; + } + + centertime_off = scr_centertime; + centertime_start = sbar_time; + + if (center_string.str && !strcmp (str, center_string.str)) { + // same string as last time, no need to lay out the text again + return; + } + + dstring_copystr (¢er_string, str); + Passage_ParseText (¢er_passage, center_string.str); + // Standard centerprint strings are pre-flowed so each line in the message + // is a paragraph in the passage. + center_lines = center_passage.hierarchy->childCount[0]; +} + +static void +Sbar_DrawCenterString (view_t view, unsigned remaining) +{ + view_pos_t abs = View_GetAbs (view); + view_pos_t len = View_GetLen (view); + + int x, y; + + if (center_lines <= 4) + y = abs.y + len.y * 0.35; + else + y = abs.y + 48; + + __auto_type h = center_passage.hierarchy; + psg_text_t *line = h->components[passage_type_text_obj]; + int line_count = center_lines; + while (line_count-- > 0 && remaining > 0) { + line++; + const char *text = center_passage.text + line->text; + unsigned count = min (40, line->size); + x = abs.x + (len.x - count * 8) / 2; + count = min (count, remaining); + remaining -= count; + r_funcs->Draw_nString (x, y, text, count); + y += 8; + } +} + +static void +clear_views (view_t view) +{ + sbar_remcomponent (view, canvas_cachepic); + sbar_remcomponent (view, canvas_pic); + + for (uint32_t i = 0; i < View_ChildCount (view); i++) { + clear_views (View_GetChild (view, i)); + } +} + +static void +draw_intermission (view_t view) +{ + clear_views (view); + const char *n; + n = "gfx/complete.lmp"; + sbar_setcomponent (View_GetChild (view, 0), canvas_cachepic, &n); + n = "gfx/inter.lmp"; + sbar_setcomponent (View_GetChild (view, 1), canvas_cachepic, &n); + + view_t time_views[] = { + View_GetChild (intermission_time, 0), + View_GetChild (intermission_time, 1), + View_GetChild (intermission_time, 2), + View_GetChild (intermission_time, 3), + View_GetChild (intermission_time, 4), + View_GetChild (intermission_time, 5), + }; + int dig = sbar_completed_time / 60; + int num = sbar_completed_time - dig * 60; + draw_num (time_views + 0, dig, 3, 0); + sbar_setcomponent (time_views[3], canvas_pic, &sb_colon); + draw_num (time_views + 4, num, 2, 0); + + view_t secr_views[] = { + View_GetChild (intermission_secr, 0), + View_GetChild (intermission_secr, 1), + View_GetChild (intermission_secr, 2), + View_GetChild (intermission_secr, 3), + View_GetChild (intermission_secr, 4), + View_GetChild (intermission_secr, 5), + View_GetChild (intermission_secr, 6), + }; + draw_num (secr_views + 0, sbar_stats[STAT_SECRETS], 3, 0); + sbar_setcomponent (secr_views[3], canvas_pic, &sb_slash); + draw_num (secr_views + 4, sbar_stats[STAT_TOTALSECRETS], 3, 0); + + view_t kill_views[] = { + View_GetChild (intermission_kill, 0), + View_GetChild (intermission_kill, 1), + View_GetChild (intermission_kill, 2), + View_GetChild (intermission_kill, 3), + View_GetChild (intermission_kill, 4), + View_GetChild (intermission_kill, 5), + View_GetChild (intermission_kill, 6), + }; + draw_num (kill_views + 0, sbar_stats[STAT_MONSTERS], 3, 0); + sbar_setcomponent (kill_views[3], canvas_pic, &sb_slash); + draw_num (kill_views + 4, sbar_stats[STAT_TOTALMONSTERS], 3, 0); +} + +static void +draw_finale (view_t view) +{ + clear_views (view); + + r_data->scr_copyeverything = 1; + + const char *n = "gfx/finale.lmp"; + sbar_setcomponent (View_GetChild (view, 2), canvas_cachepic, &n); +} + +static void +draw_cutscene (view_t view) +{ + clear_views (view); +} + +void +Sbar_Intermission (int mode, double completed_time) +{ + static canvas_update_f intermission_funcs[2][3] = { + { draw_intermission, draw_finale, draw_cutscene, }, + { draw_deathmatch, draw_finale, draw_cutscene, }, + }; + static view_t *views[2][3] = { + { &intermission_view, &intermission_view, &intermission_view }, + { &deathmatch_view, &intermission_view, &intermission_view }, + }; + sbar_completed_time = completed_time; + if ((unsigned) mode > 3) { + mode = 0; + } + sbar_intermission = mode; + set_update (intermission_view, clear_views); + set_update (deathmatch_view, clear_views); + if (mode > 0) { + set_update (*views[sbar_gametype][mode - 1], + intermission_funcs[sbar_gametype][mode - 1]); + } +} + +void +Sbar_DrawCenterPrint (void) +{ + r_data->scr_copytop = 1; + + centertime_off -= r_data->frametime; + if (!center_passage.hierarchy + || (centertime_off <= 0 && !sbar_intermission)) + return; + + + int remaining = -1; + if (sbar_intermission) { + // the finale prints the characters one at a time + remaining = scr_printspeed * (sbar_time - centertime_start); + } + Sbar_DrawCenterString (hud_overlay_view, remaining); +} + +void +Sbar_Update (double time) +{ + fps_count++; + sbar_time = time; + if (!sbar_active) { + return; + } + if (sbar_showscores) { + draw_solo_time (); + } + if (sb_updates < r_data->vid->numpages) { + //FIXME find a better way to support animations + sb_updates++; + draw_weapons (sbar_weapons); + draw_items (sbar_items); + draw_face (sbar_face); + } +} + +void +Sbar_UpdatePings (void) +{ + for (int i = 0; i < sbar_maxplayers; i++) { + player_info_t *p = &sbar_players[i]; + if (!p->name || !p->name->value) { + continue; + } + write_charbuff (sb_ping[i], 0, 0, va (0, "%3d", p->ping)); + write_charbuff (sb_pl[i], 0, 0, va (0, "%3d", p->pl)); + } + write_charbuff (ping_buff, 0, 0, + va (0, "%3d ms", sbar_players[sbar_playernum].ping)); +} + +void +Sbar_UpdatePL (int pl) +{ + write_charbuff (pl_buff, 0, 0, va (0, "%3d pl", pl)); +} + +void +Sbar_UpdateFrags (int playernum) +{ + player_info_t *p = &sbar_players[playernum]; + write_charbuff (sb_frags[playernum], 0, 0, va (0, "%3d", p->frags)); +} + +void +Sbar_UpdateInfo (int playernum) +{ + player_info_t *p = &sbar_players[playernum]; + //FIXME update top/bottom color + write_charbuff (sb_uid[playernum], 0, 0, va (0, "%4d", p->userid)); + write_charbuff_cl (sb_name[playernum], 0, 0, p->name->value); + if (sbar_teamplay && p->team) { + write_charbuff_cl (sb_team[playernum], 0, 0, p->team->value); + } + if (sb_game && sbar_maxplayers > 1 + && sbar_teamplay > 3 && sbar_teamplay < 7) { + set_update (sbar_face, draw_rogue_ctf_face); + } +} + +static void +update_health (int stat) +{ + set_update (sbar_health, draw_health); + set_update (sbar_face, draw_face); + if (sbar_stats[STAT_HEALTH] <= 0) { + draw_status (); + } else if (1||!sbar_showscores) { + hide_status (); + } +} + +static void +update_frags (int stat) +{ + write_charbuff (sb_frags[sbar_playernum], 0, 0, + va (0, "%3d", sbar_stats[stat])); + set_update (sbar_frags, draw_frags);//FIXME + set_update (hud_minifrags, draw_minifrags);//FIXME + set_update (deathmatch_view, draw_deathmatch);//FIXME + if (sbar_teamplay) { + set_update (hud_miniteam, draw_miniteam);//FIXME + } +} + +static void +update_weapon (int stat) +{ + set_update (sbar_weapons, draw_weapons); + sb_ibar_index = (sb_game + && sbar_stats[STAT_ACTIVEWEAPON] < RIT_LAVA_NAILGUN); + if (hud_sbar) { + // shouldn't need to sort the pics because the component is already + // on the entity, so the position in the pool won't be affected + sbar_setcomponent (sbar_inventory, canvas_pic, &sb_ibar[sb_ibar_index]); + } else { + for (int i = 0; i < 4; i++) { + view_t v = View_GetChild (sbar_miniammo, i); + sb_miniammo[i].pic = sb_ibar[sb_ibar_index]; + sbar_setcomponent (v, canvas_subpic, &sb_miniammo[i]); + } + } +} + +static void +update_ammo (int stat) +{ + set_update (sbar_ammo, draw_ammo); +} + +static void +update_miniammo (int stat) +{ + set_update (sbar_miniammo, draw_miniammo);//FIXME +} + +static void +update_armor (int stat) +{ + set_update (sbar_armor, draw_armor); +} + +static void +update_totalsecrets (int stat) +{ + write_charbuff (solo_secrets, 14, 0, + va (0, "%3i", sbar_stats[STAT_TOTALSECRETS])); +} + +static void +update_secrets (int stat) +{ + write_charbuff (solo_secrets, 9, 0, + va (0, "%3i", sbar_stats[STAT_SECRETS])); +} + +static void +update_totalmonsters (int stat) +{ + write_charbuff (solo_monsters, 14, 0, + va (0, "%3i", sbar_stats[STAT_TOTALMONSTERS])); +} + +static void +update_monsters (int stat) +{ + write_charbuff (solo_monsters, 9, 0, + va (0, "%3i", sbar_stats[STAT_MONSTERS])); +} + +static void +update_items (int stat) +{ + set_update (sbar_items, draw_items); + set_update (sbar_weapons, draw_weapons); + if (sb_item_count < 7) { + // hipnotic and rogue don't use sigils + set_update (sbar_sigils, draw_sigils); + } +} + +typedef void (*stat_update_f) (int stat); + +static stat_update_f stat_update[MAX_CL_STATS] = { + [STAT_HEALTH] = update_health, + [STAT_FRAGS] = update_frags, + [STAT_AMMO] = update_ammo, + [STAT_ARMOR] = update_armor, + [STAT_SHELLS] = update_miniammo, + [STAT_NAILS] = update_miniammo, + [STAT_ROCKETS] = update_miniammo, + [STAT_CELLS] = update_miniammo, + [STAT_ACTIVEWEAPON] = update_weapon, + [STAT_TOTALSECRETS] = update_totalsecrets, + [STAT_TOTALMONSTERS] = update_totalmonsters, + [STAT_SECRETS] = update_secrets, + [STAT_MONSTERS] = update_monsters, + [STAT_ITEMS] = update_items, +}; + +void +Sbar_UpdateStats (int stat) +{ + if ((unsigned) stat >= MAX_CL_STATS) { + Sys_Error ("Sbar_UpdateStats: invalid stat: %d", stat); + } + if (stat_update[stat]) { + stat_update[stat] (stat); + } +} + +void +Sbar_Damage (double time) +{ + sbar_faceanimtime = time + 0.2; +} + +void +Sbar_SetPlayerNum (int playernum, int spectator) +{ + sbar_playernum = playernum; + sbar_spectator = spectator; + + if (sbar_spectator) { + set_update (spectator_view, draw_spectator); + } else { + set_update (spectator_view, hide_spectator); + } +} + +void +Sbar_SetAutotrack (int autotrack) +{ + sbar_autotrack = autotrack; + if (sbar_spectator) { + set_update (spectator_view, draw_spectator); + } else { + set_update (spectator_view, hide_spectator); + } +} + +void +Sbar_SetViewEntity (int viewentity) +{ + sbar_viewplayer = viewentity - 1; +} + +void +Sbar_SetLevelName (const char *levelname, const char *servername) +{ + sbar_levelname = levelname; + sbar_servername = servername; + solo_name->cursx = write_charbuff_cl (solo_name, 0, 0, sbar_levelname); + + view_pos_t len = View_GetLen (sbar_solo_name); + len.x = 8 * solo_name->cursx; + View_SetLen (sbar_solo_name, len.x, len.y); + View_UpdateHierarchy (sbar_solo); +} + +void +Sbar_SetTeamplay (int teamplay) +{ + sbar_teamplay = teamplay; + if (sb_game && sbar_maxplayers > 1 + && sbar_teamplay > 3 && sbar_teamplay < 7) { + set_update (sbar_face, draw_rogue_ctf_face); + } +} + +void +Sbar_SetGameType (int gametype) +{ + sbar_gametype = gametype; +} + +void +Sbar_SetActive (int active) +{ + sbar_active = active; +} + +static void +sbar_hud_swap_f (void *data, const cvar_t *cvar) +{ + if (hud_sbar) { + return; + } + grav_t armament_grav = hud_swap ? grav_southwest : grav_southeast; + grav_t weapons_grav = hud_swap ? grav_northwest : grav_northeast; + View_SetGravity (sbar_armament, armament_grav); + View_SetGravity (sbar_weapons, weapons_grav); + View_UpdateHierarchy (hud_view); +} + +static void +set_hud_sbar (void) +{ + view_t v; + + if (hud_sbar) { + View_SetParent (sbar_armament, sbar_inventory); + View_SetPos (sbar_armament, 0, 0); + View_SetLen (sbar_armament, 202, 24); + View_SetGravity (sbar_armament, grav_northwest); + + View_SetLen (sbar_weapons, 192, 16); + View_SetGravity (sbar_weapons, grav_southwest); + + View_SetLen (sbar_miniammo, 202, 8); + View_SetGravity (sbar_miniammo, grav_northwest); + + int x = 0; + for (int i = 0; i < sb_weapon_view_count; i++) { + v = View_GetChild (sbar_weapons, i); + View_SetPos (v, x, 0); + View_SetLen (v, sb_weapons[0][i].pic->width, 16); + x += sb_weapons[0][i].pic->width; + + if (sbar_hascomponent (v, canvas_subpic)) { + canvas_subpic_t *subpic = sbar_getcomponent(v, canvas_subpic); + subpic->w = subpic->pic->width; + } + } + for (int i = 0; i < 4; i++) { + v = View_GetChild (sbar_miniammo, i); + View_SetPos (v, i * 48 + 10, 0); + View_SetLen (v, 42, 8); + sbar_remcomponent (v, canvas_subpic); + } + for (int i = 0; i < 7; i++) { + for (int j = 0; j < sb_weapon_view_count; j++) { + if (sb_weapons[i][j].pic) { + sb_weapons[i][j].w = sb_weapons[i][j].pic->width; + sb_weapons[i][j].h = sb_weapons[i][j].pic->height; + } + } + } + + sbar_setcomponent (sbar_inventory, canvas_pic, &sb_ibar[sb_ibar_index]); + sbar_setcomponent (sbar_statusbar, canvas_pic, &sb_sbar); + sbar_setcomponent (sbar_tile[0], canvas_tile, 0); + sbar_setcomponent (sbar_tile[1], canvas_tile, 0); + } else { + View_SetParent (sbar_armament, hud_view); + View_SetPos (sbar_armament, 0, 48); + View_SetLen (sbar_armament, 42, 44 + 16 * sb_weapon_view_count); + View_SetGravity (sbar_armament, grav_southeast); + + View_SetLen (sbar_weapons, 24, 16 * sb_weapon_view_count); + View_SetGravity (sbar_weapons, grav_northeast); + + View_SetLen (sbar_miniammo, 42, 44); + View_SetGravity (sbar_miniammo, grav_southeast); + + for (int i = 0; i < sb_weapon_view_count; i++) { + v = View_GetChild (sbar_weapons, i); + View_SetPos (v, 0, i * 16); + View_SetLen (v, 24, 16); + + if (sbar_hascomponent (v, canvas_subpic)) { + canvas_subpic_t *subpic = sbar_getcomponent(v, canvas_subpic); + subpic->w = 24; + } + } + for (int i = 0; i < 4; i++) { + v = View_GetChild (sbar_miniammo, i); + View_SetPos (v, 0, i * 11); + View_SetLen (v, 42, 11); + sbar_setcomponent (v, canvas_subpic, &sb_miniammo[i]); + } + for (int i = 0; i < 7; i++) { + for (int j = 0; j < sb_weapon_view_count; j++) { + if (sb_weapons[i][j].pic) { + sb_weapons[i][j].w = 24; + sb_weapons[i][j].h = sb_weapons[i][j].pic->height; + } + } + } + + sbar_remcomponent (sbar_inventory, canvas_pic); + sbar_remcomponent (sbar_statusbar, canvas_pic); + sbar_remcomponent (sbar_tile[0], canvas_tile); + sbar_remcomponent (sbar_tile[1], canvas_tile); + } + Canvas_SortComponentPool (cl_canvas_sys, hud_canvas, canvas_pic); +} + +static void +sbar_hud_sbar_f (void *data, const cvar_t *cvar) +{ + set_hud_sbar (); + View_UpdateHierarchy (hud_view); +} + +static void +sbar_hud_fps_f (void *data, const cvar_t *cvar) +{ + if (hud_fps) { + void *f = draw_fps; + sbar_setcomponent (hud_fps_view, canvas_update, &f); + sbar_setcomponent (hud_fps_view, canvas_charbuff, &fps_buff); + } else { + sbar_remcomponent (hud_fps_view, canvas_update); + sbar_remcomponent (hud_fps_view, canvas_charbuff); + } +} + +static void +sbar_hud_ping_f (void *data, const cvar_t *cvar) +{ + if (hud_ping) { + sbar_setcomponent (hud_ping_view, canvas_charbuff, &ping_buff); + } else { + sbar_remcomponent (hud_ping_view, canvas_charbuff); + } +} + +static void +sbar_hud_pl_f (void *data, const cvar_t *cvar) +{ + if (hud_pl) { + sbar_setcomponent (hud_pl_view, canvas_charbuff, &pl_buff); + } else { + sbar_remcomponent (hud_pl_view, canvas_charbuff); + } +} + +static void +sbar_hud_time_f (void *data, const cvar_t *cvar) +{ + if (hud_time) { + void *f = draw_time; + sbar_setcomponent (hud_time_view, canvas_update, &f); + sbar_setcomponent (hud_time_view, canvas_charbuff, &time_buff); + } else { + sbar_remcomponent (hud_time_view, canvas_update); + sbar_remcomponent (hud_time_view, canvas_charbuff); + } +} + +static void +create_views (view_def_t *view_defs, view_t parent) +{ + for (int i = 0; view_defs[i].view || view_defs[i].parent; i++) { + view_def_t *def = &view_defs[i]; + view_t p = parent; + int x = def->rect.x; + int y = def->rect.y; + int w = def->rect.w; + int h = def->rect.h; + if (def->parent != &pseudo_parent) { + p = def->parent ? *def->parent : nullview; + } + if (def->view) { + *def->view = sbar_view (x, y, w, h, def->gravity, p); + if (def->subviews) { + create_views (def->subviews, *def->view); + } + } else { + for (int j = 0; j < def->count; j++) { + view_t v = sbar_view (x, y, w, h, def->gravity, p); + if (def->subviews) { + create_views (def->subviews, v); + } + x += def->xstep; + y += def->ystep; + } + } + } +} + +static void +init_sbar_views (void) +{ + create_views (sbar_defs, nullview); + view_pos_t slen = View_GetLen (hud_canvas_view); + view_pos_t hlen = View_GetLen (hud_view); + View_SetLen (hud_view, slen.x, hlen.y); + View_SetResize (hud_view, 1, 0); + + for (int i = 0; i < 4; i++) { + view_t v = View_GetChild (sbar_miniammo, i); + draw_charbuffer_t *buffer = Draw_CreateBuffer (3, 1); + Draw_ClearBuffer (buffer); + sbar_setcomponent (v, canvas_charbuff, &buffer); + } + + if (r_data->vid->width > 320) { + int l = (r_data->vid->width - 320) / 2; + View_SetPos (sbar_tile[0], -l, 0); + View_SetLen (sbar_tile[0], l, 48); + View_SetPos (sbar_tile[1], -l, 0); + View_SetLen (sbar_tile[1], l, 48); + sbar_setcomponent (sbar_tile[0], canvas_tile, 0); + sbar_setcomponent (sbar_tile[1], canvas_tile, 0); + } + + solo_monsters = Draw_CreateBuffer (17, 1); + solo_secrets = Draw_CreateBuffer (17, 1); + write_charbuff (solo_monsters, 0, 0, "Monsters:xxx /xxx"); + write_charbuff (solo_secrets, 0, 0, "Secrets :xxx /xxx"); + solo_time = Draw_CreateBuffer (12, 1); + solo_name = Draw_CreateBuffer (20, 1); + + sb_item_count = 6; + if (!strcmp (qfs_gamedir->hudtype, "hipnotic")) { + sb_item_count = 8; + // adjust key view locations and sizes + for (int i = 0; i < 2; i++) { + view_t v = View_GetChild (sbar_items, i); + View_SetPos (v, 16, 16 + 3 + 9 * i); + View_SetLen (v, 16, 9); + } + } + if (!strcmp (qfs_gamedir->hudtype, "rogue")) { + sb_item_count = 8; + } +} + +static void +init_views (void) +{ + hud_stuff_view = sbar_view (0, 48, 152, 16, grav_southwest, hud_canvas_view); + hud_time_view = sbar_view (8, 0, 64, 8, grav_northwest, hud_stuff_view); + hud_fps_view = sbar_view (80, 0, 80, 8, grav_northwest, hud_stuff_view); + hud_ping_view = sbar_view (0, 8, 48, 0, grav_northwest, hud_stuff_view); + hud_pl_view = sbar_view (56, 8, 48, 0, grav_northwest, hud_stuff_view); + + Draw_ClearBuffer (time_buff = Draw_CreateBuffer (8, 1)); + Draw_ClearBuffer (fps_buff = Draw_CreateBuffer (10, 1)); + Draw_ClearBuffer (ping_buff = Draw_CreateBuffer (6, 1)); + Draw_ClearBuffer (pl_buff = Draw_CreateBuffer (6, 1)); + + for (int i = 0; i < MAX_PLAYERS; i++) { + sb_fph[i] = Draw_CreateBuffer (3, 1); + sb_time[i] = Draw_CreateBuffer (4, 1); + sb_frags[i] = Draw_CreateBuffer (3, 1); + sb_ping[i] = Draw_CreateBuffer (3, 1); + sb_pl[i] = Draw_CreateBuffer (3, 1); + sb_uid[i] = Draw_CreateBuffer (4, 1); + sb_name[i] = Draw_CreateBuffer (16, 1); + + sb_team[i] = Draw_CreateBuffer (4, 1); + sb_team_stats[i] = Draw_CreateBuffer (11, 1); + sb_team_frags[i] = Draw_CreateBuffer (5, 1); + sb_team_players[i] = Draw_CreateBuffer (5, 1); + fragsort[i] = -1; + } + sb_spectator = Draw_CreateBuffer (11, 1); + write_charbuff (sb_spectator, 0, 0, "(spectator)"); + + spec_buff[0] = Draw_CreateBuffer (14, 1); + spec_buff[1] = Draw_CreateBuffer (29, 1); + spec_buff[2] = Draw_CreateBuffer (28, 1); + spec_buff[3] = Draw_CreateBuffer (39, 1); + write_charbuff (spec_buff[0], 0, 0, "SPECTATOR MODE"); + write_charbuff (spec_buff[1], 0, 0, "Press [ATTACK] for AutoCamera"); + write_charbuff (spec_buff[2], 0, 0, "Lost player, [JUMP] for next"); + + init_sbar_views (); +} + +static void +Sbar_GIB_Print_Center_f (void) +{ + if (GIB_Argc () != 2) { + GIB_USAGE ("text"); + } else + Sbar_CenterPrint (GIB_Argv(1)); +} + +static void +load_pics (void) +{ + for (int i = 0; i < 10; i++) { + sb_nums[0][i] = r_funcs->Draw_PicFromWad (va (0, "num_%i", i)); + sb_nums[1][i] = r_funcs->Draw_PicFromWad (va (0, "anum_%i", i)); + } + + sb_nums[0][10] = r_funcs->Draw_PicFromWad ("num_minus"); + sb_nums[1][10] = r_funcs->Draw_PicFromWad ("anum_minus"); + + sb_colon = r_funcs->Draw_PicFromWad ("num_colon"); + sb_slash = r_funcs->Draw_PicFromWad ("num_slash"); + + sb_weapons[0][0].pic = r_funcs->Draw_PicFromWad ("inv_shotgun"); + sb_weapons[0][1].pic = r_funcs->Draw_PicFromWad ("inv_sshotgun"); + sb_weapons[0][2].pic = r_funcs->Draw_PicFromWad ("inv_nailgun"); + sb_weapons[0][3].pic = r_funcs->Draw_PicFromWad ("inv_snailgun"); + sb_weapons[0][4].pic = r_funcs->Draw_PicFromWad ("inv_rlaunch"); + sb_weapons[0][5].pic = r_funcs->Draw_PicFromWad ("inv_srlaunch"); + sb_weapons[0][6].pic = r_funcs->Draw_PicFromWad ("inv_lightng"); + + sb_weapons[1][0].pic = r_funcs->Draw_PicFromWad ("inv2_shotgun"); + sb_weapons[1][1].pic = r_funcs->Draw_PicFromWad ("inv2_sshotgun"); + sb_weapons[1][2].pic = r_funcs->Draw_PicFromWad ("inv2_nailgun"); + sb_weapons[1][3].pic = r_funcs->Draw_PicFromWad ("inv2_snailgun"); + sb_weapons[1][4].pic = r_funcs->Draw_PicFromWad ("inv2_rlaunch"); + sb_weapons[1][5].pic = r_funcs->Draw_PicFromWad ("inv2_srlaunch"); + sb_weapons[1][6].pic = r_funcs->Draw_PicFromWad ("inv2_lightng"); + + for (int i = 0; i < 5; i++) { + sb_weapons[2 + i][0].pic = + r_funcs->Draw_PicFromWad (va (0, "inva%i_shotgun", i + 1)); + sb_weapons[2 + i][1].pic = + r_funcs->Draw_PicFromWad (va (0, "inva%i_sshotgun", i + 1)); + sb_weapons[2 + i][2].pic = + r_funcs->Draw_PicFromWad (va (0, "inva%i_nailgun", i + 1)); + sb_weapons[2 + i][3].pic = + r_funcs->Draw_PicFromWad (va (0, "inva%i_snailgun", i + 1)); + sb_weapons[2 + i][4].pic = + r_funcs->Draw_PicFromWad (va (0, "inva%i_rlaunch", i + 1)); + sb_weapons[2 + i][5].pic = + r_funcs->Draw_PicFromWad (va (0, "inva%i_srlaunch", i + 1)); + sb_weapons[2 + i][6].pic = + r_funcs->Draw_PicFromWad (va (0, "inva%i_lightng", i + 1)); + } + + sb_ammo[0] = r_funcs->Draw_PicFromWad ("sb_shells"); + sb_ammo[1] = r_funcs->Draw_PicFromWad ("sb_nails"); + sb_ammo[2] = r_funcs->Draw_PicFromWad ("sb_rocket"); + sb_ammo[3] = r_funcs->Draw_PicFromWad ("sb_cells"); + + sb_armor[0] = r_funcs->Draw_PicFromWad ("sb_armor1"); + sb_armor[1] = r_funcs->Draw_PicFromWad ("sb_armor2"); + sb_armor[2] = r_funcs->Draw_PicFromWad ("sb_armor3"); + + sb_items[0][0] = r_funcs->Draw_PicFromWad ("sb_key1"); + sb_items[0][1] = r_funcs->Draw_PicFromWad ("sb_key2"); + sb_items[0][2] = r_funcs->Draw_PicFromWad ("sb_invis"); + sb_items[0][3] = r_funcs->Draw_PicFromWad ("sb_invuln"); + sb_items[0][4] = r_funcs->Draw_PicFromWad ("sb_suit"); + sb_items[0][5] = r_funcs->Draw_PicFromWad ("sb_quad"); + for (int i = 1; i < 6; i++) { + sb_items[i][0] = r_funcs->Draw_PicFromWad (va (0, "sba%d_key1", i)); + sb_items[i][1] = r_funcs->Draw_PicFromWad (va (0, "sba%d_key2", i)); + sb_items[i][2] = r_funcs->Draw_PicFromWad (va (0, "sba%d_invis", i)); + sb_items[i][3] = r_funcs->Draw_PicFromWad (va (0, "sba%d_invul", i)); + sb_items[i][4] = r_funcs->Draw_PicFromWad (va (0, "sba%d_suit", i)); + sb_items[i][5] = r_funcs->Draw_PicFromWad (va (0, "sba%d_quad", i)); + } + + sb_sigil[0] = r_funcs->Draw_PicFromWad ("sb_sigil1"); + sb_sigil[1] = r_funcs->Draw_PicFromWad ("sb_sigil2"); + sb_sigil[2] = r_funcs->Draw_PicFromWad ("sb_sigil3"); + sb_sigil[3] = r_funcs->Draw_PicFromWad ("sb_sigil4"); + + sb_faces[4][0] = r_funcs->Draw_PicFromWad ("face1"); + sb_faces[4][1] = r_funcs->Draw_PicFromWad ("face_p1"); + sb_faces[3][0] = r_funcs->Draw_PicFromWad ("face2"); + sb_faces[3][1] = r_funcs->Draw_PicFromWad ("face_p2"); + sb_faces[2][0] = r_funcs->Draw_PicFromWad ("face3"); + sb_faces[2][1] = r_funcs->Draw_PicFromWad ("face_p3"); + sb_faces[1][0] = r_funcs->Draw_PicFromWad ("face4"); + sb_faces[1][1] = r_funcs->Draw_PicFromWad ("face_p4"); + sb_faces[0][0] = r_funcs->Draw_PicFromWad ("face5"); + sb_faces[0][1] = r_funcs->Draw_PicFromWad ("face_p5"); + + sb_face_invis = r_funcs->Draw_PicFromWad ("face_invis"); + sb_face_invuln = r_funcs->Draw_PicFromWad ("face_invul2"); + sb_face_invis_invuln = r_funcs->Draw_PicFromWad ("face_inv2"); + sb_face_quad = r_funcs->Draw_PicFromWad ("face_quad"); + + sb_sbar = r_funcs->Draw_PicFromWad ("sbar"); + sb_ibar[0] = r_funcs->Draw_PicFromWad ("ibar"); + sb_scorebar = r_funcs->Draw_PicFromWad ("scorebar"); + sb_weapon_count = 7; + sb_weapon_view_count = 7; + sb_game = 0; + + // MED 01/04/97 added new hipnotic weapons + if (!strcmp (qfs_gamedir->hudtype, "hipnotic")) { + sb_weapon_count = 10; + sb_weapon_view_count = 9; + sb_weapons[0][7].pic = r_funcs->Draw_PicFromWad ("inv_laser"); + sb_weapons[0][8].pic = r_funcs->Draw_PicFromWad ("inv_mjolnir"); + sb_weapons[0][9].pic = r_funcs->Draw_PicFromWad ("inv_prox_gren"); + //sb_weapons[0][3].pic = r_funcs->Draw_PicFromWad ("inv_gren_prox"); + //sb_weapons[0][4] = r_funcs->Draw_PicFromWad ("inv_prox"); + + sb_weapons[1][7].pic = r_funcs->Draw_PicFromWad ("inv2_laser"); + sb_weapons[1][8].pic = r_funcs->Draw_PicFromWad ("inv2_mjolnir"); + sb_weapons[1][9].pic = r_funcs->Draw_PicFromWad ("inv2_prox_gren"); + //sb_weapons[1][3].pic = r_funcs->Draw_PicFromWad ("inv2_gren_prox"); + //sb_weapons[1][4] = r_funcs->Draw_PicFromWad ("inv2_prox"); + + for (int i = 0; i < 5; i++) { + sb_weapons[2 + i][7].pic = + r_funcs->Draw_PicFromWad (va (0, "inva%i_laser", i + 1)); + sb_weapons[2 + i][8].pic = + r_funcs->Draw_PicFromWad (va (0, "inva%i_mjolnir", i + 1)); + sb_weapons[2 + i][9].pic = + r_funcs->Draw_PicFromWad (va (0, "inva%i_prox_gren", i + 1)); + //sb_weapons[2 + i][2] = + // r_funcs->Draw_PicFromWad (va (0, "inva%i_gren_prox", i + 1)); + //sb_weapons[2 + i][4] = + // r_funcs->Draw_PicFromWad (va (0, "inva%i_prox", i + 1)); + } + + sb_items[0][6] = r_funcs->Draw_PicFromWad ("sb_wsuit"); + sb_items[0][7] = r_funcs->Draw_PicFromWad ("sb_eshld"); + } + + // FIXME: MISSIONHUD + if (!strcmp (qfs_gamedir->hudtype, "rogue")) { + sb_weapon_count = 12; + sb_game = 1; + sb_ibar[0] = r_funcs->Draw_PicFromWad ("r_invbar1"); + sb_ibar[1] = r_funcs->Draw_PicFromWad ("r_invbar2"); + + sb_weapons[0][7].pic = r_funcs->Draw_PicFromWad ("r_lava"); + sb_weapons[0][8].pic = r_funcs->Draw_PicFromWad ("r_superlava"); + sb_weapons[0][9].pic = r_funcs->Draw_PicFromWad ("r_gren"); + sb_weapons[0][10].pic = r_funcs->Draw_PicFromWad ("r_multirock"); + sb_weapons[0][11].pic = r_funcs->Draw_PicFromWad ("r_plasma"); + for (int i = 1; i < 7; i++) { + sb_weapons[i][7].pic = sb_weapons[0][7].pic; + sb_weapons[i][8].pic = sb_weapons[0][8].pic; + sb_weapons[i][9].pic = sb_weapons[0][9].pic; + sb_weapons[i][10].pic = sb_weapons[0][10].pic; + sb_weapons[i][11].pic = sb_weapons[0][11].pic; + } + + sb_items[0][6] = r_funcs->Draw_PicFromWad ("r_shield1"); + sb_items[0][7] = r_funcs->Draw_PicFromWad ("r_agrav1"); + + // PGM 01/19/97 - team color border + rsb_teambord = r_funcs->Draw_PicFromWad ("r_teambord"); + // PGM 01/19/97 - team color border + + // It seems the pics for plasma and multi-rockets are swapped + sb_ammo[4] = r_funcs->Draw_PicFromWad ("r_ammolava"); + sb_ammo[5] = r_funcs->Draw_PicFromWad ("r_ammoplasma"); + sb_ammo[6] = r_funcs->Draw_PicFromWad ("r_ammomulti"); + } + + for (int i = 0; i < 7; i++) { + for (int j = 0; j < 12; j++) { + if (sb_weapons[i][j].pic) { + sb_weapons[i][j].w = sb_weapons[i][j].pic->width; + sb_weapons[i][j].h = sb_weapons[i][j].pic->height; + } + } + } + for (int i = 1; i < 6; i++) { + for (int j = 0; j < 32; j++) { + if (!sb_items[i][j]) { + sb_items[i][j] = sb_items[0][j]; + } + } + } + if (!sb_ibar[1]) { + sb_ibar[1] = sb_ibar[0]; + } + for (int i = 0; i < 4; i++) { + sb_miniammo[i] = (canvas_subpic_t) { + .pic = sb_ibar[0], + .x = 3 + (i * 48), + .y = 0, + .w = 42, + .h = 11, + }; + } +} + +static void +Sbar_ShowScores (void) +{ + sbar_showscores = true; + draw_status (); +} + +static void +Sbar_DontShowScores (void) +{ + if (sbar_autotrack >= 0 && sbar_stats[STAT_HEALTH] <= 0) { + return; + } + sbar_showscores = false; + hide_status (); +} + +static void +Sbar_ShowTeamScores (void) +{ + if (sbar_showteamscores) + return; + + sbar_showteamscores = true; + sb_updates = 0; +} + +static void +Sbar_DontShowTeamScores (void) +{ + sbar_showteamscores = false; + sb_updates = 0; +} + +void +Sbar_SetPlayers (player_info_t *players, int maxplayers) +{ + sbar_players = players; + sbar_maxplayers = maxplayers; +} + +void +Sbar_Init (int *stats, float *item_gettime) +{ + sbar_stats = stats; + sbar_item_gettime = item_gettime; + + sbar_viewsys = (ecs_system_t) { + .reg = cl_canvas_sys.reg, + .base = cl_canvas_sys.view_base, + }; + + center_passage.reg = hud_psgsys.reg; + center_passage.comp_base = hud_psgsys.base; + HUD_Init_Cvars (); + Cvar_AddListener (Cvar_FindVar ("hud_sbar"), sbar_hud_sbar_f, 0); + Cvar_AddListener (Cvar_FindVar ("hud_swap"), sbar_hud_swap_f, 0); + Cvar_AddListener (Cvar_FindVar ("hud_fps"), sbar_hud_fps_f, 0); + Cvar_AddListener (Cvar_FindVar ("hud_time"), sbar_hud_time_f, 0); + Cvar_AddListener (Cvar_FindVar ("hud_pl"), sbar_hud_pl_f, 0); + Cvar_AddListener (Cvar_FindVar ("hud_ping"), sbar_hud_ping_f, 0); + + load_pics (); + init_views (); + + View_UpdateHierarchy (sbar_main); + set_hud_sbar (); + View_UpdateHierarchy (sbar_main); + + sbar_hud_fps_f (0, 0); + sbar_hud_time_f (0, 0); + sbar_hud_pl_f (0, 0); + sbar_hud_ping_f (0, 0); + + Cmd_AddCommand ("+showscores", Sbar_ShowScores, + "Display information on everyone playing"); + Cmd_AddCommand ("-showscores", Sbar_DontShowScores, + "Stop displaying information on everyone playing"); + Cmd_AddCommand ("+showteamscores", Sbar_ShowTeamScores, + "Display information for your team"); + Cmd_AddCommand ("-showteamscores", Sbar_DontShowTeamScores, + "Stop displaying information for your team"); + + r_data->viewsize_callback = viewsize_f; + Cvar_Register (&hud_scoreboard_uid_cvar, 0/*FIXME Sbar_DMO_Init_f*/, 0); + Cvar_Register (&fs_fraglog_cvar, 0, 0); + Cvar_Register (&cl_fraglog_cvar, 0, 0); + Cvar_Register (&scr_centertime_cvar, 0, 0); + Cvar_Register (&scr_printspeed_cvar, 0, 0); + + // register GIB builtins + GIB_Builtin_Add ("print::center", Sbar_GIB_Print_Center_f); +} diff --git a/libs/console/Makefile.am b/libs/console/Makefile.am deleted file mode 100644 index d7cce92e7..000000000 --- a/libs/console/Makefile.am +++ /dev/null @@ -1,43 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CFLAGS= @PREFER_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include $(FNM_FLAGS) -plugin_ldflags= @plugin_ldflags@ -avoid-version -module -rpath $(plugindir) -plugin_libadd= @plugin_libadd@ -lib_ldflags=-version-info $(QUAKE_LIBRARY_VERSION_INFO) \ - -rpath $(libdir) -no-undefined -EXEEXT= - -lib_LTLIBRARIES= libQFconsole.la -plugin_LTLIBRARIES= @console_plugins@ -noinst_LTLIBRARIES= @client_static_plugins@ @server_static_plugins@ -EXTRA_LTLIBRARIES= console_server.la console_client.la - -common_sources= \ - buffer.c complete.c console.c inputline.c list.c filelist.c view.c -client_sources= bi_inputline.c client.c menu.c -server_sources= server.c - -console_deps=$(top_builddir)/libs/util/libQFutil.la -client_deps= libQFconsole.la \ - $(top_builddir)/libs/audio/libQFsound.la \ - $(top_builddir)/libs/ruamoko/libQFruamoko.la \ - $(top_builddir)/libs/gib/libQFgib.la \ - $(top_builddir)/libs/ruamoko/libQFruamoko.la \ - $(console_deps) -server_deps= libQFconsole.la $(console_deps) - -libQFconsole_la_LDFLAGS= $(lib_ldflags) -libQFconsole_la_LIBADD= $(console_deps) $(plugin_libadd) -libQFconsole_la_DEPENDENCIES= $(console_deps) -libQFconsole_la_SOURCES= $(common_sources) - -console_client_la_LDFLAGS= $(plugin_ldflags) -console_client_la_LIBADD= $(client_deps) $(plugin_libadd) -console_client_la_DEPENDENCIES= $(client_deps) -console_client_la_SOURCES= $(client_sources) - -console_server_la_LDFLAGS= $(plugin_ldflags) -console_server_la_LIBADD= $(server_deps) $(CURSES_LIBS) $(plugin_libadd) -console_server_la_DEPENDENCIES= $(server_deps) -console_server_la_SOURCES= $(server_sources) diff --git a/libs/console/Makemodule.am b/libs/console/Makemodule.am new file mode 100644 index 000000000..bf8a24f30 --- /dev/null +++ b/libs/console/Makemodule.am @@ -0,0 +1,47 @@ +include libs/console/test/Makemodule.am + +lib_LTLIBRARIES += libs/console/libQFconsole.la +plugin_LTLIBRARIES += @console_plugins@ +noinst_LTLIBRARIES += @client_static_plugins@ @server_static_plugins@ +EXTRA_LTLIBRARIES += libs/console/console_server.la libs/console/console_client.la + +console_common_sources= \ + libs/console/buffer.c \ + libs/console/complete.c \ + libs/console/console.c \ + libs/console/list.c \ + libs/console/filelist.c +client_sources= \ + libs/console/bi_inputline.c \ + libs/console/client.c \ + libs/console/menu.c +server_sources= \ + libs/console/server.c + +console_deps=\ + libs/ui/libQFui.la \ + libs/util/libQFutil.la +client_deps= \ + libs/console/libQFconsole.la \ + libs/audio/libQFsound.la \ + libs/ruamoko/libQFruamoko.la \ + libs/gib/libQFgib.la \ + libs/ruamoko/libQFruamoko_client.la \ + libs/ruamoko/libQFruamoko.la \ + $(console_deps) +server_deps= libs/console/libQFconsole.la $(console_deps) + +libs_console_libQFconsole_la_LDFLAGS= $(lib_ldflags) +libs_console_libQFconsole_la_LIBADD= $(console_deps) $(plugin_libadd) +libs_console_libQFconsole_la_DEPENDENCIES= $(console_deps) +libs_console_libQFconsole_la_SOURCES= $(console_common_sources) + +libs_console_console_client_la_LDFLAGS= $(plugin_ldflags) +libs_console_console_client_la_LIBADD= $(client_deps) $(plugin_libadd) +libs_console_console_client_la_DEPENDENCIES=$(client_deps) +libs_console_console_client_la_SOURCES= $(client_sources) + +libs_console_console_server_la_LDFLAGS= $(plugin_ldflags) +libs_console_console_server_la_LIBADD= $(server_deps) $(NCURSES_LIBS) $(plugin_libadd) +libs_console_console_server_la_DEPENDENCIES=$(server_deps) +libs_console_console_server_la_SOURCES= $(server_sources) diff --git a/libs/console/bi_inputline.c b/libs/console/bi_inputline.c index e4a936d6f..78cf923f3 100644 --- a/libs/console/bi_inputline.c +++ b/libs/console/bi_inputline.c @@ -36,20 +36,21 @@ # include #endif -#include "QF/console.h" #include "QF/csqc.h" #include "QF/draw.h" #include "QF/progs.h" #include "QF/sys.h" #include "QF/zone.h" +#include "QF/ui/inputline.h" + typedef struct il_data_s { struct il_data_s *next; struct il_data_s **prev; inputline_t *line; progs_t *pr; - func_t enter; // enter key callback - pointer_t data[2]; // allow two data params for the callback + pr_func_t enter; // enter key callback + pr_ptr_t data[2]; // allow two data params for the callback int method; // true if method rather than function } il_data_t; @@ -62,37 +63,37 @@ typedef struct { static il_data_t * il_data_new (il_resources_t *res) { - PR_RESNEW (il_data_t, res->line_map); + return PR_RESNEW (res->line_map); } static void il_data_free (il_resources_t *res, il_data_t *line) { - PR_RESFREE (il_data_t, res->line_map, line); + PR_RESFREE (res->line_map, line); } static void il_data_reset (il_resources_t *res) { - PR_RESRESET (il_data_t, res->line_map); + PR_RESRESET (res->line_map); } static inline il_data_t * il_data_get (il_resources_t *res, unsigned index) { - PR_RESGET (res->line_map, index); + return PR_RESGET (res->line_map, index); } -static inline int +static inline int __attribute__((pure)) il_data_index (il_resources_t *res, il_data_t *line) { - PR_RESINDEX (res->line_map, line); + return PR_RESINDEX (res->line_map, line); } static void -bi_il_clear (progs_t *pr, void *data) +bi_il_clear (progs_t *pr, void *_res) { - il_resources_t *res = (il_resources_t *)data; + il_resources_t *res = (il_resources_t *)_res; il_data_t *line; for (line = res->lines; line; line = line->next) @@ -101,10 +102,17 @@ bi_il_clear (progs_t *pr, void *data) il_data_reset (res); } -static il_data_t * -get_inputline (progs_t *pr, int arg, const char *func) +static void +bi_il_destroy (progs_t *pr, void *_res) +{ + il_resources_t *res = _res; + PR_RESDELMAP (res->line_map); + free (res); +} + +static il_data_t * __attribute__((pure)) +get_inputline (progs_t *pr, il_resources_t *res, int arg, const char *func) { - il_resources_t *res = PR_Resources_Find (pr, "InputLine"); il_data_t *line = il_data_get (res, arg); // line->prev will be null if the handle is unallocated @@ -130,18 +138,20 @@ bi_inputline_enter (inputline_t *il) P_POINTER (pr, 0) = data->data[0]; P_POINTER (pr, 1) = data->data[1]; P_STRING (pr, 2) = PR_SetTempString (pr, line); + pr->pr_argc = 3; } else { P_STRING (pr, 0) = PR_SetTempString (pr, line); P_POINTER (pr, 1) = data->data[0]; + pr->pr_argc = 2; } PR_ExecuteProgram (pr, data->enter); PR_PopFrame (pr); } static void -bi_InputLine_Create (progs_t *pr) +bi_InputLine_Create (progs_t *pr, void *_res) { - il_resources_t *res = PR_Resources_Find (pr, "InputLine"); + il_resources_t *res = _res; il_data_t *data; inputline_t *line; int lines = P_INT (pr, 0); @@ -179,26 +189,29 @@ bi_InputLine_Create (progs_t *pr) } static void -bi_InputLine_SetPos (progs_t *pr) +bi_InputLine_SetPos (progs_t *pr, void *_res) { - il_data_t *line = get_inputline (pr, P_INT (pr, 0), + il_resources_t *res = _res; + il_data_t *line = get_inputline (pr, res, P_INT (pr, 0), "InputLine_SetPos"); line->line->x = P_INT (pr, 1); line->line->y = P_INT (pr, 2); } static void -bi_InputLine_SetCursor (progs_t *pr) +bi_InputLine_SetCursor (progs_t *pr, void *_res) { - il_data_t *line = get_inputline (pr, P_INT (pr, 0), + il_resources_t *res = _res; + il_data_t *line = get_inputline (pr, res, P_INT (pr, 0), "InputLine_SetCursor"); line->line->cursor = P_INT (pr, 1); } static void -bi_InputLine_SetEnter (progs_t *pr) +bi_InputLine_SetEnter (progs_t *pr, void *_res) { - il_data_t *line = get_inputline (pr, P_INT (pr, 0), + il_resources_t *res = _res; + il_data_t *line = get_inputline (pr, res, P_INT (pr, 0), "InputLine_SetEnter"); line->data[1] = 0; @@ -218,9 +231,10 @@ bi_InputLine_SetEnter (progs_t *pr) } static void -bi_InputLine_SetWidth (progs_t *pr) +bi_InputLine_SetWidth (progs_t *pr, void *_res) { - il_data_t *line = get_inputline (pr, P_INT (pr, 0), + il_resources_t *res = _res; + il_data_t *line = get_inputline (pr, res, P_INT (pr, 0), "InputLine_SetWidth"); int width = P_INT (pr, 1); @@ -228,10 +242,11 @@ bi_InputLine_SetWidth (progs_t *pr) } static void -bi_InputLine_Destroy (progs_t *pr) +bi_InputLine_Destroy (progs_t *pr, void *_res) { - il_resources_t *res = PR_Resources_Find (pr, "InputLine"); - il_data_t *line = get_inputline (pr, P_INT (pr, 0), "InputLine_Destroy"); + il_resources_t *res = _res; + il_data_t *line = get_inputline (pr, res, P_INT (pr, 0), + "InputLine_Destroy"); Con_DestroyInputLine (line->line); *line->prev = line->next; @@ -241,18 +256,22 @@ bi_InputLine_Destroy (progs_t *pr) } static void -bi_InputLine_Clear (progs_t *pr) +bi_InputLine_Clear (progs_t *pr, void *_res) { - il_data_t *line = get_inputline (pr, P_INT (pr, 0), "InputLine_Clear"); + il_resources_t *res = _res; + il_data_t *line = get_inputline (pr, res, P_INT (pr, 0), + "InputLine_Clear"); int save = P_INT (pr, 1); Con_ClearTyping (line->line, save); } static void -bi_InputLine_Process (progs_t *pr) +bi_InputLine_Process (progs_t *pr, void *_res) { - il_data_t *line = get_inputline (pr, P_INT (pr, 0), "InputLine_Process"); + il_resources_t *res = _res; + il_data_t *line = get_inputline (pr, res, P_INT (pr, 0), + "InputLine_Process"); int ch = P_INT (pr, 1); Con_ProcessInputLine (line->line, ch); @@ -264,9 +283,11 @@ bi_InputLine_Process (progs_t *pr) Sets the inputline to a specified text */ static void -bi_InputLine_SetText (progs_t *pr) +bi_InputLine_SetText (progs_t *pr, void *_res) { - il_data_t *line = get_inputline (pr, P_INT (pr, 0), "InputLine_SetText"); + il_resources_t *res = _res; + il_data_t *line = get_inputline (pr, res, P_INT (pr, 0), + "InputLine_SetText"); const char *str = P_GSTRING (pr, 1); inputline_t *il = line->line; @@ -283,37 +304,43 @@ bi_InputLine_SetText (progs_t *pr) Gets the text from a inputline */ static void -bi_InputLine_GetText (progs_t *pr) +bi_InputLine_GetText (progs_t *pr, void *_res) { - il_data_t *line = get_inputline (pr, P_INT (pr, 0), "InputLine_GetText"); + il_resources_t *res = _res; + il_data_t *line = get_inputline (pr, res, P_INT (pr, 0), + "InputLine_GetText"); inputline_t *il = line->line; RETURN_STRING(pr, il->lines[il->edit_line]+1); } static void -bi_InputLine_Draw (progs_t *pr) +bi_InputLine_Draw (progs_t *pr, void *_res) { - il_data_t *line = get_inputline (pr, P_INT (pr, 0), "InputLine_Draw"); + il_resources_t *res = _res; + il_data_t *line = get_inputline (pr, res, P_INT (pr, 0), + "InputLine_Draw"); line->line->draw (line->line); } +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) static builtin_t builtins[] = { - {"InputLine_Create", bi_InputLine_Create, -1}, - {"InputLine_SetPos", bi_InputLine_SetPos, -1}, - {"InputLine_SetCursor", bi_InputLine_SetCursor, -1}, + bi(InputLine_Create, 3, p(int), p(int), p(int)), + bi(InputLine_SetPos, 3, p(ptr), p(int), p(int)), + bi(InputLine_SetCursor, 2, p(ptr), p(int)), {"InputLine_SetEnter|^{tag _inputline_t=}(v*^v)^v", - bi_InputLine_SetEnter, -1}, + bi_InputLine_SetEnter, -1, 3, {p(ptr), p(func), p(ptr)}}, {"InputLine_SetEnter|^{tag _inputline_t=}(@@:.)@:", - bi_InputLine_SetEnter, -1}, - {"InputLine_SetWidth", bi_InputLine_SetWidth, -1}, - {"InputLine_SetText", bi_InputLine_SetText, -1}, - {"InputLine_GetText", bi_InputLine_GetText, -1}, - {"InputLine_Destroy", bi_InputLine_Destroy, -1}, - {"InputLine_Clear", bi_InputLine_Clear, -1}, - {"InputLine_Process", bi_InputLine_Process, -1}, - {"InputLine_Draw", bi_InputLine_Draw, -1}, + bi_InputLine_SetEnter, -1, 4, {p(ptr), p(func), p(ptr), p(ptr)}}, + bi(InputLine_SetWidth, 2, p(ptr), p(int)), + bi(InputLine_SetText, 2, p(ptr), p(string)), + bi(InputLine_GetText, 1, p(ptr)), + bi(InputLine_Destroy, 1, p(ptr)), + bi(InputLine_Clear, 2, p(ptr), p(int)), + bi(InputLine_Process, 2, p(ptr), p(int)), + bi(InputLine_Draw, 1, p(ptr)), {0} }; @@ -322,8 +349,8 @@ InputLine_Progs_Init (progs_t *pr) { il_resources_t *res = calloc (1, sizeof (il_resources_t)); - PR_Resources_Register (pr, "InputLine", res, bi_il_clear); - PR_RegisterBuiltins (pr, builtins); + PR_Resources_Register (pr, "InputLine", res, bi_il_clear, bi_il_destroy); + PR_RegisterBuiltins (pr, builtins, res); } VISIBLE void diff --git a/libs/console/buffer.c b/libs/console/buffer.c index d494c0655..9f0b08c5e 100644 --- a/libs/console/buffer.c +++ b/libs/console/buffer.c @@ -55,9 +55,8 @@ Con_CreateBuffer (size_t buffer_size, int max_lines) if (!(buffer->lines = calloc (max_lines, sizeof (con_line_t)))) goto err; buffer->max_lines = max_lines; - buffer->num_lines = 1; - buffer->cur_line = 0; - buffer->lines[0].text = buffer->buffer; + buffer->line_head = 1; + buffer->line_tail = 0; return buffer; err: if (buffer->buffer) @@ -77,43 +76,61 @@ Con_DestroyBuffer (con_buffer_t *buffer) VISIBLE void Con_BufferAddText (con_buffer_t *buf, const char *text) { - con_line_t *cur_line = &buf->lines[buf->cur_line]; - con_line_t *tail_line; - size_t len = strlen (text); - byte *pos = cur_line->text + cur_line->len; + con_line_t *cur_line = &buf->lines[(buf->line_head - 1 + buf->max_lines) + % buf->max_lines]; + con_line_t *tail_line = &buf->lines[buf->line_tail]; + uint32_t text_head = (cur_line->text + cur_line->len) % buf->buffer_size; + byte c; - if (pos >= buf->buffer + buf->buffer_size) - pos -= buf->buffer_size; - tail_line = buf->lines + (buf->cur_line + buf->max_lines + 1 - - buf->num_lines) % buf->max_lines; - if (len > buf->buffer_size) { - text += len - buf->buffer_size; - len = buf->buffer_size; - } - while (len--) { - byte c = *pos++ = *text++; - if ((size_t) (pos - buf->buffer) >= buf->buffer_size) - pos = buf->buffer; + while ((c = *text++)) { cur_line->len++; - if (pos == tail_line->text) { - if (buf->num_lines > 0) - buf->num_lines--; - tail_line->text = 0; - tail_line->len = 0; - tail_line++; - if (tail_line - buf->lines >= buf->max_lines) - tail_line = buf->lines; + buf->buffer[text_head++] = c; + if (text_head >= buf->buffer_size) { + text_head -= buf->buffer_size; + } + + if (text_head == tail_line->text) { + tail_line->len--; + buf->buffer[tail_line->text++] = 0; + if (tail_line->text >= buf->buffer_size) { + tail_line->text -= buf->buffer_size; + } + if (!tail_line->len) { + buf->line_tail++; + buf->line_tail %= buf->max_lines; + tail_line = &buf->lines[buf->line_tail]; + } } if (c == '\n') { - if (buf->num_lines < buf->max_lines) - buf->num_lines++; cur_line++; - buf->cur_line++; - if (cur_line - buf->lines >= buf->max_lines) - cur_line = buf->lines; - cur_line->text = pos; + if ((uint32_t) (cur_line - buf->lines) >= buf->max_lines) { + cur_line -= buf->max_lines; + } + cur_line->text = text_head; cur_line->len = 0; + + buf->line_head++; + if (buf->line_head >= buf->max_lines) { + buf->line_head -= buf->max_lines; + } + + if (buf->line_head == buf->line_tail) { + buf->lines[buf->line_tail].text = 0; + buf->lines[buf->line_tail].len = 0; + buf->line_tail++; + if (buf->line_tail >= buf->max_lines) { + buf->line_tail -= buf->max_lines; + } + } } } - buf->cur_line %= buf->max_lines; +} + +VISIBLE void +Con_ClearBuffer (con_buffer_t *buffer) +{ + buffer->line_head = 1; + buffer->line_tail = 0; + buffer->lines[0].text = 0; + buffer->lines[0].len = 0; } diff --git a/libs/console/client.c b/libs/console/client.c index f31ccada9..446912ab6 100644 --- a/libs/console/client.c +++ b/libs/console/client.c @@ -1,7 +1,7 @@ /* - console.c + client.c - (description) + Client console routines Copyright (C) 1996-1997 Id Software, Inc. @@ -42,235 +42,346 @@ # include #endif -#include "QF/cbuf.h" #include "QF/cmd.h" #include "QF/console.h" #include "QF/cvar.h" -#include "QF/draw.h" #include "QF/dstring.h" -#include "QF/gib.h" -#include "QF/input.h" #include "QF/keys.h" #include "QF/qargs.h" #include "QF/quakefs.h" -#include "QF/render.h" -#include "QF/screen.h" -#include "QF/sys.h" #include "QF/va.h" -#include "QF/vid.h" -#include "QF/view.h" + +#include "QF/input/event.h" #include "QF/plugin/general.h" #include "QF/plugin/console.h" #include "QF/plugin/vid_render.h" +#include "QF/ui/canvas.h" +#include "QF/ui/inputline.h" +#include "QF/ui/view.h" + #include "compat.h" -static general_data_t plugin_info_general_data; -console_data_t con_data; +static con_buffer_t *con; -static old_console_t con_main; -static old_console_t con_chat; -static old_console_t *con; +static float con_cursorspeed = 4; -static float con_cursorspeed = 4; +static int con_scale; +static cvar_t con_scale_cvar = { + .name = "con_scale", + .description = + "Pixel scale factor for the console and all 2D user interface " + "elements", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &con_scale }, +}; +static float con_notifytime; +static cvar_t con_notifytime_cvar = { + .name = "con_notifytime", + .description = + "How long in seconds messages are displayed on screen", + .default_value = "3", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &con_notifytime }, +}; +static float con_alpha; +static cvar_t con_alpha_cvar = { + .name = "con_alpha", + .description = + "alpha value for the console background", + .default_value = "0.6", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &con_alpha }, +}; +static float con_size; +static cvar_t con_size_cvar = { + .name = "con_size", + .description = + "Fraction of the screen the console covers when down", + .default_value = "0.5", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &con_size }, +}; +static float con_speed; +static cvar_t con_speed_cvar = { + .name = "con_speed", + .description = + "How quickly the console scrolls up or down", + .default_value = "300", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &con_speed }, +}; +static exprenum_t cl_conmode_enum; +static exprtype_t cl_conmode_type = { + .name = "cl_conmode", + .size = sizeof (con_data.exec_line), + .data = &cl_conmode_enum, + .get_string = cexpr_enum_get_string, +}; +static int cl_exec_line_command (void *data, const char *line); +static int cl_exec_line_chat (void *data, const char *line); +static int cl_exec_line_rcon (void *data, const char *line); +static int (*cl_conmode_values[])(void *, const char *) = { + cl_exec_line_command, + cl_exec_line_chat, + cl_exec_line_rcon, +}; +static exprsym_t cl_conmode_symbols[] = { + {"command", &cl_conmode_type, cl_conmode_values + 0}, + {"chat", &cl_conmode_type, cl_conmode_values + 1}, + {"rcon", &cl_conmode_type, cl_conmode_values + 1}, + {} +}; +static exprtab_t cl_conmode_symtab = { + cl_conmode_symbols, +}; +static exprenum_t cl_conmode_enum = { + &cl_conmode_type, + &cl_conmode_symtab, +}; +static cvar_t cl_conmode_cvar = { + .name = "cl_conmode", + .description = + "Set the console input mode (command, chat, rcon)", + .default_value = "command", + .flags = CVAR_ARCHIVE, + .value = { .type = &cl_conmode_type, .value = &con_data.exec_line }, +}; -static cvar_t *con_notifytime; // seconds -static cvar_t *con_alpha; -static cvar_t *con_size; -static cvar_t *con_speed; -static cvar_t *cl_conmode; +static uint32_t client_base; +static uint32_t canvas_base; +static uint32_t view_base; -#define NUM_CON_TIMES 4 -static float con_times[NUM_CON_TIMES]; // realtime time the line was generated - // for transparent notify lines +static con_state_t con_state; +static int con_event_id; +static int con_saved_focos; -static int con_totallines; // total lines in console scrollback +static bool con_debuglog; +static bool chat_team; +static dstring_t *c_print_buffer; +static dstring_t *dlbar; +static dstring_t *old_gamedir = 0; -static qboolean con_debuglog; -static qboolean chat_team; -#define MAXCMDLINE 256 -static inputline_t *input_line; -static inputline_t *say_line; -static inputline_t *say_team_line; +typedef struct { + const char *prompt; + inputline_t *input_line; + draw_charbuffer_t *buffer; +} con_input_t; -static view_t *console_view; -static view_t *say_view; -static view_t *notify_view; -static view_t *menu_view; -static view_t *hud_view; +enum { + client_input, + client_cursor, -static qboolean con_initialized; + client_comp_count +}; -static keydest_t con_keydest; +static const component_t client_components[client_comp_count] = { + [client_input] = { + .size = sizeof (con_input_t *), + .name = "input", + }, + [client_cursor] = { + .size = sizeof (con_input_t *), + .name = "cursor", + }, +}; + +console_data_t con_data = { + .components = client_components, + .num_components = client_comp_count, +}; + +#define MAXCMDLINE 256 + +static qpic_t *conback; +static con_input_t cmd_line; +static con_input_t say_line; +static inputline_t *chat_input; +static inputline_t *team_input; + +static uint32_t screen_canvas; +static view_t screen_view; +static view_t console_view; +static view_t buffer_view; +static view_t command_view; +static view_t download_view; +static view_t notify_view; +static view_t say_view; +static view_t menu_view; + +#define CON_BUFFER_SIZE 32768 +#define CON_LINES 1024 +static draw_charbuffer_t *console_buffer; +static con_buffer_t *con_main; +static int view_offset; +static draw_charbuffer_t *download_buffer; + +#define NOTIFY_LINES 4 +static draw_charbuffer_t *notify_buffer; +// ring buffer holding realtime time the line was generated +static float notify_times[NOTIFY_LINES + 1]; +static int notify_head; +static int notify_tail; + +static bool con_initialized; + +static inline void * +con_getcomponent (view_t view, uint32_t comp) +{ + return Ent_GetComponent (view.id, comp, view.reg); +} + +static inline int +con_hascomponent (view_t view, uint32_t comp) +{ + return Ent_HasComponent (view.id, comp, view.reg); +} + +static inline void * +con_setcomponent (view_t view, uint32_t comp, void *data) +{ + return Ent_SetComponent (view.id, comp, view.reg, data); +} + +static void +con_setfunc (view_t view, uint32_t comp, canvas_update_f func) +{ + con_setcomponent (view, canvas_base + comp, &func); +} + +static void +con_setinput (view_t view, con_input_t *input) +{ + con_setcomponent (view, client_base + client_input, &input); +} + +static con_input_t * +con_getinput (view_t view) +{ + return *(con_input_t**)con_getcomponent (view, client_base + client_input); +} + +static int +con_hasinput (view_t view) +{ + return con_hascomponent (view, client_base + client_input); +} + +static void +con_setfitpic (view_t view, qpic_t *pic) +{ + con_setcomponent (view, canvas_base + canvas_fitpic, &pic); +} + +static void +con_setcharbuf (view_t view, draw_charbuffer_t *buffer) +{ + con_setcomponent (view, canvas_base + canvas_charbuff, &buffer); +} + +static void +con_setcursor (view_t view, con_input_t *input) +{ + con_setcomponent (view, client_base + client_cursor, &input); +} + +static inline void +con_remcomponent (view_t view, uint32_t comp) +{ + Ent_RemoveComponent (view.id, comp, view.reg); +} + +static void +con_remfunc (view_t view, uint32_t comp) +{ + con_remcomponent (view, canvas_base + comp); +} + +static inline void +con_remcharbuf (view_t view) +{ + con_remcomponent (view, canvas_base + canvas_charbuff); +} + +static inline void +con_remcursor (view_t view) +{ + con_remcomponent (view, client_base + client_cursor); +} + +static void +load_conback (const char *path) +{ + qpic_t *p; + if (strlen (path) < 4 || strcmp (path + strlen (path) - 4, ".lmp") + || !(p = (qpic_t *) QFS_LoadFile (QFS_FOpenFile (path), 0))) { + return; + } + conback = r_funcs->Draw_MakePic (p->width, p->height, p->data); + free (p); +} static void ClearNotify (void) { - int i; - - for (i = 0; i < NUM_CON_TIMES; i++) - con_times[i] = 0; + Draw_ClearBuffer (notify_buffer); + notify_head = notify_tail = 0; } +static void +C_SetState (con_state_t state) +{ + con_state_t old_state = con_state; + con_state = state; + if (con_state == con_inactive) { + IE_Set_Focus (con_saved_focos); + } else if (old_state == con_inactive) { + con_saved_focos = IE_Get_Focus (); + IE_Set_Focus (con_event_id); + } + + if (state == con_message) { + say_line.prompt = "say:"; + say_line.input_line = chat_input; + if (chat_team) { + say_line.prompt = "say_team:"; + say_line.input_line = team_input; + } + __auto_type buffer = say_line.buffer; + buffer->cursx = 0; + Draw_PrintBuffer (buffer, say_line.prompt); + say_line.input_line->width = buffer->width - buffer->cursx; + } + + if (con_state == con_menu && old_state != con_menu) { + Menu_Enter (); + } +} static void ToggleConsole_f (void) { - Con_ClearTyping (input_line, 0); - - if (con_keydest == key_console && !con_data.force_commandline) { - Key_SetKeyDest (key_game); - } else { - Key_SetKeyDest (key_console); + switch (con_state) { + case con_menu: + case con_message: + return; + case con_inactive: + C_SetState (con_active); + break; + case con_active: + C_SetState (con_inactive); + break; + case con_fullscreen: + break; } + Con_ClearTyping (cmd_line.input_line, 0); ClearNotify (); } -static void -ToggleChat_f (void) -{ - Con_ClearTyping (input_line, 0); - - if (con_keydest == key_console && !con_data.force_commandline) { - Key_SetKeyDest (key_game); - } else { - Key_SetKeyDest (key_console); - } - - ClearNotify (); -} - -static void -Clear_f (void) -{ - con_main.numlines = 0; - con_chat.numlines = 0; - memset (con_main.text, ' ', CON_TEXTSIZE); - memset (con_chat.text, ' ', CON_TEXTSIZE); - con_main.display = con_main.current; -} - -static void -MessageMode_f (void) -{ - if (con_data.force_commandline) - return; - chat_team = false; - Key_SetKeyDest (key_message); -} - -static void -MessageMode2_f (void) -{ - if (con_data.force_commandline) - return; - chat_team = true; - Key_SetKeyDest (key_message); -} - -static void -Resize (old_console_t *con) -{ - char tbuf[CON_TEXTSIZE]; - int width, oldwidth, oldtotallines, numlines, numchars, i, j; - - width = (r_data->vid->conwidth >> 3) - 2; - - if (width < 1) { // video hasn't been initialized yet - width = 38; - con_linewidth = width; - con_totallines = CON_TEXTSIZE / con_linewidth; - memset (con->text, ' ', CON_TEXTSIZE); - } else { - oldwidth = con_linewidth; - con_linewidth = width; - oldtotallines = con_totallines; - con_totallines = CON_TEXTSIZE / con_linewidth; - numlines = oldtotallines; - - if (con_totallines < numlines) - numlines = con_totallines; - - numchars = oldwidth; - - if (con_linewidth < numchars) - numchars = con_linewidth; - - memcpy (tbuf, con->text, CON_TEXTSIZE); - memset (con->text, ' ', CON_TEXTSIZE); - - for (i = 0; i < numlines; i++) { - for (j = 0; j < numchars; j++) { - con->text[(con_totallines - 1 - i) * con_linewidth + j] = - tbuf[((con->current - i + oldtotallines) % - oldtotallines) * oldwidth + j]; - } - } - - ClearNotify (); - } - say_team_line->width = con_linewidth - 9; - say_line->width = con_linewidth - 4; - input_line->width = con_linewidth; - - con->current = con_totallines - 1; - con->display = con->current; -} - -/* - C_CheckResize - - If the line width has changed, reformat the buffer. -*/ -static void -C_CheckResize (void) -{ - Resize (&con_main); - Resize (&con_chat); - - view_resize (con_data.view, r_data->vid->conwidth, r_data->vid->conheight); -} - -static void -Condump_f (void) -{ - int line = con->current - con->numlines; - const char *start, *end; - QFile *file; - char *name; - - if (Cmd_Argc () != 2) { - Sys_Printf ("usage: condump \n"); - return; - } - - if (strchr (Cmd_Argv (1), '/') || strchr (Cmd_Argv (1), '\\')) { - Sys_Printf ("invalid character in filename\n"); - return; - } - name = va ("%s/%s.txt", qfs_gamedir->dir.def, Cmd_Argv (1)); - - if (!(file = QFS_WOpen (name, 0))) { - Sys_Printf ("could not open %s for writing: %s\n", name, - strerror (errno)); - return; - } - - while (line < con->current) { - start = &con->text[(line % con_totallines) * con_linewidth]; - end = start + con_linewidth; - while (end > start && end[-1] != ' ') - end--; - Qprintf (file, "%.*s\n", (int)(end - start), start); - line++; - } - - Qclose (file); -} - static int cl_exec_line_command (void *data, const char *line) { @@ -299,58 +410,35 @@ cl_exec_line_rcon (void *data, const char *line) } static void -cl_conmode_f (cvar_t *var) +con_end_message (inputline_t *line) { - if (!strcmp (var->string, "command")) { - con_data.exec_line = cl_exec_line_command; - } else if (!strcmp (var->string, "chat")) { - con_data.exec_line = cl_exec_line_chat; - } else if (!strcmp (var->string, "rcon")) { - con_data.exec_line = cl_exec_line_rcon; - } else { - Sys_Printf ("mode must be one of \"command\", \"chat\" or \"rcon\"\n"); - Sys_Printf (" forcing \"command\"\n"); - Cvar_Set (var, "command"); - } + Con_ClearTyping (line, 1); + C_SetState (con_inactive); } static void C_Say (inputline_t *il) { const char *line = il->line; - if (!*line) - return; - - Cbuf_AddText (con_data.cbuf, "say \""); - Cbuf_AddText (con_data.cbuf, line); - Cbuf_AddText (con_data.cbuf, "\"\n"); - Key_SetKeyDest (key_game); + if (*line) { + Cbuf_AddText (con_data.cbuf, "say \""); + Cbuf_AddText (con_data.cbuf, line); + Cbuf_AddText (con_data.cbuf, "\"\n"); + } + con_end_message (il); } static void C_SayTeam (inputline_t *il) { const char *line = il->line; - if (!*line) - return; - Cbuf_AddText (con_data.cbuf, "say_team \""); - Cbuf_AddText (con_data.cbuf, line); - Cbuf_AddText (con_data.cbuf, "\"\n"); - Key_SetKeyDest (key_game); -} - -static void -Linefeed (void) -{ - con->x = 0; - if (con->display == con->current) - con->display++; - con->current++; - if (con->numlines < con_totallines) - con->numlines++; - memset (&con->text[(con->current % con_totallines) * con_linewidth], - ' ', con_linewidth); + if (*line) { + Cbuf_AddText (con_data.cbuf, "say_team \""); + Cbuf_AddText (con_data.cbuf, line); + Cbuf_AddText (con_data.cbuf, "\"\n"); + } + con_end_message (il); } /* @@ -360,194 +448,77 @@ Linefeed (void) All console printing must go through this in order to be logged to disk If no console is visible, the notify window will pop up. */ -static void +static __attribute__((format(PRINTF, 1, 0))) void C_Print (const char *fmt, va_list args) { char *s; - static dstring_t *buffer; - int mask, c, l, y; - static int cr; + int mask, c; - if (!buffer) - buffer = dstring_new (); + if (!c_print_buffer) + c_print_buffer = dstring_new (); - dvsprintf (buffer, fmt, args); + dvsprintf (c_print_buffer, fmt, args); // log all messages to file if (con_debuglog) - Sys_DebugLog (va ("%s/%s/qconsole.log", qfs_userpath, - qfs_gamedir->dir.def), "%s", buffer->str); + Sys_DebugLog (va (0, "%s/%s/qconsole.log", qfs_userpath,//FIXME + qfs_gamedir->dir.def), "%s", c_print_buffer->str); if (!con_initialized) return; - s = buffer->str; + s = c_print_buffer->str; + mask = 0; if (s[0] == 1 || s[0] == 2) { mask = 128; // go to colored text s++; - } else - mask = 0; - - while ((c = (byte)*s)) { - // count word length - for (l = 0; l < con_linewidth; l++) - if (s[l] <= ' ') - break; - - // word wrap - if (l != con_linewidth && (con->x + l > con_linewidth)) - con->x = 0; - - *s++ = sys_char_map[c]; - - if (cr) { - con->current--; - cr = false; + } + if (mask || con_data.ormask) { + for (char *m = s; *m; m++) { + if (*m >= ' ') { + *m |= mask | con_data.ormask; + } } + } - if (!con->x) { - Linefeed (); - // mark time for transparent overlay - if (con->current >= 0 && con_data.realtime) - con_times[con->current % NUM_CON_TIMES] = *con_data.realtime; - } - - switch (c) { - case '\n': - con->x = 0; - break; - - case '\r': - con->x = 0; - cr = 1; - break; - - default: // display character and advance - y = con->current % con_totallines; - con->text[y * con_linewidth + con->x] = c | mask | con_data.ormask; - con->x++; - if (con->x >= con_linewidth) - con->x = 0; - break; + if (con_data.realtime) { + c = Draw_PrintBuffer (notify_buffer, s); + while (c-- > 0) { + notify_times[notify_head++] = *con_data.realtime; + if (notify_head >= NOTIFY_LINES + 1) { + notify_head -= NOTIFY_LINES + 1; + } + if (notify_head == notify_tail) { + notify_tail++; + if (notify_tail >= NOTIFY_LINES + 1) { + notify_tail -= NOTIFY_LINES + 1; + } + } } + } + Con_BufferAddText (con_main, s); + if (!view_offset) { + Draw_PrintBuffer (console_buffer, s); + } + while (*s) { + *s = sys_char_map[(byte) *s]; + s++; } // echo to debugging console - if ((byte)buffer->str[0] > 2) - fputs (buffer->str, stdout); - else if ((byte)buffer->str[0]) - fputs (buffer->str + 1, stdout); -} - -static void -C_KeyEvent (knum_t key, short unicode, qboolean down) -{ - inputline_t *il; - - if (!down) - return; - - if (con_keydest == key_menu) { - if (Menu_KeyEvent (key, unicode, down)) - return; - } - - if (down) { - if (key == key_togglemenu) { - switch (con_keydest) { - case key_menu: - Menu_Leave (); - return; - case key_message: - if (chat_team) { - Con_ClearTyping (say_team_line, 1); - } else { - Con_ClearTyping (say_line, 1); - } - Key_SetKeyDest (key_game); - return; - case key_console: - if (!con_data.force_commandline) { - Cbuf_AddText (con_data.cbuf, "toggleconsole\n"); - return; - } - case key_game: - case key_demo: - Menu_Enter (); - return; - case key_unfocused: - return; - case key_last: - break; // should not happen, so hit error - } - Sys_Error ("Bad con_keydest"); - } else if (key == key_toggleconsole) { - ToggleConsole_f (); - return; - } - } - - if (con_keydest == key_menu) { - return; - } else if (con_keydest == key_message) { - if (chat_team) { - il = say_team_line; - } else { - il = say_line; - } - } else { - switch (key) { - case QFK_PAGEUP: - if (keydown[QFK_RCTRL] || keydown[QFK_LCTRL]) - con->display = 0; - else - con->display -= 10; - if (con->display < con->current - con->numlines) - con->display = con->current - con->numlines; - return; - case QFK_PAGEDOWN: - if (keydown[QFK_RCTRL] || keydown[QFK_LCTRL]) - con->display = con->current; - else - con->display += 10; - if (con->display > con->current) - con->display = con->current; - return; - case QFM_WHEEL_UP: - con->display -= 3; - if (con->display < con->current - con->numlines) - con->display = con->current - con->numlines; - return; - case QFM_WHEEL_DOWN: - con->display += 3; - if (con->display > con->current) - con->display = con->current; - return; - default: - break; - } - il = input_line; - } - //FIXME should this translation be here? - if ((unicode==0x0A) && (key==QFK_RETURN)) { - Con_ProcessInputLine (il, key); - } - if ((unicode==0x7F) && (key==QFK_BACKSPACE)) { - Con_ProcessInputLine (il, key); - } - if (unicode!=0) { - Con_ProcessInputLine (il, key >= 256 ? (int) key : unicode); - } else { - Con_ProcessInputLine (il, key); - } + // but don't print the highchars flag (leading \x01) + if ((byte)c_print_buffer->str[0] > 2) + fputs (c_print_buffer->str, stdout); + else if ((byte)c_print_buffer->str[0]) + fputs (c_print_buffer->str + 1, stdout); } /* DRAWING */ static void -DrawInputLine (int x, int y, int cursor, inputline_t *il) +DrawInputLine (int x, int y, inputline_t *il) { const char *s = il->lines[il->edit_line] + il->scroll; @@ -557,197 +528,237 @@ DrawInputLine (int x, int y, int cursor, inputline_t *il) } else { r_funcs->Draw_nString (x, y, s, il->width - 1); } - if (cursor && con_data.realtime) { - float t = *con_data.realtime * con_cursorspeed; - int ch = 10 + ((int) (t) & 1); - r_funcs->Draw_Character (x + ((il->linepos - il->scroll) << 3), y, ch); - } if (strlen (s) >= il->width) - r_funcs->Draw_Character (x + ((il->width - 1) << 3), y, '>' | 0x80); + r_funcs->Draw_Character (x + ((il->width - 1) * 8), y, '>' | 0x80); } void C_DrawInputLine (inputline_t *il) { - DrawInputLine (il->x, il->y, il->cursor, il); + DrawInputLine (il->x, il->y, il); } static void -draw_input (view_t *view) +draw_input_line (inputline_t *il, draw_charbuffer_t *buffer) { - if (con_keydest != key_console)// && !con_data.force_commandline) - return; // don't draw anything (always draw if not active) + char *dst = buffer->chars + buffer->cursx; + char *src = il->lines[il->edit_line] + il->scroll + 1; + size_t i; - DrawInputLine (view->xabs + 8, view->yabs, 1, input_line); + *dst++ = il->scroll ? '<' | 0x80 : il->lines[il->edit_line][0]; + for (i = 0; i < il->width - 2 && *src; i++) { + *dst++ = *src++; + } + while (i++ < il->width - 2) { + *dst++ = ' '; + } + *dst++ = *src ? '>' | 0x80 : ' '; } static void -draw_download (view_t *view) +input_line_draw (inputline_t *il) { - char dlbar[1024]; - const char *text; - size_t i, j, x, y, n; + __auto_type inp = (con_input_t *) il->user_data; + draw_input_line (il, inp->buffer); +} - if (!con_data.dl_name || !*con_data.dl_name->str) +static void +resize_input (view_t view, view_pos_t len) +{ + if (!con_hasinput (view)) { return; + } + __auto_type inp = con_getinput (view); + + if (inp->buffer) { + Draw_DestroyBuffer (inp->buffer); + } + inp->buffer = Draw_CreateBuffer (len.x / 8, 1); + Draw_ClearBuffer (inp->buffer); + Draw_PrintBuffer (inp->buffer, inp->prompt); + if (inp->input_line) { + inp->input_line->width = inp->buffer->width - inp->buffer->cursx; + inp->buffer->chars[inp->buffer->cursx] = inp->input_line->prompt_char; + } +} + +static void +update_download (void) +{ + const char *text; + + if (!dlbar) { + dlbar = dstring_new (); + } text = QFS_SkipPath(con_data.dl_name->str); - x = con_linewidth - ((con_linewidth * 7) / 40); - y = x - strlen (text) - 8; - i = con_linewidth / 3; - if (strlen (text) > i) { - y = x - i - 11; - strncpy (dlbar, text, i); - dlbar[i] = 0; - strncat (dlbar, "...", sizeof (dlbar) - strlen (dlbar)); - } else - strncpy (dlbar, text, sizeof (dlbar)); - strncat (dlbar, ": ", sizeof (dlbar) - strlen (dlbar)); - i = strlen (dlbar); - dlbar[i++] = '\x80'; + int line_size = con_linewidth - ((con_linewidth * 7) / 40); + int dot_space = line_size - strlen (text) - 8; + const char *ellipsis = ""; + int txt_size = con_linewidth / 3; + if (strlen (text) > (size_t) txt_size) { + ellipsis = "..."; + dot_space = line_size - txt_size - 11; + } else { + txt_size = strlen (text); + } // where's the dot go? - if (con_data.dl_percent == 0) - n = 0; - else - n = y * *con_data.dl_percent / 100; - for (j = 0; j < y; j++) - if (j == n) - dlbar[i++] = '\x83'; - else - dlbar[i++] = '\x81'; - dlbar[i++] = '\x82'; - dlbar[i] = 0; - - snprintf (dlbar + strlen (dlbar), sizeof (dlbar) - strlen (dlbar), - " %02d%%", *con_data.dl_percent); + int n = 0; + if (con_data.dl_percent) { + n = dot_space * *con_data.dl_percent / 100; + } + char *dots = alloca (dot_space + 1); + dots[dot_space] = 0; + for (int j = 0; j < dot_space; j++) { + if (j == n) { + dots[j++] = '\x83'; + } else { + dots[j++] = '\x81'; + } + } + dsprintf (dlbar, "%.*s%s: \x80%s\x82 %02d%%", + txt_size, text, ellipsis, dots, *con_data.dl_percent); // draw it - r_funcs->Draw_String (view->xabs, view->yabs, dlbar); + int i; + for (i = 0; i < download_buffer->width && dlbar->str[i]; i++) { + download_buffer->chars[i] = dlbar->str[i]; + } + for (; i < download_buffer->width; i++) { + download_buffer->chars[i] = ' '; + } } static void -draw_console_text (view_t *view) +clear_console_text (void) { - char *text; - int row, rows, i, x, y; + Draw_ClearBuffer (console_buffer); + console_buffer->cursy = console_buffer->height - 1; +} - rows = view->ylen >> 3; // rows of text to draw +static void +resize_console_text (view_t view, view_pos_t len) +{ + int width = len.x / 8; + int height = len.y / 8; - x = view->xabs + 8; - y = view->yabs + view->ylen - 8; + if (console_buffer->width != width || console_buffer->height != height) { + con_linewidth = width; + Draw_DestroyBuffer (console_buffer); + console_buffer = Draw_CreateBuffer (width, height); + con_setcharbuf (buffer_view, console_buffer); + clear_console_text (); + } +} - // draw from the bottom up - if (con->display != con->current) { +static void +draw_con_scrollback (void) +{ + __auto_type cb = console_buffer; + char *dst = cb->chars + (cb->height - 1) * cb->width; + int rows = cb->height; + int cur_line = con_main->line_head - 1 + view_offset; + + if (view_offset) { // draw arrows to show the buffer is backscrolled - for (i = 0; i < con_linewidth; i += 4) - r_funcs->Draw_Character (x + (i << 3), y, '^'); - - y -= 8; + memset (dst, '^' | 0x80, cb->width); + dst -= cb->width; rows--; } - - row = con->display; - for (i = 0; i < rows; i++, y -= 8, row--) { - if (row < 0) - break; - if (con->current - row >= con_totallines) - break; // past scrollback wrap point - - text = con->text + (row % con_totallines) * con_linewidth; - - r_funcs->Draw_nString(x, y, text, con_linewidth); + for (int i = 0; i < rows; i++) { + con_line_t *l = Con_BufferLine (con_main, cur_line - i); + int len = max (min ((int) l->len, cb->width + 1) - 1, 0); + memcpy (dst, con_main->buffer + l->text, len); + memset (dst + len, ' ', cb->width - len); + dst -= cb->width; } } static void -draw_console (view_t *view) +draw_cursor (view_t view) { - byte alpha; + float t = *con_data.realtime * con_cursorspeed; + int ch = 10 + ((int) (t) & 1); - if (con_data.force_commandline) { - alpha = 255; - } else { - float y = r_data->vid->conheight * con_size->value; - alpha = 255 * con_alpha->value * view->ylen / y; - alpha = min (alpha, 255); - } - // draw the background - r_funcs->Draw_ConsoleBackground (view->ylen, alpha); - - // draw everything else - view_draw (view); -} - -static void -draw_say (view_t *view) -{ - r_data->scr_copytop = 1; - - if (chat_team) { - r_funcs->Draw_String (view->xabs + 8, view->yabs, "say_team:"); - DrawInputLine (view->xabs + 80, view->yabs, 1, say_team_line); - } else { - r_funcs->Draw_String (view->xabs + 8, view->yabs, "say:"); - DrawInputLine (view->xabs + 40, view->yabs, 1, say_line); - } -} - -static void -draw_notify (view_t *view) -{ - int i, x, y; - char *text; - float time; - - if (!con_data.realtime) + __auto_type inp = con_getinput (view); + __auto_type buff = inp->buffer; + __auto_type il = inp->input_line; + if (!il->cursor) { return; + } + view_pos_t pos = View_GetAbs (view); + int x = (buff->cursx + il->linepos - il->scroll) * 8; + r_funcs->Draw_Character (pos.x + x, pos.y, ch); +} - x = view->xabs + 8; - y = view->yabs; - for (i = con->current - NUM_CON_TIMES + 1; i <= con->current; i++) { - if (i < 0) - continue; - time = con_times[i % NUM_CON_TIMES]; - if (time == 0) - continue; - time = *con_data.realtime - time; - if (time > con_notifytime->value) - continue; - text = con->text + (i % con_totallines) * con_linewidth; +static void +update_notify (void) +{ + if (con_data.realtime && notify_tail != notify_head + && *con_data.realtime - notify_times[notify_tail] > con_notifytime) { + Draw_ScrollBuffer (notify_buffer, 1); + notify_buffer->cursy--; + notify_tail++; + if (notify_tail >= (NOTIFY_LINES + 1)) { + notify_tail -= NOTIFY_LINES + 1; + } + } +} - r_data->scr_copytop = 1; +static void +resize_notify (view_t view, view_pos_t len) +{ + Draw_DestroyBuffer (notify_buffer); + notify_buffer = Draw_CreateBuffer (len.x / 8, NOTIFY_LINES + 1); + con_setcharbuf (notify_view, notify_buffer); + ClearNotify (); +} - r_funcs->Draw_nString (x, y, text, con_linewidth); - y += 8; +static void +resize_download (view_t view, view_pos_t len) +{ + if (download_buffer) { + Draw_DestroyBuffer (download_buffer); + download_buffer = Draw_CreateBuffer (len.x / 8, 1); } } static void setup_console (void) { - float lines; + float lines = 0; + view_pos_t screen_len = View_GetLen (screen_view); - if (con_data.force_commandline) { - lines = con_data.lines = r_data->vid->conheight; - } else if (con_keydest == key_console) { - lines = r_data->vid->conheight * bound (0.2, con_size->value, 1); - } else { - lines = 0; + switch (con_state) { + case con_message: + case con_menu: + case con_inactive: + lines = 0; + break; + case con_active: + lines = screen_len.y * bound (0.2, con_size, 1); + break; + case con_fullscreen: + lines = con_data.lines = screen_len.y; + break; } - if (con_speed->value) { + if (con_speed) { if (lines < con_data.lines) { - con_data.lines -= max (0.2, con_speed->value) * *con_data.frametime; + con_data.lines -= max (0.2, con_speed) * *con_data.frametime; con_data.lines = max (con_data.lines, lines); } else if (lines > con_data.lines) { - con_data.lines += max (0.2, con_speed->value) * *con_data.frametime; + con_data.lines += max (0.2, con_speed) * *con_data.frametime; con_data.lines = min (con_data.lines, lines); } } else { con_data.lines = lines; } - if (con_data.lines >= r_data->vid->conheight - r_data->lineadj) + if (con_data.lines > screen_len.y) { + con_data.lines = screen_len.y; + } + if (con_data.lines >= screen_len.y - r_data->lineadj) r_data->scr_copyeverything = 1; } @@ -755,27 +766,57 @@ static void C_DrawConsole (void) { setup_console (); + view_pos_t screen_len = View_GetLen (screen_view); - if (console_view->ylen != con_data.lines) - view_resize (console_view, console_view->xlen, con_data.lines); + view_pos_t pos = View_GetPos (console_view); + int ypos = con_data.lines - screen_len.y; + if (pos.y != ypos) { + View_SetPos (console_view, pos.x, ypos); + View_UpdateHierarchy (console_view); + } + if (con_state == con_menu) { + Menu_Draw (menu_view); + return; + } - say_view->visible = con_keydest == key_message; - console_view->visible = con_data.lines != 0; - menu_view->visible = con_keydest == key_menu; + if (!con_data.lines && con_state == con_message) { + con_setcharbuf (say_view, say_line.buffer); + con_setcursor (say_view, &say_line); + con_setfunc (say_view, canvas_lateupdate, draw_cursor); + } else { + con_remcharbuf (say_view); + con_remcursor (say_view); + con_remfunc (say_view, canvas_lateupdate); + } + if (con_data.lines) { + con_remcharbuf (notify_view); + con_setcharbuf (command_view, cmd_line.buffer); + con_setcursor (command_view, &cmd_line); + con_setfunc (command_view, canvas_lateupdate, draw_cursor); + } else { + con_setcharbuf (notify_view, notify_buffer); + con_remcharbuf (command_view); + con_remcursor (command_view); + con_remfunc (command_view, canvas_lateupdate); + } + if (con_data.dl_name && *con_data.dl_name->str) { + if (!download_buffer) { + view_pos_t len = View_GetLen (download_view); + download_buffer = Draw_CreateBuffer (len.x / 8, 1); + con_setcharbuf (download_view, download_buffer); + } + update_download (); + } else if (download_buffer) { + Draw_DestroyBuffer (download_buffer); + con_remcharbuf (download_view); + } - con_data.view->draw (con_data.view); -} - -static void -C_ProcessInput (void) -{ + update_notify (); } static void C_NewMap (void) { - static dstring_t *old_gamedir = 0; - if (!old_gamedir || !strequal (old_gamedir->str, qfs_gamedir->gamedir)) Menu_Load (); if (!old_gamedir) @@ -783,137 +824,360 @@ C_NewMap (void) dstring_copystr (old_gamedir, qfs_gamedir->gamedir); } -static void -C_GIB_HUD_Enable_f (void) -{ - hud_view->visible = 1; -} - -static void -C_GIB_HUD_Disable_f (void) -{ - hud_view->visible = 0; -} - static void exec_line (inputline_t *il) { Con_ExecLine (il->line); } +static int win_xlen = -1; +static int win_ylen = -1; + static void -con_keydest_callback (keydest_t kd) +con_set_size (void) { - // simply cache the value - con_keydest = kd; + int xlen = win_xlen / con_scale; + int ylen = win_ylen / con_scale; + if (xlen > 0 && ylen > 0) { + View_SetLen (screen_view, xlen, ylen); + View_UpdateHierarchy (screen_view); + } +} + +static void +con_app_window (const IE_event_t *event) +{ + if (win_xlen != event->app_window.xlen + || win_ylen != event->app_window.ylen) { + win_xlen = event->app_window.xlen; + win_ylen = event->app_window.ylen; + con_set_size (); + } +} + +static void +con_scale_f (void *data, const cvar_t *cvar) +{ + con_scale = bound (1, con_scale, Draw_MaxScale ()); + Draw_SetScale (con_scale); + if (con_initialized) { + con_set_size (); + } +} + +static void +con_key_event (const IE_event_t *event) +{ + inputline_t *il; + __auto_type key = &event->key; + int old_view_offset = view_offset; + +#if 0 + if (con_curr_keydest == key_menu) { + Menu_KeyEvent (key, unicode, down); + return; + } + + if (down) { + if (key == key_toggleconsole) { + ToggleConsole_f (); + return; + } + } +#endif + if (con_state == con_message) { + if (chat_team) { + il = team_input; + } else { + il = chat_input; + } + if (key->code == QFK_ESCAPE) { + con_end_message (il); + return; + } + } else { + con_buffer_t *cb = con_main; + int num_lines = (cb->line_head - cb->line_tail + + cb->max_lines) % cb->max_lines; + int con_lines = con_data.lines / 8; + switch (key->code) { + case QFK_ESCAPE: + ToggleConsole_f (); + break; + case QFK_PAGEUP: + view_offset -= 10; + if (view_offset <= -(num_lines - con_lines)) { + view_offset = -(num_lines - con_lines) + 1; + } + break; + case QFK_PAGEDOWN: + view_offset += 10; + if (view_offset > 0) { + view_offset = 0; + } + break; +#if 0 + case QFM_WHEEL_UP: + break; + case QFM_WHEEL_DOWN: + break; +#endif + default: + break; + } + il = cmd_line.input_line; + } + if (old_view_offset != view_offset) { + draw_con_scrollback (); + } + + if (key->unicode) { + Con_ProcessInputLine (il, key->code >= 256 ? (int) key->code + : key->unicode); + } else { + Con_ProcessInputLine (il, key->code); + } +} + +static void +con_mouse_event (const IE_event_t *event) +{ +} + +static int +con_event_handler (const IE_event_t *ie_event, void *data) +{ + if (con_state == con_menu) { + if (ie_event->type == ie_app_window) { + con_app_window (ie_event); + } + return Menu_EventHandler (ie_event); + } + static void (*handlers[ie_event_count]) (const IE_event_t *ie_event) = { + [ie_app_window] = con_app_window, + [ie_key] = con_key_event, + [ie_mouse] = con_mouse_event, + }; + if ((unsigned) ie_event->type >= ie_event_count + || !handlers[ie_event->type]) { + return 0; + } + handlers[ie_event->type] (ie_event); + return 1; +} + +static void +Clear_f (void) +{ + Con_ClearBuffer (con_main); + clear_console_text (); +} + +static void +MessageMode_f (void) +{ + if (con_state != con_inactive) + return; + chat_team = false; + C_SetState (con_message); +} + +static void +MessageMode2_f (void) +{ + if (con_state != con_inactive) + return; + chat_team = true; + C_SetState (con_message); +} + +static void +Condump_f (void) +{ + QFile *file; + const char *name; + + if (Cmd_Argc () != 2) { + Sys_Printf ("usage: condump \n"); + return; + } + + if (strchr (Cmd_Argv (1), '/') || strchr (Cmd_Argv (1), '\\')) { + Sys_Printf ("invalid character in filename\n"); + return; + } + name = va (0, "%s/%s.txt", qfs_gamedir->dir.def, Cmd_Argv (1));//FIXME + + if (!(file = QFS_WOpen (name, 0))) { + Sys_Printf ("could not open %s for writing: %s\n", name, + strerror (errno)); + return; + } + + for (uint32_t line_ind = con->line_tail; line_ind != con->line_head; + line_ind = (line_ind + 1) % con->max_lines) { + con_line_t *line = &con->lines[line_ind]; + Qwrite (file, con->buffer + line->text, line->len); + } + + Qclose (file); +} + +static void +C_InitCvars (void) +{ + Cvar_Register (&con_notifytime_cvar, 0, 0); + Cvar_Register (&con_scale_cvar, con_scale_f, 0); + Cvar_Register (&con_alpha_cvar, 0, 0); + Cvar_Register (&con_size_cvar, 0, 0); + Cvar_Register (&con_speed_cvar, 0, 0); + Cvar_Register (&cl_conmode_cvar, 0, 0); } static void C_Init (void) { - view_t *view; - + client_base = con_data.component_base; + canvas_base = con_data.canvas_sys->base; + view_base = con_data.canvas_sys->view_base; #ifdef __QNXNTO__ setlocale (LC_ALL, "C-TRADITIONAL"); #endif - Key_KeydestCallback (con_keydest_callback); + con_event_id = IE_Add_Handler (con_event_handler, 0); + Menu_Init (); - con_notifytime = Cvar_Get ("con_notifytime", "3", CVAR_NONE, NULL, - "How long in seconds messages are displayed " - "on screen"); - - con_alpha = Cvar_Get ("con_alpha", "0.6", CVAR_ARCHIVE, NULL, - "alpha value for the console background"); - con_size = Cvar_Get ("con_size", "0.5", CVAR_ARCHIVE, NULL, - "Fraction of the screen the console covers when " - "down"); - con_speed = Cvar_Get ("con_speed", "300", CVAR_ARCHIVE, NULL, - "How quickly the console scrolls up or down"); - cl_conmode = Cvar_Get ("cl_conmode", "command", CVAR_ARCHIVE, cl_conmode_f, - "Set the console input mode (command, chat, rcon)"); - con_debuglog = COM_CheckParm ("-condebug"); // The console will get resized, so assume initial size is 320x200 - con_data.view = view_new (0, 0, 320, 200, grav_northeast); - console_view = view_new (0, 0, 320, 200, grav_northwest); - say_view = view_new (0, 0, 320, 8, grav_northwest); - notify_view = view_new (0, 8, 320, 32, grav_northwest); - menu_view = view_new (0, 0, 320, 200, grav_center); - hud_view = view_new (0, 0, 320, 200, grav_northeast); + ecs_system_t sys = { con_data.canvas_sys->reg, view_base }; + screen_canvas = Canvas_New (*con_data.canvas_sys); + screen_view = Canvas_GetRootView (*con_data.canvas_sys, screen_canvas); + console_view = View_New (sys, screen_view); + buffer_view = View_New (sys, console_view); + command_view = View_New (sys, console_view); + download_view = View_New (sys, console_view); + notify_view = View_New (sys, screen_view); + say_view = View_New (sys, screen_view); + menu_view = View_New (sys, screen_view); - view_add (con_data.view, say_view); - view_add (con_data.view, notify_view); - view_add (con_data.view, hud_view); - view_add (con_data.view, console_view); - view_add (con_data.view, menu_view); + View_SetVisible (screen_view, 1); + View_SetVisible (console_view, 1); + View_SetVisible (buffer_view, 1); + View_SetVisible (command_view, 1); + View_SetVisible (download_view, 1); + View_SetVisible (notify_view, 1); + View_SetVisible (say_view, 1); + View_SetVisible (menu_view, 1); - console_view->draw = draw_console; - console_view->visible = 0; - console_view->resize_x = console_view->resize_y = 1; + View_SetGravity (screen_view, grav_northwest); + View_SetGravity (console_view, grav_northwest); + View_SetGravity (buffer_view, grav_southwest); + View_SetGravity (command_view, grav_southwest); + View_SetGravity (download_view, grav_southwest); + View_SetGravity (notify_view, grav_northwest); + View_SetGravity (say_view, grav_northwest); + View_SetGravity (menu_view, grav_center); - say_view->draw = draw_say; - say_view->visible = 0; - say_view->resize_x = 1; + View_SetResize (screen_view, 1, 1); + View_SetResize (console_view, 1, 1); + View_SetResize (buffer_view, 1, 1); + View_SetResize (command_view, 1, 0); + View_SetResize (download_view, 1, 0); + View_SetResize (notify_view, 1, 0); + View_SetResize (say_view, 1, 0); + View_SetResize (menu_view, 0, 0); - notify_view->draw = draw_notify; - notify_view->resize_x = 1; + View_SetPos (screen_view, 0, 0); + View_SetPos (console_view, 0, 0); + View_SetPos (buffer_view, 0, 24); + View_SetPos (command_view, 0, 0); + View_SetPos (download_view, 0, 2); + View_SetPos (notify_view, 8, 8); + View_SetPos (say_view, 0, 0); - menu_view->draw = Menu_Draw; - menu_view->visible = 0; + View_SetLen (screen_view, 320, 200); + View_SetLen (console_view, 320, 200); + View_SetLen (buffer_view, 320, 176); + View_SetLen (command_view, 320, 12); + View_SetLen (download_view, 320, 8); + View_SetLen (notify_view, 312, NOTIFY_LINES * 8); + View_SetLen (say_view, 320, 8); + View_SetLen (menu_view, 320, 200); - hud_view->draw = Menu_Draw_Hud; - hud_view->visible = 0; + load_conback ("gfx/conback.lmp"); + if (conback) { + con_setfitpic (console_view, conback); + } - view = view_new (0, 0, 320, 170, grav_northwest); - view->draw = draw_console_text; - view->resize_x = view->resize_y = 1; - view_add (console_view, view); + cmd_line.prompt = ""; + cmd_line.input_line = Con_CreateInputLine (32, MAXCMDLINE, ']'); + cmd_line.input_line->complete = Con_BasicCompleteCommandLine; + cmd_line.input_line->enter = exec_line; + cmd_line.input_line->width = con_linewidth; + cmd_line.input_line->user_data = &cmd_line; + cmd_line.input_line->draw = input_line_draw; - view = view_new (0, 12, 320, 10, grav_southwest); - view->draw = draw_input; - view->resize_x = 1; - view_add (console_view, view); + say_line.prompt = "say:"; + chat_input = Con_CreateInputLine (32, MAXCMDLINE, ' '); + chat_input->complete = 0; + chat_input->enter = C_Say; + chat_input->width = con_linewidth - 5; + chat_input->user_data = &say_line; + chat_input->draw = input_line_draw; - view = view_new (0, 2, 320, 11, grav_southwest); - view->draw = draw_download; - view->resize_x = 1; - view_add (console_view, view); + team_input = Con_CreateInputLine (32, MAXCMDLINE, ' '); + team_input->complete = 0; + team_input->enter = C_SayTeam; + team_input->width = con_linewidth - 10; + team_input->user_data = &say_line; + team_input->draw = input_line_draw; - con = &con_main; + con_setinput (say_view, &say_line); + con_setinput (command_view, &cmd_line); + + + view_pos_t len; + + len = View_GetLen (buffer_view); + console_buffer = Draw_CreateBuffer (len.x / 8, len.y / 8); + Draw_ClearBuffer (console_buffer); + con_setcharbuf (buffer_view, console_buffer); + con_main = Con_CreateBuffer (CON_BUFFER_SIZE, CON_LINES); + + len = View_GetLen (command_view); + cmd_line.buffer = Draw_CreateBuffer (len.x / 8, len.y / 8); + Draw_ClearBuffer (cmd_line.buffer); + con_setcharbuf (command_view, cmd_line.buffer); + + len = View_GetLen (notify_view); + notify_buffer = Draw_CreateBuffer (len.x / 8, NOTIFY_LINES + 1); + Draw_ClearBuffer (notify_buffer); + + len = View_GetLen (say_view); + say_line.buffer = Draw_CreateBuffer (len.x / 8, len.y / 8); + Draw_ClearBuffer (say_line.buffer); + + View_UpdateHierarchy (screen_view); + + // set onresize after View_UpdateHierarchy so the callbacks are NOT called + // during init + View_SetOnResize (buffer_view, resize_console_text); + View_SetOnResize (command_view, resize_input); + View_SetOnResize (download_view, resize_download); + View_SetOnResize (notify_view, resize_notify); + View_SetOnResize (say_view, resize_input); + + con = con_main; con_linewidth = -1; - input_line = Con_CreateInputLine (32, MAXCMDLINE, ']'); - input_line->complete = Con_BasicCompleteCommandLine; - input_line->enter = exec_line; - input_line->width = con_linewidth; - input_line->user_data = 0; - input_line->draw = 0; - - say_line = Con_CreateInputLine (32, MAXCMDLINE, ' '); - say_line->complete = 0; - say_line->enter = C_Say; - say_line->width = con_linewidth - 5; - say_line->user_data = 0; - say_line->draw = 0; - - say_team_line = Con_CreateInputLine (32, MAXCMDLINE, ' '); - say_team_line->complete = 0; - say_team_line->enter = C_SayTeam; - say_team_line->width = con_linewidth - 10; - say_team_line->user_data = 0; - say_team_line->draw = 0; - - C_CheckResize (); - Sys_Printf ("Console initialized.\n"); // register our commands Cmd_AddCommand ("toggleconsole", ToggleConsole_f, "Toggle the console up and down"); - Cmd_AddCommand ("togglechat", ToggleChat_f, + Cmd_AddCommand ("togglechat", ToggleConsole_f, "Toggle the console up and down"); Cmd_AddCommand ("messagemode", MessageMode_f, "Prompt to send a message to everyone"); @@ -923,44 +1187,63 @@ C_Init (void) Cmd_AddCommand ("condump", Condump_f, "dump the console text to a " "file"); - // register GIB builtins - GIB_Builtin_Add ("HUD::enable", C_GIB_HUD_Enable_f); - GIB_Builtin_Add ("HUD::disable", C_GIB_HUD_Disable_f); - con_initialized = true; } static void -C_Shutdown (void) +C_shutdown (void) { + r_funcs->Draw_DestroyPic (conback); + IE_Remove_Handler (con_event_id); + Menu_Shutdown (); + + Con_DestroyInputLine (cmd_line.input_line); + Con_DestroyInputLine (team_input); + Con_DestroyInputLine (chat_input); + Con_DestroyBuffer (con_main); + + if (download_buffer) { + Draw_DestroyBuffer (download_buffer); + } + Draw_DestroyBuffer (console_buffer); + Draw_DestroyBuffer (cmd_line.buffer); + Draw_DestroyBuffer (notify_buffer); + Draw_DestroyBuffer (say_line.buffer); + + if (c_print_buffer) { + dstring_delete (c_print_buffer); + } + if (dlbar) { + dstring_delete (dlbar); + } + if (old_gamedir) { + dstring_delete (old_gamedir); + } } +static general_data_t plugin_info_general_data; + static general_funcs_t plugin_info_general_funcs = { - C_Init, - C_Shutdown, + .init = C_InitCvars, + .shutdown = C_shutdown, }; static console_funcs_t plugin_info_console_funcs = { - C_Print, - C_ProcessInput, - C_KeyEvent, - C_DrawConsole, - C_CheckResize, - C_NewMap, + .init = C_Init, + .print = C_Print, + .draw_console = C_DrawConsole, + .new_map = C_NewMap, + .set_state = C_SetState, }; static plugin_funcs_t plugin_info_funcs = { - &plugin_info_general_funcs, - 0, - 0, - &plugin_info_console_funcs, + .general = &plugin_info_general_funcs, + .console = &plugin_info_console_funcs, }; static plugin_data_t plugin_info_data = { - &plugin_info_general_data, - 0, - 0, - &con_data, + .general = &plugin_info_general_data, + .console = &con_data, }; static plugin_t plugin_info = { diff --git a/libs/console/complete.c b/libs/console/complete.c index 83dedc7a1..6e5499dd9 100644 --- a/libs/console/complete.c +++ b/libs/console/complete.c @@ -47,6 +47,8 @@ #include "QF/plugin/console.h" +#include "QF/ui/inputline.h" + #include "compat.h" /* @@ -157,7 +159,7 @@ Con_BasicCompleteCommandLine (inputline_t *il) && strncmp (s + bound, cmd, strlen (s + bound))) bound++; - overwrite = va("%.*s%.*s", bound, s, cmd_len, cmd); + overwrite = va (0, "%.*s%.*s", bound, s, cmd_len, cmd); il->lines[il->edit_line][1] = '/'; strncpy (il->lines[il->edit_line] + 2, overwrite, il->line_size - 3); diff --git a/libs/console/console.c b/libs/console/console.c index fbfdbd608..9c3fc8140 100644 --- a/libs/console/console.c +++ b/libs/console/console.c @@ -42,6 +42,8 @@ #include "QF/plugin/general.h" #include "QF/plugin/console.h" +#include "QF/ui/inputline.h" + //FIXME probably shouldn't be visible VISIBLE int con_linewidth; // characters across screen @@ -54,22 +56,31 @@ static U inputline_t *(*const create)(int, int, char) = Con_CreateInputLine; static U void (*const display)(const char **, int) = Con_DisplayList; #undef U -static cvar_t *con_interpreter; +static char *con_interpreter; +static cvar_t con_interpreter_cvar = { + .name = "con_interpreter", + .description = + "Interpreter for the interactive console", + .default_value = "id", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &con_interpreter }, +}; +static sys_printf_t saved_sys_printf; static void -Con_Interp_f (cvar_t *var) +Con_Interp_f (void *data, const cvar_t *cvar) { cbuf_interpreter_t *interp; if (!con_module) return; - interp = Cmd_GetProvider(var->string); + interp = Cmd_GetProvider(con_interpreter); if (interp) { cbuf_t *new; - Sys_Printf ("Switching to interpreter '%s'\n", var->string); + Sys_Printf ("Switching to interpreter '%s'\n", con_interpreter); new = Cbuf_New (interp); @@ -80,23 +91,41 @@ Con_Interp_f (cvar_t *var) } con_module->data->console->cbuf = new; } else { - Sys_Printf ("Unknown interpreter '%s'\n", var->string); + Sys_Printf ("Unknown interpreter '%s'\n", con_interpreter); + } +} + +static void +Con_shutdown (void *data) +{ + if (saved_sys_printf) { + Sys_SetStdPrintf (saved_sys_printf); + } + if (con_module) { + PI_UnloadPlugin (con_module); } } VISIBLE void -Con_Init (const char *plugin_name) +Con_Load (const char *plugin_name) { + Sys_RegisterShutdown (Con_shutdown, 0); + con_module = PI_LoadPlugin ("console", plugin_name); - if (con_module) { - con_module->functions->general->p_Init (); - Sys_SetStdPrintf (con_module->functions->console->pC_Print); - } else { + if (!con_module) { setvbuf (stdout, 0, _IOLBF, BUFSIZ); } - con_interpreter = - Cvar_Get("con_interpreter", "id", CVAR_NONE, Con_Interp_f, - "Interpreter for the interactive console"); +} + +VISIBLE void +Con_Init (void) +{ + if (con_module) { + __auto_type funcs = con_module->functions->console; + funcs->init (); + saved_sys_printf = Sys_SetStdPrintf (funcs->print); + } + Cvar_Register (&con_interpreter_cvar, Con_Interp_f, 0); } VISIBLE void @@ -123,15 +152,6 @@ Con_ExecLine (const char *line) Sys_Printf ("%s\n", line); } -VISIBLE void -Con_Shutdown (void) -{ - if (con_module) { - con_module->functions->general->p_Shutdown (); - PI_UnloadPlugin (con_module); - } -} - VISIBLE void Con_Printf (const char *fmt, ...) { @@ -139,7 +159,7 @@ Con_Printf (const char *fmt, ...) va_start (args, fmt); if (con_module) - con_module->functions->console->pC_Print (fmt, args); + con_module->functions->console->print (fmt, args); else vfprintf (stdout, fmt, args); va_end (args); @@ -149,16 +169,26 @@ VISIBLE void Con_Print (const char *fmt, va_list args) { if (con_module) - con_module->functions->console->pC_Print (fmt, args); + con_module->functions->console->print (fmt, args); else vfprintf (stdout, fmt, args); } +VISIBLE void +Con_SetState (con_state_t state) +{ + if (con_module) { + con_module->functions->console->set_state (state); + } +} + VISIBLE void Con_ProcessInput (void) { if (con_module) { - con_module->functions->console->pC_ProcessInput (); + if (con_module->functions->console->process_input) { + con_module->functions->console->process_input (); + } } else { static int been_there_done_that = 0; @@ -169,13 +199,6 @@ Con_ProcessInput (void) } } -VISIBLE void -Con_KeyEvent (knum_t key, short unicode, qboolean down) -{ - if (con_module) - con_module->functions->console->pC_KeyEvent (key, unicode, down); -} - VISIBLE void Con_SetOrMask (int mask) { @@ -187,19 +210,12 @@ VISIBLE void Con_DrawConsole (void) { if (con_module) - con_module->functions->console->pC_DrawConsole (); -} - -VISIBLE void -Con_CheckResize (void) -{ - if (con_module) - con_module->functions->console->pC_CheckResize (); + con_module->functions->console->draw_console (); } VISIBLE void Con_NewMap (void) { if (con_module) - con_module->functions->console->pC_NewMap (); + con_module->functions->console->new_map (); } diff --git a/libs/console/filelist.c b/libs/console/filelist.c index 73595de82..b2942c593 100644 --- a/libs/console/filelist.c +++ b/libs/console/filelist.c @@ -102,7 +102,7 @@ filelist_print (filelist_t *filelist) if (filelist->count) { qsort (filelist->list, filelist->count, sizeof (char *), filelist_cmp); - list = malloc ((filelist->count + 1) * sizeof (char **)); + list = malloc ((filelist->count + 1) * sizeof (char *)); list[filelist->count] = 0; for (i = 0; i < filelist->count; i++) list[i] = filelist->list[i]; @@ -167,7 +167,7 @@ Con_Skyboxlist_f (void) for (j = 1; sb_endings[j]; j++) { b = 0; for (k = 0; k < skyboxlist->count; k++) { - if (strcmp(va("%s%s", basename->str, sb_endings[j]), + if (strcmp(va (0, "%s%s", basename->str, sb_endings[j]), skyboxlist->list[k]) == 0) { b = 1; *skyboxlist->list[k] = 0; diff --git a/libs/console/menu.c b/libs/console/menu.c index 48df52b7d..af64b90ba 100644 --- a/libs/console/menu.c +++ b/libs/console/menu.c @@ -40,13 +40,16 @@ #include "QF/cvar.h" #include "QF/draw.h" #include "QF/hash.h" +#include "QF/keys.h" #include "QF/progs.h" #include "QF/quakefs.h" #include "QF/render.h" #include "QF/ruamoko.h" #include "QF/sound.h" #include "QF/sys.h" -#include "QF/view.h" + +#include "QF/input/event.h" +#include "QF/ui/view.h" #include "QF/plugin/console.h" #include "QF/plugin/vid_render.h" @@ -65,33 +68,42 @@ typedef struct menu_item_s { int max_items; int cur_item; int x, y; - func_t func; - func_t cursor; - func_t keyevent; - func_t draw; - func_t enter_hook; - func_t leave_hook; + pr_func_t func; + pr_func_t cursor; + pr_func_t keyevent; + pr_func_t draw; + pr_func_t enter_hook; + pr_func_t leave_hook; unsigned fadescreen:1; unsigned allkeys:1; const char *text; menu_pic_t *pics; } menu_item_t; -static cvar_t *confirm_quit; +static int confirm_quit; +static cvar_t confirm_quit_cvar = { + .name = "confirm_quit", + .description = + "confirm quit command", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &confirm_quit }, +}; static progs_t menu_pr_state; static menu_item_t *menu; +//static keydest_t menu_keydest; static hashtab_t *menu_hash; -static func_t menu_init; -static func_t menu_quit; -static func_t menu_draw_hud; -static func_t menu_pre; -static func_t menu_post; -static const char *top_menu; +static pr_func_t menu_init; +static pr_func_t menu_quit; +static pr_func_t menu_draw_hud; +static pr_func_t menu_pre; +static pr_func_t menu_post; +static char *top_menu; typedef struct menu_func_s { const char *name; - func_t *func; + pr_func_t *func; } menu_func_t; static menu_func_t menu_functions[] = { @@ -117,7 +129,7 @@ static int menu_resolve_globals (progs_t *pr) { const char *sym; - ddef_t *def; + pr_def_t *def; dfunction_t *f; size_t i; @@ -126,12 +138,12 @@ menu_resolve_globals (progs_t *pr) sym = menu_functions[i].name; if (!(f = PR_FindFunction (pr, sym))) goto error; - *menu_functions[i].func = (func_t) (f - menu_pr_state.pr_functions); + *menu_functions[i].func = (pr_func_t) (f - menu_pr_state.pr_functions); } if (!(def = PR_FindGlobal (pr, sym = "time"))) goto error; - menu_pr_state.globals.time = &G_FLOAT (pr, def->ofs); + menu_pr_state.globals.ftime = &G_FLOAT (pr, def->ofs);//FIXME double time return 1; error: Sys_Printf ("%s: undefined symbol %s\n", pr->progs_name, sym); @@ -209,7 +221,7 @@ menu_pic (int x, int y, const char *name, } static void -bi_Menu_Begin (progs_t *pr) +bi_Menu_Begin (progs_t *pr, void *data) { int x = P_INT (pr, 0); int y = P_INT (pr, 1); @@ -227,31 +239,31 @@ bi_Menu_Begin (progs_t *pr) } static void -bi_Menu_FadeScreen (progs_t *pr) +bi_Menu_FadeScreen (progs_t *pr, void *data) { menu->fadescreen = P_INT (pr, 0); } static void -bi_Menu_Draw (progs_t *pr) +bi_Menu_Draw (progs_t *pr, void *data) { menu->draw = P_FUNCTION (pr, 0); } static void -bi_Menu_EnterHook (progs_t *pr) +bi_Menu_EnterHook (progs_t *pr, void *data) { menu->enter_hook = P_FUNCTION (pr, 0); } static void -bi_Menu_LeaveHook (progs_t *pr) +bi_Menu_LeaveHook (progs_t *pr, void *data) { menu->leave_hook = P_FUNCTION (pr, 0); } static void -bi_Menu_Pic (progs_t *pr) +bi_Menu_Pic (progs_t *pr, void *data) { int x = P_INT (pr, 0); int y = P_INT (pr, 1); @@ -261,7 +273,7 @@ bi_Menu_Pic (progs_t *pr) } static void -bi_Menu_SubPic (progs_t *pr) +bi_Menu_SubPic (progs_t *pr, void *data) { int x = P_INT (pr, 0); int y = P_INT (pr, 1); @@ -275,7 +287,7 @@ bi_Menu_SubPic (progs_t *pr) } static void -bi_Menu_CenterPic (progs_t *pr) +bi_Menu_CenterPic (progs_t *pr, void *data) { int x = P_INT (pr, 0); int y = P_INT (pr, 1); @@ -289,7 +301,7 @@ bi_Menu_CenterPic (progs_t *pr) } static void -bi_Menu_CenterSubPic (progs_t *pr) +bi_Menu_CenterSubPic (progs_t *pr, void *data) { int x = P_INT (pr, 0); int y = P_INT (pr, 1); @@ -307,12 +319,12 @@ bi_Menu_CenterSubPic (progs_t *pr) } static void -bi_Menu_Item (progs_t *pr) +bi_Menu_Item (progs_t *pr, void *data) { int x = P_INT (pr, 0); int y = P_INT (pr, 1); const char *text = P_GSTRING (pr, 2); - func_t func = P_FUNCTION (pr, 3); + pr_func_t func = P_FUNCTION (pr, 3); int allkeys = P_INT (pr, 4); menu_item_t *mi = calloc (sizeof (menu_item_t), 1); @@ -326,39 +338,39 @@ bi_Menu_Item (progs_t *pr) } static void -bi_Menu_Cursor (progs_t *pr) +bi_Menu_Cursor (progs_t *pr, void *data) { - func_t func = P_FUNCTION (pr, 0); + pr_func_t func = P_FUNCTION (pr, 0); menu->cursor = func; } static void -bi_Menu_KeyEvent (progs_t *pr) +bi_Menu_KeyEvent (progs_t *pr, void *data) { - func_t func = P_FUNCTION (pr, 0); + pr_func_t func = P_FUNCTION (pr, 0); menu->keyevent = func; } static void -bi_Menu_End (progs_t *pr) +bi_Menu_End (progs_t *pr, void *data) { menu = menu->parent; } static void -bi_Menu_TopMenu (progs_t *pr) +bi_Menu_TopMenu (progs_t *pr, void *data) { const char *name = P_GSTRING (pr, 0); if (top_menu) - free ((char *) top_menu); + free (top_menu); top_menu = strdup (name); } static void -bi_Menu_SelectMenu (progs_t *pr) +bi_Menu_SelectMenu (progs_t *pr, void *data) { const char *name = P_GSTRING (pr, 0); @@ -366,7 +378,7 @@ bi_Menu_SelectMenu (progs_t *pr) if (name && *name) menu = Hash_Find (menu_hash, name); if (menu) { - Key_SetKeyDest (key_menu); + Con_SetState (con_menu); if (menu->enter_hook) { run_menu_pre (); PR_ExecuteProgram (&menu_pr_state, menu->enter_hook); @@ -375,24 +387,20 @@ bi_Menu_SelectMenu (progs_t *pr) } else { if (name && *name) Sys_Printf ("no menu \"%s\"\n", name); - if (con_data.force_commandline) { - Key_SetKeyDest (key_console); - } else { - Key_SetKeyDest (key_game); - } + Con_SetState (con_inactive); } } static void -bi_Menu_SetQuit (progs_t *pr) +bi_Menu_SetQuit (progs_t *pr, void *data) { - func_t func = P_FUNCTION (pr, 0); + pr_func_t func = P_FUNCTION (pr, 0); menu_quit = func; } static void -bi_Menu_Quit (progs_t *pr) +bi_Menu_Quit (progs_t *pr, void *data) { if (con_data.quit) con_data.quit (); @@ -400,7 +408,7 @@ bi_Menu_Quit (progs_t *pr) } static void -bi_Menu_GetIndex (progs_t *pr) +bi_Menu_GetIndex (progs_t *pr, void *data) { if (menu) { R_INT (pr) = menu->cur_item; @@ -410,21 +418,21 @@ bi_Menu_GetIndex (progs_t *pr) } static void -bi_Menu_Next (progs_t *pr) +bi_Menu_Next (progs_t *pr, void *data) { menu->cur_item++; menu->cur_item %= menu->num_items; } static void -bi_Menu_Prev (progs_t *pr) +bi_Menu_Prev (progs_t *pr, void *data) { menu->cur_item += menu->num_items - 1; menu->cur_item %= menu->num_items; } static void -bi_Menu_Enter (progs_t *pr) +bi_Menu_Enter (progs_t *pr, void *data) { menu_item_t *item; @@ -439,6 +447,7 @@ bi_Menu_Enter (progs_t *pr) P_STRING (&menu_pr_state, 0) = PR_SetTempString (&menu_pr_state, item->text); P_INT (&menu_pr_state, 1) = 0; + pr->pr_argc = 2; PR_ExecuteProgram (&menu_pr_state, item->func); PR_PopFrame (&menu_pr_state); run_menu_post (); @@ -453,7 +462,7 @@ bi_Menu_Enter (progs_t *pr) } static void -bi_Menu_Leave (progs_t *pr) +bi_Menu_Leave (progs_t *pr, void *data) { if (menu) { if (menu->leave_hook) { @@ -463,11 +472,7 @@ bi_Menu_Leave (progs_t *pr) } menu = menu->parent; if (!menu) { - if (con_data.force_commandline) { - Key_SetKeyDest (key_console); - } else { - Key_SetKeyDest (key_game); - } + Con_SetState (con_inactive); } } } @@ -486,7 +491,7 @@ quit_f (void) { int ret; - if (confirm_quit->int_val && menu_quit) { + if (confirm_quit && menu_quit) { run_menu_pre (); PR_ExecuteProgram (&menu_pr_state, menu_quit); ret = R_INT (&menu_pr_state); @@ -494,7 +499,7 @@ quit_f (void) if (!ret) return; } - bi_Menu_Quit (&menu_pr_state); + bi_Menu_Quit (&menu_pr_state, 0); } static void * @@ -510,38 +515,56 @@ menu_free_progs_mem (progs_t *pr, void *mem) } static void * -menu_load_file (progs_t *pr, const char *path) +menu_load_file (progs_t *pr, const char *path, off_t *size) { - return QFS_LoadFile (QFS_FOpenFile (path), 0); + void *data = QFS_LoadFile (QFS_FOpenFile (path), 0); + *size = qfs_filesize; + return data; } +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) static builtin_t builtins[] = { - {"Menu_Begin", bi_Menu_Begin, -1}, - {"Menu_FadeScreen", bi_Menu_FadeScreen, -1}, - {"Menu_Draw", bi_Menu_Draw, -1}, - {"Menu_EnterHook", bi_Menu_EnterHook, -1}, - {"Menu_LeaveHook", bi_Menu_LeaveHook, -1}, - {"Menu_Pic", bi_Menu_Pic, -1}, - {"Menu_SubPic", bi_Menu_SubPic, -1}, - {"Menu_CenterPic", bi_Menu_CenterPic, -1}, - {"Menu_CenterSubPic", bi_Menu_CenterSubPic, -1}, - {"Menu_Item", bi_Menu_Item, -1}, - {"Menu_Cursor", bi_Menu_Cursor, -1}, - {"Menu_KeyEvent", bi_Menu_KeyEvent, -1}, - {"Menu_End", bi_Menu_End, -1}, - {"Menu_TopMenu", bi_Menu_TopMenu, -1}, - {"Menu_SelectMenu", bi_Menu_SelectMenu, -1}, - {"Menu_SetQuit", bi_Menu_SetQuit, -1}, - {"Menu_Quit", bi_Menu_Quit, -1}, - {"Menu_GetIndex", bi_Menu_GetIndex, -1}, - {"Menu_Next", bi_Menu_Next, -1}, - {"Menu_Prev", bi_Menu_Prev, -1}, - {"Menu_Enter", bi_Menu_Enter, -1}, - {"Menu_Leave", bi_Menu_Leave, -1}, + bi(Menu_Begin, 3, p(int), p(int), p(string)), + bi(Menu_FadeScreen, 1, p(int)), + bi(Menu_Draw, 2, p(int), p(int)), + bi(Menu_EnterHook, 1, p(func)), + bi(Menu_LeaveHook, 1, p(func)), + bi(Menu_Pic, 3, p(int), p(int), p(string)), + bi(Menu_SubPic, 7, p(int), p(int), p(string), + p(int), p(int), p(int), p(int)), + bi(Menu_CenterPic, 3, p(int), p(int), p(string)), + bi(Menu_CenterSubPic, 7, p(int), p(int), p(string), + p(int), p(int), p(int), p(int)), + bi(Menu_Item, 5, p(int), p(int), p(string), p(func), p(int)), + bi(Menu_Cursor, 1, p(func)), + bi(Menu_KeyEvent, 1, p(func)), + bi(Menu_End, 0), + bi(Menu_TopMenu, 1, p(string)), + bi(Menu_SelectMenu, 1, p(string)), + bi(Menu_SetQuit, 1, p(func)), + bi(Menu_Quit, 0), + bi(Menu_GetIndex, 0), + bi(Menu_Next, 0), + bi(Menu_Prev, 0), + bi(Menu_Enter, 0), + bi(Menu_Leave, 0), {0}, }; - +static int//FIXME reimplement users properly (or remove?) +Menu_KeyEvent (int key, int unicode, int pressed) +{ + IE_event_t event = { + .type = ie_key, + .when = Sys_LongTime (), + .key = { + .code = key, + .unicode = unicode, + } + }; + return IE_Send_Event (&event); +} void Menu_Enter_f (void) @@ -568,6 +591,13 @@ Menu_Next_f (void) Menu_KeyEvent (QFK_DOWN, '\0', true); } +void +Menu_Shutdown (void) +{ + PR_Shutdown (&menu_pr_state); + Hash_DelTable (menu_hash); + free (top_menu); +} void Menu_Init (void) @@ -578,21 +608,26 @@ Menu_Init (void) menu_pr_state.load_file = menu_load_file; menu_pr_state.resolve = menu_resolve_globals; - menu_hash = Hash_NewTable (61, menu_get_key, menu_free, 0); + menu_pr_state.max_edicts = 0; + menu_pr_state.zone_size = 1024 * 1024; + menu_pr_state.stack_size = 64 * 1024; - PR_RegisterBuiltins (&menu_pr_state, builtins); + PR_Init (&menu_pr_state); - RUA_Init (&menu_pr_state, 1); + menu_hash = Hash_NewTable (61, menu_get_key, menu_free, 0, 0); + + PR_RegisterBuiltins (&menu_pr_state, builtins, 0); + + RUA_Init (&menu_pr_state, 3); InputLine_Progs_Init (&menu_pr_state); - Key_Progs_Init (&menu_pr_state); + RUA_Game_Init (&menu_pr_state, 1); GIB_Progs_Init (&menu_pr_state); PR_Cmds_Init (&menu_pr_state); R_Progs_Init (&menu_pr_state); S_Progs_Init (&menu_pr_state); - confirm_quit = Cvar_Get ("confirm_quit", "1", CVAR_ARCHIVE, NULL, - "confirm quit command"); + Cvar_Register (&confirm_quit_cvar, 0, 0); Cmd_AddCommand ("togglemenu", togglemenu_f, "Toggle the display of the menu"); @@ -617,10 +652,11 @@ Menu_Load (void) menu_pr_state.progs = 0; if ((file = QFS_FOpenFile (menu_pr_state.progs_name))) { size = Qfilesize (file); - PR_LoadProgsFile (&menu_pr_state, file, size, 0, 1024 * 1024); + PR_LoadProgsFile (&menu_pr_state, file, size); Qclose (file); - if (!PR_RunLoadFuncs (&menu_pr_state)) { + if (!PR_RunLoadFuncs (&menu_pr_state) + || !PR_RunPostLoadFuncs (&menu_pr_state)) { free (menu_pr_state.progs); menu_pr_state.progs = 0; } @@ -641,7 +677,7 @@ Menu_Load (void) } void -Menu_Draw (view_t *view) +Menu_Draw (view_t view) { menu_pic_t *m_pic; int i, x, y; @@ -650,23 +686,27 @@ Menu_Draw (view_t *view) if (!menu) return; - x = view->xabs; - y = view->yabs; + view_pos_t abs = View_GetAbs (view); + x = abs.x; + y = abs.y; if (menu->fadescreen) r_funcs->Draw_FadeScreen (); - *menu_pr_state.globals.time = *con_data.realtime; + *menu_pr_state.globals.ftime = *con_data.realtime;//FIXME double time if (menu->draw) { int ret; run_menu_pre (); + PR_PushFrame (&menu_pr_state); PR_RESET_PARAMS (&menu_pr_state); P_INT (&menu_pr_state, 0) = x; P_INT (&menu_pr_state, 1) = y; + menu_pr_state.pr_argc = 2; PR_ExecuteProgram (&menu_pr_state, menu->draw); ret = R_INT (&menu_pr_state); + PR_PopFrame (&menu_pr_state); run_menu_post (); if (!ret) return; @@ -696,10 +736,13 @@ Menu_Draw (view_t *view) item = menu->items[menu->cur_item]; if (menu->cursor) { run_menu_pre (); + PR_PushFrame (&menu_pr_state); PR_RESET_PARAMS (&menu_pr_state); P_INT (&menu_pr_state, 0) = x + item->x; P_INT (&menu_pr_state, 1) = y + item->y; + menu_pr_state.pr_argc = 2; PR_ExecuteProgram (&menu_pr_state, menu->cursor); + PR_PopFrame (&menu_pr_state); run_menu_post (); } else { r_funcs->Draw_Character (x + item->x, y + item->y, @@ -708,31 +751,35 @@ Menu_Draw (view_t *view) } void -Menu_Draw_Hud (view_t *view) +Menu_Draw_Hud (view_t view) { run_menu_pre (); - *menu_pr_state.globals.time = *con_data.realtime; + *menu_pr_state.globals.ftime = *con_data.realtime;//FIXME double time PR_ExecuteProgram (&menu_pr_state, menu_draw_hud); run_menu_post (); } -int -Menu_KeyEvent (knum_t key, short unicode, qboolean down) +static int +menu_key_event (const IE_event_t *ie_event) { menu_item_t *item; int ret; + __auto_type key = ie_event->key; if (!menu) return 0; if (menu->keyevent) { run_menu_pre (); + PR_PushFrame (&menu_pr_state); PR_RESET_PARAMS (&menu_pr_state); - P_INT (&menu_pr_state, 0) = key; - P_INT (&menu_pr_state, 1) = unicode; - P_INT (&menu_pr_state, 2) = down; + P_INT (&menu_pr_state, 0) = key.code; + P_INT (&menu_pr_state, 1) = key.unicode; + P_INT (&menu_pr_state, 2) = 1; //FIXME only presses now + menu_pr_state.pr_argc = 3; PR_ExecuteProgram (&menu_pr_state, menu->keyevent); ret = R_INT (&menu_pr_state); + PR_PopFrame (&menu_pr_state); run_menu_post (); if (ret) return 1; @@ -744,47 +791,79 @@ Menu_KeyEvent (knum_t key, short unicode, qboolean down) PR_RESET_PARAMS (&menu_pr_state); P_STRING (&menu_pr_state, 0) = PR_SetTempString (&menu_pr_state, item->text); - P_INT (&menu_pr_state, 1) = key; + P_INT (&menu_pr_state, 1) = key.code; + menu_pr_state.pr_argc = 2; PR_ExecuteProgram (&menu_pr_state, item->func); - PR_PopFrame (&menu_pr_state); ret = R_INT (&menu_pr_state); + PR_PopFrame (&menu_pr_state); run_menu_post (); if (ret) return 1; } + if (key.code == QFK_ESCAPE) { + Menu_Leave (); + return 1; + } if (!menu || !menu->items) return 0; - switch (key) { + switch (key.code) { + case QFK_ESCAPE: + break; case QFK_DOWN: - case QFM_WHEEL_DOWN: - bi_Menu_Next (&menu_pr_state); - return 1; +// case QFM_WHEEL_DOWN: + bi_Menu_Next (&menu_pr_state, 0); + break; case QFK_UP: - case QFM_WHEEL_UP: - bi_Menu_Prev (&menu_pr_state); - return 1; +// case QFM_WHEEL_UP: + bi_Menu_Prev (&menu_pr_state, 0); + break; case QFK_RETURN: - case QFM_BUTTON1: - bi_Menu_Enter (&menu_pr_state); - return 1; +// case QFM_BUTTON1: + bi_Menu_Enter (&menu_pr_state, 0); + break; default: - return 0; + break; } + return 1; +} + +static int +menu_mouse_event (const IE_event_t *ie_event) +{ + return 0; +} + +int +Menu_EventHandler (const IE_event_t *ie_event) +{ + static int (*handlers[ie_event_count]) (const IE_event_t *ie_event) = { + [ie_key] = menu_key_event, + [ie_mouse] = menu_mouse_event, + }; + if ((unsigned) ie_event->type >= ie_event_count + || !handlers[ie_event->type]) { + return 0; + } + return handlers[ie_event->type] (ie_event); } void Menu_Enter () { if (!top_menu) { - Key_SetKeyDest (key_console); + Con_SetState (con_active); return; } - Key_SetKeyDest (key_menu); - menu = Hash_Find (menu_hash, top_menu); - if (menu && menu->enter_hook) { - run_menu_pre (); - PR_ExecuteProgram (&menu_pr_state, menu->enter_hook); - run_menu_post (); + if (!menu) { + menu = Hash_Find (menu_hash, top_menu); + } + if (menu) { + Con_SetState (con_menu); + if (menu->enter_hook) { + run_menu_pre (); + PR_ExecuteProgram (&menu_pr_state, menu->enter_hook); + run_menu_post (); + } } } @@ -799,12 +878,7 @@ Menu_Leave () } menu = menu->parent; if (!menu) { - if (con_data.force_commandline) { - Key_SetKeyDest (key_console); - } else { - Key_SetKeyDest (key_game); - } + Con_SetState (con_inactive); } } - r_data->vid->recalc_refdef = true; } diff --git a/libs/console/server.c b/libs/console/server.c index 5bf9061ec..f591f792b 100644 --- a/libs/console/server.c +++ b/libs/console/server.c @@ -31,7 +31,7 @@ # include "config.h" #endif -#ifdef HAVE_CURSES_H +#ifdef HAVE_NCURSES # include #endif #ifdef HAVE_STRING_H @@ -68,23 +68,76 @@ #include "QF/quakefs.h" #include "QF/sys.h" #include "QF/va.h" -#include "QF/view.h" #include "QF/plugin/general.h" #include "QF/plugin/console.h" +#include "QF/ui/view.h" + +#include "QF/ui/inputline.h" + #include "compat.h" #include "sv_console.h" static console_data_t sv_con_data; static QFile *log_file; -static cvar_t *sv_logfile; -static cvar_t *sv_conmode; +static char *sv_logfile; +static cvar_t sv_logfile_cvar = { + .name = "sv_logfile", + .description = + "Control server console logging. \"none\" for off, or " + "\"filename:gzflags\"", + .default_value = "none", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &sv_logfile }, +}; +static exprenum_t sv_conmode_enum; +static exprtype_t sv_conmode_type = { + .name = "sv_conmode", + .size = sizeof (sv_con_data.exec_line), + .data = &sv_conmode_enum, + .get_string = cexpr_enum_get_string, +}; +static int sv_exec_line_command (void *data, const char *line); +static int sv_exec_line_chat (void *data, const char *line); +static int (*sv_conmode_values[])(void *, const char *) = { + sv_exec_line_command, + sv_exec_line_chat, +}; +static exprsym_t sv_conmode_symbols[] = { + {"command", &sv_conmode_type, sv_conmode_values + 0}, + {"chat", &sv_conmode_type, sv_conmode_values + 1}, + {} +}; +static exprtab_t sv_conmode_symtab = { + sv_conmode_symbols, +}; +static exprenum_t sv_conmode_enum = { + &sv_conmode_type, + &sv_conmode_symtab, +}; +static cvar_t sv_conmode_cvar = { + .name = "sv_conmode", + .description = + "Set the console input mode (command, chat)", + .default_value = "command", + .flags = CVAR_NONE, + .value = { .type = &sv_conmode_type, .value = &sv_con_data.exec_line }, +}; +static int sv_use_curses; +static cvar_t sv_use_curses_cvar = { + .name = "sv_use_curses", + .description = + "Set to 1 to enable curses server console.", + .default_value = "0", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &sv_use_curses }, +}; -static void C_KeyEvent (knum_t key, short unicode, qboolean down); +#ifdef HAVE_NCURSES -#ifdef HAVE_CURSES_H +static void key_event (knum_t key, short unicode, bool down); enum { sv_resize_x = 1, @@ -93,15 +146,24 @@ enum { sv_cursor = 8, }; +#ifdef HAVE_NCURSES static int use_curses = 1; +#else +static int use_curses = 0; +#endif -static view_t *output; -static view_t *status; -static view_t *input; +static view_t sv_view; +static view_t output; +static view_t status; +static view_t input; static int screen_x, screen_y; -static int interrupted; +static volatile sig_atomic_t interrupted; static int batch_print; +static ecs_registry_t *server_reg; +static uint32_t server_base; +static uint32_t view_base; + #define MAXCMDLINE 256 #define BUFFER_SIZE 32768 @@ -118,6 +180,7 @@ static int view_offset; #define CP_RED_BLUE (8) #define CP_CYAN_BLUE (9) #define CP_MAGENTA_BLUE (10) +#define CP_WHITE_BLUE (11) static chtype attr_table[16] = { A_NORMAL, @@ -128,13 +191,13 @@ static chtype attr_table[16] = { COLOR_PAIR (CP_CYAN_BLACK), COLOR_PAIR (CP_MAGENTA_BLACK), 0, - A_NORMAL, - COLOR_PAIR (CP_GREEN_BLUE), - COLOR_PAIR (CP_RED_BLUE), + COLOR_PAIR(CP_WHITE_BLUE), + A_BOLD | COLOR_PAIR (CP_GREEN_BLUE), + A_BOLD | COLOR_PAIR (CP_RED_BLUE), 0, - COLOR_PAIR (CP_YELLOW_BLUE), - COLOR_PAIR (CP_CYAN_BLUE), - COLOR_PAIR (CP_MAGENTA_BLUE), + A_BOLD | COLOR_PAIR (CP_YELLOW_BLUE), + A_BOLD | COLOR_PAIR (CP_CYAN_BLUE), + A_BOLD | COLOR_PAIR (CP_MAGENTA_BLUE), 0, }; @@ -157,6 +220,22 @@ static const byte attr_map[256] = { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, }; +static const component_t server_components[server_comp_count] = { + [server_href] = { + .size = sizeof (hierref_t), + .name = "href", + .destroy = Hierref_DestroyComponent, + }, + [server_view] = { + .size = sizeof (sv_view_t), + .name = "sv_view", + }, + [server_window] = { + .size = sizeof (sv_view_t), + .name = "sv_window", + }, +}; + static inline void draw_fun_char (WINDOW *win, byte c, int blue) @@ -168,37 +247,46 @@ draw_fun_char (WINDOW *win, byte c, int blue) } static inline void -sv_refresh (view_t *view) +sv_refresh_windows (void) { - sv_view_t *sv_view = view->data; - wnoutrefresh ((WINDOW *) sv_view->win); + uint32_t window_comp = server_base + server_window; + sv_view_t *window = server_reg->comp_pools[window_comp].data; + uint32_t count = server_reg->comp_pools[window_comp].count; + while (count-- > 0) { + wnoutrefresh ((WINDOW *) (window++)->win); + } + doupdate (); } static inline int -sv_getch (view_t *view) +sv_getch (view_t view) { - sv_view_t *sv_view = view->data; - return wgetch ((WINDOW *) sv_view->win); + sv_view_t *window = Ent_GetComponent (view.id, server_window, view.reg); + return wgetch ((WINDOW *) window->win); } static inline void -sv_draw (view_t *view) +sv_draw (view_t view) { - sv_view_t *sv_view = view->data; - if (sv_view->draw) - sv_view->draw (view); - wnoutrefresh ((WINDOW *) sv_view->win); + sv_view_t *window = Ent_GetComponent (view.id, server_window, view.reg); + if (window->draw) + window->draw (view); + wnoutrefresh ((WINDOW *) window->win); + doupdate (); } -static inline void -sv_setgeometry (view_t *view) +static void +sv_setgeometry (view_t view, view_pos_t foo) { - sv_view_t *sv_view = view->data; - WINDOW *win = sv_view->win; - wresize (win, view->ylen, view->xlen); - mvwin (win, view->yabs, view->xabs); - if (sv_view->setgeometry) - sv_view->setgeometry (view); + sv_view_t *window = Ent_GetComponent (view.id, server_window, view.reg); + WINDOW *win = window->win; + + view_pos_t pos = View_GetAbs (view); + view_pos_t len = View_GetLen (view); + wresize (win, len.y, len.x); + mvwin (win, pos.y, pos.x); + if (window->setgeometry) + window->setgeometry (view); } static void @@ -208,22 +296,21 @@ sv_complete (inputline_t *il) Con_BasicCompleteCommandLine (il); batch_print = 0; - sv_refresh (output); - sv_refresh (input); - doupdate (); + sv_refresh_windows (); } static void -draw_output (view_t *view) +draw_output (view_t view) { - sv_view_t *sv_view = view->data; - WINDOW *win = sv_view->win; - con_buffer_t *output_buffer = sv_view->obj; + sv_view_t *window = Ent_GetComponent (view.id, server_window, view.reg); + WINDOW *win = window->win; + con_buffer_t *output_buffer = window->obj; + view_pos_t len = View_GetLen (view); // this is not the most efficient way to update the screen, but oh well - int lines = view->ylen - 1; // leave a blank line - int width = view->xlen; - int cur_line = output_buffer->cur_line + view_offset; + int lines = len.y - 1; // leave a blank line + int width = len.x; + int cur_line = output_buffer->line_head - 1 + view_offset; int i, y; if (lines < 1) @@ -243,7 +330,7 @@ draw_output (view_t *view) wmove (win, 0, 0); do { con_line_t *l = Con_BufferLine (output_buffer, cur_line++); - byte *text = l->text; + byte *text = output_buffer->buffer + l->text; int len = l->len; if (y > 0) { @@ -252,30 +339,37 @@ draw_output (view_t *view) y = 0; if (len < 1) { len = 1; - text = l->text + l->len - 1; + text = output_buffer->buffer + l->text + l->len - 1; } } while (len--) draw_fun_char (win, *text++, 0); - } while (cur_line < output_buffer->cur_line + view_offset); + } while (cur_line < (int) output_buffer->line_head - 1 + view_offset); } static void -draw_status (view_t *view) +draw_status (view_t view) { - sv_view_t *sv_view = view->data; - WINDOW *win = sv_view->win; - sv_sbar_t *sb = sv_view->obj; - int i; + sv_view_t *window = Ent_GetComponent (view.id, server_window, view.reg); + WINDOW *win = window->win; + sv_sbar_t *sb = window->obj; char *old = alloca (sb->width); memcpy (old, sb->text, sb->width); memset (sb->text, ' ', sb->width); - view_draw (view); + + ecs_pool_t *pool = &server_reg->comp_pools[server_view]; + sv_view_t *sv_view = pool->data; + for (uint32_t i = 0; i < pool->count; i++) { + view_t v = { .reg = view.reg, .id = pool->dense[i], + .comp = view.comp }; + (sv_view++)->draw (v); + } + if (memcmp (old, sb->text, sb->width)) { - wbkgdset (win, COLOR_PAIR (CP_YELLOW_BLUE)); + wbkgdset (win, COLOR_PAIR (CP_WHITE_BLUE)); wmove (win, 0, 0); - for (i = 0; i < sb->width; i++) + for (int i = 0; i < sb->width; i++) draw_fun_char (win, sb->text[i], 1); } } @@ -283,9 +377,9 @@ draw_status (view_t *view) static void draw_input_line (inputline_t *il) { - view_t *view = il->user_data; - sv_view_t *sv_view = view->data; - WINDOW *win = sv_view->win; + view_t view = *(view_t *) il->user_data; + sv_view_t *window = Ent_GetComponent (view.id, server_window, view.reg); + WINDOW *win = window->win; size_t i; const char *text; @@ -314,26 +408,28 @@ draw_input_line (inputline_t *il) } static void -draw_input (view_t *view) +draw_input (view_t view) { - sv_view_t *sv_view = view->data; - draw_input_line (sv_view->obj); + sv_view_t *window = Ent_GetComponent (view.id, server_window, view.reg); + draw_input_line (window->obj); } static void -setgeometry_input (view_t *view) +setgeometry_input (view_t view) { - sv_view_t *sv_view = view->data; - inputline_t *il = sv_view->obj; - il->width = view->xlen; + sv_view_t *window = Ent_GetComponent (view.id, server_window, view.reg); + view_pos_t len = View_GetLen (view); + inputline_t *il = window->obj; + il->width = len.x; } static void -setgeometry_status (view_t *view) +setgeometry_status (view_t view) { - sv_view_t *sv_view = view->data; - sv_sbar_t *sb = sv_view->obj; - sb->width = view->xlen; + sv_view_t *window = Ent_GetComponent (view.id, server_window, view.reg); + sv_sbar_t *sb = window->obj; + view_pos_t len = View_GetLen (view); + sb->width = len.x; sb->text = realloc (sb->text, sb->width); memset (sb->text, 0, sb->width); // force an update } @@ -342,13 +438,12 @@ static void sigwinch (int sig) { interrupted = 1; - signal (SIGWINCH, sigwinch); } #endif static void get_size (int *xlen, int *ylen) { -#if 0 +#ifdef SIGWINCH struct winsize size; *xlen = *ylen = 0; @@ -356,8 +451,9 @@ get_size (int *xlen, int *ylen) return; *xlen = size.ws_col; *ylen = size.ws_row; -#endif +#else getmaxyx (stdscr, *ylen, *xlen); +#endif } static void @@ -366,14 +462,15 @@ process_input (void) int ch; int escape = 0; - if (interrupted) { -#ifdef SIGWINCH + if (__builtin_expect (interrupted, 0)) { interrupted = 0; +#ifdef SIGWINCH get_size (&screen_x, &screen_y); + Sys_MaskPrintf (SYS_dev, "resizing to %d x %d\n", screen_x, screen_y); resizeterm (screen_y, screen_x); con_linewidth = screen_x; - view_resize (sv_con_data.view, screen_x, screen_y); - sv_con_data.view->draw (sv_con_data.view); + View_SetLen (sv_view, screen_x, screen_y); + sv_refresh_windows (); #endif } @@ -461,24 +558,27 @@ process_input (void) if (ch < 0 || ch >= 256) ch = 0; } - C_KeyEvent (ch, 0, 1); + key_event (ch, 0, 1); } } static void -key_event (knum_t key, short unicode, qboolean down) +key_event (knum_t key, short unicode, bool down) { int ovf = view_offset; - sv_view_t *sv_view; + sv_view_t *window; con_buffer_t *buffer; + int num_lines; switch (key) { case QFK_PAGEUP: view_offset -= 10; - sv_view = output->data; - buffer = sv_view->obj; - if (view_offset <= -(buffer->num_lines - (screen_y - 3))) - view_offset = -(buffer->num_lines - (screen_y - 3)) + 1; + window = Ent_GetComponent (output.id, server_window, output.reg); + buffer = window->obj; + num_lines = (buffer->line_head - buffer->line_tail + + buffer->max_lines) % buffer->max_lines; + if (view_offset <= -(num_lines - (screen_y - 3))) + view_offset = -(num_lines - (screen_y - 3)) + 1; if (ovf != view_offset) sv_draw (output); break; @@ -493,54 +593,54 @@ key_event (knum_t key, short unicode, qboolean down) sv_draw (output); break; default: - sv_view = input->data; - Con_ProcessInputLine (sv_view->obj, key); - sv_refresh (input); + window = Ent_GetComponent (input.id, server_window, input.reg); + Con_ProcessInputLine (window->obj, key); + sv_refresh_windows (); break; } - doupdate (); } static void print (char *txt) { - sv_view_t *sv_view = output->data; - Con_BufferAddText (sv_view->obj, txt); + sv_view_t *window = Ent_GetComponent (output.id, server_window, + output.reg); + Con_BufferAddText (window->obj, txt); if (!view_offset) { while (*txt) - draw_fun_char (sv_view->win, (byte) *txt++, 0); + draw_fun_char (window->win, (byte) *txt++, 0); if (!batch_print) { - sv_refresh (output); - doupdate (); + sv_refresh_windows (); } } } -static view_t * -create_window (view_t *parent, int xpos, int ypos, int xlen, int ylen, - grav_t grav, void *obj, int opts, void (*draw) (view_t *), - void (*setgeometry) (view_t *)) +static view_t +create_window (view_t parent, int xpos, int ypos, int xlen, int ylen, + grav_t grav, void *obj, int opts, void (*draw) (view_t), + void (*setgeometry) (view_t)) { - view_t *view; - sv_view_t *sv_view; + view_t view = View_New ((ecs_system_t) { server_reg, view_base }, + parent); + View_SetPos (view, xpos, ypos); + View_SetLen (view, xlen, ylen); + View_SetGravity (view, grav); + View_SetResize (view, !!(opts & sv_resize_x), !!(opts & sv_resize_y)); + View_SetOnResize (view, sv_setgeometry); + View_SetOnMove (view, sv_setgeometry); - sv_view = calloc (1, sizeof (sv_view_t)); - sv_view->obj = obj; - sv_view->win = newwin (ylen, xlen, 0, 0); // will get moved when added - scrollok (sv_view->win, (opts & sv_scroll) ? TRUE : FALSE); - leaveok (sv_view->win, (opts & sv_cursor) ? FALSE : TRUE); - nodelay (sv_view->win, TRUE); - keypad (sv_view->win, TRUE); - sv_view->draw = draw; - sv_view->setgeometry = setgeometry; + sv_view_t window = { + .obj = obj, + .win = newwin (ylen, xlen, 0, 0), // will get moved when updated + .draw = draw, + .setgeometry = setgeometry, + }; + Ent_SetComponent (view.id, server_window, view.reg, &window); - view = view_new (xpos, ypos, xlen, ylen, grav); - view->data = sv_view; - view->draw = sv_draw; - view->setgeometry = sv_setgeometry; - view->resize_x = (opts & sv_resize_x) != 0; - view->resize_y = (opts & sv_resize_y) != 0; - view_add (parent, view); + scrollok (window.win, (opts & sv_scroll) ? TRUE : FALSE); + leaveok (window.win, (opts & sv_cursor) ? FALSE : TRUE); + nodelay (window.win, TRUE); + keypad (window.win, TRUE); return view; } @@ -552,14 +652,14 @@ exec_line (inputline_t *il) } static inputline_t * -create_input_line (int width) +create_input_line (int width, view_t *view) { inputline_t *input_line; input_line = Con_CreateInputLine (16, MAXCMDLINE, ']'); input_line->complete = sv_complete; input_line->enter = exec_line; - input_line->user_data = input; + input_line->user_data = view; input_line->draw = draw_input_line; input_line->width = width; @@ -570,7 +670,9 @@ static void init (void) { #ifdef SIGWINCH - signal (SIGWINCH, sigwinch); + struct sigaction action = {}; + action.sa_handler = sigwinch; + sigaction (SIGWINCH, &action, 0); #endif initscr (); @@ -580,28 +682,40 @@ init (void) nonl (); - get_size (&screen_x, &screen_y); - sv_con_data.view = view_new (0, 0, screen_x, screen_y, grav_northwest); + server_reg = ECS_NewRegistry (); + server_base = ECS_RegisterComponents (server_reg, server_components, + server_comp_count); + view_base = ECS_RegisterComponents (server_reg, view_components, + view_comp_count); + ECS_CreateComponentPools (server_reg); - output = create_window (sv_con_data.view, + get_size (&screen_x, &screen_y); + + sv_view = View_New ((ecs_system_t) { server_reg, view_base }, nullview); + View_SetPos (sv_view, 0, 0); + View_SetLen (sv_view, screen_x, screen_y); + View_SetGravity (sv_view, grav_northwest); + + output = create_window (sv_view, 0, 0, screen_x, screen_y - 2, grav_northwest, Con_CreateBuffer (BUFFER_SIZE, MAX_LINES), sv_resize_x | sv_resize_y | sv_scroll, draw_output, 0); - status = create_window (sv_con_data.view, + status = create_window (sv_view, 0, 1, screen_x, 1, grav_southwest, calloc (1, sizeof (sv_sbar_t)), sv_resize_x, draw_status, setgeometry_status); - sv_con_data.status_view = status; + sv_con_data.status_view = &status; - input = create_window (sv_con_data.view, + input = create_window (sv_view, 0, 0, screen_x, 1, grav_southwest, - create_input_line (screen_x), + create_input_line (screen_x, &input), sv_resize_x | sv_cursor, draw_input, setgeometry_input); - ((inputline_t *) ((sv_view_t *) input->data)->obj)->user_data = input; + + View_UpdateHierarchy (sv_view); init_pair (CP_YELLOW_BLACK, COLOR_YELLOW, COLOR_BLACK); init_pair (CP_GREEN_BLACK, COLOR_GREEN, COLOR_BLACK); @@ -614,23 +728,23 @@ init (void) init_pair (CP_RED_BLUE, COLOR_RED, COLOR_BLUE); init_pair (CP_CYAN_BLUE, COLOR_CYAN, COLOR_BLUE); init_pair (CP_MAGENTA_BLUE, COLOR_MAGENTA, COLOR_BLUE); + init_pair (CP_WHITE_BLUE, COLOR_WHITE, COLOR_BLUE); con_linewidth = screen_x; - sv_con_data.view->draw (sv_con_data.view); wrefresh (curscr); } #endif static void -sv_logfile_f (cvar_t *var) +sv_logfile_f (void *data, const cvar_t *cvar) { - if (!var->string[0] || strequal (var->string, "none")) { + if (!sv_logfile[0] || strequal (sv_logfile, "none")) { if (log_file) Qclose (log_file); log_file = 0; } else { - char *fname = strdup (var->string); + char *fname = strdup (sv_logfile); char *flags = strrchr (fname, ':'); if (flags) { @@ -663,52 +777,39 @@ sv_exec_line_chat (void *data, const char *line) } static void -sv_conmode_f (cvar_t *var) +C_InitCvars (void) { - if (!strcmp (var->string, "command")) { - sv_con_data.exec_line = sv_exec_line_command; - } else if (!strcmp (var->string, "chat")) { - sv_con_data.exec_line = sv_exec_line_chat; - } else { - Sys_Printf ("mode must be one of \"command\" or \"chat\"\n"); - Sys_Printf (" forcing \"command\"\n"); - Cvar_Set (var, "command"); - } + Cvar_Register (&sv_use_curses_cvar, 0, 0); + Cvar_Register (&sv_logfile_cvar, sv_logfile_f, 0); + Cvar_Register (&sv_conmode_cvar, 0, 0); } static void C_Init (void) { -#ifdef HAVE_CURSES_H - cvar_t *curses = Cvar_Get ("sv_use_curses", "0", CVAR_ROM, NULL, - "Set to 1 to enable curses server console."); - use_curses = curses->int_val; +#ifdef HAVE_NCURSES + use_curses = sv_use_curses; if (use_curses) { init (); } else #endif setvbuf (stdout, 0, _IOLBF, BUFSIZ); - sv_logfile = Cvar_Get ("sv_logfile", "none", CVAR_NONE, sv_logfile_f, - "Control server console logging. \"none\" for off, " - "or \"filename:gzflags\""); - sv_conmode = Cvar_Get ("sv_conmode", "command", CVAR_NONE, sv_conmode_f, - "Set the console input mode (command, chat)"); } static void -C_Shutdown (void) +C_shutdown (void) { if (log_file) { Qclose (log_file); log_file = 0; } -#ifdef HAVE_CURSES_H +#ifdef HAVE_NCURSES if (use_curses) endwin (); #endif } -static void +static __attribute__((format(PRINTF, 1, 0))) void C_Print (const char *fmt, va_list args) { static dstring_t *buffer; @@ -722,7 +823,7 @@ C_Print (const char *fmt, va_list args) Qputs (log_file, buffer->str); Qflush (log_file); } -#ifdef HAVE_CURSES_H +#ifdef HAVE_NCURSES if (use_curses) { print (buffer->str); } else @@ -738,7 +839,7 @@ C_Print (const char *fmt, va_list args) static void C_ProcessInput (void) { -#ifdef HAVE_CURSES_H +#ifdef HAVE_NCURSES if (use_curses) { process_input (); } else @@ -751,26 +852,15 @@ C_ProcessInput (void) } } -static void -C_KeyEvent (knum_t key, short unicode, qboolean down) -{ -#ifdef HAVE_CURSES_H - key_event (key, unicode, down); -#endif -} - static void C_DrawConsole (void) { // only the status bar is drawn because the inputline and output views // take care of themselves - if (sv_con_data.status_view) - sv_con_data.status_view->draw (sv_con_data.status_view); -} - -static void -C_CheckResize (void) -{ + if (use_curses) { + draw_status (status); + sv_refresh_windows (); + } } static void @@ -779,32 +869,27 @@ C_NewMap (void) } static general_funcs_t plugin_info_general_funcs = { - C_Init, - C_Shutdown, + .init = C_InitCvars, + .shutdown = C_shutdown, }; static general_data_t plugin_info_general_data; static console_funcs_t plugin_info_console_funcs = { - C_Print, - C_ProcessInput, - C_KeyEvent, - C_DrawConsole, - C_CheckResize, - C_NewMap, + .init = C_Init, + .print = C_Print, + .process_input = C_ProcessInput, + .draw_console = C_DrawConsole, + .new_map = C_NewMap, }; static plugin_funcs_t plugin_info_funcs = { - &plugin_info_general_funcs, - 0, - 0, - &plugin_info_console_funcs, + .general = &plugin_info_general_funcs, + .console = &plugin_info_console_funcs, }; static plugin_data_t plugin_info_data = { - &plugin_info_general_data, - 0, - 0, - &sv_con_data, + .general = &plugin_info_general_data, + .console = &sv_con_data, }; static plugin_t plugin_info = { diff --git a/libs/console/test/Makemodule.am b/libs/console/test/Makemodule.am new file mode 100644 index 000000000..2116b5919 --- /dev/null +++ b/libs/console/test/Makemodule.am @@ -0,0 +1,15 @@ +libs_console_tests = \ + libs/console/test/test-buffer + +TESTS += $(libs_console_tests) + +check_PROGRAMS += $(libs_console_tests) + +test_console_libs= \ + libs/console/libQFconsole.la \ + libs/util/libQFutil.la + +libs_console_test_test_buffer_SOURCES= \ + libs/console/test/test-buffer.c +libs_console_test_test_buffer_LDADD= $(test_console_libs) +libs_console_test_test_buffer_DEPENDENCIES=$(test_console_libs) diff --git a/libs/console/test/test-buffer.c b/libs/console/test/test-buffer.c new file mode 100644 index 000000000..176e019a2 --- /dev/null +++ b/libs/console/test/test-buffer.c @@ -0,0 +1,236 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include + +#include "QF/console.h" + +static int +test_1 (void) +{ + int ret = 1; + __auto_type con = Con_CreateBuffer (1024, 25); + if (!con) { + printf ("con_buffer allocation failed\n"); + goto fail; + } + if (!con->buffer) { + printf ("con_buffer buffer not set\n"); + goto fail; + } + if (!con->lines) { + printf ("con_buffer lines not set\n"); + goto fail; + } + if (con->buffer_size != 1024) { + printf ("con_buffer buffer_size incorrect: %u\n", con->buffer_size); + goto fail; + } + if (con->max_lines != 25) { + printf ("con_buffer max_lines incorrect: %d\n", con->max_lines); + goto fail; + } + if (con->line_head != 1) { + printf ("con_buffer line_head incorrect: %d\n", con->line_head); + goto fail; + } + if (con->line_tail != 0) { + printf ("con_buffer line_tail incorrect: %d\n", con->line_tail); + goto fail; + } + if (con->lines[con->line_tail].text != 0) { + printf ("con_buffer line_tail.text incorrect: %u\n", + con->lines[con->line_tail].text); + goto fail; + } + if (con->lines[con->line_tail].len != 0) { + printf ("con_buffer line_tail.len incorrect: %u\n", + con->lines[con->line_tail].len); + goto fail; + } + + ret = 0; +fail: + Con_DestroyBuffer (con); + return ret; +} + +static int +test_2 (void) +{ + int ret = 1; + char text[2049]; + + for (size_t i = 0; i < sizeof (text); i++) { + int x = i % 13; + text[i] = x + (x > 9 ? 'a' - 10 : '0'); + } + text[sizeof(text) - 1] = 0; + + __auto_type con = Con_CreateBuffer (1024, 25); + Con_BufferAddText (con, text); + + if (con->line_head != 1) { + printf ("con_buffer line_head incorrect: %d\n", con->line_head); + goto fail; + } + if (con->line_tail != 0) { + printf ("con_buffer line_tail incorrect: %d\n", con->line_tail); + goto fail; + } + if (con->lines[con->line_tail].text != 1) { + printf ("con_buffer line_tail.text incorrect: %u\n", + con->lines[con->line_tail].text); + goto fail; + } + if (con->lines[con->line_tail].len != 1023) { + printf ("con_buffer line_tail.len incorrect: %u\n", + con->lines[con->line_tail].len); + goto fail; + } + if (memcmp (con->buffer + 1, text + 1025, 1023) + || con->buffer[0] != 0) { + printf ("con_buffer incorrect\n"); + goto fail; + } + + // Add a single char at the end of the full buffer. The buffer should + // just effectively scroll through the text as chars are added (via + // the single line object maintaining constant length but updating its + // text pointer) + Con_BufferAddText (con, "N"); + + if (con->line_head != 1) { + printf ("2 con_buffer line_head incorrect: %d\n", con->line_head); + goto fail; + } + if (con->line_tail != 0) { + printf ("2 con_buffer line_tail incorrect: %d\n", con->line_tail); + goto fail; + } + if (con->lines[con->line_tail].text != 2) { + printf ("2 con_buffer line_tail.text incorrect: %u\n", + con->lines[con->line_tail].text); + goto fail; + } + if (con->lines[con->line_tail].len != 1023) { + printf ("2 con_buffer line_tail.len incorrect: %u\n", + con->lines[con->line_tail].len); + goto fail; + } + if (memcmp (con->buffer + 2, text + 1026, 1022) + || con->buffer[0] != 'N' + || con->buffer[1] != 0) { + printf ("2 con_buffer incorrect\n"); + goto fail; + } + + ret = 0; +fail: + if (ret) { + printf ("%s failed\n", __FUNCTION__); + } + Con_DestroyBuffer (con); + return ret; +} + +static int +test_3 (void) +{ + int ret = 1; + static char text[] = R"(01 don't forget this line +02 and some more lines here +03 adsf +04 adfa +06 hi there +06 don't forget there's line 07 +)"; + static uint32_t lengths[] = { 26, 28, 9, 8, 12, 32, 0 }; + static uint32_t lengths2[] = { 1, 5, 2 }; + + __auto_type con = Con_CreateBuffer (256, 8); + Con_BufferAddText (con, text); + + if (con->line_head != 7) { + printf ("con_buffer line_head incorrect: %d\n", con->line_head); + goto fail; + } + if (con->line_tail != 0) { + printf ("con_buffer line_tail incorrect: %d\n", con->line_tail); + goto fail; + } + uint32_t offs = 0; + for (uint32_t i = 0; i < 7; i++) { + if (con->lines[i].text != offs) { + printf ("con_buffer lines[%d].text incorrect: %u %u\n", + i, con->lines[i].text, offs); + goto fail; + } + if (con->lines[i].len != lengths[i]) { + printf ("con_buffer lines[%d].len incorrect: %u %u\n", + i, con->lines[i].len, lengths[i]); + goto fail; + } + offs += lengths[i]; + } + + Con_BufferAddText (con, "\nasdf\nas"); + if (con->line_head != 1) { + printf ("2 con_buffer line_head incorrect: %d\n", con->line_head); + goto fail; + } + if (con->line_tail != 2) { + printf ("2 con_buffer line_tail incorrect: %d\n", con->line_tail); + goto fail; + } + offs = lengths[0] + lengths[1]; + for (uint32_t i = 0; i < 4; i++) { + uint32_t n = i + 2; + if (con->lines[n].text != offs) { + printf ("2 con_buffer lines[%d].text incorrect: %u %u\n", + n, con->lines[n].text, offs); + goto fail; + } + if (con->lines[n].len != lengths[n]) { + printf ("2 con_buffer lines[%d].len incorrect: %u %u\n", + n, con->lines[n].len, lengths[n]); + goto fail; + } + offs += lengths[n]; + } + for (uint32_t i = 0; i < 3; i++) { + uint32_t n = (i + 6) % con->max_lines; + if (con->lines[n].text != offs) { + printf ("3 con_buffer lines[%d].text incorrect: %u %u\n", + n, con->lines[n].text, offs); + goto fail; + } + if (con->lines[n].len != lengths2[i]) { + printf ("3 con_buffer lines[%d].len incorrect: %u %u\n", + n, con->lines[n].len, lengths2[i]); + goto fail; + } + offs += lengths2[i]; + } + + ret = 0; +fail: + if (ret) { + printf ("%s failed\n", __FUNCTION__); + } + Con_DestroyBuffer (con); + return ret; +} + +int +main (void) +{ + int ret = 0; + ret |= test_1 (); + ret |= test_2 (); + ret |= test_3 (); + return ret; +} diff --git a/libs/console/view.c b/libs/console/view.c deleted file mode 100644 index ed1ae064e..000000000 --- a/libs/console/view.c +++ /dev/null @@ -1,223 +0,0 @@ -/* - view.c - - console view object - - Copyright (C) 2003 Bill Currie - - Author: Bill Currie - Date: 2003/5/5 - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif -#include - -#include "QF/view.h" - -static void -setgeometry (view_t *view) -{ - int i; - view_t *par = view->parent; - - if (!par) { - view->xabs = view->xrel = view->xpos; - view->yabs = view->yrel = view->ypos; - if (view->setgeometry) - view->setgeometry (view); - for (i = 0; i < view->num_children; i++) - setgeometry (view->children[i]); - return; - } - - switch (view->gravity) { - case grav_center: - view->xrel = view->xpos + (par->xlen - view->xlen) / 2; - view->yrel = view->ypos + (par->ylen - view->ylen) / 2; - break; - case grav_north: - view->xrel = view->xpos + (par->xlen - view->xlen) / 2; - view->yrel = view->ypos; - break; - case grav_northeast: - view->xrel = par->xlen - view->xpos - view->xlen; - view->yrel = view->ypos; - break; - case grav_east: - view->xrel = par->xlen - view->xpos - view->xlen; - view->yrel = view->ypos + (par->ylen - view->ylen) / 2; - break; - case grav_southeast: - view->xrel = par->xlen - view->xpos - view->xlen; - view->yrel = par->ylen - view->ypos - view->ylen; - break; - case grav_south: - view->xrel = view->xpos + (par->xlen - view->xlen) / 2; - view->yrel = par->ylen - view->ypos - view->ylen; - break; - case grav_southwest: - view->xrel = view->xpos; - view->yrel = par->ylen - view->ypos - view->ylen; - break; - case grav_west: - view->xrel = view->xpos; - view->yrel = view->ypos + (par->ylen - view->ylen) / 2; - break; - case grav_northwest: - view->xrel = view->xpos; - view->yrel = view->ypos; - break; - } - view->xabs = par->xabs + view->xrel; - view->yabs = par->yabs + view->yrel; - if (view->setgeometry) - view->setgeometry (view); - for (i = 0; i < view->num_children; i++) - setgeometry (view->children[i]); -} - -VISIBLE view_t * -view_new (int xp, int yp, int xl, int yl, grav_t grav) -{ - view_t *view = calloc (1, sizeof (view_t)); - view->xpos = xp; - view->ypos = yp; - view->xlen = xl; - view->ylen = yl; - view->gravity = grav; - view->visible = 1; - view->draw = view_draw; - setgeometry (view); - return view; -} - -VISIBLE void -view_insert (view_t *par, view_t *view, int pos) -{ - view->parent = par; - if (pos < 0) - pos = par->num_children + 1 + pos; - if (pos < 0) - pos = 0; - if (pos > par->num_children) - pos = par->num_children; - if (par->num_children == par->max_children) { - par->max_children += 8; - par->children = realloc (par->children, - par->max_children * sizeof (view_t *)); - memset (par->children + par->num_children, 0, - (par->max_children - par->num_children) * sizeof (view_t *)); - } - memmove (par->children + pos + 1, par->children + pos, - (par->num_children - pos) * sizeof (view_t *)); - par->num_children++; - par->children[pos] = view; - setgeometry (view); -} - -VISIBLE void -view_add (view_t *par, view_t *view) -{ - view_insert (par, view, -1); -} - -VISIBLE void -view_remove (view_t *par, view_t *view) -{ - int i; - - for (i = 0; i < par->num_children; i++) { - if (par->children[i] == view) { - memmove (par->children + i, par->children + i + 1, - (par->num_children - i - 1) * sizeof (view_t *)); - par->children [--par->num_children] = 0; - break; - } - } -} - -VISIBLE void -view_delete (view_t *view) -{ - if (view->parent) - view_remove (view->parent, view); - while (view->num_children) - view_delete (view->children[0]); - free (view); -} - -VISIBLE void -view_draw (view_t *view) -{ - int i; - - for (i = 0; i < view->num_children; i++) { - view_t *v = view->children[i]; - if (v->visible && v->draw) - v->draw (v); - } -} - -static void -_resize (view_t *view, int xl, int yl) -{ - int i, xd, yd; - - xd = xl - view->xlen; - yd = yl - view->ylen; - view->xlen = xl; - view->ylen = yl; - for (i = 0; i < view->num_children; i++) { - view_t *v = view->children[i]; - - if (v->resize_x && v->resize_y) { - _resize (v, v->xlen + xd, v->ylen + yd); - } else if (v->resize_x) { - _resize (v, v->xlen + xd, v->ylen); - } else if (v->resize_y) { - _resize (v, v->xlen, v->ylen + yd); - } - } -} - -VISIBLE void -view_resize (view_t *view, int xl, int yl) -{ - _resize (view, xl, yl); - setgeometry (view); -} - -VISIBLE void -view_move (view_t *view, int xp, int yp) -{ - view->xpos = xp; - view->ypos = yp; - setgeometry (view); -} diff --git a/libs/ecs/Makemodule.am b/libs/ecs/Makemodule.am new file mode 100644 index 000000000..141db3b62 --- /dev/null +++ b/libs/ecs/Makemodule.am @@ -0,0 +1,15 @@ +include libs/ecs/test/Makemodule.am + +ecs_deps=libs/util/libQFutil.la + +lib_LTLIBRARIES += libs/ecs/libQFecs.la + +libs_ecs_libQFecs_la_LDFLAGS= $(lib_ldflags) +libs_ecs_libQFecs_la_LIBADD= $(ecs_deps) +libs_ecs_libQFecs_la_DEPENDENCIES= $(ecs_deps) +libs_ecs_libQFecs_la_SOURCES= \ + libs/ecs/component.c \ + libs/ecs/ecs.c \ + libs/ecs/entity.c \ + libs/ecs/hierarchy.c \ + libs/ecs/subpool.c diff --git a/nq/include/chase.h b/libs/ecs/component.c similarity index 74% rename from nq/include/chase.h rename to libs/ecs/component.c index a2d894b9d..c466f6b50 100644 --- a/nq/include/chase.h +++ b/libs/ecs/component.c @@ -1,9 +1,9 @@ /* - chase.h + compoent.c - @description@ + ECS Component management - Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2022 Bill Currke This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -24,14 +24,9 @@ Boston, MA 02111-1307, USA */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif -#ifndef __chase_h -#define __chase_h - -extern struct cvar_s *chase_active; - -void Chase_Init_Cvars (void); -void Chase_Reset (void); -void Chase_Update (void); - -#endif // __chase_h +#define IMPLEMENT_ECS_COMPONENT_Funcs +#include "QF/ecs.h" diff --git a/libs/ecs/ecs.c b/libs/ecs/ecs.c new file mode 100644 index 000000000..3466e4423 --- /dev/null +++ b/libs/ecs/ecs.c @@ -0,0 +1,240 @@ +/* + ecs.c + + Entity Component System + + Copyright (C) 2022 Bill Currke + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/heapsort.h" +#include "QF/sys.h" + +#define IMPLEMENT_ECS_Funcs +#include "QF/ecs.h" + +VISIBLE ecs_registry_t * +ECS_NewRegistry (void) +{ + ecs_registry_t *reg = calloc (1, sizeof (ecs_registry_t)); + reg->components = (componentset_t) DARRAY_STATIC_INIT (32); + reg->next = Ent_Index (nullent); + return reg; +} + +VISIBLE void +ECS_DelRegistry (ecs_registry_t *registry) +{ + if (!registry) { + return; + } + registry->locked = 1; + for (uint32_t i = 0; i < registry->components.size; i++) { + __auto_type comp = ®istry->components.a[i]; + __auto_type pool = ®istry->comp_pools[i]; + Component_DestroyElements (comp, pool->data, 0, pool->count); + } + free (registry->entities); + for (uint32_t i = 0; i < registry->components.size; i++) { + free (registry->comp_pools[i].sparse); + free (registry->comp_pools[i].dense); + free (registry->comp_pools[i].data); + + free (registry->subpools[i].sorted); + free (registry->subpools[i].ranges); + free (registry->subpools[i].rangeids); + } + DARRAY_CLEAR (®istry->components); + free (registry->subpools); + free (registry->comp_pools); + PR_RESDELMAP (registry->hierarchies); + free (registry); +} + +VISIBLE uint32_t +ECS_RegisterComponents (ecs_registry_t *registry, + const component_t *components, uint32_t count) +{ + uint32_t base = registry->components.size; + DARRAY_RESIZE (®istry->components, base + count); + memcpy (registry->components.a + base, components, + count * sizeof (component_t)); + return base; +} + +VISIBLE void +ECS_CreateComponentPools (ecs_registry_t *registry) +{ + uint32_t count = registry->components.size; + registry->comp_pools = calloc (count, sizeof (ecs_pool_t)); + size_t size = registry->max_entities * sizeof (uint32_t); + for (uint32_t i = 0; i < count; i++) { + registry->comp_pools[i].sparse = malloc (size); + memset (registry->comp_pools[i].sparse, nullent, size); + } + registry->subpools = calloc (count, sizeof (ecs_subpool_t)); +} + +typedef struct { + __compar_d_fn_t cmp; + void *arg; + ecs_pool_t *pool; + const component_t *comp; +} ecs_sort_t; + +static int +ecs_compare (const void *a, const void *b, void *arg) +{ + ecs_sort_t *sortctx = arg; + return sortctx->cmp (a, b, sortctx->arg); +} + +static void +swap_uint32 (uint32_t *a, uint32_t *b) +{ + uint32_t t = *a; + *a = *b; + *b = t; +} + +static void +ecs_swap (void *_a, void *_b, void *arg) +{ + ecs_sort_t *sortctx = arg; + size_t size = sortctx->comp->size; + ecs_pool_t *pool = sortctx->pool; + uint32_t *a = _a; + uint32_t *b = _b; + uint32_t a_ind = a - pool->dense; + uint32_t b_ind = b - pool->dense; + uint32_t a_ent_ind = Ent_Index (pool->dense[a_ind]); + uint32_t b_ent_ind = Ent_Index (pool->dense[b_ind]); + __auto_type a_data = (byte *) pool->data + a_ind * size; + __auto_type b_data = (byte *) pool->data + b_ind * size; + Component_SwapElements (sortctx->comp, a_data, b_data); + swap_uint32 (a, b); + swap_uint32 (&pool->sparse[a_ent_ind], &pool->sparse[b_ent_ind]); +} + +VISIBLE void +ECS_SortComponentPoolRange (ecs_registry_t *registry, uint32_t component, + ecs_range_t range, __compar_d_fn_t cmp, void *arg) +{ + if (component >= registry->components.size) { + Sys_Error ("ECS_SortComponentPoolRange: invalid component: %u", + component); + } + ecs_pool_t *pool = ®istry->comp_pools[component]; + if (!pool->count || range.end <= range.start) { + return; + } + uint32_t count = range.end - range.start; + uint32_t *start = pool->dense + range.start; + __auto_type comp = ®istry->components.a[component]; + ecs_sort_t sortctx = { .cmp = cmp, .arg = arg, .pool = pool, .comp = comp }; + heapsort_s (start, count, sizeof (uint32_t), + ecs_compare, ecs_swap, &sortctx); +} + +VISIBLE void +ECS_SortComponentPool (ecs_registry_t *registry, uint32_t component, + __compar_d_fn_t cmp, void *arg) +{ + if (component >= registry->components.size) { + Sys_Error ("ECS_SortComponentPool: invalid component: %u", component); + } + ecs_pool_t *pool = ®istry->comp_pools[component]; + ecs_range_t range = { .start = 0, .end = pool->count }; + ECS_SortComponentPoolRange (registry, component, range, cmp, arg); +} + +VISIBLE uint32_t +ECS_NewEntity (ecs_registry_t *registry) +{ + uint32_t ent; + if (registry->available) { + registry->available--; + uint32_t next = registry->next; + ent = next | Ent_Generation (registry->entities[next]); + registry->next = Ent_Index (registry->entities[next]); + registry->entities[next] = ent; + } else { + if (registry->num_entities == Ent_Index (nullent)) { + Sys_Error ("ECS_NewEntity: out of entities"); + } + if (registry->num_entities == registry->max_entities) { + registry->max_entities += ENT_GROW; + size_t size = registry->max_entities * sizeof (uint32_t); + registry->entities = realloc (registry->entities, size); + for (uint32_t i = 0; i < registry->components.size; i++) { + uint32_t *sparse = registry->comp_pools[i].sparse; + sparse = realloc (sparse, size); + memset (sparse + registry->max_entities - ENT_GROW, nullent, + ENT_GROW * sizeof (uint32_t)); + registry->comp_pools[i].sparse = sparse; + } + } + ent = registry->num_entities++; + // ent starts out with generation 0 + registry->entities[ent] = ent; + } + return ent; +} + +VISIBLE void +ECS_DelEntity (ecs_registry_t *registry, uint32_t ent) +{ + if (!ECS_EntValid (ent, registry)) { + return; + } + if (registry->locked) { + // the registry is being deleted and mass entity and component + // deletions are going on + return; + } + uint32_t next = registry->next | Ent_NextGen (Ent_Generation (ent)); + uint32_t id = Ent_Index (ent); + registry->entities[id] = next; + registry->next = id; + registry->available++; + + for (uint32_t i = 0; i < registry->components.size; i++) { + Ent_RemoveComponent (ent, i, registry); + } +} + +VISIBLE void +ECS_RemoveEntities (ecs_registry_t *registry, uint32_t component) +{ + ecs_pool_t *pool = ®istry->comp_pools[component]; + const component_t *comp = ®istry->components.a[component]; + __auto_type destroy = comp->destroy; + if (destroy) { + byte *data = registry->comp_pools[component].data; + for (uint32_t i = 0; i < pool->count; i++) { + destroy (data + i * comp->size); + } + } + pool->count = 0; +} diff --git a/libs/ecs/entity.c b/libs/ecs/entity.c new file mode 100644 index 000000000..1fd315538 --- /dev/null +++ b/libs/ecs/entity.c @@ -0,0 +1,148 @@ +/* + entity.c + + ECS entity management + + Copyright (C) 2022 Bill Currke + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/heapsort.h" +#include "QF/sys.h" + +#define IMPLEMENT_ECS_ENTITY_Funcs +#include "QF/ecs.h" + +#include "compat.h" + +static void swap_inds (uint32_t *a, uint32_t *b) +{ + uint32_t t = *a; + *a = *b; + *b = t; +} + +VISIBLE void * +Ent_AddComponent (uint32_t ent, uint32_t comp, ecs_registry_t *registry) +{ + ecs_pool_t *pool = ®istry->comp_pools[comp]; + ecs_subpool_t *subpool = ®istry->subpools[comp]; + component_t *c = ®istry->components.a[comp]; + uint32_t id = Ent_Index (ent); + uint32_t ind = pool->sparse[id]; + if (ind >= pool->count || pool->dense[ind] != ent) { + if (pool->count == pool->max_count) { + pool->max_count += COMP_GROW; + pool->dense = realloc (pool->dense, + pool->max_count * sizeof (uint32_t)); + Component_ResizeArray (c, &pool->data, pool->max_count); + } + uint32_t ind = pool->count++; + pool->sparse[id] = ind; + pool->dense[ind] = ent; + uint32_t rind = subpool->num_ranges - subpool->available; + if (rind && c->rangeid) { + uint32_t rangeid = c->rangeid (registry, ent, comp); + uint32_t rangeind = subpool->sorted[Ent_Index (rangeid)]; + while (rind-- > rangeind) { + if (subpool->ranges[rind] == ind) { + subpool->ranges[rind]++; + continue; + } + uint32_t end = subpool->ranges[rind]++; + Component_MoveElements (c, pool->data, ind, end, 1); + swap_inds (&pool->sparse[Ent_Index (pool->dense[end])], + &pool->sparse[Ent_Index (pool->dense[ind])]); + swap_inds (&pool->dense[ind], &pool->dense[end]); + ind = end; + } + } + } + return Ent_GetComponent (ent, comp, registry); +} + +static int +range_cmp (const void *_key, const void *_range, void *_subpool) +{ + const uint32_t *key = _key; + const uint32_t *range = _range; + ecs_subpool_t *subpool = _subpool; + + // range is the first index for the next subpool range + if (*key >= *range) { + return 1; + } + if (range - subpool->ranges > 0) { + return *key >= range[-1] ? 0 : -1; + } + return 0; +} + +static uint32_t * +find_range (ecs_subpool_t *subpool, uint32_t ind) +{ + return bsearch_r (&ind, subpool->ranges, + subpool->num_ranges - subpool->available, + sizeof (uint32_t), range_cmp, subpool); +} + +VISIBLE void +Ent_RemoveComponent (uint32_t ent, uint32_t comp, ecs_registry_t *registry) +{ + uint32_t id = Ent_Index (ent); + ecs_pool_t *pool = ®istry->comp_pools[comp]; + ecs_subpool_t *subpool = ®istry->subpools[comp]; + uint32_t ind = pool->sparse[id]; + component_t *c = ®istry->components.a[comp]; + if (ind < pool->count && pool->dense[ind] == ent) { + uint32_t last = pool->count - 1; + // invalidate the entity for this component to prevent the component + // being double-removed due to deletion of the component resulting + // in the entity being deleted (happens with hierarchies) + pool->dense[ind] = -1; + Component_DestroyElements (c, pool->data, ind, 1); + uint32_t range_count = subpool->num_ranges - subpool->available; + // if ind >= the last range, then it is outside the subpools + if (range_count && ind < subpool->ranges[range_count - 1]) { + uint32_t *range = find_range (subpool, ind); + while ((size_t) (range - subpool->ranges) < range_count) { + uint32_t end = --*range; + range++; + if (ind < end) { + pool->sparse[Ent_Index (pool->dense[end])] = ind; + pool->dense[ind] = pool->dense[end]; + Component_MoveElements (c, pool->data, ind, end, 1); + ind = end; + } + } + } + if (last > ind) { + pool->sparse[Ent_Index (pool->dense[last])] = ind; + pool->dense[ind] = pool->dense[last]; + Component_MoveElements (c, pool->data, ind, last, 1); + } + pool->count--; + pool->sparse[id] = nullent; + } +} diff --git a/libs/ecs/hierarchy.c b/libs/ecs/hierarchy.c new file mode 100644 index 000000000..58328978a --- /dev/null +++ b/libs/ecs/hierarchy.c @@ -0,0 +1,491 @@ +/* + hierarchy.c + + ECS hierarchy handling + + Copyright (C) 2021 Bill Currke + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/sys.h" + +#include "QF/ecs.h" + +static component_t ent_component = { .size = sizeof (uint32_t) }; +static component_t childCount_component = { .size = sizeof (uint32_t) }; +static component_t childIndex_component = { .size = sizeof (uint32_t) }; +static component_t parentIndex_component = { .size = sizeof (uint32_t) }; + +static void +hierarchy_UpdateTransformIndices (hierarchy_t *hierarchy, uint32_t start, + int offset) +{ + ecs_registry_t *reg = hierarchy->reg; + uint32_t href = hierarchy->href_comp; + for (size_t i = start; i < hierarchy->num_objects; i++) { + if (ECS_EntValid (hierarchy->ent[i], reg)) { + hierref_t *ref = Ent_GetComponent (hierarchy->ent[i], href, reg); + ref->index += offset; + } + } +} + +static void +hierarchy_InvalidateReferences (hierarchy_t *hierarchy, uint32_t start, + uint32_t count) +{ + ecs_registry_t *reg = hierarchy->reg; + uint32_t href = hierarchy->href_comp; + for (size_t i = start; count-- > 0; i++) { + if (ECS_EntValid (hierarchy->ent[i], reg)) { + hierref_t *ref = Ent_GetComponent (hierarchy->ent[i], href, reg); + ref->hierarchy = 0; + ref->index = -1; + } + } +} + +static void +hierarchy_UpdateChildIndices (hierarchy_t *hierarchy, uint32_t start, + int offset) +{ + for (size_t i = start; i < hierarchy->num_objects; i++) { + hierarchy->childIndex[i] += offset; + } +} + +static void +hierarchy_UpdateParentIndices (hierarchy_t *hierarchy, uint32_t start, + int offset) +{ + for (size_t i = start; i < hierarchy->num_objects; i++) { + hierarchy->parentIndex[i] += offset; + } +} + +void +Hierarchy_Reserve (hierarchy_t *hierarchy, uint32_t count) +{ + if (hierarchy->num_objects + count > hierarchy->max_objects) { + uint32_t new_max = hierarchy->num_objects + count; + new_max += 15; + new_max &= ~15; + + Component_ResizeArray (&ent_component, + (void **) &hierarchy->ent, new_max); + Component_ResizeArray (&childCount_component, + (void **) &hierarchy->childCount, new_max); + Component_ResizeArray (&childIndex_component, + (void **) &hierarchy->childIndex, new_max); + Component_ResizeArray (&parentIndex_component, + (void **) &hierarchy->parentIndex, new_max); + + if (hierarchy->type) { + for (uint32_t i = 0; i < hierarchy->type->num_components; i++) { + Component_ResizeArray (&hierarchy->type->components[i], + &hierarchy->components[i], new_max); + } + } + hierarchy->max_objects = new_max; + } +} + +static void +hierarchy_open (hierarchy_t *hierarchy, uint32_t index, uint32_t count) +{ + Hierarchy_Reserve (hierarchy, count); + + hierarchy->num_objects += count; + uint32_t dstIndex = index + count; + count = hierarchy->num_objects - index - count; + Component_MoveElements (&ent_component, + hierarchy->ent, dstIndex, index, count); + Component_MoveElements (&childCount_component, + hierarchy->childCount, dstIndex, index, count); + Component_MoveElements (&childIndex_component, + hierarchy->childIndex, dstIndex, index, count); + Component_MoveElements (&parentIndex_component, + hierarchy->parentIndex, dstIndex, index, count); + if (hierarchy->type) { + for (uint32_t i = 0; i < hierarchy->type->num_components; i++) { + Component_MoveElements (&hierarchy->type->components[i], + hierarchy->components[i], + dstIndex, index, count); + } + } +} + +static void +hierarchy_close (hierarchy_t *hierarchy, uint32_t index, uint32_t count) +{ + if (!count) { + return; + } + hierarchy->num_objects -= count; + uint32_t srcIndex = index + count; + count = hierarchy->num_objects - index; + Component_MoveElements (&ent_component, + hierarchy->ent, index, srcIndex, count); + Component_MoveElements (&childCount_component, + hierarchy->childCount, index, srcIndex, count); + Component_MoveElements (&childIndex_component, + hierarchy->childIndex, index, srcIndex, count); + Component_MoveElements (&parentIndex_component, + hierarchy->parentIndex, index, srcIndex, count); + if (hierarchy->type) { + for (uint32_t i = 0; i < hierarchy->type->num_components; i++) { + Component_MoveElements (&hierarchy->type->components[i], + hierarchy->components[i], + index, srcIndex, count); + } + } +} + +static void +hierarchy_move (hierarchy_t *dst, const hierarchy_t *src, + uint32_t dstIndex, uint32_t srcIndex, uint32_t count) +{ + ecs_registry_t *reg = dst->reg; + uint32_t href = dst->href_comp; + Component_CopyElements (&ent_component, + dst->ent, dstIndex, + src->ent, srcIndex, count); + // Actually move (as in C++ move semantics) source hierarchy object + // references so that their indices do not get updated when the objects + // are removed from the source hierarchy + memset (&src->ent[srcIndex], nullent, count * sizeof(dst->ent[0])); + + for (uint32_t i = 0; i < count; i++) { + if (dst->ent[dstIndex + i] != nullent) { + uint32_t ent = dst->ent[dstIndex + i]; + hierref_t *ref = Ent_GetComponent (ent, href, reg); + ref->hierarchy = dst; + ref->index = dstIndex + i; + } + } + if (dst->type) { + for (uint32_t i = 0; i < dst->type->num_components; i++) { + Component_CopyElements (&dst->type->components[i], + dst->components[i], dstIndex, + src->components[i], srcIndex, count); + } + } +} + +static void +hierarchy_init (hierarchy_t *dst, uint32_t index, + uint32_t parentIndex, uint32_t childIndex, uint32_t count) +{ + memset (&dst->ent[index], nullent, count * sizeof(uint32_t)); + + for (uint32_t i = 0; i < count; i++) { + dst->parentIndex[index + i] = parentIndex; + dst->childCount[index + i] = 0; + dst->childIndex[index + i] = childIndex; + } + if (dst->type) { + for (uint32_t i = 0; i < dst->type->num_components; i++) { + Component_CreateElements (&dst->type->components[i], + dst->components[i], index, count); + } + } +} + +static uint32_t +hierarchy_insert (hierarchy_t *dst, const hierarchy_t *src, + uint32_t dstParent, uint32_t *srcRoot, uint32_t count) +{ + uint32_t insertIndex; // where the objects will be inserted + uint32_t childIndex; // where the objects' children will inserted + + // The newly added objects are always last children of the parent + // object + insertIndex = dst->childIndex[dstParent] + dst->childCount[dstParent]; + // By design, all of an object's children are in one contiguous block, + // and the blocks of children for each object are ordered by their + // parents. Thus the child index of each object increases monotonically + // for each child index in the array, regardless of the level of the owning + // object (higher levels always come before lower levels). + uint32_t neighbor = insertIndex - 1; // insertIndex never zero + childIndex = dst->childIndex[neighbor] + dst->childCount[neighbor]; + + // Any objects that come after the inserted objects need to have + // thier indices adjusted. + hierarchy_UpdateTransformIndices (dst, insertIndex, count); + // The parent object's child index is not affected, but the child + // indices of all objects immediately after the parent object are. + hierarchy_UpdateChildIndices (dst, dstParent + 1, count); + hierarchy_UpdateParentIndices (dst, childIndex, count); + + // The beginning of the block of children for the new objects was + // computed from the pre-insert indices of the related objects, thus + // the index must be updated by the number of objects being inserted + // (it would have been updated thusly if the insert was done before + // updating the indices of the other objects). + childIndex += count; + + hierarchy_open (dst, insertIndex, count); + if (dst == src && insertIndex <= *srcRoot) { + *srcRoot += count; + } + if (src) { + hierarchy_move (dst, src, insertIndex, *srcRoot, count); + } else { + hierarchy_init (dst, insertIndex, dstParent, childIndex, count); + } + for (uint32_t i = 0; i < count; i++) { + dst->parentIndex[insertIndex + i] = dstParent; + dst->childIndex[insertIndex + i] = childIndex; + dst->childCount[insertIndex + i] = 0; + } + + dst->childCount[dstParent] += count; + return insertIndex; +} + +static void +hierarchy_insert_children (hierarchy_t *dst, const hierarchy_t *src, + uint32_t dstParent, uint32_t *srcRoot) +{ + uint32_t insertIndex; + uint32_t childIndex = src->childIndex[*srcRoot]; + uint32_t childCount = src->childCount[*srcRoot]; + + if (childCount) { + insertIndex = hierarchy_insert (dst, src, dstParent, + &childIndex, childCount); + if (dst == src && insertIndex <= *srcRoot) { + *srcRoot += childCount; + } + for (uint32_t i = 0; i < childCount; i++, childIndex++) { + hierarchy_insert_children (dst, src, insertIndex + i, &childIndex); + } + } +} + +static uint32_t +hierarchy_insertHierarchy (hierarchy_t *dst, const hierarchy_t *src, + uint32_t dstParent, uint32_t *srcRoot) +{ + uint32_t insertIndex; + + if (dstParent == nullent) { + if (dst->num_objects) { + Sys_Error ("attempt to insert root in non-empty hierarchy"); + } + hierarchy_open (dst, 0, 1); + if (src) { + hierarchy_move (dst, src, 0, *srcRoot, 1); + } + dst->parentIndex[0] = nullent; + dst->childIndex[0] = 1; + dst->childCount[0] = 0; + insertIndex = 0; + } else { + if (!dst->num_objects) { + Sys_Error ("attempt to insert non-root in empty hierarchy"); + } + insertIndex = hierarchy_insert (dst, src, dstParent, srcRoot, 1); + } + // if src is null, then inserting a new object which has no children + if (src) { + hierarchy_insert_children (dst, src, insertIndex, srcRoot); + } + return insertIndex; +} + +uint32_t +Hierarchy_InsertHierarchy (hierarchy_t *dst, const hierarchy_t *src, + uint32_t dstParent, uint32_t srcRoot) +{ + return hierarchy_insertHierarchy (dst, src, dstParent, &srcRoot); +} + +static void +hierarchy_remove_children (hierarchy_t *hierarchy, uint32_t index, + int delEntities) +{ + uint32_t childIndex = hierarchy->childIndex[index]; + uint32_t childCount = hierarchy->childCount[index]; + + for (uint32_t i = childCount; i-- > 0; ) { + hierarchy_remove_children (hierarchy, childIndex + i, delEntities); + } + if (delEntities) { + hierarchy_InvalidateReferences (hierarchy, childIndex, childCount); + for (uint32_t i = 0; i < childCount; i++) { + ECS_DelEntity (hierarchy->reg, hierarchy->ent[childIndex + i]); + } + } + hierarchy_close (hierarchy, childIndex, childCount); + hierarchy->childCount[index] = 0; + + if (childCount) { + hierarchy_UpdateTransformIndices (hierarchy, childIndex, -childCount); + hierarchy_UpdateChildIndices (hierarchy, index, -childCount); + } + if (childIndex < hierarchy->num_objects) { + hierarchy_UpdateParentIndices (hierarchy, childIndex, -1); + } +} + +void +Hierarchy_RemoveHierarchy (hierarchy_t *hierarchy, uint32_t index, + int delEntities) +{ + uint32_t parentIndex = hierarchy->parentIndex[index]; + + hierarchy_remove_children (hierarchy, index, delEntities); + if (delEntities) { + hierarchy_InvalidateReferences (hierarchy, index, 1); + ECS_DelEntity (hierarchy->reg, hierarchy->ent[index]); + } + hierarchy_close (hierarchy, index, 1); + + hierarchy_UpdateTransformIndices (hierarchy, index, -1); + if (parentIndex != nullent) { + hierarchy_UpdateChildIndices (hierarchy, parentIndex + 1, -1); + hierarchy->childCount[parentIndex] -= 1; + } +} + +hierarchy_t * +Hierarchy_New (ecs_registry_t *reg, uint32_t href_comp, + const hierarchy_type_t *type, int createRoot) +{ + hierarchy_t *hierarchy = PR_RESNEW (reg->hierarchies); + hierarchy->reg = reg; + hierarchy->href_comp = href_comp; + + hierarchy->components = 0; + hierarchy->type = type; + if (type) { + hierarchy->components = calloc (hierarchy->type->num_components, + sizeof (void *)); + } + + if (createRoot) { + hierarchy_open (hierarchy, 0, 1); + hierarchy_init (hierarchy, 0, nullent, 1, 1); + } + + return hierarchy; +} + +void +Hierarchy_Delete (hierarchy_t *hierarchy) +{ + hierarchy_InvalidateReferences (hierarchy, 0, hierarchy->num_objects); + free (hierarchy->ent); + free (hierarchy->childCount); + free (hierarchy->childIndex); + free (hierarchy->parentIndex); + if (hierarchy->type) { + for (uint32_t i = 0; i < hierarchy->type->num_components; i++) { + free (hierarchy->components[i]); + } + free (hierarchy->components); + } + + ecs_registry_t *reg = hierarchy->reg; + PR_RESFREE (reg->hierarchies, hierarchy); +} + +hierarchy_t * +Hierarchy_Copy (ecs_registry_t *dstReg, uint32_t href_comp, + const hierarchy_t *src) +{ + hierarchy_t *dst = Hierarchy_New (dstReg, href_comp, src->type, 0); + size_t count = src->num_objects; + + Hierarchy_Reserve (dst, count); + + for (size_t i = 0; i < count; i++) { + dst->ent[i] = ECS_NewEntity (dstReg); + hierref_t *ref = Ent_AddComponent (dst->ent[i], href_comp, dstReg); + ref->hierarchy = dst; + ref->index = i; + } + + Component_CopyElements (&childCount_component, + dst->childCount, 0, src->childCount, 0, count); + Component_CopyElements (&childIndex_component, + dst->childIndex, 0, src->childIndex, 0, count); + Component_CopyElements (&parentIndex_component, + dst->parentIndex, 0, src->parentIndex, 0, count); + if (dst->type) { + for (uint32_t i = 0; i < dst->type->num_components; i++) { + Component_CopyElements (&dst->type->components[i], + dst->components[i], 0, + src->components[i], 0, count); + } + } + return dst; +} + +hierref_t +Hierarchy_SetParent (hierarchy_t *dst, uint32_t dstParent, + hierarchy_t *src, uint32_t srcRoot) +{ + hierref_t r = {}; + if (dst && dstParent != nullent) { + if (dst->type != src->type) { + Sys_Error ("Can't set parent in hierarchy of different type"); + } + } else { + if (!srcRoot) { + r.hierarchy = src; + r.index = 0; + return r; + } + dst = Hierarchy_New (src->reg, src->href_comp, src->type, 0); + } + r.hierarchy = dst; + r.index = hierarchy_insertHierarchy (dst, src, dstParent, &srcRoot); + Hierarchy_RemoveHierarchy (src, srcRoot, 0); + if (!src->num_objects) { + Hierarchy_Delete (src); + } + return r; +} + +void +Hierref_DestroyComponent (void *href) +{ + hierref_t ref = *(hierref_t *) href; + if (ref.hierarchy) { + ref.hierarchy->ent[ref.index] = -1; + Hierarchy_RemoveHierarchy (ref.hierarchy, ref.index, 1); + if (!ref.hierarchy->num_objects) { + Hierarchy_Delete (ref.hierarchy); + } + } +} diff --git a/libs/ecs/subpool.c b/libs/ecs/subpool.c new file mode 100644 index 000000000..8da0b6f6a --- /dev/null +++ b/libs/ecs/subpool.c @@ -0,0 +1,90 @@ +/* + subpool.c + + ECS subpool management + + Copyright (C) 2022 Bill Currke + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/sys.h" + +#include "QF/ecs.h" + +VISIBLE uint32_t +ECS_NewSubpoolRange (ecs_registry_t *registry, uint32_t component) +{ + ecs_subpool_t *subpool = ®istry->subpools[component]; + uint32_t id; + uint32_t num_ranges = subpool->num_ranges - subpool->available; + if (subpool->available) { + subpool->available--; + uint32_t next = subpool->next; + id = next | Ent_Generation (subpool->rangeids[next]); + subpool->next = Ent_Index (subpool->rangeids[next]); + subpool->rangeids[next] = id; + subpool->sorted[next] = num_ranges; + } else { + if (subpool->num_ranges == Ent_Index (nullent)) { + Sys_Error ("ECS_NewEntity: out of rangeids"); + } + if (subpool->num_ranges == subpool->max_ranges) { + subpool->max_ranges += RANGE_GROW; + size_t idsize = subpool->max_ranges * sizeof (uint32_t); + size_t rsize = subpool->max_ranges * sizeof (ecs_range_t); + subpool->rangeids = realloc (subpool->rangeids, idsize); + subpool->sorted = realloc (subpool->sorted, idsize); + subpool->ranges = realloc (subpool->ranges, rsize); + } + id = subpool->num_ranges++; + // id starts out with generation 0 + subpool->rangeids[id] = id; + subpool->sorted[id] = num_ranges; + } + uint32_t end = 0; + if (num_ranges) { + end = subpool->ranges[num_ranges - 1]; + } + subpool->ranges[subpool->sorted[Ent_Index (id)]] = end; + return id; +} + +VISIBLE void +ECS_DelSubpoolRange (ecs_registry_t *registry, uint32_t component, uint32_t id) +{ + ecs_subpool_t *subpool = ®istry->subpools[component]; + uint32_t next = subpool->next | Ent_NextGen (Ent_Generation (id)); + uint32_t ind = Ent_Index (id); + uint32_t count = subpool->num_ranges - subpool->available; + subpool->rangeids[ind] = next; + subpool->next = ind; + subpool->available++; + memmove (subpool->ranges + ind, subpool->ranges + ind + 1, + (count - 1 - ind) * sizeof (ecs_range_t)); + for (uint32_t i = 0; i < count; i++) { + if (subpool->sorted[i] > ind) { + subpool->sorted[i]--; + } + } +} diff --git a/libs/ecs/test/Makemodule.am b/libs/ecs/test/Makemodule.am new file mode 100644 index 000000000..ab574491c --- /dev/null +++ b/libs/ecs/test/Makemodule.am @@ -0,0 +1,41 @@ +libs_ecs_tests = \ + libs/ecs/test/test-components \ + libs/ecs/test/test-hierarchy \ + libs/ecs/test/test-registry \ + libs/ecs/test/test-subpools + +TESTS += $(libs_ecs_tests) + +check_PROGRAMS += $(libs_ecs_tests) + +libs_ecs_test_libs= \ + libs/ecs/libQFecs.la \ + libs/util/libQFutil.la + +libs_ecs_test_test_components_SOURCES= \ + libs/ecs/test/test-components.c +libs_ecs_test_test_components_LDADD= \ + $(libs_ecs_test_libs) +libs_ecs_test_test_components_DEPENDENCIES= \ + $(libs_ecs_test_libs) + +libs_ecs_test_test_hierarchy_SOURCES= \ + libs/ecs/test/test-hierarchy.c +libs_ecs_test_test_hierarchy_LDADD= \ + $(libs_ecs_test_libs) +libs_ecs_test_test_hierarchy_DEPENDENCIES= \ + $(libs_ecs_test_libs) + +libs_ecs_test_test_registry_SOURCES= \ + libs/ecs/test/test-registry.c +libs_ecs_test_test_registry_LDADD= \ + $(libs_ecs_test_libs) +libs_ecs_test_test_registry_DEPENDENCIES= \ + $(libs_ecs_test_libs) + +libs_ecs_test_test_subpools_SOURCES= \ + libs/ecs/test/test-subpools.c +libs_ecs_test_test_subpools_LDADD= \ + $(libs_ecs_test_libs) +libs_ecs_test_test_subpools_DEPENDENCIES= \ + $(libs_ecs_test_libs) diff --git a/libs/ecs/test/test-components.c b/libs/ecs/test/test-components.c new file mode 100644 index 000000000..1ff8dc88a --- /dev/null +++ b/libs/ecs/test/test-components.c @@ -0,0 +1,135 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "QF/simd/types.h" +#include "QF/mathlib.h" +#include "QF/ecs.h" + +enum test_components { + test_position, + test_scale, + test_rotation, + + test_num_components +}; + +static void +create_position (void *data) +{ + vec4f_t *pos = data; + *pos = (vec4f_t) { 0, 0, 0, 1 }; +} + +static void +create_scale (void *data) +{ + vec_t *scale = data; + VectorSet (1, 1, 1, scale); +} + +static void +create_rotation (void *data) +{ + vec4f_t *rot = data; + *rot = (vec4f_t) { 0, 0, 0, 1 }; +} + +static const component_t test_components[] = { + [test_position] = { + .size = sizeof (vec4f_t), + .create = create_position, + .name = "position", + }, + [test_scale] = { + .size = sizeof (vec3_t), + .create = create_scale, + .name = "scale", + }, + [test_rotation] = { + .size = sizeof (vec4f_t), + .create = create_rotation, + .name = "rotation", + }, +}; + +static int +check_ent_components (const uint32_t *ents, uint32_t count, uint32_t comp, + ecs_registry_t *reg) +{ + ecs_pool_t *pool = ®->comp_pools[comp]; + const component_t *component = ®->components.a[comp]; + if (pool->count != count) { + printf ("%s pool has wrong object count: %d %d\n", component->name, + pool->count, count); + return 0; + } + uint32_t num_entities = 0; + for (uint32_t i = 0; i < reg->max_entities; i++) { + if (pool->sparse[i] == nullent) { + continue; + } + if (pool->sparse[i] < pool->count) { + num_entities++; + continue; + } + printf ("invalid index in sparse array of %s: %d %d\n", component->name, + pool->sparse[i], pool->count); + return 0; + } + if (num_entities != count) { + printf ("%s sparse has wrong entity count: %d %d\n", component->name, + num_entities, count); + return 0; + } + + for (uint32_t i = 0; i < count; i++) { + if (pool->sparse[Ent_Index (ents[i])] == nullent) { + printf ("ent not in %s sparse: %x\n", component->name, ents[i]); + return 0; + } + if (pool->dense[pool->sparse[Ent_Index (ents[i])]] != ents[i]) { + printf ("indexed %s dense does have ent: %x %x\n", component->name, + pool->dense[pool->sparse[Ent_Index (ents[i])]], ents[i]); + return 0; + } + } + return 1; +} + +int +main (void) +{ + ecs_registry_t *reg = ECS_NewRegistry (); + ECS_RegisterComponents (reg, test_components, test_num_components); + ECS_CreateComponentPools (reg); + + uint32_t enta = ECS_NewEntity (reg); + uint32_t entb = ECS_NewEntity (reg); + uint32_t entc = ECS_NewEntity (reg); + Ent_AddComponent (enta, test_position, reg); + Ent_AddComponent (entb, test_position, reg); + Ent_AddComponent (entc, test_position, reg); + Ent_AddComponent (enta, test_rotation, reg); + Ent_AddComponent (entb, test_rotation, reg); + Ent_AddComponent (enta, test_scale, reg); + + if (!check_ent_components ((uint32_t[]){enta, entb, entc}, 3, + test_position, reg)) { + return 1; + } + if (!check_ent_components ((uint32_t[]){enta, entb}, 2, + test_rotation, reg)) { + return 1; + } + if (!check_ent_components ((uint32_t[]){enta}, 1, test_scale, reg)) { + return 1; + } + + ECS_DelRegistry (reg); + return 0; +} diff --git a/libs/ecs/test/test-hierarchy.c b/libs/ecs/test/test-hierarchy.c new file mode 100644 index 000000000..3d9be814a --- /dev/null +++ b/libs/ecs/test/test-hierarchy.c @@ -0,0 +1,902 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "QF/ecs.h" + +enum { + test_href, + test_name, + test_highlight, + + test_num_components +}; + +static const component_t test_components[] = { + [test_href] = { + .size = sizeof (hierref_t), + .create = 0,//create_href, + .name = "href", + .destroy = Hierref_DestroyComponent, + }, + [test_name] = { + .size = sizeof (const char *), + .name = "name", + }, + [test_highlight] = { + .size = sizeof (byte), + .name = "highlight", + }, +}; + +ecs_registry_t *test_reg; + +#define DFL "\e[39;49m" +#define BLK "\e[30;40m" +#define RED "\e[31;40m" +#define GRN "\e[32;40m" +#define ONG "\e[33;40m" +#define BLU "\e[34;40m" +#define MAG "\e[35;40m" +#define CYN "\e[36;40m" +#define WHT "\e[37;40m" + +static int +check_hierarchy_size (hierarchy_t *h, uint32_t size) +{ + if (h->num_objects != size) { + printf ("hierarchy does not have exactly %u transform\n", size); + return 0; + } + ecs_registry_t *reg = h->reg; + for (uint32_t i = 0; i < h->num_objects; i++) { + hierref_t *ref = Ent_GetComponent (h->ent[i], test_href, reg); + char **name = Ent_GetComponent (h->ent[i], test_name, reg);; + if (ref->hierarchy != h) { + printf ("transform %d (%s) does not point to hierarchy\n", + i, *name); + } + } + return 1; +} + +static const char * +ref_index_color (uint32_t i, uint32_t rind) +{ + return rind != i ? RED : DFL; +} + +static const char * +parent_index_color (hierarchy_t *h, uint32_t i) +{ + if (!i && h->parentIndex[i] == nullent) { + return GRN; + } + if (h->parentIndex[i] >= i) { + return RED; + } + uint32_t ci = h->childIndex[h->parentIndex[i]]; + uint32_t cc = h->childCount[h->parentIndex[i]]; + if (i < ci || i >= ci + cc) { + return ONG; + } + return DFL; +} + +static const char * +child_index_color (hierarchy_t *h, uint32_t i) +{ + if (h->childIndex[i] > h->num_objects + || h->childCount[i] > h->num_objects + || h->childIndex[i] + h->childCount[i] > h->num_objects) { + return RED; + } + if (h->childIndex[i] <= i) { + return ONG; + } + return DFL; +} + +static const char * +child_count_color (hierarchy_t *h, uint32_t i) +{ + if (h->childIndex[i] > h->num_objects + || h->childCount[i] > h->num_objects + || h->childIndex[i] + h->childCount[i] > h->num_objects) { + return RED; + } + return DFL; +} + +static const char * +entity_color (hierarchy_t *h, uint32_t i) +{ + return h->ent[i] == nullent ? MAG : DFL; +} + +static const char * +highlight_color (hierarchy_t *h, uint32_t i) +{ + uint32_t ent = h->ent[i]; + if (ECS_EntValid (ent, test_reg) + && Ent_HasComponent (ent, test_highlight, test_reg)) { + static char color_str[] = "\e[3.;4.m"; + byte *color = Ent_GetComponent (ent, test_highlight, test_reg); + if (*color) { + byte fg = *color & 0x0f; + byte bg = *color >> 4; + color_str[3] = fg < 8 ? '0' + fg : '9'; + color_str[6] = bg < 8 ? '0' + bg : '9'; + return color_str; + } + } + return ""; +} + +static void +dump_hierarchy (hierarchy_t *h) +{ + ecs_registry_t *reg = h->reg; + puts ("in: ri pa ci cc en name"); + for (uint32_t i = 0; i < h->num_objects; i++) { + uint32_t rind = nullent; + static char fake_name[] = ONG "null" DFL; + static char *fake_nameptr = fake_name; + char **name = &fake_nameptr; + if (ECS_EntValid (h->ent[i], reg)) { + hierref_t *ref = Ent_GetComponent (h->ent[i], test_href, reg); + rind = ref->index; + if (Ent_HasComponent (h->ent[i], test_name, reg)) { + name = Ent_GetComponent (h->ent[i], test_name, reg); + } + } + printf ("%2d: %s%2d %s%2d %s%2d %s%2d %s%2d"DFL" %s%s"DFL"\n", i, + ref_index_color (i, rind), rind, + parent_index_color (h, i), h->parentIndex[i], + child_index_color (h, i), h->childIndex[i], + child_count_color (h, i), h->childCount[i], + entity_color (h, i), h->ent[i], + highlight_color (h, i), *name); + } + puts (""); +} + +static void +dump_tree (hierarchy_t *h, uint32_t ind, int level) +{ + if (ind >= h->num_objects) { + printf ("index %d out of bounds (%d)\n", ind, h->num_objects); + return; + } + if (!level) { + puts ("in: pa ci cc en|name"); + } + static char fake_name[] = ONG "null" DFL; + static char *fake_nameptr = fake_name; + char **name = &fake_nameptr; + ecs_registry_t *reg = h->reg; + if (ECS_EntValid (h->ent[ind], reg) + && Ent_HasComponent (h->ent[ind], test_name, reg)) { + name = Ent_GetComponent (h->ent[ind], test_name, reg);; + } + printf ("%2d: %s%2d %s%2d %s%2d %s%2d"DFL"|%*s%s%s"DFL"\n", ind, + parent_index_color (h, ind), h->parentIndex[ind], + child_index_color (h, ind), h->childIndex[ind], + child_count_color (h, ind), h->childCount[ind], + entity_color (h, ind), h->ent[ind], + level * 3, "", highlight_color (h, ind), *name); + + if (h->childIndex[ind] > ind) { + for (uint32_t i = 0; i < h->childCount[ind]; i++) { + if (h->childIndex[ind] + i >= h->num_objects) { + break; + } + dump_tree (h, h->childIndex[ind] + i, level + 1); + } + } + if (!level) { + puts (""); + } +} + +static int +check_indices (uint32_t ent, uint32_t index, uint32_t parentIndex, + uint32_t childIndex, uint32_t childCount) +{ + ecs_registry_t *reg = test_reg; + char **entname = Ent_GetComponent (ent, test_name, reg);; + hierref_t *ref = Ent_GetComponent (ent, test_href, reg); + hierarchy_t *h = ref->hierarchy; + if (ref->index != index) { + char **name = Ent_GetComponent (h->ent[index], test_name, reg);; + printf ("%s/%s index incorrect: expect %u got %u\n", + *entname, *name, + index, ref->index); + return 0; + } + if (h->parentIndex[index] != parentIndex) { + printf ("%s parent index incorrect: expect %u got %u\n", + *entname, parentIndex, h->parentIndex[index]); + return 0; + } + if (h->childIndex[index] != childIndex) { + printf ("%s child index incorrect: expect %u got %u\n", + *entname, childIndex, h->childIndex[index]); + return 0; + } + if (h->childCount[index] != childCount) { + printf ("%s child count incorrect: expect %u got %u\n", + *entname, childCount, h->childCount[index]); + return 0; + } + return 1; +} + +static uint32_t +create_ent (uint32_t parent, const char *name) +{ + uint32_t ent = ECS_NewEntity (test_reg); + Ent_SetComponent (ent, test_name, test_reg, &name); + hierref_t *ref = Ent_AddComponent (ent, test_href, test_reg); + + if (parent != nullent) { + hierref_t *pref = Ent_GetComponent (parent, test_href, test_reg); + ref->hierarchy = pref->hierarchy; + ref->index = Hierarchy_InsertHierarchy (pref->hierarchy, 0, + pref->index, 0); + } else { + ref->hierarchy = Hierarchy_New (test_reg, test_href, 0, 1); + ref->index = 0; + } + ref->hierarchy->ent[ref->index] = ent; + return ent; +} + +static void +highlight_ent (uint32_t ent, byte color) +{ + Ent_SetComponent (ent, test_highlight, test_reg, &color); +} + +static void +set_parent (uint32_t child, uint32_t parent) +{ + if (parent != nullent) { + hierref_t *pref = Ent_GetComponent (parent, test_href, test_reg); + hierref_t *cref = Ent_GetComponent (child, test_href, test_reg); + Hierarchy_SetParent (pref->hierarchy, pref->index, + cref->hierarchy, cref->index); + } else { + hierref_t *cref = Ent_GetComponent (child, test_href, test_reg); + Hierarchy_SetParent (0, nullent, cref->hierarchy, cref->index); + } +} + +static int +test_single_transform (void) +{ + uint32_t ent = create_ent (nullent, "test"); + hierarchy_t *h; + + if (ent == nullent) { + printf ("create_ent returned null\n"); + return 1; + } + hierref_t *ref = Ent_GetComponent (ent, test_href, test_reg); + if (!ref) { + printf ("Ent_GetComponent(test_href) returned null\n"); + return 1; + } + if (!(h = ref->hierarchy)) { + printf ("New entity has no hierarchy\n"); + return 1; + } + if (!check_hierarchy_size (h, 1)) { return 1; } + if (!check_indices (ent, 0, nullent, 1, 0)) { return 1; } + + // Delete the hierarchy directly as setparent isn't fully tested + Hierarchy_Delete (h); + + return 0; +} + +static int +test_parent_child_init (void) +{ + uint32_t parent = create_ent (nullent, "parent"); + uint32_t child = create_ent (parent, "child"); + + hierref_t *pref = Ent_GetComponent (parent, test_href, test_reg); + hierref_t *cref = Ent_GetComponent (child, test_href, test_reg); + if (pref->hierarchy != cref->hierarchy) { + printf ("parent and child transforms have separate hierarchies\n"); + return 1; + } + + hierarchy_t *h = pref->hierarchy; + + if (!check_hierarchy_size (h, 2)) { return 1; } + + if (!check_indices (parent, 0, nullent, 1, 1)) { return 1; } + if (!check_indices (child, 1, 0, 2, 0)) { return 1; } + + // Delete the hierarchy directly as setparent isn't fully tested + Hierarchy_Delete (h); + + return 0; +} + +static int +test_parent_child_setparent (void) +{ + uint32_t parent = create_ent (nullent, "parent"); + uint32_t child = create_ent (nullent, "child"); + + if (!check_indices (parent, 0, nullent, 1, 0)) { return 1; } + if (!check_indices (child, 0, nullent, 1, 0)) { return 1; } + + hierref_t *pref = Ent_GetComponent (parent, test_href, test_reg); + hierref_t *cref = Ent_GetComponent (child, test_href, test_reg); + if (pref->hierarchy == cref->hierarchy) { + printf ("parent and child entities have same hierarchy before" + " set paret\n"); + return 1; + } + + set_parent (child, parent); + + if (pref->hierarchy != cref->hierarchy) { + printf ("parent and child transforms have separate hierarchies\n"); + return 1; + } + + hierarchy_t *h = pref->hierarchy; + + if (!check_hierarchy_size (h, 2)) { return 1; } + + if (!check_indices (parent, 0, nullent, 1, 1)) { return 1; } + if (!check_indices (child, 1, 0, 2, 0)) { return 1; } + + // Delete the hierarchy directly as setparent isn't fully tested + Hierarchy_Delete (h); + + return 0; +} + +static int +test_build_hierarchy (void) +{ + printf ("test_build_hierarchy\n"); + + uint32_t root = create_ent (nullent, "root"); + uint32_t A = create_ent (root, "A"); + uint32_t B = create_ent (root, "B"); + uint32_t C = create_ent (root, "C"); + + hierref_t *ref = Ent_GetComponent (root, test_href, test_reg); + + if (!check_indices (root, 0, nullent, 1, 3)) { return 1; } + if (!check_indices (A, 1, 0, 4, 0)) { return 1; } + if (!check_indices (B, 2, 0, 4, 0)) { return 1; } + if (!check_indices (C, 3, 0, 4, 0)) { return 1; } + + uint32_t B1 = create_ent (B, "B1"); + + if (!check_indices (root, 0, nullent, 1, 3)) { return 1; } + if (!check_indices ( A, 1, 0, 4, 0)) { return 1; } + if (!check_indices ( B, 2, 0, 4, 1)) { return 1; } + if (!check_indices ( C, 3, 0, 5, 0)) { return 1; } + if (!check_indices (B1, 4, 2, 5, 0)) { return 1; } + + uint32_t A1 = create_ent (A, "A1"); + + if (!check_indices (root, 0, nullent, 1, 3)) { return 1; } + if (!check_indices ( A, 1, 0, 4, 1)) { return 1; } + if (!check_indices ( B, 2, 0, 5, 1)) { return 1; } + if (!check_indices ( C, 3, 0, 6, 0)) { return 1; } + if (!check_indices (A1, 4, 1, 6, 0)) { return 1; } + if (!check_indices (B1, 5, 2, 6, 0)) { return 1; } + uint32_t A1a = create_ent (A1, "A1a"); + uint32_t B2 = create_ent (B, "B2"); + uint32_t A2 = create_ent (A, "A2"); + uint32_t B3 = create_ent (B, "B3"); + uint32_t B2a = create_ent (B2, "B2a"); + + if (!check_hierarchy_size (ref->hierarchy, 11)) { return 1; } + + if (!check_indices (root, 0, nullent, 1, 3)) { return 1; } + if (!check_indices ( A, 1, 0, 4, 2)) { return 1; } + if (!check_indices ( B, 2, 0, 6, 3)) { return 1; } + if (!check_indices ( C, 3, 0, 9, 0)) { return 1; } + if (!check_indices ( A1, 4, 1, 9, 1)) { return 1; } + if (!check_indices ( A2, 5, 1, 10, 0)) { return 1; } + if (!check_indices ( B1, 6, 2, 10, 0)) { return 1; } + if (!check_indices ( B2, 7, 2, 10, 1)) { return 1; } + if (!check_indices ( B3, 8, 2, 11, 0)) { return 1; } + if (!check_indices (A1a, 9, 4, 11, 0)) { return 1; } + if (!check_indices (B2a, 10, 7, 11, 0)) { return 1; } + + uint32_t D = create_ent (root, "D"); + + if (!check_hierarchy_size (ref->hierarchy, 12)) { return 1; } + + if (!check_indices (root, 0, nullent, 1, 4)) { return 1; } + if (!check_indices ( A, 1, 0, 5, 2)) { return 1; } + if (!check_indices ( B, 2, 0, 7, 3)) { return 1; } + if (!check_indices ( C, 3, 0, 10, 0)) { return 1; } + if (!check_indices ( D, 4, 0, 10, 0)) { return 1; } + if (!check_indices ( A1, 5, 1, 10, 1)) { return 1; } + if (!check_indices ( A2, 6, 1, 11, 0)) { return 1; } + if (!check_indices ( B1, 7, 2, 11, 0)) { return 1; } + if (!check_indices ( B2, 8, 2, 11, 1)) { return 1; } + if (!check_indices ( B3, 9, 2, 12, 0)) { return 1; } + if (!check_indices (A1a, 10, 5, 12, 0)) { return 1; } + if (!check_indices (B2a, 11, 8, 12, 0)) { return 1; } + + dump_hierarchy (ref->hierarchy); + uint32_t C1 = create_ent (C, "C1"); + dump_hierarchy (ref->hierarchy); + if (!check_hierarchy_size (ref->hierarchy, 13)) { return 1; } + + if (!check_indices (root, 0, nullent, 1, 4)) { return 1; } + if (!check_indices ( A, 1, 0, 5, 2)) { return 1; } + if (!check_indices ( B, 2, 0, 7, 3)) { return 1; } + if (!check_indices ( C, 3, 0, 10, 1)) { return 1; } + if (!check_indices ( D, 4, 0, 11, 0)) { return 1; } + if (!check_indices ( A1, 5, 1, 11, 1)) { return 1; } + if (!check_indices ( A2, 6, 1, 12, 0)) { return 1; } + if (!check_indices ( B1, 7, 2, 12, 0)) { return 1; } + if (!check_indices ( B2, 8, 2, 12, 1)) { return 1; } + if (!check_indices ( B3, 9, 2, 13, 0)) { return 1; } + if (!check_indices ( C1, 10, 3, 13, 0)) { return 1; } + if (!check_indices (A1a, 11, 5, 13, 0)) { return 1; } + if (!check_indices (B2a, 12, 8, 13, 0)) { return 1; } + + // Delete the hierarchy directly as setparent isn't fully tested + Hierarchy_Delete (ref->hierarchy); + + return 0; +} + +static int +test_build_hierarchy2 (void) +{ + printf ("test_build_hierarchy2\n"); + + uint32_t root = create_ent (nullent, "root"); + uint32_t A = create_ent (root, "A"); + uint32_t B = create_ent (root, "B"); + uint32_t C = create_ent (root, "C"); + uint32_t B1 = create_ent (B, "B1"); + uint32_t A1 = create_ent (A, "A1"); + uint32_t A1a = create_ent (A1, "A1a"); + uint32_t B2 = create_ent (B, "B2"); + uint32_t A2 = create_ent (A, "A2"); + uint32_t B3 = create_ent (B, "B3"); + uint32_t B2a = create_ent (B2, "B2a"); + uint32_t D = create_ent (root, "D"); + uint32_t C1 = create_ent (C, "C1"); + + hierref_t *ref = Ent_GetComponent (root, test_href, test_reg); + + if (!check_hierarchy_size (ref->hierarchy, 13)) { return 1; } + + if (!check_indices (root, 0, nullent, 1, 4)) { return 1; } + if (!check_indices ( A, 1, 0, 5, 2)) { return 1; } + if (!check_indices ( B, 2, 0, 7, 3)) { return 1; } + if (!check_indices ( C, 3, 0, 10, 1)) { return 1; } + if (!check_indices ( D, 4, 0, 11, 0)) { return 1; } + if (!check_indices ( A1, 5, 1, 11, 1)) { return 1; } + if (!check_indices ( A2, 6, 1, 12, 0)) { return 1; } + if (!check_indices ( B1, 7, 2, 12, 0)) { return 1; } + if (!check_indices ( B2, 8, 2, 12, 1)) { return 1; } + if (!check_indices ( B3, 9, 2, 13, 0)) { return 1; } + if (!check_indices ( C1, 10, 3, 13, 0)) { return 1; } + if (!check_indices (A1a, 11, 5, 13, 0)) { return 1; } + if (!check_indices (B2a, 12, 8, 13, 0)) { return 1; } + + uint32_t T = create_ent (nullent, "T"); + uint32_t X = create_ent (T, "X"); + uint32_t Y = create_ent (T, "Y"); + uint32_t Z = create_ent (T, "Z"); + uint32_t Y1 = create_ent (Y, "Y1"); + uint32_t X1 = create_ent (X, "X1"); + uint32_t X1a = create_ent (X1, "X1a"); + uint32_t Y2 = create_ent (Y, "Y2"); + uint32_t X2 = create_ent (X, "X2"); + uint32_t Y3 = create_ent (Y, "Y3"); + uint32_t Y2a = create_ent (Y2, "Y2a"); + uint32_t Z1 = create_ent (Z, "Z1"); + + + hierref_t *Tref = Ent_GetComponent (T, test_href, test_reg); + dump_hierarchy (Tref->hierarchy); + if (!check_hierarchy_size (Tref->hierarchy, 12)) { return 1; } + + if (!check_indices ( T, 0, nullent, 1, 3)) { return 1; } + if (!check_indices ( X, 1, 0, 4, 2)) { return 1; } + if (!check_indices ( Y, 2, 0, 6, 3)) { return 1; } + if (!check_indices ( Z, 3, 0, 9, 1)) { return 1; } + if (!check_indices ( X1, 4, 1, 10, 1)) { return 1; } + if (!check_indices ( X2, 5, 1, 11, 0)) { return 1; } + if (!check_indices ( Y1, 6, 2, 11, 0)) { return 1; } + if (!check_indices ( Y2, 7, 2, 11, 1)) { return 1; } + if (!check_indices ( Y3, 8, 2, 12, 0)) { return 1; } + if (!check_indices ( Z1, 9, 3, 12, 0)) { return 1; } + if (!check_indices (X1a, 10, 4, 12, 0)) { return 1; } + if (!check_indices (Y2a, 11, 7, 12, 0)) { return 1; } + + set_parent (T, B); + + dump_hierarchy (ref->hierarchy); + + if (!check_hierarchy_size (ref->hierarchy, 25)) { return 1; } + + if (!check_indices (root, 0, nullent, 1, 4)) { return 1; } + if (!check_indices ( A, 1, 0, 5, 2)) { return 1; } + if (!check_indices ( B, 2, 0, 7, 4)) { return 1; } + if (!check_indices ( C, 3, 0, 11, 1)) { return 1; } + if (!check_indices ( D, 4, 0, 12, 0)) { return 1; } + if (!check_indices ( A1, 5, 1, 12, 1)) { return 1; } + if (!check_indices ( A2, 6, 1, 13, 0)) { return 1; } + if (!check_indices ( B1, 7, 2, 13, 0)) { return 1; } + if (!check_indices ( B2, 8, 2, 13, 1)) { return 1; } + if (!check_indices ( B3, 9, 2, 14, 0)) { return 1; } + if (!check_indices ( T, 10, 2, 14, 3)) { return 1; } + if (!check_indices ( C1, 11, 3, 17, 0)) { return 1; } + if (!check_indices (A1a, 12, 5, 17, 0)) { return 1; } + if (!check_indices (B2a, 13, 8, 17, 0)) { return 1; } + if (!check_indices ( X, 14, 10, 17, 2)) { return 1; } + if (!check_indices ( Y, 15, 10, 19, 3)) { return 1; } + if (!check_indices ( Z, 16, 10, 22, 1)) { return 1; } + if (!check_indices ( X1, 17, 14, 23, 1)) { return 1; } + if (!check_indices ( X2, 18, 14, 24, 0)) { return 1; } + if (!check_indices ( Y1, 19, 15, 24, 0)) { return 1; } + if (!check_indices ( Y2, 20, 15, 24, 1)) { return 1; } + if (!check_indices ( Y3, 21, 15, 25, 0)) { return 1; } + if (!check_indices ( Z1, 22, 16, 25, 0)) { return 1; } + if (!check_indices (X1a, 23, 17, 25, 0)) { return 1; } + if (!check_indices (Y2a, 24, 20, 25, 0)) { return 1; } + + set_parent (Y, nullent); + + dump_hierarchy (ref->hierarchy); + hierref_t *Yref = Ent_GetComponent (Y, test_href, test_reg); + dump_hierarchy (Yref->hierarchy); + if (!check_hierarchy_size (ref->hierarchy, 20)) { return 1; } + if (!check_hierarchy_size (Yref->hierarchy, 5)) { return 1; } + + if (!check_indices (root, 0, nullent, 1, 4)) { return 1; } + if (!check_indices ( A, 1, 0, 5, 2)) { return 1; } + if (!check_indices ( B, 2, 0, 7, 4)) { return 1; } + if (!check_indices ( C, 3, 0, 11, 1)) { return 1; } + if (!check_indices ( D, 4, 0, 12, 0)) { return 1; } + if (!check_indices ( A1, 5, 1, 12, 1)) { return 1; } + if (!check_indices ( A2, 6, 1, 13, 0)) { return 1; } + if (!check_indices ( B1, 7, 2, 13, 0)) { return 1; } + if (!check_indices ( B2, 8, 2, 13, 1)) { return 1; } + if (!check_indices ( B3, 9, 2, 14, 0)) { return 1; } + if (!check_indices ( T, 10, 2, 14, 2)) { return 1; } + if (!check_indices ( C1, 11, 3, 16, 0)) { return 1; } + if (!check_indices (A1a, 12, 5, 16, 0)) { return 1; } + if (!check_indices (B2a, 13, 8, 16, 0)) { return 1; } + if (!check_indices ( X, 14, 10, 16, 2)) { return 1; } + if (!check_indices ( Z, 15, 10, 18, 1)) { return 1; } + if (!check_indices ( X1, 16, 14, 19, 1)) { return 1; } + if (!check_indices ( X2, 17, 14, 20, 0)) { return 1; } + if (!check_indices ( Z1, 18, 15, 20, 0)) { return 1; } + if (!check_indices (X1a, 19, 16, 20, 0)) { return 1; } + + if (!check_indices ( Y, 0, nullent, 1, 3)) { return 1; } + if (!check_indices ( Y1, 1, 0, 4, 0)) { return 1; } + if (!check_indices ( Y2, 2, 0, 4, 1)) { return 1; } + if (!check_indices ( Y3, 3, 0, 5, 0)) { return 1; } + if (!check_indices (Y2a, 4, 2, 5, 0)) { return 1; } + + // Delete the hierarchy directly as setparent isn't fully tested + Hierarchy_Delete (ref->hierarchy); + Hierarchy_Delete (Yref->hierarchy); + + return 0; +} + +static int +test_build_hierarchy3 (void) +{ + printf ("test_build_hierarchy3\n"); + + uint32_t root = create_ent (nullent, "root"); + uint32_t A = create_ent (root, "A"); + uint32_t B = create_ent (root, "B"); + uint32_t C = create_ent (root, "C"); + uint32_t B1 = create_ent (B, "B1"); + uint32_t A1 = create_ent (A, "A1"); + uint32_t A1a = create_ent (A1, "A1a"); + uint32_t B2 = create_ent (B, "B2"); + uint32_t A2 = create_ent (A, "A2"); + uint32_t B3 = create_ent (B, "B3"); + uint32_t B2a = create_ent (B2, "B2a"); + uint32_t D = create_ent (root, "D"); + uint32_t C1 = create_ent (C, "C1"); + + hierref_t *ref = Ent_GetComponent (root, test_href, test_reg); + dump_hierarchy (ref->hierarchy); + dump_tree (ref->hierarchy, 0, 0); + + if (!check_hierarchy_size (ref->hierarchy, 13)) { return 1; } + if (!check_indices (root, 0, nullent, 1, 4)) { return 1; } + if (!check_indices ( A, 1, 0, 5, 2)) { return 1; } + if (!check_indices ( B, 2, 0, 7, 3)) { return 1; } + if (!check_indices ( C, 3, 0, 10, 1)) { return 1; } + if (!check_indices ( D, 4, 0, 11, 0)) { return 1; } + if (!check_indices ( A1, 5, 1, 11, 1)) { return 1; } + if (!check_indices ( A2, 6, 1, 12, 0)) { return 1; } + if (!check_indices ( B1, 7, 2, 12, 0)) { return 1; } + if (!check_indices ( B2, 8, 2, 12, 1)) { return 1; } + if (!check_indices ( B3, 9, 2, 13, 0)) { return 1; } + if (!check_indices ( C1, 10, 3, 13, 0)) { return 1; } + if (!check_indices (A1a, 11, 5, 13, 0)) { return 1; } + if (!check_indices (B2a, 12, 8, 13, 0)) { return 1; } + + set_parent (B2, C1); + dump_hierarchy (ref->hierarchy); + dump_tree (ref->hierarchy, 0, 0); + + if (!check_hierarchy_size (ref->hierarchy, 13)) { return 1; } + + if (!check_indices (root, 0, nullent, 1, 4)) { return 1; } + if (!check_indices ( A, 1, 0, 5, 2)) { return 1; } + if (!check_indices ( B, 2, 0, 7, 2)) { return 1; } + if (!check_indices ( C, 3, 0, 9, 1)) { return 1; } + if (!check_indices ( D, 4, 0, 10, 0)) { return 1; } + if (!check_indices ( A1, 5, 1, 10, 1)) { return 1; } + if (!check_indices ( A2, 6, 1, 11, 0)) { return 1; } + if (!check_indices ( B1, 7, 2, 11, 0)) { return 1; } + if (!check_indices ( B3, 8, 2, 11, 0)) { return 1; } + if (!check_indices ( C1, 9, 3, 11, 1)) { return 1; } + if (!check_indices (A1a, 10, 5, 12, 0)) { return 1; } + if (!check_indices ( B2, 11, 9, 12, 1)) { return 1; } + if (!check_indices (B2a, 12, 11, 13, 0)) { return 1; } + + uint32_t A1b = create_ent (A1, "A1b"); + uint32_t A1c = create_ent (A1, "A1c"); + uint32_t B2a1 = create_ent (B2a, "B2a1"); + uint32_t B2a2 = create_ent (B2a, "B2a2"); + uint32_t B2b = create_ent (B2, "B2b"); + uint32_t B2b1 = create_ent (B2b, "B2b1"); + uint32_t B2b2 = create_ent (B2b, "B2b2"); + + dump_hierarchy (ref->hierarchy); + dump_tree (ref->hierarchy, 0, 0); + + if (!check_hierarchy_size (ref->hierarchy, 20)) { return 1; } + + if (!check_indices (root, 0, nullent, 1, 4)) { return 1; } + if (!check_indices ( A, 1, 0, 5, 2)) { return 1; } + if (!check_indices ( B, 2, 0, 7, 2)) { return 1; } + if (!check_indices ( C, 3, 0, 9, 1)) { return 1; } + if (!check_indices ( D, 4, 0, 10, 0)) { return 1; } + if (!check_indices ( A1, 5, 1, 10, 3)) { return 1; } + if (!check_indices ( A2, 6, 1, 13, 0)) { return 1; } + if (!check_indices ( B1, 7, 2, 13, 0)) { return 1; } + if (!check_indices ( B3, 8, 2, 13, 0)) { return 1; } + if (!check_indices ( C1, 9, 3, 13, 1)) { return 1; } + if (!check_indices (A1a, 10, 5, 14, 0)) { return 1; } + if (!check_indices (A1b, 11, 5, 14, 0)) { return 1; } + if (!check_indices (A1c, 12, 5, 14, 0)) { return 1; } + if (!check_indices ( B2, 13, 9, 14, 2)) { return 1; } + if (!check_indices (B2a, 14, 13, 16, 2)) { return 1; } + if (!check_indices (B2b, 15, 13, 18, 2)) { return 1; } + if (!check_indices (B2a1, 16, 14, 20, 0)) { return 1; } + if (!check_indices (B2a2, 17, 14, 20, 0)) { return 1; } + if (!check_indices (B2b1, 18, 15, 20, 0)) { return 1; } + if (!check_indices (B2b2, 19, 15, 20, 0)) { return 1; } + + set_parent (B2, root); + dump_hierarchy (ref->hierarchy); + dump_tree (ref->hierarchy, 0, 0); + + if (!check_hierarchy_size (ref->hierarchy, 20)) { return 1; } + + if (!check_indices (root, 0, nullent, 1, 5)) { return 1; } + if (!check_indices ( A, 1, 0, 6, 2)) { return 1; } + if (!check_indices ( B, 2, 0, 8, 2)) { return 1; } + if (!check_indices ( C, 3, 0, 10, 1)) { return 1; } + if (!check_indices ( D, 4, 0, 11, 0)) { return 1; } + if (!check_indices ( B2, 5, 0, 11, 2)) { return 1; } + if (!check_indices ( A1, 6, 1, 13, 3)) { return 1; } + if (!check_indices ( A2, 7, 1, 16, 0)) { return 1; } + if (!check_indices ( B1, 8, 2, 16, 0)) { return 1; } + if (!check_indices ( B3, 9, 2, 16, 0)) { return 1; } + if (!check_indices ( C1, 10, 3, 16, 0)) { return 1; } + if (!check_indices (B2a, 11, 5, 16, 2)) { return 1; } + if (!check_indices (B2b, 12, 5, 18, 2)) { return 1; } + if (!check_indices (A1a, 13, 6, 20, 0)) { return 1; } + if (!check_indices (A1b, 14, 6, 20, 0)) { return 1; } + if (!check_indices (A1c, 15, 6, 20, 0)) { return 1; } + if (!check_indices (B2a1, 16, 11, 20, 0)) { return 1; } + if (!check_indices (B2a2, 17, 11, 20, 0)) { return 1; } + if (!check_indices (B2b1, 18, 12, 20, 0)) { return 1; } + if (!check_indices (B2b2, 19, 12, 20, 0)) { return 1; } + + // Delete the hierarchy directly as setparent isn't fully tested + Hierarchy_Delete (ref->hierarchy); + + return 0; +} + +static int +test_build_hierarchy4 (void) +{ + printf ("test_build_hierarchy4\n"); + + uint32_t hud = create_ent (nullent, "hud"); + uint32_t mt = create_ent (hud, "mt"); + uint32_t mt_b = create_ent (mt, "mt_b"); + uint32_t mt_b3 = create_ent (mt_b, "mt_b3"); + uint32_t main = create_ent (hud, "main"); + uint32_t main_mf = create_ent (main, "main_mf"); + uint32_t main_mf_b = create_ent (main_mf, "main_mf_b"); + uint32_t main_mf_b7 = create_ent (main_mf_b, "main_mf_b7"); + uint32_t main_i = create_ent (main, "main_i"); + uint32_t main_i_f = create_ent (main_i, "main_i_f"); + uint32_t main_i_f_b = create_ent (main_i_f, "main_i_f_b"); + uint32_t main_i_f_b4 = create_ent (main_i_f_b, "main_i_f_b4"); + uint32_t main_i_s = create_ent (main_i, "main_i_s"); + uint32_t main_i_s4 = create_ent (main_i_s, "main_i_s4"); + uint32_t main_i_i = create_ent (main_i, "main_i_i"); + uint32_t main_i_i4 = create_ent (main_i_i, "main_i_i4"); + uint32_t main_i_a = create_ent (main_i, "main_i_a"); + uint32_t main_i_a_w = create_ent (main_i_a, "main_i_a_w"); + uint32_t main_i_a_w7 = create_ent (main_i_a_w, "main_i_a_w7"); + uint32_t main_i_a_ma = create_ent (main_i_a, "main_i_a_ma"); + uint32_t main_i_a_ma4 = create_ent (main_i_a_ma, "main_i_a_ma4"); + uint32_t main_sb = create_ent (main, "main_sb"); + uint32_t main_sb_a = create_ent (main_sb, "main_sb_a"); + uint32_t main_sb_a4 = create_ent (main_sb_a, "main_sb_a4"); + uint32_t main_sb_f = create_ent (main_sb, "main_sb_f"); + uint32_t main_sb_h = create_ent (main_sb, "main_sb_h"); + uint32_t main_sb_h3 = create_ent (main_sb_h, "main_sb_h3"); + uint32_t main_sb_A = create_ent (main_sb, "main_sb_A"); + uint32_t main_sb_A4 = create_ent (main_sb_A, "main_sb_A4"); + uint32_t main_t0 = create_ent (main, "main_t0"); + uint32_t main_t1 = create_ent (main, "main_t1"); + uint32_t main_S = create_ent (main, "main_S"); + uint32_t main_S_m = create_ent (main_S, "main_S_m"); + uint32_t main_S_s = create_ent (main_S, "main_S_s"); + uint32_t main_S_t = create_ent (main_S, "main_S_t"); + uint32_t main_S_a = create_ent (main_S, "main_S_a"); + uint32_t main_S_a_n = create_ent (main_S_a, "main_S_a_n"); + + highlight_ent (main_i_a, 0x06); + highlight_ent (main_i_a_w, 0x05); + highlight_ent (main_i_a_ma, 0x05); + highlight_ent (main_i_a_w7, 0x02); + highlight_ent (main_i_a_ma4, 0x02); + + highlight_ent (main_sb_a, 0x03); + highlight_ent (main_sb_h, 0x03); + highlight_ent (main_sb_A, 0x03); + highlight_ent (main_S_a, 0x03); + highlight_ent (main_i_f_b, 0x03); + highlight_ent (main_sb_a4, 0x01); + highlight_ent (main_sb_h3, 0x01); + highlight_ent (main_sb_A4, 0x01); + highlight_ent (main_S_a_n, 0x01); + highlight_ent (main_i_f_b4, 0x01); + + hierref_t *ref = Ent_GetComponent (hud, test_href, test_reg); + dump_hierarchy (ref->hierarchy); + dump_tree (ref->hierarchy, 0, 0); + + if (!check_hierarchy_size (ref->hierarchy, 37)) { return 1; } + + if (!check_indices (hud, 0, nullent, 1, 2)) { return 1; } + if (!check_indices (mt, 1, 0, 3, 1)) { return 1; } + if (!check_indices (main, 2, 0, 4, 6)) { return 1; } + if (!check_indices (mt_b, 3, 1, 10, 1)) { return 1; } + if (!check_indices (main_mf, 4, 2, 11, 1)) { return 1; } + if (!check_indices (main_i, 5, 2, 12, 4)) { return 1; } + if (!check_indices (main_sb, 6, 2, 16, 4)) { return 1; } + if (!check_indices (main_t0, 7, 2, 20, 0)) { return 1; } + if (!check_indices (main_t1, 8, 2, 20, 0)) { return 1; } + if (!check_indices (main_S, 9, 2, 20, 4)) { return 1; } + if (!check_indices (mt_b3, 10, 3, 24, 0)) { return 1; } + if (!check_indices (main_mf_b, 11, 4, 24, 1)) { return 1; } + if (!check_indices (main_i_f, 12, 5, 25, 1)) { return 1; } + if (!check_indices (main_i_s, 13, 5, 26, 1)) { return 1; } + if (!check_indices (main_i_i, 14, 5, 27, 1)) { return 1; } + if (!check_indices (main_i_a, 15, 5, 28, 2)) { return 1; } + if (!check_indices (main_sb_a, 16, 6, 30, 1)) { return 1; } + if (!check_indices (main_sb_f, 17, 6, 31, 0)) { return 1; } + if (!check_indices (main_sb_h, 18, 6, 31, 1)) { return 1; } + if (!check_indices (main_sb_A, 19, 6, 32, 1)) { return 1; } + if (!check_indices (main_S_m, 20, 9, 33, 0)) { return 1; } + if (!check_indices (main_S_s, 21, 9, 33, 0)) { return 1; } + if (!check_indices (main_S_t, 22, 9, 33, 0)) { return 1; } + if (!check_indices (main_S_a, 23, 9, 33, 1)) { return 1; } + if (!check_indices (main_mf_b7, 24, 11, 34, 0)) { return 1; } + if (!check_indices (main_i_f_b, 25, 12, 34, 1)) { return 1; } + if (!check_indices (main_i_s4, 26, 13, 35, 0)) { return 1; } + if (!check_indices (main_i_i4, 27, 14, 35, 0)) { return 1; } + if (!check_indices (main_i_a_w, 28, 15, 35, 1)) { return 1; } + if (!check_indices (main_i_a_ma, 29, 15, 36, 1)) { return 1; } + if (!check_indices (main_sb_a4, 30, 16, 37, 0)) { return 1; } + if (!check_indices (main_sb_h3, 31, 18, 37, 0)) { return 1; } + if (!check_indices (main_sb_A4, 32, 19, 37, 0)) { return 1; } + if (!check_indices (main_S_a_n, 33, 23, 37, 0)) { return 1; } + if (!check_indices (main_i_f_b4, 34, 25, 37, 0)) { return 1; } + if (!check_indices (main_i_a_w7, 35, 28, 37, 0)) { return 1; } + if (!check_indices (main_i_a_ma4, 36, 29, 37, 0)) { return 1; } + + set_parent (main_i_a, hud); + dump_hierarchy (ref->hierarchy); + dump_tree (ref->hierarchy, 0, 0); + + if (!check_indices (hud, 0, nullent, 1, 3)) { return 1; } + if (!check_indices (mt, 1, 0, 4, 1)) { return 1; } + if (!check_indices (main, 2, 0, 5, 6)) { return 1; } + if (!check_indices (main_i_a, 3, 0, 11, 2)) { return 1; } + if (!check_indices (mt_b, 4, 1, 13, 1)) { return 1; } + if (!check_indices (main_mf, 5, 2, 14, 1)) { return 1; } + if (!check_indices (main_i, 6, 2, 15, 3)) { return 1; } + if (!check_indices (main_sb, 7, 2, 18, 4)) { return 1; } + if (!check_indices (main_t0, 8, 2, 22, 0)) { return 1; } + if (!check_indices (main_t1, 9, 2, 22, 0)) { return 1; } + if (!check_indices (main_S, 10, 2, 22, 4)) { return 1; } + if (!check_indices (main_i_a_w, 11, 3, 26, 1)) { return 1; } + if (!check_indices (main_i_a_ma, 12, 3, 27, 1)) { return 1; } + if (!check_indices (mt_b3, 13, 4, 28, 0)) { return 1; } + if (!check_indices (main_mf_b, 14, 5, 28, 1)) { return 1; } + if (!check_indices (main_i_f, 15, 6, 29, 1)) { return 1; } + if (!check_indices (main_i_s, 16, 6, 30, 1)) { return 1; } + if (!check_indices (main_i_i, 17, 6, 31, 1)) { return 1; } + if (!check_indices (main_sb_a, 18, 7, 32, 1)) { return 1; } + if (!check_indices (main_sb_f, 19, 7, 33, 0)) { return 1; } + if (!check_indices (main_sb_h, 20, 7, 33, 1)) { return 1; } + if (!check_indices (main_sb_A, 21, 7, 34, 1)) { return 1; } + if (!check_indices (main_S_m, 22, 10, 35, 0)) { return 1; } + if (!check_indices (main_S_s, 23, 10, 35, 0)) { return 1; } + if (!check_indices (main_S_t, 24, 10, 35, 0)) { return 1; } + if (!check_indices (main_S_a, 25, 10, 35, 1)) { return 1; } + if (!check_indices (main_i_a_w7, 26, 11, 36, 0)) { return 1; } + if (!check_indices (main_i_a_ma4, 27, 12, 36, 0)) { return 1; } + if (!check_indices (main_mf_b7, 28, 14, 36, 0)) { return 1; } + if (!check_indices (main_i_f_b, 29, 15, 36, 1)) { return 1; } + if (!check_indices (main_i_s4, 30, 16, 37, 0)) { return 1; } + if (!check_indices (main_i_i4, 31, 17, 37, 0)) { return 1; } + if (!check_indices (main_sb_a4, 32, 18, 37, 0)) { return 1; } + if (!check_indices (main_sb_h3, 33, 20, 37, 0)) { return 1; } + if (!check_indices (main_sb_A4, 34, 21, 37, 0)) { return 1; } + if (!check_indices (main_S_a_n, 35, 25, 37, 0)) { return 1; } + if (!check_indices (main_i_f_b4, 36, 29, 37, 0)) { return 1; } + + // Delete the hierarchy directly as setparent isn't fully tested + Hierarchy_Delete (ref->hierarchy); + + return 0; +} + +int +main (void) +{ + test_reg = ECS_NewRegistry (); + ECS_RegisterComponents (test_reg, test_components, test_num_components); + ECS_CreateComponentPools (test_reg); + + if (test_single_transform ()) { return 1; } + if (test_parent_child_init ()) { return 1; } + if (test_parent_child_setparent ()) { return 1; } + if (test_build_hierarchy ()) { return 1; } + if (test_build_hierarchy2 ()) { return 1; } + if (test_build_hierarchy3 ()) { return 1; } + if (test_build_hierarchy4 ()) { return 1; } + + ECS_DelRegistry (test_reg); + + return 0; +} diff --git a/libs/ecs/test/test-registry.c b/libs/ecs/test/test-registry.c new file mode 100644 index 000000000..9de0220ca --- /dev/null +++ b/libs/ecs/test/test-registry.c @@ -0,0 +1,151 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "QF/ecs.h" + +static int +test_new_del (void) +{ + ecs_registry_t *reg = ECS_NewRegistry (); + if (!reg) { + printf ("could not create registry\n"); + return 0; + } + if (reg->max_entities && !reg->entities) { + printf ("Non-zero max_entities with null entities pointer\n"); + return 0; + } + if (!reg->max_entities && reg->entities) { + printf ("Zero max_entities with non-null entities pointer\n"); + return 0; + } + if (reg->num_entities > reg->max_entities) { + printf ("num_entities > max_entities\n"); + return 0; + } + if (reg->num_entities) { + printf ("Fresh registry has entities\n"); + return 0; + } + if (reg->available) { + printf ("Fresh registry has recycled entities\n"); + return 0; + } + ECS_DelRegistry (reg); + return 1; +} + +#define NUM_ENTS 5 + +static int +test_entities (void) +{ + ecs_registry_t *reg = ECS_NewRegistry (); + + uint32_t entities[NUM_ENTS]; + + for (int i = 0; i < NUM_ENTS; i++) { + entities[i] = ECS_NewEntity (reg); + } + if (!reg->max_entities || !reg->entities) { + printf ("Zero max_entities or non-null entities pointer\n"); + return 0; + } + if (reg->available) { + printf ("unexpected recycled entities\n"); + return 0; + } + if (reg->num_entities != NUM_ENTS) { + printf ("wrong number of entities in registry: %d vs %d\n", NUM_ENTS, + reg->num_entities); + return 0; + } + for (int i = 0; i < NUM_ENTS; i++) { + if (Ent_Index (entities[i]) > reg->num_entities) { + printf ("bad entity returned: %d > %d\n", Ent_Index (entities[i]), + reg->num_entities); + return 0; + } + if (Ent_Generation (entities[i])) { + printf ("fresh entity not generation 0\n"); + return 0; + } + for (int j = 0; j < i; j++) { + if (Ent_Index (entities[j]) == Ent_Index (entities[i])) { + printf ("duplicate entity id\n"); + return 0; + } + } + if (reg->entities[Ent_Index (entities[i])] != entities[i]) { + printf ("wrong entity in entities array: %d %d\n", + entities[i], reg->entities[Ent_Index (entities[i])]); + return 0; + } + } + ECS_DelEntity (reg, entities[2]); + ECS_DelEntity (reg, entities[0]); + if (reg->entities[Ent_Index (entities[2])] == entities[2]) { + printf ("deleted entity not deleted: %d %d\n", + entities[2], reg->entities[Ent_Index (entities[2])]); + return 0; + } + if (reg->entities[Ent_Index (entities[0])] == entities[0]) { + printf ("deleted entity not deleted: %d %d\n", + entities[0], reg->entities[Ent_Index (entities[0])]); + return 0; + } + if (reg->available != 2) { + printf ("not 2 available entity for recycling\n"); + return 0; + } + for (uint32_t i = reg->next, c = 0; i != Ent_Index (nullent); + i = Ent_Index (reg->entities[i]), c++) { + if (c >= reg->available || i >= reg->num_entities) { + printf ("invalid deleted entity chain\n"); + return 0; + } + if (Ent_Index (reg->entities[i]) == i) { + printf ("deleted entity points to itself\n"); + return 0; + } + //printf ("%x\n", reg->entities[i]); + } + entities[2] = ECS_NewEntity (reg); + if (reg->available != 1) { + printf ("not 1 available entity for recycling\n"); + return 0; + } + entities[0] = ECS_NewEntity (reg); + if (reg->available != 0) { + printf ("not 0 available entities for recycling\n"); + return 0; + } + if (reg->num_entities != NUM_ENTS) { + printf ("wrong number of entities in registry: %d vs %d\n", NUM_ENTS, + reg->num_entities); + return 0; + } + if (!Ent_Generation (entities[2]) || !Ent_Generation (entities[0])) { + printf ("recycled entity generations not updated\n"); + return 0; + } + //for (int i = 0; i < NUM_ENTS; i++) { + // printf ("%x\n", entities[i]); + //} + + ECS_DelRegistry (reg); + return 1; +} + +int +main (void) +{ + if (!test_new_del ()) { return 1; } + if (!test_entities ()) { return 1; } + return 0; +} diff --git a/libs/ecs/test/test-subpools.c b/libs/ecs/test/test-subpools.c new file mode 100644 index 000000000..9e51d31b7 --- /dev/null +++ b/libs/ecs/test/test-subpools.c @@ -0,0 +1,261 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "QF/simd/types.h" +#include "QF/mathlib.h" +#include "QF/ecs.h" + +enum test_components { + test_subpool, + test_obj, + + test_num_components +}; + +static uint32_t +obj_rangeid (ecs_registry_t *reg, uint32_t ent, uint32_t comp) +{ + uint32_t sp_comp = comp - (test_obj - test_subpool); + return *(uint32_t *) Ent_GetComponent (ent, sp_comp, reg); +} + +static const component_t test_components[] = { + [test_subpool] = { + .size = sizeof (uint32_t), + .name = "subpool", + }, + [test_obj] = { + .size = sizeof (uint32_t), + .name = "obj", + .rangeid = obj_rangeid, + }, +}; + +static void +dump_sp_ids (ecs_registry_t *reg, uint32_t comp) +{ + ecs_pool_t *pool = ®->comp_pools[comp]; + uint32_t *ent = pool->dense; + uint32_t *id = pool->data; + + for (uint32_t i = 0; i < pool->count; i++) { + printf ("ent[%d]: %d, %d\n", i, ent[i], id[i]); + } +} + +static int +check_subpool_ranges (ecs_subpool_t *subpool, uint32_t *expect) +{ + uint32_t count = subpool->num_ranges - subpool->available; + uint32_t *range = subpool->ranges; + + while (count--) { + uint32_t *r = range++; + uint32_t e = *expect++; + printf ("%d: %d %d\n", (int)(r - subpool->ranges), *r, e); + if (*r != e++) { + return 1; + } + } + return 0; +} + +static int +check_obj_comps (ecs_registry_t *reg, uint32_t comp, uint32_t *expect) +{ + ecs_pool_t *pool = ®->comp_pools[comp]; + uint32_t *val = pool->data; + int fail = 0; + + for (uint32_t i = 0; i < pool->count; i++) { + printf ("val[%d]: %d %d\n", i, val[i], expect[i]); + if (val[i] != expect[i]) { + fail = 1; + } + } + return fail; +} + +int +main (void) +{ + ecs_registry_t *reg = ECS_NewRegistry (); + uint32_t base = ECS_RegisterComponents (reg, test_components, + test_num_components); + ECS_CreateComponentPools (reg); + + uint32_t sp1 = ECS_NewSubpoolRange (reg, base + test_obj); + uint32_t sp2 = ECS_NewSubpoolRange (reg, base + test_obj); + uint32_t sp3 = ECS_NewSubpoolRange (reg, base + test_obj); + + printf ("%d %d %d\n", sp1, sp2, sp3); + if (reg->subpools[base + test_subpool].num_ranges != 0 + || reg->subpools[base + test_subpool].available != 0) { + printf ("subpool not 0 count: %d %d\n", + reg->subpools[base + test_subpool].num_ranges, + reg->subpools[base + test_subpool].available); + return 1; + } + if (reg->subpools[base + test_obj].num_ranges != 3 + || reg->subpools[base + test_obj].available != 0) { + printf ("obj not 3 count: %d %d\n", + reg->subpools[base + test_obj].num_ranges, + reg->subpools[base + test_obj].available); + return 1; + } + for (uint32_t i = 0; i < reg->subpools[base + test_obj].num_ranges; i++) { + if (reg->subpools[base + test_obj].ranges[i] != 0) { + printf ("end %d not 0 count: %d\n", i, + reg->subpools[base + test_obj].ranges[i]); + return 1; + } + } + + uint32_t enta = ECS_NewEntity (reg); + uint32_t entb = ECS_NewEntity (reg); + uint32_t entc = ECS_NewEntity (reg); + uint32_t entd = ECS_NewEntity (reg); + uint32_t ente = ECS_NewEntity (reg); + uint32_t entf = ECS_NewEntity (reg); + uint32_t entg = ECS_NewEntity (reg); + uint32_t enth = ECS_NewEntity (reg); + + Ent_SetComponent (enta, base + test_subpool, reg, &sp1); + Ent_SetComponent (entb, base + test_subpool, reg, &sp1); + Ent_SetComponent (entc, base + test_subpool, reg, &sp2); + Ent_SetComponent (entd, base + test_subpool, reg, &sp3); + Ent_SetComponent (ente, base + test_subpool, reg, &sp3); + Ent_SetComponent (entf, base + test_subpool, reg, &sp2); + Ent_SetComponent (entg, base + test_subpool, reg, &sp2); + Ent_SetComponent (enth, base + test_subpool, reg, &sp1); + + dump_sp_ids (reg, base + test_subpool); + if (check_subpool_ranges (®->subpools[base + test_obj], + (uint32_t[]) { 0, 0, 0 })) { + printf ("oops\n"); + return 1; + } + + uint32_t val = 0; + Ent_SetComponent (enta, base + test_obj, reg, &val); val++; + Ent_SetComponent (entb, base + test_obj, reg, &val); val++; + Ent_SetComponent (entc, base + test_obj, reg, &val); val++; + Ent_SetComponent (entd, base + test_obj, reg, &val); val++; + Ent_SetComponent (ente, base + test_obj, reg, &val); val++; + Ent_SetComponent (entf, base + test_obj, reg, &val); val++; + Ent_SetComponent (entg, base + test_obj, reg, &val); val++; + Ent_SetComponent (enth, base + test_obj, reg, &val); val++; + + if (check_subpool_ranges (®->subpools[base + test_obj], + (uint32_t[]) { 3, 6, 8 })) { + printf ("oops\n"); + return 1; + } + if (check_obj_comps (reg, base + test_obj, + (uint32_t[]) { 0, 1, 7, 5, 6, 2, 4, 3 })) { + printf ("oops\n"); + return 1; + } + + Ent_RemoveComponent (entb, base + test_obj, reg); + if (check_subpool_ranges (®->subpools[base + test_obj], + (uint32_t[]) { 2, 5, 7 })) { + printf ("oops\n"); + return 1; + } + if (check_obj_comps (reg, base + test_obj, + (uint32_t[]) { 0, 7, 2, 5, 6, 3, 4 })) { + printf ("oops\n"); + return 1; + } + + Ent_RemoveComponent (entd, base + test_obj, reg); + if (check_subpool_ranges (®->subpools[base + test_obj], + (uint32_t[]) { 2, 5, 6 })) { + printf ("oops\n"); + return 1; + } + if (check_obj_comps (reg, base + test_obj, + (uint32_t[]) { 0, 7, 2, 5, 6, 4 })) { + printf ("oops\n"); + return 1; + } + + Ent_RemoveComponent (ente, base + test_obj, reg); + if (check_subpool_ranges (®->subpools[base + test_obj], + (uint32_t[]) { 2, 5, 5 })) { + printf ("oops\n"); + return 1; + } + if (check_obj_comps (reg, base + test_obj, + (uint32_t[]) { 0, 7, 2, 5, 6 })) { + printf ("oops\n"); + return 1; + } + + Ent_SetComponent (entd, base + test_obj, reg, &val); val++; + Ent_SetComponent (ente, base + test_obj, reg, &val); val++; + if (check_subpool_ranges (®->subpools[base + test_obj], + (uint32_t[]) { 2, 5, 7 })) { + printf ("oops\n"); + return 1; + } + if (check_obj_comps (reg, base + test_obj, + (uint32_t[]) { 0, 7, 2, 5, 6, 8, 9 })) { + printf ("oops\n"); + return 1; + } + + Ent_RemoveComponent (entc, base + test_obj, reg); + Ent_RemoveComponent (entf, base + test_obj, reg); + Ent_RemoveComponent (entg, base + test_obj, reg); + if (check_subpool_ranges (®->subpools[base + test_obj], + (uint32_t[]) { 2, 2, 4 })) { + printf ("oops\n"); + return 1; + } + if (check_obj_comps (reg, base + test_obj, + (uint32_t[]) { 0, 7, 9, 8 })) { + printf ("oops\n"); + return 1; + } + + ECS_DelSubpoolRange (reg, base + test_obj, sp2); + if (check_subpool_ranges (®->subpools[base + test_obj], + (uint32_t[]) { 2, 4 })) { + printf ("oops\n"); + return 1; + } + + sp2 = ECS_NewSubpoolRange (reg, base + test_obj); + printf ("sp2: %d\n", sp2); + if (check_subpool_ranges (®->subpools[base + test_obj], + (uint32_t[]) { 2, 4, 4 })) { + printf ("oops\n"); + return 1; + } + Ent_SetComponent (entc, base + test_subpool, reg, &sp2); + Ent_SetComponent (entf, base + test_subpool, reg, &sp2); + Ent_SetComponent (entg, base + test_subpool, reg, &sp2); + Ent_SetComponent (entc, base + test_obj, reg, &val); val++; + Ent_SetComponent (entf, base + test_obj, reg, &val); val++; + Ent_SetComponent (entg, base + test_obj, reg, &val); val++; + if (check_subpool_ranges (®->subpools[base + test_obj], + (uint32_t[]) { 2, 4, 7})) { + printf ("oops\n"); + return 1; + } + if (check_obj_comps (reg, base + test_obj, + (uint32_t[]) { 0, 7, 9, 8, 10, 11, 12 })) { + printf ("oops\n"); + return 1; + } + + ECS_DelRegistry (reg); + return 0; +} diff --git a/libs/gamecode/Makefile.am b/libs/gamecode/Makefile.am deleted file mode 100644 index 6d31504a8..000000000 --- a/libs/gamecode/Makefile.am +++ /dev/null @@ -1,16 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CFLAGS= @PREFER_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include -lib_ldflags= - -gc_deps=$(top_builddir)/libs/util/libQFutil.la - -noinst_LTLIBRARIES= libQFgamecode.la - -libQFgamecode_la_LDFLAGS= $(lib_ldflags) -libQFgamecode_la_LIBADD= $(gc_deps) -libQFgamecode_la_DEPENDENCIES= $(gc_deps) -libQFgamecode_la_SOURCES= \ - pr_builtins.c pr_edict.c pr_debug.c pr_exec.c pr_load.c pr_parse.c \ - pr_opcode.c pr_resolve.c pr_resource.c pr_strings.c pr_zone.c diff --git a/libs/gamecode/Makemodule.am b/libs/gamecode/Makemodule.am new file mode 100644 index 000000000..748fdb635 --- /dev/null +++ b/libs/gamecode/Makemodule.am @@ -0,0 +1,97 @@ +include libs/gamecode/test/Makemodule.am + +gc_deps=libs/util/libQFutil.la + +noinst_LTLIBRARIES += libs/gamecode/libQFgamecode.la + +libs_gamecode_libQFgamecode_la_LDFLAGS= +libs_gamecode_libQFgamecode_la_LIBADD= $(gc_deps) +libs_gamecode_libQFgamecode_la_DEPENDENCIES=$(gc_deps) +libs_gamecode_libQFgamecode_la_SOURCES= \ + libs/gamecode/pr_builtins.c \ + libs/gamecode/pr_edict.c \ + libs/gamecode/pr_debug.c \ + libs/gamecode/pr_exec.c \ + libs/gamecode/pr_load.c \ + libs/gamecode/pr_opcode.c \ + libs/gamecode/pr_parse.c \ + libs/gamecode/pr_resolve.c \ + libs/gamecode/pr_resource.c \ + libs/gamecode/pr_strings.c \ + libs/gamecode/pr_v6p_opcode.c \ + libs/gamecode/pr_zone.c + +noinst_PYTHON += \ + $(opcodes_py) \ + $(convert_py) \ + ${extend_py} \ + $(hops_py) \ + $(swizzle_py) + +opcodes_py = $(srcdir)/libs/gamecode/opcodes.py +pr_opcode_cinc = $(top_builddir)/libs/gamecode/pr_opcode.cinc +pr_opcode_hinc = $(top_builddir)/include/QF/progs/pr_opcode.hinc +pr_opcode_src = \ + ${pr_opcode_cinc} \ + ${pr_opcode_hinc} +libs/gamecode/pr_opcode.lo: libs/gamecode/pr_opcode.c ${pr_opcode_src} + +convert_py = $(srcdir)/libs/gamecode/convert.py +pr_convert_cinc = $(top_builddir)/libs/gamecode/pr_convert.cinc + +extend_py = $(srcdir)/libs/gamecode/extend.py +pr_extend_cinc = $(top_builddir)/libs/gamecode/pr_extend.cinc + +hops_py = $(srcdir)/libs/gamecode/hops.py +pr_hops_cinc = $(top_builddir)/libs/gamecode/pr_hops.cinc + +swizzle_py = $(srcdir)/libs/gamecode/swizzle.py +pr_swizzle32_cinc = $(top_builddir)/libs/gamecode/pr_swizzle32.cinc +pr_swizzle64_cinc = $(top_builddir)/libs/gamecode/pr_swizzle64.cinc + +BUILT_SOURCES += \ + $(pr_opcode_cinc) \ + $(pr_opcode_hinc) \ + $(pr_convert_cinc) \ + $(pr_extend_cinc) \ + $(pr_hops_cinc) \ + $(pr_swizzle32_cinc) \ + $(pr_swizzle64_cinc) + +CLEANFILES += \ + $(pr_opcode_cinc) \ + $(pr_opcode_hinc) \ + $(pr_convert_cinc) \ + $(pr_extend_cinc) \ + $(pr_hops_cinc) \ + $(pr_swizzle32_cinc) \ + $(pr_swizzle64_cinc) + +$(pr_opcode_cinc): $(opcodes_py) + $(V_PY)$(PYTHON) $(opcodes_py) table > $(pr_opcode_cinc).t && \ + $(am__mv) $(pr_opcode_cinc).t $(pr_opcode_cinc) + +$(pr_opcode_hinc): $(opcodes_py) + $(V_PY) mkdir -p `dirname $(pr_opcode_hinc)` &&\ + $(PYTHON) $(opcodes_py) enum > $(pr_opcode_hinc).t && \ + $(am__mv) $(pr_opcode_hinc).t $(pr_opcode_hinc) + +$(pr_convert_cinc): $(convert_py) + $(V_PY)$(PYTHON) $(convert_py) table > $(pr_convert_cinc).t && \ + $(am__mv) $(pr_convert_cinc).t $(pr_convert_cinc) + +$(pr_extend_cinc): $(extend_py) + $(V_PY)$(PYTHON) $(extend_py) table > $(pr_extend_cinc).t && \ + $(am__mv) $(pr_extend_cinc).t $(pr_extend_cinc) + +$(pr_hops_cinc): $(hops_py) + $(V_PY)$(PYTHON) $(hops_py) table > $(pr_hops_cinc).t && \ + $(am__mv) $(pr_hops_cinc).t $(pr_hops_cinc) + +$(pr_swizzle32_cinc): $(swizzle_py) + $(V_PY)$(PYTHON) $(swizzle_py) case int > $(pr_swizzle32_cinc).t && \ + $(am__mv) $(pr_swizzle32_cinc).t $(pr_swizzle32_cinc) + +$(pr_swizzle64_cinc): $(swizzle_py) + $(V_PY)$(PYTHON) $(swizzle_py) case long > $(pr_swizzle64_cinc).t && \ + $(am__mv) $(pr_swizzle64_cinc).t $(pr_swizzle64_cinc) diff --git a/libs/gamecode/convert.py b/libs/gamecode/convert.py new file mode 100644 index 000000000..b148eb1c2 --- /dev/null +++ b/libs/gamecode/convert.py @@ -0,0 +1,132 @@ +print("""// types are encoded as ubf where: +// u = 0: signed, u = 1: unsigned +// b = 0: 32-bit, b = 1: 64-bit +// f = 0: int, f = 1: float/double +// unsigned float/double is interpreted as bool +// width is ww where: +// ww = 00: 1 component +// ww = 01: 2 components +// ww = 10: 3 components +// ww = 11: 4 components +// full conversion code is wwsssddd where: +// ww = width +// sss = src type +// ddd = dst type +// case values are in octal +""") +types = [ + "int", + "float", + "long", + "double", + "uint", + "int", # 32-bit bool + "ulong", + "long", # 64-bit bool +] +#does not include size (2 or 4, 3 is special) +vec_types = [ + "ivec", + "vec", + "lvec", + "dvec", + "uivec", + "ivec", # 32-bit bool + "ulvec", + "lvec", # 64-bit bool +] +convert_matrix = [ + #i f l d ui b ul B + [0, 1, 1, 1, 0, 3, 1, 3], # i + [1, 0, 1, 1, 1, 3, 1, 3], # f + [1, 1, 0, 1, 1, 3, 0, 3], # l + [1, 1, 1, 0, 1, 3, 1, 3], # d + + [0, 1, 1, 1, 0, 3, 1, 3], # ui + [2, 2, 2, 2, 2, 0, 2, 3], # 32-bit bool + [1, 1, 0, 1, 1, 3, 0, 3], # ul + [2, 2, 2, 2, 2, 3, 2, 0], # 64-bit bool +] + +def case_str(width, src_type, dst_type): + case = (width << 6) | (src_type << 3) | (dst_type) + return f"case {case:04o}:" + +#FIXME look into using gcc's __builtin_convertvector +def cast_str(width, src_type, dst_type): + if width & 1: + return f"(pr_{vec_types[dst_type]}{width+1}_t)" + else: + return f"(pr_{types[dst_type]}_t)" + +def src_str(width, src_type, dst_type): + if width & 1: + return f"OPA({vec_types[src_type]}{width+1})" + else: + return f"OPA({types[src_type]})" + +def dst_str(width, src_type, dst_type): + if width & 1: + return f"OPC({vec_types[dst_type]}{width+1})" + else: + return f"OPC({types[dst_type]})" + +def zero_str(width, src_type): + zeros = "{%s}" % (", ".join(["0"] * (width + 1))) + return f"{cast_str(width, src_type, src_type)} {zeros}" + +def one_str(width, src_type): + ones = "{%s}" % (", ".join(["1"] * (width + 1))) + return f"{cast_str(width, src_type, src_type)} {ones}" + +def expand_str(width, src, pref=""): + src = [f"{pref}{src}[{i}]" for i in range(width + 1)] + return "{%s}" % (", ".join(src)); + +for width in range(4): + for src_type in range(8): + for dst_type in range(8): + case = case_str(width, src_type, dst_type) + cast = cast_str(width, src_type, dst_type) + src = src_str(width, src_type, dst_type) + dst = dst_str(width, src_type, dst_type) + mode = convert_matrix[src_type][dst_type] + if mode == 0: + if dst_type & 2 != src_type & 2: + continue + if dst_type & 2: + src = src_str(width, 2, 2) + dst = dst_str(width, 2, 2) + else: + src = src_str(width, 0, 0) + dst = dst_str(width, 0, 0) + if width == 2: + print(f"{case} VectorCopy(&{src},&{dst}); break;") + else: + print(f"{case} {dst} = {src}; break;") + elif mode == 1: + if width == 0: + print(f"{case} {dst} = {cast} {src}; break;") + elif width == 2: + print(f"{case} VectorCompUop(&{dst},{cast},&{src}); break;") + else: + expand = expand_str(width, src, f"(pr_{types[dst_type]}_t)") + print(f"{case} {dst} = {cast} {expand}; break;") + elif mode == 2: + one = one_str(width, src_type) + if width == 0: + print(f"{case} {dst} = !!{src}; break;") + elif width == 2: + print(f"{case} VectorCompUop(&{dst},!!,&{src}); break;") + else: + expand = expand_str(width, src, "!!") + print(f"{case} {dst} = {cast} {expand}; break;") + elif mode == 3: + zero = zero_str(width, src_type) + if width == 0: + print(f"{case} {dst} = -!!{src}; break;") + elif width == 2: + print(f"{case} VectorCompUop(&{dst},-!!,&{src}); break;") + else: + expand = expand_str(width, src, "-!!") + print(f"{case} {dst} = {cast} {expand}; break;") diff --git a/libs/gamecode/extend.py b/libs/gamecode/extend.py new file mode 100644 index 000000000..99aa9ed81 --- /dev/null +++ b/libs/gamecode/extend.py @@ -0,0 +1,77 @@ +print("""// encoding is teemmm +// t = 0: 32-bit, t = 1: 64-bit +// e = 00: 0 +// e = 01: 1.0 +// e = 10: copy (1-n, 2-4, otherwise 0) +// e = 11: -1.0 +// mmm = 000: 1 -> 2 +// mmm = 001: 1 -> 3 +// mmm = 010: 1 -> 4 +// mmm = 011: 2 -> 3 +// mmm = 100: 2 -> 4 +// mmm = 101: 3 -> 4 +// mmm = 110: reserved +// mmm = 111: reserved +""") + +types = [ + ["ivec2", "ivec3", "ivec4", "ivec3", "ivec4", "ivec4"], + ["lvec2", "lvec3", "lvec4", "lvec3", "lvec4", "lvec4"], +] + +src_types = [ + ["int", "int", "int", "ivec2", "ivec2", "ivec3"], + ["long", "long", "long", "lvec2", "lvec2", "lvec3"], +] + +extend = [ + ["0", "INT32_C(0x3f800000)", "0", "INT32_C(0xbf800000)"], + ["0", "INT64_C(0x3ff0000000000000)", "0", "INT64_C(0xbff0000000000000)"], +] + +def case_str(type, ext, mode): + case = (type << 5) | (ext << 3) | (mode) + return f"case {case:03o}:" + +def dst_str(type, ext, mode): + return f"OPC({types[type][mode]})" + +def cast_str(type, ext, mode): + return f"(pr_{types[type][mode]}_t)" + +def init_str(type, ext, mode): + ext_str = extend[type][ext] + src = f"OPA({src_types[type][mode]})" + if mode == 0: + if ext == 2: + ext_str = src + return f"{src}, {ext_str}" + elif mode == 1: + if ext == 2: + ext_str = src + return f"{src}, {ext_str}, {ext_str}" + elif mode == 2: + if ext == 2: + ext_str = src + return f"{src}, {ext_str}, {ext_str}, {ext_str}" + elif mode == 3: + return f"{src}[0], {src}[1], {ext_str}" + elif mode == 4: + if ext == 2: + return f"{src}[0], {src}[1], {src}[0], {src}[1]" + else: + return f"{src}[0], {src}[1], {ext_str}, {ext_str}" + elif mode == 5: + return f"{src}[0], {src}[1], {src}[2], {ext_str}" + +for type in range(2): + for ext in range(4): + for mode in range(6): # 6, 7 are reserved + case = case_str(type, ext, mode) + dst = dst_str(type, ext, mode) + cast = cast_str(type, ext, mode) + init = init_str(type, ext, mode) + if mode in [1, 3]: + print(f"{case} VectorSet({init}, {dst}); break;"); + else: + print(f"{case} {dst} = {cast} {{ {init} }}; break;"); diff --git a/libs/gamecode/hops.py b/libs/gamecode/hops.py new file mode 100644 index 000000000..634decc47 --- /dev/null +++ b/libs/gamecode/hops.py @@ -0,0 +1,55 @@ +print("""// encoding is tssooo +// t = 0: 32-bit, t = 1: 64-bit +// ss = 00: reserved +// ss = 01: 2 components +// ss = 10: 3 components +// ss = 11: 4 components +// ooo = 000: and +// ooo = 001: or +// ooo = 010: xor +// ooo = 011: add.i +// ooo = 100: nand +// ooo = 101: nor +// ooo = 110: xnor +// ooo = 111: add.f +""") +#for vec3 +types = [ + ["int", "int", "int", "int", "int", "int", "int", "float"], + ["long", "long", "long", "long", "long", "long", "long", "double"] +] +#does not include size (2 or 4, 3 is special) +vec_types = [ + ["ivec", "ivec", "ivec", "ivec", "ivec", "ivec", "ivec", "vec"], + ["lvec", "lvec", "lvec", "lvec", "lvec", "lvec", "lvec", "dvec"] +] +operators = ["&", "|", "^", "+"] + +def case_str(type, width, op): + case = (type << 5) | (width << 3) | (op) + return f"case {case:03o}:" + +def src_str(type, width, op): + if width & 1: + return f"OPA({vec_types[type][op]}{width+1})" + else: + return f"OPA({types[type][op]})" + +def dst_str(type, width, op): + return f"OPC({types[type][op]})" + +def hop_str(type, width, op): + return f"{'~' if op & 4 and op != 7 else ''}OP_hop{width+1}" + +for type in range(2): + for width in range(1, 4): # 0 is reserved + for opcode in range(8): + case = case_str(type, width, opcode) + src = src_str(type, width, opcode) + dst = dst_str(type, width, opcode) + hop = hop_str(type, width, opcode) + op = operators[opcode & 3] + if width == 2: + print(f"{case} {dst} = {hop} (&{src}, {op}); break;") + else: + print(f"{case} {dst} = {hop} ({src}, {op}); break;") diff --git a/libs/gamecode/opcodes.py b/libs/gamecode/opcodes.py new file mode 100644 index 000000000..b9c316a1d --- /dev/null +++ b/libs/gamecode/opcodes.py @@ -0,0 +1,735 @@ +bitmap_txt = """ +0 0000 mmss load +0 0001 mmss store +0 0010 mmss push +0 0011 mmss pop +0 1ccc ttss compare +0 0000 00nn +0 0000 0000 noop +0 0000 0001 adjstk +0 0000 0010 constant +0 1011 otss udivops +0 1111 s0mm load64 +0 1111 s1mm store64 +0 1111 m000 lea2 + +1 0ooo ttss mathops +1 011r tuss shiftops +1 0110 o1oo string +1 1ccc t0ss compare2 +1 1t00 ooss bitops +1 1001 01mm jump +1 1001 11mm call (return specified st->c) +1 1001 1100 return (size in st->c) +1 1010 t1ss scale +1 1010 t100 swizzle +1 1011 tooo vecops +1 1101 01oo move +1 1101 11oo memset +1 1101 c111 statef +1 1110 c1cc branch +1 1110 c111 stated +1 1111 00mm lea +1 1111 01td vecops2 +1 1111 10oo fbitops +1 1111 1100 convert (conversion mode in st->b) +1 1111 1101 with (mode in st->a, value in st->b, reg in st->c) +1 1111 1110 extend +1 1111 1111 hops +""" + +import copy + +address_mode = "ABCD" +address_types = [ + "ev_void, ev_invalid", + "ev_entity, ev_field", + "ev_ptr, ev_short", + "ev_ptr, ev_int", + "ev_void, ev_short", + "ev_void, ev_int", +] +address_widths = [ + [ "1, 0", "1, 1", "1, 0", "1, 1", ], + [ "2, 0", "1, 1", "1, 0", "1, 1", ], + [ "3, 0", "1, 1", "1, 0", "1, 1", ], + [ "4, 0", "1, 1", "1, 0", "1, 1", ], + [ "-1, 0", "1, 1", "1, 0", "1, 1", "-1, 0", "-1, 1"], +] +#store, pop, lea +store_fmt = [ + "%ga", + "%Ga.%Gb(%Ea)", + "*(%Ga + %sb)", + "*(%Ga + %Gb)", + "%ga + %sb", + "%ga + %Gb", +] +# load and push +load_fmt = [ + "*%Ga, %gc", + "%Ga.%Gb(%Ea)", + "*(%Ga + %sb)", + "*(%Ga + %Gb)", +] +branch_fmt = [ + "branch %sa (%Oa)", + "*%Ga", + "%Ga[%sb]", + "%Ga[%Gb]", +] +compare_ccc = [ "eq", "lt", "gt", None, "ne", "ge", "le", None] +type_tt = ['I', 'F', 'L', 'D'] +etype_tt = ["ev_int", "ev_float", "ev_long", "ev_double"] +unsigned_t = ["ev_uint", "ev_ulong"] +float_t = ["ev_float", "ev_double"] + +adjstk_formats = { + "opcode": "OP_ADJSTK", + "mnemonic": "adjstk", + "opname": "adjstk", + "format": "%sa, %sb", + "widths": "0, 0, 0", + "types": "ev_short, ev_short, ev_invalid", +} +bitops_formats = { + "opcode": "OP_{op_bit[oo].upper()}_{bit_type[t]}_{ss+1}", + "mnemonic": "{op_bit[oo]}", + "opname": "{op_bit[oo]}", + "format": "{bit_fmt[oo]}", + "widths": "{ss+1}, { oo < 3 and ss+1 or 0}, {ss+1}", + "types": "{bit_types[t]}, {oo < 3 and bit_types[t] or 'ev_invalid'}, {bit_types[t]}", + "args": { + "op_bit": ["bitand", "bitor", "bitxor", "bitnot"], + "bit_type": ["I", "L"], + "bit_types": ["ev_int", "ev_long"], + "bit_fmt": [ + "%Ga, %Gb, %gc", + "%Ga, %Gb, %gc", + "%Ga, %Gb, %gc", + "%Ga, %gc", + ], + }, +} +branch_formats = { + "opcode": "OP_{op_cond[c*4+cc].upper()}", + "mnemonic": "{op_cond[c*4+cc]}", + "opname": "{op_cond[c*4+cc]}", + "format": "{cond_fmt[c*4+cc]}{branch_fmt[0]}", + "widths": "0, 0, 1", + "types": "ev_short, ev_invalid, ev_int", + "args": { + "op_mode": "ABCD", + "op_cond": ["ifz", "ifb", "ifa", None, + "ifnz", "ifae", "ifbe", None], + "branch_fmt": branch_fmt, + "cond_fmt": ["%Gc ", "%Gc ", "%Gc ", "", "%Gc ", "%Gc ", "%Gc ", ""], + }, +} +call_formats = { + "opcode": "OP_CALL_{op_mode[mm]}", + "mnemonic": "call", + "opname": "call", + "format": "{call_fmt[mm]}", + "widths": "{call_widths[mm]}, -1", + "types": "{call_types[mm]}, ev_void", + "args": { + "op_mode": ".BCD", + "call_fmt": [ + None, # return handled seprately + "%Ga, %gc", + "%Ga[%sb], %gc", + "%Ga[%Gb], %gc", + ], + "call_types": [ + None, + "ev_void, ev_invalid", + "ev_ptr, ev_short", + "ev_ptr, ev_int", + ], + "call_widths": [ None, "1, 0", "1, 0", "1, 1" ] + }, +} +compare_formats = { + "opcode": "OP_{op_cmp[ccc].upper()}_{cmp_type[tt]}_{ss+1}", + "mnemonic": "{op_cmp[ccc]}.{cmp_type[tt]}", + "opname": "{op_cmp[ccc]}", + "widths": "{ss+1}, {ss+1}, {ss+1}", + "types": "{cmp_types[tt]}, {cmp_types[tt]}, ev_int", + "args": { + "op_cmp": compare_ccc, + "cmp_type": type_tt, + "cmp_types": etype_tt, + }, +} +compare2_formats = { + "opcode": "OP_{op_cmp[ccc].upper()}_{cmp_type[t]}_{ss+1}", + "mnemonic": "{op_cmp[ccc]}.{cmp_type[t]}", + "opname": "{op_cmp[ccc]}", + "widths": "{ss+1}, {ss+1}, {ss+1}", + "types": "{cmp_types[t]}, {cmp_types[t]}, ev_int", + "args": { + "op_cmp": compare_ccc, + "cmp_type": ['u', 'U'], + "cmp_types": unsigned_t, + }, +} +constant_formats = { + "opcode": "OP_LDCONST", + "mnemonic": "ldconst", + "opname": "ldconst", + "format": "%sa, %sb, %gc", + "widths": "0, 0, -1", + "types": "ev_short, ev_short, ev_void", +} +convert_formats = { + "opcode": "OP_CONV", + "mnemonic": "conv", + "opname": "conv", + "format": "%Ga %Cb %gc", + "widths": "-1, 0, -1", + "types": "ev_void, ev_short, ev_void", +} +fbitops_formats = { + "opcode": "OP_{op_fbit[oo].upper()}_F", + "mnemonic": "{op_fbit[oo]}.f", + "opname": "{op_fbit[oo]}", + "format": "{fbit_fmt[oo]}", + "widths": "1, 1, 1", + "types": "{fbit_types[0]}, {fbit_types[oo==3]}, {fbit_types[0]}", + "args": { + "op_fbit": ["bitand", "bitor", "bitxor", "bitnot"], + "fbit_types": ["ev_float", "ev_invalid"], + "fbit_fmt": [ + "%Ga, %Gb, %gc", + "%Ga, %Gb, %gc", + "%Ga, %Gb, %gc", + "%Ga, %gc", + ], + }, +} +extend_formats = { + "opcode": "OP_EXTEND", + "mnemonic": "extend", + "opname": "extend", + "format": "%Ga %Hb %gc", + "widths": "-1, 0, -1", + "types": "ev_void, ev_short, ev_void", +} +hops_formats = { + "opcode": "OP_HOPS", + "mnemonic": "hops", + "opname": "hops", + "format": "%Ga %Hb %gc", + "widths": "-1, 0, 1", + "types": "ev_void, ev_short, ev_void", +} +jump_formats = { + "opcode": "OP_JUMP_{op_mode[mm]}", + "mnemonic": "jump", + "opname": "jump", + "format": "{jump_fmt[mm]}", + "widths": "{jump_widths[mm]}, 0", + "types": "{jump_types[mm]}", + "args": { + "op_mode": "ABCD", + "jump_fmt": branch_fmt, + "jump_types": [ + "ev_short, ev_invalid, ev_invalid", + "ev_void, ev_int, ev_invalid", + "ev_ptr, ev_short, ev_invalid", + "ev_ptr, ev_int, ev_invalid", + ], + "jump_widths": [ "0, 0", "1, 1", "1, 0", "1, 1" ] + }, +} +load64_formats = { + "opcode": "OP_LOAD64_{op_mode[mm]}_{s+3}", + "mnemonic": "load64", + "opname": "load64", + "format": "{load_fmt[mm]}, %gc", + "widths": "{load_widths[s+2][mm]}, {s+3}", + "types": "{load_types[mm]}, ev_void", + "args": { + "op_mode": address_mode, + "load_fmt": load_fmt, + "load_types": address_types, + "load_widths": address_widths, + }, +} +lea_formats = { + "opcode": "OP_LEA_{op_mode[mm]}", + "mnemonic": "lea", + "opname": "lea", + "format": "{lea_fmt[mm]}, %gc", + "widths": "{lea_widths[mm]}, 1", + "types": "{lea_types[mm]}, ev_ptr", + "args": { + "op_mode": address_mode, + "lea_fmt": store_fmt, + "lea_types": address_types, + "lea_widths": address_widths[4], + }, +} +lea2_formats = { + "opcode": "OP_LEA_{op_mode[m]}", + "mnemonic": "lea", + "opname": "lea", + "format": "{lea_fmt[m+4]}, %gc", + "widths": "{lea_widths[m+4]}, 1", + "types": "{lea_types[m+4]}, ev_ptr", + "args": { + "op_mode": "EF", + "lea_fmt": store_fmt, + "lea_types": address_types, + "lea_widths": address_widths[4], + }, +} +load_formats = { + "opcode": "OP_LOAD_{op_mode[mm]}_{ss+1}", + "mnemonic": "load", + "opname": "load", + "format": "{load_fmt[mm]}, %gc", + "widths": "{load_widths[ss][mm]}, {ss+1}", + "types": "{load_types[mm]}, ev_void", + "args": { + "op_mode": address_mode, + "load_fmt": load_fmt, + "load_types": address_types, + "load_widths": address_widths, + }, +} +mathops_formats = { + "opcode": "OP_{op_math[ooo].upper()}_{math_type[tt]}_{ss+1}", + "mnemonic": "{op_math[ooo]}.{math_type[tt]}", + "opname": "{op_math[ooo]}", + "widths": "{ss+1}, {ss+1}, {ss+1}", + "types": "{math_types[tt]}, {math_types[tt]}, {math_types[tt]}", + "args": { + "op_math": ["mul", "div", "rem", "mod", "add", "sub", None, None], + "math_type": type_tt, + "math_types": etype_tt, + }, +} +memset_formats = { + "opcode": "OP_MEMSET_{op_memset[oo].upper()}", + "mnemonic": "memset.{op_memset[oo]}", + "opname": "memset{suff_memset[oo]}", + "format": "{memset_fmt[oo]}", + "widths": "{memset_widths[oo]}", + "types": "{memset_types[oo]}", + "args": { + "op_memset": ["i", "p", "pi", None], + "suff_memset": ["", "p", "p", None], + "memset_fmt": ["%Ga, %sb, %gc", "%Ga, %Gb, %Gc", "%Ga, %sb, %Gc", None], + "memset_widths": [ + "1, 0, -1", + "1, 1, 1", + "1, 0, 1", + None, + ], + "memset_types": [ + "ev_int, ev_short, ev_void", + "ev_int, ev_int, ev_ptr", + "ev_int, ev_short, ev_ptr", + ], + }, +} +move_formats = { + "opcode": "OP_MOVE_{op_move[oo].upper()}", + "mnemonic": "move.{op_move[oo]}", + "opname": "move{suff_move[oo]}", + "format": "{move_fmt[oo]}", + "widths": "{move_widths[oo]}", + "types": "{move_types[oo]}", + "args": { + "op_move": ["i", "p", "pi", None], + "suff_move": ["", "p", "p", None], + "move_fmt": ["%Ga, %sb, %gc", "%Ga, %Gb, %Gc", "%Ga, %sb, %Gc", None], + "move_widths": [ + "-1, 0, -1", + "1, 1, 1", + "1, 0, 1", + None, + ], + "move_types": [ + "ev_void, ev_short, ev_void", + "ev_ptr, ev_int, ev_ptr", + "ev_ptr, ev_short, ev_ptr", + ], + }, +} +noop_formats = { + "opcode": "OP_NOP", + "mnemonic": "nop", + "opname": "nop", + "format": "there were plums...", + "widths": "0, 0, 0", + "types": "ev_invalid, ev_invalid, ev_invalid", +} +push_formats = { + "opcode": "OP_PUSH_{op_mode[mm]}_{ss+1}", + "mnemonic": "push", + "opname": "push", + "format": "{push_fmt[mm]}", + "widths": "{ss+1}, 0, 0", + "types": "{push_types[mm]}, ev_invalid", + "args": { + "op_mode": address_mode, + "push_fmt": load_fmt, + "push_types": address_types, + }, +} +pop_formats = { + "opcode": "OP_POP_{op_mode[mm]}_{ss+1}", + "mnemonic": "pop", + "opname": "pop", + "format": "{pop_fmt[mm]}", + "widths": "{ss+1}, 0, 0", + "types": "{pop_types[mm]}, ev_invalid", + "args": { + "op_mode": address_mode, + "pop_fmt": store_fmt, + "pop_types": address_types, + }, +} +scale_formats = { + "opcode": "OP_SCALE_{scale_type[t]}_{ss+1}", + "mnemonic": "scale.{scale_type[t]}", + "opname": "scale", + "widths": "{ss+1}, 1, {ss+1}", + "types": "{scale_types[t]}, {scale_types[t]}, {scale_types[t]}", + "args": { + "scale_type": ['F', 'D'], + "scale_types": float_t, + }, +} +shiftops_formats = { + "opcode": "OP_{mn_shift[u*2+r].upper()}_{shift_type[u*2+t]}_{ss+1}", + "mnemonic": "{mn_shift[u*2+r]}.{shift_type[u*2+t]}", + "opname": "{op_shift[u*2+r]}", + "widths": "{ss+1}, {ss+1}, {ss+1}", + "types": "{shift_types[t][u]}, {shift_types[t][0]}, {shift_types[t][u]}", + "args": { + "mn_shift": ["shl", "asr", "shl", "shr"], + "op_shift": ["shl", "shr", "shl", "shr"], + "shift_type": ['I', 'L', 'u', 'U'], + "shift_types": [ + ["ev_int", "ev_uint"], + ["ev_long", "ev_ulong"], + ], + }, +} +statef_formats = { + "opcode": "OP_STATE_{state[c]}", + "mnemonic": "state.{state[c]}", + "opname": "state", + "format": "{state_fmt[c]}", + "widths": "1, 1, {c}", + "types": "ev_float, ev_func, {state_types[c]}", + "args": { + "state": ["ft", "ftt"], + "state_fmt": ["%Ga, %Gb", "%Ga, %Gb, %Gc"], + "state_types": ["ev_invalid", "ev_float"], + }, +} +stated_formats = { + "opcode": "OP_STATE_{state[c]}", + "mnemonic": "state.{state[c]}", + "opname": "state", + "format": "{state_fmt[c]}", + "widths": "1, 1, {c}", + "types": "ev_int, ev_func, {state_types[c]}", + "args": { + "state": ["dt", "dtt"], + "state_fmt": ["%Ga, %Gb", "%Ga, %Gb, %Gc"], + "state_types": ["ev_invalid", "ev_double"], + }, +} +store_formats = { + "opcode": "OP_STORE_{op_mode[mm]}_{ss+1}", + "mnemonic": "{store_op[mm]}", + "opname": "{store_op[mm]}", + "format": "%Gc, {store_fmt[mm]}", + "widths": "{store_widths[ss][mm]}, {ss+1}", + "types": "{store_types[mm]}, ev_void", + "args": { + "op_mode": address_mode, + "store_fmt": store_fmt, + "store_op": ["assign", "store", "store", "store"], + "store_types": address_types, + "store_widths": address_widths, + }, +} +store64_formats = { + "opcode": "OP_STORE64_{op_mode[mm]}_{s+3}", + "mnemonic": "{store_op[mm]}64", + "opname": "{store_op[mm]}64", + "format": "%Gc, {store_fmt[mm]}", + "widths": "{store_widths[s+2][mm]}, {s+3}", + "types": "{store_types[mm]}, ev_void", + "args": { + "op_mode": address_mode, + "store_fmt": store_fmt, + "store_op": ["assign", "store", "store", "store"], + "store_types": address_types, + "store_widths": address_widths, + }, +} +string_formats = { + "opcode": "OP_{op_str[o*4+oo].upper()}_S", + "mnemonic": "{op_str[o*4+oo]}.s", + "opname": "{op_str[o*4+oo]}", + "format": "{str_fmt[o*4+oo]}", + "widths": "1, {(o*4+oo)<7 and 1 or 0}, 1", + "types": "{str_types[o*4+oo]}", + "args": { + "op_str": ["eq", "lt", "gt", "add", "cmp", "ge", "le", "not"], + "str_fmt": [ + "%Ga, %Gb, %gc", + "%Ga, %Gb, %gc", + "%Ga, %Gb, %gc", + "%Ga, %Gb, %gc", + "%Ga, %Gb, %gc", + "%Ga, %Gb, %gc", + "%Ga, %Gb, %gc", + "%Ga, %gc", + ], + "str_types": [ + "ev_string, ev_string, ev_int", + "ev_string, ev_string, ev_int", + "ev_string, ev_string, ev_int", + "ev_string, ev_string, ev_string", + "ev_string, ev_string, ev_int", + "ev_string, ev_string, ev_int", + "ev_string, ev_string, ev_int", + "ev_string, ev_invalid, ev_int", + ], + }, +} +swizzle_formats = { + "opcode": "OP_SWIZZLE_{swiz_type[t]}", + "mnemonic": "swizzle.{swiz_type[t]}", + "opname": "swizzle", + "format": "%Ga %sb %gc", + "widths": "4, 0, 4", + "types": "{swizzle_types[t]}", + "args": { + "swiz_type": ['F', 'D'], + "swizzle_types": float_t, + }, +} +return_formats = { + "opcode": "OP_RETURN", + "mnemonic": "return", + "opname": "return", + "widths": "-1, -1, 0", # width specified by st->c + "format": "%Mc5", + "types": "ev_void, ev_void, ev_void", +} +udivops_formats = { + "opcode": "OP_{op_udiv[o].upper()}_{udiv_type[t]}_{ss+1}", + "mnemonic": "{op_udiv[o]}.{udiv_type[t]}", + "opname": "{op_udiv[o]}", + "widths": "{ss+1}, {ss+1}, {ss+1}", + "types": "{udiv_types[t]}, {udiv_types[t]}, {udiv_types[t]}", + "args": { + "op_udiv": ["div", "rem"], + "udiv_type": ['u', 'U'], + "udiv_types": ["ev_uint", "ev_ulong"], + }, +} +vecops_formats = { + "opcode": "OP_{op_vop[ooo].upper()}_{vop_type[t]}", + "mnemonic": "{op_vop[ooo]}.{vop_type[t]}", + "opname": "{op_vop[ooo]}", + "widths": "{vec_widths[ooo]}", + "types": "{vec_types[t]}, {vec_types[t]}, {vec_types[t]}", + "args": { + "op_vop": ["cross", "cdot", "vdot", "qdot", + "cmul", "qvmul", "vqmul", "qmul"], + "vop_type": ['F', 'D'], + "vec_widths": [ + "3, 3, 3", + "2, 2, 2", + "3, 3, 3", + "4, 4, 4", + "2, 2, 2", + "4, 3, 3", + "3, 4, 3", + "4, 4, 4", + ], + "vec_types": float_t, + }, +} +vecops2_formats = { + "opcode": "OP_{op_vop[d].upper()}_{vop_type[t]}", + "mnemonic": "{op_vop[d]}.{vop_type[t]}", + "opname": "{op_vop[d]}", + "widths": "4, 4, 4", + "types": "{vec_types[t]}, {vec_types[t]}, {vec_types[t]}", + "args": { + "op_vop": ["qv4mul", "v4qmul"], + "vop_type": ['F', 'D'], + "vec_types": float_t, + }, +} +with_formats = { + "opcode": "OP_WITH", + "mnemonic": "with", + "opname": "with", + "format": "%sa, %sb, %sc", + "widths": "0, -1, 0", + "types": "ev_short, ev_void, ev_short", +} + +group_map = { + "adjstk": adjstk_formats, + "bitops": bitops_formats, + "branch": branch_formats, + "call": call_formats, + "compare": compare_formats, + "compare2": compare2_formats, + "constant": constant_formats, + "convert": convert_formats, + "extend": extend_formats, + "fbitops": fbitops_formats, + "hops": hops_formats, + "jump": jump_formats, + "lea": lea_formats, + "lea2": lea2_formats, + "load": load_formats, + "load64": load64_formats, + "mathops": mathops_formats, + "memset": memset_formats, + "move": move_formats, + "noop": noop_formats, + "push": push_formats, + "pop": pop_formats, + "scale": scale_formats, + "shiftops": shiftops_formats, + "statef": statef_formats, + "stated": stated_formats, + "store": store_formats, + "store64": store64_formats, + "string": string_formats, + "swizzle": swizzle_formats, + "return": return_formats, + "udivops": udivops_formats, + "vecops": vecops_formats, + "vecops2": vecops2_formats, + "with": with_formats, +} + +def parse_bits(bit_string): + bits = [""] + isbit = bit_string[0] in ['0', '1'] + lastbit = bit_string[0] + while bit_string: + bit = bit_string[0] + bit_string = bit_string[1:] + if isbit and bit in ['0', '1']: + bits[-1] = bits[-1] + bit + elif lastbit == bit: + bits[-1] = bits[-1] + bit + else: + bits.append(bit) + lastbit = bit + isbit = bit in ['0', '1'] + return bits + +opcodes = [None] * 512 + +def expand_opcodes(bits, group, num=0): + if not bits: + opcodes[num] = group + return + block = bits[0] + bits = bits[1:] + num <<= len(block) + if block[0] in ['0', '1']: + num |= int(block, 2) + expand_opcodes(bits, group, num) + else: + for n in range(1<bi_next++; } +static void +bi_no_function (progs_t *pr, void *data) +{ + // no need for checking: the /only/ way to get here is via a function + // descriptor with a bad builtin number + dstatement_t *st = pr->pr_statements + pr->pr_xstatement; + dfunction_t *desc = pr->pr_functions + G_FUNCTION (pr, st->a); + const char *bi_name = PR_GetString (pr, desc->name); + int ind = -desc->first_statement; + + PR_RunError (pr, "Bad builtin called: %s = #%d", bi_name, ind); +} + VISIBLE void -PR_RegisterBuiltins (progs_t *pr, builtin_t *builtins) +PR_RegisterBuiltins (progs_t *pr, builtin_t *builtins, void *data) { builtin_t *bi; int count; if (!pr->builtin_hash) { - pr->builtin_hash = Hash_NewTable (1021, builtin_get_key, 0, pr); - pr->builtin_num_hash = Hash_NewTable (1021, 0, 0, pr); + pr->builtin_blocks = malloc (sizeof (biblock_t)); + DARRAY_INIT (pr->builtin_blocks, 16); + pr->builtin_hash = Hash_NewTable (1021, builtin_get_key, 0, pr, + pr->hashctx); + pr->builtin_num_hash = Hash_NewTable (1021, 0, 0, pr, pr->hashctx); Hash_SetHashCompare (pr->builtin_num_hash, builtin_get_hash, builtin_compare); + + bi = malloc (sizeof (builtin_t)); + bi->name = "invalid function"; + bi->proc = bi_no_function; + bi->binum = builtin_next (pr); + pr->bi_no_function = bi; + DARRAY_APPEND (pr->builtin_blocks, bi); + Hash_AddElement (pr->builtin_num_hash, bi); } // count = 1 for terminator for (bi = builtins, count = 1; bi->name; bi++) count++; bi = malloc (count * sizeof (builtin_t)); + DARRAY_APPEND (pr->builtin_blocks, bi); memcpy (bi, builtins, count * sizeof (builtin_t)); builtins = bi; while (builtins->name) { - if (builtins->binum == 0 || builtins->binum >= PR_AUTOBUILTIN) + if (builtins->binum == 0 || builtins->binum >= PR_AUTOBUILTIN) { PR_Error (pr, "bad builtin number: %s = #%d", builtins->name, builtins->binum); + } - if (builtins->binum < 0) + if (builtins->binum < 0) { builtins->binum = builtin_next (pr); + } if ((bi = Hash_Find (pr->builtin_hash, builtins->name)) - || (bi = Hash_FindElement (pr->builtin_num_hash, builtins))) + || (bi = Hash_FindElement (pr->builtin_num_hash, builtins))) { PR_Error (pr, "builtin %s = #%d already defined (%s = #%d)", builtins->name, builtins->binum, bi->name, bi->binum); + } Hash_Add (pr->builtin_hash, builtins); Hash_AddElement (pr->builtin_num_hash, builtins); + builtins->data = data; builtins++; } } +void +PR_Builtins_Shutdown (progs_t *pr) +{ + for (size_t i = 0; i < pr->builtin_blocks->size; i++) { + free (pr->builtin_blocks->a[i]); + } + DARRAY_CLEAR (pr->builtin_blocks); + free (pr->builtin_blocks); + pr->builtin_blocks = 0; + Hash_DelTable (pr->builtin_hash); + pr->builtin_hash = 0; + Hash_DelTable (pr->builtin_num_hash); + pr->builtin_num_hash = 0; + + free (pr->function_table); + pr->function_table = 0; +} + VISIBLE builtin_t * PR_FindBuiltin (progs_t *pr, const char *name) { @@ -142,23 +191,11 @@ PR_FindBuiltinNum (progs_t *pr, pr_int_t num) return (builtin_t *) Hash_FindElement (pr->builtin_num_hash, &bi); } -static void -bi_no_function (progs_t *pr) -{ - // no need for checking: the /only/ way to get here is via a function - // descriptor with a bad builtin number - dstatement_t *st = pr->pr_statements + pr->pr_xstatement; - dfunction_t *desc = pr->pr_functions + G_FUNCTION (pr, st->a); - const char *bi_name = PR_GetString (pr, desc->s_name); - int ind = -desc->first_statement; - - PR_RunError (pr, "Bad builtin called: %s = #%d", bi_name, ind); -} - VISIBLE int PR_RelocateBuiltins (progs_t *pr) { - pr_int_t i, ind; + pr_uint_t i; + pr_int_t ind; int bad = 0; dfunction_t *desc; bfunction_t *func; @@ -168,32 +205,32 @@ PR_RelocateBuiltins (progs_t *pr) if (pr->function_table) free (pr->function_table); - pr->function_table = calloc (pr->progs->numfunctions, + pr->function_table = calloc (pr->progs->functions.count, sizeof (bfunction_t)); - for (i = 1; i < pr->progs->numfunctions; i++) { + for (i = 1; i < pr->progs->functions.count; i++) { desc = pr->pr_functions + i; func = pr->function_table + i; func->first_statement = desc->first_statement; - func->parm_start = desc->parm_start; + func->params_start = desc->params_start; func->locals = desc->locals; - func->numparms = desc->numparms; - memcpy (func->parm_size, desc->parm_size, sizeof (func->parm_size)); + func->numparams = desc->numparams; + memcpy (func->param_size, desc->param_size, sizeof (func->param_size)); func->descriptor = desc; if (desc->first_statement > 0) continue; - bi_name = PR_GetString (pr, desc->s_name); + bi_name = PR_GetString (pr, desc->name); if (!desc->first_statement) { bi = PR_FindBuiltin (pr, bi_name); if (!bi) { Sys_Printf ("PR_RelocateBuiltins: %s: undefined builtin %s\n", pr->progs_name, bi_name); + bi = pr->bi_no_function; bad = 1; - continue; } desc->first_statement = -bi->binum; } @@ -203,17 +240,24 @@ PR_RelocateBuiltins (progs_t *pr) ind = pr->bi_map (pr, ind); bi = PR_FindBuiltinNum (pr, ind); if (!bi || !(proc = bi->proc)) { - Sys_MaskPrintf (SYS_DEV, + Sys_MaskPrintf (SYS_rua_resolve, "WARNING: Bad builtin call number: %s = #%d\n", bi_name, -desc->first_statement); proc = bi_no_function; } - if (!desc->s_name && bi) { - desc->s_name = PR_SetString (pr, bi->name); + if (!desc->name && bi) { + desc->name = PR_SetString (pr, bi->name); Hash_Add (pr->function_hash, &pr->pr_functions[i]); } func->first_statement = desc->first_statement; func->func = proc; + if (bi) { + func->data = bi->data; + } } - return !bad; + if (bad) { + Sys_Printf ("PR_RelocateBuiltins: %s: progs may not work due to " + "unresolved builtins\n", pr->progs_name); + } + return 1; } diff --git a/libs/gamecode/pr_debug.c b/libs/gamecode/pr_debug.c index 602a2cc15..74d1cfd0b 100644 --- a/libs/gamecode/pr_debug.c +++ b/libs/gamecode/pr_debug.c @@ -40,12 +40,14 @@ #include #include #include +#include +#include "QF/fbsearch.h" #include "QF/cvar.h" #include "QF/dstring.h" #include "QF/hash.h" +#include "QF/heapsort.h" #include "QF/mathlib.h" -#include "QF/pr_debug.h" #include "QF/progs.h" #include "QF/qendian.h" #include "QF/quakefs.h" @@ -54,6 +56,10 @@ #include "QF/va.h" #include "QF/zone.h" +#include "QF/progs/pr_debug.h" +#include "QF/progs/pr_type.h" +#include "QF/simd/types.h" + #include "compat.h" typedef struct { @@ -64,17 +70,98 @@ typedef struct { typedef struct { char *name; char *text; + off_t size; line_t *lines; pr_uint_t num_lines; progs_t *pr; } file_t; -cvar_t *pr_debug; -cvar_t *pr_source_path; -static hashtab_t *file_hash; -static char *source_path_string; -static char **source_paths; +typedef struct compunit_s { + const char *file; + pr_compunit_t *unit; +} compunit_t; +typedef struct { + const char *file; + pr_uint_t line; +} func_key_t; + +typedef struct prdeb_resources_s { + progs_t *pr; + dstring_t *string; + dstring_t *dva; + dstring_t *line; + dstring_t *dstr; + va_ctx_t *va; + const char *debugfile; + pr_debug_header_t *debug; + pr_auxfunction_t *auxfunctions; + pr_auxfunction_t **auxfunction_map; + pr_func_t *sorted_functions; + pr_lineno_t *linenos; + pr_def_t *local_defs; + pr_def_t *type_encodings_def; + qfot_type_t void_type; + qfot_type_t *type_encodings[ev_type_count]; + pr_def_t *debug_defs; + pr_type_t *debug_data; + hashtab_t *debug_syms; + hashtab_t *compunits; // by source file + PR_RESMAP (compunit_t) compmap; // handy allocation/freeing + hashtab_t *file_hash; +} prdeb_resources_t; + +typedef struct { + progs_t *pr; + dstring_t *dstr; +} pr_debug_data_t; + +int pr_debug; +static cvar_t pr_debug_cvar = { + .name = "pr_debug", + .description = + "enable progs debugging", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &pr_debug }, +}; +char *pr_source_path; +static cvar_t pr_source_path_cvar = { + .name = "pr_source_path", + .description = + "where to look (within gamedir) for source files", + .default_value = ".", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &pr_source_path }, +}; +static char *source_path_string; +static char **source_paths; + +static void pr_debug_struct_view (qfot_type_t *type, pr_type_t *value, + void *_data); +static void pr_debug_union_view (qfot_type_t *type, pr_type_t *value, + void *_data); +static void pr_debug_enum_view (qfot_type_t *type, pr_type_t *value, + void *_data); +static void pr_debug_array_view (qfot_type_t *type, pr_type_t *value, + void *_data); +static void pr_debug_class_view (qfot_type_t *type, pr_type_t *value, + void *_data); +#define EV_TYPE(t) \ + static void pr_debug_##t##_view (qfot_type_t *type, pr_type_t *value, \ + void *_data); +#include "QF/progs/pr_type_names.h" + +static type_view_t raw_type_view = { + pr_debug_struct_view, + pr_debug_union_view, + pr_debug_enum_view, + pr_debug_array_view, + pr_debug_class_view, +#define EV_TYPE(t) \ + pr_debug_##t##_view, +#include "QF/progs/pr_type_names.h" +}; static const char * file_get_key (const void *_f, void *unused) @@ -94,56 +181,125 @@ file_free (void *_f, void *unused) free (f); } +static const char * +def_get_key (const void *d, void *p) +{ + __auto_type def = (pr_def_t *) d; + __auto_type pr = (progs_t *) p; + return PR_GetString (pr, def->name); +} + +static const char * +compunit_get_key (const void *cu, void *p) +{ + __auto_type compunit = (compunit_t *) cu; + return compunit->file; +} + static void -source_path_f (cvar_t *var) +source_path_f (void *data, const cvar_t *cvar) { int i; char *s; - if (source_path_string) + if (source_path_string) { free (source_path_string); - source_path_string = strdup (var->string); - if (source_paths) + } + source_path_string = strdup (pr_source_path); + if (source_paths) { free (source_paths); - for (i = 2, s = source_path_string; *s; s++) - if (*s == ';') - i++; - source_paths = malloc (i * sizeof (char **)); - source_paths[0] = source_path_string; - for (i = 1, s = source_path_string; *s; s++) + } + // i starts at 2 because an empty path is equivalent to "." and the + // list is null terminated + for (i = 2, s = source_path_string; *s; s++) { if (*s == ';') { - *s++ = 0; - source_paths[i++] = s; + i++; } + } + source_paths = malloc (i * sizeof (char *)); + source_paths[0] = source_path_string; + // i starts at one because the first path is in 0 and any additional + // paths come after, then the null terminator + for (i = 1, s = source_path_string; *s; s++) { + if (*s == ';') { + *s = 0; + source_paths[i++] = s + 1; + } + } source_paths[i] = 0; } -static void -pr_debug_expression_error (script_t *script, const char *msg) +#define RUP(x,a) (((x) + ((a) - 1)) & ~((a) - 1)) +static pr_short_t __attribute__((pure)) +pr_debug_type_size (const progs_t *pr, const qfot_type_t *type) { - Sys_Printf ("%s\n", msg); + pr_short_t size; + qfot_type_t *aux_type; + switch (type->meta) { + case ty_basic: + case ty_handle: + return pr_type_size[type->type]; + case ty_struct: + case ty_union: + size = 0; + for (pr_int_t i = 0; i < type->strct.num_fields; i++) { + const qfot_var_t *field = &type->strct.fields[i]; + aux_type = &G_STRUCT (pr, qfot_type_t, field->type); + size = max (size, + field->offset + pr_debug_type_size (pr, aux_type)); + } + return size; + case ty_enum: + return pr_type_size[ev_int]; + case ty_array: + aux_type = &G_STRUCT (pr, qfot_type_t, type->array.type); + size = pr_debug_type_size (pr, aux_type); + return type->array.size * size; + case ty_class: + return 1; //FIXME or should it return sizeof class struct? + case ty_alias: + aux_type = &G_STRUCT (pr, qfot_type_t, type->alias.aux_type); + return pr_debug_type_size (pr, aux_type); + } + return 0; } -static ddef_t +static qfot_type_t * +get_def_type (progs_t *pr, pr_def_t *def, qfot_type_t *type) +{ + if (!def->type_encoding) { + // no type encoding, so use basic type data to fill in and return + // the dummy encoding + memset (type, 0, sizeof (*type)); + type->type = def->type; + } else { + type = &G_STRUCT (pr, qfot_type_t, def->type_encoding); + if (!def->size) { + def->size = pr_debug_type_size (pr, type); + } + } + return type; +} + +static pr_def_t parse_expression (progs_t *pr, const char *expr, int conditional) { script_t *es; char *e; pr_type_t *expr_ptr; - ddef_t d; + pr_def_t d; d.ofs = 0; d.type = ev_invalid; - d.s_name = 0; + d.name = 0; es = Script_New (); - es->error = pr_debug_expression_error; Script_Start (es, "", expr); expr_ptr = 0; es->single = "{}()':[]."; if (Script_GetToken (es, 1)) { if (strequal (es->token->str, "[")) { edict_t *ent; - ddef_t *field; + pr_def_t *field; if (!Script_GetToken (es, 1)) goto error; @@ -160,14 +316,14 @@ parse_expression (progs_t *pr, const char *expr, int conditional) if (!field) goto error; d = *field; - expr_ptr = &ent->v[field->ofs]; + expr_ptr = &E_fld (ent, field->ofs); d.ofs = PR_SetPointer (pr, expr_ptr); } else if (isdigit ((byte) es->token->str[0])) { expr_ptr = PR_GetPointer (pr, strtol (es->token->str, 0, 0)); d.type = ev_void; d.ofs = PR_SetPointer (pr, expr_ptr); } else { - ddef_t *global = PR_FindGlobal (pr, es->token->str); + pr_def_t *global = PR_FindGlobal (pr, es->token->str); if (!global) goto error; d = *global; @@ -181,11 +337,11 @@ parse_expression (progs_t *pr, const char *expr, int conditional) goto error; if (!Script_GetToken (es, 1)) goto error; - pr->wp_val.integer_var = strtol (es->token->str, &e, 0); + PR_PTR (int, &pr->wp_val) = strtol (es->token->str, &e, 0); if (e == es->token->str) goto error; if (*e == '.' || *e == 'e' || *e == 'E') - pr->wp_val.float_var = strtod (es->token->str, &e); + PR_PTR (float, &pr->wp_val) = strtod (es->token->str, &e); pr->wp_conditional = 1; } } @@ -193,31 +349,77 @@ parse_expression (progs_t *pr, const char *expr, int conditional) Sys_Printf ("ignoring tail\n"); } error: + if (es->error) { + Sys_Printf ("%s\n", es->error); + } Script_Delete (es); return d; } -void -PR_Debug_Init (void) +static void +pr_debug_clear (progs_t *pr, void *data) { - file_hash = Hash_NewTable (1024, file_get_key, file_free, 0); + __auto_type res = (prdeb_resources_t *) data; + + dstring_clearstr (res->string); + dstring_clearstr (res->dva); + dstring_clearstr (res->line); + dstring_clearstr (res->dstr); + + if (res->debug) + pr->free_progs_mem (pr, res->debug); + Hash_FlushTable (res->file_hash); + Hash_FlushTable (res->debug_syms); + Hash_FlushTable (res->compunits); + PR_RESRESET (res->compmap); + res->debug = 0; + res->auxfunctions = 0; + if (res->auxfunction_map) + pr->free_progs_mem (pr, res->auxfunction_map); + res->auxfunction_map = 0; + if (res->sorted_functions) + pr->free_progs_mem (pr, res->sorted_functions); + res->sorted_functions = 0; + res->linenos = 0; + res->local_defs = 0; + + pr->watch = 0; + pr->wp_conditional = 0; + PR_PTR (int, &pr->wp_val) = 0; + + for (int i = 0; i < ev_type_count; i++ ) { + res->type_encodings[i] = &res->void_type; + } } -void -PR_Debug_Init_Cvars (void) +static void +pr_debug_destroy (progs_t *pr, void *_res) { - pr_debug = Cvar_Get ("pr_debug", "0", CVAR_NONE, NULL, - "enable progs debugging"); - pr_source_path = Cvar_Get ("pr_source_path", ".", CVAR_NONE, source_path_f, - "where to look (within gamedir) for source " - "files"); + __auto_type res = (prdeb_resources_t *) _res; + + dstring_delete (res->string); + dstring_delete (res->dva); + dstring_delete (res->line); + dstring_delete (res->dstr); + va_destroy_context (res->va); + + Hash_DelTable (res->file_hash); + Hash_DelTable (res->debug_syms); + Hash_DelTable (res->compunits); + + PR_RESDELMAP (res->compmap); + + pr->pr_debug_resources = 0; + + free (res); } static file_t * PR_Load_Source_File (progs_t *pr, const char *fname) { + prdeb_resources_t *res = pr->pr_debug_resources; char *l, *p, **dir; - file_t *f = Hash_Find (file_hash, fname); + file_t *f = Hash_Find (res->file_hash, fname); if (f) return f; @@ -225,8 +427,9 @@ PR_Load_Source_File (progs_t *pr, const char *fname) if (!f) return 0; for (dir = source_paths; *dir && !f->text; dir++) { - f->text = pr->load_file (pr, va ("%s%s%s", *dir, **dir ? "/" : "", - fname)); + f->text = pr->load_file (pr, dsprintf (res->dva, "%s%s%s", *dir, + **dir ? "/" : "", fname), + &f->size); } if (!f->text) { pr->file_error (pr, fname); @@ -263,140 +466,351 @@ PR_Load_Source_File (progs_t *pr, const char *fname) f->num_lines++; } f->pr = pr; - Hash_Add (file_hash, f); + Hash_Add (res->file_hash, f); return f; } +static void +byteswap_def (pr_def_t *def) +{ + def->type = LittleShort (def->type); + def->size = LittleShort (def->size); + def->ofs = LittleLong (def->ofs); + def->name = LittleLong (def->name); + def->type_encoding = LittleLong (def->type_encoding); +} + +static compunit_t * +new_compunit (prdeb_resources_t *res) +{ + return PR_RESNEW (res->compmap); +} + +static void +process_compunit (prdeb_resources_t *res, pr_def_t *def) +{ + progs_t *pr = res->pr; + __auto_type compunit = (pr_compunit_t *) (res->debug_data + def->ofs); + + for (unsigned i = 0; i < compunit->num_files; i++) { + compunit_t *cu = new_compunit (res); + cu->unit = compunit; + cu->file = PR_GetString (pr, compunit->files[i]); + Hash_Add (res->compunits, cu); + } +} + +static int +def_compare_sort (const void *_da, const void *_db, void *_res) +{ + pr_def_t da = *(const pr_def_t *)_da; + pr_def_t db = *(const pr_def_t *)_db; + return da.ofs - db.ofs; +} + +static int +func_compare_sort (const void *_fa, const void *_fb, void *_res) +{ + prdeb_resources_t *res = _res; + progs_t *pr = res->pr; + pr_func_t fa = *(const pr_func_t *)_fa; + pr_func_t fb = *(const pr_func_t *)_fb; + const char *fa_file = PR_GetString (pr, pr->pr_functions[fa].file); + const char *fb_file = PR_GetString (pr, pr->pr_functions[fb].file); + int cmp = strcmp (fa_file, fb_file); + if (cmp) { + return cmp; + } + pr_auxfunction_t *fa_aux = res->auxfunction_map[fa]; + pr_auxfunction_t *fb_aux = res->auxfunction_map[fb]; + pr_uint_t fa_line = fa_aux ? fa_aux->source_line : 0; + pr_uint_t fb_line = fb_aux ? fb_aux->source_line : 0; + return fa_line - fb_line; +} + +static int +func_compare_search (const void *_key, const void *_f, void *_res) +{ + prdeb_resources_t *res = _res; + progs_t *pr = res->pr; + const func_key_t *key = _key; + pr_func_t f = *(const pr_func_t *)_f; + const char *f_file = PR_GetString (pr, pr->pr_functions[f].file); + int cmp = strcmp (key->file, f_file); + if (cmp) { + return cmp; + } + pr_auxfunction_t *f_aux = res->auxfunction_map[f]; + pr_uint_t f_line = f_aux ? f_aux->source_line : 0; + return key->line - f_line; +} + +VISIBLE void +PR_DebugSetSym (progs_t *pr, pr_debug_header_t *debug) +{ + prdeb_resources_t *res = pr->pr_debug_resources; + + res->auxfunctions = (pr_auxfunction_t*)((char*)debug + debug->auxfunctions); + res->linenos = (pr_lineno_t*)((char*)debug + debug->linenos); + res->local_defs = (pr_def_t*)((char*)debug + debug->locals); + res->debug_defs = (pr_def_t*)((char*)debug + debug->debug_defs); + res->debug_data = (pr_type_t*)((char*)debug + debug->debug_data); + + size_t size; + size = pr->progs->functions.count * sizeof (pr_auxfunction_t *); + res->auxfunction_map = pr->allocate_progs_mem (pr, size); + size = pr->progs->functions.count * sizeof (pr_func_t); + res->sorted_functions = pr->allocate_progs_mem (pr, size); + + for (pr_uint_t i = 0; i < pr->progs->functions.count; i++) { + res->auxfunction_map[i] = 0; + res->sorted_functions[i] = i; + } + + qfot_type_encodings_t *encodings = 0; + pr_ptr_t type_encodings = 0; + res->type_encodings_def = PR_FindGlobal (pr, ".type_encodings"); + if (res->type_encodings_def) { + encodings = &G_STRUCT (pr, qfot_type_encodings_t, + res->type_encodings_def->ofs); + type_encodings = encodings->types; + } + + for (pr_uint_t i = 0; i < debug->num_auxfunctions; i++) { + if (type_encodings) { + res->auxfunctions[i].return_type += type_encodings; + } + res->auxfunction_map[res->auxfunctions[i].function] = + &res->auxfunctions[i]; + heapsort_r (res->local_defs + res->auxfunctions[i].local_defs, + res->auxfunctions[i].num_locals, sizeof (pr_def_t), + def_compare_sort, res); + } + heapsort_r (res->sorted_functions, pr->progs->functions.count, + sizeof (pr_func_t), func_compare_sort, res); + + for (pr_uint_t i = 0; i < debug->num_locals; i++) { + if (type_encodings) { + res->local_defs[i].type_encoding += type_encodings; + } + } + + pr_string_t compunit_str = PR_FindString (pr, ".compile_unit"); + for (pr_uint_t i = 0; i < debug->num_debug_defs; i++) { + pr_def_t *def = &res->debug_defs[i]; + if (type_encodings) { + def->type_encoding += type_encodings; + } + Hash_Add (res->debug_syms, def); + if (def->name == compunit_str) { + process_compunit (res, def); + } + } + + if (encodings) { + qfot_type_t *type; + for (pr_ptr_t type_ptr = 4; type_ptr < encodings->size; + type_ptr += type->size) { + type = &G_STRUCT (pr, qfot_type_t, type_encodings + type_ptr); + if (type->meta == ty_basic + && (unsigned) type->type < ev_type_count) { + res->type_encodings[type->type] = type; + } + } + } + + res->debug = debug; +} + VISIBLE int PR_LoadDebug (progs_t *pr) { + prdeb_resources_t *res = pr->pr_debug_resources; char *sym_path; const char *path_end, *sym_file; + off_t debug_size; pr_uint_t i; - ddef_t *def; + pr_def_t *def; pr_type_t *str = 0; + pr_debug_header_t *debug; - if (pr->debug) - pr->free_progs_mem (pr, pr->debug); - pr->debug = 0; - pr->auxfunctions = 0; - if (pr->auxfunction_map) - pr->free_progs_mem (pr, pr->auxfunction_map); - pr->auxfunction_map = 0; - pr->linenos = 0; - pr->local_defs = 0; + res->debug = 0; - if (!pr_debug->int_val) + if (!pr_debug) return 1; def = PR_FindGlobal (pr, ".debug_file"); if (def) str = &pr->pr_globals[def->ofs]; - Hash_FlushTable (file_hash); if (!str) return 1; - pr->debugfile = PR_GetString (pr, str->string_var); - sym_file = QFS_SkipPath (pr->debugfile); + res->debugfile = PR_GetString (pr, PR_PTR (string, str)); + sym_file = QFS_SkipPath (res->debugfile); path_end = QFS_SkipPath (pr->progs_name); sym_path = malloc (strlen (sym_file) + (path_end - pr->progs_name) + 1); strncpy (sym_path, pr->progs_name, path_end - pr->progs_name); strcpy (sym_path + (path_end - pr->progs_name), sym_file); - pr->debug = pr->load_file (pr, sym_path); - if (!pr->debug) { + debug = pr->load_file (pr, sym_path, &debug_size); + if (!debug) { Sys_Printf ("can't load %s for debug info\n", sym_path); free (sym_path); return 1; } - pr->debug->version = LittleLong (pr->debug->version); - if (pr->debug->version != PROG_DEBUG_VERSION) { + debug->version = LittleLong (debug->version); + if (debug->version != PROG_DEBUG_VERSION) { Sys_Printf ("ignoring %s with unsupported version %x.%03x.%03x\n", sym_path, - (pr->debug->version >> 24) & 0xff, - (pr->debug->version >> 12) & 0xfff, - pr->debug->version & 0xfff); - pr->debug = 0; + (debug->version >> 24) & 0xff, + (debug->version >> 12) & 0xfff, + debug->version & 0xfff); free (sym_path); return 1; } - pr->debug->crc = LittleShort (pr->debug->crc); - if (pr->debug->crc != pr->crc) { + debug->crc = LittleShort (debug->crc); + if (debug->crc != pr->crc) { Sys_Printf ("ignoring %s that doesn't match %s. (CRCs: " "sym:%d dat:%d)\n", - sym_path, - pr->progs_name, - pr->debug->crc, - pr->crc); - pr->debug = 0; + sym_path, pr->progs_name, debug->crc, pr->crc); free (sym_path); return 1; } free (sym_path); - pr->debug->you_tell_me_and_we_will_both_know = LittleShort - (pr->debug->you_tell_me_and_we_will_both_know); - pr->debug->auxfunctions = LittleLong (pr->debug->auxfunctions); - pr->debug->num_auxfunctions = LittleLong (pr->debug->num_auxfunctions); - pr->debug->linenos = LittleLong (pr->debug->linenos); - pr->debug->num_linenos = LittleLong (pr->debug->num_linenos); - pr->debug->locals = LittleLong (pr->debug->locals); - pr->debug->num_locals = LittleLong (pr->debug->num_locals); + debug->you_tell_me_and_we_will_both_know = LittleShort + (debug->you_tell_me_and_we_will_both_know); + debug->auxfunctions = LittleLong (debug->auxfunctions); + debug->num_auxfunctions = LittleLong (debug->num_auxfunctions); + debug->linenos = LittleLong (debug->linenos); + debug->num_linenos = LittleLong (debug->num_linenos); + debug->locals = LittleLong (debug->locals); + debug->num_locals = LittleLong (debug->num_locals); + debug->debug_defs = LittleLong (debug->debug_defs); + debug->num_debug_defs = LittleLong (debug->num_debug_defs); + debug->debug_data = LittleLong (debug->debug_data); + debug->debug_data_size = LittleLong (debug->debug_data_size); - pr->auxfunctions = (pr_auxfunction_t*)((char*)pr->debug + - pr->debug->auxfunctions); - pr->linenos = (pr_lineno_t*)((char*)pr->debug + pr->debug->linenos); - pr->local_defs = (ddef_t*)((char*)pr->debug + pr->debug->locals); - - i = pr->progs->numfunctions * sizeof (pr_auxfunction_t *); - pr->auxfunction_map = pr->allocate_progs_mem (pr, i); - for (i = 0; (int) i < pr->progs->numfunctions; i++) //FIXME (cast) - pr->auxfunction_map[i] = 0; - - for (i = 0; i < pr->debug->num_auxfunctions; i++) { - pr->auxfunctions[i].function = LittleLong - (pr->auxfunctions[i].function); - pr->auxfunctions[i].source_line = LittleLong - (pr->auxfunctions[i].source_line); - pr->auxfunctions[i].line_info = LittleLong - (pr->auxfunctions[i].line_info); - pr->auxfunctions[i].local_defs = LittleLong - (pr->auxfunctions[i].local_defs); - pr->auxfunctions[i].num_locals = LittleLong - (pr->auxfunctions[i].num_locals); - - pr->auxfunction_map[pr->auxfunctions[i].function] = - &pr->auxfunctions[i]; + __auto_type auxfuncs = (pr_auxfunction_t*)((char*)debug + + debug->auxfunctions); + for (i = 0; i < debug->num_auxfunctions; i++) { + auxfuncs[i].function = LittleLong (auxfuncs[i].function); + auxfuncs[i].source_line = LittleLong (auxfuncs[i].source_line); + auxfuncs[i].line_info = LittleLong (auxfuncs[i].line_info); + auxfuncs[i].local_defs = LittleLong (auxfuncs[i].local_defs); + auxfuncs[i].num_locals = LittleLong (auxfuncs[i].num_locals); } - for (i = 0; i < pr->debug->num_linenos; i++) { - pr->linenos[i].fa.func = LittleLong (pr->linenos[i].fa.func); - pr->linenos[i].line = LittleLong (pr->linenos[i].line); + + __auto_type linenos = (pr_lineno_t*)((char*)debug + debug->linenos); + for (i = 0; i < debug->num_linenos; i++) { + linenos[i].fa.func = LittleLong (linenos[i].fa.func); + linenos[i].line = LittleLong (linenos[i].line); } - for (i = 0; i < pr->debug->num_locals; i++) { - pr->local_defs[i].type = LittleShort (pr->local_defs[i].type); - pr->local_defs[i].ofs = LittleShort (pr->local_defs[i].ofs); - pr->local_defs[i].s_name = LittleLong (pr->local_defs[i].s_name); + + __auto_type local_defs = (pr_def_t*)((char*)debug + debug->locals); + for (i = 0; i < debug->num_locals; i++) { + byteswap_def (&local_defs[i]); } + __auto_type debug_defs = (pr_def_t*)((char*)debug + debug->locals); + for (i = 0; i < debug->num_debug_defs; i++) { + byteswap_def (&debug_defs[i]); + } + + PR_DebugSetSym (pr, debug); return 1; } +VISIBLE const char * +PR_Debug_GetBaseDirectory (progs_t *pr, const char *file) +{ + prdeb_resources_t *res = pr->pr_debug_resources; + __auto_type cu = (compunit_t *) Hash_Find (res->compunits, file); + + if (cu) { + return PR_GetString (pr, cu->unit->basedir); + } + return 0; +} + +VISIBLE pr_auxfunction_t * +PR_Debug_AuxFunction (progs_t *pr, pr_uint_t func) +{ + prdeb_resources_t *res = pr->pr_debug_resources; + if (!res->debug || func >= res->debug->num_auxfunctions) { + return 0; + } + return &res->auxfunctions[func]; +} + +VISIBLE pr_auxfunction_t * +PR_Debug_MappedAuxFunction (progs_t *pr, pr_uint_t func) +{ + prdeb_resources_t *res = pr->pr_debug_resources; + if (!res->debug || func >= pr->progs->functions.count) { + return 0; + } + return res->auxfunction_map[func]; +} + +VISIBLE pr_def_t * +PR_Debug_LocalDefs (progs_t *pr, pr_auxfunction_t *aux_function) +{ + prdeb_resources_t *res = pr->pr_debug_resources; + if (!res->debug || !aux_function) { + return 0; + } + if (aux_function->local_defs > res->debug->num_locals) { + return 0; + } + return res->local_defs + aux_function->local_defs; +} + +VISIBLE pr_lineno_t * +PR_Debug_Linenos (progs_t *pr, pr_auxfunction_t *aux_function, + pr_uint_t *num_linenos) +{ + pr_uint_t i, count; + prdeb_resources_t *res = pr->pr_debug_resources; + if (!res->debug) { + return 0; + } + if (!aux_function) { + *num_linenos = res->debug->num_linenos; + return res->linenos; + } + if (aux_function->line_info > res->debug->num_linenos) { + return 0; + } + //FIXME put lineno count in sym file + for (count = 1, i = aux_function->line_info + 1; + i < res->debug->num_linenos; i++, count++) { + if (!res->linenos[i].line) { + break; + } + } + *num_linenos = count; + return res->linenos + aux_function->line_info; +} + pr_auxfunction_t * PR_Get_Lineno_Func (progs_t *pr, pr_lineno_t *lineno) { - while (lineno > pr->linenos && lineno->line) + prdeb_resources_t *res = pr->pr_debug_resources; + while (lineno > res->linenos && lineno->line) lineno--; if (lineno->line) return 0; - return &pr->auxfunctions[lineno->fa.func]; + return &res->auxfunctions[lineno->fa.func]; } pr_uint_t PR_Get_Lineno_Addr (progs_t *pr, pr_lineno_t *lineno) { + prdeb_resources_t *res = pr->pr_debug_resources; pr_auxfunction_t *f; if (lineno->line) return lineno->fa.addr; - if (lineno->fa.func < pr->debug->num_auxfunctions) { - f = &pr->auxfunctions[lineno->fa.func]; + if (lineno->fa.func < res->debug->num_auxfunctions) { + f = &res->auxfunctions[lineno->fa.func]; return pr->pr_functions[f->function].first_statement; } // take a wild guess that only the line number is bogus and return @@ -415,36 +829,75 @@ PR_Get_Lineno_Line (progs_t *pr, pr_lineno_t *lineno) pr_lineno_t * PR_Find_Lineno (progs_t *pr, pr_uint_t addr) { + prdeb_resources_t *res = pr->pr_debug_resources; pr_uint_t i; pr_lineno_t *lineno = 0; - if (!pr->debug) + if (!res->debug) return 0; - if (!pr->debug->num_linenos) + if (!res->debug->num_linenos) return 0; - for (i = pr->debug->num_linenos; i > 0; i--) { - if (PR_Get_Lineno_Addr (pr, &pr->linenos[i - 1]) <= addr) { - lineno = &pr->linenos[i - 1]; + for (i = res->debug->num_linenos; i > 0; i--) { + if (PR_Get_Lineno_Addr (pr, &res->linenos[i - 1]) <= addr) { + lineno = &res->linenos[i - 1]; break; } } return lineno; } +pr_uint_t +PR_FindSourceLineAddr (progs_t *pr, const char *file, pr_uint_t line) +{ + prdeb_resources_t *res = pr->pr_debug_resources; + func_key_t key = { file, line }; + pr_func_t *f = fbsearch_r (&key, res->sorted_functions, + pr->progs->functions.count, sizeof (pr_func_t), + func_compare_search, res); + if (!f) { + return 0; + } + dfunction_t *func = &pr->pr_functions[*f]; + if (func->first_statement <= 0 + || strcmp (file, PR_GetString (pr, func->file)) != 0) { + return 0; + } + pr_auxfunction_t *aux = res->auxfunction_map[*f]; + if (!aux) { + return 0; + } + pr_uint_t addr = func->first_statement; + line -= aux->source_line; + + //FIXME put lineno count in sym file + for (pr_uint_t i = aux->line_info + 1; i < res->debug->num_linenos; i++) { + if (!res->linenos[i].line) { + break; + } + if (res->linenos[i].line <= line) { + addr = res->linenos[i].fa.addr; + } else { + break; + } + } + return addr; +} + const char * PR_Get_Source_File (progs_t *pr, pr_lineno_t *lineno) { pr_auxfunction_t *f; f = PR_Get_Lineno_Func (pr, lineno); - if (f->function >= (unsigned) pr->progs->numfunctions) + if (f->function >= (unsigned) pr->progs->functions.count) return 0; - return PR_GetString(pr, pr->pr_functions[f->function].s_file); + return PR_GetString(pr, pr->pr_functions[f->function].file); } const char * PR_Get_Source_Line (progs_t *pr, pr_uint_t addr) { + prdeb_resources_t *res = pr->pr_debug_resources; const char *fname; pr_uint_t line; file_t *file; @@ -464,41 +917,43 @@ PR_Get_Source_Line (progs_t *pr, pr_uint_t addr) file = PR_Load_Source_File (pr, fname); if (!file || !file->lines || !line || line > file->num_lines) - return va ("%s:%u", fname, line); + return dsprintf (res->dva, "%s:%u", fname, line); - return va ("%s:%u:%.*s", fname, line, (int)file->lines[line - 1].len, - file->lines[line - 1].text); + return dsprintf (res->dva, "%s:%u:%.*s", fname, line, + (int)file->lines[line - 1].len, + file->lines[line - 1].text); } -ddef_t * -PR_Get_Param_Def (progs_t *pr, dfunction_t *func, unsigned parm) +pr_def_t * +PR_Get_Param_Def (progs_t *pr, dfunction_t *func, unsigned param) { + prdeb_resources_t *res = pr->pr_debug_resources; pr_uint_t i; pr_auxfunction_t *aux_func; - ddef_t *ddef = 0; + pr_def_t *ddef = 0; int num_params; int param_offs = 0; - if (!pr->debug) + if (!res->debug) return 0; if (!func) return 0; - num_params = func->numparms; + num_params = func->numparams; if (num_params < 0) { num_params = ~num_params; // one's compliment param_offs = 1; // skip over @args def } - if (parm >= (unsigned) num_params) + if (param >= (unsigned) num_params) return 0; - aux_func = pr->auxfunction_map[func - pr->pr_functions]; + aux_func = res->auxfunction_map[func - pr->pr_functions]; if (!aux_func) return 0; for (i = 0; i < aux_func->num_locals; i++) { - ddef = &pr->local_defs[aux_func->local_defs + param_offs + i]; - if (!parm--) + ddef = &res->local_defs[aux_func->local_defs + param_offs + i]; + if (!param--) break; } return ddef; @@ -507,42 +962,70 @@ PR_Get_Param_Def (progs_t *pr, dfunction_t *func, unsigned parm) static pr_auxfunction_t * get_aux_function (progs_t *pr) { + prdeb_resources_t *res = pr->pr_debug_resources; dfunction_t *func; - if (!pr->pr_xfunction || !pr->auxfunction_map) + if (!pr->pr_xfunction || !res->auxfunction_map) return 0; func = pr->pr_xfunction->descriptor; - return pr->auxfunction_map[func - pr->pr_functions]; + return res->auxfunction_map[func - pr->pr_functions]; } -ddef_t * -PR_Get_Local_Def (progs_t *pr, pr_int_t offs) +static qfot_type_t * +get_type (prdeb_resources_t *res, int typeptr) { - pr_uint_t i; + progs_t *pr = res->pr; + + if (!typeptr) { + return &res->void_type; + } + return &G_STRUCT (pr, qfot_type_t, typeptr); +} + +pr_def_t * +PR_Get_Local_Def (progs_t *pr, pr_ptr_t *offset) +{ + prdeb_resources_t *res = pr->pr_debug_resources; dfunction_t *func; pr_auxfunction_t *aux_func; + pr_ptr_t offs = *offset; + pr_def_t *def; if (!pr->pr_xfunction) return 0; func = pr->pr_xfunction->descriptor; if (!func) return 0; - aux_func = pr->auxfunction_map[func - pr->pr_functions]; + aux_func = res->auxfunction_map[func - pr->pr_functions]; if (!aux_func) return 0; - offs -= func->parm_start; - if (offs < 0 || offs >= func->locals) + + pr_ptr_t locals_start; + if (pr->progs->version == PROG_VERSION) { + if (pr->pr_depth) { + prstack_t *frame = pr->pr_stack + pr->pr_depth - 1; + locals_start = frame->stack_ptr - func->params_start; + } else { + locals_start = 0; //FIXME ? when disassembling in qfprogs + } + } else { + locals_start = func->params_start; + } + offs -= locals_start; + if (offs >= func->locals) return 0; - for (i = 0; i < aux_func->num_locals; i++) - if (pr->local_defs[aux_func->local_defs + i].ofs == offs) - return &pr->local_defs[aux_func->local_defs + i]; - return 0; + if ((def = PR_SearchDefs (res->local_defs + aux_func->local_defs, + aux_func->num_locals, offs))) { + *offset = offs - def->ofs; + } + return def; } VISIBLE void PR_DumpState (progs_t *pr) { + prdeb_resources_t *res = pr->pr_debug_resources; if (pr->pr_xfunction) { - if (pr_debug->int_val && pr->debug) { + if (pr_debug && res->debug) { pr_lineno_t *lineno; pr_auxfunction_t *func = 0; dfunction_t *descriptor = pr->pr_xfunction->descriptor; @@ -566,183 +1049,424 @@ PR_DumpState (progs_t *pr) #define ISDENORM(x) ((x) && !((x) & 0x7f800000)) -static const char * -value_string (progs_t *pr, etype_t type, pr_type_t *val) +static void +value_string (pr_debug_data_t *data, qfot_type_t *type, pr_type_t *value) { - static dstring_t *line; - ddef_t *def; - pr_int_t ofs; - edict_t *edict; - dfunction_t *f; - const char *str; + switch (type->meta) { + case ty_handle: + case ty_basic: + switch (type->type) { +#define EV_TYPE(t) \ + case ev_##t: \ + raw_type_view.t##_view (type, value, data); \ + break; +#include "QF/progs/pr_type_names.h" - if (!line) - line = dstring_new (); - - type &= ~DEF_SAVEGLOBAL; - - switch (type) { - case ev_string: - if (!PR_StringValid (pr, val->string_var)) - return "*** invalid ***"; - str = PR_GetString (pr, val->string_var); - dstring_copystr (line, "\""); - while (*str) { - const char *s; - - for (s = str; *s && !strchr ("\"\n\t", *s); s++) - ; - if (s != str) - dstring_appendsubstr (line, str, s - str); - if (*s) { - switch (*s) { - case '\"': - dstring_appendstr (line, "\\\""); - break; - case '\n': - dstring_appendstr (line, "\\n"); - break; - case '\t': - dstring_appendstr (line, "\\t"); - break; - default: - dasprintf (line, "\\x%02x", *s & 0xff); - } - s++; - } - str = s; - } - dstring_appendstr (line, "\""); - break; - case ev_entity: - edict = PROG_TO_EDICT (pr, val->entity_var); - dsprintf (line, "entity %d", NUM_FOR_BAD_EDICT (pr, edict)); - break; - case ev_func: - if (val->func_var < 0 || val->func_var >= pr->progs->numfunctions) - dsprintf (line, "INVALID:%d", val->func_var); - else if (!val->func_var) - return "NULL"; - else { - f = pr->pr_functions + val->func_var; - dsprintf (line, "%s()", PR_GetString (pr, f->s_name)); + case ev_invalid: + case ev_type_count: + dstring_appendstr (data->dstr, ""); } break; - case ev_field: - def = PR_FieldAtOfs (pr, val->integer_var); - if (def) - dsprintf (line, ".%s", PR_GetString (pr, def->s_name)); - else - dsprintf (line, ".<$%04x>", val->integer_var); + case ty_struct: + raw_type_view.struct_view (type, value, data); break; - case ev_void: - return "void"; - case ev_float: - if (ISDENORM (val->integer_var) && val->uinteger_var != 0x80000000) - dsprintf (line, "<%08x>", val->integer_var); - else - dsprintf (line, "%g", val->float_var); + case ty_union: + raw_type_view.union_view (type, value, data); break; - case ev_vector: - dsprintf (line, "'%g %g %g'", - val->vector_var[0], val->vector_var[1], - val->vector_var[2]); + case ty_enum: + raw_type_view.enum_view (type, value, data); break; - case ev_pointer: - def = 0; - ofs = val->integer_var; - if (pr_debug->int_val && pr->debug) - def = PR_Get_Local_Def (pr, ofs); - if (!def) - def = PR_GlobalAtOfs (pr, ofs); - if (def && def->s_name) - dsprintf (line, "&%s", PR_GetString (pr, def->s_name)); - else - dsprintf (line, "[$%x]", ofs); + case ty_array: + raw_type_view.array_view (type, value, data); break; - case ev_quat: - dsprintf (line, "'%g %g %g %g'", - val->vector_var[0], val->vector_var[1], - val->vector_var[2], val->vector_var[3]); + case ty_class: + raw_type_view.class_view (type, value, data); break; - case ev_integer: - dsprintf (line, "%d", val->integer_var); - break; - case ev_uinteger: - dsprintf (line, "$%08x", val->uinteger_var); - break; - default: - //dsprintf (line, "bad type %i", type); - dsprintf (line, "<%x %x %x %x>", - val[0].integer_var, val[1].integer_var, - val[2].integer_var, val[3].integer_var); + case ty_alias://XXX + type = &G_STRUCT (data->pr, qfot_type_t, type->alias.aux_type); + value_string (data, type, value); break; } - - return line->str; } -static ddef_t * -def_string (progs_t *pr, pr_int_t ofs, dstring_t *dstr) +static pr_def_t * +pr_debug_find_def (progs_t *pr, pr_ptr_t *ofs) { - ddef_t *def = 0; - const char *name; + prdeb_resources_t *res = pr->pr_debug_resources; + pr_def_t *def = 0; - if (pr_debug->int_val && pr->debug) + if (pr_debug && res->debug) { def = PR_Get_Local_Def (pr, ofs); - if (!def) - def = PR_GlobalAtOfs (pr, ofs); - if (!def || !*(name = PR_GetString (pr, def->s_name))) - dsprintf (dstr, "[$%x]", ofs); - else - dsprintf (dstr, "%s", name); + } + if (*ofs >= pr->progs->globals.count) { + return 0; + } + if (!def) { + def = PR_GlobalAtOfs (pr, *ofs); + if (def) { + *ofs -= def->ofs; + } + } return def; } static const char * -global_string (progs_t *pr, pointer_t ofs, etype_t type, int contents) +global_string (pr_debug_data_t *data, pr_ptr_t offset, qfot_type_t *type, + int contents) { - static dstring_t *line = NULL; - ddef_t *def = NULL; - const char *s; + progs_t *pr = data->pr; + prdeb_resources_t *res = pr->pr_debug_resources; + dstring_t *dstr = data->dstr; + pr_def_t *def = NULL; + qfot_type_t dummy_type = { }; + const char *name = 0; + pr_ptr_t offs = offset; - if (!line) - line = dstring_newstr(); + dstring_clearstr (dstr); - if (type == ev_short) { - dsprintf (line, "%04x", (short) ofs); - return line->str; + if (type && type->meta == ty_basic && type->type == ev_short) { + dsprintf (dstr, "%04x", (short) offset); + return dstr->str; } - def = def_string (pr, ofs, line); + if (offset > pr->globals_size) { + dsprintf (dstr, "%08x out of bounds", offset); + return dstr->str; + } - if (contents && (def || type != ev_void)) { - const char *oi = ""; - if (def) { - if (type == ev_void) - type = def->type; - if (type != (etype_t) (def->type & ~DEF_SAVEGLOBAL)) - oi = "?"; - } - - if (ofs > pr->globals_size) - s = "Out of bounds"; - else - s = value_string (pr, type, &pr->pr_globals[ofs]); - - if (strequal(line->str, "IMMEDIATE") || strequal(line->str, ".imm")) { - dsprintf (line, "%s", s); + def = pr_debug_find_def (pr, &offs); + if (!def || !PR_StringValid (pr, def->name) + || !*(name = PR_GetString (pr, def->name))) { + dsprintf (dstr, "[$%x]", offset); + } + if (name) { + if (strequal (name, "IMMEDIATE") || strequal (name, ".imm")) { + contents = 1; } else { - dasprintf (line, "%s(%s)", oi, s); + if (offs) { + dsprintf (dstr, "{%s + %u}", name, offs); + } else { + dsprintf (dstr, "%s", name); + } } } - return line->str; + if (contents) { + if (name) { + dstring_appendstr (dstr, "("); + } + if (!type) { + if (def) { + if (!def->type_encoding) { + dummy_type.type = def->type; + type = &dummy_type; + } else { + type = &G_STRUCT (pr, qfot_type_t, def->type_encoding); + } + } else { + type = &res->void_type; + } + } + value_string (data, type, pr->pr_globals + offset); + if (name) { + dstring_appendstr (dstr, ")"); + } + } + return dstr->str; +} + +const char * +PR_Debug_ValueString (progs_t *pr, pr_ptr_t offset, qfot_type_t *type, + dstring_t *dstr) +{ + pr_debug_data_t data = {pr, dstr}; + value_string (&data, type, pr->pr_globals + offset); + return dstr->str; +} + +static void +pr_debug_void_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + __auto_type data = (pr_debug_data_t *) _data; + dasprintf (data->dstr, ""); +} + +static void +pr_debug_string_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + __auto_type data = (pr_debug_data_t *) _data; + dstring_t *dstr = data->dstr; + pr_string_t string = PR_PTR (string, value); + if (PR_StringValid (data->pr, string)) { + const char *str = PR_GetString (data->pr, string); + + dstring_appendstr (dstr, "\""); + while (*str) { + const char *s; + + for (s = str; *s && !strchr ("\"\n\t", *s); s++) { + } + if (s != str) { + dstring_appendsubstr (dstr, str, s - str); + } + if (*s) { + switch (*s) { + case '\"': + dstring_appendstr (dstr, "\\\""); + break; + case '\n': + dstring_appendstr (dstr, "\\n"); + break; + case '\t': + dstring_appendstr (dstr, "\\t"); + break; + default: + dasprintf (dstr, "\\x%02x", *s & 0xff); + } + s++; + } + str = s; + } + dstring_appendstr (dstr, "\""); + } else { + dstring_appendstr (dstr, "*** invalid string offset ***"); + } +} + +static void +pr_debug_float_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + __auto_type data = (pr_debug_data_t *) _data; + dstring_t *dstr = data->dstr; + + if (data->pr->progs->version == PROG_ID_VERSION + && ISDENORM (PR_PTR (int, value)) + && PR_PTR (uint, value) != 0x80000000) { + dasprintf (dstr, "<%08x>", PR_PTR (int, value)); + } else { + dasprintf (dstr, "%.9g", PR_PTR (float, value)); + } +} + +static void +pr_debug_vector_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + __auto_type data = (pr_debug_data_t *) _data; + dstring_t *dstr = data->dstr; + + dasprintf (dstr, "'%.9g %.9g %.9g'", VectorExpand (&PR_PTR (float, value))); +} + +static void +pr_debug_entity_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + __auto_type data = (pr_debug_data_t *) _data; + progs_t *pr = data->pr; + dstring_t *dstr = data->dstr; + + if (pr->pr_edicts + && PR_PTR (entity, value) < pr->max_edicts + && !(PR_PTR (entity, value) % pr->pr_edict_size)) { + edict_t *edict = PROG_TO_EDICT (pr, PR_PTR (entity, value)); + if (edict) { + dasprintf (dstr, "entity %d", NUM_FOR_BAD_EDICT (pr, edict)); + return; + } + } + dasprintf (dstr, "entity [%x]", PR_PTR (entity, value)); +} + +static void +pr_debug_field_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + __auto_type data = (pr_debug_data_t *) _data; + progs_t *pr = data->pr; + dstring_t *dstr = data->dstr; + pr_def_t *def = PR_FieldAtOfs (pr, PR_PTR (int, value)); + + if (def) { + dasprintf (dstr, ".%s", PR_GetString (pr, def->name)); + } else { + dasprintf (dstr, ".<$%04x>", PR_PTR (int, value)); + } +} + +static void +pr_debug_func_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + __auto_type data = (pr_debug_data_t *) _data; + progs_t *pr = data->pr; + dstring_t *dstr = data->dstr; + + if (PR_PTR (func, value) >= pr->progs->functions.count) { + dasprintf (dstr, "INVALID:%d", PR_PTR (func, value)); + } else if (!PR_PTR (func, value)) { + dstring_appendstr (dstr, "NULL"); + } else { + dfunction_t *f = pr->pr_functions + PR_PTR (func, value); + dasprintf (dstr, "%s()", PR_GetString (pr, f->name)); + } +} + +static void +pr_debug_ptr_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + __auto_type data = (pr_debug_data_t *) _data; + progs_t *pr = data->pr; + dstring_t *dstr = data->dstr; + pr_ptr_t offset = PR_PTR (int, value); + pr_ptr_t offs = offset; + pr_def_t *def = 0; + + def = pr_debug_find_def (pr, &offs); + if (def && def->name) { + if (offs) { + dasprintf (dstr, "&%s + %u", PR_GetString (pr, def->name), offs); + } else { + dasprintf (dstr, "&%s", PR_GetString (pr, def->name)); + } + } else { + dasprintf (dstr, "[$%x]", offset); + } +} + +static void +pr_debug_quaternion_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + __auto_type data = (pr_debug_data_t *) _data; + dstring_t *dstr = data->dstr; + + dasprintf (dstr, "'%.9g %.9g %.9g %.9g'", QuatExpand (&PR_PTR (float, value))); +} + +static void +pr_debug_int_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + __auto_type data = (pr_debug_data_t *) _data; + dstring_t *dstr = data->dstr; + + dasprintf (dstr, "%d", PR_PTR (int, value)); +} + +static void +pr_debug_uint_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + __auto_type data = (pr_debug_data_t *) _data; + dstring_t *dstr = data->dstr; + + dasprintf (dstr, "$%08x", PR_PTR (uint, value)); +} + +static void +pr_debug_short_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + __auto_type data = (pr_debug_data_t *) _data; + dstring_t *dstr = data->dstr; + + dasprintf (dstr, "%04x", (short)PR_PTR (int, value)); +} + +static void +pr_debug_double_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + __auto_type data = (pr_debug_data_t *) _data; + dstring_t *dstr = data->dstr; + + dasprintf (dstr, "%.17g", *(double *)value); +} + +static void +pr_debug_long_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + __auto_type data = (pr_debug_data_t *) _data; + dstring_t *dstr = data->dstr; + + dasprintf (dstr, "%" PRIi64, *(int64_t *)value); +} + +static void +pr_debug_ulong_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + __auto_type data = (pr_debug_data_t *) _data; + dstring_t *dstr = data->dstr; + + dasprintf (dstr, "%" PRIu64, *(uint64_t *)value); +} + +static void +pr_debug_ushort_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + __auto_type data = (pr_debug_data_t *) _data; + dstring_t *dstr = data->dstr; + + dasprintf (dstr, "%04x", (pr_ushort_t)PR_PTR (int, value)); +} + +static void +pr_dump_struct (qfot_type_t *type, pr_type_t *value, void *_data, + const char *struct_type) +{ + __auto_type data = (pr_debug_data_t *) _data; + progs_t *pr = data->pr; + dstring_t *dstr = data->dstr; + qfot_struct_t *strct = &type->strct; + + dstring_appendstr (dstr, "{"); + for (int i = 0; i < strct->num_fields; i++) { + qfot_var_t *field = strct->fields + i; + qfot_type_t *val_type = &G_STRUCT (pr, qfot_type_t, field->type); + pr_type_t *val = value + field->offset; + dasprintf (dstr, "%s=", PR_GetString (pr, field->name)); + value_string (data, val_type, val); + if (i < strct->num_fields - 1) { + dstring_appendstr (dstr, ", "); + } + } + dstring_appendstr (dstr, "}"); + //dasprintf (dstr, "<%s>", struct_type); +} +static void +pr_debug_struct_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + pr_dump_struct (type, value, _data, "struct"); +} + +static void +pr_debug_union_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + pr_dump_struct (type, value, _data, "union"); +} + +static void +pr_debug_enum_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + __auto_type data = (pr_debug_data_t *) _data; + dstring_t *dstr = data->dstr; + + dstring_appendstr (dstr, ""); +} + +static void +pr_debug_array_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + __auto_type data = (pr_debug_data_t *) _data; + dstring_t *dstr = data->dstr; + + dstring_appendstr (dstr, ""); +} + +static void +pr_debug_class_view (qfot_type_t *type, pr_type_t *value, void *_data) +{ + __auto_type data = (pr_debug_data_t *) _data; + dstring_t *dstr = data->dstr; + + dstring_appendstr (dstr, ""); } VISIBLE void PR_Debug_Watch (progs_t *pr, const char *expr) { - ddef_t watch; + pr_def_t watch; if (!expr) { Sys_Printf ("watch \n"); if (pr->watch) { @@ -750,9 +1474,8 @@ PR_Debug_Watch (progs_t *pr, const char *expr) (int) (intptr_t) (pr->watch - pr->pr_globals)); if (pr->wp_conditional) Sys_Printf (" if new val == %d\n", - pr->wp_val.integer_var); - } else { - Sys_Printf (" none active\n"); + PR_PTR (int, &pr->wp_val)); + } else { Sys_Printf (" none active\n"); } return; } @@ -764,7 +1487,7 @@ PR_Debug_Watch (progs_t *pr, const char *expr) if (pr->watch) { Sys_Printf ("watchpoint set to [%d]\n", PR_SetPointer (pr, pr->watch)); if (pr->wp_conditional) - Sys_Printf (" if new val == %d\n", pr->wp_val.integer_var); + Sys_Printf (" if new val == %d\n", PR_PTR (int, &pr->wp_val)); } else { Sys_Printf ("watchpoint cleared\n"); } @@ -773,7 +1496,9 @@ PR_Debug_Watch (progs_t *pr, const char *expr) VISIBLE void PR_Debug_Print (progs_t *pr, const char *expr) { - ddef_t print; + prdeb_resources_t *res = pr->pr_debug_resources; + pr_def_t print; + pr_debug_data_t data = {pr, res->dstr}; if (!expr) { Sys_Printf ("print \n"); @@ -781,37 +1506,54 @@ PR_Debug_Print (progs_t *pr, const char *expr) } print = parse_expression (pr, expr, 0); - if (print.type != ev_invalid) { - const char *s = global_string (pr, print.ofs, print.type, 1); + if (print.type_encoding) { + qfot_type_t *type = get_type (res, print.type_encoding); + const char *s = global_string (&data, print.ofs, type, 1); Sys_Printf ("[%d] = %s\n", print.ofs, s); } } +static const char * +print_raw_op (progs_t *pr, pr_ushort_t op, pr_ushort_t base_ind, + etype_t op_type, int op_width) +{ + prdeb_resources_t *res = pr->pr_debug_resources; + const char *width = va (res->va, "%d", op_width); + return va (res->va, "%d:%04x<%08x>%s:%-8s", + base_ind, op, op + pr->pr_bases[base_ind], + op_width > 0 ? width : op_width < 0 ? "X" : "?", + pr_type_name[op_type]); +} + VISIBLE void PR_PrintStatement (progs_t *pr, dstatement_t *s, int contents) { + prdeb_resources_t *res = pr->pr_debug_resources; int addr = s - pr->pr_statements; int dump_code = contents & 2; const char *fmt; - opcode_t *op; - static dstring_t *line; + const char *mnemonic; + const char *width = ""; dfunction_t *call_func = 0; - ddef_t *parm_def = 0; + pr_def_t *param_def = 0; pr_auxfunction_t *aux_func = 0; + pr_debug_data_t data; + etype_t op_type[3]; + int op_width[3]; - if (!line) - line = dstring_new (); + dstring_clearstr (res->line); - dstring_clearstr (line); + data.pr = pr; + data.dstr = res->dstr; - if (pr_debug->int_val > 1) + if (pr_debug > 1) dump_code = 1; - if (pr_debug->int_val && pr->debug) { + if (pr_debug && res->debug) { const char *source_line = PR_Get_Source_Line (pr, addr); if (source_line) { - dasprintf (line, "%s%s", source_line, dump_code ? "\n" : ""); + dasprintf (res->line, "%s%s", source_line, dump_code ? "\n" : ""); if (!dump_code) goto do_print; } @@ -819,174 +1561,271 @@ PR_PrintStatement (progs_t *pr, dstatement_t *s, int contents) return; } - op = PR_Opcode (s->op); - if (!op) { - Sys_Printf ("%sUnknown instruction %d\n", line->str, s->op); - return; + if (pr->progs->version < PROG_VERSION) { + const v6p_opcode_t *op = PR_v6p_Opcode (s->op); + if (!op) { + Sys_Printf ("%sUnknown instruction %d\n", res->line->str, s->op); + return; + } + VectorSet (op->type_a, op->type_b, op->type_c, op_type); + VectorSet (1, 1, 1, op_width); + fmt = op->fmt; + mnemonic = op->opname; + } else { + const opcode_t *op = PR_Opcode (s->op); + if (!op) { + Sys_Printf ("%sUnknown instruction %d\n", res->line->str, s->op); + return; + } + VectorCopy (op->widths, op_width); + VectorCopy (op->types, op_type); + fmt = op->fmt; + mnemonic = op->mnemonic; } - if (!(fmt = op->fmt)) + if (!fmt) { fmt = "%Ga, %Gb, %gc"; + } - dasprintf (line, "%04x ", addr); - if (pr_debug->int_val > 2) - dasprintf (line, "%02x %04x(%8s) %04x(%8s) %04x(%8s)\t", - s->op, - s->a, pr_type_name[op->type_a], - s->b, pr_type_name[op->type_b], - s->c, pr_type_name[op->type_c]); + dasprintf (res->line, "%04x ", addr); + if (pr_debug > 2) { + if (pr->progs->version < PROG_VERSION) { + dasprintf (res->line, + "%03x %04x(%8s) %04x(%8s) %04x(%8s)\t", + s->op, + s->a, pr_type_name[op_type[0]], + s->b, pr_type_name[op_type[1]], + s->c, pr_type_name[op_type[2]]); + } else { + dasprintf (res->line, "%04x %s %s %s\t", + s->op, + print_raw_op (pr, s->a, PR_BASE_IND (s->op, A), + op_type[0], op_width[0]), + print_raw_op (pr, s->b, PR_BASE_IND (s->op, B), + op_type[1], op_width[1]), + print_raw_op (pr, s->c, PR_BASE_IND (s->op, C), + op_type[2], op_width[2])); + } + } else if (op_width[0] > 1 || op_width[1] > 1 || op_width[2] > 1) { + width = va (res->va, "{%d,%d,%d}", VectorExpand (op_width)); + } - dasprintf (line, "%s ", op->opname); + dasprintf (res->line, "%s%s ", mnemonic, width); while (*fmt) { if (*fmt == '%') { if (fmt[1] == '%') { - dstring_appendsubstr (line, fmt + 1, 1); + dstring_appendsubstr (res->line, fmt + 1, 1); fmt += 2; } else { const char *str; char mode = fmt[1], opchar = fmt[2]; - unsigned parm_ind = 0; - pr_int_t opval; - etype_t optype = ev_void; + unsigned param_ind = 0; + pr_uint_t shift = 0; + pr_uint_t opreg; + pr_uint_t opval; + qfot_type_t *optype = &res->void_type; + pr_func_t func; if (mode == 'P') { opchar = fmt[3]; - parm_ind = fmt[2] - '0'; + param_ind = fmt[2] - '0'; fmt++; // P has one extra item - if (parm_ind >= MAX_PARMS) + if (param_ind >= PR_MAX_PARAMS) goto err; } + if (mode == 'M' || mode == 'm') { + if (!isxdigit ((byte) fmt[3])) { + goto err; + } + shift = fmt[3]; + shift = (shift & 0xf) + + (((shift & 0x40) >> 3) | ((shift & 0x40) >> 5)); + fmt++; // M/m have one extra item + } switch (opchar) { case 'a': + opreg = PR_BASE_IND (s->op, A); opval = s->a; - optype = op->type_a; + optype = res->type_encodings[op_type[0]]; break; case 'b': + opreg = PR_BASE_IND (s->op, B); opval = s->b; - optype = op->type_b; + optype = res->type_encodings[op_type[1]]; break; case 'c': + opreg = PR_BASE_IND (s->op, C); opval = s->c; - optype = op->type_c; + optype = res->type_encodings[op_type[2]]; + break; + case 'o': + opreg = 0; + opval = s->op; break; case 'x': if (mode == 'P') { - opval = pr->pr_real_params[parm_ind] + opval = pr->pr_real_params[param_ind] - pr->pr_globals; break; } + goto err; default: goto err; } switch (mode) { case 'R': - optype = ev_void; + optype = &res->void_type; aux_func = get_aux_function (pr); - if (aux_func) - optype = aux_func->return_type; - str = global_string (pr, opval, optype, contents & 1); + if (aux_func) { + optype = get_type (res, aux_func->return_type); + } + str = global_string (&data, opval, optype, + contents & 1); break; case 'F': - str = global_string (pr, opval, optype, contents & 1); - if (G_FUNCTION (pr, opval) >= 0 - && G_FUNCTION (pr, opval) - < pr->progs->numfunctions) - call_func = pr->pr_functions + G_FUNCTION (pr, opval); + str = global_string (&data, opval, optype, + contents & 1); + func = G_FUNCTION (pr, opval); + if (func < pr->progs->functions.count) { + call_func = pr->pr_functions + func; + } break; case 'P': - parm_def = PR_Get_Param_Def (pr, call_func, parm_ind); - optype = ev_void; - if (parm_def) - optype = parm_def->type; - str = global_string (pr, opval, optype, contents & 1); + param_def = PR_Get_Param_Def (pr, call_func, param_ind); + optype = &res->void_type; + if (param_def) { + optype = get_type (res, param_def->type_encoding); + } + str = global_string (&data, opval, optype, + contents & 1); break; case 'V': - str = global_string (pr, opval, ev_void, contents & 1); + opval += pr->pr_bases[opreg]; + optype = &res->void_type; + str = global_string (&data, opval, optype, + contents & 1); break; case 'G': - str = global_string (pr, opval, optype, contents & 1); + opval += pr->pr_bases[opreg]; + str = global_string (&data, opval, optype, + contents & 1); break; case 'g': - str = global_string (pr, opval, optype, 0); + opval += pr->pr_bases[opreg]; + str = global_string (&data, opval, optype, 0); break; case 's': - str = va ("%d", (short) opval); + str = dsprintf (res->dva, "%d", (short) opval); break; case 'O': - str = va ("%04x", addr + (short) opval); + str = dsprintf (res->dva, "%04x", + addr + (short) opval); + break; + case 'C': + str = dsprintf (res->dva, "%03o", opval); break; case 'E': { edict_t *ed = 0; - opval = pr->pr_globals[s->a].entity_var; - parm_ind = pr->pr_globals[s->b].uinteger_var; - if (parm_ind < pr->progs->entityfields - && opval >= 0 - && opval < pr->pr_edictareasize) { + opval = G_ENTITY (pr, s->a); + param_ind = G_FIELD (pr, s->b); + if (param_ind < pr->progs->entityfields + && opval > 0 + && opval < pr->pr_edict_area_size) { ed = PROG_TO_EDICT (pr, opval); - opval = &ed->v[parm_ind] - pr->pr_globals; + opval = &E_fld(ed, param_ind) - pr->pr_globals; } if (!ed) { str = "bad entity.field"; break; } - str = global_string (pr, opval, optype, + str = global_string (&data, opval, optype, contents & 1); - str = va ("$%x $%x %s", s->a, s->b, str); + str = dsprintf (res->dva, "$%x $%x %s", + s->a, s->b, str); + } + break; + case 'M': + case 'm': + { + pr_ptr_t ptr = 0; + pr_int_t offs = 0; + switch ((opval >> shift) & 3) { + case 0: + ptr = s->a + PR_BASE (pr, s, A); + break; + case 1: + break; + case 2: + ptr = s->a + PR_BASE (pr, s, A); + ptr = G_POINTER (pr, ptr); + offs = (short) s->b; + break; + case 3: + ptr = s->a + PR_BASE (pr, s, A); + ptr = G_POINTER (pr, ptr); + offs = s->b + PR_BASE (pr, s, B); + offs = G_INT (pr, offs); + break; + } + ptr += offs; + str = global_string (&data, ptr, optype, + mode == 'M'); } break; default: goto err; } - dstring_appendstr (line, str); + dstring_appendstr (res->line, str); fmt += 3; continue; err: - dstring_appendstr (line, fmt); + dstring_appendstr (res->line, fmt); break; } } else { - dstring_appendsubstr (line, fmt++, 1); + dstring_appendsubstr (res->line, fmt++, 1); } } do_print: - Sys_Printf ("%s\n", line->str); + Sys_Printf ("%s\n", res->line->str); } static void dump_frame (progs_t *pr, prstack_t *frame) { - dfunction_t *f = frame->f ? frame->f->descriptor : 0; + prdeb_resources_t *res = pr->pr_debug_resources; + dfunction_t *f = frame->func ? frame->func->descriptor : 0; if (!f) { Sys_Printf ("\n"); return; } - if (pr_debug->int_val && pr->debug) { - pr_lineno_t *lineno = PR_Find_Lineno (pr, frame->s); + if (pr_debug && res->debug) { + pr_lineno_t *lineno = PR_Find_Lineno (pr, frame->staddr); pr_auxfunction_t *func = PR_Get_Lineno_Func (pr, lineno); pr_uint_t line = PR_Get_Lineno_Line (pr, lineno); - pr_int_t addr = PR_Get_Lineno_Addr (pr, lineno); + pr_uint_t addr = PR_Get_Lineno_Addr (pr, lineno); line += func->source_line; - if (addr == frame->s) { + if (addr == frame->staddr) { Sys_Printf ("%12s:%u : %s: %x\n", - PR_GetString (pr, f->s_file), + PR_GetString (pr, f->file), line, - PR_GetString (pr, f->s_name), - frame->s); + PR_GetString (pr, f->name), + frame->staddr); } else { Sys_Printf ("%12s:%u+%d : %s: %x\n", - PR_GetString (pr, f->s_file), - line, frame->s - addr, - PR_GetString (pr, f->s_name), - frame->s); + PR_GetString (pr, f->file), + line, frame->staddr - addr, + PR_GetString (pr, f->name), + frame->staddr); } } else { - Sys_Printf ("%12s : %s: %x\n", PR_GetString (pr, f->s_file), - PR_GetString (pr, f->s_name), frame->s); + Sys_Printf ("%12s : %s: %x\n", PR_GetString (pr, f->file), + PR_GetString (pr, f->name), frame->staddr); } } @@ -1001,8 +1840,8 @@ PR_StackTrace (progs_t *pr) return; } - top.s = pr->pr_xstatement; - top.f = pr->pr_xfunction; + top.staddr = pr->pr_xstatement; + top.func = pr->pr_xfunction; dump_frame (pr, &top); for (i = pr->pr_depth - 1; i >= 0; i--) dump_frame (pr, pr->pr_stack + i); @@ -1011,28 +1850,63 @@ PR_StackTrace (progs_t *pr) VISIBLE void PR_Profile (progs_t * pr) { - pr_int_t max, num, i; - dfunction_t *best, *f; + pr_ulong_t max, num, i; + pr_ulong_t total; + dfunction_t *f; + bfunction_t *best, *bf; num = 0; + total = 0; + for (i = 0; i < pr->progs->functions.count; i++) { + bf = &pr->function_table[i]; + total += bf->profile; + } do { max = 0; best = NULL; - for (i = 0; i < pr->progs->numfunctions; i++) { - f = &pr->pr_functions[i]; - if (f->profile > max) { - max = f->profile; - best = f; + for (i = 0; i < pr->progs->functions.count; i++) { + bf = &pr->function_table[i]; + if (bf->profile > max) { + max = bf->profile; + best = bf; } } if (best) { - if (num < 10) - Sys_Printf ("%7i %s\n", best->profile, - PR_GetString (pr, best->s_name)); + if (num < 10) { + f = pr->pr_functions + (best - pr->function_table); + Sys_Printf ("%7"PRIu64" %s%s\n", best->profile, + PR_GetString (pr, f->name), + f->first_statement < 0 ? " (builtin)" : ""); + } num++; best->profile = 0; } } while (best); + Sys_Printf ("total: %7"PRIu64"\n", total); +} + +static void +print_field (progs_t *pr, edict_t *ed, pr_def_t *d) +{ + prdeb_resources_t *res = pr->pr_debug_resources; + int l; + pr_type_t *v; + qfot_type_t dummy_type = { }; + qfot_type_t *type; + pr_debug_data_t data = {pr, res->dstr}; + const char *name; + + name = PR_GetString (pr, d->name); + type = get_def_type (pr, d, &dummy_type); + v = &E_fld (ed, d->ofs); + + l = 15 - strlen (name); + if (l < 1) + l = 1; + + dstring_clearstr (res->dstr); + value_string (&data, type, v); + Sys_Printf ("%s%*s%s\n", name, l, "", res->dstr->str); } /* @@ -1041,13 +1915,12 @@ PR_Profile (progs_t * pr) For debugging */ VISIBLE void -ED_Print (progs_t *pr, edict_t *ed) +ED_Print (progs_t *pr, edict_t *ed, const char *fieldname) { - int type, l; - pr_uint_t i; + pr_uint_t i, j; const char *name; - ddef_t *d; - pr_type_t *v; + pr_def_t *d; + int l; if (ed->free) { Sys_Printf ("FREE\n"); @@ -1055,52 +1928,79 @@ ED_Print (progs_t *pr, edict_t *ed) } Sys_Printf ("\nEDICT %d:\n", NUM_FOR_BAD_EDICT (pr, ed)); - for (i = 0; i < pr->progs->numfielddefs; i++) { + + if (fieldname) { + d = PR_FindField(pr, fieldname); + if (!d) { + Sys_Printf ("unknown field '%s'\n", fieldname); + } else { + print_field (pr, ed, d); + } + return; + } + for (i = 0; i < pr->progs->fielddefs.count; i++) { d = &pr->pr_fielddefs[i]; - if (!d->s_name) // null field def (probably 1st) + if (!d->name) // null field def (probably 1st) continue; - name = PR_GetString (pr, d->s_name); - if (name[strlen (name) - 2] == '_' - && strchr ("xyz", name[strlen (name) -1])) + name = PR_GetString (pr, d->name); + l = strlen (name); + if (l >= 2 && name[l - 2] == '_' && strchr ("xyz", name[l - 1])) continue; // skip _x, _y, _z vars - v = ed->v + d->ofs; - - // if the value is still all 0, skip the field - type = d->type & ~DEF_SAVEGLOBAL; - - switch (type) { - case ev_entity: - case ev_integer: - case ev_uinteger: - case ev_pointer: - case ev_func: - case ev_field: - if (!v->integer_var) - continue; + qfot_type_t dummy_type = { }; + get_def_type (pr, d, &dummy_type); + for (j = 0; j < d->size; j++) { + if (E_INT (ed, d->ofs + j)) { break; - case ev_string: - if (PR_StringValid (pr, v->string_var)) - if (!PR_GetString (pr, v->string_var)[0]) - continue; - break; - case ev_float: - if (!v->float_var) - continue; - break; - case ev_vector: - if (!v[0].float_var && !v[1].float_var && !v[2].float_var) - continue; - break; - case ev_void: - break; - default: - PR_Error (pr, "ED_Print: Unhandled type %d", type); + } + } + if (j == d->size) { + continue; } - l = 15 - strlen (name); - if (l < 1) - l = 1; - Sys_Printf ("%s%*s%s\n", name, l, "", value_string (pr, d->type, v)); + print_field (pr, ed, d); } } + +void +PR_Debug_Init (progs_t *pr) +{ + prdeb_resources_t *res = calloc (1, sizeof (*res)); + res->pr = pr; + res->string = dstring_newstr (); + res->dva = dstring_newstr (); + res->line = dstring_newstr (); + res->dstr = dstring_newstr (); + res->va = va_create_context (8); + + res->void_type.meta = ty_basic; + res->void_type.size = 4; + res->void_type.encoding = 0; + res->void_type.type = ev_void; + for (int i = 0; i < ev_type_count; i++ ) { + res->type_encodings[i] = &res->void_type; + } + res->file_hash = Hash_NewTable (509, file_get_key, file_free, 0, + pr->hashctx); + res->debug_syms = Hash_NewTable (509, def_get_key, 0, pr, pr->hashctx); + res->compunits = Hash_NewTable (509, compunit_get_key, 0, pr, pr->hashctx); + + PR_Resources_Register (pr, "PR_Debug", res, pr_debug_clear, + pr_debug_destroy); + pr->pr_debug_resources = res; +} + +static void +pr_debug_shutdown (void *data) +{ + free (source_paths); + free (source_path_string); +} + +void +PR_Debug_Init_Cvars (void) +{ + Sys_RegisterShutdown (pr_debug_shutdown, 0); + Cvar_Register (&pr_debug_cvar, 0, 0); + Cvar_Register (&pr_source_path_cvar, source_path_f, 0); +} diff --git a/libs/gamecode/pr_edict.c b/libs/gamecode/pr_edict.c index 2241e3e9f..9a0479403 100644 --- a/libs/gamecode/pr_edict.c +++ b/libs/gamecode/pr_edict.c @@ -34,22 +34,10 @@ #ifdef HAVE_STRINGS_H # include #endif -#include -#include -#include "QF/cbuf.h" -#include "QF/crc.h" #include "QF/cvar.h" -#include "QF/dstring.h" -#include "QF/hash.h" -#include "QF/idparse.h" #include "QF/progs.h" -#include "QF/qdefs.h" -#include "QF/qendian.h" -#include "QF/quakefs.h" #include "QF/sys.h" -#include "QF/zone.h" -#include "QF/va.h" #include "compat.h" @@ -63,16 +51,14 @@ ED_ClearEdict (progs_t *pr, edict_t *e, int val) { pr_uint_t i; - if (NUM_FOR_EDICT (pr, e) < *pr->reserved_edicts) + if (pr->reserved_edicts && NUM_FOR_EDICT (pr, e) < *pr->reserved_edicts) Sys_Printf ("clearing reserved edict %d\n", NUM_FOR_EDICT (pr, e)); for (i=0; i < pr->progs->entityfields; i++) - e->v[i].integer_var = val; + E_INT (e, i) = val; e->free = false; } /* - ED_Alloc - Either finds a free edict, or allocates a new one. Try to avoid reusing an entity that was recently freed, because it can cause the client to think the entity morphed into something else @@ -82,17 +68,20 @@ ED_ClearEdict (progs_t *pr, edict_t *e, int val) VISIBLE edict_t * ED_Alloc (progs_t *pr) { - pr_int_t i; + pr_uint_t i; edict_t *e; int start = pr->reserved_edicts ? *pr->reserved_edicts : 0; + if (!pr->num_edicts) { + PR_RunError (pr, "Edicts not supported in this VM\n"); + } for (i = start + 1; i < *pr->num_edicts; i++) { e = EDICT_NUM (pr, i); // the first couple seconds of server time can involve a lot of // freeing and allocating, so relax the replacement policy - if (e->free && (!pr->globals.time + if (e->free && (!pr->globals.ftime//FIXME double time || e->freetime < 2 - || *pr->globals.time - e->freetime > 0.5)) { + || *pr->globals.ftime - e->freetime > 0.5)) { ED_ClearEdict (pr, e, 0); return e; } @@ -125,7 +114,7 @@ ED_Free (progs_t *pr, edict_t *ed) if (pr->unlink) pr->unlink (ed); // unlink from world bsp - if (pr_deadbeef_ents->int_val) { + if (pr_deadbeef_ents) { ED_ClearEdict (pr, ed, 0xdeadbeef); } else { if (pr->free_edict) @@ -134,8 +123,8 @@ ED_Free (progs_t *pr, edict_t *ed) ED_ClearEdict (pr, ed, 0); } ed->free = true; - if (pr->globals.time) - ed->freetime = *pr->globals.time; + if (pr->globals.ftime)//FIXME double time + ed->freetime = *pr->globals.ftime; } //=========================================================================== @@ -143,9 +132,13 @@ ED_Free (progs_t *pr, edict_t *ed) VISIBLE void -ED_PrintNum (progs_t *pr, pr_int_t ent) +ED_PrintNum (progs_t *pr, pr_int_t ent, const char *fieldname) { - ED_Print (pr, EDICT_NUM (pr, ent)); + if (!pr->num_edicts) { + Sys_Printf ("Edicts not supported in this VM\n"); + return; + } + ED_Print (pr, EDICT_NUM (pr, ent), fieldname); } /* @@ -156,25 +149,29 @@ ED_PrintNum (progs_t *pr, pr_int_t ent) VISIBLE void ED_PrintEdicts (progs_t *pr, const char *fieldval) { - pr_int_t i; + pr_uint_t i; int count; - ddef_t *def; + pr_def_t *def; def = PR_FindField(pr, "classname"); + if (!pr->num_edicts) { + Sys_Printf ("Edicts not supported in this VM\n"); + return; + } if (fieldval && fieldval[0] && def) { count = 0; - for (i = 0; i < *(pr)->num_edicts; i++) + for (i = 0; i < *pr->num_edicts; i++) if (strequal(fieldval, E_GSTRING (pr, EDICT_NUM(pr, i), def->ofs))) { - ED_PrintNum (pr, i); + ED_PrintNum (pr, i, 0); count++; } Sys_Printf ("%i entities\n", count); } else { - for (i = 0; i < *(pr)->num_edicts; i++) - ED_PrintNum (pr, i); - Sys_Printf ("%i entities\n", *(pr)->num_edicts); + for (i = 0; i < *pr->num_edicts; i++) + ED_PrintNum (pr, i, 0); + Sys_Printf ("%i entities\n", *pr->num_edicts); } } @@ -186,30 +183,33 @@ ED_PrintEdicts (progs_t *pr, const char *fieldval) VISIBLE void ED_Count (progs_t *pr) { - pr_int_t i; int active, models, solid, step, zombie; - ddef_t *solid_def; - ddef_t *model_def; + pr_def_t *solid_def; + pr_def_t *model_def; edict_t *ent; + if (!pr->num_edicts) { + Sys_Printf ("Edicts not supported in this VM\n"); + return; + } solid_def = PR_FindField (pr, "solid"); model_def = PR_FindField (pr, "model"); active = models = solid = step = zombie = 0; - for (i = 0; i < *(pr)->num_edicts; i++) { + for (pr_uint_t i = 0; i < *pr->num_edicts; i++) { ent = EDICT_NUM (pr, i); if (ent->free) { - if (pr->globals.time && *pr->globals.time - ent->freetime <= 0.5) + if (pr->globals.ftime && *pr->globals.ftime - ent->freetime <= 0.5)//FIXME double time zombie++; continue; } active++; - if (solid_def && ent->v[solid_def->ofs].float_var) + if (solid_def && E_FLOAT (ent, solid_def->ofs)) solid++; - if (model_def && ent->v[model_def->ofs].float_var) + if (model_def && E_FLOAT (ent, model_def->ofs)) models++; } - Sys_Printf ("num_edicts:%3i\n", *(pr)->num_edicts); + Sys_Printf ("num_edicts:%3i\n", *pr->num_edicts); Sys_Printf ("active :%3i\n", active); Sys_Printf ("view :%3i\n", models); Sys_Printf ("touch :%3i\n", solid); @@ -217,34 +217,35 @@ ED_Count (progs_t *pr) } edict_t * -ED_EdictNum (progs_t *pr, pr_int_t n) +ED_EdictNum (progs_t *pr, pr_uint_t n) { - pr_int_t offs = n * pr->pr_edict_size; - - if (offs < 0 || n >= pr->pr_edictareasize) + if (n >= *pr->num_edicts) PR_RunError (pr, "EDICT_NUM: bad number %d", n); - return PROG_TO_EDICT (pr, offs); + return PR_edicts(pr) + n; } -pr_int_t +pr_uint_t ED_NumForEdict (progs_t *pr, edict_t *e) { - pr_int_t b; + pr_uint_t b; b = NUM_FOR_BAD_EDICT (pr, e); - if (b && (b < 0 || b >= *(pr)->num_edicts)) + if (b && b >= *pr->num_edicts) PR_RunError (pr, "NUM_FOR_EDICT: bad pointer %d %p %p", b, e, - *(pr)->edicts); + pr->pr_edicts); return b; } -qboolean -PR_EdictValid (progs_t *pr, pr_int_t e) +bool +PR_EdictValid (progs_t *pr, pr_uint_t e) { - if (e < 0 || e >= pr->pr_edictareasize) + if (!pr->num_edicts) { + return false; + } + if (e >= pr->pr_edict_area_size) return false; if (e % pr->pr_edict_size) return false; diff --git a/libs/gamecode/pr_exec.c b/libs/gamecode/pr_exec.c index 579992271..639416c37 100644 --- a/libs/gamecode/pr_exec.c +++ b/libs/gamecode/pr_exec.c @@ -44,8 +44,26 @@ #include "QF/sys.h" #include "QF/zone.h" +#include "QF/simd/vec2d.h" +#include "QF/simd/vec2f.h" +#include "QF/simd/vec2i.h" +#include "QF/simd/vec4d.h" +#include "QF/simd/vec4f.h" +#include "QF/simd/vec4i.h" #include "compat.h" +const char *prdebug_names[] = { + [prd_none] = "none", + [prd_trace] = "trace", + [prd_breakpoint] = "breakpoint", + [prd_watchpoint] = "watchpoint", + [prd_subenter] = "subenter", + [prd_subexit] = "subexit", + [prd_begin] = "begin", + [prd_terminate] = "terminate", + [prd_runerror] = "runerror", + [prd_error] = "error", +}; /* PR_RunError @@ -55,54 +73,60 @@ VISIBLE void PR_RunError (progs_t * pr, const char *error, ...) { - dstring_t *string = dstring_new (); + dstring_t *string = dstring_new ();//FIXME leaks when debugging va_list argptr; va_start (argptr, error); dvsprintf (string, error, argptr); va_end (argptr); + if (pr->debug_handler) { + pr->debug_handler (prd_runerror, string->str, pr->debug_data); + // not expected to return, but if so, behave as if there was no handler + } + Sys_Printf ("%s\n", string->str); PR_DumpState (pr); // dump the stack so PR_Error can shutdown functions pr->pr_depth = 0; + pr->localstack_used = 0; PR_Error (pr, "Program error: %s", string->str); } -VISIBLE void -PR_SaveParams (progs_t *pr) +VISIBLE pr_stashed_params_t * +_PR_SaveParams (progs_t *pr, pr_stashed_params_t *params) { int i; int size = pr->pr_param_size * sizeof (pr_type_t); - pr->pr_param_ptrs[0] = pr->pr_params[0]; - pr->pr_param_ptrs[1] = pr->pr_params[1]; + params->param_ptrs[0] = pr->pr_params[0]; + params->param_ptrs[1] = pr->pr_params[1]; pr->pr_params[0] = pr->pr_real_params[0]; pr->pr_params[1] = pr->pr_real_params[1]; for (i = 0; i < pr->pr_argc; i++) { - memcpy (pr->pr_saved_params + i * pr->pr_param_size, + memcpy (params->params + i * pr->pr_param_size, pr->pr_real_params[i], size); - if (i < 2) - memcpy (pr->pr_real_params[i], pr->pr_param_ptrs[0], size); } - pr->pr_saved_argc = pr->pr_argc; + params->argc = pr->pr_argc; + return params; } VISIBLE void -PR_RestoreParams (progs_t *pr) +PR_RestoreParams (progs_t *pr, pr_stashed_params_t *params) { int i; int size = pr->pr_param_size * sizeof (pr_type_t); - pr->pr_params[0] = pr->pr_param_ptrs[0]; - pr->pr_params[1] = pr->pr_param_ptrs[1]; - pr->pr_argc = pr->pr_saved_argc; - for (i = 0; i < pr->pr_argc; i++) + pr->pr_params[0] = params->param_ptrs[0]; + pr->pr_params[1] = params->param_ptrs[1]; + pr->pr_argc = params->argc; + for (i = 0; i < pr->pr_argc; i++) { memcpy (pr->pr_real_params[i], - pr->pr_saved_params + i * pr->pr_param_size, size); + params->params + i * pr->pr_param_size, size); + } } VISIBLE inline void @@ -110,16 +134,22 @@ PR_PushFrame (progs_t *pr) { prstack_t *frame; - if (pr->pr_depth == MAX_STACK_DEPTH) + if (pr->pr_depth == PR_MAX_STACK_DEPTH) PR_RunError (pr, "stack overflow"); frame = pr->pr_stack + pr->pr_depth++; - frame->s = pr->pr_xstatement; - frame->f = pr->pr_xfunction; - frame->tstr = pr->pr_xtstr; + frame->staddr = pr->pr_xstatement; + if (pr->globals.stack) { + frame->stack_ptr = *pr->globals.stack; + } + frame->bases = pr->pr_bases; + frame->func = pr->pr_xfunction; + frame->tstr = pr->pr_xtstr; + frame->return_ptr = pr->pr_return; - pr->pr_xtstr = 0; + pr->pr_xtstr = pr->pr_pushtstr; + pr->pr_pushtstr = 0; pr->pr_xfunction = 0; } @@ -133,13 +163,45 @@ PR_PopFrame (progs_t *pr) if (pr->pr_xtstr) PR_FreeTempStrings (pr); + // normally, this won't happen, but if a builtin pushed a temp string + // when calling a function and the callee was another builtin that + // did not call a progs function, then the push strings will still be + // valid because PR_EnterFunction was never called + // however, not if a temp string survived: better to hold on to the push + // strings a little longer than lose one erroneously + if (!pr->pr_xtstr && pr->pr_pushtstr) { + pr->pr_xtstr = pr->pr_pushtstr; + pr->pr_pushtstr = 0; + PR_FreeTempStrings (pr); + } // up stack frame = pr->pr_stack + --pr->pr_depth; - pr->pr_xfunction = frame->f; - pr->pr_xstatement = frame->s; + pr->pr_return = frame->return_ptr; + pr->pr_xfunction = frame->func; + pr->pr_xstatement = frame->staddr; pr->pr_xtstr = frame->tstr; + pr->pr_bases = frame->bases; + // restore data stack (discard any locals) + if (pr->globals.stack) { + *pr->globals.stack = frame->stack_ptr; + } +} + +static __attribute__((pure)) long +align_offset (long offset, dparmsize_t paramsize) +{ + int mask = (1 << paramsize.alignment) - 1; + return (offset + mask) & ~mask; +} + +static void +copy_param (pr_type_t *dst, pr_type_t *src, size_t size) +{ + while (size--) { + memcpy (dst++, src++, sizeof (pr_type_t)); + } } /** Setup the stackframe prior to calling a progs function. Saves all local @@ -153,117 +215,146 @@ PR_PopFrame (progs_t *pr) static void PR_EnterFunction (progs_t *pr, bfunction_t *f) { - pr_int_t i, j, c, o; - pr_int_t k; - pr_int_t count = 0; - int size[2] = {0, 0}; - long paramofs = 0; - long offs; + pr_int_t i; + pr_type_t *dstParams[PR_MAX_PARAMS]; + pr_ptr_t paramofs = 0; + + if (pr->pr_trace && !pr->debug_handler) { + Sys_Printf ("Entering function %s\n", + PR_GetString (pr, f->descriptor->name)); + } PR_PushFrame (pr); - if (f->numparms > 0) { - for (i = 0; i < 2 && i < f->numparms; i++) { - paramofs += f->parm_size[i]; - size[i] = f->parm_size[i]; - } - count = i; - } else if (f->numparms < 0) { - for (i = 0; i < 2 && i < -f->numparms - 1; i++) { - paramofs += f->parm_size[i]; - size[i] = f->parm_size[i]; - } - for (; i < 2; i++) { - paramofs += pr->pr_param_size; - size[i] = pr->pr_param_size; - } - count = i; - } - - for (i = 0; i < count && i < pr->pr_argc; i++) { - offs = (pr->pr_params[i] - pr->pr_globals) - f->parm_start; - if (offs >= 0 && offs < paramofs) { - memcpy (pr->pr_real_params[i], pr->pr_params[i], - size[i] * sizeof (pr_type_t)); - pr->pr_params[i] = pr->pr_real_params[i]; - } - } - - //Sys_Printf("%s:\n", PR_GetString(pr,f->s_name)); + //Sys_Printf("%s:\n", PR_GetString(pr,f->name)); pr->pr_xfunction = f; pr->pr_xstatement = f->first_statement - 1; // offset the st++ + if (pr->progs->version == PROG_VERSION) { + return; + } + + if (f->numparams > 0) { + paramofs = f->params_start; + for (i = 0; i < f->numparams; i++) { + paramofs = align_offset (paramofs, f->param_size[i]); + dstParams[i] = pr->pr_globals + paramofs; + paramofs += f->param_size[i].size; + if (pr->pr_params[i] != pr->pr_real_params[i]) { + copy_param (pr->pr_real_params[i], pr->pr_params[i], + f->param_size[i].size); + pr->pr_params[i] = pr->pr_real_params[i]; + } + } + } else if (f->numparams < 0) { + paramofs = f->params_start + 2; // argc and argv + for (i = 0; i < -f->numparams - 1; i++) { + paramofs = align_offset (paramofs, f->param_size[i]); + dstParams[i] = pr->pr_globals + paramofs; + paramofs += f->param_size[i].size; + if (pr->pr_params[i] != pr->pr_real_params[i]) { + copy_param (pr->pr_real_params[i], pr->pr_params[i], + f->param_size[i].size); + pr->pr_params[i] = pr->pr_real_params[i]; + } + } + dparmsize_t paramsize = { pr->pr_param_size, pr->pr_param_alignment }; + paramofs = align_offset (paramofs, paramsize ); + if (i < PR_MAX_PARAMS) { + dstParams[i] = pr->pr_globals + paramofs; + } + for (; i < pr->pr_argc; i++) { + if (pr->pr_params[i] != pr->pr_real_params[i]) { + copy_param (pr->pr_real_params[i], pr->pr_params[i], + paramsize.size); + pr->pr_params[i] = pr->pr_real_params[i]; + } + } + } + // save off any locals that the new function steps on - c = f->locals; - if (pr->localstack_used + c > LOCALSTACK_SIZE) + if (pr->localstack_used + f->locals > PR_LOCAL_STACK_SIZE) PR_RunError (pr, "PR_EnterFunction: locals stack overflow"); memcpy (&pr->localstack[pr->localstack_used], - &pr->pr_globals[f->parm_start], - sizeof (pr_type_t) * c); - pr->localstack_used += c; + &pr->pr_globals[f->params_start], + sizeof (pr_type_t) * f->locals); + pr->localstack_used += f->locals; - if (pr_deadbeef_locals->int_val) - for (k = f->parm_start; k < f->parm_start + c; k++) - pr->pr_globals[k].integer_var = 0xdeadbeef; + if (pr_deadbeef_locals) { + for (pr_uint_t i = f->params_start; + i < f->params_start + f->locals; i++) { + pr->pr_globals[i].value = 0xdeadbeef; + } + } // copy parameters - o = f->parm_start; - if (f->numparms >= 0) { - for (i = 0; i < f->numparms; i++) { - for (j = 0; j < f->parm_size[i]; j++) { - memcpy (&pr->pr_globals[o], &P_INT (pr, i) + j, - sizeof (pr_type_t)); - o++; - } + if (f->numparams >= 0) { + for (i = 0; i < f->numparams; i++) { + copy_param (dstParams[i], pr->pr_params[i], f->param_size[i].size); } } else { - pr_type_t *argc = &pr->pr_globals[o++]; - pr_type_t *argv = &pr->pr_globals[o++]; - for (i = 0; i < -f->numparms - 1; i++) { - for (j = 0; j < f->parm_size[i]; j++) { - memcpy (&pr->pr_globals[o], &P_INT (pr, i) + j, - sizeof (pr_type_t)); - o++; - } + int copy_args; + pr_type_t *argc = &pr->pr_globals[f->params_start + 0]; + pr_type_t *argv = &pr->pr_globals[f->params_start + 1]; + for (i = 0; i < -f->numparams - 1; i++) { + copy_param (dstParams[i], pr->pr_params[i], f->param_size[i].size); } - argc->integer_var = pr->pr_argc - i; - argv->integer_var = o; - if (i < MAX_PARMS) { - memcpy (&pr->pr_globals[o], &P_INT (pr, i), - (MAX_PARMS - i) * pr->pr_param_size * sizeof (pr_type_t)); + copy_args = pr->pr_argc - i; + PR_PTR (int, argc) = copy_args; + PR_PTR (ptr, argv) = PR_SetPointer (pr, dstParams[i]); + if (i < PR_MAX_PARAMS) { + memcpy (dstParams[i], pr->pr_params[i], + (copy_args * pr->pr_param_size) * sizeof (pr_type_t)); } } } static void -PR_LeaveFunction (progs_t *pr) +PR_LeaveFunction (progs_t *pr, int to_engine) { - int c; bfunction_t *f = pr->pr_xfunction; PR_PopFrame (pr); + if (pr->pr_trace && !pr->debug_handler) { + Sys_Printf ("Leaving function %s\n", + PR_GetString (pr, f->descriptor->name)); + if (to_engine) { + Sys_Printf ("Returning to engine\n"); + } else { + bfunction_t *rf = pr->pr_xfunction; + if (rf) { + Sys_Printf ("Returning to function %s\n", + PR_GetString (pr, rf->descriptor->name)); + } + } + } + + if (pr->progs->version == PROG_VERSION) { + return; + } + // restore locals from the stack - c = f->locals; - pr->localstack_used -= c; + pr->localstack_used -= f->locals; if (pr->localstack_used < 0) PR_RunError (pr, "PR_LeaveFunction: locals stack underflow"); - memcpy (&pr->pr_globals[f->parm_start], - &pr->localstack[pr->localstack_used], sizeof (pr_type_t) * c); + memcpy (&pr->pr_globals[f->params_start], + &pr->localstack[pr->localstack_used], + sizeof (pr_type_t) * f->locals); } VISIBLE void -PR_BoundsCheckSize (progs_t *pr, pointer_t addr, unsigned size) +PR_BoundsCheckSize (progs_t *pr, pr_ptr_t addr, unsigned size) { - if (addr < (pointer_t) (pr->pr_return - pr->pr_globals)) + if (addr < pr->null_size) PR_RunError (pr, "null pointer access"); if (addr >= pr->globals_size || size > (unsigned) (pr->globals_size - addr)) PR_RunError (pr, "invalid memory access: %d (0 to %d-%d)", addr, pr->globals_size, size); - if (pr_boundscheck->int_val >= 2 + if (pr_boundscheck >= 2 && PR_GetPointer (pr, addr + size) > (pr_type_t *) pr->zone) { void *mem = (void *) PR_GetPointer (pr, addr); Z_CheckPointer (pr->zone, mem, size * sizeof (pr_type_t)); @@ -276,23 +367,22 @@ PR_BoundsCheck (progs_t *pr, int addr, etype_t type) PR_BoundsCheckSize (pr, addr, pr_type_size[type]); } -#define OPA (*op_a) -#define OPB (*op_b) -#define OPC (*op_c) +#define OPA(type) (*((pr_##type##_t *) (op_a))) +#define OPB(type) (*((pr_##type##_t *) (op_b))) +#define OPC(type) (*((pr_##type##_t *) (op_c))) /* This gets around the problem of needing to test for -0.0 but denormals causing exceptions (or wrong results for what we need) on the alpha. */ -#define FNZ(x) ((x).uinteger_var && (x).uinteger_var != 0x80000000u) - +#define FNZ(x) ((x) & ~0x80000000u) static int signal_hook (int sig, void *data) { progs_t *pr = (progs_t *) data; - if (sig == SIGFPE && pr_faultchecks->int_val) { + if (sig == SIGFPE && pr_faultchecks) { dstatement_t *st; pr_type_t *op_a, *op_b, *op_c; @@ -302,22 +392,24 @@ signal_hook (int sig, void *data) op_c = pr->pr_globals + st->c; switch (st->op) { - case OP_DIV_F: - if ((OPA.integer_var & 0x80000000) - ^ (OPB.integer_var & 0x80000000)) - OPC.integer_var = 0xff7fffff; + case OP_DIV_F_v6p: + if ((OPA(int) & 0x80000000) + ^ (OPB(int) & 0x80000000)) + OPC(int) = 0xff7fffff; else - OPC.integer_var = 0x7f7fffff; + OPC(int) = 0x7f7fffff; return 1; - case OP_DIV_I: - if (OPA.integer_var & 0x80000000) - OPC.integer_var = -0x80000000; + case OP_DIV_I_v6p: + if (OPA(int) & 0x80000000) + OPC(int) = -0x80000000; else - OPC.integer_var = 0x7fffffff; + OPC(int) = 0x7fffffff; return 1; - case OP_MOD_I: - case OP_MOD_F: - OPC.integer_var = 0x00000000; + case OP_MOD_I_v6p: + case OP_MOD_F_v6p: + case OP_REM_I_v6p: + case OP_REM_F_v6p: + OPC(int) = 0x00000000; return 1; default: break; @@ -337,60 +429,113 @@ error_handler (void *data) } VISIBLE int -PR_CallFunction (progs_t *pr, func_t fnum) +PR_CallFunction (progs_t *pr, pr_func_t fnum, pr_type_t *return_ptr) { bfunction_t *f; if (!fnum) PR_RunError (pr, "NULL function"); + if (!return_ptr || return_ptr == pr->pr_globals) { + return_ptr = pr->pr_return_buffer; + } f = pr->function_table + fnum; if (f->first_statement < 0) { // negative statements are built in functions - if (pr->pr_trace) { - Sys_Printf ("Calling builtin %s @ %p\n", - PR_GetString (pr, f->descriptor->s_name), f->func); + if (pr->progs->version == PROG_VERSION) { + PR_SetupParams (pr, 0, 0); + } + if (pr->pr_trace && !pr->debug_handler) { + Sys_Printf ("Calling builtin %s @ %p\n", + PR_GetString (pr, f->descriptor->name), f->func); + } + pr_type_t *saved_return = pr->pr_return; + int builtin_depth = pr->pr_depth; + pr->pr_return = return_ptr; + f->func (pr, f->data); + f->profile++; // to show number times the builtin is called + if (builtin_depth == pr->pr_depth) { + pr->pr_return = saved_return; + } else if (builtin_depth < pr->pr_depth) { + pr->pr_stack[builtin_depth].return_ptr = saved_return; } - f->func (pr); return 0; } else { PR_EnterFunction (pr, f); + pr->pr_return = return_ptr; return 1; } } -/* - PR_ExecuteProgram - - The interpretation main loop -*/ -VISIBLE void -PR_ExecuteProgram (progs_t * pr, func_t fnum) +static void +check_stack_pointer (progs_t *pr, pr_ptr_t stack, int size) { - int exitdepth, profile, startprofile; + if (stack & 3) { + PR_RunError (pr, "Progs stack not aligned"); + } + if (stack < pr->stack_bottom) { + PR_RunError (pr, "Progs stack overflow"); + } + if (stack > pr->globals_size - size) { + PR_RunError (pr, "Progs stack underflow"); + } +} + +VISIBLE pr_type_t * +PR_SetupParams (progs_t *pr, int num_params, int min_alignment) +{ + if (pr->progs->version < PROG_VERSION) { + if (num_params > PR_MAX_PARAMS) { + PR_Error (pr, "attempt to settup more than %d params", + PR_MAX_PARAMS); + } + pr->pr_params[0] = pr->pr_real_params[0]; + pr->pr_params[1] = pr->pr_real_params[1]; + return pr->pr_real_params[0]; + } + int offset = num_params * 4; + if (min_alignment < 4) { + min_alignment = 4; + } + pr_ptr_t mask = ~(min_alignment - 1); + pr_ptr_t stack = (*pr->globals.stack - offset) & mask; + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 0); + } + *pr->globals.stack = stack; + pr->pr_params[0] = pr->pr_globals + stack; + num_params = max (num_params, PR_MAX_PARAMS); + for (int i = 1; i < num_params; i++) { + pr->pr_params[i] = pr->pr_params[0] + i * 4; + } + return pr->pr_params[0]; +} + +static inline void +pr_memset (pr_type_t *dst, int val, pr_uint_t count) +{ + while (count-- > 0) { + (*dst++).value = val; + } +} + +static void +pr_exec_quakec (progs_t *pr, int exitdepth) +{ + int profile, startprofile; + int fldofs; pr_uint_t pointer; dstatement_t *st; - edict_t *ed; pr_type_t *ptr; - pr_type_t old_val = {0}, *watch = 0; + pr_type_t old_val = {0}; // make a stack frame - exitdepth = pr->pr_depth; startprofile = profile = 0; - Sys_PushSignalHook (signal_hook, pr); - Sys_PushErrorHandler (error_handler, pr); - - if (!PR_CallFunction (pr, fnum)) { - // called a builtin instead of progs code - goto exit_program; - } st = pr->pr_statements + pr->pr_xstatement; if (pr->watch) { - watch = pr->watch; - old_val = *watch; + old_val = *pr->watch; } - while (1) { pr_type_t *op_a, *op_b, *op_c; @@ -406,568 +551,977 @@ PR_ExecuteProgram (progs_t * pr, func_t fnum) op_b = pr->pr_globals + st->b; op_c = pr->pr_globals + st->c; - if (pr->pr_trace) - PR_PrintStatement (pr, st, 1); + if (pr->pr_trace) { + if (pr->debug_handler) { + pr->debug_handler (prd_trace, 0, pr->debug_data); + } else { + PR_PrintStatement (pr, st, 1); + } + } - switch (st->op) { - case OP_ADD_F: - OPC.float_var = OPA.float_var + OPB.float_var; + if (st->op & OP_BREAK) { + if (pr->debug_handler) { + pr->debug_handler (prd_breakpoint, 0, pr->debug_data); + } else { + PR_RunError (pr, "breakpoint hit"); + } + } + + pr_opcode_v6p_e op = st->op & ~OP_BREAK; + switch (op) { + case OP_ADD_D_v6p: + OPC(double) = OPA(double) + OPB(double); break; - case OP_ADD_V: - VectorAdd (OPA.vector_var, OPB.vector_var, OPC.vector_var); + case OP_ADD_F_v6p: + OPC(float) = OPA(float) + OPB(float); break; - case OP_ADD_Q: - QuatAdd (OPA.quat_var, OPB.quat_var, OPC.quat_var); + case OP_ADD_V_v6p: + VectorAdd (&OPA(float), &OPB(float), &OPC(float)); break; - case OP_ADD_S: - OPC.string_var = PR_CatStrings (pr, + case OP_ADD_Q_v6p: + QuatAdd (&OPA(float), &OPB(float), &OPC(float)); + break; + case OP_ADD_S_v6p: + OPC(string) = PR_CatStrings (pr, PR_GetString (pr, - OPA.string_var), + OPA(string)), PR_GetString (pr, - OPB.string_var)); + OPB(string))); break; - case OP_SUB_F: - OPC.float_var = OPA.float_var - OPB.float_var; + case OP_SUB_D_v6p: + OPC(double) = OPA(double) - OPB(double); break; - case OP_SUB_V: - VectorSubtract (OPA.vector_var, OPB.vector_var, OPC.vector_var); + case OP_SUB_F_v6p: + OPC(float) = OPA(float) - OPB(float); break; - case OP_SUB_Q: - QuatSubtract (OPA.quat_var, OPB.quat_var, OPC.quat_var); + case OP_SUB_V_v6p: + VectorSubtract (&OPA(float), &OPB(float), + &OPC(float)); break; - case OP_MUL_F: - OPC.float_var = OPA.float_var * OPB.float_var; + case OP_SUB_Q_v6p: + QuatSubtract (&OPA(float), &OPB(float), &OPC(float)); break; - case OP_MUL_V: - OPC.float_var = DotProduct (OPA.vector_var, OPB.vector_var); + case OP_MUL_D_v6p: + OPC(double) = OPA(double) * OPB(double); break; - case OP_MUL_FV: + case OP_MUL_F_v6p: + OPC(float) = OPA(float) * OPB(float); + break; + case OP_MUL_V_v6p: + OPC(float) = DotProduct (&OPA(float), &OPB(float)); + break; + case OP_MUL_DV_v6p: { // avoid issues with the likes of x = x.x * x; // makes for faster code, too - float scale = OPA.float_var; - VectorScale (OPB.vector_var, scale, OPC.vector_var); + double scale = OPA(double); + VectorScale (&OPB(float), scale, &OPC(float)); } break; - case OP_MUL_VF: + case OP_MUL_VD_v6p: { // avoid issues with the likes of x = x * x.x; // makes for faster code, too - float scale = OPB.float_var; - VectorScale (OPA.vector_var, scale, OPC.vector_var); + double scale = OPB(double); + VectorScale (&OPA(float), scale, &OPC(float)); } break; - case OP_MUL_Q: - QuatMult (OPA.quat_var, OPB.quat_var, OPC.quat_var); + case OP_MUL_FV_v6p: + { + // avoid issues with the likes of x = x.x * x; + // makes for faster code, too + float scale = OPA(float); + VectorScale (&OPB(float), scale, &OPC(float)); + } break; - case OP_MUL_QV: - QuatMultVec (OPA.quat_var, OPB.vector_var, OPC.vector_var); + case OP_MUL_VF_v6p: + { + // avoid issues with the likes of x = x * x.x; + // makes for faster code, too + float scale = OPB(float); + VectorScale (&OPA(float), scale, &OPC(float)); + } break; - case OP_MUL_FQ: + case OP_MUL_Q_v6p: + QuatMult (&OPA(float), &OPB(float), &OPC(float)); + break; + case OP_MUL_QV_v6p: + QuatMultVec (&OPA(float), &OPB(float), &OPC(float)); + break; + case OP_MUL_DQ_v6p: { // avoid issues with the likes of x = x.s * x; // makes for faster code, too - float scale = OPA.float_var; - QuatScale (OPB.quat_var, scale, OPC.quat_var); + double scale = OPA(double); + QuatScale (&OPB(float), scale, &OPC(float)); } break; - case OP_MUL_QF: + case OP_MUL_QD_v6p: { // avoid issues with the likes of x = x * x.s; // makes for faster code, too - float scale = OPB.float_var; - QuatScale (OPA.quat_var, scale, OPC.quat_var); + double scale = OPB(double); + QuatScale (&OPA(float), scale, &OPC(float)); } break; - case OP_CONJ_Q: - QuatConj (OPA.quat_var, OPC.quat_var); - break; - case OP_DIV_F: - OPC.float_var = OPA.float_var / OPB.float_var; - break; - case OP_BITAND: - OPC.float_var = (int) OPA.float_var & (int) OPB.float_var; - break; - case OP_BITOR: - OPC.float_var = (int) OPA.float_var | (int) OPB.float_var; - break; - case OP_BITXOR_F: - OPC.float_var = (int) OPA.float_var ^ (int) OPB.float_var; - break; - case OP_BITNOT_F: - OPC.float_var = ~ (int) OPA.float_var; - break; - case OP_SHL_F: - OPC.float_var = (int) OPA.float_var << (int) OPB.float_var; - break; - case OP_SHR_F: - OPC.float_var = (int) OPA.float_var >> (int) OPB.float_var; - break; - case OP_SHL_I: - OPC.integer_var = OPA.integer_var << OPB.integer_var; - break; - case OP_SHR_I: - OPC.integer_var = OPA.integer_var >> OPB.integer_var; - break; - case OP_SHR_U: - OPC.uinteger_var = OPA.uinteger_var >> OPB.integer_var; - break; - case OP_GE_F: - OPC.float_var = OPA.float_var >= OPB.float_var; - break; - case OP_LE_F: - OPC.float_var = OPA.float_var <= OPB.float_var; - break; - case OP_GT_F: - OPC.float_var = OPA.float_var > OPB.float_var; - break; - case OP_LT_F: - OPC.float_var = OPA.float_var < OPB.float_var; - break; - case OP_AND: // OPA and OPB have to be float for -0.0 - OPC.integer_var = FNZ (OPA) && FNZ (OPB); - break; - case OP_OR: // OPA and OPB have to be float for -0.0 - OPC.integer_var = FNZ (OPA) || FNZ (OPB); - break; - case OP_NOT_F: - OPC.integer_var = !FNZ (OPA); - break; - case OP_NOT_V: - OPC.integer_var = VectorIsZero (OPA.vector_var); - break; - case OP_NOT_Q: - OPC.integer_var = QuatIsZero (OPA.quat_var); - break; - case OP_NOT_S: - OPC.integer_var = !OPA.string_var || - !*PR_GetString (pr, OPA.string_var); - break; - case OP_NOT_FN: - OPC.integer_var = !OPA.func_var; - break; - case OP_NOT_ENT: - OPC.integer_var = !OPA.entity_var; - break; - case OP_EQ_F: - OPC.integer_var = OPA.float_var == OPB.float_var; - break; - case OP_EQ_V: - OPC.integer_var = VectorCompare (OPA.vector_var, - OPB.vector_var); - break; - case OP_EQ_Q: - OPC.integer_var = QuatCompare (OPA.quat_var, OPB.quat_var); - break; - case OP_EQ_E: - OPC.integer_var = OPA.integer_var == OPB.integer_var; - break; - case OP_EQ_FN: - OPC.integer_var = OPA.func_var == OPB.func_var; - break; - case OP_NE_F: - OPC.integer_var = OPA.float_var != OPB.float_var; - break; - case OP_NE_V: - OPC.integer_var = !VectorCompare (OPA.vector_var, - OPB.vector_var); - break; - case OP_NE_Q: - OPC.integer_var = !QuatCompare (OPA.quat_var, OPB.quat_var); - break; - case OP_LE_S: - case OP_GE_S: - case OP_LT_S: - case OP_GT_S: - case OP_NE_S: - case OP_EQ_S: + case OP_MUL_FQ_v6p: { - int cmp = strcmp (PR_GetString (pr, OPA.string_var), - PR_GetString (pr, OPB.string_var)); + // avoid issues with the likes of x = x.s * x; + // makes for faster code, too + float scale = OPA(float); + QuatScale (&OPB(float), scale, &OPC(float)); + } + break; + case OP_MUL_QF_v6p: + { + // avoid issues with the likes of x = x * x.s; + // makes for faster code, too + float scale = OPB(float); + QuatScale (&OPA(float), scale, &OPC(float)); + } + break; + case OP_CONJ_Q_v6p: + QuatConj (&OPA(float), &OPC(float)); + break; + case OP_DIV_D_v6p: + OPC(double) = OPA(double) / OPB(double); + break; + case OP_DIV_F_v6p: + OPC(float) = OPA(float) / OPB(float); + break; + case OP_BITAND_v6p: + OPC(float) = (int) OPA(float) & (int) OPB(float); + break; + case OP_BITOR_v6p: + OPC(float) = (int) OPA(float) | (int) OPB(float); + break; + case OP_BITXOR_F_v6p: + OPC(float) = (int) OPA(float) ^ (int) OPB(float); + break; + case OP_BITNOT_F_v6p: + OPC(float) = ~ (int) OPA(float); + break; + case OP_SHL_F_v6p: + OPC(float) = (int) OPA(float) << (int) OPB(float); + break; + case OP_SHR_F_v6p: + OPC(float) = (int) OPA(float) >> (int) OPB(float); + break; + case OP_SHL_I_v6p: + OPC(int) = OPA(int) << OPB(int); + break; + case OP_SHR_I_v6p: + OPC(int) = OPA(int) >> OPB(int); + break; + case OP_SHR_U_v6p: + OPC(uint) = OPA(uint) >> OPB(int); + break; + case OP_GE_F_v6p: + OPC(float) = OPA(float) >= OPB(float); + break; + case OP_LE_F_v6p: + OPC(float) = OPA(float) <= OPB(float); + break; + case OP_GT_F_v6p: + OPC(float) = OPA(float) > OPB(float); + break; + case OP_LT_F_v6p: + OPC(float) = OPA(float) < OPB(float); + break; + case OP_AND_v6p: // OPA and OPB have to be float for -0.0 + OPC(int) = FNZ (OPA(uint)) && FNZ (OPB(uint)); + break; + case OP_OR_v6p: // OPA and OPB have to be float for -0.0 + OPC(int) = FNZ (OPA(uint)) || FNZ (OPB(uint)); + break; + case OP_NOT_F_v6p: + OPC(int) = !FNZ (OPA(uint)); + break; + case OP_NOT_V_v6p: + OPC(int) = VectorIsZero (&OPA(float)); + break; + case OP_NOT_Q_v6p: + OPC(int) = QuatIsZero (&OPA(float)); + break; + case OP_NOT_S_v6p: + OPC(int) = !OPA(string) || !*PR_GetString (pr, OPA(string)); + break; + case OP_NOT_FN_v6p: + OPC(int) = !OPA(func); + break; + case OP_NOT_ENT_v6p: + OPC(int) = !OPA(entity); + break; + case OP_EQ_F_v6p: + OPC(int) = OPA(float) == OPB(float); + break; + case OP_EQ_V_v6p: + OPC(int) = VectorCompare (&OPA(float), &OPB(float)); + break; + case OP_EQ_Q_v6p: + OPC(int) = QuatCompare (&OPA(float), &OPB(float)); + break; + case OP_EQ_E_v6p: + OPC(int) = OPA(field) == OPB(field); + break; + case OP_EQ_FN_v6p: + OPC(int) = OPA(func) == OPB(func); + break; + case OP_NE_F_v6p: + OPC(int) = OPA(float) != OPB(float); + break; + case OP_NE_V_v6p: + OPC(int) = !VectorCompare (&OPA(float), &OPB(float)); + break; + case OP_NE_Q_v6p: + OPC(int) = !QuatCompare (&OPA(float), &OPB(float)); + break; + case OP_LE_S_v6p: + case OP_GE_S_v6p: + case OP_LT_S_v6p: + case OP_GT_S_v6p: + case OP_NE_S_v6p: + case OP_EQ_S_v6p: + { + int cmp = strcmp (PR_GetString (pr, OPA(string)), + PR_GetString (pr, OPB(string))); switch (st->op) { - case OP_LE_S: cmp = (cmp <= 0); break; - case OP_GE_S: cmp = (cmp >= 0); break; - case OP_LT_S: cmp = (cmp < 0); break; - case OP_GT_S: cmp = (cmp > 0); break; - case OP_NE_S: break; - case OP_EQ_S: cmp = !cmp; break; + case OP_LE_S_v6p: cmp = (cmp <= 0); break; + case OP_GE_S_v6p: cmp = (cmp >= 0); break; + case OP_LT_S_v6p: cmp = (cmp < 0); break; + case OP_GT_S_v6p: cmp = (cmp > 0); break; + case OP_NE_S_v6p: break; + case OP_EQ_S_v6p: cmp = !cmp; break; default: break; } - OPC.integer_var = cmp; + OPC(int) = cmp; } break; - case OP_NE_E: - OPC.integer_var = OPA.integer_var != OPB.integer_var; + case OP_NE_E_v6p: + OPC(int) = OPA(entity) != OPB(entity); break; - case OP_NE_FN: - OPC.integer_var = OPA.func_var != OPB.func_var; + case OP_NE_FN_v6p: + OPC(int) = OPA(func) != OPB(func); break; // ================== - case OP_STORE_F: - case OP_STORE_ENT: - case OP_STORE_FLD: // integers - case OP_STORE_S: - case OP_STORE_FN: // pointers - case OP_STORE_I: - case OP_STORE_P: - OPB.integer_var = OPA.integer_var; + case OP_STORE_F_v6p: + case OP_STORE_ENT_v6p: + case OP_STORE_FLD_v6p: // integers + case OP_STORE_S_v6p: + case OP_STORE_FN_v6p: // pointers + case OP_STORE_I_v6p: + case OP_STORE_P_v6p: + OPB(int) = OPA(int); break; - case OP_STORE_V: - VectorCopy (OPA.vector_var, OPB.vector_var); + case OP_STORE_V_v6p: + VectorCopy (&OPA(float), &OPB(float)); break; - case OP_STORE_Q: - QuatCopy (OPA.quat_var, OPB.quat_var); + case OP_STORE_Q_v6p: + QuatCopy (&OPA(float), &OPB(float)); + break; + case OP_STORE_D_v6p: + OPB(double) = OPA(double); break; - case OP_STOREP_F: - case OP_STOREP_ENT: - case OP_STOREP_FLD: // integers - case OP_STOREP_S: - case OP_STOREP_FN: // pointers - case OP_STOREP_I: - case OP_STOREP_P: - pointer = OPB.integer_var; - if (pr_boundscheck->int_val) { - PR_BoundsCheck (pr, pointer, ev_integer); + case OP_STOREP_F_v6p: + case OP_STOREP_ENT_v6p: + case OP_STOREP_FLD_v6p: // integers + case OP_STOREP_S_v6p: + case OP_STOREP_FN_v6p: // pointers + case OP_STOREP_I_v6p: + case OP_STOREP_P_v6p: + pointer = OPB(ptr); + if (pr_boundscheck) { + PR_BoundsCheck (pr, pointer, ev_int); } ptr = pr->pr_globals + pointer; - ptr->integer_var = OPA.integer_var; + ptr->value = OPA(int); break; - case OP_STOREP_V: - pointer = OPB.integer_var; - if (pr_boundscheck->int_val) { + case OP_STOREP_V_v6p: + pointer = OPB(ptr); + if (pr_boundscheck) { PR_BoundsCheck (pr, pointer, ev_vector); } ptr = pr->pr_globals + pointer; - VectorCopy (OPA.vector_var, ptr->vector_var); + VectorCopy (&OPA(float), &PR_PTR (float, ptr)); break; - case OP_STOREP_Q: - pointer = OPB.integer_var; - if (pr_boundscheck->int_val) { - PR_BoundsCheck (pr, pointer, ev_quat); + case OP_STOREP_Q_v6p: + pointer = OPB(ptr); + if (pr_boundscheck) { + PR_BoundsCheck (pr, pointer, ev_quaternion); } ptr = pr->pr_globals + pointer; - QuatCopy (OPA.quat_var, ptr->quat_var); + QuatCopy (&OPA(float), &PR_PTR (float, ptr)); + break; + case OP_STOREP_D_v6p: + pointer = OPB(ptr); + if (pr_boundscheck) { + PR_BoundsCheck (pr, pointer, ev_double); + } + ptr = pr->pr_globals + pointer; + *(double *) ptr = OPA(double); break; - case OP_ADDRESS: - if (pr_boundscheck->int_val) { - if (OPA.entity_var < 0 - || OPA.entity_var >= pr->pr_edictareasize) + case OP_ADDRESS_v6p: + if (pr_boundscheck) { + if (OPA(entity) >= pr->pr_edict_area_size) PR_RunError (pr, "Progs attempted to address an out " "of bounds edict"); - if (OPA.entity_var == 0 && pr->null_bad) + if (OPA(entity) == 0 && pr->null_bad) PR_RunError (pr, "assignment to world entity"); - if (OPB.uinteger_var >= pr->progs->entityfields) + if (OPB(field) >= pr->progs->entityfields) PR_RunError (pr, "Progs attempted to address an " "invalid field in an edict"); } - ed = PROG_TO_EDICT (pr, OPA.entity_var); - OPC.integer_var = &ed->v[OPB.integer_var] - pr->pr_globals; + fldofs = OPA(entity) + OPB(field); + OPC(ptr) = &pr->pr_edict_area[fldofs] - pr->pr_globals; break; - case OP_ADDRESS_VOID: - case OP_ADDRESS_F: - case OP_ADDRESS_V: - case OP_ADDRESS_Q: - case OP_ADDRESS_S: - case OP_ADDRESS_ENT: - case OP_ADDRESS_FLD: - case OP_ADDRESS_FN: - case OP_ADDRESS_I: - case OP_ADDRESS_P: - OPC.integer_var = st->a; + case OP_ADDRESS_VOID_v6p: + case OP_ADDRESS_F_v6p: + case OP_ADDRESS_V_v6p: + case OP_ADDRESS_Q_v6p: + case OP_ADDRESS_S_v6p: + case OP_ADDRESS_ENT_v6p: + case OP_ADDRESS_FLD_v6p: + case OP_ADDRESS_FN_v6p: + case OP_ADDRESS_I_v6p: + case OP_ADDRESS_P_v6p: + case OP_ADDRESS_D_v6p: + OPC(int) = st->a; break; - case OP_LOAD_F: - case OP_LOAD_FLD: - case OP_LOAD_ENT: - case OP_LOAD_S: - case OP_LOAD_FN: - case OP_LOAD_I: - case OP_LOAD_P: - if (pr_boundscheck->int_val) { - if (OPA.entity_var < 0 - || OPA.entity_var >= pr->pr_edictareasize) + case OP_LOAD_F_v6p: + case OP_LOAD_FLD_v6p: + case OP_LOAD_ENT_v6p: + case OP_LOAD_S_v6p: + case OP_LOAD_FN_v6p: + case OP_LOAD_I_v6p: + case OP_LOAD_P_v6p: + if (pr_boundscheck) { + if (OPA(entity) >= pr->pr_edict_area_size) PR_RunError (pr, "Progs attempted to read an out of " "bounds edict number"); - if (OPB.uinteger_var >= pr->progs->entityfields) + if (OPB(field) >= pr->progs->entityfields) PR_RunError (pr, "Progs attempted to read an invalid " "field in an edict"); } - ed = PROG_TO_EDICT (pr, OPA.entity_var); - OPC.integer_var = ed->v[OPB.integer_var].integer_var; + fldofs = OPA(entity) + OPB(field); + OPC(int) = PR_PTR (int, &pr->pr_edict_area[fldofs]); break; - case OP_LOAD_V: - if (pr_boundscheck->int_val) { - if (OPA.entity_var < 0 - || OPA.entity_var >= pr->pr_edictareasize) + case OP_LOAD_V_v6p: + if (pr_boundscheck) { + if (OPA(entity) >= pr->pr_edict_area_size) PR_RunError (pr, "Progs attempted to read an out of " "bounds edict number"); - if (OPB.uinteger_var + 2 >= pr->progs->entityfields) + if (OPB(field) + 2 >= pr->progs->entityfields) PR_RunError (pr, "Progs attempted to read an invalid " "field in an edict"); } - ed = PROG_TO_EDICT (pr, OPA.entity_var); - memcpy (&OPC, &ed->v[OPB.integer_var], 3 * sizeof (OPC)); + fldofs = OPA(entity) + OPB(field); + memcpy (op_c, &pr->pr_edict_area[fldofs], 3 * sizeof (*op_c)); break; - case OP_LOAD_Q: - if (pr_boundscheck->int_val) { - if (OPA.entity_var < 0 - || OPA.entity_var >= pr->pr_edictareasize) + case OP_LOAD_Q_v6p: + if (pr_boundscheck) { + if (OPA(entity) >= pr->pr_edict_area_size) PR_RunError (pr, "Progs attempted to read an out of " "bounds edict number"); - if (OPB.uinteger_var + 3 >= pr->progs->entityfields) + if (OPB(field) + 3 >= pr->progs->entityfields) PR_RunError (pr, "Progs attempted to read an invalid " "field in an edict"); } - ed = PROG_TO_EDICT (pr, OPA.entity_var); - memcpy (&OPC, &ed->v[OPB.integer_var], 3 * sizeof (OPC)); + fldofs = OPA(entity) + OPB(field); + memcpy (op_c, &pr->pr_edict_area[fldofs], 4 * sizeof (*op_c)); + break; + case OP_LOAD_D_v6p: + if (pr_boundscheck) { + if (OPA(entity) >= pr->pr_edict_area_size) + PR_RunError (pr, "Progs attempted to read an out of " + "bounds edict number"); + if (OPB(field) + 1 >= pr->progs->entityfields) + PR_RunError (pr, "Progs attempted to read an invalid " + "field in an edict"); + } + fldofs = OPA(entity) + OPB(field); + memcpy (op_c, &pr->pr_edict_area[fldofs], sizeof (double)); break; - case OP_LOADB_F: - case OP_LOADB_S: - case OP_LOADB_ENT: - case OP_LOADB_FLD: - case OP_LOADB_FN: - case OP_LOADB_I: - case OP_LOADB_P: - pointer = OPA.integer_var + OPB.integer_var; - if (pr_boundscheck->int_val) { - PR_BoundsCheck (pr, pointer, ev_integer); + case OP_LOADB_F_v6p: + case OP_LOADB_S_v6p: + case OP_LOADB_ENT_v6p: + case OP_LOADB_FLD_v6p: + case OP_LOADB_FN_v6p: + case OP_LOADB_I_v6p: + case OP_LOADB_P_v6p: + pointer = OPA(entity) + OPB(field); + if (pr_boundscheck) { + PR_BoundsCheck (pr, pointer, ev_int); } ptr = pr->pr_globals + pointer; - OPC.integer_var = ptr->integer_var; + OPC(int) = ptr->value; break; - case OP_LOADB_V: - pointer = OPA.integer_var + OPB.integer_var; - if (pr_boundscheck->int_val) { + case OP_LOADB_V_v6p: + pointer = OPA(entity) + OPB(field); + if (pr_boundscheck) { + PR_BoundsCheck (pr, pointer, ev_vector); + } + VectorCopy (G_VECTOR (pr, pointer), &OPC(float)); + break; + case OP_LOADB_Q_v6p: + pointer = OPA(entity) + OPB(field); + if (pr_boundscheck) { + PR_BoundsCheck (pr, pointer, ev_quaternion); + } + QuatCopy (G_QUAT (pr, pointer), &OPC(float)); + break; + case OP_LOADB_D_v6p: + pointer = OPA(entity) + OPB(field); + if (pr_boundscheck) { + PR_BoundsCheck (pr, pointer, ev_double); + } + ptr = pr->pr_globals + pointer; + OPC(double) = *(double *) ptr; + break; + + case OP_LOADBI_F_v6p: + case OP_LOADBI_S_v6p: + case OP_LOADBI_ENT_v6p: + case OP_LOADBI_FLD_v6p: + case OP_LOADBI_FN_v6p: + case OP_LOADBI_I_v6p: + case OP_LOADBI_P_v6p: + pointer = OPA(ptr) + (short) st->b; + if (pr_boundscheck) { + PR_BoundsCheck (pr, pointer, ev_int); + } + ptr = pr->pr_globals + pointer; + OPC(int) = ptr->value; + break; + case OP_LOADBI_V_v6p: + pointer = OPA(ptr) + (short) st->b; + if (pr_boundscheck) { PR_BoundsCheck (pr, pointer, ev_vector); } ptr = pr->pr_globals + pointer; - VectorCopy (ptr->vector_var, OPC.vector_var); + VectorCopy (G_VECTOR (pr, pointer), &OPC(float)); break; - case OP_LOADB_Q: - pointer = OPA.integer_var + OPB.integer_var; - if (pr_boundscheck->int_val) { - PR_BoundsCheck (pr, pointer, ev_quat); + case OP_LOADBI_Q_v6p: + pointer = OPA(ptr) + (short) st->b; + if (pr_boundscheck) { + PR_BoundsCheck (pr, pointer, ev_quaternion); } ptr = pr->pr_globals + pointer; - QuatCopy (ptr->quat_var, OPC.quat_var); + QuatCopy (G_QUAT (pr, pointer), &OPC(float)); + break; + case OP_LOADBI_D_v6p: + pointer = OPA(ptr) + (short) st->b; + if (pr_boundscheck) { + PR_BoundsCheck (pr, pointer, ev_quaternion); + } + ptr = pr->pr_globals + pointer; + OPC(double) = *(double *) ptr; break; - case OP_LOADBI_F: - case OP_LOADBI_S: - case OP_LOADBI_ENT: - case OP_LOADBI_FLD: - case OP_LOADBI_FN: - case OP_LOADBI_I: - case OP_LOADBI_P: - pointer = OPA.integer_var + (short) st->b; - if (pr_boundscheck->int_val) { - PR_BoundsCheck (pr, pointer, ev_integer); + case OP_LEA_v6p: + pointer = OPA(ptr) + OPB(int); + OPC(ptr) = pointer; + break; + + case OP_LEAI_v6p: + pointer = OPA(ptr) + (short) st->b; + OPC(ptr) = pointer; + break; + + case OP_STOREB_F_v6p: + case OP_STOREB_S_v6p: + case OP_STOREB_ENT_v6p: + case OP_STOREB_FLD_v6p: + case OP_STOREB_FN_v6p: + case OP_STOREB_I_v6p: + case OP_STOREB_P_v6p: + pointer = OPB(ptr) + OPC(int); + if (pr_boundscheck) { + PR_BoundsCheck (pr, pointer, ev_int); } ptr = pr->pr_globals + pointer; - OPC.integer_var = ptr->integer_var; + ptr->value = OPA(int); break; - case OP_LOADBI_V: - pointer = OPA.integer_var + (short) st->b; - if (pr_boundscheck->int_val) { + case OP_STOREB_V_v6p: + pointer = OPB(ptr) + OPC(int); + if (pr_boundscheck) { PR_BoundsCheck (pr, pointer, ev_vector); } ptr = pr->pr_globals + pointer; - VectorCopy (ptr->vector_var, OPC.vector_var); + VectorCopy (&OPA(float), G_VECTOR (pr, pointer)); break; - case OP_LOADBI_Q: - pointer = OPA.integer_var + (short) st->b; - if (pr_boundscheck->int_val) { - PR_BoundsCheck (pr, pointer, ev_quat); + case OP_STOREB_Q_v6p: + pointer = OPB(ptr) + OPC(int); + if (pr_boundscheck) { + PR_BoundsCheck (pr, pointer, ev_quaternion); } ptr = pr->pr_globals + pointer; - QuatCopy (ptr->quat_var, OPC.quat_var); + QuatCopy (&OPA(float), G_QUAT (pr, pointer)); break; - - case OP_LEA: - pointer = OPA.integer_var + OPB.integer_var; - OPC.integer_var = pointer; - break; - - case OP_LEAI: - pointer = OPA.integer_var + (short) st->b; - OPC.integer_var = pointer; - break; - - case OP_STOREB_F: - case OP_STOREB_S: - case OP_STOREB_ENT: - case OP_STOREB_FLD: - case OP_STOREB_FN: - case OP_STOREB_I: - case OP_STOREB_P: - pointer = OPB.integer_var + OPC.integer_var; - if (pr_boundscheck->int_val) { - PR_BoundsCheck (pr, pointer, ev_integer); + case OP_STOREB_D_v6p: + pointer = OPB(ptr) + OPC(int); + if (pr_boundscheck) { + PR_BoundsCheck (pr, pointer, ev_quaternion); } ptr = pr->pr_globals + pointer; - ptr->integer_var = OPA.integer_var; + *(double *) ptr = OPA(double); break; - case OP_STOREB_V: - pointer = OPB.integer_var + OPC.integer_var; - if (pr_boundscheck->int_val) { + + case OP_STOREBI_F_v6p: + case OP_STOREBI_S_v6p: + case OP_STOREBI_ENT_v6p: + case OP_STOREBI_FLD_v6p: + case OP_STOREBI_FN_v6p: + case OP_STOREBI_I_v6p: + case OP_STOREBI_P_v6p: + pointer = OPB(ptr) + (short) st->c; + if (pr_boundscheck) { + PR_BoundsCheck (pr, pointer, ev_int); + } + ptr = pr->pr_globals + pointer; + ptr->value = OPA(int); + break; + case OP_STOREBI_V_v6p: + pointer = OPB(ptr) + (short) st->c; + if (pr_boundscheck) { PR_BoundsCheck (pr, pointer, ev_vector); } ptr = pr->pr_globals + pointer; - VectorCopy (OPA.vector_var, ptr->vector_var); + VectorCopy (&OPA(float), G_VECTOR (pr, pointer)); break; - case OP_STOREB_Q: - pointer = OPB.integer_var + OPC.integer_var; - if (pr_boundscheck->int_val) { - PR_BoundsCheck (pr, pointer, ev_quat); + case OP_STOREBI_Q_v6p: + pointer = OPB(ptr) + (short) st->c; + if (pr_boundscheck) { + PR_BoundsCheck (pr, pointer, ev_quaternion); } ptr = pr->pr_globals + pointer; - QuatCopy (OPA.quat_var, ptr->quat_var); + QuatCopy (&OPA(float), G_QUAT (pr, pointer)); + break; + case OP_STOREBI_D_v6p: + pointer = OPB(ptr) + (short) st->c; + if (pr_boundscheck) { + PR_BoundsCheck (pr, pointer, ev_quaternion); + } + ptr = pr->pr_globals + pointer; + *(double *) ptr = OPA(double); break; - case OP_STOREBI_F: - case OP_STOREBI_S: - case OP_STOREBI_ENT: - case OP_STOREBI_FLD: - case OP_STOREBI_FN: - case OP_STOREBI_I: - case OP_STOREBI_P: - pointer = OPB.integer_var + (short) st->c; - if (pr_boundscheck->int_val) { - PR_BoundsCheck (pr, pointer, ev_integer); + case OP_PUSH_F_v6p: + case OP_PUSH_FLD_v6p: + case OP_PUSH_ENT_v6p: + case OP_PUSH_S_v6p: + case OP_PUSH_FN_v6p: + case OP_PUSH_I_v6p: + case OP_PUSH_P_v6p: + { + pr_ptr_t stack = *pr->globals.stack - 1; + pr_type_t *stk = pr->pr_globals + stack; + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 1); + } + stk->value = OPA(int); + *pr->globals.stack = stack; } - ptr = pr->pr_globals + pointer; - ptr->integer_var = OPA.integer_var; break; - case OP_STOREBI_V: - pointer = OPB.integer_var + (short) st->c; - if (pr_boundscheck->int_val) { - PR_BoundsCheck (pr, pointer, ev_vector); + case OP_PUSH_V_v6p: + { + pr_ptr_t stack = *pr->globals.stack - 3; + pr_type_t *stk = pr->pr_globals + stack; + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 3); + } + memcpy (stk, op_a, 3 * sizeof (*op_c)); + *pr->globals.stack = stack; } - ptr = pr->pr_globals + pointer; - VectorCopy (OPA.vector_var, ptr->vector_var); break; - case OP_STOREBI_Q: - pointer = OPB.integer_var + (short) st->c; - if (pr_boundscheck->int_val) { - PR_BoundsCheck (pr, pointer, ev_quat); + case OP_PUSH_Q_v6p: + { + pr_ptr_t stack = *pr->globals.stack - 4; + pr_type_t *stk = pr->pr_globals + stack; + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 4); + } + memcpy (stk, op_a, 4 * sizeof (*op_c)); + *pr->globals.stack = stack; + } + break; + + case OP_PUSHB_F_v6p: + case OP_PUSHB_S_v6p: + case OP_PUSHB_ENT_v6p: + case OP_PUSHB_FLD_v6p: + case OP_PUSHB_FN_v6p: + case OP_PUSHB_I_v6p: + case OP_PUSHB_P_v6p: + { + pr_ptr_t stack = *pr->globals.stack - 1; + pr_type_t *stk = pr->pr_globals + stack; + + pointer = OPA(ptr) + OPB(int); + ptr = pr->pr_globals + pointer; + + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 1); + PR_BoundsCheck (pr, pointer, ev_int); + } + + stk->value = ptr->value; + *pr->globals.stack = stack; + } + break; + case OP_PUSHB_V_v6p: + { + pr_ptr_t stack = *pr->globals.stack - 3; + + pointer = OPA(ptr) + OPB(int); + ptr = pr->pr_globals + pointer; + + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 3); + PR_BoundsCheck (pr, pointer, ev_int); + } + + VectorCopy (G_VECTOR (pr, pointer), G_VECTOR (pr, stack)); + *pr->globals.stack = stack; + } + break; + case OP_PUSHB_Q_v6p: + { + pr_ptr_t stack = *pr->globals.stack - 4; + + pointer = OPA(ptr) + OPB(int); + ptr = pr->pr_globals + pointer; + + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 4); + PR_BoundsCheck (pr, pointer, ev_quaternion); + } + + QuatCopy (G_QUAT (pr, pointer), G_QUAT (pr, stack)); + *pr->globals.stack = stack; + } + break; + + case OP_PUSHBI_F_v6p: + case OP_PUSHBI_S_v6p: + case OP_PUSHBI_ENT_v6p: + case OP_PUSHBI_FLD_v6p: + case OP_PUSHBI_FN_v6p: + case OP_PUSHBI_I_v6p: + case OP_PUSHBI_P_v6p: + { + pr_ptr_t stack = *pr->globals.stack - 1; + pr_type_t *stk = pr->pr_globals + stack; + + pointer = OPA(ptr) + st->b; + ptr = pr->pr_globals + pointer; + + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 1); + PR_BoundsCheck (pr, pointer, ev_int); + } + + stk->value = ptr->value; + *pr->globals.stack = stack; + } + break; + case OP_PUSHBI_V_v6p: + { + pr_ptr_t stack = *pr->globals.stack - 3; + + pointer = OPA(ptr) + st->b; + ptr = pr->pr_globals + pointer; + + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 3); + PR_BoundsCheck (pr, pointer, ev_int); + } + + VectorCopy (G_VECTOR (pr, pointer), G_VECTOR (pr, stack)); + *pr->globals.stack = stack; + } + break; + case OP_PUSHBI_Q_v6p: + { + pr_ptr_t stack = *pr->globals.stack - 4; + + pointer = OPA(ptr) + st->b; + ptr = pr->pr_globals + pointer; + + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 4); + PR_BoundsCheck (pr, pointer, ev_quaternion); + } + + QuatCopy (G_QUAT (pr, pointer), G_QUAT (pr, stack)); + *pr->globals.stack = stack; + } + break; + + case OP_POP_F_v6p: + case OP_POP_FLD_v6p: + case OP_POP_ENT_v6p: + case OP_POP_S_v6p: + case OP_POP_FN_v6p: + case OP_POP_I_v6p: + case OP_POP_P_v6p: + { + pr_ptr_t stack = *pr->globals.stack; + pr_type_t *stk = pr->pr_globals + stack; + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 1); + } + OPA(int) = stk->value; + *pr->globals.stack = stack + 1; + } + break; + case OP_POP_V_v6p: + { + pr_ptr_t stack = *pr->globals.stack; + pr_type_t *stk = pr->pr_globals + stack; + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 3); + } + memcpy (op_a, stk, 3 * sizeof (*op_c)); + *pr->globals.stack = stack + 3; + } + break; + case OP_POP_Q_v6p: + { + pr_ptr_t stack = *pr->globals.stack; + pr_type_t *stk = pr->pr_globals + stack; + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 4); + } + memcpy (op_a, stk, 4 * sizeof (*op_c)); + *pr->globals.stack = stack + 4; + } + break; + + case OP_POPB_F_v6p: + case OP_POPB_S_v6p: + case OP_POPB_ENT_v6p: + case OP_POPB_FLD_v6p: + case OP_POPB_FN_v6p: + case OP_POPB_I_v6p: + case OP_POPB_P_v6p: + { + pr_ptr_t stack = *pr->globals.stack; + pr_type_t *stk = pr->pr_globals + stack; + + pointer = OPA(ptr) + OPB(int); + ptr = pr->pr_globals + pointer; + + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 1); + PR_BoundsCheck (pr, pointer, ev_int); + } + + ptr->value = stk->value; + *pr->globals.stack = stack + 1; + } + break; + case OP_POPB_V_v6p: + { + pr_ptr_t stack = *pr->globals.stack; + + pointer = OPA(ptr) + OPB(int); + ptr = pr->pr_globals + pointer; + + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 3); + PR_BoundsCheck (pr, pointer, ev_int); + } + + VectorCopy (G_VECTOR (pr, stack), G_VECTOR (pr, pointer)); + *pr->globals.stack = stack + 3; + } + break; + case OP_POPB_Q_v6p: + { + pr_ptr_t stack = *pr->globals.stack; + + pointer = OPA(ptr) + OPB(int); + ptr = pr->pr_globals + pointer; + + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 4); + PR_BoundsCheck (pr, pointer, ev_quaternion); + } + + QuatCopy (G_QUAT (pr, stack), G_QUAT (pr, pointer)); + *pr->globals.stack = stack + 4; + } + break; + + case OP_POPBI_F_v6p: + case OP_POPBI_S_v6p: + case OP_POPBI_ENT_v6p: + case OP_POPBI_FLD_v6p: + case OP_POPBI_FN_v6p: + case OP_POPBI_I_v6p: + case OP_POPBI_P_v6p: + { + pr_ptr_t stack = *pr->globals.stack; + pr_type_t *stk = pr->pr_globals + stack; + + pointer = OPA(ptr) + st->b; + ptr = pr->pr_globals + pointer; + + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 1); + PR_BoundsCheck (pr, pointer, ev_int); + } + + ptr->value = stk->value; + *pr->globals.stack = stack + 1; + } + break; + case OP_POPBI_V_v6p: + { + pr_ptr_t stack = *pr->globals.stack; + + pointer = OPA(ptr) + st->b; + ptr = pr->pr_globals + pointer; + + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 3); + PR_BoundsCheck (pr, pointer, ev_int); + } + + VectorCopy (G_VECTOR (pr, stack), G_VECTOR (pr, pointer)); + *pr->globals.stack = stack + 3; + } + break; + case OP_POPBI_Q_v6p: + { + pr_ptr_t stack = *pr->globals.stack; + + pointer = OPA(ptr) + st->b; + ptr = pr->pr_globals + pointer; + + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 4); + PR_BoundsCheck (pr, pointer, ev_quaternion); + } + + QuatCopy (G_QUAT (pr, stack), G_QUAT (pr, pointer)); + *pr->globals.stack = stack + 4; } - ptr = pr->pr_globals + pointer; - QuatCopy (OPA.quat_var, ptr->quat_var); break; // ================== - case OP_IFNOT: - if (!OPA.integer_var) { + case OP_IFNOT_v6p: + if (!OPA(int)) { pr->pr_xstatement += (short)st->b - 1; // offset the st++ st = pr->pr_statements + pr->pr_xstatement; } break; - case OP_IF: - if (OPA.integer_var) { + case OP_IF_v6p: + if (OPA(int)) { pr->pr_xstatement += (short)st->b - 1; // offset the st++ st = pr->pr_statements + pr->pr_xstatement; } break; - case OP_IFBE: - if (OPA.integer_var <= 0) { + case OP_IFBE_v6p: + if (OPA(int) <= 0) { pr->pr_xstatement += (short)st->b - 1; // offset the st++ st = pr->pr_statements + pr->pr_xstatement; } break; - case OP_IFB: - if (OPA.integer_var < 0) { + case OP_IFB_v6p: + if (OPA(int) < 0) { pr->pr_xstatement += (short)st->b - 1; // offset the st++ st = pr->pr_statements + pr->pr_xstatement; } break; - case OP_IFAE: - if (OPA.integer_var >= 0) { + case OP_IFAE_v6p: + if (OPA(int) >= 0) { pr->pr_xstatement += (short)st->b - 1; // offset the st++ st = pr->pr_statements + pr->pr_xstatement; } break; - case OP_IFA: - if (OPA.integer_var > 0) { + case OP_IFA_v6p: + if (OPA(int) > 0) { pr->pr_xstatement += (short)st->b - 1; // offset the st++ st = pr->pr_statements + pr->pr_xstatement; } break; - case OP_GOTO: + case OP_GOTO_v6p: pr->pr_xstatement += (short)st->a - 1; // offset the st++ st = pr->pr_statements + pr->pr_xstatement; break; - case OP_JUMP: - if (pr_boundscheck->int_val - && (OPA.uinteger_var >= pr->progs->numstatements)) { + case OP_JUMP_v6p: + if (pr_boundscheck + && (OPA(uint) >= pr->progs->statements.count)) { PR_RunError (pr, "Invalid jump destination"); } - pr->pr_xstatement = OPA.uinteger_var - 1; // offset the st++ + pr->pr_xstatement = OPA(uint) - 1; // offset the st++ st = pr->pr_statements + pr->pr_xstatement; break; - case OP_JUMPB: - pointer = st->a + OPB.integer_var; - if (pr_boundscheck->int_val) { - PR_BoundsCheck (pr, pointer, ev_integer); + case OP_JUMPB_v6p: + pointer = st->a + OPB(int); + if (pr_boundscheck) { + PR_BoundsCheck (pr, pointer, ev_int); } - ptr = pr->pr_globals + pointer; - pointer = ptr->integer_var; - if (pr_boundscheck->int_val - && (pointer >= pr->progs->numstatements)) { + pointer = G_POINTER (pr, pointer); + if (pr_boundscheck + && (pointer >= pr->progs->statements.count)) { PR_RunError (pr, "Invalid jump destination"); } pr->pr_xstatement = pointer - 1; // offset the st++ st = pr->pr_statements + pr->pr_xstatement; break; - case OP_RCALL2: - case OP_RCALL3: - case OP_RCALL4: - case OP_RCALL5: - case OP_RCALL6: - case OP_RCALL7: - case OP_RCALL8: - pr->pr_params[1] = &OPC; + case OP_RCALL2_v6p: + case OP_RCALL3_v6p: + case OP_RCALL4_v6p: + case OP_RCALL5_v6p: + case OP_RCALL6_v6p: + case OP_RCALL7_v6p: + case OP_RCALL8_v6p: + pr->pr_params[1] = op_c; goto op_rcall; - case OP_RCALL1: + case OP_RCALL1_v6p: pr->pr_params[1] = pr->pr_real_params[1]; op_rcall: - pr->pr_params[0] = &OPB; - pr->pr_argc = st->op - OP_RCALL1 + 1; + pr->pr_params[0] = op_b; + pr->pr_argc = st->op - OP_RCALL1_v6p + 1; goto op_call; - case OP_CALL0: - case OP_CALL1: - case OP_CALL2: - case OP_CALL3: - case OP_CALL4: - case OP_CALL5: - case OP_CALL6: - case OP_CALL7: - case OP_CALL8: + case OP_CALL0_v6p: + case OP_CALL1_v6p: + case OP_CALL2_v6p: + case OP_CALL3_v6p: + case OP_CALL4_v6p: + case OP_CALL5_v6p: + case OP_CALL6_v6p: + case OP_CALL7_v6p: + case OP_CALL8_v6p: PR_RESET_PARAMS (pr); - pr->pr_argc = st->op - OP_CALL0; + pr->pr_argc = st->op - OP_CALL0_v6p; op_call: pr->pr_xfunction->profile += profile - startprofile; startprofile = profile; - PR_CallFunction (pr, OPA.func_var); + PR_CallFunction (pr, OPA(func), pr->pr_return); st = pr->pr_statements + pr->pr_xstatement; break; - case OP_DONE: - case OP_RETURN: + case OP_DONE_v6p: + case OP_RETURN_v6p: if (!st->a) memset (&R_INT (pr), 0, - pr->pr_param_size * sizeof (OPA)); - else if (&R_INT (pr) != &OPA.integer_var) - memcpy (&R_INT (pr), &OPA, - pr->pr_param_size * sizeof (OPA)); + pr->pr_param_size * sizeof (*op_a)); + else if (&R_INT (pr) != &OPA(int)) + memcpy (&R_INT (pr), op_a, + pr->pr_param_size * sizeof (*op_a)); // fallthrough - case OP_RETURN_V: + case OP_RETURN_V_v6p: pr->pr_xfunction->profile += profile - startprofile; startprofile = profile; - PR_LeaveFunction (pr); + PR_LeaveFunction (pr, pr->pr_depth == exitdepth); st = pr->pr_statements + pr->pr_xstatement; if (pr->pr_depth == exitdepth) { if (pr->pr_trace && pr->pr_depth <= pr->pr_trace_depth) @@ -976,139 +1530,235 @@ op_call: goto exit_program; } break; - case OP_STATE: - ed = PROG_TO_EDICT (pr, *pr->globals.self); - ed->v[pr->fields.nextthink].float_var = *pr->globals.time + - 0.1; - ed->v[pr->fields.frame].float_var = OPA.float_var; - ed->v[pr->fields.think].func_var = OPB.func_var; - break; - case OP_STATE_F: - ed = PROG_TO_EDICT (pr, *pr->globals.self); - ed->v[pr->fields.nextthink].float_var = *pr->globals.time + - OPC.float_var; - ed->v[pr->fields.frame].float_var = OPA.float_var; - ed->v[pr->fields.think].func_var = OPB.func_var; - break; - case OP_ADD_I: - OPC.integer_var = OPA.integer_var + OPB.integer_var; - break; - case OP_SUB_I: - OPC.integer_var = OPA.integer_var - OPB.integer_var; - break; - case OP_MUL_I: - OPC.integer_var = OPA.integer_var * OPB.integer_var; - break; -/* - case OP_DIV_VF: + case OP_STATE_v6p: { - float temp = 1.0f / OPB.float_var; - VectorScale (OPA.vector_var, temp, OPC.vector_var); + int self = *pr->globals.self; + int nextthink = pr->fields.nextthink + self; + int frame = pr->fields.frame + self; + int think = pr->fields.think + self; + float time = *pr->globals.ftime + 0.1; + PR_PTR (float, &pr->pr_edict_area[nextthink]) = time; + PR_PTR (float, &pr->pr_edict_area[frame]) = OPA(float); + PR_PTR (func, &pr->pr_edict_area[think]) = OPB(func); } break; -*/ - case OP_DIV_I: - OPC.integer_var = OPA.integer_var / OPB.integer_var; - break; - case OP_MOD_I: - OPC.integer_var = OPA.integer_var % OPB.integer_var; - break; - case OP_MOD_F: - OPC.float_var = (int) OPA.float_var % (int) OPB.float_var; - break; - case OP_CONV_IF: - OPC.float_var = OPA.integer_var; - break; - case OP_CONV_FI: - OPC.integer_var = OPA.float_var; - break; - case OP_BITAND_I: - OPC.integer_var = OPA.integer_var & OPB.integer_var; - break; - case OP_BITOR_I: - OPC.integer_var = OPA.integer_var | OPB.integer_var; - break; - case OP_BITXOR_I: - OPC.integer_var = OPA.integer_var ^ OPB.integer_var; - break; - case OP_BITNOT_I: - OPC.integer_var = ~OPA.integer_var; - break; - - case OP_GE_I: - case OP_GE_P: - OPC.integer_var = OPA.integer_var >= OPB.integer_var; - break; - case OP_GE_U: - OPC.integer_var = OPA.uinteger_var >= OPB.uinteger_var; - break; - case OP_LE_I: - case OP_LE_P: - OPC.integer_var = OPA.integer_var <= OPB.integer_var; - break; - case OP_LE_U: - OPC.integer_var = OPA.uinteger_var <= OPB.uinteger_var; - break; - case OP_GT_I: - case OP_GT_P: - OPC.integer_var = OPA.integer_var > OPB.integer_var; - break; - case OP_GT_U: - OPC.integer_var = OPA.uinteger_var > OPB.uinteger_var; - break; - case OP_LT_I: - case OP_LT_P: - OPC.integer_var = OPA.integer_var < OPB.integer_var; - break; - case OP_LT_U: - OPC.integer_var = OPA.uinteger_var < OPB.uinteger_var; - break; - - case OP_AND_I: - OPC.integer_var = OPA.integer_var && OPB.integer_var; - break; - case OP_OR_I: - OPC.integer_var = OPA.integer_var || OPB.integer_var; - break; - case OP_NOT_I: - case OP_NOT_P: - OPC.integer_var = !OPA.integer_var; - break; - case OP_EQ_I: - case OP_EQ_P: - OPC.integer_var = OPA.integer_var == OPB.integer_var; - break; - case OP_NE_I: - case OP_NE_P: - OPC.integer_var = OPA.integer_var != OPB.integer_var; - break; - - case OP_MOVEI: - memmove (&OPC, &OPA, st->b * 4); - break; - case OP_MOVEP: - if (pr_boundscheck->int_val) { - PR_BoundsCheckSize (pr, OPC.integer_var, OPB.uinteger_var); - PR_BoundsCheckSize (pr, OPA.integer_var, OPB.uinteger_var); + case OP_STATE_F_v6p: + { + int self = *pr->globals.self; + int nextthink = pr->fields.nextthink + self; + int frame = pr->fields.frame + self; + int think = pr->fields.think + self; + float time = *pr->globals.ftime + OPC(float); + PR_PTR (float, &pr->pr_edict_area[nextthink]) = time; + PR_PTR (float, &pr->pr_edict_area[frame]) = OPA(float); + PR_PTR (func, &pr->pr_edict_area[think]) = OPB(func); } - memmove (pr->pr_globals + OPC.integer_var, - pr->pr_globals + OPA.integer_var, - OPB.uinteger_var * 4); break; - case OP_MOVEPI: - if (pr_boundscheck->int_val) { - PR_BoundsCheckSize (pr, OPC.integer_var, st->b); - PR_BoundsCheckSize (pr, OPA.integer_var, st->b); + case OP_ADD_I_v6p: + OPC(int) = OPA(int) + OPB(int); + break; + case OP_SUB_I_v6p: + OPC(int) = OPA(int) - OPB(int); + break; + case OP_MUL_I_v6p: + OPC(int) = OPA(int) * OPB(int); + break; + case OP_DIV_I_v6p: + OPC(int) = OPA(int) / OPB(int); + break; + case OP_MOD_I_v6p: + { + // implement true modulo for integers: + // 5 mod 3 = 2 + // -5 mod 3 = 1 + // 5 mod -3 = -1 + // -5 mod -3 = -2 + int a = OPA(int); + int b = OPB(int); + int c = a % b; + // % is really remainder and so has the same sign rules + // as division: -5 % 3 = -2, so need to add b (3 here) + // if c's sign is incorrect, but only if c is non-zero + int mask = (a ^ b) >> 31; + mask &= ~(!!c + 0) + 1; // +0 to convert bool to int (gcc) + OPC(int) = c + (mask & b); } - memmove (pr->pr_globals + OPC.integer_var, - pr->pr_globals + OPA.integer_var, + break; + case OP_REM_I_v6p: + OPC(int) = OPA(int) % OPB(int); + break; + case OP_MOD_D_v6p: + { + double a = OPA(double); + double b = OPB(double); + // floating point modulo is so much easier :P + OPC(double) = a - b * floor (a / b); + } + break; + case OP_REM_D_v6p: + { + double a = OPA(double); + double b = OPB(double); + OPC(double) = a - b * trunc (a / b); + } + break; + case OP_MOD_F_v6p: + { + float a = OPA(float); + float b = OPB(float); + OPC(float) = a - b * floorf (a / b); + } + break; + case OP_REM_F_v6p: + { + float a = OPA(float); + float b = OPB(float); + OPC(float) = a - b * truncf (a / b); + } + break; + case OP_CONV_IF_v6p: + OPC(float) = OPA(int); + break; + case OP_CONV_FI_v6p: + OPC(int) = OPA(float); + break; + case OP_BITAND_I_v6p: + OPC(int) = OPA(int) & OPB(int); + break; + case OP_BITOR_I_v6p: + OPC(int) = OPA(int) | OPB(int); + break; + case OP_BITXOR_I_v6p: + OPC(int) = OPA(int) ^ OPB(int); + break; + case OP_BITNOT_I_v6p: + OPC(int) = ~OPA(int); + break; + + case OP_GE_I_v6p: + case OP_GE_P_v6p: + OPC(int) = OPA(int) >= OPB(int); + break; + case OP_GE_U_v6p: + OPC(int) = OPA(uint) >= OPB(uint); + break; + case OP_LE_I_v6p: + case OP_LE_P_v6p: + OPC(int) = OPA(int) <= OPB(int); + break; + case OP_LE_U_v6p: + OPC(int) = OPA(uint) <= OPB(uint); + break; + case OP_GT_I_v6p: + case OP_GT_P_v6p: + OPC(int) = OPA(int) > OPB(int); + break; + case OP_GT_U_v6p: + OPC(int) = OPA(uint) > OPB(uint); + break; + case OP_LT_I_v6p: + case OP_LT_P_v6p: + OPC(int) = OPA(int) < OPB(int); + break; + case OP_LT_U_v6p: + OPC(int) = OPA(uint) < OPB(uint); + break; + + case OP_AND_I_v6p: + OPC(int) = OPA(int) && OPB(int); + break; + case OP_OR_I_v6p: + OPC(int) = OPA(int) || OPB(int); + break; + case OP_NOT_I_v6p: + case OP_NOT_P_v6p: + OPC(int) = !OPA(int); + break; + case OP_EQ_I_v6p: + case OP_EQ_P_v6p: + OPC(int) = OPA(int) == OPB(int); + break; + case OP_NE_I_v6p: + case OP_NE_P_v6p: + OPC(int) = OPA(int) != OPB(int); + break; + + case OP_MOVEI_v6p: + memmove (op_c, op_a, st->b * 4); + break; + case OP_MOVEP_v6p: + if (pr_boundscheck) { + PR_BoundsCheckSize (pr, OPC(ptr), OPB(uint)); + PR_BoundsCheckSize (pr, OPA(ptr), OPB(uint)); + } + memmove (pr->pr_globals + OPC(ptr), + pr->pr_globals + OPA(ptr), + OPB(uint) * 4); + break; + case OP_MOVEPI_v6p: + if (pr_boundscheck) { + PR_BoundsCheckSize (pr, OPC(ptr), st->b); + PR_BoundsCheckSize (pr, OPA(ptr), st->b); + } + memmove (pr->pr_globals + OPC(ptr), + pr->pr_globals + OPA(ptr), st->b * 4); break; + case OP_MEMSETI_v6p: + pr_memset (op_c, OPA(ptr), st->b); + break; + case OP_MEMSETP_v6p: + if (pr_boundscheck) { + PR_BoundsCheckSize (pr, OPC(ptr), OPB(uint)); + } + pr_memset (pr->pr_globals + OPC(ptr), OPA(int), + OPB(uint)); + break; + case OP_MEMSETPI_v6p: + if (pr_boundscheck) { + PR_BoundsCheckSize (pr, OPC(ptr), st->b); + } + pr_memset (pr->pr_globals + OPC(ptr), OPA(int), + st->b); + break; + case OP_GE_D_v6p: + OPC(float) = OPA(double) >= OPB(double); + break; + case OP_LE_D_v6p: + OPC(float) = OPA(double) <= OPB(double); + break; + case OP_GT_D_v6p: + OPC(float) = OPA(double) > OPB(double); + break; + case OP_LT_D_v6p: + OPC(float) = OPA(double) < OPB(double); + break; + case OP_NOT_D_v6p: + OPC(int) = (op_a[0].value || (op_a[1].value & ~0x80000000u)); + break; + case OP_EQ_D_v6p: + OPC(int) = OPA(double) == OPB(double); + break; + case OP_NE_D_v6p: + OPC(int) = OPA(double) != OPB(double); + break; + case OP_CONV_ID_v6p: + OPC(double) = OPA(int); + break; + case OP_CONV_DI_v6p: + OPC(int) = OPA(double); + break; + case OP_CONV_FD_v6p: + OPC(double) = OPA(float); + break; + case OP_CONV_DF_v6p: + OPC(float) = OPA(double); + break; // LordHavoc: to be enabled when Progs version 7 (or whatever it will be numbered) is finalized /* - case OP_BOUNDCHECK: - if (OPA.integer_var < 0 || OPA.integer_var >= st->b) { + case OP_BOUNDCHECK_v6p: + if (OPA(ptr) >= st->b) { PR_RunError (pr, "Progs boundcheck failed at line number " "%d, value is < 0 or >= %d", st->b, st->c); } @@ -1116,15 +1766,1148 @@ op_call: */ default: - PR_RunError (pr, "Bad opcode %i", st->op); + PR_RunError (pr, "Bad opcode %i", st->op & ~OP_BREAK); + } + if (pr->watch && pr->watch->value != old_val.value) { + if (!pr->wp_conditional + || pr->watch->value == pr->wp_val.value) { + if (pr->debug_handler) { + pr->debug_handler (prd_watchpoint, 0, pr->debug_data); + } else { + PR_RunError (pr, "watchpoint hit: %d -> %d", + old_val.value, pr->watch->value); + } + } + old_val.value = pr->watch->value; } - if (watch && watch->integer_var != old_val.integer_var - && (!pr->wp_conditional - || watch->integer_var == pr->wp_val.integer_var)) - PR_RunError (pr, "watchpoint hit: %d -> %d", old_val.integer_var, - watch->integer_var); } exit_program: +} + +#define MM(type) (*((pr_##type##_t *) (mm))) +#define STK(type) (*((pr_##type##_t *) (stk))) + +static pr_type_t * +pr_address_mode (progs_t *pr, const dstatement_t *st, int mm_ind) +{ + pr_type_t *op_a = pr->pr_globals + st->a + PR_BASE (pr, st, A); + pr_type_t *op_b = pr->pr_globals + st->b + PR_BASE (pr, st, B); + pr_ptr_t mm_offs = 0; + + switch (mm_ind) { + case 0: + // regular global access + mm_offs = op_a - pr->pr_globals; + break; + case 1: + // entity.field (equivalent to OP_LOAD_t_v6p) + pr_ptr_t edict_area = pr->pr_edict_area - pr->pr_globals; + mm_offs = edict_area + OPA(entity) + OPB(field); + break; + case 2: + // constant indexed pointer: *a + b (supports -ve offset) + mm_offs = OPA(ptr) + (short) st->b; + break; + case 3: + // variable indexed pointer: *a + *b (supports -ve offset) + mm_offs = OPA(ptr) + OPB(int); + break; + case 4: + // global access with constant offset (supports -ve offset) + mm_offs = op_a - pr->pr_globals + (short) st->b; + break; + case 5: + // global access with variable offset (supports -ve offset) + mm_offs = op_a - pr->pr_globals + OPB(int); + break; + } + return pr->pr_globals + mm_offs; +} + +static pr_type_t * +pr_call_mode (progs_t *pr, const dstatement_t *st, int mm_ind) +{ + pr_type_t *op_a = pr->pr_globals + st->a + PR_BASE (pr, st, A); + pr_type_t *op_b = pr->pr_globals + st->b + PR_BASE (pr, st, B); + pr_ptr_t mm_offs = 0; + + switch (mm_ind) { + case 1: + // regular global access + mm_offs = op_a - pr->pr_globals; + break; + case 2: + // constant indexed pointer: *a + b (supports -ve offset) + mm_offs = OPA(ptr) + (short) st->b; + break; + case 3: + // variable indexed pointer: *a + *b (supports -ve offset) + mm_offs = OPA(ptr) + OPB(int); + break; + case 4: + // entity.field (equivalent to OP_LOAD_t_v6p) + pr_ptr_t edict_area = pr->pr_edict_area - pr->pr_globals; + mm_offs = edict_area + OPA(entity) + OPB(field); + break; + } + return pr->pr_globals + mm_offs; +} + +static pr_ptr_t __attribute__((pure)) +pr_jump_mode (progs_t *pr, const dstatement_t *st, int jump_ind) +{ + pr_type_t *op_a = pr->pr_globals + st->a + PR_BASE (pr, st, A); + pr_type_t *op_b = pr->pr_globals + st->b + PR_BASE (pr, st, B); + pr_ptr_t jump_offs = pr->pr_xstatement; + + switch (jump_ind) { + case 0: + // instruction relative offset + jump_offs = jump_offs + (short) st->a; + break; + case 1: + // variable indexed array: a + *b (only +ve) + jump_offs = PR_PTR (uint, op_a + OPB(uint)); + break; + case 2: + // constant indexed pointer: *a + b (supports -ve offset) + jump_offs = OPA(ptr) + (short) st->b; + break; + case 3: + // variable indexed pointer: *a + *b (supports -ve offset) + jump_offs = OPA(ptr) + OPB(int); + break; + } + if (pr_boundscheck && jump_offs >= pr->progs->statements.count) { + PR_RunError (pr, "out of bounds: %x", jump_offs); + } + return jump_offs - 1; // for st++ +} + +static pr_type_t * +pr_stack_push (progs_t *pr) +{ + // keep the stack 16-byte aligned + pr_ptr_t stack = *pr->globals.stack - 4; + pr_type_t *stk = pr->pr_globals + stack; + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 4); + } + *pr->globals.stack = stack; + return stk; +} + +static pr_type_t * +pr_stack_pop (progs_t *pr) +{ + pr_ptr_t stack = *pr->globals.stack; + pr_type_t *stk = pr->pr_globals + stack; + if (pr_boundscheck) { + check_stack_pointer (pr, stack, 4); + } + // keep the stack 16-byte aligned + *pr->globals.stack = stack + 4; + return stk; +} + +static void +pr_stack_adjust (progs_t *pr, int mode, int offset) +{ + // keep the stack 16-byte aligned + if (mode || (offset & 3)) { + PR_RunError (pr, "invalid stack adjustment: %d, %d", mode, offset); + } + + pr_ptr_t stack = *pr->globals.stack; + if (pr_boundscheck) { + check_stack_pointer (pr, stack + offset, 0); + } + *pr->globals.stack = stack + offset; +} + +static void +pr_with (progs_t *pr, const dstatement_t *st) +{ + pr_ptr_t edict_area = pr->pr_edict_area - pr->pr_globals; + pr_type_t *op_b = pr->pr_globals + PR_BASE (pr, st, B) + st->b; + pr_type_t *stk; + + switch (st->a) { + // fixed offset + case 0: + // hard-0 base + pr->pr_bases[st->c & 3] = st->b; + return; + case 1: + // relative to current base (-ve offset) + pr->pr_bases[st->c & 3] = PR_BASE (pr, st, B) + (pr_short_t) st->b; + return; + case 2: + // relative to stack (-ve offset) + pr->pr_bases[st->c & 3] = *pr->globals.stack + (pr_short_t) st->b; + return; + case 3: + // relative to edict_area (only +ve) + pr->pr_bases[st->c & 3] = edict_area + st->b; + return; + + case 4: + // hard-0 base + pr->pr_bases[st->c & 3] = G_POINTER (pr, st->b);; + return; + case 5: + pr->pr_bases[st->c & 3] = OPB(ptr); + return; + case 6: + // relative to stack (-ve offset) + pr->pr_bases[st->c & 3] = *pr->globals.stack + OPB(int); + return; + case 7: + // relative to edict_area (only +ve) + pr->pr_bases[st->c & 3] = edict_area + OPB(field); + return; + + case 8: + // pushregs + stk = pr_stack_push (pr); + STK(uivec4) = pr->pr_bases; + return; + case 9: + // popregs + stk = pr_stack_pop (pr); + pr->pr_bases = STK(uivec4); + return; + case 10: + // reset + pr->pr_bases = (pr_uivec4_t) {}; + return; + case 11: + // return pointer + pr->pr_bases[st->c & 3] = pr->pr_return - pr->pr_globals; + return; + } + PR_RunError (pr, "Invalid with index: %u", st->a); +} + +static pr_ivec4_t +pr_swizzle_f (pr_ivec4_t vec, pr_ushort_t swiz) +{ + goto do_swizzle; +#include "libs/gamecode/pr_swizzle32.cinc" + static const pr_ivec4_t neg[16] = { + { 0, 0, 0, 0 }, + { 1<<31, 0, 0, 0 }, + { 0, 1<<31, 0, 0 }, + { 1<<31, 1<<31, 0, 0 }, + { 0, 0, 1<<31, 0 }, + { 1<<31, 0, 1<<31, 0 }, + { 0, 1<<31, 1<<31, 0 }, + { 1<<31, 1<<31, 1<<31, 0 }, + { 0, 0, 0, 1<<31 }, + { 1<<31, 0, 0, 1<<31 }, + { 0, 1<<31, 0, 1<<31 }, + { 1<<31, 1<<31, 0, 1<<31 }, + { 0, 0, 1<<31, 1<<31 }, + { 1<<31, 0, 1<<31, 1<<31 }, + { 0, 1<<31, 1<<31, 1<<31 }, + { 1<<31, 1<<31, 1<<31, 1<<31 }, + }; + static const pr_ivec4_t zero[16] = { + { ~0, ~0, ~0, ~0 }, + { 0, ~0, ~0, ~0 }, + { ~0, 0, ~0, ~0 }, + { 0, 0, ~0, ~0 }, + { ~0, ~0, 0, ~0 }, + { 0, ~0, 0, ~0 }, + { ~0, 0, 0, ~0 }, + { 0, 0, 0, ~0 }, + { ~0, ~0, ~0, 0 }, + { 0, ~0, ~0, 0 }, + { ~0, 0, ~0, 0 }, + { 0, 0, ~0, 0 }, + { ~0, ~0, 0, 0 }, + { 0, ~0, 0, 0 }, + { ~0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + }; + +do_swizzle: + goto *swizzle_table[swiz & 0xff]; +negate: + vec ^= neg[(swiz >> 8) & 0xf]; + vec &= zero[(swiz >> 12) & 0xf]; + return vec; +} + +static pr_lvec4_t +#ifdef _WIN64 +//force gcc to use registers for the parameters to avoid alignment issues +//on the stack (gcc bug as of 11.2) +__attribute__((sysv_abi)) +#endif +pr_swizzle_d (pr_lvec4_t vec, pr_ushort_t swiz) +{ + goto do_swizzle; +#include "libs/gamecode/pr_swizzle64.cinc" +#define L(x) UINT64_C(x) + static const pr_lvec4_t neg[16] = { + { INT64_C(0), INT64_C(0), INT64_C(0), INT64_C(0) }, + { INT64_C(1)<<63, INT64_C(0), INT64_C(0), INT64_C(0) }, + { INT64_C(0), INT64_C(1)<<63, INT64_C(0), INT64_C(0) }, + { INT64_C(1)<<63, INT64_C(1)<<63, INT64_C(0), INT64_C(0) }, + { INT64_C(0), INT64_C(0), INT64_C(1)<<63, INT64_C(0) }, + { INT64_C(1)<<63, INT64_C(0), INT64_C(1)<<63, INT64_C(0) }, + { INT64_C(0), INT64_C(1)<<63, INT64_C(1)<<63, INT64_C(0) }, + { INT64_C(1)<<63, INT64_C(1)<<63, INT64_C(1)<<63, INT64_C(0) }, + { INT64_C(0), INT64_C(0), INT64_C(0), INT64_C(1)<<63 }, + { INT64_C(1)<<63, INT64_C(0), INT64_C(0), INT64_C(1)<<63 }, + { INT64_C(0), INT64_C(1)<<63, INT64_C(0), INT64_C(1)<<63 }, + { INT64_C(1)<<63, INT64_C(1)<<63, INT64_C(0), INT64_C(1)<<63 }, + { INT64_C(0), INT64_C(0), INT64_C(1)<<63, INT64_C(1)<<63 }, + { INT64_C(1)<<63, INT64_C(0), INT64_C(1)<<63, INT64_C(1)<<63 }, + { INT64_C(0), INT64_C(1)<<63, INT64_C(1)<<63, INT64_C(1)<<63 }, + { INT64_C(1)<<63, INT64_C(1)<<63, INT64_C(1)<<63, INT64_C(1)<<63 }, + }; + static const pr_lvec4_t zero[16] = { + { ~INT64_C(0), ~INT64_C(0), ~INT64_C(0), ~INT64_C(0) }, + { INT64_C(0), ~INT64_C(0), ~INT64_C(0), ~INT64_C(0) }, + { ~INT64_C(0), INT64_C(0), ~INT64_C(0), ~INT64_C(0) }, + { INT64_C(0), INT64_C(0), ~INT64_C(0), ~INT64_C(0) }, + { ~INT64_C(0), ~INT64_C(0), INT64_C(0), ~INT64_C(0) }, + { INT64_C(0), ~INT64_C(0), INT64_C(0), ~INT64_C(0) }, + { ~INT64_C(0), INT64_C(0), INT64_C(0), ~INT64_C(0) }, + { INT64_C(0), INT64_C(0), INT64_C(0), ~INT64_C(0) }, + { ~INT64_C(0), ~INT64_C(0), ~INT64_C(0), INT64_C(0) }, + { INT64_C(0), ~INT64_C(0), ~INT64_C(0), INT64_C(0) }, + { ~INT64_C(0), INT64_C(0), ~INT64_C(0), INT64_C(0) }, + { INT64_C(0), INT64_C(0), ~INT64_C(0), INT64_C(0) }, + { ~INT64_C(0), ~INT64_C(0), INT64_C(0), INT64_C(0) }, + { INT64_C(0), ~INT64_C(0), INT64_C(0), INT64_C(0) }, + { ~INT64_C(0), INT64_C(0), INT64_C(0), INT64_C(0) }, + { INT64_C(0), INT64_C(0), INT64_C(0), INT64_C(0) }, + }; + +do_swizzle: + goto *swizzle_table[swiz & 0xff]; +negate: + vec ^= neg[(swiz >> 8) & 0xf]; + vec &= zero[(swiz >> 12) & 0xf]; + return vec; +} + +static void +pr_exec_ruamoko (progs_t *pr, int exitdepth) +{ + int profile, startprofile; + dstatement_t *st; + pr_type_t old_val = {0}; + + // make a stack frame + startprofile = profile = 0; + + st = pr->pr_statements + pr->pr_xstatement; + + if (pr->watch) { + old_val = *pr->watch; + } + + while (1) { + st++; + ++pr->pr_xstatement; + if (pr->pr_xstatement != st - pr->pr_statements) + PR_RunError (pr, "internal error"); + if (++profile > 1000000 && !pr->no_exec_limit) { + PR_RunError (pr, "runaway loop error"); + } + + if (pr->pr_trace) { + if (pr->debug_handler) { + pr->debug_handler (prd_trace, 0, pr->debug_data); + } else { + PR_PrintStatement (pr, st, 1); + } + } + + if (st->op & OP_BREAK) { + if (pr->debug_handler) { + pr->debug_handler (prd_breakpoint, 0, pr->debug_data); + } else { + PR_RunError (pr, "breakpoint hit"); + } + } + + pr_ptr_t st_a = st->a + PR_BASE (pr, st, A); + pr_ptr_t st_b = st->b + PR_BASE (pr, st, B); + pr_ptr_t st_c = st->c + PR_BASE (pr, st, C); + + + pr_type_t *op_a = pr->pr_globals + st_a; + pr_type_t *op_b = pr->pr_globals + st_b; + pr_type_t *op_c = pr->pr_globals + st_c; + + pr_type_t *stk; + pr_type_t *mm; + pr_func_t function; + pr_opcode_e st_op = st->op & OP_MASK; + switch (st_op) { + // 0 0000 + case OP_NOP: + break; + case OP_ADJSTK: + pr_stack_adjust (pr, st->a, (short) st->b); + break; + case OP_LDCONST: + PR_RunError (pr, "OP_LDCONST not implemented"); + break; + case OP_LOAD_B_1: + case OP_LOAD_C_1: + case OP_LOAD_D_1: + mm = pr_address_mode (pr, st, (st_op - OP_LOAD_B_1 + 4) >> 2); + OPC(int) = MM(int); + break; + case OP_LOAD_B_2: + case OP_LOAD_C_2: + case OP_LOAD_D_2: + mm = pr_address_mode (pr, st, (st_op - OP_LOAD_B_2 + 4) >> 2); + OPC(ivec2) = MM(ivec2); + break; + case OP_LOAD_B_3: + case OP_LOAD_C_3: + case OP_LOAD_D_3: + mm = pr_address_mode (pr, st, (st_op - OP_LOAD_B_3 + 4) >> 2); + VectorCopy (&MM(int), &OPC(int)); + break; + case OP_LOAD_B_4: + case OP_LOAD_C_4: + case OP_LOAD_D_4: + mm = pr_address_mode (pr, st, (st_op - OP_LOAD_B_4 + 4) >> 2); + OPC(ivec4) = MM(ivec4); + break; + // 0 0001 + case OP_STORE_A_1: + case OP_STORE_B_1: + case OP_STORE_C_1: + case OP_STORE_D_1: + mm = pr_address_mode (pr, st, (st_op - OP_STORE_A_1) >> 2); + MM(int) = OPC(int); + break; + case OP_STORE_A_2: + case OP_STORE_B_2: + case OP_STORE_C_2: + case OP_STORE_D_2: + mm = pr_address_mode (pr, st, (st_op - OP_STORE_A_2) >> 2); + MM(ivec2) = OPC(ivec2); + break; + case OP_STORE_A_3: + case OP_STORE_B_3: + case OP_STORE_C_3: + case OP_STORE_D_3: + mm = pr_address_mode (pr, st, (st_op - OP_STORE_A_3) >> 2); + VectorCopy (&OPC(int), &MM(int)); + break; + case OP_STORE_A_4: + case OP_STORE_B_4: + case OP_STORE_C_4: + case OP_STORE_D_4: + mm = pr_address_mode (pr, st, (st_op - OP_STORE_A_4) >> 2); + MM(ivec4) = OPC(ivec4); + break; + // 0 0010 + case OP_PUSH_A_1: + case OP_PUSH_B_1: + case OP_PUSH_C_1: + case OP_PUSH_D_1: + mm = pr_address_mode (pr, st, (st_op - OP_PUSH_A_1) >> 2); + stk = pr_stack_push (pr); + STK(int) = MM(int); + break; + case OP_PUSH_A_2: + case OP_PUSH_B_2: + case OP_PUSH_C_2: + case OP_PUSH_D_2: + mm = pr_address_mode (pr, st, (st_op - OP_PUSH_A_2) >> 2); + stk = pr_stack_push (pr); + STK(ivec2) = MM(ivec2); + break; + case OP_PUSH_A_3: + case OP_PUSH_B_3: + case OP_PUSH_C_3: + case OP_PUSH_D_3: + mm = pr_address_mode (pr, st, (st_op - OP_PUSH_A_3) >> 2); + stk = pr_stack_push (pr); + VectorCopy (&MM(int), &STK(int)); + break; + case OP_PUSH_A_4: + case OP_PUSH_B_4: + case OP_PUSH_C_4: + case OP_PUSH_D_4: + mm = pr_address_mode (pr, st, (st_op - OP_PUSH_A_4) >> 2); + stk = pr_stack_push (pr); + STK(ivec4) = MM(ivec4); + break; + // 0 0011 + case OP_POP_A_1: + case OP_POP_B_1: + case OP_POP_C_1: + case OP_POP_D_1: + mm = pr_address_mode (pr, st, (st_op - OP_POP_A_1) >> 2); + stk = pr_stack_pop (pr); + MM(int) = STK(int); + break; + case OP_POP_A_2: + case OP_POP_B_2: + case OP_POP_C_2: + case OP_POP_D_2: + mm = pr_address_mode (pr, st, (st_op - OP_POP_A_2) >> 2); + stk = pr_stack_pop (pr); + MM(ivec2) = STK(ivec2); + break; + case OP_POP_A_3: + case OP_POP_B_3: + case OP_POP_C_3: + case OP_POP_D_3: + mm = pr_address_mode (pr, st, (st_op - OP_POP_A_3) >> 2); + stk = pr_stack_pop (pr); + VectorCopy (&STK(int), &MM(int)); + break; + case OP_POP_A_4: + case OP_POP_B_4: + case OP_POP_C_4: + case OP_POP_D_4: + mm = pr_address_mode (pr, st, (st_op - OP_POP_A_4) >> 2); + stk = pr_stack_pop (pr); + MM(ivec4) = STK(ivec4); + break; + // 0 0100 + // spare + // 0 0101 + // spare + // 0 0110 + // spare + // 0 0111 + // spare + +#define OP_cmp_1(OP, T, rt, cmp, ct) \ + case OP_##OP##_##T##_1: \ + OPC(rt) = -(OPA(ct) cmp OPB(ct)); \ + break +#define OP_cmp_2(OP, T, rt, cmp, ct) \ + case OP_##OP##_##T##_2: \ + OPC(rt) = (OPA(ct) cmp OPB(ct)); \ + break +#define OP_cmp_3(OP, T, rt, cmp, ct) \ + case OP_##OP##_##T##_3: \ + VectorCompCompare (&OPC(rt), -, &OPA(ct), cmp, &OPB(ct)); \ + break; +#define OP_cmp_4(OP, T, rt, cmp, ct) \ + case OP_##OP##_##T##_4: \ + OPC(rt) = (OPA(ct) cmp OPB(ct)); \ + break +#define OP_cmp_T(OP, T, rt1, rt2, rt4, cmp, ct1, ct2, ct4) \ + OP_cmp_1 (OP, T, rt1, cmp, ct1); \ + OP_cmp_2 (OP, T, rt2, cmp, ct2); \ + OP_cmp_3 (OP, T, rt1, cmp, ct1); \ + OP_cmp_4 (OP, T, rt4, cmp, ct4) +#define OP_cmp(OP, cmp) \ + OP_cmp_T (OP, I, int, ivec2, ivec4, cmp, int, ivec2, ivec4); \ + OP_cmp_T (OP, F, int, ivec2, ivec4, cmp, float, vec2, vec4); \ + OP_cmp_T (OP, L, long, lvec2, lvec4, cmp, long, lvec2, lvec4); \ + OP_cmp_T (OP, D, long, lvec2, lvec4, cmp, double, dvec2, dvec4) + + // 0 1000 + OP_cmp(EQ, ==); + // 0 1001 + OP_cmp(LT, <); + // 0 1010 + OP_cmp(GT, >); + +#define OP_op_1(OP, T, t, op) \ + case OP_##OP##_##T##_1: \ + OPC(t) = (OPA(t) op OPB(t)); \ + break +#define OP_op_2(OP, T, t, op) \ + case OP_##OP##_##T##_2: \ + OPC(t) = (OPA(t) op OPB(t)); \ + break +#define OP_op_3(OP, T, t, op) \ + case OP_##OP##_##T##_3: \ + VectorCompOp (&OPC(t), &OPA(t), op, &OPB(t)); \ + break; +#define OP_op_4(OP, T, t, op) \ + case OP_##OP##_##T##_4: \ + OPC(t) = (OPA(t) op OPB(t)); \ + break +#define OP_op_T(OP, T, t1, t2, t4, op) \ + OP_op_1 (OP, T, t1, op); \ + OP_op_2 (OP, T, t2, op); \ + OP_op_3 (OP, T, t1, op); \ + OP_op_4 (OP, T, t4, op) + // 0 1011 + OP_op_T (DIV, u, uint, uivec2, uivec4, /); + OP_op_T (DIV, U, ulong, ulvec2, ulvec4, /); + OP_op_T (REM, u, uint, uivec2, uivec4, %); + OP_op_T (REM, U, ulong, ulvec2, ulvec4, %); + // 0 1100 + OP_cmp(NE, !=); + // 0 1101 + OP_cmp(GE, >=); + // 0 1110 + OP_cmp(LE, <=); + // 0 1111 + case OP_LEA_E: + mm = pr_address_mode (pr, st, 4); + OPC(ptr) = mm - pr->pr_globals; + break; + case OP_LOAD64_B_3: + case OP_LOAD64_C_3: + case OP_LOAD64_D_3: + mm = pr_address_mode (pr, st, (st_op - OP_LOAD64_B_3 + 1)); + VectorCopy (&MM(long), &OPC(long)); + break; + case OP_STORE64_A_3: + case OP_STORE64_B_3: + case OP_STORE64_C_3: + case OP_STORE64_D_3: + mm = pr_address_mode (pr, st, (st_op - OP_STORE64_A_3)); + VectorCopy (&OPC(long), &MM(long)); + break; + case OP_LEA_F: + mm = pr_address_mode (pr, st, 5); + OPC(ptr) = mm - pr->pr_globals; + break; + case OP_LOAD64_B_4: + case OP_LOAD64_C_4: + case OP_LOAD64_D_4: + mm = pr_address_mode (pr, st, (st_op - OP_LOAD64_B_4 + 1)); + OPC(lvec4) = MM(lvec4); + break; + case OP_STORE64_A_4: + case OP_STORE64_B_4: + case OP_STORE64_C_4: + case OP_STORE64_D_4: + mm = pr_address_mode (pr, st, (st_op - OP_STORE64_A_4)); + MM(lvec4) = OPC(lvec4); + break; + +#define OP_op(OP, op) \ + OP_op_T (OP, I, int, ivec2, ivec4, op); \ + OP_op_T (OP, F, float, vec2, vec4, op); \ + OP_op_T (OP, L, long, lvec2, lvec4, op); \ + OP_op_T (OP, D, double, dvec2, dvec4, op) +#define OP_uop_1(OP, T, t, op) \ + case OP_##OP##_##T##_1: \ + OPC(t) = op (OPA(t)); \ + break +#define OP_uop_2(OP, T, t, op) \ + case OP_##OP##_##T##_2: \ + OPC(t) = op (OPA(t)); \ + break +#define OP_uop_3(OP, T, t, op) \ + case OP_##OP##_##T##_3: \ + VectorCompUop (&OPC(t), op, &OPA(t)); \ + break; +#define OP_uop_4(OP, T, t, op) \ + case OP_##OP##_##T##_4: \ + OPC(t) = op (OPA(t)); \ + break +#define OP_uop_T(OP, T, t1, t2, t4, op) \ + OP_uop_1 (OP, T, t1, op); \ + OP_uop_2 (OP, T, t2, op); \ + OP_uop_3 (OP, T, t1, op); \ + OP_uop_4 (OP, T, t4, op) + + // 1 0000 + OP_op(MUL, *); + // 1 0001 + OP_op(DIV, /); + +// implement remainder (c %) for integers: +// 5 rem 3 = 2 +// -5 rem 3 = -2 +// 5 rem -3 = 2 +// -5 rem -3 = -2 +#define OP_store(d, s) *(d) = s +#define OP_remmod_T(OP, T, n, t, l, f, s) \ + case OP_##OP##_##T##_##n: \ + { \ + __auto_type a = l (&OPA(t)); \ + __auto_type b = l (&OPB(t)); \ + s (&OPC(t), a - b * f(a / b)); \ + } \ + break +#define OP_rem_T(T, n, t, l, f, s) \ + OP_remmod_T(REM, T, n, t, l, f, s) + + // 1 0010 + OP_op_T (REM, I, int, ivec2, ivec4, %); + OP_rem_T (F, 1, float, *, truncf, OP_store); + OP_rem_T (F, 2, vec2, *, vtrunc2f, OP_store); + OP_rem_T (F, 3, float, loadvec3f, vtrunc4f, storevec3f); + OP_rem_T (F, 4, vec4, *, vtrunc4f, OP_store); + OP_op_T (REM, L, long, lvec2, lvec4, %); + OP_rem_T (D, 1, double, *, trunc, OP_store); + OP_rem_T (D, 2, dvec2, *, vtrunc2d, OP_store); + OP_rem_T (D, 3, double, loadvec3d, vtrunc4d, storevec3d); + OP_rem_T (D, 4, dvec4, *, vtrunc4d, OP_store); + +// implement true modulo (python %) for integers: +// 5 mod 3 = 2 +// -5 mod 3 = 1 +// 5 mod -3 = -1 +// -5 mod -3 = -2 +#define OP_mod_Ti(T, n, t, l, m, s) \ + case OP_MOD_##T##_##n: \ + { \ + __auto_type a = l(&OPA(t)); \ + __auto_type b = l(&OPB(t)); \ + __auto_type c = a % b; \ + /* % is really remainder and so has the same sign rules */\ + /* as division: -5 % 3 = -2, so need to add b (3 here) */\ + /* if c's sign is incorrect, but only if c is non-zero */\ + __auto_type mask = m((a ^ b) < 0); \ + mask &= m(c != 0); \ + s(&OPC(t), c + (mask & b)); \ + } \ + break +// floating point modulo is so much easier :P (just use floor instead of trunc) +#define OP_mod_Tf(T, n, t, l, f, s) \ + OP_remmod_T(MOD, T, n, t, l, f, s) + + // 1 0011 + OP_mod_Ti (I, 1, int, *, -, OP_store); + OP_mod_Ti (I, 2, ivec2, *, +, OP_store); + OP_mod_Ti (I, 3, int, loadvec3i1, +, storevec3i); + OP_mod_Ti (I, 4, ivec4, *, +, OP_store); + OP_mod_Tf (F, 1, float, *, floorf, OP_store); + OP_mod_Tf (F, 2, vec2, *, vfloor2f, OP_store); + OP_mod_Tf (F, 3, float, loadvec3f, vfloor4f, storevec3f); + OP_mod_Tf (F, 4, vec4, *, vfloor4f, OP_store); + OP_mod_Ti (L, 1, long, *, -, OP_store); + OP_mod_Ti (L, 2, lvec2, *, +, OP_store); + OP_mod_Ti (L, 3, long, loadvec3l1, +, storevec3l); + OP_mod_Ti (L, 4, lvec4, *, +, OP_store); + OP_mod_Tf (D, 1, double, *, floor, OP_store); + OP_mod_Tf (D, 2, dvec2, *, vfloor2d, OP_store); + OP_mod_Tf (D, 3, double, loadvec3d, vfloor4d, storevec3d); + OP_mod_Tf (D, 4, dvec4, *, vfloor4d, OP_store); + + // 1 0100 + OP_op(ADD, +); + // 1 0101 + OP_op(SUB, -); + // 1 0110 + OP_op_T (SHL, I, int, ivec2, ivec4, <<); + OP_op_T (SHL, L, long, lvec2, lvec4, <<); + case OP_EQ_S: + case OP_LT_S: + case OP_GT_S: + case OP_CMP_S: + case OP_GE_S: + case OP_LE_S: + { + int cmp = strcmp (PR_GetString (pr, OPA(string)), + PR_GetString (pr, OPB(string))); + switch (st_op) { + case OP_EQ_S: cmp = -(cmp == 0); break; + case OP_LT_S: cmp = -(cmp < 0); break; + case OP_GT_S: cmp = -(cmp > 0); break; + case OP_GE_S: cmp = -(cmp >= 0); break; + case OP_LE_S: cmp = -(cmp <= 0); break; + case OP_CMP_S: break; + default: break; + } + OPC(int) = cmp; + } + break; + case OP_ADD_S: + OPC(string) = PR_CatStrings(pr, PR_GetString (pr, OPA(string)), + PR_GetString (pr, OPB(string))); + break; + case OP_NOT_S: + OPC(int) = -(!OPA(string) || !*PR_GetString (pr, OPA(string))); + break; + // 1 0111 + OP_op_T (ASR, I, int, ivec2, ivec4, >>); + OP_op_T (SHR, u, uint, uivec2, uivec4, >>); + OP_op_T (ASR, L, long, lvec2, lvec4, >>); + OP_op_T (SHR, U, ulong, ulvec2, ulvec4, >>); + // 1 1000 + OP_op_T (BITAND, I, int, ivec2, ivec4, &); + OP_op_T (BITOR, I, int, ivec2, ivec4, |); + OP_op_T (BITXOR, I, int, ivec2, ivec4, ^); + OP_uop_T (BITNOT, I, int, ivec2, ivec4, ~); + // 1 1001 + OP_cmp_T (LT, u, int, ivec2, ivec4, <, uint, uivec2, uivec4); + case OP_JUMP_A: + case OP_JUMP_B: + case OP_JUMP_C: + case OP_JUMP_D: + pr->pr_xstatement = pr_jump_mode (pr, st, st_op - OP_JUMP_A); + st = pr->pr_statements + pr->pr_xstatement; + break; + OP_cmp_T (LT, U, long, lvec2, lvec4, <, ulong, ulvec2, ulvec4); + case OP_RETURN: + int ret_size = (st->c & 0x1f) + 1; // up to 32 words + if (st->c != 0xffff) { + mm = pr_address_mode (pr, st, st->c >> 5); + memcpy (&R_INT (pr), mm, ret_size * sizeof (*op_a)); + } + pr->pr_xfunction->profile += profile - startprofile; + startprofile = profile; + PR_LeaveFunction (pr, pr->pr_depth == exitdepth); + st = pr->pr_statements + pr->pr_xstatement; + if (pr->pr_depth== exitdepth) { + if (pr->pr_trace && pr->pr_depth <= pr->pr_trace_depth) { + pr->pr_trace = false; + } + goto exit_program; + } + break; + case OP_CALL_B: + case OP_CALL_C: + case OP_CALL_D: + mm = pr_call_mode (pr, st, st_op - OP_CALL_B + 1); + function = PR_PTR (func, mm); + pr->pr_argc = 0; + // op_c specifies the location for the return value if any + pr->pr_xfunction->profile += profile - startprofile; + startprofile = profile; + PR_CallFunction (pr, function, op_c); + st = pr->pr_statements + pr->pr_xstatement; + break; + // 1 1010 + OP_cmp_T (GT, u, int, ivec2, ivec4, >, uint, uivec2, uivec4); + case OP_SWIZZLE_F: + OPC(ivec4) = pr_swizzle_f (OPA(ivec4), st->b); + break; + case OP_SCALE_F_2: + OPC(vec2) = OPA(vec2) * OPB(float); + break; + case OP_SCALE_F_3: + VectorScale (&OPA(float), OPB(float), &OPC(float)); + break; + case OP_SCALE_F_4: + OPC(vec4) = OPA(vec4) * OPB(float); + break; + OP_cmp_T (GT, U, long, lvec2, lvec4, >, ulong, ulvec2, ulvec4); + case OP_SWIZZLE_D: + OPC(lvec4) = pr_swizzle_d (OPA(lvec4), st->b); + break; + case OP_SCALE_D_2: + OPC(dvec2) = OPA(dvec2) * OPB(double); + break; + case OP_SCALE_D_3: + VectorScale (&OPA(double), OPB(double), &OPC(double)); + break; + case OP_SCALE_D_4: + OPC(dvec4) = OPA(dvec4) * OPB(double); + break; + // 1 1011 + case OP_CROSS_F: + { + pr_vec4_t a = loadvec3f (&OPA(float)); + pr_vec4_t b = loadvec3f (&OPB(float)); + pr_vec4_t c = crossf (a, b); + storevec3f (&OPC(float), c); + } + break; + case OP_CDOT_F: + OPC(vec2) = dot2f (OPA(vec2), OPB(vec2)); + break; + case OP_VDOT_F: + { + vec_t d = DotProduct (&OPA(float), + &OPB(float)); + VectorSet (d, d, d, &OPC(float)); + } + break; + case OP_QDOT_F: + OPC(vec4) = dotf (OPA(vec4), OPB(vec4)); + break; + case OP_CMUL_F: + OPC(vec2) = cmulf (OPA(vec2), OPB(vec2)); + break; + case OP_QVMUL_F: + { + pr_vec4_t v = loadvec3f (&OPB(float)); + v = qvmulf (OPA(vec4), v); + storevec3f (&OPC(float), v); + } + break; + case OP_VQMUL_F: + { + pr_vec4_t v = loadvec3f (&OPA(float)); + v = vqmulf (v, OPB(vec4)); + storevec3f (&OPC(float), v); + } + break; + case OP_QMUL_F: + OPC(vec4) = qmulf (OPA(vec4), OPB(vec4)); + break; + case OP_CROSS_D: + { + pr_dvec4_t a = loadvec3d (&OPA(double)); + pr_dvec4_t b = loadvec3d (&OPB(double)); + pr_dvec4_t c = crossd (a, b); + storevec3d (&OPC(double), c); + } + break; + case OP_CDOT_D: + OPC(dvec2) = dot2d (OPA(dvec2), OPB(dvec2)); + break; + case OP_VDOT_D: + { + double d = DotProduct (&OPA(double), + &OPB(double)); + VectorSet (d, d, d, &OPC(double)); + } + break; + case OP_QDOT_D: + OPC(dvec4) = dotd (OPA(dvec4), OPB(dvec4)); + break; + case OP_CMUL_D: + OPC(dvec2) = cmuld (OPA(dvec2), OPB(dvec2)); + break; + case OP_QVMUL_D: + { + pr_dvec4_t v = loadvec3d (&OPB(double)); + v = qvmuld (OPA(dvec4), v); + storevec3d (&OPC(double), v); + } + break; + case OP_VQMUL_D: + { + pr_dvec4_t v = loadvec3d (&OPA(double)); + v = vqmuld (v, OPB(dvec4)); + storevec3d (&OPC(double), v); + } + break; + case OP_QMUL_D: + OPC(dvec4) = qmuld (OPA(dvec4), OPB(dvec4)); + break; + // 1 1100 + OP_op_T (BITAND, L, long, lvec2, lvec4, &); + OP_op_T (BITOR, L, long, lvec2, lvec4, |); + OP_op_T (BITXOR, L, long, lvec2, lvec4, ^); + OP_uop_T (BITNOT, L, long, lvec2, lvec4, ~); + // 1 1101 + OP_cmp_T (GE, u, int, ivec2, ivec4, >=, uint, uivec2, uivec4); + case OP_MOVE_I: + memmove (op_c, op_a, st->b * sizeof (pr_type_t)); + break; + case OP_MOVE_P: + memmove (pr->pr_globals + OPC(ptr), pr->pr_globals + OPA(ptr), + OPB(uint) * sizeof (pr_type_t)); + break; + case OP_MOVE_PI: + memmove (pr->pr_globals + OPC(ptr), pr->pr_globals + OPA(ptr), + st->b * sizeof (pr_type_t)); + break; + case OP_STATE_ft: + { + int self = *pr->globals.self; + int nextthink = pr->fields.nextthink + self; + int frame = pr->fields.frame + self; + int think = pr->fields.think + self; + float time = *pr->globals.ftime + 0.1; + PR_PTR (float, &pr->pr_edict_area[nextthink]) = time; + PR_PTR (float, &pr->pr_edict_area[frame]) = OPA(float); + PR_PTR (func, &pr->pr_edict_area[think]) = OPB(func); + } + break; + OP_cmp_T (GE, U, long, lvec2, lvec4, >=, ulong, ulvec2, ulvec4); + case OP_MEMSET_I: + pr_memset (op_c, OPA(int), st->b); + break; + case OP_MEMSET_P: + pr_memset (pr->pr_globals + OPC(ptr), OPA(int), OPB(uint)); + break; + case OP_MEMSET_PI: + pr_memset (pr->pr_globals + OPC(ptr), OPA(int), st->b); + break; + case OP_STATE_ftt: + { + int self = *pr->globals.self; + int nextthink = pr->fields.nextthink + self; + int frame = pr->fields.frame + self; + int think = pr->fields.think + self; + float time = *pr->globals.ftime + OPC(float); + PR_PTR (float, &pr->pr_edict_area[nextthink]) = time; + PR_PTR (float, &pr->pr_edict_area[frame]) = OPA(float); + PR_PTR (func, &pr->pr_edict_area[think]) = OPB(func); + } + break; + // 1 1110 + OP_cmp_T (LE, u, int, ivec2, ivec4, <=, uint, uivec2, uivec4); + case OP_IFZ: + if (!OPC(int)) { + pr->pr_xstatement = pr_jump_mode (pr, st, 0); + st = pr->pr_statements + pr->pr_xstatement; + } + break; + case OP_IFB: + if (OPC(int) < 0) { + pr->pr_xstatement = pr_jump_mode (pr, st, 0); + st = pr->pr_statements + pr->pr_xstatement; + } + break; + case OP_IFA: + if (OPC(int) > 0) { + pr->pr_xstatement = pr_jump_mode (pr, st, 0); + st = pr->pr_statements + pr->pr_xstatement; + } + break; + case OP_STATE_dt: + { + int self = *pr->globals.self; + int nextthink = pr->fields.nextthink + self; + int frame = pr->fields.frame + self; + int think = pr->fields.think + self; + double time = *pr->globals.dtime + 0.1; + PR_PTR (double, &pr->pr_edict_area[nextthink]) = time; + PR_PTR (int, &pr->pr_edict_area[frame]) = OPA(int); + PR_PTR (func, &pr->pr_edict_area[think]) = OPB(func); + } + break; + OP_cmp_T (LE, U, long, lvec2, lvec4, <=, ulong, ulvec2, ulvec4); + case OP_IFNZ: + if (OPC(int)) { + pr->pr_xstatement = pr_jump_mode (pr, st, 0); + st = pr->pr_statements + pr->pr_xstatement; + } + break; + case OP_IFAE: + if (OPC(int) >= 0) { + pr->pr_xstatement = pr_jump_mode (pr, st, 0); + st = pr->pr_statements + pr->pr_xstatement; + } + break; + case OP_IFBE: + if (OPC(int) <= 0) { + pr->pr_xstatement = pr_jump_mode (pr, st, 0); + st = pr->pr_statements + pr->pr_xstatement; + } + break; + case OP_STATE_dtt: + { + int self = *pr->globals.self; + int nextthink = pr->fields.nextthink + self; + int frame = pr->fields.frame + self; + int think = pr->fields.think + self; + double time = *pr->globals.dtime + OPC(double); + PR_PTR (double, &pr->pr_edict_area[nextthink]) = time; + PR_PTR (int, &pr->pr_edict_area[frame]) = OPA(int); + PR_PTR (func, &pr->pr_edict_area[think]) = OPB(func); + } + break; + // 1 1111 + case OP_LEA_A: + case OP_LEA_B: + case OP_LEA_C: + case OP_LEA_D: + mm = pr_address_mode (pr, st, (st_op - OP_LEA_A)); + OPC(ptr) = mm - pr->pr_globals; + break; + case OP_QV4MUL_F: + OPC(vec4) = qvmulf (OPA(vec4), OPB(vec4)); + break; + case OP_V4QMUL_F: + OPC(vec4) = vqmulf (OPA(vec4), OPB(vec4)); + break; + case OP_QV4MUL_D: + OPC(dvec4) = qvmuld (OPA(dvec4), OPB(dvec4)); + break; + case OP_V4QMUL_D: + OPC(dvec4) = vqmuld (OPA(dvec4), OPB(dvec4)); + break; + case OP_BITAND_F: + OPC(float) = (int) OPA(float) & (int) OPB(float); + break; + case OP_BITOR_F: + OPC(float) = (int) OPA(float) | (int) OPB(float); + break; + case OP_BITXOR_F: + OPC(float) = (int) OPA(float) ^ (int) OPB(float); + break; + case OP_BITNOT_F: + OPC(float) = ~ (int) OPA(float); + break; + case OP_CONV: + switch (st->b) { +#include "libs/gamecode/pr_convert.cinc" + default: + PR_RunError (pr, "invalid conversion code: %04o", + st->b); + } + break; + case OP_WITH: + pr_with (pr, st); + break; + case OP_EXTEND: + switch (st->b) { +#include "libs/gamecode/pr_extend.cinc" + default: + PR_RunError (pr, "invalid extend code: %04o", st->b); + } + break; +#define OP_hop2(vec, op) ((vec)[0] op (vec)[1]) +#define OP_hop3(vec, op) ((vec)[0] op (vec)[1] op (vec)[2]) +#define OP_hop4(vec, op) ((vec)[0] op (vec)[1] op (vec)[2] op (vec)[3]) + case OP_HOPS: + switch (st->b) { +#include "libs/gamecode/pr_hops.cinc" + default: + PR_RunError (pr, "invalid hops code: %04o", st->b); + } + break; + default: + PR_RunError (pr, "Bad opcode o%03o", st->op & OP_MASK); + } + if (pr->watch && pr->watch->value != old_val.value) { + if (!pr->wp_conditional + || pr->watch->value == pr->wp_val.value) { + if (pr->debug_handler) { + pr->debug_handler (prd_watchpoint, 0, pr->debug_data); + } else { + PR_RunError (pr, "watchpoint hit: %d -> %d", + old_val.value, pr->watch->value); + } + } + old_val.value = pr->watch->value; + } + } +exit_program: +} +/* + PR_ExecuteProgram + + The interpretation main loop +*/ +VISIBLE void +PR_ExecuteProgram (progs_t *pr, pr_func_t fnum) +{ + Sys_PushSignalHook (signal_hook, pr); + Sys_PushErrorHandler (error_handler, pr); + + if (pr->debug_handler) { + pr->debug_handler (prd_subenter, &fnum, pr->debug_data); + } + + int exitdepth = pr->pr_depth; + if (!PR_CallFunction (pr, fnum, pr->pr_return)) { + // called a builtin instead of progs code + goto exit_program; + } + if (pr->progs->version < PROG_VERSION) { + pr_exec_quakec (pr, exitdepth); + } else { + pr_exec_ruamoko (pr, exitdepth); + } +exit_program: + if (pr->debug_handler) { + pr->debug_handler (prd_subexit, 0, pr->debug_data); + } + pr->pr_argc = 0; Sys_PopErrorHandler (); Sys_PopSignalHook (); } diff --git a/libs/gamecode/pr_load.c b/libs/gamecode/pr_load.c index b9a6df13a..abde7ba06 100644 --- a/libs/gamecode/pr_load.c +++ b/libs/gamecode/pr_load.c @@ -34,44 +34,81 @@ #ifdef HAVE_STRINGS_H # include #endif -#include -#include -#include "QF/cmd.h" #include "QF/crc.h" #include "QF/cvar.h" #include "QF/dstring.h" #include "QF/hash.h" #include "QF/mathlib.h" #include "QF/progs.h" -#include "QF/qdefs.h" #include "QF/qendian.h" #include "QF/quakefs.h" #include "QF/sys.h" #include "QF/zone.h" -#include "QF/va.h" + +#include "QF/progs/pr_type.h" #include "compat.h" -VISIBLE cvar_t *pr_boundscheck; -cvar_t *pr_deadbeef_ents; -cvar_t *pr_deadbeef_locals; -cvar_t *pr_faultchecks; +VISIBLE int pr_boundscheck; +static cvar_t pr_boundscheck_cvar = { + .name = "pr_boundscheck", + .description = + "Server progs bounds checking", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &pr_boundscheck }, +}; +int pr_deadbeef_ents; +static cvar_t pr_deadbeef_ents_cvar = { + .name = "pr_deadbeef_ents", + .description = + "set to clear unallocated memory to 0xdeadbeef", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &pr_deadbeef_ents }, +}; +int pr_deadbeef_locals; +static cvar_t pr_deadbeef_locals_cvar = { + .name = "pr_deadbeef_locals", + .description = + "set to clear uninitialized local vars to 0xdeadbeef", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &pr_deadbeef_locals }, +}; +int pr_faultchecks; +static cvar_t pr_faultchecks_cvar = { + .name = "pr_faultchecks", + .description = + "capture and handle division by 0 in progs", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &pr_faultchecks }, +}; static const char * function_get_key (const void *f, void *_pr) { progs_t *pr = (progs_t*)_pr; dfunction_t *func = (dfunction_t*)f; - return PR_GetString (pr, func->s_name); + return PR_GetString (pr, func->name); } static const char * var_get_key (const void *d, void *_pr) { progs_t *pr = (progs_t*)_pr; - ddef_t *def = (ddef_t*)d; - return PR_GetString (pr, def->s_name); + pr_def_t *def = (pr_def_t*)d; + return PR_GetString (pr, def->name); +} + +static const char * +type_get_key (const void *t, void *_pr) +{ + progs_t *pr = (progs_t *) _pr; + __auto_type type = (qfot_type_t *) t; + return PR_GetString (pr, type->encoding); } static void @@ -81,15 +118,17 @@ file_error (progs_t *pr, const char *path) } static void * -load_file (progs_t *pr, const char *path) +load_file (progs_t *pr, const char *path, off_t *size) { - return QFS_LoadHunkFile (QFS_FOpenFile (path)); + void *data = QFS_LoadHunkFile (QFS_FOpenFile (path)); + *size = qfs_filesize; + return data; } static void * allocate_progs_mem (progs_t *pr, int size) { - return Hunk_AllocName (size, pr->progs_name); + return Hunk_AllocName (0, size, pr->progs_name); } static void @@ -97,14 +136,27 @@ free_progs_mem (progs_t *pr, void *mem) { } +static int +align_size (int size, int align) +{ + size += align - 1; + size &= ~(align - 1); + return size; +} + VISIBLE void -PR_LoadProgsFile (progs_t *pr, QFile *file, int size, int max_edicts, int zone) +PR_LoadProgsFile (progs_t *pr, QFile *file, int size) { size_t i; int mem_size; int offset_tweak; dprograms_t progs; byte *base; + byte *heap; + ddef_t *global_ddefs; + ddef_t *field_ddefs; + // absolute minimum alignment is 4 bytes + int edict_alignment = __alignof__(pr_lvec4_t); if (!pr->file_error) pr->file_error = file_error; @@ -116,7 +168,6 @@ PR_LoadProgsFile (progs_t *pr, QFile *file, int size, int max_edicts, int zone) pr->free_progs_mem = free_progs_mem; PR_Resources_Clear (pr); - PR_ClearReturnStrings (pr); if (pr->progs) pr->free_progs_mem (pr, pr->progs); pr->progs = 0; @@ -134,6 +185,7 @@ PR_LoadProgsFile (progs_t *pr, QFile *file, int size, int max_edicts, int zone) ((int *) &progs)[i] = LittleLong (((int *) &progs)[i]); if (progs.version != PROG_VERSION + && progs.version != PROG_V6P_VERSION && progs.version != PROG_ID_VERSION) { if (progs.version < 0x00fff000) { PR_Error (pr, "%s has unrecognised version number (%u)", @@ -151,129 +203,198 @@ PR_LoadProgsFile (progs_t *pr, QFile *file, int size, int max_edicts, int zone) PROG_VERSION & 0xfff); } } + if (progs.version == PROG_VERSION) { + // ensure SIMD types can be aligned (qfcc will align them within + // the progs memory map, so the engine needs to ensure the progs memory + // is aligned) + edict_alignment = __alignof__(pr_lvec4_t); + } + // edict size is in words + edict_alignment /= 4; // Some compilers (eg, FTE) put extra data between the header and the // strings section. What's worse, they de-align the data. - offset_tweak = progs.ofs_strings % sizeof (pr_int_t); + offset_tweak = progs.strings.offset % sizeof (pr_int_t); offset_tweak = (sizeof (pr_int_t) - offset_tweak) % sizeof (pr_int_t); // size of progs themselves pr->progs_size = size + offset_tweak; - Sys_MaskPrintf (SYS_DEV, "Programs occupy %iK.\n", size / 1024); - // round off to next highest whole word address (esp for Alpha) - // this ensures that pointers in the engine data area are always - // properly aligned - pr->progs_size += sizeof (void*) - 1; - pr->progs_size &= ~(sizeof (void*) - 1); + Sys_MaskPrintf (SYS_dev, "Programs occupy %iK.\n", size / 1024); - // size of heap asked for by vm-subsystem - pr->zone_size = zone; - // round off to next highest whole word address (esp for Alpha) - // this ensures that pointers in the engine data area are always - // properly aligned - pr->zone_size += sizeof (void*) - 1; - pr->zone_size &= ~(sizeof (void*) - 1); + pr->progs_size = align_size (pr->progs_size, 64); + pr->zone_size = align_size (pr->zone_size, 64); + pr->stack_size = align_size (pr->stack_size, 64); - // size of edict asked for by progs - pr->pr_edict_size = max (1, progs.entityfields) * 4; - // size of engine data - pr->pr_edict_size += sizeof (edict_t) - sizeof (pr_type_t); - // round off to next highest whole word address (esp for Alpha) - // this ensures that pointers in the engine data area are always - // properly aligned - pr->pr_edict_size += sizeof (void*) - 1; - pr->pr_edict_size &= ~(sizeof (void*) - 1); - pr->pr_edictareasize = max_edicts * pr->pr_edict_size; - pr->max_edicts = max_edicts; + // size of edict asked for by progs, but at least 1 + pr->pr_edict_size = max (1, progs.entityfields); + pr->pr_edict_size = align_size (pr->pr_edict_size, edict_alignment); + pr->pr_edict_area_size = pr->max_edicts * pr->pr_edict_size; + pr->pr_edict_area_size = align_size (pr->pr_edict_area_size, 64); - mem_size = pr->progs_size + pr->zone_size + pr->pr_edictareasize; + mem_size = pr->pr_edict_area_size * sizeof (pr_type_t); + mem_size += pr->progs_size + pr->zone_size + pr->stack_size; + // space for return buffer + mem_size += PR_MAX_RETURN * sizeof (pr_type_t); + + // +1 for a nul terminator pr->progs = pr->allocate_progs_mem (pr, mem_size + 1); if (!pr->progs) return; + // Place a nul at the end of progs memory to ensure any unterminated + // strings within progs memory don't run off the end. ((byte *) pr->progs)[mem_size] = 0; memcpy (pr->progs, &progs, sizeof (progs)); base = (byte *) (pr->progs + 1) + offset_tweak; Qread (file, base, size - sizeof (progs)); CRC_ProcessBlock (base, &pr->crc, size - sizeof (progs)); + + pr->pr_edict_area = (pr_type_t *)((byte *) pr->progs + pr->progs_size); + base -= sizeof (progs); // offsets are from file start + heap = (byte *) &pr->pr_edict_area[pr->pr_edict_area_size]; - if (pr->edicts) - *pr->edicts = (edict_t *)((byte *) pr->progs + pr->progs_size); - pr->zone = (memzone_t *)((byte *) pr->progs + pr->progs_size - + pr->pr_edictareasize); + pr->zone = 0; + if (pr->zone_size) { + //FIXME zone_size needs to be at least as big as memzone_t, but + //memzone_t is opaque so its size is unknown + pr->zone = (memzone_t *) heap; + } - pr->pr_functions = - (dfunction_t *) (base + pr->progs->ofs_functions); - pr->pr_strings = (char *) base + pr->progs->ofs_strings; - pr->pr_stringsize = (char *) pr->zone + pr->zone_size - (char *) base; - pr->pr_globaldefs = (ddef_t *) (base + pr->progs->ofs_globaldefs); - pr->pr_fielddefs = (ddef_t *) (base + pr->progs->ofs_fielddefs); - pr->pr_statements = (dstatement_t *) (base + pr->progs->ofs_statements); + pr->pr_functions = (dfunction_t *) (base + pr->progs->functions.offset); + pr->pr_strings = (char *) base + pr->progs->strings.offset; + pr->pr_stringsize = (heap - base) + pr->zone_size; + global_ddefs = (ddef_t *) (base + pr->progs->globaldefs.offset); + field_ddefs = (ddef_t *) (base + pr->progs->fielddefs.offset); + pr->pr_statements = (dstatement_t *) (base + pr->progs->statements.offset); - pr->pr_globals = (pr_type_t *) (base + pr->progs->ofs_globals); - - pr->globals_size = (pr_type_t*)((byte *) pr->zone + pr->zone_size) + pr->pr_globals = (pr_type_t *) (base + pr->progs->globals.offset); + pr->stack = (pr_type_t *) ((byte *) pr->zone + pr->zone_size); + pr->stack_bottom = pr->stack - pr->pr_globals; + pr->globals_size = (pr_type_t *) ((byte *) pr->stack + pr->stack_size) - pr->pr_globals; - if (pr->zone_size) - PR_Zone_Init (pr); + if (pr->globals.stack && pr->stack_bottom) { + *pr->globals.stack = pr->globals_size; + } + pr->pr_return_buffer = pr->pr_globals + pr->globals_size; - if (pr->function_hash) { - Hash_FlushTable (pr->function_hash); - } else { - pr->function_hash = Hash_NewTable (1021, function_get_key, 0, pr); - } - if (pr->global_hash) { - Hash_FlushTable (pr->global_hash); - } else { - pr->global_hash = Hash_NewTable (1021, var_get_key, 0, pr); - } - if (pr->field_hash) { - Hash_FlushTable (pr->field_hash); - } else { - pr->field_hash = Hash_NewTable (1021, var_get_key, 0, pr); + if (pr->zone) { + PR_Zone_Init (pr); } + Hash_FlushTable (pr->function_hash); + Hash_FlushTable (pr->global_hash); + Hash_FlushTable (pr->field_hash); + Hash_FlushTable (pr->type_hash); + // byte swap the lumps - for (i = 0; i < pr->progs->numstatements; i++) { + for (i = 0; i < pr->progs->statements.count; i++) { pr->pr_statements[i].op = LittleShort (pr->pr_statements[i].op); pr->pr_statements[i].a = LittleShort (pr->pr_statements[i].a); pr->pr_statements[i].b = LittleShort (pr->pr_statements[i].b); pr->pr_statements[i].c = LittleShort (pr->pr_statements[i].c); } - for (i = 0; i < (size_t) pr->progs->numfunctions; i++) { + for (i = 0; i < pr->progs->functions.count; i++) { pr->pr_functions[i].first_statement = LittleLong (pr->pr_functions[i].first_statement); - pr->pr_functions[i].parm_start = - LittleLong (pr->pr_functions[i].parm_start); - pr->pr_functions[i].s_name = LittleLong (pr->pr_functions[i].s_name); - pr->pr_functions[i].s_file = LittleLong (pr->pr_functions[i].s_file); - pr->pr_functions[i].numparms = - LittleLong (pr->pr_functions[i].numparms); + pr->pr_functions[i].params_start = + LittleLong (pr->pr_functions[i].params_start); + pr->pr_functions[i].name = LittleLong (pr->pr_functions[i].name); + pr->pr_functions[i].file = LittleLong (pr->pr_functions[i].file); + pr->pr_functions[i].numparams = + LittleLong (pr->pr_functions[i].numparams); pr->pr_functions[i].locals = LittleLong (pr->pr_functions[i].locals); - if (pr->pr_functions[i].s_name) + if (pr->pr_functions[i].name) Hash_Add (pr->function_hash, &pr->pr_functions[i]); } - for (i = 0; i < pr->progs->numglobaldefs; i++) { - pr->pr_globaldefs[i].type = LittleShort (pr->pr_globaldefs[i].type); - pr->pr_globaldefs[i].ofs = LittleShort (pr->pr_globaldefs[i].ofs); - pr->pr_globaldefs[i].s_name = LittleLong (pr->pr_globaldefs[i].s_name); + if (pr->pr_globaldefs) { + free (pr->pr_globaldefs); + } + pr->pr_globaldefs = calloc (pr->progs->globaldefs.count, sizeof (pr_def_t)); + + for (i = 0; i < pr->progs->globaldefs.count; i++) { + pr_ushort_t safe_type = global_ddefs[i].type & ~DEF_SAVEGLOBAL; + global_ddefs[i].type = LittleShort (global_ddefs[i].type); + global_ddefs[i].ofs = LittleShort (global_ddefs[i].ofs); + global_ddefs[i].name = LittleLong (global_ddefs[i].name); + + pr->pr_globaldefs[i].type = global_ddefs[i].type; + pr->pr_globaldefs[i].size = pr_type_size[safe_type]; + pr->pr_globaldefs[i].ofs = global_ddefs[i].ofs; + pr->pr_globaldefs[i].name = global_ddefs[i].name; Hash_Add (pr->global_hash, &pr->pr_globaldefs[i]); } - for (i = 0; i < pr->progs->numfielddefs; i++) { - pr->pr_fielddefs[i].type = LittleShort (pr->pr_fielddefs[i].type); - if (pr->pr_fielddefs[i].type & DEF_SAVEGLOBAL) - PR_Error (pr, "PR_LoadProgs: pr_fielddefs[i].type & DEF_SAVEGLOBAL"); - pr->pr_fielddefs[i].ofs = LittleShort (pr->pr_fielddefs[i].ofs); - pr->pr_fielddefs[i].s_name = LittleLong (pr->pr_fielddefs[i].s_name); + if (pr->pr_fielddefs) { + free (pr->pr_fielddefs); + } + pr->pr_fielddefs = calloc (pr->progs->fielddefs.count, sizeof (pr_def_t)); + for (i = 0; i < pr->progs->fielddefs.count; i++) { + field_ddefs[i].type = LittleShort (field_ddefs[i].type); + if (field_ddefs[i].type & DEF_SAVEGLOBAL) + PR_Error (pr, "PR_LoadProgs: DEF_SAVEGLOBAL on field def %zd", i); + field_ddefs[i].ofs = LittleShort (field_ddefs[i].ofs); + field_ddefs[i].name = LittleLong (field_ddefs[i].name); + + pr->pr_fielddefs[i].type = field_ddefs[i].type; + pr->pr_fielddefs[i].ofs = field_ddefs[i].ofs; + pr->pr_fielddefs[i].name = field_ddefs[i].name; Hash_Add (pr->field_hash, &pr->pr_fielddefs[i]); } - for (i = 0; i < pr->progs->numglobals; i++) + for (i = 0; i < pr->progs->globals.count; i++) ((int *) pr->pr_globals)[i] = LittleLong (((int *) pr->pr_globals)[i]); + + pr_def_t *types_def = PR_FindGlobal (pr, ".type_encodings"); + if (types_def) { + __auto_type encodings = &G_STRUCT (pr, qfot_type_encodings_t, + types_def->ofs); + pr->type_encodings = encodings->types; + qfot_type_t *type; + for (pr_ptr_t type_ptr = 4; type_ptr < encodings->size; + type_ptr += type->size) { + type = &G_STRUCT (pr, qfot_type_t, pr->type_encodings + type_ptr); + Hash_Add (pr->type_hash, type); + } + } else { + pr->type_encodings = 0; + } + + pr_def_t *xdefs_def = PR_FindGlobal (pr, ".xdefs"); + if (xdefs_def) { + pr_xdefs_t *xdefs = &G_STRUCT (pr, pr_xdefs_t, xdefs_def->ofs); + xdef_t *xdef = &G_STRUCT (pr, xdef_t, xdefs->xdefs); + pr_def_t *def; + for (def = pr->pr_globaldefs, i = 0; i < pr->progs->globaldefs.count; + i++, xdef++, def++) { + def->ofs = xdef->ofs; + def->type_encoding = xdef->type; + } + for (def = pr->pr_fielddefs, i = 0; i < pr->progs->fielddefs.count; + i++, xdef++, def++) { + def->ofs = xdef->ofs; + def->type_encoding = xdef->type; + } + } else { + pr_def_t *def; + for (def = pr->pr_globaldefs, i = 0; i < pr->progs->globaldefs.count; + i++, def++) { + def->size = pr_type_size[def->type & ~DEF_SAVEGLOBAL]; + } + for (def = pr->pr_fielddefs, i = 0; i < pr->progs->fielddefs.count; + i++, def++) { + def->size = pr_type_size[def->type & ~DEF_SAVEGLOBAL]; + } + } + pr->pr_trace = 0; + pr->pr_trace_depth = 0; + pr->pr_xfunction = 0; + pr->pr_xstatement = 0; + pr->pr_depth = 0; + pr->localstack_used = 0; + pr->pr_argc = 0; } VISIBLE void @@ -307,12 +428,12 @@ PR_AddLoadFinishFunc (progs_t *pr, int (*func)(progs_t *)) static int pr_run_ctors (progs_t *pr) { - pr_int_t fnum; + pr_uint_t fnum; dfunction_t *func; - for (fnum = 0; fnum < pr->progs->numfunctions; fnum++) { + for (fnum = 0; fnum < pr->progs->functions.count; fnum++) { func = pr->pr_functions + fnum; - if (strequal (PR_GetString (pr, func->s_name), ".ctor")) + if (strequal (PR_GetString (pr, func->name), ".ctor")) PR_ExecuteProgram (pr, fnum); } return 1; @@ -365,6 +486,12 @@ PR_RunLoadFuncs (progs_t *pr) if (!pr->load_funcs[i] (pr)) return 0; + return 1; +} + +VISIBLE int +PR_RunPostLoadFuncs (progs_t *pr) +{ if (!pr_run_ctors (pr)) return 0; @@ -379,55 +506,93 @@ PR_RunLoadFuncs (progs_t *pr) PR_LoadProgs */ VISIBLE void -PR_LoadProgs (progs_t *pr, const char *progsname, int max_edicts, int zone) +PR_LoadProgs (progs_t *pr, const char *progsname) { QFile *file; file = QFS_FOpenFile (progsname); pr->progs_name = progsname; if (file) { - PR_LoadProgsFile (pr, file, qfs_filesize, max_edicts, zone); + PR_LoadProgsFile (pr, file, qfs_filesize); Qclose (file); } if (!pr->progs) return; - if (!PR_RunLoadFuncs (pr)) + if (!PR_RunLoadFuncs (pr)) { PR_Error (pr, "unable to load %s", progsname); + } + if (!pr->debug_handler && !PR_RunPostLoadFuncs (pr)) { + PR_Error (pr, "unable to load %s", progsname); + } } VISIBLE void PR_Init_Cvars (void) { - pr_boundscheck = - Cvar_Get ("pr_boundscheck", "0", CVAR_NONE, NULL, - "Server progs bounds checking"); - pr_deadbeef_ents = Cvar_Get ("pr_deadbeef_ents", "0", CVAR_NONE, NULL, - "set to clear unallocated memory to 0xdeadbeef"); - pr_deadbeef_locals = Cvar_Get ("pr_deadbeef_locals", "0", CVAR_NONE, NULL, - "set to clear uninitialized local vars to " - "0xdeadbeef"); - pr_faultchecks = Cvar_Get ("pr_faultchecks", "0", CVAR_NONE, NULL, - "capture and handle division by 0 in progs"); + Cvar_Register (&pr_boundscheck_cvar, 0, 0); + Cvar_Register (&pr_deadbeef_ents_cvar, 0, 0); + Cvar_Register (&pr_deadbeef_locals_cvar, 0, 0); + Cvar_Register (&pr_faultchecks_cvar, 0, 0); PR_Debug_Init_Cvars (); } VISIBLE void -PR_Init (void) +PR_Init (progs_t *pr) { - PR_Opcode_Init (); - PR_Debug_Init (); + PR_Resources_Init (pr); + PR_Strings_Init (pr); + PR_Debug_Init (pr); + pr->function_hash = Hash_NewTable (1021, function_get_key, 0, pr, + pr->hashctx); + pr->global_hash = Hash_NewTable (1021, var_get_key, 0, pr, pr->hashctx); + pr->field_hash = Hash_NewTable (1021, var_get_key, 0, pr, pr->hashctx); + pr->type_hash = Hash_NewTable (1021, type_get_key, 0, pr, pr->hashctx); +} + +VISIBLE void +PR_Shutdown (progs_t *pr) +{ + PR_Resources_Shutdown (pr); + PR_Builtins_Shutdown (pr); + + free (pr->load_funcs); + free (pr->load_finish_funcs); + pr->num_load_funcs = pr->max_load_funcs = 0; + pr->num_load_finish_funcs = pr->max_load_finish_funcs = 0; + + Hash_DelTable (pr->function_hash); + Hash_DelTable (pr->global_hash); + Hash_DelTable (pr->field_hash); + Hash_DelTable (pr->type_hash); + pr->function_hash = 0; + pr->global_hash = 0; + pr->field_hash = 0; + pr->type_hash = 0; + + if (pr->progs) { + pr->free_progs_mem (pr, pr->progs); + } + free (pr->pr_globaldefs); + free (pr->pr_fielddefs); + pr->progs = 0; + pr->pr_globaldefs = 0; + pr->pr_fielddefs = 0; } VISIBLE void PR_Error (progs_t *pr, const char *error, ...) { va_list argptr; - dstring_t *string = dstring_new (); + dstring_t *string = dstring_new ();//FIXME leaks when debugging va_start (argptr, error); dvsprintf (string, error, argptr); va_end (argptr); + if (pr->debug_handler) { + pr->debug_handler (prd_error, string->str, pr->debug_data); + // not expected to return, but if so, behave as if there was no handler + } Sys_Error ("%s: %s", pr->progs_name, string->str); } diff --git a/libs/gamecode/pr_opcode.c b/libs/gamecode/pr_opcode.c index d93e7340c..34a36c242 100644 --- a/libs/gamecode/pr_opcode.c +++ b/libs/gamecode/pr_opcode.c @@ -1,12 +1,12 @@ /* - #FILENAME# + pr_opcode.c - #DESCRIPTION# + Ruamoko instruction set opcode tables. - Copyright (C) 2001 #AUTHOR# + Copyright (C) 2022 Bill Currie - Author: #AUTHOR# - Date: #DATE# + Author: Bill Currie + Date: 2022/1/4 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -31,1250 +31,42 @@ # include "config.h" #endif -#ifdef HAVE_STRING_H -# include "string.h" -#endif -#ifdef HAVE_STRINGS_H -# include "strings.h" -#endif - -#include "QF/cvar.h" -#include "QF/hash.h" -#include "QF/pr_comp.h" #include "QF/progs.h" -#include "QF/sys.h" -hashtab_t *opcode_table; - -VISIBLE int pr_type_size[ev_type_count] = { - 1, // ev_void - 1, // ev_string - 1, // ev_float - 3, // ev_vector - 1, // ev_entity - 1, // ev_field - 1, // ev_func - 1, // ev_pointer - 4, // ev_quat - 1, // ev_integer - 1, // ev_uinteger - 0, // ev_short value in opcode +#define EV_TYPE(type) PR_SIZEOF(type), +VISIBLE const pr_ushort_t pr_type_size[ev_type_count] = { +#include "QF/progs/pr_type_names.h" + 0, // ev_invalid not a valid/simple type }; -VISIBLE const char *pr_type_name[ev_type_count] = { - "void", - "string", - "float", - "vector", - "entity", - "field", - "function", - "pointer", - "quaternion", - "integer", - "uinteger", - "short", +#define EV_TYPE(type) PR_ALIGNOF(type), +VISIBLE const pr_ushort_t pr_type_alignment[ev_type_count] = { +#include "QF/progs/pr_type_names.h" + 0, // ev_invalid not a valid/simple type +}; + +#define EV_TYPE(type) #type, +VISIBLE const char * const pr_type_name[ev_type_count] = { +#include "QF/progs/pr_type_names.h" "invalid", }; -// default format is "%Ga, %Gb, %gc" -// V global_string, contents, void -// G global_string, contents -// g global_string, no contents -// s as short -// O address + short -// P function parameter -// F function (must come before any P) -// R return value -// E entity + field (%Eab) -// -// a operand a -// b operand b -// c operand c -// x place holder for P (padding) -// 0-7 parameter index (for P) -VISIBLE opcode_t pr_opcodes[] = { - {"", "done", OP_DONE, false, // OP_DONE is actually the same as - ev_entity, ev_field, ev_void, // OP_RETURN, the types are bogus - PROG_ID_VERSION, - "%Va", - }, - - {"*", "mul.f", OP_MUL_F, false, - ev_float, ev_float, ev_float, - PROG_ID_VERSION, - }, - {"*", "mul.v", OP_MUL_V, false, - ev_vector, ev_vector, ev_float, - PROG_ID_VERSION, - }, - {"*", "mul.fv", OP_MUL_FV, false, - ev_float, ev_vector, ev_vector, - PROG_ID_VERSION, - }, - {"*", "mul.vf", OP_MUL_VF, false, - ev_vector, ev_float, ev_vector, - PROG_ID_VERSION, - }, - {"*", "mul.q", OP_MUL_Q, false, - ev_quat, ev_quat, ev_quat, - PROG_VERSION, - }, - {"*", "mul.fq", OP_MUL_FQ, false, - ev_float, ev_quat, ev_quat, - PROG_VERSION, - }, - {"*", "mul.qf", OP_MUL_QF, false, - ev_quat, ev_float, ev_quat, - PROG_VERSION, - }, - {"*", "mul.qv", OP_MUL_QV, false, - ev_quat, ev_vector, ev_vector, - PROG_VERSION, - }, - - {"~", "conj.q", OP_CONJ_Q, false, - ev_quat, ev_invalid, ev_quat, - PROG_VERSION, - "%Ga, %gc", - }, - - {"/", "div.f", OP_DIV_F, false, - ev_float, ev_float, ev_float, - PROG_ID_VERSION, - }, - - {"+", "add.f", OP_ADD_F, false, - ev_float, ev_float, ev_float, - PROG_ID_VERSION, - }, - {"+", "add.v", OP_ADD_V, false, - ev_vector, ev_vector, ev_vector, - PROG_ID_VERSION, - }, - {"+", "add.q", OP_ADD_Q, false, - ev_quat, ev_quat, ev_quat, - PROG_VERSION, - }, - {"+", "add.s", OP_ADD_S, false, - ev_string, ev_string, ev_string, - PROG_VERSION, - }, - - {"-", "sub.f", OP_SUB_F, false, - ev_float, ev_float, ev_float, - PROG_ID_VERSION, - }, - {"-", "sub.v", OP_SUB_V, false, - ev_vector, ev_vector, ev_vector, - PROG_ID_VERSION, - }, - {"-", "sub.q", OP_SUB_Q, false, - ev_quat, ev_quat, ev_quat, - PROG_VERSION, - }, - - {"==", "eq.f", OP_EQ_F, false, - ev_float, ev_float, ev_integer, - PROG_ID_VERSION, - }, - {"==", "eq.v", OP_EQ_V, false, - ev_vector, ev_vector, ev_integer, - PROG_ID_VERSION, - }, - {"==", "eq.q", OP_EQ_Q, false, - ev_quat, ev_quat, ev_integer, - PROG_VERSION, - }, - {"==", "eq.s", OP_EQ_S, false, - ev_string, ev_string, ev_integer, - PROG_ID_VERSION, - }, - {"==", "eq.e", OP_EQ_E, false, - ev_entity, ev_entity, ev_integer, - PROG_ID_VERSION, - }, - {"==", "eq.fn", OP_EQ_FN, false, - ev_func, ev_func, ev_integer, - PROG_ID_VERSION, - }, - - {"!=", "ne.f", OP_NE_F, false, - ev_float, ev_float, ev_integer, - PROG_ID_VERSION, - }, - {"!=", "ne.v", OP_NE_V, false, - ev_vector, ev_vector, ev_integer, - PROG_ID_VERSION, - }, - {"!=", "ne.q", OP_NE_Q, false, - ev_quat, ev_quat, ev_integer, - PROG_VERSION, - }, - {"!=", "ne.s", OP_NE_S, false, - ev_string, ev_string, ev_integer, - PROG_ID_VERSION, - }, - {"!=", "ne.e", OP_NE_E, false, - ev_entity, ev_entity, ev_integer, - PROG_ID_VERSION, - }, - {"!=", "ne.fn", OP_NE_FN, false, - ev_func, ev_func, ev_integer, - PROG_ID_VERSION, - }, - - {"<=", "le.f", OP_LE_F, false, - ev_float, ev_float, ev_integer, - PROG_ID_VERSION, - }, - {">=", "ge.f", OP_GE_F, false, - ev_float, ev_float, ev_integer, - PROG_ID_VERSION, - }, - {"<=", "le.s", OP_LE_S, false, - ev_string, ev_string, ev_integer, - PROG_VERSION, - }, - {">=", "ge.s", OP_GE_S, false, - ev_string, ev_string, ev_integer, - PROG_VERSION, - }, - {"<", "lt.f", OP_LT_F, false, - ev_float, ev_float, ev_integer, - PROG_ID_VERSION, - }, - {">", "gt.f", OP_GT_F, false, - ev_float, ev_float, ev_integer, - PROG_ID_VERSION, - }, - {"<", "lt.s", OP_LT_S, false, - ev_string, ev_string, ev_integer, - PROG_VERSION, - }, - {">", "gt.s", OP_GT_S, false, - ev_string, ev_string, ev_integer, - PROG_VERSION, - }, - - {".", "load.f", OP_LOAD_F, false, - ev_entity, ev_field, ev_float, - PROG_ID_VERSION, - "%Ga.%Gb(%Ec), %gc",//FIXME %E more flexible? - }, - {".", "load.v", OP_LOAD_V, false, - ev_entity, ev_field, ev_vector, - PROG_ID_VERSION, - "%Ga.%Gb(%Ec), %gc", - }, - {".", "load.q", OP_LOAD_Q, false, - ev_entity, ev_field, ev_quat, - PROG_VERSION, - "%Ga.%Gb(%Ec), %gc", - }, - {".", "load.s", OP_LOAD_S, false, - ev_entity, ev_field, ev_string, - PROG_ID_VERSION, - "%Ga.%Gb(%Ec), %gc", - }, - {".", "load.ent", OP_LOAD_ENT, false, - ev_entity, ev_field, ev_entity, - PROG_ID_VERSION, - "%Ga.%Gb(%Ec), %gc", - }, - {".", "load.fld", OP_LOAD_FLD, false, - ev_entity, ev_field, ev_field, - PROG_ID_VERSION, - "%Ga.%Gb(%Ec), %gc", - }, - {".", "load.fn", OP_LOAD_FN, false, - ev_entity, ev_field, ev_func, - PROG_ID_VERSION, - "%Ga.%Gb(%Ec), %gc", - }, - {".", "load.i", OP_LOAD_I, false, - ev_entity, ev_field, ev_integer, - PROG_VERSION, - "%Ga.%Gb(%Ec), %gc", - }, - {".", "load.p", OP_LOAD_P, false, - ev_entity, ev_field, ev_pointer, - PROG_VERSION, - "%Ga.%Gb(%Ec), %gc", - }, - - {".", "loadb.f", OP_LOADB_F, false, - ev_pointer, ev_integer, ev_float, - PROG_VERSION, - "*(%Ga + %Gb), %gc", - }, - {".", "loadb.v", OP_LOADB_V, false, - ev_pointer, ev_integer, ev_vector, - PROG_VERSION, - "*(%Ga + %Gb), %gc", - }, - {".", "loadb.q", OP_LOADB_Q, false, - ev_pointer, ev_integer, ev_quat, - PROG_VERSION, - "*(%Ga + %Gb), %gc", - }, - {".", "loadb.s", OP_LOADB_S, false, - ev_pointer, ev_integer, ev_string, - PROG_VERSION, - "*(%Ga + %Gb), %gc", - }, - {".", "loadb.ent", OP_LOADB_ENT, false, - ev_pointer, ev_integer, ev_entity, - PROG_VERSION, - "*(%Ga + %Gb), %gc", - }, - {".", "loadb.fld", OP_LOADB_FLD, false, - ev_pointer, ev_integer, ev_field, - PROG_VERSION, - "*(%Ga + %Gb), %gc", - }, - {".", "loadb.fn", OP_LOADB_FN, false, - ev_pointer, ev_integer, ev_func, - PROG_VERSION, - "*(%Ga + %Gb), %gc", - }, - {".", "loadb.i", OP_LOADB_I, false, - ev_pointer, ev_integer, ev_integer, - PROG_VERSION, - "*(%Ga + %Gb), %gc", - }, - {".", "loadb.p", OP_LOADB_P, false, - ev_pointer, ev_integer, ev_pointer, - PROG_VERSION, - "*(%Ga + %Gb), %gc", - }, - - {".", "loadbi.f", OP_LOADBI_F, false, - ev_pointer, ev_short, ev_float, - PROG_VERSION, - "*(%Ga + %sb), %gc", - }, - {".", "loadbi.v", OP_LOADBI_V, false, - ev_pointer, ev_short, ev_vector, - PROG_VERSION, - "*(%Ga + %sb), %gc", - }, - {".", "loadbi.q", OP_LOADBI_Q, false, - ev_pointer, ev_short, ev_quat, - PROG_VERSION, - "*(%Ga + %sb), %gc", - }, - {".", "loadbi.s", OP_LOADBI_S, false, - ev_pointer, ev_short, ev_string, - PROG_VERSION, - "*(%Ga + %sb), %gc", - }, - {".", "loadbi.ent", OP_LOADBI_ENT, false, - ev_pointer, ev_short, ev_entity, - PROG_VERSION, - "*(%Ga + %sb), %gc", - }, - {".", "loadbi.fld", OP_LOADBI_FLD, false, - ev_pointer, ev_short, ev_field, - PROG_VERSION, - "*(%Ga + %sb), %gc", - }, - {".", "loadbi.fn", OP_LOADBI_FN, false, - ev_pointer, ev_short, ev_func, - PROG_VERSION, - "*(%Ga + %sb), %gc", - }, - {".", "loadbi.i", OP_LOADBI_I, false, - ev_pointer, ev_short, ev_integer, - PROG_VERSION, - "*(%Ga + %sb), %gc", - }, - {".", "loadbi.p", OP_LOADBI_P, false, - ev_pointer, ev_short, ev_pointer, - PROG_VERSION, - "*(%Ga + %sb), %gc", - }, - - {"&", "address", OP_ADDRESS, false, - ev_entity, ev_field, ev_pointer, - PROG_ID_VERSION, - "%Ga.%Gb(%Ec), %gc", - }, - - {"&", "address", OP_ADDRESS_VOID, false, - ev_void, ev_invalid, ev_pointer, - PROG_VERSION, - "%Ga, %gc", - }, - {"&", "address.f", OP_ADDRESS_F, false, - ev_float, ev_invalid, ev_pointer, - PROG_VERSION, - "%Ga, %gc", - }, - {"&", "address.v", OP_ADDRESS_V, false, - ev_vector, ev_invalid, ev_pointer, - PROG_VERSION, - "%Ga, %gc", - }, - {"&", "address.q", OP_ADDRESS_Q, false, - ev_quat, ev_invalid, ev_pointer, - PROG_VERSION, - "%Ga, %gc", - }, - {"&", "address.s", OP_ADDRESS_S, false, - ev_string, ev_invalid, ev_pointer, - PROG_VERSION, - "%Ga, %gc", - }, - {"&", "address.ent", OP_ADDRESS_ENT, false, - ev_entity, ev_invalid, ev_pointer, - PROG_VERSION, - "%Ga, %gc", - }, - {"&", "address.fld", OP_ADDRESS_FLD, false, - ev_field, ev_invalid, ev_pointer, - PROG_VERSION, - "%Ga, %gc", - }, - {"&", "address.fn", OP_ADDRESS_FN, false, - ev_func, ev_invalid, ev_pointer, - PROG_VERSION, - "%Ga, %gc", - }, - {"&", "address.i", OP_ADDRESS_I, false, - ev_integer, ev_invalid, ev_pointer, - PROG_VERSION, - "%Ga, %gc", - }, - {"&", "address.p", OP_ADDRESS_P, false, - ev_pointer, ev_invalid, ev_pointer, - PROG_VERSION, - "%Ga, %gc", - }, - - {"&", "lea", OP_LEA, false, - ev_pointer, ev_integer, ev_pointer, - PROG_VERSION, - "%Ga, %Gb, %gc", - }, - {"&", "leai", OP_LEAI, false, - ev_pointer, ev_short, ev_pointer, - PROG_VERSION, - "%Ga, %sb, %gc", - }, - - {"", "conv.if", OP_CONV_IF, false, - ev_integer, ev_invalid, ev_float, - PROG_VERSION, - "%Ga, %gc", - }, - {"", "conv.fi", OP_CONV_FI, false, - ev_float, ev_invalid, ev_integer, - PROG_VERSION, - "%Ga, %gc", - }, - - {"=", "store.f", OP_STORE_F, true, - ev_float, ev_float, ev_invalid, - PROG_ID_VERSION, - "%Ga, %gb", - }, - {"=", "store.v", OP_STORE_V, true, - ev_vector, ev_vector, ev_invalid, - PROG_ID_VERSION, - "%Ga, %gb", - }, - {"=", "store.q", OP_STORE_Q, true, - ev_quat, ev_quat, ev_invalid, - PROG_VERSION, - "%Ga, %gb", - }, - {"=", "store.s", OP_STORE_S, true, - ev_string, ev_string, ev_invalid, - PROG_ID_VERSION, - "%Ga, %gb", - }, - {"=", "store.ent", OP_STORE_ENT, true, - ev_entity, ev_entity, ev_invalid, - PROG_ID_VERSION, - "%Ga, %gb", - }, - {"=", "store.fld", OP_STORE_FLD, true, - ev_field, ev_field, ev_invalid, - PROG_ID_VERSION, - "%Ga, %gb", - }, - {"=", "store.fn", OP_STORE_FN, true, - ev_func, ev_func, ev_invalid, - PROG_ID_VERSION, - "%Ga, %gb", - }, - {"=", "store.i", OP_STORE_I, true, - ev_integer, ev_integer, ev_invalid, - PROG_VERSION, - "%Ga, %gb", - }, - {"=", "store.p", OP_STORE_P, true, - ev_pointer, ev_pointer, ev_invalid, - PROG_VERSION, - "%Ga, %gb", - }, - - {".=", "storep.f", OP_STOREP_F, true, - ev_float, ev_pointer, ev_invalid, - PROG_ID_VERSION, - "%Ga, *%Gb", - }, - {".=", "storep.v", OP_STOREP_V, true, - ev_vector, ev_pointer, ev_invalid, - PROG_ID_VERSION, - "%Ga, *%Gb", - }, - {".=", "storep.q", OP_STOREP_Q, true, - ev_quat, ev_pointer, ev_invalid, - PROG_VERSION, - "%Ga, *%Gb", - }, - {".=", "storep.s", OP_STOREP_S, true, - ev_string, ev_pointer, ev_invalid, - PROG_ID_VERSION, - "%Ga, *%Gb", - }, - {".=", "storep.ent", OP_STOREP_ENT, true, - ev_entity, ev_pointer, ev_invalid, - PROG_ID_VERSION, - "%Ga, *%Gb", - }, - {".=", "storep.fld", OP_STOREP_FLD, true, - ev_field, ev_pointer, ev_invalid, - PROG_ID_VERSION, - "%Ga, *%Gb", - }, - {".=", "storep.fn", OP_STOREP_FN, true, - ev_func, ev_pointer, ev_invalid, - PROG_ID_VERSION, - "%Ga, *%Gb", - }, - {".=", "storep.i", OP_STOREP_I, true, - ev_integer, ev_pointer, ev_invalid, - PROG_VERSION, - "%Ga, *%Gb", - }, - {".=", "storep.p", OP_STOREP_P, true, - ev_pointer, ev_pointer, ev_invalid, - PROG_VERSION, - "%Ga, *%Gb", - }, - - {".=", "storeb.f", OP_STOREB_F, true, - ev_float, ev_pointer, ev_integer, - PROG_VERSION, - "%Ga, *(%Gb + %Gc)", - }, - {".=", "storeb.v", OP_STOREB_V, true, - ev_vector, ev_pointer, ev_integer, - PROG_VERSION, - "%Ga, *(%Gb + %Gc)", - }, - {".=", "storeb.q", OP_STOREB_Q, true, - ev_quat, ev_pointer, ev_integer, - PROG_VERSION, - "%Ga, *(%Gb + %Gc)", - }, - {".=", "storeb.s", OP_STOREB_S, true, - ev_string, ev_pointer, ev_integer, - PROG_VERSION, - "%Ga, *(%Gb + %Gc)", - }, - {".=", "storeb.ent", OP_STOREB_ENT, true, - ev_entity, ev_pointer, ev_integer, - PROG_VERSION, - "%Ga, *(%Gb + %Gc)", - }, - {".=", "storeb.fld", OP_STOREB_FLD, true, - ev_field, ev_pointer, ev_integer, - PROG_VERSION, - "%Ga, *(%Gb + %Gc)", - }, - {".=", "storeb.fn", OP_STOREB_FN, true, - ev_func, ev_pointer, ev_integer, - PROG_VERSION, - "%Ga, *(%Gb + %Gc)", - }, - {".=", "storeb.i", OP_STOREB_I, true, - ev_integer, ev_pointer, ev_integer, - PROG_VERSION, - "%Ga, *(%Gb + %Gc)", - }, - {".=", "storeb.p", OP_STOREB_P, true, - ev_pointer, ev_pointer, ev_integer, - PROG_VERSION, - "%Ga, *(%Gb + %Gc)", - }, - - {".=", "storebi.f", OP_STOREBI_F, true, - ev_float, ev_pointer, ev_short, - PROG_VERSION, - "%Ga, *(%Gb + %sc)", - }, - {".=", "storebi.v", OP_STOREBI_V, true, - ev_vector, ev_pointer, ev_short, - PROG_VERSION, - "%Ga, *(%Gb + %sc)", - }, - {".=", "storebi.q", OP_STOREBI_Q, true, - ev_quat, ev_pointer, ev_short, - PROG_VERSION, - "%Ga, *(%Gb + %sc)", - }, - {".=", "storebi.s", OP_STOREBI_S, true, - ev_string, ev_pointer, ev_short, - PROG_VERSION, - "%Ga, *(%Gb + %sc)", - }, - {".=", "storebi.ent", OP_STOREBI_ENT, true, - ev_entity, ev_pointer, ev_short, - PROG_VERSION, - "%Ga, *(%Gb + %sc)", - }, - {".=", "storebi.fld", OP_STOREBI_FLD, true, - ev_field, ev_pointer, ev_short, - PROG_VERSION, - "%Ga, *(%Gb + %sc)", - }, - {".=", "storebi.fn", OP_STOREBI_FN, true, - ev_func, ev_pointer, ev_short, - PROG_VERSION, - "%Ga, *(%Gb + %sc)", - }, - {".=", "storebi.i", OP_STOREBI_I, true, - ev_integer, ev_pointer, ev_short, - PROG_VERSION, - "%Ga, *(%Gb + %sc)", - }, - {".=", "storebi.p", OP_STOREBI_P, true, - ev_pointer, ev_pointer, ev_short, - PROG_VERSION, - "%Ga, *(%Gb + %sc)", - }, - - {"", "return", OP_RETURN, false, - ev_void, ev_invalid, ev_invalid, - PROG_ID_VERSION, - "%Ra", - }, - - {"", "return", OP_RETURN_V, false, - ev_invalid, ev_invalid, ev_invalid, - PROG_VERSION, - "", - }, - - {"!", "not.f", OP_NOT_F, false, - ev_float, ev_invalid, ev_integer, - PROG_ID_VERSION, - "%Ga, %gc", - }, - {"!", "not.v", OP_NOT_V, false, - ev_vector, ev_invalid, ev_integer, - PROG_ID_VERSION, - "%Ga, %gc", - }, - {"!", "not.q", OP_NOT_Q, false, - ev_quat, ev_invalid, ev_integer, - PROG_VERSION, - "%Ga, %gc", - }, - {"!", "not.s", OP_NOT_S, false, - ev_string, ev_invalid, ev_integer, - PROG_ID_VERSION, - "%Ga, %gc", - }, - {"!", "not.ent", OP_NOT_ENT, false, - ev_entity, ev_invalid, ev_integer, - PROG_ID_VERSION, - "%Ga, %gc", - }, - {"!", "not.fn", OP_NOT_FN, false, - ev_func, ev_invalid, ev_integer, - PROG_ID_VERSION, - "%Ga, %gc", - }, - {"!", "not.p", OP_NOT_P, false, - ev_pointer, ev_invalid, ev_integer, - PROG_VERSION, - "%Ga, %gc", - }, - - {"", "if", OP_IF, false, - ev_integer, ev_short, ev_invalid, - PROG_ID_VERSION, - "%Ga branch %sb (%Ob)", - }, - {"", "ifnot", OP_IFNOT, false, - ev_integer, ev_short, ev_invalid, - PROG_ID_VERSION, - "%Ga branch %sb (%Ob)", - }, - {"", "ifbe", OP_IFBE, true, - ev_integer, ev_short, ev_invalid, - PROG_VERSION, - "%Ga branch %sb (%Ob)", - }, - {"", "ifb", OP_IFB, true, - ev_integer, ev_short, ev_invalid, - PROG_VERSION, - "%Ga branch %sb (%Ob)", - }, - {"", "ifae", OP_IFAE, true, - ev_integer, ev_short, ev_invalid, - PROG_VERSION, - "%Ga branch %sb (%Ob)", - }, - {"", "ifa", OP_IFA, true, - ev_integer, ev_short, ev_invalid, - PROG_VERSION, - "%Ga branch %sb (%Ob)", - }, - -// calls returns REG_RETURN - {"", "call0", OP_CALL0, false, - ev_func, ev_invalid, ev_invalid, - PROG_ID_VERSION, - "%Fa ()", - }, - {"", "call1", OP_CALL1, false, - ev_func, ev_invalid, ev_invalid, - PROG_ID_VERSION, - "%Fa (%P0x)", - }, - {"", "call2", OP_CALL2, false, - ev_func, ev_invalid, ev_invalid, - PROG_ID_VERSION, - "%Fa (%P0x, %P1x)", - }, - {"", "call3", OP_CALL3, false, - ev_func, ev_invalid, ev_invalid, - PROG_ID_VERSION, - "%Fa (%P0x, %P1x, %P2x)", - }, - {"", "call4", OP_CALL4, false, - ev_func, ev_invalid, ev_invalid, - PROG_ID_VERSION, - "%Fa (%P0x, %P1x, %P2x, %P3x)", - }, - {"", "call5", OP_CALL5, false, - ev_func, ev_invalid, ev_invalid, - PROG_ID_VERSION, - "%Fa (%P0x, %P1x, %P2x, %P3x, %P4x)", - }, - {"", "call6", OP_CALL6, false, - ev_func, ev_invalid, ev_invalid, - PROG_ID_VERSION, - "%Fa (%P0x, %P1x, %P2x, %P3x, %P4x, %P5x)", - }, - {"", "call7", OP_CALL7, false, - ev_func, ev_invalid, ev_invalid, - PROG_ID_VERSION, - "%Fa (%P0x, %P1x, %P2x, %P3x, %P4x, %P5x, %P6x)", - }, - {"", "call8", OP_CALL8, false, - ev_func, ev_invalid, ev_invalid, - PROG_ID_VERSION, - "%Fa (%P0x, %P1x, %P2x, %P3x, %P4x, %P5x, %P6x, %P7x)", - }, - {"", "rcall1", OP_RCALL1, false, - ev_func, ev_void, ev_invalid, - PROG_VERSION, - "%Fa (%P0b)", - }, - {"", "rcall2", OP_RCALL2, false, - ev_func, ev_void, ev_void, - PROG_VERSION, - "%Fa (%P0b, %P1c)", - }, - {"", "rcall3", OP_RCALL3, false, - ev_func, ev_void, ev_void, - PROG_VERSION, - "%Fa (%P0b, %P1c, %P2x)", - }, - {"", "rcall4", OP_RCALL4, false, - ev_func, ev_void, ev_void, - PROG_VERSION, - "%Fa (%P0b, %P1c, %P2x, %P3x)", - }, - {"", "rcall5", OP_RCALL5, false, - ev_func, ev_void, ev_void, - PROG_VERSION, - "%Fa (%P0b, %P1c, %P2x, %P3x, %P4x)", - }, - {"", "rcall6", OP_RCALL6, false, - ev_func, ev_void, ev_void, - PROG_VERSION, - "%Fa (%P0b, %P1c, %P2x, %P3x, %P4x, %P5x)", - }, - {"", "rcall7", OP_RCALL7, false, - ev_func, ev_void, ev_void, - PROG_VERSION, - "%Fa (%P0b, %P1c, %P2x, %P3x, %P4x, %P5x, %P6x)", - }, - {"", "rcall8", OP_RCALL8, false, - ev_func, ev_void, ev_void, - PROG_VERSION, - "%Fa (%P0b, %P1c, %P2x, %P3x, %P4x, %P5x, %P6x, %P7x)", - }, - - {"", "state", OP_STATE, false, - ev_float, ev_func, ev_invalid, - PROG_ID_VERSION, - "%Ga, %Gb", - }, - - {"", "state.f", OP_STATE_F, false, - ev_float, ev_func, ev_float, - PROG_VERSION, - "%Ga, %Gb, %Gc", - }, - - {"", "goto", OP_GOTO, false, - ev_short, ev_invalid, ev_invalid, - PROG_ID_VERSION, - "branch %sa (%Oa)", - }, - {"", "jump", OP_JUMP, false, - ev_integer, ev_invalid, ev_invalid, - PROG_VERSION, - "%Ga", - }, - {"", "jumpb", OP_JUMPB, false, - ev_void, ev_integer, ev_invalid, - PROG_VERSION, - "%Ga[%Gb]", - }, - - {"&&", "and.f", OP_AND, false, - ev_float, ev_float, ev_integer, - PROG_ID_VERSION, - }, - {"||", "or.f", OP_OR, false, - ev_float, ev_float, ev_integer, - PROG_ID_VERSION, - }, - - {"<<", "shl.f", OP_SHL_F, false, - ev_float, ev_float, ev_float, - PROG_VERSION, - }, - {">>", "shr.f", OP_SHR_F, false, - ev_float, ev_float, ev_float, - PROG_VERSION, - }, - {"<<", "shl.i", OP_SHL_I, false, - ev_integer, ev_integer, ev_integer, - PROG_VERSION, - }, - {">>", "shr.i", OP_SHR_I, false, - ev_integer, ev_integer, ev_integer, - PROG_VERSION, - }, - {">>", "shr.u", OP_SHR_U, false, - ev_uinteger, ev_integer, ev_uinteger, - PROG_VERSION, - }, - - {"&", "bitand", OP_BITAND, false, - ev_float, ev_float, ev_float, - PROG_ID_VERSION, - }, - {"|", "bitor", OP_BITOR, false, - ev_float, ev_float, ev_float, - PROG_ID_VERSION, - }, - - {"+", "add.i", OP_ADD_I, false, - ev_integer, ev_integer, ev_integer, - PROG_VERSION, - }, - {"-", "sub.i", OP_SUB_I, false, - ev_integer, ev_integer, ev_integer, - PROG_VERSION, - }, - {"*", "mul.i", OP_MUL_I, false, - ev_integer, ev_integer, ev_integer, - PROG_VERSION, - }, - {"/", "div.i", OP_DIV_I, false, - ev_integer, ev_integer, ev_integer, - PROG_VERSION, - }, - {"%", "mod.i", OP_MOD_I, false, - ev_integer, ev_integer, ev_integer, - PROG_VERSION, - }, - {"&", "bitand.i", OP_BITAND_I, false, - ev_integer, ev_integer, ev_integer, - PROG_VERSION, - }, - {"|", "bitor.i", OP_BITOR_I, false, - ev_integer, ev_integer, ev_integer, - PROG_VERSION, - }, - - {"%", "mod.f", OP_MOD_F, false, - ev_float, ev_float, ev_float, - PROG_VERSION, - }, - - {">=", "ge.i", OP_GE_I, false, - ev_integer, ev_integer, ev_integer, - PROG_VERSION, - }, - {"<=", "le.i", OP_LE_I, false, - ev_integer, ev_integer, ev_integer, - PROG_VERSION, - }, - {">", "gt.i", OP_GT_I, false, - ev_integer, ev_integer, ev_integer, - PROG_VERSION, - }, - {"<", "lt.i", OP_LT_I, false, - ev_integer, ev_integer, ev_integer, - PROG_VERSION, - }, - - {"&&", "and.i", OP_AND_I, false, - ev_integer, ev_integer, ev_integer, - PROG_VERSION, - }, - {"||", "or.i", OP_OR_I, false, - ev_integer, ev_integer, ev_integer, - PROG_VERSION, - }, - {"!", "not.i", OP_NOT_I, false, - ev_integer, ev_invalid, ev_integer, - PROG_VERSION, - "%Ga, %gc", - }, - {"==", "eq.i", OP_EQ_I, false, - ev_integer, ev_integer, ev_integer, - PROG_VERSION, - }, - {"!=", "ne.i", OP_NE_I, false, - ev_integer, ev_integer, ev_integer, - PROG_VERSION, - }, - - {">=", "ge.u", OP_GE_U, false, - ev_uinteger, ev_uinteger, ev_integer, - PROG_VERSION, - }, - {"<=", "le.u", OP_LE_U, false, - ev_uinteger, ev_uinteger, ev_integer, - PROG_VERSION, - }, - {">", "gt.u", OP_GT_U, false, - ev_uinteger, ev_uinteger, ev_integer, - PROG_VERSION, - }, - {"<", "lt.u", OP_LT_U, false, - ev_uinteger, ev_uinteger, ev_integer, - PROG_VERSION, - }, - - {"^", "bitxor.f", OP_BITXOR_F, false, - ev_float, ev_float, ev_float, - PROG_VERSION, - }, - {"~", "bitnot.f", OP_BITNOT_F, false, - ev_float, ev_invalid, ev_float, - PROG_VERSION, - "%Ga, %gc", - }, - {"^", "bitxor.i", OP_BITXOR_I, false, - ev_integer, ev_integer, ev_integer, - PROG_VERSION, - }, - {"~", "bitnot.i", OP_BITNOT_I, false, - ev_integer, ev_invalid, ev_integer, - PROG_VERSION, - "%Ga, %gc", - }, - - {">=", "ge.p", OP_GE_P, false, - ev_pointer, ev_pointer, ev_integer, - PROG_VERSION, - }, - {"<=", "le.p", OP_LE_P, false, - ev_pointer, ev_pointer, ev_integer, - PROG_VERSION, - }, - {">", "gt.p", OP_GT_P, false, - ev_pointer, ev_pointer, ev_integer, - PROG_VERSION, - }, - {"<", "lt.p", OP_LT_P, false, - ev_pointer, ev_pointer, ev_integer, - PROG_VERSION, - }, - {"==", "eq.p", OP_EQ_P, false, - ev_pointer, ev_pointer, ev_integer, - PROG_VERSION, - }, - {"!=", "ne.p", OP_NE_P, false, - ev_pointer, ev_pointer, ev_integer, - PROG_VERSION, - }, - - {"", "movei", OP_MOVEI, true, - ev_void, ev_short, ev_void, - PROG_VERSION, - "%Ga, %sb, %gc", - }, - {"", "movep", OP_MOVEP, true, - ev_pointer, ev_integer, ev_pointer, - PROG_VERSION, - "%Ga, %Gb, %Gc", - }, - {"", "movepi", OP_MOVEPI, true, - ev_pointer, ev_short, ev_pointer, - PROG_VERSION, - "%Ga, %Gb, %Gc", - }, - - // end of table - {0}, +const opcode_t pr_opcodes[512] = { +#include "libs/gamecode/pr_opcode.cinc" }; - -static uintptr_t -opcode_get_hash (const void *op, void *unused) +const opcode_t * +PR_Opcode (pr_ushort_t opcode) { - return ((opcode_t *)op)->opcode; -} - -static int -opcode_compare (const void *_opa, const void *_opb, void *unused) -{ - opcode_t *opa = (opcode_t *)_opa; - opcode_t *opb = (opcode_t *)_opb; - - return opa->opcode == opb->opcode; -} - -opcode_t * -PR_Opcode (pr_short_t opcode) -{ - opcode_t op; - - op.opcode = opcode; - return Hash_FindElement (opcode_table, &op); -} - -VISIBLE void -PR_Opcode_Init (void) -{ - opcode_t *op; - - opcode_table = Hash_NewTable (1021, 0, 0, 0); - Hash_SetHashCompare (opcode_table, opcode_get_hash, opcode_compare); - - for (op = pr_opcodes; op->name; op++) { - Hash_AddElement (opcode_table, op); - } -} - -static inline void -check_branch (progs_t *pr, dstatement_t *st, opcode_t *op, short offset) -{ - pr_int_t address = st - pr->pr_statements; - - address += offset; - if (address < 0 || (pr_uint_t) address >= pr->progs->numstatements) - PR_Error (pr, "PR_Check_Opcodes: invalid branch (statement %ld: %s)", - (long)(st - pr->pr_statements), op->opname); -} - -static int -is_vector_parameter_store (progs_t *pr, dstatement_t *st, - unsigned short operand) -{ - int i; - - if (st->op != OP_STORE_V) - return 0; - if (operand != st->a) - return 0; - for (i = 0; i < MAX_PARMS; i++) - if (st->b == pr->pr_params[i] - pr->pr_globals) - return 1; - return 0; -} - -#define ISDENORM(x) ((x) && !((x) & 0x7f800000)) - -static inline void -check_global (progs_t *pr, dstatement_t *st, opcode_t *op, etype_t type, - unsigned short operand, int check_denorm) -{ - const char *msg; - ddef_t *def; - - switch (type) { - case ev_short: - break; - case ev_invalid: - if (operand) { - msg = "non-zero global index in invalid operand"; - goto error; - } - break; - default: - if (operand + (unsigned) pr_type_size[type] - > pr->progs->numglobals) { - if (operand >= pr->progs->numglobals - || !is_vector_parameter_store (pr, st, operand)) { - msg = "out of bounds global index"; - goto error; - } - } - if (type != ev_float || !check_denorm) - break; - if (!ISDENORM (G_INT (pr, operand)) - || G_UINT(pr, operand) == 0x80000000) - break; - if ((def = PR_GlobalAtOfs (pr, operand)) - && (def->type & ~DEF_SAVEGLOBAL) != ev_float) { - // FTEqcc uses store.f parameters of most types :/ - break; - } - if (!pr->denorm_found) { - pr->denorm_found = 1; - if (pr_boundscheck->int_val) { - Sys_Printf ("DENORMAL floats detected, these progs might " - "not work. Good luck.\n"); - return; - } - msg = "DENORMAL float detected. These progs are probably " - "using qccx arrays and integers. If just simple arrays " - "are being used, then they should work, but if " - "internal.qc is used, they most definitely will NOT. To" - "allow these progs to be used, set pr_boundscheck to 1."; - goto error; - } - break; - } - return; -error: - PR_PrintStatement (pr, st, 0); - PR_Error (pr, "PR_Check_Opcodes: %s (statement %ld: %s)", msg, - (long)(st - pr->pr_statements), op->opname); -} - -static void -check_global_size (progs_t *pr, dstatement_t *st, opcode_t *op, - unsigned short size, unsigned short operand) -{ - const char *msg; - if (operand + size > pr->progs->numglobals) { - msg = "out of bounds global index"; - goto error; - } - return; -error: - PR_PrintStatement (pr, st, 0); - PR_Error (pr, "PR_Check_Opcodes: %s (statement %ld: %s)", msg, - (long)(st - pr->pr_statements), op->opname); + opcode &= OP_MASK; + return &pr_opcodes[opcode]; } int PR_Check_Opcodes (progs_t *pr) { - opcode_t *op; - dstatement_t *st; - int state_ok = 0; - pr_uint_t i; - - if (pr->globals.time && pr->globals.self && pr->fields.nextthink != -1 - && pr->fields.think != -1 && pr->fields.frame != -1) - state_ok = 1; - - //FIXME need to decide if I really want to always do static bounds checking - // the only problem is that it slows progs load a little, but it's the only - // way to check for qccx' evil - if (0 && !pr_boundscheck->int_val) { - for (i = 0, st = pr->pr_statements; i < pr->progs->numstatements; - st++, i++) { - op = PR_Opcode (st->op); - if (!op) { - PR_Error (pr, "PR_Check_Opcodes: unknown opcode %d at " - "statement %ld", st->op, - (long)(st - pr->pr_statements)); - } - if ((st->op == OP_STATE || st->op == OP_STATE_F) && !state_ok) { - PR_Error (pr, "PR_Check_Opcodes: %s used with missing fields " - "or globals", op->opname); - } - } - } else { - for (i = 0, st = pr->pr_statements; i < pr->progs->numstatements; - st++, i++) { - op = PR_Opcode (st->op); - if (!op) { - PR_Error (pr, "PR_Check_Opcodes: unknown opcode %d at " - "statement %ld", st->op, - (long)(st - pr->pr_statements)); - } - switch (st->op) { - case OP_IF: - case OP_IFNOT: - check_global (pr, st, op, op->type_a, st->a, 1); - check_branch (pr, st, op, st->b); - break; - case OP_GOTO: - check_branch (pr, st, op, st->a); - break; - case OP_DONE: - case OP_RETURN: - check_global (pr, st, op, ev_integer, st->a, 1); - check_global (pr, st, op, ev_void, st->b, 0); - check_global (pr, st, op, ev_void, st->c, 0); - break; - case OP_RCALL1: - check_global (pr, st, op, ev_void, st->c, 1); - case OP_RCALL2: - case OP_RCALL3: - case OP_RCALL4: - case OP_RCALL5: - case OP_RCALL6: - case OP_RCALL7: - case OP_RCALL8: - if (st->op > OP_RCALL1) - check_global (pr, st, op, ev_integer, st->c, 1); - check_global (pr, st, op, ev_integer, st->b, 1); - check_global (pr, st, op, ev_func, st->a, 1); - break; - case OP_STATE: - case OP_STATE_F: - if (!state_ok) { - PR_Error (pr, "PR_Check_Opcodes: %s used with missing " - "fields or globals", op->opname); - } - check_global (pr, st, op, op->type_a, st->a, 1); - check_global (pr, st, op, op->type_b, st->b, 1); - check_global (pr, st, op, op->type_c, st->c, 1); - break; - case OP_MOVEI: - check_global_size (pr, st, op, st->b, st->a); - check_global_size (pr, st, op, st->b, st->c); - break; - default: - check_global (pr, st, op, op->type_a, st->a, 1); - check_global (pr, st, op, op->type_b, st->b, - op->opcode != OP_STORE_F); - check_global (pr, st, op, op->type_c, st->c, 0); - break; - } - } + if (pr->progs->version < PROG_VERSION) { + return PR_Check_v6p_Opcodes (pr); } return 1; } diff --git a/libs/gamecode/pr_parse.c b/libs/gamecode/pr_parse.c index a8259ee78..56a01a37a 100644 --- a/libs/gamecode/pr_parse.c +++ b/libs/gamecode/pr_parse.c @@ -43,21 +43,12 @@ #include #endif -#include "QF/cbuf.h" -#include "QF/crc.h" -#include "QF/cvar.h" #include "QF/dstring.h" -#include "QF/hash.h" #include "QF/mathlib.h" +#include "QF/plist.h" #include "QF/progs.h" -#include "QF/qdefs.h" -#include "QF/qfplist.h" -#include "QF/qendian.h" -#include "QF/quakefs.h" #include "QF/script.h" #include "QF/sys.h" -#include "QF/zone.h" -#include "QF/va.h" #include "compat.h" @@ -68,47 +59,43 @@ Easier to parse than PR_ValueString */ static const char * -PR_UglyValueString (progs_t *pr, etype_t type, pr_type_t *val) +PR_UglyValueString (progs_t *pr, etype_t type, pr_type_t *val, dstring_t *line) { - static dstring_t *line = 0; - ddef_t *def; + pr_def_t *def; dfunction_t *f; - if (!line) - line = dstring_new (); - type &= ~DEF_SAVEGLOBAL; switch (type) { case ev_string: - dsprintf (line, "%s", PR_GetString (pr, val->string_var)); + dsprintf (line, "%s", PR_GetString (pr, PR_PTR (string, val))); break; case ev_entity: dsprintf (line, "%d", - NUM_FOR_BAD_EDICT (pr, PROG_TO_EDICT (pr, val->entity_var))); + NUM_FOR_BAD_EDICT (pr, PROG_TO_EDICT (pr, PR_PTR (entity, val)))); break; case ev_func: - f = pr->pr_functions + val->func_var; - dsprintf (line, "%s", PR_GetString (pr, f->s_name)); + f = pr->pr_functions + PR_PTR (func, val); + dsprintf (line, "%s", PR_GetString (pr, f->name)); break; case ev_field: - def = PR_FieldAtOfs (pr, val->integer_var); - dsprintf (line, "%s", PR_GetString (pr, def->s_name)); + def = PR_FieldAtOfs (pr, PR_PTR (int, val)); + dsprintf (line, "%s", PR_GetString (pr, def->name)); break; case ev_void: dstring_copystr (line, "void"); break; case ev_float: - dsprintf (line, "%.9g", val->float_var); + dsprintf (line, "%.9g", PR_PTR (float, val)); break; - case ev_integer: - dsprintf (line, "%d", val->integer_var); + case ev_int: + dsprintf (line, "%d", PR_PTR (int, val)); break; case ev_vector: - dsprintf (line, "%.9g %.9g %.9g", VectorExpand (val->vector_var)); + dsprintf (line, "%.9g %.9g %.9g", VectorExpand (&PR_PTR (float, val))); break; - case ev_quat: - dsprintf (line, "%.9g %.9g %.9g %.9g", QuatExpand (val->quat_var)); + case ev_quaternion: + dsprintf (line, "%.9g %.9g %.9g %.9g", QuatExpand (&PR_PTR (float, val))); break; default: dsprintf (line, "bad type %i", type); @@ -121,7 +108,8 @@ PR_UglyValueString (progs_t *pr, etype_t type, pr_type_t *val) VISIBLE plitem_t * ED_EntityDict (progs_t *pr, edict_t *ed) { - plitem_t *entity = PL_NewDictionary (); + dstring_t *dstr = dstring_newstr (); + plitem_t *entity = PL_NewDictionary (pr->hashctx); pr_uint_t i; int j; int type; @@ -130,29 +118,30 @@ ED_EntityDict (progs_t *pr, edict_t *ed) pr_type_t *v; if (!ed->free) { - for (i = 0; i < pr->progs->numfielddefs; i++) { - ddef_t *d = &pr->pr_fielddefs[i]; + for (i = 0; i < pr->progs->fielddefs.count; i++) { + pr_def_t *d = &pr->pr_fielddefs[i]; - name = PR_GetString (pr, d->s_name); + name = PR_GetString (pr, d->name); if (!name[0]) continue; // skip unnamed fields if (name[strlen (name) - 2] == '_') continue; // skip _x, _y, _z vars - v = &ed->v[d->ofs]; + v = &E_fld (ed, d->ofs); // if the value is still all 0, skip the field type = d->type & ~DEF_SAVEGLOBAL; for (j = 0; j < pr_type_size[type]; j++) - if (v[j].integer_var) + if (v[j].value) break; if (j == pr_type_size[type]) continue; - value = PR_UglyValueString (pr, type, v); + value = PR_UglyValueString (pr, type, v, dstr); PL_D_AddObject (entity, name, PL_NewString (value)); } } + dstring_delete (dstr); return entity; } @@ -165,14 +154,15 @@ ED_EntityDict (progs_t *pr, edict_t *ed) VISIBLE plitem_t * ED_GlobalsDict (progs_t *pr) { - plitem_t *globals = PL_NewDictionary (); + dstring_t *dstr = dstring_newstr (); + plitem_t *globals = PL_NewDictionary (pr->hashctx); pr_uint_t i; const char *name; const char *value; - ddef_t *def; + pr_def_t *def; int type; - for (i = 0; i < pr->progs->numglobaldefs; i++) { + for (i = 0; i < pr->progs->globaldefs.count; i++) { def = &pr->pr_globaldefs[i]; type = def->type; if (!(def->type & DEF_SAVEGLOBAL)) @@ -182,10 +172,11 @@ ED_GlobalsDict (progs_t *pr) if (type != ev_string && type != ev_float && type != ev_entity) continue; - name = PR_GetString (pr, def->s_name); - value = PR_UglyValueString (pr, type, &pr->pr_globals[def->ofs]); + name = PR_GetString (pr, def->name); + value = PR_UglyValueString (pr, type, &pr->pr_globals[def->ofs], dstr); PL_D_AddObject (globals, name, PL_NewString (value)); } + dstring_delete (dstr); return globals; } @@ -221,13 +212,10 @@ ED_NewString (progs_t *pr, const char *string) Can parse either fields or globals returns false if error */ -VISIBLE qboolean -ED_ParseEpair (progs_t *pr, pr_type_t *base, ddef_t *key, const char *s) +VISIBLE bool +ED_ParseEpair (progs_t *pr, pr_type_t *base, pr_def_t *key, const char *s) { - int i; - char *string; - ddef_t *def; - char *v, *w; + pr_def_t *def; pr_type_t *d; dfunction_t *func; @@ -235,29 +223,30 @@ ED_ParseEpair (progs_t *pr, pr_type_t *base, ddef_t *key, const char *s) switch (key->type & ~DEF_SAVEGLOBAL) { case ev_string: - d->string_var = ED_NewString (pr, s); + PR_PTR (string, d) = ED_NewString (pr, s); break; case ev_float: - d->float_var = atof (s); + PR_PTR (float, d) = atof (s); break; case ev_vector: - string = strdup (s); - v = string; - w = string; - for (i = 0; i < 3; i++) { - while (*v && *v != ' ') - v++; - *v = 0; - d->vector_var[i] = atof (w); - w = v = v + 1; + vec3_t vec = {}; + char *str = alloca (strlen (s) + 1); + strcpy (str, s); + for (char *v = str; *v; v++) { + if (*v == ',') { + *v = ' '; + } } - free (string); + if (sscanf (str, "%f %f %f", VectorExpandAddr (vec)) != 3) { + Sys_Printf ("Malformed vector %s\n", s); + } + VectorCopy (vec, PR_PTR (vector, d)); break; case ev_entity: - d->entity_var = EDICT_TO_PROG (pr, EDICT_NUM (pr, atoi (s))); + PR_PTR (entity, d) = EDICT_TO_PROG (pr, EDICT_NUM (pr, atoi (s))); break; case ev_field: @@ -266,7 +255,7 @@ ED_ParseEpair (progs_t *pr, pr_type_t *base, ddef_t *key, const char *s) Sys_Printf ("Can't find field %s\n", s); return false; } - d->integer_var = G_INT (pr, def->ofs); + PR_PTR (int, d) = G_INT (pr, def->ofs); break; case ev_func: @@ -275,7 +264,7 @@ ED_ParseEpair (progs_t *pr, pr_type_t *base, ddef_t *key, const char *s) Sys_Printf ("Can't find function %s\n", s); return false; } - d->func_var = func - pr->pr_functions; + PR_PTR (func, d) = func - pr->pr_functions; break; default: @@ -299,25 +288,31 @@ ED_ParseEpair (progs_t *pr, pr_type_t *base, ddef_t *key, const char *s) */ VISIBLE plitem_t * -ED_ConvertToPlist (script_t *script, int nohack) +ED_ConvertToPlist (script_t *script, int nohack, struct hashctx_s **hashctx) { + dstring_t *dstr = dstring_newstr (); plitem_t *plist = PL_NewArray (); plitem_t *ent; plitem_t *key; plitem_t *value; char *token; int anglehack; + const char *msg = ""; while (Script_GetToken (script, 1)) { token = script->token->str; - if (!strequal (token, "{")) - Sys_Error ("ED_ConvertToPlist: EOF without closing brace"); - ent = PL_NewDictionary (); + if (!strequal (token, "{")) { + msg = "EOF without closing brace"; + goto parse_error; + } + ent = PL_NewDictionary (hashctx); while (1) { int n; - if (!Script_GetToken (script, 1)) - Sys_Error ("ED_ConvertToPlist: EOF without closing brace"); + if (!Script_GetToken (script, 1)) { + msg = "EOF without closing brace"; + goto parse_error; + } token = script->token->str; if (strequal (token, "}")) break; @@ -335,30 +330,42 @@ ED_ConvertToPlist (script_t *script, int nohack) } else { key = PL_NewString (token); } - if (!Script_TokenAvailable (script, 0)) - Sys_Error ("ED_ConvertToPlist: EOL without value"); + if (!Script_TokenAvailable (script, 0)) { + msg = "EOL without value"; + goto parse_error; + } Script_GetToken (script, 0); token = script->token->str; - if (strequal (token, "}")) - Sys_Error ("ED_ConvertToPlist: closing brace without data"); - if (anglehack) - value = PL_NewString (va ("0 %s 0", token)); - else + if (strequal (token, "}")) { + msg = "closing brace without data"; + goto parse_error; + } + if (anglehack) { + dsprintf (dstr, "0 %s 0", token); + value = PL_NewString (dstr->str); + } else { value = PL_NewString (token); + } PL_D_AddObject (ent, PL_String (key), value); - PL_Free (key); + PL_Release (key); } PL_A_AddObject (plist, ent); } + dstring_delete (dstr); return plist; +parse_error: + Sys_Printf ("%s:%d: %s", script->file, script->line, msg); + dstring_delete (dstr); + PL_Release (plist); + return 0; } VISIBLE void ED_InitGlobals (progs_t *pr, plitem_t *globals) { - ddef_t vector_def; - ddef_t *global; + pr_def_t vector_def; + pr_def_t *global; plitem_t *keys; int count; const char *global_name; @@ -399,13 +406,13 @@ ED_InitGlobals (progs_t *pr, plitem_t *globals) if (!ED_ParseEpair (pr, pr->pr_globals, global, value)) PR_Error (pr, "ED_InitGlobals: parse error"); } - PL_Free (keys); + PL_Release (keys); } VISIBLE void ED_InitEntity (progs_t *pr, plitem_t *entity, edict_t *ent) { - ddef_t *field; + pr_def_t *field; plitem_t *keys; const char *field_name; const char *value; @@ -425,12 +432,12 @@ ED_InitEntity (progs_t *pr, plitem_t *entity, edict_t *ent) continue; } } else { - if (!ED_ParseEpair (pr, ent->v, field, value)) + if (!ED_ParseEpair (pr, &E_fld (ent, 0), field, value)) PR_Error (pr, "ED_InitEntity: parse error"); } init = 1; } - PL_Free (keys); + PL_Release (keys); if (!init) ent->free = 1; } @@ -446,7 +453,7 @@ ED_SpawnEntities (progs_t *pr, plitem_t *entity_list) int count; const char *classname; dfunction_t *func; - pr_int_t max_edicts = pr->pr_edictareasize / pr->pr_edict_size; + pr_int_t max_edicts = pr->pr_edict_area_size / pr->pr_edict_size; max_edicts -= *pr->num_edicts; count = PL_A_NumObjects (entity_list); @@ -480,7 +487,7 @@ ED_SpawnEntities (progs_t *pr, plitem_t *entity_list) func = PR_FindFunction (pr, classname); if (!func) { Sys_Printf ("No spawn function for :\n"); - ED_Print (pr, ent); + ED_Print (pr, ent, 0); ED_Free (pr, ent); continue; } @@ -504,11 +511,11 @@ ED_Parse (progs_t *pr, const char *data) if (Script_GetToken (script, 1)) { if (strequal (script->token->str, "(")) { // new style (plist) entity data - entity_list = PL_GetPropertyList (data); + entity_list = PL_GetPropertyList (data, pr->hashctx); } else { - // oldstyle entity data + // old style entity data Script_UngetToken (script); - entity_list = ED_ConvertToPlist (script, 0); + entity_list = ED_ConvertToPlist (script, 0, pr->hashctx); } } Script_Delete (script); @@ -524,6 +531,7 @@ ED_LoadFromFile (progs_t *pr, const char *data) PR_PushFrame (pr); PR_RESET_PARAMS (pr); P_INT (pr, 0) = PR_SetTempString (pr, data); + pr->pr_argc = 1; PR_ExecuteProgram (pr, pr->edict_parse); PR_PopFrame (pr); return; @@ -531,12 +539,12 @@ ED_LoadFromFile (progs_t *pr, const char *data) entity_list = ED_Parse (pr, data); if (entity_list) { ED_SpawnEntities (pr, entity_list); - PL_Free (entity_list); + PL_Release (entity_list); } } VISIBLE void -ED_EntityParseFunction (progs_t *pr) +ED_EntityParseFunction (progs_t *pr, void *data) { pr->edict_parse = P_FUNCTION (pr, 0); } diff --git a/libs/gamecode/pr_resolve.c b/libs/gamecode/pr_resolve.c index 626540fda..008c1abed 100644 --- a/libs/gamecode/pr_resolve.c +++ b/libs/gamecode/pr_resolve.c @@ -34,59 +34,59 @@ #ifdef HAVE_STRINGS_H # include #endif -#include -#include -#include "QF/cmd.h" -#include "QF/crc.h" -#include "QF/cvar.h" #include "QF/hash.h" #include "QF/progs.h" -#include "QF/qdefs.h" -#include "QF/qendian.h" -#include "QF/quakefs.h" #include "QF/sys.h" -#include "QF/zone.h" -#include "QF/va.h" #include "compat.h" +static const char param_str[] = ".param_0"; -ddef_t * -PR_GlobalAtOfs (progs_t * pr, pr_int_t ofs) +pr_def_t * +PR_SearchDefs (pr_def_t *defs, unsigned num_defs, pr_ptr_t offset) { - ddef_t *def; - pr_uint_t i; + // fuzzy bsearh + unsigned left = 0; + unsigned right = num_defs - 1; + unsigned mid; - for (i = 0; i < pr->progs->numglobaldefs; i++) { - def = &pr->pr_globaldefs[i]; - if (def->ofs == ofs) - return def; + if (!num_defs) { + return 0; } - return NULL; + while (left != right) { + mid = (left + right + 1) / 2; + if (defs[mid].ofs > offset) { + right = mid - 1; + } else { + left = mid; + } + } + if (defs[left].ofs <= offset) { + return defs + left; + } + return 0; } -VISIBLE ddef_t * -PR_FieldAtOfs (progs_t * pr, pr_int_t ofs) +pr_def_t * +PR_GlobalAtOfs (progs_t * pr, pr_ptr_t ofs) { - ddef_t *def; - pr_uint_t i; - - for (i = 0; i < pr->progs->numfielddefs; i++) { - def = &pr->pr_fielddefs[i]; - if (def->ofs == ofs) - return def; - } - return NULL; + return PR_SearchDefs (pr->pr_globaldefs, pr->progs->globaldefs.count, ofs); } -VISIBLE ddef_t * +VISIBLE pr_def_t * +PR_FieldAtOfs (progs_t * pr, pr_ptr_t ofs) +{ + return PR_SearchDefs (pr->pr_fielddefs, pr->progs->fielddefs.count, ofs); +} + +VISIBLE pr_def_t * PR_FindField (progs_t * pr, const char *name) { return Hash_Find (pr->field_hash, name); } -VISIBLE ddef_t * +VISIBLE pr_def_t * PR_FindGlobal (progs_t * pr, const char *name) { return Hash_Find (pr->global_hash, name); @@ -108,7 +108,7 @@ VISIBLE int PR_ResolveGlobals (progs_t *pr) { const char *sym; - ddef_t *def; + pr_def_t *def; int i; if (pr->progs->version == PROG_ID_VERSION) { @@ -122,32 +122,44 @@ PR_ResolveGlobals (progs_t *pr) pr->pr_params[6] = &pr->pr_globals[OFS_PARM6]; pr->pr_params[7] = &pr->pr_globals[OFS_PARM7]; pr->pr_param_size = OFS_PARM1 - OFS_PARM0; + pr->pr_param_alignment = 0; // log2 } else { + char *param_n = alloca (sizeof (param_str)); + strcpy (param_n, param_str); if (!(def = PR_FindGlobal (pr, sym = ".return"))) goto error; pr->pr_return = &pr->pr_globals[def->ofs]; - for (i = 0; i < MAX_PARMS; i++) { - if (!(def = PR_FindGlobal (pr, sym = va(".param_%d", i)))) + for (i = 0; i < PR_MAX_PARAMS; i++) { + param_n[sizeof (param_str) - 2] = i + '0'; + if (!(def = PR_FindGlobal (pr, sym = param_n))) goto error; pr->pr_params[i] = &pr->pr_globals[def->ofs]; } if (!(def = PR_FindGlobal (pr, sym = ".param_size"))) goto error; pr->pr_param_size = G_INT (pr, def->ofs); + if (!(def = PR_FindGlobal (pr, sym = ".param_alignment"))) + goto error; + pr->pr_param_alignment = G_INT (pr, def->ofs); } - if (pr->pr_saved_params) - free (pr->pr_saved_params); - pr->pr_saved_params = calloc (pr->pr_param_size * MAX_PARMS, - sizeof (pr_type_t)); + pr->null_size = pr->pr_return - pr->pr_globals; memcpy (pr->pr_real_params, pr->pr_params, sizeof (pr->pr_params)); - if (!pr->globals.time) { + if (!pr->globals.ftime) {//FIXME double time if ((def = PR_FindGlobal (pr, "time"))) - pr->globals.time = &G_FLOAT (pr, def->ofs); + pr->globals.ftime = &G_FLOAT (pr, def->ofs); } if (!pr->globals.self) { if ((def = PR_FindGlobal (pr, ".self")) || (def = PR_FindGlobal (pr, "self"))) - pr->globals.self = &G_INT (pr, def->ofs); + pr->globals.self = &G_UINT (pr, def->ofs); + } + if (!pr->globals.stack) { + if ((def = PR_FindGlobal (pr, ".stack")) + || (def = PR_FindGlobal (pr, "stack"))) { + pr->globals.stack = &G_POINTER (pr, def->ofs); + // the stack is at the very end of the progs memory map + *pr->globals.stack = pr->globals_size; + } } if (pr->fields.nextthink == -1) if ((def = PR_FindField (pr, "nextthink"))) @@ -168,7 +180,7 @@ int PR_AccessField (progs_t *pr, const char *name, etype_t type, const char *file, int line) { - ddef_t *def = PR_FindField (pr, name); + pr_def_t *def = PR_FindField (pr, name); if (!def) PR_Error (pr, "undefined field %s accessed at %s:%d", name, file, line); diff --git a/libs/gamecode/pr_resource.c b/libs/gamecode/pr_resource.c index 14cd4a38b..6b51a4f64 100644 --- a/libs/gamecode/pr_resource.c +++ b/libs/gamecode/pr_resource.c @@ -42,6 +42,7 @@ struct pr_resource_s { pr_resource_t *next; void *data; void (*clear)(progs_t *pr, void *data); + void (*destroy)(progs_t *pr, void *data); }; static const char * @@ -53,7 +54,8 @@ resource_get_key (const void *r, void *unused) VISIBLE void PR_Resources_Init (progs_t *pr) { - pr->resource_hash = Hash_NewTable (1021, resource_get_key, 0, 0); + pr->resource_hash = Hash_NewTable (1021, resource_get_key, 0, 0, + pr->hashctx); pr->resources = 0; } @@ -67,9 +69,29 @@ PR_Resources_Clear (progs_t *pr) } } +VISIBLE void +PR_Resources_Shutdown (progs_t *pr) +{ + // Clear resources first in case there are any cross-dependencies + PR_Resources_Clear (pr); + + pr_resource_t *res = pr->resources; + while (res) { + pr_resource_t *t = res->next; + res->destroy (pr, res->data); + free (res); + res = t; + } + pr->resources = 0; + + Hash_DelTable (pr->resource_hash); + pr->resource_hash = 0; +} + VISIBLE void PR_Resources_Register (progs_t *pr, const char *name, void *data, - void (*clear)(progs_t *, void *)) + void (*clear)(progs_t *, void *), + void (*destroy)(progs_t *, void *)) { pr_resource_t *res = malloc (sizeof (pr_resource_t)); if (!res) @@ -77,6 +99,7 @@ PR_Resources_Register (progs_t *pr, const char *name, void *data, res->name = name; res->data = data; res->clear = clear; + res->destroy = destroy; res->next = pr->resources; pr->resources = res; Hash_Add (pr->resource_hash, res); diff --git a/libs/gamecode/pr_strings.c b/libs/gamecode/pr_strings.c index f6983469a..7dac24ff7 100644 --- a/libs/gamecode/pr_strings.c +++ b/libs/gamecode/pr_strings.c @@ -38,11 +38,63 @@ #include #include #include +#include +#include "QF/darray.h" #include "QF/dstring.h" #include "QF/hash.h" #include "QF/progs.h" -#include "QF/va.h" + +// format adjustments +#define FMT_ALTFORM (1<<0) +#define FMT_LJUSTIFY (1<<1) +#define FMT_ZEROPAD (1<<2) +#define FMT_ADDSIGN (1<<3) +#define FMT_ADDBLANK (1<<4) +#define FMT_HEX (1<<5) +#define FMT_LONG (1<<6) +#define FMT_WIDTH (1<<7) + +typedef struct fmt_item_s { + byte type; + unsigned flags; + int minFieldWidth; + int precision; + union { + const char *string_var; + pr_int_t int_var; + pr_uint_t uint_var; + float float_var; + double double_var; + pr_long_t long_var; + pr_ulong_t ulong_var; + } data; + struct fmt_item_s *next; +} fmt_item_t; + +typedef struct strref_slot_s { + struct strref_slot_s *next; + struct strref_slot_s **prev; + strref_t *strref; +} strref_slot_t; + +typedef struct prstr_resources_s { + progs_t *pr; + dstring_mem_t ds_mem; + strref_t *free_string_refs; + strref_t *static_strings; + strref_t **string_map; + strref_slot_t return_strings[PR_RS_SLOTS]; + strref_slot_t *rs_slot; + unsigned dyn_str_size; + struct hashtab_s *strref_hash; + int num_strings; + struct DARRAY_TYPE (fmt_item_t *) fmt_item_blocks; + fmt_item_t *free_fmt_items; + dstring_t *print_str; + prstr_at_handler_t at_handler; + void *at_handler_data; +} prstr_resources_t; typedef enum { str_free, @@ -55,7 +107,7 @@ typedef enum { struct strref_s { strref_t *next; - strref_t **prev; + strref_slot_t *rs_slot; str_e type; union { char *string; @@ -63,30 +115,6 @@ struct strref_s { } s; }; -// format adjustments -#define FMT_ALTFORM (1<<0) -#define FMT_LJUSTIFY (1<<1) -#define FMT_ZEROPAD (1<<2) -#define FMT_ADDSIGN (1<<3) -#define FMT_ADDBLANK (1<<4) -#define FMT_HEX (1<<5) - -typedef struct fmt_item_s { - byte type; - unsigned flags; - int minFieldWidth; - int precision; - union { - const char *string_var; - int integer_var; - unsigned uinteger_var; - float float_var; - } data; - struct fmt_item_s *next; -} fmt_item_t; - -static fmt_item_t *free_fmt_items; - static void * pr_strings_alloc (void *_pr, size_t size) { @@ -109,51 +137,50 @@ pr_strings_realloc (void *_pr, void *ptr, size_t size) } static strref_t * -new_string_ref (progs_t *pr) +new_string_ref (prstr_resources_t *res) { strref_t *sr; - if (!pr->free_string_refs) { + if (!res->free_string_refs) { int i; size_t size; - pr->dyn_str_size++; - size = pr->dyn_str_size * sizeof (strref_t *); - pr->string_map = realloc (pr->string_map, size); - if (!pr->string_map) - PR_Error (pr, "out of memory"); - if (!(pr->free_string_refs = calloc (1024, sizeof (strref_t)))) - PR_Error (pr, "out of memory"); - pr->string_map[pr->dyn_str_size - 1] = pr->free_string_refs; - for (i = 0, sr = pr->free_string_refs; i < 1023; i++, sr++) + res->dyn_str_size++; + size = res->dyn_str_size * sizeof (strref_t *); + res->string_map = realloc (res->string_map, size); + if (!res->string_map) + PR_Error (res->pr, "out of memory"); + if (!(res->free_string_refs = calloc (1024, sizeof (strref_t)))) + PR_Error (res->pr, "out of memory"); + res->string_map[res->dyn_str_size - 1] = res->free_string_refs; + for (i = 0, sr = res->free_string_refs; i < 1023; i++, sr++) sr->next = sr + 1; sr->next = 0; } - sr = pr->free_string_refs; - pr->free_string_refs = sr->next; + sr = res->free_string_refs; + res->free_string_refs = sr->next; sr->next = 0; + sr->rs_slot = 0; return sr; } static void -free_string_ref (progs_t *pr, strref_t *sr) +free_string_ref (prstr_resources_t *res, strref_t *sr) { sr->type = str_free; - if (sr->prev) - *sr->prev = sr->next; - sr->next = pr->free_string_refs; - pr->free_string_refs = sr; + sr->next = res->free_string_refs; + res->free_string_refs = sr; } -static string_t -string_index (progs_t *pr, strref_t *sr) +static __attribute__((pure)) pr_string_t +string_index (prstr_resources_t *res, strref_t *sr) { - long o = (long) (sr - pr->static_strings); + long o = (long) (sr - res->static_strings); unsigned i; - if (o >= 0 && o < pr->num_strings) - return sr->s.string - pr->pr_strings; - for (i = 0; i < pr->dyn_str_size; i++) { - int d = sr - pr->string_map[i]; + if (o >= 0 && o < res->num_strings) + return sr->s.string - res->pr->pr_strings; + for (i = 0; i < res->dyn_str_size; i++) { + int d = sr - res->string_map[i]; if (d >= 0 && d < 1024) return ~(i * 1024 + d); } @@ -170,70 +197,141 @@ strref_get_key (const void *_sr, void *notused) } static void -strref_free (void *_sr, void *_pr) +strref_free (void *_sr, void *_res) { - progs_t *pr = (progs_t*)_pr; - strref_t *sr = (strref_t*)_sr; + __auto_type res = (prstr_resources_t *) _res; + __auto_type sr = (strref_t *) _sr; // Since this is called only by Hash_FlushTable, the memory pointed // to by sr->string or sr->dstring has already been lost in the progs // load/reload and thus there's no need to free it. // free the string and ref only if it's not a static string - if (sr < pr->static_strings || sr >= pr->static_strings + pr->num_strings) { - free_string_ref (pr, sr); + if (sr < res->static_strings + || sr >= res->static_strings + res->num_strings) { + free_string_ref (res, sr); } } +static void +pr_strings_clear (progs_t *pr, void *data) +{ + __auto_type res = (prstr_resources_t *) data; + int i; + + for (i = 0; i < PR_RS_SLOTS; i++) { + if (res->return_strings[i].strref) + free_string_ref (res, res->return_strings[i].strref); + res->return_strings[i].strref = 0; + } + if (!res->rs_slot) { + strref_slot_t * const rs = res->return_strings; + for (i = 0; i < PR_RS_SLOTS; i++) { + rs[i].next = &rs[(i + 1) % PR_RS_SLOTS]; + rs[i].prev = &rs[(i - 1 + PR_RS_SLOTS) % PR_RS_SLOTS].next; + } + res->rs_slot = rs; + } + + pr->pr_xtstr = 0; +} + +static void +pr_strings_destroy (progs_t *pr, void *_res) +{ + __auto_type res = (prstr_resources_t *) _res; + dstring_delete (res->print_str); + Hash_DelTable (res->strref_hash); + free (res->static_strings); + res->static_strings = 0; + + for (unsigned i = 0; i < res->dyn_str_size; i++) { + free (res->string_map[i]); + } + free (res->string_map); + + for (size_t i = 0; i < res->fmt_item_blocks.size; i++) { + free (res->fmt_item_blocks.a[i]); + } + DARRAY_CLEAR (&res->fmt_item_blocks); + + pr->pr_string_resources = 0; + free (res); +} + VISIBLE int PR_LoadStrings (progs_t *pr) { - char *end = pr->pr_strings + pr->progs->numstrings; + prstr_resources_t *res = PR_Resources_Find (pr, "Strings"); + + char *end = pr->pr_strings + pr->progs->strings.count; char *str = pr->pr_strings; int count = 0; + pr->float_promoted = 0; + while (str < end) { count++; + if (*str == '@' && pr->progs->version == PROG_V6P_VERSION) { + if (!strcmp (str, "@float_promoted@")) { + pr->float_promoted = 1; + } + } str += strlen (str) + 1; } - - if (!pr->ds_mem) { - pr->ds_mem = malloc (sizeof (dstring_mem_t)); - pr->ds_mem->alloc = pr_strings_alloc; - pr->ds_mem->free = pr_strings_free; - pr->ds_mem->realloc = pr_strings_realloc; - pr->ds_mem->data = pr; - } - if (pr->strref_hash) { - Hash_FlushTable (pr->strref_hash); - } else { - pr->strref_hash = Hash_NewTable (1021, strref_get_key, strref_free, - pr); - pr->string_map = 0; - pr->free_string_refs = 0; - pr->dyn_str_size = 0; + if (pr->progs->version == PROG_VERSION) { + pr->float_promoted = 1; } - if (pr->static_strings) - free (pr->static_strings); - pr->static_strings = malloc (count * sizeof (strref_t)); + res->ds_mem.alloc = pr_strings_alloc; + res->ds_mem.free = pr_strings_free; + res->ds_mem.realloc = pr_strings_realloc; + res->ds_mem.data = pr; + + Hash_FlushTable (res->strref_hash); + + if (res->static_strings) + free (res->static_strings); + res->static_strings = calloc (count, sizeof (strref_t)); count = 0; str = pr->pr_strings; while (str < end) { - if (!Hash_Find (pr->strref_hash, str)) { - pr->static_strings[count].type = str_static; - pr->static_strings[count].s.string = str; - Hash_Add (pr->strref_hash, &pr->static_strings[count]); + if (!Hash_Find (res->strref_hash, str)) { + res->static_strings[count].type = str_static; + res->static_strings[count].s.string = str; + Hash_Add (res->strref_hash, &res->static_strings[count]); count++; } str += strlen (str) + 1; } - pr->num_strings = count; + res->num_strings = count; return 1; } +static void +requeue_strref (prstr_resources_t *res, strref_t *sr) +{ + strref_slot_t *rs_slot = sr->rs_slot; + if (rs_slot->next != res->rs_slot) { + // this is the oldest slot, so advance res->rs_slot to the + // next oldest slot so this slot does not get reused just yet + if (res->rs_slot == rs_slot) { + res->rs_slot = rs_slot->next; + } + // unlink this slot from the chain + rs_slot->next->prev = rs_slot->prev; + *rs_slot->prev = rs_slot->next; + // link this slot just before the oldest slot: all the slots + // form a doubly linked circular list + rs_slot->prev = res->rs_slot->prev; + rs_slot->next = res->rs_slot; + *res->rs_slot->prev = rs_slot; + res->rs_slot->prev = &rs_slot->next; + } +} + static inline strref_t * -get_strref (progs_t *pr, string_t num) +get_strref (prstr_resources_t *res, pr_string_t num) { if (num < 0) { strref_t *ref; @@ -241,9 +339,9 @@ get_strref (progs_t *pr, string_t num) num = ~num % 1024; - if (row >= pr->dyn_str_size) + if (row >= res->dyn_str_size) return 0; - ref = &pr->string_map[row][num]; + ref = &res->string_map[row][num]; if (ref->type == str_free) return 0; return ref; @@ -251,25 +349,27 @@ get_strref (progs_t *pr, string_t num) return 0; } -static inline const char * -get_string (progs_t *pr, string_t num) +static inline __attribute__((pure)) const char * +get_string (progs_t *pr, pr_string_t num) { + __auto_type res = pr->pr_string_resources; if (num < 0) { - strref_t *ref = get_strref (pr, num); + strref_t *ref = get_strref (res, num); if (!ref) return 0; switch (ref->type) { + case str_return: + requeue_strref (res, ref); case str_static: case str_temp: case str_dynamic: - case str_return: return ref->s.string; case str_mutable: return ref->s.dstring->str; case str_free: break; } - PR_Error (pr, "internal string error"); + PR_Error (pr, "internal string error: line:%d", __LINE__); } else { if (num >= pr->pr_stringsize) return 0; @@ -277,14 +377,28 @@ get_string (progs_t *pr, string_t num) } } -VISIBLE qboolean -PR_StringValid (progs_t *pr, string_t num) +VISIBLE bool +PR_StringValid (progs_t *pr, pr_string_t num) { - return get_string (pr, num) != 0; + if (num >= 0) { + return num < pr->pr_stringsize; + } + return get_strref (pr->pr_string_resources, num) != 0; +} + +VISIBLE bool +PR_StringMutable (progs_t *pr, pr_string_t num) +{ + strref_t *sr; + if (num >= 0) { + return 0; + } + sr = get_strref (pr->pr_string_resources, num); + return sr && sr->type == str_mutable; } VISIBLE const char * -PR_GetString (progs_t *pr, string_t num) +PR_GetString (progs_t *pr, pr_string_t num) { const char *str; @@ -295,9 +409,9 @@ PR_GetString (progs_t *pr, string_t num) } VISIBLE dstring_t * -PR_GetMutableString (progs_t *pr, string_t num) +PR_GetMutableString (progs_t *pr, pr_string_t num) { - strref_t *ref = get_strref (pr, num); + strref_t *ref = get_strref (pr->pr_string_resources, num); if (ref) { if (ref->type == str_mutable) return ref->s.dstring; @@ -306,10 +420,16 @@ PR_GetMutableString (progs_t *pr, string_t num) PR_RunError (pr, "Invalid string offset: %d", num); } +static inline void * +pr_strmalloc (progs_t *pr, size_t size) +{ + return PR_Zone_Malloc (pr, size); +} + static inline char * pr_stralloc (progs_t *pr, size_t len) { - return PR_Zone_Malloc (pr, len + 1); + return pr_strmalloc (pr, len + 1); } static inline void @@ -326,76 +446,95 @@ pr_strdup (progs_t *pr, const char *s) return new; } -VISIBLE string_t +VISIBLE pr_string_t PR_SetString (progs_t *pr, const char *s) { + prstr_resources_t *res = pr->pr_string_resources; strref_t *sr; if (!s) s = ""; - sr = Hash_Find (pr->strref_hash, s); + sr = Hash_Find (res->strref_hash, s); if (__builtin_expect (!sr, 1)) { - sr = new_string_ref (pr); + sr = new_string_ref (res); sr->type = str_static; sr->s.string = pr_strdup(pr, s); - Hash_Add (pr->strref_hash, sr); + Hash_Add (res->strref_hash, sr); } - return string_index (pr, sr); + return string_index (res, sr); } -void -PR_ClearReturnStrings (progs_t *pr) -{ - int i; - - for (i = 0; i < PR_RS_SLOTS; i++) { - if (pr->return_strings[i]) - free_string_ref (pr, pr->return_strings[i]); - pr->return_strings[i] = 0; - } -} - -VISIBLE string_t -PR_SetReturnString (progs_t *pr, const char *s) +VISIBLE pr_string_t +PR_FindString (progs_t *pr, const char *s) { + prstr_resources_t *res = pr->pr_string_resources; strref_t *sr; if (!s) s = ""; - if ((sr = Hash_Find (pr->strref_hash, s))) { - return string_index (pr, sr); - } + sr = Hash_Find (res->strref_hash, s); - if ((sr = pr->return_strings[pr->rs_slot])) { - if (sr->type != str_return) - PR_Error (pr, "internal string error"); - pr_strfree (pr, sr->s.string); - } else { - sr = new_string_ref (pr); + if (sr) { + return string_index (res, sr); } - sr->type = str_return; - sr->s.string = pr_strdup(pr, s); - - pr->return_strings[pr->rs_slot++] = sr; - pr->rs_slot %= PR_RS_SLOTS; - return string_index (pr, sr); + return 0; } -static inline string_t -pr_settempstring (progs_t *pr, char *s) +VISIBLE pr_string_t +PR_SetReturnString (progs_t *pr, const char *s) +{ + prstr_resources_t *res = pr->pr_string_resources; + strref_t *sr; + + if (!s) + s = ""; + if ((sr = Hash_Find (res->strref_hash, s))) { + if (sr->type == str_return && sr->rs_slot) { + requeue_strref (res, sr); + } else if ((sr->type == str_return && !sr->rs_slot) + || (sr->type != str_return && sr->rs_slot)) { + PR_Error (pr, "internal string error: line:%d %d %p", __LINE__, + sr->type, sr->rs_slot); + } + return string_index (res, sr); + } + + // grab the string ref from the oldest slot, or make a new one if the + // slot is empty + if ((sr = res->rs_slot->strref)) { + if (sr->type != str_return || sr->rs_slot != res->rs_slot) { + PR_Error (pr, "internal string error: line:%d", __LINE__); + } + pr_strfree (pr, sr->s.string); + } else { + sr = new_string_ref (res); + res->rs_slot->strref = sr; + } + sr->type = str_return; + sr->rs_slot = res->rs_slot; + sr->s.string = pr_strdup(pr, s); + + // the oldest slot just became the newest, so advance to the next oldest + res->rs_slot = res->rs_slot->next; + + return string_index (res, sr); +} + +static inline pr_string_t +pr_settempstring (progs_t *pr, prstr_resources_t *res, char *s) { strref_t *sr; - sr = new_string_ref (pr); + sr = new_string_ref (res); sr->type = str_temp; sr->s.string = s; sr->next = pr->pr_xtstr; pr->pr_xtstr = sr; - return string_index (pr, sr); + return string_index (res, sr); } -VISIBLE string_t +VISIBLE pr_string_t PR_CatStrings (progs_t *pr, const char *a, const char *b) { size_t lena; @@ -408,46 +547,77 @@ PR_CatStrings (progs_t *pr, const char *a, const char *b) strcpy (c, a); strcpy (c + lena, b); - return pr_settempstring (pr, c); + return pr_settempstring (pr, pr->pr_string_resources, c); } -VISIBLE string_t +VISIBLE pr_string_t PR_SetTempString (progs_t *pr, const char *s) { + prstr_resources_t *res = pr->pr_string_resources; strref_t *sr; if (!s) return PR_SetString (pr, ""); - if ((sr = Hash_Find (pr->strref_hash, s))) { - return string_index (pr, sr); + if ((sr = Hash_Find (res->strref_hash, s))) { + return string_index (res, sr); } - return pr_settempstring (pr, pr_strdup (pr, s)); + return pr_settempstring (pr, res, pr_strdup (pr, s)); } -VISIBLE string_t +VISIBLE pr_string_t +PR_AllocTempBlock (progs_t *pr, size_t size) +{ + prstr_resources_t *res = pr->pr_string_resources; + return pr_settempstring (pr, res, pr_strmalloc (pr, size)); +} + +VISIBLE void +PR_PushTempString (progs_t *pr, pr_string_t num) +{ + prstr_resources_t *res = pr->pr_string_resources; + strref_t *ref = get_strref (res, num); + strref_t **temp_ref; + + if (!ref || ref->type != str_temp) { + PR_Error (pr, "attempt to push a non-temp string"); + } + for (temp_ref = &pr->pr_xtstr; *temp_ref; temp_ref = &(*temp_ref)->next) { + if (*temp_ref == ref) { + *temp_ref = ref->next; + ref->next = pr->pr_pushtstr; + pr->pr_pushtstr = ref; + return; + } + } + PR_Error (pr, "attempt to push stale temp string"); +} + +VISIBLE pr_string_t PR_SetDynamicString (progs_t *pr, const char *s) { + prstr_resources_t *res = pr->pr_string_resources; strref_t *sr; if (!s) return PR_SetString (pr, ""); - if ((sr = Hash_Find (pr->strref_hash, s))) { - return string_index (pr, sr); + if ((sr = Hash_Find (res->strref_hash, s))) { + return string_index (res, sr); } - sr = new_string_ref (pr); + sr = new_string_ref (res); sr->type = str_dynamic; sr->s.string = pr_strdup (pr, s); - return string_index (pr, sr); + return string_index (res, sr); } -void -PR_MakeTempString (progs_t *pr, string_t str) +VISIBLE void +PR_MakeTempString (progs_t *pr, pr_string_t str) { - strref_t *sr = get_strref (pr, str); + prstr_resources_t *res = pr->pr_string_resources; + strref_t *sr = get_strref (res, str); if (!sr) PR_RunError (pr, "invalid string %d", str); @@ -465,24 +635,57 @@ PR_MakeTempString (progs_t *pr, string_t str) pr->pr_xtstr = sr; } -VISIBLE string_t +VISIBLE pr_string_t PR_NewMutableString (progs_t *pr) { - strref_t *sr = new_string_ref (pr); + prstr_resources_t *res = pr->pr_string_resources; + strref_t *sr = new_string_ref (res); sr->type = str_mutable; - sr->s.dstring = _dstring_newstr (pr->ds_mem); - return string_index (pr, sr); + sr->s.dstring = _dstring_newstr (&res->ds_mem); + return string_index (res, sr); } VISIBLE void -PR_FreeString (progs_t *pr, string_t str) +PR_HoldString (progs_t *pr, pr_string_t str) { - strref_t *sr = get_strref (pr, str); + prstr_resources_t *res = pr->pr_string_resources; + strref_t *sr = get_strref (res, str); + + if (sr) { + switch (sr->type) { + case str_temp: + break; + case str_return: + sr->rs_slot->strref = 0; + sr->rs_slot = 0; + break; + case str_static: + case str_mutable: + case str_dynamic: + // non-ephemeral string, no-op + return; + default: + PR_Error (pr, "internal string error: line:%d", __LINE__); + } + sr->type = str_dynamic; + return; + } + if (!PR_StringValid (pr, str)) { + PR_RunError (pr, "attempt to hold invalid string %d", str); + } +} + +VISIBLE void +PR_FreeString (progs_t *pr, pr_string_t str) +{ + prstr_resources_t *res = pr->pr_string_resources; + strref_t *sr = get_strref (res, str); if (sr) { switch (sr->type) { case str_static: case str_temp: + case str_return: return; case str_mutable: dstring_delete (sr->s.dstring); @@ -490,55 +693,68 @@ PR_FreeString (progs_t *pr, string_t str) case str_dynamic: pr_strfree (pr, sr->s.string); break; - case str_return: default: - PR_Error (pr, "internal string error"); + PR_Error (pr, "internal string error: line:%d", __LINE__); } - free_string_ref (pr, sr); + free_string_ref (res, sr); return; } - if (!get_string (pr, str)) + if (!PR_StringValid (pr, str)) { PR_RunError (pr, "attempt to free invalid string %d", str); + } } -void +VISIBLE void PR_FreeTempStrings (progs_t *pr) { + prstr_resources_t *res = pr->pr_string_resources; strref_t *sr, *t; for (sr = pr->pr_xtstr; sr; sr = t) { t = sr->next; + if (sr->type == str_dynamic) { + // the string has been held, so simply remove the ref from the + // queue + continue; + } if (sr->type != str_temp) - PR_Error (pr, "internal string error"); - if (R_STRING (pr) < 0 && string_index (pr, sr) == R_STRING (pr) + PR_Error (pr, "internal string error: line:%d", __LINE__); + if (R_STRING (pr) < 0 && string_index (res, sr) == R_STRING (pr) && pr->pr_depth) { + // It looks like the temp string is being returned. While this + // may be a false positive (just a random integer with the same + // value), it is better to hold onto the temp string a little + // longer than to remove it prematurely. This allows functions + // to return the result of "str a" + "str b" prstack_t *frame = pr->pr_stack + pr->pr_depth - 1; sr->next = frame->tstr; frame->tstr = sr; } else { pr_strfree (pr, sr->s.string); - free_string_ref (pr, sr); + free_string_ref (res, sr); } } pr->pr_xtstr = 0; } +#define hasprintf ((char *(*)(dstring_t *, const char *, ...))dasprintf) + #define PRINT(t) \ switch ((doWidth << 1) | doPrecision) { \ case 3: \ - dasprintf (result, tmp->str, current->minFieldWidth, \ + hasprintf (result, tmp->str, current->minFieldWidth, \ current->precision, current->data.t##_var); \ break; \ case 2: \ - dasprintf (result, tmp->str, current->minFieldWidth, \ + hasprintf (result, tmp->str, current->minFieldWidth, \ current->data.t##_var); \ break; \ case 1: \ - dasprintf (result, tmp->str, current->precision, \ + hasprintf (result, tmp->str, current->precision, \ current->data.t##_var); \ break; \ case 0: \ - dasprintf (result, tmp->str, current->data.t##_var); \ + hasprintf (result, tmp->str, current->data.t##_var); \ break; \ } @@ -549,16 +765,15 @@ PR_FreeTempStrings (progs_t *pr) list item. */ static void -I_DoPrint (dstring_t *result, fmt_item_t *formatting) +I_DoPrint (dstring_t *tmp, dstring_t *result, fmt_item_t *formatting) { fmt_item_t *current = formatting; - dstring_t *tmp = dstring_new (); while (current) { - qboolean doPrecision, doWidth; + bool doPrecision, doWidth; doPrecision = -1 != current->precision; - doWidth = 0 != current->minFieldWidth; + doWidth = 0 != (current->flags & FMT_WIDTH); dsprintf (tmp, "%%%s%s%s%s%s%s%s", (current->flags & FMT_ALTFORM) ? "#" : "", // hash @@ -576,290 +791,526 @@ I_DoPrint (dstring_t *result, fmt_item_t *formatting) break; case 'c': dstring_appendstr (tmp, "c"); - PRINT (integer); + PRINT (int); break; case 'i': case 'd': - dstring_appendstr (tmp, "d"); - PRINT (integer); + if (current->flags & FMT_LONG) { + dstring_appendstr (tmp, PRId64); + PRINT (ulong); + } else { + dstring_appendstr (tmp, PRId32); + PRINT (uint); + } break; case 'x': - dstring_appendstr (tmp, "x"); - PRINT (integer); + if (current->flags & FMT_LONG) { + dstring_appendstr (tmp, PRIx64); + PRINT (ulong); + } else { + dstring_appendstr (tmp, PRIx32); + PRINT (uint); + } break; case 'u': - if (current->flags & FMT_HEX) - dstring_appendstr (tmp, "x"); - else - dstring_appendstr (tmp, "u"); - PRINT (uinteger); + if (current->flags & FMT_LONG) { + dstring_appendstr (tmp, PRIu64); + PRINT (ulong); + } else { + dstring_appendstr (tmp, PRIu32); + PRINT (uint); + } break; case 'f': dstring_appendstr (tmp, "f"); - PRINT (float); + if (current->flags & FMT_LONG) { + PRINT (double); + } else { + PRINT (float); + } break; case 'g': dstring_appendstr (tmp, "g"); - PRINT (float); + if (current->flags & FMT_LONG) { + PRINT (double); + } else { + PRINT (float); + } break; default: break; } current = current->next; } - dstring_delete (tmp); } static fmt_item_t * -new_fmt_item (void) +new_fmt_item (prstr_resources_t *res) { int i; fmt_item_t *fi; - if (!free_fmt_items) { - free_fmt_items = malloc (16 * sizeof (fmt_item_t)); + if (!res->free_fmt_items) { + res->free_fmt_items = malloc (16 * sizeof (fmt_item_t)); for (i = 0; i < 15; i++) - free_fmt_items[i].next = free_fmt_items + i + 1; - free_fmt_items[i].next = 0; + res->free_fmt_items[i].next = res->free_fmt_items + i + 1; + res->free_fmt_items[i].next = 0; + DARRAY_APPEND (&res->fmt_item_blocks, res->free_fmt_items); } - fi = free_fmt_items; - free_fmt_items = fi->next; + fi = res->free_fmt_items; + res->free_fmt_items = fi->next; memset (fi, 0, sizeof (*fi)); fi->precision = -1; return fi; } static void -free_fmt_item (fmt_item_t *fi) +free_fmt_item (prstr_resources_t *res, fmt_item_t *fi) { - fi->next = free_fmt_items; - free_fmt_items = fi; + fi->next = res->free_fmt_items; + res->free_fmt_items = fi; +} + +struct fmt_state_s; +typedef void (*fmt_state_f) (struct fmt_state_s *); + +typedef struct fmt_state_s { + fmt_state_f state; + prstr_resources_t *res; + progs_t *pr; + pr_type_t **args; + const char *c; + const char *msg; + fmt_item_t *fmt_items; + fmt_item_t **fi; + int fmt_count; + prstr_at_handler_t at_handler; + void *at_handler_data; +} fmt_state_t; + +static inline void +fmt_append_item (fmt_state_t *state) +{ + (*state->fi)->next = new_fmt_item (state->res); + state->fi = &(*state->fi)->next; } #undef P_var -#define P_var(p,n,t) (args[n]->t##_var) +#define P_var(p,n,t) PR_PTR (t, state->args[n]) + +/** State machine for PR_Sprintf + * + * Parsing of the format string is implemented via the following state + * machine. Note that in all states, end-of-string terminates the machine. + * If the machine terminates in any state other than format or conversion, + * an error is generated. + * \dot + * digraph PR_Sprintf_fmt_state_machine { + * format -> flags [label="{%}"]; + * flogs -> format [label="{%}"]; + * flags -> flags [label="{#+0 -}"]; + * flags -> var_field_width [label="{*}"]; + * flags -> precision [label="{.}"]; + * flags -> field_width [label="{[1-9]}"]; + * flags -> modifiers [label="other"]; + * var_field_width -> precision [label="{.}"]; + * var_field_width -> modifiers [label="other"]; + * field_width -> field_width [label="{[0-9]}"]; + * field_width -> precision [label="{.}"]; + * field_width -> modifiers [label="other"]; + * precision -> var_precision [label="{*}"]; + * precision -> fixed_precision [label="{[0-9]}"]; + * precision -> modifiers [label="other"]; + * var_precision -> modifiers [label="instant"]; + * fixed_precision -> fixed_precision [label="{[0-9]}"]; + * fixed_precision -> modifiers [label="other"]; + * modifiers -> conversion [label="instant/other"]; + * conversion -> format [label="other"]; + * } + * \enddot + */ +///@{ +static void fmt_state_format (fmt_state_t *state); +static void fmt_state_flags (fmt_state_t *state); +static void fmt_state_var_field_width (fmt_state_t *state); +static void fmt_state_field_width (fmt_state_t *state); +static void fmt_state_precision (fmt_state_t *state); +static void fmt_state_var_precision (fmt_state_t *state); +static void fmt_state_fixed_precision (fmt_state_t *state); +static void fmt_state_modifiers (fmt_state_t *state); +static void fmt_state_conversion (fmt_state_t *state); + +static void +fmt_state_flags (fmt_state_t *state) +{ + state->c++; // skip over % + while (1) { + switch (*state->c) { + case '%': + state->c++; + (*state->fi)->flags = 0; + (*state->fi)->precision = 1; + (*state->fi)->minFieldWidth = 0; + (*state->fi)->type = 's'; + (*state->fi)->data.string_var = "%"; + + fmt_append_item (state); + state->state = fmt_state_format; + return; + case '0': + (*state->fi)->flags |= FMT_ZEROPAD; + break; + case '#': + (*state->fi)->flags |= FMT_ALTFORM; + break; + case ' ': + (*state->fi)->flags |= FMT_ADDBLANK; + break; + case '-': + (*state->fi)->flags |= FMT_LJUSTIFY; + break; + case '+': + (*state->fi)->flags |= FMT_ADDSIGN; + break; + case '*': + state->state = fmt_state_var_field_width; + return; + case '.': + state->state = fmt_state_precision; + return; + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + state->state = fmt_state_field_width; + return; + default: + state->state = fmt_state_modifiers; + return; + } + state->c++; + } +} + +static void +fmt_state_var_field_width (fmt_state_t *state) +{ + (*state->fi)->flags |= FMT_WIDTH; + (*state->fi)->minFieldWidth = P_INT (pr, state->fmt_count); + state->fmt_count++; + if (*++state->c == '.') { + state->state = fmt_state_precision; + } else { + state->state = fmt_state_modifiers; + } +} + +static void +fmt_state_field_width (fmt_state_t *state) +{ + (*state->fi)->flags |= FMT_WIDTH; + while (isdigit ((byte )*state->c)) { + (*state->fi)->minFieldWidth *= 10; + (*state->fi)->minFieldWidth += *state->c++ - '0'; + } + if (*state->c == '.') { + state->state = fmt_state_precision; + } else { + state->state = fmt_state_modifiers; + } +} + +static void +fmt_state_precision (fmt_state_t *state) +{ + state->c++; // skip over . + (*state->fi)->precision = 0; + if (isdigit ((byte )*state->c)) { + state->state = fmt_state_fixed_precision; + } else if (*state->c == '*') { + state->state = fmt_state_var_precision; + } else { + state->state = fmt_state_modifiers; + } +} + +static void +fmt_state_var_precision (fmt_state_t *state) +{ + state->c++; // skip over * + (*state->fi)->precision = P_INT (pr, state->fmt_count); + state->fmt_count++; + state->state = fmt_state_modifiers; +} + +static void +fmt_state_fixed_precision (fmt_state_t *state) +{ + while (isdigit ((byte )*state->c)) { + (*state->fi)->precision *= 10; + (*state->fi)->precision += *state->c++ - '0'; + } + state->state = fmt_state_modifiers; +} + +static void +fmt_state_modifiers (fmt_state_t *state) +{ + // no modifiers supported + if (state->c[0] == 'l' + && (state->c[1] == 'i' || state->c[1] == 'd' || state->c[1] == 'x' + || state->c[1] == 'u')) { + (*state->fi)->flags |= FMT_LONG; + state->c++; + } + state->state = fmt_state_conversion; +} + +static void +fmt_state_conversion (fmt_state_t *state) +{ + progs_t *pr = state->pr; + char conv; + switch ((conv = *state->c++)) { + case '@': + // object + pr_ptr_t at_param = P_UINT (pr, state->fmt_count); + if (state->at_handler) { + const char *at_str = state->at_handler (pr, at_param, + state->at_handler_data); + (*state->fi)->type = 's'; + (*state->fi)->data.string_var = at_str; + } else { + (*state->fi)->type = 's'; + (*state->fi)->data.string_var = "["; + fmt_append_item (state); + + (*state->fi)->flags |= FMT_ALTFORM; + (*state->fi)->type = 'x'; + (*state->fi)->data.uint_var = at_param; + fmt_append_item (state); + + (*state->fi)->type = 's'; + (*state->fi)->data.string_var = "]"; + } + state->fmt_count++; + fmt_append_item (state); + break; + case 'e': + // entity + (*state->fi)->type = 'i'; + (*state->fi)->data.int_var = P_EDICTNUM (pr, state->fmt_count); + + state->fmt_count++; + fmt_append_item (state); + break; + case 'i': + case 'd': + case 'c': + // int + (*state->fi)->type = conv; + if ((*state->fi)->flags & FMT_LONG) { + (*state->fi)->data.long_var = P_LONG (pr, state->fmt_count); + } else { + (*state->fi)->data.int_var = P_INT (pr, state->fmt_count); + } + + state->fmt_count++; + fmt_append_item (state); + break; + case 'f': + // float or double + case 'g': + // float or double, no trailing zeroes, trim "." + // if nothing after + (*state->fi)->type = conv; + if (pr->float_promoted) { + (*state->fi)->flags |= FMT_LONG; + (*state->fi)->data.double_var + = P_DOUBLE (pr, state->fmt_count); + } else { + (*state->fi)->data.float_var + = P_FLOAT (pr, state->fmt_count); + } + + state->fmt_count++; + fmt_append_item (state); + break; + case 'p': + // pointer + (*state->fi)->flags |= FMT_ALTFORM; + (*state->fi)->type = 'x'; + (*state->fi)->data.uint_var = P_UINT (pr, state->fmt_count); + + state->fmt_count++; + fmt_append_item (state); + break; + case 's': + // string + (*state->fi)->type = conv; + (*state->fi)->data.string_var = P_GSTRING (pr, state->fmt_count); + + state->fmt_count++; + fmt_append_item (state); + break; + case 'v': + case 'q': + // vector + { + int i, count = 3; + int flags = (*state->fi)->flags; + int precision = (*state->fi)->precision; + unsigned minWidth = (*state->fi)->minFieldWidth; + + (*state->fi)->flags = 0; + (*state->fi)->precision = -1; + (*state->fi)->minFieldWidth = 0; + + if (conv == 'q') + count = 4; + + for (i = 0; i < count; i++) { + if (i == 0) { + (*state->fi)->type = 's'; + (*state->fi)->data.string_var = "'"; + } else { + (*state->fi)->type = 's'; + (*state->fi)->data.string_var = " "; + } + fmt_append_item (state); + + (*state->fi)->flags = flags; + (*state->fi)->precision = precision; + (*state->fi)->minFieldWidth = minWidth; + (*state->fi)->type = 'g'; + (*state->fi)->data.float_var = + P_VECTOR (pr, state->fmt_count)[i]; + + fmt_append_item (state); + } + } + + (*state->fi)->type = 's'; + (*state->fi)->data.string_var = "'"; + + state->fmt_count++; + fmt_append_item (state); + break; + case 'u': + case 'x': + // int, unsigned or hex notation + (*state->fi)->type = conv; + if ((*state->fi)->flags & FMT_LONG) { + (*state->fi)->data.ulong_var = P_ULONG (pr, state->fmt_count); + } else { + (*state->fi)->data.uint_var = P_UINT (pr, state->fmt_count); + } + + state->fmt_count++; + fmt_append_item (state); + break; + } + if (*state->c) { + state->state = fmt_state_format; + } else { + state->state = 0; // finished + } +} + +static void +fmt_state_format (fmt_state_t *state) +{ + const char *l = state->c; + while (*state->c && *state->c != '%') { + state->c++; + } + if (state->c != l) { + // have some unformatted text to print + (*state->fi)->precision = state->c - l; + (*state->fi)->type = 's'; + (*state->fi)->data.string_var = l; + if (*state->c) { + fmt_append_item (state); + } + } + if (*state->c) { + state->state = fmt_state_flags; + } else { + state->state = 0; // finished + } +} +///@} + +VISIBLE void +PR_Sprintf_SetAtHandler (progs_t *pr, prstr_at_handler_t at_handler, + void *data) +{ + prstr_resources_t *res = pr->pr_string_resources; + res->at_handler = at_handler; + res->at_handler_data = data; +} + VISIBLE void PR_Sprintf (progs_t *pr, dstring_t *result, const char *name, const char *format, int count, pr_type_t **args) { - const char *c, *l; - const char *msg = ""; - fmt_item_t *fmt_items = 0; - fmt_item_t **fi = &fmt_items; - int fmt_count = 0; + prstr_resources_t *res = pr->pr_string_resources; + fmt_state_t state = { }; + + state.pr = pr; + state.res = res; + state.args = args; + state.fi = &state.fmt_items; + *state.fi = new_fmt_item (res); + state.c = format; + state.state = fmt_state_format; + state.at_handler = res->at_handler; + state.at_handler_data = res->at_handler_data; if (!name) - name = "PF_InternalSprintf"; + name = "PR_Sprintf"; - *fi = new_fmt_item (); - c = l = format; - while (*c) { - if (*c++ == '%') { - if (c != l + 1) { - // have some unformatted text to print - (*fi)->precision = c - l - 1; - (*fi)->type = 's'; - (*fi)->data.string_var = l; - - (*fi)->next = new_fmt_item (); - fi = &(*fi)->next; - } - if (*c == '%') { - (*fi)->type = 's'; - (*fi)->data.string_var = "%"; - - (*fi)->next = new_fmt_item (); - fi = &(*fi)->next; - } else { - do { - switch (*c) { - // format options - case '\0': - msg = "Unexpected end of format string"; - goto error; - case '0': - (*fi)->flags |= FMT_ZEROPAD; - c++; - continue; - case '#': - (*fi)->flags |= FMT_ALTFORM; - c++; - continue; - case ' ': - (*fi)->flags |= FMT_ADDBLANK; - c++; - continue; - case '-': - (*fi)->flags |= FMT_LJUSTIFY; - c++; - continue; - case '+': - (*fi)->flags |= FMT_ADDSIGN; - c++; - continue; - case '.': - (*fi)->precision = 0; - c++; - while (isdigit ((byte )*c)) { - (*fi)->precision *= 10; - (*fi)->precision += *c++ - '0'; - } - continue; - case '1': case '2': case '3': case '4': case '5': - case '6': case '7': case '8': case '9': - while (isdigit ((byte )*c)) { - (*fi)->minFieldWidth *= 10; - (*fi)->minFieldWidth += *c++ - '0'; - } - continue; - // format types - case '@': - // object - fmt_count++; - (*fi)->next = new_fmt_item (); - fi = &(*fi)->next; - break; - case 'e': - // entity - (*fi)->type = 'i'; - (*fi)->data.integer_var = - P_EDICTNUM (pr, fmt_count); - - fmt_count++; - (*fi)->next = new_fmt_item (); - fi = &(*fi)->next; - break; - case 'i': - case 'd': - case 'c': - // integer - (*fi)->type = *c; - (*fi)->data.integer_var = P_INT (pr, fmt_count); - - fmt_count++; - (*fi)->next = new_fmt_item (); - fi = &(*fi)->next; - break; - case 'f': - // float - case 'g': - // float, no trailing zeroes, trim "." if nothing - // after - (*fi)->type = *c; - (*fi)->data.float_var = P_FLOAT (pr, fmt_count); - - fmt_count++; - (*fi)->next = new_fmt_item (); - fi = &(*fi)->next; - break; - case 'p': - // pointer - (*fi)->flags |= FMT_ALTFORM; - (*fi)->type = 'x'; - (*fi)->data.uinteger_var = P_UINT (pr, fmt_count); - - fmt_count++; - (*fi)->next = new_fmt_item (); - fi = &(*fi)->next; - break; - case 's': - // string - (*fi)->type = *c; - (*fi)->data.string_var = P_GSTRING (pr, fmt_count); - - fmt_count++; - (*fi)->next = new_fmt_item (); - fi = &(*fi)->next; - break; - case 'v': - case 'q': - // vector - { - int i, count = 3; - int flags = (*fi)->flags; - int precision = (*fi)->precision; - unsigned minWidth = (*fi)->minFieldWidth; - - (*fi)->flags = 0; - (*fi)->precision = -1; - (*fi)->minFieldWidth = 0; - - if (*c == 'q') - count = 4; - - for (i = 0; i < count; i++) { - if (i == 0) { - (*fi)->type = 's'; - (*fi)->data.string_var = "'"; - } else { - (*fi)->type = 's'; - (*fi)->data.string_var = " "; - } - (*fi)->next = new_fmt_item (); - fi = &(*fi)->next; - - (*fi)->flags = flags; - (*fi)->precision = precision; - (*fi)->minFieldWidth = minWidth; - (*fi)->type = 'g'; - (*fi)->data.float_var = - P_VECTOR (pr, fmt_count)[i]; - - (*fi)->next = new_fmt_item (); - fi = &(*fi)->next; - } - } - - (*fi)->type = 's'; - (*fi)->data.string_var = "'"; - - fmt_count++; - (*fi)->next = new_fmt_item (); - fi = &(*fi)->next; - break; - case 'x': - // integer, hex notation - (*fi)->type = *c; - (*fi)->data.uinteger_var = P_UINT (pr, fmt_count); - - fmt_count++; - (*fi)->next = new_fmt_item (); - fi = &(*fi)->next; - break; - } - break; - } while (1); - } - l = ++c; - } - } - if (c != l) { - // have some unformatted text to print - (*fi)->precision = c - l; - (*fi)->type = 's'; - (*fi)->data.string_var = l; - - (*fi)->next = new_fmt_item (); - fi = &(*fi)->next; + while (*state.c && state.state) { + state.state (&state); } - if (fmt_count != count) { - if (fmt_count > count) - msg = "Not enough arguments for format string."; + if (state.state) { + state.msg = "Unexpected end of format string"; + } else if (state.fmt_count != count) { + if (state.fmt_count > count) + state.msg = "Not enough arguments for format string."; else - msg = "Too many arguments for format string."; - msg = va ("%s: %d %d", msg, fmt_count, count); + state.msg = "Too many arguments for format string."; + } + if (state.msg) { + dsprintf (res->print_str, "%s: %d %d", state.msg, state.fmt_count, + count); + state.msg = res->print_str->str; goto error; } - I_DoPrint (result, fmt_items); - while (fmt_items) { - fmt_item_t *t = fmt_items->next; - free_fmt_item (fmt_items); - fmt_items = t; + dstring_clear (res->print_str); + I_DoPrint (res->print_str, result, state.fmt_items); + while (state.fmt_items) { + fmt_item_t *t = state.fmt_items->next; + free_fmt_item (res, state.fmt_items); + state.fmt_items = t; } return; error: - PR_RunError (pr, "%s: %s", name, msg); + PR_RunError (pr, "%s: %s", name, state.msg); +} + +void +PR_Strings_Init (progs_t *pr) +{ + prstr_resources_t *res = calloc (1, sizeof (*res)); + res->pr = pr; + res->print_str = dstring_new (); + res->strref_hash = Hash_NewTable (1021, strref_get_key, strref_free, + res, pr->hashctx); + DARRAY_INIT (&res->fmt_item_blocks, 8); + + PR_Resources_Register (pr, "Strings", res, pr_strings_clear, + pr_strings_destroy); + pr->pr_string_resources = res; } diff --git a/libs/gamecode/pr_v6p_opcode.c b/libs/gamecode/pr_v6p_opcode.c new file mode 100644 index 000000000..392e5f14b --- /dev/null +++ b/libs/gamecode/pr_v6p_opcode.c @@ -0,0 +1,1772 @@ +/* + pr_v6p_opcodes.c + + Opcode table and checking for v6+ progs. + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2001 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include "string.h" +#endif +#ifdef HAVE_STRINGS_H +# include "strings.h" +#endif + +#include "QF/cvar.h" +#include "QF/progs.h" +#include "QF/sys.h" + +#include "QF/progs/pr_comp.h" + +#include "compat.h" + +// default format is "%Ga, %Gb, %gc" +// V global_string, contents, void +// G global_string, contents +// g global_string, no contents +// s as short +// O address + short +// P function parameter +// F function (must come before any P) +// R return value +// E entity + field (%Eab) +// M addressing mode, contents +// m addressing mode, no contents +// takes operand (a,b,c,o (opcode)) and right shift(hex). always masked +// by 3 +// %Mc5 -> contents, operand c, shift right 5 bits +// %mo2 -> no contents, opcode, shift right 2 bits +// %mo0 -> no contents, opcode, no shift +// always uses a and b for the address calculation (Ruamoko convention)) +// +// a operand a +// b operand b +// c operand c +// o opcode +// x place holder for P (padding) +// 0-7 parameter index (for P) +VISIBLE const v6p_opcode_t pr_v6p_opcodes[] = { + // OP_DONE_v6p is actually the same as OP_RETURN_v6p, the types are bogus + [OP_DONE_v6p] = {"done", "done", + ev_entity, ev_field, ev_void, + PROG_ID_VERSION, + "%Va", + }, + + [OP_MUL_D_v6p] = {"mul", "mul.d", + ev_double, ev_double, ev_double, + PROG_V6P_VERSION, + }, + [OP_MUL_F_v6p] = {"mul", "mul.f", + ev_float, ev_float, ev_float, + PROG_ID_VERSION, + }, + [OP_MUL_V_v6p] = {"vdot", "mul.v", + ev_vector, ev_vector, ev_float, + PROG_ID_VERSION, + }, + [OP_MUL_FV_v6p] = {"scale", "mul.fv", + ev_float, ev_vector, ev_vector, + PROG_ID_VERSION, + }, + [OP_MUL_VF_v6p] = {"scale", "mul.vf", + ev_vector, ev_float, ev_vector, + PROG_ID_VERSION, + }, + [OP_MUL_DV_v6p] = {"mul", "mul.dv", + ev_double, ev_vector, ev_vector, + PROG_ID_VERSION, + }, + [OP_MUL_VD_v6p] = {"mul", "mul.vd", + ev_vector, ev_double, ev_vector, + PROG_ID_VERSION, + }, + [OP_MUL_Q_v6p] = {"mul", "mul.q", + ev_quaternion, ev_quaternion, ev_quaternion, + PROG_V6P_VERSION, + }, + [OP_MUL_FQ_v6p] = {"mul", "mul.fq", + ev_float, ev_quaternion, ev_quaternion, + PROG_V6P_VERSION, + }, + [OP_MUL_QF_v6p] = {"mul", "mul.qf", + ev_quaternion, ev_float, ev_quaternion, + PROG_V6P_VERSION, + }, + [OP_MUL_DQ_v6p] = {"mul", "mul.dq", + ev_double, ev_quaternion, ev_quaternion, + PROG_V6P_VERSION, + }, + [OP_MUL_QD_v6p] = {"mul", "mul.qd", + ev_quaternion, ev_double, ev_quaternion, + PROG_V6P_VERSION, + }, + [OP_MUL_QV_v6p] = {"mul", "mul.qv", + ev_quaternion, ev_vector, ev_vector, + PROG_V6P_VERSION, + }, + + [OP_CONJ_Q_v6p] = {"conj", "conj.q", + ev_quaternion, ev_invalid, ev_quaternion, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + + [OP_DIV_F_v6p] = {"div", "div.f", + ev_float, ev_float, ev_float, + PROG_ID_VERSION, + }, + [OP_DIV_D_v6p] = {"div", "div.d", + ev_double, ev_double, ev_double, + PROG_V6P_VERSION, + }, + [OP_REM_D_v6p] = {"rem", "rem.d", + ev_double, ev_double, ev_double, + PROG_V6P_VERSION, + }, + [OP_MOD_D_v6p] = {"mod", "mod.d", + ev_double, ev_double, ev_double, + PROG_V6P_VERSION, + }, + + [OP_ADD_D_v6p] = {"add", "add.d", + ev_double, ev_double, ev_double, + PROG_V6P_VERSION, + }, + [OP_ADD_F_v6p] = {"add", "add.f", + ev_float, ev_float, ev_float, + PROG_ID_VERSION, + }, + [OP_ADD_V_v6p] = {"add", "add.v", + ev_vector, ev_vector, ev_vector, + PROG_ID_VERSION, + }, + [OP_ADD_Q_v6p] = {"add", "add.q", + ev_quaternion, ev_quaternion, ev_quaternion, + PROG_V6P_VERSION, + }, + [OP_ADD_S_v6p] = {"add", "add.s", + ev_string, ev_string, ev_string, + PROG_V6P_VERSION, + }, + + [OP_SUB_D_v6p] = {"sub", "sub.d", + ev_double, ev_double, ev_double, + PROG_V6P_VERSION, + }, + [OP_SUB_F_v6p] = {"sub", "sub.f", + ev_float, ev_float, ev_float, + PROG_ID_VERSION, + }, + [OP_SUB_V_v6p] = {"sub", "sub.v", + ev_vector, ev_vector, ev_vector, + PROG_ID_VERSION, + }, + [OP_SUB_Q_v6p] = {"sub", "sub.q", + ev_quaternion, ev_quaternion, ev_quaternion, + PROG_V6P_VERSION, + }, + + [OP_EQ_D_v6p] = {"eq", "eq.d", + ev_double, ev_double, ev_int, + PROG_V6P_VERSION, + }, + [OP_EQ_F_v6p] = {"eq", "eq.f", + ev_float, ev_float, ev_int, + PROG_ID_VERSION, + }, + [OP_EQ_V_v6p] = {"eq", "eq.v", + ev_vector, ev_vector, ev_int, + PROG_ID_VERSION, + }, + [OP_EQ_Q_v6p] = {"eq", "eq.q", + ev_quaternion, ev_quaternion, ev_int, + PROG_V6P_VERSION, + }, + [OP_EQ_S_v6p] = {"eq", "eq.s", + ev_string, ev_string, ev_int, + PROG_ID_VERSION, + }, + [OP_EQ_E_v6p] = {"eq", "eq.e", + ev_entity, ev_entity, ev_int, + PROG_ID_VERSION, + }, + [OP_EQ_FN_v6p] = {"eq", "eq.fn", + ev_func, ev_func, ev_int, + PROG_ID_VERSION, + }, + + [OP_NE_D_v6p] = {"ne", "ne.d", + ev_double, ev_double, ev_int, + PROG_V6P_VERSION, + }, + [OP_NE_F_v6p] = {"ne", "ne.f", + ev_float, ev_float, ev_int, + PROG_ID_VERSION, + }, + [OP_NE_V_v6p] = {"ne", "ne.v", + ev_vector, ev_vector, ev_int, + PROG_ID_VERSION, + }, + [OP_NE_Q_v6p] = {"ne", "ne.q", + ev_quaternion, ev_quaternion, ev_int, + PROG_V6P_VERSION, + }, + [OP_NE_S_v6p] = {"cmp", "ne.s", + ev_string, ev_string, ev_int, + PROG_ID_VERSION, + }, + [OP_NE_E_v6p] = {"ne", "ne.e", + ev_entity, ev_entity, ev_int, + PROG_ID_VERSION, + }, + [OP_NE_FN_v6p] = {"ne", "ne.fn", + ev_func, ev_func, ev_int, + PROG_ID_VERSION, + }, + + [OP_LE_D_v6p] = {"le", "le.d", + ev_double, ev_double, ev_int, + PROG_V6P_VERSION, + }, + [OP_LE_F_v6p] = {"le", "le.f", + ev_float, ev_float, ev_int, + PROG_ID_VERSION, + }, + [OP_GE_D_v6p] = {"ge", "ge.d", + ev_double, ev_double, ev_int, + PROG_V6P_VERSION, + }, + [OP_GE_F_v6p] = {"ge", "ge.f", + ev_float, ev_float, ev_int, + PROG_ID_VERSION, + }, + [OP_LE_S_v6p] = {"le", "le.s", + ev_string, ev_string, ev_int, + PROG_V6P_VERSION, + }, + [OP_GE_S_v6p] = {"ge", "ge.s", + ev_string, ev_string, ev_int, + PROG_V6P_VERSION, + }, + [OP_LT_D_v6p] = {"lt", "lt.d", + ev_double, ev_double, ev_int, + PROG_V6P_VERSION, + }, + [OP_LT_F_v6p] = {"lt", "lt.f", + ev_float, ev_float, ev_int, + PROG_ID_VERSION, + }, + [OP_GT_D_v6p] = {"gt", "gt.d", + ev_double, ev_double, ev_int, + PROG_V6P_VERSION, + }, + [OP_GT_F_v6p] = {"gt", "gt.f", + ev_float, ev_float, ev_int, + PROG_ID_VERSION, + }, + [OP_LT_S_v6p] = {"lt", "lt.s", + ev_string, ev_string, ev_int, + PROG_V6P_VERSION, + }, + [OP_GT_S_v6p] = {"gt", "gt.s", + ev_string, ev_string, ev_int, + PROG_V6P_VERSION, + }, + + [OP_LOAD_F_v6p] = {"load", "load.f", + ev_entity, ev_field, ev_float, + PROG_ID_VERSION, + "%Ga.%Gb(%Ec), %gc",//FIXME %E more flexible? + }, + [OP_LOAD_D_v6p] = {"load", "load.d", + ev_entity, ev_field, ev_double, + PROG_V6P_VERSION, + "%Ga.%Gb(%Ec), %gc", + }, + [OP_LOAD_V_v6p] = {"load", "load.v", + ev_entity, ev_field, ev_vector, + PROG_ID_VERSION, + "%Ga.%Gb(%Ec), %gc", + }, + [OP_LOAD_Q_v6p] = {"load", "load.q", + ev_entity, ev_field, ev_quaternion, + PROG_V6P_VERSION, + "%Ga.%Gb(%Ec), %gc", + }, + [OP_LOAD_S_v6p] = {"load", "load.s", + ev_entity, ev_field, ev_string, + PROG_ID_VERSION, + "%Ga.%Gb(%Ec), %gc", + }, + [OP_LOAD_ENT_v6p] = {"load", "load.ent", + ev_entity, ev_field, ev_entity, + PROG_ID_VERSION, + "%Ga.%Gb(%Ec), %gc", + }, + [OP_LOAD_FLD_v6p] = {"load", "load.fld", + ev_entity, ev_field, ev_field, + PROG_ID_VERSION, + "%Ga.%Gb(%Ec), %gc", + }, + [OP_LOAD_FN_v6p] = {"load", "load.fn", + ev_entity, ev_field, ev_func, + PROG_ID_VERSION, + "%Ga.%Gb(%Ec), %gc", + }, + [OP_LOAD_I_v6p] = {"load", "load.i", + ev_entity, ev_field, ev_int, + PROG_V6P_VERSION, + "%Ga.%Gb(%Ec), %gc", + }, + [OP_LOAD_P_v6p] = {"load", "load.p", + ev_entity, ev_field, ev_ptr, + PROG_V6P_VERSION, + "%Ga.%Gb(%Ec), %gc", + }, + + [OP_LOADB_D_v6p] = {"load", "loadb.d", + ev_ptr, ev_int, ev_double, + PROG_V6P_VERSION, + "*(%Ga + %Gb), %gc", + }, + [OP_LOADB_F_v6p] = {"load", "loadb.f", + ev_ptr, ev_int, ev_float, + PROG_V6P_VERSION, + "*(%Ga + %Gb), %gc", + }, + [OP_LOADB_V_v6p] = {"load", "loadb.v", + ev_ptr, ev_int, ev_vector, + PROG_V6P_VERSION, + "*(%Ga + %Gb), %gc", + }, + [OP_LOADB_Q_v6p] = {"load", "loadb.q", + ev_ptr, ev_int, ev_quaternion, + PROG_V6P_VERSION, + "*(%Ga + %Gb), %gc", + }, + [OP_LOADB_S_v6p] = {"load", "loadb.s", + ev_ptr, ev_int, ev_string, + PROG_V6P_VERSION, + "*(%Ga + %Gb), %gc", + }, + [OP_LOADB_ENT_v6p] = {"load", "loadb.ent", + ev_ptr, ev_int, ev_entity, + PROG_V6P_VERSION, + "*(%Ga + %Gb), %gc", + }, + [OP_LOADB_FLD_v6p] = {"load", "loadb.fld", + ev_ptr, ev_int, ev_field, + PROG_V6P_VERSION, + "*(%Ga + %Gb), %gc", + }, + [OP_LOADB_FN_v6p] = {"load", "loadb.fn", + ev_ptr, ev_int, ev_func, + PROG_V6P_VERSION, + "*(%Ga + %Gb), %gc", + }, + [OP_LOADB_I_v6p] = {"load", "loadb.i", + ev_ptr, ev_int, ev_int, + PROG_V6P_VERSION, + "*(%Ga + %Gb), %gc", + }, + [OP_LOADB_P_v6p] = {"load", "loadb.p", + ev_ptr, ev_int, ev_ptr, + PROG_V6P_VERSION, + "*(%Ga + %Gb), %gc", + }, + + [OP_LOADBI_D_v6p] = {"load", "loadbi.d", + ev_ptr, ev_short, ev_double, + PROG_V6P_VERSION, + "*(%Ga + %sb), %gc", + }, + [OP_LOADBI_F_v6p] = {"load", "loadbi.f", + ev_ptr, ev_short, ev_float, + PROG_V6P_VERSION, + "*(%Ga + %sb), %gc", + }, + [OP_LOADBI_V_v6p] = {"load", "loadbi.v", + ev_ptr, ev_short, ev_vector, + PROG_V6P_VERSION, + "*(%Ga + %sb), %gc", + }, + [OP_LOADBI_Q_v6p] = {"load", "loadbi.q", + ev_ptr, ev_short, ev_quaternion, + PROG_V6P_VERSION, + "*(%Ga + %sb), %gc", + }, + [OP_LOADBI_S_v6p] = {"load", "loadbi.s", + ev_ptr, ev_short, ev_string, + PROG_V6P_VERSION, + "*(%Ga + %sb), %gc", + }, + [OP_LOADBI_ENT_v6p] = {"load", "loadbi.ent", + ev_ptr, ev_short, ev_entity, + PROG_V6P_VERSION, + "*(%Ga + %sb), %gc", + }, + [OP_LOADBI_FLD_v6p] = {"load", "loadbi.fld", + ev_ptr, ev_short, ev_field, + PROG_V6P_VERSION, + "*(%Ga + %sb), %gc", + }, + [OP_LOADBI_FN_v6p] = {"load", "loadbi.fn", + ev_ptr, ev_short, ev_func, + PROG_V6P_VERSION, + "*(%Ga + %sb), %gc", + }, + [OP_LOADBI_I_v6p] = {"load", "loadbi.i", + ev_ptr, ev_short, ev_int, + PROG_V6P_VERSION, + "*(%Ga + %sb), %gc", + }, + [OP_LOADBI_P_v6p] = {"load", "loadbi.p", + ev_ptr, ev_short, ev_ptr, + PROG_V6P_VERSION, + "*(%Ga + %sb), %gc", + }, + + [OP_ADDRESS_v6p] = {"lea", "address", + ev_entity, ev_field, ev_ptr, + PROG_ID_VERSION, + "%Ga.%Gb(%Ec), %gc", + }, + + [OP_ADDRESS_VOID_v6p] = {"lea", "address", + ev_void, ev_invalid, ev_ptr, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + [OP_ADDRESS_D_v6p] = {"lea", "address.d", + ev_double, ev_invalid, ev_ptr, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + [OP_ADDRESS_F_v6p] = {"lea", "address.f", + ev_float, ev_invalid, ev_ptr, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + [OP_ADDRESS_V_v6p] = {"lea", "address.v", + ev_vector, ev_invalid, ev_ptr, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + [OP_ADDRESS_Q_v6p] = {"lea", "address.q", + ev_quaternion, ev_invalid, ev_ptr, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + [OP_ADDRESS_S_v6p] = {"lea", "address.s", + ev_string, ev_invalid, ev_ptr, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + [OP_ADDRESS_ENT_v6p] = {"lea", "address.ent", + ev_entity, ev_invalid, ev_ptr, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + [OP_ADDRESS_FLD_v6p] = {"lea", "address.fld", + ev_field, ev_invalid, ev_ptr, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + [OP_ADDRESS_FN_v6p] = {"lea", "address.fn", + ev_func, ev_invalid, ev_ptr, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + [OP_ADDRESS_I_v6p] = {"lea", "address.i", + ev_int, ev_invalid, ev_ptr, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + [OP_ADDRESS_P_v6p] = {"lea", "address.p", + ev_ptr, ev_invalid, ev_ptr, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + + [OP_LEA_v6p] = {"lea", "lea", + ev_ptr, ev_int, ev_ptr, + PROG_V6P_VERSION, + "(%Ga + %Gb), %gc", + }, + [OP_LEAI_v6p] = {"lea", "leai", + ev_ptr, ev_short, ev_ptr, + PROG_V6P_VERSION, + "(%Ga + %sb), %gc", + }, + + [OP_CONV_IF_v6p] = {"conv", "conv.if", + ev_int, ev_invalid, ev_float, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + [OP_CONV_FI_v6p] = {"conv", "conv.fi", + ev_float, ev_invalid, ev_int, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + [OP_CONV_ID_v6p] = {"conv", "conv.id", + ev_int, ev_invalid, ev_double, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + [OP_CONV_DI_v6p] = {"conv", "conv.di", + ev_double, ev_invalid, ev_int, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + [OP_CONV_FD_v6p] = {"conv", "conv.fd", + ev_float, ev_invalid, ev_double, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + [OP_CONV_DF_v6p] = {"conv", "conv.df", + ev_double, ev_invalid, ev_float, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + + [OP_STORE_D_v6p] = {"assign", "store.d", + ev_double, ev_double, ev_invalid, + PROG_V6P_VERSION, + "%Ga, %gb", + }, + [OP_STORE_F_v6p] = {"assign", "store.f", + ev_float, ev_float, ev_invalid, + PROG_ID_VERSION, + "%Ga, %gb", + }, + [OP_STORE_V_v6p] = {"assign", "store.v", + ev_vector, ev_vector, ev_invalid, + PROG_ID_VERSION, + "%Ga, %gb", + }, + [OP_STORE_Q_v6p] = {"assign", "store.q", + ev_quaternion, ev_quaternion, ev_invalid, + PROG_V6P_VERSION, + "%Ga, %gb", + }, + [OP_STORE_S_v6p] = {"assign", "store.s", + ev_string, ev_string, ev_invalid, + PROG_ID_VERSION, + "%Ga, %gb", + }, + [OP_STORE_ENT_v6p] = {"assign", "store.ent", + ev_entity, ev_entity, ev_invalid, + PROG_ID_VERSION, + "%Ga, %gb", + }, + [OP_STORE_FLD_v6p] = {"assign", "store.fld", + ev_field, ev_field, ev_invalid, + PROG_ID_VERSION, + "%Ga, %gb", + }, + [OP_STORE_FN_v6p] = {"assign", "store.fn", + ev_func, ev_func, ev_invalid, + PROG_ID_VERSION, + "%Ga, %gb", + }, + [OP_STORE_I_v6p] = {"assign", "store.i", + ev_int, ev_int, ev_invalid, + PROG_V6P_VERSION, + "%Ga, %gb", + }, + [OP_STORE_P_v6p] = {"assign", "store.p", + ev_ptr, ev_ptr, ev_invalid, + PROG_V6P_VERSION, + "%Ga, %gb", + }, + + [OP_STOREP_D_v6p] = {"store", "storep.d", + ev_double, ev_ptr, ev_invalid, + PROG_ID_VERSION, + "%Ga, *%Gb", + }, + [OP_STOREP_F_v6p] = {"store", "storep.f", + ev_float, ev_ptr, ev_invalid, + PROG_ID_VERSION, + "%Ga, *%Gb", + }, + [OP_STOREP_V_v6p] = {"store", "storep.v", + ev_vector, ev_ptr, ev_invalid, + PROG_ID_VERSION, + "%Ga, *%Gb", + }, + [OP_STOREP_Q_v6p] = {"store", "storep.q", + ev_quaternion, ev_ptr, ev_invalid, + PROG_V6P_VERSION, + "%Ga, *%Gb", + }, + [OP_STOREP_S_v6p] = {"store", "storep.s", + ev_string, ev_ptr, ev_invalid, + PROG_ID_VERSION, + "%Ga, *%Gb", + }, + [OP_STOREP_ENT_v6p] = {"store", "storep.ent", + ev_entity, ev_ptr, ev_invalid, + PROG_ID_VERSION, + "%Ga, *%Gb", + }, + [OP_STOREP_FLD_v6p] = {"store", "storep.fld", + ev_field, ev_ptr, ev_invalid, + PROG_ID_VERSION, + "%Ga, *%Gb", + }, + [OP_STOREP_FN_v6p] = {"store", "storep.fn", + ev_func, ev_ptr, ev_invalid, + PROG_ID_VERSION, + "%Ga, *%Gb", + }, + [OP_STOREP_I_v6p] = {"store", "storep.i", + ev_int, ev_ptr, ev_invalid, + PROG_V6P_VERSION, + "%Ga, *%Gb", + }, + [OP_STOREP_P_v6p] = {"store", "storep.p", + ev_ptr, ev_ptr, ev_invalid, + PROG_V6P_VERSION, + "%Ga, *%Gb", + }, + + [OP_STOREB_D_v6p] = {"store", "storeb.d", + ev_double, ev_ptr, ev_int, + PROG_V6P_VERSION, + "%Ga, *(%Gb + %Gc)", + }, + [OP_STOREB_F_v6p] = {"store", "storeb.f", + ev_float, ev_ptr, ev_int, + PROG_V6P_VERSION, + "%Ga, *(%Gb + %Gc)", + }, + [OP_STOREB_V_v6p] = {"store", "storeb.v", + ev_vector, ev_ptr, ev_int, + PROG_V6P_VERSION, + "%Ga, *(%Gb + %Gc)", + }, + [OP_STOREB_Q_v6p] = {"store", "storeb.q", + ev_quaternion, ev_ptr, ev_int, + PROG_V6P_VERSION, + "%Ga, *(%Gb + %Gc)", + }, + [OP_STOREB_S_v6p] = {"store", "storeb.s", + ev_string, ev_ptr, ev_int, + PROG_V6P_VERSION, + "%Ga, *(%Gb + %Gc)", + }, + [OP_STOREB_ENT_v6p] = {"store", "storeb.ent", + ev_entity, ev_ptr, ev_int, + PROG_V6P_VERSION, + "%Ga, *(%Gb + %Gc)", + }, + [OP_STOREB_FLD_v6p] = {"store", "storeb.fld", + ev_field, ev_ptr, ev_int, + PROG_V6P_VERSION, + "%Ga, *(%Gb + %Gc)", + }, + [OP_STOREB_FN_v6p] = {"store", "storeb.fn", + ev_func, ev_ptr, ev_int, + PROG_V6P_VERSION, + "%Ga, *(%Gb + %Gc)", + }, + [OP_STOREB_I_v6p] = {"store", "storeb.i", + ev_int, ev_ptr, ev_int, + PROG_V6P_VERSION, + "%Ga, *(%Gb + %Gc)", + }, + [OP_STOREB_P_v6p] = {"store", "storeb.p", + ev_ptr, ev_ptr, ev_int, + PROG_V6P_VERSION, + "%Ga, *(%Gb + %Gc)", + }, + + [OP_STOREBI_D_v6p] = {"store", "storebi.d", + ev_double, ev_ptr, ev_short, + PROG_V6P_VERSION, + "%Ga, *(%Gb + %sc)", + }, + [OP_STOREBI_F_v6p] = {"store", "storebi.f", + ev_float, ev_ptr, ev_short, + PROG_V6P_VERSION, + "%Ga, *(%Gb + %sc)", + }, + [OP_STOREBI_V_v6p] = {"store", "storebi.v", + ev_vector, ev_ptr, ev_short, + PROG_V6P_VERSION, + "%Ga, *(%Gb + %sc)", + }, + [OP_STOREBI_Q_v6p] = {"store", "storebi.q", + ev_quaternion, ev_ptr, ev_short, + PROG_V6P_VERSION, + "%Ga, *(%Gb + %sc)", + }, + [OP_STOREBI_S_v6p] = {"store", "storebi.s", + ev_string, ev_ptr, ev_short, + PROG_V6P_VERSION, + "%Ga, *(%Gb + %sc)", + }, + [OP_STOREBI_ENT_v6p] = {"store", "storebi.ent", + ev_entity, ev_ptr, ev_short, + PROG_V6P_VERSION, + "%Ga, *(%Gb + %sc)", + }, + [OP_STOREBI_FLD_v6p] = {"store", "storebi.fld", + ev_field, ev_ptr, ev_short, + PROG_V6P_VERSION, + "%Ga, *(%Gb + %sc)", + }, + [OP_STOREBI_FN_v6p] = {"store", "storebi.fn", + ev_func, ev_ptr, ev_short, + PROG_V6P_VERSION, + "%Ga, *(%Gb + %sc)", + }, + [OP_STOREBI_I_v6p] = {"store", "storebi.i", + ev_int, ev_ptr, ev_short, + PROG_V6P_VERSION, + "%Ga, *(%Gb + %sc)", + }, + [OP_STOREBI_P_v6p] = {"store", "storebi.p", + ev_ptr, ev_ptr, ev_short, + PROG_V6P_VERSION, + "%Ga, *(%Gb + %sc)", + }, + + [OP_RETURN_v6p] = {"return", "return", + ev_void, ev_invalid, ev_invalid, + PROG_ID_VERSION, + "%Ra", + }, + + [OP_RETURN_V_v6p] = {"return", "return", + ev_invalid, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "", + }, + + [OP_NOT_D_v6p] = {"not", "not.d", + ev_double, ev_invalid, ev_int, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + [OP_NOT_F_v6p] = {"not", "not.f", + ev_float, ev_invalid, ev_int, + PROG_ID_VERSION, + "%Ga, %gc", + }, + [OP_NOT_V_v6p] = {"not", "not.v", + ev_vector, ev_invalid, ev_int, + PROG_ID_VERSION, + "%Ga, %gc", + }, + [OP_NOT_Q_v6p] = {"not", "not.q", + ev_quaternion, ev_invalid, ev_int, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + [OP_NOT_S_v6p] = {"not", "not.s", + ev_string, ev_invalid, ev_int, + PROG_ID_VERSION, + "%Ga, %gc", + }, + [OP_NOT_ENT_v6p] = {"not", "not.ent", + ev_entity, ev_invalid, ev_int, + PROG_ID_VERSION, + "%Ga, %gc", + }, + [OP_NOT_FN_v6p] = {"not", "not.fn", + ev_func, ev_invalid, ev_int, + PROG_ID_VERSION, + "%Ga, %gc", + }, + [OP_NOT_P_v6p] = {"not", "not.p", + ev_ptr, ev_invalid, ev_int, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + + [OP_IF_v6p] = {"ifnz", "if", + ev_int, ev_short, ev_invalid, + PROG_ID_VERSION, + "%Ga branch %sb (%Ob)", + }, + [OP_IFNOT_v6p] = {"ifz", "ifnot", + ev_int, ev_short, ev_invalid, + PROG_ID_VERSION, + "%Ga branch %sb (%Ob)", + }, + [OP_IFBE_v6p] = {"ifbe", "ifbe", + ev_int, ev_short, ev_invalid, + PROG_V6P_VERSION, + "%Ga branch %sb (%Ob)", + }, + [OP_IFB_v6p] = {"ifb", "ifb", + ev_int, ev_short, ev_invalid, + PROG_V6P_VERSION, + "%Ga branch %sb (%Ob)", + }, + [OP_IFAE_v6p] = {"ifae", "ifae", + ev_int, ev_short, ev_invalid, + PROG_V6P_VERSION, + "%Ga branch %sb (%Ob)", + }, + [OP_IFA_v6p] = {"ifa", "ifa", + ev_int, ev_short, ev_invalid, + PROG_V6P_VERSION, + "%Ga branch %sb (%Ob)", + }, + +// calls returns REG_RETURN + [OP_CALL0_v6p] = {"call0", "call0", + ev_func, ev_invalid, ev_invalid, + PROG_ID_VERSION, + "%Fa ()", + }, + [OP_CALL1_v6p] = {"call1", "call1", + ev_func, ev_invalid, ev_invalid, + PROG_ID_VERSION, + "%Fa (%P0x)", + }, + [OP_CALL2_v6p] = {"call2", "call2", + ev_func, ev_invalid, ev_invalid, + PROG_ID_VERSION, + "%Fa (%P0x, %P1x)", + }, + [OP_CALL3_v6p] = {"call3", "call3", + ev_func, ev_invalid, ev_invalid, + PROG_ID_VERSION, + "%Fa (%P0x, %P1x, %P2x)", + }, + [OP_CALL4_v6p] = {"call4", "call4", + ev_func, ev_invalid, ev_invalid, + PROG_ID_VERSION, + "%Fa (%P0x, %P1x, %P2x, %P3x)", + }, + [OP_CALL5_v6p] = {"call5", "call5", + ev_func, ev_invalid, ev_invalid, + PROG_ID_VERSION, + "%Fa (%P0x, %P1x, %P2x, %P3x, %P4x)", + }, + [OP_CALL6_v6p] = {"call6", "call6", + ev_func, ev_invalid, ev_invalid, + PROG_ID_VERSION, + "%Fa (%P0x, %P1x, %P2x, %P3x, %P4x, %P5x)", + }, + [OP_CALL7_v6p] = {"call7", "call7", + ev_func, ev_invalid, ev_invalid, + PROG_ID_VERSION, + "%Fa (%P0x, %P1x, %P2x, %P3x, %P4x, %P5x, %P6x)", + }, + [OP_CALL8_v6p] = {"call8", "call8", + ev_func, ev_invalid, ev_invalid, + PROG_ID_VERSION, + "%Fa (%P0x, %P1x, %P2x, %P3x, %P4x, %P5x, %P6x, %P7x)", + }, + [OP_RCALL0_v6p] = {"rcall0", 0, + ev_invalid, ev_invalid, ev_invalid, + ~0, // not a valid instruction + 0, + }, + [OP_RCALL1_v6p] = {"rcall1", "rcall1", + ev_func, ev_void, ev_invalid, + PROG_V6P_VERSION, + "%Fa (%P0b)", + }, + [OP_RCALL2_v6p] = {"rcall2", "rcall2", + ev_func, ev_void, ev_void, + PROG_V6P_VERSION, + "%Fa (%P0b, %P1c)", + }, + [OP_RCALL3_v6p] = {"rcall3", "rcall3", + ev_func, ev_void, ev_void, + PROG_V6P_VERSION, + "%Fa (%P0b, %P1c, %P2x)", + }, + [OP_RCALL4_v6p] = {"rcall4", "rcall4", + ev_func, ev_void, ev_void, + PROG_V6P_VERSION, + "%Fa (%P0b, %P1c, %P2x, %P3x)", + }, + [OP_RCALL5_v6p] = {"rcall5", "rcall5", + ev_func, ev_void, ev_void, + PROG_V6P_VERSION, + "%Fa (%P0b, %P1c, %P2x, %P3x, %P4x)", + }, + [OP_RCALL6_v6p] = {"rcall6", "rcall6", + ev_func, ev_void, ev_void, + PROG_V6P_VERSION, + "%Fa (%P0b, %P1c, %P2x, %P3x, %P4x, %P5x)", + }, + [OP_RCALL7_v6p] = {"rcall7", "rcall7", + ev_func, ev_void, ev_void, + PROG_V6P_VERSION, + "%Fa (%P0b, %P1c, %P2x, %P3x, %P4x, %P5x, %P6x)", + }, + [OP_RCALL8_v6p] = {"rcall8", "rcall8", + ev_func, ev_void, ev_void, + PROG_V6P_VERSION, + "%Fa (%P0b, %P1c, %P2x, %P3x, %P4x, %P5x, %P6x, %P7x)", + }, + + [OP_STATE_v6p] = {"state", "state", + ev_float, ev_func, ev_invalid, + PROG_ID_VERSION, + "%Ga, %Gb", + }, + + [OP_STATE_F_v6p] = {"state", "state.f", + ev_float, ev_func, ev_float, + PROG_V6P_VERSION, + "%Ga, %Gb, %Gc", + }, + + [OP_GOTO_v6p] = {"jump", "goto", + ev_short, ev_invalid, ev_invalid, + PROG_ID_VERSION, + "branch %sa (%Oa)", + }, + [OP_JUMP_v6p] = {"jump", "jump", + ev_int, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%Ga", + }, + [OP_JUMPB_v6p] = {"jump", "jump", + ev_void, ev_int, ev_invalid, + PROG_V6P_VERSION, + "%Ga[%Gb]", + }, + + [OP_AND_v6p] = {"and", "and.f", + ev_float, ev_float, ev_int, + PROG_ID_VERSION, + }, + [OP_OR_v6p] = {"or", "or.f", + ev_float, ev_float, ev_int, + PROG_ID_VERSION, + }, + + [OP_SHL_F_v6p] = {"shl", "shl.f", + ev_float, ev_float, ev_float, + PROG_V6P_VERSION, + }, + [OP_SHR_F_v6p] = {"shr", "shr.f", + ev_float, ev_float, ev_float, + PROG_V6P_VERSION, + }, + [OP_SHL_I_v6p] = {"shl", "shl.i", + ev_int, ev_int, ev_int, + PROG_V6P_VERSION, + }, + [OP_SHR_I_v6p] = {"shr", "shr.i", + ev_int, ev_int, ev_int, + PROG_V6P_VERSION, + }, + [OP_SHR_U_v6p] = {"shr", "shr.u", + ev_uint, ev_int, ev_uint, + PROG_V6P_VERSION, + }, + + [OP_BITAND_v6p] = {"bitand", "bitand.f", + ev_float, ev_float, ev_float, + PROG_ID_VERSION, + }, + [OP_BITOR_v6p] = {"bitor", "bitor.f", + ev_float, ev_float, ev_float, + PROG_ID_VERSION, + }, + + [OP_ADD_I_v6p] = {"add", "add.i", + ev_int, ev_int, ev_int, + PROG_V6P_VERSION, + }, + [OP_SUB_I_v6p] = {"sub", "sub.i", + ev_int, ev_int, ev_int, + PROG_V6P_VERSION, + }, + [OP_MUL_I_v6p] = {"mul", "mul.i", + ev_int, ev_int, ev_int, + PROG_V6P_VERSION, + }, + [OP_DIV_I_v6p] = {"div", "div.i", + ev_int, ev_int, ev_int, + PROG_V6P_VERSION, + }, + [OP_REM_I_v6p] = {"rem", "rem.i", + ev_int, ev_int, ev_int, + PROG_V6P_VERSION, + }, + [OP_MOD_I_v6p] = {"mod", "mod.i", + ev_int, ev_int, ev_int, + PROG_V6P_VERSION, + }, + [OP_BITAND_I_v6p] = {"bitand", "bitand.i", + ev_int, ev_int, ev_int, + PROG_V6P_VERSION, + }, + [OP_BITOR_I_v6p] = {"bitor", "bitor.i", + ev_int, ev_int, ev_int, + PROG_V6P_VERSION, + }, + + [OP_REM_F_v6p] = {"rem", "rem.f", + ev_float, ev_float, ev_float, + PROG_V6P_VERSION, + }, + + [OP_MOD_F_v6p] = {"mod", "mod.f", + ev_float, ev_float, ev_float, + PROG_V6P_VERSION, + }, + + [OP_GE_I_v6p] = {"ge", "ge.i", + ev_int, ev_int, ev_int, + PROG_V6P_VERSION, + }, + [OP_LE_I_v6p] = {"le", "le.i", + ev_int, ev_int, ev_int, + PROG_V6P_VERSION, + }, + [OP_GT_I_v6p] = {"gt", "gt.i", + ev_int, ev_int, ev_int, + PROG_V6P_VERSION, + }, + [OP_LT_I_v6p] = {"lt", "lt.i", + ev_int, ev_int, ev_int, + PROG_V6P_VERSION, + }, + + [OP_AND_I_v6p] = {"and", "and.i", + ev_int, ev_int, ev_int, + PROG_V6P_VERSION, + }, + [OP_OR_I_v6p] = {"or", "or.i", + ev_int, ev_int, ev_int, + PROG_V6P_VERSION, + }, + [OP_NOT_I_v6p] = {"not", "not.i", + ev_int, ev_invalid, ev_int, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + [OP_EQ_I_v6p] = {"eq", "eq.i", + ev_int, ev_int, ev_int, + PROG_V6P_VERSION, + }, + [OP_NE_I_v6p] = {"ne", "ne.i", + ev_int, ev_int, ev_int, + PROG_V6P_VERSION, + }, + + [OP_GE_U_v6p] = {"ge", "ge.u", + ev_uint, ev_uint, ev_int, + PROG_V6P_VERSION, + }, + [OP_LE_U_v6p] = {"le", "le.u", + ev_uint, ev_uint, ev_int, + PROG_V6P_VERSION, + }, + [OP_GT_U_v6p] = {"gt", "gt.u", + ev_uint, ev_uint, ev_int, + PROG_V6P_VERSION, + }, + [OP_LT_U_v6p] = {"lt", "lt.u", + ev_uint, ev_uint, ev_int, + PROG_V6P_VERSION, + }, + + [OP_BITXOR_F_v6p] = {"bitxor", "bitxor.f", + ev_float, ev_float, ev_float, + PROG_V6P_VERSION, + }, + [OP_BITNOT_F_v6p] = {"bitnot", "bitnot.f", + ev_float, ev_invalid, ev_float, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + [OP_BITXOR_I_v6p] = {"bitxor", "bitxor.i", + ev_int, ev_int, ev_int, + PROG_V6P_VERSION, + }, + [OP_BITNOT_I_v6p] = {"bitnot", "bitnot.i", + ev_int, ev_invalid, ev_int, + PROG_V6P_VERSION, + "%Ga, %gc", + }, + + [OP_GE_P_v6p] = {"ge", "ge.p", + ev_ptr, ev_ptr, ev_int, + PROG_V6P_VERSION, + }, + [OP_LE_P_v6p] = {"le", "le.p", + ev_ptr, ev_ptr, ev_int, + PROG_V6P_VERSION, + }, + [OP_GT_P_v6p] = {"gt", "gt.p", + ev_ptr, ev_ptr, ev_int, + PROG_V6P_VERSION, + }, + [OP_LT_P_v6p] = {"lt", "lt.p", + ev_ptr, ev_ptr, ev_int, + PROG_V6P_VERSION, + }, + [OP_EQ_P_v6p] = {"eq", "eq.p", + ev_ptr, ev_ptr, ev_int, + PROG_V6P_VERSION, + }, + [OP_NE_P_v6p] = {"ne", "ne.p", + ev_ptr, ev_ptr, ev_int, + PROG_V6P_VERSION, + }, + + [OP_MOVEI_v6p] = {"move", "movei", + ev_void, ev_short, ev_void, + PROG_V6P_VERSION, + "%Ga, %sb, %gc", + }, + [OP_MOVEP_v6p] = {"movep", "movep", + ev_ptr, ev_int, ev_ptr, + PROG_V6P_VERSION, + "%Ga, %Gb, %Gc", + }, + [OP_MOVEPI_v6p] = {"movep", "movepi", + ev_ptr, ev_short, ev_ptr, + PROG_V6P_VERSION, + "%Ga, %sb, %Gc", + }, + [OP_MEMSETI_v6p] = {"memset", "memseti", + ev_int, ev_short, ev_void, + PROG_V6P_VERSION, + "%Ga, %sb, %gc", + }, + [OP_MEMSETP_v6p] = {"memsetp", "memsetp", + ev_int, ev_int, ev_ptr, + PROG_V6P_VERSION, + "%Ga, %Gb, %Gc", + }, + [OP_MEMSETPI_v6p] = {"memsetp", "memsetpi", + ev_int, ev_short, ev_ptr, + PROG_V6P_VERSION, + "%Ga, %sb, %Gc", + }, + + [OP_PUSH_S_v6p] = {"push", "push.s", + ev_string, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%Ga", + }, + [OP_PUSH_F_v6p] = {"push", "push.f", + ev_float, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%Ga", + }, + [OP_PUSH_V_v6p] = {"push", "push.v", + ev_vector, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%Ga", + }, + [OP_PUSH_ENT_v6p] = {"push", "push.ent", + ev_entity, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%Ga", + }, + [OP_PUSH_FLD_v6p] = {"push", "push.fld", + ev_field, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%Ga", + }, + [OP_PUSH_FN_v6p] = {"push", "push.fn", + ev_func, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%Ga", + }, + [OP_PUSH_P_v6p] = {"push", "push.p", + ev_ptr, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%Ga", + }, + [OP_PUSH_Q_v6p] = {"push", "push.q", + ev_quaternion, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%Ga", + }, + [OP_PUSH_I_v6p] = {"push", "push.i", + ev_int, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%Ga", + }, + [OP_PUSH_D_v6p] = {"push", "push.d", + ev_double, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%Ga", + }, + + [OP_PUSHB_S_v6p] = {"push", "pushb.s", + ev_ptr, ev_int, ev_string, + PROG_V6P_VERSION, + "*(%Ga + %Gb)", + }, + [OP_PUSHB_F_v6p] = {"push", "pushb.f", + ev_ptr, ev_int, ev_float, + PROG_V6P_VERSION, + "*(%Ga + %Gb)", + }, + [OP_PUSHB_V_v6p] = {"push", "pushb.v", + ev_ptr, ev_int, ev_vector, + PROG_V6P_VERSION, + "*(%Ga + %Gb)", + }, + [OP_PUSHB_ENT_v6p] = {"push", "pushb.ent", + ev_ptr, ev_int, ev_entity, + PROG_V6P_VERSION, + "*(%Ga + %Gb)", + }, + [OP_PUSHB_FLD_v6p] = {"push", "pushb.fld", + ev_ptr, ev_int, ev_field, + PROG_V6P_VERSION, + "*(%Ga + %Gb)", + }, + [OP_PUSHB_FN_v6p] = {"push", "pushb.fn", + ev_ptr, ev_int, ev_func, + PROG_V6P_VERSION, + "*(%Ga + %Gb)", + }, + [OP_PUSHB_P_v6p] = {"push", "pushb.p", + ev_ptr, ev_int, ev_ptr, + PROG_V6P_VERSION, + "*(%Ga + %Gb)", + }, + [OP_PUSHB_Q_v6p] = {"push", "pushb.q", + ev_ptr, ev_int, ev_quaternion, + PROG_V6P_VERSION, + "*(%Ga + %Gb)", + }, + [OP_PUSHB_I_v6p] = {"push", "pushb.i", + ev_ptr, ev_int, ev_int, + PROG_V6P_VERSION, + "*(%Ga + %Gb)", + }, + [OP_PUSHB_D_v6p] = {"push", "pushb.d", + ev_ptr, ev_int, ev_double, + PROG_V6P_VERSION, + "*(%Ga + %Gb)", + }, + + [OP_PUSHBI_S_v6p] = {"push", "pushbi.s", + ev_ptr, ev_short, ev_string, + PROG_V6P_VERSION, + "*(%Ga + %sb)", + }, + [OP_PUSHBI_F_v6p] = {"push", "pushbi.f", + ev_ptr, ev_short, ev_float, + PROG_V6P_VERSION, + "*(%Ga + %sb)", + }, + [OP_PUSHBI_V_v6p] = {"push", "pushbi.v", + ev_ptr, ev_short, ev_vector, + PROG_V6P_VERSION, + "*(%Ga + %sb)", + }, + [OP_PUSHBI_ENT_v6p] = {"push", "pushbi.ent", + ev_ptr, ev_short, ev_entity, + PROG_V6P_VERSION, + "*(%Ga + %sb)", + }, + [OP_PUSHBI_FLD_v6p] = {"push", "pushbi.fld", + ev_ptr, ev_short, ev_field, + PROG_V6P_VERSION, + "*(%Ga + %sb)", + }, + [OP_PUSHBI_FN_v6p] = {"push", "pushbi.fn", + ev_ptr, ev_short, ev_func, + PROG_V6P_VERSION, + "*(%Ga + %sb)", + }, + [OP_PUSHBI_P_v6p] = {"push", "pushbi.p", + ev_ptr, ev_short, ev_ptr, + PROG_V6P_VERSION, + "*(%Ga + %sb)", + }, + [OP_PUSHBI_Q_v6p] = {"push", "pushbi.q", + ev_ptr, ev_short, ev_quaternion, + PROG_V6P_VERSION, + "*(%Ga + %sb)", + }, + [OP_PUSHBI_I_v6p] = {"push", "pushbi.i", + ev_ptr, ev_short, ev_int, + PROG_V6P_VERSION, + "*(%Ga + %sb)", + }, + [OP_PUSHBI_D_v6p] = {"push", "pushbi.d", + ev_ptr, ev_short, ev_double, + PROG_V6P_VERSION, + "*(%Ga + %sb)", + }, + + [OP_POP_S_v6p] = {"pop", "pop.s", + ev_string, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%ga", + }, + [OP_POP_F_v6p] = {"pop", "pop.f", + ev_float, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%ga", + }, + [OP_POP_V_v6p] = {"pop", "pop.v", + ev_vector, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%ga", + }, + [OP_POP_ENT_v6p] = {"pop", "pop.ent", + ev_entity, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%ga", + }, + [OP_POP_FLD_v6p] = {"pop", "pop.fld", + ev_field, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%ga", + }, + [OP_POP_FN_v6p] = {"pop", "pop.fn", + ev_func, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%ga", + }, + [OP_POP_P_v6p] = {"pop", "pop.p", + ev_ptr, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%ga", + }, + [OP_POP_Q_v6p] = {"pop", "pop.q", + ev_quaternion, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%ga", + }, + [OP_POP_I_v6p] = {"pop", "pop.i", + ev_int, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%ga", + }, + [OP_POP_D_v6p] = {"pop", "pop.d", + ev_double, ev_invalid, ev_invalid, + PROG_V6P_VERSION, + "%ga", + }, + + [OP_POPB_S_v6p] = {"pop", "popb.s", + ev_ptr, ev_int, ev_string, + PROG_V6P_VERSION, + "*(%Ga + %Gb)", + }, + [OP_POPB_F_v6p] = {"pop", "popb.f", + ev_ptr, ev_int, ev_float, + PROG_V6P_VERSION, + "*(%Ga + %Gb)", + }, + [OP_POPB_V_v6p] = {"pop", "popb.v", + ev_ptr, ev_int, ev_vector, + PROG_V6P_VERSION, + "*(%Ga + %Gb)", + }, + [OP_POPB_ENT_v6p] = {"pop", "popb.ent", + ev_ptr, ev_int, ev_entity, + PROG_V6P_VERSION, + "*(%Ga + %Gb)", + }, + [OP_POPB_FLD_v6p] = {"pop", "popb.fld", + ev_ptr, ev_int, ev_field, + PROG_V6P_VERSION, + "*(%Ga + %Gb)", + }, + [OP_POPB_FN_v6p] = {"pop", "popb.fn", + ev_ptr, ev_int, ev_func, + PROG_V6P_VERSION, + "*(%Ga + %Gb)", + }, + [OP_POPB_P_v6p] = {"pop", "popb.p", + ev_ptr, ev_int, ev_ptr, + PROG_V6P_VERSION, + "*(%Ga + %Gb)", + }, + [OP_POPB_Q_v6p] = {"pop", "popb.q", + ev_ptr, ev_int, ev_quaternion, + PROG_V6P_VERSION, + "*(%Ga + %Gb)", + }, + [OP_POPB_I_v6p] = {"pop", "popb.i", + ev_ptr, ev_int, ev_int, + PROG_V6P_VERSION, + "*(%Ga + %Gb)", + }, + [OP_POPB_D_v6p] = {"pop", "popb.d", + ev_ptr, ev_int, ev_double, + PROG_V6P_VERSION, + "*(%Ga + %Gb)", + }, + + [OP_POPBI_S_v6p] = {"pop", "popbi.s", + ev_ptr, ev_short, ev_string, + PROG_V6P_VERSION, + "*(%Ga + %sb)", + }, + [OP_POPBI_F_v6p] = {"pop", "popbi.f", + ev_ptr, ev_short, ev_float, + PROG_V6P_VERSION, + "*(%Ga + %sb)", + }, + [OP_POPBI_V_v6p] = {"pop", "popbi.v", + ev_ptr, ev_short, ev_vector, + PROG_V6P_VERSION, + "*(%Ga + %sb)", + }, + [OP_POPBI_ENT_v6p] = {"pop", "popbi.ent", + ev_ptr, ev_short, ev_entity, + PROG_V6P_VERSION, + "*(%Ga + %sb)", + }, + [OP_POPBI_FLD_v6p] = {"pop", "popbi.fld", + ev_ptr, ev_short, ev_field, + PROG_V6P_VERSION, + "*(%Ga + %sb)", + }, + [OP_POPBI_FN_v6p] = {"pop", "popbi.fn", + ev_ptr, ev_short, ev_func, + PROG_V6P_VERSION, + "*(%Ga + %sb)", + }, + [OP_POPBI_P_v6p] = {"pop", "popbi.p", + ev_ptr, ev_short, ev_ptr, + PROG_V6P_VERSION, + "*(%Ga + %sb)", + }, + [OP_POPBI_Q_v6p] = {"pop", "popbi.q", + ev_ptr, ev_short, ev_quaternion, + PROG_V6P_VERSION, + "*(%Ga + %sb)", + }, + [OP_POPBI_I_v6p] = {"pop", "popbi.i", + ev_ptr, ev_short, ev_int, + PROG_V6P_VERSION, + "*(%Ga + %sb)", + }, + [OP_POPBI_D_v6p] = {"pop", "popbi.d", + ev_ptr, ev_short, ev_double, + PROG_V6P_VERSION, + "*(%Ga + %sb)", + }, + + // end of table + [OP_MEMSETPI_v6p+1] = {0}, //XXX FIXME relies on OP_MEMSETPI_v6p being last +}; + +const v6p_opcode_t * +PR_v6p_Opcode (pr_ushort_t opcode) +{ + size_t opcode_count = sizeof (pr_v6p_opcodes) / sizeof (pr_v6p_opcodes[0]); + if (opcode >= opcode_count - 1) { + return 0; + } + return &pr_v6p_opcodes[opcode]; +} + +static inline void +check_branch (progs_t *pr, dstatement_t *st, const v6p_opcode_t *op, short offset) +{ + pr_int_t address = st - pr->pr_statements; + + address += offset; + if (address < 0 || (pr_uint_t) address >= pr->progs->statements.count) + PR_Error (pr, "PR_Check_Opcodes: invalid branch (statement %ld: %s)", + (long)(st - pr->pr_statements), op->opname); +} + +static int +is_vector_parameter_store (progs_t *pr, dstatement_t *st, + unsigned short operand) +{ + int i; + + if ((pr_opcode_v6p_e) st->op != OP_STORE_V_v6p) + return 0; + if (operand != st->a) + return 0; + for (i = 0; i < PR_MAX_PARAMS; i++) + if (st->b == pr->pr_params[i] - pr->pr_globals) + return 1; + return 0; +} + +#define ISDENORM(x) ((x) && !((x) & 0x7f800000)) + +static inline void +check_global (progs_t *pr, dstatement_t *st, const v6p_opcode_t *op, etype_t type, + unsigned short operand, int check_denorm) +{ + const char *msg; + pr_def_t *def; + + switch (type) { + case ev_short: + break; + case ev_invalid: + if (operand) { + msg = "non-zero global index in invalid operand"; + goto error; + } + break; + default: + if (operand + (unsigned) pr_type_size[type] + > pr->progs->globals.count) { + if (operand >= pr->progs->globals.count + || !is_vector_parameter_store (pr, st, operand)) { + msg = "out of bounds global index"; + goto error; + } + } + if (type != ev_float || !check_denorm) + break; + if (!ISDENORM (G_INT (pr, operand)) + || G_UINT(pr, operand) == 0x80000000) + break; + if ((def = PR_GlobalAtOfs (pr, operand)) + && (def->type & ~DEF_SAVEGLOBAL) != ev_float) { + // FTEqcc uses store.f parameters of most types :/ + break; + } + if (!pr->denorm_found) { + pr->denorm_found = 1; + if (pr_boundscheck) { + Sys_Printf ("DENORMAL floats detected, these progs might " + "not work. Good luck.\n"); + return; + } + msg = "DENORMAL float detected. These progs are probably " + "using qccx arrays and integers. If just simple arrays " + "are being used, then they should work, but if " + "internal.qc is used, they most definitely will NOT. To " + "allow these progs to be used, set pr_boundscheck to 1."; + goto error; + } + break; + } + return; +error: + PR_PrintStatement (pr, st, 0); + PR_Error (pr, "PR_Check_Opcodes: %s (statement %ld: %s)", msg, + (long)(st - pr->pr_statements), op->opname); +} + +static void +check_global_size (progs_t *pr, dstatement_t *st, const v6p_opcode_t *op, + unsigned short size, unsigned short operand) +{ + const char *msg; + if (operand + size > pr->progs->globals.count) { + msg = "out of bounds global index"; + goto error; + } + return; +error: + PR_PrintStatement (pr, st, 0); + PR_Error (pr, "PR_Check_Opcodes: %s (statement %ld: %s)", msg, + (long)(st - pr->pr_statements), op->opname); +} + +int +PR_Check_v6p_Opcodes (progs_t *pr) +{ + const v6p_opcode_t *op; + dstatement_t *st; + int state_ok = 0; + int pushpop_ok = 0; + pr_uint_t i; + + if (pr->globals.ftime && pr->globals.self && pr->fields.nextthink != -1 + && pr->fields.think != -1 && pr->fields.frame != -1) { + state_ok = 1; + } + if (pr->globals.stack) { + pushpop_ok = 1; + } + + //FIXME need to decide if I really want to always do static bounds checking + // the only problem is that it slows progs load a little, but it's the only + // way to check for qccx' evil + if (0 && !pr_boundscheck) { + for (i = 0, st = pr->pr_statements; i < pr->progs->statements.count; + st++, i++) { + pr_opcode_v6p_e st_op = (pr_opcode_v6p_e) st->op; + op = PR_v6p_Opcode (st_op); + if (!op) { + PR_Error (pr, "PR_Check_Opcodes: unknown opcode %d at " + "statement %ld", st_op, + (long)(st - pr->pr_statements)); + } + if ((st_op == OP_STATE_v6p || st_op == OP_STATE_F_v6p) + && !state_ok) { + PR_Error (pr, "PR_Check_Opcodes: %s used with missing fields " + "or globals", op->opname); + } + if ((strequal(op->name, "push") || strequal(op->name, "pop")) + && !pushpop_ok) { + PR_Error (pr, "PR_Check_Opcodes: %s used with missing .stack " + "globals", op->opname); + } + } + } else { + for (i = 0, st = pr->pr_statements; i < pr->progs->statements.count; + st++, i++) { + pr_opcode_v6p_e st_op = (pr_opcode_v6p_e) st->op; + op = PR_v6p_Opcode (st_op); + if (!op) { + PR_Error (pr, "PR_Check_Opcodes: unknown opcode %d at " + "statement %ld", st_op, + (long)(st - pr->pr_statements)); + } + switch (st_op) { + case OP_IF_v6p: + case OP_IFNOT_v6p: + check_global (pr, st, op, op->type_a, st->a, 1); + check_branch (pr, st, op, st->b); + break; + case OP_GOTO_v6p: + check_branch (pr, st, op, st->a); + break; + case OP_DONE_v6p: + case OP_RETURN_v6p: + check_global (pr, st, op, ev_int, st->a, 1); + check_global (pr, st, op, ev_void, st->b, 0); + check_global (pr, st, op, ev_void, st->c, 0); + break; + case OP_RCALL1_v6p: + check_global (pr, st, op, ev_void, st->c, 1); + case OP_RCALL2_v6p: + case OP_RCALL3_v6p: + case OP_RCALL4_v6p: + case OP_RCALL5_v6p: + case OP_RCALL6_v6p: + case OP_RCALL7_v6p: + case OP_RCALL8_v6p: + if (st_op > OP_RCALL1_v6p) + check_global (pr, st, op, ev_int, st->c, 1); + check_global (pr, st, op, ev_int, st->b, 1); + check_global (pr, st, op, ev_func, st->a, 1); + break; + case OP_STATE_v6p: + case OP_STATE_F_v6p: + if (!state_ok) { + PR_Error (pr, "PR_Check_Opcodes: %s used with missing " + "fields or globals", op->opname); + } + check_global (pr, st, op, op->type_a, st->a, 1); + check_global (pr, st, op, op->type_b, st->b, 1); + check_global (pr, st, op, op->type_c, st->c, 1); + break; + case OP_MOVEI_v6p: + check_global_size (pr, st, op, st->b, st->a); + check_global_size (pr, st, op, st->b, st->c); + break; + case OP_MEMSETI_v6p: + check_global_size (pr, st, op, st->b, st->c); + break; + case OP_PUSHB_F_v6p: + case OP_PUSHB_S_v6p: + case OP_PUSHB_ENT_v6p: + case OP_PUSHB_FLD_v6p: + case OP_PUSHB_FN_v6p: + case OP_PUSHB_I_v6p: + case OP_PUSHB_P_v6p: + case OP_PUSHB_V_v6p: + case OP_PUSHB_Q_v6p: + case OP_PUSHBI_F_v6p: + case OP_PUSHBI_S_v6p: + case OP_PUSHBI_ENT_v6p: + case OP_PUSHBI_FLD_v6p: + case OP_PUSHBI_FN_v6p: + case OP_PUSHBI_I_v6p: + case OP_PUSHBI_P_v6p: + case OP_PUSHBI_V_v6p: + case OP_PUSHBI_Q_v6p: + // op->type_c is used for selecting the operator during + // compilation, but is invalid when running + check_global (pr, st, op, op->type_a, st->a, 1); + check_global (pr, st, op, op->type_b, st->b, 1); + check_global (pr, st, op, ev_invalid, st->c, 1); + break; + case OP_POP_F_v6p: + case OP_POP_FLD_v6p: + case OP_POP_ENT_v6p: + case OP_POP_S_v6p: + case OP_POP_FN_v6p: + case OP_POP_I_v6p: + case OP_POP_P_v6p: + case OP_POP_V_v6p: + case OP_POP_Q_v6p: + // don't want to check for denormal floats, otherwise + // OP_POP__v6p* could use the defualt rule + check_global (pr, st, op, op->type_a, st->a, 0); + check_global (pr, st, op, ev_invalid, st->b, 1); + check_global (pr, st, op, ev_invalid, st->c, 1); + break; + case OP_POPB_F_v6p: + case OP_POPB_S_v6p: + case OP_POPB_ENT_v6p: + case OP_POPB_FLD_v6p: + case OP_POPB_FN_v6p: + case OP_POPB_I_v6p: + case OP_POPB_P_v6p: + case OP_POPB_V_v6p: + case OP_POPB_Q_v6p: + case OP_POPBI_F_v6p: + case OP_POPBI_S_v6p: + case OP_POPBI_ENT_v6p: + case OP_POPBI_FLD_v6p: + case OP_POPBI_FN_v6p: + case OP_POPBI_I_v6p: + case OP_POPBI_P_v6p: + case OP_POPBI_V_v6p: + case OP_POPBI_Q_v6p: + // op->type_c is used for selecting the operator during + // compilation, but is invalid when running + check_global (pr, st, op, op->type_a, st->a, 1); + check_global (pr, st, op, op->type_b, st->b, 1); + check_global (pr, st, op, ev_invalid, st->c, 1); + break; + default: + check_global (pr, st, op, op->type_a, st->a, 1); + check_global (pr, st, op, op->type_b, st->b, + (op - pr_v6p_opcodes) != OP_STORE_F_v6p); + check_global (pr, st, op, op->type_c, st->c, 0); + break; + } + } + } + return 1; +} diff --git a/libs/gamecode/pr_zone.c b/libs/gamecode/pr_zone.c index e7239bc0c..44f527ffe 100644 --- a/libs/gamecode/pr_zone.c +++ b/libs/gamecode/pr_zone.c @@ -49,6 +49,7 @@ static void pr_zone_error (void *_pr, const char *msg) { progs_t *pr = (progs_t *) _pr; + Z_Print (pr->zone); PR_RunError (pr, "%s", msg); } @@ -63,17 +64,35 @@ PR_Zone_Init (progs_t *pr) VISIBLE void PR_Zone_Free (progs_t *pr, void *ptr) { - Z_Free (pr->zone, ptr); + if (ptr) { + Z_Free (pr->zone, ptr); + } } VISIBLE void * PR_Zone_Malloc (progs_t *pr, pr_int_t size) { - if (size <= 0) - PR_RunError (pr, "attempt to allocate less than 1 byte"); + if (!size) { + return 0; + } + if (size < 0) { + PR_RunError (pr, "attempt to allocate less than 0 bytes"); + } return Z_Malloc (pr->zone, size); } +VISIBLE void * +PR_Zone_TagMalloc (progs_t *pr, int size, int tag) +{ + if (!size) { + return 0; + } + if (size < 0) { + PR_RunError (pr, "attempt to allocate less than 0 bytes"); + } + return Z_TagMalloc (pr->zone, size, tag); +} + VISIBLE void * PR_Zone_Realloc (progs_t *pr, void *ptr, pr_int_t size) { @@ -81,7 +100,11 @@ PR_Zone_Realloc (progs_t *pr, void *ptr, pr_int_t size) Z_Free (pr->zone, ptr); return 0; } - if (size <= 0) - PR_RunError (pr, "attempt to allocate less than 1 byte"); + if (!size) { + return 0; + } + if (size < 0) { + PR_RunError (pr, "attempt to allocate less than 0 bytes"); + } return Z_Realloc (pr->zone, ptr, size); } diff --git a/libs/gamecode/swizzle.py b/libs/gamecode/swizzle.py new file mode 100644 index 000000000..59640ea66 --- /dev/null +++ b/libs/gamecode/swizzle.py @@ -0,0 +1,187 @@ +def iter(func): + for i in range(4): + for j in range(4): + for k in range(4): + for l in range(4): + func(i, j, k, l) + +def iter16(func): + for i in range(16): + func(i) + +import sys + +coord=['x', 'y', 'z', 'w'] +def label(i, j, k, l): + return f"swizzle_{coord[l]}{coord[k]}{coord[j]}{coord[i]}" + +def print_ref(i, j, k, l): + print(f"\t\t&&{label(i, j, k, l)},") + +def print_op(i, j, k, l): + print(f"\t{label(i, j, k, l)}: vec = ({optype}) {{ vec[{l}], vec[{k}], vec[{j}], vec[{i}] }}; goto negate;") + +def print_data(i, j, k, l): + print(f"\t{{ {l+1:2}, {k+1:2}, {j+1:2}, {i+1:2} }},") + +def print_swizzle_f(i, j, k, l): + swiz = i * 64 + j * 16 + k * 4 + l + addr = (swiz + 1) * 4 + print(f"\t{{ OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x{swiz:04x}, {addr} }},") + +def print_neg_f(i): + swiz = i * 0x100 + 0xe4 + addr = (i + 1) * 4 + print(f"\t{{ OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x{swiz:04x}, {addr} }},") + +def print_zero_f(i): + swiz = i * 0x1000 + 0xe4 + addr = (i + 1) * 4 + print(f"\t{{ OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x{swiz:04x}, {addr} }},") + +def print_swizzle_d(i, j, k, l): + swiz = i * 64 + j * 16 + k * 4 + l + addr = (swiz + 1) * 8 + print(f"\t{{ OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x{swiz:04x}, {addr} }},") + +def print_neg_d(i): + swiz = i * 0x100 + 0xe4 + addr = (i + 1) * 8 + print(f"\t{{ OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x{swiz:04x}, {addr} }},") + +def print_zero_d(i): + swiz = i * 0x1000 + 0xe4 + addr = (i + 1) * 8 + print(f"\t{{ OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x{swiz:04x}, {addr} }},") + +def print_eights(i, j, k, l): + print(f"\t{{ {8:2}, {8:2}, {8:2}, {8:2} }},") + +def print_nines(i): + print(f"\t{{ {9:2}, {9:2}, {9:2}, {9:2} }},") + +def print_neg(n): + x = [1, 2, 3, 4] + for i in range(4): + if n & (1<< i): + x[i] = -x[i] + print(f"\t{{ {x[0]:2}, {x[1]:2}, {x[2]:2}, {x[3]:2} }},") + +def print_zero(z): + x = [1, 2, 3, 4] + for i in range(4): + if z & (1<< i): + x[i] = 0 + print(f"\t{{ {x[0]:2}, {x[1]:2}, {x[2]:2}, {x[3]:2} }},") + +types = ["f", "d"] +tests = ["swizzle", "neg", "zero"] + +if sys.argv[1] == "case": + if sys.argv[2] == "int": + optype = "pr_ivec4_t" + elif sys.argv[2] == "long": + optype = "pr_lvec4_t" + iter(print_op) + print("\tstatic void *swizzle_table[256] = {") + iter(print_ref) + print("\t};") +elif sys.argv[1] == "test": + print('#include "head.c"') + print() + print("static pr_vec4_t swizzle_f_init[] = {") + print_data(3, 2, 1, 0) + iter(print_eights) + print("};") + print("static pr_vec4_t swizzle_f_expect[] = {") + print_data(3, 2, 1, 0) + iter(print_data) + print("};") + print() + print("static dstatement_t swizzle_f_statements[] = {") + iter(print_swizzle_f) + print("};") + print() + print("static pr_vec4_t neg_f_init[] = {") + print_neg(0) + iter16(print_nines) + print("};") + print() + print("static pr_vec4_t neg_f_expect[] = {") + print_neg(0) + iter16(print_neg) + print("};") + print() + print("static dstatement_t neg_f_statements[] = {") + iter16(print_neg_f) + print("};") + print() + print("static pr_vec4_t zero_f_init[] = {") + print_zero(0) + iter16(print_nines) + print("};") + print() + print("static pr_vec4_t zero_f_expect[] = {") + print_zero(0) + iter16(print_zero) + print("};") + print() + print("static dstatement_t zero_f_statements[] = {") + iter16(print_zero_f) + print("};") + print() + print("static pr_dvec4_t swizzle_d_init[] = {") + print_data(3, 2, 1, 0) + iter(print_eights) + print("};") + print("static pr_dvec4_t swizzle_d_expect[] = {") + print_data(3, 2, 1, 0) + iter(print_data) + print("};") + print() + print("static dstatement_t swizzle_d_statements[] = {") + iter(print_swizzle_d) + print("};") + print() + print("static pr_dvec4_t neg_d_init[] = {") + print_neg(0) + iter16(print_nines) + print("};") + print() + print("static pr_dvec4_t neg_d_expect[] = {") + print_neg(0) + iter16(print_neg) + print("};") + print() + print("static dstatement_t neg_d_statements[] = {") + iter16(print_neg_d) + print("};") + print() + print("static pr_dvec4_t zero_d_init[] = {") + print_zero(0) + iter16(print_nines) + print("};") + print() + print("static pr_dvec4_t zero_d_expect[] = {") + print_zero(0) + iter16(print_zero) + print("};") + print() + print("static dstatement_t zero_d_statements[] = {") + iter16(print_zero_d) + print("};") + print() + print("test_t tests[] = {") + for t in types: + for o in tests: + print("\t{") + print(f'\t\t.desc = "{o} {t}",') + print(f"\t\t.num_globals = num_globals({o}_{t}_init,{o}_{t}_expect),") + print(f"\t\t.num_statements = num_statements({o}_{t}_statements),") + print(f"\t\t.statements = {o}_{t}_statements,") + print(f"\t\t.init_globals = (pr_int_t *) {o}_{t}_init,") + print(f"\t\t.expect_globals = (pr_int_t *) {o}_{t}_expect,") + print("\t},") + print("};") + print() + print('#include "main.c"') diff --git a/libs/gamecode/test/Makemodule.am b/libs/gamecode/test/Makemodule.am new file mode 100644 index 000000000..dfccd91ae --- /dev/null +++ b/libs/gamecode/test/Makemodule.am @@ -0,0 +1,202 @@ +libs_gamecode_tests = \ + libs/gamecode/test/test-branch \ + libs/gamecode/test/test-bitops \ + libs/gamecode/test/test-callret \ + libs/gamecode/test/test-conv0 \ + libs/gamecode/test/test-conv1 \ + libs/gamecode/test/test-conv2 \ + libs/gamecode/test/test-conv3 \ + libs/gamecode/test/test-conv4 \ + libs/gamecode/test/test-conv5 \ + libs/gamecode/test/test-conv6 \ + libs/gamecode/test/test-conv7 \ + libs/gamecode/test/test-double \ + libs/gamecode/test/test-extend \ + libs/gamecode/test/test-float \ + libs/gamecode/test/test-hops \ + libs/gamecode/test/test-int \ + libs/gamecode/test/test-jump \ + libs/gamecode/test/test-lea \ + libs/gamecode/test/test-load \ + libs/gamecode/test/test-load64 \ + libs/gamecode/test/test-long \ + libs/gamecode/test/test-mem \ + libs/gamecode/test/test-scale \ + libs/gamecode/test/test-stack \ + libs/gamecode/test/test-state \ + libs/gamecode/test/test-store \ + libs/gamecode/test/test-store64 \ + libs/gamecode/test/test-string \ + libs/gamecode/test/test-swizzle \ + libs/gamecode/test/test-unsigned \ + libs/gamecode/test/test-vector \ + libs/gamecode/test/test-with + +TESTS += $(libs_gamecode_tests) + +check_PROGRAMS += $(libs_gamecode_tests) + +EXTRA_DIST += libs/gamecode/test/head.c libs/gamecode/test/main.c + +test_gamecode_libs= \ + libs/gamecode/libQFgamecode.la \ + libs/util/libQFutil.la + +libs_gamecode_test_test_branch_SOURCES= \ + libs/gamecode/test/test-branch.c +libs_gamecode_test_test_branch_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_branch_DEPENDENCIES=$(test_gamecode_libs) + +libs_gamecode_test_test_bitops_SOURCES= \ + libs/gamecode/test/test-bitops.c +libs_gamecode_test_test_bitops_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_bitops_DEPENDENCIES=$(test_gamecode_libs) + +libs_gamecode_test_test_callret_SOURCES= \ + libs/gamecode/test/test-callret.c +libs_gamecode_test_test_callret_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_callret_DEPENDENCIES=$(test_gamecode_libs) + +libs_gamecode_test_test_conv0_SOURCES= \ + libs/gamecode/test/test-conv0.c +libs_gamecode_test_test_conv0_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_conv0_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_conv1_SOURCES= \ + libs/gamecode/test/test-conv1.c +libs_gamecode_test_test_conv1_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_conv1_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_conv2_SOURCES= \ + libs/gamecode/test/test-conv2.c +libs_gamecode_test_test_conv2_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_conv2_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_conv3_SOURCES= \ + libs/gamecode/test/test-conv3.c +libs_gamecode_test_test_conv3_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_conv3_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_conv4_SOURCES= \ + libs/gamecode/test/test-conv4.c +libs_gamecode_test_test_conv4_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_conv4_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_conv5_SOURCES= \ + libs/gamecode/test/test-conv5.c +libs_gamecode_test_test_conv5_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_conv5_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_conv6_SOURCES= \ + libs/gamecode/test/test-conv6.c +libs_gamecode_test_test_conv6_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_conv6_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_conv7_SOURCES= \ + libs/gamecode/test/test-conv7.c +libs_gamecode_test_test_conv7_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_conv7_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_double_SOURCES= \ + libs/gamecode/test/test-double.c +libs_gamecode_test_test_double_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_double_DEPENDENCIES=$(test_gamecode_libs) + +libs_gamecode_test_test_extend_SOURCES= \ + libs/gamecode/test/test-extend.c +libs_gamecode_test_test_extend_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_extend_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_float_SOURCES= \ + libs/gamecode/test/test-float.c +libs_gamecode_test_test_float_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_float_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_hops_SOURCES= \ + libs/gamecode/test/test-hops.c +libs_gamecode_test_test_hops_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_hops_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_int_SOURCES= \ + libs/gamecode/test/test-int.c +libs_gamecode_test_test_int_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_int_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_jump_SOURCES= \ + libs/gamecode/test/test-jump.c +libs_gamecode_test_test_jump_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_jump_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_lea_SOURCES= \ + libs/gamecode/test/test-lea.c +libs_gamecode_test_test_lea_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_lea_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_load_SOURCES= \ + libs/gamecode/test/test-load.c +libs_gamecode_test_test_load_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_load_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_load64_SOURCES= \ + libs/gamecode/test/test-load64.c +libs_gamecode_test_test_load64_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_load64_DEPENDENCIES=$(test_gamecode_libs) + +libs_gamecode_test_test_long_SOURCES= \ + libs/gamecode/test/test-long.c +libs_gamecode_test_test_long_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_long_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_mem_SOURCES= \ + libs/gamecode/test/test-mem.c +libs_gamecode_test_test_mem_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_mem_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_scale_SOURCES= \ + libs/gamecode/test/test-scale.c +libs_gamecode_test_test_scale_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_scale_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_stack_SOURCES= \ + libs/gamecode/test/test-stack.c +libs_gamecode_test_test_stack_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_stack_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_state_SOURCES= \ + libs/gamecode/test/test-state.c +libs_gamecode_test_test_state_LDADD= $(test_gamecode_libs) + +libs_gamecode_test_test_store_SOURCES= \ + libs/gamecode/test/test-store.c +libs_gamecode_test_test_store_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_store_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_store64_SOURCES= \ + libs/gamecode/test/test-store64.c +libs_gamecode_test_test_store64_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_store64_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_string_SOURCES= \ + libs/gamecode/test/test-string.c +libs_gamecode_test_test_string_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_string_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_swizzle_SOURCES= \ + libs/gamecode/test/test-swizzle.c +libs_gamecode_test_test_swizzle_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_swizzle_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_unsigned_SOURCES= \ + libs/gamecode/test/test-unsigned.c +libs_gamecode_test_test_unsigned_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_unsigned_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_vector_SOURCES= \ + libs/gamecode/test/test-vector.c +libs_gamecode_test_test_vector_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_vector_DEPENDENCIES= $(test_gamecode_libs) + +libs_gamecode_test_test_with_SOURCES= \ + libs/gamecode/test/test-with.c +libs_gamecode_test_test_with_LDADD= $(test_gamecode_libs) +libs_gamecode_test_test_with_DEPENDENCIES= $(test_gamecode_libs) diff --git a/libs/gamecode/test/head.c b/libs/gamecode/test/head.c new file mode 100644 index 000000000..78410dfe5 --- /dev/null +++ b/libs/gamecode/test/head.c @@ -0,0 +1,54 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/progs.h" + +static int verbose = 0; + +// both calculates the number of globals in the test, and ensures that both +// init and expect are the same size (will product a "void value not ignored" +// error if the sizes differ) +#define num_globals(init, expect) \ + __builtin_choose_expr ( \ + sizeof (init) == sizeof (expect), \ + (sizeof (init) / sizeof (init[0])) \ + * (sizeof (init[0]) / sizeof (pr_type_t)), \ + (void) 0\ + ) + +// calculate the numver of statements in the test +#define num_statements(statements) \ + (sizeof (statements) / sizeof (statements[0])) + +#define num_functions(functions) \ + (sizeof (functions) / sizeof (functions[0])) + +#define BASE(b, base) (((base) & 3) << OP_##b##_SHIFT) +#define OP(a, b, c, op) ((op) | BASE(A, a) | BASE(B, b) | BASE(C, c)) + +typedef struct { + const char *desc; + pr_ptr_t edict_area; + pr_uint_t stack_size; + pr_uint_t extra_globals; + pr_uint_t num_globals; + pr_uint_t num_statements; + dstatement_t *statements; + pr_int_t *init_globals; + pr_int_t *expect_globals; + const char *strings; + pr_uint_t string_size; + // pointers/globals for state + double *double_time; + pr_uint_t dtime; + float *float_time; + pr_uint_t ftime; + pr_uint_t self; + // fields for state + pr_uint_t think; + pr_uint_t nextthink; + pr_uint_t frame; + bfunction_t *functions; + pr_uint_t num_functions; +} test_t; diff --git a/libs/gamecode/test/main.c b/libs/gamecode/test/main.c new file mode 100644 index 000000000..4d4c31d1e --- /dev/null +++ b/libs/gamecode/test/main.c @@ -0,0 +1,254 @@ +#include +#include +#include +#include + +#include "QF/va.h" + +#include "QF/simd/types.h" + +#define num_tests (sizeof (tests) / sizeof (tests[0])) +static int test_enabled[num_tests] = { 0 }; + +#include "getopt.h" + +#include "QF/cmd.h" +#include "QF/cvar.h" + +static bfunction_t test_functions[] = { + {}, // null function + { .first_statement = 0 } +}; +static dprograms_t test_progs = { + .version = PROG_VERSION, +}; +static progs_t test_pr; + +static jmp_buf jump_buffer; + +static void +test_debug_handler (prdebug_t event, void *param, void *data) +{ + progs_t *pr = data; + + switch (event) { + case prd_breakpoint: + if (verbose > 0) { + printf ("debug: %s\n", prdebug_names[event]); + } + longjmp (jump_buffer, 1); + case prd_subenter: + if (verbose > 0) { + printf ("debug: subenter %d\n", *(pr_func_t *) param); + } + case prd_subexit: + break; + case prd_trace: + dstatement_t *st = test_pr.pr_statements + test_pr.pr_xstatement; + if (verbose > 1) { + printf ("---\n"); + printf ("debug: trace %05x %04x %04x %04x %04x%s\n", + test_pr.pr_xstatement, st->op, st->a, st->b, st->c, + pr->globals.stack ? va (0, " %05x", *pr->globals.stack) + : ""); + printf (" %04x %04x %04x\n", + st->a + PR_BASE (pr, st, A), + st->b + PR_BASE (pr, st, B), + st->c + PR_BASE (pr, st, C)); + } + if (verbose > 0) { + PR_PrintStatement (&test_pr, st, 0); + } + if (pr->globals.stack) { + if (*pr->globals.stack & 3) { + printf ("stack not aligned: %d\n", *pr->globals.stack); + longjmp (jump_buffer, 3); + } + } + break; + case prd_runerror: + printf ("debug: %s: %s\n", prdebug_names[event], (char *)param); + longjmp (jump_buffer, 3); + case prd_watchpoint: + case prd_begin: + case prd_terminate: + case prd_error: + case prd_none: + printf ("debug: unexpected:%s %p\n", prdebug_names[event], param); + longjmp (jump_buffer, 2); + } +} + +static void +setup_test (test_t *test) +{ + memset (&test_pr, 0, sizeof (test_pr)); + PR_Init (&test_pr); + PR_Debug_Init (&test_pr); + test_pr.progs = &test_progs; + test_pr.debug_handler = test_debug_handler; + test_pr.debug_data = &test_pr; + test_pr.pr_trace = 1; + test_pr.pr_trace_depth = -1; + if (test->num_functions && test->functions) { + test_pr.function_table = calloc ((test->num_functions + 2), + sizeof (bfunction_t)); + memcpy (test_pr.function_table, test_functions, + 2 * sizeof (bfunction_t)); + memcpy (test_pr.function_table + 2, test->functions, + test->num_functions * sizeof (bfunction_t)); + } else { + test_pr.function_table = test_functions; + } + + pr_uint_t num_globals = test->num_globals; + num_globals += test->extra_globals + test->stack_size; + + test_pr.globals_size = num_globals; + test_pr.pr_globals = Sys_Alloc (num_globals * sizeof (pr_type_t)); + memcpy (test_pr.pr_globals, test->init_globals, + test->num_globals * sizeof (pr_type_t)); + memset (test_pr.pr_globals + test->num_globals, 0, + test->extra_globals * sizeof (pr_type_t)); + if (test->stack_size) { + pr_ptr_t stack = num_globals - test->stack_size; + test_pr.stack_bottom = stack + 4; + test_pr.globals.stack = (pr_ptr_t *) (test_pr.pr_globals + stack); + *test_pr.globals.stack = num_globals; + } + if (test->edict_area) { + test_pr.pr_edict_area = test_pr.pr_globals + test->edict_area; + } + if (test->double_time || test->float_time) { + test_pr.fields.nextthink = test->nextthink; + test_pr.fields.frame = test->frame; + test_pr.fields.think = test->think; + test_pr.globals.self = (pr_uint_t *) &test_pr.pr_globals[test->self]; + if (test->double_time) { + test_pr.globals.dtime = (double *)&test_pr.pr_globals[test->dtime]; + *test_pr.globals.dtime = *test->double_time; + } + if (test->float_time) { + test_pr.globals.ftime = (float *) &test_pr.pr_globals[test->ftime]; + *test_pr.globals.ftime = *test->float_time; + } + } + + test_progs.statements.count = test->num_statements + 1; + test_pr.pr_statements + = malloc ((test->num_statements + 1) * sizeof (dstatement_t)); + memcpy (test_pr.pr_statements, test->statements, + (test->num_statements + 1) * sizeof (dstatement_t)); + test_pr.pr_statements[test->num_statements] = + (dstatement_t) { OP_BREAK, 0, 0, 0 }; + + test_pr.pr_strings = (char *) test->strings; + test_pr.pr_stringsize = test->string_size; +} + +static int +check_result (test_t *test) +{ + int ret = 0; + + if (memcmp (test_pr.pr_globals, test->expect_globals, + test->num_globals * sizeof (pr_int_t)) == 0) { + ret = 1; + printf ("test #%zd: %s: OK\n", test - tests, test->desc); + } else { + printf ("test #%zd: %s: words differ\n", test - tests, test->desc); + for (pr_uint_t i = 0; i < test->num_globals; i += 4) { + pr_ivec4_t *a = (pr_ivec4_t *) &test->expect_globals[i]; + pr_ivec4_t *b = (pr_ivec4_t *) &test_pr.pr_globals[i]; + if (memcmp (a, b, sizeof (pr_ivec4_t))) { + printf ("-%4x { %8x, %8x, %8x, %8x }\n", i, VEC4_EXP (*a)); + printf ("+%4x { %8x, %8x, %8x, %8x }\n", i, VEC4_EXP (*b)); + } + } + } + return ret; +} + +static int +run_test (test_t *test) +{ + int jump_ret; + int ret = 0; + + setup_test (test); + + if (!(jump_ret = setjmp (jump_buffer))) { + PR_ExecuteProgram (&test_pr, 1); + printf ("returned from progs\n"); + } + if (jump_ret == 1) { + ret = check_result (test); + } else { + printf ("test #%zd: %s: critical failure\n", test - tests, test->desc); + } + + pr_uint_t num_globals = test->num_globals; + num_globals += test->extra_globals + test->stack_size; + if (test->num_functions && test->functions) { + free (test_pr.function_table); + } + Sys_Free (test_pr.pr_globals, num_globals * sizeof (pr_type_t)); + + free (test_pr.pr_statements); + return ret; +} + +int +main (int argc, char **argv) +{ + int c; + size_t i, test; + int pass = 1; + + Cmd_Init_Hash (); + Cvar_Init_Hash (); + Cmd_Init (); + Cvar_Init (); + PR_Init_Cvars (); + pr_boundscheck = 1; + + while ((c = getopt (argc, argv, "qvt:")) != EOF) { + switch (c) { + case 'q': + verbose--; + break; + case 'v': + verbose++; + break; + case 't': + test = atoi (optarg); + if (test < num_tests) { + test_enabled[test] = 1; + } else { + fprintf (stderr, "Bad test number (0 - %zd)\n", num_tests); + return 1; + } + break; + default: + fprintf (stderr, "-q (quiet) -v (verbose) and/or -t TEST " + "(test number)\n"); + return 1; + } + } + + for (i = 0; i < num_tests; i++) + if (test_enabled[i]) + break; + if (i == num_tests) { + for (i = 0; i < num_tests; i++) + test_enabled[i] = 1; + } + + for (i = 0; i < num_tests; i++) { + if (!test_enabled[i]) + continue; + pass &= run_test (&tests[i]); + } + + return !pass; +} diff --git a/libs/gamecode/test/test-bitops.c b/libs/gamecode/test/test-bitops.c new file mode 100644 index 000000000..b692223d7 --- /dev/null +++ b/libs/gamecode/test/test-bitops.c @@ -0,0 +1,260 @@ +#include "head.c" + +#include "QF/mathlib.h" + +static pr_ivec4_t int_bitop_init[] = { + { 5, -5, 5, -5}, + { 5, 5, -5, -5}, + + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +static pr_ivec4_t int_bitop_expect[] = { + { 5, -5, 5, -5}, + { 5, 5, -5, -5}, + + { 5, 1, 1, -5}, + { 5, -1, -1, -5}, + { 0, -2, -2, 0}, + { -6, 4, -6, 4}, +}; + +static dstatement_t int_bitop_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 24 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 24, -1, 24 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 24 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 24, 1 }, + + { OP(1, 1, 1, OP_BITAND_I_1), 0, 4, 8}, + { OP(1, 1, 1, OP_BITOR_I_1), 0, 4, 12}, + { OP(1, 1, 1, OP_BITXOR_I_1), 0, 4, 16}, + { OP(1, 1, 1, OP_BITNOT_I_1), 0, 4, 20}, + + { OP(0, 0, 0, OP_JUMP_A), -8, 0, 0 }, +}; + +static dstatement_t int_bitop_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 24 }, // index +//loop: + { OP(0, 0, 0, OP_LEA_C), 24, -2, 24 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 24 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 24, 1 }, + + { OP(1, 1, 1, OP_BITAND_I_2), 0, 4, 8}, + { OP(1, 1, 1, OP_BITOR_I_2), 0, 4, 12}, + { OP(1, 1, 1, OP_BITXOR_I_2), 0, 4, 16}, + { OP(1, 1, 1, OP_BITNOT_I_2), 0, 4, 20}, + + { OP(0, 0, 0, OP_JUMP_A), -8, 0, 0 }, +}; + +static dstatement_t int_bitop_3a_statements[] = { + { OP(1, 1, 1, OP_BITAND_I_3), 0, 4, 8}, + { OP(1, 1, 1, OP_BITAND_I_1), 3, 7, 11}, + { OP(1, 1, 1, OP_BITOR_I_3), 0, 4, 12}, + { OP(1, 1, 1, OP_BITOR_I_1), 3, 7, 15}, + { OP(1, 1, 1, OP_BITXOR_I_3), 0, 4, 16}, + { OP(1, 1, 1, OP_BITXOR_I_1), 3, 7, 19}, + { OP(1, 1, 1, OP_BITNOT_I_3), 0, 4, 20}, + { OP(1, 1, 1, OP_BITNOT_I_1), 3, 7, 23}, +}; + +static dstatement_t int_bitop_3b_statements[] = { + { OP(1, 1, 1, OP_BITAND_I_1), 0, 4, 8}, + { OP(1, 1, 1, OP_BITAND_I_3), 1, 5, 9}, + { OP(1, 1, 1, OP_BITOR_I_1), 0, 4, 12}, + { OP(1, 1, 1, OP_BITOR_I_3), 1, 5, 13}, + { OP(1, 1, 1, OP_BITXOR_I_1), 0, 4, 16}, + { OP(1, 1, 1, OP_BITXOR_I_3), 1, 5, 17}, + { OP(1, 1, 1, OP_BITNOT_I_1), 0, 4, 20}, + { OP(1, 1, 1, OP_BITNOT_I_3), 1, 5, 21}, +}; + +static dstatement_t int_bitop_4_statements[] = { + { OP(1, 1, 1, OP_BITAND_I_4), 0, 4, 8}, + { OP(1, 1, 1, OP_BITOR_I_4), 0, 4, 12}, + { OP(1, 1, 1, OP_BITXOR_I_4), 0, 4, 16}, + { OP(1, 1, 1, OP_BITNOT_I_4), 0, 4, 20}, +}; + +static pr_lvec4_t long_bitop_init[] = { + { 5, -5, 5, -5}, + { 5, 5, -5, -5}, + + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +static pr_lvec4_t long_bitop_expect[] = { + { 5, -5, 5, -5}, + { 5, 5, -5, -5}, + + { 5, 1, 1, -5}, + { 5, -1, -1, -5}, + { 0, -2, -2, 0}, + { -6, 4, -6, 4}, +}; + +static dstatement_t long_bitop_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 8, 0, 48 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 48, -2, 48 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 48 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 48, 1 }, + + { OP(1, 1, 1, OP_BITAND_L_1), 0, 8, 16}, + { OP(1, 1, 1, OP_BITOR_L_1), 0, 8, 24}, + { OP(1, 1, 1, OP_BITXOR_L_1), 0, 8, 32}, + { OP(1, 1, 1, OP_BITNOT_L_1), 0, 8, 40}, + + { OP(0, 0, 0, OP_JUMP_A), -8, 0, 0 }, +}; + +static dstatement_t long_bitop_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 8, 0, 48 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 48, -4, 48 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 48 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 48, 1 }, + + { OP(1, 1, 1, OP_BITAND_L_2), 0, 8, 16}, + { OP(1, 1, 1, OP_BITOR_L_2), 0, 8, 24}, + { OP(1, 1, 1, OP_BITXOR_L_2), 0, 8, 32}, + { OP(1, 1, 1, OP_BITNOT_L_2), 0, 8, 40}, + + { OP(0, 0, 0, OP_JUMP_A), -8, 0, 0 }, +}; + +static dstatement_t long_bitop_3a_statements[] = { + { OP(1, 1, 1, OP_BITAND_L_3), 0, 8, 16}, + { OP(1, 1, 1, OP_BITAND_L_1), 6, 14, 22}, + { OP(1, 1, 1, OP_BITOR_L_3), 0, 8, 24}, + { OP(1, 1, 1, OP_BITOR_L_1), 6, 14, 30}, + { OP(1, 1, 1, OP_BITXOR_L_3), 0, 8, 32}, + { OP(1, 1, 1, OP_BITXOR_L_1), 6, 14, 38}, + { OP(1, 1, 1, OP_BITNOT_L_3), 0, 8, 40}, + { OP(1, 1, 1, OP_BITNOT_L_1), 6, 14, 46}, +}; + +static dstatement_t long_bitop_3b_statements[] = { + { OP(1, 1, 1, OP_BITAND_L_1), 0, 8, 16}, + { OP(1, 1, 1, OP_BITAND_L_3), 2, 10, 18}, + { OP(1, 1, 1, OP_BITOR_L_1), 0, 8, 24}, + { OP(1, 1, 1, OP_BITOR_L_3), 2, 10, 26}, + { OP(1, 1, 1, OP_BITXOR_L_1), 0, 8, 32}, + { OP(1, 1, 1, OP_BITXOR_L_3), 2, 10, 34}, + { OP(1, 1, 1, OP_BITNOT_L_1), 0, 8, 40}, + { OP(1, 1, 1, OP_BITNOT_L_3), 2, 10, 42}, +}; + +static dstatement_t long_bitop_4_statements[] = { + { OP(1, 1, 1, OP_BITAND_L_4), 0, 8, 16}, + { OP(1, 1, 1, OP_BITOR_L_4), 0, 8, 24}, + { OP(1, 1, 1, OP_BITXOR_L_4), 0, 8, 32}, + { OP(1, 1, 1, OP_BITNOT_L_4), 0, 8, 40}, +}; + +test_t tests[] = { + { + .desc = "int bitop 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(int_bitop_init,int_bitop_expect), + .num_statements = num_statements (int_bitop_1_statements), + .statements = int_bitop_1_statements, + .init_globals = (pr_int_t *) int_bitop_init, + .expect_globals = (pr_int_t *) int_bitop_expect, + }, + { + .desc = "int bitop 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(int_bitop_init,int_bitop_expect), + .num_statements = num_statements (int_bitop_2_statements), + .statements = int_bitop_2_statements, + .init_globals = (pr_int_t *) int_bitop_init, + .expect_globals = (pr_int_t *) int_bitop_expect, + }, + { + .desc = "int bitop 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(int_bitop_init,int_bitop_expect), + .num_statements = num_statements (int_bitop_3a_statements), + .statements = int_bitop_3a_statements, + .init_globals = (pr_int_t *) int_bitop_init, + .expect_globals = (pr_int_t *) int_bitop_expect, + }, + { + .desc = "int bitop 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(int_bitop_init,int_bitop_expect), + .num_statements = num_statements (int_bitop_3b_statements), + .statements = int_bitop_3b_statements, + .init_globals = (pr_int_t *) int_bitop_init, + .expect_globals = (pr_int_t *) int_bitop_expect, + }, + { + .desc = "int bitop 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(int_bitop_init,int_bitop_expect), + .num_statements = num_statements (int_bitop_4_statements), + .statements = int_bitop_4_statements, + .init_globals = (pr_int_t *) int_bitop_init, + .expect_globals = (pr_int_t *) int_bitop_expect, + }, + { + .desc = "long bitop 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(long_bitop_init,long_bitop_expect), + .num_statements = num_statements (long_bitop_1_statements), + .statements = long_bitop_1_statements, + .init_globals = (pr_int_t *) long_bitop_init, + .expect_globals = (pr_int_t *) long_bitop_expect, + }, + { + .desc = "long bitop 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(long_bitop_init,long_bitop_expect), + .num_statements = num_statements (long_bitop_2_statements), + .statements = long_bitop_2_statements, + .init_globals = (pr_int_t *) long_bitop_init, + .expect_globals = (pr_int_t *) long_bitop_expect, + }, + { + .desc = "long bitop 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(long_bitop_init,long_bitop_expect), + .num_statements = num_statements (long_bitop_3a_statements), + .statements = long_bitop_3a_statements, + .init_globals = (pr_int_t *) long_bitop_init, + .expect_globals = (pr_int_t *) long_bitop_expect, + }, + { + .desc = "long bitop 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(long_bitop_init,long_bitop_expect), + .num_statements = num_statements (long_bitop_3b_statements), + .statements = long_bitop_3b_statements, + .init_globals = (pr_int_t *) long_bitop_init, + .expect_globals = (pr_int_t *) long_bitop_expect, + }, + { + .desc = "long bitop 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(long_bitop_init,long_bitop_expect), + .num_statements = num_statements (long_bitop_4_statements), + .statements = long_bitop_4_statements, + .init_globals = (pr_int_t *) long_bitop_init, + .expect_globals = (pr_int_t *) long_bitop_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-branch.c b/libs/gamecode/test/test-branch.c new file mode 100644 index 000000000..e02f229ba --- /dev/null +++ b/libs/gamecode/test/test-branch.c @@ -0,0 +1,193 @@ +#include "head.c" + +#define DB 0xdeadbeef + +static pr_int_t test_globals_init[] = { + -1, 1, 0, DB, DB, +}; + +static pr_int_t test_globals_expect[] = { + -1, 1, 0, DB, 1, +}; + +static dstatement_t ifz_taken_statements[] = { + { OP_IFZ, 4, 0, 2 }, + { OP_LEA_A, 1, 0, 3 }, + { OP_LEA_A, 1, 0, 4 }, + { OP_BREAK }, + { OP_IFZ, -2, 0, 2 }, +}; + +static dstatement_t ifb_taken_statements[] = { + { OP_IFB, 4, 0, 0 }, + { OP_LEA_A, 1, 0, 3 }, + { OP_LEA_A, 1, 0, 4 }, + { OP_BREAK }, + { OP_IFB, -2, 0, 0 }, +}; + +static dstatement_t ifa_taken_statements[] = { + { OP_IFA, 4, 0, 1 }, + { OP_LEA_A, 1, 0, 3 }, + { OP_LEA_A, 1, 0, 4 }, + { OP_BREAK }, + { OP_IFA, -2, 0, 1 }, +}; + +static dstatement_t ifnz_taken_statements[] = { + { OP_IFNZ, 4, 0, 0 }, + { OP_LEA_A, 1, 0, 3 }, + { OP_LEA_A, 1, 0, 4 }, + { OP_BREAK }, + { OP_IFNZ, -2, 0, 1 }, +}; + +static dstatement_t ifae_taken_statements[] = { + { OP_IFAE, 4, 0, 1 }, + { OP_LEA_A, 1, 0, 3 }, + { OP_LEA_A, 1, 0, 4 }, + { OP_BREAK }, + { OP_IFAE, -2, 0, 2 }, +}; + +static dstatement_t ifbe_taken_statements[] = { + { OP_IFBE, 4, 0, 0 }, + { OP_LEA_A, 1, 0, 3 }, + { OP_LEA_A, 1, 0, 4 }, + { OP_BREAK }, + { OP_IFBE, -2, 0, 2 }, +}; + +static dstatement_t ifz_not_taken_statements[] = { + { OP_IFZ, 3, 0, 0 }, + { OP_IFZ, 2, 0, 1 }, + { OP_LEA_A, 1, 0, 4 }, +}; + +static dstatement_t ifb_not_taken_statements[] = { + { OP_IFB, 3, 0, 2 }, + { OP_IFB, 2, 0, 1 }, + { OP_LEA_A, 1, 0, 4 }, +}; + +static dstatement_t ifa_not_taken_statements[] = { + { OP_IFA, 3, 0, 2 }, + { OP_IFA, 2, 0, 0 }, + { OP_LEA_A, 1, 0, 4 }, +}; + +static dstatement_t ifnz_not_taken_statements[] = { + { OP_IFNZ, 3, 0, 2 }, + { OP_LEA_A, 1, 0, 4 }, +}; + +static dstatement_t ifae_not_taken_statements[] = { + { OP_IFAE, 2, 0, 0 }, + { OP_LEA_A, 1, 0, 4 }, +}; + +static dstatement_t ifbe_not_taken_statements[] = { + { OP_IFBE, 2, 0, 1 }, + { OP_LEA_A, 1, 0, 4 }, +}; + +test_t tests[] = { + { + .desc = "ifz taken", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (ifz_taken_statements), + .statements = ifz_taken_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, + { + .desc = "ifb taken", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (ifb_taken_statements), + .statements = ifb_taken_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, + { + .desc = "ifa taken", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (ifa_taken_statements), + .statements = ifa_taken_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, + { + .desc = "ifnz taken", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (ifnz_taken_statements), + .statements = ifnz_taken_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, + { + .desc = "ifae taken", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (ifae_taken_statements), + .statements = ifae_taken_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, + { + .desc = "ifbe taken", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (ifbe_taken_statements), + .statements = ifbe_taken_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, + { + .desc = "ifz not taken", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (ifz_not_taken_statements), + .statements = ifz_not_taken_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, + { + .desc = "ifb not taken", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (ifb_not_taken_statements), + .statements = ifb_not_taken_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, + { + .desc = "ifa not taken", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (ifa_not_taken_statements), + .statements = ifa_not_taken_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, + { + .desc = "ifnz not taken", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (ifnz_not_taken_statements), + .statements = ifnz_not_taken_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, + { + .desc = "ifae not taken", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (ifae_not_taken_statements), + .statements = ifae_not_taken_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, + { + .desc = "ifbe not taken", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (ifbe_not_taken_statements), + .statements = ifbe_not_taken_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-callret.c b/libs/gamecode/test/test-callret.c new file mode 100644 index 000000000..d90b95efa --- /dev/null +++ b/libs/gamecode/test/test-callret.c @@ -0,0 +1,208 @@ +#include "head.c" + +#include "QF/mathlib.h" + +#define sq(x) ((float)(x)*(float)(x)) +#define pi_6 0x3f060a92 // pi/6 +#define r3_2 0x3f5db3d7 // sqrt(3)/2 +#define f1_2 0x3f000000 // 1/2 +#define f1 0x3f800000 // 1 +#define f2 0x40000000 // 2 +#define shx 0x3f0c4020 // sinh(pi/6) +#define chx 0x3f91f354 // cosh(pi/6) + +#define DB 0xdeadbeef + +#define STK (32 * 4) // stack ptr just after globals + +static pr_ivec4_t callret_init[32] = { + { 0, pi_6, 2, 0}, + { f1, f2, 0, 0}, + // result + { DB, DB, DB, DB }, + // pre-call with + { DB, DB, DB, DB }, + // post-call with + { DB, DB, DB, DB }, + { DB, DB, DB, DB }, +}; + +static pr_ivec4_t callret_expect[32] = { + // constants + { 0, pi_6, 2, 0 }, + { f1, f2, 0, 0 }, + // result + { r3_2, f1_2, chx, shx }, + // pre-call with: should be all 0 on progs init + { 0, 0, 0, 0 }, + // post-call with; should be restored to pre-call values (in this case, + // progs init) + { 0, 0, 0, 0 }, + { DB, DB, DB, DB }, +}; + +static dstatement_t callret_statements[] = { + { OP_WITH, 8, 0, 0 }, // pushregs + { OP_POP_A_4, 12, 0, 0 }, + { OP_STORE_A_1, 7, 0, STK }, // save stack pointer for check + { OP_PUSH_A_1, 1, 0, 0 }, + { OP_CALL_B, 2, 0, 8 }, + { OP_LEA_C, STK, 4, STK }, // discard param + { OP_SUB_I_1, 7, STK, 7 }, // check stack restored + { OP_WITH, 8, 0, 0 }, // pushregs + { OP_POP_A_4, 16, 0, 0 }, + { OP_BREAK }, +// cos_sin_cosh_sinh: +// calculate cos(x), sin(x), cosh(x) and sinh(x) simultaneously +[32]= + { OP_WITH, 2, 0, 1 }, // put params into reg 1 + { OP_LEA_C, STK, -24, STK }, // reserve 24 words on the stack + { OP_WITH, 2, 0, 2 }, // put locals into reg 2 +#define x 0 // in parameters float +#define xn 0 // in locals vec4 +#define x2 4 // in locals vec4 +#define ac 8 // in locals vec4 +#define fa 12 // in locals vec4 +#define fi 16 // in locals vec4 +#define c 20 // in locals int + { OP(2, 0, 1, OP_STORE_A_1), xn+1,0, x }, // init xn to [1, x, 0, 0] + { OP(2, 0, 0, OP_STORE_A_1), xn, 0, 4 }, + { OP(2, 0, 2, OP_SWIZZLE_F), xn, 0x0044, xn }, // xn -> [1, x, 1, x] + { OP(1, 1, 2, OP_MUL_F_1), x, x, x2 }, // x2 -> [x*x, ?, ?, ?] + { OP(2, 0, 2, OP_SWIZZLE_F), x2, 0x0300, x2},//x2 -> [-x*x, -x*x, x*x, x*x] + { OP(2, 0, 0, OP_STORE_A_1), fa, 0, 4 }, // init factorial + { OP(2, 0, 0, OP_STORE_A_1), fa+1,0, 5 }, + { OP(2, 0, 2, OP_SWIZZLE_F), fa, 0x0044, fa }, // fa -> [1, 2, 1, 2] + { OP(2, 0, 2, OP_SWIZZLE_F), fa, 0x0000, fi }, // init fi -> [1, 1, 1, 1] + { OP(2, 2, 2, OP_SUB_F_4), ac, ac, ac }, // init acc (output) to 0 + { OP(0, 0, 2, OP_LEA_A), 25, 0, c }, // init count +// loop: + { OP(2, 2, 2, OP_ADD_F_4), ac, xn, ac }, // acc += xn + { OP(2, 2, 2, OP_MUL_F_4), xn, x2, xn }, // xn *= x2 + { OP(2, 2, 2, OP_DIV_F_4), xn, fa, xn }, // xn /= f + { OP(2, 2, 2, OP_ADD_F_4), fa, fi, fa }, // f += inc + { OP(2, 2, 2, OP_DIV_F_4), xn, fa, xn }, // xn /= f + { OP(2, 2, 2, OP_ADD_F_4), fa, fi, fa }, // f += inc + { OP(2, 0, 2, OP_LEA_C), c, -1, c }, // dec count + { OP(0, 0, 2, OP_IFA), -7, 0, c }, // count > 0 + { OP(2, 0, 0, OP_RETURN), ac, 0, 3 }, // size is (c&31)+1 +#undef x +#undef xn +#undef x2 +#undef ac +#undef fa +#undef fi +#undef c +}; + +static pr_ivec4_t call32_init[32] = { + { 0, 2, 0, 0 }, + { DB, DB, DB, DB }, + { DB, DB, DB, DB }, + { DB, DB, DB, DB }, + { DB, DB, DB, DB }, + { DB, DB, DB, DB }, + { DB, DB, DB, DB }, + { DB, DB, DB, DB }, + { DB, DB, DB, DB }, +}; + +static pr_ivec4_t call32_expect[32] = { + { 0, 2, 0, 0 }, + { 0, 1, 2, 3 }, + { 4, 5, 6, 7 }, + { 8, 9, 10, 11 }, + { 12, 13, 14, 15 }, + { 16, 17, 18, 19 }, + { 20, 21, 22, 23 }, + { 24, 25, 26, 27 }, + { 28, 29, 30, 31 }, +}; + +static dstatement_t call32_statements[] = { + { OP_CALL_B, 1, 0, 4 }, + { OP_BREAK }, +[32]= + { OP_LEA_C, STK, -36, STK }, // reserve 36 words on the stack + { OP_WITH, 2, 0, 2 }, // put locals into reg 2 + { OP(0, 0, 2, OP_LEA_A), 32, 0, 32 }, // init index + { OP(2, 0, 2, OP_LEA_A), 0, 0, 33 }, // init base to array +//loop: + { OP(0, 0, 2, OP_IFBE), 4, 0, 32 }, // if index-- > 0 + { OP(2, 0, 2, OP_LEA_C), 32, -1, 32 }, + { OP(2, 2, 2, OP_STORE_D_1), 33, 32, 32 }, // array[index] = index + { OP(0, 0, 0, OP_JUMP_A), -3, 0, 0 }, + { OP(2, 0, 0, OP_RETURN), 0, 0, 0x1f }, // only bits 0-5 are size +}; + +static pr_ivec4_t callchain_init[32] = { + { 0, 2, 3, 4 }, + { DB, DB, DB, DB }, +}; + +static pr_ivec4_t callchain_expect[32] = { + { 0, 2, 3, 4 }, + { 42, DB, DB, DB }, +}; + +static dstatement_t callchain_statements[] = { + { OP_CALL_B, 1, 0, 4 }, + { OP_BREAK }, +[32]= + { OP_LEA_C, STK, -4, STK }, // reserve 4 words on the stack + { OP_WITH, 2, 0, 2 }, // put locals into reg 2 + { OP(0, 0, 2, OP_CALL_B), 2, 0, 0 }, + { OP(0, 0, 2, OP_CALL_B), 3, 0, 1 }, + { OP(2, 0, 0, OP_RETURN), 0, 0, 0 }, +[64]= + { OP_LEA_C, STK, -4, STK }, // reserve 4 words on the stack + { OP_WITH, 2, 0, 2 }, // put locals into reg 2 + { OP(0, 0, 2, OP_LEA_A), 42, 0, 0 }, // init value + { OP(2, 0, 0, OP_RETURN), 0, 0, 0 }, // return value +[96]= + { OP_RETURN, 0, 0, -1 } // void return +}; + +static bfunction_t callret_functions[] = { + { .first_statement = 32 }, + { .first_statement = 64 }, + { .first_statement = 96 }, +}; + +test_t tests[] = { + { + .desc = "callret", + .num_globals = num_globals(callret_init,callret_expect), + .num_statements = num_statements (callret_statements), + .statements = callret_statements, + .init_globals = (pr_int_t *) callret_init, + .expect_globals = (pr_int_t *) callret_expect, + .functions = callret_functions, + .num_functions = num_functions(callret_functions), + .stack_size = 128, + }, + { + .desc = "call32", + .num_globals = num_globals(call32_init,call32_expect), + .num_statements = num_statements (call32_statements), + .statements = call32_statements, + .init_globals = (pr_int_t *) call32_init, + .expect_globals = (pr_int_t *) call32_expect, + .functions = callret_functions, + .num_functions = num_functions(callret_functions), + .stack_size = 128, + }, + { + .desc = "callchain", + .num_globals = num_globals(callchain_init,callchain_expect), + .num_statements = num_statements (callchain_statements), + .statements = callchain_statements, + .init_globals = (pr_int_t *) callchain_init, + .expect_globals = (pr_int_t *) callchain_expect, + .functions = callret_functions, + .num_functions = num_functions(callret_functions), + .stack_size = 128, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-conv0.c b/libs/gamecode/test/test-conv0.c new file mode 100644 index 000000000..d5153b5a4 --- /dev/null +++ b/libs/gamecode/test/test-conv0.c @@ -0,0 +1,190 @@ +#include "head.c" + +#include "QF/mathlib.h" + +static pr_ivec4_t int_conv_init[] = { + { 5, -5, 0x80000000, 0x7fffffff}, //int + { 0x3fc00000, 0xbfc00000, 0x40100000, 0xc0100000}, //float 1.5, -1.5, 2.25, -2.25 + { 99, 0x80000000, 0x80000000, 99}, //long + { 256, 0, 0x7fffffff, 0}, //long + { 0, 0x3ff80000, 0, 0xbff80000}, //double 1.5, -1.5 + { 0, 0x40020000, 0, 0xc0020000}, //double 2.25, -2.25 + { 5, -5, 0x80000000, 0x7fffffff}, //uint + { ~0, 1, 0x80000000, 0}, //bool32 + { 99, 0x80000000, 0x80000000, 99}, //ulong + { 256, 0, 0x7fffffff, 0}, //ulong + { ~0, ~0, ~0, 0}, //bool64 + { 0, ~0, 0, 0}, //bool64 + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +static pr_ivec4_t int_conv_expect[] = { + { 5, -5, 0x80000000, 0x7fffffff}, //int + { 0x3fc00000, 0xbfc00000, 0x40100000, 0xc0100000}, //float 1.5, -1.5, 2.25, -2.25 + { 99, 0x80000000, 0x80000000, 99}, //long + { 256, 0, 0x7fffffff, 0}, //long + { 0, 0x3ff80000, 0, 0xbff80000}, //double 1.5, -1.5 + { 0, 0x40020000, 0, 0xc0020000}, //double 2.25, -2.25 + { 5, -5, 0x80000000, 0x7fffffff}, //uint + { ~0, 1, 0x80000000, 0}, //bool32 + { 99, 0x80000000, 0x80000000, 99}, //ulong + { 256, 0, 0x7fffffff, 0}, //ulong + { ~0, ~0, ~0, 0}, //bool64 + { 0, ~0, 0, 0}, //bool64 + { 5, -5, 0x80000000, 0x7fffffff}, // int + { 1, -1, 2, -2}, // float + { 99, 0x80000000, 256, 0x7fffffff}, // long + { 1, -1, 2, -2}, // double + { 5, -5, 0x80000000, 0x7fffffff}, // uint + { 1, 1, 1, 0}, // bool32 + { 99, 0x80000000, 256, 0x7fffffff}, // ulong + { 1, 1, 1, 0}, // bool64 +}; + +static dstatement_t int_conv_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 80 }, // init index + { OP(0, 0, 0, OP_LEA_A), 8, 0, 81 }, // init index for 64-bits +//loop: + { OP(0, 0, 0, OP_LEA_C), 80, -1, 80 }, // dec index + { OP(0, 0, 0, OP_LEA_C), 81, -2, 81 }, // dec index for 64-bits + { OP(0, 0, 0, OP_IFAE), 2, 0, 80 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 80, 1 }, + { OP(0, 0, 0, OP_WITH), 4, 81, 2 }, + { OP(1, 1, 1, OP_CONV), 0, 0000, 48 }, + { OP(1, 1, 1, OP_CONV), 4, 0010, 52 }, + { OP(2, 1, 1, OP_CONV), 8, 0020, 56 }, + { OP(2, 1, 1, OP_CONV), 16, 0030, 60 }, + { OP(1, 1, 1, OP_CONV), 24, 0040, 64 }, + { OP(1, 1, 1, OP_CONV), 28, 0050, 68 }, + { OP(2, 1, 1, OP_CONV), 32, 0060, 72 }, + { OP(2, 1, 1, OP_CONV), 40, 0070, 76 }, + { OP(1, 1, 1, OP_JUMP_A), -14, 0, 0 }, +}; + +static dstatement_t int_conv_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 80 }, // index + { OP(0, 0, 0, OP_LEA_A), 8, 0, 81 }, // init index for 64-bits +//loop: + { OP(0, 0, 0, OP_LEA_C), 80, -2, 80 }, // dec index + { OP(0, 0, 0, OP_LEA_C), 81, -4, 81 }, // dec index for 64-bits + { OP(0, 0, 0, OP_IFAE), 2, 0, 80 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 80, 1 }, + { OP(0, 0, 0, OP_WITH), 4, 81, 2 }, + { OP(1, 1, 1, OP_CONV), 0, 0100, 48 }, + { OP(1, 1, 1, OP_CONV), 4, 0110, 52 }, + { OP(2, 1, 1, OP_CONV), 8, 0120, 56 }, + { OP(2, 1, 1, OP_CONV), 16, 0130, 60 }, + { OP(1, 1, 1, OP_CONV), 24, 0140, 64 }, + { OP(1, 1, 1, OP_CONV), 28, 0150, 68 }, + { OP(2, 1, 1, OP_CONV), 32, 0160, 72 }, + { OP(2, 1, 1, OP_CONV), 40, 0170, 76 }, + { OP(1, 1, 1, OP_JUMP_A), -14, 0, 0 }, +}; + +static dstatement_t int_conv_3a_statements[] = { + { OP(1, 1, 1, OP_CONV), 0, 0200, 48 }, + { OP(1, 1, 1, OP_CONV), 3, 0200, 51 }, + { OP(1, 1, 1, OP_CONV), 4, 0210, 52 }, + { OP(1, 1, 1, OP_CONV), 7, 0010, 55 }, + { OP(2, 1, 1, OP_CONV), 8, 0220, 56 }, + { OP(2, 1, 1, OP_CONV), 14, 0020, 59 }, + { OP(2, 1, 1, OP_CONV), 16, 0230, 60 }, + { OP(2, 1, 1, OP_CONV), 22, 0030, 63 }, + { OP(1, 1, 1, OP_CONV), 24, 0240, 64 }, + { OP(1, 1, 1, OP_CONV), 27, 0240, 67 }, + { OP(1, 1, 1, OP_CONV), 28, 0250, 68 }, + { OP(1, 1, 1, OP_CONV), 31, 0050, 71 }, + { OP(2, 1, 1, OP_CONV), 32, 0260, 72 }, + { OP(2, 1, 1, OP_CONV), 38, 0060, 75 }, + { OP(2, 1, 1, OP_CONV), 40, 0270, 76 }, + { OP(2, 1, 1, OP_CONV), 46, 0070, 79 }, +}; + +static dstatement_t int_conv_3b_statements[] = { + { OP(1, 1, 1, OP_CONV), 0, 0200, 48 }, + { OP(1, 1, 1, OP_CONV), 1, 0200, 49 }, + { OP(1, 1, 1, OP_CONV), 4, 0010, 52 }, + { OP(1, 1, 1, OP_CONV), 5, 0210, 53 }, + { OP(2, 1, 1, OP_CONV), 8, 0020, 56 }, + { OP(2, 1, 1, OP_CONV), 10, 0220, 57 }, + { OP(2, 1, 1, OP_CONV), 16, 0030, 60 }, + { OP(2, 1, 1, OP_CONV), 18, 0230, 61 }, + { OP(1, 1, 1, OP_CONV), 24, 0240, 64 }, + { OP(1, 1, 1, OP_CONV), 25, 0240, 65 }, + { OP(1, 1, 1, OP_CONV), 28, 0050, 68 }, + { OP(1, 1, 1, OP_CONV), 29, 0250, 69 }, + { OP(2, 1, 1, OP_CONV), 32, 0060, 72 }, + { OP(2, 1, 1, OP_CONV), 34, 0260, 73 }, + { OP(2, 1, 1, OP_CONV), 40, 0070, 76 }, + { OP(2, 1, 1, OP_CONV), 42, 0270, 77 }, +}; + +static dstatement_t int_conv_4_statements[] = { + { OP(1, 1, 1, OP_CONV), 0, 0300, 48 }, + { OP(1, 1, 1, OP_CONV), 4, 0310, 52 }, + { OP(2, 1, 1, OP_CONV), 8, 0320, 56 }, + { OP(2, 1, 1, OP_CONV), 16, 0330, 60 }, + { OP(1, 1, 1, OP_CONV), 28, 0350, 68 }, + { OP(2, 1, 1, OP_CONV), 32, 0360, 72 }, + { OP(2, 1, 1, OP_CONV), 40, 0370, 76 }, + { OP(1, 1, 1, OP_CONV), 24, 0340, 64 }, +}; + +test_t tests[] = { + { + .desc = "int conv 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(int_conv_init,int_conv_expect), + .num_statements = num_statements (int_conv_1_statements), + .statements = int_conv_1_statements, + .init_globals = (pr_int_t *) int_conv_init, + .expect_globals = (pr_int_t *) int_conv_expect, + }, + { + .desc = "int conv 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(int_conv_init,int_conv_expect), + .num_statements = num_statements (int_conv_2_statements), + .statements = int_conv_2_statements, + .init_globals = (pr_int_t *) int_conv_init, + .expect_globals = (pr_int_t *) int_conv_expect, + }, + { + .desc = "int conv 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(int_conv_init,int_conv_expect), + .num_statements = num_statements (int_conv_3a_statements), + .statements = int_conv_3a_statements, + .init_globals = (pr_int_t *) int_conv_init, + .expect_globals = (pr_int_t *) int_conv_expect, + }, + { + .desc = "int conv 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(int_conv_init,int_conv_expect), + .num_statements = num_statements (int_conv_3b_statements), + .statements = int_conv_3b_statements, + .init_globals = (pr_int_t *) int_conv_init, + .expect_globals = (pr_int_t *) int_conv_expect, + }, + { + .desc = "int conv 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(int_conv_init,int_conv_expect), + .num_statements = num_statements (int_conv_4_statements), + .statements = int_conv_4_statements, + .init_globals = (pr_int_t *) int_conv_init, + .expect_globals = (pr_int_t *) int_conv_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-conv1.c b/libs/gamecode/test/test-conv1.c new file mode 100644 index 000000000..663604205 --- /dev/null +++ b/libs/gamecode/test/test-conv1.c @@ -0,0 +1,190 @@ +#include "head.c" + +#include "QF/mathlib.h" + +static pr_ivec4_t float_conv_init[] = { + { 5, -5, 0x80000000, 0x7fffffff}, //int + { 0x3fc00000, 0xbfc00000, 0x7149f2ca, 0xf149f2ca}, //float 1e30, -1e30 + { 99, 0x80000000, 0x80000000, 99}, //long + { 256, 0, 0x7fffffff, 0}, //long + { 0x39a08cea, 0x46293e59, 0x39a08cea, 0xc6293e59}, //double 1e30, -1e30 + { 0, 0x3ff80000, 0, 0xbff80000}, //double 1.5, -1.5 + { 5, -5, 0x80000000, 0x7fffffff}, //uint + { ~0, 1, 0x80000000, 0}, //bool32 + { 99, 0x80000000, 0x80000000, 99}, //ulong + { 256, 0, 0x7fffffff, 0}, //ulong + { ~0, ~0, ~0, 0}, //bool64 + { 0, ~0, 0, 0}, //bool64 + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +static pr_ivec4_t float_conv_expect[] = { + { 5, -5, 0x80000000, 0x7fffffff}, //int + { 0x3fc00000, 0xbfc00000, 0x7149f2ca, 0xf149f2ca}, //float + { 99, 0x80000000, 0x80000000, 99}, //long + { 256, 0, 0x7fffffff, 0}, //long + { 0x39a08cea, 0x46293e59, 0x39a08cea, 0xc6293e59}, //double 1e30, -1e30 + { 0, 0x3ff80000, 0, 0xbff80000}, //double 1.5, -1.5 + { 5, -5, 0x80000000, 0x7fffffff}, //uint + { ~0, 1, 0x80000000, 0}, //bool32 + { 99, 0x80000000, 0x80000000, 99}, //ulong + { 256, 0, 0x7fffffff, 0}, //ulong + { ~0, ~0, ~0, 0}, //bool64 + { 0, ~0, 0, 0}, //bool64 + { 0x40a00000, 0xc0a00000, 0xcf000000, 0x4f000000}, // int + { 0x3fc00000, 0xbfc00000, 0x7149f2ca, 0xf149f2ca}, // float + { 0xdf000000, 0x52c70000, 0x43800000, 0x4f000000}, // long + { 0x7149f2ca, 0xf149f2ca, 0x3fc00000, 0xbfc00000}, // double + { 0x40a00000, 0x4f800000, 0x4f000000, 0x4f000000}, // uint + { 0x3f800000, 0x3f800000, 0x3f800000, 0}, // bool32 + { 0x5f000000, 0x52c70000, 0x43800000, 0x4f000000}, // ulong + { 0x3f800000, 0x3f800000, 0x3f800000, 0}, // bool64 +}; + +static dstatement_t float_conv_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 80 }, // init index + { OP(0, 0, 0, OP_LEA_A), 8, 0, 81 }, // init index for 64-bits +//loop: + { OP(0, 0, 0, OP_LEA_C), 80, -1, 80 }, // dec index + { OP(0, 0, 0, OP_LEA_C), 81, -2, 81 }, // dec index for 64-bits + { OP(0, 0, 0, OP_IFAE), 2, 0, 80 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 80, 1 }, + { OP(0, 0, 0, OP_WITH), 4, 81, 2 }, + { OP(1, 1, 1, OP_CONV), 0, 0001, 48 }, + { OP(1, 1, 1, OP_CONV), 4, 0011, 52 }, + { OP(2, 1, 1, OP_CONV), 8, 0021, 56 }, + { OP(2, 1, 1, OP_CONV), 16, 0031, 60 }, + { OP(1, 1, 1, OP_CONV), 24, 0041, 64 }, + { OP(1, 1, 1, OP_CONV), 28, 0051, 68 }, + { OP(2, 1, 1, OP_CONV), 32, 0061, 72 }, + { OP(2, 1, 1, OP_CONV), 40, 0071, 76 }, + { OP(1, 1, 1, OP_JUMP_A), -14, 0, 0 }, +}; + +static dstatement_t float_conv_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 80 }, // index + { OP(0, 0, 0, OP_LEA_A), 8, 0, 81 }, // init index for 64-bits +//loop: + { OP(0, 0, 0, OP_LEA_C), 80, -2, 80 }, // dec index + { OP(0, 0, 0, OP_LEA_C), 81, -4, 81 }, // dec index for 64-bits + { OP(0, 0, 0, OP_IFAE), 2, 0, 80 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 80, 1 }, + { OP(0, 0, 0, OP_WITH), 4, 81, 2 }, + { OP(1, 1, 1, OP_CONV), 0, 0101, 48 }, + { OP(1, 1, 1, OP_CONV), 4, 0111, 52 }, + { OP(2, 1, 1, OP_CONV), 8, 0121, 56 }, + { OP(2, 1, 1, OP_CONV), 16, 0131, 60 }, + { OP(1, 1, 1, OP_CONV), 24, 0141, 64 }, + { OP(1, 1, 1, OP_CONV), 28, 0151, 68 }, + { OP(2, 1, 1, OP_CONV), 32, 0161, 72 }, + { OP(2, 1, 1, OP_CONV), 40, 0171, 76 }, + { OP(1, 1, 1, OP_JUMP_A), -14, 0, 0 }, +}; + +static dstatement_t float_conv_3a_statements[] = { + { OP(1, 1, 1, OP_CONV), 0, 0201, 48 }, + { OP(1, 1, 1, OP_CONV), 3, 0001, 51 }, + { OP(1, 1, 1, OP_CONV), 4, 0211, 52 }, + { OP(1, 1, 1, OP_CONV), 7, 0011, 55 }, + { OP(2, 1, 1, OP_CONV), 8, 0221, 56 }, + { OP(2, 1, 1, OP_CONV), 14, 0021, 59 }, + { OP(2, 1, 1, OP_CONV), 16, 0231, 60 }, + { OP(2, 1, 1, OP_CONV), 22, 0031, 63 }, + { OP(1, 1, 1, OP_CONV), 24, 0241, 64 }, + { OP(1, 1, 1, OP_CONV), 27, 0041, 67 }, + { OP(1, 1, 1, OP_CONV), 28, 0251, 68 }, + { OP(1, 1, 1, OP_CONV), 31, 0051, 71 }, + { OP(2, 1, 1, OP_CONV), 32, 0261, 72 }, + { OP(2, 1, 1, OP_CONV), 38, 0061, 75 }, + { OP(2, 1, 1, OP_CONV), 40, 0271, 76 }, + { OP(2, 1, 1, OP_CONV), 46, 0071, 79 }, +}; + +static dstatement_t float_conv_3b_statements[] = { + { OP(1, 1, 1, OP_CONV), 0, 0001, 48 }, + { OP(1, 1, 1, OP_CONV), 1, 0201, 49 }, + { OP(1, 1, 1, OP_CONV), 4, 0011, 52 }, + { OP(1, 1, 1, OP_CONV), 5, 0211, 53 }, + { OP(2, 1, 1, OP_CONV), 8, 0021, 56 }, + { OP(2, 1, 1, OP_CONV), 10, 0221, 57 }, + { OP(2, 1, 1, OP_CONV), 16, 0031, 60 }, + { OP(2, 1, 1, OP_CONV), 18, 0231, 61 }, + { OP(1, 1, 1, OP_CONV), 24, 0241, 64 }, + { OP(1, 1, 1, OP_CONV), 27, 0041, 67 }, + { OP(1, 1, 1, OP_CONV), 28, 0051, 68 }, + { OP(1, 1, 1, OP_CONV), 29, 0251, 69 }, + { OP(2, 1, 1, OP_CONV), 32, 0061, 72 }, + { OP(2, 1, 1, OP_CONV), 34, 0261, 73 }, + { OP(2, 1, 1, OP_CONV), 40, 0071, 76 }, + { OP(2, 1, 1, OP_CONV), 42, 0271, 77 }, +}; + +static dstatement_t float_conv_4_statements[] = { + { OP(1, 1, 1, OP_CONV), 0, 0301, 48 }, + { OP(1, 1, 1, OP_CONV), 4, 0311, 52 }, + { OP(2, 1, 1, OP_CONV), 8, 0321, 56 }, + { OP(2, 1, 1, OP_CONV), 16, 0331, 60 }, + { OP(1, 1, 1, OP_CONV), 24, 0341, 64 }, + { OP(1, 1, 1, OP_CONV), 28, 0351, 68 }, + { OP(2, 1, 1, OP_CONV), 32, 0361, 72 }, + { OP(2, 1, 1, OP_CONV), 40, 0371, 76 }, +}; + +test_t tests[] = { + { + .desc = "float conv 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(float_conv_init,float_conv_expect), + .num_statements = num_statements (float_conv_1_statements), + .statements = float_conv_1_statements, + .init_globals = (pr_int_t *) float_conv_init, + .expect_globals = (pr_int_t *) float_conv_expect, + }, + { + .desc = "float conv 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(float_conv_init,float_conv_expect), + .num_statements = num_statements (float_conv_2_statements), + .statements = float_conv_2_statements, + .init_globals = (pr_int_t *) float_conv_init, + .expect_globals = (pr_int_t *) float_conv_expect, + }, + { + .desc = "float conv 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(float_conv_init,float_conv_expect), + .num_statements = num_statements (float_conv_3a_statements), + .statements = float_conv_3a_statements, + .init_globals = (pr_int_t *) float_conv_init, + .expect_globals = (pr_int_t *) float_conv_expect, + }, + { + .desc = "float conv 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(float_conv_init,float_conv_expect), + .num_statements = num_statements (float_conv_3b_statements), + .statements = float_conv_3b_statements, + .init_globals = (pr_int_t *) float_conv_init, + .expect_globals = (pr_int_t *) float_conv_expect, + }, + { + .desc = "float conv 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(float_conv_init,float_conv_expect), + .num_statements = num_statements (float_conv_4_statements), + .statements = float_conv_4_statements, + .init_globals = (pr_int_t *) float_conv_init, + .expect_globals = (pr_int_t *) float_conv_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-conv2.c b/libs/gamecode/test/test-conv2.c new file mode 100644 index 000000000..647ea2ed4 --- /dev/null +++ b/libs/gamecode/test/test-conv2.c @@ -0,0 +1,206 @@ +#include "head.c" + +#include "QF/mathlib.h" + +static pr_ivec4_t long_conv_init[] = { + { 5, -5, 0x80000000, 0x7fffffff}, //int + { 0x3fc00000, 0x40100000, 0x40700000, 0x40a00000}, //float 1.5, 2.25, 3.75, 5 + { 99, 0x80000000, 0x80000000, 99}, //long + { 256, 0, 0x7fffffff, 0}, //long + { 0, 0x3ff80000, 0, 0x40020000}, //double 1.5, 2.25, 3.75, 5 + { 0, 0x400e0000, 0, 0x40140000}, + { 5, -5, 0x80000000, 0x7fffffff}, //uint + { ~0, 1, 0x80000000, 0}, //bool32 + { 99, 0x80000000, 0x80000000, 99}, //ulong + { 256, 0, 0x7fffffff, 0}, //ulong + { ~0, ~0, ~0, 0}, //bool64 + { 0, ~0, 0, 0}, //bool64 + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +static pr_ivec4_t long_conv_expect[] = { + { 5, -5, 0x80000000, 0x7fffffff}, //int + { 0x3fc00000, 0x40100000, 0x40700000, 0x40a00000}, //float 1.5, 2.25, 3.75, 5 + { 99, 0x80000000, 0x80000000, 99}, //long + { 256, 0, 0x7fffffff, 0}, //long + { 0, 0x3ff80000, 0, 0x40020000}, //double 1.5, 2.25, 3.75, 5 + { 0, 0x400e0000, 0, 0x40140000}, + { 5, -5, 0x80000000, 0x7fffffff}, //uint + { ~0, 1, 0x80000000, 0}, //bool32 + { 99, 0x80000000, 0x80000000, 99}, //ulong + { 256, 0, 0x7fffffff, 0}, //ulong + { ~0, ~0, ~0, 0}, //bool64 + { 0, ~0, 0, 0}, //bool64 + { 5, 0, -5, 0xffffffff}, // int + { 0x80000000, 0xffffffff, 0x7fffffff, 0}, + { 1, 0, 2, 0}, // float + { 3, 0, 5, 0}, + { 99, 0x80000000, 0x80000000, 99}, // long + { 256, 0, 0x7fffffff, 0}, + { 1, 0, 2, 0}, // double + { 3, 0, 5, 0}, + { 5, 0, -5, 0}, // uint + { 0x80000000, 0, 0x7fffffff, 0}, + { 1, 0, 1, 0}, // bool32 + { 1, 0, 0, 0}, + { 99, 0x80000000, 0x80000000, 99}, // ulong + { 256, 0, 0x7fffffff, 0}, + { 1, 0, 1, 0}, // bool64 + { 1, 0, 0, 0}, +}; + +static dstatement_t long_conv_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 112 }, // init index + { OP(0, 0, 0, OP_LEA_A), 8, 0, 113 }, // init index for 64-bits +//loop: + { OP(0, 0, 0, OP_LEA_C), 112, -1, 112 }, // dec index + { OP(0, 0, 0, OP_LEA_C), 113, -2, 113 }, // dec index for 64-bits + { OP(0, 0, 0, OP_IFAE), 2, 0, 112 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 112, 1 }, + { OP(0, 0, 0, OP_WITH), 4, 113, 2 }, + { OP(1, 1, 2, OP_CONV), 0, 0002, 48 }, + { OP(1, 1, 2, OP_CONV), 4, 0012, 56 }, + { OP(2, 1, 2, OP_CONV), 8, 0022, 64 }, + { OP(2, 1, 2, OP_CONV), 16, 0032, 72 }, + { OP(1, 1, 2, OP_CONV), 24, 0042, 80 }, + { OP(1, 1, 2, OP_CONV), 28, 0052, 88 }, + { OP(2, 1, 2, OP_CONV), 32, 0062, 96 }, + { OP(2, 1, 2, OP_CONV), 40, 0072, 104 }, + { OP(0, 0, 0, OP_JUMP_A), -14, 0, 0 }, +}; + +static dstatement_t long_conv_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 112 }, // index + { OP(0, 0, 0, OP_LEA_A), 8, 0, 113 }, // init index for 64-bits +//loop: + { OP(0, 0, 0, OP_LEA_C), 112, -2, 112 }, // dec index + { OP(0, 0, 0, OP_LEA_C), 113, -4, 113 }, // dec index for 64-bits + { OP(0, 0, 0, OP_IFAE), 2, 0, 112 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 112, 1 }, + { OP(0, 0, 0, OP_WITH), 4, 113, 2 }, + { OP(1, 1, 2, OP_CONV), 0, 0102, 48 }, + { OP(1, 1, 2, OP_CONV), 4, 0112, 56 }, + { OP(2, 1, 2, OP_CONV), 8, 0122, 64 }, + { OP(2, 1, 2, OP_CONV), 16, 0132, 72 }, + { OP(1, 1, 2, OP_CONV), 24, 0142, 80 }, + { OP(1, 1, 2, OP_CONV), 28, 0152, 88 }, + { OP(2, 1, 2, OP_CONV), 32, 0162, 96 }, + { OP(2, 1, 2, OP_CONV), 40, 0172, 104 }, + { OP(0, 0, 0, OP_JUMP_A), -14, 0, 0 }, +}; + +static dstatement_t long_conv_3a_statements[] = { + { OP(1, 1, 2, OP_CONV), 0, 0202, 48 }, + { OP(1, 1, 2, OP_CONV), 3, 0002, 54 }, + { OP(1, 1, 2, OP_CONV), 4, 0212, 56 }, + { OP(1, 1, 2, OP_CONV), 7, 0012, 62 }, + { OP(2, 1, 2, OP_CONV), 8, 0222, 64 }, + { OP(2, 1, 2, OP_CONV), 14, 0022, 70 }, + { OP(2, 1, 2, OP_CONV), 16, 0232, 72 }, + { OP(2, 1, 2, OP_CONV), 22, 0032, 78 }, + { OP(1, 1, 2, OP_CONV), 24, 0242, 80 }, + { OP(1, 1, 2, OP_CONV), 27, 0042, 86 }, + { OP(1, 1, 2, OP_CONV), 28, 0252, 88 }, + { OP(1, 1, 2, OP_CONV), 31, 0052, 94 }, + { OP(2, 1, 2, OP_CONV), 32, 0262, 96 }, + { OP(2, 1, 2, OP_CONV), 38, 0062, 102 }, + { OP(2, 1, 2, OP_CONV), 40, 0272, 104 }, + { OP(2, 1, 2, OP_CONV), 46, 0072, 110 }, +}; + +static dstatement_t long_conv_3b_statements[] = { + { OP(1, 1, 2, OP_CONV), 0, 0002, 48 }, + { OP(1, 1, 2, OP_CONV), 1, 0202, 50 }, + { OP(1, 1, 2, OP_CONV), 4, 0012, 56 }, + { OP(1, 1, 2, OP_CONV), 5, 0212, 58 }, + { OP(2, 1, 2, OP_CONV), 8, 0022, 64 }, + { OP(2, 1, 2, OP_CONV), 10, 0222, 66 }, + { OP(2, 1, 2, OP_CONV), 16, 0032, 72 }, + { OP(2, 1, 2, OP_CONV), 18, 0232, 74 }, + { OP(1, 1, 2, OP_CONV), 24, 0042, 80 }, + { OP(1, 1, 2, OP_CONV), 25, 0242, 82 }, + { OP(1, 1, 2, OP_CONV), 28, 0052, 88 }, + { OP(1, 1, 2, OP_CONV), 29, 0252, 90 }, + { OP(2, 1, 2, OP_CONV), 32, 0062, 96 }, + { OP(2, 1, 2, OP_CONV), 34, 0262, 98 }, + { OP(2, 1, 2, OP_CONV), 40, 0072, 104 }, + { OP(2, 1, 2, OP_CONV), 42, 0272, 106 }, +}; + +static dstatement_t long_conv_4_statements[] = { + { OP(1, 1, 2, OP_CONV), 0, 0302, 48 }, + { OP(1, 1, 2, OP_CONV), 4, 0312, 56 }, + { OP(2, 1, 2, OP_CONV), 8, 0322, 64 }, + { OP(2, 1, 2, OP_CONV), 16, 0332, 72 }, + { OP(1, 1, 2, OP_CONV), 24, 0342, 80 }, + { OP(1, 1, 2, OP_CONV), 28, 0352, 88 }, + { OP(2, 1, 2, OP_CONV), 32, 0362, 96 }, + { OP(2, 1, 2, OP_CONV), 40, 0372, 104 }, +}; + +test_t tests[] = { + { + .desc = "long conv 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(long_conv_init,long_conv_expect), + .num_statements = num_statements (long_conv_1_statements), + .statements = long_conv_1_statements, + .init_globals = (pr_int_t *) long_conv_init, + .expect_globals = (pr_int_t *) long_conv_expect, + }, + { + .desc = "long conv 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(long_conv_init,long_conv_expect), + .num_statements = num_statements (long_conv_2_statements), + .statements = long_conv_2_statements, + .init_globals = (pr_int_t *) long_conv_init, + .expect_globals = (pr_int_t *) long_conv_expect, + }, + { + .desc = "long conv 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(long_conv_init,long_conv_expect), + .num_statements = num_statements (long_conv_3a_statements), + .statements = long_conv_3a_statements, + .init_globals = (pr_int_t *) long_conv_init, + .expect_globals = (pr_int_t *) long_conv_expect, + }, + { + .desc = "long conv 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(long_conv_init,long_conv_expect), + .num_statements = num_statements (long_conv_3b_statements), + .statements = long_conv_3b_statements, + .init_globals = (pr_int_t *) long_conv_init, + .expect_globals = (pr_int_t *) long_conv_expect, + }, + { + .desc = "long conv 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(long_conv_init,long_conv_expect), + .num_statements = num_statements (long_conv_4_statements), + .statements = long_conv_4_statements, + .init_globals = (pr_int_t *) long_conv_init, + .expect_globals = (pr_int_t *) long_conv_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-conv3.c b/libs/gamecode/test/test-conv3.c new file mode 100644 index 000000000..7ea316d8b --- /dev/null +++ b/libs/gamecode/test/test-conv3.c @@ -0,0 +1,206 @@ +#include "head.c" + +#include "QF/mathlib.h" + +static pr_ivec4_t double_conv_init[] = { + { 5, -5, 0x80000000, 0x7fffffff}, //int + { 0x3fc00000, 0xbfc00000, 0x7149f2ca, 0xf149f2ca}, //float 1e30, -1e30 + { 99, 0x80000000, 0x80000000, 99}, //long + { 256, 0, 0x7fffffff, 0}, //long + { 0x39a08cea, 0x46293e59, 0x39a08cea, 0xc6293e59}, //double 1e30, -1e30 + { 0, 0x3ff80000, 0, 0xbff80000}, //double 1.5, -1.5 + { 5, -5, 0x80000000, 0x7fffffff}, //uint + { ~0, 1, 0x80000000, 0}, //bool32 + { 99, 0x80000000, 0x80000000, 99}, //ulong + { 256, 0, 0x7fffffff, 0}, //ulong + { ~0, ~0, ~0, 0}, //bool64 + { 0, ~0, 0, 0}, //bool64 + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +static pr_ivec4_t double_conv_expect[] = { + { 5, -5, 0x80000000, 0x7fffffff}, //int + { 0x3fc00000, 0xbfc00000, 0x7149f2ca, 0xf149f2ca}, //float + { 99, 0x80000000, 0x80000000, 99}, //long + { 256, 0, 0x7fffffff, 0}, //long + { 0x39a08cea, 0x46293e59, 0x39a08cea, 0xc6293e59}, //double 1e30, -1e30 + { 0, 0x3ff80000, 0, 0xbff80000}, //double 1.5, -1.5 + { 5, -5, 0x80000000, 0x7fffffff}, //uint + { ~0, 1, 0x80000000, 0}, //bool32 + { 99, 0x80000000, 0x80000000, 99}, //ulong + { 256, 0, 0x7fffffff, 0}, //ulong + { ~0, ~0, ~0, 0}, //bool64 + { 0, ~0, 0, 0}, //bool64 + { 0x00000000, 0x40140000, 0x00000000, 0xc0140000}, // int + { 0x00000000, 0xc1e00000, 0xffc00000, 0x41dfffff}, + { 0x00000000, 0x3ff80000, 0x00000000, 0xbff80000}, // float + { 0x40000000, 0x46293e59, 0x40000000, 0xc6293e59}, + { 0x00000000, 0xc3e00000, 0x00000000, 0x4258e000}, // long + { 0x00000000, 0x40700000, 0xffc00000, 0x41dfffff}, + { 0x39a08cea, 0x46293e59, 0x39a08cea, 0xc6293e59}, // double + { 0, 0x3ff80000, 0, 0xbff80000}, + { 0x00000000, 0x40140000, 0xff600000, 0x41efffff}, // uint + { 0x00000000, 0x41e00000, 0xffc00000, 0x41dfffff}, + { 0x00000000, 0x3ff00000, 0x00000000, 0x3ff00000}, // bool32 + { 0x00000000, 0x3ff00000, 0x00000000, 0x00000000}, + { 0x00000000, 0x43e00000, 0x00000000, 0x4258e000}, // long + { 0x00000000, 0x40700000, 0xffc00000, 0x41dfffff}, + { 0x00000000, 0x3ff00000, 0x00000000, 0x3ff00000}, // bool64 + { 0x00000000, 0x3ff00000, 0x00000000, 0x00000000}, +}; + +static dstatement_t double_conv_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 112 }, // init index + { OP(0, 0, 0, OP_LEA_A), 8, 0, 113 }, // init index for 64-bits +//loop: + { OP(0, 0, 0, OP_LEA_C), 112, -1, 112 }, // dec index + { OP(0, 0, 0, OP_LEA_C), 113, -2, 113 }, // dec index for 64-bits + { OP(0, 0, 0, OP_IFAE), 2, 0, 112 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 112, 1 }, + { OP(0, 0, 0, OP_WITH), 4, 113, 2 }, + { OP(1, 1, 2, OP_CONV), 0, 0003, 48 }, + { OP(1, 1, 2, OP_CONV), 4, 0013, 56 }, + { OP(2, 1, 2, OP_CONV), 8, 0023, 64 }, + { OP(2, 1, 2, OP_CONV), 16, 0033, 72 }, + { OP(1, 1, 2, OP_CONV), 24, 0043, 80 }, + { OP(1, 1, 2, OP_CONV), 28, 0053, 88 }, + { OP(2, 1, 2, OP_CONV), 32, 0063, 96 }, + { OP(2, 1, 2, OP_CONV), 40, 0073, 104 }, + { OP(0, 0, 0, OP_JUMP_A), -14, 0, 0 }, +}; + +static dstatement_t double_conv_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 112 }, // index + { OP(0, 0, 0, OP_LEA_A), 8, 0, 113 }, // init index for 64-bits +//loop: + { OP(0, 0, 0, OP_LEA_C), 112, -2, 112 }, // dec index + { OP(0, 0, 0, OP_LEA_C), 113, -4, 113 }, // dec index for 64-bits + { OP(0, 0, 0, OP_IFAE), 2, 0, 112 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 112, 1 }, + { OP(0, 0, 0, OP_WITH), 4, 113, 2 }, + { OP(1, 1, 2, OP_CONV), 0, 0103, 48 }, + { OP(1, 1, 2, OP_CONV), 4, 0113, 56 }, + { OP(2, 1, 2, OP_CONV), 8, 0123, 64 }, + { OP(2, 1, 2, OP_CONV), 16, 0133, 72 }, + { OP(1, 1, 2, OP_CONV), 24, 0143, 80 }, + { OP(1, 1, 2, OP_CONV), 28, 0153, 88 }, + { OP(2, 1, 2, OP_CONV), 32, 0163, 96 }, + { OP(2, 1, 2, OP_CONV), 40, 0173, 104 }, + { OP(0, 0, 0, OP_JUMP_A), -14, 0, 0 }, +}; + +static dstatement_t double_conv_3a_statements[] = { + { OP(1, 1, 2, OP_CONV), 0, 0203, 48 }, + { OP(1, 1, 2, OP_CONV), 3, 0003, 54 }, + { OP(1, 1, 2, OP_CONV), 4, 0213, 56 }, + { OP(1, 1, 2, OP_CONV), 7, 0013, 62 }, + { OP(2, 1, 2, OP_CONV), 8, 0223, 64 }, + { OP(2, 1, 2, OP_CONV), 14, 0023, 70 }, + { OP(2, 1, 2, OP_CONV), 16, 0233, 72 }, + { OP(2, 1, 2, OP_CONV), 22, 0033, 78 }, + { OP(1, 1, 2, OP_CONV), 24, 0243, 80 }, + { OP(1, 1, 2, OP_CONV), 27, 0043, 86 }, + { OP(1, 1, 2, OP_CONV), 28, 0253, 88 }, + { OP(1, 1, 2, OP_CONV), 31, 0053, 94 }, + { OP(2, 1, 2, OP_CONV), 32, 0263, 96 }, + { OP(2, 1, 2, OP_CONV), 38, 0063, 102 }, + { OP(2, 1, 2, OP_CONV), 40, 0273, 104 }, + { OP(2, 1, 2, OP_CONV), 46, 0073, 110 }, +}; + +static dstatement_t double_conv_3b_statements[] = { + { OP(1, 1, 2, OP_CONV), 0, 0003, 48 }, + { OP(1, 1, 2, OP_CONV), 1, 0203, 50 }, + { OP(1, 1, 2, OP_CONV), 4, 0013, 56 }, + { OP(1, 1, 2, OP_CONV), 5, 0213, 58 }, + { OP(2, 1, 2, OP_CONV), 8, 0023, 64 }, + { OP(2, 1, 2, OP_CONV), 10, 0223, 66 }, + { OP(2, 1, 2, OP_CONV), 16, 0033, 72 }, + { OP(2, 1, 2, OP_CONV), 18, 0233, 74 }, + { OP(1, 1, 2, OP_CONV), 24, 0043, 80 }, + { OP(1, 1, 2, OP_CONV), 25, 0243, 82 }, + { OP(1, 1, 2, OP_CONV), 28, 0053, 88 }, + { OP(1, 1, 2, OP_CONV), 29, 0253, 90 }, + { OP(2, 1, 2, OP_CONV), 32, 0063, 96 }, + { OP(2, 1, 2, OP_CONV), 34, 0263, 98 }, + { OP(2, 1, 2, OP_CONV), 40, 0073, 104 }, + { OP(2, 1, 2, OP_CONV), 42, 0273, 106 }, +}; + +static dstatement_t double_conv_4_statements[] = { + { OP(1, 1, 2, OP_CONV), 0, 0303, 48 }, + { OP(1, 1, 2, OP_CONV), 4, 0313, 56 }, + { OP(2, 1, 2, OP_CONV), 8, 0323, 64 }, + { OP(2, 1, 2, OP_CONV), 16, 0333, 72 }, + { OP(1, 1, 2, OP_CONV), 24, 0343, 80 }, + { OP(1, 1, 2, OP_CONV), 28, 0353, 88 }, + { OP(2, 1, 2, OP_CONV), 32, 0363, 96 }, + { OP(2, 1, 2, OP_CONV), 40, 0373, 104 }, +}; + +test_t tests[] = { + { + .desc = "double conv 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(double_conv_init,double_conv_expect), + .num_statements = num_statements (double_conv_1_statements), + .statements = double_conv_1_statements, + .init_globals = (pr_int_t *) double_conv_init, + .expect_globals = (pr_int_t *) double_conv_expect, + }, + { + .desc = "double conv 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(double_conv_init,double_conv_expect), + .num_statements = num_statements (double_conv_2_statements), + .statements = double_conv_2_statements, + .init_globals = (pr_int_t *) double_conv_init, + .expect_globals = (pr_int_t *) double_conv_expect, + }, + { + .desc = "double conv 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(double_conv_init,double_conv_expect), + .num_statements = num_statements (double_conv_3a_statements), + .statements = double_conv_3a_statements, + .init_globals = (pr_int_t *) double_conv_init, + .expect_globals = (pr_int_t *) double_conv_expect, + }, + { + .desc = "double conv 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(double_conv_init,double_conv_expect), + .num_statements = num_statements (double_conv_3b_statements), + .statements = double_conv_3b_statements, + .init_globals = (pr_int_t *) double_conv_init, + .expect_globals = (pr_int_t *) double_conv_expect, + }, + { + .desc = "double conv 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(double_conv_init,double_conv_expect), + .num_statements = num_statements (double_conv_4_statements), + .statements = double_conv_4_statements, + .init_globals = (pr_int_t *) double_conv_init, + .expect_globals = (pr_int_t *) double_conv_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-conv4.c b/libs/gamecode/test/test-conv4.c new file mode 100644 index 000000000..ed455b02f --- /dev/null +++ b/libs/gamecode/test/test-conv4.c @@ -0,0 +1,190 @@ +#include "head.c" + +#include "QF/mathlib.h" + +static pr_ivec4_t uint_conv_init[] = { + { 5, -5, 0x80000000, 0x7fffffff}, //int + { 0x3fc00000, 0x40100000, 0x40700000, 0x40a00000}, //float 1.5, 2.25, 3.75, 5 + { 99, 0x80000000, 0x80000000, 99}, //long + { 256, 0, 0x7fffffff, 0}, //long + { 0, 0x3ff80000, 0, 0x40020000}, //double 1.5, 2.25, 3.75, 5 + { 0, 0x400e0000, 0, 0x40140000}, + { 5, -5, 0x80000000, 0x7fffffff}, //uint + { ~0, 1, 0x80000000, 0}, //bool32 + { 99, 0x80000000, 0x80000000, 99}, //ulong + { 256, 0, 0x7fffffff, 0}, //ulong + { ~0, ~0, ~0, 0}, //bool64 + { 0, ~0, 0, 0}, //bool64 + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +static pr_ivec4_t uint_conv_expect[] = { + { 5, -5, 0x80000000, 0x7fffffff}, //int + { 0x3fc00000, 0x40100000, 0x40700000, 0x40a00000}, //float 1.5, 2.25, 3.75, 5 + { 99, 0x80000000, 0x80000000, 99}, //long + { 256, 0, 0x7fffffff, 0}, //long + { 0, 0x3ff80000, 0, 0x40020000}, //double 1.5, 2.25, 3.75, 5 + { 0, 0x400e0000, 0, 0x40140000}, + { 5, -5, 0x80000000, 0x7fffffff}, //uint + { ~0, 1, 0x80000000, 0}, //bool32 + { 99, 0x80000000, 0x80000000, 99}, //ulong + { 256, 0, 0x7fffffff, 0}, //ulong + { ~0, ~0, ~0, 0}, //bool64 + { 0, ~0, 0, 0}, //bool64 + { 5, -5, 0x80000000, 0x7fffffff}, // int + { 1, 2, 3, 5}, // float + { 99, 0x80000000, 256, 0x7fffffff}, // long + { 1, 2, 3, 5}, // double + { 5, -5, 0x80000000, 0x7fffffff}, // uint + { 1, 1, 1, 0}, // bool32 + { 99, 0x80000000, 256, 0x7fffffff}, // ulong + { 1, 1, 1, 0}, // bool64 +}; + +static dstatement_t uint_conv_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 80 }, // init index + { OP(0, 0, 0, OP_LEA_A), 8, 0, 81 }, // init index for 64-bits +//loop: + { OP(0, 0, 0, OP_LEA_C), 80, -1, 80 }, // dec index + { OP(0, 0, 0, OP_LEA_C), 81, -2, 81 }, // dec index for 64-bits + { OP(0, 0, 0, OP_IFAE), 2, 0, 80 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 80, 1 }, + { OP(0, 0, 0, OP_WITH), 4, 81, 2 }, + { OP(1, 1, 1, OP_CONV), 0, 0004, 48 }, + { OP(1, 1, 1, OP_CONV), 4, 0014, 52 }, + { OP(2, 1, 1, OP_CONV), 8, 0024, 56 }, + { OP(2, 1, 1, OP_CONV), 16, 0034, 60 }, + { OP(1, 1, 1, OP_CONV), 24, 0044, 64 }, + { OP(1, 1, 1, OP_CONV), 28, 0054, 68 }, + { OP(2, 1, 1, OP_CONV), 32, 0064, 72 }, + { OP(2, 1, 1, OP_CONV), 40, 0074, 76 }, + { OP(1, 1, 1, OP_JUMP_A), -14, 0, 0 }, +}; + +static dstatement_t uint_conv_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 80 }, // index + { OP(0, 0, 0, OP_LEA_A), 8, 0, 81 }, // init index for 64-bits +//loop: + { OP(0, 0, 0, OP_LEA_C), 80, -2, 80 }, // dec index + { OP(0, 0, 0, OP_LEA_C), 81, -4, 81 }, // dec index for 64-bits + { OP(0, 0, 0, OP_IFAE), 2, 0, 80 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 80, 1 }, + { OP(0, 0, 0, OP_WITH), 4, 81, 2 }, + { OP(1, 1, 1, OP_CONV), 0, 0104, 48 }, + { OP(1, 1, 1, OP_CONV), 4, 0114, 52 }, + { OP(2, 1, 1, OP_CONV), 8, 0124, 56 }, + { OP(2, 1, 1, OP_CONV), 16, 0134, 60 }, + { OP(1, 1, 1, OP_CONV), 24, 0144, 64 }, + { OP(1, 1, 1, OP_CONV), 28, 0154, 68 }, + { OP(2, 1, 1, OP_CONV), 32, 0164, 72 }, + { OP(2, 1, 1, OP_CONV), 40, 0174, 76 }, + { OP(1, 1, 1, OP_JUMP_A), -14, 0, 0 }, +}; + +static dstatement_t uint_conv_3a_statements[] = { + { OP(1, 1, 1, OP_CONV), 0, 0204, 48 }, + { OP(1, 1, 1, OP_CONV), 3, 0004, 51 }, + { OP(1, 1, 1, OP_CONV), 4, 0214, 52 }, + { OP(1, 1, 1, OP_CONV), 7, 0014, 55 }, + { OP(2, 1, 1, OP_CONV), 8, 0224, 56 }, + { OP(2, 1, 1, OP_CONV), 14, 0024, 59 }, + { OP(2, 1, 1, OP_CONV), 16, 0234, 60 }, + { OP(2, 1, 1, OP_CONV), 22, 0034, 63 }, + { OP(1, 1, 1, OP_CONV), 24, 0244, 64 }, + { OP(1, 1, 1, OP_CONV), 27, 0044, 67 }, + { OP(1, 1, 1, OP_CONV), 28, 0254, 68 }, + { OP(1, 1, 1, OP_CONV), 31, 0054, 71 }, + { OP(2, 1, 1, OP_CONV), 32, 0264, 72 }, + { OP(2, 1, 1, OP_CONV), 38, 0064, 75 }, + { OP(2, 1, 1, OP_CONV), 40, 0274, 76 }, + { OP(2, 1, 1, OP_CONV), 46, 0074, 79 }, +}; + +static dstatement_t uint_conv_3b_statements[] = { + { OP(1, 1, 1, OP_CONV), 0, 0004, 48 }, + { OP(1, 1, 1, OP_CONV), 1, 0204, 49 }, + { OP(1, 1, 1, OP_CONV), 4, 0014, 52 }, + { OP(1, 1, 1, OP_CONV), 5, 0214, 53 }, + { OP(2, 1, 1, OP_CONV), 8, 0024, 56 }, + { OP(2, 1, 1, OP_CONV), 10, 0224, 57 }, + { OP(2, 1, 1, OP_CONV), 16, 0034, 60 }, + { OP(2, 1, 1, OP_CONV), 18, 0234, 61 }, + { OP(1, 1, 1, OP_CONV), 24, 0044, 64 }, + { OP(1, 1, 1, OP_CONV), 25, 0244, 65 }, + { OP(1, 1, 1, OP_CONV), 28, 0054, 68 }, + { OP(1, 1, 1, OP_CONV), 29, 0254, 69 }, + { OP(2, 1, 1, OP_CONV), 32, 0064, 72 }, + { OP(2, 1, 1, OP_CONV), 34, 0264, 73 }, + { OP(2, 1, 1, OP_CONV), 40, 0074, 76 }, + { OP(2, 1, 1, OP_CONV), 42, 0274, 77 }, +}; + +static dstatement_t uint_conv_4_statements[] = { + { OP(1, 1, 1, OP_CONV), 0, 0304, 48 }, + { OP(1, 1, 1, OP_CONV), 4, 0314, 52 }, + { OP(2, 1, 1, OP_CONV), 8, 0324, 56 }, + { OP(2, 1, 1, OP_CONV), 16, 0334, 60 }, + { OP(1, 1, 1, OP_CONV), 24, 0344, 64 }, + { OP(1, 1, 1, OP_CONV), 28, 0354, 68 }, + { OP(2, 1, 1, OP_CONV), 32, 0364, 72 }, + { OP(2, 1, 1, OP_CONV), 40, 0374, 76 }, +}; + +test_t tests[] = { + { + .desc = "uint conv 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(uint_conv_init,uint_conv_expect), + .num_statements = num_statements (uint_conv_1_statements), + .statements = uint_conv_1_statements, + .init_globals = (pr_int_t *) uint_conv_init, + .expect_globals = (pr_int_t *) uint_conv_expect, + }, + { + .desc = "uint conv 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(uint_conv_init,uint_conv_expect), + .num_statements = num_statements (uint_conv_2_statements), + .statements = uint_conv_2_statements, + .init_globals = (pr_int_t *) uint_conv_init, + .expect_globals = (pr_int_t *) uint_conv_expect, + }, + { + .desc = "uint conv 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(uint_conv_init,uint_conv_expect), + .num_statements = num_statements (uint_conv_3a_statements), + .statements = uint_conv_3a_statements, + .init_globals = (pr_int_t *) uint_conv_init, + .expect_globals = (pr_int_t *) uint_conv_expect, + }, + { + .desc = "uint conv 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(uint_conv_init,uint_conv_expect), + .num_statements = num_statements (uint_conv_3b_statements), + .statements = uint_conv_3b_statements, + .init_globals = (pr_int_t *) uint_conv_init, + .expect_globals = (pr_int_t *) uint_conv_expect, + }, + { + .desc = "uint conv 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(uint_conv_init,uint_conv_expect), + .num_statements = num_statements (uint_conv_4_statements), + .statements = uint_conv_4_statements, + .init_globals = (pr_int_t *) uint_conv_init, + .expect_globals = (pr_int_t *) uint_conv_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-conv5.c b/libs/gamecode/test/test-conv5.c new file mode 100644 index 000000000..fe75e0ff2 --- /dev/null +++ b/libs/gamecode/test/test-conv5.c @@ -0,0 +1,190 @@ +#include "head.c" + +#include "QF/mathlib.h" + +static pr_ivec4_t bool32_conv_init[] = { + { 5, -5, 0x80000000, 0x7fffffff}, //int + { 0x3fc00000, 0xbfc00000, 0x7149f2ca, 0xf149f2ca}, //float 1e30, -1e30 + { 99, 0x80000000, 0x80000000, 99}, //long + { 256, 0, 0x7fffffff, 0}, //long + { 0x39a08cea, 0x46293e59, 0x39a08cea, 0xc6293e59}, //double 1e30, -1e30 + { 0, 0x3ff80000, 0, 0xbff80000}, //double 1.5, -1.5 + { 5, -5, 0x80000000, 0x7fffffff}, //uint + { ~0, 1, 0x80000000, 0}, //bool32 + { 99, 0x80000000, 0x80000000, 99}, //ulong + { 256, 0, 0x7fffffff, 0}, //ulong + { ~0, ~0, ~0, 0}, //bool64 + { 0, ~0, 0, 0}, //bool64 + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +static pr_ivec4_t bool32_conv_expect[] = { + { 5, -5, 0x80000000, 0x7fffffff}, //int + { 0x3fc00000, 0xbfc00000, 0x7149f2ca, 0xf149f2ca}, //float + { 99, 0x80000000, 0x80000000, 99}, //long + { 256, 0, 0x7fffffff, 0}, //long + { 0x39a08cea, 0x46293e59, 0x39a08cea, 0xc6293e59}, //double 1e30, -1e30 + { 0, 0x3ff80000, 0, 0xbff80000}, //double 1.5, -1.5 + { 5, -5, 0x80000000, 0x7fffffff}, //uint + { ~0, 1, 0x80000000, 0}, //bool32 + { 99, 0x80000000, 0x80000000, 99}, //ulong + { 256, 0, 0x7fffffff, 0}, //ulong + { ~0, ~0, ~0, 0}, //bool64 + { 0, ~0, 0, 0}, //bool64 + { -1, -1, -1, -1}, // int + { -1, -1, -1, -1}, // float + { -1, -1, -1, -1}, // long + { -1, -1, -1, -1}, // double + { -1, -1, -1, -1}, // uint + { ~0, 1, 0x80000000, 0}, // bool32 + { -1, -1, -1, -1}, // ulong + { -1, -1, -1, 0}, // bool64 +}; + +static dstatement_t bool32_conv_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 80 }, // init index + { OP(0, 0, 0, OP_LEA_A), 8, 0, 81 }, // init index for 64-bits +//loop: + { OP(0, 0, 0, OP_LEA_C), 80, -1, 80 }, // dec index + { OP(0, 0, 0, OP_LEA_C), 81, -2, 81 }, // dec index for 64-bits + { OP(0, 0, 0, OP_IFAE), 2, 0, 80 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 80, 1 }, + { OP(0, 0, 0, OP_WITH), 4, 81, 2 }, + { OP(1, 1, 1, OP_CONV), 0, 0005, 48 }, + { OP(1, 1, 1, OP_CONV), 4, 0015, 52 }, + { OP(2, 1, 1, OP_CONV), 8, 0025, 56 }, + { OP(2, 1, 1, OP_CONV), 16, 0035, 60 }, + { OP(1, 1, 1, OP_CONV), 24, 0045, 64 }, + { OP(1, 1, 1, OP_CONV), 28, 0055, 68 }, + { OP(2, 1, 1, OP_CONV), 32, 0065, 72 }, + { OP(2, 1, 1, OP_CONV), 40, 0075, 76 }, + { OP(1, 1, 1, OP_JUMP_A), -14, 0, 0 }, +}; + +static dstatement_t bool32_conv_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 80 }, // index + { OP(0, 0, 0, OP_LEA_A), 8, 0, 81 }, // init index for 64-bits +//loop: + { OP(0, 0, 0, OP_LEA_C), 80, -2, 80 }, // dec index + { OP(0, 0, 0, OP_LEA_C), 81, -4, 81 }, // dec index for 64-bits + { OP(0, 0, 0, OP_IFAE), 2, 0, 80 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 80, 1 }, + { OP(0, 0, 0, OP_WITH), 4, 81, 2 }, + { OP(1, 1, 1, OP_CONV), 0, 0105, 48 }, + { OP(1, 1, 1, OP_CONV), 4, 0115, 52 }, + { OP(2, 1, 1, OP_CONV), 8, 0125, 56 }, + { OP(2, 1, 1, OP_CONV), 16, 0135, 60 }, + { OP(1, 1, 1, OP_CONV), 24, 0145, 64 }, + { OP(1, 1, 1, OP_CONV), 28, 0155, 68 }, + { OP(2, 1, 1, OP_CONV), 32, 0165, 72 }, + { OP(2, 1, 1, OP_CONV), 40, 0175, 76 }, + { OP(1, 1, 1, OP_JUMP_A), -14, 0, 0 }, +}; + +static dstatement_t bool32_conv_3a_statements[] = { + { OP(1, 1, 1, OP_CONV), 0, 0205, 48 }, + { OP(1, 1, 1, OP_CONV), 3, 0005, 51 }, + { OP(1, 1, 1, OP_CONV), 4, 0215, 52 }, + { OP(1, 1, 1, OP_CONV), 7, 0015, 55 }, + { OP(2, 1, 1, OP_CONV), 8, 0225, 56 }, + { OP(2, 1, 1, OP_CONV), 14, 0025, 59 }, + { OP(2, 1, 1, OP_CONV), 16, 0235, 60 }, + { OP(2, 1, 1, OP_CONV), 22, 0035, 63 }, + { OP(1, 1, 1, OP_CONV), 24, 0245, 64 }, + { OP(1, 1, 1, OP_CONV), 27, 0045, 67 }, + { OP(1, 1, 1, OP_CONV), 28, 0255, 68 }, + { OP(1, 1, 1, OP_CONV), 31, 0055, 71 }, + { OP(2, 1, 1, OP_CONV), 32, 0265, 72 }, + { OP(2, 1, 1, OP_CONV), 38, 0065, 75 }, + { OP(2, 1, 1, OP_CONV), 40, 0275, 76 }, + { OP(2, 1, 1, OP_CONV), 46, 0075, 79 }, +}; + +static dstatement_t bool32_conv_3b_statements[] = { + { OP(1, 1, 1, OP_CONV), 0, 0005, 48 }, + { OP(1, 1, 1, OP_CONV), 1, 0205, 49 }, + { OP(1, 1, 1, OP_CONV), 4, 0015, 52 }, + { OP(1, 1, 1, OP_CONV), 5, 0215, 53 }, + { OP(2, 1, 1, OP_CONV), 8, 0025, 56 }, + { OP(2, 1, 1, OP_CONV), 10, 0225, 57 }, + { OP(2, 1, 1, OP_CONV), 16, 0035, 60 }, + { OP(2, 1, 1, OP_CONV), 18, 0235, 61 }, + { OP(1, 1, 1, OP_CONV), 24, 0045, 64 }, + { OP(1, 1, 1, OP_CONV), 25, 0245, 65 }, + { OP(1, 1, 1, OP_CONV), 28, 0055, 68 }, + { OP(1, 1, 1, OP_CONV), 29, 0255, 69 }, + { OP(2, 1, 1, OP_CONV), 32, 0065, 72 }, + { OP(2, 1, 1, OP_CONV), 34, 0265, 73 }, + { OP(2, 1, 1, OP_CONV), 40, 0075, 76 }, + { OP(2, 1, 1, OP_CONV), 42, 0275, 77 }, +}; + +static dstatement_t bool32_conv_4_statements[] = { + { OP(1, 1, 1, OP_CONV), 0, 0305, 48 }, + { OP(1, 1, 1, OP_CONV), 4, 0315, 52 }, + { OP(2, 1, 1, OP_CONV), 8, 0325, 56 }, + { OP(2, 1, 1, OP_CONV), 16, 0335, 60 }, + { OP(1, 1, 1, OP_CONV), 24, 0345, 64 }, + { OP(1, 1, 1, OP_CONV), 28, 0355, 68 }, + { OP(2, 1, 1, OP_CONV), 32, 0365, 72 }, + { OP(2, 1, 1, OP_CONV), 40, 0375, 76 }, +}; + +test_t tests[] = { + { + .desc = "bool32 conv 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(bool32_conv_init,bool32_conv_expect), + .num_statements = num_statements (bool32_conv_1_statements), + .statements = bool32_conv_1_statements, + .init_globals = (pr_int_t *) bool32_conv_init, + .expect_globals = (pr_int_t *) bool32_conv_expect, + }, + { + .desc = "bool32 conv 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(bool32_conv_init,bool32_conv_expect), + .num_statements = num_statements (bool32_conv_2_statements), + .statements = bool32_conv_2_statements, + .init_globals = (pr_int_t *) bool32_conv_init, + .expect_globals = (pr_int_t *) bool32_conv_expect, + }, + { + .desc = "bool32 conv 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(bool32_conv_init,bool32_conv_expect), + .num_statements = num_statements (bool32_conv_3a_statements), + .statements = bool32_conv_3a_statements, + .init_globals = (pr_int_t *) bool32_conv_init, + .expect_globals = (pr_int_t *) bool32_conv_expect, + }, + { + .desc = "bool32 conv 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(bool32_conv_init,bool32_conv_expect), + .num_statements = num_statements (bool32_conv_3b_statements), + .statements = bool32_conv_3b_statements, + .init_globals = (pr_int_t *) bool32_conv_init, + .expect_globals = (pr_int_t *) bool32_conv_expect, + }, + { + .desc = "bool32 conv 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(bool32_conv_init,bool32_conv_expect), + .num_statements = num_statements (bool32_conv_4_statements), + .statements = bool32_conv_4_statements, + .init_globals = (pr_int_t *) bool32_conv_init, + .expect_globals = (pr_int_t *) bool32_conv_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-conv6.c b/libs/gamecode/test/test-conv6.c new file mode 100644 index 000000000..5cfaab926 --- /dev/null +++ b/libs/gamecode/test/test-conv6.c @@ -0,0 +1,211 @@ +#include "head.c" + +#include "QF/mathlib.h" + +static pr_ivec4_t ulong_conv_init[] = { + { 5, -5, 0x80000000, 0x7fffffff}, //int + { 0x3fc00000, 0x40100000, 0x40700000, 0x40a00000}, //float 1.5, 2.25, 3.75, 5 + { 99, 0x80000000, 0x80000000, 99}, //long + { 256, 0, 0x7fffffff, 0}, //long + { 0, 0x3ff80000, 0, 0x40020000}, //double 1.5, 2.25, 3.75, 5 + { 0, 0x400e0000, 0, 0x40140000}, + { 5, -5, 0x80000000, 0x7fffffff}, //uint + { ~0, 1, 0x80000000, 0}, //bool32 + { 99, 0x80000000, 0x80000000, 99}, //ulong + { 256, 0, 0x7fffffff, 0}, //ulong + { ~0, ~0, ~0, 0}, //bool64 + { 0, ~0, 0, 0}, //bool64 + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +static pr_ivec4_t ulong_conv_expect[] = { + { 5, -5, 0x80000000, 0x7fffffff}, //int + //XXX{ 0x3fc00000, 0xbfc00000, 0x7149f2ca, 0xf149f2ca}, //float 1e30, -1e30 + { 0x3fc00000, 0x40100000, 0x40700000, 0x40a00000}, //float 1.5, 2.25, 3.75, 5 + { 99, 0x80000000, 0x80000000, 99}, //long + { 256, 0, 0x7fffffff, 0}, //long + + { 0, 0x3ff80000, 0, 0x40020000}, //double 1.5, 2.25, 3.75, 5 + { 0, 0x400e0000, 0, 0x40140000}, + { 5, -5, 0x80000000, 0x7fffffff}, //uint + { ~0, 1, 0x80000000, 0}, //bool32 + + { 99, 0x80000000, 0x80000000, 99}, //ulong + { 256, 0, 0x7fffffff, 0}, //ulong + { ~0, ~0, ~0, 0}, //bool64 + { 0, ~0, 0, 0}, //bool64 + + { 5, 0, -5, 0xffffffff}, // int + { 0x80000000, 0xffffffff, 0x7fffffff, 0}, + { 1, 0, 2, 0}, // float + { 3, 0, 5, 0}, + + { 99, 0x80000000, 0x80000000, 99}, // long + { 256, 0, 0x7fffffff, 0}, + { 1, 0, 2, 0}, // double + { 3, 0, 5, 0}, + { 5, 0, -5, 0}, // uint + { 0x80000000, 0, 0x7fffffff, 0}, + { 1, 0, 1, 0}, // bool32 + { 1, 0, 0, 0}, + { 99, 0x80000000, 0x80000000, 99}, // ulong + { 256, 0, 0x7fffffff, 0}, + { 1, 0, 1, 0}, // bool64 + { 1, 0, 0, 0}, +}; + +static dstatement_t ulong_conv_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 112 }, // init index + { OP(0, 0, 0, OP_LEA_A), 8, 0, 113 }, // init index for 64-bits +//loop: + { OP(0, 0, 0, OP_LEA_C), 112, -1, 112 }, // dec index + { OP(0, 0, 0, OP_LEA_C), 113, -2, 113 }, // dec index for 64-bits + { OP(0, 0, 0, OP_IFAE), 2, 0, 112 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 112, 1 }, + { OP(0, 0, 0, OP_WITH), 4, 113, 2 }, + { OP(1, 1, 2, OP_CONV), 0, 0006, 48 }, + { OP(1, 1, 2, OP_CONV), 4, 0016, 56 }, + { OP(2, 1, 2, OP_CONV), 8, 0026, 64 }, + { OP(2, 1, 2, OP_CONV), 16, 0036, 72 }, + { OP(1, 1, 2, OP_CONV), 24, 0046, 80 }, + { OP(1, 1, 2, OP_CONV), 28, 0056, 88 }, + { OP(2, 1, 2, OP_CONV), 32, 0066, 96 }, + { OP(2, 1, 2, OP_CONV), 40, 0076, 104 }, + { OP(0, 0, 0, OP_JUMP_A), -14, 0, 0 }, +}; + +static dstatement_t ulong_conv_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 112 }, // index + { OP(0, 0, 0, OP_LEA_A), 8, 0, 113 }, // init index for 64-bits +//loop: + { OP(0, 0, 0, OP_LEA_C), 112, -2, 112 }, // dec index + { OP(0, 0, 0, OP_LEA_C), 113, -4, 113 }, // dec index for 64-bits + { OP(0, 0, 0, OP_IFAE), 2, 0, 112 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 112, 1 }, + { OP(0, 0, 0, OP_WITH), 4, 113, 2 }, + { OP(1, 1, 2, OP_CONV), 0, 0106, 48 }, + { OP(1, 1, 2, OP_CONV), 4, 0116, 56 }, + { OP(2, 1, 2, OP_CONV), 8, 0126, 64 }, + { OP(2, 1, 2, OP_CONV), 16, 0136, 72 }, + { OP(1, 1, 2, OP_CONV), 24, 0146, 80 }, + { OP(1, 1, 2, OP_CONV), 28, 0156, 88 }, + { OP(2, 1, 2, OP_CONV), 32, 0166, 96 }, + { OP(2, 1, 2, OP_CONV), 40, 0176, 104 }, + { OP(0, 0, 0, OP_JUMP_A), -14, 0, 0 }, +}; + +static dstatement_t ulong_conv_3a_statements[] = { + { OP(1, 1, 2, OP_CONV), 0, 0206, 48 }, + { OP(1, 1, 2, OP_CONV), 3, 0006, 54 }, + { OP(1, 1, 2, OP_CONV), 4, 0216, 56 }, + { OP(1, 1, 2, OP_CONV), 7, 0016, 62 }, + { OP(2, 1, 2, OP_CONV), 8, 0226, 64 }, + { OP(2, 1, 2, OP_CONV), 14, 0026, 70 }, + { OP(2, 1, 2, OP_CONV), 16, 0236, 72 }, + { OP(2, 1, 2, OP_CONV), 22, 0036, 78 }, + { OP(1, 1, 2, OP_CONV), 24, 0246, 80 }, + { OP(1, 1, 2, OP_CONV), 27, 0046, 86 }, + { OP(1, 1, 2, OP_CONV), 28, 0256, 88 }, + { OP(1, 1, 2, OP_CONV), 31, 0056, 94 }, + { OP(2, 1, 2, OP_CONV), 32, 0266, 96 }, + { OP(2, 1, 2, OP_CONV), 38, 0066, 102 }, + { OP(2, 1, 2, OP_CONV), 40, 0276, 104 }, + { OP(2, 1, 2, OP_CONV), 46, 0076, 110 }, +}; + +static dstatement_t ulong_conv_3b_statements[] = { + { OP(1, 1, 2, OP_CONV), 0, 0006, 48 }, + { OP(1, 1, 2, OP_CONV), 1, 0206, 50 }, + { OP(1, 1, 2, OP_CONV), 4, 0016, 56 }, + { OP(1, 1, 2, OP_CONV), 5, 0216, 58 }, + { OP(2, 1, 2, OP_CONV), 8, 0026, 64 }, + { OP(2, 1, 2, OP_CONV), 10, 0226, 66 }, + { OP(2, 1, 2, OP_CONV), 16, 0036, 72 }, + { OP(2, 1, 2, OP_CONV), 18, 0236, 74 }, + { OP(1, 1, 2, OP_CONV), 24, 0046, 80 }, + { OP(1, 1, 2, OP_CONV), 25, 0246, 82 }, + { OP(1, 1, 2, OP_CONV), 28, 0056, 88 }, + { OP(1, 1, 2, OP_CONV), 29, 0256, 90 }, + { OP(2, 1, 2, OP_CONV), 32, 0066, 96 }, + { OP(2, 1, 2, OP_CONV), 34, 0266, 98 }, + { OP(2, 1, 2, OP_CONV), 40, 0076, 104 }, + { OP(2, 1, 2, OP_CONV), 42, 0276, 106 }, +}; + +static dstatement_t ulong_conv_4_statements[] = { + { OP(1, 1, 2, OP_CONV), 0, 0306, 48 }, + { OP(1, 1, 2, OP_CONV), 4, 0316, 56 }, + { OP(2, 1, 2, OP_CONV), 8, 0326, 64 }, + { OP(2, 1, 2, OP_CONV), 16, 0336, 72 }, + { OP(1, 1, 2, OP_CONV), 24, 0346, 80 }, + { OP(1, 1, 2, OP_CONV), 28, 0356, 88 }, + { OP(2, 1, 2, OP_CONV), 32, 0366, 96 }, + { OP(2, 1, 2, OP_CONV), 40, 0376, 104 }, +}; + +test_t tests[] = { + { + .desc = "ulong conv 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(ulong_conv_init,ulong_conv_expect), + .num_statements = num_statements (ulong_conv_1_statements), + .statements = ulong_conv_1_statements, + .init_globals = (pr_int_t *) ulong_conv_init, + .expect_globals = (pr_int_t *) ulong_conv_expect, + }, + { + .desc = "ulong conv 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(ulong_conv_init,ulong_conv_expect), + .num_statements = num_statements (ulong_conv_2_statements), + .statements = ulong_conv_2_statements, + .init_globals = (pr_int_t *) ulong_conv_init, + .expect_globals = (pr_int_t *) ulong_conv_expect, + }, + { + .desc = "ulong conv 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(ulong_conv_init,ulong_conv_expect), + .num_statements = num_statements (ulong_conv_3a_statements), + .statements = ulong_conv_3a_statements, + .init_globals = (pr_int_t *) ulong_conv_init, + .expect_globals = (pr_int_t *) ulong_conv_expect, + }, + { + .desc = "ulong conv 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(ulong_conv_init,ulong_conv_expect), + .num_statements = num_statements (ulong_conv_3b_statements), + .statements = ulong_conv_3b_statements, + .init_globals = (pr_int_t *) ulong_conv_init, + .expect_globals = (pr_int_t *) ulong_conv_expect, + }, + { + .desc = "ulong conv 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(ulong_conv_init,ulong_conv_expect), + .num_statements = num_statements (ulong_conv_4_statements), + .statements = ulong_conv_4_statements, + .init_globals = (pr_int_t *) ulong_conv_init, + .expect_globals = (pr_int_t *) ulong_conv_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-conv7.c b/libs/gamecode/test/test-conv7.c new file mode 100644 index 000000000..cbc2306e0 --- /dev/null +++ b/libs/gamecode/test/test-conv7.c @@ -0,0 +1,206 @@ +#include "head.c" + +#include "QF/mathlib.h" + +static pr_ivec4_t bool64_conv_init[] = { + { 5, -5, 0x80000000, 0x7fffffff}, //int + { 0x3fc00000, 0xbfc00000, 0x7149f2ca, 0xf149f2ca}, //float 1e30, -1e30 + { 99, 0x80000000, 0x80000000, 99}, //long + { 256, 0, 0x7fffffff, 0}, //long + { 0x39a08cea, 0x46293e59, 0x39a08cea, 0xc6293e59}, //double 1e30, -1e30 + { 0, 0x3ff80000, 0, 0xbff80000}, //double 1.5, -1.5 + { 5, -5, 0x80000000, 0x7fffffff}, //uint + { ~0, 1, 0x80000000, 0}, //bool32 + { 99, 0x80000000, 0x80000000, 99}, //ulong + { 256, 0, 0x7fffffff, 0}, //ulong + { ~0, ~0, ~0, 0}, //bool64 + { 0, ~0, 0, 0}, //bool64 + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +static pr_ivec4_t bool64_conv_expect[] = { + { 5, -5, 0x80000000, 0x7fffffff}, //int + { 0x3fc00000, 0xbfc00000, 0x7149f2ca, 0xf149f2ca}, //float + { 99, 0x80000000, 0x80000000, 99}, //long + { 256, 0, 0x7fffffff, 0}, //long + { 0x39a08cea, 0x46293e59, 0x39a08cea, 0xc6293e59}, //double 1e30, -1e30 + { 0, 0x3ff80000, 0, 0xbff80000}, //double 1.5, -1.5 + { 5, -5, 0x80000000, 0x7fffffff}, //uint + { ~0, 1, 0x80000000, 0}, //bool32 + { 99, 0x80000000, 0x80000000, 99}, //ulong + { 256, 0, 0x7fffffff, 0}, //ulong + { ~0, ~0, ~0, 0}, //bool64 + { 0, ~0, 0, 0}, //bool64 + { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, // int + { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, + { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, // float + { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, + { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, // long + { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, + { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, // double + { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, + { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, // uint + { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, + { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, // bool32 + { 0xffffffff, 0xffffffff, 0, 0}, + { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, // ulong + { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, + { ~0, ~0, ~0, 0}, // bool64 + { 0, ~0, 0, 0}, +}; + +static dstatement_t bool64_conv_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 112 }, // init index + { OP(0, 0, 0, OP_LEA_A), 8, 0, 113 }, // init index for 64-bits +//loop: + { OP(0, 0, 0, OP_LEA_C), 112, -1, 112 }, // dec index + { OP(0, 0, 0, OP_LEA_C), 113, -2, 113 }, // dec index for 64-bits + { OP(0, 0, 0, OP_IFAE), 2, 0, 112 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 112, 1 }, + { OP(0, 0, 0, OP_WITH), 4, 113, 2 }, + { OP(1, 1, 2, OP_CONV), 0, 0007, 48 }, + { OP(1, 1, 2, OP_CONV), 4, 0017, 56 }, + { OP(2, 1, 2, OP_CONV), 8, 0027, 64 }, + { OP(2, 1, 2, OP_CONV), 16, 0037, 72 }, + { OP(1, 1, 2, OP_CONV), 24, 0047, 80 }, + { OP(1, 1, 2, OP_CONV), 28, 0057, 88 }, + { OP(2, 1, 2, OP_CONV), 32, 0067, 96 }, + { OP(2, 1, 2, OP_CONV), 40, 0077, 104 }, + { OP(0, 0, 0, OP_JUMP_A), -14, 0, 0 }, +}; + +static dstatement_t bool64_conv_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 112 }, // index + { OP(0, 0, 0, OP_LEA_A), 8, 0, 113 }, // init index for 64-bits +//loop: + { OP(0, 0, 0, OP_LEA_C), 112, -2, 112 }, // dec index + { OP(0, 0, 0, OP_LEA_C), 113, -4, 113 }, // dec index for 64-bits + { OP(0, 0, 0, OP_IFAE), 2, 0, 112 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 112, 1 }, + { OP(0, 0, 0, OP_WITH), 4, 113, 2 }, + { OP(1, 1, 2, OP_CONV), 0, 0107, 48 }, + { OP(1, 1, 2, OP_CONV), 4, 0117, 56 }, + { OP(2, 1, 2, OP_CONV), 8, 0127, 64 }, + { OP(2, 1, 2, OP_CONV), 16, 0137, 72 }, + { OP(1, 1, 2, OP_CONV), 24, 0147, 80 }, + { OP(1, 1, 2, OP_CONV), 28, 0157, 88 }, + { OP(2, 1, 2, OP_CONV), 32, 0167, 96 }, + { OP(2, 1, 2, OP_CONV), 40, 0177, 104 }, + { OP(0, 0, 0, OP_JUMP_A), -14, 0, 0 }, +}; + +static dstatement_t bool64_conv_3a_statements[] = { + { OP(1, 1, 2, OP_CONV), 0, 0207, 48 }, + { OP(1, 1, 2, OP_CONV), 3, 0007, 54 }, + { OP(1, 1, 2, OP_CONV), 4, 0217, 56 }, + { OP(1, 1, 2, OP_CONV), 7, 0017, 62 }, + { OP(2, 1, 2, OP_CONV), 8, 0227, 64 }, + { OP(2, 1, 2, OP_CONV), 14, 0027, 70 }, + { OP(2, 1, 2, OP_CONV), 16, 0237, 72 }, + { OP(2, 1, 2, OP_CONV), 22, 0037, 78 }, + { OP(1, 1, 2, OP_CONV), 24, 0247, 80 }, + { OP(1, 1, 2, OP_CONV), 27, 0047, 86 }, + { OP(1, 1, 2, OP_CONV), 28, 0257, 88 }, + { OP(1, 1, 2, OP_CONV), 31, 0057, 94 }, + { OP(2, 1, 2, OP_CONV), 32, 0267, 96 }, + { OP(2, 1, 2, OP_CONV), 38, 0067, 102 }, + { OP(2, 1, 2, OP_CONV), 40, 0277, 104 }, + { OP(2, 1, 2, OP_CONV), 46, 0077, 110 }, +}; + +static dstatement_t bool64_conv_3b_statements[] = { + { OP(1, 1, 2, OP_CONV), 0, 0007, 48 }, + { OP(1, 1, 2, OP_CONV), 1, 0207, 50 }, + { OP(1, 1, 2, OP_CONV), 4, 0017, 56 }, + { OP(1, 1, 2, OP_CONV), 5, 0217, 58 }, + { OP(2, 1, 2, OP_CONV), 8, 0027, 64 }, + { OP(2, 1, 2, OP_CONV), 10, 0227, 66 }, + { OP(2, 1, 2, OP_CONV), 16, 0037, 72 }, + { OP(2, 1, 2, OP_CONV), 18, 0237, 74 }, + { OP(1, 1, 2, OP_CONV), 24, 0047, 80 }, + { OP(1, 1, 2, OP_CONV), 25, 0247, 82 }, + { OP(1, 1, 2, OP_CONV), 28, 0057, 88 }, + { OP(1, 1, 2, OP_CONV), 29, 0257, 90 }, + { OP(2, 1, 2, OP_CONV), 32, 0067, 96 }, + { OP(2, 1, 2, OP_CONV), 34, 0267, 98 }, + { OP(2, 1, 2, OP_CONV), 40, 0077, 104 }, + { OP(2, 1, 2, OP_CONV), 42, 0277, 106 }, +}; + +static dstatement_t bool64_conv_4_statements[] = { + { OP(1, 1, 2, OP_CONV), 0, 0307, 48 }, + { OP(1, 1, 2, OP_CONV), 4, 0317, 56 }, + { OP(2, 1, 2, OP_CONV), 8, 0327, 64 }, + { OP(2, 1, 2, OP_CONV), 16, 0337, 72 }, + { OP(1, 1, 2, OP_CONV), 24, 0347, 80 }, + { OP(1, 1, 2, OP_CONV), 28, 0357, 88 }, + { OP(2, 1, 2, OP_CONV), 32, 0367, 96 }, + { OP(2, 1, 2, OP_CONV), 40, 0377, 104 }, +}; + +test_t tests[] = { + { + .desc = "bool64 conv 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(bool64_conv_init,bool64_conv_expect), + .num_statements = num_statements (bool64_conv_1_statements), + .statements = bool64_conv_1_statements, + .init_globals = (pr_int_t *) bool64_conv_init, + .expect_globals = (pr_int_t *) bool64_conv_expect, + }, + { + .desc = "bool64 conv 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(bool64_conv_init,bool64_conv_expect), + .num_statements = num_statements (bool64_conv_2_statements), + .statements = bool64_conv_2_statements, + .init_globals = (pr_int_t *) bool64_conv_init, + .expect_globals = (pr_int_t *) bool64_conv_expect, + }, + { + .desc = "bool64 conv 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(bool64_conv_init,bool64_conv_expect), + .num_statements = num_statements (bool64_conv_3a_statements), + .statements = bool64_conv_3a_statements, + .init_globals = (pr_int_t *) bool64_conv_init, + .expect_globals = (pr_int_t *) bool64_conv_expect, + }, + { + .desc = "bool64 conv 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(bool64_conv_init,bool64_conv_expect), + .num_statements = num_statements (bool64_conv_3b_statements), + .statements = bool64_conv_3b_statements, + .init_globals = (pr_int_t *) bool64_conv_init, + .expect_globals = (pr_int_t *) bool64_conv_expect, + }, + { + .desc = "bool64 conv 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(bool64_conv_init,bool64_conv_expect), + .num_statements = num_statements (bool64_conv_4_statements), + .statements = bool64_conv_4_statements, + .init_globals = (pr_int_t *) bool64_conv_init, + .expect_globals = (pr_int_t *) bool64_conv_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-double.c b/libs/gamecode/test/test-double.c new file mode 100644 index 000000000..33bd4ff7a --- /dev/null +++ b/libs/gamecode/test/test-double.c @@ -0,0 +1,333 @@ +#include "head.c" + +#include "QF/mathlib.h" + +#define sq(x) ((x)*(x)) + +static pr_dvec4_t double_binop_init[] = { + { 5, -5, 5, -5}, + { 3, 3, -3, -3}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +static pr_dvec4_t double_binop_expect[] = { + { 5, -5, 5, -5}, + { 3, 3, -3, -3}, + { 15, -15, -15, 15}, + { 5.0/3, -5.0/3, -5.0/3, 5.0/3}, + { 2, -2, 2, -2}, + { 2, 1, -1, -2}, + { 8, -2, 2, -8}, + { 2, -8, 8, -2}, +}; + +static dstatement_t double_binop_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 8, 0, 64 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 64, -2, 64 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 64 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 64, 1 }, + { OP(1, 1, 1, OP_MUL_D_1), 0, 8, 16 }, + { OP(1, 1, 1, OP_DIV_D_1), 0, 8, 24 }, + { OP(1, 1, 1, OP_REM_D_1), 0, 8, 32 }, + { OP(1, 1, 1, OP_MOD_D_1), 0, 8, 40 }, + { OP(1, 1, 1, OP_ADD_D_1), 0, 8, 48 }, + { OP(1, 1, 1, OP_SUB_D_1), 0, 8, 56 }, + { OP(1, 1, 1, OP_JUMP_A), -10, 0, 0 }, +}; + +static dstatement_t double_binop_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 8, 0, 64 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 64, -4, 64 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 64 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 64, 1 }, + { OP(1, 1, 1, OP_MUL_D_2), 0, 8, 16 }, + { OP(1, 1, 1, OP_DIV_D_2), 0, 8, 24 }, + { OP(1, 1, 1, OP_REM_D_2), 0, 8, 32 }, + { OP(1, 1, 1, OP_MOD_D_2), 0, 8, 40 }, + { OP(1, 1, 1, OP_ADD_D_2), 0, 8, 48 }, + { OP(1, 1, 1, OP_SUB_D_2), 0, 8, 56 }, + { OP(1, 1, 1, OP_JUMP_A), -10, 0, 0 }, +}; + +static dstatement_t double_binop_3a_statements[] = { + { OP(1, 1, 1, OP_MUL_D_3), 0, 8, 16 }, + { OP(1, 1, 1, OP_MUL_D_1), 6, 14, 22 }, + { OP(1, 1, 1, OP_DIV_D_3), 0, 8, 24 }, + { OP(1, 1, 1, OP_DIV_D_1), 6, 14, 30 }, + { OP(1, 1, 1, OP_REM_D_3), 0, 8, 32 }, + { OP(1, 1, 1, OP_REM_D_1), 6, 14, 38 }, + { OP(1, 1, 1, OP_MOD_D_3), 0, 8, 40 }, + { OP(1, 1, 1, OP_MOD_D_1), 6, 14, 46 }, + { OP(1, 1, 1, OP_ADD_D_3), 0, 8, 48 }, + { OP(1, 1, 1, OP_ADD_D_1), 6, 14, 54 }, + { OP(1, 1, 1, OP_SUB_D_3), 0, 8, 56 }, + { OP(1, 1, 1, OP_SUB_D_1), 6, 14, 62 }, +}; + +static dstatement_t double_binop_3b_statements[] = { + { OP(1, 1, 1, OP_MUL_D_1), 0, 8, 16 }, + { OP(1, 1, 1, OP_MUL_D_3), 2, 10, 18 }, + { OP(1, 1, 1, OP_DIV_D_1), 0, 8, 24 }, + { OP(1, 1, 1, OP_DIV_D_3), 2, 10, 26 }, + { OP(1, 1, 1, OP_REM_D_1), 0, 8, 32 }, + { OP(1, 1, 1, OP_REM_D_3), 2, 10, 34 }, + { OP(1, 1, 1, OP_MOD_D_1), 0, 8, 40 }, + { OP(1, 1, 1, OP_MOD_D_3), 2, 10, 42 }, + { OP(1, 1, 1, OP_ADD_D_1), 0, 8, 48 }, + { OP(1, 1, 1, OP_ADD_D_3), 2, 10, 50 }, + { OP(1, 1, 1, OP_SUB_D_1), 0, 8, 56 }, + { OP(1, 1, 1, OP_SUB_D_3), 2, 10, 58 }, +}; + +static dstatement_t double_binop_4_statements[] = { + { OP(1, 1, 1, OP_MUL_D_4), 0, 8, 16 }, + { OP(1, 1, 1, OP_DIV_D_4), 0, 8, 24 }, + { OP(1, 1, 1, OP_REM_D_4), 0, 8, 32 }, + { OP(1, 1, 1, OP_MOD_D_4), 0, 8, 40 }, + { OP(1, 1, 1, OP_ADD_D_4), 0, 8, 48 }, + { OP(1, 1, 1, OP_SUB_D_4), 0, 8, 56 }, +}; + +static pr_dvec4_t double_cossin_init[] = { + { 1, 2, 3, 4 }, // 0: output + { M_PI/6, 0, 0, 0 }, // 4: x + { 1, 2, 0, 0 }, // 8: f + { 1, 1, 0, 25 }, // 12: f inc and f0 max + { 0, 0, 0, 0 }, // 16: x2 -> [xx, xx] + // { } // 20: xn +}; + +static pr_dvec4_t double_cossin_expect[] = { + { 0.8660254037844386, 0.49999999999999994, 0, 0 }, // 0: output + { M_PI/6, 0, 0, 0 }, // 4: x + { 25, 26, 0, 0 }, // 8: f + { 1, 1, 0, 25 }, // 12: f inc and f0 max + { -sq(M_PI/6), -sq(M_PI/6), 0, 0 }, // 16: x2 -> [xx, xx] +}; + +static dstatement_t double_cossin_statements[] = { + { OP(0, 0, 0, OP_STORE_A_2), 42, 0, 8 }, // init xn -> [?, x] + { OP(0, 0, 0, OP_STORE_A_2), 40, 0, 16 }, // init xn -> [1, x] + { OP(0, 0, 0, OP_SWIZZLE_D), 8,0xc000, 32 }, // init x2 -> [x, x, 0, 0] + { OP(0, 0, 0, OP_MUL_D_2), 32, 32, 32 }, // x2 -> [x*x, x*x, 0, 0] + { OP(0, 0, 0, OP_SWIZZLE_D), 32,0xc3e4, 32 }, // init x2 -> -x2 + { OP(0, 0, 0, OP_SUB_D_4), 0, 0, 0 }, // init acc (output) to 0 +// loop: + { OP(0, 0, 0, OP_ADD_D_2), 0, 40, 0 }, // acc += xn + { OP(0, 0, 0, OP_MUL_D_2), 40, 32, 40 }, // xn *= x2 + { OP(0, 0, 0, OP_DIV_D_2), 40, 16, 40 }, // xn /= f + { OP(0, 0, 0, OP_ADD_D_2), 16, 24, 16 }, // f += inc + { OP(0, 0, 0, OP_DIV_D_2), 40, 16, 40 }, // xn /= f + { OP(0, 0, 0, OP_ADD_D_2), 16, 24, 16 }, // f += inc + { OP(0, 0, 0, OP_LT_D_1), 16, 30, 46 }, // f0 < fmax + { OP(0, 0, 0, OP_IFNZ), -7, 0, 46 }, // f0 < fmax +}; + +static pr_dvec4_t double_cmpop_init[] = { + { 5, -5, 5, -5}, + { 5, 5, -5, -5}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +// 5.0 as 64-bit int +#define F 0x4014000000000000l +#define mF 0xc014000000000000l +static pr_lvec4_t double_cmpop_expect[] = { + { F, mF, F, mF}, + { F, F, mF, mF}, + { -1, 0, 0, -1}, + { 0, -1, 0, 0}, + { 0, 0, -1, 0}, + { 0, -1, -1, 0}, + { -1, 0, -1, -1}, + { -1, -1, 0, -1}, +}; + +static dstatement_t double_cmpop_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 8, 0, 64 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 64, -2, 64 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 64 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 64, 1 }, + { OP(1, 1, 1, OP_EQ_D_1), 0, 8, 16 }, + { OP(1, 1, 1, OP_LT_D_1), 0, 8, 24 }, + { OP(1, 1, 1, OP_GT_D_1), 0, 8, 32 }, + { OP(1, 1, 1, OP_NE_D_1), 0, 8, 40 }, + { OP(1, 1, 1, OP_GE_D_1), 0, 8, 48 }, + { OP(1, 1, 1, OP_LE_D_1), 0, 8, 56 }, + { OP(1, 1, 1, OP_JUMP_A), -10, 0, 0 }, +}; + +static dstatement_t double_cmpop_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 8, 0, 64 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 64, -4, 64 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 64 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 64, 1 }, + { OP(1, 1, 1, OP_EQ_D_2), 0, 8, 16 }, + { OP(1, 1, 1, OP_LT_D_2), 0, 8, 24 }, + { OP(1, 1, 1, OP_GT_D_2), 0, 8, 32 }, + { OP(1, 1, 1, OP_NE_D_2), 0, 8, 40 }, + { OP(1, 1, 1, OP_GE_D_2), 0, 8, 48 }, + { OP(1, 1, 1, OP_LE_D_2), 0, 8, 56 }, + { OP(1, 1, 1, OP_JUMP_A), -10, 0, 0 }, +}; + +static dstatement_t double_cmpop_3a_statements[] = { + { OP(1, 1, 1, OP_EQ_D_3), 0, 8, 16 }, + { OP(1, 1, 1, OP_EQ_D_1), 6, 14, 22 }, + { OP(1, 1, 1, OP_LT_D_3), 0, 8, 24 }, + { OP(1, 1, 1, OP_LT_D_1), 6, 14, 30 }, + { OP(1, 1, 1, OP_GT_D_3), 0, 8, 32 }, + { OP(1, 1, 1, OP_GT_D_1), 6, 14, 38 }, + { OP(1, 1, 1, OP_NE_D_3), 0, 8, 40 }, + { OP(1, 1, 1, OP_NE_D_1), 6, 14, 46 }, + { OP(1, 1, 1, OP_GE_D_3), 0, 8, 48 }, + { OP(1, 1, 1, OP_GE_D_1), 6, 14, 54 }, + { OP(1, 1, 1, OP_LE_D_3), 0, 8, 56 }, + { OP(1, 1, 1, OP_LE_D_1), 6, 14, 62 }, +}; + +static dstatement_t double_cmpop_3b_statements[] = { + { OP(1, 1, 1, OP_EQ_D_1), 0, 8, 16 }, + { OP(1, 1, 1, OP_EQ_D_3), 2, 10, 18 }, + { OP(1, 1, 1, OP_LT_D_1), 0, 8, 24 }, + { OP(1, 1, 1, OP_LT_D_3), 2, 10, 26 }, + { OP(1, 1, 1, OP_GT_D_1), 0, 8, 32 }, + { OP(1, 1, 1, OP_GT_D_3), 2, 10, 34 }, + { OP(1, 1, 1, OP_NE_D_1), 0, 8, 40 }, + { OP(1, 1, 1, OP_NE_D_3), 2, 10, 42 }, + { OP(1, 1, 1, OP_GE_D_1), 0, 8, 48 }, + { OP(1, 1, 1, OP_GE_D_3), 2, 10, 50 }, + { OP(1, 1, 1, OP_LE_D_1), 0, 8, 56 }, + { OP(1, 1, 1, OP_LE_D_3), 2, 10, 58 }, +}; + +static dstatement_t double_cmpop_4_statements[] = { + { OP(1, 1, 1, OP_EQ_D_4), 0, 8, 16 }, + { OP(1, 1, 1, OP_LT_D_4), 0, 8, 24 }, + { OP(1, 1, 1, OP_GT_D_4), 0, 8, 32 }, + { OP(1, 1, 1, OP_NE_D_4), 0, 8, 40 }, + { OP(1, 1, 1, OP_GE_D_4), 0, 8, 48 }, + { OP(1, 1, 1, OP_LE_D_4), 0, 8, 56 }, +}; + +test_t tests[] = { + { + .desc = "double binop 1", + .extra_globals = 8 * 1, + .num_globals = num_globals(double_binop_init,double_binop_expect), + .num_statements = num_statements (double_binop_1_statements), + .statements = double_binop_1_statements, + .init_globals = (pr_int_t *) double_binop_init, + .expect_globals = (pr_int_t *) double_binop_expect, + }, + { + .desc = "double binop 2", + .extra_globals = 8 * 1, + .num_globals = num_globals(double_binop_init,double_binop_expect), + .num_statements = num_statements (double_binop_2_statements), + .statements = double_binop_2_statements, + .init_globals = (pr_int_t *) double_binop_init, + .expect_globals = (pr_int_t *) double_binop_expect, + }, + { + .desc = "double binop 3a", + .extra_globals = 8 * 1, + .num_globals = num_globals(double_binop_init,double_binop_expect), + .num_statements = num_statements (double_binop_3a_statements), + .statements = double_binop_3a_statements, + .init_globals = (pr_int_t *) double_binop_init, + .expect_globals = (pr_int_t *) double_binop_expect, + }, + { + .desc = "double binop 3b", + .extra_globals = 8 * 1, + .num_globals = num_globals(double_binop_init,double_binop_expect), + .num_statements = num_statements (double_binop_3b_statements), + .statements = double_binop_3b_statements, + .init_globals = (pr_int_t *) double_binop_init, + .expect_globals = (pr_int_t *) double_binop_expect, + }, + { + .desc = "double binop 4", + .extra_globals = 8 * 1, + .num_globals = num_globals(double_binop_init,double_binop_expect), + .num_statements = num_statements (double_binop_4_statements), + .statements = double_binop_4_statements, + .init_globals = (pr_int_t *) double_binop_init, + .expect_globals = (pr_int_t *) double_binop_expect, + }, + { + .desc = "double cos sin", + .extra_globals = 8 * 1, + .num_globals = num_globals(double_cossin_init,double_cossin_expect), + .num_statements = num_statements (double_cossin_statements), + .statements = double_cossin_statements, + .init_globals = (pr_int_t *) double_cossin_init, + .expect_globals = (pr_int_t *) double_cossin_expect, + }, + { + .desc = "double cmpop 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(double_cmpop_init,double_cmpop_expect), + .num_statements = num_statements (double_cmpop_1_statements), + .statements = double_cmpop_1_statements, + .init_globals = (pr_int_t *) double_cmpop_init, + .expect_globals = (pr_int_t *) double_cmpop_expect, + }, + { + .desc = "double cmpop 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(double_cmpop_init,double_cmpop_expect), + .num_statements = num_statements (double_cmpop_2_statements), + .statements = double_cmpop_2_statements, + .init_globals = (pr_int_t *) double_cmpop_init, + .expect_globals = (pr_int_t *) double_cmpop_expect, + }, + { + .desc = "double cmpop 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(double_cmpop_init,double_cmpop_expect), + .num_statements = num_statements (double_cmpop_3a_statements), + .statements = double_cmpop_3a_statements, + .init_globals = (pr_int_t *) double_cmpop_init, + .expect_globals = (pr_int_t *) double_cmpop_expect, + }, + { + .desc = "double cmpop 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(double_cmpop_init,double_cmpop_expect), + .num_statements = num_statements (double_cmpop_3b_statements), + .statements = double_cmpop_3b_statements, + .init_globals = (pr_int_t *) double_cmpop_init, + .expect_globals = (pr_int_t *) double_cmpop_expect, + }, + { + .desc = "double cmpop 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(double_cmpop_init,double_cmpop_expect), + .num_statements = num_statements (double_cmpop_4_statements), + .statements = double_cmpop_4_statements, + .init_globals = (pr_int_t *) double_cmpop_init, + .expect_globals = (pr_int_t *) double_cmpop_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-extend.c b/libs/gamecode/test/test-extend.c new file mode 100644 index 000000000..bd05ab394 --- /dev/null +++ b/libs/gamecode/test/test-extend.c @@ -0,0 +1,223 @@ +#include "head.c" + +#include "QF/mathlib.h" + +#define _ -6.259853398707798e+18 // 0xdeadbeef + +static pr_vec4_t float_extend_init[] = { + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { 2, 3, 4, 5 }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, +}; + +static pr_vec4_t float_extend_expect[] = { + { 5, 0, _, _ }, + { 4, 0, 0, _ }, + { 3, 0, 0, 0 }, + { 4, 5, 0, _ }, + { 2, 3, 0, 0 }, + { 3, 4, 5, 0 }, + { 2, 3, 4, 5 }, + { _, _, 4, 1 }, + { _, 3, 1, 1 }, + { 2, 1, 1, 1 }, + { _, 4, 5, 1 }, + { 2, 3, 1, 1 }, + { 2, 3, 4, 1 }, + { _, _, _, _ }, + { 5, 5, _, _ }, + { 4, 4, 4, _ }, + { 3, 3, 3, 3 }, + { 4, 5, 0, _ }, + { 2, 3, 2, 3 }, + { 3, 4, 5, 0 }, + { _, _, _, _ }, + { _, _, 4, -1 }, + { _, 3, -1, -1 }, + { 2, -1, -1, -1 }, + { _, 4, 5, -1 }, + { 2, 3, -1, -1 }, + { 2, 3, 4, -1 }, + { _, _, _, _ }, +}; + +static dstatement_t float_extend_statements[] = { + { OP(0, 0, 0, OP_WITH), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 0, 28, 1 }, + { OP(0, 0, 0, OP_WITH), 0, 56, 2 }, + { OP(0, 0, 0, OP_WITH), 0, 84, 3 }, + + { OP(0, 0, 0, OP_EXTEND), 27, 000, 0 }, + { OP(0, 0, 0, OP_EXTEND), 26, 001, 4 }, + { OP(0, 0, 0, OP_EXTEND), 25, 002, 8 }, + { OP(0, 0, 0, OP_EXTEND), 26, 003, 12 }, + { OP(0, 0, 0, OP_EXTEND), 24, 004, 16 }, + { OP(0, 0, 0, OP_EXTEND), 25, 005, 20 }, + + { OP(0, 0, 1, OP_EXTEND), 26, 010, 2 }, + { OP(0, 0, 1, OP_EXTEND), 25, 011, 5 }, + { OP(0, 0, 1, OP_EXTEND), 24, 012, 8 }, + { OP(0, 0, 1, OP_EXTEND), 26, 013, 13 }, + { OP(0, 0, 1, OP_EXTEND), 24, 014, 16 }, + { OP(0, 0, 1, OP_EXTEND), 24, 015, 20 }, + + { OP(0, 0, 2, OP_EXTEND), 27, 020, 0 }, + { OP(0, 0, 2, OP_EXTEND), 26, 021, 4 }, + { OP(0, 0, 2, OP_EXTEND), 25, 022, 8 }, + { OP(0, 0, 2, OP_EXTEND), 26, 023, 12 }, + { OP(0, 0, 2, OP_EXTEND), 24, 024, 16 }, + { OP(0, 0, 2, OP_EXTEND), 25, 025, 20 }, + + { OP(0, 0, 3, OP_EXTEND), 26, 030, 2 }, + { OP(0, 0, 3, OP_EXTEND), 25, 031, 5 }, + { OP(0, 0, 3, OP_EXTEND), 24, 032, 8 }, + { OP(0, 0, 3, OP_EXTEND), 26, 033, 13 }, + { OP(0, 0, 3, OP_EXTEND), 24, 034, 16 }, + { OP(0, 0, 3, OP_EXTEND), 24, 035, 20 }, +}; + +#undef _ +#define _ -1.1885959257070704e+148 // 0xdeadbeefdeadbeef + +static pr_dvec4_t double_extend_init[] = { + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { 2, 3, 4, 5 }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, + { _, _, _, _ }, +}; + +static pr_dvec4_t double_extend_expect[] = { + { 5, 0, _, _ }, + { 4, 0, 0, _ }, + { 3, 0, 0, 0 }, + { 4, 5, 0, _ }, + { 2, 3, 0, 0 }, + { 3, 4, 5, 0 }, + { 2, 3, 4, 5 }, + { _, _, 4, 1 }, + { _, 3, 1, 1 }, + { 2, 1, 1, 1 }, + { _, 4, 5, 1 }, + { 2, 3, 1, 1 }, + { 2, 3, 4, 1 }, + { _, _, _, _ }, + { 5, 5, _, _ }, + { 4, 4, 4, _ }, + { 3, 3, 3, 3 }, + { 4, 5, 0, _ }, + { 2, 3, 2, 3 }, + { 3, 4, 5, 0 }, + { _, _, _, _ }, + { _, _, 4, -1 }, + { _, 3, -1, -1 }, + { 2, -1, -1, -1 }, + { _, 4, 5, -1 }, + { 2, 3, -1, -1 }, + { 2, 3, 4, -1 }, + { _, _, _, _ }, +}; + +static dstatement_t double_extend_statements[] = { + { OP(0, 0, 0, OP_WITH), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 0, 56, 1 }, + { OP(0, 0, 0, OP_WITH), 0, 112, 2 }, + { OP(0, 0, 0, OP_WITH), 0, 168, 3 }, + + { OP(0, 0, 0, OP_EXTEND), 54, 040, 0 }, + { OP(0, 0, 0, OP_EXTEND), 52, 041, 8 }, + { OP(0, 0, 0, OP_EXTEND), 50, 042, 16 }, + { OP(0, 0, 0, OP_EXTEND), 52, 043, 24 }, + { OP(0, 0, 0, OP_EXTEND), 48, 044, 32 }, + { OP(0, 0, 0, OP_EXTEND), 50, 045, 40 }, + + { OP(0, 0, 1, OP_EXTEND), 52, 050, 4 }, + { OP(0, 0, 1, OP_EXTEND), 50, 051, 10 }, + { OP(0, 0, 1, OP_EXTEND), 48, 052, 16 }, + { OP(0, 0, 1, OP_EXTEND), 52, 053, 26 }, + { OP(0, 0, 1, OP_EXTEND), 48, 054, 32 }, + { OP(0, 0, 1, OP_EXTEND), 48, 055, 40 }, + + { OP(0, 0, 2, OP_EXTEND), 54, 060, 0 }, + { OP(0, 0, 2, OP_EXTEND), 52, 061, 8 }, + { OP(0, 0, 2, OP_EXTEND), 50, 062, 16 }, + { OP(0, 0, 2, OP_EXTEND), 52, 063, 24 }, + { OP(0, 0, 2, OP_EXTEND), 48, 064, 32 }, + { OP(0, 0, 2, OP_EXTEND), 50, 065, 40 }, + + { OP(0, 0, 3, OP_EXTEND), 52, 070, 4 }, + { OP(0, 0, 3, OP_EXTEND), 50, 071, 10 }, + { OP(0, 0, 3, OP_EXTEND), 48, 072, 16 }, + { OP(0, 0, 3, OP_EXTEND), 52, 073, 26 }, + { OP(0, 0, 3, OP_EXTEND), 48, 074, 32 }, + { OP(0, 0, 3, OP_EXTEND), 48, 075, 40 }, +}; + +test_t tests[] = { + { + .desc = "float extend", + .num_globals = num_globals(float_extend_init,float_extend_expect), + .num_statements = num_statements (float_extend_statements), + .statements = float_extend_statements, + .init_globals = (pr_int_t *) float_extend_init, + .expect_globals = (pr_int_t *) float_extend_expect, + }, + { + .desc = "double extend", + .num_globals = num_globals(double_extend_init,double_extend_expect), + .num_statements = num_statements (double_extend_statements), + .statements = double_extend_statements, + .init_globals = (pr_int_t *) double_extend_init, + .expect_globals = (pr_int_t *) double_extend_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-float.c b/libs/gamecode/test/test-float.c new file mode 100644 index 000000000..576518128 --- /dev/null +++ b/libs/gamecode/test/test-float.c @@ -0,0 +1,333 @@ +#include "head.c" + +#include "QF/mathlib.h" + +#define sq(x) ((float)(x)*(float)(x)) + +static pr_vec4_t float_binop_init[] = { + { 5, -5, 5, -5}, + { 3, 3, -3, -3}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +static pr_vec4_t float_binop_expect[] = { + { 5, -5, 5, -5}, + { 3, 3, -3, -3}, + { 15, -15, -15, 15}, + { 1.666666627, -1.666666627, -1.666666627, 1.666666627}, + { 2, -2, 2, -2}, + { 2, 1, -1, -2}, + { 8, -2, 2, -8}, + { 2, -8, 8, -2}, +}; + +static dstatement_t float_binop_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 32 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 32, -1, 32 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 32 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 32, 1 }, + { OP(1, 1, 1, OP_MUL_F_1), 0, 4, 8 }, + { OP(1, 1, 1, OP_DIV_F_1), 0, 4, 12 }, + { OP(1, 1, 1, OP_REM_F_1), 0, 4, 16 }, + { OP(1, 1, 1, OP_MOD_F_1), 0, 4, 20 }, + { OP(1, 1, 1, OP_ADD_F_1), 0, 4, 24 }, + { OP(1, 1, 1, OP_SUB_F_1), 0, 4, 28 }, + { OP(1, 1, 1, OP_JUMP_A), -10, 0, 0 }, +}; + +static dstatement_t float_binop_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 32 }, // index +//loop: + { OP(0, 0, 0, OP_LEA_C), 32, -2, 32 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 32 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 32, 1 }, + { OP(1, 1, 1, OP_MUL_F_2), 0, 4, 8 }, + { OP(1, 1, 1, OP_DIV_F_2), 0, 4, 12 }, + { OP(1, 1, 1, OP_REM_F_2), 0, 4, 16 }, + { OP(1, 1, 1, OP_MOD_F_2), 0, 4, 20 }, + { OP(1, 1, 1, OP_ADD_F_2), 0, 4, 24 }, + { OP(1, 1, 1, OP_SUB_F_2), 0, 4, 28 }, + { OP(1, 1, 1, OP_JUMP_A), -10, 0, 0 }, +}; + +static dstatement_t float_binop_3a_statements[] = { + { OP(1, 1, 1, OP_MUL_F_3), 0, 4, 8 }, + { OP(1, 1, 1, OP_MUL_F_1), 3, 7, 11 }, + { OP(1, 1, 1, OP_DIV_F_3), 0, 4, 12 }, + { OP(1, 1, 1, OP_DIV_F_1), 3, 7, 15 }, + { OP(1, 1, 1, OP_REM_F_3), 0, 4, 16 }, + { OP(1, 1, 1, OP_REM_F_1), 3, 7, 19 }, + { OP(1, 1, 1, OP_MOD_F_3), 0, 4, 20 }, + { OP(1, 1, 1, OP_MOD_F_1), 3, 7, 23 }, + { OP(1, 1, 1, OP_ADD_F_3), 0, 4, 24 }, + { OP(1, 1, 1, OP_ADD_F_1), 3, 7, 27 }, + { OP(1, 1, 1, OP_SUB_F_3), 0, 4, 28 }, + { OP(1, 1, 1, OP_SUB_F_1), 3, 7, 31 }, +}; + +static dstatement_t float_binop_3b_statements[] = { + { OP(1, 1, 1, OP_MUL_F_1), 0, 4, 8 }, + { OP(1, 1, 1, OP_MUL_F_3), 1, 5, 9 }, + { OP(1, 1, 1, OP_DIV_F_1), 0, 4, 12 }, + { OP(1, 1, 1, OP_DIV_F_3), 1, 5, 13 }, + { OP(1, 1, 1, OP_REM_F_1), 0, 4, 16 }, + { OP(1, 1, 1, OP_REM_F_3), 1, 5, 17 }, + { OP(1, 1, 1, OP_MOD_F_1), 0, 4, 20 }, + { OP(1, 1, 1, OP_MOD_F_3), 1, 5, 21 }, + { OP(1, 1, 1, OP_ADD_F_1), 0, 4, 24 }, + { OP(1, 1, 1, OP_ADD_F_3), 1, 5, 25 }, + { OP(1, 1, 1, OP_SUB_F_1), 0, 4, 28 }, + { OP(1, 1, 1, OP_SUB_F_3), 1, 5, 29 }, +}; + +static dstatement_t float_binop_4_statements[] = { + { OP(1, 1, 1, OP_MUL_F_4), 0, 4, 8 }, + { OP(1, 1, 1, OP_DIV_F_4), 0, 4, 12 }, + { OP(1, 1, 1, OP_REM_F_4), 0, 4, 16 }, + { OP(1, 1, 1, OP_MOD_F_4), 0, 4, 20 }, + { OP(1, 1, 1, OP_ADD_F_4), 0, 4, 24 }, + { OP(1, 1, 1, OP_SUB_F_4), 0, 4, 28 }, +}; + +static pr_vec4_t float_cossin_init[] = { + { 1, 2, 3, 4 }, // 0: output + { M_PI/6, 0, 0, 0 }, // 4: x + { 1, 2, 0, 0 }, // 8: f + { 1, 1, 0, 25 }, // 12: f inc and f0 max + { 0, 0, 0, 0 }, // 16: x2 -> [xx, xx] + // { } // 20: xn +}; + +static pr_vec4_t float_cossin_expect[] = { + { 0.866025388, 0.5, 0, 0 }, // 0: output + { M_PI/6, 0, 0, 0 }, // 4: x + { 25, 26, 0, 0 }, // 8: f + { 1, 1, 0, 25 }, // 12: f inc and f0 max + { -sq(M_PI/6), -sq(M_PI/6), 0, 0 }, // 16: x2 -> [xx, xx] +}; + +static dstatement_t float_cossin_statements[] = { + { OP(0, 0, 0, OP_STORE_A_1), 21, 0, 4 }, // init xn -> [?, x] + { OP(0, 0, 0, OP_STORE_A_1), 20, 0, 8 }, // init xn -> [1, x] + { OP(0, 0, 0, OP_SWIZZLE_F), 4, 0xc000, 16 },// init x2 -> [x, x, 0, 0] + { OP(0, 0, 0, OP_MUL_F_2), 16, 16, 16 }, // x2 -> [x*x, x*x, 0, 0] + { OP(0, 0, 0, OP_SWIZZLE_F), 16, 0xc3e4, 16 },// init x2 -> -x2 + { OP(0, 0, 0, OP_SUB_F_4), 0, 0, 0 }, // init acc (output) to 0 +// loop: + { OP(0, 0, 0, OP_ADD_F_2), 0, 20, 0 }, // acc += xn + { OP(0, 0, 0, OP_MUL_F_2), 20, 16, 20 }, // xn *= x2 + { OP(0, 0, 0, OP_DIV_F_2), 20, 8, 20 }, // xn /= f + { OP(0, 0, 0, OP_ADD_F_2), 8, 12, 8 }, // f += inc + { OP(0, 0, 0, OP_DIV_F_2), 20, 8, 20 }, // xn /= f + { OP(0, 0, 0, OP_ADD_F_2), 8, 12, 8 }, // f += inc + { OP(0, 0, 0, OP_LT_F_1), 8, 15, 23 }, // f0 < fmax + { OP(0, 0, 0, OP_IFNZ), -7, 0, 23 }, // f0 < fmax +}; + +static pr_vec4_t float_cmpop_init[] = { + { 5, -5, 5, -5}, + { 5, 5, -5, -5}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +// 5.0 as 32-bit int +#define F 0x40a00000 +#define mF 0xc0a00000 +static pr_ivec4_t float_cmpop_expect[] = { + { F, mF, F, mF}, + { F, F, mF, mF}, + { -1, 0, 0, -1}, + { 0, -1, 0, 0}, + { 0, 0, -1, 0}, + { 0, -1, -1, 0}, + { -1, 0, -1, -1}, + { -1, -1, 0, -1}, +}; + +static dstatement_t float_cmpop_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 32 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 32, -1, 32 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 32 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 32, 1 }, + { OP(1, 1, 1, OP_EQ_F_1), 0, 4, 8 }, + { OP(1, 1, 1, OP_LT_F_1), 0, 4, 12 }, + { OP(1, 1, 1, OP_GT_F_1), 0, 4, 16 }, + { OP(1, 1, 1, OP_NE_F_1), 0, 4, 20 }, + { OP(1, 1, 1, OP_GE_F_1), 0, 4, 24 }, + { OP(1, 1, 1, OP_LE_F_1), 0, 4, 28 }, + { OP(1, 1, 1, OP_JUMP_A), -10, 0, 0 }, +}; + +static dstatement_t float_cmpop_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 32 }, // index +//loop: + { OP(0, 0, 0, OP_LEA_C), 32, -2, 32 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 32 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 32, 1 }, + { OP(1, 1, 1, OP_EQ_F_2), 0, 4, 8 }, + { OP(1, 1, 1, OP_LT_F_2), 0, 4, 12 }, + { OP(1, 1, 1, OP_GT_F_2), 0, 4, 16 }, + { OP(1, 1, 1, OP_NE_F_2), 0, 4, 20 }, + { OP(1, 1, 1, OP_GE_F_2), 0, 4, 24 }, + { OP(1, 1, 1, OP_LE_F_2), 0, 4, 28 }, + { OP(1, 1, 1, OP_JUMP_A), -10, 0, 0 }, +}; + +static dstatement_t float_cmpop_3a_statements[] = { + { OP(1, 1, 1, OP_EQ_F_3), 0, 4, 8 }, + { OP(1, 1, 1, OP_EQ_F_1), 3, 7, 11 }, + { OP(1, 1, 1, OP_LT_F_3), 0, 4, 12 }, + { OP(1, 1, 1, OP_LT_F_1), 3, 7, 15 }, + { OP(1, 1, 1, OP_GT_F_3), 0, 4, 16 }, + { OP(1, 1, 1, OP_GT_F_1), 3, 7, 19 }, + { OP(1, 1, 1, OP_NE_F_3), 0, 4, 20 }, + { OP(1, 1, 1, OP_NE_F_1), 3, 7, 23 }, + { OP(1, 1, 1, OP_GE_F_3), 0, 4, 24 }, + { OP(1, 1, 1, OP_GE_F_1), 3, 7, 27 }, + { OP(1, 1, 1, OP_LE_F_3), 0, 4, 28 }, + { OP(1, 1, 1, OP_LE_F_1), 3, 7, 31 }, +}; + +static dstatement_t float_cmpop_3b_statements[] = { + { OP(1, 1, 1, OP_EQ_F_1), 0, 4, 8 }, + { OP(1, 1, 1, OP_EQ_F_3), 1, 5, 9 }, + { OP(1, 1, 1, OP_LT_F_1), 0, 4, 12 }, + { OP(1, 1, 1, OP_LT_F_3), 1, 5, 13 }, + { OP(1, 1, 1, OP_GT_F_1), 0, 4, 16 }, + { OP(1, 1, 1, OP_GT_F_3), 1, 5, 17 }, + { OP(1, 1, 1, OP_NE_F_1), 0, 4, 20 }, + { OP(1, 1, 1, OP_NE_F_3), 1, 5, 21 }, + { OP(1, 1, 1, OP_GE_F_1), 0, 4, 24 }, + { OP(1, 1, 1, OP_GE_F_3), 1, 5, 25 }, + { OP(1, 1, 1, OP_LE_F_1), 0, 4, 28 }, + { OP(1, 1, 1, OP_LE_F_3), 1, 5, 29 }, +}; + +static dstatement_t float_cmpop_4_statements[] = { + { OP(1, 1, 1, OP_EQ_F_4), 0, 4, 8 }, + { OP(1, 1, 1, OP_LT_F_4), 0, 4, 12 }, + { OP(1, 1, 1, OP_GT_F_4), 0, 4, 16 }, + { OP(1, 1, 1, OP_NE_F_4), 0, 4, 20 }, + { OP(1, 1, 1, OP_GE_F_4), 0, 4, 24 }, + { OP(1, 1, 1, OP_LE_F_4), 0, 4, 28 }, +}; + +test_t tests[] = { + { + .desc = "float binop 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(float_binop_init,float_binop_expect), + .num_statements = num_statements (float_binop_1_statements), + .statements = float_binop_1_statements, + .init_globals = (pr_int_t *) float_binop_init, + .expect_globals = (pr_int_t *) float_binop_expect, + }, + { + .desc = "float binop 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(float_binop_init,float_binop_expect), + .num_statements = num_statements (float_binop_2_statements), + .statements = float_binop_2_statements, + .init_globals = (pr_int_t *) float_binop_init, + .expect_globals = (pr_int_t *) float_binop_expect, + }, + { + .desc = "float binop 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(float_binop_init,float_binop_expect), + .num_statements = num_statements (float_binop_3a_statements), + .statements = float_binop_3a_statements, + .init_globals = (pr_int_t *) float_binop_init, + .expect_globals = (pr_int_t *) float_binop_expect, + }, + { + .desc = "float binop 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(float_binop_init,float_binop_expect), + .num_statements = num_statements (float_binop_3b_statements), + .statements = float_binop_3b_statements, + .init_globals = (pr_int_t *) float_binop_init, + .expect_globals = (pr_int_t *) float_binop_expect, + }, + { + .desc = "float binop 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(float_binop_init,float_binop_expect), + .num_statements = num_statements (float_binop_4_statements), + .statements = float_binop_4_statements, + .init_globals = (pr_int_t *) float_binop_init, + .expect_globals = (pr_int_t *) float_binop_expect, + }, + { + .desc = "float cos sin", + .extra_globals = 4 * 1, + .num_globals = num_globals (float_cossin_init, float_cossin_expect), + .num_statements = num_statements (float_cossin_statements), + .statements = float_cossin_statements, + .init_globals = (pr_int_t *) float_cossin_init, + .expect_globals = (pr_int_t *) float_cossin_expect, + }, + { + .desc = "float cmpop 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(float_cmpop_init,float_cmpop_expect), + .num_statements = num_statements (float_cmpop_1_statements), + .statements = float_cmpop_1_statements, + .init_globals = (pr_int_t *) float_cmpop_init, + .expect_globals = (pr_int_t *) float_cmpop_expect, + }, + { + .desc = "float cmpop 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(float_cmpop_init,float_cmpop_expect), + .num_statements = num_statements (float_cmpop_2_statements), + .statements = float_cmpop_2_statements, + .init_globals = (pr_int_t *) float_cmpop_init, + .expect_globals = (pr_int_t *) float_cmpop_expect, + }, + { + .desc = "float cmpop 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(float_cmpop_init,float_cmpop_expect), + .num_statements = num_statements (float_cmpop_3a_statements), + .statements = float_cmpop_3a_statements, + .init_globals = (pr_int_t *) float_cmpop_init, + .expect_globals = (pr_int_t *) float_cmpop_expect, + }, + { + .desc = "float cmpop 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(float_cmpop_init,float_cmpop_expect), + .num_statements = num_statements (float_cmpop_3b_statements), + .statements = float_cmpop_3b_statements, + .init_globals = (pr_int_t *) float_cmpop_init, + .expect_globals = (pr_int_t *) float_cmpop_expect, + }, + { + .desc = "float cmpop 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(float_cmpop_init,float_cmpop_expect), + .num_statements = num_statements (float_cmpop_4_statements), + .statements = float_cmpop_4_statements, + .init_globals = (pr_int_t *) float_cmpop_init, + .expect_globals = (pr_int_t *) float_cmpop_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-hops.c b/libs/gamecode/test/test-hops.c new file mode 100644 index 000000000..34d14fe2d --- /dev/null +++ b/libs/gamecode/test/test-hops.c @@ -0,0 +1,166 @@ +#include "head.c" + +#include "QF/mathlib.h" + +static pr_uivec4_t uint_hop_init[7] = { + { 0x0000ff00, 0x0000f0f0, 0x0000cccc, 0x0000aaaa }, +}; + +static pr_uivec4_t uint_hop_expect[] = { + { 0x0000ff00, 0x0000f0f0, 0x0000cccc, 0x0000aaaa }, + { 0x00008888, 0x0000eeee, 0x00006666, 0x00017776 }, + { 0xffff7777, 0xffff1111, 0xffff9999, 0 }, + { 0x00008080, 0x0000fefe, 0x00009696, 0x00026866 }, + { 0xffff7f7f, 0xffff0101, 0xffff6969, 0 }, + { 0x00008000, 0x0000fffe, 0x00006996, 0x00036766 }, + { 0xffff7fff, 0xffff0001, 0xffff9669, 0 }, +}; + +static dstatement_t uint_hop_statements[] = { + { OP(0, 0, 0, OP_WITH), 0, 4, 1 }, + { OP(0, 0, 0, OP_WITH), 0, 12, 2 }, + { OP(0, 0, 0, OP_WITH), 0, 20, 3 }, + + { OP(0, 0, 1, OP_HOPS), 2, 010, 0 }, + { OP(0, 0, 1, OP_HOPS), 2, 011, 1 }, + { OP(0, 0, 1, OP_HOPS), 2, 012, 2 }, + { OP(0, 0, 1, OP_HOPS), 2, 013, 3 }, + { OP(0, 0, 1, OP_HOPS), 2, 014, 4 }, + { OP(0, 0, 1, OP_HOPS), 2, 015, 5 }, + { OP(0, 0, 1, OP_HOPS), 2, 016, 6 }, + + { OP(0, 0, 2, OP_HOPS), 1, 020, 0 }, + { OP(0, 0, 2, OP_HOPS), 1, 021, 1 }, + { OP(0, 0, 2, OP_HOPS), 1, 022, 2 }, + { OP(0, 0, 2, OP_HOPS), 1, 023, 3 }, + { OP(0, 0, 2, OP_HOPS), 1, 024, 4 }, + { OP(0, 0, 2, OP_HOPS), 1, 025, 5 }, + { OP(0, 0, 2, OP_HOPS), 1, 026, 6 }, + + { OP(0, 0, 3, OP_HOPS), 0, 030, 0 }, + { OP(0, 0, 3, OP_HOPS), 0, 031, 1 }, + { OP(0, 0, 3, OP_HOPS), 0, 032, 2 }, + { OP(0, 0, 3, OP_HOPS), 0, 033, 3 }, + { OP(0, 0, 3, OP_HOPS), 0, 034, 4 }, + { OP(0, 0, 3, OP_HOPS), 0, 035, 5 }, + { OP(0, 0, 3, OP_HOPS), 0, 036, 6 }, +}; + +static pr_ulvec4_t ulong_hop_init[7] = { + { UINT64_C(0x00ff00000000), UINT64_C(0x0f0f00000000), + UINT64_C(0x333300000000), UINT64_C(0x555500000000) }, +}; + +static pr_ulvec4_t ulong_hop_expect[] = { + { UINT64_C(0x000000ff00000000), UINT64_C(0x00000f0f00000000), + UINT64_C(0x0000333300000000), UINT64_C(0x0000555500000000) }, + { UINT64_C(0x0000111100000000), UINT64_C(0x0000777700000000), + UINT64_C(0x0000666600000000), UINT64_C(0x0000888800000000) }, + { UINT64_C(0xffffeeeeffffffff), UINT64_C(0xffff8888ffffffff), + UINT64_C(0xffff9999ffffffff), 0 }, + { UINT64_C(0x0000010100000000), UINT64_C(0x00007f7f00000000), + UINT64_C(0x0000696900000000), UINT64_C(0x0000979700000000) }, + { UINT64_C(0xfffffefeffffffff), UINT64_C(0xffff8080ffffffff), + UINT64_C(0xffff9696ffffffff), 0 }, + { UINT64_C(0x0000000100000000), UINT64_C(0x00007fff00000000), + UINT64_C(0x0000699600000000), UINT64_C(0x0000989600000000) }, + { UINT64_C(0xfffffffeffffffff), UINT64_C(0xffff8000ffffffff), + UINT64_C(0xffff9669ffffffff), 0 }, +}; + +static dstatement_t ulong_hop_statements[] = { + { OP(0, 0, 0, OP_WITH), 0, 8, 1 }, + { OP(0, 0, 0, OP_WITH), 0, 24, 2 }, + { OP(0, 0, 0, OP_WITH), 0, 40, 3 }, + + { OP(0, 0, 1, OP_HOPS), 4, 050, 0 }, + { OP(0, 0, 1, OP_HOPS), 4, 051, 2 }, + { OP(0, 0, 1, OP_HOPS), 4, 052, 4 }, + { OP(0, 0, 1, OP_HOPS), 4, 053, 6 }, + { OP(0, 0, 1, OP_HOPS), 4, 054, 8 }, + { OP(0, 0, 1, OP_HOPS), 4, 055, 10 }, + { OP(0, 0, 1, OP_HOPS), 4, 056, 12 }, + + { OP(0, 0, 2, OP_HOPS), 2, 060, 0 }, + { OP(0, 0, 2, OP_HOPS), 2, 061, 2 }, + { OP(0, 0, 2, OP_HOPS), 2, 062, 4 }, + { OP(0, 0, 2, OP_HOPS), 2, 063, 6 }, + { OP(0, 0, 2, OP_HOPS), 2, 064, 8 }, + { OP(0, 0, 2, OP_HOPS), 2, 065, 10 }, + { OP(0, 0, 2, OP_HOPS), 2, 066, 12 }, + + { OP(0, 0, 3, OP_HOPS), 0, 070, 0 }, + { OP(0, 0, 3, OP_HOPS), 0, 071, 2 }, + { OP(0, 0, 3, OP_HOPS), 0, 072, 4 }, + { OP(0, 0, 3, OP_HOPS), 0, 073, 6 }, + { OP(0, 0, 3, OP_HOPS), 0, 074, 8 }, + { OP(0, 0, 3, OP_HOPS), 0, 075, 10 }, + { OP(0, 0, 3, OP_HOPS), 0, 076, 12 }, +}; + +static pr_vec4_t float_hop_init[2] = { + { 1, 2, 3, 4 }, +}; + +static pr_vec4_t float_hop_expect[] = { + { 1, 2, 3, 4 }, + { 3, 6, 10, 0 }, +}; + +static dstatement_t float_hop_statements[] = { + { OP(0, 0, 0, OP_HOPS), 0, 017, 4 }, + { OP(0, 0, 0, OP_HOPS), 0, 027, 5 }, + { OP(0, 0, 0, OP_HOPS), 0, 037, 6 }, +}; + +static pr_dvec4_t double_hop_init[2] = { + { 1, 2, 3, 4 }, +}; + +static pr_dvec4_t double_hop_expect[] = { + { 1, 2, 3, 4 }, + { 3, 6, 10, 0 }, +}; + +static dstatement_t double_hop_statements[] = { + { OP(0, 0, 0, OP_HOPS), 0, 057, 8 }, + { OP(0, 0, 0, OP_HOPS), 0, 067, 10 }, + { OP(0, 0, 0, OP_HOPS), 0, 077, 12 }, +}; + +test_t tests[] = { + { + .desc = "uint hops", + .num_globals = num_globals(uint_hop_init,uint_hop_expect), + .num_statements = num_statements (uint_hop_statements), + .statements = uint_hop_statements, + .init_globals = (pr_int_t *) uint_hop_init, + .expect_globals = (pr_int_t *) uint_hop_expect, + }, + { + .desc = "ulong hops", + .num_globals = num_globals(ulong_hop_init,ulong_hop_expect), + .num_statements = num_statements (ulong_hop_statements), + .statements = ulong_hop_statements, + .init_globals = (pr_int_t *) ulong_hop_init, + .expect_globals = (pr_int_t *) ulong_hop_expect, + }, + { + .desc = "float hops", + .num_globals = num_globals(float_hop_init,float_hop_expect), + .num_statements = num_statements (float_hop_statements), + .statements = float_hop_statements, + .init_globals = (pr_int_t *) float_hop_init, + .expect_globals = (pr_int_t *) float_hop_expect, + }, + { + .desc = "double hops", + .num_globals = num_globals(double_hop_init,double_hop_expect), + .num_statements = num_statements (double_hop_statements), + .statements = double_hop_statements, + .init_globals = (pr_int_t *) double_hop_init, + .expect_globals = (pr_int_t *) double_hop_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-int.c b/libs/gamecode/test/test-int.c new file mode 100644 index 000000000..6ca0dc928 --- /dev/null +++ b/libs/gamecode/test/test-int.c @@ -0,0 +1,284 @@ +#include "head.c" + +#include "QF/mathlib.h" + +static pr_ivec4_t int_binop_init[] = { + { 5, -5, 5, -5}, + { 3, 3, -3, -3}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +static pr_ivec4_t int_binop_expect[] = { + { 5, -5, 5, -5}, + { 3, 3, -3, -3}, + { 15, -15, -15, 15}, + { 1, -1, -1, 1}, + { 2, -2, 2, -2}, + { 2, 1, -1, -2}, + { 8, -2, 2, -8}, + { 2, -8, 8, -2}, +}; + +static dstatement_t int_binop_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 32 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 32, -1, 32 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 32 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 32, 1 }, + { OP(1, 1, 1, OP_MUL_I_1), 0, 4, 8 }, + { OP(1, 1, 1, OP_DIV_I_1), 0, 4, 12 }, + { OP(1, 1, 1, OP_REM_I_1), 0, 4, 16 }, + { OP(1, 1, 1, OP_MOD_I_1), 0, 4, 20 }, + { OP(1, 1, 1, OP_ADD_I_1), 0, 4, 24 }, + { OP(1, 1, 1, OP_SUB_I_1), 0, 4, 28 }, + { OP(1, 1, 1, OP_JUMP_A), -10, 0, 0 }, +}; + +static dstatement_t int_binop_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 32 }, // index +//loop: + { OP(0, 0, 0, OP_LEA_C), 32, -2, 32 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 32 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 32, 1 }, + { OP(1, 1, 1, OP_MUL_I_2), 0, 4, 8 }, + { OP(1, 1, 1, OP_DIV_I_2), 0, 4, 12 }, + { OP(1, 1, 1, OP_REM_I_2), 0, 4, 16 }, + { OP(1, 1, 1, OP_MOD_I_2), 0, 4, 20 }, + { OP(1, 1, 1, OP_ADD_I_2), 0, 4, 24 }, + { OP(1, 1, 1, OP_SUB_I_2), 0, 4, 28 }, + { OP(1, 1, 1, OP_JUMP_A), -10, 0, 0 }, +}; + +static dstatement_t int_binop_3a_statements[] = { + { OP(1, 1, 1, OP_MUL_I_3), 0, 4, 8 }, + { OP(1, 1, 1, OP_MUL_I_1), 3, 7, 11 }, + { OP(1, 1, 1, OP_DIV_I_3), 0, 4, 12 }, + { OP(1, 1, 1, OP_DIV_I_1), 3, 7, 15 }, + { OP(1, 1, 1, OP_REM_I_3), 0, 4, 16 }, + { OP(1, 1, 1, OP_REM_I_1), 3, 7, 19 }, + { OP(1, 1, 1, OP_MOD_I_3), 0, 4, 20 }, + { OP(1, 1, 1, OP_MOD_I_1), 3, 7, 23 }, + { OP(1, 1, 1, OP_ADD_I_3), 0, 4, 24 }, + { OP(1, 1, 1, OP_ADD_I_1), 3, 7, 27 }, + { OP(1, 1, 1, OP_SUB_I_3), 0, 4, 28 }, + { OP(1, 1, 1, OP_SUB_I_1), 3, 7, 31 }, +}; + +static dstatement_t int_binop_3b_statements[] = { + { OP(1, 1, 1, OP_MUL_I_1), 0, 4, 8 }, + { OP(1, 1, 1, OP_MUL_I_3), 1, 5, 9 }, + { OP(1, 1, 1, OP_DIV_I_1), 0, 4, 12 }, + { OP(1, 1, 1, OP_DIV_I_3), 1, 5, 13 }, + { OP(1, 1, 1, OP_REM_I_1), 0, 4, 16 }, + { OP(1, 1, 1, OP_REM_I_3), 1, 5, 17 }, + { OP(1, 1, 1, OP_MOD_I_1), 0, 4, 20 }, + { OP(1, 1, 1, OP_MOD_I_3), 1, 5, 21 }, + { OP(1, 1, 1, OP_ADD_I_1), 0, 4, 24 }, + { OP(1, 1, 1, OP_ADD_I_3), 1, 5, 25 }, + { OP(1, 1, 1, OP_SUB_I_1), 0, 4, 28 }, + { OP(1, 1, 1, OP_SUB_I_3), 1, 5, 29 }, +}; + +static dstatement_t int_binop_4_statements[] = { + { OP(1, 1, 1, OP_MUL_I_4), 0, 4, 8 }, + { OP(1, 1, 1, OP_DIV_I_4), 0, 4, 12 }, + { OP(1, 1, 1, OP_REM_I_4), 0, 4, 16 }, + { OP(1, 1, 1, OP_MOD_I_4), 0, 4, 20 }, + { OP(1, 1, 1, OP_ADD_I_4), 0, 4, 24 }, + { OP(1, 1, 1, OP_SUB_I_4), 0, 4, 28 }, +}; + +static pr_ivec4_t int_cmpop_init[] = { + { 5, -5, 5, -5}, + { 5, 5, -5, -5}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +static pr_ivec4_t int_cmpop_expect[] = { + { 5, -5, 5, -5}, + { 5, 5, -5, -5}, + { -1, 0, 0, -1}, + { 0, -1, 0, 0}, + { 0, 0, -1, 0}, + { 0, -1, -1, 0}, + { -1, 0, -1, -1}, + { -1, -1, 0, -1}, +}; + +static dstatement_t int_cmpop_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 32 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 32, -1, 32 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 32 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 32, 1 }, + { OP(1, 1, 1, OP_EQ_I_1), 0, 4, 8 }, + { OP(1, 1, 1, OP_LT_I_1), 0, 4, 12 }, + { OP(1, 1, 1, OP_GT_I_1), 0, 4, 16 }, + { OP(1, 1, 1, OP_NE_I_1), 0, 4, 20 }, + { OP(1, 1, 1, OP_GE_I_1), 0, 4, 24 }, + { OP(1, 1, 1, OP_LE_I_1), 0, 4, 28 }, + { OP(1, 1, 1, OP_JUMP_A), -10, 0, 0 }, +}; + +static dstatement_t int_cmpop_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 32 }, // index +//loop: + { OP(0, 0, 0, OP_LEA_C), 32, -2, 32 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 32 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 32, 1 }, + { OP(1, 1, 1, OP_EQ_I_2), 0, 4, 8 }, + { OP(1, 1, 1, OP_LT_I_2), 0, 4, 12 }, + { OP(1, 1, 1, OP_GT_I_2), 0, 4, 16 }, + { OP(1, 1, 1, OP_NE_I_2), 0, 4, 20 }, + { OP(1, 1, 1, OP_GE_I_2), 0, 4, 24 }, + { OP(1, 1, 1, OP_LE_I_2), 0, 4, 28 }, + { OP(1, 1, 1, OP_JUMP_A), -10, 0, 0 }, +}; + +static dstatement_t int_cmpop_3a_statements[] = { + { OP(1, 1, 1, OP_EQ_I_3), 0, 4, 8 }, + { OP(1, 1, 1, OP_EQ_I_1), 3, 7, 11 }, + { OP(1, 1, 1, OP_LT_I_3), 0, 4, 12 }, + { OP(1, 1, 1, OP_LT_I_1), 3, 7, 15 }, + { OP(1, 1, 1, OP_GT_I_3), 0, 4, 16 }, + { OP(1, 1, 1, OP_GT_I_1), 3, 7, 19 }, + { OP(1, 1, 1, OP_NE_I_3), 0, 4, 20 }, + { OP(1, 1, 1, OP_NE_I_1), 3, 7, 23 }, + { OP(1, 1, 1, OP_GE_I_3), 0, 4, 24 }, + { OP(1, 1, 1, OP_GE_I_1), 3, 7, 27 }, + { OP(1, 1, 1, OP_LE_I_3), 0, 4, 28 }, + { OP(1, 1, 1, OP_LE_I_1), 3, 7, 31 }, +}; + +static dstatement_t int_cmpop_3b_statements[] = { + { OP(1, 1, 1, OP_EQ_I_1), 0, 4, 8 }, + { OP(1, 1, 1, OP_EQ_I_3), 1, 5, 9 }, + { OP(1, 1, 1, OP_LT_I_1), 0, 4, 12 }, + { OP(1, 1, 1, OP_LT_I_3), 1, 5, 13 }, + { OP(1, 1, 1, OP_GT_I_1), 0, 4, 16 }, + { OP(1, 1, 1, OP_GT_I_3), 1, 5, 17 }, + { OP(1, 1, 1, OP_NE_I_1), 0, 4, 20 }, + { OP(1, 1, 1, OP_NE_I_3), 1, 5, 21 }, + { OP(1, 1, 1, OP_GE_I_1), 0, 4, 24 }, + { OP(1, 1, 1, OP_GE_I_3), 1, 5, 25 }, + { OP(1, 1, 1, OP_LE_I_1), 0, 4, 28 }, + { OP(1, 1, 1, OP_LE_I_3), 1, 5, 29 }, +}; + +static dstatement_t int_cmpop_4_statements[] = { + { OP(1, 1, 1, OP_EQ_I_4), 0, 4, 8 }, + { OP(1, 1, 1, OP_LT_I_4), 0, 4, 12 }, + { OP(1, 1, 1, OP_GT_I_4), 0, 4, 16 }, + { OP(1, 1, 1, OP_NE_I_4), 0, 4, 20 }, + { OP(1, 1, 1, OP_GE_I_4), 0, 4, 24 }, + { OP(1, 1, 1, OP_LE_I_4), 0, 4, 28 }, +}; + +test_t tests[] = { + { + .desc = "int binop 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(int_binop_init,int_binop_expect), + .num_statements = num_statements (int_binop_1_statements), + .statements = int_binop_1_statements, + .init_globals = (pr_int_t *) int_binop_init, + .expect_globals = (pr_int_t *) int_binop_expect, + }, + { + .desc = "int binop 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(int_binop_init,int_binop_expect), + .num_statements = num_statements (int_binop_2_statements), + .statements = int_binop_2_statements, + .init_globals = (pr_int_t *) int_binop_init, + .expect_globals = (pr_int_t *) int_binop_expect, + }, + { + .desc = "int binop 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(int_binop_init,int_binop_expect), + .num_statements = num_statements (int_binop_3a_statements), + .statements = int_binop_3a_statements, + .init_globals = (pr_int_t *) int_binop_init, + .expect_globals = (pr_int_t *) int_binop_expect, + }, + { + .desc = "int binop 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(int_binop_init,int_binop_expect), + .num_statements = num_statements (int_binop_3b_statements), + .statements = int_binop_3b_statements, + .init_globals = (pr_int_t *) int_binop_init, + .expect_globals = (pr_int_t *) int_binop_expect, + }, + { + .desc = "int binop 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(int_binop_init,int_binop_expect), + .num_statements = num_statements (int_binop_4_statements), + .statements = int_binop_4_statements, + .init_globals = (pr_int_t *) int_binop_init, + .expect_globals = (pr_int_t *) int_binop_expect, + }, + { + .desc = "int cmpop 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(int_cmpop_init,int_cmpop_expect), + .num_statements = num_statements (int_cmpop_1_statements), + .statements = int_cmpop_1_statements, + .init_globals = (pr_int_t *) int_cmpop_init, + .expect_globals = (pr_int_t *) int_cmpop_expect, + }, + { + .desc = "int cmpop 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(int_cmpop_init,int_cmpop_expect), + .num_statements = num_statements (int_cmpop_2_statements), + .statements = int_cmpop_2_statements, + .init_globals = (pr_int_t *) int_cmpop_init, + .expect_globals = (pr_int_t *) int_cmpop_expect, + }, + { + .desc = "int cmpop 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(int_cmpop_init,int_cmpop_expect), + .num_statements = num_statements (int_cmpop_3a_statements), + .statements = int_cmpop_3a_statements, + .init_globals = (pr_int_t *) int_cmpop_init, + .expect_globals = (pr_int_t *) int_cmpop_expect, + }, + { + .desc = "int cmpop 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(int_cmpop_init,int_cmpop_expect), + .num_statements = num_statements (int_cmpop_3b_statements), + .statements = int_cmpop_3b_statements, + .init_globals = (pr_int_t *) int_cmpop_init, + .expect_globals = (pr_int_t *) int_cmpop_expect, + }, + { + .desc = "int cmpop 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(int_cmpop_init,int_cmpop_expect), + .num_statements = num_statements (int_cmpop_4_statements), + .statements = int_cmpop_4_statements, + .init_globals = (pr_int_t *) int_cmpop_init, + .expect_globals = (pr_int_t *) int_cmpop_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-jump.c b/libs/gamecode/test/test-jump.c new file mode 100644 index 000000000..d05d6b92b --- /dev/null +++ b/libs/gamecode/test/test-jump.c @@ -0,0 +1,77 @@ +#include "head.c" + +#define DB 0xdeadbeef + +static pr_int_t test_globals_init[] = { + DB, 1, 2, 3, DB, +}; + +static pr_int_t test_globals_expect[] = { + DB, 1, 2, 3, 1, +}; + +static dstatement_t jump_A_statements[] = { + { OP(0, 0, 0, OP_JUMP_A), 4, 0, 0 }, + { OP(0, 0, 0, OP_LEA_A), 1, 0, 0 }, + { OP(0, 0, 0, OP_LEA_A), 1, 0, 4 }, + { OP(0, 0, 0, OP_JUMP_A), 2, 0, 0 }, + { OP(0, 0, 0, OP_JUMP_A), -2, 0, 0 }, +}; + +static dstatement_t jump_B_statements[] = { + { OP(0, 0, 0, OP_JUMP_B), 1, 2, 0 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_LEA_A), 1, 0, 0 }, + { OP(0, 0, 0, OP_LEA_A), 1, 0, 4 }, +}; + +static dstatement_t jump_C_statements[] = { + { OP(0, 0, 0, OP_JUMP_C), 1, 2, 0 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_LEA_A), 1, 0, 0 }, + { OP(0, 0, 0, OP_LEA_A), 1, 0, 4 }, +}; + +static dstatement_t jump_D_statements[] = { + { OP(0, 0, 0, OP_JUMP_D), 1, 2, 0 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_LEA_A), 1, 0, 0 }, + { OP(0, 0, 0, OP_LEA_A), 1, 0, 4 }, +}; + +test_t tests[] = { + { + .desc = "jump A", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (jump_A_statements), + .statements = jump_A_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, + { + .desc = "jump B", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (jump_B_statements), + .statements = jump_B_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, + { + .desc = "jump C", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (jump_C_statements), + .statements = jump_C_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, + { + .desc = "jump D", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (jump_D_statements), + .statements = jump_D_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-lea.c b/libs/gamecode/test/test-lea.c new file mode 100644 index 000000000..a99702144 --- /dev/null +++ b/libs/gamecode/test/test-lea.c @@ -0,0 +1,42 @@ +#include "head.c" + +static pr_int_t lea_globals_init[] = { + // pointers + 24, 26, 28, 29, + 32, -4, -2, 0, + 1, 4, 0xdeadbeef, 0xfeedf00d, + + 0, 0, 0, 0, +}; + +static pr_int_t lea_globals_expect[] = { + // pointers + 24, 26, 28, 29, + 32, -4, -2, 0, + 1, 4, 6, 32, + + 7, 34, 26, 88, +}; + +static dstatement_t lea_statements[] = { + {OP(0, 0, 0, OP_LEA_A), 7, 9, 12}, + {OP(0, 0, 0, OP_LEA_C), 2, 6, 13}, + {OP(0, 0, 0, OP_LEA_D), 2, 6, 14}, + {OP(0, 0, 0, OP_LEA_B), 4, 2, 15}, + {OP(0, 0, 0, OP_LEA_E), 4, 2, 10}, + {OP(0, 0, 0, OP_LEA_F), 4, 2, 11}, +}; + +test_t tests[] = { + { + .desc = "lea", + .num_globals = num_globals (lea_globals_init, lea_globals_expect), + .num_statements = num_statements (lea_statements), + .statements = lea_statements, + .init_globals = lea_globals_init, + .expect_globals = lea_globals_expect, + .edict_area = 28, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-load.c b/libs/gamecode/test/test-load.c new file mode 100644 index 000000000..986a58163 --- /dev/null +++ b/libs/gamecode/test/test-load.c @@ -0,0 +1,87 @@ +#include "head.c" + +static pr_int_t test_globals_init[] = { + // pointers + 24, 26, 28, 29, + 32, -4, -2, 0, + 1, 4, 0xdeadbeef, 0xfeedf00d, + // destination data + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + // source data + 11, 12, 9, 10, + 8, 5, 6, 7, + 1, 2, 3, 4, +}; + +static pr_int_t test_globals_expect[] = { + // pointers + 24, 26, 28, 29, + 32, -4, -2, 0, + 1, 4, 0xdeadbeef, 0xfeedf00d, + // destination data + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + // source data + 11, 12, 9, 10, + 8, 5, 6, 7, + 1, 2, 3, 4, +}; + +static dstatement_t load_B_statements[] = { + {OP(0, 0, 0, OP_LOAD_B_4), 7, 9, 12}, + {OP(0, 0, 0, OP_LOAD_B_3), 7, 8, 16}, + {OP(0, 0, 0, OP_LOAD_B_1), 7, 7, 19}, + {OP(0, 0, 0, OP_LOAD_B_2), 7, 6, 20}, + {OP(0, 0, 0, OP_LOAD_B_2), 7, 5, 22}, +}; + +static dstatement_t load_C_statements[] = { + {OP(0, 0, 0, OP_LOAD_C_4), 2, 4, 12}, + {OP(0, 0, 0, OP_LOAD_C_3), 2, 1, 16}, + {OP(0, 0, 0, OP_LOAD_C_1), 2, 0, 19}, + {OP(0, 0, 0, OP_LOAD_C_2), 2, -2, 20}, + {OP(0, 0, 0, OP_LOAD_C_2), 2, -4, 22}, +}; + +static dstatement_t load_D_statements[] = { + {OP(0, 0, 0, OP_LOAD_D_4), 2, 9, 12}, + {OP(0, 0, 0, OP_LOAD_D_3), 2, 8, 16}, + {OP(0, 0, 0, OP_LOAD_D_1), 2, 7, 19}, + {OP(0, 0, 0, OP_LOAD_D_2), 2, 6, 20}, + {OP(0, 0, 0, OP_LOAD_D_2), 2, 5, 22}, +}; + +test_t tests[] = { + { + .desc = "load B", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (load_B_statements), + .statements = load_B_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + // FIXME negative field offsets are not official but work because all + // offset calculations are done in 32-bit and thus wrap anyway + .edict_area = 28, + }, + { + .desc = "load C", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (load_C_statements), + .statements = load_C_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, + { + .desc = "load D", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (load_D_statements), + .statements = load_D_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-load64.c b/libs/gamecode/test/test-load64.c new file mode 100644 index 000000000..abb6052eb --- /dev/null +++ b/libs/gamecode/test/test-load64.c @@ -0,0 +1,89 @@ +#include "head.c" + +static pr_int_t test_globals_init[] = { + // pointers + 24, 26, 48, 29, + 32, -8, -4, 0, + 2, 8, 0xdeadbeef, 0xfeedf00d, + 0xdeadbeef, 0xfeedf00d, 0xdeadbeef, 0xfeedf00d, + // destination data + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + // source data + 21, 22, 23, 24, 17, 18, 19, 20, + 15, 16, 9, 10, 11, 12, 13, 14, + 1, 2, 3, 4, 5, 6, 7, 8, +}; + +static pr_int_t test_globals_expect[] = { + // pointers + 24, 26, 48, 29, + 32, -8, -4, 0, + 2, 8, 0xdeadbeef, 0xfeedf00d, + 0xdeadbeef, 0xfeedf00d, 0xdeadbeef, 0xfeedf00d, + // destination data +/*16*/ 1, 2, 3, 4, 5, 6, 7, 8, +/*24*/ 9, 10, 11, 12, 13, 14, 15, 16, +/*32*/ 17, 18, 19, 20, 21, 22, 23, 24, + // source data +/*40*/ 21, 22, 23, 24, 17, 18, 19, 20, +/*48*/ 15, 16, 9, 10, 11, 12, 13, 14, +/*56*/ 1, 2, 3, 4, 5, 6, 7, 8, +}; + +static dstatement_t load64_B_statements[] = { + {OP(0, 0, 0, OP_LOAD64_B_4), 7, 9, 16}, + {OP(0, 0, 0, OP_LOAD64_B_3), 7, 8, 24}, + {OP(0, 0, 0, OP_LOAD_B_2), 7, 7, 30}, + {OP(0, 0, 0, OP_LOAD_B_4), 7, 6, 32}, + {OP(0, 0, 0, OP_LOAD_B_4), 7, 5, 36}, +}; + +static dstatement_t load64_C_statements[] = { + {OP(0, 0, 0, OP_LOAD64_C_4), 2, 8, 16}, + {OP(0, 0, 0, OP_LOAD64_C_3), 2, 2, 24}, + {OP(0, 0, 0, OP_LOAD_C_2), 2, 0, 30}, + {OP(0, 0, 0, OP_LOAD_C_4), 2, -4, 32}, + {OP(0, 0, 0, OP_LOAD_C_4), 2, -8, 36}, +}; + +static dstatement_t load64_D_statements[] = { + {OP(0, 0, 0, OP_LOAD64_D_4), 2, 9, 16}, + {OP(0, 0, 0, OP_LOAD64_D_3), 2, 8, 24}, + {OP(0, 0, 0, OP_LOAD_D_2), 2, 7, 30}, + {OP(0, 0, 0, OP_LOAD_D_4), 2, 6, 32}, + {OP(0, 0, 0, OP_LOAD_D_4), 2, 5, 36}, +}; + +test_t tests[] = { + { + .desc = "load64 B", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (load64_B_statements), + .statements = load64_B_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + // FIXME negative field offsets are not official but work because all + // offset calculations are done in 32-bit and thus wrap anyway + .edict_area = 48, + }, + { + .desc = "load64 C", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (load64_C_statements), + .statements = load64_C_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, + { + .desc = "load64 D", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (load64_D_statements), + .statements = load64_D_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-long.c b/libs/gamecode/test/test-long.c new file mode 100644 index 000000000..b5b021e17 --- /dev/null +++ b/libs/gamecode/test/test-long.c @@ -0,0 +1,286 @@ +#include "head.c" + +#include "QF/mathlib.h" + +#define sq(x) ((x)*(x)) + +static pr_lvec4_t long_binop_init[] = { + { 5, -5, 5, -5}, + { 3, 3, -3, -3}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +static pr_lvec4_t long_binop_expect[] = { + { 5, -5, 5, -5}, + { 3, 3, -3, -3}, + { 15, -15, -15, 15}, + { 5.0/3, -5.0/3, -5.0/3, 5.0/3}, + { 2, -2, 2, -2}, + { 2, 1, -1, -2}, + { 8, -2, 2, -8}, + { 2, -8, 8, -2}, +}; + +static dstatement_t long_binop_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 8, 0, 64 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 64, -2, 64 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 64 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 64, 1 }, + { OP(1, 1, 1, OP_MUL_L_1), 0, 8, 16 }, + { OP(1, 1, 1, OP_DIV_L_1), 0, 8, 24 }, + { OP(1, 1, 1, OP_REM_L_1), 0, 8, 32 }, + { OP(1, 1, 1, OP_MOD_L_1), 0, 8, 40 }, + { OP(1, 1, 1, OP_ADD_L_1), 0, 8, 48 }, + { OP(1, 1, 1, OP_SUB_L_1), 0, 8, 56 }, + { OP(1, 1, 1, OP_JUMP_A), -10, 0, 0 }, +}; + +static dstatement_t long_binop_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 8, 0, 64 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 64, -4, 64 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 64 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 64, 1 }, + { OP(1, 1, 1, OP_MUL_L_2), 0, 8, 16 }, + { OP(1, 1, 1, OP_DIV_L_2), 0, 8, 24 }, + { OP(1, 1, 1, OP_REM_L_2), 0, 8, 32 }, + { OP(1, 1, 1, OP_MOD_L_2), 0, 8, 40 }, + { OP(1, 1, 1, OP_ADD_L_2), 0, 8, 48 }, + { OP(1, 1, 1, OP_SUB_L_2), 0, 8, 56 }, + { OP(1, 1, 1, OP_JUMP_A), -10, 0, 0 }, +}; + +static dstatement_t long_binop_3a_statements[] = { + { OP(1, 1, 1, OP_MUL_L_3), 0, 8, 16 }, + { OP(1, 1, 1, OP_MUL_L_1), 6, 14, 22 }, + { OP(1, 1, 1, OP_DIV_L_3), 0, 8, 24 }, + { OP(1, 1, 1, OP_DIV_L_1), 6, 14, 30 }, + { OP(1, 1, 1, OP_REM_L_3), 0, 8, 32 }, + { OP(1, 1, 1, OP_REM_L_1), 6, 14, 38 }, + { OP(1, 1, 1, OP_MOD_L_3), 0, 8, 40 }, + { OP(1, 1, 1, OP_MOD_L_1), 6, 14, 46 }, + { OP(1, 1, 1, OP_ADD_L_3), 0, 8, 48 }, + { OP(1, 1, 1, OP_ADD_L_1), 6, 14, 54 }, + { OP(1, 1, 1, OP_SUB_L_3), 0, 8, 56 }, + { OP(1, 1, 1, OP_SUB_L_1), 6, 14, 62 }, +}; + +static dstatement_t long_binop_3b_statements[] = { + { OP(1, 1, 1, OP_MUL_L_1), 0, 8, 16 }, + { OP(1, 1, 1, OP_MUL_L_3), 2, 10, 18 }, + { OP(1, 1, 1, OP_DIV_L_1), 0, 8, 24 }, + { OP(1, 1, 1, OP_DIV_L_3), 2, 10, 26 }, + { OP(1, 1, 1, OP_REM_L_1), 0, 8, 32 }, + { OP(1, 1, 1, OP_REM_L_3), 2, 10, 34 }, + { OP(1, 1, 1, OP_MOD_L_1), 0, 8, 40 }, + { OP(1, 1, 1, OP_MOD_L_3), 2, 10, 42 }, + { OP(1, 1, 1, OP_ADD_L_1), 0, 8, 48 }, + { OP(1, 1, 1, OP_ADD_L_3), 2, 10, 50 }, + { OP(1, 1, 1, OP_SUB_L_1), 0, 8, 56 }, + { OP(1, 1, 1, OP_SUB_L_3), 2, 10, 58 }, +}; + +static dstatement_t long_binop_4_statements[] = { + { OP(1, 1, 1, OP_MUL_L_4), 0, 8, 16 }, + { OP(1, 1, 1, OP_DIV_L_4), 0, 8, 24 }, + { OP(1, 1, 1, OP_REM_L_4), 0, 8, 32 }, + { OP(1, 1, 1, OP_MOD_L_4), 0, 8, 40 }, + { OP(1, 1, 1, OP_ADD_L_4), 0, 8, 48 }, + { OP(1, 1, 1, OP_SUB_L_4), 0, 8, 56 }, +}; + +static pr_lvec4_t long_cmpop_init[] = { + { 5, -5, 5, -5}, + { 5, 5, -5, -5}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +static pr_lvec4_t long_cmpop_expect[] = { + { 5, -5, 5, -5}, + { 5, 5, -5, -5}, + { -1, 0, 0, -1}, + { 0, -1, 0, 0}, + { 0, 0, -1, 0}, + { 0, -1, -1, 0}, + { -1, 0, -1, -1}, + { -1, -1, 0, -1}, +}; + +static dstatement_t long_cmpop_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 8, 0, 64 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 64, -2, 64 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 64 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 64, 1 }, + { OP(1, 1, 1, OP_EQ_L_1), 0, 8, 16 }, + { OP(1, 1, 1, OP_LT_L_1), 0, 8, 24 }, + { OP(1, 1, 1, OP_GT_L_1), 0, 8, 32 }, + { OP(1, 1, 1, OP_NE_L_1), 0, 8, 40 }, + { OP(1, 1, 1, OP_GE_L_1), 0, 8, 48 }, + { OP(1, 1, 1, OP_LE_L_1), 0, 8, 56 }, + { OP(1, 1, 1, OP_JUMP_A), -10, 0, 0 }, +}; + +static dstatement_t long_cmpop_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 8, 0, 64 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 64, -4, 64 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 64 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 64, 1 }, + { OP(1, 1, 1, OP_EQ_L_2), 0, 8, 16 }, + { OP(1, 1, 1, OP_LT_L_2), 0, 8, 24 }, + { OP(1, 1, 1, OP_GT_L_2), 0, 8, 32 }, + { OP(1, 1, 1, OP_NE_L_2), 0, 8, 40 }, + { OP(1, 1, 1, OP_GE_L_2), 0, 8, 48 }, + { OP(1, 1, 1, OP_LE_L_2), 0, 8, 56 }, + { OP(1, 1, 1, OP_JUMP_A), -10, 0, 0 }, +}; + +static dstatement_t long_cmpop_3a_statements[] = { + { OP(1, 1, 1, OP_EQ_L_3), 0, 8, 16 }, + { OP(1, 1, 1, OP_EQ_L_1), 6, 14, 22 }, + { OP(1, 1, 1, OP_LT_L_3), 0, 8, 24 }, + { OP(1, 1, 1, OP_LT_L_1), 6, 14, 30 }, + { OP(1, 1, 1, OP_GT_L_3), 0, 8, 32 }, + { OP(1, 1, 1, OP_GT_L_1), 6, 14, 38 }, + { OP(1, 1, 1, OP_NE_L_3), 0, 8, 40 }, + { OP(1, 1, 1, OP_NE_L_1), 6, 14, 46 }, + { OP(1, 1, 1, OP_GE_L_3), 0, 8, 48 }, + { OP(1, 1, 1, OP_GE_L_1), 6, 14, 54 }, + { OP(1, 1, 1, OP_LE_L_3), 0, 8, 56 }, + { OP(1, 1, 1, OP_LE_L_1), 6, 14, 62 }, +}; + +static dstatement_t long_cmpop_3b_statements[] = { + { OP(1, 1, 1, OP_EQ_L_1), 0, 8, 16 }, + { OP(1, 1, 1, OP_EQ_L_3), 2, 10, 18 }, + { OP(1, 1, 1, OP_LT_L_1), 0, 8, 24 }, + { OP(1, 1, 1, OP_LT_L_3), 2, 10, 26 }, + { OP(1, 1, 1, OP_GT_L_1), 0, 8, 32 }, + { OP(1, 1, 1, OP_GT_L_3), 2, 10, 34 }, + { OP(1, 1, 1, OP_NE_L_1), 0, 8, 40 }, + { OP(1, 1, 1, OP_NE_L_3), 2, 10, 42 }, + { OP(1, 1, 1, OP_GE_L_1), 0, 8, 48 }, + { OP(1, 1, 1, OP_GE_L_3), 2, 10, 50 }, + { OP(1, 1, 1, OP_LE_L_1), 0, 8, 56 }, + { OP(1, 1, 1, OP_LE_L_3), 2, 10, 58 }, +}; + +static dstatement_t long_cmpop_4_statements[] = { + { OP(1, 1, 1, OP_EQ_L_4), 0, 8, 16 }, + { OP(1, 1, 1, OP_LT_L_4), 0, 8, 24 }, + { OP(1, 1, 1, OP_GT_L_4), 0, 8, 32 }, + { OP(1, 1, 1, OP_NE_L_4), 0, 8, 40 }, + { OP(1, 1, 1, OP_GE_L_4), 0, 8, 48 }, + { OP(1, 1, 1, OP_LE_L_4), 0, 8, 56 }, +}; + +test_t tests[] = { + { + .desc = "long binop 1", + .extra_globals = 8 * 1, + .num_globals = num_globals(long_binop_init,long_binop_expect), + .num_statements = num_statements (long_binop_1_statements), + .statements = long_binop_1_statements, + .init_globals = (pr_int_t *) long_binop_init, + .expect_globals = (pr_int_t *) long_binop_expect, + }, + { + .desc = "long binop 2", + .extra_globals = 8 * 1, + .num_globals = num_globals(long_binop_init,long_binop_expect), + .num_statements = num_statements (long_binop_2_statements), + .statements = long_binop_2_statements, + .init_globals = (pr_int_t *) long_binop_init, + .expect_globals = (pr_int_t *) long_binop_expect, + }, + { + .desc = "long binop 3a", + .extra_globals = 8 * 1, + .num_globals = num_globals(long_binop_init,long_binop_expect), + .num_statements = num_statements (long_binop_3a_statements), + .statements = long_binop_3a_statements, + .init_globals = (pr_int_t *) long_binop_init, + .expect_globals = (pr_int_t *) long_binop_expect, + }, + { + .desc = "long binop 3b", + .extra_globals = 8 * 1, + .num_globals = num_globals(long_binop_init,long_binop_expect), + .num_statements = num_statements (long_binop_3b_statements), + .statements = long_binop_3b_statements, + .init_globals = (pr_int_t *) long_binop_init, + .expect_globals = (pr_int_t *) long_binop_expect, + }, + { + .desc = "long binop 4", + .extra_globals = 8 * 1, + .num_globals = num_globals(long_binop_init,long_binop_expect), + .num_statements = num_statements (long_binop_4_statements), + .statements = long_binop_4_statements, + .init_globals = (pr_int_t *) long_binop_init, + .expect_globals = (pr_int_t *) long_binop_expect, + }, + { + .desc = "long cmpop 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(long_cmpop_init,long_cmpop_expect), + .num_statements = num_statements (long_cmpop_1_statements), + .statements = long_cmpop_1_statements, + .init_globals = (pr_int_t *) long_cmpop_init, + .expect_globals = (pr_int_t *) long_cmpop_expect, + }, + { + .desc = "long cmpop 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(long_cmpop_init,long_cmpop_expect), + .num_statements = num_statements (long_cmpop_2_statements), + .statements = long_cmpop_2_statements, + .init_globals = (pr_int_t *) long_cmpop_init, + .expect_globals = (pr_int_t *) long_cmpop_expect, + }, + { + .desc = "long cmpop 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(long_cmpop_init,long_cmpop_expect), + .num_statements = num_statements (long_cmpop_3a_statements), + .statements = long_cmpop_3a_statements, + .init_globals = (pr_int_t *) long_cmpop_init, + .expect_globals = (pr_int_t *) long_cmpop_expect, + }, + { + .desc = "long cmpop 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(long_cmpop_init,long_cmpop_expect), + .num_statements = num_statements (long_cmpop_3b_statements), + .statements = long_cmpop_3b_statements, + .init_globals = (pr_int_t *) long_cmpop_init, + .expect_globals = (pr_int_t *) long_cmpop_expect, + }, + { + .desc = "long cmpop 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(long_cmpop_init,long_cmpop_expect), + .num_statements = num_statements (long_cmpop_4_statements), + .statements = long_cmpop_4_statements, + .init_globals = (pr_int_t *) long_cmpop_init, + .expect_globals = (pr_int_t *) long_cmpop_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-mem.c b/libs/gamecode/test/test-mem.c new file mode 100644 index 000000000..1bba532e8 --- /dev/null +++ b/libs/gamecode/test/test-mem.c @@ -0,0 +1,51 @@ +#include "head.c" + +#define DB 0xdeadbeef + +static pr_int_t mem_globals_init[] = { + 0, 8, 68, 9, 80, 112, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 0, 68, 6, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 + + DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, + DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, + DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, + DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, + DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, + DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, +}; + +static pr_int_t mem_globals_expect[] = { + 0, 8, 68, 9, 80, 112, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, // 0 + 0, 0, 68, 6, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 + + DB, 0, 0, 0, 0, 0, DB, DB, 9, 9, DB, DB, DB, DB, DB, DB, // 32 + 1, 2, 3, 4, 5, 6, 7, 8, DB, DB, DB, DB, 5, 6, 7, 8, // 48 + DB, DB, DB, DB, 1, 2, 1, 2, 3, 4, 5, 6, DB, DB, DB, DB, // 64 + 68, 68, 68, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, // 80 + DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, // 96 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, DB, // 112 +}; + +static dstatement_t mem_statements[] = { + {OP(0, 0, 0, OP_MEMSET_I), 0, 5, 33}, + {OP(0, 0, 0, OP_MEMSET_I), 3, 2, 40}, + {OP(0, 0, 0, OP_MOVE_I), 8, 8, 48}, + {OP(0, 0, 0, OP_MOVE_I), 12, 4, 60}, + {OP(0, 0, 0, OP_MOVE_PI), 1, 8, 2}, + {OP(0, 0, 0, OP_MEMSET_P), 2, 10, 4}, + {OP(0, 0, 0, OP_MEMSET_PI), 1, 15, 5}, + {OP(0, 0, 0, OP_MOVE_P), 18, 19, 20}, +}; + +test_t tests[] = { + { + .desc = "mem", + .num_globals = num_globals (mem_globals_init, mem_globals_expect), + .num_statements = num_statements (mem_statements), + .statements = mem_statements, + .init_globals = mem_globals_init, + .expect_globals = mem_globals_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-scale.c b/libs/gamecode/test/test-scale.c new file mode 100644 index 000000000..4117fd718 --- /dev/null +++ b/libs/gamecode/test/test-scale.c @@ -0,0 +1,68 @@ +#include "head.c" + +#include "QF/mathlib.h" + +static pr_vec4_t float_scale_init[] = { + { 5, 0, 0, 0}, + { 3, 4, 13, 85}, + { 0, 0, -1, -2}, + { 0, 0, 0, -3}, + { 0, 0, 0, 0}, +}; + +static pr_vec4_t float_scale_expect[] = { + { 5, 0, 0, 0}, + { 3, 4, 13, 85}, + { 15, 20, -1, -2}, + { 15, 20, 65, -3}, + { 15, 20, 65, 425}, +}; + +static dstatement_t float_scale_statements[] = { + { OP(1, 1, 1, OP_SCALE_F_2), 4, 0, 8 }, + { OP(1, 1, 1, OP_SCALE_F_3), 4, 0, 12 }, + { OP(1, 1, 1, OP_SCALE_F_4), 4, 0, 16 }, +}; + +static pr_dvec4_t double_scale_init[] = { + { 5, 0, 0, 0}, + { 3, 4, 13, 85}, + { 0, 0, -1, -2}, + { 0, 0, 0, -3}, + { 0, 0, 0, 0}, +}; + +static pr_dvec4_t double_scale_expect[] = { + { 5, 0, 0, 0}, + { 3, 4, 13, 85}, + { 15, 20, -1, -2}, + { 15, 20, 65, -3}, + { 15, 20, 65, 425}, +}; + +static dstatement_t double_scale_statements[] = { + { OP(1, 1, 1, OP_SCALE_D_2), 8, 0, 16 }, + { OP(1, 1, 1, OP_SCALE_D_3), 8, 0, 24 }, + { OP(1, 1, 1, OP_SCALE_D_4), 8, 0, 32 }, +}; + +test_t tests[] = { + { + .desc = "float scale", + .num_globals = num_globals(float_scale_init,float_scale_expect), + .num_statements = num_statements (float_scale_statements), + .statements = float_scale_statements, + .init_globals = (pr_int_t *) float_scale_init, + .expect_globals = (pr_int_t *) float_scale_expect, + }, + { + .desc = "double scale", + .num_globals = num_globals(double_scale_init,double_scale_expect), + .num_statements = num_statements (double_scale_statements), + .statements = double_scale_statements, + .init_globals = (pr_int_t *) double_scale_init, + .expect_globals = (pr_int_t *) double_scale_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-stack.c b/libs/gamecode/test/test-stack.c new file mode 100644 index 000000000..eba5a06d8 --- /dev/null +++ b/libs/gamecode/test/test-stack.c @@ -0,0 +1,233 @@ +#include "head.c" + +static pr_int_t test_globals_init1[] = { + // pointers + 24, 26, 28, 29, + 32, -4, -2, 0, + 1, 4, 0xdeadbeef, 0xfeedf00d, + // source data + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + // destination data + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, +}; + +static pr_int_t test_globals_expect1[] = { + // pointers + 24, 26, 28, 29, + 32, -4, -2, 0, + 1, 4, 0xdeadbeef, 0xfeedf00d, + // source data + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + // destination data + 11, 12, 9, 10, + 8, 5, 6, 7, + 1, 2, 3, 4, +}; + +static pr_int_t test_globals_init2[] = { + // pointers + 24, 26, 28, 29, + 32, -4, -2, 0, + 1, 4, 0xdeadbeef, 0xfeedf00d, + // destination data + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + // source data + 11, 12, 9, 10, + 8, 5, 6, 7, + 1, 2, 3, 4, +}; + +static pr_int_t test_globals_expect2[] = { + // pointers + 24, 26, 28, 29, + 32, -4, -2, 0, + 1, 4, 0xdeadbeef, 0xfeedf00d, + // destination data + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + // source data + 11, 12, 9, 10, + 8, 5, 6, 7, + 1, 2, 3, 4, +}; + +static dstatement_t stack_AA_statements[] = { + {OP(0, 0, 0, OP_PUSH_A_4), 12, 0, 0}, + {OP(0, 0, 0, OP_PUSH_A_3), 16, 0, 0}, + {OP(0, 0, 0, OP_PUSH_A_1), 19, 0, 0}, + {OP(0, 0, 0, OP_PUSH_A_2), 20, 0, 0}, + {OP(0, 0, 0, OP_PUSH_A_2), 22, 0, 0}, + + {OP(0, 0, 0, OP_POP_A_2), 24, 0, 0}, + {OP(0, 0, 0, OP_POP_A_2), 26, 0, 0}, + {OP(0, 0, 0, OP_POP_A_1), 28, 0, 0}, + {OP(0, 0, 0, OP_POP_A_3), 29, 0, 0}, + {OP(0, 0, 0, OP_POP_A_4), 32, 0, 0}, +}; + +static dstatement_t stack_AB_statements[] = { + {OP(0, 0, 0, OP_PUSH_A_4), 12, 0, 0}, + {OP(0, 0, 0, OP_PUSH_A_3), 16, 0, 0}, + {OP(0, 0, 0, OP_PUSH_A_1), 19, 0, 0}, + {OP(0, 0, 0, OP_PUSH_A_2), 20, 0, 0}, + {OP(0, 0, 0, OP_PUSH_A_2), 22, 0, 0}, + + {OP(0, 0, 0, OP_POP_B_2), 7, 5, 0}, + {OP(0, 0, 0, OP_POP_B_2), 7, 6, 0}, + {OP(0, 0, 0, OP_POP_B_1), 7, 7, 0}, + {OP(0, 0, 0, OP_POP_B_3), 7, 8, 0}, + {OP(0, 0, 0, OP_POP_B_4), 7, 9, 0}, +}; + +static dstatement_t stack_AC_statements[] = { + {OP(0, 0, 0, OP_PUSH_A_4), 12, 0, 0}, + {OP(0, 0, 0, OP_PUSH_A_3), 16, 0, 0}, + {OP(0, 0, 0, OP_PUSH_A_1), 19, 0, 0}, + {OP(0, 0, 0, OP_PUSH_A_2), 20, 0, 0}, + {OP(0, 0, 0, OP_PUSH_A_2), 22, 0, 0}, + + {OP(0, 0, 0, OP_POP_C_2), 2, -4, 0}, + {OP(0, 0, 0, OP_POP_C_2), 2, -2, 0}, + {OP(0, 0, 0, OP_POP_C_1), 2, 0, 0}, + {OP(0, 0, 0, OP_POP_C_3), 2, 1, 0}, + {OP(0, 0, 0, OP_POP_C_4), 2, 4, 0}, +}; + +static dstatement_t stack_AD_statements[] = { + {OP(0, 0, 0, OP_PUSH_A_4), 12, 0, 0}, + {OP(0, 0, 0, OP_PUSH_A_3), 16, 0, 0}, + {OP(0, 0, 0, OP_PUSH_A_1), 19, 0, 0}, + {OP(0, 0, 0, OP_PUSH_A_2), 20, 0, 0}, + {OP(0, 0, 0, OP_PUSH_A_2), 22, 0, 0}, + + {OP(0, 0, 0, OP_POP_D_2), 2, 5, 0}, + {OP(0, 0, 0, OP_POP_D_2), 2, 6, 0}, + {OP(0, 0, 0, OP_POP_D_1), 2, 7, 0}, + {OP(0, 0, 0, OP_POP_D_3), 2, 8, 0}, + {OP(0, 0, 0, OP_POP_D_4), 2, 9, 0}, +}; + +static dstatement_t stack_BA_statements[] = { + {OP(0, 0, 0, OP_PUSH_B_2), 7, 5, 0}, + {OP(0, 0, 0, OP_PUSH_B_2), 7, 6, 0}, + {OP(0, 0, 0, OP_PUSH_B_1), 7, 7, 0}, + {OP(0, 0, 0, OP_PUSH_B_3), 7, 8, 0}, + {OP(0, 0, 0, OP_PUSH_B_4), 7, 9, 0}, + + {OP(0, 0, 0, OP_POP_A_4), 12, 0, 0}, + {OP(0, 0, 0, OP_POP_A_3), 16, 0, 0}, + {OP(0, 0, 0, OP_POP_A_1), 19, 0, 0}, + {OP(0, 0, 0, OP_POP_A_2), 20, 0, 0}, + {OP(0, 0, 0, OP_POP_A_2), 22, 0, 0}, +}; + +static dstatement_t stack_CA_statements[] = { + {OP(0, 0, 0, OP_PUSH_C_2), 2, -4, 0}, + {OP(0, 0, 0, OP_PUSH_C_2), 2, -2, 0}, + {OP(0, 0, 0, OP_PUSH_C_1), 2, 0, 0}, + {OP(0, 0, 0, OP_PUSH_C_3), 2, 1, 0}, + {OP(0, 0, 0, OP_PUSH_C_4), 2, 4, 0}, + + {OP(0, 0, 0, OP_POP_A_4), 12, 0, 0}, + {OP(0, 0, 0, OP_POP_A_3), 16, 0, 0}, + {OP(0, 0, 0, OP_POP_A_1), 19, 0, 0}, + {OP(0, 0, 0, OP_POP_A_2), 20, 0, 0}, + {OP(0, 0, 0, OP_POP_A_2), 22, 0, 0}, +}; + +static dstatement_t stack_DA_statements[] = { + {OP(0, 0, 0, OP_PUSH_D_2), 2, 5, 0}, + {OP(0, 0, 0, OP_PUSH_D_2), 2, 6, 0}, + {OP(0, 0, 0, OP_PUSH_D_1), 2, 7, 0}, + {OP(0, 0, 0, OP_PUSH_D_3), 2, 8, 0}, + {OP(0, 0, 0, OP_PUSH_D_4), 2, 9, 0}, + + {OP(0, 0, 0, OP_POP_A_4), 12, 0, 0}, + {OP(0, 0, 0, OP_POP_A_3), 16, 0, 0}, + {OP(0, 0, 0, OP_POP_A_1), 19, 0, 0}, + {OP(0, 0, 0, OP_POP_A_2), 20, 0, 0}, + {OP(0, 0, 0, OP_POP_A_2), 22, 0, 0}, +}; + +test_t tests[] = { + { + .desc = "stack push A pop A", + .stack_size = 32, + .num_globals = num_globals (test_globals_init1, test_globals_expect1), + .num_statements = num_statements (stack_AA_statements), + .statements = stack_AA_statements, + .init_globals = test_globals_init1, + .expect_globals = test_globals_expect1, + }, + { + .desc = "stack push A pop B", + .stack_size = 32, + .num_globals = num_globals (test_globals_init1, test_globals_expect1), + .num_statements = num_statements (stack_AB_statements), + .statements = stack_AB_statements, + .init_globals = test_globals_init1, + .expect_globals = test_globals_expect1, + // FIXME negative field offsets are not official but work because all + // offset calculations are done in 32-bit and thus wrap anyway + .edict_area = 28, + }, + { + .desc = "stack push A pop C", + .stack_size = 32, + .num_globals = num_globals (test_globals_init1, test_globals_expect1), + .num_statements = num_statements (stack_AC_statements), + .statements = stack_AC_statements, + .init_globals = test_globals_init1, + .expect_globals = test_globals_expect1, + }, + { + .desc = "stack push A pop D", + .stack_size = 32, + .num_globals = num_globals (test_globals_init1, test_globals_expect1), + .num_statements = num_statements (stack_AD_statements), + .statements = stack_AD_statements, + .init_globals = test_globals_init1, + .expect_globals = test_globals_expect1, + }, + { + .desc = "stack push B pop A", + .stack_size = 32, + .num_globals = num_globals (test_globals_init2, test_globals_expect2), + .num_statements = num_statements (stack_BA_statements), + .statements = stack_BA_statements, + .init_globals = test_globals_init2, + .expect_globals = test_globals_expect2, + // FIXME negative field offsets are not official but work because all + // offset calculations are done in 32-bit and thus wrap anyway + .edict_area = 28, + }, + { + .desc = "stack push C pop A", + .stack_size = 32, + .num_globals = num_globals (test_globals_init2, test_globals_expect2), + .num_statements = num_statements (stack_CA_statements), + .statements = stack_CA_statements, + .init_globals = test_globals_init2, + .expect_globals = test_globals_expect2, + }, + { + .desc = "stack push D pop A", + .stack_size = 32, + .num_globals = num_globals (test_globals_init2, test_globals_expect2), + .num_statements = num_statements (stack_DA_statements), + .statements = stack_DA_statements, + .init_globals = test_globals_init2, + .expect_globals = test_globals_expect2, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-state.c b/libs/gamecode/test/test-state.c new file mode 100644 index 000000000..f7a45076d --- /dev/null +++ b/libs/gamecode/test/test-state.c @@ -0,0 +1,99 @@ +#include "head.c" + +#include "QF/mathlib.h" + +#define DB 0xdeadbeef + +static float float_time = 1; + +static pr_ivec4_t float_state_init[] = { + { 0, 0, DB, DB }, + { 10, 11, 20, 21 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, +}; + +#define f1 0x3f800000 +#define f11 0x3f8ccccd +#define f2 0x40000000 +static pr_ivec4_t float_state_expect[] = { + { 0, 8, f1, DB }, + { 10, 11, 20, 21 }, + { 0, 0, 0, 0 }, + { 10, 20, f11, 0 }, + { 11, 21, f2, 0 }, +}; + +static dstatement_t float_state_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 1 }, + { OP(0, 0, 0, OP_STATE_ft), 4, 6, 0 }, + { OP(0, 0, 0, OP_LEA_A), 8, 0, 1 }, + { OP(0, 0, 0, OP_STATE_ftt), 5, 7, 2 }, +}; + +static double double_time = 1; + +static pr_ivec4_t double_state_init[] = { + { 0, 0, DB, DB }, + { 10, 11, 20, 21 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, +}; + +#define d1l 0x00000000 +#define d1h 0x3ff00000 +#define d11l 0x9999999a +#define d11h 0x3ff19999 +#define d2l 0x00000000 +#define d2h 0x40000000 +static pr_ivec4_t double_state_expect[] = { + { 0, 8, d1l, d1h }, + { 10, 11, 20, 21 }, + { 0, 0, 0, 0 }, + { 10, 20, d11l, d11h }, + { 11, 21, d2l, d2h }, +}; + +static dstatement_t double_state_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 1 }, + { OP(0, 0, 0, OP_STATE_dt), 4, 6, 0 }, + { OP(0, 0, 0, OP_LEA_A), 8, 0, 1 }, + { OP(0, 0, 0, OP_STATE_dtt), 5, 7, 2 }, +}; + +test_t tests[] = { + { + .desc = "float state", + .num_globals = num_globals(float_state_init,float_state_expect), + .num_statements = num_statements (float_state_statements), + .statements = float_state_statements, + .init_globals = (pr_int_t *) float_state_init, + .expect_globals = (pr_int_t *) float_state_expect, + .self = 1, + .ftime = 2, + .float_time = &float_time, + .edict_area = 8, + .frame = 0, + .think = 1, + .nextthink = 2, + }, + { + .desc = "double state", + .num_globals = num_globals(double_state_init,double_state_expect), + .num_statements = num_statements (double_state_statements), + .statements = double_state_statements, + .init_globals = (pr_int_t *) double_state_init, + .expect_globals = (pr_int_t *) double_state_expect, + .self = 1, + .dtime = 2, + .double_time = &double_time, + .edict_area = 8, + .frame = 0, + .think = 1, + .nextthink = 2, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-store.c b/libs/gamecode/test/test-store.c new file mode 100644 index 000000000..5ed5147b8 --- /dev/null +++ b/libs/gamecode/test/test-store.c @@ -0,0 +1,103 @@ +#include "head.c" + +static pr_int_t test_globals_init[] = { + // pointers + 24, 26, 28, 29, + 32, -4, -2, 0, + 1, 4, 0xdeadbeef, 0xfeedf00d, + // source data + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + // destination data + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, +}; + +static pr_int_t test_globals_expect[] = { + // pointers + 24, 26, 28, 29, + 32, -4, -2, 0, + 1, 4, 0xdeadbeef, 0xfeedf00d, + // source data + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + // destination data + 11, 12, 9, 10, + 8, 5, 6, 7, + 1, 2, 3, 4, +}; + +static dstatement_t store_A_statements[] = { + {OP(0, 0, 0, OP_STORE_A_4), 32, 0, 12}, + {OP(0, 0, 0, OP_STORE_A_3), 29, 0, 16}, + {OP(0, 0, 0, OP_STORE_A_1), 28, 0, 19}, + {OP(0, 0, 0, OP_STORE_A_2), 26, 0, 20}, + {OP(0, 0, 0, OP_STORE_A_2), 24, 0, 22}, +}; + +static dstatement_t store_B_statements[] = { + {OP(0, 0, 0, OP_STORE_B_4), 7, 9, 12}, + {OP(0, 0, 0, OP_STORE_B_3), 7, 8, 16}, + {OP(0, 0, 0, OP_STORE_B_1), 7, 7, 19}, + {OP(0, 0, 0, OP_STORE_B_2), 7, 6, 20}, + {OP(0, 0, 0, OP_STORE_B_2), 7, 5, 22}, +}; + +static dstatement_t store_C_statements[] = { + {OP(0, 0, 0, OP_STORE_C_4), 2, 4, 12}, + {OP(0, 0, 0, OP_STORE_C_3), 2, 1, 16}, + {OP(0, 0, 0, OP_STORE_C_1), 2, 0, 19}, + {OP(0, 0, 0, OP_STORE_C_2), 2, -2, 20}, + {OP(0, 0, 0, OP_STORE_C_2), 2, -4, 22}, +}; + +static dstatement_t store_D_statements[] = { + {OP(0, 0, 0, OP_STORE_D_4), 2, 9, 12}, + {OP(0, 0, 0, OP_STORE_D_3), 2, 8, 16}, + {OP(0, 0, 0, OP_STORE_D_1), 2, 7, 19}, + {OP(0, 0, 0, OP_STORE_D_2), 2, 6, 20}, + {OP(0, 0, 0, OP_STORE_D_2), 2, 5, 22}, +}; + +test_t tests[] = { + { + .desc = "store A", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (store_A_statements), + .statements = store_A_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, + { + .desc = "store B", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (store_B_statements), + .statements = store_B_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + // FIXME negative field offsets are not official but work because all + // offset calculations are done in 32-bit and thus wrap anyway + .edict_area = 28, + }, + { + .desc = "store C", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (store_C_statements), + .statements = store_C_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, + { + .desc = "store D", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (store_D_statements), + .statements = store_D_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-store64.c b/libs/gamecode/test/test-store64.c new file mode 100644 index 000000000..d8dfd2018 --- /dev/null +++ b/libs/gamecode/test/test-store64.c @@ -0,0 +1,105 @@ +#include "head.c" + +static pr_int_t test_globals_init[] = { + // pointers + 24, 26, 48, 29, + 32, -8, -4, 0, + 2, 8, 0xdeadbeef, 0xfeedf00d, + 0xdeadbeef, 0xfeedf00d, 0xdeadbeef, 0xfeedf00d, + // source data +/*16*/ 1, 2, 3, 4, 5, 6, 7, 8, +/*24*/ 9, 10, 11, 12, 13, 14, 15, 16, +/*32*/ 17, 18, 19, 20, 21, 22, 23, 24, + // destination data + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static pr_int_t test_globals_expect[] = { + // pointers + 24, 26, 48, 29, + 32, -8, -4, 0, + 2, 8, 0xdeadbeef, 0xfeedf00d, + 0xdeadbeef, 0xfeedf00d, 0xdeadbeef, 0xfeedf00d, + // source data +/*16*/ 1, 2, 3, 4, 5, 6, 7, 8, +/*24*/ 9, 10, 11, 12, 13, 14, 15, 16, +/*32*/ 17, 18, 19, 20, 21, 22, 23, 24, + // destination data +/*40*/ 21, 22, 23, 24, 17, 18, 19, 20, +/*48*/ 15, 16, 9, 10, 11, 12, 13, 14, +/*56*/ 1, 2, 3, 4, 5, 6, 7, 8, +}; + +static dstatement_t store64_A_statements[] = { + {OP(0, 0, 0, OP_STORE64_A_4), 56, 0, 16}, + {OP(0, 0, 0, OP_STORE64_A_3), 50, 0, 24}, + {OP(0, 0, 0, OP_STORE_A_2), 48, 0, 30}, + {OP(0, 0, 0, OP_STORE_A_4), 44, 0, 32}, + {OP(0, 0, 0, OP_STORE_A_4), 40, 0, 36}, +}; + +static dstatement_t store64_B_statements[] = { + {OP(0, 0, 0, OP_STORE64_B_4), 7, 9, 16}, + {OP(0, 0, 0, OP_STORE64_B_3), 7, 8, 24}, + {OP(0, 0, 0, OP_STORE_B_2), 7, 7, 30}, + {OP(0, 0, 0, OP_STORE_B_4), 7, 6, 32}, + {OP(0, 0, 0, OP_STORE_B_4), 7, 5, 36}, +}; + +static dstatement_t store64_C_statements[] = { + {OP(0, 0, 0, OP_STORE64_C_4), 2, 8, 16}, + {OP(0, 0, 0, OP_STORE64_C_3), 2, 2, 24}, + {OP(0, 0, 0, OP_STORE_C_2), 2, 0, 30}, + {OP(0, 0, 0, OP_STORE_C_4), 2, -4, 32}, + {OP(0, 0, 0, OP_STORE_C_4), 2, -8, 36}, +}; + +static dstatement_t store64_D_statements[] = { + {OP(0, 0, 0, OP_STORE64_D_4), 2, 9, 16}, + {OP(0, 0, 0, OP_STORE64_D_3), 2, 8, 24}, + {OP(0, 0, 0, OP_STORE_D_2), 2, 7, 30}, + {OP(0, 0, 0, OP_STORE_D_4), 2, 6, 32}, + {OP(0, 0, 0, OP_STORE_D_4), 2, 5, 36}, +}; + +test_t tests[] = { + { + .desc = "store64 A", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (store64_A_statements), + .statements = store64_A_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, + { + .desc = "store64 B", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (store64_B_statements), + .statements = store64_B_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + // FIXME negative field offsets are not official but work because all + // offset calculations are done in 32-bit and thus wrap anyway + .edict_area = 48, + }, + { + .desc = "store64 C", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (store64_C_statements), + .statements = store64_C_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, + { + .desc = "store64 D", + .num_globals = num_globals (test_globals_init, test_globals_expect), + .num_statements = num_statements (store64_D_statements), + .statements = store64_D_statements, + .init_globals = test_globals_init, + .expect_globals = test_globals_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-string.c b/libs/gamecode/test/test-string.c new file mode 100644 index 000000000..955431d19 --- /dev/null +++ b/libs/gamecode/test/test-string.c @@ -0,0 +1,88 @@ +#include "head.c" + +const char test_strings[] = + "\0" + "abc\0" + "def\0" + "abc\0"; + +static pr_int_t string_globals_init[] = { + 0, 1, 5, 9, // string pointers + 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, +}; + +static pr_int_t string_globals_expect[] = { + 0, 1, 5, 9, // string pointers + 0, 0, 8, 0, + +// "\0" "abc" "def" "abc" + -1, 0, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, -1, 0, -1, // eq + 0, -1, -1, -1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, // lt + 0, 0, 0, 0, -1, 0, 0, 0, -1, -1, 0, -1, -1, 0, 0, 0, // gt + 0, -1, -1, -1, 1, 0, -1, 0, 1, 1, 0, 1, 1, 0, -1, 0, // cmp + -1, 0, 0, 0, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, 0, -1, // ge + -1, -1, -1, -1, 0, -1, -1, -1, 0, 0, -1, 0, 0, -1, -1, -1, // le + -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // not +}; + +static dstatement_t string_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 24, 0, 6 }, // init k +// for (i = 4; i-- > 0; ) { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 4 }, + { OP(0, 0, 0, OP_IFA), 2, 0, 4 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_LEA_C), 4, -1, 4 }, // dec i + + { OP(0, 0, 0, OP_WITH), 4, 4, 1 }, // load i + +// for (j = 4; j-- > 0; ) { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 5 }, // init j + + { OP(0, 0, 0, OP_IFA), 2, 0, 5 }, + { OP(0, 0, 0, OP_JUMP_A), -6, 0, 0 }, + { OP(0, 0, 0, OP_LEA_C), 5, -1, 5 }, // dec j + + { OP(0, 0, 0, OP_WITH), 4, 5, 2 }, // load j + + { OP(0, 0, 0, OP_LEA_C), 6, -1, 6 }, // dec k + { OP(0, 0, 0, OP_WITH), 4, 6, 3 }, // load k + + // i j k + { OP(1, 2, 3, OP_EQ_S), 0, 0, 0 }, + { OP(1, 2, 3, OP_LT_S), 0, 0, 16 }, + { OP(1, 2, 3, OP_GT_S), 0, 0, 32 }, + { OP(1, 2, 3, OP_CMP_S), 0, 0, 48 }, + { OP(3, 0, 0, OP_LT_I_1), 48, 0, 7 }, // convert < 0, 0, > 0 to -1, 0, 1 + { OP(3, 0, 3, OP_GT_I_1), 48, 0, 48 }, // ... + { OP(0, 3, 3, OP_SUB_I_1), 7, 48, 48 }, // ... + { OP(1, 2, 3, OP_GE_S), 0, 0, 64 }, + { OP(1, 2, 3, OP_LE_S), 0, 0, 80 }, + { OP(1, 2, 3, OP_NOT_S), 0, 0, 96 }, + +// } + { OP(0, 0, 0, OP_JUMP_A), -16, 0, 0 }, +// } +}; + +test_t tests[] = { + { + .desc = "string", + .extra_globals = 4 * 1, + .num_globals = num_globals (string_globals_init, string_globals_expect), + .num_statements = num_statements (string_statements), + .statements = string_statements, + .init_globals = string_globals_init, + .expect_globals = string_globals_expect, + .strings = test_strings, + .string_size = sizeof (test_strings), + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-swizzle.c b/libs/gamecode/test/test-swizzle.c new file mode 100644 index 000000000..c47acbb5c --- /dev/null +++ b/libs/gamecode/test/test-swizzle.c @@ -0,0 +1,1846 @@ +#include "head.c" + +static pr_vec4_t swizzle_f_init[] = { + { 1, 2, 3, 4 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, +}; +static pr_vec4_t swizzle_f_expect[] = { + { 1, 2, 3, 4 }, + { 1, 1, 1, 1 }, + { 2, 1, 1, 1 }, + { 3, 1, 1, 1 }, + { 4, 1, 1, 1 }, + { 1, 2, 1, 1 }, + { 2, 2, 1, 1 }, + { 3, 2, 1, 1 }, + { 4, 2, 1, 1 }, + { 1, 3, 1, 1 }, + { 2, 3, 1, 1 }, + { 3, 3, 1, 1 }, + { 4, 3, 1, 1 }, + { 1, 4, 1, 1 }, + { 2, 4, 1, 1 }, + { 3, 4, 1, 1 }, + { 4, 4, 1, 1 }, + { 1, 1, 2, 1 }, + { 2, 1, 2, 1 }, + { 3, 1, 2, 1 }, + { 4, 1, 2, 1 }, + { 1, 2, 2, 1 }, + { 2, 2, 2, 1 }, + { 3, 2, 2, 1 }, + { 4, 2, 2, 1 }, + { 1, 3, 2, 1 }, + { 2, 3, 2, 1 }, + { 3, 3, 2, 1 }, + { 4, 3, 2, 1 }, + { 1, 4, 2, 1 }, + { 2, 4, 2, 1 }, + { 3, 4, 2, 1 }, + { 4, 4, 2, 1 }, + { 1, 1, 3, 1 }, + { 2, 1, 3, 1 }, + { 3, 1, 3, 1 }, + { 4, 1, 3, 1 }, + { 1, 2, 3, 1 }, + { 2, 2, 3, 1 }, + { 3, 2, 3, 1 }, + { 4, 2, 3, 1 }, + { 1, 3, 3, 1 }, + { 2, 3, 3, 1 }, + { 3, 3, 3, 1 }, + { 4, 3, 3, 1 }, + { 1, 4, 3, 1 }, + { 2, 4, 3, 1 }, + { 3, 4, 3, 1 }, + { 4, 4, 3, 1 }, + { 1, 1, 4, 1 }, + { 2, 1, 4, 1 }, + { 3, 1, 4, 1 }, + { 4, 1, 4, 1 }, + { 1, 2, 4, 1 }, + { 2, 2, 4, 1 }, + { 3, 2, 4, 1 }, + { 4, 2, 4, 1 }, + { 1, 3, 4, 1 }, + { 2, 3, 4, 1 }, + { 3, 3, 4, 1 }, + { 4, 3, 4, 1 }, + { 1, 4, 4, 1 }, + { 2, 4, 4, 1 }, + { 3, 4, 4, 1 }, + { 4, 4, 4, 1 }, + { 1, 1, 1, 2 }, + { 2, 1, 1, 2 }, + { 3, 1, 1, 2 }, + { 4, 1, 1, 2 }, + { 1, 2, 1, 2 }, + { 2, 2, 1, 2 }, + { 3, 2, 1, 2 }, + { 4, 2, 1, 2 }, + { 1, 3, 1, 2 }, + { 2, 3, 1, 2 }, + { 3, 3, 1, 2 }, + { 4, 3, 1, 2 }, + { 1, 4, 1, 2 }, + { 2, 4, 1, 2 }, + { 3, 4, 1, 2 }, + { 4, 4, 1, 2 }, + { 1, 1, 2, 2 }, + { 2, 1, 2, 2 }, + { 3, 1, 2, 2 }, + { 4, 1, 2, 2 }, + { 1, 2, 2, 2 }, + { 2, 2, 2, 2 }, + { 3, 2, 2, 2 }, + { 4, 2, 2, 2 }, + { 1, 3, 2, 2 }, + { 2, 3, 2, 2 }, + { 3, 3, 2, 2 }, + { 4, 3, 2, 2 }, + { 1, 4, 2, 2 }, + { 2, 4, 2, 2 }, + { 3, 4, 2, 2 }, + { 4, 4, 2, 2 }, + { 1, 1, 3, 2 }, + { 2, 1, 3, 2 }, + { 3, 1, 3, 2 }, + { 4, 1, 3, 2 }, + { 1, 2, 3, 2 }, + { 2, 2, 3, 2 }, + { 3, 2, 3, 2 }, + { 4, 2, 3, 2 }, + { 1, 3, 3, 2 }, + { 2, 3, 3, 2 }, + { 3, 3, 3, 2 }, + { 4, 3, 3, 2 }, + { 1, 4, 3, 2 }, + { 2, 4, 3, 2 }, + { 3, 4, 3, 2 }, + { 4, 4, 3, 2 }, + { 1, 1, 4, 2 }, + { 2, 1, 4, 2 }, + { 3, 1, 4, 2 }, + { 4, 1, 4, 2 }, + { 1, 2, 4, 2 }, + { 2, 2, 4, 2 }, + { 3, 2, 4, 2 }, + { 4, 2, 4, 2 }, + { 1, 3, 4, 2 }, + { 2, 3, 4, 2 }, + { 3, 3, 4, 2 }, + { 4, 3, 4, 2 }, + { 1, 4, 4, 2 }, + { 2, 4, 4, 2 }, + { 3, 4, 4, 2 }, + { 4, 4, 4, 2 }, + { 1, 1, 1, 3 }, + { 2, 1, 1, 3 }, + { 3, 1, 1, 3 }, + { 4, 1, 1, 3 }, + { 1, 2, 1, 3 }, + { 2, 2, 1, 3 }, + { 3, 2, 1, 3 }, + { 4, 2, 1, 3 }, + { 1, 3, 1, 3 }, + { 2, 3, 1, 3 }, + { 3, 3, 1, 3 }, + { 4, 3, 1, 3 }, + { 1, 4, 1, 3 }, + { 2, 4, 1, 3 }, + { 3, 4, 1, 3 }, + { 4, 4, 1, 3 }, + { 1, 1, 2, 3 }, + { 2, 1, 2, 3 }, + { 3, 1, 2, 3 }, + { 4, 1, 2, 3 }, + { 1, 2, 2, 3 }, + { 2, 2, 2, 3 }, + { 3, 2, 2, 3 }, + { 4, 2, 2, 3 }, + { 1, 3, 2, 3 }, + { 2, 3, 2, 3 }, + { 3, 3, 2, 3 }, + { 4, 3, 2, 3 }, + { 1, 4, 2, 3 }, + { 2, 4, 2, 3 }, + { 3, 4, 2, 3 }, + { 4, 4, 2, 3 }, + { 1, 1, 3, 3 }, + { 2, 1, 3, 3 }, + { 3, 1, 3, 3 }, + { 4, 1, 3, 3 }, + { 1, 2, 3, 3 }, + { 2, 2, 3, 3 }, + { 3, 2, 3, 3 }, + { 4, 2, 3, 3 }, + { 1, 3, 3, 3 }, + { 2, 3, 3, 3 }, + { 3, 3, 3, 3 }, + { 4, 3, 3, 3 }, + { 1, 4, 3, 3 }, + { 2, 4, 3, 3 }, + { 3, 4, 3, 3 }, + { 4, 4, 3, 3 }, + { 1, 1, 4, 3 }, + { 2, 1, 4, 3 }, + { 3, 1, 4, 3 }, + { 4, 1, 4, 3 }, + { 1, 2, 4, 3 }, + { 2, 2, 4, 3 }, + { 3, 2, 4, 3 }, + { 4, 2, 4, 3 }, + { 1, 3, 4, 3 }, + { 2, 3, 4, 3 }, + { 3, 3, 4, 3 }, + { 4, 3, 4, 3 }, + { 1, 4, 4, 3 }, + { 2, 4, 4, 3 }, + { 3, 4, 4, 3 }, + { 4, 4, 4, 3 }, + { 1, 1, 1, 4 }, + { 2, 1, 1, 4 }, + { 3, 1, 1, 4 }, + { 4, 1, 1, 4 }, + { 1, 2, 1, 4 }, + { 2, 2, 1, 4 }, + { 3, 2, 1, 4 }, + { 4, 2, 1, 4 }, + { 1, 3, 1, 4 }, + { 2, 3, 1, 4 }, + { 3, 3, 1, 4 }, + { 4, 3, 1, 4 }, + { 1, 4, 1, 4 }, + { 2, 4, 1, 4 }, + { 3, 4, 1, 4 }, + { 4, 4, 1, 4 }, + { 1, 1, 2, 4 }, + { 2, 1, 2, 4 }, + { 3, 1, 2, 4 }, + { 4, 1, 2, 4 }, + { 1, 2, 2, 4 }, + { 2, 2, 2, 4 }, + { 3, 2, 2, 4 }, + { 4, 2, 2, 4 }, + { 1, 3, 2, 4 }, + { 2, 3, 2, 4 }, + { 3, 3, 2, 4 }, + { 4, 3, 2, 4 }, + { 1, 4, 2, 4 }, + { 2, 4, 2, 4 }, + { 3, 4, 2, 4 }, + { 4, 4, 2, 4 }, + { 1, 1, 3, 4 }, + { 2, 1, 3, 4 }, + { 3, 1, 3, 4 }, + { 4, 1, 3, 4 }, + { 1, 2, 3, 4 }, + { 2, 2, 3, 4 }, + { 3, 2, 3, 4 }, + { 4, 2, 3, 4 }, + { 1, 3, 3, 4 }, + { 2, 3, 3, 4 }, + { 3, 3, 3, 4 }, + { 4, 3, 3, 4 }, + { 1, 4, 3, 4 }, + { 2, 4, 3, 4 }, + { 3, 4, 3, 4 }, + { 4, 4, 3, 4 }, + { 1, 1, 4, 4 }, + { 2, 1, 4, 4 }, + { 3, 1, 4, 4 }, + { 4, 1, 4, 4 }, + { 1, 2, 4, 4 }, + { 2, 2, 4, 4 }, + { 3, 2, 4, 4 }, + { 4, 2, 4, 4 }, + { 1, 3, 4, 4 }, + { 2, 3, 4, 4 }, + { 3, 3, 4, 4 }, + { 4, 3, 4, 4 }, + { 1, 4, 4, 4 }, + { 2, 4, 4, 4 }, + { 3, 4, 4, 4 }, + { 4, 4, 4, 4 }, +}; + +static dstatement_t swizzle_f_statements[] = { + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0000, 4 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0001, 8 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0002, 12 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0003, 16 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0004, 20 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0005, 24 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0006, 28 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0007, 32 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0008, 36 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0009, 40 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x000a, 44 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x000b, 48 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x000c, 52 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x000d, 56 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x000e, 60 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x000f, 64 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0010, 68 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0011, 72 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0012, 76 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0013, 80 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0014, 84 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0015, 88 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0016, 92 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0017, 96 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0018, 100 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0019, 104 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x001a, 108 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x001b, 112 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x001c, 116 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x001d, 120 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x001e, 124 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x001f, 128 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0020, 132 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0021, 136 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0022, 140 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0023, 144 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0024, 148 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0025, 152 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0026, 156 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0027, 160 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0028, 164 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0029, 168 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x002a, 172 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x002b, 176 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x002c, 180 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x002d, 184 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x002e, 188 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x002f, 192 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0030, 196 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0031, 200 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0032, 204 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0033, 208 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0034, 212 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0035, 216 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0036, 220 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0037, 224 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0038, 228 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0039, 232 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x003a, 236 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x003b, 240 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x003c, 244 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x003d, 248 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x003e, 252 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x003f, 256 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0040, 260 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0041, 264 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0042, 268 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0043, 272 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0044, 276 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0045, 280 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0046, 284 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0047, 288 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0048, 292 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0049, 296 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x004a, 300 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x004b, 304 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x004c, 308 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x004d, 312 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x004e, 316 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x004f, 320 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0050, 324 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0051, 328 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0052, 332 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0053, 336 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0054, 340 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0055, 344 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0056, 348 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0057, 352 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0058, 356 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0059, 360 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x005a, 364 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x005b, 368 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x005c, 372 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x005d, 376 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x005e, 380 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x005f, 384 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0060, 388 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0061, 392 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0062, 396 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0063, 400 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0064, 404 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0065, 408 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0066, 412 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0067, 416 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0068, 420 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0069, 424 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x006a, 428 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x006b, 432 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x006c, 436 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x006d, 440 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x006e, 444 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x006f, 448 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0070, 452 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0071, 456 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0072, 460 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0073, 464 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0074, 468 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0075, 472 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0076, 476 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0077, 480 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0078, 484 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0079, 488 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x007a, 492 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x007b, 496 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x007c, 500 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x007d, 504 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x007e, 508 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x007f, 512 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0080, 516 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0081, 520 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0082, 524 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0083, 528 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0084, 532 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0085, 536 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0086, 540 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0087, 544 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0088, 548 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0089, 552 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x008a, 556 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x008b, 560 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x008c, 564 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x008d, 568 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x008e, 572 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x008f, 576 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0090, 580 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0091, 584 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0092, 588 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0093, 592 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0094, 596 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0095, 600 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0096, 604 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0097, 608 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0098, 612 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0099, 616 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x009a, 620 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x009b, 624 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x009c, 628 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x009d, 632 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x009e, 636 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x009f, 640 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00a0, 644 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00a1, 648 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00a2, 652 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00a3, 656 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00a4, 660 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00a5, 664 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00a6, 668 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00a7, 672 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00a8, 676 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00a9, 680 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00aa, 684 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00ab, 688 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00ac, 692 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00ad, 696 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00ae, 700 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00af, 704 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00b0, 708 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00b1, 712 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00b2, 716 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00b3, 720 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00b4, 724 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00b5, 728 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00b6, 732 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00b7, 736 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00b8, 740 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00b9, 744 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00ba, 748 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00bb, 752 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00bc, 756 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00bd, 760 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00be, 764 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00bf, 768 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00c0, 772 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00c1, 776 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00c2, 780 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00c3, 784 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00c4, 788 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00c5, 792 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00c6, 796 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00c7, 800 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00c8, 804 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00c9, 808 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00ca, 812 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00cb, 816 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00cc, 820 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00cd, 824 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00ce, 828 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00cf, 832 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00d0, 836 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00d1, 840 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00d2, 844 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00d3, 848 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00d4, 852 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00d5, 856 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00d6, 860 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00d7, 864 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00d8, 868 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00d9, 872 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00da, 876 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00db, 880 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00dc, 884 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00dd, 888 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00de, 892 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00df, 896 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00e0, 900 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00e1, 904 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00e2, 908 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00e3, 912 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00e4, 916 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00e5, 920 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00e6, 924 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00e7, 928 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00e8, 932 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00e9, 936 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00ea, 940 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00eb, 944 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00ec, 948 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00ed, 952 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00ee, 956 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00ef, 960 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00f0, 964 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00f1, 968 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00f2, 972 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00f3, 976 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00f4, 980 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00f5, 984 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00f6, 988 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00f7, 992 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00f8, 996 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00f9, 1000 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00fa, 1004 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00fb, 1008 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00fc, 1012 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00fd, 1016 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00fe, 1020 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00ff, 1024 }, +}; + +static pr_vec4_t neg_f_init[] = { + { 1, 2, 3, 4 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, +}; + +static pr_vec4_t neg_f_expect[] = { + { 1, 2, 3, 4 }, + { 1, 2, 3, 4 }, + { -1, 2, 3, 4 }, + { 1, -2, 3, 4 }, + { -1, -2, 3, 4 }, + { 1, 2, -3, 4 }, + { -1, 2, -3, 4 }, + { 1, -2, -3, 4 }, + { -1, -2, -3, 4 }, + { 1, 2, 3, -4 }, + { -1, 2, 3, -4 }, + { 1, -2, 3, -4 }, + { -1, -2, 3, -4 }, + { 1, 2, -3, -4 }, + { -1, 2, -3, -4 }, + { 1, -2, -3, -4 }, + { -1, -2, -3, -4 }, +}; + +static dstatement_t neg_f_statements[] = { + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00e4, 4 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x01e4, 8 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x02e4, 12 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x03e4, 16 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x04e4, 20 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x05e4, 24 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x06e4, 28 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x07e4, 32 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x08e4, 36 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x09e4, 40 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0ae4, 44 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0be4, 48 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0ce4, 52 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0de4, 56 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0ee4, 60 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x0fe4, 64 }, +}; + +static pr_vec4_t zero_f_init[] = { + { 1, 2, 3, 4 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, +}; + +static pr_vec4_t zero_f_expect[] = { + { 1, 2, 3, 4 }, + { 1, 2, 3, 4 }, + { 0, 2, 3, 4 }, + { 1, 0, 3, 4 }, + { 0, 0, 3, 4 }, + { 1, 2, 0, 4 }, + { 0, 2, 0, 4 }, + { 1, 0, 0, 4 }, + { 0, 0, 0, 4 }, + { 1, 2, 3, 0 }, + { 0, 2, 3, 0 }, + { 1, 0, 3, 0 }, + { 0, 0, 3, 0 }, + { 1, 2, 0, 0 }, + { 0, 2, 0, 0 }, + { 1, 0, 0, 0 }, + { 0, 0, 0, 0 }, +}; + +static dstatement_t zero_f_statements[] = { + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x00e4, 4 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x10e4, 8 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x20e4, 12 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x30e4, 16 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x40e4, 20 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x50e4, 24 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x60e4, 28 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x70e4, 32 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x80e4, 36 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0x90e4, 40 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0xa0e4, 44 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0xb0e4, 48 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0xc0e4, 52 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0xd0e4, 56 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0xe0e4, 60 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 0, 0xf0e4, 64 }, +}; + +static pr_dvec4_t swizzle_d_init[] = { + { 1, 2, 3, 4 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, + { 8, 8, 8, 8 }, +}; +static pr_dvec4_t swizzle_d_expect[] = { + { 1, 2, 3, 4 }, + { 1, 1, 1, 1 }, + { 2, 1, 1, 1 }, + { 3, 1, 1, 1 }, + { 4, 1, 1, 1 }, + { 1, 2, 1, 1 }, + { 2, 2, 1, 1 }, + { 3, 2, 1, 1 }, + { 4, 2, 1, 1 }, + { 1, 3, 1, 1 }, + { 2, 3, 1, 1 }, + { 3, 3, 1, 1 }, + { 4, 3, 1, 1 }, + { 1, 4, 1, 1 }, + { 2, 4, 1, 1 }, + { 3, 4, 1, 1 }, + { 4, 4, 1, 1 }, + { 1, 1, 2, 1 }, + { 2, 1, 2, 1 }, + { 3, 1, 2, 1 }, + { 4, 1, 2, 1 }, + { 1, 2, 2, 1 }, + { 2, 2, 2, 1 }, + { 3, 2, 2, 1 }, + { 4, 2, 2, 1 }, + { 1, 3, 2, 1 }, + { 2, 3, 2, 1 }, + { 3, 3, 2, 1 }, + { 4, 3, 2, 1 }, + { 1, 4, 2, 1 }, + { 2, 4, 2, 1 }, + { 3, 4, 2, 1 }, + { 4, 4, 2, 1 }, + { 1, 1, 3, 1 }, + { 2, 1, 3, 1 }, + { 3, 1, 3, 1 }, + { 4, 1, 3, 1 }, + { 1, 2, 3, 1 }, + { 2, 2, 3, 1 }, + { 3, 2, 3, 1 }, + { 4, 2, 3, 1 }, + { 1, 3, 3, 1 }, + { 2, 3, 3, 1 }, + { 3, 3, 3, 1 }, + { 4, 3, 3, 1 }, + { 1, 4, 3, 1 }, + { 2, 4, 3, 1 }, + { 3, 4, 3, 1 }, + { 4, 4, 3, 1 }, + { 1, 1, 4, 1 }, + { 2, 1, 4, 1 }, + { 3, 1, 4, 1 }, + { 4, 1, 4, 1 }, + { 1, 2, 4, 1 }, + { 2, 2, 4, 1 }, + { 3, 2, 4, 1 }, + { 4, 2, 4, 1 }, + { 1, 3, 4, 1 }, + { 2, 3, 4, 1 }, + { 3, 3, 4, 1 }, + { 4, 3, 4, 1 }, + { 1, 4, 4, 1 }, + { 2, 4, 4, 1 }, + { 3, 4, 4, 1 }, + { 4, 4, 4, 1 }, + { 1, 1, 1, 2 }, + { 2, 1, 1, 2 }, + { 3, 1, 1, 2 }, + { 4, 1, 1, 2 }, + { 1, 2, 1, 2 }, + { 2, 2, 1, 2 }, + { 3, 2, 1, 2 }, + { 4, 2, 1, 2 }, + { 1, 3, 1, 2 }, + { 2, 3, 1, 2 }, + { 3, 3, 1, 2 }, + { 4, 3, 1, 2 }, + { 1, 4, 1, 2 }, + { 2, 4, 1, 2 }, + { 3, 4, 1, 2 }, + { 4, 4, 1, 2 }, + { 1, 1, 2, 2 }, + { 2, 1, 2, 2 }, + { 3, 1, 2, 2 }, + { 4, 1, 2, 2 }, + { 1, 2, 2, 2 }, + { 2, 2, 2, 2 }, + { 3, 2, 2, 2 }, + { 4, 2, 2, 2 }, + { 1, 3, 2, 2 }, + { 2, 3, 2, 2 }, + { 3, 3, 2, 2 }, + { 4, 3, 2, 2 }, + { 1, 4, 2, 2 }, + { 2, 4, 2, 2 }, + { 3, 4, 2, 2 }, + { 4, 4, 2, 2 }, + { 1, 1, 3, 2 }, + { 2, 1, 3, 2 }, + { 3, 1, 3, 2 }, + { 4, 1, 3, 2 }, + { 1, 2, 3, 2 }, + { 2, 2, 3, 2 }, + { 3, 2, 3, 2 }, + { 4, 2, 3, 2 }, + { 1, 3, 3, 2 }, + { 2, 3, 3, 2 }, + { 3, 3, 3, 2 }, + { 4, 3, 3, 2 }, + { 1, 4, 3, 2 }, + { 2, 4, 3, 2 }, + { 3, 4, 3, 2 }, + { 4, 4, 3, 2 }, + { 1, 1, 4, 2 }, + { 2, 1, 4, 2 }, + { 3, 1, 4, 2 }, + { 4, 1, 4, 2 }, + { 1, 2, 4, 2 }, + { 2, 2, 4, 2 }, + { 3, 2, 4, 2 }, + { 4, 2, 4, 2 }, + { 1, 3, 4, 2 }, + { 2, 3, 4, 2 }, + { 3, 3, 4, 2 }, + { 4, 3, 4, 2 }, + { 1, 4, 4, 2 }, + { 2, 4, 4, 2 }, + { 3, 4, 4, 2 }, + { 4, 4, 4, 2 }, + { 1, 1, 1, 3 }, + { 2, 1, 1, 3 }, + { 3, 1, 1, 3 }, + { 4, 1, 1, 3 }, + { 1, 2, 1, 3 }, + { 2, 2, 1, 3 }, + { 3, 2, 1, 3 }, + { 4, 2, 1, 3 }, + { 1, 3, 1, 3 }, + { 2, 3, 1, 3 }, + { 3, 3, 1, 3 }, + { 4, 3, 1, 3 }, + { 1, 4, 1, 3 }, + { 2, 4, 1, 3 }, + { 3, 4, 1, 3 }, + { 4, 4, 1, 3 }, + { 1, 1, 2, 3 }, + { 2, 1, 2, 3 }, + { 3, 1, 2, 3 }, + { 4, 1, 2, 3 }, + { 1, 2, 2, 3 }, + { 2, 2, 2, 3 }, + { 3, 2, 2, 3 }, + { 4, 2, 2, 3 }, + { 1, 3, 2, 3 }, + { 2, 3, 2, 3 }, + { 3, 3, 2, 3 }, + { 4, 3, 2, 3 }, + { 1, 4, 2, 3 }, + { 2, 4, 2, 3 }, + { 3, 4, 2, 3 }, + { 4, 4, 2, 3 }, + { 1, 1, 3, 3 }, + { 2, 1, 3, 3 }, + { 3, 1, 3, 3 }, + { 4, 1, 3, 3 }, + { 1, 2, 3, 3 }, + { 2, 2, 3, 3 }, + { 3, 2, 3, 3 }, + { 4, 2, 3, 3 }, + { 1, 3, 3, 3 }, + { 2, 3, 3, 3 }, + { 3, 3, 3, 3 }, + { 4, 3, 3, 3 }, + { 1, 4, 3, 3 }, + { 2, 4, 3, 3 }, + { 3, 4, 3, 3 }, + { 4, 4, 3, 3 }, + { 1, 1, 4, 3 }, + { 2, 1, 4, 3 }, + { 3, 1, 4, 3 }, + { 4, 1, 4, 3 }, + { 1, 2, 4, 3 }, + { 2, 2, 4, 3 }, + { 3, 2, 4, 3 }, + { 4, 2, 4, 3 }, + { 1, 3, 4, 3 }, + { 2, 3, 4, 3 }, + { 3, 3, 4, 3 }, + { 4, 3, 4, 3 }, + { 1, 4, 4, 3 }, + { 2, 4, 4, 3 }, + { 3, 4, 4, 3 }, + { 4, 4, 4, 3 }, + { 1, 1, 1, 4 }, + { 2, 1, 1, 4 }, + { 3, 1, 1, 4 }, + { 4, 1, 1, 4 }, + { 1, 2, 1, 4 }, + { 2, 2, 1, 4 }, + { 3, 2, 1, 4 }, + { 4, 2, 1, 4 }, + { 1, 3, 1, 4 }, + { 2, 3, 1, 4 }, + { 3, 3, 1, 4 }, + { 4, 3, 1, 4 }, + { 1, 4, 1, 4 }, + { 2, 4, 1, 4 }, + { 3, 4, 1, 4 }, + { 4, 4, 1, 4 }, + { 1, 1, 2, 4 }, + { 2, 1, 2, 4 }, + { 3, 1, 2, 4 }, + { 4, 1, 2, 4 }, + { 1, 2, 2, 4 }, + { 2, 2, 2, 4 }, + { 3, 2, 2, 4 }, + { 4, 2, 2, 4 }, + { 1, 3, 2, 4 }, + { 2, 3, 2, 4 }, + { 3, 3, 2, 4 }, + { 4, 3, 2, 4 }, + { 1, 4, 2, 4 }, + { 2, 4, 2, 4 }, + { 3, 4, 2, 4 }, + { 4, 4, 2, 4 }, + { 1, 1, 3, 4 }, + { 2, 1, 3, 4 }, + { 3, 1, 3, 4 }, + { 4, 1, 3, 4 }, + { 1, 2, 3, 4 }, + { 2, 2, 3, 4 }, + { 3, 2, 3, 4 }, + { 4, 2, 3, 4 }, + { 1, 3, 3, 4 }, + { 2, 3, 3, 4 }, + { 3, 3, 3, 4 }, + { 4, 3, 3, 4 }, + { 1, 4, 3, 4 }, + { 2, 4, 3, 4 }, + { 3, 4, 3, 4 }, + { 4, 4, 3, 4 }, + { 1, 1, 4, 4 }, + { 2, 1, 4, 4 }, + { 3, 1, 4, 4 }, + { 4, 1, 4, 4 }, + { 1, 2, 4, 4 }, + { 2, 2, 4, 4 }, + { 3, 2, 4, 4 }, + { 4, 2, 4, 4 }, + { 1, 3, 4, 4 }, + { 2, 3, 4, 4 }, + { 3, 3, 4, 4 }, + { 4, 3, 4, 4 }, + { 1, 4, 4, 4 }, + { 2, 4, 4, 4 }, + { 3, 4, 4, 4 }, + { 4, 4, 4, 4 }, +}; + +static dstatement_t swizzle_d_statements[] = { + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0000, 8 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0001, 16 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0002, 24 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0003, 32 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0004, 40 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0005, 48 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0006, 56 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0007, 64 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0008, 72 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0009, 80 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x000a, 88 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x000b, 96 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x000c, 104 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x000d, 112 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x000e, 120 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x000f, 128 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0010, 136 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0011, 144 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0012, 152 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0013, 160 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0014, 168 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0015, 176 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0016, 184 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0017, 192 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0018, 200 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0019, 208 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x001a, 216 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x001b, 224 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x001c, 232 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x001d, 240 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x001e, 248 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x001f, 256 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0020, 264 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0021, 272 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0022, 280 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0023, 288 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0024, 296 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0025, 304 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0026, 312 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0027, 320 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0028, 328 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0029, 336 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x002a, 344 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x002b, 352 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x002c, 360 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x002d, 368 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x002e, 376 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x002f, 384 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0030, 392 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0031, 400 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0032, 408 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0033, 416 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0034, 424 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0035, 432 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0036, 440 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0037, 448 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0038, 456 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0039, 464 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x003a, 472 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x003b, 480 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x003c, 488 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x003d, 496 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x003e, 504 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x003f, 512 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0040, 520 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0041, 528 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0042, 536 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0043, 544 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0044, 552 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0045, 560 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0046, 568 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0047, 576 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0048, 584 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0049, 592 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x004a, 600 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x004b, 608 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x004c, 616 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x004d, 624 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x004e, 632 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x004f, 640 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0050, 648 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0051, 656 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0052, 664 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0053, 672 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0054, 680 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0055, 688 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0056, 696 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0057, 704 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0058, 712 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0059, 720 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x005a, 728 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x005b, 736 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x005c, 744 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x005d, 752 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x005e, 760 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x005f, 768 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0060, 776 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0061, 784 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0062, 792 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0063, 800 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0064, 808 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0065, 816 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0066, 824 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0067, 832 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0068, 840 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0069, 848 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x006a, 856 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x006b, 864 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x006c, 872 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x006d, 880 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x006e, 888 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x006f, 896 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0070, 904 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0071, 912 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0072, 920 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0073, 928 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0074, 936 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0075, 944 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0076, 952 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0077, 960 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0078, 968 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0079, 976 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x007a, 984 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x007b, 992 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x007c, 1000 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x007d, 1008 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x007e, 1016 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x007f, 1024 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0080, 1032 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0081, 1040 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0082, 1048 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0083, 1056 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0084, 1064 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0085, 1072 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0086, 1080 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0087, 1088 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0088, 1096 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0089, 1104 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x008a, 1112 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x008b, 1120 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x008c, 1128 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x008d, 1136 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x008e, 1144 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x008f, 1152 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0090, 1160 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0091, 1168 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0092, 1176 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0093, 1184 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0094, 1192 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0095, 1200 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0096, 1208 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0097, 1216 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0098, 1224 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0099, 1232 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x009a, 1240 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x009b, 1248 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x009c, 1256 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x009d, 1264 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x009e, 1272 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x009f, 1280 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00a0, 1288 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00a1, 1296 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00a2, 1304 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00a3, 1312 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00a4, 1320 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00a5, 1328 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00a6, 1336 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00a7, 1344 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00a8, 1352 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00a9, 1360 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00aa, 1368 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00ab, 1376 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00ac, 1384 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00ad, 1392 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00ae, 1400 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00af, 1408 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00b0, 1416 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00b1, 1424 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00b2, 1432 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00b3, 1440 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00b4, 1448 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00b5, 1456 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00b6, 1464 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00b7, 1472 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00b8, 1480 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00b9, 1488 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00ba, 1496 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00bb, 1504 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00bc, 1512 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00bd, 1520 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00be, 1528 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00bf, 1536 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00c0, 1544 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00c1, 1552 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00c2, 1560 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00c3, 1568 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00c4, 1576 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00c5, 1584 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00c6, 1592 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00c7, 1600 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00c8, 1608 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00c9, 1616 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00ca, 1624 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00cb, 1632 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00cc, 1640 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00cd, 1648 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00ce, 1656 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00cf, 1664 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00d0, 1672 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00d1, 1680 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00d2, 1688 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00d3, 1696 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00d4, 1704 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00d5, 1712 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00d6, 1720 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00d7, 1728 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00d8, 1736 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00d9, 1744 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00da, 1752 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00db, 1760 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00dc, 1768 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00dd, 1776 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00de, 1784 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00df, 1792 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00e0, 1800 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00e1, 1808 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00e2, 1816 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00e3, 1824 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00e4, 1832 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00e5, 1840 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00e6, 1848 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00e7, 1856 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00e8, 1864 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00e9, 1872 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00ea, 1880 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00eb, 1888 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00ec, 1896 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00ed, 1904 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00ee, 1912 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00ef, 1920 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00f0, 1928 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00f1, 1936 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00f2, 1944 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00f3, 1952 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00f4, 1960 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00f5, 1968 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00f6, 1976 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00f7, 1984 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00f8, 1992 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00f9, 2000 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00fa, 2008 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00fb, 2016 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00fc, 2024 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00fd, 2032 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00fe, 2040 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00ff, 2048 }, +}; + +static pr_dvec4_t neg_d_init[] = { + { 1, 2, 3, 4 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, +}; + +static pr_dvec4_t neg_d_expect[] = { + { 1, 2, 3, 4 }, + { 1, 2, 3, 4 }, + { -1, 2, 3, 4 }, + { 1, -2, 3, 4 }, + { -1, -2, 3, 4 }, + { 1, 2, -3, 4 }, + { -1, 2, -3, 4 }, + { 1, -2, -3, 4 }, + { -1, -2, -3, 4 }, + { 1, 2, 3, -4 }, + { -1, 2, 3, -4 }, + { 1, -2, 3, -4 }, + { -1, -2, 3, -4 }, + { 1, 2, -3, -4 }, + { -1, 2, -3, -4 }, + { 1, -2, -3, -4 }, + { -1, -2, -3, -4 }, +}; + +static dstatement_t neg_d_statements[] = { + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00e4, 8 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x01e4, 16 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x02e4, 24 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x03e4, 32 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x04e4, 40 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x05e4, 48 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x06e4, 56 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x07e4, 64 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x08e4, 72 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x09e4, 80 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0ae4, 88 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0be4, 96 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0ce4, 104 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0de4, 112 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0ee4, 120 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x0fe4, 128 }, +}; + +static pr_dvec4_t zero_d_init[] = { + { 1, 2, 3, 4 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, + { 9, 9, 9, 9 }, +}; + +static pr_dvec4_t zero_d_expect[] = { + { 1, 2, 3, 4 }, + { 1, 2, 3, 4 }, + { 0, 2, 3, 4 }, + { 1, 0, 3, 4 }, + { 0, 0, 3, 4 }, + { 1, 2, 0, 4 }, + { 0, 2, 0, 4 }, + { 1, 0, 0, 4 }, + { 0, 0, 0, 4 }, + { 1, 2, 3, 0 }, + { 0, 2, 3, 0 }, + { 1, 0, 3, 0 }, + { 0, 0, 3, 0 }, + { 1, 2, 0, 0 }, + { 0, 2, 0, 0 }, + { 1, 0, 0, 0 }, + { 0, 0, 0, 0 }, +}; + +static dstatement_t zero_d_statements[] = { + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x00e4, 8 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x10e4, 16 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x20e4, 24 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x30e4, 32 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x40e4, 40 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x50e4, 48 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x60e4, 56 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x70e4, 64 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x80e4, 72 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0x90e4, 80 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0xa0e4, 88 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0xb0e4, 96 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0xc0e4, 104 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0xd0e4, 112 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0xe0e4, 120 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 0, 0xf0e4, 128 }, +}; + +test_t tests[] = { + { + .desc = "swizzle f", + .num_globals = num_globals(swizzle_f_init,swizzle_f_expect), + .num_statements = num_statements(swizzle_f_statements), + .statements = swizzle_f_statements, + .init_globals = (pr_int_t *) swizzle_f_init, + .expect_globals = (pr_int_t *) swizzle_f_expect, + }, + { + .desc = "neg f", + .num_globals = num_globals(neg_f_init,neg_f_expect), + .num_statements = num_statements(neg_f_statements), + .statements = neg_f_statements, + .init_globals = (pr_int_t *) neg_f_init, + .expect_globals = (pr_int_t *) neg_f_expect, + }, + { + .desc = "zero f", + .num_globals = num_globals(zero_f_init,zero_f_expect), + .num_statements = num_statements(zero_f_statements), + .statements = zero_f_statements, + .init_globals = (pr_int_t *) zero_f_init, + .expect_globals = (pr_int_t *) zero_f_expect, + }, + { + .desc = "swizzle d", + .num_globals = num_globals(swizzle_d_init,swizzle_d_expect), + .num_statements = num_statements(swizzle_d_statements), + .statements = swizzle_d_statements, + .init_globals = (pr_int_t *) swizzle_d_init, + .expect_globals = (pr_int_t *) swizzle_d_expect, + }, + { + .desc = "neg d", + .num_globals = num_globals(neg_d_init,neg_d_expect), + .num_statements = num_statements(neg_d_statements), + .statements = neg_d_statements, + .init_globals = (pr_int_t *) neg_d_init, + .expect_globals = (pr_int_t *) neg_d_expect, + }, + { + .desc = "zero d", + .num_globals = num_globals(zero_d_init,zero_d_expect), + .num_statements = num_statements(zero_d_statements), + .statements = zero_d_statements, + .init_globals = (pr_int_t *) zero_d_init, + .expect_globals = (pr_int_t *) zero_d_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-unsigned.c b/libs/gamecode/test/test-unsigned.c new file mode 100644 index 000000000..66f68e482 --- /dev/null +++ b/libs/gamecode/test/test-unsigned.c @@ -0,0 +1,776 @@ +#include "head.c" + +#include "QF/mathlib.h" + +static pr_uivec4_t uint_divop_init[] = { + { 5, -5, 5, -5}, + { 3, 3, -3, -3}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +static pr_uivec4_t uint_divop_expect[] = { + { 5, -5, 5, -5}, + { 3, 3, -3, -3}, + { 1, 0x55555553, 0, 0}, + { 2, 2, 5, -5}, +}; + +static dstatement_t uint_divop_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 32 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 32, -1, 32 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 32 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 32, 1 }, + { OP(1, 1, 1, OP_DIV_u_1), 0, 4, 8 }, + { OP(1, 1, 1, OP_REM_u_1), 0, 4, 12 }, + { OP(1, 1, 1, OP_JUMP_A), -6, 0, 0 }, +}; + +static dstatement_t uint_divop_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 32 }, // index +//loop: + { OP(0, 0, 0, OP_LEA_C), 32, -2, 32 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 32 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 32, 1 }, + { OP(1, 1, 1, OP_DIV_u_2), 0, 4, 8 }, + { OP(1, 1, 1, OP_REM_u_2), 0, 4, 12 }, + { OP(1, 1, 1, OP_JUMP_A), -6, 0, 0 }, +}; + +static dstatement_t uint_divop_3a_statements[] = { + { OP(1, 1, 1, OP_DIV_u_3), 0, 4, 8 }, + { OP(1, 1, 1, OP_DIV_u_1), 3, 7, 11 }, + { OP(1, 1, 1, OP_REM_u_3), 0, 4, 12 }, + { OP(1, 1, 1, OP_REM_u_1), 3, 7, 15 }, +}; + +static dstatement_t uint_divop_3b_statements[] = { + { OP(1, 1, 1, OP_DIV_u_1), 0, 4, 8 }, + { OP(1, 1, 1, OP_DIV_u_3), 1, 5, 9 }, + { OP(1, 1, 1, OP_REM_u_1), 0, 4, 12 }, + { OP(1, 1, 1, OP_REM_u_3), 1, 5, 13 }, +}; + +static dstatement_t uint_divop_4_statements[] = { + { OP(1, 1, 1, OP_DIV_u_4), 0, 4, 8 }, + { OP(1, 1, 1, OP_REM_u_4), 0, 4, 12 }, +}; + +static pr_uivec4_t uint_cmpop_init[] = { + { 5, -5, 5, -5}, + { 5, 5, -5, -5}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +static pr_uivec4_t uint_cmpop_expect[] = { + { 5, -5, 5, -5}, + { 5, 5, -5, -5}, + { 0, 0, 0, 0}, // no unsigned EQ (redundant) + { 0, 0, -1, 0}, + { 0, -1, 0, 0}, + { 0, 0, 0, 0}, // no unsigned NE (redundant) + { -1, -1, 0, -1}, + { -1, 0, -1, -1}, +}; + +static dstatement_t uint_cmpop_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 32 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 32, -1, 32 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 32 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 32, 1 }, + // no unsigned EQ (redundant) + { OP(1, 1, 1, OP_LT_u_1), 0, 4, 12 }, + { OP(1, 1, 1, OP_GT_u_1), 0, 4, 16 }, + // no unsigned NE (redundant) + { OP(1, 1, 1, OP_GE_u_1), 0, 4, 24 }, + { OP(1, 1, 1, OP_LE_u_1), 0, 4, 28 }, + { OP(1, 1, 1, OP_JUMP_A), -8, 0, 0 }, +}; + +static dstatement_t uint_cmpop_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 32 }, // index +//loop: + { OP(0, 0, 0, OP_LEA_C), 32, -2, 32 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 32 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 32, 1 }, + // no unsigned EQ (redundant) + { OP(1, 1, 1, OP_LT_u_2), 0, 4, 12 }, + { OP(1, 1, 1, OP_GT_u_2), 0, 4, 16 }, + // no unsigned NE (redundant) + { OP(1, 1, 1, OP_GE_u_2), 0, 4, 24 }, + { OP(1, 1, 1, OP_LE_u_2), 0, 4, 28 }, + { OP(1, 1, 1, OP_JUMP_A), -8, 0, 0 }, +}; + +static dstatement_t uint_cmpop_3a_statements[] = { + // no unsigned EQ (redundant) + // no unsigned EQ (redundant) + { OP(1, 1, 1, OP_LT_u_3), 0, 4, 12 }, + { OP(1, 1, 1, OP_LT_u_1), 3, 7, 15 }, + { OP(1, 1, 1, OP_GT_u_3), 0, 4, 16 }, + { OP(1, 1, 1, OP_GT_u_1), 3, 7, 19 }, + // no unsigned NE (redundant) + // no unsigned NE (redundant) + { OP(1, 1, 1, OP_GE_u_3), 0, 4, 24 }, + { OP(1, 1, 1, OP_GE_u_1), 3, 7, 27 }, + { OP(1, 1, 1, OP_LE_u_3), 0, 4, 28 }, + { OP(1, 1, 1, OP_LE_u_1), 3, 7, 31 }, +}; + +static dstatement_t uint_cmpop_3b_statements[] = { + // no unsigned EQ (redundant) + // no unsigned EQ (redundant) + { OP(1, 1, 1, OP_LT_u_1), 0, 4, 12 }, + { OP(1, 1, 1, OP_LT_u_3), 1, 5, 13 }, + { OP(1, 1, 1, OP_GT_u_1), 0, 4, 16 }, + { OP(1, 1, 1, OP_GT_u_3), 1, 5, 17 }, + // no unsigned NE (redundant) + // no unsigned NE (redundant) + { OP(1, 1, 1, OP_GE_u_1), 0, 4, 24 }, + { OP(1, 1, 1, OP_GE_u_3), 1, 5, 25 }, + { OP(1, 1, 1, OP_LE_u_1), 0, 4, 28 }, + { OP(1, 1, 1, OP_LE_u_3), 1, 5, 29 }, +}; + +static dstatement_t uint_cmpop_4_statements[] = { + // no unsigned EQ (redundant) + { OP(1, 1, 1, OP_LT_u_4), 0, 4, 12 }, + { OP(1, 1, 1, OP_GT_u_4), 0, 4, 16 }, + // no unsigned NE (redundant) + { OP(1, 1, 1, OP_GE_u_4), 0, 4, 24 }, + { OP(1, 1, 1, OP_LE_u_4), 0, 4, 28 }, +}; + +static pr_ulvec4_t ulong_divop_init[] = { + { 5, -5, 5, -5}, + { 3, 3, -3, -3}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +static pr_ulvec4_t ulong_divop_expect[] = { + { 5, -5, 5, -5}, + { 3, 3, -3, -3}, + { 1, UINT64_C(0x5555555555555553), 0, 0}, + { 2, 2, 5, -5}, +}; + +static dstatement_t ulong_divop_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 8, 0, 64 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 64, -2, 64 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 64 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 64, 1 }, + { OP(1, 1, 1, OP_DIV_U_1), 0, 8, 16 }, + { OP(1, 1, 1, OP_REM_U_1), 0, 8, 24 }, + { OP(1, 1, 1, OP_JUMP_A), -6, 0, 0 }, +}; + +static dstatement_t ulong_divop_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 8, 0, 64 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 64, -4, 64 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 64 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 64, 1 }, + { OP(1, 1, 1, OP_DIV_U_2), 0, 8, 16 }, + { OP(1, 1, 1, OP_REM_U_2), 0, 8, 24 }, + { OP(1, 1, 1, OP_JUMP_A), -6, 0, 0 }, +}; + +static dstatement_t ulong_divop_3a_statements[] = { + { OP(1, 1, 1, OP_DIV_U_3), 0, 8, 16 }, + { OP(1, 1, 1, OP_DIV_U_1), 6, 14, 22 }, + { OP(1, 1, 1, OP_REM_U_3), 0, 8, 24 }, + { OP(1, 1, 1, OP_REM_U_1), 6, 14, 30 }, +}; + +static dstatement_t ulong_divop_3b_statements[] = { + { OP(1, 1, 1, OP_DIV_U_1), 0, 8, 16 }, + { OP(1, 1, 1, OP_DIV_U_3), 2, 10, 18 }, + { OP(1, 1, 1, OP_REM_U_1), 0, 8, 24 }, + { OP(1, 1, 1, OP_REM_U_3), 2, 10, 26 }, +}; + +static dstatement_t ulong_divop_4_statements[] = { + { OP(1, 1, 1, OP_DIV_U_4), 0, 8, 16 }, + { OP(1, 1, 1, OP_REM_U_4), 0, 8, 24 }, +}; + +static pr_ulvec4_t ulong_cmpop_init[] = { + { 5, -5, 5, -5}, + { 5, 5, -5, -5}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0}, +}; + +static pr_ulvec4_t ulong_cmpop_expect[] = { + { 5, -5, 5, -5}, + { 5, 5, -5, -5}, + { 0, 0, 0, 0}, // no unsigned EQ (redundant) + { 0, 0, -1, 0}, + { 0, -1, 0, 0}, + { 0, 0, 0, 0}, // no unsigned NE (redundant) + { -1, -1, 0, -1}, + { -1, 0, -1, -1}, +}; + +static dstatement_t ulong_cmpop_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 8, 0, 64 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 64, -2, 64 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 64 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 64, 1 }, + // no unsigned EQ (redundant) + { OP(1, 1, 1, OP_LT_U_1), 0, 8, 24 }, + { OP(1, 1, 1, OP_GT_U_1), 0, 8, 32 }, + // no unsigned NE (redundant) + { OP(1, 1, 1, OP_GE_U_1), 0, 8, 48 }, + { OP(1, 1, 1, OP_LE_U_1), 0, 8, 56 }, + { OP(1, 1, 1, OP_JUMP_A), -8, 0, 0 }, +}; + +static dstatement_t ulong_cmpop_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 8, 0, 64 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 64, -4, 64 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 64 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 64, 1 }, + // no unsigned EQ (redundant) + { OP(1, 1, 1, OP_LT_U_2), 0, 8, 24 }, + { OP(1, 1, 1, OP_GT_U_2), 0, 8, 32 }, + // no unsigned NE (redundant) + { OP(1, 1, 1, OP_GE_U_2), 0, 8, 48 }, + { OP(1, 1, 1, OP_LE_U_2), 0, 8, 56 }, + { OP(1, 1, 1, OP_JUMP_A), -8, 0, 0 }, +}; + +static dstatement_t ulong_cmpop_3a_statements[] = { + // no unsigned EQ (redundant) + // no unsigned EQ (redundant) + { OP(1, 1, 1, OP_LT_U_3), 0, 8, 24 }, + { OP(1, 1, 1, OP_LT_U_1), 6, 14, 30 }, + { OP(1, 1, 1, OP_GT_U_3), 0, 8, 32 }, + { OP(1, 1, 1, OP_GT_U_1), 6, 14, 38 }, + // no unsigned NE (redundant) + // no unsigned NE (redundant) + { OP(1, 1, 1, OP_GE_U_3), 0, 8, 48 }, + { OP(1, 1, 1, OP_GE_U_1), 6, 14, 54 }, + { OP(1, 1, 1, OP_LE_U_3), 0, 8, 56 }, + { OP(1, 1, 1, OP_LE_U_1), 6, 14, 62 }, +}; + +static dstatement_t ulong_cmpop_3b_statements[] = { + // no unsigned EQ (redundant) + // no unsigned EQ (redundant) + { OP(1, 1, 1, OP_LT_U_1), 0, 8, 24 }, + { OP(1, 1, 1, OP_LT_U_3), 2, 10, 26 }, + { OP(1, 1, 1, OP_GT_U_1), 0, 8, 32 }, + { OP(1, 1, 1, OP_GT_U_3), 2, 10, 34 }, + // no unsigned NE (redundant) + // no unsigned NE (redundant) + { OP(1, 1, 1, OP_GE_U_1), 0, 8, 48 }, + { OP(1, 1, 1, OP_GE_U_3), 2, 10, 50 }, + { OP(1, 1, 1, OP_LE_U_1), 0, 8, 56 }, + { OP(1, 1, 1, OP_LE_U_3), 2, 10, 58 }, +}; + +static dstatement_t ulong_cmpop_4_statements[] = { + // no unsigned EQ (redundant) + { OP(1, 1, 1, OP_LT_U_4), 0, 8, 24 }, + { OP(1, 1, 1, OP_GT_U_4), 0, 8, 32 }, + // no unsigned NE (redundant) + { OP(1, 1, 1, OP_GE_U_4), 0, 8, 48 }, + { OP(1, 1, 1, OP_LE_U_4), 0, 8, 56 }, +}; + +static pr_uivec4_t uint_shiftop_init[] = { + { 0x12345678, 0x9abcdef0, 0x80000001, 0xaaaa5555 }, + { 12, 16, 9, 1 }, + { 20, 16, 23, 31 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, +}; + +static pr_uivec4_t uint_shiftop_expect[] = { + { 0x12345678, 0x9abcdef0, 0x80000001, 0xaaaa5555 }, + { 12, 16, 9, 1 },//a + { 20, 16, 23, 31 },//b + { 0x45678000, 0xdef00000, 0x00000200, 0x5554aaaa },//shl a + { 0x67800000, 0xdef00000, 0x00800000, 0x80000000 },//shl b + { 0x00012345, 0x00009abc, 0x00400000, 0x55552aaa },//shr a + { 0x00000123, 0x00009abc, 0x00000100, 0x00000001 },//shr b + { 0x00012345, 0xffff9abc, 0xffc00000, 0xd5552aaa },//asr a + { 0x00000123, 0xffff9abc, 0xffffff00, 0xffffffff },//asr b +}; + +static dstatement_t uint_shiftop_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 36 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 36, -1, 36 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 36 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 36, 1 }, + { OP(1, 1, 1, OP_SHL_I_1), 0, 4, 12 }, + { OP(1, 1, 1, OP_SHL_I_1), 0, 8, 16 }, + { OP(1, 1, 1, OP_SHR_u_1), 0, 4, 20 }, + { OP(1, 1, 1, OP_SHR_u_1), 0, 8, 24 }, + { OP(1, 1, 1, OP_ASR_I_1), 0, 4, 28 }, + { OP(1, 1, 1, OP_ASR_I_1), 0, 8, 32 }, + { OP(1, 1, 1, OP_JUMP_A), -10, 0, 0 }, +}; + +static dstatement_t uint_shiftop_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 4, 0, 36 }, // index +//loop: + { OP(0, 0, 0, OP_LEA_C), 36, -2, 36 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 36 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 36, 1 }, + { OP(1, 1, 1, OP_SHL_I_2), 0, 4, 12 }, + { OP(1, 1, 1, OP_SHL_I_2), 0, 8, 16 }, + { OP(1, 1, 1, OP_SHR_u_2), 0, 4, 20 }, + { OP(1, 1, 1, OP_SHR_u_2), 0, 8, 24 }, + { OP(1, 1, 1, OP_ASR_I_2), 0, 4, 28 }, + { OP(1, 1, 1, OP_ASR_I_2), 0, 8, 32 }, + { OP(1, 1, 1, OP_JUMP_A), -10, 0, 0 }, +}; + +static dstatement_t uint_shiftop_3a_statements[] = { + { OP(1, 1, 1, OP_SHL_I_3), 0, 4, 12 }, + { OP(1, 1, 1, OP_SHL_I_1), 3, 7, 15 }, + { OP(1, 1, 1, OP_SHL_I_3), 0, 8, 16 }, + { OP(1, 1, 1, OP_SHL_I_1), 3, 11, 19 }, + { OP(1, 1, 1, OP_SHR_u_3), 0, 4, 20 }, + { OP(1, 1, 1, OP_SHR_u_1), 3, 7, 23 }, + { OP(1, 1, 1, OP_SHR_u_3), 0, 8, 24 }, + { OP(1, 1, 1, OP_SHR_u_1), 3, 11, 27 }, + { OP(1, 1, 1, OP_ASR_I_3), 0, 4, 28 }, + { OP(1, 1, 1, OP_ASR_I_1), 3, 7, 31 }, + { OP(1, 1, 1, OP_ASR_I_3), 0, 8, 32 }, + { OP(1, 1, 1, OP_ASR_I_1), 3, 11, 35 }, +}; + +static dstatement_t uint_shiftop_3b_statements[] = { + { OP(1, 1, 1, OP_SHL_I_1), 0, 4, 12 }, + { OP(1, 1, 1, OP_SHL_I_3), 1, 5, 13 }, + { OP(1, 1, 1, OP_SHL_I_1), 0, 8, 16 }, + { OP(1, 1, 1, OP_SHL_I_3), 1, 9, 17 }, + { OP(1, 1, 1, OP_SHR_u_1), 0, 4, 20 }, + { OP(1, 1, 1, OP_SHR_u_3), 1, 5, 21 }, + { OP(1, 1, 1, OP_SHR_u_1), 0, 8, 24 }, + { OP(1, 1, 1, OP_SHR_u_3), 1, 9, 25 }, + { OP(1, 1, 1, OP_ASR_I_1), 0, 4, 28 }, + { OP(1, 1, 1, OP_ASR_I_3), 1, 5, 29 }, + { OP(1, 1, 1, OP_ASR_I_1), 0, 8, 32 }, + { OP(1, 1, 1, OP_ASR_I_3), 1, 9, 33 }, +}; + +static dstatement_t uint_shiftop_4_statements[] = { + { OP(1, 1, 1, OP_SHL_I_4), 0, 4, 12 }, + { OP(1, 1, 1, OP_SHL_I_4), 0, 8, 16 }, + { OP(1, 1, 1, OP_SHR_u_4), 0, 4, 20 }, + { OP(1, 1, 1, OP_SHR_u_4), 0, 8, 24 }, + { OP(1, 1, 1, OP_ASR_I_4), 0, 4, 28 }, + { OP(1, 1, 1, OP_ASR_I_4), 0, 8, 32 }, +}; + +static pr_ulvec4_t ulong_shiftop_init[] = { + { UINT64_C(0x123456789abcdef0), UINT64_C(0x9abcdef012345678), + UINT64_C(0x8000000180000001), UINT64_C(0xaaaa5555aaaa5555) }, + { 12, 16, 9, 1 }, + { 52, 48, 55, 63 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, +}; + +static pr_ulvec4_t ulong_shiftop_expect[] = { + { UINT64_C(0x123456789abcdef0), UINT64_C(0x9abcdef012345678), + UINT64_C(0x8000000180000001), UINT64_C(0xaaaa5555aaaa5555) }, + { 12, 16, 9, 1 },//a + { 52, 48, 55, 63 },//b + { UINT64_C(0x456789abcdef0000), UINT64_C(0xdef0123456780000), + UINT64_C(0x0000030000000200), UINT64_C(0x5554aaab5554aaaa) },//shl a + { UINT64_C(0xef00000000000000), UINT64_C(0x5678000000000000), + UINT64_C(0x0080000000000000), UINT64_C(0x8000000000000000) },//shl b + { UINT64_C(0x000123456789abcd), UINT64_C(0x00009abcdef01234), + UINT64_C(0x0040000000c00000), UINT64_C(0x55552aaad5552aaa) },//shr a + { UINT64_C(0x0000000000000123), UINT64_C(0x0000000000009abc), + UINT64_C(0x0000000000000100), UINT64_C(0x0000000000000001) },//shr b + { UINT64_C(0x000123456789abcd), UINT64_C(0xffff9abcdef01234), + UINT64_C(0xffc0000000c00000), UINT64_C(0xd5552aaad5552aaa) },//asr a + { UINT64_C(0x0000000000000123), UINT64_C(0xffffffffffff9abc), + UINT64_C(0xffffffffffffff00), UINT64_C(0xffffffffffffffff) },//asr b +}; + +static dstatement_t ulong_shiftop_1_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 8, 0, 72 }, // init index +//loop: + { OP(0, 0, 0, OP_LEA_C), 72, -2, 72 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 72 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 72, 1 }, + { OP(1, 1, 1, OP_SHL_L_1), 0, 8, 24 }, + { OP(1, 1, 1, OP_SHL_L_1), 0, 16, 32 }, + { OP(1, 1, 1, OP_SHR_U_1), 0, 8, 40 }, + { OP(1, 1, 1, OP_SHR_U_1), 0, 16, 48 }, + { OP(1, 1, 1, OP_ASR_L_1), 0, 8, 56 }, + { OP(1, 1, 1, OP_ASR_L_1), 0, 16, 64 }, + { OP(1, 1, 1, OP_JUMP_A), -10, 0, 0 }, +}; + +static dstatement_t ulong_shiftop_2_statements[] = { + { OP(0, 0, 0, OP_LEA_A), 8, 0, 72 }, // index +//loop: + { OP(0, 0, 0, OP_LEA_C), 72, -4, 72 }, // dec index + { OP(0, 0, 0, OP_IFAE), 2, 0, 72 }, + { OP(0, 0, 0, OP_BREAK), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 4, 72, 1 }, + { OP(1, 1, 1, OP_SHL_L_2), 0, 8, 24 }, + { OP(1, 1, 1, OP_SHL_L_2), 0, 16, 32 }, + { OP(1, 1, 1, OP_SHR_U_2), 0, 8, 40 }, + { OP(1, 1, 1, OP_SHR_U_2), 0, 16, 48 }, + { OP(1, 1, 1, OP_ASR_L_2), 0, 8, 56 }, + { OP(1, 1, 1, OP_ASR_L_2), 0, 16, 64 }, + { OP(1, 1, 1, OP_JUMP_A), -10, 0, 0 }, +}; + +static dstatement_t ulong_shiftop_3a_statements[] = { + { OP(1, 1, 1, OP_SHL_L_3), 0, 8, 24 }, + { OP(1, 1, 1, OP_SHL_L_1), 6, 14, 30 }, + { OP(1, 1, 1, OP_SHL_L_3), 0, 16, 32 }, + { OP(1, 1, 1, OP_SHL_L_1), 6, 22, 38 }, + { OP(1, 1, 1, OP_SHR_U_3), 0, 8, 40 }, + { OP(1, 1, 1, OP_SHR_U_1), 6, 14, 46 }, + { OP(1, 1, 1, OP_SHR_U_3), 0, 16, 48 }, + { OP(1, 1, 1, OP_SHR_U_1), 6, 22, 54 }, + { OP(1, 1, 1, OP_ASR_L_3), 0, 8, 56 }, + { OP(1, 1, 1, OP_ASR_L_1), 6, 14, 62 }, + { OP(1, 1, 1, OP_ASR_L_3), 0, 16, 64 }, + { OP(1, 1, 1, OP_ASR_L_1), 6, 22, 70 }, +}; + +static dstatement_t ulong_shiftop_3b_statements[] = { + { OP(1, 1, 1, OP_SHL_L_1), 0, 8, 24 }, + { OP(1, 1, 1, OP_SHL_L_3), 2, 10, 26 }, + { OP(1, 1, 1, OP_SHL_L_1), 0, 16, 32 }, + { OP(1, 1, 1, OP_SHL_L_3), 2, 18, 34 }, + { OP(1, 1, 1, OP_SHR_U_1), 0, 8, 40 }, + { OP(1, 1, 1, OP_SHR_U_3), 2, 10, 42 }, + { OP(1, 1, 1, OP_SHR_U_1), 0, 16, 48 }, + { OP(1, 1, 1, OP_SHR_U_3), 2, 18, 50 }, + { OP(1, 1, 1, OP_ASR_L_1), 0, 8, 56 }, + { OP(1, 1, 1, OP_ASR_L_3), 2, 10, 58 }, + { OP(1, 1, 1, OP_ASR_L_1), 0, 16, 64 }, + { OP(1, 1, 1, OP_ASR_L_3), 2, 18, 66 }, +}; + +static dstatement_t ulong_shiftop_4_statements[] = { + { OP(1, 1, 1, OP_SHL_L_4), 0, 8, 24 }, + { OP(1, 1, 1, OP_SHL_L_4), 0, 16, 32 }, + { OP(1, 1, 1, OP_SHR_U_4), 0, 8, 40 }, + { OP(1, 1, 1, OP_SHR_U_4), 0, 16, 48 }, + { OP(1, 1, 1, OP_ASR_L_4), 0, 8, 56 }, + { OP(1, 1, 1, OP_ASR_L_4), 0, 16, 64 }, +}; + +test_t tests[] = { + { + .desc = "uint divop 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(uint_divop_init,uint_divop_expect), + .num_statements = num_statements (uint_divop_1_statements), + .statements = uint_divop_1_statements, + .init_globals = (pr_int_t *) uint_divop_init, + .expect_globals = (pr_int_t *) uint_divop_expect, + }, + { + .desc = "uint divop 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(uint_divop_init,uint_divop_expect), + .num_statements = num_statements (uint_divop_2_statements), + .statements = uint_divop_2_statements, + .init_globals = (pr_int_t *) uint_divop_init, + .expect_globals = (pr_int_t *) uint_divop_expect, + }, + { + .desc = "uint divop 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(uint_divop_init,uint_divop_expect), + .num_statements = num_statements (uint_divop_3a_statements), + .statements = uint_divop_3a_statements, + .init_globals = (pr_int_t *) uint_divop_init, + .expect_globals = (pr_int_t *) uint_divop_expect, + }, + { + .desc = "uint divop 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(uint_divop_init,uint_divop_expect), + .num_statements = num_statements (uint_divop_3b_statements), + .statements = uint_divop_3b_statements, + .init_globals = (pr_int_t *) uint_divop_init, + .expect_globals = (pr_int_t *) uint_divop_expect, + }, + { + .desc = "uint divop 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(uint_divop_init,uint_divop_expect), + .num_statements = num_statements (uint_divop_4_statements), + .statements = uint_divop_4_statements, + .init_globals = (pr_int_t *) uint_divop_init, + .expect_globals = (pr_int_t *) uint_divop_expect, + }, + { + .desc = "uint cmpop 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(uint_cmpop_init,uint_cmpop_expect), + .num_statements = num_statements (uint_cmpop_1_statements), + .statements = uint_cmpop_1_statements, + .init_globals = (pr_int_t *) uint_cmpop_init, + .expect_globals = (pr_int_t *) uint_cmpop_expect, + }, + { + .desc = "uint cmpop 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(uint_cmpop_init,uint_cmpop_expect), + .num_statements = num_statements (uint_cmpop_2_statements), + .statements = uint_cmpop_2_statements, + .init_globals = (pr_int_t *) uint_cmpop_init, + .expect_globals = (pr_int_t *) uint_cmpop_expect, + }, + { + .desc = "uint cmpop 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(uint_cmpop_init,uint_cmpop_expect), + .num_statements = num_statements (uint_cmpop_3a_statements), + .statements = uint_cmpop_3a_statements, + .init_globals = (pr_int_t *) uint_cmpop_init, + .expect_globals = (pr_int_t *) uint_cmpop_expect, + }, + { + .desc = "uint cmpop 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(uint_cmpop_init,uint_cmpop_expect), + .num_statements = num_statements (uint_cmpop_3b_statements), + .statements = uint_cmpop_3b_statements, + .init_globals = (pr_int_t *) uint_cmpop_init, + .expect_globals = (pr_int_t *) uint_cmpop_expect, + }, + { + .desc = "uint cmpop 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(uint_cmpop_init,uint_cmpop_expect), + .num_statements = num_statements (uint_cmpop_4_statements), + .statements = uint_cmpop_4_statements, + .init_globals = (pr_int_t *) uint_cmpop_init, + .expect_globals = (pr_int_t *) uint_cmpop_expect, + }, + { + .desc = "ulong divop 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(ulong_divop_init,ulong_divop_expect), + .num_statements = num_statements (ulong_divop_1_statements), + .statements = ulong_divop_1_statements, + .init_globals = (pr_int_t *) ulong_divop_init, + .expect_globals = (pr_int_t *) ulong_divop_expect, + }, + { + .desc = "ulong divop 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(ulong_divop_init,ulong_divop_expect), + .num_statements = num_statements (ulong_divop_2_statements), + .statements = ulong_divop_2_statements, + .init_globals = (pr_int_t *) ulong_divop_init, + .expect_globals = (pr_int_t *) ulong_divop_expect, + }, + { + .desc = "ulong divop 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(ulong_divop_init,ulong_divop_expect), + .num_statements = num_statements (ulong_divop_3a_statements), + .statements = ulong_divop_3a_statements, + .init_globals = (pr_int_t *) ulong_divop_init, + .expect_globals = (pr_int_t *) ulong_divop_expect, + }, + { + .desc = "ulong divop 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(ulong_divop_init,ulong_divop_expect), + .num_statements = num_statements (ulong_divop_3b_statements), + .statements = ulong_divop_3b_statements, + .init_globals = (pr_int_t *) ulong_divop_init, + .expect_globals = (pr_int_t *) ulong_divop_expect, + }, + { + .desc = "ulong divop 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(ulong_divop_init,ulong_divop_expect), + .num_statements = num_statements (ulong_divop_4_statements), + .statements = ulong_divop_4_statements, + .init_globals = (pr_int_t *) ulong_divop_init, + .expect_globals = (pr_int_t *) ulong_divop_expect, + }, + { + .desc = "ulong cmpop 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(ulong_cmpop_init,ulong_cmpop_expect), + .num_statements = num_statements (ulong_cmpop_1_statements), + .statements = ulong_cmpop_1_statements, + .init_globals = (pr_int_t *) ulong_cmpop_init, + .expect_globals = (pr_int_t *) ulong_cmpop_expect, + }, + { + .desc = "ulong cmpop 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(ulong_cmpop_init,ulong_cmpop_expect), + .num_statements = num_statements (ulong_cmpop_2_statements), + .statements = ulong_cmpop_2_statements, + .init_globals = (pr_int_t *) ulong_cmpop_init, + .expect_globals = (pr_int_t *) ulong_cmpop_expect, + }, + { + .desc = "ulong cmpop 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(ulong_cmpop_init,ulong_cmpop_expect), + .num_statements = num_statements (ulong_cmpop_3a_statements), + .statements = ulong_cmpop_3a_statements, + .init_globals = (pr_int_t *) ulong_cmpop_init, + .expect_globals = (pr_int_t *) ulong_cmpop_expect, + }, + { + .desc = "ulong cmpop 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(ulong_cmpop_init,ulong_cmpop_expect), + .num_statements = num_statements (ulong_cmpop_3b_statements), + .statements = ulong_cmpop_3b_statements, + .init_globals = (pr_int_t *) ulong_cmpop_init, + .expect_globals = (pr_int_t *) ulong_cmpop_expect, + }, + { + .desc = "ulong cmpop 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(ulong_cmpop_init,ulong_cmpop_expect), + .num_statements = num_statements (ulong_cmpop_4_statements), + .statements = ulong_cmpop_4_statements, + .init_globals = (pr_int_t *) ulong_cmpop_init, + .expect_globals = (pr_int_t *) ulong_cmpop_expect, + }, + { + .desc = "uint shiftop 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(uint_shiftop_init,uint_shiftop_expect), + .num_statements = num_statements (uint_shiftop_1_statements), + .statements = uint_shiftop_1_statements, + .init_globals = (pr_int_t *) uint_shiftop_init, + .expect_globals = (pr_int_t *) uint_shiftop_expect, + }, + { + .desc = "uint shiftop 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(uint_shiftop_init,uint_shiftop_expect), + .num_statements = num_statements (uint_shiftop_2_statements), + .statements = uint_shiftop_2_statements, + .init_globals = (pr_int_t *) uint_shiftop_init, + .expect_globals = (pr_int_t *) uint_shiftop_expect, + }, + { + .desc = "uint shiftop 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(uint_shiftop_init,uint_shiftop_expect), + .num_statements = num_statements (uint_shiftop_3a_statements), + .statements = uint_shiftop_3a_statements, + .init_globals = (pr_int_t *) uint_shiftop_init, + .expect_globals = (pr_int_t *) uint_shiftop_expect, + }, + { + .desc = "uint shiftop 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(uint_shiftop_init,uint_shiftop_expect), + .num_statements = num_statements (uint_shiftop_3b_statements), + .statements = uint_shiftop_3b_statements, + .init_globals = (pr_int_t *) uint_shiftop_init, + .expect_globals = (pr_int_t *) uint_shiftop_expect, + }, + { + .desc = "uint shiftop 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(uint_shiftop_init,uint_shiftop_expect), + .num_statements = num_statements (uint_shiftop_4_statements), + .statements = uint_shiftop_4_statements, + .init_globals = (pr_int_t *) uint_shiftop_init, + .expect_globals = (pr_int_t *) uint_shiftop_expect, + }, + { + .desc = "ulong shiftop 1", + .extra_globals = 4 * 1, + .num_globals = num_globals(ulong_shiftop_init,ulong_shiftop_expect), + .num_statements = num_statements (ulong_shiftop_1_statements), + .statements = ulong_shiftop_1_statements, + .init_globals = (pr_int_t *) ulong_shiftop_init, + .expect_globals = (pr_int_t *) ulong_shiftop_expect, + }, + { + .desc = "ulong shiftop 2", + .extra_globals = 4 * 1, + .num_globals = num_globals(ulong_shiftop_init,ulong_shiftop_expect), + .num_statements = num_statements (ulong_shiftop_2_statements), + .statements = ulong_shiftop_2_statements, + .init_globals = (pr_int_t *) ulong_shiftop_init, + .expect_globals = (pr_int_t *) ulong_shiftop_expect, + }, + { + .desc = "ulong shiftop 3a", + .extra_globals = 4 * 1, + .num_globals = num_globals(ulong_shiftop_init,ulong_shiftop_expect), + .num_statements = num_statements (ulong_shiftop_3a_statements), + .statements = ulong_shiftop_3a_statements, + .init_globals = (pr_int_t *) ulong_shiftop_init, + .expect_globals = (pr_int_t *) ulong_shiftop_expect, + }, + { + .desc = "ulong shiftop 3b", + .extra_globals = 4 * 1, + .num_globals = num_globals(ulong_shiftop_init,ulong_shiftop_expect), + .num_statements = num_statements (ulong_shiftop_3b_statements), + .statements = ulong_shiftop_3b_statements, + .init_globals = (pr_int_t *) ulong_shiftop_init, + .expect_globals = (pr_int_t *) ulong_shiftop_expect, + }, + { + .desc = "ulong shiftop 4", + .extra_globals = 4 * 1, + .num_globals = num_globals(ulong_shiftop_init,ulong_shiftop_expect), + .num_statements = num_statements (ulong_shiftop_4_statements), + .statements = ulong_shiftop_4_statements, + .init_globals = (pr_int_t *) ulong_shiftop_init, + .expect_globals = (pr_int_t *) ulong_shiftop_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-vector.c b/libs/gamecode/test/test-vector.c new file mode 100644 index 000000000..f80253886 --- /dev/null +++ b/libs/gamecode/test/test-vector.c @@ -0,0 +1,170 @@ +#include "head.c" + +static pr_vec4_t float_globals_init[] = { + {3, 4, 5, 12}, + {0, 0, 0, 0}, + {1, 2, 3, 8}, + {4, 5, 6, 8}, + + {0, 0, 0, 7}, + {0, 0, 0, 7}, + {1, 2, 3, 4}, + {5, 6, 7, 8}, + + {2, 3, 4, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 7}, + + {0, 0, 0, 7}, + {0, 0, 0, 7}, + {0, 0, 0, 7}, + {0, 0, 0, 0}, + + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, +}; + +static pr_vec4_t float_globals_expect[] = { + {3, 4, 5, 12}, + {63, 63, -33, 56}, + {1, 2, 3, 8}, + {4, 5, 6, 8}, + + {32, 32, 32, 7}, + {-3, 6, -3, 7}, + {1, 2, 3, 4}, + {5, 6, 7, 8}, + + {2, 3, 4, 0}, + {70, 70, 70, 70}, + {24, 48, 48, -6}, + {36, 102, 120, 7}, + + {52, 70, 136, 7}, + {36, 102, 120, 0}, + {52, 70, 136, 0}, + {-1, -2, -3, 4}, + + {-1, -2, -3, 4}, + {36, 102, 120, 0}, + {52, 70, 136, 0}, +}; + +static dstatement_t float_vector_statements[] = { + { OP(0, 0, 0, OP_CDOT_F), 0, 2, 4 }, + { OP(0, 0, 0, OP_CMUL_F), 0, 2, 6 }, + { OP(0, 0, 0, OP_VDOT_F), 8, 12, 16 }, + { OP(0, 0, 0, OP_CROSS_F), 8, 12, 20 }, + { OP(0, 0, 0, OP_QDOT_F), 24, 28, 36 }, + { OP(0, 0, 0, OP_QMUL_F), 24, 28, 40 }, + { OP(0, 0, 0, OP_QVMUL_F), 24, 32, 44 }, + { OP(0, 0, 0, OP_VQMUL_F), 32, 24, 48 }, + + { OP(0, 0, 0, OP_QMUL_F), 24, 32, 52 }, + { OP(0, 0, 0, OP_SWIZZLE_F), 24, 0x07e4, 60 }, + { OP(0, 0, 0, OP_QMUL_F), 52, 60, 52 }, + + { OP(0, 0, 0, OP_SWIZZLE_F), 24, 0x07e4, 64 }, + { OP(0, 0, 0, OP_QMUL_F), 64, 32, 56 }, + { OP(0, 0, 0, OP_QMUL_F), 56, 24, 56 }, + + { OP(0, 0, 0, OP_QV4MUL_F), 24, 32, 68 }, + { OP(0, 0, 0, OP_V4QMUL_F), 32, 24, 72 }, +}; + +static pr_dvec4_t double_globals_init[] = { + {3, 4, 5, 12}, + {0, 0, 0, 0}, + {1, 2, 3, 8}, + {4, 5, 6, 8}, + + {0, 0, 0, 7}, + {0, 0, 0, 7}, + {1, 2, 3, 4}, + {5, 6, 7, 8}, + + {2, 3, 4, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 7}, + + {0, 0, 0, 7}, + {0, 0, 0, 7}, + {0, 0, 0, 7}, + {0, 0, 0, 0}, + + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, +}; + +static pr_dvec4_t double_globals_expect[] = { + {3, 4, 5, 12}, + {63, 63, -33, 56}, + {1, 2, 3, 8}, + {4, 5, 6, 8}, + + {32, 32, 32, 7}, + {-3, 6, -3, 7}, + {1, 2, 3, 4}, + {5, 6, 7, 8}, + + {2, 3, 4, 0}, + {70, 70, 70, 70}, + {24, 48, 48, -6}, + {36, 102, 120, 7}, + + {52, 70, 136, 7}, + {36, 102, 120, 0}, + {52, 70, 136, 0}, + {-1, -2, -3, 4}, + + {-1, -2, -3, 4}, + {36, 102, 120, 0}, + {52, 70, 136, 0}, +}; + +static dstatement_t double_vector_statements[] = { + { OP(0, 0, 0, OP_CDOT_D), 0, 4, 8 }, + { OP(0, 0, 0, OP_CMUL_D), 0, 4, 12 }, + { OP(0, 0, 0, OP_VDOT_D), 16, 24, 32 }, + { OP(0, 0, 0, OP_CROSS_D), 16, 24, 40 }, + { OP(0, 0, 0, OP_QDOT_D), 48, 56, 72 }, + { OP(0, 0, 0, OP_QMUL_D), 48, 56, 80 }, + { OP(0, 0, 0, OP_QVMUL_D), 48, 64, 88 }, + { OP(0, 0, 0, OP_VQMUL_D), 64, 48, 96 }, + + { OP(0, 0, 0, OP_QMUL_D), 48, 64, 104 }, + { OP(0, 0, 0, OP_SWIZZLE_D), 48, 0x07e4, 120 }, + { OP(0, 0, 0, OP_QMUL_D), 104, 120, 104 }, + + { OP(0, 0, 0, OP_SWIZZLE_D), 48, 0x07e4, 128 }, + { OP(0, 0, 0, OP_QMUL_D), 128, 64, 112 }, + { OP(0, 0, 0, OP_QMUL_D), 112, 48, 112 }, + + { OP(0, 0, 0, OP_QV4MUL_D), 48, 64, 136 }, + { OP(0, 0, 0, OP_V4QMUL_D), 64, 48, 144 }, +}; + +test_t tests[] = { + { + .desc = "float vector", + .num_globals = num_globals(float_globals_init, float_globals_expect), + .num_statements = num_statements (float_vector_statements), + .statements = float_vector_statements, + .init_globals = (pr_int_t *) float_globals_init, + .expect_globals = (pr_int_t *) float_globals_expect, + }, + { + .desc = "double vector", + .num_globals = num_globals(double_globals_init,double_globals_expect), + .num_statements = num_statements (double_vector_statements), + .statements = double_vector_statements, + .init_globals = (pr_int_t *) double_globals_init, + .expect_globals = (pr_int_t *) double_globals_expect, + }, +}; + +#include "main.c" diff --git a/libs/gamecode/test/test-with.c b/libs/gamecode/test/test-with.c new file mode 100644 index 000000000..f86637360 --- /dev/null +++ b/libs/gamecode/test/test-with.c @@ -0,0 +1,307 @@ +#include "head.c" + +#include "QF/mathlib.h" + +#define DB 0xdeadbeef + +static pr_ivec4_t pushregs_init[] = { + { DB, DB, DB, DB}, +}; + +static pr_ivec4_t pushregs_expect[] = { + { 0, 0, 0, 0}, // initial base regs should all be 0 +}; + +static dstatement_t pushregs_statements[] = { + { OP(0, 0, 0, OP_WITH), 8, 0, 0 }, // pushregs + { OP(0, 0, 0, OP_POP_A_4), 0, 0, 0 }, +}; + +static pr_ivec4_t popregs_init[] = { + { 4, 5, 6, 7}, + { DB, DB, DB, DB}, +}; + +static pr_ivec4_t popregs_expect[] = { + { 4, 5, 6, 7}, + { 7, 6, 5, 4}, +}; + +static dstatement_t popregs_statements[] = { + { OP(0, 0, 0, OP_PUSH_A_4), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 9, 0, 0 }, // popregs + { OP(3, 0, 0, OP_LEA_A), 0, 0, 0 }, + { OP(2, 0, 1, OP_LEA_A), 0, 0, 0 }, + { OP(1, 0, 2, OP_LEA_A), 0, 0, 0 }, + { OP(0, 0, 3, OP_LEA_A), 0, 0, 0 }, +}; + +static pr_ivec4_t with_0_init[] = { + { 4, 5, 6, 7}, + { DB, DB, DB, DB}, + { DB, DB, DB, DB}, + { DB, DB, DB, DB}, +}; + +static pr_ivec4_t with_0_expect[] = { + { 4, 5, 6, 7}, + { 0, 1, 6, 7}, + { 4, 5, 0, 1}, + { 0, 0, 0, 0}, +}; + +static dstatement_t with_0_statements[] = { + { OP(0, 0, 0, OP_PUSH_A_4), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 9, 0, 0 }, // popregs + { OP(0, 0, 0, OP_WITH), 0, 0, 0 }, // set reg 0 to 0 + { OP(0, 0, 0, OP_WITH), 0, 1, 1 }, // set reg 1 to 1 + { OP(0, 0, 0, OP_WITH), 8, 0, 0 }, // pushregs + { OP(0, 0, 0, OP_POP_A_4), 4, 0, 0 }, + { OP(0, 0, 0, OP_PUSH_A_4), 0, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 9, 0, 0 }, // popregs + { OP(0, 0, 0, OP_WITH), 0, 0, 2 }, // set reg 2 to 0 + { OP(0, 0, 0, OP_WITH), 0, 1, 3 }, // set reg 3 to 1 + { OP(0, 0, 0, OP_WITH), 8, 0, 0 }, // pushregs + { OP(2, 0, 0, OP_POP_A_4), 8, 0, 0 }, + { OP(0, 0, 0, OP_WITH), 10, 0, 0 }, // reset + { OP(0, 0, 0, OP_WITH), 8, 0, 0 }, // pushregs + { OP(2, 0, 0, OP_POP_A_4), 12, 0, 0 }, +}; + +static pr_ivec4_t with_1_init[] = { + { 4, 5, 6, 7}, + { DB, DB, DB, DB}, +}; + +static pr_ivec4_t with_1_expect[] = { + { 4, 5, 6, 7}, + { 0, 4, 6, 2}, +}; + +static dstatement_t with_1_statements[] = { + { OP(0, 0, 0, OP_WITH), 0, 4, 1 }, + { OP(0, 1, 0, OP_WITH), 1, 2, 2 }, + { OP(0, 1, 0, OP_WITH), 1, -2, 3 }, + { OP(0, 0, 0, OP_WITH), 8, 0, 0 }, // pushregs + { OP(0, 0, 0, OP_POP_A_4), 4, 0, 0 }, +}; + +static pr_ivec4_t with_2_init[] = { + { 4, 5, 6, 7}, + { DB, DB, DB, DB}, + { DB, DB, DB, DB}, + { DB, DB, DB, DB}, +}; + +static pr_ivec4_t with_2_expect[] = { + { 4, 5, 6, 7}, + { 0, 44, 48, 40}, + { DB, DB, DB, DB}, + { DB, DB, DB, DB}, +}; + +static dstatement_t with_2_statements[] = { + { OP(0, 0, 0, OP_PUSH_A_4), 0, 0, 0 }, // so something is on the stack + { OP(0, 0, 0, OP_WITH), 2, 0, 1 }, + { OP(0, 1, 0, OP_WITH), 2, 4, 2 }, + { OP(0, 1, 0, OP_WITH), 2, -4, 3 }, + { OP(0, 0, 0, OP_WITH), 8, 0, 0 }, // pushregs + { OP(0, 0, 0, OP_POP_A_4), 4, 0, 0 }, +}; + +static pr_ivec4_t with_3_init[] = { + { 4, 5, 6, 7}, + { DB, DB, DB, DB}, +}; + +static pr_ivec4_t with_3_expect[] = { + { 4, 5, 6, 7}, + { 0, 64, 68, 65596}, // edict-area relative is only +ve +}; + +static dstatement_t with_3_statements[] = { + { OP(0, 0, 0, OP_WITH), 3, 0, 1 }, + { OP(0, 1, 0, OP_WITH), 3, 4, 2 }, + { OP(0, 1, 0, OP_WITH), 3, -4, 3 }, + { OP(0, 0, 0, OP_WITH), 8, 0, 0 }, // pushregs + { OP(0, 0, 0, OP_POP_A_4), 4, 0, 0 }, +}; + +static pr_ivec4_t with_4_init[] = { + { 4, 5, 6, 7}, + { DB, DB, DB, DB}, +}; + +static pr_ivec4_t with_4_expect[] = { + { 4, 5, 6, 7}, + { 0, 4, 5, 6}, +}; + +static dstatement_t with_4_statements[] = { + { OP(0, 0, 0, OP_WITH), 4, 0, 1 }, + { OP(0, 1, 0, OP_WITH), 4, 1, 2 }, + { OP(0, 1, 0, OP_WITH), 4, 2, 3 }, + { OP(0, 0, 0, OP_WITH), 8, 0, 0 }, // pushregs + { OP(0, 0, 0, OP_POP_A_4), 4, 0, 0 }, +}; + +static pr_ivec4_t with_5_init[] = { + { 4, 5, 6, 7}, + { DB, DB, DB, DB}, +}; + +static pr_ivec4_t with_5_expect[] = { + { 4, 5, 6, 7}, + { 0, 2, 6, 7}, +}; + +static dstatement_t with_5_statements[] = { + { OP(0, 0, 0, OP_WITH), 0, 2, 1 }, + { OP(0, 1, 0, OP_WITH), 5, 0, 2 }, + { OP(0, 1, 0, OP_WITH), 5, 1, 3 }, + { OP(0, 0, 0, OP_WITH), 8, 0, 0 }, // pushregs + { OP(0, 0, 0, OP_POP_A_4), 4, 0, 0 }, +}; + +static pr_ivec4_t with_6_init[] = { + { 4, 5, 6, -4}, + { DB, DB, DB, DB}, + { DB, DB, DB, DB}, + { DB, DB, DB, DB}, +}; + +static pr_ivec4_t with_6_expect[] = { + { 4, 5, 6, -4}, + { 0, 44, 48, 40}, + { DB, DB, DB, DB}, + { DB, DB, DB, DB}, +}; + +static dstatement_t with_6_statements[] = { + { OP(0, 0, 0, OP_PUSH_A_4), 0, 0, 0 }, // so something is on the stack + { OP(0, 0, 0, OP_WITH), 2, 0, 1 }, + { OP(0, 1, 0, OP_WITH), 6, 0, 2 }, + { OP(0, 1, 0, OP_WITH), 6, 3, 3 }, + { OP(0, 0, 0, OP_WITH), 8, 0, 0 }, // pushregs + { OP(0, 0, 0, OP_POP_A_4), 4, 0, 0 }, +}; + +static pr_ivec4_t with_7_init[] = { + { 4, 5, 6, -4}, + { DB, DB, DB, DB}, +}; + +static pr_ivec4_t with_7_expect[] = { + { 4, 5, 6, -4}, + { 0, 2, 70, 60}, // edict-area relative is only +ve, but 32-bit wrap +}; + +static dstatement_t with_7_statements[] = { + { OP(0, 0, 0, OP_WITH), 0, 2, 1 }, + { OP(0, 1, 0, OP_WITH), 7, 0, 2 }, + { OP(0, 1, 0, OP_WITH), 7, 1, 3 }, + { OP(0, 0, 0, OP_WITH), 8, 0, 0 }, // pushregs + { OP(0, 0, 0, OP_POP_A_4), 4, 0, 0 }, +}; + +test_t tests[] = { + { + .desc = "pushregs", + .num_globals = num_globals(pushregs_init,pushregs_expect), + .num_statements = num_statements (pushregs_statements), + .statements = pushregs_statements, + .init_globals = (pr_int_t *) pushregs_init, + .expect_globals = (pr_int_t *) pushregs_expect, + .stack_size = 32, + }, + { + .desc = "popregs", + .num_globals = num_globals(popregs_init,popregs_expect), + .num_statements = num_statements (popregs_statements), + .statements = popregs_statements, + .init_globals = (pr_int_t *) popregs_init, + .expect_globals = (pr_int_t *) popregs_expect, + .stack_size = 32, + }, + { + .desc = "with 0", + .num_globals = num_globals(with_0_init,with_0_expect), + .num_statements = num_statements (with_0_statements), + .statements = with_0_statements, + .init_globals = (pr_int_t *) with_0_init, + .expect_globals = (pr_int_t *) with_0_expect, + .stack_size = 32, + }, + { + .desc = "with 1", + .num_globals = num_globals(with_1_init,with_1_expect), + .num_statements = num_statements (with_1_statements), + .statements = with_1_statements, + .init_globals = (pr_int_t *) with_1_init, + .expect_globals = (pr_int_t *) with_1_expect, + .stack_size = 32, + .edict_area = 64, + }, + { + .desc = "with 2", + .num_globals = num_globals(with_2_init,with_2_expect), + .num_statements = num_statements (with_2_statements), + .statements = with_2_statements, + .init_globals = (pr_int_t *) with_2_init, + .expect_globals = (pr_int_t *) with_2_expect, + .stack_size = 32, + .edict_area = 64, + }, + { + .desc = "with 3", + .num_globals = num_globals(with_3_init,with_3_expect), + .num_statements = num_statements (with_3_statements), + .statements = with_3_statements, + .init_globals = (pr_int_t *) with_3_init, + .expect_globals = (pr_int_t *) with_3_expect, + .stack_size = 32, + .edict_area = 64, + }, + { + .desc = "with 4", + .num_globals = num_globals(with_4_init,with_4_expect), + .num_statements = num_statements (with_4_statements), + .statements = with_4_statements, + .init_globals = (pr_int_t *) with_4_init, + .expect_globals = (pr_int_t *) with_4_expect, + .stack_size = 32, + .edict_area = 64, + }, + { + .desc = "with 5", + .num_globals = num_globals(with_5_init,with_5_expect), + .num_statements = num_statements (with_5_statements), + .statements = with_5_statements, + .init_globals = (pr_int_t *) with_5_init, + .expect_globals = (pr_int_t *) with_5_expect, + .stack_size = 32, + .edict_area = 64, + }, + { + .desc = "with 6", + .num_globals = num_globals(with_6_init,with_6_expect), + .num_statements = num_statements (with_6_statements), + .statements = with_6_statements, + .init_globals = (pr_int_t *) with_6_init, + .expect_globals = (pr_int_t *) with_6_expect, + .stack_size = 32, + .edict_area = 64, + }, + { + .desc = "with 7", + .num_globals = num_globals(with_7_init,with_7_expect), + .num_statements = num_statements (with_7_statements), + .statements = with_7_statements, + .init_globals = (pr_int_t *) with_7_init, + .expect_globals = (pr_int_t *) with_7_expect, + .stack_size = 32, + .edict_area = 64, + }, +}; + +#include "main.c" diff --git a/libs/gib/Makefile.am b/libs/gib/Makefile.am deleted file mode 100644 index 80ea92067..000000000 --- a/libs/gib/Makefile.am +++ /dev/null @@ -1,22 +0,0 @@ -AUTOMAKE_OPTIONS= foreign -AM_CFLAGS= @PREFER_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include $(FNM_FLAGS) - -lib_ldflags=-version-info $(QUAKE_LIBRARY_VERSION_INFO) \ - -rpath $(libdir) -no-undefined - -gib_deps= \ - $(top_builddir)/libs/ruamoko/libQFruamoko.la \ - $(top_builddir)/libs/util/libQFutil.la - -lib_LTLIBRARIES= libQFgib.la - -libQFgib_la_LDFLAGS= $(lib_ldflags) -libQFgib_la_LIBADD= $(gib_deps) -libQFgib_la_DEPENDENCIES= $(gib_deps) -libQFgib_la_SOURCES= \ - bi_gib.c \ - gib_buffer.c gib_builtin.c gib_classes.c gib_execute.c gib_function.c \ - gib_parse.c gib_handle.c gib_object.c gib_process.c gib_regex.c \ - gib_thread.c gib_vars.c gib_init.c gib_tree.c \ - gib_semantics.c ops.c exp.c regex.c diff --git a/libs/gib/Makemodule.am b/libs/gib/Makemodule.am new file mode 100644 index 000000000..5773bbafd --- /dev/null +++ b/libs/gib/Makemodule.am @@ -0,0 +1,37 @@ +gib_deps= \ + libs/ruamoko/libQFruamoko.la \ + libs/util/libQFutil.la + +lib_LTLIBRARIES += \ + libs/gib/libQFgib.la \ + libs/gib/libQFgib_client.la + +libs_gib_libQFgib_la_LDFLAGS= $(lib_ldflags) +libs_gib_libQFgib_la_LIBADD= $(gib_deps) +libs_gib_libQFgib_la_DEPENDENCIES= $(gib_deps) +libs_gib_libQFgib_la_SOURCES= \ + libs/gib/bi_gib.c \ + libs/gib/gib_buffer.c \ + libs/gib/gib_builtin.c \ + libs/gib/gib_classes.c \ + libs/gib/gib_execute.c \ + libs/gib/gib_function.c \ + libs/gib/gib_parse.c \ + libs/gib/gib_handle.c \ + libs/gib/gib_object.c \ + libs/gib/gib_process.c \ + libs/gib/gib_regex.c \ + libs/gib/gib_thread.c \ + libs/gib/gib_vars.c \ + libs/gib/gib_init.c \ + libs/gib/gib_tree.c \ + libs/gib/gib_semantics.c \ + libs/gib/ops.c \ + libs/gib/exp.c \ + libs/gib/regex.c + +libs_gib_libQFgib_client_la_LDFLAGS= $(lib_ldflags) +libs_gib_libQFgib_client_la_LIBADD= libs/gib/libQFgib.la +libs_gib_libQFgib_client_la_DEPENDENCIES= libs/gib/libQFgib.la ${gib_deps} +libs_gib_libQFgib_client_la_SOURCES= \ + libs/gib/gib_keys.c diff --git a/libs/gib/bi_gib.c b/libs/gib/bi_gib.c index b1482c030..049507e16 100644 --- a/libs/gib/bi_gib.c +++ b/libs/gib/bi_gib.c @@ -39,6 +39,7 @@ #include "QF/cmd.h" #include "QF/csqc.h" +#include "QF/dstring.h" #include "QF/hash.h" #include "QF/progs.h" #include "QF/sys.h" @@ -48,8 +49,8 @@ typedef struct bi_gib_builtin_s { struct bi_gib_builtin_s *next; gib_builtin_t *builtin; - progs_t *pr; - func_t func; + progs_t *pr; + pr_func_t func; } bi_gib_builtin_t; typedef struct bi_gib_resources_s { @@ -86,20 +87,22 @@ bi_gib_builtin_f (void) pr_list = PR_Zone_Malloc (builtin->pr, GIB_Argc() * sizeof (pr_type_t)); for (i = 0; i < GIB_Argc(); i++) - pr_list[i].integer_var = PR_SetTempString (builtin->pr, GIB_Argv(i)); + PR_PTR (string, &pr_list[i]) = PR_SetTempString (builtin->pr, + GIB_Argv(i)); PR_RESET_PARAMS (builtin->pr); P_INT (builtin->pr, 0) = GIB_Argc(); P_INT (builtin->pr, 1) = PR_SetPointer (builtin->pr, pr_list); + builtin->pr->pr_argc = 2; PR_ExecuteProgram (builtin->pr, builtin->func); PR_PopFrame (builtin->pr); PR_Zone_Free (builtin->pr, pr_list); } static void -bi_gib_builtin_clear (progs_t *progs, void *data) +bi_gib_builtin_clear (progs_t *progs, void *_res) { - bi_gib_resources_t *res = (bi_gib_resources_t *) data; + bi_gib_resources_t *res = (bi_gib_resources_t *) _res; bi_gib_builtin_t *cur; while ((cur = res->builtins)) { @@ -111,12 +114,20 @@ bi_gib_builtin_clear (progs_t *progs, void *data) } static void -bi_GIB_Builtin_Add (progs_t *pr) +bi_gib_builtin_destroy (progs_t *progs, void *_res) { - bi_gib_resources_t *res = PR_Resources_Find (pr, "GIB"); + bi_gib_resources_t *res = (bi_gib_resources_t *) _res; + Hash_DelTable (bi_gib_builtins); + free (res); +} + +static void +bi_GIB_Builtin_Add (progs_t *pr, void *_res) +{ + bi_gib_resources_t *res = _res; bi_gib_builtin_t *builtin; const char *name = P_GSTRING (pr, 0); - func_t func = P_FUNCTION (pr, 1); + pr_func_t func = P_FUNCTION (pr, 1); if (GIB_Builtin_Exists (name)) { R_INT (pr) = 0; @@ -137,7 +148,7 @@ bi_GIB_Builtin_Add (progs_t *pr) } static void -bi_GIB_Return (progs_t *pr) +bi_GIB_Return (progs_t *pr, void *_res) { const char *str = P_GSTRING(pr, 0); @@ -147,7 +158,7 @@ bi_GIB_Return (progs_t *pr) } static void -bi_GIB_Handle_New (progs_t *pr) +bi_GIB_Handle_New (progs_t *pr, void *_res) { //long *qcptr = malloc (sizeof (long)); //*qcptr = P_POINTER (pr, 0); @@ -155,7 +166,7 @@ bi_GIB_Handle_New (progs_t *pr) } static void -bi_GIB_Handle_Free (progs_t *pr) +bi_GIB_Handle_Free (progs_t *pr, void *_res) { //unsigned long int hand = P_INT (pr, 0); //long *qcptr = GIB_Handle_Get (hand); @@ -165,7 +176,7 @@ bi_GIB_Handle_Free (progs_t *pr) } static void -bi_GIB_Handle_Get (progs_t *pr) +bi_GIB_Handle_Get (progs_t *pr, void *_res) { //long *hand = GIB_Handle_Get (P_INT (pr, 0)); //if (hand) @@ -174,12 +185,15 @@ bi_GIB_Handle_Get (progs_t *pr) // R_INT (pr) = 0; } +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) +#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } static builtin_t builtins[] = { - {"GIB_Builtin_Add", bi_GIB_Builtin_Add, -1}, - {"GIB_Return", bi_GIB_Return, -1}, - {"GIB_Handle_New", bi_GIB_Handle_New, -1}, - {"GIB_Handle_Free", bi_GIB_Handle_Free, -1}, - {"GIB_Handle_Get", bi_GIB_Handle_Get, -1}, + bi(GIB_Builtin_Add, 2, p(string), p(func)), + bi(GIB_Return, 1, p(string)), + bi(GIB_Handle_New, 0),//FIXME + bi(GIB_Handle_Free, 0),//FIXME + bi(GIB_Handle_Get, 0),//FIXME {0} }; @@ -189,10 +203,13 @@ GIB_Progs_Init (progs_t *pr) bi_gib_resources_t *res = malloc (sizeof (bi_gib_resources_t)); res->builtins = 0; - PR_Resources_Register (pr, "GIB", res, bi_gib_builtin_clear); - + if (bi_gib_builtins) { + Sys_Error ("GIB_Progs_Init: only one progs VM supported FIXME"); + } bi_gib_builtins = Hash_NewTable (1021, bi_gib_builtin_get_key, - bi_gib_builtin_free, 0); + bi_gib_builtin_free, 0, pr->hashctx); - PR_RegisterBuiltins (pr, builtins); + PR_Resources_Register (pr, "GIB", res, bi_gib_builtin_clear, + bi_gib_builtin_destroy); + PR_RegisterBuiltins (pr, builtins, res); } diff --git a/libs/gib/exp.c b/libs/gib/exp.c index 7f831a122..3ca5b19b8 100644 --- a/libs/gib/exp.c +++ b/libs/gib/exp.c @@ -154,7 +154,7 @@ EXP_FindFuncByStr (const char *str) return 0; } -static int +static __attribute__((pure)) int EXP_ContainsCommas (token * chain) { token *cur; @@ -311,7 +311,7 @@ EXP_ParseString (char *str) } else { EXP_DestroyTokens (chain); EXP_Error (EXP_E_INVOP, - va ("Unknown operator or function '%s'.", buf)); + va (0, "Unknown operator or function '%s'.", buf)); return 0; } } @@ -347,8 +347,7 @@ EXP_SimplifyTokens (token * chain) cur = cur->generic.prev; if (EXP_DoFunction (cur)) return EXP_Error (EXP_E_SYNTAX, - va - ("Invalid number of arguments to function '%s'.", + va (0, "Invalid number of arguments to function '%s'.", cur->func.func->str)); } else { if (EXP_ContainsCommas (cur)) @@ -374,8 +373,7 @@ EXP_SimplifyTokens (token * chain) if (cur->generic.next->generic.type == TOKEN_OP) if (EXP_DoUnary (cur->generic.next)) return EXP_Error (EXP_E_SYNTAX, - va - ("Unary operator '%s' not followed by a unary operator or numerical value.", + va (0, "Unary operator '%s' not followed by a unary operator or numerical value.", cur->generic.next->op.op->str)); if (optable[i].operands == 1 && cur->generic.next->generic.type == TOKEN_NUM) { @@ -482,14 +480,12 @@ EXP_Validate (token * chain) cur->generic.next->op.op = EXP_FindOpByStr ("neg"); else if (cur->generic.next->op.op->operands == 2) return EXP_Error (EXP_E_SYNTAX, - va - ("Operator '%s' does not follow a number or numerical value.", + va (0, "Operator '%s' does not follow a number or numerical value.", cur->generic.next->op.op->str)); } else if (cur->generic.type == TOKEN_FUNC && cur->generic.next->generic.type != TOKEN_OPAREN) return EXP_Error (EXP_E_SYNTAX, - va - ("Function '%s' called without an argument list.", + va (0, "Function '%s' called without an argument list.", cur->func.func->str)); else if (cur->generic.type == TOKEN_COMMA && @@ -501,7 +497,7 @@ EXP_Validate (token * chain) else if (cur->generic.type == TOKEN_OP && cur->generic.next->generic.type == TOKEN_CPAREN) return EXP_Error (EXP_E_SYNTAX, - va ("Operator '%s' is missing an operand.", + va (0, "Operator '%s' is missing an operand.", cur->op.op->str)); else if (cur->generic.type == TOKEN_NUM && cur->generic.next->generic.type == TOKEN_NUM) diff --git a/libs/gib/gib_builtin.c b/libs/gib/gib_builtin.c index c3ba2910a..eef664cc9 100644 --- a/libs/gib/gib_builtin.c +++ b/libs/gib/gib_builtin.c @@ -99,7 +99,7 @@ GIB_Builtin_Add (const char *name, void (*func) (void)) if (!gib_builtins) gib_builtins = - Hash_NewTable (1024, GIB_Builtin_Get_Key, GIB_Builtin_Free, 0); + Hash_NewTable (1024, GIB_Builtin_Get_Key, GIB_Builtin_Free, 0, 0); new = calloc (1, sizeof (gib_builtin_t)); new->func = func; @@ -116,7 +116,7 @@ GIB_Builtin_Remove (const char *name) Hash_Free (gib_builtins, del); } -VISIBLE qboolean +VISIBLE bool GIB_Builtin_Exists (const char *name) { return Hash_Find (gib_builtins, name) ? true : false; @@ -236,8 +236,8 @@ GIB_Local_f (void) for (i = 3; i < GIB_Argc(); i++) GIB_Return (GIB_Argv(i)); } else for (i = 1; i < GIB_Argc(); i++) - var = GIB_Var_Get_Complex (&GIB_DATA (cbuf_active)->locals, &zero, - GIB_Argv (i), &index, true); + GIB_Var_Get_Complex (&GIB_DATA (cbuf_active)->locals, &zero, + GIB_Argv (i), &index, true); } @@ -261,8 +261,8 @@ GIB_Shared_f (void) for (i = 3; i < GIB_Argc(); i++) GIB_Return (GIB_Argv(i)); } else for (i = 1; i < GIB_Argc(); i++) - var = GIB_Var_Get_Complex (&GIB_DATA (cbuf_active)->globals, &zero, - GIB_Argv (i), &index, true); + GIB_Var_Get_Complex (&GIB_DATA (cbuf_active)->globals, &zero, + GIB_Argv (i), &index, true); } static void @@ -823,7 +823,7 @@ GIB_File_Write_f (void) } path = GIB_Argv (1); - QFS_WriteFile (va ("%s/%s", qfs_gamedir->dir.def, path), + QFS_WriteFile (va (0, "%s/%s", qfs_gamedir->dir.def, path), GIB_Argv(2), GIB_Argd(2)->size-1); } @@ -1039,8 +1039,14 @@ GIB_bp4_f (void) { } +static void +gib_builtin_shutdown (void *data) +{ + Hash_DelTable (gib_builtins); +} + void -GIB_Builtin_Init (qboolean sandbox) +GIB_Builtin_Init (bool sandbox) { if (sandbox) @@ -1090,4 +1096,6 @@ GIB_Builtin_Init (qboolean sandbox) GIB_Builtin_Add ("bp2", GIB_bp2_f); GIB_Builtin_Add ("bp3", GIB_bp3_f); GIB_Builtin_Add ("bp4", GIB_bp4_f); + + Sys_RegisterShutdown (gib_builtin_shutdown, 0); } diff --git a/libs/gib/gib_classes.c b/libs/gib/gib_classes.c index 749e7ff07..e02beada2 100644 --- a/libs/gib/gib_classes.c +++ b/libs/gib/gib_classes.c @@ -38,6 +38,7 @@ #include +#include "QF/cbuf.h" #include "QF/gib.h" #include "QF/va.h" #include "QF/sys.h" @@ -187,7 +188,7 @@ Object_Class_New_f (gib_object_t *obj, gib_method_t *method, void *data, static const char **g_occ_reply; static unsigned int g_occ_i = 0; -static qboolean occ_iterator (gib_class_t *class, void *unused) +static bool occ_iterator (gib_class_t *class, void *unused) { g_occ_reply[g_occ_i++] = class->name; return false; @@ -281,7 +282,7 @@ typedef struct Thread_class_s { typedef struct Thread_s { gib_object_t *obj; cbuf_t *thread; - qboolean ended; + bool ended; } Thread_t; static int @@ -388,8 +389,7 @@ ObjectHash_Construct (gib_object_t *obj) { ObjectHash_t *data = malloc (sizeof (ObjectHash_t)); - data->objects = Hash_NewTable (1024, ObjRef_Get_Key, ObjRef_Free, - NULL); + data->objects = Hash_NewTable (1024, ObjRef_Get_Key, ObjRef_Free, NULL, 0); return data; } @@ -445,7 +445,7 @@ ObjectHash_Get_f (gib_object_t *obj, gib_method_t *method, void *data, if ((refs = (ObjRef_t **) Hash_FindList (objh->objects, mesg.argv[1]))) { for (r = refs, len = 0; *r; r++, len++); - reply = malloc (sizeof (char **) * len); + reply = malloc (sizeof (char *) * len); for (r = refs, i = 0; *r; r++, i++) reply[i] = (*r)->obj->handstr; GIB_Reply (obj, mesg, len, reply); @@ -613,9 +613,9 @@ static const char *g_gcbs_name; static const char *gcbs_fname (const char *str) { if (g_gcbs_mode == INSTANCE) - return va ("__%s_%s__", g_gcbs_name, str); + return va (0, "__%s_%s__", g_gcbs_name, str); else - return va ("%s::%s", g_gcbs_name, str); + return va (0, "%s::%s", g_gcbs_name, str); } void diff --git a/libs/gib/gib_execute.c b/libs/gib/gib_execute.c index c93f91f31..a8d3138cb 100644 --- a/libs/gib/gib_execute.c +++ b/libs/gib/gib_execute.c @@ -203,7 +203,7 @@ GIB_Execute (cbuf_t * cbuf) unsigned int index; gib_var_t *var; int i; - qboolean super; + bool super; static const char **mesg = NULL; static int maxmesg = 0; diff --git a/libs/gib/gib_function.c b/libs/gib/gib_function.c index 72c1d1ea5..b0141dca5 100644 --- a/libs/gib/gib_function.c +++ b/libs/gib/gib_function.c @@ -38,6 +38,7 @@ #include "QF/sys.h" #include "QF/dstring.h" #include "QF/hash.h" +#include "QF/llist.h" #include "QF/cbuf.h" #include "QF/va.h" #include "QF/gib.h" @@ -117,8 +118,8 @@ GIB_Function_Define (const char *name, const char *text, gib_tree_t * program, if (script) script->refs++; if (!gib_functions) - gib_functions = - Hash_NewTable (1024, GIB_Function_Get_Key, GIB_Function_Free, 0); + gib_functions = Hash_NewTable (1024, GIB_Function_Get_Key, + GIB_Function_Free, 0, 0); func = Hash_Find (gib_functions, name); if (func) { @@ -162,7 +163,7 @@ static unsigned int g_fpa_argc; static hashtab_t *g_fpa_zero = 0; static unsigned int g_fpa_i, g_fpa_ind; -static qboolean fpa_iterate (char *arg, llist_node_t *node) +static bool fpa_iterate (char *arg, llist_node_t *node) { gib_var_t *var = GIB_Var_Get_Complex (&GIB_DATA(g_fpa_cbuf)->locals, &g_fpa_zero, arg, &g_fpa_ind, true); @@ -209,7 +210,7 @@ static unsigned int g_fpad_argc; static hashtab_t *g_fpad_zero = 0; static unsigned int g_fpad_i, g_fpad_ind; -static qboolean fpad_iterate (char *arg, llist_node_t *node) +static bool fpad_iterate (char *arg, llist_node_t *node) { gib_var_t *var; @@ -234,11 +235,10 @@ GIB_Function_Prepare_Args_D (cbuf_t * cbuf, dstring_t **args, unsigned int g_fpad_args = args; g_fpad_argc = argc; - i = 1; llist_iterate (arglist, LLIST_ICAST (fpad_iterate)); + llist_iterate (arglist, LLIST_ICAST (fpad_iterate)); - var = - GIB_Var_Get_Complex (&GIB_DATA (cbuf)->locals, &g_fpad_zero, argss, - &g_fpad_ind, true); + var = GIB_Var_Get_Complex (&GIB_DATA (cbuf)->locals, &g_fpad_zero, argss, + &g_fpad_ind, true); var->array = realloc (var->array, sizeof (struct gib_varray_s) * argc); memset (var->array + 1, 0, (argc - 1) * sizeof (struct gib_varray_s)); var->size = argc; diff --git a/libs/gib/gib_handle.c b/libs/gib/gib_handle.c index b6816396c..3d2c7fd96 100644 --- a/libs/gib/gib_handle.c +++ b/libs/gib/gib_handle.c @@ -31,6 +31,7 @@ #include +#include "QF/cbuf.h" #include "QF/gib.h" #include "gib_handle.h" diff --git a/libs/gib/gib_init.c b/libs/gib/gib_init.c index b689e141e..1730c550d 100644 --- a/libs/gib/gib_init.c +++ b/libs/gib/gib_init.c @@ -37,6 +37,7 @@ #include "QF/qtypes.h" #include "QF/cbuf.h" +#include "QF/dstring.h" #include "QF/quakefs.h" #include "QF/cmd.h" #include "QF/sys.h" @@ -62,22 +63,22 @@ static void GIB_Exec_Override_f (void) { char *f; - int mark; + size_t mark; if (Cmd_Argc () != 2) { Sys_Printf ("exec : execute a script file\n"); return; } - mark = Hunk_LowMark (); + mark = Hunk_LowMark (0); f = (char *) QFS_LoadHunkFile (QFS_FOpenFile (Cmd_Argv (1))); if (!f) { Sys_Printf ("couldn't exec %s\n", Cmd_Argv (1)); return; } if (!Cvar_Command () - && (cmd_warncmd->int_val - || (developer && developer->int_val & SYS_DEV))) + && (cmd_warncmd + || (developer && developer & SYS_dev))) Sys_Printf ("execing %s\n", Cmd_Argv (1)); if ((strlen (Cmd_Argv (1)) >= 4 && !strcmp (Cmd_Argv (1) + strlen (Cmd_Argv (1)) - 4, ".gib")) @@ -95,11 +96,11 @@ GIB_Exec_Override_f (void) Cmd_Argv (0), Cmd_Argv (1)); } else Cbuf_InsertText (cbuf_active, f); - Hunk_FreeToLowMark (mark); + Hunk_FreeToLowMark (0, mark); } VISIBLE void -GIB_Init (qboolean sandbox) +GIB_Init (bool sandbox) { // Override the exec command with a GIB-aware one if (Cmd_Exists ("exec")) { diff --git a/libs/gib/gib_keys.c b/libs/gib/gib_keys.c new file mode 100644 index 000000000..ae5aea823 --- /dev/null +++ b/libs/gib/gib_keys.c @@ -0,0 +1,80 @@ +/* + gib_keys.c + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2002 Brian Koropoff + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/cbuf.h" +#include "QF/cmd.h" +#include "QF/input.h" +#include "QF/sys.h" +#include "QF/gib.h" +#include "QF/keys.h" + +#include "compat.h" +#include "old_keys.h" + +static void +Key_GIB_Bind_Get_f (void) +{ +#if 0 + const char *key, *cmd; + imt_t *imt; + int k; + + if (GIB_Argc () != 2) { + GIB_USAGE ("key"); + return; + } + + key = OK_TranslateKeyName (GIB_Argv (1)); + + if ((k = Key_StringToKeynum (key)) == -1) { + GIB_Error ("bind", "bind::get: invalid key %s", key); + return; + } + + imt = Key_FindIMT ("imt_mod"); + if (!imt || !(cmd = Key_GetBinding (imt, k))) + GIB_Return (""); + else + GIB_Return (cmd); +#endif +} + +void +GIB_Key_Init (void) +{ + GIB_Builtin_Add ("bind::get", Key_GIB_Bind_Get_f); +} diff --git a/libs/gib/gib_object.c b/libs/gib/gib_object.c index a4954762b..3ddad1a4f 100644 --- a/libs/gib/gib_object.c +++ b/libs/gib/gib_object.c @@ -39,7 +39,9 @@ #include #include +#include "QF/cbuf.h" #include "QF/hash.h" +#include "QF/llist.h" #include "QF/sys.h" #include "QF/va.h" #include "QF/gib.h" @@ -125,7 +127,8 @@ GIB_Method_Build_Hash (gib_class_t *class, hashtab_t *inherited, { gib_methodtab_t *m; gib_method_t *method; - hashtab_t *new = Hash_NewTable (1024, GIB_Method_Get_Key, GIB_Method_Free, 0); + hashtab_t *new = Hash_NewTable (1024, GIB_Method_Get_Key, + GIB_Method_Free, 0, 0); for (m = methods; m->name; m++) { method = malloc (sizeof (gib_method_t)); @@ -190,7 +193,7 @@ GIB_Class_Create (gib_classdesc_t *desc) */ gib_object_t * -GIB_Object_Create (const char *classname, qboolean classobj) +GIB_Object_Create (const char *classname, bool classobj) { gib_class_t *temp, *class = Hash_Find (gib_classes, classname); gib_object_t *obj; @@ -204,10 +207,10 @@ GIB_Object_Create (const char *classname, qboolean classobj) obj->data = malloc (sizeof (void *) * (class->depth+1)); obj->methods = classobj ? class->class_methods : class->methods; obj->handle = classobj ? 0 : GIB_Handle_New (obj); - obj->handstr = strdup (va ("%lu", obj->handle)); + obj->handstr = strdup (va (0, "%lu", obj->handle)); obj->refs = 1; obj->signals = Hash_NewTable (128, GIB_Signal_Get_Key, - GIB_Signal_Free, NULL); + GIB_Signal_Free, NULL, 0); obj->slots = llist_new (GIB_Slot_Free, NULL, NULL); if (classobj) { @@ -392,7 +395,8 @@ GIB_Object_Signal_Emit (gib_object_t *sender, int argc, const char **argv) void GIB_Object_Init (void) { - gib_classes = Hash_NewTable (1024, GIB_Class_Get_Key, GIB_Class_Free, 0); + gib_classes = Hash_NewTable (1024, GIB_Class_Get_Key, + GIB_Class_Free, 0, 0); GIB_Classes_Init (); } diff --git a/libs/gib/gib_parse.c b/libs/gib/gib_parse.c index 0ace51571..fd0c34038 100644 --- a/libs/gib/gib_parse.c +++ b/libs/gib/gib_parse.c @@ -54,7 +54,7 @@ escaped with a backslash (and the backslash is not itself escaped). */ -inline qboolean +inline bool GIB_Escaped (const char *str, int i) { int n, c; @@ -186,7 +186,7 @@ GIB_Parse_Match_Var (const char *str, unsigned int *i) return 0; } -VISIBLE qboolean gib_parse_error; +VISIBLE bool gib_parse_error; unsigned int gib_parse_error_pos; const char *gib_parse_error_msg; @@ -333,7 +333,8 @@ GIB_Parse_Tokens (const char *program, unsigned int *i, unsigned int pofs) return nodes; ERROR: if (c) - GIB_Parse_Error (va ("Could not find match for '%c'.", c), *i + pofs); + GIB_Parse_Error (va (0, "Could not find match for '%c'.", c), + *i + pofs); if (nodes) GIB_Tree_Unref (&nodes); return 0; @@ -496,7 +497,8 @@ GIB_Parse_Embedded (gib_tree_t *token) return lines; ERROR: if (c) - GIB_Parse_Error (va ("Could not find match for '%c'.", c), i + token->start); + GIB_Parse_Error (va (0, "Could not find match for '%c'.", c), + i + token->start); if (lines) GIB_Tree_Unref (&lines); return 0; diff --git a/libs/gib/gib_process.c b/libs/gib/gib_process.c index 7492c8a91..182b92294 100644 --- a/libs/gib/gib_process.c +++ b/libs/gib/gib_process.c @@ -114,7 +114,8 @@ GIB_Process_Embedded (gib_tree_t * node, cbuf_args_t * args) } else if (cur->delim == '#') dstring_appendstr (args->argv[args->argc - 1], "0"); else if (cvar) - dstring_appendstr (args->argv[args->argc - 1], cvar->string); + dstring_appendstr (args->argv[args->argc - 1], + Cvar_VarString (cvar)); } else if ((var = GIB_Var_Get_Complex (&GIB_DATA (cbuf_active)->locals, &GIB_DATA (cbuf_active)->globals, (char *) cur->str, &index, false))) { @@ -126,7 +127,8 @@ GIB_Process_Embedded (gib_tree_t * node, cbuf_args_t * args) } else if (cur->delim == '#') dstring_appendstr (args->argv[args->argc - 1], "0"); else if ((cvar = Cvar_FindVar (cur->str))) - dstring_appendstr (args->argv[args->argc - 1], cvar->string); + dstring_appendstr (args->argv[args->argc - 1], + Cvar_VarString (cvar)); } if (str[prev]) dstring_appendstr (args->argv[args->argc - 1], str + prev); diff --git a/libs/gib/gib_regex.c b/libs/gib/gib_regex.c index e808532d6..dbe43cf04 100644 --- a/libs/gib/gib_regex.c +++ b/libs/gib/gib_regex.c @@ -63,7 +63,7 @@ GIB_Regex_Free (void *ele, void *ptr) void GIB_Regex_Init (void) { - gib_regexs = Hash_NewTable (512, GIB_Regex_Get_Key, GIB_Regex_Free, 0); + gib_regexs = Hash_NewTable (512, GIB_Regex_Get_Key, GIB_Regex_Free, 0, 0); } regex_t * diff --git a/libs/gib/gib_thread.c b/libs/gib/gib_thread.c index 9c317d9fe..f8679772c 100644 --- a/libs/gib/gib_thread.c +++ b/libs/gib/gib_thread.c @@ -83,7 +83,7 @@ GIB_Thread_Count (void) return llist_size (gib_threads); } -static qboolean te_iterator (cbuf_t *cbuf, llist_node_t *node) +static bool te_iterator (cbuf_t *cbuf, llist_node_t *node) { if (GIB_DATA(cbuf)->program) Cbuf_Execute_Stack (cbuf); @@ -171,5 +171,5 @@ GIB_Event_Callback (gib_event_t * event, unsigned int argc, ...) void GIB_Event_Init (void) { - gib_events = Hash_NewTable (1024, GIB_Event_Get_Key, GIB_Event_Free, 0); + gib_events = Hash_NewTable (1024, GIB_Event_Get_Key, GIB_Event_Free, 0, 0); } diff --git a/libs/gib/gib_tree.c b/libs/gib/gib_tree.c index 64a6f1172..fbf462072 100644 --- a/libs/gib/gib_tree.c +++ b/libs/gib/gib_tree.c @@ -72,13 +72,13 @@ void GIB_Tree_Ref (gib_tree_t ** tp) { (*tp)->refs++; -// Sys_MaskPrintf (SYS_DEV, "Ref: %p %u\n", *tp, (*tp)->refs); +// Sys_MaskPrintf (SYS_dev, "Ref: %p %u\n", *tp, (*tp)->refs); } void GIB_Tree_Unref (gib_tree_t ** tp) { -// Sys_MaskPrintf (SYS_DEV, "Unref: %p %u\n", *tp, (*tp)->refs - 1); +// Sys_MaskPrintf (SYS_dev, "Unref: %p %u\n", *tp, (*tp)->refs - 1); if (!(--(*tp)->refs)) { GIB_Tree_Free_Recursive (*tp); *tp = 0; diff --git a/libs/gib/gib_vars.c b/libs/gib/gib_vars.c index d8a555673..e1cccb9bf 100644 --- a/libs/gib/gib_vars.c +++ b/libs/gib/gib_vars.c @@ -51,7 +51,7 @@ GIB_Var_New (const char *key) { gib_var_t *new = calloc (1, sizeof (gib_var_t)); - new->array = calloc (1, sizeof (dstring_t *)); + new->array = calloc (1, sizeof (struct gib_varray_s)); new->key = strdup (key); return new; } @@ -95,7 +95,7 @@ GIB_Var_Get (hashtab_t * first, hashtab_t * second, const char *key) /* Alters key, but restores it */ gib_var_t * GIB_Var_Get_Complex (hashtab_t ** first, hashtab_t ** second, char *key, - unsigned int *ind, qboolean create) + unsigned int *ind, bool create) { static hashtab_t *zero = 0; unsigned int i, n, index = 0, len, start; @@ -117,7 +117,8 @@ GIB_Var_Get_Complex (hashtab_t ** first, hashtab_t ** second, char *key, if (!(var = GIB_Var_Get (*first, *second, key+start)) && create) { var = GIB_Var_New (key+start); if (!*first) - *first = Hash_NewTable (256, GIB_Var_Get_Key, GIB_Var_Free, 0); + *first = Hash_NewTable (256, GIB_Var_Get_Key, + GIB_Var_Free, 0, 0); Hash_Add (*first, var); } @@ -154,15 +155,16 @@ GIB_Var_Get_Complex (hashtab_t ** first, hashtab_t ** second, char *key, /* Mangles the hell out of key */ gib_var_t * GIB_Var_Get_Very_Complex (hashtab_t ** first, hashtab_t ** second, dstring_t *key, unsigned int start, - unsigned int *ind, qboolean create) + unsigned int *ind, bool create) { static hashtab_t *zero = 0; hashtab_t *one = *first, *two = *second; unsigned int i, index = 0, index2 = 0, n, protect, varstartskip; gib_var_t *var = 0; cvar_t *cvar; - char c, *str; - qboolean done = false; + char c; + const char *str; + bool done = false; for (i = start, protect = 0; !done; i++) { if (key->str[i] == '.' || key->str[i] == 0) { @@ -181,7 +183,8 @@ GIB_Var_Get_Very_Complex (hashtab_t ** first, hashtab_t ** second, dstring_t *ke if (create) { var = GIB_Var_New (key->str+start); if (!*first) - *first = Hash_NewTable (256, GIB_Var_Get_Key, GIB_Var_Free, 0); + *first = Hash_NewTable (256, GIB_Var_Get_Key, + GIB_Var_Free, 0, 0); Hash_Add (*first, var); } else return 0; @@ -208,7 +211,7 @@ GIB_Var_Get_Very_Complex (hashtab_t ** first, hashtab_t ** second, dstring_t *ke key->str[i] = 0; if ((var = GIB_Var_Get_Very_Complex (&one, &two, key, n+1+varstartskip, &index2, create))) { if (key->str[n] == '#') - str = va("%u", var->size); + str = va (0, "%u", var->size); else str = var->array[index2].value->str; key->str[i] = c; @@ -219,9 +222,11 @@ GIB_Var_Get_Very_Complex (hashtab_t ** first, hashtab_t ** second, dstring_t *ke dstring_replace (key, n, i-n+varstartskip, "0", 1); protect = n+1; } else if ((cvar = Cvar_FindVar (key->str+n+1+varstartskip))) { + const char *cvar_str = Cvar_VarString (cvar); key->str[i] = c; - dstring_replace (key, n, i-n+varstartskip, cvar->string, strlen (cvar->string)); - protect = n+strlen(cvar->string); + dstring_replace (key, n, i-n+varstartskip, cvar_str, + strlen (cvar_str)); + protect = n+strlen(cvar_str); } else { key->str[i] = c; dstring_snip (key, n, n-i+varstartskip); @@ -239,7 +244,7 @@ GIB_Var_Get_Very_Complex (hashtab_t ** first, hashtab_t ** second, dstring_t *ke void GIB_Var_Assign (gib_var_t * var, unsigned int index, dstring_t ** values, - unsigned int numv, qboolean shrink) + unsigned int numv, bool shrink) { unsigned int i, len; @@ -293,7 +298,7 @@ GIB_Domain_Get (const char *name) if (!d) { d = calloc (1, sizeof (gib_domain_t)); d->name = strdup (name); - d->vars = Hash_NewTable (1024, GIB_Var_Get_Key, GIB_Var_Free, 0); + d->vars = Hash_NewTable (1024, GIB_Var_Get_Key, GIB_Var_Free, 0, 0); Hash_Add (gib_domains, d); } return d->vars; @@ -302,12 +307,13 @@ GIB_Domain_Get (const char *name) hashtab_t * GIB_Var_Hash_New (void) { - return Hash_NewTable (1024, GIB_Var_Get_Key, GIB_Var_Free, 0); + return Hash_NewTable (1024, GIB_Var_Get_Key, GIB_Var_Free, 0, 0); } void GIB_Var_Init (void) { - gib_globals = Hash_NewTable (1024, GIB_Var_Get_Key, GIB_Var_Free, 0); - gib_domains = Hash_NewTable (1024, GIB_Domain_Get_Key, GIB_Domain_Free, 0); + gib_globals = Hash_NewTable (1024, GIB_Var_Get_Key, GIB_Var_Free, 0, 0); + gib_domains = Hash_NewTable (1024, GIB_Domain_Get_Key, + GIB_Domain_Free, 0, 0); } diff --git a/libs/gib/regex.c b/libs/gib/regex.c index 1d6e19dc4..4b7817d4f 100644 --- a/libs/gib/regex.c +++ b/libs/gib/regex.c @@ -816,8 +816,7 @@ reg_syntax_t re_syntax_options = RE_SYNTAX_EMACS; defined in regex.h. We return the old syntax. */ reg_syntax_t -re_set_syntax (syntax) - reg_syntax_t syntax; +re_set_syntax (reg_syntax_t syntax) { reg_syntax_t ret = re_syntax_options; @@ -963,7 +962,11 @@ static reg_errcode_t compile_range (const char **p_ptr, const char *pend, char * being larger than MAX_BUF_SIZE, then flag memory exhausted. */ #define EXTEND_BUFFER() \ do { \ - unsigned char *old_buffer = bufp->buffer; \ + intptr_t b_offs = b - bufp->buffer; \ + intptr_t begalt_offs = b - begalt; \ + intptr_t fixup_alt_jump_offs = b - fixup_alt_jump; \ + intptr_t laststart_offs = b - laststart; \ + intptr_t pending_exact_offs = b - pending_exact; \ if (bufp->allocated == MAX_BUF_SIZE) \ return REG_ESIZE; \ bufp->allocated <<= 1; \ @@ -971,19 +974,18 @@ static reg_errcode_t compile_range (const char **p_ptr, const char *pend, char * bufp->allocated = MAX_BUF_SIZE; \ bufp->buffer = (unsigned char *) realloc (bufp->buffer, bufp->allocated);\ if (bufp->buffer == NULL) \ - return REG_ESPACE; \ - /* If the buffer moved, move all the pointers into it. */ \ - if (old_buffer != bufp->buffer) \ { \ - b = (b - old_buffer) + bufp->buffer; \ - begalt = (begalt - old_buffer) + bufp->buffer; \ - if (fixup_alt_jump) \ - fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\ - if (laststart) \ - laststart = (laststart - old_buffer) + bufp->buffer; \ - if (pending_exact) \ - pending_exact = (pending_exact - old_buffer) + bufp->buffer; \ + laststart = fixup_alt_jump = 0; \ + return REG_ESPACE; \ } \ + b = b_offs + bufp->buffer; \ + begalt = b - begalt_offs; \ + if (fixup_alt_jump) \ + fixup_alt_jump = b - fixup_alt_jump_offs; \ + if (laststart) \ + laststart = b - laststart_offs; \ + if (pending_exact) \ + pending_exact = b - pending_exact_offs; \ } while (0) @@ -1069,13 +1071,7 @@ typedef struct compile_stack_elt_t The `fastmap' and `newline_anchor' fields are neither examined nor set. */ -static reg_errcode_t regex_compile (const char *pattern, int size, reg_syntax_t syntax, struct re_pattern_buffer *bufp); -static reg_errcode_t -regex_compile (pattern, size, syntax, bufp) - const char *pattern; - int size; - reg_syntax_t syntax; - struct re_pattern_buffer *bufp; +static reg_errcode_t regex_compile (const char *pattern, int size, reg_syntax_t syntax, struct re_pattern_buffer *bufp) { /* We fetch characters from PATTERN here. Even though PATTERN is `char *' (i.e., signed), we declare these variables as unsigned, so @@ -2078,10 +2074,7 @@ regex_compile (pattern, size, syntax, bufp) /* Store OP at LOC followed by two-byte integer parameter ARG. */ static void -store_op1 (op, loc, arg) - re_opcode_t op; - unsigned char *loc; - int arg; +store_op1 (re_opcode_t op, unsigned char *loc, int arg) { *loc = (unsigned char) op; STORE_NUMBER (loc + 1, arg); @@ -2091,10 +2084,7 @@ store_op1 (op, loc, arg) /* Like `store_op1', but for two two-byte parameters ARG1 and ARG2. */ static void -store_op2 (op, loc, arg1, arg2) - re_opcode_t op; - unsigned char *loc; - int arg1, arg2; +store_op2 (re_opcode_t op, unsigned char *loc, int arg1, int arg2) { *loc = (unsigned char) op; STORE_NUMBER (loc + 1, arg1); @@ -2106,11 +2096,7 @@ store_op2 (op, loc, arg1, arg2) for OP followed by two-byte integer parameter ARG. */ static void -insert_op1 (op, loc, arg, end) - re_opcode_t op; - unsigned char *loc; - int arg; - unsigned char *end; +insert_op1 (re_opcode_t op, unsigned char *loc, int arg, unsigned char *end) { register unsigned char *pfrom = end; register unsigned char *pto = end + 3; @@ -2125,11 +2111,7 @@ insert_op1 (op, loc, arg, end) /* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2. */ static void -insert_op2 (op, loc, arg1, arg2, end) - re_opcode_t op; - unsigned char *loc; - int arg1, arg2; - unsigned char *end; +insert_op2 (re_opcode_t op, unsigned char *loc, int arg1, int arg2, unsigned char *end) { register unsigned char *pfrom = end; register unsigned char *pto = end + 5; @@ -2146,9 +2128,7 @@ insert_op2 (op, loc, arg1, arg2, end) least one character before the ^. */ static boolean -at_begline_loc_p (pattern, p, syntax) - const char *pattern, *p; - reg_syntax_t syntax; +at_begline_loc_p (const char *pattern, const char *p, reg_syntax_t syntax) { const char *prev = p - 2; boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\'; @@ -2165,9 +2145,7 @@ at_begline_loc_p (pattern, p, syntax) at least one character after the $, i.e., `P < PEND'. */ static boolean -at_endline_loc_p (p, pend, syntax) - const char *p, *pend; - int syntax; +at_endline_loc_p (const char *p, const char *pend, int syntax) { const char *next = p; boolean next_backslash = *next == '\\'; @@ -2187,9 +2165,7 @@ at_endline_loc_p (p, pend, syntax) false if it's not. */ static boolean -group_in_compile_stack (compile_stack, regnum) - compile_stack_type compile_stack; - regnum_t regnum; +group_in_compile_stack (compile_stack_type compile_stack, regnum_t regnum) { int this_element; @@ -2215,11 +2191,7 @@ group_in_compile_stack (compile_stack, regnum) `regex_compile' itself. */ static reg_errcode_t -compile_range (p_ptr, pend, translate, syntax, b) - const char **p_ptr, *pend; - char *translate; - reg_syntax_t syntax; - unsigned char *b; +compile_range (const char **p_ptr, const char *pend, char *translate, reg_syntax_t syntax, unsigned char *b) { int this_char; @@ -2547,8 +2519,7 @@ typedef struct Returns 0 if we succeed, -2 if an internal error. */ int -re_compile_fastmap (bufp) - struct re_pattern_buffer *bufp; +re_compile_fastmap (struct re_pattern_buffer *bufp) { int j, k; fail_stack_type fail_stack; @@ -2832,11 +2803,7 @@ re_compile_fastmap (bufp) freeing the old data. */ void -re_set_registers (bufp, regs, num_regs, starts, ends) - struct re_pattern_buffer *bufp; - struct re_registers *regs; - unsigned num_regs; - regoff_t *starts, *ends; +re_set_registers (struct re_pattern_buffer *bufp, struct re_registers *regs, unsigned num_regs, regoff_t *starts, regoff_t *ends) { if (num_regs) { @@ -2849,7 +2816,7 @@ re_set_registers (bufp, regs, num_regs, starts, ends) { bufp->regs_allocated = REGS_UNALLOCATED; regs->num_regs = 0; - regs->start = regs->end = (regoff_t) 0; + regs->start = regs->end = 0; } } @@ -2859,11 +2826,7 @@ re_set_registers (bufp, regs, num_regs, starts, ends) doesn't let you say where to stop matching. */ int -re_search (bufp, string, size, startpos, range, regs) - struct re_pattern_buffer *bufp; - const char *string; - int size, startpos, range; - struct re_registers *regs; +re_search (struct re_pattern_buffer *bufp, const char *string, int size, int startpos, int range, struct re_registers *regs) { return re_search_2 (bufp, NULL, 0, string, size, startpos, range, regs, size); @@ -2892,14 +2855,7 @@ re_search (bufp, string, size, startpos, range, regs) stack overflow). */ int -re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop) - struct re_pattern_buffer *bufp; - const char *string1, *string2; - int size1, size2; - int startpos; - int range; - struct re_registers *regs; - int stop; +re_search_2 (struct re_pattern_buffer *bufp, const char *string1, int size1, const char *string2, int size2, int startpos, int range, struct re_registers *regs, int stop) { int val; register char *fastmap = bufp->fastmap; @@ -3151,12 +3107,8 @@ typedef union register_info_type /* re_match is like re_match_2 except it takes only a single string. */ int -re_match (bufp, string, size, pos, regs) - struct re_pattern_buffer *bufp; - const char *string; - int size, pos; - struct re_registers *regs; - { +re_match (struct re_pattern_buffer *bufp, const char *string, int size, int pos, struct re_registers *regs) +{ return re_match_2 (bufp, NULL, 0, string, size, pos, regs, size); } #endif /* not emacs */ @@ -3176,13 +3128,7 @@ re_match (bufp, string, size, pos, regs) matched substring. */ int -re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) - struct re_pattern_buffer *bufp; - const char *string1, *string2; - int size1, size2; - int pos; - struct re_registers *regs; - int stop; +re_match_2 (struct re_pattern_buffer *bufp, const char *string1, int size1, const char *string2, int size2, int pos, struct re_registers *regs, int stop) { /* General temporaries. */ int mcnt; @@ -4376,9 +4322,10 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) We don't handle duplicates properly (yet). */ static boolean -group_match_null_string_p (p, end, reg_info) - unsigned char **p, *end; - register_info_type *reg_info; +group_match_null_string_p ( + unsigned char **p, + unsigned char *end, + register_info_type *reg_info) { int mcnt; /* Point to after the args to the start_memory. */ @@ -4485,9 +4432,10 @@ group_match_null_string_p (p, end, reg_info) byte past the last. The alternative can contain groups. */ static boolean -alt_match_null_string_p (p, end, reg_info) - unsigned char *p, *end; - register_info_type *reg_info; +alt_match_null_string_p ( + unsigned char *p, + unsigned char *end, + register_info_type *reg_info) { int mcnt; unsigned char *p1 = p; @@ -4522,9 +4470,10 @@ alt_match_null_string_p (p, end, reg_info) Sets P to one after the op and its arguments, if any. */ static boolean -common_op_match_null_string_p (p, end, reg_info) - unsigned char **p, *end; - register_info_type *reg_info; +common_op_match_null_string_p ( + unsigned char **p, + unsigned char *end, + register_info_type *reg_info) { int mcnt; boolean ret; @@ -4610,10 +4559,11 @@ common_op_match_null_string_p (p, end, reg_info) bytes; nonzero otherwise. */ static int -bcmp_translate (s1, s2, len, translate) - const unsigned char *s1, *s2; - register int len; - char *translate; +bcmp_translate ( + const unsigned char *s1, + const unsigned char *s2, + register int len, + char *translate) { register const unsigned char *p1 = s1, *p2 = s2; while (len) @@ -4636,10 +4586,10 @@ bcmp_translate (s1, s2, len, translate) We call regex_compile to do the actual compilation. */ const char * -re_compile_pattern (pattern, length, bufp) - const char *pattern; - int length; - struct re_pattern_buffer *bufp; +re_compile_pattern ( + const char *pattern, + int length, + struct re_pattern_buffer *bufp) { reg_errcode_t ret; @@ -4755,10 +4705,7 @@ re_exec (s) the return codes and their meanings.) */ int -regcomp (preg, pattern, cflags) - regex_t *preg; - const char *pattern; - int cflags; +regcomp (regex_t *preg, const char *pattern, int cflags) { reg_errcode_t ret; unsigned syntax @@ -4830,12 +4777,13 @@ regcomp (preg, pattern, cflags) We return 0 if we find a match and REG_NOMATCH if not. */ int -regexec (preg, string, nmatch, pmatch, eflags) - const regex_t *preg; - const char *string; - size_t nmatch; - regmatch_t pmatch[]; - int eflags; +regexec ( + const regex_t *preg, + const char *string, + size_t nmatch, + regmatch_t pmatch[], + int eflags + ) { int ret; struct re_registers regs; @@ -4934,8 +4882,7 @@ regerror (int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size) { /* Free dynamically allocated space used by PREG. */ void -regfree (preg) - regex_t *preg; +regfree (regex_t *preg) { if (preg->buffer != NULL) free (preg->buffer); diff --git a/libs/image/Makefile.am b/libs/image/Makefile.am deleted file mode 100644 index 344bd7dd7..000000000 --- a/libs/image/Makefile.am +++ /dev/null @@ -1,18 +0,0 @@ -AUTOMAKE_OPTIONS= foreign -AM_CFLAGS= @PREFER_PIC@ $(Z_CFLAGS) $(PNG_CFLAGS) -AM_CPPFLAGS= -I$(top_srcdir)/include - -lib_ldflags=-version-info $(QUAKE_LIBRARY_VERSION_INFO) \ - -rpath $(libdir) -no-undefined - -image_deps=$(top_builddir)/libs/util/libQFutil.la - -lib_LTLIBRARIES= libQFimage.la - -libQFimage_la_LDFLAGS= $(lib_ldflags) -libQFimage_la_LIBADD= $(image_deps) $(PNG_LIBS) -libQFimage_la_DEPENDENCIES= $(pmage_deps) -libQFimage_la_SOURCES= \ - image.c pcx.c png.c tga.c - -EXTRA_DIST= diff --git a/libs/image/Makemodule.am b/libs/image/Makemodule.am new file mode 100644 index 000000000..0e402b796 --- /dev/null +++ b/libs/image/Makemodule.am @@ -0,0 +1,13 @@ +image_deps=libs/util/libQFutil.la + +lib_LTLIBRARIES += libs/image/libQFimage.la + +libs_image_libQFimage_la_LDFLAGS= $(lib_ldflags) +libs_image_libQFimage_la_LIBADD= $(image_deps) $(PNG_LIBS) +libs_image_libQFimage_la_DEPENDENCIES= $(image_deps) +libs_image_libQFimage_la_SOURCES= \ + libs/image/convert.c \ + libs/image/image.c \ + libs/image/pcx.c \ + libs/image/png.c \ + libs/image/tga.c diff --git a/libs/image/convert.c b/libs/image/convert.c new file mode 100644 index 000000000..54133a9ed --- /dev/null +++ b/libs/image/convert.c @@ -0,0 +1,184 @@ +/* + convert.c + + Image/color conversion routins (RGB to paletted) + + Copyright (C) 2013 Bill Currie + + Author: Bill Currie + Date: 2013/5/10 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/alloc.h" +#include "QF/hash.h" +#include "QF/image.h" +#include "QF/mathlib.h" + +struct colcache_s { + struct colcache_s *next; + hashtab_t *tab; +}; + +typedef struct colcache_color_s { + struct colcache_color_s *next; + byte rgb[3]; + byte col; +} colcache_color_t; + +ALLOC_STATE (colcache_t, colcache); +ALLOC_STATE (colcache_color_t, colcache_color); + +static colcache_color_t * +colcache_new_color (const byte *rgb, byte ind) +{ + colcache_color_t *col; + ALLOC (256, colcache_color_t, colcache_color, col); + VectorCopy (rgb, col->rgb); + col->col = ind; + return col; +} + +static void +colcache_free_color (void *_col, void *unused) +{ + colcache_color_t *col = (colcache_color_t *) _col; + FREE (colcache_color, col); +} + +static uintptr_t +colcache_get_hash (const void *_col, void *unused) +{ + colcache_color_t *col = (colcache_color_t *) _col; + uintptr_t r, g, b; + r = col->rgb[0]; + g = col->rgb[1]; + b = col->rgb[2]; + return (r << 8) ^ (g << 4) ^ b; +} + +static int +colcache_compare (const void *_cola, const void *_colb, void *unused) +{ + colcache_color_t *cola = (colcache_color_t *) _cola; + colcache_color_t *colb = (colcache_color_t *) _colb; + + return VectorCompare (cola->rgb, colb->rgb); +} + +colcache_t * +ColorCache_New (void) +{ + colcache_t *cache; + + ALLOC (16, colcache_t, colcache, cache); + cache->tab = Hash_NewTable (1023, 0, colcache_free_color, 0, 0); + Hash_SetHashCompare (cache->tab, colcache_get_hash, colcache_compare); + return cache; +} + +void +ColorCache_Delete (colcache_t *cache) +{ + Hash_DelTable (cache->tab); + FREE (colcache, cache); +} + +void +ColorCache_Shutdown (void) +{ + ALLOC_FREE_BLOCKS (colcache); + ALLOC_FREE_BLOCKS (colcache_color); +} + +byte +ConvertColor (const byte *rgb, const byte *pal, colcache_t *cache) +{ + //FIXME slow! + int dist[3]; + int d, bestd = 256 * 256 * 3, bestc = -1; + int i; + colcache_color_t *col; + + if (cache) { + colcache_color_t search; + VectorCopy (rgb, search.rgb); + col = Hash_FindElement (cache->tab, &search); + if (col) + return col->col; + } + + for (i = 0; i < 256; i++) { + VectorSubtract (pal + i * 3, rgb, dist); + d = DotProduct (dist, dist); + if (d < bestd) { + bestd = d; + bestc = i; + } + } + if (cache) { + col = colcache_new_color (rgb, bestc); + Hash_AddElement (cache->tab, col); + } + return bestc; +} + +tex_t * +ConvertImage (const tex_t *tex, const byte *pal) +{ + tex_t *new; + int pixels; + int bpp = 3; + int i; + colcache_t *cache; + + pixels = tex->width * tex->height; + new = malloc (sizeof (tex_t) + pixels); + new->data = (byte *) (new + 1); + new->width = tex->width; + new->height = tex->height; + new->format = tex_palette; + new->palette = pal; + switch (tex->format) { + case tex_palette: + case tex_l: // will not work as expected FIXME + case tex_a: // will not work as expected FIXME + case tex_frgba: // will not work as expected FIXME + memcpy (new->data, tex->data, pixels); + break; + case tex_la: // will not work as expected FIXME + for (i = 0; i < pixels; i++) + new->data[i] = tex->data[i * 2]; + break; + case tex_rgba: + bpp = 4; + case tex_rgb: + cache = ColorCache_New (); + for (i = 0; i < pixels; i++) + new->data[i] = ConvertColor (tex->data + i * bpp, pal, cache); + ColorCache_Delete (cache); + break; + } + return new; +} diff --git a/libs/image/image.c b/libs/image/image.c index 7df5d9bb2..f619727f5 100644 --- a/libs/image/image.c +++ b/libs/image/image.c @@ -43,7 +43,7 @@ #include "QF/tga.h" VISIBLE tex_t * -LoadImage (const char *imageFile) +LoadImage (const char *imageFile, int load) { int tmp; dstring_t *tmpFile; @@ -64,7 +64,7 @@ LoadImage (const char *imageFile) dstring_replace (tmpFile, tmp, tmpFile->size, ".png", 5); fp = QFS_FOpenFile (tmpFile->str); if (fp) { - tex = LoadPNG (fp); + tex = LoadPNG (fp, load); Qclose (fp); dstring_delete (tmpFile); return (tex); @@ -74,7 +74,7 @@ LoadImage (const char *imageFile) dstring_replace (tmpFile, tmp, tmpFile->size, ".tga", 5); fp = QFS_FOpenFile (tmpFile->str); if (fp) { - tex = LoadTGA (fp); + tex = LoadTGA (fp, load); Qclose (fp); dstring_delete (tmpFile); return (tex); @@ -85,7 +85,7 @@ LoadImage (const char *imageFile) dstring_replace (tmpFile, tmp, tmpFile->size, ".jpg", 5); fp = QFS_FOpenFile (tmpFile->str); if (fp) { - tex = LoadJPG (fp); + tex = LoadJPG (fp, load); Qclose (fp); dstring_delete (tmpFile); return (tex); @@ -96,7 +96,8 @@ LoadImage (const char *imageFile) dstring_replace (tmpFile, tmp, tmpFile->size, ".pcx", 5); fp = QFS_FOpenFile (tmpFile->str); if (fp) { - tex = LoadPCX (fp, 1, NULL); // Convert, some users don't grok paletted + // Convert, some users don't grok paletted + tex = LoadPCX (fp, 1, NULL, load); Qclose (fp); dstring_delete (tmpFile); return (tex); @@ -105,3 +106,31 @@ LoadImage (const char *imageFile) dstring_delete (tmpFile); return (tex); } + +size_t +ImageSize (const tex_t *tex, int incl_struct) +{ + size_t w =tex->width; + size_t h =tex->height; + size_t bpp = 1; + switch (tex->format) { + case tex_palette: + case tex_rgb: + bpp = 3; + break; + case tex_l: + case tex_a: + bpp = 1; + break; + case tex_la: + bpp = 2; + break; + case tex_rgba: + bpp = 4; + break; + case tex_frgba: + bpp = 16; + break; + } + return bpp * w * h + (incl_struct ? sizeof (tex_t) : 0); +} diff --git a/libs/image/pcx.c b/libs/image/pcx.c index 9ca47bc34..031d8a1a0 100644 --- a/libs/image/pcx.c +++ b/libs/image/pcx.c @@ -47,10 +47,10 @@ VISIBLE tex_t * -LoadPCX (QFile *f, qboolean convert, byte *pal) +LoadPCX (QFile *f, bool convert, const byte *pal, int load) { pcx_t *pcx; - int pcx_mark; + size_t pcx_mark; byte *palette; byte *end; byte *pix; @@ -58,11 +58,14 @@ LoadPCX (QFile *f, qboolean convert, byte *pal) int runLength = 1; int count; tex_t *tex; - int fsize = Qfilesize(f); + int fsize = sizeof (pcx_t); + if (load) { + fsize = Qfilesize(f); + } // parse the PCX file - pcx_mark = Hunk_LowMark (); - pcx = Hunk_AllocName (fsize, "PCX"); + pcx_mark = Hunk_LowMark (0); + pcx = Hunk_AllocName (0, fsize, "PCX"); Qread (f, pcx, fsize); pcx->xmax = LittleShort (pcx->xmax); @@ -79,20 +82,24 @@ LoadPCX (QFile *f, qboolean convert, byte *pal) || pcx->encoding != 1 || pcx->bits_per_pixel != 8) { Sys_Printf ("Bad pcx file: %x %d %d %d\n", - pcx->manufacturer, pcx->version, pcx->encoding, pcx->bits_per_pixel); + pcx->manufacturer, pcx->version, pcx->encoding, + pcx->bits_per_pixel); + Hunk_FreeToLowMark (0, pcx_mark); return 0; } end = palette = ((byte *) pcx) + fsize - 768; dataByte = (byte *) &pcx[1]; - count = (pcx->xmax + 1) * (pcx->ymax + 1); + count = load ? (pcx->xmax + 1) * (pcx->ymax + 1) : 0; if (convert) { - tex = Hunk_TempAlloc (field_offset (tex_t, data[count * 3])); + tex = Hunk_TempAlloc (0, sizeof (tex_t) + count * 3); + tex->data = (byte *) (tex + 1); tex->format = tex_rgb; tex->palette = 0; } else { - tex = Hunk_TempAlloc (field_offset (tex_t, data[count])); + tex = Hunk_TempAlloc (0, sizeof (tex_t) + count); + tex->data = (byte *) (tex + 1); tex->format = tex_palette; if (pal) tex->palette = pal; @@ -101,6 +108,11 @@ LoadPCX (QFile *f, qboolean convert, byte *pal) } tex->width = pcx->xmax + 1; tex->height = pcx->ymax + 1; + tex->loaded = load; + if (!load) { + Hunk_FreeToLowMark (0, pcx_mark); + return tex; + } pix = tex->data; while (count) { @@ -132,7 +144,7 @@ LoadPCX (QFile *f, qboolean convert, byte *pal) } dataByte++; } - Hunk_FreeToLowMark (pcx_mark); + Hunk_FreeToLowMark (0, pcx_mark); if (count || runLength) { Sys_Printf ("PCX was malformed. You should delete it.\n"); return 0; @@ -141,15 +153,16 @@ LoadPCX (QFile *f, qboolean convert, byte *pal) } VISIBLE pcx_t * -EncodePCX (byte * data, int width, int height, - int rowbytes, byte * palette, qboolean flip, int *length) +EncodePCX (const byte *data, int width, int height, + int rowbytes, const byte *palette, bool flip, int *length) { - int i, run, pix, size; - pcx_t *pcx; - byte *pack, *dataend; + int i, run, pix, size; + pcx_t *pcx; + byte *pack; + const byte *dataend; size = width * height * 2 + 1000; - if (!(pcx = Hunk_TempAlloc (size))) { + if (!(pcx = Hunk_TempAlloc (0, size))) { Sys_Printf ("EncodePCX: not enough memory\n"); return 0; } diff --git a/libs/image/png.c b/libs/image/png.c index 62d9977df..83a443472 100644 --- a/libs/image/png.c +++ b/libs/image/png.c @@ -48,6 +48,7 @@ #include "QF/zone.h" #include "compat.h" +#include "qfalloca.h" /* Qread wrapper for libpng */ @@ -115,7 +116,7 @@ readpng_init (QFile *infile, png_structp *png_ptr, png_infop *info_ptr) /* Load the png file and return a texture */ VISIBLE tex_t * -LoadPNG (QFile *infile) +LoadPNG (QFile *infile, int load) { double gamma; png_structp png_ptr = NULL; @@ -131,37 +132,43 @@ LoadPNG (QFile *infile) png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); - if (color_type == PNG_COLOR_TYPE_PALETTE) - png_set_expand (png_ptr); + if (load) { + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_expand (png_ptr); - if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) - png_set_expand (png_ptr); + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand (png_ptr); - if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) - png_set_expand (png_ptr); + if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_expand (png_ptr); - if (bit_depth == 16) - png_set_strip_16 (png_ptr); + if (bit_depth == 16) + png_set_strip_16 (png_ptr); - if (color_type == PNG_COLOR_TYPE_GRAY - || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - png_set_gray_to_rgb (png_ptr); + if (color_type == PNG_COLOR_TYPE_GRAY + || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb (png_ptr); - /* NOTE: gamma support? */ - /* unlike the example in the libpng documentation, we have *no* idea where - * this file may have come from--so if it doesn't have a file gamma, don't - * do any correction ("do no harm") - */ - if (png_get_gAMA(png_ptr, info_ptr, &gamma)) - png_set_gamma (png_ptr, 1.0, gamma); + /* NOTE: gamma support? */ + /* unlike the example in the libpng documentation, we have *no* idea + * wherethis file may have come from--so if it doesn't have a file + * gamma, don't do any correction ("do no harm") + */ + if (png_get_gAMA(png_ptr, info_ptr, &gamma)) + png_set_gamma (png_ptr, 1.0, gamma); - /* All transformations have been registered, now update the info_ptr - * structure */ - png_read_update_info (png_ptr, info_ptr); + /* All transformations have been registered, now update the info_ptr + * structure */ + png_read_update_info (png_ptr, info_ptr); - /* Allocate tex_t structure */ - rowbytes = png_get_rowbytes(png_ptr, info_ptr); - tex = Hunk_TempAlloc (field_offset (tex_t, data[height * rowbytes])); + /* Allocate tex_t structure */ + rowbytes = png_get_rowbytes(png_ptr, info_ptr); + tex = Hunk_TempAlloc (0, sizeof (tex_t) + height * rowbytes); + tex->data = (byte *) (tex + 1); + } else { + tex = Hunk_TempAlloc (0, sizeof (tex_t)); + tex->data = 0; + } tex->width = width; tex->height = height; @@ -170,6 +177,12 @@ LoadPNG (QFile *infile) else tex->format = tex_rgb; tex->palette = NULL; + tex->loaded = load; + + if (!load) { + png_read_end (png_ptr, NULL); + return tex; + } if ((row_pointers = (png_bytepp) malloc (height * sizeof (png_bytep))) == NULL) { @@ -193,8 +206,8 @@ LoadPNG (QFile *infile) #define WRITEPNG_BIT_DEPTH 8 -static int -write_png (QFile *outfile, const byte *data, int width, int height) +VISIBLE int +WritePNG (QFile *outfile, const tex_t *tex) { int i; png_structp png_ptr; @@ -228,27 +241,32 @@ write_png (QFile *outfile, const byte *data, int width, int height) return 0; } - png_set_IHDR (png_ptr, info_ptr, width, height, WRITEPNG_BIT_DEPTH, + png_set_IHDR (png_ptr, info_ptr, tex->width, tex->height, + WRITEPNG_BIT_DEPTH, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); /* NOTE: Write gamma support? */ /* png_set_gAMA (png_ptr, info_ptr, gamma); */ - png_set_bgr(png_ptr); + if (tex->bgr) { + png_set_bgr(png_ptr); + } png_write_info (png_ptr, info_ptr); /* Setup row pointers */ - row_pointers = (png_bytepp)malloc(height * sizeof(png_bytep)); - if (row_pointers == NULL) { - png_destroy_write_struct (&png_ptr, &info_ptr); - return 0; /* Out of memory */ - } + row_pointers = (png_bytepp)alloca(tex->height * sizeof(png_bytep)); - for (i = 0; i < height; i++) { - //FIXME stupid png types :P - row_pointers[height - i - 1] = (byte *) data + (i * width * 3); + int rowbytes = tex->width * 3; + if (tex->flipped) { + for (i = 0; i < tex->height; i++) { + row_pointers[tex->height - i - 1] = tex->data + (i * rowbytes); + } + } else { + for (i = 0; i < tex->height; i++) { + row_pointers[i] = tex->data + (i * rowbytes); + } } @@ -270,55 +288,21 @@ write_png (QFile *outfile, const byte *data, int width, int height) return 1; } -VISIBLE void -WritePNG (const char *fileName, const byte *data, int width, int height) -{ - QFile *outfile; - - outfile = Qopen (fileName, "wb"); - if (!outfile) { - Sys_Printf ("Couldn't open %s\n", fileName); - return; /* Can't open file */ - } - if (!write_png (outfile, data, width, height)) - Qremove (fileName); - Qclose (outfile); -} - -VISIBLE void -WritePNGqfs (const char *fileName, const byte *data, int width, int height) -{ - QFile *outfile; - - outfile = QFS_Open (fileName, "wb"); - if (!outfile) { - Sys_Printf ("Couldn't open %s\n", fileName); - return; /* Can't open file */ - } - if (!write_png (outfile, data, width, height)) - QFS_Remove (fileName); - Qclose (outfile); -} - #else #include "QF/image.h" #include "QF/png.h" VISIBLE tex_t * -LoadPNG (QFile *infile) +LoadPNG (QFile *infile, int load) { return 0; } -VISIBLE void -WritePNG (const char *fileName, const byte *data, int width, int height) -{ -} - -VISIBLE void -WritePNGqfs (const char *fileName, const byte *data, int width, int height) +VISIBLE int +WritePNG (QFile *outfile, const tex_t *tex) { + return 0; } #endif diff --git a/libs/image/tga.c b/libs/image/tga.c index 323a05d04..03368ce6f 100644 --- a/libs/image/tga.c +++ b/libs/image/tga.c @@ -232,7 +232,7 @@ skip_colormap (TargaHeader *targa, byte *data) int bpe; if (!targa->colormap_type) return data; - Sys_MaskPrintf (SYS_DEV, "LoadTGA: skipping colormap\n"); + Sys_MaskPrintf (SYS_dev, "LoadTGA: skipping colormap\n"); bpe = (targa->pixel_size +7) / 8; return data + bpe * targa->colormap_length; } @@ -264,7 +264,7 @@ parse_colormap (TargaHeader *targa, byte **dataByte) case 24: case 16: case 15: - cmap = Hunk_AllocName (256 * sizeof (cmap_t), "TGA cmap"); + cmap = Hunk_AllocName (0, 256 * sizeof (cmap_t), "TGA cmap"); break; default: Sys_Error ("LoadTGA: unsupported color map size"); @@ -605,32 +605,42 @@ decode_greyscale_rle (TargaHeader *targa, tex_t *tex, byte *dataByte) typedef void (*decoder_t) (TargaHeader *, tex_t *, byte *); static decoder_t decoder_functions[] = { - 0, // 0 invalid - decode_colormap, - decode_truecolor, - decode_greyscale, - 0, 0, 0, 0, // 5-7 invalid - 0, // 8 invalid - decode_colormap_rle, - decode_truecolor_rle, - decode_greyscale_rle, - 0, 0, 0, 0, // 12-15 invalid + [targa_colormap] = decode_colormap, + [targa_truecolor] = decode_truecolor, + [targa_greyscale] = decode_greyscale, + [targa_colormap_rle] = decode_colormap_rle, + [targa_truecolor_rle] = decode_truecolor_rle, + [targa_greyscale_rle] = decode_greyscale_rle, + [targa_max_image_type] = 0 }; #define NUM_DECODERS (sizeof (decoder_functions) \ / sizeof (decoder_functions[0])) +static QFFormat targa_formats[] = { + [targa_colormap] = tex_palette, + [targa_truecolor] = tex_rgba, + [targa_greyscale] = tex_l, + [targa_colormap_rle] = tex_palette, + [targa_truecolor_rle] = tex_rgba, + [targa_greyscale_rle] = tex_l, + [targa_max_image_type] = 0 +}; struct tex_s * -LoadTGA (QFile *fin) +LoadTGA (QFile *fin, int load) { byte *dataByte; decoder_t decode; - int fsize = Qfilesize (fin); - int numPixels, targa_mark; + int fsize = sizeof (TargaHeader); + int numPixels; + size_t targa_mark; TargaHeader *targa; tex_t *tex; - targa_mark = Hunk_LowMark (); - targa = Hunk_AllocName (fsize, "TGA"); + if (load) { + fsize = Qfilesize (fin); + } + targa_mark = Hunk_LowMark (0); + targa = Hunk_AllocName (0, fsize, "TGA"); Qread (fin, targa, fsize); targa->colormap_index = LittleShort (targa->colormap_index); @@ -641,22 +651,38 @@ LoadTGA (QFile *fin) targa->height = LittleShort (targa->height); if (targa->image_type >= NUM_DECODERS - || !(decode = decoder_functions[targa->image_type])) - Sys_Error ("LoadTGA: Unsupported targa type"); + || !(decode = decoder_functions[targa->image_type])) { + Sys_Printf ("LoadTGA: Unsupported targa type"); + Hunk_FreeToLowMark (0, targa_mark); + return 0; + } - numPixels = targa->width * targa->height; - tex = Hunk_TempAlloc (field_offset (tex_t, data[numPixels * 4])); + if (load) { + numPixels = targa->width * targa->height; + } else { + numPixels = 0; + } + tex = Hunk_TempAlloc (0, sizeof (tex_t) + numPixels * 4); + tex->data = (byte *) (tex + 1); tex->width = targa->width; tex->height = targa->height; tex->palette = 0; + tex->loaded = load; - // skip TARGA image comment - dataByte = (byte *) (targa + 1); - dataByte += targa->id_length; + if (load) { + // skip TARGA image comment + dataByte = (byte *) (targa + 1); + dataByte += targa->id_length; - decode (targa, tex, dataByte); + decode (targa, tex, dataByte); + } else { + //FIXME + // assume the format is valid so we can return a format type without + // having to check individial image type specific data + tex->format = targa_formats[targa->image_type]; + } - Hunk_FreeToLowMark (targa_mark); + Hunk_FreeToLowMark (0, targa_mark); return tex; } diff --git a/libs/input/Makemodule.am b/libs/input/Makemodule.am new file mode 100644 index 000000000..79d0f851e --- /dev/null +++ b/libs/input/Makemodule.am @@ -0,0 +1,29 @@ +lib_LTLIBRARIES += libs/input/libQFinput.la +noinst_LTLIBRARIES += @input_libs@ + +input_deps = @input_libs@ + +in_evdev_src= \ + libs/input/evdev/hotplug.c \ + libs/input/evdev/inputlib.c \ + libs/input/in_evdev.c + +libs_input_libinput_evdev_la_LIBADD= +libs_input_libinput_evdev_la_DEPENDENCIES= +libs_input_libinput_evdev_la_CFLAGS= +libs_input_libinput_evdev_la_SOURCES= $(in_evdev_src) + +libs_input_libQFinput_la_LDFLAGS= $(lib_ldflags) +libs_input_libQFinput_la_LIBADD= $(input_deps) +libs_input_libQFinput_la_DEPENDENCIES= $(input_deps) +libs_input_libQFinput_la_SOURCES= \ + libs/input/in_axis.c \ + libs/input/in_binding.c \ + libs/input/in_button.c \ + libs/input/in_common.c \ + libs/input/in_event.c \ + libs/input/in_imt.c \ + libs/input/keys.c + +EXTRA_LTLIBRARIES += \ + libs/input/libinput_evdev.la diff --git a/libs/input/evdev/hotplug.c b/libs/input/evdev/hotplug.c new file mode 100644 index 000000000..afaff2715 --- /dev/null +++ b/libs/input/evdev/hotplug.c @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "evdev/hotplug.h" + +static int inotify_fd; +static int devinput_wd; +static char *devinput_path; +static void (*device_deleted) (const char *name); +static void (*device_created) (const char *name); + +static unsigned +get_queue_size (int fd) +{ + unsigned queue_len; + int ret = ioctl (fd, FIONREAD, &queue_len); + if (ret < 0) { + perror ("ioctl"); + return 0; + } + return queue_len; +} + +static void +parse_inotify_events (int fd) +{ + ssize_t len, i; + ssize_t queue_len = get_queue_size (fd); + char *buf = alloca (queue_len); + struct inotify_event *event; + + len = read (fd, buf, queue_len); + for (i = 0; i < len; i += sizeof (struct inotify_event) + event->len) { + event = (struct inotify_event *) &buf[i]; + //printf ("%-3d %08x %5u %4d %s\n", event->wd, event->mask, + // event->cookie, event->len, + // event->len ? event->name : ""); + if ((event->mask & IN_DELETE) && event->len) { + if (strncmp (event->name, "event", 5) == 0) { + // interested in only evdev devices + //printf("deleted device %s\n", event->name); + device_deleted (event->name); + } + } + if (((event->mask & IN_ATTRIB) || (event->mask & IN_CREATE)) + && event->len) { + // done this way because may not have read permission when the + // device is created, so try again when (presumabely) permission is + // granted + if (strncmp (event->name, "event", 5) == 0) { + // interested in only evdev devices + //printf("created device %s\n", event->name); + device_created (event->name); + } + } + } +} + +int +inputlib_hotplug_init(const char *path, + void (*created) (const char*), + void (*deleted) (const char *)) +{ + inotify_fd = inotify_init (); + if (inotify_fd == -1) { + perror ("inotify_init"); + return -1; + } + devinput_wd = inotify_add_watch (inotify_fd, path, + IN_CREATE | IN_DELETE | IN_ATTRIB + | IN_ONLYDIR); + if (devinput_wd == -1) { + perror ("inotify_add_watch"); + close (inotify_fd); + return -1; + } + devinput_path = strdup (path); + device_created = created; + device_deleted = deleted; + //printf ("inputlib_hotplug_init: %s %d %d\n", path, inotify_fd, + // devinput_wd); + return 0; +} + +void +inputlib_hotplug_close (void) +{ + if (inotify_fd != -1) { + close (inotify_fd); + free (devinput_path); + device_created = 0; + device_deleted = 0; + } +} + +int +inputlib_hotplug_add_select (fd_set *fdset, int *maxfd) +{ + if (inotify_fd != -1) { + FD_SET (inotify_fd, fdset); + if (inotify_fd > *maxfd) { + *maxfd = inotify_fd; + } + } + return inotify_fd; +} + +int +inputlib_hotplug_check_select (fd_set *fdset) +{ + if (inotify_fd != -1) { + if (FD_ISSET (inotify_fd, fdset)) { + parse_inotify_events (inotify_fd); + return 1; + } + } + return 0; +} diff --git a/libs/input/evdev/inputlib.c b/libs/input/evdev/inputlib.c new file mode 100644 index 000000000..1f678b9b2 --- /dev/null +++ b/libs/input/evdev/inputlib.c @@ -0,0 +1,512 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "QF/dstring.h" +#include "QF/sys.h" + +#include "evdev/hotplug.h" +#include "evdev/inputlib.h" + +static const char *devinput_path = "/dev/input"; +static device_t *devices; +void (*device_add) (device_t *); +void (*device_remove) (device_t *); + +static void +setup_buttons (device_t *dev) +{ + int i, j, len; + unsigned char buf[1024]; + button_t *button; + + dev->max_button = -1; + dev->num_buttons = 0; + dev->button_map = 0; + dev->buttons = 0; + len = ioctl (dev->fd, EVIOCGBIT (EV_KEY, sizeof (buf)), buf); + for (i = 0; i < len; i++) { + //Sys_Printf("%c%02x", !(i % 16) ? '\n': !(i % 8) ? '-' : ' ', buf[i]); + for (j = 0; j < 8; j++) { + if (buf[i] & (1 << j)) { + dev->num_buttons++; + dev->max_button = i * 8 + j; + } + } + } + //Sys_Printf("\n"); + dev->button_map = malloc ((dev->max_button + 1) * sizeof (int)); + dev->buttons = malloc (dev->num_buttons * sizeof (button_t)); + for (i = 0, button = dev->buttons; i < len; i++) { + for (j = 0; j < 8; j++) { + int button_ind = i * 8 + j; + if (buf[i] & (1 << j)) { + button->num = button - dev->buttons; + button->evnum = button_ind; + button->state = 0; + dev->button_map[button_ind] = button->num; + + button++; + } else { + if (button_ind <= dev->max_button) { + dev->button_map[button_ind] = -1; + } + } + } + } + len = ioctl (dev->fd, EVIOCGKEY (sizeof (buf)), buf); + for (i = 0; i < dev->num_buttons; i++) { + int key = dev->buttons[i].evnum; + dev->buttons[i].state = !!(buf[key / 8] & (1 << (key % 8))); + } +} + +static int +count_axes (const unsigned char *buf, int len, int *max_axis) +{ + int count = 0; + int i, j; + + for (i = 0; i < len; i++) { + for (j = 0; j < 8; j++) { + if (buf[i] & (1 << j)) { + count++; + *max_axis = i * 8 + j; + } + } + } + return count; +} + +static void +abs_info (device_t *dev, int axis_ind, axis_t *axis) +{ + struct input_absinfo absinfo; + ioctl (dev->fd, EVIOCGABS(axis_ind), &absinfo); + axis->value = absinfo.value; + axis->min = absinfo.minimum; + axis->max = absinfo.maximum; + +} + +static void +rel_info (device_t *dev, int axis_ind, axis_t *axis) +{ + // relative axes are marked by having 0 min/max + axis->value = 0; + axis->min = 0; + axis->max = 0; +} + +static void +map_axes (const unsigned char *buf, int len, device_t *dev, + int max_axis, int *axis_map, axis_t *first_axis, + void (*info)(device_t*, int, axis_t *)) +{ + int i, j; + axis_t *axis; + + for (i = 0, axis = first_axis; i < len; i++) { + for (j = 0; j < 8; j++) { + int axis_ind = i * 8 + j; + if (buf[i] & (1 << j)) { + axis->num = axis - dev->axes; + axis->evnum = axis_ind; + axis_map[axis_ind] = axis->num; + info (dev, axis_ind, axis); + axis++; + } else { + if (axis_ind <= max_axis) { + axis_map[axis_ind] = -1; + } + } + } + } +} + +static void +setup_axes (device_t *dev) +{ + int alen, rlen; + unsigned char abuf[1024]; + unsigned char rbuf[1024]; + + dev->max_abs_axis = -1; + dev->max_rel_axis = -1; + dev->num_axes = 0; + dev->abs_axis_map = 0; + dev->rel_axis_map = 0; + dev->axes = 0; + + alen = ioctl (dev->fd, EVIOCGBIT (EV_ABS, sizeof (abuf)), abuf); + rlen = ioctl (dev->fd, EVIOCGBIT (EV_REL, sizeof (rbuf)), rbuf); + + dev->num_abs_axes = count_axes (abuf, alen, &dev->max_abs_axis); + dev->num_rel_axes = count_axes (rbuf, alen, &dev->max_rel_axis); + + dev->num_axes = dev->num_abs_axes + dev->num_rel_axes; + + dev->abs_axis_map = malloc ((dev->max_abs_axis + 1) * sizeof (int)); + dev->rel_axis_map = malloc ((dev->max_rel_axis + 1) * sizeof (int)); + + dev->axes = malloc (dev->num_axes * sizeof (axis_t)); + map_axes (abuf, alen, dev, dev->max_abs_axis, dev->abs_axis_map, + dev->axes, abs_info); + map_axes (rbuf, rlen, dev, dev->max_rel_axis, dev->rel_axis_map, + dev->axes + dev->num_abs_axes, rel_info); +} + +static void device_created (const char *name); +static void device_deleted (const char *name); + +#define get_string(fd, ioctlid, dstr) \ + ({ \ + int size; \ + while ((size = ioctl (fd, ioctlid (dstr->truesize), dstr->str)) \ + == (int) dstr->truesize) { \ + dstr->size = dstr->truesize + 1024; \ + dstring_adjust (dstr); \ + } \ + dstr->size = size <= 0 ? 1 : size; \ + dstr->str[dstr->size - 1] = 0; \ + dstr->str; \ + }) + +static int +check_device (const char *path) +{ + device_t *dev; + int fd; + + fd = open (path, O_RDWR); + if (fd == -1) + return -1; + + dev = malloc (sizeof (device_t)); + dev->next = devices; + dev->prev = &devices; + if (devices) { + devices->prev = &dev->next; + } + devices = dev; + + dev->path = strdup (path); + dev->fd = fd; + + dstring_t *buff = dstring_newstr (); + dev->name = strdup (get_string (fd, EVIOCGNAME, buff)); + dev->phys = strdup (get_string (fd, EVIOCGPHYS, buff)); + dev->uniq = strdup (get_string (fd, EVIOCGUNIQ, buff)); + dstring_delete (buff); + + setup_buttons(dev); + setup_axes(dev); + + dev->event_count = 0; + + dev->data = 0; + dev->axis_event = 0; + dev->button_event = 0; + + + //Sys_Printf ("%s:\n", path); + //Sys_Printf ("\tname: %s\n", dev->name); + //Sys_Printf ("\tbuttons: %d\n", dev->num_buttons); + //Sys_Printf ("\taxes: %d\n", dev->num_axes); + + if (device_add) { + device_add (dev); + } + + return fd; +} + +/*static const char *event_codes[] = { + "EV_SYN", + "EV_KEY", + "EV_REL", + "EV_ABS", + "EV_MSC", + "EV_SW", + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + "EV_LED", + "EV_SND", + "EV_REP", + "EV_FF", + "EV_PWR", + "EV_FF_STATUS", +};*/ + +static void +read_device_input (device_t *dev) +{ + struct input_event event; + button_t *button; + axis_t *axis; + //int i; + + // zero motion counters for relative axes + //for (i = dev->num_abs_axes; i < dev->num_axes; i++) { + // dev->axes[i].value = 0; + //} + + while (1) { + if (read (dev->fd, &event, sizeof (event)) < 0) { + perror(dev->name); + dev->fd = -1; + return; + } + //const char *ev = event_codes[event.type]; + //Sys_Printf ("%6d(%s) %6d %6x\n", event.type, ev ? ev : "?", event.code, event.value); + switch (event.type) { + case EV_SYN: + dev->event_count++; + return; + case EV_KEY: + button = &dev->buttons[dev->button_map[event.code]]; + button->state = event.value; + if (dev->button_event) { + dev->button_event (button, dev->data); + } + break; + case EV_ABS: + axis = &dev->axes[dev->abs_axis_map[event.code]]; + axis->value = event.value; + if (dev->axis_event) { + dev->axis_event (axis, dev->data); + } + break; + case EV_MSC: + break; + case EV_REL: + axis = &dev->axes[dev->rel_axis_map[event.code]]; + //Sys_Printf ("EV_REL %6d %6x %6d %p\n", event.code, event.value, + // dev->rel_axis_map[event.code], axis); + axis->value = event.value; + if (dev->axis_event) { + dev->axis_event (axis, dev->data); + } + break; + case EV_SW: + case EV_LED: + case EV_SND: + case EV_REP: + case EV_FF: + case EV_PWR: + case EV_FF_STATUS: + //Sys_Printf ("%6d %6d %6x\n", event.type, event.code, event.value); + break; + } + } +} + +void +inputlib_add_select (fd_set *fdset, int *maxfd) +{ + inputlib_hotplug_add_select (fdset, maxfd); + + for (device_t *dev = devices; dev; dev = dev->next) { + if (dev->fd < 0) { + continue; + } + FD_SET (dev->fd, fdset); + if (dev->fd > *maxfd) { + *maxfd = dev->fd; + } + } +} + +void +inputlib_check_select (fd_set *fdset) +{ + inputlib_hotplug_check_select (fdset); + + for (device_t *dev = devices; dev; dev = dev->next) { + if (dev->fd < 0) { + continue; + } + if (FD_ISSET (dev->fd, fdset)) { + read_device_input (dev); + } + } +} + +int +inputlib_check_input (void) +{ + fd_set fdset; + struct timeval _timeout; + struct timeval *timeout = &_timeout; + int res; + int maxfd = -1; + + _timeout.tv_sec = 0; + _timeout.tv_usec = 0; + + FD_ZERO (&fdset); + + inputlib_add_select (&fdset, &maxfd); + if (maxfd < 0) { + return 0; + } + res = select (maxfd + 1, &fdset, NULL, NULL, timeout); + if (res <= 0) { + return 0; + } + + inputlib_check_select (&fdset); + return 1; +} + +static void +close_device (device_t *dev) +{ + if (dev->next) { + dev->next->prev = dev->prev; + } + *dev->prev = dev->next; + + if (device_remove) { + device_remove (dev); + } + close (dev->fd); + free (dev->button_map); + if (dev->buttons) { + free (dev->buttons); + } + free (dev->abs_axis_map); + free (dev->rel_axis_map); + if (dev->axes) { + free (dev->axes); + } + free (dev->phys); + free (dev->uniq); + free (dev->name); + free (dev->path); + free (dev); +} + +static char * +make_devname (const char *path, const char *name) +{ + int plen = strlen (path); + int nlen = strlen (name); + char *devname = malloc (plen + nlen + 2); + + strcpy (devname, path); + devname[plen] = '/'; + strcpy (devname + plen + 1, name); + + return devname; +} + +static int +check_input_device (const char *path, const char *name) +{ + int ret; + char *devname = make_devname (path, name); + + //puts (devname); + ret = check_device (devname); + free (devname); + return ret; +} + +static void +device_created (const char *name) +{ + char *devname = make_devname (devinput_path, name); + device_t *dev; + int olddev = 0; + + for (dev = devices; dev; dev = dev->next) { + if (strcmp (dev->path, devname) == 0) { + // already have this device open + olddev = 1; + break; + } + } + if (!olddev && check_device (devname) >= 0) { + //Sys_Printf ("found device %s\n", devname); + } + free (devname); +} + +static void +device_deleted (const char *name) +{ + char *devname = make_devname (devinput_path, name); + device_t **dev; + + for (dev = &devices; *dev; dev = &(*dev)->next) { + if (strcmp ((*dev)->path, devname) == 0) { + //Sys_Printf ("lost device %s\n", (*dev)->path); + close_device (*dev); + break; + } + } + free (devname); +} + +static int +scan_devices (void) +{ + struct dirent *dirent; + DIR *dir; + + dir = opendir (devinput_path); + if (!dir) { + return -1; + } + + while ((dirent = readdir (dir))) { + if (dirent->d_type != DT_CHR) { + continue; + } + if (strncmp (dirent->d_name, "event", 5)) { + continue; + } + if (check_input_device (devinput_path, dirent->d_name) < 0) { + continue; + } + //Sys_Printf("%s\n", dirent->d_name); + } + closedir (dir); + return 0; +} + +int +inputlib_init (void (*dev_add) (device_t *), void (*dev_rem) (device_t *)) +{ + device_add = dev_add; + device_remove = dev_rem; + if (scan_devices () != -1) { + inputlib_hotplug_init (devinput_path, device_created, device_deleted); + return 0; + } + return -1; +} + +void +inputlib_close (void) +{ + inputlib_hotplug_close (); + while (devices) { + close_device (devices); + } +} diff --git a/libs/input/in_axis.c b/libs/input/in_axis.c new file mode 100644 index 000000000..0c250b4ce --- /dev/null +++ b/libs/input/in_axis.c @@ -0,0 +1,119 @@ +/* + in_axis.c + + Logical axis support + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/10/1 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/cmd.h" +#include "QF/hash.h" +#include "QF/input.h" +#include "QF/sys.h" + +static hashtab_t *axis_tab; + +static const char * +axis_get_key (const void *a, void *data) +{ + __auto_type axis = (const in_axis_t *) a; + return axis->name; +} + +static void +axis_free (void *a, void *data) +{ +} + +VISIBLE int +IN_RegisterAxis (in_axis_t *axis) +{ + const char *name = axis->name; + if (Hash_Find (axis_tab, name)) { + return 0; + } + + Hash_Add (axis_tab, axis); + return 1; +} + +VISIBLE in_axis_t * +IN_FindAxis (const char *name) +{ + return Hash_Find (axis_tab, name); +} + +static void +axis_clear_state (void *_a, void *data) +{ + //FIXME what to do here? +} + +void +IN_AxisClearStates (void) +{ + Hash_ForEach (axis_tab, axis_clear_state, 0); +} + +void +IN_AxisAddListener (in_axis_t *axis, axis_listener_t listener, void *data) +{ + if (!axis->listeners) { + axis->listeners = malloc (sizeof (*axis->listeners)); + LISTENER_SET_INIT (axis->listeners, 8); + } + LISTENER_ADD (axis->listeners, listener, data); +} + +void +IN_AxisRemoveListener (in_axis_t *axis, axis_listener_t listener, void *data) +{ + if (axis->listeners) { + LISTENER_REMOVE (axis->listeners, listener, data); + } +} + +static void +in_axis_shutdown (void *data) +{ + Hash_DelTable (axis_tab); +} + +static void __attribute__((constructor)) +in_axis_init (void) +{ + axis_tab = Hash_NewTable (127, axis_get_key, axis_free, 0, 0); + Sys_RegisterShutdown (in_axis_shutdown, 0); +} diff --git a/libs/input/in_binding.c b/libs/input/in_binding.c new file mode 100644 index 000000000..9c8957975 --- /dev/null +++ b/libs/input/in_binding.c @@ -0,0 +1,992 @@ +/* + in_binding.c + + Input binding management + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/11/2 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/cexpr.h" +#include "QF/cmd.h" +#include "QF/cmem.h" +#include "QF/dstring.h" +#include "QF/hash.h" +#include "QF/heapsort.h" +#include "QF/input.h" +#include "QF/plist.h" +#include "QF/progs.h" // for PR_RESMAP +#include "QF/sys.h" +#include "QF/va.h" + +#include "QF/input/imt.h" + +#include "QF/input/binding.h" +#include "QF/input/event.h" +#include "QF/input/imt.h" + +/*** Connect a device and its axes and buttons to logical axes and buttons. + + \a name is used to specify the device when creating bindings, and + identifying the device for hints etc (eg, "press joy1 1 to ..."). + + \a devname is the device name (eg, "6d spacemouse") + + \a id is the device id (eg "usb-0000:00:1d.1-2/input0"). + + The device name is useful for ease of user identification and allowing + the device to be plugged into any USB socket. However, it doesn't allow + multiple devices with the same name (eg, using twin joysticks of the same + model). Thus if \a match_id is true, both the device name and the device + id used to auto-connect the device, but it does require the device to be + plugged into the same uSB path (ie, same socket on the same hub connected + to the same port on the PC) + + \a devid is the actual device associated with the bindings. If -1, the + device is not currently connected. + + \a axis_info holds the device/axis specific range info and the current + raw value of the axis. + + \a button_info holds the current raw state of the button + + \a axis_imt_id is -1 if the device has no axis bindings, otherwise it is + the base index into the imt axis bindings array. + + \a button_imt_id is -1 if the device has no button bindings, otherwise it + is the base index into the imt button bindings array. +*/ +typedef struct in_devbindings_s { + struct in_devbindings_s *next; + char *name; ///< name used when binding inputs + char *devname; ///< physical device name + char *id; ///< physical device id + int match_id; ///< if true, both devname and id must match + int devid; ///< id of device associated with these bindings + int num_axes; + int num_buttons; + in_axisinfo_t *axis_info; ///< axis range info and raw state + in_buttoninfo_t *button_info; ///< button raw state + int axis_imt_id; ///< index into array of imt axis bindings + int button_imt_id; ///< index into array of imt button bindings +} in_devbindings_t; + +typedef struct DARRAY_TYPE (int) in_knowndevset_t; + +static in_knowndevset_t known_devices = DARRAY_STATIC_INIT (8); +static int in_binding_handler; +static int in_keyhelp_handler; +static int in_keyhelp_saved_handler; + +static PR_RESMAP (in_devbindings_t) devbindings; +static in_devbindings_t *devbindings_list; + +static int +devid_cmp (const void *a, const void *b) +{ + return *(const int *)a - *(const int *)b; +} + +static int * __attribute__ ((pure)) +in_find_devid (int devid) +{ + return bsearch (&devid, known_devices.a, known_devices.size, + sizeof (int), devid_cmp); +} + +static in_devbindings_t * __attribute__ ((pure)) +in_binding_find_connection (const char *devname, const char *id) +{ + in_devbindings_t *db; + + //FIXME slow + for (db = devbindings_list; db; db = db->next) { + if (strcmp (devname, db->devname) != 0) { + continue; + } + if (db->match_id && strcmp (id, db->id) != 0) { + continue; + } + return db; + } + return 0; +} + +static void +alloc_input_info (in_devbindings_t *db) +{ + db->axis_info = malloc (db->num_axes * sizeof (in_axisinfo_t) + + db->num_buttons * sizeof (in_buttoninfo_t)); + db->button_info = (in_buttoninfo_t *) &db->axis_info[db->num_axes]; +} + +static void +in_binding_add_device (const IE_event_t *ie_event) +{ + size_t devid = ie_event->device.devid; + const char *devname = IN_GetDeviceName (devid); + const char *id = IN_GetDeviceId (devid); + + if (!in_find_devid (devid)) { + DARRAY_APPEND (&known_devices, devid); + // keep the known devices sorted by id + heapsort (known_devices.a, known_devices.size, sizeof (int), devid_cmp); + Sys_Printf ("Added device %s %s\n", devname, id); + } + + in_devbindings_t *db = IN_GetDeviceEventData (devid); + if (db) { + return; + } + + db = in_binding_find_connection (devname, id); + + if (db) { + if (db->match_id) { + Sys_Printf ("Reconnected %s to %s %s\n", db->name, devname, id); + } else { + Sys_Printf ("Reconnected %s to %s\n", db->name, devname); + } + db->devid = devid; + IN_SetDeviceEventData (devid, db); + if (!db->axis_info) { + alloc_input_info (db); + } + IN_AxisInfo (devid, db->axis_info, &db->num_axes); + IN_ButtonInfo (devid, db->button_info, &db->num_buttons); + } +} + +static void +in_binding_remove_device (const IE_event_t *ie_event) +{ + size_t devid = ie_event->device.devid; + in_devbindings_t *db = IN_GetDeviceEventData (devid); + int *kd; + + if (!(kd = in_find_devid (devid))) { + Sys_Error ("in_binding_remove_device: invalid devid: %zd", devid); + } + DARRAY_REMOVE_AT (&known_devices, kd - known_devices.a); + + const char *devname = IN_GetDeviceName (devid); + const char *id = IN_GetDeviceId (devid); + if (db) { + db->devid = -1; + if (db->match_id) { + Sys_Printf ("Disconnected %s from %s %s\n", db->name, devname, id); + } else { + Sys_Printf ("Disconnected %s from %s\n", db->name, devname); + } + } + Sys_Printf ("Removed device %s %s\n", devname, id); +} + +static void +in_binding_axis (const IE_event_t *ie_event) +{ + int axis = ie_event->axis.axis; + int value = ie_event->axis.value; + in_devbindings_t *db = ie_event->axis.data;; + + if (db && axis < db->num_axes) { + db->axis_info[axis].value = value; + if (db->axis_imt_id >= 0) { + IMT_ProcessAxis (db->axis_imt_id + axis, value); + } + } +} + +static void +in_binding_button (const IE_event_t *ie_event) +{ + int button = ie_event->button.button; + int state = ie_event->button.state; + in_devbindings_t *db = ie_event->button.data; + + if (db && button < db->num_buttons) { + db->button_info[button].state = state; + if (db->button_imt_id >= 0) { + IMT_ProcessButton (db->button_imt_id + button, state); + } + } +} + +static int +in_binding_event_handler (const IE_event_t *ie_event, void *unused) +{ + static void (*handlers[ie_event_count]) (const IE_event_t *ie_event) = { + [ie_add_device] = in_binding_add_device, + [ie_remove_device] = in_binding_remove_device, + }; + if ((unsigned) ie_event->type >= ie_event_count + || !handlers[ie_event->type]) { + return 0; + } + handlers[ie_event->type] (ie_event); + return 1; +} + +int +IN_Binding_HandleEvent (const IE_event_t *ie_event) +{ + static void (*handlers[ie_event_count]) (const IE_event_t *ie_event) = { + [ie_axis] = in_binding_axis, + [ie_button] = in_binding_button, + }; + if ((unsigned) ie_event->type >= ie_event_count + || !handlers[ie_event->type]) { + return 0; + } + handlers[ie_event->type] (ie_event); + return 1; +} + +static int keyhelp_axis_threshold; + +static int +in_keyhelp_event_handler (const IE_event_t *ie_event, void *unused) +{ + if (ie_event->type != ie_axis && ie_event->type != ie_button) { + return 0; + } + + size_t devid = ie_event->button.devid; + in_devbindings_t *db = ie_event->button.data; + const char *bind_name = db ? db->name : 0; + const char *type = 0; + int num = -1; + const char *devname = IN_GetDeviceName (devid); + const char *id = IN_GetDeviceId (devid); + const char *name = 0; + + if (ie_event->type == ie_axis) { + int axis = ie_event->axis.axis; + int value = ie_event->axis.value; + in_axisinfo_t *ai; + if (db) { + ai = &db->axis_info[axis]; + } else { + in_axisinfo_t axis_info; + IN_GetAxisInfo (devid, axis, &axis_info); + ai = &axis_info; + } + if (!ai->min && !ai->max) { + if (abs (value) > keyhelp_axis_threshold) { + num = axis; + type = "axis"; + } + } else { + //FIXME does not work if device has not been connected (db is null) + int diff = abs (value - ai->value); + if (diff * 5 >= ai->max - ai->min) { + num = axis; + type = "axis"; + } + } + name = IN_GetAxisName (devid, num); + } else if (ie_event->type == ie_button) { + if (ie_event->button.state) { + num = ie_event->button.button; + type = "button"; + } + name = IN_GetButtonName (devid, num); + } + if (!type) { + return 0; + } + IE_Set_Focus (in_keyhelp_saved_handler); + Sys_Printf ("%s (%s %s) %s %d (%s)\n", bind_name, devname, id, type, num, + name ? name : ""); + return 1; +} + +static in_devbindings_t * __attribute__ ((pure)) +in_binding_find_device (const char *name) +{ + in_devbindings_t *db; + + for (db = devbindings_list; db; db = db->next) { + if (strcmp (name, db->name) == 0) { + break; + } + } + return db; +} + +static void +clear_connection (in_devbindings_t *db) +{ + if (db->devid >= 0) { + IN_SetDeviceEventData (db->devid, 0); + } + free (db->name); + free (db->devname); + free (db->id); + free (db->axis_info); +} + +static void +in_bind_f (void) +{ + int argc = Cmd_Argc (); + if (argc < 6) { + Sys_Printf ("in_bind imt device type number binding...\n"); + Sys_Printf (" imt: the name of the input mapping table in which the" + " intput will be bound\n"); + Sys_Printf (" device: the nickname or id of the device owning" + " the input to be bound\n"); + Sys_Printf (" type: the type of input to be bound (axis or" + " button)\n"); + // FIXME support names + Sys_Printf (" number: the numeric id of the input to be bound\n"); + Sys_Printf (" binding...: the destination to which the input will" + " be bound\n"); + Sys_Printf (" for axis inputs, this can be an analog input or" + " an axis-button\n"); + Sys_Printf (" for button inputs, this can be a button or a" + " command (spaces ok, but\n" + " quotes recommended)\n"); + return; + } + + const char *imt_name = Cmd_Argv (1); + const char *dev_name = Cmd_Argv (2); + const char *type = Cmd_Argv (3); + const char *number = Cmd_Argv (4); + + imt_t *imt = IMT_FindIMT (imt_name); + in_devbindings_t *dev = in_binding_find_device (dev_name); + char *end; + int num = strtol (number, &end, 0); + if (!imt) { + Sys_Printf ("unknown imt: %s\n", imt_name); + return; + } + if (!dev) { + Sys_Printf ("unknown device: %s\n", dev_name); + return; + } + if (strcmp (type, "axis") != 0 && strcmp (type, "button") != 0) { + Sys_Printf ("invalid input type: %s\n", type); + return; + } + if (*type == 'a') { + if (*end) { + num = IN_GetAxisNumber (dev->devid, number); + } + if (num < 0 || num >= dev->num_axes) { + Sys_Printf ("invalid axis number: %s\n", number); + return; + } + if (dev->axis_imt_id == -1) { + dev->axis_imt_id = IMT_GetAxisBlock (dev->num_axes); + } + const char *axis_name = Cmd_Argv (5); + in_axis_t *axis = IN_FindAxis (axis_name); + if (!axis) { + Sys_Printf ("unknown axis: %s\n", axis_name); + return; + } + in_axisinfo_t *axisinfo = &dev->axis_info[num]; + in_recipe_t recipe = { + .min = axisinfo->min, + .max = axisinfo->max, + .curve = 1, + .scale = 1, + }; + double curve = recipe.curve; + double scale = recipe.scale; + exprsym_t var_syms[] = { + {"minzone", &cexpr_int, &recipe.minzone}, + {"maxzone", &cexpr_int, &recipe.maxzone}, + {"deadzone", &cexpr_int, &recipe.deadzone}, + {"curve", &cexpr_double, &curve}, + {"scale", &cexpr_double, &scale}, + {} + }; + exprtab_t vars_tab = { var_syms, 0 }; + exprctx_t exprctx = { + .symtab = &vars_tab, + .memsuper = new_memsuper (), + .messages = PL_NewArray (), + }; + cexpr_init_symtab (&vars_tab, &exprctx); + + int i; + for (i = 6; i < argc; i++) { + const char *arg = Cmd_Argv (i); + if (cexpr_eval_string (arg, &exprctx)) { + plitem_t *messages = exprctx.messages; + for (int j = 0; j < PL_A_NumObjects (messages); j++) { + Sys_Printf ("%s\n", + PL_String (PL_ObjectAtIndex (messages, j))); + } + break; + } + } + if (i == argc) { + recipe.curve = curve; + recipe.scale = scale; + IMT_BindAxis (imt, dev->axis_imt_id + num, axis, &recipe); + } + Hash_DelTable (vars_tab.tab); + PL_Release (exprctx.messages); + delete_memsuper (exprctx.memsuper); + } else { + // the rest of the command line is the binding + // However, have to put the args into a single string ourselves because + // Cmd_Args includes any quotes used to build compound commands + static dstring_t *binding; + if (!binding) { + binding = dstring_new (); + } + dsprintf (binding, "%s", Cmd_Argv (5)); + for (int i = 6; i < argc; i++) { + dasprintf (binding, " %s", Cmd_Argv (i)); + } + + if (*end) { + num = IN_GetButtonNumber (dev->devid, number); + } + if (num < 0 || num >= dev->num_buttons) { + Sys_Printf ("invalid button number: %s\n", number); + return; + } + if (dev->button_imt_id == -1) { + dev->button_imt_id = IMT_GetButtonBlock (dev->num_buttons); + } + IMT_BindButton (imt, dev->button_imt_id + num, binding->str); + } +} + +static void +in_unbind_f (void) +{ + if (Cmd_Argc () < 5) { + Sys_Printf ("in_unbind imt device type number\n"); + Sys_Printf (" imt: the name of the input mapping table in which the" + " intput will be unbound\n"); + Sys_Printf (" device: the nickname or id of the devise owning" + " the input to be unbound\n"); + Sys_Printf (" type: the type of input to be unbound (axis or" + " button)\n"); + // FIXME support names + Sys_Printf (" number: the numeric id of the input to be unbound\n"); + return; + } + + const char *imt_name = Cmd_Argv (1); + const char *dev_name = Cmd_Argv (2); + const char *type = Cmd_Argv (3); + const char *number = Cmd_Argv (4); + + imt_t *imt = IMT_FindIMT (imt_name); + in_devbindings_t *dev = in_binding_find_device (dev_name); + char *end; + int num = strtol (number, &end, 0); + if (!imt) { + Sys_Printf ("unknown imt: %s\n", imt_name); + return; + } + if (!dev) { + Sys_Printf ("unknown device: %s\n", dev_name); + return; + } + if (strcmp (type, "axis") != 0 && strcmp (type, "button") != 0) { + Sys_Printf ("invalid input type: %s\n", type); + return; + } + if (*type == 'a') { + if (*end || num < 0 || num >= dev->num_axes) { + Sys_Printf ("invalid axis number: %s\n", number); + return; + } + IMT_BindAxis (imt, dev->axis_imt_id + num, 0, 0); + } else { + if (*end || num < 0 || num >= dev->num_buttons) { + Sys_Printf ("invalid button number: %s\n", number); + return; + } + IMT_BindButton (imt, dev->button_imt_id + num, 0); + } +} + +static void +in_clear_f (void) +{ + int argc = Cmd_Argc (); + if (argc < 2) { + Sys_Printf ("in_clear imt [imt...]\n"); + return; + } + for (int i = 1; i < argc; i++) { + const char *imt_name = Cmd_Argv (i); + imt_t *imt = IMT_FindIMT (imt_name); + if (!imt) { + Sys_Printf ("unknown imt: %s\n", imt_name); + continue; + } + for (size_t ind = 0; ind < imt->axis_bindings.size; ind++) { + IMT_BindAxis (imt, ind, 0, 0); + } + for (size_t ind = 0; ind < imt->button_bindings.size; ind++) { + IMT_BindButton (imt, ind, 0); + } + } +} + +static void +in_devices_f (void) +{ + for (size_t i = 0; i < known_devices.size; i++) { + int devid = known_devices.a[i]; + in_devbindings_t *db = IN_GetDeviceEventData (devid); + const char *name = IN_GetDeviceName (devid); + const char *id = IN_GetDeviceId (devid); + int num_axes, num_buttons; + IN_AxisInfo (devid, 0, &num_axes); + IN_ButtonInfo (devid, 0, &num_buttons); + + Sys_Printf ("devid %d:\n", devid); + if (db) { + Sys_Printf (" bind name: %s\n", db->name); + } else { + Sys_Printf (" no bind name\n"); + } + Sys_Printf (" name: %s\n", name); + Sys_Printf (" id: %s\n", id); + Sys_Printf (" axes: %d\n", num_axes); + Sys_Printf (" buttons: %d\n", num_buttons); + } +} + +static void +in_connect_f (void) +{ + int argc = Cmd_Argc (); + const char *fullid = 0; + + if (argc == 4) { + fullid = Cmd_Argv (3); + } + if (argc < 3 || argc > 4 || (fullid && strcmp (fullid, "fullid"))) { + goto in_connect_usage; + } + const char *bindname = Cmd_Argv (1); + const char *device_id = Cmd_Argv (2); + int devid = -1; + + if (in_binding_find_device (bindname)) { + Sys_Printf ("%s already exists\n", bindname); + return; + } + + if (device_id[0] == '#') { + char *end; + devid = strtol (device_id + 1, &end, 0); + if (*end || !in_find_devid (devid)) { + Sys_Printf ("Not a valid device number: %s", device_id); + return; + } + } else { + int len = strlen (device_id); + + for (size_t i = 0; i < known_devices.size; i++) { + if (strcmp (device_id, IN_GetDeviceId (known_devices.a[i])) == 0) { + devid = known_devices.a[i]; + break; + } + if (strncasecmp (device_id, + IN_GetDeviceName (known_devices.a[i]), + len) == 0) { + if (devid > -1) { + Sys_Printf ("'%s' is ambiguous\n", device_id); + return; + } + devid = known_devices.a[i]; + } + } + } + if (devid == -1) { + Sys_Printf ("No such device: %s\n", device_id); + return; + } + if (IN_GetDeviceEventData (devid)) { + Sys_Printf ("%s already connected\n", device_id); + return; + } + + in_devbindings_t *db = PR_RESNEW (devbindings); + db->next = devbindings_list; + devbindings_list = db; + + db->name = strdup (bindname); + db->devname = strdup (IN_GetDeviceName (devid)); + db->id = strdup (IN_GetDeviceId (devid)); + db->match_id = !!fullid; + db->devid = devid; + + IN_AxisInfo (devid, 0, &db->num_axes); + IN_ButtonInfo (devid, 0, &db->num_buttons); + alloc_input_info (db); + IN_AxisInfo (devid, db->axis_info, &db->num_axes); + IN_ButtonInfo (devid, db->button_info, &db->num_buttons); + + db->axis_imt_id = -1; + db->button_imt_id = -1; + + IN_SetDeviceEventData (devid, db); + + return; +in_connect_usage: + Sys_Printf ("in_connect bindname device_id [fullid]\n"); + Sys_Printf (" Create a new device binding connection.\n"); + Sys_Printf (" bindname: Connection name used for binding inputs\n."); + Sys_Printf (" device_id: Specify the device to be connected.\n"); + Sys_Printf (" May be the numeric device number (#N), the device\n"); + Sys_Printf (" name or device id as shown by in_devices.\n"); + Sys_Printf (" fullid: if present, both device name and device id\n"); + Sys_Printf (" will be used when automatically reconnecting the\n"); + Sys_Printf (" device.\n"); +} + +static void +in_connections_f (void) +{ + for (in_devbindings_t *db = devbindings_list; db; db = db->next) { + if (db->match_id) { + Sys_Printf ("%s: %s %s\n", db->name, db->devname, db->id); + } else { + Sys_Printf ("%s: %s\n", db->name, db->devname); + } + if (db->devid > -1) { + Sys_Printf (" connected\n"); + } else { + Sys_Printf (" disconnected\n"); + } + } +} + +static void +keyhelp_f (void) +{ + keyhelp_axis_threshold = 3; + if (Cmd_Argc () > 1) { + char *end; + int threshold = strtol (Cmd_Argv (1), &end, 0); + if (!*end && threshold > 0) { + keyhelp_axis_threshold = threshold; + } + } + in_keyhelp_saved_handler = IE_Get_Focus (); + IE_Set_Focus (in_keyhelp_handler); + Sys_Printf ("Press button or move axis to identify\n"); +} + +typedef struct { + const char *name; + xcommand_t func; + const char *desc; +} bindcmd_t; + +static bindcmd_t in_binding_commands[] = { + { "in_bind", in_bind_f, + "Assign a command or a set of commands to a key.\n" + "Note: To bind multiple commands to a key, enclose the " + "commands in quotes and separate with semi-colons." + }, + { "in_unbind", in_unbind_f, + "Remove the bind from the the selected key." + }, + { "in_clear", in_clear_f, + "Remove all binds from the specified imts." + }, + { "in_devices", in_devices_f, + "List the known devices and their status." + }, + { "in_connect", in_connect_f, + "Create a device binding connection. Supports hot-plug: the " + "device will be automatically reconnected when plugged in or " + PACKAGE_NAME " is restarted." + }, + { "in_connections", in_connections_f, + "List device bindings and statuses." + }, + { "keyhelp", keyhelp_f, + "Identify the next active input axis or button.\n" + "\n" + "The identification includes the device binding name, axis or button " + "number, and (if known) the name of the axis or button. Axes and " + "buttons can always be bound by number, so even those for which a " + "name is not known, but " PACKAGE_NAME " sees, can be bound." + }, + { } +#if 0 + { "bindlist", Key_Bindlist_f, + "list all of the key bindings" + }, + { "unbindall", Key_Unbindall_f, + "Remove all binds (USE CAUTIOUSLY!!!" + }, + { "imt", Key_InputMappingTable_f, + "" + }, + { "imt_keydest", Key_IMT_Keydest_f, + "" + }, + { "in_type", Key_In_Type_f, + "Send the given string as simulated key presses." + }, +#endif +}; + +void +IN_Binding_Activate (void) +{ + IE_Set_Focus (in_binding_handler); +} + +void +IN_Binding_Init (void) +{ + in_binding_handler = IE_Add_Handler (in_binding_event_handler, 0); + in_keyhelp_handler = IE_Add_Handler (in_keyhelp_event_handler, 0); + + for (bindcmd_t *cmd = in_binding_commands; cmd->name; cmd++) { + Cmd_AddCommand (cmd->name, cmd->func, cmd->desc); + } +} + +void +IN_Binding_Shutdown () +{ + for (in_devbindings_t *db = devbindings_list; db; db = db->next) { + clear_connection (db); + } + for (unsigned i = 0; i < devbindings._size; i++) { + free (devbindings._map[i]); + } + free (devbindings._map); + DARRAY_CLEAR (&known_devices); +} + +void +IN_Binding_SaveConfig (plitem_t *config) +{ + plitem_t *devices = PL_NewArray (); + PL_D_AddObject (config, "devices", devices); + for (in_devbindings_t *db = devbindings_list; db; db = db->next) { + plitem_t *db_cfg = PL_NewDictionary (0); + PL_A_AddObject (devices, db_cfg); + PL_D_AddObject (db_cfg, "name", PL_NewString (db->name)); + PL_D_AddObject (db_cfg, "devname", PL_NewString (db->devname)); + if (db->match_id) { + PL_D_AddObject (db_cfg, "id", PL_NewString (db->id)); + } + PL_D_AddObject (db_cfg, "num_axes", + PL_NewString (va (0, "%d", db->num_axes))); + PL_D_AddObject (db_cfg, "num_buttons", + PL_NewString (va (0, "%d", db->num_buttons))); + if (db->axis_imt_id >= 0) { + plitem_t *axes = PL_NewArray (); + PL_D_AddObject (db_cfg, "axes", axes); + for (int i = 0; i < db->num_axes; i++) { + IMT_SaveAxisConfig (axes, db->axis_imt_id + i, i); + } + } + if (db->button_imt_id >= 0) { + plitem_t *buttons = PL_NewArray (); + PL_D_AddObject (db_cfg, "buttons", buttons); + for (int i = 0; i < db->num_buttons; i++) { + IMT_SaveButtonConfig (buttons, db->button_imt_id + i, i); + } + } + } +} + +static int +parse_num (plitem_t *item) +{ + char *end; + const char *str = PL_String (item); + if (!str) { + return -1; + } + int num = strtol (str, &end, 0); + if (*end || num < 0) { + return -1; + } + return num; +} + +static int +parse_int (plitem_t *item, int dflt) +{ + char *end; + const char *str = PL_String (item); + if (!str) { + return dflt; + } + int num = strtol (str, &end, 0); + if (*end) { + return dflt; + } + return num; +} + +static float +parse_float (plitem_t *item, float dflt) +{ + char *end; + const char *str = PL_String (item); + if (!str) { + return dflt; + } + float num = strtof (str, &end); + if (*end) { + return dflt; + } + return num; +} + +void +IN_Binding_LoadConfig (plitem_t *config) +{ + for (in_devbindings_t *db = devbindings_list; db; db = db->next) { + clear_connection (db); + } + PR_RESRESET (devbindings); + devbindings_list = 0; + + plitem_t *devices = PL_ObjectForKey (config, "devices"); + if (PL_Type (devices) != QFArray) { + Sys_Printf ("IN_Binding_LoadConfig: devices not an array\n"); + return; + } + for (int i = 0, count = PL_A_NumObjects (devices); i < count; i++) { + plitem_t *db_cfg = PL_ObjectAtIndex (devices, i); + const char *bindname = PL_String (PL_ObjectForKey (db_cfg, "name")); + const char *devname = PL_String (PL_ObjectForKey (db_cfg, "devname")); + const char *id = PL_String (PL_ObjectForKey (db_cfg, "id")); + int num_axes = parse_num (PL_ObjectForKey (db_cfg, + "num_axes")); + int num_buttons = parse_num (PL_ObjectForKey (db_cfg, + "num_buttons")); + if (in_binding_find_device (bindname)) { + Sys_Printf ("%s already exists\n", bindname); + continue; + } + if (num_axes < 0) { + continue; + } + if (num_buttons < 0) { + continue; + } + + in_devbindings_t *db = PR_RESNEW (devbindings); + db->next = devbindings_list; + devbindings_list = db; + + db->name = strdup (bindname); + db->devname = strdup (devname); + if (id) { + db->id = strdup (id); + db->match_id = 1; + } else { + db->id = 0; + db->match_id = 0; + } + db->devid = -1; // not connected yet + + db->num_axes = num_axes; + db->num_buttons = num_buttons; + + db->axis_imt_id = -1; + db->button_imt_id = -1; + + plitem_t *axes = PL_ObjectForKey (db_cfg, "axes"); + if (PL_A_NumObjects (axes)) { + db->axis_imt_id = IMT_GetAxisBlock (db->num_axes); + } + for (int i = 0, count = PL_A_NumObjects (axes); i < count; i++) { + plitem_t *a = PL_ObjectAtIndex (axes, i); + const char *imt_name = PL_String (PL_ObjectForKey (a, "imt")); + int num = parse_num (PL_ObjectForKey (a, "num")); + const char *axis_name = PL_String (PL_ObjectForKey (a, "axis")); + in_recipe_t recipe = { + .min = parse_int (PL_ObjectForKey (a, "min"), 0), + .max = parse_int (PL_ObjectForKey (a, "max"), 0), + .minzone = parse_int (PL_ObjectForKey (a, "minzone"), 0), + .maxzone = parse_int (PL_ObjectForKey (a, "maxzone"), 0), + .deadzone = parse_int (PL_ObjectForKey (a, "deadzone"), 0), + .curve = parse_float (PL_ObjectForKey (a, "curve"), 1), + .scale = parse_float (PL_ObjectForKey (a, "scale"), 1), + }; + if (!imt_name || num < 0 || num >= db->num_axes) { + continue; + } + imt_t *imt = IMT_FindIMT (imt_name); + if (!imt) { + continue; + } + in_axis_t *axis = IN_FindAxis (axis_name); + if (!axis) { + continue; + } + IMT_BindAxis (imt, db->axis_imt_id + num, axis, &recipe); + } + plitem_t *buttons = PL_ObjectForKey (db_cfg, "buttons"); + if (PL_A_NumObjects (buttons)) { + db->button_imt_id = IMT_GetButtonBlock (db->num_buttons); + } + for (int i = 0, count = PL_A_NumObjects (buttons); i < count; i++) { + plitem_t *b = PL_ObjectAtIndex (buttons, i); + const char *imt_name = PL_String (PL_ObjectForKey (b, "imt")); + int num = parse_num (PL_ObjectForKey (b, "num")); + const char *binding = PL_String (PL_ObjectForKey (b, "binding")); + if (!imt_name || num < 0 || num >= db->num_buttons) { + continue; + } + imt_t *imt = IMT_FindIMT (imt_name); + if (!imt) { + continue; + } + IMT_BindButton (imt, db->button_imt_id + num, binding); + } + } + // force device connection events so any connected devices get hoocked up + // with their bindings + IN_SendConnectedDevices (); +} diff --git a/libs/input/in_button.c b/libs/input/in_button.c new file mode 100644 index 000000000..4847783eb --- /dev/null +++ b/libs/input/in_button.c @@ -0,0 +1,254 @@ +/* + in_button.c + + Logical button support + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/09/29 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/cmd.h" +#include "QF/hash.h" +#include "QF/input.h" +#include "QF/sys.h" + +typedef struct regbutton_s { + in_button_t *button; + char *press_cmd; + char *release_cmd; +} regbutton_t; + +static hashtab_t *button_tab; + +static const char * +button_get_key (const void *b, void *data) +{ + __auto_type regbutton = (const regbutton_t *) b; + return regbutton->button->name; +} + +static void +button_free (void *b, void *data) +{ + regbutton_t *rb = b; + if (rb->button->listeners) { + DARRAY_CLEAR (rb->button->listeners); + free (rb->button->listeners); + } + free (rb); +} + +static void +button_press (in_button_t *button, int id) +{ + if (id == button->down[0] || id == button->down[1]) { + // repeating key + return; + } + + if (!button->down[0]) { + button->down[0] = id; + } else if (!button->down[1]) { + button->down[1] = id; + } else { + Sys_Printf ("Three keys down for a button!\n"); + return; + } + + if (button->state & inb_down) { + // still down + return; + } + button->state |= inb_down | inb_edge_down; + if (button->listeners) { + LISTENER_INVOKE (button->listeners, button); + } +} + +static void +button_release (in_button_t *button, int id) +{ + if (id == -1) { + // typed manually at the console, assume for unsticking, so clear all + button->down[0] = button->down[1] = 0; + button->state = inb_edge_up; + return; + } + + if (button->down[0] == id) { + button->down[0] = 0; + } else if (button->down[1] == id) { + button->down[1] = 0; + } else { + // key up without coresponding down (menu pass through) + return; + } + if (button->down[0] || button->down[1]) { + // some other key is still holding it down + return; + } + + if (!(button->state & inb_down)) { + // still up (this should not happen) + return; + } + button->state &= ~inb_down; // now up + button->state |= inb_edge_up; + if (button->listeners) { + LISTENER_INVOKE (button->listeners, button); + } +} + +void +IN_ButtonAction (in_button_t *button, int id, int pressed) +{ + if (pressed) { + button_press (button, id); + } else { + button_release (button, id); + } +} + + +static void +button_press_cmd (void *_b) +{ + in_button_t *button = _b; + const char *idstr = Cmd_Argv (1); + // assume typed manually at the console for continuous down + int id = -1; + + if (idstr[0]) { + id = atoi (idstr); + } + button_press (button, id); +} + +static void +button_release_cmd (void *_b) +{ + in_button_t *button = _b; + const char *idstr = Cmd_Argv (1); + // assume typed manually at the console, probably for unsticking + int id = -1; + + if (idstr[0]) { + id = atoi (idstr); + } + button_release (button, id); +} + +VISIBLE int +IN_RegisterButton (in_button_t *button) +{ + const char *name = button->name; + if (Hash_Find (button_tab, name)) { + return 0; + } + size_t size = strlen (name) + 2; + regbutton_t *regbutton = malloc (sizeof (regbutton_t) + 2 * size); + regbutton->button = button; + + regbutton->press_cmd = (char *) (regbutton + 1); + regbutton->release_cmd = regbutton->press_cmd + size; + *regbutton->press_cmd = '+'; + *regbutton->release_cmd = '-'; + strcpy (regbutton->press_cmd + 1, name); + strcpy (regbutton->release_cmd + 1, name); + + Cmd_AddDataCommand (regbutton->press_cmd, button_press_cmd, button, + "Set the button's state to on/pressed."); + Cmd_AddDataCommand (regbutton->release_cmd, button_release_cmd, button, + "Set the button's state to off/released."); + + Hash_Add (button_tab, regbutton); + return 1; +} + +in_button_t * +IN_FindButton (const char *name) +{ + regbutton_t *regbutton = Hash_Find (button_tab, name); + if (regbutton) { + return regbutton->button; + } + return 0; +} + +void +IN_ButtonAddListener (in_button_t *button, button_listener_t listener, + void *data) +{ + if (!button->listeners) { + button->listeners = malloc (sizeof (*button->listeners)); + LISTENER_SET_INIT (button->listeners, 8); + } + LISTENER_ADD (button->listeners, listener, data); +} + +void +IN_ButtonRemoveListener (in_button_t *button, button_listener_t listener, + void *data) +{ + if (button->listeners) { + LISTENER_REMOVE (button->listeners, listener, data); + } +} + +static void +button_clear_state (void *_rb, void *data) +{ + regbutton_t *rb = _rb; + button_release_cmd (rb->button); +} + +void +IN_ButtonClearStates (void) +{ + Hash_ForEach (button_tab, button_clear_state, 0); +} + +static void +in_button_shutdown (void *data) +{ + Hash_DelTable (button_tab); +} + +static void __attribute__((constructor)) +in_button_init (void) +{ + button_tab = Hash_NewTable (127, button_get_key, button_free, 0, 0); + Sys_RegisterShutdown (in_button_shutdown, 0); +} diff --git a/libs/input/in_common.c b/libs/input/in_common.c new file mode 100644 index 000000000..f4024c753 --- /dev/null +++ b/libs/input/in_common.c @@ -0,0 +1,597 @@ +/* + in_common.c + + general input driver + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#define _BSD +#include +#include +#include +#include + +#include "QF/cvar.h" +#include "QF/darray.h" +#define IMPLEMENT_INPUT_Funcs +#include "QF/input.h" +#include "QF/joystick.h" +#include "QF/keys.h" +#include "QF/mathlib.h" +#include "QF/plist.h" +#include "QF/sys.h" +#include "QF/vid.h" + +#include "QF/input/event.h" + +#include "qfselect.h" + +typedef struct { + in_driver_t driver; + void *data; +} in_regdriver_t; + +static struct DARRAY_TYPE (in_regdriver_t) in_drivers = { .grow = 8 }; +static struct DARRAY_TYPE (in_device_t) in_devices = { .grow = 8 }; + +int in_grab; +static cvar_t in_grab_cvar = { + .name = "in_grab", + .description = + "With this set to 1, quake will grab the mouse, preventing loss of " + "input focus.", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &in_grab }, +}; +VISIBLE float in_amp; +static cvar_t in_amp_cvar = { + .name = "in_amp", + .description = + "global in_amp multiplier", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &in_amp }, +}; +VISIBLE float in_pre_amp; +static cvar_t in_pre_amp_cvar = { + .name = "in_pre_amp", + .description = + "global in_pre_amp multiplier", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &in_pre_amp }, +}; +int in_freelook; +static cvar_t in_freelook_cvar = { + .name = "freelook", + .description = + "force +mlook", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &in_freelook }, +}; +int in_mouse_filter; +static cvar_t in_mouse_filter_cvar = { + .name = "in_mouse_filter", + .description = + "Toggle mouse input filtering.", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &in_mouse_filter }, +}; +float in_mouse_amp; +static cvar_t in_mouse_amp_cvar = { + .name = "in_mouse_amp", + .description = + "mouse in_mouse_amp multiplier", + .default_value = "15", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &in_mouse_amp }, +}; +float in_mouse_pre_amp; +static cvar_t in_mouse_pre_amp_cvar = { + .name = "in_mouse_pre_amp", + .description = + "mouse in_mouse_pre_amp multiplier", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &in_mouse_pre_amp }, +}; +int lookstrafe; +static cvar_t lookstrafe_cvar = { + .name = "lookstrafe", + .description = + "when mlook/klook on player will strafe", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &lookstrafe }, +}; + +int64_t in_timeout = 10000;//10ms default timeout + +int +IN_RegisterDriver (in_driver_t *driver, void *data) +{ + in_regdriver_t rdriver = { *driver, data }; + DARRAY_APPEND (&in_drivers, rdriver); + return in_drivers.size - 1; +} + +void +IN_DriverData (int handle, void *data) +{ + in_drivers.a[handle].data = data; +} + +static int +in_add_device (int driver, void *device, const char *name, + const char *id) +{ + size_t devid; + in_device_t indev = { + .driverid = driver, + .device = device, + .name = name, + .id = id, + }; + + for (devid = 0; devid < in_devices.size; devid++) { + if (in_devices.a[devid].driverid == -1) { + in_devices.a[devid] = indev; + return devid; + } + } + DARRAY_APPEND (&in_devices, indev); + return devid; +} + +int +IN_AddDevice (int driver, void *device, const char *name, const char *id) +{ + if ((size_t) driver >= in_drivers.size) { + Sys_Error ("IN_AddDevice: invalid driver: %d", driver); + } + + int devid = in_add_device (driver, device, name, id); + + IE_event_t event = { + .type = ie_add_device, + .when = Sys_LongTime (), + .device = { + .devid = devid, + }, + }; + IE_Send_Event (&event); + return devid; +} + +void +IN_RemoveDevice (int devid) +{ + if ((size_t) devid >= in_devices.size) { + Sys_Error ("IN_RemoveDevice: invalid devid: %d", devid); + } + + IE_event_t event = { + .type = ie_remove_device, + .when = Sys_LongTime (), + .device = { + .devid = devid, + }, + }; + IE_Send_Event (&event); + + in_devices.a[devid].device = 0; +} + +void +IN_SendConnectedDevices (void) +{ + for (size_t devid = 0; devid < in_devices.size; devid++) { + if (in_devices.a[devid].driverid >= 0 + && in_devices.a[devid].device) { + IE_event_t event = { + .type = ie_add_device, + .when = Sys_LongTime (),//FIXME actual time? + .device = { + .devid = devid, + }, + }; + IE_Send_Event (&event); + } + } +} + +int +IN_FindDeviceId (const char *id) +{ + for (size_t i = 0; i < in_devices.size; i++) { + if (strcmp (id, in_devices.a[i].id) == 0) { + return i; + } + } + return -1; +} + +const char * +IN_GetDeviceName (int devid) +{ + if ((size_t) devid >= in_devices.size) { + return 0; + } + if (!in_devices.a[devid].device || in_devices.a[devid].driverid == -1) { + return 0; + } + return in_devices.a[devid].name; +} + +const char * +IN_GetDeviceId (int devid) +{ + if ((size_t) devid >= in_devices.size) { + return 0; + } + if (!in_devices.a[devid].device || in_devices.a[devid].driverid == -1) { + return 0; + } + return in_devices.a[devid].id; +} + +void +IN_SetDeviceEventData (int devid, void *data) +{ + if ((size_t) devid >= in_devices.size) { + return; + } + if (!in_devices.a[devid].device || in_devices.a[devid].driverid == -1) { + return; + } + in_regdriver_t *rd = &in_drivers.a[in_devices.a[devid].driverid]; + rd->driver.set_device_event_data (in_devices.a[devid].device, data, + rd->data); +} + +void * +IN_GetDeviceEventData (int devid) +{ + if ((size_t) devid >= in_devices.size) { + return 0; + } + if (!in_devices.a[devid].device || in_devices.a[devid].driverid == -1) { + return 0; + } + in_regdriver_t *rd = &in_drivers.a[in_devices.a[devid].driverid]; + return rd->driver.get_device_event_data (in_devices.a[devid].device, + rd->data); +} + +int +IN_AxisInfo (int devid, in_axisinfo_t *axes, int *numaxes) +{ + if ((size_t) devid >= in_devices.size) { + return -1; + } + if (!in_devices.a[devid].device || in_devices.a[devid].driverid == -1) { + return -1; + } + int driver = in_devices.a[devid].driverid; + in_regdriver_t *rd = &in_drivers.a[driver]; + if (!rd->driver.axis_info) { + return -1; + } + rd->driver.axis_info (rd->data, in_devices.a[devid].device, axes, numaxes); + if (axes) { + for (int i = 0; i < *numaxes; i++) { + axes[i].deviceid = devid; + } + } + return 0; +} + +int +IN_ButtonInfo (int devid, in_buttoninfo_t *buttons, int *numbuttons) +{ + if ((size_t) devid >= in_devices.size) { + return -1; + } + if (!in_devices.a[devid].device || in_devices.a[devid].driverid == -1) { + return -1; + } + int driver = in_devices.a[devid].driverid; + in_regdriver_t *rd = &in_drivers.a[driver]; + if (!rd->driver.button_info) { + return -1; + } + rd->driver.button_info (rd->data, in_devices.a[devid].device, + buttons, numbuttons); + if (buttons) { + for (int i = 0; i < *numbuttons; i++) { + buttons[i].deviceid = devid; + } + } + return 0; +} + +const char * +IN_GetAxisName (int devid, int axis_num) +{ + if ((size_t) devid >= in_devices.size) { + return 0; + } + if (!in_devices.a[devid].device || in_devices.a[devid].driverid == -1) { + return 0; + } + int driver = in_devices.a[devid].driverid; + in_regdriver_t *rd = &in_drivers.a[driver]; + if (!rd->driver.get_axis_name) { + return 0; + } + return rd->driver.get_axis_name (rd->data, in_devices.a[devid].device, + axis_num); +} + +const char * +IN_GetButtonName (int devid, int button_num) +{ + if ((size_t) devid >= in_devices.size) { + return 0; + } + if (!in_devices.a[devid].device || in_devices.a[devid].driverid == -1) { + return 0; + } + int driver = in_devices.a[devid].driverid; + in_regdriver_t *rd = &in_drivers.a[driver]; + if (!rd->driver.get_button_name) { + return 0; + } + return rd->driver.get_button_name (rd->data, in_devices.a[devid].device, + button_num); +} + +int +IN_GetAxisNumber (int devid, const char *axis_name) +{ + if ((size_t) devid >= in_devices.size) { + return -1; + } + if (!in_devices.a[devid].device || in_devices.a[devid].driverid == -1) { + return -1; + } + int driver = in_devices.a[devid].driverid; + in_regdriver_t *rd = &in_drivers.a[driver]; + if (!rd->driver.get_axis_num) { + return -1; + } + return rd->driver.get_axis_num (rd->data, in_devices.a[devid].device, + axis_name); +} + +int +IN_GetButtonNumber (int devid, const char *button_name) +{ + if ((size_t) devid >= in_devices.size) { + return -1; + } + if (!in_devices.a[devid].device || in_devices.a[devid].driverid == -1) { + return -1; + } + int driver = in_devices.a[devid].driverid; + in_regdriver_t *rd = &in_drivers.a[driver]; + if (!rd->driver.get_button_num) { + return -1; + } + return rd->driver.get_button_num (rd->data, in_devices.a[devid].device, + button_name); +} + +int +IN_GetAxisInfo (int devid, int axis_num, in_axisinfo_t *info) +{ + if ((size_t) devid >= in_devices.size) { + return 0; + } + if (!in_devices.a[devid].device || in_devices.a[devid].driverid == -1) { + return 0; + } + int driver = in_devices.a[devid].driverid; + in_regdriver_t *rd = &in_drivers.a[driver]; + if (rd->driver.get_axis_info (rd->data, in_devices.a[devid].device, + axis_num, info)) { + info->deviceid = devid; + return 1; + } + return 0; +} + +int +IN_GetButtonInfo (int devid, int button_num, in_buttoninfo_t *info) +{ + if ((size_t) devid >= in_devices.size) { + return 0; + } + if (!in_devices.a[devid].device || in_devices.a[devid].driverid == -1) { + return 0; + } + int driver = in_devices.a[devid].driverid; + in_regdriver_t *rd = &in_drivers.a[driver]; + if (rd->driver.get_button_info (rd->data, in_devices.a[devid].device, + button_num, info)) { + info->deviceid = devid; + return 1; + } + return 0; +} + +void +IN_UpdateGrab (int grab) +{ + for (size_t i = 0; i < in_drivers.size; i++) { + in_regdriver_t *rd = &in_drivers.a[i]; + if (rd->driver.grab_input) { + rd->driver.grab_input (rd->data, grab); + } + } +} + +static void +in_grab_f (void *data, const cvar_t *cvar) +{ + IN_UpdateGrab (in_grab); +} + +void +IN_ProcessEvents (void) +{ + qf_fd_set fdset; + int maxfd = -1; + int64_t timeout = in_timeout; + + QF_FD_ZERO (&fdset); + for (size_t i = 0; i < in_drivers.size; i++) { + in_regdriver_t *rd = &in_drivers.a[i]; + if (rd->driver.add_select) { + rd->driver.add_select (&fdset, &maxfd, rd->data); + } + if (rd->driver.process_events) { + rd->driver.process_events (rd->data); + // if a driver can't use select, then we can't block in select + timeout = 0; + } + } + if (maxfd >= 0 && Sys_Select (maxfd, &fdset, timeout) > 0) { + for (size_t i = 0; i < in_drivers.size; i++) { + in_regdriver_t *rd = &in_drivers.a[i]; + if (rd->driver.check_select) { + rd->driver.check_select (&fdset, rd->data); + } + } + } +} + +void +IN_SaveConfig (plitem_t *config) +{ + plitem_t *input_config = PL_NewDictionary (0); + PL_D_AddObject (config, "input", input_config); + + IMT_SaveConfig (input_config); + IN_Binding_SaveConfig (input_config); +} + +void +IN_LoadConfig (plitem_t *config) +{ + plitem_t *input_config = PL_ObjectForKey (config, "input"); + + if (input_config) { + IMT_LoadConfig (input_config); + IN_Binding_LoadConfig (input_config); + } +} + +/* Called at shutdown */ +static void +IN_shutdown (void *data) +{ + //JOY_Shutdown (); + + Sys_MaskPrintf (SYS_vid, "IN_Shutdown\n"); + for (size_t i = in_drivers.size; i-- > 0; ) { + in_regdriver_t *rd = &in_drivers.a[i]; + if (rd->driver.shutdown) { + rd->driver.shutdown (rd->data); + } + } + IN_Binding_Shutdown (); + IMT_Shutdown (); + + DARRAY_CLEAR (&in_drivers); + DARRAY_CLEAR (&in_devices); +} + +void +IN_Init (void) +{ + Sys_RegisterShutdown (IN_shutdown, 0); + IMT_Init (); + IN_Binding_Init (); + + for (size_t i = 0; i < in_drivers.size; i++) { + in_regdriver_t *rd = &in_drivers.a[i]; + rd->driver.init (rd->data); + } +} + +void +IN_Init_Cvars (void) +{ + Cvar_Register (&in_grab_cvar, in_grab_f, 0); + Cvar_Register (&in_amp_cvar, 0, 0); + Cvar_Register (&in_pre_amp_cvar, 0, 0); + Cvar_Register (&in_freelook_cvar, 0, 0); + Cvar_Register (&in_mouse_filter_cvar, 0, 0); + Cvar_Register (&in_mouse_amp_cvar, 0, 0); + Cvar_Register (&in_mouse_pre_amp_cvar, 0, 0); + Cvar_Register (&lookstrafe_cvar, 0, 0); + for (size_t i = 0; i < in_drivers.size; i++) { + in_regdriver_t *rd = &in_drivers.a[i]; + if (rd->driver.init_cvars) { + rd->driver.init_cvars (rd->data); + } + } +} + +void +IN_ClearStates (void) +{ + for (size_t i = 0; i < in_drivers.size; i++) { + in_regdriver_t *rd = &in_drivers.a[i]; + if (rd->driver.clear_states) { + rd->driver.clear_states (rd->data); + } + } + IN_AxisClearStates (); + IN_ButtonClearStates (); +} + +#ifdef HAVE_EVDEV +extern int in_evdev_force_link; +static __attribute__((used)) int *evdev_force_link = &in_evdev_force_link; +#endif diff --git a/libs/input/in_evdev.c b/libs/input/in_evdev.c new file mode 100644 index 000000000..af49d2677 --- /dev/null +++ b/libs/input/in_evdev.c @@ -0,0 +1,321 @@ +/* + in_evdev.c + + general evdev input driver + + Copyright (C) 2021 Bill Currie + Please see the file "AUTHORS" for a list of contributors + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/cvar.h" +#include "QF/input.h" +#include "QF/progs.h" // for PR_RESMAP +#include "QF/sys.h" + +#include "QF/input/event.h" + +#include "compat.h" +#include "qfselect.h" +#include "evdev/inputlib.h" + +typedef struct devmap_s { + struct devmap_s *next; + struct devmap_s **prev; + device_t *device; + void *event_data; + int devid; +} devmap_t; + +static int evdev_driver_handle = -1; +static int evdev_have_focus; +static PR_RESMAP (devmap_t) devmap; +static devmap_t *devmap_list; + +static void +in_evdev_add_select (qf_fd_set *fdset, int *maxfd, void *data) +{ + inputlib_add_select (&fdset->fdset, maxfd); +} + +static void +in_evdev_check_select (qf_fd_set *fdset, void *data) +{ + inputlib_check_select (&fdset->fdset); +} + +static void +in_evdev_shutdown (void *data) +{ + inputlib_close (); + + for (unsigned i = 0; i < devmap._size; i++) { + free (devmap._map[i]); + } + free (devmap._map); +} + +static void +in_evdev_set_device_event_data (void *device, void *event_data, void *data) +{ + device_t *dev = device; + devmap_t *dm = dev->data; + dm->event_data = event_data; +} + +static void * +in_evdev_get_device_event_data (void *device, void *data) +{ + device_t *dev = device; + devmap_t *dm = dev->data; + return dm->event_data; +} + +static void +in_evdev_axis_event (axis_t *axis, void *_dm) +{ + if (!evdev_have_focus) { + return; + } + + devmap_t *dm = _dm; + //Sys_Printf ("in_evdev_axis_event: %d %d\n", axis->num, axis->value); + + IE_event_t event = { + .type = ie_axis, + .when = Sys_LongTime (), + .axis = { + .data = dm->event_data, + .devid = dm->devid, + .axis = axis->num, + .value = axis->value, + }, + }; + IE_Send_Event (&event); +} + +static void +in_evdev_button_event (button_t *button, void *_dm) +{ + if (!evdev_have_focus) { + return; + } + + devmap_t *dm = _dm; + //Sys_Printf ("in_evdev_button_event: %d %d\n", button->num, button->state); + + IE_event_t event = { + .type = ie_button, + .when = Sys_LongTime (), + .button = { + .data = dm->event_data, + .devid = dm->devid, + .button = button->num, + .state = button->state, + }, + }; + IE_Send_Event (&event); +} + +static void +device_add (device_t *dev) +{ + const char *name = dev->name; + // prefer device unique string if available, otherwise fall back to + // the physical path + const char *id = dev->uniq; + if (!id || !*id) { + id = dev->phys; + } + + devmap_t *dm = PR_RESNEW (devmap); + dm->next = devmap_list; + dm->prev = &devmap_list; + if (devmap_list) { + devmap_list->prev = &dm->next; + } + devmap_list = dm; + + dev->data = dm; + dev->axis_event = in_evdev_axis_event; + dev->button_event = in_evdev_button_event; + + dm->device = dev; + dm->devid = IN_AddDevice (evdev_driver_handle, dev, name, id); + +#if 0 + Sys_Printf ("in_evdev: add %s\n", dev->path); + Sys_Printf (" %s\n", dev->name); + Sys_Printf (" %s\n", dev->phys); + for (int i = 0; i < dev->num_axes; i++) { + axis_t *axis = dev->axes + i; + Sys_Printf ("axis: %d %d\n", axis->num, axis->value); + } + for (int i = 0; i < dev->num_buttons; i++) { + button_t *button = dev->buttons + i; + Sys_Printf ("button: %d %d\n", button->num, button->state); + } +#endif +} + +static void +device_remove (device_t *dev) +{ + for (devmap_t *dm = devmap_list; dm; dm = dm->next) { + if (dm->device == dev) { + IN_RemoveDevice (dm->devid); + + if (dm->next) { + dm->next->prev = dm->prev; + } + *dm->prev = dm->next; + + PR_RESFREE (devmap, dm); + break; + } + } +} + +static void +in_evdev_init (void *data) +{ + inputlib_init (device_add, device_remove); +} + +static void +in_evdev_clear_states (void *data) +{ +} + +static void +in_evdev_axis_info (void *data, void *device, in_axisinfo_t *axes, + int *numaxes) +{ + device_t *dev = device; + if (!axes) { + *numaxes = dev->num_axes; + return; + } + if (*numaxes > dev->num_axes) { + *numaxes = dev->num_axes; + } + for (int i = 0; i < *numaxes; i++) { + axes[i].axis = dev->axes[i].num; + axes[i].value = dev->axes[i].value; + axes[i].min = dev->axes[i].min; + axes[i].max = dev->axes[i].max; + } +} + +static void +in_evdev_button_info (void *data, void *device, in_buttoninfo_t *buttons, + int *numbuttons) +{ + device_t *dev = device; + if (!buttons) { + *numbuttons = dev->num_buttons; + return; + } + if (*numbuttons > dev->num_buttons) { + *numbuttons = dev->num_buttons; + } + for (int i = 0; i < *numbuttons; i++) { + buttons[i].button = dev->buttons[i].num; + buttons[i].state = dev->buttons[i].state; + } +} + +static int +in_evdev_get_axis_info (void *data, void *device, int axis_num, + in_axisinfo_t *info) +{ + device_t *dev = device; + if (axis_num < 0 || axis_num > dev->num_axes) { + return 0; + } + info->axis = dev->axes[axis_num].num; + info->value = dev->axes[axis_num].value; + info->min = dev->axes[axis_num].min; + info->max = dev->axes[axis_num].max; + return 1; +} + +static int +in_evdev_get_button_info (void *data, void *device, int button_num, + in_buttoninfo_t *info) +{ + device_t *dev = device; + if (button_num < 0 || button_num > dev->num_buttons) { + return 0; + } + info->button = dev->buttons[button_num].num; + info->state = dev->buttons[button_num].state; + return 1; +} + +static in_driver_t in_evdev_driver = { + .init = in_evdev_init, + .shutdown = in_evdev_shutdown, + .set_device_event_data = in_evdev_set_device_event_data, + .get_device_event_data = in_evdev_get_device_event_data, + .add_select = in_evdev_add_select, + .check_select = in_evdev_check_select, + .clear_states = in_evdev_clear_states, + + .axis_info = in_evdev_axis_info, + .button_info = in_evdev_button_info, + + .get_axis_info = in_evdev_get_axis_info, + .get_button_info = in_evdev_get_button_info, +}; + +static int +in_evdev_evend_handler (const IE_event_t *event, void *data) +{ + if (event->type == ie_app_gain_focus) { + evdev_have_focus = 1; + return 1; + } else if (event->type == ie_app_lose_focus) { + evdev_have_focus = 0; + return 1; + } + return 0; +} + +static void __attribute__((constructor)) +in_evdev_register_driver (void) +{ + evdev_driver_handle = IN_RegisterDriver (&in_evdev_driver, 0); + //FIXME probably shouldn't be here + IE_Add_Handler (in_evdev_evend_handler, 0); +} + +int in_evdev_force_link; diff --git a/libs/input/in_event.c b/libs/input/in_event.c new file mode 100644 index 000000000..e5240f3b9 --- /dev/null +++ b/libs/input/in_event.c @@ -0,0 +1,135 @@ +/* + in_event.c + + input event handling + + Copyright (C) 2001 Bill Currie + + Author: Bill Currie + Date: 2001/8/9 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include + +#include "QF/darray.h" + +#include "QF/input/event.h" + +#define IE_EVENT(event) #event, +const char *ie_event_names[] = { +#include "QF/input/event_names.h" + 0 +}; + +typedef struct { + ie_handler_t *handler; + void *data; +} ie_reghandler_t; + +static struct DARRAY_TYPE (ie_reghandler_t) ie_handlers = { .grow = 8, }; +static unsigned focus; + +int +IE_Send_Event (const IE_event_t *event) +{ + if ((1 << event->type) & IE_broadcast_events) { + for (size_t i = 0; i < ie_handlers.size; i++) { + ie_reghandler_t *reg = &ie_handlers.a[i]; + if (reg->handler) { + reg->handler (event, reg->data); + } + } + return 1; + } + + if (focus < ie_handlers.size && ie_handlers.a[focus].handler) { + ie_reghandler_t *reg = &ie_handlers.a[focus]; + return reg->handler (event, reg->data); + } + return 0; +} + +int +IE_Add_Handler (ie_handler_t *event_handler, void *data) +{ + size_t handle; + ie_reghandler_t reg = { event_handler, data }; + + for (handle = 0; handle < ie_handlers.size; handle++) { + if (!ie_handlers.a[handle].handler) { + ie_handlers.a[handle] = reg; + return handle; + } + } + DARRAY_APPEND (&ie_handlers, reg); + return handle; +} + +void +IE_Remove_Handler (int handle) +{ + if ((size_t) (ssize_t) handle < ie_handlers.size) { + ie_handlers.a[handle].handler = 0; + } +} + +void +IE_Set_Focus (int handle) +{ + unsigned h = handle; + if (h < ie_handlers.size && ie_handlers.a[handle].handler && focus != h) { + IE_event_t event; + event.type = ie_lose_focus; + IE_Send_Event (&event); + focus = handle; + event.type = ie_gain_focus; + IE_Send_Event (&event); + } +} + +int +IE_Get_Focus (void) +{ + return focus; +} + +static void +in_event_shutdown (void *data) +{ + DARRAY_CLEAR (&ie_handlers); +} + +static void __attribute__((constructor)) +in_event_init (void) +{ + //FIXME see in_evdev + Sys_RegisterShutdown (in_event_shutdown, 0); +} diff --git a/libs/input/in_imt.c b/libs/input/in_imt.c new file mode 100644 index 000000000..3a4f3488a --- /dev/null +++ b/libs/input/in_imt.c @@ -0,0 +1,1179 @@ +/* + in_imt.c + + Input Mapping Table management + + Copyright (C) 2001 Zephaniah E. Hull + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/10/30 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/cmd.h" +#include "QF/cmem.h" +#include "QF/cvar.h" +#include "QF/hash.h" +#include "QF/input.h" +#include "QF/mathlib.h" +#include "QF/plist.h" +#include "QF/sys.h" +#include "QF/va.h" + +#include "QF/input/imt.h" + +#include "QF/input/binding.h" +#include "QF/input/imt.h" + +#include "qfalloca.h" + +/** Describe a region of imt bindings (axis or button) + + Each device may have a block of axis bindings and a block of button + bindings (some devices will have only the one block). + + Bindings are allocated to a device in contiguous blocks. +*/ +typedef struct imt_block_s { + int base; ///< index of first binding + int count; ///< number of bindings +} imt_block_t; + +typedef struct DARRAY_TYPE (in_context_t) in_contextset_t; +typedef struct DARRAY_TYPE (imt_block_t) imt_blockset_t; +/** Binding blocks are allocated across all imts +*/ +static imt_blockset_t axis_blocks = DARRAY_STATIC_INIT (8); +static imt_blockset_t button_blocks = DARRAY_STATIC_INIT (8); + +static in_contextset_t in_contexts = DARRAY_STATIC_INIT (8); +static size_t imt_current_context; + +static memsuper_t *binding_mem; +static hashtab_t *recipe_tab; + +static in_recipe_t * +alloc_recipe (void) +{ + return cmemalloc (binding_mem, sizeof (in_recipe_t)); +} + +static void +free_recipe (in_recipe_t *recipe) +{ + if (!recipe) { + return; + } + cmemfree (binding_mem, recipe); +} + +static void +recipe_free (void *recipe, void *data) +{ + free_recipe (recipe); +} + +static uintptr_t +recipe_get_hash (const void *recipe, void *data) +{ + return Hash_Buffer (recipe, sizeof (in_recipe_t)); +} + +static int +recipe_compare (const void *a, const void *b, void *data) +{ + return !memcmp (a, b, sizeof (in_recipe_t)); +} + +static in_axisbinding_t * +alloc_axis_binding (void) +{ + return cmemalloc (binding_mem, sizeof (in_axisbinding_t)); +} + +static void +free_axis_binding (in_axisbinding_t *binding) +{ + if (!binding) { + return; + } + cmemfree (binding_mem, binding); +} + +static in_buttonbinding_t * +alloc_button_binding (void) +{ + return cmemalloc (binding_mem, sizeof (in_buttonbinding_t)); +} + +static void +free_button_binding (in_buttonbinding_t *binding) +{ + if (!binding) { + return; + } + switch (binding->type) { + case inb_button: + break; + case inb_command: + free (binding->command); + break; + } + cmemfree (binding_mem, binding); +} + +static imt_block_t * +imt_get_block (imt_blockset_t *blockset) +{ + return DARRAY_OPEN_AT (blockset, blockset->size, 1); +} + +static int +imt_get_next_base (imt_blockset_t *blockset) +{ + if (!blockset->size) { + return 0; + } + imt_block_t *b = &blockset->a[blockset->size - 1]; + return b->base + b->count; +} + +static int +imt_get_axis_block (int count) +{ + int base = imt_get_next_base (&axis_blocks); + + for (size_t i = 0; i < in_contexts.size; i++) { + for (imt_t *imt = in_contexts.a[i].imts; imt; imt = imt->next) { + in_axisbinding_t **binding; + binding = DARRAY_OPEN_AT (&imt->axis_bindings, base, count); + memset (binding, 0, count * sizeof (binding)); + } + } + return base; +} + +static int +imt_get_button_block (int count) +{ + int base = imt_get_next_base (&button_blocks); + + for (size_t i = 0; i < in_contexts.size; i++) { + for (imt_t *imt = in_contexts.a[i].imts; imt; imt = imt->next) { + in_buttonbinding_t **binding; + binding = DARRAY_OPEN_AT (&imt->button_bindings, base, count); + memset (binding, 0, count * sizeof (binding)); + } + } + return base; +} + +static void +imt_reset_blocks (void) +{ + DARRAY_RESIZE (&axis_blocks, 0); + DARRAY_RESIZE (&button_blocks, 0); + for (size_t i = 0; i < in_contexts.size; i++) { + for (imt_t *imt = in_contexts.a[i].imts; imt; imt = imt->next) { + DARRAY_RESIZE (&imt->axis_bindings, 0); + DARRAY_RESIZE (&imt->button_bindings, 0); + } + } +} + +int +IMT_GetAxisBlock (int num_axes) +{ + int base = imt_get_axis_block (num_axes); + imt_block_t *block = imt_get_block (&axis_blocks); + block->base = base; + block->count = num_axes; + return base; +} + +int +IMT_GetButtonBlock (int num_buttons) +{ + int base = imt_get_button_block (num_buttons); + imt_block_t *block = imt_get_block (&button_blocks); + block->base = base; + block->count = num_buttons; + return base; +} + +int +IMT_CreateContext (const char *name) +{ + in_context_t *ctx = DARRAY_OPEN_AT (&in_contexts, in_contexts.size, 1); + memset (ctx, 0, sizeof (*ctx)); + ctx->imt_tail = &ctx->imts; + ctx->switcher_tail = &ctx->switchers; + ctx->name = name; + return ctx - in_contexts.a; +} + +static in_context_t * __attribute__ ((pure)) +imt_find_context (const char *name) +{ + if (name) { + for (size_t i = 0; i < in_contexts.size; i++) { + if (strcmp (name, in_contexts.a[i].name) == 0) { + return &in_contexts.a[i]; + } + } + } + return 0; +} + +static void +imt_switcher_update (imt_switcher_t *switcher) +{ + int state = 0; + + for (int i = 0; i < switcher->num_inputs; i++) { + imt_input_t *input = &switcher->inputs[i]; + int val = 0; + switch (input->type) { + case imti_button: + val = !!(input->button->state & inb_down); + break; + case imti_cvar: + //FIXME check cvar type + val = !!*(int *) input->cvar->value.value; + break; + } + state |= val << i; + } + switcher->context->active_imt = switcher->imts[state]; +} + +static void +imt_switcher_button_update (void *_switcher, const in_button_t *button) +{ + imt_switcher_update (_switcher); +} + +static void +imt_switcher_cvar_update (void *_switcher, const cvar_t *cvar) +{ + imt_switcher_update (_switcher); +} + +int +IMT_GetContext (void) +{ + return imt_current_context; +} + +void +IMT_SetContext (int ctx) +{ + if ((size_t) ctx >= in_contexts.size) { + Sys_Error ("IMT_SetContext: invalid context %d", ctx); + } + imt_current_context = ctx; + for (imt_switcher_t *switcher = in_contexts.a[ctx].switchers; switcher; + switcher = switcher->next) { + imt_switcher_update (switcher); + } + imt_t *imt = in_contexts.a[imt_current_context].active_imt; + Sys_MaskPrintf (SYS_input, "Switched to %s context, imt %s\n", + in_contexts.a[imt_current_context].name, + imt ? imt->name : 0); +} + +void +IMT_SetContextCbuf (int ctx, cbuf_t *cbuf) +{ + if ((size_t) ctx >= in_contexts.size) { + Sys_Error ("IMT_SetContextCbuf: invalid context %d", ctx); + } + in_contexts.a[ctx].cbuf = cbuf; +} + +static imt_t * __attribute__ ((pure)) +imt_find_imt (in_context_t *ctx, const char *name) +{ + for (imt_t *imt = ctx->imts; imt; imt = imt->next) { + if (strcasecmp (imt->name, name) == 0) { + return imt; + } + } + return 0; +} + +imt_t * +IMT_FindIMT (const char *name) +{ + for (size_t i = 0; i < in_contexts.size; i++) { + in_context_t *ctx = &in_contexts.a[i]; + imt_t *imt = imt_find_imt (ctx, name); + if (imt) { + return imt; + } + } + return 0; +} + +static imt_switcher_t * __attribute__((pure)) +imt_find_switcher (in_context_t *ctx, const char *name) +{ + for (imt_switcher_t *switcher = ctx->switchers; switcher; + switcher = switcher->next) { + if (strcasecmp (switcher->name, name) == 0) { + return switcher; + } + } + return 0; +} + +imt_switcher_t * +IMT_FindSwitcher (const char *name) +{ + for (size_t i = 0; i < in_contexts.size; i++) { + in_context_t *ctx = &in_contexts.a[i]; + imt_switcher_t *switcher = imt_find_switcher (ctx, name); + if (switcher) { + return switcher; + } + } + return 0; +} + +int +IMT_CreateIMT (int context, const char *imt_name, const char *chain_imt_name) +{ + in_context_t *ctx = &in_contexts.a[context]; + imt_t *imt; + imt_t *chain_imt = 0; + + if ((size_t) context >= in_contexts.size) { + Sys_Printf ("invalid imt context %d\n", context); + return 0; + } + + if (IMT_FindIMT (imt_name)) { + Sys_Printf ("imt %s already exists\n", imt_name); + return 0; + } + if (chain_imt_name) { + chain_imt = IMT_FindIMT (chain_imt_name); + if (!chain_imt) { + Sys_Printf ("chain imt %s does not exist\n", chain_imt_name); + return 0; + } + chain_imt = imt_find_imt (ctx, chain_imt_name); + if (!chain_imt) { + Sys_Printf ("chain imt %s not in target context\n", + chain_imt_name); + return 0; + } + } + imt = malloc (sizeof (imt_t)); + if (!ctx->imts) { + ctx->default_imt = imt; + ctx->active_imt = imt; + } + *ctx->imt_tail = imt; + ctx->imt_tail = &imt->next; + + imt->next = 0; + imt->chain = chain_imt; + imt->name = strdup (imt_name); + imt->written = 0; + DARRAY_INIT (&imt->axis_bindings, 8); + DARRAY_INIT (&imt->button_bindings, 8); + int num_axes = imt_get_next_base (&axis_blocks); + int num_buttons = imt_get_next_base (&button_blocks); + DARRAY_RESIZE (&imt->axis_bindings, num_axes); + DARRAY_RESIZE (&imt->button_bindings, num_buttons); + if (num_axes) { + memset (imt->axis_bindings.a, 0, + num_axes * sizeof (in_axisbinding_t *)); + } + if (num_buttons) { + memset (imt->button_bindings.a, 0, + num_buttons * sizeof (in_buttonbinding_t *)); + } + return 1; +} + +int +IMT_CreateSwitcher (const char *switcher_name, int context, imt_t *default_imt, + int num_inputs, const char **input_names) +{ + if (IMT_FindSwitcher (switcher_name)) { + Sys_Printf ("imt switcher %s already exists\n", switcher_name); + return 0; + } + + if ((size_t) context >= in_contexts.size) { + Sys_Printf ("invalid imt context %d\n", context); + return 0; + } + in_context_t *ctx = &in_contexts.a[context]; + + if (!imt_find_imt (ctx, default_imt->name)) { + Sys_Printf ("default imt %s not in context %s\n", + default_imt->name, ctx->name); + return 0; + } + + int input_error = 0; + if (num_inputs > 16) { + Sys_Printf ("too many inputs %d\n", num_inputs); + return 0; + } + for (int i = 0; i < num_inputs; i++) { + const char *input_name = input_names[i]; + if (!input_name) { + input_error = 1; + continue; + } + if (input_name[0] == '+') { + if (!IN_FindButton (input_name + 1)) { + Sys_Printf ("invalid button %s\n", input_name); + input_error = 1; + } + } else { + if (!Cvar_FindVar (input_name)) { + Sys_Printf ("invalid cvar %s\n", input_name); + input_error = 1; + } + } + } + if (input_error) { + return 0; + } + int num_states = 1 << num_inputs; + size_t size = sizeof (imt_switcher_t) + + num_inputs * sizeof (imt_input_t) + + num_states * sizeof (imt_t *); + imt_switcher_t *switcher = malloc (size); + *ctx->switcher_tail = switcher; + ctx->switcher_tail = &switcher->next; + + switcher->next = 0; + switcher->name = strdup (switcher_name); + switcher->num_inputs = num_inputs; + switcher->inputs = (imt_input_t *) (switcher + 1); + switcher->imts = (imt_t **) (switcher->inputs + num_inputs); + switcher->context = ctx; + for (int i = 0; i < num_inputs; i++) { + imt_input_t *input = &switcher->inputs[i]; + const char *input_name = input_names[i]; + if (input_name[0] == '+') { + input->type = imti_button; + input->button = IN_FindButton (input_name + 1); + IN_ButtonAddListener (input->button, imt_switcher_button_update, + switcher); + } else { + input->type = imti_cvar; + //FIXME check cvar type + input->cvar = Cvar_FindVar (input_name); + Cvar_AddListener (input->cvar, imt_switcher_cvar_update, switcher); + } + } + for (int i = 0; i < num_states; i++) { + switcher->imts[i] = default_imt; + } + return 1; +} + +void +IMT_BindAxis (imt_t *imt, int axis_num, in_axis_t *axis, + const in_recipe_t *recipe) +{ + if ((size_t) axis_num >= imt->axis_bindings.size) + return; + + in_axisbinding_t **bind = &imt->axis_bindings.a[axis_num]; + free_axis_binding ((*bind)); + (*bind) = 0; + if (axis && recipe) { + in_axisbinding_t *a = alloc_axis_binding (); + (*bind) = a; + if (!(a->recipe = Hash_FindElement (recipe_tab, recipe))) { + *(a->recipe = alloc_recipe ()) = *recipe; + Hash_AddElement (recipe_tab, a->recipe); + } + a->axis = axis; + } +} + +void +IMT_BindButton (imt_t *imt, int button, const char *binding) +{ + if ((size_t) button >= imt->button_bindings.size) + return; + + in_buttonbinding_t **bind = &imt->button_bindings.a[button]; + free_button_binding ((*bind)); + (*bind) = 0; + if (binding) { + in_buttonbinding_t *b = alloc_button_binding (); + (*bind) = b; + in_button_t *button; + if (binding[0] == '+' && (button = IN_FindButton (binding + 1))) { + b->type = inb_button; + b->button = button; + } else { + b->type = inb_command; + b->command = strdup(binding); + } + } +} + +bool +IMT_ProcessAxis (int axis, int value) +{ + imt_t *imt = in_contexts.a[imt_current_context].active_imt; + + while (imt) { + in_axisbinding_t *a = imt->axis_bindings.a[axis]; + if (a) { + in_recipe_t *recipe = a->recipe; + int relative = recipe->min == recipe->max; + int deadzone = recipe->deadzone; + int minval = recipe->min + recipe->minzone; + int maxval = recipe->max - recipe->maxzone; + float output; + if (relative) { + int input = value; + if (deadzone > 0) { + if (input > deadzone) { + input -= deadzone; + } else if (input < -deadzone) { + input += deadzone; + } else { + input = 0; + } + } + output = input * recipe->scale; + if (recipe->curve != 1) { + output = powf (output, recipe->curve); + } + a->axis->rel_input += output; + } else { + int input = bound (minval, value, maxval); + int range = maxval - minval; + int zero = minval; + if (recipe->deadzone >= 0) { + // balanced axis: -1..1 + int center = (recipe->min + recipe->max + 1) / 2; + minval += deadzone; + maxval -= deadzone; + input -= center; + if (input < -deadzone) { + input += deadzone; + } else if (input > deadzone) { + input -= deadzone; + } else { + input = 0; + } + if (center - minval > maxval - center) { + range = center - minval; + } else { + range = maxval - center; + } + zero = 0; + } + output = (float)(input - zero) / (range); + if (recipe->curve != 1) { + output = powf (output, recipe->curve); + } + output *= recipe->scale; + a->axis->abs_input = output; + } + return true; + } + imt = imt->chain; + } + return false; +} + +static void +process_binding (int button, int state, const char *cmd) +{ + cbuf_t *cbuf = in_contexts.a[imt_current_context].cbuf; + + if (!cbuf) { + return; + } + + if (cmd[0] == '+') { + if (state) { + Cbuf_AddText (cbuf, va (0, "%s %d\n", cmd, button)); + } else { + Cbuf_AddText (cbuf, va (0, "-%s %d\n", cmd + 1, button)); + } + } else { + if (state) { + Cbuf_AddText (cbuf, va (0, "%s\n", cmd)); + } + } +} + +bool +IMT_ProcessButton (int button, int state) +{ + imt_t *imt = in_contexts.a[imt_current_context].active_imt; + + while (imt) { + in_buttonbinding_t *b = imt->button_bindings.a[button]; + if (b) { + // ensure IN_ButtonAction never sees button id 0 + button += 1; + switch (b->type) { + case inb_button: + IN_ButtonAction (b->button, button, state); + break; + case inb_command: + //FIXME avoid repeat + process_binding (button, state, b->command); + break; + } + return true; + } + imt = imt->chain; + } + return false; +} + +static void +imt_f (void) +{ + int c; + imt_t *imt; + const char *imt_name = 0; + const char *context_name; + + c = Cmd_Argc (); + switch (c) { + case 3: + imt_name = Cmd_Argv (2); + case 2: + context_name = Cmd_Argv (1); + break; + default: + return; + } + in_context_t *ctx = imt_find_context (context_name); + if (!ctx) { + Sys_Printf ("imt error: invalid context: %s\n", context_name); + return; + } + + if (!imt_name) { + Sys_Printf ("Current imt is %s\n", ctx->active_imt->name); + Sys_Printf ("imt : set to a specific input mapping table\n"); + return; + } + + imt = imt_find_imt (ctx, imt_name); + if (!imt) { + Sys_Printf ("\"%s\" is not an imt in %s\n", imt_name, ctx->name); + return; + } + + ctx->active_imt = imt; +} + +static void +imt_list_f (void) +{ + for (size_t i = 0; i < in_contexts.size; i++) { + in_context_t *ctx = &in_contexts.a[i]; + Sys_Printf ("context: %s\n", ctx->name); + for (imt_t *imt = ctx->imts; imt; imt = imt->next) { + if (imt->chain) { + Sys_Printf (" %s -> %s\n", imt->name, imt->chain->name); + } else { + Sys_Printf (" %s\n", imt->name); + } + } + } +} + +static void +imt_create_f (void) +{ + const char *context_name; + const char *imt_name; + const char *chain_imt_name = 0; + + if (Cmd_Argc () < 3 || Cmd_Argc () > 4) { + Sys_Printf ("see help imt_create\n"); + return; + } + context_name = Cmd_Argv (1); + imt_name = Cmd_Argv (2); + if (Cmd_Argc () == 4) { + chain_imt_name = Cmd_Argv (3); + } + in_context_t *ctx = imt_find_context (context_name); + if (!ctx) { + Sys_Printf ("imt error: invalid context: %s\n", context_name); + return; + } + IMT_CreateIMT (ctx - in_contexts.a, imt_name, chain_imt_name); +} + +static void +imt_switcher_create_f (void) +{ + int argc = Cmd_Argc (); + + if (argc < 5) { + Sys_Printf ("see help imt_switcher_create\n"); + return; + } + + const char *switcher_name = Cmd_Argv (1); + const char *context_name = Cmd_Argv (2); + const char *default_imt_name = Cmd_Argv (3); + + in_context_t *ctx = imt_find_context (context_name); + if (!ctx) { + Sys_Printf ("imt error: invalid context: %s\n", context_name); + return; + } + + imt_t *default_imt = imt_find_imt (ctx, default_imt_name); + if (!default_imt) { + Sys_Printf ("default imt %s not in context %s\n", + default_imt_name, context_name); + return; + } + + int num_inputs = argc - 4; + const char **input_names = alloca (num_inputs * sizeof (const char*)); + for (int i = 0; i < num_inputs; i++) { + input_names[i] = Cmd_Argv (i + 4); + } + + IMT_CreateSwitcher (switcher_name, ctx - in_contexts.a, default_imt, + num_inputs, input_names); +} + +static void +imt_switcher_f (void) +{ + int argc = Cmd_Argc (); + + // state/imt pairs result in an even number of arguments + if (argc < 4 || (argc & 1)) { + Sys_Printf ("see help imt_switcher\n"); + return; + } + const char *switcher_name = Cmd_Argv (1); + imt_switcher_t *switcher = IMT_FindSwitcher (switcher_name); + if (!switcher) { + Sys_Printf ("switcher %s does not exist\n", switcher_name); + return; + } + int num_states = 1 << switcher->num_inputs; + for (int i = 2; i < argc; i += 2) { + const char *state_str = Cmd_Argv (i + 0); + const char *imt_name = Cmd_Argv (i + 1); + char *end; + int state = strtol (state_str, &end, 0); + imt_t *imt = imt_find_imt (switcher->context, imt_name); + if (*end || state < 0 || state >= num_states) { + Sys_Printf ("invalid state: %s\n", state_str); + continue; + } + if (!imt) { + Sys_Printf ("imt %s not in context %s\n", + imt_name, switcher->context->name); + continue; + } + switcher->imts[state] = imt; + } +} + +static void +imt_drop_all_f (void) +{ + for (size_t i = 0; i < in_contexts.size; i++) { + in_context_t *ctx = &in_contexts.a[i]; + while (ctx->imts) { + imt_t *imt = ctx->imts; + ctx->imts = imt->next; + for (size_t i = 0; i < imt->axis_bindings.size; i++) { + free_axis_binding (imt->axis_bindings.a[i]); + } + for (size_t i = 0; i < imt->button_bindings.size; i++) { + free_button_binding (imt->button_bindings.a[i]); + } + DARRAY_CLEAR (&imt->axis_bindings); + DARRAY_CLEAR (&imt->button_bindings); + free ((char *) imt->name); + free (imt); + } + while (ctx->switchers) { + imt_switcher_t *switcher = ctx->switchers; + ctx->switchers = switcher->next; + for (int i = 0; i < switcher->num_inputs; i++) { + imt_input_t *input = &switcher->inputs[i]; + switch (input->type) { + case imti_button: + IN_ButtonRemoveListener (input->button, + imt_switcher_button_update, + switcher); + break; + case imti_cvar: + Cvar_RemoveListener (input->cvar, + imt_switcher_cvar_update, + switcher); + break; + } + } + free ((char *) switcher->name); + free (switcher); + } + ctx->active_imt = 0; + ctx->default_imt = 0; + ctx->imt_tail = &ctx->imts; + ctx->switcher_tail = &ctx->switchers; + } +} + +typedef struct { + const char *name; + xcommand_t func; + const char *desc; +} imtcmd_t; + +static imtcmd_t imt_commands[] = { + { "imt", imt_f, + "Set the active imt of the specified context" + }, + { "imt_list", imt_list_f, + "List the available input mapping tables." + }, + { "imt_create", imt_create_f, + "create a new imt table:\n" + " imt_create [chain_name]\n" + "\n" + "The new table will be attached to the specified context.\n" + "\n" + "imt_name must not already exist.\n" + "If given, chain_name must already exist and be in the context.\n" + }, + { "imt_switcher_create", imt_switcher_create_f, + "create a new imt switcher:\n" + " imt_switcher_create " + " [..]\n" + "name is the name of the switcher and must be unique across all\n" + "contexts.\n" + "\n" + "The new switcher will be attached to the specified context\n" + "\n" + "default_imt specifies the default imt to be used for all possible\n" + "states and must exist and be in the context.\n" + "\n" + "input0..inputN specify the inputs (cvar or button) used to set the\n" + "switcher's state. As each input forms a bit in the state index,\n" + "there will be 2**(N+1) states (so 4 inputs will result in 16\n" + "states, and 16 inputs will result in 65536 states). Up to 16 inputs\n" + "are allowed.\n" + "\n" + "Buttons are spefied as +buttonname (eg, +mlook, +strafe).\n" + "Cvars are just the cvar name (eg, freelook, lookstrafe).\n" + "\n" + "Use imt_switcher to set the imt for each state.\n" + "\n" + "There can be multiple switchers in a context, but they can wind up\n" + "fighting over the active imt for the context if they share inputs.\n" + }, + { "imt_switcher", imt_switcher_f, + "Set the imts for states in an imt switcher:\n" + " imt_switcher " + " [.. ]\n" + "name is the name of the switcher to be modifed and must exist\n" + "state_index is the state index formed by the binary number\n" + "interpretation of the inputs with input0 being bit 0 and inputN\n" + "being bit N.\n" + "\n" + "imt is the name of the imt to be assigned to the state and must\n" + "exist and be in the same context as the switcher.\n" + "\n" + "Any number of state_index imt pairs can be specified.\n" + }, + { "imt_drop_all", imt_drop_all_f, + "Delete all imt tables.\n" + }, + {}, +}; + +void +IMT_Init (void) +{ + binding_mem = new_memsuper (); + recipe_tab = Hash_NewTable (61, 0, recipe_free, 0, 0); + Hash_SetHashCompare (recipe_tab, recipe_get_hash, recipe_compare); + for (imtcmd_t *cmd = imt_commands; cmd->name; cmd++) { + Cmd_AddCommand (cmd->name, cmd->func, cmd->desc); + } +} + +void +IMT_Shutdown (void) +{ + imt_drop_all_f (); + Hash_DelTable (recipe_tab); + + delete_memsuper (binding_mem); + binding_mem = 0; + DARRAY_CLEAR (&axis_blocks); + DARRAY_CLEAR (&button_blocks); + DARRAY_CLEAR (&in_contexts); +} + +void +IMT_SaveConfig (plitem_t *config) +{ + plitem_t *ctx_list = PL_NewArray (); + PL_D_AddObject (config, "contexts", ctx_list); + for (size_t i = 0; i < in_contexts.size; i++) { + in_context_t *context = &in_contexts.a[i]; + plitem_t *ctx = PL_NewDictionary (0); + PL_A_AddObject (ctx_list, ctx); + PL_D_AddObject (ctx, "name", PL_NewString (context->name)); + if (context->imts) { + plitem_t *imt_list = PL_NewArray (); + PL_D_AddObject (ctx, "imts", imt_list); + for (imt_t *imt = context->imts; imt; imt = imt->next) { + plitem_t *imt_cfg = PL_NewDictionary (0); + PL_D_AddObject (imt_cfg, "name", PL_NewString (imt->name)); + if (imt->chain) { + PL_D_AddObject (imt_cfg, "chain", + PL_NewString (imt->chain->name)); + } + PL_A_AddObject (imt_list, imt_cfg); + // the bindings are not written here because they are managed + // by IN_Binding_SaveConfig: IMT does not really know the + // device-input structure (it cound via the blocks, but it + // doesn't know the device names (by design)) + } + } + if (context->default_imt) { + PL_D_AddObject (ctx, "default_imt", + PL_NewString (context->default_imt->name)); + } + if (context->switchers) { + plitem_t *switcher_list = PL_NewArray (); + PL_D_AddObject (ctx, "switchers", switcher_list); + for (imt_switcher_t *switcher = context->switchers; switcher; + switcher = switcher->next) { + plitem_t *switcher_cfg = PL_NewDictionary (0); + PL_A_AddObject (switcher_list, switcher_cfg); + + PL_D_AddObject (switcher_cfg, "name", + PL_NewString (switcher->name)); + + plitem_t *input_list = PL_NewArray (); + PL_D_AddObject (switcher_cfg, "inputs", input_list); + for (int i = 0; i < switcher->num_inputs; i++) { + imt_input_t *input = &switcher->inputs[i]; + const char *name = 0; + switch (input->type) { + case imti_button: + name = va (0, "+%s", input->button->name); + break; + case imti_cvar: + name = input->cvar->name; + break; + } + PL_A_AddObject (input_list, PL_NewString (name)); + } + + plitem_t *state_list = PL_NewArray (); + PL_D_AddObject (switcher_cfg, "imts", state_list); + int num_states = 1 << switcher->num_inputs; + for (int i = 0; i < num_states; i++) { + imt_t *imt = switcher->imts[i]; + PL_A_AddObject (state_list, PL_NewString (imt->name)); + } + } + } + } +} + +void +IMT_SaveAxisConfig (plitem_t *axes, int axis_ind, int dev_axis) +{ + for (size_t i = 0; i < in_contexts.size; i++) { + in_context_t *context = &in_contexts.a[i]; + for (imt_t *imt = context->imts; imt; imt = imt->next) { + in_axisbinding_t *a = imt->axis_bindings.a[axis_ind]; + if (a) { + in_recipe_t *recipe = a->recipe; + plitem_t *axis = PL_NewDictionary (0); + PL_A_AddObject (axes, axis); + + PL_D_AddObject (axis, "imt", PL_NewString (imt->name)); + PL_D_AddObject (axis, "num", + PL_NewString (va (0, "%d", dev_axis))); + PL_D_AddObject (axis, "axis", PL_NewString (a->axis->name)); + PL_D_AddObject (axis, "min", + PL_NewString (va (0, "%d", recipe->min))); + PL_D_AddObject (axis, "max", + PL_NewString (va (0, "%d", recipe->max))); + PL_D_AddObject (axis, "minzone", + PL_NewString (va (0, "%d", recipe->minzone))); + PL_D_AddObject (axis, "maxzone", + PL_NewString (va (0, "%d", recipe->maxzone))); + PL_D_AddObject (axis, "deadzone", + PL_NewString (va (0, "%d", recipe->deadzone))); + PL_D_AddObject (axis, "curve", + PL_NewString (va (0, "%.9g", recipe->curve))); + PL_D_AddObject (axis, "scale", + PL_NewString (va (0, "%.9g", recipe->scale))); + } + } + } +} + +void +IMT_SaveButtonConfig (plitem_t *buttons, int button_ind, int dev_button) +{ + for (size_t i = 0; i < in_contexts.size; i++) { + in_context_t *context = &in_contexts.a[i]; + for (imt_t *imt = context->imts; imt; imt = imt->next) { + in_buttonbinding_t *b = imt->button_bindings.a[button_ind]; + if (b) { + plitem_t *button = PL_NewDictionary (0); + PL_A_AddObject (buttons, button); + + PL_D_AddObject (button, "imt", PL_NewString (imt->name)); + PL_D_AddObject (button, "num", + PL_NewString (va (0, "%d", dev_button))); + switch (b->type) { + case inb_button: + PL_D_AddObject (button, "binding", + PL_NewString (va (0, "+%s", + b->button->name))); + break; + case inb_command: + PL_D_AddObject (button, "binding", + PL_NewString (b->command)); + break; + } + } + } + } +} + +void +IMT_LoadConfig (plitem_t *config) +{ + imt_drop_all_f (); + imt_reset_blocks (); + + plitem_t *ctx_list = PL_ObjectForKey (config, "contexts"); + if (PL_Type (ctx_list) != QFArray) { + Sys_Printf ("IMT_LoadConfig: contexts not an array\n"); + return; + } + for (int i = 0, count = PL_A_NumObjects (ctx_list); i < count; i++) { + plitem_t *ctx = PL_ObjectAtIndex (ctx_list, i); + const char *name = PL_String (PL_ObjectForKey (ctx, "name")); + in_context_t *context = imt_find_context (name); + if (!context) { + continue; + } + plitem_t *imts = PL_ObjectForKey (ctx, "imts"); + if (!imts || PL_Type (imts) != QFArray) { + continue; + } + for (int j = 0, num_imts = PL_A_NumObjects (imts); j < num_imts; j++) { + plitem_t *imt = PL_ObjectAtIndex (imts, j); + const char *imt_name = PL_String (PL_ObjectForKey (imt, "name")); + const char *imt_chain = PL_String (PL_ObjectForKey (imt, "chain")); + if (imt_name) { + IMT_CreateIMT (context - in_contexts.a, imt_name, imt_chain); + } + } + const char *default_imt = PL_String (PL_ObjectForKey (ctx, + "default_imt")); + if (default_imt) { + context->default_imt = IMT_FindIMT (default_imt); + } + if (!context->default_imt) { + context->default_imt = context->imts; + } + context->active_imt = context->default_imt; + + plitem_t *switcher_list = PL_ObjectForKey (ctx, "switchers"); + if (!switcher_list || PL_Type (switcher_list) != QFArray) { + continue; + } + for (int j = 0, num_switchers = PL_A_NumObjects (switcher_list); + j < num_switchers; j++) { + plitem_t *switcher = PL_ObjectAtIndex (switcher_list, j); + const char *name = PL_String (PL_ObjectForKey (switcher, "name")); + plitem_t *input_list = PL_ObjectForKey (switcher, "inputs"); + if (!name || !input_list || PL_Type (input_list) != QFArray) { + continue; + } + int num_inputs = PL_A_NumObjects (input_list); + const char **input_names = alloca (num_inputs * sizeof (char *)); + for (int k = 0; k < num_inputs; k++) { + input_names[k] = PL_String (PL_ObjectAtIndex (input_list, k)); + } + if (!IMT_CreateSwitcher (name, context - in_contexts.a, + context->default_imt, + num_inputs, input_names)) { + continue; + } + imt_switcher_t *s = imt_find_switcher (context, name); + + plitem_t *imt_list = PL_ObjectForKey (switcher, "imts"); + if (!imt_list || PL_Type (imt_list) != QFArray) { + continue; + } + for (int k = 0, count = PL_A_NumObjects (imt_list); k < count; + k++) { + const char *imt_name = PL_String(PL_ObjectAtIndex(imt_list, k)); + if (!imt_name) { + continue; + } + imt_t *imt = imt_find_imt (context, imt_name); + if (imt) { + s->imts[k] = imt; + } + } + } + } +} diff --git a/libs/input/keys.c b/libs/input/keys.c new file mode 100644 index 000000000..d519c3fb8 --- /dev/null +++ b/libs/input/keys.c @@ -0,0 +1,406 @@ +/* + keys.c + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include +#include + +#include "QF/cbuf.h" +#include "QF/cmd.h" +#include "QF/cmem.h" +#include "QF/cvar.h" +#include "QF/darray.h" +#include "QF/dstring.h" +#include "QF/keys.h" +#include "QF/input.h" +#include "QF/sys.h" +#include "QF/va.h" + +#include "compat.h" +#include "old_keys.h" + +typedef struct { + const char *name; + knum_t keynum; +} keyname_t; + +keyname_t keynames[] = { + { "K_UNKNOWN", QFK_UNKNOWN }, + { "K_FIRST", QFK_FIRST }, + { "K_BACKSPACE", QFK_BACKSPACE }, + { "K_TAB", QFK_TAB }, + { "K_CLEAR", QFK_CLEAR }, + { "K_RETURN", QFK_RETURN }, + { "K_PAUSE", QFK_PAUSE }, + { "K_ESCAPE", QFK_ESCAPE }, + { "K_SPACE", QFK_SPACE }, + { "K_EXCLAIM", QFK_EXCLAIM }, + { "K_QUOTEDBL", QFK_QUOTEDBL }, + { "K_HASH", QFK_HASH }, + { "K_DOLLAR", QFK_DOLLAR }, + { "K_PERCENT", QFK_PERCENT }, + { "K_AMPERSAND", QFK_AMPERSAND }, + { "K_QUOTE", QFK_QUOTE }, + { "K_LEFTPAREN", QFK_LEFTPAREN }, + { "K_RIGHTPAREN", QFK_RIGHTPAREN }, + { "K_ASTERISK", QFK_ASTERISK }, + { "K_PLUS", QFK_PLUS }, + { "K_COMMA", QFK_COMMA }, + { "K_MINUS", QFK_MINUS }, + { "K_PERIOD", QFK_PERIOD }, + { "K_SLASH", QFK_SLASH }, + { "K_0", QFK_0 }, + { "K_1", QFK_1 }, + { "K_2", QFK_2 }, + { "K_3", QFK_3 }, + { "K_4", QFK_4 }, + { "K_5", QFK_5 }, + { "K_6", QFK_6 }, + { "K_7", QFK_7 }, + { "K_8", QFK_8 }, + { "K_9", QFK_9 }, + { "K_COLON", QFK_COLON }, + { "K_SEMICOLON", QFK_SEMICOLON }, + { "K_LESS", QFK_LESS }, + { "K_EQUALS", QFK_EQUALS }, + { "K_GREATER", QFK_GREATER }, + { "K_QUESTION", QFK_QUESTION }, + { "K_AT", QFK_AT }, + { "K_LEFTBRACKET", QFK_LEFTBRACKET }, + { "K_BACKSLASH", QFK_BACKSLASH }, + { "K_RIGHTBRACKET", QFK_RIGHTBRACKET }, + { "K_CARET", QFK_CARET }, + { "K_UNDERSCORE", QFK_UNDERSCORE }, + { "K_BACKQUOTE", QFK_BACKQUOTE }, + { "K_a", QFK_a }, + { "K_b", QFK_b }, + { "K_c", QFK_c }, + { "K_d", QFK_d }, + { "K_e", QFK_e }, + { "K_f", QFK_f }, + { "K_g", QFK_g }, + { "K_h", QFK_h }, + { "K_i", QFK_i }, + { "K_j", QFK_j }, + { "K_k", QFK_k }, + { "K_l", QFK_l }, + { "K_m", QFK_m }, + { "K_n", QFK_n }, + { "K_o", QFK_o }, + { "K_p", QFK_p }, + { "K_q", QFK_q }, + { "K_r", QFK_r }, + { "K_s", QFK_s }, + { "K_t", QFK_t }, + { "K_u", QFK_u }, + { "K_v", QFK_v }, + { "K_w", QFK_w }, + { "K_x", QFK_x }, + { "K_y", QFK_y }, + { "K_z", QFK_z }, + { "K_BRACELEFT", QFK_BRACELEFT }, + { "K_BAR", QFK_BAR }, + { "K_BRACERIGHT", QFK_BRACERIGHT }, + { "K_ASCIITILDE", QFK_ASCIITILDE }, + { "K_DELETE", QFK_DELETE }, + { "K_WORLD_0", QFK_WORLD_0 }, + { "K_WORLD_1", QFK_WORLD_1 }, + { "K_WORLD_2", QFK_WORLD_2 }, + { "K_WORLD_3", QFK_WORLD_3 }, + { "K_WORLD_4", QFK_WORLD_4 }, + { "K_WORLD_5", QFK_WORLD_5 }, + { "K_WORLD_6", QFK_WORLD_6 }, + { "K_WORLD_7", QFK_WORLD_7 }, + { "K_WORLD_8", QFK_WORLD_8 }, + { "K_WORLD_9", QFK_WORLD_9 }, + { "K_WORLD_10", QFK_WORLD_10 }, + { "K_WORLD_11", QFK_WORLD_11 }, + { "K_WORLD_12", QFK_WORLD_12 }, + { "K_WORLD_13", QFK_WORLD_13 }, + { "K_WORLD_14", QFK_WORLD_14 }, + { "K_WORLD_15", QFK_WORLD_15 }, + { "K_WORLD_16", QFK_WORLD_16 }, + { "K_WORLD_17", QFK_WORLD_17 }, + { "K_WORLD_18", QFK_WORLD_18 }, + { "K_WORLD_19", QFK_WORLD_19 }, + { "K_WORLD_20", QFK_WORLD_20 }, + { "K_WORLD_21", QFK_WORLD_21 }, + { "K_WORLD_22", QFK_WORLD_22 }, + { "K_WORLD_23", QFK_WORLD_23 }, + { "K_WORLD_24", QFK_WORLD_24 }, + { "K_WORLD_25", QFK_WORLD_25 }, + { "K_WORLD_26", QFK_WORLD_26 }, + { "K_WORLD_27", QFK_WORLD_27 }, + { "K_WORLD_28", QFK_WORLD_28 }, + { "K_WORLD_29", QFK_WORLD_29 }, + { "K_WORLD_30", QFK_WORLD_30 }, + { "K_WORLD_31", QFK_WORLD_31 }, + { "K_WORLD_32", QFK_WORLD_32 }, + { "K_WORLD_33", QFK_WORLD_33 }, + { "K_WORLD_34", QFK_WORLD_34 }, + { "K_WORLD_35", QFK_WORLD_35 }, + { "K_WORLD_36", QFK_WORLD_36 }, + { "K_WORLD_37", QFK_WORLD_37 }, + { "K_WORLD_38", QFK_WORLD_38 }, + { "K_WORLD_39", QFK_WORLD_39 }, + { "K_WORLD_40", QFK_WORLD_40 }, + { "K_WORLD_41", QFK_WORLD_41 }, + { "K_WORLD_42", QFK_WORLD_42 }, + { "K_WORLD_43", QFK_WORLD_43 }, + { "K_WORLD_44", QFK_WORLD_44 }, + { "K_WORLD_45", QFK_WORLD_45 }, + { "K_WORLD_46", QFK_WORLD_46 }, + { "K_WORLD_47", QFK_WORLD_47 }, + { "K_WORLD_48", QFK_WORLD_48 }, + { "K_WORLD_49", QFK_WORLD_49 }, + { "K_WORLD_50", QFK_WORLD_50 }, + { "K_WORLD_51", QFK_WORLD_51 }, + { "K_WORLD_52", QFK_WORLD_52 }, + { "K_WORLD_53", QFK_WORLD_53 }, + { "K_WORLD_54", QFK_WORLD_54 }, + { "K_WORLD_55", QFK_WORLD_55 }, + { "K_WORLD_56", QFK_WORLD_56 }, + { "K_WORLD_57", QFK_WORLD_57 }, + { "K_WORLD_58", QFK_WORLD_58 }, + { "K_WORLD_59", QFK_WORLD_59 }, + { "K_WORLD_60", QFK_WORLD_60 }, + { "K_WORLD_61", QFK_WORLD_61 }, + { "K_WORLD_62", QFK_WORLD_62 }, + { "K_WORLD_63", QFK_WORLD_63 }, + { "K_WORLD_64", QFK_WORLD_64 }, + { "K_WORLD_65", QFK_WORLD_65 }, + { "K_WORLD_66", QFK_WORLD_66 }, + { "K_WORLD_67", QFK_WORLD_67 }, + { "K_WORLD_68", QFK_WORLD_68 }, + { "K_WORLD_69", QFK_WORLD_69 }, + { "K_WORLD_70", QFK_WORLD_70 }, + { "K_WORLD_71", QFK_WORLD_71 }, + { "K_WORLD_72", QFK_WORLD_72 }, + { "K_WORLD_73", QFK_WORLD_73 }, + { "K_WORLD_74", QFK_WORLD_74 }, + { "K_WORLD_75", QFK_WORLD_75 }, + { "K_WORLD_76", QFK_WORLD_76 }, + { "K_WORLD_77", QFK_WORLD_77 }, + { "K_WORLD_78", QFK_WORLD_78 }, + { "K_WORLD_79", QFK_WORLD_79 }, + { "K_WORLD_80", QFK_WORLD_80 }, + { "K_WORLD_81", QFK_WORLD_81 }, + { "K_WORLD_82", QFK_WORLD_82 }, + { "K_WORLD_83", QFK_WORLD_83 }, + { "K_WORLD_84", QFK_WORLD_84 }, + { "K_WORLD_85", QFK_WORLD_85 }, + { "K_WORLD_86", QFK_WORLD_86 }, + { "K_WORLD_87", QFK_WORLD_87 }, + { "K_WORLD_88", QFK_WORLD_88 }, + { "K_WORLD_89", QFK_WORLD_89 }, + { "K_WORLD_90", QFK_WORLD_90 }, + { "K_WORLD_91", QFK_WORLD_91 }, + { "K_WORLD_92", QFK_WORLD_92 }, + { "K_WORLD_93", QFK_WORLD_93 }, + { "K_WORLD_94", QFK_WORLD_94 }, + { "K_WORLD_95", QFK_WORLD_95 }, + { "K_KP0", QFK_KP0 }, + { "K_KP1", QFK_KP1 }, + { "K_KP2", QFK_KP2 }, + { "K_KP3", QFK_KP3 }, + { "K_KP4", QFK_KP4 }, + { "K_KP5", QFK_KP5 }, + { "K_KP6", QFK_KP6 }, + { "K_KP7", QFK_KP7 }, + { "K_KP8", QFK_KP8 }, + { "K_KP9", QFK_KP9 }, + { "K_KP_PERIOD", QFK_KP_PERIOD }, + { "K_KP_DIVIDE", QFK_KP_DIVIDE }, + { "K_KP_MULTIPLY", QFK_KP_MULTIPLY }, + { "K_KP_MINUS", QFK_KP_MINUS }, + { "K_KP_PLUS", QFK_KP_PLUS }, + { "K_KP_ENTER", QFK_KP_ENTER }, + { "K_KP_EQUALS", QFK_KP_EQUALS }, + { "K_UP", QFK_UP }, + { "K_DOWN", QFK_DOWN }, + { "K_RIGHT", QFK_RIGHT }, + { "K_LEFT", QFK_LEFT }, + { "K_INSERT", QFK_INSERT }, + { "K_HOME", QFK_HOME }, + { "K_END", QFK_END }, + { "K_PAGEUP", QFK_PAGEUP }, + { "K_PAGEDOWN", QFK_PAGEDOWN }, + { "K_F1", QFK_F1 }, + { "K_F2", QFK_F2 }, + { "K_F3", QFK_F3 }, + { "K_F4", QFK_F4 }, + { "K_F5", QFK_F5 }, + { "K_F6", QFK_F6 }, + { "K_F7", QFK_F7 }, + { "K_F8", QFK_F8 }, + { "K_F9", QFK_F9 }, + { "K_F10", QFK_F10 }, + { "K_F11", QFK_F11 }, + { "K_F12", QFK_F12 }, + { "K_F13", QFK_F13 }, + { "K_F14", QFK_F14 }, + { "K_F15", QFK_F15 }, + { "K_F16", QFK_F16 }, + { "K_F17", QFK_F17 }, + { "K_F18", QFK_F18 }, + { "K_F19", QFK_F19 }, + { "K_F20", QFK_F20 }, + { "K_F21", QFK_F21 }, + { "K_F22", QFK_F22 }, + { "K_F23", QFK_F23 }, + { "K_F24", QFK_F24 }, + { "K_F25", QFK_F25 }, + { "K_F26", QFK_F26 }, + { "K_F27", QFK_F27 }, + { "K_F28", QFK_F28 }, + { "K_F29", QFK_F29 }, + { "K_F30", QFK_F30 }, + { "K_F31", QFK_F31 }, + { "K_F32", QFK_F32 }, + { "K_F33", QFK_F33 }, + { "K_F34", QFK_F34 }, + { "K_F35", QFK_F35 }, + { "K_F36", QFK_F36 }, + { "K_F37", QFK_F37 }, + { "K_F38", QFK_F38 }, + { "K_F39", QFK_F39 }, + { "K_F40", QFK_F40 }, + { "K_F41", QFK_F41 }, + { "K_F42", QFK_F42 }, + { "K_F43", QFK_F43 }, + { "K_F44", QFK_F44 }, + { "K_F45", QFK_F45 }, + { "K_F46", QFK_F46 }, + { "K_F47", QFK_F47 }, + { "K_F48", QFK_F48 }, + { "K_NUMLOCK", QFK_NUMLOCK }, + { "K_CAPSLOCK", QFK_CAPSLOCK }, + { "K_SCROLLOCK", QFK_SCROLLOCK }, + { "K_RSHIFT", QFK_RSHIFT }, + { "K_LSHIFT", QFK_LSHIFT }, + { "K_RCTRL", QFK_RCTRL }, + { "K_LCTRL", QFK_LCTRL }, + { "K_RALT", QFK_RALT }, + { "K_LALT", QFK_LALT }, + { "K_RMETA", QFK_RMETA }, + { "K_LMETA", QFK_LMETA }, + { "K_LSUPER", QFK_LSUPER }, + { "K_RSUPER", QFK_RSUPER }, + { "K_MODE", QFK_MODE }, + { "K_COMPOSE", QFK_COMPOSE }, + { "K_HELP", QFK_HELP }, + { "K_PRINT", QFK_PRINT }, + { "K_SYSREQ", QFK_SYSREQ }, + { "K_BREAK", QFK_BREAK }, + { "K_MENU", QFK_MENU }, + { "K_POWER", QFK_POWER }, + { "K_EURO", QFK_EURO }, + + { "K_KANJI", QFK_KANJI }, + { "K_MUHENKAN", QFK_MUHENKAN }, + { "K_HENKAN", QFK_HENKAN }, + { "K_ROMAJI", QFK_ROMAJI }, + { "K_HIRAGANA", QFK_HIRAGANA }, + { "K_KATAKANA", QFK_KATAKANA }, + { "K_HIRAGANA_KATAKANA", QFK_HIRAGANA_KATAKANA }, + { "K_ZENKAKU", QFK_ZENKAKU }, + { "K_HANKAKU", QFK_HANKAKU }, + { "K_ZENKAKU_HANKAKU", QFK_ZENKAKU_HANKAKU }, + { "K_TOUROKU", QFK_TOUROKU }, + { "K_MASSYO", QFK_MASSYO }, + { "K_KANA_LOCK", QFK_KANA_LOCK }, + { "K_KANA_SHIFT", QFK_KANA_SHIFT }, + { "K_EISU_SHIFT", QFK_EISU_SHIFT }, + { "K_EISU_TOGGLE", QFK_EISU_TOGGLE }, + { "K_KANJI_BANGOU", QFK_KANJI_BANGOU }, + { "K_ZEN_KOHO", QFK_ZEN_KOHO }, + { "K_MAE_KOHO", QFK_MAE_KOHO }, + + { "K_HOMEPAGE", QFK_HOMEPAGE }, + { "K_SEARCH", QFK_SEARCH }, + { "K_MAIL", QFK_MAIL }, + { "K_FAVORITES", QFK_FAVORITES }, + { "K_AUDIOMUTE", QFK_AUDIOMUTE }, + { "K_AUDIOLOWERVOLUME", QFK_AUDIOLOWERVOLUME }, + { "K_AUDIORAISEVOLUME", QFK_AUDIORAISEVOLUME }, + { "K_AUDIOPLAY", QFK_AUDIOPLAY }, + { "K_CALCULATOR", QFK_CALCULATOR }, + { "K_UNDO", QFK_UNDO }, + { "K_REDO", QFK_REDO }, + { "K_NEW", QFK_NEW }, + { "K_RELOAD", QFK_RELOAD }, + { "K_OPEN", QFK_OPEN }, + { "K_CLOSE", QFK_CLOSE }, + { "K_REPLY", QFK_REPLY }, + { "K_MAILFORWARD", QFK_MAILFORWARD }, + { "K_SEND", QFK_SEND }, + { "K_SAVE", QFK_SAVE }, + { "K_BACK", QFK_BACK }, + { "K_FORWARD", QFK_FORWARD }, + + {NULL, 0} +}; + +VISIBLE int +Key_StringToKeynum (const char *str) +{ + keyname_t *kn; + + if (!str || !str[0]) + return -1; + + for (kn = keynames; kn->name; kn++) { + if (!strcasecmp (str, kn->name)) + return kn->keynum; + } + return -1; +} + +VISIBLE const char * +Key_KeynumToString (knum_t keynum) +{ + keyname_t *kn; + + if (keynum == (knum_t) -1) + return ""; + + for (kn = keynames; kn->name; kn++) + if (keynum == kn->keynum) + return kn->name; + + return ""; +} diff --git a/libs/models/Makefile.am b/libs/models/Makefile.am deleted file mode 100644 index 2fa145649..000000000 --- a/libs/models/Makefile.am +++ /dev/null @@ -1,58 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -SUBDIRS= alias brush iqm sprite . test -AM_CFLAGS= @PREFER_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include - -lib_LTLIBRARIES= libQFmodels.la -noinst_LTLIBRARIES= @models_libs@ -EXTRA_LTLIBRARIES= libmodels_gl.la libmodels_glsl.la libmodels_sw.la - -lib_ldflags=-version-info $(QUAKE_LIBRARY_VERSION_INFO) \ - -rpath $(libdir) -no-undefined - -models_sources = clip_hull.c model.c portal.c trace.c winding.c - -common_libs = \ - $(top_builddir)/libs/util/libQFutil.la - -models_libs=brush/libbrush.la $(common_libs) - -libQFmodels_la_LDFLAGS= $(lib_ldflags) -libQFmodels_la_LIBADD= $(models_libs) -libQFmodels_la_DEPENDENCIES= $(models_libs) -libQFmodels_la_SOURCES= $(models_sources) - -gl_sources=gl_model_fullbright.c gl_skin.c skin.c -gl_libs= \ - alias/libalias_gl.la \ - brush/libbrush_gl.la \ - iqm/libiqm_gl.la \ - sprite/libsprite_gl.la \ - $(top_builddir)/libs/image/libQFimage.la -libmodels_gl_la_LDFLAGS= -libmodels_gl_la_LIBADD= $(gl_libs) -libmodels_gl_la_DEPENDENCIES= $(gl_libs) -libmodels_gl_la_SOURCES= $(gl_sources) - -glsl_libs= \ - alias/libalias_glsl.la \ - brush/libbrush_glsl.la \ - iqm/libiqm_glsl.la \ - sprite/libsprite_glsl.la \ - $(top_builddir)/libs/image/libQFimage.la -libmodels_glsl_la_LDFLAGS= -libmodels_glsl_la_LIBADD= $(glsl_libs) -libmodels_glsl_la_DEPENDENCIES= $(glsl_libs) -libmodels_glsl_la_SOURCES= glsl_skin.c skin.c - -sw_libs= \ - alias/libalias_sw.la \ - brush/libbrush_sw.la \ - iqm/libiqm_sw.la \ - sprite/libsprite_sw.la \ - $(top_builddir)/libs/image/libQFimage.la -libmodels_sw_la_LDFLAGS= -libmodels_sw_la_LIBADD= $(sw_libs) -libmodels_sw_la_DEPENDENCIES= $(sw_libs) -libmodels_sw_la_SOURCES= sw_skin.c skin.c diff --git a/libs/models/Makemodule.am b/libs/models/Makemodule.am new file mode 100644 index 000000000..069066ca8 --- /dev/null +++ b/libs/models/Makemodule.am @@ -0,0 +1,76 @@ +include libs/models/alias/Makemodule.am +include libs/models/brush/Makemodule.am +include libs/models/iqm/Makemodule.am +include libs/models/sprite/Makemodule.am +include libs/models/test/Makemodule.am + +lib_LTLIBRARIES += libs/models/libQFmodels.la +noinst_LTLIBRARIES += @models_libs@ +EXTRA_LTLIBRARIES += \ + libs/models/libmodels_gl.la \ + libs/models/libmodels_glsl.la \ + libs/models/libmodels_sw.la \ + libs/models/libmodels_vulkan.la + +models_sources = \ + libs/models/clip_hull.c \ + libs/models/fullbright.c \ + libs/models/model.c \ + libs/models/portal.c \ + libs/models/trace.c \ + libs/models/winding.c + +common_libs = \ + libs/util/libQFutil.la + +models_libs=libs/models/brush/libbrush.la $(common_libs) + +libs_models_libQFmodels_la_LDFLAGS= $(lib_ldflags) +libs_models_libQFmodels_la_LIBADD= $(models_libs) +libs_models_libQFmodels_la_DEPENDENCIES= $(models_libs) +libs_models_libQFmodels_la_SOURCES= $(models_sources) + +gl_sources=libs/models/gl_model_fullbright.c libs/models/gl_skin.c libs/models/skin.c +gl_libs= \ + libs/models/alias/libalias_gl.la \ + libs/models/brush/libbrush_gl.la \ + libs/models/iqm/libiqm_gl.la \ + libs/models/sprite/libsprite_gl.la \ + libs/image/libQFimage.la +libs_models_libmodels_gl_la_LDFLAGS= +libs_models_libmodels_gl_la_LIBADD= $(gl_libs) +libs_models_libmodels_gl_la_DEPENDENCIES= $(gl_libs) +libs_models_libmodels_gl_la_SOURCES= $(gl_sources) + +glsl_libs= \ + libs/models/alias/libalias_glsl.la \ + libs/models/brush/libbrush_glsl.la \ + libs/models/iqm/libiqm_glsl.la \ + libs/models/sprite/libsprite_glsl.la \ + libs/image/libQFimage.la +libs_models_libmodels_glsl_la_LDFLAGS= +libs_models_libmodels_glsl_la_LIBADD= $(glsl_libs) +libs_models_libmodels_glsl_la_DEPENDENCIES= $(glsl_libs) +libs_models_libmodels_glsl_la_SOURCES= libs/models/glsl_skin.c libs/models/skin.c + +sw_libs= \ + libs/models/alias/libalias_sw.la \ + libs/models/brush/libbrush_sw.la \ + libs/models/iqm/libiqm_sw.la \ + libs/models/sprite/libsprite_sw.la \ + libs/image/libQFimage.la +libs_models_libmodels_sw_la_LDFLAGS= +libs_models_libmodels_sw_la_LIBADD= $(sw_libs) +libs_models_libmodels_sw_la_DEPENDENCIES= $(sw_libs) +libs_models_libmodels_sw_la_SOURCES= libs/models/sw_skin.c libs/models/skin.c + +vulkan_libs= \ + libs/models/alias/libalias_vulkan.la \ + libs/models/brush/libbrush_vulkan.la \ + libs/models/iqm/libiqm_vulkan.la \ + libs/models/sprite/libsprite_vulkan.la \ + libs/image/libQFimage.la +libs_models_libmodels_vulkan_la_LDFLAGS= +libs_models_libmodels_vulkan_la_LIBADD= $(vulkan_libs) +libs_models_libmodels_vulkan_la_DEPENDENCIES= $(vulkan_libs) +libs_models_libmodels_vulkan_la_SOURCES= libs/models/vulkan_skin.c libs/models/skin.c libs/models/fullbright.c diff --git a/libs/models/alias/Makefile.am b/libs/models/alias/Makefile.am deleted file mode 100644 index e74edfeda..000000000 --- a/libs/models/alias/Makefile.am +++ /dev/null @@ -1,20 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CFLAGS= @PREFER_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include - -noinst_LTLIBRARIES= @alias_libs@ -EXTRA_LTLIBRARIES=libalias_gl.la libalias_glsl.la libalias_sw.la - -alias_src= model_alias.c -gl_src= gl_mesh.c gl_model_alias.c floodfill.c -glsl_src= glsl_model_alias.c floodfill.c -sw_src= sw_model_alias.c - -libalias_gl_la_SOURCES= $(gl_src) $(alias_src) - -libalias_glsl_la_SOURCES= $(glsl_src) $(alias_src) - -libalias_sw_la_SOURCES= $(sw_src) $(alias_src) - -EXTRA_DIST= $(gl_src) $(glsl_src) $(sw_src) $(alias_src) diff --git a/libs/models/alias/Makemodule.am b/libs/models/alias/Makemodule.am new file mode 100644 index 000000000..71601b573 --- /dev/null +++ b/libs/models/alias/Makemodule.am @@ -0,0 +1,26 @@ +noinst_LTLIBRARIES += @alias_libs@ +EXTRA_LTLIBRARIES += \ + libs/models/alias/libalias_gl.la \ + libs/models/alias/libalias_glsl.la \ + libs/models/alias/libalias_sw.la \ + libs/models/alias/libalias_vulkan.la + +alias_src= libs/models/alias/model_alias.c +alias_gl_src= libs/models/alias/gl_mesh.c libs/models/alias/gl_model_alias.c libs/models/alias/floodfill.c +alias_glsl_src= libs/models/alias/glsl_model_alias.c libs/models/alias/floodfill.c +alias_sw_src= libs/models/alias/sw_model_alias.c +alias_vulkan_src= libs/models/alias/vulkan_model_alias.c libs/models/alias/floodfill.c + +libs_models_alias_libalias_gl_la_SOURCES= $(alias_gl_src) $(alias_src) + +libs_models_alias_libalias_glsl_la_SOURCES= $(alias_glsl_src) $(alias_src) + +libs_models_alias_libalias_sw_la_SOURCES= $(alias_sw_src) $(alias_src) +libs_models_alias_libalias_vulkan_la_SOURCES= $(alias_vulkan_src) $(alias_src) + +EXTRA_DIST += \ + $(alias_gl_src) \ + $(alias_glsl_src) \ + $(alias_sw_src) \ + $(alias_vulkan_src) \ + $(alias_src) diff --git a/libs/models/alias/floodfill.c b/libs/models/alias/floodfill.c index 29e73a1f5..e7ea195a9 100644 --- a/libs/models/alias/floodfill.c +++ b/libs/models/alias/floodfill.c @@ -82,7 +82,7 @@ Mod_FloodFillSkin (byte * skin, int skinwidth, int skinheight) } // can't fill to filled color or transparent color (used as visited marker) if ((fillcolor == filledcolor) || (fillcolor == 255)) { - Sys_MaskPrintf (SYS_GLT, "not filling skin from %d to %d\n", + Sys_MaskPrintf (SYS_glt, "not filling skin from %d to %d\n", fillcolor, filledcolor); return; } diff --git a/libs/models/alias/gl_mesh.c b/libs/models/alias/gl_mesh.c index 22498ddea..ef68d3e07 100644 --- a/libs/models/alias/gl_mesh.c +++ b/libs/models/alias/gl_mesh.c @@ -49,10 +49,7 @@ // ALIAS MODEL DISPLAY LIST GENERATION ======================================== -static model_t *aliasmodel; -static aliashdr_t *paliashdr; - -static qboolean *used; +static int *used; static int used_size; // the command list holds counts and s/t values that are valid for every frame @@ -126,14 +123,15 @@ add_strip (int vert, int tri) } static int -StripLength (int starttri, int startv) +StripLength (mod_alias_ctx_t *alias_ctx, int starttri, int startv) { + aliashdr_t *header = alias_ctx->header; int m1, m2, j, k; mtriangle_t *last, *check; used[starttri] = 2; - last = &triangles[starttri]; + last = &alias_ctx->triangles.a[starttri]; stripcount = 0; add_strip (last->vertindex[(startv) % 3], starttri); @@ -145,8 +143,8 @@ StripLength (int starttri, int startv) // look for a matching triangle nexttri: - for (j = starttri + 1, check = &triangles[starttri + 1]; - j < pheader->mdl.numtris; j++, check++) { + for (j = starttri + 1, check = &alias_ctx->triangles.a[starttri + 1]; + j < header->mdl.numtris; j++, check++) { if (check->facesfront != last->facesfront) continue; for (k = 0; k < 3; k++) { @@ -176,7 +174,7 @@ nexttri: done: // clear the temp used flags - for (j = starttri + 1; j < pheader->mdl.numtris; j++) + for (j = starttri + 1; j < header->mdl.numtris; j++) if (used[j] == 2) used[j] = 0; @@ -184,14 +182,15 @@ done: } static int -FanLength (int starttri, int startv) +FanLength (mod_alias_ctx_t *alias_ctx, int starttri, int startv) { + aliashdr_t *header = alias_ctx->header; int m1, m2, j, k; mtriangle_t *last, *check; used[starttri] = 2; - last = &triangles[starttri]; + last = &alias_ctx->triangles.a[starttri]; stripcount = 0; add_strip (last->vertindex[(startv) % 3], starttri); @@ -204,8 +203,8 @@ FanLength (int starttri, int startv) // look for a matching triangle nexttri: - for (j = starttri + 1, check = &triangles[starttri + 1]; - j < pheader->mdl.numtris; j++, check++) { + for (j = starttri + 1, check = &alias_ctx->triangles.a[starttri + 1]; + j < header->mdl.numtris; j++, check++) { if (check->facesfront != last->facesfront) continue; for (k = 0; k < 3; k++) { @@ -232,7 +231,7 @@ FanLength (int starttri, int startv) done: // clear the temp used flags - for (j = starttri + 1; j < pheader->mdl.numtris; j++) + for (j = starttri + 1; j < header->mdl.numtris; j++) if (used[j] == 2) used[j] = 0; @@ -246,8 +245,9 @@ FanLength (int starttri, int startv) for the model, which holds for all frames */ static void -BuildTris (void) +BuildTris (mod_alias_ctx_t *alias_ctx) { + aliashdr_t *header = alias_ctx->header; float s, t; int bestlen, len, startv, type, i, j, k; int besttype = 0; @@ -257,10 +257,10 @@ BuildTris (void) numorder = 0; numcommands = 0; stripcount = 0; - alloc_used (pheader->mdl.numtris); + alloc_used (header->mdl.numtris); memset (used, 0, used_size * sizeof (used[0])); - for (i = 0; i < pheader->mdl.numtris; i++) { + for (i = 0; i < header->mdl.numtris; i++) { // pick an unused triangle and start the trifan if (used[i]) continue; @@ -270,9 +270,9 @@ BuildTris (void) // type = 1; for (startv = 0; startv < 3; startv++) { if (type == 1) - len = StripLength (i, startv); + len = StripLength (alias_ctx, i, startv); else - len = FanLength (i, startv); + len = FanLength (alias_ctx, i, startv); if (len > bestlen) { besttype = type; bestlen = len; @@ -304,12 +304,13 @@ BuildTris (void) add_vertex (k); // emit s/t coords into the commands stream - s = stverts[k].s; - t = stverts[k].t; - if (!triangles[besttris[0]].facesfront && stverts[k].onseam) - s += pheader->mdl.skinwidth / 2; // on back side - s = (s + 0.5) / pheader->mdl.skinwidth; - t = (t + 0.5) / pheader->mdl.skinheight; + s = alias_ctx->stverts.a[k].s; + t = alias_ctx->stverts.a[k].t; + if (!alias_ctx->triangles.a[besttris[0]].facesfront + && alias_ctx->stverts.a[k].onseam) + s += header->mdl.skinwidth / 2; // on back side + s = (s + 0.5) / header->mdl.skinwidth; + t = (t + 0.5) / header->mdl.skinheight; memcpy (&tmp, &s, 4); add_command (tmp); @@ -320,11 +321,11 @@ BuildTris (void) add_command (0); // end of list marker - Sys_MaskPrintf (SYS_DEV, "%3i tri %3i vert %3i cmd\n", - pheader->mdl.numtris, numorder, numcommands); + Sys_MaskPrintf (SYS_dev, "%3i tri %3i vert %3i cmd\n", + header->mdl.numtris, numorder, numcommands); allverts += numorder; - alltris += pheader->mdl.numtris; + alltris += header->mdl.numtris; if (bestverts) free (bestverts); @@ -333,36 +334,33 @@ BuildTris (void) } void -gl_Mod_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, void *_m, +gl_Mod_MakeAliasModelDisplayLists (mod_alias_ctx_t *alias_ctx, void *_m, int _s, int extra) { + aliashdr_t *header = alias_ctx->header; dstring_t *cache, *fullpath; unsigned char model_digest[MDFOUR_DIGEST_BYTES]; unsigned char mesh_digest[MDFOUR_DIGEST_BYTES]; int i, j; int *cmds; QFile *f; - qboolean remesh = true; - qboolean do_cache = false; - - aliasmodel = m; - paliashdr = hdr; + bool remesh = true; + bool do_cache = false; cache = dstring_new (); fullpath = dstring_new (); - if (!gl_alias_render_tri->int_val) { + if (!gl_alias_render_tri) { - if (gl_mesh_cache->int_val - && gl_mesh_cache->int_val <= paliashdr->mdl.numtris) { + if (gl_mesh_cache && gl_mesh_cache <= header->mdl.numtris) { do_cache = true; mdfour (model_digest, (unsigned char *) _m, _s); // look for a cached version dstring_copystr (cache, "glquake/"); - dstring_appendstr (cache, m->name); - QFS_StripExtension (m->name + strlen ("progs/"), + dstring_appendstr (cache, alias_ctx->mod->path); + QFS_StripExtension (alias_ctx->mod->path + strlen ("progs/"), cache->str + strlen ("glquake/")); dstring_appendstr (cache, ".qfms"); @@ -433,9 +431,9 @@ gl_Mod_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, void *_m, } if (remesh) { // build it from scratch - Sys_MaskPrintf (SYS_DEV, "meshing %s...\n", m->name); + Sys_MaskPrintf (SYS_dev, "meshing %s...\n", alias_ctx->mod->path); - BuildTris (); // trifans or lists + BuildTris (alias_ctx); // trifans or lists if (do_cache) { // save out the cached version @@ -474,35 +472,36 @@ gl_Mod_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, void *_m, } // save the data out - paliashdr->poseverts = numorder; + header->poseverts = numorder; - cmds = Hunk_Alloc (numcommands * sizeof (int)); - paliashdr->commands = (byte *) cmds - (byte *) paliashdr; + cmds = Hunk_Alloc (0, numcommands * sizeof (int)); + header->commands = (byte *) cmds - (byte *) header; memcpy (cmds, commands, numcommands * sizeof (int)); } else { tex_coord_t *tex_coord; numorder = 0; - for (i=0; i < pheader->mdl.numtris; i++) { - add_vertex(triangles[i].vertindex[0]); - add_vertex(triangles[i].vertindex[1]); - add_vertex(triangles[i].vertindex[2]); + for (i=0; i < header->mdl.numtris; i++) { + add_vertex(alias_ctx->triangles.a[i].vertindex[0]); + add_vertex(alias_ctx->triangles.a[i].vertindex[1]); + add_vertex(alias_ctx->triangles.a[i].vertindex[2]); } - paliashdr->poseverts = numorder; + header->poseverts = numorder; - tex_coord = Hunk_Alloc (numorder * sizeof(tex_coord_t)); - paliashdr->tex_coord = (byte *) tex_coord - (byte *) paliashdr; + tex_coord = Hunk_Alloc (0, numorder * sizeof(tex_coord_t)); + header->tex_coord = (byte *) tex_coord - (byte *) header; for (i=0; i < numorder; i++) { float s, t; int k; k = vertexorder[i]; - s = stverts[k].s; - t = stverts[k].t; - if (!triangles[i/3].facesfront && stverts[k].onseam) - s += pheader->mdl.skinwidth / 2; // on back side - s = (s + 0.5) / pheader->mdl.skinwidth; - t = (t + 0.5) / pheader->mdl.skinheight; + s = alias_ctx->stverts.a[k].s; + t = alias_ctx->stverts.a[k].t; + if (!alias_ctx->triangles.a[i/3].facesfront + && alias_ctx->stverts.a[k].onseam) + s += header->mdl.skinwidth / 2; // on back side + s = (s + 0.5) / header->mdl.skinwidth; + t = (t + 0.5) / header->mdl.skinheight; tex_coord[i].st[0] = s; tex_coord[i].st[1] = t; } @@ -510,11 +509,11 @@ gl_Mod_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, void *_m, if (extra) { trivertx16_t *verts; - verts = Hunk_Alloc (paliashdr->numposes * paliashdr->poseverts + verts = Hunk_Alloc (0, header->numposes * header->poseverts * sizeof (trivertx16_t)); - paliashdr->posedata = (byte *) verts - (byte *) paliashdr; - for (i = 0; i < paliashdr->numposes; i++) { - trivertx_t *pv = poseverts[i]; + header->posedata = (byte *) verts - (byte *) header; + for (i = 0; i < header->numposes; i++) { + trivertx_t *pv = alias_ctx->poseverts.a[i]; for (j = 0; j < numorder; j++) { trivertx16_t v; // convert MD16's split coordinates into something a little @@ -523,21 +522,21 @@ gl_Mod_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, void *_m, // fractional bits of the vertex, giving 8.8. However, it's // easier for us to multiply everything by 256 and adjust the // model scale appropriately - VectorMultAdd (pv[vertexorder[j] + hdr->mdl.numverts].v, + VectorMultAdd (pv[vertexorder[j] + header->mdl.numverts].v, 256, pv[vertexorder[j]].v, v.v); v.lightnormalindex = - poseverts[i][vertexorder[j]].lightnormalindex; + alias_ctx->poseverts.a[i][vertexorder[j]].lightnormalindex; *verts++ = v; } } } else { trivertx_t *verts; - verts = Hunk_Alloc (paliashdr->numposes * paliashdr->poseverts + verts = Hunk_Alloc (0, header->numposes * header->poseverts * sizeof (trivertx_t)); - paliashdr->posedata = (byte *) verts - (byte *) paliashdr; - for (i = 0; i < paliashdr->numposes; i++) { + header->posedata = (byte *) verts - (byte *) header; + for (i = 0; i < header->numposes; i++) { for (j = 0; j < numorder; j++) - *verts++ = poseverts[i][vertexorder[j]]; + *verts++ = alias_ctx->poseverts.a[i][vertexorder[j]]; } } dstring_delete (cache); diff --git a/libs/models/alias/gl_model_alias.c b/libs/models/alias/gl_model_alias.c index ec304b80d..4dee2b98e 100644 --- a/libs/models/alias/gl_model_alias.c +++ b/libs/models/alias/gl_model_alias.c @@ -38,6 +38,7 @@ # include #endif +#include "QF/dstring.h" #include "QF/image.h" #include "QF/qendian.h" #include "QF/quakefs.h" @@ -51,64 +52,90 @@ #include "compat.h" -void * -gl_Mod_LoadSkin (byte * skin, int skinsize, int snum, int gnum, qboolean group, - maliasskindesc_t *skindesc) +static void +gl_alias_clear (model_t *m, void *data) { - byte *pskin; - char name[32], modname[MAX_QPATH + 4]; + m->needload = true; + + Cache_Free (&m->cache); +} + +static void +gl_Mod_LoadSkin (mod_alias_ctx_t *alias_ctx, byte *texels, + int snum, int gnum, maliasskindesc_t *skindesc) +{ + aliashdr_t *header = alias_ctx->header; + char modname[MAX_QPATH + 4]; int fb_texnum = 0, texnum = 0; + dstring_t *name = dstring_new (); - pskin = Hunk_AllocName (skinsize, loadname); - skindesc->skin = (byte *) pskin - (byte *) pheader; - - memcpy (pskin, skin, skinsize); - - Mod_FloodFillSkin (pskin, pheader->mdl.skinwidth, pheader->mdl.skinheight); + Mod_FloodFillSkin (texels, header->mdl.skinwidth, header->mdl.skinheight); // save 8 bit texels for the player model to remap // FIXME remove model restriction - if (strequal (loadmodel->name, "progs/player.mdl")) - gl_Skin_SetPlayerSkin (pheader->mdl.skinwidth, pheader->mdl.skinheight, - pskin); + if (strequal (alias_ctx->mod->path, "progs/player.mdl")) + gl_Skin_SetPlayerSkin (header->mdl.skinwidth, header->mdl.skinheight, + texels); - QFS_StripExtension (loadmodel->name, modname); + QFS_StripExtension (alias_ctx->mod->path, modname); - if (!loadmodel->fullbright) { - if (group) { - snprintf (name, sizeof (name), "fb_%s_%i_%i", modname, - snum, gnum); + if (!alias_ctx->mod->fullbright) { + if (gnum != -1) { + dsprintf (name, "fb_%s_%i_%i", modname, snum, gnum); } else { - snprintf (name, sizeof (name), "fb_%s_%i", modname, snum); + dsprintf (name, "fb_%s_%i", modname, snum); } - fb_texnum = Mod_Fullbright (pskin, pheader->mdl.skinwidth, - pheader->mdl.skinheight, name); - Sys_MaskPrintf (SYS_GLT, "%s %d\n", name, fb_texnum); + fb_texnum = Mod_Fullbright (texels, header->mdl.skinwidth, + header->mdl.skinheight, name->str); + Sys_MaskPrintf (SYS_glt, "%s %d\n", name->str, fb_texnum); } - if (group) { - snprintf (name, sizeof (name), "%s_%i_%i", modname, snum, - gnum); + if (gnum != -1) { + dsprintf (name, "%s_%i_%i", modname, snum, gnum); } else { - snprintf (name, sizeof (name), "%s_%i", modname, snum); + dsprintf (name, "%s_%i", modname, snum); } - texnum = GL_LoadTexture (name, pheader->mdl.skinwidth, - pheader->mdl.skinheight, pskin, true, false, 1); - Sys_MaskPrintf (SYS_GLT, "%s %d\n", name, texnum); + texnum = GL_LoadTexture (name->str, header->mdl.skinwidth, + header->mdl.skinheight, texels, true, false, 1); + Sys_MaskPrintf (SYS_glt, "%s %d\n", name->str, texnum); skindesc->texnum = texnum; skindesc->fb_texnum = fb_texnum; - loadmodel->hasfullbrights = fb_texnum; + alias_ctx->mod->hasfullbrights = fb_texnum; + dstring_delete (name); // alpha param was true for non group skins - return skin + skinsize; } void -gl_Mod_FinalizeAliasModel (model_t *m, aliashdr_t *hdr) +gl_Mod_LoadAllSkins (mod_alias_ctx_t *alias_ctx) { - if (strequal (m->name, "progs/eyes.mdl")) { - hdr->mdl.scale_origin[2] -= (22 + 8); - VectorScale (hdr->mdl.scale, 2, hdr->mdl.scale); + aliashdr_t *header = alias_ctx->header; + int skinsize = header->mdl.skinwidth * header->mdl.skinheight; + int num_skins = alias_ctx->skins.size; + byte *texel_block = Hunk_AllocName (0, skinsize * num_skins, + alias_ctx->mod->name); + + for (int i = 0; i < num_skins; i++) { + __auto_type skin = alias_ctx->skins.a + i; + byte *texels = texel_block + i * skinsize; + + skin->skindesc->skin = texels - (byte *) header; + memcpy (texels, skin->texels, skinsize); + gl_Mod_LoadSkin (alias_ctx, texels, skin->skin_num, skin->group_num, + skin->skindesc); } } +void +gl_Mod_FinalizeAliasModel (mod_alias_ctx_t *alias_ctx) +{ + aliashdr_t *header = alias_ctx->header; + + if (strequal (alias_ctx->mod->path, "progs/eyes.mdl")) { + header->mdl.scale_origin[2] -= (22 + 8); + VectorScale (header->mdl.scale, 2, header->mdl.scale); + } + + alias_ctx->mod->clear = gl_alias_clear; +} + static void Mod_LoadExternalSkin (maliasskindesc_t *pskindesc, char *filename) { @@ -119,9 +146,9 @@ Mod_LoadExternalSkin (maliasskindesc_t *pskindesc, char *filename) if (!ptr) ptr = filename; - tex = LoadImage (filename); + tex = LoadImage (filename, 1); if (!tex) - tex = LoadImage (va ("textures/%s", ptr + 1)); + tex = LoadImage (va (0, "textures/%s", ptr + 1), 1); if (tex) { pskindesc->texnum = GL_LoadTexture (filename, tex->width, tex->height, tex->data, true, false, @@ -129,49 +156,50 @@ Mod_LoadExternalSkin (maliasskindesc_t *pskindesc, char *filename) pskindesc->fb_texnum = 0; - glow = LoadImage (va ("%s_luma", filename)); + glow = LoadImage (va (0, "%s_luma", filename), 1); if (!glow) - glow = LoadImage (va ("%s_glow", filename)); + glow = LoadImage (va (0, "%s_glow", filename), 1); if (!glow) - glow = LoadImage (va ("textures/%s_luma", ptr + 1)); + glow = LoadImage (va (0, "textures/%s_luma", ptr + 1), 1); if (!glow) - glow = LoadImage (va ("textures/%s_glow", ptr + 1)); + glow = LoadImage (va (0, "textures/%s_glow", ptr + 1), 1); if (glow) pskindesc->fb_texnum = - GL_LoadTexture (va ("fb_%s", filename), glow->width, + GL_LoadTexture (va (0, "fb_%s", filename), glow->width, glow->height, glow->data, true, true, glow->format > 2 ? glow->format : 1); else if (tex->format < 3) pskindesc->fb_texnum = Mod_Fullbright (tex->data, tex->width, tex->height, - va ("fb_%s", filename)); + va (0, "fb_%s", filename)); } } void -gl_Mod_LoadExternalSkins (model_t *mod) +gl_Mod_LoadExternalSkins (mod_alias_ctx_t *alias_ctx) { - char filename[MAX_QPATH + 4], modname[MAX_QPATH + 4]; + aliashdr_t *header = alias_ctx->header; + char modname[MAX_QPATH + 4]; int i, j; maliasskindesc_t *pskindesc; maliasskingroup_t *pskingroup; + dstring_t *filename = dstring_new (); - QFS_StripExtension (mod->name, modname); + QFS_StripExtension (alias_ctx->mod->path, modname); - for (i = 0; i < pheader->mdl.numskins; i++) { + for (i = 0; i < header->mdl.numskins; i++) { pskindesc = ((maliasskindesc_t *) - ((byte *) pheader + pheader->skindesc)) + i; + ((byte *) header + header->skindesc)) + i; if (pskindesc->type == ALIAS_SKIN_SINGLE) { - snprintf (filename, sizeof (filename), "%s_%i", modname, i); - Mod_LoadExternalSkin (pskindesc, filename); + dsprintf (filename, "%s_%i", modname, i); + Mod_LoadExternalSkin (pskindesc, filename->str); } else { pskingroup = (maliasskingroup_t *) - ((byte *) pheader + pskindesc->skin); + ((byte *) header + pskindesc->skin); for (j = 0; j < pskingroup->numskins; j++) { - snprintf (filename, sizeof (filename), "%s_%i_%i", - modname, i, j); - Mod_LoadExternalSkin (pskingroup->skindescs + j, filename); + dsprintf (filename, "%s_%i_%i", modname, i, j); + Mod_LoadExternalSkin (pskingroup->skindescs + j, filename->str); } } } diff --git a/libs/models/alias/glsl_model_alias.c b/libs/models/alias/glsl_model_alias.c index 5e80ccbb8..4e04b419d 100644 --- a/libs/models/alias/glsl_model_alias.c +++ b/libs/models/alias/glsl_model_alias.c @@ -48,6 +48,7 @@ #include "QF/GLSL/qf_textures.h" #include "mod_internal.h" +#include "qfalloca.h" #include "r_shared.h" static vec3_t vertex_normals[NUMVERTEXNORMALS] = { @@ -55,27 +56,27 @@ static vec3_t vertex_normals[NUMVERTEXNORMALS] = { }; static void -glsl_alias_clear (model_t *m) +glsl_alias_clear (model_t *m, void *data) { int i, j; - aliashdr_t *hdr; + aliashdr_t *header; GLuint bufs[2]; maliasskindesc_t *skins; maliasskingroup_t *group; m->needload = true; - if (!(hdr = m->aliashdr)) - hdr = Cache_Get (&m->cache); + if (!(header = m->aliashdr)) + header = Cache_Get (&m->cache); - bufs[0] = hdr->posedata; - bufs[1] = hdr->commands; + bufs[0] = header->posedata; + bufs[1] = header->commands; qfeglDeleteBuffers (2, bufs); - skins = ((maliasskindesc_t *) ((byte *) hdr + hdr->skindesc)); - for (i = 0; i < hdr->mdl.numskins; i++) { + skins = ((maliasskindesc_t *) ((byte *) header + header->skindesc)); + for (i = 0; i < header->mdl.numskins; i++) { if (skins[i].type == ALIAS_SKIN_GROUP) { - group = (maliasskingroup_t *) ((byte *) hdr + skins[i].skin); + group = (maliasskingroup_t *) ((byte *) header + skins[i].skin); for (j = 0; j < group->numskins; j++) { GLSL_ReleaseTexture (group->skindescs[j].texnum); } @@ -90,45 +91,56 @@ glsl_alias_clear (model_t *m) } } -void * -glsl_Mod_LoadSkin (byte *skin, int skinsize, int snum, int gnum, - qboolean group, maliasskindesc_t *skindesc) +static void +glsl_Mod_LoadSkin (mod_alias_ctx_t *alias_ctx, byte *texels, + int snum, int gnum, maliasskindesc_t *skindesc) { - byte *tskin; + aliashdr_t *header = alias_ctx->header; + int w = header->mdl.skinwidth; + int h = header->mdl.skinheight; + size_t skinsize = w * h; + byte *tskin = alloca (skinsize); const char *name; - int w, h; - w = pheader->mdl.skinwidth; - h = pheader->mdl.skinheight; - tskin = malloc (skinsize); - memcpy (tskin, skin, skinsize); + memcpy (tskin, texels, skinsize); Mod_FloodFillSkin (tskin, w, h); - if (group) - name = va ("%s_%i_%i", loadmodel->name, snum, gnum); + if (gnum != -1) + name = va (0, "%s_%i_%i", alias_ctx->mod->path, snum, gnum); else - name = va ("%s_%i", loadmodel->name, snum); + name = va (0, "%s_%i", alias_ctx->mod->path, snum); skindesc->texnum = GLSL_LoadQuakeTexture (name, w, h, tskin); - free (tskin); - return skin + skinsize; } void -glsl_Mod_FinalizeAliasModel (model_t *m, aliashdr_t *hdr) +glsl_Mod_LoadAllSkins (mod_alias_ctx_t *alias_ctx) { - if (hdr->mdl.ident == HEADER_MDL16) - VectorScale (hdr->mdl.scale, 1/256.0, hdr->mdl.scale); - m->clear = glsl_alias_clear; + for (size_t i = 0; i < alias_ctx->skins.size; i++) { + __auto_type skin = alias_ctx->skins.a + i; + glsl_Mod_LoadSkin (alias_ctx, skin->texels, + skin->skin_num, skin->group_num, skin->skindesc); + } } void -glsl_Mod_LoadExternalSkins (model_t *mod) +glsl_Mod_FinalizeAliasModel (mod_alias_ctx_t *alias_ctx) +{ + aliashdr_t *header = alias_ctx->header; + + if (header->mdl.ident == HEADER_MDL16) + VectorScale (header->mdl.scale, 1/256.0, header->mdl.scale); + alias_ctx->mod->clear = glsl_alias_clear; +} + +void +glsl_Mod_LoadExternalSkins (mod_alias_ctx_t *alias_ctx) { } void -glsl_Mod_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, void *_m, +glsl_Mod_MakeAliasModelDisplayLists (mod_alias_ctx_t *alias_ctx, void *_m, int _s, int extra) { + aliashdr_t *header = alias_ctx->header; mtriangle_t *tris; stvert_t *st; aliasvrt_t *verts; @@ -142,12 +154,12 @@ glsl_Mod_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, void *_m, int i, j; int pose; - numverts = hdr->mdl.numverts; - numtris = hdr->mdl.numtris; + numverts = header->mdl.numverts; + numtris = header->mdl.numtris; // copy triangles before editing them tris = malloc (numtris * sizeof (mtriangle_t)); - memcpy (tris, triangles, numtris * sizeof (mtriangle_t)); + memcpy (tris, alias_ctx->triangles.a, numtris * sizeof (mtriangle_t)); // initialize indexmap to -1 (unduplicated). any other value indicates // both that the vertex has been duplicated and the index of the @@ -157,7 +169,7 @@ glsl_Mod_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, void *_m, // copy stverts. need space for duplicates st = malloc (2 * numverts * sizeof (stvert_t)); - memcpy (st, stverts, numverts * sizeof (stvert_t)); + memcpy (st, alias_ctx->stverts.a, numverts * sizeof (stvert_t)); // check for onseam verts, and duplicate any that are associated with // back-facing triangles. the s coordinate is shifted right by half @@ -168,7 +180,7 @@ glsl_Mod_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, void *_m, if (st[vind].onseam && !tris[i].facesfront) { if (indexmap[vind] == -1) { st[numverts] = st[vind]; - st[numverts].s += hdr->mdl.skinwidth / 2; + st[numverts].s += header->mdl.skinwidth / 2; indexmap[vind] = numverts++; } tris[i].vertindex[j] = indexmap[vind]; @@ -178,13 +190,13 @@ glsl_Mod_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, void *_m, // we now know exactly how many vertices we need, so built the vertex // array - vertexsize = hdr->numposes * numverts * sizeof (aliasvrt_t); + vertexsize = header->numposes * numverts * sizeof (aliasvrt_t); verts = malloc (vertexsize); - for (i = 0, pose = 0; i < hdr->numposes; i++, pose += numverts) { - for (j = 0; j < hdr->mdl.numverts; j++) { - pv = &poseverts[i][j]; + for (i = 0, pose = 0; i < header->numposes; i++, pose += numverts) { + for (j = 0; j < header->mdl.numverts; j++) { + pv = &alias_ctx->poseverts.a[i][j]; if (extra) { - VectorMultAdd (pv[hdr->mdl.numverts].v, 256, pv->v, + VectorMultAdd (pv[header->mdl.numverts].v, 256, pv->v, verts[pose + j].vertex); } else { VectorCopy (pv->v, verts[pose + j].vertex); @@ -218,14 +230,14 @@ glsl_Mod_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, void *_m, // finished with tris free (tris); - hdr->poseverts = numverts; + header->poseverts = numverts; // load the vertex data and indices into GL qfeglGenBuffers (2, bnum); - hdr->posedata = bnum[0]; - hdr->commands = bnum[1]; - qfeglBindBuffer (GL_ARRAY_BUFFER, hdr->posedata); - qfeglBindBuffer (GL_ELEMENT_ARRAY_BUFFER, hdr->commands); + header->posedata = bnum[0]; + header->commands = bnum[1]; + qfeglBindBuffer (GL_ARRAY_BUFFER, header->posedata); + qfeglBindBuffer (GL_ELEMENT_ARRAY_BUFFER, header->commands); qfeglBufferData (GL_ARRAY_BUFFER, vertexsize, verts, GL_STATIC_DRAW); qfeglBufferData (GL_ELEMENT_ARRAY_BUFFER, indexsize, indices, GL_STATIC_DRAW); diff --git a/libs/models/alias/model_alias.c b/libs/models/alias/model_alias.c index af2731d71..a080fc0e9 100644 --- a/libs/models/alias/model_alias.c +++ b/libs/models/alias/model_alias.c @@ -49,23 +49,11 @@ #include "mod_internal.h" #include "r_local.h" -aliashdr_t *pheader; - -stvert_t *stverts; -mtriangle_t *triangles; -int stverts_size = 0; -int triangles_size = 0; - -// a pose is a single set of vertexes. a frame may be an animating -// sequence of poses -trivertx_t *poseverts[MAXALIASFRAMES]; -int posenum = 0; -int aliasbboxmins[3], aliasbboxmaxs[3]; - - static void * -Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype, int *pskinindex) +Mod_LoadAllSkins (mod_alias_ctx_t *alias_ctx, int numskins, + daliasskintype_t *pskintype, int *pskinindex) { + aliashdr_t *header = alias_ctx->header; byte *skin; float *poutskinintervals; int groupskins, skinsize, gnum, snum, t; @@ -77,34 +65,40 @@ Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype, int *pskinindex) if (numskins < 1 || numskins > MAX_SKINS) Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d", numskins); - skinsize = pheader->mdl.skinwidth * pheader->mdl.skinheight; - pskindesc = Hunk_AllocName (numskins * sizeof (maliasskindesc_t), - loadname); + skinsize = header->mdl.skinwidth * header->mdl.skinheight; + pskindesc = Hunk_AllocName (0, numskins * sizeof (maliasskindesc_t), + alias_ctx->mod->name); - *pskinindex = (byte *) pskindesc - (byte *) pheader; + *pskinindex = (byte *) pskindesc - (byte *) header; for (snum = 0; snum < numskins; snum++) { pskindesc[snum].type = pskintype->type; if (pskintype->type == ALIAS_SKIN_SINGLE) { skin = (byte *) (pskintype + 1); - skin = m_funcs->Mod_LoadSkin (skin, skinsize, snum, 0, false, - &pskindesc[snum]); + mod_alias_skin_t askin = { + .skin_num = snum, + .group_num = -1, + .texels = skin, + .skindesc = &pskindesc[snum], + }; + skin += skinsize; + DARRAY_APPEND (&alias_ctx->skins, askin); } else { pskintype++; pinskingroup = (daliasskingroup_t *) pskintype; groupskins = LittleLong (pinskingroup->numskins); t = field_offset (maliasskingroup_t, skindescs[groupskins]); - paliasskingroup = Hunk_AllocName (t, loadname); + paliasskingroup = Hunk_AllocName (0, t, alias_ctx->mod->name); paliasskingroup->numskins = groupskins; - pskindesc[snum].skin = (byte *) paliasskingroup - (byte *) pheader; + pskindesc[snum].skin = (byte *) paliasskingroup - (byte *) header; pinskinintervals = (daliasskininterval_t *) (pinskingroup + 1); - poutskinintervals = Hunk_AllocName (groupskins * sizeof (float), - loadname); + poutskinintervals = Hunk_AllocName (0, groupskins * sizeof (float), + alias_ctx->mod->name); paliasskingroup->intervals = - (byte *) poutskinintervals - (byte *) pheader; + (byte *) poutskinintervals - (byte *) header; for (gnum = 0; gnum < groupskins; gnum++) { *poutskinintervals = LittleFloat (pinskinintervals->interval); if (*poutskinintervals <= 0) @@ -119,26 +113,34 @@ Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype, int *pskinindex) for (gnum = 0; gnum < groupskins; gnum++) { paliasskingroup->skindescs[gnum].type = ALIAS_SKIN_SINGLE; - skin = mod_funcs->Mod_LoadSkin (skin, skinsize, snum, gnum, - true, &paliasskingroup->skindescs[gnum]); + mod_alias_skin_t askin = { + .skin_num = snum, + .group_num = gnum, + .texels = skin, + .skindesc = &paliasskingroup->skindescs[gnum], + }; + skin += skinsize; + DARRAY_APPEND (&alias_ctx->skins, askin); } } pskintype = (daliasskintype_t *) skin; } + mod_funcs->Mod_LoadAllSkins (alias_ctx); return pskintype; } -void * -Mod_LoadAliasFrame (void *pin, int *posenum, maliasframedesc_t *frame, - int extra) +static void * +Mod_LoadAliasFrame (mod_alias_ctx_t *alias_ctx, void *pin, int *posenum, + maliasframedesc_t *frame, int extra) { + aliashdr_t *header = alias_ctx->header; daliasframe_t *pdaliasframe; trivertx_t *pinframe; pdaliasframe = (daliasframe_t *) pin; - strncpy (frame->name, pdaliasframe->name, sizeof (frame->name)); + memcpy (frame->name, pdaliasframe->name, sizeof (frame->name)); frame->name[sizeof (frame->name) - 1] = 0; frame->firstpose = (*posenum); frame->numposes = 1; @@ -146,25 +148,29 @@ Mod_LoadAliasFrame (void *pin, int *posenum, maliasframedesc_t *frame, // byte values, don't worry about endianness VectorCopy (pdaliasframe->bboxmin.v, frame->bboxmin.v); VectorCopy (pdaliasframe->bboxmax.v, frame->bboxmax.v); - VectorCompMin (frame->bboxmin.v, aliasbboxmins, aliasbboxmins); - VectorCompMax (frame->bboxmax.v, aliasbboxmaxs, aliasbboxmaxs); + VectorCompMin (frame->bboxmin.v, alias_ctx->aliasbboxmins, + alias_ctx->aliasbboxmins); + VectorCompMax (frame->bboxmax.v, alias_ctx->aliasbboxmaxs, + alias_ctx->aliasbboxmaxs); pinframe = (trivertx_t *) (pdaliasframe + 1); - poseverts[(*posenum)] = pinframe; + DARRAY_APPEND (&alias_ctx->poseverts, pinframe); (*posenum)++; - pinframe += pheader->mdl.numverts; + pinframe += header->mdl.numverts; if (extra) - pinframe += pheader->mdl.numverts; + pinframe += header->mdl.numverts; return pinframe; } -void * -Mod_LoadAliasGroup (void *pin, int *posenum, maliasframedesc_t *frame, - int extra) +static void * +Mod_LoadAliasGroup (mod_alias_ctx_t *alias_ctx, void *pin, int *posenum, + maliasframedesc_t *frame, int extra) { + aliashdr_t *header = alias_ctx->header; + model_t *mod = alias_ctx->mod; daliasgroup_t *pingroup; daliasinterval_t *pin_intervals; float *poutintervals; @@ -179,20 +185,23 @@ Mod_LoadAliasGroup (void *pin, int *posenum, maliasframedesc_t *frame, frame->firstpose = (*posenum); frame->numposes = numframes; - paliasgroup = Hunk_AllocName (field_offset (maliasgroup_t, - frames[numframes]), loadname); + paliasgroup = Hunk_AllocName (0, field_offset (maliasgroup_t, + frames[numframes]), + mod->name); paliasgroup->numframes = numframes; - frame->frame = (byte *) paliasgroup - (byte *) pheader; + frame->frame = (byte *) paliasgroup - (byte *) header; // these are byte values, so we don't have to worry about endianness VectorCopy (pingroup->bboxmin.v, frame->bboxmin.v); VectorCopy (pingroup->bboxmax.v, frame->bboxmax.v); - VectorCompMin (frame->bboxmin.v, aliasbboxmins, aliasbboxmins); - VectorCompMax (frame->bboxmax.v, aliasbboxmaxs, aliasbboxmaxs); + VectorCompMin (frame->bboxmin.v, alias_ctx->aliasbboxmins, + alias_ctx->aliasbboxmins); + VectorCompMax (frame->bboxmax.v, alias_ctx->aliasbboxmaxs, + alias_ctx->aliasbboxmaxs); pin_intervals = (daliasinterval_t *) (pingroup + 1); - poutintervals = Hunk_AllocName (numframes * sizeof (float), loadname); - paliasgroup->intervals = (byte *) poutintervals - (byte *) pheader; + poutintervals = Hunk_AllocName (0, numframes * sizeof (float), mod->name); + paliasgroup->intervals = (byte *) poutintervals - (byte *) header; frame->interval = LittleFloat (pin_intervals->interval); for (i = 0; i < numframes; i++) { *poutintervals = LittleFloat (pin_intervals->interval); @@ -205,7 +214,8 @@ Mod_LoadAliasGroup (void *pin, int *posenum, maliasframedesc_t *frame, ptemp = (void *) pin_intervals; for (i = 0; i < numframes; i++) { maliasframedesc_t temp_frame; - ptemp = Mod_LoadAliasFrame (ptemp, posenum, &temp_frame, extra); + ptemp = Mod_LoadAliasFrame (alias_ctx, ptemp, posenum, &temp_frame, + extra); memcpy (&paliasgroup->frames[i], &temp_frame, sizeof (paliasgroup->frames[i])); } @@ -216,7 +226,8 @@ Mod_LoadAliasGroup (void *pin, int *posenum, maliasframedesc_t *frame, void Mod_LoadAliasModel (model_t *mod, void *buffer, cache_allocator_t allocator) { - int size, version, numframes, start, end, total, i, j; + size_t size, start, end, total; + int version, numframes, i, j; int extra = 0; // extra precision bytes void *mem; dtriangle_t *pintriangles; @@ -225,6 +236,15 @@ Mod_LoadAliasModel (model_t *mod, void *buffer, cache_allocator_t allocator) mdl_t *pinmodel, *pmodel; unsigned short crc; stvert_t *pinstverts; + mod_alias_ctx_t alias_ctx = {}; + aliashdr_t *header; + + alias_ctx.mod = mod; + //FIXME should be per batch rather than per model + DARRAY_INIT (&alias_ctx.poseverts, 256); + DARRAY_INIT (&alias_ctx.stverts, 256); + DARRAY_INIT (&alias_ctx.triangles, 256); + DARRAY_INIT (&alias_ctx.skins, 256); if (LittleLong (* (unsigned int *) buffer) == HEADER_MDL16) extra = 1; // extra precision bytes @@ -232,7 +252,7 @@ Mod_LoadAliasModel (model_t *mod, void *buffer, cache_allocator_t allocator) CRC_Init (&crc); CRC_ProcessBlock (buffer, &crc, qfs_filesize); - start = Hunk_LowMark (); + start = Hunk_LowMark (0); pinmodel = (mdl_t *) buffer; @@ -242,14 +262,15 @@ Mod_LoadAliasModel (model_t *mod, void *buffer, cache_allocator_t allocator) mod->name, version, ALIAS_VERSION_MDL); // allocate space for a working header, plus all the data except the - // frames, skin and group info + // frame data, skin and group info size = field_offset (aliashdr_t, frames[LittleLong (pinmodel->numframes)]); - pheader = Hunk_AllocName (size, loadname); - memset (pheader, 0, size); - pmodel = &pheader->mdl; - pheader->model = (byte *) pmodel - (byte *) pheader; + header = Hunk_AllocName (0, size, mod->name); + memset (header, 0, size); + alias_ctx.header = header; + pmodel = &header->mdl; + header->model = (byte *) pmodel - (byte *) header; - pheader->crc = crc; + header->crc = crc; mod->flags = LittleLong (pinmodel->flags); @@ -264,30 +285,20 @@ Mod_LoadAliasModel (model_t *mod, void *buffer, cache_allocator_t allocator) Sys_Error ("model %s has a skin taller than %d", mod->name, MAX_LBM_HEIGHT); + DARRAY_RESIZE (&alias_ctx.poseverts, 0); pmodel->numverts = LittleLong (pinmodel->numverts); if (pmodel->numverts <= 0) Sys_Error ("model %s has no vertices", mod->name); - if (pmodel->numverts > stverts_size) { - stverts = realloc (stverts, pmodel->numverts * sizeof (stvert_t)); - if (!stverts) - Sys_Error ("model_alias: out of memory"); - stverts_size = pmodel->numverts; - } + DARRAY_RESIZE (&alias_ctx.stverts, pmodel->numverts); pmodel->numtris = LittleLong (pinmodel->numtris); if (pmodel->numtris <= 0) Sys_Error ("model %s has no triangles", mod->name); - if (pmodel->numtris > triangles_size) { - triangles = realloc (triangles, - pmodel->numtris * sizeof (mtriangle_t)); - if (!triangles) - Sys_Error ("model_alias: out of memory"); - triangles_size = pmodel->numtris; - } + DARRAY_RESIZE (&alias_ctx.triangles, pmodel->numtris); pmodel->numframes = LittleLong (pinmodel->numframes); numframes = pmodel->numframes; @@ -306,86 +317,93 @@ Mod_LoadAliasModel (model_t *mod, void *buffer, cache_allocator_t allocator) // load the skins pskintype = (daliasskintype_t *) &pinmodel[1]; - pskintype = Mod_LoadAllSkins (pheader->mdl.numskins, pskintype, - &pheader->skindesc); + pskintype = Mod_LoadAllSkins (&alias_ctx, header->mdl.numskins, pskintype, + &header->skindesc); // load base s and t vertices pinstverts = (stvert_t *) pskintype; - for (i = 0; i < pheader->mdl.numverts; i++) { - stverts[i].onseam = LittleLong (pinstverts[i].onseam); - stverts[i].s = LittleLong (pinstverts[i].s); - stverts[i].t = LittleLong (pinstverts[i].t); + for (i = 0; i < header->mdl.numverts; i++) { + alias_ctx.stverts.a[i].onseam = LittleLong (pinstverts[i].onseam); + alias_ctx.stverts.a[i].s = LittleLong (pinstverts[i].s); + alias_ctx.stverts.a[i].t = LittleLong (pinstverts[i].t); } // load triangle lists - pintriangles = (dtriangle_t *) &pinstverts[pheader->mdl.numverts]; + pintriangles = (dtriangle_t *) &pinstverts[header->mdl.numverts]; - for (i = 0; i < pheader->mdl.numtris; i++) { - triangles[i].facesfront = LittleLong (pintriangles[i].facesfront); + for (i = 0; i < header->mdl.numtris; i++) { + alias_ctx.triangles.a[i].facesfront = + LittleLong (pintriangles[i].facesfront); for (j = 0; j < 3; j++) { - triangles[i].vertindex[j] = + alias_ctx.triangles.a[i].vertindex[j] = LittleLong (pintriangles[i].vertindex[j]); } } // load the frames - posenum = 0; - pframetype = (daliasframetype_t *) &pintriangles[pheader->mdl.numtris]; - aliasbboxmins[0] = aliasbboxmins[1] = aliasbboxmins[2] = 99999; - aliasbboxmaxs[0] = aliasbboxmaxs[1] = aliasbboxmaxs[2] = -99999; + int posenum = 0; + pframetype = (daliasframetype_t *) &pintriangles[header->mdl.numtris]; + VectorSet (99999, 99999, 99999, alias_ctx.aliasbboxmins); + VectorSet (-99999, -99999, -99999, alias_ctx.aliasbboxmaxs); for (i = 0; i < numframes; i++) { aliasframetype_t frametype; frametype = LittleLong (pframetype->type); - pheader->frames[i].type = frametype; + header->frames[i].type = frametype; if (frametype == ALIAS_SINGLE) { pframetype = (daliasframetype_t *) - Mod_LoadAliasFrame (pframetype + 1, &posenum, - &pheader->frames[i], extra); + Mod_LoadAliasFrame (&alias_ctx, pframetype + 1, &posenum, + &header->frames[i], extra); } else { pframetype = (daliasframetype_t *) - Mod_LoadAliasGroup (pframetype + 1, &posenum, - &pheader->frames[i], extra); + Mod_LoadAliasGroup (&alias_ctx, pframetype + 1, &posenum, + &header->frames[i], extra); } } - pheader->numposes = posenum; + header->numposes = posenum; mod->type = mod_alias; - for (i = 0; i < 3; i++) { - mod->mins[i] = aliasbboxmins[i] * pheader->mdl.scale[i] + - pheader->mdl.scale_origin[i]; - mod->maxs[i] = aliasbboxmaxs[i] * pheader->mdl.scale[i] + - pheader->mdl.scale_origin[i]; - } + VectorCompMultAdd (header->mdl.scale_origin, header->mdl.scale, + alias_ctx.aliasbboxmins, mod->mins); + VectorCompMultAdd (header->mdl.scale_origin, header->mdl.scale, + alias_ctx.aliasbboxmaxs, mod->maxs); mod->radius = RadiusFromBounds (mod->mins, mod->maxs); // build the draw lists - m_funcs->Mod_MakeAliasModelDisplayLists (mod, pheader, buffer, + m_funcs->Mod_MakeAliasModelDisplayLists (&alias_ctx, buffer, qfs_filesize, extra); - m_funcs->Mod_FinalizeAliasModel (mod, pheader); + if (m_funcs->Mod_FinalizeAliasModel) { + m_funcs->Mod_FinalizeAliasModel (&alias_ctx); + } - m_funcs->Mod_LoadExternalSkins (mod); + if (m_funcs->Mod_LoadExternalSkins) { + m_funcs->Mod_LoadExternalSkins (&alias_ctx); + } // move the complete, relocatable alias model to the cache if (m_funcs->alias_cache) { - end = Hunk_LowMark (); + end = Hunk_LowMark (0); total = end - start; - mem = allocator (&mod->cache, total, loadname); + mem = allocator (&mod->cache, total, mod->name); if (mem) - memcpy (mem, pheader, total); + memcpy (mem, header, total); - Hunk_FreeToLowMark (start); + Hunk_FreeToLowMark (0, start); mod->aliashdr = 0; } else { - mod->aliashdr = pheader; + mod->aliashdr = header; } + DARRAY_CLEAR (&alias_ctx.poseverts); + DARRAY_CLEAR (&alias_ctx.stverts); + DARRAY_CLEAR (&alias_ctx.triangles); + DARRAY_CLEAR (&alias_ctx.skins); } diff --git a/libs/models/alias/sw_model_alias.c b/libs/models/alias/sw_model_alias.c index b252ad474..f1f104462 100644 --- a/libs/models/alias/sw_model_alias.c +++ b/libs/models/alias/sw_model_alias.c @@ -45,92 +45,103 @@ #include "d_iface.h" #include "mod_internal.h" +static void +sw_alias_clear (model_t *m, void *data) +{ + m->needload = true; + + Cache_Free (&m->cache); +} + // a pose is a single set of vertexes. a frame may be // an animating sequence of poses - -void * -sw_Mod_LoadSkin (byte *skin, int skinsize, int snum, int gnum, - qboolean group, maliasskindesc_t *skindesc) +void +sw_Mod_LoadAllSkins (mod_alias_ctx_t *alias_ctx) { - byte *pskin; + aliashdr_t *header = alias_ctx->header; + int skinsize = header->mdl.skinwidth * header->mdl.skinheight; + int num_skins = alias_ctx->skins.size; + byte *texel_block = Hunk_AllocName (0, skinsize * num_skins, + alias_ctx->mod->name); - pskin = Hunk_AllocName (skinsize, loadname); - skindesc->skin = (byte *) pskin - (byte *) pheader; + for (size_t i = 0; i < alias_ctx->skins.size; i++) { + __auto_type skin = alias_ctx->skins.a + i; + byte *texels = texel_block + i * skinsize; - memcpy (pskin, skin, skinsize); + skin->skindesc->skin = texels - (byte *) header; + memcpy (texels, skin->texels, skinsize); + } +} - return skin + skinsize; +void +sw_Mod_FinalizeAliasModel (mod_alias_ctx_t *alias_ctx) +{ + alias_ctx->mod->clear = sw_alias_clear; } static void -process_frame (maliasframedesc_t *frame, int posenum, int extra) +process_frame (mod_alias_ctx_t *alias_ctx, maliasframedesc_t *frame, + int posenum, int extra) { - int size = pheader->mdl.numverts * sizeof (trivertx_t); + aliashdr_t *header = alias_ctx->header; + int size = header->mdl.numverts * sizeof (trivertx_t); trivertx_t *frame_verts; if (extra) size *= 2; - frame_verts = Hunk_AllocName (size, loadname); - frame->frame = (byte *) frame_verts - (byte *) pheader; + frame_verts = Hunk_AllocName (0, size, alias_ctx->mod->name); + frame->frame = (byte *) frame_verts - (byte *) header; // The low-order 8 bits (actually, fractional) are completely separate // from the high-order bits (see R_AliasTransformFinalVert16 in // sw_ralias.c), but in adjacant arrays. This means we can get away with // just one memcpy as there are no endian issues. - memcpy (frame_verts, poseverts[posenum], size); + memcpy (frame_verts, alias_ctx->poseverts.a[posenum], size); } void -sw_Mod_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, void *_m, +sw_Mod_MakeAliasModelDisplayLists (mod_alias_ctx_t *alias_ctx, void *_m, int _s, int extra) { + aliashdr_t *header = alias_ctx->header; int i, j; int posenum = 0; - int numv = hdr->mdl.numverts, numt = hdr->mdl.numtris; - stvert_t *pstverts; - mtriangle_t *ptri; + int numv = header->mdl.numverts, numt = header->mdl.numtris; + stvert_t *stverts; + mtriangle_t *tris; - pstverts = (stvert_t *) Hunk_AllocName (numv * sizeof (stvert_t), - loadname); - ptri = (mtriangle_t *) Hunk_AllocName (numt * sizeof (mtriangle_t), - loadname); + stverts = (stvert_t *) Hunk_AllocName (0, numv * sizeof (stvert_t), + alias_ctx->mod->name); + tris = (mtriangle_t *) Hunk_AllocName (0, numt * sizeof (mtriangle_t), + alias_ctx->mod->name); - hdr->stverts = (byte *) pstverts - (byte *) hdr; - hdr->triangles = (byte *) ptri - (byte *) hdr; + header->stverts = (byte *) stverts - (byte *) header; + header->triangles = (byte *) tris - (byte *) header; for (i = 0; i < numv; i++) { - pstverts[i].onseam = stverts[i].onseam; - pstverts[i].s = stverts[i].s << 16; - pstverts[i].t = stverts[i].t << 16; + stverts[i].onseam = alias_ctx->stverts.a[i].onseam; + stverts[i].s = alias_ctx->stverts.a[i].s << 16; + stverts[i].t = alias_ctx->stverts.a[i].t << 16; } for (i = 0; i < numt; i++) { - ptri[i].facesfront = triangles[i].facesfront; - VectorCopy (triangles[i].vertindex, ptri[i].vertindex); + tris[i].facesfront = alias_ctx->triangles.a[i].facesfront; + VectorCopy (alias_ctx->triangles.a[i].vertindex, tris[i].vertindex); } - for (i = 0; i < pheader->mdl.numframes; i++) { - maliasframedesc_t *frame = pheader->frames + i; + for (i = 0; i < header->mdl.numframes; i++) { + maliasframedesc_t *frame = header->frames + i; if (frame->type) { maliasgroup_t *group; - group = (maliasgroup_t *) ((byte *) pheader + frame->frame); - for (j = 0; j < group->numframes; j++) - process_frame ((maliasframedesc_t *) &group->frames[j], - posenum++, extra); + group = (maliasgroup_t *) ((byte *) header + frame->frame); + for (j = 0; j < group->numframes; j++) { + __auto_type frame = (maliasframedesc_t *) &group->frames[j]; + process_frame (alias_ctx, frame, posenum++, extra); + } } else { - process_frame (frame, posenum++, extra); + process_frame (alias_ctx, frame, posenum++, extra); } } } - -void -sw_Mod_FinalizeAliasModel (model_t *m, aliashdr_t *hdr) -{ -} - -void -sw_Mod_LoadExternalSkins (model_t *mod) -{ -} diff --git a/libs/models/alias/vulkan_model_alias.c b/libs/models/alias/vulkan_model_alias.c new file mode 100644 index 000000000..c39ee77d3 --- /dev/null +++ b/libs/models/alias/vulkan_model_alias.c @@ -0,0 +1,478 @@ +/* + vulkan_model_alais.c + + Alias model processing for Vulkan + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/1/24 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "QF/cvar.h" +#include "QF/va.h" + +#include "QF/modelgen.h" +#include "QF/vid.h" +#include "QF/Vulkan/qf_alias.h" +#include "QF/Vulkan/qf_texture.h" +#include "QF/Vulkan/barrier.h" +#include "QF/Vulkan/buffer.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/image.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/staging.h" + +#include "mod_internal.h" +#include "r_internal.h" +#include "vid_vulkan.h" + +static vec3_t vertex_normals[NUMVERTEXNORMALS] = { +#include "anorms.h" +}; + +static void +skin_clear (int skin_offset, aliashdr_t *hdr, vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + qfv_alias_skin_t *skin = (qfv_alias_skin_t *) ((byte *) hdr + skin_offset); + + Vulkan_AliasRemoveSkin (ctx, skin); + dfunc->vkDestroyImageView (device->dev, skin->view, 0); + dfunc->vkDestroyImage (device->dev, skin->image, 0); + dfunc->vkFreeMemory (device->dev, skin->memory, 0); +} + +static void +vulkan_alias_clear (model_t *m, void *data) +{ + vulkan_ctx_t *ctx = data; + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + aliashdr_t *hdr; + qfv_alias_mesh_t *mesh; + + QFV_DeviceWaitIdle (device); + + m->needload = true; //FIXME is this right? + if (!(hdr = m->aliashdr)) { + hdr = Cache_Get (&m->cache); + } + mesh = (qfv_alias_mesh_t *) ((byte *) hdr + hdr->commands); + dfunc->vkDestroyBuffer (device->dev, mesh->vertex_buffer, 0); + dfunc->vkDestroyBuffer (device->dev, mesh->uv_buffer, 0); + dfunc->vkDestroyBuffer (device->dev, mesh->index_buffer, 0); + dfunc->vkFreeMemory (device->dev, mesh->memory, 0); + + __auto_type skins = (maliasskindesc_t *) ((byte *) hdr + hdr->skindesc); + for (int i = 0; i < hdr->mdl.numskins; i++) { + if (skins[i].type == ALIAS_SKIN_GROUP) { + __auto_type group = (maliasskingroup_t *) + ((byte *) hdr + skins[i].skin); + for (int j = 0; j < group->numskins; j++) { + skin_clear (group->skindescs[j].skin, hdr, ctx); + } + } else { + skin_clear (skins[i].skin, hdr, ctx); + } + } +} + +#define SKIN_LAYERS 3 + +static void * +Vulkan_Mod_LoadSkin (mod_alias_ctx_t *alias_ctx, byte *skinpix, int skinsize, + int snum, int gnum, bool group, + maliasskindesc_t *skindesc, vulkan_ctx_t *ctx) +{ + qfvPushDebug (ctx, va (ctx->va_ctx, "alias.load_skin: %s", alias_ctx->mod->name)); + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + aliashdr_t *header = alias_ctx->header; + qfv_alias_skin_t *skin; + byte *tskin; + int w, h; + + skin = Hunk_Alloc (0, sizeof (qfv_alias_skin_t)); + QuatSet (TOP_RANGE + 7, BOTTOM_RANGE + 7, 0, 0, skin->colors); + skindesc->skin = (byte *) skin - (byte *) header; + //FIXME move all skins into arrays(?) + w = header->mdl.skinwidth; + h = header->mdl.skinheight; + tskin = malloc (2 * skinsize); + memcpy (tskin, skinpix, skinsize); + Mod_FloodFillSkin (tskin, w, h); + + int mipLevels = QFV_MipLevels (w, h); + VkExtent3D extent = { w, h, 1 }; + skin->image = QFV_CreateImage (device, 0, VK_IMAGE_TYPE_2D, + VK_FORMAT_R8G8B8A8_UNORM, extent, + mipLevels, 3, VK_SAMPLE_COUNT_1_BIT, + VK_IMAGE_USAGE_SAMPLED_BIT + | VK_IMAGE_USAGE_TRANSFER_DST_BIT + | VK_IMAGE_USAGE_TRANSFER_SRC_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE, skin->image, + va (ctx->va_ctx, "image:%s:%d:%d", + alias_ctx->mod->name, snum, gnum)); + skin->memory = QFV_AllocImageMemory (device, skin->image, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + 0, 0); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, skin->memory, + va (ctx->va_ctx, "memory:%s:%d:%d", + alias_ctx->mod->name, snum, gnum)); + QFV_BindImageMemory (device, skin->image, skin->memory, 0); + skin->view = QFV_CreateImageView (device, skin->image, + VK_IMAGE_VIEW_TYPE_2D_ARRAY, + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_ASPECT_COLOR_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE_VIEW, skin->view, + va (ctx->va_ctx, "iview:%s:%d:%d", + alias_ctx->mod->name, snum, gnum)); + + qfv_stagebuf_t *stage = QFV_CreateStagingBuffer (device, "alias stage", + SKIN_LAYERS * skinsize * 4, + ctx->cmdpool); + qfv_packet_t *packet = QFV_PacketAcquire (stage); + byte *base_data = QFV_PacketExtend (packet, skinsize * 4); + byte *glow_data = QFV_PacketExtend (packet, skinsize * 4); + byte *cmap_data = QFV_PacketExtend (packet, skinsize * 4); + + Mod_CalcFullbright (tskin + skinsize, tskin, skinsize); + Vulkan_ExpandPalette (glow_data, tskin + skinsize, vid.palette, 1, + skinsize); + Mod_ClearFullbright (tskin, tskin, skinsize); + + Skin_CalcTopColors (cmap_data + 0, tskin, skinsize, 4); + Skin_CalcTopMask (cmap_data + 1, tskin, skinsize, 4); + Skin_CalcBottomColors (cmap_data + 2, tskin, skinsize, 4); + Skin_CalcBottomMask (cmap_data + 3, tskin, skinsize, 4); + Skin_ClearTopColors (tskin, tskin, skinsize); + Skin_ClearBottomColors (tskin, tskin, skinsize); + + Vulkan_ExpandPalette (base_data, tskin, vid.palette, 1, skinsize); + + qfv_imagebarrier_t ib = imageBarriers[qfv_LT_Undefined_to_TransferDst]; + ib.barrier.image = skin->image; + ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); + + VkBufferImageCopy copy = { + packet->offset, 0, 0, + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, SKIN_LAYERS}, + {0, 0, 0}, {w, h, 1}, + }; + dfunc->vkCmdCopyBufferToImage (packet->cmd, packet->stage->buffer, + skin->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, ©); + + if (mipLevels == 1) { + ib = imageBarriers[qfv_LT_TransferDst_to_ShaderReadOnly]; + ib.barrier.image = skin->image; + ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); + } else { + QFV_GenerateMipMaps (device, packet->cmd, skin->image, + mipLevels, w, h, SKIN_LAYERS); + } + QFV_PacketSubmit (packet); + QFV_DestroyStagingBuffer (stage); + + free (tskin); + + Vulkan_AliasAddSkin (ctx, skin); + + qfvPopDebug (ctx); + return skinpix + skinsize; +} + +void +Vulkan_Mod_LoadAllSkins (mod_alias_ctx_t *alias_ctx, vulkan_ctx_t *ctx) +{ + aliashdr_t *header = alias_ctx->header; + int skinsize = header->mdl.skinwidth * header->mdl.skinheight; + + for (size_t i = 0; i < alias_ctx->skins.size; i++) { + __auto_type skin = alias_ctx->skins.a + i; + Vulkan_Mod_LoadSkin (alias_ctx, skin->texels, skinsize, + skin->skin_num, skin->group_num, + skin->group_num != -1, skin->skindesc, ctx); + } +} + +void +Vulkan_Mod_FinalizeAliasModel (mod_alias_ctx_t *alias_ctx, vulkan_ctx_t *ctx) +{ + alias_ctx->mod->clear = vulkan_alias_clear; + alias_ctx->mod->data = ctx; +} + +void +Vulkan_Mod_LoadExternalSkins (mod_alias_ctx_t *alias_ctx, vulkan_ctx_t *ctx) +{ +} + +static size_t +get_buffer_size (qfv_device_t *device, VkBuffer buffer) +{ + qfv_devfuncs_t *dfunc = device->funcs; + size_t size; + size_t align; + + VkMemoryRequirements requirements; + dfunc->vkGetBufferMemoryRequirements (device->dev, buffer, &requirements); + size = requirements.size; + align = requirements.alignment - 1; + size = (size + align) & ~(align); + return size; +} + +void +Vulkan_Mod_MakeAliasModelDisplayLists (mod_alias_ctx_t *alias_ctx, void *_m, + int _s, int extra, vulkan_ctx_t *ctx) +{ + aliashdr_t *header = alias_ctx->header; + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + aliasvrt_t *verts; + aliasuv_t *uv; + trivertx_t *pv; + int *indexmap; + uint32_t *indices; + int numverts; + int numtris; + int i, j; + int pose; + vec3_t pos; + + if (header->mdl.ident == HEADER_MDL16) + VectorScale (header->mdl.scale, 1/256.0, header->mdl.scale); + + numverts = header->mdl.numverts; + numtris = header->mdl.numtris; + + // initialize indexmap to -1 (unduplicated). any other value indicates + // both that the vertex has been duplicated and the index of the + // duplicate vertex. + indexmap = malloc (numverts * sizeof (int)); + memset (indexmap, -1, numverts * sizeof (int)); + + // check for onseam verts, and duplicate any that are associated with + // back-facing triangles + for (i = 0; i < numtris; i++) { + for (j = 0; j < 3; j++) { + int vind = alias_ctx->triangles.a[i].vertindex[j]; + if (alias_ctx->stverts.a[vind].onseam + && !alias_ctx->triangles.a[i].facesfront) { + // duplicate the vertex if it has not alreaddy been + // duplicated + if (indexmap[vind] == -1) { + indexmap[vind] = numverts++; + } + } + } + } + + // we now know exactly how many vertices we need, so built the vertex + // and index data arrays + // The layout is: + // vbuf:{vertex, normal} * (numposes * numverts) + // uvbuf:{uv} * (numverts) + // ibuf:{index} * (numtris * 3) + // numverts includes the duplicated seam vertices. + // The vertex buffer will be bound with various offsets based on the + // current and previous pose, uvbuff "statically" bound as uvs are not + // animated by pose, and the same for ibuf: indices will never change for + // the mesh + size_t vert_count = numverts * header->numposes; + size_t vert_size = vert_count * sizeof (aliasvrt_t); + size_t uv_size = numverts * sizeof (aliasuv_t); + size_t ind_size = 3 * numtris * sizeof (uint32_t); + + VkBuffer vbuff = QFV_CreateBuffer (device, vert_size, + VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); + VkBuffer uvbuff = QFV_CreateBuffer (device, uv_size, + VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); + VkBuffer ibuff = QFV_CreateBuffer (device, ind_size, + VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_INDEX_BUFFER_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER, vbuff, + va (ctx->va_ctx, "buffer:alias:vertex:%s", + alias_ctx->mod->name)); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER, uvbuff, + va (ctx->va_ctx, "buffer:alias:uv:%s", + alias_ctx->mod->name)); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER, ibuff, + va (ctx->va_ctx, "buffer:alias:index:%s", + alias_ctx->mod->name)); + size_t voffs = 0; + size_t uvoffs = voffs + get_buffer_size (device, vbuff); + size_t ioffs = uvoffs + get_buffer_size (device, uvbuff); + size_t buff_size = ioffs + get_buffer_size (device, ibuff); + VkDeviceMemory mem; + mem = QFV_AllocBufferMemory (device, vbuff, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + buff_size, 0); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, mem, + va (ctx->va_ctx, "memory:alias:vuvi:%s", + alias_ctx->mod->name)); + QFV_BindBufferMemory (device, vbuff, mem, voffs); + QFV_BindBufferMemory (device, uvbuff, mem, uvoffs); + QFV_BindBufferMemory (device, ibuff, mem, ioffs); + + qfv_stagebuf_t *stage = QFV_CreateStagingBuffer (device, + va (ctx->va_ctx, + "alias:%s", + alias_ctx->mod->name), + buff_size, ctx->cmdpool); + qfv_packet_t *packet = QFV_PacketAcquire (stage); + verts = QFV_PacketExtend (packet, vert_size); + uv = QFV_PacketExtend (packet, uv_size); + indices = QFV_PacketExtend (packet, ind_size); + + // populate the uvs, duplicating and shifting any that are on the seam + // and associated with back-facing triangles (marked by non-negative + // indexmap entry). + // the s coordinate is shifted right by half the skin width. + for (i = 0; i < header->mdl.numverts; i++) { + int vind = indexmap[i]; + uv[i].u = (float) alias_ctx->stverts.a[i].s / header->mdl.skinwidth; + uv[i].v = (float) alias_ctx->stverts.a[i].t / header->mdl.skinheight; + if (vind != -1) { + uv[vind] = uv[i]; + uv[vind].u += 0.5; + } + } + + // poputlate the vertex position and normal data, duplicating for + // back-facing on-seam verts (indicated by non-negative indexmap entry) + for (i = 0, pose = 0; i < header->numposes; i++, pose += numverts) { + for (j = 0; j < header->mdl.numverts; j++) { + pv = &alias_ctx->poseverts.a[i][j]; + if (extra) { + VectorMultAdd (pv[header->mdl.numverts].v, 256, pv->v, pos); + } else { + VectorCopy (pv->v, pos); + } + VectorCompMultAdd (header->mdl.scale_origin, header->mdl.scale, + pos, verts[pose + j].vertex); + verts[pose + j].vertex[3] = 1; + VectorCopy (vertex_normals[pv->lightnormalindex], + verts[pose + j].normal); + verts[pose + j].normal[3] = 0; + // duplicate on-seam vert associated with back-facing triangle + if (indexmap[j] != -1) { + verts[pose + indexmap[j]] = verts[pose + j]; + } + } + } + + // now build the indices for DrawElements + for (i = 0; i < numtris; i++) { + for (j = 0; j < 3; j++) { + int vind = alias_ctx->triangles.a[i].vertindex[j]; + // can't use indexmap to do the test because it indicates only + // that the vertex has been duplicated, not whether or not + // the vertex is the original or the duplicate + if (alias_ctx->stverts.a[vind].onseam + && !alias_ctx->triangles.a[i].facesfront) { + vind = indexmap[vind]; + } + indices[3 * i + j] = vind; + } + } + // finished with indexmap + free (indexmap); + + header->poseverts = numverts; + + qfv_bufferbarrier_t bb = bufferBarriers[qfv_BB_Unknown_to_TransferWrite]; + VkBufferMemoryBarrier wr_barriers[] = { + bb.barrier, bb.barrier, bb.barrier, + }; + wr_barriers[0].buffer = vbuff; + wr_barriers[0].size = vert_size; + wr_barriers[1].buffer = uvbuff; + wr_barriers[1].size = uv_size; + wr_barriers[2].buffer = ibuff; + wr_barriers[2].size = ind_size; + dfunc->vkCmdPipelineBarrier (packet->cmd, bb.srcStages, bb.dstStages, + 0, 0, 0, 3, wr_barriers, 0, 0); + VkBufferCopy copy_region[] = { + { packet->offset, 0, vert_size }, + { packet->offset + vert_size, 0, uv_size }, + { packet->offset + vert_size + uv_size, 0, ind_size }, + }; + dfunc->vkCmdCopyBuffer (packet->cmd, stage->buffer, + vbuff, 1, ©_region[0]); + dfunc->vkCmdCopyBuffer (packet->cmd, stage->buffer, + uvbuff, 1, ©_region[1]); + dfunc->vkCmdCopyBuffer (packet->cmd, stage->buffer, + ibuff, 1, ©_region[2]); + // both qfv_BB_TransferWrite_to_VertexAttrRead and + // qfv_BB_TransferWrite_to_IndexRead have the same stage flags + bb = bufferBarriers[qfv_BB_TransferWrite_to_VertexAttrRead]; + VkBufferMemoryBarrier rd_barriers[] = { + bufferBarriers[qfv_BB_TransferWrite_to_VertexAttrRead].barrier, + bufferBarriers[qfv_BB_TransferWrite_to_VertexAttrRead].barrier, + bufferBarriers[qfv_BB_TransferWrite_to_IndexRead].barrier, + }; + rd_barriers[0].buffer = vbuff; + rd_barriers[0].size = vert_size; + rd_barriers[1].buffer = uvbuff; + rd_barriers[1].size = uv_size; + rd_barriers[2].buffer = ibuff; + rd_barriers[2].size = ind_size; + dfunc->vkCmdPipelineBarrier (packet->cmd, bb.srcStages, bb.dstStages, + 0, 0, 0, 3, rd_barriers, 0, 0); + QFV_PacketSubmit (packet); + QFV_DestroyStagingBuffer (stage); + + qfv_alias_mesh_t *mesh = Hunk_Alloc (0, sizeof (qfv_alias_mesh_t)); + mesh->vertex_buffer = vbuff; + mesh->uv_buffer = uvbuff; + mesh->index_buffer = ibuff; + mesh->memory = mem; + header->commands = (byte *) mesh - (byte *) header; +} diff --git a/libs/models/brush/Makefile.am b/libs/models/brush/Makefile.am deleted file mode 100644 index cd728baed..000000000 --- a/libs/models/brush/Makefile.am +++ /dev/null @@ -1,22 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CFLAGS= @PREFER_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include - -noinst_LTLIBRARIES= libbrush.la @brush_libs@ -EXTRA_LTLIBRARIES=libbrush_gl.la libbrush_glsl.la libbrush_sw.la - -brush_src= model_brush.c -gl_src= gl_model_brush.c -glsl_src= glsl_model_brush.c -sw_src= sw_model_brush.c - -libbrush_la_SOURCES= $(brush_src) - -libbrush_gl_la_SOURCES= $(gl_src) $(brush_src) - -libbrush_glsl_la_SOURCES= $(glsl_src) $(brush_src) - -libbrush_sw_la_SOURCES= $(sw_src) $(brush_src) - -EXTRA_DIST= $(gl_src) $(glsl_src) $(sw_src) $(brush_src) diff --git a/libs/models/brush/Makemodule.am b/libs/models/brush/Makemodule.am new file mode 100644 index 000000000..4a6f03a1f --- /dev/null +++ b/libs/models/brush/Makemodule.am @@ -0,0 +1,29 @@ +noinst_LTLIBRARIES += libs/models/brush/libbrush.la @brush_libs@ +EXTRA_LTLIBRARIES += \ + libs/models/brush/libbrush_gl.la \ + libs/models/brush/libbrush_glsl.la \ + libs/models/brush/libbrush_sw.la \ + libs/models/brush/libbrush_vulkan.la + +brush_src= libs/models/brush/model_brush.c +brush_gl_src= libs/models/brush/gl_model_brush.c +brush_glsl_src= libs/models/brush/glsl_model_brush.c +brush_sw_src= libs/models/brush/sw_model_brush.c +brush_vulkan_src= libs/models/brush/vulkan_model_brush.c + +libs_models_brush_libbrush_la_SOURCES= $(brush_src) + +libs_models_brush_libbrush_gl_la_SOURCES= $(brush_gl_src) $(brush_src) + +libs_models_brush_libbrush_glsl_la_SOURCES= $(brush_glsl_src) $(brush_src) + +libs_models_brush_libbrush_sw_la_SOURCES= $(brush_sw_src) $(brush_src) + +libs_models_brush_libbrush_vulkan_la_SOURCES= $(brush_vulkan_src) $(brush_src) + +EXTRA_DIST += \ + $(brush_gl_src) \ + $(brush_glsl_src) \ + $(brush_sw_src) \ + ${brush_vulkan_src} \ + $(brush_src) diff --git a/libs/models/brush/gl_model_brush.c b/libs/models/brush/gl_model_brush.c index 18744a529..02ed475a1 100644 --- a/libs/models/brush/gl_model_brush.c +++ b/libs/models/brush/gl_model_brush.c @@ -45,30 +45,15 @@ #include "QF/quakefs.h" #include "QF/sys.h" #include "QF/va.h" +#include "QF/GL/qf_rsurf.h" #include "QF/GL/qf_textures.h" #include "compat.h" #include "mod_internal.h" #include "r_internal.h" - -void -gl_Mod_ProcessTexture (texture_t *tx) -{ - char name[32]; - - if (!strncmp (tx->name, "sky", 3)) - return; - snprintf (name, sizeof (name), "fb_%s", tx->name); - tx->gl_fb_texturenum = - Mod_Fullbright ((byte *) (tx + 1), tx->width, tx->height, name); - tx->gl_texturenum = - GL_LoadTexture (tx->name, tx->width, tx->height, (byte *) (tx + 1), - true, false, 1); -} - static tex_t * -Mod_LoadAnExternalTexture (char * tname, char *mname) +Mod_LoadAnExternalTexture (const char *tname, const char *mname) { char rname[32]; tex_t *image; @@ -77,64 +62,85 @@ Mod_LoadAnExternalTexture (char * tname, char *mname) if (rname[0] == '*') rname[0] = '#'; - image = LoadImage (va ("textures/%.*s/%s", (int) strlen (mname + 5) - 4, - mname + 5, rname)); + image = LoadImage (va (0, "textures/%.*s/%s", (int) strlen (mname + 5) - 4, + mname + 5, rname), 1); if (!image) - image = LoadImage (va ("maps/%.*s/%s", - (int) strlen (mname + 5) - 4, - mname + 5, rname)); + image = LoadImage (va (0, "maps/%.*s/%s", (int) strlen (mname + 5) - 4, + mname + 5, rname), 1); // if (!image) -// image = LoadImage (va ("textures/bmodels/%s", rname)); +// image = LoadImage (va (0, "textures/bmodels/%s", rname)); if (!image) - image = LoadImage (va ("textures/%s", rname)); + image = LoadImage (va (0, "textures/%s", rname), 1); if (!image) - image = LoadImage (va ("maps/%s", rname)); + image = LoadImage (va (0, "maps/%s", rname), 1); return image; } -void -gl_Mod_LoadExternalTextures (model_t *mod) +static int +Mod_LoadExternalTextures (model_t *mod, texture_t *tx) { - int i; tex_t *base, *luma; - texture_t *tx; + gltex_t *gltx; + int external = 0; - for (i = 0; i < mod->numtextures; i++) { - tx = mod->textures[i]; - if (!tx) - continue; + gltx = tx->render; + if ((base = Mod_LoadAnExternalTexture (tx->name, mod->path))) { + external = 1; + gltx->gl_texturenum = + GL_LoadTexture (tx->name, base->width, base->height, + base->data, true, false, + base->format > 2 ? base->format : 1); - if ((base = Mod_LoadAnExternalTexture (tx->name, mod->name))) { - tx->gl_texturenum = - GL_LoadTexture (tx->name, base->width, base->height, - base->data, true, false, - base->format > 2 ? base->format : 1); + luma = Mod_LoadAnExternalTexture (va (0, "%s_luma", tx->name), + mod->path); + if (!luma) + luma = Mod_LoadAnExternalTexture (va (0, "%s_glow", tx->name), + mod->path); - luma = Mod_LoadAnExternalTexture (va ("%s_luma", tx->name), - mod->name); - if (!luma) - luma = Mod_LoadAnExternalTexture (va ("%s_glow", tx->name), - mod->name); + gltx->gl_fb_texturenum = 0; - tx->gl_fb_texturenum = 0; - - if (luma) { - tx->gl_fb_texturenum = - GL_LoadTexture (va ("fb_%s", tx->name), luma->width, - luma->height, luma->data, true, true, - luma->format > 2 ? luma->format : 1); - } else if (base->format < 3) { - tx->gl_fb_texturenum = - Mod_Fullbright (base->data, base->width, base->height, - va ("fb_%s", tx->name)); - } + if (luma) { + gltx->gl_fb_texturenum = + GL_LoadTexture (va (0, "fb_%s", tx->name), luma->width, + luma->height, luma->data, true, true, + luma->format > 2 ? luma->format : 1); + } else if (base->format < 3) { + gltx->gl_fb_texturenum = + Mod_Fullbright (base->data, base->width, base->height, + va (0, "fb_%s", tx->name)); } } + return external; } void -gl_Mod_LoadLighting (bsp_t *bsp) +gl_Mod_ProcessTexture (model_t *mod, texture_t *tx) +{ + const char *name; + + if (!tx) { + return; + } + if (gl_textures_external) { + if (Mod_LoadExternalTextures (mod, tx)) { + return; + } + } + if (strncmp (tx->name, "sky", 3) == 0) { + return; + } + gltex_t *gltex = tx->render; + name = va (0, "fb_%s", tx->name); + gltex->gl_fb_texturenum = + Mod_Fullbright ((byte *) (tx + 1), tx->width, tx->height, name); + gltex->gl_texturenum = + GL_LoadTexture (tx->name, tx->width, tx->height, (byte *) (tx + 1), + true, false, 1); +} + +void +gl_Mod_LoadLighting (model_t *mod, bsp_t *bsp) { byte d; byte *in, *out, *data; @@ -142,28 +148,29 @@ gl_Mod_LoadLighting (bsp_t *bsp) size_t i; int ver; QFile *lit_file; + mod_brush_t *brush = &mod->brush; - dstring_copystr (litfilename, loadmodel->name); - loadmodel->lightdata = NULL; + dstring_copystr (litfilename, mod->path); + brush->lightdata = NULL; if (mod_lightmap_bytes > 1) { // LordHavoc: check for a .lit file to load QFS_StripExtension (litfilename->str, litfilename->str); dstring_appendstr (litfilename, ".lit"); - lit_file = QFS_VOpenFile (litfilename->str, 0, loadmodel->vpath); + lit_file = QFS_VOpenFile (litfilename->str, 0, mod->vpath); data = (byte *) QFS_LoadHunkFile (lit_file); if (data) { if (data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T') { ver = LittleLong (((int32_t *) data)[1]); if (ver == 1) { - Sys_MaskPrintf (SYS_DEV, "%s loaded", litfilename->str); - loadmodel->lightdata = data + 8; + Sys_MaskPrintf (SYS_dev, "%s loaded", litfilename->str); + brush->lightdata = data + 8; return; } else - Sys_MaskPrintf (SYS_DEV, + Sys_MaskPrintf (SYS_dev, "Unknown .lit file version (%d)\n", ver); } else - Sys_MaskPrintf (SYS_DEV, "Corrupt .lit file (old version?)\n"); + Sys_MaskPrintf (SYS_dev, "Corrupt .lit file (old version?)\n"); } } // LordHavoc: oh well, expand the white lighting data @@ -171,11 +178,11 @@ gl_Mod_LoadLighting (bsp_t *bsp) dstring_delete (litfilename); return; } - loadmodel->lightdata = Hunk_AllocName (bsp->lightdatasize - * mod_lightmap_bytes, - litfilename->str); + brush->lightdata = Hunk_AllocName (0, + bsp->lightdatasize * mod_lightmap_bytes, + litfilename->str); in = bsp->lightdata; - out = loadmodel->lightdata; + out = brush->lightdata; if (mod_lightmap_bytes > 1) for (i = 0; i < bsp->lightdatasize ; i++) { @@ -222,15 +229,14 @@ SubdividePolygon (int numverts, float *verts) vec3_t mins, maxs; vec3_t front[64], back[64]; - if (numverts > 60) + if (numverts < 3 || numverts > 60) Sys_Error ("numverts = %i", numverts); BoundPoly (numverts, verts, mins, maxs); for (i = 0; i < 3; i++) { m = (mins[i] + maxs[i]) * 0.5; - m = gl_subdivide_size->value * floor (m / gl_subdivide_size->value + - 0.5); + m = gl_subdivide_size * floor (m / gl_subdivide_size + 0.5); if (maxs[i] - m < 8) continue; if (m - mins[i] < 8) @@ -274,7 +280,7 @@ SubdividePolygon (int numverts, float *verts) return; } - poly = Hunk_Alloc (sizeof (glpoly_t) + (numverts - 4) * VERTEXSIZE * + poly = Hunk_Alloc (0, sizeof (glpoly_t) + (numverts - 4) * VERTEXSIZE * sizeof (float)); poly->next = warpface->polys; warpface->polys = poly; @@ -296,26 +302,29 @@ SubdividePolygon (int numverts, float *verts) can be done reasonably. */ void -gl_Mod_SubdivideSurface (msurface_t *fa) +gl_Mod_SubdivideSurface (model_t *mod, msurface_t *fa) { float *vec; int lindex, numverts, i; vec3_t verts[64]; + mod_brush_t *brush = &mod->brush; warpface = fa; // convert edges back to a normal polygon numverts = 0; for (i = 0; i < fa->numedges; i++) { - lindex = loadmodel->surfedges[fa->firstedge + i]; + lindex = brush->surfedges[fa->firstedge + i]; if (lindex > 0) - vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position; + vec = brush->vertexes[brush->edges[lindex].v[0]].position; else - vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position; + vec = brush->vertexes[brush->edges[-lindex].v[1]].position; VectorCopy (vec, verts[numverts]); numverts++; } - SubdividePolygon (numverts, verts[0]); + if (numverts > 3) { + SubdividePolygon (numverts, verts[0]); + } } diff --git a/libs/models/brush/glsl_model_brush.c b/libs/models/brush/glsl_model_brush.c index 8827844da..da6fad3d0 100644 --- a/libs/models/brush/glsl_model_brush.c +++ b/libs/models/brush/glsl_model_brush.c @@ -53,30 +53,36 @@ #include "QF/vid.h" #include "QF/GLSL/defines.h" #include "QF/GLSL/funcs.h" +#include "QF/GLSL/qf_bsp.h" #include "QF/GLSL/qf_textures.h" #include "compat.h" #include "mod_internal.h" +#include "r_internal.h" static void -glsl_brush_clear (model_t *m) +glsl_brush_clear (model_t *m, void *data) { - int i; + mod_brush_t *brush = &m->brush; m->needload = true; - for (i = 0; i < m->numtextures; i++) { + for (unsigned i = 0; i < brush->numtextures; i++) { // NOTE: some maps (eg e1m2) have empty texture slots - if (m->textures[i] && m->textures[i]->gl_texturenum) { - GLSL_ReleaseTexture (m->textures[i]->gl_texturenum); - GLSL_ReleaseTexture (m->textures[i]->sky_tex[0]); - GLSL_ReleaseTexture (m->textures[i]->sky_tex[1]); - m->textures[i]->gl_texturenum = 0; + glsltex_t *tex = 0; + if (brush->textures[i]) { + tex = brush->textures[i]->render; + } + if (tex && tex->gl_texturenum) { + GLSL_ReleaseTexture (tex->gl_texturenum); + GLSL_ReleaseTexture (tex->sky_tex[0]); + GLSL_ReleaseTexture (tex->sky_tex[1]); + tex->gl_texturenum = 0; } } - for (i = 0; i < m->numsurfaces; i++) { - if (m->surfaces[i].polys) { - free (m->surfaces[i].polys); - m->surfaces[i].polys = 0; + for (unsigned i = 0; i < brush->numsurfaces; i++) { + if (brush->surfaces[i].polys) { + free (brush->surfaces[i].polys); + brush->surfaces[i].polys = 0; } } } @@ -94,8 +100,12 @@ load_skytex (texture_t *tx, byte *data) } void -glsl_Mod_ProcessTexture (texture_t *tx) +glsl_Mod_ProcessTexture (model_t *mod, texture_t *tx) { + if (!tx) { + return; + } + glsltex_t *tex = tx->render; if (!strncmp (tx->name, "sky", 3)) { // sky textures need to be loaded as two separate textures to allow // wrapping on both sky layers. @@ -112,46 +122,36 @@ glsl_Mod_ProcessTexture (texture_t *tx) // a square sky texture probably means it's black, but just in // case some other magic is being done, duplicate the square to // both sky layers. - tx->sky_tex[0] = load_skytex (tx, tx_data); - tx->sky_tex[1] = tx->sky_tex[0]; + tex->sky_tex[0] = load_skytex (tx, tx_data); + tex->sky_tex[1] = tex->sky_tex[0]; } else if (tx_w == 2 * tx_h) { data = alloca (tx_h * tx_h); for (i = 0; i < 2; i++) { for (j = 0; j < tx_h; j++) memcpy (&data[j * tx_h], &tx_data[j * tx_w + i * tx_h], tx_h); - tx->sky_tex[i] = load_skytex (tx, data); + tex->sky_tex[i] = load_skytex (tx, data); } - tx->gl_texturenum = 0; + tex->gl_texturenum = 0; } else { Sys_Error ("Mod_ProcessTexture: invalid sky texture: %dx%d\n", tx_w, tx_h); } } else { - tx->gl_texturenum = GLSL_LoadQuakeMipTex (tx); + tex->gl_texturenum = GLSL_LoadQuakeMipTex (tx); } } void -glsl_Mod_LoadExternalTextures (model_t *mod) +glsl_Mod_LoadLighting (model_t *mod, bsp_t *bsp) { -} - -void -glsl_Mod_LoadLighting (bsp_t *bsp) -{ - // a big hacky, but it's as good a place as any - loadmodel->clear = glsl_brush_clear; + // a bit hacky, but it's as good a place as any + mod->clear = glsl_brush_clear; mod_lightmap_bytes = 1; if (!bsp->lightdatasize) { - loadmodel->lightdata = NULL; + mod->brush.lightdata = NULL; return; } - loadmodel->lightdata = Hunk_AllocName (bsp->lightdatasize, loadname); - memcpy (loadmodel->lightdata, bsp->lightdata, bsp->lightdatasize); -} - -void -glsl_Mod_SubdivideSurface (msurface_t *fa) -{ + mod->brush.lightdata = Hunk_AllocName (0, bsp->lightdatasize, mod->name); + memcpy (mod->brush.lightdata, bsp->lightdata, bsp->lightdatasize); } diff --git a/libs/models/brush/model_brush.c b/libs/models/brush/model_brush.c index 931575534..b7f8d4fff 100644 --- a/libs/models/brush/model_brush.c +++ b/libs/models/brush/model_brush.c @@ -42,63 +42,62 @@ #include "QF/checksum.h" #include "QF/cvar.h" +#include "QF/dstring.h" #include "QF/model.h" #include "QF/qendian.h" #include "QF/quakefs.h" #include "QF/render.h" +#include "QF/set.h" #include "QF/sys.h" #include "QF/va.h" #include "QF/plugin/vid_render.h" +#include "QF/simd/vec4f.h" #include "compat.h" +#include "mod_internal.h" -byte mod_novis[MAX_MAP_LEAFS / 8]; - -VISIBLE cvar_t *gl_sky_divide; //FIXME visibility? -VISIBLE int mod_lightmap_bytes = 1; //FIXME should this be visible? +VISIBLE int mod_sky_divide; //FIXME visibility? +VISIBLE int mod_lightmap_bytes = 1; //FIXME should this be visible? VISIBLE mleaf_t * -Mod_PointInLeaf (const vec3_t p, model_t *model) +Mod_PointInLeaf (vec4f_t p, model_t *model) { float d; - mnode_t *node; - plane_t *plane; - if (!model || !model->nodes) + if (!model || !model->brush.nodes) Sys_Error ("Mod_PointInLeaf: bad model"); - node = model->nodes; + int node_id = 0; while (1) { - if (node->contents < 0) - return (mleaf_t *) node; - plane = node->plane; - d = DotProduct (p, plane->normal) - plane->dist; - if (d >= 0) - node = node->children[0]; - else - node = node->children[1]; + if (node_id < 0) + return model->brush.leafs + ~node_id; + mnode_t *node = model->brush.nodes + node_id; + d = dotf (p, node->plane)[0]; + node_id = node->children[d < 0]; } return NULL; // never reached } -static inline byte * -Mod_DecompressVis (byte * in, model_t *model) +static inline void +Mod_DecompressVis_set (const byte *in, const mod_brush_t *brush, byte defvis, + set_t *pvs) { - static byte decompressed[MAX_MAP_LEAFS / 8]; - byte *out; + byte *out = (byte *) pvs->map; + byte *start = out; int row, c; - row = (model->numleafs + 7) >> 3; - out = decompressed; + // Ensure the set repesents visible leafs rather than invisible leafs. + pvs->inverted = 0; + row = (brush->visleafs + 7) >> 3; if (!in) { // no vis info, so make all visible while (row) { - *out++ = 0xff; + *out++ = defvis; row--; } - return decompressed; + return; } do { @@ -113,17 +112,106 @@ Mod_DecompressVis (byte * in, model_t *model) *out++ = 0; c--; } - } while (out - decompressed < row); + } while (out - start < row); +} +static inline void +Mod_DecompressVis_mix (const byte *in, const mod_brush_t *brush, byte defvis, + set_t *pvs) +{ + byte *out = (byte *) pvs->map; + byte *start = out; + int row, c; + + //FIXME should pvs->inverted be checked and the vis bits used to remove + // set bits? + row = (brush->visleafs + 7) >> 3; + + if (!in) { // no vis info, so make all visible + while (row) { + *out++ |= defvis; + row--; + } + return; + } + + do { + if (*in) { + *out++ |= *in++; + continue; + } + + c = in[1]; + in += 2; + out += c; + } while (out - start < row); +} + +VISIBLE set_t * +Mod_LeafPVS (const mleaf_t *leaf, const model_t *model) +{ + static set_t *novis; + static set_t *decompressed; + unsigned numvis = model->brush.visleafs; + unsigned excess = SET_SIZE (numvis) - numvis; + + if (leaf == model->brush.leafs) { + if (!novis) { + novis = set_new_size (numvis); + } + if (!novis->map[0] || SET_SIZE (numvis) > novis->size) { + set_expand (novis, numvis); + memset (novis->map, 0xff, + SET_WORDS (novis) * sizeof (*novis->map)); + novis->map[SET_WORDS (novis) - 1] &= (~SET_ZERO) >> excess; + } + return novis; + } + if (!decompressed) { + decompressed = set_new (); + } + set_expand (decompressed, numvis); + Mod_DecompressVis_set (leaf->compressed_vis, &model->brush, 0xff, + decompressed); + decompressed->map[SET_WORDS (decompressed) - 1] &= (~SET_ZERO) >> excess; return decompressed; } -VISIBLE byte * -Mod_LeafPVS (mleaf_t *leaf, model_t *model) +VISIBLE void +Mod_LeafPVS_set (const mleaf_t *leaf, const model_t *model, byte defvis, + set_t *out) { - if (leaf == model->leafs) - return mod_novis; - return Mod_DecompressVis (leaf->compressed_vis, model); + unsigned numvis = model->brush.visleafs; + unsigned excess = SET_SIZE (numvis) - numvis; + + set_expand (out, numvis); + if (leaf == model->brush.leafs) { + memset (out->map, defvis, SET_WORDS (out) * sizeof (*out->map)); + out->map[SET_WORDS (out) - 1] &= (~SET_ZERO) >> excess; + return; + } + Mod_DecompressVis_set (leaf->compressed_vis, &model->brush, defvis, out); + out->map[SET_WORDS (out) - 1] &= (~SET_ZERO) >> excess; +} + +VISIBLE void +Mod_LeafPVS_mix (const mleaf_t *leaf, const model_t *model, byte defvis, + set_t *out) +{ + unsigned numvis = model->brush.visleafs; + unsigned excess = SET_SIZE (numvis) - numvis; + + set_expand (out, numvis); + if (leaf == model->brush.leafs) { + byte *o = (byte *) out->map; + for (int i = SET_WORDS (out) * sizeof (*out->map); i-- > 0; ) { + *o++ |= defvis; + } + out->map[SET_WORDS (out) - 1] &= (~SET_ZERO) >> excess; + return; + } + Mod_DecompressVis_mix (leaf->compressed_vis, &model->brush, defvis, out); + out->map[SET_WORDS (out) - 1] &= (~SET_ZERO) >> excess; } // BRUSHMODEL LOADING ========================================================= @@ -144,8 +232,9 @@ mod_unique_miptex_name (texture_t **textures, texture_t *tx, int ind) break; if (i == ind) break; - tag = va ("~%x", num++); + tag = va (0, "~%x", num++); strncpy (tx->name, name, 16); + tx->name[15] = 0; if (strlen (name) + strlen (tag) <= 15) strcat (tx->name, tag); else @@ -154,59 +243,77 @@ mod_unique_miptex_name (texture_t **textures, texture_t *tx, int ind) } static void -Mod_LoadTextures (bsp_t *bsp) +Mod_LoadTextures (model_t *mod, bsp_t *bsp) { dmiptexlump_t *m; - int i, j, pixels, num, max, altmax; + int pixels, num, max, altmax; miptex_t *mt; texture_t *tx, *tx2; texture_t *anims[10], *altanims[10]; + mod_brush_t *brush = &mod->brush; if (!bsp->texdatasize) { - loadmodel->textures = NULL; + brush->textures = NULL; return; } m = (dmiptexlump_t *) bsp->texdata; - loadmodel->numtextures = m->nummiptex; - loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof - (*loadmodel->textures), loadname); + brush->numtextures = m->nummiptex; + brush->textures = Hunk_AllocName (0, + m->nummiptex * sizeof (*brush->textures), + mod->name); - for (i = 0; i < m->nummiptex; i++) { - if (m->dataofs[i] == -1) + for (uint32_t i = 0; i < m->nummiptex; i++) { + if (m->dataofs[i] == ~0u) continue; mt = (miptex_t *) ((byte *) m + m->dataofs[i]); mt->width = LittleLong (mt->width); mt->height = LittleLong (mt->height); - for (j = 0; j < MIPLEVELS; j++) + for (int j = 0; j < MIPLEVELS; j++) mt->offsets[j] = LittleLong (mt->offsets[j]); if ((mt->width & 15) || (mt->height & 15)) Sys_Error ("Texture %s is not 16 aligned", mt->name); pixels = mt->width * mt->height / 64 * 85; - tx = Hunk_AllocName (sizeof (texture_t) + pixels, loadname); + tx = Hunk_AllocName (0, sizeof (texture_t) + pixels, mod->name); - loadmodel->textures[i] = tx; + brush->textures[i] = tx; memcpy (tx->name, mt->name, sizeof (tx->name)); - mod_unique_miptex_name (loadmodel->textures, tx, i); + mod_unique_miptex_name (brush->textures, tx, i); tx->width = mt->width; tx->height = mt->height; - for (j = 0; j < MIPLEVELS; j++) + for (int j = 0; j < MIPLEVELS; j++) tx->offsets[j] = mt->offsets[j] + sizeof (texture_t) - sizeof (miptex_t); // the pixels immediately follow the structures memcpy (tx + 1, mt + 1, pixels); if (!strncmp (mt->name, "sky", 3)) - loadmodel->skytexture = tx; - if (mod_funcs) - mod_funcs->Mod_ProcessTexture (tx); + brush->skytexture = tx; + } + if (mod_funcs && mod_funcs->Mod_ProcessTexture) { + size_t render_size = mod_funcs->texture_render_size; + byte *render_data = 0; + if (render_size) { + render_data = Hunk_AllocName (0, m->nummiptex * render_size, + mod->name); + } + for (uint32_t i = 0; i < m->nummiptex; i++) { + if (!(tx = brush->textures[i])) { + continue; + } + tx->render = render_data; + render_data += render_size; + mod_funcs->Mod_ProcessTexture (mod, tx); + } + // signal the end of the textures + mod_funcs->Mod_ProcessTexture (mod, 0); } // sequence the animations - for (i = 0; i < m->nummiptex; i++) { - tx = loadmodel->textures[i]; + for (uint32_t i = 0; i < m->nummiptex; i++) { + tx = brush->textures[i]; if (!tx || tx->name[0] != '+') continue; if (tx->anim_next) @@ -216,10 +323,13 @@ Mod_LoadTextures (bsp_t *bsp) memset (anims, 0, sizeof (anims)); memset (altanims, 0, sizeof (altanims)); +// convert to uppercase, avoiding toupper (table lookup, +// localization issues, etc) +#define QTOUPPER(x) ((x) - ('a' - 'A')) + max = tx->name[1]; - altmax = 0; if (max >= 'a' && max <= 'z') - max -= 'a' - 'A'; + max = QTOUPPER (max); if (max >= '0' && max <= '9') { max -= '0'; altmax = 0; @@ -233,8 +343,8 @@ Mod_LoadTextures (bsp_t *bsp) } else Sys_Error ("Bad animating texture %s", tx->name); - for (j = i + 1; j < m->nummiptex; j++) { - tx2 = loadmodel->textures[j]; + for (uint32_t j = i + 1; j < m->nummiptex; j++) { + tx2 = brush->textures[j]; if (!tx2 || tx2->name[0] != '+') continue; if (strcmp (tx2->name + 2, tx->name + 2)) @@ -242,7 +352,7 @@ Mod_LoadTextures (bsp_t *bsp) num = tx2->name[1]; if (num >= 'a' && num <= 'z') - num -= 'a' - 'A'; + num = QTOUPPER (num); if (num >= '0' && num <= '9') { num -= '0'; anims[num] = tx2; @@ -257,9 +367,8 @@ Mod_LoadTextures (bsp_t *bsp) Sys_Error ("Bad animating texture %s", tx->name); } -#define ANIM_CYCLE 2 // link them all together - for (j = 0; j < max; j++) { + for (int j = 0; j < max; j++) { tx2 = anims[j]; if (!tx2) Sys_Error ("Missing frame %i of %s", j, tx->name); @@ -270,7 +379,7 @@ Mod_LoadTextures (bsp_t *bsp) if (altmax) tx2->alternate_anims = altanims[0]; } - for (j = 0; j < altmax; j++) { + for (int j = 0; j < altmax; j++) { tx2 = altanims[j]; if (!tx2) Sys_Error ("Missing frame %i of %s", j, tx->name); @@ -285,29 +394,29 @@ Mod_LoadTextures (bsp_t *bsp) } static void -Mod_LoadVisibility (bsp_t *bsp) +Mod_LoadVisibility (model_t *mod, bsp_t *bsp) { if (!bsp->visdatasize) { - loadmodel->visdata = NULL; + mod->brush.visdata = NULL; return; } - loadmodel->visdata = Hunk_AllocName (bsp->visdatasize, loadname); - memcpy (loadmodel->visdata, bsp->visdata, bsp->visdatasize); + mod->brush.visdata = Hunk_AllocName (0, bsp->visdatasize, mod->name); + memcpy (mod->brush.visdata, bsp->visdata, bsp->visdatasize); } static void -Mod_LoadEntities (bsp_t *bsp) +Mod_LoadEntities (model_t *mod, bsp_t *bsp) { if (!bsp->entdatasize) { - loadmodel->entities = NULL; + mod->brush.entities = NULL; return; } - loadmodel->entities = Hunk_AllocName (bsp->entdatasize, loadname); - memcpy (loadmodel->entities, bsp->entdata, bsp->entdatasize); + mod->brush.entities = Hunk_AllocName (0, bsp->entdatasize, mod->name); + memcpy (mod->brush.entities, bsp->entdata, bsp->entdatasize); } static void -Mod_LoadVertexes (bsp_t *bsp) +Mod_LoadVertexes (model_t *mod, bsp_t *bsp) { dvertex_t *in; int count, i; @@ -315,27 +424,28 @@ Mod_LoadVertexes (bsp_t *bsp) in = bsp->vertexes; count = bsp->numvertexes; - out = Hunk_AllocName (count * sizeof (*out), loadname); + out = Hunk_AllocName (0, count * sizeof (*out), mod->name); - loadmodel->vertexes = out; - loadmodel->numvertexes = count; + mod->brush.vertexes = out; + mod->brush.numvertexes = count; for (i = 0; i < count; i++, in++, out++) VectorCopy (in->point, out->position); } static void -Mod_LoadSubmodels (bsp_t *bsp) +Mod_LoadSubmodels (model_t *mod, bsp_t *bsp) { dmodel_t *in, *out; int count, i, j; + mod_brush_t *brush = &mod->brush; in = bsp->models; count = bsp->nummodels; - out = Hunk_AllocName (count * sizeof (*out), loadname); + out = Hunk_AllocName (0, count * sizeof (*out), mod->name); - loadmodel->submodels = out; - loadmodel->numsubmodels = count; + brush->submodels = out; + brush->numsubmodels = count; for (i = 0; i < count; i++, in++, out++) { static vec3_t offset = {1, 1, 1}; @@ -350,21 +460,16 @@ Mod_LoadSubmodels (bsp_t *bsp) out->numfaces = in->numfaces; } - out = loadmodel->submodels; - - if (out->visleafs > MAX_MAP_LEAFS) { - Sys_Error ("Mod_LoadSubmodels: too many visleafs (%d, max = %d) in %s", - out->visleafs, MAX_MAP_LEAFS, loadmodel->name); - } + out = brush->submodels; if (out->visleafs > 8192) - Sys_MaskPrintf (SYS_WARN, + Sys_MaskPrintf (SYS_warn, "%i visleafs exceeds standard limit of 8192.\n", out->visleafs); } static void -Mod_LoadEdges (bsp_t *bsp) +Mod_LoadEdges (model_t *mod, bsp_t *bsp) { dedge_t *in; int count, i; @@ -372,10 +477,10 @@ Mod_LoadEdges (bsp_t *bsp) in = bsp->edges; count = bsp->numedges; - out = Hunk_AllocName ((count + 1) * sizeof (*out), loadname); + out = Hunk_AllocName (0, (count + 1) * sizeof (*out), mod->name); - loadmodel->edges = out; - loadmodel->numedges = count; + mod->brush.edges = out; + mod->brush.numedges = count; for (i = 0; i < count; i++, in++, out++) { out->v[0] = in->v[0]; @@ -384,19 +489,19 @@ Mod_LoadEdges (bsp_t *bsp) } static void -Mod_LoadTexinfo (bsp_t *bsp) +Mod_LoadTexinfo (model_t *mod, bsp_t *bsp) { float len1, len2; - int count, miptex, i, j; + unsigned count, miptex, i, j; mtexinfo_t *out; texinfo_t *in; in = bsp->texinfo; count = bsp->numtexinfo; - out = Hunk_AllocName (count * sizeof (*out), loadname); + out = Hunk_AllocName (0, count * sizeof (*out), mod->name); - loadmodel->texinfo = out; - loadmodel->numtexinfo = count; + mod->brush.texinfo = out; + mod->brush.numtexinfo = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 4; j++) { @@ -419,13 +524,13 @@ Mod_LoadTexinfo (bsp_t *bsp) miptex = in->miptex; out->flags = in->flags; - if (!loadmodel->textures) { + if (!mod->brush.textures) { out->texture = r_notexture_mip; // checkerboard texture out->flags = 0; } else { - if (miptex >= loadmodel->numtextures) - Sys_Error ("miptex >= loadmodel->numtextures"); - out->texture = loadmodel->textures[miptex]; + if (miptex >= mod->brush.numtextures) + Sys_Error ("miptex >= mod->brush.numtextures"); + out->texture = mod->brush.textures[miptex]; if (!out->texture) { out->texture = r_notexture_mip; // texture not found out->flags = 0; @@ -440,13 +545,14 @@ Mod_LoadTexinfo (bsp_t *bsp) Fills in s->texturemins[] and s->extents[] */ static void -CalcSurfaceExtents (msurface_t *s) +CalcSurfaceExtents (model_t *mod, msurface_t *s) { float mins[2], maxs[2], val; int e, i, j; int bmins[2], bmaxs[2]; mtexinfo_t *tex; mvertex_t *v; + mod_brush_t *brush = &mod->brush; mins[0] = mins[1] = 999999; maxs[0] = maxs[1] = -99999; @@ -454,11 +560,11 @@ CalcSurfaceExtents (msurface_t *s) tex = s->texinfo; for (i = 0; i < s->numedges; i++) { - e = loadmodel->surfedges[s->firstedge + i]; + e = brush->surfedges[s->firstedge + i]; if (e >= 0) - v = &loadmodel->vertexes[loadmodel->edges[e].v[0]]; + v = &brush->vertexes[brush->edges[e].v[0]]; else - v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]]; + v = &brush->vertexes[brush->edges[-e].v[1]]; for (j = 0; j < 2; j++) { val = v->position[0] * tex->vecs[j][0] + @@ -485,23 +591,24 @@ CalcSurfaceExtents (msurface_t *s) } static void -Mod_LoadFaces (bsp_t *bsp) +Mod_LoadFaces (model_t *mod, bsp_t *bsp) { dface_t *in; int count, planenum, side, surfnum, i; msurface_t *out; + mod_brush_t *brush = &mod->brush; in = bsp->faces; count = bsp->numfaces; - out = Hunk_AllocName (count * sizeof (*out), loadname); + out = Hunk_AllocName (0, count * sizeof (*out), mod->name); if (count > 32767) { - Sys_MaskPrintf (SYS_WARN, + Sys_MaskPrintf (SYS_warn, "%i faces exceeds standard limit of 32767.\n", count); } - loadmodel->surfaces = out; - loadmodel->numsurfaces = count; + brush->surfaces = out; + brush->numsurfaces = count; for (surfnum = 0; surfnum < count; surfnum++, in++, out++) { out->firstedge = in->firstedge; @@ -513,11 +620,11 @@ Mod_LoadFaces (bsp_t *bsp) if (side) out->flags |= SURF_PLANEBACK; - out->plane = loadmodel->planes + planenum; + out->plane = brush->planes + planenum; - out->texinfo = loadmodel->texinfo + in->texinfo; + out->texinfo = brush->texinfo + in->texinfo; - CalcSurfaceExtents (out); + CalcSurfaceExtents (mod, out); // lighting info @@ -527,63 +634,92 @@ Mod_LoadFaces (bsp_t *bsp) if (i == -1) out->samples = NULL; else - out->samples = loadmodel->lightdata + (i * mod_lightmap_bytes); + out->samples = brush->lightdata + (i * mod_lightmap_bytes); // set the drawing flags flag - if (!out->texinfo->texture || !out->texinfo->texture->name) - continue; // avoid crashing on null textures - - if (!strncmp (out->texinfo->texture->name, "sky", 3)) { // sky - out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); - if (gl_sky_divide && gl_sky_divide->int_val) - if (mod_funcs) - mod_funcs->Mod_SubdivideSurface (out); + if (!out->texinfo->texture) { + // avoid crashing on null textures (which do exist) continue; } - if (out->texinfo->texture->name[0] == '*') { // turbulent - out->flags |= (SURF_DRAWTURB - | SURF_DRAWTILED - | SURF_LIGHTBOTHSIDES); - for (i = 0; i < 2; i++) { - out->extents[i] = 16384; - out->texturemins[i] = -8192; + if (!strncmp (out->texinfo->texture->name, "sky", 3)) { // sky + out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); + if (mod_sky_divide) { + if (mod_funcs && mod_funcs->Mod_SubdivideSurface) { + mod_funcs->Mod_SubdivideSurface (mod, out); + } } - if (mod_funcs) // cut up polygon for warps - mod_funcs->Mod_SubdivideSurface (out); continue; } + + switch (out->texinfo->texture->name[0]) { + case '*': // turbulent + out->flags |= (SURF_DRAWTURB + | SURF_DRAWTILED + | SURF_LIGHTBOTHSIDES); + for (i = 0; i < 2; i++) { + out->extents[i] = 16384; + out->texturemins[i] = -8192; + } + if (mod_funcs && mod_funcs->Mod_SubdivideSurface) { + // cut up polygon for warps + mod_funcs->Mod_SubdivideSurface (mod, out); + } + break; + case '{': + out->flags |= SURF_DRAWALPHA; + break; + } } } static void -Mod_SetParent (mnode_t *node, mnode_t *parent) +Mod_SetParent (mod_brush_t *brush, int node_id, int parent_id) { - node->parent = parent; - if (node->contents < 0) + if (node_id < 0) { + brush->leaf_parents[~node_id] = parent_id; return; - Mod_SetParent (node->children[0], node); - Mod_SetParent (node->children[1], node); + } + brush->node_parents[node_id] = parent_id; + mnode_t *node = brush->nodes + node_id; + Mod_SetParent (brush, node->children[0], node_id); + Mod_SetParent (brush, node->children[1], node_id); } static void -Mod_LoadNodes (bsp_t *bsp) +Mod_SetLeafFlags (mod_brush_t *brush) +{ + for (unsigned i = 0; i < brush->modleafs; i++) { + int flags = 0; + mleaf_t *leaf = &brush->leafs[i]; + msurface_t **msurf = brush->marksurfaces + leaf->firstmarksurface; + for (int j = 0; j < leaf->nummarksurfaces; j++) { + msurface_t *surf = *msurf++; + flags |= surf->flags; + } + brush->leaf_flags[i] = flags; + } +} + +static void +Mod_LoadNodes (model_t *mod, bsp_t *bsp) { dnode_t *in; int count, i, j, p; mnode_t *out; + mod_brush_t *brush = &mod->brush; in = bsp->nodes; count = bsp->numnodes; - out = Hunk_AllocName (count * sizeof (*out), loadname); + out = Hunk_AllocName (0, count * sizeof (*out), mod->name); if (count > 32767) { - Sys_MaskPrintf (SYS_WARN, + Sys_MaskPrintf (SYS_warn, "%i nodes exceeds standard limit of 32767.\n", count); } - loadmodel->nodes = out; - loadmodel->numnodes = count; + brush->nodes = out; + brush->numnodes = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) { @@ -591,8 +727,11 @@ Mod_LoadNodes (bsp_t *bsp) out->minmaxs[3 + j] = in->maxs[j]; } - p = in->planenum; - out->plane = loadmodel->planes + p; + plane_t *plane = brush->planes + in->planenum; + out->plane = loadvec3f (plane->normal); + out->plane[3] = -plane->dist; + out->type = plane->type; + out->signbits = plane->signbits; out->firstsurface = in->firstface; out->numsurfaces = in->numfaces; @@ -601,43 +740,44 @@ Mod_LoadNodes (bsp_t *bsp) p = in->children[j]; // this check is for extended bsp 29 files if (p >= 0) { - out->children[j] = loadmodel->nodes + p; + out->children[j] = p; } else { - p = ~p; - if (p < loadmodel->numleafs) { - out->children[j] = (mnode_t *) (loadmodel->leafs + p); + if ((unsigned) ~p < brush->modleafs) { + out->children[j] = p; } else { Sys_Printf ("Mod_LoadNodes: invalid leaf index %i " - "(file has only %i leafs)\n", p, - loadmodel->numleafs); + "(file has only %i leafs)\n", ~p, + brush->modleafs); //map it to the solid leaf - out->children[j] = (mnode_t *)(loadmodel->leafs); + out->children[j] = ~0; } } } } - Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs + size_t size = (brush->modleafs + brush->numnodes) * sizeof (int32_t); + size += brush->modleafs * sizeof (int); + brush->node_parents = Hunk_AllocName (0, size, mod->name); + brush->leaf_parents = brush->node_parents + brush->numnodes; + brush->leaf_flags = (int *) (brush->leaf_parents + brush->modleafs); + Mod_SetParent (brush, 0, -1); // sets nodes and leafs + Mod_SetLeafFlags (brush); } static void -Mod_LoadLeafs (bsp_t *bsp) +Mod_LoadLeafs (model_t *mod, bsp_t *bsp) { dleaf_t *in; int count, i, j, p; mleaf_t *out; - qboolean isnotmap = true; + mod_brush_t *brush = &mod->brush; in = bsp->leafs; count = bsp->numleafs; - out = Hunk_AllocName (count * sizeof (*out), loadname); + out = Hunk_AllocName (0, count * sizeof (*out), mod->name); - loadmodel->leafs = out; - loadmodel->numleafs = count; -// snprintf(s, sizeof (s), "maps/%s.bsp", -// Info_ValueForKey(cl.serverinfo,"map")); - if (!strncmp ("maps/", loadmodel->name, 5)) - isnotmap = false; + brush->leafs = out; + brush->modleafs = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) { out->mins[j] = in->mins[j]; @@ -647,58 +787,49 @@ Mod_LoadLeafs (bsp_t *bsp) p = in->contents; out->contents = p; - out->firstmarksurface = loadmodel->marksurfaces + in->firstmarksurface; + out->firstmarksurface = in->firstmarksurface; out->nummarksurfaces = in->nummarksurfaces; p = in->visofs; if (p == -1) out->compressed_vis = NULL; else - out->compressed_vis = loadmodel->visdata + p; + out->compressed_vis = brush->visdata + p; out->efrags = NULL; for (j = 0; j < 4; j++) out->ambient_sound_level[j] = in->ambient_level[j]; - - // gl underwater warp - if (out->contents != CONTENTS_EMPTY) { - for (j = 0; j < out->nummarksurfaces; j++) - out->firstmarksurface[j]->flags |= SURF_UNDERWATER; - } - if (isnotmap) { - for (j = 0; j < out->nummarksurfaces; j++) - out->firstmarksurface[j]->flags |= SURF_DONTWARP; - } } } static void -Mod_LoadClipnodes (bsp_t *bsp) +Mod_LoadClipnodes (model_t *mod, bsp_t *bsp) { dclipnode_t *in; - mclipnode_t *out; + dclipnode_t *out; hull_t *hull; - int count, i; + int count, i; + mod_brush_t *brush = &mod->brush; in = bsp->clipnodes; count = bsp->numclipnodes; - out = Hunk_AllocName (count * sizeof (*out), loadname); + out = Hunk_AllocName (0, count * sizeof (*out), mod->name); if (count > 32767) { - Sys_MaskPrintf (SYS_WARN, + Sys_MaskPrintf (SYS_warn, "%i clilpnodes exceeds standard limit of 32767.\n", count); } - loadmodel->clipnodes = out; - loadmodel->numclipnodes = count; + brush->clipnodes = out; + brush->numclipnodes = count; - hull = &loadmodel->hulls[1]; - loadmodel->hull_list[1] = hull; + hull = &brush->hulls[1]; + brush->hull_list[1] = hull; hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count - 1; - hull->planes = loadmodel->planes; + hull->planes = brush->planes; hull->clip_mins[0] = -16; hull->clip_mins[1] = -16; hull->clip_mins[2] = -24; @@ -706,12 +837,12 @@ Mod_LoadClipnodes (bsp_t *bsp) hull->clip_maxs[1] = 16; hull->clip_maxs[2] = 32; - hull = &loadmodel->hulls[2]; - loadmodel->hull_list[2] = hull; + hull = &brush->hulls[2]; + brush->hull_list[2] = hull; hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count - 1; - hull->planes = loadmodel->planes; + hull->planes = brush->planes; hull->clip_mins[0] = -32; hull->clip_mins[1] = -32; hull->clip_mins[2] = -24; @@ -721,7 +852,7 @@ Mod_LoadClipnodes (bsp_t *bsp) for (i = 0; i < count; i++, out++, in++) { out->planenum = in->planenum; - if (out->planenum < 0 || out->planenum >= loadmodel->numplanes) + if (out->planenum >= brush->numplanes) Sys_Error ("Mod_LoadClipnodes: planenum out of bounds"); out->children[0] = in->children[0]; out->children[1] = in->children[1]; @@ -746,96 +877,101 @@ Mod_LoadClipnodes (bsp_t *bsp) Replicate the drawing hull structure as a clipping hull */ static void -Mod_MakeHull0 (void) +Mod_MakeHull0 (model_t *mod, bsp_t *bsp) { - mclipnode_t *out; + dclipnode_t *out; hull_t *hull; int count, i, j; - mnode_t *in, *child; + mnode_t *in; + mod_brush_t *brush = &mod->brush; - hull = &loadmodel->hulls[0]; - loadmodel->hull_list[0] = hull; + hull = &brush->hulls[0]; + brush->hull_list[0] = hull; - in = loadmodel->nodes; - count = loadmodel->numnodes; - out = Hunk_AllocName (count * sizeof (*out), loadname); + in = brush->nodes; + count = brush->numnodes; + out = Hunk_AllocName (0, count * sizeof (*out), mod->name); hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count - 1; - hull->planes = loadmodel->planes; + hull->planes = brush->planes; for (i = 0; i < count; i++, out++, in++) { - out->planenum = in->plane - loadmodel->planes; + out->planenum = bsp->nodes[i].planenum; for (j = 0; j < 2; j++) { - child = in->children[j]; - if (child->contents < 0) - out->children[j] = child->contents; - else - out->children[j] = child - loadmodel->nodes; + int child_id = in->children[j]; + if (child_id < 0) { + out->children[j] = bsp->leafs[~child_id].contents; + } else { + out->children[j] = child_id; + } } } } static void -Mod_LoadMarksurfaces (bsp_t *bsp) +Mod_LoadMarksurfaces (model_t *mod, bsp_t *bsp) { - int count, i, j; + unsigned count, i, j; msurface_t **out; uint32_t *in; + mod_brush_t *brush = &mod->brush; in = bsp->marksurfaces; count = bsp->nummarksurfaces; - out = Hunk_AllocName (count * sizeof (*out), loadname); + out = Hunk_AllocName (0, count * sizeof (*out), mod->name); if (count > 32767) { - Sys_MaskPrintf (SYS_WARN, + Sys_MaskPrintf (SYS_warn, "%i marksurfaces exceeds standard limit of 32767.\n", count); } - loadmodel->marksurfaces = out; - loadmodel->nummarksurfaces = count; + brush->marksurfaces = out; + brush->nummarksurfaces = count; for (i = 0; i < count; i++) { j = in[i]; - if (j >= loadmodel->numsurfaces) + if (j >= brush->numsurfaces) Sys_Error ("Mod_ParseMarksurfaces: bad surface number"); - out[i] = loadmodel->surfaces + j; + out[i] = brush->surfaces + j; } } static void -Mod_LoadSurfedges (bsp_t *bsp) +Mod_LoadSurfedges (model_t *mod, bsp_t *bsp) { int count, i; int32_t *in; int *out; + mod_brush_t *brush = &mod->brush; in = bsp->surfedges; count = bsp->numsurfedges; - out = Hunk_AllocName (count * sizeof (*out), loadname); + out = Hunk_AllocName (0, count * sizeof (*out), mod->name); - loadmodel->surfedges = out; - loadmodel->numsurfedges = count; + brush->surfedges = out; + brush->numsurfedges = count; for (i = 0; i < count; i++) out[i] = in[i]; } static void -Mod_LoadPlanes (bsp_t *bsp) +Mod_LoadPlanes (model_t *mod, bsp_t *bsp) { dplane_t *in; int bits, count, i, j; plane_t *out; + mod_brush_t *brush = &mod->brush; in = bsp->planes; count = bsp->numplanes; - out = Hunk_AllocName (count * 2 * sizeof (*out), loadname); + out = Hunk_AllocName (0, count * 2 * sizeof (*out), mod->name); - loadmodel->planes = out; - loadmodel->numplanes = count; + brush->planes = out; + brush->numplanes = count; for (i = 0; i < count; i++, in++, out++) { bits = 0; @@ -857,12 +993,13 @@ do_checksums (const bsp_t *bsp, void *_mod) int i; model_t *mod = (model_t *) _mod; byte *base; + mod_brush_t *brush = &mod->brush; base = (byte *) bsp->header; // checksum all of the map, except for entities - mod->checksum = 0; - mod->checksum2 = 0; + brush->checksum = 0; + brush->checksum2 = 0; for (i = 0; i < HEADER_LUMPS; i++) { lump_t *lump = bsp->header->lumps + i; int csum; @@ -870,102 +1007,111 @@ do_checksums (const bsp_t *bsp, void *_mod) if (i == LUMP_ENTITIES) continue; csum = Com_BlockChecksum (base + lump->fileofs, lump->filelen); - mod->checksum ^= csum; + brush->checksum ^= csum; if (i != LUMP_VISIBILITY && i != LUMP_LEAFS && i != LUMP_NODES) - mod->checksum2 ^= csum; + brush->checksum2 ^= csum; } } static void -recurse_draw_tree (mnode_t *node, int depth) +recurse_draw_tree (mod_brush_t *brush, int node_id, int depth) { - if (!node || node->contents < 0) { - if (depth > loadmodel->depth) - loadmodel->depth = depth; + if (node_id < 0) { + if (depth > brush->depth) + brush->depth = depth; return; } - recurse_draw_tree (node->children[0], depth + 1); - recurse_draw_tree (node->children[1], depth + 1); + mnode_t *node = &brush->nodes[node_id]; + recurse_draw_tree (brush, node->children[0], depth + 1); + recurse_draw_tree (brush, node->children[1], depth + 1); } static void -Mod_FindDrawDepth (void) +Mod_FindDrawDepth (mod_brush_t *brush) { - loadmodel->depth = 0; - recurse_draw_tree (loadmodel->nodes, 1); + brush->depth = 0; + recurse_draw_tree (brush, 0, 1); } void Mod_LoadBrushModel (model_t *mod, void *buffer) { dmodel_t *bm; - int i, j; + unsigned i, j; bsp_t *bsp; - loadmodel->type = mod_brush; + mod->type = mod_brush; bsp = LoadBSPMem (buffer, qfs_filesize, do_checksums, mod); // load into heap - Mod_LoadVertexes (bsp); - Mod_LoadEdges (bsp); - Mod_LoadSurfedges (bsp); - Mod_LoadTextures (bsp); - if (mod_funcs) - mod_funcs->Mod_LoadLighting (bsp); - Mod_LoadPlanes (bsp); - Mod_LoadTexinfo (bsp); - Mod_LoadFaces (bsp); - Mod_LoadMarksurfaces (bsp); - Mod_LoadVisibility (bsp); - Mod_LoadLeafs (bsp); - Mod_LoadNodes (bsp); - Mod_LoadClipnodes (bsp); - Mod_LoadEntities (bsp); - Mod_LoadSubmodels (bsp); + Mod_LoadVertexes (mod, bsp); + Mod_LoadEdges (mod, bsp); + Mod_LoadSurfedges (mod, bsp); + Mod_LoadTextures (mod, bsp); + if (mod_funcs && mod_funcs->Mod_LoadLighting) { + mod_funcs->Mod_LoadLighting (mod, bsp); + } + Mod_LoadPlanes (mod, bsp); + Mod_LoadTexinfo (mod, bsp); + Mod_LoadFaces (mod, bsp); + Mod_LoadMarksurfaces (mod, bsp); + Mod_LoadVisibility (mod, bsp); + Mod_LoadLeafs (mod, bsp); + Mod_LoadNodes (mod, bsp); + Mod_LoadClipnodes (mod, bsp); + Mod_LoadEntities (mod, bsp); + Mod_LoadSubmodels (mod, bsp); + + Mod_MakeHull0 (mod, bsp); BSP_Free(bsp); - Mod_MakeHull0 (); - - Mod_FindDrawDepth (); + Mod_FindDrawDepth (&mod->brush); for (i = 0; i < MAX_MAP_HULLS; i++) - Mod_FindClipDepth (&mod->hulls[i]); + Mod_FindClipDepth (&mod->brush.hulls[i]); mod->numframes = 2; // regular and alternate animation // set up the submodels (FIXME: this is confusing) - for (i = 0; i < mod->numsubmodels; i++) { - bm = &mod->submodels[i]; + for (i = 0; i < mod->brush.numsubmodels; i++) { + bm = &mod->brush.submodels[i]; - mod->hulls[0].firstclipnode = bm->headnode[0]; - mod->hull_list[0] = &mod->hulls[0]; + mod->brush.hulls[0].firstclipnode = bm->headnode[0]; + mod->brush.hull_list[0] = &mod->brush.hulls[0]; for (j = 1; j < MAX_MAP_HULLS; j++) { - mod->hulls[j].firstclipnode = bm->headnode[j]; - mod->hulls[j].lastclipnode = mod->numclipnodes - 1; - mod->hull_list[j] = &mod->hulls[j]; + mod->brush.hulls[j].firstclipnode = bm->headnode[j]; + mod->brush.hulls[j].lastclipnode = mod->brush.numclipnodes - 1; + mod->brush.hull_list[j] = &mod->brush.hulls[j]; } - mod->firstmodelsurface = bm->firstface; - mod->nummodelsurfaces = bm->numfaces; + mod->brush.firstmodelsurface = bm->firstface; + mod->brush.nummodelsurfaces = bm->numfaces; VectorCopy (bm->maxs, mod->maxs); VectorCopy (bm->mins, mod->mins); mod->radius = RadiusFromBounds (mod->mins, mod->maxs); - mod->numleafs = bm->visleafs; + mod->brush.visleafs = bm->visleafs; + // The bsp file has leafs for all submodes and hulls, so update the + // leaf count for this model to be the correct number (which is one + // more than the number of visible leafs) + mod->brush.modleafs = bm->visleafs + 1; - if (i < mod->numsubmodels - 1) { + if (i < mod->brush.numsubmodels - 1) { // duplicate the basic information - char name[10]; + char name[12]; snprintf (name, sizeof (name), "*%i", i + 1); - loadmodel = Mod_FindName (name); - *loadmodel = *mod; - strcpy (loadmodel->name, name); - mod = loadmodel; + model_t *m = Mod_FindName (name); + *m = *mod; + strcpy (m->path, name); + mod = m; + // make sure clear is called only for the main model + m->clear = 0; + m->data = 0; } } } diff --git a/libs/models/brush/sw_model_brush.c b/libs/models/brush/sw_model_brush.c index c78cd12b4..ca16af58e 100644 --- a/libs/models/brush/sw_model_brush.c +++ b/libs/models/brush/sw_model_brush.c @@ -40,30 +40,14 @@ #include "mod_internal.h" - void -sw_Mod_SubdivideSurface (msurface_t *fa) -{ -} - -void -sw_Mod_ProcessTexture (texture_t *tx) -{ -} - -void -sw_Mod_LoadExternalTextures (model_t *mod) -{ -} - -void -sw_Mod_LoadLighting (bsp_t *bsp) +sw_Mod_LoadLighting (model_t *mod, bsp_t *bsp) { mod_lightmap_bytes = 1; if (!bsp->lightdatasize) { - loadmodel->lightdata = NULL; + mod->brush.lightdata = NULL; return; } - loadmodel->lightdata = Hunk_AllocName (bsp->lightdatasize, loadname); - memcpy (loadmodel->lightdata, bsp->lightdata, bsp->lightdatasize); + mod->brush.lightdata = Hunk_AllocName (0, bsp->lightdatasize, mod->name); + memcpy (mod->brush.lightdata, bsp->lightdata, bsp->lightdatasize); } diff --git a/libs/models/brush/vulkan_model_brush.c b/libs/models/brush/vulkan_model_brush.c new file mode 100644 index 000000000..94e428671 --- /dev/null +++ b/libs/models/brush/vulkan_model_brush.c @@ -0,0 +1,393 @@ +/* + vulkan_model_brush.c + + Vulkan support routines for model loading and caching + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/cvar.h" +#include "QF/dstring.h" +#include "QF/image.h" +#include "QF/qendian.h" +#include "QF/quakefs.h" +#include "QF/sys.h" +#include "QF/va.h" +#include "QF/Vulkan/qf_bsp.h" +#include "QF/Vulkan/qf_model.h" +#include "QF/Vulkan/qf_texture.h" +#include "QF/Vulkan/barrier.h" +#include "QF/Vulkan/command.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/image.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/staging.h" + +#include "qfalloca.h" +#include "compat.h" +#include "mod_internal.h" +#include "r_internal.h" +#include "vid_vulkan.h" + +static void +vulkan_brush_clear (model_t *mod, void *data) +{ + modelctx_t *mctx = data; + vulkan_ctx_t *ctx = mctx->ctx; + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + mod_brush_t *brush = &mod->brush; + + QFV_DeviceWaitIdle (device); + + for (unsigned i = 0; i < brush->numtextures; i++) { + texture_t *tx = brush->textures[i]; + if (!tx) { + continue; + } + vulktex_t *tex = tx->render; + dfunc->vkDestroyImage (device->dev, tex->tex->image, 0); + dfunc->vkDestroyImageView (device->dev, tex->tex->view, 0); + if (tex->descriptor) { + Vulkan_FreeTexture (ctx, tex->descriptor); + tex->descriptor = 0; + } + } + dfunc->vkFreeMemory (device->dev, mctx->texture_memory, 0); +} + +typedef int (*vprocess_t) (byte *, const byte *, size_t); + +static size_t +mipsize (size_t size) +{ + const int n = MIPLEVELS; + return size * ((1 << (2 * n)) - 1) / (3 * (1 << (2 * n - 2))); +} + +static void +transfer_mips (byte *dst, const void *_src, const texture_t *tx, byte *palette, + vprocess_t process) +{ + const byte *src = _src; + unsigned width = tx->width; + unsigned height = tx->height; + unsigned count, offset; + + for (int i = 0; i < MIPLEVELS; i++) { + // mip offsets are relative to the texture pointer rather than the + // end of the texture struct + offset = tx->offsets[i] - sizeof (texture_t); + count = width * height; + // use the upper block of the destination as a temporary buffer for + // the processed pixels. Vulkan_ExpandPalette works in a linearly + // increasing manner thus the processed pixels will be overwritten + // only after they have been read + byte *tmp = dst + count * 3; + process (tmp, src + offset, count); + Vulkan_ExpandPalette (dst, tmp, palette, 2, count); + dst += count * 4; + width >>= 1; + height >>= 1; + } +} + +static void +copy_mips (qfv_packet_t *packet, texture_t *tx, size_t offset, VkImage image, + int layer, qfv_devfuncs_t *dfunc) +{ + // base copy + VkBufferImageCopy copy = { + offset, tx->width, tx->height, + {VK_IMAGE_ASPECT_COLOR_BIT, 0, layer, 1}, + {0, 0, 0}, {tx->width, tx->height, 1}, + }; + int is_sky = 0; + int sky_offset = 0; + size_t size = tx->width * tx->height * 4; + int copy_count = MIPLEVELS; + + if (strncmp (tx->name, "sky", 3) == 0) { + if (tx->width == 2 * tx->height) { + copy.imageExtent.width /= 2; + // sky layers are interleaved on each row + sky_offset = tx->width * 4 / 2; + } + is_sky = 1; + copy_count *= 2; + } + + __auto_type copies = QFV_AllocBufferImageCopy (copy_count, alloca); + copies->size = 0; + + for (int i = 0; i < MIPLEVELS; i++) { + __auto_type c = &copies->a[copies->size++]; + *c = copy; + if (is_sky) { + __auto_type c = &copies->a[copies->size++]; + *c = copy; + c->bufferOffset += sky_offset; + c->imageSubresource.baseArrayLayer = 1; + } + copy.bufferOffset += size; + size >>= 2; + copy.bufferRowLength >>= 1; + copy.bufferImageHeight >>= 1; + copy.imageExtent.width >>= 1; + copy.imageExtent.height >>= 1; + copy.imageSubresource.mipLevel++; + sky_offset >>= 1; + } + dfunc->vkCmdCopyBufferToImage (packet->cmd, packet->stage->buffer, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + copies->size, copies->a); +} + +static void +transfer_texture (texture_t *tx, VkImage image, qfv_packet_t *packet, + byte *palette, qfv_devfuncs_t *dfunc) +{ + byte *base = packet->stage->data; + + size_t layer_size = mipsize (tx->width * tx->height * 4); + byte *dst = QFV_PacketExtend (packet, layer_size); + + qfv_imagebarrier_t ib = imageBarriers[qfv_LT_Undefined_to_TransferDst]; + ib.barrier.image = image; + ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); + + if (strncmp (tx->name, "sky", 3) == 0) { + transfer_mips (dst, tx + 1, tx, palette, (vprocess_t) memcpy); + copy_mips (packet, tx, dst - base, image, 0, dfunc); + } else { + transfer_mips (dst, tx + 1, tx, palette, Mod_ClearFullbright); + copy_mips (packet, tx, dst - base, image, 0, dfunc); + byte *glow = QFV_PacketExtend (packet, layer_size); + transfer_mips (glow, tx + 1, tx, palette, Mod_CalcFullbright); + copy_mips (packet, tx, glow - base, image, 1, dfunc); + } + + ib = imageBarriers[qfv_LT_TransferDst_to_ShaderReadOnly]; + ib.barrier.image = image; + ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); +} + +static void +load_textures (model_t *mod, vulkan_ctx_t *ctx) +{ + qfvPushDebug (ctx, va (ctx->va_ctx, "brush.load_textures: %s", mod->name)); + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + modelctx_t *mctx = mod->data; + mod_brush_t *brush = &mod->brush; + VkImage image = 0; + byte sky_palette[256 * 4]; + + memcpy (sky_palette, vid.palette32, sizeof (sky_palette)); + // sky's black is transparent + // this hits both layers, but so long as the screen is cleared + // to black, no one should notice :) + sky_palette[3] = 0; + + size_t image_count = 0; + size_t memsize = 0; + for (unsigned i = 0; i < brush->numtextures; i++) { + texture_t *tx = brush->textures[i]; + if (!tx) { + continue; + } + vulktex_t *tex = tx->render; + memsize += QFV_GetImageSize (device, tex->tex->image); + image_count++; + // just so we have one in the end + image = tex->tex->image; + } + VkDeviceMemory mem; + mem = QFV_AllocImageMemory (device, image, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + memsize, 0); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, + mem, va (ctx->va_ctx, "memory:%s:texture", mod->name)); + mctx->texture_memory = mem; + + qfv_stagebuf_t *stage = QFV_CreateStagingBuffer (device, + va (ctx->va_ctx, + "brush:%s", mod->name), + memsize, ctx->cmdpool); + qfv_packet_t *packet = QFV_PacketAcquire (stage); + size_t offset = 0; + for (unsigned i = 0; i < brush->numtextures; i++) { + texture_t *tx = brush->textures[i]; + + if (!tx) { + continue; + } + qfv_tex_t *tex = ((vulktex_t *) tx->render)->tex; + + dfunc->vkBindImageMemory (device->dev, tex->image, mem, offset); + offset += QFV_GetImageSize (device, tex->image); + + VkImageViewType type = VK_IMAGE_VIEW_TYPE_2D_ARRAY; + tex->view = QFV_CreateImageView (device, tex->image, + type, VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_ASPECT_COLOR_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE_VIEW, + tex->view, + va (ctx->va_ctx, "iview:%s:%s:tex", + mod->name, tx->name)); + + byte *palette = vid.palette32; + if (strncmp (tx->name, "sky", 3) == 0) { + palette = sky_palette; + } + transfer_texture (tx, tex->image, packet, palette, dfunc); + } + QFV_PacketSubmit (packet); + QFV_DestroyStagingBuffer (stage); + qfvPopDebug (ctx); +} + +void +Vulkan_Mod_ProcessTexture (model_t *mod, texture_t *tx, vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + + if (!tx) { + modelctx_t *mctx = Hunk_AllocName (0, sizeof (modelctx_t), mod->name); + mctx->ctx = ctx; + mod->clear = vulkan_brush_clear; + mod->data = mctx; + + if (mod->brush.numtextures) { + load_textures (mod, ctx); + } + return; + } + + vulktex_t *tex = tx->render; + tex->tex = (qfv_tex_t *) (tex + 1); + VkExtent3D extent = { tx->width, tx->height, 1 }; + + // Skies are two overlapping layers (one partly transparent), other + // textures are split into main color and glow color on separate layers + int layers = 2; + if (strncmp (tx->name, "sky", 3) == 0) { + // the sky texture is normally 2 side-by-side squares, but + // some maps have just a single square + if (tx->width == 2 * tx->height) { + extent.width /= 2; + } + } + + tex->tex->image = QFV_CreateImage (device, 0, VK_IMAGE_TYPE_2D, + VK_FORMAT_R8G8B8A8_UNORM, + extent, 4, layers, + VK_SAMPLE_COUNT_1_BIT, + VK_IMAGE_USAGE_TRANSFER_DST_BIT + | VK_IMAGE_USAGE_SAMPLED_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE, + tex->tex->image, + va (ctx->va_ctx, "image:%s:%s:tex", mod->name, + tx->name)); +} + +void +Vulkan_Mod_LoadLighting (model_t *mod, bsp_t *bsp, vulkan_ctx_t *ctx) +{ + mod_brush_t *brush = &mod->brush; + + mod_lightmap_bytes = 3; + if (!bsp->lightdatasize) { + brush->lightdata = NULL; + return; + } + + byte d; + byte *in, *out, *data; + size_t i; + int ver; + QFile *lit_file; + + brush->lightdata = 0; + if (mod_lightmap_bytes > 1) { + // LordHavoc: check for a .lit file to load + dstring_t *litfilename = dstring_new (); + dstring_copystr (litfilename, mod->name); + QFS_StripExtension (litfilename->str, litfilename->str); + dstring_appendstr (litfilename, ".lit"); + lit_file = QFS_VOpenFile (litfilename->str, 0, mod->vpath); + data = (byte *) QFS_LoadHunkFile (lit_file); + if (data) { + if (data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' + && data[3] == 'T') { + ver = LittleLong (((int32_t *) data)[1]); + if (ver == 1) { + Sys_MaskPrintf (SYS_dev, "%s loaded", litfilename->str); + brush->lightdata = data + 8; + } else { + Sys_MaskPrintf (SYS_dev, + "Unknown .lit file version (%d)\n", ver); + } + } else { + Sys_MaskPrintf (SYS_dev, "Corrupt .lit file (old version?)\n"); + } + } + dstring_delete (litfilename); + } + if (brush->lightdata || !bsp->lightdatasize) { + return; + } + // LordHavoc: oh well, expand the white lighting data + brush->lightdata = Hunk_AllocName (0, bsp->lightdatasize * 3, mod->name); + in = bsp->lightdata; + out = brush->lightdata; + + for (i = 0; i < bsp->lightdatasize ; i++) { + d = *in++; + *out++ = d; + *out++ = d; + *out++ = d; + } +} diff --git a/libs/models/clip_hull.c b/libs/models/clip_hull.c index f47c1886d..0c9820eff 100644 --- a/libs/models/clip_hull.c +++ b/libs/models/clip_hull.c @@ -36,6 +36,7 @@ #include "QF/clip_hull.h" #include "QF/model.h" +#include "mod_internal.h" VISIBLE clip_hull_t * MOD_Alloc_Hull (int nodes, int planes) @@ -44,7 +45,7 @@ MOD_Alloc_Hull (int nodes, int planes) int size, i; size = sizeof (hull_t); - size += sizeof (mclipnode_t) * nodes + sizeof (plane_t) * planes; + size += sizeof (dclipnode_t) * nodes + sizeof (plane_t) * planes; size *= MAX_MAP_HULLS; size += sizeof (clip_hull_t); @@ -54,11 +55,11 @@ MOD_Alloc_Hull (int nodes, int planes) ch->hulls[0] = (hull_t *) &ch[1]; for (i = 1; i < MAX_MAP_HULLS; i++) ch->hulls[i] = &ch->hulls[i - 1][1]; - ch->hulls[0]->clipnodes = (mclipnode_t *) &ch->hulls[i - 1][1]; + ch->hulls[0]->clipnodes = (dclipnode_t *) &ch->hulls[i - 1][1]; ch->hulls[0]->planes = (plane_t *) &ch->hulls[0]->clipnodes[nodes]; for (i = 1; i < MAX_MAP_HULLS; i++) { ch->hulls[i]->clipnodes = - (mclipnode_t *) &ch->hulls[i - 1]->planes[planes]; + (dclipnode_t *) &ch->hulls[i - 1]->planes[planes]; ch->hulls[i]->planes = (plane_t *) &ch->hulls[i]->clipnodes[nodes]; } return ch; @@ -73,7 +74,7 @@ MOD_Free_Hull (clip_hull_t *ch) static void recurse_clip_tree (hull_t *hull, int num, int depth) { - mclipnode_t *node; + dclipnode_t *node; if (num < 0) { if (depth > hull->depth) diff --git a/libs/models/fullbright.c b/libs/models/fullbright.c new file mode 100644 index 000000000..56588f4c3 --- /dev/null +++ b/libs/models/fullbright.c @@ -0,0 +1,65 @@ +/* + fullbright.c + + fullbright skin handling + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "mod_internal.h" + +VISIBLE int +Mod_CalcFullbright (byte *out, const byte *in, size_t pixels) +{ + byte fb = 0; + + while (pixels-- > 0) { + byte pix = *in++; + if (pix >= 256 - 32) { + fb = 1; + *out++ = pix; + } else { + *out++ = 0; + } + } + return fb; +} + +VISIBLE int +Mod_ClearFullbright (byte *out, const byte *in, size_t pixels) +{ + while (pixels-- > 0) { + byte pix = *in++; + if (pix >= 256 - 32) { + *out++ = 0; + } else { + *out++ = pix; + } + } + return 0; +} diff --git a/libs/models/gl_model_fullbright.c b/libs/models/gl_model_fullbright.c index f556c5dd7..fbb330b94 100644 --- a/libs/models/gl_model_fullbright.c +++ b/libs/models/gl_model_fullbright.c @@ -38,45 +38,33 @@ #include "QF/qendian.h" #include "QF/sys.h" -#include "r_local.h" - - -VISIBLE int -Mod_CalcFullbright (byte *in, byte *out, int pixels) -{ - int fb = 0; - - while (pixels--) { - if (*in >= 256 - 32) { - fb = 1; - *out++ = *in++; - } else { - *out++ = 255; - in++; - } - } - return fb; -} +#include "mod_internal.h" int -Mod_Fullbright (byte *skin, int width, int height, char *name) +Mod_Fullbright (byte *skin, int width, int height, const char *name) { - byte *ptexels; + byte *texels; int pixels; int texnum = 0; pixels = width * height; -// ptexels = Hunk_Alloc(s); - ptexels = malloc (pixels); - SYS_CHECKMEM (ptexels); + texels = malloc (pixels); + SYS_CHECKMEM (texels); // Check for fullbright pixels - if (Mod_CalcFullbright (skin, ptexels, pixels)) { - Sys_MaskPrintf (SYS_DEV, "FB Model ID: '%s'\n", name); - texnum = GL_LoadTexture (name, width, height, ptexels, true, true, 1); + if (Mod_CalcFullbright (texels, skin, pixels)) { + //FIXME black should be transparent for fullbrights (or just fix + //fullbright rendering in gl) + Sys_MaskPrintf (SYS_dev, "FB Model ID: '%s'\n", name); + for (int i = 0; i < pixels; i++) { + if (!texels[i]) { + texels[i] = 255; + } + } + texnum = GL_LoadTexture (name, width, height, texels, true, true, 1); } - free (ptexels); + free (texels); return texnum; } diff --git a/libs/models/gl_skin.c b/libs/models/gl_skin.c index 442def004..b8b17c5e8 100644 --- a/libs/models/gl_skin.c +++ b/libs/models/gl_skin.c @@ -57,11 +57,11 @@ typedef struct { tex_t *tex; tex_t *fb_tex; - qboolean fb; + bool fb; } glskin_t; -static int skin_textures; -static int skin_fb_textures; +static GLuint skin_textures[MAX_TRANSLATIONS]; +static GLuint skin_fb_textures[MAX_TRANSLATIONS]; static byte skin_cmap[MAX_TRANSLATIONS][256]; static glskin_t skins[MAX_TRANSLATIONS]; @@ -72,12 +72,13 @@ do_fb_skin (glskin_t *s) { int size = s->tex->width * s->tex->height; - s->fb_tex = realloc (s->fb_tex, field_offset(tex_t, data[size])); + s->fb_tex = realloc (s->fb_tex, sizeof (tex_t) + size); + s->fb_tex->data = (byte *) (s->fb_tex + 1); s->fb_tex->width = s->tex->width; s->fb_tex->height = s->tex->height; s->fb_tex->format = tex_palette; s->fb_tex->palette = vid.palette; - s->fb = Mod_CalcFullbright (s->tex->data, s->fb_tex->data, size); + s->fb = Mod_CalcFullbright (s->fb_tex->data, s->tex->data, size); } void @@ -87,7 +88,8 @@ gl_Skin_SetPlayerSkin (int width, int height, const byte *data) glskin_t *s; s = &player_skin; - s->tex = realloc (s->tex, field_offset(tex_t, data[size])); + s->tex = realloc (s->tex, sizeof (tex_t) + size); + s->tex->data = (byte *) (s->tex + 1); s->tex->width = width; s->tex->height = height; s->tex->format = tex_palette; @@ -99,7 +101,7 @@ gl_Skin_SetPlayerSkin (int width, int height, const byte *data) static void build_skin_8 (tex_t *tex, int texnum, byte *translate, - unsigned scaled_width, unsigned scaled_height, qboolean alpha) + unsigned scaled_width, unsigned scaled_height, bool alpha) { // Improvements should be mirrored in GL_ResampleTexture in gl_textures.c byte *inrow; @@ -125,14 +127,15 @@ build_skin_8 (tex_t *tex, int texnum, byte *translate, static void build_skin_32 (tex_t *tex, int texnum, byte *translate, - unsigned scaled_width, unsigned scaled_height, qboolean alpha) + unsigned scaled_width, unsigned scaled_height, bool alpha) { // Improvements should be mirrored in GL_ResampleTexture in gl_textures.c byte *inrow; unsigned i, j; int samples = alpha ? gl_alpha_format : gl_solid_format; unsigned frac, fracstep; - byte pixels[512 * 256 * 4], *out, *pal; + byte pixels[512 * 256 * 4], *out; + const byte *pal; byte c; out = pixels; @@ -171,12 +174,12 @@ build_skin (skin_t *skin, int cmap) int texnum, fb_texnum; // FIXME deek: This 512x256 limit sucks! - scaled_width = min (gl_max_size->int_val, 512); - scaled_height = min (gl_max_size->int_val, 256); + scaled_width = min (gl_max_size, 512); + scaled_height = min (gl_max_size, 256); // allow users to crunch sizes down even more if they want - scaled_width >>= gl_playermip->int_val; - scaled_height >>= gl_playermip->int_val; + scaled_width >>= gl_playermip; + scaled_height >>= gl_playermip; scaled_width = max (scaled_width, 1); scaled_height = max (scaled_height, 1); @@ -186,10 +189,10 @@ build_skin (skin_t *skin, int cmap) if (!s->tex) // we haven't loaded the player model yet return; - texnum = skin_textures + cmap; + texnum = skin_textures[cmap]; fb_texnum = 0; if (s->fb) - fb_texnum = skin_fb_textures + cmap; + fb_texnum = skin_fb_textures[cmap]; if (skin) { skin->texnum = texnum; skin->auxtex = fb_texnum; @@ -242,9 +245,9 @@ gl_Skin_SetupSkin (skin_t *skin, int cmap) changed = (s->tex != skin->texels); s->tex = skin->texels; if (!changed) { - skin->texnum = skin_textures + cmap; + skin->texnum = skin_textures[cmap]; if (s->fb) - skin->auxtex = skin_fb_textures + cmap; + skin->auxtex = skin_fb_textures[cmap]; return; } if (s->tex) @@ -257,12 +260,9 @@ gl_Skin_InitTranslations (void) { } -int -gl_Skin_Init_Textures (int base) +void +gl_Skin_Init_Textures (void) { - skin_textures = base; - base += MAX_TRANSLATIONS; - skin_fb_textures = base; - base += MAX_TRANSLATIONS; - return base; + qfglGenTextures (MAX_TRANSLATIONS, skin_textures); + qfglGenTextures (MAX_TRANSLATIONS, skin_fb_textures); } diff --git a/libs/models/glsl_skin.c b/libs/models/glsl_skin.c index c2e5d39c3..8e6d74878 100644 --- a/libs/models/glsl_skin.c +++ b/libs/models/glsl_skin.c @@ -67,7 +67,7 @@ glsl_Skin_ProcessTranslation (int cmap, const byte *translation) for (i = 0, dst = top; i < VID_GRADES; i++, src += 256 - 16) { for (j = 0; j < 16; j++) { byte c = *src++; - byte *in = vid.palette + c * 3; + const byte *in = vid.palette + c * 3; *dst++ = *in++; *dst++ = *in++; *dst++ = *in++; @@ -78,7 +78,7 @@ glsl_Skin_ProcessTranslation (int cmap, const byte *translation) for (i = 0, dst = bottom; i < VID_GRADES; i++, src += 256 - 16) { for (j = 0; j < 16; j++) { byte c = *src++; - byte *in = vid.palette + c * 3; + const byte *in = vid.palette + c * 3; *dst++ = *in++; *dst++ = *in++; *dst++ = *in++; @@ -129,7 +129,7 @@ glsl_Skin_InitTranslations (void) for (i = 0, dst = map, src = vid.colormap8; i < 256 * VID_GRADES; i++) { byte c = *src++; - byte *in = vid.palette + c * 3; + const byte *in = vid.palette + c * 3; *dst++ = *in++; *dst++ = *in++; *dst++ = *in++; diff --git a/libs/models/iqm/Makefile.am b/libs/models/iqm/Makefile.am deleted file mode 100644 index 71f54f576..000000000 --- a/libs/models/iqm/Makefile.am +++ /dev/null @@ -1,20 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CFLAGS= @PREFER_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include - -noinst_LTLIBRARIES= @iqm_libs@ -EXTRA_LTLIBRARIES=libiqm_gl.la libiqm_glsl.la libiqm_sw.la - -iqm_src= model_iqm.c -gl_src= gl_model_iqm.c -glsl_src= glsl_model_iqm.c -sw_src= sw_model_iqm.c - -libiqm_gl_la_SOURCES= $(gl_src) $(iqm_src) - -libiqm_glsl_la_SOURCES= $(glsl_src) $(iqm_src) - -libiqm_sw_la_SOURCES= $(sw_src) $(iqm_src) - -EXTRA_DIST= $(gl_src) $(glsl_src) $(sw_src) $(iqm_src) diff --git a/libs/models/iqm/Makemodule.am b/libs/models/iqm/Makemodule.am new file mode 100644 index 000000000..cc362050d --- /dev/null +++ b/libs/models/iqm/Makemodule.am @@ -0,0 +1,27 @@ +noinst_LTLIBRARIES += @iqm_libs@ +EXTRA_LTLIBRARIES += \ + libs/models/iqm/libiqm_gl.la \ + libs/models/iqm/libiqm_glsl.la \ + libs/models/iqm/libiqm_sw.la \ + libs/models/iqm/libiqm_vulkan.la + +iqm_src= libs/models/iqm/model_iqm.c +iqm_gl_src= libs/models/iqm/gl_model_iqm.c +iqm_glsl_src= libs/models/iqm/glsl_model_iqm.c +iqm_sw_src= libs/models/iqm/sw_model_iqm.c +iqm_vulkan_src= libs/models/iqm/vulkan_model_iqm.c + +libs_models_iqm_libiqm_gl_la_SOURCES= $(iqm_gl_src) $(iqm_src) + +libs_models_iqm_libiqm_glsl_la_SOURCES= $(iqm_glsl_src) $(iqm_src) + +libs_models_iqm_libiqm_sw_la_SOURCES= $(iqm_sw_src) $(iqm_src) + +libs_models_iqm_libiqm_vulkan_la_SOURCES= $(iqm_vulkan_src) $(iqm_src) + +EXTRA_DIST += \ + $(iqm_gl_src) \ + $(iqm_glsl_src) \ + $(iqm_sw_src) \ + $(iqm_vulkan_src) \ + $(iqm_src) diff --git a/libs/models/iqm/gl_model_iqm.c b/libs/models/iqm/gl_model_iqm.c index e2e877d1d..fc9c50f1a 100644 --- a/libs/models/iqm/gl_model_iqm.c +++ b/libs/models/iqm/gl_model_iqm.c @@ -56,7 +56,7 @@ static byte null_texture[] = { }; static void -gl_iqm_clear (model_t *mod) +gl_iqm_clear (model_t *mod, void *data) { iqm_t *iqm = (iqm_t *) mod->aliashdr; gliqm_t *gl = (gliqm_t *) iqm->extra_data; @@ -80,7 +80,7 @@ gl_iqm_load_textures (iqm_t *iqm) for (i = 0; i < iqm->num_meshes; i++) { dstring_copystr (str, iqm->text + iqm->meshes[i].material); QFS_StripExtension (str->str, str->str); - if ((tex = LoadImage (va ("textures/%s", str->str)))) + if ((tex = LoadImage (va (0, "textures/%s", str->str), 1))) gl->textures[i] = GL_LoadTexture (str->str, tex->width, tex->height, tex->data, true, false, diff --git a/libs/models/iqm/glsl_model_iqm.c b/libs/models/iqm/glsl_model_iqm.c index 5e1a26030..9f7a75ee6 100644 --- a/libs/models/iqm/glsl_model_iqm.c +++ b/libs/models/iqm/glsl_model_iqm.c @@ -68,7 +68,7 @@ static byte null_normmap[] = { }; static void -glsl_iqm_clear (model_t *mod) +glsl_iqm_clear (model_t *mod, void *data) { iqm_t *iqm = (iqm_t *) mod->aliashdr; glsliqm_t *glsl = (glsliqm_t *) iqm->extra_data; @@ -85,6 +85,7 @@ glsl_iqm_clear (model_t *mod) GLSL_ReleaseTexture (glsl->textures[i]); GLSL_ReleaseTexture (glsl->normmaps[i]); } + free (glsl->textures); free (glsl); Mod_FreeIQM (iqm); } @@ -102,12 +103,12 @@ glsl_iqm_load_textures (iqm_t *iqm) for (i = 0; i < iqm->num_meshes; i++) { dstring_copystr (str, iqm->text + iqm->meshes[i].material); QFS_StripExtension (str->str, str->str); - if ((tex = LoadImage (va ("textures/%s", str->str)))) + if ((tex = LoadImage (va (0, "textures/%s", str->str), 1))) glsl->textures[i] = GLSL_LoadRGBATexture (str->str, tex->width, tex->height, tex->data); else glsl->textures[i] = GLSL_LoadRGBATexture ("", 2, 2, null_texture); - if ((tex = LoadImage (va ("textures/%s_norm", str->str)))) + if ((tex = LoadImage (va (0, "textures/%s_norm", str->str), 1))) glsl->normmaps[i] = GLSL_LoadRGBATexture (str->str, tex->width, tex->height, tex->data); else diff --git a/libs/models/iqm/model_iqm.c b/libs/models/iqm/model_iqm.c index 6eabf7117..147bf8dba 100644 --- a/libs/models/iqm/model_iqm.c +++ b/libs/models/iqm/model_iqm.c @@ -118,7 +118,6 @@ get_joints (const iqmheader *hdr, byte *buffer) { iqmjoint *joint; uint32_t i, j; - float t; if (hdr->ofs_joints + hdr->num_joints * sizeof (iqmjoint) > hdr->filesize) return 0; @@ -135,17 +134,13 @@ get_joints (const iqmheader *hdr, byte *buffer) joint[i].translate[j] = LittleFloat (joint[i].translate[j]); for (j = 0; j < 4; j++) joint[i].rotate[j] = LittleFloat (joint[i].rotate[j]); - // iqm quaternions use xyzw but QF quaternions use wxyz - t = joint[i].rotate[3]; - memmove (&joint[i].rotate[1], &joint[i].rotate[0], 3 * sizeof (float)); - joint[i].rotate[0] = t; for (j = 0; j < 3; j++) joint[i].scale[j] = LittleFloat (joint[i].scale[j]); } return joint; } -static qboolean +static bool load_iqm_vertex_arrays (model_t *mod, const iqmheader *hdr, byte *buffer) { iqm_t *iqm = (iqm_t *) mod->aliashdr; @@ -167,7 +162,7 @@ load_iqm_vertex_arrays (model_t *mod, const iqmheader *hdr, byte *buffer) for (i = 0; i < hdr->num_vertexarrays; i++) { va = vas + i; - Sys_MaskPrintf (SYS_MODEL, "%u %u %u %u %u %u\n", i, va->type, va->flags, va->format, va->size, va->offset); + Sys_MaskPrintf (SYS_model, "%u %u %u %u %u %u\n", i, va->type, va->flags, va->format, va->size, va->offset); switch (va->type) { case IQM_POSITION: if (position) @@ -333,7 +328,7 @@ load_iqm_vertex_arrays (model_t *mod, const iqmheader *hdr, byte *buffer) return true; } -static qboolean +static bool load_iqm_meshes (model_t *mod, const iqmheader *hdr, byte *buffer) { iqm_t *iqm = (iqm_t *) mod->aliashdr; @@ -380,7 +375,7 @@ load_iqm_meshes (model_t *mod, const iqmheader *hdr, byte *buffer) return true; } -static qboolean +static bool load_iqm_anims (model_t *mod, const iqmheader *hdr, byte *buffer) { iqm_t *iqm = (iqm_t *) mod->aliashdr; @@ -442,19 +437,18 @@ load_iqm_anims (model_t *mod, const iqmheader *hdr, byte *buffer) if (p->mask & 0x004) translation[2] += *framedata++ * p->channelscale[2]; - // QF's quaternions are wxyz while IQM's quaternions are xyzw - rotation[1] = p->channeloffset[3]; + rotation[0] = p->channeloffset[3]; if (p->mask & 0x008) - rotation[1] += *framedata++ * p->channelscale[3]; - rotation[2] = p->channeloffset[4]; + rotation[0] += *framedata++ * p->channelscale[3]; + rotation[1] = p->channeloffset[4]; if (p->mask & 0x010) - rotation[2] += *framedata++ * p->channelscale[4]; - rotation[3] = p->channeloffset[5]; + rotation[1] += *framedata++ * p->channelscale[4]; + rotation[2] = p->channeloffset[5]; if (p->mask & 0x020) - rotation[3] += *framedata++ * p->channelscale[5]; - rotation[0] = p->channeloffset[6]; + rotation[2] += *framedata++ * p->channelscale[5]; + rotation[3] = p->channeloffset[6]; if (p->mask & 0x040) - rotation[0] += *framedata++ * p->channelscale[6]; + rotation[3] += *framedata++ * p->channelscale[6]; scale[0] = p->channeloffset[7]; if (p->mask & 0x080) @@ -503,25 +497,25 @@ Mod_LoadIQM (model_t *mod, void *buffer) uint32_t *swap; if (!strequal (hdr->magic, IQM_MAGIC)) - Sys_Error ("%s: not an IQM", loadname); + Sys_Error ("%s: not an IQM", mod->path); // Byte swap the header. Everything is the same type, so no problem :) for (swap = &hdr->version; swap <= &hdr->ofs_extensions; swap++) *swap = LittleLong (*swap); //if (hdr->version < 1 || hdr->version > IQM_VERSION) if (hdr->version != IQM_VERSION) - Sys_Error ("%s: unable to handle iqm version %d", loadname, + Sys_Error ("%s: unable to handle iqm version %d", mod->path, hdr->version); if (hdr->filesize != (uint32_t) qfs_filesize) - Sys_Error ("%s: invalid filesize", loadname); + Sys_Error ("%s: invalid filesize", mod->path); iqm = calloc (1, sizeof (iqm_t)); iqm->text = malloc (hdr->num_text); memcpy (iqm->text, (byte *) buffer + hdr->ofs_text, hdr->num_text); mod->aliashdr = (aliashdr_t *) iqm; mod->type = mod_iqm; if (hdr->num_meshes && !load_iqm_meshes (mod, hdr, (byte *) buffer)) - Sys_Error ("%s: error loading meshes", loadname); + Sys_Error ("%s: error loading meshes", mod->path); if (hdr->num_anims && !load_iqm_anims (mod, hdr, (byte *) buffer)) - Sys_Error ("%s: error loading anims", loadname); + Sys_Error ("%s: error loading anims", mod->path); m_funcs->Mod_IQMFinish (mod); } @@ -594,7 +588,7 @@ Mod_IQMBuildBlendPalette (iqm_t *iqm, int *size) if (!bindices || !bweights) { // Not necessarily an error: might be a static model with no bones // Either way, no need to make a blend palette - Sys_MaskPrintf (SYS_MODEL, "bone index or weight array missing\n"); + Sys_MaskPrintf (SYS_model, "bone index or weight array missing\n"); *size = 0; return 0; } @@ -606,7 +600,7 @@ Mod_IQMBuildBlendPalette (iqm_t *iqm, int *size) } num_blends = iqm->num_joints; - blend_hash = Hash_NewTable (1023, 0, 0, 0); + blend_hash = Hash_NewTable (1023, 0, 0, 0, 0); Hash_SetHashCompare (blend_hash, blend_get_hash, blend_compare); for (i = 0; i < iqm->num_verts; i++) { diff --git a/libs/models/iqm/sw_model_iqm.c b/libs/models/iqm/sw_model_iqm.c index 2a32ecca0..0e6d1fa48 100644 --- a/libs/models/iqm/sw_model_iqm.c +++ b/libs/models/iqm/sw_model_iqm.c @@ -53,12 +53,18 @@ #include "mod_internal.h" #include "r_internal.h" +static byte null_data[] = {15, 15, 15, 15}; static tex_t null_texture = { - 2, 2, tex_palette, 0, {15, 15, 15, 15} + .width = 2, + .height = 2, + .format = tex_palette, + .loaded = 1, + .palette =0, + .data = null_data }; static void -sw_iqm_clear (model_t *mod) +sw_iqm_clear (model_t *mod, void *data) { iqm_t *iqm = (iqm_t *) mod->aliashdr; swiqm_t *sw = (swiqm_t *) iqm->extra_data; @@ -81,59 +87,6 @@ sw_iqm_clear (model_t *mod) Mod_FreeIQM (iqm); } -static byte -convert_color (byte *rgb) -{ - //FIXME slow! - int dist[3]; - int d, bestd = 256 * 256 * 3, bestc = -1; - int i; - - for (i = 0; i < 256; i++) { - VectorSubtract (vid.basepal + i * 3, rgb, dist); - d = DotProduct (dist, dist); - if (d < bestd) { - bestd = d; - bestc = i; - } - } - return bestc; -} - -static tex_t * -convert_tex (tex_t *tex) -{ - tex_t *new; - int pixels; - int bpp = 3; - int i; - - pixels = tex->width * tex->height; - new = malloc (field_offset (tex_t, data[pixels])); - new->width = tex->width; - new->height = tex->height; - new->format = tex_palette; - new->palette = 0; - switch (tex->format) { - case tex_palette: - case tex_l: // will not work as expected - case tex_a: // will not work as expected - memcpy (new->data, tex->data, pixels); - break; - case tex_la: // will not work as expected - for (i = 0; i < pixels; i++) - new->data[i] = tex->data[i * 2]; - break; - case tex_rgba: - bpp = 4; - case tex_rgb: - for (i = 0; i < pixels; i++) - new->data[i] = convert_color (tex->data + i * bpp); - break; - } - return new; -} - static inline void convert_coord (byte *tc, int size) { @@ -153,7 +106,7 @@ sw_iqm_load_textures (iqm_t *iqm) bytes = (iqm->num_verts + 7) / 8; done_verts = alloca (bytes); memset (done_verts, 0, bytes); - sw->skins = malloc (iqm->num_meshes * sizeof (tex_t)); + sw->skins = malloc (iqm->num_meshes * sizeof (tex_t *)); for (i = 0; i < iqm->num_meshes; i++) { for (j = 0; j < i; j++) { if (iqm->meshes[j].material == iqm->meshes[i].material) { @@ -165,8 +118,8 @@ sw_iqm_load_textures (iqm_t *iqm) continue; dstring_copystr (str, iqm->text + iqm->meshes[i].material); QFS_StripExtension (str->str, str->str); - if ((tex = LoadImage (va ("textures/%s", str->str)))) - tex = sw->skins[i] = convert_tex (tex); + if ((tex = LoadImage (va (0, "textures/%s", str->str), 1))) + tex = sw->skins[i] = ConvertImage (tex, vid.basepal); else tex = sw->skins[i] = &null_texture; for (j = 0; j < (int) iqm->meshes[i].num_triangles * 3; j++) { diff --git a/libs/models/iqm/vulkan_model_iqm.c b/libs/models/iqm/vulkan_model_iqm.c new file mode 100644 index 000000000..b86e4108d --- /dev/null +++ b/libs/models/iqm/vulkan_model_iqm.c @@ -0,0 +1,464 @@ +/* + vulkan_model_iqm.c + + iqm model processing for Vulkan + + Copyright (C) 2011 Bill Currie + + Author: Bill Currie + Date: 2022/05/03 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "QF/dstring.h" +#include "QF/image.h" +#include "QF/quakefs.h" +#include "QF/va.h" + +#include "QF/Vulkan/barrier.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/image.h" +#include "QF/Vulkan/resource.h" +#include "QF/Vulkan/staging.h" +#include "QF/Vulkan/qf_iqm.h" +#include "QF/Vulkan/qf_texture.h" + +#include "mod_internal.h" +#include "r_shared.h" +#include "vid_vulkan.h" + +static byte null_texture[] = { + 204, 204, 204, 255, + 204, 204, 204, 255, + 204, 204, 204, 255, + 204, 204, 204, 255, +}; +#if 0 +static byte null_normmap[] = { + 127, 127, 255, 255, + 127, 127, 255, 255, + 127, 127, 255, 255, + 127, 127, 255, 255, +}; +#endif +static void +vulkan_iqm_clear (model_t *mod, void *data) +{ + vulkan_ctx_t *ctx = data; + qfv_device_t *device = ctx->device; + iqm_t *iqm = (iqm_t *) mod->aliashdr; + qfv_iqm_t *mesh = iqm->extra_data; + + mod->needload = true; + + for (int i = 0; i < iqm->num_meshes; i++) { + Vulkan_IQMRemoveSkin (ctx, &mesh->skins[i]); + } + Vulkan_IQMRemoveBones (ctx, iqm);//FIXME doesn't belong here (per-instance) + + QFV_DestroyResource (device, mesh->bones); + QFV_DestroyResource (device, mesh->mesh); + free (mesh); + Mod_FreeIQM (iqm); +} + +static void +vulkan_iqm_init_image (iqm_t *iqm, int meshnum, qfv_resobj_t *image) +{ + const char *material = iqm->text + iqm->meshes[meshnum].material; + dstring_t *str = dstring_new (); + dstring_copystr (str, material); + QFS_StripExtension (str->str, str->str); + + tex_t dummy_tex; + tex_t *tex; + if (!(tex = LoadImage (va (0, "textures/%s", str->str), 0))) { + dummy_tex = (tex_t) { + .width = 2, + .height = 2, + .format = tex_rgba, + }; + tex = &dummy_tex; + } + QFV_ResourceInitTexImage (image, material, 1, tex); + dstring_delete (str); +} + +static void +iqm_transfer_texture (tex_t *tex, VkImage image, qfv_stagebuf_t *stage, + qfv_device_t *device) +{ + qfv_devfuncs_t *dfunc = device->funcs; + + if (tex->format != tex_rgb && tex->format != tex_rgba) { + Sys_Error ("can't transfer iqm image"); + } + // FIXME correct only for rgb and rgba + size_t layer_size = tex->width * tex->height * tex->format; + + qfv_packet_t *packet = QFV_PacketAcquire (stage); + byte *dst = QFV_PacketExtend (packet, layer_size); + + qfv_imagebarrier_t ib = imageBarriers[qfv_LT_Undefined_to_TransferDst]; + ib.barrier.image = image; + ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); + memcpy (dst, tex->data, layer_size); + VkBufferImageCopy copy = { + packet->offset, 0, 0, + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, + {0, 0, 0}, {tex->width, tex->height, 1}, + }; + dfunc->vkCmdCopyBufferToImage (packet->cmd, packet->stage->buffer, + image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, ©); + + int mipLevels = QFV_MipLevels (tex->width, tex->height); + if (mipLevels == 1) { + ib = imageBarriers[qfv_LT_TransferDst_to_ShaderReadOnly]; + ib.barrier.image = image; + ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); + } else { + QFV_GenerateMipMaps (device, packet->cmd, image, mipLevels, + tex->width, tex->height, 1); + } + QFV_PacketSubmit (packet); +} + +static void +vulkan_iqm_load_textures (model_t *mod, iqm_t *iqm, qfv_iqm_t *mesh, + vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + dstring_t *str = dstring_new (); + tex_t *tex; + size_t buff_size = 0; + qfv_resobj_t *objects = mesh->mesh->objects; + + for (int i = 0; i < iqm->num_meshes; i++) { + int image_ind = 3 + 2 * i; + VkExtent3D extent = objects[image_ind].image.extent; + // probably 3 or 4 bytes per pixel FIXME + buff_size = max (buff_size, extent.width * extent.height * 4); + } + + qfv_stagebuf_t *stage = QFV_CreateStagingBuffer (device, + va (ctx->va_ctx, "iqm:%s", + mod->name), + 4 * buff_size, + ctx->cmdpool); + + for (int i = 0; i < iqm->num_meshes; i++) { + int image_ind = 3 + 2 * i; + __auto_type image = &objects[image_ind].image; + __auto_type view = &objects[image_ind + 1].image_view; + qfv_iqm_skin_t *skin = &mesh->skins[i]; + *skin = (qfv_iqm_skin_t) { + .view = view->view, + .colora = { 255, 255, 255, 255 }, + .colorb = { 255, 255, 255, 255 }, + }; + + dstring_copystr (str, iqm->text + iqm->meshes[i].material); + QFS_StripExtension (str->str, str->str); + if (!(tex = LoadImage (va (0, "textures/%s", str->str), 1))) { + tex_t null_tex = { + .width = 2, + .height = 2, + .format = tex_rgba, + .data = null_texture, + }; + tex = &null_tex; + } + iqm_transfer_texture (tex, image->image, stage, device); + Vulkan_IQMAddSkin (ctx, skin); + } + dstring_delete (str); + QFV_DestroyStagingBuffer (stage); +} + +static void +vulkan_iqm_load_arrays (model_t *mod, iqm_t *iqm, qfv_iqm_t *mesh, + vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + iqmctx_t *ictx = ctx->iqm_context; + + size_t geom_size = iqm->num_verts * sizeof (iqmgvert_t); + size_t rend_size = iqm->num_verts * sizeof (iqmrvert_t); + size_t elem_size = iqm->num_elements * sizeof (uint16_t); + size_t buff_size = geom_size + rend_size + elem_size + 1024; + qfv_stagebuf_t *stage = QFV_CreateStagingBuffer (device, + va (ctx->va_ctx, "iqm:%s", + mod->name), + buff_size, ctx->cmdpool); + qfv_packet_t *gpacket = QFV_PacketAcquire (stage); + iqmgvert_t *gverts = QFV_PacketExtend (gpacket, geom_size); + qfv_packet_t *rpacket = QFV_PacketAcquire (stage); + iqmrvert_t *rverts = QFV_PacketExtend (rpacket, rend_size); + qfv_packet_t *epacket = QFV_PacketAcquire (stage); + uint16_t *elements = QFV_PacketExtend (epacket, elem_size); + //FIXME this whole thing is silly, but some person went and interleaved + //all the vertex data prematurely + for (int i = 0; i < iqm->num_verts; i++) { + byte *data = iqm->vertices + i * iqm->stride; + iqmgvert_t *gv = gverts + i; + iqmrvert_t *rv = rverts + i; + for (int j = 0; j < iqm->num_arrays; j++) { + __auto_type va = &iqm->vertexarrays[j]; + // FIXME assumes standard iqm sizes + size_t size = 0; + switch (va->type) { + case IQM_POSITION: + size = sizeof (gv->vertex); + memcpy (gv->vertex, data, size); + break; + case IQM_TEXCOORD: + size = sizeof (rv->uv); + memcpy (rv->uv, data, size); + break; + case IQM_NORMAL: + size = sizeof (rv->normal); + memcpy (rv->normal, data, size); + break; + case IQM_TANGENT: + size = sizeof (rv->tangent); + memcpy (rv->tangent, data, size); + break; + case IQM_BLENDINDEXES: + size = sizeof (gv->bones); + memcpy (gv->bones, data, size); + break; + case IQM_BLENDWEIGHTS: + size = sizeof (gv->weights); + memcpy (gv->weights, data, size); + break; + case IQM_COLOR: + size = sizeof (rv->color); + memcpy (rv->color, data, size); + break; + case IQM_CUSTOM: + // FIXME model loader doesn't handle these, so nothing to do + break; + } + data += size; + } + } + memcpy (elements, iqm->elements, elem_size); + + qfv_bufferbarrier_t bb[] = { + bufferBarriers[qfv_BB_Unknown_to_TransferWrite], + bufferBarriers[qfv_BB_Unknown_to_TransferWrite], + bufferBarriers[qfv_BB_Unknown_to_TransferWrite], + }; + bb[0].barrier.buffer = mesh->geom_buffer; + bb[0].barrier.size = geom_size; + bb[1].barrier.buffer = mesh->rend_buffer; + bb[1].barrier.size = rend_size; + bb[2].barrier.buffer = mesh->index_buffer; + bb[2].barrier.size = elem_size; + VkBufferCopy copy_region[] = { + { gpacket->offset, 0, geom_size }, + { rpacket->offset, 0, rend_size }, + { epacket->offset, 0, elem_size }, + }; + + dfunc->vkCmdPipelineBarrier (gpacket->cmd, bb[0].srcStages, bb[0].dstStages, + 0, 0, 0, 1, &bb[0].barrier, 0, 0); + dfunc->vkCmdPipelineBarrier (rpacket->cmd, bb[0].srcStages, bb[0].dstStages, + 0, 0, 0, 1, &bb[1].barrier, 0, 0); + dfunc->vkCmdPipelineBarrier (epacket->cmd, bb[0].srcStages, bb[0].dstStages, + 0, 0, 0, 1, &bb[2].barrier, 0, 0); + dfunc->vkCmdCopyBuffer (gpacket->cmd, stage->buffer, + mesh->geom_buffer, 1, ©_region[0]); + dfunc->vkCmdCopyBuffer (rpacket->cmd, stage->buffer, + mesh->rend_buffer, 1, ©_region[1]); + dfunc->vkCmdCopyBuffer (epacket->cmd, stage->buffer, + mesh->index_buffer, 1, ©_region[2]); + bb[0] = bufferBarriers[qfv_BB_TransferWrite_to_VertexAttrRead]; + bb[1] = bufferBarriers[qfv_BB_TransferWrite_to_VertexAttrRead]; + bb[2] = bufferBarriers[qfv_BB_TransferWrite_to_VertexAttrRead]; + bb[0].barrier.buffer = mesh->geom_buffer; + bb[0].barrier.size = geom_size; + bb[1].barrier.buffer = mesh->rend_buffer; + bb[1].barrier.size = rend_size; + bb[2].barrier.buffer = mesh->index_buffer; + bb[2].barrier.size = elem_size; + dfunc->vkCmdPipelineBarrier (gpacket->cmd, bb[0].srcStages, bb[0].dstStages, + 0, 0, 0, 1, &bb[0].barrier, 0, 0); + dfunc->vkCmdPipelineBarrier (rpacket->cmd, bb[0].srcStages, bb[0].dstStages, + 0, 0, 0, 1, &bb[1].barrier, 0, 0); + dfunc->vkCmdPipelineBarrier (epacket->cmd, bb[0].srcStages, bb[0].dstStages, + 0, 0, 0, 1, &bb[2].barrier, 0, 0); + QFV_PacketSubmit (gpacket); + QFV_PacketSubmit (rpacket); + QFV_PacketSubmit (epacket); + QFV_DestroyStagingBuffer (stage); + + vec4f_t *bone_data; + dfunc->vkMapMemory (device->dev, mesh->bones->memory, 0, VK_WHOLE_SIZE, + 0, (void **)&bone_data); + for (size_t i = 0; i < ictx->frames.size * iqm->num_joints; i++) { + vec4f_t *bone = bone_data + i * 3; + bone[0] = (vec4f_t) {1, 0, 0, 0}; + bone[1] = (vec4f_t) {0, 1, 0, 0}; + bone[2] = (vec4f_t) {0, 0, 1, 0}; + } + VkMappedMemoryRange range = { + VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0, + mesh->bones->memory, 0, VK_WHOLE_SIZE, + }; + dfunc->vkFlushMappedMemoryRanges (device->dev, 1, &range); + + dfunc->vkUnmapMemory (device->dev, mesh->bones->memory); + + Vulkan_IQMAddBones (ctx, iqm); //FIXME doesn't belong here (per-instance) +} + +void +Vulkan_Mod_IQMFinish (model_t *mod, vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + iqmctx_t *ictx = ctx->iqm_context; + iqm_t *iqm = (iqm_t *) mod->aliashdr; + mod->clear = vulkan_iqm_clear; + mod->data = ctx; + + // FIXME assumes only one texture per mesh (currently the case, but + // when materials are added...) + // 2 is for image + image view + int num_objects = 4 + 2 * iqm->num_meshes; + qfv_iqm_t *mesh = calloc (1, sizeof (qfv_iqm_t) + + ictx->frames.size * sizeof (VkDescriptorSet) + + 2 * sizeof (qfv_resource_t) + + num_objects * sizeof (qfv_resobj_t) + + iqm->num_meshes * sizeof (qfv_iqm_skin_t)); + mesh->bones_descriptors = (VkDescriptorSet *) &mesh[1]; + mesh->bones = (qfv_resource_t *)&mesh->bones_descriptors[ictx->frames.size]; + mesh->mesh = &mesh->bones[1]; + + mesh->bones[0] = (qfv_resource_t) { + .name = mod->name, + .va_ctx = ctx->va_ctx, + .memory_properties = VK_MEMORY_PROPERTY_HOST_CACHED_BIT, + .num_objects = 1, + .objects = (qfv_resobj_t *) &mesh->bones[2], + }; + mesh->bones->objects[0] = (qfv_resobj_t) { + .name = "bones", + .type = qfv_res_buffer, + .buffer = { + .size = ictx->frames.size * iqm->num_joints * 3 * sizeof (vec4f_t), + .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + }, + }; + + mesh->mesh[0] = (qfv_resource_t) { + .name = "mesh", + .va_ctx = ctx->va_ctx, + .memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + .num_objects = num_objects - 1, + .objects = mesh->bones->objects + 1, + }; + mesh->mesh->objects[0] = (qfv_resobj_t) { + .name = "geom", + .type = qfv_res_buffer, + .buffer = { + .size = iqm->num_verts * sizeof (iqmgvert_t), + .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + }, + }; + mesh->mesh->objects[1] = (qfv_resobj_t) { + .name = "rend", + .type = qfv_res_buffer, + .buffer = { + .size = iqm->num_verts * sizeof (iqmrvert_t), + .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + }, + }; + mesh->mesh->objects[2] = (qfv_resobj_t) { + .name = "index", + .type = qfv_res_buffer, + .buffer = { + .size = iqm->num_elements * sizeof (uint16_t), + .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + }, + }; + + for (int i = 0; i < iqm->num_meshes; i++) { + int image_ind = 3 + 2 * i; + __auto_type image = &mesh->mesh->objects[image_ind]; + vulkan_iqm_init_image (iqm, i, image); + + mesh->mesh->objects[image_ind + 1] = (qfv_resobj_t) { + .name = "view", + .type = qfv_res_image_view, + .image_view = { + .image = image_ind, + .type = VK_IMAGE_VIEW_TYPE_2D, + .format = mesh->mesh->objects[image_ind].image.format, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .levelCount = VK_REMAINING_MIP_LEVELS, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }, + }; + } + + mesh->skins = (qfv_iqm_skin_t *) &mesh->bones->objects[num_objects]; + + QFV_CreateResource (device, mesh->mesh); + QFV_CreateResource (device, mesh->bones); + mesh->geom_buffer = mesh->mesh->objects[0].buffer.buffer; + mesh->rend_buffer = mesh->mesh->objects[1].buffer.buffer; + mesh->index_buffer = mesh->mesh->objects[2].buffer.buffer; + mesh->bones_buffer = mesh->bones->objects[0].buffer.buffer; + + iqm->extra_data = mesh; + + vulkan_iqm_load_textures (mod, iqm, mesh, ctx); + vulkan_iqm_load_arrays (mod, iqm, mesh, ctx); +} diff --git a/libs/models/model.c b/libs/models/model.c index bac0223de..729aa624d 100644 --- a/libs/models/model.c +++ b/libs/models/model.c @@ -39,6 +39,7 @@ #endif #include "QF/cvar.h" +#include "QF/darray.h" #include "QF/iqm.h" #include "QF/model.h" #include "QF/qendian.h" @@ -48,26 +49,68 @@ #include "QF/plugin/vid_render.h" #include "compat.h" +#include "mod_internal.h" vid_model_funcs_t *mod_funcs; -model_t *loadmodel; -char *loadname; // for hunk tags - #define MOD_BLOCK 16 // allocate 16 models at a time -model_t **mod_known; -int mod_numknown; -int mod_maxknown; +static struct DARRAY_TYPE (model_t *) mod_known = {0, 0, MOD_BLOCK}; +static struct DARRAY_TYPE (model_t *) mod_blocks = {0, 0, MOD_BLOCK}; +static size_t mod_numknown; VISIBLE texture_t *r_notexture_mip; -cvar_t *gl_mesh_cache; -cvar_t *gl_subdivide_size; -cvar_t *gl_alias_render_tri; -cvar_t *gl_textures_external; +int gl_mesh_cache; +static cvar_t gl_mesh_cache_cvar = { + .name = "gl_mesh_cache", + .description = + "minimum triangle count in a model for its mesh to be cached. 0 to " + "disable caching", + .default_value = "256", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &gl_mesh_cache }, +}; +float gl_subdivide_size; +static cvar_t gl_subdivide_size_cvar = { + .name = "gl_subdivide_size", + .description = + "Sets the division value for the sky brushes.", + .default_value = "128", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &gl_subdivide_size }, +}; +int gl_alias_render_tri; +static cvar_t gl_alias_render_tri_cvar = { + .name = "gl_alias_render_tri", + .description = + "When loading alias models mesh for pure triangle rendering", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &gl_alias_render_tri }, +}; +int gl_textures_external; +static cvar_t gl_textures_external_cvar = { + .name = "gl_textures_external", + .description = + "Use external textures to replace BSP textures", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &gl_textures_external }, +}; static void Mod_CallbackLoad (void *object, cache_allocator_t allocator); +static void +mod_shutdown (void *data) +{ + Mod_ClearAll (); + for (size_t i = 0; i < mod_blocks.size; i++) { + free (mod_blocks.a[i]); + } + DARRAY_CLEAR (&mod_known); + DARRAY_CLEAR (&mod_blocks); +} + VISIBLE void Mod_Init (void) { @@ -75,8 +118,10 @@ Mod_Init (void) int m, x, y; int mip0size = 16*16, mip1size = 8*8, mip2size = 4*4, mip3size = 2*2; - memset (mod_novis, 0xff, sizeof (mod_novis)); - r_notexture_mip = Hunk_AllocName (sizeof (texture_t) + mip0size + mip1size + Sys_RegisterShutdown (mod_shutdown, 0); + + r_notexture_mip = Hunk_AllocName (0, + sizeof (texture_t) + mip0size + mip1size + mip2size + mip3size, "notexture"); r_notexture_mip->width = r_notexture_mip->height = 16; @@ -101,62 +146,66 @@ Mod_Init (void) VISIBLE void Mod_Init_Cvars (void) { - gl_subdivide_size = - Cvar_Get ("gl_subdivide_size", "128", CVAR_ARCHIVE, NULL, - "Sets the division value for the sky brushes."); - gl_mesh_cache = Cvar_Get ("gl_mesh_cache", "256", CVAR_ARCHIVE, NULL, - "minimum triangle count in a model for its mesh" - " to be cached. 0 to disable caching"); - gl_alias_render_tri = - Cvar_Get ("gl_alias_render_tri", "0", CVAR_ARCHIVE, NULL, "When " - "loading alias models mesh for pure triangle rendering"); - gl_textures_external = - Cvar_Get ("gl_textures_external", "1", CVAR_ARCHIVE, NULL, - "Use external textures to replace BSP textures"); + Cvar_Register (&gl_subdivide_size_cvar, 0, 0); + Cvar_Register (&gl_mesh_cache_cvar, 0, 0); + Cvar_Register (&gl_alias_render_tri_cvar, 0, 0); + Cvar_Register (&gl_textures_external_cvar, 0, 0); +} + +static void +mod_unload_model (size_t ind) +{ + model_t *mod = mod_known.a[ind]; + + //FIXME this seems to be correct but need to double check the behavior + //with alias models + if (!mod->needload && mod->clear) { + mod->clear (mod, mod->data); + } + if (mod->type != mod_alias) { + mod->needload = true; + } + if (mod->type == mod_sprite) { + mod->cache.data = 0; + } } VISIBLE void Mod_ClearAll (void) { - int i; - model_t **mod; + size_t i; - for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) { - if (!(*mod)->needload && (*mod)->clear) { - (*mod)->clear (*mod); - } else { - if ((*mod)->type != mod_alias) - (*mod)->needload = true; - if ((*mod)->type == mod_sprite) - (*mod)->cache.data = 0; - } + for (i = 0; i < mod_numknown; i++) { + mod_unload_model (i); } + mod_numknown = 0; } model_t * Mod_FindName (const char *name) { - int i; + size_t i; model_t **mod; if (!name[0]) Sys_Error ("Mod_FindName: empty name"); // search the currently loaded models - for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) - if (!strcmp ((*mod)->name, name)) + for (i = 0, mod = mod_known.a; i < mod_numknown; i++, mod++) + if (!strcmp ((*mod)->path, name)) break; if (i == mod_numknown) { - if (mod_numknown == mod_maxknown) { - mod_maxknown += MOD_BLOCK; - mod_known = realloc (mod_known, mod_maxknown * sizeof (model_t *)); - mod = mod_known + mod_numknown; - *mod = calloc (MOD_BLOCK, sizeof (model_t)); - for (i = 1; i < MOD_BLOCK; i++) - mod[i] = mod[0] + i; + if (mod_numknown == mod_known.size) { + model_t *block = calloc (MOD_BLOCK, sizeof (model_t)); + for (i = 0; i < MOD_BLOCK; i++) { + DARRAY_APPEND (&mod_known, &block[i]); + } + mod = &mod_known.a[mod_numknown]; + DARRAY_APPEND (&mod_blocks, block); } - strcpy ((*mod)->name, name); + memset ((*mod), 0, sizeof (model_t)); + strncpy ((*mod)->path, name, sizeof (*mod)->path - 1); (*mod)->needload = true; mod_numknown++; Cache_Add (&(*mod)->cache, *mod, Mod_CallbackLoad); @@ -166,22 +215,22 @@ Mod_FindName (const char *name) } static model_t * -Mod_RealLoadModel (model_t *mod, qboolean crash, cache_allocator_t allocator) +Mod_RealLoadModel (model_t *mod, bool crash, cache_allocator_t allocator) { uint32_t *buf; // load the file - buf = (uint32_t *) QFS_LoadFile (QFS_FOpenFile (mod->name), 0); + buf = (uint32_t *) QFS_LoadFile (QFS_FOpenFile (mod->path), 0); if (!buf) { if (crash) - Sys_Error ("Mod_LoadModel: %s not found", mod->name); + Sys_Error ("Mod_LoadModel: %s not found", mod->path); return NULL; } - if (loadname) - free (loadname); - loadname = QFS_FileBase (mod->name); - loadmodel = mod; + char *name = QFS_FileBase (mod->path); + strncpy (mod->name, name, sizeof (mod->name) - 1); + mod->name[sizeof (mod->name) - 1] = 0; + free (name); // fill it in mod->vpath = qfs_foundfile.vpath; @@ -202,17 +251,17 @@ Mod_RealLoadModel (model_t *mod, qboolean crash, cache_allocator_t allocator) break; case IDHEADER_MDL: // Type 6: Quake 1 .mdl case HEADER_MDL16: // QF Type 6 extended for 16bit precision - if (strequal (mod->name, "progs/grenade.mdl")) { + if (strequal (mod->path, "progs/grenade.mdl")) { mod->fullbright = 0; mod->shadow_alpha = 255; - } else if (strnequal (mod->name, "progs/flame", 11) - || strnequal (mod->name, "progs/bolt", 10)) { + } else if (strnequal (mod->path, "progs/flame", 11) + || strnequal (mod->path, "progs/bolt", 10)) { mod->fullbright = 1; mod->shadow_alpha = 0; } - if (strnequal (mod->name, "progs/v_", 8)) { + if (strnequal (mod->path, "progs/v_", 8)) { mod->min_light = 0.12; - } else if (strequal (mod->name, "progs/player.mdl")) { + } else if (strequal (mod->path, "progs/player.mdl")) { mod->min_light = 0.04; } if (mod_funcs) @@ -231,10 +280,6 @@ Mod_RealLoadModel (model_t *mod, qboolean crash, cache_allocator_t allocator) default: // Version 29: Quake 1 .bsp // Version 38: Quake 2 .bsp Mod_LoadBrushModel (mod, buf); - - if (gl_textures_external && gl_textures_external->int_val - && mod_funcs && mod_funcs->Mod_LoadExternalTextures) - mod_funcs->Mod_LoadExternalTextures (mod); break; } free (buf); @@ -248,7 +293,7 @@ Mod_RealLoadModel (model_t *mod, qboolean crash, cache_allocator_t allocator) Loads a model into the cache */ static model_t * -Mod_LoadModel (model_t *mod, qboolean crash) +Mod_LoadModel (model_t *mod, bool crash) { if (!mod->needload) { if (mod->type == mod_alias && !mod->aliashdr) { @@ -280,13 +325,13 @@ Mod_CallbackLoad (void *object, cache_allocator_t allocator) Loads in a model for the given name */ VISIBLE model_t * -Mod_ForName (const char *name, qboolean crash) +Mod_ForName (const char *name, bool crash) { model_t *mod; mod = Mod_FindName (name); - Sys_MaskPrintf (SYS_DEV, "Mod_ForName: %s, %p\n", name, mod); + Sys_MaskPrintf (SYS_dev, "Mod_ForName: %s, %p\n", name, mod); return Mod_LoadModel (mod, crash); } @@ -303,15 +348,25 @@ Mod_TouchModel (const char *name) } } +VISIBLE void +Mod_UnloadModel (model_t *model) +{ + for (size_t i = 0; i < mod_numknown; i++) { + if (mod_known.a[i] == model) { + mod_unload_model (i); + } + } +} + VISIBLE void Mod_Print (void) { - int i; + size_t i; model_t **mod; Sys_Printf ("Cached models:\n"); - for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) { - Sys_Printf ("%8p : %s\n", (*mod)->cache.data, (*mod)->name); + for (i = 0, mod = mod_known.a; i < mod_numknown; i++, mod++) { + Sys_Printf ("%8p : %s\n", (*mod)->cache.data, (*mod)->path); } } diff --git a/libs/models/null_model.c b/libs/models/null_model.c index 18cb987c1..6f482cc01 100644 --- a/libs/models/null_model.c +++ b/libs/models/null_model.c @@ -48,26 +48,11 @@ Mod_LoadSpriteModel (model_t *mod, void *buf) { } -void -Mod_ProcessTexture (texture_t *tx) -{ -} - void Mod_LoadExternalSkins (model_t *mod) { } -void -Mod_LoadExternalTextures (model_t *mod) -{ -} - -void -Mod_SubdivideSurface (msurface_t *fa) -{ -} - viddef_t vid; VISIBLE void diff --git a/libs/models/portal.c b/libs/models/portal.c index ca09be139..2745277b9 100644 --- a/libs/models/portal.c +++ b/libs/models/portal.c @@ -111,7 +111,7 @@ add_portal (clipport_t *portal, clipleaf_t *front, clipleaf_t *back) static clipleaf_t * carve_leaf (hull_t *hull, nodeleaf_t *nodeleafs, clipleaf_t *leaf, int num) { - mclipnode_t *node; + dclipnode_t *node; plane_t *plane; winding_t *winding, *fw, *bw; clipport_t *portal; diff --git a/libs/models/skin.c b/libs/models/skin.c index 14c21cd81..93be64571 100644 --- a/libs/models/skin.c +++ b/libs/models/skin.c @@ -71,6 +71,14 @@ new_skin (void) return calloc (1, sizeof (skin_t)); } +VISIBLE void +Skin_Free (skin_t *skin) +{ + if (skin) { + free (skin); + } +} + VISIBLE void Skin_SetTranslation (int cmap, int top, int bottom) { @@ -84,7 +92,7 @@ Skin_SetTranslation (int cmap, int top, int bottom) bottom = bound (0, bottom, 13) * 16; if (cmap < 0 || cmap > MAX_TRANSLATIONS) { - Sys_MaskPrintf (SYS_SKIN, "invalid skin slot: %d\n", cmap); + Sys_MaskPrintf (SYS_skin, "invalid skin slot: %d\n", cmap); cmap = 1; } @@ -129,7 +137,7 @@ Skin_SetColormap (skin_t *skin, int cmap) skin = new_skin (); skin->colormap = 0; if (cmap < 0 || cmap > MAX_TRANSLATIONS) { - Sys_MaskPrintf (SYS_SKIN, "invalid skin slot: %d\n", cmap); + Sys_MaskPrintf (SYS_skin, "invalid skin slot: %d\n", cmap); cmap = 0; } if (cmap) @@ -177,14 +185,14 @@ Skin_SetSkin (skin_t *skin, int cmap, const char *skinname) break; } - file = QFS_FOpenFile (va ("skins/%s.pcx", name)); + file = QFS_FOpenFile (va (0, "skins/%s.pcx", name)); if (!file) { Sys_Printf ("Couldn't load skin %s\n", name); free (name); name = 0; break; } - tex = LoadPCX (file, 0, r_data->vid->palette); + tex = LoadPCX (file, 0, r_data->vid->palette, 1); Qclose (file); if (!tex || tex->width > 320 || tex->height > 200) { Sys_Printf ("Bad skin %s\n", name); @@ -193,7 +201,8 @@ Skin_SetSkin (skin_t *skin, int cmap, const char *skinname) tex = 0; break; } - out = malloc (field_offset (tex_t, data[PLAYER_WIDTH*PLAYER_HEIGHT])); + out = malloc (sizeof (tex_t) + PLAYER_WIDTH*PLAYER_HEIGHT); + out->data = (byte *) (out + 1); out->width = PLAYER_WIDTH; out->height = PLAYER_HEIGHT; out->format = tex_palette; @@ -242,6 +251,112 @@ skin_free (void *_sb, void *unused) void Skin_Init (void) { - skin_cache = Hash_NewTable (127, skin_getkey, skin_free, 0); + skin_cache = Hash_NewTable (127, skin_getkey, skin_free, 0, 0); m_funcs->Skin_InitTranslations (); } + +void +Skin_Shutdown (void) +{ + Hash_DelTable (skin_cache); +} + +VISIBLE int +Skin_CalcTopColors (byte *out, const byte *in, size_t pixels, int stride) +{ + byte tc = 0; + + while (pixels-- > 0) { + byte pix = *in++; + if (pix >= TOP_RANGE && pix < TOP_RANGE + 16) { + tc = 1; + *out = (pix - TOP_RANGE) * 16 + 8; + } else { + *out = 0; + } + out += stride; + } + return tc; +} + +VISIBLE int +Skin_CalcTopMask (byte *out, const byte *in, size_t pixels, int stride) +{ + byte tc = 0; + + while (pixels-- > 0) { + byte pix = *in++; + if (pix >= TOP_RANGE && pix < TOP_RANGE + 16) { + tc = 1; + *out = 0xff; + } else { + *out = 0; + } + out += stride; + } + return tc; +} + +VISIBLE int +Skin_CalcBottomColors (byte *out, const byte *in, size_t pixels, int stride) +{ + byte bc = 0; + + while (pixels-- > 0) { + byte pix = *in++; + if (pix >= BOTTOM_RANGE && pix < BOTTOM_RANGE + 16) { + bc = 1; + *out = (pix - BOTTOM_RANGE) * 16 + 8; + } else { + *out = 0; + } + out += stride; + } + return bc; +} + +VISIBLE int +Skin_CalcBottomMask (byte *out, const byte *in, size_t pixels, int stride) +{ + byte bc = 0; + + while (pixels-- > 0) { + byte pix = *in++; + if (pix >= BOTTOM_RANGE && pix < BOTTOM_RANGE + 16) { + bc = 1; + *out = 0xff; + } else { + *out = 0; + } + out += stride; + } + return bc; +} + +VISIBLE int +Skin_ClearTopColors (byte *out, const byte *in, size_t pixels) +{ + while (pixels-- > 0) { + byte pix = *in++; + if (pix >= TOP_RANGE && pix < TOP_RANGE + 16) { + *out++ = 0; + } else { + *out++ = pix; + } + } + return 0; +} + +VISIBLE int +Skin_ClearBottomColors (byte *out, const byte *in, size_t pixels) +{ + while (pixels-- > 0) { + byte pix = *in++; + if (pix >= BOTTOM_RANGE && pix < BOTTOM_RANGE + 16) { + *out++ = 0; + } else { + *out++ = pix; + } + } + return 0; +} diff --git a/libs/models/sprite/Makefile.am b/libs/models/sprite/Makefile.am deleted file mode 100644 index 3c6c023d1..000000000 --- a/libs/models/sprite/Makefile.am +++ /dev/null @@ -1,20 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CFLAGS= @PREFER_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include - -noinst_LTLIBRARIES= @sprite_libs@ -EXTRA_LTLIBRARIES=libsprite_gl.la libsprite_glsl.la libsprite_sw.la - -sprite_src= model_sprite.c -gl_src= gl_model_sprite.c -glsl_src= glsl_model_sprite.c -sw_src= sw_model_sprite.c - -libsprite_gl_la_SOURCES= $(gl_src) $(sprite_src) - -libsprite_glsl_la_SOURCES= $(glsl_src) $(sprite_src) - -libsprite_sw_la_SOURCES= $(sw_src) $(sprite_src) - -EXTRA_DIST= $(gl_src) $(glsl_src) $(sw_src) $(sprite_src) diff --git a/libs/models/sprite/Makemodule.am b/libs/models/sprite/Makemodule.am new file mode 100644 index 000000000..2021eb370 --- /dev/null +++ b/libs/models/sprite/Makemodule.am @@ -0,0 +1,27 @@ +noinst_LTLIBRARIES += @sprite_libs@ +EXTRA_LTLIBRARIES += \ + libs/models/sprite/libsprite_gl.la \ + libs/models/sprite/libsprite_glsl.la \ + libs/models/sprite/libsprite_sw.la \ + libs/models/sprite/libsprite_vulkan.la + +sprite_src= libs/models/sprite/model_sprite.c +sprite_gl_src= libs/models/sprite/gl_model_sprite.c +sprite_glsl_src= libs/models/sprite/glsl_model_sprite.c +sprite_sw_src= libs/models/sprite/sw_model_sprite.c +sprite_vulkan_src= libs/models/sprite/vulkan_model_sprite.c + +libs_models_sprite_libsprite_gl_la_SOURCES= $(sprite_gl_src) $(sprite_src) + +libs_models_sprite_libsprite_glsl_la_SOURCES= $(sprite_glsl_src) $(sprite_src) + +libs_models_sprite_libsprite_sw_la_SOURCES= $(sprite_sw_src) $(sprite_src) + +libs_models_sprite_libsprite_vulkan_la_SOURCES= $(sprite_vulkan_src) $(sprite_src) + +EXTRA_DIST += \ + $(sprite_gl_src) \ + $(sprite_glsl_src) \ + $(sprite_sw_src) \ + $(sprite_vulkan_src) \ + $(sprite_src) diff --git a/libs/models/sprite/gl_model_sprite.c b/libs/models/sprite/gl_model_sprite.c index 647b5f687..546e97e96 100644 --- a/libs/models/sprite/gl_model_sprite.c +++ b/libs/models/sprite/gl_model_sprite.c @@ -43,25 +43,36 @@ #include "compat.h" #include "mod_internal.h" -void -gl_Mod_SpriteLoadTexture (mspriteframe_t *pspriteframe, int framenum) +static int +load_texture (model_t *mod, int framenum, const dspriteframe_t *dframe) { tex_t *targa; const char *name; - targa = LoadImage (name = va ("%s_%i", loadmodel->name, framenum)); + targa = LoadImage (name = va (0, "%s_%i", mod->path, framenum), 1); if (targa) { - if (targa->format < 4) - pspriteframe->gl_texturenum = GL_LoadTexture (name, - targa->width, targa->height, targa->data, - true, false, 3); - else - pspriteframe->gl_texturenum = GL_LoadTexture (name, - targa->width, targa->height, targa->data, - true, true, 4); - return; + if (targa->format < 4) { + return GL_LoadTexture (name, targa->width, targa->height, + targa->data, true, false, 3); + } else { + return GL_LoadTexture (name, targa->width, targa->height, + targa->data, true, true, 4); + } + } + return GL_LoadTexture (name, dframe->width, dframe->height, + (const byte *)(dframe + 1), true, true, 1); +} + +void +gl_Mod_SpriteLoadFrames (mod_sprite_ctx_t *ctx) +{ + for (int i = 0; i < ctx->numframes; i++) { + __auto_type dframe = ctx->dframes[i]; + size_t size = sizeof (mspriteframe_t); + mspriteframe_t *frame = Hunk_AllocName (0, size, ctx->mod->name); + *ctx->frames[i] = frame; + Mod_LoadSpriteFrame (frame, dframe); + frame->gl_texturenum = load_texture (ctx->mod, ctx->frame_numbers[i], + dframe); } - pspriteframe->gl_texturenum = - GL_LoadTexture (name, pspriteframe->width, pspriteframe->height, - pspriteframe->pixels, true, true, 1); } diff --git a/libs/models/sprite/glsl_model_sprite.c b/libs/models/sprite/glsl_model_sprite.c index 66e1318e2..c6f267d57 100644 --- a/libs/models/sprite/glsl_model_sprite.c +++ b/libs/models/sprite/glsl_model_sprite.c @@ -47,7 +47,7 @@ #include "mod_internal.h" static void -glsl_sprite_clear (model_t *m) +glsl_sprite_clear (model_t *m, void *data) { int i, j; msprite_t *sprite = (msprite_t *) m->cache.data; @@ -58,10 +58,10 @@ glsl_sprite_clear (model_t *m) m->cache.data = 0; for (i = 0; i < sprite->numframes; i++) { if (sprite->frames[i].type == SPR_SINGLE) { - frame = sprite->frames[i].frameptr; + frame = sprite->frames[i].frame; GLSL_ReleaseTexture (frame->gl_texturenum); } else { - group = (mspritegroup_t *) sprite->frames[i].frameptr; + group = sprite->frames[i].group; for (j = 0; j < group->numframes; j++) { frame = group->frames[j]; GLSL_ReleaseTexture (frame->gl_texturenum); @@ -71,13 +71,19 @@ glsl_sprite_clear (model_t *m) } void -glsl_Mod_SpriteLoadTexture (mspriteframe_t *pspriteframe, int framenum) +glsl_Mod_SpriteLoadFrames (mod_sprite_ctx_t *ctx) { - const char *name; - - loadmodel->clear = glsl_sprite_clear; - name = va ("%s_%i", loadmodel->name, framenum); - pspriteframe->gl_texturenum = - GLSL_LoadQuakeTexture (name, pspriteframe->width, pspriteframe->height, - pspriteframe->pixels); + ctx->mod->clear = glsl_sprite_clear; + for (int i = 0; i < ctx->numframes; i++) { + __auto_type dframe = ctx->dframes[i]; + size_t size = sizeof (mspriteframe_t); + mspriteframe_t *frame = Hunk_AllocName (0, size, ctx->mod->name); + *ctx->frames[i] = frame; + Mod_LoadSpriteFrame (frame, dframe); + const char *name = va (0, "%s_%i", ctx->mod->path, + ctx->frame_numbers[i]); + frame->gl_texturenum = + GLSL_LoadQuakeTexture (name, dframe->width, dframe->height, + (const byte *)(dframe + 1)); + } } diff --git a/libs/models/sprite/model_sprite.c b/libs/models/sprite/model_sprite.c index ce962fec4..1bd9ca2cc 100644 --- a/libs/models/sprite/model_sprite.c +++ b/libs/models/sprite/model_sprite.c @@ -38,151 +38,187 @@ #include "QF/qendian.h" #include "QF/sys.h" -#include "compat.h" #include "mod_internal.h" +#include "qfalloca.h" -static void * -Mod_LoadSpriteFrame (void *pin, mspriteframe_t **ppframe, int framenum) +void +Mod_LoadSpriteFrame (mspriteframe_t *frame, const dspriteframe_t *dframe) { - dspriteframe_t *pinframe; - int width, height, size, origin[2]; - mspriteframe_t *pspriteframe; + frame->width = dframe->width; + frame->height = dframe->height; - pinframe = (dspriteframe_t *) pin; - - width = LittleLong (pinframe->width); - height = LittleLong (pinframe->height); - size = width * height; - - pspriteframe = Hunk_AllocName (sizeof (mspriteframe_t) + size, loadname); - - memset (pspriteframe, 0, sizeof (mspriteframe_t) + size); - - *ppframe = pspriteframe; - - pspriteframe->width = width; - pspriteframe->height = height; - origin[0] = LittleLong (pinframe->origin[0]); - origin[1] = LittleLong (pinframe->origin[1]); - - pspriteframe->up = origin[1]; - pspriteframe->down = origin[1] - height; - pspriteframe->left = origin[0]; - pspriteframe->right = width + origin[0]; - - memcpy (pspriteframe->pixels, (byte *) (pinframe + 1), size); - - m_funcs->Mod_SpriteLoadTexture (pspriteframe, framenum); - - return (void *) ((byte *) pinframe + sizeof (dspriteframe_t) + size); + frame->up = dframe->origin[1]; + frame->down = dframe->origin[1] - dframe->height; + frame->left = dframe->origin[0]; + frame->right = dframe->width + dframe->origin[0]; } static void * -Mod_LoadSpriteGroup (void *pin, mspriteframe_t **ppframe, int framenum) +skip_frame (dspriteframe_t *frame) { - dspritegroup_t *pingroup; - dspriteinterval_t *pin_intervals; - float *poutintervals; - int numframes, i; - mspritegroup_t *pspritegroup; - void *ptemp; + __auto_type pixels = (byte *) (frame + 1); + return pixels + frame->width * frame->height; +} - pingroup = (dspritegroup_t *) pin; - - numframes = LittleLong (pingroup->numframes); - - pspritegroup = Hunk_AllocName (field_offset (mspritegroup_t, - frames[numframes]), loadname); - - pspritegroup->numframes = numframes; - - *ppframe = (mspriteframe_t *) pspritegroup; - - pin_intervals = (dspriteinterval_t *) (pingroup + 1); - - poutintervals = Hunk_AllocName (numframes * sizeof (float), loadname); - - pspritegroup->intervals = poutintervals; - - for (i = 0; i < numframes; i++) { - *poutintervals = LittleFloat (pin_intervals->interval); - if (*poutintervals <= 0.0) - Sys_Error ("Mod_LoadSpriteGroup: interval<=0"); - - poutintervals++; - pin_intervals++; +static void * +swap_frame (dspriteframe_t *frame) +{ + for (int i = 0; i < 2; i++) { + frame->origin[i] = LittleLong (frame->origin[i]); } + frame->width = LittleLong (frame->width); + frame->height = LittleLong (frame->height); + return skip_frame (frame); +} - ptemp = (void *) pin_intervals; - - for (i = 0; i < numframes; i++) { - ptemp = - Mod_LoadSpriteFrame (ptemp, &pspritegroup->frames[i], - framenum * 100 + i); +static void * +swap_group (dspritegroup_t *group) +{ + group->numframes = LittleLong (group->numframes); + __auto_type interval = (dspriteinterval_t *) (group + 1); + for (int i = 0; i < group->numframes; i++) { + interval->interval = LittleFloat (interval->interval); + interval++; } + __auto_type frame = (dspriteframe_t *) interval; + for (int i = 0; i < group->numframes; i++) { + frame = swap_frame (frame); + } + return frame + 1; +} - return ptemp; +static int +swap_sprite (dsprite_t *sprite) +{ + sprite->ident = LittleLong (sprite->ident); + sprite->version = LittleLong (sprite->version); + sprite->type = LittleLong (sprite->type); + sprite->boundingradius = LittleFloat (sprite->boundingradius); + sprite->width = LittleLong (sprite->width); + sprite->height = LittleLong (sprite->height); + sprite->numframes = LittleLong (sprite->numframes); + sprite->beamlength = LittleFloat (sprite->beamlength); + sprite->synctype = LittleLong (sprite->synctype); + + int numframes = 0; + __auto_type type = (dspriteframetype_t *) (sprite + 1); + for (int i = 0; i < sprite->numframes; i++) { + type->type = LittleLong (type->type); + if (type->type == SPR_SINGLE) { + __auto_type frame = (dspriteframe_t *) (type + 1); + type = swap_frame (frame); + numframes += 1; + } else { + __auto_type group = (dspritegroup_t *) (type + 1); + type = swap_group (group); + numframes += group->numframes; + } + } + return numframes; +} + +static void * +find_group_frames (mspritegroup_t **group, dspritegroup_t *dgroup, + mspriteframe_t ***frames, dspriteframe_t **dframes, + int *frame_numbers, const char *modname) +{ + int numframes = dgroup->numframes; + size_t size = field_offset (mspritegroup_t, frames[numframes]); + *group = Hunk_AllocName (0, size, modname); + (*group)->numframes = numframes; + (*group)->intervals = Hunk_AllocName (0, numframes * sizeof (float), + modname); + + __auto_type interval = (dspriteinterval_t *) (dgroup + 1); + for (int i = 0; i < numframes; i++) { + (*group)->intervals[i] = interval->interval; + interval++; + } + __auto_type dframe = (dspriteframe_t *) interval; + for (int i = 0; i < numframes; i++) { + frames[i] = &(*group)->frames[i]; + dframes[i] = dframe; + frame_numbers[i] = i; + dframe = skip_frame (dframe); + } + return dframe; +} + +static void +find_frames (msprite_t *sprite, dsprite_t *dsprite, + mspriteframe_t ***frames, dspriteframe_t **dframes, + int *frame_numbers, const char *modname) +{ + int frame_index = 0; + __auto_type type = (dspriteframetype_t *) (dsprite + 1); + for (int i = 0; i < dsprite->numframes; i++) { + sprite->frames[i].type = type->type; + if (type->type == SPR_SINGLE) { + __auto_type frame = (dspriteframe_t *) (type + 1); + dframes[frame_index] = frame; + frames[frame_index] = &sprite->frames[i].frame; + frame_numbers[frame_index] = i; + frame_index += 1; + type = skip_frame (frame); + } else { + __auto_type group = (dspritegroup_t *) (type + 1); + type = find_group_frames (&sprite->frames[i].group, group, + frames + frame_index, + dframes + frame_index, + frame_numbers + frame_index, + modname); + for (int j = 0; j < group->numframes; j++) { + frame_numbers[frame_index + j] += i * 100; + } + frame_index += group->numframes; + } + } } void Mod_LoadSpriteModel (model_t *mod, void *buffer) { - dsprite_t *pin; - dspriteframetype_t *pframetype; - int numframes, size, version, i; - msprite_t *psprite; + __auto_type dsprite = (dsprite_t *) buffer; + msprite_t *sprite; - pin = (dsprite_t *) buffer; - - version = LittleLong (pin->version); - if (version != SPR_VERSION) - Sys_Error ("%s has wrong version number " - "(%i should be %i)", mod->name, version, SPR_VERSION); - - numframes = LittleLong (pin->numframes); - - size = field_offset (msprite_t, frames[numframes]); - - psprite = Hunk_AllocName (size, loadname); - - mod->cache.data = psprite; - - psprite->type = LittleLong (pin->type); - psprite->maxwidth = LittleLong (pin->width); - psprite->maxheight = LittleLong (pin->height); - psprite->beamlength = LittleFloat (pin->beamlength); - mod->synctype = LittleLong (pin->synctype); - psprite->numframes = numframes; - - mod->mins[0] = mod->mins[1] = -psprite->maxwidth / 2; - mod->maxs[0] = mod->maxs[1] = psprite->maxwidth / 2; - mod->mins[2] = -psprite->maxheight / 2; - mod->maxs[2] = psprite->maxheight / 2; - - // load the frames - if (numframes < 1) - Sys_Error ("Mod_LoadSpriteModel: Invalid # of frames: %d", numframes); - - mod->numframes = numframes; - - pframetype = (dspriteframetype_t *) (pin + 1); - - for (i = 0; i < numframes; i++) { - spriteframetype_t frametype; - - frametype = LittleLong (pframetype->type); - psprite->frames[i].type = frametype; - - if (frametype == SPR_SINGLE) { - pframetype = (dspriteframetype_t *) - Mod_LoadSpriteFrame (pframetype + 1, - &psprite->frames[i].frameptr, i); - } else { - pframetype = (dspriteframetype_t *) - Mod_LoadSpriteGroup (pframetype + 1, - &psprite->frames[i].frameptr, i); - } + if (LittleLong (dsprite->version) != SPR_VERSION) { + Sys_Error ("%s has wrong version number (%i should be %i)", + mod->path, LittleLong (dsprite->version), SPR_VERSION); } + // total number of frames (direct and in groups) + int numframes = swap_sprite (dsprite); + + if (numframes < 1) { + Sys_Error ("Mod_LoadSpriteModel: Invalid # of frames: %d", numframes); + } + + sprite = Hunk_AllocName (0, field_offset (msprite_t, + frames[dsprite->numframes]), + mod->name); + sprite->type = dsprite->type; + sprite->beamlength = dsprite->beamlength; + sprite->numframes = dsprite->numframes; + sprite->data = 0; + + mod->cache.data = sprite; + mod->mins[0] = mod->mins[1] = -dsprite->width / 2; + mod->maxs[0] = mod->maxs[1] = dsprite->width / 2; + mod->mins[2] = -dsprite->height / 2; + mod->maxs[2] = dsprite->height / 2; + mod->numframes = dsprite->numframes; mod->type = mod_sprite; + + mod_sprite_ctx_t sprite_ctx = { + .mod = mod, + .dsprite = dsprite, + .sprite = sprite, + .numframes = numframes, + .frame_numbers = alloca (numframes * sizeof (int)), + .dframes = alloca (numframes * sizeof (dspriteframe_t *)), + .frames = alloca (numframes * sizeof (mspriteframe_t **)), + }; + find_frames (sprite, dsprite, sprite_ctx.frames, sprite_ctx.dframes, + sprite_ctx.frame_numbers, mod->name); + m_funcs->Mod_SpriteLoadFrames (&sprite_ctx); } diff --git a/libs/models/sprite/sw_model_sprite.c b/libs/models/sprite/sw_model_sprite.c index 9d3f25bd2..6834413e9 100644 --- a/libs/models/sprite/sw_model_sprite.c +++ b/libs/models/sprite/sw_model_sprite.c @@ -28,9 +28,24 @@ # include "config.h" #endif +#ifdef HAVE_STRING_H +# include +#endif + +#include "QF/zone.h" + #include "mod_internal.h" void -sw_Mod_SpriteLoadTexture (mspriteframe_t *pspriteframe, int framenum) +sw_Mod_SpriteLoadFrames (mod_sprite_ctx_t *ctx) { + for (int i = 0; i < ctx->numframes; i++) { + __auto_type dframe = ctx->dframes[i]; + size_t pixels = dframe->width * dframe->height; + size_t size = field_offset (mspriteframe_t, pixels[pixels]); + mspriteframe_t *frame = Hunk_AllocName (0, size, ctx->mod->name); + *ctx->frames[i] = frame; + Mod_LoadSpriteFrame (frame, dframe); + memcpy (frame->pixels, dframe + 1, pixels); + } } diff --git a/libs/models/sprite/vulkan_model_sprite.c b/libs/models/sprite/vulkan_model_sprite.c new file mode 100644 index 000000000..865d349ea --- /dev/null +++ b/libs/models/sprite/vulkan_model_sprite.c @@ -0,0 +1,206 @@ +/* + vulkan_model_sprite.c + + Sprite model mesh processing for Vulkan + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/12/13 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/cvar.h" +#include "QF/darray.h" +#include "QF/image.h" +#include "QF/quakefs.h" +#include "QF/va.h" +#include "QF/Vulkan/qf_sprite.h" +#include "QF/Vulkan/qf_texture.h" +#include "QF/Vulkan/barrier.h" +#include "QF/Vulkan/buffer.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/image.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/staging.h" + +#include "compat.h" +#include "mod_internal.h" +#include "r_internal.h" +#include "vid_vulkan.h" + +static void +vulkan_sprite_clear (model_t *m, void *data) +{ + vulkan_ctx_t *ctx = data; + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + msprite_t *msprite = m->cache.data; + __auto_type sprite = (qfv_sprite_t *) ((byte *) msprite + msprite->data); + + Vulkan_Sprite_FreeDescriptors (ctx, sprite); + + dfunc->vkDestroyBuffer (device->dev, sprite->verts, 0); + dfunc->vkDestroyImageView (device->dev, sprite->view, 0); + dfunc->vkDestroyImage (device->dev, sprite->image, 0); + dfunc->vkFreeMemory (device->dev, sprite->memory, 0); +} + +void +Vulkan_Mod_SpriteLoadFrames (mod_sprite_ctx_t *sprite_ctx, vulkan_ctx_t *ctx) +{ + qfvPushDebug (ctx, va (ctx->va_ctx, "sprite.load_frames: %s", + sprite_ctx->mod->name)); + + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + model_t *mod = sprite_ctx->mod; + dsprite_t *dsprite = sprite_ctx->dsprite; + mod->clear = vulkan_sprite_clear; + mod->data = ctx; + + qfv_sprite_t *sprite = Hunk_AllocName (0, sizeof (*sprite), mod->name); + int mipLevels = QFV_MipLevels (dsprite->width, dsprite->height); + VkExtent3D extent = { dsprite->width, dsprite->height, 1 }; + sprite->image = QFV_CreateImage (device, 0, VK_IMAGE_TYPE_2D, + VK_FORMAT_R8G8B8A8_UNORM, extent, + mipLevels, sprite_ctx->numframes, + VK_SAMPLE_COUNT_1_BIT, + VK_IMAGE_USAGE_SAMPLED_BIT + | VK_IMAGE_USAGE_TRANSFER_DST_BIT + | VK_IMAGE_USAGE_TRANSFER_SRC_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE, sprite->image, + va (ctx->va_ctx, "image:%s", mod->name)); + + int numverts = 4 * sprite_ctx->numframes; + sprite->verts = QFV_CreateBuffer (device, numverts * sizeof (spritevrt_t), + VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER, sprite->verts, + va (ctx->va_ctx, "buffer:sprite:vertex:%s", + mod->name)); + + VkMemoryRequirements ireq; + dfunc->vkGetImageMemoryRequirements (device->dev, sprite->image, &ireq); + VkMemoryRequirements vreq; + dfunc->vkGetBufferMemoryRequirements (device->dev, sprite->verts, &vreq); + size_t size = QFV_NextOffset (vreq.size, &ireq) + ireq.size; + + sprite->memory = QFV_AllocBufferMemory (device, sprite->verts, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + size, 0); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, sprite->memory, + va (ctx->va_ctx, "memory:sprite:%s", + sprite_ctx->mod->name)); + + QFV_BindBufferMemory (device, sprite->verts, sprite->memory, 0); + QFV_BindImageMemory (device, sprite->image, sprite->memory, + QFV_NextOffset (vreq.size, &ireq)); + sprite->view = QFV_CreateImageView (device, sprite->image, + VK_IMAGE_VIEW_TYPE_2D_ARRAY, + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_ASPECT_COLOR_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE_VIEW, sprite->view, + va (ctx->va_ctx, "view:sprite:%s", + sprite_ctx->mod->name)); + + qfv_stagebuf_t *stage = QFV_CreateStagingBuffer (device, + va (ctx->va_ctx, + "sprite:%s", + sprite_ctx->mod->name), + size, ctx->cmdpool); + qfv_packet_t *packet = QFV_PacketAcquire (stage); + spritevrt_t *verts = QFV_PacketExtend (packet, + numverts * sizeof (spritevrt_t)); + int texsize = 4 * dsprite->width * dsprite->height; + byte *pixels = QFV_PacketExtend (packet, + sprite_ctx->numframes * texsize); + + for (int i = 0; i < sprite_ctx->numframes; i++) { + __auto_type dframe = sprite_ctx->dframes[i]; + mspriteframe_t f; + Mod_LoadSpriteFrame (&f, dframe); + verts[i * 4 + 0] = (spritevrt_t) { f.left, f.up, 0, 0 }; + verts[i * 4 + 1] = (spritevrt_t) { f.right, f.up, 1, 0 }; + verts[i * 4 + 2] = (spritevrt_t) { f.left, f.down, 0, 1 }; + verts[i * 4 + 3] = (spritevrt_t) { f.right, f.down, 1, 1 }; + Vulkan_ExpandPalette (pixels + i * texsize, (const byte *)(dframe + 1), + vid.palette32, 2, texsize / 4); + *sprite_ctx->frames[i] = (mspriteframe_t *) (ptrdiff_t) i; + } + + qfv_bufferbarrier_t bb = bufferBarriers[qfv_BB_Unknown_to_TransferWrite]; + bb.barrier.buffer = sprite->verts; + bb.barrier.size = numverts * sizeof (spritevrt_t); + dfunc->vkCmdPipelineBarrier (packet->cmd, bb.srcStages, bb.dstStages, + 0, 0, 0, 1, &bb.barrier, 0, 0); + VkBufferCopy copy_region[] = { + { packet->offset, 0, numverts * sizeof (spritevrt_t) }, + }; + dfunc->vkCmdCopyBuffer (packet->cmd, stage->buffer, + sprite->verts, 1, ©_region[0]); + bb = bufferBarriers[qfv_BB_TransferWrite_to_VertexAttrRead]; + bb.barrier.buffer = sprite->verts; + bb.barrier.size = numverts * sizeof (spritevrt_t); + dfunc->vkCmdPipelineBarrier (packet->cmd, bb.srcStages, bb.dstStages, + 0, 0, 0, 1, &bb.barrier, 0, 0); + + qfv_imagebarrier_t ib = imageBarriers[qfv_LT_Undefined_to_TransferDst]; + ib.barrier.image = sprite->image; + ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); + + VkBufferImageCopy copy = { + packet->offset + numverts * sizeof (spritevrt_t), 0, 0, + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, sprite_ctx->numframes}, + {0, 0, 0}, {dsprite->width, dsprite->height, 1}, + }; + dfunc->vkCmdCopyBufferToImage (packet->cmd, packet->stage->buffer, + sprite->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, ©); + QFV_GenerateMipMaps (device, packet->cmd, sprite->image, + mipLevels, dsprite->width, dsprite->height, + sprite_ctx->numframes); + + QFV_PacketSubmit (packet); + QFV_DestroyStagingBuffer (stage); + + Vulkan_Sprite_DescriptorSet (ctx, sprite); + + sprite_ctx->sprite->data = (byte *) sprite - (byte *) sprite_ctx->sprite; + + qfvPopDebug (ctx); +} diff --git a/libs/models/test/Makefile.am b/libs/models/test/Makefile.am deleted file mode 100644 index 2718f5ddd..000000000 --- a/libs/models/test/Makefile.am +++ /dev/null @@ -1,24 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CPPFLAGS= -I$(srcdir) -I$(top_srcdir)/include - -check_PROGRAMS=testclip testcontents testportals -EXTRA_DIST= trace-id.c trace-qf-bad.c hulls.h main.c - -test_libs= \ - $(top_builddir)/libs/models/libQFmodels.la \ - $(top_builddir)/libs/util/libQFutil.la - -testclip_SOURCES= testclip.c hulls.c -testclip_LDADD= $(test_libs) -testclip_DEPENDENCIES= $(test_libs) - -testcontents_SOURCES= testcontents.c hulls.c -testcontents_LDADD= $(test_libs) -testcontents_DEPENDENCIES= $(test_libs) - -testportals_SOURCES= testportals.c hulls.c -testportals_LDADD= $(test_libs) -testportals_DEPENDENCIES= $(test_libs) - -TESTS=$(check_PROGRAMS) diff --git a/libs/models/test/Makemodule.am b/libs/models/test/Makemodule.am new file mode 100644 index 000000000..15cbe5e35 --- /dev/null +++ b/libs/models/test/Makemodule.am @@ -0,0 +1,36 @@ +libs_model_tests = \ + libs/models/test/testclip \ + libs/models/test/testcontents \ + libs/models/test/testportals + +TESTS += $(libs_model_tests) + +check_PROGRAMS += $(libs_model_tests) + +EXTRA_DIST += \ + libs/models/test/trace-id.c \ + libs/models/test/trace-qf-bad.c \ + libs/models/test/hulls.h \ + libs/models/test/main.c + +test_libs= \ + libs/models/libQFmodels.la \ + libs/util/libQFutil.la + +libs_models_test_testclip_SOURCES= \ + libs/models/test/testclip.c \ + libs/models/test/hulls.c +libs_models_test_testclip_LDADD= $(test_libs) +libs_models_test_testclip_DEPENDENCIES= $(test_libs) + +libs_models_test_testcontents_SOURCES= \ + libs/models/test/testcontents.c \ + libs/models/test/hulls.c +libs_models_test_testcontents_LDADD= $(test_libs) +libs_models_test_testcontents_DEPENDENCIES= $(test_libs) + +libs_models_test_testportals_SOURCES= \ + libs/models/test/testportals.c \ + libs/models/test/hulls.c +libs_models_test_testportals_LDADD= $(test_libs) +libs_models_test_testportals_DEPENDENCIES= $(test_libs) diff --git a/libs/models/test/hulls.c b/libs/models/test/hulls.c index 1b7fa39ba..02e2db43d 100644 --- a/libs/models/test/hulls.c +++ b/libs/models/test/hulls.c @@ -10,7 +10,7 @@ // |ss\ . // 0 1 -static mclipnode_t clipnodes_simple_wedge[] = { +static dclipnode_t clipnodes_simple_wedge[] = { { 0, { 1, CONTENTS_EMPTY}}, { 1, {CONTENTS_EMPTY, CONTENTS_SOLID}}, }; @@ -34,7 +34,7 @@ hull_t hull_simple_wedge = { // sss|sss| |sss // 0 1 2 -static mclipnode_t clipnodes_tpp1[] = { +static dclipnode_t clipnodes_tpp1[] = { { 0, { 1, CONTENTS_SOLID}}, { 1, { 2, CONTENTS_SOLID}}, { 2, {CONTENTS_SOLID, CONTENTS_EMPTY}}, @@ -60,7 +60,7 @@ hull_t hull_tpp1 = { // sss|sss| |sss // 1 0 2 -static mclipnode_t clipnodes_tpp2[] = { +static dclipnode_t clipnodes_tpp2[] = { { 0, { 2, 1}}, { 1, {CONTENTS_SOLID, CONTENTS_SOLID}}, { 2, {CONTENTS_SOLID, CONTENTS_EMPTY}}, @@ -86,7 +86,7 @@ hull_t hull_tpp2 = { // sss| |www|sss // 1 0 2 -static mclipnode_t clipnodes_tppw[] = { +static dclipnode_t clipnodes_tppw[] = { { 0, { 2, 1}}, { 1, {CONTENTS_EMPTY, CONTENTS_SOLID}}, { 2, {CONTENTS_SOLID, CONTENTS_WATER}}, @@ -112,7 +112,7 @@ hull_t hull_tppw = { // |sss // ---+--- 0 // ss0,0ss -static mclipnode_t clipnodes_step1[] = { +static dclipnode_t clipnodes_step1[] = { { 0, { 1, CONTENTS_SOLID}}, { 1, {CONTENTS_EMPTY, 2}}, { 2, {CONTENTS_SOLID, CONTENTS_EMPTY}}, @@ -139,7 +139,7 @@ hull_t hull_step1 = { // |sss // ---+sss 2 // ss0,0ss -static mclipnode_t clipnodes_step2[] = { +static dclipnode_t clipnodes_step2[] = { { 0, { 1, 2}}, { 1, {CONTENTS_EMPTY, CONTENTS_SOLID}}, { 2, {CONTENTS_EMPTY, CONTENTS_SOLID}}, @@ -166,7 +166,7 @@ hull_t hull_step2 = { // sss| // sss+--- 1 // ss0,0ss -static mclipnode_t clipnodes_step3[] = { +static dclipnode_t clipnodes_step3[] = { { 0, { 1, 2}}, { 1, {CONTENTS_EMPTY, CONTENTS_SOLID}}, { 2, {CONTENTS_EMPTY, CONTENTS_SOLID}}, @@ -195,7 +195,7 @@ hull_t hull_step3 = { // |sss // ---+--- 0 // ss0,0ss -static mclipnode_t clipnodes_covered_step[] = { +static dclipnode_t clipnodes_covered_step[] = { { 0, { 1, CONTENTS_SOLID}}, { 1, { 3, 2}}, { 2, {CONTENTS_SOLID, CONTENTS_EMPTY}}, @@ -225,7 +225,7 @@ hull_t hull_covered_step = { // 0,0+--- 1 // /ssss // 2 ssss -static mclipnode_t clipnodes_ramp[] = { +static dclipnode_t clipnodes_ramp[] = { { 0, { 1, 2}}, { 1, {CONTENTS_EMPTY, CONTENTS_SOLID}}, { 2, {CONTENTS_EMPTY, CONTENTS_SOLID}}, @@ -254,7 +254,7 @@ hull_t hull_ramp = { // ss|sss|ss // -8 8 // looking at plane 0: back of 0 is empty, front of 0 has above hole -static mclipnode_t clipnodes_hole[] = { +static dclipnode_t clipnodes_hole[] = { { 0, { 1, CONTENTS_EMPTY}}, { 1, {CONTENTS_SOLID, 2}}, { 2, { 3, CONTENTS_SOLID}}, @@ -284,7 +284,7 @@ hull_t hull_hole = { // |sss| // ---+---+--- 0 // ss0,0s8,0ss -static mclipnode_t clipnodes_ridge[] = { +static dclipnode_t clipnodes_ridge[] = { { 0, { 1, CONTENTS_SOLID}}, { 1, {CONTENTS_EMPTY, 2}}, { 2, { 3, CONTENTS_EMPTY}}, @@ -320,7 +320,7 @@ hull_t hull_ridge = { // 0 -------.------- -20 // sssssssssssssss // sssssssssssssss -static mclipnode_t clipnodes_cave[] = { +static dclipnode_t clipnodes_cave[] = { { 0, { 1, CONTENTS_SOLID}}, { 1, { 2, 3}}, { 2, {CONTENTS_SOLID, 4}}, diff --git a/libs/models/test/testclip.c b/libs/models/test/testclip.c index 6b6672e4b..329f7124b 100644 --- a/libs/models/test/testclip.c +++ b/libs/models/test/testclip.c @@ -6,6 +6,7 @@ #include "QF/va.h" #include "getopt.h" +#include "mod_internal.h" #include "world.h" #include "hulls.h" @@ -40,10 +41,10 @@ typedef struct { vec3_t end; struct { float frac; - qboolean allsolid; - qboolean startsolid; - qboolean inopen; - qboolean inwater; + bool allsolid; + bool startsolid; + bool inopen; + bool inwater; } expect; } test_t; @@ -217,7 +218,7 @@ do_trace (box_t *box, hull_t *hull, vec3_t start, vec3_t end) trace.inwater = false; trace.fraction = 1; VectorCopy (box->extents, trace.extents); - // FIXME specify tract type in test spec + // FIXME specify trace type in test spec trace.type = box == &point ? tr_point : tr_box; VectorCopy (end, trace.endpos); MOD_TraceLine (hull, 0, start, end, &trace); @@ -323,9 +324,9 @@ run_test (test_t *test) err = 1; if (test->desc) - desc = va ("(%d) %s", (int)(long)(test - tests), test->desc); + desc = va (0, "(%d) %s", (int)(long)(test - tests), test->desc); else - desc = va ("test #%d", (int)(long)(test - tests)); + desc = va (0, "test #%d", (int)(long)(test - tests)); if (verbose >= 0 || err) { if (output) puts(""); diff --git a/libs/models/test/testcontents.c b/libs/models/test/testcontents.c index 605ed7a6c..7f69624b8 100644 --- a/libs/models/test/testcontents.c +++ b/libs/models/test/testcontents.c @@ -114,7 +114,7 @@ do_contents (box_t *box, hull_t *hull, vec3_t origin, trace_t *trace) { memset (trace, 0xff, sizeof (*trace)); VectorCopy (box->extents, trace->extents); - // FIXME specify tract type in test spec + // FIXME specify trace type in test spec trace->type = box == &point ? tr_point : tr_box; return MOD_HullContents (hull, 0, origin, trace); } @@ -145,9 +145,9 @@ run_test (test_t *test) res = 1; if (test->desc) - desc = va ("(%d) %s", (int)(long)(test - tests), test->desc); + desc = va (0, "(%d) %s", (int)(long)(test - tests), test->desc); else - desc = va ("test #%d", (int)(long)(test - tests)); + desc = va (0, "test #%d", (int)(long)(test - tests)); if (verbose >= 0 || !res) { if (output) puts(""); diff --git a/libs/models/test/testportals.c b/libs/models/test/testportals.c index 1cc2f50ec..facfa2914 100644 --- a/libs/models/test/testportals.c +++ b/libs/models/test/testportals.c @@ -170,9 +170,9 @@ nodeleaf_bail: MOD_FreeBrushes (test->hull); if (test->desc) - desc = va ("(%d) %s", (int)(long)(test - tests), test->desc); + desc = va (0, "(%d) %s", (int)(long)(test - tests), test->desc); else - desc = va ("test #%d", (int)(long)(test - tests)); + desc = va (0, "test #%d", (int)(long)(test - tests)); if (verbose >= 0 || err) { if (output) puts(""); diff --git a/libs/models/test/trace-id.c b/libs/models/test/trace-id.c index da85a01d0..799575818 100644 --- a/libs/models/test/trace-id.c +++ b/libs/models/test/trace-id.c @@ -5,7 +5,7 @@ static int HullPointContents (hull_t *hull, int num, const vec3_t p) { float d; - mclipnode_t *node; + dclipnode_t *node; mplane_t *plane; while (num >= 0) @@ -34,10 +34,10 @@ HullPointContents (hull_t *hull, int num, const vec3_t p) #define DIST_EPSILON (0.03125) #endif -static qboolean +static bool SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, const vec3_t p1, const vec3_t p2, trace_t *trace) { - mclipnode_t *node; + dclipnode_t *node; mplane_t *plane; float t1, t2; float frac; diff --git a/libs/models/test/trace-qf-bad.c b/libs/models/test/trace-qf-bad.c index 05d31581b..0bf56bbb0 100644 --- a/libs/models/test/trace-qf-bad.c +++ b/libs/models/test/trace-qf-bad.c @@ -94,7 +94,7 @@ MOD_TraceLine (hull_t *hull, int num, int side, empty, solid; tracestack_t *tstack; tracestack_t tracestack[256]; - mclipnode_t *node; + dclipnode_t *node; mplane_t *plane, *split_plane; VectorCopy (start_point, start); diff --git a/libs/models/trace.c b/libs/models/trace.c index 1fcecb47b..be77f6157 100644 --- a/libs/models/trace.c +++ b/libs/models/trace.c @@ -64,9 +64,9 @@ typedef struct { } clipbox_t; typedef struct { - qboolean seen_empty; - qboolean seen_solid; - qboolean moved; + bool seen_empty; + bool seen_solid; + bool moved; plane_t *split_plane; vec3_t dist; const vec_t *origin; @@ -120,7 +120,7 @@ init_box (const trace_t *trace, clipbox_t *box, const vec3_t vel) //FIXME rotated box for (i = 0; i < 3; i++) u[i] = (vel[i] >= 0 ? 1 : -1); - VectorCompMult (u, trace->extents, p); + VectorCompMult (p, u, trace->extents); for (i = 0; i < 3; i++) { box->portals[i].planenum = i; box->portals[i].next[0] = 0; @@ -153,17 +153,17 @@ init_box (const trace_t *trace, clipbox_t *box, const vec3_t vel) box->edges[i].points[j][a] = s[k] * u[i] * box->edges[i].points[j - 1][b]; } - VectorCompMult (box->points[i].points[j - 1], trace->extents, - box->points[i].points[j - 1]); - VectorCompMult (box->edges[i].points[j - 1], trace->extents, - box->edges[i].points[j - 1]); + VectorCompMult (box->points[i].points[j - 1], + box->points[i].points[j - 1], trace->extents); + VectorCompMult (box->edges[i].points[j - 1], + box->edges[i].points[j - 1], trace->extents); VectorScale (box->edges[i].points[j - 1], 2, box->edges[i].points[j - 1]); } - VectorCompMult (box->points[i].points[3], trace->extents, - box->points[i].points[3]); - VectorCompMult (box->edges[i].points[3], trace->extents, - box->edges[i].points[3]); + VectorCompMult (box->points[i].points[3], + box->points[i].points[3], trace->extents); + VectorCompMult (box->edges[i].points[3], + box->edges[i].points[3], trace->extents); VectorScale (box->edges[i].points[3], 2, box->edges[i].points[3]); } @@ -197,7 +197,7 @@ calc_offset (const trace_t *trace, const plane_t *plane) return d; } -static qboolean +static bool point_inside_portal (const clipport_t *portal, const plane_t *plane, const vec3_t p) { @@ -218,7 +218,7 @@ point_inside_portal (const clipport_t *portal, const plane_t *plane, return true; } -static qboolean +static bool edges_intersect (const vec3_t p1, const vec3_t p2, const vec3_t r1, const vec3_t r2) { @@ -242,7 +242,7 @@ edges_intersect (const vec3_t p1, const vec3_t p2, return true; } -static qboolean +static bool trace_hits_portal (const hull_t *hull, const trace_t *trace, clipport_t *portal, const vec3_t start, const vec3_t vel) { @@ -274,7 +274,7 @@ trace_hits_portal (const hull_t *hull, const trace_t *trace, return true; } -static qboolean +static bool trace_enters_leaf (hull_t *hull, trace_t *trace, clipleaf_t *leaf, plane_t *plane, const vec3_t vel, const vec3_t org) { @@ -529,7 +529,7 @@ finish_impact: } } -static qboolean +static bool portal_intersect (trace_t *trace, clipport_t *portal, plane_t *plane, const vec3_t origin) { @@ -566,8 +566,8 @@ portal_intersect (trace_t *trace, clipport_t *portal, plane_t *plane, vec3_t p1, p2, imp, dist; vec_t t1, t2, frac; - VectorCompMult (trace->extents, verts[i][0], p1); - VectorCompMult (trace->extents, verts[i][1], p2); + VectorCompMult (p1, trace->extents, verts[i][0]); + VectorCompMult (p2, trace->extents, verts[i][1]); t1 = PlaneDiff (p1, plane) + o_n; t2 = PlaneDiff (p2, plane) + o_n; // if both ends of the box edge are on the same side (or touching) the @@ -670,7 +670,7 @@ trace_to_leaf (const hull_t *hull, clipleaf_t *leaf, int side; vec_t frac = 1; vec_t t1, t2, offset, f; - qboolean clipped = false; + bool clipped = false; clipleaf_t *l; trace_state_t lstate = *state; @@ -786,7 +786,7 @@ MOD_TraceLine (hull_t *hull, int num, int side; tracestack_t *tstack; tracestack_t *tracestack; - mclipnode_t *node; + dclipnode_t *node; plane_t *plane; clipleaf_t *leaf; trace_state_t trace_state; @@ -927,7 +927,7 @@ MOD_HullContents (hull_t *hull, int num, const vec3_t origin, trace_t *trace) // follow origin down the bsp tree to find the "central" leaf while (num >= 0) { vec_t d; - mclipnode_t *node; + dclipnode_t *node; plane_t *plane; node = hull->clipnodes + num; diff --git a/libs/models/vulkan_skin.c b/libs/models/vulkan_skin.c new file mode 100644 index 000000000..e69de29bb diff --git a/libs/models/winding.c b/libs/models/winding.c index d065224f7..3186ba51a 100644 --- a/libs/models/winding.c +++ b/libs/models/winding.c @@ -40,10 +40,6 @@ #define BOGUS (18000.0) -/** \addtogroup qfbsp_winding -*/ -//@{ - int c_activewindings, c_peakwindings; winding_t * @@ -159,7 +155,7 @@ WindingVectors (const winding_t *w, int unit) } winding_t * -ClipWinding (winding_t *in, plane_t *split, qboolean keepon) +ClipWinding (winding_t *in, plane_t *split, bool keepon) { int maxpts, i, j; int *sides; @@ -175,6 +171,8 @@ ClipWinding (winding_t *in, plane_t *split, qboolean keepon) // +1 for duplicating the first point sides = alloca ((in->numpoints + 1) * sizeof (int)); dists = alloca ((in->numpoints + 1) * sizeof (vec_t)); + sides[0] = 0; + dists[0] = 0; // determine sides for each point for (i = 0; i < in->numpoints; i++) { @@ -326,5 +324,3 @@ FreeWinding (winding_t *w) c_activewindings--; free (w); } - -//@} diff --git a/libs/net/Makefile.am b/libs/net/Makefile.am deleted file mode 100644 index 69f2fbb6c..000000000 --- a/libs/net/Makefile.am +++ /dev/null @@ -1,19 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -SUBDIRS= nc nm -AM_CFLAGS= @PREFER_NON_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include - -noinst_LTLIBRARIES= libnet_chan.la libnet_main.la - -nc_libs=nc/libnc.la -libnet_chan_la_LDFLAGS= @STATIC@ -libnet_chan_la_LIBADD= $(nc_libs) -libnet_chan_la_DEPENDENCIES=$(nc_libs) -libnet_chan_la_SOURCES= net_chan.c - -nm_libs=nm/libnm.la -libnet_main_la_LDFLAGS= @STATIC@ -libnet_main_la_LIBADD= $(nm_libs) -libnet_main_la_DEPENDENCIES=$(nm_libs) -libnet_main_la_SOURCES= net_main.c diff --git a/libs/net/Makemodule.am b/libs/net/Makemodule.am new file mode 100644 index 000000000..05d0d0136 --- /dev/null +++ b/libs/net/Makemodule.am @@ -0,0 +1,14 @@ + +noinst_LTLIBRARIES += libs/net/libnet_chan.la libs/net/libnet_main.la + +include libs/net/nc/Makemodule.am +include libs/net/nm/Makemodule.am + +libs_net_libnet_chan_la_LDFLAGS= @STATIC@ +libs_net_libnet_chan_la_SOURCES= libs/net/net_chan.c ${nc_src} +EXTRA_libs_net_libnet_chan_la_SOURCES = $(ipv4_src) $(ipv6_src) + +nm_libs=nm/libnm.la +libs_net_libnet_main_la_LDFLAGS= @STATIC@ +libs_net_libnet_main_la_SOURCES= libs/net/net_main.c ${nm_src} +EXTRA_libs_net_libnet_main_la_SOURCES = $(nm_extra) diff --git a/libs/net/nc/Makefile.am b/libs/net/nc/Makefile.am deleted file mode 100644 index 08b0cb810..000000000 --- a/libs/net/nc/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CFLAGS= @PREFER_NON_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include - -noinst_LTLIBRARIES= libnc.la - -ipv6_src= net_udp6.c -ipv4_src= net_udp.c -if NETTYPE_IPV6 -ipvX_src= $(ipv6_src) -else -ipvX_src= $(ipv4_src) -endif -libnc_la_SOURCES= $(ipvX_src) -libnc_la_LDFLAGS= @STATIC@ -EXTRA_libnc_la_SOURCES= $(ipv4_src) $(ipv6_src) diff --git a/libs/net/nc/Makemodule.am b/libs/net/nc/Makemodule.am new file mode 100644 index 000000000..ae51d29e2 --- /dev/null +++ b/libs/net/nc/Makemodule.am @@ -0,0 +1,8 @@ +ipv6_src= libs/net/nc/net_udp6.c +ipv4_src= libs/net/nc/net_udp.c +if NETTYPE_IPV6 +ipvX_src= $(ipv6_src) +else +ipvX_src= $(ipv4_src) +endif +nc_src= $(ipvX_src) diff --git a/libs/net/nc/net_udp.c b/libs/net/nc/net_udp.c index 051785c5e..5d238bf37 100644 --- a/libs/net/nc/net_udp.c +++ b/libs/net/nc/net_udp.c @@ -163,7 +163,7 @@ SockadrToNetadr (AF_address_t *s, netadr_t *a) a->port = s->s4.sin_port; } -qboolean +bool NET_CompareBaseAdr (netadr_t a, netadr_t b) { if (memcmp (a.ip, b.ip, ADDR_SIZE) == 0) @@ -171,7 +171,7 @@ NET_CompareBaseAdr (netadr_t a, netadr_t b) return false; } -qboolean +bool NET_CompareAdr (netadr_t a, netadr_t b) { if (memcmp (a.ip, b.ip, ADDR_SIZE) == 0 && a.port == b.port) @@ -208,7 +208,7 @@ NET_BaseAdrToString (netadr_t a) 192.246.40.70 192.246.40.70:28000 */ -qboolean +bool NET_StringToAdr (const char *s, netadr_t *a) { static dstring_t *copy; @@ -246,7 +246,7 @@ NET_StringToAdr (const char *s, netadr_t *a) return true; } -qboolean +bool NET_GetPacket (void) { int ret; @@ -403,6 +403,17 @@ NET_GetLocalAddress (void) Sys_Printf ("IP address %s\n", NET_AdrToString (net_local_adr)); } +static void +NET_shutdown (void *data) +{ +#ifdef _WIN32 + closesocket (net_socket); + WSACleanup (); +#else + close (net_socket); +#endif +} + void NET_Init (int port) { @@ -416,6 +427,7 @@ NET_Init (int port) if (r) Sys_Error ("Winsock initialization failed."); #endif /* _WIN32 */ + Sys_RegisterShutdown (NET_shutdown, 0); net_socket = UDP_OpenSocket (port); @@ -431,14 +443,3 @@ NET_Init (int port) Sys_Printf ("UDP (IPv4) Initialized\n"); } - -void -NET_Shutdown (void) -{ -#ifdef _WIN32 - closesocket (net_socket); - WSACleanup (); -#else - close (net_socket); -#endif -} diff --git a/libs/net/nc/net_udp6.c b/libs/net/nc/net_udp6.c index bad834082..98b2cd51d 100644 --- a/libs/net/nc/net_udp6.c +++ b/libs/net/nc/net_udp6.c @@ -114,7 +114,15 @@ # endif #endif -static cvar_t *net_family; +static char *net_family; +static cvar_t net_family_cvar = { + .name = "net_family", + .description = + "Set the address family to ipv4, ipv6 or unspecified", + .default_value = "unspecified", + .flags = CVAR_ROM, + .value = { .type = 0, .value = &net_family }, +}; netadr_t net_from; netadr_t net_local_adr; @@ -166,7 +174,7 @@ NetadrToSockadr (netadr_t *a, AF_address_t *s) switch (a->family) { case AF_INET: { - Sys_MaskPrintf (SYS_NET, "err, converting v4 to v6...\n"); + Sys_MaskPrintf (SYS_net, "err, converting v4 to v6...\n"); s->ss.ss_family = AF_INET6; s->s6.sin6_addr.s6_addr[10] = s->s6.sin6_addr.s6_addr[11] = 0xff; memcpy (&s->s6.sin6_addr.s6_addr[12], &a->ip, sizeof (s->s4.sin_addr)); @@ -186,7 +194,7 @@ NetadrToSockadr (netadr_t *a, AF_address_t *s) break; } default: - Sys_MaskPrintf (SYS_NET, "%s: Unknown address family %d", __FUNCTION__, a->family); + Sys_MaskPrintf (SYS_net, "%s: Unknown address family %d", __FUNCTION__, a->family); break; } } @@ -207,13 +215,13 @@ SockadrToNetadr (AF_address_t *s, netadr_t *a) break; } default: - Sys_MaskPrintf (SYS_NET, "%s: Unknown address family 0x%x\n", __FUNCTION__, s->ss.ss_family); + Sys_MaskPrintf (SYS_net, "%s: Unknown address family 0x%x\n", __FUNCTION__, s->ss.ss_family); break; } } /* -static qboolean +static bool NET_AdrIsLoopback (netadr_t a) { if (IN6_IS_ADDR_LOOPBACK ((struct in6_addr *) &a.ip)) @@ -226,7 +234,7 @@ NET_AdrIsLoopback (netadr_t a) } */ -qboolean +bool NET_CompareBaseAdr (netadr_t a, netadr_t b) { if (memcmp (a.ip, b.ip, sizeof (a.ip)) == 0) @@ -234,7 +242,7 @@ NET_CompareBaseAdr (netadr_t a, netadr_t b) return false; } -qboolean +bool NET_CompareAdr (netadr_t a, netadr_t b) { if (memcmp (a.ip, b.ip, sizeof (a.ip)) == 0 && a.port == b.port) @@ -313,7 +321,7 @@ NET_BaseAdrToString (netadr_t a) 192.246.40.70 192.246.40.70:28000 */ -qboolean +bool NET_StringToAdr (const char *s, netadr_t *a) { static dstring_t *copy; @@ -330,9 +338,9 @@ NET_StringToAdr (const char *s, netadr_t *a) memset (&hints, 0, sizeof (hints)); hints.ai_socktype = SOCK_DGRAM; - if (strchr (net_family->string, '6')) { + if (strchr (net_family, '6')) { hints.ai_family = AF_INET6; - } else if (strchr (net_family->string, '4')) { + } else if (strchr (net_family, '4')) { hints.ai_family = AF_INET; } else { hints.ai_family = AF_UNSPEC; @@ -387,12 +395,12 @@ NET_StringToAdr (const char *s, netadr_t *a) freeaddrinfo (resultp); SockadrToNetadr (&addr, a); - Sys_MaskPrintf (SYS_NET, "Raw address: %s\n", NET_BaseAdrToString (*a)); + Sys_MaskPrintf (SYS_net, "Raw address: %s\n", NET_BaseAdrToString (*a)); return true; } -qboolean +bool NET_GetPacket (void) { int ret; @@ -492,9 +500,9 @@ UDP_OpenSocket (int port) address.sin6_family = AF_INET6; memset (&hints, 0, sizeof (hints)); - if (strchr (net_family->string, '6')) { + if (strchr (net_family, '6')) { hints.ai_family = AF_INET6; - } else if (strchr (net_family->string, '4')) { + } else if (strchr (net_family, '4')) { hints.ai_family = AF_INET; } else { hints.ai_family = AF_UNSPEC; @@ -509,7 +517,7 @@ UDP_OpenSocket (int port) } else { Host = "::0"; } - Sys_MaskPrintf (SYS_NET, "Binding to IP address [%s]\n", Host); + Sys_MaskPrintf (SYS_net, "Binding to IP address [%s]\n", Host); if (port == PORT_ANY) Service = NULL; @@ -581,9 +589,7 @@ NET_Init (int port) if (r) Sys_Error ("Winsock initialization failed."); #endif /* _WIN32 */ - net_family = Cvar_Get ("net_family", "unspecified", CVAR_ROM, 0, - "Set the address family to ipv4, ipv6 or" - " unspecified"); + Cvar_Register (&net_family_cvar, 0, 0); // open the single socket to be used for all communications net_socket = UDP_OpenSocket (port); @@ -600,8 +606,8 @@ NET_Init (int port) Sys_Printf ("UDP (IPv6) Initialized\n"); } -void -NET_Shutdown (void) +static void +NET_shutdown (void) { #ifdef _WIN32 closesocket (net_socket); diff --git a/libs/net/net_chan.c b/libs/net/net_chan.c index 88f7ad08e..e596281a1 100644 --- a/libs/net/net_chan.c +++ b/libs/net/net_chan.c @@ -49,16 +49,41 @@ #include "compat.h" #include "netchan.h" -#include "../qw/include/client.h" - #define PACKET_HEADER 8 int net_nochoke; int net_blocksend; double *net_realtime; -cvar_t *showpackets; -cvar_t *showdrop; -cvar_t *qport; +int showpackets; +static cvar_t showpackets_cvar = { + .name = "showpackets", + .description = + "Show all network packets", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &showpackets }, +}; +int showdrop; +static cvar_t showdrop_cvar = { + .name = "showdrop", + .description = + "Toggle the display of how many packets you are dropping", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &showdrop }, +}; +int qport; +static cvar_t qport_cvar = { + .name = "qport", + .description = + "The internal port number for the game networking code. Useful for " + "clients who use multiple connections through one IP address (NAT/IP-" + "MASQ) because default port is random.", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &qport }, +}; +void (*net_log_packet) (int length, const void *data, netadr_t to); void @@ -69,21 +94,15 @@ Netchan_Init (void) // pick a port value that should be nice and random port = Sys_TimeID (); - Cvar_SetValue (qport, port); + qport = port; } void Netchan_Init_Cvars (void) { - showpackets = Cvar_Get ("showpackets", "0", CVAR_NONE, NULL, - "Show all network packets"); - showdrop = Cvar_Get ("showdrop", "0", CVAR_NONE, NULL, "Toggle the " - "display of how many packets you are dropping"); - qport = Cvar_Get ("qport", "0", CVAR_NONE, NULL, "The internal port " - "number for the game networking code. Useful for " - "clients who use multiple connections through one " - "IP address (NAT/IP-MASQ) because default port is " - "random."); + Cvar_Register (&showpackets_cvar, 0, 0); + Cvar_Register (&showdrop_cvar, 0, 0); + Cvar_Register (&qport_cvar, 0, 0); } /* @@ -92,7 +111,7 @@ Netchan_Init_Cvars (void) Sends an out-of-band datagram */ void -Netchan_OutOfBand (netadr_t adr, int length, byte * data) +Netchan_OutOfBand (netadr_t adr, unsigned length, byte * data) { byte send_buf[MAX_MSGLEN + PACKET_HEADER]; sizebuf_t send; @@ -163,7 +182,7 @@ Netchan_Setup (netchan_t *chan, netadr_t adr, int qport, ncqport_e flags) Returns true if the bandwidth choke isn't active */ -qboolean +bool Netchan_CanPacket (netchan_t *chan) { if (chan->cleartime < *net_realtime + MAX_BACKUP * chan->rate) @@ -176,7 +195,7 @@ Netchan_CanPacket (netchan_t *chan) Returns true if the bandwidth choke isn't */ -qboolean +bool Netchan_CanReliable (netchan_t *chan) { if (chan->reliable_length) @@ -185,12 +204,12 @@ Netchan_CanReliable (netchan_t *chan) } void -Netchan_Transmit (netchan_t *chan, int length, byte *data) +Netchan_Transmit (netchan_t *chan, unsigned length, byte *data) { byte send_buf[MAX_MSGLEN + PACKET_HEADER]; int i; unsigned int w1, w2; - qboolean send_reliable; + bool send_reliable; sizebuf_t send; // check for message overflow @@ -257,15 +276,19 @@ Netchan_Transmit (netchan_t *chan, int length, byte *data) if (net_nochoke) chan->cleartime = *net_realtime; - if (showpackets->int_val & 1) + if (showpackets & 1) { Sys_Printf ("--> s=%i(%i) a=%i(%i) %-4i %i\n", chan->outgoing_sequence, send_reliable, chan->incoming_sequence, chan->incoming_reliable_sequence, send.cursize, chan->outgoing_sequence - chan->incoming_sequence); + if (showpackets & 4) { + SZ_Dump (&send); + } + } } -qboolean +bool Netchan_Process (netchan_t *chan) { unsigned int reliable_ack, reliable_message, sequence, sequence_ack; @@ -289,9 +312,13 @@ Netchan_Process (netchan_t *chan) sequence &= ~(1 << 31); sequence_ack &= ~(1 << 31); - if (showpackets->int_val & 2) + if (showpackets & 2) { Sys_Printf ("<-- s=%i(%i) a=%i(%i) %i\n", sequence, reliable_message, sequence_ack, reliable_ack, net_message->message->cursize); + if (showpackets & 8) { + SZ_Dump (net_message->message); + } + } // get a rate estimation #if 0 // FIXME: Dead code @@ -322,7 +349,7 @@ Netchan_Process (netchan_t *chan) /// Discard stale or duplicated packets. if (sequence < (unsigned int) chan->incoming_sequence + 1) { - if (showdrop->int_val) + if (showdrop) Sys_Printf ("%s:Out of order packet %i at %i\n", NET_AdrToString (chan->remote_address), sequence, chan->incoming_sequence); @@ -334,7 +361,7 @@ Netchan_Process (netchan_t *chan) if (chan->net_drop > 0) { chan->drop_count += 1; - if (showdrop->int_val) + if (showdrop) Sys_Printf ("%s:Dropped %i packets at %i\n", NET_AdrToString (chan->remote_address), sequence - (chan->incoming_sequence + 1), sequence); @@ -369,9 +396,8 @@ Netchan_Process (netchan_t *chan) void Netchan_SendPacket (int length, const void *data, netadr_t to) { -#if 0 - if (net_packetlog->int_val) - Log_Outgoing_Packet (data, length, 1); -#endif + if (net_log_packet) { + net_log_packet (length, data, to); + } NET_SendPacket (length, data, to); } diff --git a/libs/net/net_main.c b/libs/net/net_main.c index 7c235f4a9..99a1f2c28 100644 --- a/libs/net/net_main.c +++ b/libs/net/net_main.c @@ -47,25 +47,26 @@ #include "netmain.h" #include "net_vcr.h" -#include "../nq/include/host.h" #include "../nq/include/server.h" +int net_is_dedicated = 0; + qsocket_t *net_activeSockets = NULL; qsocket_t *net_freeSockets = NULL; int net_numsockets = 0; -qboolean tcpipAvailable = false; +bool tcpipAvailable = false; int net_hostport; int DEFAULTnet_hostport = 26000; char my_tcpip_address[NET_NAMELEN]; -static qboolean listening = false; +static bool listening = false; -qboolean slistInProgress = false; -qboolean slistSilent = false; -qboolean slistLocal = true; +bool slistInProgress = false; +bool slistSilent = false; +bool slistLocal = true; static double slistStartTime; static int slistLastShown; @@ -78,18 +79,34 @@ PollProcedure slistPollProcedure = { NULL, 0.0, Slist_Poll }; static sizebuf_t _net_message_message; static qmsg_t _net_message = { 0, 0, &_net_message_message }; qmsg_t *net_message = &_net_message; -int net_activeconnections = 0; +unsigned net_activeconnections = 0; int messagesSent = 0; int messagesReceived = 0; int unreliableMessagesSent = 0; int unreliableMessagesReceived = 0; -cvar_t *net_messagetimeout; -cvar_t *hostname; +float net_messagetimeout; +static cvar_t net_messagetimeout_cvar = { + .name = "net_messagetimeout", + .description = + "None", + .default_value = "300", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &net_messagetimeout }, +}; +char *hostname; +static cvar_t hostname_cvar = { + .name = "hostname", + .description = + "None", + .default_value = "UNNAMED", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &hostname }, +}; QFile *vcrFile; -qboolean recording = false; +bool recording = false; // these two macros are to make the code more readable #define sfunc net_drivers[sock->driver] @@ -97,9 +114,13 @@ qboolean recording = false; int net_driverlevel; - double net_time; +static int hostCacheCount = 0; +static hostcache_t hostcache[HOSTCACHESIZE]; + +static cbuf_t *net_cbuf; + double SetNetTime (void) { @@ -195,7 +216,7 @@ NET_Listen_f (void) static void MaxPlayers_f (void) { - int n; + unsigned n; if (Cmd_Argc () != 2) { Sys_Printf ("\"maxplayers\" is \"%u\"\n", svs.maxclients); @@ -217,16 +238,16 @@ MaxPlayers_f (void) } if ((n == 1) && listening) - Cbuf_AddText (host_cbuf, "listen 0\n"); + Cbuf_AddText (net_cbuf, "listen 0\n"); if ((n > 1) && (!listening)) - Cbuf_AddText (host_cbuf, "listen 1\n"); + Cbuf_AddText (net_cbuf, "listen 1\n"); svs.maxclients = n; if (n == 1) - Cvar_Set (deathmatch, "0"); + Cvar_Set ("deathmatch", "0"); else - Cvar_Set (deathmatch, "1"); + Cvar_Set ("deathmatch", "1"); } @@ -251,8 +272,8 @@ NET_Port_f (void) if (listening) { // force a change to the new port - Cbuf_AddText (host_cbuf, "listen 0\n"); - Cbuf_AddText (host_cbuf, "listen 1\n"); + Cbuf_AddText (net_cbuf, "listen 0\n"); + Cbuf_AddText (net_cbuf, "listen 1\n"); } } @@ -265,6 +286,58 @@ PrintSlistHeader (void) slistLastShown = 0; } +void +NET_AddCachedHost (const char *name, const char *map, const char *cname, + int users, int maxusers, int driver, int ldriver, + const netadr_t *addr) +{ + if (hostCacheCount == HOSTCACHESIZE) { + return; + } + for (int i = 0; i < hostCacheCount; i++) { + // addr will be 0 for loopback, and there can be only one loopback + // server. + if (!addr || !memcmp (addr, &hostcache[i].addr, sizeof (netadr_t))) { + return; + } + } + + const int namesize = sizeof (hostcache[0].name) - 1; + const int mapsize = sizeof (hostcache[0].map) - 1; + const int cnamesize = sizeof (hostcache[0].cname) - 1; + + hostcache_t *host = &hostcache[hostCacheCount++]; + strncpy (host->name, name, namesize); + strncpy (host->map, map, mapsize); + strncpy (host->cname, cname, cnamesize); + host->name[namesize] = 0; + host->map[mapsize] = 0; + host->cname[cnamesize] = 0; + + host->users = users; + host->maxusers = maxusers; + host->driver = driver; + host->ldriver = ldriver; + if (addr) { + host->addr = *addr; + } else { + memset (&host->addr, 0, sizeof (host->addr)); + } + + // check for and resolve name conflicts + for (int i = 0; i < hostCacheCount - 1; i++) { + if (strcasecmp (host->name, hostcache[i].name) == 0) { + int len = strlen (host->name); + if (len < namesize && host->name[len - 1] > '8') { + host->name[len] = '0'; + host->name[len + 1] = 0; + } else { + host->name[len - 1]++; + } + i = -1; // restart loop + } + } +} static void PrintSlist (void) @@ -359,10 +432,6 @@ Slist_Poll (void *unused) slistLocal = true; } - -int hostCacheCount = 0; -hostcache_t hostcache[HOSTCACHESIZE]; - qsocket_t * NET_Connect (const char *host) { @@ -488,7 +557,7 @@ NET_Close (qsocket_t *sock) // call the driver_Close function sfunc.Close (sock); - Sys_MaskPrintf (SYS_NET, "closing socket\n"); + Sys_MaskPrintf (SYS_net, "closing socket\n"); NET_FreeQSocket (sock); } @@ -521,8 +590,8 @@ NET_GetMessage (qsocket_t *sock) // see if this connection has timed out if (ret == 0 && sock->driver) { - if (net_time - sock->lastMessageTime > net_messagetimeout->value) { - Sys_MaskPrintf (SYS_NET, "socket timed out\n"); + if (net_time - sock->lastMessageTime > net_messagetimeout) { + Sys_MaskPrintf (SYS_net, "socket timed out\n"); NET_Close (sock); return -1; } @@ -629,7 +698,7 @@ NET_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data) } -qboolean +bool NET_CanSendMessage (qsocket_t *sock) { int r; @@ -660,10 +729,10 @@ int NET_SendToAll (sizebuf_t *data, double blocktime) { double start; - int i; + unsigned i; int count = 0; - qboolean state1[MAX_SCOREBOARD]; /* can we send */ - qboolean state2[MAX_SCOREBOARD]; /* did we send */ + bool state1[MAX_SCOREBOARD]; /* can we send */ + bool state2[MAX_SCOREBOARD]; /* did we send */ for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { @@ -718,80 +787,8 @@ NET_SendToAll (sizebuf_t *data, double blocktime) //============================================================================= -void -NET_Init (void) -{ - int i; - int controlSocket; - qsocket_t *s; - - if (COM_CheckParm ("-playback")) { - net_numdrivers = 1; - net_drivers[0].Init = VCR_Init; - } - - if (COM_CheckParm ("-record")) - recording = true; - - i = COM_CheckParm ("-port"); - if (!i) - i = COM_CheckParm ("-udpport"); - if (!i) - i = COM_CheckParm ("-ipxport"); - - if (i) { - if (i < com_argc - 1) - DEFAULTnet_hostport = atoi (com_argv[i + 1]); - else - Sys_Error ("NET_Init: you must specify a number after -port"); - } - net_hostport = DEFAULTnet_hostport; - - if (COM_CheckParm ("-listen") || cls.state == ca_dedicated) - listening = true; - net_numsockets = svs.maxclientslimit; - if (cls.state != ca_dedicated) - net_numsockets++; - - SetNetTime (); - - for (i = 0; i < net_numsockets; i++) { - s = (qsocket_t *) Hunk_AllocName (sizeof (qsocket_t), "qsocket"); - s->next = net_freeSockets; - net_freeSockets = s; - s->disconnected = true; - } - - // allocate space for network message buffer - SZ_Alloc (&_net_message_message, NET_MAXMESSAGE); - - net_messagetimeout = - Cvar_Get ("net_messagetimeout", "300", CVAR_NONE, NULL, "None"); - hostname = Cvar_Get ("hostname", "UNNAMED", CVAR_NONE, NULL, "None"); - - Cmd_AddCommand ("slist", NET_Slist_f, "No Description"); - Cmd_AddCommand ("listen", NET_Listen_f, "No Description"); - Cmd_AddCommand ("maxplayers", MaxPlayers_f, "No Description"); - Cmd_AddCommand ("port", NET_Port_f, "No Description"); - - // initialize all the drivers - for (net_driverlevel = 0; net_driverlevel < net_numdrivers; - net_driverlevel++) { - controlSocket = net_drivers[net_driverlevel].Init (); - if (controlSocket == -1) - continue; - net_drivers[net_driverlevel].initialized = true; - net_drivers[net_driverlevel].controlSock = controlSocket; - if (listening) - net_drivers[net_driverlevel].Listen (true); - } - - if (*my_tcpip_address) - Sys_MaskPrintf (SYS_NET, "TCP/IP address %s\n", my_tcpip_address); -} - -void -NET_Shutdown (void) +static void +NET_shutdown (void *data) { qsocket_t *sock; @@ -817,6 +814,81 @@ NET_Shutdown (void) } } +void +NET_Init (cbuf_t *cbuf) +{ + int i; + int controlSocket; + qsocket_t *s; + + net_cbuf = cbuf; + + Sys_RegisterShutdown (NET_shutdown, 0); + + if (COM_CheckParm ("-playback")) { + net_numdrivers = 1; + net_drivers[0].Init = VCR_Init; + } + + if (COM_CheckParm ("-record")) + recording = true; + + i = COM_CheckParm ("-port"); + if (!i) + i = COM_CheckParm ("-udpport"); + if (!i) + i = COM_CheckParm ("-ipxport"); + + if (i) { + if (i < com_argc - 1) + DEFAULTnet_hostport = atoi (com_argv[i + 1]); + else + Sys_Error ("NET_Init: you must specify a number after -port"); + } + net_hostport = DEFAULTnet_hostport; + + if (COM_CheckParm ("-listen") || net_is_dedicated) + listening = true; + net_numsockets = svs.maxclientslimit; + if (!net_is_dedicated) + net_numsockets++; + + SetNetTime (); + + for (i = 0; i < net_numsockets; i++) { + s = (qsocket_t *) Hunk_AllocName (0, sizeof (qsocket_t), "qsocket"); + s->next = net_freeSockets; + net_freeSockets = s; + s->disconnected = true; + } + + // allocate space for network message buffer + SZ_Alloc (&_net_message_message, NET_MAXMESSAGE); + + Cvar_Register (&net_messagetimeout_cvar, 0, 0); + Cvar_Register (&hostname_cvar, 0, 0); + + Cmd_AddCommand ("slist", NET_Slist_f, "No Description"); + Cmd_AddCommand ("listen", NET_Listen_f, "No Description"); + Cmd_AddCommand ("maxplayers", MaxPlayers_f, "No Description"); + Cmd_AddCommand ("port", NET_Port_f, "No Description"); + + // initialize all the drivers + for (net_driverlevel = 0; net_driverlevel < net_numdrivers; + net_driverlevel++) { + controlSocket = net_drivers[net_driverlevel].Init (); + if (controlSocket == -1) + continue; + net_drivers[net_driverlevel].initialized = true; + net_drivers[net_driverlevel].controlSock = controlSocket; + if (listening) + net_drivers[net_driverlevel].Listen (true); + } + + if (*my_tcpip_address) + Sys_MaskPrintf (SYS_net, "TCP/IP address %s\n", my_tcpip_address); +} + static PollProcedure *pollProcedureList = NULL; diff --git a/libs/net/nm/Makefile.am b/libs/net/nm/Makefile.am deleted file mode 100644 index cfdaae275..000000000 --- a/libs/net/nm/Makefile.am +++ /dev/null @@ -1,18 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CFLAGS= @PREFER_NON_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include - -noinst_LTLIBRARIES= libnm.la - -if SYSTYPE_WIN32 -net_sources= net_win.c net_wins.c -else -net_sources= net_bsd.c net_udp.c -endif -libnm_la_SOURCES= net_dgrm.c net_loop.c net_vcr.c $(net_sources) -libnm_la_LDFLAGS= @STATIC@ - - -EXTRA_libnm_la_SOURCES= \ - net_bsd.c net_win.c net_wins.c net_udp.c diff --git a/libs/net/nm/Makemodule.am b/libs/net/nm/Makemodule.am new file mode 100644 index 000000000..002b24b02 --- /dev/null +++ b/libs/net/nm/Makemodule.am @@ -0,0 +1,8 @@ +if SYSTYPE_WIN32 +net_sources= libs/net/nm/net_win.c libs/net/nm/net_wins.c +else +net_sources= libs/net/nm/net_bsd.c libs/net/nm/net_udp.c +endif +nm_src= libs/net/nm/net_dgrm.c libs/net/nm/net_loop.c libs/net/nm/net_vcr.c $(net_sources) + +nm_extra = libs/net/nm/net_bsd.c libs/net/nm/net_win.c libs/net/nm/net_wins.c libs/net/nm/net_udp.c diff --git a/libs/net/nm/net_bsd.c b/libs/net/nm/net_bsd.c index 008a0b58a..3e49f31e8 100644 --- a/libs/net/nm/net_bsd.c +++ b/libs/net/nm/net_bsd.c @@ -34,36 +34,37 @@ net_driver_t net_drivers[MAX_NET_DRIVERS] = { { - "Loopback", - false, - Loop_Init, - Loop_Listen, - Loop_SearchForHosts, - Loop_Connect, - Loop_CheckNewConnections, - Loop_GetMessage, - Loop_SendMessage, - Loop_SendUnreliableMessage, - Loop_CanSendMessage, - Loop_CanSendUnreliableMessage, - Loop_Close, - Loop_Shutdown} - , + .name = "Loopback", + .initialized = false, + .Init = Loop_Init, + .Listen = Loop_Listen, + .SearchForHosts = Loop_SearchForHosts, + .Connect = Loop_Connect, + .CheckNewConnections = Loop_CheckNewConnections, + .QGetMessage = Loop_GetMessage, + .QSendMessage = Loop_SendMessage, + .SendUnreliableMessage = Loop_SendUnreliableMessage, + .CanSendMessage = Loop_CanSendMessage, + .CanSendUnreliableMessage = Loop_CanSendUnreliableMessage, + .Close = Loop_Close, + .Shutdown = Loop_Shutdown, + }, { - "Datagram", - false, - Datagram_Init, - Datagram_Listen, - Datagram_SearchForHosts, - Datagram_Connect, - Datagram_CheckNewConnections, - Datagram_GetMessage, - Datagram_SendMessage, - Datagram_SendUnreliableMessage, - Datagram_CanSendMessage, - Datagram_CanSendUnreliableMessage, - Datagram_Close, - Datagram_Shutdown} + .name = "Datagram", + .initialized = false, + .Init = Datagram_Init, + .Listen = Datagram_Listen, + .SearchForHosts = Datagram_SearchForHosts, + .Connect = Datagram_Connect, + .CheckNewConnections = Datagram_CheckNewConnections, + .QGetMessage = Datagram_GetMessage, + .QSendMessage = Datagram_SendMessage, + .SendUnreliableMessage = Datagram_SendUnreliableMessage, + .CanSendMessage = Datagram_CanSendMessage, + .CanSendUnreliableMessage = Datagram_CanSendUnreliableMessage, + .Close = Datagram_Close, + .Shutdown = Datagram_Shutdown, + }, }; int net_numdrivers = 2; @@ -72,26 +73,27 @@ int net_numdrivers = 2; net_landriver_t net_landrivers[MAX_NET_DRIVERS] = { { - "UDP", - false, - 0, - UDP_Init, - UDP_Shutdown, - UDP_Listen, - UDP_OpenSocket, - UDP_CloseSocket, - UDP_Connect, - UDP_CheckNewConnections, - UDP_Read, - UDP_Write, - UDP_Broadcast, - UDP_AddrToString, - UDP_GetSocketAddr, - UDP_GetNameFromAddr, - UDP_GetAddrFromName, - UDP_AddrCompare, - UDP_GetSocketPort, - UDP_SetSocketPort} + .name = "UDP", + .initialized = false, + .controlSock = 0, + .Init = UDP_Init, + .Shutdown = UDP_Shutdown, + .Listen = UDP_Listen, + .OpenSocket = UDP_OpenSocket, + .CloseSocket = UDP_CloseSocket, + .Connect = UDP_Connect, + .CheckNewConnections = UDP_CheckNewConnections, + .Read = UDP_Read, + .Write = UDP_Write, + .Broadcast = UDP_Broadcast, + .AddrToString = UDP_AddrToString, + .GetSocketAddr = UDP_GetSocketAddr, + .GetNameFromAddr = UDP_GetNameFromAddr, + .GetAddrFromName = UDP_GetAddrFromName, + .AddrCompare = UDP_AddrCompare, + .GetSocketPort = UDP_GetSocketPort, + .SetSocketPort = UDP_SetSocketPort, + }, }; int net_numlandrivers = 1; diff --git a/libs/net/nm/net_dgrm.c b/libs/net/nm/net_dgrm.c index 7f7e302c7..2832b418d 100644 --- a/libs/net/nm/net_dgrm.c +++ b/libs/net/nm/net_dgrm.c @@ -38,6 +38,7 @@ #include "QF/cmd.h" #include "QF/cvar.h" #include "QF/sys.h" +#include "QF/heapsort.h" #include "QF/keys.h" #include "QF/qendian.h" #include "QF/msg.h" @@ -46,9 +47,10 @@ #include "netmain.h" +//FIXME these should not be here!!! +#include "client/screen.h" #include "../nq/include/client.h" #include "../nq/include/server.h" -#include "../nq/include/game.h" // This is enables a simple IP banning mechanism #define BAN_TEST @@ -82,16 +84,25 @@ int droppedDatagrams; static int myDriverLevel; -struct { - unsigned int length; - unsigned int sequence; - byte data[MAX_DATAGRAM]; -} packetBuffer; +static byte dgrm_send_packet_data[MAX_DATAGRAM + NET_HEADERSIZE]; +static sizebuf_t dgrm_send_packet = { + .data = dgrm_send_packet_data, + .maxsize = sizeof (dgrm_send_packet_data), +}; + +static byte dgrm_receive_packet_data[MAX_DATAGRAM + NET_HEADERSIZE]; +static sizebuf_t dgrm_receive_packet = { + .data = dgrm_receive_packet_data, + .maxsize = sizeof (dgrm_receive_packet_data), +}; +static qmsg_t dgrm_message = { + .message = &dgrm_receive_packet, +}; // FIXME: MENUCODE //extern int m_return_state; //extern int m_state; -//extern qboolean m_return_onerror; +//extern bool m_return_onerror; //extern char m_return_reason[32]; @@ -104,7 +115,7 @@ NET_Ban_f (void) { char addrStr[32]; //FIXME: overflow char maskStr[32]; //FIXME: overflow - void (*print) (const char *fmt, ...); + __attribute__((format(PRINTF, 1, 2))) void (*print) (const char *fmt, ...); if (cmd_source == src_command) { if (!sv.active) { @@ -113,40 +124,43 @@ NET_Ban_f (void) } print = Sys_Printf; } else { - if (*sv_globals.deathmatch - && !host_client->privileged) return; + if (*sv_globals.deathmatch && !host_client->privileged) { + return; + } print = SV_ClientPrintf; } switch (Cmd_Argc ()) { case 1: - if (((struct in_addr *) &banAddr)->s_addr) { - struct in_addr t; - t.s_addr = banAddr; - strcpy (addrStr, inet_ntoa (t)); - t.s_addr = banMask; - strcpy (maskStr, inet_ntoa (t)); - print ("Banning %s [%s]\n", addrStr, maskStr); - } else - print ("Banning not active\n"); - break; + if (banAddr) { + struct in_addr t; + t.s_addr = banAddr; + strcpy (addrStr, inet_ntoa (t)); + t.s_addr = banMask; + strcpy (maskStr, inet_ntoa (t)); + print ("Banning %s [%s]\n", addrStr, maskStr); + } else { + print ("Banning not active\n"); + } + break; case 2: - if (strcasecmp (Cmd_Argv (1), "off") == 0) - banAddr = 0x00000000; - else - banAddr = inet_addr (Cmd_Argv (1)); - banMask = 0xffffffff; - break; + if (strcasecmp (Cmd_Argv (1), "off") == 0) { + banAddr = 0x00000000; + } else { + banAddr = inet_addr (Cmd_Argv (1)); + } + banMask = 0xffffffff; + break; case 3: - banAddr = inet_addr (Cmd_Argv (1)); - banMask = inet_addr (Cmd_Argv (2)); - break; + banAddr = inet_addr (Cmd_Argv (1)); + banMask = inet_addr (Cmd_Argv (2)); + break; default: - print ("BAN ip_address [mask]\n"); - break; + print ("BAN ip_address [mask]\n"); + break; } } #endif @@ -155,10 +169,9 @@ NET_Ban_f (void) int Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data) { - unsigned int packetLen; - unsigned int dataLen; - unsigned int eom; - + unsigned packetLen; + unsigned dataLen; + unsigned eom; memcpy (sock->sendMessage, data->data, data->cursize); sock->sendMessageLength = data->cursize; @@ -172,15 +185,17 @@ Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data) } packetLen = NET_HEADERSIZE + dataLen; - packetBuffer.length = BigLong (packetLen | (NETFLAG_DATA | eom)); - packetBuffer.sequence = BigLong (sock->sendSequence++); - memcpy (packetBuffer.data, sock->sendMessage, dataLen); + SZ_Clear (&dgrm_send_packet); + MSG_WriteLongBE (&dgrm_send_packet, packetLen | (NETFLAG_DATA | eom)); + MSG_WriteLongBE (&dgrm_send_packet, sock->sendSequence++); + MSG_WriteBytes (&dgrm_send_packet, sock->sendMessage, dataLen); sock->canSend = false; - if (sfunc.Write (sock->socket, (byte *) & packetBuffer, packetLen, - &sock->addr) == -1) + if (sfunc.Write (sock->socket, dgrm_send_packet.data, + dgrm_send_packet.cursize, &sock->addr) == -1) { return -1; + } sock->lastSendTime = net_time; packetsSent++; @@ -191,9 +206,9 @@ Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data) static int SendMessageNext (qsocket_t *sock) { - unsigned int packetLen; - unsigned int dataLen; - unsigned int eom; + unsigned packetLen; + unsigned dataLen; + unsigned eom; if (sock->sendMessageLength <= MAX_DATAGRAM) { dataLen = sock->sendMessageLength; @@ -204,15 +219,17 @@ SendMessageNext (qsocket_t *sock) } packetLen = NET_HEADERSIZE + dataLen; - packetBuffer.length = BigLong (packetLen | (NETFLAG_DATA | eom)); - packetBuffer.sequence = BigLong (sock->sendSequence++); - memcpy (packetBuffer.data, sock->sendMessage, dataLen); + SZ_Clear (&dgrm_send_packet); + MSG_WriteLongBE (&dgrm_send_packet, packetLen | (NETFLAG_DATA | eom)); + MSG_WriteLongBE (&dgrm_send_packet, sock->sendSequence++); + MSG_WriteBytes (&dgrm_send_packet, sock->sendMessage, dataLen); sock->sendNext = false; - if (sfunc.Write (sock->socket, (byte *) & packetBuffer, packetLen, - &sock->addr) == -1) + if (sfunc.Write (sock->socket, dgrm_send_packet.data, + dgrm_send_packet.cursize, &sock->addr) == -1) { return -1; + } sock->lastSendTime = net_time; packetsSent++; @@ -223,9 +240,9 @@ SendMessageNext (qsocket_t *sock) static int ReSendMessage (qsocket_t *sock) { - unsigned int packetLen; - unsigned int dataLen; - unsigned int eom; + unsigned packetLen; + unsigned dataLen; + unsigned eom; if (sock->sendMessageLength <= MAX_DATAGRAM) { dataLen = sock->sendMessageLength; @@ -236,15 +253,17 @@ ReSendMessage (qsocket_t *sock) } packetLen = NET_HEADERSIZE + dataLen; - packetBuffer.length = BigLong (packetLen | (NETFLAG_DATA | eom)); - packetBuffer.sequence = BigLong (sock->sendSequence - 1); - memcpy (packetBuffer.data, sock->sendMessage, dataLen); + SZ_Clear (&dgrm_send_packet); + MSG_WriteLongBE (&dgrm_send_packet, packetLen | (NETFLAG_DATA | eom)); + MSG_WriteLongBE (&dgrm_send_packet, sock->sendSequence - 1); + MSG_WriteBytes (&dgrm_send_packet, sock->sendMessage, dataLen); sock->sendNext = false; - if (sfunc.Write (sock->socket, (byte *) & packetBuffer, packetLen, - &sock->addr) == -1) + if (sfunc.Write (sock->socket, dgrm_send_packet.data, + dgrm_send_packet.cursize, &sock->addr) == -1) { return -1; + } sock->lastSendTime = net_time; packetsReSent++; @@ -252,17 +271,18 @@ ReSendMessage (qsocket_t *sock) } -qboolean +bool Datagram_CanSendMessage (qsocket_t *sock) { - if (sock->sendNext) + if (sock->sendNext) { SendMessageNext (sock); + } return sock->canSend; } -qboolean +__attribute__((const)) bool Datagram_CanSendUnreliableMessage (qsocket_t *sock) { return true; @@ -276,13 +296,15 @@ Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data) packetLen = NET_HEADERSIZE + data->cursize; - packetBuffer.length = BigLong (packetLen | NETFLAG_UNRELIABLE); - packetBuffer.sequence = BigLong (sock->unreliableSendSequence++); - memcpy (packetBuffer.data, data->data, data->cursize); + SZ_Clear (&dgrm_send_packet); + MSG_WriteLongBE (&dgrm_send_packet, packetLen | NETFLAG_UNRELIABLE); + MSG_WriteLongBE (&dgrm_send_packet, sock->unreliableSendSequence++); + MSG_WriteBytes (&dgrm_send_packet, data->data, data->cursize); - if (sfunc.Write (sock->socket, (byte *) & packetBuffer, packetLen, - &sock->addr) == -1) + if (sfunc.Write (sock->socket, dgrm_send_packet.data, + dgrm_send_packet.cursize, &sock->addr) == -1) { return -1; + } packetsSent++; return 1; @@ -292,29 +314,33 @@ Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data) int Datagram_GetMessage (qsocket_t *sock) { - unsigned int length; - unsigned int flags; + unsigned length; + unsigned flags; int ret = 0; netadr_t readaddr; - unsigned int sequence; - unsigned int count; + unsigned sequence; + unsigned count; /// If there is an outstanding reliable packet and more than 1 second has /// passed, resend the packet. - if (!sock->canSend) - if ((net_time - sock->lastSendTime) > 1.0) + if (!sock->canSend) { + if ((net_time - sock->lastSendTime) > 1.0) { ReSendMessage (sock); + } + } while (1) { - length = - sfunc.Read (sock->socket, (byte *) &packetBuffer, NET_DATAGRAMSIZE, - &readaddr); + SZ_Clear (&dgrm_receive_packet); + length = sfunc.Read (sock->socket, dgrm_receive_packet.data, + dgrm_receive_packet.maxsize, &readaddr); + dgrm_receive_packet.cursize = length; // if ((rand() & 255) > 220) // continue; - if (length == 0) + if (length == 0) { break; + } if ((int) length == -1) { Sys_Printf ("Read error\n"); @@ -330,26 +356,29 @@ Datagram_GetMessage (qsocket_t *sock) continue; } - length = BigLong (packetBuffer.length); + MSG_BeginReading (&dgrm_message); + length = MSG_ReadLongBE (&dgrm_message); + flags = length & (~NETFLAG_LENGTH_MASK); length &= NETFLAG_LENGTH_MASK; - if (flags & NETFLAG_CTL) + if (flags & NETFLAG_CTL) { continue; + } - sequence = BigLong (packetBuffer.sequence); + sequence = MSG_ReadLongBE (&dgrm_message); packetsReceived++; if (flags & NETFLAG_UNRELIABLE) { if (sequence < sock->unreliableReceiveSequence) { - Sys_MaskPrintf (SYS_NET, "Got a stale datagram\n"); + Sys_MaskPrintf (SYS_net, "Got a stale datagram\n"); ret = 0; break; } if (sequence != sock->unreliableReceiveSequence) { count = sequence - sock->unreliableReceiveSequence; droppedDatagrams += count; - Sys_MaskPrintf (SYS_NET, "Dropped %u datagram(s)\n", count); + Sys_MaskPrintf (SYS_net, "Dropped %u datagram(s)\n", count); } sock->unreliableReceiveSequence = sequence + 1; @@ -357,7 +386,8 @@ Datagram_GetMessage (qsocket_t *sock) /// Copy unreliable data to net_message SZ_Clear (net_message->message); - SZ_Write (net_message->message, packetBuffer.data, length); + byte *buf = SZ_GetSpace (net_message->message, length); + MSG_ReadBytes (&dgrm_message, buf, length); ret = 2; break; @@ -365,15 +395,16 @@ Datagram_GetMessage (qsocket_t *sock) if (flags & NETFLAG_ACK) { if (sequence != (sock->sendSequence - 1)) { - Sys_MaskPrintf (SYS_NET, "Stale ACK received\n"); + Sys_MaskPrintf (SYS_net, "Stale ACK received\n"); continue; } if (sequence == sock->ackSequence) { sock->ackSequence++; - if (sock->ackSequence != sock->sendSequence) - Sys_MaskPrintf (SYS_NET, "ack sequencing error\n"); + if (sock->ackSequence != sock->sendSequence) { + Sys_MaskPrintf (SYS_net, "ack sequencing error\n"); + } } else { - Sys_MaskPrintf (SYS_NET, "Duplicate ACK received\n"); + Sys_MaskPrintf (SYS_net, "Duplicate ACK received\n"); continue; } sock->sendMessageLength -= MAX_DATAGRAM; @@ -389,10 +420,11 @@ Datagram_GetMessage (qsocket_t *sock) } if (flags & NETFLAG_DATA) { - packetBuffer.length = BigLong (NET_HEADERSIZE | NETFLAG_ACK); - packetBuffer.sequence = BigLong (sequence); - sfunc.Write (sock->socket, (byte *) & packetBuffer, NET_HEADERSIZE, - &readaddr); + SZ_Clear (&dgrm_send_packet); + MSG_WriteLongBE (&dgrm_send_packet, NET_HEADERSIZE | NETFLAG_ACK); + MSG_WriteLongBE (&dgrm_send_packet, sequence); + sfunc.Write (sock->socket, dgrm_send_packet.data, + dgrm_send_packet.cursize, &readaddr); if (sequence != sock->receiveSequence) { receivedDuplicateCount++; @@ -406,7 +438,8 @@ Datagram_GetMessage (qsocket_t *sock) SZ_Clear (net_message->message); SZ_Write (net_message->message, sock->receiveMessage, sock->receiveMessageLength); - SZ_Write (net_message->message, packetBuffer.data, length); + byte *buf = SZ_GetSpace (net_message->message, length); + MSG_ReadBytes (&dgrm_message, buf, length); sock->receiveMessageLength = 0; ret = 1; @@ -414,15 +447,17 @@ Datagram_GetMessage (qsocket_t *sock) } /// Append reliable data to sock->receiveMessage. - memcpy (sock->receiveMessage + sock->receiveMessageLength, - packetBuffer.data, length); + MSG_ReadBytes (&dgrm_message, + sock->receiveMessage + sock->receiveMessageLength, + length); sock->receiveMessageLength += length; continue; } } - if (sock->sendNext) + if (sock->sendNext) { SendMessageNext (sock); + } return ret; } @@ -457,291 +492,32 @@ NET_Stats_f (void) Sys_Printf ("shortPacketCount = %i\n", shortPacketCount); Sys_Printf ("droppedDatagrams = %i\n", droppedDatagrams); } else if (strcmp (Cmd_Argv (1), "*") == 0) { - for (s = net_activeSockets; s; s = s->next) + for (s = net_activeSockets; s; s = s->next) { PrintStats (s); - for (s = net_freeSockets; s; s = s->next) + } + for (s = net_freeSockets; s; s = s->next) { PrintStats (s); + } } else { - for (s = net_activeSockets; s; s = s->next) - if (strcasecmp (Cmd_Argv (1), s->address) == 0) + for (s = net_activeSockets; s; s = s->next) { + if (strcasecmp (Cmd_Argv (1), s->address) == 0) { break; - if (s == NULL) - for (s = net_freeSockets; s; s = s->next) - if (strcasecmp (Cmd_Argv (1), s->address) == 0) + } + } + if (s == NULL) { + for (s = net_freeSockets; s; s = s->next) { + if (strcasecmp (Cmd_Argv (1), s->address) == 0) { break; - if (s == NULL) + } + } + } + if (s == NULL) { return; + } PrintStats (s); } } - -static qboolean testInProgress = false; -static int testPollCount; -static int testDriver; -static int testSocket; - -static void Test_Poll (void *); -PollProcedure testPollProcedure = { NULL, 0.0, Test_Poll }; - -static void -Test_Poll (void *unused) -{ - netadr_t clientaddr; - int control; - int len; - char name[32]; //FIXME: overflow - char address[64]; //FIXME: overflow - int colors; - int frags; - int connectTime; - byte playerNumber; - - net_landriverlevel = testDriver; - - while (1) { - len = - dfunc.Read (testSocket, net_message->message->data, - net_message->message->maxsize, &clientaddr); - if (len < (int) sizeof (int)) - break; - - net_message->message->cursize = len; - - MSG_BeginReading (net_message); - control = BigLong (*((int *) net_message->message->data)); - MSG_ReadLong (net_message); - if (control == -1) - break; - if ((control & (~NETFLAG_LENGTH_MASK)) != (int) NETFLAG_CTL) - break; - if ((control & NETFLAG_LENGTH_MASK) != len) - break; - - if (MSG_ReadByte (net_message) != CCREP_PLAYER_INFO) - Sys_Error ("Unexpected repsonse to Player Info request"); - - playerNumber = MSG_ReadByte (net_message); - strcpy (name, MSG_ReadString (net_message)); - colors = MSG_ReadLong (net_message); - frags = MSG_ReadLong (net_message); - connectTime = MSG_ReadLong (net_message); - strcpy (address, MSG_ReadString (net_message)); - - Sys_Printf ("%d, %s\n frags:%3i colors:%u %u time:%u\n %s\n", - playerNumber, name, frags, colors >> 4, colors & 0x0f, - connectTime / 60, address); - } - - testPollCount--; - if (testPollCount) { - SchedulePollProcedure (&testPollProcedure, 0.1); - } else { - dfunc.CloseSocket (testSocket); - testInProgress = false; - } -} - -static void -Test_f (void) -{ - const char *host; - int n; - int max = MAX_SCOREBOARD; - netadr_t sendaddr; - - if (testInProgress) - return; - - host = Cmd_Argv (1); - - if (host && hostCacheCount) { - for (n = 0; n < hostCacheCount; n++) - if (strcasecmp (host, hostcache[n].name) == 0) { - if (hostcache[n].driver != myDriverLevel) - continue; - net_landriverlevel = hostcache[n].ldriver; - max = hostcache[n].maxusers; - memcpy (&sendaddr, &hostcache[n].addr, - - sizeof (netadr_t)); - break; - } - if (n < hostCacheCount) - goto JustDoIt; - } - - for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; - net_landriverlevel++) { - if (!net_landrivers[net_landriverlevel].initialized) - continue; - - // see if we can resolve the host name - if (dfunc.GetAddrFromName (host, &sendaddr) != -1) - break; - } - if (net_landriverlevel == net_numlandrivers) - return; - - JustDoIt: - testSocket = dfunc.OpenSocket (0); - if (testSocket == -1) - return; - - testInProgress = true; - testPollCount = 20; - testDriver = net_landriverlevel; - - for (n = 0; n < max; n++) { - SZ_Clear (net_message->message); - // save space for the header, filled in later - MSG_WriteLong (net_message->message, 0); - MSG_WriteByte (net_message->message, CCREQ_PLAYER_INFO); - MSG_WriteByte (net_message->message, n); - *((int *) net_message->message->data) = - BigLong (NETFLAG_CTL | - (net_message->message->cursize & NETFLAG_LENGTH_MASK)); - dfunc.Write (testSocket, net_message->message->data, - net_message->message->cursize, &sendaddr); - } - SZ_Clear (net_message->message); - SchedulePollProcedure (&testPollProcedure, 0.1); -} - - -static qboolean test2InProgress = false; -static int test2Driver; -static int test2Socket; - -static void Test2_Poll (void *); -PollProcedure test2PollProcedure = { NULL, 0.0, Test2_Poll }; - -static void -Test2_Poll (void *unused) -{ - netadr_t clientaddr; - int control; - int len; - char name[256]; //FIXME: overflow - char value[256]; //FIXME: overflow - - net_landriverlevel = test2Driver; - name[0] = 0; - - len = - dfunc.Read (test2Socket, net_message->message->data, - net_message->message->maxsize, &clientaddr); - if (len < (int) sizeof (int)) - goto Reschedule; - - net_message->message->cursize = len; - - MSG_BeginReading (net_message); - control = BigLong (*((int *) net_message->message->data)); - MSG_ReadLong (net_message); - if (control == -1) - goto Error; - if ((control & (~NETFLAG_LENGTH_MASK)) != (int) NETFLAG_CTL) - goto Error; - if ((control & NETFLAG_LENGTH_MASK) != len) - goto Error; - - if (MSG_ReadByte (net_message) != CCREP_RULE_INFO) - goto Error; - - strcpy (name, MSG_ReadString (net_message)); - if (name[0] == 0) - goto Done; - strcpy (value, MSG_ReadString (net_message)); - - Sys_Printf ("%-16.16s %-16.16s\n", name, value); - - SZ_Clear (net_message->message); - // save space for the header, filled in later - MSG_WriteLong (net_message->message, 0); - MSG_WriteByte (net_message->message, CCREQ_RULE_INFO); - MSG_WriteString (net_message->message, name); - *((int *) net_message->message->data) = - BigLong (NETFLAG_CTL | - (net_message->message->cursize & NETFLAG_LENGTH_MASK)); - dfunc.Write (test2Socket, net_message->message->data, - net_message->message->cursize, &clientaddr); - SZ_Clear (net_message->message); - - Reschedule: - SchedulePollProcedure (&test2PollProcedure, 0.05); - return; - - Error: - Sys_Printf ("Unexpected repsonse to Rule Info request\n"); - Done: - dfunc.CloseSocket (test2Socket); - test2InProgress = false; - return; -} - -static void -Test2_f (void) -{ - const char *host; - int n; - netadr_t sendaddr; - - if (test2InProgress) - return; - - host = Cmd_Argv (1); - - if (host && hostCacheCount) { - for (n = 0; n < hostCacheCount; n++) - if (strcasecmp (host, hostcache[n].name) == 0) { - if (hostcache[n].driver != myDriverLevel) - continue; - net_landriverlevel = hostcache[n].ldriver; - memcpy (&sendaddr, &hostcache[n].addr, - - sizeof (netadr_t)); - break; - } - if (n < hostCacheCount) - goto JustDoIt; - } - - for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; - net_landriverlevel++) { - if (!net_landrivers[net_landriverlevel].initialized) - continue; - - // see if we can resolve the host name - if (dfunc.GetAddrFromName (host, &sendaddr) != -1) - break; - } - if (net_landriverlevel == net_numlandrivers) - return; - - JustDoIt: - test2Socket = dfunc.OpenSocket (0); - if (test2Socket == -1) - return; - - test2InProgress = true; - test2Driver = net_landriverlevel; - - SZ_Clear (net_message->message); - // save space for the header, filled in later - MSG_WriteLong (net_message->message, 0); - MSG_WriteByte (net_message->message, CCREQ_RULE_INFO); - MSG_WriteString (net_message->message, ""); - *((int *) net_message->message->data) = - BigLong (NETFLAG_CTL | - (net_message->message->cursize & NETFLAG_LENGTH_MASK)); - dfunc.Write (test2Socket, net_message->message->data, - net_message->message->cursize, &sendaddr); - SZ_Clear (net_message->message); - SchedulePollProcedure (&test2PollProcedure, 0.05); -} - - int Datagram_Init (void) { @@ -751,13 +527,15 @@ Datagram_Init (void) myDriverLevel = net_driverlevel; Cmd_AddCommand ("net_stats", NET_Stats_f, "No Description"); - if (COM_CheckParm ("-nolan")) + if (COM_CheckParm ("-nolan")) { return -1; + } for (i = 0; i < net_numlandrivers; i++) { csock = net_landrivers[i].Init (); - if (csock == -1) + if (csock == -1) { continue; + } net_landrivers[i].initialized = true; net_landrivers[i].controlSock = csock; } @@ -765,8 +543,6 @@ Datagram_Init (void) #ifdef BAN_TEST Cmd_AddCommand ("ban", NET_Ban_f, "No Description"); #endif - Cmd_AddCommand ("test", Test_f, "No Description"); - Cmd_AddCommand ("test2", Test2_f, "No Description"); return 0; } @@ -797,15 +573,37 @@ Datagram_Close (qsocket_t *sock) void -Datagram_Listen (qboolean state) +Datagram_Listen (bool state) { int i; - for (i = 0; i < net_numlandrivers; i++) - if (net_landrivers[i].initialized) + for (i = 0; i < net_numlandrivers; i++) { + if (net_landrivers[i].initialized) { net_landrivers[i].Listen (state); + } + } } +static const cvar_t **cvar_list; +static int num_cvars; + +static int +dg_cvar_cmp (const void *_a, const void *_b) +{ + const cvar_t * const *a = _a; + const cvar_t * const *b = _b; + return strcmp ((*a)->name, (*b)->name); +} + +static int +dg_cvar_select (const cvar_t *cvar, void *data) +{ + if (cvar->flags & CVAR_SERVERINFO) { + *(int *)data += 1; + return 1; + } + return 0; +} static qsocket_t * _Datagram_CheckNewConnections (void) @@ -822,33 +620,37 @@ _Datagram_CheckNewConnections (void) int ret; acceptsock = dfunc.CheckNewConnections (); - if (acceptsock == -1) + if (acceptsock == -1) { return NULL; + } SZ_Clear (net_message->message); - len = - dfunc.Read (acceptsock, net_message->message->data, - net_message->message->maxsize, &clientaddr); - if (len < (int) sizeof (int)) + len = dfunc.Read (acceptsock, net_message->message->data, + net_message->message->maxsize, &clientaddr); + if (len < (int) sizeof (int)) { return NULL; + } net_message->message->cursize = len; MSG_BeginReading (net_message); - control = BigLong (*((int *) net_message->message->data)); - MSG_ReadLong (net_message); - if (control == -1) + control = MSG_ReadLongBE (net_message); + if (control == -1) { return NULL; - if ((control & (~NETFLAG_LENGTH_MASK)) != (int) NETFLAG_CTL) + } + if ((control & (~NETFLAG_LENGTH_MASK)) != (int) NETFLAG_CTL) { return NULL; - if ((control & NETFLAG_LENGTH_MASK) != len) + } + if ((control & NETFLAG_LENGTH_MASK) != len) { return NULL; + } command = MSG_ReadByte (net_message); if (command == CCREQ_SERVER_INFO) { - if (strcmp (MSG_ReadString (net_message), "QUAKE") != 0) + if (strcmp (MSG_ReadString (net_message), "QUAKE") != 0) { return NULL; + } SZ_Clear (net_message->message); // save space for the header, filled in later @@ -856,14 +658,13 @@ _Datagram_CheckNewConnections (void) MSG_WriteByte (net_message->message, CCREP_SERVER_INFO); dfunc.GetSocketAddr (acceptsock, &newaddr); MSG_WriteString (net_message->message, dfunc.AddrToString (&newaddr)); - MSG_WriteString (net_message->message, hostname->string); + MSG_WriteString (net_message->message, hostname); MSG_WriteString (net_message->message, sv.name); MSG_WriteByte (net_message->message, net_activeconnections); MSG_WriteByte (net_message->message, svs.maxclients); MSG_WriteByte (net_message->message, NET_PROTOCOL_VERSION); - *((int *) net_message->message->data) = - BigLong (NETFLAG_CTL | - (net_message->message->cursize & NETFLAG_LENGTH_MASK)); + MSG_PokeLongBE (net_message->message, 0, + NETFLAG_CTL | net_message->message->cursize); dfunc.Write (acceptsock, net_message->message->data, net_message->message->cursize, &clientaddr); SZ_Clear (net_message->message); @@ -873,7 +674,7 @@ _Datagram_CheckNewConnections (void) if (command == CCREQ_PLAYER_INFO) { int playerNumber; int activeNumber; - int clientNumber; + unsigned clientNumber; client_t *client; playerNumber = MSG_ReadByte (net_message); @@ -882,12 +683,14 @@ _Datagram_CheckNewConnections (void) clientNumber < svs.maxclients; clientNumber++, client++) { if (client->active) { activeNumber++; - if (activeNumber == playerNumber) + if (activeNumber == playerNumber) { break; + } } } - if (clientNumber == svs.maxclients) + if (clientNumber == svs.maxclients) { return NULL; + } SZ_Clear (net_message->message); // save space for the header, filled in later @@ -900,9 +703,8 @@ _Datagram_CheckNewConnections (void) MSG_WriteLong (net_message->message, (int) (net_time - client->netconnection->connecttime)); MSG_WriteString (net_message->message, client->netconnection->address); - *((int *) net_message->message->data) = - BigLong (NETFLAG_CTL | - (net_message->message->cursize & NETFLAG_LENGTH_MASK)); + MSG_PokeLongBE (net_message->message, 0, + NETFLAG_CTL | net_message->message->cursize); dfunc.Write (acceptsock, net_message->message->data, net_message->message->cursize, &clientaddr); SZ_Clear (net_message->message); @@ -912,23 +714,25 @@ _Datagram_CheckNewConnections (void) if (command == CCREQ_RULE_INFO) { const char *prevCvarName; - cvar_t *var; + const cvar_t *var; // find the search start location prevCvarName = MSG_ReadString (net_message); if (*prevCvarName) { - var = Cvar_FindVar (prevCvarName); + cvar_t key = { .name = prevCvarName }; + var = bsearch (&key, cvar_list, num_cvars, sizeof (cvar_t *), + dg_cvar_cmp); if (!var) - return NULL; - var = var->next; - } else - var = cvar_vars; - - // search for the next server cvar - while (var) { - if (var->flags & CVAR_SERVERINFO) - break; - var = var->next; + return 0; + var++; + } else { + if (cvar_list) { + free (cvar_list); + } + num_cvars = 0; + cvar_list = Cvar_Select (dg_cvar_select, &num_cvars); + heapsort (cvar_list, num_cvars, sizeof (cvar_t *), dg_cvar_cmp); + var = cvar_list[0]; } // send the response @@ -939,11 +743,10 @@ _Datagram_CheckNewConnections (void) MSG_WriteByte (net_message->message, CCREP_RULE_INFO); if (var) { MSG_WriteString (net_message->message, var->name); - MSG_WriteString (net_message->message, var->string); + MSG_WriteString (net_message->message, Cvar_VarString (var)); } - *((int *) net_message->message->data) = - BigLong (NETFLAG_CTL | - (net_message->message->cursize & NETFLAG_LENGTH_MASK)); + MSG_PokeLongBE (net_message->message, 0, + NETFLAG_CTL | net_message->message->cursize); dfunc.Write (acceptsock, net_message->message->data, net_message->message->cursize, &clientaddr); SZ_Clear (net_message->message); @@ -951,11 +754,13 @@ _Datagram_CheckNewConnections (void) return NULL; } - if (command != CCREQ_CONNECT) + if (command != CCREQ_CONNECT) { return NULL; + } - if (strcmp (MSG_ReadString (net_message), "QUAKE") != 0) + if (strcmp (MSG_ReadString (net_message), "QUAKE") != 0) { return NULL; + } if (MSG_ReadByte (net_message) != NET_PROTOCOL_VERSION) { SZ_Clear (net_message->message); @@ -963,9 +768,8 @@ _Datagram_CheckNewConnections (void) MSG_WriteLong (net_message->message, 0); MSG_WriteByte (net_message->message, CCREP_REJECT); MSG_WriteString (net_message->message, "Incompatible version.\n"); - *((int *) net_message->message->data) = - BigLong (NETFLAG_CTL | - (net_message->message->cursize & NETFLAG_LENGTH_MASK)); + MSG_PokeLongBE (net_message->message, 0, + NETFLAG_CTL | net_message->message->cursize); dfunc.Write (acceptsock, net_message->message->data, net_message->message->cursize, &clientaddr); SZ_Clear (net_message->message); @@ -983,9 +787,8 @@ _Datagram_CheckNewConnections (void) MSG_WriteLong (net_message->message, 0); MSG_WriteByte (net_message->message, CCREP_REJECT); MSG_WriteString (net_message->message, "You have been banned.\n"); - *((int *) net_message->message->data) = - BigLong (NETFLAG_CTL | - (net_message->message->cursize & NETFLAG_LENGTH_MASK)); + MSG_PokeLongBE (net_message->message, 0, + NETFLAG_CTL | net_message->message->cursize); dfunc.Write (acceptsock, net_message->message->data, net_message->message->cursize, &clientaddr); SZ_Clear (net_message->message); @@ -996,8 +799,9 @@ _Datagram_CheckNewConnections (void) // see if this guy is already connected for (s = net_activeSockets; s; s = s->next) { - if (s->driver != net_driverlevel) + if (s->driver != net_driverlevel) { continue; + } ret = dfunc.AddrCompare (&clientaddr, &s->addr); if (ret >= 0) { // is this a duplicate connection reqeust? @@ -1010,10 +814,8 @@ _Datagram_CheckNewConnections (void) dfunc.GetSocketAddr (s->socket, &newaddr); MSG_WriteLong (net_message->message, dfunc.GetSocketPort (&newaddr)); - *((int *) net_message->message->data) = - BigLong (NETFLAG_CTL | - (net_message-> - message->cursize & NETFLAG_LENGTH_MASK)); + MSG_PokeLongBE (net_message->message, 0, + NETFLAG_CTL | net_message->message->cursize); dfunc.Write (acceptsock, net_message->message->data, net_message->message->cursize, &clientaddr); SZ_Clear (net_message->message); @@ -1021,7 +823,7 @@ _Datagram_CheckNewConnections (void) } // it's somebody coming back in from a crash/disconnect // so close the old qsocket and let their retry get them back in - Sys_MaskPrintf (SYS_NET, "closing stale socket %d %g\n", ret, + Sys_MaskPrintf (SYS_net, "closing stale socket %d %g\n", ret, net_time - s->connecttime); NET_Close (s); return NULL; @@ -1037,9 +839,8 @@ _Datagram_CheckNewConnections (void) MSG_WriteLong (net_message->message, 0); MSG_WriteByte (net_message->message, CCREP_REJECT); MSG_WriteString (net_message->message, "Server is full.\n"); - *((int *) net_message->message->data) = - BigLong (NETFLAG_CTL | - (net_message->message->cursize & NETFLAG_LENGTH_MASK)); + MSG_PokeLongBE (net_message->message, 0, + NETFLAG_CTL | net_message->message->cursize); dfunc.Write (acceptsock, net_message->message->data, net_message->message->cursize, &clientaddr); SZ_Clear (net_message->message); @@ -1048,13 +849,13 @@ _Datagram_CheckNewConnections (void) // allocate a network socket newsock = dfunc.OpenSocket (0); if (newsock == -1) { - Sys_MaskPrintf (SYS_NET, "failed to open socket"); + Sys_MaskPrintf (SYS_net, "failed to open socket"); NET_FreeQSocket (sock); return NULL; } // connect to the client if (dfunc.Connect (newsock, &clientaddr) == -1) { - Sys_MaskPrintf (SYS_NET, "failed to connect client"); + Sys_MaskPrintf (SYS_net, "failed to connect client"); dfunc.CloseSocket (newsock); NET_FreeQSocket (sock); return NULL; @@ -1074,9 +875,8 @@ _Datagram_CheckNewConnections (void) dfunc.GetSocketAddr (newsock, &newaddr); MSG_WriteLong (net_message->message, dfunc.GetSocketPort (&newaddr)); // MSG_WriteString(net_message->message, dfunc.AddrToString(&newaddr)); - *((int *) net_message->message->data) = - BigLong (NETFLAG_CTL | - (net_message->message->cursize & NETFLAG_LENGTH_MASK)); + MSG_PokeLongBE (net_message->message, 0, + NETFLAG_CTL | net_message->message->cursize); dfunc.Write (acceptsock, net_message->message->data, net_message->message->cursize, &clientaddr); SZ_Clear (net_message->message); @@ -1084,7 +884,7 @@ _Datagram_CheckNewConnections (void) return sock; } -qsocket_t * +qsocket_t * Datagram_CheckNewConnections (void) { qsocket_t *ret = NULL; @@ -1099,11 +899,9 @@ Datagram_CheckNewConnections (void) static void -_Datagram_SearchForHosts (qboolean xmit) +_Datagram_SearchForHosts (bool xmit) { int ret; - int n; - int i; netadr_t readaddr; netadr_t myaddr; int control; @@ -1116,98 +914,72 @@ _Datagram_SearchForHosts (qboolean xmit) MSG_WriteByte (net_message->message, CCREQ_SERVER_INFO); MSG_WriteString (net_message->message, "QUAKE"); MSG_WriteByte (net_message->message, NET_PROTOCOL_VERSION); - *((int *) net_message->message->data) = - BigLong (NETFLAG_CTL | - (net_message->message->cursize & NETFLAG_LENGTH_MASK)); + MSG_PokeLongBE (net_message->message, 0, + NETFLAG_CTL | net_message->message->cursize); dfunc.Broadcast (dfunc.controlSock, net_message->message->data, net_message->message->cursize); SZ_Clear (net_message->message); } - while ( - (ret = - dfunc.Read (dfunc.controlSock, net_message->message->data, - net_message->message->maxsize, &readaddr)) > 0) { - if (ret < (int) sizeof (int)) + while ((ret = dfunc.Read (dfunc.controlSock, net_message->message->data, + net_message->message->maxsize, &readaddr)) > 0) { + if (ret < (int) sizeof (int)) { continue; + } net_message->message->cursize = ret; // don't answer our own query - if (dfunc.AddrCompare (&readaddr, &myaddr) >= 0) - continue; - - // is the cache full? - if (hostCacheCount == HOSTCACHESIZE) + if (dfunc.AddrCompare (&readaddr, &myaddr) >= 0) { continue; + } MSG_BeginReading (net_message); - control = BigLong (*((int *) net_message->message->data)); - MSG_ReadLong (net_message); - if (control == -1) + control = MSG_ReadLongBE (net_message); + if (control == -1) { continue; - if ((control & (~NETFLAG_LENGTH_MASK)) != (int) NETFLAG_CTL) - continue; - if ((control & NETFLAG_LENGTH_MASK) != ret) - continue; - - if (MSG_ReadByte (net_message) != CCREP_SERVER_INFO) - continue; - - dfunc.GetAddrFromName (MSG_ReadString (net_message), &readaddr); - // search the cache for this server - for (n = 0; n < hostCacheCount; n++) - if (dfunc.AddrCompare (&readaddr, &hostcache[n].addr) == 0) - break; - - // is it already there? - if (n < hostCacheCount) - continue; - - // add it - hostCacheCount++; - strcpy (hostcache[n].name, MSG_ReadString (net_message)); - strcpy (hostcache[n].map, MSG_ReadString (net_message)); - hostcache[n].users = MSG_ReadByte (net_message); - hostcache[n].maxusers = MSG_ReadByte (net_message); - if (MSG_ReadByte (net_message) != NET_PROTOCOL_VERSION) { - strcpy (hostcache[n].cname, hostcache[n].name); - hostcache[n].cname[14] = 0; - strcpy (hostcache[n].name, "*"); - strcat (hostcache[n].name, hostcache[n].cname); } - memcpy (&hostcache[n].addr, &readaddr, sizeof (netadr_t)); - - hostcache[n].driver = net_driverlevel; - hostcache[n].ldriver = net_landriverlevel; - strcpy (hostcache[n].cname, dfunc.AddrToString (&readaddr)); - - // check for a name conflict - for (i = 0; i < hostCacheCount; i++) { - if (i == n) - continue; - if (strcasecmp (hostcache[n].name, hostcache[i].name) == 0) { - i = strlen (hostcache[n].name); - if (i < 15 && hostcache[n].name[i - 1] > '8') { - hostcache[n].name[i] = '0'; - hostcache[n].name[i + 1] = 0; - } else - hostcache[n].name[i - 1]++; - i = -1; - } + if ((control & (~NETFLAG_LENGTH_MASK)) != (int) NETFLAG_CTL) { + continue; } + if ((control & NETFLAG_LENGTH_MASK) != ret) { + continue; + } + + if (MSG_ReadByte (net_message) != CCREP_SERVER_INFO) { + continue; + } + + const char *addrstr = MSG_ReadString (net_message); + dfunc.GetAddrFromName (addrstr, &readaddr); + + const char *name = MSG_ReadString (net_message); + const char *map = MSG_ReadString (net_message); + int users = MSG_ReadByte (net_message); + int maxusers = MSG_ReadByte (net_message); + int protocol = MSG_ReadByte (net_message); + const char *cname = dfunc.AddrToString (&readaddr); + + if (protocol != NET_PROTOCOL_VERSION) { + char *new_name = alloca (strlen (name) + 2); + new_name[0] = '*'; + strcpy (new_name + 1, name); + name = new_name; + } + + NET_AddCachedHost (name, map, cname, users, maxusers, net_driverlevel, + net_landriverlevel, &readaddr); } } void -Datagram_SearchForHosts (qboolean xmit) +Datagram_SearchForHosts (bool xmit) { for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) { - if (hostCacheCount == HOSTCACHESIZE) - break; - if (net_landrivers[net_landriverlevel].initialized) + if (net_landrivers[net_landriverlevel].initialized) { _Datagram_SearchForHosts (xmit); + } } } @@ -1226,26 +998,31 @@ _Datagram_Connect (const char *host) const char *reason; // see if we can resolve the host name - if (dfunc.GetAddrFromName (host, &sendaddr) == -1) + if (dfunc.GetAddrFromName (host, &sendaddr) == -1) { return NULL; + } newsock = dfunc.OpenSocket (0); - if (newsock == -1) + if (newsock == -1) { return NULL; + } sock = NET_NewQSocket (); - if (sock == NULL) + if (sock == NULL) { goto ErrorReturn2; + } sock->socket = newsock; sock->landriver = net_landriverlevel; // connect to the host - if (dfunc.Connect (newsock, &sendaddr) == -1) + if (dfunc.Connect (newsock, &sendaddr) == -1) { goto ErrorReturn; + } // send the connection request Sys_Printf ("trying...\n"); - CL_UpdateScreen (cl.time); + cl.viewstate.time = cl.time; + CL_UpdateScreen (&cl.viewstate); start_time = net_time; for (reps = 0; reps < 3; reps++) { @@ -1255,9 +1032,8 @@ _Datagram_Connect (const char *host) MSG_WriteByte (net_message->message, CCREQ_CONNECT); MSG_WriteString (net_message->message, "QUAKE"); MSG_WriteByte (net_message->message, NET_PROTOCOL_VERSION); - *((int *) net_message->message->data) = - BigLong (NETFLAG_CTL | - (net_message->message->cursize & NETFLAG_LENGTH_MASK)); + MSG_PokeLongBE (net_message->message, 0, + NETFLAG_CTL | net_message->message->cursize); dfunc.Write (newsock, net_message->message->data, net_message->message->cursize, &sendaddr); SZ_Clear (net_message->message); @@ -1269,11 +1045,11 @@ _Datagram_Connect (const char *host) if (ret > 0) { // is it from the right place? if (sfunc.AddrCompare (&readaddr, &sendaddr) != 0) { - Sys_MaskPrintf (SYS_NET, "%2d ", + Sys_MaskPrintf (SYS_net, "%2d ", sfunc.AddrCompare (&readaddr, &sendaddr)); - Sys_MaskPrintf (SYS_NET, "%d %s ", readaddr.family, + Sys_MaskPrintf (SYS_net, "%d %s ", readaddr.family, sfunc.AddrToString (&readaddr)); - Sys_MaskPrintf (SYS_NET, "%d %s\n", sendaddr.family, + Sys_MaskPrintf (SYS_net, "%d %s\n", sendaddr.family, sfunc.AddrToString (&sendaddr)); ret = 0; continue; @@ -1287,8 +1063,7 @@ _Datagram_Connect (const char *host) net_message->message->cursize = ret; MSG_BeginReading (net_message); - control = BigLong (*((int *) net_message->message->data)); - MSG_ReadLong (net_message); + control = MSG_ReadLongBE (net_message); if (control == -1) { ret = 0; continue; @@ -1302,12 +1077,14 @@ _Datagram_Connect (const char *host) continue; } } - } - while (ret == 0 && (SetNetTime () - start_time) < 2.5); - if (ret) + } while (ret == 0 && (SetNetTime () - start_time) < 2.5); + + if (ret) { break; + } Sys_Printf ("still trying...\n"); - CL_UpdateScreen (cl.time); + cl.viewstate.time = cl.time; + CL_UpdateScreen (&cl.viewstate); start_time = SetNetTime (); } @@ -1335,7 +1112,6 @@ _Datagram_Connect (const char *host) if (ret == CCREP_ACCEPT) { memcpy (&sock->addr, &sendaddr, sizeof (netadr_t)); - dfunc.SetSocketPort (&sock->addr, MSG_ReadLong (net_message)); } else { reason = "Bad Response"; @@ -1363,7 +1139,7 @@ _Datagram_Connect (const char *host) ErrorReturn: // FIXME: MENUCODE - do something with reason - Sys_MaskPrintf (SYS_NET, "FIXME: MENUCODE - do something with reason\n"); + Sys_MaskPrintf (SYS_net, "FIXME: MENUCODE - do something with reason\n"); NET_FreeQSocket (sock); ErrorReturn2: dfunc.CloseSocket (newsock); @@ -1382,9 +1158,12 @@ Datagram_Connect (const char *host) qsocket_t *ret = NULL; for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; - net_landriverlevel++) - if (net_landrivers[net_landriverlevel].initialized) - if ((ret = _Datagram_Connect (host)) != NULL) + net_landriverlevel++) { + if (net_landrivers[net_landriverlevel].initialized) { + if ((ret = _Datagram_Connect (host)) != NULL) { break; + } + } + } return ret; } diff --git a/libs/net/nm/net_loop.c b/libs/net/nm/net_loop.c index 0323b760d..d7e17c898 100644 --- a/libs/net/nm/net_loop.c +++ b/libs/net/nm/net_loop.c @@ -35,17 +35,16 @@ #include "netmain.h" #include "net_loop.h" -#include "../nq/include/client.h" #include "../nq/include/server.h" -qboolean localconnectpending = false; +bool localconnectpending = false; qsocket_t *loop_client = NULL; qsocket_t *loop_server = NULL; -int +__attribute__((pure)) int Loop_Init (void) { - if (cls.state == ca_dedicated) + if (net_is_dedicated) return -1; return 0; } @@ -58,27 +57,27 @@ Loop_Shutdown (void) void -Loop_Listen (qboolean state) +Loop_Listen (bool state) { } void -Loop_SearchForHosts (qboolean xmit) +Loop_SearchForHosts (bool xmit) { if (!sv.active) return; - hostCacheCount = 1; - if (strcmp (hostname->string, "UNNAMED") == 0) - strcpy (hostcache[0].name, "local"); - else - strcpy (hostcache[0].name, hostname->string); - strcpy (hostcache[0].map, sv.name); - hostcache[0].users = net_activeconnections; - hostcache[0].maxusers = svs.maxclients; - hostcache[0].driver = net_driverlevel; - strcpy (hostcache[0].cname, "local"); + const char *name = "local"; + if (strcmp (hostname, "UNNAMED") != 0) { + name = hostname; + } + const char *map = sv.name; + int users = net_activeconnections; + int maxusers = svs.maxclients; + const char *cname = "local"; + NET_AddCachedHost (name, map, cname, users, maxusers, net_driverlevel, + 0, 0); } @@ -240,7 +239,7 @@ Loop_SendUnreliableMessage (qsocket_t * sock, sizebuf_t *data) } -qboolean +__attribute__((pure)) bool Loop_CanSendMessage (qsocket_t * sock) { if (!sock->driverdata) @@ -249,7 +248,7 @@ Loop_CanSendMessage (qsocket_t * sock) } -qboolean +__attribute__((const)) bool Loop_CanSendUnreliableMessage (qsocket_t * sock) { return true; diff --git a/libs/net/nm/net_udp.c b/libs/net/nm/net_udp.c index 84f01a3a9..b7b5c4d68 100644 --- a/libs/net/nm/net_udp.c +++ b/libs/net/nm/net_udp.c @@ -58,12 +58,6 @@ #ifdef HAVE_SYS_IOCTL_H # include #endif -#ifdef HAVE_WINDOWS_H -# include -#endif -#ifdef HAVE_WINSOCK_H -# include -#endif #ifdef HAVE_UNISTD_H # include #endif @@ -77,6 +71,7 @@ # include #endif +#include #include #include #include @@ -194,7 +189,7 @@ get_iface_list (int sock) if (getifaddrs (&ifa_head) < 0) goto no_ifaddrs; for (ifa = ifa_head; ifa; ifa = ifa->ifa_next) { - if (!ifa->ifa_flags & IFF_UP) + if (!(ifa->ifa_flags & IFF_UP)) continue; if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET) continue; @@ -203,18 +198,18 @@ get_iface_list (int sock) num_ifaces = index; } ifaces = malloc (num_ifaces * sizeof (uint32_t)); - Sys_MaskPrintf (SYS_NET, "%d interfaces\n", num_ifaces); + Sys_MaskPrintf (SYS_net, "%d interfaces\n", num_ifaces); for (ifa = ifa_head; ifa; ifa = ifa->ifa_next) { struct sockaddr_in *sa; - if (!ifa->ifa_flags & IFF_UP) + if (!(ifa->ifa_flags & IFF_UP)) continue; if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET) continue; index = if_nametoindex (ifa->ifa_name) - 1; sa = (struct sockaddr_in *) ifa->ifa_addr; memcpy (&ifaces[index], &sa->sin_addr, sizeof (uint32_t)); - Sys_MaskPrintf (SYS_NET, " %-10s %s\n", ifa->ifa_name, + Sys_MaskPrintf (SYS_net, " %-10s %s\n", ifa->ifa_name, inet_ntoa (sa->sin_addr)); if (!default_iface && ifaces[index] != htonl (0x7f000001)) default_iface = &ifaces[index]; @@ -258,9 +253,9 @@ UDP_Init (void) myAddr = 0; // if the quake hostname isn't set, set it to the machine name - if (strcmp (hostname->string, "UNNAMED") == 0) { + if (strcmp (hostname, "UNNAMED") == 0) { buff[15] = 0; - Cvar_Set (hostname, buff); + Cvar_Set ("hostname", buff); } if ((net_controlsocket = UDP_OpenSocket (0)) == -1) @@ -297,7 +292,7 @@ UDP_Shutdown (void) } void -UDP_Listen (qboolean state) +UDP_Listen (bool state) { // enable listening if (state) { @@ -387,7 +382,7 @@ PartialIPAddress (const char *in, netadr_t *hostaddr) { char *buff; char *b; - int addr, mask, num, port, run; + unsigned addr, mask, num, port, run; buff = nva (".%s", in); b = buff; @@ -407,7 +402,7 @@ PartialIPAddress (const char *in, netadr_t *hostaddr) } if ((*b < '0' || *b > '9') && *b != '.' && *b != ':' && *b != 0) goto error; - if (num < 0 || num > 255) + if (num > 255) goto error; mask <<= 8; addr = (addr << 8) + num; @@ -433,7 +428,7 @@ error: return -1; } -int +__attribute__((const)) int UDP_Connect (int socket, netadr_t *addr) { return 0; @@ -471,6 +466,29 @@ UDP_CheckNewConnections (void) #endif } +static void +hex_dump_buf (byte *buf, int len) +{ + int pos = 0, llen, i; + + while (pos < len) { + llen = (len - pos < 16 ? len - pos : 16); + printf ("%08x: ", pos); + for (i = 0; i < llen; i++) + printf ("%02x ", buf[pos + i]); + for (i = 0; i < 16 - llen; i++) + printf (" "); + printf (" | "); + + for (i = 0; i < llen; i++) + printf ("%c", isprint (buf[pos + i]) ? buf[pos + i] : '.'); + for (i = 0; i < 16 - llen; i++) + printf (" "); + printf ("\n"); + pos += llen; + } +} + int UDP_Read (int socket, byte *buf, int len, netadr_t *from) { @@ -498,7 +516,7 @@ UDP_Read (int socket, byte *buf, int len, netadr_t *from) return 0; for (cmsg = CMSG_FIRSTHDR (&msghdr); cmsg; cmsg = CMSG_NXTHDR (&msghdr, cmsg)) { - Sys_MaskPrintf (SYS_NET, "%d\n", cmsg->cmsg_type); + Sys_MaskPrintf (SYS_net, "%d\n", cmsg->cmsg_type); if (cmsg->cmsg_type == IP_PKTINFO) { info = (struct in_pktinfo *) CMSG_DATA (cmsg); break; @@ -512,8 +530,8 @@ UDP_Read (int socket, byte *buf, int len, netadr_t *from) last_iface = &ifaces[info->ipi_ifindex - 1]; } SockadrToNetadr (&addr, from); - Sys_MaskPrintf (SYS_NET, "got %d bytes from %s on iface %d (%s)\n", ret, - UDP_AddrToString (from), info ? info->ipi_ifindex - 1 : -1, + Sys_MaskPrintf (SYS_net, "got %d bytes from %s on iface %d (%s)\n", ret, + UDP_AddrToString (from), info ? (int) info->ipi_ifindex - 1 : -1, last_iface ? inet_ntoa (info->ipi_addr) : "?"); #else socklen_t addrlen = sizeof (AF_address_t); @@ -524,10 +542,13 @@ UDP_Read (int socket, byte *buf, int len, netadr_t *from) if (ret == -1 && (errno == EWOULDBLOCK || errno == ECONNREFUSED)) return 0; SockadrToNetadr (&addr, from); - Sys_MaskPrintf (SYS_NET, "got %d bytes from %s\n", ret, + Sys_MaskPrintf (SYS_net, "got %d bytes from %s\n", ret, UDP_AddrToString (from)); last_iface = default_iface; #endif + if (developer & SYS_net) { + hex_dump_buf (buf, ret); + } return ret; } @@ -574,8 +595,11 @@ UDP_Write (int socket, byte *buf, int len, netadr_t *to) SA_LEN (&addr.sa)); if (ret == -1 && errno == EWOULDBLOCK) return 0; - Sys_MaskPrintf (SYS_NET, "sent %d bytes to %s\n", ret, + Sys_MaskPrintf (SYS_net, "sent %d bytes to %s\n", ret, UDP_AddrToString (to)); + if (developer & SYS_net) { + hex_dump_buf (buf, len); + } return ret; } @@ -651,7 +675,7 @@ UDP_GetAddrFromName (const char *name, netadr_t *addr) return 0; } -int +__attribute__((pure)) int UDP_AddrCompare (netadr_t *addr1, netadr_t *addr2) { if (addr1->family != addr2->family) @@ -666,7 +690,7 @@ UDP_AddrCompare (netadr_t *addr1, netadr_t *addr2) return 0; } -int +__attribute__((pure)) int UDP_GetSocketPort (netadr_t *addr) { return ntohs (addr->port); diff --git a/libs/net/nm/net_vcr.c b/libs/net/nm/net_vcr.c index 603257813..56914d34f 100644 --- a/libs/net/nm/net_vcr.c +++ b/libs/net/nm/net_vcr.c @@ -79,7 +79,7 @@ VCR_ReadNext (void) void -VCR_Listen (qboolean state) +VCR_Listen (bool state) { } @@ -136,10 +136,10 @@ VCR_SendMessage (qsocket_t * sock, sizebuf_t *data) } -qboolean +bool VCR_CanSendMessage (qsocket_t * sock) { - qboolean ret; + bool ret; long *driverdata = (long *) (char *) &sock->driverdata; if (host_time != next.time || next.op != VCR_OP_CANSENDMESSAGE @@ -161,19 +161,19 @@ VCR_Close (qsocket_t * sock) void -VCR_SearchForHosts (qboolean xmit) +VCR_SearchForHosts (bool xmit) { } -qsocket_t * +__attribute__((const)) qsocket_t * VCR_Connect (const char *host) { return NULL; } -qsocket_t * +qsocket_t * VCR_CheckNewConnections (void) { qsocket_t *sock; diff --git a/libs/net/nm/net_win.c b/libs/net/nm/net_win.c index 0dcbd27e6..f343d907a 100644 --- a/libs/net/nm/net_win.c +++ b/libs/net/nm/net_win.c @@ -33,36 +33,37 @@ net_driver_t net_drivers[MAX_NET_DRIVERS] = { { - "Loopback", - false, - Loop_Init, - Loop_Listen, - Loop_SearchForHosts, - Loop_Connect, - Loop_CheckNewConnections, - Loop_GetMessage, - Loop_SendMessage, - Loop_SendUnreliableMessage, - Loop_CanSendMessage, - Loop_CanSendUnreliableMessage, - Loop_Close, - Loop_Shutdown} - , + .name = "Loopback", + .initialized = false, + .Init = Loop_Init, + .Listen = Loop_Listen, + .SearchForHosts = Loop_SearchForHosts, + .Connect = Loop_Connect, + .CheckNewConnections = Loop_CheckNewConnections, + .QGetMessage = Loop_GetMessage, + .QSendMessage = Loop_SendMessage, + .SendUnreliableMessage = Loop_SendUnreliableMessage, + .CanSendMessage = Loop_CanSendMessage, + .CanSendUnreliableMessage = Loop_CanSendUnreliableMessage, + .Close = Loop_Close, + .Shutdown = Loop_Shutdown, + }, { - "Datagram", - false, - Datagram_Init, - Datagram_Listen, - Datagram_SearchForHosts, - Datagram_Connect, - Datagram_CheckNewConnections, - Datagram_GetMessage, - Datagram_SendMessage, - Datagram_SendUnreliableMessage, - Datagram_CanSendMessage, - Datagram_CanSendUnreliableMessage, - Datagram_Close, - Datagram_Shutdown} + .name = "Datagram", + .initialized = false, + .Init = Datagram_Init, + .Listen = Datagram_Listen, + .SearchForHosts = Datagram_SearchForHosts, + .Connect = Datagram_Connect, + .CheckNewConnections = Datagram_CheckNewConnections, + .QGetMessage = Datagram_GetMessage, + .QSendMessage = Datagram_SendMessage, + .SendUnreliableMessage = Datagram_SendUnreliableMessage, + .CanSendMessage = Datagram_CanSendMessage, + .CanSendUnreliableMessage = Datagram_CanSendUnreliableMessage, + .Close = Datagram_Close, + .Shutdown = Datagram_Shutdown, + }, }; int net_numdrivers = 2; @@ -72,26 +73,27 @@ int net_numdrivers = 2; net_landriver_t net_landrivers[MAX_NET_DRIVERS] = { { - "Winsock TCPIP", - false, - 0, - WINS_Init, - WINS_Shutdown, - WINS_Listen, - WINS_OpenSocket, - WINS_CloseSocket, - WINS_Connect, - WINS_CheckNewConnections, - WINS_Read, - WINS_Write, - WINS_Broadcast, - WINS_AddrToString, - WINS_GetSocketAddr, - WINS_GetNameFromAddr, - WINS_GetAddrFromName, - WINS_AddrCompare, - WINS_GetSocketPort, - WINS_SetSocketPort}, + .name = "Winsock TCPIP", + .initialized = false, + .controlSock = 0, + .Init = WINS_Init, + .Shutdown = WINS_Shutdown, + .Listen = WINS_Listen, + .OpenSocket = WINS_OpenSocket, + .CloseSocket = WINS_CloseSocket, + .Connect = WINS_Connect, + .CheckNewConnections = WINS_CheckNewConnections, + .Read = WINS_Read, + .Write = WINS_Write, + .Broadcast = WINS_Broadcast, + .AddrToString = WINS_AddrToString, + .GetSocketAddr = WINS_GetSocketAddr, + .GetNameFromAddr = WINS_GetNameFromAddr, + .GetAddrFromName = WINS_GetAddrFromName, + .AddrCompare = WINS_AddrCompare, + .GetSocketPort = WINS_GetSocketPort, + .SetSocketPort = WINS_SetSocketPort, + }, }; int net_numlandrivers = 1; diff --git a/libs/net/nm/net_wins.c b/libs/net/nm/net_wins.c index 77eaccc21..b86fc4bc0 100644 --- a/libs/net/nm/net_wins.c +++ b/libs/net/nm/net_wins.c @@ -1,4 +1,3 @@ - /* net_wins.c @@ -74,7 +73,7 @@ static netadr_t broadcastaddr; static unsigned long myAddr; -qboolean winsock_lib_initialized; +bool winsock_lib_initialized; int (PASCAL FAR * pWSAStartup) (WORD wVersionRequired, @@ -247,13 +246,13 @@ WINS_Init (void) // determine my name if (pgethostname (buff, MAXHOSTNAMELEN) == SOCKET_ERROR) { - Sys_MaskPrintf (SYS_NET, "Winsock TCP/IP Initialization failed.\n"); + Sys_MaskPrintf (SYS_net, "Winsock TCP/IP Initialization failed.\n"); if (--winsock_initialized == 0) pWSACleanup (); return -1; } // if the quake hostname isn't set, set it to the machine name - if (strcmp (hostname->string, "UNNAMED") == 0) { + if (strcmp (hostname, "UNNAMED") == 0) { // see if it's a text IP address (well, close enough) for (p = buff; *p; p++) if ((*p < '0' || *p > '9') && *p != '.') @@ -266,7 +265,7 @@ WINS_Init (void) break; buff[i] = 0; } - Cvar_Set (hostname, buff); + Cvar_Set ("hostname", buff); } i = COM_CheckParm ("-ip"); @@ -319,7 +318,7 @@ WINS_Shutdown (void) //============================================================================= void -WINS_Listen (qboolean state) +WINS_Listen (bool state) { // enable listening if (state) { @@ -437,10 +436,11 @@ PartialIPAddress (const char *in, netadr_t *hostaddr) return 0; } //============================================================================= - +static int WINS_Connect_called; int WINS_Connect (int socket, netadr_t *addr) { + WINS_Connect_called++; return 0; } @@ -543,6 +543,10 @@ WINS_AddrToString (netadr_t *addr) { static dstring_t *buffer; + if (!buffer) { + buffer = dstring_new (); + } + dsprintf (buffer, "%d.%d.%d.%d:%d", addr->ip[0], addr->ip[1], addr->ip[2], addr->ip[3], ntohs (addr->port)); diff --git a/libs/qw/Makefile.am b/libs/qw/Makefile.am deleted file mode 100644 index 48a58fa72..000000000 --- a/libs/qw/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CPPFLAGS= -I$(top_srcdir)/include - -noinst_LIBRARIES= libqw.a - -libqw_a_SOURCES= \ - msg_backbuf.c msg_ucmd.c diff --git a/libs/qw/Makemodule.am b/libs/qw/Makemodule.am new file mode 100644 index 000000000..42834b43a --- /dev/null +++ b/libs/qw/Makemodule.am @@ -0,0 +1,4 @@ +noinst_LIBRARIES += libs/qw/libqw.a + +libs_qw_libqw_a_SOURCES= \ + libs/qw/msg_backbuf.c libs/qw/msg_ucmd.c diff --git a/libs/qw/msg_backbuf.c b/libs/qw/msg_backbuf.c index ce3607516..3a87cedc5 100644 --- a/libs/qw/msg_backbuf.c +++ b/libs/qw/msg_backbuf.c @@ -47,7 +47,7 @@ PushBackbuf (backbuf_t *rel) { int tail_backbuf; - Sys_MaskPrintf (SYS_DEV, "backbuffering %d %s\n", rel->num_backbuf, + Sys_MaskPrintf (SYS_dev, "backbuffering %d %s\n", rel->num_backbuf, rel->name); tail_backbuf = (rel->head_backbuf + rel->num_backbuf) % MAX_BACK_BUFFERS; memset (&rel->backbuf, 0, sizeof (rel->backbuf)); @@ -59,7 +59,7 @@ PushBackbuf (backbuf_t *rel) } int -MSG_ReliableCheckSize (backbuf_t *rel, int maxsize, int minsize) +MSG_ReliableCheckSize (backbuf_t *rel, unsigned maxsize, unsigned minsize) { sizebuf_t *msg = &rel->netchan->message; @@ -80,7 +80,7 @@ MSG_ReliableCheckSize (backbuf_t *rel, int maxsize, int minsize) // check to see if client block will fit, if not, rotate buffers sizebuf_t * -MSG_ReliableCheckBlock (backbuf_t *rel, int maxsize) +MSG_ReliableCheckBlock (backbuf_t *rel, unsigned maxsize) { sizebuf_t *msg = &rel->netchan->message; @@ -108,7 +108,7 @@ MSG_ReliableCheckBlock (backbuf_t *rel, int maxsize) // begin a client block, estimated maximum size sizebuf_t * -MSG_ReliableWrite_Begin (backbuf_t *rel, int c, int maxsize) +MSG_ReliableWrite_Begin (backbuf_t *rel, int c, unsigned maxsize) { sizebuf_t *msg; msg = MSG_ReliableCheckBlock (rel, maxsize); @@ -226,7 +226,7 @@ MSG_ReliableWrite_String (backbuf_t *rel, const char *s) } void -MSG_ReliableWrite_SZ (backbuf_t *rel, const void *data, int len) +MSG_ReliableWrite_SZ (backbuf_t *rel, const void *data, unsigned len) { if (rel->num_backbuf) { SZ_Write (&rel->backbuf, data, len); @@ -266,7 +266,7 @@ MSG_Reliable_Send (backbuf_t *rel) return; // will it fit? if (msg->cursize + *size < msg->maxsize) { - Sys_MaskPrintf (SYS_DEV, "%s: backbuf %d bytes\n", rel->name, *size); + Sys_MaskPrintf (SYS_dev, "%s: backbuf %d bytes\n", rel->name, *size); // it'll fit SZ_Write (msg, data, *size); diff --git a/libs/ruamoko/Makefile.am b/libs/ruamoko/Makefile.am deleted file mode 100644 index 64c65e972..000000000 --- a/libs/ruamoko/Makefile.am +++ /dev/null @@ -1,21 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CFLAGS= @PREFER_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include $(FNM_FLAGS) - -lib_ldflags=-version-info $(QUAKE_LIBRARY_VERSION_INFO) \ - -rpath $(libdir) -no-undefined -rua_libs= \ - $(top_builddir)/libs/gamecode/libQFgamecode.la \ - $(top_builddir)/libs/util/libQFutil.la - -lib_LTLIBRARIES= libQFruamoko.la - -libQFruamoko_la_LDFLAGS= $(lib_ldflags) -libQFruamoko_la_LIBADD= $(rua_libs) -libQFruamoko_la_DEPENDENCIES= $(rua_libs) -libQFruamoko_la_SOURCES= \ - pr_cmds.c \ - rua_cbuf.c rua_cmd.c rua_cvar.c rua_file.c rua_hash.c rua_init.c \ - rua_math.c rua_msgbuf.c rua_obj.c rua_plist.c rua_qfile.c rua_qfs.c \ - rua_script.c rua_set.c rua_string.c diff --git a/libs/ruamoko/Makemodule.am b/libs/ruamoko/Makemodule.am new file mode 100644 index 000000000..e0f39850c --- /dev/null +++ b/libs/ruamoko/Makemodule.am @@ -0,0 +1,45 @@ +ruamoko_rua_libs= \ + libs/gamecode/libQFgamecode.la \ + libs/util/libQFutil.la + +lib_LTLIBRARIES += \ + libs/ruamoko/libQFruamoko.la \ + libs/ruamoko/libQFruamoko_client.la + +libs_ruamoko_libQFruamoko_la_LDFLAGS= $(lib_ldflags) +libs_ruamoko_libQFruamoko_la_LIBADD= $(ruamoko_rua_libs) +libs_ruamoko_libQFruamoko_la_DEPENDENCIES= $(ruamoko_rua_libs) +libs_ruamoko_libQFruamoko_la_SOURCES= \ + libs/ruamoko/pr_cmds.c \ + libs/ruamoko/rua_cbuf.c \ + libs/ruamoko/rua_cmd.c \ + libs/ruamoko/rua_cvar.c \ + libs/ruamoko/rua_hash.c \ + libs/ruamoko/rua_init.c \ + libs/ruamoko/rua_math.c \ + libs/ruamoko/rua_msgbuf.c \ + libs/ruamoko/rua_obj.c \ + libs/ruamoko/rua_plist.c \ + libs/ruamoko/rua_qfile.c \ + libs/ruamoko/rua_qfs.c \ + libs/ruamoko/rua_runtime.c \ + libs/ruamoko/rua_script.c \ + libs/ruamoko/rua_set.c \ + libs/ruamoko/rua_stdlib.c \ + libs/ruamoko/rua_string.c + +libs_ruamoko_libQFruamoko_client_la_LDFLAGS= $(lib_ldflags) +libs_ruamoko_libQFruamoko_client_la_LIBADD= \ + libs/ruamoko/libQFruamoko.la \ + libs/ui/libQFgui.la +libs_ruamoko_libQFruamoko_client_la_DEPENDENCIES= \ + libs/ruamoko/libQFruamoko.la \ + libs/ui/libQFgui.la \ + $(ruamoko_rua_libs) +libs_ruamoko_libQFruamoko_client_la_SOURCES= \ + libs/ruamoko/rua_game_init.c \ + libs/ruamoko/rua_gui.c \ + libs/ruamoko/rua_input.c \ + libs/ruamoko/rua_mersenne.c \ + libs/ruamoko/rua_model.c \ + libs/ruamoko/rua_scene.c diff --git a/libs/ruamoko/pr_cmds.c b/libs/ruamoko/pr_cmds.c index 8352228d4..63fc6c1e3 100644 --- a/libs/ruamoko/pr_cmds.c +++ b/libs/ruamoko/pr_cmds.c @@ -54,17 +54,18 @@ VISIBLE const char *pr_gametype = ""; /* BUILT-IN FUNCTIONS */ VISIBLE char * -PF_VarString (progs_t *pr, int first) +PF_VarString (progs_t *pr, int first, int argc) { char *out, *dst; const char *src; int len, i; + pr_type_t **argv = pr->pr_params; - for (len = 0, i = first; i < pr->pr_argc; i++) - len += strlen (P_GSTRING (pr, i)); - dst = out = Hunk_TempAlloc (len + 1); - for (i = first; i < pr->pr_argc; i++) { - src = P_GSTRING (pr, i); + for (len = 0, i = first; i < argc; i++) + len += strlen (PR_GetString (pr, *(pr_string_t *) argv[i])); + dst = out = Hunk_TempAlloc (0, len + 1); + for (i = first; i < argc; i++) { + src = PR_GetString (pr, PR_PTR (string, argv[i])); while (*src) *dst++ = *src++; } @@ -76,7 +77,7 @@ PF_VarString (progs_t *pr, int first) vector (vector v) normalize */ static void -PF_normalize (progs_t *pr) +PF_normalize (progs_t *pr, void *data) { float new; float *value1; @@ -104,7 +105,7 @@ PF_normalize (progs_t *pr) float (vector v) vlen */ static void -PF_vlen (progs_t *pr) +PF_vlen (progs_t *pr, void *data) { float new; float *value1; @@ -122,7 +123,7 @@ PF_vlen (progs_t *pr) float (vector v) vectoyaw */ static void -PF_vectoyaw (progs_t *pr) +PF_vectoyaw (progs_t *pr, void *data) { float yaw; float *value1; @@ -144,7 +145,7 @@ PF_vectoyaw (progs_t *pr) vector (vector v) vectoangles */ static void -PF_vectoangles (progs_t *pr) +PF_vectoangles (progs_t *pr, void *data) { float forward, pitch, yaw; float *value1; @@ -179,7 +180,7 @@ PF_vectoangles (progs_t *pr) Returns a number from 0<= num < 1 */ static void -PF_random (progs_t *pr) +PF_random (progs_t *pr, void *data) { float num; @@ -192,7 +193,7 @@ PF_random (progs_t *pr) void () break */ static void -PF_break (progs_t *pr) +PF_break (progs_t *pr, void *data) { Sys_Printf ("break statement\n"); PR_DumpState (pr); @@ -202,20 +203,20 @@ PF_break (progs_t *pr) float (string s) cvar */ static void -PF_cvar (progs_t *pr) +PF_cvar (progs_t *pr, void *data) { const char *str; str = P_GSTRING (pr, 0); - R_FLOAT (pr) = Cvar_VariableValue (str); + R_FLOAT (pr) = Cvar_Value (str); } /* void (string var, string val) cvar_set */ static void -PF_cvar_set (progs_t *pr) +PF_cvar_set (progs_t *pr, void *data) { const char *var_name, *val; cvar_t *var; @@ -230,14 +231,14 @@ PF_cvar_set (progs_t *pr) return; } - Cvar_Set (var, val); + Cvar_SetVar (var, val); } /* float (float f) fabs */ static void -PF_fabs (progs_t *pr) +PF_fabs (progs_t *pr, void *data) { float v; @@ -249,13 +250,13 @@ PF_fabs (progs_t *pr) entity (entity start, .(...) fld, ... match) find */ static void -PF_Find (progs_t *pr) +PF_find (progs_t *pr, void *data) { const char *s = 0, *t; // ev_string int i; // ev_vector - int e, f; + pr_uint_t e, f; etype_t type; - ddef_t *field_def; + pr_def_t *field_def; edict_t *ed; e = P_EDICTNUM (pr, 0); @@ -295,7 +296,7 @@ PF_Find (progs_t *pr) continue; RETURN_EDICT (pr, ed); return; - case ev_integer: + case ev_int: case ev_entity: if (P_INT (pr, 2) != E_INT (ed, f)) continue; @@ -306,14 +307,14 @@ PF_Find (progs_t *pr) } } - RETURN_EDICT (pr, *pr->edicts); + RETURN_EDICT (pr, EDICT_NUM (pr, 0)); } /* void () coredump */ static void -PF_coredump (progs_t *pr) +PF_coredump (progs_t *pr, void *data) { ED_PrintEdicts (pr, ""); } @@ -322,7 +323,7 @@ PF_coredump (progs_t *pr) void () traceon */ static void -PF_traceon (progs_t *pr) +PF_traceon (progs_t *pr, void *data) { pr->pr_trace = true; pr->pr_trace_depth = pr->pr_depth; @@ -332,7 +333,7 @@ PF_traceon (progs_t *pr) void () traceoff */ static void -PF_traceoff (progs_t *pr) +PF_traceoff (progs_t *pr, void *data) { pr->pr_trace = false; } @@ -341,25 +342,25 @@ PF_traceoff (progs_t *pr) void (entity e) eprint */ static void -PF_eprint (progs_t *pr) +PF_eprint (progs_t *pr, void *data) { - ED_PrintNum (pr, P_EDICTNUM (pr, 0)); + ED_PrintNum (pr, P_EDICTNUM (pr, 0), 0); } /* void (string s) dprint */ static void -PF_dprint (progs_t *pr) +PF_dprint (progs_t *pr, void *data) { - Sys_Printf ("%s", PF_VarString (pr, 0)); + Sys_Printf ("%s", PF_VarString (pr, 0, 1)); } /* float (float v) rint */ static void -PF_rint (progs_t *pr) +PF_rint (progs_t *pr, void *data) { float f; @@ -374,7 +375,7 @@ PF_rint (progs_t *pr) float (float v) floor */ static void -PF_floor (progs_t *pr) +PF_floor (progs_t *pr, void *data) { R_FLOAT (pr) = floor (P_FLOAT (pr, 0)); } @@ -383,7 +384,7 @@ PF_floor (progs_t *pr) float (float v) ceil */ static void -PF_ceil (progs_t *pr) +PF_ceil (progs_t *pr, void *data) { R_FLOAT (pr) = ceil (P_FLOAT (pr, 0)); } @@ -392,16 +393,16 @@ PF_ceil (progs_t *pr) entity (entity e) nextent */ static void -PF_nextent (progs_t *pr) +PF_nextent (progs_t *pr, void *data) { - int i; + pr_uint_t i; edict_t *ent; i = P_EDICTNUM (pr, 0); while (1) { i++; if (i == *pr->num_edicts) { - RETURN_EDICT (pr, *pr->edicts); + RETURN_EDICT (pr, EDICT_NUM (pr, 0)); return; } ent = EDICT_NUM (pr, i); @@ -420,10 +421,10 @@ PF_nextent (progs_t *pr) #endif /* - integer (float f) ftoi + int (float f) ftoi */ static void -PF_ftoi (progs_t *pr) +PF_ftoi (progs_t *pr, void *data) { R_INT (pr) = P_FLOAT (pr, 0); } @@ -432,7 +433,7 @@ PF_ftoi (progs_t *pr) string (float f) ftos */ static void -PF_ftos (progs_t *pr) +PF_ftos (progs_t *pr, void *data) { char string[STRING_BUF]; int i; @@ -453,19 +454,19 @@ PF_ftos (progs_t *pr) } /* - float (integer i) itof + float (int i) itof */ static void -PF_itof (progs_t *pr) +PF_itof (progs_t *pr, void *data) { R_FLOAT (pr) = P_INT (pr, 0); } /* - string (integer i) itos + string (int i) itos */ static void -PF_itos (progs_t *pr) +PF_itos (progs_t *pr, void *data) { char string[STRING_BUF]; @@ -478,16 +479,16 @@ PF_itos (progs_t *pr) float (string s) stof */ static void -PF_stof (progs_t *pr) +PF_stof (progs_t *pr, void *data) { R_FLOAT (pr) = atof (P_GSTRING (pr, 0)); } /* - integer (string s) stoi + int (string s) stoi */ static void -PF_stoi (progs_t *pr) +PF_stoi (progs_t *pr, void *data) { R_INT (pr) = atoi (P_GSTRING (pr, 0)); } @@ -496,7 +497,7 @@ PF_stoi (progs_t *pr) vector (string s) stov */ static void -PF_stov (progs_t *pr) +PF_stov (progs_t *pr, void *data) { float v[3] = {0, 0, 0}; @@ -509,7 +510,7 @@ PF_stov (progs_t *pr) string (vector v) vtos */ static void -PF_vtos (progs_t *pr) +PF_vtos (progs_t *pr, void *data) { char string[STRING_BUF * 3 + 5]; @@ -521,23 +522,11 @@ PF_vtos (progs_t *pr) RETURN_STRING (pr, string); } -/* - float (string s) strlen -*/ -static void -PF_strlen (progs_t *pr) -{ - const char *s; - - s = P_GSTRING (pr, 0); - R_FLOAT (pr) = strlen(s); -} - /* float (string char, string s) charcount */ static void -PF_charcount (progs_t *pr) +PF_charcount (progs_t *pr, void *data) { char goal; const char *s; @@ -565,47 +554,29 @@ PF_charcount (progs_t *pr) # define INT_WIDTH 20 #endif -#define MAX_ARG 7 -/* - string (...) sprintf -*/ -static void -PF_sprintf (progs_t *pr) -{ - const char *fmt = P_GSTRING (pr, 0); - int count = pr->pr_argc - 1; - pr_type_t **args = pr->pr_params + 1; - dstring_t *dstr; - - dstr = dstring_newstr (); - PR_Sprintf (pr, dstr, "PF_sprintf", fmt, count, args); - RETURN_STRING (pr, dstr->str); - dstring_delete (dstr); -} - /* string () gametype */ static void -PR_gametype (progs_t *pr) +PF_gametype (progs_t *pr, void *data) { RETURN_STRING (pr, pr_gametype); } static void -PF_PR_SetField (progs_t *pr) +PF_PR_SetField (progs_t *pr, void *data) { edict_t *ent = P_EDICT (pr, 0); - ddef_t *field = PR_FindField (pr, P_GSTRING (pr, 1)); + pr_def_t *field = PR_FindField (pr, P_GSTRING (pr, 1)); const char *value = P_GSTRING (pr, 2); R_INT (pr) = 0; if (field) - R_INT (pr) = ED_ParseEpair (pr, ent->v, field, value); + R_INT (pr) = ED_ParseEpair (pr, &E_fld (ent, 0), field, value); } static void -PF_PR_FindFunction (progs_t *pr) +PF_PR_FindFunction (progs_t *pr, void *data) { dfunction_t *func = PR_FindFunction (pr, P_GSTRING (pr, 0)); R_FUNCTION (pr) = 0; @@ -615,48 +586,48 @@ PF_PR_FindFunction (progs_t *pr) #define QF (PR_RANGE_QF << PR_RANGE_SHIFT) | +#define bi(x,n,np,params...) {#x, PF_##x, n, np, {params}} +#define p(type) PR_PARAM(type) static builtin_t builtins[] = { - {"break", PF_break, 6}, - {"random", PF_random, 7}, - {"normalize", PF_normalize, 9}, - {"vlen", PF_vlen, 12}, - {"vectoyaw", PF_vectoyaw, 13}, - {"find", PF_Find, 18}, - {"dprint", PF_dprint, 25}, - {"ftos", PF_ftos, 26}, - {"vtos", PF_vtos, 27}, - {"coredump", PF_coredump, 28}, - {"traceon", PF_traceon, 29}, - {"traceoff", PF_traceoff, 30}, - {"eprint", PF_eprint, 31}, - {"rint", PF_rint, 36}, - {"floor", PF_floor, 37}, - {"ceil", PF_ceil, 38}, - {"fabs", PF_fabs, 43}, - {"cvar", PF_cvar, 45}, - {"nextent", PF_nextent, 47}, - {"vectoangles", PF_vectoangles, 51}, - {"cvar_set", PF_cvar_set, 72}, - {"stof", PF_stof, 81}, + bi(break, 6, 0), + bi(random, 7, 0), + bi(normalize, 9, 1, p(vector)), + bi(vlen, 12, 1, p(vector)), + bi(vectoyaw, 13, 1, p(vector)), + bi(find, 18, -3, p(entity), p(field)), + bi(dprint, 25, -1), + bi(ftos, 26, 1, p(float)), + bi(vtos, 27, 1, p(vector)), + bi(coredump, 28, 0), + bi(traceon, 29, 0), + bi(traceoff, 30, 0), + bi(eprint, 31, 1, p(entity)), + bi(rint, 36, 1, p(float)), + bi(floor, 37, 1, p(float)), + bi(ceil, 38, 1, p(float)), + bi(fabs, 43, 1, p(float)), + bi(cvar, 45, 1, p(string)), + bi(nextent, 47, 1, p(entity)), + bi(vectoangles, 51, 1, p(vector)), + bi(cvar_set, 72, 2, p(string), p(string)), + bi(stof, 81, 1, p(string)), - {"strlen", PF_strlen, QF 100}, - {"charcount", PF_charcount, QF 101}, - {"sprintf", PF_sprintf, QF 109}, - {"ftoi", PF_ftoi, QF 110}, - {"itof", PF_itof, QF 111}, - {"itos", PF_itos, QF 112}, - {"stoi", PF_stoi, QF 113}, - {"stov", PF_stov, QF 114}, - {"gametype", PR_gametype, QF 115}, + bi(charcount, QF 101, 2, p(string), p(string)), + bi(ftoi, QF 110, 1, p(float)), + bi(itof, QF 111, 1, p(int)), + bi(itos, QF 112, 1, p(int)), + bi(stoi, QF 113, 1, p(string)), + bi(stov, QF 114, 1, p(string)), + bi(gametype, QF 115, 0), - {"PR_SetField", PF_PR_SetField, -1}, - {"PR_FindFunction", PF_PR_FindFunction, -1}, + bi(PR_SetField, -1, 3, p(entity), p(string), p(string)), + bi(PR_FindFunction, -1, 1, p(string)), {0} }; VISIBLE void PR_Cmds_Init (progs_t *pr) { - PR_RegisterBuiltins (pr, builtins); + PR_RegisterBuiltins (pr, builtins, 0); } diff --git a/libs/ruamoko/rua_cbuf.c b/libs/ruamoko/rua_cbuf.c index 5d1bbc095..91ad02405 100644 --- a/libs/ruamoko/rua_cbuf.c +++ b/libs/ruamoko/rua_cbuf.c @@ -38,53 +38,53 @@ #include "rua_internal.h" typedef struct { - cbuf_t *cbuf; + progs_t *pr; + cbuf_t *default_cbuf; } cbuf_resources_t; -static cbuf_t * -get_cbuf (progs_t *pr, int arg, const char *func) +static cbuf_t * __attribute__((pure)) +_get_cbuf (progs_t *pr, cbuf_resources_t *res, int arg, const char *func) { cbuf_t *cbuf = 0; if (arg == 0) { - cbuf_resources_t *res = PR_Resources_Find (pr, "Cbuf"); - cbuf = res->cbuf; + // a nil cbuf is valid only if the default cbuf has been set + cbuf = res->default_cbuf; } else { - PR_RunError (pr, "%s: Invalid cbuf_t", func); } - if (!cbuf) - PR_RunError (pr, "Invalid cbuf_t"); + if (!cbuf) { + PR_RunError (pr, "%s: Invalid cbuf_t: %d", func, arg); + } return cbuf; } +#define get_cbuf(pr, res, arg) _get_cbuf(pr, res, arg, __FUNCTION__) -static void -bi_Cbuf_AddText (progs_t *pr) +#define bi(n) static void bi_##n (progs_t *pr, void *data) + +bi(Cbuf_AddText) { - const char *text = P_GSTRING (pr, 0); - cbuf_t *cbuf = get_cbuf (pr, 0, __FUNCTION__); + cbuf_t *cbuf = get_cbuf (pr, data, P_INT (pr, 0)); + const char *text = P_GSTRING (pr, 1); Cbuf_AddText (cbuf, text); } -static void -bi_Cbuf_InsertText (progs_t *pr) +bi(Cbuf_InsertText) { - const char *text = P_GSTRING (pr, 0); - cbuf_t *cbuf = get_cbuf (pr, 0, __FUNCTION__); + cbuf_t *cbuf = get_cbuf (pr, data, P_INT (pr, 0)); + const char *text = P_GSTRING (pr, 1); Cbuf_InsertText (cbuf, text); } -static void -bi_Cbuf_Execute (progs_t *pr) +bi(Cbuf_Execute) { - cbuf_t *cbuf = get_cbuf (pr, 0, __FUNCTION__); + cbuf_t *cbuf = get_cbuf (pr, data, P_INT (pr, 0)); Cbuf_Execute (cbuf); } -static void -bi_Cbuf_Execute_Sets (progs_t *pr) +bi(Cbuf_Execute_Sets) { - cbuf_t *cbuf = get_cbuf (pr, 0, __FUNCTION__); + cbuf_t *cbuf = get_cbuf (pr, data, P_INT (pr, 0)); Cbuf_Execute_Sets (cbuf); } @@ -93,11 +93,21 @@ bi_cbuf_clear (progs_t *pr, void *data) { } +static void +bi_cbuf_destroy (progs_t *pr, void *data) +{ + free (data); +} + +#undef bi +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) +#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } static builtin_t builtins[] = { - {"Cbuf_AddText", bi_Cbuf_AddText, -1}, - {"Cbuf_InsertText", bi_Cbuf_InsertText, -1}, - {"Cbuf_Execute", bi_Cbuf_Execute, -1}, - {"Cbuf_Execute_Sets", bi_Cbuf_Execute_Sets, -1}, + bi(Cbuf_AddText, 2, p(ptr), p(string)), + bi(Cbuf_InsertText, 2, p(ptr), p(string)), + bi(Cbuf_Execute, 1, p(ptr)), + bi(Cbuf_Execute_Sets, 1, p(ptr)), {0} }; @@ -105,13 +115,14 @@ void RUA_Cbuf_Init (progs_t *pr, int secure) { cbuf_resources_t *res = calloc (sizeof (cbuf_resources_t), 1); - PR_Resources_Register (pr, "Cbuf", res, bi_cbuf_clear); - PR_RegisterBuiltins (pr, builtins); + res->pr = pr; + PR_Resources_Register (pr, "Cbuf", res, bi_cbuf_clear, bi_cbuf_destroy); + PR_RegisterBuiltins (pr, builtins, res); } VISIBLE void RUA_Cbuf_SetCbuf (progs_t *pr, cbuf_t *cbuf) { cbuf_resources_t *res = PR_Resources_Find (pr, "Cbuf"); - res->cbuf = cbuf; + res->default_cbuf = cbuf; } diff --git a/libs/ruamoko/rua_cmd.c b/libs/ruamoko/rua_cmd.c index a18e2805e..c147b6990 100644 --- a/libs/ruamoko/rua_cmd.c +++ b/libs/ruamoko/rua_cmd.c @@ -50,7 +50,7 @@ typedef struct bi_cmd_s { struct bi_cmd_s *next; char *name; progs_t *pr; - func_t func; + pr_func_t func; } bi_cmd_t; typedef struct { @@ -58,6 +58,8 @@ typedef struct { } cmd_resources_t; static hashtab_t *bi_cmds; +static hashctx_t *bi_cmd_hashctx; +static int bi_cmds_refs; static const char * bi_cmd_get_key (const void *c, void *unused) @@ -85,12 +87,12 @@ bi_cmd_f (void) } static void -bi_Cmd_AddCommand (progs_t *pr) +bi_Cmd_AddCommand (progs_t *pr, void *_res) { - cmd_resources_t *res = PR_Resources_Find (pr, "Cmd"); + __auto_type res = (cmd_resources_t *) _res; bi_cmd_t *cmd = malloc (sizeof (bi_cmd_t)); char *name = strdup (P_GSTRING (pr, 0)); - func_t func = P_FUNCTION (pr, 1); + pr_func_t func = P_FUNCTION (pr, 1); if (!cmd || !name || !Cmd_AddCommand (name, bi_cmd_f, "CSQC command")) { if (name) @@ -124,19 +126,29 @@ bi_cmd_clear (progs_t *pr, void *data) } static void -bi_Cmd_Argc (progs_t *pr) +bi_cmd_destroy (progs_t *pr, void *data) +{ + if (!--bi_cmds_refs) { + Hash_DelTable (bi_cmds); + Hash_DelContext (bi_cmd_hashctx); + } + free (data); +} + +static void +bi_Cmd_Argc (progs_t *pr, void *data) { R_INT (pr) = Cmd_Argc (); } static void -bi_Cmd_Argv (progs_t *pr) +bi_Cmd_Argv (progs_t *pr, void *data) { RETURN_STRING (pr, Cmd_Argv (P_INT (pr, 0))); } static void -bi_Cmd_Args (progs_t *pr) +bi_Cmd_Args (progs_t *pr, void *data) { RETURN_STRING (pr, Cmd_Args (P_INT (pr, 0))); } @@ -145,11 +157,13 @@ bi_Cmd_Args (progs_t *pr) //Cmd_ExecuteString //Cmd_ForwardToServer +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) static builtin_t builtins[] = { - {"Cmd_AddCommand", bi_Cmd_AddCommand, -1}, - {"Cmd_Argc", bi_Cmd_Argc, -1}, - {"Cmd_Argv", bi_Cmd_Argv, -1}, - {"Cmd_Args", bi_Cmd_Args, -1}, + bi(Cmd_AddCommand, 2, p(string), p(func)), + bi(Cmd_Argc, 0), + bi(Cmd_Argv, 1, p(int)), + bi(Cmd_Args, 1, p(int)), {0} }; @@ -159,10 +173,14 @@ RUA_Cmd_Init (progs_t *pr, int secure) cmd_resources_t *res = calloc (1, sizeof (cmd_resources_t)); res->cmds = 0; - PR_Resources_Register (pr, "Cmd", res, bi_cmd_clear); - if (!bi_cmds) - bi_cmds = Hash_NewTable (1021, bi_cmd_get_key, bi_cmd_free, 0); + if (!bi_cmds) { + //FIXME not thread-safe + bi_cmds = Hash_NewTable (1021, bi_cmd_get_key, bi_cmd_free, 0, + &bi_cmd_hashctx); + } + bi_cmds_refs++; - PR_RegisterBuiltins (pr, builtins); + PR_Resources_Register (pr, "Cmd", res, bi_cmd_clear, bi_cmd_destroy); + PR_RegisterBuiltins (pr, builtins, res); } diff --git a/libs/ruamoko/rua_cvar.c b/libs/ruamoko/rua_cvar.c index 18275988e..79ebffda2 100644 --- a/libs/ruamoko/rua_cvar.c +++ b/libs/ruamoko/rua_cvar.c @@ -41,6 +41,8 @@ #include "QF/progs.h" #include "QF/va.h" +#include "QF/simd/types.h" + #include "rua_internal.h" typedef struct bi_alias_s { @@ -62,9 +64,9 @@ bi_alias_free (void *_a, void *unused) } static void -bi_cvar_clear (progs_t *pr, void *data) +bi_cvar_clear (progs_t *pr, void *_res) { - cvar_resources_t *res = (cvar_resources_t *) data; + cvar_resources_t *res = (cvar_resources_t *) _res; bi_alias_t *alias; while ((alias = res->aliases)) { @@ -75,9 +77,15 @@ bi_cvar_clear (progs_t *pr, void *data) } static void -bi_Cvar_MakeAlias (progs_t *pr) +bi_cvar_destroy (progs_t *pr, void *_res) { - cvar_resources_t *res = PR_Resources_Find (pr, "Cvar"); + free (_res); +} + +static void +bi_Cvar_MakeAlias (progs_t *pr, void *_res) +{ + __auto_type res = (cvar_resources_t *) _res; const char *alias_name = P_GSTRING (pr, 0); const char *cvar_name = P_GSTRING (pr, 1); cvar_t *cvar = Cvar_FindVar (cvar_name); @@ -99,9 +107,9 @@ bi_Cvar_MakeAlias (progs_t *pr) } static void -bi_Cvar_RemoveAlias (progs_t *pr) +bi_Cvar_RemoveAlias (progs_t *pr, void *_res) { - cvar_resources_t *res = PR_Resources_Find (pr, "Cvar"); + __auto_type res = (cvar_resources_t *) _res; const char *alias_name = P_GSTRING (pr, 0); bi_alias_t **a; @@ -118,7 +126,7 @@ bi_Cvar_RemoveAlias (progs_t *pr) } static void -bi_Cvar_SetString (progs_t *pr) +bi_Cvar_SetString (progs_t *pr, void *_res) { const char *varname = P_GSTRING (pr, 0); const char *val = P_GSTRING (pr, 1); @@ -127,11 +135,11 @@ bi_Cvar_SetString (progs_t *pr) if (!var) var = Cvar_FindAlias (varname); if (var) - Cvar_Set (var, val); + Cvar_SetVar (var, val); } static void -bi_Cvar_SetInteger (progs_t *pr) +bi_Cvar_SetInteger (progs_t *pr, void *_res) { const char *varname = P_GSTRING (pr, 0); int val = P_INT (pr, 1); @@ -140,11 +148,11 @@ bi_Cvar_SetInteger (progs_t *pr) if (!var) var = Cvar_FindAlias (varname); if (var) - Cvar_Set (var, va ("%d", val)); + Cvar_SetVar (var, va (0, "%d", val)); } static void -bi_Cvar_SetFloat (progs_t *pr) +bi_Cvar_SetFloat (progs_t *pr, void *_res) { const char *varname = P_GSTRING (pr, 0); float val = P_FLOAT (pr, 1); @@ -153,11 +161,11 @@ bi_Cvar_SetFloat (progs_t *pr) if (!var) var = Cvar_FindAlias (varname); if (var) - Cvar_Set (var, va ("%g", val)); + Cvar_SetVar (var, va (0, "%g", val)); } static void -bi_Cvar_SetVector (progs_t *pr) +bi_Cvar_SetVector (progs_t *pr, void *_res) { const char *varname = P_GSTRING (pr, 0); float *val = P_VECTOR (pr, 1); @@ -166,11 +174,11 @@ bi_Cvar_SetVector (progs_t *pr) if (!var) var = Cvar_FindAlias (varname); if (var) - Cvar_Set (var, va ("%g %g %g", val[0], val[1], val[2])); + Cvar_SetVar (var, va (0, "%g %g %g", val[0], val[1], val[2])); } static void -bi_Cvar_GetString (progs_t *pr) +bi_Cvar_GetString (progs_t *pr, void *_res) { const char *varname = P_GSTRING (pr, 0); cvar_t *var = Cvar_FindVar (varname); @@ -178,49 +186,56 @@ bi_Cvar_GetString (progs_t *pr) if (!var) var = Cvar_FindAlias (varname); if (var) - RETURN_STRING (pr, var->string); + RETURN_STRING (pr, Cvar_VarString (var)); else RETURN_STRING (pr, ""); } static void -bi_Cvar_GetInteger (progs_t *pr) +bi_Cvar_GetInteger (progs_t *pr, void *_res) { const char *varname = P_GSTRING (pr, 0); cvar_t *var = Cvar_FindVar (varname); + R_INT (pr) = 0; if (!var) var = Cvar_FindAlias (varname); - R_INT (pr) = var ? var->int_val : 0; + if (!var || var->value.type != &cexpr_int) + return; + + R_INT (pr) = *(int *) var->value.value; } static void -bi_Cvar_GetFloat (progs_t *pr) +bi_Cvar_GetFloat (progs_t *pr, void *_res) { const char *varname = P_GSTRING (pr, 0); cvar_t *var = Cvar_FindVar (varname); + R_FLOAT (pr) = 0; if (!var) var = Cvar_FindAlias (varname); - R_FLOAT (pr) = var ? var->value : 0; + if (!var || var->value.type != &cexpr_float) + return; + R_INT (pr) = *(float *) var->value.value; } static void -bi_Cvar_GetVector (progs_t *pr) +bi_Cvar_GetVector (progs_t *pr, void *_res) { const char *varname = P_GSTRING (pr, 0); cvar_t *var = Cvar_FindVar (varname); if (!var) var = Cvar_FindAlias (varname); - if (var) - RETURN_VECTOR (pr, var->vec); + if (var && var->value.type == &cexpr_vector) + RETURN_VECTOR (pr, *(vec4f_t *) var->value.value); else VectorZero (R_VECTOR (pr)); } static void -bi_Cvar_Toggle (progs_t *pr) +bi_Cvar_Toggle (progs_t *pr, void *_res) { const char *varname = P_GSTRING (pr, 0); cvar_t *var; @@ -228,22 +243,26 @@ bi_Cvar_Toggle (progs_t *pr) var = Cvar_FindVar (varname); if (!var) var = Cvar_FindAlias (varname); - if (var) - Cvar_Set (var, var->int_val ? "0" : "1"); + if (var && var->value.type == &cexpr_int) { + *(int *) var->value.value = !*(int *) var->value.value; + } } +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) +#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } static builtin_t builtins[] = { - {"Cvar_MakeAlias", bi_Cvar_MakeAlias, -1}, - {"Cvar_RemoveAlias", bi_Cvar_RemoveAlias, -1}, - {"Cvar_SetFloat", bi_Cvar_SetFloat, -1}, - {"Cvar_SetInteger", bi_Cvar_SetInteger, -1}, - {"Cvar_SetVector", bi_Cvar_SetVector, -1}, - {"Cvar_SetString", bi_Cvar_SetString, -1}, - {"Cvar_GetFloat", bi_Cvar_GetFloat, -1}, - {"Cvar_GetInteger", bi_Cvar_GetInteger, -1}, - {"Cvar_GetVector", bi_Cvar_GetVector, -1}, - {"Cvar_GetString", bi_Cvar_GetString, -1}, - {"Cvar_Toggle", bi_Cvar_Toggle, -1}, + bi(Cvar_MakeAlias, 2, p(string), p(string)), + bi(Cvar_RemoveAlias, 1, p(string)), + bi(Cvar_SetFloat, 2, p(string), p(float)), + bi(Cvar_SetInteger, 2, p(string), p(int)), + bi(Cvar_SetVector, 2, p(string), p(vector)), + bi(Cvar_SetString, 2, p(string), p(string)), + bi(Cvar_GetFloat, 1, p(string)), + bi(Cvar_GetInteger, 1, p(string)), + bi(Cvar_GetVector, 1, p(string)), + bi(Cvar_GetString, 1, p(string)), + bi(Cvar_Toggle, 1, p(string)), {0} }; @@ -253,7 +272,6 @@ RUA_Cvar_Init (progs_t *pr, int secure) cvar_resources_t *res = calloc (1, sizeof (cvar_resources_t)); res->aliases = 0; - PR_Resources_Register (pr, "Cvar", res, bi_cvar_clear); - - PR_RegisterBuiltins (pr, builtins); + PR_Resources_Register (pr, "Cvar", res, bi_cvar_clear, bi_cvar_destroy); + PR_RegisterBuiltins (pr, builtins, res); } diff --git a/libs/ruamoko/rua_file.c b/libs/ruamoko/rua_file.c deleted file mode 100644 index 95d21682c..000000000 --- a/libs/ruamoko/rua_file.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - bi_file.c - - CSQC file builtins - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif - -#ifdef HAVE_FNMATCH_H -# define model_t sunmodel_t -# include -# undef model_t -#else -# ifdef _WIN32 -# include "fnmatch.h" -# endif -#endif - -#ifndef HAVE_FNMATCH_PROTO -int fnmatch (const char *__pattern, const char *__string, int __flags); -#endif - -#include "QF/cvar.h" -#include "QF/progs.h" -#include "QF/quakefs.h" -#include "QF/va.h" -#include "QF/zone.h" - -#include "rua_internal.h" - -static const char *file_ban_list[] = { - "default.cfg{,.gz}", - "demo1.dem{,.gz}", - "demo2.dem{,.gz}", - "demo3.dem{,.gz}", - "end1.bin{,.gz}", - "end2.bin{,.gz}", - "gfx.wad{,.gz}", - "progs.dat{,.gz}", - "quake.rc{,.gz}", - 0, -}; - -static const char *dir_ban_list[] = { - "gfx", - "maps", - "progs", - "skins", - "sound", - 0, -}; - -static int -file_readable (char *path) -{ - char t; - char *p = strchr (path, '/'); - const char **match; - - if (p) { - t = *p; - *p = 0; - for (match = dir_ban_list; *match; match++) { - if (fnmatch (*match, path, FNM_PATHNAME) == 0) { - *p = t; - return 0; - } - } - } else { - for (match = file_ban_list; *match; match++) { - if (fnmatch (*match, path, FNM_PATHNAME) == 0) { - return 0; - } - } - } - return 1; -} - -static int -file_writeable (char *path) -{ - return file_readable (path); -} - -static void -bi_File_Open (progs_t *pr) -{ - QFile *file; - const char *pth = P_GSTRING (pr, 0); - const char *mode = P_GSTRING (pr, 1); - char *path; - char *p; - int do_write = 0; - int do_read = 0; - - p = strchr (mode, 'r'); - if (p) { - do_read |= 1; - if (p[1] == '+') - do_write |= 1; - } - - p = strchr (mode, 'w'); - if (p) { - do_write |= 1; - if (p[1] == '+') - do_read |= 1; - } - - p = strchr (mode, 'a'); - if (p) { - do_write |= 1; - if (p[1] == '+') - do_read |= 1; - } - - path = QFS_CompressPath (pth); - //printf ("'%s' '%s'\n", P_GSTRING (pr, 0), path); - if (!path[0]) - goto error; - if (path[0] == '.' && path[1] == '.' && (path[2] == '/' || path [2] == 0)) - goto error; - if (path[strlen (path) - 1] =='/') - goto error; - if (!do_read && !do_write) - goto error; - if (do_read && !file_readable (path)) - goto error; - if (do_write && !file_writeable (path)) - goto error; - - file = QFS_Open (va ("%s/%s", qfs_gamedir->dir.def, path), mode); - if (!file) - goto error; - free (path); - if ((R_INT (pr) = QFile_AllocHandle (pr, file))) - return; - Qclose (file); -error: - free (path); - R_INT (pr) = 0; -} - -static builtin_t builtins[] = { - {"File_Open", bi_File_Open, -1}, - {0} -}; - -void -RUA_File_Init (progs_t *pr, int secure) -{ - PR_RegisterBuiltins (pr, builtins); -} diff --git a/include/QF/uint32.h b/libs/ruamoko/rua_game_init.c similarity index 57% rename from include/QF/uint32.h rename to libs/ruamoko/rua_game_init.c index 513117d01..81b2dde72 100644 --- a/include/QF/uint32.h +++ b/libs/ruamoko/rua_game_init.c @@ -1,12 +1,9 @@ /* - uint32.h + bi_game_init.c - Definitions for portable (?) unsigned int + CSQC builtins init - Copyright (C) 2000 Jeff Teunissen - - Author: Jeff Teunissen - Date: 01 Jan 2000 + Copyright (C) 2021 Bill Currie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -27,26 +24,28 @@ Boston, MA 02111-1307, USA */ - -#ifndef __uint32_h -#define __uint32_h - -#ifndef int32 -# if (SIZEOF_INT == 4) -# define int32 int -# elif (SIZEOF_LONG == 4) -# define int32 long -# elif (SIZEOF_SHORT == 4) -# define int32 short -# else -/* I hope this works */ -# define int32 int -# define LARGE_INT32 -# endif +#ifdef HAVE_CONFIG_H +# include "config.h" #endif -#ifndef uint32 -# define uint32 unsigned int32 -#endif +#include "QF/progs.h" +#include "QF/ruamoko.h" -#endif // __uint32_h +#include "rua_internal.h" + +static void (*init_funcs[])(progs_t *, int) = { + RUA_GUI_Init, + RUA_Input_Init, + RUA_Mersenne_Init, + RUA_Model_Init, + RUA_Scene_Init, +}; + +VISIBLE void +RUA_Game_Init (progs_t *pr, int secure) +{ + size_t i; + + for (i = 0; i < sizeof (init_funcs) / sizeof (init_funcs[0]); i++) + init_funcs[i] (pr, secure); +} diff --git a/libs/ruamoko/rua_gui.c b/libs/ruamoko/rua_gui.c new file mode 100644 index 000000000..b3d222802 --- /dev/null +++ b/libs/ruamoko/rua_gui.c @@ -0,0 +1,494 @@ +/* + r_gui.c + + Ruamoko GUI support functions + + Copyright (C) 2022 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include + +#include "QF/draw.h" +#include "QF/progs.h" +#include "QF/quakefs.h" +#include "QF/render.h" + +#include "QF/ui/font.h" +#include "QF/ui/passage.h" +#include "QF/ui/text.h" +#include "QF/ui/view.h" + +#include "QF/plugin/vid_render.h" + +#include "rua_internal.h" + +typedef struct rua_passage_s { + struct rua_passage_s *next; + struct rua_passage_s **prev; + passage_t *passage; +} rua_passage_t; + +typedef struct rua_font_s { + struct rua_font_s *next; + struct rua_font_s **prev; + font_t *font; +} rua_font_t; + +typedef struct { + progs_t *pr; + PR_RESMAP (rua_passage_t) passage_map; + rua_passage_t *passages; + PR_RESMAP (rua_font_t) font_map; + rua_font_t *fonts; + + ecs_registry_t *reg; + uint32_t text_base; + uint32_t view_base; + uint32_t passage_base; +} gui_resources_t; + +static rua_passage_t * +passage_new (gui_resources_t *res) +{ + return PR_RESNEW (res->passage_map); +} + +static void +passage_free (gui_resources_t *res, rua_passage_t *passage) +{ + if (passage->next) { + passage->next->prev = passage->prev; + } + *passage->prev = passage->next; + PR_RESFREE (res->passage_map, passage); +} + +static void +passage_reset (gui_resources_t *res) +{ + PR_RESRESET (res->passage_map); +} + +static inline rua_passage_t * +passage_get (gui_resources_t *res, int index) +{ + return PR_RESGET(res->passage_map, index); +} + +static inline int __attribute__((pure)) +passage_index (gui_resources_t *res, rua_passage_t *passage) +{ + return PR_RESINDEX(res->passage_map, passage); +} + +static int +alloc_passage (gui_resources_t *res, passage_t *passage) +{ + rua_passage_t *psg = passage_new (res); + + psg->next = res->passages; + psg->prev = &res->passages; + if (res->passages) { + res->passages->prev = &psg->next; + } + res->passages = psg; + psg->passage = passage; + return passage_index (res, psg); +} + +static rua_passage_t * __attribute__((pure)) +_get_passage (gui_resources_t *res, int handle, const char *func) +{ + rua_passage_t *psg = passage_get (res, handle); + if (!psg) { + PR_RunError (res->pr, "invalid passage handle passed to %s", func); + } + return psg; +} +#define get_passage(res, handle) _get_passage (res, handle, __FUNCTION__ + 3) + +static rua_font_t * +font_new (gui_resources_t *res) +{ + return PR_RESNEW (res->font_map); +} + +static void +font_free (gui_resources_t *res, rua_font_t *font) +{ + PR_RESFREE (res->font_map, font); +} + +static void +font_reset (gui_resources_t *res) +{ + PR_RESRESET (res->font_map); +} + +static inline rua_font_t * +font_get (gui_resources_t *res, int index) +{ + return PR_RESGET(res->font_map, index); +} + +static inline int __attribute__((pure)) +font_index (gui_resources_t *res, rua_font_t *font) +{ + return PR_RESINDEX(res->font_map, font); +} + +static int +alloc_font (gui_resources_t *res, font_t *font) +{ + rua_font_t *fnt = font_new (res); + + fnt->next = res->fonts; + fnt->prev = &res->fonts; + if (res->fonts) { + res->fonts->prev = &fnt->next; + } + res->fonts = fnt; + fnt->font = font; + return font_index (res, fnt); +} + +static rua_font_t * __attribute__((pure)) +_get_font (gui_resources_t *res, int handle, const char *func) +{ + rua_font_t *psg = font_get (res, handle); + if (!psg) { + PR_RunError (res->pr, "invalid font handle passed to %s", func); + } + return psg; +} +#define get_font(res, handle) _get_font (res, handle, __FUNCTION__ + 3) + +static void +bi_gui_clear (progs_t *pr, void *_res) +{ + gui_resources_t *res = _res; + + for (rua_passage_t *psg = res->passages; psg; psg = psg->next) { + Passage_Delete (psg->passage); + } + res->passages = 0; + passage_reset (res); + + rua_font_t *font; + for (font = res->fonts; font; font = font->next) { + //FIXME can't unload fonts + } + res->fonts = 0; + font_reset (res); +} + +static void +bi_gui_destroy (progs_t *pr, void *_res) +{ + gui_resources_t *res = _res; + ECS_DelRegistry (res->reg); + PR_RESDELMAP (res->passage_map); + PR_RESDELMAP (res->font_map); + free (res); +} + +#define bi(x) static void bi_##x (progs_t *pr, void *_res) + +bi (Font_Load) +{ + gui_resources_t *res = _res; + const char *font_path = P_GSTRING (pr, 0); + int font_size = P_INT (pr, 1); + + R_INT (pr) = 0; + + QFile *font_file = QFS_FOpenFile (font_path); + font_t *font = Font_Load (font_file, font_size); + if (font) { + R_INT (pr) = alloc_font (res, font); + } +} + +bi (Font_Free) +{ + gui_resources_t *res = _res; + rua_font_t *font = get_font (res, P_INT (pr, 0)); + + Font_Free (font->font); + font_free (res, font); +} + +bi (Passage_New) +{ + gui_resources_t *res = _res; + ecs_system_t passage_sys = { .reg = res->reg, .base = res->passage_base }; + passage_t *passage = Passage_New (passage_sys); + R_INT (pr) = alloc_passage (res, passage); +} + +bi (Passage_ParseText) +{ + gui_resources_t *res = _res; + int handle = P_INT (pr, 0); + rua_passage_t *psg = get_passage (res, handle); + const char *text = P_GSTRING (pr, 1); + Passage_ParseText (psg->passage, text); +} + +bi (Passage_Delete) +{ + gui_resources_t *res = _res; + int handle = P_INT (pr, 0); + rua_passage_t *psg = get_passage (res, handle); + Passage_Delete (psg->passage); + passage_free (res, psg); +} + +bi (Passage_ChildCount) +{ + gui_resources_t *res = _res; + rua_passage_t *psg = get_passage (res, P_INT (pr, 0)); + + uint32_t par = P_UINT (pr, 1); + R_UINT (pr) = psg->passage->hierarchy->childCount[par]; +} + +bi (Passage_GetChild) +{ + gui_resources_t *res = _res; + rua_passage_t *psg = get_passage (res, P_INT (pr, 0)); + + hierarchy_t *h = psg->passage->hierarchy; + uint32_t par = P_UINT (pr, 1); + uint32_t index = P_UINT (pr, 2); + R_UINT (pr) = h->ent[h->childIndex[par] + index]; +} + +bi (Text_View) +{ + gui_resources_t *res = _res; + rua_font_t *font = get_font (res, P_INT (pr, 0)); + rua_passage_t *psg = get_passage (res, P_INT (pr, 1)); + ecs_system_t viewsys = { .reg = res->reg, .base = res->view_base }; + view_t view = Text_View (viewsys, font->font, psg->passage); + R_INT (pr) = view.id;//FIXME +} + +bi (Text_SetScript) +{ + gui_resources_t *res = _res; + uint32_t textent = P_UINT (pr, 0); + const char *lang = P_GSTRING (pr, 1); + int script = P_INT (pr, 1); + int dir = P_INT (pr, 1); + ecs_system_t textsys = { .reg = res->reg, .base = res->text_base }; + Text_SetScript (textsys, textent, lang, script, dir); +} + +static void +draw_glyphs (view_pos_t *abs, glyphset_t *glyphset, glyphref_t *gref) +{ + uint32_t count = gref->count; + glyphobj_t *glyph = glyphset->glyphs + gref->start; + + while (count-- > 0) { + glyphobj_t *g = glyph++; + r_funcs->Draw_Glyph (abs->x + g->x, abs->y + g->y, + g->fontid, g->glyphid, 254); + } +} + +static void +draw_box (view_pos_t *abs, view_pos_t *len, uint32_t ind, int c) +{ + int x = abs[ind].x; + int y = abs[ind].y; + int w = len[ind].x; + int h = len[ind].y; + r_funcs->Draw_Line (x, y, x + w, y, c); + r_funcs->Draw_Line (x, y + h, x + w, y + h, c); + r_funcs->Draw_Line (x, y, x, y + h, c); + r_funcs->Draw_Line (x + w, y, x + w, y + h, c); +} + +bi (Text_Draw) +{ + gui_resources_t *res = _res; + uint32_t passage_glyphs = res->text_base + text_passage_glyphs; + uint32_t glyphs = res->text_base + text_glyphs; + uint32_t vhref = res->view_base + view_href; + ecs_pool_t *pool = &res->reg->comp_pools[passage_glyphs]; + uint32_t count = pool->count; + uint32_t *ent = pool->dense; + glyphset_t *glyphset = pool->data; + + while (count-- > 0) { + view_t psg_view = { .id = *ent++, .reg = res->reg, .comp = vhref}; + // first child is always a paragraph view, and all views after the + // first paragraph's first child are all text views + view_t para_view = View_GetChild (psg_view, 0); + view_t text_view = View_GetChild (para_view, 0); + hierref_t *href = View_GetRef (text_view); + glyphset_t *gs = glyphset++; + hierarchy_t *h = href->hierarchy; + view_pos_t *abs = h->components[view_abs]; + view_pos_t *len = h->components[view_len]; + + for (uint32_t i = href->index; i < h->num_objects; i++) { + glyphref_t *gref = Ent_GetComponent (h->ent[i], glyphs, res->reg); + draw_glyphs (&abs[i], gs, gref); + + if (0) draw_box (abs, len, i, 253); + } + if (0) { + for (uint32_t i = 1; i < href->index; i++) { + draw_box (abs, len, i, 251); + } + } + } +} + +bi (View_Delete) +{ + gui_resources_t *res = _res; + uint32_t viewid = P_UINT (pr, 0); + view_t view = { .id = viewid, .reg = res->reg, + .comp = res->view_base + view_href }; + View_Delete (view); +} + +bi (View_ChildCount) +{ + gui_resources_t *res = _res; + uint32_t viewid = P_UINT (pr, 0); + view_t view = { .id = viewid, .reg = res->reg, + .comp = res->view_base + view_href }; + R_UINT (pr) = View_ChildCount (view); +} + +bi (View_GetChild) +{ + gui_resources_t *res = _res; + uint32_t viewid = P_UINT (pr, 0); + uint32_t index = P_UINT (pr, 1); + view_t view = { .id = viewid, .reg = res->reg, + .comp = res->view_base + view_href }; + R_UINT (pr) = View_GetChild (view, index).id; +} + +bi (View_SetPos) +{ + gui_resources_t *res = _res; + uint32_t viewid = P_UINT (pr, 0); + int x = P_INT (pr, 1); + int y = P_INT (pr, 2); + view_t view = { .id = viewid, .reg = res->reg, + .comp = res->view_base + view_href }; + View_SetPos (view, x, y); +} + +bi (View_SetLen) +{ + gui_resources_t *res = _res; + uint32_t viewid = P_UINT (pr, 0); + int x = P_INT (pr, 1); + int y = P_INT (pr, 2); + view_t view = { .id = viewid, .reg = res->reg, + .comp = res->view_base + view_href }; + View_SetLen (view, x, y); +} + +bi (View_GetLen) +{ + gui_resources_t *res = _res; + uint32_t viewid = P_UINT (pr, 0); + view_t view = { .id = viewid, .reg = res->reg, + .comp = res->view_base + view_href }; + view_pos_t l = View_GetLen (view); + R_var (pr, ivec2) = (pr_ivec2_t) { l.x, l.y }; +} + +bi (View_UpdateHierarchy) +{ + gui_resources_t *res = _res; + uint32_t viewid = P_UINT (pr, 0); + view_t view = { .id = viewid, .reg = res->reg, + .comp = res->view_base + view_href }; + View_UpdateHierarchy (view); +} + +#undef bi +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) +#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } +static builtin_t builtins[] = { + bi(Font_Load, 2, p(string), p(int)), + bi(Font_Free, 2, p(int)), + bi(Passage_New, 0), + bi(Passage_ParseText, 2, p(ptr), p(string)), + bi(Passage_Delete, 1, p(ptr)), + bi(Passage_ChildCount, 2, p(ptr), p(uint)), + bi(Passage_GetChild, 3, p(ptr), p(uint), p(uint)), + + bi(Text_View, 2, p(ptr), p(int)), + bi(Text_SetScript, 4, p(uint), p(string), p(int), p (int)), + + bi(View_Delete, 1, p(uint)), + bi(View_ChildCount, 1, p(uint)), + bi(View_GetChild, 2, p(uint), p(uint)), + bi(View_SetPos, 3, p(uint), p(int), p(int)), + bi(View_SetLen, 3, p(uint), p(int), p(int)), + bi(View_GetLen, 1, p(uint)), + bi(View_UpdateHierarchy,1, p(uint)), + + bi(Text_Draw, 0), + + {0} +}; + +void +RUA_GUI_Init (progs_t *pr, int secure) +{ + gui_resources_t *res = calloc (1, sizeof (gui_resources_t)); + res->pr = pr; + + PR_Resources_Register (pr, "Draw", res, bi_gui_clear, bi_gui_destroy); + PR_RegisterBuiltins (pr, builtins, res); + + res->reg = ECS_NewRegistry (); + res->text_base = ECS_RegisterComponents (res->reg, text_components, + text_comp_count); + res->view_base = ECS_RegisterComponents (res->reg, view_components, + view_comp_count); + res->passage_base = ECS_RegisterComponents (res->reg, passage_components, + passage_comp_count); + ECS_CreateComponentPools (res->reg); +} diff --git a/libs/ruamoko/rua_hash.c b/libs/ruamoko/rua_hash.c index cc6d2b8c6..040d5dcaa 100644 --- a/libs/ruamoko/rua_hash.c +++ b/libs/ruamoko/rua_hash.c @@ -49,11 +49,11 @@ typedef struct bi_hashtab_s { struct bi_hashtab_s **prev; progs_t *pr; hashtab_t *tab; - func_t gk; - func_t gh; - func_t cmp; - func_t f; - pointer_t ud; + pr_func_t gk; + pr_func_t gh; + pr_func_t cmp; + pr_func_t f; + pr_ptr_t ud; } bi_hashtab_t; typedef struct { @@ -64,81 +64,96 @@ typedef struct { static bi_hashtab_t * table_new (hash_resources_t *res) { - PR_RESNEW (bi_hashtab_t, res->table_map); + return PR_RESNEW (res->table_map); } static void table_free (hash_resources_t *res, bi_hashtab_t *table) { - PR_RESFREE (bi_hashtab_t, res->table_map, table); + PR_RESFREE (res->table_map, table); } static void table_reset (hash_resources_t *res) { - PR_RESRESET (bi_hashtab_t, res->table_map); + PR_RESRESET (res->table_map); } static inline bi_hashtab_t * table_get (hash_resources_t *res, int index) { - PR_RESGET(res->table_map, index); + return PR_RESGET(res->table_map, index); } -static inline int +static inline int __attribute__((pure)) table_index (hash_resources_t *res, bi_hashtab_t *table) { - PR_RESINDEX(res->table_map, table); + return PR_RESINDEX(res->table_map, table); } static const char * bi_get_key (const void *key, void *_ht) { bi_hashtab_t *ht = (bi_hashtab_t *)_ht; + PR_PushFrame (ht->pr); PR_RESET_PARAMS (ht->pr); P_INT (ht->pr, 0) = (intptr_t) (key); P_INT (ht->pr, 1) = ht->ud; + ht->pr->pr_argc = 2; PR_ExecuteProgram (ht->pr, ht->gk); - return PR_GetString (ht->pr, R_STRING (ht->pr)); + pr_string_t string = R_STRING (ht->pr); + PR_PopFrame (ht->pr); + return PR_GetString (ht->pr, string); } static uintptr_t bi_get_hash (const void *key, void *_ht) { bi_hashtab_t *ht = (bi_hashtab_t *)_ht; + PR_PushFrame (ht->pr); PR_RESET_PARAMS (ht->pr); P_INT (ht->pr, 0) = (intptr_t) (key); P_INT (ht->pr, 1) = ht->ud; + ht->pr->pr_argc = 2; PR_ExecuteProgram (ht->pr, ht->gh); - return R_INT (ht->pr); + int hash = R_INT (ht->pr); + PR_PopFrame (ht->pr); + return hash; } static int bi_compare (const void *key1, const void *key2, void *_ht) { bi_hashtab_t *ht = (bi_hashtab_t *)_ht; + PR_PushFrame (ht->pr); PR_RESET_PARAMS (ht->pr); P_INT (ht->pr, 0) = (intptr_t) (key1); P_INT (ht->pr, 1) = (intptr_t) (key2); P_INT (ht->pr, 2) = ht->ud; + ht->pr->pr_argc = 3; PR_ExecuteProgram (ht->pr, ht->cmp); - return R_INT (ht->pr); + int cmp = R_INT (ht->pr); + PR_PopFrame (ht->pr); + return cmp; } static void bi_free (void *key, void *_ht) { bi_hashtab_t *ht = (bi_hashtab_t *)_ht; + PR_PushFrame (ht->pr); PR_RESET_PARAMS (ht->pr); P_INT (ht->pr, 0) = (intptr_t) (key); P_INT (ht->pr, 1) = ht->ud; + ht->pr->pr_argc = 2; PR_ExecuteProgram (ht->pr, ht->f); + PR_PopFrame (ht->pr); } static void -bi_Hash_NewTable (progs_t *pr) +bi_Hash_NewTable (progs_t *pr, void *_res) { - hash_resources_t *res = PR_Resources_Find (pr, "Hash"); + __auto_type res = (hash_resources_t *) _res; int tsize = P_INT (pr, 0); const char *(*gk)(const void*,void*); void (*f)(void*,void*); @@ -158,14 +173,13 @@ bi_Hash_NewTable (progs_t *pr) gk = ht->gk ? bi_get_key : 0; f = ht->f ? bi_free : 0; - ht->tab = Hash_NewTable (tsize, gk, f, ht); + ht->tab = Hash_NewTable (tsize, gk, f, ht, pr->hashctx); R_INT (pr) = table_index (res, ht); } -static bi_hashtab_t * -get_table (progs_t *pr, const char *name, int index) +static bi_hashtab_t * __attribute__((pure)) +get_table (progs_t *pr, hash_resources_t *res, const char *name, int index) { - hash_resources_t *res = PR_Resources_Find (pr, "Hash"); bi_hashtab_t *ht = table_get (res, index); if (!ht) @@ -174,9 +188,10 @@ get_table (progs_t *pr, const char *name, int index) } static void -bi_Hash_SetHashCompare (progs_t *pr) +bi_Hash_SetHashCompare (progs_t *pr, void *_res) { - bi_hashtab_t *ht = get_table (pr, __FUNCTION__, P_INT (pr, 0)); + __auto_type res = (hash_resources_t *) _res; + bi_hashtab_t *ht = get_table (pr, res, __FUNCTION__, P_INT (pr, 0)); uintptr_t (*gh)(const void*,void*); int (*cmp)(const void*,const void*,void*); @@ -188,10 +203,10 @@ bi_Hash_SetHashCompare (progs_t *pr) } static void -bi_Hash_DelTable (progs_t *pr) +bi_Hash_DelTable (progs_t *pr, void *_res) { - hash_resources_t *res = PR_Resources_Find (pr, "Hash"); - bi_hashtab_t *ht = get_table (pr, __FUNCTION__, P_INT (pr, 0)); + __auto_type res = (hash_resources_t *) _res; + bi_hashtab_t *ht = get_table (pr, res, __FUNCTION__, P_INT (pr, 0)); Hash_DelTable (ht->tab); *ht->prev = ht->next; @@ -201,50 +216,56 @@ bi_Hash_DelTable (progs_t *pr) } static void -bi_Hash_FlushTable (progs_t *pr) +bi_Hash_FlushTable (progs_t *pr, void *_res) { - bi_hashtab_t *ht = get_table (pr, __FUNCTION__, P_INT (pr, 0)); + __auto_type res = (hash_resources_t *) _res; + bi_hashtab_t *ht = get_table (pr, res, __FUNCTION__, P_INT (pr, 0)); Hash_FlushTable (ht->tab); } static void -bi_Hash_Add (progs_t *pr) +bi_Hash_Add (progs_t *pr, void *_res) { - bi_hashtab_t *ht = get_table (pr, __FUNCTION__, P_INT (pr, 0)); + __auto_type res = (hash_resources_t *) _res; + bi_hashtab_t *ht = get_table (pr, res, __FUNCTION__, P_INT (pr, 0)); R_INT (pr) = Hash_Add (ht->tab, (void *) (intptr_t) P_INT (pr, 1)); } static void -bi_Hash_AddElement (progs_t *pr) +bi_Hash_AddElement (progs_t *pr, void *_res) { - bi_hashtab_t *ht = get_table (pr, __FUNCTION__, P_INT (pr, 0)); + __auto_type res = (hash_resources_t *) _res; + bi_hashtab_t *ht = get_table (pr, res, __FUNCTION__, P_INT (pr, 0)); R_INT (pr) = Hash_AddElement (ht->tab, (void *) (intptr_t) P_INT (pr, 1)); } static void -bi_Hash_Find (progs_t *pr) +bi_Hash_Find (progs_t *pr, void *_res) { - bi_hashtab_t *ht = get_table (pr, __FUNCTION__, P_INT (pr, 0)); + __auto_type res = (hash_resources_t *) _res; + bi_hashtab_t *ht = get_table (pr, res, __FUNCTION__, P_INT (pr, 0)); R_INT (pr) = (intptr_t) Hash_Find (ht->tab, P_GSTRING (pr, 1)); } static void -bi_Hash_FindElement (progs_t *pr) +bi_Hash_FindElement (progs_t *pr, void *_res) { - bi_hashtab_t *ht = get_table (pr, __FUNCTION__, P_INT (pr, 0)); + __auto_type res = (hash_resources_t *) _res; + bi_hashtab_t *ht = get_table (pr, res, __FUNCTION__, P_INT (pr, 0)); R_INT (pr) = (intptr_t) Hash_FindElement (ht->tab, (void *) (intptr_t) P_INT (pr, 1)); } static void -bi_Hash_FindList (progs_t *pr) +bi_Hash_FindList (progs_t *pr, void *_res) { - bi_hashtab_t *ht = get_table (pr, __FUNCTION__, P_INT (pr, 0)); + __auto_type res = (hash_resources_t *) _res; + bi_hashtab_t *ht = get_table (pr, res, __FUNCTION__, P_INT (pr, 0)); void **list, **l; pr_type_t *pr_list; int count; @@ -255,15 +276,16 @@ bi_Hash_FindList (progs_t *pr) pr_list = PR_Zone_Malloc (pr, count * sizeof (pr_type_t)); // the hash tables stores progs pointers... for (count = 0, l = list; *l; l++) - pr_list[count++].integer_var = (intptr_t) *l; + PR_PTR (ptr, &pr_list[count++]) = (intptr_t) *l; free (list); RETURN_POINTER (pr, pr_list); } static void -bi_Hash_FindElementList (progs_t *pr) +bi_Hash_FindElementList (progs_t *pr, void *_res) { - bi_hashtab_t *ht = get_table (pr, __FUNCTION__, P_INT (pr, 0)); + __auto_type res = (hash_resources_t *) _res; + bi_hashtab_t *ht = get_table (pr, res, __FUNCTION__, P_INT (pr, 0)); void **list, **l; pr_type_t *pr_list; int count; @@ -274,52 +296,56 @@ bi_Hash_FindElementList (progs_t *pr) pr_list = PR_Zone_Malloc (pr, count * sizeof (pr_type_t)); // the hash tables stores progs pointers... for (count = 0, l = list; *l; l++) - pr_list[count++].integer_var = (intptr_t) *l; + PR_PTR (ptr, &pr_list[count++]) = (intptr_t) *l; free (list); RETURN_POINTER (pr, pr_list); } static void -bi_Hash_Del (progs_t *pr) +bi_Hash_Del (progs_t *pr, void *_res) { - bi_hashtab_t *ht = get_table (pr, __FUNCTION__, P_INT (pr, 0)); + __auto_type res = (hash_resources_t *) _res; + bi_hashtab_t *ht = get_table (pr, res, __FUNCTION__, P_INT (pr, 0)); R_INT (pr) = (intptr_t) Hash_Del (ht->tab, P_GSTRING (pr, 1)); } static void -bi_Hash_DelElement (progs_t *pr) +bi_Hash_DelElement (progs_t *pr, void *_res) { - bi_hashtab_t *ht = get_table (pr, __FUNCTION__, P_INT (pr, 0)); + __auto_type res = (hash_resources_t *) _res; + bi_hashtab_t *ht = get_table (pr, res, __FUNCTION__, P_INT (pr, 0)); R_INT (pr) = (intptr_t) Hash_DelElement (ht->tab, (void *) (intptr_t) P_INT (pr, 1)); } static void -bi_Hash_Free (progs_t *pr) +bi_Hash_Free (progs_t *pr, void *_res) { - bi_hashtab_t *ht = get_table (pr, __FUNCTION__, P_INT (pr, 0)); + __auto_type res = (hash_resources_t *) _res; + bi_hashtab_t *ht = get_table (pr, res, __FUNCTION__, P_INT (pr, 0)); Hash_Free (ht->tab, (void *) (intptr_t) P_INT (pr, 1)); } static void -bi_Hash_String (progs_t *pr) +bi_Hash_String (progs_t *pr, void *_res) { R_INT (pr) = Hash_String (P_GSTRING (pr, 0)); } static void -bi_Hash_Buffer (progs_t *pr) +bi_Hash_Buffer (progs_t *pr, void *_res) { R_INT (pr) = Hash_Buffer (P_GPOINTER (pr, 0), P_INT (pr, 1)); } static void -bi_Hash_GetList (progs_t *pr) +bi_Hash_GetList (progs_t *pr, void *_res) { - bi_hashtab_t *ht = get_table (pr, __FUNCTION__, P_INT (pr, 0)); + __auto_type res = (hash_resources_t *) _res; + bi_hashtab_t *ht = get_table (pr, res, __FUNCTION__, P_INT (pr, 0)); void **list, **l; pr_type_t *pr_list; int count; @@ -330,23 +356,24 @@ bi_Hash_GetList (progs_t *pr) pr_list = PR_Zone_Malloc (pr, count * sizeof (pr_type_t)); // the hash tables stores progs pointers... for (count = 0, l = list; *l; l++) - pr_list[count++].integer_var = (intptr_t) *l; + PR_PTR(ptr, &pr_list[count++]) = (intptr_t) *l; free (list); RETURN_POINTER (pr, pr_list); } static void -bi_Hash_Stats (progs_t *pr) +bi_Hash_Stats (progs_t *pr, void *_res) { - bi_hashtab_t *ht = get_table (pr, __FUNCTION__, P_INT (pr, 0)); + __auto_type res = (hash_resources_t *) _res; + bi_hashtab_t *ht = get_table (pr, res, __FUNCTION__, P_INT (pr, 0)); Hash_Stats (ht->tab); } static void -bi_hash_clear (progs_t *pr, void *data) +bi_hash_clear (progs_t *pr, void *_res) { - hash_resources_t *res = (hash_resources_t *) data; + hash_resources_t *res = (hash_resources_t *) _res; bi_hashtab_t *ht; for (ht = res->tabs; ht; ht = ht->next) @@ -355,24 +382,36 @@ bi_hash_clear (progs_t *pr, void *data) table_reset (res); } +static void +bi_hash_destroy (progs_t *pr, void *_res) +{ + hash_resources_t *res = _res; + + PR_RESDELMAP (res->table_map); + + free (res); +} + +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) static builtin_t builtins[] = { - {"Hash_NewTable", bi_Hash_NewTable, -1}, - {"Hash_SetHashCompare", bi_Hash_SetHashCompare, -1}, - {"Hash_DelTable", bi_Hash_DelTable, -1}, - {"Hash_FlushTable", bi_Hash_FlushTable, -1}, - {"Hash_Add", bi_Hash_Add, -1}, - {"Hash_AddElement", bi_Hash_AddElement, -1}, - {"Hash_Find", bi_Hash_Find, -1}, - {"Hash_FindElement", bi_Hash_FindElement, -1}, - {"Hash_FindList", bi_Hash_FindList, -1}, - {"Hash_FindElementList", bi_Hash_FindElementList, -1}, - {"Hash_Del", bi_Hash_Del, -1}, - {"Hash_DelElement", bi_Hash_DelElement, -1}, - {"Hash_Free", bi_Hash_Free, -1}, - {"Hash_String", bi_Hash_String, -1}, - {"Hash_Buffer", bi_Hash_Buffer, -1}, - {"Hash_GetList", bi_Hash_GetList, -1}, - {"Hash_Stats", bi_Hash_Stats, -1}, + bi(Hash_NewTable, 4, p(int), p(func), p(func), p(ptr)), + bi(Hash_SetHashCompare, 3, p(ptr), p(func), p(func)), + bi(Hash_DelTable, 1, p(ptr)), + bi(Hash_FlushTable, 1, p(ptr)), + bi(Hash_Add, 2, p(ptr), p(ptr)), + bi(Hash_AddElement, 2, p(ptr), p(ptr)), + bi(Hash_Find, 2, p(ptr), p(string)), + bi(Hash_FindElement, 2, p(ptr), p(ptr)), + bi(Hash_FindList, 2, p(ptr), p(string)), + bi(Hash_FindElementList, 2, p(ptr), p(ptr)), + bi(Hash_Del, 2, p(ptr), p(string)), + bi(Hash_DelElement, 2, p(ptr), p(ptr)), + bi(Hash_Free, 2, p(ptr), p(ptr)), + bi(Hash_String, 1, p(string)), + bi(Hash_Buffer, 2, p(ptr), p(int)), + bi(Hash_GetList, 1, p(ptr)), + bi(Hash_Stats, 1, p(ptr)), {0} }; @@ -382,6 +421,6 @@ RUA_Hash_Init (progs_t *pr, int secure) hash_resources_t *res = calloc (1, sizeof (hash_resources_t)); res->tabs = 0; - PR_Resources_Register (pr, "Hash", res, bi_hash_clear); - PR_RegisterBuiltins (pr, builtins); + PR_Resources_Register (pr, "Hash", res, bi_hash_clear, bi_hash_destroy); + PR_RegisterBuiltins (pr, builtins, res); } diff --git a/libs/ruamoko/rua_init.c b/libs/ruamoko/rua_init.c index ab973b569..8c69a75d9 100644 --- a/libs/ruamoko/rua_init.c +++ b/libs/ruamoko/rua_init.c @@ -39,15 +39,16 @@ static void (*init_funcs[])(progs_t *, int) = { RUA_Cbuf_Init, RUA_Cmd_Init, RUA_Cvar_Init, - RUA_File_Init, RUA_Hash_Init, RUA_Math_Init, RUA_MsgBuf_Init, RUA_Plist_Init, RUA_QFile_Init, RUA_QFS_Init, + RUA_Runtime_Init, RUA_Script_Init, RUA_Set_Init, + RUA_Stdlib_Init, RUA_String_Init, }; @@ -56,7 +57,6 @@ RUA_Init (progs_t *pr, int secure) { size_t i; - PR_Resources_Init (pr); for (i = 0; i < sizeof (init_funcs) / sizeof (init_funcs[0]); i++) init_funcs[i] (pr, secure); } diff --git a/libs/ruamoko/rua_input.c b/libs/ruamoko/rua_input.c new file mode 100644 index 000000000..df86bcec1 --- /dev/null +++ b/libs/ruamoko/rua_input.c @@ -0,0 +1,532 @@ +/* + bi_input.c + + CSQC file builtins + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/cmem.h" +#include "QF/hash.h" +#include "QF/input.h" +#include "QF/progs.h" + +#include "rua_internal.h" + +typedef struct rua_in_cookie_s { + size_t users; + progs_t *pr; + pr_func_t func; + pr_ptr_t data; +} rua_in_cookie_t; + +typedef struct input_resources_s { + hashctx_t *hashctx; + hashtab_t *cookies; + memsuper_t *cookie_super; +} input_resources_t; + +static void +bi_IN_FindDeviceId (progs_t *pr, void *_res) +{ + const char *id = P_GSTRING (pr, 0); + + R_INT (pr) = IN_FindDeviceId (id); +} + +static void +bi_IN_GetDeviceName (progs_t *pr, void *_res) +{ + int devid = P_INT (pr, 0); + + RETURN_STRING (pr, IN_GetDeviceName (devid)); +} + +static void +bi_IN_GetDeviceId (progs_t *pr, void *_res) +{ + int devid = P_INT (pr, 0); + + RETURN_STRING (pr, IN_GetDeviceId (devid)); +} + +static void +bi_IN_AxisInfo (progs_t *pr, void *_res) +{ +} + +static void +bi_IN_ButtonInfo (progs_t *pr, void *_res) +{ +} + +static void +bi_IN_GetAxisName (progs_t *pr, void *_res) +{ + int devid = P_INT (pr, 0); + int axis = P_INT (pr, 1); + + RETURN_STRING (pr, IN_GetAxisName (devid, axis)); +} + +static void +bi_IN_GetButtonName (progs_t *pr, void *_res) +{ + int devid = P_INT (pr, 0); + int button = P_INT (pr, 1); + + RETURN_STRING (pr, IN_GetButtonName (devid, button)); +} + +static void +bi_IN_GetAxisNumber (progs_t *pr, void *_res) +{ + int devid = P_INT (pr, 0); + const char *axis_name = P_GSTRING (pr, 1); + + R_INT (pr) = IN_GetAxisNumber (devid, axis_name); +} + +static void +bi_IN_GetButtonNumber (progs_t *pr, void *_res) +{ + int devid = P_INT (pr, 0); + const char *button_name = P_GSTRING (pr, 1); + + R_INT (pr) = IN_GetButtonNumber (devid, button_name); +} + +static void +bi_IN_ProcessEvents (progs_t *pr, void *_res) +{ + IN_ProcessEvents (); +} + +static void +bi_IN_ClearStates (progs_t *pr, void *_res) +{ + IN_ClearStates (); +} + +static void +bi_IN_CreateButton (progs_t *pr, void *_res) +{ + const char *name = P_GSTRING (pr, 0); + const char *desc = P_GSTRING (pr, 1); + in_button_t *button = PR_Zone_Malloc (pr, sizeof (in_button_t)); + button->name = name; + button->description = desc; + IN_RegisterButton (button); + RETURN_POINTER (pr, button); +} + +static void +bi_IN_CreateAxis (progs_t *pr, void *_res) +{ + const char *name = P_GSTRING (pr, 0); + const char *desc = P_GSTRING (pr, 1); + in_axis_t *axis = PR_Zone_Malloc (pr, sizeof (in_axis_t)); + axis->name = name; + axis->description = desc; + IN_RegisterAxis (axis); + RETURN_POINTER (pr, axis); +} + +static void +bi_IN_GetAxisInfo (progs_t *pr, void *_res) +{ + int devid = P_INT (pr, 0); + int axis = P_INT (pr, 1); + + R_INT (pr) = 0; + + in_axisinfo_t info; + if (IN_GetAxisInfo (devid, axis, &info)) { + P_STRUCT (pr, in_axisinfo_t, 2) = info; + R_INT (pr) = 1; + } +} + +static void +bi_IN_GetButtonInfo (progs_t *pr, void *_res) +{ + int devid = P_INT (pr, 0); + int button = P_INT (pr, 1); + + R_INT (pr) = 0; + + in_buttoninfo_t info; + if (IN_GetButtonInfo (devid, button, &info)) { + P_STRUCT (pr, in_buttoninfo_t, 2) = info; + R_INT (pr) = 1; + } +} + +static rua_in_cookie_t * +make_cookie (progs_t *pr, input_resources_t *res, pr_func_t func, pr_ptr_t data) +{ + rua_in_cookie_t search = { + .func = func, + .data = data, + }; + rua_in_cookie_t *cookie = Hash_FindElement (res->cookies, &search); + if (!cookie) { + cookie = cmemalloc (res->cookie_super, sizeof (rua_in_cookie_t)); + *cookie = search; + cookie->pr = pr; + Hash_AddElement (res->cookies, cookie); + } + cookie->users++; + return cookie; +} + +static rua_in_cookie_t * +find_cookie (progs_t *pr, input_resources_t *res, pr_func_t func, pr_ptr_t data) +{ + rua_in_cookie_t search = { + .func = func, + .data = data, + }; + return Hash_FindElement (res->cookies, &search); +} + +static void +release_cookie (progs_t *pr, input_resources_t *res, rua_in_cookie_t *cookie) +{ + if (!--cookie->users) { + Hash_DelElement (res->cookies, cookie); + Hash_Free (res->cookies, cookie); + } +} + +static void +rua_add_axis_listener (progs_t *pr, input_resources_t *res, + axis_listener_t listener) +{ + in_axis_t *axis = &P_STRUCT (pr, in_axis_t, 0); + pr_func_t func = P_FUNCTION (pr, 1); + pr_func_t data = P_POINTER (pr, 2); + rua_in_cookie_t *cookie = make_cookie (pr, res, func, data); + IN_AxisAddListener (axis, listener, cookie); +} + +static void +rua_remove_axis_listener (progs_t *pr, input_resources_t *res, + axis_listener_t listener) +{ + in_axis_t *axis = &P_STRUCT (pr, in_axis_t, 0); + pr_func_t func = P_FUNCTION (pr, 1); + pr_func_t data = P_POINTER (pr, 2); + rua_in_cookie_t *cookie = find_cookie (pr, res, func, data); + if (cookie) { + IN_AxisRemoveListener (axis, listener, cookie); + release_cookie (pr, res, cookie); + } +} + +static void +rua_add_button_listener (progs_t *pr, input_resources_t *res, + button_listener_t listener) +{ + in_button_t *button = &P_STRUCT (pr, in_button_t, 0); + pr_func_t func = P_FUNCTION (pr, 1); + pr_func_t data = P_POINTER (pr, 2); + rua_in_cookie_t *cookie = make_cookie (pr, res, func, data); + IN_ButtonAddListener (button, listener, cookie); +} + +static void +rua_remove_button_listener (progs_t *pr, input_resources_t *res, + button_listener_t listener) +{ + in_button_t *button = &P_STRUCT (pr, in_button_t, 0); + pr_func_t func = P_FUNCTION (pr, 1); + pr_func_t data = P_POINTER (pr, 2); + rua_in_cookie_t *cookie = find_cookie (pr, res, func, data); + if (cookie) { + IN_ButtonRemoveListener (button, listener, cookie); + release_cookie (pr, res, cookie); + } +} + +static void +rua_listener_func (rua_in_cookie_t *cookie, const void *input) +{ + progs_t *pr = cookie->pr; + PR_PushFrame (pr); + PR_RESET_PARAMS (pr); + P_POINTER (pr, 0) = cookie->data; + P_POINTER (pr, 1) = PR_SetPointer (pr, input);//FIXME check input + pr->pr_argc = 2; + PR_ExecuteProgram (pr, cookie->func); + PR_PopFrame (pr); +} + +static void +rua_listener_method (rua_in_cookie_t *cookie, const void *input) +{ + progs_t *pr = cookie->pr; + PR_PushFrame (pr); + PR_RESET_PARAMS (pr); + P_POINTER (pr, 0) = cookie->data; + P_POINTER (pr, 1) = 0; // don't know the method name (selector) + P_POINTER (pr, 2) = PR_SetPointer (pr, input);//FIXME check input + pr->pr_argc = 3; + PR_ExecuteProgram (pr, cookie->func); + PR_PopFrame (pr); +} + +static void +rua_axis_listener_func (void *data, const in_axis_t *axis) +{ + rua_listener_func (data, axis); +} + +static void +rua_axis_listener_method (void *data, const in_axis_t *axis) +{ + rua_listener_method (data, axis); +} + +static void +rua_button_listener_func (void *data, const in_button_t *button) +{ + rua_listener_func (data, button); +} + +static void +rua_button_listener_method (void *data, const in_button_t *button) +{ + rua_listener_method (data, button); +} + +static void +rua_IN_ButtonAddListener_func (progs_t *pr, void *_res) +{ + input_resources_t *res = _res; + rua_add_button_listener (pr, res, rua_button_listener_func); +} + +static void +rua_IN_ButtonRemoveListener_func (progs_t *pr, void *_res) +{ + input_resources_t *res = _res; + rua_remove_button_listener (pr, res, rua_button_listener_func); +} + +static void +rua_IN_AxisAddListener_func (progs_t *pr, void *_res) +{ + input_resources_t *res = _res; + rua_add_axis_listener (pr, res, rua_axis_listener_func); +} + +static void +rua_IN_AxisRemoveListener_func (progs_t *pr, void *_res) +{ + input_resources_t *res = _res; + rua_remove_axis_listener (pr, res, rua_axis_listener_func); +} + +static void +rua_IN_ButtonAddListener_method (progs_t *pr, void *_res) +{ + input_resources_t *res = _res; + rua_add_button_listener (pr, res, rua_button_listener_method); +} + +static void +rua_IN_ButtonRemoveListener_method (progs_t *pr, void *_res) +{ + input_resources_t *res = _res; + rua_remove_button_listener (pr, res, rua_button_listener_method); +} + +static void +rua_IN_AxisAddListener_method (progs_t *pr, void *_res) +{ + input_resources_t *res = _res; + rua_add_axis_listener (pr, res, rua_axis_listener_method); +} + +static void +rua_IN_AxisRemoveListener_method (progs_t *pr, void *_res) +{ + input_resources_t *res = _res; + rua_remove_axis_listener (pr, res, rua_axis_listener_method); +} + +static void +bi_IN_LoadConfig (progs_t *pr, void *_res) +{ + IN_LoadConfig (Plist_GetItem (pr, P_INT (pr, 0))); +} + +static void +bi_IMT_CreateContext (progs_t *pr, void *_res) +{ + const char *name = P_GSTRING (pr, 0); + R_INT (pr) = IMT_CreateContext (name); +} + +static void +bi_IMT_GetContext (progs_t *pr, void *_res) +{ + R_INT (pr) = IMT_GetContext (); +} + +static void +bi_IMT_SetContext (progs_t *pr, void *_res) +{ + IMT_SetContext (P_INT (pr, 0)); +} + +static void +secured (progs_t *pr, void *_res) +{ + PR_RunError (pr, "Secured function called"); +} + +#define p(type) PR_PARAM(type) +#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } +#define bi(x,np,params...) {#x, secured, -1, np, {params}} +static builtin_t secure_builtins[] = { + bi(IN_CreateButton, 2, p(string), p(string)), + bi(IN_CreateAxis, 2, p(string), p(string)), + bi(IN_LoadConfig, 1, p(ptr)), + {0} +}; + +#undef bi +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +static builtin_t insecure_builtins[] = { + bi(IN_CreateButton, 2, p(string), p(string)), + bi(IN_CreateAxis, 2, p(string), p(string)), + bi(IN_LoadConfig, 1, p(ptr)), + {0} +}; +static builtin_t builtins[] = { + bi(IN_FindDeviceId, 1, p(string)), + bi(IN_GetDeviceName, 1, p(int)), + bi(IN_GetDeviceId, 1, p(int)), + bi(IN_AxisInfo, 0), //FIXME + bi(IN_ButtonInfo, 0), //FIXME + bi(IN_GetAxisName, 2, p(int), p(int)), + bi(IN_GetButtonName, 2, p(int), p(int)), + bi(IN_GetAxisNumber, 2, p(int), p(string)), + bi(IN_GetButtonNumber, 2, p(int), p(string)), + bi(IN_ProcessEvents, 0), + bi(IN_ClearStates, 0), + bi(IN_GetAxisInfo, 3, p(int), p(int), p(ptr)), + bi(IN_GetButtonInfo, 3, p(int), p(int), p(ptr)), + {"IN_ButtonAddListener|^{tag in_button_s=}^(v^v^{tag in_button_s=})^v", + rua_IN_ButtonAddListener_func, -1, 3, {p(ptr), p(func), p(ptr)}}, + {"IN_ButtonRemoveListener|^{tag in_button_s=}^(v^v^{tag in_button_s=})^v", + rua_IN_ButtonRemoveListener_func, -1, 3, {p(ptr), p(func), p(ptr)}}, + {"IN_AxisAddListener|^{tag in_axis_s=}^(v^v^{tag in_axis_s=})^v", + rua_IN_AxisAddListener_func, -1, 3, {p(ptr), p(func), p(ptr)}}, + {"IN_AxisRemoveListener|^{tag in_axis_s=}^(v^v^{tag in_axis_s=})^v", + rua_IN_AxisRemoveListener_func, -1, 3, {p(ptr), p(func), p(ptr)}}, + {"IN_ButtonAddListener|^{tag in_button_s=}(@@:.)@", + rua_IN_ButtonAddListener_method, -1, 3, {p(ptr), p(func), p(ptr)}}, + {"IN_ButtonRemoveListener|^{tag in_button_s=}(@@:.)@", + rua_IN_ButtonRemoveListener_method, -1, 3, {p(ptr), p(func), p(ptr)}}, + {"IN_AxisAddListener|^{tag in_axis_s=}(@@:.)@", + rua_IN_AxisAddListener_method, -1, 3, {p(ptr), p(func), p(ptr)}}, + {"IN_AxisRemoveListener|^{tag in_axis_s=}(@@:.)@", + rua_IN_AxisRemoveListener_method, -1, 3, {p(ptr), p(func), p(ptr)}}, + + bi(IMT_CreateContext, 1, p(string)), + bi(IMT_GetContext, 0), + bi(IMT_SetContext, 1, p(int)), + + {0} +}; + +static void +bi_input_clear (progs_t *pr, void *_res) +{ + input_resources_t *res = _res; + Hash_FlushTable (res->cookies); +} + +static void +bi_input_destroy (progs_t *pr, void *_res) +{ + input_resources_t *res = _res; + Hash_DelTable (res->cookies); + delete_memsuper (res->cookie_super); + + free (res); +} + +static uintptr_t +rua_in_hash_cookie (const void *_cookie, void *_res) +{ + const rua_in_cookie_t *cookie = _cookie; + return cookie->func + cookie->data; +} + +static int +rua_in_cmp_cookies (const void *_a, const void *_b, void *_res) +{ + const rua_in_cookie_t *a = _a; + const rua_in_cookie_t *b = _b; + return a->func == b->func && a->data == b->data; +} + +static void +rua_in_free_cookie (void *_cookie, void *_res) +{ + input_resources_t *res = _res; + rua_in_cookie_t *cookie = _cookie; + cmemfree (res->cookie_super, cookie); +} + +void +RUA_Input_Init (progs_t *pr, int secure) +{ + input_resources_t *res = calloc (sizeof (input_resources_t), 1); + + res->cookie_super = new_memsuper (); + res->cookies = Hash_NewTable (251, 0, rua_in_free_cookie, res, pr->hashctx); + Hash_SetHashCompare (res->cookies, rua_in_hash_cookie, rua_in_cmp_cookies); + + PR_Resources_Register (pr, "input", res, bi_input_clear, bi_input_destroy); + if (secure & 2) { + PR_RegisterBuiltins (pr, secure_builtins, res); + } else { + PR_RegisterBuiltins (pr, insecure_builtins, res); + } + PR_RegisterBuiltins (pr, builtins, res); +} diff --git a/libs/video/targets/pr_keys.c b/libs/ruamoko/rua_keys.c similarity index 73% rename from libs/video/targets/pr_keys.c rename to libs/ruamoko/rua_keys.c index b72eb006e..7a0d5d43f 100644 --- a/libs/video/targets/pr_keys.c +++ b/libs/ruamoko/rua_keys.c @@ -1,7 +1,7 @@ /* - bi_keys.c + rua_keys.c - CSQC key-api builtins + Ruamoko key-api builtins Copyright (C) 1996-1997 Id Software, Inc. @@ -36,15 +36,17 @@ #endif #include "QF/csqc.h" -#include "QF/keys.h" +#include "QF/input.h" #include "QF/progs.h" #include "QF/zone.h" static void -bi_Key_keydown (progs_t *pr) +bi_Key_keydown (progs_t *pr, void *data) { +#if 0 int keynum = P_INT (pr, 0); R_INT (pr) = keydown[keynum]; +#endif } /* @@ -53,8 +55,9 @@ bi_Key_keydown (progs_t *pr) QC-Function for set a binding */ static void -bi_Key_SetBinding (progs_t *pr) +bi_Key_SetBinding (progs_t *pr, void *data) { +#if 0 const char *imt_name = P_GSTRING (pr, 0); int keynum = P_INT (pr, 1); const char *binding = P_GSTRING (pr, 2); @@ -68,6 +71,7 @@ bi_Key_SetBinding (progs_t *pr) if (imt) { Key_SetBinding (imt, keynum, binding); } +#endif } /* @@ -76,8 +80,9 @@ bi_Key_SetBinding (progs_t *pr) Perform a reverse-binding-lookup */ static void -bi_Key_LookupBinding (progs_t *pr) +bi_Key_LookupBinding (progs_t *pr, void *data) { +#if 0 const char *imt_name = P_GSTRING (pr, 0); int bindnum = P_INT (pr, 1); const char *binding = P_GSTRING (pr, 2); @@ -89,7 +94,7 @@ bi_Key_LookupBinding (progs_t *pr) imt = Key_FindIMT (imt_name); if (imt) { for (i = 0; i < QFK_LAST; i++) { - keybind = imt->bindings[i].str; + keybind = imt->button_bindings.a[i].str; if (keybind == NULL) { continue; } @@ -104,7 +109,8 @@ bi_Key_LookupBinding (progs_t *pr) } R_INT (pr) = keynum; -}; +#endif +} /* bi_Key_CountBinding @@ -112,8 +118,9 @@ bi_Key_LookupBinding (progs_t *pr) Counts how often a binding is assigned to a key */ static void -bi_Key_CountBinding (progs_t *pr) +bi_Key_CountBinding (progs_t *pr, void *data) { +#if 0 const char *imt_name = P_GSTRING (pr, 0); const char *binding = P_GSTRING (pr, 1); int i, res = 0; @@ -123,7 +130,7 @@ bi_Key_CountBinding (progs_t *pr) imt = Key_FindIMT (imt_name); if (imt) { for (i = 0; i < QFK_LAST; i++) { - keybind = imt->bindings[i].str; + keybind = imt->button_bindings.a[i].str; if (keybind == NULL) { continue; } @@ -134,7 +141,8 @@ bi_Key_CountBinding (progs_t *pr) } R_INT (pr) = res; -}; +#endif +} /* @@ -143,32 +151,35 @@ bi_Key_CountBinding (progs_t *pr) Convertes a keynum to a string */ static void -bi_Key_KeynumToString (progs_t *pr) +bi_Key_KeynumToString (progs_t *pr, void *data) { int keynum = P_INT (pr, 0); RETURN_STRING (pr, Key_KeynumToString (keynum)); -}; +} static void -bi_Key_StringToKeynum (progs_t *pr) +bi_Key_StringToKeynum (progs_t *pr, void *data) { const char *keyname = P_GSTRING (pr, 0); R_INT (pr) = Key_StringToKeynum (keyname); } +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) +#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } static builtin_t builtins[] = { - {"Key_keydown", bi_Key_keydown, -1}, - {"Key_SetBinding", bi_Key_SetBinding, -1}, - {"Key_LookupBinding", bi_Key_LookupBinding, -1}, - {"Key_CountBinding", bi_Key_CountBinding, -1}, - {"Key_KeynumToString", bi_Key_KeynumToString, -1}, - {"Key_StringToKeynum", bi_Key_StringToKeynum, -1}, + bi(Key_keydown, 1, p(int)), + bi(Key_SetBinding, 3, p(string), p(int), p(string)), + bi(Key_LookupBinding, 3, p(string), p(int), p(string)), + bi(Key_CountBinding, 2, p(string), p(string)), + bi(Key_KeynumToString, 1, p(int)), + bi(Key_StringToKeynum, 1, p(string)), {0} }; void -Key_Progs_Init (progs_t *pr) +RUA_Key_Init (progs_t *pr, void *data) { - PR_RegisterBuiltins (pr, builtins); + PR_RegisterBuiltins (pr, builtins, 0); } diff --git a/libs/ruamoko/rua_math.c b/libs/ruamoko/rua_math.c index 0cc5e5636..cf00b84a3 100644 --- a/libs/ruamoko/rua_math.c +++ b/libs/ruamoko/rua_math.c @@ -42,55 +42,61 @@ #include "rua_internal.h" static void -bi_sin (progs_t *pr) +bi_sinf (progs_t *pr, void *data) { R_FLOAT (pr) = sinf (P_FLOAT (pr, 0)); } static void -bi_cos (progs_t *pr) +bi_cosf (progs_t *pr, void *data) { R_FLOAT (pr) = cosf (P_FLOAT (pr, 0)); } static void -bi_tan (progs_t *pr) +bi_tanf (progs_t *pr, void *data) { R_FLOAT (pr) = tanf (P_FLOAT (pr, 0)); } static void -bi_asin (progs_t *pr) +bi_asinf (progs_t *pr, void *data) { R_FLOAT (pr) = asinf (P_FLOAT (pr, 0)); } static void -bi_acos (progs_t *pr) +bi_acosf (progs_t *pr, void *data) { R_FLOAT (pr) = acosf (P_FLOAT (pr, 0)); } static void -bi_atan (progs_t *pr) +bi_atanf (progs_t *pr, void *data) { R_FLOAT (pr) = atanf (P_FLOAT (pr, 0)); } static void -bi_atan2 (progs_t *pr) +bi_atan2f (progs_t *pr, void *data) { R_FLOAT (pr) = atan2f (P_FLOAT (pr, 0), P_FLOAT (pr, 1)); } static void -bi_log (progs_t *pr) +bi_expf (progs_t *pr, void *data) +{ + R_FLOAT (pr) = expf (P_FLOAT (pr, 0)); +} + +static void +bi_logf (progs_t *pr, void *data) { R_FLOAT (pr) = logf (P_FLOAT (pr, 0)); } static void -bi_log2 (progs_t *pr) +bi_log2f (progs_t *pr, void *data) { #ifdef HAVE_LOG2F R_FLOAT (pr) = log2f (P_FLOAT (pr, 0)); @@ -100,100 +106,274 @@ bi_log2 (progs_t *pr) } static void -bi_log10 (progs_t *pr) +bi_log10f (progs_t *pr, void *data) { R_FLOAT (pr) = log10f (P_FLOAT (pr, 0)); } static void -bi_pow (progs_t *pr) +bi_powf (progs_t *pr, void *data) { R_FLOAT (pr) = powf (P_FLOAT (pr, 0), P_FLOAT (pr, 1)); } static void -bi_sqrt (progs_t *pr) +bi_sqrtf (progs_t *pr, void *data) { R_FLOAT (pr) = sqrtf (P_FLOAT (pr, 0)); } static void -bi_cbrt (progs_t *pr) +bi_cbrtf (progs_t *pr, void *data) { R_FLOAT (pr) = cbrtf (P_FLOAT (pr, 0)); } static void -bi_hypot (progs_t *pr) +bi_hypotf (progs_t *pr, void *data) { R_FLOAT (pr) = hypotf (P_FLOAT (pr, 0), P_FLOAT (pr, 1)); } static void -bi_sinh (progs_t *pr) +bi_sinhf (progs_t *pr, void *data) { R_FLOAT (pr) = sinhf (P_FLOAT (pr, 0)); } static void -bi_cosh (progs_t *pr) +bi_coshf (progs_t *pr, void *data) { R_FLOAT (pr) = coshf (P_FLOAT (pr, 0)); } static void -bi_tanh (progs_t *pr) +bi_tanhf (progs_t *pr, void *data) { R_FLOAT (pr) = tanhf (P_FLOAT (pr, 0)); } static void -bi_asinh (progs_t *pr) +bi_asinhf (progs_t *pr, void *data) { double y = P_FLOAT (pr, 0); R_FLOAT (pr) = logf (y + sqrtf (y * y + 1)); } static void -bi_acosh (progs_t *pr) +bi_acoshf (progs_t *pr, void *data) { double y = P_FLOAT (pr, 0); R_FLOAT (pr) = logf (y + sqrtf (y * y - 1)); } static void -bi_atanh (progs_t *pr) +bi_atanhf (progs_t *pr, void *data) { double y = P_FLOAT (pr, 0); R_FLOAT (pr) = logf ((1 + y) / (1 - y)) / 2; } +static void +bi_floor (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = floor (P_DOUBLE (pr, 0)); +} + +static void +bi_ceil (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = ceil (P_DOUBLE (pr, 0)); +} + +static void +bi_fabs (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = fabs (P_DOUBLE (pr, 0)); +} + +static void +bi_sin (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = sin (P_DOUBLE (pr, 0)); +} + +static void +bi_cos (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = cos (P_DOUBLE (pr, 0)); +} + +static void +bi_tan (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = tan (P_DOUBLE (pr, 0)); +} + +static void +bi_asin (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = asin (P_DOUBLE (pr, 0)); +} + +static void +bi_acos (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = acos (P_DOUBLE (pr, 0)); +} + +static void +bi_atan (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = atan (P_DOUBLE (pr, 0)); +} + +static void +bi_atan2 (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = atan2 (P_DOUBLE (pr, 0), P_DOUBLE (pr, 1)); +} + +static void +bi_exp (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = exp (P_DOUBLE (pr, 0)); +} + +static void +bi_log (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = log (P_DOUBLE (pr, 0)); +} + +static void +bi_log2 (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = log (P_DOUBLE (pr, 0)) / M_LOG2E; +} + +static void +bi_log10 (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = log10 (P_DOUBLE (pr, 0)); +} + +static void +bi_pow (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = pow (P_DOUBLE (pr, 0), P_DOUBLE (pr, 1)); +} + +static void +bi_sqrt (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = sqrt (P_DOUBLE (pr, 0)); +} + +static void +bi_cbrt (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = cbrt (P_DOUBLE (pr, 0)); +} + +static void +bi_hypot (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = hypot (P_DOUBLE (pr, 0), P_DOUBLE (pr, 1)); +} + +static void +bi_sinh (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = sinh (P_DOUBLE (pr, 0)); +} + +static void +bi_cosh (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = cosh (P_DOUBLE (pr, 0)); +} + +static void +bi_tanh (progs_t *pr, void *data) +{ + R_DOUBLE (pr) = tanh (P_DOUBLE (pr, 0)); +} + +static void +bi_asinh (progs_t *pr, void *data) +{ + double y = P_DOUBLE (pr, 0); + R_DOUBLE (pr) = log (y + sqrt (y * y + 1)); +} + +static void +bi_acosh (progs_t *pr, void *data) +{ + double y = P_DOUBLE (pr, 0); + R_DOUBLE (pr) = log (y + sqrt (y * y - 1)); +} + +static void +bi_atanh (progs_t *pr, void *data) +{ + double y = P_DOUBLE (pr, 0); + R_DOUBLE (pr) = log ((1 + y) / (1 - y)) / 2; +} + +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) static builtin_t builtins[] = { - {"sin", bi_sin, -1}, - {"cos", bi_cos, -1}, - {"tan", bi_tan, -1}, - {"asin", bi_asin, -1}, - {"acos", bi_acos, -1}, - {"atan", bi_atan, -1}, - {"atan2", bi_atan2, -1}, - {"log", bi_log, -1}, - {"log2", bi_log2, -1}, - {"log10", bi_log10, -1}, - {"pow", bi_pow, -1}, - {"sqrt", bi_sqrt, -1}, - {"cbrt", bi_cbrt, -1}, - {"hypot", bi_hypot, -1}, - {"sinh", bi_sinh, -1}, - {"cosh", bi_cosh, -1}, - {"tanh", bi_tanh, -1}, - {"asinh", bi_asinh, -1}, - {"acosh", bi_acosh, -1}, - {"atanh", bi_atanh, -1}, + {"sin|f", bi_sinf, -1, 1, {p(float)}}, + {"cos|f", bi_cosf, -1, 1, {p(float)}}, + {"tan|f", bi_tanf, -1, 1, {p(float)}}, + {"asin|f", bi_asinf, -1, 1, {p(float)}}, + {"acos|f", bi_acosf, -1, 1, {p(float)}}, + {"atan|f", bi_atanf, -1, 1, {p(float)}}, + {"atan2|ff",bi_atan2f, -1, 2, {p(float), p(float)}}, + {"exp|f", bi_expf, -1, 1, {p(float)}}, + {"log|f", bi_logf, -1, 1, {p(float)}}, + {"log2|f", bi_log2f, -1, 1, {p(float)}}, + {"log10|f", bi_log10f, -1, 1, {p(float)}}, + {"pow|ff", bi_powf, -1, 2, {p(float), p(float)}}, + {"sqrt|f", bi_sqrtf, -1, 1, {p(float)}}, + {"cbrt|f", bi_cbrtf, -1, 1, {p(float)}}, + {"hypot|ff",bi_hypotf, -1, 2, {p(float), p(float)}}, + {"sinh|f", bi_sinhf, -1, 1, {p(float)}}, + {"cosh|f", bi_coshf, -1, 1, {p(float)}}, + {"tanh|f", bi_tanhf, -1, 1, {p(float)}}, + {"asinh|f", bi_asinhf, -1, 1, {p(float)}}, + {"acosh|f", bi_acoshf, -1, 1, {p(float)}}, + {"atanh|f", bi_atanhf, -1, 1, {p(float)}}, + {"floor|d", bi_floor, -1, 1, {p(double)}}, // float version in pr_cmds + {"ceil|d", bi_ceil, -1, 1, {p(double)}}, // float version in pr_cmds + {"fabs|d", bi_fabs, -1, 1, {p(double)}}, // float version in pr_cmds + {"sin|d", bi_sin, -1, 1, {p(double)}}, + {"cos|d", bi_cos, -1, 1, {p(double)}}, + {"tan|d", bi_tan, -1, 1, {p(double)}}, + {"asin|d", bi_asin, -1, 1, {p(double)}}, + {"acos|d", bi_acos, -1, 1, {p(double)}}, + {"atan|d", bi_atan, -1, 1, {p(double)}}, + {"atan2|dd",bi_atan2, -1, 2, {p(double), p(double)}}, + {"exp|d", bi_exp, -1, 1, {p(double)}}, + {"log|d", bi_log, -1, 1, {p(double)}}, + {"log2|d", bi_log2, -1, 1, {p(double)}}, + {"log10|d", bi_log10, -1, 1, {p(double)}}, + {"pow|dd", bi_pow, -1, 2, {p(double), p(double)}}, + {"sqrt|d", bi_sqrt, -1, 1, {p(double)}}, + {"cbrt|d", bi_cbrt, -1, 1, {p(double)}}, + {"hypot|dd",bi_hypot, -1, 2, {p(double), p(double)}}, + {"sinh|d", bi_sinh, -1, 1, {p(double)}}, + {"cosh|d", bi_cosh, -1, 1, {p(double)}}, + {"tanh|d", bi_tanh, -1, 1, {p(double)}}, + {"asinh|d", bi_asinh, -1, 1, {p(double)}}, + {"acosh|d", bi_acosh, -1, 1, {p(double)}}, + {"atanh|d", bi_atanh, -1, 1, {p(double)}}, {0} }; void RUA_Math_Init (progs_t *pr, int secure) { - PR_RegisterBuiltins (pr, builtins); + PR_RegisterBuiltins (pr, builtins, 0); } diff --git a/libs/ruamoko/rua_mersenne.c b/libs/ruamoko/rua_mersenne.c new file mode 100644 index 000000000..15ce3e41c --- /dev/null +++ b/libs/ruamoko/rua_mersenne.c @@ -0,0 +1,176 @@ +/* + bi_mersenne.c + + Ruamoko Mersenne Twister api + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/12/21 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include + +#include "QF/mersenne.h" +#include "QF/progs.h" + +#include "rua_internal.h" + +typedef struct { + //FIXME each mtstate_t is 2500 bytes and the map has 1024 elements + //per row, so having only one mtstate_t has an overhead of about 2.5MB + PR_RESMAP (mtstate_t) state_map; +} mtwist_resources_t; + +static mtstate_t * +state_new (mtwist_resources_t *res) +{ + return PR_RESNEW (res->state_map); +} + +static void +state_free (mtwist_resources_t *res, mtstate_t *state) +{ + PR_RESFREE (res->state_map, state); +} + +static void +state_reset (mtwist_resources_t *res) +{ + PR_RESRESET (res->state_map); +} + +static inline mtstate_t * +state_get (mtwist_resources_t *res, int index) +{ + return PR_RESGET(res->state_map, index); +} + +static inline int __attribute__((pure)) +state_index (mtwist_resources_t *res, mtstate_t *state) +{ + return PR_RESINDEX(res->state_map, state); +} + +static void +bi_mtwist_new (progs_t *pr, void *_res) +{ + mtwist_resources_t *res = (mtwist_resources_t *) _res; + mtstate_t *mt = state_new (res); + mtwist_seed (mt, P_INT (pr, 0)); + R_INT (pr) = state_index (res, mt); +} + +static mtstate_t * __attribute__((pure)) +get_state (progs_t *pr, mtwist_resources_t *res, const char *name, int index) +{ + mtstate_t *mt = state_get (res, index); + + if (!mt) + PR_RunError (pr, "invalid Mersenne Twister state index passed to %s", + name + 3); + return mt; +} + +static void +bi_mtwist_delete (progs_t *pr, void *_res) +{ + mtwist_resources_t *res = _res; + mtstate_t *mt = get_state (pr, res, __FUNCTION__, P_INT (pr, 0)); + state_free (res, mt); +} + +static void +bi_mtwist_seed (progs_t *pr, void *_res) +{ + mtwist_resources_t *res = _res; + mtstate_t *mt = get_state (pr, res, __FUNCTION__, P_INT (pr, 0)); + mtwist_seed (mt, P_INT (pr, 1)); +} + +static void +bi_mtwist_rand (progs_t *pr, void *_res) +{ + mtwist_resources_t *res = _res; + mtstate_t *mt = get_state (pr, res, __FUNCTION__, P_INT (pr, 0)); + R_INT (pr) = mtwist_rand (mt); +} + +static void +bi_mtwist_rand_0_1 (progs_t *pr, void *_res) +{ + mtwist_resources_t *res = _res; + mtstate_t *mt = get_state (pr, res, __FUNCTION__, P_INT (pr, 0)); + R_FLOAT (pr) = mtwist_rand_0_1 (mt); +} + +static void +bi_mtwist_rand_m1_1 (progs_t *pr, void *_res) +{ + mtwist_resources_t *res = _res; + mtstate_t *mt = get_state (pr, res, __FUNCTION__, P_INT (pr, 0)); + R_FLOAT (pr) = mtwist_rand_m1_1 (mt); +} + +static void +bi_mtwist_clear (progs_t *pr, void *_res) +{ + mtwist_resources_t *res = (mtwist_resources_t *) _res; + state_reset (res); +} + +static void +bi_mtwist_destroy (progs_t *pr, void *_res) +{ + free (_res); +} + +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) +static builtin_t builtins[] = { + bi(mtwist_new, 1, p(int)), + bi(mtwist_delete, 1, p(ptr)), + bi(mtwist_seed, 2, p(ptr), p(int)), + bi(mtwist_rand, 1, p(ptr)), + bi(mtwist_rand_0_1, 1, p(ptr)), + bi(mtwist_rand_m1_1, 1, p(ptr)), + {0} +}; + +void +RUA_Mersenne_Init (progs_t *pr, int secure) +{ + mtwist_resources_t *res = calloc (1, sizeof (mtwist_resources_t)); + + PR_Resources_Register (pr, "Mersenne Twister", res, bi_mtwist_clear, + bi_mtwist_destroy); + PR_RegisterBuiltins (pr, builtins, res); +} diff --git a/libs/ruamoko/rua_model.c b/libs/ruamoko/rua_model.c new file mode 100644 index 000000000..055462ec0 --- /dev/null +++ b/libs/ruamoko/rua_model.c @@ -0,0 +1,191 @@ +/* + bi_model.c + + Ruamkoko model builtins + + Copyright (C) 2022 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/model.h" +#include "QF/progs.h" + +#include "rua_internal.h" + +typedef struct rua_model_s { + struct rua_model_s *next; + struct rua_model_s **prev; + model_t *model; +} rua_model_t; + +typedef struct { + PR_RESMAP (rua_model_t) model_map; + rua_model_t *handles; + progs_t *pr; +} rua_model_resources_t; + +static rua_model_t * +rua_model_handle_new (rua_model_resources_t *res) +{ + return PR_RESNEW (res->model_map); +} + +static void +rua_model_handle_free (rua_model_resources_t *res, rua_model_t *handle) +{ + PR_RESFREE (res->model_map, handle); +} + +static void +rua_model_handle_reset (rua_model_resources_t *res) +{ + PR_RESRESET (res->model_map); +} + +static inline rua_model_t * __attribute__((pure)) +rua__model_handle_get (rua_model_resources_t *res, int index, const char *name) +{ + rua_model_t *handle = 0; + + handle = PR_RESGET(res->model_map, index); + if (!handle) { + PR_RunError (res->pr, "invalid model handle passed to %s", name + 3); + } + return handle; +} +#define rua_model_handle_get(res, index) \ + rua__model_handle_get (res, index, __FUNCTION__) + +static inline int __attribute__((pure)) +rua_model_handle_index (rua_model_resources_t *res, rua_model_t *handle) +{ + return PR_RESINDEX(res->model_map, handle); +} + +static void +bi_rua_model_clear (progs_t *pr, void *_res) +{ + rua_model_resources_t *res = (rua_model_resources_t *) _res; + rua_model_t *handle; + + for (handle = res->handles; handle; handle = handle->next) + Mod_UnloadModel (handle->model); + res->handles = 0; + rua_model_handle_reset (res); +} + +static void +bi_rua_model_destroy (progs_t *pr, void *_res) +{ + rua_model_resources_t *res = _res; + PR_RESDELMAP (res->model_map); + free (res); +} + +static int +alloc_handle (rua_model_resources_t *res, model_t *model) +{ + rua_model_t *handle = rua_model_handle_new (res); + + if (!handle) + return 0; + + handle->next = res->handles; + handle->prev = &res->handles; + if (res->handles) + res->handles->prev = &handle->next; + res->handles = handle; + handle->model = model; + return rua_model_handle_index (res, handle); +} + +static void +bi_Model_Load (progs_t *pr, void *_res) +{ + __auto_type res = (rua_model_resources_t *) _res; + const char *path = P_GSTRING (pr, 0); + model_t *model; + + R_INT (pr) = 0; + if (!(model = Mod_ForName (path, 0))) + return; + if (!(R_INT (pr) = alloc_handle (res, model))) + Mod_UnloadModel (model); +} + +model_t * +Model_GetModel (progs_t *pr, int handle) +{ + if (!handle) { + return 0; + } + rua_model_resources_t *res = PR_Resources_Find (pr, "Model"); + rua_model_t *h = rua_model_handle_get (res, handle); + + return h->model; +} + +static void +bi_Model_Unload (progs_t *pr, void *_res) +{ + __auto_type res = (rua_model_resources_t *) _res; + int handle = P_INT (pr, 0); + rua_model_t *h = rua_model_handle_get (res, handle); + + if (!h) + PR_RunError (pr, "invalid model handle passed to Qclose"); + Mod_UnloadModel (h->model); + *h->prev = h->next; + if (h->next) + h->next->prev = h->prev; + rua_model_handle_free (res, h); +} + +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) +#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } +static builtin_t builtins[] = { + bi(Model_Load, 1, p(string)), + bi(Model_Unload, 1, p(ptr)), + {0} +}; + +void +RUA_Model_Init (progs_t *pr, int secure) +{ + rua_model_resources_t *res = calloc (sizeof (rua_model_resources_t), 1); + res->pr = pr; + + PR_Resources_Register (pr, "Model", res, bi_rua_model_clear, + bi_rua_model_destroy); + PR_RegisterBuiltins (pr, builtins, res); +} diff --git a/libs/ruamoko/rua_msgbuf.c b/libs/ruamoko/rua_msgbuf.c index 9b026c90f..b8300d382 100644 --- a/libs/ruamoko/rua_msgbuf.c +++ b/libs/ruamoko/rua_msgbuf.c @@ -58,32 +58,32 @@ typedef struct { static msgbuf_t * msgbuf_new (msgbuf_resources_t *res) { - PR_RESNEW (msgbuf_t, res->msgbuf_map); + return PR_RESNEW (res->msgbuf_map); } static void msgbuf_free (progs_t *pr, msgbuf_resources_t *res, msgbuf_t *msgbuf) { PR_Zone_Free (pr, msgbuf->sizebuf.data); - PR_RESFREE (msgbuf_t, res->msgbuf_map, msgbuf); + PR_RESFREE (res->msgbuf_map, msgbuf); } static void msgbuf_reset (msgbuf_resources_t *res) { - PR_RESRESET (msgbuf_t, res->msgbuf_map); + PR_RESRESET (res->msgbuf_map); } static inline msgbuf_t * msgbuf_get (msgbuf_resources_t *res, int index) { - PR_RESGET(res->msgbuf_map, index); + return PR_RESGET(res->msgbuf_map, index); } -static inline int +static inline int __attribute__((pure)) msgbuf_index (msgbuf_resources_t *res, msgbuf_t *msgbuf) { - PR_RESINDEX(res->msgbuf_map, msgbuf); + return PR_RESINDEX(res->msgbuf_map, msgbuf); } static void @@ -94,6 +94,12 @@ bi_msgbuf_clear (progs_t *pr, void *data) msgbuf_reset (res); } +static void +bi_msgbuf_destroy (progs_t *pr, void *_res) +{ + free (_res); +} + static int alloc_msgbuf (msgbuf_resources_t *res, byte *buf, int size) { @@ -110,10 +116,9 @@ alloc_msgbuf (msgbuf_resources_t *res, byte *buf, int size) return msgbuf_index (res, msgbuf); } -static msgbuf_t * -get_msgbuf (progs_t *pr, const char *name, int msgbuf) +static msgbuf_t * __attribute__((pure)) +get_msgbuf (progs_t *pr, msgbuf_resources_t *res, const char *name, int msgbuf) { - msgbuf_resources_t *res = PR_Resources_Find (pr, "MsgBuf"); msgbuf_t *mb = msgbuf_get (res, msgbuf); if (!mb) @@ -122,7 +127,7 @@ get_msgbuf (progs_t *pr, const char *name, int msgbuf) } static void -bi_MsgBuf_New (progs_t *pr) +bi_MsgBuf_New (progs_t *pr, void *_res) { msgbuf_resources_t *res = PR_Resources_Find (pr, "MsgBuf"); int size = P_INT (pr, 0); @@ -133,17 +138,18 @@ bi_MsgBuf_New (progs_t *pr) } static void -bi_MsgBuf_Delete (progs_t *pr) +bi_MsgBuf_Delete (progs_t *pr, void *_res) { msgbuf_resources_t *res = PR_Resources_Find (pr, "MsgBuf"); - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); msgbuf_free (pr, res, mb); } static void -bi_MsgBuf_FromFile (progs_t *pr) +bi_MsgBuf_FromFile (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); QFile *file = QFile_GetFile (pr, P_INT (pr, 1)); int bytes; @@ -154,179 +160,204 @@ bi_MsgBuf_FromFile (progs_t *pr) } static void -bi_MsgBuf_MaxSize (progs_t *pr) +bi_MsgBuf_MaxSize (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); R_INT (pr) = mb->sizebuf.maxsize; } static void -bi_MsgBuf_CurSize (progs_t *pr) +bi_MsgBuf_CurSize (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); R_INT (pr) = mb->sizebuf.cursize; } static void -bi_MsgBuf_ReadCount (progs_t *pr) +bi_MsgBuf_ReadCount (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); R_INT (pr) = mb->msg.readcount; } static void -bi_MsgBuf_DataPtr (progs_t *pr) +bi_MsgBuf_DataPtr (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); byte *ptr = mb->sizebuf.data + mb->msg.readcount; R_INT (pr) = ptr - (byte *) pr->pr_strings; } static void -bi_MsgBuf_Clear (progs_t *pr) +bi_MsgBuf_Clear (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); SZ_Clear (&mb->sizebuf); } static void -bi_MsgBuf_WriteByte (progs_t *pr) +bi_MsgBuf_WriteByte (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); MSG_WriteByte (&mb->sizebuf, P_INT (pr, 1)); } static void -bi_MsgBuf_WriteShort (progs_t *pr) +bi_MsgBuf_WriteShort (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); MSG_WriteShort (&mb->sizebuf, P_INT (pr, 1)); } static void -bi_MsgBuf_WriteLong (progs_t *pr) +bi_MsgBuf_WriteLong (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); MSG_WriteLong (&mb->sizebuf, P_INT (pr, 1)); } static void -bi_MsgBuf_WriteFloat (progs_t *pr) +bi_MsgBuf_WriteFloat (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); MSG_WriteFloat (&mb->sizebuf, P_FLOAT (pr, 1)); } static void -bi_MsgBuf_WriteString (progs_t *pr) +bi_MsgBuf_WriteString (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); MSG_WriteString (&mb->sizebuf, P_GSTRING (pr, 1)); } #if 0 static void -bi_MsgBuf_WriteBytes (progs_t *pr) +bi_MsgBuf_WriteBytes (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); MSG_WriteBytes (&mb->sizebuf, P_INT (pr, 1)); } #endif static void -bi_MsgBuf_WriteCoord (progs_t *pr) +bi_MsgBuf_WriteCoord (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); MSG_WriteCoord (&mb->sizebuf, P_FLOAT (pr, 1)); } static void -bi_MsgBuf_WriteCoordV (progs_t *pr) +bi_MsgBuf_WriteCoordV (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); MSG_WriteCoordV (&mb->sizebuf, P_VECTOR (pr, 1)); } static void -bi_MsgBuf_WriteCoordAngleV (progs_t *pr) +bi_MsgBuf_WriteCoordAngleV (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); MSG_WriteCoordAngleV (&mb->sizebuf, P_VECTOR (pr, 1), P_VECTOR (pr, 2)); } static void -bi_MsgBuf_WriteAngle (progs_t *pr) +bi_MsgBuf_WriteAngle (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); MSG_WriteAngle (&mb->sizebuf, P_FLOAT (pr, 1)); } static void -bi_MsgBuf_WriteAngleV (progs_t *pr) +bi_MsgBuf_WriteAngleV (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); MSG_WriteAngleV (&mb->sizebuf, P_VECTOR (pr, 1)); } static void -bi_MsgBuf_WriteAngle16 (progs_t *pr) +bi_MsgBuf_WriteAngle16 (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); MSG_WriteAngle16 (&mb->sizebuf, P_FLOAT (pr, 1)); } static void -bi_MsgBuf_WriteAngle16V (progs_t *pr) +bi_MsgBuf_WriteAngle16V (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); MSG_WriteAngle16V (&mb->sizebuf, P_VECTOR (pr, 1)); } static void -bi_MsgBuf_WriteUTF8 (progs_t *pr) +bi_MsgBuf_WriteUTF8 (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); MSG_WriteUTF8 (&mb->sizebuf, P_INT (pr, 1)); } static void -bi_MsgBuf_BeginReading (progs_t *pr) +bi_MsgBuf_BeginReading (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); MSG_BeginReading (&mb->msg); } static void -bi_MsgBuf_ReadByte (progs_t *pr) +bi_MsgBuf_ReadByte (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); R_INT (pr) = MSG_ReadByte (&mb->msg); } static void -bi_MsgBuf_ReadShort (progs_t *pr) +bi_MsgBuf_ReadShort (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); R_INT (pr) = MSG_ReadShort (&mb->msg); } static void -bi_MsgBuf_ReadLong (progs_t *pr) +bi_MsgBuf_ReadLong (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); R_INT (pr) = MSG_ReadLong (&mb->msg); } static void -bi_MsgBuf_ReadFloat (progs_t *pr) +bi_MsgBuf_ReadFloat (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); R_FLOAT (pr) = MSG_ReadFloat (&mb->msg); } static void -bi_MsgBuf_ReadString (progs_t *pr) +bi_MsgBuf_ReadString (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); const char *str; str = MSG_ReadString (&mb->msg); @@ -334,109 +365,120 @@ bi_MsgBuf_ReadString (progs_t *pr) } #if 0 static void -bi_MsgBuf_ReadBytes (progs_t *pr) +bi_MsgBuf_ReadBytes (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); MSG_ReadBytes (&mb->msg); } #endif static void -bi_MsgBuf_ReadCoord (progs_t *pr) +bi_MsgBuf_ReadCoord (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); R_FLOAT (pr) = MSG_ReadCoord (&mb->msg); } static void -bi_MsgBuf_ReadCoordV (progs_t *pr) +bi_MsgBuf_ReadCoordV (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); MSG_ReadCoordV (&mb->msg, R_VECTOR (pr)); } static void -bi_MsgBuf_ReadCoordAngleV (progs_t *pr) +bi_MsgBuf_ReadCoordAngleV (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); - MSG_ReadCoordAngleV (&mb->msg, P_GPOINTER (pr, 1)->vector_var, - P_GPOINTER (pr, 2)->vector_var); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); + MSG_ReadCoordAngleV (&mb->msg, P_VECTOR (pr, 1), P_VECTOR (pr, 2)); } static void -bi_MsgBuf_ReadAngle (progs_t *pr) +bi_MsgBuf_ReadAngle (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); R_FLOAT (pr) = MSG_ReadAngle (&mb->msg); } static void -bi_MsgBuf_ReadAngleV (progs_t *pr) +bi_MsgBuf_ReadAngleV (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); MSG_ReadAngleV (&mb->msg, R_VECTOR (pr)); } static void -bi_MsgBuf_ReadAngle16 (progs_t *pr) +bi_MsgBuf_ReadAngle16 (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); R_FLOAT (pr) = MSG_ReadAngle16 (&mb->msg); } static void -bi_MsgBuf_ReadAngle16V (progs_t *pr) +bi_MsgBuf_ReadAngle16V (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); MSG_ReadAngle16V (&mb->msg, R_VECTOR (pr)); } static void -bi_MsgBuf_ReadUTF8 (progs_t *pr) +bi_MsgBuf_ReadUTF8 (progs_t *pr, void *_res) { - msgbuf_t *mb = get_msgbuf (pr, __FUNCTION__, P_INT (pr, 0)); + msgbuf_resources_t *res = _res; + msgbuf_t *mb = get_msgbuf (pr, res, __FUNCTION__, P_INT (pr, 0)); R_INT (pr) = MSG_ReadUTF8 (&mb->msg); } +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) +#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } static builtin_t builtins[] = { - {"MsgBuf_New", bi_MsgBuf_New, -1}, - {"MsgBuf_Delete", bi_MsgBuf_Delete, -1}, - {"MsgBuf_FromFile", bi_MsgBuf_FromFile, -1}, - {"MsgBuf_MaxSize", bi_MsgBuf_MaxSize, -1}, - {"MsgBuf_CurSize", bi_MsgBuf_CurSize, -1}, - {"MsgBuf_ReadCount", bi_MsgBuf_ReadCount, -1}, - {"MsgBuf_DataPtr", bi_MsgBuf_DataPtr, -1}, + bi(MsgBuf_New, 1, p(int)), + bi(MsgBuf_Delete, 1, p(ptr)), + bi(MsgBuf_FromFile, 2, p(ptr), p(ptr)), + bi(MsgBuf_MaxSize, 1, p(ptr)), + bi(MsgBuf_CurSize, 1, p(ptr)), + bi(MsgBuf_ReadCount, 1, p(ptr)), + bi(MsgBuf_DataPtr, 1, p(ptr)), - {"MsgBuf_Clear", bi_MsgBuf_Clear, -1}, - {"MsgBuf_WriteByte", bi_MsgBuf_WriteByte, -1}, - {"MsgBuf_WriteShort", bi_MsgBuf_WriteShort, -1}, - {"MsgBuf_WriteLong", bi_MsgBuf_WriteLong, -1}, - {"MsgBuf_WriteFloat", bi_MsgBuf_WriteFloat, -1}, - {"MsgBuf_WriteString", bi_MsgBuf_WriteString, -1}, -// {"MsgBuf_WriteBytes", bi_MsgBuf_WriteBytes, -1}, - {"MsgBuf_WriteCoord", bi_MsgBuf_WriteCoord, -1}, - {"MsgBuf_WriteCoordV", bi_MsgBuf_WriteCoordV, -1}, - {"MsgBuf_WriteCoordAngleV", bi_MsgBuf_WriteCoordAngleV, -1}, - {"MsgBuf_WriteAngle", bi_MsgBuf_WriteAngle, -1}, - {"MsgBuf_WriteAngleV", bi_MsgBuf_WriteAngleV, -1}, - {"MsgBuf_WriteAngle16", bi_MsgBuf_WriteAngle16, -1}, - {"MsgBuf_WriteAngle16V", bi_MsgBuf_WriteAngle16V, -1}, - {"MsgBuf_WriteUTF8", bi_MsgBuf_WriteUTF8, -1}, + bi(MsgBuf_Clear, 1, p(ptr)), + bi(MsgBuf_WriteByte, 2, p(ptr), p(int)), + bi(MsgBuf_WriteShort, 2, p(ptr), p(int)), + bi(MsgBuf_WriteLong, 2, p(ptr), p(int)), + bi(MsgBuf_WriteFloat, 2, p(ptr), p(float)), + bi(MsgBuf_WriteString, 2, p(ptr), p(string)), +// bi(MsgBuf_WriteBytes, _, _), + bi(MsgBuf_WriteCoord, 2, p(ptr), p(float)), + bi(MsgBuf_WriteCoordV, 2, p(ptr), p(vector)), + bi(MsgBuf_WriteCoordAngleV, 2, p(ptr), p(vector)), + bi(MsgBuf_WriteAngle, 2, p(ptr), p(float)), + bi(MsgBuf_WriteAngleV, 2, p(ptr), p(vector)), + bi(MsgBuf_WriteAngle16, 2, p(ptr), p(float)), + bi(MsgBuf_WriteAngle16V, 2, p(ptr), p(vector)), + bi(MsgBuf_WriteUTF8, 2, p(ptr), p(int)), - {"MsgBuf_BeginReading", bi_MsgBuf_BeginReading, -1}, - {"MsgBuf_ReadByte", bi_MsgBuf_ReadByte, -1}, - {"MsgBuf_ReadShort", bi_MsgBuf_ReadShort, -1}, - {"MsgBuf_ReadLong", bi_MsgBuf_ReadLong, -1}, - {"MsgBuf_ReadFloat", bi_MsgBuf_ReadFloat, -1}, - {"MsgBuf_ReadString", bi_MsgBuf_ReadString, -1}, -// {"MsgBuf_ReadBytes", bi_MsgBuf_ReadBytes, -1}, - {"MsgBuf_ReadCoord", bi_MsgBuf_ReadCoord, -1}, - {"MsgBuf_ReadCoordV", bi_MsgBuf_ReadCoordV, -1}, - {"MsgBuf_ReadCoordAngleV", bi_MsgBuf_ReadCoordAngleV, -1}, - {"MsgBuf_ReadAngle", bi_MsgBuf_ReadAngle, -1}, - {"MsgBuf_ReadAngleV", bi_MsgBuf_ReadAngleV, -1}, - {"MsgBuf_ReadAngle16", bi_MsgBuf_ReadAngle16, -1}, - {"MsgBuf_ReadAngle16V", bi_MsgBuf_ReadAngle16V, -1}, - {"MsgBuf_ReadUTF8", bi_MsgBuf_ReadUTF8, -1}, + bi(MsgBuf_BeginReading, 1, p(ptr)), + bi(MsgBuf_ReadByte, 1, p(ptr)), + bi(MsgBuf_ReadShort, 1, p(ptr)), + bi(MsgBuf_ReadLong, 1, p(ptr)), + bi(MsgBuf_ReadFloat, 1, p(ptr)), + bi(MsgBuf_ReadString, 1, p(ptr)), +// bi(MsgBuf_ReadBytes, _, _), + bi(MsgBuf_ReadCoord, 1, p(ptr)), + bi(MsgBuf_ReadCoordV, 1, p(ptr)), + bi(MsgBuf_ReadCoordAngleV, 2, p(ptr), p(ptr)), + bi(MsgBuf_ReadAngle, 1, p(ptr)), + bi(MsgBuf_ReadAngleV, 1, p(ptr)), + bi(MsgBuf_ReadAngle16, 1, p(ptr)), + bi(MsgBuf_ReadAngle16V, 1, p(ptr)), + bi(MsgBuf_ReadUTF8, 1, p(ptr)), {0} }; @@ -445,6 +487,7 @@ RUA_MsgBuf_Init (progs_t *pr, int secure) { msgbuf_resources_t *res = calloc (sizeof (msgbuf_resources_t), 1); - PR_Resources_Register (pr, "MsgBuf", res, bi_msgbuf_clear); - PR_RegisterBuiltins (pr, builtins); + PR_Resources_Register (pr, "MsgBuf", res, bi_msgbuf_clear, + bi_msgbuf_destroy); + PR_RegisterBuiltins (pr, builtins, res); } diff --git a/libs/ruamoko/rua_obj.c b/libs/ruamoko/rua_obj.c index ed3a1d78b..f1d54d346 100644 --- a/libs/ruamoko/rua_obj.c +++ b/libs/ruamoko/rua_obj.c @@ -45,43 +45,173 @@ #include "qfalloca.h" #include "QF/cvar.h" +#include "QF/darray.h" #include "QF/dstring.h" #include "QF/hash.h" #include "QF/mathlib.h" -#include "QF/pr_obj.h" #include "QF/progs.h" #include "QF/ruamoko.h" #include "QF/sys.h" +#include "QF/progs/pr_obj.h" +#include "QF/progs/pr_type.h" + #include "compat.h" #include "rua_internal.h" +#define always_inline inline __attribute__((__always_inline__)) + +/* Macros to help with setting up to call a function, and cleaning up + * afterwards. The problem is that PR_CallFunction saves the CURRENT stack + * pointer, which has been adjusted by PR_SetupParams in order to push + * the function arguments. + * + * RUA_CALL_BEGIN and RUA_CALL_END must be used in pairs and this is enforced + * by the unbalanced {}s in the macros. + */ +#define RUA_CALL_BEGIN(pr, argc) \ + { \ + pr_ptr_t saved_stack = 0; \ + int call_depth = (pr)->pr_depth; \ + if ((pr)->globals.stack) { \ + saved_stack = *(pr)->globals.stack; \ + } \ + PR_SetupParams (pr, argc, 1); \ + (pr)->pr_argc = argc; + +#define RUA_CALL_END(pr, imp) \ + if (PR_CallFunction ((pr), (imp), (pr)->pr_return)) { \ + /* the call is to a progs function so a frame was pushed, */ \ + /* ensure the stack pointer is restored on return */ \ + /* if there's no stack, then the following is effectively a */ \ + /* noop */ \ + (pr)->pr_stack[call_depth].stack_ptr = saved_stack; \ + } else if ((pr)->globals.stack) { \ + /* the call was to a builtin, restore the stack */ \ + *(pr)->globals.stack = saved_stack; \ + } \ + } + +// number of selectors to allocate at once (to minimize memory waste) +#define SELECTOR_BLOCK 64 + typedef struct obj_list_s { struct obj_list_s *next; void *data; } obj_list; -static obj_list *obj_list_free_list; +typedef struct listset_s DARRAY_TYPE (obj_list *) listset_t; + +typedef struct dtable_s { + struct dtable_s *next; + struct dtable_s **prev; + size_t size; + pr_func_t *imp; +} dtable_t; + +typedef struct class_tree { + pr_class_t *class; + obj_list *subclasses; +} class_tree; + +typedef struct treeset_s DARRAY_TYPE (class_tree *) treeset_t; + +typedef struct probj_resources_s { + progs_t *pr; + unsigned selector_index; + unsigned selector_index_max; + pr_sel_t *selector_block; + int available_selectors; + obj_list **selector_sels; + pr_string_t *selector_names; + pr_int_t *selector_argc; + PR_RESMAP (dtable_t) dtables; + dtable_t *dtable_list; + pr_func_t obj_forward; + pr_sel_t *forward_selector; + dstring_t *msg; + hashtab_t *selector_hash; + hashtab_t *classes; + hashtab_t *protocols; + hashtab_t *load_methods; + listset_t obj_list_sets; + obj_list *obj_list_free_list; + obj_list *unresolved_classes; + obj_list *unclaimed_categories; + obj_list *uninitialized_statics; + obj_list *unclaimed_proto_list; + obj_list *module_list; + obj_list *class_tree_list; + + class_tree *class_tree_free_list; + treeset_t class_tree_sets; +} probj_t; + +static dtable_t * +dtable_new (probj_t *probj) +{ + dtable_t *dtable = PR_RESNEW (probj->dtables); + dtable->next = probj->dtable_list; + dtable->prev = &probj->dtable_list; + if (probj->dtable_list) { + probj->dtable_list->prev = &dtable->next; + } + probj->dtable_list = dtable; + return dtable; +} + +static void +dtable_reset (probj_t *probj) +{ + PR_RESRESET (probj->dtables); + probj->dtable_list = 0; +} + +static inline dtable_t * +dtable_get (probj_t *probj, int index) +{ + return PR_RESGET (probj->dtables, index); +} + +static inline int __attribute__((pure)) +dtable_index (probj_t *probj, dtable_t *dtable) +{ + return PR_RESINDEX (probj->dtables, dtable); +} + +static always_inline dtable_t * __attribute__((pure)) +get_dtable (probj_t *probj, const char *name, int index) +{ + dtable_t *dtable = dtable_get (probj, index); + + if (!dtable) { + PR_RunError (probj->pr, "invalid dtable index in %s", name); + } + return dtable; +} static obj_list * -obj_list_new (void) +obj_list_new (probj_t *probj) { int i; obj_list *l; - if (!obj_list_free_list) { - obj_list_free_list = calloc (128, sizeof (obj_list)); - for (i = 0; i < 127; i++) - obj_list_free_list[i].next = &obj_list_free_list[i + 1]; + if (!probj->obj_list_free_list) { + probj->obj_list_free_list = calloc (128, sizeof (obj_list)); + for (i = 0; i < 127; i++) { + __auto_type next = &probj->obj_list_free_list[i + 1]; + probj->obj_list_free_list[i].next = next; + } + DARRAY_APPEND (&probj->obj_list_sets, probj->obj_list_free_list); } - l = obj_list_free_list; - obj_list_free_list = l->next; + l = probj->obj_list_free_list; + probj->obj_list_free_list = l->next; l->next = 0; return l; } static void -obj_list_free (obj_list *l) +obj_list_free (probj_t *probj, obj_list *l) { obj_list *e; @@ -90,61 +220,55 @@ obj_list_free (obj_list *l) for (e = l; e->next; e = e->next) ; - e->next = obj_list_free_list; - obj_list_free_list = l; + e->next = probj->obj_list_free_list; + probj->obj_list_free_list = l; } static inline obj_list * -list_cons (void *data, obj_list *next) +list_cons (probj_t *probj, void *data, obj_list *next) { - obj_list *l = obj_list_new (); + obj_list *l = obj_list_new (probj); l->data = data; l->next = next; return l; } static inline void -list_remove (obj_list **list) +list_remove (probj_t *probj, obj_list **list) { if ((*list)->next) { obj_list *l = *list; *list = (*list)->next; l->next = 0; - obj_list_free (l); + obj_list_free (probj, l); } else { - obj_list_free (*list); + obj_list_free (probj, *list); *list = 0; } } -typedef struct class_tree { - pr_class_t *class; - obj_list *subclasses; -} class_tree; - -class_tree *class_tree_free_list; - static class_tree * -class_tree_new (void) +class_tree_new (probj_t *probj) { int i; class_tree *t; - if (!class_tree_free_list) { - class_tree_free_list = calloc (128, sizeof (class_tree)); + if (!probj->class_tree_free_list) { + probj->class_tree_free_list = calloc (128, sizeof (class_tree)); for (i = 0; i < 127; i++) { - class_tree *x = &class_tree_free_list[i]; + class_tree *x = &probj->class_tree_free_list[i]; x->subclasses = (obj_list *) (x + 1); } + DARRAY_APPEND (&probj->class_tree_sets, probj->class_tree_free_list); } - t = class_tree_free_list; - class_tree_free_list = (class_tree *) t->subclasses; + t = probj->class_tree_free_list; + probj->class_tree_free_list = (class_tree *) t->subclasses; t->subclasses = 0; return t; } static int -class_is_subclass_of_class (progs_t *pr, pr_class_t *class, +class_is_subclass_of_class (probj_t *probj, pr_class_t *class, pr_class_t *superclass) { while (class) { @@ -152,29 +276,31 @@ class_is_subclass_of_class (progs_t *pr, pr_class_t *class, return 1; if (!class->super_class) break; - class = Hash_Find (pr->classes, PR_GetString (pr, class->super_class)); + class = Hash_Find (probj->classes, + PR_GetString (probj->pr, class->super_class)); } return 0; } static class_tree * -create_tree_of_subclasses_inherited_from (progs_t *pr, pr_class_t *bottom, +create_tree_of_subclasses_inherited_from (probj_t *probj, pr_class_t *bottom, pr_class_t *upper) { + progs_t *pr = probj->pr; const char *super_class = PR_GetString (pr, bottom->super_class); pr_class_t *superclass; class_tree *tree, *prev; - superclass = bottom->super_class ? Hash_Find (pr->classes, super_class) + superclass = bottom->super_class ? Hash_Find (probj->classes, super_class) : 0; - tree = prev = class_tree_new (); + tree = prev = class_tree_new (probj); prev->class = bottom; while (superclass != upper) { - tree = class_tree_new (); + tree = class_tree_new (probj); tree->class = superclass; - tree->subclasses = list_cons (prev, tree->subclasses); + tree->subclasses = list_cons (probj, prev, tree->subclasses); super_class = PR_GetString (pr, superclass->super_class); - superclass = (superclass->super_class ? Hash_Find (pr->classes, + superclass = (superclass->super_class ? Hash_Find (probj->classes, super_class) : 0); prev = tree; @@ -183,16 +309,17 @@ create_tree_of_subclasses_inherited_from (progs_t *pr, pr_class_t *bottom, } static class_tree * -_obj_tree_insert_class (progs_t *pr, class_tree *tree, pr_class_t *class) +_obj_tree_insert_class (probj_t *probj, class_tree *tree, pr_class_t *class) { + progs_t *pr = probj->pr; obj_list *subclasses; class_tree *new_tree; if (!tree) - return create_tree_of_subclasses_inherited_from (pr, class, 0); + return create_tree_of_subclasses_inherited_from (probj, class, 0); if (class == tree->class) return tree; - if ((class->super_class ? Hash_Find (pr->classes, + if ((class->super_class ? Hash_Find (probj->classes, PR_GetString (pr, class->super_class)) : 0) == tree->class) { @@ -204,37 +331,37 @@ _obj_tree_insert_class (progs_t *pr, class_tree *tree, pr_class_t *class) return tree; list = list->next; } - node = class_tree_new (); + node = class_tree_new (probj); node->class = class; - tree->subclasses = list_cons (node, tree->subclasses); + tree->subclasses = list_cons (probj, node, tree->subclasses); return tree; } - if (!class_is_subclass_of_class (pr, class, tree->class)) + if (!class_is_subclass_of_class (probj, class, tree->class)) return 0; for (subclasses = tree->subclasses; subclasses; subclasses = subclasses->next) { pr_class_t *aclass = ((class_tree *)subclasses->data)->class; - if (class_is_subclass_of_class (pr, class, aclass)) { - subclasses->data = _obj_tree_insert_class (pr, subclasses->data, + if (class_is_subclass_of_class (probj, class, aclass)) { + subclasses->data = _obj_tree_insert_class (probj, subclasses->data, class); return tree; } } - new_tree = create_tree_of_subclasses_inherited_from (pr, class, + new_tree = create_tree_of_subclasses_inherited_from (probj, class, tree->class); - tree->subclasses = list_cons (new_tree, tree->subclasses); + tree->subclasses = list_cons (probj, new_tree, tree->subclasses); return tree; } static void -obj_tree_insert_class (progs_t *pr, pr_class_t *class) +obj_tree_insert_class (probj_t *probj, pr_class_t *class) { obj_list *list_node; class_tree *tree; - list_node = pr->class_tree_list; + list_node = probj->class_tree_list; while (list_node) { - tree = _obj_tree_insert_class (pr, list_node->data, class); + tree = _obj_tree_insert_class (probj, list_node->data, class); if (tree) { list_node->data = tree; break; @@ -243,73 +370,83 @@ obj_tree_insert_class (progs_t *pr, pr_class_t *class) } } if (!list_node) { - tree = _obj_tree_insert_class (pr, 0, class); - pr->class_tree_list = list_cons (tree, pr->class_tree_list); + tree = _obj_tree_insert_class (probj, 0, class); + probj->class_tree_list = list_cons (probj, tree, + probj->class_tree_list); } } static void -obj_create_classes_tree (progs_t *pr, pr_module_t *module) +obj_create_classes_tree (probj_t *probj, pr_module_t *module) { + progs_t *pr = probj->pr; pr_symtab_t *symtab = &G_STRUCT (pr, pr_symtab_t, module->symtab); int i; for (i = 0; i < symtab->cls_def_cnt; i++) { pr_class_t *class = &G_STRUCT (pr, pr_class_t, symtab->defs[i]); - obj_tree_insert_class (pr, class); + obj_tree_insert_class (probj, class); } } static void -obj_destroy_class_tree_node (progs_t *pr, class_tree *tree, int level) +obj_destroy_class_tree_node (probj_t *probj, class_tree *tree, int level) { - tree->subclasses = (obj_list *) class_tree_free_list; - class_tree_free_list = tree; + tree->subclasses = (obj_list *) probj->class_tree_free_list; + probj->class_tree_free_list = tree; } static void -obj_preorder_traverse (progs_t *pr, class_tree *tree, int level, - void (*func) (progs_t *, class_tree *, int)) +obj_preorder_traverse (probj_t *probj, class_tree *tree, int level, + void (*func) (probj_t *, class_tree *, int)) { obj_list *node; - func (pr, tree, level); + func (probj, tree, level); for (node = tree->subclasses; node; node = node->next) - obj_preorder_traverse (pr, node->data, level + 1, func); + obj_preorder_traverse (probj, node->data, level + 1, func); } static void -obj_postorder_traverse (progs_t *pr, class_tree *tree, int level, - void (*func) (progs_t *, class_tree *, int)) +obj_postorder_traverse (probj_t *probj, class_tree *tree, int level, + void (*func) (probj_t *, class_tree *, int)) { obj_list *node; for (node = tree->subclasses; node; node = node->next) - obj_postorder_traverse (pr, node->data, level + 1, func); - func (pr, tree, level); + obj_postorder_traverse (probj, node->data, level + 1, func); + func (probj, tree, level); } static const char * -selector_get_key (const void *s, void *_pr) +selector_get_key (const void *s, void *_probj) { - progs_t *pr = (progs_t *) _pr; - return PR_GetString (pr, pr->selector_names[(intptr_t) s]); + __auto_type probj = (probj_t *) _probj; + return PR_GetString (probj->pr, probj->selector_names[(intptr_t) s]); } static const char * -class_get_key (const void *c, void *pr) +class_get_key (const void *c, void *_probj) { - return PR_GetString ((progs_t *)pr, ((pr_class_t *)c)->name); + __auto_type probj = (probj_t *) _probj; + return PR_GetString (probj->pr, ((pr_class_t *)c)->name); +} + +static const char * +protocol_get_key (const void *p, void *_probj) +{ + __auto_type probj = (probj_t *) _probj; + return PR_GetString (probj->pr, ((pr_protocol_t *)p)->protocol_name); } static uintptr_t -load_methods_get_hash (const void *m, void *pr) +load_methods_get_hash (const void *m, void *_probj) { return (uintptr_t) m; } static int -load_methods_compare (const void *m1, const void *m2, void *pr) +load_methods_compare (const void *m1, const void *m2, void *_probj) { return m1 == m2; } @@ -323,8 +460,9 @@ sel_eq (pr_sel_t *s1, pr_sel_t *s2) } static int -object_is_instance (progs_t *pr, pr_id_t *object) +object_is_instance (probj_t *probj, pr_id_t *object) { + progs_t *pr = probj->pr; pr_class_t *class; if (object) { @@ -334,9 +472,10 @@ object_is_instance (progs_t *pr, pr_id_t *object) return 0; } -static string_t -object_get_class_name (progs_t *pr, pr_id_t *object) +static pr_string_t +object_get_class_name (probj_t *probj, pr_id_t *object) { + progs_t *pr = probj->pr; pr_class_t *class; if (object) { @@ -356,8 +495,9 @@ object_get_class_name (progs_t *pr, pr_id_t *object) //==================================================================== static void -finish_class (progs_t *pr, pr_class_t *class, pointer_t object_ptr) +finish_class (probj_t *probj, pr_class_t *class, pr_ptr_t object_ptr) { + progs_t *pr = probj->pr; pr_class_t *meta = &G_STRUCT (pr, pr_class_t, class->class_pointer); pr_class_t *val; @@ -365,58 +505,61 @@ finish_class (progs_t *pr, pr_class_t *class, pointer_t object_ptr) if (class->super_class) { const char *super_class = PR_GetString (pr, class->super_class); const char *class_name = PR_GetString (pr, class->name); - val = Hash_Find (pr->classes, super_class); + val = Hash_Find (probj->classes, super_class); if (!val) PR_Error (pr, "broken class %s: super class %s not found", class_name, super_class); meta->super_class = val->class_pointer; class->super_class = PR_SetPointer (pr, val); } else { - pointer_t *ml = &meta->methods; + pr_ptr_t *ml = &meta->methods; while (*ml) ml = &G_STRUCT (pr, pr_method_list_t, *ml).method_next; *ml = class->methods; } - Sys_MaskPrintf (SYS_RUA_OBJ, " %x %x %x\n", meta->class_pointer, + Sys_MaskPrintf (SYS_rua_obj, " %x %x %x\n", meta->class_pointer, meta->super_class, class->super_class); } //==================================================================== static int -add_sel_name (progs_t *pr, const char *name) +add_sel_name (probj_t *probj, const char *name) { - int ind = ++pr->selector_index; + int ind = ++probj->selector_index; int size, i; - if (pr->selector_index >= pr->selector_index_max) { - size = pr->selector_index_max + 128; - pr->selector_sels = realloc (pr->selector_sels, - size * sizeof (obj_list *)); - pr->selector_names = realloc (pr->selector_names, - size * sizeof (string_t)); - for (i = pr->selector_index_max; i < size; i++) { - pr->selector_sels[i] = 0; - pr->selector_names[i] = 0; + if (probj->selector_index >= probj->selector_index_max) { + size = probj->selector_index_max + 128; + probj->selector_sels = realloc (probj->selector_sels, + size * sizeof (obj_list *)); + probj->selector_names = realloc (probj->selector_names, + size * sizeof (pr_string_t)); + probj->selector_argc = realloc (probj->selector_argc, + size * sizeof (pr_int_t)); + for (i = probj->selector_index_max; i < size; i++) { + probj->selector_sels[i] = 0; + probj->selector_names[i] = 0; + probj->selector_argc[i] = 0; } - pr->selector_index_max = size; + probj->selector_index_max = size; } - pr->selector_names[ind] = PR_SetString (pr, name); + probj->selector_names[ind] = PR_SetString (probj->pr, name); return ind; } static pr_sel_t * -sel_register_typed_name (progs_t *pr, const char *name, const char *types, +sel_register_typed_name (probj_t *probj, const char *name, const char *types, pr_sel_t *sel) { + progs_t *pr = probj->pr; intptr_t index; int is_new = 0; obj_list *l; - Sys_MaskPrintf (SYS_RUA_OBJ, " Registering SEL %s %s\n", name, types); - index = (intptr_t) Hash_Find (pr->selector_hash, name); + index = (intptr_t) Hash_Find (probj->selector_hash, name); if (index) { - for (l = pr->selector_sels[index]; l; l = l->next) { + for (l = probj->selector_sels[index]; l; l = l->next) { pr_sel_t *s = l->data; if (!types || !s->sel_types) { if (!s->sel_types && !types) { @@ -435,63 +578,131 @@ sel_register_typed_name (progs_t *pr, const char *name, const char *types, } } } else { - index = add_sel_name (pr, name); + Sys_MaskPrintf (SYS_rua_obj, " Registering SEL %s %s\n", + name, types); + index = add_sel_name (probj, name); is_new = 1; } - if (!sel) - sel = PR_Zone_Malloc (pr, sizeof (pr_sel_t)); + if (!sel) { + if (!probj->available_selectors) { + probj->available_selectors = SELECTOR_BLOCK; + sel = PR_Zone_Malloc (pr, SELECTOR_BLOCK*sizeof (pr_sel_t)); + probj->selector_block = sel; + } + sel = &probj->selector_block[--probj->available_selectors]; + } sel->sel_id = index; sel->sel_types = PR_SetString (pr, types); - l = obj_list_new (); + l = obj_list_new (probj); l->data = sel; - l->next = pr->selector_sels[index]; - pr->selector_sels[index] = l; + l->next = probj->selector_sels[index]; + probj->selector_sels[index] = l; + + if (sel->sel_types && pr->type_encodings) { + const char *enc = PR_GetString (pr, sel->sel_types); + __auto_type type = (qfot_type_t *) Hash_Find (pr->type_hash, enc); + if (type->meta != ty_basic || type->type != ev_func) { + PR_RunError (pr, "selector type encoing is not a function"); + } + probj->selector_argc[index] = type->func.num_params; + } if (is_new) - Hash_Add (pr->selector_hash, (void *) index); + Hash_Add (probj->selector_hash, (void *) index); done: - Sys_MaskPrintf (SYS_RUA_OBJ, " %d @ %x\n", + Sys_MaskPrintf (SYS_rua_obj, " %d @ %x\n", sel->sel_id, PR_SetPointer (pr, sel)); return sel; } static pr_sel_t * -sel_register_name (progs_t *pr, const char *name) +sel_register_name (probj_t *probj, const char *name) { - return sel_register_typed_name (pr, name, "", 0); + return sel_register_typed_name (probj, name, "", 0); } static void -register_selectors_from_list (progs_t *pr, pr_method_list_t *method_list) +obj_register_selectors_from_description_list (probj_t *probj, + pr_method_description_list_t *method_list) { + progs_t *pr = probj->pr; + int i; + + if (!method_list) { + return; + } + for (i = 0; i < method_list->count; i++) { + pr_method_description_t *method = &method_list->list[i]; + const char *name = PR_GetString (pr, method->name); + const char *types = PR_GetString (pr, method->types); + pr_sel_t *sel = sel_register_typed_name (probj, name, types, 0); + method->name = PR_SetPointer (pr, sel); + } +} + +static void +obj_register_selectors_from_list (probj_t *probj, + pr_method_list_t *method_list) +{ + progs_t *pr = probj->pr; int i; for (i = 0; i < method_list->method_count; i++) { pr_method_t *method = &method_list->method_list[i]; const char *name = PR_GetString (pr, method->method_name); const char *types = PR_GetString (pr, method->method_types); - pr_sel_t *sel = sel_register_typed_name (pr, name, types, 0); + pr_sel_t *sel = sel_register_typed_name (probj, name, types, 0); method->method_name = PR_SetPointer (pr, sel); } } static void -obj_register_selectors_from_class (progs_t *pr, pr_class_t *class) +obj_register_selectors_from_class (probj_t *probj, pr_class_t *class) { + progs_t *pr = probj->pr; pr_method_list_t *method_list = &G_STRUCT (pr, pr_method_list_t, class->methods); while (method_list) { - register_selectors_from_list (pr, method_list); + obj_register_selectors_from_list (probj, method_list); method_list = &G_STRUCT (pr, pr_method_list_t, method_list->method_next); } } +static void obj_init_protocols (probj_t *probj, pr_protocol_list_t *protos); + static void -obj_init_protocols (progs_t *pr, pr_protocol_list_t *protos) +obj_init_protocol (probj_t *probj, pr_class_t *proto_class, + pr_protocol_t *proto) { + progs_t *pr = probj->pr; + + if (!proto->class_pointer) { + const char *protocol_name = PR_GetString (pr, proto->protocol_name); + proto->class_pointer = PR_SetPointer (pr, proto_class); + obj_register_selectors_from_description_list (probj, + &G_STRUCT (pr, pr_method_description_list_t, + proto->instance_methods)); + obj_register_selectors_from_description_list (probj, + &G_STRUCT (pr, pr_method_description_list_t, + proto->class_methods)); + if (!Hash_Find (probj->protocols, protocol_name)) { + Hash_Add (probj->protocols, proto); + } + obj_init_protocols (probj, &G_STRUCT (pr, pr_protocol_list_t, + proto->protocol_list)); + } else { + if (proto->class_pointer != PR_SetPointer (pr, proto_class)) + PR_RunError (pr, "protocol broken"); + } +} + +static void +obj_init_protocols (probj_t *probj, pr_protocol_list_t *protos) +{ + progs_t *pr = probj->pr; pr_class_t *proto_class; pr_protocol_t *proto; int i; @@ -499,28 +710,23 @@ obj_init_protocols (progs_t *pr, pr_protocol_list_t *protos) if (!protos) return; - if (!(proto_class = Hash_Find (pr->classes, "Protocol"))) { - pr->unclaimed_proto_list = list_cons (protos, - pr->unclaimed_proto_list); + if (!(proto_class = Hash_Find (probj->classes, "Protocol"))) { + probj->unclaimed_proto_list = list_cons (probj, protos, + probj->unclaimed_proto_list); return; } for (i = 0; i < protos->count; i++) { proto = &G_STRUCT (pr, pr_protocol_t, protos->list[i]); - if (!proto->class_pointer) { - proto->class_pointer = PR_SetPointer (pr, proto_class); - obj_init_protocols (pr, &G_STRUCT (pr, pr_protocol_list_t, - proto->protocol_list)); - } else { - if (proto->class_pointer != PR_SetPointer (pr, proto_class)) - PR_RunError (pr, "protocol broken"); - } + obj_init_protocol (probj, proto_class, proto); } } static void -class_add_method_list (progs_t *pr, pr_class_t *class, pr_method_list_t *list) +class_add_method_list (probj_t *probj, pr_class_t *class, + pr_method_list_t *list) { + progs_t *pr = probj->pr; int i; for (i = 0; i < list->method_count; i++) { @@ -528,7 +734,7 @@ class_add_method_list (progs_t *pr, pr_class_t *class, pr_method_list_t *list) if (method->method_name) { const char *name = PR_GetString (pr, method->method_name); const char *types = PR_GetString (pr, method->method_types); - pr_sel_t *sel = sel_register_typed_name (pr, name, types, 0); + pr_sel_t *sel = sel_register_typed_name (probj, name, types, 0); method->method_name = PR_SetPointer (pr, sel); } } @@ -538,7 +744,7 @@ class_add_method_list (progs_t *pr, pr_class_t *class, pr_method_list_t *list) } static void -obj_class_add_protocols (progs_t *pr, pr_class_t *class, +obj_class_add_protocols (probj_t *probj, pr_class_t *class, pr_protocol_list_t *protos) { if (!protos) @@ -549,50 +755,54 @@ obj_class_add_protocols (progs_t *pr, pr_class_t *class, } static void -finish_category (progs_t *pr, pr_category_t *category, pr_class_t *class) +finish_category (probj_t *probj, pr_category_t *category, pr_class_t *class) { + progs_t *pr = probj->pr; pr_method_list_t *method_list; pr_protocol_list_t *protocol_list; if (category->instance_methods) { method_list = &G_STRUCT (pr, pr_method_list_t, category->instance_methods); - class_add_method_list (pr, class, method_list); + class_add_method_list (probj, class, method_list); } if (category->class_methods) { pr_class_t *meta = &G_STRUCT (pr, pr_class_t, class->class_pointer); method_list = &G_STRUCT (pr, pr_method_list_t, category->class_methods); - class_add_method_list (pr, meta, method_list); + class_add_method_list (probj, meta, method_list); } if (category->protocols) { protocol_list = &G_STRUCT (pr, pr_protocol_list_t, category->protocols); - obj_init_protocols (pr, protocol_list); - obj_class_add_protocols (pr, class, protocol_list); + obj_init_protocols (probj, protocol_list); + obj_class_add_protocols (probj, class, protocol_list); } } static void -obj_send_message_in_list (progs_t *pr, pr_method_list_t *method_list, +obj_send_message_in_list (probj_t *probj, pr_method_list_t *method_list, pr_class_t *class, pr_sel_t *op) { + progs_t *pr = probj->pr; int i; if (!method_list) return; - obj_send_message_in_list (pr, &G_STRUCT (pr, pr_method_list_t, - method_list->method_next), + obj_send_message_in_list (probj, &G_STRUCT (pr, pr_method_list_t, + method_list->method_next), class, op); for (i = 0; i < method_list->method_count; i++) { pr_method_t *mth = &method_list->method_list[i]; if (mth->method_name && sel_eq (&G_STRUCT (pr, pr_sel_t, mth->method_name), op) - && !Hash_FindElement (pr->load_methods, (void *) (intptr_t) mth->method_imp)) { - Hash_AddElement (pr->load_methods, (void *) (intptr_t) mth->method_imp); - + && !Hash_FindElement (probj->load_methods, + (void *) (intptr_t) mth->method_imp)) { + Hash_AddElement (probj->load_methods, + (void *) (intptr_t) mth->method_imp); + //FIXME need to wrap in save/restore params? PR_ExecuteProgram (pr, mth->method_imp); break; } @@ -600,93 +810,97 @@ obj_send_message_in_list (progs_t *pr, pr_method_list_t *method_list, } static void -send_load (progs_t *pr, class_tree *tree, int level) +send_load (probj_t *probj, class_tree *tree, int level) { - pr_sel_t *load_sel = sel_register_name (pr, "load"); + progs_t *pr = probj->pr; + pr_sel_t *load_sel = sel_register_name (probj, "load"); pr_class_t *class = tree->class; pr_class_t *meta = &G_STRUCT (pr, pr_class_t, class->class_pointer); pr_method_list_t *method_list = &G_STRUCT (pr, pr_method_list_t, meta->methods); - obj_send_message_in_list (pr, method_list, class, load_sel); + obj_send_message_in_list (probj, method_list, class, load_sel); } static void -obj_send_load (progs_t *pr) +obj_send_load (probj_t *probj) { + progs_t *pr = probj->pr; obj_list *m; - if (pr->unresolved_classes) { - pr_class_t *class = pr->unresolved_classes->data; + if (probj->unresolved_classes) { + pr_class_t *class = probj->unresolved_classes->data; const char *super_class = PR_GetString (pr, class->super_class); - while (Hash_Find (pr->classes, super_class)) { - list_remove (&pr->unresolved_classes); - if (pr->unresolved_classes) { - class = pr->unresolved_classes->data; + while (Hash_Find (probj->classes, super_class)) { + list_remove (probj, &probj->unresolved_classes); + if (probj->unresolved_classes) { + class = probj->unresolved_classes->data; super_class = PR_GetString (pr, class->super_class); } else { break; } } - if (pr->unresolved_classes) + if (probj->unresolved_classes) return; } //XXX constant string stuff here (see init.c in libobjc source) - for (m = pr->module_list; m; m = m->next) - obj_create_classes_tree (pr, m->data); - while (pr->class_tree_list) { - obj_preorder_traverse (pr, pr->class_tree_list->data, 0, send_load); - obj_postorder_traverse (pr, pr->class_tree_list->data, 0, + for (m = probj->module_list; m; m = m->next) + obj_create_classes_tree (probj, m->data); + while (probj->class_tree_list) { + obj_preorder_traverse (probj, probj->class_tree_list->data, 0, + send_load); + obj_postorder_traverse (probj, probj->class_tree_list->data, 0, obj_destroy_class_tree_node); - list_remove (&pr->class_tree_list); + list_remove (probj, &probj->class_tree_list); } //XXX callback - //for (m = pr->module_list; m; m = m->next) - // obj_create_classes_tree (pr, m->data); - obj_list_free (pr->module_list); - pr->module_list = 0; + //for (m = probj->module_list; m; m = m->next) + // obj_create_classes_tree (probj, m->data); + obj_list_free (probj, probj->module_list); + probj->module_list = 0; } static pr_method_t * -obj_find_message (progs_t *pr, pr_class_t *class, pr_sel_t *selector) +obj_find_message (probj_t *probj, pr_class_t *class, pr_sel_t *selector) { + progs_t *pr = probj->pr; pr_class_t *c = class; pr_method_list_t *method_list; pr_method_t *method; pr_sel_t *sel; int i; - int dev = developer->int_val; - string_t *names; + int dev = developer; + pr_string_t *names; - if (dev & SYS_RUA_MSG) { - names = pr->selector_names; + if (dev & SYS_rua_msg) { + names = probj->selector_names; Sys_Printf ("Searching for %s\n", PR_GetString (pr, names[selector->sel_id])); } while (c) { - if (dev & SYS_RUA_MSG) + if (dev & SYS_rua_msg) Sys_Printf ("Checking class %s @ %x\n", PR_GetString (pr, c->name), PR_SetPointer (pr, c)); method_list = &G_STRUCT (pr, pr_method_list_t, c->methods); while (method_list) { - if (dev & SYS_RUA_MSG) { + if (dev & SYS_rua_msg) { Sys_Printf ("method list %x\n", PR_SetPointer (pr, method_list)); } for (i = 0, method = method_list->method_list; i < method_list->method_count; i++, method++) { sel = &G_STRUCT (pr, pr_sel_t, method->method_name); - if (developer->int_val & SYS_RUA_MSG) { - names = pr->selector_names; + if (developer & SYS_rua_msg) { + names = probj->selector_names; Sys_Printf (" %s\n", PR_GetString (pr, names[sel->sel_id])); } if (sel->sel_id == selector->sel_id) { - if (dev & SYS_RUA_MSG) { - names = pr->selector_names; + if (dev & SYS_rua_msg) { + names = probj->selector_names; Sys_Printf ("found %s: %x\n", PR_GetString (pr, names[selector->sel_id]), method->method_imp); @@ -703,13 +917,14 @@ obj_find_message (progs_t *pr, pr_class_t *class, pr_sel_t *selector) } static void -obj_send_initialize (progs_t *pr, pr_class_t *class) +obj_send_initialize (probj_t *probj, pr_class_t *class) { + progs_t *pr = probj->pr; pr_method_list_t *method_list; pr_method_t *method; pr_sel_t *sel; pr_class_t *class_pointer; - pr_sel_t *selector = sel_register_name (pr, "initialize"); + pr_sel_t *selector = sel_register_name (probj, "initialize"); int i; if (PR_CLS_ISINITIALIZED (class)) @@ -718,8 +933,8 @@ obj_send_initialize (progs_t *pr, pr_class_t *class) PR_CLS_SETINITIALIZED (class); PR_CLS_SETINITIALIZED (class_pointer); if (class->super_class) - obj_send_initialize (pr, &G_STRUCT (pr, pr_class_t, - class->super_class)); + obj_send_initialize (probj, &G_STRUCT (pr, pr_class_t, + class->super_class)); method_list = &G_STRUCT (pr, pr_method_list_t, class_pointer->methods); while (method_list) { @@ -728,12 +943,12 @@ obj_send_initialize (progs_t *pr, pr_class_t *class) sel = &G_STRUCT (pr, pr_sel_t, method->method_name); if (sel->sel_id == selector->sel_id) { PR_PushFrame (pr); - PR_SaveParams (pr); + __auto_type params = PR_SaveParams (pr); // param 0 is known to be the class pointer P_POINTER (pr, 1) = method->method_name; // pr->pr_argc is known to be 2 PR_ExecuteProgram (pr, method->method_imp); - PR_RestoreParams (pr); + PR_RestoreParams (pr, params); PR_PopFrame (pr); return; } @@ -743,57 +958,162 @@ obj_send_initialize (progs_t *pr, pr_class_t *class) } } -static func_t -get_imp (progs_t *pr, pr_class_t *class, pr_sel_t *sel) +static void +obj_install_methods_in_dtable (probj_t *probj, pr_class_t *class, + pr_method_list_t *method_list) { - pr_method_t *method = obj_find_message (pr, class, sel); + progs_t *pr = probj->pr; + dtable_t *dtable; - return method ? method->method_imp : 0; + if (!method_list) { + return; + } + if (method_list->method_next) { + obj_install_methods_in_dtable (probj, class, + &G_STRUCT (pr, pr_method_list_t, + method_list->method_next)); + } + + dtable = get_dtable (probj, __FUNCTION__, class->dtable); + for (int i = 0; i < method_list->method_count; i++) { + pr_method_t *method = &method_list->method_list[i]; + pr_sel_t *sel = &G_STRUCT (pr, pr_sel_t, method->method_name); + if (sel->sel_id < dtable->size) { + dtable->imp[sel->sel_id] = method->method_imp; + } + } } -static func_t -obj_msg_lookup (progs_t *pr, pr_id_t *receiver, pr_sel_t *op) +static void +obj_install_dispatch_table_for_class (probj_t *probj, pr_class_t *class) { + progs_t *pr = probj->pr; + pr_class_t *super = &G_STRUCT (pr, pr_class_t, class->super_class); + dtable_t *dtable; + + Sys_MaskPrintf (SYS_rua_obj, " install dispatch for class %s %x %d\n", + PR_GetString (pr, class->name), + class->methods, + PR_CLS_ISMETA(class)); + + if (super && !super->dtable) { + obj_install_dispatch_table_for_class (probj, super); + } + + dtable = dtable_new (probj); + class->dtable = dtable_index (probj, dtable); + dtable->size = probj->selector_index + 1; + dtable->imp = calloc (dtable->size, sizeof (pr_func_t)); + if (super) { + dtable_t *super_dtable = get_dtable (probj, __FUNCTION__, + super->dtable); + memcpy (dtable->imp, super_dtable->imp, + super_dtable->size * sizeof (*dtable->imp)); + } + obj_install_methods_in_dtable (probj, class, + &G_STRUCT (pr, pr_method_list_t, + class->methods)); +} + +static inline dtable_t * +obj_check_dtable_installed (probj_t *probj, pr_class_t *class) +{ + if (!class->dtable) { + obj_install_dispatch_table_for_class (probj, class); + } + return get_dtable (probj, __FUNCTION__, class->dtable); +} + +static pr_func_t +get_imp (probj_t *probj, pr_class_t *class, pr_sel_t *sel) +{ + pr_func_t imp = 0; + + if (class->dtable) { + dtable_t *dtable = get_dtable (probj, __FUNCTION__, class->dtable); + if (sel->sel_id < dtable->size) { + imp = dtable->imp[sel->sel_id]; + } + } + if (!imp) { + if (!class->dtable) { + obj_install_dispatch_table_for_class (probj, class); + imp = get_imp (probj, class, sel); + } else { + imp = probj->obj_forward; + } + } + return imp; +} + +static int +obj_reponds_to (probj_t *probj, pr_id_t *obj, pr_sel_t *sel) +{ + progs_t *pr = probj->pr; + pr_class_t *class; + dtable_t *dtable; + pr_func_t imp = 0; + + class = &G_STRUCT (pr, pr_class_t, obj->class_pointer); + dtable = obj_check_dtable_installed (probj, class); + + if (sel->sel_id < dtable->size) { + imp = dtable->imp[sel->sel_id]; + } + return imp != 0; +} + +static pr_func_t +obj_msg_lookup (probj_t *probj, pr_id_t *receiver, pr_sel_t *op) +{ + progs_t *pr = probj->pr; pr_class_t *class; if (!receiver) return 0; class = &G_STRUCT (pr, pr_class_t, receiver->class_pointer); if (PR_CLS_ISCLASS (class)) { if (!PR_CLS_ISINITIALIZED (class)) - obj_send_initialize (pr, class); + obj_send_initialize (probj, class); } else if (PR_CLS_ISMETA (class) && PR_CLS_ISCLASS ((pr_class_t *) receiver)) { if (!PR_CLS_ISINITIALIZED ((pr_class_t *) receiver)) - obj_send_initialize (pr, (pr_class_t *) receiver); + obj_send_initialize (probj, (pr_class_t *) receiver); } - return get_imp (pr, class, op); + return get_imp (probj, class, op); } -static func_t -obj_msg_lookup_super (progs_t *pr, pr_super_t *super, pr_sel_t *op) +static pr_func_t +obj_msg_lookup_super (probj_t *probj, pr_super_t *super, pr_sel_t *op) { + progs_t *pr = probj->pr; pr_class_t *class; if (!super->self) return 0; class = &G_STRUCT (pr, pr_class_t, super->class); - return get_imp (pr, class, op); + return get_imp (probj, class, op); } static void -obj_verror (progs_t *pr, pr_id_t *object, int code, const char *fmt, int count, +obj_verror (probj_t *probj, pr_id_t *object, int code, const char *fmt, int count, pr_type_t **args) { - dstring_t *dstr = dstring_newstr (); + progs_t *pr = probj->pr; + const char *name = "nil"; + if (object) { + __auto_type class = &G_STRUCT (pr, pr_class_t, object->class_pointer); + name = PR_GetString (pr, class->name); + } - PR_Sprintf (pr, dstr, "obj_verror", fmt, count, args); - PR_RunError (pr, "%s", dstr->str); + PR_Sprintf (pr, probj->msg, "obj_verror", fmt, count, args); + PR_RunError (pr, "%s: %s", name, probj->msg->str); } static void -dump_ivars (progs_t *pr, pointer_t _ivars) +dump_ivars (probj_t *probj, pr_ptr_t _ivars) { + progs_t *pr = probj->pr; pr_ivar_list_t *ivars; int i; @@ -809,12 +1129,58 @@ dump_ivars (progs_t *pr, pointer_t _ivars) } static void -rua___obj_exec_class (progs_t *pr) +obj_init_statics (probj_t *probj) { + progs_t *pr = probj->pr; + obj_list **cell = &probj->uninitialized_statics; + pr_ptr_t *ptr; + pr_ptr_t *inst; + + Sys_MaskPrintf (SYS_rua_obj, "Initializing statics\n"); + while (*cell) { + int initialized = 1; + + for (ptr = (*cell)->data; *ptr; ptr++) { + __auto_type statics = &G_STRUCT (pr, pr_static_instances_t, *ptr); + const char *class_name = PR_GetString (pr, statics->class_name); + pr_class_t *class = Hash_Find (probj->classes, class_name); + + Sys_MaskPrintf (SYS_rua_obj, " %s %p\n", class_name, class); + if (!class) { + initialized = 0; + continue; + } + + if (strcmp (class_name, "Protocol") == 0) { + // protocols are special + for (inst = statics->instances; *inst; inst++) { + obj_init_protocol (probj, class, + &G_STRUCT (pr, pr_protocol_t, *inst)); + } + } else { + for (inst = statics->instances; *inst; inst++) { + pr_id_t *id = &G_STRUCT (pr, pr_id_t, *inst); + id->class_pointer = PR_SetPointer (pr, class); + } + } + } + + if (initialized) { + list_remove (probj, cell); + } else { + cell = &(*cell)->next; + } + } +} + +static void +rua___obj_exec_class (progs_t *pr, void *data) +{ + probj_t *probj = pr->pr_objective_resources; pr_module_t *module = &P_STRUCT (pr, pr_module_t, 0); pr_symtab_t *symtab; pr_sel_t *sel; - pointer_t *ptr; + pr_ptr_t *ptr; int i; obj_list **cell; @@ -823,24 +1189,27 @@ rua___obj_exec_class (progs_t *pr) symtab = &G_STRUCT (pr, pr_symtab_t, module->symtab); if (!symtab) return; - Sys_MaskPrintf (SYS_RUA_OBJ, "Initializing %s module\n" + Sys_MaskPrintf (SYS_rua_obj, "Initializing %s module\n" "symtab @ %x : %d selector%s @ %x, " - "%d class%s and %d categor%s\n", + "%d class%s and %d categor%s\n" + "static instance lists: %s\n", PR_GetString (pr, module->name), module->symtab, symtab->sel_ref_cnt, symtab->sel_ref_cnt == 1 ? "" : "s", symtab->refs, symtab->cls_def_cnt, symtab->cls_def_cnt == 1 ? "" : "es", symtab->cat_def_cnt, - symtab->cat_def_cnt == 1 ? "y" : "ies"); + symtab->cat_def_cnt == 1 ? "y" : "ies", + symtab->defs[symtab->cls_def_cnt + + symtab->cat_def_cnt] ? "yes" : "no"); - pr->module_list = list_cons (module, pr->module_list); + probj->module_list = list_cons (probj, module, probj->module_list); sel = &G_STRUCT (pr, pr_sel_t, symtab->refs); for (i = 0; i < symtab->sel_ref_cnt; i++) { const char *name, *types; name = PR_GetString (pr, sel->sel_id); types = PR_GetString (pr, sel->sel_types); - sel_register_typed_name (pr, name, types, sel); + sel_register_typed_name (probj, name, types, sel); sel++; } @@ -850,111 +1219,235 @@ rua___obj_exec_class (progs_t *pr) pr_class_t *meta = &G_STRUCT (pr, pr_class_t, class->class_pointer); const char *super_class = PR_GetString (pr, class->super_class); - Sys_MaskPrintf (SYS_RUA_OBJ, "Class %s @ %x\n", + Sys_MaskPrintf (SYS_rua_obj, "Class %s @ %x\n", PR_GetString (pr, class->name), *ptr); - Sys_MaskPrintf (SYS_RUA_OBJ, " class pointer: %x\n", + Sys_MaskPrintf (SYS_rua_obj, " class pointer: %x\n", class->class_pointer); - Sys_MaskPrintf (SYS_RUA_OBJ, " super class: %s\n", + Sys_MaskPrintf (SYS_rua_obj, " super class: %s\n", PR_GetString (pr, class->super_class)); - Sys_MaskPrintf (SYS_RUA_OBJ, " instance variables: %d @ %x\n", + Sys_MaskPrintf (SYS_rua_obj, " instance variables: %d @ %x\n", class->instance_size, class->ivars); - if (developer->int_val & SYS_RUA_OBJ) - dump_ivars (pr, class->ivars); - Sys_MaskPrintf (SYS_RUA_OBJ, " instance methods: %x\n", + if (developer & SYS_rua_obj) + dump_ivars (probj, class->ivars); + Sys_MaskPrintf (SYS_rua_obj, " instance methods: %x\n", class->methods); - Sys_MaskPrintf (SYS_RUA_OBJ, " protocols: %x\n", class->protocols); + Sys_MaskPrintf (SYS_rua_obj, " protocols: %x\n", class->protocols); - Sys_MaskPrintf (SYS_RUA_OBJ, " class methods: %x\n", meta->methods); - Sys_MaskPrintf (SYS_RUA_OBJ, " instance variables: %d @ %x\n", + Sys_MaskPrintf (SYS_rua_obj, " class methods: %x\n", meta->methods); + Sys_MaskPrintf (SYS_rua_obj, " instance variables: %d @ %x\n", meta->instance_size, meta->ivars); - if (developer->int_val & SYS_RUA_OBJ) - dump_ivars (pr, meta->ivars); + if (developer & SYS_rua_obj) + dump_ivars (probj, meta->ivars); class->subclass_list = 0; - Hash_Add (pr->classes, class); + Hash_Add (probj->classes, class); - obj_register_selectors_from_class (pr, class); - obj_register_selectors_from_class (pr, meta); + obj_register_selectors_from_class (probj, class); + obj_register_selectors_from_class (probj, meta); if (class->protocols) { pr_protocol_list_t *protocol_list; protocol_list = &G_STRUCT (pr, pr_protocol_list_t, class->protocols); - obj_init_protocols (pr, protocol_list); + obj_init_protocols (probj, protocol_list); } - if (class->super_class && !Hash_Find (pr->classes, super_class)) - pr->unresolved_classes = list_cons (class, pr->unresolved_classes); + if (class->super_class && !Hash_Find (probj->classes, super_class)) + probj->unresolved_classes = list_cons (probj, class, + probj->unresolved_classes); } for (i = 0; i < symtab->cat_def_cnt; i++, ptr++) { pr_category_t *category = &G_STRUCT (pr, pr_category_t, *ptr); const char *class_name = PR_GetString (pr, category->class_name); - pr_class_t *class = Hash_Find (pr->classes, class_name); + pr_class_t *class = Hash_Find (probj->classes, class_name); - Sys_MaskPrintf (SYS_RUA_OBJ, "Category %s (%s) @ %x\n", + Sys_MaskPrintf (SYS_rua_obj, "Category %s (%s) @ %x\n", PR_GetString (pr, category->class_name), PR_GetString (pr, category->category_name), *ptr); - Sys_MaskPrintf (SYS_RUA_OBJ, " instance methods: %x\n", + Sys_MaskPrintf (SYS_rua_obj, " instance methods: %x\n", category->instance_methods); - Sys_MaskPrintf (SYS_RUA_OBJ, " class methods: %x\n", + Sys_MaskPrintf (SYS_rua_obj, " class methods: %x\n", category->class_methods); - Sys_MaskPrintf (SYS_RUA_OBJ, " protocols: %x\n", + Sys_MaskPrintf (SYS_rua_obj, " protocols: %x\n", category->protocols); if (class) { - finish_category (pr, category, class); + finish_category (probj, category, class); } else { - pr->unclaimed_categories = list_cons (category, - pr->unclaimed_categories); + probj->unclaimed_categories + = list_cons (probj, category, probj->unclaimed_categories); } } - for (cell = &pr->unclaimed_categories; *cell; ) { + if (*ptr) { + Sys_MaskPrintf (SYS_rua_obj, "Static instances lists: %x\n", *ptr); + probj->uninitialized_statics + = list_cons (probj, &G_STRUCT (pr, pr_ptr_t, *ptr), + probj->uninitialized_statics); + } + if (probj->uninitialized_statics) { + obj_init_statics (probj); + } + + for (cell = &probj->unclaimed_categories; *cell; ) { pr_category_t *category = (*cell)->data; const char *class_name = PR_GetString (pr, category->class_name); - pr_class_t *class = Hash_Find (pr->classes, class_name); + pr_class_t *class = Hash_Find (probj->classes, class_name); if (class) { - list_remove (cell); - finish_category (pr, category, class); + list_remove (probj, cell); + finish_category (probj, category, class); } else { cell = &(*cell)->next; } } - if (pr->unclaimed_proto_list && Hash_Find (pr->classes, "Protocol")) { - for (cell = &pr->unclaimed_proto_list; *cell; ) { - obj_init_protocols (pr, (*cell)->data); - list_remove (cell); + if (probj->unclaimed_proto_list + && Hash_Find (probj->classes, "Protocol")) { + for (cell = &probj->unclaimed_proto_list; *cell; ) { + obj_init_protocols (probj, (*cell)->data); + list_remove (probj, cell); } } - Sys_MaskPrintf (SYS_RUA_OBJ, "Finished initializing %s module\n", + Sys_MaskPrintf (SYS_rua_obj, "Finished initializing %s module\n", PR_GetString (pr, module->name)); - obj_send_load (pr); - Sys_MaskPrintf (SYS_RUA_OBJ, "Leaving %s module init\n", + obj_send_load (probj); + Sys_MaskPrintf (SYS_rua_obj, "Leaving %s module init\n", PR_GetString (pr, module->name)); } static void -rua_obj_error (progs_t *pr) +rua___obj_forward (progs_t *pr, void *data) { + probj_t *probj = pr->pr_objective_resources; + pr_id_t *obj = &P_STRUCT (pr, pr_id_t, 0); + pr_sel_t *sel = &P_STRUCT (pr, pr_sel_t, 1); + pr_sel_t *fwd_sel = probj->forward_selector; + pr_sel_t *err_sel; + pr_class_t *class = &G_STRUCT (pr, pr_class_t, obj->class_pointer); + pr_func_t imp; + + if (!fwd_sel) { + //FIXME sel_register_typed_name is really not the way to go about + //looking for a selector by name + fwd_sel = sel_register_typed_name (probj, "forward::", "", 0); + probj->forward_selector = fwd_sel; + } + if (obj_reponds_to (probj, obj, fwd_sel)) { + imp = get_imp (probj, class, fwd_sel); + // forward:(SEL) sel :(@va_list) args + // args is full param list as a va_list + pr_string_t args_block = 0; + int argc; + pr_type_t *argv; + if (pr->globals.stack) { + argv = pr->pr_params[0]; + argc = probj->selector_argc[sel->sel_id]; + if (argc < 0) { + // -ve values indicate varargs functions and is the ones + // complement of the number of real parameters before the + // ellipsis. However, Ruamoko ISA progs pass va_list through + // ... so in the end, a -ve value indicates the total number + // of arguments (including va_list) passed to the function. + argc = -argc; + } + } else { + size_t parm_size = pr->pr_param_size * sizeof(pr_type_t); + size_t size = pr->pr_argc * parm_size; + args_block = PR_AllocTempBlock (pr, size); + + argc = pr->pr_argc; + argv = (pr_type_t *) PR_GetString (pr, args_block); + // can't memcpy all params because 0 and 1 could be anywhere + memcpy (argv + 0, &P_INT (pr, 0), 4 * sizeof (pr_type_t)); + memcpy (argv + 4, &P_INT (pr, 1), 4 * sizeof (pr_type_t)); + memcpy (argv + 8, &P_INT (pr, 2), (argc - 2) * parm_size); + } + + RUA_CALL_BEGIN (pr, 4); + P_POINTER (pr, 0) = PR_SetPointer (pr, obj); + P_POINTER (pr, 1) = PR_SetPointer (pr, fwd_sel); + P_POINTER (pr, 2) = PR_SetPointer (pr, sel); + P_PACKED (pr, pr_va_list_t, 3).count = argc; + P_PACKED (pr, pr_va_list_t, 3).list = PR_SetPointer (pr, argv); + if (args_block) { + PR_PushTempString (pr, args_block); + } + RUA_CALL_END (pr, imp); + return; + } + err_sel = sel_register_typed_name (probj, "doesNotRecognize:", "", 0); + if (obj_reponds_to (probj, obj, err_sel)) { + RUA_CALL_BEGIN (pr, 3) + P_POINTER (pr, 0) = PR_SetPointer (pr, obj); + P_POINTER (pr, 1) = PR_SetPointer (pr, err_sel); + P_POINTER (pr, 2) = PR_SetPointer (pr, sel); + RUA_CALL_END (pr, get_imp (probj, class, err_sel)) + return; + } + + dsprintf (probj->msg, "(%s) %s does not recognize %s", + PR_CLS_ISMETA (class) ? "class" : "instance", + PR_GetString (pr, class->name), + PR_GetString (pr, probj->selector_names[sel->sel_id])); + + err_sel = sel_register_typed_name (probj, "error:", "", 0); + if (obj_reponds_to (probj, obj, err_sel)) { + RUA_CALL_BEGIN (pr, 3) + P_POINTER (pr, 0) = PR_SetPointer (pr, obj); + P_POINTER (pr, 1) = PR_SetPointer (pr, err_sel); + P_POINTER (pr, 2) = PR_SetTempString (pr, probj->msg->str); + RUA_CALL_END (pr, get_imp (probj, class, err_sel)) + return; + } + + PR_RunError (pr, "%s", probj->msg->str); +} + +static void +rua___obj_responds_to (progs_t *pr, void *data) +{ + probj_t *probj = pr->pr_objective_resources; + pr_id_t *obj = &P_STRUCT (pr, pr_id_t, 0); + pr_sel_t *sel = &P_STRUCT (pr, pr_sel_t, 1); + + R_INT (pr) = obj_reponds_to (probj, obj, sel); +} + +static void +rua_obj_error (progs_t *pr, void *data) +{ + probj_t *probj = pr->pr_objective_resources; pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); int code = P_INT (pr, 1); const char *fmt = P_GSTRING (pr, 2); int count = pr->pr_argc - 3; pr_type_t **args = &pr->pr_params[3]; - obj_verror (pr, object, code, fmt, count, args); + if (pr->progs->version == PROG_VERSION) { + __auto_type va_list = &P_PACKED (pr, pr_va_list_t, 3); + count = va_list->count; + if (count) { + args = alloca (count * sizeof (pr_type_t *)); + for (int i = 0; i < count; i++) { + args[i] = &pr->pr_globals[va_list->list + i * 4]; + } + } else { + args = 0; + } + } + obj_verror (probj, object, code, fmt, count, args); } static void -rua_obj_verror (progs_t *pr) +rua_obj_verror (progs_t *pr, void *data) { + probj_t *probj = pr->pr_objective_resources; pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); int code = P_INT (pr, 1); const char *fmt = P_GSTRING (pr, 2); @@ -965,81 +1458,131 @@ rua_obj_verror (progs_t *pr) for (i = 0; i < val->count; i++) args[i] = params + i * pr->pr_param_size; - obj_verror (pr, object, code, fmt, val->count, args); + obj_verror (probj, object, code, fmt, val->count, args); } static void -rua_obj_set_error_handler (progs_t *pr) +rua_obj_set_error_handler (progs_t *pr, void *data) { - //func_t func = P_INT (pr, 0); + //probj_t *probj = pr->pr_objective_resources; + //pr_func_t func = P_INT (pr, 0); //arglist //XXX PR_RunError (pr, "%s, not implemented", __FUNCTION__); } -static void -rua_obj_msg_lookup (progs_t *pr) +static const char * +rua_at_handler (progs_t *pr, pr_ptr_t at_param, void *_probj) { - pr_id_t *receiver = &P_STRUCT (pr, pr_id_t, 0); - pr_sel_t *op = &P_STRUCT (pr, pr_sel_t, 1); + if (!at_param) { + return "nil"; + } + probj_t *probj = _probj; + pr_id_t *obj = &G_STRUCT (pr, pr_id_t, at_param); + pr_class_t *class = &G_STRUCT (pr, pr_class_t, obj->class_pointer); + //FIXME sel_register_typed_name is really not the way to go about + //looking for a selector by name + pr_sel_t *describe_sel=sel_register_typed_name (probj, "describe", "", 0); + pr_func_t imp = get_imp (probj, class, describe_sel); - R_INT (pr) = obj_msg_lookup (pr, receiver, op); + PR_PushFrame (pr); + PR_RESET_PARAMS (pr); + P_POINTER (pr, 0) = at_param; + P_POINTER (pr, 1) = PR_SetPointer (pr, describe_sel); + pr->pr_argc = 2; + PR_ExecuteProgram (pr, imp); + PR_PopFrame (pr); + //FIXME the lifetime of the string may be a problem + return PR_GetString (pr, R_STRING (pr)); } static void -rua_obj_msg_lookup_super (progs_t *pr) +rua_obj_msg_lookup (progs_t *pr, void *data) { + probj_t *probj = pr->pr_objective_resources; + pr_id_t *receiver = &P_STRUCT (pr, pr_id_t, 0); + pr_sel_t *op = &P_STRUCT (pr, pr_sel_t, 1); + + R_INT (pr) = obj_msg_lookup (probj, receiver, op); +} + +static void +rua_obj_msg_lookup_super (progs_t *pr, void *data) +{ + probj_t *probj = pr->pr_objective_resources; pr_super_t *super = &P_STRUCT (pr, pr_super_t, 0); pr_sel_t *_cmd = &P_STRUCT (pr, pr_sel_t, 1); - R_INT (pr) = obj_msg_lookup_super (pr, super, _cmd); + R_INT (pr) = obj_msg_lookup_super (probj, super, _cmd); } static void -rua_obj_msg_sendv (progs_t *pr) +rua_obj_msg_sendv (progs_t *pr, void *data) { + probj_t *probj = pr->pr_objective_resources; + pr_ptr_t obj = P_POINTER (pr, 0); pr_id_t *receiver = &P_STRUCT (pr, pr_id_t, 0); + pr_ptr_t sel = P_POINTER (pr, 1); pr_sel_t *op = &P_STRUCT (pr, pr_sel_t, 1); - pr_va_list_t *args = (pr_va_list_t *) &P_POINTER (pr, 2); - pr_type_t *params = G_GPOINTER (pr, args->list); + pr_func_t imp = obj_msg_lookup (probj, receiver, op); + + __auto_type args = &P_PACKED (pr, pr_va_list_t, 2); int count = args->count; - func_t imp = obj_msg_lookup (pr, receiver, op); + pr_type_t *params = G_GPOINTER (pr, args->list); - count = bound (0, count, 6); - if (count && pr_boundscheck->int_val) + if (count < 2 || count > PR_MAX_PARAMS) { + PR_RunError (pr, "bad args count in obj_msg_sendv: %d", count); + } + if (pr_boundscheck) { PR_BoundsCheckSize (pr, args->list, count * pr->pr_param_size); - if (!imp) + } + + if (!imp) { PR_RunError (pr, "%s does not respond to %s", - PR_GetString (pr, object_get_class_name (pr, receiver)), - PR_GetString (pr, pr->selector_names[op->sel_id])); - if (count) - memcpy (pr->pr_params[2], params, count * 4 * pr->pr_param_size); - PR_CallFunction (pr, imp); + PR_GetString (pr, object_get_class_name (probj, receiver)), + PR_GetString (pr, probj->selector_names[op->sel_id])); + } + + RUA_CALL_BEGIN (pr, count) + // skip over the first two parameters because receiver and op will + // replace them + count -= 2; + params += 2 * pr->pr_param_size; + + P_POINTER (pr, 0) = obj; + P_POINTER (pr, 1) = sel; + if (count) { + memcpy (&P_INT (pr, 2), params, + count * sizeof (pr_type_t) * pr->pr_param_size); + } + RUA_CALL_END (pr, imp) } +#define RETAIN_COUNT(obj) PR_PTR (int, &(obj)[-1]) + static void -rua_obj_increment_retaincount (progs_t *pr) +rua_obj_increment_retaincount (progs_t *pr, void *data) { pr_type_t *obj = &P_STRUCT (pr, pr_type_t, 0); - R_INT (pr) = ++(*--obj).integer_var; + R_INT (pr) = ++RETAIN_COUNT (obj); } static void -rua_obj_decrement_retaincount (progs_t *pr) +rua_obj_decrement_retaincount (progs_t *pr, void *data) { pr_type_t *obj = &P_STRUCT (pr, pr_type_t, 0); - R_INT (pr) = --(*--obj).integer_var; + R_INT (pr) = --RETAIN_COUNT (obj); } static void -rua_obj_get_retaincount (progs_t *pr) +rua_obj_get_retaincount (progs_t *pr, void *data) { pr_type_t *obj = &P_STRUCT (pr, pr_type_t, 0); - R_INT (pr) = (*--obj).integer_var; + R_INT (pr) = RETAIN_COUNT (obj); } static void -rua_obj_malloc (progs_t *pr) +rua_obj_malloc (progs_t *pr, void *data) { int size = P_INT (pr, 0) * sizeof (pr_type_t); void *mem = PR_Zone_Malloc (pr, size); @@ -1048,7 +1591,7 @@ rua_obj_malloc (progs_t *pr) } static void -rua_obj_atomic_malloc (progs_t *pr) +rua_obj_atomic_malloc (progs_t *pr, void *data) { int size = P_INT (pr, 0) * sizeof (pr_type_t); void *mem = PR_Zone_Malloc (pr, size); @@ -1057,7 +1600,7 @@ rua_obj_atomic_malloc (progs_t *pr) } static void -rua_obj_valloc (progs_t *pr) +rua_obj_valloc (progs_t *pr, void *data) { int size = P_INT (pr, 0) * sizeof (pr_type_t); void *mem = PR_Zone_Malloc (pr, size); @@ -1066,7 +1609,7 @@ rua_obj_valloc (progs_t *pr) } static void -rua_obj_realloc (progs_t *pr) +rua_obj_realloc (progs_t *pr, void *data) { void *mem = (void*)P_GPOINTER (pr, 0); int size = P_INT (pr, 1) * sizeof (pr_type_t); @@ -1076,9 +1619,9 @@ rua_obj_realloc (progs_t *pr) } static void -rua_obj_calloc (progs_t *pr) +rua_obj_calloc (progs_t *pr, void *data) { - int size = P_INT (pr, 0) * sizeof (pr_type_t); + int size = P_INT (pr, 0) * P_INT (pr, 1) * sizeof (pr_type_t); void *mem = PR_Zone_Malloc (pr, size); memset (mem, 0, size); @@ -1086,7 +1629,7 @@ rua_obj_calloc (progs_t *pr) } static void -rua_obj_free (progs_t *pr) +rua_obj_free (progs_t *pr, void *data) { void *mem = (void*)P_GPOINTER (pr, 0); @@ -1094,78 +1637,89 @@ rua_obj_free (progs_t *pr) } static void -rua_obj_get_uninstalled_dtable (progs_t *pr) +rua_obj_get_uninstalled_dtable (progs_t *pr, void *data) { + //probj_t *probj = pr->pr_objective_resources; //XXX PR_RunError (pr, "%s, not implemented", __FUNCTION__); } static void -rua_obj_msgSend (progs_t *pr) +rua_obj_msgSend (progs_t *pr, void *data) { + probj_t *probj = pr->pr_objective_resources; pr_id_t *self = &P_STRUCT (pr, pr_id_t, 0); pr_sel_t *_cmd = &P_STRUCT (pr, pr_sel_t, 1); - func_t imp; + pr_func_t imp; if (!self) { R_INT (pr) = P_INT (pr, 0); return; } + if (P_UINT (pr, 0) >= pr->globals_size) { + PR_RunError (pr, "invalid self: %x", P_UINT (pr, 0)); + } if (!_cmd) PR_RunError (pr, "null selector"); - imp = obj_msg_lookup (pr, self, _cmd); + imp = obj_msg_lookup (probj, self, _cmd); if (!imp) PR_RunError (pr, "%s does not respond to %s", - PR_GetString (pr, object_get_class_name (pr, self)), - PR_GetString (pr, pr->selector_names[_cmd->sel_id])); + PR_GetString (pr, object_get_class_name (probj, self)), + PR_GetString (pr, probj->selector_names[_cmd->sel_id])); - PR_CallFunction (pr, imp); + PR_CallFunction (pr, imp, pr->pr_return); } static void -rua_obj_msgSend_super (progs_t *pr) +rua_obj_msgSend_super (progs_t *pr, void *data) { + probj_t *probj = pr->pr_objective_resources; pr_super_t *super = &P_STRUCT (pr, pr_super_t, 0); pr_sel_t *_cmd = &P_STRUCT (pr, pr_sel_t, 1); - func_t imp; + pr_func_t imp; - imp = obj_msg_lookup_super (pr, super, _cmd); + imp = obj_msg_lookup_super (probj, super, _cmd); if (!imp) { pr_id_t *self = &G_STRUCT (pr, pr_id_t, super->self); PR_RunError (pr, "%s does not respond to %s", - PR_GetString (pr, object_get_class_name (pr, self)), - PR_GetString (pr, pr->selector_names[_cmd->sel_id])); + PR_GetString (pr, object_get_class_name (probj, self)), + PR_GetString (pr, probj->selector_names[_cmd->sel_id])); + } + if (pr->progs->version < PROG_VERSION) { + pr->pr_params[0] = pr->pr_real_params[0]; } - pr->pr_params[0] = pr->pr_real_params[0]; P_POINTER (pr, 0) = super->self; - PR_CallFunction (pr, imp); + PR_CallFunction (pr, imp, pr->pr_return); } static void -rua_obj_get_class (progs_t *pr) +rua_obj_get_class (progs_t *pr, void *data) { + probj_t *probj = pr->pr_objective_resources; const char *name = P_GSTRING (pr, 0); pr_class_t *class; - class = Hash_Find (pr->classes, name); + class = Hash_Find (probj->classes, name); if (!class) PR_RunError (pr, "could not find class %s", name); RETURN_POINTER (pr, class); } static void -rua_obj_lookup_class (progs_t *pr) +rua_obj_lookup_class (progs_t *pr, void *data) { + probj_t *probj = pr->pr_objective_resources; const char *name = P_GSTRING (pr, 0); pr_class_t *class; - class = Hash_Find (pr->classes, name); + class = Hash_Find (probj->classes, name); RETURN_POINTER (pr, class); } static void -rua_obj_next_class (progs_t *pr) +rua_obj_next_class (progs_t *pr, void *data) { + //probj_t *probj = pr->pr_objective_resources; //XXX PR_RunError (pr, "%s, not implemented", __FUNCTION__); } @@ -1173,18 +1727,19 @@ rua_obj_next_class (progs_t *pr) //==================================================================== static void -rua_sel_get_name (progs_t *pr) +rua_sel_get_name (progs_t *pr, void *data) { + probj_t *probj = pr->pr_objective_resources; pr_sel_t *sel = &P_STRUCT (pr, pr_sel_t, 0); - if (sel->sel_id > 0 && sel->sel_id <= pr->selector_index) - R_STRING (pr) = pr->selector_names[sel->sel_id]; + if (sel->sel_id > 0 && sel->sel_id <= probj->selector_index) + R_STRING (pr) = probj->selector_names[sel->sel_id]; else R_STRING (pr) = 0; } static void -rua_sel_get_type (progs_t *pr) +rua_sel_get_type (progs_t *pr, void *data) { pr_sel_t *sel = &P_STRUCT (pr, pr_sel_t, 0); @@ -1192,64 +1747,69 @@ rua_sel_get_type (progs_t *pr) } static void -rua_sel_get_uid (progs_t *pr) +rua_sel_get_uid (progs_t *pr, void *data) { + probj_t *probj = pr->pr_objective_resources; const char *name = P_GSTRING (pr, 0); - RETURN_POINTER (pr, sel_register_typed_name (pr, name, "", 0)); + RETURN_POINTER (pr, sel_register_typed_name (probj, name, "", 0)); } static void -rua_sel_register_name (progs_t *pr) +rua_sel_register_name (progs_t *pr, void *data) { + probj_t *probj = pr->pr_objective_resources; const char *name = P_GSTRING (pr, 0); - RETURN_POINTER (pr, sel_register_typed_name (pr, name, "", 0)); + RETURN_POINTER (pr, sel_register_typed_name (probj, name, "", 0)); } static void -rua_sel_is_mapped (progs_t *pr) +rua_sel_is_mapped (progs_t *pr, void *data) { + probj_t *probj = pr->pr_objective_resources; // FIXME might correspond to a string pr_sel_t *sel = &P_STRUCT (pr, pr_sel_t, 0); - R_INT (pr) = sel->sel_id > 0 && sel->sel_id <= pr->selector_index; + R_INT (pr) = sel->sel_id > 0 && sel->sel_id <= probj->selector_index; } //==================================================================== static void -rua_class_get_class_method (progs_t *pr) +rua_class_get_class_method (progs_t *pr, void *data) { + probj_t *probj = pr->pr_objective_resources; pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); pr_sel_t *aSel = &P_STRUCT (pr, pr_sel_t, 1); pr_method_t *method; class = &G_STRUCT (pr, pr_class_t, class->class_pointer); - method = obj_find_message (pr, class, aSel); + method = obj_find_message (probj, class, aSel); RETURN_POINTER (pr, method); } static void -rua_class_get_instance_method (progs_t *pr) +rua_class_get_instance_method (progs_t *pr, void *data) { + probj_t *probj = pr->pr_objective_resources; pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); pr_sel_t *aSel = &P_STRUCT (pr, pr_sel_t, 1); - pr_method_t *method = obj_find_message (pr, class, aSel); + pr_method_t *method = obj_find_message (probj, class, aSel); RETURN_POINTER (pr, method); } #define CLASSOF(x) (&G_STRUCT (pr, pr_class_t, (x)->class_pointer)) static void -rua_class_pose_as (progs_t *pr) +rua_class_pose_as (progs_t *pr, void *data) { pr_class_t *impostor = &P_STRUCT (pr, pr_class_t, 0); pr_class_t *superclass = &P_STRUCT (pr, pr_class_t, 1); - pointer_t *subclass; + pr_ptr_t *subclass; subclass = &superclass->subclass_list; while (*subclass) { pr_class_t *sub = &P_STRUCT (pr, pr_class_t, *subclass); - pointer_t nextSub = sub->sibling_class; + pr_ptr_t nextSub = sub->sibling_class; if (sub != impostor) { sub->sibling_class = impostor->subclass_list; sub->super_class = P_POINTER (pr, 0); // impostor @@ -1275,19 +1835,21 @@ rua_class_pose_as (progs_t *pr) static inline pr_id_t * class_create_instance (progs_t *pr, pr_class_t *class) { - int size = (class->instance_size + 1) * sizeof (pr_type_t); + int size = class->instance_size * sizeof (pr_type_t); pr_type_t *mem; - pr_id_t *id; + pr_id_t *id = 0; - mem = PR_Zone_Malloc (pr, size); - // redundant memset (id, 0, size); - id = (pr_id_t *) (mem + 1); - id->class_pointer = PR_SetPointer (pr, class); + mem = PR_Zone_TagMalloc (pr, size, class->name); + if (mem) { + memset (mem, 0, size); + id = (pr_id_t *) mem; + id->class_pointer = PR_SetPointer (pr, class); + } return id; } static void -rua_class_create_instance (progs_t *pr) +rua_class_create_instance (progs_t *pr, void *data) { pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); pr_id_t *id = class_create_instance (pr, class); @@ -1296,7 +1858,7 @@ rua_class_create_instance (progs_t *pr) } static void -rua_class_get_class_name (progs_t *pr) +rua_class_get_class_name (progs_t *pr, void *data) { pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); R_INT (pr) = PR_CLS_ISCLASS (class) ? class->name @@ -1304,49 +1866,49 @@ rua_class_get_class_name (progs_t *pr) } static void -rua_class_get_instance_size (progs_t *pr) +rua_class_get_instance_size (progs_t *pr, void *data) { pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); R_INT (pr) = PR_CLS_ISCLASS (class) ? class->instance_size : 0; } static void -rua_class_get_meta_class (progs_t *pr) +rua_class_get_meta_class (progs_t *pr, void *data) { pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); R_INT (pr) = PR_CLS_ISCLASS (class) ? class->class_pointer : 0; } static void -rua_class_get_super_class (progs_t *pr) +rua_class_get_super_class (progs_t *pr, void *data) { pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); R_INT (pr) = PR_CLS_ISCLASS (class) ? class->super_class : 0; } static void -rua_class_get_version (progs_t *pr) +rua_class_get_version (progs_t *pr, void *data) { pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); R_INT (pr) = PR_CLS_ISCLASS (class) ? class->version : -1; } static void -rua_class_is_class (progs_t *pr) +rua_class_is_class (progs_t *pr, void *data) { pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); R_INT (pr) = PR_CLS_ISCLASS (class); } static void -rua_class_is_meta_class (progs_t *pr) +rua_class_is_meta_class (progs_t *pr, void *data) { pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); R_INT (pr) = PR_CLS_ISMETA (class); } static void -rua_class_set_version (progs_t *pr) +rua_class_set_version (progs_t *pr, void *data) { pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); if (PR_CLS_ISCLASS (class)) @@ -1354,15 +1916,16 @@ rua_class_set_version (progs_t *pr) } static void -rua_class_get_gc_object_type (progs_t *pr) +rua_class_get_gc_object_type (progs_t *pr, void *data) { pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); R_INT (pr) = PR_CLS_ISCLASS (class) ? class->gc_object_type : 0; } static void -rua_class_ivar_set_gcinvisible (progs_t *pr) +rua_class_ivar_set_gcinvisible (progs_t *pr, void *data) { + //probj_t *probj = pr->pr_objective_resources; //pr_class_t *impostor = &P_STRUCT (pr, pr_class_t, 0); //const char *ivarname = P_GSTRING (pr, 1); //int gcInvisible = P_INT (pr, 2); @@ -1373,48 +1936,51 @@ rua_class_ivar_set_gcinvisible (progs_t *pr) //==================================================================== static void -rua_method_get_imp (progs_t *pr) +rua_method_get_imp (progs_t *pr, void *data) { pr_method_t *method = &P_STRUCT (pr, pr_method_t, 0); - R_INT (pr) = method->method_imp; + R_INT (pr) = method ? method->method_imp : 0; } static void -rua_get_imp (progs_t *pr) +rua_get_imp (progs_t *pr, void *data) { + probj_t *probj = pr->pr_objective_resources; pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); pr_sel_t *sel = &P_STRUCT (pr, pr_sel_t, 1); - R_INT (pr) = get_imp (pr, class, sel); + R_INT (pr) = get_imp (probj, class, sel); } //==================================================================== static void -rua_object_dispose (progs_t *pr) +rua_object_dispose (progs_t *pr, void *data) { pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); pr_type_t *mem = (pr_type_t *) object; - PR_Zone_Free (pr, mem - 1); + PR_Zone_Free (pr, mem); } static void -rua_object_copy (progs_t *pr) +rua_object_copy (progs_t *pr, void *data) { pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); pr_class_t *class = &G_STRUCT (pr, pr_class_t, object->class_pointer); pr_id_t *id; id = class_create_instance (pr, class); - memcpy (id, object, sizeof (pr_type_t) * class->instance_size); - // copy the retain count - ((pr_type_t *) id)[-1] = ((pr_type_t *) object)[-1]; + if (id) { + memcpy (id, object, sizeof (pr_type_t) * class->instance_size); + // copy the retain count + ((pr_type_t *) id)[-1] = ((pr_type_t *) object)[-1]; + } RETURN_POINTER (pr, id); } static void -rua_object_get_class (progs_t *pr) +rua_object_get_class (progs_t *pr, void *data) { pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); pr_class_t *class; @@ -1434,7 +2000,7 @@ rua_object_get_class (progs_t *pr) } static void -rua_object_get_super_class (progs_t *pr) +rua_object_get_super_class (progs_t *pr, void *data) { pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); pr_class_t *class; @@ -1454,7 +2020,7 @@ rua_object_get_super_class (progs_t *pr) } static void -rua_object_get_meta_class (progs_t *pr) +rua_object_get_meta_class (progs_t *pr, void *data) { pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); pr_class_t *class; @@ -1474,15 +2040,16 @@ rua_object_get_meta_class (progs_t *pr) } static void -rua_object_get_class_name (progs_t *pr) +rua_object_get_class_name (progs_t *pr, void *data) { + probj_t *probj = pr->pr_objective_resources; pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); - R_STRING (pr) = object_get_class_name (pr, object); + R_STRING (pr) = object_get_class_name (probj, object); } static void -rua_object_is_class (progs_t *pr) +rua_object_is_class (progs_t *pr, void *data) { pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); @@ -1494,15 +2061,16 @@ rua_object_is_class (progs_t *pr) } static void -rua_object_is_instance (progs_t *pr) +rua_object_is_instance (progs_t *pr, void *data) { + probj_t *probj = pr->pr_objective_resources; pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); - R_INT (pr) = object_is_instance (pr, object); + R_INT (pr) = object_is_instance (probj, object); } static void -rua_object_is_meta_class (progs_t *pr) +rua_object_is_meta_class (progs_t *pr, void *data) { pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); @@ -1516,135 +2084,227 @@ rua_object_is_meta_class (progs_t *pr) //==================================================================== static void -rua__i_Object__hash (progs_t *pr) +rua__i_Object__hash (progs_t *pr, void *data) { R_INT (pr) = P_INT (pr, 0); } static void -rua__i_Object_error_error_ (progs_t *pr) +rua__i_Object_error_error_ (progs_t *pr, void *data) { + probj_t *probj = pr->pr_objective_resources; pr_id_t *self = &P_STRUCT (pr, pr_id_t, 0); const char *fmt = P_GSTRING (pr, 2); - dstring_t *dstr = dstring_new (); int count = pr->pr_argc - 3; pr_type_t **args = &pr->pr_params[3]; - dsprintf (dstr, "error: %s (%s)\n%s", - PR_GetString (pr, object_get_class_name (pr, self)), - object_is_instance (pr, self) ? "instance" : "class", fmt); - obj_verror (pr, self, 0, dstr->str, count, args); + if (pr->progs->version == PROG_VERSION) { + __auto_type va_list = &P_PACKED (pr, pr_va_list_t, 3); + count = va_list->count; + if (count) { + args = alloca (count * sizeof (pr_type_t *)); + for (int i = 0; i < count; i++) { + args[i] = &pr->pr_globals[va_list->list + i * 4]; + } + } else { + args = 0; + } + } + dsprintf (probj->msg, "error: %s (%s)\n%s", + PR_GetString (pr, object_get_class_name (probj, self)), + object_is_instance (probj, self) ? "instance" : "class", fmt); + obj_verror (probj, self, 0, probj->msg->str, count, args); } -static void -rua__c_Object__conformsToProtocol_ (progs_t *pr) +static int +obj_protocol_conformsToProtocol (probj_t *probj, pr_protocol_t *proto, + pr_protocol_t *protocol) { - //pr_id_t *object = &P_STRUCT (pr, pr_id_t, 0); - //pr_protocol_t *proto = &P_STRUCT (pr, pr_protocol_t, 2); - //... - //XXX - PR_RunError (pr, "%s, not implemented", __FUNCTION__); + progs_t *pr = probj->pr; + + pr_protocol_list_t *proto_list; + + if (!proto || !protocol) { + return 0; + } + if (proto == protocol) { + return 1; + } + if (proto->protocol_name == protocol->protocol_name + || strcmp (PR_GetString (pr, proto->protocol_name), + PR_GetString (pr, protocol->protocol_name)) == 0) { + return 1; + } + proto_list = &G_STRUCT (pr, pr_protocol_list_t, proto->protocol_list); + while (proto_list) { + Sys_MaskPrintf (SYS_rua_obj, "%x %x %d\n", + PR_SetPointer (pr, proto_list), proto_list->next, + proto_list->count); + for (int i = 0; i < proto_list->count; i++) { + proto = &G_STRUCT (pr, pr_protocol_t, proto_list->list[i]); + if (proto == protocol + || obj_protocol_conformsToProtocol (probj, proto, protocol)) { + return 1; + } + } + + proto_list = &G_STRUCT (pr, pr_protocol_list_t, proto_list->next); + } + return 0; } static void -rua_PR_FindGlobal (progs_t *pr) +rua__c_Object__conformsToProtocol_ (progs_t *pr, void *data) +{ + probj_t *probj = pr->pr_objective_resources; + // class points to _OBJ_CLASS_foo, and class->class_pointer points to + // _OBJ_METACLASS_foo + pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0); + // protocol->class_pointer must point to _OBJ_CLASS_Protocol (ie, if + // protocol is not actually a protocol, then the class cannot conform + // to it) + pr_protocol_t *protocol = &P_STRUCT (pr, pr_protocol_t, 2); + pr_protocol_list_t *proto_list; + + if (!class || !protocol) { + goto not_conforms; + } + if (protocol->class_pointer != PR_SetPointer (pr, + Hash_Find (probj->classes, + "Protocol"))) { + goto not_conforms; + } + proto_list = &G_STRUCT (pr, pr_protocol_list_t, class->protocols); + while (proto_list) { + Sys_MaskPrintf (SYS_rua_obj, "%x %x %d\n", + PR_SetPointer (pr, proto_list), proto_list->next, + proto_list->count); + for (int i = 0; i < proto_list->count; i++) { + __auto_type proto = &G_STRUCT (pr, pr_protocol_t, + proto_list->list[i]); + if (proto == protocol + || obj_protocol_conformsToProtocol (probj, proto, protocol)) { + goto conforms; + } + } + + proto_list = &G_STRUCT (pr, pr_protocol_list_t, proto_list->next); + } +not_conforms: + Sys_MaskPrintf (SYS_rua_obj, "does not conform\n"); + R_INT (pr) = 0; + return; +conforms: + Sys_MaskPrintf (SYS_rua_obj, "conforms\n"); + R_INT (pr) = 1; + return; +} + +static void +rua_PR_FindGlobal (progs_t *pr, void *data) { const char *name = P_GSTRING (pr, 0); - ddef_t *def; + pr_def_t *def; R_POINTER (pr) = 0; def = PR_FindGlobal (pr, name); if (def) - R_POINTER (pr) = def->ofs; //FIXME def's can't access > 32k + R_POINTER (pr) = def->ofs; } //==================================================================== +#define bi(x,np,params...) {#x, rua_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) +#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } static builtin_t obj_methods [] = { - {"__obj_exec_class", rua___obj_exec_class, -1}, + bi(__obj_exec_class, 1, p(ptr)), + bi(__obj_forward, -3, p(ptr), p(ptr)), + bi(__obj_responds_to, 2, p(ptr), p(ptr)), - {"obj_error", rua_obj_error, -1}, - {"obj_verror", rua_obj_verror, -1}, - {"obj_set_error_handler", rua_obj_set_error_handler, -1}, - {"obj_msg_lookup", rua_obj_msg_lookup, -1}, - {"obj_msg_lookup_super", rua_obj_msg_lookup_super, -1}, - {"obj_msg_sendv", rua_obj_msg_sendv, -1}, - {"obj_increment_retaincount", rua_obj_increment_retaincount, -1}, - {"obj_decrement_retaincount", rua_obj_decrement_retaincount, -1}, - {"obj_get_retaincount", rua_obj_get_retaincount, -1}, - {"obj_malloc", rua_obj_malloc, -1}, - {"obj_atomic_malloc", rua_obj_atomic_malloc, -1}, - {"obj_valloc", rua_obj_valloc, -1}, - {"obj_realloc", rua_obj_realloc, -1}, - {"obj_calloc", rua_obj_calloc, -1}, - {"obj_free", rua_obj_free, -1}, - {"obj_get_uninstalled_dtable", rua_obj_get_uninstalled_dtable, -1}, - {"obj_msgSend", rua_obj_msgSend, -1}, - {"obj_msgSend_super", rua_obj_msgSend_super, -1}, + bi(obj_error, -4, p(ptr), p(int), p(string)), + bi(obj_verror, 4, p(ptr), p(int), p(string), P(1, 2)), + bi(obj_set_error_handler, 1, p(func)), + bi(obj_msg_lookup, 2, p(ptr), p(ptr)), + bi(obj_msg_lookup_super, 2, p(ptr), p(ptr)), + bi(obj_msg_sendv, 3, p(ptr), p(ptr), P(1, 2)), + bi(obj_increment_retaincount, 1, p(ptr)), + bi(obj_decrement_retaincount, 1, p(ptr)), + bi(obj_get_retaincount, 1, p(ptr)), + bi(obj_malloc, 1, p(int)), + bi(obj_atomic_malloc, 1, p(int)), + bi(obj_valloc, 1, p(int)), + bi(obj_realloc, 2, p(ptr), p(int)), + bi(obj_calloc, 2, p(int), p(int)), + bi(obj_free, 1, p(ptr)), + bi(obj_get_uninstalled_dtable, 0), + bi(obj_msgSend, 2, p(ptr), p(ptr)),//magic + bi(obj_msgSend_super, 2, p(ptr), p(ptr)),//magic - {"obj_get_class", rua_obj_get_class, -1}, - {"obj_lookup_class", rua_obj_lookup_class, -1}, - {"obj_next_class", rua_obj_next_class, -1}, + bi(obj_get_class, 1, p(string)), + bi(obj_lookup_class, 1, p(string)), + bi(obj_next_class, 1, p(ptr)), - {"sel_get_name", rua_sel_get_name, -1}, - {"sel_get_type", rua_sel_get_type, -1}, - {"sel_get_uid", rua_sel_get_uid, -1}, - {"sel_register_name", rua_sel_register_name, -1}, - {"sel_is_mapped", rua_sel_is_mapped, -1}, + bi(sel_get_name, 1, p(ptr)), + bi(sel_get_type, 1, p(ptr)), + bi(sel_get_uid, 1, p(string)), + bi(sel_register_name, 1, p(string)), + bi(sel_is_mapped, 1, p(ptr)), - {"class_get_class_method", rua_class_get_class_method, -1}, - {"class_get_instance_method", rua_class_get_instance_method, -1}, - {"class_pose_as", rua_class_pose_as, -1}, - {"class_create_instance", rua_class_create_instance, -1}, - {"class_get_class_name", rua_class_get_class_name, -1}, - {"class_get_instance_size", rua_class_get_instance_size, -1}, - {"class_get_meta_class", rua_class_get_meta_class, -1}, - {"class_get_super_class", rua_class_get_super_class, -1}, - {"class_get_version", rua_class_get_version, -1}, - {"class_is_class", rua_class_is_class, -1}, - {"class_is_meta_class", rua_class_is_meta_class, -1}, - {"class_set_version", rua_class_set_version, -1}, - {"class_get_gc_object_type", rua_class_get_gc_object_type, -1}, - {"class_ivar_set_gcinvisible", rua_class_ivar_set_gcinvisible, -1}, + bi(class_get_class_method, 2, p(ptr), p(ptr)), + bi(class_get_instance_method, 2, p(ptr), p(ptr)), + bi(class_pose_as, 2, p(ptr), p(ptr)), + bi(class_create_instance, 1, p(ptr)), + bi(class_get_class_name, 1, p(ptr)), + bi(class_get_instance_size, 1, p(ptr)), + bi(class_get_meta_class, 1, p(ptr)), + bi(class_get_super_class, 1, p(ptr)), + bi(class_get_version, 1, p(ptr)), + bi(class_is_class, 1, p(ptr)), + bi(class_is_meta_class, 1, p(ptr)), + bi(class_set_version, 2, p(ptr), p(int)), + bi(class_get_gc_object_type, 1, p(ptr)), + bi(class_ivar_set_gcinvisible, 3, p(ptr), p(string), p(int)), - {"method_get_imp", rua_method_get_imp, -1}, - {"get_imp", rua_get_imp, -1}, + bi(method_get_imp, 1, p(ptr)), + bi(get_imp, 1, p(ptr), p(ptr)), - {"object_copy", rua_object_copy, -1}, - {"object_dispose", rua_object_dispose, -1}, - {"object_get_class", rua_object_get_class, -1}, - {"object_get_class_name", rua_object_get_class_name, -1}, - {"object_get_meta_class", rua_object_get_meta_class, -1}, - {"object_get_super_class", rua_object_get_super_class, -1}, - {"object_is_class", rua_object_is_class, -1}, - {"object_is_instance", rua_object_is_instance, -1}, - {"object_is_meta_class", rua_object_is_meta_class, -1}, + bi(object_copy, 1, p(ptr)), + bi(object_dispose, 1, p(ptr)), + bi(object_get_class, 1, p(ptr)), + bi(object_get_class_name, 1, p(ptr)), + bi(object_get_meta_class, 1, p(ptr)), + bi(object_get_super_class, 1, p(ptr)), + bi(object_is_class, 1, p(ptr)), + bi(object_is_instance, 1, p(ptr)), + bi(object_is_meta_class, 1, p(ptr)), - {"_i_Object__hash", rua__i_Object__hash, -1}, - {"_i_Object_error_error_", rua__i_Object_error_error_, -1}, - {"_c_Object__conformsToProtocol_", rua__c_Object__conformsToProtocol_, -1}, + bi(_i_Object__hash, 2, p(ptr), p(ptr)), + bi(_i_Object_error_error_, -4, p(ptr), p(ptr), p(string)), + bi(_c_Object__conformsToProtocol_, 3, p(ptr), p(ptr), p(ptr)), - {"PR_FindGlobal", rua_PR_FindGlobal, -1},//FIXME + bi(PR_FindGlobal, 1, p(string)),//FIXME {0} }; static int rua_init_finish (progs_t *pr) { + probj_t *probj = pr->pr_objective_resources; pr_class_t **class_list, **class; - class_list = (pr_class_t **) Hash_GetList (pr->classes); + class_list = (pr_class_t **) Hash_GetList (probj->classes); if (*class_list) { pr_class_t *object_class; - pointer_t object_ptr; + pr_ptr_t object_ptr; - object_class = Hash_Find (pr->classes, "Object"); + object_class = Hash_Find (probj->classes, "Object"); if (object_class && !object_class->super_class) object_ptr = (pr_type_t *)object_class - pr->pr_globals; else PR_Error (pr, "root class Object not found"); for (class = class_list; *class; class++) - finish_class (pr, *class, object_ptr); + finish_class (probj, *class, object_ptr); } free (class_list); @@ -1652,72 +2312,146 @@ rua_init_finish (progs_t *pr) } static int -rua_init_runtime (progs_t *pr) +rua_obj_init_runtime (progs_t *pr) { - ddef_t *def; - unsigned i; - - if (!pr->selector_hash) - pr->selector_hash = Hash_NewTable (1021, selector_get_key, 0, pr); - else - Hash_FlushTable (pr->selector_hash); - pr->selector_index = 0; - for (i = 0; i < pr->selector_index_max; i++) { - obj_list_free (pr->selector_sels[i]); - pr->selector_sels[i] = 0; - pr->selector_names[i] = 0; - } - - if (!pr->classes) - pr->classes = Hash_NewTable (1021, class_get_key, 0, pr); - else - Hash_FlushTable (pr->classes); - - if (!pr->load_methods) { - pr->load_methods = Hash_NewTable (1021, 0, 0, pr); - Hash_SetHashCompare (pr->load_methods, load_methods_get_hash, - load_methods_compare); - } else { - Hash_FlushTable (pr->load_methods); - } - - pr->unresolved_classes = 0; - pr->unclaimed_categories = 0; - pr->unclaimed_proto_list = 0; - pr->module_list = 0; - pr->class_tree_list = 0; + probj_t *probj = pr->pr_objective_resources; + pr_def_t *def; + dfunction_t *obj_forward; if ((def = PR_FindField (pr, ".this"))) pr->fields.this = def->ofs; + probj->obj_forward = 0; + if ((obj_forward = PR_FindFunction (pr, "__obj_forward"))) { + probj->obj_forward = (intptr_t) (obj_forward - pr->pr_functions); + } + PR_AddLoadFinishFunc (pr, rua_init_finish); return 1; } +static void +rua_obj_cleanup (progs_t *pr, void *data) +{ + unsigned i; + + __auto_type probj = (probj_t *) data; + pr->pr_objective_resources = probj; + + Hash_FlushTable (probj->selector_hash); + probj->selector_index = 0; + probj->available_selectors = 0; + probj->selector_block = 0; + for (i = 0; i < probj->selector_index_max; i++) { + obj_list_free (probj, probj->selector_sels[i]); + probj->selector_sels[i] = 0; + probj->selector_names[i] = 0; + } + + for (dtable_t *dtable = probj->dtable_list; dtable; + dtable = dtable->next) { + free (dtable->imp); + } + dtable_reset (probj); + + Hash_FlushTable (probj->classes); + Hash_FlushTable (probj->protocols); + Hash_FlushTable (probj->load_methods); + probj->unresolved_classes = 0; + probj->unclaimed_categories = 0; + probj->unclaimed_proto_list = 0; + probj->module_list = 0; + probj->class_tree_list = 0; +} + +static void +rua_obj_destroy (progs_t *pr, void *_res) +{ + probj_t *probj = _res; + + dstring_delete (probj->msg); + Hash_DelTable (probj->selector_hash); + Hash_DelTable (probj->classes); + Hash_DelTable (probj->protocols); + Hash_DelTable (probj->load_methods); + + free (probj->selector_sels); + free (probj->selector_names); + free (probj->selector_argc); + + for (size_t i = 0; i < probj->obj_list_sets.size; i++) { + free (probj->obj_list_sets.a[i]); + } + DARRAY_CLEAR (&probj->obj_list_sets); + + for (size_t i = 0; i < probj->class_tree_sets.size; i++) { + free (probj->class_tree_sets.a[i]); + } + DARRAY_CLEAR (&probj->class_tree_sets); + + PR_RESDELMAP (probj->dtables); + + free (probj); +} + void RUA_Obj_Init (progs_t *pr, int secure) { - PR_RegisterBuiltins (pr, obj_methods); + probj_t *probj = calloc (1, sizeof (*probj)); - PR_AddLoadFunc (pr, rua_init_runtime); + probj->pr = pr; + probj->selector_hash = Hash_NewTable (1021, selector_get_key, 0, probj, + pr->hashctx); + probj->classes = Hash_NewTable (1021, class_get_key, 0, probj, pr->hashctx); + probj->protocols = Hash_NewTable (1021, protocol_get_key, 0, probj, + pr->hashctx); + probj->load_methods = Hash_NewTable (1021, 0, 0, probj, pr->hashctx); + Hash_SetHashCompare (probj->load_methods, load_methods_get_hash, + load_methods_compare); + probj->msg = dstring_newstr(); + DARRAY_INIT (&probj->obj_list_sets, 32); + DARRAY_INIT (&probj->class_tree_sets, 32); + + PR_Sprintf_SetAtHandler (pr, rua_at_handler, probj); + + PR_Resources_Register (pr, "RUA_ObjectiveQuakeC", probj, rua_obj_cleanup, + rua_obj_destroy); + PR_RegisterBuiltins (pr, obj_methods, probj); + + PR_AddLoadFunc (pr, rua_obj_init_runtime); } -func_t -RUA_Obj_msg_lookup (progs_t *pr, pointer_t _self, pointer_t __cmd) +pr_func_t +RUA_Obj_msg_lookup (progs_t *pr, pr_ptr_t _self, pr_ptr_t __cmd) { + probj_t *probj = pr->pr_objective_resources; pr_id_t *self = &G_STRUCT (pr, pr_id_t, _self); pr_sel_t *_cmd = &G_STRUCT (pr, pr_sel_t, __cmd); - func_t imp; + pr_func_t imp; if (!self) return 0; if (!_cmd) PR_RunError (pr, "null selector"); - imp = obj_msg_lookup (pr, self, _cmd); + imp = obj_msg_lookup (probj, self, _cmd); if (!imp) PR_RunError (pr, "%s does not respond to %s", - PR_GetString (pr, object_get_class_name (pr, self)), - PR_GetString (pr, pr->selector_names[_cmd->sel_id])); + PR_GetString (pr, object_get_class_name (probj, self)), + PR_GetString (pr, probj->selector_names[_cmd->sel_id])); return imp; } + +int +RUA_obj_increment_retaincount (progs_t *pr, void *data) +{ + rua_obj_increment_retaincount (pr, data); + return R_INT (pr); +} + +int +RUA_obj_decrement_retaincount (progs_t *pr, void *data) +{ + rua_obj_decrement_retaincount (pr, data); + return R_INT (pr); +} diff --git a/libs/ruamoko/rua_plist.c b/libs/ruamoko/rua_plist.c index 0fdb1ff8d..972acb237 100644 --- a/libs/ruamoko/rua_plist.c +++ b/libs/ruamoko/rua_plist.c @@ -40,8 +40,8 @@ #include #include "QF/hash.h" +#include "QF/plist.h" #include "QF/progs.h" -#include "QF/qfplist.h" #include "rua_internal.h" @@ -51,66 +51,72 @@ typedef struct bi_plist_s { struct bi_plist_s *next; struct bi_plist_s **prev; plitem_t *plitem; - int own; + int users; } bi_plist_t; typedef struct { PR_RESMAP (bi_plist_t) plist_map; bi_plist_t *plists; - hashtab_t *plist_tab; } plist_resources_t; static bi_plist_t * plist_new (plist_resources_t *res) { - PR_RESNEW (bi_plist_t, res->plist_map); + return PR_RESNEW (res->plist_map); } static void plist_free (plist_resources_t *res, bi_plist_t *plist) { - PR_RESFREE (bi_plist_t, res->plist_map, plist); + PR_RESFREE (res->plist_map, plist); } static void plist_reset (plist_resources_t *res) { - PR_RESRESET (bi_plist_t, res->plist_map); + PR_RESRESET (res->plist_map); } static inline bi_plist_t * plist_get (plist_resources_t *res, unsigned index) { - PR_RESGET(res->plist_map, index); + return PR_RESGET(res->plist_map, index); } -static inline int +static inline int __attribute__((pure)) plist_index (plist_resources_t *res, bi_plist_t *plist) { - PR_RESINDEX(res->plist_map, plist); + return PR_RESINDEX(res->plist_map, plist); } static void -bi_plist_clear (progs_t *pr, void *data) +bi_plist_clear (progs_t *pr, void *_res) { - plist_resources_t *res = (plist_resources_t *) data; + plist_resources_t *res = (plist_resources_t *) _res; bi_plist_t *plist; for (plist = res->plists; plist; plist = plist->next) { - if (plist->own) - PL_Free (plist->plitem); + PL_Release (plist->plitem); } res->plists = 0; - Hash_FlushTable (res->plist_tab); plist_reset (res); } +static void +bi_plist_destroy (progs_t *pr, void *_res) +{ + __auto_type res = (plist_resources_t *) _res; + + PR_RESDELMAP (res->plist_map); + + free (res); +} + static inline int plist_handle (plist_resources_t *res, plitem_t *plitem) { - bi_plist_t dummy = {0, 0, plitem, 0}; - bi_plist_t *plist = Hash_FindElement (res->plist_tab, &dummy); + bi_plist_t *plist = PL_GetUserData (plitem); if (plist) return plist_index (res, plist); @@ -126,26 +132,28 @@ plist_handle (plist_resources_t *res, plitem_t *plitem) res->plists->prev = &plist->next; res->plists = plist; + PL_Retain (plitem); plist->plitem = plitem; - Hash_AddElement (res->plist_tab, plist); + PL_SetUserData (plitem, plist); return plist_index (res, plist); } static inline void plist_free_handle (plist_resources_t *res, bi_plist_t *plist) { - Hash_DelElement (res->plist_tab, plist); + PL_SetUserData (plist->plitem, 0); + PL_Release (plist->plitem); + *plist->prev = plist->next; if (plist->next) plist->next->prev = plist->prev; plist_free (res, plist); } -static always_inline bi_plist_t * -get_plist (progs_t *pr, const char *name, int handle) +static always_inline bi_plist_t * __attribute__((pure)) +get_plist (progs_t *pr, plist_resources_t *res, const char *name, int handle) { - plist_resources_t *res = PR_Resources_Find (pr, "plist"); bi_plist_t *plist = plist_get (res, handle); // plist->prev will be null if the handle is unallocated @@ -158,7 +166,6 @@ static inline int plist_retain (plist_resources_t *res, plitem_t *plitem) { int handle; - bi_plist_t *plist; if (!plitem) return 0; @@ -168,19 +175,17 @@ plist_retain (plist_resources_t *res, plitem_t *plitem) // we're taking ownership of the plitem, but we have nowhere to store // it, so we have to lose it. However, in this situation, we have worse // things to worry about. - PL_Free (plitem); + PL_Release (plitem); return 0; } - plist = plist_get (res, handle); - plist->own = 1; return handle; } static void -bi_PL_GetFromFile (progs_t *pr) +bi_pl_getfromfile (progs_t *pr, plist_resources_t *res, + plitem_t *get (const char *, struct hashctx_s **)) { - plist_resources_t *res = PR_Resources_Find (pr, "plist"); QFile *file = QFile_GetFile (pr, P_INT (pr, 0)); plitem_t *plitem; long offset; @@ -195,25 +200,69 @@ bi_PL_GetFromFile (progs_t *pr) Qread (file, buf, len); buf[len] = 0; - plitem = PL_GetPropertyList (buf); + plitem = get (buf, pr->hashctx); + free (buf); R_INT (pr) = plist_retain (res, plitem); } static void -bi_PL_GetPropertyList (progs_t *pr) +bi_pl_get (progs_t *pr, plist_resources_t *res, + plitem_t *get (const char *, struct hashctx_s **)) { - plist_resources_t *res = PR_Resources_Find (pr, "plist"); - plitem_t *plitem = PL_GetPropertyList (P_GSTRING (pr, 0)); + plitem_t *plitem = get (P_GSTRING (pr, 0), pr->hashctx); R_INT (pr) = plist_retain (res, plitem); } static void -bi_PL_WritePropertyList (progs_t *pr) +bi_PL_GetFromFile (progs_t *pr, void *_res) { + plist_resources_t *res = _res; + bi_pl_getfromfile (pr, res, PL_GetPropertyList); +} + +static void +bi_PL_GetPropertyList (progs_t *pr, void *_res) +{ + plist_resources_t *res = _res; + bi_pl_get (pr, res, PL_GetPropertyList); +} + +static void +bi_PL_GetDictionaryFromFile (progs_t *pr, void *_res) +{ + plist_resources_t *res = _res; + bi_pl_getfromfile (pr, res, PL_GetDictionary); +} + +static void +bi_PL_GetDictionary (progs_t *pr, void *_res) +{ + plist_resources_t *res = _res; + bi_pl_get (pr, res, PL_GetDictionary); +} + +static void +bi_PL_GetArrayFromFile (progs_t *pr, void *_res) +{ + plist_resources_t *res = _res; + bi_pl_getfromfile (pr, res, PL_GetArray); +} + +static void +bi_PL_GetArray (progs_t *pr, void *_res) +{ + plist_resources_t *res = _res; + bi_pl_get (pr, res, PL_GetArray); +} + +static void +bi_PL_WritePropertyList (progs_t *pr, void *_res) +{ + plist_resources_t *res = _res; int handle = P_INT (pr, 0); - bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle); + bi_plist_t *plist = get_plist (pr, res, __FUNCTION__, handle); char *pl = PL_WritePropertyList (plist->plitem); R_STRING (pr) = PR_SetDynamicString (pr, pl); @@ -221,30 +270,42 @@ bi_PL_WritePropertyList (progs_t *pr) } static void -bi_PL_Type (progs_t *pr) +bi_PL_Type (progs_t *pr, void *_res) { + plist_resources_t *res = _res; int handle = P_INT (pr, 0); - bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle); + bi_plist_t *plist = get_plist (pr, res, __FUNCTION__, handle); R_INT (pr) = PL_Type (plist->plitem); } static void -bi_PL_String (progs_t *pr) +bi_PL_Line (progs_t *pr, void *_res) { + plist_resources_t *res = _res; int handle = P_INT (pr, 0); - bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle); + bi_plist_t *plist = get_plist (pr, res, __FUNCTION__, handle); + + R_INT (pr) = PL_Line (plist->plitem); +} + +static void +bi_PL_String (progs_t *pr, void *_res) +{ + plist_resources_t *res = _res; + int handle = P_INT (pr, 0); + bi_plist_t *plist = get_plist (pr, res, __FUNCTION__, handle); const char *str = PL_String (plist->plitem); RETURN_STRING (pr, str); } static void -bi_PL_ObjectForKey (progs_t *pr) +bi_PL_ObjectForKey (progs_t *pr, void *_res) { - plist_resources_t *res = PR_Resources_Find (pr, "plist"); + plist_resources_t *res = _res; int handle = P_INT (pr, 0); - bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle); + bi_plist_t *plist = get_plist (pr, res, __FUNCTION__, handle); const char *key = P_GSTRING (pr, 1); plitem_t *plitem = PL_ObjectForKey (plist->plitem, key); @@ -255,23 +316,21 @@ bi_PL_ObjectForKey (progs_t *pr) } static void -bi_PL_RemoveObjectForKey (progs_t *pr) +bi_PL_RemoveObjectForKey (progs_t *pr, void *_res) { - plist_resources_t *res = PR_Resources_Find (pr, "plist"); + plist_resources_t *res = _res; int handle = P_INT (pr, 0); - bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle); + bi_plist_t *plist = get_plist (pr, res, __FUNCTION__, handle); const char *key = P_GSTRING (pr, 1); - plitem_t *plitem = PL_RemoveObjectForKey (plist->plitem, key); - - R_INT (pr) = plist_retain (res, plitem); + PL_RemoveObjectForKey (plist->plitem, key); } static void -bi_PL_ObjectAtIndex (progs_t *pr) +bi_PL_ObjectAtIndex (progs_t *pr, void *_res) { - plist_resources_t *res = PR_Resources_Find (pr, "plist"); + plist_resources_t *res = _res; int handle = P_INT (pr, 0); - bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle); + bi_plist_t *plist = get_plist (pr, res, __FUNCTION__, handle); int ind = P_INT (pr, 1); plitem_t *plitem = PL_ObjectAtIndex (plist->plitem, ind); @@ -282,172 +341,196 @@ bi_PL_ObjectAtIndex (progs_t *pr) } static void -bi_PL_D_AllKeys (progs_t *pr) +bi_PL_D_AllKeys (progs_t *pr, void *_res) { - plist_resources_t *res = PR_Resources_Find (pr, "plist"); + plist_resources_t *res = _res; int handle = P_INT (pr, 0); - bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle); + bi_plist_t *plist = get_plist (pr, res, __FUNCTION__, handle); plitem_t *plitem = PL_D_AllKeys (plist->plitem); R_INT (pr) = plist_retain (res, plitem); } static void -bi_PL_D_NumKeys (progs_t *pr) +bi_PL_D_NumKeys (progs_t *pr, void *_res) { + plist_resources_t *res = _res; int handle = P_INT (pr, 0); - bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle); + bi_plist_t *plist = get_plist (pr, res, __FUNCTION__, handle); R_INT (pr) = PL_D_NumKeys (plist->plitem); } static void -bi_PL_D_AddObject (progs_t *pr) +bi_PL_KeyAtIndex (progs_t *pr, void *_res) { + plist_resources_t *res = _res; + int handle = P_INT (pr, 0); + int index = P_INT (pr, 1); + bi_plist_t *plist = get_plist (pr, res, __FUNCTION__, handle); + const char *key = PL_KeyAtIndex (plist->plitem, index); + + RETURN_STRING (pr, key); +} + +static void +bi_PL_D_AddObject (progs_t *pr, void *_res) +{ + plist_resources_t *res = _res; int dict_handle = P_INT (pr, 0); int obj_handle = P_INT (pr, 2); - bi_plist_t *dict = get_plist (pr, __FUNCTION__, dict_handle); + bi_plist_t *dict = get_plist (pr, res, __FUNCTION__, dict_handle); const char *key = P_GSTRING (pr, 1); - bi_plist_t *obj = get_plist (pr, __FUNCTION__, obj_handle); + bi_plist_t *obj = get_plist (pr, res, __FUNCTION__, obj_handle); - obj->own = 0; R_INT (pr) = PL_D_AddObject (dict->plitem, key, obj->plitem); } static void -bi_PL_A_AddObject (progs_t *pr) +bi_PL_A_AddObject (progs_t *pr, void *_res) { + plist_resources_t *res = _res; int arr_handle = P_INT (pr, 0); int obj_handle = P_INT (pr, 1); - bi_plist_t *arr = get_plist (pr, __FUNCTION__, arr_handle); - bi_plist_t *obj = get_plist (pr, __FUNCTION__, obj_handle); + bi_plist_t *arr = get_plist (pr, res, __FUNCTION__, arr_handle); + bi_plist_t *obj = get_plist (pr, res, __FUNCTION__, obj_handle); - obj->own = 0; R_INT (pr) = PL_A_AddObject (arr->plitem, obj->plitem); } static void -bi_PL_A_NumObjects (progs_t *pr) +bi_PL_A_NumObjects (progs_t *pr, void *_res) { + plist_resources_t *res = _res; int handle = P_INT (pr, 0); - bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle); + bi_plist_t *plist = get_plist (pr, res, __FUNCTION__, handle); R_INT (pr) = PL_A_NumObjects (plist->plitem); } static void -bi_PL_A_InsertObjectAtIndex (progs_t *pr) +bi_PL_A_InsertObjectAtIndex (progs_t *pr, void *_res) { + plist_resources_t *res = _res; int dict_handle = P_INT (pr, 0); int obj_handle = P_INT (pr, 1); - bi_plist_t *arr = get_plist (pr, __FUNCTION__, dict_handle); - bi_plist_t *obj = get_plist (pr, __FUNCTION__, obj_handle); + bi_plist_t *arr = get_plist (pr, res, __FUNCTION__, dict_handle); + bi_plist_t *obj = get_plist (pr, res, __FUNCTION__, obj_handle); int ind = P_INT (pr, 2); - obj->own = 0; R_INT (pr) = PL_A_InsertObjectAtIndex (arr->plitem, obj->plitem, ind); } static void -bi_PL_RemoveObjectAtIndex (progs_t *pr) +bi_PL_RemoveObjectAtIndex (progs_t *pr, void *_res) { - plist_resources_t *res = PR_Resources_Find (pr, "plist"); + plist_resources_t *res = _res; int handle = P_INT (pr, 0); - bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle); + bi_plist_t *plist = get_plist (pr, res, __FUNCTION__, handle); int ind = P_INT (pr, 1); - plitem_t *plitem = PL_RemoveObjectAtIndex (plist->plitem, ind); + PL_RemoveObjectAtIndex (plist->plitem, ind); +} + +static void +bi_PL_NewDictionary (progs_t *pr, void *_res) +{ + plist_resources_t *res = _res; + plitem_t *plitem = PL_NewDictionary (pr->hashctx); R_INT (pr) = plist_retain (res, plitem); } static void -bi_PL_NewDictionary (progs_t *pr) +bi_PL_NewArray (progs_t *pr, void *_res) { - plist_resources_t *res = PR_Resources_Find (pr, "plist"); - plitem_t *plitem = PL_NewDictionary (); - - R_INT (pr) = plist_retain (res, plitem); -} - -static void -bi_PL_NewArray (progs_t *pr) -{ - plist_resources_t *res = PR_Resources_Find (pr, "plist"); + plist_resources_t *res = _res; plitem_t *plitem = PL_NewArray (); R_INT (pr) = plist_retain (res, plitem); } static void -bi_PL_NewData (progs_t *pr) +bi_PL_NewData (progs_t *pr, void *_res) { //FIXME not safe - plist_resources_t *res = PR_Resources_Find (pr, "plist"); + plist_resources_t *res = _res; plitem_t *plitem = PL_NewData (P_GPOINTER (pr, 0), P_INT (pr, 1)); R_INT (pr) = plist_retain (res, plitem); } static void -bi_PL_NewString (progs_t *pr) +bi_PL_NewString (progs_t *pr, void *_res) { - plist_resources_t *res = PR_Resources_Find (pr, "plist"); + plist_resources_t *res = _res; plitem_t *plitem = PL_NewString (P_GSTRING (pr, 0)); R_INT (pr) = plist_retain (res, plitem); } static void -bi_PL_Free (progs_t *pr) +bi_PL_Release (progs_t *pr, void *_res) +{ + plist_resources_t *res = _res; + int handle = P_INT (pr, 0); + bi_plist_t *plist = get_plist (pr, res, __FUNCTION__, handle); + + if (!(plist->users && --plist->users > 0)) { + plist_free_handle (res, plist); + handle = 0; + } + R_INT (pr) = handle; +} + +static void +bi_PL_Retain (progs_t *pr, void *_res) +{ + plist_resources_t *res = _res; + int handle = P_INT (pr, 0); + bi_plist_t *plist = get_plist (pr, res, __FUNCTION__, handle); + + plist->users++; + R_INT (pr) = handle; +} + +plitem_t * +Plist_GetItem (progs_t *pr, int handle) { plist_resources_t *res = PR_Resources_Find (pr, "plist"); - int handle = P_INT (pr, 0); - bi_plist_t *plist = get_plist (pr, __FUNCTION__, handle); - - if (!plist->own) - PR_RunError (pr, "attempt to free unowned plist"); - - PL_Free (plist->plitem); - - plist_free_handle (res, plist); -} - -static uintptr_t -plist_get_hash (const void *key, void *unused) -{ - bi_plist_t *plist = (bi_plist_t *) key; - return (uintptr_t) plist->plitem; -} - -static int -plist_compare (const void *k1, const void *k2, void *unused) -{ - bi_plist_t *pl1 = (bi_plist_t *) k1; - bi_plist_t *pl2 = (bi_plist_t *) k2; - return pl1->plitem == pl2->plitem; + bi_plist_t *plist = get_plist (pr, res, __FUNCTION__, handle); + return plist->plitem; } +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) static builtin_t builtins[] = { - {"PL_GetFromFile", bi_PL_GetFromFile, -1}, - {"PL_GetPropertyList", bi_PL_GetPropertyList, -1}, - {"PL_WritePropertyList", bi_PL_WritePropertyList, -1}, - {"PL_Type", bi_PL_Type, -1}, - {"PL_String", bi_PL_String, -1}, - {"PL_ObjectForKey", bi_PL_ObjectForKey, -1}, - {"PL_RemoveObjectForKey", bi_PL_RemoveObjectForKey, -1}, - {"PL_ObjectAtIndex", bi_PL_ObjectAtIndex, -1}, - {"PL_D_AllKeys", bi_PL_D_AllKeys, -1}, - {"PL_D_NumKeys", bi_PL_D_NumKeys, -1}, - {"PL_D_AddObject", bi_PL_D_AddObject, -1}, - {"PL_A_AddObject", bi_PL_A_AddObject, -1}, - {"PL_A_NumObjects", bi_PL_A_NumObjects, -1}, - {"PL_A_InsertObjectAtIndex", bi_PL_A_InsertObjectAtIndex, -1}, - {"PL_RemoveObjectAtIndex", bi_PL_RemoveObjectAtIndex, -1}, - {"PL_NewDictionary", bi_PL_NewDictionary, -1}, - {"PL_NewArray", bi_PL_NewArray, -1}, - {"PL_NewData", bi_PL_NewData, -1}, - {"PL_NewString", bi_PL_NewString, -1}, - {"PL_Free", bi_PL_Free, -1}, + bi(PL_GetFromFile, 1, p(ptr)), + bi(PL_GetPropertyList, 1, p(string)), + bi(PL_GetDictionary, 1, p(string)), + bi(PL_GetDictionaryFromFile, 1, p(string)), + bi(PL_GetArray, 1, p(string)), + bi(PL_GetArrayFromFile, 1, p(string)), + bi(PL_WritePropertyList, 1, p(ptr)), + bi(PL_Type, 1, p(ptr)), + bi(PL_Line, 1, p(ptr)), + bi(PL_String, 1, p(ptr)), + bi(PL_ObjectForKey, 2, p(ptr), p(string)), + bi(PL_RemoveObjectForKey, 2, p(ptr), p(string)), + bi(PL_ObjectAtIndex, 2, p(ptr), p(int)), + bi(PL_D_AllKeys, 1, p(ptr)), + bi(PL_D_NumKeys, 1, p(ptr)), + bi(PL_KeyAtIndex, 2, p(ptr), p(int)), + bi(PL_D_AddObject, 3, p(ptr), p(string), p(ptr)), + bi(PL_A_AddObject, 2, p(ptr), p(ptr)), + bi(PL_A_NumObjects, 1, p(ptr)), + bi(PL_A_InsertObjectAtIndex, 3, p(ptr), p(ptr), p(int)), + bi(PL_RemoveObjectAtIndex, 2, p(ptr), p(int)), + bi(PL_NewDictionary, 0), + bi(PL_NewArray, 0), + bi(PL_NewData, 2, p(ptr), p(int)), + bi(PL_NewString, 1, p(string)), + bi(PL_Release, 1, p(ptr)), + bi(PL_Retain, 1, p(ptr)), {0} }; @@ -455,9 +538,7 @@ void RUA_Plist_Init (progs_t *pr, int secure) { plist_resources_t *res = calloc (1, sizeof (plist_resources_t)); - res->plist_tab = Hash_NewTable (1021, 0, 0, 0); - Hash_SetHashCompare (res->plist_tab, plist_get_hash, plist_compare); - PR_Resources_Register (pr, "plist", res, bi_plist_clear); - PR_RegisterBuiltins (pr, builtins); + PR_Resources_Register (pr, "plist", res, bi_plist_clear, bi_plist_destroy); + PR_RegisterBuiltins (pr, builtins, res); } diff --git a/libs/ruamoko/rua_qfile.c b/libs/ruamoko/rua_qfile.c index 057ff0ef6..4de8c55ad 100644 --- a/libs/ruamoko/rua_qfile.c +++ b/libs/ruamoko/rua_qfile.c @@ -38,9 +38,7 @@ #include "QF/dstring.h" #include "QF/progs.h" -#include "QF/quakefs.h" -#include "QF/va.h" -#include "QF/zone.h" +#include "QF/quakeio.h" #include "rua_internal.h" @@ -58,37 +56,37 @@ typedef struct { static qfile_t * handle_new (qfile_resources_t *res) { - PR_RESNEW (qfile_t, res->handle_map); + return PR_RESNEW (res->handle_map); } static void handle_free (qfile_resources_t *res, qfile_t *handle) { - PR_RESFREE (qfile_t, res->handle_map, handle); + PR_RESFREE (res->handle_map, handle); } static void handle_reset (qfile_resources_t *res) { - PR_RESRESET (qfile_t, res->handle_map); + PR_RESRESET (res->handle_map); } static inline qfile_t * handle_get (qfile_resources_t *res, int index) { - PR_RESGET(res->handle_map, index); + return PR_RESGET(res->handle_map, index); } -static inline int +static inline int __attribute__((pure)) handle_index (qfile_resources_t *res, qfile_t *handle) { - PR_RESINDEX(res->handle_map, handle); + return PR_RESINDEX(res->handle_map, handle); } static void -bi_qfile_clear (progs_t *pr, void *data) +bi_qfile_clear (progs_t *pr, void *_res) { - qfile_resources_t *res = (qfile_resources_t *) data; + qfile_resources_t *res = (qfile_resources_t *) _res; qfile_t *handle; for (handle = res->handles; handle; handle = handle->next) @@ -97,6 +95,16 @@ bi_qfile_clear (progs_t *pr, void *data) handle_reset (res); } +static void +bi_qfile_destroy (progs_t *pr, void *_res) +{ + qfile_resources_t *res = _res; + + PR_RESDELMAP (res->handle_map); + + free (res); +} + static int alloc_handle (qfile_resources_t *res, QFile *file) { @@ -123,13 +131,13 @@ QFile_AllocHandle (progs_t *pr, QFile *file) } static void -secured (progs_t *pr) +secured (progs_t *pr, void *_res) { PR_RunError (pr, "Secured function called"); } static void -bi_Qrename (progs_t *pr) +bi_Qrename (progs_t *pr, void *_res) { const char *old = P_GSTRING (pr, 0); const char *new = P_GSTRING (pr, 1); @@ -138,7 +146,7 @@ bi_Qrename (progs_t *pr) } static void -bi_Qremove (progs_t *pr) +bi_Qremove (progs_t *pr, void *_res) { const char *path = P_GSTRING (pr, 0); @@ -146,9 +154,9 @@ bi_Qremove (progs_t *pr) } static void -bi_Qopen (progs_t *pr) +bi_Qopen (progs_t *pr, void *_res) { - qfile_resources_t *res = PR_Resources_Find (pr, "QFile"); + __auto_type res = (qfile_resources_t *) _res; const char *path = P_GSTRING (pr, 0); const char *mode = P_GSTRING (pr, 1); QFile *file; @@ -160,10 +168,9 @@ bi_Qopen (progs_t *pr) Qclose (file); } -static qfile_t * -get_handle (progs_t *pr, const char *name, int handle) +static qfile_t * __attribute__((pure)) +get_handle (progs_t *pr, qfile_resources_t *res, const char *name, int handle) { - qfile_resources_t *res = PR_Resources_Find (pr, "QFile"); qfile_t *h = handle_get (res, handle); if (!h) @@ -174,15 +181,16 @@ get_handle (progs_t *pr, const char *name, int handle) QFile * QFile_GetFile (progs_t *pr, int handle) { - qfile_t *h = get_handle (pr, __FUNCTION__, handle); + qfile_resources_t *res = PR_Resources_Find (pr, "QFile"); + qfile_t *h = get_handle (pr, res, __FUNCTION__, handle); return h->file; } static void -bi_Qclose (progs_t *pr) +bi_Qclose (progs_t *pr, void *_res) { - qfile_resources_t *res = PR_Resources_Find (pr, "QFile"); + __auto_type res = (qfile_resources_t *) _res; int handle = P_INT (pr, 0); qfile_t *h = handle_get (res, handle); @@ -196,10 +204,11 @@ bi_Qclose (progs_t *pr) } static void -bi_Qgetline (progs_t *pr) +bi_Qgetline (progs_t *pr, void *_res) { + __auto_type res = (qfile_resources_t *) _res; int handle = P_INT (pr, 0); - qfile_t *h = get_handle (pr, __FUNCTION__, handle); + qfile_t *h = get_handle (pr, res, __FUNCTION__, handle); const char *s; s = Qgetline (h->file); @@ -210,12 +219,13 @@ bi_Qgetline (progs_t *pr) } static void -bi_Qreadstring (progs_t *pr) +bi_Qreadstring (progs_t *pr, void *_res) { + __auto_type res = (qfile_resources_t *) _res; int handle = P_INT (pr, 0); int len = P_INT (pr, 1); - qfile_t *h = get_handle (pr, __FUNCTION__, handle); - string_t str = PR_NewMutableString (pr); + qfile_t *h = get_handle (pr, res, __FUNCTION__, handle); + pr_string_t str = PR_NewMutableString (pr); dstring_t *dstr = PR_GetMutableString (pr, str); dstr->size = len + 1; @@ -237,10 +247,11 @@ check_buffer (progs_t *pr, pr_type_t *buf, int count, const char *name) } static void -bi_Qread (progs_t *pr) +bi_Qread (progs_t *pr, void *_res) { + __auto_type res = (qfile_resources_t *) _res; int handle = P_INT (pr, 0); - qfile_t *h = get_handle (pr, __FUNCTION__, handle); + qfile_t *h = get_handle (pr, res, __FUNCTION__, handle); pr_type_t *buf = P_GPOINTER (pr, 1); int count = P_INT (pr, 2); @@ -249,10 +260,11 @@ bi_Qread (progs_t *pr) } static void -bi_Qwrite (progs_t *pr) +bi_Qwrite (progs_t *pr, void *_res) { + __auto_type res = (qfile_resources_t *) _res; int handle = P_INT (pr, 0); - qfile_t *h = get_handle (pr, __FUNCTION__, handle); + qfile_t *h = get_handle (pr, res, __FUNCTION__, handle); pr_type_t *buf = P_GPOINTER (pr, 1); int count = P_INT (pr, 2); @@ -261,20 +273,22 @@ bi_Qwrite (progs_t *pr) } static void -bi_Qputs (progs_t *pr) +bi_Qputs (progs_t *pr, void *_res) { + __auto_type res = (qfile_resources_t *) _res; int handle = P_INT (pr, 0); - qfile_t *h = get_handle (pr, __FUNCTION__, handle); + qfile_t *h = get_handle (pr, res, __FUNCTION__, handle); const char *str = P_GSTRING (pr, 1); R_INT (pr) = Qputs (h->file, str); } #if 0 static void -bi_Qgets (progs_t *pr) +bi_Qgets (progs_t *pr, void *_res) { + __auto_type res = (qfile_resources_t *) _res; int handle = P_INT (pr, 0); - qfile_t *h = get_handle (pr, __FUNCTION__, handle); + qfile_t *h = get_handle (pr, res, __FUNCTION__, handle); pr_type_t *buf = P_GPOINTER (pr, 1); int count = P_INT (pr, 2); @@ -283,29 +297,32 @@ bi_Qgets (progs_t *pr) } #endif static void -bi_Qgetc (progs_t *pr) +bi_Qgetc (progs_t *pr, void *_res) { + __auto_type res = (qfile_resources_t *) _res; int handle = P_INT (pr, 0); - qfile_t *h = get_handle (pr, __FUNCTION__, handle); + qfile_t *h = get_handle (pr, res, __FUNCTION__, handle); R_INT (pr) = Qgetc (h->file); } static void -bi_Qputc (progs_t *pr) +bi_Qputc (progs_t *pr, void *_res) { + __auto_type res = (qfile_resources_t *) _res; int handle = P_INT (pr, 0); - qfile_t *h = get_handle (pr, __FUNCTION__, handle); + qfile_t *h = get_handle (pr, res, __FUNCTION__, handle); int c = P_INT (pr, 1); R_INT (pr) = Qputc (h->file, c); } static void -bi_Qseek (progs_t *pr) +bi_Qseek (progs_t *pr, void *_res) { + __auto_type res = (qfile_resources_t *) _res; int handle = P_INT (pr, 0); - qfile_t *h = get_handle (pr, __FUNCTION__, handle); + qfile_t *h = get_handle (pr, res, __FUNCTION__, handle); int offset = P_INT (pr, 1); int whence = P_INT (pr, 2); @@ -313,70 +330,79 @@ bi_Qseek (progs_t *pr) } static void -bi_Qtell (progs_t *pr) +bi_Qtell (progs_t *pr, void *_res) { + __auto_type res = (qfile_resources_t *) _res; int handle = P_INT (pr, 0); - qfile_t *h = get_handle (pr, __FUNCTION__, handle); + qfile_t *h = get_handle (pr, res, __FUNCTION__, handle); R_INT (pr) = Qtell (h->file); } static void -bi_Qflush (progs_t *pr) +bi_Qflush (progs_t *pr, void *_res) { + __auto_type res = (qfile_resources_t *) _res; int handle = P_INT (pr, 0); - qfile_t *h = get_handle (pr, __FUNCTION__, handle); + qfile_t *h = get_handle (pr, res, __FUNCTION__, handle); R_INT (pr) = Qflush (h->file); } static void -bi_Qeof (progs_t *pr) +bi_Qeof (progs_t *pr, void *_res) { + __auto_type res = (qfile_resources_t *) _res; int handle = P_INT (pr, 0); - qfile_t *h = get_handle (pr, __FUNCTION__, handle); + qfile_t *h = get_handle (pr, res, __FUNCTION__, handle); R_INT (pr) = Qeof (h->file); } static void -bi_Qfilesize (progs_t *pr) +bi_Qfilesize (progs_t *pr, void *_res) { + __auto_type res = (qfile_resources_t *) _res; int handle = P_INT (pr, 0); - qfile_t *h = get_handle (pr, __FUNCTION__, handle); + qfile_t *h = get_handle (pr, res, __FUNCTION__, handle); R_INT (pr) = Qfilesize (h->file); } +#define bi(x,np,params...) {#x, secured, -1, np, {params}} +#define p(type) PR_PARAM(type) +#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } static builtin_t secure_builtins[] = { - {"Qrename", secured, -1}, - {"Qremove", secured, -1}, - {"Qopen", secured, -1}, + bi(Qrename, 2, p(string), p(string)), + bi(Qremove, 1, p(string)), + bi(Qopen, 2, p(string), p(string)), {0} }; +#undef bi +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} static builtin_t insecure_builtins[] = { - {"Qrename", bi_Qrename, -1}, - {"Qremove", bi_Qremove, -1}, - {"Qopen", bi_Qopen, -1}, + bi(Qrename, 2, p(string), p(string)), + bi(Qremove, 1, p(string)), + bi(Qopen, 2, p(string), p(string)), {0} }; static builtin_t builtins[] = { - {"Qclose", bi_Qclose, -1}, - {"Qgetline", bi_Qgetline, -1}, - {"Qreadstring", bi_Qreadstring, -1}, - {"Qread", bi_Qread, -1}, - {"Qwrite", bi_Qwrite, -1}, - {"Qputs", bi_Qputs, -1}, -// {"Qgets", bi_Qgets, -1}, - {"Qgetc", bi_Qgetc, -1}, - {"Qputc", bi_Qputc, -1}, - {"Qseek", bi_Qseek, -1}, - {"Qtell", bi_Qtell, -1}, - {"Qflush", bi_Qflush, -1}, - {"Qeof", bi_Qeof, -1}, - {"Qfilesize", bi_Qfilesize, -1}, + bi(Qclose, 1, p(ptr)), + bi(Qgetline, 1, p(ptr)), + bi(Qreadstring, 2, p(ptr), p(int)), + bi(Qread, 3, p(ptr), p(ptr), p(int)), + bi(Qwrite, 3, p(ptr), p(ptr), p(int)), + bi(Qputs, 2, p(ptr), p(string)), +// bi(Qgets, _, _), + bi(Qgetc, 1, p(ptr)), + bi(Qputc, 2, p(ptr), p(int)), + bi(Qseek, 3, p(ptr), p(int), p(int)), + bi(Qtell, 1, p(ptr)), + bi(Qflush, 1, p(ptr)), + bi(Qeof, 1, p(ptr)), + bi(Qfilesize, 1, p(ptr)), {0} }; @@ -385,11 +411,11 @@ RUA_QFile_Init (progs_t *pr, int secure) { qfile_resources_t *res = calloc (sizeof (qfile_resources_t), 1); - PR_Resources_Register (pr, "QFile", res, bi_qfile_clear); + PR_Resources_Register (pr, "QFile", res, bi_qfile_clear, bi_qfile_destroy); if (secure) { - PR_RegisterBuiltins (pr, secure_builtins); + PR_RegisterBuiltins (pr, secure_builtins, res); } else { - PR_RegisterBuiltins (pr, insecure_builtins); + PR_RegisterBuiltins (pr, insecure_builtins, res); } - PR_RegisterBuiltins (pr, builtins); + PR_RegisterBuiltins (pr, builtins, res); } diff --git a/libs/ruamoko/rua_qfs.c b/libs/ruamoko/rua_qfs.c index 35d69d0c8..f920678ca 100644 --- a/libs/ruamoko/rua_qfs.c +++ b/libs/ruamoko/rua_qfs.c @@ -45,7 +45,7 @@ typedef struct { int count; - pointer_t list; + pr_ptr_t list; } qfslist_t; static void @@ -60,7 +60,7 @@ check_buffer (progs_t *pr, pr_type_t *buf, int count, const char *name) static void -bi_QFS_Open (progs_t *pr) +bi_QFS_Open (progs_t *pr, void *data) { QFile *file; const char *path = P_GSTRING (pr, 0); @@ -75,7 +75,7 @@ bi_QFS_Open (progs_t *pr) } static void -bi_QFS_WOpen (progs_t *pr) +bi_QFS_WOpen (progs_t *pr, void *data) { QFile *file; const char *path = P_GSTRING (pr, 0); @@ -90,7 +90,7 @@ bi_QFS_WOpen (progs_t *pr) } static void -bi_QFS_Rename (progs_t *pr) +bi_QFS_Rename (progs_t *pr, void *data) { const char *old = P_GSTRING (pr, 0); const char *new = P_GSTRING (pr, 1); @@ -99,7 +99,7 @@ bi_QFS_Rename (progs_t *pr) } static void -bi_QFS_LoadFile (progs_t *pr) +bi_QFS_LoadFile (progs_t *pr, void *data) { const char *filename = P_GSTRING (pr, 0); QFile *file; @@ -124,7 +124,7 @@ bi_QFS_LoadFile (progs_t *pr) } static void -bi_QFS_OpenFile (progs_t *pr) +bi_QFS_OpenFile (progs_t *pr, void *data) { QFile *file; const char *filename = P_GSTRING (pr, 0); @@ -139,22 +139,23 @@ bi_QFS_OpenFile (progs_t *pr) } static void -bi_QFS_WriteFile (progs_t *pr) +bi_QFS_WriteFile (progs_t *pr, void *data) { const char *filename = P_GSTRING (pr, 0); pr_type_t *buf = P_GPOINTER (pr, 1); int count = P_INT (pr, 2); check_buffer (pr, buf, count, "QFS_WriteFile"); - QFS_WriteFile (va ("%s/%s", qfs_gamedir->dir.def, filename), buf, count); + QFS_WriteFile (va (0, "%s/%s", qfs_gamedir->dir.def, filename), buf, + count); } static void -bi_QFS_Filelist (progs_t *pr) +bi_QFS_Filelist (progs_t *pr, void *data) { filelist_t *filelist = QFS_FilelistNew (); qfslist_t *list; - string_t *strings; + pr_string_t *strings; int i; QFS_FilelistFill (filelist, P_GSTRING (pr, 0), P_GSTRING (pr, 1), @@ -162,7 +163,7 @@ bi_QFS_Filelist (progs_t *pr) list = PR_Zone_Malloc (pr, sizeof (list) + filelist->count * 4); list->count = filelist->count; - strings = (string_t *) (list + 1); + strings = (pr_string_t *) (list + 1); list->list = PR_SetPointer (pr, strings); for (i = 0; i < filelist->count; i++) strings[i] = PR_SetDynamicString (pr, filelist->list[i]); @@ -170,10 +171,10 @@ bi_QFS_Filelist (progs_t *pr) } static void -bi_QFS_FilelistFree (progs_t *pr) +bi_QFS_FilelistFree (progs_t *pr, void *data) { qfslist_t *list = &P_STRUCT (pr, qfslist_t, 0); - string_t *strings = &G_STRUCT (pr, string_t, list->list); + pr_string_t *strings = &G_STRUCT (pr, pr_string_t, list->list); int i; for (i = 0; i < list->count; i++) @@ -181,20 +182,29 @@ bi_QFS_FilelistFree (progs_t *pr) PR_Zone_Free (pr, list); } +static void +bi_QFS_GetDirectory (progs_t *pr, void *data) +{ + RETURN_STRING (pr, qfs_gamedir->dir.def); +} + +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) static builtin_t builtins[] = { - {"QFS_Open", bi_QFS_Open, -1}, - {"QFS_WOpen", bi_QFS_WOpen, -1}, - {"QFS_Rename", bi_QFS_Rename, -1}, - {"QFS_LoadFile", bi_QFS_LoadFile, -1}, - {"QFS_OpenFile", bi_QFS_OpenFile, -1}, - {"QFS_WriteFile", bi_QFS_WriteFile, -1}, - {"QFS_Filelist", bi_QFS_Filelist, -1}, - {"QFS_FilelistFree", bi_QFS_FilelistFree, -1}, + bi(QFS_Open, 2, p(string), p(string)), + bi(QFS_WOpen, 2, p(string), p(int)), + bi(QFS_Rename, 2, p(string), p(string)), + bi(QFS_LoadFile, 1, p(string)), + bi(QFS_OpenFile, 1, p(string)), + bi(QFS_WriteFile, 3, p(string), p(ptr), p(int)), + bi(QFS_Filelist, 3, p(string), p(string), p(int)), + bi(QFS_FilelistFree, 1, p(ptr)), + bi(QFS_GetDirectory, 0), {0} }; void RUA_QFS_Init (progs_t *pr, int secure) { - PR_RegisterBuiltins (pr, builtins); + PR_RegisterBuiltins (pr, builtins, 0); } diff --git a/libs/ruamoko/rua_runtime.c b/libs/ruamoko/rua_runtime.c new file mode 100644 index 000000000..7ea55a68b --- /dev/null +++ b/libs/ruamoko/rua_runtime.c @@ -0,0 +1,84 @@ +/* + bi_runtime.c + + QuakeC runtime api + + Copyright (C) 2020 Bill Currie + + Author: Bill Currie + Date: 2020/4/1 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include + +#include "qfalloca.h" + +#if defined(_WIN32) && defined(HAVE_MALLOC_H) +#include +#endif + +#include "QF/progs.h" + +#include "rua_internal.h" + +static void +bi_va_copy (progs_t *pr, void *data) +{ + __auto_type src_args = (pr_va_list_t *) &P_POINTER (pr, 0); + __auto_type src_list = &G_STRUCT (pr, pr_type_t, src_args->list); + size_t parm_size = pr->pr_param_size * sizeof(pr_type_t); + size_t size = src_args->count * parm_size; + pr_string_t dst_list_block = 0; + pr_type_t *dst_list = 0; + + if (size) { + dst_list_block = PR_AllocTempBlock (pr, size); + dst_list = (pr_type_t *) PR_GetString (pr, dst_list_block); + } + + memcpy (dst_list, src_list, size); + R_PACKED (pr, pr_va_list_t).count = src_args->count; + R_PACKED (pr, pr_va_list_t).list = PR_SetPointer (pr, dst_list); +} + +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) +#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } +static builtin_t builtins[] = { + bi(va_copy, 1, P(1, 2)), + {0} +}; + +void +RUA_Runtime_Init (progs_t *pr, int secure) +{ + PR_RegisterBuiltins (pr, builtins, 0); +} diff --git a/libs/ruamoko/rua_scene.c b/libs/ruamoko/rua_scene.c new file mode 100644 index 000000000..e0a8a023a --- /dev/null +++ b/libs/ruamoko/rua_scene.c @@ -0,0 +1,675 @@ +/* + bi_scene.c + + Ruamoko scene builtins + + Copyright (C) 2022 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/cmem.h" +#include "QF/hash.h" +#include "QF/model.h" +#include "QF/progs.h" +#include "QF/render.h" + +#include "QF/plugin/vid_render.h" + +#include "QF/scene/entity.h" +#include "QF/scene/light.h" +#include "QF/scene/scene.h" +#include "QF/scene/transform.h" + +#include "rua_internal.h" + +typedef struct rua_scene_s { + struct rua_scene_s *next; + struct rua_scene_s **prev; + scene_t *scene; +} rua_scene_t; + +typedef struct rua_lighting_s { + struct rua_lighting_s *next; + struct rua_lighting_s **prev; + lightingdata_t *ldata; +} rua_lighting_t; + +typedef struct rua_scene_resources_s { + progs_t *pr; + PR_RESMAP (rua_scene_t) scene_map; + PR_RESMAP (rua_lighting_t) lighting_map; + rua_scene_t *scenes; + rua_lighting_t *ldatas; +} rua_scene_resources_t; + +static rua_scene_t * +rua_scene_new (rua_scene_resources_t *res) +{ + return PR_RESNEW (res->scene_map); +} + +static void +rua_scene_free (rua_scene_resources_t *res, rua_scene_t *scene) +{ + if (scene->next) { + scene->next->prev = scene->prev; + } + *scene->prev = scene->next; + scene->prev = 0; + PR_RESFREE (res->scene_map, scene); +} + +static rua_scene_t * __attribute__((pure)) +rua__scene_get (rua_scene_resources_t *res, pr_ulong_t id, const char *name) +{ + rua_scene_t *scene = 0; + + id &= 0xffffffffu; + scene = PR_RESGET (res->scene_map, (pr_int_t) id); + + // scene->prev will be null if the handle is unallocated + if (!scene || !scene->prev) { + PR_RunError (res->pr, "invalid scene passed to %s", name + 3); + } + return scene; +} +#define rua_scene_get(res, id) rua__scene_get(res, id, __FUNCTION__) + +static int __attribute__((pure)) +rua_scene_index (rua_scene_resources_t *res, rua_scene_t *scene) +{ + return PR_RESINDEX (res->scene_map, scene); +} + +static entity_t __attribute__((pure)) +rua__entity_get (rua_scene_resources_t *res, pr_ulong_t id, const char *name) +{ + pr_ulong_t scene_id = id & 0xffffffff; + entity_t ent = nullentity; + + rua_scene_t *scene = rua__scene_get (res, scene_id, name); + if (scene) { + pr_int_t entity_id = id >> 32; + ent.id = entity_id; + ent.reg = scene->scene->reg; + } + + if (!Entity_Valid (ent)) { + PR_RunError (res->pr, "invalid entity passed to %s", name + 3); + } + return ent; +} +#define rua_entity_get(res, id) rua__entity_get(res, id, __FUNCTION__) + +static transform_t __attribute__((pure)) +rua__transform_get (rua_scene_resources_t *res, pr_ulong_t id, const char *name) +{ + pr_ulong_t scene_id = id & 0xffffffff; + transform_t transform = nulltransform; + + rua_scene_t *scene = rua_scene_get (res, scene_id); + if (scene) { + entity_t transform_id = { .reg = scene->scene->reg, .id = id >> 32 }; + transform = Entity_Transform (transform_id); + } + + if (!Transform_Valid (transform)) { + PR_RunError (res->pr, "invalid transform passed to %s", name + 3); + } + return transform; +} +#define rua_transform_get(res, id) rua__transform_get(res, id, __FUNCTION__) + +static rua_lighting_t * +rua_lighting_new (rua_scene_resources_t *res) +{ + return PR_RESNEW (res->lighting_map); +} + +static void +rua_lighting_free (rua_scene_resources_t *res, rua_lighting_t *ldata) +{ + if (ldata->next) { + ldata->next->prev = ldata->prev; + } + *ldata->prev = ldata->next; + ldata->prev = 0; + PR_RESFREE (res->lighting_map, ldata); +} + +static rua_lighting_t * __attribute__((pure)) +rua__lighting_get (rua_scene_resources_t *res, pr_ulong_t id, const char *name) +{ + rua_lighting_t *ldata = 0; + + if (id <= 0xffffffffu) { + ldata = PR_RESGET (res->lighting_map, (pr_int_t) id); + } + + // ldata->prev will be null if the handle is unallocated + if (!ldata || !ldata->prev) { + PR_RunError (res->pr, "invalid lighting passed to %s", name + 3); + } + return ldata; +} +#define rua_lighting_get(res, id) rua__lighting_get(res, id, __FUNCTION__) + +static int __attribute__((pure)) +rua_lighting_index (rua_scene_resources_t *res, rua_lighting_t *ldata) +{ + return PR_RESINDEX (res->lighting_map, ldata); +} + +#define MAKE_ID(id, sc_id) ((((pr_ulong_t) (id)) << 32) \ + | ((sc_id) & 0xffffffff)) + +static void +bi_Scene_NewScene (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + + rua_scene_t *scene = rua_scene_new (res); + + scene->scene = Scene_NewScene (); + + scene->next = res->scenes; + if (res->scenes) { + res->scenes->prev = &scene->next; + } + scene->prev = &res->scenes; + res->scenes = scene; + + // scene id in lower 32-bits for all handles + // zero upper 32-bits zero means scene, otherwise transform or entity + R_ULONG (pr) = MAKE_ID (0, rua_scene_index (res, scene)); +} + +static void +rua_delete_scene (rua_scene_resources_t *res, rua_scene_t *scene) +{ + Scene_DeleteScene (scene->scene); + rua_scene_free (res, scene); +} + +static void +bi_Scene_DeleteScene (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + rua_scene_t *scene = rua_scene_get (res, P_ULONG (pr, 0)); + + rua_delete_scene (res, scene); +} + +scene_t * +Scene_GetScene (progs_t *pr, pr_ulong_t handle) +{ + if (!handle) { + return 0; + } + rua_scene_resources_t *res = PR_Resources_Find (pr, "Scene"); + rua_scene_t *scene = rua_scene_get (res, P_ULONG (pr, 0)); + return scene->scene; +} + +static void +bi_Scene_CreateEntity (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + pr_ulong_t scene_id = P_ULONG (pr, 0); + rua_scene_t *scene = rua_scene_get (res, scene_id); + entity_t ent = Scene_CreateEntity (scene->scene); + R_ULONG (pr) = MAKE_ID (ent.id, scene_id); +} + +static void +bi_Scene_DestroyEntity (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + pr_ulong_t id = P_ULONG (pr, 0); + entity_t ent = rua_entity_get (res, id); + pr_ulong_t scene_id = id & 0xffffffff; + rua_scene_t *scene = rua_scene_get (res, scene_id); + + // bad scene caught above + Scene_DestroyEntity (scene->scene, ent); +} + +static void +bi_Scene_SetLighting (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + pr_ulong_t scene_id = P_ULONG (pr, 0); + rua_scene_t *scene = rua_scene_get (res, scene_id); + pr_ulong_t ldata_id = P_ULONG (pr, 1); + rua_lighting_t *ldata = 0; + + if (ldata_id) { + ldata = rua_lighting_get (res, ldata_id); + } + + scene->scene->lights = ldata->ldata; +} + +static void +bi_Entity_GetTransform (progs_t *pr, void *_res) +{ + pr_ulong_t ent_id = P_ULONG (pr, 0); + + // ent_id is used to fetch the transform every time + R_ULONG (pr) = ent_id; +} + +static void +bi_Entity_SetModel (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + pr_ulong_t ent_id = P_ULONG (pr, 0); + pr_int_t model_id = P_INT (pr, 1); + entity_t ent = rua_entity_get (res, ent_id); + model_t *model = Model_GetModel (pr, model_id); + pr_ulong_t scene_id = ent_id & 0xffffffff; + // bad scene caught above + rua_scene_t *scene = rua_scene_get (res, scene_id); + + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); + renderer->model = model; + R_AddEfrags (&scene->scene->worldmodel->brush, ent); +} + +static void +bi_Transform_ChildCount (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + + R_UINT (pr) = Transform_ChildCount (transform); +} + +static void +bi_Transform_GetChild (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + pr_ulong_t transform_id = P_ULONG (pr, 0); + transform_t transform = rua_transform_get (res, transform_id); + transform_t child = Transform_GetChild (transform, P_UINT (pr, 2)); + R_ULONG (pr) = Transform_Valid (child) ? MAKE_ID (child.id, transform_id) + : 0; +} + +static void +bi_Transform_SetParent (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + transform_t parent = rua_transform_get (res, P_ULONG (pr, 1)); + + Transform_SetParent (transform, parent); +} + +static void +bi_Transform_GetParent (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + pr_ulong_t transform_id = P_ULONG (pr, 0); + transform_t transform = rua_transform_get (res, transform_id); + transform_t parent = Transform_GetParent (transform); + + // transform_id contains scene id + R_ULONG (pr) = Transform_Valid (parent) ? MAKE_ID (parent.id, transform_id) + : 0; +} + +static void +bi_Transform_SetTag (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + pr_uint_t tag = P_UINT (pr, 2); + Transform_SetTag (transform, tag); +} + +static void +bi_Transform_GetTag (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + + R_UINT (pr) = Transform_GetTag (transform); +} + +static void +bi_Transform_GetLocalMatrix (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + Transform_GetLocalMatrix (transform, &R_PACKED (pr, pr_vec4_t)); +} + +static void +bi_Transform_GetLocalInverse (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + Transform_GetLocalInverse (transform, &R_PACKED (pr, pr_vec4_t)); +} + +static void +bi_Transform_GetWorldMatrix (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + Transform_GetWorldMatrix (transform, &R_PACKED (pr, pr_vec4_t)); +} + +static void +bi_Transform_GetWorldInverse (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + Transform_GetWorldInverse (transform, &R_PACKED (pr, pr_vec4_t)); +} + +static void +bi_Transform_SetLocalPosition (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + Transform_SetLocalPosition (transform, P_PACKED (pr, pr_vec4_t, 1)); +} + +static void +bi_Transform_GetLocalPosition (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + R_PACKED (pr, pr_vec4_t) = Transform_GetLocalPosition (transform); +} + +static void +bi_Transform_SetLocalRotation (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + Transform_SetLocalRotation (transform, P_PACKED (pr, pr_vec4_t, 1)); +} + +static void +bi_Transform_GetLocalRotation (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + R_PACKED (pr, pr_vec4_t) = Transform_GetLocalRotation (transform); +} + +static void +bi_Transform_SetLocalScale (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + Transform_SetLocalScale (transform, P_PACKED (pr, pr_vec4_t, 1)); +} + +static void +bi_Transform_GetLocalScale (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + R_PACKED (pr, pr_vec4_t) = Transform_GetLocalScale (transform); +} + +static void +bi_Transform_SetWorldPosition (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + Transform_SetWorldPosition (transform, P_PACKED (pr, pr_vec4_t, 1)); +} + +static void +bi_Transform_GetWorldPosition (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + R_PACKED (pr, pr_vec4_t) = Transform_GetWorldPosition (transform); +} + +static void +bi_Transform_SetWorldRotation (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + Transform_SetWorldRotation (transform, P_PACKED (pr, pr_vec4_t, 1)); +} + +static void +bi_Transform_GetWorldRotation (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + R_PACKED (pr, pr_vec4_t) = Transform_GetWorldRotation (transform); +} + +static void +bi_Transform_GetWorldScale (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + R_PACKED (pr, pr_vec4_t) = Transform_GetWorldScale (transform); +} + +static void +bi_Transform_SetLocalTransform (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + Transform_SetLocalTransform (transform, P_PACKED (pr, pr_vec4_t, 1), + P_PACKED (pr, pr_vec4_t, 2), P_PACKED (pr, pr_vec4_t, 3)); +} + +static void +bi_Transform_Forward (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + R_PACKED (pr, pr_vec4_t) = Transform_Forward (transform); +} + +static void +bi_Transform_Right (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + R_PACKED (pr, pr_vec4_t) = Transform_Right (transform); +} + +static void +bi_Transform_Up (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + transform_t transform = rua_transform_get (res, P_ULONG (pr, 0)); + R_PACKED (pr, pr_vec4_t) = Transform_Up (transform); +} + +static void +bi_Light_CreateLightingData (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + pr_ulong_t scene_id = P_ULONG (pr, 0); + rua_scene_t *scene = rua_scene_get (res, scene_id); + + rua_lighting_t *ldata = rua_lighting_new (res); + + ldata->ldata = Light_CreateLightingData (scene->scene); + + ldata->next = res->ldatas; + if (res->ldatas) { + res->ldatas->prev = &ldata->next; + } + ldata->prev = &res->ldatas; + res->ldatas = ldata; + + // ldata id in lower 32-bits for all handles + // upper 32-bits reserved + R_ULONG (pr) = MAKE_ID (0, rua_lighting_index (res, ldata)); +} + +static void +rua_delete_lighting (rua_scene_resources_t *res, rua_lighting_t *ldata) +{ + Light_DestroyLightingData (ldata->ldata); + rua_lighting_free (res, ldata); +} + +static void +bi_Light_DestroyLightingData (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + rua_lighting_t *ldata = rua_lighting_get (res, P_ULONG (pr, 0)); + + rua_delete_lighting (res, ldata); +} + +static void +bi_Light_ClearLights (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + rua_lighting_t *ldata = rua_lighting_get (res, P_ULONG (pr, 0)); + + Light_ClearLights (ldata->ldata); +} + +static void +bi_Light_AddLight (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + rua_lighting_t *ldata = rua_lighting_get (res, P_ULONG (pr, 0)); + light_t *light = &P_PACKED (pr, light_t, 1); + int style = P_INT (pr, 5); + + Light_AddLight (ldata->ldata, light, style); +} + +static void +bi_Light_EnableSun (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + rua_lighting_t *ldata = rua_lighting_get (res, P_ULONG (pr, 0)); + + Light_EnableSun (ldata->ldata); +} + +#define p(type) PR_PARAM(type) +#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +static builtin_t builtins[] = { + bi(Scene_NewScene, 0), + bi(Scene_DeleteScene, 1, p(ulong)), + bi(Scene_CreateEntity, 1, p(ulong)), + bi(Scene_DestroyEntity, 1, p(ulong)), + bi(Scene_SetLighting , 2, p(ulong), p(ulong)), + + bi(Entity_GetTransform, 1, p(ulong)), + bi(Entity_SetModel, 2, p(ulong), p(int)), + + bi(Transform_ChildCount, 1, p(ulong)), + bi(Transform_GetChild, 2, p(ulong), p(int)), + bi(Transform_SetParent, 2, p(ulong), p(ulong)), + bi(Transform_GetParent, 1, p(ulong)), + + bi(Transform_SetTag, 2, p(ulong), p(uint)), + bi(Transform_GetTag, 1, p(ulong)), + + bi(Transform_GetLocalMatrix, 1, p(ulong)), + bi(Transform_GetLocalInverse, 1, p(ulong)), + bi(Transform_GetWorldMatrix, 1, p(ulong)), + bi(Transform_GetWorldInverse, 1, p(ulong)), + + bi(Transform_SetLocalPosition, 2, p(ulong), p(vec4)), + bi(Transform_GetLocalPosition, 1, p(ulong)), + bi(Transform_SetLocalRotation, 2, p(ulong), p(vec4)), + bi(Transform_GetLocalRotation, 1, p(ulong)), + bi(Transform_SetLocalScale, 2, p(ulong), p(vec4)), + bi(Transform_GetLocalScale, 1, p(ulong)), + + bi(Transform_SetWorldPosition, 2, p(ulong), p(vec4)), + bi(Transform_GetWorldPosition, 1, p(ulong)), + bi(Transform_SetWorldRotation, 2, p(ulong), p(vec4)), + bi(Transform_GetWorldRotation, 1, p(ulong)), + bi(Transform_GetWorldScale, 1, p(ulong)), + + bi(Transform_SetLocalTransform, 4, p(ulong), p(vec4), p(vec4), p(vec4)), + bi(Transform_Forward, 1, p(ulong)), + bi(Transform_Right, 1, p(ulong)), + bi(Transform_Up, 1, p(ulong)), + + bi(Light_CreateLightingData, 1, p(ulong)), + bi(Light_DestroyLightingData, 1, p(ulong)), + bi(Light_ClearLights, 1, p(ulong)), + bi(Light_AddLight, 5, p(ulong),// really, 3 + p(vec4), p(vec4), p(vec4), p(vec4), p(int)), + bi(Light_EnableSun, 1, p(ulong)), + + {0} +}; + +static void +bi_scene_clear (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + + while (res->ldatas) { + rua_delete_lighting (res, res->ldatas); + } + while (res->scenes) { + rua_delete_scene (res, res->scenes); + } +} + +static void +bi_scene_destroy (progs_t *pr, void *_res) +{ + rua_scene_resources_t *res = _res; + PR_RESDELMAP (res->scene_map); + PR_RESDELMAP (res->lighting_map); + free (res); +} + +void +RUA_Scene_Init (progs_t *pr, int secure) +{ + rua_scene_resources_t *res = calloc (sizeof (rua_scene_resources_t), 1); + + res->pr = pr; + + PR_Resources_Register (pr, "Scene", res, bi_scene_clear, bi_scene_destroy); + PR_RegisterBuiltins (pr, builtins, res); +} diff --git a/libs/ruamoko/rua_script.c b/libs/ruamoko/rua_script.c index c6fae5750..41e8cacd8 100644 --- a/libs/ruamoko/rua_script.c +++ b/libs/ruamoko/rua_script.c @@ -44,65 +44,72 @@ #include "rua_internal.h" -typedef struct { +typedef struct rua_script_s { + struct rua_script_s *next; + struct rua_script_s **prev; script_t script; - string_t dstr; + char *text; // only for Script_FromFile + pr_string_t dstr; progs_t *pr; - string_t err_msg; } rua_script_t; typedef struct { - PR_RESMAP(rua_script_t) scripts; + PR_RESMAP(rua_script_t) script_map; + rua_script_t *scripts; } script_resources_t; static rua_script_t * script_new (script_resources_t *res) { - PR_RESNEW (rua_script_t, res->scripts); + return PR_RESNEW (res->script_map); } static void script_free (script_resources_t *res, rua_script_t *script) { - PR_RESFREE (rua_script_t, res->scripts, script); + PR_RESFREE (res->script_map, script); } static void script_reset (script_resources_t *res) { - PR_RESRESET (rua_script_t, res->scripts); + PR_RESRESET (res->script_map); } static inline rua_script_t * script_get (script_resources_t *res, int index) { - PR_RESGET(res->scripts, index); + return PR_RESGET(res->script_map, index); } -static inline int +static inline int __attribute__((pure)) script_index (script_resources_t *res, rua_script_t *script) { - PR_RESINDEX(res->scripts, script); + return PR_RESINDEX(res->script_map, script); } static void -bi_script_clear (progs_t *pr, void *data) +bi_script_clear (progs_t *pr, void *_res) { - script_resources_t *res = (script_resources_t *) data; + script_resources_t *res = (script_resources_t *) _res; + for (rua_script_t *s = res->scripts; s; s = s->next) { + free ((char *) s->script.single); + free (s->text); + } + res->scripts = 0; script_reset (res); } static void -bi_script_error (script_t *_script, const char *msg) +bi_script_destroy (progs_t *pr, void *_res) { - rua_script_t *script = (rua_script_t *)_script; - script->err_msg = PR_SetString (script->pr, msg); + free (_res); } static void -bi_Script_New (progs_t *pr) +bi_Script_New (progs_t *pr, void *_res) { - script_resources_t *res = PR_Resources_Find (pr, "Script"); + script_resources_t *res = _res; rua_script_t *script = script_new (res); if (!script) @@ -110,27 +117,38 @@ bi_Script_New (progs_t *pr) script->dstr = PR_NewMutableString (pr); script->script.token = PR_GetMutableString (pr, script->dstr); - script->script.error = bi_script_error; script->pr = pr; + script->next = res->scripts; + script->prev = &res->scripts; + if (res->scripts) { + res->scripts->prev = &script->next; + } + res->scripts = script; R_INT (pr) = script_index (res, script); } static void -bi_Script_Delete (progs_t *pr) +bi_Script_Delete (progs_t *pr, void *_res) { - script_resources_t *res = PR_Resources_Find (pr, "Script"); + script_resources_t *res = _res; rua_script_t *script = script_get (res, P_INT (pr, 0)); if (!script) PR_RunError (pr, "invalid script handle"); PR_FreeString (pr, script->dstr); + free ((char *) script->script.single); + free (script->text); + if (script->next) { + script->next->prev = script->prev; + } + *script->prev = script->next; script_free (res, script); } static void -bi_Script_Start (progs_t *pr) +bi_Script_Start (progs_t *pr, void *_res) { - script_resources_t *res = PR_Resources_Find (pr, "Script"); + script_resources_t *res = _res; rua_script_t *script = script_get (res, P_INT (pr, 0)); if (!script) @@ -140,9 +158,32 @@ bi_Script_Start (progs_t *pr) } static void -bi_Script_TokenAvailable (progs_t *pr) +bi_Script_FromFile (progs_t *pr, void *_res) { - script_resources_t *res = PR_Resources_Find (pr, "Script"); + script_resources_t *res = _res; + rua_script_t *script = script_get (res, P_INT (pr, 0)); + if (!script) + PR_RunError (pr, "invalid script handle"); + QFile *file = QFile_GetFile (pr, P_INT (pr, 2)); + long offset; + long size; + long len; + + offset = Qtell (file); + size = Qfilesize (file); + len = size - offset; + script->text = malloc (len + 1); + Qread (file, script->text, len); + script->text[len] = 0; + + Script_Start (&script->script, P_GSTRING (pr, 1), script->text); + R_STRING (pr) = script->dstr; +} + +static void +bi_Script_TokenAvailable (progs_t *pr, void *_res) +{ + script_resources_t *res = _res; rua_script_t *script = script_get (res, P_INT (pr, 0)); if (!script) @@ -151,9 +192,9 @@ bi_Script_TokenAvailable (progs_t *pr) } static void -bi_Script_GetToken (progs_t *pr) +bi_Script_GetToken (progs_t *pr, void *_res) { - script_resources_t *res = PR_Resources_Find (pr, "Script"); + script_resources_t *res = _res; rua_script_t *script = script_get (res, P_INT (pr, 0)); if (!script) @@ -162,9 +203,9 @@ bi_Script_GetToken (progs_t *pr) } static void -bi_Script_UngetToken (progs_t *pr) +bi_Script_UngetToken (progs_t *pr, void *_res) { - script_resources_t *res = PR_Resources_Find (pr, "Script"); + script_resources_t *res = _res; rua_script_t *script = script_get (res, P_INT (pr, 0)); if (!script) @@ -173,21 +214,21 @@ bi_Script_UngetToken (progs_t *pr) } static void -bi_Script_Error (progs_t *pr) +bi_Script_Error (progs_t *pr, void *_res) { - script_resources_t *res = PR_Resources_Find (pr, "Script"); + script_resources_t *res = _res; rua_script_t *script = script_get (res, P_INT (pr, 0)); if (!script) PR_RunError (pr, "invalid script handle"); - R_STRING (pr) = script->err_msg; - script->err_msg = 0; + RETURN_STRING (pr, script->script.error); + script->script.error = 0; } static void -bi_Script_NoQuoteLines (progs_t *pr) +bi_Script_NoQuoteLines (progs_t *pr, void *_res) { - script_resources_t *res = PR_Resources_Find (pr, "Script"); + script_resources_t *res = _res; rua_script_t *script = script_get (res, P_INT (pr, 0)); if (!script) @@ -196,15 +237,45 @@ bi_Script_NoQuoteLines (progs_t *pr) script->script.no_quote_lines = P_INT (pr, 1); } +static void +bi_Script_SetSingle (progs_t *pr, void *_res) +{ + script_resources_t *res = _res; + rua_script_t *script = script_get (res, P_INT (pr, 0)); + if (!script) + PR_RunError (pr, "invalid script handle"); + free ((char *) script->script.single); + script->script.single = 0; + const char *single = P_GSTRING (pr, 1); + if (single) { + script->script.single = strdup (single); + } +} + +static void +bi_Script_GetLine (progs_t *pr, void *_res) +{ + script_resources_t *res = _res; + rua_script_t *script = script_get (res, P_INT (pr, 0)); + if (!script) + PR_RunError (pr, "invalid script handle"); + R_INT (pr) = script->script.line; +} + +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) static builtin_t builtins[] = { - {"Script_New", bi_Script_New, -1}, - {"Script_Delete", bi_Script_Delete, -1}, - {"Script_Start", bi_Script_Start, -1}, - {"Script_TokenAvailable", bi_Script_TokenAvailable, -1}, - {"Script_GetToken", bi_Script_GetToken, -1}, - {"Script_UngetToken", bi_Script_UngetToken, -1}, - {"Script_Error", bi_Script_Error, -1}, - {"Script_NoQuoteLines", bi_Script_NoQuoteLines, -1}, + bi(Script_New, 0), + bi(Script_Delete, 1, p(ptr)), + bi(Script_Start, 3, p(ptr), p(string), p(string)), + bi(Script_FromFile, 3, p(ptr), p(string), p(ptr)), + bi(Script_TokenAvailable, 2, p(ptr), p(int)), + bi(Script_GetToken, 2, p(ptr), p(int)), + bi(Script_UngetToken, 1, p(ptr)), + bi(Script_Error, 1, p(ptr)), + bi(Script_NoQuoteLines, 1, p(ptr)), + bi(Script_SetSingle, 2, p(ptr), p(string)), + bi(Script_GetLine, 1, p(ptr)), {0} }; @@ -213,7 +284,7 @@ RUA_Script_Init (progs_t *pr, int secure) { script_resources_t *res = calloc (1, sizeof (script_resources_t)); - PR_Resources_Register (pr, "Script", res, bi_script_clear); - - PR_RegisterBuiltins (pr, builtins); + PR_Resources_Register (pr, "Script", res, bi_script_clear, + bi_script_destroy); + PR_RegisterBuiltins (pr, builtins, res); } diff --git a/libs/ruamoko/rua_set.c b/libs/ruamoko/rua_set.c index 0fdd964e7..161cfa8c2 100644 --- a/libs/ruamoko/rua_set.c +++ b/libs/ruamoko/rua_set.c @@ -40,9 +40,10 @@ #include #include "QF/progs.h" -#include "QF/pr_obj.h" #include "QF/set.h" +#include "QF/progs/pr_obj.h" + #include "rua_internal.h" typedef struct { @@ -52,6 +53,7 @@ typedef struct { typedef struct { pr_id_t obj; + pr_int_t set; pr_int_t iter; } pr_set_iter_t; @@ -77,67 +79,66 @@ typedef struct { static bi_set_t * res_set_new (set_resources_t *res) { - PR_RESNEW (bi_set_t, res->set_map); + return PR_RESNEW (res->set_map); } static void res_set_free (set_resources_t *res, bi_set_t *set) { - PR_RESFREE (bi_set_t, res->set_map, set); + PR_RESFREE (res->set_map, set); } static void res_set_reset (set_resources_t *res) { - PR_RESRESET (bi_set_t, res->set_map); + PR_RESRESET (res->set_map); } static inline bi_set_t * res_set_get (set_resources_t *res, int index) { - PR_RESGET(res->set_map, index); + return PR_RESGET(res->set_map, index); } -static inline int +static inline int __attribute__((pure)) res_set_index (set_resources_t *res, bi_set_t *set) { - PR_RESINDEX(res->set_map, set); + return PR_RESINDEX(res->set_map, set); } static bi_set_iter_t * res_set_iter_new (set_resources_t *res) { - PR_RESNEW (bi_set_iter_t, res->set_iter_map); + return PR_RESNEW (res->set_iter_map); } static void res_set_iter_free (set_resources_t *res, bi_set_iter_t *set_iter) { - PR_RESFREE (bi_set_iter_t, res->set_iter_map, set_iter); + PR_RESFREE (res->set_iter_map, set_iter); } static void res_set_iter_reset (set_resources_t *res) { - PR_RESRESET (bi_set_iter_t, res->set_iter_map); + PR_RESRESET (res->set_iter_map); } static inline bi_set_iter_t * res_set_iter_get (set_resources_t *res, int index) { - PR_RESGET(res->set_iter_map, index); + return PR_RESGET(res->set_iter_map, index); } -static inline int +static inline int __attribute__((pure)) res_set_iter_index (set_resources_t *res, bi_set_iter_t *set_iter) { - PR_RESINDEX(res->set_iter_map, set_iter); + return PR_RESINDEX(res->set_iter_map, set_iter); } -static bi_set_t * -get_set (progs_t *pr, const char *name, int index) +static bi_set_t * __attribute__((pure)) +get_set (progs_t *pr, set_resources_t *res, const char *name, int index) { - set_resources_t *res = PR_Resources_Find (pr, "Set"); bi_set_t *set = res_set_get (res, index); if (!set) @@ -145,10 +146,9 @@ get_set (progs_t *pr, const char *name, int index) return set; } -static bi_set_iter_t * -get_set_iter (progs_t *pr, const char *name, int index) +static bi_set_iter_t * __attribute__((pure)) +get_set_iter (progs_t *pr, set_resources_t *res, const char *name, int index) { - set_resources_t *res = PR_Resources_Find (pr, "Set"); bi_set_iter_t *set = res_set_iter_get (res, index); if (!set) @@ -157,10 +157,8 @@ get_set_iter (progs_t *pr, const char *name, int index) } static void -del_set_iter (progs_t *pr, bi_set_iter_t *set_iter) +del_set_iter (progs_t *pr, set_resources_t *res, bi_set_iter_t *set_iter) { - set_resources_t *res = PR_Resources_Find (pr, "Set"); - *set_iter->prev = set_iter->next; if (set_iter->next) set_iter->next->prev = set_iter->prev; @@ -168,18 +166,31 @@ del_set_iter (progs_t *pr, bi_set_iter_t *set_iter) } static void -bi_set_del_iter (progs_t *pr) +bi_set_del_iter (progs_t *pr, void *_res) { - bi_set_iter_t *set_iter = get_set_iter (pr, __FUNCTION__, P_INT (pr, 0)); + set_resources_t *res = _res; + bi_set_iter_t *set_iter = get_set_iter (pr, res, __FUNCTION__, + P_INT (pr, 0)); set_del_iter (set_iter->iter); - del_set_iter (pr, set_iter); + del_set_iter (pr, res, set_iter); } static void -bi_set_new (progs_t *pr) +bi_set_iter_element (progs_t *pr, void *_res) { - set_resources_t *res = PR_Resources_Find (pr, "Set"); + set_resources_t *res = _res; + bi_set_iter_t *set_iter = get_set_iter (pr, res, __FUNCTION__, + P_INT (pr, 0)); + + R_INT (pr) = set_iter->iter->element; + +} + +static void +bi_set_new (progs_t *pr, void *_res) +{ + set_resources_t *res = _res; bi_set_t *set; set = res_set_new (res); @@ -196,10 +207,10 @@ bi_set_new (progs_t *pr) } static void -bi_set_delete (progs_t *pr) +bi_set_delete (progs_t *pr, void *_res) { - set_resources_t *res = PR_Resources_Find (pr, "Set"); - bi_set_t *set = get_set (pr, __FUNCTION__, P_INT (pr, 0)); + set_resources_t *res = _res; + bi_set_t *set = get_set (pr, res, __FUNCTION__, P_INT (pr, 0)); set_delete (set->set); *set->prev = set->next; @@ -209,173 +220,311 @@ bi_set_delete (progs_t *pr) } static void -bi_set_add (progs_t *pr) +rua_set_add (progs_t *pr, set_resources_t *res, + pr_int_t setid, pr_uint_t element) { - bi_set_t *set = get_set (pr, __FUNCTION__, P_INT (pr, 0)); + bi_set_t *set = get_set (pr, res, __FUNCTION__, setid); - set_add (set->set, P_UINT (pr, 1)); + set_add (set->set, element); +} + +static void +bi_set_add (progs_t *pr, void *_res) +{ + set_resources_t *res = _res; + rua_set_add (pr, res, P_INT (pr, 0), P_UINT (pr, 1)); R_INT (pr) = P_INT (pr, 0); } static void -bi_set_remove (progs_t *pr) +rua_set_remove (progs_t *pr, set_resources_t *res, + pr_int_t setid, pr_uint_t element) { - bi_set_t *set = get_set (pr, __FUNCTION__, P_INT (pr, 0)); + bi_set_t *set = get_set (pr, res, __FUNCTION__, setid); - set_remove (set->set, P_UINT (pr, 1)); + set_remove (set->set, element); +} + +static void +bi_set_remove (progs_t *pr, void *_res) +{ + set_resources_t *res = _res; + rua_set_remove (pr, res, P_INT (pr, 0), P_UINT (pr, 1)); R_INT (pr) = P_INT (pr, 0); } static void -bi_set_invert (progs_t *pr) +rua_set_invert (progs_t *pr, set_resources_t *res, pr_int_t setid) { - bi_set_t *set = get_set (pr, __FUNCTION__, P_INT (pr, 0)); + bi_set_t *set = get_set (pr, res, __FUNCTION__, setid); set_invert (set->set); +} + +static void +bi_set_invert (progs_t *pr, void *_res) +{ + set_resources_t *res = _res; + rua_set_invert (pr, res, P_INT (pr, 0)); R_INT (pr) = P_INT (pr, 0); } static void -bi_set_union (progs_t *pr) +rua_set_union (progs_t *pr, set_resources_t *res, + pr_int_t dstid, pr_int_t srcid) { - bi_set_t *set1 = get_set (pr, __FUNCTION__, P_INT (pr, 0)); - bi_set_t *set2 = get_set (pr, __FUNCTION__, P_INT (pr, 1)); + bi_set_t *dst = get_set (pr, res, __FUNCTION__, dstid); + bi_set_t *src = get_set (pr, res, __FUNCTION__, srcid); - set_union (set1->set, set2->set); + set_union (dst->set, src->set); +} + +static void +bi_set_union (progs_t *pr, void *_res) +{ + set_resources_t *res = _res; + rua_set_union (pr, res, P_INT (pr, 0), P_INT (pr, 1)); R_INT (pr) = P_INT (pr, 0); } static void -bi_set_intersection (progs_t *pr) +rua_set_intersection (progs_t *pr, set_resources_t *res, + pr_int_t dstid, pr_int_t srcid) { - bi_set_t *set1 = get_set (pr, __FUNCTION__, P_INT (pr, 0)); - bi_set_t *set2 = get_set (pr, __FUNCTION__, P_INT (pr, 1)); + bi_set_t *dst = get_set (pr, res, __FUNCTION__, dstid); + bi_set_t *src = get_set (pr, res, __FUNCTION__, srcid); - set_intersection (set1->set, set2->set); + set_intersection (dst->set, src->set); +} + +static void +bi_set_intersection (progs_t *pr, void *_res) +{ + set_resources_t *res = _res; + rua_set_intersection (pr, res, P_INT (pr, 0), P_INT (pr, 1)); R_INT (pr) = P_INT (pr, 0); } static void -bi_set_difference (progs_t *pr) +rua_set_difference (progs_t *pr, set_resources_t *res, + pr_int_t dstid, pr_int_t srcid) { - bi_set_t *set1 = get_set (pr, __FUNCTION__, P_INT (pr, 0)); - bi_set_t *set2 = get_set (pr, __FUNCTION__, P_INT (pr, 1)); + bi_set_t *dst = get_set (pr, res, __FUNCTION__, dstid); + bi_set_t *src = get_set (pr, res, __FUNCTION__, srcid); - set_difference (set1->set, set2->set); + set_difference (dst->set, src->set); +} + +static void +bi_set_difference (progs_t *pr, void *_res) +{ + set_resources_t *res = _res; + rua_set_difference (pr, res, P_INT (pr, 0), P_INT (pr, 1)); R_INT (pr) = P_INT (pr, 0); } static void -bi_set_reverse_difference (progs_t *pr) +rua_set_reverse_difference (progs_t *pr, set_resources_t *res, + pr_int_t dstid, pr_int_t srcid) { - bi_set_t *set1 = get_set (pr, __FUNCTION__, P_INT (pr, 0)); - bi_set_t *set2 = get_set (pr, __FUNCTION__, P_INT (pr, 1)); + bi_set_t *dst = get_set (pr, res, __FUNCTION__, dstid); + bi_set_t *src = get_set (pr, res, __FUNCTION__, srcid); - set_reverse_difference (set1->set, set2->set); + set_reverse_difference (dst->set, src->set); +} + +static void +bi_set_reverse_difference (progs_t *pr, void *_res) +{ + set_resources_t *res = _res; + rua_set_reverse_difference (pr, res, P_INT (pr, 0), P_INT (pr, 1)); R_INT (pr) = P_INT (pr, 0); } static void -bi_set_assign (progs_t *pr) +rua_set_assign (progs_t *pr, set_resources_t *res, + pr_int_t dstid, pr_int_t srcid) { - bi_set_t *set1 = get_set (pr, __FUNCTION__, P_INT (pr, 0)); - bi_set_t *set2 = get_set (pr, __FUNCTION__, P_INT (pr, 1)); + bi_set_t *dst = get_set (pr, res, __FUNCTION__, dstid); + bi_set_t *src = get_set (pr, res, __FUNCTION__, srcid); - set_assign (set1->set, set2->set); + set_assign (dst->set, src->set); +} + +static void +bi_set_assign (progs_t *pr, void *_res) +{ + set_resources_t *res = _res; + rua_set_assign (pr, res, P_INT (pr, 0), P_INT (pr, 1)); R_INT (pr) = P_INT (pr, 0); } static void -bi_set_empty (progs_t *pr) +rua_set_empty (progs_t *pr, set_resources_t *res, pr_int_t setid) { - bi_set_t *set = get_set (pr, __FUNCTION__, P_INT (pr, 0)); + bi_set_t *set = get_set (pr, res, __FUNCTION__, setid); set_empty (set->set); +} + +static void +bi_set_empty (progs_t *pr, void *_res) +{ + set_resources_t *res = _res; + rua_set_empty (pr, res, P_INT (pr, 0)); R_INT (pr) = P_INT (pr, 0); } static void -bi_set_everything (progs_t *pr) +rua_set_everything (progs_t *pr, set_resources_t *res, pr_int_t setid) { - bi_set_t *set = get_set (pr, __FUNCTION__, P_INT (pr, 0)); + bi_set_t *set = get_set (pr, res, __FUNCTION__, setid); set_everything (set->set); +} + +static void +bi_set_everything (progs_t *pr, void *_res) +{ + set_resources_t *res = _res; + rua_set_everything (pr, res, P_INT (pr, 0)); R_INT (pr) = P_INT (pr, 0); } static void -bi_set_is_empty (progs_t *pr) +rua_set_is_empty (progs_t *pr, set_resources_t *res, pr_int_t setid) { - bi_set_t *set = get_set (pr, __FUNCTION__, P_INT (pr, 0)); + bi_set_t *set = get_set (pr, res, __FUNCTION__, setid); R_INT (pr) = set_is_empty (set->set); } static void -bi_set_is_everything (progs_t *pr) +bi_set_is_empty (progs_t *pr, void *_res) { - bi_set_t *set = get_set (pr, __FUNCTION__, P_INT (pr, 0)); + set_resources_t *res = _res; + rua_set_is_empty (pr, res, P_INT (pr, 0)); +} + +static void +rua_set_is_everything (progs_t *pr, set_resources_t *res, pr_int_t setid) +{ + bi_set_t *set = get_set (pr, res, __FUNCTION__, P_INT (pr, 0)); R_INT (pr) = set_is_everything (set->set); } static void -bi_set_is_disjoint (progs_t *pr) +bi_set_is_everything (progs_t *pr, void *_res) { - bi_set_t *set1 = get_set (pr, __FUNCTION__, P_INT (pr, 0)); - bi_set_t *set2 = get_set (pr, __FUNCTION__, P_INT (pr, 1)); + set_resources_t *res = _res; + rua_set_is_everything (pr, res, P_INT (pr, 0)); +} + +static void +rua_set_is_disjoint (progs_t *pr, set_resources_t *res, + pr_int_t sid1, pr_int_t sid2) +{ + bi_set_t *set1 = get_set (pr, res, __FUNCTION__, sid1); + bi_set_t *set2 = get_set (pr, res, __FUNCTION__, sid2); R_INT (pr) = set_is_disjoint (set1->set, set2->set); } static void -bi_set_is_intersecting (progs_t *pr) +bi_set_is_disjoint (progs_t *pr, void *_res) { - bi_set_t *set1 = get_set (pr, __FUNCTION__, P_INT (pr, 0)); - bi_set_t *set2 = get_set (pr, __FUNCTION__, P_INT (pr, 1)); + set_resources_t *res = _res; + rua_set_is_disjoint (pr, res, P_INT (pr, 0), P_INT (pr, 1)); +} + +static void +rua_set_is_intersecting (progs_t *pr, set_resources_t *res, + pr_int_t sid1, pr_int_t sid2) +{ + bi_set_t *set1 = get_set (pr, res, __FUNCTION__, sid1); + bi_set_t *set2 = get_set (pr, res, __FUNCTION__, sid2); R_INT (pr) = set_is_intersecting (set1->set, set2->set); } static void -bi_set_is_equivalent (progs_t *pr) +bi_set_is_intersecting (progs_t *pr, void *_res) { - bi_set_t *set1 = get_set (pr, __FUNCTION__, P_INT (pr, 0)); - bi_set_t *set2 = get_set (pr, __FUNCTION__, P_INT (pr, 1)); + set_resources_t *res = _res; + rua_set_is_intersecting (pr, res, P_INT (pr, 0), P_INT (pr, 1)); +} + +static void +rua_set_is_equivalent (progs_t *pr, set_resources_t *res, + pr_int_t sid1, pr_int_t sid2) +{ + bi_set_t *set1 = get_set (pr, res, __FUNCTION__, sid1); + bi_set_t *set2 = get_set (pr, res, __FUNCTION__, sid2); R_INT (pr) = set_is_equivalent (set1->set, set2->set); } static void -bi_set_is_subset (progs_t *pr) +bi_set_is_equivalent (progs_t *pr, void *_res) { - bi_set_t *set1 = get_set (pr, __FUNCTION__, P_INT (pr, 0)); - bi_set_t *set2 = get_set (pr, __FUNCTION__, P_INT (pr, 1)); - - R_INT (pr) = set_is_subset (set1->set, set2->set); + set_resources_t *res = _res; + rua_set_is_equivalent (pr, res, P_INT (pr, 0), P_INT (pr, 1)); } static void -bi_set_is_member (progs_t *pr) +rua_set_is_subset (progs_t *pr, set_resources_t *res, pr_int_t setid, + pr_int_t subid) { - bi_set_t *set = get_set (pr, __FUNCTION__, P_INT (pr, 0)); + bi_set_t *set = get_set (pr, res, __FUNCTION__, setid); + bi_set_t *sub = get_set (pr, res, __FUNCTION__, subid); - R_INT (pr) = set_is_member (set->set, P_UINT (pr, 1)); + R_INT (pr) = set_is_subset (set->set, sub->set); } static void -bi_set_size (progs_t *pr) +bi_set_is_subset (progs_t *pr, void *_res) { - bi_set_t *set = get_set (pr, __FUNCTION__, P_INT (pr, 0)); - - R_INT (pr) = set_size (set->set); + set_resources_t *res = _res; + rua_set_is_subset (pr, res, P_INT (pr, 0), P_INT (pr, 1)); } static void -bi_set_first (progs_t *pr) +rua_set_is_member (progs_t *pr, set_resources_t *res, pr_int_t setid, + pr_uint_t element) { - set_resources_t *res = PR_Resources_Find (pr, "Set"); - bi_set_t *set = get_set (pr, __FUNCTION__, P_INT (pr, 0)); + bi_set_t *set = get_set (pr, res, __FUNCTION__, setid); + + R_INT (pr) = set_is_member (set->set, element); +} + +static void +bi_set_is_member (progs_t *pr, void *_res) +{ + set_resources_t *res = _res; + rua_set_is_member (pr, res, P_INT (pr, 0), P_UINT (pr, 1)); +} + +static void +rua_set_count (progs_t *pr, set_resources_t *res, pr_int_t setid) +{ + bi_set_t *set = get_set (pr, res, __FUNCTION__, setid); + + R_INT (pr) = set_count (set->set); +} + +static void +bi_set_count (progs_t *pr, void *_res) +{ + set_resources_t *res = _res; + rua_set_count (pr, res, P_INT (pr, 0)); +} + +static void +bi_set_first (progs_t *pr, void *_res) +{ + set_resources_t *res = _res; + bi_set_t *set = get_set (pr, res, __FUNCTION__, P_INT (pr, 0)); set_iter_t *iter; bi_set_iter_t *set_iter; @@ -392,18 +541,20 @@ bi_set_first (progs_t *pr) res->set_iters->prev = &set_iter->next; res->set_iters = set_iter; - set_iter->iter = iter;; + set_iter->iter = iter; R_INT (pr) = res_set_iter_index (res, set_iter); } static void -bi_set_next (progs_t *pr) +bi_set_next (progs_t *pr, void *_res) { - bi_set_iter_t *set_iter = get_set_iter (pr, __FUNCTION__, P_INT (pr, 0)); + set_resources_t *res = _res; + bi_set_iter_t *set_iter = get_set_iter (pr, res, __FUNCTION__, + P_INT (pr, 0)); if (!set_next (set_iter->iter)) { - del_set_iter (pr, set_iter); + del_set_iter (pr, res, set_iter); R_INT (pr) = 0; return; } @@ -411,257 +562,234 @@ bi_set_next (progs_t *pr) } static void -bi_set_as_string (progs_t *pr) +rua_set_as_string (progs_t *pr, set_resources_t *res, pr_int_t setid) { - bi_set_t *set = get_set (pr, __FUNCTION__, P_INT (pr, 0)); + bi_set_t *set = get_set (pr, res, __FUNCTION__, setid); RETURN_STRING (pr, set_as_string (set->set)); } static void -bi_i_SetIterator__element (progs_t *pr) +bi_set_as_string (progs_t *pr, void *_res) { + set_resources_t *res = _res; + rua_set_as_string (pr, res, P_INT (pr, 0)); +} + +static void +bi__i_SetIterator__element (progs_t *pr, void *_res) +{ + set_resources_t *res = _res; pr_set_iter_t *iter_obj = &P_STRUCT (pr, pr_set_iter_t, 0); - bi_set_iter_t *set_iter = get_set_iter (pr, __FUNCTION__, iter_obj->iter); + bi_set_iter_t *set_iter = get_set_iter (pr, res, __FUNCTION__, + iter_obj->iter); R_INT (pr) = set_iter->iter->element; } static void -bi_i_Set__add_ (progs_t *pr) +bi__i_Set__add_ (progs_t *pr, void *_res) { - pointer_t set_ptr = P_POINTER (pr, 0); - pr_set_t *set_obj = &G_STRUCT (pr, pr_set_t, set_ptr); + set_resources_t *res = _res; + pr_ptr_t set_ptr = P_POINTER (pr, 0); + pr_set_t *set_obj = &G_STRUCT (pr, pr_set_t, set_ptr); - PR_RESET_PARAMS (pr); - P_INT (pr, 0) = set_obj->set; - P_INT (pr, 1) = P_INT (pr, 2); - bi_set_add (pr); + rua_set_add (pr, res, set_obj->set, P_UINT (pr, 2)); R_INT (pr) = set_ptr; } static void -bi_i_Set__remove_ (progs_t *pr) +bi__i_Set__remove_ (progs_t *pr, void *_res) { - pointer_t set_ptr = P_POINTER (pr, 0); - pr_set_t *set_obj = &G_STRUCT (pr, pr_set_t, set_ptr); + set_resources_t *res = _res; + pr_ptr_t set_ptr = P_POINTER (pr, 0); + pr_set_t *set_obj = &G_STRUCT (pr, pr_set_t, set_ptr); - PR_RESET_PARAMS (pr); - P_INT (pr, 0) = set_obj->set; - P_INT (pr, 1) = P_INT (pr, 2); - bi_set_remove (pr); + rua_set_remove (pr, res, set_obj->set, P_UINT (pr, 2)); R_INT (pr) = set_ptr; } static void -bi_i_Set__invert (progs_t *pr) +bi__i_Set__invert (progs_t *pr, void *_res) { - pointer_t set_ptr = P_POINTER (pr, 0); - pr_set_t *set_obj = &G_STRUCT (pr, pr_set_t, set_ptr); + set_resources_t *res = _res; + pr_ptr_t set_ptr = P_POINTER (pr, 0); + pr_set_t *set_obj = &G_STRUCT (pr, pr_set_t, set_ptr); - PR_RESET_PARAMS (pr); - P_INT (pr, 0) = set_obj->set; - bi_set_invert (pr); + rua_set_invert (pr, res, set_obj->set); R_INT (pr) = set_ptr; } static void -bi_i_Set__union_ (progs_t *pr) +bi__i_Set__union_ (progs_t *pr, void *_res) { - pointer_t dst_ptr = P_POINTER (pr, 0); - pr_set_t *dst_obj = &G_STRUCT (pr, pr_set_t, dst_ptr); - pr_set_t *src_obj = &P_STRUCT (pr, pr_set_t, 2); + set_resources_t *res = _res; + pr_ptr_t dst_ptr = P_POINTER (pr, 0); + pr_set_t *dst_obj = &G_STRUCT (pr, pr_set_t, dst_ptr); + pr_set_t *src_obj = &P_STRUCT (pr, pr_set_t, 2); - PR_RESET_PARAMS (pr); - P_INT (pr, 0) = dst_obj->set; - P_INT (pr, 1) = src_obj->set; - bi_set_union (pr); + rua_set_union (pr, res, dst_obj->set, src_obj->set); +} + +static void +bi__i_Set__intersection_ (progs_t *pr, void *_res) +{ + set_resources_t *res = _res; + pr_ptr_t dst_ptr = P_POINTER (pr, 0); + pr_set_t *dst_obj = &G_STRUCT (pr, pr_set_t, dst_ptr); + pr_set_t *src_obj = &P_STRUCT (pr, pr_set_t, 2); + + rua_set_intersection (pr, res, dst_obj->set, src_obj->set); R_INT (pr) = dst_ptr; } static void -bi_i_Set__intersection_ (progs_t *pr) +bi__i_Set__difference_ (progs_t *pr, void *_res) { - pointer_t dst_ptr = P_POINTER (pr, 0); - pr_set_t *dst_obj = &G_STRUCT (pr, pr_set_t, dst_ptr); - pr_set_t *src_obj = &P_STRUCT (pr, pr_set_t, 2); + set_resources_t *res = _res; + pr_ptr_t dst_ptr = P_POINTER (pr, 0); + pr_set_t *dst_obj = &G_STRUCT (pr, pr_set_t, dst_ptr); + pr_set_t *src_obj = &P_STRUCT (pr, pr_set_t, 2); - PR_RESET_PARAMS (pr); - P_INT (pr, 0) = dst_obj->set; - P_INT (pr, 1) = src_obj->set; - bi_set_intersection (pr); + rua_set_difference (pr, res, dst_obj->set, src_obj->set); R_INT (pr) = dst_ptr; } static void -bi_i_Set__difference_ (progs_t *pr) +bi__i_Set__reverse_difference_ (progs_t *pr, void *_res) { - pointer_t dst_ptr = P_POINTER (pr, 0); - pr_set_t *dst_obj = &G_STRUCT (pr, pr_set_t, dst_ptr); - pr_set_t *src_obj = &P_STRUCT (pr, pr_set_t, 2); + set_resources_t *res = _res; + pr_ptr_t dst_ptr = P_POINTER (pr, 0); + pr_set_t *dst_obj = &G_STRUCT (pr, pr_set_t, dst_ptr); + pr_set_t *src_obj = &P_STRUCT (pr, pr_set_t, 2); - PR_RESET_PARAMS (pr); - P_INT (pr, 0) = dst_obj->set; - P_INT (pr, 1) = src_obj->set; - bi_set_difference (pr); + rua_set_reverse_difference (pr, res, dst_obj->set, src_obj->set); R_INT (pr) = dst_ptr; } static void -bi_i_Set__reverse_difference_ (progs_t *pr) +bi__i_Set__assign_ (progs_t *pr, void *_res) { - pointer_t dst_ptr = P_POINTER (pr, 0); - pr_set_t *dst_obj = &G_STRUCT (pr, pr_set_t, dst_ptr); - pr_set_t *src_obj = &P_STRUCT (pr, pr_set_t, 2); + set_resources_t *res = _res; + pr_ptr_t dst_ptr = P_POINTER (pr, 0); + pr_set_t *dst_obj = &G_STRUCT (pr, pr_set_t, dst_ptr); + pr_set_t *src_obj = &P_STRUCT (pr, pr_set_t, 2); - PR_RESET_PARAMS (pr); - P_INT (pr, 0) = dst_obj->set; - P_INT (pr, 1) = src_obj->set; - bi_set_reverse_difference (pr); + rua_set_assign (pr, res, dst_obj->set, src_obj->set); R_INT (pr) = dst_ptr; } static void -bi_i_Set__assign_ (progs_t *pr) +bi__i_Set__empty (progs_t *pr, void *_res) { - pointer_t dst_ptr = P_POINTER (pr, 0); - pr_set_t *dst_obj = &G_STRUCT (pr, pr_set_t, dst_ptr); - pr_set_t *src_obj = &P_STRUCT (pr, pr_set_t, 2); + set_resources_t *res = _res; + pr_ptr_t set_ptr = P_POINTER (pr, 0); + pr_set_t *set_obj = &G_STRUCT (pr, pr_set_t, set_ptr); - PR_RESET_PARAMS (pr); - P_INT (pr, 0) = dst_obj->set; - P_INT (pr, 1) = src_obj->set; - bi_set_assign (pr); - R_INT (pr) = dst_ptr; -} - -static void -bi_i_Set__empty (progs_t *pr) -{ - pointer_t set_ptr = P_POINTER (pr, 0); - pr_set_t *set_obj = &G_STRUCT (pr, pr_set_t, set_ptr); - - PR_RESET_PARAMS (pr); - P_INT (pr, 0) = set_obj->set; - bi_set_empty (pr); + rua_set_empty (pr, res, set_obj->set); R_INT (pr) = set_ptr; } static void -bi_i_Set__everything (progs_t *pr) +bi__i_Set__everything (progs_t *pr, void *_res) { - pointer_t set_ptr = P_POINTER (pr, 0); - pr_set_t *set_obj = &G_STRUCT (pr, pr_set_t, set_ptr); + set_resources_t *res = _res; + pr_ptr_t set_ptr = P_POINTER (pr, 0); + pr_set_t *set_obj = &G_STRUCT (pr, pr_set_t, set_ptr); - PR_RESET_PARAMS (pr); - P_INT (pr, 0) = set_obj->set; - bi_set_everything (pr); + rua_set_everything (pr, res, set_obj->set); R_INT (pr) = set_ptr; } static void -bi_i_Set__is_empty (progs_t *pr) +bi__i_Set__is_empty (progs_t *pr, void *_res) { + set_resources_t *res = _res; pr_set_t *set_obj = &P_STRUCT (pr, pr_set_t, 0); - PR_RESET_PARAMS (pr); - P_INT (pr, 0) = set_obj->set; - bi_set_is_empty (pr); + rua_set_is_empty (pr, res, set_obj->set); } static void -bi_i_Set__is_everything (progs_t *pr) +bi__i_Set__is_everything (progs_t *pr, void *_res) { + set_resources_t *res = _res; pr_set_t *set_obj = &P_STRUCT (pr, pr_set_t, 0); - PR_RESET_PARAMS (pr); - P_INT (pr, 0) = set_obj->set; - bi_set_is_everything (pr); + rua_set_is_everything (pr, res, set_obj->set); } static void -bi_i_Set__is_disjoint_ (progs_t *pr) +bi__i_Set__is_disjoint_ (progs_t *pr, void *_res) { + set_resources_t *res = _res; pr_set_t *s1_obj = &P_STRUCT (pr, pr_set_t, 0); pr_set_t *s2_obj = &P_STRUCT (pr, pr_set_t, 2); - PR_RESET_PARAMS (pr); - P_INT (pr, 0) = s1_obj->set; - P_INT (pr, 1) = s2_obj->set; - bi_set_is_disjoint (pr); + rua_set_is_disjoint (pr, res, s1_obj->set, s2_obj->set); } static void -bi_i_Set__is_intersecting_ (progs_t *pr) +bi__i_Set__is_intersecting_ (progs_t *pr, void *_res) { + set_resources_t *res = _res; pr_set_t *s1_obj = &P_STRUCT (pr, pr_set_t, 0); pr_set_t *s2_obj = &P_STRUCT (pr, pr_set_t, 2); - PR_RESET_PARAMS (pr); - P_INT (pr, 0) = s1_obj->set; - P_INT (pr, 1) = s2_obj->set; - bi_set_is_intersecting (pr); + rua_set_is_intersecting (pr, res, s1_obj->set, s2_obj->set); } static void -bi_i_Set__is_equivalent_ (progs_t *pr) +bi__i_Set__is_equivalent_ (progs_t *pr, void *_res) { + set_resources_t *res = _res; pr_set_t *s1_obj = &P_STRUCT (pr, pr_set_t, 0); pr_set_t *s2_obj = &P_STRUCT (pr, pr_set_t, 2); - PR_RESET_PARAMS (pr); - P_INT (pr, 0) = s1_obj->set; - P_INT (pr, 1) = s2_obj->set; - bi_set_is_equivalent (pr); + rua_set_is_equivalent (pr, res, s1_obj->set, s2_obj->set); } static void -bi_i_Set__is_subset_ (progs_t *pr) +bi__i_Set__is_subset_ (progs_t *pr, void *_res) { - pr_set_t *s1_obj = &P_STRUCT (pr, pr_set_t, 0); - pr_set_t *s2_obj = &P_STRUCT (pr, pr_set_t, 2); + set_resources_t *res = _res; + pr_set_t *set_obj = &P_STRUCT (pr, pr_set_t, 0); + pr_set_t *sub_obj = &P_STRUCT (pr, pr_set_t, 2); - PR_RESET_PARAMS (pr); - P_INT (pr, 0) = s1_obj->set; - P_INT (pr, 1) = s2_obj->set; - bi_set_is_subset (pr); + rua_set_is_subset (pr, res, set_obj->set, sub_obj->set); } static void -bi_i_Set__is_member_ (progs_t *pr) +bi__i_Set__is_member_ (progs_t *pr, void *_res) { + set_resources_t *res = _res; pr_set_t *set_obj = &P_STRUCT (pr, pr_set_t, 0); - PR_RESET_PARAMS (pr); - P_INT (pr, 0) = set_obj->set; - P_UINT (pr, 1) = P_UINT (pr, 2); - bi_set_is_member (pr); + rua_set_is_member (pr, res, set_obj->set, P_UINT (pr, 2)); } static void -bi_i_Set__size (progs_t *pr) +bi__i_Set__size (progs_t *pr, void *_res) { + set_resources_t *res = _res; pr_set_t *set_obj = &P_STRUCT (pr, pr_set_t, 0); - PR_RESET_PARAMS (pr); - P_INT (pr, 0) = set_obj->set; - bi_set_size (pr); + rua_set_count (pr, res, set_obj->set); } static void -bi_i_Set__as_string (progs_t *pr) +bi__i_Set__as_string (progs_t *pr, void *_res) { + set_resources_t *res = _res; pr_set_t *set_obj = &P_STRUCT (pr, pr_set_t, 0); - PR_RESET_PARAMS (pr); - P_INT (pr, 0) = set_obj->set; - bi_set_as_string (pr); + rua_set_as_string (pr, res, set_obj->set); } static void -res_set_clear (progs_t *pr, void *data) +res_set_clear (progs_t *pr, void *_res) { - set_resources_t *res = (set_resources_t *) data; + set_resources_t *res = (set_resources_t *) _res; bi_set_t *set; bi_set_iter_t *set_iter; @@ -675,53 +803,62 @@ res_set_clear (progs_t *pr, void *data) res_set_iter_reset (res); } +static void +res_set_destroy (progs_t *pr, void *_res) +{ + free (_res); +} + +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) static builtin_t builtins[] = { - {"set_del_iter", bi_set_del_iter, -1}, - {"set_new", bi_set_new, -1}, - {"set_delete", bi_set_delete, -1}, - {"set_add", bi_set_add, -1}, - {"set_remove", bi_set_remove, -1}, - {"set_invert", bi_set_invert, -1}, - {"set_union", bi_set_union, -1}, - {"set_intersection", bi_set_intersection, -1}, - {"set_difference", bi_set_difference, -1}, - {"set_reverse_difference", bi_set_reverse_difference, -1}, - {"set_assign", bi_set_assign, -1}, - {"set_empty", bi_set_empty, -1}, - {"set_everything", bi_set_everything, -1}, - {"set_is_empty", bi_set_is_empty, -1}, - {"set_is_everything", bi_set_is_everything, -1}, - {"set_is_disjoint", bi_set_is_disjoint, -1}, - {"set_is_intersecting", bi_set_is_intersecting, -1}, - {"set_is_equivalent", bi_set_is_equivalent, -1}, - {"set_is_subset", bi_set_is_subset, -1}, - {"set_is_member", bi_set_is_member, -1}, - {"set_size", bi_set_size, -1}, - {"set_first", bi_set_first, -1}, - {"set_next", bi_set_next, -1}, - {"set_as_string", bi_set_as_string, -1}, + bi(set_del_iter, 1, p(ptr)), + bi(set_iter_element, 1, p(ptr)), + bi(set_new, 0), + bi(set_delete, 1, p(ptr)), + bi(set_add, 2, p(ptr), p(uint)), + bi(set_remove, 2, p(ptr), p(uint)), + bi(set_invert, 1, p(ptr)), + bi(set_union, 2, p(ptr), p(ptr)), + bi(set_intersection, 2, p(ptr), p(ptr)), + bi(set_difference, 2, p(ptr), p(ptr)), + bi(set_reverse_difference, 2, p(ptr), p(ptr)), + bi(set_assign, 2, p(ptr), p(ptr)), + bi(set_empty, 1, p(ptr)), + bi(set_everything, 1, p(ptr)), + bi(set_is_empty, 1, p(ptr)), + bi(set_is_everything, 1, p(ptr)), + bi(set_is_disjoint, 2, p(ptr), p(ptr)), + bi(set_is_intersecting, 2, p(ptr), p(ptr)), + bi(set_is_equivalent, 2, p(ptr), p(ptr)), + bi(set_is_subset, 2, p(ptr), p(ptr)), + bi(set_is_member, 2, p(ptr), p(uint)), + bi(set_count, 1, p(ptr)), + bi(set_first, 1, p(ptr)), + bi(set_next, 1, p(ptr)), + bi(set_as_string, 1, p(ptr)), - {"_i_SetIterator__element", bi_i_SetIterator__element, -1}, + bi(_i_SetIterator__element, 2, p(ptr), p(ptr)), - {"_i_Set__add_", bi_i_Set__add_, -1}, - {"_i_Set__remove_", bi_i_Set__remove_, -1}, - {"_i_Set__invert", bi_i_Set__invert, -1}, - {"_i_Set__union_", bi_i_Set__union_, -1}, - {"_i_Set__intersection_", bi_i_Set__intersection_, -1}, - {"_i_Set__difference_", bi_i_Set__difference_, -1}, - {"_i_Set__reverse_difference_", bi_i_Set__reverse_difference_, -1}, - {"_i_Set__assign_", bi_i_Set__assign_, -1}, - {"_i_Set__empty", bi_i_Set__empty, -1}, - {"_i_Set__everything", bi_i_Set__everything, -1}, - {"_i_Set__is_empty", bi_i_Set__is_empty, -1}, - {"_i_Set__is_everything", bi_i_Set__is_everything, -1}, - {"_i_Set__is_disjoint_", bi_i_Set__is_disjoint_, -1}, - {"_i_Set__is_intersecting_", bi_i_Set__is_intersecting_, -1}, - {"_i_Set__is_equivalent_", bi_i_Set__is_equivalent_, -1}, - {"_i_Set__is_subset_", bi_i_Set__is_subset_, -1}, - {"_i_Set__is_member_", bi_i_Set__is_member_, -1}, - {"_i_Set__size", bi_i_Set__size, -1}, - {"_i_Set__as_string", bi_i_Set__as_string, -1}, + bi(_i_Set__add_, 3, p(ptr), p(ptr), p(uint)), + bi(_i_Set__remove_, 3, p(ptr), p(ptr), p(uint)), + bi(_i_Set__invert, 2, p(ptr), p(ptr)), + bi(_i_Set__union_, 3, p(ptr), p(ptr), p(ptr)), + bi(_i_Set__intersection_, 3, p(ptr), p(ptr), p(ptr)), + bi(_i_Set__difference_, 3, p(ptr), p(ptr), p(ptr)), + bi(_i_Set__reverse_difference_, 3, p(ptr), p(ptr), p(ptr)), + bi(_i_Set__assign_, 3, p(ptr), p(ptr), p(ptr)), + bi(_i_Set__empty, 2, p(ptr), p(ptr)), + bi(_i_Set__everything, 2, p(ptr), p(ptr)), + bi(_i_Set__is_empty, 2, p(ptr), p(ptr)), + bi(_i_Set__is_everything, 2, p(ptr), p(ptr)), + bi(_i_Set__is_disjoint_, 3, p(ptr), p(ptr), p(ptr)), + bi(_i_Set__is_intersecting_, 3, p(ptr), p(ptr), p(ptr)), + bi(_i_Set__is_equivalent_, 3, p(ptr), p(ptr), p(ptr)), + bi(_i_Set__is_subset_, 3, p(ptr), p(ptr), p(ptr)), + bi(_i_Set__is_member_, 3, p(ptr), p(ptr), p(uint)), + bi(_i_Set__size, 2, p(ptr), p(ptr)), + bi(_i_Set__as_string, 2, p(ptr), p(ptr)), {0} }; @@ -732,6 +869,6 @@ RUA_Set_Init (progs_t *pr, int secure) set_resources_t *res = calloc (1, sizeof (set_resources_t)); res->sets = 0; - PR_Resources_Register (pr, "Set", res, res_set_clear); - PR_RegisterBuiltins (pr, builtins); + PR_Resources_Register (pr, "Set", res, res_set_clear, res_set_destroy); + PR_RegisterBuiltins (pr, builtins, res); } diff --git a/libs/ruamoko/rua_stdlib.c b/libs/ruamoko/rua_stdlib.c new file mode 100644 index 000000000..d74d6b403 --- /dev/null +++ b/libs/ruamoko/rua_stdlib.c @@ -0,0 +1,178 @@ +/* + bi_stdlib.c + + QuakeC stdlib api + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/5/31 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#define _GNU_SOURCE // for qsort_r + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include +#include + +#include "compat.h" +#include "qfalloca.h" + +#if defined(_WIN32) && defined(HAVE_MALLOC_H) +#include +#endif + +#include "QF/fbsearch.h" +#include "QF/progs.h" + +#include "rua_internal.h" + +typedef struct { + progs_t *pr; + pr_func_t func; +} function_t; + +static int +int_compare (const void *_a, const void *_b) +{ + const int *a = _a; + const int *b = _b; + return *a - *b; +} + +static int +rua_compare (const void *a, const void *b, void *_f) +{ + function_t *f = _f; + + PR_PushFrame (f->pr); + PR_RESET_PARAMS (f->pr); + P_POINTER (f->pr, 0) = PR_SetPointer (f->pr, a); + P_POINTER (f->pr, 1) = PR_SetPointer (f->pr, b); + f->pr->pr_argc = 2; + PR_ExecuteProgram (f->pr, f->func); + int cmp = R_INT (f->pr); + PR_PopFrame (f->pr); + return cmp; +} + +static void +bi_bsearch (progs_t *pr, void *data) +{ + const void *key = P_GPOINTER (pr, 0); + const void *array = P_GPOINTER (pr, 1); + size_t nmemb = P_INT (pr, 2); + size_t size = P_INT (pr, 3) * sizeof (pr_int_t); + pr_func_t cmp = P_FUNCTION (pr, 4); + void *p = 0; + + if (!cmp) { + p = bsearch (key, array, nmemb, size, int_compare); + } else { + function_t func = { pr, cmp }; + p = bsearch_r (key, array, nmemb, size, rua_compare, &func); + } + RETURN_POINTER (pr, p); +} + +static void +bi_fbsearch (progs_t *pr, void *data) +{ + const void *key = P_GPOINTER (pr, 0); + const void *array = P_GPOINTER (pr, 1); + size_t nmemb = P_INT (pr, 2); + size_t size = P_INT (pr, 3) * sizeof (pr_int_t); + pr_func_t cmp = P_FUNCTION (pr, 4); + void *p = 0; + + if (!cmp) { + p = fbsearch (key, array, nmemb, size, int_compare); + } else { + function_t func = { pr, cmp }; + p = fbsearch_r (key, array, nmemb, size, rua_compare, &func); + } + RETURN_POINTER (pr, p); +} + +static void +bi_qsort (progs_t *pr, void *data) +{ + void *array = P_GPOINTER (pr, 0); + size_t nmemb = P_INT (pr, 1); + size_t size = P_INT (pr, 2) * sizeof (pr_int_t); + pr_func_t cmp = P_FUNCTION (pr, 3); + + if (!cmp) { + qsort (array, nmemb, size, int_compare); + } else { + function_t func = { pr, cmp }; + qsort_r (array, nmemb, size, rua_compare, &func); + } +} + +static void +bi_prefixsumi (progs_t *pr, void *data) +{ + int *array = (int *) P_GPOINTER (pr, 0); + int count = P_INT (pr, 1); + + for (int i = 1; i < count; i++) { + array[i] += array[i - 1]; + } +} + +static void +bi_prefixsumf (progs_t *pr, void *data) +{ + float *array = (float *) P_GPOINTER (pr, 0); + int count = P_INT (pr, 1); + + for (int i = 1; i < count; i++) { + array[i] += array[i - 1]; + } +} + +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) +#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } +static builtin_t builtins[] = { + bi(bsearch, 4, p(ptr), p(ptr), p(int), p(int), p(func)), + bi(fbsearch, 4, p(ptr), p(ptr), p(int), p(int), p(func)), + bi(qsort, 3, p(ptr), p(int), p(int), p(func)), + {"prefixsum|^ii", bi_prefixsumi, -1, 2, {p(ptr), p(int)}}, + {"prefixsum|^fi", bi_prefixsumf, -1, 2, {p(ptr), p(int)}}, + {0} +}; + +void +RUA_Stdlib_Init (progs_t *pr, int secure) +{ + PR_RegisterBuiltins (pr, builtins, 0); +} diff --git a/libs/ruamoko/rua_string.c b/libs/ruamoko/rua_string.c index 7f233e362..6e625fdf2 100644 --- a/libs/ruamoko/rua_string.c +++ b/libs/ruamoko/rua_string.c @@ -38,6 +38,8 @@ # include #endif #include +#include +#include #include "qfalloca.h" @@ -51,19 +53,107 @@ #include "rua_internal.h" static void -bi_str_new (progs_t *pr) +bi_strlen (progs_t *pr, void *data) +{ + const char *s; + + s = P_GSTRING (pr, 0); + R_INT (pr) = strlen(s); +} + +void +RUA_Sprintf (progs_t *pr, dstring_t *dstr, const char *func, int fmt_arg) +{ + const char *fmt = P_GSTRING (pr, fmt_arg); + int count = pr->pr_argc - (fmt_arg + 1); + pr_type_t **args = pr->pr_params + (fmt_arg + 1); + + if (pr->progs->version == PROG_VERSION) { + __auto_type va_list = &P_PACKED (pr, pr_va_list_t, (fmt_arg + 1)); + count = va_list->count; + if (count) { + args = alloca (count * sizeof (pr_type_t *)); + for (int i = 0; i < count; i++) { + args[i] = &pr->pr_globals[va_list->list + i * 4]; + } + } else { + args = 0; + } + } + + PR_Sprintf (pr, dstr, func, fmt, count, args); +} + +static void +bi_sprintf (progs_t *pr, void *data) +{ + dstring_t *dstr; + + dstr = dstring_newstr (); + RUA_Sprintf (pr, dstr, "sprintf", 0); + RETURN_STRING (pr, dstr->str); + dstring_delete (dstr); +} + +static void +bi_vsprintf (progs_t *pr, void *data) +{ + const char *fmt = P_GSTRING (pr, 0); + __auto_type args = &P_PACKED (pr, pr_va_list_t, 1); + pr_type_t *list_start = PR_GetPointer (pr, args->list); + pr_type_t **list = alloca (args->count * sizeof (*list)); + dstring_t *dstr; + + for (int i = 0; i < args->count; i++) { + list[i] = list_start + i * pr->pr_param_size; + } + + dstr = dstring_newstr (); + PR_Sprintf (pr, dstr, "bi_vsprintf", fmt, args->count, list); + RETURN_STRING (pr, dstr->str); + dstring_delete (dstr); +} + +static void +bi_str_new (progs_t *pr, void *data) { R_STRING (pr) = PR_NewMutableString (pr); } static void -bi_str_free (progs_t *pr) +bi_str_unmutable (progs_t *pr, void *data) +{ + RETURN_STRING (pr, P_GSTRING (pr, 0)); +} + +static void +bi_str_free (progs_t *pr, void *data) { PR_FreeString (pr, P_STRING (pr, 0)); } static void -bi_str_copy (progs_t *pr) +bi_str_hold (progs_t *pr, void *data) +{ + pr_string_t str = P_STRING (pr, 0); + PR_HoldString (pr, str); + R_STRING (pr) = str; +} + +static void +bi_str_valid (progs_t *pr, void *data) +{ + R_INT (pr) = PR_StringValid (pr, P_STRING (pr, 0)); +} + +static void +bi_str_mutable (progs_t *pr, void *data) +{ + R_INT (pr) = PR_StringMutable (pr, P_STRING (pr, 0)); +} + +static void +bi_str_copy (progs_t *pr, void *data) { dstring_t *dst = P_DSTRING (pr, 0); const char *src = P_GSTRING (pr, 1); @@ -73,7 +163,7 @@ bi_str_copy (progs_t *pr) } static void -bi_str_cat (progs_t *pr) +bi_str_cat (progs_t *pr, void *data) { dstring_t *dst = P_DSTRING (pr, 0); const char *src = P_GSTRING (pr, 1); @@ -83,7 +173,7 @@ bi_str_cat (progs_t *pr) } static void -bi_str_clear (progs_t *pr) +bi_str_clear (progs_t *pr, void *data) { dstring_t *str = P_DSTRING (pr, 0); @@ -92,17 +182,10 @@ bi_str_clear (progs_t *pr) } static void -bi_str_mid (progs_t *pr) +str_mid (progs_t *pr, const char *str, int pos, int end, int size) { - const char *str = P_GSTRING (pr, 0); - int pos = P_INT (pr, 1); - int end = P_INT (pr, 2); - int size = strlen (str); char *temp; - if (pr->pr_argc == 2) - end = size; - R_STRING (pr) = 0; if (pos < 0) pos += size; @@ -112,6 +195,10 @@ bi_str_mid (progs_t *pr) end = size; if (pos < 0 || pos >= size || end <= pos) return; + if (end == size) { + R_STRING (pr) = str + pos - pr->pr_strings; + return; + } temp = alloca (end - pos + 1); strncpy (temp, str + pos, end - pos); temp[end - pos] = 0; @@ -119,31 +206,202 @@ bi_str_mid (progs_t *pr) } static void -bi_str_str (progs_t *pr) +bi_str_mid_2 (progs_t *pr, void *data) +{ + const char *str = P_GSTRING (pr, 0); + int pos = P_INT (pr, 1); + int size = strlen (str); + + str_mid (pr, str, pos, size, size); +} + +static void +bi_str_mid_3 (progs_t *pr, void *data) +{ + const char *str = P_GSTRING (pr, 0); + int pos = P_INT (pr, 1); + int end = P_INT (pr, 2); + int size = strlen (str); + + str_mid (pr, str, pos, end, size); +} + +static void +bi_str_str (progs_t *pr, void *data) { const char *haystack = P_GSTRING (pr, 0); const char *needle = P_GSTRING (pr, 1); char *res = strstr (haystack, needle); - R_STRING (pr) = 0; - if (res) - R_STRING (pr) = res - pr->pr_strings; + R_INT (pr) = -1; + if (res) { + R_INT (pr) = res - haystack; + } } +static void +bi_str_char (progs_t *pr, void *data) +{ + const char *str = P_GSTRING (pr, 0); + int ind = P_INT (pr, 1); + + if (ind < 0) { + PR_RunError (pr, "negative index to str_char"); + } + R_INT (pr) = str[ind]; +} + +static void +bi_str_quote (progs_t *pr, void *data) +{ + const char *str = P_GSTRING (pr, 0); + // can have up to 4 chars per char (a -> \x61) + char *quote = alloca (strlen (str) * 4 + 1); + char *q = quote; + char c; + int h; + + while ((c = *str++)) { + if (c >= ' ' && c < 127 && c != '\"') { + *q++ = c; + } else { + *q++ = '\\'; + switch (c) { + case '\a': c = 'a'; break; + case '\b': c = 'b'; break; + case '\f': c = 'f'; break; + case '\n': c = 'n'; break; + case '\r': c = 'r'; break; + case '\t': c = 't'; break; + case '\"': c = '\"'; break; + default: + *q++ = 'x'; + h = (c & 0xf0) >> 4; + *q++ = h > 9 ? h + 'a' - 10 : h + '0'; + h = (c & 0x0f); + c = h > 9 ? h + 'a' - 10 : h + '0'; + break; + } + *q++ = c; + } + } + *q++ = 0; + + RETURN_STRING (pr, quote); +} + +static void +bi_str_lower (progs_t *pr, void *data) +{ + const char *str = P_GSTRING (pr, 0); + char *lower = alloca (strlen (str) + 1); + char *l = lower; + byte c; + + while ((c = *str++)) { + *l++ = tolower (c); + } + *l++ = 0; + + RETURN_STRING (pr, lower); +} + +static void +bi_str_upper (progs_t *pr, void *data) +{ + const char *str = P_GSTRING (pr, 0); + char *upper = alloca (strlen (str) + 1); + char *l = upper; + byte c; + + while ((c = *str++)) { + *l++ = toupper (c); + } + *l++ = 0; + + RETURN_STRING (pr, upper); +} + +static void +bi_strtod (progs_t *pr, void *data) +{ + const char *str = P_GSTRING (pr, 0); + pr_type_t *end = P_GPOINTER (pr, 1); + char *end_ptr; + R_DOUBLE (pr) = strtod (str, &end_ptr); + if (end) { + end->value = end_ptr - str; + } +} + +static void +bi_strtof (progs_t *pr, void *data) +{ + const char *str = P_GSTRING (pr, 0); + pr_type_t *end = P_GPOINTER (pr, 1); + char *end_ptr; + R_FLOAT (pr) = strtof (str, &end_ptr); + if (end) { + end->value = end_ptr - str; + } +} + +static void +bi_strtol (progs_t *pr, void *data) +{ + const char *str = P_GSTRING (pr, 0); + pr_type_t *end = P_GPOINTER (pr, 1); + char *end_ptr; + R_LONG (pr) = strtol (str, &end_ptr, P_INT (pr, 2)); + if (end) { + end->value = end_ptr - str; + } +} + +static void +bi_strtoul (progs_t *pr, void *data) +{ + const char *str = P_GSTRING (pr, 0); + pr_type_t *end = P_GPOINTER (pr, 1); + char *end_ptr; + R_ULONG (pr) = strtoul (str, &end_ptr, P_INT (pr, 2)); + if (end) { + end->value = end_ptr - str; + } +} + +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) +#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } static builtin_t builtins[] = { - {"str_new", bi_str_new, -1}, - {"str_free", bi_str_free, -1}, - {"str_copy", bi_str_copy, -1}, - {"str_cat", bi_str_cat, -1}, - {"str_clear", bi_str_clear, -1}, - {"str_mid|*i", bi_str_mid, -1}, - {"str_mid|*ii", bi_str_mid, -1}, - {"str_str", bi_str_str, -1}, + bi(strlen, 1, p(string)), + bi(sprintf, -2, p(string)), + bi(vsprintf, 2, p(string), P(1, 2)), + bi(str_new, 0), + bi(str_unmutable, 1, p(string)), + bi(str_free, 1, p(string)), + bi(str_hold, 1, p(string)), + bi(str_valid, 1, p(string)), + bi(str_mutable, 1, p(string)), + bi(str_copy, 2, p(string), p(string)), + bi(str_cat, 2, p(string), p(string)), + bi(str_clear, 1, p(string)), + {"str_mid|*i", bi_str_mid_2, -1, 2, {p(string), p(int)}}, + {"str_mid|*ii", bi_str_mid_3, -1, 3, {p(string), p(int), p(int)}}, + bi(str_str, 2, p(string), p(string)), + bi(str_char, 2, p(string), p(int)), + bi(str_quote, 1, p(string)), + bi(str_lower, 1, p(string)), + bi(str_upper, 1, p(string)), + bi(strtod, 1, p(string)), + bi(strtof, 1, p(string)), + bi(strtol, 1, p(string)), + bi(strtoul, 1, p(string)), {0} }; void RUA_String_Init (progs_t *pr, int secure) { - PR_RegisterBuiltins (pr, builtins); + PR_RegisterBuiltins (pr, builtins, 0); } diff --git a/libs/scene/Makemodule.am b/libs/scene/Makemodule.am new file mode 100644 index 000000000..e614934c1 --- /dev/null +++ b/libs/scene/Makemodule.am @@ -0,0 +1,19 @@ +include libs/scene/test/Makemodule.am + +scene_deps=\ + libs/models/libQFmodels.la \ + libs/ecs/libQFecs.la \ + libs/util/libQFutil.la + +lib_LTLIBRARIES += libs/scene/libQFscene.la + +libs_scene_libQFscene_la_LDFLAGS= $(lib_ldflags) +libs_scene_libQFscene_la_LIBADD= $(scene_deps) +libs_scene_libQFscene_la_DEPENDENCIES= $(scene_deps) +libs_scene_libQFscene_la_SOURCES= \ + libs/scene/camera.c \ + libs/scene/efrag.c \ + libs/scene/entity.c \ + libs/scene/light.c \ + libs/scene/scene.c \ + libs/scene/transform.c diff --git a/libs/scene/camera.c b/libs/scene/camera.c new file mode 100644 index 000000000..3739305da --- /dev/null +++ b/libs/scene/camera.c @@ -0,0 +1,62 @@ +/* + camera.c + + Scene camera data + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/02/18 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/scene/camera.h" +#include "QF/scene/scene.h" +#include "QF/scene/transform.h" + +void +Camera_GetViewMatrix (const camera_t *camera, mat4f_t view) +{ + vec4f_t rotation = Transform_GetWorldRotation (camera->transform); + vec4f_t position = Transform_GetWorldPosition (camera->transform); + mat4fquat (view, qconjf (rotation)); + // qconjf negates xyz but leaves w alone, which is what we want for + // inverting the translation (essentially, rotation+position form a + // dual quaternion, but without the complication of dealing with + // half-translations) + view[3] = mvmulf (view, qconjf (position)); +} + +void Camera_GetProjectionMatrix (const camera_t *camera, mat4f_t proj) +{ + float v = camera->field_of_view; + float a = camera->aspect; + float f = camera->far_clip; + float n = camera->near_clip; + + proj[0] = (vec4f_t) { v / a, 0, 0, 0 }; + proj[1] = (vec4f_t) { 0, v, 0, 0 }; + proj[2] = (vec4f_t) { 0, 0, f / (f - n), 1 }; + proj[2] = (vec4f_t) { 0, 0, n * f / (n - f), 0 }; +} diff --git a/libs/video/renderer/r_efrag.c b/libs/scene/efrag.c similarity index 55% rename from libs/video/renderer/r_efrag.c rename to libs/scene/efrag.c index 74592fa30..45e2d8eb0 100644 --- a/libs/video/renderer/r_efrag.c +++ b/libs/scene/efrag.c @@ -33,10 +33,10 @@ #include "QF/render.h" #include "QF/sys.h" -#include "r_internal.h" +#include "QF/scene/entity.h" -static mnode_t *r_pefragtopnode; -static vec3_t r_emins, r_emaxs; +#include "qfalloca.h" +#include "r_internal.h" typedef struct s_efrag_list { struct s_efrag_list *next; @@ -46,12 +46,10 @@ typedef struct s_efrag_list { static efrag_t *r_free_efrags; static t_efrag_list *efrag_list; +entqueue_t *r_ent_queue; + /* ENTITY FRAGMENT FUNCTIONS */ -static efrag_t **lastlink; -static entity_t *r_addent; - - static inline void init_efrag_list (t_efrag_list *efl) { @@ -89,7 +87,7 @@ R_ClearEfrags (void) if (!efrag_list) efrag_list = calloc (1, sizeof (t_efrag_list)); - r_free_efrags = efrag_list->efrags;; + r_free_efrags = efrag_list->efrags; for (efl = efrag_list; efl; efl = efl->next) { init_efrag_list (efl); if (efl->next) @@ -97,29 +95,30 @@ R_ClearEfrags (void) } } -/* - R_RemoveEfrags - - Call when removing an object from the world or moving it to another position -*/ void -R_RemoveEfrags (entity_t *ent) +R_ShutdownEfrags (void) { - efrag_t *ef, *old, *walk, **prev; + while (efrag_list) { + t_efrag_list *efl = efrag_list->next; + free (efrag_list); + efrag_list = efl; + } +} - ef = ent->efrag; +void +R_ClearEfragChain (efrag_t *ef) +{ + efrag_t *old, *walk, **prev; while (ef) { prev = &ef->leaf->efrags; - while (1) { - walk = *prev; - if (!walk) - break; + while ((walk = *prev)) { if (walk == ef) { // remove this fragment *prev = ef->leafnext; break; - } else + } else { prev = &walk->leafnext; + } } old = ef; @@ -129,36 +128,42 @@ R_RemoveEfrags (entity_t *ent) old->entnext = r_free_efrags; r_free_efrags = old; } - - ent->efrag = 0; } -#define NODE_STACK_SIZE 1024 -static mnode_t *node_stack[NODE_STACK_SIZE]; -static mnode_t **node_ptr = node_stack + NODE_STACK_SIZE; - static void -R_SplitEntityOnNode (mnode_t *node) +R_SplitEntityOnNode (mod_brush_t *brush, entity_t ent, uint32_t queue, + visibility_t *visibility, vec3_t emins, vec3_t emaxs) { efrag_t *ef; plane_t *splitplane; mleaf_t *leaf; int sides; + efrag_t **lastlink; + int *node_stack; + int32_t *node_ptr; - *--node_ptr = 0; + node_stack = alloca ((brush->depth + 2) * sizeof (int32_t *)); + node_ptr = node_stack; - while (node) { + lastlink = &visibility->efrag; + + *node_ptr++ = brush->numnodes; + + int32_t node_id = 0; + while (node_id != (int) brush->numnodes) { // add an efrag if the node is a leaf - if (__builtin_expect (node->contents < 0, 0)) { - if (!r_pefragtopnode) - r_pefragtopnode = node; + if (__builtin_expect (node_id < 0, 0)) { + if (visibility->topnode_id == -1) { + visibility->topnode_id = node_id; + } - leaf = (mleaf_t *) node; + leaf = brush->leafs + ~node_id; ef = new_efrag (); // ensures ef->entnext is 0 // add the link to the chain of links on the entity - ef->entity = r_addent; + ef->entity = ent; + ef->queue_num = queue; *lastlink = ef; lastlink = &ef->entnext; @@ -167,85 +172,72 @@ R_SplitEntityOnNode (mnode_t *node) ef->leafnext = leaf->efrags; leaf->efrags = ef; - node = *node_ptr++; + node_id = *--node_ptr; } else { + mnode_t *node = brush->nodes + node_id; // NODE_MIXED - splitplane = node->plane; - sides = BOX_ON_PLANE_SIDE (r_emins, r_emaxs, splitplane); + splitplane = (plane_t *) &node->plane; + sides = BOX_ON_PLANE_SIDE (emins, emaxs, splitplane); if (sides == 3) { // split on this plane // if this is the first splitter of this bmodel, remember it - if (!r_pefragtopnode) - r_pefragtopnode = node; + if (visibility->topnode_id == -1) { + visibility->topnode_id = node_id; + } } // recurse down the contacted sides - if (sides & 1 && node->children[0]->contents != CONTENTS_SOLID) { - if (sides & 2 && node->children[1]->contents != CONTENTS_SOLID) - *--node_ptr = node->children[1]; - node = node->children[0]; + if (sides & 1) { + if (sides & 2) + *node_ptr++ = node->children[1]; + node_id = node->children[0]; } else { - if (sides & 2 && node->children[1]->contents != CONTENTS_SOLID) - node = node->children[1]; + if (sides & 2) + node_id = node->children[1]; else - node = *node_ptr++; + node_id = *--node_ptr; } } } } void -R_AddEfrags (entity_t *ent) +R_AddEfrags (mod_brush_t *brush, entity_t ent) { model_t *entmodel; + vec3_t emins, emaxs; + transform_t transform = Entity_Transform (ent); + renderer_t *rend = Ent_GetComponent (ent.id, scene_renderer, ent.reg); - if (!ent->model) + if (!rend->model) { + Ent_RemoveComponent (ent.id, scene_visibility, ent.reg); return; + } + visibility_t *vis; + if (Ent_HasComponent (ent.id, scene_visibility, ent.reg)) { + vis = Ent_GetComponent (ent.id, scene_visibility, ent.reg); + R_ClearEfragChain (vis->efrag); + } else { + vis = Ent_AddComponent (ent.id, scene_visibility, ent.reg); + } + vis->efrag = 0; - if (ent == &r_worldentity) - return; // never add the world + entmodel = rend->model; - r_addent = ent; + vec4f_t org = Transform_GetWorldPosition (transform); + VectorAdd (org, entmodel->mins, emins); + VectorAdd (org, entmodel->maxs, emaxs); - lastlink = &ent->efrag; - r_pefragtopnode = 0; - - entmodel = ent->model; - - VectorAdd (ent->origin, entmodel->mins, r_emins); - VectorAdd (ent->origin, entmodel->maxs, r_emaxs); - - R_SplitEntityOnNode (r_worldentity.model->nodes); - - ent->topnode = r_pefragtopnode; + vis->topnode_id = -1; // leaf 0 (solid space) + R_SplitEntityOnNode (brush, ent, rend->model->type, vis, emins, emaxs); } void -R_StoreEfrags (const efrag_t *pefrag) +R_StoreEfrags (const efrag_t *efrag) { - entity_t *pent; - model_t *model; - - while (pefrag) { - pent = pefrag->entity; - model = pent->model; - - switch (model->type) { - case mod_alias: - case mod_brush: - case mod_sprite: - case mod_iqm: - if (pent->visframe != r_framecount) { - R_EnqueueEntity (pent); - // mark that we've recorded this entity for this frame - pent->visframe = r_framecount; - } - pefrag = pefrag->leafnext; - break; - - default: - Sys_Error ("R_StoreEfrags: Bad entity type %d", - model->type); - } + while (efrag) { + entity_t ent = efrag->entity; + EntQueue_AddEntity (r_ent_queue, ent, efrag->queue_num); + efrag = efrag->leafnext; } } diff --git a/libs/scene/entity.c b/libs/scene/entity.c new file mode 100644 index 000000000..e984865f6 --- /dev/null +++ b/libs/scene/entity.c @@ -0,0 +1,72 @@ +/* + entity.c + + Entity management + + Copyright (C) 2022 Bill Currke + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/set.h" + +#define IMPLEMENT_ENTITY_Funcs + +#include "QF/scene/entity.h" +#include "QF/scene/scene.h" + +entqueue_t * +EntQueue_New (int num_queues) +{ + int size = sizeof (entqueue_t) + num_queues * sizeof (entityset_t); + entqueue_t *queue = calloc (1, size); + queue->queued_ents = set_new (); + queue->ent_queues = (entityset_t *) (queue + 1); + queue->num_queues = num_queues; + for (int i = 0; i < num_queues; i++) { + queue->ent_queues[i].grow = 64; + } + return queue; +} + +void +EntQueue_Delete (entqueue_t *queue) +{ + if (!queue) { + return; + } + for (int i = 0; i < queue->num_queues; i++) { + DARRAY_CLEAR (&queue->ent_queues[i]); + } + set_delete (queue->queued_ents); + free (queue); +} + +void +EntQueue_Clear (entqueue_t *queue) +{ + for (int i = 0; i < queue->num_queues; i++) { + queue->ent_queues[i].size = 0; + } + set_empty (queue->queued_ents); +} diff --git a/libs/scene/light.c b/libs/scene/light.c new file mode 100644 index 000000000..dfbd026c9 --- /dev/null +++ b/libs/scene/light.c @@ -0,0 +1,162 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "QF/model.h" +#include "QF/set.h" +#include "QF/scene/light.h" +#include "QF/scene/scene.h" +#include "QF/simd/vec4f.h" + +static void +expand_pvs (set_t *pvs, model_t *model) +{ + set_t base_pvs = SET_STATIC_INIT (model->brush.visleafs, alloca); + set_assign (&base_pvs, pvs); + for (unsigned i = 0; i < model->brush.visleafs; i++) { + if (set_is_member (&base_pvs, i)) { + Mod_LeafPVS_mix (model->brush.leafs + i + 1, model, 0, pvs); + } + } +} + +lightingdata_t * +Light_CreateLightingData (scene_t *scene) +{ + lightingdata_t *ldata = calloc (1, sizeof (lightingdata_t)); + + DARRAY_INIT (&ldata->lights, 16); + DARRAY_INIT (&ldata->lightstyles, 16); + DARRAY_INIT (&ldata->lightleafs, 16); + DARRAY_INIT (&ldata->lightvis, 16); + + ldata->scene = scene; + + return ldata; +} + +void +Light_DestroyLightingData (lightingdata_t *ldata) +{ + DARRAY_CLEAR (&ldata->lights); + DARRAY_CLEAR (&ldata->lightstyles); + DARRAY_CLEAR (&ldata->lightleafs); + DARRAY_CLEAR (&ldata->lightvis); + + free (ldata); +} + +void +Light_ClearLights (lightingdata_t *ldata) +{ + ldata->lights.size = 0; + ldata->lightstyles.size = 0; + ldata->lightleafs.size = 0; + ldata->lightvis.size = 0; + if (ldata->sun_pvs) { + set_delete (ldata->sun_pvs); + } + ldata->sun_pvs = 0; + if (ldata->pvs) { + set_delete (ldata->pvs); + } + ldata->pvs = 0; + ldata->leaf = 0; +} + +void +Light_AddLight (lightingdata_t *ldata, const light_t *light, int style) +{ + scene_t *scene = ldata->scene; + model_t *model = scene->worldmodel; + + DARRAY_APPEND (&ldata->lights, *light); + DARRAY_APPEND (&ldata->lightstyles, style); + + int visleaf = -1; // directional light + if (light->position[3]) { + // positional light + mleaf_t *leaf = Mod_PointInLeaf (light->position, model); + visleaf = leaf - model->brush.leafs - 1; + } else if (!DotProduct (light->direction, light->direction)) { + // ambient light + visleaf = -2; + } + DARRAY_APPEND (&ldata->lightleafs, visleaf); + DARRAY_APPEND (&ldata->lightvis, 0); +} + +void +Light_EnableSun (lightingdata_t *ldata) +{ + scene_t *scene = ldata->scene; + model_t *model = scene->worldmodel; + + if (!ldata->sun_pvs) { + ldata->sun_pvs = set_new_size (model->brush.visleafs); + } + set_expand (ldata->sun_pvs, model->brush.visleafs); + set_empty (ldata->sun_pvs); + // Any leaf with sky surfaces can potentially see the sun, thus put + // the sun "in" every leaf with a sky surface + // however, skip leaf 0 as it is the exterior solid leaf + for (unsigned l = 1; l < model->brush.modleafs; l++) { + if (model->brush.leaf_flags[l] & SURF_DRAWSKY) { + set_add (ldata->sun_pvs, l - 1); //pvs is 1-based + } + } + // any leaf visible from a leaf with a sky surface (and thus the sun) + // can receive shadows from the sun + expand_pvs (ldata->sun_pvs, model); +} + +void +Light_FindVisibleLights (lightingdata_t *ldata) +{ + scene_t *scene = ldata->scene; + mleaf_t *leaf = scene->viewleaf; + model_t *model = scene->worldmodel; + + if (!leaf) { + return; + } + if (!ldata->pvs) { + ldata->pvs = set_new_size (model->brush.visleafs); + } + + if (leaf != ldata->leaf) { + //double start = Sys_DoubleTime (); + int flags = 0; + + if (leaf == model->brush.leafs) { + set_everything (ldata->pvs); + flags = SURF_DRAWSKY; + } else { + Mod_LeafPVS_set (leaf, model, 0, ldata->pvs); + if (set_is_intersecting (ldata->pvs, ldata->sun_pvs)) { + flags |= SURF_DRAWSKY; + } + expand_pvs (ldata->pvs, model); + } + ldata->leaf = leaf; + + //double end = Sys_DoubleTime (); + //Sys_Printf ("find_visible_lights: %.5gus\n", (end - start) * 1e6); + + int visible = 0; + memset (ldata->lightvis.a, 0, ldata->lightvis.size * sizeof (byte)); + for (size_t i = 0; i < ldata->lightleafs.size; i++) { + int l = ldata->lightleafs.a[i]; + if ((l == -2) || (l == -1 && (flags & SURF_DRAWSKY)) + || set_is_member (ldata->pvs, l)) { + ldata->lightvis.a[i] = 1; + visible++; + } + } + Sys_MaskPrintf (SYS_lighting, + "find_visible_lights: %d / %zd visible\n", visible, + ldata->lightvis.size); + } +} diff --git a/libs/scene/scene.c b/libs/scene/scene.c new file mode 100644 index 000000000..d7cfa519f --- /dev/null +++ b/libs/scene/scene.c @@ -0,0 +1,258 @@ +/* + scene.c + + General scene handling + + Copyright (C) 2021 Bill Currke + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/mathlib.h" +#include "QF/sys.h" +#include "QF/model.h" + +#include "QF/scene/entity.h" +#include "QF/scene/scene.h" +#include "QF/scene/transform.h" + +static void +create_active (void *_active) +{ + byte *active = _active; + *active = 1; +} + +static void +create_old_origin (void *_old_origin) +{ + vec4f_t *old_origin = _old_origin; + *old_origin = (vec4f_t) {0, 0, 0, 1}; +} + +static void +create_colormap (void *_colormap) +{ + colormap_t *colormap = _colormap; + *colormap = (colormap_t) {1, 6}; +} + +static void +destroy_visibility (void *_visibility) +{ + visibility_t *visibility = _visibility; + if (visibility->efrag) { + R_ClearEfragChain (visibility->efrag); + } +} + +static void +sw_identity_matrix (void *_mat) +{ + mat4f_t *mat = _mat; + mat4fidentity (*mat); +} + +static void +sw_frame_0 (void *_frame) +{ + byte *frame = _frame; + *frame = 0; +} + +static void +sw_null_brush (void *_brush) +{ + struct mod_brush_s **brush = _brush; + *brush = 0; +} + +static const component_t scene_components[scene_comp_count] = { + [scene_href] = { + .size = sizeof (hierref_t), + .create = 0,//create_href, + .name = "href", + .destroy = Hierref_DestroyComponent, + }, + [scene_animation] = { + .size = sizeof (animation_t), + .create = 0,//create_animation, + .name = "animation", + }, + [scene_visibility] = { + .size = sizeof (visibility_t), + .create = 0,//create_visibility, + .destroy = destroy_visibility, + .name = "visibility", + }, + [scene_renderer] = { + .size = sizeof (renderer_t), + .create = 0,//create_renderer, + .name = "renderer", + }, + [scene_active] = { + .size = sizeof (byte), + .create = create_active, + .name = "active", + }, + [scene_old_origin] = { + .size = sizeof (vec4f_t), + .create = create_old_origin, + .name = "old_origin", + }, + [scene_colormap] = { + .size = sizeof (colormap_t), + .create = create_colormap, + .name = "colormap", + }, + + [scene_sw_matrix] = { + .size = sizeof (mat4f_t), + .create = sw_identity_matrix, + .name = "sw world transform", + }, + [scene_sw_frame] = { + .size = sizeof (byte), + .create = sw_frame_0, + .name = "sw brush model animation frame", + }, + [scene_sw_brush] = { + .size = sizeof (struct mod_brush_s *), + .create = sw_null_brush, + .name = "sw brush model data pointer", + }, +}; + +static byte empty_visdata[] = { 0x01 }; + +static mleaf_t empty_leafs[] = { + [1] = { + .contents = CONTENTS_EMPTY, + .mins = {-INFINITY, -INFINITY, -INFINITY}, + .maxs = { INFINITY, INFINITY, INFINITY}, + .compressed_vis = empty_visdata, + }, +}; + +static mnode_t empty_nodes[] = { + [0] = { + .plane = { 0, 0, 0, -1 }, + .type = 3, + .children = { ~0, ~1 }, + .minmaxs = {-INFINITY, -INFINITY, -INFINITY, + INFINITY, INFINITY, INFINITY}, + }, +}; + +static int empty_node_parents[] = { + [0] = -1, +}; + +static int empty_leaf_parents[] = { + [0] = 0, + [1] = 0, +}; + +static int empty_leaf_flags[] = { + [1] = SURF_DRAWSKY, +}; + +static char empty_entities[] = { 0 }; + +static model_t empty_world = { + .type = mod_brush, + .radius = INFINITY, + .mins = {-INFINITY, -INFINITY, -INFINITY}, + .maxs = { INFINITY, INFINITY, INFINITY}, + .brush = { + .modleafs = 2, + .visleafs = 1, + .numnodes = 1, + .nodes = empty_nodes, + .leafs = empty_leafs, + .entities = empty_entities, + .visdata = empty_visdata, + .node_parents = empty_node_parents, + .leaf_parents = empty_leaf_parents, + .leaf_flags = empty_leaf_flags, + }, +}; + +scene_t * +Scene_NewScene (void) +{ + scene_t *scene = calloc (1, sizeof (scene_t)); + + scene->reg = ECS_NewRegistry (); + ECS_RegisterComponents (scene->reg, scene_components, scene_comp_count); + ECS_CreateComponentPools (scene->reg); + + scene->worldmodel = &empty_world; + + return scene; +} + +void +Scene_DeleteScene (scene_t *scene) +{ + ECS_DelRegistry (scene->reg); + + free (scene); +} + +entity_t +Scene_CreateEntity (scene_t *scene) +{ + // Transform_New creates an entity and adds a scene_href component to the + // entity + transform_t trans = Transform_New (scene->reg, nulltransform); + uint32_t id = trans.id; + + Ent_SetComponent (id, scene_animation, scene->reg, 0); + Ent_SetComponent (id, scene_renderer, scene->reg, 0); + Ent_SetComponent (id, scene_active, scene->reg, 0); + Ent_SetComponent (id, scene_old_origin, scene->reg, 0); + + renderer_t *renderer = Ent_GetComponent (id, scene_renderer, scene->reg); + QuatSet (1, 1, 1, 1, renderer->colormod); + + return (entity_t) { .reg = scene->reg, .id = id }; +} + +void +Scene_DestroyEntity (scene_t *scene, entity_t ent) +{ + ECS_DelEntity (scene->reg, ent.id); +} + +void +Scene_FreeAllEntities (scene_t *scene) +{ +} diff --git a/libs/scene/test/Makemodule.am b/libs/scene/test/Makemodule.am new file mode 100644 index 000000000..d1074ed13 --- /dev/null +++ b/libs/scene/test/Makemodule.am @@ -0,0 +1,18 @@ +libs_scene_tests = \ + libs/scene/test/test-transform + +TESTS += $(libs_scene_tests) + +check_PROGRAMS += $(libs_scene_tests) + +libs_scene_test_libs= \ + libs/scene/libQFscene.la \ + libs/ecs/libQFecs.la \ + libs/util/libQFutil.la + +libs_scene_test_test_transform_SOURCES= \ + libs/scene/test/test-transform.c +libs_scene_test_test_transform_LDADD= \ + $(libs_scene_test_libs) +libs_scene_test_test_transform_DEPENDENCIES= \ + $(libs_scene_test_libs) diff --git a/libs/scene/test/test-transform.c b/libs/scene/test/test-transform.c new file mode 100644 index 000000000..6311bb4a8 --- /dev/null +++ b/libs/scene/test/test-transform.c @@ -0,0 +1,496 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "QF/ecs/component.h" +#include "QF/ecs/hierarchy.h" +#include "QF/scene/scene.h" +#include "QF/scene/transform.h" + +ecs_registry_t *reg; + +// NOTE: these are the columns of the matrix! (not that it matters for a +// symmetrical matrix, but...) +mat4f_t identity = { + { 1, 0, 0, 0 }, + { 0, 1, 0, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 0, 1 }, +}; +vec4f_t one = { 1, 1, 1, 1 }; + +static int +vec4_equal (vec4f_t a, vec4f_t b) +{ + vec4i_t res = a != b; + return !(res[0] || res[1] || res[2] || res[3]); +} + +static int +mat4_equal (const mat4f_t a, const mat4f_t b) +{ + vec4i_t res = {}; + + for (int i = 0; i < 4; i++) { + res |= a[i] != b[i]; + } + return !(res[0] || res[1] || res[2] || res[3]); +} + +static int +check_hierarchy_size (transform_t t, uint32_t size) +{ + hierarchy_t *h = Transform_GetRef (t)->hierarchy; + if (h->num_objects != size) { + printf ("hierarchy does not have exactly %u transform\n", size); + return 0; + } + char **name = h->components[transform_type_name]; + ecs_registry_t *reg = h->reg; + for (uint32_t i = 0; i < h->num_objects; i++) { + hierref_t *ref = Ent_GetComponent (h->ent[i], scene_href, reg); + if (ref->hierarchy != h) { + printf ("transform %d (%s) does not point to hierarchy\n", + i, name[i]); + } + } + return 1; +} + +static int +check_indices (transform_t transform, uint32_t index, uint32_t parentIndex, + uint32_t childIndex, uint32_t childCount) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + char **name = h->components[transform_type_name]; + if (ref->index != index) { + printf ("%s/%s index incorrect: expect %u got %u\n", + name[ref->index], name[index], + index, ref->index); + return 0; + } + if (h->parentIndex[index] != parentIndex) { + printf ("%s parent index incorrect: expect %u got %u\n", + name[index], parentIndex, h->parentIndex[index]); + return 0; + } + if (h->childIndex[index] != childIndex) { + printf ("%s child index incorrect: expect %u got %u\n", + name[index], childIndex, h->childIndex[index]); + return 0; + } + if (h->childCount[index] != childCount) { + printf ("%s child count incorrect: expect %u got %u\n", + name[index], childCount, h->childCount[index]); + return 0; + } + return 1; +} + +static int +test_single_transform (void) +{ + transform_t transform = Transform_New (reg, (transform_t) {}); + hierarchy_t *h; + + if (!transform.reg || transform.id == nullent) { + printf ("Transform_New returned null\n"); + return 1; + } + if (!Transform_GetRef (transform)) { + printf ("Transform_GetRef returned null\n"); + return 1; + } + if (!(h = Transform_GetRef (transform)->hierarchy)) { + printf ("New transform has no hierarchy\n"); + return 1; + } + mat4f_t *localMatrix = h->components[transform_type_localMatrix]; + mat4f_t *localInverse = h->components[transform_type_localInverse]; + mat4f_t *worldMatrix = h->components[transform_type_worldMatrix]; + mat4f_t *worldInverse = h->components[transform_type_worldInverse]; + vec4f_t *localRotation = h->components[transform_type_localRotation]; + vec4f_t *localScale = h->components[transform_type_localScale]; + if (!check_hierarchy_size (transform, 1)) { return 1; } + if (!check_indices (transform, 0, nullent, 1, 0)) { return 1; } + + if (!mat4_equal (localMatrix[0], identity) + || !mat4_equal (localInverse[0], identity) + || !mat4_equal (worldMatrix[0], identity) + || !mat4_equal (worldInverse[0], identity)) { + printf ("New transform matrices not identity\n"); + return 1; + } + + if (!vec4_equal (localRotation[0], identity[3]) + || !vec4_equal (localScale[0], one)) { + printf ("New transform rotation or scale not identity\n"); + return 1; + } + + // Delete the hierarchy directly as setparent isn't fully tested + Hierarchy_Delete (h); + + return 0; +} + +static int +test_parent_child_init (void) +{ + transform_t parent = Transform_New (reg, (transform_t) {}); + transform_t child = Transform_New (reg, parent); + + if (Transform_GetRef (parent)->hierarchy + != Transform_GetRef (child)->hierarchy) { + printf ("parent and child transforms have separate hierarchies\n"); + return 1; + } + + if (!check_hierarchy_size (parent, 2)) { return 1; } + + if (!check_indices (parent, 0, nullent, 1, 1)) { return 1; } + if (!check_indices (child, 1, 0, 2, 0)) { return 1; } + + hierarchy_t *h = Transform_GetRef (parent)->hierarchy; + mat4f_t *localMatrix = h->components[transform_type_localMatrix]; + mat4f_t *localInverse = h->components[transform_type_localInverse]; + mat4f_t *worldMatrix = h->components[transform_type_worldMatrix]; + mat4f_t *worldInverse = h->components[transform_type_worldInverse]; + vec4f_t *localRotation = h->components[transform_type_localRotation]; + vec4f_t *localScale = h->components[transform_type_localScale]; + if (!mat4_equal (localMatrix[0], identity) + || !mat4_equal (localInverse[0], identity) + || !mat4_equal (worldMatrix[0], identity) + || !mat4_equal (worldInverse[0], identity)) { + printf ("Parent transform matrices not identity\n"); + return 1; + } + + if (!vec4_equal (localRotation[0], identity[3]) + || !vec4_equal (localScale[0], one)) { + printf ("Parent transform rotation or scale not identity\n"); + return 1; + } + + if (!mat4_equal (localMatrix[1], identity) + || !mat4_equal (localInverse[1], identity) + || !mat4_equal (worldMatrix[1], identity) + || !mat4_equal (worldInverse[1], identity)) { + printf ("Child transform matrices not identity\n"); + return 1; + } + + if (!vec4_equal (localRotation[1], identity[3]) + || !vec4_equal (localScale[1], one)) { + printf ("Child transform rotation or scale not identity\n"); + return 1; + } + + // Delete the hierarchy directly as setparent isn't fully tested + Hierarchy_Delete (Transform_GetRef (parent)->hierarchy); + + return 0; +} + +static int +test_parent_child_setparent (void) +{ + transform_t parent = Transform_New (reg, (transform_t) {}); + transform_t child = Transform_New (reg, (transform_t) {}); + + Transform_SetName (parent, "parent"); + Transform_SetName (child, "child"); + + if (!check_indices (parent, 0, nullent, 1, 0)) { return 1; } + if (!check_indices (child, 0, nullent, 1, 0)) { return 1; } + + if (Transform_GetRef (parent)->hierarchy + == Transform_GetRef (child)->hierarchy) { + printf ("parent and child transforms have same hierarchy before" + " set paret\n"); + return 1; + } + + Transform_SetParent (child, parent); + + if (Transform_GetRef (parent)->hierarchy + != Transform_GetRef (child)->hierarchy) { + printf ("parent and child transforms have separate hierarchies\n"); + return 1; + } + + if (!check_hierarchy_size (parent, 2)) { return 1; } + + if (!check_indices (parent, 0, nullent, 1, 1)) { return 1; } + if (!check_indices (child, 1, 0, 2, 0)) { return 1; } + + hierarchy_t *h = Transform_GetRef (parent)->hierarchy; + mat4f_t *localMatrix = h->components[transform_type_localMatrix]; + mat4f_t *localInverse = h->components[transform_type_localInverse]; + mat4f_t *worldMatrix = h->components[transform_type_worldMatrix]; + mat4f_t *worldInverse = h->components[transform_type_worldInverse]; + vec4f_t *localRotation = h->components[transform_type_localRotation]; + vec4f_t *localScale = h->components[transform_type_localScale]; + if (!mat4_equal (localMatrix[0], identity) + || !mat4_equal (localInverse[0], identity) + || !mat4_equal (worldMatrix[0], identity) + || !mat4_equal (worldInverse[0], identity)) { + printf ("Parent transform matrices not identity\n"); + return 1; + } + + if (!vec4_equal (localRotation[0], identity[3]) + || !vec4_equal (localScale[0], one)) { + printf ("Parent transform rotation or scale not identity\n"); + return 1; + } + + if (!mat4_equal (localMatrix[1], identity) + || !mat4_equal (localInverse[1], identity) + || !mat4_equal (worldMatrix[1], identity) + || !mat4_equal (worldInverse[1], identity)) { + printf ("Child transform matrices not identity\n"); + return 1; + } + + if (!vec4_equal (localRotation[1], identity[3]) + || !vec4_equal (localScale[1], one)) { + printf ("Child transform rotation or scale not identity\n"); + return 1; + } + + // Delete the hierarchy directly as setparent isn't fully tested + Hierarchy_Delete (Transform_GetRef (parent)->hierarchy); + + return 0; +} + +static int +check_vector (transform_t transform, + vec4f_t (*func) (transform_t t), + vec4f_t expect, const char *msg) +{ + vec4f_t res = func(transform); + if (!vec4_equal (res, expect)) { + printf ("%s %s: expected "VEC4F_FMT" got "VEC4F_FMT"\n", + Transform_GetName (transform), msg, + VEC4_EXP (expect), VEC4_EXP (res)); + return 0; + } + return 1; +} + +static int +test_frames (void) +{ + transform_t root = Transform_NewNamed (reg, (transform_t) {}, "root"); + transform_t A = Transform_NewNamed (reg, root, "A"); + transform_t B = Transform_NewNamed (reg, root, "B"); + transform_t A1 = Transform_NewNamed (reg, A, "A1"); + transform_t B1 = Transform_NewNamed (reg, B, "B1"); + + Transform_SetLocalPosition (root, (vec4f_t) { 0, 0, 1, 1 }); + Transform_SetLocalPosition (A, (vec4f_t) { 1, 0, 0, 1 }); + Transform_SetLocalRotation (A, (vec4f_t) { 0.5, 0.5, 0.5, 0.5 }); + Transform_SetLocalPosition (B, (vec4f_t) { 0, 1, 0, 1 }); + Transform_SetLocalRotation (B, (vec4f_t) { 0.5, -0.5, 0.5, 0.5 }); + Transform_SetLocalPosition (A1, (vec4f_t) { 1, 0, 0, 1 }); + Transform_SetLocalRotation (A1, (vec4f_t) { -0.5, -0.5, -0.5, 0.5 }); + Transform_SetLocalPosition (B1, (vec4f_t) { 0, 1, 0, 1 }); + Transform_SetLocalRotation (B1, (vec4f_t) { -0.5, 0.5, -0.5, 0.5 }); + + hierarchy_t *h = Transform_GetRef (root)->hierarchy; + mat4f_t *localMatrix = h->components[transform_type_localMatrix]; + mat4f_t *localInverse = h->components[transform_type_localInverse]; + mat4f_t *worldMatrix = h->components[transform_type_worldMatrix]; + mat4f_t *worldInverse = h->components[transform_type_worldInverse]; + char **name = h->components[transform_type_name]; + for (uint32_t i = 0; i < h->num_objects; i++) { + mat4f_t res; + mmulf (res, localMatrix[i], localInverse[i]); + if (!mat4_equal (res, identity)) { + printf ("%s: localInverse not inverse of localMatrix\n", + name[i]); + printf ("l: " VEC4F_FMT "\n", MAT4_ROW(localMatrix[i], 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(localMatrix[i], 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(localMatrix[i], 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(localMatrix[i], 3)); + printf ("i: " VEC4F_FMT "\n", MAT4_ROW(localInverse[i], 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(localInverse[i], 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(localInverse[i], 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(localInverse[i], 3)); + printf ("r: " VEC4F_FMT "\n", MAT4_ROW(res, 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(res, 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(res, 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(res, 3)); + return 1; + } + puts (name[i]); + printf ("l: " VEC4F_FMT "\n", MAT4_ROW(localMatrix[i], 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(localMatrix[i], 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(localMatrix[i], 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(localMatrix[i], 3)); + printf ("i: " VEC4F_FMT "\n", MAT4_ROW(localInverse[i], 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(localInverse[i], 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(localInverse[i], 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(localInverse[i], 3)); + } + for (uint32_t i = 0; i < h->num_objects; i++) { + mat4f_t res; + mmulf (res, worldMatrix[i], worldInverse[i]); + if (!mat4_equal (res, identity)) { + printf ("%s: worldInverse not inverse of worldMatrix\n", + name[i]); + printf ("l: " VEC4F_FMT "\n", MAT4_ROW(worldMatrix[i], 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(worldMatrix[i], 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(worldMatrix[i], 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(worldMatrix[i], 3)); + printf ("i: " VEC4F_FMT "\n", MAT4_ROW(worldInverse[i], 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(worldInverse[i], 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(worldInverse[i], 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(worldInverse[i], 3)); + printf ("r: " VEC4F_FMT "\n", MAT4_ROW(res, 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(res, 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(res, 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(res, 3)); + return 1; + } + puts (name[i]); + printf ("l: " VEC4F_FMT "\n", MAT4_ROW(worldMatrix[i], 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(worldMatrix[i], 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(worldMatrix[i], 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(worldMatrix[i], 3)); + printf ("i: " VEC4F_FMT "\n", MAT4_ROW(worldInverse[i], 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(worldInverse[i], 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(worldInverse[i], 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(worldInverse[i], 3)); + } + + if (!check_vector (root, Transform_GetLocalPosition, + (vec4f_t) { 0, 0, 1, 1 }, "local position")) { + return 1; + } + if (!check_vector (root, Transform_GetWorldPosition, + (vec4f_t) { 0, 0, 1, 1 }, "world position")) { + return 1; + } + if (!check_vector (root, Transform_Forward, (vec4f_t) { 1, 0, 0, 0 }, + "forward")) { + return 1; + } + if (!check_vector (root, Transform_Right, (vec4f_t) { 0, -1, 0, 0 }, + "right")) { + return 1; + } + if (!check_vector (root, Transform_Up, (vec4f_t) { 0, 0, 1, 0 }, + "up")) { + return 1; + } + + if (!check_vector (A, Transform_GetLocalPosition, (vec4f_t) { 1, 0, 0, 1 }, + "local position")) { + return 1; + } + if (!check_vector (A, Transform_GetWorldPosition, (vec4f_t) { 1, 0, 1, 1 }, + "world position")) { + return 1; + } + if (!check_vector (A, Transform_Forward, (vec4f_t) { 0, 1, 0, 0 }, + "forward")) { + return 1; + } + if (!check_vector (A, Transform_Right, (vec4f_t) { 0, 0, -1, 0 }, + "right")) { + return 1; + } + if (!check_vector (A, Transform_Up, (vec4f_t) { 1, 0, 0, 0 }, + "up")) { + return 1; + } + if (!check_vector (A1, Transform_GetLocalPosition, (vec4f_t) { 1, 0, 0, 1 }, + "local position")) { + return 1; + } + if (!check_vector (A1, Transform_GetWorldPosition, (vec4f_t) { 1, 1, 1, 1 }, + "world position")) { + return 1; + } + if (!check_vector (A1, Transform_Forward, (vec4f_t) { 1, 0, 0, 0 }, + "forward")) { + return 1; + } + if (!check_vector (A1, Transform_Right, (vec4f_t) { 0, -1, 0, 0 }, + "right")) { + return 1; + } + if (!check_vector (A1, Transform_Up, (vec4f_t) { 0, 0, 1, 0 }, + "up")) { + return 1; + } + + if (!check_vector (B, Transform_GetLocalPosition, (vec4f_t) { 0, 1, 0, 1 }, + "local position")) { + return 1; + } + if (!check_vector (B, Transform_GetWorldPosition, (vec4f_t) { 0, 1, 1, 1 }, + "world position")) { + return 1; + } + if (!check_vector (B, Transform_Forward, (vec4f_t) { 0, 0, 1, 0 }, + "forward")) { + return 1; + } + if (!check_vector (B, Transform_Right, (vec4f_t) { 1, 0, 0, 0 }, + "right")) { + return 1; + } + if (!check_vector (B, Transform_Up, (vec4f_t) { 0,-1, 0, 0 }, + "up")) { + return 1; + } + if (!check_vector (B1, Transform_GetLocalPosition, (vec4f_t) { 0, 1, 0, 1 }, + "local position")) { + return 1; + } + if (!check_vector (B1, Transform_GetWorldPosition, (vec4f_t) {-1, 1, 1, 1 }, + "world position")) { + return 1; + } + if (!check_vector (B1, Transform_Forward, (vec4f_t) { 1, 0, 0, 0 }, + "forward")) { + return 1; + } + if (!check_vector (B1, Transform_Right, (vec4f_t) { 0, -1, 0, 0 }, + "right")) { + return 1; + } + if (!check_vector (B1, Transform_Up, (vec4f_t) { 0, 0, 1, 0 }, + "up")) { + return 1; + } + + Transform_Delete (root); + + return 0; +} + +int +main (void) +{ + scene_t *scene = Scene_NewScene (); + reg = scene->reg; + + if (test_single_transform ()) { return 1; } + if (test_parent_child_init ()) { return 1; } + if (test_parent_child_setparent ()) { return 1; } + if (test_frames ()) { return 1; } + + Scene_DeleteScene (scene); + + return 0; +} diff --git a/libs/scene/transform.c b/libs/scene/transform.c new file mode 100644 index 000000000..c9b532fab --- /dev/null +++ b/libs/scene/transform.c @@ -0,0 +1,397 @@ +/* + transform.c + + General transform handling + + Copyright (C) 2021 Bill Currke + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#define IMPLEMENT_TRANSFORM_Funcs + +#include "QF/scene/scene.h" +#include "QF/scene/transform.h" + +static void +transform_mat4f_identity (void *_mat) +{ + vec4f_t *mat = _mat; + mat4fidentity (mat); +} + +static void +transform_rotation_identity (void *_rot) +{ + vec4f_t *rot = _rot; + *rot = (vec4f_t) { 0, 0, 0, 1 }; +} + +static void +transform_scale_identity (void *_scale) +{ + vec4f_t *scale = _scale; + *scale = (vec4f_t) { 1, 1, 1, 1 }; +} + +static void +transform_modified_init (void *_modified) +{ + byte *modified = _modified; + *modified = 1; +} + +static const component_t transform_components[transform_type_count] = { + [transform_type_name] = { + .size = sizeof (char *), + .name = "Name", + }, + [transform_type_tag] = { + .size = sizeof (uint32_t), + .name = "Tag", + }, + [transform_type_modified] = { + .size = sizeof (byte), + .create = transform_modified_init, + .name = "Modified", + }, + [transform_type_localMatrix] = { + .size = sizeof (mat4f_t), + .create = transform_mat4f_identity, + .name = "Local Matrix", + }, + [transform_type_localInverse] = { + .size = sizeof (mat4f_t), + .create = transform_mat4f_identity, + .name = "Local Inverse", + }, + [transform_type_worldMatrix] = { + .size = sizeof (mat4f_t), + .create = transform_mat4f_identity, + .name = "World Matrix", + }, + [transform_type_worldInverse] = { + .size = sizeof (mat4f_t), + .create = transform_mat4f_identity, + .name = "World Inverse", + }, + [transform_type_localRotation] = { + .size = sizeof (vec4f_t), + .create = transform_rotation_identity, + .name = "Local Rotation", + }, + [transform_type_localScale] = { + .size = sizeof (vec4f_t), + .create = transform_scale_identity, + .name = "Local Scale", + }, + [transform_type_worldRotation] = { + .size = sizeof (vec4f_t), + .create = transform_rotation_identity, + .name = "World Rotation", + }, +}; + +static const hierarchy_type_t transform_type = { + .num_components = transform_type_count, + .components = transform_components, +}; + +static void +transform_calcLocalInverse (hierarchy_t *h, uint32_t index) +{ + mat4f_t *localMatrix = h->components[transform_type_localMatrix]; + mat4f_t *localInverse = h->components[transform_type_localInverse]; + // This takes advantage of the fact that localMatrix is a simple + // homogenous scale/rotate/translate matrix with no shear + vec4f_t x = localMatrix[index][0]; + vec4f_t y = localMatrix[index][1]; + vec4f_t z = localMatrix[index][2]; + vec4f_t t = localMatrix[index][3]; + + // "one" is to ensure both the scalar and translation have 1 in their + // fourth components + vec4f_t one = { 0, 0, 0, 1 }; + vec4f_t nx = { x[0], y[0], z[0], 0 }; + vec4f_t ny = { x[1], y[1], z[1], 0 }; + vec4f_t nz = { x[2], y[2], z[2], 0 }; + vec4f_t nt = one - t[0] * nx - t[1] * ny - t[2] * nz; + // vertical dot product!!! + vec4f_t s = 1 / (nx * nx + ny * ny + nz * nz + one); + localInverse[index][0] = nx * s; + localInverse[index][1] = ny * s; + localInverse[index][2] = nz * s; + localInverse[index][3] = nt * s; +} + +static void +Transform_UpdateMatrices (hierarchy_t *h) +{ + mat4f_t *localMatrix = h->components[transform_type_localMatrix]; + mat4f_t *localInverse = h->components[transform_type_localInverse]; + mat4f_t *worldMatrix = h->components[transform_type_worldMatrix]; + mat4f_t *worldInverse = h->components[transform_type_worldInverse]; + vec4f_t *localRotation = h->components[transform_type_localRotation]; + vec4f_t *worldRotation = h->components[transform_type_worldRotation]; + byte *modified = h->components[transform_type_modified]; + + for (uint32_t i = 0; i < h->num_objects; i++) { + if (modified[i]) { + transform_calcLocalInverse (h, i); + } + } + if (modified[0]) { + memcpy (worldMatrix[0], + localMatrix[0], sizeof (mat4_t)); + memcpy (worldInverse[0], + localInverse[0], sizeof (mat4_t)); + worldRotation[0] = localRotation[0]; + } + for (size_t i = 1; i < h->num_objects; i++) { + uint32_t parent = h->parentIndex[i]; + + if (modified[i] || modified[parent]) { + mmulf (worldMatrix[i], + worldMatrix[parent], localMatrix[i]); + modified[i] = 1; + } + } + for (size_t i = 1; i < h->num_objects; i++) { + uint32_t parent = h->parentIndex[i]; + + if (modified[i] || modified[parent]) { + mmulf (worldInverse[i], + localInverse[i], worldInverse[parent]); + } + } + for (size_t i = 1; i < h->num_objects; i++) { + uint32_t parent = h->parentIndex[i]; + if (modified[i] || modified[parent]) { + worldRotation[i] = qmulf (worldRotation[parent], + localRotation[i]); + } + } + memset (modified, 0, h->num_objects); +} + +transform_t +Transform_New (ecs_registry_t *reg, transform_t parent) +{ + uint32_t transform = ECS_NewEntity (reg); + hierref_t *ref = Ent_AddComponent (transform, scene_href, reg); + + if (parent.reg && parent.id != nullent) { + hierref_t *pref = Transform_GetRef (parent); + ref->hierarchy = pref->hierarchy; + ref->index = Hierarchy_InsertHierarchy (pref->hierarchy, 0, + pref->index, 0); + } else { + ref->hierarchy = Hierarchy_New (reg, scene_href, &transform_type, 1); + ref->index = 0; + } + ref->hierarchy->ent[ref->index] = transform; + Transform_UpdateMatrices (ref->hierarchy); + return (transform_t) { .reg = reg, .id = transform, .comp = scene_href }; +} + +void +Transform_Delete (transform_t transform) +{ + hierref_t *ref = Transform_GetRef (transform); + if (ref->index != 0) { + // The transform is not the root, so pull it out of its current + // hierarchy so deleting it is easier + Transform_SetParent (transform, (transform_t) {}); + } + // Takes care of freeing the transforms + Hierarchy_Delete (ref->hierarchy); +} + +transform_t +Transform_NewNamed (ecs_registry_t *reg, transform_t parent, const char *name) +{ + transform_t transform = Transform_New (reg, parent); + Transform_SetName (transform, name); + return transform; +} + +void +Transform_SetParent (transform_t transform, transform_t parent) +{ + hierarchy_t *dst = 0; + uint32_t dstParent = nullent; + hierarchy_t *src = 0; + uint32_t srcIndex = 0; + if (Transform_Valid (parent)) { + __auto_type ref = Transform_GetRef (parent); + dst = ref->hierarchy; + dstParent = ref->index; + } + { + __auto_type ref = Transform_GetRef (transform); + src = ref->hierarchy; + srcIndex = ref->index; + } + Hierarchy_SetParent (dst, dstParent, src, srcIndex); + + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + byte *modified = h->components[transform_type_modified]; + modified[ref->index] = 1; + Transform_UpdateMatrices (h); +} + +void +Transform_SetName (transform_t transform, const char *_name) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + char **name = h->components[transform_type_name]; + //FIXME create a string pool (similar to qfcc's, or even move that to util) + if (name[ref->index]) { + free (name[ref->index]); + } + name[ref->index] = strdup (_name); +} + +void +Transform_SetTag (transform_t transform, uint32_t _tag) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + uint32_t *tag = h->components[transform_type_tag]; + tag[ref->index] = _tag; +} + +void +Transform_SetLocalPosition (transform_t transform, vec4f_t position) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + mat4f_t *localMatrix = h->components[transform_type_localMatrix]; + byte *modified = h->components[transform_type_modified]; + localMatrix[ref->index][3] = position; + modified[ref->index] = 1; + Transform_UpdateMatrices (h); +} + +void +Transform_SetLocalRotation (transform_t transform, vec4f_t rotation) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + mat4f_t *localMatrix = h->components[transform_type_localMatrix]; + vec4f_t *localRotation = h->components[transform_type_localRotation]; + vec4f_t *localScale = h->components[transform_type_localScale]; + byte *modified = h->components[transform_type_modified]; + vec4f_t scale = localScale[ref->index]; + + mat4f_t mat; + mat4fquat (mat, rotation); + + localRotation[ref->index] = rotation; + localMatrix[ref->index][0] = mat[0] * scale[0]; + localMatrix[ref->index][1] = mat[1] * scale[1]; + localMatrix[ref->index][2] = mat[2] * scale[2]; + modified[ref->index] = 1; + Transform_UpdateMatrices (h); +} + +void +Transform_SetLocalScale (transform_t transform, vec4f_t scale) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + mat4f_t *localMatrix = h->components[transform_type_localMatrix]; + vec4f_t *localRotation = h->components[transform_type_localRotation]; + vec4f_t *localScale = h->components[transform_type_localScale]; + byte *modified = h->components[transform_type_modified]; + vec4f_t rotation = localRotation[ref->index]; + + mat4f_t mat; + mat4fquat (mat, rotation); + + localScale[ref->index] = scale; + localMatrix[ref->index][0] = mat[0] * scale[0]; + localMatrix[ref->index][1] = mat[1] * scale[1]; + localMatrix[ref->index][2] = mat[2] * scale[2]; + modified[ref->index] = 1; + Transform_UpdateMatrices (h); +} + +void +Transform_SetWorldPosition (transform_t transform, vec4f_t position) +{ + __auto_type ref = Transform_GetRef (transform); + if (ref->index) { + hierarchy_t *h = ref->hierarchy; + mat4f_t *worldInverse = h->components[transform_type_worldInverse]; + uint32_t parent = h->parentIndex[ref->index]; + position = mvmulf (worldInverse[parent], position); + } + Transform_SetLocalPosition (transform, position); +} + +void +Transform_SetWorldRotation (transform_t transform, vec4f_t rotation) +{ + __auto_type ref = Transform_GetRef (transform); + if (ref->index) { + hierarchy_t *h = ref->hierarchy; + vec4f_t *worldRotation = h->components[transform_type_worldRotation]; + uint32_t parent = h->parentIndex[ref->index]; + rotation = qmulf (qconjf (worldRotation[parent]), rotation); + } + Transform_SetLocalRotation (transform, rotation); +} + +void +Transform_SetLocalTransform (transform_t transform, vec4f_t scale, + vec4f_t rotation, vec4f_t position) +{ + __auto_type ref = Transform_GetRef (transform); + hierarchy_t *h = ref->hierarchy; + mat4f_t *localMatrix = h->components[transform_type_localMatrix]; + vec4f_t *localRotation = h->components[transform_type_localRotation]; + vec4f_t *localScale = h->components[transform_type_localScale]; + byte *modified = h->components[transform_type_modified]; + mat4f_t mat; + mat4fquat (mat, rotation); + + position[3] = 1; + localRotation[ref->index] = rotation; + localScale[ref->index] = scale; + localMatrix[ref->index][0] = mat[0] * scale[0]; + localMatrix[ref->index][1] = mat[1] * scale[1]; + localMatrix[ref->index][2] = mat[2] * scale[2]; + localMatrix[ref->index][3] = position; + modified[ref->index] = 1; + Transform_UpdateMatrices (h); +} diff --git a/libs/ui/Makemodule.am b/libs/ui/Makemodule.am new file mode 100644 index 000000000..d73d20d8e --- /dev/null +++ b/libs/ui/Makemodule.am @@ -0,0 +1,34 @@ +include libs/ui/test/Makemodule.am + +lib_LTLIBRARIES += \ + libs/ui/libQFui.la \ + libs/ui/libQFgui.la + +gui_deps = \ + libs/ui/libQFui.la + +gui_libadd = \ + $(FREETYPE_LIBS) \ + $(HARFBUZZ_LIBS) + +ui_deps = \ + libs/ecs/libQFecs.la \ + libs/util/libQFutil.la + +libs_ui_libQFgui_la_LDFLAGS= $(lib_ldflags) +libs_ui_libQFgui_la_LIBADD= $(gui_deps) $(ui_deps) $(gui_libadd) +libs_ui_libQFgui_la_DEPENDENCIES= $(gui_deps) $(ui_deps) +libs_ui_libQFgui_la_SOURCES= \ + libs/ui/canvas.c \ + libs/ui/font.c \ + libs/ui/text.c + +libs_ui_libQFui_la_LDFLAGS= $(lib_ldflags) +libs_ui_libQFui_la_LIBADD= $(ui_deps) +libs_ui_libQFui_la_DEPENDENCIES= $(ui_deps) +libs_ui_libQFui_la_SOURCES= \ + libs/ui/inputline.c \ + libs/ui/passage.c \ + libs/ui/txtbuffer.c \ + libs/ui/view.c \ + libs/ui/vrect.c diff --git a/libs/ui/canvas.c b/libs/ui/canvas.c new file mode 100644 index 000000000..b2974da8e --- /dev/null +++ b/libs/ui/canvas.c @@ -0,0 +1,461 @@ +/* + canvas.c + + Integration of 2d and 3d objects + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/12/12 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#define IMPLEMENT_CANVAS_Funcs +#include "QF/ui/canvas.h" +#include "QF/ui/text.h" +#include "QF/ui/view.h" + +#include "QF/plugin/vid_render.h" + +static uint32_t +_canvas_rangeid (ecs_registry_t *reg, uint32_t ent, uint32_t comp, uint32_t c) +{ + comp += canvas_canvas - c; + // view components come immediately after canvas components + uint32_t vcomp = view_href + comp + canvas_comp_count - canvas_canvas; + hierref_t *href = Ent_GetComponent (ent, vcomp, reg); + // the root entity of the hierarchy has the canvas component + uint32_t cent = href->hierarchy->ent[0]; + canvas_t *canvas = Ent_GetComponent (cent, comp, reg); + return canvas->range[c]; +} + +#define canvas_rangeid(c) \ +static uint32_t \ +canvas_##c##_rangeid (ecs_registry_t *reg, uint32_t ent, uint32_t comp) \ +{ \ + return _canvas_rangeid (reg, ent, comp, canvas_##c); \ +} +canvas_rangeid(update) +canvas_rangeid(updateonce) +canvas_rangeid(tile) +canvas_rangeid(pic) +canvas_rangeid(fitpic) +canvas_rangeid(subpic) +canvas_rangeid(cachepic) +canvas_rangeid(fill) +canvas_rangeid(charbuff) +canvas_rangeid(func) +canvas_rangeid(lateupdate) +canvas_rangeid(outline) +#undef canvas_rangeid + +static void +canvas_canvas_destroy (void *_canvas) +{ +} + +const component_t canvas_components[canvas_comp_count] = { + [canvas_update] = { + .size = sizeof (canvas_update_f), + .name = "update", + .rangeid = canvas_update_rangeid, + }, + [canvas_updateonce] = { + .size = sizeof (canvas_update_f), + .name = "updateonce", + .rangeid = canvas_updateonce_rangeid, + }, + [canvas_tile] = { + .size = sizeof (byte), + .name = "tile", + .rangeid = canvas_tile_rangeid, + }, + [canvas_pic] = { + .size = sizeof (qpic_t *), + .name = "pic", + .rangeid = canvas_pic_rangeid, + }, + [canvas_fitpic] = { + .size = sizeof (qpic_t *), + .name = "fitpic", + .rangeid = canvas_fitpic_rangeid, + }, + [canvas_subpic] = { + .size = sizeof (canvas_subpic_t), + .name = "subpic", + .rangeid = canvas_subpic_rangeid, + }, + [canvas_cachepic] = { + .size = sizeof (const char *), + .name = "cachepic", + .rangeid = canvas_cachepic_rangeid, + }, + [canvas_fill] = { + .size = sizeof (byte), + .name = "fill", + .rangeid = canvas_fill_rangeid, + }, + [canvas_charbuff] = { + .size = sizeof (draw_charbuffer_t *), + .name = "charbuffer", + .rangeid = canvas_charbuff_rangeid, + }, + [canvas_func] = { + .size = sizeof (canvas_func_f), + .name = "func", + .rangeid = canvas_func_rangeid, + }, + [canvas_lateupdate] = { + .size = sizeof (canvas_update_f), + .name = "lateupdate", + .rangeid = canvas_lateupdate_rangeid, + }, + [canvas_outline] = { + .size = sizeof (byte), + .name = "outline", + .rangeid = canvas_outline_rangeid, + }, + [canvas_canvas] = { + .size = sizeof (canvas_t), + .name = "canvas", + .destroy = canvas_canvas_destroy, + }, +}; + +typedef void (*canvas_sysfunc_f) (canvas_system_t *canvas_sys, + ecs_pool_t *pool, ecs_range_t range); +static void +draw_update (canvas_system_t *canvas_sys, ecs_pool_t *pool, ecs_range_t range) +{ + ecs_system_t viewsys = { canvas_sys->reg, canvas_sys->view_base }; + uint32_t count = range.end - range.start; + uint32_t *ent = pool->dense + range.start; + __auto_type func = (canvas_update_f *) pool->data + range.start; + while (count-- > 0) { + (*func++) (View_FromEntity (viewsys, *ent++)); + } +} + +static void +draw_tile_views (canvas_system_t *canvas_sys, ecs_pool_t *pool, + ecs_range_t range) +{ + ecs_system_t viewsys = { canvas_sys->reg, canvas_sys->view_base }; + uint32_t count = range.end - range.start; + uint32_t *ent = pool->dense + range.start; + while (count-- > 0) { + view_t view = View_FromEntity (viewsys, *ent++); + if (View_GetVisible (view)) { + view_pos_t pos = View_GetAbs (view); + view_pos_t len = View_GetLen (view); + r_funcs->Draw_TileClear (pos.x, pos.y, len.x, len.y); + } + } +} + +static void +draw_pic_views (canvas_system_t *canvas_sys, ecs_pool_t *pool, + ecs_range_t range) +{ + ecs_system_t viewsys = { canvas_sys->reg, canvas_sys->view_base }; + uint32_t count = range.end - range.start; + uint32_t *ent = pool->dense + range.start; + __auto_type pic = (qpic_t **) pool->data + range.start; + while (count-- > 0) { + view_t view = View_FromEntity (viewsys, *ent++); + if (View_GetVisible (view)) { + view_pos_t pos = View_GetAbs (view); + r_funcs->Draw_Pic (pos.x, pos.y, *pic); + } + pic++; + } +} + +static void +draw_fitpic_views (canvas_system_t *canvas_sys, ecs_pool_t *pool, + ecs_range_t range) +{ + ecs_system_t viewsys = { canvas_sys->reg, canvas_sys->view_base }; + uint32_t count = range.end - range.start; + uint32_t *ent = pool->dense + range.start; + __auto_type pic = (qpic_t **) pool->data + range.start; + while (count-- > 0) { + view_t view = View_FromEntity (viewsys, *ent++); + if (View_GetVisible (view)) { + view_pos_t pos = View_GetAbs (view); + view_pos_t len = View_GetLen (view); + r_funcs->Draw_FitPic (pos.x, pos.y, len.x, len.y, *pic); + } + pic++; + } +} + +static void +draw_subpic_views (canvas_system_t *canvas_sys, ecs_pool_t *pool, + ecs_range_t range) +{ + ecs_system_t viewsys = { canvas_sys->reg, canvas_sys->view_base }; + uint32_t count = range.end - range.start; + uint32_t *ent = pool->dense + range.start; + __auto_type subpic = (canvas_subpic_t *) pool->data + range.start; + while (count-- > 0) { + view_t view = View_FromEntity (viewsys, *ent++); + if (View_GetVisible (view)) { + view_pos_t pos = View_GetAbs (view); + r_funcs->Draw_SubPic (pos.x, pos.y, subpic->pic, + subpic->x, subpic->y, subpic->w, subpic->h); + } + subpic++; + } +} + +static void +draw_cachepic_views (canvas_system_t *canvas_sys, ecs_pool_t *pool, + ecs_range_t range) +{ + ecs_system_t viewsys = { canvas_sys->reg, canvas_sys->view_base }; + uint32_t count = range.end - range.start; + uint32_t *ent = pool->dense + range.start; + __auto_type name = (const char **) pool->data + range.start; + while (count-- > 0) { + view_t view = View_FromEntity (viewsys, *ent++); + if (View_GetVisible (view)) { + view_pos_t pos = View_GetAbs (view); + qpic_t *pic = r_funcs->Draw_CachePic (*name, 1); + r_funcs->Draw_Pic (pos.x, pos.y, pic); + } + name++; + } +} + +static void +draw_fill_views (canvas_system_t *canvas_sys, ecs_pool_t *pool, + ecs_range_t range) +{ + ecs_system_t viewsys = { canvas_sys->reg, canvas_sys->view_base }; + uint32_t count = range.end - range.start; + uint32_t *ent = pool->dense + range.start; + __auto_type fill = (byte *) pool->data + range.start; + while (count-- > 0) { + view_t view = View_FromEntity (viewsys, *ent++); + if (View_GetVisible (view)) { + view_pos_t pos = View_GetAbs (view); + view_pos_t len = View_GetLen (view); + r_funcs->Draw_Fill (pos.x, pos.y, len.x, len.y, *fill); + } + fill++; + } +} + +static void +draw_charbuff_views (canvas_system_t *canvas_sys, ecs_pool_t *pool, + ecs_range_t range) +{ + ecs_system_t viewsys = { canvas_sys->reg, canvas_sys->view_base }; + uint32_t count = range.end - range.start; + uint32_t *ent = pool->dense + range.start; + __auto_type charbuff = (draw_charbuffer_t **) pool->data + range.start; + while (count-- > 0) { + view_t view = View_FromEntity (viewsys, *ent++); + if (View_GetVisible (view)) { + view_pos_t pos = View_GetAbs (view); + r_funcs->Draw_CharBuffer (pos.x, pos.y, *charbuff); + } + charbuff++; + } +} + +static void +draw_func_views (canvas_system_t *canvas_sys, ecs_pool_t *pool, + ecs_range_t range) +{ + ecs_system_t viewsys = { canvas_sys->reg, canvas_sys->view_base }; + uint32_t count = range.end - range.start; + uint32_t *ent = pool->dense + range.start; + __auto_type func = (canvas_func_f *) pool->data + range.start; + while (count-- > 0) { + view_t view = View_FromEntity (viewsys, *ent++); + if (View_GetVisible (view)) { + view_pos_t pos = View_GetAbs (view); + view_pos_t len = View_GetLen (view); + (*func) (pos, len); + } + func++; + } +} + +static void +draw_outline_views (canvas_system_t *canvas_sys, ecs_pool_t *pool, + ecs_range_t range) +{ + ecs_system_t viewsys = { canvas_sys->reg, canvas_sys->view_base }; + uint32_t count = range.end - range.start; + uint32_t *ent = pool->dense + range.start; + __auto_type col = (byte *) pool->data + range.start; + __auto_type line = r_funcs->Draw_Line; + while (count-- > 0) { + view_t view = View_FromEntity (viewsys, *ent++); + byte c = *col++; + if (View_GetVisible (view)) { + view_pos_t p = View_GetAbs (view); + view_pos_t l = View_GetLen (view); + view_pos_t q = { p.x + l.x - 1, p.y + l.y - 1 }; + line (p.x, p.y, q.x, p.y, c); + line (p.x, q.y, q.x, q.y, c); + line (p.x, p.y, p.x, q.y, c); + line (q.x, p.y, q.x, q.y, c); + } + } +} + +void +Canvas_Draw (canvas_system_t canvas_sys) +{ + static canvas_sysfunc_f draw_func[canvas_comp_count] = { + [canvas_update] = draw_update, + [canvas_updateonce] = draw_update, + [canvas_tile] = draw_tile_views, + [canvas_pic] = draw_pic_views, + [canvas_fitpic] = draw_fitpic_views, + [canvas_subpic] = draw_subpic_views, + [canvas_cachepic] = draw_cachepic_views, + [canvas_fill] = draw_fill_views, + [canvas_charbuff] = draw_charbuff_views, + [canvas_func] = draw_func_views, + [canvas_lateupdate] = draw_update, + [canvas_outline] = draw_outline_views, + }; + + uint32_t comp = canvas_sys.base + canvas_canvas; + ecs_pool_t *canvas_pool = &canvas_sys.reg->comp_pools[comp]; + uint32_t count = canvas_pool->count; + //uint32_t *entities = canvas_pool->dense; + __auto_type canvases = (canvas_t *) canvas_pool->data; + + while (count-- > 0) { + canvas_t *canvas = canvases++; + //uint32_t ent = *entities++; + + for (int i = 0; i < canvas_comp_count; i++) { + uint32_t c = canvas_sys.base + i; + uint32_t rid = canvas->range[i]; + ecs_range_t range = ECS_GetSubpoolRange (canvas_sys.reg, c, rid); + if (draw_func[i]) { + ecs_pool_t *pool = &canvas_sys.reg->comp_pools[c]; + draw_func[i] (&canvas_sys, pool, range); + } + } + } + { + ecs_pool_t *pool = &canvas_sys.reg->comp_pools[canvas_updateonce]; + ecs_subpool_t *subpool = &canvas_sys.reg->subpools[canvas_updateonce]; + pool->count = 0; + uint32_t rcount = subpool->num_ranges - subpool->available; + memset (subpool->ranges, 0, rcount * sizeof (*subpool->ranges)); + } +} + +void +Canvas_InitSys (canvas_system_t *canvas_sys, ecs_registry_t *reg) +{ + *canvas_sys = (canvas_system_t) { + .reg = reg, + .base = ECS_RegisterComponents (reg, canvas_components, + canvas_comp_count), + .view_base = ECS_RegisterComponents (reg, view_components, + view_comp_count), + .text_base = ECS_RegisterComponents (reg, text_components, + text_comp_count), + }; +} + +void +Canvas_AddToEntity (canvas_system_t canvas_sys, uint32_t ent) +{ + canvas_t canvas = { }; + for (uint32_t i = 0; i < canvas_comp_count; i++) { + canvas.range[i] = ECS_NewSubpoolRange (canvas_sys.reg, + canvas_sys.base + i); + } + Ent_SetComponent (ent, canvas_sys.base + canvas_canvas, canvas_sys.reg, + &canvas); + View_AddToEntity (ent, + (ecs_system_t) { canvas_sys.reg, canvas_sys.view_base }, + nullview); +} + +uint32_t +Canvas_New (canvas_system_t canvas_sys) +{ + uint32_t ent = ECS_NewEntity (canvas_sys.reg); + Canvas_AddToEntity (canvas_sys, ent); + return ent; +} + +static int +canvas_href_cmp (const void *_a, const void *_b, void *arg) +{ + uint32_t enta = *(const uint32_t *)_a; + uint32_t entb = *(const uint32_t *)_b; + canvas_system_t *canvas_sys = arg; + ecs_registry_t *reg = canvas_sys->reg; + uint32_t href = canvas_sys->view_base + view_href; + hierref_t *ref_a = Ent_GetComponent (enta, href, reg); + hierref_t *ref_b = Ent_GetComponent (entb, href, reg); + if (ref_a->hierarchy == ref_b->hierarchy) { + return ref_a->index - ref_b->index; + } + ptrdiff_t diff = ref_a->hierarchy - ref_b->hierarchy; + return diff > 0 ? 1 : diff < 0 ? -1 : 0; +} + +void +Canvas_SortComponentPool (canvas_system_t canvas_sys, uint32_t ent, + uint32_t component) +{ + canvas_t *canvas = Ent_GetComponent (ent, canvas_sys.base + canvas_canvas, + canvas_sys.reg); + uint32_t rid = canvas->range[component]; + uint32_t c = component + canvas_sys.base; + ecs_range_t range = ECS_GetSubpoolRange (canvas_sys.reg, c, rid); + ECS_SortComponentPoolRange (canvas_sys.reg, c, range, + canvas_href_cmp, &canvas_sys); +} + +void +Canvas_SetLen (canvas_system_t canvas_sys, view_pos_t len) +{ + uint32_t comp = canvas_sys.base + canvas_canvas; + ecs_pool_t *canvas_pool = &canvas_sys.reg->comp_pools[comp]; + uint32_t count = canvas_pool->count; + uint32_t *entities = canvas_pool->dense; + + while (count-- > 0) { + uint32_t ent = *entities++; + view_t view = Canvas_GetRootView (canvas_sys, ent); + View_SetLen (view, len.x, len.y); + View_UpdateHierarchy (view); + } +} diff --git a/libs/ui/font.c b/libs/ui/font.c new file mode 100644 index 000000000..9b02188f8 --- /dev/null +++ b/libs/ui/font.c @@ -0,0 +1,146 @@ +/* + font.c + + Font management + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/8/26 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#include "QF/quakefs.h" +#include "QF/sys.h" +#include "QF/math/bitop.h" + +#include "QF/plugin/vid_render.h" + +#include "QF/ui/font.h" + +#include "compat.h" + +static FT_Library ft; + +static void +copy_glyph (vrect_t *rect, FT_GlyphSlot src_glyph, font_t *font) +{ + int dst_pitch = font->scrap.width; + byte *dst = font->scrap_bitmap + rect->x + rect->y * dst_pitch; + int src_pitch = src_glyph->bitmap.pitch; + byte *src = src_glyph->bitmap.buffer; + + for (unsigned i = 0; i < src_glyph->bitmap.rows; i++) { + memcpy (dst, src, src_glyph->bitmap.width); + dst += dst_pitch; + src += src_pitch; + } +} + +static void +Font_shutdown (void *data) +{ + FT_Done_FreeType (ft); +} + +VISIBLE void +Font_Init (void) +{ + if (FT_Init_FreeType (&ft)) { + Sys_Error ("Could not init FreeType library"); + } + Sys_RegisterShutdown (Font_shutdown, 0); +} + +VISIBLE void +Font_Free (font_t *font) +{ + if (font->face) { + FT_Done_Face (font->face); + } + if (font->scrap.rects || font->scrap.free_rects) { + R_ScrapDelete (&font->scrap); + } + free (font->glyph_rects); + free (font->glyph_bearings); + free (font->scrap_bitmap); + free (font->font_resource); + free (font); +} + +VISIBLE font_t * +Font_Load (QFile *font_file, int size) +{ + byte *font_data = QFS_LoadFile (font_file, 0); + if (!font_data) { + return 0; + } + size_t font_size = qfs_filesize; + font_t *font = calloc (1, sizeof (font_t)); + font->font_resource = font_data; + if (FT_New_Memory_Face (ft, font_data, font_size, 0, &font->face)) { + Font_Free (font); + return 0; + } + + FT_Set_Pixel_Sizes(font->face, 0, size); + int pixels = 0; + for (FT_Long gind = 0; gind < font->face->num_glyphs; gind++) { + FT_Load_Glyph (font->face, gind, FT_LOAD_DEFAULT); + + __auto_type g = font->face->glyph; + // include padding around the glyph to avoid texel leaks + pixels += (g->bitmap.width + 1) * (g->bitmap.rows + 1); + } + pixels = sqrt (5 * pixels / 4); + pixels = BITOP_RUP (pixels); + R_ScrapInit (&font->scrap, pixels, pixels); + font->scrap_bitmap = calloc (1, pixels * pixels); + font->num_glyphs = font->face->num_glyphs; + font->glyph_rects = malloc (font->num_glyphs * sizeof (vrect_t)); + font->glyph_bearings = malloc (font->num_glyphs * sizeof (vec2i_t)); + + for (FT_Long gind = 0; gind < font->face->num_glyphs; gind++) { + vrect_t *rect = &font->glyph_rects[gind]; + vec2i_t *bearing = &font->glyph_bearings[gind]; + FT_Load_Glyph (font->face, gind, FT_LOAD_DEFAULT); + __auto_type slot = font->face->glyph; + FT_Render_Glyph (slot, FT_RENDER_MODE_NORMAL); + // add padding to create a buffer around the glyph to prevent texel + // leaks + int width = slot->bitmap.width + 1; + int height = slot->bitmap.rows + 1; + *rect = *R_ScrapAlloc (&font->scrap, width, height); + *bearing = (vec2i_t) { slot->bitmap_left, slot->bitmap_top }; + // shrink the rect so as to NOT include the padding + rect->width -= 1; + rect->height -= 1; + + copy_glyph (rect, slot, font); + } + font->fontid = r_funcs->Draw_AddFont (font); + + return font; +} diff --git a/libs/console/inputline.c b/libs/ui/inputline.c similarity index 98% rename from libs/console/inputline.c rename to libs/ui/inputline.c index 0123bf0da..93c13d2bc 100644 --- a/libs/console/inputline.c +++ b/libs/ui/inputline.c @@ -39,12 +39,10 @@ # include #endif -#include "QF/cmd.h" -#include "QF/console.h" #include "QF/keys.h" #include "QF/mathlib.h" -#include "compat.h" +#include "QF/ui/inputline.h" VISIBLE struct inputline_s * Con_CreateInputLine (int lines, int lsize, char prompt) diff --git a/libs/ui/passage.c b/libs/ui/passage.c new file mode 100644 index 000000000..d3c374d80 --- /dev/null +++ b/libs/ui/passage.c @@ -0,0 +1,227 @@ +/* + passage.c + + Text passage formatting. + + Copyright (C) 2022 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/alloc.h" +#include "QF/qtypes.h" +#include "QF/sys.h" + +#include "QF/ecs.h" + +#include "QF/ui/passage.h" +#include "QF/ui/view.h" + +const component_t passage_components[passage_comp_count] = { + [passage_href] = { + .size = sizeof (hierref_t), + .name = "passage href", + .destroy = Hierref_DestroyComponent, + }, +}; + +static const component_t passage_type_components[passage_type_count] = { + [passage_type_text_obj] = { + .size = sizeof (psg_text_t), + .name = "Text", + }, +}; + +static const hierarchy_type_t passage_type = { + .num_components = passage_type_count, + .components = passage_type_components, +}; + +VISIBLE int +Passage_IsSpace (const char *text) +{ + if (text[0] == ' ') { + return 1; + } + // 2002;EN SPACE;Zs;0;WS; 0020;;;;N;;;;; + // 2003;EM SPACE;Zs;0;WS; 0020;;;;N;;;;; + // 2004;THREE-PER-EM SPACE;Zs;0;WS; 0020;;;;N;;;;; + // 2005;FOUR-PER-EM SPACE;Zs;0;WS; 0020;;;;N;;;;; + // 2006;SIX-PER-EM SPACE;Zs;0;WS; 0020;;;;N;;;;; + // 2008;PUNCTUATION SPACE;Zs;0;WS; 0020;;;;N;;;;; + // 2009;THIN SPACE;Zs;0;WS; 0020;;;;N;;;;; + // 200A;HAIR SPACE;Zs;0;WS; 0020;;;;N;;;;; + if ((byte)text[0] == 0xe2 && (byte)text[1] == 0x80 + && ((byte)text[2] >= 0x80 && (byte)text[2] < 0x90 + && ((1 << (text[2] & 0xf)) & 0x077c))) { + return 3; + } + // 205F;MEDIUM MATHEMATICAL SPACE;Zs;0;WS; 0020;;;;N;;;;; + if ((byte)text[0] == 0xe2 && (byte)text[1] == 0x81 + && (byte)text[2] == 0x9f) { + return 3; + } + return 0; +} + +static void +add_entity (hierarchy_t *h, uint32_t parent) +{ + uint32_t i = Hierarchy_InsertHierarchy (h, 0, parent, 0); + h->ent[i] = ECS_NewEntity (h->reg); + hierref_t *ref = Ent_AddComponent (h->ent[i], h->href_comp, h->reg); + ref->hierarchy = h; + ref->index = i; +} + +VISIBLE void +Passage_ParseText (passage_t *passage, const char *text) +{ + if (passage->hierarchy) {//FIXME just empty hierarchy + Hierarchy_Delete (passage->hierarchy); + } + if (!*text) { + return; + } + passage->text = text; + + unsigned num_paragraphs = 1; + unsigned num_text_objects = 1; + psg_text_t root_text = {}; + int parsing_space = Passage_IsSpace (text); + for (const char *c = text; *c; c++) { + int size; + if ((size = Passage_IsSpace (c))) { + if (!parsing_space) { + num_text_objects++; + } + parsing_space = 1; + c += size - 1; + } else if (*c == '\n') { + if (c[1]) { + num_paragraphs++; + num_text_objects += !Passage_IsSpace (c + 1); + } + } else { + if (parsing_space) { + num_text_objects++; + } + parsing_space = 0; + } + root_text.size = c - text; + } + passage->hierarchy = Hierarchy_New (passage->reg, + passage->comp_base + passage_href, + &passage_type, 0); + Hierarchy_Reserve (passage->hierarchy, + 1 + num_paragraphs + num_text_objects); +#if 0 + printf ("num_paragraphs %d, num_text_objects %d\n", num_paragraphs, + passage->num_text_objects); +#endif + add_entity (passage->hierarchy, nullent); + for (unsigned i = 0; i < num_paragraphs; i++) { + add_entity (passage->hierarchy, 0); + } + + num_paragraphs = 0; + hierarchy_t *h = passage->hierarchy; + psg_text_t *passage_obj = h->components[passage_type_text_obj]; + psg_text_t *par_obj = &passage_obj[h->childIndex[0]]; + psg_text_t *text_obj = &passage_obj[h->childIndex[1]]; + *par_obj = *text_obj = (psg_text_t) { }; + + *passage_obj = root_text; + add_entity (passage->hierarchy, par_obj - passage_obj); + + parsing_space = Passage_IsSpace (text); + for (const char *c = text; *c; c++) { + int size; + if ((size = Passage_IsSpace (c))) { + if (!parsing_space) { + add_entity (passage->hierarchy, par_obj - passage_obj); + text_obj->size = c - text - text_obj->text; + (++text_obj)->text = c - text; + } + parsing_space = 1; + c += size - 1; + } else if (*c == '\n') { + text_obj->size = c - text - text_obj->text; + par_obj->size = c - text - par_obj->text; + if (c[1]) { + (++par_obj)->text = c + 1 - text; + add_entity (passage->hierarchy, par_obj - passage_obj); + (++text_obj)->text = c + 1 - text; + parsing_space = Passage_IsSpace (c + 1); + } + } else { + if (parsing_space) { + add_entity (passage->hierarchy, par_obj - passage_obj); + text_obj->size = c - text - text_obj->text; + (++text_obj)->text = c - text; + } + parsing_space = 0; + if (!c[1]) { + text_obj->size = c + 1 - text - text_obj->text; + par_obj->size = c + 1 - text - par_obj->text; + passage_obj->size = c + 1 - text - passage_obj->text; + } + } + } +#if 0 + for (uint32_t i = 0; i < h->num_objects; i++) { + psg_text_t *to = &passage_obj[i]; + uint32_t ent = h->ent[i]; + hierref_t *ref = Ent_GetComponent (ent, h->href_comp, reg); + printf ("%3d %8x %3d %4d %4d '%.*s'\n", i, ent, ref->index, + to->text, to->size, to->size, text + to->text); + } +#endif +} + +VISIBLE passage_t * +Passage_New (ecs_system_t passage_sys) +{ + passage_t *passage = malloc (sizeof (passage_t)); + passage->text = 0; + passage->reg = passage_sys.reg; + passage->comp_base = passage_sys.base; + passage->hierarchy = 0; + return passage; +} + +VISIBLE void +Passage_Delete (passage_t *passage) +{ + if (passage->hierarchy) { + Hierarchy_Delete (passage->hierarchy); + } + free (passage); +} diff --git a/libs/ui/test/Makemodule.am b/libs/ui/test/Makemodule.am new file mode 100644 index 000000000..2e9ac62f1 --- /dev/null +++ b/libs/ui/test/Makemodule.am @@ -0,0 +1,32 @@ +libs_ui_tests = \ + libs/ui/test/test-flow \ + libs/ui/test/test-flow-size \ + libs/ui/test/test-passage \ + libs/ui/test/test-txtbuffer \ + libs/ui/test/test-vrect + +TESTS += $(libs_ui_tests) + +XFAIL_TESTS += $(libs_ui_xfail_tests) + +check_PROGRAMS += $(libs_ui_tests) $(libs_ui_xfail_tests) + +libs_ui_test_test_flow_SOURCES=libs/ui/test/test-flow.c +libs_ui_test_test_flow_LDADD=libs/ui/libQFui.la libs/ecs/libQFecs.la +libs_ui_test_test_flow_DEPENDENCIES=libs/ui/libQFui.la + +libs_ui_test_test_flow_size_SOURCES=libs/ui/test/test-flow-size.c +libs_ui_test_test_flow_size_LDADD=libs/ui/libQFui.la libs/ecs/libQFecs.la +libs_ui_test_test_flow_size_DEPENDENCIES=libs/ui/libQFui.la + +libs_ui_test_test_passage_SOURCES=libs/ui/test/test-passage.c +libs_ui_test_test_passage_LDADD=libs/ui/libQFui.la libs/ecs/libQFecs.la +libs_ui_test_test_passage_DEPENDENCIES=libs/ui/libQFui.la libs/ecs/libQFecs.la + +libs_ui_test_test_txtbuffer_SOURCES=libs/ui/test/test-txtbuffer.c +libs_ui_test_test_txtbuffer_LDADD=libs/ui/libQFui.la +libs_ui_test_test_txtbuffer_DEPENDENCIES=libs/ui/libQFui.la + +libs_ui_test_test_vrect_SOURCES=libs/ui/test/test-vrect.c +libs_ui_test_test_vrect_LDADD=libs/ui/libQFui.la +libs_ui_test_test_vrect_DEPENDENCIES=libs/ui/libQFui.la diff --git a/libs/ui/test/test-flow-size.c b/libs/ui/test/test-flow-size.c new file mode 100644 index 000000000..2c6bbf092 --- /dev/null +++ b/libs/ui/test/test-flow-size.c @@ -0,0 +1,322 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#include "QF/ui/view.h" + +static ecs_system_t test_sys; + +enum { + test_href, + + test_comp_count +}; + +static const component_t test_components[] = { + [test_href] = { + .size = sizeof (hierref_t), + .create = 0,//create_href, + .name = "href", + .destroy = Hierref_DestroyComponent, + }, +}; + +typedef struct { + struct { + int xlen, ylen; + }; + int bol_suppress; + struct { + struct { + int xpos, ypos; + }; + struct { + int xrel, yrel; + }; + struct { + int xabs, yabs; + }; + } expect; +} testdata_t; + +#define array_size(array) (sizeof (array) / sizeof(array[0])) + +static testdata_t right_down_views[] = { + {{ 48, 8}, 0, { { 0, 0}, { 0, 0}, { 8, 16} }}, // 0 + {{128, 8}, 0, { { 48, 0}, { 48, 0}, { 56, 16} }}, + {{ 64, 8}, 0, { {176, 0}, {176, 0}, {184, 16} }}, + + {{ 32, 8}, 0, { { 0, 0}, { 0, 8}, { 8, 24} }}, // 3 + {{224, 8}, 0, { { 32, 0}, { 32, 8}, { 40, 24} }}, + + {{ 64, 8}, 0, { { 0, 0}, { 0, 16}, { 8, 32} }}, // 5 + {{ 64, 8}, 0, { { 64, 0}, { 64, 16}, { 72, 32} }}, + {{ 64, 8}, 0, { {128, 0}, {128, 16}, {136, 32} }}, + {{ 32, 8}, 0, { {192, 0}, {192, 16}, {200, 32} }}, + + {{288, 8}, 0, { { 0, 0}, { 0, 24}, { 8, 40} }}, // 9 + + {{ 48, 8}, 0, { { 0, 0}, { 0, 32}, { 8, 48} }}, // 10 + {{128, 8}, 0, { { 48, 0}, { 48, 32}, { 56, 48} }}, + {{ 64, 8}, 0, { {176, 0}, {176, 32}, {184, 48} }}, +}; +#define right_down_count array_size(right_down_views) + +static testdata_t right_up_views[] = { + {{ 48, 8}, 0, { { 0, 0}, { 0, 32}, { 8, 48} }}, // 0 + {{128, 8}, 0, { { 48, 0}, { 48, 32}, { 56, 48} }}, + {{ 64, 8}, 0, { {176, 0}, {176, 32}, {184, 48} }}, + + {{ 32, 8}, 0, { { 0, 0}, { 0, 24}, { 8, 40} }}, // 3 + {{224, 8}, 0, { { 32, 0}, { 32, 24}, { 40, 40} }}, + + {{ 64, 8}, 0, { { 0, 0}, { 0, 16}, { 8, 32} }}, // 5 + {{ 64, 8}, 0, { { 64, 0}, { 64, 16}, { 72, 32} }}, + {{ 64, 8}, 0, { {128, 0}, {128, 16}, {136, 32} }}, + {{ 32, 8}, 0, { {192, 0}, {192, 16}, {200, 32} }}, + + {{288, 8}, 0, { { 0, 0}, { 0, 8}, { 8, 24} }}, // 9 + + {{ 48, 8}, 0, { { 0, 0}, { 0, 0}, { 8, 16} }}, // 10 + {{128, 8}, 0, { { 48, 0}, { 48, 0}, { 56, 16} }}, + {{ 64, 8}, 0, { {176, 0}, {176, 0}, {184, 16} }}, +}; +#define right_up_count array_size(right_up_views) + +static testdata_t left_down_views[] = { + {{ 48, 8}, 0, { {208, 0}, {208, 0}, {216, 16} }}, // 0 + {{128, 8}, 0, { { 80, 0}, { 80, 0}, { 88, 16} }}, + {{ 64, 8}, 0, { { 16, 0}, { 16, 0}, { 24, 16} }}, + + {{ 32, 8}, 0, { {224, 0}, {224, 8}, {232, 24} }}, // 3 + {{224, 8}, 0, { { 0, 0}, { 0, 8}, { 8, 24} }}, + + {{ 64, 8}, 0, { {192, 0}, {192, 16}, {200, 32} }}, // 5 + {{ 64, 8}, 0, { {128, 0}, {128, 16}, {136, 32} }}, + {{ 64, 8}, 0, { { 64, 0}, { 64, 16}, { 72, 32} }}, + {{ 32, 8}, 0, { { 32, 0}, { 32, 16}, { 40, 32} }}, + + {{288, 8}, 0, { {-32, 0}, {-32, 24}, {-24, 40} }}, // 9 + + {{ 48, 8}, 0, { {208, 0}, {208, 32}, {216, 48} }}, // 10 + {{128, 8}, 0, { { 80, 0}, { 80, 32}, { 88, 48} }}, + {{ 64, 8}, 0, { { 16, 0}, { 16, 32}, { 24, 48} }}, +}; +#define left_down_count array_size(left_down_views) + +static testdata_t left_up_views[] = { + {{ 48, 8}, 0, { {208, 0}, {208, 32}, {216, 48} }}, // 0 + {{128, 8}, 0, { { 80, 0}, { 80, 32}, { 88, 48} }}, + {{ 64, 8}, 0, { { 16, 0}, { 16, 32}, { 24, 48} }}, + + {{ 32, 8}, 0, { {224, 0}, {224, 24}, {232, 40} }}, // 3 + {{224, 8}, 0, { { 0, 0}, { 0, 24}, { 8, 40} }}, + + {{ 64, 8}, 0, { {192, 0}, {192, 16}, {200, 32} }}, // 5 + {{ 64, 8}, 0, { {128, 0}, {128, 16}, {136, 32} }}, + {{ 64, 8}, 0, { { 64, 0}, { 64, 16}, { 72, 32} }}, + {{ 32, 8}, 0, { { 32, 0}, { 32, 16}, { 40, 32} }}, + + {{288, 8}, 0, { {-32, 0}, {-32, 8}, {-24, 24} }}, // 9 + + {{ 48, 8}, 0, { {208, 0}, {208, 0}, {216, 16} }}, // 10 + {{128, 8}, 0, { { 80, 0}, { 80, 0}, { 88, 16} }}, + {{ 64, 8}, 0, { { 16, 0}, { 16, 0}, { 24, 16} }}, +}; +#define left_up_count array_size(left_up_views) + +static testdata_t down_right_views[] = { + {{ 8, 48}, 0, { { 0, 0}, { 0, 0}, { 8, 16} }}, // 0 + {{ 8,128}, 0, { { 0, 48}, { 0, 48}, { 8, 64} }}, + {{ 8, 64}, 0, { { 0,176}, { 0,176}, { 8,192} }}, + + {{ 8, 32}, 0, { { 0, 0}, { 8, 0}, { 16, 16} }}, // 3 + {{ 8,224}, 0, { { 0, 32}, { 8, 32}, { 16, 48} }}, + + {{ 8, 64}, 0, { { 0, 0}, { 16, 0}, { 24, 16} }}, // 5 + {{ 8, 64}, 0, { { 0, 64}, { 16, 64}, { 24, 80} }}, + {{ 8, 64}, 0, { { 0,128}, { 16,128}, { 24,144} }}, + {{ 8, 32}, 0, { { 0,192}, { 16,192}, { 24,208} }}, + + {{ 8,288}, 0, { { 0, 0}, { 24, 0}, { 32, 16} }}, // 9 + + {{ 8, 48}, 0, { { 0, 0}, { 32, 0}, { 40, 16} }}, // 10 + {{ 8,128}, 0, { { 0, 48}, { 32, 48}, { 40, 64} }}, + {{ 8, 64}, 0, { { 0,176}, { 32,176}, { 40,192} }}, +}; +#define down_right_count array_size(down_right_views) + +static testdata_t up_right_views[] = { + {{ 8, 48}, 0, { { 0,208}, { 0,208}, { 8,224} }}, // 0 + {{ 8,128}, 0, { { 0, 80}, { 0, 80}, { 8, 96} }}, + {{ 8, 64}, 0, { { 0, 16}, { 0, 16}, { 8, 32} }}, + + {{ 8, 32}, 0, { { 0,224}, { 8,224}, { 16,240} }}, // 3 + {{ 8,224}, 0, { { 0, 0}, { 8, 0}, { 16, 16} }}, + + {{ 8, 64}, 0, { { 0,192}, { 16,192}, { 24,208} }}, // 5 + {{ 8, 64}, 0, { { 0,128}, { 16,128}, { 24,144} }}, + {{ 8, 64}, 0, { { 0, 64}, { 16, 64}, { 24, 80} }}, + {{ 8, 32}, 0, { { 0, 32}, { 16, 32}, { 24, 48} }}, + + {{ 8,288}, 0, { { 0,-32}, { 24,-32}, { 32,-16} }}, // 9 + + {{ 8, 48}, 0, { { 0,208}, { 32,208}, { 40,224} }}, // 10 + {{ 8,128}, 0, { { 0, 80}, { 32, 80}, { 40, 96} }}, + {{ 8, 64}, 0, { { 0, 16}, { 32, 16}, { 40, 32} }}, +}; +#define up_right_count array_size(up_right_views) + +static testdata_t down_left_views[] = { + {{ 8, 48}, 0, { { 0, 0}, { 32, 0}, { 40, 16} }}, // 0 + {{ 8,128}, 0, { { 0, 48}, { 32, 48}, { 40, 64} }}, + {{ 8, 64}, 0, { { 0,176}, { 32,176}, { 40,192} }}, + + {{ 8, 32}, 0, { { 0, 0}, { 24, 0}, { 32, 16} }}, // 3 + {{ 8,224}, 0, { { 0, 32}, { 24, 32}, { 32, 48} }}, + + {{ 8, 64}, 0, { { 0, 0}, { 16, 0}, { 24, 16} }}, // 5 + {{ 8, 64}, 0, { { 0, 64}, { 16, 64}, { 24, 80} }}, + {{ 8, 64}, 0, { { 0,128}, { 16,128}, { 24,144} }}, + {{ 8, 32}, 0, { { 0,192}, { 16,192}, { 24,208} }}, + + {{ 8,288}, 0, { { 0, 0}, { 8, 0}, { 16, 16} }}, // 9 + + {{ 8, 48}, 0, { { 0, 0}, { 0, 0}, { 8, 16} }}, // 10 + {{ 8,128}, 0, { { 0, 48}, { 0, 48}, { 8, 64} }}, + {{ 8, 64}, 0, { { 0,176}, { 0,176}, { 8,192} }}, +}; +#define down_left_count array_size(down_left_views) + +static testdata_t up_left_views[] = { + {{ 8, 48}, 0, { { 0,208}, { 32,208}, { 40,224} }}, // 0 + {{ 8,128}, 0, { { 0, 80}, { 32, 80}, { 40, 96} }}, + {{ 8, 64}, 0, { { 0, 16}, { 32, 16}, { 40, 32} }}, + + {{ 8, 32}, 0, { { 0,224}, { 24,224}, { 32,240} }}, // 3 + {{ 8,224}, 0, { { 0, 0}, { 24, 0}, { 32, 16} }}, + + {{ 8, 64}, 0, { { 0,192}, { 16,192}, { 24,208} }}, // 5 + {{ 8, 64}, 0, { { 0,128}, { 16,128}, { 24,144} }}, + {{ 8, 64}, 0, { { 0, 64}, { 16, 64}, { 24, 80} }}, + {{ 8, 32}, 0, { { 0, 32}, { 16, 32}, { 24, 48} }}, + + {{ 8,288}, 0, { { 0,-32}, { 8,-32}, { 16,-16} }}, // 9 + + {{ 8, 48}, 0, { { 0,208}, { 0,208}, { 8,224} }}, // 10 + {{ 8,128}, 0, { { 0, 80}, { 0, 80}, { 8, 96} }}, + {{ 8, 64}, 0, { { 0, 16}, { 0, 16}, { 8, 32} }}, +}; +#define up_left_count array_size(up_left_views) + +static void +print_view (view_t view) +{ + view_t parent = View_GetParent (view); + view_pos_t pos = View_GetPos (view); + view_pos_t len = View_GetLen (view); + view_pos_t rel = View_GetRel (view); + view_pos_t abs = View_GetAbs (view); + printf ("%s[%3d %3d %3d %3d %3d %3d %3d %3d]\n", + View_Valid (parent) ? " " : "****", + pos.x, pos.y, len.x, len.y, rel.x, rel.y, abs.x, abs.y); +} + +static int +test_flow (testdata_t *child_views, int count, void (flow) (view_t, view_pos_t)) +{ + view_t flow_view = View_New (test_sys, nullview); + View_SetPos (flow_view, 0, 0); + View_SetLen (flow_view, 256, 256); + View_SetGravity (flow_view, grav_northwest); + View_SetOnResize (flow_view, flow); + View_Control (flow_view)->flow_size = 1; + + for (int i = 0; i < count; i++) { + testdata_t *td = &child_views[i]; + view_t child = View_New (test_sys, flow_view); + View_SetPos (child, 0, 0); + View_SetLen (child, td->xlen, td->ylen); + View_SetGravity (child, grav_flow); + View_Control (child)->bol_suppress = td->bol_suppress; + } + + View_SetPos (flow_view, 8, 16); + View_UpdateHierarchy (flow_view); + + int ret = 0; + __auto_type ref = View_GetRef (flow_view); + hierarchy_t *h = ref->hierarchy; + uint32_t *childIndex = h->childIndex; + uint32_t *childCount = h->childCount; + uint32_t *ent = h->ent; + view_pos_t *pos = h->components[view_pos]; + view_pos_t *rel = h->components[view_rel]; + view_pos_t *abs = h->components[view_abs]; + for (uint32_t i = 0; i < childCount[ref->index]; i++) { + testdata_t *td = &child_views[i]; + uint32_t child = childIndex[ref->index] + i; + if (pos[child].x != td->expect.xpos + || pos[child].y != td->expect.ypos + || rel[child].x != td->expect.xrel + || rel[child].y != td->expect.yrel + || abs[child].x != td->expect.xabs + || abs[child].y != td->expect.yabs) { + ret = 1; + printf ("child %d misflowed:\n" + " [%3d %3d %3d %3d %3d %3d]\n", i, + td->expect.xpos, td->expect.ypos, + td->expect.xrel, td->expect.yrel, + td->expect.xabs, td->expect.yabs); + print_view (View_FromEntity (test_sys, ent[child])); + } + } + return ret; +} + +int +main (void) +{ + int ret = 0; + + test_sys.reg = ECS_NewRegistry (); + test_sys.base = ECS_RegisterComponents (test_sys.reg, test_components, + test_comp_count); + ECS_CreateComponentPools (test_sys.reg); + + if (test_flow (right_down_views, right_down_count, view_flow_right_down)) { + printf ("right-down failed\n"); + ret = 1; + } + if (test_flow (right_up_views, right_up_count, view_flow_right_up)) { + printf ("right-up failed\n"); + ret = 1; + } + if (test_flow (left_down_views, left_down_count, view_flow_left_down)) { + printf ("left-down failed\n"); + ret = 1; + } + if (test_flow (left_up_views, left_up_count, view_flow_left_up)) { + printf ("left-up failed\n"); + ret = 1; + } + + if (test_flow (down_right_views, down_right_count, view_flow_down_right)) { + printf ("down-right failed\n"); + ret = 1; + } + if (test_flow (up_right_views, up_right_count, view_flow_up_right)) { + printf ("up-right failed\n"); + ret = 1; + } + if (test_flow (down_left_views, down_left_count, view_flow_down_left)) { + printf ("down-left failed\n"); + ret = 1; + } + if (test_flow (up_left_views, up_left_count, view_flow_up_left)) { + printf ("up-left failed\n"); + ret = 1; + } + return ret; +} diff --git a/libs/ui/test/test-flow.c b/libs/ui/test/test-flow.c new file mode 100644 index 000000000..bbcfdeba2 --- /dev/null +++ b/libs/ui/test/test-flow.c @@ -0,0 +1,322 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#include "QF/ui/view.h" + +static ecs_system_t test_sys; + +enum { + test_href, + + test_comp_count +}; + +static const component_t test_components[] = { + [test_href] = { + .size = sizeof (hierref_t), + .create = 0,//create_href, + .name = "href", + .destroy = Hierref_DestroyComponent, + }, +}; + +typedef struct { + struct { + int xlen, ylen; + }; + int bol_suppress; + struct { + struct { + int xpos, ypos; + }; + struct { + int xrel, yrel; + }; + struct { + int xabs, yabs; + }; + } expect; +} testdata_t; + +#define array_size(array) (sizeof (array) / sizeof(array[0])) + +static testdata_t right_down_views[] = { + {{ 48, 8}, 0, { { 0, 0}, { 0, 0}, { 8, 16} }}, // 0 + {{128, 8}, 0, { { 48, 0}, { 48, 0}, { 56, 16} }}, + {{ 64, 8}, 0, { {176, 0}, {176, 0}, {184, 16} }}, + + {{ 32, 8}, 0, { { 0, 0}, { 0, 8}, { 8, 24} }}, // 3 + {{224, 8}, 0, { { 32, 0}, { 32, 8}, { 40, 24} }}, + + {{ 64, 8}, 0, { { 0, 0}, { 0, 16}, { 8, 32} }}, // 5 + {{ 64, 8}, 0, { { 64, 0}, { 64, 16}, { 72, 32} }}, + {{ 64, 8}, 0, { {128, 0}, {128, 16}, {136, 32} }}, + {{ 32, 8}, 0, { {192, 0}, {192, 16}, {200, 32} }}, + + {{288, 8}, 0, { { 0, 0}, { 0, 24}, { 8, 40} }}, // 9 + + {{ 48, 8}, 0, { { 0, 0}, { 0, 32}, { 8, 48} }}, // 10 + {{128, 8}, 0, { { 48, 0}, { 48, 32}, { 56, 48} }}, + {{ 64, 8}, 0, { {176, 0}, {176, 32}, {184, 48} }}, +}; +#define right_down_count array_size(right_down_views) + +static testdata_t right_up_views[] = { + {{ 48, 8}, 0, { { 0, 0}, { 0,248}, { 8,264} }}, // 0 + {{128, 8}, 0, { { 48, 0}, { 48,248}, { 56,264} }}, + {{ 64, 8}, 0, { {176, 0}, {176,248}, {184,264} }}, + + {{ 32, 8}, 0, { { 0, 0}, { 0,240}, { 8,256} }}, // 3 + {{224, 8}, 0, { { 32, 0}, { 32,240}, { 40,256} }}, + + {{ 64, 8}, 0, { { 0, 0}, { 0,232}, { 8,248} }}, // 5 + {{ 64, 8}, 0, { { 64, 0}, { 64,232}, { 72,248} }}, + {{ 64, 8}, 0, { {128, 0}, {128,232}, {136,248} }}, + {{ 32, 8}, 0, { {192, 0}, {192,232}, {200,248} }}, + + {{288, 8}, 0, { { 0, 0}, { 0,224}, { 8,240} }}, // 9 + + {{ 48, 8}, 0, { { 0, 0}, { 0,216}, { 8,232} }}, // 10 + {{128, 8}, 0, { { 48, 0}, { 48,216}, { 56,232} }}, + {{ 64, 8}, 0, { {176, 0}, {176,216}, {184,232} }}, +}; +#define right_up_count array_size(right_up_views) + +static testdata_t left_down_views[] = { + {{ 48, 8}, 0, { {208, 0}, {208, 0}, {216, 16} }}, // 0 + {{128, 8}, 0, { { 80, 0}, { 80, 0}, { 88, 16} }}, + {{ 64, 8}, 0, { { 16, 0}, { 16, 0}, { 24, 16} }}, + + {{ 32, 8}, 0, { {224, 0}, {224, 8}, {232, 24} }}, // 3 + {{224, 8}, 0, { { 0, 0}, { 0, 8}, { 8, 24} }}, + + {{ 64, 8}, 0, { {192, 0}, {192, 16}, {200, 32} }}, // 5 + {{ 64, 8}, 0, { {128, 0}, {128, 16}, {136, 32} }}, + {{ 64, 8}, 0, { { 64, 0}, { 64, 16}, { 72, 32} }}, + {{ 32, 8}, 0, { { 32, 0}, { 32, 16}, { 40, 32} }}, + + {{288, 8}, 0, { {-32, 0}, {-32, 24}, {-24, 40} }}, // 9 + + {{ 48, 8}, 0, { {208, 0}, {208, 32}, {216, 48} }}, // 10 + {{128, 8}, 0, { { 80, 0}, { 80, 32}, { 88, 48} }}, + {{ 64, 8}, 0, { { 16, 0}, { 16, 32}, { 24, 48} }}, +}; +#define left_down_count array_size(left_down_views) + +static testdata_t left_up_views[] = { + {{ 48, 8}, 0, { {208, 0}, {208,248}, {216,264} }}, // 0 + {{128, 8}, 0, { { 80, 0}, { 80,248}, { 88,264} }}, + {{ 64, 8}, 0, { { 16, 0}, { 16,248}, { 24,264} }}, + + {{ 32, 8}, 0, { {224, 0}, {224,240}, {232,256} }}, // 3 + {{224, 8}, 0, { { 0, 0}, { 0,240}, { 8,256} }}, + + {{ 64, 8}, 0, { {192, 0}, {192,232}, {200,248} }}, // 5 + {{ 64, 8}, 0, { {128, 0}, {128,232}, {136,248} }}, + {{ 64, 8}, 0, { { 64, 0}, { 64,232}, { 72,248} }}, + {{ 32, 8}, 0, { { 32, 0}, { 32,232}, { 40,248} }}, + + {{288, 8}, 0, { {-32, 0}, {-32,224}, {-24,240} }}, // 9 + + {{ 48, 8}, 0, { {208, 0}, {208,216}, {216,232} }}, // 10 + {{128, 8}, 0, { { 80, 0}, { 80,216}, { 88,232} }}, + {{ 64, 8}, 0, { { 16, 0}, { 16,216}, { 24,232} }}, +}; +#define left_up_count array_size(left_up_views) + +static testdata_t down_right_views[] = { + {{ 8, 48}, 0, { { 0, 0}, { 0, 0}, { 8, 16} }}, // 0 + {{ 8,128}, 0, { { 0, 48}, { 0, 48}, { 8, 64} }}, + {{ 8, 64}, 0, { { 0,176}, { 0,176}, { 8,192} }}, + + {{ 8, 32}, 0, { { 0, 0}, { 8, 0}, { 16, 16} }}, // 3 + {{ 8,224}, 0, { { 0, 32}, { 8, 32}, { 16, 48} }}, + + {{ 8, 64}, 0, { { 0, 0}, { 16, 0}, { 24, 16} }}, // 5 + {{ 8, 64}, 0, { { 0, 64}, { 16, 64}, { 24, 80} }}, + {{ 8, 64}, 0, { { 0,128}, { 16,128}, { 24,144} }}, + {{ 8, 32}, 0, { { 0,192}, { 16,192}, { 24,208} }}, + + {{ 8,288}, 0, { { 0, 0}, { 24, 0}, { 32, 16} }}, // 9 + + {{ 8, 48}, 0, { { 0, 0}, { 32, 0}, { 40, 16} }}, // 10 + {{ 8,128}, 0, { { 0, 48}, { 32, 48}, { 40, 64} }}, + {{ 8, 64}, 0, { { 0,176}, { 32,176}, { 40,192} }}, +}; +#define down_right_count array_size(down_right_views) + +static testdata_t up_right_views[] = { + {{ 8, 48}, 0, { { 0,208}, { 0,208}, { 8,224} }}, // 0 + {{ 8,128}, 0, { { 0, 80}, { 0, 80}, { 8, 96} }}, + {{ 8, 64}, 0, { { 0, 16}, { 0, 16}, { 8, 32} }}, + + {{ 8, 32}, 0, { { 0,224}, { 8,224}, { 16,240} }}, // 3 + {{ 8,224}, 0, { { 0, 0}, { 8, 0}, { 16, 16} }}, + + {{ 8, 64}, 0, { { 0,192}, { 16,192}, { 24,208} }}, // 5 + {{ 8, 64}, 0, { { 0,128}, { 16,128}, { 24,144} }}, + {{ 8, 64}, 0, { { 0, 64}, { 16, 64}, { 24, 80} }}, + {{ 8, 32}, 0, { { 0, 32}, { 16, 32}, { 24, 48} }}, + + {{ 8,288}, 0, { { 0,-32}, { 24,-32}, { 32,-16} }}, // 9 + + {{ 8, 48}, 0, { { 0,208}, { 32,208}, { 40,224} }}, // 10 + {{ 8,128}, 0, { { 0, 80}, { 32, 80}, { 40, 96} }}, + {{ 8, 64}, 0, { { 0, 16}, { 32, 16}, { 40, 32} }}, +}; +#define up_right_count array_size(up_right_views) + +static testdata_t down_left_views[] = { + {{ 8, 48}, 0, { { 0, 0}, {248, 0}, {256, 16} }}, // 0 + {{ 8,128}, 0, { { 0, 48}, {248, 48}, {256, 64} }}, + {{ 8, 64}, 0, { { 0,176}, {248,176}, {256,192} }}, + + {{ 8, 32}, 0, { { 0, 0}, {240, 0}, {248, 16} }}, // 3 + {{ 8,224}, 0, { { 0, 32}, {240, 32}, {248, 48} }}, + + {{ 8, 64}, 0, { { 0, 0}, {232, 0}, {240, 16} }}, // 5 + {{ 8, 64}, 0, { { 0, 64}, {232, 64}, {240, 80} }}, + {{ 8, 64}, 0, { { 0,128}, {232,128}, {240,144} }}, + {{ 8, 32}, 0, { { 0,192}, {232,192}, {240,208} }}, + + {{ 8,288}, 0, { { 0, 0}, {224, 0}, {232, 16} }}, // 9 + + {{ 8, 48}, 0, { { 0, 0}, {216, 0}, {224, 16} }}, // 10 + {{ 8,128}, 0, { { 0, 48}, {216, 48}, {224, 64} }}, + {{ 8, 64}, 0, { { 0,176}, {216,176}, {224,192} }}, +}; +#define down_left_count array_size(down_left_views) + +static testdata_t up_left_views[] = { + {{ 8, 48}, 0, { { 0,208}, {248,208}, {256,224} }}, // 0 + {{ 8,128}, 0, { { 0, 80}, {248, 80}, {256, 96} }}, + {{ 8, 64}, 0, { { 0, 16}, {248, 16}, {256, 32} }}, + + {{ 8, 32}, 0, { { 0,224}, {240,224}, {248,240} }}, // 3 + {{ 8,224}, 0, { { 0, 0}, {240, 0}, {248, 16} }}, + + {{ 8, 64}, 0, { { 0,192}, {232,192}, {240,208} }}, // 5 + {{ 8, 64}, 0, { { 0,128}, {232,128}, {240,144} }}, + {{ 8, 64}, 0, { { 0, 64}, {232, 64}, {240, 80} }}, + {{ 8, 32}, 0, { { 0, 32}, {232, 32}, {240, 48} }}, + + {{ 8,288}, 0, { { 0,-32}, {224,-32}, {232,-16} }}, // 9 + + {{ 8, 48}, 0, { { 0,208}, {216,208}, {224,224} }}, // 10 + {{ 8,128}, 0, { { 0, 80}, {216, 80}, {224, 96} }}, + {{ 8, 64}, 0, { { 0, 16}, {216, 16}, {224, 32} }}, +}; +#define up_left_count array_size(up_left_views) + +static void +print_view (view_t view) +{ + view_t parent = View_GetParent (view); + view_pos_t pos = View_GetPos (view); + view_pos_t len = View_GetLen (view); + view_pos_t rel = View_GetRel (view); + view_pos_t abs = View_GetAbs (view); + printf ("%s[%3d %3d %3d %3d %3d %3d %3d %3d]\n", + View_Valid (parent) ? " " : "****", + pos.x, pos.y, len.x, len.y, rel.x, rel.y, abs.x, abs.y); +} + +static int +test_flow (testdata_t *child_views, int count, + void (*flow) (view_t, view_pos_t)) +{ + view_t flow_view = View_New (test_sys, nullview); + View_SetPos (flow_view, 0, 0); + View_SetLen (flow_view, 256, 256); + View_SetGravity (flow_view, grav_northwest); + View_SetOnResize (flow_view, flow); + + for (int i = 0; i < count; i++) { + testdata_t *td = &child_views[i]; + view_t child = View_New (test_sys, flow_view); + View_SetPos (child, 0, 0); + View_SetLen (child, td->xlen, td->ylen); + View_SetGravity (child, grav_flow); + View_Control (child)->bol_suppress = td->bol_suppress; + } + + View_SetPos (flow_view, 8, 16); + View_UpdateHierarchy (flow_view); + + int ret = 0; + __auto_type ref = View_GetRef (flow_view); + hierarchy_t *h = ref->hierarchy; + uint32_t *childIndex = h->childIndex; + uint32_t *childCount = h->childCount; + uint32_t *ent = h->ent; + view_pos_t *pos = h->components[view_pos]; + view_pos_t *rel = h->components[view_rel]; + view_pos_t *abs = h->components[view_abs]; + for (uint32_t i = 0; i < childCount[ref->index]; i++) { + testdata_t *td = &child_views[i]; + uint32_t child = childIndex[ref->index] + i; + if (pos[child].x != td->expect.xpos + || pos[child].y != td->expect.ypos + || rel[child].x != td->expect.xrel + || rel[child].y != td->expect.yrel + || abs[child].x != td->expect.xabs + || abs[child].y != td->expect.yabs) { + ret = 1; + printf ("child %d misflowed:\n" + " [%3d %3d %3d %3d %3d %3d]\n", i, + td->expect.xpos, td->expect.ypos, + td->expect.xrel, td->expect.yrel, + td->expect.xabs, td->expect.yabs); + print_view (View_FromEntity (test_sys, ent[child])); + } + } + return ret; +} + +int +main (void) +{ + int ret = 0; + + test_sys.reg = ECS_NewRegistry (); + test_sys.base = ECS_RegisterComponents (test_sys.reg, test_components, + test_comp_count); + ECS_CreateComponentPools (test_sys.reg); + + if (test_flow (right_down_views, right_down_count, view_flow_right_down)) { + printf ("right-down failed\n"); + ret = 1; + } + if (test_flow (right_up_views, right_up_count, view_flow_right_up)) { + printf ("right-up failed\n"); + ret = 1; + } + if (test_flow (left_down_views, left_down_count, view_flow_left_down)) { + printf ("left-down failed\n"); + ret = 1; + } + if (test_flow (left_up_views, left_up_count, view_flow_left_up)) { + printf ("left-up failed\n"); + ret = 1; + } + + if (test_flow (down_right_views, down_right_count, view_flow_down_right)) { + printf ("down-right failed\n"); + ret = 1; + } + if (test_flow (up_right_views, up_right_count, view_flow_up_right)) { + printf ("up-right failed\n"); + ret = 1; + } + if (test_flow (down_left_views, down_left_count, view_flow_down_left)) { + printf ("down-left failed\n"); + ret = 1; + } + if (test_flow (up_left_views, up_left_count, view_flow_up_left)) { + printf ("up-left failed\n"); + ret = 1; + } + return ret; +} diff --git a/libs/ui/test/test-passage.c b/libs/ui/test/test-passage.c new file mode 100644 index 000000000..74c917160 --- /dev/null +++ b/libs/ui/test/test-passage.c @@ -0,0 +1,132 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#include "QF/ui/view.h" +#include "QF/ui/passage.h" +#include "QF/ecs/component.h" + +static const char test_text[] = { + "Guarding the entrance to the Grendal " + "Gorge is the Shadow Gate, a small keep " + "and monastary which was once the home " + "of the Shadow cult.\n" + " For years the Shadow Gate existed in " + "obscurity but after the cult discovered " + "the \u00c2\u00ec\u00e1\u00e3\u00eb\u2002\u00c7\u00e1\u00f4\u00e5 " + "in the caves below the empire took notice. " + "A batallion of Imperial Knights were " + "sent to the gate to destroy the cult " + "and claim the artifact for the King.\nasdf", +}; + +static int __attribute__((pure)) +check_non_space (const char *text, psg_text_t *to) +{ + int size; + + for (uint32_t offs = 0; offs < to->size; offs += size) { + if (!(size = Passage_IsSpace (text + to->text + offs))) { + return 1; + } + } + return 0; +} + +static int __attribute__((pure)) +check_space_or_nl (const char *text, psg_text_t *to) +{ + for (uint32_t offs = 0; offs < to->size; offs++) { + if (text[to->text + offs] == '\n' + || Passage_IsSpace (text + to->text + offs)) { + return 1; + } + } + return 0; +} + +int +main (void) +{ + int ret = 0; + ecs_system_t psg_sys = { + .reg = ECS_NewRegistry (), + .base = ECS_RegisterComponents (psg_sys.reg, passage_components, + passage_comp_count), + }; + ECS_CreateComponentPools (psg_sys.reg); + + passage_t *passage = Passage_New (psg_sys); + Passage_ParseText (passage, test_text); + if (passage->hierarchy->childCount[0] != 3) { + ret = 1; + printf ("incorrect number of paragraphs: %d\n", + passage->hierarchy->childCount[0]); + } + if (passage->hierarchy->num_objects != 144) { + ret = 1; + printf ("incorrect number of text objects: %d\n", + passage->hierarchy->num_objects); + } + if (passage->hierarchy->childCount[1] != 49) { + ret = 1; + printf ("incorrect number of text objects in first paragraph: %d\n", + passage->hierarchy->childCount[1]); + } + if (passage->hierarchy->childCount[2] != 90) { + ret = 1; + printf ("incorrect number of text objects in second paragraph: %d\n", + passage->hierarchy->childCount[2]); + } + if (passage->hierarchy->childCount[3] != 1) { + ret = 1; + printf ("incorrect number of text objects in third paragraph: %d\n", + passage->hierarchy->childCount[3]); + } + uint32_t *childIndex = passage->hierarchy->childIndex; + psg_text_t *text_objs = passage->hierarchy->components[0]; + psg_text_t *to = &text_objs[childIndex[2] + 0]; + if (to->size != 2 && (passage->text[to->text] != ' ' + && passage->text[to->text + 1] != ' ')) { + ret = 1; + printf ("second paragram does not begin with double space: %d '%.*s'\n", + to->size, to->size, passage->text + to->text); + } + //if (text_view->bol_suppress) { + // ret = 1; + // printf ("second paragram indent suppressed\n"); + //} + for (uint32_t i = 0; i < passage->hierarchy->childCount[0]; i++) { + for (uint32_t j = 0; j < passage->hierarchy->childCount[1 + i]; j++) { + psg_text_t *to = &text_objs[childIndex[1 + i] + j]; + unsigned is_space = Passage_IsSpace (passage->text + to->text); + if (i == 1 && j == 0) { + // second paragraph indent, tested above + continue; + } + //if ((!!is_space) != text_view->bol_suppress) { + // ret = 1; + // printf ("text/suppress mismatch %d [%d '%.*s'] %d %d\n", + // text_view->bol_suppress, to->size, to->size, + // passage->text + to->text, i, j); + //} + if (is_space) { + if (!check_non_space (passage->text, to)) { + continue; + } + } else { + if (!check_space_or_nl (passage->text, to)) { + continue; + } + } + ret = 1; + printf ("mixed space/text/\\n [%d '%.*s'] %d %d\n", + to->size, to->size, passage->text + to->text, i, j); + } + } + Passage_Delete (passage); + + ECS_DelRegistry (psg_sys.reg); + return ret; +} diff --git a/libs/ui/test/test-txtbuffer.c b/libs/ui/test/test-txtbuffer.c new file mode 100644 index 000000000..81e24f621 --- /dev/null +++ b/libs/ui/test/test-txtbuffer.c @@ -0,0 +1,240 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include + +#include "QF/ui/txtbuffer.h" + +static size_t +check_text_ptr (txtbuffer_t *buffer, size_t offset, const char *txt, + size_t length) +{ + return buffer->text != 0; +} + +static size_t +get_textSize (txtbuffer_t *buffer, size_t offset, const char *txt, + size_t length) +{ + return buffer->textSize; +} + +static size_t +get_bufferSize (txtbuffer_t *buffer, size_t offset, const char *txt, + size_t length) +{ + return buffer->bufferSize; +} + +static size_t +get_gapOffset (txtbuffer_t *buffer, size_t offset, const char *txt, + size_t length) +{ + return buffer->gapOffset; +} + +static size_t +get_gapSize (txtbuffer_t *buffer, size_t offset, const char *txt, + size_t length) +{ + return buffer->gapSize; +} + +static size_t +insert_text (txtbuffer_t *buffer, size_t offset, const char *txt, + size_t length) +{ + if (TextBuffer_InsertAt (buffer, offset, txt, length)) { + memset (buffer->text + buffer->gapOffset, 0xff, buffer->gapSize); + return 1; + } + return 0; +} + +static size_t +delete_text (txtbuffer_t *buffer, size_t offset, const char *txt, + size_t length) +{ + if (TextBuffer_DeleteAt (buffer, offset, length)) { + memset (buffer->text + buffer->gapOffset, 0xff, buffer->gapSize); + return 1; + } + return 0; +} + +static size_t +destroy_buffer (txtbuffer_t *buffer, size_t offset, const char *txt, + size_t length) +{ + TextBuffer_Destroy (buffer); + return 0; +} + +typedef size_t (*test_func) (txtbuffer_t *buffer, size_t offset, + const char *text, size_t length); + +static const char test_text[] = { + "Guarding the entrance to the Grendal\n" + "Gorge is the Shadow Gate, a small keep\n" + "and monastary which was once the home\n" + "of the Shadow cult.\n\n" + "For years the Shadow Gate existed in\n" + "obscurity but after the cult discovered\n" + "the \3023\354\341\343\353\240\307\341\364\345 in the caves below\n" + "the empire took notice.\n" + "A batallion of Imperial Knights were\n" + "sent to the gate to destroy the cult\n" + "and claim the artifact for the King.", +}; +static const char empty[TXTBUFFER_GROW] = { }; + +static size_t +compare_text (txtbuffer_t *buffer, size_t offset, const char *txt, + size_t length) +{ + //printf("%zd %ld %zd\n", offset, txt - test_text, length); + size_t res = memcmp (buffer->text + offset, txt, length) == 0; + if (!res) { + for (size_t i = 0; i < length; i++) { + //printf ("%02x %02x\n", txt[i] & 0xff, buffer->text[offset + i] & 0xff); + } + } + return res; +} + +#define txtsize (sizeof (test_text) - 1) +#define txtsize0 txtsize +#define bufsize0 (TXTBUFFER_GROW) +#define gapoffs0 txtsize0 +#define gapsize0 (bufsize0 - txtsize0) +#define subtxtlen 200 +#define suboffs 200 +#define txtsize1 (txtsize0 + subtxtlen) +#define bufsize1 (TXTBUFFER_GROW) +#define gapoffs1 (suboffs + subtxtlen) +#define gapsize1 (bufsize1 - txtsize1) +#define deltxtlen 350 +#define deloffs 150 +#define txtsize2 (txtsize + subtxtlen - deltxtlen) +#define bufsize2 (TXTBUFFER_GROW) +#define gapoffs2 (deloffs) +#define gapsize2 (bufsize2 - txtsize2) +#define deltxtrem (txtsize2 - gapoffs2) +#define emptyoffs 370 +#define txtsize3 (txtsize + sizeof (empty)) +#define bufsize3 (2 * TXTBUFFER_GROW) +#define gapoffs3 (emptyoffs + sizeof (empty)) +#define gapsize3 (bufsize3 - txtsize3) +#define txtsize4 (txtsize + 2 * sizeof (empty)) +#define bufsize4 (3 * TXTBUFFER_GROW) +#define gapoffs4 (emptyoffs + sizeof (empty)) +#define gapsize4 (bufsize4 - txtsize4) + +struct { + test_func test; + size_t offset_param; + const char *text_param; + size_t length_param; + int test_expect; +} tests[] = { + { check_text_ptr, 0, 0, 0, 0 }, + { get_textSize, 0, 0, 0, 0 }, + { get_bufferSize, 0, 0, 0, 0 }, + { get_gapOffset, 0, 0, 0, 0 }, + { get_gapSize, 0, 0, 0, 0 }, + { insert_text, 0, test_text, txtsize, 1 }, + { get_textSize, 0, 0, 0, txtsize0 }, + { get_bufferSize, 0, 0, 0, bufsize0 }, + { get_gapOffset, 0, 0, 0, gapoffs0 }, + { get_gapSize, 0, 0, 0, gapsize0 }, + { compare_text, 0, test_text, txtsize, 1 }, + { insert_text, suboffs, test_text, subtxtlen, 1 }, + { get_textSize, 0, 0, 0, txtsize1 }, + { get_bufferSize, 0, 0, 0, bufsize1 }, + { get_gapOffset, 0, 0, 0, gapoffs1 }, + { get_gapSize, 0, 0, 0, gapsize1 }, + { compare_text, 0, test_text, txtsize, 0 }, + { compare_text, 0, test_text, suboffs, 1 }, + { compare_text, suboffs, test_text, subtxtlen, 1 }, + { compare_text, gapoffs1 + gapsize1, test_text + subtxtlen, txtsize - subtxtlen, 1 }, + { delete_text, deloffs, 0, deltxtlen, 1 }, + { get_textSize, 0, 0, 0, txtsize2 }, + { get_bufferSize, 0, 0, 0, bufsize2 }, + { get_gapOffset, 0, 0, 0, gapoffs2 }, + { get_gapSize, 0, 0, 0, gapsize2 }, + { compare_text, 0, test_text, suboffs, 0 }, + { compare_text, suboffs, test_text, subtxtlen, 0 }, + { compare_text, gapoffs1 + gapsize1, test_text + subtxtlen, txtsize - subtxtlen, 0 }, + { compare_text, 0, test_text, deloffs, 1 }, + { compare_text, gapoffs2 + gapsize2, test_text + txtsize - deltxtrem, deltxtrem, 1 }, + { compare_text, gapoffs2 + gapsize2 - 1, test_text + txtsize - deltxtrem - 1, 1, 0 }, + { delete_text, 0, 0, -1, 1 }, + { check_text_ptr, 0, 0, 0, 1 }, + { get_textSize, 0, 0, 0, 0 }, + { get_bufferSize, 0, 0, 0, bufsize2 }, + { get_gapOffset, 0, 0, 0, 0 }, + { get_gapSize, 0, 0, 0, bufsize2 }, + { insert_text, 1, test_text, txtsize, 0 }, + { get_textSize, 0, 0, 0, 0 }, + { get_bufferSize, 0, 0, 0, bufsize2 }, + { get_gapOffset, 0, 0, 0, 0 }, + { get_gapSize, 0, 0, 0, bufsize2 }, + { insert_text, 0, test_text, txtsize, 1 }, + { insert_text, 300, 0, 0, 1 }, + { get_textSize, 0, 0, 0, txtsize0 }, + { get_bufferSize, 0, 0, 0, bufsize2 }, + { get_gapOffset, 0, 0, 0, 300 }, + { get_gapSize, 0, 0, 0, gapsize0 }, + { compare_text, 0, test_text, 300, 1 }, + { compare_text, 300, test_text + 300, 1, 0 }, + { compare_text, 300 + gapsize0, test_text + 300, txtsize - 300, 1 }, + { compare_text, 300 + gapsize0 - 1, test_text + 300 - 1, 1, 0 }, + { insert_text, 350, 0, 0, 1 }, + { compare_text, (emptyoffs - 20) + gapsize0, test_text + (emptyoffs - 20), txtsize - (emptyoffs - 20), 1 }, + { get_textSize, 0, 0, 0, txtsize0 }, + { get_bufferSize, 0, 0, 0, bufsize2 }, + { get_gapOffset, 0, 0, 0, (emptyoffs - 20) }, + { get_gapSize, 0, 0, 0, gapsize0 }, + { insert_text, emptyoffs, empty, sizeof (empty), 1 }, + { get_textSize, 0, 0, 0, txtsize3 }, + { get_bufferSize, 0, 0, 0, bufsize3 }, + { get_gapOffset, 0, 0, 0, gapoffs3 }, + { get_gapSize, 0, 0, 0, gapsize3 }, + { insert_text, emptyoffs, empty, sizeof (empty), 1 }, + { get_textSize, 0, 0, 0, txtsize4 }, + { get_bufferSize, 0, 0, 0, bufsize4 }, + { get_gapOffset, 0, 0, 0, gapoffs4 }, + { get_gapSize, 0, 0, 0, gapsize4 }, + { destroy_buffer, 0, 0, 0, 0 }, +}; +#define num_tests (sizeof (tests) / sizeof (tests[0])) +int test_start_line = __LINE__ - num_tests - 2; + +int +main (int argc, const char **argv) +{ + size_t i; + int res = 0; + + txtbuffer_t *buffer = TextBuffer_Create (); + + for (i = 0; i < num_tests; i++) { + if (tests[i].test) { + int test_res; + test_res = tests[i].test (buffer, + tests[i].offset_param, + tests[i].text_param, + tests[i].length_param); + if (test_res != tests[i].test_expect) { + res |= 1; + printf ("test %d (line %d) failed\n", (int) i, + (int) i + test_start_line); + printf ("expect: %d\n", tests[i].test_expect); + printf ("got : %d\n", test_res); + continue; + } + } + } + return res; +} diff --git a/libs/util/test/test-vrect.c b/libs/ui/test/test-vrect.c similarity index 99% rename from libs/util/test/test-vrect.c rename to libs/ui/test/test-vrect.c index b1ba859bc..6adc4452e 100644 --- a/libs/util/test/test-vrect.c +++ b/libs/ui/test/test-vrect.c @@ -3,7 +3,7 @@ #endif #include -#include "QF/vrect.h" +#include "QF/ui/vrect.h" #define VR(x,y,w,h) {x,y,w,h,0} #define VRn(x,y,w,h,n) {x,y,w,h,n} @@ -134,7 +134,7 @@ print_rects (vrect_t *rect) printf ("\n"); } -static int +static __attribute__((pure)) int compare_rects (vrect_t *r1, vrect_t *r2) { if (!r1 && !r2) diff --git a/libs/ui/text.c b/libs/ui/text.c new file mode 100644 index 000000000..96abcc85c --- /dev/null +++ b/libs/ui/text.c @@ -0,0 +1,337 @@ +/* + text.c + + Font text management + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/8/26 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#include "QF/mathlib.h" +#include "QF/quakefs.h" +#include "QF/sys.h" + +#include "QF/ui/font.h" +#include "QF/ui/passage.h" +#include "QF/ui/text.h" +#include "QF/ui/view.h" + +#include "compat.h" + +static void +text_passage_glyphs_destroy (void *_glyphset) +{ + glyphset_t *glyphset = _glyphset; + free (glyphset->glyphs); +} + +static void +text_features_create (void *_features) +{ + featureset_t *features = _features; + *features = (featureset_t) DARRAY_STATIC_INIT (4); +} + +static void +text_features_destroy (void *_features) +{ + featureset_t *features = _features; + DARRAY_CLEAR (features); +} + +const component_t text_components[text_comp_count] = { + [text_passage_glyphs] = { + .size = sizeof (glyphset_t), + .name = "passage glyphs", + .destroy = text_passage_glyphs_destroy, + }, + [text_glyphs] = { + .size = sizeof (glyphref_t), + .name = "glyphs", + }, + [text_script] = { + .size = sizeof (script_component_t), + .name = "script", + }, + [text_font] = { + .size = sizeof (font_t *), + .name = "font", + }, + [text_features] = { + .size = sizeof (featureset_t), + .name = "features", + .create = text_features_create, + .destroy = text_features_destroy, + }, +}; + +typedef struct glyphnode_s { + struct glyphnode_s *next; + uint32_t ent; + uint32_t count; + glyphobj_t *glyphs; + int mins[2]; + int maxs[2]; +} glyphnode_t; + +static view_resize_f text_flow_funcs[] = { + [text_right_down] = view_flow_right_down, + [text_left_down] = view_flow_left_down, + [text_down_right] = view_flow_down_right, + [text_up_right] = view_flow_up_right, + [text_right_up] = view_flow_right_up, + [text_left_up] = view_flow_left_up, + [text_down_left] = view_flow_down_left, + [text_up_left] = view_flow_up_left, +}; + +view_t +Text_View (ecs_system_t viewsys, font_t *font, passage_t *passage) +{ + ecs_registry_t *reg = passage->reg; + hierarchy_t *h = passage->hierarchy; + psg_text_t *text_objects = h->components[passage_type_text_obj]; + glyphnode_t *glyph_nodes = 0; + glyphnode_t **head = &glyph_nodes; + + hb_font_t *fnt = hb_ft_font_create (font->face, 0); + hb_buffer_t *buffer = hb_buffer_create (); + hb_buffer_allocation_successful (buffer); + + hb_script_t psg_script = HB_SCRIPT_LATIN; + text_dir_e psg_direction = text_right_down; + hb_language_t psg_language = hb_language_from_string ("en", 2); + featureset_t passage_features = DARRAY_STATIC_INIT (0); + featureset_t *psg_features = &passage_features; + + uint32_t glyph_count = 0; + + // passages have only the root, paragraphs, and text objects in their + // hierarchies + for (uint32_t i = 0; i < h->childCount[0]; i++) { + uint32_t paragraph = h->childIndex[0] + i; + uint32_t para_ent = h->ent[paragraph]; + + hb_script_t para_script = psg_script; + text_dir_e para_direction = psg_direction; + hb_language_t para_language = psg_language; + featureset_t *para_features = psg_features;; + + if (Ent_HasComponent (para_ent, text_script, reg)) { + script_component_t *s = Ent_GetComponent (para_ent, text_script, + reg); + para_script = s->script; + para_language = s->language; + para_direction = s->direction; + } + if (Ent_HasComponent (para_ent, text_features, reg)) { + para_features = Ent_GetComponent (para_ent, text_features, reg); + } + for (uint32_t j = 0; j < h->childCount[paragraph]; j++) { + uint32_t textind = h->childIndex[paragraph] + j; + uint32_t text_ent = h->ent[textind]; + psg_text_t *textobj = &text_objects[textind]; + const char *str = passage->text + textobj->text; + uint32_t len = textobj->size; + + hb_script_t txt_script = para_script; + text_dir_e txt_direction = para_direction; + hb_language_t txt_language = para_language; + featureset_t *txt_features = para_features;; + + if (Ent_HasComponent (text_ent, text_script, reg)) { + script_component_t *s = Ent_GetComponent (text_ent, text_script, + reg); + txt_script = s->script; + txt_language = s->language; + txt_direction = s->direction; + } + if (Ent_HasComponent (text_ent, text_features, reg)) { + txt_features = Ent_GetComponent (text_ent, text_features, reg); + } + + hb_buffer_reset (buffer); + hb_buffer_set_direction (buffer, txt_direction | HB_DIRECTION_LTR); + hb_buffer_set_script (buffer, txt_script); + hb_buffer_set_language (buffer, txt_language); + hb_buffer_add_utf8 (buffer, str, len, 0, len); + hb_shape (fnt, buffer, txt_features->a, txt_features->size); + + unsigned c; + __auto_type glyphInfo = hb_buffer_get_glyph_infos (buffer, &c); + __auto_type glyphPos = hb_buffer_get_glyph_positions (buffer, &c); + + *head = alloca (sizeof (glyphnode_t)); + **head = (glyphnode_t) { + .ent = h->ent[textind], + .count = c, + .glyphs = alloca (c * sizeof (glyphobj_t)), + .mins = { 0, 0 }, + .maxs = { INT32_MIN, INT32_MIN }, + }; + glyph_count += c; + + int x = 0, y = 0; + for (unsigned k = 0; k < c; k++) { + uint32_t glyphid = glyphInfo[k].codepoint; + vec2i_t bearing = font->glyph_bearings[glyphid]; + + int xp = glyphPos[k].x_offset / 64; + int yp = glyphPos[k].y_offset / 64; + int xa = glyphPos[k].x_advance / 64; + int ya = glyphPos[k].y_advance / 64; + xp += bearing[0]; + yp += bearing[1]; + int xm = xp + font->glyph_rects[glyphid].width; + int ym = yp - font->glyph_rects[glyphid].height; + + if (xm - xp == 0) { + xm = xa; + } + if (ym - yp == 0) { + ym = ya; + } + (*head)->mins[0] = min ((*head)->mins[0], xp + x); + (*head)->mins[1] = min ((*head)->mins[1], ym + y); + (*head)->maxs[0] = max ((*head)->maxs[0], xm + x); + (*head)->maxs[1] = max ((*head)->maxs[1], yp + y); + + (*head)->glyphs[k] = (glyphobj_t) { + .glyphid = glyphid, + .x = x + xp, + .y = y - yp, + .fontid = font->fontid, + }; + x += xa; + y += ya; + } + + head = &(*head)->next; + } + } + view_t passage_view = View_AddToEntity (h->ent[0], viewsys, nullview); + glyphref_t passage_ref = {}; + glyphobj_t *glyphs = malloc (glyph_count * sizeof (glyphobj_t)); + glyphnode_t *g = glyph_nodes; + // paragraph flow + int psg_vertical = !!(psg_direction & 2); + for (uint32_t i = 0; i < h->childCount[0]; i++) { + uint32_t paragraph = h->childIndex[0] + i; + view_t paraview = View_AddToEntity (h->ent[paragraph], viewsys, + passage_view); + glyphref_t pararef = { .start = passage_ref.count }; + for (uint32_t j = 0; j < h->childCount[paragraph]; j++, g = g->next) { + uint32_t to = h->childIndex[paragraph] + j; + view_t textview = View_AddToEntity (h->ent[to], viewsys, + paraview); + glyphref_t glyph_ref = { + .start = passage_ref.count, + .count = g->count, + }; + for (uint32_t k = 0; k < g->count; k++) { + glyphobj_t *go = g->glyphs + k; + glyphs[passage_ref.count + k] = (glyphobj_t) { + .glyphid = go->glyphid, + .x = go->x + g->mins[0], + .y = go->y + g->maxs[1], + .fontid = go->fontid, + }; + } + Ent_SetComponent (textview.id, text_glyphs, reg, &glyph_ref); + View_SetPos (textview, g->mins[0], -g->mins[1]); + View_SetLen (textview, g->maxs[0] - g->mins[0], + g->maxs[1] - g->mins[1]); + View_SetGravity (textview, grav_flow); + passage_ref.count += g->count; + } + pararef.count = passage_ref.count - pararef.start; + Ent_SetComponent (paraview.id, text_glyphs, reg, ¶ref); + + uint32_t para_ent = h->ent[paragraph]; + text_dir_e para_direction = psg_direction; + if (Ent_HasComponent (para_ent, text_script, reg)) { + script_component_t *s = Ent_GetComponent (para_ent, text_script, + reg); + para_direction = s->direction; + } + View_SetOnResize (paraview, text_flow_funcs[para_direction]); + View_SetResize (paraview, !psg_vertical, psg_vertical); + View_SetGravity (paraview, grav_northwest); + View_Control (paraview)->flow_size = 1; + } + Ent_SetComponent (passage_view.id, text_glyphs, reg, &passage_ref); + glyphset_t glyphset = { + .glyphs = glyphs, + .count = glyph_count, + }; + Ent_SetComponent (passage_view.id, text_passage_glyphs, reg, &glyphset); + hb_buffer_destroy (buffer); + hb_font_destroy (fnt); + return passage_view; +} + +void +Text_SetScript (ecs_system_t textsys, uint32_t textid, const char *lang, hb_script_t script, + text_dir_e dir) +{ + script_component_t scr = { + .language = hb_language_from_string (lang, strlen (lang)), + .script = script, + .direction = dir, + }; + Ent_SetComponent (textid, textsys.base + text_script, textsys.reg, &scr); +} + +void +Text_SetFont (ecs_system_t textsys, uint32_t textid, font_t *font) +{ + Ent_SetComponent (textid, textsys.base + text_font, textsys.reg, &font); +} + +void +Text_SetFeatures (ecs_system_t textsys, uint32_t textid, featureset_t *features) +{ + uint32_t features_comp = textsys.base + text_features; + if (Ent_HasComponent (textid, features_comp, textsys.reg)) { + Ent_RemoveComponent (textid, features_comp, textsys.reg); + } + featureset_t *f = Ent_SetComponent (textid, features_comp, textsys.reg, 0); + DARRAY_RESIZE (f, features->size); + memcpy (f->a, features->a, features->size * sizeof (hb_feature_t)); +} + +void +Text_AddFeature (ecs_system_t textsys, uint32_t textid, hb_feature_t feature) +{ + uint32_t features_comp = textsys.base + text_features; + if (!Ent_HasComponent (textid, features_comp, textsys.reg)) { + Ent_SetComponent (textid, features_comp, textsys.reg, 0); + } + featureset_t *f = Ent_GetComponent (textid, features_comp, textsys.reg); + DARRAY_APPEND (f, feature); +} diff --git a/libs/ui/txtbuffer.c b/libs/ui/txtbuffer.c new file mode 100644 index 000000000..b4d7e11d2 --- /dev/null +++ b/libs/ui/txtbuffer.c @@ -0,0 +1,210 @@ +/* + txtbuffer.c + + Text buffer + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/alloc.h" +#include "QF/qtypes.h" +#include "QF/sys.h" + +#include "QF/ui/txtbuffer.h" + +#include "compat.h" + +ALLOC_STATE (txtbuffer_t, txtbuffers); + +static char * +txtbuffer_open_gap (txtbuffer_t *buffer, size_t offset, size_t length) +{ + size_t len; + char *dst; + char *src; + + if (offset == buffer->gapOffset && length <= buffer->gapSize) { + return buffer->text + buffer->gapOffset; + } + if (length <= buffer->gapSize) { + // no resize needed + if (offset < buffer->gapOffset) { + len = buffer->gapOffset - offset; + dst = buffer->text + buffer->gapOffset + buffer->gapSize - len; + src = buffer->text + offset; + } else { + len = offset - buffer->gapOffset; + dst = buffer->text + buffer->gapOffset; + src = buffer->text + buffer->gapOffset + buffer->gapSize; + } + memmove (dst, src, len); + } else { + // need to resize the buffer + // grow the buffer by the difference in lengths rounded up to the + // next multiple of TXTBUFFER_GROW + size_t new_size = buffer->bufferSize + length - buffer->gapSize; + new_size = (new_size + TXTBUFFER_GROW - 1) & ~(TXTBUFFER_GROW - 1); + char *new_text = realloc (buffer->text, new_size); + + if (!new_text) { + return 0; + } + buffer->text = new_text; + + if (offset < buffer->gapOffset) { + // move the old post-gap to the end of the new buffer + len = buffer->bufferSize - (buffer->gapOffset + buffer->gapSize); + dst = buffer->text + new_size - len; + src = buffer->text + buffer->gapOffset + buffer->gapSize; + memmove (dst, src, len); + // move the remaining chunk to after the end of the new gap or + // just before the location of the previous move + len = buffer->gapOffset - offset; + dst -= len; + src = buffer->text + offset; + memmove (dst, src, len); + } else if (offset > buffer->gapOffset) { + // move the old post-offset to the end of the new buffer + len = buffer->bufferSize - (offset + buffer->gapSize); + dst = buffer->text + new_size - len; + src = buffer->text + offset + buffer->gapSize; + memmove (dst, src, len); + // move the old post-gap to offset block to before the new gap + len = offset - buffer->gapOffset; + dst = buffer->text + buffer->gapOffset; + src = buffer->text + buffer->gapOffset + buffer->gapSize; + memmove (dst, src, len); + } else { + // the gap only grew, did not move + len = buffer->bufferSize - (offset + buffer->gapSize); + dst = buffer->text + new_size - len; + src = buffer->text + buffer->gapOffset + buffer->gapSize; + memmove (dst, src, len); + } + buffer->gapSize += new_size - buffer->bufferSize; + buffer->bufferSize = new_size; + } + buffer->gapOffset = offset; + return buffer->text + buffer->gapOffset; +} + +static char * +txtbuffer_delete_text (txtbuffer_t *buffer, size_t offset, size_t length) +{ + size_t len; + char *dst; + char *src; + + if (offset > buffer->gapOffset) { + len = offset - buffer->gapOffset; + dst = buffer->text + buffer->gapOffset; + src = buffer->text + buffer->gapOffset + buffer->gapSize; + memmove (dst, src, len); + } else if (offset + length < buffer->gapOffset) { + len = buffer->gapOffset - (offset + length); + dst = buffer->text + buffer->gapSize + (offset + length); + src = buffer->text + (offset + length); + memmove (dst, src, len); + } + // don't need to do any copying when offset <= gapOffset && + // offset + length >= offset + buffer->gapOffset = offset; + buffer->gapSize += length; + buffer->textSize -= length; + return buffer->text + buffer->gapOffset; +} + +VISIBLE txtbuffer_t * +TextBuffer_Create (void) +{ + txtbuffer_t *buffer; + ALLOC (16, txtbuffer_t, txtbuffers, buffer); + return buffer; +} + +VISIBLE void +TextBuffer_Destroy (txtbuffer_t *buffer) +{ + if (buffer->text) { + free (buffer->text); + } + FREE (txtbuffers, buffer); +} + +VISIBLE char * +TextBuffer_OpenGap (txtbuffer_t *buffer, size_t offset, size_t text_len) +{ + char *dst; + + if (offset > buffer->textSize) { + return 0; + } + dst = txtbuffer_open_gap (buffer, offset, text_len); + if (!dst) { + return 0; + } + return dst; +} + +VISIBLE int +TextBuffer_InsertAt (txtbuffer_t *buffer, size_t offset, + const char *text, size_t text_len) +{ + char *dst; + + if (offset > buffer->textSize) { + return 0; + } + dst = txtbuffer_open_gap (buffer, offset, text_len); + if (!dst) { + return 0; + } + memcpy (dst, text, text_len); + buffer->textSize += text_len; + buffer->gapOffset += text_len; + buffer->gapSize -= text_len; + return 1; +} + +VISIBLE int +TextBuffer_DeleteAt (txtbuffer_t *buffer, size_t offset, size_t len) +{ + if (offset > buffer->textSize) { + return 0; + } + // clamp len to the amount of text beyond offset + if (len > buffer->textSize - offset) { + len = buffer->textSize - offset; + } + txtbuffer_delete_text (buffer, offset, len); + return 1; +} diff --git a/libs/ui/view.c b/libs/ui/view.c new file mode 100644 index 000000000..337cb53f6 --- /dev/null +++ b/libs/ui/view.c @@ -0,0 +1,616 @@ +/* + view.c + + console view object + + Copyright (C) 2003 Bill Currie + + Author: Bill Currie + Date: 2003/5/5 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include + +#include "QF/cexpr.h" +#include "QF/mathlib.h" + +#define IMPLEMENT_VIEW_Funcs +#include "QF/ui/view.h" + +static exprenum_t grav_t_enum; +exprtype_t grav_t_type = { + .name = "grav_t", + .size = sizeof (grav_t), + .data = &grav_t_enum, + .get_string = cexpr_enum_get_string, +}; +static grav_t grav_t_values[] = { + grav_center, + grav_north, + grav_northeast, + grav_east, + grav_southeast, + grav_south, + grav_southwest, + grav_west, + grav_northwest, +}; +static exprsym_t grav_t_symbols[] = { + { "center", &grav_t_type, grav_t_values + grav_center }, + { "north", &grav_t_type, grav_t_values + grav_north }, + { "northeast", &grav_t_type, grav_t_values + grav_northeast }, + { "east", &grav_t_type, grav_t_values + grav_east }, + { "southeast", &grav_t_type, grav_t_values + grav_southeast }, + { "south", &grav_t_type, grav_t_values + grav_south }, + { "southwest", &grav_t_type, grav_t_values + grav_southwest }, + { "west", &grav_t_type, grav_t_values + grav_west }, + { "northwest", &grav_t_type, grav_t_values + grav_northwest }, + {} +}; +static exprtab_t grav_t_symtab = { + grav_t_symbols, +}; +static exprenum_t grav_t_enum = { + &grav_t_type, + &grav_t_symtab, +}; + +static void +view_modified_init (void *_modified) +{ + byte *modified = _modified; + *modified = 1; +} + +const component_t view_components[view_comp_count] = { + [view_href] = { + .size = sizeof (hierref_t), + .name = "view href", + .destroy = Hierref_DestroyComponent, + }, +}; + +static const component_t view_type_components[view_type_count] = { + [view_pos] = { + .size = sizeof (view_pos_t), + .name = "pos", + }, + [view_len] = { + .size = sizeof (view_pos_t), + .name = "len", + }, + [view_abs] = { + .size = sizeof (view_pos_t), + .name = "abs", + }, + [view_rel] = { + .size = sizeof (view_pos_t), + .name = "rel", + }, + [view_oldlen] = { + .size = sizeof (view_pos_t), + .name = "oldlen", + }, + [view_control] = { + .size = sizeof (viewcont_t), + .name = "control", + }, + [view_modified] = { + .size = sizeof (byte), + .create = view_modified_init, + .name = "modified", + }, + [view_onresize] = { + .size = sizeof (view_resize_f), + .name = "onresize", + }, + [view_onmove] = { + .size = sizeof (view_move_f), + .name = "onmove", + }, +}; + +static const hierarchy_type_t view_type = { + .num_components = view_type_count, + .components = view_type_components, +}; + +view_t +View_AddToEntity (uint32_t ent, ecs_system_t viewsys, view_t parent) +{ + uint32_t href_comp = viewsys.base + view_href; + hierref_t *ref = Ent_AddComponent (ent, href_comp, viewsys.reg); + + if (parent.reg && parent.id != nullent) { + hierref_t *pref = View_GetRef (parent); + ref->hierarchy = pref->hierarchy; + ref->index = Hierarchy_InsertHierarchy (pref->hierarchy, 0, + pref->index, 0); + } else { + ref->hierarchy = Hierarchy_New (viewsys.reg, href_comp, &view_type, 1); + ref->index = 0; + } + ref->hierarchy->ent[ref->index] = ent; + return (view_t) { .reg = viewsys.reg, .id = ent, .comp = href_comp }; +} + +view_t +View_New (ecs_system_t viewsys, view_t parent) +{ + uint32_t view = ECS_NewEntity (viewsys.reg); + return View_AddToEntity (view, viewsys, parent); +} + +void +View_UpdateHierarchy (view_t view) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + + byte *modified = h->components[view_modified]; + view_pos_t *pos = h->components[view_pos]; + view_pos_t *len = h->components[view_len]; + view_pos_t *abs = h->components[view_abs]; + view_pos_t *rel = h->components[view_rel]; + view_pos_t *oldlen = h->components[view_oldlen]; + viewcont_t *cont = h->components[view_control]; + view_resize_f *onresize = h->components[view_onresize]; + view_resize_f *onmove = h->components[view_onmove]; + uint32_t *parent = h->parentIndex; + uint32_t *id = h->ent; + + if (abs[0].x != pos[0].x || abs[0].y != pos[0].y) { + modified[0] |= 1; + abs[0] = pos[0]; + rel[0] = pos[0]; + } + if (oldlen[0].x != len[0].x || oldlen[0].y != len[0].y) { + modified[0] |= 2; + if (onresize[0]) { + view_t v = { .reg = view.reg, .id = id[0], .comp = view.comp }; + onresize[0] (v, len[0]); + } + } + for (uint32_t i = 1; i < h->num_objects; i++) { + uint32_t par = parent[i]; + if (!(modified[i] & 2) && (modified[par] & 2) + && (cont[i].resize_x || cont[i].resize_y)) { + int dx = len[par].x - oldlen[par].x; + int dy = len[par].y - oldlen[par].y; + modified[i] |= 2; // propogate resize modifications + oldlen[i] = len[i]; // for child resize calculations + if (cont[i].resize_x) { + len[i].x += dx; + } + if (cont[i].resize_y) { + len[i].y += dy; + } + if (onresize[i]) { + view_t v = { .reg = view.reg, .id = id[i], + .comp = view.comp }; + onresize[i] (v, len[i]); + } + } + if (modified[i] || modified[par]) { + modified[i] |= 1; // propogate motion modifications + switch (cont[i].gravity) { + case grav_center: + rel[i].x = pos[i].x + (len[par].x - len[i].x) / 2; + rel[i].y = pos[i].y + (len[par].y - len[i].y) / 2; + break; + case grav_north: + rel[i].x = pos[i].x + (len[par].x - len[i].x) / 2; + rel[i].y = pos[i].y; + break; + case grav_northeast: + rel[i].x = len[par].x - pos[i].x - len[i].x; + rel[i].y = pos[i].y; + break; + case grav_east: + rel[i].x = len[par].x - pos[i].x - len[i].x; + rel[i].y = pos[i].y + (len[par].y - len[i].y) / 2; + break; + case grav_southeast: + rel[i].x = len[par].x - pos[i].x - len[i].x; + rel[i].y = len[par].y - pos[i].y - len[i].y; + break; + case grav_south: + rel[i].x = pos[i].x + (len[par].x - len[i].x) / 2; + rel[i].y = len[par].y - pos[i].y - len[i].y; + break; + case grav_southwest: + rel[i].x = pos[i].x; + rel[i].y = len[par].y - pos[i].y - len[i].y; + break; + case grav_west: + rel[i].x = pos[i].x; + rel[i].y = pos[i].y + (len[par].y - len[i].y) / 2; + break; + case grav_northwest: + rel[i].x = pos[i].x; + rel[i].y = pos[i].y; + break; + case grav_flow: + //rel is set by the flow functions + break; + } + abs[i].x = abs[par].x + rel[i].x; + abs[i].y = abs[par].y + rel[i].y; + } + } + for (uint32_t i = 0; i < h->num_objects; i++) { + if (modified[i] & 2) { + oldlen[i] = len[i]; + } + if ((modified[i] & 1) && onmove[i]) { + view_t v = { .reg = view.reg, .id = id[i], .comp = view.comp }; + onmove[i] (v, abs[i]); + } + modified[i] = 0; + } +} + +void +View_SetParent (view_t view, view_t parent) +{ + hierarchy_t *dst = 0; + uint32_t dstParent = nullent; + hierarchy_t *src = 0; + uint32_t srcIndex = 0; + if (View_Valid (parent)) { + __auto_type ref = View_GetRef (parent); + dst = ref->hierarchy; + dstParent = ref->index; + } + { + __auto_type ref = View_GetRef (view); + src = ref->hierarchy; + srcIndex = ref->index; + } + Hierarchy_SetParent (dst, dstParent, src, srcIndex); + + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + byte *modified = h->components[view_modified]; + modified[ref->index] = 1; + View_UpdateHierarchy (view); +} + +typedef struct flowline_s { + struct flowline_s *next; + int first_child; + int child_count; + int cursor; + int height; // from baseline + int depth; // from baseline +} flowline_t; + +#define NEXT_LINE(line, child_index) \ + do { \ + line->next = alloca (sizeof (flowline_t)); \ + memset (line->next, 0, sizeof (flowline_t)); \ + line = line->next; \ + line->first_child = child_index; \ + } while (0) + +static void +flow_right (view_t view, void (*set_rows) (view_t, flowline_t *)) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + view_pos_t *pos = h->components[view_pos]; + view_pos_t *len = h->components[view_len]; + viewcont_t *cont = h->components[view_control]; + uint32_t vind = ref->index; + + flowline_t flowline = { .first_child = h->childIndex[ref->index] }; + flowline_t *line = &flowline; + for (uint32_t i = 0; i < h->childCount[vind]; i++) { + uint32_t child = h->childIndex[vind] + i; + if (line->cursor && line->cursor + len[child].x > len[vind].x) { + NEXT_LINE(line, child); + } + pos[child].x = line->cursor; + if (pos[child].x || !cont[child].bol_suppress) { + line->cursor += len[child].x; + } + line->height = max (len[child].y - pos[child].y, line->height); + line->depth = max (pos[child].y, line->depth); + line->child_count++; + } + set_rows (view, &flowline); +} + +static void +flow_left (view_t view, void (*set_rows) (view_t, flowline_t *)) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + view_pos_t *pos = h->components[view_pos]; + view_pos_t *len = h->components[view_len]; + viewcont_t *cont = h->components[view_control]; + uint32_t vind = ref->index; + + flowline_t flowline = { .first_child = h->childIndex[ref->index] }; + flowline_t *line = &flowline; + line->cursor = len[vind].x; + for (uint32_t i = 0; i < h->childCount[vind]; i++) { + uint32_t child = h->childIndex[ref->index] + i; + if (line->cursor < len[vind].x && line->cursor - len[child].x < 0) { + NEXT_LINE(line, child); + line->cursor = len[vind].x; + } + if (pos[child].x < len[vind].x || !cont[child].bol_suppress) { + line->cursor -= len[child].x; + } + pos[child].x = line->cursor; + line->height = max (len[child].y - pos[child].y, line->height); + line->depth = max (pos[child].y, line->depth); + line->child_count++; + } + set_rows (view, &flowline); +} + +static void +flow_down (view_t view, void (*set_rows) (view_t, flowline_t *)) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + view_pos_t *pos = h->components[view_pos]; + view_pos_t *len = h->components[view_len]; + viewcont_t *cont = h->components[view_control]; + uint32_t vind = ref->index; + + flowline_t flowline = { .first_child = h->childIndex[ref->index] }; + flowline_t *line = &flowline; + for (uint32_t i = 0; i < h->childCount[vind]; i++) { + uint32_t child = h->childIndex[vind] + i; + if (line->cursor && line->cursor + len[child].y > len[vind].y) { + NEXT_LINE(line, child); + } + pos[child].y = line->cursor; + if (pos[child].y || !cont[child].bol_suppress) { + line->cursor += len[child].y; + } + line->height = max (len[child].x - pos[child].x, line->height); + line->depth = max (pos[child].x, line->depth); + line->child_count++; + } + set_rows (view, &flowline); +} + +static void +flow_up (view_t view, void (*set_rows) (view_t, flowline_t *)) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + view_pos_t *pos = h->components[view_pos]; + view_pos_t *len = h->components[view_len]; + viewcont_t *cont = h->components[view_control]; + uint32_t vind = ref->index; + + flowline_t flowline = { .first_child = h->childIndex[ref->index] }; + flowline_t *line = &flowline; + line->cursor = len[vind].y; + for (uint32_t i = 0; i < h->childCount[vind]; i++) { + uint32_t child = h->childIndex[ref->index] + i; + if (line->cursor < len[vind].y && line->cursor - len[child].y < 0) { + NEXT_LINE(line, child); + line->cursor = len[vind].y; + } + if (pos[child].y < len[vind].y || !cont[child].bol_suppress) { + line->cursor -= len[child].y; + } + pos[child].y = line->cursor; + line->height = max (len[child].x - pos[child].x, line->height); + line->depth = max (pos[child].x, line->depth); + line->child_count++; + } + set_rows (view, &flowline); +} + +static void +flow_view_height (view_pos_t *len, flowline_t *flowlines) +{ + len->y = 0; + for (flowline_t *line = flowlines; line; line = line->next) { + len->y += line->height + line->depth; + } +} + +static void +flow_view_width (view_pos_t *len, flowline_t *flowlines) +{ + len->x = 0; + for (flowline_t *line = flowlines; line; line = line->next) { + len->x += line->height + line->depth; + } +} + +static void +set_rows_down (view_t view, flowline_t *flowlines) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + view_pos_t *pos = h->components[view_pos]; + view_pos_t *rel = h->components[view_rel]; + view_pos_t *len = h->components[view_len]; + viewcont_t *cont = h->components[view_control]; + uint32_t vind = ref->index; + + if (cont[vind].flow_size) { + flow_view_height (&len[ref->index], flowlines); + } + + int cursor = 0; + for (flowline_t *line = flowlines; line; line = line->next) { + cursor += line->height; + for (int i = 0; i < line->child_count; i++) { + uint32_t child = line->first_child + i; + + rel[child].x = pos[child].x; + rel[child].y = cursor + pos[child].y - len[child].y; + } + cursor += line->depth; + } +} + +static void +set_rows_up (view_t view, flowline_t *flowlines) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + view_pos_t *pos = h->components[view_pos]; + view_pos_t *rel = h->components[view_rel]; + view_pos_t *len = h->components[view_len]; + viewcont_t *cont = h->components[view_control]; + uint32_t vind = ref->index; + + if (cont[vind].flow_size) { + flow_view_height (&len[ref->index], flowlines); + } + + int cursor = len[vind].y; + for (flowline_t *line = flowlines; line; line = line->next) { + cursor -= line->depth; + for (int i = 0; i < line->child_count; i++) { + uint32_t child = line->first_child + i; + + rel[child].x = pos[child].x; + rel[child].y = cursor + pos[child].y - len[child].y; + } + cursor -= line->height; + } +} + +static void +set_columns_right (view_t view, flowline_t *flowlines) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + view_pos_t *pos = h->components[view_pos]; + view_pos_t *rel = h->components[view_rel]; + view_pos_t *len = h->components[view_len]; + viewcont_t *cont = h->components[view_control]; + uint32_t vind = ref->index; + + if (cont[vind].flow_size) { + flow_view_width (&len[ref->index], flowlines); + } + + int cursor = 0; + for (flowline_t *line = flowlines; line; line = line->next) { + cursor += line->depth; + for (int i = 0; i < line->child_count; i++) { + uint32_t child = line->first_child + i; + + rel[child].x = cursor + pos[child].x; + rel[child].y = pos[child].y; + } + cursor += line->height; + } +} + +static void +set_columns_left (view_t view, flowline_t *flowlines) +{ + __auto_type ref = View_GetRef (view); + hierarchy_t *h = ref->hierarchy; + view_pos_t *pos = h->components[view_pos]; + view_pos_t *rel = h->components[view_rel]; + view_pos_t *len = h->components[view_len]; + viewcont_t *cont = h->components[view_control]; + uint32_t vind = ref->index; + + if (cont[vind].flow_size) { + flow_view_width (&len[ref->index], flowlines); + } + + int cursor = len[vind].x; + for (flowline_t *line = flowlines; line; line = line->next) { + cursor -= line->height; + for (int i = 0; i < line->child_count; i++) { + uint32_t child = line->first_child + i; + + rel[child].x = cursor + pos[child].x; + rel[child].y = pos[child].y; + } + cursor -= line->depth; + } +} + +VISIBLE void +view_flow_right_down (view_t view, view_pos_t len) +{ + flow_right (view, set_rows_down); +} + +VISIBLE void +view_flow_right_up (view_t view, view_pos_t len) +{ + flow_right (view, set_rows_up); +} + +VISIBLE void +view_flow_left_down (view_t view, view_pos_t len) +{ + flow_left (view, set_rows_down); +} + +VISIBLE void +view_flow_left_up (view_t view, view_pos_t len) +{ + flow_left (view, set_rows_up); +} + +VISIBLE void +view_flow_down_right (view_t view, view_pos_t len) +{ + flow_down (view, set_columns_right); +} + +VISIBLE void +view_flow_up_right (view_t view, view_pos_t len) +{ + flow_up (view, set_columns_right); +} + +VISIBLE void +view_flow_down_left (view_t view, view_pos_t len) +{ + flow_down (view, set_columns_left); +} + +VISIBLE void +view_flow_up_left (view_t view, view_pos_t len) +{ + flow_up (view, set_columns_left); +} diff --git a/libs/util/vrect.c b/libs/ui/vrect.c similarity index 76% rename from libs/util/vrect.c rename to libs/ui/vrect.c index 169ea8080..975bf4423 100644 --- a/libs/util/vrect.c +++ b/libs/ui/vrect.c @@ -33,14 +33,17 @@ #include +#include "QF/darray.h" #include "QF/mathlib.h" -#include "QF/vrect.h" + +#include "QF/ui/vrect.h" //#define TEST_MEMORY #define RECT_BLOCK 128 #ifndef TEST_MEMORY static vrect_t *free_rects; +static struct DARRAY_TYPE(vrect_t *) rect_sets = DARRAY_STATIC_INIT (32); #endif VISIBLE vrect_t * @@ -55,6 +58,7 @@ VRect_New (int x, int y, int width, int height) for (i = 0; i < RECT_BLOCK - 1; i++) free_rects[i].next = &free_rects[i + 1]; free_rects[i].next = 0; + DARRAY_APPEND (&rect_sets, free_rects); } r = free_rects; free_rects = free_rects->next; @@ -257,3 +261,81 @@ cleanup: } return 0; } + +VISIBLE vrect_t * +VRect_SubRect (const vrect_t *rect, int width, int height) +{ + if (width > rect->width) { + width = rect->width; + } + if (height > rect->height) { + height = rect->height; + } + // First check for exact fits as no heuristics are necessary + if (width == rect->width) { + if (height == rect->height) { + // Exact fit, return a new rect of the same size + return VRect_New (rect->x, rect->y, rect->width, rect->height); + } + + vrect_t *r = VRect_HSplit (rect, rect->y + height); + if (VRect_IsEmpty (r->next)) { + VRect_Delete (r->next); + r->next = 0; + } + return r; + } else if (height == rect->height) { + // Because rect's width ks known to be greater than the requested + // width, the split is guaranteed to produce two non-empty rectangles. + return VRect_VSplit (rect, rect->x + width); + } + + int a = rect->height - height; + int b = rect->width - width; + vrect_t *r, *s; + if (b * height <= a * width || 16 * a > 15 * rect->height) { + // horizontal cuts produce better memory locality so favor them + r = VRect_HSplit (rect, rect->y + height); + if (VRect_IsEmpty (r->next)) { + VRect_Delete (r->next); + r->next = 0; + } + s = VRect_VSplit (r, r->x + width); + } else { + // a horizontal cut produces a larger off-cut rectangle than a vertical + // cut, so do a vertical cut first so the off-cut is as small as + // possible + r = VRect_VSplit (rect, rect->x + width); + if (VRect_IsEmpty (r->next)) { + VRect_Delete (r->next); + r->next = 0; + } + s = VRect_HSplit (r, r->y + height); + } + + if (VRect_IsEmpty (s->next)) { + VRect_Delete (s->next); + s->next = r->next; + } else { + s->next->next = r->next; + } + VRect_Delete (r); + return s; +} + +#ifndef TEST_MEMORY +static void +vrect_shutdown (void *data) +{ + for (size_t i = 0; i < rect_sets.size; i++) { + free (rect_sets.a[i]); + } + DARRAY_CLEAR (&rect_sets); +} + +static void __attribute__((constructor)) +vrect_init (void) +{ + Sys_RegisterShutdown (vrect_shutdown, 0); +} +#endif diff --git a/libs/util/Makefile.am b/libs/util/Makefile.am deleted file mode 100644 index 0c6421450..000000000 --- a/libs/util/Makefile.am +++ /dev/null @@ -1,59 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -SUBDIRS= . test - -AM_CFLAGS= @PREFER_PIC@ -CCASFLAGS+= @PREFER_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include $(Z_CFLAGS) $(FNM_FLAGS) - -lib_LTLIBRARIES= libQFutil.la - -lib_ldflags=-version-info $(QUAKE_LIBRARY_VERSION_INFO) \ - -rpath $(libdir) -no-undefined - -if ASM_ARCH -asm= libasm.la -else -asm= -endif - -noinst_LTLIBRARIES= $(asm) -#EXTRA_LTLIBRARIES= libasm.la - -libasm_la_SOURCES= math.S sys_ia32.S - - -dirent_src= dirent.c -fnmatch_src= fnmatch.c -getopt_src= getopt.c getopt1.c - -if BUILD_DIRENT -dirent= $(dirent_src) -else -dirent= -endif - -if BUILD_FNMATCH -fnmatch= $(fnmatch_src) -else -fnmatch= -endif - -if BUILD_GETOPT -getopt= $(getopt_src) -else -getopt= -endif - -libQFutil_la_LDFLAGS= $(lib_ldflags) -libQFutil_la_LIBADD= $(asm) $(Z_LIBS) $(DL_LIBS) $(WIN32_LIBS) -libQFutil_la_DEPENDENCIES= $(asm) -libQFutil_la_SOURCES= \ - bspfile.c buildnum.c cbuf.c checksum.c cmd.c crc.c cvar.c dstring.c \ - fendian.c hash.c idparse.c info.c link.c llist.c \ - mathlib.c mdfour.c mersenne.c msg.c pakfile.c plugin.c qargs.c qendian.c \ - qfplist.c quakefs.c quakeio.c riff.c script.c segtext.c set.c sizebuf.c \ - string.c sys.c va.c ver_check.c vrect.c wad.c wadfile.c zone.c \ - $(dirent) $(fnmatch) $(getopt) - -EXTRA_DIST= $(fnmatch_src) $(getopt_src) diff --git a/libs/util/Makemodule.am b/libs/util/Makemodule.am new file mode 100644 index 000000000..fdf579f10 --- /dev/null +++ b/libs/util/Makemodule.am @@ -0,0 +1,101 @@ +include libs/util/test/Makemodule.am + +lib_LTLIBRARIES += libs/util/libQFutil.la + +if ASM_ARCH +util_asm= libs/util/libasm.la +else +util_asm= +endif + +noinst_LTLIBRARIES += $(util_asm) +#EXTRA_LTLIBRARIES += libasm.la + +libs_util_libasm_la_SOURCES = libs/util/math.S libs/util/sys_ia32.S + + +dirent_src= libs/util/dirent.c +fnmatch_src= libs/util/fnmatch.c +getopt_src= libs/util/getopt.c libs/util/getopt1.c + +if BUILD_DIRENT +dirent= $(dirent_src) +else +dirent= +endif + +if BUILD_FNMATCH +fnmatch= $(fnmatch_src) +else +fnmatch= +endif + +if BUILD_GETOPT +getopt= $(getopt_src) +else +getopt= +endif + +libs_util_libQFutil_la_LDFLAGS= $(lib_ldflags) +libs_util_libQFutil_la_LIBADD= $(util_asm) $(Z_LIBS) $(DL_LIBS) $(WIN32_LIBS) +libs_util_libQFutil_la_DEPENDENCIES= $(util_asm) +libs_util_libQFutil_la_SOURCES= \ + libs/util/bsearch.c \ + libs/util/bspfile.c \ + libs/util/buildnum.c \ + libs/util/cbuf.c \ + libs/util/cexpr-lex.l \ + libs/util/cexpr-lib.c \ + libs/util/cexpr-parse.y \ + libs/util/cexpr-type.c \ + libs/util/cexpr-vars.c \ + libs/util/checksum.c \ + libs/util/cmd.c \ + libs/util/cmem.c \ + libs/util/crc.c \ + libs/util/cvar.c \ + libs/util/dstring.c \ + libs/util/fendian.c \ + libs/util/hash.c \ + libs/util/heapsort.c \ + libs/util/idparse.c \ + libs/util/info.c \ + libs/util/link.c \ + libs/util/llist.c \ + libs/util/mathlib.c \ + libs/util/mdfour.c \ + libs/util/mersenne.c \ + libs/util/msg.c \ + libs/util/pakfile.c \ + libs/util/plist.c \ + libs/util/plugin.c \ + libs/util/qargs.c \ + libs/util/qendian.c \ + libs/util/qsort_r.c \ + libs/util/quakefs.c \ + libs/util/quakeio.c \ + libs/util/riff.c \ + libs/util/script.c \ + libs/util/segtext.c \ + libs/util/set.c \ + libs/util/simd.c \ + libs/util/sizebuf.c \ + libs/util/string.c \ + libs/util/sys.c \ + libs/util/va.c \ + libs/util/ver_check.c \ + libs/util/wad.c \ + libs/util/wadfile.c \ + libs/util/zone.c \ + $(dirent) $(fnmatch) $(getopt) + +BUILT_SOURCES += \ + libs/util/cexpr-lex.c \ + libs/util/cexpr-parse.c + +libs/util/cexpr-parse.c: libs/util/cexpr-parse.y + $(AM_V_YACC)$(YACCCOMPILE) $< -o $@ +libs/util/cexpr-lex.c: libs/util/cexpr-lex.l libs/util/cexpr-parse.h + $(AM_V_LEX)$(LEXCOMPILE) -o$@ $< + +EXTRA_DIST += $(fnmatch_src) $(getopt_src) diff --git a/libs/util/bsearch.c b/libs/util/bsearch.c new file mode 100644 index 000000000..7114bf134 --- /dev/null +++ b/libs/util/bsearch.c @@ -0,0 +1,97 @@ +#include +#include + +#include "QF/fbsearch.h" +#include "bsearch.h" + +void *fbsearch (const void *key, const void *_base, size_t nmemb, size_t size, + __compar_fn_t cmp) +{ + // fuzzy bsearh + const char *base = (const char *) _base; + unsigned left = 0; + unsigned right = nmemb - 1; + unsigned mid; + const void *p = 0; + + if (!nmemb) { + return 0; + } + while (left != right) { + mid = (left + right + 1) / 2; + p = base + mid * size; + if (cmp (key, p) < 0) { + right = mid - 1; + } else { + left = mid; + } + } + p = base + left * size; + if (cmp (key, p) >= 0) { + return (void *) p; + } + return 0; +} + +void *fbsearch_r (const void *key, const void *_base, size_t nmemb, size_t size, + __compar_d_fn_t cmp, void *arg) +{ + // fuzzy bsearh + const char *base = (const char *) _base; + unsigned left = 0; + unsigned right = nmemb - 1; + unsigned mid; + const void *p = 0; + + if (!nmemb) { + return 0; + } + while (left != right) { + mid = (left + right + 1) / 2; + p = base + mid * size; + if (cmp (key, p, arg) < 0) { + right = mid - 1; + } else { + left = mid; + } + } + p = base + left * size; + if (cmp (key, p, arg) >= 0) { + return (void *) p; + } + return 0; +} + +void * +QF_bsearch_r(const void *key, const void *_base, size_t nmemb, size_t size, + __compar_d_fn_t cmp, void *arg) +{ + const char *base = (const char *) _base; + unsigned left = 0; + unsigned right = nmemb - 1; + unsigned mid; + const void *p = 0; + int c; + + if (!nmemb) { + return 0; + } + while (left != right) { + mid = (left + right + 1) / 2; + p = base + mid * size; + c = cmp (key, p, arg); + if (c == 0) { + return (void *) p; + } + if (c < 0) { + right = mid - 1; + } else { + left = mid; + } + } + p = base + left * size; + if (cmp (key, p, arg) == 0) { + return (void *) p; + } + return 0; +} diff --git a/libs/util/bspfile.c b/libs/util/bspfile.c index eb438c959..1cb36429d 100644 --- a/libs/util/bspfile.c +++ b/libs/util/bspfile.c @@ -92,7 +92,7 @@ typedef struct bsp29_s { dheader_t *header; int own_models; - int nummodels; + size_t nummodels; dmodel_t *models; int own_visdata; @@ -112,56 +112,55 @@ typedef struct bsp29_s { char *entdata; int own_leafs; - int numleafs; + size_t numleafs; dleaf29_t *leafs; int own_planes; - int numplanes; + size_t numplanes; dplane_t *planes; int own_vertexes; - int numvertexes; + size_t numvertexes; dvertex_t *vertexes; int own_nodes; - int numnodes; + size_t numnodes; dnode29_t *nodes; int own_texinfo; - int numtexinfo; + size_t numtexinfo; texinfo_t *texinfo; int own_faces; - int numfaces; + size_t numfaces; dface29_t *faces; int own_clipnodes; - int numclipnodes; + size_t numclipnodes; dclipnode29_t *clipnodes; int own_edges; - int numedges; + size_t numedges; dedge29_t *edges; int own_marksurfaces; - int nummarksurfaces; + size_t nummarksurfaces; uint16_t *marksurfaces; int own_surfedges; - int numsurfedges; + size_t numsurfedges; int32_t *surfedges; } bsp29_t; static void swap_to_bsp29 (bsp29_t *bsp29, const bsp_t *bsp2) { - int c, i, j; dmiptexlump_t *mtl; dmodel_t *d; if (bsp29->header) { bsp29->header->version = LittleLong (bsp29->header->version); - for (i = 0; i < HEADER_LUMPS; i++) { + for (int i = 0; i < HEADER_LUMPS; i++) { bsp29->header->lumps[i].fileofs = LittleLong (bsp29->header->lumps[i].fileofs); bsp29->header->lumps[i].filelen = @@ -170,17 +169,17 @@ swap_to_bsp29 (bsp29_t *bsp29, const bsp_t *bsp2) } // models - for (i=0 ; inummodels ; i++) { + for (size_t i=0 ; inummodels ; i++) { d = &bsp29->models[i]; - for (j=0 ; jheadnode[j] = LittleLong (d->headnode[j]); d->visleafs = LittleLong (d->visleafs); d->firstface = LittleLong (d->firstface); d->numfaces = LittleLong (d->numfaces); - for (j=0 ; j<3 ; j++) { + for (int j = 0; j < 3; j++) { d->mins[j] = LittleFloat(d->mins[j]); d->maxs[j] = LittleFloat(d->maxs[j]); d->origin[j] = LittleFloat(d->origin[j]); @@ -188,25 +187,25 @@ swap_to_bsp29 (bsp29_t *bsp29, const bsp_t *bsp2) } // vertexes - for (i=0 ; inumvertexes ; i++) { + for (size_t i = 0; i < bsp29->numvertexes; i++) { dvertex_t *vertex = &bsp29->vertexes[i]; - for (j=0 ; j<3 ; j++) + for (int j = 0; j < 3; j++) vertex->point[j] = LittleFloat (vertex->point[j]); } // planes - for (i=0 ; inumplanes ; i++) { + for (size_t i = 0; i < bsp29->numplanes; i++) { dplane_t *plane = &bsp29->planes[i]; - for (j=0 ; j<3 ; j++) + for (int j = 0; j < 3; j++) plane->normal[j] = LittleFloat (plane->normal[j]); plane->dist = LittleFloat (plane->dist); plane->type = LittleLong (plane->type); } // texinfos - for (i=0 ; inumtexinfo ; i++) { + for (size_t i = 0; i < bsp29->numtexinfo; i++) { texinfo_t *texinfo = &bsp29->texinfo[i]; - for (j=0 ; j < 4 ; j++) { + for (int j = 0; j < 4; j++) { texinfo->vecs[0][j] = LittleFloat (texinfo->vecs[0][j]); texinfo->vecs[1][j] = LittleFloat (texinfo->vecs[1][j]); } @@ -215,7 +214,7 @@ swap_to_bsp29 (bsp29_t *bsp29, const bsp_t *bsp2) } // faces - for (i=0 ; inumfaces ; i++) { + for (size_t i = 0; i < bsp29->numfaces; i++) { const dface_t *face2 = &bsp2->faces[i]; dface29_t *face29 = &bsp29->faces[i]; face29->planenum = LittleShort (face2->planenum); @@ -228,28 +227,28 @@ swap_to_bsp29 (bsp29_t *bsp29, const bsp_t *bsp2) } // nodes - for (i=0 ; inumnodes ; i++) { + for (size_t i = 0; i < bsp29->numnodes; i++) { const dnode_t *node2 = &bsp2->nodes[i]; dnode29_t *node29 = &bsp29->nodes[i]; node29->planenum = LittleLong (node2->planenum); - for (j=0 ; j<3 ; j++) { - node29->mins[j] = LittleShort (node2->mins[j]); - node29->maxs[j] = LittleShort (node2->maxs[j]); + for (int j = 0; j < 3; j++) { + node29->mins[j] = LittleShort ((int16_t) node2->mins[j]); + node29->maxs[j] = LittleShort ((int16_t) node2->maxs[j]); } - node29->children[0] = (uint16_t) LittleShort (node2->children[0]); - node29->children[1] = (uint16_t) LittleShort (node2->children[1]); + node29->children[0] = LittleShort (node2->children[0]); + node29->children[1] = LittleShort (node2->children[1]); node29->firstface = LittleShort (node2->firstface); node29->numfaces = LittleShort (node2->numfaces); } // leafs - for (i=0 ; inumleafs ; i++) { + for (size_t i = 0; i < bsp29->numleafs; i++) { const dleaf_t *leaf2 = &bsp2->leafs[i]; dleaf29_t *leaf29 = &bsp29->leafs[i]; leaf29->contents = LittleLong (leaf2->contents); - for (j=0 ; j<3 ; j++) { - leaf29->mins[j] = LittleShort (leaf2->mins[j]); - leaf29->maxs[j] = LittleShort (leaf2->maxs[j]); + for (int j = 0; j < 3; j++) { + leaf29->mins[j] = LittleShort ((int16_t) leaf2->mins[j]); + leaf29->maxs[j] = LittleShort ((int16_t) leaf2->maxs[j]); } leaf29->firstmarksurface = LittleShort (leaf2->firstmarksurface); @@ -260,39 +259,39 @@ swap_to_bsp29 (bsp29_t *bsp29, const bsp_t *bsp2) } // clipnodes - for (i=0 ; inumclipnodes ; i++) { + for (size_t i = 0; i < bsp29->numclipnodes; i++) { const dclipnode_t *clipnode2 = &bsp2->clipnodes[i]; dclipnode29_t *clipnode29 = (dclipnode29_t *) &bsp29->clipnodes[i]; clipnode29->planenum = LittleLong (clipnode2->planenum); - clipnode29->children[0] = (uint16_t) LittleShort (clipnode2->children[0]); - clipnode29->children[1] = (uint16_t) LittleShort (clipnode2->children[1]); + clipnode29->children[0] = LittleShort (clipnode2->children[0]); + clipnode29->children[1] = LittleShort (clipnode2->children[1]); } // miptex if (bsp29->texdatasize) { mtl = (dmiptexlump_t *)bsp29->texdata; //miptexlumps have not yet been swapped - c = mtl->nummiptex; + uint32_t c = mtl->nummiptex; mtl->nummiptex = LittleLong (mtl->nummiptex); - for (i=0 ; idataofs[i] = LittleLong(mtl->dataofs[i]); } // marksurfaces - for (i=0 ; inummarksurfaces ; i++) { + for (size_t i = 0 ; i < bsp29->nummarksurfaces; i++) { const uint32_t *marksurface2 = &bsp2->marksurfaces[i]; uint16_t *marksurface29 = (uint16_t *) &bsp29->marksurfaces[i]; *marksurface29 = LittleShort (*marksurface2); } // surfedges - for (i=0 ; inumsurfedges ; i++) { + for (size_t i = 0; i < bsp29->numsurfedges; i++) { int32_t *surfedge = &bsp29->surfedges[i]; *surfedge = LittleLong (*surfedge); } // edges - for (i=0 ; inumedges ; i++) { + for (size_t i = 0; i < bsp29->numedges; i++) { const dedge_t *edge2 = &bsp2->edges[i]; dedge29_t *edge29 = (dedge29_t *) &bsp29->edges[i]; edge29->v[0] = LittleShort (edge2->v[0]); @@ -304,13 +303,12 @@ static void swap_from_bsp29 (bsp_t *bsp2, const bsp29_t *bsp29, void (*cb) (const bsp_t *, void *), void *cbdata) { - int c, i, j; dmiptexlump_t *mtl; dmodel_t *d; if (bsp2->header) { bsp2->header->version = LittleLong (bsp2->header->version); - for (i = 0; i < HEADER_LUMPS; i++) { + for (int i = 0; i < HEADER_LUMPS; i++) { bsp2->header->lumps[i].fileofs = LittleLong (bsp2->header->lumps[i].fileofs); bsp2->header->lumps[i].filelen = @@ -321,17 +319,17 @@ swap_from_bsp29 (bsp_t *bsp2, const bsp29_t *bsp29, } // models - for (i=0 ; inummodels ; i++) { + for (size_t i = 0; i < bsp2->nummodels; i++) { d = &bsp2->models[i]; - for (j=0 ; jheadnode[j] = LittleLong (d->headnode[j]); d->visleafs = LittleLong (d->visleafs); d->firstface = LittleLong (d->firstface); d->numfaces = LittleLong (d->numfaces); - for (j=0 ; j<3 ; j++) { + for (int j = 0; j < 3; j++) { d->mins[j] = LittleFloat(d->mins[j]); d->maxs[j] = LittleFloat(d->maxs[j]); d->origin[j] = LittleFloat(d->origin[j]); @@ -339,25 +337,25 @@ swap_from_bsp29 (bsp_t *bsp2, const bsp29_t *bsp29, } // vertexes - for (i=0 ; inumvertexes ; i++) { + for (size_t i = 0; i < bsp2->numvertexes; i++) { dvertex_t *vertex = &bsp2->vertexes[i]; - for (j=0 ; j<3 ; j++) + for (int j = 0; j < 3; j++) vertex->point[j] = LittleFloat (vertex->point[j]); } // planes - for (i=0 ; inumplanes ; i++) { + for (size_t i = 0; i < bsp2->numplanes; i++) { dplane_t *plane = &bsp2->planes[i]; - for (j=0 ; j<3 ; j++) + for (int j = 0; j < 3; j++) plane->normal[j] = LittleFloat (plane->normal[j]); plane->dist = LittleFloat (plane->dist); plane->type = LittleLong (plane->type); } // texinfos - for (i=0 ; inumtexinfo ; i++) { + for (size_t i = 0; i < bsp2->numtexinfo; i++) { texinfo_t *texinfo = &bsp2->texinfo[i]; - for (j=0 ; j < 4 ; j++) { + for (int j = 0; j < 4; j++) { texinfo->vecs[0][j] = LittleFloat (texinfo->vecs[0][j]); texinfo->vecs[1][j] = LittleFloat (texinfo->vecs[1][j]); } @@ -366,7 +364,7 @@ swap_from_bsp29 (bsp_t *bsp2, const bsp29_t *bsp29, } // faces - for (i=0 ; inumfaces ; i++) { + for (size_t i = 0; i < bsp2->numfaces; i++) { dface_t *face2 = &bsp2->faces[i]; const dface29_t *face29 = &bsp29->faces[i]; face2->planenum = LittleShort (face29->planenum); @@ -379,17 +377,17 @@ swap_from_bsp29 (bsp_t *bsp2, const bsp29_t *bsp29, } // nodes - for (i=0 ; inumnodes ; i++) { + for (size_t i = 0; i < bsp2->numnodes; i++) { dnode_t *node2 = &bsp2->nodes[i]; const dnode29_t *node29 = &bsp29->nodes[i]; node2->planenum = LittleLong (node29->planenum); - for (j=0 ; j<3 ; j++) { + for (int j = 0; j < 3; j++) { node2->mins[j] = (int16_t) LittleShort (node29->mins[j]); node2->maxs[j] = (int16_t) LittleShort (node29->maxs[j]); } - for (j = 0; j < 2; j++) { + for (int j = 0; j < 2; j++) { node2->children[j] = LittleShort (node29->children[j]); - if (node2->children[j] >= bsp2->numnodes) + if (node2->children[j] >= (int32_t) bsp2->numnodes) node2->children[j] = (int16_t) node2->children[j]; } node2->firstface = LittleShort (node29->firstface); @@ -397,13 +395,13 @@ swap_from_bsp29 (bsp_t *bsp2, const bsp29_t *bsp29, } // leafs - for (i=0 ; inumleafs ; i++) { + for (size_t i = 0; i < bsp2->numleafs; i++) { dleaf_t *leaf2 = &bsp2->leafs[i]; const dleaf29_t *leaf29 = &bsp29->leafs[i]; leaf2->contents = LittleLong (leaf29->contents); - for (j=0 ; j<3 ; j++) { - leaf2->mins[j] = LittleShort (leaf29->mins[j]); - leaf2->maxs[j] = LittleShort (leaf29->maxs[j]); + for (int j = 0; j < 3; j++) { + leaf2->mins[j] = (int16_t) LittleShort (leaf29->mins[j]); + leaf2->maxs[j] = (int16_t) LittleShort (leaf29->maxs[j]); } leaf2->firstmarksurface = LittleShort (leaf29->firstmarksurface); @@ -414,38 +412,38 @@ swap_from_bsp29 (bsp_t *bsp2, const bsp29_t *bsp29, } // clipnodes - for (i=0 ; inumclipnodes ; i++) { + for (size_t i = 0; i < bsp2->numclipnodes; i++) { dclipnode_t *clipnode2 = &bsp2->clipnodes[i]; const dclipnode29_t *clipnode29 = &bsp29->clipnodes[i]; clipnode2->planenum = LittleLong (clipnode29->planenum); - clipnode2->children[0] = (uint16_t) LittleShort (clipnode29->children[0]); - clipnode2->children[1] = (uint16_t) LittleShort (clipnode29->children[1]); + clipnode2->children[0] = LittleShort (clipnode29->children[0]); + clipnode2->children[1] = LittleShort (clipnode29->children[1]); } // miptex if (bsp2->texdatasize) { mtl = (dmiptexlump_t *)bsp2->texdata; - c = LittleLong(mtl->nummiptex); + uint32_t c = LittleLong (mtl->nummiptex); mtl->nummiptex = LittleLong (mtl->nummiptex); - for (i=0 ; idataofs[i] = LittleLong(mtl->dataofs[i]); + for (uint32_t i = 0; i < c; i++) + mtl->dataofs[i] = LittleLong (mtl->dataofs[i]); } // marksurfaces - for (i=0 ; inummarksurfaces ; i++) { + for (size_t i = 0; i < bsp2->nummarksurfaces; i++) { uint32_t *marksurface2 = &bsp2->marksurfaces[i]; const uint16_t *marksurface29 = &bsp29->marksurfaces[i]; *marksurface2 = LittleShort (*marksurface29); } // surfedges - for (i=0 ; inumsurfedges ; i++) { + for (size_t i = 0; i < bsp2->numsurfedges; i++) { int32_t *surfedge = &bsp2->surfedges[i]; *surfedge = LittleLong (*surfedge); } // edges - for (i=0 ; inumedges ; i++) { + for (size_t i = 0; i < bsp2->numedges; i++) { dedge_t *edge2 = &bsp2->edges[i]; const dedge29_t *edge29 = &bsp29->edges[i]; edge2->v[0] = LittleShort (edge29->v[0]); @@ -457,13 +455,12 @@ static void swap_bsp (bsp_t *bsp, int todisk, void (*cb) (const bsp_t *, void *), void *cbdata) { - int c, i, j; dmiptexlump_t *mtl; dmodel_t *d; if (bsp->header) { bsp->header->version = LittleLong (bsp->header->version); - for (i = 0; i < HEADER_LUMPS; i++) { + for (int i = 0; i < HEADER_LUMPS; i++) { bsp->header->lumps[i].fileofs = LittleLong (bsp->header->lumps[i].fileofs); bsp->header->lumps[i].filelen = @@ -474,17 +471,17 @@ swap_bsp (bsp_t *bsp, int todisk, void (*cb) (const bsp_t *, void *), } // models - for (i=0 ; inummodels ; i++) { + for (size_t i = 0; i < bsp->nummodels; i++) { d = &bsp->models[i]; - for (j=0 ; jheadnode[j] = LittleLong (d->headnode[j]); d->visleafs = LittleLong (d->visleafs); d->firstface = LittleLong (d->firstface); d->numfaces = LittleLong (d->numfaces); - for (j=0 ; j<3 ; j++) { + for (int j = 0; j < 3; j++) { d->mins[j] = LittleFloat(d->mins[j]); d->maxs[j] = LittleFloat(d->maxs[j]); d->origin[j] = LittleFloat(d->origin[j]); @@ -492,25 +489,25 @@ swap_bsp (bsp_t *bsp, int todisk, void (*cb) (const bsp_t *, void *), } // vertexes - for (i=0 ; inumvertexes ; i++) { + for (size_t i = 0; i < bsp->numvertexes; i++) { dvertex_t *vertex = &bsp->vertexes[i]; - for (j=0 ; j<3 ; j++) + for (int j = 0; j < 3; j++) vertex->point[j] = LittleFloat (vertex->point[j]); } // planes - for (i=0 ; inumplanes ; i++) { + for (size_t i = 0; i < bsp->numplanes; i++) { dplane_t *plane = &bsp->planes[i]; - for (j=0 ; j<3 ; j++) + for (int j = 0; j < 3; j++) plane->normal[j] = LittleFloat (plane->normal[j]); plane->dist = LittleFloat (plane->dist); plane->type = LittleLong (plane->type); } // texinfos - for (i=0 ; inumtexinfo ; i++) { + for (size_t i = 0; i < bsp->numtexinfo; i++) { texinfo_t *texinfo = &bsp->texinfo[i]; - for (j=0 ; j < 4 ; j++) { + for (int j = 0; j < 4; j++) { texinfo->vecs[0][j] = LittleFloat (texinfo->vecs[0][j]); texinfo->vecs[1][j] = LittleFloat (texinfo->vecs[1][j]); } @@ -519,7 +516,7 @@ swap_bsp (bsp_t *bsp, int todisk, void (*cb) (const bsp_t *, void *), } // faces - for (i=0 ; inumfaces ; i++) { + for (size_t i = 0; i < bsp->numfaces; i++) { dface_t *face = &bsp->faces[i]; face->texinfo = LittleLong (face->texinfo); face->planenum = LittleLong (face->planenum); @@ -530,10 +527,10 @@ swap_bsp (bsp_t *bsp, int todisk, void (*cb) (const bsp_t *, void *), } // nodes - for (i=0 ; inumnodes ; i++) { + for (size_t i = 0; i < bsp->numnodes; i++) { dnode_t *node = &bsp->nodes[i]; node->planenum = LittleLong (node->planenum); - for (j=0 ; j<3 ; j++) { + for (int j = 0; j < 3; j++) { node->mins[j] = LittleFloat (node->mins[j]); node->maxs[j] = LittleFloat (node->maxs[j]); } @@ -544,10 +541,10 @@ swap_bsp (bsp_t *bsp, int todisk, void (*cb) (const bsp_t *, void *), } // leafs - for (i=0 ; inumleafs ; i++) { + for (size_t i = 0; i < bsp->numleafs; i++) { dleaf_t *leaf = &bsp->leafs[i]; leaf->contents = LittleLong (leaf->contents); - for (j=0 ; j<3 ; j++) { + for (int j = 0; j < 3 ; j++) { leaf->mins[j] = LittleFloat (leaf->mins[j]); leaf->maxs[j] = LittleFloat (leaf->maxs[j]); } @@ -558,7 +555,7 @@ swap_bsp (bsp_t *bsp, int todisk, void (*cb) (const bsp_t *, void *), } // clipnodes - for (i=0 ; inumclipnodes ; i++) { + for (size_t i = 0; i < bsp->numclipnodes; i++) { dclipnode_t *clipnode = &bsp->clipnodes[i]; clipnode->planenum = LittleLong (clipnode->planenum); clipnode->children[0] = LittleLong (clipnode->children[0]); @@ -567,30 +564,31 @@ swap_bsp (bsp_t *bsp, int todisk, void (*cb) (const bsp_t *, void *), // miptex if (bsp->texdatasize) { - mtl = (dmiptexlump_t *)bsp->texdata; + mtl = (dmiptexlump_t *) bsp->texdata; + uint32_t c; if (todisk) c = mtl->nummiptex; else c = LittleLong(mtl->nummiptex); mtl->nummiptex = LittleLong (mtl->nummiptex); - for (i=0 ; idataofs[i] = LittleLong(mtl->dataofs[i]); } // marksurfaces - for (i=0 ; inummarksurfaces ; i++) { + for (size_t i = 0; i < bsp->nummarksurfaces; i++) { uint32_t *marksurface = &bsp->marksurfaces[i]; *marksurface = LittleLong (*marksurface); } // surfedges - for (i=0 ; inumsurfedges ; i++) { + for (size_t i = 0; i < bsp->numsurfedges; i++) { int32_t *surfedge = &bsp->surfedges[i]; *surfedge = LittleLong (*surfedge); } // edges - for (i=0 ; inumedges ; i++) { + for (size_t i = 0; i < bsp->numedges; i++) { dedge_t *edge = &bsp->edges[i]; edge->v[0] = LittleLong (edge->v[0]); edge->v[1] = LittleLong (edge->v[1]); @@ -685,7 +683,7 @@ do { \ size_t size = LittleLong (bsp29->header->lumps[l].filelen); \ size_t offs = LittleLong (bsp29->header->lumps[l].fileofs); \ void *data = (byte *) mem + offs; \ - if (offs >= mem_size || (offs + size) > mem_size) \ + if (offs > mem_size || (offs + size) > mem_size) \ Sys_Error ("invalid lump"); \ bsp29->n = 0; \ if (size) \ @@ -705,7 +703,7 @@ LoadBSPMem (void *mem, size_t mem_size, void (*cb) (const bsp_t *, void *), { bsp_t *bsp; int version; - qboolean bsp2 = false; + bool bsp2 = false; bsp = calloc (sizeof (bsp_t), 1); @@ -810,7 +808,8 @@ do { \ } \ } while (0) - tbsp->header->version = BSPVERSION; + memcpy (&tbsp->header->version, BSP2VERSION, + sizeof (tbsp->header->version)); data = (byte *) &tbsp->header[1]; SET_LUMP (LUMP_PLANES, planes); @@ -943,7 +942,7 @@ do { \ VISIBLE void WriteBSPFile (const bsp_t *bsp, QFile *file) { - qboolean bsp2 = ( bsp->models[0].mins[0] < -32768.0f + bool bsp2 = ( bsp->models[0].mins[0] < -32768.0f || bsp->models[0].mins[1] < -32768.0f || bsp->models[0].mins[2] < -32768.0f || bsp->models[0].mins[0] >= 32768.0f diff --git a/libs/util/cbuf.c b/libs/util/cbuf.c index 2249499e7..36765daf2 100644 --- a/libs/util/cbuf.c +++ b/libs/util/cbuf.c @@ -123,6 +123,17 @@ Cbuf_DeleteStack (cbuf_t *stack) } } +VISIBLE void +Cbuf_DeleteStackReverse (cbuf_t *stack) +{ + cbuf_t *next; + + for (; stack; stack = next) { + next = stack->up; + Cbuf_Delete (stack); + } +} + void Cbuf_Reset (cbuf_t *cbuf) { diff --git a/libs/util/cexpr-lex.l b/libs/util/cexpr-lex.l new file mode 100644 index 000000000..0bb03246d --- /dev/null +++ b/libs/util/cexpr-lex.l @@ -0,0 +1,379 @@ +/* + cexpr-lex.l + + Config expression parser. Or concurrent. + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +%option bison-bridge +%option reentrant +%option prefix="cexpr_yy" +%option noyywrap + +%{ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include +#include + +#include "QF/cmem.h" +#include "QF/dstring.h" +#include "QF/hash.h" +#include "QF/plist.h" +#include "QF/sys.h" + +#define CEXPR_YYDEBUG 1 + +#include "QF/cexpr.h" +#include "libs/util/cexpr-parse.h" + +#define YY_NO_INPUT +#define YY_NO_UNPUT + +#define YYSTYPE CEXPR_YYSTYPE +#define YY_EXTRA_TYPE exprctx_t * + +exprctx_t *cexpr_yyget_extra (yyscan_t yyscanner) __attribute__((pure)); +int cexpr_yyget_lineno (yyscan_t yyscanner) __attribute__((pure)); +int cexpr_yyget_column (yyscan_t yyscanner) __attribute__((pure)); +YYSTYPE *cexpr_yyget_lval (yyscan_t yyscanner) __attribute__((pure)); +int cexpr_yyget_debug (yyscan_t yyscanner) __attribute__((pure)); +char *cexpr_yyget_text (yyscan_t yyscanner) __attribute__((pure)); +int cexpr_yyget_leng (yyscan_t yyscanner) __attribute__((pure)); +FILE *cexpr_yyget_out (yyscan_t yyscanner) __attribute__((pure)); +FILE *cexpr_yyget_in (yyscan_t yyscanner) __attribute__((pure)); + +static exprval_t *parse_int (const char *str, exprctx_t *context); +static exprval_t *parse_uint (const char *str, exprctx_t *context); +static exprval_t *parse_size_t (const char *str, exprctx_t *context); +static exprval_t *parse_float (const char *str, exprctx_t *context); +static exprval_t *parse_double (const char *str, exprctx_t *context); +static exprval_t *parse_string (const char *str, exprctx_t *context); +static exprsym_t *parse_name (const char *str, exprctx_t *context); +static exprval_t *parse_variable (const char *str, exprctx_t *context); + +VISIBLE void +cexpr_error(exprctx_t *ctx, const char *fmt, ...) +{ + va_list args; + dstring_t *string; + + ctx->errors++; + + string = dstring_new (); + + va_start (args, fmt); + dvsprintf (string, fmt, args); + va_end (args); + + if (ctx->messages) { + if (ctx->msg_prefix) { + PL_Message (ctx->messages, ctx->item, "%s:%s", + ctx->msg_prefix, string->str); + } else { + PL_Message (ctx->messages, ctx->item, "%s", string->str); + } + } else { + if (ctx->msg_prefix) { + Sys_Printf ("%s:%s\n", ctx->msg_prefix, string->str); + } else { + Sys_Printf ("%s\n", string->str); + } + } + dstring_delete (string); +} + +%} + +s [ \t] +m [\-+] +D [0-9] +B [01] +X [0-9a-fA-F] +id [a-zA-Z_0-9]* +ID [a-zA-Z_]{id} +FLOAT ({D}+|{D}*\.{D}+|{D}+\.{D}*)([eE]{m}?{D}+)? +FLOATf {FLOAT}[fF] +FLOATd {FLOAT}[dD] +INT ({D}+|0[xX]{X}+|0[bB]{B}) +STRING \"(\\.|[^"\\])*\" + +%% +%{ + __auto_type context = yyget_extra (yyscanner); +%} + +{INT}+[uU] { + yylval->value = parse_uint (yytext, context); + return VALUE; + } + +{INT}+ { + yylval->value = parse_int (yytext, context); + return VALUE; + } + +{INT}+[zZ] { + yylval->value = parse_size_t (yytext, context); + return VALUE; + } + +{FLOAT} { + yylval->value = parse_double (yytext, context); + return VALUE; + } +{FLOATf} { + yylval->value = parse_float (yytext, context); + return VALUE; + } +{FLOATd} { + yylval->value = parse_double (yytext, context); + return VALUE; + } + +{ID} { + yylval->symbol = parse_name (yytext, context); + return NAME; + } + +`{id} { + yylval->symbol = parse_name (yytext + 1, context); + return NAME; + } + +\${ID} { + yylval->value = parse_variable (yytext + 1, context); + return VALUE; + } + +{STRING} { + yylval->value = parse_string (yytext, context); + return VALUE; + } +@ return '@'; + +'(\\[^xX0-7\r\n]|[^'\r\n]|\\[xX][0-9A-Fa-f]+|\\[0-7]+)*' { + } + +[+\-*/&|^%]= { + yylval->op = yytext[0]; + return ASX; + } + +"%%=" { + yylval->op = MOD; + return ASX; + } + +"<<=" { + yylval->op = SHL; + return ASX; + } + +">>=" { + yylval->op = SHR; + return ASX; + } + +[!(){}.*/&|^~+\-=\[\];,#%?:] { + return yytext[0]; + } + +"%%" { + return MOD; + } + +"<<" return SHL; +">>" return SHR; + +"&&" return AND; +"||" return OR; +"==" return EQ; +"!=" return NE; +"<=" return LE; +">=" return GE; +"<" return LT; +">" return GT; + +"++" { + } + +"--" { + } + +<*>\r*\n { + } + +<*>{s}* /* skip */ + +<*>. cexpr_error (context, "all your typo are belong to us"); + +%% + +VISIBLE int +cexpr_eval_string (const char *str, exprctx_t *context) +{ + int status; + yyscan_t scanner; + cexpr_yypstate *ps = cexpr_yypstate_new (); + + yylex_init_extra (context, &scanner); + yy_scan_string (str, scanner); + + context->errors = 0; + do { + CEXPR_YYSTYPE lval; + int token = yylex (&lval, scanner); + status = cexpr_yypush_parse (ps, token, &lval, scanner, context); + } while (status == YYPUSH_MORE); + + yylex_destroy (scanner); + cexpr_yypstate_delete (ps); + return status || context->errors; +} + +static exprval_t *parse_int (const char *str, exprctx_t *context) +{ + exprval_t *val = cexpr_value (&cexpr_int, context); + *(int *) val->value = strtoimax (str, 0, 0); + return val; +} + +static exprval_t *parse_uint (const char *str, exprctx_t *context) +{ + exprval_t *val = cexpr_value (&cexpr_uint, context); + *(unsigned *) val->value = strtoumax (str, 0, 0); + return val; +} + +static exprval_t *parse_size_t (const char *str, exprctx_t *context) +{ + exprval_t *val = cexpr_value (&cexpr_size_t, context); + *(unsigned *) val->value = strtoumax (str, 0, 0); + return val; +} + +static exprval_t *parse_float (const char *str, exprctx_t *context) +{ + exprval_t *val = cexpr_value (&cexpr_float, context); + *(float *) val->value = strtof (str, 0); + return val; +} + +static exprval_t *parse_double (const char *str, exprctx_t *context) +{ + exprval_t *val = cexpr_value (&cexpr_double, context); + *(double *) val->value = strtod (str, 0); + return val; +} + +static exprval_t *parse_string (const char *str, exprctx_t *context) +{ + exprval_t *val = cexpr_value (&cexpr_string, context); + // str includes the quotes, which add 2 to the length of the string, but + // only one byte is needed for the terminating nul + size_t size = strlen (str) - 1; + char *dup = cmemalloc (context->memsuper, size); + strncpy (dup, str + 1, size - 1); + dup[size - 1] = 0; + *(char **) val->value = dup; + return val; +} + +static exprsym_t * +parse_name (const char *name, exprctx_t *context) +{ + exprsym_t *sym = 0; + exprtab_t *prev_tab = 0; + + for (exprctx_t *ctx = context; ctx && !sym; ctx = ctx->parent) { + exprtab_t *symtab = ctx->symtab; + if (!symtab) { + // scope barrier + break; + } + if (symtab == prev_tab) { + // already checked this symtab + continue; + } + prev_tab = symtab; + + if (symtab->tab) { + sym = Hash_Find (symtab->tab, name); + } else { + for (exprsym_t *s = symtab->symbols; s->name; s++) { + if (!strcmp (s->name, name)) { + sym = s; + break; + } + } + } + } + if (!sym) { + sym = cmemalloc (context->memsuper, sizeof (exprsym_t)); + size_t size = strlen (name) + 1; + char *sym_name = cmemalloc (context->memsuper, size); + memcpy (sym_name, name, size); + *sym = (exprsym_t) { .name = sym_name }; + } + return sym; +} + +static exprval_t * +parse_variable (const char *name, exprctx_t *context) +{ + exprval_t *val = 0; + if (strcmp (name, "cvars") == 0) { + val = cexpr_cvar_struct (context); + } else { + exprsym_t *sym = 0; + exprtab_t *prev_tab = 0; + for (exprctx_t *ctx = context; ctx && !sym; ctx = ctx->parent) { + exprtab_t *symtab = ctx->external_variables; + if (!symtab) { + // scope barrier + break; + } + if (symtab == prev_tab) { + // already checked this symtab + continue; + } + sym = Hash_Find (symtab->tab, name); + } + if (sym) { + val = cexpr_value_reference (sym->type, sym->value, context); + } + } + if (!val) { + cexpr_error (context, "undefined variable %s", name); + } + return val; +} diff --git a/libs/util/cexpr-lib.c b/libs/util/cexpr-lib.c new file mode 100644 index 000000000..4fa920752 --- /dev/null +++ b/libs/util/cexpr-lib.c @@ -0,0 +1,301 @@ +/* + cexpr-lib.c + + Config expression parser. Or concurrent. + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include + +#include "QF/cexpr.h" +#include "QF/mathlib.h" +#include "QF/simd/vec4f.h" + +#include "libs/util/cexpr-parse.h" + +#undef uint +#define uint unsigned +#undef cexpr_unsigned +#define cexpr_unsigned cexpr_uint + +#define FNAME(name, rtype, ptype) name##_##rtype##_##ptype + +#define FUNC1(name, rtype, ptype, func) \ +static void \ +FNAME(name, rtype, ptype) (const exprval_t **params, exprval_t *result, \ + exprctx_t *context) \ +{ \ + ptype *a = params[0]->value; \ + rtype *r = result->value; \ + *r = func (*a); \ +} + +#define FUNC2(name, rtype, ptype, func) \ +static void \ +FNAME(name, rtype, ptype) (const exprval_t **params, exprval_t *result, \ + exprctx_t *context) \ +{ \ + /* parameters are reversed! */ \ + ptype *a = params[1]->value; \ + ptype *b = params[0]->value; \ + rtype *r = result->value; \ + *r = func (*a, *b); \ +} + +#define FUNC3(name, rtype, ptype, func) \ +static void \ +FNAME(name, rtype, ptype) (const exprval_t **params, exprval_t *result, \ + exprctx_t *context) \ +{ \ + /* parameters are reversed! */ \ + ptype *a = params[2]->value; \ + ptype *b = params[1]->value; \ + ptype *c = params[0]->value; \ + rtype *r = result->value; \ + *r = func (*a, *b, *c); \ +} + +#undef vector +#define vector vec4f_t + +FUNC2 (dot, vector, vector, dotf) + +FUNC1 (sin, float, float, sinf) +FUNC1 (sin, double, double, sin) + +FUNC1 (cos, float, float, cosf) +FUNC1 (cos, double, double, cos) + +FUNC1 (tan, float, float, tanf) +FUNC1 (tan, double, double, tan) + +FUNC1 (asin, float, float, asinf) +FUNC1 (asin, double, double, asin) + +FUNC1 (acos, float, float, acosf) +FUNC1 (acos, double, double, acos) + +FUNC1 (atan, float, float, atanf) +FUNC1 (atan, double, double, atan) + +FUNC2 (atan2, float, float, atan2f) +FUNC2 (atan2, double, double, atan2) + +#define CAST_TO(rtype) \ +FUNC1 (cast, rtype, int, (rtype)) \ +FUNC1 (cast, rtype, uint, (rtype)) \ +FUNC1 (cast, rtype, size_t, (rtype)) \ +FUNC1 (cast, rtype, float, (rtype)) \ +FUNC1 (cast, rtype, double, (rtype)) + +CAST_TO (int) +CAST_TO (uint) +CAST_TO (size_t) +CAST_TO (float) +CAST_TO (double) +#undef CAST_TO + +static void +cast_plitem (const exprval_t **params, exprval_t *result, exprctx_t *context) +{ + // first argument is ignored because cexpr_cast_plitem is meant to used + // in an operator list + cexpr_cast_plitem (0, params[0], result, context); +} + +FUNC2 (min, int, int, min) +FUNC2 (min, uint, uint, min) +FUNC2 (min, size_t, size_t, min) +FUNC2 (min, float, float, min) +FUNC2 (min, double, double, min) + +FUNC2 (max, int, int, max) +FUNC2 (max, uint, uint, max) +FUNC2 (max, size_t, size_t, max) +FUNC2 (max, float, float, max) +FUNC2 (max, double, double, max) + +FUNC3 (bound, int, int, bound) +FUNC3 (bound, uint, uint, bound) +FUNC3 (bound, size_t, size_t, bound) +FUNC3 (bound, float, float, bound) +FUNC3 (bound, double, double, bound) + +static exprtype_t *vector_params[] = { + &cexpr_vector, + &cexpr_vector, +}; + +static exprtype_t *int_params[] = { + &cexpr_int, + &cexpr_int, + &cexpr_int, +}; + +static exprtype_t *uint_params[] = { + &cexpr_uint, + &cexpr_uint, + &cexpr_uint, +}; + +static exprtype_t *size_t_params[] = { + &cexpr_size_t, + &cexpr_size_t, + &cexpr_size_t, +}; + +static exprtype_t *float_params[] = { + &cexpr_float, + &cexpr_float, + &cexpr_float, +}; + +static exprtype_t *double_params[] = { + &cexpr_double, + &cexpr_double, + &cexpr_double, +}; + +static exprtype_t *plitem_params[] = { + &cexpr_plitem, + &cexpr_plitem, + &cexpr_plitem, +}; + +#define FUNC(name, rtype, n, ptype) \ + { &cexpr_##rtype, n, ptype##_params, FNAME(name, rtype, ptype) } + +static exprfunc_t dot_func[] = { + FUNC (dot, vector, 2, vector), + {} +}; + +static exprfunc_t sin_func[] = { + FUNC (sin, float, 1, float), + FUNC (sin, double, 1, double), + {} +}; + +static exprfunc_t cos_func[] = { + FUNC (cos, float, 1, float), + FUNC (cos, double, 1, double), + {} +}; + +static exprfunc_t tan_func[] = { + FUNC (tan, float, 1, float), + FUNC (tan, double, 1, double), + {} +}; + +static exprfunc_t asin_func[] = { + FUNC (asin, float, 1, float), + FUNC (asin, double, 1, double), + {} +}; + +static exprfunc_t acos_func[] = { + FUNC (acos, float, 1, float), + FUNC (acos, double, 1, double), + {} +}; + +static exprfunc_t atan_func[] = { + FUNC (atan, float, 1, float), + FUNC (atan, double, 1, double), + {} +}; + +static exprfunc_t atan2_func[] = { + FUNC (atan2, float, 2, float), + FUNC (atan2, double, 2, double), + {} +}; + +#define CAST_TO(rtype) \ +static exprfunc_t rtype##_func[] = { \ + FUNC (cast, rtype, 1, int), \ + FUNC (cast, rtype, 1, uint), \ + FUNC (cast, rtype, 1, size_t), \ + FUNC (cast, rtype, 1, float), \ + FUNC (cast, rtype, 1, double), \ + { &cexpr_##rtype, 1, plitem_params, cast_plitem }, \ + {} \ +}; + +CAST_TO (int) +CAST_TO (uint) +CAST_TO (size_t) +CAST_TO (float) +CAST_TO (double) +#undef CAST_TO + +static exprfunc_t min_func[] = { + FUNC (min, int, 2, int), + FUNC (min, uint, 2, uint), + FUNC (min, size_t, 2, size_t), + FUNC (min, float, 2, float), + FUNC (min, double, 2, double), + {} +}; + +static exprfunc_t max_func[] = { + FUNC (max, int, 2, int), + FUNC (max, uint, 2, uint), + FUNC (max, size_t, 2, size_t), + FUNC (max, float, 2, float), + FUNC (max, double, 2, double), + {} +}; + +static exprfunc_t bound_func[] = { + FUNC (bound, int, 3, int), + FUNC (bound, uint, 3, uint), + FUNC (bound, size_t, 3, size_t), + FUNC (bound, float, 3, float), + FUNC (bound, double, 3, double), + {} +}; + +VISIBLE exprsym_t cexpr_lib_symbols[] = { + { "dot", &cexpr_function, dot_func }, + { "sin", &cexpr_function, sin_func }, + { "cos", &cexpr_function, cos_func }, + { "tan", &cexpr_function, tan_func }, + { "asin", &cexpr_function, asin_func }, + { "acos", &cexpr_function, acos_func }, + { "atan", &cexpr_function, atan_func }, + { "atan2", &cexpr_function, atan2_func }, + { "int", &cexpr_function, int_func }, + { "uint", &cexpr_function, uint_func }, + { "size_t", &cexpr_function, size_t_func }, + { "float", &cexpr_function, float_func }, + { "double", &cexpr_function, double_func }, + { "min", &cexpr_function, min_func }, + { "max", &cexpr_function, max_func }, + { "bound", &cexpr_function, bound_func }, + {} +}; diff --git a/libs/util/cexpr-parse.y b/libs/util/cexpr-parse.y new file mode 100644 index 000000000..900073038 --- /dev/null +++ b/libs/util/cexpr-parse.y @@ -0,0 +1,437 @@ +/* + cexpr-parse.y + + Config expression parser. Or concurrent. + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +%define api.prefix {cexpr_yy} +%define api.pure full +%define api.push-pull push +%define parse.trace +%parse-param {void *scanner} {exprctx_t *context} + +%{ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include + +#include "QF/cmem.h" +#include "QF/dstring.h" +#include "QF/hash.h" +#include "QF/plist.h" +#include "QF/sys.h" + +#include "QF/cexpr.h" + +static exprval_t *binary_expr (int op, const exprval_t *a, const exprval_t *b, + exprctx_t *context); +static exprval_t *field_expr (const exprval_t *a, const exprval_t *b, + exprctx_t *context); +static exprval_t *index_expr (const exprval_t *a, const exprval_t *b, + exprctx_t *context); +static exprval_t *unary_expr (int op, const exprval_t *val, + exprctx_t *context); +static exprval_t *vector_expr (exprlist_t *list, exprctx_t *context); +static exprval_t *function_expr (exprsym_t *fsym, exprlist_t *list, + exprctx_t *context); +static exprlist_t *expr_item (exprval_t *val, exprctx_t *context); + +static void +yyerror (void *scanner, exprctx_t *context, const char *s) +{ + cexpr_error (context, "%s before %s", s, cexpr_yyget_text (scanner)); +} + +%} + +%left COMMA +%right '=' ASX +%right '?' ':' +%left OR +%left AND +%left '|' +%left '^' +%left '&' +%left EQ NE +%left LE GE LT GT + +%left SHL SHR +%left '+' '-' +%left '*' '/' '%' MOD +%right SIZEOF UNARY INCOP +%left HYPERUNARY +%left '.' '(' '[' + +%token NAME +%token VALUE + +%type expr field uexpr +%type opt_arg_list arg_list arg_expr + +%union { + int op; + exprsym_t *symbol; + exprval_t *value; + exprlist_t *list; + const char *string; +} + +%% + +start + : expr { cexpr_assign_value (context->result, $1, context); } + ; + +uexpr + : NAME + { + if ($1->value) { + $$ = (exprval_t *) cmemalloc (context->memsuper, sizeof (*$$)); + $$->type = $1->type; + $$->value = $1->value; + } else { + cexpr_error (context, "undefined identifier %s", $1->name); + $$ = 0; + } + } + | VALUE + | '[' arg_list ']' { $$ = vector_expr ($2, context); } + | '(' expr ')' { $$ = $2; } + | NAME '(' opt_arg_list ')' { $$ = function_expr ($1, $3, context); } + | uexpr '.' field { $$ = field_expr ($1, $3, context); } + | uexpr '[' expr ']' { $$ = index_expr ($1, $3, context); } + | '+' uexpr %prec UNARY { $$ = $2; } + | '-' uexpr %prec UNARY { $$ = unary_expr ('-', $2, context); } + | '!' uexpr %prec UNARY { $$ = unary_expr ('!', $2, context); } + | '~' uexpr %prec UNARY { $$ = unary_expr ('~', $2, context); } + ; + +expr + : uexpr + | expr '=' expr { $$ = cexpr_assign_value ($1, $3, context); } + | expr SHL expr { $$ = binary_expr (SHL, $1, $3, context); } + | expr SHR expr { $$ = binary_expr (SHR, $1, $3, context); } + | expr '+' expr { $$ = binary_expr ('+', $1, $3, context); } + | expr '-' expr { $$ = binary_expr ('-', $1, $3, context); } + | expr '*' expr { $$ = binary_expr ('*', $1, $3, context); } + | expr '/' expr { $$ = binary_expr ('/', $1, $3, context); } + | expr '&' expr { $$ = binary_expr ('&', $1, $3, context); } + | expr '|' expr { $$ = binary_expr ('|', $1, $3, context); } + | expr '^' expr { $$ = binary_expr ('^', $1, $3, context); } + | expr '%' expr { $$ = binary_expr ('%', $1, $3, context); } + | expr MOD expr { $$ = binary_expr (MOD, $1, $3, context); } + ; + +field + : NAME + { + exprctx_t *ctx = context; + const char *name = cexpr_yyget_text (scanner); + size_t size = strlen (name) + 1; + //FIXME reuse strings + $$ = (exprval_t *) cmemalloc (ctx->memsuper, sizeof (exprval_t)); + $$->type = &cexpr_field; + $$->value = cmemalloc (ctx->memsuper, size); + memcpy ($$->value, name, size); + } + ; + +opt_arg_list + : { $$ = 0; } + | arg_list { $$ = $1; } + ; + +arg_list + : arg_expr + | arg_list ',' arg_expr + { + $3-> next = $1; + $$ = $3; + } + +arg_expr + : expr { $$ = expr_item ($1, context); } + ; + +%% + +exprval_t * +cexpr_assign_value (exprval_t *dst, const exprval_t *src, exprctx_t *context) +{ + binop_t *binop = 0; + if (!dst || !src) { + return 0; + } + if (dst->type == &cexpr_exprval) { + *(exprval_t **) dst->value = (exprval_t *) src; + return dst; + } + if (dst->type) { + binop = cexpr_find_cast (dst->type, src->type); + } + if (binop && binop->op) { + binop->func (dst, src, dst, context); + } else { + if (!dst->type) { + dst->type = src->type; + dst->value = src->value; + } else { + if (dst->type != src->type) { + cexpr_error (context, + "type mismatch in expression result: %s = %s", + dst->type->name, src->type->name); + return dst; + } + memcpy (dst->value, src->value, dst->type->size); + } + } + return dst; +} + +static exprval_t * +binary_expr (int op, const exprval_t *a, const exprval_t *b, + exprctx_t *context) +{ + binop_t *binop; + + for (binop = a->type->binops; binop->op; binop++) { + exprtype_t *otype = binop->other; + if (!otype) { + otype = a->type; + } + if (binop->op == op && otype == b->type) { + break; + } + } + exprtype_t *rtype = binop->result; + if (!rtype) { + rtype = a->type; + } + exprval_t *result = cexpr_value (rtype, context); + if (!binop->op) { + cexpr_error (context, "invalid binary expression: %s %c %s", + a->type->name, op, b->type->name); + memset (result->value, 0, rtype->size); + } else { + binop->func (a, b, result, context); + } + return result; +} + +static exprval_t * +field_expr (const exprval_t *a, const exprval_t *b, exprctx_t *context) +{ + binop_t *binop; + exprval_t *result = 0; + + if (!a) { + return 0; + } + + for (binop = a->type->binops; binop->op; binop++) { + if (binop->op == '.' && binop->other == b->type) { + break; + } + } + if (!binop->op) { + cexpr_error (context, "invalid binary expression: %s.%s", + a->type->name, b->type->name); + result = cexpr_value (&cexpr_int, context); + *(int *) result->value = 0; + } else { + exprval_t c = { 0, &result }; + binop->func (a, b, &c, context); + } + return result; +} + +static exprval_t * +index_expr (const exprval_t *a, const exprval_t *b, exprctx_t *context) +{ + binop_t *binop; + exprval_t *result = 0; + + if (!a || !b) { + return 0; + } + for (binop = a->type->binops; binop->op; binop++) { + if (binop->op == '[' && binop->other == b->type) { + break; + } + } + if (!binop->op) { + cexpr_error (context, "invalid index expression: %s.%s", + a->type->name, b->type->name); + result = cexpr_value (&cexpr_int, context); + *(int *) result->value = 0; + } else { + exprval_t c = { 0, &result }; + binop->func (a, b, &c, context); + } + return result; +} + +static exprval_t * +unary_expr (int op, const exprval_t *val, exprctx_t *context) +{ + unop_t *unop; + + for (unop = val->type->unops; unop->op; unop++) { + if (unop->op == op) { + break; + } + } + exprtype_t *rtype = unop->result; + if (!rtype) { + rtype = val->type; + } + exprval_t *result = cexpr_value (rtype, context); + if (!unop->op) { + cexpr_error (context, "invalid unary expression: %c %s", + op, val->type->name); + } else { + unop->func (val, result, context); + } + return result; +} + +exprval_t * +vector_expr (exprlist_t *list, exprctx_t *context) +{ + exprlist_t *l; + exprval_t *val = cexpr_value (&cexpr_vector, context); + float *vector = val->value; + int i; + exprlist_t *rlist = 0; + + // list is built in reverse order, so need to reverse it to make converting + // to an array easier + while (list) { + exprlist_t *t = list->next; + list->next = rlist; + rlist = list; + list = t; + } + list = rlist; + + for (i = 0; i < 4 && list; i++, list = l) { + exprval_t dst = { &cexpr_float, &vector[i] }; + exprval_t *src = list->value; + binop_t *cast = cexpr_find_cast (&cexpr_float, src->type); + if (cast) { + cast->func (&dst, src, &dst, context); + } else { + cexpr_error (context, "invalid vector expression type: [%d] %s", + i, val->type->name); + } + l = list->next; + cmemfree (context->memsuper, list); + } + if (i == 4 && list) { + cexpr_error (context, "excess elements in vector expression"); + } + for ( ; i < 4; i++) { + vector[i] = 0; + } + return val; +} + +static exprval_t *function_expr (exprsym_t *fsym, exprlist_t *list, + exprctx_t *context) +{ + exprlist_t *l; + int num_args = 1;// one extra for terminating null + exprfunc_t *func = 0; + exprval_t *result; + + for (l = list; l; l = l->next) { + num_args++; + } + __auto_type args = (const exprval_t **) alloca (num_args * sizeof (exprval_t *)); + args[num_args - 1] = 0; // terminate array of args for varargs functions + __auto_type types = (exprtype_t **) alloca (num_args * sizeof (exprtype_t *)); + for (num_args = 0; list; list = l, num_args++) { + args[num_args] = list->value; + types[num_args] = list->value->type; + l = list->next; + cmemfree (context->memsuper, list); + } + if (fsym->type != &cexpr_function) { + cexpr_error (context, "invalid function %s", fsym->name); + result = cexpr_value (&cexpr_int, context); + *(int *) result->value = 0; + return result; + } + for (exprfunc_t *f = fsym->value; f->func; f++) { + if (!f->result) { + continue; + } + int num_params = f->num_params; + if (num_params >= 0 && num_args == num_params) { + } else if (num_params < 0 && num_args >= ~num_params) { + num_params = ~num_params; + } else { + continue; + } + if (!num_params + || memcmp (f->param_types, types, + num_args * sizeof (exprtype_t *)) == 0) { + func = f; + break; + } + } + if (!func) { + dstring_t *argstr = dstring_newstr(); + for (int i = 0; i < num_args; i++) { + dasprintf (argstr, "%s%s", types[i]->name, + i + 1 < num_args ? ", ": ""); + } + cexpr_error (context, "no overload for %s(%s)", fsym->name, + argstr->str); + dstring_delete (argstr); + result = cexpr_value (&cexpr_int, context); + *(int *) result->value = 0; + return result; + } + result = cexpr_value (func->result, context); + func->func (args, result, context); + return result; +} + +static exprlist_t * +expr_item (exprval_t *val, exprctx_t *context) +{ + __auto_type item = (exprlist_t *) cmemalloc (context->memsuper, + sizeof (exprlist_t)); + item->next = 0; + item->value = val; + return item; +} diff --git a/libs/util/cexpr-type.c b/libs/util/cexpr-type.c new file mode 100644 index 000000000..3eff83c57 --- /dev/null +++ b/libs/util/cexpr-type.c @@ -0,0 +1,931 @@ +/* + cexpr-type.c + + Config expression parser. Or concurrent. + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include + +#include "QF/cexpr.h" +#include "QF/cmem.h" +#include "QF/mathlib.h" +#include "QF/plist.h" +#include "QF/sys.h" +#include "QF/va.h" +#include "QF/simd/vec4f.h" + +#include "libs/util/cexpr-parse.h" + +#undef uint +#define uint unsigned + +#define BINOP(pre, opname, type, op) \ +static void \ +pre##_##opname (const exprval_t *a, const exprval_t *b, exprval_t *c, \ + exprctx_t *ctx) \ +{ \ + (*(type *) c->value) = (*(type *) a->value) op (*(type *) b->value); \ +} + +#define CASTOP(dst_type, src_type) \ +static void \ +dst_type##_##cast##_##src_type (const exprval_t *a, const exprval_t *src, \ + exprval_t *res, exprctx_t *ctx) \ +{ \ + (*(dst_type *) res->value) = *(src_type *) src->value; \ +} + +#define UNOP(pre, opname, type, op) \ +static void \ +pre##_##opname (const exprval_t *a, exprval_t *b, exprctx_t *ctx) \ +{ \ + (*(type *) b->value) = op (*(type *) a->value); \ +} + +BINOP(bool, and, bool, &) +BINOP(bool, or, bool, |) +BINOP(bool, xor, bool, ^) + +UNOP(bool, not, bool, !) + +static const char * +bool_get_string (const exprval_t *val, va_ctx_t *va_ctx) +{ + return val->value ? "true" : "false"; +} + +binop_t bool_binops[] = { + { '&', &cexpr_bool, &cexpr_bool, bool_and }, + { '|', &cexpr_bool, &cexpr_bool, bool_or }, + { '^', &cexpr_bool, &cexpr_bool, bool_xor }, + { '=', &cexpr_plitem, &cexpr_bool, cexpr_cast_plitem }, + {} +}; + +unop_t bool_unops[] = { + { '!', &cexpr_bool, bool_not }, + {} +}; + +exprtype_t cexpr_bool = { + .name = "bool", + .size = sizeof (bool), + .binops = bool_binops, + .unops = bool_unops, + .data = &cexpr_bool_enum, + .get_string = bool_get_string, +}; + +static int bool_values[] = { + false, + true, +}; +static exprsym_t bool_symbols[] = { + {"false", &cexpr_bool, bool_values + 0}, + {"true", &cexpr_bool, bool_values + 1}, + {} +}; +static exprtab_t bool_symtab = { + bool_symbols, +}; +exprenum_t cexpr_bool_enum = { + &cexpr_bool, + &bool_symtab, +}; + +BINOP(int, shl, int, <<) +BINOP(int, shr, int, >>) +BINOP(int, add, int, +) +BINOP(int, sub, int, -) +BINOP(int, mul, int, *) +BINOP(int, div, int, /) +BINOP(int, band, int, &) +BINOP(int, bor, int, |) +BINOP(int, xor, int, ^) +BINOP(int, rem, int, %) + +UNOP(int, pos, int, +) +UNOP(int, neg, int, -) +UNOP(int, tnot, int, !) +UNOP(int, bnot, int, ~) + +static void +int_mod (const exprval_t *val1, const exprval_t *val2, exprval_t *result, + exprctx_t *ctx) +{ + // implement true modulo for integers: + // 5 mod 3 = 2 + // -5 mod 3 = 1 + // 5 mod -3 = -1 + // -5 mod -3 = -2 + int a = *(int *) val1->value; + int b = *(int *) val2->value; + int c = a % b; + // % is really remainder and so has the same sign rules + // as division: -5 % 3 = -2, so need to add b (3 here) + // if c's sign is incorrect, but only if c is non-zero + int mask = (a ^ b) >> 31; + mask &= ~(!!c + 0) + 1; // +0 to convert bool to int (gcc) + *(int *) result->value = c + (mask & b); +} + +static const char * +int_get_string (const exprval_t *val, va_ctx_t *va_ctx) +{ + return va (va_ctx, "%d", *(int *) val->value); +} + +binop_t int_binops[] = { + { SHL, &cexpr_int, &cexpr_int, int_shl }, + { SHR, &cexpr_int, &cexpr_int, int_shr }, + { '+', &cexpr_int, &cexpr_int, int_add }, + { '-', &cexpr_int, &cexpr_int, int_sub }, + { '*', &cexpr_int, &cexpr_int, int_mul }, + { '/', &cexpr_int, &cexpr_int, int_div }, + { '&', &cexpr_int, &cexpr_int, int_band }, + { '|', &cexpr_int, &cexpr_int, int_bor }, + { '^', &cexpr_int, &cexpr_int, int_xor }, + { '%', &cexpr_int, &cexpr_int, int_rem }, + { MOD, &cexpr_int, &cexpr_int, int_mod }, + { '=', &cexpr_plitem, &cexpr_int, cexpr_cast_plitem }, + {} +}; + +unop_t int_unops[] = { + { '+', &cexpr_int, int_pos }, + { '-', &cexpr_int, int_neg }, + { '!', &cexpr_int, int_tnot }, + { '~', &cexpr_int, int_bnot }, + {} +}; + +exprtype_t cexpr_int = { + .name = "int", + .size = sizeof (int), + .binops = int_binops, + .unops = int_unops, + .get_string = int_get_string, +}; + +BINOP(uint, shl, unsigned, <<) +BINOP(uint, shr, unsigned, >>) +BINOP(uint, add, unsigned, +) +BINOP(uint, sub, unsigned, -) +BINOP(uint, mul, unsigned, *) +BINOP(uint, div, unsigned, /) +BINOP(uint, band, unsigned, &) +BINOP(uint, bor, unsigned, |) +BINOP(uint, xor, unsigned, ^) +BINOP(uint, rem, unsigned, %) + +static void +uint_cast_int (const exprval_t *val1, const exprval_t *src, exprval_t *result, + exprctx_t *ctx) +{ + *(unsigned *) result->value = *(int *) src->value; +} + +UNOP(uint, pos, unsigned, +) +UNOP(uint, neg, unsigned, -) +UNOP(uint, tnot, unsigned, !) +UNOP(uint, bnot, unsigned, ~) + +static const char * +uint_get_string (const exprval_t *val, va_ctx_t *va_ctx) +{ + return va (va_ctx, "%u", *(unsigned *) val->value); +} + +binop_t uint_binops[] = { + { SHL, &cexpr_uint, &cexpr_uint, uint_shl }, + { SHR, &cexpr_uint, &cexpr_uint, uint_shr }, + { '+', &cexpr_uint, &cexpr_uint, uint_add }, + { '-', &cexpr_uint, &cexpr_uint, uint_sub }, + { '*', &cexpr_uint, &cexpr_uint, uint_mul }, + { '/', &cexpr_uint, &cexpr_uint, uint_div }, + { '&', &cexpr_uint, &cexpr_uint, uint_band }, + { '|', &cexpr_uint, &cexpr_uint, uint_bor }, + { '^', &cexpr_uint, &cexpr_uint, uint_xor }, + { '%', &cexpr_uint, &cexpr_uint, uint_rem }, + { MOD, &cexpr_uint, &cexpr_uint, uint_rem }, + { '=', &cexpr_int, &cexpr_uint, uint_cast_int }, + { '=', &cexpr_plitem, &cexpr_uint, cexpr_cast_plitem }, + {} +}; + +unop_t uint_unops[] = { + { '+', &cexpr_uint, uint_pos }, + { '-', &cexpr_uint, uint_neg }, + { '!', &cexpr_uint, uint_tnot }, + { '~', &cexpr_uint, uint_bnot }, + {} +}; + +exprtype_t cexpr_uint = { + .name = "uint", + .size = sizeof (unsigned), + .binops = uint_binops, + .unops = uint_unops, + .get_string = uint_get_string, +}; + +BINOP(size_t, shl, unsigned, <<) +BINOP(size_t, shr, unsigned, >>) +BINOP(size_t, add, unsigned, +) +BINOP(size_t, sub, unsigned, -) +BINOP(size_t, mul, unsigned, *) +BINOP(size_t, div, unsigned, /) +BINOP(size_t, band, unsigned, &) +BINOP(size_t, bor, unsigned, |) +BINOP(size_t, xor, unsigned, ^) +BINOP(size_t, rem, unsigned, %) + +static void +size_t_cast_int (const exprval_t *val1, const exprval_t *src, + exprval_t *result, exprctx_t *ctx) +{ + int val = *(int *) src->value; + if (val < 0) { + PL_Message (ctx->messages, ctx->item, "int value clamped to 0: %d", + val); + val = 0; + } + *(size_t *) result->value = val; +} + +static void +size_t_cast_uint (const exprval_t *val1, const exprval_t *src, + exprval_t *result, exprctx_t *ctx) +{ + *(size_t *) result->value = *(unsigned *) src->value; +} + +UNOP(size_t, pos, unsigned, +) +UNOP(size_t, neg, unsigned, -) +UNOP(size_t, tnot, unsigned, !) +UNOP(size_t, bnot, unsigned, ~) + +static const char * +size_t_get_string (const exprval_t *val, va_ctx_t *va_ctx) +{ + return va (va_ctx, "%zd", *(size_t *) val->value); +} + +binop_t size_t_binops[] = { + { SHL, &cexpr_size_t, &cexpr_size_t, size_t_shl }, + { SHR, &cexpr_size_t, &cexpr_size_t, size_t_shr }, + { '+', &cexpr_size_t, &cexpr_size_t, size_t_add }, + { '-', &cexpr_size_t, &cexpr_size_t, size_t_sub }, + { '*', &cexpr_size_t, &cexpr_size_t, size_t_mul }, + { '/', &cexpr_size_t, &cexpr_size_t, size_t_div }, + { '&', &cexpr_size_t, &cexpr_size_t, size_t_band }, + { '|', &cexpr_size_t, &cexpr_size_t, size_t_bor }, + { '^', &cexpr_size_t, &cexpr_size_t, size_t_xor }, + { '%', &cexpr_size_t, &cexpr_size_t, size_t_rem }, + { MOD, &cexpr_size_t, &cexpr_size_t, size_t_rem }, + { '=', &cexpr_int, &cexpr_size_t, size_t_cast_int }, + { '=', &cexpr_uint, &cexpr_size_t, size_t_cast_uint }, + { '=', &cexpr_plitem, &cexpr_size_t, cexpr_cast_plitem }, + {} +}; + +unop_t size_t_unops[] = { + { '+', &cexpr_size_t, size_t_pos }, + { '-', &cexpr_size_t, size_t_neg }, + { '!', &cexpr_size_t, size_t_tnot }, + { '~', &cexpr_size_t, size_t_bnot }, + {} +}; + +exprtype_t cexpr_size_t = { + .name = "size_t", + .size = sizeof (size_t), + .binops = size_t_binops, + .unops = size_t_unops, + .get_string = size_t_get_string, +}; + +BINOP(float, add, float, +) +BINOP(float, sub, float, -) +BINOP(float, mul, float, *) +BINOP(float, div, float, /) + +static void +float_rem (const exprval_t *val1, const exprval_t *val2, exprval_t *result, + exprctx_t *ctx) +{ + float a = *(float *) val1->value; + float b = *(float *) val2->value; + *(float *) result->value = a - b * truncf (a / b); +} + +static void +float_mod (const exprval_t *val1, const exprval_t *val2, exprval_t *result, + exprctx_t *ctx) +{ + // implement true modulo for floats: + // 5 mod 3 = 2 + // -5 mod 3 = 1 + // 5 mod -3 = -1 + // -5 mod -3 = -2 + float a = *(float *) val1->value; + float b = *(float *) val2->value; + *(float *) result->value = a - b * floorf (a / b); +} + +static void +float_mul_vec4f (const exprval_t *val1, const exprval_t *val2, + exprval_t *result, exprctx_t *ctx) +{ + float s = *(float *) val1->value; + __auto_type v = (vec4f_t *) val2->value; + __auto_type r = (vec4f_t *) result->value; + *r = s * *v; +} + +static void +float_div_quat (const exprval_t *val1, const exprval_t *val2, + exprval_t *result, exprctx_t *ctx) +{ + float a = *(float *) val1->value; + vec4f_t b = *(vec4f_t *) val2->value; + __auto_type c = (vec4f_t *) result->value; + *c = a * qconjf (b) / dotf (b, b); +} + +CASTOP (float, int) +CASTOP (float, uint) +CASTOP (float, double) + +UNOP(float, pos, float, +) +UNOP(float, neg, float, -) +UNOP(float, tnot, float, !) + +static const char * +float_get_string (const exprval_t *val, va_ctx_t *va_ctx) +{ + return va (va_ctx, "%.9g", *(float *) val->value); +} + +binop_t float_binops[] = { + { '+', &cexpr_float, &cexpr_float, float_add }, + { '-', &cexpr_float, &cexpr_float, float_sub }, + { '*', &cexpr_float, &cexpr_float, float_mul }, + { '*', &cexpr_vector, &cexpr_vector, float_mul_vec4f }, + { '*', &cexpr_quaternion, &cexpr_quaternion, float_mul_vec4f }, + { '/', &cexpr_float, &cexpr_float, float_div }, + { '/', &cexpr_quaternion, &cexpr_quaternion, float_div_quat }, + { '%', &cexpr_float, &cexpr_float, float_rem }, + { MOD, &cexpr_float, &cexpr_float, float_mod }, + { '=', &cexpr_int, &cexpr_float, float_cast_int }, + { '=', &cexpr_uint, &cexpr_float, float_cast_uint }, + { '=', &cexpr_double, &cexpr_float, float_cast_double }, + { '=', &cexpr_plitem, &cexpr_float, cexpr_cast_plitem }, + {} +}; + +unop_t float_unops[] = { + { '+', &cexpr_float, float_pos }, + { '-', &cexpr_float, float_neg }, + { '!', &cexpr_float, float_tnot }, + {} +}; + +exprtype_t cexpr_float = { + .name = "float", + .size = sizeof (float), + .binops = float_binops, + .unops = float_unops, + .get_string = float_get_string, +}; + +BINOP(double, add, double, +) +BINOP(double, sub, double, -) +BINOP(double, mul, double, *) +BINOP(double, div, double, /) + +static void +double_rem (const exprval_t *val1, const exprval_t *val2, exprval_t *result, + exprctx_t *ctx) +{ + double a = *(double *) val1->value; + double b = *(double *) val2->value; + *(double *) result->value = a - b * trunc (a / b); +} + +static void +double_mod (const exprval_t *val1, const exprval_t *val2, exprval_t *result, + exprctx_t *ctx) +{ + // implement true modulo for doubles: + // 5 mod 3 = 2 + // -5 mod 3 = 1 + // 5 mod -3 = -1 + // -5 mod -3 = -2 + double a = *(double *) val1->value; + double b = *(double *) val2->value; + *(double *) result->value = a - b * floor (a / b); +} + +CASTOP (double, int) +CASTOP (double, uint) +CASTOP (double, float) + +UNOP(double, pos, double, +) +UNOP(double, neg, double, -) +UNOP(double, tnot, double, !) + +static const char * +double_get_string (const exprval_t *val, va_ctx_t *va_ctx) +{ + return va (va_ctx, "%.17g", *(double *) val->value); +} + +binop_t double_binops[] = { + { '+', &cexpr_double, &cexpr_double, double_add }, + { '-', &cexpr_double, &cexpr_double, double_sub }, + { '*', &cexpr_double, &cexpr_double, double_mul }, + { '/', &cexpr_double, &cexpr_double, double_div }, + { '%', &cexpr_double, &cexpr_double, double_rem }, + { MOD, &cexpr_double, &cexpr_double, double_mod }, + { '=', &cexpr_int, &cexpr_double, double_cast_int }, + { '=', &cexpr_uint, &cexpr_double, double_cast_uint }, + { '=', &cexpr_uint, &cexpr_double, double_cast_float }, + { '=', &cexpr_plitem, &cexpr_double, cexpr_cast_plitem }, + {} +}; + +unop_t double_unops[] = { + { '+', &cexpr_double, double_pos }, + { '-', &cexpr_double, double_neg }, + { '!', &cexpr_double, double_tnot }, + {} +}; + +exprtype_t cexpr_double = { + .name = "double", + .size = sizeof (double), + .binops = double_binops, + .unops = double_unops, + .get_string = double_get_string, +}; + +BINOP(vector, add, vec4f_t, +) +BINOP(vector, sub, vec4f_t, -) +BINOP(vector, mul, vec4f_t, *) +BINOP(vector, div, vec4f_t, /) + +static void +vector_quaternion_mul (const exprval_t *val1, const exprval_t *val2, + exprval_t *result, exprctx_t *ctx) +{ + vec4f_t a = *(vec4f_t *) val1->value; + vec4f_t b = *(vec4f_t *) val2->value; + __auto_type c = (vec4f_t *) result->value; + *c = vqmulf (a, b); +} + +static void +vector_rem (const exprval_t *val1, const exprval_t *val2, exprval_t *result, + exprctx_t *ctx) +{ + vec4f_t a = *(vec4f_t *) val1->value; + vec4f_t b = *(vec4f_t *) val2->value; + __auto_type c = (vec4f_t *) result->value; + *c = a - b * vtrunc4f (a / b); +} + +static void +vector_mod (const exprval_t *val1, const exprval_t *val2, exprval_t *result, + exprctx_t *ctx) +{ + // implement true modulo for doubles: + // 5 mod 3 = 2 + // -5 mod 3 = 1 + // 5 mod -3 = -1 + // -5 mod -3 = -2 + vec4f_t a = *(vec4f_t *) val1->value; + vec4f_t b = *(vec4f_t *) val2->value; + __auto_type c = (vec4f_t *) result->value; + *c = a - b * vfloor4f (a / b); +} + +static void +vector_swizzle (const exprval_t *val1, const exprval_t *val2, + exprval_t *result, exprctx_t *ctx) +{ +#define m(x) (1 << ((x) - 'a')) +#define v(x, mask) (((x) & 0x60) == 0x60 && (m(x) & (mask))) +#define vind(x) ((x) & 3) +#define cind(x) (-(((x) >> 3) ^ (x)) & 3) + const int color = m('r') | m('g') | m('b') | m('a'); + const int vector = m('x') | m('y') | m('z') | m('w'); + float *vec = val1->value; + const char *name = val2->value; + exprval_t *val = 0; + + if (v (name[0], vector) && v (name[1], vector) + && v (name[2], vector) && v (name[3], vector) && !name[4]) { + val = cexpr_value (&cexpr_vector, ctx); + float *res = val->value; + res[0] = vec[vind (name[0])]; + res[1] = vec[vind (name[1])]; + res[2] = vec[vind (name[2])]; + res[3] = vec[vind (name[3])]; + } else if (v (name[0], color) && v (name[1], color) + && v (name[2], color) && v (name[3], color) && !name[4]) { + val = cexpr_value (&cexpr_vector, ctx); + float *res = val->value; + res[0] = vec[cind (name[0])]; + res[1] = vec[cind (name[1])]; + res[2] = vec[cind (name[2])]; + res[3] = vec[cind (name[3])]; + } else if (v (name[0], vector) && v (name[1], vector) + && v (name[2], vector) && !name[3]) { + val = cexpr_value (&cexpr_vector, ctx); + float *res = val->value; + res[0] = vec[vind (name[0])]; + res[1] = vec[vind (name[1])]; + res[2] = vec[vind (name[2])]; + res[3] = 0; + } else if (v (name[0], color) && v (name[1], color) + && v (name[2], color) && !name[3]) { + val = cexpr_value (&cexpr_vector, ctx); + float *res = val->value; + res[0] = vec[cind (name[0])]; + res[1] = vec[cind (name[1])]; + res[2] = vec[cind (name[2])]; + res[3] = 0; + } else if (v (name[0], vector) && v (name[1], vector) && !name[2]) { + val = cexpr_value (&cexpr_vector, ctx); + float *res = val->value; + res[0] = vec[vind (name[0])]; + res[1] = vec[vind (name[1])]; + res[2] = 0; + res[3] = 0; + } else if (v (name[0], color) && v (name[1], color) && !name[2]) { + val = cexpr_value (&cexpr_vector, ctx); + float *res = val->value; + res[0] = vec[cind (name[0])]; + res[1] = vec[cind (name[1])]; + res[2] = 0; + res[3] = 0; + } else if (v (name[0], vector) && !name[1]) { + val = cexpr_value (&cexpr_float, ctx); + float *res = val->value; + res[0] = vec[vind (name[0])]; + } else if (v (name[0], color) && !name[1]) { + val = cexpr_value (&cexpr_float, ctx); + float *res = val->value; + res[0] = vec[cind (name[0])]; + } + *(exprval_t **) result->value = val; +} + +UNOP(vector, pos, vec4f_t, +) +UNOP(vector, neg, vec4f_t, -) + +static const char * +vector_get_string (const exprval_t *val, va_ctx_t *va_ctx) +{ + vec4f_t vec = *(vec4f_t *) val->value; + return va (va_ctx, VEC4F_FMT, VEC4_EXP (vec)); +} + +static void +vector_tnot (const exprval_t *val, exprval_t *result, exprctx_t *ctx) +{ + vec4f_t v = *(vec4f_t *) val->value; + __auto_type c = (vec4i_t *) result->value; + *c = v != 0; +} + +binop_t vector_binops[] = { + { '+', &cexpr_vector, &cexpr_vector, vector_add }, + { '-', &cexpr_vector, &cexpr_vector, vector_sub }, + { '*', &cexpr_vector, &cexpr_vector, vector_mul }, + { '*', &cexpr_quaternion, &cexpr_vector, vector_quaternion_mul }, + { '/', &cexpr_vector, &cexpr_vector, vector_div }, + { '%', &cexpr_vector, &cexpr_vector, vector_rem }, + { MOD, &cexpr_vector, &cexpr_vector, vector_mod }, + { '.', &cexpr_field, &cexpr_exprval, vector_swizzle }, + { '=', &cexpr_plitem, &cexpr_vector, cexpr_cast_plitem }, + {} +}; + +unop_t vector_unops[] = { + { '+', &cexpr_vector, vector_pos }, + { '-', &cexpr_vector, vector_neg }, + { '!', &cexpr_vector, vector_tnot }, + {} +}; + +exprtype_t cexpr_vector = { + .name = "vector", + .size = sizeof (vec4f_t), + .binops = vector_binops, + .unops = vector_unops, + .get_string = vector_get_string, +}; + +static void +quaternion_mul (const exprval_t *val1, const exprval_t *val2, + exprval_t *result, exprctx_t *ctx) +{ + vec4f_t a = *(vec4f_t *) val1->value; + vec4f_t b = *(vec4f_t *) val2->value; + __auto_type c = (vec4f_t *) result->value; + *c = qmulf (a, b); +} + +static void +quaternion_vector_mul (const exprval_t *val1, const exprval_t *val2, + exprval_t *result, exprctx_t *ctx) +{ + vec4f_t a = *(vec4f_t *) val1->value; + vec4f_t b = *(vec4f_t *) val2->value; + __auto_type c = (vec4f_t *) result->value; + *c = qvmulf (a, b); +} + +static const char * +quaternion_get_string (const exprval_t *val, va_ctx_t *va_ctx) +{ + vec4f_t vec = *(vec4f_t *) val->value; + return va (va_ctx, VEC4F_FMT, VEC4_EXP (vec)); +} + +binop_t quaternion_binops[] = { + { '+', &cexpr_quaternion, &cexpr_quaternion, vector_add }, + { '-', &cexpr_quaternion, &cexpr_quaternion, vector_sub }, + { '*', &cexpr_quaternion, &cexpr_quaternion, quaternion_mul }, + { '*', &cexpr_vector, &cexpr_vector, quaternion_vector_mul }, + {} +}; + +unop_t quaternion_unops[] = { + { '+', &cexpr_vector, vector_pos }, + { '-', &cexpr_vector, vector_neg }, + { '!', &cexpr_vector, vector_tnot }, + {} +}; + +exprtype_t cexpr_quaternion = { + .name = "quaterion", + .size = sizeof (vec4f_t), + .binops = quaternion_binops, + .unops = quaternion_unops, + .get_string = quaternion_get_string, +}; + +exprtype_t cexpr_exprval = { + .name = "exprval", + .size = sizeof (exprval_t *), + .binops = 0, // can't actually do anything with an exprval + .unops = 0, +}; + +exprtype_t cexpr_field = { + .name = "field", + .size = 0, // has no size of its own, rather, it's the length of the name + .binops = 0, // can't actually do anything with a field + .unops = 0, +}; + +exprtype_t cexpr_function = { + .name = "function", + .size = 0, // has no size of its own + .binops = 0,// can't actually do anything with a function other than call + .unops = 0, +}; + +void +cexpr_cast_plitem (const exprval_t *val1, const exprval_t *src, + exprval_t *result, exprctx_t *ctx) +{ + plitem_t *item = *(plitem_t **) src->value; + const char *str = PL_String (item); + if (!str) { + cexpr_error (ctx, "not a string object: %d", PL_Line (item)); + return; + } + + exprctx_t ectx = *ctx; + ectx.result = result; + cexpr_eval_string (str, &ectx); + ctx->errors += ectx.errors; + if (ectx.errors) { + cexpr_error (ctx, "could not convert: %d", PL_Line (item)); + } +} + +static void +plitem_field (const exprval_t *a, const exprval_t *b, exprval_t *c, + exprctx_t *ctx) +{ + __auto_type dict = *(plitem_t **) a->value; + __auto_type key = (const char *) b->value; + + if (PL_Type (dict) != QFDictionary) { + cexpr_error(ctx, "not a dictionary object"); + return; + } + plitem_t *item = PL_ObjectForKey (dict, key); + exprval_t *val = 0; + if (!item) { + cexpr_error (ctx, "key not found: %s", key); + } else { + val = cexpr_value (&cexpr_plitem, ctx); + *(plitem_t **) val->value = item; + } + *(exprval_t **) c->value = val; +} + +static void +plitem_index (const exprval_t *a, int index, exprval_t *c, + exprctx_t *ctx) +{ + __auto_type array = *(plitem_t **) a->value; + + if (PL_Type (array) != QFArray) { + cexpr_error(ctx, "not an array object"); + return; + } + plitem_t *item = PL_ObjectAtIndex (array, index); + exprval_t *val = 0; + if (!item) { + cexpr_error (ctx, "invalid index: %d", index); + } else { + val = cexpr_value (&cexpr_plitem, ctx); + *(plitem_t **) val->value = item; + } + *(exprval_t **) c->value = val; +} + +static void +plitem_int (const exprval_t *a, const exprval_t *b, exprval_t *c, + exprctx_t *ctx) +{ + int index = *(int *) b->value; + plitem_index (a, index, c, ctx); +} + +static void +plitem_uint (const exprval_t *a, const exprval_t *b, exprval_t *c, + exprctx_t *ctx) +{ + int index = *(unsigned *) b->value; + plitem_index (a, index, c, ctx); +} + +static void +plitem_size_t (const exprval_t *a, const exprval_t *b, exprval_t *c, + exprctx_t *ctx) +{ + int index = *(size_t *) b->value; + plitem_index (a, index, c, ctx); +} + +binop_t plitem_binops[] = { + { '.', &cexpr_field, &cexpr_plitem, plitem_field }, + { '[', &cexpr_int, &cexpr_plitem, plitem_int }, + { '[', &cexpr_uint, &cexpr_plitem, plitem_uint }, + { '[', &cexpr_size_t, &cexpr_plitem, plitem_size_t }, + {} +}; + +exprtype_t cexpr_plitem = { + .name = "plitem", + .size = sizeof (plitem_t *), + .binops = plitem_binops, + .unops = 0, +}; + +exprtype_t cexpr_string = { + .name = "string", + .size = sizeof (char *), +}; + +exprtype_t cexpr_voidptr = { + .name = "voidptr", + .size = sizeof (void *), +}; + +VISIBLE binop_t * +cexpr_find_cast (exprtype_t *dst_type, exprtype_t *src_type) +{ + binop_t *binop = 0; + + for (binop = dst_type->binops; binop && binop->op; binop++) { + if (binop->op == '=' && binop->other == src_type) { + break; + } + } + if (binop && binop->op) { + return binop; + } + return 0; +} + +VISIBLE int +cexpr_parse_enum (exprenum_t *enm, const char *str, const exprctx_t *ctx, + void *data) +{ + exprval_t result = { enm->type, data }; + exprctx_t context = *ctx; + context.symtab = enm->symtab; + context.result = &result; + return cexpr_eval_string (str, &context); +} + +VISIBLE const char * +cexpr_enum_get_string (const exprval_t *val, va_ctx_t *va_ctx) +{ + exprenum_t *enm = val->type->data; + exprsym_t *symbols = enm->symtab->symbols; + for (exprsym_t *sym = symbols; sym->name; sym++) { + // if there are duplicate values, choose the *later* value + if (sym[1].name + && memcmp (sym->value, sym[1].value, val->type->size) == 0) { + continue; + } + if (memcmp (sym->value, val->value, val->type->size) == 0) { + return sym->name; + } + } + return ""; +} + +BINOP(flag, and, int, &) +BINOP(flag, or, int, |) +BINOP(flag, xor, int, ^) + +UNOP(flag, not, int, ~) + +binop_t cexpr_flag_binops[] = { + { '&', 0, 0, flag_and }, + { '|', 0, 0, flag_or }, + { '^', 0, 0, flag_xor }, + { '=', &cexpr_int, 0, uint_cast_int }, + {} +}; + +unop_t cexpr_flag_unops[] = { + { '~', 0, flag_not }, + {} +}; + +VISIBLE const char * +cexpr_flags_get_string (const exprval_t *val, va_ctx_t *va_ctx) +{ + exprenum_t *enm = val->type->data; + exprsym_t *symbols = enm->symtab->symbols; + const char *val_str = 0; + + if (val->type->size != 4) { + Sys_Error ("cexpr_flags_get_string: only 32-bit values supported"); + } + uint32_t flags = *(uint32_t *) val->value; + for (exprsym_t *sym = symbols; sym->name; sym++) { + uint32_t sym_flags = *(uint32_t *) sym->value; + // if there are duplicate values, choose the *later* value + if (sym[1].name && sym_flags == *(uint32_t *) sym[1].value) { + continue; + } + if ((flags & sym_flags) && !(sym_flags & ~flags)) { + if (val_str) { + val_str = va (va_ctx, "%s | %s", val_str, sym->name); + } else { + val_str = sym->name; + } + } + } + if (!val_str) { + val_str = "0"; + } + return val_str; +} diff --git a/libs/util/cexpr-vars.c b/libs/util/cexpr-vars.c new file mode 100644 index 000000000..02fbd56db --- /dev/null +++ b/libs/util/cexpr-vars.c @@ -0,0 +1,186 @@ +/* + cexpr-vars.c + + Config expression parser. Or concurrent. + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include + +#include "QF/cexpr.h" +#include "QF/cmem.h" +#include "QF/cvar.h" +#include "QF/hash.h" + +#include "libs/util/cexpr-parse.h" + +VISIBLE void +cexpr_array_getelement (const exprval_t *a, const exprval_t *b, exprval_t *c, + exprctx_t *ctx) +{ + __auto_type array = (exprarray_t *) a->type->data; + unsigned index = *(const unsigned *) b->value; + exprval_t *val = 0; + if (index < array->size) { + val = cmemalloc (ctx->memsuper, sizeof (exprval_t)); + val->type = array->type; + val->value = a->value + array->type->size * index; + } else { + cexpr_error (ctx, "index %d out of bounds for %s", index, + a->type->name); + } + *(exprval_t **) c->value = val; +} + +VISIBLE binop_t cexpr_array_binops[] = { + { '[', &cexpr_int, &cexpr_exprval, cexpr_array_getelement }, + {} +}; + +VISIBLE void +cexpr_struct_getfield (const exprval_t *a, const exprval_t *b, exprval_t *c, + exprctx_t *ctx) +{ + __auto_type symtab = (exprtab_t *) a->type->data; + __auto_type name = (const char *) b->value; + __auto_type field = (exprsym_t *) Hash_Find (symtab->tab, name); + exprval_t *val = 0; + if (field) { + val = cmemalloc (ctx->memsuper, sizeof (exprval_t)); + val->type = field->type; + val->value = a->value + (ptrdiff_t) field->value; + } else { + cexpr_error (ctx, "%s has no field %s", a->type->name, name); + } + *(exprval_t **) c->value = val; +} + +VISIBLE binop_t cexpr_struct_binops[] = { + { '.', &cexpr_field, &cexpr_exprval, cexpr_struct_getfield }, + {} +}; + +VISIBLE void +cexpr_struct_pointer_getfield (const exprval_t *a, const exprval_t *b, + exprval_t *c, exprctx_t *ctx) +{ + // "dereference" the pointer + exprval_t struct_val = { a->type, *(void **) a->value }; + cexpr_struct_getfield (&struct_val, b, c, ctx); +} + +VISIBLE binop_t cexpr_struct_pointer_binops[] = { + { '.', &cexpr_field, &cexpr_exprval, cexpr_struct_pointer_getfield }, + {} +}; + +static void +cvar_get (const exprval_t *a, const exprval_t *b, exprval_t *c, exprctx_t *ctx) +{ + __auto_type name = (const char *) b->value; + exprval_t *var = cexpr_cvar (name, ctx); + if (!var) { + cexpr_error (ctx, "unknown cvar %s", name); + } + *(exprval_t **) c->value = var; +} + +static binop_t cvar_binops[] = { + { '.', &cexpr_field, &cexpr_exprval, cvar_get }, + {} +}; + +static exprtype_t cvar_type = { + .name = "cvar", + .size = sizeof (void *), // ref to struct (will always be 0) + .binops = cvar_binops, + .unops = 0, +}; + +VISIBLE exprval_t * +cexpr_cvar (const char *name, exprctx_t *ctx) +{ + cvar_t *var = Cvar_FindVar (name); + if (!var) { + var = Cvar_FindAlias (name); + } + if (!var) { + return 0; + } + + exprtype_t *type = ctx->result->type; + if (var->value.type) { + exprval_t *val = cexpr_value (type, ctx); + cexpr_assign_value (val, &var->value, ctx); + return val; + } + cexpr_error (ctx, "cvar %s has unknown type", var->name); + return 0; +} + +VISIBLE exprval_t * +cexpr_cvar_struct (exprctx_t *ctx) +{ + exprval_t *cvars = cexpr_value (&cvar_type, ctx); + *(void **) cvars->value = 0; + return cvars; +} + +static const char * +expr_getkey (const void *s, void *unused) +{ + __auto_type sym = (exprsym_t *) s; + return sym->name; +} + +void +cexpr_init_symtab (exprtab_t *symtab, exprctx_t *ctx) +{ + exprsym_t *sym; + + symtab->tab = Hash_NewTable (61, expr_getkey, 0, 0, ctx->hashctx); + for (sym = symtab->symbols; sym->name; sym++) { + Hash_Add (symtab->tab, sym); + } +} + +VISIBLE exprval_t * +cexpr_value_reference (exprtype_t *type, void *data, exprctx_t *ctx) +{ + __auto_type ref = (exprval_t *) cmemalloc (ctx->memsuper, + sizeof (exprval_t)); + ref->type = type; + ref->value = data; + return ref; +} + +VISIBLE exprval_t * +cexpr_value (exprtype_t *type, exprctx_t *ctx) +{ + exprval_t *val = cexpr_value_reference (type, 0, ctx); + val->value = cmemalloc (ctx->memsuper, type->size); + return val; +} diff --git a/libs/util/cmd.c b/libs/util/cmd.c index dcdbe6855..f73d38d32 100644 --- a/libs/util/cmd.c +++ b/libs/util/cmd.c @@ -65,7 +65,15 @@ typedef struct cmd_provider_s static cmdalias_t *cmd_alias; -VISIBLE cvar_t *cmd_warncmd; +VISIBLE int cmd_warncmd; +static cvar_t cmd_warncmd_cvar = { + .name = "cmd_warncmd", + .description = + "Toggles the display of error messages for unknown commands", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cmd_warncmd }, +}; static hashtab_t *cmd_alias_hash; static hashtab_t *cmd_hash; @@ -118,6 +126,8 @@ Cmd_Command (cbuf_args_t *args) if (cmd) { if (cmd->function) { cmd->function (); + } else if (cmd->datafunc) { + cmd->datafunc (cmd->data); } return 0; } @@ -128,15 +138,14 @@ Cmd_Command (cbuf_args_t *args) return 0; if (cbuf_active->strict) return -1; - else if (cmd_warncmd->int_val || developer->int_val & SYS_DEV) + else if (cmd_warncmd || developer & SYS_dev) Sys_Printf ("Unknown command \"%s\"\n", Cmd_Argv (0)); return 0; } -/* Registers a command and handler function */ -VISIBLE int -Cmd_AddCommand (const char *cmd_name, xcommand_t function, - const char *description) +static int +add_command (const char *cmd_name, xcommand_t func, xdatacmd_t datafunc, + void *data, const char *description) { cmd_function_t *cmd; cmd_function_t **c; @@ -144,7 +153,7 @@ Cmd_AddCommand (const char *cmd_name, xcommand_t function, // fail if the command already exists cmd = (cmd_function_t *) Hash_Find (cmd_hash, cmd_name); if (cmd) { - Sys_MaskPrintf (SYS_DEV, "Cmd_AddCommand: %s already defined\n", + Sys_MaskPrintf (SYS_dev, "Cmd_AddCommand: %s already defined\n", cmd_name); return 0; } @@ -152,7 +161,9 @@ Cmd_AddCommand (const char *cmd_name, xcommand_t function, cmd = calloc (1, sizeof (cmd_function_t)); SYS_CHECKMEM (cmd); cmd->name = cmd_name; - cmd->function = function; + cmd->function = func; + cmd->datafunc = datafunc; + cmd->data = data; cmd->description = description; Hash_Add (cmd_hash, cmd); for (c = &cmd_functions; *c; c = &(*c)->next) @@ -163,6 +174,22 @@ Cmd_AddCommand (const char *cmd_name, xcommand_t function, return 1; } +/* Registers a command and handler function */ +VISIBLE int +Cmd_AddCommand (const char *cmd_name, xcommand_t function, + const char *description) +{ + return add_command (cmd_name, function, 0, 0, description); +} + +/* Registers a command and handler function with data */ +VISIBLE int +Cmd_AddDataCommand (const char *cmd_name, xdatacmd_t function, + void *data, const char *description) +{ + return add_command (cmd_name, 0, function, data, description); +} + /* Unregisters a command */ VISIBLE int Cmd_RemoveCommand (const char *name) @@ -182,7 +209,7 @@ Cmd_RemoveCommand (const char *name) } /* Checks for the existance of a command */ -VISIBLE qboolean +VISIBLE bool Cmd_Exists (const char *cmd_name) { cmd_function_t *cmd; @@ -283,6 +310,13 @@ Cmd_CompleteBuildList (const char *partial) } /* Hash table functions for aliases and commands */ +static void +cmd_free (void *_c, void *unused) +{ + cmd_function_t *cmd = _c; + free (cmd); +} + static void cmd_alias_free (void *_a, void *unused) { @@ -478,29 +512,28 @@ Cmd_Help_f (void) Sys_Printf ("variable/command not found\n"); } + static void Cmd_Exec_f (void) { char *f; - int mark; + size_t mark; if (Cmd_Argc () != 2) { Sys_Printf ("exec : execute a script file\n"); return; } - mark = Hunk_LowMark (); + mark = Hunk_LowMark (0); f = (char *) QFS_LoadHunkFile (QFS_FOpenFile (Cmd_Argv (1))); if (!f) { Sys_Printf ("couldn't exec %s\n", Cmd_Argv (1)); return; } - if (!Cvar_Command () - && (cmd_warncmd->int_val - || (developer && developer->int_val & SYS_DEV))) + if (!Cvar_Command () && (cmd_warncmd || (developer & SYS_dev))) Sys_Printf ("execing %s\n", Cmd_Argv (1)); Cbuf_InsertText (cbuf_active, f); - Hunk_FreeToLowMark (mark); + Hunk_FreeToLowMark (0, mark); } /* @@ -580,12 +613,24 @@ Cmd_StuffCmds_f (void) Cmd_StuffCmds (cbuf_active); } +static void +cmd_shutdown (void *data) +{ + Cbuf_Delete (cmd_cbuf); + Hash_DelTable (cmd_hash); + Hash_DelTable (cmd_alias_hash); + Hash_DelTable (cmd_provider_hash); +} + VISIBLE void Cmd_Init_Hash (void) { - cmd_hash = Hash_NewTable (1021, cmd_get_key, 0, 0); - cmd_alias_hash = Hash_NewTable (1021, cmd_alias_get_key, cmd_alias_free, 0); - cmd_provider_hash = Hash_NewTable(1021, cmd_provider_get_key, cmd_provider_free, 0); + Sys_RegisterShutdown (cmd_shutdown, 0); + cmd_hash = Hash_NewTable (1021, cmd_get_key, cmd_free, 0, 0); + cmd_alias_hash = Hash_NewTable (1021, cmd_alias_get_key, + cmd_alias_free, 0, 0); + cmd_provider_hash = Hash_NewTable(1021, cmd_provider_get_key, + cmd_provider_free, 0, 0); } VISIBLE void @@ -607,8 +652,7 @@ Cmd_Init (void) Cmd_AddCommand ("echo", Cmd_Echo_f, "Print text to console"); Cmd_AddCommand ("wait", Cmd_Wait_f, "Wait a game tic"); Cmd_AddCommand ("sleep", Cmd_Sleep_f, "Wait for a certain number of seconds."); - cmd_warncmd = Cvar_Get ("cmd_warncmd", "0", CVAR_NONE, NULL, "Toggles the " - "display of error messages for unknown commands"); + Cvar_Register (&cmd_warncmd_cvar, 0, 0); cmd_cbuf = Cbuf_New (&id_interp); Cmd_AddProvider("id", &id_interp); @@ -628,7 +672,7 @@ Cmd_ExecuteString (const char *text, cmd_source_t src) return 0; } -VISIBLE void +VISIBLE int Cmd_Exec_File (cbuf_t *cbuf, const char *path, int qfs) { char *f; @@ -636,7 +680,7 @@ Cmd_Exec_File (cbuf_t *cbuf, const char *path, int qfs) QFile *file; if (!path || !*path) - return; + return 0; if (qfs) { file = QFS_FOpenFile (path); } else { @@ -654,7 +698,9 @@ Cmd_Exec_File (cbuf_t *cbuf, const char *path, int qfs) free (f); } Qclose (file); + return 1; } + return 0; } VISIBLE void diff --git a/libs/util/cmem.c b/libs/util/cmem.c new file mode 100644 index 000000000..9b13a4b6e --- /dev/null +++ b/libs/util/cmem.c @@ -0,0 +1,424 @@ +/* + cmem.c + + Cache-line aligned memory allocator + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/alloc.h" +#include "QF/cmem.h" +#include "QF/sys.h" + +static size_t __attribute__((const)) +ilog2 (size_t x) +{ + size_t l = 0; + while (x >>= 1) { + l++; + } + return l; +} + +static void +link_free_line (memsuper_t *super, memline_t *line) +{ + size_t ind = ilog2 (line->size) - 6; + if (super->free_lines[ind]) { + super->free_lines[ind]->free_prev = &line->free_next; + } + line->free_next = super->free_lines[ind]; + line->free_prev = &super->free_lines[ind]; + super->free_lines[ind] = line; +} + +static void +unlink_free_line (memline_t *line) +{ + if (line->free_next) { + line->free_next->free_prev = line->free_prev; + } + *line->free_prev = line->free_next; +} + +static void +unlink_line (memline_t *line) +{ + if (line->block_next) { + line->block_next->block_prev = line->block_prev; + } + *line->block_prev = line->block_next; + + unlink_free_line (line); +} + +static memblock_t * +init_block (memsuper_t *super, void *mem, size_t alloc_size) +{ + memblock_t *block = mem; + + memset (block, 0, sizeof (memblock_t)); + + if (super->memblocks) { + super->memblocks->prev = &block->next; + } + block->next = super->memblocks; + block->prev = &super->memblocks; + super->memblocks = block; + + block->size = super->page_size - sizeof (*block); + block->post_size = alloc_size - super->page_size; + + memline_t *line = (memline_t *) (block + 1); + + line->block = block; + line->size = block->size; + + line->block_next = 0; + line->block_prev = &block->free_lines; + block->free_lines = line; + + link_free_line (super, line); + + return block; +} + +static memblock_t * +block_alloc (memsuper_t *super, size_t size) +{ + memblock_t *block; + memblock_t *best = 0; + size_t best_size = ~0u; + + for (block = super->memblocks; block; block = block->next) { + if (block->post_free && block->post_size >= size + && block->post_size < best_size) { + best = block; + best_size = block->post_size; + } + } + if (best) { + best->post_free = 0; + return best; + } + + size_t page_size = super->page_size; + size_t alloc_size = page_size + size; + void *mem = Sys_Alloc (alloc_size); + block = init_block (super, mem, alloc_size); + return block; +} + +static void * +alloc_line (memline_t *line, size_t size) +{ + void *mem = line; + + if (line->size > size) { + // split the line block and insert the new block into the list + memline_t *split = (memline_t *)((size_t) line + size); + split->block = line->block; + split->size = line->size - size; + line->size = size; + + split->block_next = line->block_next; + if (split->block_next) { + split->block_next->block_prev = &split->block_next; + } + line->block_next = split; + split->block_prev = &line->block_next; + + split->free_next = line->free_next; + if (split->free_next) { + split->free_next->free_prev = &split->free_next; + } + line->free_next = split; + split->free_prev = &line->free_next; + } + line->block->allocated += line->size; + unlink_line (line); + return mem; +} + +static void +line_free (memsuper_t *super, memblock_t *block, void *mem) +{ + //FIXME right now, can free only single lines (need allocated lines to + // have a control block) + size_t size = MEM_LINE_SIZE; + memline_t **l; + memline_t *line = 0; + + block->allocated -= size; + + for (l = &block->free_lines; *l; l = &(*l)->block_next) { + line = *l; +// if (line->block_next && line->block_next < line) { +// *(int *)0 = 0; +// } + if ((size_t) mem + size < (size_t) line) { + // line to be freed is below the free line + break; + } + if ((size_t) mem + size == (size_t) line) { + // line to be freed is immediately below the free line + // merge with the free line by "allocating" the line and then + // "freeing" it with the line to be freed + size += line->size; + unlink_line (line); // does not modify line->block_next + line = line->block_next; + break; + } + if ((size_t) line + line->size == (size_t) mem) { + // line to be freed is immediately above the free line + // merge with the free line by growing the line + line->size += size; + if (line->block_next + && (size_t) line->block_next == (size_t) mem + size) { + // the line to be freed connects two free lines + line->size += line->block_next->size; + unlink_line (line->block_next); + } + // the line changed size so needs to be relinked in the super + unlink_free_line (line); + link_free_line (super, line); + return; + } +// if ((size_t) mem >= (size_t) line +// && (size_t) mem < (size_t) line + line->size) { +// *(int *) 0 = 0; +// } + line = 0; + } + memline_t *memline = (memline_t *) mem; + memline->block_next = line; + if (memline->block_next) { + memline->block_next->block_prev = &memline->block_next; + } + memline->block_prev = l; + memline->size = size; + memline->block = block; + *l = memline; + link_free_line (super, memline); +} + +static memsline_t * +sline_new (memsuper_t *super, size_t size_ind) +{ + size_t size = 4 << size_ind; + size_t free_loc = (sizeof (memsline_t) + size - 1) & ~(size - 1); + memsline_t *sline = cmemalloc (super, MEM_LINE_SIZE); + sline->size = size_ind; + sline->list = free_loc >> 2; + while (free_loc + size < MEM_LINE_SIZE) { + *(uint16_t *)((size_t) sline + free_loc) = free_loc + size; + free_loc += size; + } + *(uint16_t *)((size_t) sline + free_loc) = 0; + if (super->last_freed[size_ind]) { + super->last_freed[size_ind]->prev = (size_t) &sline->next >> 6; + } + sline->next = super->last_freed[size_ind]; + sline->prev = (size_t) &super->last_freed[size_ind] >> 6; + super->last_freed[size_ind] = sline; + return sline; +} + +void * +cmemalloc (memsuper_t *super, size_t size) +{ + size_t ind = 0; + // allocation sizes start at 4 (sizeof(float)) and go up in powers of two + while ((4u << ind) < size) { + ind++; + } + // round size up + if (size > MEM_LINE_SIZE * 8 || size > super->page_size / 8) { + // the object is large enough it could cause excessive fragmentation, + memblock_t *block = block_alloc (super, 4 << ind); + if (!block) { + return 0; + } + return (void *) ((size_t) block + super->page_size); + } else { + size = 4 << ind; + if (size >= MEM_LINE_SIZE) { + // whole cache lines are required for this object + // convert from byte log2 to cache-line log2 + ind -= 4; + memline_t *line = 0; + + while (!line && ind < MAX_CACHE_LINES) { + line = super->free_lines[ind++]; + } + while (line && line->size < size) { + line = line->free_next; + } + if (!line) { + // need a new line, one that doesn't make me fe... wrong song + void *mem; + /* The cache-line pool is page aligned for two reasons: + * 1) so it fits exactly within a page + * 2) the control block can be found easily + * And the reason the pool is exactly one page large is so no + * allocated line is ever page-aligned as that would make the + * line indistinguishable from a large block. + */ + mem = Sys_Alloc (super->page_size); + // sets super->free_lines, the block is guarnateed to be big + // enough to hold the requested allocation as otherwise a full + // block allocation would have been used + memblock_t *block = init_block (super, mem, super->page_size); + line = block->free_lines; + } + return alloc_line (line, size); + } else { + void *mem = 0; + memsline_t **sline = &super->last_freed[ind]; + if (!*sline) { + *sline = sline_new (super, ind); + } + if (*sline) { + size_t list = (*sline)->list << 2; + mem = (void *) ((size_t) *sline + list); + (*sline)->list = *(uint16_t *) mem >> 2; + if (!(*sline)->list) { + // the sub-line is full, so remove it from the free + // list. Freeing a block from the line will add it back + // to the list + memsline_t *s = *sline; + if ((*sline)->next) { + (*sline)->next->prev = (*sline)->prev; + } + *sline = (*sline)->next; + s->next = 0; + s->prev = 0; + } + } + return mem; + } + } + return 0; +} + +static void +unlink_block (memblock_t *block) +{ +// if (!block->free_lines || block->free_lines->block_next) { +// *(int *) 0 = 0; +// } + unlink_line (block->free_lines); + + if (block->next) { + block->next->prev = block->prev; + } + *block->prev = block->next; +} + +void +cmemfree (memsuper_t *super, void *mem) +{ + memsline_t **super_sline; + memsline_t *sline; + memblock_t *block; + + if ((size_t) mem & (MEM_LINE_SIZE - 1)) { + // sub line block + sline = (memsline_t *) ((size_t) mem & ~(MEM_LINE_SIZE - 1)); + *(uint16_t *) mem = sline->list << 2; + sline->list = ((size_t) mem & (MEM_LINE_SIZE - 1)) >> 2; + super_sline = &super->last_freed[sline->size]; + if (*super_sline != sline) { + if (sline->next) { + sline->next->prev = sline->prev; + } + if (sline->prev) { + *(memsline_t **) (size_t)(sline->prev << 6) = sline->next; + } + + if (*super_sline) { + (*super_sline)->prev = (size_t) &sline->next >> 6; + } + sline->next = *super_sline; + sline->prev = (size_t) super_sline >> 6; + (*super_sline) = sline; + } + return; + } else if ((size_t) mem & super->page_mask) { + // cache line + block = (memblock_t *) ((size_t) mem & ~super->page_mask); + line_free (super, block, mem); + } else { + // large block + block = (memblock_t *) ((size_t) mem - super->page_size); + block->post_free = 1; + } + if (!block->allocated && (!block->post_size || block->post_free)) { + unlink_block (block); + Sys_Free (block, super->page_size + block->post_size); + } +} + +memsuper_t * +new_memsuper (void) +{ + // Temporary superblock used to bootstrap a pool + memsuper_t bootstrap = { }; + bootstrap.page_size = Sys_PageSize (); + bootstrap.page_mask = (bootstrap.page_size - 1); + + // Allocate the real superblock from the pool. As a superblock is only + // two cache lines large (for 64-byte cache lines), it will always be + // allocated using a block's cache lines, and thus will be inside the first + // block. + memsuper_t *super = cmemalloc (&bootstrap, sizeof (*super)); + *super = bootstrap; + // The block used to allocate the real superblock points to the bootstrap + // superblock, but needs to point to the real superblock. + super->memblocks->prev = &super->memblocks; + + // Any free cache line block chains will also point to the bootstrap + // block instead of the resl superblock, so fix them up too (there should + // be only one, but no harm in being paranoid) + for (int i = 0; i < MAX_CACHE_LINES; i++) { + if (super->free_lines[i]) { + super->free_lines[i]->free_prev = &super->free_lines[i]; + } + } + return super; +} + +void +delete_memsuper (memsuper_t *super) +{ + // The block holding the superblock is always the last block in the list + while (super->memblocks && super->memblocks->next) { + memblock_t *t = super->memblocks; + super->memblocks = super->memblocks->next; + Sys_Free (t, super->page_size + t->post_size); + } + memblock_t *block = super->memblocks; + Sys_Free (block, super->page_size + block->post_size); +} diff --git a/libs/util/cvar.c b/libs/util/cvar.c index 32e3af26a..d92ca8e05 100644 --- a/libs/util/cvar.c +++ b/libs/util/cvar.c @@ -37,13 +37,17 @@ # include #endif +#include #include #include #include "QF/cmd.h" #include "QF/cvar.h" +#include "QF/cmem.h" #include "QF/hash.h" +#include "QF/heapsort.h" #include "QF/mathlib.h" +#include "QF/plist.h" #include "QF/qargs.h" #include "QF/quakefs.h" #include "QF/sys.h" @@ -54,18 +58,83 @@ #define USER_RO_CVAR "User-created READ-ONLY Cvar" #define USER_CVAR "User-created cvar" -VISIBLE cvar_t *developer; -VISIBLE cvar_t *cvar_vars; +static exprenum_t developer_enum; +static exprtype_t developer_type = { + .name = "developer", + .size = sizeof (int), + .binops = cexpr_flag_binops, + .unops = cexpr_flag_unops, + .data = &developer_enum, + .get_string = cexpr_enum_get_string, +}; + +#define SYS_DEVELOPER(dev) (SYS_##dev & ~SYS_dev), +static int developer_values[] = { + SYS_dev, +#include "QF/sys_developer.h" +}; +#undef SYS_DEVELOPER +#define SYS_DEVELOPER(dev) {#dev, &developer_type, developer_values + SYS_DeveloperID_##dev + 1}, +static exprsym_t developer_symbols[] = { + {"dev", &developer_type, developer_values + 0}, +#include "QF/sys_developer.h" + {} +}; +#undef SYS_DEVELOPER +static exprtab_t developer_symtab = { + developer_symbols, +}; +static exprenum_t developer_enum = { + &developer_type, + &developer_symtab, +}; + +VISIBLE int developer; +static cvar_t developer_cvar = { + .name = "developer", + .description = + "set to enable extra debugging information", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &developer_type, .value = &developer }, +}; static const char *cvar_null_string = ""; -static cvar_alias_t *calias_vars; static hashtab_t *cvar_hash; +static hashtab_t *user_cvar_hash; static hashtab_t *calias_hash; +static cvar_t * +cvar_create (const char *name, const char *value) +{ + cvar_t *var = calloc (1, sizeof (cvar_t) + sizeof (char *)); + var->name = strdup (name); + var->description = cvar_null_string; + var->default_value = cvar_null_string; + var->flags = CVAR_USER_CREATED; + var->value.value = var + 1; + *(char **)var->value.value = strdup (value); + + Hash_Add (user_cvar_hash, var); + return var; +} + +static void +cvar_destroy (cvar_t *var) +{ + if (!(var->flags & CVAR_USER_CREATED)) { + Sys_Error ("Attempt to destroy non-user cvar"); + } + Hash_Free (user_cvar_hash, Hash_Del (user_cvar_hash, var->name)); +} VISIBLE cvar_t * Cvar_FindVar (const char *var_name) { - return (cvar_t*) Hash_Find (cvar_hash, var_name); + cvar_t *var = Hash_Find (cvar_hash, var_name); + if (!var) { + var = Hash_Find (user_cvar_hash, var_name); + } + return var; } VISIBLE cvar_t * @@ -103,8 +172,6 @@ Cvar_MakeAlias (const char *name, cvar_t *cvar) if (!var) { alias = (cvar_alias_t *) calloc (1, sizeof (cvar_alias_t)); - alias->next = calias_vars; - calias_vars = alias; alias->name = strdup (name); alias->cvar = cvar; Hash_Add (calias_hash, alias); @@ -128,8 +195,21 @@ Cvar_RemoveAlias (const char *name) return var; } +static float +cvar_value (cvar_t *var) +{ + if (!var->value.type) { + return atof (*(char **)var->value.value); + } else if (var->value.type == &cexpr_int) { + return *(int *)var->value.value; + } else if (var->value.type == &cexpr_float) { + return *(float *)var->value.value; + } + return 0; +} + VISIBLE float -Cvar_VariableValue (const char *var_name) +Cvar_Value (const char *var_name) { cvar_t *var; @@ -138,11 +218,22 @@ Cvar_VariableValue (const char *var_name) var = Cvar_FindAlias (var_name); if (!var) return 0; - return atof (var->string); + return cvar_value (var); +} + +static const char * +cvar_string (const cvar_t *var) +{ + if (!var->value.type) { + return *(char **)var->value.value; + } else if (var->value.type->get_string) { + return var->value.type->get_string (&var->value, 0); + } + return cvar_null_string; } VISIBLE const char * -Cvar_VariableString (const char *var_name) +Cvar_String (const char *var_name) { cvar_t *var; @@ -151,137 +242,186 @@ Cvar_VariableString (const char *var_name) var = Cvar_FindAlias (var_name); if (!var) return cvar_null_string; - return var->string; + return cvar_string (var); } VISIBLE const char * -Cvar_CompleteVariable (const char *partial) +Cvar_VarString (const cvar_t *var) { - cvar_t *cvar; - cvar_alias_t *alias; - int len; - - len = strlen (partial); - - if (!len) - return NULL; - - // check exact match - for (cvar = cvar_vars; cvar; cvar = cvar->next) - if (!strcmp (partial, cvar->name)) - return cvar->name; - - // check aliases too :) - for (alias = calias_vars; alias; alias = alias->next) - if (!strcmp (partial, alias->name)) - return alias->name; - - // check partial match - for (cvar = cvar_vars; cvar; cvar = cvar->next) - if (!strncmp (partial, cvar->name, len)) - return cvar->name; - - // check aliases too :) - for (alias = calias_vars; alias; alias = alias->next) - if (!strncmp (partial, alias->name, len)) - return alias->name; - - return NULL; + return cvar_string (var); } -/* - CVar_CompleteCountPossible +typedef struct { + const char *match; + size_t match_len; + int num_matches; +} cvar_count_ctx_t; + +static void +cvar_match_count (void *ele, void *data) +{ + cvar_count_ctx_t *ctx = data; + const cvar_t *cvar = ele; + + if (strncmp (cvar->name, ctx->match, ctx->match_len) == 0) { + ctx->num_matches++; + } +} - New function for tab-completion system - Added by EvilTypeGuy - Thanks to Fett erich@heintz.com -*/ VISIBLE int Cvar_CompleteCountPossible (const char *partial) { - cvar_t *cvar; - int len; - int h; + cvar_count_ctx_t ctx = { + .match = partial, + .match_len = strlen (partial), + .num_matches = 0, + }; - h = 0; - len = strlen(partial); + Hash_ForEach (cvar_hash, cvar_match_count, &ctx); + Hash_ForEach (user_cvar_hash, cvar_match_count, &ctx); + // this is a bit of a hack, but both cvar_alias_t and cvar_t have + // name in the first file, so it will work out as that's the only + // criteron for a match + Hash_ForEach (calias_hash, cvar_match_count, &ctx); - if (!len) - return 0; - - // Loop through the cvars and count all possible matches - for (cvar = cvar_vars; cvar; cvar = cvar->next) - if (!strncmp(partial, cvar->name, len)) - h++; - - return h; + return ctx.num_matches; } -/* - CVar_CompleteBuildList +typedef struct { + const char *match; + size_t match_len; + const char **list; + int index; +} cvar_copy_ctx_t; - New function for tab-completion system - Added by EvilTypeGuy - Thanks to Fett erich@heintz.com - Thanks to taniwha -*/ -VISIBLE const char ** +static void +cvar_match_copy (void *ele, void *data) +{ + cvar_copy_ctx_t *ctx = data; + const cvar_t *cvar = ele; + + if (strncmp (cvar->name, ctx->match, ctx->match_len) == 0) { + ctx->list[ctx->index++] = cvar->name; + } +} + +static int +cvar_cmp_name (const void *_a, const void *_b) +{ + const char * const *a = _a; + const char * const *b = _b; + return strcmp (*a, *b); +} + +VISIBLE const char ** Cvar_CompleteBuildList (const char *partial) { - cvar_t *cvar; - int len = 0; - int bpos = 0; - int sizeofbuf = (Cvar_CompleteCountPossible (partial) + 1) * - sizeof (char *); - const char **buf; + int num_matches = Cvar_CompleteCountPossible (partial); - len = strlen(partial); - buf = malloc(sizeofbuf + sizeof (char *)); - SYS_CHECKMEM (buf); - // Loop through the alias list and print all matches - for (cvar = cvar_vars; cvar; cvar = cvar->next) - if (!strncmp(partial, cvar->name, len)) - buf[bpos++] = cvar->name; + cvar_copy_ctx_t ctx = { + .match = partial, + .match_len = strlen (partial), + .list = malloc((num_matches + 1) * sizeof (char *)), + .index = 0, + }; - buf[bpos] = NULL; - return buf; + Hash_ForEach (cvar_hash, cvar_match_copy, &ctx); + Hash_ForEach (user_cvar_hash, cvar_match_copy, &ctx); + // this is a bit of a hack, but both cvar_alias_t and cvar_t have + // name in the first file, so it will work out as that's the only + // criteron for a match + Hash_ForEach (calias_hash, cvar_match_copy, &ctx); + ctx.list[ctx.index] = 0; + heapsort (ctx.list, ctx.index, sizeof (char *), cvar_cmp_name); + return ctx.list; } VISIBLE void -Cvar_Set (cvar_t *var, const char *value) +Cvar_AddListener (cvar_t *cvar, cvar_listener_t listener, void *data) { - int changed; - int vals; + if (!cvar->listeners) { + cvar->listeners = malloc (sizeof (*cvar->listeners)); + LISTENER_SET_INIT (cvar->listeners, 8); + } + LISTENER_ADD (cvar->listeners, listener, data); +} + +VISIBLE void +Cvar_RemoveListener (cvar_t *cvar, cvar_listener_t listener, void *data) +{ + if (cvar->listeners) { + LISTENER_REMOVE (cvar->listeners, listener, data); + } +} + +static int +cvar_setvar (cvar_t *var, const char *value) +{ + int changed = 0; + + if (!var->value.type) { + char **str_value = var->value.value; + changed = !*str_value || !strequal (*str_value, value); + if (var->validator) { + changed = changed && var->validator (var); + } + if (changed) { + free (*str_value); + *str_value = strdup (value); + } + } else { + exprenum_t *enm = var->value.type->data; + exprctx_t context = { + .memsuper = new_memsuper (), + .symtab = enm ? enm->symtab : 0, + .msg_prefix = var->name, + }; + if (context.symtab && !context.symtab->tab) { + cexpr_init_symtab (context.symtab, &context); + } + context.result = cexpr_value (var->value.type, &context); + if (!cexpr_eval_string (value, &context)) { + changed = memcmp (context.result->value, var->value.value, + var->value.type->size) != 0; + if (var->validator) { + changed = changed && var->validator (var); + } + if (changed) { + memcpy (var->value.value, context.result->value, + var->value.type->size); + } + } + delete_memsuper (context.memsuper); + } + + if (changed && var->listeners) { + LISTENER_INVOKE (var->listeners, var); + } + return changed; +} + +VISIBLE void +Cvar_SetVar (cvar_t *var, const char *value) +{ + if (var->flags & CVAR_ROM) { + Sys_MaskPrintf (SYS_dev, "Cvar \"%s\" is read-only, cannot modify\n", + var->name); + return; + } + cvar_setvar (var, value); +} + +VISIBLE void +Cvar_Set (const char *var_name, const char *value) +{ + cvar_t *var; + + var = Cvar_FindVar (var_name); if (!var) return; - if (var->flags & CVAR_ROM) { - Sys_MaskPrintf (SYS_DEV, "Cvar \"%s\" is read-only, cannot modify\n", - var->name); - return; - } - - changed = !strequal (var->string, value); - free ((char*)var->string); // free the old value string - - var->string = strdup (value); - var->value = atof (var->string); - var->int_val = atoi (var->string); - VectorZero (var->vec); - vals = sscanf (var->string, "%f %f %f", - &var->vec[0], &var->vec[1], &var->vec[2]); - if (vals == 1) - var->vec[2] = var->vec[1] = var->vec[0]; - - if (changed && var->callback) - var->callback (var); -} - -VISIBLE void -Cvar_SetValue (cvar_t *var, float value) -{ - Cvar_Set (var, va ("%g", value)); + Cvar_SetVar (var, value); } /* @@ -289,7 +429,7 @@ Cvar_SetValue (cvar_t *var, float value) Handles variable inspection and changing from the console */ -VISIBLE qboolean +VISIBLE bool Cvar_Command (void) { cvar_t *v; @@ -303,14 +443,25 @@ Cvar_Command (void) // perform a variable print or set if (Cmd_Argc () == 1) { - Sys_Printf ("\"%s\" is \"%s\"\n", v->name, v->string); + Sys_Printf ("\"%s\" is \"%s\"\n", v->name, cvar_string (v)); return true; } - Cvar_Set (v, Cmd_Argv (1)); + Cvar_SetVar (v, Cmd_Argv (1)); return true; } +static void +cvar_write_variable (void *ele, void *data) +{ + cvar_t *cvar = ele; + QFile *f = data; + + if (cvar->flags & CVAR_ARCHIVE) { + Qprintf (f, "seta %s \"%s\"\n", cvar->name, cvar_string (cvar)); + } +} + /* Cvar_WriteVariables @@ -320,11 +471,52 @@ Cvar_Command (void) VISIBLE void Cvar_WriteVariables (QFile *f) { - cvar_t *var; + Hash_ForEach (cvar_hash, cvar_write_variable, f); + Hash_ForEach (user_cvar_hash, cvar_write_variable, f); +} - for (var = cvar_vars; var; var = var->next) - if (var->flags & CVAR_ARCHIVE) - Qprintf (f, "seta %s \"%s\"\n", var->name, var->string); +static void +cvar_write_config (void *ele, void *data) +{ + cvar_t *cvar = ele; + plitem_t *cfg = data; + + if (cvar->flags & CVAR_ARCHIVE) { + PL_D_AddObject (cfg, cvar->name, PL_NewString (cvar_string (cvar))); + } +} + +VISIBLE void +Cvar_SaveConfig (plitem_t *config) +{ + plitem_t *cvars = PL_NewDictionary (0); + PL_D_AddObject (config, "cvars", cvars); + Hash_ForEach (cvar_hash, cvar_write_config, cvars); + Hash_ForEach (user_cvar_hash, cvar_write_config, cvars); +} + +VISIBLE void +Cvar_LoadConfig (plitem_t *config) +{ + plitem_t *cvars = PL_ObjectForKey (config, "cvars"); + + if (!cvars) { + return; + } + for (int i = 0, count = PL_D_NumKeys (cvars); i < count; i++) { + const char *cvar_name = PL_KeyAtIndex (cvars, i); + const char *value = PL_String (PL_ObjectForKey (cvars, cvar_name)); + if (value) { + cvar_t *var = Cvar_FindVar (cvar_name); + if (var) { + Cvar_SetVar (var, value); + var->flags |= CVAR_ARCHIVE; + } else { + var = cvar_create (cvar_name, value); + var->flags |= CVAR_ARCHIVE; + } + } + } } static void @@ -348,16 +540,16 @@ set_cvar (const char *cmd, int orflags) if (var) { if (var->flags & CVAR_ROM) { - Sys_MaskPrintf (SYS_DEV, + Sys_MaskPrintf (SYS_dev, "Cvar \"%s\" is read-only, cannot modify\n", var_name); } else { - Cvar_Set (var, value); - Cvar_SetFlags (var, var->flags | orflags); + Cvar_SetVar (var, value); + var->flags |= orflags; } } else { - var = Cvar_Get (var_name, value, CVAR_USER_CREATED | orflags, NULL, - USER_CVAR); + var = cvar_create (var_name, value); + var->flags |= orflags; } } @@ -398,11 +590,23 @@ Cvar_Inc_f (void) var = Cvar_FindVar (name); if (!var) var = Cvar_FindAlias (name); - if (!var) + if (!var) { Sys_Printf ("Unknown variable \"%s\"\n", name); + return; + } + if (var->flags & CVAR_ROM) { + Sys_Printf ("Variable \"%s\" is read-only\n", name); + return; + } break; } - Cvar_SetValue (var, var->value + inc); + if (var->value.type == &cexpr_float) { + *(float *) var->value.value += inc; + } else if (var->value.type == &cexpr_int) { + *(int *) var->value.value += inc; + } else { + Sys_Printf ("Variable \"%s\" cannot be incremented\n", name); + } } static void @@ -422,8 +626,12 @@ Cvar_Toggle_f (void) Sys_Printf ("Unknown variable \"%s\"\n", Cmd_Argv (1)); return; } + if ((var->flags & CVAR_ROM) || var->value.type != &cexpr_int) { + Sys_Printf ("Variable \"%s\" cannot be toggled\n", Cmd_Argv (1)); + return; + } - Cvar_Set (var, var->int_val ? "0" : "1"); + *(int *) var->value.value = !*(int *) var->value.value; } static void @@ -444,8 +652,7 @@ Cvar_Cycle_f (void) if (!var) var = Cvar_FindAlias (name); if (!var) { - var = Cvar_Get (name, Cmd_Argv (Cmd_Argc () - 1), CVAR_USER_CREATED, - 0, USER_CVAR); + var = cvar_create (name, Cmd_Argv (Cmd_Argc () - 1)); } // loop through the args until you find one that matches the current cvar @@ -458,27 +665,22 @@ Cvar_Cycle_f (void) // it won't match on zero when it should, but after that, it will be // comparing string that all had the same source (the user) so it will // work. - if (atof (Cmd_Argv (i)) == 0) { - if (!strcmp (Cmd_Argv (i), var->string)) - break; - } else { - if (atof (Cmd_Argv (i)) == var->value) - break; - } + if (!strcmp (Cmd_Argv (i), cvar_string (var))) + break; } if (i == Cmd_Argc ()) - Cvar_Set (var, Cmd_Argv (2)); // no match + Cvar_SetVar (var, Cmd_Argv (2)); // no match else if (i + 1 == Cmd_Argc ()) - Cvar_Set (var, Cmd_Argv (2)); // matched last value in list + Cvar_SetVar (var, Cmd_Argv (2)); // matched last value in list else - Cvar_Set (var, Cmd_Argv (i + 1)); // matched earlier in list + Cvar_SetVar (var, Cmd_Argv (i + 1)); // matched earlier in list } static void Cvar_Reset (cvar_t *var) { - Cvar_Set (var, var->default_string); + Cvar_SetVar (var, var->default_value); } static void @@ -505,54 +707,90 @@ Cvar_Reset_f (void) } } +static void +cvar_reset_var (void *ele, void *data) +{ + cvar_t *var = ele; + if (!(var->flags & CVAR_ROM)) + Cvar_Reset (var); +} + static void Cvar_ResetAll_f (void) { - cvar_t *var; + Hash_ForEach (cvar_hash, cvar_reset_var, 0); +} - for (var = cvar_vars; var; var = var->next) - if (!(var->flags & CVAR_ROM)) - Cvar_Reset (var); +static int +cvar_cmp (const void *_a, const void *_b) +{ + const cvar_t * const *a = _a; + const cvar_t * const *b = _b; + return strcmp ((*a)->name, (*b)->name); } static void Cvar_CvarList_f (void) { - cvar_t *var; - int i; int showhelp = 0; - const char *flags; - if (Cmd_Argc () > 1) { showhelp = 1; if (strequal (Cmd_Argv (1), "cfg")) showhelp++; } - for (var = cvar_vars, i = 0; var; var = var->next, i++) { - flags = va ("%c%c%c%c", - var->flags & CVAR_ROM ? 'r' : ' ', - var->flags & CVAR_ARCHIVE ? '*' : ' ', - var->flags & CVAR_USERINFO ? 'u' : ' ', - var->flags & CVAR_SERVERINFO ? 's' : ' '); + + void **cvar_list = Hash_GetList (cvar_hash); + int num_vars = Hash_NumElements (cvar_hash); + heapsort (cvar_list, num_vars, sizeof (void *), cvar_cmp); + + for (cvar_t **cvar = (cvar_t **) cvar_list; *cvar; cvar++) { + cvar_t *var = *cvar; + const char *flags = va (0, "%c%c%c%c", + var->flags & CVAR_ROM ? 'r' : ' ', + var->flags & CVAR_ARCHIVE ? '*' : ' ', + var->flags & CVAR_USERINFO ? 'u' : ' ', + var->flags & CVAR_SERVERINFO ? 's' : ' '); if (showhelp == 2) Sys_Printf ("//%s %s\n%s \"%s\"\n\n", flags, var->description, - var->name, var->string); + var->name, cvar_string (var)); else if (showhelp) Sys_Printf ("%s %-20s : %s\n", flags, var->name, var->description); else Sys_Printf ("%s %s\n", flags, var->name); } - Sys_Printf ("------------\n%d variables\n", i); + Sys_Printf ("------------\n%d variables\n", num_vars); } static void -cvar_free (void *c, void *unused) +cvar_free_memory (void *ele, void *data) { - cvar_t *cvar = (cvar_t*)c; - free ((char*)cvar->name); - free ((char*)cvar->string); - free (cvar); + const cvar_t *cvar = ele; + if (!cvar->value.type) { + char **str_value = cvar->value.value; + free (*str_value); + *str_value = 0; + } else if (cvar->value.type->data) { + exprenum_t *enm = cvar->value.type->data; + if (enm->symtab && enm->symtab->tab) { + Hash_DelTable (enm->symtab->tab); + enm->symtab->tab = 0; + } + } + + if (cvar->listeners) { + DARRAY_CLEAR (cvar->listeners); + free (cvar->listeners); + } +} + +static void +cvar_shutdown (void *data) +{ + Hash_ForEach (cvar_hash, cvar_free_memory, 0); + Hash_DelTable (cvar_hash); + Hash_DelTable (user_cvar_hash); + Hash_DelTable (calias_hash); } static const char * @@ -562,6 +800,16 @@ cvar_get_key (const void *c, void *unused) return cvar->name; } +static void +cvar_free (void *c, void *unused) +{ + cvar_t *cvar = (cvar_t*)c; + + free (*(char **) cvar->value.value); + free ((char *) cvar->name); + free (cvar); +} + static void calias_free (void *c, void *unused) { @@ -580,15 +828,18 @@ calias_get_key (const void *c, void *unused) VISIBLE void Cvar_Init_Hash (void) { - cvar_hash = Hash_NewTable (1021, cvar_get_key, cvar_free, 0); - calias_hash = Hash_NewTable (1021, calias_get_key, calias_free, 0); + cvar_hash = Hash_NewTable (1021, cvar_get_key, 0, 0, 0); + user_cvar_hash = Hash_NewTable (1021, cvar_get_key, cvar_free, 0, 0); + calias_hash = Hash_NewTable (1021, calias_get_key, calias_free, 0, 0); + + Sys_RegisterShutdown (cvar_shutdown, 0); + } VISIBLE void Cvar_Init (void) { - developer = Cvar_Get ("developer", "0", CVAR_NONE, NULL, - "set to enable extra debugging information"); + Cvar_Register (&developer_cvar, 0, 0); Cmd_AddCommand ("set", Cvar_Set_f, "Set the selected variable, useful on " "the command line (+set variablename setting)"); @@ -607,57 +858,36 @@ Cvar_Init (void) Cmd_AddCommand ("resetall", Cvar_ResetAll_f, "Reset all cvars"); } -VISIBLE cvar_t * -Cvar_Get (const char *name, const char *string, int cvarflags, - void (*callback)(cvar_t*), const char *description) +VISIBLE void +Cvar_Register (cvar_t *var, cvar_listener_t listener, void *data) { + cvar_t *user_var; - cvar_t *var; - - if (Cmd_Exists (name)) { - Sys_Printf ("Cvar_Get: %s is a command\n", name); - return NULL; + if (Cmd_Exists (var->name)) { + Sys_Printf ("Cvar_Get: %s is a command\n", var->name); + return; + } + if (var->flags & CVAR_REGISTERED) { + Sys_Error ("Cvar %s already registered", var->name); } - var = Cvar_FindVar (name); - if (!var) { - cvar_t **v; - var = (cvar_t *) calloc (1, sizeof (cvar_t)); - // Cvar doesn't exist, so we create it - var->name = strdup (name); - var->string = strdup (string); - var->default_string = strdup (string); - var->flags = cvarflags; - var->callback = callback; - var->description = description; - var->value = atof (var->string); - var->int_val = atoi (var->string); - sscanf (var->string, "%f %f %f", - &var->vec[0], &var->vec[1], &var->vec[2]); - Hash_Add (cvar_hash, var); - - for (v = &cvar_vars; *v; v = &(*v)->next) - if (strcmp ((*v)->name, var->name) >= 0) - break; - var->next = *v; - *v = var; + if ((user_var = Hash_Find (user_cvar_hash, var->name))) { + cvar_setvar (var, cvar_string (user_var)); + cvar_destroy (user_var); } else { - // Cvar does exist, so we update the flags and return. - var->flags &= ~CVAR_USER_CREATED; - var->flags |= cvarflags; - if (!var->callback) - var->callback = callback; - if (!var->description - || strequal (var->description, USER_RO_CVAR) - || strequal (var->description, USER_CVAR)) - var->description = description; - if (!var->default_string) - var->default_string = strdup (string); + cvar_setvar (var, var->default_value); } - if (var->callback) - var->callback (var); + var->flags |= CVAR_REGISTERED; - return var; + if (listener) { + Cvar_AddListener (var, listener, data); + } + + Hash_Add (cvar_hash, var); + + if (var->listeners) { + LISTENER_INVOKE (var->listeners, var); + } } /* @@ -673,3 +903,38 @@ Cvar_SetFlags (cvar_t *var, int cvarflags) var->flags = cvarflags; } + +typedef struct { + cvar_select_t select; + void *data; + const cvar_t **list; + int index; +} cvar_select_ctx_t; + +static void +cvar_select (void *ele, void *data) +{ + const cvar_t *cvar = ele; + cvar_select_ctx_t *ctx = data; + + if (ctx->select (cvar, ctx->data)) { + ctx->list[ctx->index++] = cvar; + } +} + +VISIBLE const cvar_t ** +Cvar_Select (cvar_select_t select, void *data) +{ + int num_cvars = Hash_NumElements (cvar_hash) + + Hash_NumElements (user_cvar_hash); + cvar_select_ctx_t ctx = { + .select = select, + .data = data, + .list = malloc ((num_cvars + 1) * sizeof (cvar_t *)), + .index = 0, + }; + Hash_ForEach (cvar_hash, cvar_select, &ctx); + Hash_ForEach (user_cvar_hash, cvar_select, &ctx); + ctx.list[num_cvars] = 0; + return ctx.list; +} diff --git a/libs/util/dstring.c b/libs/util/dstring.c index 4e43a0aae..dc478b8c2 100644 --- a/libs/util/dstring.c +++ b/libs/util/dstring.c @@ -83,6 +83,9 @@ dstring_new (void) VISIBLE void dstring_delete (dstring_t *dstr) { + if (!dstr) { + return; + } if (dstr->str) dstr->mem->free (dstr->mem->data, dstr->str); dstr->mem->free (dstr->mem->data, dstr); @@ -101,7 +104,7 @@ dstring_adjust (dstring_t *dstr) } VISIBLE char * -dstring_reserve (dstring_t *dstr, unsigned len) +dstring_reserve (dstring_t *dstr, size_t len) { dstr->size += len; dstring_adjust (dstr); @@ -109,7 +112,7 @@ dstring_reserve (dstring_t *dstr, unsigned len) } VISIBLE void -dstring_copy (dstring_t *dstr, const char *data, unsigned int len) +dstring_copy (dstring_t *dstr, const char *data, size_t len) { dstr->size = len; dstring_adjust (dstr); @@ -117,9 +120,9 @@ dstring_copy (dstring_t *dstr, const char *data, unsigned int len) } VISIBLE void -dstring_append (dstring_t *dstr, const char *data, unsigned int len) +dstring_append (dstring_t *dstr, const char *data, size_t len) { - unsigned int ins = dstr->size; // Save insertion point + size_t ins = dstr->size; // Save insertion point dstr->size += len; dstring_adjust (dstr); @@ -127,10 +130,9 @@ dstring_append (dstring_t *dstr, const char *data, unsigned int len) } VISIBLE void -dstring_insert (dstring_t *dstr, unsigned int pos, const char *data, - unsigned int len) +dstring_insert (dstring_t *dstr, size_t pos, const char *data, size_t len) { - unsigned int oldsize = dstr->size; + size_t oldsize = dstr->size; if (pos > dstr->size) pos = dstr->size; @@ -141,7 +143,7 @@ dstring_insert (dstring_t *dstr, unsigned int pos, const char *data, } VISIBLE void -dstring_snip (dstring_t *dstr, unsigned int pos, unsigned int len) +dstring_snip (dstring_t *dstr, size_t pos, size_t len) { if (pos > dstr->size) pos = dstr->size; @@ -162,10 +164,10 @@ dstring_clear (dstring_t *dstr) } VISIBLE void -dstring_replace (dstring_t *dstr, unsigned int pos, unsigned int rlen, - const char *data, unsigned int len) +dstring_replace (dstring_t *dstr, size_t pos, size_t rlen, + const char *data, size_t len) { - unsigned int oldsize = dstr->size; + size_t oldsize = dstr->size; if (pos > dstr->size) pos = dstr->size; if (rlen > dstr->size - pos) @@ -223,7 +225,7 @@ dstring_strdup (const char *str) } VISIBLE char * -dstring_reservestr (dstring_t *dstr, unsigned len) +dstring_reservestr (dstring_t *dstr, size_t len) { int pos = dstr->size; if (pos && !dstr->str[pos - 1]) @@ -242,7 +244,7 @@ dstring_copystr (dstring_t *dstr, const char *str) } VISIBLE void -dstring_copysubstr (dstring_t *dstr, const char *str, unsigned int len) +dstring_copysubstr (dstring_t *dstr, const char *str, size_t len) { len = strnlen (str, len); @@ -255,8 +257,8 @@ dstring_copysubstr (dstring_t *dstr, const char *str, unsigned int len) VISIBLE void dstring_appendstr (dstring_t *dstr, const char *str) { - unsigned int pos = strnlen (dstr->str, dstr->size); - unsigned int len = strlen (str); + size_t pos = strnlen (dstr->str, dstr->size); + size_t len = strlen (str); dstr->size = pos + len + 1; dstring_adjust (dstr); @@ -264,9 +266,9 @@ dstring_appendstr (dstring_t *dstr, const char *str) } VISIBLE void -dstring_appendsubstr (dstring_t *dstr, const char *str, unsigned int len) +dstring_appendsubstr (dstring_t *dstr, const char *str, size_t len) { - unsigned int pos = strnlen (dstr->str, dstr->size); + size_t pos = strnlen (dstr->str, dstr->size); len = strnlen (str, len); dstr->size = pos + len + 1; @@ -276,15 +278,14 @@ dstring_appendsubstr (dstring_t *dstr, const char *str, unsigned int len) } VISIBLE void -dstring_insertstr (dstring_t *dstr, unsigned int pos, const char *str) +dstring_insertstr (dstring_t *dstr, size_t pos, const char *str) { // Don't insert strlen + 1 to achieve concatenation dstring_insert (dstr, pos, str, strlen (str)); } VISIBLE void -dstring_insertsubstr (dstring_t *dstr, unsigned int pos, const char *str, - unsigned int len) +dstring_insertsubstr (dstring_t *dstr, size_t pos, const char *str, size_t len) { len = strnlen (str, len); @@ -299,7 +300,7 @@ dstring_clearstr (dstring_t *dstr) dstr->str[0] = 0; } -static int +static __attribute__((format(PRINTF, 3, 0))) char * _dvsprintf (dstring_t *dstr, int offs, const char *fmt, va_list args) { int size; @@ -325,20 +326,20 @@ _dvsprintf (dstring_t *dstr, int offs, const char *fmt, va_list args) } dstr->size = size + offs + 1; dstr->str[dstr->size - 1] = 0; - return size; + return dstr->str; } -VISIBLE int +VISIBLE char * dvsprintf (dstring_t *dstr, const char *fmt, va_list args) { return _dvsprintf (dstr, 0, fmt, args); } -VISIBLE int +VISIBLE char * dsprintf (dstring_t *dstr, const char *fmt, ...) { va_list args; - int ret; + char *ret; va_start (args, fmt); ret = _dvsprintf (dstr, 0, fmt, args); @@ -347,7 +348,7 @@ dsprintf (dstring_t *dstr, const char *fmt, ...) return ret; } -VISIBLE int +VISIBLE char * davsprintf (dstring_t *dstr, const char *fmt, va_list args) { int offs = 0; @@ -357,11 +358,11 @@ davsprintf (dstring_t *dstr, const char *fmt, va_list args) return _dvsprintf (dstr, offs, fmt, args); } -VISIBLE int +VISIBLE char * dasprintf (dstring_t *dstr, const char *fmt, ...) { va_list args; - int ret; + char *ret; int offs = 0; if (dstr->size) diff --git a/libs/util/hash.c b/libs/util/hash.c index 587e4a24d..28bf9a93f 100644 --- a/libs/util/hash.c +++ b/libs/util/hash.c @@ -51,6 +51,15 @@ typedef struct hashlink_s { void *data; } hashlink_t; +typedef struct hlinkset_s { + struct hlinkset_s *next; +} hlinkset_t; + +struct hashctx_s { + hlinkset_t *linksets; + hashlink_t *free_hashlinks; +}; + struct hashtab_s { size_t tab_size; unsigned int size_bits; @@ -60,43 +69,47 @@ struct hashtab_s { uintptr_t (*get_hash)(const void*,void*); const char *(*get_key)(const void*,void*); void (*free_ele)(void*,void*); + hashctx_t *hashctx; hashlink_t *tab[1]; // variable size }; -static hashlink_t *free_hashlinks; - static hashlink_t * -new_hashlink (void) +new_hashlink (hashctx_t *hctx) { hashlink_t *link; - if (!free_hashlinks) { + if (!hctx->free_hashlinks) { int i; - if (!(free_hashlinks = calloc (1024, sizeof (hashlink_t)))) - return 0; - for (i = 0, link = free_hashlinks; i < 1023; i++, link++) + hlinkset_t *linkset = malloc (sizeof (hlinkset_t) + + 1024 * sizeof (hashlink_t)); + linkset->next = hctx->linksets; + hctx->linksets = linkset; + hctx->free_hashlinks = (hashlink_t *) &linkset[1]; + for (i = 0, link = hctx->free_hashlinks; i < 1023; i++, link++) link->next = link + 1; link->next = 0; } - link = free_hashlinks; - free_hashlinks = link->next; + link = hctx->free_hashlinks; + hctx->free_hashlinks = link->next; link->next = 0; return link; } static void -free_hashlink (hashlink_t *link) +free_hashlink (hashlink_t *link, hashctx_t *hctx) { - link->next = free_hashlinks; - free_hashlinks = link; + link->next = hctx->free_hashlinks; + hctx->free_hashlinks = link; } -VISIBLE unsigned long +static hashctx_t *default_hashctx; + +VISIBLE uintptr_t Hash_String (const char *str) { #if 0 - unsigned long h = 0; + uintptr_t h = 0; while (*str) { h = (h << 4) + (unsigned char)*str++; if (h&0xf0000000) @@ -118,12 +131,38 @@ Hash_String (const char *str) #endif } -VISIBLE unsigned long +VISIBLE uintptr_t +Hash_nString (const char *str, size_t sz) +{ +#if 0 + uintptr_t h = 0; + while (*str && sz-- > 0) { + h = (h << 4) + (unsigned char)*str++; + if (h&0xf0000000) + h = (h ^ (h >> 24)) & 0xfffffff; + } + return h; +#else + // dx_hack_hash + // shamelessly stolen from Daniel Phillips + // from his post to lkml + uint32_t hash0 = 0x12a3fe2d, hash1 = 0x37abe8f9; + while (sz-- > 0 && *str) { + uint32_t hash = hash1 + (hash0 ^ ((unsigned char)*str++ * 71523)); + if (hash & 0x80000000) hash -= 0x7fffffff; + hash1 = hash0; + hash0 = hash; + } + return hash0; +#endif +} + +VISIBLE uintptr_t Hash_Buffer (const void *_buf, int len) { const unsigned char *buf = _buf; #if 0 - unsigned long h = 0; + uintptr_t h = 0; while (len-- > 0) { h = (h << 4) + (unsigned char)*buf++; if (h&0xf0000000) @@ -161,8 +200,8 @@ static inline int get_index (uintptr_t hash, size_t size, size_t bits) { #if 0 - unsigned long mask = ~0UL << bits; - unsigned long extract; + uintptr_t mask = ~0UL << bits; + uintptr_t extract; size -= 1; for (extract = (hash & mask) >> bits; @@ -181,9 +220,29 @@ get_index (uintptr_t hash, size_t size, size_t bits) #endif } +static void +hash_shutdown (void *data) +{ + Hash_DelContext (default_hashctx); +} + +VISIBLE void +Hash_DelContext (hashctx_t *hashctx) +{ + if (!hashctx) { + return; + } + while (hashctx->linksets) { + hlinkset_t *l = hashctx->linksets->next; + free (hashctx->linksets); + hashctx->linksets = l; + } + free (hashctx); +} + VISIBLE hashtab_t * Hash_NewTable (int tsize, const char *(*gk)(const void*,void*), - void (*f)(void*,void*), void *ud) + void (*f)(void*,void*), void *ud, hashctx_t **hctx) { hashtab_t *tab = calloc (1, field_offset (hashtab_t, tab[tsize])); if (!tab) @@ -192,6 +251,16 @@ Hash_NewTable (int tsize, const char *(*gk)(const void*,void*), tab->user_data = ud; tab->get_key = gk; tab->free_ele = f; + if (!hctx) { + hctx = &default_hashctx; + if (!default_hashctx) { + Sys_RegisterShutdown (hash_shutdown, 0); + } + } + if (!*hctx) { + *hctx = calloc (1, sizeof (hashctx_t)); + } + tab->hashctx = *hctx; while (tsize) { tab->size_bits++; @@ -214,6 +283,9 @@ Hash_SetHashCompare (hashtab_t *tab, uintptr_t (*gh)(const void*,void*), VISIBLE void Hash_DelTable (hashtab_t *tab) { + if (!tab) { + return; + } Hash_FlushTable (tab); free (tab); } @@ -228,7 +300,7 @@ Hash_FlushTable (hashtab_t *tab) hashlink_t *t = tab->tab[i]->next; void *data = tab->tab[i]->data; - free_hashlink (tab->tab[i]); + free_hashlink (tab->tab[i], tab->hashctx); tab->tab[i] = t; if (tab->free_ele) tab->free_ele (data, tab->user_data); @@ -237,12 +309,11 @@ Hash_FlushTable (hashtab_t *tab) tab->num_ele = 0; } -VISIBLE int -Hash_Add (hashtab_t *tab, void *ele) +static int +hash_add_element (hashtab_t *tab, uintptr_t h, void *ele) { - unsigned long h = Hash_String (tab->get_key(ele, tab->user_data)); size_t ind = get_index (h, tab->tab_size, tab->size_bits); - hashlink_t *lnk = new_hashlink (); + hashlink_t *lnk = new_hashlink (tab->hashctx); if (!lnk) return -1; @@ -256,29 +327,24 @@ Hash_Add (hashtab_t *tab, void *ele) return 0; } +VISIBLE int +Hash_Add (hashtab_t *tab, void *ele) +{ + uintptr_t h = Hash_String (tab->get_key(ele, tab->user_data)); + return hash_add_element (tab, h, ele); +} + VISIBLE int Hash_AddElement (hashtab_t *tab, void *ele) { - unsigned long h = tab->get_hash (ele, tab->user_data); - size_t ind = get_index (h, tab->tab_size, tab->size_bits); - hashlink_t *lnk = new_hashlink (); - - if (!lnk) - return -1; - if (tab->tab[ind]) - tab->tab[ind]->prev = &lnk->next; - lnk->next = tab->tab[ind]; - lnk->prev = &tab->tab[ind]; - lnk->data = ele; - tab->tab[ind] = lnk; - tab->num_ele++; - return 0; + uintptr_t h = tab->get_hash (ele, tab->user_data); + return hash_add_element (tab, h, ele); } VISIBLE void * Hash_Find (hashtab_t *tab, const char *key) { - unsigned long h = Hash_String (key); + uintptr_t h = Hash_String (key); size_t ind = get_index (h, tab->tab_size, tab->size_bits); hashlink_t *lnk = tab->tab[ind]; @@ -290,10 +356,25 @@ Hash_Find (hashtab_t *tab, const char *key) return 0; } +VISIBLE void * +Hash_nFind (hashtab_t *tab, const char *key, size_t sz) +{ + uintptr_t h = Hash_nString (key, sz); + size_t ind = get_index (h, tab->tab_size, tab->size_bits); + hashlink_t *lnk = tab->tab[ind]; + + while (lnk) { + if (strncmp (key, tab->get_key (lnk->data, tab->user_data), sz) == 0) + return lnk->data; + lnk = lnk->next; + } + return 0; +} + VISIBLE void * Hash_FindElement (hashtab_t *tab, const void *ele) { - unsigned long h = tab->get_hash (ele, tab->user_data); + uintptr_t h = tab->get_hash (ele, tab->user_data); size_t ind = get_index (h, tab->tab_size, tab->size_bits); hashlink_t *lnk = tab->tab[ind]; @@ -308,7 +389,7 @@ Hash_FindElement (hashtab_t *tab, const void *ele) VISIBLE void ** Hash_FindList (hashtab_t *tab, const char *key) { - unsigned long h = Hash_String (key); + uintptr_t h = Hash_String (key); size_t ind = get_index (h, tab->tab_size, tab->size_bits); hashlink_t *lnk = tab->tab[ind], *start = 0; int count = 0; @@ -336,7 +417,7 @@ Hash_FindList (hashtab_t *tab, const char *key) VISIBLE void ** Hash_FindElementList (hashtab_t *tab, void *ele) { - unsigned long h = tab->get_hash (ele, tab->user_data); + uintptr_t h = tab->get_hash (ele, tab->user_data); size_t ind = get_index (h, tab->tab_size, tab->size_bits); hashlink_t *lnk = tab->tab[ind], *start = 0; int count = 0; @@ -364,7 +445,7 @@ Hash_FindElementList (hashtab_t *tab, void *ele) VISIBLE void * Hash_Del (hashtab_t *tab, const char *key) { - unsigned long h = Hash_String (key); + uintptr_t h = Hash_String (key); size_t ind = get_index (h, tab->tab_size, tab->size_bits); hashlink_t *lnk = tab->tab[ind]; void *data; @@ -375,7 +456,7 @@ Hash_Del (hashtab_t *tab, const char *key) if (lnk->next) lnk->next->prev = lnk->prev; *lnk->prev = lnk->next; - free_hashlink (lnk); + free_hashlink (lnk, tab->hashctx); tab->num_ele--; return data; } @@ -387,7 +468,7 @@ Hash_Del (hashtab_t *tab, const char *key) VISIBLE void * Hash_DelElement (hashtab_t *tab, void *ele) { - unsigned long h = tab->get_hash (ele, tab->user_data); + uintptr_t h = tab->get_hash (ele, tab->user_data); size_t ind = get_index (h, tab->tab_size, tab->size_bits); hashlink_t *lnk = tab->tab[ind]; void *data; @@ -398,7 +479,7 @@ Hash_DelElement (hashtab_t *tab, void *ele) if (lnk->next) lnk->next->prev = lnk->prev; *lnk->prev = lnk->next; - free_hashlink (lnk); + free_hashlink (lnk, tab->hashctx); tab->num_ele--; return data; } @@ -441,6 +522,43 @@ Hash_GetList (hashtab_t *tab) return list; } +VISIBLE void ** +Hash_Select (hashtab_t *tab, hash_select_t select, void *data) +{ + void **list; + void **l; + size_t ind; + + l = list = malloc ((tab->num_ele + 1) * sizeof (void *)); + if (!list) + return 0; + for (ind = 0; ind < tab->tab_size; ind++) { + hashlink_t *lnk; + + for (lnk = tab->tab[ind]; lnk; lnk = lnk->next) { + if (select (lnk->data, data)) { + *l++ = lnk->data; + } + } + } + *l++ = 0; + return list; +} + +VISIBLE void +Hash_ForEach (hashtab_t *tab, hash_action_t action, void *data) +{ + size_t ind; + + for (ind = 0; ind < tab->tab_size; ind++) { + hashlink_t *lnk; + + for (lnk = tab->tab[ind]; lnk; lnk = lnk->next) { + action (lnk->data, data); + } + } +} + static inline double sqr (double x) { diff --git a/libs/util/heapsort.c b/libs/util/heapsort.c new file mode 100644 index 000000000..e7f840433 --- /dev/null +++ b/libs/util/heapsort.c @@ -0,0 +1,211 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "QF/heapsort.h" +#include "QF/qtypes.h" + +static inline void +swap_elements (void *a, void *b, size_t size) +{ + byte tmp[size]; + memcpy (tmp, a, size); + memcpy (a, b, size); + memcpy (b, tmp, size); +} + +VISIBLE void +heap_sink (void *base, size_t ind, size_t nmemb, size_t size, + __compar_fn_t cmp) +{ + size_t left_ind = 2 * ind + 1; + size_t right_ind = 2 * ind + 2; + void *node = (byte *) base + ind * size; + void *left = (byte *) base + left_ind * size; + void *right = (byte *) base + right_ind * size; + + size_t largest_ind = ind; + void *largest = node; + + if (left_ind < nmemb && cmp (left, node) > 0) { + largest = left; + largest_ind = left_ind; + } + if (right_ind < nmemb && cmp (right, largest) > 0) { + largest = right; + largest_ind = right_ind; + } + if (largest_ind != ind) { + swap_elements (largest, node, size); + heap_sink (base, largest_ind, nmemb, size, cmp); + } +} + +VISIBLE void +heap_sink_r (void *base, size_t ind, size_t nmemb, size_t size, + __compar_d_fn_t cmp, void *arg) +{ + size_t left_ind = 2 * ind + 1; + size_t right_ind = 2 * ind + 2; + void *node = (byte *) base + ind * size; + void *left = (byte *) base + left_ind * size; + void *right = (byte *) base + right_ind * size; + + size_t largest_ind = ind; + void *largest = node; + + if (left_ind < nmemb && cmp (left, node, arg) > 0) { + largest = left; + largest_ind = left_ind; + } + if (right_ind < nmemb && cmp (right, largest, arg) > 0) { + largest = right; + largest_ind = right_ind; + } + if (largest_ind != ind) { + swap_elements (largest, node, size); + heap_sink_r (base, largest_ind, nmemb, size, cmp, arg); + } +} + +VISIBLE void +heap_sink_s (void *base, size_t ind, size_t nmemb, size_t size, + __compar_d_fn_t cmp, __swap_d_fn_t swp, void *arg) +{ + size_t left_ind = 2 * ind + 1; + size_t right_ind = 2 * ind + 2; + void *node = (byte *) base + ind * size; + void *left = (byte *) base + left_ind * size; + void *right = (byte *) base + right_ind * size; + + size_t largest_ind = ind; + void *largest = node; + + if (left_ind < nmemb && cmp (left, node, arg) > 0) { + largest = left; + largest_ind = left_ind; + } + if (right_ind < nmemb && cmp (right, largest, arg) > 0) { + largest = right; + largest_ind = right_ind; + } + if (largest_ind != ind) { + swp (largest, node, arg); + heap_sink_s (base, largest_ind, nmemb, size, cmp, swp, arg); + } +} + +VISIBLE void +heap_swim (void *base, size_t ind, size_t nmemb, size_t size, + __compar_fn_t cmp) +{ + size_t parent_ind = (ind - 1) / 2; + void *node = (byte *) base + ind * size; + void *parent = (byte *) base + parent_ind * size; + + if (ind > 0 && cmp (node, parent) > 0) { + swap_elements (node, parent, size); + heap_swim (base, parent_ind, nmemb, size, cmp); + } +} + +VISIBLE void +heap_swim_r (void *base, size_t ind, size_t nmemb, size_t size, + __compar_d_fn_t cmp, void *arg) +{ + size_t parent_ind = (ind - 1) / 2; + void *node = (byte *) base + ind * size; + void *parent = (byte *) base + parent_ind * size; + + if (ind > 0 && cmp (node, parent, arg) > 0) { + swap_elements (node, parent, size); + heap_swim_r (base, parent_ind, nmemb, size, cmp, arg); + } +} + +VISIBLE void +heap_swim_s (void *base, size_t ind, size_t nmemb, size_t size, + __compar_d_fn_t cmp, __swap_d_fn_t swp, void *arg) +{ + size_t parent_ind = (ind - 1) / 2; + void *node = (byte *) base + ind * size; + void *parent = (byte *) base + parent_ind * size; + + if (ind > 0 && cmp (node, parent, arg) > 0) { + swp (node, parent, arg); + heap_swim_s (base, parent_ind, nmemb, size, cmp, swp, arg); + } +} + +VISIBLE void +heap_build (void *base, size_t nmemb, size_t size, __compar_fn_t cmp) +{ + if (nmemb < 2) { + return; + } + for (size_t i = nmemb / 2; i-- > 0; ) { + heap_sink (base, i, nmemb, size, cmp); + } +} + +VISIBLE void +heap_build_r (void *base, size_t nmemb, size_t size, + __compar_d_fn_t cmp, void *arg) +{ + if (nmemb < 2) { + return; + } + for (size_t i = nmemb / 2; i-- > 0; ) { + heap_sink_r (base, i, nmemb, size, cmp, arg); + } +} + +VISIBLE void +heap_build_s (void *base, size_t nmemb, size_t size, + __compar_d_fn_t cmp, __swap_d_fn_t swp, void *arg) +{ + if (nmemb < 2) { + return; + } + for (size_t i = nmemb / 2; i-- > 0; ) { + heap_sink_s (base, i, nmemb, size, cmp, swp, arg); + } +} + +VISIBLE void +heapsort (void *base, size_t nmemb, size_t size, __compar_fn_t cmp) +{ + heap_build (base, nmemb, size, cmp); + for (size_t i = nmemb; i-- > 1; ) { + void *last = (byte *) base + i * size; + swap_elements (base, last, size); + heap_sink (base, 0, i, size, cmp); + } +} + +VISIBLE void +heapsort_r (void *base, size_t nmemb, size_t size, __compar_d_fn_t cmp, + void *arg) +{ + heap_build_r (base, nmemb, size, cmp, arg); + for (size_t i = nmemb; i-- > 1; ) { + void *last = (byte *) base + i * size; + swap_elements (base, last, size); + heap_sink_r (base, 0, i, size, cmp, arg); + } +} + +VISIBLE void +heapsort_s (void *base, size_t nmemb, size_t size, __compar_d_fn_t cmp, + __swap_d_fn_t swp, void *arg) +{ + heap_build_s (base, nmemb, size, cmp, swp, arg); + for (size_t i = nmemb; i-- > 1; ) { + void *last = (byte *) base + i * size; + swp (base, last, arg); + heap_sink_s (base, 0, i, size, cmp, swp, arg); + } +} diff --git a/libs/util/idparse.c b/libs/util/idparse.c index 440f74029..24aac7c3f 100644 --- a/libs/util/idparse.c +++ b/libs/util/idparse.c @@ -40,6 +40,7 @@ #include "QF/cbuf.h" #include "QF/cmd.h" #include "QF/idparse.h" +#include "QF/sys.h" typedef struct idbuf_s { dstring_t *buf, *line; @@ -253,3 +254,17 @@ VISIBLE cbuf_interpreter_t id_interp = { COM_execute_sets, NULL }; + +static void +idparse_shutdown (void *data) +{ + if (_com_token) { + dstring_delete (_com_token); + } +} + +static void __attribute__((constructor)) +idparse_init (void) +{ + Sys_RegisterShutdown (idparse_shutdown, 0); +} diff --git a/libs/util/info.c b/libs/util/info.c index e9e8e9727..187cc2531 100644 --- a/libs/util/info.c +++ b/libs/util/info.c @@ -56,7 +56,7 @@ struct info_s { Searches for key in the "client-needed" info string list */ -VISIBLE qboolean +VISIBLE bool Info_FilterForKey (const char *key, const char **filter_list) { const char **s; @@ -224,11 +224,11 @@ VISIBLE info_t * Info_ParseString (const char *s, int maxsize, int flags) { info_t *info; - char *string = Hunk_TempAlloc (strlen (s) + 1); + char *string = Hunk_TempAlloc (0, strlen (s) + 1); char *key, *value, *end; info = malloc (sizeof (info_t)); - info->tab = Hash_NewTable (61, info_get_key, free_key, 0); + info->tab = Hash_NewTable (61, info_get_key, free_key, 0, 0); info->maxsize = maxsize; info->cursize = 0; @@ -271,7 +271,7 @@ Info_MakeString (info_t *info, int (*filter) (const char *)) info_key_t **key_list; info_key_t **key; - d = string = Hunk_TempAlloc (info->cursize + 1); + d = string = Hunk_TempAlloc (0, info->cursize + 1); key_list = (info_key_t **) Hash_GetList (info->tab); for (key = key_list; *key; key++) { diff --git a/libs/util/llist.c b/libs/util/llist.c index cd4bfba99..0809f93cb 100644 --- a/libs/util/llist.c +++ b/libs/util/llist.c @@ -50,7 +50,7 @@ llist_newnode (llist_t *list, void *data) } VISIBLE llist_t * -llist_new (void (*freedata)(void *element, void *userdata), qboolean (*cmpdata)(const void *element, const void *comparison, void *userdata), void *userdata) +llist_new (void (*freedata)(void *element, void *userdata), bool (*cmpdata)(const void *element, const void *comparison, void *userdata), void *userdata) { llist_t *new = calloc (1, sizeof (llist_t)); diff --git a/libs/util/math.S b/libs/util/math.S index 1118da8c9..f3e1fa13e 100644 --- a/libs/util/math.S +++ b/libs/util/math.S @@ -392,3 +392,7 @@ Lerror: #endif #endif // USE_INTEL_ASM + +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/libs/util/mathlib.c b/libs/util/mathlib.c index 9f1053025..e5fda93b3 100644 --- a/libs/util/mathlib.c +++ b/libs/util/mathlib.c @@ -35,6 +35,8 @@ # include #endif +#include "qfalloca.h" + #include #define IMPLEMENT_R_Cull @@ -42,11 +44,9 @@ #include "QF/mathlib.h" #include "QF/qtypes.h" +#include "QF/set.h" #include "QF/sys.h" -VISIBLE int nanmask = 255 << 23; -static plane_t _frustum[4]; -VISIBLE plane_t *const frustum = _frustum; static vec3_t _vec3_origin = { 0, 0, 0 }; VISIBLE const vec_t * const vec3_origin = _vec3_origin; static quat_t _quat_origin = { 0, 0, 0, 0 }; @@ -248,25 +248,46 @@ QuatMult (const quat_t q1, const quat_t q2, quat_t out) vec_t s; vec3_t v; - s = q1[0] * q2[0] - DotProduct (q1 + 1, q2 + 1); - CrossProduct (q1 + 1, q2 + 1, v); - VectorMultAdd (v, q1[0], q2 + 1, v); - VectorMultAdd (v, q2[0], q1 + 1, out + 1); - out[0] = s; + s = q1[3] * q2[3] - DotProduct (q1, q2); + CrossProduct (q1, q2, v); + VectorMultAdd (v, q1[3], q2, v); + VectorMultAdd (v, q2[3], q1, out); + out[3] = s; } VISIBLE void QuatMultVec (const quat_t q, const vec3_t v, vec3_t out) { - vec_t s; vec3_t tv; + vec_t dqv, dqq; + vec_t s; - s = -DotProduct (q + 1, v); - CrossProduct (q + 1, v, tv); - VectorMultAdd (tv, q[0], v, tv); - CrossProduct (q + 1, tv, out); - VectorMultSub (out, s, q + 1, out); - VectorMultAdd (out, q[0], tv, out); + s = q[3]; + CrossProduct (q, v, tv); + dqv = DotProduct (q, v); + dqq = DotProduct (q, q); + VectorScale (tv, s, tv); + VectorMultAdd (tv, dqv, q, tv); + VectorScale (tv, 2, tv); + VectorMultAdd (tv, s * s - dqq, v, out); +} + +VISIBLE void +QuatRotation(const vec3_t a, const vec3_t b, quat_t out) +{ + vec_t ma, mb; + vec_t den, mba_mab; + vec3_t t; + + ma = VectorLength(a); + mb = VectorLength(b); + den = 2 * ma * mb; + VectorScale (a, mb, t); + VectorMultAdd(t, ma, b, t); + mba_mab = VectorLength(t); + CrossProduct (a, b, t); + VectorScale(t, 1 / mba_mab, out); + out[3] = mba_mab / den; } VISIBLE void @@ -288,19 +309,19 @@ QuatExp (const quat_t a, quat_t b) vec_t r; vec_t c, s; - VectorCopy (a + 1, n); + VectorCopy (a, n); th = VectorNormalize (n); - r = expf (a[0]); + r = expf (a[3]); c = cosf (th); s = sinf (th); - VectorScale (n, r * s, b + 1); - b[0] = r * c; + VectorScale (n, r * s, b); + b[3] = r * c; } VISIBLE void QuatToMatrix (const quat_t q, vec_t *m, int homogenous, int vertical) { - vec_t aa, ab, ac, ad, bb, bc, bd, cc, cd, dd; + vec_t xx, xy, xz, xw, yy, yz, yw, zz, zw; vec_t *_m[4] = { m + (homogenous ? 0 : 0), m + (homogenous ? 4 : 3), @@ -308,28 +329,26 @@ QuatToMatrix (const quat_t q, vec_t *m, int homogenous, int vertical) m + (homogenous ? 12 : 9), }; - aa = q[0] * q[0]; - ab = q[0] * q[1]; - ac = q[0] * q[2]; - ad = q[0] * q[3]; + xx = 2 * q[0] * q[0]; + xy = 2 * q[0] * q[1]; + xz = 2 * q[0] * q[2]; + xw = 2 * q[0] * q[3]; - bb = q[1] * q[1]; - bc = q[1] * q[2]; - bd = q[1] * q[3]; + yy = 2 * q[1] * q[1]; + yz = 2 * q[1] * q[2]; + yw = 2 * q[1] * q[3]; - cc = q[2] * q[2]; - cd = q[2] * q[3]; - - dd = q[3] * q[3]; + zz = 2 * q[2] * q[2]; + zw = 2 * q[2] * q[3]; if (vertical) { - VectorSet (aa + bb - cc - dd, 2 * (bc + ad), 2 * (bd - ac), _m[0]); - VectorSet (2 * (bc - ad), aa - bb + cc - dd, 2 * (cd + ab), _m[1]); - VectorSet (2 * (bd + ac), 2 * (cd - ab), aa - bb - cc + dd, _m[2]); + VectorSet (1.0f - yy - zz, xy + zw, xz - yw, _m[0]); + VectorSet (xy - zw, 1.0f - xx - zz, yz + xw, _m[1]); + VectorSet (xz + yw, yz - xw, 1.0f - xx - yy, _m[2]); } else { - VectorSet (aa + bb - cc - dd, 2 * (bc - ad), 2 * (bd + ac), _m[0]); - VectorSet (2 * (bc + ad), aa - bb + cc - dd, 2 * (cd - ab), _m[1]); - VectorSet (2 * (bd - ac), 2 * (cd + ab), aa - bb - cc + dd, _m[2]); + VectorSet (1.0f - yy - zz, xy - zw, xz + yw, _m[0]); + VectorSet (xy + zw, 1.0f - xx - zz, yz - xw, _m[1]); + VectorSet (xz - yw, yz + xw, 1.0f - xx - yy, _m[2]); } if (homogenous) { _m[0][3] = 0; @@ -371,7 +390,7 @@ BOPS_Error (void) Returns 1, 2, or 1 + 2 */ VISIBLE int -BoxOnPlaneSide (const vec3_t emins, const vec3_t emaxs, plane_t *p) +BoxOnPlaneSide (const vec3_t emins, const vec3_t emaxs, const plane_t *p) { float dist1, dist2; int sides; @@ -467,9 +486,9 @@ BoxOnPlaneSide (const vec3_t emins, const vec3_t emaxs, plane_t *p) #endif sides = 0; - if (dist1 >= p->dist) + if (dist1 >= -p->dist) sides = 1; - if (dist2 < p->dist) + if (dist2 < -p->dist) sides |= 2; #ifdef PARANOID @@ -482,13 +501,21 @@ BoxOnPlaneSide (const vec3_t emins, const vec3_t emaxs, plane_t *p) #endif /* - angles is a left(?) handed system: 'pitch yaw roll' with x (pitch) axis to - the right, y (yaw) axis up and z (roll) axis forward. + angles is a left handed system: 'pitch yaw roll' with x (pitch) axis to + the right, y (yaw) axis up and z (roll) axis forward. However, the + rotations themselves are right-handed in that they follow the right-hand + rule for the world axes: pitch around +y, yaw around +z, and roll around + +x. - the math in AngleVectors has the entity frame as left handed with x - (forward) axis forward, y (right) axis to the right and z (up) up. However, - the world is a right handed system with x to the right, y forward and - z up. + This results in the entity frame having forward pointed along the world +x + axis, right along the world -y axis, and up along the world +z axis. + Whether this means the entity frame is left-handed depends on whether + forward is local X and right is local Y (left handed), or forward is local + Y and right is local X (right handed). + + NOTE: these matrices have forward, left and up vectors horizontal rather + than vertical and are thus the inverse of the matrices to produce the + actual rotation. pitch = cp 0 -sp @@ -529,11 +556,10 @@ AngleVectors (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) forward[0] = cp * cy; forward[1] = cp * sy; forward[2] = -sp; - // need to flip right because it's a left handed system in a right handed - // world - right[0] = -1 * (sr * sp * cy + cr * -sy); - right[1] = -1 * (sr * sp * sy + cr * cy); - right[2] = -1 * (sr * cp); + // need to flip right because the trig produces +Y but right is -Y + right[0] = -(sr * sp * cy + cr * -sy); + right[1] = -(sr * sp * sy + cr * cy); + right[2] = -(sr * cp); up[0] = (cr * sp * cy + -sr * -sy); up[1] = (cr * sp * sy + -sr * cy); up[2] = cr * cp; @@ -555,10 +581,10 @@ AngleQuat (const vec3_t angles, quat_t q) sr = sin (alpha); cr = cos (alpha); - QuatSet (cy * cp * cr + sy * sp * sr, - cy * cp * sr - sy * sp * cr, - cy * sp * cr + sy * cp * sr, - sy * cp * cr - cy * sp * sr, + QuatSet (cy * cp * sr - sy * sp * cr, // x + cy * sp * cr + sy * cp * sr, // y + sy * cp * cr - cy * sp * sr, // z + cy * cp * cr + sy * sp * sr, // w q); } @@ -1112,29 +1138,29 @@ Mat3Decompose (const mat3_t mat, quat_t rot, vec3_t shear, vec3_t scale) t = 1 + row[0][0] + row[1][1] + row[2][2]; if (t >= 1e-5) { vec_t s = sqrt (t) * 2; - rot[0] = s / 4; - rot[1] = (row[2][1] - row[1][2]) / s; - rot[2] = (row[0][2] - row[2][0]) / s; - rot[3] = (row[1][0] - row[0][1]) / s; + rot[0] = (row[2][1] - row[1][2]) / s; + rot[1] = (row[0][2] - row[2][0]) / s; + rot[2] = (row[1][0] - row[0][1]) / s; + rot[3] = s / 4; } else { if (row[0][0] > row[1][1] && row[0][0] > row[2][2]) { vec_t s = sqrt (1 + row[0][0] - row[1][1] - row[2][2]) * 2; - rot[0] = (row[2][1] - row[1][2]) / s; - rot[1] = s / 4; - rot[2] = (row[1][0] + row[0][1]) / s; - rot[3] = (row[0][2] + row[2][0]) / s; + rot[0] = s / 4; + rot[1] = (row[1][0] + row[0][1]) / s; + rot[2] = (row[0][2] + row[2][0]) / s; + rot[3] = (row[2][1] - row[1][2]) / s; } else if (row[1][1] > row[2][2]) { vec_t s = sqrt (1 + row[1][1] - row[0][0] - row[2][2]) * 2; - rot[0] = (row[0][2] - row[2][0]) / s; - rot[1] = (row[1][0] + row[0][1]) / s; - rot[2] = s / 4; - rot[3] = (row[2][1] + row[1][2]) / s; + rot[0] = (row[1][0] + row[0][1]) / s; + rot[1] = s / 4; + rot[2] = (row[2][1] + row[1][2]) / s; + rot[3] = (row[0][2] - row[2][0]) / s; } else { vec_t s = sqrt (1 + row[2][2] - row[0][0] - row[1][1]) * 2; - rot[0] = (row[1][0] - row[0][1]) / s; - rot[1] = (row[0][2] + row[2][0]) / s; - rot[2] = (row[2][1] + row[1][2]) / s; - rot[3] = s / 4; + rot[0] = (row[0][2] + row[2][0]) / s; + rot[1] = (row[2][1] + row[1][2]) / s; + rot[2] = s / 4; + rot[3] = (row[1][0] - row[0][1]) / s; } } return 1; @@ -1178,10 +1204,11 @@ BarycentricCoords (const vec_t **points, int num_points, const vec3_t p, CrossProduct (a, b, ab); div = DotProduct (ab, ab); CrossProduct (x, b, n); - lambda[1] = DotProduct (n, ab) / div; + lambda[1] = DotProduct (n, ab); CrossProduct (a, x, n); - lambda[2] = DotProduct (n, ab) / div; - lambda[0] = 1 - lambda[1] - lambda[2]; + lambda[2] = DotProduct (n, ab); + lambda[0] = div - lambda[1] - lambda[2]; + VectorScale (lambda, 1 / div, lambda); return; case 4: VectorSubtract (p, points[0], x); @@ -1256,7 +1283,6 @@ circum_circle (const vec_t **points, int num_points, sphere_t *sphere) VectorMultAdd (sphere->center, bb, ca, sphere->center); VectorMultAdd (sphere->center, cc, ab, sphere->center); VectorAdd (sphere->center, points[0], sphere->center); - sphere->radius = VectorDistance (sphere->center, points[0]); return 1; } return 0; @@ -1283,8 +1309,8 @@ CircumSphere (const vec3_t points[], int num_points, sphere_t *sphere) } static void -closest_point (const vec_t **points, int num_points, const vec3_t x, - vec3_t closest) +closest_affine_point (const vec_t **points, int num_points, const vec3_t x, + vec3_t closest) { vec3_t a, b, n, d; vec_t l; @@ -1311,95 +1337,151 @@ closest_point (const vec_t **points, int num_points, const vec3_t x, } } +static int +test_support_points(const vec_t **points, int *num_points, const vec3_t center) +{ + int in_affine = 0; + int in_convex = 0; + vec3_t v, d, n, a, b; + vec_t nn, dd, vv, dn; + + switch (*num_points) { + case 1: + in_affine = VectorCompare (points[0], center); + // the convex hull and affine hull for a single point are the same + in_convex = in_affine; + break; + case 2: + VectorSubtract (points[1], points[0], v); + VectorAdd (points[0], points[1], d); + VectorScale (d, 0.5, d); + VectorSubtract (center, d, d); + CrossProduct (v, d, n); + nn = DotProduct (n, n); + vv = DotProduct (v, v); + in_affine = nn < 1e-5 * vv * vv; + break; + case 3: + VectorSubtract (points[1], points[0], a); + VectorSubtract (points[2], points[0], b); + VectorSubtract (center, points[0], d); + CrossProduct (a, b, n); + dn = DotProduct (d, n); + dd = DotProduct (d, d); + nn = DotProduct (n, n); + in_affine = dn * dn < 1e-5 * dd * nn; + break; + case 4: + in_affine = 1; + break; + default: + Sys_Error ("Invalid number of points (%d) in test_support_points", + *num_points); + } + + // if in_convex is not true while in_affine is, then need to test as + // there is more than one dimension for the affine hull (a single support + // point is never dropped as it cannot be redundant) + if (in_affine && !in_convex) { + vec_t lambda[4]; + int dropped = 0; + int count = *num_points; + + BarycentricCoords (points, count, center, lambda); + + for (int i = 0; i < count; i++) { + points[i - dropped] = points[i]; + if (lambda[i] < -1e-4) { + dropped++; + (*num_points)--; + } + } + in_convex = !dropped; + if (dropped) { + for (int i = count - dropped; i < count; i++) { + points[i] = 0; + } + } + } + return in_convex; +} + sphere_t SmallestEnclosingBall (const vec3_t points[], int num_points) { + set_t was_support = SET_STATIC_INIT (num_points, alloca); sphere_t sphere; const vec_t *best; const vec_t *support[4]; int num_support; vec_t dist, best_dist; int i; - int itters = 0; + int best_i = 0; + int iters = 0; - if (num_points < 3) { - CircumSphere (points, num_points, &sphere); + if (num_points < 1) { + VectorZero (sphere.center); + sphere.radius = 0; return sphere; } for (i = 0; i < 4; i++) support[i] = 0; + set_empty (&was_support); VectorCopy (points[0], sphere.center); best_dist = dist = 0; - best = 0; + best = points[0]; for (i = 1; i < num_points; i++) { dist = VectorDistance_fast (points[i], sphere.center); if (dist > best_dist) { best_dist = dist; + best_i = i; best = points[i]; } } num_support = 1; support[0] = best; sphere.radius = best_dist; // note: radius squared until the end + set_add (&was_support, best_i); - while (1) { - vec3_t affine; - vec3_t center_to_affine, center_to_point; - vec_t affine_dist, point_proj, point_dist, bound; - vec_t scale = 1; + while (!test_support_points (support, &num_support, sphere.center)) { + vec3_t affine, v, p, r; + vec_t x, best_x = 0, rr, pv, pp; int i; - if (itters++ > 10) + if (iters++ > 2 * num_points) Sys_Error ("stuck SEB"); + + closest_affine_point (support, num_support, sphere.center, affine); + VectorSubtract (support[0], affine, r); + rr = DotProduct (r, r); + VectorSubtract (sphere.center, affine, v); + best = 0; - - if (num_support == 4) { - vec_t lambda[4]; - int dropped = 0; - - BarycentricCoords (support, 4, sphere.center, lambda); - for (i = 0; i < 4; i++) { - support[i - dropped] = support[i]; - if (lambda[i] < 0) { - dropped++; - num_support--; - } - } - if (!dropped) - break; - for (i = 4 - dropped; i < 4; i++) - support[i] = 0; - } - closest_point (support, num_support, sphere.center, affine); - VectorSubtract (affine, sphere.center, center_to_affine); - affine_dist = DotProduct (center_to_affine, center_to_affine); - if (affine_dist < 1e-2 * sphere.radius) - break; for (i = 0; i < num_points; i++) { - if (points[i] == support [0] || points[i] == support[1] - || points[i] == support[2]) + if (SET_TEST_MEMBER (&was_support, i)) { continue; - VectorSubtract (points[i], sphere.center, center_to_point); - point_proj = DotProduct (center_to_affine, center_to_point); - if (affine_dist - point_proj <= 0 - || ((affine_dist - point_proj) * (affine_dist - point_proj) - < 1e-8 * sphere.radius * affine_dist)) + } + VectorSubtract (points[i], affine, p); + pp = DotProduct (p, p); + pv = DotProduct (p, v); + if (pp <= rr || pv <= 0 || pv * pv < 1e-6 * rr) { continue; - point_dist = DotProduct (center_to_point, center_to_point); - bound = sphere.radius - point_dist; - bound /= 2 * (affine_dist - point_proj); - if (bound < scale) { + } + x = (pp - rr) / (2 * pv); + if (x > best_x) { best = points[i]; - scale = bound; + best_i = i; + best_x = x; } } - VectorMultAdd (sphere.center, scale, center_to_affine, sphere.center); - if (!best) - break; - sphere.radius = VectorDistance_fast (sphere.center, best); - support[num_support++] = best; + VectorMultAdd (affine, best_x, v, sphere.center); + sphere.radius = VectorDistance_fast (sphere.center, support[0]); + if (best) { + support[num_support++] = best; + set_add (&was_support, best_i); + } } best_dist = 0; for (i = 0; i < num_points; i++) { diff --git a/libs/util/mdfour.c b/libs/util/mdfour.c index 99f57bef2..f77fa0db7 100644 --- a/libs/util/mdfour.c +++ b/libs/util/mdfour.c @@ -37,14 +37,11 @@ #endif #include "QF/mdfour.h" -#include "QF/uint32.h" /* NOTE: This code makes no attempt to be fast! It assumes that a int is at least 32 bits long */ -static struct mdfour *m; - #define F(X,Y,Z) (((X)&(Y)) | ((~(X))&(Z))) #define G(X,Y,Z) (((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z))) #define H(X,Y,Z) ((X)^(Y)^(Z)) @@ -62,12 +59,12 @@ static struct mdfour *m; /* this applies md4 to 64 byte chunks */ static void -mdfour64 (uint32 * M) +mdfour64 (struct mdfour *m, uint32_t * M) { int j; - uint32 AA, BB, CC, DD; - uint32 X[16]; - uint32 A, B, C, D; + uint32_t AA, BB, CC, DD; + uint32_t X[16]; + uint32_t A, B, C, D; for (j = 0; j < 16; j++) X[j] = M[j]; @@ -154,7 +151,7 @@ mdfour64 (uint32 * M) } static void -copy64 (uint32 * M, const unsigned char *in) +copy64 (uint32_t * M, const unsigned char *in) { int i; @@ -164,7 +161,7 @@ copy64 (uint32 * M, const unsigned char *in) } static void -copy4 (unsigned char *out, uint32 x) +copy4 (unsigned char *out, uint32_t x) { out[0] = x & 0xFF; out[1] = (x >> 8) & 0xFF; @@ -183,11 +180,11 @@ mdfour_begin (struct mdfour *md) } static void -mdfour_tail (const unsigned char *in, int n) +mdfour_tail (struct mdfour *m, const unsigned char *in, int n) { unsigned char buf[128]; - uint32 M[16]; - uint32 b; + uint32_t M[16]; + uint32_t b; m->totalN += n; @@ -201,46 +198,42 @@ mdfour_tail (const unsigned char *in, int n) if (n <= 55) { copy4 (buf + 56, b); copy64 (M, buf); - mdfour64 (M); + mdfour64 (m, M); } else { copy4 (buf + 120, b); copy64 (M, buf); - mdfour64 (M); + mdfour64 (m, M); copy64 (M, buf + 64); - mdfour64 (M); + mdfour64 (m, M); } } VISIBLE void mdfour_update (struct mdfour *md, const unsigned char *in, int n) { - uint32 M[16]; + uint32_t M[16]; if (n == 0) - mdfour_tail (in, n); - - m = md; + mdfour_tail (md, in, n); while (n >= 64) { copy64 (M, in); - mdfour64 (M); + mdfour64 (md, M); in += 64; n -= 64; - m->totalN += 64; + md->totalN += 64; } - mdfour_tail (in, n); + mdfour_tail (md, in, n); } VISIBLE void mdfour_result (struct mdfour *md, unsigned char *out) { - m = md; - - copy4 (out, m->A); - copy4 (out + 4, m->B); - copy4 (out + 8, m->C); - copy4 (out + 12, m->D); + copy4 (out, md->A); + copy4 (out + 4, md->B); + copy4 (out + 8, md->C); + copy4 (out + 12, md->D); } VISIBLE void diff --git a/libs/util/mersenne.c b/libs/util/mersenne.c index 35794932b..5c0cbacbc 100644 --- a/libs/util/mersenne.c +++ b/libs/util/mersenne.c @@ -48,6 +48,7 @@ There were no differences. */ +#define IMPLEMENT_MTWIST_Funcs #include "QF/mersenne.h" #define KNUTH_MULT 1812433253ul // 0x6c078965 diff --git a/libs/util/msg.c b/libs/util/msg.c index 27ce51d5c..cc0b7f56a 100644 --- a/libs/util/msg.c +++ b/libs/util/msg.c @@ -68,6 +68,16 @@ MSG_WriteShort (sizebuf_t *sb, int c) *buf = ((unsigned int) c) >> 8; } +VISIBLE void +MSG_WriteShortBE (sizebuf_t *sb, int c) +{ + byte *buf; + + buf = SZ_GetSpace (sb, 2); + *buf++ = ((unsigned int) c) >> 8; + *buf = ((unsigned int) c) & 0xff; +} + VISIBLE void MSG_WriteLong (sizebuf_t *sb, int c) { @@ -80,6 +90,18 @@ MSG_WriteLong (sizebuf_t *sb, int c) *buf = ((unsigned int) c) >> 24; } +VISIBLE void +MSG_WriteLongBE (sizebuf_t *sb, int c) +{ + byte *buf; + + buf = SZ_GetSpace (sb, 4); + *buf++ = ((unsigned int) c) >> 24; + *buf++ = (((unsigned int) c) >> 16) & 0xff; + *buf++ = (((unsigned int) c) >> 8) & 0xff; + *buf = ((unsigned int) c) & 0xff; +} + VISIBLE void MSG_WriteFloat (sizebuf_t *sb, float f) { @@ -104,7 +126,7 @@ MSG_WriteString (sizebuf_t *sb, const char *s) } VISIBLE void -MSG_WriteBytes (sizebuf_t *sb, const void *buf, int len) +MSG_WriteBytes (sizebuf_t *sb, const void *buf, unsigned len) { SZ_Write (sb, buf, len); } @@ -193,27 +215,27 @@ MSG_WriteUTF8 (sizebuf_t *sb, unsigned utf8) return; // invalid (FIXME die?) } else if (utf8 & 0x7c000000) { buf = SZ_GetSpace (sb, count = 6); - *buf = 0xfc | ((utf8 & 0x40000000) >> 30); // 1 bit + *buf++ = 0xfc | ((utf8 & 0x40000000) >> 30); // 1 bit utf8 <<= 2; } else if (utf8 & 0x03e00000) { buf = SZ_GetSpace (sb, count = 5); - *buf = 0xf8 | ((utf8 & 0x30000000) >> 28); // 2 bits - utf8 <<= 4; + *buf++ = 0xf8 | ((utf8 & 0x03000000) >> 24); // 2 bits + utf8 <<= 8; } else if (utf8 & 0x001f0000) { buf = SZ_GetSpace (sb, count = 4); - *buf = 0xf0 | ((utf8 & 0x001c0000) >> 18); // 3 bits + *buf++ = 0xf0 | ((utf8 & 0x001c0000) >> 18); // 3 bits utf8 <<= 14; } else if (utf8 & 0x0000f800) { buf = SZ_GetSpace (sb, count = 3); - *buf = 0xe0 | ((utf8 & 0x0000f000) >> 12); // 4 bits + *buf++ = 0xe0 | ((utf8 & 0x0000f000) >> 12); // 4 bits utf8 <<= 20; } else if (utf8 & 0x00000780) { buf = SZ_GetSpace (sb, count = 2); - *buf = 0xc0 | ((utf8 & 0x00000780) >> 7); // 5 bits - utf8 <<= 25; + *buf++ = 0xc0 | ((utf8 & 0x000007c0) >> 6); // 5 bits + utf8 <<= 26; } else { buf = SZ_GetSpace (sb, count = 1); - *buf = utf8; + *buf++ = utf8; return; } while (--count) { @@ -222,6 +244,62 @@ MSG_WriteUTF8 (sizebuf_t *sb, unsigned utf8) } } +VISIBLE void +MSG_PokeShort (sizebuf_t *sb, unsigned offset, int c) +{ + if (__builtin_expect (offset + 2 > sb->cursize, 0)) { + Sys_Error ("MSG_PokeShort: invalid offset %d / %d", + offset, sb->cursize); + } + byte *buf = sb->data + offset; + + *buf++ = ((unsigned int) c) & 0xff; + *buf = ((unsigned int) c) >> 8; +} + +VISIBLE void +MSG_PokeShortBE (sizebuf_t *sb, unsigned offset, int c) +{ + if (__builtin_expect (offset + 2 > sb->cursize, 0)) { + Sys_Error ("MSG_PokeShortBE: invalid offset %d / %d", + offset, sb->cursize); + } + byte *buf = sb->data + offset; + + *buf++ = ((unsigned int) c) >> 8; + *buf = ((unsigned int) c) & 0xff; +} + +VISIBLE void +MSG_PokeLong (sizebuf_t *sb, unsigned offset, int c) +{ + if (__builtin_expect (offset + 2 > sb->cursize, 0)) { + Sys_Error ("MSG_PokeLong: invalid offset %d / %d", + offset, sb->cursize); + } + byte *buf = sb->data + offset; + + *buf++ = ((unsigned int) c) & 0xff; + *buf++ = (((unsigned int) c) >> 8) & 0xff; + *buf++ = (((unsigned int) c) >> 16) & 0xff; + *buf = ((unsigned int) c) >> 24; +} + +VISIBLE void +MSG_PokeLongBE (sizebuf_t *sb, unsigned offset, int c) +{ + if (__builtin_expect (offset + 2 > sb->cursize, 0)) { + Sys_Error ("MSG_PokeLongBE: invalid offset %d / %d", + offset, sb->cursize); + } + byte *buf = sb->data + offset; + + *buf++ = ((unsigned int) c) >> 24; + *buf++ = (((unsigned int) c) >> 16) & 0xff; + *buf++ = (((unsigned int) c) >> 8) & 0xff; + *buf = ((unsigned int) c) & 0xff; +} + // reading functions ========================================================== VISIBLE void @@ -231,7 +309,7 @@ MSG_BeginReading (qmsg_t *msg) msg->badread = false; } -VISIBLE int +VISIBLE unsigned MSG_GetReadCount (qmsg_t *msg) { return msg->readcount; @@ -253,8 +331,26 @@ MSG_ReadShort (qmsg_t *msg) int c; if (msg->readcount + 2 <= msg->message->cursize) { - c = (msg->message->data[msg->readcount] - + (msg->message->data[msg->readcount + 1] << 8)); + byte *buf = msg->message->data + msg->readcount; + c = *buf++; + c |= (*buf) << 8; + msg->readcount += 2; + return c; + } + msg->readcount = msg->message->cursize; + msg->badread = true; + return -1; +} + +VISIBLE int +MSG_ReadShortBE (qmsg_t *msg) +{ + int c; + + if (msg->readcount + 2 <= msg->message->cursize) { + byte *buf = msg->message->data + msg->readcount; + c = (*buf++) << 8; + c |= *buf; msg->readcount += 2; return c; } @@ -269,10 +365,30 @@ MSG_ReadLong (qmsg_t *msg) int c; if (msg->readcount + 4 <= msg->message->cursize) { - c = msg->message->data[msg->readcount] - + (msg->message->data[msg->readcount + 1] << 8) - + (msg->message->data[msg->readcount + 2] << 16) - + (msg->message->data[msg->readcount + 3] << 24); + byte *buf = msg->message->data + msg->readcount; + c = *buf++; + c |= (*buf++) << 8; + c |= (*buf++) << 16; + c |= (*buf) << 24; + msg->readcount += 4; + return c; + } + msg->readcount = msg->message->cursize; + msg->badread = true; + return -1; +} + +VISIBLE int +MSG_ReadLongBE (qmsg_t *msg) +{ + int c; + + if (msg->readcount + 4 <= msg->message->cursize) { + byte *buf = msg->message->data + msg->readcount; + c = (*buf++) << 24; + c |= (*buf++) << 16; + c |= (*buf++) << 8; + c |= *buf; msg->readcount += 4; return c; } @@ -343,7 +459,7 @@ MSG_ReadString (qmsg_t *msg) } VISIBLE int -MSG_ReadBytes (qmsg_t *msg, void *buf, int len) +MSG_ReadBytes (qmsg_t *msg, void *buf, unsigned len) { if (msg->badread || len > msg->message->cursize - msg->readcount) { msg->badread = true; @@ -413,8 +529,9 @@ MSG_ReadAngle16V (qmsg_t *msg, vec3_t angles) VISIBLE int MSG_ReadUTF8 (qmsg_t *msg) { - byte *buf, *start, c; - int val = 0, count; + byte *buf, *start, c, min_follow = 0x80; + int val = 0; + unsigned count; if (msg->badread || msg->message->cursize == msg->readcount) { msg->badread = true; @@ -426,7 +543,7 @@ MSG_ReadUTF8 (qmsg_t *msg) if (c < 0x80) { // 0x00 - 0x7f 1,7,7 val = c; count = 1; - } else if (c < 0xc0) { // 0x80 - 0xbf not a valid first byte + } else if (c < 0xc2) { // 0x80 - 0xc1 not a valid first byte msg->badread = true; return -1; } else if (c < 0xe0) { // 0xc0 - 0xdf 2,5,11 @@ -435,15 +552,19 @@ MSG_ReadUTF8 (qmsg_t *msg) } else if (c < 0xf0) { // 0xe0 - 0xef 3,4,16 count = 3; val = c & 0x0f; + min_follow = val == 0xe0 ? 0xa0 : 0x80; } else if (c < 0xf8) { // 0xf0 - 0xf7 4,3,21 count = 4; val = c & 0x07; + min_follow = val == 0xf0 ? 0x90 : 0x80; } else if (c < 0xfc) { // 0xf8 - 0xfb 5,2,26 count = 5; val = c & 0x03; + min_follow = val == 0xf8 ? 0x88 : 0x80; } else if (c < 0xfe) { // 0xfc - 0xfd 6,1,31 count = 6; val = c & 0x01; + min_follow = val == 0xfc ? 0x84 : 0x80; } else { // 0xfe - 0xff never valid msg->badread = true; return -1; @@ -452,6 +573,10 @@ MSG_ReadUTF8 (qmsg_t *msg) msg->badread = true; return -1; } + if (count > 2 && *buf < min_follow) { + msg->badread = true; + return -1; + } while (--count) { c = *buf++; if ((c & 0xc0) != 0x80) { diff --git a/libs/util/pakfile.c b/libs/util/pakfile.c index 5ddbbb9a2..f3f0352ed 100644 --- a/libs/util/pakfile.c +++ b/libs/util/pakfile.c @@ -66,7 +66,7 @@ pack_new (const char *name) free (pack); return 0; } - pack->file_hash = Hash_NewTable (1021, pack_get_key, 0, 0); + pack->file_hash = Hash_NewTable (1021, pack_get_key, 0, 0, 0); if (!pack->file_hash) { free (pack->filename); free (pack); @@ -161,7 +161,7 @@ pack_create (const char *name) pack_del (pack); return 0; } - strncpy (pack->header.id, "PACK", sizeof (pack->header.id)); + memcpy (pack->header.id, "PACK", sizeof (pack->header.id)); Qwrite (pack->handle, &pack->header, sizeof (pack->header)); diff --git a/libs/util/plist.c b/libs/util/plist.c new file mode 100644 index 000000000..db3c607ed --- /dev/null +++ b/libs/util/plist.c @@ -0,0 +1,1600 @@ +/* + plist.c + + Property list management + + Copyright (C) 2000 Jeff Teunissen + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#if defined(_WIN32) && defined(HAVE_MALLOC_H) +#include +#endif + +#include "qfalloca.h" + +#include "QF/darray.h" +#include "QF/dstring.h" +#include "QF/hash.h" +#include "QF/plist.h" +#include "QF/qtypes.h" +#include "QF/sys.h" + +/* + Generic property list item. +*/ +struct plitem_s { + pltype_t type; + unsigned users; + void *data; + void *user_data; + int line; +};//plitem_t + +/* + Dictionaries +*/ +struct dictkey_s { + char *key; + plitem_t *value; +}; +typedef struct dictkey_s dictkey_t; + +struct pldict_s { + hashtab_t *tab; + struct DARRAY_TYPE (dictkey_t *) keys; +}; +typedef struct pldict_s pldict_t; + +/* + Arrays +*/ +struct plarray_s { + int numvals; ///< Number of items in array + int maxvals; ///< Number of items that can be stored + ///< before a realloc is necesary. + struct plitem_s **values; ///< Array data +}; +typedef struct plarray_s plarray_t; + +/* + Typeless, unformatted binary data +*/ +struct plbinary_s { + size_t size; + void *data; +}; +typedef struct plbinary_s plbinary_t; + +typedef struct pldata_s { // Unparsed property list string + const char *ptr; + unsigned end; + unsigned pos; + unsigned line; + unsigned line_start; + dstring_t *errmsg; + hashctx_t **hashctx; +} pldata_t; + +// Ugly defines for fast checking and conversion from char to number +#define inrange(ch,min,max) ((ch) >= (min) && (ch) <= (max)) +#define char2num(ch) \ + (inrange((ch), '0', '9') ? ((ch) - '0') \ + : 10 + (inrange((ch), 'a', 'f') ? ((ch) - 'a') \ + : ((ch) - 'A'))) + +static const char *pl_types[] = { + "dictionary", + "array", + "biinary", + "string", +}; + +static byte quotable_bitmap[32]; +static inline int +is_quotable (byte x) +{ + return quotable_bitmap[x / 8] & (1 << (x % 8)); +} + +static void +init_quotables (void) +{ + const char *unquotables = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "`abcdefghijklmnopqrstuvwxyz!#$%&*+-./:?@|~_^"; + const byte *c; + memset (quotable_bitmap, ~0, sizeof (quotable_bitmap)); + for (c = (byte *) unquotables; *c; c++) + quotable_bitmap[*c / 8] &= ~(1 << (*c % 8)); +} + +static plitem_t *pl_parsepropertylistitem (pldata_t *pl); + +static const char * +dict_get_key (const void *i, void *unused) +{ + dictkey_t *item = (dictkey_t *) i; + return item->key; +} + +static void +dict_free (void *i, void *unused) +{ + dictkey_t *item = (dictkey_t *) i; + free (item->key); + if (item->value) // Make descended stuff get freed + PL_Release (item->value); + free (item); +} + +static plitem_t * +pl_newitem (pltype_t type) +{ + plitem_t *item = calloc (1, sizeof (plitem_t)); + item->type = type; + return item; +} + +VISIBLE plitem_t * +PL_NewDictionary (hashctx_t **hashctx) +{ + plitem_t *item = pl_newitem (QFDictionary); + pldict_t *dict = malloc (sizeof (pldict_t)); + dict->tab = Hash_NewTable (1021, dict_get_key, dict_free, NULL, hashctx); + DARRAY_INIT (&dict->keys, 8); + item->data = dict; + return item; +} + +VISIBLE plitem_t * +PL_NewArray (void) +{ + plitem_t *item = pl_newitem (QFArray); + plarray_t *array = calloc (1, sizeof (plarray_t)); + item->data = array; + return item; +} + +VISIBLE plitem_t * +PL_NewData (void *data, size_t size) +{ + plitem_t *item = pl_newitem (QFBinary); + plbinary_t *bin = malloc (sizeof (plbinary_t)); + item->data = bin; + bin->data = data; + bin->size = size; + return item; +} + +static plitem_t * +new_string (const char *str, size_t len, pldata_t *pl) +{ + plitem_t *item = pl_newitem (QFString); + item->data = malloc (len + 1); + memcpy (item->data, str, len); + ((char *) item->data)[len] = 0; + item->line = pl ? pl->line : 0; + return item; +} + +VISIBLE plitem_t * +PL_NewString (const char *str) +{ + return new_string (str, strlen (str), 0); +} + +VISIBLE plitem_t * +PL_Retain (plitem_t *item) +{ + if (item) { + item->users++; + } + return item; +} + +VISIBLE plitem_t * +PL_Release (plitem_t *item) +{ + pldict_t *dict; + plarray_t *array; + + if (!item || (item->users && --item->users > 0)) { + return item; + } + switch (item->type) { + case QFDictionary: + dict = item->data; + Hash_DelTable (dict->tab); + DARRAY_CLEAR (&dict->keys); + free (item->data); + break; + + case QFArray: + { + array = item->data; + int i = array->numvals; + + while (i-- > 0) { + PL_Release (array->values[i]); + } + free (array->values); + free (item->data); + } + break; + + case QFBinary: + free (((plbinary_t *) item->data)->data); + free (item->data); + break; + + case QFString: + free (item->data); + break; + case QFMultiType: + break; + } + free (item); + return 0; +} + +VISIBLE void +PL_SetUserData (plitem_t *item, void *data) +{ + item->user_data = data; +} + +VISIBLE void * +PL_GetUserData (plitem_t *item) +{ + return item->user_data; +} + +VISIBLE size_t +PL_BinarySize (const plitem_t *binary) +{ + if (!binary || binary->type != QFBinary) { + return 0; + } + + plbinary_t *bin = (plbinary_t *) binary->data; + return bin->size; +} + +VISIBLE const void * +PL_BinaryData (const plitem_t *binary) +{ + if (!binary || binary->type != QFBinary) { + return 0; + } + + plbinary_t *bin = (plbinary_t *) binary->data; + return bin->data; +} + +VISIBLE const char * +PL_String (const plitem_t *string) +{ + if (!string || string->type != QFString) { + return NULL; + } + return string->data; +} + +VISIBLE plitem_t * +PL_ObjectForKey (const plitem_t *item, const char *key) +{ + if (!item || item->type != QFDictionary) { + return NULL; + } + + pldict_t *dict = (pldict_t *) item->data; + dictkey_t *k = (dictkey_t *) Hash_Find (dict->tab, key); + return k ? k->value : NULL; +} + +VISIBLE const char * +PL_KeyAtIndex (const plitem_t *item, int index) +{ + if (!item || item->type != QFDictionary) { + return NULL; + } + + pldict_t *dict = (pldict_t *) item->data; + if (index < 0 || (size_t) index >= dict->keys.size) { + return NULL; + } + + return dict->keys.a[index]->key; +} + +VISIBLE void +PL_RemoveObjectForKey (plitem_t *item, const char *key) +{ + if (!item || item->type != QFDictionary) { + return; + } + + pldict_t *dict = (pldict_t *) item->data; + dictkey_t *k; + plitem_t *value; + + k = (dictkey_t *) Hash_Del (dict->tab, key); + if (!k) { + return; + } + value = k->value; + k->value = 0; + for (size_t i = 0; i < dict->keys.size; i++) { + if (dict->keys.a[i] == k) { + DARRAY_REMOVE_AT (&dict->keys, i); + break; + } + } + dict_free (k, 0); + value->users--; +} + +VISIBLE plitem_t * +PL_D_AllKeys (const plitem_t *item) +{ + if (!item || item->type != QFDictionary) { + return NULL; + } + + pldict_t *dict = (pldict_t *) item->data; + dictkey_t *current; + plitem_t *array; + + if (!(array = PL_NewArray ())) + return NULL; + + for (size_t i = 0; i < dict->keys.size; i++) { + current = dict->keys.a[i]; + PL_A_AddObject (array, PL_NewString (current->key)); + } + + return array; +} + +VISIBLE int +PL_D_NumKeys (const plitem_t *item) +{ + if (!item || item->type != QFDictionary) { + return 0; + } + + pldict_t *dict = (pldict_t *) item->data; + return Hash_NumElements (dict->tab); +} + +VISIBLE plitem_t * +PL_ObjectAtIndex (const plitem_t *array, int index) +{ + if (!array || array->type != QFArray) { + return NULL; + } + + plarray_t *arr = (plarray_t *) array->data; + return index >= 0 && index < arr->numvals ? arr->values[index] : NULL; +} + +VISIBLE bool +PL_D_AddObject (plitem_t *item, const char *key, plitem_t *value) +{ + if (!item || item->type != QFDictionary || !value) { + return false; + } + + pldict_t *dict = (pldict_t *) item->data; + dictkey_t *k; + + if ((k = Hash_Find (dict->tab, key))) { + PL_Retain (value); + PL_Release (k->value); + k->value = value; + } else { + k = malloc (sizeof (dictkey_t)); + + if (!k) + return false; + + PL_Retain (value); + k->key = strdup (key); + k->value = value; + + Hash_Add (dict->tab, k); + DARRAY_APPEND (&dict->keys, k); + } + return true; +} + +VISIBLE bool +PL_D_Extend (plitem_t *dstDict, plitem_t *srcDict) +{ + if (!dstDict || dstDict->type != QFDictionary + || !srcDict || srcDict->type != QFDictionary + || ((pldict_t *) srcDict->data)->keys.size < 1) { + return false; + } + pldict_t *dst = dstDict->data; + pldict_t *src = srcDict->data; + size_t count = dst->keys.size; + DARRAY_RESIZE (&dst->keys, dst->keys.size + src->keys.size);// open space + DARRAY_RESIZE (&dst->keys, count); // put size back so it's correct + for (size_t i = 0; i < src->keys.size; i++) { + dictkey_t *key = src->keys.a[i]; + dictkey_t *k; + if ((k = Hash_Find (dst->tab, key->key))) { + PL_Retain (key->value); + PL_Release (k->value); + k->value = key->value; + } else { + k = malloc (sizeof (dictkey_t)); + + if (!k) + return false; + + PL_Retain (key->value); + k->key = strdup (key->key); + k->value = key->value; + + Hash_Add (dst->tab, k); + DARRAY_APPEND (&dst->keys, k); + } + } + return true; +} + +static bool +check_array_size (plarray_t *arr, int count) +{ + if (count > arr->maxvals) { + int newmax = (count + 127) & ~127; + int size = newmax * sizeof (plitem_t *); + plitem_t **tmp = realloc (arr->values, size); + + if (!tmp) + return false; + + arr->maxvals = newmax; + arr->values = tmp; + } + return true; +} + +VISIBLE bool +PL_A_InsertObjectAtIndex (plitem_t *array, plitem_t *item, int index) +{ + if (!array || array->type != QFArray || !item) { + return false; + } + + plarray_t *arr; + + arr = (plarray_t *)array->data; + + if (!check_array_size (arr, arr->numvals + 1)) { + return false; + } + + if (index == -1) + index = arr->numvals; + + if (index < 0 || index > arr->numvals) + return false; + + memmove (arr->values + index + 1, arr->values + index, + (arr->numvals - index) * sizeof (plitem_t *)); + + PL_Retain (item); + arr->values[index] = item; + arr->numvals++; + return true; +} + +VISIBLE bool +PL_A_AddObject (plitem_t *array, plitem_t *item) +{ + return PL_A_InsertObjectAtIndex (array, item, -1); +} + +VISIBLE bool +PL_A_Extend (plitem_t *dstArray, plitem_t *srcArray) +{ + if (!dstArray || dstArray->type != QFArray + || !srcArray || srcArray->type != QFArray + || ((plarray_t *) srcArray->data)->numvals < 1) { + return false; + } + plarray_t *dst = dstArray->data; + plarray_t *src = srcArray->data; + if (!check_array_size (dst, dst->numvals + src->numvals)) { + return false; + } + for (int i = 0; i < src->numvals; i++) { + PL_Retain (src->values[i]); + dst->values[dst->numvals++] = src->values[i]; + } + return true; +} + +VISIBLE int +PL_A_NumObjects (const plitem_t *array) +{ + if (!array || array->type != QFArray) { + return 0; + } + return ((plarray_t *) array->data)->numvals; +} + +VISIBLE void +PL_RemoveObjectAtIndex (plitem_t *array, int index) +{ + if (!array || array->type != QFArray) { + return; + } + + plarray_t *arr; + plitem_t *item; + + arr = (plarray_t *)array->data; + + if (index < 0 || index >= arr->numvals) + return; + + item = arr->values[index]; + arr->numvals--; + while (index < arr->numvals) { + arr->values[index] = arr->values[index + 1]; + index++; + } + + item->users--; +} + +static void __attribute__((format(PRINTF, 2, 3))) +pl_error (pldata_t *pl, const char *fmt, ...) +{ + if (!pl->errmsg) { + pl->errmsg = dstring_new (); + } + + va_list args; + va_start (args, fmt); + dvsprintf (pl->errmsg, fmt, args); + va_end (args); +} + +static bool +pl_skipspace (pldata_t *pl, int end_ok) +{ + while (pl->pos < pl->end) { + char c = pl->ptr[pl->pos]; + + if (!isspace ((byte) c)) { + if (c == '/' && pl->pos < pl->end - 1) { // check for comments + if (pl->ptr[pl->pos + 1] == '/') { + pl->pos += 2; + + while (pl->pos < pl->end) { + c = pl->ptr[pl->pos]; + + if (c == '\n') + break; + pl->pos++; + } + if (pl->pos >= pl->end) { + // end of string in a single-line comment is always + // an error + pl_error (pl, "Reached end of string in comment"); + return false; + } + } else if (pl->ptr[pl->pos + 1] == '*') { // "/*" comments + pl->pos += 2; + + while (pl->pos < pl->end) { + c = pl->ptr[pl->pos]; + + if (c == '\n') { + pl->line++; + pl->line_start = pl->pos + 1; + } else if (c == '*' && pl->pos < pl->end - 1 + && pl->ptr[pl->pos+1] == '/') { + pl->pos++; + break; + } + pl->pos++; + } + if (pl->pos >= pl->end) { + // end of string in a multi-line comment is always + // an error + pl_error (pl, "Reached end of string in comment"); + return false; + } + } else { + return true; + } + } else { + return true; + } + } + if (c == '\n') { + pl->line++; + pl->line_start = pl->pos + 1; + } + pl->pos++; + } + if (!end_ok) { + pl_error (pl, "Reached end of string"); + } + return false; +} + +static int +pl_checknext (pldata_t *pl, const char *valid, int end_ok) +{ + if (!pl_skipspace (pl, end_ok)) { + return end_ok; + } + + char ch = pl->ptr[pl->pos]; + if (strchr (valid, ch)) { + return 1; + } + + size_t len = strlen (valid); + size_t size = 3 + (strlen (valid) - 1) * 7 + 1; + char expected[size], *p = expected; + p[0] = '\''; + p[1] = valid[0]; + p[2] = '\''; + p += 3; + for (size_t i = 1; i < len; i++, p++) { + memcpy (p, " or 'x'", 7); + p[5] = valid[i]; + } + p[0] = 0; + pl_error (pl, "Unexpected character %c (wanted %s)", ch, expected); + return 0; +} + +static inline byte +from_hex (byte a) +{ + if (a >= '0' && a <= '9') + return a - '0'; + if (a >= 'A' && a <= 'F') + return a - 'A' + 10; + return a - 'a' + 10; +} + +static inline byte +make_byte (byte h, byte l) +{ + return (from_hex (h) << 4) | from_hex (l); +} + +static int +pl_parsekeyvalue (pldata_t *pl, plitem_t *dict, int end_ok) +{ + plitem_t *key = 0; + plitem_t *value = 0; + + if (!(key = pl_parsepropertylistitem (pl))) { + return 0; + } + if (key->type != QFString) { + pl_error (pl, "Key is not a string"); + goto error; + } + + if (!pl_checknext (pl, "=", 0)) { + goto error; + } + pl->pos++; + + if (!(value = pl_parsepropertylistitem (pl))) { + goto error; + } + + if (!PL_D_AddObject (dict, PL_String (key), value)) { + goto error; + } + PL_Release (key); // don't need the key item + + if (!pl_checknext (pl, end_ok ? ";" : ";}", end_ok)) { + return 0; + } + + if (pl->ptr[pl->pos] == ';') { + pl->pos++; + } + return 1; +error: + PL_Release (key); + PL_Release (value); + return 0; +} + +static plitem_t * +pl_parsedictionary (pldata_t *pl) +{ + plitem_t *dict = PL_NewDictionary (pl->hashctx); + dict->line = pl->line; + + pl->pos++; // skip over opening { + while (pl_skipspace (pl, 0) && pl->ptr[pl->pos] != '}') { + if (!pl_parsekeyvalue (pl, dict, 0)) { + PL_Release (dict); + return NULL; + } + } + if (pl->pos >= pl->end) { + pl_error (pl, "Unexpected end of string when parsing dictionary"); + PL_Release (dict); + return NULL; + } + pl->pos++; // skip over closing } + + return dict; +} + +static int +pl_parsevalue (pldata_t *pl, plitem_t *array, int end_ok) +{ + plitem_t *value; + + if (!(value = pl_parsepropertylistitem (pl))) { + return 0; + } + if (!PL_A_AddObject (array, value)) { + pl_error (pl, "too many items in array"); + PL_Release (value); + return 0; + } + + if (!pl_checknext (pl, end_ok ? "," : ",)", end_ok)) { + return 0; + } + + if (pl->ptr[pl->pos] == ',') { + pl->pos++; + } + + return 1; +} + +static plitem_t * +pl_parsearray (pldata_t *pl) +{ + plitem_t *array = PL_NewArray (); + array->line = pl->line; + + pl->pos++; // skip over opening ( + + while (pl_skipspace (pl, 0) && pl->ptr[pl->pos] != ')') { + if (!pl_parsevalue (pl, array, 0)) { + PL_Release (array); + return NULL; + } + } + if (pl->pos >= pl->end) { + pl_error (pl, "Unexpected end of string when parsing array"); + PL_Release (array); + return NULL; + } + pl->pos++; // skip over opening ) + + return array; +} + +static plitem_t * +pl_parsebinary (pldata_t *pl) +{ + unsigned start = ++pl->pos; + int nibbles = 0, i; + char *str, c; + + while (pl->pos < pl->end) { + c = pl->ptr[pl->pos++]; + if (isxdigit ((byte) c)) { + nibbles++; + continue; + } + if (c == '>') { + if (nibbles & 1) { + pl_error (pl, "invalid data, missing nibble"); + return NULL; + } + int len = nibbles / 2; + str = malloc (len); + for (i = 0; i < len; i++) { + str[i] = make_byte (pl->ptr[start + i * 2], + pl->ptr[start + i * 2 + 1]); + } + plitem_t *item = PL_NewData (str, len); + item->line = pl->line; + return item; + } + pl_error (pl, "invalid character in data: %02x", c); + return NULL; + } + pl_error (pl, "Reached end of string while parsing data"); + return NULL; +} + +static plitem_t * +pl_parsequotedstring (pldata_t *pl) +{ + unsigned int start = ++pl->pos; + unsigned int escaped = 0; + unsigned int shrink = 0; + bool hex = false; + bool long_string = false; + plitem_t *str; + + if (pl->ptr[pl->pos] == '"' && + pl->ptr[pl->pos + 1] == '"') { + long_string = true; + start += 2; + pl->pos += 2; + } + + while (pl->pos < pl->end) { + + char c = pl->ptr[pl->pos]; + + if (escaped) { + if (escaped == 1 && inrange (c, '0', '7')) { + escaped++; + hex = false; + } else if (escaped > 1) { + if (escaped == 2 && c == 'x') { + hex = true; + shrink++; + escaped++; + } else if (hex && inrange (escaped, 3, 4) + && isxdigit ((byte) c)) { + shrink++; + escaped++; + } else if (inrange (escaped, 1, 3) && inrange (c, '0', '7')) { + shrink++; + escaped++; + } else { + pl->pos--; + escaped = 0; + } + } else { + escaped = 0; + } + } else { + if (c == '\\') { + escaped = 1; + shrink++; + } else if (c == '"' + && (!long_string || (pl->ptr[pl->pos + 1] == '"' + && pl->ptr[pl->pos + 2] == '"'))) { + break; + } + } + + if (c == '\n') { + pl->line++; + pl->line_start = pl->pos + 1; + } + + pl->pos++; + } + + if (pl->pos >= pl->end) { + pl_error (pl, "Reached end of string while parsing quoted string"); + return NULL; + } + + if (pl->pos - start - shrink == 0) { + str = new_string ("", 0, pl); + } else { + char *chars = alloca(pl->pos - start - shrink); + unsigned int j; + unsigned int k; + + escaped = 0; + hex = false; + + for (j = start, k = 0; j < pl->pos; j++) { + + char c = pl->ptr[j]; + + if (escaped) { + if (escaped == 1 && inrange (c, '0', '7')) { + chars[k] = c - '0'; + hex = false; + escaped++; + } else if (escaped > 1) { + if (escaped == 2 && c == 'x') { + hex = true; + escaped++; + } else if (hex && inrange (escaped, 3, 4) + && isxdigit ((byte) c)) { + chars[k] <<= 4; + chars[k] |= char2num (c); + escaped++; + } else if (inrange (escaped, 1, 3) + && inrange (c, '0', '7')) { + chars[k] <<= 3; + chars[k] |= (c - '0'); + escaped++; + } else { + escaped = 0; + j--; + k++; + } + } else { + escaped = 0; + switch (c) { + case 'a': + chars[k] = '\a'; + break; + case 'b': + chars[k] = '\b'; + break; + case 't': + chars[k] = '\t'; + break; + case 'r': + chars[k] = '\r'; + break; + case 'n': + chars[k] = '\n'; + break; + case 'v': + chars[k] = '\v'; + break; + case 'f': + chars[k] = '\f'; + break; + default: + chars[k] = c; + break; + } + k++; + } + } else { + chars[k] = c; + if (c == '\\') { + escaped = 1; + } else { + k++; + } + } + } + str = new_string (chars, pl->pos - start - shrink, pl); + } + if (long_string) + pl->pos += 2; + pl->pos++; + return str; +} + +static plitem_t * +pl_parseunquotedstring (pldata_t *pl) +{ + unsigned int start = pl->pos; + + while (pl->pos < pl->end) { + if (is_quotable (pl->ptr[pl->pos])) + break; + pl->pos++; + } + return new_string (&pl->ptr[start], pl->pos - start, pl); +} + +static plitem_t * +pl_parsepropertylistitem (pldata_t *pl) +{ + if (!pl_skipspace (pl, 0)) { + return NULL; + } + + switch (pl->ptr[pl->pos]) { + case '{': return pl_parsedictionary (pl); + case '(': return pl_parsearray (pl); + case '<': return pl_parsebinary (pl); + case '"': return pl_parsequotedstring (pl); + default: return pl_parseunquotedstring (pl); + } +} + +static plitem_t * +pl_parseitem (const char *string, hashctx_t **hashctx, + plitem_t *(*parse) (pldata_t *)) +{ + plitem_t *newpl = NULL; + + if (!quotable_bitmap[0]) + init_quotables (); + + pldata_t pl = { + .ptr = string, + .end = strlen (string), + .line = 1, + .hashctx = hashctx, + }; + + if (!(newpl = parse (&pl))) { + if (pl.errmsg) { + Sys_Printf ("plist: %d,%d: %s\n", pl.line, pl.pos - pl.line_start, + pl.errmsg->str); + dstring_delete (pl.errmsg); + } + return NULL; + } + return newpl; +} + +VISIBLE plitem_t * +PL_GetPropertyList (const char *string, hashctx_t **hashctx) +{ + return pl_parseitem (string, hashctx, pl_parsepropertylistitem); +} + + +static plitem_t * +pl_getdictionary (pldata_t *pl) +{ + plitem_t *dict = PL_NewDictionary (pl->hashctx); + dict->line = pl->line; + + while (pl_skipspace (pl, 1)) { + if (!pl_parsekeyvalue (pl, dict, 1)) { + PL_Release (dict); + return NULL; + } + } + + return dict; +} + +VISIBLE plitem_t * +PL_GetDictionary (const char *string, hashctx_t **hashctx) +{ + return pl_parseitem (string, hashctx, pl_getdictionary); +} + +static plitem_t * +pl_getarray (pldata_t *pl) +{ + plitem_t *array = PL_NewArray (); + array->line = pl->line; + + while (pl_skipspace (pl, 1)) { + if (!pl_parsevalue (pl, array, 1)) { + PL_Release (array); + return NULL; + } + } + + return array; +} + +VISIBLE plitem_t * +PL_GetArray (const char *string, hashctx_t **hashctx) +{ + return pl_parseitem (string, hashctx, pl_getarray); +} + +static void +write_tabs (dstring_t *dstr, int num) +{ + char *tabs = dstring_reservestr (dstr, num); + + memset (tabs, '\t', num); + tabs[num] = 0; +} + +static void +write_string_len (dstring_t *dstr, const char *str, int len) +{ + char *dst = dstring_reservestr (dstr, len); + memcpy (dst, str, len); + dst[len] = 0; +} + +static char +to_hex (byte b) +{ + char c = (b & 0xf) + '0'; + if (c > '9') + c = c - '0' + 'A'; + return c; +} + +static void +write_binary (dstring_t *dstr, byte *binary, int len) +{ + int i; + char *dst = dstring_reservestr (dstr, len * 2); + for (i = 0; i < len; i++) { + *dst++ = to_hex (binary[i] >> 4); + *dst++ = to_hex (binary[i]); + } +} + +static void +write_string (dstring_t *dstr, const char *str) +{ + const char *s; + int len = 0; + char *dst; + int quoted = 0; + + for (s = str; *s; s++) { + if (is_quotable (*s)) + quoted = 1; + len++; + } + if (!quoted) { + dst = dstring_reservestr (dstr, len); + strcpy (dst, str); + return; + } + // assume worst case of all octal chars plus two quotes. + dst = dstring_reservestr (dstr, len * 4 + 2); + *dst++= '\"'; + while (*str) { + if (*str && isascii ((byte) *str) && isprint ((byte) *str) + && *str != '\\' && *str != '\"') { + *dst++ = *str++; + continue; + } + if (*str) { + *dst++ = '\\'; + switch (*str) { + case '\"': + case '\\': + *dst++ = *str; + break; + case '\n': + *dst++ = 'n'; + break; + case '\a': + *dst++ = 'a'; + break; + case '\b': + *dst++ = 'b'; + break; + case '\f': + *dst++ = 'f'; + break; + case '\r': + *dst++ = 'r'; + break; + case '\t': + *dst++ = 't'; + break; + case '\v': + *dst++ = 'v'; + break; + default: + *dst++ = '0' + ((((byte) *str) >> 6) & 3); + *dst++ = '0' + ((((byte) *str) >> 3) & 7); + *dst++ = '0' + (((byte) *str) & 7); + break; + } + str++; + } + } + *dst++ = '\"'; + *dst++ = 0; + dstr->size = dst - dstr->str; +} + +static void +write_item (dstring_t *dstr, const plitem_t *item, int level) +{ + dictkey_t *current; + plarray_t *array; + pldict_t *dict; + plbinary_t *binary; + int i; + + switch (item->type) { + case QFDictionary: + write_string_len (dstr, "{\n", 2); + dict = (pldict_t *) item->data; + for (size_t i = 0; i < dict->keys.size; i++) { + current = dict->keys.a[i]; + write_tabs (dstr, level + 1); + write_string (dstr, current->key); + write_string_len (dstr, " = ", 3); + write_item (dstr, current->value, level + 1); + write_string_len (dstr, ";\n", 2); + } + write_tabs (dstr, level); + write_string_len (dstr, "}", 1); + break; + case QFArray: + write_string_len (dstr, "(\n", 2); + array = (plarray_t *) item->data; + for (i = 0; i < array->numvals; i++) { + write_tabs (dstr, level + 1); + write_item (dstr, array->values[i], level + 1); + if (i < array->numvals - 1) + write_string_len (dstr, ",\n", 2); + } + write_string_len (dstr, "\n", 1); + write_tabs (dstr, level); + write_string_len (dstr, ")", 1); + break; + case QFBinary: + write_string_len (dstr, "<", 1); + binary = (plbinary_t *) item->data; + write_binary (dstr, binary->data, binary->size); + write_string_len (dstr, ">", 1); + break; + case QFString: + write_string (dstr, item->data); + break; + default: + break; + } +} + +VISIBLE char * +PL_WritePropertyList (const plitem_t *pl) +{ + dstring_t *dstr = dstring_newstr (); + + if (pl) { + if (!quotable_bitmap[0]) + init_quotables (); + write_item (dstr, pl, 0); + write_string_len (dstr, "\n", 1); + } + return dstring_freeze (dstr); +} + +VISIBLE pltype_t +PL_Type (const plitem_t *item) +{ + return item->type; +} + +VISIBLE int +PL_Line (const plitem_t *item) +{ + return item->line; +} + +VISIBLE void +PL_Message (plitem_t *messages, const plitem_t *item, const char *fmt, ...) +{ + va_list args; + dstring_t *va_str; + dstring_t *msg_str; + char *msg; + + va_str = dstring_new (); + msg_str = dstring_new (); + + va_start (args, fmt); + dvsprintf (va_str, fmt, args); + va_end (args); + + if (item) { + msg = dsprintf (msg_str, "%d: %s", item->line, va_str->str); + } else { + msg = dsprintf (msg_str, "internal: %s", va_str->str); + } + PL_A_AddObject (messages, PL_NewString (msg)); + dstring_delete (va_str); + dstring_delete (msg_str); +} + +static int +pl_default_parser (const plfield_t *field, const plitem_t *item, void *data, + plitem_t *messages, void *context) +{ + switch (field->type) { + case QFDictionary: + { + *(hashtab_t **)data = ((pldict_t *)item->data)->tab; + } + return 1; + case QFArray: + { + plarray_t *array = (plarray_t *)item->data; + typedef struct DARRAY_TYPE (plitem_t *) arraydata_t; + arraydata_t *arraydata = DARRAY_ALLOCFIXED(arraydata_t, + array->numvals, + malloc); + memcpy (arraydata->a, array->values, + array->numvals * sizeof (arraydata->a[0])); + } + return 1; + case QFBinary: + { + plbinary_t *binary = (plbinary_t *)item->data; + typedef struct DARRAY_TYPE (byte) bindata_t; + bindata_t *bindata = DARRAY_ALLOCFIXED(bindata_t, + binary->size, malloc); + memcpy (bindata->a, binary->data, binary->size); + *(bindata_t **)data = bindata; + } + return 1; + case QFString: + *(char **)data = (char *)item->data; + return 1; + case QFMultiType: + break; + } + PL_Message (messages, 0, "invalid item type: %d", field->type); + return 0; +} + +VISIBLE int +PL_CheckType (pltype_t field_type, pltype_t item_type) +{ + if (field_type & QFMultiType) { + // field_type is a mask of allowed types + return field_type & (1 << item_type); + } else { + // exact match + return field_type == item_type; + } +} + +VISIBLE void +PL_TypeMismatch (plitem_t *messages, const plitem_t *item, const char *name, + pltype_t field_type, pltype_t item_type) +{ + const int num_types = sizeof (pl_types) / sizeof (pl_types[0]); + if (field_type & QFMultiType) { + PL_Message (messages, item, + "error: %s is the wrong type. Got %s, expected one of:", + name, pl_types[item_type]); + field_type &= ~QFMultiType; + for (int type = 0; field_type && type < num_types; + type++, field_type >>= 1) { + if (field_type & 1) { + PL_Message (messages, item, " %s", pl_types[type]); + } + } + } else { + PL_Message (messages, item, + "error: %s is the wrong type. Got %s, expected %s", name, + pl_types[item_type], pl_types[field_type]); + } +} + +VISIBLE int +PL_ParseStruct (const plfield_t *fields, const plitem_t *item, void *data, + plitem_t *messages, void *context) +{ + pldict_t *dict = item->data; + dictkey_t *current; + int result = 1; + plparser_t parser; + + if (item->type != QFDictionary) { + PL_Message (messages, item, "error: not a dictionary object"); + return 0; + } + + + for (size_t i = 0; i < dict->keys.size; i++) { + const plfield_t *f; + current = dict->keys.a[i]; + for (f = fields; f->name; f++) { + if (strcmp (f->name, current->key) == 0) { + plitem_t *item = current->value; + void *flddata = (byte *)data + f->offset; + + if (f->parser) { + parser = f->parser; + } else { + parser = pl_default_parser; + } + if (!PL_CheckType (f->type, item->type)) { + PL_TypeMismatch (messages, item, current->key, + f->type, item->type); + result = 0; + } else { + if (!parser (f, item, flddata, messages, context)) { + result = 0; + } + } + break; + } + } + if (!f->name) { + PL_Message (messages, item, "error: unknown field '%s'", + current->key); + result = 0; + } + } + return result; +} + +VISIBLE int +PL_ParseArray (const plfield_t *field, const plitem_t *array, void *data, + plitem_t *messages, void *context) +{ + int result = 1; + plparser_t parser; + plarray_t *plarray = (plarray_t *) array->data; + plelement_t *element = (plelement_t *) field->data; + typedef struct arr_s DARRAY_TYPE(byte) arr_t; + arr_t *arr; + plfield_t f = { 0, 0, element->type, element->parser, element->data }; + + if (array->type != QFArray) { + PL_Message (messages, array, "error: not an array object"); + return 0; + } + if (f.parser) { + parser = f.parser; + } else { + parser = pl_default_parser; + } + + arr = DARRAY_ALLOCFIXED_OBJ (arr_t, plarray->numvals * element->stride, + element->alloc, context); + memset (arr->a, 0, arr->size); + // the array is allocated using bytes, but need the actual number of + // elements in the array + arr->size = arr->maxSize = plarray->numvals; + + for (int i = 0; i < plarray->numvals; i++) { + plitem_t *item = plarray->values[i]; + void *eledata = &arr->a[i * element->stride]; + + f.offset = i; + if (!PL_CheckType (element->type, item->type)) { + char index[16]; + snprintf (index, sizeof(index) - 1, "%d", i); + index[15] = 0; + PL_TypeMismatch (messages, item, index, element->type, item->type); + result = 0; + } else { + if (!parser (&f, item, eledata, messages, context)) { + result = 0; + } + } + } + *(arr_t **) data = arr; + return result; +} + +VISIBLE int +PL_ParseLabeledArray (const plfield_t *field, const plitem_t *item, + void *data, plitem_t *messages, void *context) +{ + pldict_t *dict = item->data; + dictkey_t *current; + int result = 1; + plparser_t parser; + plelement_t *element = (plelement_t *) field->data; + typedef struct arr_s DARRAY_TYPE(byte) arr_t; + arr_t *arr; + plfield_t f = { 0, 0, element->type, element->parser, element->data }; + + if (item->type != QFDictionary) { + PL_Message (messages, item, "error: not a dictionary object"); + return 0; + } + if (f.parser) { + parser = f.parser; + } else { + parser = pl_default_parser; + } + + arr = DARRAY_ALLOCFIXED_OBJ (arr_t, dict->keys.size * element->stride, + element->alloc, context); + memset (arr->a, 0, arr->size); + // the array is allocated using bytes, but need the actual number of + // elements in the array + arr->size = arr->maxSize = dict->keys.size; + + for (size_t i = 0; i < dict->keys.size; i++) { + current = dict->keys.a[i]; + plitem_t *item = current->value; + void *eledata = &arr->a[i * element->stride]; + + f.name = current->key; + f.offset = i; + if (!PL_CheckType (element->type, item->type)) { + char index[16]; + snprintf (index, sizeof(index) - 1, "%zd", i); + index[15] = 0; + PL_TypeMismatch (messages, item, index, element->type, item->type); + result = 0; + } else { + if (!parser (&f, item, eledata, messages, context)) { + result = 0; + } + } + } + *(arr_t **) data = arr; + return result; +} + +VISIBLE int +PL_ParseSymtab (const plfield_t *field, const plitem_t *item, void *data, + plitem_t *messages, void *context) +{ + pldict_t *dict = item->data; + dictkey_t *current; + int result = 1; + plparser_t parser; + __auto_type tab = (hashtab_t *) data; + + plelement_t *element = (plelement_t *) field->data; + plfield_t f = { 0, 0, element->type, element->parser, element->data }; + + if (item->type != QFDictionary) { + PL_Message (messages, item, "error: not a dictionary object"); + return 0; + } + + if (f.parser) { + parser = f.parser; + } else { + PL_Message (messages, item, "no parser set"); + return 0; + } + + void *obj = element->alloc (context, element->stride); + memset (obj, 0, element->stride); + for (size_t i = 0; i < dict->keys.size; i++) { + current = dict->keys.a[i]; + const char *key = current->key; + plitem_t *item = current->value; + + if (!PL_CheckType (element->type, item->type)) { + PL_TypeMismatch (messages, item, key, element->type, item->type); + result = 0; + continue; + } + f.name = key; + if (Hash_Find (tab, key)) { + PL_Message (messages, item, "duplicate name"); + result = 0; + } else { + if (!parser (&f, item, obj, messages, context)) { + result = 0; + } else { + Hash_Add (tab, obj); + obj = element->alloc (context, element->stride); + memset (obj, 0, element->stride); + } + } + } + Hash_Free (tab, obj); + return result; +} diff --git a/libs/util/plugin.c b/libs/util/plugin.c index 31fa725fd..6145afd9f 100644 --- a/libs/util/plugin.c +++ b/libs/util/plugin.c @@ -52,11 +52,12 @@ # endif #endif +#include "QF/cmd.h" #include "QF/cvar.h" #include "QF/hash.h" #include "QF/plugin.h" #include "QF/sys.h" -#include "QF/cmd.h" +#include "QF/va.h" #include "QF/plugin/general.h" @@ -70,7 +71,15 @@ typedef struct loaded_plugin_s { plugin_t *plugin; } loaded_plugin_t; -cvar_t *fs_pluginpath; +char *fs_pluginpath; +static cvar_t fs_pluginpath_cvar = { + .name = "fs_pluginpath", + .description = + "Location of your plugin directory", + .default_value = FS_PLUGINPATH, + .flags = CVAR_ROM, + .value = { .type = 0, .value = &fs_pluginpath }, +}; hashtab_t *registered_plugins, *loaded_plugins; @@ -98,11 +107,13 @@ loaded_plugin_delete (void *lp, void *unused) static int pi_close_lib (void *handle) { + if (handle) { #if defined(HAVE_DLOPEN) - return (dlclose (handle) == 0); + return (dlclose (handle) == 0); #elif defined (_WIN32) - return (FreeLibrary (handle) == 0); + return (FreeLibrary (handle) == 0); #endif + } return 1; } @@ -148,36 +159,34 @@ pi_open_lib (const char *name, int global_syms) return dlhand; } -static void -pi_realname (char *realname, int size, const char *type, const char *name) +static const char * +pi_realname (const char *type, const char *name) { #if defined(HAVE_DLOPEN) - const char *format = "%s/%s_%s.so"; + return va (0, "%s/%s_%s.so", fs_pluginpath, type, name); #elif defined(_WIN32) - const char *format = "%s/%s_%s.dll"; + return va (0, "%s/%s_%s.dll", fs_pluginpath, type, name); #else - const char *format = "No shared library support. FIXME"; + return "No shared library support. FIXME"; #endif - - snprintf (realname, size, format, fs_pluginpath->string, type, name); } -static void -pi_info_name (char *info_name, int size, const char *type, const char *name) +static const char * +pi_info_name (const char *type, const char *name) { - if (type && name) - snprintf (info_name, size, "%s_%s_PluginInfo", type, name); - else if (type) - snprintf (info_name, size, "%s_PluginInfo", type); - else - snprintf (info_name, size, "PluginInfo"); + if (type && name) { + return va (0, "%s_%s_PluginInfo", type, name); + } else if (type) { + return va (0, "%s_PluginInfo", type); + } else { + return "PluginInfo"; + } } static void PI_InitCvars (void) { - fs_pluginpath = Cvar_Get ("fs_pluginpath", FS_PLUGINPATH, CVAR_ROM, NULL, - "Location of your plugin directory"); + Cvar_Register (&fs_pluginpath_cvar, 0, 0); } static void @@ -195,18 +204,15 @@ PI_Plugin_Load_f (void) name = Cmd_Argv(2); pi = PI_LoadPlugin (type, name); - if (!pi) + if (!pi) { Sys_Printf ("Error loading plugin %s %s\n", type, name); - - else if (pi->functions && pi->functions->general && - pi->functions->general->p_Init) - pi->functions->general->p_Init (); + } } static void PI_Plugin_Unload_f (void) { - char plugin_name[1024]; + const char *plugin_name; loaded_plugin_t *lp; plugin_t *pi; @@ -216,8 +222,8 @@ PI_Plugin_Unload_f (void) } // try to locate the plugin - snprintf (plugin_name, sizeof (plugin_name), "%s_%s", Cmd_Argv(1), - Cmd_Argv(2)); + plugin_name = va (0, "%s_%s", Cmd_Argv(1), Cmd_Argv(2)); + lp = Hash_Find (loaded_plugins, plugin_name); if (lp) { pi = lp->plugin; @@ -229,27 +235,8 @@ PI_Plugin_Unload_f (void) PI_UnloadPlugin (pi); } -/* - PI_Init - - Eventually this function will likely initialize libltdl. It doesn't, yet, - since we aren't using libltdl yet. -*/ -VISIBLE void -PI_Init (void) -{ - PI_InitCvars (); - registered_plugins = Hash_NewTable (253, plugin_get_key, 0, 0); - loaded_plugins = Hash_NewTable(253, loaded_plugin_get_key, - loaded_plugin_delete, 0); - Cmd_AddCommand("plugin_load", PI_Plugin_Load_f, - "load the plugin of the given type name and name"); - Cmd_AddCommand("plugin_unload", PI_Plugin_Unload_f, - "unload the plugin of the given type name and name"); -} - -void -PI_Shutdown (void) +static void +PI_Shutdown (void *data) { void **elems, **cur; @@ -260,29 +247,54 @@ PI_Shutdown (void) free (elems); Hash_DelTable (loaded_plugins); + Hash_DelTable (registered_plugins); +} +/* + PI_Init + + Eventually this function will likely initialize libltdl. It doesn't, yet, + since we aren't using libltdl yet. +*/ +VISIBLE void +PI_Init (void) +{ + Sys_RegisterShutdown (PI_Shutdown, 0); + + PI_InitCvars (); + registered_plugins = Hash_NewTable (253, plugin_get_key, 0, 0, 0); + loaded_plugins = Hash_NewTable(253, loaded_plugin_get_key, + loaded_plugin_delete, 0, 0); + Cmd_AddCommand("plugin_load", PI_Plugin_Load_f, + "load the plugin of the given type name and name"); + Cmd_AddCommand("plugin_unload", PI_Plugin_Unload_f, + "unload the plugin of the given type name and name"); } VISIBLE plugin_t * PI_LoadPlugin (const char *type, const char *name) { - char realname[4096]; - char plugin_name[1024]; - char plugin_info_name[1024]; - char *tmpname; - void *dlhand = NULL; - plugin_t *plugin = NULL; - P_PluginInfo plugin_info = NULL; - plugin_list_t *pl; + const char *realname = 0; + const char *plugin_name = 0; + const char *plugin_info_name = 0; + const char *tmpname; + void *dlhand = 0; + plugin_t *plugin = 0; + plugin_info_t plugin_info = 0; + plugin_list_t *pl; loaded_plugin_t *lp; if (!name) return NULL; - tmpname = strrchr (name, '/'); // Get the base name, don't allow paths + // Get the base name, don't allow paths + tmpname = strrchr (name, '/'); + if (tmpname) { + name = tmpname + 1; // skip over '/' + } // Build the plugin name - snprintf (plugin_name, sizeof (plugin_name), "%s_%s", type, name); + plugin_name = va (0, "%s_%s", type, name); // make sure we're not already loaded lp = Hash_Find (loaded_plugins, plugin_name); @@ -297,8 +309,7 @@ PI_LoadPlugin (const char *type, const char *name) } if (!plugin_info) { // Build the path to the file to load - pi_realname (realname, sizeof (realname), type, - (tmpname ? tmpname + 1 : name)); + realname = pi_realname (type, name); if (!(dlhand = pi_open_lib (realname, 0))) { // lib not found @@ -308,15 +319,13 @@ PI_LoadPlugin (const char *type, const char *name) } // Build the plugin info name as $type_$name_PluginInfo - pi_info_name (plugin_info_name, sizeof (plugin_info_name), type, name); + plugin_info_name = pi_info_name (type, name); if (!(plugin_info = pi_get_symbol (dlhand, plugin_info_name))) { // Build the plugin info name as $type_PluginInfo - pi_info_name (plugin_info_name, sizeof (plugin_info_name), - type, 0); + plugin_info_name = pi_info_name (type, 0); if (!(plugin_info = pi_get_symbol (dlhand, plugin_info_name))) { // Build the plugin info name as PluginInfo - pi_info_name (plugin_info_name, sizeof (plugin_info_name), - 0, 0); + plugin_info_name = pi_info_name (0, 0); if (!(plugin_info = pi_get_symbol (dlhand, plugin_info_name))) { // info function not found pi_close_lib (dlhand); @@ -342,7 +351,7 @@ PI_LoadPlugin (const char *type, const char *name) // try to reopen if (!(dlhand = pi_open_lib (realname, 1))) { Sys_Printf ("Error reopening plugin \"%s\".\n", realname); - Sys_MaskPrintf (SYS_DEV, "Reason: \"%s\".\n", pi_error); + Sys_MaskPrintf (SYS_dev, "Reason: \"%s\".\n", pi_error); return NULL; } @@ -376,25 +385,32 @@ PI_LoadPlugin (const char *type, const char *name) plugin->full_name = lp->name; plugin->handle = dlhand; + + if (plugin->functions && plugin->functions->general + && plugin->functions->general->init) { + plugin->functions->general->init (); + } return plugin; } -VISIBLE qboolean +VISIBLE bool PI_UnloadPlugin (plugin_t *plugin) { - if (plugin - && plugin->functions - && plugin->functions->general - && plugin->functions->general->p_Shutdown) { - plugin->functions->general->p_Shutdown (); + // Remove the plugin from the set of loaded plugins to ensure that a + // shutdown triggered by an error in the unload process doesn't try to + // unload the plugin a second time + loaded_plugin_t *lp = Hash_Del (loaded_plugins, plugin->full_name); + + if (plugin && plugin->functions && plugin->functions->general + && plugin->functions->general->shutdown) { + plugin->functions->general->shutdown (); } else { - Sys_MaskPrintf (SYS_DEV, + Sys_MaskPrintf (SYS_dev, "Warning: No shutdown function for type %d plugin!\n", plugin->type); } - // remove from the table of loaded plugins - Hash_Free (loaded_plugins, Hash_Del (loaded_plugins, plugin->full_name)); + Hash_Free (loaded_plugins, lp); if (!plugin->handle) // we didn't load it return true; diff --git a/libs/util/qargs.c b/libs/util/qargs.c index 5ba36b842..11e97d15c 100644 --- a/libs/util/qargs.c +++ b/libs/util/qargs.c @@ -46,11 +46,29 @@ #include "QF/cvar.h" #include "QF/idparse.h" #include "QF/qargs.h" +#include "QF/quakefs.h" #include "QF/qtypes.h" #include "QF/sys.h" +#include "QF/va.h" -cvar_t *fs_globalcfg; -cvar_t *fs_usercfg; +char *fs_globalcfg; +static cvar_t fs_globalcfg_cvar = { + .name = "fs_globalcfg", + .description = + "global configuration file", + .default_value = FS_GLOBALCFG, + .flags = CVAR_ROM, + .value = { .type = 0, .value = &fs_globalcfg }, +}; +char *fs_usercfg; +static cvar_t fs_usercfg_cvar = { + .name = "fs_usercfg", + .description = + "user configuration file", + .default_value = FS_USERCFG, + .flags = CVAR_ROM, + .value = { .type = 0, .value = &fs_usercfg }, +}; static const char **largv; static const char *argvdummy = " "; @@ -65,7 +83,7 @@ VISIBLE int com_argc; VISIBLE const char **com_argv; const char *com_cmdline; -VISIBLE qboolean nouse = false; // 1999-10-29 +USE fix by Maddes +VISIBLE bool nouse = false; // 1999-10-29 +USE fix by Maddes /* @@ -93,20 +111,20 @@ COM_CheckParm (const char *parm) VISIBLE void COM_InitArgv (int argc, const char **argv) { - qboolean safe; + bool safe; int i, len; char *cmdline; safe = false; largv = (const char **) calloc (1, (argc + NUM_SAFE_ARGVS + 1) * - sizeof (const char **)); + sizeof (const char *)); for (com_argc = 0, len = 0; com_argc < argc; com_argc++) { largv[com_argc] = argv[com_argc]; if ((argv[com_argc]) && !strcmp ("-safe", argv[com_argc])) safe = true; - if (com_argc) + if (argv[com_argc]) len += strlen (argv[com_argc]) + 1; } @@ -151,37 +169,89 @@ COM_AddParm (const char *parm) } void -COM_ParseConfig (void) +COM_ParseConfig (cbuf_t *cbuf) { - cbuf_t *cbuf; - - cbuf = Cbuf_New (&id_interp); - // execute +set as early as possible Cmd_StuffCmds (cbuf); Cbuf_Execute_Sets (cbuf); - // execute the global configuration file if it exists - // would have been nice if Cmd_Exec_f could have been used, but it - // reads from only within the quake file system, and changing that is - // probably Not A Good Thing (tm). - fs_globalcfg = Cvar_Get ("fs_globalcfg", FS_GLOBALCFG, CVAR_ROM, NULL, - "global configuration file"); - Cmd_Exec_File (cbuf, fs_globalcfg->string, 0); + // execute set commands in the global configuration file if it exists + Cvar_Register (&fs_globalcfg_cvar, 0, 0); + Cmd_Exec_File (cbuf, fs_globalcfg, 0); Cbuf_Execute_Sets (cbuf); // execute +set again to override the config file Cmd_StuffCmds (cbuf); Cbuf_Execute_Sets (cbuf); - fs_usercfg = Cvar_Get ("fs_usercfg", FS_USERCFG, CVAR_ROM, NULL, - "user configuration file"); - Cmd_Exec_File (cbuf, fs_usercfg->string, 0); + // execute set commands in the user configuration file if it exists + Cvar_Register (&fs_usercfg_cvar, 0, 0); + Cmd_Exec_File (cbuf, fs_usercfg, 0); Cbuf_Execute_Sets (cbuf); // execute +set again to override the config file Cmd_StuffCmds (cbuf); Cbuf_Execute_Sets (cbuf); - - Cbuf_Delete (cbuf); +} + +int +COM_Check_quakerc (const char *cmd, cbuf_t *cbuf) +{ + const char *l, *p; + int ret = 0; + QFile *f; + + f = QFS_FOpenFile ("quake.rc"); + while (f && (l = Qgetline (f))) { + if ((p = strstr (l, cmd))) { + if (p == l) { + if (cbuf) { + Cbuf_AddText (cbuf, l); + } + ret = 1; + break; + } + } + } + Qclose (f); + return ret; +} + +void +COM_ExecConfig (cbuf_t *cbuf, int skip_quakerc) +{ + // quakeforge.cfg overrides quake.rc as it contains quakeforge-specific + // commands. If it doesn't exist, then this is the first time quakeforge + // has been used in this installation, thus any existing legacy config + // should be used to set up defaults on the assumption that the user has + // things set up to work with another (hopefully compatible) client + if (Cmd_Exec_File (cbuf, "quakeforge.cfg", 1)) { + Cmd_Exec_File (cbuf, fs_usercfg, 0); + Cmd_StuffCmds (cbuf); + COM_Check_quakerc ("startdemos", cbuf); + } else { + if (!skip_quakerc) { + Cbuf_InsertText (cbuf, "exec quake.rc\n"); + } + Cmd_Exec_File (cbuf, fs_usercfg, 0); + // Reparse the command line for + commands. + // (sets still done, but it doesn't matter) + // (Note, no non-base commands exist yet) + if (skip_quakerc || !COM_Check_quakerc ("stuffcmds", 0)) { + Cmd_StuffCmds (cbuf); + } + } +} + +static void +qargs_shutdown (void *data) +{ + free (largv); + free ((char *) com_cmdline); +} + +static void __attribute__((constructor)) +qargs_init (void) +{ + Sys_RegisterShutdown (qargs_shutdown, 0); } diff --git a/libs/util/qendian.c b/libs/util/qendian.c index 12873af9f..d1bfbb56f 100644 --- a/libs/util/qendian.c +++ b/libs/util/qendian.c @@ -41,9 +41,9 @@ */ #ifndef WORDS_BIGENDIAN -VISIBLE qboolean bigendien = false;; +VISIBLE bool bigendien = false; #else -VISIBLE qboolean bigendien = true;; +VISIBLE bool bigendien = true; #endif diff --git a/libs/util/qfplist.c b/libs/util/qfplist.c deleted file mode 100644 index 03a85b5b2..000000000 --- a/libs/util/qfplist.c +++ /dev/null @@ -1,1039 +0,0 @@ -/* - qfplist.c - - Property list management - - Copyright (C) 2000 Jeff Teunissen - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include -#include - -#if defined(_WIN32) && defined(HAVE_MALLOC_H) -#include -#endif - -#include "qfalloca.h" - -#include "QF/dstring.h" -#include "QF/hash.h" -#include "QF/qfplist.h" -#include "QF/qtypes.h" -#include "QF/sys.h" - -/* - Generic property list item. -*/ -struct plitem_s { - pltype_t type; - void *data; -}; - -/* - Dictionaries -*/ -struct dictkey_s { - char *key; - plitem_t *value; -}; -typedef struct dictkey_s dictkey_t; - -/* - Arrays -*/ -struct plarray_s { - int numvals; ///< Number of items in array - int maxvals; ///< Number of items that can be stored - ///< before a realloc is necesary. - struct plitem_s **values; ///< Array data -}; -typedef struct plarray_s plarray_t; - -/* - Typeless, unformatted binary data -*/ -struct plbinary_s { - size_t size; - void *data; -}; -typedef struct plbinary_s plbinary_t; - -typedef struct pldata_s { // Unparsed property list string - const char *ptr; - unsigned int end; - unsigned int pos; - unsigned int line; - const char *error; -} pldata_t; - -// Ugly defines for fast checking and conversion from char to number -#define inrange(ch,min,max) ((ch) >= (min) && (ch) <= (max)) -#define char2num(ch) \ - (inrange((ch), '0', '9') ? ((ch) - '0') \ - : 10 + (inrange((ch), 'a', 'f') ? ((ch) - 'a') \ - : ((ch) - 'A'))) - -static byte quotable_bitmap[32]; -static inline int -is_quotable (byte x) -{ - return quotable_bitmap[x / 8] & (1 << (x % 8)); -} - -static void -init_quotables (void) -{ - const char *unquotables = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz!#$%&*+-./:?@|~_^"; - const byte *c; - memset (quotable_bitmap, ~0, sizeof (quotable_bitmap)); - for (c = (byte *) unquotables; *c; c++) - quotable_bitmap[*c / 8] &= ~(1 << (*c % 8)); -} - -static plitem_t *PL_ParsePropertyListItem (pldata_t *); -static qboolean PL_SkipSpace (pldata_t *); -static char *PL_ParseQuotedString (pldata_t *); -static char *PL_ParseUnquotedString (pldata_t *); -static char *PL_ParseData (pldata_t *, int *); - -static const char * -dict_get_key (const void *i, void *unused) -{ - dictkey_t *item = (dictkey_t *) i; - return item->key; -} - -static void -dict_free (void *i, void *unused) -{ - dictkey_t *item = (dictkey_t *) i; - free (item->key); - if (item->value) // Make descended stuff get freed - PL_Free (item->value); - free (item); -} - -static plitem_t * -PL_NewItem (pltype_t type) -{ - plitem_t *item = calloc (1, sizeof (plitem_t)); - item->type = type; - return item; -} - -VISIBLE plitem_t * -PL_NewDictionary (void) -{ - plitem_t *item = PL_NewItem (QFDictionary); - hashtab_t *dict = Hash_NewTable (1021, dict_get_key, dict_free, NULL); - item->data = dict; - return item; -} - -VISIBLE plitem_t * -PL_NewArray (void) -{ - plitem_t *item = PL_NewItem (QFArray); - plarray_t *array = calloc (1, sizeof (plarray_t)); - item->data = array; - return item; -} - -VISIBLE plitem_t * -PL_NewData (void *data, size_t size) -{ - plitem_t *item = PL_NewItem (QFBinary); - plbinary_t *bin = malloc (sizeof (plbinary_t)); - item->data = bin; - bin->data = data; - bin->size = size; - return item; -} - -static plitem_t * -new_string (char *str) -{ - plitem_t *item = PL_NewItem (QFString); - item->data = str; - return item; -} - -VISIBLE plitem_t * -PL_NewString (const char *str) -{ - return new_string (strdup (str)); -} - -VISIBLE void -PL_Free (plitem_t *item) -{ - switch (item->type) { - case QFDictionary: - Hash_DelTable (item->data); - break; - - case QFArray: { - int i = ((plarray_t *) item->data)->numvals; - - while (i-- > 0) { - PL_Free (((plarray_t *) item->data)->values[i]); - } - free (((plarray_t *) item->data)->values); - free (item->data); - } - break; - - case QFBinary: - free (((plbinary_t *) item->data)->data); - free (item->data); - break; - - case QFString: - free (item->data); - break; - } - free (item); -} - -VISIBLE const char * -PL_String (plitem_t *string) -{ - if (string->type != QFString) - return NULL; - return string->data; -} - -VISIBLE plitem_t * -PL_ObjectForKey (plitem_t *dict, const char *key) -{ - hashtab_t *table = (hashtab_t *) dict->data; - dictkey_t *k; - - if (dict->type != QFDictionary) - return NULL; - - k = (dictkey_t *) Hash_Find (table, key); - return k ? k->value : NULL; -} - -VISIBLE plitem_t * -PL_RemoveObjectForKey (plitem_t *dict, const char *key) -{ - hashtab_t *table = (hashtab_t *) dict->data; - dictkey_t *k; - plitem_t *value; - - if (dict->type != QFDictionary) - return NULL; - - k = (dictkey_t *) Hash_Del (table, key); - if (!k) - return NULL; - value = k->value; - k->value = 0; - dict_free (k, 0); - return value; -} - -VISIBLE plitem_t * -PL_D_AllKeys (plitem_t *dict) -{ - void **list, **l; - dictkey_t *current; - plitem_t *array; - - if (dict->type != QFDictionary) - return NULL; - - if (!(l = list = Hash_GetList ((hashtab_t *) dict->data))) - return NULL; - - if (!(array = PL_NewArray ())) - return NULL; - - while ((current = (dictkey_t *) *l++)) { - PL_A_AddObject (array, PL_NewString (current->key)); - } - free (list); - - return array; -} - -VISIBLE int -PL_D_NumKeys (plitem_t *dict) -{ - if (dict->type != QFDictionary) - return 0; - return Hash_NumElements ((hashtab_t *) dict->data); -} - -VISIBLE plitem_t * -PL_ObjectAtIndex (plitem_t *array, int index) -{ - plarray_t *arr = (plarray_t *) array->data; - - if (array->type != QFArray) - return NULL; - - return index >= 0 && index < arr->numvals ? arr->values[index] : NULL; -} - -VISIBLE qboolean -PL_D_AddObject (plitem_t *dict, const char *key, plitem_t *value) -{ - dictkey_t *k; - - if (dict->type != QFDictionary) - return false; - - if ((k = Hash_Find ((hashtab_t *)dict->data, key))) { - PL_Free ((plitem_t *) k->value); - k->value = value; - } else { - k = malloc (sizeof (dictkey_t)); - - if (!k) - return false; - - k->key = strdup (key); - k->value = value; - - Hash_Add ((hashtab_t *)dict->data, k); - } - return true; -} - -VISIBLE qboolean -PL_A_InsertObjectAtIndex (plitem_t *array, plitem_t *item, int index) -{ - plarray_t *arr; - - if (array->type != QFArray) - return false; - - arr = (plarray_t *)array->data; - - if (arr->numvals == arr->maxvals) { - int size = (arr->maxvals + 128) * sizeof (plitem_t *); - plitem_t **tmp = realloc (arr->values, size); - - if (!tmp) - return false; - - arr->maxvals += 128; - arr->values = tmp; - memset (arr->values + arr->numvals, 0, - (arr->maxvals - arr->numvals) * sizeof (plitem_t *)); - } - - if (index == -1) - index = arr->numvals; - - if (index < 0 || index > arr->numvals) - return false; - - memmove (arr->values + index + 1, arr->values + index, - (arr->numvals - index) * sizeof (plitem_t *)); - arr->values[index] = item; - arr->numvals++; - return true; -} - -VISIBLE qboolean -PL_A_AddObject (plitem_t *array, plitem_t *item) -{ - return PL_A_InsertObjectAtIndex (array, item, -1); -} - -VISIBLE int -PL_A_NumObjects (plitem_t *array) -{ - if (array->type != QFArray) - return 0; - return ((plarray_t *) array->data)->numvals; -} - -VISIBLE plitem_t * -PL_RemoveObjectAtIndex (plitem_t *array, int index) -{ - plarray_t *arr; - plitem_t *item; - - if (array->type != QFArray) - return 0; - - arr = (plarray_t *)array->data; - - if (index < 0 || index >= arr->numvals) - return 0; - - item = arr->values[index]; - arr->numvals--; - while (index < arr->numvals) { - arr->values[index] = arr->values[index + 1]; - index++; - } - - return item; -} - -static qboolean -PL_SkipSpace (pldata_t *pl) -{ - while (pl->pos < pl->end) { - char c = pl->ptr[pl->pos]; - - if (!isspace ((byte) c)) { - if (c == '/' && pl->pos < pl->end - 1) { // check for comments - if (pl->ptr[pl->pos + 1] == '/') { - pl->pos += 2; - - while (pl->pos < pl->end) { - c = pl->ptr[pl->pos]; - - if (c == '\n') - break; - pl->pos++; - } - if (pl->pos >= pl->end) { - pl->error = "Reached end of string in comment"; - return false; - } - } else if (pl->ptr[pl->pos + 1] == '*') { // "/*" comments - pl->pos += 2; - - while (pl->pos < pl->end) { - c = pl->ptr[pl->pos]; - - if (c == '\n') { - pl->line++; - } else if (c == '*' && pl->pos < pl->end - 1 - && pl->ptr[pl->pos+1] == '/') { - pl->pos++; - break; - } - pl->pos++; - } - if (pl->pos >= pl->end) { - pl->error = "Reached end of string in comment"; - return false; - } - } else { - return true; - } - } else { - return true; - } - } - if (c == '\n') { - pl->line++; - } - pl->pos++; - } - pl->error = "Reached end of string"; - return false; -} - -static inline byte -from_hex (byte a) -{ - if (a >= '0' && a <= '9') - return a - '0'; - if (a >= 'A' && a <= 'F') - return a - 'A' + 10; - return a - 'a' + 10; -} - -static inline byte -make_byte (byte h, byte l) -{ - return (from_hex (h) << 4) | from_hex (l); -} - -static char * -PL_ParseData (pldata_t *pl, int *len) -{ - unsigned start = ++pl->pos; - int nibbles = 0, i; - char *str, c; - - while (pl->pos < pl->end) { - c = pl->ptr[pl->pos++]; - if (isxdigit ((byte) c)) { - nibbles++; - continue; - } - if (c == '>') { - if (nibbles & 1) { - pl->error = "invalid data, missing nibble"; - return NULL; - } - *len = nibbles / 2; - str = malloc (*len); - for (i = 0; i < *len; i++) - str[i] = make_byte (pl->ptr[start + i * 2], - pl->ptr[start + i * 2 + 1]); - return str; - } - pl->error = "invalid character in data"; - return NULL; - } - pl->error = "Reached end of string while parsing data"; - return NULL; -} - -static char * -PL_ParseQuotedString (pldata_t *pl) -{ - unsigned int start = ++pl->pos; - unsigned int escaped = 0; - unsigned int shrink = 0; - qboolean hex = false; - qboolean long_string = false; - char *str; - - if (pl->ptr[pl->pos] == '"' && - pl->ptr[pl->pos + 1] == '"') { - long_string = true; - start += 2; - pl->pos += 2; - } - - while (pl->pos < pl->end) { - - char c = pl->ptr[pl->pos]; - - if (escaped) { - if (escaped == 1 && inrange (c, '0', '7')) { - escaped++; - hex = false; - } else if (escaped > 1) { - if (escaped == 2 && c == 'x') { - hex = true; - shrink++; - escaped++; - } else if (hex && inrange (escaped, 3, 4) - && isxdigit ((byte) c)) { - shrink++; - escaped++; - } else if (inrange (escaped, 1, 3) && inrange (c, '0', '7')) { - shrink++; - escaped++; - } else { - pl->pos--; - escaped = 0; - } - } else { - escaped = 0; - } - } else { - if (c == '\\') { - escaped = 1; - shrink++; - } else if (c == '"' - && (!long_string || (pl->ptr[pl->pos + 1] == '"' - && pl->ptr[pl->pos + 2] == '"'))) { - break; - } - } - - if (c == '\n') { - pl->line++; - } - - pl->pos++; - } - - if (pl->pos >= pl->end) { - pl->error = "Reached end of string while parsing quoted string"; - return NULL; - } - - if (pl->pos - start - shrink == 0) { - str = strdup (""); - } else { - char *chars = alloca(pl->pos - start - shrink); - unsigned int j; - unsigned int k; - - escaped = 0; - hex = false; - - for (j = start, k = 0; j < pl->pos; j++) { - - char c = pl->ptr[j]; - - if (escaped) { - if (escaped == 1 && inrange (c, '0', '7')) { - chars[k] = c - '0'; - hex = false; - escaped++; - } else if (escaped > 1) { - if (escaped == 2 && c == 'x') { - hex = true; - escaped++; - } else if (hex && inrange (escaped, 3, 4) - && isxdigit ((byte) c)) { - chars[k] <<= 4; - chars[k] |= char2num (c); - escaped++; - } else if (inrange (escaped, 1, 3) - && inrange (c, '0', '7')) { - chars[k] <<= 3; - chars[k] |= (c - '0'); - escaped++; - } else { - escaped = 0; - j--; - k++; - } - } else { - escaped = 0; - switch (c) { - case 'a': - chars[k] = '\a'; - break; - case 'b': - chars[k] = '\b'; - break; - case 't': - chars[k] = '\t'; - break; - case 'r': - chars[k] = '\r'; - break; - case 'n': - chars[k] = '\n'; - break; - case 'v': - chars[k] = '\v'; - break; - case 'f': - chars[k] = '\f'; - break; - default: - chars[k] = c; - break; - } - k++; - } - } else { - chars[k] = c; - if (c == '\\') { - escaped = 1; - } else { - k++; - } - } - } - str = strncat (calloc ((pl->pos - start - shrink) + 1, 1), chars, - pl->pos - start - shrink); - } - if (long_string) - pl->pos += 2; - pl->pos++; - return str; -} - -static char * -PL_ParseUnquotedString (pldata_t *pl) -{ - unsigned int start = pl->pos; - char *str; - - while (pl->pos < pl->end) { - if (is_quotable (pl->ptr[pl->pos])) - break; - pl->pos++; - } - str = strncat (calloc ((pl->pos - start) + 1, 1), &pl->ptr[start], - pl->pos - start); - return str; -} - -static plitem_t * -PL_ParsePropertyListItem (pldata_t *pl) -{ - plitem_t *item = NULL; - - if (!PL_SkipSpace (pl)) - return NULL; - - switch (pl->ptr[pl->pos]) { - case '{': - { - item = PL_NewDictionary (); - - pl->pos++; - - while (PL_SkipSpace (pl) && pl->ptr[pl->pos] != '}') { - plitem_t *key; - plitem_t *value; - - if (!(key = PL_ParsePropertyListItem (pl))) { - PL_Free (item); - return NULL; - } - - if (!(PL_SkipSpace (pl))) { - PL_Free (key); - PL_Free (item); - return NULL; - } - - if (key->type != QFString) { - pl->error = "Key is not a string"; - PL_Free (key); - PL_Free (item); - return NULL; - } - - if (pl->ptr[pl->pos] != '=') { - pl->error = "Unexpected character (expected '=')"; - PL_Free (key); - PL_Free (item); - return NULL; - } - pl->pos++; - - // If there is no value, lose the key - if (!(value = PL_ParsePropertyListItem (pl))) { - PL_Free (key); - PL_Free (item); - return NULL; - } - - if (!(PL_SkipSpace (pl))) { - PL_Free (key); - PL_Free (value); - PL_Free (item); - return NULL; - } - - if (pl->ptr[pl->pos] == ';') { - pl->pos++; - } else if (pl->ptr[pl->pos] != '}') { - pl->error = "Unexpected character (wanted ';' or '}')"; - PL_Free (key); - PL_Free (value); - PL_Free (item); - return NULL; - } - - // Add the key/value pair to the dictionary - if (!PL_D_AddObject (item, PL_String (key), value)) { - PL_Free (key); - PL_Free (value); - PL_Free (item); - return NULL; - } - PL_Free (key); - } - - if (pl->pos >= pl->end) { // Catch the error - pl->error = "Unexpected end of string when parsing dictionary"; - PL_Free (item); - return NULL; - } - pl->pos++; - - return item; - } - - case '(': { - item = PL_NewArray (); - - pl->pos++; - - while (PL_SkipSpace (pl) && pl->ptr[pl->pos] != ')') { - plitem_t *value; - - if (!(value = PL_ParsePropertyListItem (pl))) { - PL_Free (item); - return NULL; - } - - if (!(PL_SkipSpace (pl))) { - PL_Free (value); - PL_Free (item); - return NULL; - } - - if (pl->ptr[pl->pos] == ',') { - pl->pos++; - } else if (pl->ptr[pl->pos] != ')') { - pl->error = "Unexpected character (wanted ',' or ')')"; - PL_Free (value); - PL_Free (item); - return NULL; - } - - if (!PL_A_AddObject (item, value)) { - pl->error = "Unexpected character (too many items in array)"; - PL_Free (value); - PL_Free (item); - return NULL; - } - } - pl->pos++; - - return item; - } - - case '<': { - int len; - char *str = PL_ParseData (pl, &len); - - if (!str) { - return NULL; - } else { - return PL_NewData (str, len); - } - } - - case '"': { - char *str = PL_ParseQuotedString (pl); - - if (!str) { - return NULL; - } else { - return new_string (str); - } - } - - default: { - char *str = PL_ParseUnquotedString (pl); - - if (!str) { - return NULL; - } else { - return new_string (str); - } - } - } // switch -} - -VISIBLE plitem_t * -PL_GetPropertyList (const char *string) -{ - pldata_t *pl = calloc (1, sizeof (pldata_t)); - plitem_t *newpl = NULL; - - if (!quotable_bitmap[0]) - init_quotables (); - - pl->ptr = string; - pl->pos = 0; - pl->end = strlen (string); - pl->error = NULL; - pl->line = 1; - - if ((newpl = PL_ParsePropertyListItem (pl))) { - free (pl); - return newpl; - } else { - if (pl && pl->error && pl->error[0]) - Sys_Printf ("plist: %d,%d: %s", pl->line, pl->pos, pl->error); - free (pl); - return NULL; - } -} - -static void -write_tabs (dstring_t *dstr, int num) -{ - char *tabs = dstring_reservestr (dstr, num); - - memset (tabs, '\t', num); - tabs[num] = 0; -} - -static void -write_string_len (dstring_t *dstr, const char *str, int len) -{ - char *dst = dstring_reservestr (dstr, len); - memcpy (dst, str, len); - dst[len] = 0; -} - -static char -to_hex (byte b) -{ - char c = (b & 0xf) + '0'; - if (c > '9') - c = c - '0' + 'A'; - return c; -} - -static void -write_binary (dstring_t *dstr, byte *binary, int len) -{ - int i; - char *dst = dstring_reservestr (dstr, len * 2); - for (i = 0; i < len; i++) { - *dst++ = to_hex (binary[i] >> 4); - *dst++ = to_hex (binary[i]); - } -} - -static void -write_string (dstring_t *dstr, const char *str) -{ - const char *s; - int len = 0; - char *dst; - int quoted = 0; - - for (s = str; *s; s++) { - if (is_quotable (*s)) - quoted = 1; - len++; - } - if (!quoted) { - dst = dstring_reservestr (dstr, len); - strcpy (dst, str); - return; - } - // assume worst case of all octal chars plus two quotes. - dst = dstring_reservestr (dstr, len * 4 + 2); - *dst++= '\"'; - while (*str) { - if (*str && isascii ((byte) *str) && isprint ((byte) *str) - && *str != '\\' && *str != '\"') { - *dst++ = *str++; - continue; - } - if (*str) { - *dst++ = '\\'; - switch (*str) { - case '\"': - case '\\': - *dst++ = *str; - break; - case '\n': - *dst++ = 'n'; - break; - case '\a': - *dst++ = 'a'; - break; - case '\b': - *dst++ = 'b'; - break; - case '\f': - *dst++ = 'f'; - break; - case '\r': - *dst++ = 'r'; - break; - case '\t': - *dst++ = 't'; - break; - case '\v': - *dst++ = 'v'; - break; - default: - *dst++ = '0' + ((((byte) *str) >> 6) & 3); - *dst++ = '0' + ((((byte) *str) >> 3) & 7); - *dst++ = '0' + (((byte) *str) & 7); - break; - } - str++; - } - } - *dst++ = '\"'; - *dst++ = 0; - dstr->size = dst - dstr->str; -} - -static void -write_item (dstring_t *dstr, plitem_t *item, int level) -{ - void **list, **l; - dictkey_t *current; - plarray_t *array; - plbinary_t *binary; - int i; - - switch (item->type) { - case QFDictionary: - write_string_len (dstr, "{\n", 2); - l = list = Hash_GetList ((hashtab_t *) item->data); - while ((current = (dictkey_t *) *l++)) { - write_tabs (dstr, level + 1); - write_string (dstr, current->key); - write_string_len (dstr, " = ", 3); - write_item (dstr, current->value, level + 1); - write_string_len (dstr, ";\n", 2); - } - free (list); - write_tabs (dstr, level); - write_string_len (dstr, "}", 1); - break; - case QFArray: - write_string_len (dstr, "(\n", 2); - array = (plarray_t *) item->data; - for (i = 0; i < array->numvals; i++) { - write_tabs (dstr, level + 1); - write_item (dstr, array->values[i], level + 1); - if (i < array->numvals - 1) - write_string_len (dstr, ",\n", 2); - } - write_string_len (dstr, "\n", 1); - write_tabs (dstr, level); - write_string_len (dstr, ")", 1); - break; - case QFBinary: - write_string_len (dstr, "<", 1); - binary = (plbinary_t *) item->data; - write_binary (dstr, binary->data, binary->size); - write_string_len (dstr, ">", 1); - break; - case QFString: - write_string (dstr, item->data); - break; - default: - break; - } -} - -VISIBLE char * -PL_WritePropertyList (plitem_t *pl) -{ - dstring_t *dstr = dstring_newstr (); - - if (!quotable_bitmap[0]) - init_quotables (); - write_item (dstr, pl, 0); - write_string_len (dstr, "\n", 1); - return dstring_freeze (dstr); -} - -VISIBLE pltype_t -PL_Type (plitem_t *item) -{ - return item->type; -} diff --git a/libs/util/qsort_r.c b/libs/util/qsort_r.c new file mode 100644 index 000000000..6fbb0297f --- /dev/null +++ b/libs/util/qsort_r.c @@ -0,0 +1,250 @@ +/* Copyright (C) 1991-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Douglas C. Schmidt (schmidt@ics.uci.edu). + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* If you consider tuning this algorithm, you should consult first: + Engineering a sort function; Jon Bentley and M. Douglas McIlroy; + Software - Practice and Experience; Vol. 23 (11), 1249-1265, 1993. */ + +#include +#include + +#include "qfalloca.h" +#include "quicksort.h" + +/* Byte-wise swap two items of size SIZE. */ +#define SWAP(a, b, size) \ + do \ + { \ + size_t __size = (size); \ + char *__a = (a), *__b = (b); \ + do \ + { \ + char __tmp = *__a; \ + *__a++ = *__b; \ + *__b++ = __tmp; \ + } while (--__size > 0); \ + } while (0) + +/* Discontinue quicksort algorithm when partition gets below this size. + This particular magic number was chosen to work best on a Sun 4/260. */ +#define MAX_THRESH 4 + +/* Stack node declarations used to store unfulfilled partition obligations. */ +typedef struct + { + char *lo; + char *hi; + } stack_node; + +/* The next 4 #defines implement a very fast in-line stack abstraction. */ +/* The stack needs log (total_elements) entries (we could even subtract + log(MAX_THRESH)). Since total_elements has type size_t, we get as + upper bound for log (total_elements): + bits per byte (CHAR_BIT) * sizeof(size_t). */ +#define STACK_SIZE (CHAR_BIT * sizeof(size_t)) +#define PUSH(low, high) ((void) ((top->lo = (low)), (top->hi = (high)), ++top)) +#define POP(low, high) ((void) (--top, (low = top->lo), (high = top->hi))) +#define STACK_NOT_EMPTY (stack < top) + + +/* Order size using quicksort. This implementation incorporates + four optimizations discussed in Sedgewick: + + 1. Non-recursive, using an explicit stack of pointer that store the + next array partition to sort. To save time, this maximum amount + of space required to store an array of SIZE_MAX is allocated on the + stack. Assuming a 32-bit (64 bit) integer for size_t, this needs + only 32 * sizeof(stack_node) == 256 bytes (for 64 bit: 1024 bytes). + Pretty cheap, actually. + + 2. Chose the pivot element using a median-of-three decision tree. + This reduces the probability of selecting a bad pivot value and + eliminates certain extraneous comparisons. + + 3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving + insertion sort to order the MAX_THRESH items within each partition. + This is a big win, since insertion sort is faster for small, mostly + sorted array segments. + + 4. The larger of the two sub-partitions is always pushed onto the + stack first, with the algorithm then concentrating on the + smaller partition. This *guarantees* no more than log (total_elems) + stack size is needed (actually O(1) in this case)! */ + +void +_quicksort (void *const pbase, size_t total_elems, size_t size, + __compar_d_fn_t cmp, void *arg) +{ + char *base_ptr = (char *) pbase; + + const size_t max_thresh = MAX_THRESH * size; + + if (total_elems == 0) + /* Avoid lossage with unsigned arithmetic below. */ + return; + + if (total_elems > MAX_THRESH) + { + char *lo = base_ptr; + char *hi = &lo[size * (total_elems - 1)]; + stack_node stack[STACK_SIZE]; + stack_node *top = stack; + + PUSH (NULL, NULL); + + while (STACK_NOT_EMPTY) + { + char *left_ptr; + char *right_ptr; + + /* Select median value from among LO, MID, and HI. Rearrange + LO and HI so the three values are sorted. This lowers the + probability of picking a pathological pivot value and + skips a comparison for both the LEFT_PTR and RIGHT_PTR in + the while loops. */ + + char *mid = lo + size * ((hi - lo) / size >> 1); + + if ((*cmp) ((void *) mid, (void *) lo, arg) < 0) + SWAP (mid, lo, size); + if ((*cmp) ((void *) hi, (void *) mid, arg) < 0) + SWAP (mid, hi, size); + else + goto jump_over; + if ((*cmp) ((void *) mid, (void *) lo, arg) < 0) + SWAP (mid, lo, size); + jump_over:; + + left_ptr = lo + size; + right_ptr = hi - size; + + /* Here's the famous ``collapse the walls'' section of quicksort. + Gotta like those tight inner loops! They are the main reason + that this algorithm runs much faster than others. */ + do + { + while ((*cmp) ((void *) left_ptr, (void *) mid, arg) < 0) + left_ptr += size; + + while ((*cmp) ((void *) mid, (void *) right_ptr, arg) < 0) + right_ptr -= size; + + if (left_ptr < right_ptr) + { + SWAP (left_ptr, right_ptr, size); + if (mid == left_ptr) + mid = right_ptr; + else if (mid == right_ptr) + mid = left_ptr; + left_ptr += size; + right_ptr -= size; + } + else if (left_ptr == right_ptr) + { + left_ptr += size; + right_ptr -= size; + break; + } + } + while (left_ptr <= right_ptr); + + /* Set up pointers for next iteration. First determine whether + left and right partitions are below the threshold size. If so, + ignore one or both. Otherwise, push the larger partition's + bounds on the stack and continue sorting the smaller one. */ + + if ((size_t) (right_ptr - lo) <= max_thresh) + { + if ((size_t) (hi - left_ptr) <= max_thresh) + /* Ignore both small partitions. */ + POP (lo, hi); + else + /* Ignore small left partition. */ + lo = left_ptr; + } + else if ((size_t) (hi - left_ptr) <= max_thresh) + /* Ignore small right partition. */ + hi = right_ptr; + else if ((right_ptr - lo) > (hi - left_ptr)) + { + /* Push larger left partition indices. */ + PUSH (lo, right_ptr); + lo = left_ptr; + } + else + { + /* Push larger right partition indices. */ + PUSH (left_ptr, hi); + hi = right_ptr; + } + } + } + + /* Once the BASE_PTR array is partially sorted by quicksort the rest + is completely sorted using insertion sort, since this is efficient + for partitions below MAX_THRESH size. BASE_PTR points to the beginning + of the array to sort, and END_PTR points at the very last element in + the array (*not* one beyond it!). */ + +#define min(x, y) ((x) < (y) ? (x) : (y)) + + { + char *const end_ptr = &base_ptr[size * (total_elems - 1)]; + char *tmp_ptr = base_ptr; + char *thresh = min(end_ptr, base_ptr + max_thresh); + char *run_ptr; + + /* Find smallest element in first threshold and place it at the + array's beginning. This is the smallest array element, + and the operation speeds up insertion sort's inner loop. */ + + for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size) + if ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0) + tmp_ptr = run_ptr; + + if (tmp_ptr != base_ptr) + SWAP (tmp_ptr, base_ptr, size); + + /* Insertion sort, running from left-hand-side up to right-hand-side. */ + + run_ptr = base_ptr + size; + while ((run_ptr += size) <= end_ptr) + { + tmp_ptr = run_ptr - size; + while ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0) + tmp_ptr -= size; + + tmp_ptr += size; + if (tmp_ptr != run_ptr) + { + char *trav; + + trav = run_ptr + size; + while (--trav >= run_ptr) + { + char c = *trav; + char *hi, *lo; + + for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo) + *hi = *lo; + *hi = c; + } + } + } + } +} diff --git a/libs/util/quakefs.c b/libs/util/quakefs.c index 5131f7194..ae736d433 100644 --- a/libs/util/quakefs.c +++ b/libs/util/quakefs.c @@ -82,9 +82,9 @@ #include "QF/mathlib.h" #include "QF/pak.h" #include "QF/pakfile.h" +#include "QF/plist.h" #include "QF/qargs.h" #include "QF/qendian.h" -#include "QF/qfplist.h" #include "QF/qtypes.h" #include "QF/quakefs.h" #include "QF/sys.h" @@ -122,9 +122,34 @@ int fnmatch (const char *__pattern, const char *__string, int __flags); // QUAKE FILESYSTEM -static cvar_t *fs_userpath; -static cvar_t *fs_sharepath; -static cvar_t *fs_dirconf; +static memhunk_t *qfs_hunk; +static char *fs_userpath; +static cvar_t fs_userpath_cvar = { + .name = "fs_userpath", + .description = + "location of your game directories", + .default_value = FS_USERPATH, + .flags = CVAR_ROM, + .value = { .type = 0, .value = &fs_userpath }, +}; +static char *fs_sharepath; +static cvar_t fs_sharepath_cvar = { + .name = "fs_sharepath", + .description = + "location of shared (read-only) game directories", + .default_value = FS_SHAREPATH, + .flags = CVAR_ROM, + .value = { .type = 0, .value = &fs_sharepath }, +}; +static char *fs_dirconf; +static cvar_t fs_dirconf_cvar = { + .name = "fs_dirconf", + .description = + "full path to gamedir.conf FIXME", + .default_value = "", + .flags = CVAR_ROM, + .value = { .type = 0, .value = &fs_dirconf }, +}; VISIBLE const char *qfs_userpath; @@ -161,8 +186,8 @@ typedef struct int_findfile_s { int fname_index; } int_findfile_t; -static searchpath_t *searchpaths_freelist; -static vpath_t *vpaths_freelist; +ALLOC_STATE (searchpath_t, searchpaths); +ALLOC_STATE (vpath_t, vpaths); static vpath_t *qfs_vpaths; //QFS @@ -234,9 +259,13 @@ static const char *qfs_default_dirconf = " };" "}"; +typedef struct { + gamedir_callback_t *callback; + void *data; +} gdcallback_t; #define GAMEDIR_CALLBACK_CHUNK 16 -static gamedir_callback_t **gamedir_callbacks; +static gdcallback_t *gamedir_callbacks; static int num_gamedir_callbacks; static int max_gamedir_callbacks; @@ -252,12 +281,11 @@ static void delete_searchpath (searchpath_t *searchpath) { if (searchpath->pack) { - Qclose (searchpath->pack->handle); - free (searchpath->pack->files); - free (searchpath->pack); + pack_del (searchpath->pack); } - if (searchpath->filename) + if (searchpath->filename) { free (searchpath->filename); + } FREE (searchpaths, searchpath); } @@ -274,8 +302,9 @@ delete_vpath (vpath_t *vpath) { searchpath_t *next; - if (vpath->name) + if (vpath->name) { free (vpath->name); + } while (vpath->user) { next = vpath->user->next; delete_searchpath (vpath->user); @@ -307,7 +336,7 @@ qfs_var_free (void *_v, void *unused) static hashtab_t * qfs_new_vars (void) { - return Hash_NewTable (61, qfs_var_get_key, qfs_var_free, 0); + return Hash_NewTable (61, qfs_var_get_key, qfs_var_free, 0, 0); } static void @@ -359,7 +388,7 @@ qfs_var_subst (const char *string, hashtab_t *vars) dstring_appendsubstr (new, s, (e - s)); break; } - var = va ("%.*s", (int) (e - s) - 1, s + 1); + var = va (0, "%.*s", (int) (e - s) - 1, s + 1); sub = Hash_Find (vars, var); if (sub) dstring_appendstr (new, sub->val); @@ -370,7 +399,7 @@ qfs_var_subst (const char *string, hashtab_t *vars) s = e; while (qfs_isident (*e)) e++; - var = va ("%.*s", (int) (e - s), s); + var = va (0, "%.*s", (int) (e - s), s); sub = Hash_Find (vars, var); if (sub) dstring_appendstr (new, sub->val); @@ -514,7 +543,7 @@ qfs_find_gamedir (const char *name, hashtab_t *dirs) } } free (list); - PL_Free (keys); + PL_Release (keys); } return gdpl; } @@ -543,18 +572,8 @@ qfs_process_path (const char *path, const char *gamedir) } static void -qfs_build_gamedir (const char **list) +qfs_free_gamedir (void) { - int j; - gamedir_t *gamedir; - plitem_t *gdpl; - dstring_t *path; - hashtab_t *dirs = Hash_NewTable (31, qfs_dir_get_key, qfs_dir_free, 0); - hashtab_t *vars = qfs_new_vars (); - const char *dir = 0; - - qfs_set_var (vars, "game", qfs_game); - if (qfs_gamedir) { if (qfs_gamedir->name) free ((char *)qfs_gamedir->name); @@ -564,6 +583,8 @@ qfs_build_gamedir (const char **list) free ((char *)qfs_gamedir->path); if (qfs_gamedir->gamecode) free ((char *)qfs_gamedir->gamecode); + if (qfs_gamedir->hudtype) + free ((char *)qfs_gamedir->hudtype); if (qfs_gamedir->dir.def) free ((char *)qfs_gamedir->dir.def); if (qfs_gamedir->dir.skins) @@ -574,6 +595,8 @@ qfs_build_gamedir (const char **list) free ((char *)qfs_gamedir->dir.sound); if (qfs_gamedir->dir.maps) free ((char *)qfs_gamedir->dir.maps); + if (qfs_gamedir->dir.shots) + free ((char *)qfs_gamedir->dir.shots); free (qfs_gamedir); } @@ -582,13 +605,29 @@ qfs_build_gamedir (const char **list) delete_vpath (qfs_vpaths); qfs_vpaths = next; } +} + +static void +qfs_build_gamedir (const char **list) +{ + int j; + gamedir_t *gamedir; + plitem_t *gdpl; + dstring_t *path; + hashtab_t *dirs = Hash_NewTable (31, qfs_dir_get_key, qfs_dir_free, 0, 0); + hashtab_t *vars = qfs_new_vars (); + const char *dir = 0; + + qfs_set_var (vars, "game", qfs_game); + + qfs_free_gamedir (); for (j = 0; list[j]; j++) ; gamedir = calloc (1, sizeof (gamedir_t)); path = dstring_newstr (); while (j--) { - const char *name = va ("%s:%s", qfs_game, dir = list[j]); + const char *name = va (0, "%s:%s", qfs_game, dir = list[j]); if (Hash_Find (dirs, name)) continue; gdpl = qfs_find_gamedir (name, dirs); @@ -625,16 +664,16 @@ qfs_build_gamedir (const char **list) gamedir->dir.shots = strdup ("QF"); qfs_gamedir = gamedir; - Sys_MaskPrintf (SYS_FS, "%s\n", qfs_gamedir->name); - Sys_MaskPrintf (SYS_FS, " gamedir : %s\n", qfs_gamedir->gamedir); - Sys_MaskPrintf (SYS_FS, " path : %s\n", qfs_gamedir->path); - Sys_MaskPrintf (SYS_FS, " gamecode: %s\n", qfs_gamedir->gamecode); - Sys_MaskPrintf (SYS_FS, " hudtype : %s\n", qfs_gamedir->hudtype); - Sys_MaskPrintf (SYS_FS, " def : %s\n", qfs_gamedir->dir.def); - Sys_MaskPrintf (SYS_FS, " skins : %s\n", qfs_gamedir->dir.skins); - Sys_MaskPrintf (SYS_FS, " models : %s\n", qfs_gamedir->dir.models); - Sys_MaskPrintf (SYS_FS, " sound : %s\n", qfs_gamedir->dir.sound); - Sys_MaskPrintf (SYS_FS, " maps : %s\n", qfs_gamedir->dir.maps); + Sys_MaskPrintf (SYS_fs, "%s\n", qfs_gamedir->name); + Sys_MaskPrintf (SYS_fs, " gamedir : %s\n", qfs_gamedir->gamedir); + Sys_MaskPrintf (SYS_fs, " path : %s\n", qfs_gamedir->path); + Sys_MaskPrintf (SYS_fs, " gamecode: %s\n", qfs_gamedir->gamecode); + Sys_MaskPrintf (SYS_fs, " hudtype : %s\n", qfs_gamedir->hudtype); + Sys_MaskPrintf (SYS_fs, " def : %s\n", qfs_gamedir->dir.def); + Sys_MaskPrintf (SYS_fs, " skins : %s\n", qfs_gamedir->dir.skins); + Sys_MaskPrintf (SYS_fs, " models : %s\n", qfs_gamedir->dir.models); + Sys_MaskPrintf (SYS_fs, " sound : %s\n", qfs_gamedir->dir.sound); + Sys_MaskPrintf (SYS_fs, " maps : %s\n", qfs_gamedir->dir.maps); qfs_process_path (qfs_gamedir->path, dir); free (path); Hash_DelTable (dirs); @@ -649,10 +688,10 @@ qfs_load_config (void) char *buf; char *dirconf; - if (*fs_dirconf->string) { - dirconf = Sys_ExpandSquiggle (fs_dirconf->string); + if (*fs_dirconf) { + dirconf = Sys_ExpandSquiggle (fs_dirconf); if (!(f = Qopen (dirconf, "rt"))) - Sys_MaskPrintf (SYS_FS, + Sys_MaskPrintf (SYS_fs, "Could not load `%s', using builtin defaults\n", dirconf); free (dirconf); @@ -661,26 +700,22 @@ qfs_load_config (void) goto no_config; len = Qfilesize (f); - buf = malloc (len + 3); // +3 for { } and \0 + buf = malloc (len + 1); // +1 for nul - Qread (f, buf + 1, len); + Qread (f, buf, len); Qclose (f); - // convert the config file to a plist dictionary - buf[0] = '{'; - buf[len + 1] = '}'; - buf[len + 2] = 0; if (qfs_gd_plist) - PL_Free (qfs_gd_plist); - qfs_gd_plist = PL_GetPropertyList (buf); + PL_Release (qfs_gd_plist); + qfs_gd_plist = PL_GetDictionary (buf, 0); free (buf); if (qfs_gd_plist && PL_Type (qfs_gd_plist) == QFDictionary) return; // done Sys_Printf ("not a dictionary\n"); no_config: if (qfs_gd_plist) - PL_Free (qfs_gd_plist); - qfs_gd_plist = PL_GetPropertyList (qfs_default_dirconf); + PL_Release (qfs_gd_plist); + qfs_gd_plist = PL_GetPropertyList (qfs_default_dirconf, 0); } /* @@ -800,13 +835,10 @@ QFS_WriteFile (const char *filename, const void *data, int len) Qclose (f); } -static int_findfile_t * -qfs_findfile_search (const vpath_t *vpath, const searchpath_t *sp, - const char **fnames) +static int_findfile_t found; +static void +clear_findfile (void) { - static int_findfile_t found; - const char **fn; - found.ff.vpath = 0; found.ff.in_pak = false; found.pack = 0; @@ -820,6 +852,15 @@ qfs_findfile_search (const vpath_t *vpath, const searchpath_t *sp, free ((char *) found.path); found.path = 0; } +} + +static int_findfile_t * +qfs_findfile_search (const vpath_t *vpath, const searchpath_t *sp, + const char **fnames) +{ + const char **fn; + + clear_findfile (); // is the element a pak file? if (sp->pack) { dpackfile_t *packfile = 0; @@ -831,7 +872,7 @@ qfs_findfile_search (const vpath_t *vpath, const searchpath_t *sp, } } if (packfile) { - Sys_MaskPrintf (SYS_FS_F, "PackFile: %s : %s\n", + Sys_MaskPrintf (SYS_fs_f, "PackFile: %s : %s\n", sp->pack->filename, packfile->name); found.ff.vpath = vpath; found.ff.in_pak = true; @@ -852,13 +893,14 @@ qfs_findfile_search (const vpath_t *vpath, const searchpath_t *sp, continue; } - Sys_MaskPrintf (SYS_FS_F, "FindFile: %s\n", path->str); + Sys_MaskPrintf (SYS_fs_f, "FindFile: %s\n", path->str); found.ff.vpath = vpath; found.ff.in_pak = false; found.ff.realname = strdup (*fn); found.path = strdup (path->str); found.fname_index = fn - fnames; + dstring_delete (path); return &found; } } @@ -1045,7 +1087,7 @@ _QFS_VOpenFile (const char *filename, int zip, // make sure they're not trying to do weird stuff with our private files path = QFS_CompressPath (filename); if (qfs_contains_updir(path, 1)) { - Sys_MaskPrintf (SYS_FS, + Sys_MaskPrintf (SYS_fs, "FindFile: %s: attempt to escape directory tree!\n", path); goto error; @@ -1054,11 +1096,10 @@ _QFS_VOpenFile (const char *filename, int zip, ind = 0; #ifdef HAVE_VORBIS if (strequal (".wav", QFS_FileExtension (path))) { - char *oggfilename; - oggfilename = alloca (strlen (path) + 1); + size_t len = strlen (path); + char *oggfilename = alloca (len + 1); QFS_StripExtension (path, oggfilename); - strncat (oggfilename, ".ogg", - sizeof (oggfilename) - strlen (oggfilename) - 1); + strcat (oggfilename, ".ogg"); fnames[ind] = oggfilename; zip_flags[ind] = 0; ind++; @@ -1089,7 +1130,7 @@ _QFS_VOpenFile (const char *filename, int zip, return gzfile; } - Sys_MaskPrintf (SYS_FS_NF, "FindFile: can't find %s\n", filename); + Sys_MaskPrintf (SYS_fs_nf, "FindFile: can't find %s\n", filename); error: qfs_filesize = -1; free (path); @@ -1141,11 +1182,11 @@ QFS_LoadFile (QFile *file, int usehunk) //base = QFS_FileBase (path); if (usehunk == 1) - buf = Hunk_AllocName (len + 1, base); + buf = Hunk_RawAllocName (qfs_hunk, len + 1, base); else if (usehunk == 2) - buf = Hunk_TempAlloc (len + 1); + buf = Hunk_TempAlloc (qfs_hunk, len + 1); else if (usehunk == 0) - buf = calloc (1, len + 1); + buf = malloc (len + 1); else if (usehunk == 3) buf = Cache_Alloc (loadcache, len + 1, base); else @@ -1155,8 +1196,8 @@ QFS_LoadFile (QFile *file, int usehunk) Sys_Error ("QFS_LoadFile: not enough space"); //Sys_Error ("QFS_LoadFile: not enough space for %s", path); + len = Qread (file, buf, len); buf[len] = 0; - Qread (file, buf, len); Qclose (file); free (base); @@ -1191,7 +1232,7 @@ qfs_load_pakfile (char *packfile) pack_t *pack = pack_open (packfile); if (pack) - Sys_MaskPrintf (SYS_FS, "Added packfile %s (%i files)\n", + Sys_MaskPrintf (SYS_fs, "Added packfile %s (%i files)\n", packfile, pack->numfiles); return pack; } @@ -1209,7 +1250,7 @@ qfs_file_sort (char **os1, char **os2) s2 = *os2; while (1) { - in1 = in2 = n1 = n2 = 0; + n1 = n2 = 0; if ((in1 = isdigit ((byte) *s1))) n1 = strtol (s1, &s1, 10); @@ -1241,7 +1282,7 @@ qfs_load_gamedir (searchpath_t **searchpath, const char *dir) char **pakfiles = NULL; int i = 0, bufsize = 0, count = 0; - Sys_MaskPrintf (SYS_FS, "qfs_load_gamedir (\"%s\")\n", dir); + Sys_MaskPrintf (SYS_fs, "qfs_load_gamedir (\"%s\")\n", dir); pakfiles = calloc (1, FBLOCK_SIZE * sizeof (char *)); @@ -1332,22 +1373,22 @@ qfs_add_gamedir (vpath_t *vpath, const char *dir) if (!*dir) return; - e = fs_sharepath->string + strlen (fs_sharepath->string); + e = fs_sharepath + strlen (fs_sharepath); s = e; s_dir = dstring_new (); f_dir = dstring_new (); - while (s >= fs_sharepath->string) { - while (s != fs_sharepath->string && s[-1] !=':') + while (s >= fs_sharepath) { + while (s != fs_sharepath && s[-1] !=':') s--; if (s != e) { dsprintf (s_dir, "%.*s", (int) (e - s), s); - if (strcmp (s_dir->str, fs_userpath->string) != 0) { + if (strcmp (s_dir->str, fs_userpath) != 0) { if (qfs_expand_path (f_dir, s_dir->str, dir, 0) != 0) { Sys_Printf ("dropping bad directory %s\n", dir); break; } - Sys_MaskPrintf (SYS_FS, "qfs_add_gamedir (\"%s\")\n", + Sys_MaskPrintf (SYS_fs, "qfs_add_gamedir (\"%s\")\n", f_dir->str); qfs_add_dir (&vpath->share, f_dir->str); @@ -1357,7 +1398,7 @@ qfs_add_gamedir (vpath_t *vpath, const char *dir) } qfs_expand_userpath (f_dir, dir); - Sys_MaskPrintf (SYS_FS, "qfs_add_gamedir (\"%s\")\n", f_dir->str); + Sys_MaskPrintf (SYS_fs, "qfs_add_gamedir (\"%s\")\n", f_dir->str); qfs_add_dir (&vpath->user, f_dir->str); dstring_delete (f_dir); @@ -1379,20 +1420,20 @@ QFS_Gamedir (const char *gamedir) // Make sure everyone else knows we've changed gamedirs for (i = 0; i < num_gamedir_callbacks; i++) { - gamedir_callbacks[i] (0); + gamedir_callbacks[i].callback (0, gamedir_callbacks[i].data); } Cache_Flush (); for (i = 0; i < num_gamedir_callbacks; i++) { - gamedir_callbacks[i] (1); + gamedir_callbacks[i].callback (1, gamedir_callbacks[i].data); } } VISIBLE void -QFS_GamedirCallback (gamedir_callback_t *func) +QFS_GamedirCallback (gamedir_callback_t *func, void *data) { if (num_gamedir_callbacks == max_gamedir_callbacks) { size_t size = (max_gamedir_callbacks + GAMEDIR_CALLBACK_CHUNK) - * sizeof (gamedir_callback_t *); + * sizeof (gdcallback_t); gamedir_callbacks = realloc (gamedir_callbacks, size); if (!gamedir_callbacks) Sys_Error ("Too many gamedir callbacks!\n"); @@ -1403,37 +1444,53 @@ QFS_GamedirCallback (gamedir_callback_t *func) Sys_Error ("null gamedir callback\n"); } - gamedir_callbacks[num_gamedir_callbacks] = func; + gamedir_callbacks[num_gamedir_callbacks].callback = func; + gamedir_callbacks[num_gamedir_callbacks].data = data; num_gamedir_callbacks++; } static void -qfs_path_cvar (cvar_t *var) +qfs_path_cvar (void *data, const cvar_t *cvar) { - char *cpath = QFS_CompressPath (var->string); - if (strcmp (cpath, var->string)) - Cvar_Set (var, cpath); - free (cpath); + char *cpath = QFS_CompressPath (*(char **)data); + if (!*cpath) { + free (cpath); + cpath = strdup ("."); + } + if (strcmp (cpath, *(char **)data)) { + free (*(char **)cvar->value.value); + *(char **)cvar->value.value = cpath; + } else { + free (cpath); + } +} + +static void +qfs_shutdown (void *data) +{ + clear_findfile (); + qfs_free_gamedir (); + PL_Release (qfs_gd_plist); + free ((char *) qfs_userpath); + free (gamedir_callbacks); + ALLOC_FREE_BLOCKS (vpaths); + ALLOC_FREE_BLOCKS (searchpaths); } VISIBLE void -QFS_Init (const char *game) +QFS_Init (memhunk_t *hunk, const char *game) { int i; - fs_sharepath = Cvar_Get ("fs_sharepath", FS_SHAREPATH, CVAR_ROM, - qfs_path_cvar, - "location of shared (read-only) game " - "directories"); - fs_userpath = Cvar_Get ("fs_userpath", FS_USERPATH, CVAR_ROM, - qfs_path_cvar, - "location of your game directories"); - fs_dirconf = Cvar_Get ("fs_dirconf", "", CVAR_ROM, NULL, - "full path to gamedir.conf FIXME"); + qfs_hunk = hunk; + + Cvar_Register (&fs_sharepath_cvar, qfs_path_cvar, &fs_sharepath); + Cvar_Register (&fs_userpath_cvar, qfs_path_cvar, &fs_userpath); + Cvar_Register (&fs_dirconf_cvar, 0, 0); Cmd_AddCommand ("path", qfs_path_f, "Show what paths Quake is using"); - qfs_userpath = Sys_ExpandSquiggle (fs_userpath->string); + qfs_userpath = Sys_ExpandSquiggle (fs_userpath); qfs_load_config (); @@ -1465,6 +1522,7 @@ QFS_Init (const char *game) } else { QFS_Gamedir (""); } + Sys_RegisterShutdown (qfs_shutdown, 0); } VISIBLE const char * @@ -1540,33 +1598,27 @@ QFS_SetExtension (struct dstring_s *path, const char *extension) dstring_appendstr (path, extension); } -VISIBLE int -QFS_NextFilename (dstring_t *filename, const char *prefix, const char *ext) +VISIBLE QFile * +QFS_NextFile (dstring_t *filename, const char *prefix, const char *ext) { - char *digits; - int i; - int ret = 0; + QFile *file = 0; dstring_t *full_path = dstring_new (); - dsprintf (filename, "%s0000%s", prefix, ext); - digits = filename->str + strlen (prefix); - - for (i = 0; i <= 9999; i++) { - digits[0] = i / 1000 + '0'; - digits[1] = i / 100 % 10 + '0'; - digits[2] = i / 10 % 10 + '0'; - digits[3] = i % 10 + '0'; - - if (qfs_expand_userpath (full_path, filename->str) == -1) - break; - if (Sys_FileExists (full_path->str) == -1) { - // file doesn't exist, so we can use this name - ret = 1; - break; + if (qfs_expand_userpath (full_path, "") == -1) { + dsprintf (filename, "failed to expand userpath"); + } else { + size_t qfs_pos = strlen (full_path->str); + dstring_appendstr (full_path, prefix); + int fd = Sys_UniqueFile (filename, full_path->str, ext, 4); + if (fd >= 0) { + dstring_snip (filename, 0, qfs_pos); + // Sys_UniqueFile opens with O_RDWR, and ensure binary files work + // on Windows. gzip writing is NOT specified + file = Qdopen (fd, "w+b"); } } dstring_delete (full_path); - return ret; + return file; } VISIBLE QFile * @@ -1578,7 +1630,7 @@ QFS_Open (const char *path, const char *mode) int write = 0; if (qfs_expand_userpath (full_path, path) == 0) { - Sys_MaskPrintf (SYS_FS, "QFS_Open: %s %s\n", full_path->str, mode); + Sys_MaskPrintf (SYS_fs, "QFS_Open: %s %s\n", full_path->str, mode); for (m = mode; *m; m++) if (*m == 'w' || *m == '+' || *m == 'a') write = 1; @@ -1614,7 +1666,7 @@ QFS_Rename (const char *old_path, const char *new_path) if ((ret = qfs_expand_userpath (full_old, old_path)) != -1) if ((ret = qfs_expand_userpath (full_new, new_path)) != -1) if ((ret = Sys_CreatePath (full_new->str)) != -1) { - Sys_MaskPrintf (SYS_FS, "QFS_Rename %s %s\n", full_old->str, + Sys_MaskPrintf (SYS_fs, "QFS_Rename %s %s\n", full_old->str, full_new->str); ret = Qrename (full_old->str, full_new->str); } @@ -1661,7 +1713,7 @@ QFS_FilelistAdd (filelist_t *filelist, const char *fname, const char *ext) } str = strdup (fname); - if (ext && (s = strstr(str, va(".%s", ext)))) + if (ext && (s = strstr(str, va (0, ".%s", ext)))) *s = 0; filelist->list[filelist->count++] = str; } @@ -1680,9 +1732,9 @@ qfs_filelistfill_do (filelist_t *list, const searchpath_t *search, const char *c for (i = 0; i < pak->numfiles; i++) { char *name = pak->files[i].name; - if (!fnmatch (va("%s%s*.%s", cp, separator, ext), name, + if (!fnmatch (va (0, "%s%s*.%s", cp, separator, ext), name, FNM_PATHNAME) - || !fnmatch (va("%s%s*.%s.gz", cp, separator, ext), name, + || !fnmatch (va (0, "%s%s*.%s.gz", cp, separator, ext), name, FNM_PATHNAME)) QFS_FilelistAdd (list, name, strip ? ext : 0); } @@ -1690,12 +1742,12 @@ qfs_filelistfill_do (filelist_t *list, const searchpath_t *search, const char *c DIR *dir_ptr; struct dirent *dirent; - dir_ptr = opendir (va ("%s/%s", search->filename, cp)); + dir_ptr = opendir (va (0, "%s/%s", search->filename, cp)); if (!dir_ptr) return; while ((dirent = readdir (dir_ptr))) - if (!fnmatch (va("*.%s", ext), dirent->d_name, 0) - || !fnmatch (va("*.%s.gz", ext), dirent->d_name, 0)) + if (!fnmatch (va (0, "*.%s", ext), dirent->d_name, 0) + || !fnmatch (va (0, "*.%s.gz", ext), dirent->d_name, 0)) QFS_FilelistAdd (list, dirent->d_name, strip ? ext : 0); closedir (dir_ptr); } @@ -1718,6 +1770,9 @@ QFS_FilelistFill (filelist_t *list, const char *path, const char *ext, for (search = vpath->user; search; search = search->next) { qfs_filelistfill_do (list, search, cp, ext, strip); } + for (search = vpath->share; search; search = search->next) { + qfs_filelistfill_do (list, search, cp, ext, strip); + } } free (cpath); } diff --git a/libs/util/quakeio.c b/libs/util/quakeio.c index f8e62df46..3de4d15b6 100644 --- a/libs/util/quakeio.c +++ b/libs/util/quakeio.c @@ -412,7 +412,7 @@ VISIBLE char * Qgets (QFile *file, char *buf, int count) { char *ret = buf; - char c; + int c; while (buf - ret < count - 1) { c = Qgetc (file); diff --git a/libs/util/riff.c b/libs/util/riff.c index ce3fb2e22..c4590ce2d 100644 --- a/libs/util/riff.c +++ b/libs/util/riff.c @@ -138,7 +138,6 @@ read_adtl (dstring_t *list_buf, QFile *f, int len) //FIXME list = (riff_list_t *) list_buf->str; while (len) { if (!Rread (f, &ck, sizeof (ck))) { - len = 0; break; } len -= sizeof (ck); @@ -161,7 +160,7 @@ read_adtl (dstring_t *list_buf, QFile *f, int len) chunk = &label->ck; break; default: - data = malloc (sizeof (data)); + data = malloc (sizeof (riff_data_t)); data->ck = ck; data->data = read_data (f, ck.len); chunk = &data->ck; @@ -229,6 +228,7 @@ read_list (riff_d_chunk_t *ck, QFile *f, int len) riff_data_t *data = malloc (sizeof (riff_data_t)); if (!Rread (f, &data->ck, sizeof (data->ck))) { free (data); + len = 0; } else { data->ck.len = LittleLong (data->ck.len); data->data = read_data (f, data->ck.len); @@ -236,7 +236,6 @@ read_list (riff_d_chunk_t *ck, QFile *f, int len) chunk = &data->ck; } } - len = 0; break; } if (chunk) { @@ -392,7 +391,6 @@ riff_read (QFile *f) break; } dstring_append (riff_buf, (char *)&chunk, sizeof (chunk)); - riff = (riff_list_t *) riff_buf->str; chunk = 0; } bail: diff --git a/libs/util/script.c b/libs/util/script.c index eeb1df977..1f9d5c037 100644 --- a/libs/util/script.c +++ b/libs/util/script.c @@ -34,19 +34,6 @@ #include "QF/dstring.h" #include "QF/script.h" -static void __attribute__ ((format (printf, 2, 3), noreturn)) -script_error (script_t *script, const char *fmt, ...) -{ - va_list args; - - va_start (args, fmt); - fprintf (stderr, "%s:%d: ", script->file, script->line); - vfprintf (stderr, fmt, args); - fprintf (stderr, "\n"); - va_end (args); - exit (1); -} - VISIBLE script_t * Script_New (void) { @@ -69,11 +56,16 @@ Script_Start (script_t *script, const char *file, const char *data) script->file = file; script->p = data; script->unget = false; + script->error = 0; } -VISIBLE qboolean -Script_TokenAvailable (script_t *script, qboolean crossline) +VISIBLE bool +Script_TokenAvailable (script_t *script, bool crossline) { + if (script->error) { + return false; + } + if (script->unget) return true; skipspace: @@ -106,11 +98,15 @@ Script_TokenAvailable (script_t *script, qboolean crossline) return true; } -VISIBLE qboolean -Script_GetToken (script_t *script, qboolean crossline) +VISIBLE bool +Script_GetToken (script_t *script, bool crossline) { const char *token_p; + if (script->error) { + return false; + } + if (script->unget) { // is a token allready waiting? script->unget = false; return true; @@ -118,10 +114,7 @@ Script_GetToken (script_t *script, qboolean crossline) if (!Script_TokenAvailable (script, crossline)) { if (!crossline) { - if (script->error) - script->error (script, "line is incomplete"); - else - script_error (script, "line is incomplete"); + script->error = "line is incomplete"; } return false; } @@ -134,18 +127,13 @@ Script_GetToken (script_t *script, qboolean crossline) while (*script->p != '"') { if (!*script->p) { script->line = start_line; - if (script->error) - script->error (script, "EOF inside quoted token"); - else - script_error (script, "EOF inside quoted token"); + script->error = "EOF inside quoted token"; return false; } if (*script->p == '\n') { if (script->no_quote_lines) { - if (script->error) - script->error (script, "EOL inside quoted token"); - else - script_error (script, "EOL inside quoted token"); + script->error = "EOL inside quoted token"; + return false; } script->line++; } @@ -175,11 +163,16 @@ Script_GetToken (script_t *script, qboolean crossline) VISIBLE void Script_UngetToken (script_t *script) { - script->unget = true; + if (!script->error) { + script->unget = true; + } } VISIBLE const char * Script_Token (script_t *script) { + if (script->error) { + return 0; + } return script->token->str; } diff --git a/libs/util/segtext.c b/libs/util/segtext.c index 851dff94f..347f0fe61 100644 --- a/libs/util/segtext.c +++ b/libs/util/segtext.c @@ -38,8 +38,8 @@ #include "QF/qtypes.h" #include "QF/segtext.h" -static segchunk_t *chunks_freelist; -static segtext_t *texts_freelist; +ALLOC_STATE (segchunk_t, chunks); +ALLOC_STATE (segtext_t, texts); static segchunk_t * new_chunk (void) @@ -140,7 +140,7 @@ Segtext_new (const char *source_string) if (!source_string) return 0; text = new_text (); - text->tab = Hash_NewTable (61, segtext_getkey, 0, 0); + text->tab = Hash_NewTable (61, segtext_getkey, 0, 0, 0); src = strdup (source_string); // The first chunk is special in that it holds the pointer to the copied @@ -176,8 +176,9 @@ Segtext_delete (segtext_t *st) while (st->chunk_list) { chunk = st->chunk_list; st->chunk_list = chunk->next; - if (chunk->tag) + if (chunk->tag) { free ((char *) chunk->tag); + } delete_chunk (chunk); } Hash_DelTable (st->tab); @@ -212,3 +213,16 @@ Segtext_Find (const segtext_t *st, const char *tag) return chunk->text; return 0; } + +static void +segtext_shutdown (void *data) +{ + ALLOC_FREE_BLOCKS (chunks); + ALLOC_FREE_BLOCKS (texts); +} + +static void __attribute__((constructor)) +segtext_init (void) +{ + Sys_RegisterShutdown (segtext_shutdown, 0); +} diff --git a/libs/util/set.c b/libs/util/set.c index 4b3b3a033..71f1a82e9 100644 --- a/libs/util/set.c +++ b/libs/util/set.c @@ -45,7 +45,11 @@ #include "QF/mathlib.h" #include "QF/set.h" -static set_pool_t static_set_pool = {0, 0}; +static set_pool_t static_set_pool = { + 0, 0, + DARRAY_STATIC_INIT (8), + DARRAY_STATIC_INIT (8), +}; static set_iter_t * new_setiter (set_pool_t *set_pool) @@ -76,8 +80,10 @@ set_del_iter_r (set_pool_t *set_pool, set_iter_t *set_iter) void set_pool_init (set_pool_t *set_pool) { - set_pool->set_freelist = 0; - set_pool->set_iter_freelist = 0; + *set_pool = (set_pool_t) { + .set_blocks = DARRAY_STATIC_INIT (8), + .set_iter_blocks = DARRAY_STATIC_INIT (8), + }; } inline set_t * @@ -111,16 +117,15 @@ set_delete (set_t *set) set_delete_r (&static_set_pool, set); } -static void -set_expand (set_t *set, unsigned x) +void +set_expand (set_t *set, unsigned size) { set_bits_t *map = set->map; - size_t size; - if (x <= set->size) + if (size <= set->size) return; - size = SET_SIZE (x); + size = SET_SIZE (size - 1); set->map = malloc (size / 8); memcpy (set->map, map, set->size / 8); memset (set->map + SET_WORDS (set), 0, (size - set->size) / 8); @@ -129,8 +134,32 @@ set_expand (set_t *set, unsigned x) free (map); } +void +set_trim (set_t *set) +{ + if (set->map == set->defmap) { + return; + } + unsigned words = SET_WORDS (set); + + while (words > SET_DEFMAP_SIZE && !set->map[words - 1]) { + words--; + set->size -= SET_BITS; + } + if (words > SET_DEFMAP_SIZE) { + set_bits_t *map = realloc (set->map, words * sizeof (set_bits_t)); + if (map && map != set->map) { + set->map = map; + } + } else { + memcpy (set->defmap, set->map, sizeof (set->defmap)); + free (set->map); + set->map = set->defmap; + } +} + inline set_t * -set_new_size_r (set_pool_t *set_pool, int size) +set_new_size_r (set_pool_t *set_pool, unsigned size) { set_t *set; @@ -141,7 +170,7 @@ set_new_size_r (set_pool_t *set_pool, int size) } set_t * -set_new_size (int size) +set_new_size (unsigned size) { return set_new_size_r (&static_set_pool, size); } @@ -151,7 +180,7 @@ _set_add (set_t *set, unsigned x) { if (x >= set->size) set_expand (set, x + 1); - set->map[x / SET_BITS] |= SET_ONE << (x % SET_BITS); + SET_ADD(set, x); } static inline void @@ -159,7 +188,60 @@ _set_remove (set_t *set, unsigned x) { if (x >= set->size) return; - set->map[x / SET_BITS] &= ~(SET_ONE << (x % SET_BITS)); + SET_REMOVE(set, x); +} + +static inline void +_set_add_range (set_t *set, unsigned start, unsigned count) +{ + if (!count) { + return; + } + if (start + count > set->size) { + set_expand (set, start + count); + } + unsigned end = start + count - 1; + set_bits_t start_mask = (~SET_ZERO) << (start % SET_BITS); + set_bits_t end_mask = (~SET_ZERO) >> (SET_BITS - ((end + 1) % SET_BITS)); + unsigned start_ind = start / SET_BITS; + unsigned end_ind = end / SET_BITS; + if (start_ind == end_ind) { + set->map[start_ind] |= start_mask & end_mask; + } else { + set->map[start_ind] |= start_mask; + for (unsigned i = start_ind + 1; i < end_ind; i++) { + set->map[i] = ~SET_ZERO; + } + set->map[end_ind] |= end_mask; + } +} + +static inline void +_set_remove_range (set_t *set, unsigned start, unsigned count) +{ + if (!count) { + return; + } + if (start >= set->size) { + return; + } + if (start + count > set->size) { + count = set->size - start; + } + unsigned end = start + count - 1; + set_bits_t start_mask = (~SET_ZERO) << (start % SET_BITS); + set_bits_t end_mask = (~SET_ZERO) >> (SET_BITS - ((end + 1) % SET_BITS)); + unsigned start_ind = start / SET_BITS; + unsigned end_ind = end / SET_BITS; + if (start_ind == end_ind) { + set->map[start_ind] &= ~(start_mask & end_mask); + } else { + set->map[start_ind] &= ~start_mask; + for (unsigned i = start_ind + 1; i < end_ind; i++) { + set->map[i] = SET_ZERO; + } + set->map[end_ind] &= ~end_mask; + } } set_t * @@ -172,6 +254,16 @@ set_add (set_t *set, unsigned x) return set; } +set_t * +set_add_range (set_t *set, unsigned start, unsigned count) +{ + if (set->inverted) + _set_remove_range (set, start, count); + else + _set_add_range (set, start, count); + return set; +} + set_t * set_remove (set_t *set, unsigned x) { @@ -182,6 +274,16 @@ set_remove (set_t *set, unsigned x) return set; } +set_t * +set_remove_range (set_t *set, unsigned start, unsigned count) +{ + if (set->inverted) + _set_add_range (set, start, count); + else + _set_remove_range (set, start, count); + return set; +} + set_t * set_invert (set_t *set) { @@ -205,13 +307,14 @@ _set_union (set_t *dst, const set_t *src) static set_t * _set_intersection (set_t *dst, const set_t *src) { - unsigned size; + unsigned words; unsigned i; - size = max (dst->size, src->size); - set_expand (dst, size); - for (i = 0; i < SET_WORDS (src); i++) + words = min (SET_WORDS (dst), SET_WORDS (src)); + for (i = 0; i < words; i++) dst->map[i] &= src->map[i]; + // if dst is larger than src, then none of the excess elements in dst + // can be in the intersection for ( ; i < SET_WORDS (dst); i++) dst->map[i] = 0; return dst; @@ -220,26 +323,37 @@ _set_intersection (set_t *dst, const set_t *src) static set_t * _set_difference (set_t *dst, const set_t *src) { - unsigned size; + unsigned words; unsigned i; - size = max (dst->size, src->size); - set_expand (dst, size); - for (i = 0; i < SET_WORDS (src); i++) + words = min (SET_WORDS (dst), SET_WORDS (src)); + for (i = 0; i < words; i++) dst->map[i] &= ~src->map[i]; + // if src is larger than dst, excess elements in src cannot be in dst thus + // there is nothing to remove + // if dst is larger than src, there is nothing to remove regardless of what + // is in src return dst; } static set_t * _set_reverse_difference (set_t *dst, const set_t *src) { - unsigned size; + unsigned words; unsigned i; - size = max (dst->size, src->size); - set_expand (dst, size); - for (i = 0; i < SET_WORDS (src); i++) + words = min (SET_WORDS (dst), SET_WORDS (src)); + set_expand (dst, src->size); + for (i = 0; i < words; i++) dst->map[i] = ~dst->map[i] & src->map[i]; + // if src is larger than dst, then dst cannot remove the excess elements + // from src and thus the src elements must be copied + for ( ; i < SET_WORDS (src); i++) + dst->map[i] = src->map[i]; + // if dst is larger than src, then the excess elements in dst must be + // removed + for ( ; i < SET_WORDS (dst); i++) + dst->map[i] = 0; return dst; } @@ -250,9 +364,9 @@ set_union (set_t *dst, const set_t *src) return _set_intersection (dst, src); } else if (src->inverted) { dst->inverted = 1; - return _set_difference (dst, src); - } else if (dst->inverted) { return _set_reverse_difference (dst, src); + } else if (dst->inverted) { + return _set_difference (dst, src); } else { return _set_union (dst, src); } @@ -343,7 +457,7 @@ set_everything (set_t *set) return set; } -static inline int +static inline __attribute__((pure)) int _set_is_empty (const set_t *set) { unsigned i; @@ -370,7 +484,7 @@ set_is_everything (const set_t *set) return _set_is_empty (set); } -static int +static __attribute__((pure)) int set_test_n_n (const set_t *s1, const set_t *s2) { unsigned i, end; @@ -394,7 +508,7 @@ set_test_n_n (const set_t *s1, const set_t *s2) return (difference != 0) | ((intersection != 0) << 1); } -static int +static __attribute__((pure)) int set_test_n_i (const set_t *s1, const set_t *s2) { unsigned i, end; @@ -419,7 +533,7 @@ set_test_n_i (const set_t *s1, const set_t *s2) return (difference != 0) | ((intersection != 0) << 1); } -static int +static __attribute__((pure)) int set_test_i_n (const set_t *s1, const set_t *s2) { unsigned i, end; @@ -444,7 +558,7 @@ set_test_i_n (const set_t *s1, const set_t *s2) return (difference != 0) | ((intersection != 0) << 1); } -static int +static __attribute__((pure)) int set_test_i_i (const set_t *s1, const set_t *s2) { unsigned i, end; @@ -470,7 +584,7 @@ set_test_i_i (const set_t *s1, const set_t *s2) return (difference != 0) | ((intersection != 0) << 1); } -static int +static __attribute__((pure)) int set_test (const set_t *s1, const set_t *s2) { if (s1->inverted && s2->inverted) @@ -540,7 +654,7 @@ _set_is_member (const set_t *set, unsigned x) { if (x >= set->size) return 0; - return (set->map[x / SET_BITS] & (SET_ONE << (x % SET_BITS))) != 0; + return SET_TEST_MEMBER(set, x) != 0; } int @@ -552,14 +666,33 @@ set_is_member (const set_t *set, unsigned x) } unsigned -set_size (const set_t *set) +set_count (const set_t *set) { + static byte bit_counts[] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, + }; unsigned count = 0; - unsigned i; + byte *b = (byte *) set->map; + unsigned i = SET_WORDS (set) * sizeof (set_bits_t); - for (i = 0; i < set->size; i++) - if (_set_is_member (set, i)) - count++; + while (i-- > 0) { + count += bit_counts[*b++]; + } return count; } @@ -607,33 +740,102 @@ set_next (set_iter_t *set_iter) return set_next_r (&static_set_pool, set_iter); } +set_iter_t * +set_while_r (set_pool_t *set_pool, set_iter_t *set_iter) +{ + unsigned x; + + if (_set_is_member (set_iter->set, set_iter->element)) { + for (x = set_iter->element + 1; x < set_iter->set->size; x++) { + if (!_set_is_member (set_iter->set, x)) { + set_iter->element = x; + return set_iter; + } + } + } else { + for (x = set_iter->element + 1; x < set_iter->set->size; x++) { + if (_set_is_member (set_iter->set, x)) { + set_iter->element = x; + return set_iter; + } + } + } + delete_setiter (set_pool, set_iter); + return 0; +} + +set_iter_t * +set_while (set_iter_t *set_iter) +{ + return set_while_r (&static_set_pool, set_iter); +} + +const char * +set_to_dstring_r (set_pool_t *set_pool, dstring_t *str, const set_t *set) +{ + set_iter_t *iter; + int first = 1; + + if (set_is_empty (set)) { + dstring_appendstr (str, "{}"); + return str->str; + } + if (set_is_everything (set)) { + dstring_appendstr (str, "{...}"); + return str->str; + } + dstring_appendstr (str, "{"); + if (set->inverted) { + unsigned start = 0; + + for (iter = set_first_r (set_pool, set); iter; + iter = set_next_r (set_pool, iter)) { + unsigned end = iter->element; + while (start < end) { + dasprintf (str, "%s%d", first ? "" : " ", start++); + first = 0; + } + start = end + 1; + } + dasprintf (str, "%s%d ...", first ? "" : " ", start); + } else { + for (iter = set_first_r (set_pool, set); iter; + iter = set_next_r (set_pool, iter)) { + dasprintf (str, "%s%d", first ? "" : " ", iter->element); + first = 0; + } + } + dstring_appendstr (str, "}"); + return str->str; +} + +const char * +set_to_dstring (dstring_t *str, const set_t *set) +{ + return set_to_dstring_r (&static_set_pool, str, set); +} + const char * set_as_string (const set_t *set) { static dstring_t *str; - unsigned i; - if (!str) + if (!str) { str = dstring_new (); - if (set_is_empty (set)) { - dstring_copystr (str, "{}"); - return str->str; } - if (set_is_everything (set)) { - dstring_copystr (str, "{...}"); - return str->str; - } - dstring_copystr (str, "{"); - for (i = 0; i < set->size; i++) { - if (set_is_member (set, i)) { - if (str->str[1]) - dasprintf (str, " %d", i); - else - dasprintf (str, "%d", i); - } - } - if (set->inverted) - dasprintf (str, "%s%d ...", str->str[1] ? " " : "", i); - dstring_appendstr (str, "}"); - return str->str; + dstring_clearstr (str); + return set_to_dstring_r (&static_set_pool, str, set); +} + +static void +set_shutdown (void *data) +{ + ALLOC_FREE_BLOCKS (static_set_pool.set); + ALLOC_FREE_BLOCKS (static_set_pool.set_iter); +} + +static void __attribute__((constructor)) +set_init (void) +{ + Sys_RegisterShutdown (set_shutdown, 0); } diff --git a/libs/util/simd.c b/libs/util/simd.c new file mode 100644 index 000000000..352a4169a --- /dev/null +++ b/libs/util/simd.c @@ -0,0 +1,366 @@ +/* + simd.c + + SIMD math support + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "qfalloca.h" + +#include + +#define IMPLEMENT_VEC2F_Funcs +#define IMPLEMENT_VEC2D_Funcs +#define IMPLEMENT_VEC2I_Funcs +#define IMPLEMENT_VEC4F_Funcs +#define IMPLEMENT_VEC4D_Funcs +#define IMPLEMENT_VEC4I_Funcs +#define IMPLEMENT_MAT4F_Funcs + +#include "QF/mathlib.h" +#include "QF/simd/vec2d.h" +#include "QF/simd/vec2f.h" +#include "QF/simd/vec2i.h" +#include "QF/simd/vec4d.h" +#include "QF/simd/vec4f.h" +#include "QF/simd/vec4i.h" +#include "QF/simd/mat4f.h" +#include "QF/set.h" +#include "QF/sys.h" + +vec4f_t +BarycentricCoords_vf (const vec4f_t **points, int num_points, const vec4f_t p) +{ + vec4f_t zero = { }; + vec4f_t a, b, c, x, l, ab, bc, ca, d; + if (num_points > 4) + Sys_Error ("Don't know how to compute the barycentric coordinates " + "for %d points", num_points); + switch (num_points) { + case 1: + l = zero; + l[0] = 1; + return l; + case 2: + x = p - *points[0]; + a = *points[1] - *points[0]; + d = dotf (x, a) / dotf (a, a); + l = zero; + l[1] = d[0]; + l[0] = 1 - d[0]; + return l; + case 3: + x = p - *points[0]; + a = *points[1] - *points[0]; + b = *points[2] - *points[0]; + ab = crossf (a, b); + d = dotf (ab, ab); + l[1] = dotf (crossf (x, b), ab)[0]; + l[2] = dotf (crossf (a, x), ab)[0]; + l[0] = d[0] - l[1] - l[2]; + return l / d; + case 4: + x = p - *points[0]; + a = *points[1] - *points[0]; + b = *points[2] - *points[0]; + c = *points[3] - *points[0]; + ab = crossf (a, b); + bc = crossf (b, c); + ca = crossf (c, a); + d = dotf (a, bc); + l[1] = (dotf (x, bc) / d)[0]; + l[2] = (dotf (x, ca) / d)[0]; + l[3] = (dotf (x, ab) / d)[0]; + l[0] = 1 - l[1] - l[2] - l[3]; + return l; + } + Sys_Error ("Not enough points to project or enclose the point"); +} + +static int +circum_circle (const vec4f_t points[], int num_points, vec4f_t *center) +{ + vec4f_t a, c, b; + vec4f_t bc, ca, ab; + vec4f_t aa, bb, cc; + vec4f_t div; + vec4f_t alpha, beta, gamma; + + switch (num_points) { + case 1: + *center = points[0]; + return 1; + case 2: + *center = (points[0] + points[1]) / 2; + return 1; + case 3: + a = points[0] - points[1]; + b = points[0] - points[2]; + c = points[1] - points[2]; + aa = dotf (a, a); + bb = dotf (b, b); + cc = dotf (c, c); + div = dotf (a, c); + div = 2 * (aa * cc - div * div); + if (fabs (div[0]) < EQUAL_EPSILON) { + // degenerate + return 0; + } + alpha = cc * dotf (a, b) / div; + beta = -bb * dotf (a, c) / div; + gamma = aa * dotf (b, c) / div; + *center = alpha * points[0] + beta * points[1] + gamma * points[2]; + return 1; + case 4: + a = points[1] - points[0]; + b = points[2] - points[0]; + c = points[3] - points[0]; + bc = crossf (b, c); + ca = crossf (c, a); + ab = crossf (a, b); + div = 2 * dotf (a, bc); + if (fabs (div[0]) < EQUAL_EPSILON) { + // degenerate + return 0; + } + aa = dotf (a, a) / div; + bb = dotf (b, b) / div; + cc = dotf (c, c) / div; + *center = bc * aa + bb * ca + cc * ab + points[0]; + return 1; + } + return 0; +} + +vspheref_t +CircumSphere_vf (const vec4f_t *points, int num_points) +{ + vspheref_t sphere = {}; + if (num_points > 0 && num_points <= 4) { + if (circum_circle (points, num_points, &sphere.center)) { + vec4f_t d = sphere.center - points[0]; + sphere.radius = sqrt(dotf (d, d)[0]); + } else { + // degenerate + sphere.radius = -1; + } + } + return sphere; +} + +static vec4f_t +closest_affine_point (const vec4f_t **points, int num_points, const vec4f_t x) +{ + vec4f_t closest = {}; + vec4f_t a, b, n, d; + vec4f_t l; + + switch (num_points) { + default: + case 1: + closest = *points[0]; + break; + case 2: + n = *points[1] - *points[0]; + d = x - *points[0]; + l = dotf (d, n) / dotf (n, n); + closest = *points[0] + l * n; + break; + case 3: + a = *points[1] - *points[0]; + b = *points[2] - *points[0]; + n = crossf (a, b); + d = *points[0] - x; + l = dotf (d, n) / dotf (n, n); + closest = x + l * n; + break; + } + return closest; +} + +static int +test_support_points(const vec4f_t **points, int *num_points, vec4f_t center) +{ + vec4i_t cmp; + int in_affine = 0; + int in_convex = 0; + vec4f_t v, d, n, a, b; + float nn, dd, vv, dn; + + switch (*num_points) { + case 1: + cmp = *points[0] == center; + in_affine = cmp[0] && cmp[1] && cmp[2]; + // the convex hull and affine hull for a single point are the same + in_convex = in_affine; + break; + case 2: + v = *points[1] - *points[0]; + d = center - (*points[0] + *points[1]) / 2; + n = crossf (v, d); + nn = dotf (n, n)[0]; + vv = dotf (v, v)[0]; + in_affine = nn <= 1e-5 * vv * vv; + break; + case 3: + a = *points[1] - *points[0]; + b = *points[2] - *points[0]; + d = center - *points[0]; + n = crossf (a, b); + dn = dotf (d, n)[0]; + dd = dotf (d, d)[0]; + nn = dotf (n, n)[0]; + in_affine = dn * dn < 1e-5 * dd * nn; + break; + case 4: + in_affine = 1; + break; + default: + Sys_Error ("Invalid number of points (%d) in test_support_points", + *num_points); + } + + // if in_convex is not true while in_affine is, then need to test as + // there is more than one dimension for the affine hull (a single support + // point is never dropped as it cannot be redundant) + if (in_affine && !in_convex) { + vec4f_t lambda; + int dropped = 0; + int count = *num_points; + + lambda = BarycentricCoords_vf (points, count, center); + + for (int i = 0; i < count; i++) { + points[i - dropped] = points[i]; + if (lambda[i] < -1e-4) { + dropped++; + (*num_points)--; + } + } + in_convex = !dropped; + if (dropped) { + for (int i = count - dropped; i < count; i++) { + points[i] = 0; + } + } + } + return in_convex; +} + +vspheref_t +SmallestEnclosingBall_vf (const vec4f_t *points, int num_points) +{ + set_t was_support = SET_STATIC_INIT (num_points, alloca); + vspheref_t sphere = {}; + vec4f_t center = {}; + const vec4f_t *best; + const vec4f_t *support[4]; + int num_support; + int i; + int iters = 0; + + if (num_points < 1) { + return sphere; + } + + for (i = 0; i < 4; i++) { + support[i] = 0; + } + set_empty (&was_support); + + vec4f_t dist = {}; + float best_dist = 0; + center = points[0]; + best = &points[0]; + for (i = 1; i < num_points; i++) { + dist = points[i] - center; + dist = dotf (dist, dist); + if (dist[0] > best_dist) { + best_dist = dist[0]; + best = &points[i]; + } + } + num_support = 1; + support[0] = best; + sphere.radius = best_dist; // note: radius squared until the end + set_add (&was_support, best - points); + + while (!test_support_points (support, &num_support, center)) { + vec4f_t affine, r, rr; + vec4f_t v, p, pv, pp, x; + vec4f_t best_x = { }; + int i; + + //Sys_Printf ("%d: "VEC4F_FMT", %.9g, %d\n", iters, VEC4_EXP (center), sqrt(sphere.radius), num_support); + if (iters++ > 2 * num_points) { + //for (i = 0; i < num_points; i++) { + // Sys_Printf (VEC4F_FMT",\n", VEC4_EXP (points[i])); + //} + Sys_Error ("stuck SEB"); + } + + affine = closest_affine_point (support, num_support, center); + r = *support[0] - affine; //FIXME better choice + rr = dotf (r, r); + v = center - affine; + + best = 0; + for (i = 0; i < num_points; i++) { + if (SET_TEST_MEMBER (&was_support, i)) { + continue; + } + p = points[i] - affine; + pp = dotf (p, p); + pv = dotf (p, v); + if (pp[0] <= rr[0] || pv[0] <= 0 + || (pv[0] * pv[0]) < 1e-6 * rr[0]) { + continue; + } + x = (pp - rr) / (2 * pv); + if (x[0] > best_x[0]) { + best = &points[i]; + best_x = x; + } + } + center = affine + best_x * v; + dist = center - *support[0]; + sphere.radius = dotf (dist, dist)[0]; + if (best) { + support[num_support++] = best; + set_add (&was_support, best - points); + } + } + best_dist = 0; + for (i = 0; i < num_points; i++) { + dist = center - points[i]; + dist = dotf (dist, dist); + if (dist[0] > best_dist) + best_dist = dist[0]; + } + sphere.center = center; + sphere.radius = sqrt (best_dist); + return sphere; +} diff --git a/libs/util/sizebuf.c b/libs/util/sizebuf.c index 757672367..a7b241a41 100644 --- a/libs/util/sizebuf.c +++ b/libs/util/sizebuf.c @@ -41,12 +41,12 @@ VISIBLE void -SZ_Alloc (sizebuf_t *buf, int startsize) +SZ_Alloc (sizebuf_t *buf, unsigned maxsize) { - if (startsize < 256) - startsize = 256; - buf->data = Hunk_AllocName (startsize, "sizebuf"); - buf->maxsize = startsize; + if (maxsize < 256) + maxsize = 256; + buf->data = Hunk_AllocName (0, maxsize, "sizebuf"); + buf->maxsize = maxsize; buf->cursize = 0; } @@ -58,7 +58,7 @@ SZ_Clear (sizebuf_t *buf) } VISIBLE void * -SZ_GetSpace (sizebuf_t *buf, int length) +SZ_GetSpace (sizebuf_t *buf, unsigned length) { void *data; @@ -83,7 +83,7 @@ getspace: } VISIBLE void -SZ_Write (sizebuf_t *buf, const void *data, int length) +SZ_Write (sizebuf_t *buf, const void *data, unsigned length) { memcpy (SZ_GetSpace (buf, length), data, length); } @@ -91,7 +91,7 @@ SZ_Write (sizebuf_t *buf, const void *data, int length) VISIBLE void SZ_Print (sizebuf_t *buf, const char *data) { - int len; + unsigned len; len = strlen (data) + 1; if (buf->cursize && !buf->data[buf->cursize - 1]) @@ -99,3 +99,34 @@ SZ_Print (sizebuf_t *buf, const char *data) memcpy (SZ_GetSpace (buf, len), data, len); } + +VISIBLE void +SZ_Dump (sizebuf_t *buf) +{ + unsigned i; + char chars[17], c; + + chars[16] = 0; + for (i = 0; i < buf->cursize; i++) { + if (i % 16 == 0) { + Sys_Printf ("%04x:", i); + } else if (i % 16 == 8) { + Sys_Printf (" "); + } + Sys_Printf (" %02x", buf->data[i]); + c = buf->data[i] & 0x7f; + if (c < ' ') { + c = '.'; + } + chars[i % 16] = c; + if (i % 16 == 15) { + Sys_Printf (" %s\n", chars); + } + } + i %= 16; + if (i) { + chars[i] = 0; + i = 16 - i; + Sys_Printf ("%*s %s\n", i * 3 + (i > 7), "", chars); + } +} diff --git a/libs/util/string.c b/libs/util/string.c index 4eed4a234..aeab6abb2 100644 --- a/libs/util/string.c +++ b/libs/util/string.c @@ -35,6 +35,7 @@ # include #endif #include +#include #include "qstring.h" @@ -72,6 +73,16 @@ Q_strnlen (const char *s, size_t maxlen) return i; } +char * +Q_strndup (const char *s, size_t n) +{ + size_t l = strnlen (s, n); + char *str = malloc (l + 1); + strncpy (str, s, l); + str[l] = 0; + return str; +} + #if defined(HAVE__VSNPRINTF) && !defined(HAVE_VSNPRINTF) size_t Q_snprintfz (char *dest, size_t size, const char *fmt, ...) diff --git a/libs/util/sys.c b/libs/util/sys.c index 1b6a9c269..2ae275748 100644 --- a/libs/util/sys.c +++ b/libs/util/sys.c @@ -60,6 +60,7 @@ # include #endif +#include #include #include #include @@ -89,23 +90,61 @@ #include "QF/va.h" #include "compat.h" +#define IMPLEMENT_QFSELECT_Funcs +#include "qfselect.h" -static void Sys_StdPrintf (const char *fmt, va_list args); -static void Sys_ErrPrintf (const char *fmt, va_list args); +static void Sys_StdPrintf (const char *fmt, va_list args) __attribute__((format(PRINTF, 1, 0))); +static void Sys_ErrPrintf (const char *fmt, va_list args) __attribute__((format(PRINTF, 1, 0))); -VISIBLE cvar_t *sys_nostdout; -VISIBLE cvar_t *sys_extrasleep; -cvar_t *sys_dead_sleep; -cvar_t *sys_sleep; +VISIBLE int sys_nostdout; +static cvar_t sys_nostdout_cvar = { + .name = "sys_nostdout", + .description = + "Set to disable std out", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sys_nostdout }, +}; +VISIBLE int sys_extrasleep; +static cvar_t sys_extrasleep_cvar = { + .name = "sys_extrasleep", + .description = + "Set to cause whatever amount delay in microseconds you want. Mostly " + "useful to generate simulated bad connections.", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sys_extrasleep }, +}; +int sys_dead_sleep; +static cvar_t sys_dead_sleep_cvar = { + .name = "sys_dead_sleep", + .description = + "When set, the server gets NO cpu if no clients are connected and " + "there's no other activity. *MIGHT* cause problems with some mods.", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sys_dead_sleep }, +}; +int sys_sleep; +static cvar_t sys_sleep_cvar = { + .name = "sys_sleep", + .description = + "Sleep how long in seconds between checking for connections. Minimum " + "is 0, maximum is 13", + .default_value = "8", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sys_sleep }, +}; int sys_checksum; -static sys_printf_t sys_std_printf_function = Sys_StdPrintf; -static sys_printf_t sys_err_printf_function = Sys_ErrPrintf; +static __thread sys_printf_t sys_std_printf_function = Sys_StdPrintf; +static __thread sys_printf_t sys_err_printf_function = Sys_ErrPrintf; typedef struct shutdown_list_s { struct shutdown_list_s *next; - void (*func)(void); + void (*func) (void *); + void *data; } shutdown_list_t; typedef struct error_handler_s { @@ -114,30 +153,30 @@ typedef struct error_handler_s { void *data; } error_handler_t; -static shutdown_list_t *shutdown_list; +static __thread shutdown_list_t *shutdown_list; -static error_handler_t *error_handler_freelist; -static error_handler_t *error_handler; +static __thread error_handler_t *error_handler_freelist; +static __thread error_handler_t *error_handler; #ifndef _WIN32 static int do_stdin = 1; -qboolean stdin_ready; +bool stdin_ready; #endif /* The translation table between the graphical font and plain ASCII --KB */ VISIBLE const char sys_char_map[256] = { - '\0', '#', '#', '#', '#', '.', '#', '#', - '#', 9, 10, '#', ' ', 13, '.', '.', + 0, '#', '#', '#', '#', '.', '#', '#', + '#', 9, 10, '#', ' ', 13, '.', '.', '[', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '<', '=', '>', - ' ', '!', '"', '#', '$', '%', '&', '\'', + ' ', '!', '"', '#', '$', '%', '&','\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', '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', '[', '\\', ']', '^', '_', + 'X', 'Y', 'Z', '[','\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', @@ -147,14 +186,14 @@ VISIBLE const char sys_char_map[256] = { '#', '#', ' ', '#', ' ', '>', '.', '.', '[', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '<', '=', '>', - ' ', '!', '"', '#', '$', '%', '&', '\'', + ' ', '!', '"', '#', '$', '%', '&','\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', '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', '[', '\\', ']', '^', '_', + 'X', 'Y', 'Z', '[','\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', @@ -240,38 +279,48 @@ Sys_FileExists (const char *path) for want of a better name, but it sets the function pointer for the actual implementation of Sys_Printf. */ -VISIBLE void +VISIBLE sys_printf_t Sys_SetStdPrintf (sys_printf_t func) { + sys_printf_t prev = sys_std_printf_function; + if (!func) { + func = Sys_StdPrintf; + } sys_std_printf_function = func; + return prev; } -VISIBLE void +VISIBLE sys_printf_t Sys_SetErrPrintf (sys_printf_t func) { + sys_printf_t prev = sys_err_printf_function; + if (!func) { + func = Sys_ErrPrintf; + } sys_err_printf_function = func; + return prev; } +static __thread dstring_t *sys_print_msg; void Sys_Print (FILE *stream, const char *fmt, va_list args) { - static dstring_t *msg; unsigned char *p; - if (!msg) - msg = dstring_new (); + if (!sys_print_msg) + sys_print_msg = dstring_new (); - dvsprintf (msg, fmt, args); + dvsprintf (sys_print_msg, fmt, args); if (stream == stderr) { #ifdef _WIN32 - MessageBox (NULL, msg->str, "Fatal Error", 0 /* MB_OK */ ); + MessageBox (NULL, sys_print_msg->str, "Fatal Error", 0 /* MB_OK */ ); #endif fputs ("Fatal Error: ", stream); } /* translate to ASCII instead of printing [xx] --KB */ - for (p = (unsigned char *) msg->str; *p; p++) + for (p = (unsigned char *) sys_print_msg->str; *p; p++) putc (sys_char_map[*p], stream); if (stream == stderr) { @@ -283,7 +332,7 @@ Sys_Print (FILE *stream, const char *fmt, va_list args) static void Sys_StdPrintf (const char *fmt, va_list args) { - if (sys_nostdout && sys_nostdout->int_val) + if (sys_nostdout) return; Sys_Print (stdout, fmt, args); } @@ -304,21 +353,29 @@ Sys_Printf (const char *fmt, ...) } VISIBLE void -Sys_MaskPrintf (int mask, const char *fmt, ...) +Sys_MaskPrintf (sys_developer_e mask, const char *fmt, ...) { va_list args; - if (!developer || !(developer->int_val & mask)) + if (!(developer & mask)) return; va_start (args, fmt); sys_std_printf_function (fmt, args); va_end (args); } +static int64_t sys_starttime = -1; + +VISIBLE int64_t +Sys_StartTime (void) +{ + return sys_starttime; +} + VISIBLE int64_t Sys_LongTime (void) { - static qboolean first = true; + static bool first = true; #ifdef _WIN32 # if 0 static DWORD starttime; @@ -348,11 +405,10 @@ Sys_LongTime (void) static int64_t qpcfudge = 0; int64_t currtime = 0; static int64_t lasttime = 0; - static int64_t starttime = 0; if (first) { timeBeginPeriod (1); - starttime = lasttime = timeGetTime (); + sys_starttime = lasttime = timeGetTime () * 1000; QueryPerformanceFrequency ((LARGE_INTEGER *) &qpcfreq); QueryPerformanceCounter ((LARGE_INTEGER *) &lastqpccount); first = false; @@ -360,7 +416,7 @@ Sys_LongTime (void) } // get the current time from both counters - currtime = timeGetTime (); + currtime = timeGetTime () * 1000; QueryPerformanceCounter ((LARGE_INTEGER *) &currqpccount); if (currtime != lasttime) { @@ -371,7 +427,7 @@ Sys_LongTime (void) // store back times and calc a fudge factor as timeGetTime can // overshoot on a sub-millisecond scale qpcfudge = (( (currqpccount - lastqpccount) * 1000000 / qpcfreq)) - - ((currtime - lasttime) * 1000); + - (currtime - lasttime); lastqpccount = currqpccount; lasttime = currtime; } else { @@ -379,32 +435,51 @@ Sys_LongTime (void) } // the final time is the base from timeGetTime plus an addition from QPC - return (((currtime - starttime) * 1000) + return ((currtime - sys_starttime) + ((currqpccount - lastqpccount) * 1000000 / qpcfreq) + qpcfudge); # endif +#else + int64_t now; +#ifdef CLOCK_BOOTTIME + struct timespec tp; + + clock_gettime (CLOCK_BOOTTIME, &tp); + + now = tp.tv_sec * 1000000 + tp.tv_nsec / 1000; #else struct timeval tp; struct timezone tzp; - int64_t now; - static int64_t start_time; gettimeofday (&tp, &tzp); now = tp.tv_sec * 1000000 + tp.tv_usec; +#endif if (first) { first = false; - start_time = now; + sys_starttime = now; } - return now - start_time; + return now - sys_starttime; #endif } +VISIBLE int64_t +Sys_TimeBase (void) +{ + return INT64_C (4294967296000000); +} + VISIBLE double Sys_DoubleTime (void) { - return (__INT64_C (4294967296000000) + Sys_LongTime ()) / 1e6; + return (Sys_TimeBase () + Sys_LongTime ()) / 1e6; +} + +VISIBLE double +Sys_DoubleTimeBase (void) +{ + return Sys_TimeBase () / 1e6; } VISIBLE void @@ -432,25 +507,15 @@ Sys_MakeCodeWriteable (uintptr_t startaddr, size_t length) DWORD flOldProtect; if (!VirtualProtect - ((LPVOID) startaddr, length, PAGE_READWRITE, &flOldProtect)) + ((LPVOID) startaddr, length, PAGE_EXECUTE_READWRITE, &flOldProtect)) Sys_Error ("Protection change failed"); #else # ifdef HAVE_MPROTECT int r; + long psize = Sys_PageSize (); unsigned long endaddr = startaddr + length; -# ifdef HAVE__SC_PAGESIZE - long psize = sysconf (_SC_PAGESIZE); - - startaddr &= ~(psize - 1); - endaddr = (endaddr + psize - 1) & ~(psize -1); -# else -# ifdef HAVE_GETPAGESIZE - int psize = getpagesize (); - startaddr &= ~(psize - 1); endaddr = (endaddr + psize - 1) & ~(psize - 1); -# endif -# endif // systems with mprotect but not getpagesize (or similar) probably don't // need to page align the arguments to mprotect (eg, QNX) r = mprotect ((char *) startaddr, endaddr - startaddr, @@ -465,34 +530,10 @@ Sys_MakeCodeWriteable (uintptr_t startaddr, size_t length) VISIBLE void Sys_Init_Cvars (void) { - sys_nostdout = Cvar_Get ("sys_nostdout", "0", CVAR_NONE, NULL, - "Set to disable std out"); - sys_extrasleep = Cvar_Get ("sys_extrasleep", "0", CVAR_NONE, NULL, - "Set to cause whatever amount delay in " - "microseconds you want. Mostly " - "useful to generate simulated bad " - "connections."); - sys_dead_sleep = Cvar_Get ("sys_dead_sleep", "0", CVAR_NONE, NULL, - "When set, the server gets NO cpu if no " - "clients are connected and there's no other " - "activity. *MIGHT* cause problems with some " - "mods."); - sys_sleep = Cvar_Get ("sys_sleep", "8", CVAR_NONE, NULL, "Sleep how long " - "in seconds between checking for connections. " - "Minimum is 0, maximum is 13"); -} - -void -Sys_Shutdown (void) -{ - shutdown_list_t *t; - - while (shutdown_list) { - shutdown_list->func (); - t = shutdown_list; - shutdown_list = shutdown_list->next; - free (t); - } + Cvar_Register (&sys_nostdout_cvar, 0, 0); + Cvar_Register (&sys_extrasleep_cvar, 0, 0); + Cvar_Register (&sys_dead_sleep_cvar, 0, 0); + Cvar_Register (&sys_sleep_cvar, 0, 0); } VISIBLE void @@ -507,9 +548,15 @@ VISIBLE void Sys_PushErrorHandler (sys_error_t func, void *data) { error_handler_t *eh; - ALLOC (16, error_handler_t, error_handler, eh); + if (error_handler_freelist) { + eh = error_handler_freelist; + } else { + eh = malloc (sizeof (error_handler_t)); + eh->next = 0; + } eh->func = func; eh->data = data; + error_handler_freelist = eh->next; eh->next = error_handler; error_handler = eh; } @@ -524,7 +571,8 @@ Sys_PopErrorHandler (void) } eh = error_handler; error_handler = eh->next; - FREE (error_handler, eh); + eh->next = error_handler_freelist; + error_handler_freelist = eh; } @@ -567,7 +615,7 @@ Sys_Error (const char *error, ...) } VISIBLE void -Sys_RegisterShutdown (void (*func) (void)) +Sys_RegisterShutdown (void (*func) (void *), void *data) { shutdown_list_t *p; if (!func) @@ -576,6 +624,7 @@ Sys_RegisterShutdown (void (*func) (void)) if (!p) Sys_Error ("Sys_RegisterShutdown: insufficient memory"); p->func = func; + p->data = data; p->next = shutdown_list; shutdown_list = p; } @@ -593,11 +642,11 @@ Sys_TimeID (void) //FIXME I need a new name, one that doesn't make me feel 3 fee } VISIBLE void -Sys_PageIn (void *ptr, int size) +Sys_PageIn (void *ptr, size_t size) { //may or may not be useful in linux #ifdef _WIN32 byte *x; - int m, n; + size_t m, n; // touch all the memory to make sure it's there. The 16-page skip is to // keep Win 95 from thinking we're trying to page ourselves in (we are @@ -613,71 +662,206 @@ Sys_PageIn (void *ptr, int size) //#endif } +#if defined(_WIN32) && !defined(_WIN64) +// this is a hack to make memory allocations 16-byte aligned on 32-bit +// systems (in particular for this case, windows) as vectors and +// matrices require 16-byte alignment but system malloc (etc) provide only +// 8-byte alignment. +void *__cdecl +calloc (size_t nume, size_t sizee) +{ + size_t size = nume * sizee; + void *mem = _aligned_malloc (size, 16); + memset (mem, 0, size); + return mem; +} + +void __cdecl +free (void *mem) +{ + _aligned_free (mem); +} + +void *__cdecl +malloc (size_t size) +{ + return _aligned_malloc (size, 16); +} + +void *__cdecl +realloc (void *mem, size_t size) +{ + return _aligned_realloc (mem, size, 16); +} + +char *__cdecl +strdup(const char *src) +{ + size_t len = strlen (src); + char *dup = malloc (len + 1); + strcpy (dup, src); + return dup; +} +#endif + +VISIBLE size_t +Sys_PageSize (void) +{ +#ifdef _WIN32 + SYSTEM_INFO si; + GetSystemInfo (&si); + return si.dwPageSize; +#else +# ifdef HAVE__SC_PAGESIZE + return sysconf (_SC_PAGESIZE); +# else +# ifdef HAVE_GETPAGESIZE + return getpagesize (); +# endif +# endif +#endif +} + +VISIBLE void * +Sys_Alloc (size_t size) +{ + size_t page_size = Sys_PageSize (); + size_t page_mask = page_size - 1; + size = (size + page_mask) & ~page_mask; +#ifdef _WIN32 + return VirtualAlloc (0, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); +#else + return mmap (0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); +#endif +} + +VISIBLE void +Sys_Free (void *mem, size_t size) +{ + size_t page_size = Sys_PageSize (); + size_t page_mask = page_size - 1; + size = (size + page_mask) & ~page_mask; +#ifdef _WIN32 + VirtualFree (mem, 0, MEM_RELEASE | MEM_DECOMMIT); +#else + munmap (mem, size); +#endif +} + +VISIBLE int +Sys_LockMemory (void *mem, size_t size) +{ + size_t page_size = Sys_PageSize (); + size_t page_mask = page_size - 1; + size = (size + page_mask) & ~page_mask; +#ifdef _WIN32 + return VirtualLock (mem, size) != 0; +#else + return mlock (mem, size) == 0; +#endif +} + +VISIBLE int +Sys_ProcessorCount (void) +{ + int cpus = 1; +#if defined (_SC_NPROCESSORS_ONLN) + cpus = sysconf(_SC_NPROCESSORS_ONLN); +#elif defined (_WIN32) + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + cpus = sysinfo.dwNumberOfProcessors; +#endif + if (cpus < 1) { + cpus = 1; + } + return cpus; +} + +static __thread dstring_t *sys_debuglog_data; VISIBLE void Sys_DebugLog (const char *file, const char *fmt, ...) { va_list args; - static dstring_t *data; int fd; - if (!data) - data = dstring_newstr (); + if (!sys_debuglog_data) + sys_debuglog_data = dstring_newstr (); va_start (args, fmt); - dvsprintf (data, fmt, args); + dvsprintf (sys_debuglog_data, fmt, args); va_end (args); if ((fd = open (file, O_WRONLY | O_CREAT | O_APPEND, 0644)) >= 0) { - if (write (fd, data->str, data->size - 1) != (ssize_t) (data->size - 1)) + if (write (fd, sys_debuglog_data->str, sys_debuglog_data->size - 1) + != (ssize_t) (sys_debuglog_data->size - 1)) Sys_Printf ("Error writing %s: %s\n", file, strerror(errno)); close (fd); } } VISIBLE int -Sys_CheckInput (int idle, int net_socket) +Sys_Select (int maxfd, qf_fd_set *fdset, int64_t usec) { - fd_set fdset; - int res; struct timeval _timeout; struct timeval *timeout = 0; + if (usec >= 0) { + timeout = &_timeout; + if (usec < 1000000) { + _timeout.tv_sec = 0; + _timeout.tv_usec = usec; + } else { + _timeout.tv_sec = usec / 1000000; + _timeout.tv_usec = usec % 1000000; + } + } + + return select (maxfd + 1, &fdset->fdset, NULL, NULL, timeout); +} + +VISIBLE int +Sys_CheckInput (int idle, int net_socket) +{ + qf_fd_set fdset; + int res; + int64_t usec; + #ifdef _WIN32 int sleep_msec; // Now we want to give some processing time to other applications, // such as qw_client, running on this machine. - sleep_msec = sys_sleep->int_val; + sleep_msec = sys_sleep; if (sleep_msec > 0) { if (sleep_msec > 13) sleep_msec = 13; Sleep (sleep_msec); } - _timeout.tv_sec = 0; - _timeout.tv_usec = net_socket < 0 ? 0 : 20; + usec = net_socket < 0 ? 0 : 20; #else - _timeout.tv_sec = 0; - _timeout.tv_usec = net_socket < 0 ? 0 : 2000; + usec = net_socket < 0 ? 0 : 2000; #endif // select on the net socket and stdin // the only reason we have a timeout at all is so that if the last // connected client times out, the message would not otherwise // be printed until the next event. - FD_ZERO (&fdset); + QF_FD_ZERO (&fdset); #ifndef _WIN32 if (do_stdin) - FD_SET (0, &fdset); + QF_FD_SET (0, &fdset); #endif if (net_socket >= 0) - FD_SET (((unsigned) net_socket), &fdset); // cast needed for windows + QF_FD_SET (((unsigned) net_socket), &fdset);// cast needed for windows - if (!idle || !sys_dead_sleep->int_val) - timeout = &_timeout; + if (idle && sys_dead_sleep) + usec = -1; - res = select (max (net_socket, 0) + 1, &fdset, NULL, NULL, timeout); + res = Sys_Select (max (net_socket, 0), &fdset, usec); if (res == 0 || res == -1) return 0; #ifndef _WIN32 - stdin_ready = FD_ISSET (0, &fdset); + stdin_ready = QF_FD_ISSET (0, &fdset); #endif return 1; } @@ -748,7 +932,11 @@ Sys_ConsoleInput (void) #endif } -static jmp_buf aiee_abort; +#ifdef _WIN32 +static __thread jmp_buf aiee_abort; +#else +static __thread sigjmp_buf aiee_abort; +#endif typedef struct sh_stack_s { struct sh_stack_s *next; @@ -756,10 +944,10 @@ typedef struct sh_stack_s { void *data; } sh_stack_t; -static sh_stack_t *sh_stack; -static sh_stack_t *free_sh; -static int (*signal_hook)(int,void*); -static void *signal_hook_data; +static __thread sh_stack_t *sh_stack; +static __thread sh_stack_t *free_sh; +static __thread int (*signal_hook)(int,void*); +static __thread void *signal_hook_data; VISIBLE void Sys_PushSignalHook (int (*hook)(int, void *), void *data) @@ -798,87 +986,138 @@ Sys_PopSignalHook (void) } } -static void +static void __attribute__((noreturn)) aiee (int sig) { printf ("AIEE, signal %d in shutdown code, giving up\n", sig); +#ifdef _WIN32 longjmp (aiee_abort, 1); +#else + siglongjmp (aiee_abort, 1); +#endif } +#ifdef _WIN32 static void signal_handler (int sig) { int volatile recover = 0; // volatile for longjump + static volatile int in_signal_handler = 0; + if (in_signal_handler) { + aiee (sig); + } printf ("Received signal %d, exiting...\n", sig); switch (sig) { case SIGINT: case SIGTERM: -#ifndef _WIN32 - case SIGHUP: - signal (SIGHUP, SIG_DFL); -#endif - signal (SIGINT, SIG_DFL); + signal (SIGINT, SIG_DFL); signal (SIGTERM, SIG_DFL); - Sys_Quit (); + exit(1); default: -#ifndef _WIN32 - signal (SIGQUIT, aiee); - signal (SIGTRAP, aiee); - signal (SIGIOT, aiee); - signal (SIGBUS, aiee); -#endif - signal (SIGILL, aiee); - signal (SIGSEGV, aiee); - signal (SIGFPE, aiee); - if (!setjmp (aiee_abort)) { if (signal_hook) recover = signal_hook (sig, signal_hook_data); Sys_Shutdown (); } - if (recover) { -#ifndef _WIN32 - signal (SIGQUIT, signal_handler); - signal (SIGTRAP, signal_handler); - signal (SIGIOT, signal_handler); - signal (SIGBUS, signal_handler); -#endif - signal (SIGILL, signal_handler); - signal (SIGSEGV, signal_handler); - signal (SIGFPE, signal_handler); - } else { -#ifndef _WIN32 - signal (SIGQUIT, SIG_DFL); - signal (SIGTRAP, SIG_DFL); - signal (SIGIOT, SIG_DFL); - signal (SIGBUS, SIG_DFL); -#endif - signal (SIGILL, SIG_DFL); + if (!recover) { + signal (SIGILL, SIG_DFL); signal (SIGSEGV, SIG_DFL); - signal (SIGFPE, SIG_DFL); + signal (SIGFPE, SIG_DFL); } } } +static void +hook_signlas (void) +{ + // catch signals + signal (SIGINT, signal_handler); + signal (SIGILL, signal_handler); + signal (SIGSEGV, signal_handler); + signal (SIGTERM, signal_handler); + signal (SIGFPE, signal_handler); +} +#else + +static __thread struct sigaction save_hup; +static __thread struct sigaction save_quit; +static __thread struct sigaction save_trap; +static __thread struct sigaction save_iot; +static __thread struct sigaction save_bus; +static __thread struct sigaction save_int; +static __thread struct sigaction save_ill; +static __thread struct sigaction save_segv; +static __thread struct sigaction save_term; +static __thread struct sigaction save_fpe; + +static void +signal_handler (int sig, siginfo_t *info, void *ucontext) +{ + int volatile recover = 0; // volatile for longjump + static volatile int in_signal_handler = 0; + + if (in_signal_handler) { + aiee (sig); + } + printf ("Received signal %d, exiting...\n", sig); + + switch (sig) { + case SIGINT: + case SIGTERM: + case SIGHUP: + case SIGQUIT: + sigaction (SIGHUP, &save_hup, 0); + sigaction (SIGINT, &save_int, 0); + sigaction (SIGTERM, &save_term, 0); + sigaction (SIGQUIT, &save_quit, 0); + exit(1); + default: + if (!sigsetjmp (aiee_abort, 1)) { + if (signal_hook) + recover = signal_hook (sig, signal_hook_data); + Sys_Shutdown (); + } + + if (!recover) { + sigaction (SIGTRAP, &save_trap, 0); + sigaction (SIGIOT, &save_iot, 0); + sigaction (SIGBUS, &save_bus, 0); + sigaction (SIGILL, &save_ill, 0); + sigaction (SIGSEGV, &save_segv, 0); + sigaction (SIGFPE, &save_fpe, 0); + } + } +} + +static void +hook_signlas (void) +{ + // catch signals + struct sigaction action = {}; + action.sa_flags = SA_SIGINFO; + action.sa_sigaction = signal_handler; +#ifndef _WIN32 + sigaction (SIGHUP, &action, &save_hup); + sigaction (SIGQUIT, &action, &save_quit); + sigaction (SIGTRAP, &action, &save_trap); + sigaction (SIGIOT, &action, &save_iot); + sigaction (SIGBUS, &action, &save_bus); +#endif + sigaction (SIGINT, &action, &save_int); + sigaction (SIGILL, &action, &save_ill); + sigaction (SIGSEGV, &action, &save_segv); + sigaction (SIGTERM, &action, &save_term); + sigaction (SIGFPE, &action, &save_fpe); +} +#endif + VISIBLE void Sys_Init (void) { - // catch signals -#ifndef _WIN32 - signal (SIGHUP, signal_handler); - signal (SIGQUIT, signal_handler); - signal (SIGTRAP, signal_handler); - signal (SIGIOT, signal_handler); - signal (SIGBUS, signal_handler); -#endif - signal (SIGINT, signal_handler); - signal (SIGILL, signal_handler); - signal (SIGSEGV, signal_handler); - signal (SIGTERM, signal_handler); - signal (SIGFPE, signal_handler); + hook_signlas (); Cvar_Init_Hash (); Cmd_Init_Hash (); @@ -950,3 +1189,71 @@ Sys_ExpandSquiggle (const char *path) home = "."; return nva ("%s%s", home, path + 1); // skip leading ~ } + +VISIBLE int +Sys_UniqueFile (dstring_t *name, const char *prefix, const char *suffix, + int mindigits) +{ + const int flags = O_CREAT | O_EXCL | O_RDWR; + const int mode = 0644; + int64_t seq = 0; // it should take a while to run out + + if (!suffix) { + suffix = ""; + } + while (1) { + dsprintf (name, "%s%0*"PRIi64"%s", prefix, mindigits, seq, suffix); + int fd = open (name->str, flags, mode); + if (fd >= 0) { + return fd; + } + int err = errno; + if (err != EEXIST) { + dsprintf (name, "%s", strerror (err)); + return -err; + } + seq++; + } +} + +void +Sys_Shutdown (void) +{ + shutdown_list_t *t; + + while (shutdown_list) { + void (*func) (void *) = shutdown_list->func; + void *data = shutdown_list->data; + t = shutdown_list; + shutdown_list = shutdown_list->next; + free (t); + + func (data); + } + while (free_sh) { + __auto_type t = free_sh->next; + free (free_sh); + free_sh = t; + } + while (sh_stack) { + __auto_type t = sh_stack->next; + free (sh_stack); + sh_stack = t; + } + while (error_handler_freelist) { + __auto_type t = error_handler_freelist->next; + free (error_handler_freelist); + error_handler_freelist = t; + } + while (error_handler) { + __auto_type t = error_handler->next; + free (error_handler); + error_handler = t; + } + if (sys_print_msg) { + dstring_delete (sys_print_msg); + } + if (sys_debuglog_data) { + dstring_delete (sys_debuglog_data); + } +} diff --git a/libs/util/sys_ia32.S b/libs/util/sys_ia32.S index 65d7b7388..f993d945b 100644 --- a/libs/util/sys_ia32.S +++ b/libs/util/sys_ia32.S @@ -85,3 +85,7 @@ F_BEGIN(Sys_MaskFPUExceptions) ret F_END(Sys_MaskFPUExceptions) #endif /* USE_INTEL_ASM */ + +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/libs/util/test/Makefile.am b/libs/util/test/Makefile.am deleted file mode 100644 index a7bdb5d4e..000000000 --- a/libs/util/test/Makefile.am +++ /dev/null @@ -1,61 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CPPFLAGS= -I$(top_srcdir)/include - -check_PROGRAMS= \ - test-bary test-cs test-dq test-half test-mat3 test-mat4 test-plist \ - test-qfs test-quat test-seb test-seg test-set test-vrect - -test_bary_SOURCES=test-bary.c -test_bary_LDADD=$(top_builddir)/libs/util/libQFutil.la -test_bary_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la - -test_cs_SOURCES=test-cs.c -test_cs_LDADD=$(top_builddir)/libs/util/libQFutil.la -test_cs_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la - -test_dq_SOURCES=test-dq.c -test_dq_LDADD=$(top_builddir)/libs/util/libQFutil.la -test_dq_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la - -test_half_SOURCES=test-half.c -test_half_LDADD=$(top_builddir)/libs/util/libQFutil.la -test_half_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la - -test_mat3_SOURCES=test-mat3.c -test_mat3_LDADD=$(top_builddir)/libs/util/libQFutil.la -test_mat3_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la - -test_mat4_SOURCES=test-mat4.c -test_mat4_LDADD=$(top_builddir)/libs/util/libQFutil.la -test_mat4_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la - -test_plist_SOURCES=test-plist.c -test_plist_LDADD=$(top_builddir)/libs/util/libQFutil.la -test_plist_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la - -test_qfs_SOURCES=test-qfs.c -test_qfs_LDADD=$(top_builddir)/libs/util/libQFutil.la -test_qfs_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la - -test_quat_SOURCES=test-quat.c -test_quat_LDADD=$(top_builddir)/libs/util/libQFutil.la -test_quat_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la - -test_seb_SOURCES=test-seb.c -test_seb_LDADD=$(top_builddir)/libs/util/libQFutil.la -test_seb_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la - -test_seg_SOURCES=test-seg.c -test_seg_LDADD=$(top_builddir)/libs/util/libQFutil.la -test_seg_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la - -test_set_SOURCES=test-set.c -test_set_LDADD=$(top_builddir)/libs/util/libQFutil.la -test_set_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la - -test_vrect_SOURCES=test-vrect.c -test_vrect_LDADD=$(top_builddir)/libs/util/libQFutil.la -test_vrect_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la - -TESTS=$(check_PROGRAMS) diff --git a/libs/util/test/Makemodule.am b/libs/util/test/Makemodule.am new file mode 100644 index 000000000..64cc8b9e4 --- /dev/null +++ b/libs/util/test/Makemodule.am @@ -0,0 +1,141 @@ +libs_util_tests = \ + libs/util/test/test-bary \ + libs/util/test/test-baryvf \ + libs/util/test/test-bitop \ + libs/util/test/test-bsearch \ + libs/util/test/test-cexpr \ + libs/util/test/test-cmem \ + libs/util/test/test-cs \ + libs/util/test/test-csvf \ + libs/util/test/test-darray \ + libs/util/test/test-dq \ + libs/util/test/test-half \ + libs/util/test/test-heapsort \ + libs/util/test/test-listener \ + libs/util/test/test-mat3 \ + libs/util/test/test-mat4 \ + libs/util/test/test-plist \ + libs/util/test/test-pqueue \ + libs/util/test/test-qfs \ + libs/util/test/test-quat \ + libs/util/test/test-ringbuffer \ + libs/util/test/test-seb \ + libs/util/test/test-sebvf \ + libs/util/test/test-seg \ + libs/util/test/test-set \ + libs/util/test/test-simd \ + libs/util/test/test-utf8 \ + libs/util/test/test-zone + +TESTS += $(libs_util_tests) + +check_PROGRAMS += $(libs_util_tests) + +libs_util_test_test_bary_SOURCES=libs/util/test/test-bary.c +libs_util_test_test_bary_LDADD=libs/util/libQFutil.la +libs_util_test_test_bary_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_baryvf_SOURCES=libs/util/test/test-baryvf.c +libs_util_test_test_baryvf_LDADD=libs/util/libQFutil.la +libs_util_test_test_baryvf_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_bitop_SOURCES=libs/util/test/test-bitop.c +libs_util_test_test_bitop_LDADD=libs/util/libQFutil.la +libs_util_test_test_bitop_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_bsearch_SOURCES=libs/util/test/test-bsearch.c +libs_util_test_test_bsearch_LDADD=libs/util/libQFutil.la +libs_util_test_test_bsearch_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_cexpr_SOURCES=libs/util/test/test-cexpr.c +libs_util_test_test_cexpr_LDADD=libs/util/libQFutil.la +libs_util_test_test_cexpr_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_cmem_SOURCES=libs/util/test/test-cmem.c +libs_util_test_test_cmem_LDADD=libs/util/libQFutil.la +libs_util_test_test_cmem_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_cs_SOURCES=libs/util/test/test-cs.c +libs_util_test_test_cs_LDADD=libs/util/libQFutil.la +libs_util_test_test_cs_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_csvf_SOURCES=libs/util/test/test-csvf.c +libs_util_test_test_csvf_LDADD=libs/util/libQFutil.la +libs_util_test_test_csvf_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_darray_SOURCES=libs/util/test/test-darray.c +libs_util_test_test_darray_LDADD=libs/util/libQFutil.la +libs_util_test_test_darray_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_dq_SOURCES=libs/util/test/test-dq.c +libs_util_test_test_dq_LDADD=libs/util/libQFutil.la +libs_util_test_test_dq_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_half_SOURCES=libs/util/test/test-half.c +libs_util_test_test_half_LDADD=libs/util/libQFutil.la +libs_util_test_test_half_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_heapsort_SOURCES=libs/util/test/test-heapsort.c +libs_util_test_test_heapsort_LDADD=libs/util/libQFutil.la +libs_util_test_test_heapsort_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_listener_SOURCES=libs/util/test/test-listener.c +libs_util_test_test_listener_LDADD=libs/util/libQFutil.la +libs_util_test_test_listener_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_mat3_SOURCES=libs/util/test/test-mat3.c +libs_util_test_test_mat3_LDADD=libs/util/libQFutil.la +libs_util_test_test_mat3_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_mat4_SOURCES=libs/util/test/test-mat4.c +libs_util_test_test_mat4_LDADD=libs/util/libQFutil.la +libs_util_test_test_mat4_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_plist_SOURCES=libs/util/test/test-plist.c +libs_util_test_test_plist_LDADD=libs/util/libQFutil.la +libs_util_test_test_plist_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_pqueue_SOURCES=libs/util/test/test-pqueue.c +libs_util_test_test_pqueue_LDADD=libs/util/libQFutil.la +libs_util_test_test_pqueue_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_qfs_SOURCES=libs/util/test/test-qfs.c +libs_util_test_test_qfs_LDADD=libs/util/libQFutil.la +libs_util_test_test_qfs_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_quat_SOURCES=libs/util/test/test-quat.c +libs_util_test_test_quat_LDADD=libs/util/libQFutil.la +libs_util_test_test_quat_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_ringbuffer_SOURCES=libs/util/test/test-ringbuffer.c +libs_util_test_test_ringbuffer_LDADD=libs/util/libQFutil.la +libs_util_test_test_ringbuffer_LDFLAGS=-pthread +libs_util_test_test_ringbuffer_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_seb_SOURCES=libs/util/test/test-seb.c +libs_util_test_test_seb_LDADD=libs/util/libQFutil.la +libs_util_test_test_seb_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_sebvf_SOURCES=libs/util/test/test-sebvf.c +libs_util_test_test_sebvf_LDADD=libs/util/libQFutil.la +libs_util_test_test_sebvf_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_seg_SOURCES=libs/util/test/test-seg.c +libs_util_test_test_seg_LDADD=libs/util/libQFutil.la +libs_util_test_test_seg_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_set_SOURCES=libs/util/test/test-set.c +libs_util_test_test_set_LDADD=libs/util/libQFutil.la +libs_util_test_test_set_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_simd_SOURCES=libs/util/test/test-simd.c +libs_util_test_test_simd_LDADD=libs/util/libQFutil.la +libs_util_test_test_simd_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_utf8_SOURCES=libs/util/test/test-utf8.c +libs_util_test_test_utf8_LDADD=libs/util/libQFutil.la +libs_util_test_test_utf8_DEPENDENCIES=libs/util/libQFutil.la + +libs_util_test_test_zone_SOURCES=libs/util/test/test-zone.c +libs_util_test_test_zone_LDADD=libs/util/libQFutil.la +libs_util_test_test_zone_DEPENDENCIES=libs/util/libQFutil.la diff --git a/libs/util/test/test-bary.c b/libs/util/test/test-bary.c index fdb712642..676b0038b 100644 --- a/libs/util/test/test-bary.c +++ b/libs/util/test/test-bary.c @@ -35,8 +35,8 @@ struct { {tri, 3, points[0], {1, 0, 0}}, {tri, 3, points[1], {0, 1, 0}}, {tri, 3, points[2], {0, 0, 1}}, - {tri, 3, points[3], {0.333333284, 0.333333333, 0.333333333}},//rounding :P - {tri, 3, points[8], {0.333333284, 0.333333333, 0.333333333}},//rounding :P + {tri, 3, points[3], {0.333333333, 0.333333333, 0.333333333}}, + {tri, 3, points[8], {0.333333333, 0.333333333, 0.333333333}}, {tetra, 4, points[0], {1, 0, 0, 0}}, {tetra, 4, points[1], {0, 1, 0, 0}}, {tetra, 4, points[2], {0, 0, 1, 0}}, diff --git a/libs/util/test/test-baryvf.c b/libs/util/test/test-baryvf.c new file mode 100644 index 000000000..9dac89c01 --- /dev/null +++ b/libs/util/test/test-baryvf.c @@ -0,0 +1,118 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/mathlib.h" +#include "QF/mersenne.h" +#include "QF/sys.h" +#include "QF/simd/vec4f.h" + +vec4f_t points[] = { + {-1, -1, 1}, + { 1, 1, 1}, + {-1, 1, -1}, + { 1, -1, -1}, + {-1, -1, -1}, + { 1, 1, -1}, + {-1, 1, 1}, + { 1, -1, 1}, + { 0, 0, 0}, +}; +const vec4f_t *line[] = {&points[0], &points[1]}; +const vec4f_t *tri[] = {&points[0], &points[1], &points[2]}; +const vec4f_t *tetra[] = {&points[0], &points[1], &points[2], &points[3]}; + +struct { + const vec4f_t **points; + int num_points; + vec4f_t *x; + vec_t expect[4]; +} tests[] = { + {line, 2, &points[0], {1, 0}}, + {line, 2, &points[1], {0, 1}}, + {line, 2, &points[2], {0.5, 0.5}}, + {line, 2, &points[3], {0.5, 0.5}}, + {line, 2, &points[8], {0.5, 0.5}}, + {tri, 3, &points[0], {1, 0, 0}}, + {tri, 3, &points[1], {0, 1, 0}}, + {tri, 3, &points[2], {0, 0, 1}}, + {tri, 3, &points[3], {0.333333333, 0.333333333, 0.333333333}}, + {tri, 3, &points[8], {0.333333333, 0.333333333, 0.333333333}}, + {tetra, 4, &points[0], {1, 0, 0, 0}}, + {tetra, 4, &points[1], {0, 1, 0, 0}}, + {tetra, 4, &points[2], {0, 0, 1, 0}}, + {tetra, 4, &points[3], {0, 0, 0, 1}}, + {tetra, 4, &points[4], { 0.5, -0.5, 0.5, 0.5}}, + {tetra, 4, &points[5], {-0.5, 0.5, 0.5, 0.5}}, + {tetra, 4, &points[6], { 0.5, 0.5, 0.5, -0.5}}, + {tetra, 4, &points[7], { 0.5, 0.5, -0.5, 0.5}}, + {tetra, 4, &points[8], {0.25, 0.25, 0.25, 0.25}}, +}; +#define num_tests (sizeof (tests) / sizeof (tests[0])) + +static inline float +rnd (mtstate_t *mt) +{ + union { + uint32_t u; + float f; + } uf; + + do { + uf.u = mtwist_rand (mt) & 0x007fffff; + } while (!uf.u); + uf.u |= 0x40000000; + + return uf.f - 3.0; +} + +int +main (int argc, const char **argv) +{ + int res = 0; + size_t i; + int j; + vec4f_t lambda; + mtstate_t mt; + double start, end; + + for (i = 0; i < num_tests; i ++) { + lambda = BarycentricCoords_vf (tests[i].points, tests[i].num_points, *tests[i].x); + for (j = 0; j < tests[i].num_points; j++) { + if (tests[i].expect[j] != lambda[j]) + break; + } + if (j != tests[i].num_points) { + res = 1; + printf ("test %d failed\n", (int) i); + printf ("expect:"); + for (j = 0; j < tests[i].num_points; j++) + printf (" %.9g", tests[i].expect[j]); + printf ("\ngot :"); + for (j = 0; j < tests[i].num_points; j++) + printf (" %.9g", lambda[j]); + printf ("\n"); + } + } + + mtwist_seed (&mt, 0); + start = Sys_DoubleTime (); + for (i = 0; i < 1000000; i++) { + vec4f_t p = { rnd (&mt), rnd (&mt), rnd (&mt) }; + vec4f_t x = {}; + lambda = BarycentricCoords_vf (tetra, 4, p); + for (j = 0; j < 4; j++) { + x = x + lambda[j] * *tetra[j]; + } + if (VectorDistance_fast (x, p) > 1e-4) { + res = 1; + printf ("[%.9g %.9g %.9g] != [%.9g %.9g %.9g]: [%g %g %g %g]\n", + VectorExpand (x), VectorExpand (p), QuatExpand (lambda)); + break; + } + } + end = Sys_DoubleTime (); + printf ("%d itterations in %gs: %g itters/second\n", (int) i, end - start, + i / (end - start)); + return res; +} diff --git a/libs/util/test/test-bitop.c b/libs/util/test/test-bitop.c new file mode 100644 index 000000000..795d023f5 --- /dev/null +++ b/libs/util/test/test-bitop.c @@ -0,0 +1,55 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#include "QF/math/bitop.h" + +struct { + uint32_t value; + uint32_t expect; +} tests [] = { + {BITOP_RUP(1), 1}, + {BITOP_RUP(2), 2}, + {BITOP_RUP(3), 4}, + {BITOP_RUP(4), 4}, + {BITOP_RUP(5), 8}, + {BITOP_RUP(7), 8}, + {BITOP_RUP(8), 8}, + {BITOP_RUP(0x40000000), 0x40000000}, + {BITOP_RUP(0x40000001), 0x80000000}, + {BITOP_LOG2(1), 0}, + {BITOP_LOG2(2), 1}, + {BITOP_LOG2(3), 2}, + {BITOP_LOG2(4), 2}, + {BITOP_LOG2(5), 3}, + {BITOP_LOG2(7), 3}, + {BITOP_LOG2(8), 3}, + {BITOP_LOG2(9), 4}, + {BITOP_LOG2(15), 4}, + {BITOP_LOG2(16), 4}, + {BITOP_LOG2(17), 5}, + {BITOP_LOG2(31), 5}, + {BITOP_LOG2(32), 5}, + {BITOP_LOG2(33), 6}, + {BITOP_LOG2(0x40000000), 30}, + {BITOP_LOG2(0x40000001), 31}, +}; +#define num_tests (sizeof (tests) / sizeof (tests[0])) + +int +main (int argc, const char **argv) +{ + size_t i; + int res = 0; + + for (i = 0; i < num_tests; i++) { + if (tests[i].value != tests[i].expect) { + res |= 1; + printf ("test %d failed\n", (int) i); + printf ("expect: %8x\n", tests[i].expect); + printf ("got : %8x\n", tests[i].value); + } + } + return res; +} diff --git a/libs/util/test/test-bsearch.c b/libs/util/test/test-bsearch.c new file mode 100644 index 000000000..bbe1a81bc --- /dev/null +++ b/libs/util/test/test-bsearch.c @@ -0,0 +1,123 @@ +/* + test-bsearch.c + + Fuzzy and reentrant bsearch + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "QF/fbsearch.h" +#include "bsearch.h" + +// will be prefix-summed +int srcdata[] = { + 2, 1, 3, 1, 2, 2, 5, 4, + 2, 1, 3, 1, 2, 2, 5, 4, +}; +#define nele ((int) (sizeof (srcdata) / sizeof (srcdata[0]))) +int *data; + +static int +compare (const void *a, const void *b) +{ + return *(const int *)a - *(const int *)b; +} + +static int +compared (const void *a, const void *b, void *d) +{ + return *(const int *)a - *(const int *)b; +} + +static int +fuzzy_check (const int *p, int val, const char *func) +{ + if (val < data[0] && p) { + printf ("%s found %d, but should not\n", func, val); + return 0; + } + if (p) { + int ok = p < data + nele - 1; + if (p >= data + nele) { + printf ("%s went out of bounts: %zd\n", func, p - data); + return 0; + } + if (val < *p || (ok && val >= p[1])) { + printf ("%s found %d:%d for %d\n", func, *p, ok ? p[1] : -1, val); + return 0; + } + } + if (!p && val >= data[0]) { + printf ("%s did not find %d, but should have\n", func, val); + return 0; + } + return 1; +} + +int +main(int argc, const char **argv) +{ + int ret = 0; + int *p; + + data = malloc (nele * sizeof (int)); + memcpy (data, srcdata, nele * sizeof (int)); + + for (int i = 1; i < nele; i++) { + data[i] += data[i - 1]; + } + for (int i = 0; i < nele; i++) { + printf ("%2d %d\n", i, data[i]); + } + for (int i = 0; i < data[nele - 1] + 2; i++) { + p = fbsearch (&i, data, nele, sizeof (int), compare); + if (!fuzzy_check (p, i, "fbsearch")) { + ret |= 1; + } + p = fbsearch_r (&i, data, nele, sizeof (int), compared, 0); + if (!fuzzy_check (p, i, "fbsearch_r")) { + ret |= 1; + } + if (p && i == *p) { + p = QF_bsearch_r (&i, data, nele, sizeof (int), compared, 0); + if (!p || *p != i) { + printf ("QF_bsearch_r did not find %d, but should have\n", i); + ret |= 1; + } + } else { + p = QF_bsearch_r (&i, data, nele, sizeof (int), compared, 0); + if (p) { + printf ("QF_bsearch_r found %d, but should not have\n", i); + ret |= 1; + } + } + } + free (data); + return ret; +} diff --git a/libs/util/test/test-cexpr.c b/libs/util/test/test-cexpr.c new file mode 100644 index 000000000..5b3de9875 --- /dev/null +++ b/libs/util/test/test-cexpr.c @@ -0,0 +1,182 @@ +/* + test-cexpr.c + + Config expression parser. Or concurrent. + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/cexpr.h" +#include "QF/cmem.h" +#include "QF/hash.h" +#include "QF/mathlib.h" +#include "QF/va.h" +#include "QF/simd/vec4f.h" + +int a = 5; +int b = 6; +int c; +int array[4] = { 9, 16, 25, 36 }; +vec4f_t point = { 2, 3, 4, 1 }; // a point, so w = 1 +vec4f_t normal = { 1, 2, 3, 0 }; // a vector, so w = 0 +vec4f_t direction = { 4, 5, 6, 0 }; // a vector, so w = 0 +vec4f_t plane; +vec4f_t intercept; + +exprarray_t int_array_4_data = { + &cexpr_int, + sizeof (array) / sizeof (array[0]), +}; +exprtype_t int_array_4 = { + .name = "int[4]", + .size = 4 * sizeof (int), + .binops = cexpr_array_binops, + .unops = 0, + .data = &int_array_4_data, +}; + +exprsym_t symbols[] = { + { "a", &cexpr_int, &a }, + { "b", &cexpr_int, &b }, + { "array", &int_array_4, &array }, + { "point", &cexpr_vector, &point }, + { "normal", &cexpr_vector, &normal }, + { "plane", &cexpr_vector, &plane }, + { "direction", &cexpr_vector, &direction }, + { "intercept", &cexpr_vector, &intercept }, + {} +}; +exprval_t test_result = { &cexpr_int, &c }; +exprval_t plane_result = { &cexpr_vector, &plane }; +// a bit hacky, but no l-values +exprval_t dist_result = { &cexpr_float, (float *)&plane + 3 }; +exprval_t intercept_result = { &cexpr_vector, &intercept }; + +exprtab_t root_symtab = { + .symbols = cexpr_lib_symbols, +}; +exprtab_t symtab = { + .symbols = symbols, +}; + +exprctx_t root_context = { + .symtab = &root_symtab +}; +exprctx_t context = { + .parent = &root_context, + .result = &test_result, + .symtab = &symtab +}; + +#define TEST_BINOP(op) \ + do { \ + c = -4096; \ + context.result = &test_result; \ + cexpr_eval_string ("a " #op " b", &context); \ + printf ("c = a %s b -> %d = %d %s %d\n", #op, c, a, #op, b); \ + if (c != (a op b)) { \ + ret |= 1; \ + } \ + } while (0) + +#define TEST_ARRAY(ind) \ + do { \ + c = -4096; \ + context.result = &test_result; \ + cexpr_eval_string (va (0, "array[%d]", ind), &context); \ + printf ("c = array[%d] -> %d = %d\n", ind, c, array[ind]); \ + if (c != array[ind]) { \ + ret |= 1; \ + } \ + } while (0) + +int +main(int argc, const char **argv) +{ + int ret = 0; + + cexpr_init_symtab (&root_symtab, &context); + cexpr_init_symtab (&symtab, &context); + context.memsuper = new_memsuper(); + + TEST_BINOP (<<); + TEST_BINOP (>>); + TEST_BINOP (+); + TEST_BINOP (-); + TEST_BINOP (*); + TEST_BINOP (/); + TEST_BINOP (&); + TEST_BINOP (|); + TEST_BINOP (^); + TEST_BINOP (%); + + TEST_ARRAY (0); + TEST_ARRAY (1); + TEST_ARRAY (2); + TEST_ARRAY (3); + + context.result = &plane_result; + cexpr_eval_string ("point.wzyx", &context); + if (plane[0] != point[3] || plane[1] != point[2] + || plane[2] != point[1] || plane[3] != point[0]) { + printf ("point.wzyx [%.9g, %.9g, %.9g] %.9g\n", + VectorExpand (plane), plane[3]); + ret |= 1; + } + cexpr_eval_string ("point.zyx", &context); + if (plane[0] != point[2] || plane[1] != point[1] + || plane[2] != point[0] || plane[3] != 0) { + printf ("point.zyx [%.9g, %.9g, %.9g] %.9g\n", + VectorExpand (plane), plane[3]); + ret |= 1; + } + cexpr_eval_string ("point.yx", &context); + if (plane[0] != point[1] || plane[1] != point[0] + || plane[2] != 0 || plane[3] != 0) { + printf ("point.yx [%.9g, %.9g, %.9g] %.9g\n", + VectorExpand (plane), plane[3]); + ret |= 1; + } + cexpr_eval_string ("normal", &context); + context.result = &dist_result; + cexpr_eval_string ("-dot(point, normal).x / 2f", &context); + if (!VectorCompare (normal, plane) + || plane[3] != -DotProduct (normal, point) / 2) { + printf ("plane [%.9g, %.9g, %.9g] %.9g\n", + VectorExpand (plane), plane[3]); + ret |= 1; + } + context.result = &intercept_result; + cexpr_eval_string ("point - direction * dot(point, plane)/dot(direction,plane)", &context); + if (intercept[0] != 0.75 || intercept[1] != 1.4375 + || intercept[2] != 2.125 || intercept[3] != 1) { + printf ("[%.9g, %.9g, %.9g] %.9g\n", VectorExpand (intercept), intercept[3]); + ret |= 1; + } + + Hash_DelTable (symtab.tab); + delete_memsuper (context.memsuper); + return ret; +} diff --git a/libs/util/test/test-cmem.c b/libs/util/test/test-cmem.c new file mode 100644 index 000000000..d5001a8db --- /dev/null +++ b/libs/util/test/test-cmem.c @@ -0,0 +1,682 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include + +#include "QF/cmem.h" +#include "QF/set.h" +#include "QF/sys.h" + +#define SUPER_LINES (sizeof (memsuper_t) / MEM_LINE_SIZE) + +static int +test_block (memsuper_t *super) +{ + size_t size = super->page_size; + void *mem = cmemalloc (super, size); + memblock_t *block; + + if (!mem) { + fprintf (stderr, "could not allocate %zd byte block\n", + super->page_size); + return 0; + } + if ((size_t) mem & super->page_mask) { + fprintf (stderr, "mem not page aligned: %p %zd\n", + mem, super->page_size); + return 0; + } + block = super->memblocks; + if ((size_t) mem != (size_t) block + super->page_size) { + fprintf (stderr, "super does not point to mem\n"); + return 0; + } + if (block->post_size < size) { + fprintf (stderr, "block post_size too small: %zd < %zd\n", + block->post_size, size); + return 0; + } + if (block->post_size - size >= super->page_size) { + fprintf (stderr, "block post_size too big: %zd < %zd\n", + block->post_size - size, super->page_size); + return 0; + } + memset (mem, 0, size); // valgrind check + cmemfree (super, mem); + if ((size_t) super->memblocks + super->page_size == (size_t) mem) { + fprintf (stderr, "super still points to mem\n"); + return 0; + } + return 1; +} + +static int +check_block (memblock_t *block, int line_count, int allocated) +{ + set_t *visited = set_new (); + size_t free_bytes = 0; + int ret = 1; + int count = 0; + + for (memline_t **l = &block->free_lines; *l; l = &(*l)->block_next) { + memline_t *line = *l; + ptrdiff_t ind = (byte *) line - (byte *) block; + ind /= MEM_LINE_SIZE; + if (ind < 1 || (size_t ) (ind - 1) > block->size / MEM_LINE_SIZE) { + fprintf (stderr, "line outside of block: %p %p\n", + line, block); + return 0; + } + if (set_is_member (visited, ind)) { + fprintf (stderr, "loop in block free_lines\n"); + return 0; + } + count++; + set_add (visited, ind); + if (!line->size) { + fprintf (stderr, "line with size 0\n"); + ret = 0; + } + if (line->block_next && line->block_next < line) { + fprintf (stderr, "line link in wrong direction\n"); + ret = 0; + } + if ((size_t) line + line->size == (size_t) line->block_next) { + fprintf (stderr, "adjacant free line blocks\n"); + ret = 0; + } + if (line->block_prev != l) { + fprintf (stderr, "line block_prev link incorrect\n"); + ret = 0; + } + if (line->size > block->size) { + fprintf (stderr, "line size too large: %zd / %zd\n", + line->size, block->size); + ret = 0; + } + if (line->size % MEM_LINE_SIZE) { + fprintf (stderr, "bad line size: %zd / %zd\n", + line->size, line->size % MEM_LINE_SIZE); + ret = 0; + } + if (ret) { + free_bytes += line->size; + } + } + if (ret) { + if (free_bytes + block->allocated != block->size) { + fprintf (stderr, "block space mismatch: s: %zd a: %zd f: %zd\n", + block->size, block->allocated, free_bytes); + ret = 0; + } + if (line_count >= 0 && line_count != count) { + fprintf (stderr, "incorrect number of lines: e: %d g: %d\n", + line_count, count); + ret = 0; + } + if (allocated >= 0 && (size_t) allocated != block->allocated) { + fprintf (stderr, "allocated wrong size: %zd != %d\n", + block->allocated, allocated); + } + } + set_delete (visited); + return ret; +} + +static int __attribute__ ((pure)) +check_for_loop (memline_t *line, memline_t **stop) +{ + memline_t *next = line->free_next; + + for (memline_t **l = line->free_prev; l != stop; + l = line->free_prev) { + line = (memline_t *) l; + if (line == next) { + return 1; + } + } + return 0; +} + +static int +check_bins (memsuper_t *super, int mask) +{ + int ret = 1; + for (int i = MAX_CACHE_LINES; i-- > 0; ) { + if (mask >= 0) { + if (mask & (1 << i)) { + if (!super->free_lines[i]) { + fprintf (stderr, "super free_lines[%d] is empty\n", i); + ret = 0; + } + } else { + if (super->free_lines[i]) { + fprintf (stderr, "super free_lines[%d] is occupied\n", i); + ret = 0; + } + } + } + for (memline_t **l = &super->free_lines[i]; *l; l = &(*l)->free_next) { + memline_t *line = *l; + if (line->free_prev != l) { + fprintf (stderr, "super free_lines[%d] has bad prev\n", i); + ret = 0; + break; + } + if (check_for_loop (line, &super->free_lines[i])) { + fprintf (stderr, "super free_lines[%d] loop detected\n", i); + ret = 0; + break; + } + if ((MEM_LINE_SIZE << i) > (int) line->size + || (MEM_LINE_SIZE << (i + 1)) <= (int) line->size) { + fprintf (stderr, "line in wrong i: %d %d %zd\n", + i, MEM_LINE_SIZE << i, line->size); + ret = 0; + } + } + } + return ret; +} + +static int +test_line (memsuper_t *super) +{ + memline_t *line1 = cmemalloc (super, MEM_LINE_SIZE); + memline_t *line2 = cmemalloc (super, MEM_LINE_SIZE); + memline_t *line3 = cmemalloc (super, MEM_LINE_SIZE); + memblock_t *block = super->memblocks; + + if (block->next) { + fprintf (stderr, "too many memblocks\n"); + return 0; + } + if (line1 < (memline_t *) (block + 1) + || line1 >= (memline_t *) ((byte *) block + super->page_size)) { + fprintf (stderr, "line1 outside block line pool: %p %p\n", + line1, block); + return 0; + } + if (line2 < (memline_t *) (block + 1) + || line2 >= (memline_t *) ((byte *) block + super->page_size)) { + fprintf (stderr, "line2 outside block line pool: %p %p\n", + line2, block); + return 0; + } + if (line3 < (memline_t *) (block + 1) + || line3 >= (memline_t *) ((byte *) block + super->page_size)) { + fprintf (stderr, "line3 outside block line pool: %p %p\n", + line3, block); + return 0; + } + if (!((size_t) line1 & super->page_mask)) { + fprintf (stderr, "line1 is page aligned\n"); + return 0; + } + if (!((size_t) line2 & super->page_mask)) { + fprintf (stderr, "line2 is page aligned\n"); + return 0; + } + if (!((size_t) line3 & super->page_mask)) { + fprintf (stderr, "line3 is page aligned\n"); + return 0; + } + if (line1 + 1 != line2 || line2 + 1 != line3) { + fprintf (stderr, "lines not contiguous\n"); + return 0; + } + if (line3 + 1 != block->free_lines) { + fprintf (stderr, "line3 not contiguous with free lines\n"); + return 0; + } + + if (!check_block (block, 1, (3 + SUPER_LINES) * MEM_LINE_SIZE)) { + fprintf (stderr, "line block check 1 failed\n"); + return 0; + } + if (!check_bins (super, 0x20)) { + fprintf (stderr, "bin check 1 failed\n"); + return 0; + } + + cmemfree (super, line2); + + if (!check_block (block, 2, (2 + SUPER_LINES) * MEM_LINE_SIZE)) { + fprintf (stderr, "line block check 2 failed\n"); + return 0; + } + if (!check_bins (super, 0x21)) { + fprintf (stderr, "bin check 2 failed\n"); + return 0; + } + + if (block->free_lines != line2) { + fprintf (stderr, "block free_lines not pointing to line2\n"); + return 0; + } + if (super->free_lines[0] != line2) { + fprintf (stderr, "super free_lines[0] not pointing to line2\n"); + return 0; + } + + cmemfree (super, line3); + if (!check_block (block, 1, (1 + SUPER_LINES) * MEM_LINE_SIZE)) { + fprintf (stderr, "line block check 3 failed\n"); + return 0; + } + if (!check_bins (super, 0x20)) { + fprintf (stderr, "bin check 3 failed\n"); + return 0; + } + + if (block->free_lines != line2) { + fprintf (stderr, "free lines not pointing to line2 2\n"); + return 0; + } + + cmemfree (super, line1); + if (!check_block (block, 1, (0 + SUPER_LINES) * MEM_LINE_SIZE)) { + fprintf (stderr, "line block check 4 failed\n"); + return 0; + } + if (!check_bins (super, 0x20)) { + fprintf (stderr, "bin check 4 failed\n"); + return 0; + } + + if (super->free_lines[5] != line1) { + fprintf (stderr, "super free_lines[5] not pointing to line1\n"); + return 0; + } + + line1 = cmemalloc (super, MEM_LINE_SIZE); + line2 = cmemalloc (super, MEM_LINE_SIZE); + line3 = cmemalloc (super, MEM_LINE_SIZE); + block = super->memblocks; + + if (!check_block (block, 1, (3 + SUPER_LINES) * MEM_LINE_SIZE)) { + fprintf (stderr, "line block check 4 failed\n"); + return 0; + } + if (!check_bins (super, 0x20)) { + fprintf (stderr, "bin check 4 failed\n"); + return 0; + } + + cmemfree (super, line1); + + if (!check_block (block, 2, (2 + SUPER_LINES) * MEM_LINE_SIZE)) { + fprintf (stderr, "line block check 5 failed\n"); + return 0; + } + if (!check_bins (super, 0x21)) { + fprintf (stderr, "bin check 5 failed\n"); + return 0; + } + + cmemfree (super, line2); + + if (!check_block (block, 2, (1 + SUPER_LINES) * MEM_LINE_SIZE)) { + fprintf (stderr, "line block check 6 failed\n"); + return 0; + } + if (!check_bins (super, 0x22)) { + fprintf (stderr, "bin check 6 failed\n"); + return 0; + } + + cmemfree (super, line3); + + line1 = cmemalloc (super, MEM_LINE_SIZE); + line2 = cmemalloc (super, MEM_LINE_SIZE); + line3 = cmemalloc (super, MEM_LINE_SIZE); + block = super->memblocks; + + if (!check_block (block, 1, (3 + SUPER_LINES) * MEM_LINE_SIZE)) { + fprintf (stderr, "line block check 7 failed\n"); + return 0; + } + if (!check_bins (super, 0x20)) { + fprintf (stderr, "bin check 7 failed\n"); + return 0; + } + + cmemfree (super, line3); + + if (!check_block (block, 1, (2 + SUPER_LINES) * MEM_LINE_SIZE)) { + fprintf (stderr, "line block check 8 failed\n"); + return 0; + } + if (!check_bins (super, 0x20)) { + fprintf (stderr, "bin check 8 failed\n"); + return 0; + } + + cmemfree (super, line2); + + if (!check_block (block, 1, (1 + SUPER_LINES) * MEM_LINE_SIZE)) { + fprintf (stderr, "line block check 9 failed\n"); + return 0; + } + if (!check_bins (super, 0x20)) { + fprintf (stderr, "bin check 9 failed\n"); + return 0; + } + + cmemfree (super, line1); + + return 1; +} + +typedef struct { + size_t size; + int group_id; +} sline_block_t; + +static sline_block_t group_tests[] = { + { 2, 4}, + { 4, 4}, + { 5, 8}, + { 3, 4}, + { 1, 4}, + { 6, 8}, + { 9, 16}, + { 4, 4}, + { 4, 4}, + { 7, 8}, + { 2, 4}, + { 4, 4}, + { 8, 8}, + {13, 16}, + { 3, 4}, + { 1, 4}, + { 8, 8}, + { 8, 8}, + { 4, 4}, + { 4, 4}, + {16, 16}, + {32, 32}, + {32, 33}, +}; +#define num_group_tests (sizeof (group_tests) / sizeof (group_tests[0])) +#define mask(x) (((size_t) (x)) & ~(MEM_LINE_SIZE - 1)) +#define pagemask(x,o,s) (((size_t) (x)) & (o (s)->page_mask)) + +static int +test_sline (memsuper_t *super) +{ + void *mem[num_group_tests]; + int ret = 1; + + for (size_t i = 0; i < num_group_tests; i++) { + mem[i] = cmemalloc (super, group_tests[i].size); + if (!mem[i]) { + fprintf (stderr, "mem[%zd] is null\n", i); + return 0; + } + if (!((size_t) mem[i] % MEM_LINE_SIZE)) { + fprintf (stderr, "mem[%zd] is aligned with chache line\n", i); + ret = 0; + } + } + for (size_t i = 0; i < num_group_tests; i++) { + for (size_t j = i + 1; j < num_group_tests; j++) { + if (mem[i] == mem[j]) { + fprintf (stderr, "mem[%zd] is dupped with %zd\n", i, j); + ret = 0; + } + if (mask (mem[i]) == mask (mem[j])) { + if (group_tests[i].group_id != group_tests[j].group_id) { + fprintf (stderr, "mem[%zd](%d) is grouped with %zd(%d)\n", + i, group_tests[i].group_id, + j, group_tests[j].group_id); + ret = 0; + } + } else { + if (group_tests[i].group_id == group_tests[j].group_id) { + fprintf (stderr, + "mem[%zd](%d) is not grouped with %zd(%d)\n", + i, group_tests[i].group_id, + j, group_tests[j].group_id); + ret = 0; + } + } + if (pagemask (mem[i], ~, super) != pagemask (mem[j], ~, super)) { + fprintf (stderr, + "mem[%zd](%d) is not block grouped with %zd(%d)\n", + i, group_tests[i].group_id, + j, group_tests[j].group_id); + } + } + } + for (size_t i = 0; i < num_group_tests; i++) { + cmemfree (super, mem[i]); + void *newmem = cmemalloc (super, group_tests[i].size); + if (newmem != mem[i]) { + fprintf (stderr, + "%2zd bytes not recycled (%2zd,%2d) (%06zx %06zx)\n", + group_tests[i].size, i, group_tests[i].group_id, + pagemask (mem[i], ~~, super), + pagemask (newmem, ~~, super)); + ret = 0; + } + if (!((size_t) newmem % MEM_LINE_SIZE)) { + fprintf (stderr, "newmem is aligned with chache line %p\n", + newmem); + ret = 0; + } + } + return ret; +} + +static int +test_block_line (memsuper_t *super) +{ + void *mem = cmemalloc (super, 2 * super->page_size); + void *line; + memblock_t *block = super->memblocks; + + if ((size_t) block + super->page_size != (size_t) mem) { + fprintf (stderr, "super memblocks do not point to mem\n"); + return 0; + } + if (block->size < MEM_LINE_SIZE) { + // need to figure out a way to guarantee a shared block + fprintf (stderr, "can't allocate line from block\n"); + return 0; + } + if (block->next != (memblock_t *) ((size_t) super & ~super->page_mask)) { + fprintf (stderr, "excess blocks in super\n"); + return 0; + } + line = cmemalloc (super, MEM_LINE_SIZE); + if (!((size_t) line & super->page_mask)) { + fprintf (stderr, "line is page aligned\n"); + return 0; + } + if (0 && super->memblocks->next) { + // need to figure out a way to guarantee a shared block + fprintf (stderr, "mem and line not in same block\n"); + return 0; + } + cmemfree (super, mem); + if (!super->memblocks) { + fprintf (stderr, "shared block freed\n"); + return 0; + } + if (cmemalloc (super, super->page_size) != mem) { + fprintf (stderr, "block not reused for mem\n"); + return 0; + } + //if (super->memblocks != block || super->memblocks->next) { + if (super->memblocks != block || !super->memblocks->next + || super->memblocks->next->next) { + // need to figure out a way to guarantee a shared block + fprintf (stderr, "blocks corrupt\n"); + return 0; + } + cmemfree (super, line); + if (!super->memblocks) { + fprintf (stderr, "shared block freed 2\n"); + return 0; + } + cmemfree (super, mem); + if (0 && super->memblocks) { + fprintf (stderr, "shared block not freed\n"); + return 0; + } + return 1; +} + +int +main (void) +{ + memsuper_t *super = new_memsuper (); + int i; +#if __WORDSIZE == 32 + if (sizeof (memsuper_t) != 1 * MEM_LINE_SIZE) { + fprintf (stderr, "memsuper_t not 2 * cache size: %zd\n", + sizeof (memsuper_t)); + return 1; + } +#else + if (sizeof (memsuper_t) != 2 * MEM_LINE_SIZE) { + fprintf (stderr, "memsuper_t not 2 * cache size: %zd\n", + sizeof (memsuper_t)); + return 1; + } +#endif + if (sizeof (memline_t) != MEM_LINE_SIZE) { + fprintf (stderr, "memline_t not cache size: %zd\n", + sizeof (memline_t)); + return 1; + } + if (sizeof (memsline_t) != 2 * sizeof (void *)) { + fprintf (stderr, "memsline_t not two pointers: %zd\n", + sizeof (memsline_t)); + return 1; + } + if (sizeof (memblock_t) != MEM_LINE_SIZE) { + fprintf (stderr, "memblock_t not cache size: %zd\n", + sizeof (memblock_t)); + return 1; + } + if ((size_t) super & (MEM_LINE_SIZE - 1)) { + fprintf (stderr, "super block not cache aligned: %p\n", super); + return 1; + } + if (super->page_size != Sys_PageSize ()) { + fprintf (stderr, "page size not equal to system page size: %zd, %zd\n", + super->page_size, Sys_PageSize ()); + return 1; + } + if (!super->page_size || (super->page_size & (super->page_size - 1))) { + fprintf (stderr, "page size not power of two: %zd\n", + super->page_size); + return 1; + } + if (super->page_mask + 1 != super->page_size) { + fprintf (stderr, "page mask not page size - 1: %zx %zx\n", + super->page_mask, super->page_size); + return 1; + } + if (!super->page_mask || (super->page_mask & (super->page_mask + 1))) { + fprintf (stderr, "page mask not all 1s: %zx\n", + super->page_mask); + return 1; + } + if (super->memblocks + != (memblock_t *) ((size_t) super & ~super->page_mask)) { + fprintf (stderr, "superblock not in block a: %p %p\n", super->memblocks, super); + return 1; + } + for (i = 4; i-- > 0; ) { + if (super->last_freed[i]) { + break; + } + } + if (i >= 0) { + fprintf (stderr, "super last_freed not all null\n"); + return 1; + } +#if 0 // no longer valid + for (i = MAX_CACHE_LINES; i-- > 0; ) { + if (super->free_lines[i]) { + break; + } + } + if (i >= 0) { + fprintf (stderr, "super free_lines not all null\n"); + return 1; + } +#endif + if (!test_block (super)) { + fprintf (stderr, "block tests failed\n"); + return 1; + } + if (super->memblocks + != (memblock_t *) ((size_t) super & ~super->page_mask)) { + fprintf (stderr, "superblock not in block b: %p %p\n", super->memblocks, super); + return 1; + } + for (i = 4; i-- > 0; ) { + if (super->last_freed[i]) { + break; + } + } + if (i >= 0) { + fprintf (stderr, "super last_freed not all null 2\n"); + return 1; + } +#if 0 // no longer valid + for (i = MAX_CACHE_LINES; i-- > 0; ) { + if (super->free_lines[i]) { + break; + } + } + if (i >= 0) { + fprintf (stderr, "super free_lines not all null 2\n"); + return 1; + } +#endif + if (!test_line (super)) { + fprintf (stderr, "line tests failed\n"); + return 1; + } + if (super->memblocks + != (memblock_t *) ((size_t) super & ~super->page_mask)) { + fprintf (stderr, "superblock not in block c: %p %p\n", super->memblocks, super); + return 1; + } + if (!test_block_line (super)) { + fprintf (stderr, "block-line tests failed\n"); + return 1; + } + for (size_t i = 0; i < 2 * super->page_size / MEM_LINE_SIZE; i++) { + void *line = cmemalloc (super, MEM_LINE_SIZE); + if (!line) { + fprintf (stderr, "could not allocate %d byte line\n", + MEM_LINE_SIZE); + return 1; + } + if ((size_t) line % MEM_LINE_SIZE) { + fprintf (stderr, "line not cache-line aligned: %p %d\n", + line, MEM_LINE_SIZE); + return 1; + } + if (!((size_t) line & super->page_mask)) { + fprintf (stderr, "line is page aligned: %p %zd\n", + line, super->page_size); + return 1; + } + } + if (!test_sline (super)) { + fprintf (stderr, "sub-line tests failed\n"); + return 1; + } + delete_memsuper (super); + return 0; +} diff --git a/libs/util/test/test-cs.c b/libs/util/test/test-cs.c index c30e02333..8f5b5b385 100644 --- a/libs/util/test/test-cs.c +++ b/libs/util/test/test-cs.c @@ -18,6 +18,24 @@ const vec3_t points[] = { { 0, 0, 0}, }; +// This particular triangle from ad_tears caused SEB to hit its iteration +// limit because the center in affine hull test failed due to excessivly tight +// epsilon. +// However, the problem isn't here (but good to have a set of tests for it +// anyway). +const vec3_t tears_triangleA[] = { + {2201.82007, -1262, -713.450012}, + {2201.8501, -1262, -713.593994}, +}; +const vec3_t tears_triangleB[] = { + {2201.82007, -1262, -713.450012}, + {2201.84009, -1262, -713.445007}, +}; +const vec3_t tears_triangleC[] = { + {2201.8501, -1262, -713.593994}, + {2201.84009, -1262, -713.445007}, +}; + struct { const vec3_t *points; int num_points; @@ -28,6 +46,9 @@ struct { {points, 2, {{ 0, 0, 1}, 1.41421356}}, {points, 3, {{-0.333333343, 0.333333343, 0.333333343}, 1.63299322}}, {points, 4, {{0, 0, 0}, 1.73205081}}, + {tears_triangleA, 2, {{2201.83496, -1262, -713.521973}, 0.0734853372}}, + {tears_triangleB, 2, {{2201.83008, -1262, -713.44751}, 0.0103178304}}, + {tears_triangleC, 2, {{2201.84521, -1262, -713.519531}, 0.0746228099}}, }; #define num_tests (sizeof (tests) / sizeof (tests[0])) @@ -61,10 +82,10 @@ main (int argc, const char **argv) || fabs (sphere.radius - tests[i].expect.radius) > 1e-4) { res = 1; printf ("test %d failed\n", (int) i); - printf ("expect: [%.9g %.9g %.9g],%.9g\n", + printf ("expect: {%.9g, %.9g, %.9g},%.9g\n", VectorExpand (tests[i].expect.center), tests[i].expect.radius); - printf ("got : [%.9g %.9g %.9g],%.9g\n", + printf ("got : {%.9g, %.9g, %.9g},%.9g\n", VectorExpand (sphere.center), sphere.radius); } diff --git a/libs/util/test/test-csvf.c b/libs/util/test/test-csvf.c new file mode 100644 index 000000000..305b103ac --- /dev/null +++ b/libs/util/test/test-csvf.c @@ -0,0 +1,130 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/mathlib.h" +#include "QF/mersenne.h" +#include "QF/simd/vec4f.h" +#include "QF/sys.h" + +const vec4f_t points[] = { + {-1, -1, 1}, + { 1, 1, 1}, + {-1, 1, -1}, + { 1, -1, -1}, + {-1, -1, -1}, + { 1, 1, -1}, + {-1, 1, 1}, + { 1, -1, 1}, + { 0, 0, 0}, +}; + +// This particular triangle from ad_tears caused SEB to hit its iteration +// limit because the center in affine hull test failed due to excessivly tight +// epsilon. +// However, the problem isn't here (but good to have a set of tests for it +// anyway). +const vec4f_t tears_triangleA[] = { + {2201.82007, -1262, -713.450012, 1}, + {2201.8501, -1262, -713.593994, 1}, +}; +const vec4f_t tears_triangleB[] = { + {2201.82007, -1262, -713.450012, 1}, + {2201.84009, -1262, -713.445007, 1}, +}; +const vec4f_t tears_triangleC[] = { + {2201.8501, -1262, -713.593994, 1}, + {2201.84009, -1262, -713.445007, 1}, +}; + +struct { + const vec4f_t *points; + int num_points; + sphere_t expect; +} tests[] = { + {0, 0, {{ 0, 0, 0}, 0}}, + {points, 1, {{-1, -1, 1}, 0}}, + {points, 2, {{ 0, 0, 1}, 1.41421356}}, + {points, 3, {{-0.333333343, 0.333333343, 0.333333343}, 1.63299322}}, + {points, 4, {{0, 0, 0}, 1.73205081}}, + {tears_triangleA, 2, {{2201.83496, -1262, -713.521973}, 0.0734853372}}, + {tears_triangleB, 2, {{2201.83008, -1262, -713.44751}, 0.0103178304}}, + {tears_triangleC, 2, {{2201.84521, -1262, -713.519531}, 0.0746228099}}, +}; +#define num_tests (sizeof (tests) / sizeof (tests[0])) + +static inline float +rnd (mtstate_t *mt) +{ + union { + uint32_t u; + float f; + } uf; + + do { + uf.u = mtwist_rand (mt) & 0x007fffff; + } while (!uf.u); + uf.u |= 0x40000000; + return uf.f - 3.0; +} + +int +main (int argc, const char **argv) +{ + int res = 0; + size_t i; + int j; + vspheref_t sphere; + mtstate_t mt; + double start, end; + + for (i = 0; i < num_tests; i ++) { + sphere = CircumSphere_vf (tests[i].points, tests[i].num_points); + if (VectorDistance_fast (sphere.center, tests[i].expect.center) > 1e-4 + || fabs (sphere.radius - tests[i].expect.radius) > 1e-4) { + res = 1; + printf ("test %d failed\n", (int) i); + printf ("expect: {%.9g, %.9g, %.9g},%.9g\n", + VectorExpand (tests[i].expect.center), + tests[i].expect.radius); + printf ("got : {%.9g, %.9g, %.9g},%.9g\n", + VectorExpand (sphere.center), + sphere.radius); + } + } + + mtwist_seed (&mt, 0); + start = Sys_DoubleTime (); + for (i = 0; !res && i < 1000000; i++) { + vec4f_t cloud[4]; + vspheref_t cc; + vec_t r2; + vec_t fr; + + for (j = 0; j < 4; j++) { + VectorSet (rnd (&mt), rnd (&mt), rnd (&mt), cloud[j]); + cloud[j][3] = 1; + } + cc = CircumSphere_vf (cloud, 4); + if (cc.radius < 0) { + // degenerate + continue; + } + r2 = cc.radius * cc.radius; + for (j = 0; j < 4; j++) { + vec4f_t d = cloud[j] - cc.center; + fr = dotf (d, d)[0]; + if (fabs (fr - r2) < 1e-3 * r2) + continue; + printf ("%zd %d %.9g - %.9g = %.9g %.9g\n", + i, j, fr, r2, fr - r2, fabs(fr - r2)); + printf ("[%.9g %.9g %.9g] - [%.9g %.9g %.9g] = %.9g != %.9g\n", + VectorExpand (cloud[j]), VectorExpand (cc.center), fr, r2); + res = 1; + } + } + end = Sys_DoubleTime (); + printf ("%d itterations in %gs: %g itters/second\n", (int) i, end - start, + i / (end - start)); + return res; +} diff --git a/libs/util/test/test-darray.c b/libs/util/test/test-darray.c new file mode 100644 index 000000000..95ac207bb --- /dev/null +++ b/libs/util/test/test-darray.c @@ -0,0 +1,214 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +// because libc has its own remove +#define remove remove_renamed +#include +#include +#include + +#include "QF/darray.h" +#include "QF/sys.h" +#undef remove + +typedef int (*test_func) (int a, int b); +typedef struct intarray_s DARRAY_TYPE(int) intarray_t; + +intarray_t intarray = {0, 0, 16, 0}; + +static int +append (int val, int b) +{ + return DARRAY_APPEND (&intarray, val); +} + +static int +check_size (int a, int b) +{ + return intarray.size; +} + +static int +check_maxSize (int a, int b) +{ + return intarray.maxSize; +} + +static int +check_grow (int a, int b) +{ + return intarray.grow; +} + +static int +check_maxSizeGrowth (int a, int b) +{ + return intarray.maxSize % intarray.grow; +} + +static int +check_array (int a, int b) +{ + return !!intarray.a; +} + +static int __attribute__((pure)) +check_value (int index, int b) +{ + if ((size_t) index >= intarray.size) { + Sys_Error ("indexing beyond array"); + } + return intarray.a[index]; +} + +static int +insert_at (int val, int pos) +{ + return DARRAY_INSERT_AT (&intarray, val, pos); +} + +static int +open_at (int pos, int size) +{ + return DARRAY_OPEN_AT (&intarray, pos, size) - intarray.a; +} + +static const char text[] = "Aloy is an awesome huntress."; +static int +open_at2 (int pos, int size) +{ + memcpy(DARRAY_OPEN_AT (&intarray, pos, size), text, size * sizeof (int)); + return strcmp((char*) (intarray.a + pos), text); +} + +static int +remove_at (int pos, int b) +{ + return DARRAY_REMOVE_AT (&intarray, pos); +} + +static int +remove (int pos, int b) +{ + return DARRAY_REMOVE (&intarray); +} + +static int +close_at (int pos, int size) +{ + return DARRAY_CLOSE_AT (&intarray, pos, size); +} + +static int +clear (int a, int b) +{ + DARRAY_CLEAR (&intarray); + return 0; +} + +static int +resize (int size, int b) +{ + DARRAY_RESIZE (&intarray, size); + return 0; +} + +struct { + test_func test; + int param1, param2; + int test_expect; +} tests[] = { + {check_size, 0, 0, 0}, // confirm array empty but can grow + {check_maxSize, 0, 0, 0}, + {check_grow, 0, 0, 16}, + {check_array, 0, 0, 0}, + {append, 5, 0, 5}, // test first append to emtpty array + {check_size, 0, 0, 1}, + {check_maxSizeGrowth, 0, 0, 0}, + {check_maxSize, 0, 0, 16}, + {check_array, 0, 0, 1}, + {check_value, 0, 0, 5}, + {append, 42, 0, 42}, // test a second append + {check_size, 0, 0, 2}, + {check_maxSize, 0, 0, 16}, + {check_value, 0, 0, 5}, + {check_value, 1, 0, 42}, + {insert_at, 69, 1, 69}, // test insertions + {insert_at, 96, 0, 96}, + {check_size, 0, 0, 4}, + {check_maxSize, 0, 0, 16}, + {check_value, 0, 0, 96}, + {check_value, 1, 0, 5}, + {check_value, 2, 0, 69}, + {check_value, 3, 0, 42}, + {open_at, 2, 14, 2}, // test opening a large hole + {check_maxSizeGrowth, 0, 0, 0}, + {check_maxSize, 0, 0, 32}, + {check_size, 0, 0, 18}, + {check_value, 0, 0, 96}, + {check_value, 1, 0, 5}, + {check_value, 16, 0, 69}, + {check_value, 17, 0, 42}, + {close_at, 1, 15, 5}, // test block removal + {check_maxSize, 0, 0, 32}, + {check_size, 0, 0, 3}, + {check_value, 0, 0, 96}, + {check_value, 1, 0, 69}, + {check_value, 2, 0, 42}, + {remove, 0, 0, 42}, // test "pop" + {check_maxSize, 0, 0, 32}, + {check_size, 0, 0, 2}, + {check_value, 0, 0, 96}, + {check_value, 1, 0, 69}, + {remove_at, 0, 0, 96}, // test remove at + {check_maxSize, 0, 0, 32}, + {check_size, 0, 0, 1}, + {check_value, 0, 0, 69}, + {insert_at, 71, 1, 71}, // test insertion at end + {resize, 48, 0, 0}, // test resize bigger + {check_maxSizeGrowth, 0, 0, 0}, + {check_maxSize, 0, 0, 48}, + {check_size, 0, 0, 48}, + {check_value, 0, 0, 69}, + {check_value, 1, 0, 71}, + {resize, 24, 0, 0}, // test resize smaller + {check_maxSizeGrowth, 0, 0, 0}, + {check_maxSize, 0, 0, 48}, + {check_size, 0, 0, 24}, + {check_value, 0, 0, 69}, + {check_value, 1, 0, 71}, + {open_at2, 1, (sizeof (text) + sizeof (int) - 1) / sizeof (int), 0}, + {check_value, 0, 0, 69}, + {check_value, 1, 0, 0x796f6c41}, + {check_value, 9, 0, 71}, + {clear, 0, 0, 0}, // test clearing + {check_size, 0, 0, 0}, + {check_maxSize, 0, 0, 0}, + {check_array, 0, 0, 0}, +}; +#define num_tests (sizeof (tests) / sizeof (tests[0])) +int test_start_line = __LINE__ - num_tests - 2; + +int +main (int argc, const char **argv) +{ + int res = 0; + + size_t i; + + for (i = 0; i < num_tests; i++) { + if (tests[i].test) { + int test_res; + test_res = tests[i].test (tests[i].param1, tests[i].param2); + if (test_res != tests[i].test_expect) { + res |= 1; + printf ("test %d (line %d) failed\n", (int) i, + (int) i + test_start_line); + printf ("expect: %d\n", tests[i].test_expect); + printf ("got : %d\n", test_res); + continue; + } + } + } + return res; +} diff --git a/libs/util/test/test-half.c b/libs/util/test/test-half.c index b93c46f13..4db8c9a5a 100644 --- a/libs/util/test/test-half.c +++ b/libs/util/test/test-half.c @@ -18,9 +18,11 @@ struct { {0x0001, 5.9604644775390625e-08f}, {0x0000, 0.0f}, {0x8000, -0.0f}, -#if __GNUC_PREREQ(3,3) +#if defined(__GNUC_PREREQ) +# if __GNUC_PREREQ(3,3) {0x7c00, __builtin_huge_val ()}, {0xfc00, -__builtin_huge_val ()}, +# endif #endif {0x3555, 0.333251953125f}, {0x3e00, 1.5f}, diff --git a/libs/util/test/test-heapsort.c b/libs/util/test/test-heapsort.c new file mode 100644 index 000000000..d3841cc2c --- /dev/null +++ b/libs/util/test/test-heapsort.c @@ -0,0 +1,175 @@ +/* + test-heapsort.c + + Priority heap related tests + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "QF/heapsort.h" + +#define SIZEOF(x) (sizeof(x) / sizeof(x[0])) + +static int sink_data[] = { + 4, 7, 8, 3, 2, 6, 5, +}; +static int sink_expect[] = { + 8, 7, 6, 3, 2, 4, 5, +}; + +static int swim_data[] = { + 10, 8, 9, 7, 4, 1, 3, 2, +}; +static int swim_expect[] = { + 10, 8, 9, 7, 4, 1, 3, 2, +}; + +static int swim_data2[] = { + 10, 8, 9, 7, 4, 1, 3, 0, 2, 5, +}; +static int swim_expect2[] = { + 10, 8, 9, 7, 5, 1, 3, 0, 2, 4, +}; + +static int build_data[] = { + 1, 4, 3, 7, 8, 9, 10, +}; +static int build_expect1[] = { + 10, 8, 9, 7, 4, 1, 3, +}; +static int build_expect2[] = { + 9, 8, 3, 7, 4, 1, 10, +}; +static int build_expect3[] = { + 8, 7, 3, 1, 4, 9, 10, +}; + +static int sort_data[] = { + 4, 3, 7, 1, 8, 5, +}; +static int sort_expect[] = { + 1, 3, 4, 5, 7, 8, +}; + +static int +compare (const void *a, const void *b) +{ + return *(const int *)a - *(const int *)b; +} +#if 0 +static int +compared (const void *a, const void *b, void *d) +{ + return *(const int *)a - *(const int *)b; +} +#endif + +static void +test_sink (int *data, size_t nele) +{ + heap_sink (data, 0, nele, sizeof (int), compare); +} + +static void +test_swim (int *data, size_t nele) +{ + heap_swim (data, nele - 1, nele, sizeof (int), compare); +} + +static void +test_build (int *data, size_t nele) +{ + heap_build (data, nele, sizeof (int), compare); +} + +static void +test_sort (int *data, size_t nele) +{ + heapsort (data, nele, sizeof (int), compare); +} + +typedef struct { + const int *data; + const int *expect; + size_t nele; + void (*test) (int *data, size_t nele); + size_t sub; +} test_t; + +static test_t tests[] = { + { sink_data, sink_expect, SIZEOF (sink_data), test_sink }, + { swim_data, swim_expect, SIZEOF (swim_data), test_swim }, + { swim_data2, swim_expect2, SIZEOF (swim_data2), test_swim }, + { build_data, build_expect1, SIZEOF (build_data), test_build }, + { build_data, build_expect2, SIZEOF (build_data), test_build, 1 }, + { build_data, build_expect3, SIZEOF (build_data), test_build, 2 }, + { sort_data, sort_expect, SIZEOF (sort_data), test_sort }, +}; +#define num_tests ((int) SIZEOF (tests)) + +static void +dump_array (const int *arr, size_t n) +{ + while (n-- > 0) { + printf (" %d", *arr++); + } + printf ("\n"); +} + +static int +run_test (test_t *test) +{ + int test_data[test->nele]; + + memcpy (test_data, test->data, test->nele * sizeof (int)); + printf ("start : "); + dump_array (test_data, test->nele); + test->test (test_data, test->nele - test->sub); + printf ("got : "); + dump_array (test_data, test->nele); + printf ("expect: "); + dump_array (test->expect, test->nele); + if (memcmp (test_data, test->expect, test->nele * sizeof (int))) { + return 0; + } + return 1; +} + +int +main(int argc, const char **argv) +{ + int ret = 0; + + for (int i = 0; i < num_tests; i++) { + if (!run_test (&tests[i])) { + printf ("test %d failed\n", i); + ret = 1; + } + } + return ret; +} diff --git a/libs/util/test/test-listener.c b/libs/util/test/test-listener.c new file mode 100644 index 000000000..0c9654b38 --- /dev/null +++ b/libs/util/test/test-listener.c @@ -0,0 +1,59 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#define remove remove_renamed +#include +#include +#include + +#include "QF/listener.h" +#include "QF/sys.h" +#undef remove + +typedef struct obj_s { + int count; +} obj_t; + +struct LISTENER_SET_TYPE (obj_t) listeners = LISTENER_SET_STATIC_INIT(8); + +static void +listener_a (void *data, const obj_t *obj) +{ + *(int *) data += 1; + ((obj_t *) obj)->count += 1; +} + +static void +listener_b (void *data, const obj_t *obj) +{ + *(int *) data += 2; + ((obj_t *) obj)->count += 2; +} + +int +main (int argc, const char **argv) +{ + int res = 0; + int a_count = 0; + int b_count = 0; + obj_t obj = { 0 }; + + LISTENER_ADD (&listeners, listener_a, &a_count); + LISTENER_ADD (&listeners, listener_b, &b_count); + LISTENER_INVOKE (&listeners, &obj); + if (a_count != 1 || b_count != 2) { + Sys_Error ("a_count = %d, b_count = %d", a_count, b_count); + } + if (obj.count != 3) { + Sys_Error ("obj.count = %d", obj.count); + } + LISTENER_REMOVE (&listeners, listener_b, &b_count); + LISTENER_INVOKE (&listeners, &obj); + if (a_count != 2 || b_count != 2) { + Sys_Error ("a_count = %d, b_count = %d", a_count, b_count); + } + if (obj.count != 4) { + Sys_Error ("obj.count = %d", obj.count); + } + return res; +} diff --git a/libs/util/test/test-mat3.c b/libs/util/test/test-mat3.c index 385200750..9134fb9c2 100644 --- a/libs/util/test/test-mat3.c +++ b/libs/util/test/test-mat3.c @@ -80,7 +80,8 @@ negate: return 1; fail: - printf ("\n\n(%g %g %g)\n", VectorExpand (angles)); + printf ("\ntest_angle\n"); + printf ("(%g %g %g)\n", VectorExpand (angles)); printf (" [%g %g %g %g]\n", QuatExpand (rotation)); printf (" [%g %g %g %g] [%g %g %g] [%g %g %g]\n", QuatExpand (r), VectorExpand (scale), VectorExpand (shear)); @@ -98,7 +99,7 @@ test_transform (const vec3_t angles, const vec3_t scale) VectorCopy (v, x); AngleQuat (angles, rotation); - VectorCompMult (scale, x, x); + VectorCompMult (x, scale, x); QuatMultVec (rotation, x, x); Mat3Init (rotation, scale, mat); @@ -110,7 +111,8 @@ test_transform (const vec3_t angles, const vec3_t scale) return 1; fail: - printf ("\n\n(%g %g %g) (%g %g %g)\n", VectorExpand (angles), + printf ("\ntest_transform\n"); + printf ("(%g %g %g) (%g %g %g)\n", VectorExpand (angles), VectorExpand (scale)); printf (" (%g %g %g)\n", VectorExpand (x)); printf (" (%g %g %g)\n", VectorExpand (y)); @@ -125,11 +127,12 @@ test_transform2 (const vec3_t angles, const vec3_t scale) vec3_t x, y; quat_t rotation; mat3_t mat; - vec3_t rot, sc, sh; + quat_t rot; + vec3_t sc, sh; VectorCopy (v, x); AngleQuat (angles, rotation); - VectorCompMult (scale, x, x); + VectorCompMult (x, scale, x); QuatMultVec (rotation, x, x); Mat3Init (rotation, scale, mat); @@ -138,7 +141,7 @@ test_transform2 (const vec3_t angles, const vec3_t scale) VectorCopy (v, y); QuatMultVec (rot, y, y); VectorShear (sh, y, y); - VectorCompMult (sc, y, y);//scale + VectorCompMult (y, sc, y);//scale for (i = 0; i < 3; i++) if (!compare (x[i], y[i])) @@ -146,7 +149,8 @@ test_transform2 (const vec3_t angles, const vec3_t scale) return 1; fail: - printf ("\n\n(%g %g %g) (%g %g %g) (%g %g %g)\n", + printf ("\ntest_transform2\n"); + printf ("(%g %g %g) (%g %g %g) (%g %g %g)\n", VectorExpand (angles), VectorExpand (scale), VectorExpand (v)); printf (" (%g %g %g)\n", VectorExpand (x)); printf (" (%g %g %g)\n", VectorExpand (y)); @@ -173,7 +177,8 @@ test_inverse (const vec3_t angles, const vec3_t scale) return 1; fail: - printf ("\n\n(%g %g %g) (%g %g %g)\n", + printf ("\ntest_inverse\n"); + printf ("(%g %g %g) (%g %g %g)\n", VectorExpand (angles), VectorExpand (scale)); printf (" [%g %g %g]\n [%g %g %g]\n [%g %g %g]\n\n", Mat3Expand (mat)); printf (" [%g %g %g]\n [%g %g %g]\n [%g %g %g]\n\n", Mat3Expand (inv)); diff --git a/libs/util/test/test-mat4.c b/libs/util/test/test-mat4.c index 623444a0d..cd30c0990 100644 --- a/libs/util/test/test-mat4.c +++ b/libs/util/test/test-mat4.c @@ -58,8 +58,8 @@ static vec3_t test_scales[] = { { 3, 1, 2}, { 3, 2, 1}, }; -#define num_translation_tests \ - (sizeof (test_translations) / sizeof (test_translations[0])) +#define num_scale_tests \ + (sizeof (test_scales) / sizeof (test_scales[0])) // return true if a and b are close enough (yay, floats) static int @@ -114,7 +114,7 @@ test_transform (const vec3_t angles, const vec3_t scale, VectorCopy (v, x); AngleQuat (angles, rotation); - VectorCompMult (scale, x, x); + VectorCompMult (x, scale, x); QuatMultVec (rotation, x, x); VectorAdd (x, translation, x); @@ -143,11 +143,12 @@ test_transform2 (const vec3_t angles, const vec3_t scale, vec3_t x, y; quat_t rotation; mat4_t mat; - vec3_t rot, sc, sh, tr; + quat_t rot; + vec3_t sc, sh, tr; VectorCopy (v, x); AngleQuat (angles, rotation); - VectorCompMult (scale, x, x); + VectorCompMult (x, scale, x); QuatMultVec (rotation, x, x); VectorAdd (translation, x, x); @@ -157,7 +158,7 @@ test_transform2 (const vec3_t angles, const vec3_t scale, VectorCopy (v, y); QuatMultVec (rot, y, y); VectorShear (sh, y, y); - VectorCompMult (sc, y, y);//scale + VectorCompMult (y, sc, y);//scale VectorAdd (tr, y, y); for (i = 0; i < 3; i++) @@ -171,6 +172,9 @@ fail: VectorExpand (translation), VectorExpand (v)); printf (" (%g %g %g)\n", VectorExpand (x)); printf (" (%g %g %g)\n", VectorExpand (y)); + printf (" (%g %g %g %g) (%g %g %g %g) (%g %g %g) (%g %g %g) (%g %g %g)\n", + QuatExpand (rotation), QuatExpand (rot), VectorExpand (sh), + VectorExpand (sc), VectorExpand (tr)); return 0; } @@ -214,33 +218,41 @@ main (int argc, const char **argv) size_t i, j, k; for (i = 0; i < num_angle_tests; i ++) { - if (!test_angle (test_angles[i])) + if (!test_angle (test_angles[i])) { res = 1; + printf("angle test %zd failed\n", i); + } } for (i = 0; i < num_angle_tests; i ++) { - for (j = 0; j < num_translation_tests; j ++) { + for (j = 0; j < num_scale_tests; j ++) { for (k = 0; k < num_translation_tests; k ++) { if (!test_transform (test_angles[i], test_scales[j], - test_translations[k])) + test_translations[k])) { res = 1; + printf("transform test %zd:%zd:%zd failed\n", i, j, k); + } } } } for (i = 0; i < num_angle_tests; i ++) { - for (j = 0; j < num_translation_tests; j ++) { + for (j = 0; j < num_scale_tests; j ++) { for (k = 0; k < num_translation_tests; k ++) { if (!test_transform2 (test_angles[i], test_scales[j], - test_translations[k])) + test_translations[k])) { res = 1; + printf("transform2 test %zd:%zd:%zd failed\n", i, j, k); + } } } } for (i = 0; i < num_angle_tests; i ++) { - for (j = 0; j < num_translation_tests; j ++) { + for (j = 0; j < num_scale_tests; j ++) { for (k = 0; k < num_translation_tests; k ++) { if (!test_inverse (test_angles[i], test_scales[j], - test_translations[k])) + test_translations[k])) { res = 1; + printf("inverse test %zd:%zd:%zd failed\n", i, j, k); + } } } } diff --git a/libs/util/test/test-plist.c b/libs/util/test/test-plist.c index 20ab1e94e..14b516d6c 100644 --- a/libs/util/test/test-plist.c +++ b/libs/util/test/test-plist.c @@ -2,7 +2,7 @@ # include "config.h" #endif #include -#include "QF/qfplist.h" +#include "QF/plist.h" static const char *test_strings[] = { "Guarding the entrance to the Grendal\n" @@ -11,7 +11,7 @@ static const char *test_strings[] = { "of the Shadow cult.\n\n" "For years the Shadow Gate existed in\n" "obscurity but after the cult discovered\n" - "the \3023\354\341\343\353\240\307\341\364\345 in the caves below\n" + "the \302\354\341\343\353\240\307\341\364\345 in the caves below\n" "the empire took notice.\n" "A batallion of Imperial Knights were\n" "sent to the gate to destroy the cult\n" @@ -28,8 +28,8 @@ test_string_io (const char *str) item = PL_NewString (str); saved = PL_WritePropertyList (item); - PL_Free (item); - item = PL_GetPropertyList (saved); + PL_Release (item); + item = PL_GetPropertyList (saved, 0); res = PL_String (item); if (!strcmp (str, res)) return 1; diff --git a/libs/util/test/test-pqueue.c b/libs/util/test/test-pqueue.c new file mode 100644 index 000000000..db1bd0b14 --- /dev/null +++ b/libs/util/test/test-pqueue.c @@ -0,0 +1,254 @@ +/* + test-pqueue.c + + Priority queue related tests + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "QF/pqueue.h" + +#define SIZEOF(x) (sizeof(x) / sizeof(x[0])) + +static int +compare (const int *a, const int *b, void *arg) +{ + //printf ("%d %d\n", *a, *b); + return *a - *b; +} + +static int +rev (const int *a, const int *b, void *arg) +{ + return *b - *a; +} + +static int queue_array[7]; +static struct PQUEUE_TYPE (int) pqueue = { + .maxSize = SIZEOF (queue_array), + .compare = compare, + .a = queue_array, + .data = 0, +}; + +static int +pq_is_empty (void) +{ + return PQUEUE_IS_EMPTY (&pqueue); +} + +static int +pq_is_full (void) +{ + return PQUEUE_IS_FULL (&pqueue); +} + +static int +pq_peek (void) +{ + return PQUEUE_PEEK (&pqueue); +} + +static void +pq_insert (int value) +{ + PQUEUE_INSERT (&pqueue, value); +} + +static int +pq_remove (void) +{ + return PQUEUE_REMOVE (&pqueue); +} + +static void +pq_adjust (int index) +{ + PQUEUE_ADJUST (&pqueue, index); +} + +static void +dump_array (const int *arr, size_t n) +{ + while (n-- > 0) { + printf (" %d", *arr++); + } + printf ("\n"); +} + + +static int +test_insert (const int *data, size_t count) +{ + while (count-- > 0) { + pq_insert (*data++); + } + return 1; +} + +static int +test_remove (const int *data, size_t count) +{ + while (count-- > 0) { + if (pq_remove () != *data++) { + return 0; + } + } + return 1; +} + +static int +test_adjust (const int *data, size_t count) +{ + //printf ("adjust start\n"); + pqueue.a[count] = *data; + pq_adjust (count); + //printf ("adjust end\n"); + return 1; +} + +static int +test_peek (const int *data, size_t count) +{ + return pq_peek () == *data; +} + +static int +test_size (const int *data, size_t count) +{ + return pqueue.size == count; +} + +static int +test_is_empty (const int *data, size_t count) +{ + return pq_is_empty () == *data; +} + +static int +test_is_full (const int *data, size_t count) +{ + return pq_is_full () == *data; +} + +static int +dump_queue (const int *data, size_t count) +{ + if (0) { + dump_array (pqueue.a, pqueue.size); + } + return 1; +} + +static int true_v = 1; +static int false_v = 0; + +static int data[] = { 3, 2, 5, 6, 8, 1, 9 }; +static int data1[] = { 3, 7, 5, 6, 8, 1, 9 }; +static int data2[] = { 3, 7, 5, 6, 8, 1, 4 }; +static int data3[] = { 3, 7, 0, 6, 8, 1, 4 }; +static int sort[SIZEOF(data)]; +static int sort1[SIZEOF(data1)]; +static int sort2[SIZEOF(data2)]; +static int sort3[SIZEOF(data3)]; + +typedef struct { + const int *data; + size_t nele; + int (*test) (const int *data, size_t nele); +} test_t; + +static test_t tests[] = { + { &true_v, 1, test_is_empty }, + { &false_v, 1, test_is_full }, + { 0, 0, test_size }, + + { data + 0, 1, test_insert }, + { 0, 1, test_size }, + { &false_v, 1, test_is_empty }, + { &false_v, 1, test_is_full }, + { data + 0, 1, test_peek }, + { data + 0, 1, test_remove }, + { &true_v, 1, test_is_empty }, + { &false_v, 1, test_is_full }, + { 0, 0, test_size }, + + { data + 0, 3, test_insert }, + { 0, 3, test_size }, + { data + 2, 1, test_peek }, + { data + 2, 1, test_remove }, + { data + 0, 1, test_remove }, + { data + 1, 1, test_remove }, + { &true_v, 1, test_is_empty }, + + { data + 0, SIZEOF(data), test_insert }, + { 0, SIZEOF(data), test_size }, + { &false_v, 1, test_is_empty }, + { &true_v, 1, test_is_full }, + { 0, 0, dump_queue }, + { sort1 + 2, 3, test_adjust }, + { 0, 0, dump_queue }, + { sort2 + 4, 0, test_adjust }, + { 0, 0, dump_queue }, + { sort3 + 6, 4, test_adjust }, + { 0, 0, dump_queue }, + { sort3 + 0, SIZEOF(sort), test_remove }, +}; +#define num_tests ((int) SIZEOF (tests)) + +static int +run_test (const test_t *test) +{ + return test->test (test->data, test->nele); +} + +int +main(int argc, const char **argv) +{ + int ret = 0; + + memcpy (sort, data, sizeof (sort)); + memcpy (sort1, data1, sizeof (sort1)); + memcpy (sort2, data2, sizeof (sort2)); + memcpy (sort3, data3, sizeof (sort3)); + heapsort_r (sort, SIZEOF(sort), sizeof(int), (__compar_d_fn_t)rev, 0); + heapsort_r (sort1, SIZEOF(sort1), sizeof(int), (__compar_d_fn_t)rev, 0); + heapsort_r (sort2, SIZEOF(sort2), sizeof(int), (__compar_d_fn_t)rev, 0); + heapsort_r (sort3, SIZEOF(sort3), sizeof(int), (__compar_d_fn_t)rev, 0); + //dump_array (sort, SIZEOF(sort)); + //dump_array (sort1, SIZEOF(sort1)); + //dump_array (sort2, SIZEOF(sort2)); + //dump_array (sort3, SIZEOF(sort3)); + for (int i = 0; i < num_tests; i++) { + if (!run_test (&tests[i])) { + printf ("test %d failed\n", i); + ret = 1; + } + } + return ret; +} diff --git a/libs/util/test/test-quat.c b/libs/util/test/test-quat.c index 58a3e0a2f..57e38f8dd 100644 --- a/libs/util/test/test-quat.c +++ b/libs/util/test/test-quat.c @@ -4,6 +4,15 @@ #include "QF/mathlib.h" +static struct { + quat_t q1; + quat_t q2; + quat_t expect; +} quat_mult_tests[] = { + {{1, 2, 3, 4}, {5, 6, 7, 8}, {24, 48, 48, -6}}, +}; +#define num_quat_mult_tests (sizeof (quat_mult_tests) / sizeof (quat_mult_tests[0])) + //PITCH YAW ROLL static vec3_t test_angles[] = { {0, 0, 0}, @@ -20,6 +29,45 @@ static vec3_t test_angles[] = { }; #define num_angle_tests (sizeof (test_angles) / sizeof (test_angles[0])) +static struct { + vec3_t a; + vec3_t b; + quat_t expect; +} quat_vector_rotation_tests[] = { + {{1, 0, 0}, {1, 0, 0}, {0, 0, 0, 1}}, + {{0, 1, 0}, {0, 1, 0}, {0, 0, 0, 1}}, + {{0, 0, 1}, {0, 0, 1}, {0, 0, 0, 1}}, + {{1, 0, 0}, {8, 0, 0}, {0, 0, 0, 1}}, + {{0, 8, 0}, {0, 1, 0}, {0, 0, 0, 1}}, + {{0, 0, 8}, {0, 0, 8}, {0, 0, 0, 1}}, + {{1, 0, 0}, {-1, 0, 0}, {0, 0, 0, 0}}, // x, y, z = NaN + {{0, -1, 0}, {0, 1, 0}, {0, 0, 0, 0}}, // x, y, z = NaN + {{0, 0, 1}, {0, 0, -1}, {0, 0, 0, 0}}, // x, y, z = NaN + {{-1, 0, 0}, {8, 0, 0}, {0, 0, 0, 0}}, // x, y, z = NaN + {{0, 8, 0}, {0, -1, 0}, {0, 0, 0, 0}}, // x, y, z = NaN + {{0, 0, -8}, {0, 0, 8}, {0, 0, 0, 0}}, // x, y, z = NaN + // excessive for float, but if vec_t becomes double... + // 1/50 second orbiting JNSQ Kerbin at 120km altitude. While this has + // nothing to do with quakeforge (yet?), it came up when testing camera + // rotation in KSP and Unity failed miserably. I don't remember the exact + // details, but I could see significant snapping in the rotation (I thought + // it was a few times per second, but these numbers indicate it must have + // been every few seconds). + // The quaternion is unit to 9 sigfigs (a little larger) + {{1720000, 0, 0}, {1719999.9983028059, 76.409082595828366, 0}, {0, 0, 2.22119434e-5, 1}}, + // 1/20 second, same situation + {{1720000, 0, 0}, {1719999.9893925365, 191.02270615971355, 0}, {0, 0, 5.55298575e-5, 1}}, + // 1/5 second, same situation + {{1720000, 0, 0}, {1719999.8302805868, 764.09080107761736, 0}, {0, 0, 2.2211943e-4, 1}}, + // 1/4 second, same situation + {{1720000, 0, 0}, {1719999.7348134194, 955.11348367609469, 0}, {0, 0, 2.77649262e-4, 1}}, + // 1/3 second, same situation. This is (float) 1 ulp in w: about 0.0424 + // degrees. + // The quaternion is unit to 9 sigfigs (a little larger) + {{1720000, 0, 0}, {1719999.5285571995, 1273.4845939975546, 0}, {0, 0, 3.70199094e-4, 0.99999994}}, +}; +#define num_quat_vector_rotation_tests (sizeof (quat_vector_rotation_tests) / sizeof (quat_vector_rotation_tests[0])) + // return true if a and b are close enough (yay, floats) static int compare (vec_t a, vec_t b) @@ -28,60 +76,96 @@ compare (vec_t a, vec_t b) return diff * diff < 0.001; } +static int +test_quat_mult(const quat_t q1, const quat_t q2, const quat_t expect) +{ + int i; + quat_t r; + + QuatMult (q1, q2, r); + + for (i = 0; i < 4; i++) + if (!compare (r[i], expect[i])) + goto fail; + return 1; +fail: + printf ("%11.9g %11.9g %11.9g %11.9g\n", QuatExpand (q1)); + printf ("%11.9g %11.9g %11.9g %11.9g\n", QuatExpand (q2)); + printf ("%11.9g %11.9g %11.9g %11.9g\n", QuatExpand (r)); + printf ("%11.9g %11.9g %11.9g %11.9g\n", QuatExpand (expect)); + return 0; +} + + +static void +rotate_vec (const quat_t r, const vec3_t v, vec3_t out) +{ + quat_t qv = {0, 0, 0, 0}; + quat_t t; + + VectorCopy (v, qv); + + QuatConj (r, t); + QuatMult (qv, t, t); + QuatMult (r, t, t); + VectorCopy (t, out); +} + static int test_rotation (const vec3_t angles) { int i; vec3_t forward, right, up; - quat_t quat, f, r, u, t; - quat_t qf = {0, 1, 0, 0}; - quat_t qr = {0, 0, -1, 0}; - quat_t qu = {0, 0, 0, 1}; + quat_t quat, conj, f, r, u, t; + quat_t qf = {1, 0, 0, 0}; + quat_t qr = {0, -1, 0, 0}; + quat_t qu = {0, 0, 1, 0}; AngleVectors (angles, forward, right, up); AngleQuat (angles, quat); + QuatConj (quat, conj); // rotate forward vector - QuatConj (quat, t); - QuatMult (qf, t, t); + QuatMult (qf, conj, t); QuatMult (quat, t, f); // rotate right vector - QuatConj (quat, t); - QuatMult (qr, t, t); + QuatMult (qr, conj, t); QuatMult (quat, t, r); // rotate up vector - QuatConj (quat, t); - QuatMult (qu, t, t); + QuatMult (qu, conj, t); QuatMult (quat, t, u); - if (!compare (f[0], 0)) + if (!compare (f[3], 0)) goto fail; for (i = 0; i < 3; i++) - if (!compare (forward[i], f[i + 1])) + if (!compare (forward[i], f[i])) goto fail; - if (!compare (r[0], 0)) + if (!compare (r[3], 0)) goto fail; for (i = 0; i < 3; i++) - if (!compare (right[i], r[i + 1])) + if (!compare (right[i], r[i])) goto fail; - if (!compare (u[0], 0)) + if (!compare (u[3], 0)) goto fail; for (i = 0; i < 3; i++) - if (!compare (up[i], u[i + 1])) + if (!compare (up[i], u[i])) goto fail; return 1; fail: - printf ("\n\n%g %g %g\n\n", angles[0], angles[1], angles[2]); - printf ("%g %g %g\n", forward[0], forward[1], forward[2]); - printf ("%g %g %g\n", right[0], right[1], right[2]); - printf ("%g %g %g\n\n", up[0], up[1], up[2]); + printf ("\ntest_rotation\n"); + printf ("%11.9g %11.9g %11.9g\n", VectorExpand (angles)); + printf ("%11.9g %11.9g %11.9g %11.9g\n", QuatExpand (quat)); + printf ("%11.9g %11.9g %11.9g %11.9g\n\n", QuatExpand (conj)); + printf ("%11.9g %11.9g %11.9g\n", VectorExpand (forward)); + printf ("%11.9g %11.9g %11.9g\n", VectorExpand (right)); + printf ("%11.9g %11.9g %11.9g\n\n", VectorExpand (up)); - printf ("%g %g %g %g\n", f[0], f[1], f[2], f[3]); - printf ("%g %g %g %g\n", r[0], r[1], r[2], r[3]); - printf ("%g %g %g %g\n", u[0], u[1], u[2], u[3]); + printf ("%11.9g %11.9g %11.9g %11.9g\n", QuatExpand (f)); + printf ("%11.9g %11.9g %11.9g %11.9g\n", QuatExpand (r)); + printf ("%11.9g %11.9g %11.9g %11.9g\n", QuatExpand (u)); return 0; } @@ -120,14 +204,216 @@ test_rotation2 (const vec3_t angles) goto fail; return 1; fail: - printf ("\n\n%g %g %g\n\n", angles[0], angles[1], angles[2]); - printf ("%g %g %g\n", forward[0], forward[1], forward[2]); - printf ("%g %g %g\n", right[0], right[1], right[2]); - printf ("%g %g %g\n\n", up[0], up[1], up[2]); + printf ("\ntest_rotation2\n"); + printf ("\n\n%11.9g %11.9g %11.9g\n\n", angles[0], angles[1], angles[2]); + printf ("%11.9g %11.9g %11.9g\n", forward[0], forward[1], forward[2]); + printf ("%11.9g %11.9g %11.9g\n", right[0], right[1], right[2]); + printf ("%11.9g %11.9g %11.9g\n\n", up[0], up[1], up[2]); - printf ("%g %g %g\n", f[0], f[1], f[2]); - printf ("%g %g %g\n", r[0], r[1], r[2]); - printf ("%g %g %g\n", u[0], u[1], u[2]); + printf ("%11.9g %11.9g %11.9g\n", f[0], f[1], f[2]); + printf ("%11.9g %11.9g %11.9g\n", r[0], r[1], r[2]); + printf ("%11.9g %11.9g %11.9g\n", u[0], u[1], u[2]); + return 0; +} + +static int +test_rotation3 (const vec3_t angles) +{ + int i; + quat_t quat; + vec3_t v = {1, 1, 1}; + vec3_t a, b; + + AngleQuat (angles, quat); + QuatMultVec (quat, v, a); + rotate_vec (quat, v, b); + + for (i = 0; i < 3; i++) + if (!compare (a[i], b[i])) + goto fail; + return 1; +fail: + printf ("\ntest_rotation3\n"); + printf ("%11.9g %11.9g %11.9g\n", VectorExpand(a)); + printf ("%11.9g %11.9g %11.9g\n", VectorExpand(b)); + return 0; +} + +// XXX FIXME see usage in test_rotation4. need to investigate whether +// -ffast-math is any real benefit +#define ISNAN(x) (((x) & 0x7f800000) == 0x7f800000 && ((x) & 0x7fffff)) + +// FIXME differences in precision between archs +static int +cmp (float a, float b) +{ + typedef union { + float f; + int i; + } fi; + fi ax; + fi bx; + int x; + + ax.f = a; + bx.f = b; + x = ax.i - bx.i; + if (x < 0) { + x = -x; + } + return (x & 0x7ffffffc) == 0; +} + +static int +test_rotation4 (const vec3_t a, const vec3_t b, const quat_t expect) +{ + int i; + union { int x[4]; vec_t q[4]; } q; + vec_t *quat = q.q; + vec3_t t; + vec_t d = 0; + + VectorZero (t); + + // find the rotation vector between a and b + QuatRotation (a, b, quat); + + if (quat[3] == 0) { + if (expect[3] != 0) { + goto fail; + } + // expect NaN for the vector components because the vectors are + // anti-parallel and thus the rotation axis is undefined + //XXX FIXME(?) still using -ffast-math which implies + // -ffinite-math-only which in turn disables nan/inf checks, so have + // to do it by hand + // if (!(isnan(quat[0]) && isnan(quat[1]) && isnan(quat[2]))) { + if (!(ISNAN(q.x[0]) && ISNAN(q.x[1]) && ISNAN(q.x[2]))) { + goto fail; + } + } else { + // the vectors are not anti-parallel and thus the rotation axis is + // defined, so NaN is invalid + // XXX FIXME see above + //if (isnan(quat[0]) || isnan(quat[1]) || isnan(quat[2])) { + if (ISNAN(q.x[0]) || ISNAN(q.x[1]) || ISNAN(q.x[2])) { + goto fail; + } + for (i = 0; i < 4; i++) { + // yes, float precision will make it difficult to set up expect + // but it is at least consistent (ie, the "errors" are not at all + // random and thus will be the same from run to run) + if (!cmp (quat[i], expect[i])) { + goto fail; + } + } + QuatMultVec(quat, a, t); + + d = DotProduct (t, b) / (VectorLength (t) * VectorLength (b)) - 1; + if (d * d > 1e-8) { + goto fail; + } + } + return 1; +fail: + printf ("\ntest_rotation4\n"); + printf ("a: %11.9g %11.9g %11.9g\n", VectorExpand(a)); + printf ("b: %11.9g %11.9g %11.9g\n", VectorExpand(b)); + printf ("q: %11.9g %11.9g %11.9g %11.9g\n", QuatExpand(quat)); + printf ("e: %11.9g %11.9g %11.9g %11.9g\n", QuatExpand(expect)); + printf ("t: %11.9g %11.9g %11.9g\n", VectorExpand(t)); + printf ("d: %11.9g\n", d); + return 0; +} + +#define s05 0.70710678118654757 + +static struct { + quat_t q; + vec_t expect[9]; +} quat_mat_tests[] = { + {{0, 0, 0, 1}, + {1, 0, 0, + 0, 1, 0, + 0, 0, 1}}, + {{1, 0, 0, 0}, + {1, 0, 0, + 0, -1, 0, + 0, 0, -1}}, + {{0, 1, 0, 0}, + {-1, 0, 0, + 0, 1, 0, + 0, 0, -1}}, + {{0, 0, 1, 0}, + {-1, 0, 0, + 0, -1, 0, + 0, 0, 1}}, + {{0.5, 0.5, 0.5, 0.5}, + {0, 0, 1, + 1, 0, 0, + 0, 1, 0}}, +#if (defined(__i686__) || defined(__aarch64__)) && defined(__OPTIMIZE__) + // the fp unit carries more precision than a 32-bit float, so + // the close-to-zero errors are different + {{s05, 0.0, 0.0, s05}, + {1, 0, 0, + 0, 3.42285418e-08, -0.99999994, + 0, 0.99999994, 3.42285418e-08}}, +#else + {{s05, 0.0, 0.0, s05}, + {1, 0, 0, + 0, 5.96046448e-8, -0.99999994, + 0, 0.99999994, 5.96046448e-8}}, +#endif +}; +#define num_quat_mat_tests (sizeof (quat_mat_tests) / sizeof (quat_mat_tests[0])) + +static int +test_quat_mat(const quat_t q, const quat_t expect) +{ + int i; + vec_t m[9]; + + QuatToMatrix (q, m, 0, 0); + + for (i = 0; i < 9; i++) + if (m[i] != expect[i]) // exact tests here + goto fail; + return 1; +fail: + printf ("\ntest_quat_mat\n"); + printf ("%11.9g %11.9g %11.9g %11.9g\n", QuatExpand (q)); + printf ("%11.9g %11.9g %11.9g %11.9g %11.9g %11.9g\n", + VectorExpand (m + 0), VectorExpand (expect + 0)); + printf ("%11.9g %11.9g %11.9g %11.9g %11.9g %11.9g\n", + VectorExpand (m + 3), VectorExpand (expect + 3)); + printf ("%11.9g %11.9g %11.9g %11.9g %11.9g %11.9g\n", + VectorExpand (m + 6), VectorExpand (expect + 6)); + return 0; +} + +static int +test_quat_mat2(const quat_t q, const quat_t expect) +{ + int i; + vec_t m[9]; + + QuatToMatrix (q, m, 0, 1); + Mat3Transpose (m, m); + + for (i = 0; i < 9; i++) + if (m[i] != expect[i]) // exact tests here + goto fail; + return 1; +fail: + printf ("\ntest_quat_mat\n"); + printf ("%11.9g %11.9g %11.9g %11.9g\n", QuatExpand (q)); + printf ("%11.9g %11.9g %11.9g %11.9g %11.9g %11.9g\n", + VectorExpand (m + 0), VectorExpand (expect + 0)); + printf ("%11.9g %11.9g %11.9g %11.9g %11.9g %11.9g\n", + VectorExpand (m + 3), VectorExpand (expect + 3)); + printf ("%11.9g %11.9g %11.9g %11.9g %11.9g %11.9g\n", + VectorExpand (m + 6), VectorExpand (expect + 6)); return 0; } @@ -137,6 +423,14 @@ main (int argc, const char **argv) int res = 0; size_t i; + for (i = 0; i < num_quat_mult_tests; i ++) { + vec_t *q1 = quat_mult_tests[i].q1; + vec_t *q2 = quat_mult_tests[i].q2; + vec_t *expect = quat_mult_tests[i].expect; + if (!test_quat_mult (q1, q2, expect)) + res = 1; + } + for (i = 0; i < num_angle_tests; i ++) { if (!test_rotation (test_angles[i])) res = 1; @@ -146,5 +440,34 @@ main (int argc, const char **argv) if (!test_rotation2 (test_angles[i])) res = 1; } + + for (i = 0; i < num_angle_tests; i ++) { + if (!test_rotation3 (test_angles[i])) + res = 1; + } + + for (i = 0; i < num_quat_vector_rotation_tests; i++) { + vec_t *a = quat_vector_rotation_tests[i].a; + vec_t *b = quat_vector_rotation_tests[i].b; + vec_t *expect = quat_vector_rotation_tests[i].expect; + if (!test_rotation4 (a, b, expect)) { + res = 1; + } + } + + for (i = 0; i < num_quat_mat_tests; i ++) { + vec_t *q = quat_mat_tests[i].q; + vec_t *expect = quat_mat_tests[i].expect; + if (!test_quat_mat (q, expect)) + res = 1; + } + + for (i = 0; i < num_quat_mat_tests; i ++) { + vec_t *q = quat_mat_tests[i].q; + vec_t *expect = quat_mat_tests[i].expect; + if (!test_quat_mat2 (q, expect)) + res = 1; + } + return res; } diff --git a/libs/util/test/test-ringbuffer.c b/libs/util/test/test-ringbuffer.c new file mode 100644 index 000000000..ece04c992 --- /dev/null +++ b/libs/util/test/test-ringbuffer.c @@ -0,0 +1,313 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#include "QF/dstring.h" +#include "QF/mathlib.h" +#include "QF/ringbuffer.h" +#include "QF/sys.h" + +typedef int (*test_func) (int a, int b); + +typedef RING_BUFFER (int, 8) ringbuffer_t; +typedef RING_BUFFER (char, 8) ringbuffer_char_t; +typedef RING_BUFFER_ATOMIC (char, 8) ringbuffer_atomic_t; + +static ringbuffer_t int_ringbuffer = { .buffer = { [0 ... 7] = 0xdeadbeef } }; +static ringbuffer_char_t ringbuffer_char; +static ringbuffer_atomic_t ringbuffer_atomic; +mtx_t rb_mtx; +cnd_t rb_write_cnd; +cnd_t rb_read_cnd; + +ringbuffer_atomic_t int_ringbuffer_atomic; + +static int +get_size (int a, int b) +{ + return RB_buffer_size (&int_ringbuffer); +} + +static int +space_available (int a, int b) +{ + return RB_SPACE_AVAILABLE (int_ringbuffer); +} + +static int +data_available (int a, int b) +{ + return RB_DATA_AVAILABLE (int_ringbuffer); +} + +static int +write_data (int value, int b) +{ + return RB_WRITE_DATA (int_ringbuffer, &value, 1); +} + +static int +acquire (int count, int b) +{ + return *RB_ACQUIRE (int_ringbuffer, count); +} + +static int +read_data (int a, int b) +{ + int ret; + RB_READ_DATA (int_ringbuffer, &ret, 1); + return ret; +} + +static int +release (int count, int b) +{ + return RB_RELEASE (int_ringbuffer, count); +} + +static int +peek_data (int offset, int b) +{ + return *RB_PEEK_DATA (int_ringbuffer, offset); +} + +static int +poke_data (int offset, int value) +{ + return RB_POKE_DATA (int_ringbuffer, offset, value); +} + +static int +init_threading (int a, int b) +{ + if (mtx_init (&rb_mtx, mtx_plain) != thrd_success) { + return -1; + } + if (cnd_init (&rb_write_cnd) != thrd_success) { + return -1; + } + if (cnd_init (&rb_read_cnd) != thrd_success) { + return -1; + } + return 0; +} + +static const char *text = "Sometimes, it is necessary to write long gibberish " +"in order to test things. Hopefully, this may be an exception, though it's " +"not exactly the most useful or intersting text around."; + +static dstring_t *out_text; + +static int +compare_strings (int a, int b) +{ + int ret = strcmp (out_text->str, text); + dstring_delete (out_text); + out_text = 0; + return ret; +} + +static int +locked_reader (void *data) +{ + if (!out_text) { + out_text = dstring_new (); + } + dstring_clear (out_text); + + do { + mtx_lock (&rb_mtx); + while (RB_DATA_AVAILABLE (ringbuffer_char) < 1) { + cnd_wait (&rb_read_cnd, &rb_mtx); + } + unsigned len = RB_DATA_AVAILABLE (ringbuffer_char); + RB_READ_DATA (ringbuffer_char, dstring_reserve (out_text, len), len); + cnd_signal (&rb_write_cnd); + mtx_unlock (&rb_mtx); + } while (out_text->str[out_text->size - 1]); + + return 0; +} + +static int +locked_writer (void *data) +{ + size_t data_len = strlen (text) + 1; // send nul terminator too + const char *str = text; + + while (data_len > 0) { + mtx_lock (&rb_mtx); + while (RB_SPACE_AVAILABLE (ringbuffer_char) < 1) { + cnd_wait (&rb_write_cnd, &rb_mtx); + } + unsigned len = RB_SPACE_AVAILABLE (ringbuffer_char); + len = min (len, data_len); + RB_WRITE_DATA (ringbuffer_char, str, len); + str += len; + data_len -= len; + cnd_signal (&rb_read_cnd); + mtx_unlock (&rb_mtx); + } + + return 0; +} + +static int +run_locked (int a, int b) +{ + thrd_t writer; + thrd_t reader; + + if (thrd_create (&writer, locked_writer, 0) != thrd_success) { + return -1; + } + if (thrd_create (&reader, locked_reader, 0) != thrd_success) { + return -1; + } + + int res; + if (thrd_join (writer, &res) != thrd_success) { + return -1; + } + if (thrd_join (reader, &res) != thrd_success) { + return -1; + } + return 0; +} + +static int +free_reader (void *data) +{ + if (!out_text) { + out_text = dstring_new (); + } + dstring_clear (out_text); + + do { + while (RB_DATA_AVAILABLE (ringbuffer_atomic) < 1) { + } + unsigned len = RB_DATA_AVAILABLE (ringbuffer_atomic); + RB_READ_DATA (ringbuffer_atomic, dstring_reserve (out_text, len), len); + } while (out_text->str[out_text->size - 1]); + + return 0; +} + +static int +free_writer (void *data) +{ + size_t data_len = strlen (text) + 1; // send nul terminator too + const char *str = text; + + while (data_len > 0) { + while (RB_SPACE_AVAILABLE (ringbuffer_atomic) < 1) { + } + unsigned len = RB_SPACE_AVAILABLE (ringbuffer_atomic); + len = min (len, data_len); + RB_WRITE_DATA (ringbuffer_atomic, str, len); + str += len; + data_len -= len; + } + + return 0; +} + +static int +run_free (int a, int b) +{ + thrd_t writer; + thrd_t reader; + + if (thrd_create (&writer, free_writer, 0) != thrd_success) { + return -1; + } + if (thrd_create (&reader, free_reader, 0) != thrd_success) { + return -1; + } + + int res; + if (thrd_join (writer, &res) != thrd_success) { + return -1; + } + if (thrd_join (reader, &res) != thrd_success) { + return -1; + } + return 0; +} + +#define _ 0 // unused parameter + +struct { + test_func test; + int param1, param2; + int test_expect; +} tests[] = { + { get_size, _, _, 8 }, + { space_available, _, _, 7 }, + { data_available, _, _, 0 }, + { write_data, 0x600dc0de, _, 1 }, + { space_available, _, _, 6 }, + { data_available, _, _, 1 }, + { acquire, 0, _, 0xdeadbeef }, // iffy test: 0 count + { space_available, _, _, 6 }, + { data_available, _, _, 1 }, + { read_data, 1, _, 0x600dc0de }, + { space_available, _, _, 7 }, + { data_available, _, _, 0 }, + { release, 0, _, 1 }, + { space_available, _, _, 7 }, + { data_available, _, _, 0 }, + { poke_data, 0, 1, 1 }, + { poke_data, 1, 2, 2 }, + { poke_data, 2, 3, 3 }, + { poke_data, 3, 4, 4 }, + { poke_data, 4, 5, 5 }, + { poke_data, 5, 6, 6 }, + { acquire, 6, _, 1 }, + { space_available, _, _, 1 }, + { data_available, _, _, 6 }, + { write_data, 7, _, 0 }, // head wrapped to 0 + { space_available, _, _, 0 }, + { data_available, _, _, 7 }, + { peek_data, 0, _, 1 }, + { peek_data, 1, _, 2 }, + { peek_data, 2, _, 3 }, + { peek_data, 3, _, 4 }, + { peek_data, 4, _, 5 }, + { peek_data, 5, _, 6 }, + { peek_data, 6, _, 7 }, + { space_available, _, _, 0 }, + { data_available, _, _, 7 }, + { init_threading, _, _, 0 }, + { run_locked, _, _, 0 }, + { compare_strings, _, _, 0 }, + { run_free, _, _, 0 }, + { compare_strings, _, _, 0 }, +}; +#define num_tests (sizeof (tests) / sizeof (tests[0])) +int test_start_line = __LINE__ - num_tests - 2; + +int +main (int argc, const char **argv) +{ + int res = 0; + + for (size_t i = 0; i < num_tests; i++) { + int test_res; + test_res = tests[i].test (tests[i].param1, tests[i].param2); + if (test_res != tests[i].test_expect) { + res |= 1; + printf ("test %zd (line %zd) failed\n", i, i + test_start_line); + printf ("expect: %d\n", tests[i].test_expect); + printf ("got : %d\n", test_res); + } + } + + return res; +} diff --git a/libs/util/test/test-seb.c b/libs/util/test/test-seb.c index 801e1ff88..a901e4f70 100644 --- a/libs/util/test/test-seb.c +++ b/libs/util/test/test-seb.c @@ -6,6 +6,8 @@ #include "QF/mersenne.h" #include "QF/sys.h" +#define SIZEOF(x) (sizeof(x) / sizeof(x[0])) + const vec3_t points[] = { {-1, -1, 1}, { 1, 1, 1}, @@ -18,6 +20,162 @@ const vec3_t points[] = { { 0, 0, 0}, }; +// This particular triangle from ad_tears caused SEB to hit its iteration +// limit because the center in affine hull test failed due to excessivly tight +// epsilon. Yes, a rather insanely small triangle for a quake map. Probably +// due to qfbsp not culling the portal for being too small. +const vec3_t tears_triangle[] = { + {2201.82007, -1262, -713.450012}, + {2201.8501, -1262, -713.593994}, + {2201.84009, -1262, -713.445007}, +}; +const vec3_t tears_triangle2[] = { + {-519.989014, 1111.94995, -2592.05005}, + {-520.002991, 1112, -2592.02002}, + {-520.005005, 1112, -2592.04004}, +}; +const vec3_t tears_triangle3[] = { + {284, 624, 2959.76001}, + {284, 624, 2960}, + {284.234985, 624.471008, 2959.53003}, +}; + +const vec3_t tears_quad[] = { + {1792, 1155.53003, 240}, + {1792, 1030.32996, 240}, + {1792.01001, 1030.32996, 240}, + {1792.01001, 1155.53003, 240}, +}; + +const vec3_t tears_quad2[] = { + {304, 3248, 3208}, + {-61.2307014, 3248, 3208}, + {-61.2307014, 3248, 3192}, + {304, 3248, 3192}, +}; + +const vec3_t tears_cluster[] = { + {551.638, -439.277008, 2804.63989}, + {551, -438, 2806.02002}, + {555.999023, -447.998993, 2805.1499}, + {555.999023, -447.998993, 2804.32007}, + {551.638, -439.277008, 2804.63989}, + {555.999023, -447.998993, 2804.32007}, + {555.999023, -447.998993, 2795.19995}, + {550.97699, -437.954987, 2806.02002}, + {551, -438, 2806.02002}, + {555.999023, -447.998993, 2795.19995}, + {555.999023, -447.998993, 2794.6499}, + {550.978027, -437.954987, 2806.02002}, + {545.945984, -442.945007, 2804.78003}, + {546, -442.998993, 2804.77002}, + {546, -443, 2804.69995}, + {546, -443, 2804.78003}, + {545.945007, -442.945007, 2804.78003}, + {556, -448, 2794.6499}, + {556, -448, 2805.1499}, + {546, -443, 2804.78003}, + {546, -443, 2804.69995}, + {550.97699, -437.954987, 2806.02002}, + {556, -448, 2794.6499}, + {546.000977, -443, 2804.69995}, + {545.945007, -442.945007, 2804.78003}, +}; + +const vec3_t tears_cluster2[] = { + {-2160, -192, -704.242004}, + {-2160, -192, -662}, + {-2160, -196, -658}, + {-2160, -220, -646}, + {-2160, -222.332001, -645.416992}, + {-2160, -320, -752}, + {-2160, -192, -752}, + {-2160, -192, -704.242004}, + {-2160, -222.332001, -645.416992}, + {-2160, -244, -640}, + {-2160, -268, -640}, + {-2160, -292, -646}, + {-2160, -316, -658}, + {-2160, -320, -662}, + {-2160, -196, -814}, + {-2160, -192, -810}, + {-2160, -192, -752}, + {-2160, -320, -752}, + {-2160, -320, -810}, + {-2160, -316, -814}, + {-2160, -312, -816}, + {-2160, -200, -816}, + {-2174, -334, -676}, + {-2186, -346, -700}, + {-2192, -352, -724}, + {-2192, -352, -748}, + {-2186, -346, -772}, + {-2174, -334, -796}, + {-2160, -319.998993, -810}, + {-2160, -319.998993, -661.999023}, + {-2174, -178, -796}, + {-2186, -166, -772}, + {-2192, -160, -748}, + {-2192, -160, -724}, + {-2186, -166, -700}, + {-2174, -178, -676}, + {-2160, -192, -662}, + {-2160, -192, -810}, + {-2224, -320, -810}, + {-2224, -330, -800}, + {-2224, -320, -800}, + {-2224, -200, -816}, + {-2224, -312, -816}, + {-2224, -316, -814}, + {-2224, -320, -810}, + {-2224, -320, -800}, + {-2224, -192, -800}, + {-2224, -192, -810}, + {-2224, -196, -814}, + {-2224, -192, -800}, + {-2224, -182, -800}, + {-2224, -192, -810}, + {-2224, -330, -800}, + {-2224, -334, -796}, + {-2224, -178, -796}, + {-2224, -182, -800}, + {-2224, -334, -796}, + {-2224, -346, -772}, + {-2224, -351, -752}, + {-2224, -161, -752}, + {-2224, -166, -772}, + {-2224, -178, -796}, + {-2224, -351, -752}, + {-2224, -352, -748}, + {-2224, -352, -724}, + {-2224, -351, -720}, + {-2224, -161, -720}, + {-2224, -160, -724}, + {-2224, -160, -748}, + {-2224, -161, -752}, + {-2224, -351, -720}, + {-2224, -346, -700}, + {-2224, -334, -676}, + {-2224, -316, -658}, + {-2224, -292, -646}, + {-2224, -268, -640}, + {-2224, -244, -640}, + {-2224, -220, -646}, + {-2224, -196, -658}, + {-2224, -178, -676}, + {-2224, -166, -700}, + {-2224, -161, -720}, +}; + +// random numbers dug this up. needed more than 6 iterations +const vec3_t cloud_points[] = { + {1.70695281, 2.60889673, 2.86233163}, + {2.11825109, 2.59883952, 1.48193693}, + {2.76728868, 1.57646465, 2.39769602}, + {1.88795376, 2.73980999, 1.58031297}, + {2.79736757, 1.89582968, 1.80514407}, +}; + struct { const vec3_t *points; int num_points; @@ -28,8 +186,24 @@ struct { {points, 2, {{ 0, 0, 1}, 1.41421356}}, {points, 3, {{-0.333333343, 0.333333343, 0.333333343}, 1.63299322}}, {points, 4, {{0, 0, 0}, 1.73205081}}, + {tears_triangle, SIZEOF (tears_triangle), + {{2201.84521, -1262, -713.519531}, 0.0747000724}},//FIXME numbers? + {tears_cluster, SIZEOF (tears_cluster), + {{552.678101, -443.460876, 2800.40405}, 8.04672813}},//FIXME numbers? + {tears_quad, SIZEOF (tears_quad), + {{1792.005, 1092.92999, 240}, 62.6000302}}, + {tears_triangle2, SIZEOF (tears_triangle2), + {{-519.995972, 1111.97498, -2592.03516}, 0.0300767105}},//FIXME numbers? + {tears_quad2, SIZEOF (tears_quad2), + {{121.384659, 3248, 3200}, 182.790497}}, + {tears_triangle3, SIZEOF (tears_triangle3), + {{284.117493, 624.235535, 2959.76489}, 0.352925777}}, + {tears_cluster2, SIZEOF (tears_cluster2), + {{-2192, -256.000031, -736}, 103.479492}},//FIXME numbers? + {cloud_points, SIZEOF (cloud_points), + {{2.20056152, 2.23369908, 2.2332375}, 0.88327992}}, }; -#define num_tests (sizeof (tests) / sizeof (tests[0])) +#define num_tests SIZEOF (tests) static inline float rnd (mtstate_t *mt) @@ -61,10 +235,10 @@ main (int argc, const char **argv) || fabs (sphere.radius - tests[i].expect.radius) > 1e-4) { res = 1; printf ("test %d failed\n", (int) i); - printf ("expect: [%.9g %.9g %.9g],%.9g\n", + printf ("expect: {%.9g, %.9g, %.9g},%.9g\n", VectorExpand (tests[i].expect.center), tests[i].expect.radius); - printf ("got : [%.9g %.9g %.9g],%.9g\n", + printf ("got : {%.9g, %.9g, %.9g},%.9g\n", VectorExpand (sphere.center), sphere.radius); } @@ -96,7 +270,7 @@ main (int argc, const char **argv) } } end = Sys_DoubleTime (); - printf ("%d itterations in %gs: %g itters/second\n", (int) i, end - start, + printf ("%d iterations in %gs: %g iters/second\n", (int) i, end - start, i / (end - start)); return res; } diff --git a/libs/util/test/test-sebvf.c b/libs/util/test/test-sebvf.c new file mode 100644 index 000000000..c6105d897 --- /dev/null +++ b/libs/util/test/test-sebvf.c @@ -0,0 +1,279 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/mathlib.h" +#include "QF/mersenne.h" +#include "QF/sys.h" +#include "QF/simd/vec4f.h" + +#define SIZEOF(x) (sizeof(x) / sizeof(x[0])) + +const vec4f_t points[] = { + {-1, -1, 1, 1}, + { 1, 1, 1, 1}, + {-1, 1, -1, 1}, + { 1, -1, -1, 1}, + {-1, -1, -1, 1}, + { 1, 1, -1, 1}, + {-1, 1, 1, 1}, + { 1, -1, 1, 1}, + { 0, 0, 0, 1}, +}; + +// This particular triangle from ad_tears caused SEB to hit its iteration +// limit because the center in affine hull test failed due to excessivly tight +// epsilon. Yes, a rather insanely small triangle for a quake map. Probably +// due to qfbsp not culling the portal for being too small. +const vec4f_t tears_triangle[] = { + {2201.82007, -1262, -713.450012, 1}, + {2201.8501, -1262, -713.593994, 1}, + {2201.84009, -1262, -713.445007, 1}, +}; +const vec4f_t tears_triangle2[] = { + {-519.989014, 1111.94995, -2592.05005, 1}, + {-520.002991, 1112, -2592.02002, 1}, + {-520.005005, 1112, -2592.04004, 1}, +}; +const vec4f_t tears_triangle3[] = { + {284, 624, 2959.76001, 1}, + {284, 624, 2960, 1}, + {284.234985, 624.471008, 2959.53003, 1}, +}; + +const vec4f_t tears_quad[] = { + {1792, 1155.53003, 240, 1}, + {1792, 1030.32996, 240, 1}, + {1792.01001, 1030.32996, 240, 1}, + {1792.01001, 1155.53003, 240, 1}, +}; + +const vec4f_t tears_quad2[] = { + {304, 3248, 3208, 1}, + {-61.2307014, 3248, 3208, 1}, + {-61.2307014, 3248, 3192, 1}, + {304, 3248, 3192, 1}, +}; + +const vec4f_t tears_cluster[] = { + {551.638, -439.277008, 2804.63989, 1}, + {551, -438, 2806.02002, 1}, + {555.999023, -447.998993, 2805.1499, 1}, + {555.999023, -447.998993, 2804.32007, 1}, + {551.638, -439.277008, 2804.63989, 1}, + {555.999023, -447.998993, 2804.32007, 1}, + {555.999023, -447.998993, 2795.19995, 1}, + {550.97699, -437.954987, 2806.02002, 1}, + {551, -438, 2806.02002, 1}, + {555.999023, -447.998993, 2795.19995, 1}, + {555.999023, -447.998993, 2794.6499, 1}, + {550.978027, -437.954987, 2806.02002, 1}, + {545.945984, -442.945007, 2804.78003, 1}, + {546, -442.998993, 2804.77002, 1}, + {546, -443, 2804.69995, 1}, + {546, -443, 2804.78003, 1}, + {545.945007, -442.945007, 2804.78003, 1}, + {556, -448, 2794.6499, 1}, + {556, -448, 2805.1499, 1}, + {546, -443, 2804.78003, 1}, + {546, -443, 2804.69995, 1}, + {550.97699, -437.954987, 2806.02002, 1}, + {556, -448, 2794.6499, 1}, + {546.000977, -443, 2804.69995, 1}, + {545.945007, -442.945007, 2804.78003, 1}, +}; + +const vec4f_t tears_cluster2[] = { + {-2160, -192, -704.242004, 1}, + {-2160, -192, -662, 1}, + {-2160, -196, -658, 1}, + {-2160, -220, -646, 1}, + {-2160, -222.332001, -645.416992, 1}, + {-2160, -320, -752, 1}, + {-2160, -192, -752, 1}, + {-2160, -192, -704.242004, 1}, + {-2160, -222.332001, -645.416992, 1}, + {-2160, -244, -640, 1}, + {-2160, -268, -640, 1}, + {-2160, -292, -646, 1}, + {-2160, -316, -658, 1}, + {-2160, -320, -662, 1}, + {-2160, -196, -814, 1}, + {-2160, -192, -810, 1}, + {-2160, -192, -752, 1}, + {-2160, -320, -752, 1}, + {-2160, -320, -810, 1}, + {-2160, -316, -814, 1}, + {-2160, -312, -816, 1}, + {-2160, -200, -816, 1}, + {-2174, -334, -676, 1}, + {-2186, -346, -700, 1}, + {-2192, -352, -724, 1}, + {-2192, -352, -748, 1}, + {-2186, -346, -772, 1}, + {-2174, -334, -796, 1}, + {-2160, -319.998993, -810, 1}, + {-2160, -319.998993, -661.999023, 1}, + {-2174, -178, -796, 1}, + {-2186, -166, -772, 1}, + {-2192, -160, -748, 1}, + {-2192, -160, -724, 1}, + {-2186, -166, -700, 1}, + {-2174, -178, -676, 1}, + {-2160, -192, -662, 1}, + {-2160, -192, -810, 1}, + {-2224, -320, -810, 1}, + {-2224, -330, -800, 1}, + {-2224, -320, -800, 1}, + {-2224, -200, -816, 1}, + {-2224, -312, -816, 1}, + {-2224, -316, -814, 1}, + {-2224, -320, -810, 1}, + {-2224, -320, -800, 1}, + {-2224, -192, -800, 1}, + {-2224, -192, -810, 1}, + {-2224, -196, -814, 1}, + {-2224, -192, -800, 1}, + {-2224, -182, -800, 1}, + {-2224, -192, -810, 1}, + {-2224, -330, -800, 1}, + {-2224, -334, -796, 1}, + {-2224, -178, -796, 1}, + {-2224, -182, -800, 1}, + {-2224, -334, -796, 1}, + {-2224, -346, -772, 1}, + {-2224, -351, -752, 1}, + {-2224, -161, -752, 1}, + {-2224, -166, -772, 1}, + {-2224, -178, -796, 1}, + {-2224, -351, -752, 1}, + {-2224, -352, -748, 1}, + {-2224, -352, -724, 1}, + {-2224, -351, -720, 1}, + {-2224, -161, -720, 1}, + {-2224, -160, -724, 1}, + {-2224, -160, -748, 1}, + {-2224, -161, -752, 1}, + {-2224, -351, -720, 1}, + {-2224, -346, -700, 1}, + {-2224, -334, -676, 1}, + {-2224, -316, -658, 1}, + {-2224, -292, -646, 1}, + {-2224, -268, -640, 1}, + {-2224, -244, -640, 1}, + {-2224, -220, -646, 1}, + {-2224, -196, -658, 1}, + {-2224, -178, -676, 1}, + {-2224, -166, -700, 1}, + {-2224, -161, -720, 1}, +}; + +// random numbers dug this up. needed more than 6 iterations +const vec4f_t cloud_points[] = { + {1.70695281, 2.60889673, 2.86233163, 1}, + {2.11825109, 2.59883952, 1.48193693, 1}, + {2.76728868, 1.57646465, 2.39769602, 1}, + {1.88795376, 2.73980999, 1.58031297, 1}, + {2.79736757, 1.89582968, 1.80514407, 1}, +}; + +struct { + const vec4f_t *points; + int num_points; + vspheref_t expect; +} tests[] = { + {0, 0, {{ 0, 0, 0, 1}, 0}}, + {points, 1, {{-1, -1, 1, 1}, 0}}, + {points, 2, {{ 0, 0, 1, 1}, 1.41421356}}, + {points, 3, {{-0.333333343, 0.333333343, 0.333333343, 1}, 1.63299322}}, + {points, 4, {{0, 0, 0, 1}, 1.73205081}}, + {tears_triangle, SIZEOF (tears_triangle), + {{2201.84521, -1262, -713.519531}, 0.0747000724}},//FIXME numbers? + {tears_cluster, SIZEOF (tears_cluster), + {{552.678101, -443.460876, 2800.40405}, 8.04672813}},//FIXME numbers? + {tears_quad, SIZEOF (tears_quad), + {{1792.005, 1092.92999, 240}, 62.6000302}}, + {tears_triangle2, SIZEOF (tears_triangle2), + {{-519.995972, 1111.97498, -2592.03516}, 0.0300767105}},//FIXME numbers? + {tears_quad2, SIZEOF (tears_quad2), + {{121.384659, 3248, 3200}, 182.790497}}, + {tears_triangle3, SIZEOF (tears_triangle3), + {{284.117493, 624.235535, 2959.76489}, 0.352925777}}, + {tears_cluster2, SIZEOF (tears_cluster2), + {{-2192, -256.000031, -736}, 103.479492}},//FIXME numbers? + {cloud_points, SIZEOF (cloud_points), + {{2.20056152, 2.23369908, 2.2332375}, 0.88327992}}, +}; +#define num_tests SIZEOF (tests) + +static inline float +rnd (mtstate_t *mt) +{ + union { + uint32_t u; + float f; + } uf; + + do { + uf.u = mtwist_rand (mt) & 0x007fffff; + } while (!uf.u); + uf.u |= 0x40000000; + return uf.f - 1.0; +} + +int +main (int argc, const char **argv) +{ + int res = 0; + size_t i, j; + vspheref_t sphere; + mtstate_t mt; + double start, end; + + for (i = 0; i < num_tests; i ++) { + sphere = SmallestEnclosingBall_vf (tests[i].points, + tests[i].num_points); + if (VectorDistance_fast (sphere.center, tests[i].expect.center) > 2e-4 + || fabs (sphere.radius - tests[i].expect.radius) > 2e-4) { + res = 1; + printf ("test %d failed\n", (int) i); + printf ("expect: {%.9g, %.9g, %.9g}, %.9g\n", + VectorExpand (tests[i].expect.center), + tests[i].expect.radius); + printf ("got : {%.9g, %.9g, %.9g}, %.9g\n", + VectorExpand (sphere.center), + sphere.radius); + } + } + + mtwist_seed (&mt, 0); + start = Sys_DoubleTime (); + for (i = 0; !res && i < 1000000; i++) { + vec4f_t cloud[10]; + vspheref_t seb; + vec_t r2; + + for (j = 0; j < 5; j++) { + VectorSet (rnd (&mt), rnd (&mt), rnd (&mt), cloud[j]); + cloud[j][3] = 1; + } + seb = SmallestEnclosingBall_vf (cloud, 5); + r2 = seb.radius * seb.radius; + for (j = 0; j < 5; j++) { + if (VectorDistance_fast (cloud[j], seb.center) - r2 + > 1e-5 * r2) { + res = 1; + printf ("%d %.9g - %.9g = %.9g\n", (int)j, + VectorDistance_fast (cloud[j], seb.center), r2, + VectorDistance_fast (cloud[j], seb.center) - r2); + printf ("[%.9g %.9g %.9g] - [%.9g %.9g %.9g] = %.9g > %.9g\n", + VectorExpand (cloud[j]), VectorExpand (seb.center), + VectorDistance_fast (cloud[j], seb.center), r2); + } + } + } + end = Sys_DoubleTime (); + printf ("%d iterations in %gs: %g iters/second\n", (int) i, end - start, + i / (end - start)); + return res; +} diff --git a/libs/util/test/test-seg.c b/libs/util/test/test-seg.c index d67001394..c898abcc6 100644 --- a/libs/util/test/test-seg.c +++ b/libs/util/test/test-seg.c @@ -11,6 +11,7 @@ #include #include "QF/segtext.h" +#include "QF/sys.h" static const char *test_string = "blah\n" @@ -83,5 +84,7 @@ main (int argc, const char **argv) res = 1; } } + Segtext_delete (st); + Sys_Shutdown (); return res; } diff --git a/libs/util/test/test-set.c b/libs/util/test/test-set.c index 3771d9f0e..4e214cae8 100644 --- a/libs/util/test/test-set.c +++ b/libs/util/test/test-set.c @@ -4,7 +4,9 @@ #include #include +#include "QF/dstring.h" #include "QF/set.h" +#include "QF/va.h" #define SIZE (SET_DEFMAP_SIZE * sizeof (set_bits_t) * 8) @@ -75,12 +77,40 @@ make_0_to_SIZEm1 (void) return set; } +static set_t * +make_range_0_SIZE (void) +{ + set_t *set = set_new (); + set_add_range (set, 0, SIZE); + return set; +} + +static set_t * +make_range_1_SIZE (void) +{ + set_t *set = set_new (); + set_add_range (set, 1, SIZE); + return set; +} + +static set_t * +remove_3_9 (set_t *set, const set_t *dummy) +{ + return set_remove_range (set, 3, 9); +} + static int check_size (const set_t *set, const set_t *unused) { return set->size; } +static int +check_count (const set_t *set, const set_t *unused) +{ + return set_count (set); +} + static set_t * make_5 (void) { @@ -107,6 +137,46 @@ make_not_55 (void) return set_invert (make_55 ()); } +static set_t * +make_0_1 (void) +{ + set_t *set = set_new (); + set_add (set, 0); + set_add (set, 1); + return set; +} + +static set_t * +make_not_1_2 (void) +{ + set_t *set = set_new (); + set_everything (set); + set_remove (set, 1); + set_remove (set, 2); + return set; +} + +static set_t * +expand_3xSIZEm1 (set_t *set, const set_t *x) +{ + set_expand (set, 3 * SIZE - 1); + return set; +} + +static set_t * +expand_3xSIZEp1 (set_t *set, const set_t *x) +{ + set_expand (set, 3 * SIZE + 1); + return set; +} + +static set_t * +expand_3xSIZE (set_t *set, const set_t *x) +{ + set_expand (set, 3 * SIZE); + return set; +} + struct { setup_func set1; setup_func set2; @@ -133,6 +203,11 @@ struct { {make_0_to_SIZEm1, make_everything, set_reverse_difference, check_size, SIZE, "{64 ...}" }, + {make_SIZE, 0, expand_3xSIZEm1, check_size, 3 * SIZE, + "{64}"}, + {make_SIZE, 0, expand_3xSIZE, check_size, 3 * SIZE, "{64}"}, + {make_SIZE, 0, expand_3xSIZEp1, check_size, + 3 * SIZE + SET_BITS, "{64}"}, {make_everything, make_empty, 0, set_is_subset, 1, 0}, {make_everything, make_empty, 0, set_is_equivalent, 0, 0}, {make_everything, make_empty, 0, set_is_intersecting, 0, 0}, @@ -170,6 +245,7 @@ struct { {make_55, make_5, set_union, set_is_equivalent, 0, "{5 55}"}, {make_55, make_5, set_union, set_is_intersecting, 1, "{5 55}"}, {make_55, make_5, set_union, set_is_disjoint, 0, "{5 55}"}, + {make_55, make_5, set_union, check_count, 2, "{5 55}"}, {make_not_SIZE, make_everything, 0, set_is_equivalent, 0, 0}, {make_not_SIZE, make_everything, 0, set_is_intersecting, 1, 0}, {make_not_SIZE, make_everything, 0, set_is_disjoint, 0, 0}, @@ -182,6 +258,64 @@ struct { {make_5, make_everything, 0, set_is_equivalent, 0, 0}, {make_5, make_everything, 0, set_is_intersecting, 1, 0}, {make_5, make_everything, 0, set_is_disjoint, 0, 0}, + {make_empty, 0, 0, check_count, 0, 0}, + {make_everything, 0, 0, check_count, 0, 0}, + {make_5, 0, 0, check_count, 1, 0}, + {make_not_5, 0, 0, check_count, 1, 0}, + {make_0_to_SIZEm1, 0, 0, check_size, SIZE, 0}, + {make_0_1, 0, 0, 0, 0, "{0 1}"}, + {make_not_1_2, 0, 0, check_count, 2, "{0 3 ...}"},//68 + {make_0_1, make_not_1_2, set_union, check_count, 1, "{0 1 3 ...}"}, + {make_0_1, make_not_1_2, set_intersection, check_count, 1, "{0}"}, + {make_0_1, make_not_1_2, set_difference, check_count, 1, "{1}"}, + {make_0_1, make_not_1_2, set_reverse_difference, check_count, 3, "{3 ...}"}, + {make_not_1_2, make_0_1, set_union, check_count, 1, "{0 1 3 ...}"}, + {make_not_1_2, make_0_1, set_intersection, check_count, 1, "{0}"}, + {make_not_1_2, make_0_1, set_difference, check_count, 3, "{3 ...}"}, + {make_not_1_2, make_0_1, set_reverse_difference, check_count, 1, "{1}"},//76 + {make_SIZE, make_not_1_2, set_union, check_size, SIZE + SET_BITS, + "{0 3 ...}"}, + {make_SIZE, make_not_1_2, set_intersection, check_size, SIZE + SET_BITS, + "{64}"}, + {make_SIZE, make_not_1_2, set_difference, check_size, SIZE + SET_BITS, + "{}"}, + {make_SIZE, make_not_1_2, set_reverse_difference, check_size, + SIZE + SET_BITS, + "{0 3 4 5 6 7 8 9 10 11 12 13 14 15" + " 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31" + " 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47" + " 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 65 ...}" + },//80 + {make_not_1_2, make_SIZE, set_union, check_size, SIZE, "{0 3 ...}"}, + {make_not_1_2, make_SIZE, set_intersection, check_size, SIZE + SET_BITS, + "{64}"}, + {make_not_1_2, make_SIZE, set_difference, check_size, SIZE + SET_BITS, + "{0 3 4 5 6 7 8 9 10 11 12 13 14 15" + " 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31" + " 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47" + " 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 65 ...}" + }, + {make_not_1_2, make_SIZE, set_reverse_difference, check_size, SIZE, "{}"}, + {make_range_0_SIZE, 0, 0, check_size, SIZE, + "{0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15" + " 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31" + " 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47" + " 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63}" + }, + {make_range_1_SIZE, 0, 0, check_size, SIZE + SET_BITS, + "{1 2 3 4 5 6 7 8 9 10 11 12 13 14 15" + " 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31" + " 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47" + " 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63" + " 64}" + }, + {make_everything, 0, remove_3_9, check_size, SIZE, "{0 1 2 12 ...}"}, + {make_range_0_SIZE, 0, remove_3_9, check_size, SIZE, + "{0 1 2 12 13 14 15" + " 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31" + " 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47" + " 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63}" + }, }; #define num_tests (sizeof (tests) / sizeof (tests[0])) @@ -190,6 +324,35 @@ main (int argc, const char **argv) { size_t i; int res = 0; + dstring_t *str; + + //printf ("set_bits_t: %d, SET_DEFMAP_SIZE: %d, SIZE: %d\n", + // sizeof (set_bits_t), SET_DEFMAP_SIZE, SIZE); + + tests[5].str_expect = nva ("{%zd}", SIZE); + tests[7].str_expect = nva ("{%zd ...}", SIZE); + tests[8].str_expect = tests[7].str_expect; + tests[9].str_expect = tests[5].str_expect; + tests[10].str_expect = tests[5].str_expect; + tests[11].str_expect = tests[5].str_expect; + tests[78].str_expect = tests[5].str_expect; + tests[82].str_expect = tests[5].str_expect; + + str = dstring_new (); + for (i = 0; i < SIZE; i++) { + dasprintf (str, "%c%zd", i ? ' ' : '{', i); + } + dstring_appendstr (str, "}"); + tests[6].str_expect = dstring_freeze (str); + + str = dstring_new (); + dasprintf (str, "{0"); + for (i = 3; i < SIZE; i++) { + dasprintf (str, " %zd", i); + } + dasprintf (str, " %zd ...}", SIZE + 1); + tests[80].str_expect = dstring_freeze (str); + tests[83].str_expect = tests[80].str_expect; for (i = 0; i < num_tests; i++) { set_t *s1, *s2 = 0; diff --git a/libs/util/test/test-simd.c b/libs/util/test/test-simd.c new file mode 100644 index 000000000..dd675ae77 --- /dev/null +++ b/libs/util/test/test-simd.c @@ -0,0 +1,715 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#include "QF/mathlib.h" +#include "QF/simd/vec4d.h" +#include "QF/simd/vec4f.h" +#include "QF/simd/mat4f.h" + +#define right { 1, 0, 0 } +#define forward { 0, 1, 0 } +#define up { 0, 0, 1 } +#define one { 1, 1, 1, 1 } +#define half { 0.5, 0.5, 0.5, 0.5 } +#define zero { 0, 0, 0, 0 } +#define qident { 0, 0, 0, 1 } +#define qtest { 0.64, 0.48, 0, 0.6 } + +#define nright { -1, 0, 0 } +#define nforward { 0, -1, 0 } +#define nup { 0, 0, -1 } +#define none { -1, -1, -1, -1 } +#define nqident { 0, 0, 0, -1 } + +#define pi { M_PI, M_PI, M_PI, M_PI } // lots of bits +#define pmpi { -M_PI, M_PI, -M_PI, M_PI } // lots of bits + +#define identity \ + { { 1, 0, 0, 0 }, \ + { 0, 1, 0, 0 }, \ + { 0, 0, 1, 0 }, \ + { 0, 0, 0, 1 } } +#define rotate120 \ + { { 0, 1, 0, 0 }, \ + { 0, 0, 1, 0 }, \ + { 1, 0, 0, 0 }, \ + { 0, 0, 0, 1 } } +#define rotate240 \ + { { 0, 0, 1, 0 }, \ + { 1, 0, 0, 0 }, \ + { 0, 1, 0, 0 }, \ + { 0, 0, 0, 1 } } + +#define s05 0.70710678118654757 + +typedef struct { + int line; + vec4d_t (*op) (vec4d_t a, vec4d_t b); + vec4d_t a; + vec4d_t b; + vec4d_t expect; + vec4d_t ulp_errors; +} vec4d_test_t; + +typedef struct { + int line; + vec4f_t (*op) (vec4f_t a, vec4f_t b); + vec4f_t a; + vec4f_t b; + vec4f_t expect; + vec4f_t ulp_errors; +} vec4f_test_t; + +typedef struct { + int line; + void (*op) (mat4f_t c, const mat4f_t a, const mat4f_t b); + mat4f_t a; + mat4f_t b; + mat4f_t expect; + mat4f_t ulp_errors; +} mat4f_test_t; + +typedef struct { + int line; + vec4f_t (*op) (const mat4f_t a, vec4f_t b); + mat4f_t a; + vec4f_t b; + vec4f_t expect; + vec4f_t ulp_errors; +} mv4f_test_t; + +typedef struct { + int line; + void (*op) (mat4f_t m, vec4f_t q); + vec4f_t q; + mat4f_t expect; + mat4f_t ulp_errors; +} mq4f_test_t; + +static vec4d_t tvtruncd (vec4d_t v, vec4d_t ignore) +{ + return vtrunc4d (v); +} + +static vec4d_t tvceild (vec4d_t v, vec4d_t ignore) +{ + return vceil4d (v); +} + +static vec4d_t tvfloord (vec4d_t v, vec4d_t ignore) +{ + return vfloor4d (v); +} + +static vec4d_t tqconjd (vec4d_t v, vec4d_t ignore) +{ + return qconjd (v); +} + +static vec4f_t tvtruncf (vec4f_t v, vec4f_t ignore) +{ + return vtrunc4f (v); +} + +static vec4f_t tvceilf (vec4f_t v, vec4f_t ignore) +{ + return vceil4f (v); +} + +static vec4f_t tvfloorf (vec4f_t v, vec4f_t ignore) +{ + return vfloor4f (v); +} + +static vec4f_t tqconjf (vec4f_t v, vec4f_t ignore) +{ + return qconjf (v); +} + +static vec4f_t tvabsf (vec4f_t v, vec4f_t ignore) +{ + return vabs4f (v); +} + +static vec4f_t tvsqrtf (vec4f_t v, vec4f_t ignore) +{ + return vsqrt4f (v); +} + +static vec4f_t tmagnitudef (vec4f_t v, vec4f_t ignore) +{ + return magnitudef (v); +} + +static vec4f_t tmagnitude3f (vec4f_t v, vec4f_t ignore) +{ + return magnitude3f (v); +} + +#define T(t...) { __LINE__, t } + +static vec4d_test_t vec4d_tests[] = { + // 3D dot products + T(dotd, right, right, one ), + T(dotd, right, forward, zero ), + T(dotd, right, up, zero ), + T(dotd, forward, right, zero ), + T(dotd, forward, forward, one ), + T(dotd, forward, up, zero ), + T(dotd, up, right, zero ), + T(dotd, up, forward, zero ), + T(dotd, up, up, one ), + + // one is 4D, so its self dot product is 4 + T(dotd, one, one, { 4, 4, 4, 4} ), + T(dotd, one, none, {-4, -4, -4, -4} ), + + // 3D cross products + T(crossd, right, right, zero ), + T(crossd, right, forward, up ), + T(crossd, right, up, nforward ), + T(crossd, forward, right, nup ), + T(crossd, forward, forward, zero ), + T(crossd, forward, up, right ), + T(crossd, up, right, forward ), + T(crossd, up, forward, nright ), + T(crossd, up, up, zero ), + // double whammy tests: cross product with an angled vector and + // ensuring that a 4d vector (non-zero w component) does not affect + // the result, including the result's w component remaining zero. + T(crossd, right, one, { 0, -1, 1} ), + T(crossd, forward, one, { 1, 0, -1} ), + T(crossd, up, one, {-1, 1, 0} ), + T(crossd, one, right, { 0, 1, -1} ), + T(crossd, one, forward, {-1, 0, 1} ), + T(crossd, one, up, { 1, -1, 0} ), + // This one fails when optimizing with -mfma (which is why fma is not + // used): ulp errors in z and w + T(crossd, qtest, qtest, {0, 0, 0, 0}, +#if defined(__aarch64__) +# ifdef __OPTIMIZE__ + {0, 0, -2.1938006966593093e-17, 1.3322676295501878e-17}, +# endif +#endif + ), + + T(qmuld, qident, qident, qident ), + T(qmuld, qident, right, right ), + T(qmuld, qident, forward, forward ), + T(qmuld, qident, up, up ), + T(qmuld, right, qident, right ), + T(qmuld, forward, qident, forward ), + T(qmuld, up, qident, up ), + T(qmuld, right, right, nqident ), + T(qmuld, right, forward, up ), + T(qmuld, right, up, nforward ), + T(qmuld, forward, right, nup ), + T(qmuld, forward, forward, nqident ), + T(qmuld, forward, up, right ), + T(qmuld, up, right, forward ), + T(qmuld, up, forward, nright ), + T(qmuld, up, up, nqident ), + T(qmuld, one, one, { 2, 2, 2, -2 } ), + T(qmuld, one, { 2, 2, 2, -2 }, { 0, 0, 0, -8 } ), + // This one fails when optimizing with -mfma (which is why fma is not + // used): ulp error in z + T(qmuld, qtest, qtest, {0.768, 0.576, 0, -0.28}, +#if defined(__aarch64__) +# ifdef __OPTIMIZE__ + {0, 0, -2.1938006966593093e-17, 0}, +# endif +#endif + ), + + // The one vector is not unit (magnitude 2), so using it as a rotation + // quaternion results in scaling by 4. However, it still has the effect + // of rotating 120 degrees around the axis equidistant from the three + // orthogonal axes such that x->y->z->x + T(qvmuld, one, right, { 0, 4, 0, 0 } ), + T(qvmuld, one, forward, { 0, 0, 4, 0 } ), + T(qvmuld, one, up, { 4, 0, 0, 0 } ), + T(qvmuld, one, {1,1,1,0}, { 4, 4, 4, 0 } ), + T(qvmuld, one, one, { 4, 4, 4, -2 } ), + // inverse rotation, so x->z->y->x + T(vqmuld, right, one, { 0, 0, 4, 0 } ), + T(vqmuld, forward, one, { 4, 0, 0, 0 } ), + T(vqmuld, up, one, { 0, 4, 0, 0 } ), + T(vqmuld, {1,1,1,0}, one, { 4, 4, 4, 0 } ), + T(vqmuld, one, one, { 4, 4, 4, -2 } ), + // The half vector is unit. + T(qvmuld, half, right, forward ), + T(qvmuld, half, forward, up ), + T(qvmuld, half, up, right ), + T(qvmuld, half, {1,1,1,0}, { 1, 1, 1, 0 } ), + // inverse + T(vqmuld, right, half, up ), + T(vqmuld, forward, half, right ), + T(vqmuld, up, half, forward ), + T(vqmuld, {1,1,1,0}, half, { 1, 1, 1, 0 } ), + // one is a 4D vector and qvmuld is meant for 3D vectors. However, it + // seems that the vector's w has no effect on the 3d portion of the + // result, but the result's w is cosine of the full rotation angle + // scaled by quaternion magnitude and vector w + T(qvmuld, half, one, { 1, 1, 1, -0.5 } ), + T(qvmuld, half, {2,2,2,2}, { 2, 2, 2, -1 } ), + T(qvmuld, qtest, right, {0.5392, 0.6144, -0.576, 0} ), + T(qvmuld, qtest, forward, {0.6144, 0.1808, 0.768, 0}, + {0, -2.7e-17, 0, 0} ), + T(qvmuld, qtest, up, {0.576, -0.768, -0.28, 0} ), + // inverse + T(vqmuld, one, half, { 1, 1, 1, -0.5 } ), + T(vqmuld, {2,2,2,2}, half, { 2, 2, 2, -1 } ), + T(vqmuld, right, qtest, {0.5392, 0.6144, 0.576, 0} ), + T(vqmuld, forward, qtest, {0.6144, 0.1808, -0.768, 0}, + {0, -2.7e-17, 0, 0} ), + T(vqmuld, up, qtest, {-0.576, 0.768, -0.28, 0} ), + + T(qrotd, right, right, qident ), + T(qrotd, right, forward, { 0, 0, s05, s05 }, + {0, 0, -1.1e-16, 0} ), + T(qrotd, right, up, { 0, -s05, 0, s05 }, + {0, 1.1e-16, 0, 0} ), + T(qrotd, forward, right, { 0, 0, -s05, s05 }, + {0, 0, 1.1e-16, 0} ), + T(qrotd, forward, forward, qident ), + T(qrotd, forward, up, { s05, 0, 0, s05 }, + {-1.1e-16, 0, 0, 0} ), + T(qrotd, up, right, { 0, s05, 0, s05 }, + {0, -1.1e-16, 0, 0} ), + T(qrotd, up, forward, { -s05, 0, 0, s05 }, + { 1.1e-16, 0, 0, 0} ), + T(qrotd, up, up, qident ), + + T(tvtruncd, { 1.1, 2.9, -1.1, -2.9 }, {}, { 1, 2, -1, -2 } ), + T(tvceild, { 1.1, 2.9, -1.1, -2.9 }, {}, { 2, 3, -1, -2 } ), + T(tvfloord, { 1.1, 2.9, -1.1, -2.9 }, {}, { 1, 2, -2, -3 } ), + T(tqconjd, one, {}, { -1, -1, -1, 1 } ), +}; +#define num_vec4d_tests (sizeof (vec4d_tests) / (sizeof (vec4d_tests[0]))) + +static vec4f_test_t vec4f_tests[] = { + // 3D dot products + T(dotf, right, right, one ), + T(dotf, right, forward, zero ), + T(dotf, right, up, zero ), + T(dotf, forward, right, zero ), + T(dotf, forward, forward, one ), + T(dotf, forward, up, zero ), + T(dotf, up, right, zero ), + T(dotf, up, forward, zero ), + T(dotf, up, up, one ), + + // one is 4D, so its self dot product is 4 + T(dotf, one, one, { 4, 4, 4, 4} ), + T(dotf, one, none, {-4, -4, -4, -4} ), + + // 3D cross products + T(crossf, right, right, zero ), + T(crossf, right, forward, up ), + T(crossf, right, up, nforward ), + T(crossf, forward, right, nup ), + T(crossf, forward, forward, zero ), + T(crossf, forward, up, right ), + T(crossf, up, right, forward ), + T(crossf, up, forward, nright ), + T(crossf, up, up, zero ), + // double whammy tests: cross product with an angled vector and + // ensuring that a 4d vector (non-zero w component) does not affect + // the result, including the result's w component remaining zero. + T(crossf, right, one, { 0, -1, 1} ), + T(crossf, forward, one, { 1, 0, -1} ), + T(crossf, up, one, {-1, 1, 0} ), + T(crossf, one, right, { 0, 1, -1} ), + T(crossf, one, forward, {-1, 0, 1} ), + T(crossf, one, up, { 1, -1, 0} ), +#ifdef __aarch64__ +# ifdef __OPTIMIZE__ + T(crossf, qtest, qtest, {0, 0, -1.47819534e-09, -1.43051153e-08} ), +# else + T(crossf, qtest, qtest, {0, 0, 0, 0} ), +# endif +#else +# if !defined(__SSE__) && !defined(__OPTIMIZE__) + // when not optimizing and SSE is not available (but ok when + // optimizing) + T(crossf, qtest, qtest, {0, 0, -1.47819534e-09, 0} ), +# else + T(crossf, qtest, qtest, {0, 0, 0, 0} ), +# endif +#endif + + T(qmulf, qident, qident, qident ), + T(qmulf, qident, right, right ), + T(qmulf, qident, forward, forward ), + T(qmulf, qident, up, up ), + T(qmulf, right, qident, right ), + T(qmulf, forward, qident, forward ), + T(qmulf, up, qident, up ), + T(qmulf, right, right, nqident ), + T(qmulf, right, forward, up ), + T(qmulf, right, up, nforward ), + T(qmulf, forward, right, nup ), + T(qmulf, forward, forward, nqident ), + T(qmulf, forward, up, right ), + T(qmulf, up, right, forward ), + T(qmulf, up, forward, nright ), + T(qmulf, up, up, nqident ), + T(qmulf, one, one, { 2, 2, 2, -2 } ), + T(qmulf, one, { 2, 2, 2, -2 }, { 0, 0, 0, -8 } ), + T(qmulf, qtest, qtest, {0.768, 0.576, 0, -0.28}, +#ifdef __aarch64__ +# ifdef __OPTIMIZE__ + {0, 6e-8, -1.47819534e-09, 2.98023224e-08} +# else + {0, 5.96046448e-08, 0, 2.98023224e-08} +# endif +#else +# if !defined(__SSE__) && !defined(__OPTIMIZE__) + // when not optimizing and SSE is not available (but ok when + // optimizing) + {0, 6e-8, -1.47819534e-09, 3e-8} +# elif !defined(__SSE__) + {0, 6e-8, 0, 6e-8} +# else + {0, 6e-8, 0, 3e-8} +#endif +#endif + ), + + // The one vector is not unit (magnitude 2), so using it as a rotation + // quaternion results in scaling by 4. However, it still has the effect + // of rotating 120 degrees around the axis equidistant from the three + // orthogonal axes such that x->y->z->x + T(qvmulf, one, right, { 0, 4, 0, 0 } ), + T(qvmulf, one, forward, { 0, 0, 4, 0 } ), + T(qvmulf, one, up, { 4, 0, 0, 0 } ), + T(qvmulf, one, {1,1,1,0}, { 4, 4, 4, 0 } ), + T(qvmulf, one, one, { 4, 4, 4, -2 } ), + // inverse rotation, so x->z->y->x + T(vqmulf, right, one, { 0, 0, 4, 0 } ), + T(vqmulf, forward, one, { 4, 0, 0, 0 } ), + T(vqmulf, up, one, { 0, 4, 0, 0 } ), + T(vqmulf, {1,1,1,0}, one, { 4, 4, 4, 0 } ), + T(vqmulf, one, one, { 4, 4, 4, -2 } ), + // + T(qvmulf, qtest, right, {0.5392, 0.6144, -0.576, 0}, + {0, -5.9e-8, -6e-8, 0} ), + T(qvmulf, qtest, forward, {0.6144, 0.1808, 0.768, 0}, +#if !(defined(__SSE__) || defined(__aarch64__)) && !defined(__OPTIMIZE__) + {-5.9e-8, 0, 0, 0} +#elif !(defined(__SSE__) || defined(__aarch64__)) + {-5.9e-8, 3e-8, 0, 0} +#else + {-5.9e-8, 1.5e-8, 0, 0} +#endif + ), + T(qvmulf, qtest, up, {0.576, -0.768, -0.28, 0}, +#if !(defined(__SSE__) || defined(__aarch64__)) && !defined(__OPTIMIZE__) + {6e-8, 0, 3e-8, 0} +#elif !(defined(__SSE__) || defined(__aarch64__)) + {6e-8, 0, 6e-8, 0} +#else + {6e-8, 0, 3e-8, 0} +#endif + ), + T(vqmulf, right, qtest, {0.5392, 0.6144, 0.576, 0}, + {0, -5.9e-8, 5.9e-8, 0} ), + T(vqmulf, forward, qtest, {0.6144, 0.1808, -0.768, 0}, +#if !(defined(__SSE__) || defined(__aarch64__)) && !defined(__OPTIMIZE__) + {-5.9e-8, 0, 0, 0} +#elif !(defined(__SSE__) || defined(__aarch64__)) + {-5.9e-8, 3e-8, 0, 0} +#else + {-5.9e-8, 1.5e-8, 0, 0} +#endif + ), + T(vqmulf, up, qtest, {-0.576, 0.768, -0.28, 0}, +#if !(defined(__SSE__) || defined(__aarch64__)) && !defined(__OPTIMIZE__) + {-5.9e-8, 0, 3e-8, 0} +#elif !(defined(__SSE__) || defined(__aarch64__)) + {-5.9e-8, 0, 6e-8, 0} +#else + {-5.9e-8, 0, 3e-8, 0} +#endif + ), + + T(qrotf, right, right, qident ), + T(qrotf, right, forward, { 0, 0, s05, s05 } ), + T(qrotf, right, up, { 0, -s05, 0, s05 } ), + T(qrotf, forward, right, { 0, 0, -s05, s05 } ), + T(qrotf, forward, forward, qident ), + T(qrotf, forward, up, { s05, 0, 0, s05 } ), + T(qrotf, up, right, { 0, s05, 0, s05 } ), + T(qrotf, up, forward, { -s05, 0, 0, s05 } ), + T(qrotf, up, up, qident ), + + T(tvabsf, pmpi, {}, pi ), + T(tvsqrtf, { 1, 4, 9, 16}, {}, {1, 2, 3, 4} ), + T(tvtruncf, { 1.1, 2.9, -1.1, -2.9 }, {}, { 1, 2, -1, -2 } ), + T(tvceilf, { 1.1, 2.9, -1.1, -2.9 }, {}, { 2, 3, -1, -2 } ), + T(tvfloorf, { 1.1, 2.9, -1.1, -2.9 }, {}, { 1, 2, -2, -3 } ), + T(tqconjf, one, {}, { -1, -1, -1, 1 } ), + T(tmagnitudef, { 3, 4, 12, 84}, {}, {85, 85, 85, 85} ), + T(tmagnitudef, { 3, 4, 12, -84}, {}, {85, 85, 85, 85} ), + T(tmagnitudef, { 3, 4, -12, 84}, {}, {85, 85, 85, 85} ), + T(tmagnitudef, { 3, 4, -12, -84}, {}, {85, 85, 85, 85} ), + T(tmagnitudef, { 3, -4, 12, 84}, {}, {85, 85, 85, 85} ), + T(tmagnitudef, { 3, -4, 12, -84}, {}, {85, 85, 85, 85} ), + T(tmagnitudef, { 3, -4, -12, 84}, {}, {85, 85, 85, 85} ), + T(tmagnitudef, { 3, -4, -12, -84}, {}, {85, 85, 85, 85} ), + T(tmagnitudef, { -3, 4, 12, 84}, {}, {85, 85, 85, 85} ), + T(tmagnitudef, { -3, 4, 12, -84}, {}, {85, 85, 85, 85} ), + T(tmagnitudef, { -3, 4, -12, 84}, {}, {85, 85, 85, 85} ), + T(tmagnitudef, { -3, 4, -12, -84}, {}, {85, 85, 85, 85} ), + T(tmagnitudef, { -3, -4, 12, 84}, {}, {85, 85, 85, 85} ), + T(tmagnitudef, { -3, -4, 12, -84}, {}, {85, 85, 85, 85} ), + T(tmagnitudef, { -3, -4, -12, 84}, {}, {85, 85, 85, 85} ), + T(tmagnitudef, { -3, -4, -12, -84}, {}, {85, 85, 85, 85} ), + T(tmagnitude3f, { -3, -4, -12, -84}, {}, {13, 13, 13, 13} ), +}; +#define num_vec4f_tests (sizeof (vec4f_tests) / (sizeof (vec4f_tests[0]))) + +static mat4f_test_t mat4f_tests[] = { + T(mmulf, identity, identity, identity ), + T(mmulf, rotate120, identity, rotate120 ), + T(mmulf, identity, rotate120, rotate120 ), + T(mmulf, rotate120, rotate120, rotate240 ), + T(mmulf, rotate120, rotate240, identity ), + T(mmulf, rotate240, rotate120, identity ), +}; +#define num_mat4f_tests (sizeof (mat4f_tests) / (sizeof (mat4f_tests[0]))) + +static mv4f_test_t mv4f_tests[] = { + T(mvmulf, identity, { 1, 0, 0, 0 }, { 1, 0, 0, 0 } ), + T(mvmulf, identity, { 0, 1, 0, 0 }, { 0, 1, 0, 0 } ), + T(mvmulf, identity, { 0, 0, 1, 0 }, { 0, 0, 1, 0 } ), + T(mvmulf, identity, { 0, 0, 0, 1 }, { 0, 0, 0, 1 } ), + T(mvmulf, rotate120, { 1, 2, 3, 4 }, { 3, 1, 2, 4 } ), + T(mvmulf, rotate240, { 1, 2, 3, 4 }, { 2, 3, 1, 4 } ), +}; +#define num_mv4f_tests (sizeof (mv4f_tests) / (sizeof (mv4f_tests[0]))) + +// expect filled in using non-simd QuatToMatrix (has its own tests) +static mq4f_test_t mq4f_tests[] = { + T(mat4fquat, { 0, 0, 0, 1 } ), + T(mat4fquat, { 0.5, 0.5, 0.5, 0.5 } ), + T(mat4fquat, { 0.5, 0.5, -0.5, 0.5 } ), + T(mat4fquat, { 0.5, -0.5, 0.5, 0.5 } ), + T(mat4fquat, { 0.5, -0.5, -0.5, 0.5 } ), + T(mat4fquat, { -0.5, 0.5, 0.5, 0.5 } ), + T(mat4fquat, { -0.5, 0.5, -0.5, 0.5 } ), + T(mat4fquat, { -0.5, -0.5, 0.5, 0.5 } ), + T(mat4fquat, { -0.5, -0.5, -0.5, 0.5 } ), +}; +#define num_mq4f_tests (sizeof (mq4f_tests) / (sizeof (mq4f_tests[0]))) + +static int +run_vec4d_tests (void) +{ + int ret = 0; + + for (size_t i = 0; i < num_vec4d_tests; i++) { + __auto_type test = &vec4d_tests[i]; + vec4d_t result = test->op (test->a, test->b); + vec4d_t expect = test->expect + test->ulp_errors; + vec4l_t res = result != expect; + if (res[0] || res[1] || res[2] || res[3]) { + ret |= 1; + printf ("\nrun_vec4d_tests %zd, line %d\n", i, test->line); + printf ("a: " VEC4D_FMT "\n", VEC4_EXP(test->a)); + printf ("b: " VEC4D_FMT "\n", VEC4_EXP(test->b)); + printf ("r: " VEC4D_FMT "\n", VEC4_EXP(result)); + printf ("t: " VEC4L_FMT "\n", VEC4_EXP(res)); + printf ("E: " VEC4D_FMT "\n", VEC4_EXP(expect)); + printf ("e: " VEC4D_FMT "\n", VEC4_EXP(test->expect)); + printf ("u: " VEC4D_FMT "\n", VEC4_EXP(test->ulp_errors)); + printf ("U: " VEC4D_FMT "\n", VEC4_EXP(result - test->expect)); + } + } + return ret; +} + +static int +run_vec4f_tests (void) +{ + int ret = 0; + + for (size_t i = 0; i < num_vec4f_tests; i++) { + __auto_type test = &vec4f_tests[i]; + vec4f_t result = test->op (test->a, test->b); + vec4f_t expect = test->expect + test->ulp_errors; + vec4i_t res = (vec4i_t) result != (vec4i_t) expect; + if (res[0] || res[1] || res[2] || res[3]) { + ret |= 1; + printf ("\nrun_vec4f_tests %zd, line %d\n", i, test->line); + printf ("a: " VEC4F_FMT "\n", VEC4_EXP(test->a)); + printf ("b: " VEC4F_FMT "\n", VEC4_EXP(test->b)); + printf ("r: " VEC4F_FMT "\n", VEC4_EXP(result)); + printf ("t: " VEC4I_FMT "\n", VEC4_EXP(res)); + printf ("E: " VEC4F_FMT "\n", VEC4_EXP(expect)); + printf ("e: " VEC4F_FMT "\n", VEC4_EXP(test->expect)); + printf ("u: " VEC4F_FMT "\n", VEC4_EXP(test->ulp_errors)); + printf ("U: " VEC4F_FMT "\n", VEC4_EXP(result - test->expect)); + } + } + return ret; +} + +static int +run_mat4f_tests (void) +{ + int ret = 0; + + for (size_t i = 0; i < num_mat4f_tests; i++) { + __auto_type test = &mat4f_tests[i]; + mat4f_t result; + mat4f_t expect; + mat4i_t res = {}; + + test->op (result, test->a, test->b); + maddf (expect, test->expect, test->ulp_errors); + + int fail = 0; + for (int j = 0; j < 4; j++) { + res[j] = result[j] != expect[j]; + fail |= res[j][0] || res[j][1] || res[j][2] || res[j][3]; + } + if (fail) { + ret |= 1; + printf ("\nrun_mat4f_tests %zd, line %d\n", i, test->line); + printf ("a: " VEC4F_FMT "\n", MAT4_ROW(test->a, 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->a, 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->a, 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->a, 3)); + printf ("b: " VEC4F_FMT "\n", MAT4_ROW(test->b, 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->b, 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->b, 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->b, 3)); + printf ("r: " VEC4F_FMT "\n", MAT4_ROW(result, 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(result, 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(result, 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(result, 3)); + printf ("t: " VEC4I_FMT "\n", MAT4_ROW(res, 0)); + printf (" " VEC4I_FMT "\n", MAT4_ROW(res, 1)); + printf (" " VEC4I_FMT "\n", MAT4_ROW(res, 2)); + printf (" " VEC4I_FMT "\n", MAT4_ROW(res, 3)); + printf ("E: " VEC4F_FMT "\n", MAT4_ROW(expect, 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(expect, 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(expect, 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(expect, 3)); + printf ("e: " VEC4F_FMT "\n", MAT4_ROW(test->expect, 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->expect, 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->expect, 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->expect, 3)); + printf ("u: " VEC4F_FMT "\n", MAT4_ROW(test->ulp_errors, 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->ulp_errors, 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->ulp_errors, 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->ulp_errors, 3)); + } + } + return ret; +} + +static int +run_mv4f_tests (void) +{ + int ret = 0; + + for (size_t i = 0; i < num_mv4f_tests; i++) { + __auto_type test = &mv4f_tests[i]; + vec4f_t result = test->op (test->a, test->b); + vec4f_t expect = test->expect + test->ulp_errors; + vec4i_t res = result != expect; + + if (res[0] || res[1] || res[2] || res[3]) { + ret |= 1; + printf ("\nrun_mv4f_tests %zd, line %d\n", i, test->line); + printf ("a: " VEC4F_FMT "\n", MAT4_ROW(test->a, 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->a, 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->a, 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->a, 3)); + printf ("b: " VEC4F_FMT "\n", VEC4_EXP(test->b)); + printf ("r: " VEC4F_FMT "\n", VEC4_EXP(result)); + printf ("t: " VEC4I_FMT "\n", VEC4_EXP(res)); + printf ("E: " VEC4F_FMT "\n", VEC4_EXP(expect)); + printf ("e: " VEC4F_FMT "\n", VEC4_EXP(test->expect)); + printf ("u: " VEC4F_FMT "\n", VEC4_EXP(test->ulp_errors)); + } + } + return ret; +} + +static int +run_mq4f_tests (void) +{ + int ret = 0; + + for (size_t i = 0; i < num_mq4f_tests; i++) { + __auto_type test = &mq4f_tests[i]; + quat_t q; + vec_t m[16]; + memcpy (q, &test->q, sizeof (quat_t)); + QuatToMatrix (q, m, 1, 1); + memcpy (&test->expect, m, sizeof (mat4f_t)); + } + for (size_t i = 0; i < num_mq4f_tests; i++) { + __auto_type test = &mq4f_tests[i]; + mat4f_t result; + mat4f_t expect; + mat4i_t res = {}; + + test->op (result, test->q); + maddf (expect, test->expect, test->ulp_errors); + memcpy (expect, (void *) &test->expect, sizeof (mat4f_t)); + + int fail = 0; + for (int j = 0; j < 4; j++) { + res[j] = result[j] != expect[j]; + fail |= res[j][0] || res[j][1] || res[j][2] || res[j][3]; + } + if (fail) { + ret |= 1; + printf ("\nrun_mq4f_tests %zd, line %d\n", i, test->line); + printf ("q: " VEC4F_FMT "\n", VEC4_EXP(test->q)); + printf ("r: " VEC4F_FMT "\n", MAT4_ROW(result, 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(result, 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(result, 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(result, 3)); + printf ("t: " VEC4I_FMT "\n", MAT4_ROW(res, 0)); + printf (" " VEC4I_FMT "\n", MAT4_ROW(res, 1)); + printf (" " VEC4I_FMT "\n", MAT4_ROW(res, 2)); + printf (" " VEC4I_FMT "\n", MAT4_ROW(res, 3)); + printf ("E: " VEC4F_FMT "\n", MAT4_ROW(expect, 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(expect, 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(expect, 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(expect, 3)); + printf ("e: " VEC4F_FMT "\n", MAT4_ROW(test->expect, 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->expect, 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->expect, 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->expect, 3)); + printf ("u: " VEC4F_FMT "\n", MAT4_ROW(test->ulp_errors, 0)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->ulp_errors, 1)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->ulp_errors, 2)); + printf (" " VEC4F_FMT "\n", MAT4_ROW(test->ulp_errors, 3)); + } + } + return ret; +} + +int +main (void) +{ + int ret = 0; + ret |= run_vec4d_tests (); + ret |= run_vec4f_tests (); + ret |= run_mat4f_tests (); + ret |= run_mv4f_tests (); + ret |= run_mq4f_tests (); + return ret; +} diff --git a/libs/util/test/test-utf8.c b/libs/util/test/test-utf8.c new file mode 100644 index 000000000..434d1250d --- /dev/null +++ b/libs/util/test/test-utf8.c @@ -0,0 +1,113 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#include "QF/msg.h" +#include "QF/sizebuf.h" + +static byte buffer[1024]; +static sizebuf_t sb = { .data = buffer, .maxsize = sizeof (buffer) }; +static qmsg_t msg = { .message = &sb, }; + +typedef struct { + int32_t input; + unsigned bytes; +} utf8_test_t; + +static utf8_test_t tests[] = { + {0x43214321, 6}, + {0x55555555, 6}, + {0x2aaaaaaa, 6}, + {0x55aa55aa, 6}, + {0x1df001ed, 6}, // hey, why not? good bit pattern + {0x089abcde, 6}, + {0x049abcde, 6}, + {0x023abcde, 5}, + {0x012abcde, 5}, + {0x008abcde, 5}, + {0x004abcde, 5}, + {0x002abcde, 5}, + {0x001abcde, 4}, + {0x000abcde, 4}, + {0x0006bcde, 4}, + {0x0002bcde, 4}, + {0x0001bcde, 4}, + {0x0000bcde, 3}, + {0x00007cde, 3}, + {0x00003cde, 3}, + {0x00001cde, 3}, + {0x00000cde, 3}, + {0x000004de, 2}, + {0x000002de, 2}, + {0x000001de, 2}, + {0x000000de, 2}, + {0x0000005e, 1}, + {0x0000004e, 1}, + {0x0000002e, 1}, + {0x0000001e, 1}, + {0x0000000e, 1}, + {0x00000006, 1}, + {0x00000000, 1}, + {0x40000000, 6}, + {0x20000000, 6}, + {0x10000000, 6}, + {0x08000000, 6}, + {0x04000000, 6}, + {0x02000000, 5}, + {0x01000000, 5}, + {0x00800000, 5}, + {0x00400000, 5}, + {0x00200000, 5}, + {0x00100000, 4}, + {0x00080000, 4}, + {0x00040000, 4}, + {0x00020000, 4}, + {0x00010000, 4}, + {0x00008000, 3}, + {0x00004000, 3}, + {0x00002000, 3}, + {0x00001000, 3}, + {0x00000800, 3}, + {0x00000400, 2}, + {0x00000200, 2}, + {0x00000100, 2}, + {0x00000080, 2}, + {0x00000040, 1}, + {0x00000020, 1}, + {0x00000010, 1}, + {0x00000008, 1}, + {0x00000004, 1}, + {0x00000002, 1}, + {0x00000001, 1}, +}; +#define num_tests (sizeof (tests) / (sizeof (tests[0]))) + +int +main (int argc, const char **argv) +{ + int res = 0; + + for (size_t i = 0; i < num_tests; i++) { + sb.cursize = 0; + msg.readcount = 0; + msg.badread = 0; + + MSG_WriteUTF8 (&sb, tests[i].input); + int32_t output = MSG_ReadUTF8 (&msg); + + printf ("%d %08x\n", (int) i, tests[i].input); + SZ_Dump (&sb); + + if (sb.cursize != tests[i].bytes || msg.readcount != tests[i].bytes + || output != tests[i].input || msg.badread) { + res |= 1; + printf ("test %d failed\n", (int) i); + printf ("expect: %8x %d %d 0\n", + tests[i].input, tests[i].bytes, tests[i].bytes); + printf ("got : %8x %d %d %d\n", + output, sb.cursize, msg.readcount, msg.badread); + } + } + return res; +} diff --git a/libs/util/test/test-zone.c b/libs/util/test/test-zone.c new file mode 100644 index 000000000..2fecd0218 --- /dev/null +++ b/libs/util/test/test-zone.c @@ -0,0 +1,217 @@ +#define PARANOID +#include "../zone.c" + +static int +check_hunk_block (const memhunk_t *hunk, const void *mem, size_t size) +{ + const hunkblk_t *h = (const hunkblk_t *) mem - 1; + if (h->sentinal1 != HUNK_SENTINAL || h->sentinal2 != HUNK_SENTINAL) { + printf ("invalid sentinals: %u %u\n", h->sentinal1, h->sentinal2); + return 1; + } + if (h->size % HUNK_ALIGN) { + printf ("block size misaligned: %zd %zd\n", + h->size, h->size % HUNK_ALIGN); + return 1; + } + if (h->size - sizeof (hunkblk_t) < size) { + printf ("block size too small: %zd %zd\n", + h->size - sizeof (hunkblk_t), size); + return 1; + } + if (h->size - sizeof (hunkblk_t) - size >= HUNK_ALIGN) { + printf ("block size too small: %zd %d\n", + h->size - sizeof (hunkblk_t) - size, HUNK_ALIGN); + return 1; + } + if ((byte *) h < hunk->base + || (byte *) h + h->size > hunk->base + hunk->size) { + printf ("block outside of hunk: %p %p, %p %p\n", + h, hunk->base, (byte *) h + h->size, hunk->base + hunk->size); + return 1; + } + size_t offset = (byte *) h - hunk->base; + if (offset < hunk->size - hunk->high_used + && offset + h->size > hunk->low_used) { + printf ("block in unallocated region: %zd %zd, %zd %zd\n", + offset, offset + h->size, + hunk->low_used, hunk->size - hunk->high_used); + return 1; + } + return 0; +} + +static int +check_cache_block (const memhunk_t *hunk, const void *c, size_t size) +{ + if (!c) { + printf ("cache block is null\n"); + return 1; + } + + const cache_system_t *cs = (cache_system_t *) c - 1; + + size_t hunk_high = hunk->size - hunk->high_used; + size_t offset = (byte *) cs - hunk->base; + if (offset < hunk->low_used || offset + cs->size > hunk_high) { + printf ("cache block in hunk stack: %zd %zd, %zd, %zd\n", + offset, offset + cs->size, hunk->low_used, hunk_high); + return 1; + } + return 0; +} + +static int +check_cache_order (memhunk_t *hunk) +{ + uint32_t ci; + cache_system_t *c; + int ret = 0; + + cache_system_t *first = cs_ptr (hunk, hunk->cache_head[0].next); + cache_system_t *last = cs_ptr (hunk, hunk->cache_head[0].prev); + + if (first->prev) { + printf ("first cache block does not point back to head: %u\n", + first->prev); + } + if (last->next) { + printf ("last cache block does not point forward to head: %u\n", + last->next); + } + if (first->prev || last->next) { + exit (1); // unsafe to continue + } + uint32_t prev = 0; + for (ci = hunk->cache_head[0].next; ci; prev = ci, ci = c->next) { + c = cs_ptr (hunk, ci); + if ((byte *) c + c->size > hunk->base + hunk->size) { + printf ("cache block outside of hunk (from %u)\n", prev); + exit (1); + } + if (c->readlock) { + printf ("look in cache detected at %u\n", ci); + exit (1); + } + c->readlock = 1; + if (c->next && c->next < ci) { + printf ("cache block sequence incorrect %u -> %u\n", prev, ci); + ret |= 1; + } + } + return ret; +} + +int +main (int argc, const char **argv) +{ + int ret = 0; + + if (sizeof (memhunk_t) != 128) { + ret = 1; + printf ("memhunk_t not 128 bytes: %zd\n", sizeof (memhunk_t)); + } + if (sizeof (hunkblk_t) != 64) { + ret = 1; + printf ("hunkblk_t not 64 bytes: %zd\n", sizeof (memhunk_t)); + } + size_t memsize = 64 * 1024; + void *hunk_mem = Sys_Alloc (memsize); + if ((intptr_t) hunk_mem & 63) { + // (not really part of test, but relied upon) should be 4k + // aligned, but doesn't matter for the tests + printf ("Sys_Alloc returned unaligned memory"); + ret = 1; + } + memhunk_t *hunk = Hunk_Init (hunk_mem, memsize); + if ((void *) hunk != hunk_mem) { + ret = 1; + printf ("hunk moved\n"); + } + if ((void *) hunk->base != (void *)&hunk[1]) { + ret = 1; + printf ("hunk->base does not point to beginning of hunk space\n"); + } + if (hunk->size != memsize - sizeof (memhunk_t)) { + ret = 1; + printf ("hunk size not memsize - sizeof (memhunk_t) (%zd - %zd): %zd\n", + memsize, sizeof (memhunk_t), hunk->size); + } + if (hunk->low_used || hunk->high_used) { + ret = 1; + printf ("hunk low and high used not 0: %zd %zd\n", + hunk->low_used, hunk->high_used); + } + if (hunk->tempmark || hunk->tempactive) { + ret = 1; + printf ("hunk tempmark tempactive not 0: %zd %d\n", + hunk->tempmark, hunk->tempactive); + } + if (ret) { + // no point continuing + return 1; + } + + size_t low_mark = Hunk_LowMark (hunk); + if (low_mark != 0) { + ret = 1; + printf ("initial hunk low mark not 0: %zd\n", low_mark); + } + size_t size = 1024; + void *low = Hunk_AllocName (hunk, size, "low test"); + ret |= check_hunk_block (hunk, low, size); + if (low > (void *) (hunk->base + sizeof (hunkblk_t))) { + ret = 1; + printf ("low memory not at beginning of hunk: %p %p\n", + low, hunk->base + sizeof (hunkblk_t)); + } + + size_t high_mark = Hunk_HighMark (hunk); + if (high_mark != 0) { + ret = 1; + printf ("initial hunk high mark not 0: %zd\n", high_mark); + } + void *high = Hunk_HighAlloc (hunk, size);//FIXME, "high test"); + ret |= check_hunk_block (hunk, high, size); + if (high + size < (void *) (hunk->base + hunk->size)) { + ret = 1; + printf ("high memory not at end of hunk: %p %p\n", + high + size, hunk->base + hunk->size); + } + + global_hunk = hunk;//FIXME put hunk in cache_user_t ? + cache_user_t cu = {}; + void *cm = Cache_Alloc (&cu, 512, "cache test"); + ret |= check_cache_block (hunk, cm, 512); + + size_t mark = Hunk_LowMark (hunk); + void *low2 = Hunk_AllocName (hunk, 61 * 1024, "low test 2"); + ret |= check_hunk_block (hunk, low2, 61 * 1024); + if (cm == Cache_Check (&cu)) { + ret = 1; + printf ("cache block was not moved\n"); + } + cm = Cache_Check (&cu); + ret |= check_cache_block (hunk, cm, 512); + Hunk_FreeToLowMark (hunk, mark); + if (cm != Cache_Check (&cu)) { + ret = 1; + printf ("cache block was moved\n"); + } + + void *high2 = Hunk_HighAlloc (hunk, 2 * 1024); + ret |= check_hunk_block (hunk, high2, 2 * 1024); + if (cm == Cache_Check (&cu)) { + ret = 1; + printf ("cache block was not moved\n"); + } + cm = Cache_Check (&cu); + ret |= check_cache_block (hunk, cm, 512); + printf ("%zd %zd %zd\n", hunk->low_used, hunk->size - hunk->high_used, + (byte *) cm - hunk->base); + check_cache_order (hunk); + Hunk_Print(hunk, 1); + Cache_Print (); + + return ret; +} diff --git a/libs/util/va.c b/libs/util/va.c index aa99f7955..98af3d8b0 100644 --- a/libs/util/va.c +++ b/libs/util/va.c @@ -38,29 +38,61 @@ #include #include "QF/dstring.h" +#include "QF/sys.h" #include "QF/va.h" +struct va_ctx_s { + dstring_t **strings; + int num_strings; + int str_index; +}; -/* - va +static va_ctx_t *default_va_ctx; - does a varargs printf into a temp buffer, so I don't need to have - varargs versions of all text functions. -*/ -VISIBLE char * -va (const char *fmt, ...) +VISIBLE va_ctx_t * +va_create_context (int buffers) +{ + va_ctx_t *ctx; + + ctx = malloc (sizeof (va_ctx_t) + buffers * sizeof (dstring_t *)); + ctx->strings = (dstring_t **) (ctx + 1); + ctx->num_strings = buffers; + ctx->str_index = 0; + + for (int i = 0; i < buffers; i++) { + ctx->strings[i] = dstring_new (); + } + return ctx; +} + +VISIBLE void +va_destroy_context (va_ctx_t *ctx) +{ + for (int i = 0; i < ctx->num_strings; i++) { + dstring_delete (ctx->strings[i]); + } + free (ctx); +} + +VISIBLE const char * +va (va_ctx_t *ctx, const char *fmt, ...) { va_list args; - static dstring_t *string; + dstring_t *dstr; - if (!string) - string = dstring_new (); + if (!ctx) { + if (!default_va_ctx) { + default_va_ctx = va_create_context (4); + } + ctx = default_va_ctx; + } + dstr = ctx->strings[ctx->str_index++ % ctx->num_strings]; va_start (args, fmt); - dvsprintf (string, fmt, args); + dvsprintf (dstr, fmt, args); va_end (args); - return string->str; + return dstr->str; } VISIBLE char * @@ -77,3 +109,17 @@ nva (const char *fmt, ...) return dstring_freeze (string); } + +static void +va_shutdown (void *data) +{ + if (default_va_ctx) { + va_destroy_context (default_va_ctx); + } +} + +static void __attribute__((constructor)) +va_init (void) +{ + Sys_RegisterShutdown (va_shutdown, 0); +} diff --git a/libs/util/wad.c b/libs/util/wad.c index 3b4355bbb..1fbb91b39 100644 --- a/libs/util/wad.c +++ b/libs/util/wad.c @@ -118,7 +118,7 @@ W_GetLumpinfo (const char *name) return lump_p; } - Sys_Error ("W_GetLumpinfo: %s not found", name); + Sys_MaskPrintf (SYS_warn, "W_GetLumpinfo: %s not found", name); return NULL; } @@ -127,9 +127,12 @@ W_GetLumpName (const char *name) { lumpinfo_t *lump; + if (!wad_base) { + return 0; + } lump = W_GetLumpinfo (name); - return (void *) (wad_base + lump->filepos); + return lump ? (wad_base + lump->filepos) : 0; } VISIBLE void diff --git a/libs/util/wadfile.c b/libs/util/wadfile.c index d5bf7c7fd..3d8a709ca 100644 --- a/libs/util/wadfile.c +++ b/libs/util/wadfile.c @@ -86,7 +86,7 @@ wad_new (const char *name) free (wad); return 0; } - wad->lump_hash = Hash_NewTable (1021, 0, 0, 0); + wad->lump_hash = Hash_NewTable (1021, 0, 0, 0, 0); if (!wad->lump_hash) { free (wad->filename); free (wad); @@ -182,7 +182,7 @@ wad_create (const char *name) wad_del (wad); return 0; } - strncpy (wad->header.id, "WAD2", sizeof (wad->header.id)); + memcpy (wad->header.id, "WAD2", sizeof (wad->header.id)); Qwrite (wad->handle, &wad->header, sizeof (wad->header)); diff --git a/libs/util/zone.c b/libs/util/zone.c index f5c90a379..0b4833888 100644 --- a/libs/util/zone.c +++ b/libs/util/zone.c @@ -37,6 +37,7 @@ #include #include +#include #include "QF/cmd.h" #include "QF/cvar.h" @@ -48,14 +49,16 @@ #include "compat.h" -static void Cache_FreeLow (int new_low_hunk); -static void Cache_Profile (void); -static qboolean Cache_FreeLRU (void); +static void Cache_FreeLow (memhunk_t *hunk, size_t new_low_hunk); +static void Cache_FreeHigh (memhunk_t *hunk, size_t new_high_hunk); +static void Cache_Profile_r (memhunk_t *hunk); +static bool Cache_FreeLRU (memhunk_t *hunk); #define ZONEID 0x1d4a11 #define HUNK_SENTINAL 0x1df001ed #define MINFRAGMENT 64 +#define HUNK_ALIGN 64 /* ZONE MEMORY ALLOCATION @@ -69,30 +72,29 @@ static qboolean Cache_FreeLRU (void); all big things are allocated on the hunk. */ -typedef struct memblock_s -{ - int block_size; // including the header and possibly tiny fragments +typedef struct memblock_s { + size_t block_size; // including the header and possibly tiny fragments + struct memblock_s *next; + struct memblock_s *prev; + size_t size; // requested size + byte pad[64 - 3 * 4 - 4 * sizeof (size_t)]; int tag; // a tag of 0 is a free block - struct memblock_s *next, *prev; - int size; // requested size int id; // should be ZONEID - //int id2; // pad to 64 bit boundary -} memblock_t; + int retain; // reference counter (optional usage) +} __attribute__((aligned (64))) memblock_t; -struct memzone_s -{ - int size; // total bytes malloced, including header - int used; // ammount used, including header - int offset; - int ele_size; +struct memzone_s { + size_t size; // total bytes malloced, including header + size_t used; // ammount used, including header + size_t offset; + size_t ele_size; void (*error) (void *, const char *); void *data; - memblock_t blocklist; // start / end cap for linked list memblock_t *rover; -}; + memblock_t blocklist; // start / end cap for linked list +} __attribute__((aligned (64))); - -static int +static size_t z_block_size (memblock_t *block) { return block->block_size - sizeof (memblock_t) - 4; @@ -106,10 +108,20 @@ z_offset (memzone_t *zone, memblock_t *block) return offset / zone->ele_size + zone->offset; } -VISIBLE void -Z_ClearZone (memzone_t *zone, int size, int zone_offset, int ele_size) +static void +z_error (memzone_t *zone, const char *msg) { - memblock_t *block; + if (zone->error) + zone->error (zone->data, msg); + Sys_Error ("%s", msg); +} + +VISIBLE void +Z_ClearZone (memzone_t *zone, size_t size, size_t zone_offset, size_t ele_size) +{ + memblock_t *block + = __builtin_choose_expr (__builtin_offsetof (memblock_t, retain) == 60, + 0, (void) 0); // set the entire zone to one free block @@ -172,6 +184,13 @@ Z_Free (memzone_t *zone, void *ptr) zone->error (zone->data, "Z_Free: freed a freed pointer"); Sys_Error ("Z_Free: freed a freed pointer"); } + if (block->retain) { + const char *msg = nva ("Z_Free: freed a retained pointer: %d", + block->retain); + if (zone->error) + zone->error (zone->data, msg); + Sys_Error ("%s", msg); + } block->tag = 0; // mark as free block->size = 0; @@ -200,16 +219,14 @@ Z_Free (memzone_t *zone, void *ptr) } VISIBLE void * -Z_Malloc (memzone_t *zone, int size) +Z_Malloc (memzone_t *zone, size_t size) { void *buf; - if (!developer || developer->int_val & SYS_DEV) - Z_CheckHeap (zone); // DEBUG buf = Z_TagMalloc (zone, size, 1); if (!buf) { const char *msg; - msg = nva ("Z_Malloc: failed on allocation of %i bytes",size); + msg = nva ("Z_Malloc: failed on allocation of %zd bytes", size); if (zone->error) zone->error (zone->data, msg); Sys_Error ("%s", msg); @@ -220,12 +237,15 @@ Z_Malloc (memzone_t *zone, int size) } void * -Z_TagMalloc (memzone_t *zone, int size, int tag) +Z_TagMalloc (memzone_t *zone, size_t size, int tag) { int extra; int requested_size = size; memblock_t *start, *rover, *new, *base; + if (developer & SYS_zone) + Z_CheckHeap (zone); // DEBUG + if (!tag) { if (zone->error) zone->error (zone->data, "Z_TagMalloc: tried to use a 0 tag"); @@ -236,7 +256,7 @@ Z_TagMalloc (memzone_t *zone, int size, int tag) // of sufficient size size += sizeof (memblock_t); // account for size of block header size += 4; // space for memory trash tester - size = (size + 7) & ~7; // align to 8-byte boundary + size = (size + 63) & ~63; // align to 64-byte boundary base = rover = zone->rover; start = base->prev; @@ -266,6 +286,7 @@ Z_TagMalloc (memzone_t *zone, int size, int tag) base->block_size = size; } + base->retain = 0; // use is optional, but must be 0 to free base->tag = tag; // no longer a free block base->size = requested_size; @@ -283,15 +304,18 @@ Z_TagMalloc (memzone_t *zone, int size, int tag) } VISIBLE void * -Z_Realloc (memzone_t *zone, void *ptr, int size) +Z_Realloc (memzone_t *zone, void *ptr, size_t size) { - int old_size; + size_t old_size; memblock_t *block; void *old_ptr; if (!ptr) return Z_Malloc (zone, size); + if (developer & SYS_zone) + Z_CheckHeap (zone); // DEBUG + block = (memblock_t *) ((byte *) ptr - sizeof (memblock_t)); if (block->id != ZONEID/* || block->id2 != ZONEID*/) { if (zone->error) @@ -314,7 +338,7 @@ Z_Realloc (memzone_t *zone, void *ptr, int size) ptr = Z_TagMalloc (zone, size, 1); if (!ptr) { const char *msg; - msg = nva ("Z_Realloc: failed on allocation of %i bytes", size); + msg = nva ("Z_Realloc: failed on allocation of %zd bytes", size); if (zone->error) zone->error (zone->data, msg); Sys_Error ("%s", msg); @@ -333,13 +357,13 @@ Z_Print (memzone_t *zone) { memblock_t *block; - Sys_Printf ("zone size: %i location: %p used: %i\n", + Sys_Printf ("zone size: %zd location: %p used: %zd\n", zone->size, zone, zone->used); for (block = zone->blocklist.next ; ; block = block->next) { - Sys_Printf ("block:%p size:%7i tag:%3i ofs:%x\n", + Sys_Printf ("block:%p size:%8zd tag:%5x ret: %5d ofs:%x\n", block, z_block_size (block), - block->tag, z_offset (zone, block)); + block->tag, block->retain, z_offset (zone, block)); if (block->next == &zone->blocklist) break; // all blocks have been hit @@ -351,9 +375,10 @@ Z_Print (memzone_t *zone) Sys_Printf ("ERROR: next block doesn't have proper back link\n"); if (!block->tag && !block->next->tag) Sys_Printf ("ERROR: two consecutive free blocks\n"); - if (block->tag - && (*(int *) ((byte *) block + block->block_size - 4) != ZONEID)) - Sys_Printf ("ERROR: memory trashed in block\n"); + int id = *(int *) ((byte *) block + block->block_size - 4); + if (block->tag && (id != ZONEID)) + Sys_Printf ("ERROR: memory trashed in block %x != %x\n", + id, ZONEID); fflush (stdout); } } @@ -367,13 +392,24 @@ Z_CheckHeap (memzone_t *zone) if (block->next == &zone->blocklist) break; // all blocks have been hit if ((byte *) block + block->block_size != (byte *) block->next) - Sys_Error ("Z_CheckHeap: block size does not touch the next " - "block\n"); + z_error (zone, + "Z_CheckHeap: block size does not touch the next block"); if (block->next->prev != block) - Sys_Error ("Z_CheckHeap: next block doesn't have proper back " - "link\n"); + z_error (zone, + "Z_CheckHeap: next block doesn't have proper back link"); if (!block->tag && !block->next->tag) - Sys_Error ("Z_CheckHeap: two consecutive free blocks\n"); + z_error (zone, "Z_CheckHeap: two consecutive free blocks"); + if (block->id != ZONEID/* || block->id2 != ZONEID*/) + z_error (zone, "ERROR: block ids incorrect"); + if ((byte *) block + block->block_size != (byte *) block->next) + z_error (zone, "ERROR: block size does not touch the next block"); + if (block->next->prev != block) + z_error (zone, "ERROR: next block doesn't have proper back link"); + if (!block->tag && !block->next->tag) + z_error (zone, "ERROR: two consecutive free blocks"); + if (block->tag + && (*(int *) ((byte *) block + block->block_size - 4) != ZONEID)) + z_error (zone, "ERROR: memory trashed in block"); } } @@ -384,8 +420,8 @@ Z_SetError (memzone_t *zone, void (*err) (void *, const char *), void *data) zone->data = data; } -void -Z_CheckPointer (const memzone_t *zone, const void *ptr, int size) +VISIBLE void +Z_CheckPointer (const memzone_t *zone, const void *ptr, size_t size) { const memblock_t *block; const char *block_mem; @@ -407,21 +443,146 @@ Z_CheckPointer (const memzone_t *zone, const void *ptr, int size) } } +VISIBLE int +Z_IncRetainCount (memzone_t *zone, void *ptr) +{ + memblock_t *block = (memblock_t *) ((byte *) ptr - sizeof (memblock_t)); + if (!++block->retain) { + z_error (zone, "inc retain count wrapped to 0"); + } + return block->retain; +} + +VISIBLE int +Z_DecRetainCount (memzone_t *zone, void *ptr) +{ + memblock_t *block = (memblock_t *) ((byte *) ptr - sizeof (memblock_t)); + if (--block->retain == -1) { + z_error (zone, "dec retain count wrapped past 0"); + } + return block->retain; +} + +VISIBLE int +Z_GetRetainCount (memzone_t *zone, void *ptr) +{ + memblock_t *block = (memblock_t *) ((byte *) ptr - sizeof (memblock_t)); + return block->retain; +} + +VISIBLE int +Z_GetTag (memzone_t *zone, void *ptr) +{ + memblock_t *block = (memblock_t *) ((byte *) ptr - sizeof (memblock_t)); + return block->tag; +} + +VISIBLE void +Z_SetTag (memzone_t *zone, void *ptr, int tag) +{ + if (!tag) { + z_error (zone, "Attept to set tag to 0"); + } + memblock_t *block = (memblock_t *) ((byte *) ptr - sizeof (memblock_t)); + block->tag = tag; +} +VISIBLE void +Z_MemInfo (const memzone_t *zone, size_t *used, size_t *size) +{ + *used = zone->used; + *size = zone->size; +} + //============================================================================ +typedef struct cache_system_s cache_system_t; +struct cache_system_s { + uint32_t prev; + uint32_t next; + uint32_t lru_prev; + uint32_t lru_next; + struct memhunk_s *hunk; + size_t size; // including this header + cache_user_t *user; + char name[16]; + int readlock; +} __attribute__((aligned (64))); + typedef struct { - int sentinal; - int size; // including sizeof(hunk_t), -1 = not allocated - char name[8]; -} hunk_t; + int sentinal1; + int sentinal2; + size_t size; // including sizeof(hunkblk_t), -1 = not allocated + char name[16]; +} __attribute__((aligned (64))) hunkblk_t; -byte *hunk_base; -int hunk_size; -int hunk_low_used; -int hunk_high_used; -int hunk_tempmark; +struct memhunk_s { + byte *base; + size_t size; + size_t low_used; + size_t high_used; + size_t tempmark; + bool tempactive; + cache_system_t cache_head[1]; +} __attribute__((aligned (64))); -qboolean hunk_tempactive; +static cache_system_t * +cs_ptr (memhunk_t *hunk, uint32_t cs_ind) +{ + return &hunk->cache_head[cs_ind]; +} + +static uint32_t +cs_ind (memhunk_t *hunk, cache_system_t *cs_ptr) +{ + return cs_ptr - hunk->cache_head; +} + +static memhunk_t *global_hunk; + +static int +hunk_check (memhunk_t *hunk, hunkblk_t *h, int err) +{ + const char *msg = 0; + if (h->sentinal1 != HUNK_SENTINAL || h->sentinal2 != HUNK_SENTINAL) { + msg = "Hunk_Check: trashed sentinel"; + } + if (!msg && (h->size < sizeof (hunkblk_t) + || h->size + (byte *) h > hunk->base + hunk->size)) { + msg = "Hunk_Check: bad size"; + } + if (!msg) { + return 1; + } + + byte *buf = (byte *) h; + int len = sizeof (*h); + int pos = 0, llen, i; + + fflush (stdout); + fprintf (stderr, "\n"); + while (pos < len) { + llen = (len - pos < 16 ? len - pos : 16); + fprintf (stderr, "%08x: ", pos); + for (i = 0; i < llen; i++) + fprintf (stderr, "%02x ", buf[pos + i]); + for (i = 0; i < 16 - llen; i++) + fprintf (stderr, " "); + fprintf (stderr, " | "); + + for (i = 0; i < llen; i++) + fprintf (stderr, "%c", isprint (buf[pos + i]) ? buf[pos + i] : '.'); + for (i = 0; i < 16 - llen; i++) + fprintf (stderr, " "); + fprintf (stderr, "\n"); + pos += llen; + } + + if (err) { + Sys_Error ("%p: %zd: %s", h, (byte *) h - (byte *) hunk, msg); + } + fprintf (stderr, "%p: %zd: %s", h, (byte *) h - (byte *) hunk, msg); + return 0; +} /* Hunk_Check @@ -429,16 +590,15 @@ qboolean hunk_tempactive; Run consistancy and sentinal trahing checks */ VISIBLE void -Hunk_Check (void) +Hunk_Check (memhunk_t *hunk) { - hunk_t *h; + if (!hunk) { hunk = global_hunk; } //FIXME clean up callers + hunkblk_t *h; + byte *hunk_end = hunk->base + hunk->low_used; - for (h = (hunk_t *) hunk_base; (byte *) h != hunk_base + hunk_low_used;) { - if (h->sentinal != HUNK_SENTINAL) - Sys_Error ("Hunk_Check: trashed sentinal"); - if (h->size < 16 || h->size + (byte *) h - hunk_base > hunk_size) - Sys_Error ("Hunk_Check: bad size"); - h = (hunk_t *) ((byte *) h + h->size); + for (h = (hunkblk_t *) hunk->base; (byte *) h < hunk_end; ) { + hunk_check (hunk, h, 1); + h = (hunkblk_t *) ((byte *) h + h->size); } } @@ -449,33 +609,32 @@ Hunk_Check (void) Otherwise, allocations with the same name will be totaled up before printing. */ -/* -static void -Hunk_Print (qboolean all) + +VISIBLE void +Hunk_Print (memhunk_t *hunk, bool all) { - char name[9]; - hunk_t *h, *next, *endlow, *starthigh, *endhigh; + if (!hunk) { hunk = global_hunk; } //FIXME clean up callers + hunkblk_t *h, *next, *endlow, *starthigh, *endhigh; int count, sum, totalblocks; - name[8] = 0; count = 0; sum = 0; totalblocks = 0; - h = (hunk_t *) hunk_base; - endlow = (hunk_t *) (hunk_base + hunk_low_used); - starthigh = (hunk_t *) (hunk_base + hunk_size - hunk_high_used); - endhigh = (hunk_t *) (hunk_base + hunk_size); + h = (hunkblk_t *) hunk->base; + endlow = (hunkblk_t *) (hunk->base + hunk->low_used); + starthigh = (hunkblk_t *) (hunk->base + hunk->size - hunk->high_used); + endhigh = (hunkblk_t *) (hunk->base + hunk->size); - Sys_Printf (" :%8i total hunk size\n", hunk_size); + Sys_Printf (" :%8zd total hunk size\n", hunk->size); Sys_Printf ("-------------------------\n"); while (1) { // skip to the high hunk if done with low hunk if (h == endlow) { Sys_Printf ("-------------------------\n"); - Sys_Printf (" :%8i REMAINING\n", - hunk_size - hunk_low_used - hunk_high_used); + Sys_Printf (" :%8zd REMAINING\n", + hunk->size - hunk->low_used - hunk->high_used); Sys_Printf ("-------------------------\n"); h = starthigh; } @@ -484,26 +643,30 @@ Hunk_Print (qboolean all) break; // run consistancy checks - if (h->sentinal != HUNK_SENTINAL) - Sys_Error ("Hunk_Check: trahsed sentinal"); - if (h->size < 16 || h->size + (byte *) h - hunk_base > hunk_size) - Sys_Error ("Hunk_Check: bad size"); + if (!hunk_check (hunk, h, 0)) { + break; + } - next = (hunk_t *) ((byte *) h + h->size); + next = (hunkblk_t *) ((byte *) h + h->size); count++; totalblocks++; sum += h->size; // print the single block - memcpy (name, h->name, 8); - if (all) - Sys_Printf ("%8p :%8i %8s\n", h, h->size, name); + if (all) { + const int sz = sizeof (h->name); + Sys_Printf ("%8p :%8zd %*.*s\n", h, h->size, sz, sz, + h->name[0] ? h->name : "unknown"); + } // print the total if (next == endlow || next == endhigh || - strncmp (h->name, next->name, 8)) { - if (!all) - Sys_Printf (" :%8i %8s (TOTAL)\n", sum, name); + strncmp (h->name, next->name, sizeof (h->name))) { + if (!all) { + const int sz = sizeof (h->name); + Sys_Printf (" :%8i %*.*s (TOTAL)\n", + sum, sz, sz, h->name[0] ? h->name : "unknown"); + } count = 0; sum = 0; } @@ -514,119 +677,160 @@ Hunk_Print (qboolean all) Sys_Printf ("-------------------------\n"); Sys_Printf ("%8i total blocks\n", totalblocks); } -*/ + static void -Hunk_FreeToHighMark (int mark) +Hunk_FreeToHighMark (memhunk_t *hunk, size_t mark) { - if (hunk_tempactive) { - hunk_tempactive = false; - Hunk_FreeToHighMark (hunk_tempmark); + if (!hunk) { hunk = global_hunk; } //FIXME clean up callers + if (hunk->tempactive) { + hunk->tempactive = false; + Hunk_FreeToHighMark (hunk, hunk->tempmark); } - if (mark < 0 || mark > hunk_high_used) - Sys_Error ("Hunk_FreeToHighMark: bad mark %i", mark); - memset (hunk_base + hunk_size - hunk_high_used, 0, hunk_high_used - mark); - hunk_high_used = mark; + if (mark == hunk->high_used) + return; + if (mark > hunk->high_used) + Sys_Error ("Hunk_FreeToHighMark: bad mark %zd", mark); + memset (hunk->base + hunk->size - hunk->high_used, 0, + hunk->high_used - mark); + hunk->high_used = mark; } static int -Hunk_HighMark (void) +Hunk_HighMark (memhunk_t *hunk) { - if (hunk_tempactive) { - hunk_tempactive = false; - Hunk_FreeToHighMark (hunk_tempmark); + if (!hunk) { hunk = global_hunk; } //FIXME clean up callers + if (hunk->tempactive) { + hunk->tempactive = false; + Hunk_FreeToHighMark (hunk, hunk->tempmark); } - return hunk_high_used; + return hunk->high_used; } VISIBLE void * -Hunk_AllocName (int size, const char *name) +Hunk_RawAlloc (memhunk_t *hunk, size_t size) { - hunk_t *h; + hunkblk_t *h; #ifdef PARANOID - Hunk_Check (); + Hunk_Check (hunk); #endif - if (size < 0) - Sys_Error ("Hunk_Alloc: bad size: %i", size); + size = sizeof (hunkblk_t) + ((size + HUNK_ALIGN - 1) & ~(HUNK_ALIGN - 1)); - size = sizeof (hunk_t) + ((size + 15) & ~15); - - if (hunk_size - hunk_low_used - hunk_high_used < size) { - Hunk_HighMark(); - Cache_FreeLRU (); + if (hunk->size - hunk->low_used - hunk->high_used < size) { + Hunk_HighMark (hunk); // force free of temp hunk } - if (hunk_size - hunk_low_used - hunk_high_used < size) { - int mem = hunk_size / (1024 * 1024); + if (hunk->size - hunk->low_used - hunk->high_used < size) { + int mem = (hunk->size + sizeof (memhunk_t)) / (1024 * 1024); mem += 8; mem &= ~7; - Cache_Profile (); + Hunk_Print (hunk, 1); + Cache_Profile_r (hunk); Sys_Error ("Not enough RAM allocated. Try starting using \"-mem %d\" on " - "the %s command line. (%d - %d - %d < %d)", mem, - PACKAGE_NAME, hunk_size, hunk_low_used, hunk_high_used, size); + "the %s command line. (%zd - %zd - %zd < %zd)", mem, + PACKAGE_NAME, hunk->size, hunk->low_used, hunk->high_used, size); } - h = (hunk_t *) (hunk_base + hunk_low_used); - hunk_low_used += size; + h = (hunkblk_t *) (hunk->base + hunk->low_used); + hunk->low_used += size; - Cache_FreeLow (hunk_low_used); - - memset (h, 0, size); + Cache_FreeLow (hunk, hunk->low_used); h->size = size; - h->sentinal = HUNK_SENTINAL; - strncpy (h->name, name, 8); + h->sentinal1 = HUNK_SENTINAL; + h->sentinal2 = HUNK_SENTINAL; + h->name[0] = 0; return (void *) (h + 1); } VISIBLE void * -Hunk_Alloc (int size) +Hunk_RawAllocName (memhunk_t *hunk, size_t size, const char *name) { - return Hunk_AllocName (size, "unknown"); + void *mem = Hunk_RawAlloc (hunk, size); + hunkblk_t *h = ((hunkblk_t *) mem) - 1; + memccpy (h->name, name, 0, sizeof (h->name)); + return mem; } -VISIBLE int -Hunk_LowMark (void) +VISIBLE void * +Hunk_AllocName (memhunk_t *hunk, size_t size, const char *name) { - return hunk_low_used; + if (!hunk) { hunk = global_hunk; } //FIXME clean up callers + void *mem = Hunk_RawAllocName (hunk, size, name); + memset (mem, 0, size); + return mem; +} + +VISIBLE void * +Hunk_Alloc (memhunk_t *hunk, size_t size) +{ + if (!hunk) { hunk = global_hunk; } //FIXME clean up callers + void *mem = Hunk_RawAlloc (hunk, size); + memset (mem, 0, size); + return mem; +} + +VISIBLE size_t +Hunk_LowMark (memhunk_t *hunk) +{ + if (!hunk) { hunk = global_hunk; } //FIXME clean up callers + return hunk->low_used; } VISIBLE void -Hunk_FreeToLowMark (int mark) +Hunk_RawFreeToLowMark (memhunk_t *hunk, size_t mark) { - if (mark < 0 || mark > hunk_low_used) - Sys_Error ("Hunk_FreeToLowMark: bad mark %i", mark); - memset (hunk_base + mark, 0, hunk_low_used - mark); - hunk_low_used = mark; + if (mark == hunk->low_used) + return; + if (mark > hunk->low_used) + Sys_Error ("Hunk_FreeToLowMark: bad mark %zd", mark); + hunk->low_used = mark; +} + +VISIBLE void +Hunk_FreeToLowMark (memhunk_t *hunk, size_t mark) +{ + if (!hunk) { hunk = global_hunk; } //FIXME clean up callers + if (mark == hunk->low_used) + return; + if (mark > hunk->low_used) + Sys_Error ("Hunk_FreeToLowMark: bad mark %zd", mark); + memset (hunk->base + mark, 0, hunk->low_used - mark); + hunk->low_used = mark; } static void * -Hunk_HighAlloc (int size) +Hunk_HighAlloc (memhunk_t *hunk, size_t size) { - if (size < 0) - Sys_Error ("Hunk_HighAlloc: bad size: %i", size); + hunkblk_t *h; - if (hunk_tempactive) { - Hunk_FreeToHighMark (hunk_tempmark); - hunk_tempactive = false; + if (hunk->tempactive) { + Hunk_FreeToHighMark (hunk, hunk->tempmark); + hunk->tempactive = false; } #ifdef PARANOID - Hunk_Check (); + Hunk_Check (hunk); #endif - size = ((size + 15) & ~15); + size = sizeof (hunkblk_t) + ((size + HUNK_ALIGN - 1) & ~(HUNK_ALIGN - 1)); - if (hunk_size - hunk_low_used - hunk_high_used < size) { - Sys_Printf ("Hunk_HighAlloc: failed on %i bytes\n", size); + if (hunk->size - hunk->low_used - hunk->high_used < size) { + Sys_Printf ("Hunk_HighAlloc: failed on %zd bytes\n", size); return NULL; } - hunk_high_used += size; + hunk->high_used += size; + Cache_FreeHigh (hunk, hunk->high_used); - return (void *) (hunk_base + hunk_size - hunk_high_used); + h = (void *) (hunk->base + hunk->size - hunk->high_used); + h->sentinal1 = HUNK_SENTINAL; + h->sentinal2 = HUNK_SENTINAL; + h->size = size; + h->name[0] = 0; + return h + 1; } /* @@ -635,82 +839,188 @@ Hunk_HighAlloc (int size) Return space from the top of the hunk */ VISIBLE void * -Hunk_TempAlloc (int size) +Hunk_TempAlloc (memhunk_t *hunk, size_t size) { + if (!hunk) { hunk = global_hunk; } //FIXME clean up callers void *buf; - size = (size + 15) & ~15; + size = (size + HUNK_ALIGN - 1) & ~(HUNK_ALIGN - 1); - if (hunk_tempactive) { - if (hunk_high_used - hunk_tempmark >= size + (int) sizeof (hunk_t)) { - return (hunk_t *) (hunk_base + hunk_size - hunk_high_used) + 1; + if (hunk->tempactive) { + size_t temp_free = hunk->high_used - hunk->tempmark; + if (temp_free >= size + (int) sizeof (hunkblk_t)) { + byte *temp_block = hunk->base + hunk->size - hunk->high_used; + return (hunkblk_t *) temp_block + 1; } - Hunk_FreeToHighMark (hunk_tempmark); - hunk_tempactive = false; + Hunk_FreeToHighMark (hunk, hunk->tempmark); + hunk->tempactive = false; } - hunk_tempmark = Hunk_HighMark (); + hunk->tempmark = Hunk_HighMark (hunk); - buf = Hunk_HighAlloc (size); + buf = Hunk_HighAlloc (hunk, size); - hunk_tempactive = true; + hunk->tempactive = true; return buf; } +VISIBLE int +Hunk_PointerIsValid (memhunk_t *hunk, void *ptr) +{ + if (!hunk) { hunk = global_hunk; } //FIXME clean up callers + + size_t offset = (byte *) ptr - hunk->base; + if (offset >= hunk->size) { + return 0; + } + if (offset < hunk->low_used) { + // the pointer is somewhere in the lower space of the hunk + // FIXME better checking? + return 1; + } + if (offset >= hunk->size - hunk->high_used + sizeof (hunkblk_t)) { + // the pointer is somewhere in the upper space of the hunk + // FIXME better checking? + return 1; + } + // the pointer is somewhere in between the two marks, so it has probably + // been freed + return 0; +} + /* CACHE MEMORY */ -typedef struct cache_system_s cache_system_t; -struct cache_system_s { - cache_system_t *prev, *next; - cache_system_t *lru_prev, *lru_next; // for LRU flushing - char name[16]; - size_t size; // including this header - int readlock; - cache_user_t *user; -}; - -static cache_system_t cache_head; - -static cache_system_t *Cache_TryAlloc (size_t size, qboolean nobottom); -#if 0 -static void -check_cache (void) +static inline void +Cache_UnlinkLRU (cache_system_t * cs) { - cache_system_t *cs; - int used = hunk_tempactive ? hunk_tempmark : hunk_high_used; + memhunk_t *hunk = cs->hunk; + cs_ptr (hunk, cs->lru_next)->lru_prev = cs->lru_prev; + cs_ptr (hunk, cs->lru_prev)->lru_next = cs->lru_next; - for (cs = cache_head.prev; cs != &cache_head; cs = cs->prev) - if (cs->prev != &cache_head) { - if ((byte *) cs + cs->size != (byte *) cs->prev) - Sys_Error ("inconsistent cache %p %p %d %d", cs, cs->prev, - (int)cs->size, - (int) ((char *)cs->prev - (char *)cs)); - if (hunk_size - ((byte*)cs - hunk_base) > used) - Sys_Error ("cache block out of high hunk"); - } - if (cache_head.prev != &cache_head && - hunk_size - ((byte*) cache_head.prev - hunk_base) != used) - Sys_Error ("cache bottom not at bottom of high hunk"); + cs->lru_prev = cs->lru_next = 0; } -#endif + static void -Cache_Move (cache_system_t * c) +Cache_MakeLRU (cache_system_t * cs) { + memhunk_t *hunk = cs->hunk; + __auto_type nx = cs_ptr (hunk, hunk->cache_head[0].lru_next); + nx->lru_prev = cs_ind (hunk, cs); + cs->lru_next = cs_ind (hunk, nx); + cs->lru_prev = 0; + hunk->cache_head[0].lru_next = cs_ind (hunk, cs); +} + +static void +link_cache_system (cache_system_t *new, cache_system_t *cs) +{ + memhunk_t *hunk = cs->hunk; + new->next = cs_ind (hunk, cs); + new->prev = cs->prev; + cs_ptr (hunk, cs->prev)->next = cs_ind (hunk, new); + cs->prev = cs_ind (hunk, new); +} + +/* + Cache_TryAlloc + + Looks for a free block of memory between the high and low hunk marks + Size should already include the header and padding +*/ +static cache_system_t * +Cache_TryAlloc (memhunk_t *hunk, size_t size, + size_t low_space, size_t high_space) +{ + cache_system_t *cs, *new; + + low_space = max (low_space, hunk->low_used); + high_space = max (high_space, hunk->high_used); + + if (hunk->cache_head[0].prev == 0) { + // The cache is completely empty, so just check for space + if (hunk->size - high_space < low_space + size) { + return 0; + } + // cache memory comes from the free region of the hunk. Should either + // end of the hunk need to grow, interfering cache blocks will be + // either moved or freed if there is nowhere to move the block. + new = (cache_system_t *) (hunk->base + hunk->low_used); + new->size = size; + new->hunk = hunk; + hunk->cache_head[0].prev = cs_ind (hunk, new); + hunk->cache_head[0].next = cs_ind (hunk, new); + new->prev = new->next = 0; + new->readlock = 0; + new->name[0] = 0; + Cache_MakeLRU (new); + return new; + } + + new = (cache_system_t *) (hunk->base + low_space); + uint32_t csi = hunk->cache_head[0].next; + do { + cs = cs_ptr (hunk, csi); + if ((byte *) cs >= (byte *) new + size) { + + new->size = size; + new->hunk = hunk; + + link_cache_system (new, cs); + + new->readlock = 0; + new->name[0] = 0; + + Cache_MakeLRU (new); + return new; + } + + // try next block. If it is a hole, then the resulting cs will be + // greater than new (though possibly not sufficiently so), but if + // it's not a hole, then they'll be the same and the difference in + // the test above will be 0 + new = (cache_system_t *) ((byte *) cs + cs->size); + csi = cs->next; + } while (csi); + + // came to the end of the cache. try to allocate from between the cache + // and the high hunk + if ((byte *) new < hunk->base + low_space) { + new = (cache_system_t *) (hunk->base + low_space); + } + if (hunk->base + hunk->size - high_space >= (byte *) new + size) { + new->size = size; + new->hunk = hunk; + + link_cache_system (new, hunk->cache_head); + + new->readlock = 0; + new->name[0] = 0; + + Cache_MakeLRU (new); + return new; + } + + return 0; // couldn't allocate +} + +static void +Cache_Move (cache_system_t *c, size_t new_low_hunk, size_t new_high_hunk) +{ + memhunk_t *hunk = c->hunk; cache_system_t *new; - // we are clearing up space at the bottom, so allocate it late - new = Cache_TryAlloc (c->size, true); + new = Cache_TryAlloc (hunk, c->size, new_low_hunk, new_high_hunk); if (new) { - Sys_MaskPrintf (SYS_DEV, "cache_move ok\n"); + Sys_MaskPrintf (SYS_cache, "cache_move ok\n"); memcpy (new + 1, c + 1, c->size - sizeof (cache_system_t)); new->user = c->user; - memcpy (new->name, c->name, sizeof (new->name)); + memccpy (new->name, c->name, 0, sizeof (new->name)); Cache_Free (c->user); new->user->data = (void *) (new + 1); } else { - Sys_MaskPrintf (SYS_DEV, "cache_move failed\n"); + Sys_MaskPrintf (SYS_cache, "cache_move failed\n"); Cache_Free (c->user); // tough luck... } @@ -722,154 +1032,71 @@ Cache_Move (cache_system_t * c) Throw things out until the hunk can be expanded to the given point */ static void -Cache_FreeLow (int new_low_hunk) +Cache_FreeLow (memhunk_t *hunk, size_t new_low_hunk) { cache_system_t *c; + uint32_t ci; - while (1) { - c = cache_head.prev; - if (c == &cache_head) - return; // nothing in cache at all - if ((byte *) c >= hunk_base + new_low_hunk) - return; // there is space to grow the hunk - Sys_Error ("FIXME: Cache_FreeLow: not enough memory"); - Cache_Move (c); // reclaim the space + // if next is 0, then there is nothing in the cache + while ((ci = hunk->cache_head[0].next)) { + c = cs_ptr (hunk, ci); + if ((byte *) c >= hunk->base + new_low_hunk) { + // there is space to grow the hunk + return; + } + // reclaim the space (the block will be moved or freed) + Cache_Move (c, new_low_hunk, 0); } } -static inline void -Cache_UnlinkLRU (cache_system_t * cs) -{ - if (!cs->lru_next || !cs->lru_prev) - Sys_Error ("Cache_UnlinkLRU: NULL link: %s %p %p", - cs->name, cs->lru_next, cs->lru_prev); - - cs->lru_next->lru_prev = cs->lru_prev; - cs->lru_prev->lru_next = cs->lru_next; - - cs->lru_prev = cs->lru_next = NULL; -} - static void -Cache_MakeLRU (cache_system_t * cs) +Cache_FreeHigh (memhunk_t *hunk, size_t new_high_hunk) { - if (cs->lru_next || cs->lru_prev) - Sys_Error ("Cache_MakeLRU: active link: %s %p %p", - cs->name, cs->lru_next, cs->lru_prev); - - cache_head.lru_next->lru_prev = cs; - cs->lru_next = cache_head.lru_next; - cs->lru_prev = &cache_head; - cache_head.lru_next = cs; + cache_system_t *c; + uint32_t ci; + while ((ci = hunk->cache_head[0].prev)) { + c = cs_ptr (hunk, ci); + if ((byte *) c + c->size <= hunk->base + hunk->size - new_high_hunk) { + // there is space to grow the hunk + return; + } + // reclaim the space (the block will be moved or freed) + Cache_Move (c, 0, new_high_hunk); + } } -static qboolean -Cache_FreeLRU (void) +static bool +Cache_FreeLRU (memhunk_t *hunk) { cache_system_t *cs; - //check_cache (); - for (cs = cache_head.lru_prev; - cs != &cache_head && cs->readlock; cs = cs->lru_prev) - ; - if (cs == &cache_head) + for (cs = cs_ptr (hunk, hunk->cache_head[0].lru_prev); + cs != hunk->cache_head && cs->readlock; + cs = cs_ptr (hunk, cs->lru_prev)) { + } + if (cs == hunk->cache_head) return 0; Cache_Free (cs->user); return 1; } static void -link_cache_system (cache_system_t *new, cache_system_t *cs) +Cache_Profile_r (memhunk_t *hunk) { - new->next = cs; - new->prev = cs->prev; - cs->prev->next = new; - cs->prev = new; - -} - -/* - Cache_TryAlloc - - Looks for a free block of memory between the high and low hunk marks - Size should already include the header and padding -*/ -static cache_system_t * -Cache_TryAlloc (size_t size, qboolean nobottom) -{ - cache_system_t *cs, *new; - - //check_cache (); - // is the cache completely empty? - if (!nobottom && cache_head.prev == &cache_head) { - new = (cache_system_t *) Hunk_HighAlloc (size); - if (!new) - return 0; - memset (new, 0, size); - new->size = size; - cache_head.prev = cache_head.next = new; - new->prev = new->next = &cache_head; - Cache_MakeLRU (new); - //check_cache (); - return new; - } - - // search for space in existing cache - for (cs = cache_head.next; cs != &cache_head; cs = cs->next) { - if (cs->user) - continue; // block isn't free - if (cs->size >= size) { - // found a big enough free block. If possible, carve it up for - // later reuse, using the upper portion of the block for the - // newly allocated block. - new = cs; - if (size - cs->size >= sizeof (cache_system_t)) { - new = (cache_system_t *) ((char *) cs + cs->size - size); - memset (new, 0, size); - new->size = size; - cs->size -= size; - link_cache_system (new, cs); - //check_cache (); - } - Cache_MakeLRU (new); - return new; - } - } - - if (nobottom) - return 0; - - // didn't find a free block, so make a new one. - new = Hunk_HighAlloc (size); - if (new) { - memset (new, 0, size); - new->size = size; - link_cache_system (new, &cache_head); - Cache_MakeLRU (new); - //check_cache (); - return new; - } - - return 0; // couldn't allocate -} - -static void -Cache_Profile (void) -{ - cache_system_t *cs; unsigned int i; unsigned int items[31] = {0}, sizes[31] = {0}; int count = 0, total = 0; + cache_system_t *cs; - cs = cache_head.next; - while (cs != &cache_head) { - for (i = 0; (cs->size >> (i + 1)) && i < 30; i++) - ; + for (uint32_t ind = hunk->cache_head[0].next; ind; ind = cs->next) { + cs = cs_ptr (hunk, ind); + for (i = 0; (cs->size >> (i + 1)) && i < 30; i++) { + } items[i]++; sizes[i] += cs->size; total += cs->size; count++; - cs = cs->next; + ind = cs->next; } Sys_Printf ("Cache Profile:\n"); Sys_Printf ("%8s %8s %8s %8s %8s\n", @@ -887,24 +1114,43 @@ Cache_Profile (void) count ? total / count : -1); } +static void +Cache_Profile (void) +{ + Cache_Profile_r (global_hunk); +} + +static void +Cache_Print_r (memhunk_t *hunk) +{ + cache_system_t *cs; + for (uint32_t ind = hunk->cache_head[0].next; ind; ind = cs->next) { + const int sz = sizeof (cs->name); + cs = cs_ptr (hunk, ind); + Sys_Printf ("%8zd : %.*s\n", cs->size, sz, cs->name); + } +} + static void Cache_Print (void) { - cache_system_t *cd; + Cache_Print_r (global_hunk); +} - for (cd = cache_head.next; cd != &cache_head; cd = cd->next) { - Sys_Printf ("%8d : %s\n", (int) cd->size, cd->name); - } +static void +init_cache (memhunk_t *hunk) +{ + hunk->cache_head[0].hunk = hunk; + hunk->cache_head[0].size = 0; + hunk->cache_head[0].next = hunk->cache_head[0].prev = 0; + hunk->cache_head[0].lru_next = hunk->cache_head[0].lru_prev = 0; + hunk->cache_head[0].user = (cache_user_t *) 1; // make it look allocated + hunk->cache_head[0].readlock = 1; // don't try to free or move it } static void Cache_Init (void) { - cache_head.next = cache_head.prev = &cache_head; - cache_head.lru_next = cache_head.lru_prev = &cache_head; - cache_head.user = (cache_user_t *) 1; // make it look allocated - cache_head.readlock = 1; // don't try to free or move it - Cmd_AddCommand ("cache_flush", Cache_Flush, "Clears the current game " "cache"); Cmd_AddCommand ("cache_profile", Cache_Profile, "Prints a profile of " @@ -918,20 +1164,30 @@ Cache_Init (void) Throw everything out, so new data will be demand cached */ -void -Cache_Flush (void) +static void +Cache_Flush_r (memhunk_t *hunk) { // cache_head.prev is guaranteed to not be free because it's the bottom // one and Cache_Free actually properly releases it - while (cache_head.prev != &cache_head) { - if (!cache_head.prev->user->data) + while (hunk->cache_head[0].prev) { + __auto_type cs = cs_ptr (hunk, hunk->cache_head[0].prev); + if (!cs->user->data) { + const int sz = sizeof (cs->name); Sys_Error ("Cache_Flush: user/system out of sync for " - "'%s' with %d size", - cache_head.prev->name, (int) cache_head.prev->size); - Cache_Free (cache_head.prev->user); // reclaim the space + "'%.*s' with %zd size", + sz, cs->name, cs->size); + } + Cache_Free (cs->user); // reclaim the space } } +VISIBLE void +Cache_Flush (void) +{ + // cache_head.prev is guaranteed to not be free because it's the bottom + Cache_Flush_r (global_hunk); +} + VISIBLE void * Cache_Check (cache_user_t *c) { @@ -967,72 +1223,71 @@ Cache_Free (cache_user_t *c) if (cs->readlock) Sys_Error ("Cache_Free: attempt to free locked block"); - Sys_MaskPrintf (SYS_DEV, "Cache_Free: freeing '%s' %p\n", cs->name, cs); + const int sz = sizeof (cs->name); + Sys_MaskPrintf (SYS_cache, "Cache_Free: freeing '%.*s' %p\n", + sz, cs->name, cs); - Cache_UnlinkLRU (cs); + memhunk_t *hunk = cs->hunk; - //check_cache (); - cs->user = 0; - if (!cs->prev->user) { - cs->size += cs->prev->size; - cs->prev->prev->next = cs; - cs->prev = cs->prev->prev; - } - if (!cs->next->user) { - cs = cs->next; - cs->size += cs->prev->size; - cs->prev->prev->next = cs; - cs->prev = cs->prev->prev; - } - if (cs->next == &cache_head) { - cs->next->prev = cs->prev; - cs->prev->next = cs->next; - if (cs->prev != &cache_head) - Hunk_FreeToHighMark (hunk_size - ((byte*)cs->prev - hunk_base)); - else - Hunk_FreeToHighMark (0); - } - //check_cache (); + cs_ptr (hunk, cs->prev)->next = cs->next; + cs_ptr (hunk, cs->next)->prev = cs->prev; + cs->next = cs->prev = 0; c->data = NULL; + + Cache_UnlinkLRU (cs); } -VISIBLE void * -Cache_Alloc (cache_user_t *c, int size, const char *name) +static void * +Cache_Alloc_r (memhunk_t *hunk, cache_user_t *c, size_t size, const char *name) { cache_system_t *cs; if (c->data) - Sys_Error ("Cache_Alloc: already allocated"); + Sys_Error ("Cache_Alloc_r: already allocated"); if (size <= 0) - Sys_Error ("Cache_Alloc: size %i", size); + Sys_Error ("Cache_Alloc_r: size %zd", size); - size = (size + sizeof (cache_system_t) + 15) & ~15; + size = (size + sizeof (cache_system_t) + HUNK_ALIGN - 1) & ~(HUNK_ALIGN-1); // find memory for it while (1) { - cs = Cache_TryAlloc (size, false); + cs = Cache_TryAlloc (hunk, size, 0, 0); if (cs) { - strncpy (cs->name, name, sizeof (cs->name) - 1); + memccpy (cs->name, name, 0, sizeof (cs->name)); c->data = (void *) (cs + 1); cs->user = c; break; } // free the least recently used cachedat - if (!Cache_FreeLRU()) + if (!Cache_FreeLRU (hunk)) Sys_Error ("Cache_Alloc: out of memory"); } return Cache_Check (c); } +VISIBLE void * +Cache_Alloc (cache_user_t *c, size_t size, const char *name) +{ + return Cache_Alloc_r (global_hunk, c, size, name); +} + +static void +Cache_Report_r (memhunk_t *hunk) +{ + Sys_Printf ("%4.1f megabyte data cache\n", + (hunk->size - hunk->high_used - + hunk->low_used) / (float) (1024 * 1024)); +} + VISIBLE void Cache_Report (void) { - Sys_MaskPrintf (SYS_DEV, "%4.1f megabyte data cache\n", - (hunk_size - hunk_high_used - - hunk_low_used) / (float) (1024 * 1024)); + if (developer & SYS_cache) { + Cache_Report_r (global_hunk); + } } VISIBLE void @@ -1111,13 +1366,23 @@ Cache_ReadLock (cache_user_t *c) //============================================================================ -VISIBLE void -Memory_Init (void *buf, int size) +VISIBLE memhunk_t * +Hunk_Init (void *buf, size_t size) { - hunk_base = buf; - hunk_size = size; - hunk_low_used = 0; - hunk_high_used = 0; + memhunk_t *hunk = buf; + hunk->base = (byte *) (hunk + 1); + hunk->size = size - sizeof (memhunk_t); + hunk->low_used = 0; + hunk->high_used = 0; - Cache_Init (); + init_cache (hunk); + return hunk; +} + +VISIBLE memhunk_t * +Memory_Init (void *buf, size_t size) +{ + global_hunk = Hunk_Init (buf, size); + Cache_Init (); + return global_hunk; } diff --git a/libs/video/Makefile.am b/libs/video/Makefile.am deleted file mode 100644 index 25b787b7c..000000000 --- a/libs/video/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -SUBDIRS= targets renderer - -clean-local: - rm -f *.a diff --git a/libs/video/Makemodule.am b/libs/video/Makemodule.am new file mode 100644 index 000000000..ae3d3ca44 --- /dev/null +++ b/libs/video/Makemodule.am @@ -0,0 +1,2 @@ +include libs/video/renderer/Makemodule.am +include libs/video/targets/Makemodule.am diff --git a/libs/video/renderer/Makefile.am b/libs/video/renderer/Makefile.am deleted file mode 100644 index 7f7119c14..000000000 --- a/libs/video/renderer/Makefile.am +++ /dev/null @@ -1,76 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -SUBDIRS= @vid_render_dirs@ -DIST_SUBDIRS= gl glsl sw sw32 -AM_CPPFLAGS= -I$(top_srcdir)/include - -lib_ldflags=-version-info $(QUAKE_LIBRARY_VERSION_INFO) \ - -rpath $(libdir) -no-undefined -plugin_ldflags= @plugin_ldflags@ -avoid-version -module -rpath $(plugindir) -plugin_libadd= @plugin_libadd@ -EXEEXT= - -particles_src= particles.part -particles_gen= particles.pc - -SUFFICES=.part .pc -.part.pc: - sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/\\n"/' $< > $@ - -#lib_LTLIBRARIES= @VID_REND_TARGETS@ -plugin_LTLIBRARIES= @vid_render_plugins@ -noinst_LTLIBRARIES= libQFrenderer.la @vid_render_static_plugins@ -BUILT_SOURCES= $(particles_gen) - -EXTRA_LTLIBRARIES= \ - vid_render_sw.la vid_render_sw32.la \ - vid_render_gl.la vid_render_glsl.la - -common_sources= \ - crosshair.c noisetextures.c r_alias.c r_bsp.c r_cvar.c r_dyn_textures.c \ - r_efrag.c r_ent.c r_graph.c r_iqm.c r_light.c r_main.c r_part.c \ - r_screen.c vid_common.c - -renderer_libs= \ - @vid_render_static_plugin_libs@ \ - $(top_builddir)/libs/util/libQFutil.la - -libQFrenderer_la_LDFLAGS= @STATIC@ -libQFrenderer_la_LIBADD= $(renderer_libs) -libQFrenderer_la_DEPENDENCIES= $(renderer_libs) -libQFrenderer_la_SOURCES= r_init.c r_progs.c - -gl_libs= \ - gl/libgl.la \ - $(top_builddir)/libs/models/libmodels_gl.la -vid_render_gl_la_LDFLAGS= $(plugin_ldflags) -vid_render_gl_la_LIBADD= $(gl_libs) -vid_render_gl_la_DEPENDENCIES= $(gl_libs) -vid_render_gl_la_SOURCES= $(common_sources) vid_render_gl.c - -glsl_libs= \ - glsl/libglsl.la \ - $(top_builddir)/libs/models/libmodels_glsl.la -vid_render_glsl_la_LDFLAGS= $(plugin_ldflags) -vid_render_glsl_la_LIBADD= $(glsl_libs) -vid_render_glsl_la_DEPENDENCIES=$(glsl_libs) -vid_render_glsl_la_SOURCES= $(common_sources) vid_render_glsl.c - -sw_libs= \ - sw/libsw.la \ - $(top_builddir)/libs/models/libmodels_sw.la -vid_render_sw_la_LDFLAGS= $(plugin_ldflags) -vid_render_sw_la_LIBADD= $(sw_libs) -vid_render_sw_la_DEPENDENCIES= $(sw_libs) -vid_render_sw_la_SOURCES= $(common_sources) vid_render_sw.c - -sw32_libs= \ - sw32/libsw32.la \ - $(top_builddir)/libs/models/libmodels_sw.la -vid_render_sw32_la_LDFLAGS= $(plugin_ldflags) -vid_render_sw32_la_LIBADD= $(sw32_libs) -vid_render_sw32_la_DEPENDENCIES=$(sw32_libs) -vid_render_sw32_la_SOURCES= $(common_sources) vid_render_sw32.c - -EXTRA_DIST = $(particles_src) -CLEANFILES= *.pc diff --git a/libs/video/renderer/Makemodule.am b/libs/video/renderer/Makemodule.am new file mode 100644 index 000000000..15c898e7e --- /dev/null +++ b/libs/video/renderer/Makemodule.am @@ -0,0 +1,563 @@ +include libs/video/renderer/vulkan/test/Makemodule.am +include libs/video/renderer/vulkan/vkgen/Makemodule.am + +#lib_LTLIBRARIES += @VID_REND_TARGETS@ +plugin_LTLIBRARIES += @vid_render_plugins@ +noinst_LTLIBRARIES += \ + libs/video/renderer/libQFrenderer.la \ + @render_libs@ \ + @vid_render_static_plugins@ + +#plugins +EXTRA_LTLIBRARIES += \ + libs/video/renderer/vid_render_sw.la \ + libs/video/renderer/vid_render_gl.la \ + libs/video/renderer/vid_render_glsl.la \ + libs/video/renderer/vid_render_vulkan.la + +#helper libraries +EXTRA_LTLIBRARIES += \ + libs/video/renderer/librender_sw.la \ + libs/video/renderer/librender_gl.la \ + libs/video/renderer/librender_glsl.la \ + libs/video/renderer/librender_vulkan.la + +video_renderer_common_sources = \ + libs/video/renderer/crosshair.c \ + libs/video/renderer/font8x8.c \ + libs/video/renderer/noisetextures.c \ + libs/video/renderer/r_alias.c \ + libs/video/renderer/r_billboard.c \ + libs/video/renderer/r_dyn_textures.c \ + libs/video/renderer/r_ent.c \ + libs/video/renderer/r_iqm.c \ + libs/video/renderer/r_sprite.c \ + libs/video/renderer/vid_common.c + +renderer_libs= \ + @vid_render_static_plugin_libs@ \ + libs/util/libQFutil.la + +renderer_libadd= \ + $(FREETYPE_LIBS) \ + $(HARFBUZZ_LIBS) + +libs_video_renderer_libQFrenderer_la_LDFLAGS= @STATIC@ +libs_video_renderer_libQFrenderer_la_LIBADD=\ + $(renderer_libs) \ + $(renderer_libadd) +libs_video_renderer_libQFrenderer_la_DEPENDENCIES= $(renderer_libs) +libs_video_renderer_libQFrenderer_la_SOURCES=\ + libs/video/renderer/r_bsp.c \ + libs/video/renderer/r_cvar.c \ + libs/video/renderer/r_draw.c \ + libs/video/renderer/r_fog.c \ + libs/video/renderer/r_graph.c \ + libs/video/renderer/r_init.c \ + libs/video/renderer/r_light.c \ + libs/video/renderer/r_main.c \ + libs/video/renderer/r_part.c \ + libs/video/renderer/r_scrap.c \ + libs/video/renderer/r_screen.c \ + libs/video/renderer/r_progs.c + +video_renderer_gl_libs= \ + libs/video/renderer/librender_gl.la \ + libs/models/libmodels_gl.la +libs_video_renderer_vid_render_gl_la_LDFLAGS= $(plugin_ldflags) +libs_video_renderer_vid_render_gl_la_LIBADD=\ + $(video_renderer_gl_libs) \ + $(renderer_libadd) +libs_video_renderer_vid_render_gl_la_DEPENDENCIES= $(video_renderer_gl_libs) +libs_video_renderer_vid_render_gl_la_SOURCES=\ + $(video_renderer_common_sources) \ + libs/video/renderer/vid_render_gl.c + +libs_video_renderer_librender_gl_la_SOURCES = \ + libs/video/renderer/gl/gl_draw.c \ + libs/video/renderer/gl/gl_dyn_lights.c \ + libs/video/renderer/gl/gl_dyn_part.c \ + libs/video/renderer/gl/gl_dyn_textures.c \ + libs/video/renderer/gl/gl_fisheye.c \ + libs/video/renderer/gl/gl_fog.c \ + libs/video/renderer/gl/gl_graph.c \ + libs/video/renderer/gl/gl_lightmap.c \ + libs/video/renderer/gl/gl_mod_alias.c \ + libs/video/renderer/gl/gl_mod_iqm.c \ + libs/video/renderer/gl/gl_mod_sprite.c \ + libs/video/renderer/gl/gl_rmain.c \ + libs/video/renderer/gl/gl_rmisc.c \ + libs/video/renderer/gl/gl_rsurf.c \ + libs/video/renderer/gl/gl_sky.c \ + libs/video/renderer/gl/gl_sky_clip.c \ + libs/video/renderer/gl/gl_textures.c \ + libs/video/renderer/gl/gl_warp.c \ + libs/video/renderer/gl/qfgl_ext.c \ + libs/video/renderer/gl/vid_common_gl.c \ + libs/video/renderer/gl/vtxarray.c + +glslshaderpath = libs/video/renderer/glsl + +shader_src = \ + $(glslshaderpath)/quakeforge.glsl \ + $(glslshaderpath)/sgustavson.glsl +shader_gen = \ + $(glslshaderpath)/quakeforge.slc \ + $(glslshaderpath)/sgustavson.slc + +SUFFIXES += .frag .vert .spv .spvc .fc .vc .slc .glsl +.glsl.slc: + $(V_SED)sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/\\n"/' $< > $@.t &&\ + $(am__mv) $@.t $@ + +video_renderer_glsl_libs= \ + libs/video/renderer/librender_glsl.la \ + libs/models/libmodels_glsl.la +libs_video_renderer_vid_render_glsl_la_LDFLAGS= $(plugin_ldflags) +libs_video_renderer_vid_render_glsl_la_LIBADD=\ + $(video_renderer_glsl_libs) \ + $(renderer_libadd) +libs_video_renderer_vid_render_glsl_la_DEPENDENCIES=$(video_renderer_glsl_libs) +libs_video_renderer_vid_render_glsl_la_SOURCES=\ + $(video_renderer_common_sources) \ + libs/video/renderer/vid_render_glsl.c + +libs_video_renderer_librender_glsl_la_SOURCES = \ + libs/video/renderer/glsl/glsl_alias.c \ + libs/video/renderer/glsl/glsl_bsp.c \ + libs/video/renderer/glsl/glsl_draw.c \ + libs/video/renderer/glsl/glsl_fisheye.c \ + libs/video/renderer/glsl/glsl_iqm.c \ + libs/video/renderer/glsl/glsl_lightmap.c \ + libs/video/renderer/glsl/glsl_main.c \ + libs/video/renderer/glsl/glsl_particles.c \ + libs/video/renderer/glsl/glsl_shader.c \ + libs/video/renderer/glsl/glsl_sprite.c \ + libs/video/renderer/glsl/glsl_textures.c \ + libs/video/renderer/glsl/glsl_warp.c \ + libs/video/renderer/glsl/qfglsl.c \ + libs/video/renderer/glsl/quakeforge.glsl \ + libs/video/renderer/glsl/vid_common_glsl.c + +video_renderer_sw_libs= \ + libs/video/renderer/librender_sw.la \ + libs/models/libmodels_sw.la +libs_video_renderer_vid_render_sw_la_LDFLAGS= $(plugin_ldflags) +libs_video_renderer_vid_render_sw_la_LIBADD=\ + $(video_renderer_sw_libs) \ + $(renderer_libadd) +libs_video_renderer_vid_render_sw_la_DEPENDENCIES= $(video_renderer_sw_libs) +libs_video_renderer_vid_render_sw_la_SOURCES=\ + $(video_renderer_common_sources) \ + libs/video/renderer/vid_render_sw.c + +libs_video_renderer_librender_sw_la_SOURCES = \ + libs/video/renderer/sw/d_copy.S \ + libs/video/renderer/sw/d_draw.S \ + libs/video/renderer/sw/d_edge.c \ + libs/video/renderer/sw/d_init.c \ + libs/video/renderer/sw/d_modech.c \ + libs/video/renderer/sw/d_part.c \ + libs/video/renderer/sw/d_parta.S \ + libs/video/renderer/sw/d_polysa.S \ + libs/video/renderer/sw/d_polyse.c \ + libs/video/renderer/sw/d_scan.c \ + libs/video/renderer/sw/d_scana.S \ + libs/video/renderer/sw/d_sky.c \ + libs/video/renderer/sw/d_spr8.S \ + libs/video/renderer/sw/d_sprite.c \ + libs/video/renderer/sw/d_surf.c \ + libs/video/renderer/sw/d_vars.c \ + libs/video/renderer/sw/d_varsa.S \ + libs/video/renderer/sw/d_zpoint.c \ + libs/video/renderer/sw/draw.c \ + libs/video/renderer/sw/fpu.c \ + libs/video/renderer/sw/fpua.S \ + libs/video/renderer/sw/nonintel.c \ + libs/video/renderer/sw/surf8.S \ + libs/video/renderer/sw/sw_fisheye.c \ + libs/video/renderer/sw/sw_graph.c \ + libs/video/renderer/sw/sw_raclip.c \ + libs/video/renderer/sw/sw_raclipa.S \ + libs/video/renderer/sw/sw_ralias.c \ + libs/video/renderer/sw/sw_raliasa.S \ + libs/video/renderer/sw/sw_rbsp.c \ + libs/video/renderer/sw/sw_rdraw.c \ + libs/video/renderer/sw/sw_rdrawa.S \ + libs/video/renderer/sw/sw_redge.c \ + libs/video/renderer/sw/sw_redgea.S \ + libs/video/renderer/sw/sw_riqm.c \ + libs/video/renderer/sw/sw_rmain.c \ + libs/video/renderer/sw/sw_rmisc.c \ + libs/video/renderer/sw/sw_rpart.c \ + libs/video/renderer/sw/sw_rsky.c \ + libs/video/renderer/sw/sw_rsprite.c \ + libs/video/renderer/sw/sw_rsurf.c \ + libs/video/renderer/sw/sw_rvarsa.S \ + libs/video/renderer/sw/transform.S \ + libs/video/renderer/sw/vid_common_sw.c + +rp_main_def_src = libs/video/renderer/vulkan/rp_main_def.plist +rp_main_def_gen = libs/video/renderer/vulkan/rp_main_def.plc +smp_quake_src = libs/video/renderer/vulkan/smp_quake.plist +smp_quake_gen = libs/video/renderer/vulkan/smp_quake.plc + +video_renderer_vulkan_libs = \ + libs/video/renderer/librender_vulkan.la \ + libs/models/libmodels_vulkan.la +libs_video_renderer_vid_render_vulkan_la_LDFLAGS= $(plugin_ldflags) +libs_video_renderer_vid_render_vulkan_la_LIBADD=\ + $(video_renderer_vulkan_libs) \ + $(renderer_libadd) +libs_video_renderer_vid_render_vulkan_la_DEPENDENCIES=$(video_renderer_vulkan_libs) +libs_video_renderer_vid_render_vulkan_la_SOURCES = \ + $(video_renderer_common_sources) \ + libs/video/renderer/vid_render_vulkan.c + +libs_video_renderer_librender_vulkan_la_SOURCES = \ + libs/video/renderer/vulkan/barrier.c \ + libs/video/renderer/vulkan/buffer.c \ + libs/video/renderer/vulkan/command.c \ + libs/video/renderer/vulkan/capture.c \ + libs/video/renderer/vulkan/debug.c \ + libs/video/renderer/vulkan/descriptor.c \ + libs/video/renderer/vulkan/device.c \ + libs/video/renderer/vulkan/dsmanager.c \ + libs/video/renderer/vulkan/image.c \ + libs/video/renderer/vulkan/instance.c \ + libs/video/renderer/vulkan/memory.c \ + libs/video/renderer/vulkan/pipeline.c \ + libs/video/renderer/vulkan/projection.c \ + libs/video/renderer/vulkan/render.c \ + libs/video/renderer/vulkan/render_load.c \ + libs/video/renderer/vulkan/resource.c \ + libs/video/renderer/vulkan/scrap.c \ + libs/video/renderer/vulkan/shader.c \ + libs/video/renderer/vulkan/staging.c \ + libs/video/renderer/vulkan/swapchain.c \ + libs/video/renderer/vulkan/util.c \ + libs/video/renderer/vulkan/util.h \ + libs/video/renderer/vulkan/vkparse.c \ + libs/video/renderer/vulkan/vulkan_alias.c \ + libs/video/renderer/vulkan/vulkan_bsp.c \ + libs/video/renderer/vulkan/vulkan_compose.c \ + libs/video/renderer/vulkan/vulkan_draw.c \ + libs/video/renderer/vulkan/vulkan_iqm.c \ + libs/video/renderer/vulkan/vulkan_lighting.c \ + libs/video/renderer/vulkan/vulkan_main.c \ + libs/video/renderer/vulkan/vulkan_matrices.c \ + libs/video/renderer/vulkan/vulkan_output.c \ + libs/video/renderer/vulkan/vulkan_palette.c \ + libs/video/renderer/vulkan/vulkan_particles.c \ + libs/video/renderer/vulkan/vulkan_scene.c \ + libs/video/renderer/vulkan/vulkan_sprite.c \ + libs/video/renderer/vulkan/vulkan_texture.c \ + libs/video/renderer/vulkan/vulkan_translucent.c \ + libs/video/renderer/vulkan/vulkan_vid_common.c + +libs/video/renderer/vulkan/vkparse.lo: \ + libs/video/renderer/vulkan/vkparse.c \ + $(vkparse_src) \ + $(rp_main_def_gen) \ + $(smp_quake_gen) + +libs/video/renderer/vulkan/shader.lo: \ + libs/video/renderer/vulkan/shader.c \ + $(vkshader_c) + + +qwaq_cmd = $(top_builddir)/ruamoko/qwaq/qwaq-cmd$(EXEEXT) +vkparse_cinc = $(top_builddir)/libs/video/renderer/vulkan/vkparse.cinc +vkparse_hinc = $(top_builddir)/libs/video/renderer/vulkan/vkparse.hinc +vkparse_src = \ + $(vkparse_cinc) \ + $(vkparse_hinc) +vkparse_plist = \ + $(srcdir)/libs/video/renderer/vulkan/vkparse.plist + +vkshaderpath = libs/video/renderer/vulkan/shader + +slice_src = $(vkshaderpath)/slice.vert +slice_c = $(vkshaderpath)/slice.vert.spvc +linev_src = $(vkshaderpath)/line.vert +linev_c = $(vkshaderpath)/line.vert.spvc +linef_src = $(vkshaderpath)/line.frag +linef_c = $(vkshaderpath)/line.frag.spvc +partphysicsc_src = $(vkshaderpath)/partphysics.comp +partphysicsc_c = $(vkshaderpath)/partphysics.comp.spvc +partupdatec_src = $(vkshaderpath)/partupdate.comp +partupdatec_c = $(vkshaderpath)/partupdate.comp.spvc +particlev_src = $(vkshaderpath)/particle.vert +particlev_c = $(vkshaderpath)/particle.vert.spvc +particleg_src = $(vkshaderpath)/particle.geom +particleg_c = $(vkshaderpath)/particle.geom.spvc +particlef_src = $(vkshaderpath)/particle.frag +particlef_c = $(vkshaderpath)/particle.frag.spvc +sprite_gbufv_src = $(vkshaderpath)/sprite_gbuf.vert +sprite_gbufv_c = $(vkshaderpath)/sprite_gbuf.vert.spvc +sprite_gbuff_src = $(vkshaderpath)/sprite_gbuf.frag +sprite_gbuff_c = $(vkshaderpath)/sprite_gbuf.frag.spvc +sprite_depthv_src = $(vkshaderpath)/sprite_depth.vert +sprite_depthv_c = $(vkshaderpath)/sprite_depth.vert.spvc +sprite_depthf_src = $(vkshaderpath)/sprite_depth.frag +sprite_depthf_c = $(vkshaderpath)/sprite_depth.frag.spvc +twod_depthf_src = $(vkshaderpath)/twod_depth.frag +twod_depthf_c = $(vkshaderpath)/twod_depth.frag.spvc +twodv_src = $(vkshaderpath)/twod.vert +twodv_c = $(vkshaderpath)/twod.vert.spvc +twodf_src = $(vkshaderpath)/twod.frag +twodf_c = $(vkshaderpath)/twod.frag.spvc +quakebspv_src = $(vkshaderpath)/quakebsp.vert +quakebspv_c = $(vkshaderpath)/quakebsp.vert.spvc +quakebspf_src = $(vkshaderpath)/quakebsp.frag +quakebspf_c = $(vkshaderpath)/quakebsp.frag.spvc + +bsp_depth_src = $(vkshaderpath)/bsp_depth.vert +bsp_depth_c = $(vkshaderpath)/bsp_depth.vert.spvc +bsp_gbufv_src = $(vkshaderpath)/bsp_gbuf.vert +bsp_gbufv_c = $(vkshaderpath)/bsp_gbuf.vert.spvc +bsp_gbufg_src = $(vkshaderpath)/bsp_gbuf.geom +bsp_gbufg_c = $(vkshaderpath)/bsp_gbuf.geom.spvc +bsp_gbuff_src = $(vkshaderpath)/bsp_gbuf.frag +bsp_gbuff_c = $(vkshaderpath)/bsp_gbuf.frag.spvc +bsp_shadow_src = $(vkshaderpath)/bsp_shadow.vert +bsp_shadow_c = $(vkshaderpath)/bsp_shadow.vert.spvc +bsp_skyf_src = $(vkshaderpath)/bsp_sky.frag +bsp_skyf_c = $(vkshaderpath)/bsp_sky.frag.spvc +bsp_turbf_src = $(vkshaderpath)/bsp_turb.frag +bsp_turbf_c = $(vkshaderpath)/bsp_turb.frag.spvc +lightingf_src = $(vkshaderpath)/lighting.frag +lightingf_c = $(vkshaderpath)/lighting.frag.spvc +composef_src = $(vkshaderpath)/compose.frag +composef_c = $(vkshaderpath)/compose.frag.spvc +oit_blend = $(vkshaderpath)/oit_blend.finc +oit_store = $(vkshaderpath)/oit_store.finc +oit_h = $(vkshaderpath)/oit.h +matrices_h = $(vkshaderpath)/matrices.h +entity_h = $(vkshaderpath)/entity.h +aliasv_src = $(vkshaderpath)/alias.vert +aliasv_c = $(vkshaderpath)/alias.vert.spvc +aliasf_src = $(vkshaderpath)/alias.frag +aliasf_c = $(vkshaderpath)/alias.frag.spvc +alias_depth_src = $(vkshaderpath)/alias_depth.vert +alias_depth_c = $(vkshaderpath)/alias_depth.vert.spvc +alias_gbuf_src = $(vkshaderpath)/alias_gbuf.frag +alias_gbuf_c = $(vkshaderpath)/alias_gbuf.frag.spvc +alias_shadow_src = $(vkshaderpath)/alias_shadow.vert +alias_shadow_c = $(vkshaderpath)/alias_shadow.vert.spvc +iqmv_src = $(vkshaderpath)/iqm.vert +iqmv_c = $(vkshaderpath)/iqm.vert.spvc +iqmf_src = $(vkshaderpath)/iqm.frag +iqmf_c = $(vkshaderpath)/iqm.frag.spvc +output_src = $(vkshaderpath)/output.frag +output_c = $(vkshaderpath)/output.frag.spvc +passthrough_src = $(vkshaderpath)/passthrough.vert +passthrough_c = $(vkshaderpath)/passthrough.vert.spvc +fstriangle_src = $(vkshaderpath)/fstriangle.vert +fstriangle_c = $(vkshaderpath)/fstriangle.vert.spvc +fstrianglest_src = $(vkshaderpath)/fstrianglest.vert +fstrianglest_c = $(vkshaderpath)/fstrianglest.vert.spvc +pushcolor_src = $(vkshaderpath)/pushcolor.frag +pushcolor_c = $(vkshaderpath)/pushcolor.frag.spvc +fisheye_src = $(vkshaderpath)/fisheye.frag +fisheye_c = $(vkshaderpath)/fisheye.frag.spvc +waterwarp_src = $(vkshaderpath)/waterwarp.frag +waterwarp_c = $(vkshaderpath)/waterwarp.frag.spvc + +$(slice_c): $(slice_src) $(matrices_h) + +$(linev_c): $(linev_src) $(matrices_h) +$(linef_c): $(linef_src) + +$(partphysicsc_c): $(partphysicsc_src) +$(partupdatec_c): $(partupdatec_src) +$(particlev_c): $(particlev_src) $(matrices_h) +$(particleg_c): $(particleg_src) $(matrices_h) +$(particlef_c): $(particlef_src) $(oit_store) $(oit_h) + +$(sprite_gbufv_c): $(sprite_gbufv_src) $(matrices_h) + +$(sprite_gbuff_c): $(sprite_gbuff_src) + +$(sprite_depthv_c): $(sprite_depthv_src) $(matrices_h) + +$(sprite_depthf_c): $(sprite_depthf_src) + +$(twod_depthf_c): $(twod_depthf_src) + +$(twodv_c): $(twodv_src) $(matrices_h) + +$(twodf_c): $(twodf_src) + +$(quakebspv_c): $(quakebspv_src) $(entity_h) $(matrices_h) + +$(quakebspf_c): $(quakebspf_src) + +$(bsp_depth_c): $(bsp_depth_src) $(entity_h) $(matrices_h) + +$(bsp_gbufv_c): $(bsp_gbufv_src) $(entity_h) $(matrices_h) + +$(bsp_gbufg_c): $(bsp_gbufg_src) $(matrices_h) + +$(bsp_gbuff_c): $(bsp_gbuff_src) + +$(bsp_shadow_c): $(bsp_shadow_src) $(entity_h) + +$(bsp_skyf_c): $(bsp_skyf_src) $(oit_store) $(oit_h) + +$(bsp_turbf_c): $(bsp_turbf_src) $(oit_store) $(oit_h) + +$(lightingf_c): $(lightingf_src) + +$(composef_c): $(composef_src) $(oit_blend) $(oit_h) + +$(aliasv_c): $(aliasv_src) $(matrices_h) + +$(alias_depth_c): $(alias_depth_src) $(matrices_h) + +$(aliasf_c): $(aliasf_src) + +$(alias_gbuf_c): $(alias_gbuf_src) + +$(alias_shadow_c): $(alias_shadow_src) + +$(iqmv_c): $(iqmv_src) $(matrices_h) + +$(iqmf_c): $(iqmf_src) + +$(output_c): $(output_src) $(matrices_h) + +$(passthrough_c): $(passthrough_src) + +$(fstriangle_c): $(fstriangle_src) + +$(fstrianglest_c): $(fstrianglest_src) + +$(pushcolor_c): $(pushcolor_src) + +$(fisheye_c): $(fisheye_src) $(matrices_h) + +$(waterwarp_c): $(waterwarp_src) $(matrices_h) + +vkshader_c = \ + $(slice_c) \ + $(linev_c) \ + $(linef_c) \ + $(partphysicsc_c) \ + $(partupdatec_c) \ + $(particlev_c) \ + $(particleg_c) \ + $(particlef_c) \ + $(sprite_gbufv_c) \ + $(sprite_gbuff_c) \ + $(sprite_depthv_c) \ + $(sprite_depthf_c) \ + $(twodv_c) \ + $(twodf_c) \ + $(twod_depthf_c) \ + $(quakebspv_c) \ + $(quakebspf_c) \ + $(bsp_depth_c) \ + $(bsp_gbufv_c) \ + $(bsp_gbufg_c) \ + $(bsp_gbuff_c) \ + $(bsp_shadow_c) \ + $(bsp_skyf_c) \ + $(bsp_turbf_c) \ + $(lightingf_c) \ + $(composef_c) \ + $(aliasv_c) \ + $(alias_depth_c) \ + $(aliasf_c) \ + $(alias_gbuf_c) \ + $(alias_shadow_c) \ + $(iqmv_c) \ + $(iqmf_c) \ + $(output_c) \ + $(passthrough_c) \ + $(fstriangle_c) \ + $(fstrianglest_c) \ + $(pushcolor_c) \ + $(shadow_c) \ + $(fisheye_c) \ + $(waterwarp_c) + +V_VKGEN = $(V_VKGEN_@AM_V@) +V_VKGEN_ = $(V_VKGEN_@AM_DEFAULT_V@) +V_VKGEN_0 = @echo " VKGEN " $@; +V_VKGEN_1 = + +$(vkparse_cinc): $(vkgen) $(qwaq_cmd) $(vkparse_plist) + $(V_VKGEN)$(QWAQ) $(vkgen) -- $(vkparse_plist) $(vkparse_cinc).t $(vkparse_hinc).t &&\ + $(am__mv) $(vkparse_cinc).t $(vkparse_cinc) &&\ + $(am__mv) $(vkparse_hinc).t $(vkparse_hinc) + +$(vkparse_hinc): $(vkparse_cinc) +# do nothing: hinc generated at the same time as cinc + +CLEANFILES += \ + libs/video/renderer/glsl/*.vc \ + libs/video/renderer/glsl/*.fc \ + libs/video/renderer/glsl/*.slc \ + libs/video/renderer/vulkan/*.plc \ + libs/video/renderer/vulkan/shader/*.spv \ + libs/video/renderer/vulkan/shader/*.spvc \ + vkgen.sym \ + $(vkparse_src) + +BUILT_SOURCES += $(shader_gen) + +#shader_DATA += \ +# libs/video/renderer/vulkan/passthrough.vert.spv \ +# libs/video/renderer/vulkan/pushcolor.frag.spv + +EXTRA_DIST += \ + libs/video/renderer/vulkan/vkparse.plist \ + libs/video/renderer/vulkan/vkparse.h \ + $(rp_main_def_src) \ + $(smp_quake_src) \ + $(oit_blend) \ + $(oit_store) \ + $(oit_h) \ + $(matrices_h) \ + $(entity_h) \ + $(slice_src) \ + $(linev_src) \ + $(linef_src) \ + $(partphysicsc_src) \ + $(partupdatec_src) \ + $(particlev_src) \ + $(particleg_src) \ + $(particlef_src) \ + $(sprite_gbufv_src) \ + $(sprite_gbuff_src) \ + $(sprite_depthv_src) \ + $(sprite_depthf_src) \ + $(twod_depthf_src) \ + $(twodv_src) \ + $(twodf_src) \ + $(quakebspv_src) \ + $(quakebspf_src) \ + $(bsp_depth_src) \ + $(bsp_gbufv_src) \ + $(bsp_gbufg_src) \ + $(bsp_gbuff_src) \ + $(bsp_shadow_src) \ + $(bsp_skyf_src) \ + $(bsp_turbf_src) \ + $(lightingf_src) \ + $(composef_src) \ + $(aliasv_src) \ + $(aliasf_src) \ + $(alias_depth_src) \ + $(alias_gbuf_src) \ + $(alias_shadow_src) \ + $(iqmv_src) \ + $(iqmf_src) \ + $(output_src) \ + $(passthrough_src) \ + $(fstriangle_src) \ + $(fstrianglest_src) \ + $(pushcolor_src) \ + $(fisheye_src) \ + $(waterwarp_src) diff --git a/libs/video/renderer/crosshair.c b/libs/video/renderer/crosshair.c index 7870c7aad..60b237a1b 100644 --- a/libs/video/renderer/crosshair.c +++ b/libs/video/renderer/crosshair.c @@ -35,46 +35,50 @@ #include "r_internal.h" +#define _ 0xff +#define X 0xfe // NOTE: this array is INCORRECT for direct uploading in GL // but is optimal for SW byte crosshair_data[CROSSHAIR_WIDTH * CROSSHAIR_HEIGHT * CROSSHAIR_COUNT] = { - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + _,_,_,X,_,_,_,_, + _,_,_,_,_,_,_,_, + _,_,_,X,_,_,_,_, + X,_,X,_,X,_,X,_, + _,_,_,X,_,_,_,_, + _,_,_,_,_,_,_,_, + _,_,_,X,_,_,_,_, + _,_,_,_,_,_,_,_, - 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + X,_,_,_,_,_,X,_, + _,X,_,_,_,X,_,_, + _,_,_,_,_,_,_,_, + _,_,_,_,_,_,_,_, + _,_,_,_,_,_,_,_, + _,X,_,_,_,X,_,_, + X,_,_,_,_,_,X,_, + _,_,_,_,_,_,_,_, //From FitzQuake - 255,255,255,255,255,255,255,255, - 255,255,255, 8, 9,255,255,255, - 255,255,255, 6, 8, 2,255,255, - 255, 6, 8, 8, 6, 8, 8,255, - 255,255, 2, 8, 8, 2, 2, 2, - 255,255,255, 7, 8, 2,255,255, - 255,255,255,255, 2, 2,255,255, - 255,255,255,255,255,255,255,255, + _,_,_,_,_,_,_,_, + _,_,_,8,9,_,_,_, + _,_,_,6,8,2,_,_, + _,6,8,8,6,8,8,_, + _,_,2,8,8,2,2,2, + _,_,_,7,8,2,_,_, + _,_,_,_,2,2,_,_, + _,_,_,_,_,_,_,_, - 0xff,0xff,0xfe,0xfe,0xfe,0xff,0xff,0xff, - 0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff, - 0xfe,0xff,0xff,0xff,0xff,0xff,0xfe,0xff, - 0xfe,0xff,0xff,0xfe,0xff,0xff,0xfe,0xff, - 0xfe,0xff,0xff,0xff,0xff,0xff,0xfe,0xff, - 0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff, - 0xff,0xff,0xfe,0xfe,0xfe,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + _,_,X,X,X,_,_,_, + _,X,_,_,_,X,_,_, + X,_,_,_,_,_,X,_, + X,_,_,X,_,_,X,_, + X,_,_,_,_,_,X,_, + _,X,_,_,_,X,_,_, + _,_,X,X,X,_,_,_, + _,_,_,_,_,_,_,_, }; +#undef _ +#undef X qpic_t * Draw_CrosshairPic (void) diff --git a/libs/video/renderer/font8x8.c b/libs/video/renderer/font8x8.c new file mode 100644 index 000000000..ab78882a5 --- /dev/null +++ b/libs/video/renderer/font8x8.c @@ -0,0 +1,336 @@ +/* + r_font8x8.c + + 8x8 bitmap font data. + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/1/8 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "QF/qtypes.h" +#include "QF/wadfile.h" + +#include "r_internal.h" + +//NOTE: This is packed 8x8 bitmap data, one byte per scanline, 8 scanlines +//per character. Also, it is NOT the quake font, but the IBM charset. +byte font8x8_data[] = { + 0xff, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xff, + 0x7E, 0x81, 0xA5, 0x81, 0xBD, 0x99, 0x81, 0x7E, + 0x7E, 0xFF, 0xDB, 0xFF, 0xC3, 0xE7, 0xFF, 0x7E, + 0x6C, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00, + 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x10, 0x00, + 0x38, 0x7C, 0x38, 0xFE, 0xFE, 0x92, 0x10, 0x7C, + 0x00, 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x7C, + 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x18, 0x00, 0x00, + 0xFF, 0xFF, 0xE7, 0xC3, 0xC3, 0xE7, 0xFF, 0xFF, + 0x00, 0x3C, 0x66, 0x42, 0x42, 0x66, 0x3C, 0x00, + 0xFF, 0xC3, 0x99, 0xBD, 0xBD, 0x99, 0xC3, 0xFF, + 0x0F, 0x07, 0x0F, 0x7D, 0xCC, 0xCC, 0xCC, 0x78, + 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18, + 0x3F, 0x33, 0x3F, 0x30, 0x30, 0x70, 0xF0, 0xE0, + 0x7F, 0x63, 0x7F, 0x63, 0x63, 0x67, 0xE6, 0xC0, + 0x99, 0x5A, 0x3C, 0xE7, 0xE7, 0x3C, 0x5A, 0x99, + 0x80, 0xE0, 0xF8, 0xFE, 0xF8, 0xE0, 0x80, 0x00, + 0x02, 0x0E, 0x3E, 0xFE, 0x3E, 0x0E, 0x02, 0x00, + 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x7E, 0x3C, 0x18, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, + 0x7F, 0xDB, 0xDB, 0x7B, 0x1B, 0x1B, 0x1B, 0x00, + 0x3E, 0x63, 0x38, 0x6C, 0x6C, 0x38, 0x86, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x7E, 0x00, + 0x18, 0x3C, 0x7E, 0x18, 0x7E, 0x3C, 0x18, 0xFF, + 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00, + 0x00, 0x18, 0x0C, 0xFE, 0x0C, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x60, 0xFE, 0x60, 0x30, 0x00, 0x00, + 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xFE, 0x00, 0x00, + 0x00, 0x24, 0x66, 0xFF, 0x66, 0x24, 0x00, 0x00, + 0x00, 0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x7E, 0x3C, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00, + 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00, + 0x18, 0x7E, 0xC0, 0x7C, 0x06, 0xFC, 0x18, 0x00, + 0x00, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0x00, + 0x38, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0x76, 0x00, + 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, + 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, + 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, + 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, + 0x7C, 0xCE, 0xDE, 0xF6, 0xE6, 0xC6, 0x7C, 0x00, + 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xFC, 0x00, + 0x78, 0xCC, 0x0C, 0x38, 0x60, 0xCC, 0xFC, 0x00, + 0x78, 0xCC, 0x0C, 0x38, 0x0C, 0xCC, 0x78, 0x00, + 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x1E, 0x00, + 0xFC, 0xC0, 0xF8, 0x0C, 0x0C, 0xCC, 0x78, 0x00, + 0x38, 0x60, 0xC0, 0xF8, 0xCC, 0xCC, 0x78, 0x00, + 0xFC, 0xCC, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00, + 0x78, 0xCC, 0xCC, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x78, 0xCC, 0xCC, 0x7C, 0x0C, 0x18, 0x70, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, + 0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x00, + 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00, + 0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60, 0x00, + 0x3C, 0x66, 0x0C, 0x18, 0x18, 0x00, 0x18, 0x00, + 0x7C, 0xC6, 0xDE, 0xDE, 0xDC, 0xC0, 0x7C, 0x00, + 0x30, 0x78, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0x00, + 0xFC, 0x66, 0x66, 0x7C, 0x66, 0x66, 0xFC, 0x00, + 0x3C, 0x66, 0xC0, 0xC0, 0xC0, 0x66, 0x3C, 0x00, + 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00, + 0xFE, 0x62, 0x68, 0x78, 0x68, 0x62, 0xFE, 0x00, + 0xFE, 0x62, 0x68, 0x78, 0x68, 0x60, 0xF0, 0x00, + 0x3C, 0x66, 0xC0, 0xC0, 0xCE, 0x66, 0x3A, 0x00, + 0xCC, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0xCC, 0x00, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x1E, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00, + 0xE6, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0xE6, 0x00, + 0xF0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00, + 0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6, 0xC6, 0x00, + 0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00, + 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x00, + 0xFC, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00, + 0x7C, 0xC6, 0xC6, 0xC6, 0xD6, 0x7C, 0x0E, 0x00, + 0xFC, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0xE6, 0x00, + 0x7C, 0xC6, 0xE0, 0x78, 0x0E, 0xC6, 0x7C, 0x00, + 0xFC, 0xB4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xFC, 0x00, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, + 0xC6, 0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0x6C, 0x00, + 0xC6, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0xC6, 0x00, + 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x30, 0x78, 0x00, + 0xFE, 0xC6, 0x8C, 0x18, 0x32, 0x66, 0xFE, 0x00, + 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, + 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x02, 0x00, + 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, + 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0xE0, 0x60, 0x60, 0x7C, 0x66, 0x66, 0xDC, 0x00, + 0x00, 0x00, 0x78, 0xCC, 0xC0, 0xCC, 0x78, 0x00, + 0x1C, 0x0C, 0x0C, 0x7C, 0xCC, 0xCC, 0x76, 0x00, + 0x00, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, + 0x38, 0x6C, 0x64, 0xF0, 0x60, 0x60, 0xF0, 0x00, + 0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, + 0xE0, 0x60, 0x6C, 0x76, 0x66, 0x66, 0xE6, 0x00, + 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x0C, 0x00, 0x1C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, + 0xE0, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0xE6, 0x00, + 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x00, 0xCC, 0xFE, 0xFE, 0xD6, 0xD6, 0x00, + 0x00, 0x00, 0xB8, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, + 0x00, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0x00, 0xDC, 0x66, 0x66, 0x7C, 0x60, 0xF0, + 0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0x1E, + 0x00, 0x00, 0xDC, 0x76, 0x62, 0x60, 0xF0, 0x00, + 0x00, 0x00, 0x7C, 0xC0, 0x70, 0x1C, 0xF8, 0x00, + 0x10, 0x30, 0xFC, 0x30, 0x30, 0x34, 0x18, 0x00, + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, + 0x00, 0x00, 0xC6, 0xC6, 0xD6, 0xFE, 0x6C, 0x00, + 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00, + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, + 0x00, 0x00, 0xFC, 0x98, 0x30, 0x64, 0xFC, 0x00, + 0x1C, 0x30, 0x30, 0xE0, 0x30, 0x30, 0x1C, 0x00, + 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, + 0xE0, 0x30, 0x30, 0x1C, 0x30, 0x30, 0xE0, 0x00, + 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0x00, + 0x7C, 0xC6, 0xC0, 0xC6, 0x7C, 0x0C, 0x06, 0x7C, + 0x00, 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, + 0x1C, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, + 0x7E, 0x81, 0x3C, 0x06, 0x3E, 0x66, 0x3B, 0x00, + 0xCC, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0xE0, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0x30, 0x30, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0xC0, 0x78, 0x0C, 0x38, + 0x7E, 0x81, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, + 0xCC, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, + 0xE0, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, + 0xCC, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x7C, 0x82, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00, + 0xE0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xC6, 0x10, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, + 0x30, 0x30, 0x00, 0x78, 0xCC, 0xFC, 0xCC, 0x00, + 0x1C, 0x00, 0xFC, 0x60, 0x78, 0x60, 0xFC, 0x00, + 0x00, 0x00, 0x7F, 0x0C, 0x7F, 0xCC, 0x7F, 0x00, + 0x3E, 0x6C, 0xCC, 0xFE, 0xCC, 0xCC, 0xCE, 0x00, + 0x78, 0x84, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0xCC, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0xE0, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x78, 0x84, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, + 0x00, 0xE0, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, + 0x00, 0xCC, 0x00, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, + 0xC3, 0x18, 0x3C, 0x66, 0x66, 0x3C, 0x18, 0x00, + 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x00, + 0x18, 0x18, 0x7E, 0xC0, 0xC0, 0x7E, 0x18, 0x18, + 0x38, 0x6C, 0x64, 0xF0, 0x60, 0xE6, 0xFC, 0x00, + 0xCC, 0xCC, 0x78, 0x30, 0xFC, 0x30, 0xFC, 0x30, + 0xF8, 0xCC, 0xCC, 0xFA, 0xC6, 0xCF, 0xC6, 0xC3, + 0x0E, 0x1B, 0x18, 0x3C, 0x18, 0x18, 0xD8, 0x70, + 0x1C, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x1C, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0x1C, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, + 0x00, 0xF8, 0x00, 0xB8, 0xCC, 0xCC, 0xCC, 0x00, + 0xFC, 0x00, 0xCC, 0xEC, 0xFC, 0xDC, 0xCC, 0x00, + 0x3C, 0x6C, 0x6C, 0x3E, 0x00, 0x7E, 0x00, 0x00, + 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x7C, 0x00, 0x00, + 0x18, 0x00, 0x18, 0x18, 0x30, 0x66, 0x3C, 0x00, + 0x00, 0x00, 0x00, 0xFC, 0xC0, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFC, 0x0C, 0x0C, 0x00, 0x00, + 0xC6, 0xCC, 0xD8, 0x36, 0x6B, 0xC2, 0x84, 0x0F, + 0xC3, 0xC6, 0xCC, 0xDB, 0x37, 0x6D, 0xCF, 0x03, + 0x18, 0x00, 0x18, 0x18, 0x3C, 0x3C, 0x18, 0x00, + 0x00, 0x33, 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00, + 0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00, + 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, + 0xDB, 0xF6, 0xDB, 0x6F, 0xDB, 0x7E, 0xD7, 0xED, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xF8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0xF6, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0xFE, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, + 0x36, 0x36, 0xF6, 0x06, 0xF6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xFE, 0x06, 0xF6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xF6, 0x06, 0xFE, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0xFE, 0x00, 0x00, 0x00, + 0x18, 0x18, 0xF8, 0x18, 0xF8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xF8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1F, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x30, 0x3F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0x30, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xF7, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x36, 0x36, 0xF7, 0x00, 0xF7, 0x36, 0x36, 0x36, + 0x18, 0x18, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x3F, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x1F, 0x18, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x3F, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0xFF, 0x36, 0x36, 0x36, + 0x18, 0x18, 0xFF, 0x18, 0xFF, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xF8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x18, 0x18, 0x18, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xDC, 0xC8, 0xDC, 0x76, 0x00, + 0x00, 0x78, 0xCC, 0xF8, 0xCC, 0xCC, 0xF8, 0xC0, + 0x00, 0xFC, 0xCC, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, + 0x00, 0x00, 0xFE, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, + 0xFC, 0xCC, 0x60, 0x30, 0x60, 0xCC, 0xFC, 0x00, + 0x00, 0x00, 0x7E, 0xD8, 0xD8, 0xD8, 0x70, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0xC0, + 0x00, 0x76, 0xDC, 0x18, 0x18, 0x18, 0x18, 0x00, + 0xFC, 0x30, 0x78, 0xCC, 0xCC, 0x78, 0x30, 0xFC, + 0x38, 0x6C, 0xC6, 0xFE, 0xC6, 0x6C, 0x38, 0x00, + 0x38, 0x6C, 0xC6, 0xC6, 0x6C, 0x6C, 0xEE, 0x00, + 0x1C, 0x30, 0x18, 0x7C, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0x00, 0x7E, 0xDB, 0xDB, 0x7E, 0x00, 0x00, + 0x06, 0x0C, 0x7E, 0xDB, 0xDB, 0x7E, 0x60, 0xC0, + 0x38, 0x60, 0xC0, 0xF8, 0xC0, 0x60, 0x38, 0x00, + 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, + 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, + 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x7E, 0x00, + 0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xFC, 0x00, + 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xFC, 0x00, + 0x0E, 0x1B, 0x1B, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xD8, 0xD8, 0x70, + 0x18, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x76, 0xDC, 0x00, 0x76, 0xDC, 0x00, 0x00, + 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0F, 0x0C, 0x0C, 0x0C, 0xEC, 0x6C, 0x3C, 0x1C, + 0x58, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, + 0x70, 0x98, 0x30, 0x60, 0xF8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0x00, + 0xff, 0x99, 0x99, 0xff, 0xff, 0x99, 0x99, 0xff, +}; +#define FONT_WIDTH 8 +#define FONT_HEIGHT 8 +#define FONT_HCHARS 16 +#define FONT_VCHARS 16 +#define FONT_BYTES ((FONT_VCHARS)*(FONT_HCHARS)*(FONT_HEIGHT)*(FONT_WIDTH)) + +qpic_t * +Draw_Font8x8Pic (void) +{ + qpic_t *pic; + byte *data; + byte *out; + int i, j, x, y, ind; + + pic = calloc (1, field_offset (qpic_t, data[FONT_BYTES])); + pic->width = FONT_HCHARS * FONT_WIDTH; + pic->height = FONT_VCHARS * FONT_HEIGHT; + // convert the bitmap data to pixel data in a 16x16 character "image" + // assumes the quake palette where 254 is white, and 255 is transparent + data = font8x8_data; + for (j = 0; j < FONT_VCHARS; j++) { + for (i = 0; i < FONT_HCHARS; i++) { + for (y = 0; y < FONT_HEIGHT; y++) { + byte scanline = *data++; + ind = (j * FONT_HEIGHT + y) * FONT_WIDTH * FONT_HCHARS; + ind += i * FONT_WIDTH; + out = pic->data + ind; + for (x = 0; x < FONT_WIDTH; x++) { + *out++ = 0xff - ((scanline & 0x80) >> 7); + scanline <<= 1; + } + } + } + } + return pic; +} diff --git a/libs/video/renderer/gl/Makefile.am b/libs/video/renderer/gl/Makefile.am deleted file mode 100644 index 041db8546..000000000 --- a/libs/video/renderer/gl/Makefile.am +++ /dev/null @@ -1,16 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CFLAGS= @PREFER_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include $(GLX_CFLAGS) - -noinst_LTLIBRARIES= libgl.la - -gl_src = \ - gl_draw.c gl_dyn_lights.c gl_dyn_part.c gl_dyn_textures.c \ - gl_fog.c gl_graph.c gl_lightmap.c gl_mod_alias.c gl_mod_iqm.c \ - gl_mod_sprite.c gl_rmain.c gl_rmisc.c gl_rsurf.c gl_screen.c gl_sky.c \ - gl_sky_clip.c gl_textures.c gl_warp.c qfgl_ext.c vid_common_gl.c vtxarray.c - -libgl_la_SOURCES= $(gl_src) - -EXTRA_DIST = $(gl_src) namehack.h diff --git a/libs/video/renderer/gl/gl_draw.c b/libs/video/renderer/gl/gl_draw.c index f988ddf66..3cc910b95 100644 --- a/libs/video/renderer/gl/gl_draw.c +++ b/libs/video/renderer/gl/gl_draw.c @@ -28,9 +28,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -59,10 +56,11 @@ #include "QF/GL/qf_textures.h" #include "QF/GL/qf_vid.h" #include "QF/GL/types.h" +#include "QF/ui/font.h" +#include "QF/ui/view.h" #include "compat.h" #include "r_internal.h" -#include "sbar.h" #include "varrays.h" #define CELL_SIZE (1.0 / 16.0) // conchars is 16x16 @@ -84,9 +82,18 @@ static float *textCoords, *tC; static qpic_t *draw_backtile; static cc_cell_t char_cells[256]; -static int translate_texture; +static GLuint translate_texture; static int char_texture; static int cs_texture; // crosshair texturea +static int gl_conback_texnum; +static cvar_t gl_conback_texnum_cvar = { + .name = "gl_conback_texnum", + .description = + "bind conback to this texture for debugging", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &gl_conback_texnum }, +}; static byte color_0_8[4] = { 204, 204, 204, 255 }; @@ -96,9 +103,11 @@ typedef struct { typedef struct cachepic_s { char name[MAX_QPATH]; - qboolean dirty; - qpic_t pic; - byte padding[32]; // for appended glpic + bool dirty; + union { + qpic_t pic; + byte padding[sizeof (qpic_t) + 32];// for appended glpic FIXME + }; } cachepic_t; #define MAX_CACHED_PICS 128 @@ -107,6 +116,17 @@ static int numcachepics; static byte menuplyr_pixels[4096]; +static int gl_2d_scale = 1; + +typedef struct glfont_s { + font_t *font; + GLuint texid; +} glfont_t; + +typedef struct glfontset_s + DARRAY_TYPE (glfont_t) glfontset_t; + +static glfontset_t gl_fonts = DARRAY_STATIC_INIT (16); static void Draw_InitText (void) @@ -116,14 +136,14 @@ Draw_InitText (void) if (vaelements < 0) { textUseVA = 0; tVAsize = 2048; - Sys_MaskPrintf (SYS_DEV, "Text: Vertex Array use disabled.\n"); + Sys_MaskPrintf (SYS_dev, "Text: Vertex Array use disabled.\n"); } else { textUseVA = 1; if (vaelements > 3) tVAsize = vaelements - (vaelements % 4); else tVAsize = 2048; - Sys_MaskPrintf (SYS_DEV, "Text: %i maximum vertex elements.\n", + Sys_MaskPrintf (SYS_dev, "Text: %i maximum vertex elements.\n", tVAsize); } @@ -171,11 +191,11 @@ qpic_t * gl_Draw_PicFromWad (const char *name) { glpic_t *gl; - qpic_t *p, *pic; + qpic_t *p = 0, *pic; tex_t *targa; pic = W_GetLumpName (name); - targa = LoadImage (name); + targa = LoadImage (name, 1); if (targa) { p = malloc (sizeof (qpic_t) + sizeof (glpic_t)); p->width = pic->width; @@ -187,7 +207,7 @@ gl_Draw_PicFromWad (const char *name) } else gl->texnum = GL_LoadTexture (name, targa->width, targa->height, targa->data, false, true, 4); - } else { + } else if (pic) { p = pic; gl = (glpic_t *) p->data; gl->texnum = GL_LoadTexture (name, p->width, p->height, p->data, @@ -197,7 +217,7 @@ gl_Draw_PicFromWad (const char *name) } static void -Draw_ClearCache (int phase) +Draw_ClearCache (int phase, void *data) { cachepic_t *pic; int i; @@ -209,7 +229,7 @@ Draw_ClearCache (int phase) } qpic_t * -gl_Draw_CachePic (const char *path, qboolean alpha) +gl_Draw_CachePic (const char *path, bool alpha) { cachepic_t *pic; int i; @@ -236,7 +256,7 @@ gl_Draw_CachePic (const char *path, qboolean alpha) // Adjust for endian.. SwapPic (dat); // Check for a .tga first - targa = LoadImage (path); + targa = LoadImage (path, 1); if (targa) { if (targa->format < 4) { gl->texnum = GL_LoadTexture ("", targa->width, targa->height, @@ -256,7 +276,8 @@ gl_Draw_CachePic (const char *path, qboolean alpha) } else Sys_Error ("Draw_CachePic: failed to load %s", path); - strncpy (pic->name, path, sizeof (pic->name)); + memset (pic->name, 0, sizeof (pic->name)); + strncpy (pic->name, path, sizeof (pic->name) - 1); // Now lets mark this cache entry as used.. pic->dirty = false; @@ -346,13 +367,13 @@ gl_Draw_Init (void) Cmd_AddCommand ("gl_texturemode", &GL_TextureMode_f, "Texture mipmap quality."); - QFS_GamedirCallback (Draw_ClearCache); + QFS_GamedirCallback (Draw_ClearCache, 0); // load the console background and the charset by hand, because we need to // write the version string into the background before turning it into a // texture - image = LoadImage ("gfx/conchars"); + image = LoadImage ("gfx/conchars", 1); if (image) { if (image->format < 4) { char_texture = GL_LoadTexture ("charset", image->width, @@ -364,17 +385,26 @@ gl_Draw_Init (void) true, 4); width = image->width; height = image->height; - } else { - draw_chars = W_GetLumpName ("conchars"); - for (i = 0; i < 256 * 64; i++) - if (draw_chars[i] == 0) + } else if ((draw_chars = W_GetLumpName ("conchars"))) { + for (i = 0; i < 256 * 64; i++) { + if (draw_chars[i] == 0) { draw_chars[i] = 255; // proper transparent color + } + } char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true, 1); width = 128; height = 128; + } else { + qpic_t *charspic = Draw_Font8x8Pic (); + char_texture = GL_LoadTexture ("charset", 128, 128, charspic->data, + false, true, 1); + width = 128; + height = 128; } + qfglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + qfglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // initialize the character cell texture coordinates. for (i = 0; i < 256; i++) { @@ -404,16 +434,14 @@ gl_Draw_Init (void) qfglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // save a texture slot for translated picture - translate_texture = gl_texture_number++; + qfglGenTextures (1, &translate_texture); // get the other pics we need draw_backtile = gl_Draw_PicFromWad ("backtile"); - // LordHavoc: call init code for other GL renderer modules - glrmain_init (); - gl_lightmap_init (); - Draw_InitText (); + + Cvar_Register (&gl_conback_texnum_cvar, 0, 0); } static inline void @@ -471,6 +499,21 @@ tVA_increment (void) flush_text (); } +void +gl_Draw_CharBuffer (int x, int y, draw_charbuffer_t *buffer) +{ + const byte *line = (byte *) buffer->chars; + int width = buffer->width; + int height = buffer->height; + while (height-- > 0) { + for (int i = 0; i < width; i++) { + gl_Draw_Character (x + i * 8, y, line[i]); + } + line += width; + y += 8; + } +} + /* Draw_Character @@ -572,20 +615,20 @@ crosshair_2 (int x, int y) { unsigned char *pColor; - pColor = (unsigned char *) &d_8to24table[crosshaircolor->int_val]; + pColor = (unsigned char *) &d_8to24table[crosshaircolor]; qfglColor4ubv (pColor); qfglBindTexture (GL_TEXTURE_2D, cs_texture); qfglBegin (GL_QUADS); qfglTexCoord2f (0, 0); - qfglVertex2f (x - 7, y - 7); + qfglVertex2f (x - 3, y - 3); qfglTexCoord2f (0.5, 0); - qfglVertex2f (x + 9, y - 7); + qfglVertex2f (x + 5, y - 3); qfglTexCoord2f (0.5, 0.5); - qfglVertex2f (x + 9, y + 9); + qfglVertex2f (x + 5, y + 5); qfglTexCoord2f (0, 0.5); - qfglVertex2f (x - 7, y + 9); + qfglVertex2f (x - 3, y + 5); qfglEnd (); qfglColor3ubv (color_white); @@ -596,20 +639,20 @@ crosshair_3 (int x, int y) { unsigned char *pColor; - pColor = (unsigned char *) &d_8to24table[crosshaircolor->int_val]; + pColor = (unsigned char *) &d_8to24table[crosshaircolor]; qfglColor4ubv (pColor); qfglBindTexture (GL_TEXTURE_2D, cs_texture); qfglBegin (GL_QUADS); qfglTexCoord2f (0.5, 0); - qfglVertex2f (x - 7, y - 7); + qfglVertex2f (x - 3, y - 3); qfglTexCoord2f (1, 0); - qfglVertex2f (x + 9, y - 7); + qfglVertex2f (x + 5, y - 3); qfglTexCoord2f (1, 0.5); - qfglVertex2f (x + 9, y + 9); + qfglVertex2f (x + 5, y + 5); qfglTexCoord2f (0.5, 0.5); - qfglVertex2f (x - 7, y + 9); + qfglVertex2f (x - 3, y + 5); qfglEnd (); qfglColor3ubv (color_white); @@ -620,20 +663,20 @@ crosshair_4 (int x, int y) { unsigned char *pColor; - pColor = (unsigned char *) &d_8to24table[crosshaircolor->int_val]; + pColor = (unsigned char *) &d_8to24table[crosshaircolor]; qfglColor4ubv (pColor); qfglBindTexture (GL_TEXTURE_2D, cs_texture); qfglBegin (GL_QUADS); qfglTexCoord2f (0, 0.5); - qfglVertex2f (x - 7, y - 7); + qfglVertex2f (x - 3, y - 3); qfglTexCoord2f (0.5, 0.5); - qfglVertex2f (x + 9, y - 7); + qfglVertex2f (x + 5, y - 5); qfglTexCoord2f (0.5, 1); - qfglVertex2f (x + 9, y + 9); + qfglVertex2f (x + 5, y + 5); qfglTexCoord2f (0, 1); - qfglVertex2f (x - 7, y + 9); + qfglVertex2f (x - 3, y + 5); qfglEnd (); qfglColor3ubv (color_white); @@ -644,20 +687,20 @@ crosshair_5 (int x, int y) //FIXME don't use until the data is filled in { unsigned char *pColor; - pColor = (unsigned char *) &d_8to24table[crosshaircolor->int_val]; + pColor = (unsigned char *) &d_8to24table[crosshaircolor]; qfglColor4ubv (pColor); qfglBindTexture (GL_TEXTURE_2D, cs_texture); qfglBegin (GL_QUADS); qfglTexCoord2f (0.5, 0.5); - qfglVertex2f (x - 7, y - 7); + qfglVertex2f (x - 3, y - 3); qfglTexCoord2f (1, 0.5); - qfglVertex2f (x + 9, y - 7); + qfglVertex2f (x + 5, y - 3); qfglTexCoord2f (1, 1); - qfglVertex2f (x + 9, y + 9); + qfglVertex2f (x + 5, y + 5); qfglTexCoord2f (0.5, 1); - qfglVertex2f (x - 7, y + 9); + qfglVertex2f (x - 3, y + 5); qfglEnd (); qfglColor3ubv (color_white); @@ -677,12 +720,13 @@ gl_Draw_Crosshair (void) int x, y; int ch; - ch = crosshair->int_val - 1; + ch = crosshair - 1; if ((unsigned) ch >= sizeof (crosshair_func) / sizeof (crosshair_func[0])) return; - x = vid.conwidth / 2 + cl_crossx->int_val; - y = vid.conheight / 2 + cl_crossy->int_val; + int s = 2 * gl_2d_scale; + x = vid.width / s + cl_crossx; + y = vid.height / s + cl_crossy; crosshair_func[ch] (x, y); } @@ -717,6 +761,26 @@ gl_Draw_Pic (int x, int y, qpic_t *pic) qfglEnd (); } +void +gl_Draw_FitPic (int x, int y, int width, int height, qpic_t *pic) +{ + glpic_t *gl; + + gl = (glpic_t *) pic->data; + + qfglBindTexture (GL_TEXTURE_2D, gl->texnum); + qfglBegin (GL_QUADS); + qfglTexCoord2f (0, 0); + qfglVertex2f (x, y); + qfglTexCoord2f (1, 0); + qfglVertex2f (x + width, y); + qfglTexCoord2f (1, 1); + qfglVertex2f (x + width, y + height); + qfglTexCoord2f (0, 1); + qfglVertex2f (x, y + height); + qfglEnd (); +} + void gl_Draw_Picf (float x, float y, qpic_t *pic) { @@ -787,7 +851,7 @@ gl_Draw_ConsoleBackground (int lines, byte alpha) gl = (glpic_t *) conback->data; // spin the console? - effect described in a QER tutorial - if (gl_conspin->value) { + if (gl_conspin) { static float xangle = 0; static float xfactor = .3f; static float xstep = .005f; @@ -796,7 +860,7 @@ gl_Draw_ConsoleBackground (int lines, byte alpha) qfglMatrixMode (GL_TEXTURE); qfglPushMatrix (); qfglLoadIdentity (); - xangle += gl_conspin->value; + xangle += gl_conspin; xfactor += xstep; if (xfactor > 8 || xfactor < .3f) xstep = -xstep; @@ -804,23 +868,27 @@ gl_Draw_ConsoleBackground (int lines, byte alpha) qfglScalef (xfactor, xfactor, xfactor); } // slide console up/down or stretch it? - if (gl_constretch->int_val) { + if (gl_constretch) { ofs = 0; } else - ofs = (vid.conheight - lines) / (float) vid.conheight; + ofs = (vid.height - gl_2d_scale * lines) / (float) vid.height; color_0_8[3] = alpha; qfglColor4ubv (color_0_8); // draw the console texture - qfglBindTexture (GL_TEXTURE_2D, gl->texnum); + if (gl_conback_texnum) { + qfglBindTexture (GL_TEXTURE_2D, gl_conback_texnum); + } else { + qfglBindTexture (GL_TEXTURE_2D, gl->texnum); + } qfglBegin (GL_QUADS); qfglTexCoord2f (0, 0 + ofs); qfglVertex2f (0, 0); qfglTexCoord2f (1, 0 + ofs); - qfglVertex2f (vid.conwidth, 0); + qfglVertex2f (vid.width / gl_2d_scale, 0); qfglTexCoord2f (1, 1); - qfglVertex2f (vid.conwidth, lines); + qfglVertex2f (vid.width / gl_2d_scale, lines); qfglTexCoord2f (0, 1); qfglVertex2f (0, lines); qfglEnd (); @@ -830,14 +898,15 @@ gl_Draw_ConsoleBackground (int lines, byte alpha) qfglColor3ubv (color_0_8); } - if (gl_conspin->value) { + if (gl_conspin) { qfglPopMatrix (); qfglMatrixMode (GL_MODELVIEW); qfglPopMatrix (); } - gl_Draw_AltString (vid.conwidth - strlen (cl_verstring->string) * 8 - 11, - lines - 14, cl_verstring->string); + int len = strlen (cl_verstring); + gl_Draw_AltString (vid.width - len * 8 - 11, lines - 14, + cl_verstring); qfglColor3ubv (color_white); } @@ -850,6 +919,9 @@ gl_Draw_ConsoleBackground (int lines, byte alpha) void gl_Draw_TileClear (int x, int y, int w, int h) { + if (!draw_backtile) { + return; + } glpic_t *gl; qfglColor3ubv (color_0_8); gl = (glpic_t *) draw_backtile->data; @@ -890,6 +962,22 @@ gl_Draw_Fill (int x, int y, int w, int h, int c) qfglEnable (GL_TEXTURE_2D); } +void +gl_Draw_Line (int x0, int y0, int x1, int y1, int c) +{ + qfglDisable (GL_TEXTURE_2D); + qfglColor3ubv (vid.palette + c * 3); + + qfglBegin (GL_LINES); + + qfglVertex2f (x0, y0); + qfglVertex2f (x1, y1); + + qfglEnd (); + qfglColor3ubv (color_white); + qfglEnable (GL_TEXTURE_2D); +} + void gl_Draw_FadeScreen (void) { @@ -900,9 +988,9 @@ gl_Draw_FadeScreen (void) qfglBegin (GL_QUADS); qfglVertex2f (0, 0); - qfglVertex2f (vid.conwidth, 0); - qfglVertex2f (vid.conwidth, vid.conheight); - qfglVertex2f (0, vid.conheight); + qfglVertex2f (vid.width, 0); + qfglVertex2f (vid.width, vid.height); + qfglVertex2f (0, vid.height); qfglEnd (); qfglColor3ubv (color_white); @@ -942,7 +1030,13 @@ GL_Set2D (void) void GL_Set2DScaled (void) { - set_2d (vid.conwidth, vid.conheight); + set_2d (vid.width / gl_2d_scale, vid.height / gl_2d_scale); +} + +void +gl_Draw_SetScale (int scale) +{ + gl_2d_scale = scale; } void @@ -973,12 +1067,67 @@ gl_Draw_BlendScreen (quat_t color) qfglColor4fv (color); qfglVertex2f (0, 0); - qfglVertex2f (vid.conwidth, 0); - qfglVertex2f (vid.conwidth, vid.conheight); - qfglVertex2f (0, vid.conheight); + qfglVertex2f (vid.width, 0); + qfglVertex2f (vid.width, vid.height); + qfglVertex2f (0, vid.height); qfglEnd (); qfglColor3ubv (color_white); qfglEnable (GL_TEXTURE_2D); } + +int +gl_Draw_AddFont (font_t *rfont) +{ + int fontid = gl_fonts.size; + DARRAY_OPEN_AT (&gl_fonts, fontid, 1); + glfont_t *font = &gl_fonts.a[fontid]; + + font->font = rfont; + tex_t tex = { + .width = rfont->scrap.width, + .height = rfont->scrap.height, + .format = tex_a, + .loaded = 1, + .data = rfont->scrap_bitmap, + }; + font->texid = GL_LoadTex ("", 0, &tex); + return fontid; +} + +void +gl_Draw_Glyph (int x, int y, int fontid, int glyphid, int c) +{ + if (fontid < 0 || (unsigned) fontid > gl_fonts.size) { + return; + } + + glfont_t *font = &gl_fonts.a[fontid]; + font_t *rfont = font->font; + vrect_t *rect = &rfont->glyph_rects[glyphid]; + + float w = rect->width; + float h = rect->height; + float u = rect->x; + float v = rect->y; + float s = 1.0 / rfont->scrap.width; + float t = 1.0 / rfont->scrap.height; + + qfglBindTexture (GL_TEXTURE_2D, gl_fonts.a[fontid].texid); + qfglBegin (GL_QUADS); + + byte color[4] = { VectorExpand (vid.palette + c * 3), 255 }; + qfglColor4ubv (color); + qfglTexCoord2f (u * s, v * t); + qfglVertex2f (x, y); + qfglTexCoord2f ((u + w) * s, v * t); + qfglVertex2f (x + w, y); + qfglTexCoord2f ((u + w) * s, (v + h) * t); + qfglVertex2f (x + w, y + h); + qfglTexCoord2f (u * s, (v + h) * t); + qfglVertex2f (x, y + h); + + qfglEnd (); + qfglColor4ubv (color_white); +} diff --git a/libs/video/renderer/gl/gl_dyn_lights.c b/libs/video/renderer/gl/gl_dyn_lights.c index a430f95bc..02bbcb552 100644 --- a/libs/video/renderer/gl/gl_dyn_lights.c +++ b/libs/video/renderer/gl/gl_dyn_lights.c @@ -28,9 +28,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -81,7 +78,7 @@ R_RenderDlight (dlight_t *light) bub_cos = gl_bubble_costable; rad = light->radius * 0.35; - VectorSubtract (light->origin, r_origin, v); + VectorSubtract (r_refdef.frame.position, light->origin, v); if (VectorLength (v) < rad) // view is inside the dlight return; @@ -89,7 +86,6 @@ R_RenderDlight (dlight_t *light) qfglColor4fv (light->color); - VectorSubtract (r_origin, light->origin, v); VectorNormalize (v); for (i = 0; i < 3; i++) @@ -100,8 +96,8 @@ R_RenderDlight (dlight_t *light) for (i = 16; i >= 0; i--) { for (j = 0; j < 3; j++) - v[j] = light->origin[j] + (vright[j] * (*bub_cos) + - vup[j] * (*bub_sin)) * rad; + v[j] = light->origin[j] + (r_refdef.frame.right[j] * (*bub_cos) + + r_refdef.frame.up[j] * (*bub_sin)) * rad; bub_sin += 2; bub_cos += 2; qfglVertex3fv (v); @@ -116,7 +112,7 @@ gl_R_RenderDlights (void) unsigned int i; dlight_t *l; - if (!gl_dlight_polyblend->int_val) + if (!gl_dlight_polyblend) return; qfglDepthMask (GL_FALSE); @@ -131,7 +127,7 @@ gl_R_RenderDlights (void) R_RenderDlight (l); } - if (!gl_dlight_smooth->int_val) + if (!gl_dlight_smooth) qfglShadeModel (GL_FLAT); qfglColor3ubv (color_white); qfglEnable (GL_TEXTURE_2D); diff --git a/libs/video/renderer/gl/gl_dyn_part.c b/libs/video/renderer/gl/gl_dyn_part.c index f249624b6..e6ede88fc 100644 --- a/libs/video/renderer/gl/gl_dyn_part.c +++ b/libs/video/renderer/gl/gl_dyn_part.c @@ -28,9 +28,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -48,9 +45,12 @@ #include "QF/render.h" #include "QF/sys.h" #include "QF/va.h" + +#include "QF/scene/entity.h" + #include "QF/GL/defines.h" #include "QF/GL/funcs.h" -#include "QF/GL/qf_explosions.h" +#include "QF/GL/qf_particles.h" #include "QF/GL/qf_textures.h" #include "QF/GL/qf_vid.h" @@ -58,118 +58,29 @@ #include "r_internal.h" #include "varrays.h" -static int ramp1[8] = { 0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61 }; -//static int ramp2[8] = { 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66 }; -static int ramp3[8] = { 0x6d, 0x6b, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 }; - static int partUseVA; static int pVAsize; static int *pVAindices; static varray_t2f_c4ub_v3f_t *particleVertexArray; -static mtstate_t mt; // private PRNG state - -inline static void -particle_new (const char *type, int texnum, const vec3_t org, float scale, - const vec3_t vel, float die, int color, float alpha, float ramp) -{ - particle_t *part; - -/* - // Uncomment this for particle debugging! - if (numparticles >= r_maxparticles) { - Sys_Error ("FAILED PARTICLE ALLOC!"); - return NULL; - } -*/ - - part = &particles[numparticles++]; - - VectorCopy (org, part->org); - part->color = color; - part->tex = texnum; - part->scale = scale; - part->alpha = alpha; - VectorCopy (vel, part->vel); - part->die = die; - part->ramp = ramp; - part->physics = R_ParticlePhysics (type); -} - -/* - particle_new_random - - note that org_fuzz & vel_fuzz should be ints greater than 0 if you are - going to bother using this function. -*/ -inline static void -particle_new_random (const char *type, int texnum, - const vec3_t org, int org_fuzz, - float scale, int vel_fuzz, float die, int color, - float alpha, float ramp) -{ - float o_fuzz = org_fuzz, v_fuzz = vel_fuzz; - int rnd; - vec3_t porg, pvel; - - rnd = mtwist_rand (&mt); - porg[0] = o_fuzz * ((rnd & 63) - 31.5) / 63.0 + org[0]; - porg[1] = o_fuzz * (((rnd >> 6) & 63) - 31.5) / 63.0 + org[1]; - porg[2] = o_fuzz * (((rnd >> 10) & 63) - 31.5) / 63.0 + org[2]; - rnd = mtwist_rand (&mt); - pvel[0] = v_fuzz * ((rnd & 63) - 31.5) / 63.0; - pvel[1] = v_fuzz * (((rnd >> 6) & 63) - 31.5) / 63.0; - pvel[2] = v_fuzz * (((rnd >> 10) & 63) - 31.5) / 63.0; - - particle_new (type, texnum, porg, scale, pvel, die, color, alpha, ramp); -} - -/* -inline static void -particle_new_veryrandom (ptype_t type, int texnum, const vec3_t org, - int org_fuzz, float scale, int vel_fuzz, float die, - int color, float alpha, float ramp) -{ - vec3_t porg, pvel; - - porg[0] = qfrandom (org_fuzz * 2) - org_fuzz + org[0]; - porg[1] = qfrandom (org_fuzz * 2) - org_fuzz + org[1]; - porg[2] = qfrandom (org_fuzz * 2) - org_fuzz + org[2]; - pvel[0] = qfrandom (vel_fuzz * 2) - vel_fuzz; - pvel[1] = qfrandom (vel_fuzz * 2) - vel_fuzz; - pvel[2] = qfrandom (vel_fuzz * 2) - vel_fuzz; - particle_new (type, texnum, porg, scale, pvel, die, color, alpha, ramp); -} -*/ - -void -gl_R_ClearParticles (void) -{ - numparticles = 0; -} - -void -gl_R_InitParticles (void) +static void +alloc_arrays (psystem_t *ps) { int i; - R_LoadParticles (); - - mtwist_seed (&mt, 0xdeadbeef); - - if (r_maxparticles && r_init) { + if (ps->maxparticles && r_init) { if (vaelements) { partUseVA = 0; - pVAsize = r_maxparticles * 4; - Sys_MaskPrintf (SYS_DEV, + pVAsize = ps->maxparticles * 4; + Sys_MaskPrintf (SYS_dev, "Particles: Vertex Array use disabled.\n"); } else { if (vaelements > 3) pVAsize = min ((unsigned int) (vaelements - (vaelements % 4)), - r_maxparticles * 4); + ps->maxparticles * 4); else if (vaelements >= 0) - pVAsize = r_maxparticles * 4; - Sys_MaskPrintf (SYS_DEV, + pVAsize = ps->maxparticles * 4; + Sys_MaskPrintf (SYS_dev, "Particles: %i maximum vertex elements.\n", pVAsize); } @@ -198,1304 +109,60 @@ gl_R_InitParticles (void) } } -void -gl_R_ReadPointFile_f (void) -{ - const char *name; - char *mapname; - int c, r; - vec3_t org; - QFile *f; - - mapname = strdup (r_worldentity.model->name); - if (!mapname) - Sys_Error ("Can't duplicate mapname!"); - QFS_StripExtension (mapname, mapname); - - name = va ("%s.pts", mapname); - free (mapname); - - f = QFS_FOpenFile (name); - if (!f) { - Sys_Printf ("couldn't open %s\n", name); - return; - } - - Sys_MaskPrintf (SYS_DEV, "Reading %s...\n", name); - c = 0; - for (;;) { - char buf[64]; - - Qgets (f, buf, sizeof (buf)); - r = sscanf (buf, "%f %f %f\n", &org[0], &org[1], &org[2]); - if (r != 3) - break; - c++; - - if (numparticles >= r_maxparticles) { - Sys_MaskPrintf (SYS_DEV, "Not enough free particles\n"); - break; - } else { - particle_new ("pt_static", part_tex_dot, org, 1.5, vec3_origin, - 99999, (-c) & 15, 1.0, 0.0); - } - } - Qclose (f); - Sys_MaskPrintf (SYS_DEV, "%i points read\n", c); -} - static void -R_ParticleExplosion_QF (const vec3_t org) +gl_particles_f (void *data, const cvar_t *cvar) { -// R_NewExplosion (org); - if (numparticles >= r_maxparticles) - return; - particle_new_random ("pt_smokecloud", part_tex_smoke, org, 4, 30, 8, - vr_data.realtime + 5.0, (mtwist_rand (&mt) & 7) + 8, - 0.5 + qfrandom (0.25), 0.0); -} - -static void -R_ParticleExplosion2_QF (const vec3_t org, int colorStart, int colorLength) -{ - unsigned int i, j = 512; - - if (numparticles >= r_maxparticles) - return; - else if (numparticles + j >= r_maxparticles) - j = r_maxparticles - numparticles; - - for (i = 0; i < j; i++) { - particle_new_random ("pt_blob", part_tex_dot, org, 16, 2, 256, - vr_data.realtime + 0.3, - colorStart + (i % colorLength), 1.0, 0.0); - } -} - -static void -R_BlobExplosion_QF (const vec3_t org) -{ - unsigned int i; - unsigned int j = 1024; - - if (numparticles >= r_maxparticles) - return; - else if (numparticles + j >= r_maxparticles) - j = r_maxparticles - numparticles; - - for (i = 0; i < j >> 1; i++) { - particle_new_random ("pt_blob", part_tex_dot, org, 12, 2, 256, - vr_data.realtime + 1.0 + (mtwist_rand (&mt) & 7) * 0.05, - 66 + i % 6, 1.0, 0.0); - } - for (i = 0; i < j / 2; i++) { - particle_new_random ("pt_blob2", part_tex_dot, org, 12, 2, 256, - vr_data.realtime + 1.0 + (mtwist_rand (&mt) & 7) * 0.05, - 150 + i % 6, 1.0, 0.0); - } -} - -static inline void -R_RunSparkEffect_QF (const vec3_t org, int count, int ofuzz) -{ - if (numparticles >= r_maxparticles) - return; - particle_new ("pt_smokecloud", part_tex_smoke, org, ofuzz * 0.08, - vec3_origin, vr_data.realtime + 9, 12 + (mtwist_rand (&mt) & 3), - 0.25 + qfrandom (0.125), 0.0); - - if (numparticles + count >= r_maxparticles) - count = r_maxparticles - numparticles; - if (count) { - int orgfuzz = ofuzz * 3 / 4; - if (orgfuzz < 1) - orgfuzz = 1; - - while (count--) { - int color = mtwist_rand (&mt) & 7; - - particle_new_random ("pt_fallfadespark", part_tex_dot, org, orgfuzz, - 0.7, 96, vr_data.realtime + 5.0, ramp1[color], - 1.0, color); - } - } -} - -static inline void -R_BloodPuff_QF (const vec3_t org, int count) -{ - if (numparticles >= r_maxparticles) - return; - - particle_new ("pt_bloodcloud", part_tex_smoke, org, count / 5, vec3_origin, - vr_data.realtime + 99.0, 70 + (mtwist_rand (&mt) & 3), 0.5, 0.0); -} - -static void -R_BloodPuffEffect_QF (const vec3_t org, int count) -{ - R_BloodPuff_QF (org, count); -} - -static void -R_GunshotEffect_QF (const vec3_t org, int count) -{ - int scale = 16; - - scale += count / 15; - R_RunSparkEffect_QF (org, count >> 1, scale); -} - -static void -R_LightningBloodEffect_QF (const vec3_t org) -{ - int count = 7; - - R_BloodPuff_QF (org, 50); - - if (numparticles >= r_maxparticles) - return; - particle_new ("pt_smokecloud", part_tex_smoke, org, 3.0, vec3_origin, - vr_data.realtime + 9.0, 12 + (mtwist_rand (&mt) & 3), - 0.25 + qfrandom (0.125), 0.0); - - if (numparticles + count >= r_maxparticles) - count = r_maxparticles - numparticles; - while (count--) - particle_new_random ("pt_fallfade", part_tex_spark, org, 12, 2.0, 128, - vr_data.realtime + 5.0, 244 + (count % 3), 1.0, - 0.0); -} - -static void -R_RunParticleEffect_QF (const vec3_t org, const vec3_t dir, int color, - int count) -{ - float scale; - int i; - vec3_t porg; - - if (numparticles >= r_maxparticles) - return; - - scale = pow (count, 0.23); // calculate scale before clipping to part. max - - if (numparticles + count >= r_maxparticles) - count = r_maxparticles - numparticles; - - for (i = 0; i < count; i++) { - int rnd = mtwist_rand (&mt); - - porg[0] = org[0] + scale * (((rnd >> 3) & 15) - 7.5); - porg[1] = org[1] + scale * (((rnd >> 7) & 15) - 7.5); - porg[2] = org[2] + scale * (((rnd >> 11) & 15) - 7.5); - // Note that ParseParticleEffect handles (dir * 15) - particle_new ("pt_grav", part_tex_dot, porg, 1.5, dir, - vr_data.realtime + 0.1 * (i % 5), - (color & ~7) + (rnd & 7), 1.0, 0.0); - } -} - -static void -R_SpikeEffect_QF (const vec3_t org) -{ - R_RunSparkEffect_QF (org, 5, 8); -} - -static void -R_SuperSpikeEffect_QF (const vec3_t org) -{ - R_RunSparkEffect_QF (org, 10, 8); -} - -static void -R_KnightSpikeEffect_QF (const vec3_t org) -{ - unsigned int count = 10; - - if (numparticles >= r_maxparticles) - return; - particle_new ("pt_smokecloud", part_tex_smoke, org, 1.0, vec3_origin, - vr_data.realtime + 9.0, 234, 0.25 + qfrandom (0.125), 0.0); - - if (numparticles + count >= r_maxparticles) - count = r_maxparticles - numparticles; - while (count--) - particle_new_random ("pt_fallfade", part_tex_dot, org, 6, 0.7, 96, - vr_data.realtime + 5.0, 234, 1.0, 0.0); -} - -static void -R_WizSpikeEffect_QF (const vec3_t org) -{ - unsigned int count = 15; - - if (numparticles >= r_maxparticles) - return; - particle_new ("pt_smokecloud", part_tex_smoke, org, 2.0, vec3_origin, - vr_data.realtime + 9.0, 63, 0.25 + qfrandom (0.125), 0.0); - - if (numparticles + count >= r_maxparticles) - count = r_maxparticles - numparticles; - while (count--) - particle_new_random ("pt_fallfade", part_tex_dot, org, 12, 0.7, 96, - vr_data.realtime + 5.0, 63, 1.0, 0.0); -} - -static void -R_LavaSplash_QF (const vec3_t org) -{ - float vel; - int rnd, i, j; - int k = 256; - vec3_t dir, porg, pvel; - - if (numparticles + k >= r_maxparticles) { - return; - } // else if (numparticles + k >= r_maxparticles) { -// k = r_maxparticles - numparticles; -// } - - dir[2] = 256; - for (i = -128; i < 128; i += 16) { - for (j = -128; j < 128; j += 16) { - rnd = mtwist_rand (&mt); - dir[0] = j + (rnd & 7); - dir[1] = i + ((rnd >> 6) & 7); - - porg[0] = org[0] + dir[0]; - porg[1] = org[1] + dir[1]; - porg[2] = org[2] + ((rnd >> 9) & 63); - - VectorNormalize (dir); - rnd = mtwist_rand (&mt); - vel = 50.0 + 0.5 * (float) (rnd & 127); - VectorScale (dir, vel, pvel); - particle_new ("pt_grav", part_tex_dot, porg, 3, pvel, - vr_data.realtime + 2.0 + ((rnd >> 7) & 31) * 0.02, - 224 + ((rnd >> 12) & 7), 0.75, 0.0); - } - } -} - -static void -R_TeleportSplash_QF (const vec3_t org) -{ - float vel; - int rnd, i, j, k; - int l = 896; - vec3_t dir, pdir, porg, pvel; - - if (numparticles + l >= r_maxparticles) { - return; - } // else if (numparticles + l >= r_maxparticles) { -// l = r_maxparticles - numparticles; -// } - - for (k = -24; k < 32; k += 4) { - dir[2] = k * 8; - for (i = -16; i < 16; i += 4) { - dir[1] = i * 8; - for (j = -16; j < 16; j += 4) { - dir[0] = j * 8; - - VectorCopy (dir, pdir); - VectorNormalize (pdir); - - rnd = mtwist_rand (&mt); - porg[0] = org[0] + i + (rnd & 3); - porg[1] = org[1] + j + ((rnd >> 2) & 3); - porg[2] = org[2] + k + ((rnd >> 4) & 3); - - vel = 50 + ((rnd >> 6) & 63); - VectorScale (pdir, vel, pvel); - particle_new ("pt_grav", part_tex_spark, porg, 0.6, pvel, - (vr_data.realtime + 0.2 + (mtwist_rand (&mt) & 15) * 0.01), - (7 + ((rnd >> 12) & 7)), 1.0, 0.0); - } - } - } -} - -static void -R_RocketTrail_QF (entity_t *ent) -{ - float dist, maxlen, origlen, percent, pscale, pscalenext; - float len = 0.0; - vec3_t old_origin, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - origlen = vr_data.frametime / maxlen; - pscale = 1.5 + qfrandom (1.5); - - while (len < maxlen) { - pscalenext = 1.5 + qfrandom (1.5); - dist = (pscale + pscalenext) * 3.0; - percent = len * origlen; - - particle_new ("pt_smoke", part_tex_smoke, old_origin, - pscale + percent * 4.0, vec3_origin, - vr_data.realtime + 2.0 - percent * 2.0, - 12 + (mtwist_rand (&mt) & 3), - 0.5 + qfrandom (0.125) - percent * 0.40, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorMultAdd (old_origin, len, vec, old_origin); - pscale = pscalenext; - } -} - -static void -R_GrenadeTrail_QF (entity_t *ent) -{ - float dist, maxlen, origlen, percent, pscale, pscalenext; - float len = 0.0; - vec3_t old_origin, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - origlen = vr_data.frametime / maxlen; - pscale = 6.0 + qfrandom (7.0); - - while (len < maxlen) { - pscalenext = 6.0 + qfrandom (7.0); - dist = (pscale + pscalenext) * 2.0; - percent = len * origlen; - - particle_new ("pt_smoke", part_tex_smoke, old_origin, - pscale + percent * 4.0, vec3_origin, - vr_data.realtime + 2.0 - percent * 2.0, - 1 + (mtwist_rand (&mt) & 3), - 0.625 + qfrandom (0.125) - percent * 0.40, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorMultAdd (old_origin, len, vec, old_origin); - pscale = pscalenext; - } -} - -static void -R_BloodTrail_QF (entity_t *ent) -{ - float dist, maxlen, origlen, percent, pscale, pscalenext; - float len = 0.0; - int j; - vec3_t old_origin, porg, pvel, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - origlen = vr_data.frametime / maxlen; - pscale = 5.0 + qfrandom (10.0); - - while (len < maxlen) { - pscalenext = 5.0 + qfrandom (10.0); - dist = (pscale + pscalenext) * 1.5; - - for (j = 0; j < 3; j++) { - pvel[j] = qfrandom (24.0) - 12.0; - porg[j] = old_origin[j] + qfrandom (3.0) - 1.5; - } - - percent = len * origlen; - pvel[2] -= percent * 40.0; - - particle_new ("pt_grav", part_tex_smoke, porg, pscale, pvel, - vr_data.realtime + 2.0 - percent * 2.0, - 68 + (mtwist_rand (&mt) & 3), 1.0, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorMultAdd (old_origin, len, vec, old_origin); - pscale = pscalenext; - } -} - -static void -R_SlightBloodTrail_QF (entity_t *ent) -{ - float dist, maxlen, origlen, percent, pscale, pscalenext; - float len = 0.0; - int j; - vec3_t old_origin, porg, pvel, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - origlen = vr_data.frametime / maxlen; - pscale = 1.5 + qfrandom (7.5); - - while (len < maxlen) { - pscalenext = 1.5 + qfrandom (7.5); - dist = (pscale + pscalenext) * 1.5; - - for (j = 0; j < 3; j++) { - pvel[j] = (qfrandom (12.0) - 6.0); - porg[j] = old_origin[j] + qfrandom (3.0) - 1.5; - } - - percent = len * origlen; - pvel[2] -= percent * 40; - - particle_new ("pt_grav", part_tex_smoke, porg, pscale, pvel, - vr_data.realtime + 1.5 - percent * 1.5, - 68 + (mtwist_rand (&mt) & 3), 0.75, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorMultAdd (old_origin, len, vec, old_origin); - pscale = pscalenext; - } -} - -static void -R_WizTrail_QF (entity_t *ent) -{ - float maxlen, origlen, percent; - float dist = 3.0, len = 0.0; - static int tracercount; - vec3_t old_origin, pvel, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - origlen = vr_data.frametime / maxlen; - VectorScale (vec, maxlen - dist, subtract); - - while (len < maxlen) { - percent = len * origlen; - - tracercount++; - if (tracercount & 1) { - pvel[0] = 30.0 * vec[1]; - pvel[1] = 30.0 * -vec[0]; - } else { - pvel[0] = 30.0 * -vec[1]; - pvel[1] = 30.0 * vec[0]; - } - pvel[2] = 0.0; - - particle_new ("pt_flame", part_tex_smoke, old_origin, - 2.0 + qfrandom (1.0) - percent * 2.0, pvel, - vr_data.realtime + 0.5 - percent * 0.5, - 52 + (mtwist_rand (&mt) & 4), 1.0 - percent * 0.125, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - -static void -R_FlameTrail_QF (entity_t *ent) -{ - float maxlen, origlen, percent; - float dist = 3.0, len = 0.0; - static int tracercount; - vec3_t old_origin, pvel, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - origlen = vr_data.frametime / maxlen; - VectorScale (vec, maxlen - dist, subtract); - - while (len < maxlen) { - percent = len * origlen; - - tracercount++; - if (tracercount & 1) { - pvel[0] = 30.0 * vec[1]; - pvel[1] = 30.0 * -vec[0]; - } else { - pvel[0] = 30.0 * -vec[1]; - pvel[1] = 30.0 * vec[0]; - } - pvel[2] = 0.0; - - particle_new ("pt_flame", part_tex_smoke, old_origin, - 2.0 + qfrandom (1.0) - percent * 2.0, pvel, - vr_data.realtime + 0.5 - percent * 0.5, 234, - 1.0 - percent * 0.125, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - -static void -R_VoorTrail_QF (entity_t *ent) -{ - float maxlen, origlen, percent; - float dist = 3.0, len = 0.0; - int j; - vec3_t subtract, old_origin, porg, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - origlen = vr_data.frametime / maxlen; - VectorScale (vec, maxlen - dist, subtract); - - while (len < maxlen) { - percent = len * origlen; - - for (j = 0; j < 3; j++) - porg[j] = old_origin[j] + qfrandom (16.0) - 8.0; - - particle_new ("pt_static", part_tex_dot, porg, 1.0 + qfrandom (1.0), - vec3_origin, vr_data.realtime + 0.3 - percent * 0.3, - 9 * 16 + 8 + (mtwist_rand (&mt) & 3), 1.0, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - -static void -R_GlowTrail_QF (entity_t *ent, int glow_color) -{ - float maxlen, origlen, percent; - float dist = 3.0, len = 0.0; - int rnd; - vec3_t old_origin, org, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - origlen = vr_data.frametime / maxlen; - VectorScale (vec, (maxlen - dist), subtract); - - while (len < maxlen) { - percent = len * origlen; - - rnd = mtwist_rand (&mt); - org[0] = old_origin[0] + ((rnd >> 12) & 7) * (5.0/7.0) - 2.5; - org[1] = old_origin[1] + ((rnd >> 9) & 7) * (5.0/7.0) - 2.5; - org[2] = old_origin[2] + ((rnd >> 6) & 7) * (5.0/7.0) - 2.5; - - particle_new ("pt_smoke", part_tex_dot, org, 1.0, vec3_origin, - vr_data.realtime + 2.0 - percent * 0.2, - glow_color, 1.0, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - -static void -R_ParticleExplosion_EE (const vec3_t org) -{ -/* - R_NewExplosion (org); -*/ - if (numparticles >= r_maxparticles) - return; - particle_new_random ("pt_smokecloud", part_tex_smoke, org, 4, 30, 8, - vr_data.realtime + 5.0, mtwist_rand (&mt) & 255, - 0.5 + qfrandom (0.25), 0.0); -} - -static void -R_TeleportSplash_EE (const vec3_t org) -{ - float vel; - int rnd, i, j, k; - int l = 896; - vec3_t dir, porg, pvel; - - if (numparticles + l >= r_maxparticles) { - return; - } // else if (numparticles + l >= r_maxparticles) { -// l = r_maxparticles - numparticles; -// } - - for (k = -24; k < 32; k += 4) { - dir[2] = k * 8; - for (i = -16; i < 16; i += 4) { - dir[1] = i * 8; - for (j = -16; j < 16; j += 4) { - dir[0] = j * 8; - - rnd = mtwist_rand (&mt); - porg[0] = org[0] + i + (rnd & 3); - porg[1] = org[1] + j + ((rnd >> 2) & 3); - porg[2] = org[2] + k + ((rnd >> 4) & 3); - - VectorNormalize (dir); - vel = 50 + ((rnd >> 6) & 63); - VectorScale (dir, vel, pvel); - particle_new ("pt_grav", part_tex_spark, porg, 0.6, pvel, - (vr_data.realtime + 0.2 + (mtwist_rand (&mt) & 15) * 0.01), - qfrandom (1.0), 1.0, 0.0); - } - } - } -} - -static void -R_RocketTrail_EE (entity_t *ent) -{ - float dist, maxlen, origlen, percent, pscale, pscalenext; - float len = 0.0; - vec3_t old_origin, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - origlen = vr_data.frametime / maxlen; - pscale = 1.5 + qfrandom (1.5); - - while (len < maxlen) { - pscalenext = 1.5 + qfrandom (1.5); - dist = (pscale + pscalenext) * 3.0; - percent = len * origlen; - - particle_new ("pt_smoke", part_tex_smoke, old_origin, - pscale + percent * 4.0, vec3_origin, - vr_data.realtime + 2.0 - percent * 2.0, - mtwist_rand (&mt) & 255, - 0.5 + qfrandom (0.125) - percent * 0.40, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorScale (vec, len, subtract); - VectorAdd (old_origin, subtract, old_origin); - pscale = pscalenext; - } -} - -static void -R_GrenadeTrail_EE (entity_t *ent) -{ - float dist, maxlen, origlen, percent, pscale, pscalenext; - float len = 0.0; - vec3_t old_origin, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, ent->old_origin, vec); - maxlen = VectorNormalize (vec); - origlen = vr_data.frametime / maxlen; - pscale = 6.0 + qfrandom (7.0); - - while (len < maxlen) { - pscalenext = 6.0 + qfrandom (7.0); - dist = (pscale + pscalenext) * 2.0; - percent = len * origlen; - - particle_new ("pt_smoke", part_tex_smoke, old_origin, - pscale + percent * 4.0, vec3_origin, - vr_data.realtime + 2.0 - percent * 2.0, - mtwist_rand (&mt) & 255, - 0.625 + qfrandom (0.125) - percent * 0.40, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorScale (vec, len, subtract); - VectorAdd (old_origin, subtract, old_origin); - pscale = pscalenext; - } -} - -static void -R_ParticleExplosion_ID (const vec3_t org) -{ - unsigned int i; - unsigned int j = 1024; - - if (numparticles >= r_maxparticles) - return; - else if (numparticles + j >= r_maxparticles) - j = r_maxparticles - numparticles; - - for (i = 0; i < j >> 1; i++) { - particle_new_random ("pt_explode", part_tex_dot, org, 16, 1.0, 256, - vr_data.realtime + 5.0, ramp1[0], 1.0, i & 3); - } - for (i = 0; i < j / 2; i++) { - particle_new_random ("pt_explode2", part_tex_dot, org, 16, 1.0, 256, - vr_data.realtime + 5.0, ramp1[0], 1.0, i & 3); - } -} - -static void -R_BlobExplosion_ID (const vec3_t org) -{ - unsigned int i; - unsigned int j = 1024; - - if (numparticles >= r_maxparticles) - return; - else if (numparticles + j >= r_maxparticles) - j = r_maxparticles - numparticles; - - for (i = 0; i < j >> 1; i++) { - particle_new_random ("pt_blob", part_tex_dot, org, 12, 1.0, 256, - vr_data.realtime + 1.0 + (mtwist_rand (&mt) & 8) * 0.05, - 66 + i % 6, 1.0, 0.0); - } - for (i = 0; i < j / 2; i++) { - particle_new_random ("pt_blob2", part_tex_dot, org, 12, 1.0, 256, - vr_data.realtime + 1.0 + (mtwist_rand (&mt) & 8) * 0.05, - 150 + i % 6, 1.0, 0.0); - } -} - -static inline void // FIXME: inline? -R_RunParticleEffect_ID (const vec3_t org, const vec3_t dir, int color, - int count) -{ - float scale; - int i; - vec3_t porg; - - if (numparticles >= r_maxparticles) - return; - - if (count > 130) // calculate scale before clipping to particle max - scale = 3.0; - else if (count > 20) - scale = 2.0; - else - scale = 1.0; - - if (numparticles + count >= r_maxparticles) - count = r_maxparticles - numparticles; - - for (i = 0; i < count; i++) { - int rnd = mtwist_rand (&mt); - - porg[0] = org[0] + scale * (((rnd >> 3) & 15) - 8); - porg[1] = org[1] + scale * (((rnd >> 7) & 15) - 8); - porg[2] = org[2] + scale * (((rnd >> 11) & 15) - 8); - - // Note that ParseParticleEffect handles (dir * 15) - particle_new ("pt_grav", part_tex_dot, porg, 1.0, dir, - vr_data.realtime + 0.1 * (i % 5), - (color & ~7) + (rnd & 7), 1.0, 0.0); - } -} - -static void -R_BloodPuffEffect_ID (const vec3_t org, int count) -{ - R_RunParticleEffect_ID (org, vec3_origin, 73, count); -} - -static void -R_GunshotEffect_ID (const vec3_t org, int count) -{ - R_RunParticleEffect_ID (org, vec3_origin, 0, count); -} - -static void -R_LightningBloodEffect_ID (const vec3_t org) -{ - R_RunParticleEffect_ID (org, vec3_origin, 225, 50); -} - -static void -R_SpikeEffect_ID (const vec3_t org) -{ - R_RunParticleEffect_ID (org, vec3_origin, 0, 10); -} - -static void -R_SuperSpikeEffect_ID (const vec3_t org) -{ - R_RunParticleEffect_ID (org, vec3_origin, 0, 20); -} - -static void -R_KnightSpikeEffect_ID (const vec3_t org) -{ - R_RunParticleEffect_ID (org, vec3_origin, 226, 20); -} - -static void -R_WizSpikeEffect_ID (const vec3_t org) -{ - R_RunParticleEffect_ID (org, vec3_origin, 20, 30); -} - -static void -R_LavaSplash_ID (const vec3_t org) -{ - float vel; - int rnd, i, j; - int k = 256; - vec3_t dir, porg, pvel; - - if (numparticles + k >= r_maxparticles) { - return; - } // else if (numparticles + k >= r_maxparticles) { -// k = r_maxparticles - numparticles; -// } - - dir[2] = 256; - for (i = -128; i < 128; i += 16) { - for (j = -128; j < 128; j += 16) { - rnd = mtwist_rand (&mt); - dir[0] = j + (rnd & 7); - dir[1] = i + ((rnd >> 6) & 7); - - porg[0] = org[0] + dir[0]; - porg[1] = org[1] + dir[1]; - porg[2] = org[2] + ((rnd >> 9) & 63); - - VectorNormalize (dir); - rnd = mtwist_rand (&mt); - vel = 50 + (rnd & 63); - VectorScale (dir, vel, pvel); - particle_new ("pt_grav", part_tex_dot, porg, 1.0, pvel, - vr_data.realtime + 2 + ((rnd >> 7) & 31) * 0.02, - 224 + ((rnd >> 12) & 7), 1.0, 0.0); - } - } -} - -static void -R_TeleportSplash_ID (const vec3_t org) -{ - float vel; - int rnd, i, j, k; - int l = 896; - vec3_t dir, pdir, porg, pvel; - - if (numparticles + l >= r_maxparticles) { - return; - } // else if (numparticles + l >= r_maxparticles) { -// l = r_maxparticles - numparticles; -// } - - for (k = -24; k < 32; k += 4) { - dir[2] = k * 8; - for (i = -16; i < 16; i += 4) { - dir[1] = i * 8; - for (j = -16; j < 16; j += 4) { - dir[0] = j * 8; - - VectorCopy (dir, pdir); - VectorNormalize (pdir); - - rnd = mtwist_rand (&mt); - porg[0] = org[0] + i + (rnd & 3); - porg[1] = org[1] + j + ((rnd >> 2) & 3); - porg[2] = org[2] + k + ((rnd >> 4) & 3); - - vel = 50 + ((rnd >> 6) & 63); - VectorScale (pdir, vel, pvel); - particle_new ("pt_grav", part_tex_dot, porg, 1.0, pvel, - (vr_data.realtime + 0.2 + (mtwist_rand (&mt) & 7) * 0.02), - (7 + ((rnd >> 12) & 7)), 1.0, 0.0); - } - } - } -} - -static void -R_DarkFieldParticles_ID (entity_t *ent) -{ - int i, j, k, l = 64; - unsigned int rnd; - float vel; - vec3_t dir, org, porg, pvel; - - if (numparticles + l >= r_maxparticles) { - return; - } // else if (numparticles + l >= r_maxparticles) { -// l = r_maxparticles - numparticles; -// } - - VectorCopy (ent->origin, org); - - for (i = -16; i < 16; i += 8) { - dir [1] = i * 8; - for (j = -16; j < 16; j += 8) { - dir [0] = j * 8; - for (k = 0; k < 32; k += 8) { - dir [2] = k * 8; - rnd = mtwist_rand (&mt); - - porg[0] = org[0] + i + ((rnd >> 3) & 3); - porg[1] = org[1] + j + ((rnd >> 5) & 3); - porg[2] = org[2] + k + ((rnd >> 7) & 3); - - VectorNormalize (dir); - vel = 50 + ((rnd >> 9) & 63); - VectorScale (dir, vel, pvel); - particle_new ("pt_slowgrav", part_tex_dot, porg, 1.5, pvel, - (vr_data.realtime + 0.2 + (rnd & 7) * 0.02), - (150 + mtwist_rand (&mt) % 6), 1.0, 0.0); - } - } - } -} - -static vec3_t avelocities[NUMVERTEXNORMALS]; - -static void -R_EntityParticles_ID (entity_t *ent) -{ - int i, j = NUMVERTEXNORMALS; - float angle, sp, sy, cp, cy; // cr, sr - float beamlength = 16.0, dist = 64.0; - vec3_t forward, porg; - - if (numparticles + j >= r_maxparticles) { - return; - } else if (numparticles + j >= r_maxparticles) { - j = r_maxparticles - numparticles; - } - - if (!avelocities[0][0]) { - for (i = 0; i < NUMVERTEXNORMALS; i++) { - int k; - for (k = 0; k < 3; k++) { - avelocities[i][k] = (mtwist_rand (&mt) & 255) * 0.01; - } - } - } - - for (i = 0; i < j; i++) { - angle = vr_data.realtime * avelocities[i][0]; - cy = cos (angle); - sy = sin (angle); - angle = vr_data.realtime * avelocities[i][1]; - cp = cos (angle); - sp = sin (angle); -// Next 3 lines results aren't currently used, may be in future. --Despair -// angle = vr_data.realtime * avelocities[i][2]; -// sr = sin (angle); -// cr = cos (angle); - - forward[0] = cp * cy; - forward[1] = cp * sy; - forward[2] = -sp; - - porg[0] = ent->origin[0] + r_avertexnormals[i][0] * dist + - forward[0] * beamlength; - porg[1] = ent->origin[1] + r_avertexnormals[i][1] * dist + - forward[1] * beamlength; - porg[2] = ent->origin[2] + r_avertexnormals[i][2] * dist + - forward[2] * beamlength; - particle_new ("pt_explode", part_tex_dot, porg, 1.0, vec3_origin, - vr_data.realtime + 0.01, 0x6f, 1.0, 0); - } -} - -static void -R_RocketTrail_ID (entity_t *ent) -{ - float maxlen; - float dist = 3.0, len = 0.0; - int ramp, rnd; - vec3_t old_origin, org, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, ent->old_origin, vec); - maxlen = VectorNormalize (vec); - VectorScale (vec, (maxlen - dist), subtract); - - while (len < maxlen) { - rnd = mtwist_rand (&mt); - org[0] = old_origin[0] + ((rnd >> 12) & 7) * (5.0/7.0) - 2.5; - org[1] = old_origin[1] + ((rnd >> 9) & 7) * (5.0/7.0) - 2.5; - org[2] = old_origin[2] + ((rnd >> 6) & 7) * (5.0/7.0) - 2.5; - ramp = rnd & 3; - - particle_new ("pt_fire", part_tex_dot, org, 1.0, vec3_origin, - vr_data.realtime + 2.0, ramp3[ramp], 1.0, ramp); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - -static void -R_GrenadeTrail_ID (entity_t *ent) -{ - float maxlen; - float dist = 3.0, len = 0.0; - unsigned int ramp, rnd; - vec3_t old_origin, org, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, ent->old_origin, vec); - maxlen = VectorNormalize (vec); - VectorScale (vec, maxlen - dist, subtract); - - while (len < maxlen) { - rnd = mtwist_rand (&mt); - org[0] = old_origin[0] + ((rnd >> 12) & 7) * (5.0/7.0) - 2.5; - org[1] = old_origin[1] + ((rnd >> 9) & 7) * (5.0/7.0) - 2.5; - org[2] = old_origin[2] + ((rnd >> 6) & 7) * (5.0/7.0) - 2.5; - ramp = (rnd & 3) + 2; - - particle_new ("pt_fire", part_tex_dot, org, 1.0, vec3_origin, - vr_data.realtime + 2.0, ramp3[ramp], 1.0, ramp); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - -static void -R_BloodTrail_ID (entity_t *ent) -{ - float maxlen; - float dist = 3.0, len = 0.0; - unsigned int rnd; - vec3_t old_origin, subtract, vec, porg; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - VectorScale (vec, maxlen - dist, subtract); - - while (len < maxlen) { - rnd = mtwist_rand (&mt); - porg[0] = old_origin[0] + ((rnd >> 12) & 7) * (5.0/7.0) - 2.5; - porg[1] = old_origin[1] + ((rnd >> 9) & 7) * (5.0/7.0) - 2.5; - porg[2] = old_origin[2] + ((rnd >> 6) & 7) * (5.0/7.0) - 2.5; - - particle_new ("pt_grav", part_tex_dot, porg, 1.0, vec3_origin, - vr_data.realtime + 2.0, 67 + (rnd & 3), 1.0, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - -static void -R_SlightBloodTrail_ID (entity_t *ent) -{ - float maxlen; - float dist = 6.0, len = 0.0; - unsigned int rnd; - vec3_t old_origin, porg, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - VectorScale (vec, maxlen - dist, subtract); - - while (len < maxlen) { - rnd = mtwist_rand (&mt); - porg[0] = old_origin[0] + ((rnd >> 12) & 7) * (5.0/7.0) - 2.5; - porg[1] = old_origin[1] + ((rnd >> 9) & 7) * (5.0/7.0) - 2.5; - porg[2] = old_origin[2] + ((rnd >> 6) & 7) * (5.0/7.0) - 2.5; - - particle_new ("pt_grav", part_tex_dot, porg, 1.0, vec3_origin, - vr_data.realtime + 1.5, 67 + (rnd & 3), 1.0, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - -static void -R_WizTrail_ID (entity_t *ent) -{ - float maxlen; - float dist = 3.0, len = 0.0; - static int tracercount; - vec3_t old_origin, pvel, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - VectorScale (vec, maxlen - dist, subtract); - - while (len < maxlen) { - tracercount++; - if (tracercount & 1) { - pvel[0] = 30.0 * vec[1]; - pvel[1] = 30.0 * -vec[0]; - } else { - pvel[0] = 30.0 * -vec[1]; - pvel[1] = 30.0 * vec[0]; - } - pvel[2] = 0.0; - - particle_new ("pt_static", part_tex_dot, old_origin, 1.0, pvel, - vr_data.realtime + 0.5, 52 + ((tracercount & 4) << 1), - 1.0, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - -static void -R_FlameTrail_ID (entity_t *ent) -{ - float maxlen; - float dist = 3.0, len = 0.0; - static int tracercount; - vec3_t old_origin, pvel, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - VectorScale (vec, maxlen - dist, subtract); - - while (len < maxlen) { - tracercount++; - if (tracercount & 1) { - pvel[0] = 30.0 * vec[1]; - pvel[1] = 30.0 * -vec[0]; - } else { - pvel[0] = 30.0 * -vec[1]; - pvel[1] = 30.0 * vec[0]; - } - pvel[2] = 0.0; - - particle_new ("pt_static", part_tex_dot, old_origin, 1.0, pvel, - vr_data.realtime + 0.5, 230 + ((tracercount & 4) << 1), - 1.0, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - -static void -R_VoorTrail_ID (entity_t *ent) -{ - float maxlen; - float dist = 3.0, len = 0.0; - unsigned int rnd; - vec3_t old_origin, porg, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - VectorScale (vec, maxlen - dist, subtract); - - while (len < maxlen) { - rnd = mtwist_rand (&mt); - porg[0] = old_origin[0] + ((rnd >> 3) & 15) - 7.5; - porg[1] = old_origin[1] + ((rnd >> 7) & 15) - 7.5; - porg[2] = old_origin[2] + ((rnd >> 11) & 15) - 7.5; - - particle_new ("pt_static", part_tex_dot, porg, 1.0, vec3_origin, - vr_data.realtime + 0.3, 9 * 16 + 8 + (rnd & 3), - 1.0, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } + alloc_arrays (&r_psystem);//FIXME } void -gl_R_DrawParticles (void) +gl_R_InitParticles (void) +{ + Cvar_AddListener (Cvar_FindVar ("r_particles"), gl_particles_f, 0); + Cvar_AddListener (Cvar_FindVar ("r_particles_max"), gl_particles_f, 0); + alloc_arrays (&r_psystem); +} + +void +gl_R_DrawParticles (psystem_t *psystem) { unsigned char *at; - int activeparticles, maxparticle, j, vacount; - unsigned int k; + int vacount; float minparticledist, scale; - particle_t *part; vec3_t up_scale, right_scale, up_right_scale, down_right_scale; varray_t2f_c4ub_v3f_t *VA; - if (!r_particles->int_val) + if (!r_psystem.numparticles) { return; + } qfglBindTexture (GL_TEXTURE_2D, gl_part_tex); // LordHavoc: particles should not affect zbuffer qfglDepthMask (GL_FALSE); qfglInterleavedArrays (GL_T2F_C4UB_V3F, 0, particleVertexArray); - minparticledist = DotProduct (r_refdef.vieworg, vpn) + - r_particles_nearclip->value; + minparticledist = DotProduct (r_refdef.frame.position, + r_refdef.frame.forward) + + r_particles_nearclip; - activeparticles = 0; vacount = 0; VA = particleVertexArray; - maxparticle = -1; - j = 0; - for (k = 0, part = particles; k < numparticles; k++, part++) { + for (unsigned i = 0; i < r_psystem.numparticles; i++) { + particle_t *p = &r_psystem.particles[i]; // Don't render particles too close to us. // Note, we must still do physics and such on them. - if (!(DotProduct (part->org, vpn) < minparticledist)) { - at = (byte *) &d_8to24table[(byte) part->color]; + if (!(DotProduct (p->pos, r_refdef.frame.forward) < minparticledist)) { + at = (byte *) &d_8to24table[(byte) p->icolor]; VA[0].color[0] = at[0]; VA[0].color[1] = at[1]; VA[0].color[2] = at[2]; - VA[0].color[3] = part->alpha * 255; + VA[0].color[3] = p->alpha * 255; memcpy (VA[1].color, VA[0].color, sizeof (VA[0].color)); memcpy (VA[2].color, VA[0].color, sizeof (VA[0].color)); memcpy (VA[3].color, VA[0].color, sizeof (VA[0].color)); - switch (part->tex) { + switch (p->tex) { case part_tex_dot: VA[0].texcoord[0] = 0.0; VA[0].texcoord[1] = 0.0; @@ -1528,18 +195,18 @@ gl_R_DrawParticles (void) break; } - scale = part->scale; + scale = p->scale; - VectorScale (vup, scale, up_scale); - VectorScale (vright, scale, right_scale); + VectorScale (r_refdef.frame.up, scale, up_scale); + VectorScale (r_refdef.frame.right, scale, right_scale); VectorAdd (right_scale, up_scale, up_right_scale); VectorSubtract (right_scale, up_scale, down_right_scale); - VectorAdd (part->org, down_right_scale, VA[0].vertex); - VectorSubtract (part->org, up_right_scale, VA[1].vertex); - VectorSubtract (part->org, down_right_scale, VA[2].vertex); - VectorAdd (part->org, up_right_scale, VA[3].vertex); + VectorAdd (p->pos, down_right_scale, VA[0].vertex); + VectorSubtract (p->pos, up_right_scale, VA[1].vertex); + VectorSubtract (p->pos, down_right_scale, VA[2].vertex); + VectorAdd (p->pos, up_right_scale, VA[3].vertex); VA += 4; vacount += 4; @@ -1551,18 +218,8 @@ gl_R_DrawParticles (void) VA = particleVertexArray; } } - - R_RunParticlePhysics (part); - - // LordHavoc: immediate removal of unnecessary particles (must be done - // to ensure compactor below operates properly in all cases) - if (part->die < vr_data.realtime) { - freeparticles[j++] = part; - } else { - maxparticle = k; - activeparticles++; - } } + if (vacount) { if (partUseVA) { qfglDrawElements (GL_QUADS, vacount, GL_UNSIGNED_INT, pVAindices); @@ -1580,221 +237,12 @@ gl_R_DrawParticles (void) } } - k = 0; - while (maxparticle >= activeparticles) { - *freeparticles[k++] = particles[maxparticle--]; - while (maxparticle >= activeparticles && - particles[maxparticle].die <= vr_data.realtime) - maxparticle--; - } - numparticles = activeparticles; - qfglColor3ubv (color_white); qfglDepthMask (GL_TRUE); } -static vid_particle_funcs_t particles_QF = { - R_RocketTrail_QF, - R_GrenadeTrail_QF, - R_BloodTrail_QF, - R_SlightBloodTrail_QF, - R_WizTrail_QF, - R_FlameTrail_QF, - R_VoorTrail_QF, - R_GlowTrail_QF, - R_RunParticleEffect_QF, - R_BloodPuffEffect_QF, - R_GunshotEffect_QF, - R_LightningBloodEffect_QF, - R_SpikeEffect_QF, - R_KnightSpikeEffect_QF, - R_SuperSpikeEffect_QF, - R_WizSpikeEffect_QF, - R_BlobExplosion_QF, - R_ParticleExplosion_QF, - R_ParticleExplosion2_QF, - R_LavaSplash_QF, - R_TeleportSplash_QF, - R_DarkFieldParticles_ID, - R_EntityParticles_ID, - R_Particle_New, - R_Particle_NewRandom, -}; - -static vid_particle_funcs_t particles_ID = { - R_RocketTrail_ID, - R_GrenadeTrail_ID, - R_BloodTrail_ID, - R_SlightBloodTrail_ID, - R_WizTrail_ID, - R_FlameTrail_ID, - R_VoorTrail_ID, - R_GlowTrail_QF, - R_RunParticleEffect_ID, - R_BloodPuffEffect_ID, - R_GunshotEffect_ID, - R_LightningBloodEffect_ID, - R_SpikeEffect_ID, - R_KnightSpikeEffect_ID, - R_SuperSpikeEffect_ID, - R_WizSpikeEffect_ID, - R_BlobExplosion_ID, - R_ParticleExplosion_ID, - R_ParticleExplosion2_QF, - R_LavaSplash_ID, - R_TeleportSplash_ID, - R_DarkFieldParticles_ID, - R_EntityParticles_ID, - R_Particle_New, - R_Particle_NewRandom, -}; - -static vid_particle_funcs_t particles_QF_egg = { - R_RocketTrail_EE, - R_GrenadeTrail_EE, - R_BloodTrail_QF, - R_SlightBloodTrail_QF, - R_WizTrail_QF, - R_FlameTrail_QF, - R_VoorTrail_QF, - R_GlowTrail_QF, - R_RunParticleEffect_QF, - R_BloodPuffEffect_QF, - R_GunshotEffect_QF, - R_LightningBloodEffect_QF, - R_SpikeEffect_QF, - R_KnightSpikeEffect_QF, - R_SuperSpikeEffect_QF, - R_WizSpikeEffect_QF, - R_BlobExplosion_QF, - R_ParticleExplosion_EE, - R_ParticleExplosion2_QF, - R_LavaSplash_QF, - R_TeleportSplash_EE, - R_DarkFieldParticles_ID, - R_EntityParticles_ID, - R_Particle_New, - R_Particle_NewRandom, -}; - -static vid_particle_funcs_t particles_ID_egg = { - R_RocketTrail_EE, - R_GrenadeTrail_EE, - R_BloodTrail_ID, - R_SlightBloodTrail_ID, - R_WizTrail_ID, - R_FlameTrail_ID, - R_VoorTrail_ID, - R_GlowTrail_QF, - R_RunParticleEffect_ID, - R_BloodPuffEffect_ID, - R_GunshotEffect_ID, - R_LightningBloodEffect_ID, - R_SpikeEffect_ID, - R_KnightSpikeEffect_ID, - R_SuperSpikeEffect_ID, - R_WizSpikeEffect_ID, - R_BlobExplosion_ID, - R_ParticleExplosion_EE, - R_ParticleExplosion2_QF, - R_LavaSplash_ID, - R_TeleportSplash_EE, - R_DarkFieldParticles_ID, - R_EntityParticles_ID, - R_Particle_New, - R_Particle_NewRandom, -}; - -void -gl_r_easter_eggs_f (cvar_t *var) +psystem_t * __attribute__((const))//FIXME +gl_ParticleSystem (void) { - if (easter_eggs && !gl_feature_mach64) { - if (easter_eggs->int_val) { - if (r_particles_style->int_val) { - gl_vid_render_funcs.particles = &particles_QF_egg; - } else { - gl_vid_render_funcs.particles = &particles_ID_egg; - } - } else if (r_particles_style) { - if (r_particles_style->int_val) { - gl_vid_render_funcs.particles = &particles_QF; - } else { - gl_vid_render_funcs.particles = &particles_ID; - } - } - } -} - -void -gl_r_particles_style_f (cvar_t *var) -{ - gl_r_easter_eggs_f (easter_eggs); -} - -static void -R_ParticleFunctionInit (void) -{ - gl_r_particles_style_f (r_particles_style); - gl_r_easter_eggs_f (easter_eggs); -} - -static void -r_particles_nearclip_f (cvar_t *var) -{ - Cvar_SetValue (r_particles_nearclip, bound (r_nearclip->value, var->value, - r_farclip->value)); -} - -static void -r_particles_f (cvar_t *var) -{ - R_MaxParticlesCheck (var, r_particles_max); -} - -static void -r_particles_max_f (cvar_t *var) -{ - R_MaxParticlesCheck (r_particles, var); -} - -void -gl_R_Particles_Init_Cvars (void) -{ - easter_eggs = Cvar_Get ("easter_eggs", "0", CVAR_NONE, r_easter_eggs_f, - "Enables easter eggs."); - r_particles = Cvar_Get ("r_particles", "1", CVAR_ARCHIVE, r_particles_f, - "Toggles drawing of particles."); - r_particles_max = Cvar_Get ("r_particles_max", "2048", CVAR_ARCHIVE, - r_particles_max_f, "Maximum amount of " - "particles to display. No maximum, minimum " - "is 0."); - r_particles_nearclip = Cvar_Get ("r_particles_nearclip", "32", - CVAR_ARCHIVE, r_particles_nearclip_f, - "Distance of the particle near clipping " - "plane from the player."); - r_particles_style = Cvar_Get ("r_particles_style", "1", CVAR_ARCHIVE, - r_particles_style_f, "Sets particle style. " - "0 for Id, 1 for QF."); - R_ParticleFunctionInit (); -} - -void -gl_R_Particle_New (const char *type, int texnum, const vec3_t org, float scale, - const vec3_t vel, float die, int color, float alpha, - float ramp) -{ - if (numparticles >= r_maxparticles) - return; - particle_new (type, texnum, org, scale, vel, die, color, alpha, ramp); -} - -void -gl_R_Particle_NewRandom (const char *type, int texnum, const vec3_t org, - int org_fuzz, float scale, int vel_fuzz, float die, - int color, float alpha, float ramp) -{ - if (numparticles >= r_maxparticles) - return; - particle_new_random (type, texnum, org, org_fuzz, scale, vel_fuzz, die, - color, alpha, ramp); + return &r_psystem; } diff --git a/libs/video/renderer/gl/gl_dyn_textures.c b/libs/video/renderer/gl/gl_dyn_textures.c index 093a1b905..d9004a69a 100644 --- a/libs/video/renderer/gl/gl_dyn_textures.c +++ b/libs/video/renderer/gl/gl_dyn_textures.c @@ -28,9 +28,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -49,7 +46,7 @@ #include "r_internal.h" -int gl_part_tex; +GLuint gl_part_tex; static GLint part_tex_internal_format = 2; @@ -60,7 +57,7 @@ GDT_InitParticleTexture (void) memset (data, 0, sizeof (data)); - gl_part_tex = gl_texture_number++; + qfglGenTextures (1, &gl_part_tex); qfglBindTexture (GL_TEXTURE_2D, gl_part_tex); qfglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); qfglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); diff --git a/libs/video/renderer/gl/gl_fisheye.c b/libs/video/renderer/gl/gl_fisheye.c new file mode 100644 index 000000000..2e1483214 --- /dev/null +++ b/libs/video/renderer/gl/gl_fisheye.c @@ -0,0 +1,181 @@ +/* + gl_fisheye.c + + GL fisheye rendering + + Copyright (C) 2003 Arkadi Shishlov + Copyright (C) 2022 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include +#include + +#include "QF/cvar.h" +#include "QF/mathlib.h" +#include "QF/render.h" +#include "QF/sys.h" +#include "QF/vid.h" + +#include "QF/GL/defines.h" +#include "QF/GL/funcs.h" +#include "QF/GL/qf_draw.h" +#include "QF/GL/qf_fisheye.h" +#include "QF/GL/qf_vid.h" + +#include "compat.h" +#include "r_internal.h" +#include "varrays.h" +#include "vid_gl.h" + +// Algorithm: +// Draw up to six views, one in each direction. +// Save the picture to cube map texture, use GL_ARB_texture_cube_map. +// Create FPOLYCNTxFPOLYCNT polygons sized flat grid. +// Baseing on field of view, tie cube map texture to grid using +// translation function to map texture coordinates to fixed/regular +// grid vertices coordinates. +// Render view. Fisheye is done. + +#define FPOLYCNT 128 + +struct xyz { + float x, y, z; +}; + +static struct xyz FisheyeLookupTbl[FPOLYCNT + 1][FPOLYCNT + 1]; +//static GLint gl_cube_map_size; +static GLint gl_cube_map_step; + +static void +R_BuildFisheyeLookup (int width, int height, float fov) +{ + int x, y; + struct xyz *v; + + for (y = 0; y <= height; y += gl_cube_map_step) { + for (x = 0; x <= width; x += gl_cube_map_step) { + float dx = x - width / 2; + float dy = y - height / 2; + float yaw = sqrt (dx * dx + dy * dy) * fov / width; + float roll = atan2 (dy, dx); + // X is a first index and Y is a second, because later + // when we draw QUAD_STRIPs we need next Y vertex coordinate. + v = &FisheyeLookupTbl[x / gl_cube_map_step][y / gl_cube_map_step]; + v->x = sin (yaw) * cos (roll); + v->y = sin (yaw) * sin (roll); + v->z = cos (yaw); + } + } +} + +#define CHKGLERR(s) \ +do { \ + GLint err = qfglGetError(); \ + if (err != GL_NO_ERROR) \ + printf ("%s: gl error %d\n", s, (int) err); \ +} while (0); + + +static GLuint fisheye_grid; + +void +gl_InitFisheye (void) +{ + fisheye_grid = qfglGenLists (1); +} + +static void +build_display_list (int width, int height, float fov) +{ + int maxd = max (width, height); + gl_cube_map_step = maxd / FPOLYCNT; + + R_BuildFisheyeLookup (width, height, fov * M_PI / 180.0); + + qfglNewList (fisheye_grid, GL_COMPILE); + + qfglDisable (GL_DEPTH_TEST); + qfglFrontFace (GL_CCW); + qfglClear (GL_COLOR_BUFFER_BIT); + + for (int y = 0; y < height; y += gl_cube_map_step) { + qfglBegin (GL_QUAD_STRIP); + // quad_strip, so X is inclusive + for (int x = 0; x <= width; x += gl_cube_map_step) { + int xind = x / gl_cube_map_step; + int yind = y / gl_cube_map_step; + struct xyz *v = &FisheyeLookupTbl[xind][yind + 1]; + qfglTexCoord3f (v->x, v->y, v->z); + qfglVertex2i (x, y + gl_cube_map_step); + --v; + qfglTexCoord3f (v->x, v->y, v->z); + qfglVertex2i (x, y); + } + qfglEnd (); + } + qfglEnable (GL_DEPTH_TEST); + qfglEndList (); + CHKGLERR("build list"); +} + +void +gl_FisheyeScreen (framebuffer_t *fb) +{ + static int pwidth = -1; + static int pheight = -1; + static float pfov = -1; + + if (pwidth != r_refdef.vrect.width || pheight != r_refdef.vrect.height + || pfov != scr_ffov) { + pwidth = r_refdef.vrect.width; + pheight = r_refdef.vrect.height; + pfov = scr_ffov; + + build_display_list (pwidth, pheight, pfov); + } + + gl_framebuffer_t *buffer = fb->buffer; + + qfglMatrixMode (GL_PROJECTION); + qfglLoadIdentity (); + qfglOrtho (0, r_refdef.vrect.width, r_refdef.vrect.height, 0, -9999, 9999); + qfglMatrixMode (GL_MODELVIEW); + qfglLoadIdentity (); + qfglColor3ubv (color_white); + + qfglEnable (GL_TEXTURE_CUBE_MAP_ARB); + qfglBindTexture (GL_TEXTURE_CUBE_MAP_ARB, buffer->color); + qfglCallList (fisheye_grid); + qfglDisable (GL_TEXTURE_CUBE_MAP_ARB); + CHKGLERR("fisheye"); +} diff --git a/libs/video/renderer/gl/gl_fog.c b/libs/video/renderer/gl/gl_fog.c index fe63419b5..edcac74e1 100644 --- a/libs/video/renderer/gl/gl_fog.c +++ b/libs/video/renderer/gl/gl_fog.c @@ -23,217 +23,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif - -#include "QF/cmd.h" -#include "QF/qfplist.h" -#include "QF/render.h" -#include "QF/sys.h" - #include "QF/GL/defines.h" #include "QF/GL/funcs.h" #include "compat.h" #include "r_internal.h" - -//============================================================================== -// -// GLOBAL FOG -// -//============================================================================== - -static float fog_density; -static float fog_red; -static float fog_green; -static float fog_blue; - -static float old_density; -static float old_red; -static float old_green; -static float old_blue; - -static float fade_time; //duration of fade -static float fade_done; //time when fade will be done - -/* - Fog_Update - - update internal variables -*/ -void -gl_Fog_Update (float density, float red, float green, float blue, float time) -{ - //save previous settings for fade - if (time > 0) { - //check for a fade in progress - if (fade_done > vr_data.realtime) { - float f; - - f = (fade_done - vr_data.realtime) / fade_time; - old_density = f * old_density + (1.0 - f) * fog_density; - old_red = f * old_red + (1.0 - f) * fog_red; - old_green = f * old_green + (1.0 - f) * fog_green; - old_blue = f * old_blue + (1.0 - f) * fog_blue; - } else { - old_density = fog_density; - old_red = fog_red; - old_green = fog_green; - old_blue = fog_blue; - } - } - - fog_density = density; - fog_red = red; - fog_green = green; - fog_blue = blue; - fade_time = time; - fade_done = vr_data.realtime + time; -} - -/* - Fog_FogCommand_f - - handle the 'fog' console command -*/ -static void -Fog_FogCommand_f (void) -{ - float density = fog_density; - float red = fog_red; - float green = fog_green; - float blue = fog_blue; - float time = 0.0; - - switch (Cmd_Argc ()) { - default: - case 1: - Sys_Printf ("usage:\n"); - Sys_Printf (" fog \n"); - Sys_Printf (" fog \n"); - Sys_Printf (" fog \n"); - Sys_Printf ("current values:\n"); - Sys_Printf (" \"density\" is \"%f\"\n", fog_density); - Sys_Printf (" \"red\" is \"%f\"\n", fog_red); - Sys_Printf (" \"green\" is \"%f\"\n", fog_green); - Sys_Printf (" \"blue\" is \"%f\"\n", fog_blue); - return; - case 2: - density = atof (Cmd_Argv(1)); - break; - case 3: //TEST - density = atof (Cmd_Argv(1)); - time = atof (Cmd_Argv(2)); - break; - case 4: - red = atof (Cmd_Argv(1)); - green = atof (Cmd_Argv(2)); - blue = atof (Cmd_Argv(3)); - break; - case 5: - density = atof (Cmd_Argv(1)); - red = atof (Cmd_Argv(2)); - green = atof (Cmd_Argv(3)); - blue = atof (Cmd_Argv(4)); - break; - case 6: //TEST - density = atof (Cmd_Argv(1)); - red = atof (Cmd_Argv(2)); - green = atof (Cmd_Argv(3)); - blue = atof (Cmd_Argv(4)); - time = atof (Cmd_Argv(5)); - break; - } - density = max (0.0, density); - red = bound (0.0, red, 1.0); - green = bound (0.0, green, 1.0); - blue = bound (0.0, blue, 1.0); - gl_Fog_Update (density, red, green, blue, time); -} - -/* - Fog_ParseWorldspawn - - called at map load -*/ -void -gl_Fog_ParseWorldspawn (plitem_t *worldspawn) -{ - plitem_t *fog; - const char *value; - - //initially no fog - fog_density = 0.0; - old_density = 0.0; - fade_time = 0.0; - fade_done = 0.0; - - if (!worldspawn) - return; // error - if ((fog = PL_ObjectForKey (worldspawn, "fog")) - && (value = PL_String (fog))) { - sscanf (value, "%f %f %f %f", &fog_density, - &fog_red, &fog_green, &fog_blue); - } -} - -/* - Fog_GetColor - - calculates fog color for this frame, taking into account fade times -*/ -float * -gl_Fog_GetColor (void) -{ - static float c[4]; - float f; - int i; - - if (fade_done > vr_data.realtime) { - f = (fade_done - vr_data.realtime) / fade_time; - c[0] = f * old_red + (1.0 - f) * fog_red; - c[1] = f * old_green + (1.0 - f) * fog_green; - c[2] = f * old_blue + (1.0 - f) * fog_blue; - c[3] = 1.0; - } else { - c[0] = fog_red; - c[1] = fog_green; - c[2] = fog_blue; - c[3] = 1.0; - } - - //find closest 24-bit RGB value, so solid-colored sky can match the fog - //perfectly - for (i = 0; i < 3; i++) - c[i] = (float) (rint (c[i] * 255)) / 255.0f; - - return c; -} - -/* - Fog_GetDensity - - returns current density of fog -*/ -float -gl_Fog_GetDensity (void) -{ - float f; - - if (fade_done > vr_data.realtime) { - f = (fade_done - vr_data.realtime) / fade_time; - return f * old_density + (1.0 - f) * fog_density; - } else { - return fog_density; - } -} +#include "vid_gl.h" /* Fog_SetupFrame @@ -243,10 +38,15 @@ gl_Fog_GetDensity (void) void gl_Fog_SetupFrame (void) { - qfglFogfv (GL_FOG_COLOR, gl_Fog_GetColor ()); - qfglFogf (GL_FOG_DENSITY, gl_Fog_GetDensity () / 64.0); + quat_t fogcolor; + + Fog_GetColor (fogcolor); + qfglFogfv (GL_FOG_COLOR, fogcolor); + qfglFogf (GL_FOG_DENSITY, Fog_GetDensity () / 64.0); + qfglFogi (GL_FOG_MODE, GL_EXP2); } + /* Fog_EnableGFog @@ -255,7 +55,7 @@ gl_Fog_SetupFrame (void) void gl_Fog_EnableGFog (void) { - if (gl_Fog_GetDensity () > 0) + if (Fog_GetDensity () > 0) qfglEnable (GL_FOG); } @@ -267,7 +67,7 @@ gl_Fog_EnableGFog (void) void gl_Fog_DisableGFog (void) { - if (gl_Fog_GetDensity () > 0) + if (Fog_GetDensity () > 0) qfglDisable (GL_FOG); } @@ -282,7 +82,8 @@ gl_Fog_StartAdditive (void) { vec3_t color = {0, 0, 0}; - if (gl_Fog_GetDensity () > 0) + qfglFogi (GL_FOG_MODE, GL_EXP2); + if (Fog_GetDensity () > 0) qfglFogfv (GL_FOG_COLOR, color); } @@ -294,58 +95,9 @@ gl_Fog_StartAdditive (void) void gl_Fog_StopAdditive (void) { - if (gl_Fog_GetDensity () > 0) - qfglFogfv (GL_FOG_COLOR, gl_Fog_GetColor ()); -} - -//============================================================================== -// -// VOLUMETRIC FOG -// -//============================================================================== - -//cvar_t r_vfog = {"r_vfog", "1"}; - -//void Fog_DrawVFog (void) {} -//void Fog_MarkModels (void) {} - -//============================================================================== -// -// INIT -// -//============================================================================== - -/* - Fog_NewMap - - called whenever a map is loaded -*/ -#if 0 -void -gl_Fog_NewMap (void) -{ - Fog_ParseWorldspawn (); //for global fog - Fog_MarkModels (); //for volumetric fog -} -#endif - -/* - Fog_Init - - called when quake initializes -*/ -void -gl_Fog_Init (void) -{ - Cmd_AddCommand ("fog", Fog_FogCommand_f, ""); - - //Cvar_RegisterVariable (&r_vfog, NULL); - - //set up global fog - fog_density = 0.0; - fog_red = 0.3; - fog_green = 0.3; - fog_blue = 0.3; - - qfglFogi (GL_FOG_MODE, GL_EXP2); + if (Fog_GetDensity () > 0) { + quat_t fogcolor; + Fog_GetColor (fogcolor); + qfglFogfv (GL_FOG_COLOR, fogcolor); + } } diff --git a/libs/video/renderer/gl/gl_graph.c b/libs/video/renderer/gl/gl_graph.c index b07c6c5f0..8e816eb11 100644 --- a/libs/video/renderer/gl/gl_graph.c +++ b/libs/video/renderer/gl/gl_graph.c @@ -28,9 +28,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -47,6 +44,7 @@ #include "QF/sys.h" #include "QF/GL/defines.h" #include "QF/GL/funcs.h" +#include "QF/GL/qf_rmain.h" #include "QF/GL/qf_textures.h" #include "r_internal.h" @@ -54,24 +52,20 @@ #define NUM_GRAPH_TEXTURES 8 static byte *graph_texels[NUM_GRAPH_TEXTURES]; -static int graph_texture[NUM_GRAPH_TEXTURES]; +static GLuint graph_texture[NUM_GRAPH_TEXTURES]; static int graph_index; static int graph_size[NUM_GRAPH_TEXTURES]; static int graph_width[NUM_GRAPH_TEXTURES]; -int -gl_R_InitGraphTextures (int base) +void +gl_R_InitGraphTextures (void) { - int i; - - for (i = 0; i < NUM_GRAPH_TEXTURES; i++) - graph_texture[i] = base++; - return base; + qfglGenTextures (NUM_GRAPH_TEXTURES, graph_texture); } void -gl_R_LineGraph (int x, int y, int *h_vals, int count) +gl_R_LineGraph (int x, int y, int *h_vals, int count, int height) { byte color; byte *dest; @@ -80,7 +74,7 @@ gl_R_LineGraph (int x, int y, int *h_vals, int count) if (!count) return; - s = r_graphheight->int_val; + s = height; size = s * count; if (size > graph_size[graph_index]) { diff --git a/libs/video/renderer/gl/gl_lightmap.c b/libs/video/renderer/gl/gl_lightmap.c index 9dfa3df92..d74855567 100644 --- a/libs/video/renderer/gl/gl_lightmap.c +++ b/libs/video/renderer/gl/gl_lightmap.c @@ -29,9 +29,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -45,10 +42,14 @@ #include "QF/cvar.h" #include "QF/render.h" #include "QF/sys.h" + +#include "QF/scene/entity.h" + #include "QF/GL/defines.h" #include "QF/GL/funcs.h" #include "QF/GL/qf_lightmap.h" #include "QF/GL/qf_rmain.h" +#include "QF/GL/qf_rsurf.h" #include "QF/GL/qf_sky.h" #include "QF/GL/qf_textures.h" #include "QF/GL/qf_vid.h" @@ -56,28 +57,28 @@ #include "compat.h" #include "r_internal.h" +static scrap_t *light_scrap; static int dlightdivtable[8192]; static int gl_internalformat; // 1 or 3 static int lightmap_bytes; // 1, 3, or 4 -int gl_lightmap_textures; +GLuint gl_lightmap_textures[MAX_LIGHTMAPS]; // keep lightmap texture data in main memory so texsubimage can update properly // LordHavoc: changed to be allocated at runtime (typically lower memory usage) static byte *lightmaps[MAX_LIGHTMAPS]; -static unsigned int blocklights[34 * 34 * 3]; //FIXME make dynamic +static unsigned *blocklights; +static int bl_extents[2]; static int allocated[MAX_LIGHTMAPS][BLOCK_WIDTH]; -qboolean gl_lightmap_modified[MAX_LIGHTMAPS]; -instsurf_t *gl_lightmap_polys[MAX_LIGHTMAPS]; +bool gl_lightmap_modified[MAX_LIGHTMAPS]; +instsurf_t *gl_lightmap_polys; glRect_t gl_lightmap_rectchange[MAX_LIGHTMAPS]; static int lmshift = 7; -void (*gl_R_BuildLightMap) (msurface_t *surf); - -extern void gl_multitexture_f (cvar_t *var); - +void (*gl_R_BuildLightMap) (const vec4f_t *transform, mod_brush_t *brush, + msurface_t *surf); void gl_lightmap_init (void) @@ -89,25 +90,9 @@ gl_lightmap_init (void) for (s = 1; s < 8192; s++) dlightdivtable[s] = 1048576 / (s << 7); } -/* -static void -R_RecursiveLightUpdate (mnode_t *node) -{ - int c; - msurface_t *surf; - if (node->children[0]->contents >= 0) - R_RecursiveLightUpdate (node->children[0]); - if (node->children[1]->contents >= 0) - R_RecursiveLightUpdate (node->children[1]); - if ((c = node->numsurfaces)) - for (surf = r_worldentity.model->surfaces + node->firstsurface; c; - c--, surf++) - surf->cached_dlight = true; -} -*/ static inline void -R_AddDynamicLights_1 (msurface_t *surf) +R_AddDynamicLights_1 (const vec4f_t *transform, msurface_t *surf) { float dist; unsigned int maxdist, maxdist2, maxdist3; @@ -117,17 +102,25 @@ R_AddDynamicLights_1 (msurface_t *surf) unsigned int sdtable[18]; unsigned int *bl; vec3_t impact, local; + vec4f_t entorigin = { 0, 0, 0, 1 }; smax = (surf->extents[0] >> 4) + 1; smax_bytes = smax * gl_internalformat; tmax = (surf->extents[1] >> 4) + 1; + if (transform) { + //FIXME give world entity a transform + entorigin = transform[3]; + } + for (lnum = 0; lnum < r_maxdlights; lnum++) { if (!(surf->dlightbits[lnum / 32] & (1 << (lnum % 32)))) continue; // not lit by this light - VectorSubtract (r_dlights[lnum].origin, currententity->origin, local); + VectorSubtract (r_dlights[lnum].origin, entorigin, local); dist = DotProduct (local, surf->plane->normal) - surf->plane->dist; + if (dist > min (1024, r_dlights[lnum].radius)) + continue; VectorMultSub (r_dlights[lnum].origin, dist, surf->plane->normal, impact); @@ -159,7 +152,7 @@ R_AddDynamicLights_1 (msurface_t *surf) if (td < maxdist3) { // ensure part is visible on this line maxdist2 = maxdist - td; for (s = 0; s < smax; s++) { - if (sdtable[s] < maxdist2) { + if (sdtable[s] + td < maxdist2) { j = dlightdivtable[(sdtable[s] + td) >> 7]; *bl++ += (grey * j) >> 7; } else @@ -172,7 +165,7 @@ R_AddDynamicLights_1 (msurface_t *surf) } static inline void -R_AddDynamicLights_3 (msurface_t *surf) +R_AddDynamicLights_3 (const vec4f_t *transform, msurface_t *surf) { float dist; unsigned int maxdist, maxdist2, maxdist3; @@ -182,17 +175,24 @@ R_AddDynamicLights_3 (msurface_t *surf) unsigned int sdtable[18]; unsigned int *bl; vec3_t impact, local; + vec4f_t entorigin = { 0, 0, 0, 1 }; smax = (surf->extents[0] >> 4) + 1; smax_bytes = smax * gl_internalformat; tmax = (surf->extents[1] >> 4) + 1; + if (transform) { + entorigin = transform[3]; + } + for (lnum = 0; lnum < r_maxdlights; lnum++) { if (!(surf->dlightbits[lnum / 32] & (1 << (lnum % 32)))) continue; // not lit by this light - VectorSubtract (r_dlights[lnum].origin, currententity->origin, local); + VectorSubtract (r_dlights[lnum].origin, entorigin, local); dist = DotProduct (local, surf->plane->normal) - surf->plane->dist; + if (dist > min (1024, r_dlights[lnum].radius)) + continue; VectorMultSub (r_dlights[lnum].origin, dist, surf->plane->normal, impact); @@ -225,7 +225,7 @@ R_AddDynamicLights_3 (msurface_t *surf) if (td < maxdist3) { // ensure part is visible on this line maxdist2 = maxdist - td; for (s = 0; s < smax; s++) { - if (sdtable[s] < maxdist2) { + if (sdtable[s] + td < maxdist2) { j = dlightdivtable[(sdtable[s] + td) >> 7]; *bl++ += (red * j) >> 7; *bl++ += (green * j) >> 7; @@ -240,10 +240,11 @@ R_AddDynamicLights_3 (msurface_t *surf) } static void -R_BuildLightMap_1 (msurface_t *surf) +R_BuildLightMap_1 (const vec4f_t *transform, mod_brush_t *brush, + msurface_t *surf) { byte *dest; - int maps, size, stride, smax, tmax, i, j; + int maps, size, smax, tmax, i; unsigned int scale; unsigned int *bl; @@ -254,7 +255,7 @@ R_BuildLightMap_1 (msurface_t *surf) size = smax * tmax * gl_internalformat; // set to full bright if no light data - if (!r_worldentity.model->lightdata) { + if (!brush->lightdata) { memset (&blocklights[0], 0xff, size * sizeof(int)); goto store; } @@ -279,31 +280,27 @@ R_BuildLightMap_1 (msurface_t *surf) } // add all the dynamic lights if (surf->dlightframe == r_framecount) - R_AddDynamicLights_1 (surf); + R_AddDynamicLights_1 (transform, surf); store: // bound and shift // Also, invert because we're using a diff blendfunc now - - stride = (BLOCK_WIDTH - smax) * lightmap_bytes; bl = blocklights; - - dest = lightmaps[surf->lightmaptexturenum] - + (surf->light_t * BLOCK_WIDTH + surf->light_s) * lightmap_bytes; - - for (i = 0; i < tmax; i++, dest += stride) { - for (j = smax; j; j--) { - *dest++ = min (*bl >> lmshift, 255); - bl++; - } + dest = (byte *) blocklights; + for (i = 0; i < tmax * smax; i++) { + *dest++ = min (*bl >> lmshift, 255); + bl++; } + + GL_SubpicUpdate (surf->lightpic, (byte *) blocklights, 1); } static void -R_BuildLightMap_3 (msurface_t *surf) +R_BuildLightMap_3 (const vec4f_t *transform, mod_brush_t *brush, + msurface_t *surf) { byte *dest; - int maps, size, stride, smax, tmax, i, j; + int maps, size, smax, tmax, i; unsigned int scale; unsigned int *bl; @@ -314,7 +311,7 @@ R_BuildLightMap_3 (msurface_t *surf) size = smax * tmax * gl_internalformat; // set to full bright if no light data - if (!r_worldentity.model->lightdata) { + if (!brush->lightdata) { memset (&blocklights[0], 0xff, size * sizeof(int)); goto store; } @@ -341,34 +338,31 @@ R_BuildLightMap_3 (msurface_t *surf) } // add all the dynamic lights if (surf->dlightframe == r_framecount) - R_AddDynamicLights_3 (surf); + R_AddDynamicLights_3 (transform, surf); store: // bound and shift // and invert too - stride = (BLOCK_WIDTH - smax) * lightmap_bytes; bl = blocklights; - - dest = lightmaps[surf->lightmaptexturenum] - + (surf->light_t * BLOCK_WIDTH + surf->light_s) * lightmap_bytes; - - for (i = 0; i < tmax; i++, dest += stride) { - for (j = 0; j < smax; j++) { - *dest++ = min (*bl >> lmshift, 255); - bl++; - *dest++ = min (*bl >> lmshift, 255); - bl++; - *dest++ = min (*bl >> lmshift, 255); - bl++; - } + dest = (byte *) blocklights; + for (i = 0; i < tmax * smax; i++) { + *dest++ = min (*bl >> lmshift, 255); + bl++; + *dest++ = min (*bl >> lmshift, 255); + bl++; + *dest++ = min (*bl >> lmshift, 255); + bl++; } + + GL_SubpicUpdate (surf->lightpic, (byte *) blocklights, 1); } static void -R_BuildLightMap_4 (msurface_t *surf) +R_BuildLightMap_4 (const vec4f_t *transform, mod_brush_t *brush, + msurface_t *surf) { byte *dest; - int maps, size, smax, tmax, i, j, stride; + int maps, size, smax, tmax, i; unsigned int scale; unsigned int *bl; @@ -379,7 +373,7 @@ R_BuildLightMap_4 (msurface_t *surf) size = smax * tmax * gl_internalformat; // set to full bright if no light data - if (!r_worldentity.model->lightdata) { + if (!brush->lightdata) { memset (&blocklights[0], 0xff, size * sizeof(int)); goto store; } @@ -406,121 +400,55 @@ R_BuildLightMap_4 (msurface_t *surf) } // add all the dynamic lights if (surf->dlightframe == r_framecount) - R_AddDynamicLights_3 (surf); + R_AddDynamicLights_3 (transform, surf); store: // bound and shift // and invert too - stride = (BLOCK_WIDTH - smax) * lightmap_bytes; bl = blocklights; - - dest = lightmaps[surf->lightmaptexturenum] - + (surf->light_t * BLOCK_WIDTH + surf->light_s) * lightmap_bytes; - - for (i = 0; i < tmax; i++, dest += stride) { - for (j = 0; j < smax; j++) { - *dest++ = min (*bl >> lmshift, 255); - bl++; - *dest++ = min (*bl >> lmshift, 255); - bl++; - *dest++ = min (*bl >> lmshift, 255); - bl++; - *dest++ = 255; - } + dest = (byte *) blocklights; + for (i = 0; i < tmax * smax; i++) { + *dest++ = min (*bl >> lmshift, 255); + bl++; + *dest++ = min (*bl >> lmshift, 255); + bl++; + *dest++ = min (*bl >> lmshift, 255); + bl++; + *dest++ = 255; } + + GL_SubpicUpdate (surf->lightpic, (byte *) blocklights, 1); } // BRUSH MODELS =============================================================== -static inline void -do_subimage_2 (int i) -{ - byte *block, *lm, *b; - int stride, width; - glRect_t *rect = &gl_lightmap_rectchange[i]; - - width = rect->w * lightmap_bytes; - stride = BLOCK_WIDTH * lightmap_bytes; - b = block = Hunk_TempAlloc (rect->h * width); - lm = lightmaps[i] + (rect->t * BLOCK_WIDTH + rect->l) * lightmap_bytes; - for (i = rect->h; i > 0; i--) { - memcpy (b, lm, width); - b += width; - lm += stride; - } - qfglTexSubImage2D (GL_TEXTURE_2D, 0, rect->l, rect->t, rect->w, rect->h, - gl_lightmap_format, GL_UNSIGNED_BYTE, block); -} - -static void -GL_UploadLightmap (int i) -{ - switch (gl_lightmap_subimage->int_val) { - case 2: - do_subimage_2 (i); - break; - case 1: - qfglTexSubImage2D (GL_TEXTURE_2D, 0, 0, gl_lightmap_rectchange[i].t, - BLOCK_WIDTH, gl_lightmap_rectchange[i].h, - gl_lightmap_format, GL_UNSIGNED_BYTE, - lightmaps[i] + (gl_lightmap_rectchange[i].t * - BLOCK_WIDTH) * lightmap_bytes); - break; - default: - case 0: - qfglTexImage2D (GL_TEXTURE_2D, 0, gl_internalformat, BLOCK_WIDTH, - BLOCK_HEIGHT, 0, gl_lightmap_format, GL_UNSIGNED_BYTE, - lightmaps[i]); - break; - } -} - -void -gl_R_CalcLightmaps (void) -{ - int i; - - for (i = 0; i < MAX_LIGHTMAPS; i++) { - if (!gl_lightmap_polys[i]) - continue; - if (gl_lightmap_modified[i]) { - qfglBindTexture (GL_TEXTURE_2D, gl_lightmap_textures + i); - GL_UploadLightmap (i); - gl_lightmap_modified[i] = false; - } - } -} - void gl_R_BlendLightmaps (void) { float *v; - int i, j; instsurf_t *sc; glpoly_t *p; qfglDepthMask (GL_FALSE); // don't bother writing Z qfglBlendFunc (lm_src_blend, lm_dest_blend); - for (i = 0; i < MAX_LIGHTMAPS; i++) { - for (sc = gl_lightmap_polys[i]; sc; sc = sc->lm_chain) { - qfglBindTexture (GL_TEXTURE_2D, gl_lightmap_textures + i); - if (sc->transform) { - qfglPushMatrix (); - qfglLoadMatrixf (sc->transform); - } - for (p = sc->surface->polys; p; p = p->next) { - qfglBegin (GL_POLYGON); - v = p->verts[0]; - for (j = 0; j < p->numverts; j++, v += VERTEXSIZE) { - qfglTexCoord2fv (&v[5]); - qfglVertex3fv (v); - } - qfglEnd (); - } - if (sc->transform) - qfglPopMatrix (); + qfglBindTexture (GL_TEXTURE_2D, gl_R_LightmapTexture ()); + for (sc = gl_lightmap_polys; sc; sc = sc->lm_chain) { + if (sc->transform) { + qfglPushMatrix (); + qfglLoadMatrixf ((vec_t*)&sc->transform[0]);//FIXME } + for (p = sc->surface->polys; p; p = p->next) { + qfglBegin (GL_POLYGON); + v = p->verts[0]; + for (int j = 0; j < p->numverts; j++, v += VERTEXSIZE) { + qfglTexCoord2fv (&v[5]); + qfglVertex3fv (v); + } + qfglEnd (); + } + if (sc->transform) + qfglPopMatrix (); } // Return to normal blending --KB @@ -529,17 +457,14 @@ gl_R_BlendLightmaps (void) } void -gl_overbright_f (cvar_t *var) +gl_overbright_f (void *data, const cvar_t *cvar) { - int num, i, j; - model_t *m; - msurface_t *fa; - entity_t *ent; + mod_brush_t *brush; - if (!var) + if (!cvar) return; - if (var->int_val) { + if (gl_overbright) { if (!gl_combine_capable && gl_mtex_capable) { Sys_Printf ("Warning: gl_overbright has no effect with " "gl_multitexture enabled if you don't have " @@ -552,7 +477,7 @@ gl_overbright_f (cvar_t *var) lm_src_blend = GL_DST_COLOR; lm_dest_blend = GL_SRC_COLOR; - switch (var->int_val) { + switch (gl_overbright) { case 2: lmshift = 9; gl_rgb_scale = 4.0; @@ -574,98 +499,24 @@ gl_overbright_f (cvar_t *var) gl_rgb_scale = 1.0; } - if (gl_multitexture) - gl_multitexture_f (gl_multitexture); - if (!gl_R_BuildLightMap) return; - for (ent = r_ent_queue; ent; ent = ent->next) { - m = ent->model; + brush = &r_refdef.worldmodel->brush; - if (m->type != mod_brush) - continue; - if (m->name[0] == '*') + for (unsigned i = 0; i < brush->numsurfaces; i++) { + msurface_t *surf = brush->surfaces + i; + if (surf->flags & (SURF_DRAWTURB | SURF_DRAWSKY)) continue; - for (j = 0, fa = m->surfaces; j < m->numsurfaces; j++, fa++) { - if (fa->flags & (SURF_DRAWTURB | SURF_DRAWSKY)) - continue; - - num = fa->lightmaptexturenum; - gl_lightmap_modified[num] = true; - gl_lightmap_rectchange[num].l = 0; - gl_lightmap_rectchange[num].t = 0; - gl_lightmap_rectchange[num].w = BLOCK_WIDTH; - gl_lightmap_rectchange[num].h = BLOCK_HEIGHT; - - gl_R_BuildLightMap (fa); - } - } - - m = r_worldentity.model; - - for (i = 0, fa = m->surfaces; i < m->numsurfaces; i++, fa++) { - if (fa->flags & (SURF_DRAWTURB | SURF_DRAWSKY)) - continue; - - num = fa->lightmaptexturenum; - gl_lightmap_modified[num] = true; - gl_lightmap_rectchange[num].l = 0; - gl_lightmap_rectchange[num].t = 0; - gl_lightmap_rectchange[num].w = BLOCK_WIDTH; - gl_lightmap_rectchange[num].h = BLOCK_HEIGHT; - - gl_R_BuildLightMap (fa); + gl_R_BuildLightMap (0, brush, surf); } } // LIGHTMAP ALLOCATION ======================================================== -// returns a texture number and the position inside it -static int -AllocBlock (int w, int h, int *x, int *y) -{ - int best, best2, texnum, i, j; - - for (texnum = 0; texnum < MAX_LIGHTMAPS; texnum++) { - best = BLOCK_HEIGHT; - - for (i = 0; i < BLOCK_WIDTH - w; i++) { - best2 = 0; - - for (j = 0; j < w; j++) { - if (allocated[texnum][i + j] >= best) - break; - if (allocated[texnum][i + j] > best2) - best2 = allocated[texnum][i + j]; - } - if (j == w) { - // this is a valid spot - *x = i; - *y = best = best2; - } - } - - if (best + h > BLOCK_HEIGHT) - continue; - - // LordHavoc: allocate lightmaps only as needed - if (!lightmaps[texnum]) - lightmaps[texnum] = calloc (BLOCK_WIDTH * BLOCK_HEIGHT, - lightmap_bytes); - for (i = 0; i < w; i++) - allocated[texnum][*x + i] = best + h; - - return texnum; - } - - Sys_Error ("AllocBlock: full"); - return 0; -} - static void -GL_CreateSurfaceLightmap (msurface_t *surf) +GL_CreateSurfaceLightmap (mod_brush_t *brush, msurface_t *surf) { int smax, tmax; @@ -675,9 +526,16 @@ GL_CreateSurfaceLightmap (msurface_t *surf) smax = (surf->extents[0] >> 4) + 1; tmax = (surf->extents[1] >> 4) + 1; - surf->lightmaptexturenum = - AllocBlock (smax, tmax, &surf->light_s, &surf->light_t); - gl_R_BuildLightMap (surf); + surf->lightpic = GL_ScrapSubpic (light_scrap, smax, tmax); + if (!surf->lightpic) { + Sys_Error ("FIXME taniwha is being lazy"); + } + if (smax > bl_extents[0]) { + bl_extents[0] = smax; + } + if (tmax > bl_extents[1]) { + bl_extents[1] = tmax; + } } /* @@ -688,19 +546,14 @@ GL_CreateSurfaceLightmap (msurface_t *surf) void GL_BuildLightmaps (model_t **models, int num_models) { - int i, j; model_t *m; + mod_brush_t *brush; memset (allocated, 0, sizeof (allocated)); r_framecount = 1; // no dlightcache - if (!gl_lightmap_textures) { - gl_lightmap_textures = gl_texture_number; - gl_texture_number += MAX_LIGHTMAPS; - } - - switch (r_lightmap_components->int_val) { + switch (r_lightmap_components) { case 1: gl_internalformat = 1; gl_lightmap_format = GL_LUMINANCE; @@ -728,45 +581,63 @@ GL_BuildLightmaps (model_t **models, int num_models) break; } - for (j = 1; j < num_models; j++) { + if (!light_scrap) { + light_scrap = GL_CreateScrap (4096, gl_lightmap_format, 1); + } else { + GL_ScrapClear (light_scrap); + } + + bl_extents[1] = bl_extents[0] = 0; + for (int j = 1; j < num_models; j++) { m = models[j]; if (!m) break; - if (m->name[0] == '*') { + if (m->path[0] == '*' || m->type != mod_brush) { // sub model surfaces are processed as part of the main model continue; } - r_pcurrentvertbase = m->vertexes; + brush = &m->brush; gl_currentmodel = m; // non-bsp models don't have surfaces. - for (i = 0; i < m->numsurfaces; i++) { - if (m->surfaces[i].flags & SURF_DRAWTURB) + for (unsigned i = 0; i < brush->numsurfaces; i++) { + if (brush->surfaces[i].flags & (SURF_DRAWTURB | SURF_DRAWSKY)) continue; - if (gl_sky_divide->int_val && (m->surfaces[i].flags & - SURF_DRAWSKY)) - continue; - GL_CreateSurfaceLightmap (m->surfaces + i); - GL_BuildSurfaceDisplayList (m->surfaces + i); + GL_CreateSurfaceLightmap (brush, brush->surfaces + i); + GL_BuildSurfaceDisplayList (brush, brush->surfaces + i); } } + int size = bl_extents[0] * bl_extents[1] * 3; // * 3 for rgb support + blocklights = realloc (blocklights, size * sizeof (blocklights[0])); + // upload all lightmaps that were filled - for (i = 0; i < MAX_LIGHTMAPS; i++) { - if (!allocated[i][0]) - break; // no more used - gl_lightmap_modified[i] = false; - gl_lightmap_rectchange[i].l = BLOCK_WIDTH; - gl_lightmap_rectchange[i].t = BLOCK_HEIGHT; - gl_lightmap_rectchange[i].w = 0; - gl_lightmap_rectchange[i].h = 0; - qfglBindTexture (GL_TEXTURE_2D, gl_lightmap_textures + i); - qfglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - qfglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - if (gl_Anisotropy) - qfglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, - gl_aniso); - qfglTexImage2D (GL_TEXTURE_2D, 0, lightmap_bytes, BLOCK_WIDTH, - BLOCK_HEIGHT, 0, gl_lightmap_format, - GL_UNSIGNED_BYTE, lightmaps[i]); + for (int j = 1; j < num_models; j++) { + m = models[j]; + if (!m) + break; + if (m->path[0] == '*' || m->type != mod_brush) { + // sub model surfaces are processed as part of the main model + continue; + } + brush = &m->brush; + // non-bsp models don't have surfaces. + for (unsigned i = 0; i < brush->numsurfaces; i++) { + msurface_t *surf = brush->surfaces + i; + if (surf->lightpic) { + gl_R_BuildLightMap (0, brush, surf); + } + } } } + +int +gl_R_LightmapTexture (void) +{ + return GL_ScrapTexture (light_scrap); +} + +void +gl_R_FlushLightmaps (void) +{ + GL_ScrapFlush (light_scrap); +} diff --git a/libs/video/renderer/gl/gl_mod_alias.c b/libs/video/renderer/gl/gl_mod_alias.c index c74e1815c..f3059637c 100644 --- a/libs/video/renderer/gl/gl_mod_alias.c +++ b/libs/video/renderer/gl/gl_mod_alias.c @@ -28,9 +28,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -42,17 +39,13 @@ #include #include -#include "QF/cvar.h" -#include "QF/locs.h" -#include "QF/mathlib.h" -#include "QF/qargs.h" -#include "QF/render.h" #include "QF/skin.h" -#include "QF/sound.h" -#include "QF/sys.h" -#include "QF/vid.h" + +#include "QF/scene/entity.h" + #include "QF/GL/defines.h" #include "QF/GL/funcs.h" +#include "QF/GL/qf_alias.h" #include "QF/GL/qf_rlight.h" #include "QF/GL/qf_rmain.h" #include "QF/GL/qf_rsurf.h" @@ -60,6 +53,7 @@ #include "compat.h" #include "r_internal.h" +#include "vid_gl.h" typedef struct { vec3_t normal; @@ -188,16 +182,19 @@ GL_DrawAliasFrameMulti (vert_order_t *vo) Standard shadow drawing (triangles version) */ static void -GL_DrawAliasShadowTri (const aliashdr_t *paliashdr, const vert_order_t *vo) +GL_DrawAliasShadowTri (transform_t transform, const aliashdr_t *paliashdr, + const vert_order_t *vo) { - int count = vo->count;; + int count = vo->count; const blended_vert_t *verts = vo->verts; float height, lheight; vec3_t point; const vec_t *scale = paliashdr->mdl.scale; const vec_t *scale_origin = paliashdr->mdl.scale_origin; + vec4f_t entorigin; - lheight = currententity->origin[2] - lightspot[2]; + entorigin = Transform_GetWorldPosition (transform); + lheight = entorigin[2] - lightspot[2]; height = -lheight + 1.0; qfglBegin (GL_TRIANGLES); @@ -225,15 +222,18 @@ GL_DrawAliasShadowTri (const aliashdr_t *paliashdr, const vert_order_t *vo) Standard shadow drawing */ static void -GL_DrawAliasShadow (const aliashdr_t *paliashdr, const vert_order_t *vo) +GL_DrawAliasShadow (transform_t transform, const aliashdr_t *paliashdr, + const vert_order_t *vo) { float height, lheight; int count; const int *order = vo->order; vec3_t point; const blended_vert_t *verts = vo->verts; + vec4f_t entorigin; - lheight = currententity->origin[2] - lightspot[2]; + entorigin = Transform_GetWorldPosition (transform); + lheight = entorigin[2] - lightspot[2]; height = -lheight + 1.0; while ((count = *order++)) { @@ -270,20 +270,20 @@ GL_DrawAliasShadow (const aliashdr_t *paliashdr, const vert_order_t *vo) } static inline vert_order_t * -GL_GetAliasFrameVerts16 (aliashdr_t *paliashdr, entity_t *e) +GL_GetAliasFrameVerts16 (aliashdr_t *paliashdr, entity_t e) { - float blend; + animation_t *animation = Ent_GetComponent (e.id, scene_animation, e.reg); + float blend = R_AliasGetLerpedFrames (animation, paliashdr); int count, i; trivertx16_t *verts; vert_order_t *vo; blended_vert_t *vo_v; - blend = R_AliasGetLerpedFrames (e, paliashdr); verts = (trivertx16_t *) ((byte *) paliashdr + paliashdr->posedata); count = paliashdr->poseverts; - vo = Hunk_TempAlloc (sizeof (*vo) + count * sizeof (blended_vert_t)); + vo = Hunk_TempAlloc (0, sizeof (*vo) + count * sizeof (blended_vert_t)); vo->order = (int *) ((byte *) paliashdr + paliashdr->commands); vo->verts = (blended_vert_t *) &vo[1]; if (paliashdr->tex_coord) { @@ -294,19 +294,19 @@ GL_GetAliasFrameVerts16 (aliashdr_t *paliashdr, entity_t *e) } vo->count = count; - if (!gl_lerp_anim->int_val) + if (!gl_lerp_anim) blend = 1.0; if (blend == 0.0) { - verts = verts + e->pose1 * count; + verts = verts + animation->pose1 * count; } else if (blend == 1.0) { - verts = verts + e->pose2 * count; + verts = verts + animation->pose2 * count; } else { trivertx16_t *verts1, *verts2; - verts1 = verts + e->pose1 * count; - verts2 = verts + e->pose2 * count; + verts1 = verts + animation->pose1 * count; + verts2 = verts + animation->pose2 * count; for (i = 0, vo_v = vo->verts; i < count; i++, vo_v++, verts1++, verts2++) { @@ -335,20 +335,20 @@ GL_GetAliasFrameVerts16 (aliashdr_t *paliashdr, entity_t *e) } static inline vert_order_t * -GL_GetAliasFrameVerts (aliashdr_t *paliashdr, entity_t *e) +GL_GetAliasFrameVerts (aliashdr_t *paliashdr, entity_t e) { - float blend; + animation_t *animation = Ent_GetComponent (e.id, scene_animation, e.reg); + float blend = R_AliasGetLerpedFrames (animation, paliashdr); int count, i; trivertx_t *verts; vert_order_t *vo; blended_vert_t *vo_v; - blend = R_AliasGetLerpedFrames (e, paliashdr); verts = (trivertx_t *) ((byte *) paliashdr + paliashdr->posedata); count = paliashdr->poseverts; - vo = Hunk_TempAlloc (sizeof (*vo) + count * sizeof (blended_vert_t)); + vo = Hunk_TempAlloc (0, sizeof (*vo) + count * sizeof (blended_vert_t)); vo->order = (int *) ((byte *) paliashdr + paliashdr->commands); vo->verts = (blended_vert_t *) &vo[1]; if (paliashdr->tex_coord) { @@ -358,18 +358,18 @@ GL_GetAliasFrameVerts (aliashdr_t *paliashdr, entity_t *e) } vo->count = count; - if (!gl_lerp_anim->int_val) + if (!gl_lerp_anim) blend = 1.0; if (blend == 0.0) { - verts = verts + e->pose1 * count; + verts = verts + animation->pose1 * count; } else if (blend == 1.0) { - verts = verts + e->pose2 * count; + verts = verts + animation->pose2 * count; } else { trivertx_t *verts1, *verts2; - verts1 = verts + e->pose1 * count; - verts2 = verts + e->pose2 * count; + verts1 = verts + animation->pose1 * count; + verts2 = verts + animation->pose2 * count; for (i = 0, vo_v = vo->verts; i < count; i++, vo_v++, verts1++, verts2++) { @@ -398,7 +398,7 @@ GL_GetAliasFrameVerts (aliashdr_t *paliashdr, entity_t *e) } void -gl_R_DrawAliasModel (entity_t *e) +gl_R_DrawAliasModel (entity_t e) { float radius, minlight, d; float position[4] = {0.0, 0.0, 0.0, 1.0}, @@ -407,36 +407,40 @@ gl_R_DrawAliasModel (entity_t *e) emission[4] = {0.0, 0.0, 0.0, 1.0}; int gl_light, texture; int fb_texture = 0, used_lights = 0; - qboolean is_fullbright = false; + bool is_fullbright = false; unsigned lnum; aliashdr_t *paliashdr; dlight_t *l; - model_t *model; vec3_t dist, scale; + vec4f_t origin; vert_order_t *vo; - - model = e->model; + renderer_t *renderer = Ent_GetComponent (e.id, scene_renderer, e.reg); + model_t *model = renderer->model; radius = model->radius; - if (e->scale != 1.0) - radius *= e->scale; - if (R_CullSphere (e->origin, radius)) + transform_t transform = Entity_Transform (e); + origin = Transform_GetWorldPosition (transform); + VectorCopy (Transform_GetWorldScale (transform), scale); + //FIXME assumes uniform scale + if (scale[0] != 1.0) { + radius *= scale[0]; + } + if (R_CullSphere (r_refdef.frustum, (vec_t*)&origin, radius)) {//FIXME return; + } - VectorSubtract (r_origin, e->origin, modelorg); + gl_modelalpha = renderer->colormod[3]; - gl_modelalpha = e->colormod[3]; + is_fullbright = (model->fullbright || renderer->fullbright); + minlight = max (model->min_light, renderer->min_light); - is_fullbright = (model->fullbright || e->fullbright); - minlight = max (model->min_light, e->min_light); - - qfglColor4fv (e->colormod); + qfglColor4fv (renderer->colormod); if (!is_fullbright) { float lightadj; // get lighting information - R_LightPoint (e->origin); + R_LightPoint (&r_refdef.worldmodel->brush, origin);//FIXME lightadj = (ambientcolor[0] + ambientcolor[1] + ambientcolor[2]) / 765.0; @@ -454,10 +458,10 @@ gl_R_DrawAliasModel (entity_t *e) ambientcolor[0] = ambientcolor[1] = ambientcolor[2] = minlight; } - if (gl_vector_light->int_val) { + if (gl_vector_light) { for (l = r_dlights, lnum = 0; lnum < r_maxdlights; lnum++, l++) { if (l->die >= vr_data.realtime) { - VectorSubtract (l->origin, e->origin, dist); + VectorSubtract (l->origin, origin, dist); if ((d = DotProduct (dist, dist)) > // Out of range ((l->radius + radius) * (l->radius + radius))) { continue; @@ -508,7 +512,7 @@ gl_R_DrawAliasModel (entity_t *e) for (l = r_dlights, lnum = 0; lnum < r_maxdlights; lnum++, l++) { if (l->die >= vr_data.realtime) { - VectorSubtract (l->origin, e->origin, dist); + VectorSubtract (l->origin, origin, dist); if ((d = DotProduct (dist, dist)) > (l->radius + radius) * (l->radius + radius)) { @@ -530,51 +534,56 @@ gl_R_DrawAliasModel (entity_t *e) VectorScale (emission, 1.5 / d, emission); } - emission[0] *= e->colormod[0]; - emission[1] *= e->colormod[1]; - emission[2] *= e->colormod[2]; - emission[3] *= e->colormod[3]; + emission[0] *= renderer->colormod[0]; + emission[1] *= renderer->colormod[1]; + emission[2] *= renderer->colormod[2]; + emission[3] *= renderer->colormod[3]; qfglColor4fv (emission); } } // locate the proper data - if (!(paliashdr = e->model->aliashdr)) - paliashdr = Cache_Get (&e->model->cache); - gl_c_alias_polys += paliashdr->mdl.numtris; + if (!(paliashdr = renderer->model->aliashdr)) { + paliashdr = Cache_Get (&renderer->model->cache); + } + gl_ctx->alias_polys += paliashdr->mdl.numtris; // if the model has a colorised/external skin, use it, otherwise use // the skin embedded in the model data - if (e->skin && e->skin->texnum && !gl_nocolors->int_val) { - skin_t *skin = e->skin; + if (renderer->skin && renderer->skin->texnum && !gl_nocolors) { + skin_t *skin = renderer->skin; texture = skin->texnum; - if (gl_fb_models->int_val) { + if (gl_fb_models) { fb_texture = skin->auxtex; } } else { maliasskindesc_t *skindesc; - - skindesc = R_AliasGetSkindesc (e->skinnum, paliashdr); + animation_t *animation = Ent_GetComponent (e.id, scene_animation, + e.reg); + skindesc = R_AliasGetSkindesc (animation, renderer->skinnum, paliashdr); texture = skindesc->texnum; - if (gl_fb_models->int_val && !is_fullbright) + if (gl_fb_models && !is_fullbright) { fb_texture = skindesc->fb_texnum; + } } if (paliashdr->mdl.ident == HEADER_MDL16) { // because we multipled by 256 when we loaded the verts, we have to // scale by 1/256 when drawing. - VectorScale (paliashdr->mdl.scale, e->scale / 256.0, scale); + //FIXME see scaling above + VectorScale (paliashdr->mdl.scale, 1 / 256.0, scale); vo = GL_GetAliasFrameVerts16 (paliashdr, e); } else { - VectorScale (paliashdr->mdl.scale, e->scale, scale); + //FIXME see scaling above + VectorScale (paliashdr->mdl.scale, 1, scale); vo = GL_GetAliasFrameVerts (paliashdr, e); } // setup the transform qfglPushMatrix (); - gl_R_RotateForEntity (e); + gl_R_RotateForEntity (Transform_GetWorldMatrixPtr (transform)); qfglTranslatef (paliashdr->mdl.scale_origin[0], paliashdr->mdl.scale_origin[1], @@ -588,7 +597,7 @@ gl_R_DrawAliasModel (entity_t *e) if (is_fullbright) { qfglBindTexture (GL_TEXTURE_2D, texture); - if (gl_vector_light->int_val) { + if (gl_vector_light) { qfglDisable (GL_LIGHTING); if (!gl_tess) qfglDisable (GL_NORMALIZE); @@ -599,7 +608,7 @@ gl_R_DrawAliasModel (entity_t *e) else GL_DrawAliasFrame (vo); - if (gl_vector_light->int_val) { + if (gl_vector_light) { if (!gl_tess) qfglEnable (GL_NORMALIZE); qfglEnable (GL_LIGHTING); @@ -634,18 +643,18 @@ gl_R_DrawAliasModel (entity_t *e) qfglBindTexture (GL_TEXTURE_2D, texture); GL_DrawAliasFrameTri (vo); - if (gl_vector_light->int_val) { + if (gl_vector_light) { qfglDisable (GL_LIGHTING); if (!gl_tess) qfglDisable (GL_NORMALIZE); } - qfglColor4fv (e->colormod); + qfglColor4fv (renderer->colormod); qfglBindTexture (GL_TEXTURE_2D, fb_texture); GL_DrawAliasFrameTri (vo); - if (gl_vector_light->int_val) { + if (gl_vector_light) { qfglEnable (GL_LIGHTING); if (!gl_tess) qfglEnable (GL_NORMALIZE); @@ -654,18 +663,18 @@ gl_R_DrawAliasModel (entity_t *e) qfglBindTexture (GL_TEXTURE_2D, texture); GL_DrawAliasFrame (vo); - if (gl_vector_light->int_val) { + if (gl_vector_light) { qfglDisable (GL_LIGHTING); if (!gl_tess) qfglDisable (GL_NORMALIZE); } - qfglColor4fv (e->colormod); + qfglColor4fv (renderer->colormod); qfglBindTexture (GL_TEXTURE_2D, fb_texture); GL_DrawAliasFrame (vo); - if (gl_vector_light->int_val) { + if (gl_vector_light) { qfglEnable (GL_LIGHTING); if (!gl_tess) qfglEnable (GL_NORMALIZE); @@ -677,11 +686,11 @@ gl_R_DrawAliasModel (entity_t *e) qfglPopMatrix (); // torches, grenades, and lightning bolts do not have shadows - if (r_shadows->int_val && model->shadow_alpha) { - mat4_t shadow_mat; + if (r_shadows && model->shadow_alpha) { + mat4f_t shadow_mat; qfglPushMatrix (); - gl_R_RotateForEntity (e); + gl_R_RotateForEntity (Transform_GetWorldMatrixPtr (transform)); if (!gl_tess) qfglDisable (GL_NORMALIZE); @@ -690,23 +699,23 @@ gl_R_DrawAliasModel (entity_t *e) qfglDepthMask (GL_FALSE); if (gl_modelalpha < 1.0) { - VectorBlend (e->colormod, dark, 0.5, color); + VectorBlend (renderer->colormod, dark, 0.5, color); color[3] = gl_modelalpha * (model->shadow_alpha / 255.0); qfglColor4fv (color); } else { color_black[3] = model->shadow_alpha; qfglColor4ubv (color_black); } - shadevector[0] = 1; - shadevector[1] = 0; - shadevector[2] = 1; - VectorNormalize (shadevector); - Mat4Transpose (e->transform, shadow_mat); - Mat4as3MultVec (shadow_mat, shadevector, shadevector); + //FIXME fully vectorize + vec4f_t vec = { 0.707106781, 0, 0.707106781, 0 }; + Transform_GetWorldMatrix (transform, shadow_mat); + mat4ftranspose (shadow_mat, shadow_mat); + vec = m3vmulf (shadow_mat, vec); + VectorCopy (vec, shadevector); if (vo->tex_coord) - GL_DrawAliasShadowTri (paliashdr, vo); + GL_DrawAliasShadowTri (transform, paliashdr, vo); else - GL_DrawAliasShadow (paliashdr, vo); + GL_DrawAliasShadow (transform, paliashdr, vo); qfglDepthMask (GL_TRUE); qfglEnable (GL_TEXTURE_2D); @@ -722,6 +731,7 @@ gl_R_DrawAliasModel (entity_t *e) qfglDisable (GL_LIGHT0 + used_lights); } - if (!e->model->aliashdr) - Cache_Release (&e->model->cache); + if (!renderer->model->aliashdr) { + Cache_Release (&renderer->model->cache); + } } diff --git a/libs/video/renderer/gl/gl_mod_iqm.c b/libs/video/renderer/gl/gl_mod_iqm.c index 8a6efaade..f842f2f8a 100644 --- a/libs/video/renderer/gl/gl_mod_iqm.c +++ b/libs/video/renderer/gl/gl_mod_iqm.c @@ -31,9 +31,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -52,6 +49,7 @@ #include "QF/GL/qf_iqm.h" #include "QF/GL/qf_rmain.h" #include "QF/GL/qf_vid.h" +#include "QF/scene/entity.h" #include "r_internal.h" @@ -88,23 +86,25 @@ gl_draw_iqm_frame (iqm_t *iqm, gliqm_t *gl, iqmframe_t *frame, iqmmesh *mesh) } void -gl_R_DrawIQMModel (entity_t *ent) +gl_R_DrawIQMModel (entity_t ent) { - model_t *model = ent->model; + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); + model_t *model = renderer->model; iqm_t *iqm = (iqm_t *) model->aliashdr; gliqm_t *gl = (gliqm_t *) iqm->extra_data; float blend; iqmframe_t *frame; int i; - model = ent->model; - blend = R_IQMGetLerpedFrames (ent, iqm); - frame = R_IQMBlendPalette (iqm, ent->pose1, ent->pose2, blend, 0, - gl->blend_palette, gl->palette_size); + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, + ent.reg); + blend = R_IQMGetLerpedFrames (animation, iqm); + frame = R_IQMBlendPalette (iqm, animation->pose1, animation->pose2, + blend, 0, gl->blend_palette, gl->palette_size); qfglPushMatrix (); - gl_R_RotateForEntity (ent); - qfglScalef (ent->scale, ent->scale, ent->scale); + transform_t transform = Entity_Transform (ent); + gl_R_RotateForEntity (Transform_GetWorldMatrixPtr (transform)); for (i = 0; i < iqm->num_meshes; i++) { qfglBindTexture (GL_TEXTURE_2D, gl->textures[i]); diff --git a/libs/video/renderer/gl/gl_mod_sprite.c b/libs/video/renderer/gl/gl_mod_sprite.c index c5b484ed7..d0fc5b138 100644 --- a/libs/video/renderer/gl/gl_mod_sprite.c +++ b/libs/video/renderer/gl/gl_mod_sprite.c @@ -28,9 +28,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -38,13 +35,16 @@ # include #endif -#include "QF/GL/defines.h" -#include "QF/GL/funcs.h" - #include "QF/model.h" #include "QF/render.h" #include "QF/sys.h" +#include "QF/scene/entity.h" + +#include "QF/GL/defines.h" +#include "QF/GL/funcs.h" +#include "QF/GL/qf_sprite.h" + #include "compat.h" #include "r_internal.h" #include "varrays.h" @@ -53,85 +53,35 @@ static int sVAsize; static int *sVAindices; varray_t2f_c4ub_v3f_t *gl_spriteVertexArray; -void (*gl_R_DrawSpriteModel) (struct entity_s *ent); - - -static mspriteframe_t * -R_GetSpriteFrame (entity_t *currententity) -{ - float fullinterval, targettime, time; - float *pintervals; - int frame, numframes, i; - msprite_t *psprite; - mspriteframe_t *pspriteframe; - mspritegroup_t *pspritegroup; - - psprite = currententity->model->cache.data; - frame = currententity->frame; - - if ((frame >= psprite->numframes) || (frame < 0)) { - Sys_MaskPrintf (SYS_DEV, "R_DrawSprite: no such frame %d\n", frame); - frame = 0; - } - - if (psprite->frames[frame].type == SPR_SINGLE) { - pspriteframe = psprite->frames[frame].frameptr; - } else { - pspritegroup = (mspritegroup_t *) psprite->frames[frame].frameptr; - pintervals = pspritegroup->intervals; - numframes = pspritegroup->numframes; - fullinterval = pintervals[numframes - 1]; - - time = vr_data.realtime + currententity->syncbase; - - // when loading in Mod_LoadSpriteGroup, we guaranteed all interval - // values are positive, so we don't have to worry about division by 0 - targettime = time - ((int) (time / fullinterval)) * fullinterval; - - for (i = 0; i < (numframes - 1); i++) { - if (pintervals[i] > targettime) - break; - } - - pspriteframe = pspritegroup->frames[i]; - } - - return pspriteframe; -} +void (*gl_R_DrawSpriteModel) (struct entity_s ent); static void -R_DrawSpriteModel_f (entity_t *e) +R_DrawSpriteModel_f (entity_t e) { + renderer_t *renderer = Ent_GetComponent (e.id, scene_renderer, e.reg); + msprite_t *sprite = renderer->model->cache.data; float modelalpha, color[4]; - float *up, *right; - msprite_t *psprite; + vec4f_t cameravec = {}; + vec4f_t up = {}, right = {}, pn = {}; + vec4f_t origin, point; mspriteframe_t *frame; - vec3_t point, point1, point2, v_up; + + transform_t transform = Entity_Transform (e); + origin = Transform_GetWorldPosition (transform); + cameravec = r_refdef.frame.position - origin; // don't bother culling, it's just a single polygon without a surface cache - frame = R_GetSpriteFrame (e); - psprite = e->model->cache.data; + animation_t *animation = Ent_GetComponent (e.id, scene_animation, e.reg); + frame = R_GetSpriteFrame (sprite, animation); - if (psprite->type == SPR_ORIENTED) { // bullet marks on walls - up = e->transform + 2 * 4; - right = e->transform + 1 * 4; - } else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT) { - v_up[0] = 0; - v_up[1] = 0; - v_up[2] = 1; - up = v_up; - right = vright; - } else { // normal sprite - up = vup; - right = vright; - } - if (e->scale != 1.0) { - VectorScale (up, e->scale, up); - VectorScale (right, e->scale, right); + if (!R_BillboardFrame (transform, sprite->type, cameravec, + &up, &right, &pn)) { + // the orientation is undefined so can't draw the sprite + return; } - VectorCopy (e->colormod, color); - modelalpha = color[3] = e->colormod[3]; + VectorCopy (renderer->colormod, color); + modelalpha = color[3] = renderer->colormod[3]; if (modelalpha < 1.0) qfglDepthMask (GL_FALSE); @@ -141,23 +91,23 @@ R_DrawSpriteModel_f (entity_t *e) qfglColor4fv (color); - qfglTexCoord2f (0, 1); - VectorMultAdd (e->origin, frame->down, up, point1); - VectorMultAdd (point1, frame->left, right, point); - qfglVertex3fv (point); + point = origin + frame->down * up + frame->left * right; + qfglTexCoord2f (0, 1); + qfglVertex3fv ((vec_t*)&point);//FIXME + + point = origin + frame->up * up + frame->left * right; qfglTexCoord2f (0, 0); - VectorMultAdd (e->origin, frame->up, up, point2); - VectorMultAdd (point2, frame->left, right, point); - qfglVertex3fv (point); + qfglVertex3fv ((vec_t*)&point);//FIXME + + point = origin + frame->up * up + frame->right * right; qfglTexCoord2f (1, 0); - VectorMultAdd (point2, frame->right, right, point); - qfglVertex3fv (point); + qfglVertex3fv ((vec_t*)&point);//FIXME + point = origin + frame->down * up + frame->right * right; qfglTexCoord2f (1, 1); - VectorMultAdd (point1, frame->right, right, point); - qfglVertex3fv (point); + qfglVertex3fv ((vec_t*)&point);//FIXME qfglEnd (); @@ -166,63 +116,63 @@ R_DrawSpriteModel_f (entity_t *e) } static void -R_DrawSpriteModel_VA_f (entity_t *e) +R_DrawSpriteModel_VA_f (entity_t e) { + renderer_t *renderer = Ent_GetComponent (e.id, scene_renderer, e.reg); + msprite_t *psprite = renderer->model->cache.data; unsigned char modelalpha, color[4]; - float *up, *right; + vec4f_t up = {}, right = {}; + vec4f_t origin, point; int i; // unsigned int vacount; - msprite_t *psprite; mspriteframe_t *frame; - vec3_t point1, point2, v_up; varray_t2f_c4ub_v3f_t *VA; VA = gl_spriteVertexArray; // FIXME: Despair // don't bother culling, it's just a single polygon without a surface cache - frame = R_GetSpriteFrame (e); - psprite = e->model->cache.data; + animation_t *animation = Ent_GetComponent (e.id, scene_animation, e.reg); + frame = R_GetSpriteFrame (psprite, animation); qfglBindTexture (GL_TEXTURE_2D, frame->gl_texturenum); // FIXME: DESPAIR + transform_t transform = Entity_Transform (e); if (psprite->type == SPR_ORIENTED) { // bullet marks on walls - up = e->transform + 2 * 4; - right = e->transform + 1 * 4; + up = Transform_Up (transform); + right = Transform_Right (transform); } else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT) { - v_up[0] = 0; - v_up[1] = 0; - v_up[2] = 1; - up = v_up; - right = vright; + up = (vec4f_t) { 0, 0, 1, 0 }; + VectorCopy (r_refdef.frame.right, right); } else { // normal sprite - up = vup; - right = vright; - } - if (e->scale != 1.0) { - VectorScale (up, e->scale, up); - VectorScale (right, e->scale, right); + VectorCopy (r_refdef.frame.up, up); + VectorCopy (r_refdef.frame.right, right); } - for (i = 0; i < 4; i++) - color[i] = e->colormod[i] * 255; + for (i = 0; i < 4; i++) { + color[i] = renderer->colormod[i] * 255; + } memcpy (VA[0].color, color, 4); + memcpy (VA[1].color, color, 4); + memcpy (VA[2].color, color, 4); + memcpy (VA[3].color, color, 4); modelalpha = color[3]; if (modelalpha < 255) qfglDepthMask (GL_FALSE); - VectorMultAdd (e->origin, frame->down, up, point1); - VectorMultAdd (point1, frame->left, right, VA[0].vertex); + origin = Transform_GetWorldPosition (transform); - memcpy (VA[1].color, color, 4); - VectorMultAdd (e->origin, frame->up, up, point2); - VectorMultAdd (point2, frame->left, right, VA[1].vertex); + point = origin + frame->down * up + frame->left * right; + VectorCopy (point, VA[0].vertex); - memcpy (VA[2].color, color, 4); - VectorMultAdd (point2, frame->right, right, VA[2].vertex); + point = origin + frame->up * up + frame->left * right; + VectorCopy (point, VA[1].vertex); - memcpy (VA[3].color, color, 4); - VectorMultAdd (point1, frame->right, right, VA[3].vertex); + point = origin + frame->up * up + frame->right * right; + VectorCopy (point, VA[2].vertex); + + point = origin + frame->down * up + frame->right * right; + VectorCopy (point, VA[3].vertex); // VA += 4; // vacount += 4; @@ -243,7 +193,7 @@ gl_R_InitSprites (void) int i; if (r_init) { - if (gl_va_capable) { // 0 == gl_va_capable + if (gl_va_capable) { gl_R_DrawSpriteModel = R_DrawSpriteModel_VA_f; #if 0 @@ -254,7 +204,7 @@ gl_R_InitSprites (void) #else sVAsize = 4; #endif - Sys_MaskPrintf (SYS_DEV, "Sprites: %i maximum vertex elements.\n", + Sys_MaskPrintf (SYS_dev, "Sprites: %i maximum vertex elements.\n", sVAsize); if (gl_spriteVertexArray) diff --git a/libs/video/renderer/gl/gl_rmain.c b/libs/video/renderer/gl/gl_rmain.c index 1f9b30696..23f417745 100644 --- a/libs/video/renderer/gl/gl_rmain.c +++ b/libs/video/renderer/gl/gl_rmain.c @@ -28,9 +28,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -42,126 +39,31 @@ #include #include -#include "QF/cvar.h" -#include "QF/draw.h" -#include "QF/locs.h" -#include "QF/mathlib.h" -#include "QF/qargs.h" -#include "QF/render.h" -#include "QF/skin.h" -#include "QF/sound.h" -#include "QF/sys.h" -#include "QF/vid.h" +#include "QF/scene/entity.h" + #include "QF/GL/defines.h" #include "QF/GL/funcs.h" +#include "QF/GL/qf_alias.h" #include "QF/GL/qf_draw.h" #include "QF/GL/qf_iqm.h" +#include "QF/GL/qf_particles.h" #include "QF/GL/qf_rlight.h" #include "QF/GL/qf_rmain.h" #include "QF/GL/qf_rsurf.h" +#include "QF/GL/qf_sprite.h" #include "QF/GL/qf_vid.h" #include "compat.h" #include "r_internal.h" #include "varrays.h" - -int gl_c_brush_polys, gl_c_alias_polys; - -qboolean gl_envmap; // true during envmap command capture - -int gl_mirrortexturenum; // quake texturenum, not gltexturenum -qboolean gl_mirror; -plane_t *gl_mirror_plane; +#include "vid_gl.h" float gl_r_world_matrix[16]; -static float r_base_world_matrix[16]; +//FIXME static float r_base_world_matrix[16]; //vec3_t gl_shadecolor; // Ender (Extend) Colormod float gl_modelalpha; // Ender (Extend) Alpha -/* Unknown renamed to GLErr_Unknown to solve conflict with winioctl.h */ -static unsigned int GLErr_InvalidEnum; -static unsigned int GLErr_InvalidValue; -static unsigned int GLErr_InvalidOperation; -static unsigned int GLErr_OutOfMemory; -static unsigned int GLErr_StackOverflow; -static unsigned int GLErr_StackUnderflow; -static unsigned int GLErr_Unknown; - -extern void (*R_DrawSpriteModel) (struct entity_s *ent); - - -static unsigned int -R_TestErrors (unsigned int numerous) -{ - switch (qfglGetError ()) { - case GL_NO_ERROR: - return numerous; - break; - case GL_INVALID_ENUM: - GLErr_InvalidEnum++; - R_TestErrors (numerous++); - break; - case GL_INVALID_VALUE: - GLErr_InvalidValue++; - R_TestErrors (numerous++); - break; - case GL_INVALID_OPERATION: - GLErr_InvalidOperation++; - R_TestErrors (numerous++); - break; - case GL_STACK_OVERFLOW: - GLErr_StackOverflow++; - R_TestErrors (numerous++); - break; - case GL_STACK_UNDERFLOW: - GLErr_StackUnderflow++; - R_TestErrors (numerous++); - break; - case GL_OUT_OF_MEMORY: - GLErr_OutOfMemory++; - R_TestErrors (numerous++); - break; - default: - GLErr_Unknown++; - R_TestErrors (numerous++); - break; - } - - return numerous; -} - -static void -R_DisplayErrors (void) -{ - if (GLErr_InvalidEnum) - printf ("%d OpenGL errors: Invalid Enum!\n", GLErr_InvalidEnum); - if (GLErr_InvalidValue) - printf ("%d OpenGL errors: Invalid Value!\n", GLErr_InvalidValue); - if (GLErr_InvalidOperation) - printf ("%d OpenGL errors: Invalid Operation!\n", GLErr_InvalidOperation); - if (GLErr_StackOverflow) - printf ("%d OpenGL errors: Stack Overflow!\n", GLErr_StackOverflow); - if (GLErr_StackUnderflow) - printf ("%d OpenGL errors: Stack Underflow\n!", GLErr_StackUnderflow); - if (GLErr_OutOfMemory) - printf ("%d OpenGL errors: Out Of Memory!\n", GLErr_OutOfMemory); - if (GLErr_Unknown) - printf ("%d Unknown OpenGL errors!\n", GLErr_Unknown); -} - -static void -R_ClearErrors (void) -{ - GLErr_InvalidEnum = 0; - GLErr_InvalidValue = 0; - GLErr_InvalidOperation = 0; - GLErr_OutOfMemory = 0; - GLErr_StackOverflow = 0; - GLErr_StackUnderflow = 0; - GLErr_Unknown = 0; -} - void glrmain_init (void) { @@ -169,29 +71,21 @@ glrmain_init (void) gldepthmax = 1; qfglDepthFunc (GL_LEQUAL); qfglDepthRange (gldepthmin, gldepthmax); - if (gl_multitexture) - gl_multitexture_f (gl_multitexture); - if (gl_overbright) - gl_overbright_f (gl_overbright); + + gl_overbright_f (0, 0); + gl_multitexture_f (0, 0); } void -gl_R_RotateForEntity (entity_t *e) +gl_R_RotateForEntity (const vec4f_t *mat) { - qfglMultMatrixf (e->transform); + qfglMultMatrixf ((vec_t*)&mat[0]);//FIXME } -/* - R_DrawEntitiesOnList - - Draw all the entities we have information on. -*/ -static void -R_DrawEntitiesOnList (void) +void +gl_R_RenderEntities (entqueue_t *queue) { - entity_t *ent; - - if (!r_drawentities->int_val) + if (!r_drawentities) return; // LordHavoc: split into 3 loops to simplify state changes @@ -203,25 +97,22 @@ R_DrawEntitiesOnList (void) qfglDisable (GL_TEXTURE_2D); qglActiveTexture (gl_mtex_enum + 0); } - if (gl_affinemodels->int_val) + if (gl_affinemodels) qfglHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); if (gl_tess) qfglEnable (GL_PN_TRIANGLES_ATI); qfglEnable (GL_CULL_FACE); - if (gl_vector_light->int_val) { + if (gl_vector_light) { qfglEnable (GL_LIGHTING); qfglEnable (GL_NORMALIZE); } else if (gl_tess) { qfglEnable (GL_NORMALIZE); } - for (ent = r_ent_queue; ent; ent = ent->next) { - if (ent->model->type != mod_alias) - continue; - currententity = ent; - - gl_R_DrawAliasModel (currententity); + for (size_t i = 0; i < queue->ent_queues[mod_alias].size; i++) { + entity_t ent = queue->ent_queues[mod_alias].a[i]; + gl_R_DrawAliasModel (ent); } qfglColor3ubv (color_white); @@ -230,13 +121,13 @@ R_DrawEntitiesOnList (void) if (gl_tess) qfglDisable (GL_PN_TRIANGLES_ATI); - if (gl_affinemodels->int_val) + if (gl_affinemodels) qfglHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_DONT_CARE); if (gl_mtex_active_tmus >= 2) { // FIXME: Ugly, but faster than cleaning // up in every R_DrawAliasModel()! qglActiveTexture (gl_mtex_enum + 1); qfglEnable (GL_TEXTURE_2D); - if (gl_combine_capable && gl_overbright->int_val) { + if (gl_combine_capable && gl_overbright) { qfglTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); qfglTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); qfglTexEnvf (GL_TEXTURE_ENV, GL_RGB_SCALE, gl_rgb_scale); @@ -248,12 +139,9 @@ R_DrawEntitiesOnList (void) qglActiveTexture (gl_mtex_enum + 0); } - for (ent = r_ent_queue; ent; ent = ent->next) { - if (ent->model->type != mod_iqm) - continue; - currententity = ent; - - gl_R_DrawIQMModel (currententity); + for (size_t i = 0; i < queue->ent_queues[mod_iqm].size; i++) { \ + entity_t ent = queue->ent_queues[mod_iqm].a[i]; \ + gl_R_DrawIQMModel (ent); } qfglColor3ubv (color_white); @@ -261,12 +149,9 @@ R_DrawEntitiesOnList (void) qfglEnable (GL_ALPHA_TEST); if (gl_va_capable) qfglInterleavedArrays (GL_T2F_C4UB_V3F, 0, gl_spriteVertexArray); - for (ent = r_ent_queue; ent; ent = ent->next) { - if (ent->model->type != mod_sprite) - continue; - currententity = ent; - - R_DrawSpriteModel (currententity); + for (size_t i = 0; i < queue->ent_queues[mod_sprite].size; i++) { \ + entity_t ent = queue->ent_queues[mod_sprite].a[i]; \ + gl_R_DrawSpriteModel (ent); } qfglDisable (GL_ALPHA_TEST); } @@ -274,26 +159,29 @@ R_DrawEntitiesOnList (void) static void R_DrawViewModel (void) { - currententity = vr_data.view_model; + entity_t ent = vr_data.view_model; + if (!Entity_Valid (ent)) { + return; + } + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); if (vr_data.inhibit_viewmodel - || !r_drawviewmodel->int_val - || gl_envmap - || !r_drawentities->int_val - || !currententity->model) + || !r_drawviewmodel + || !r_drawentities + || !renderer->model) return; // hack the depth range to prevent view model from poking into walls qfglDepthRange (gldepthmin, gldepthmin + 0.3 * (gldepthmax - gldepthmin)); qfglEnable (GL_CULL_FACE); - if (gl_vector_light->int_val) { + if (gl_vector_light) { qfglEnable (GL_LIGHTING); qfglEnable (GL_NORMALIZE); } else if (gl_tess) { qfglEnable (GL_NORMALIZE); } - if (gl_affinemodels->int_val) + if (gl_affinemodels) qfglHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); if (gl_mtex_active_tmus >= 2) { qglActiveTexture (gl_mtex_enum + 1); @@ -303,14 +191,14 @@ R_DrawViewModel (void) qglActiveTexture (gl_mtex_enum + 0); } - gl_R_DrawAliasModel (currententity); + gl_R_DrawAliasModel (ent); qfglColor3ubv (color_white); if (gl_mtex_active_tmus >= 2) { // FIXME: Ugly, but faster than cleaning // up in every R_DrawAliasModel()! qglActiveTexture (gl_mtex_enum + 1); qfglEnable (GL_TEXTURE_2D); - if (gl_combine_capable && gl_overbright->int_val) { + if (gl_combine_capable && gl_overbright) { qfglTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); qfglTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); qfglTexEnvf (GL_TEXTURE_ENV, GL_RGB_SCALE, gl_rgb_scale); @@ -321,7 +209,7 @@ R_DrawViewModel (void) qglActiveTexture (gl_mtex_enum + 0); } - if (gl_affinemodels->int_val) + if (gl_affinemodels) qfglHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_DONT_CARE); qfglDisable (GL_NORMALIZE); @@ -332,98 +220,26 @@ R_DrawViewModel (void) qfglDepthRange (gldepthmin, gldepthmax); } -void -gl_R_SetupFrame (void) -{ - R_AnimateLight (); - R_ClearEnts (); - r_framecount++; - - gl_Fog_SetupFrame (); - - // build the transformation matrix for the given view angles - VectorCopy (r_refdef.vieworg, r_origin); - - AngleVectors (r_refdef.viewangles, vpn, vright, vup); - - R_SetFrustum (); - - // current viewleaf - r_viewleaf = Mod_PointInLeaf (r_origin, r_worldentity.model); - - r_cache_thrash = false; - - gl_c_brush_polys = 0; - gl_c_alias_polys = 0; -} - -static void -MYgluPerspective (GLdouble fovy, GLdouble aspect, GLdouble zNear, - GLdouble zFar) -{ - GLdouble xmin, xmax, ymin, ymax; - - ymax = zNear * tan (fovy * M_PI / 360.0); - ymin = -ymax; - - xmin = ymin * aspect; - xmax = -xmin; - -// printf ("glFrustum (%f, %f, %f, %f)\n", xmin, xmax, ymin, ymax); - qfglFrustum (xmin, xmax, ymin, ymax, zNear, zFar); -} - -static void -R_SetupGL_Viewport_and_Perspective (void) -{ - float screenaspect; - int x, y2, w, h; - - // set up viewpoint - qfglMatrixMode (GL_PROJECTION); - qfglLoadIdentity (); - - if (gl_envmap) { - x = y2 = 0; - w = h = 256; - } else { - x = r_refdef.vrect.x; - y2 = (vid.height - (r_refdef.vrect.y + r_refdef.vrect.height)); - - w = r_refdef.vrect.width; - h = r_refdef.vrect.height; - } -// printf ("glViewport(%d, %d, %d, %d)\n", glx + x, gly + y2, w, h); - qfglViewport (x, y2, w, h); - screenaspect = r_refdef.vrect.width * vid.aspect / r_refdef.vrect.height; - MYgluPerspective (r_refdef.fov_y, screenaspect, r_nearclip->value, - r_farclip->value); -} - static void R_SetupGL (void) { - R_SetupGL_Viewport_and_Perspective (); + qfglMatrixMode (GL_PROJECTION); + qfglLoadMatrixf ((vec_t*)&gl_ctx->projection[0]);//FIXME - if (gl_mirror) { - if (gl_mirror_plane->normal[2]) - qfglScalef (1, -1, 1); - else - qfglScalef (-1, 1, 1); - qfglFrontFace (GL_CCW); - } else - qfglFrontFace (GL_CW); + qfglFrontFace (GL_CW); qfglMatrixMode (GL_MODELVIEW); qfglLoadIdentity (); - qfglRotatef (-90, 1, 0, 0); // put Z going up - qfglRotatef (90, 0, 0, 1); // put Z going up - qfglRotatef (-r_refdef.viewangles[ROLL], 1, 0, 0); - qfglRotatef (-r_refdef.viewangles[PITCH], 0, 1, 0); - qfglRotatef (-r_refdef.viewangles[YAW], 0, 0, 1); - qfglTranslatef (-r_refdef.vieworg[0], -r_refdef.vieworg[1], - -r_refdef.vieworg[2]); + static mat4f_t z_up = { + { 0, 0, 1, 0}, + {-1, 0, 0, 0}, + { 0, 1, 0, 0}, + { 0, 0, 0, 1}, + }; + mat4f_t view; + mmulf (view, z_up, r_refdef.camera_inverse); + qfglLoadMatrixf ((vec_t*)&view[0]);//FIXME qfglGetFloatv (GL_MODELVIEW_MATRIX, gl_r_world_matrix); @@ -432,468 +248,38 @@ R_SetupGL (void) qfglDisable (GL_ALPHA_TEST); qfglAlphaFunc (GL_GREATER, 0.5); qfglEnable (GL_DEPTH_TEST); - if (gl_dlight_smooth->int_val) + if (gl_dlight_smooth) qfglShadeModel (GL_SMOOTH); else qfglShadeModel (GL_FLAT); } -static void -R_Clear (void) -{ - if (gl_clear->int_val) - qfglClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - else - qfglClear (GL_DEPTH_BUFFER_BIT); -} - -static void -R_RenderScene (void) -{ - if (r_timegraph->int_val || r_speeds->int_val || r_dspeeds->int_val) - r_time1 = Sys_DoubleTime (); - - gl_R_SetupFrame (); - R_SetupGL (); - gl_Fog_EnableGFog (); - R_MarkLeaves (); // done here so we know if we're in water - R_PushDlights (vec3_origin); - gl_R_DrawWorld (); // adds static entities to the list - S_ExtraUpdate (); // don't let sound get messed up if going slow - R_DrawEntitiesOnList (); - gl_R_RenderDlights (); - - gl_R_DrawWaterSurfaces (); - gl_R_DrawParticles (); - - gl_Fog_DisableGFog (); - - R_DrawViewModel (); - - if (R_TestErrors (0)) - R_DisplayErrors (); - R_ClearErrors (); -} - -static void -R_Mirror (void) -{ - float d; -// msurface_t *s; - -// if (!gl_mirror) // FIXME: Broken - return; - - memcpy (r_base_world_matrix, gl_r_world_matrix, - sizeof (r_base_world_matrix)); - - d = 2 * DotProduct (r_refdef.vieworg, gl_mirror_plane->normal) - - gl_mirror_plane->dist; - VectorMultSub (r_refdef.vieworg, d, gl_mirror_plane->normal, - r_refdef.vieworg); - - d = 2 * DotProduct (vpn, gl_mirror_plane->normal); - VectorMultSub (vpn, d, gl_mirror_plane->normal, vpn); - - r_refdef.viewangles[0] = -asin (vpn[2]) / M_PI * 180; - r_refdef.viewangles[1] = atan2 (vpn[1], vpn[0]) / M_PI * 180; - r_refdef.viewangles[2] = -r_refdef.viewangles[2]; - - R_EnqueueEntity (vr_data.player_entity); - - gldepthmin = 0.5; - gldepthmax = 1; - qfglDepthRange (gldepthmin, gldepthmax); - - R_RenderScene (); - gl_R_DrawWaterSurfaces (); - - gldepthmin = 0; - gldepthmax = 1; - qfglDepthRange (gldepthmin, gldepthmax); - - // blend on top - qfglMatrixMode (GL_PROJECTION); - if (gl_mirror_plane->normal[2]) - qfglScalef (1, -1, 1); - else - qfglScalef (-1, 1, 1); - qfglFrontFace (GL_CW); - qfglMatrixMode (GL_MODELVIEW); - - qfglLoadMatrixf (r_base_world_matrix); - - color_white[3] = r_mirroralpha->value * 255; - qfglColor4ubv (color_white); -#if 0//FIXME - s = r_worldentity.model->textures[gl_mirrortexturenum]->texturechain; - for (; s; s = s->texturechain) { - texture_t *tex; - - if (!s->texinfo->texture->anim_total) - tex = s->texinfo->texture; - else - tex = R_TextureAnimation (s); - -// FIXME: Needs to set the texture, the tmu, and include the header, and then -// clean up afterwards. -// if (tex->gl_fb_texturenum && gl_mtex_fullbright -// && gl_fb_models->int_val) { -// s->polys->fb_chain = fullbright_polys[tex->gl_fb_texturenum]; -// fullbright_polys[tex->gl_fb_texturenum] = s->polys; -// } - - qfglBindTexture (GL_TEXTURE_2D, tex->gl_texturenum); -// R_RenderBrushPoly (s, tex); // FIXME: Need to move R_Mirror to gl_rsurf.c, and uncommment this line! - } - r_worldentity.model->textures[gl_mirrortexturenum]->texturechain = NULL; -#endif - qfglColor3ubv (color_white); -} - -/* - R_RenderView_ - - r_refdef must be set before the first call -*/ -static void -R_RenderView_ (void) -{ - if (r_norefresh->int_val) - return; - if (!r_worldentity.model) - Sys_Error ("R_RenderView: NULL worldmodel"); - - gl_mirror = false; - - R_Clear (); - - // render normal view - R_RenderScene (); - - // render mirror view - R_Mirror (); - - if (r_timegraph->int_val) - R_TimeGraph (); - if (r_zgraph->int_val) - R_ZGraph (); -} - -// Algorithm: -// Draw up to six views, one in each direction. -// Save the picture to cube map texture, use GL_ARB_texture_cube_map. -// Create FPOLYCNTxFPOLYCNT polygons sized flat grid. -// Baseing on field of view, tie cube map texture to grid using -// translation function to map texture coordinates to fixed/regular -// grid vertices coordinates. -// Render view. Fisheye is done. - -static void R_RenderViewFishEye (void); - void gl_R_RenderView (void) { - if(!scr_fisheye->int_val) - R_RenderView_ (); - else - R_RenderViewFishEye (); -} - -#define BOX_FRONT 0 -#define BOX_RIGHT 1 -#define BOX_BEHIND 2 -#define BOX_LEFT 3 -#define BOX_TOP 4 -#define BOX_BOTTOM 5 - -#define FPOLYCNT 16 - -struct xyz { - float x, y, z; -}; - -static struct xyz FisheyeLookupTbl[FPOLYCNT + 1][FPOLYCNT + 1]; -static GLuint cube_map_tex; -static GLint gl_cube_map_size; -static GLint gl_cube_map_step; - -static const GLenum box2cube_map[] = { - GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, - GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, - GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, - GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, - GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, - GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB -}; - -static void -R_BuildFisheyeLookup (int width, int height, float fov) -{ - int x, y; - struct xyz *v; - - for (y = 0; y <= height; y += gl_cube_map_step) { - for (x = 0; x <= width; x += gl_cube_map_step) { - float dx = x - width / 2; - float dy = y - height / 2; - float yaw = sqrt (dx * dx + dy * dy) * fov / width; - float roll = atan2 (dy, dx); - // X is a first index and Y is a second, because later - // when we draw QUAD_STRIPs we need next Y vertex coordinate. - v = &FisheyeLookupTbl[x / gl_cube_map_step][y / gl_cube_map_step]; - v->x = sin (yaw) * cos (roll); - v->y = -sin (yaw) * sin (roll); - v->z = cos (yaw); - } + if (r_norefresh) { + return; } -} - -#define CHKGLERR(s) \ -do { \ - GLint err = qfglGetError(); \ - if (err != GL_NO_ERROR) \ - printf ("%s: gl error %d\n", s, (int) err); \ -} while (0); - -#define NO(x) \ -do { \ - if (x < 0) \ - x += 360; \ - else if (x >= 360) \ - x -= 360; \ -} while (0) - -static void -R_RenderCubeSide (int side) -{ - float pitch, n_pitch; - float yaw, n_yaw; - float roll, n_roll; - float s_roll; - - pitch = n_pitch = r_refdef.viewangles[PITCH]; - yaw = n_yaw = r_refdef.viewangles[YAW]; - // setting ROLL for now to 0, correct roll handling - // requre more exhaustive changes in rotation - // TODO: implement via matrix -// roll = n_roll = r_refdef.viewangles[ROLL]; - s_roll = r_refdef.viewangles[ROLL]; - roll = n_roll = 0; -// roll -= scr_fviews->int_val * 10; -// n_roll = roll; - - switch (side) { - case BOX_FRONT: - break; - case BOX_RIGHT: - n_pitch = roll; - n_yaw -= 90; - n_roll = -pitch; - break; - case BOX_LEFT: - n_pitch = -roll; - n_yaw += 90; - n_roll = pitch; -// static int f = 0; -// if (!(f++ % 100)) -// printf ("%4d %4d %4d | %4d %4d %4d\n", (int) pitch, (int) yaw, -// (int) roll, (int) n_pitch, (int) n_yaw, (int) n_roll); - break; - case BOX_TOP: - n_pitch -= 90; - break; - case BOX_BOTTOM: - n_pitch += 90; - break; - case BOX_BEHIND: - n_pitch = -pitch; - n_yaw += 180; - break; + if (!r_refdef.worldmodel) { + return; } - NO (n_pitch); - NO (n_yaw); - NO (n_roll); - r_refdef.viewangles[PITCH] = n_pitch; - r_refdef.viewangles[YAW] = n_yaw; - r_refdef.viewangles[ROLL] = n_roll; - R_RenderView_ (); - qfglEnable (GL_TEXTURE_CUBE_MAP_ARB); - qfglBindTexture (GL_TEXTURE_CUBE_MAP_ARB, cube_map_tex); - qfglCopyTexSubImage2D (box2cube_map[side], 0, 0, 0, 0, 0, - gl_cube_map_size, gl_cube_map_size); -// CHKGLERR ("qfglCopyTexSubImage2D"); - qfglDisable (GL_TEXTURE_CUBE_MAP_ARB); + R_SetupGL (); + gl_Fog_EnableGFog (); - r_refdef.viewangles[PITCH] = pitch; - r_refdef.viewangles[YAW] = yaw; - r_refdef.viewangles[ROLL] = s_roll; -} - -static qboolean gl_cube_map_capable = false; -static GLint gl_cube_map_maxtex; -static GLuint fisheye_grid; - -static int -R_InitFishEyeOnce (void) -{ - static qboolean fisheye_init_once_completed = false; - - if (fisheye_init_once_completed) - return 1; - Sys_MaskPrintf (SYS_DEV, "GL_ARB_texture_cube_map "); - if (QFGL_ExtensionPresent ("GL_ARB_texture_cube_map")) { - qfglGetIntegerv (GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB, - &gl_cube_map_maxtex); - Sys_MaskPrintf (SYS_DEV, "present, max texture size %d.\n", - (int) gl_cube_map_maxtex); - gl_cube_map_capable = true; - } else { - Sys_MaskPrintf (SYS_DEV, "not found.\n"); - gl_cube_map_capable = false; + gl_R_DrawWorld (); + gl_R_RenderDlights (); + if (Entity_Valid (vr_data.view_model)) { + R_DrawViewModel (); } - fisheye_init_once_completed = true; - return 1; -} -static int -R_InitFishEye (void) -{ - int width = vid.width; - int height = vid.height; - int fov = scr_ffov->int_val; - int views = scr_fviews->int_val; - - static int pwidth = -1; - static int pheight = -1; - static int pfov = -1; - static int pviews = -1; - - int i, x, y, min_wh, wh_changed = 0; - - if (!R_InitFishEyeOnce()) - return 0; - if (!gl_cube_map_capable) - return 0; - - // There is a problem when max texture size is bigger than - // min(width, height), it shows up as black fat stripes at the edges - // of box polygons, probably due to missing texture fragment. Try - // to play in 640x480 with gl_cube_map_size == 512. - if (pwidth != width || pheight != height) { - wh_changed = 1; - min_wh = (height < width) ? height : width; - gl_cube_map_size = gl_cube_map_maxtex; - while (gl_cube_map_size > min_wh) - gl_cube_map_size /= 2; - gl_cube_map_step = gl_cube_map_size / FPOLYCNT; - } - if (pviews != views) { - qfglEnable (GL_TEXTURE_CUBE_MAP_ARB); - if (pviews != -1) - qfglDeleteTextures (1, &cube_map_tex); - pviews = views; - qfglGenTextures (1, &cube_map_tex); - qfglBindTexture (GL_TEXTURE_CUBE_MAP_ARB, cube_map_tex); - for (i = 0; i < 6; ++i) { - qfglTexImage2D (box2cube_map[i], 0, 3, gl_cube_map_size, - gl_cube_map_size, 0, GL_RGB, GL_UNSIGNED_SHORT, - NULL); - } - qfglTexParameteri (GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, - GL_LINEAR); - qfglTexParameteri (GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, - GL_LINEAR); - qfglDisable (GL_TEXTURE_CUBE_MAP_ARB); - } - if (wh_changed || pfov != fov) { - if (pfov != -1) - qfglDeleteLists (fisheye_grid, 1); - pwidth = width; - pheight = height; - pfov = fov; - - R_BuildFisheyeLookup (gl_cube_map_size, gl_cube_map_size, - ((float) fov) * M_PI / 180.0); - - fisheye_grid = qfglGenLists (1); - qfglNewList (fisheye_grid, GL_COMPILE); - qfglLoadIdentity (); - qfglTranslatef (-gl_cube_map_size / 2, -gl_cube_map_size / 2, - -gl_cube_map_size / 2); - - qfglDisable (GL_DEPTH_TEST); - qfglFrontFace (GL_CCW); - qfglClear (GL_COLOR_BUFFER_BIT); - - qfglEnable (GL_TEXTURE_CUBE_MAP_ARB); - qfglBindTexture (GL_TEXTURE_CUBE_MAP_ARB, cube_map_tex); - for (y = 0; y < gl_cube_map_size; y += gl_cube_map_step) { - qfglBegin (GL_QUAD_STRIP); - for (x = 0; x <= gl_cube_map_size; x += gl_cube_map_step) { // quad_strip, X should be inclusive - struct xyz *v = &FisheyeLookupTbl[x / gl_cube_map_step] - [y / gl_cube_map_step + 1]; - qfglTexCoord3f (v->x, v->y, v->z); - qfglVertex2i (x, y + gl_cube_map_step); - --v; - qfglTexCoord3f (v->x, v->y, v->z); - qfglVertex2i (x, y); - } - qfglEnd (); - } - qfglDisable (GL_TEXTURE_CUBE_MAP_ARB); - qfglEnable (GL_DEPTH_TEST); - qfglEndList (); - } - return 1; -} - -static void -R_RenderViewFishEye (void) -{ - float s_fov_x, s_fov_y; - int s_vid_w, s_vid_h, s_rect_w, s_rect_h; - - if (!R_InitFishEye()) return; - - // save values - s_fov_x = r_refdef.fov_x; - s_fov_y = r_refdef.fov_y; - s_vid_w = vid.width; - s_vid_h = vid.height; - s_rect_w = r_refdef.vrect.width; - s_rect_h = r_refdef.vrect.height; - // the view should be square - r_refdef.fov_x = r_refdef.fov_y = 90; - vid.width = vid.height = - r_refdef.vrect.height = r_refdef.vrect.width = - gl_cube_map_size; - switch (scr_fviews->int_val) { - case 6: R_RenderCubeSide (BOX_BEHIND); - case 5: R_RenderCubeSide (BOX_BOTTOM); - case 4: R_RenderCubeSide (BOX_TOP); - case 3: R_RenderCubeSide (BOX_LEFT); - case 2: R_RenderCubeSide (BOX_RIGHT); - default: R_RenderCubeSide (BOX_FRONT); - } - // restore - r_refdef.fov_x = s_fov_x; - r_refdef.fov_y = s_fov_y; - vid.width = s_vid_w; - vid.height = s_vid_h; - r_refdef.vrect.width = s_rect_w; - r_refdef.vrect.height = s_rect_h; - R_SetupGL_Viewport_and_Perspective (); - qfglMatrixMode (GL_MODELVIEW); - qfglCallList (fisheye_grid); + gl_Fog_DisableGFog (); } void gl_R_ClearState (void) { - r_worldentity.model = 0; - R_ClearEfrags (); + r_refdef.worldmodel = 0; R_ClearDlights (); - gl_R_ClearParticles (); + R_ClearParticles (); } diff --git a/libs/video/renderer/gl/gl_rmisc.c b/libs/video/renderer/gl/gl_rmisc.c index a399c26bd..4c7a02e8e 100644 --- a/libs/video/renderer/gl/gl_rmisc.c +++ b/libs/video/renderer/gl/gl_rmisc.c @@ -28,9 +28,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -56,75 +53,29 @@ #include "QF/vid.h" #include "QF/GL/defines.h" #include "QF/GL/funcs.h" +#include "QF/GL/qf_draw.h" +#include "QF/GL/qf_fisheye.h" +#include "QF/GL/qf_lightmap.h" +#include "QF/GL/qf_particles.h" +#include "QF/GL/qf_rlight.h" #include "QF/GL/qf_rmain.h" #include "QF/GL/qf_rsurf.h" +#include "QF/GL/qf_sky.h" +#include "QF/GL/qf_sprite.h" #include "QF/GL/qf_textures.h" #include "QF/GL/qf_vid.h" +#include "QF/scene/entity.h" +#include "QF/scene/scene.h" + #include "mod_internal.h" #include "r_internal.h" #include "varrays.h" +#include "vid_gl.h" -/* - R_Envmap_f +static gltex_t gl_notexture = { }; - Grab six views for environment mapping tests -*/ static void -R_Envmap_f (void) -{ - byte buffer[256 * 256 * 4]; - - qfglDrawBuffer (GL_FRONT); - qfglReadBuffer (GL_FRONT); - gl_envmap = true; - - r_refdef.vrect.x = 0; - r_refdef.vrect.y = 0; - r_refdef.vrect.width = 256; - r_refdef.vrect.height = 256; - - r_refdef.viewangles[0] = 0; - r_refdef.viewangles[1] = 0; - r_refdef.viewangles[2] = 0; - gl_R_RenderView (); - qfglReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); - QFS_WriteFile ("env0.rgb", buffer, sizeof (buffer)); - - r_refdef.viewangles[1] = 90; - gl_R_RenderView (); - qfglReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); - QFS_WriteFile ("env1.rgb", buffer, sizeof (buffer)); - - r_refdef.viewangles[1] = 180; - gl_R_RenderView (); - qfglReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); - QFS_WriteFile ("env2.rgb", buffer, sizeof (buffer)); - - r_refdef.viewangles[1] = 270; - gl_R_RenderView (); - qfglReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); - QFS_WriteFile ("env3.rgb", buffer, sizeof (buffer)); - - r_refdef.viewangles[0] = -90; - r_refdef.viewangles[1] = 0; - gl_R_RenderView (); - qfglReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); - QFS_WriteFile ("env4.rgb", buffer, sizeof (buffer)); - - r_refdef.viewangles[0] = 90; - r_refdef.viewangles[1] = 0; - gl_R_RenderView (); - qfglReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); - QFS_WriteFile ("env5.rgb", buffer, sizeof (buffer)); - - gl_envmap = false; - qfglDrawBuffer (GL_BACK); - qfglReadBuffer (GL_BACK); - vid.end_rendering (); -} - -void gl_R_LoadSky_f (void) { if (Cmd_Argc () != 2) { @@ -135,44 +86,70 @@ gl_R_LoadSky_f (void) gl_R_LoadSkys (Cmd_Argv (1)); } +/* + R_TimeRefresh_f + + For program optimization + LordHavoc: improved appearance and accuracy of timerefresh +*/ +static void +gl_R_TimeRefresh_f (void) +{ +/*FIXME update for simd + double start, stop, time; + int i; + + gl_ctx->end_rendering (); + + start = Sys_DoubleTime (); + for (i = 0; i < 128; i++) { + r_refdef.viewangles[1] = i * (360.0 / 128.0); + gl_R_RenderView (); + gl_ctx->end_rendering (); + } + + stop = Sys_DoubleTime (); + time = stop - start; + Sys_Printf ("%g seconds (%g fps)\n", time, 128 / time); +*/ +} + void gl_R_Init (void) { + r_notexture_mip->render = &gl_notexture; + R_Init_Cvars (); - gl_R_Particles_Init_Cvars (); Cmd_AddCommand ("timerefresh", gl_R_TimeRefresh_f, "Tests the current refresh rate for the current location"); - Cmd_AddCommand ("envmap", R_Envmap_f, "No Description"); - Cmd_AddCommand ("pointfile", gl_R_ReadPointFile_f, - "Load a pointfile to determine map leaks"); Cmd_AddCommand ("loadsky", gl_R_LoadSky_f, "Load a skybox"); gl_Draw_Init (); + glrmain_init (); + gl_lightmap_init (); SCR_Init (); gl_R_InitBubble (); GDT_Init (); - gl_texture_number = gl_R_InitGraphTextures (gl_texture_number); - - gl_texture_number = gl_Skin_Init_Textures (gl_texture_number); + gl_R_InitGraphTextures (); + gl_Skin_Init_Textures (); r_init = 1; gl_R_InitParticles (); gl_R_InitSprites (); - gl_Fog_Init (); Skin_Init (); + gl_InitFisheye (); } static void -register_textures (model_t *model) +register_textures (mod_brush_t *brush) { - int i; texture_t *tex; - for (i = 0; i < model->numtextures; i++) { - tex = model->textures[i]; + for (unsigned i = 0; i < brush->numtextures; i++) { + tex = brush->textures[i]; if (!tex) continue; gl_R_AddTexture (tex); @@ -180,85 +157,45 @@ register_textures (model_t *model) } void -gl_R_NewMap (model_t *worldmodel, struct model_s **models, int num_models) +gl_R_NewScene (scene_t *scene) { - int i; texture_t *tex; + mod_brush_t *brush; - for (i = 0; i < 256; i++) + for (int i = 0; i < 256; i++) d_lightstylevalue[i] = 264; // normal light value - memset (&r_worldentity, 0, sizeof (r_worldentity)); - r_worldentity.model = worldmodel; - - R_FreeAllEntities (); - - // clear out efrags in case the level hasn't been reloaded - for (i = 0; i < r_worldentity.model->numleafs; i++) - r_worldentity.model->leafs[i].efrags = NULL; + r_refdef.worldmodel = scene->worldmodel; + brush = &scene->worldmodel->brush; // Force a vis update - r_viewleaf = NULL; - R_MarkLeaves (); + R_MarkLeaves (0, 0, 0, 0); - gl_R_ClearParticles (); + R_ClearParticles (); - GL_BuildLightmaps (models, num_models); + GL_BuildLightmaps (scene->models, scene->num_models); // identify sky texture - gl_mirrortexturenum = -1; gl_R_ClearTextures (); - for (i = 0; i < r_worldentity.model->numtextures; i++) { - tex = r_worldentity.model->textures[i]; + for (unsigned i = 0; i < brush->numtextures; i++) { + tex = brush->textures[i]; if (!tex) continue; if (!strncmp (tex->name, "sky", 3)) { gl_R_InitSky (tex); } - if (!strncmp (tex->name, "window02_1", 10)) - gl_mirrortexturenum = i; } - gl_R_InitSurfaceChains (r_worldentity.model); + gl_R_InitSurfaceChains (brush); gl_R_AddTexture (r_notexture_mip); - register_textures (r_worldentity.model); - for (i = 0; i < num_models; i++) { - if (!models[i]) + register_textures (brush); + for (int i = 0; i < scene->num_models; i++) { + if (!scene->models[i]) continue; - if (*models[i]->name == '*') + if (*scene->models[i]->path == '*') continue; - if (models[i] != r_worldentity.model && models[i]->type == mod_brush) - register_textures (models[i]); + if (scene->models[i] != r_refdef.worldmodel + && scene->models[i]->type == mod_brush) + register_textures (&scene->models[i]->brush); } } - -void -gl_R_ViewChanged (float aspect) -{ -} - -/* - R_TimeRefresh_f - - For program optimization - LordHavoc: improved appearance and accuracy of timerefresh -*/ -void -gl_R_TimeRefresh_f (void) -{ - double start, stop, time; - int i; - - vid.end_rendering (); - - start = Sys_DoubleTime (); - for (i = 0; i < 128; i++) { - r_refdef.viewangles[1] = i * (360.0 / 128.0); - gl_R_RenderView (); - vid.end_rendering (); - } - - stop = Sys_DoubleTime (); - time = stop - start; - Sys_Printf ("%g seconds (%g fps)\n", time, 128 / time); -} diff --git a/libs/video/renderer/gl/gl_rsurf.c b/libs/video/renderer/gl/gl_rsurf.c index 3cfb3c49b..4f34a10ca 100644 --- a/libs/video/renderer/gl/gl_rsurf.c +++ b/libs/video/renderer/gl/gl_rsurf.c @@ -29,9 +29,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -47,6 +44,9 @@ #include "QF/cvar.h" #include "QF/render.h" #include "QF/sys.h" + +#include "QF/scene/entity.h" + #include "QF/GL/defines.h" #include "QF/GL/funcs.h" #include "QF/GL/qf_lightmap.h" @@ -58,34 +58,44 @@ #include "compat.h" #include "r_internal.h" +#include "vid_gl.h" static instsurf_t *waterchain = NULL; static instsurf_t **waterchain_tail = &waterchain; static instsurf_t *sky_chain; static instsurf_t **sky_chain_tail; +typedef struct glbspctx_s { + mod_brush_t *brush; + animation_t *animation; + vec4f_t *transform; + float *color; +} glbspctx_t; + #define CHAIN_SURF_F2B(surf,chain) \ - do { \ + ({ \ instsurf_t *inst = (surf)->instsurf; \ if (__builtin_expect(!inst, 1)) \ - (surf)->tinst = inst = get_instsurf (); \ + inst = get_instsurf (); \ inst->surface = (surf); \ *(chain##_tail) = inst; \ (chain##_tail) = &inst->tex_chain; \ *(chain##_tail) = 0; \ - } while (0) + inst; \ + }) #define CHAIN_SURF_B2F(surf,chain) \ - do { \ + ({ \ instsurf_t *inst = (surf)->instsurf; \ if (__builtin_expect(!inst, 1)) \ - (surf)->tinst = inst = get_instsurf (); \ + inst = get_instsurf (); \ inst->surface = (surf); \ inst->tex_chain = (chain); \ (chain) = inst; \ - } while (0) + inst; \ + }) -static texture_t **r_texture_chains; +static gltex_t **r_texture_chains; static int r_num_texture_chains; static int max_texture_chains; @@ -129,31 +139,30 @@ release_instsurfs (void) } void -gl_R_AddTexture (texture_t *tex) +gl_R_AddTexture (texture_t *tx) { int i; if (r_num_texture_chains == max_texture_chains) { max_texture_chains += 64; r_texture_chains = realloc (r_texture_chains, - max_texture_chains * sizeof (texture_t *)); + max_texture_chains * sizeof (gltex_t *)); for (i = r_num_texture_chains; i < max_texture_chains; i++) r_texture_chains[i] = 0; } + gltex_t *tex = tx->render; r_texture_chains[r_num_texture_chains++] = tex; tex->tex_chain = NULL; tex->tex_chain_tail = &tex->tex_chain; } void -gl_R_InitSurfaceChains (model_t *model) +gl_R_InitSurfaceChains (mod_brush_t *brush) { - int i; - if (static_chains) free (static_chains); - static_chains = calloc (model->nummodelsurfaces, sizeof (instsurf_t)); - for (i = 0; i < model->nummodelsurfaces; i++) - model->surfaces[i].instsurf = static_chains + i; + static_chains = calloc (brush->nummodelsurfaces, sizeof (instsurf_t)); + for (unsigned i = 0; i < brush->nummodelsurfaces; i++) + brush->surfaces[i].instsurf = static_chains + i; release_instsurfs (); } @@ -174,7 +183,7 @@ R_RenderFullbrights (void) int i, j; glpoly_t *p; instsurf_t *sc; - texture_t *tex; + gltex_t *tex; for (i = 0; i < r_num_texture_chains; i++) { if (!(tex = r_texture_chains[i]) || !tex->gl_fb_texturenum) @@ -183,7 +192,7 @@ R_RenderFullbrights (void) for (sc = tex->tex_chain; sc; sc = sc->tex_chain) { if (sc->transform) { qfglPushMatrix (); - qfglLoadMatrixf (sc->transform); + qfglLoadMatrixf ((vec_t*)&sc->transform[0]);//FIXME } if (sc->color) qfglColor4fv (sc->color); @@ -206,17 +215,17 @@ R_RenderFullbrights (void) } static inline void -R_RenderBrushPoly_3 (msurface_t *fa) +R_RenderBrushPoly_3 (msurface_t *surf) { float *v; int i; - gl_c_brush_polys++; + gl_ctx->brush_polys++; qfglBegin (GL_POLYGON); - v = fa->polys->verts[0]; + v = surf->polys->verts[0]; - for (i = 0; i < fa->polys->numverts; i++, v += VERTEXSIZE) { + for (i = 0; i < surf->polys->numverts; i++, v += VERTEXSIZE) { qglMultiTexCoord2fv (gl_mtex_enum + 0, &v[3]); qglMultiTexCoord2fv (gl_mtex_enum + 1, &v[5]); qglMultiTexCoord2fv (gl_mtex_enum + 2, &v[3]); @@ -227,17 +236,17 @@ R_RenderBrushPoly_3 (msurface_t *fa) } static inline void -R_RenderBrushPoly_2 (msurface_t *fa) +R_RenderBrushPoly_2 (msurface_t *surf) { float *v; int i; - gl_c_brush_polys++; + gl_ctx->brush_polys++; qfglBegin (GL_POLYGON); - v = fa->polys->verts[0]; + v = surf->polys->verts[0]; - for (i = 0; i < fa->polys->numverts; i++, v += VERTEXSIZE) { + for (i = 0; i < surf->polys->numverts; i++, v += VERTEXSIZE) { qglMultiTexCoord2fv (gl_mtex_enum + 0, &v[3]); qglMultiTexCoord2fv (gl_mtex_enum + 1, &v[5]); qfglVertex3fv (v); @@ -247,17 +256,17 @@ R_RenderBrushPoly_2 (msurface_t *fa) } static inline void -R_RenderBrushPoly_1 (msurface_t *fa) +R_RenderBrushPoly_1 (msurface_t *surf) { float *v; int i; - gl_c_brush_polys++; + gl_ctx->brush_polys++; qfglBegin (GL_POLYGON); - v = fa->polys->verts[0]; + v = surf->polys->verts[0]; - for (i = 0; i < fa->polys->numverts; i++, v += VERTEXSIZE) { + for (i = 0; i < surf->polys->numverts; i++, v += VERTEXSIZE) { qfglTexCoord2fv (&v[3]); qfglVertex3fv (v); } @@ -266,46 +275,23 @@ R_RenderBrushPoly_1 (msurface_t *fa) } static inline void -R_AddToLightmapChain (msurface_t *fa) +R_AddToLightmapChain (glbspctx_t *bctx, msurface_t *surf, instsurf_t *sc) { - int maps, smax, tmax; - glRect_t *theRect; - instsurf_t *sc; + int maps; // add the poly to the proper lightmap chain - if (!(sc = fa->instsurf)) - sc = fa->tinst; - - sc->lm_chain = gl_lightmap_polys[fa->lightmaptexturenum]; - gl_lightmap_polys[fa->lightmaptexturenum] = sc; + sc->lm_chain = gl_lightmap_polys; + gl_lightmap_polys = sc; // check for lightmap modification - for (maps = 0; maps < MAXLIGHTMAPS && fa->styles[maps] != 255; maps++) - if (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps]) + for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) + if (d_lightstylevalue[surf->styles[maps]] != surf->cached_light[maps]) goto dynamic; - if ((fa->dlightframe == r_framecount) || fa->cached_dlight) { + if ((surf->dlightframe == r_framecount) || surf->cached_dlight) { dynamic: - if (r_dynamic->int_val) { - gl_lightmap_modified[fa->lightmaptexturenum] = true; - theRect = &gl_lightmap_rectchange[fa->lightmaptexturenum]; - if (fa->light_t < theRect->t) { - if (theRect->h) - theRect->h += theRect->t - fa->light_t; - theRect->t = fa->light_t; - } - if (fa->light_s < theRect->l) { - if (theRect->w) - theRect->w += theRect->l - fa->light_s; - theRect->l = fa->light_s; - } - smax = (fa->extents[0] >> 4) + 1; - tmax = (fa->extents[1] >> 4) + 1; - if ((theRect->w + theRect->l) < (fa->light_s + smax)) - theRect->w = (fa->light_s - theRect->l) + smax; - if ((theRect->h + theRect->t) < (fa->light_t + tmax)) - theRect->h = (fa->light_t - theRect->t) + tmax; - gl_R_BuildLightMap (fa); + if (r_dynamic) { + gl_R_BuildLightMap (bctx->transform, bctx->brush, surf); } } } @@ -315,15 +301,12 @@ gl_R_DrawWaterSurfaces (void) { int i; instsurf_t *s; - msurface_t *fa; - float wateralpha = max (vr_data.min_wateralpha, r_wateralpha->value); + msurface_t *surf; + float wateralpha = max (vr_data.min_wateralpha, r_wateralpha); if (!waterchain) return; - // go back to the world matrix - qfglLoadMatrixf (gl_r_world_matrix); - if (wateralpha < 1.0) { qfglDepthMask (GL_FALSE); color_white[3] = wateralpha * 255; @@ -332,18 +315,22 @@ gl_R_DrawWaterSurfaces (void) i = -1; for (s = waterchain; s; s = s->tex_chain) { - fa = s->surface; - if (s->transform) - qfglLoadMatrixf (s->transform); - else - qfglLoadMatrixf (gl_r_world_matrix); - if (i != fa->texinfo->texture->gl_texturenum) { - i = fa->texinfo->texture->gl_texturenum; + gltex_t *tex; + surf = s->surface; + if (s->transform) { + qfglPushMatrix (); + qfglLoadMatrixf ((vec_t*)&s->transform[0]);//FIXME + } + tex = surf->texinfo->texture->render; + if (i != tex->gl_texturenum) { + i = tex->gl_texturenum; qfglBindTexture (GL_TEXTURE_2D, i); } - GL_EmitWaterPolys (fa); + GL_EmitWaterPolys (surf); + if (s->transform) { + qfglPopMatrix (); + } } - qfglLoadMatrixf (gl_r_world_matrix); waterchain = NULL; waterchain_tail = &waterchain; @@ -359,13 +346,14 @@ DrawTextureChains (int disable_blend, int do_bind) { int i; instsurf_t *s; - msurface_t *fa; - texture_t *tex; + msurface_t *surf; + gltex_t *tex; if (gl_mtex_active_tmus >= 2) { // Lightmaps qglActiveTexture (gl_mtex_enum + 1); qfglEnable (GL_TEXTURE_2D); + qfglBindTexture (GL_TEXTURE_2D, gl_R_LightmapTexture ()); // Base Texture qglActiveTexture (gl_mtex_enum + 0); @@ -382,19 +370,16 @@ DrawTextureChains (int disable_blend, int do_bind) qfglEnable (GL_TEXTURE_2D); qfglBindTexture (GL_TEXTURE_2D, tex->gl_fb_texturenum); - qglActiveTexture (gl_mtex_enum + 1); for (s = tex->tex_chain; s; s = s->tex_chain) { - fa = s->surface; + surf = s->surface; if (s->transform) { qfglPushMatrix (); - qfglLoadMatrixf (s->transform); + qfglLoadMatrixf ((vec_t*)&s->transform[0]);//FIXME } if (s->color && do_bind) qfglColor4fv (s->color); - qfglBindTexture (GL_TEXTURE_2D, gl_lightmap_textures + - fa->lightmaptexturenum); - R_RenderBrushPoly_3 (fa); + R_RenderBrushPoly_3 (surf); if (s->transform) qfglPopMatrix (); @@ -407,19 +392,16 @@ DrawTextureChains (int disable_blend, int do_bind) qglActiveTexture (gl_mtex_enum + 0); } else { - qglActiveTexture (gl_mtex_enum + 1); for (s = tex->tex_chain; s; s = s->tex_chain) { - fa = s->surface; - qfglBindTexture (GL_TEXTURE_2D, gl_lightmap_textures + - fa->lightmaptexturenum); + surf = s->surface; if (s->transform) { qfglPushMatrix (); - qfglLoadMatrixf (s->transform); + qfglLoadMatrixf ((vec_t*)&s->transform[0]);//FIXME } if (s->color && do_bind) qfglColor4fv (s->color); - R_RenderBrushPoly_2 (fa); + R_RenderBrushPoly_2 (surf); if (s->transform) qfglPopMatrix (); @@ -450,7 +432,7 @@ DrawTextureChains (int disable_blend, int do_bind) for (s = tex->tex_chain; s; s = s->tex_chain) { if (s->transform) { qfglPushMatrix (); - qfglLoadMatrixf (s->transform); + qfglLoadMatrixf ((vec_t*)&s->transform[0]);//FIXME } R_RenderBrushPoly_1 (s->surface); @@ -469,7 +451,7 @@ static void clear_texture_chains (void) { int i; - texture_t *tex; + gltex_t *tex; for (i = 0; i < r_num_texture_chains; i++) { tex = r_texture_chains[i]; @@ -478,120 +460,127 @@ clear_texture_chains (void) tex->tex_chain = NULL; tex->tex_chain_tail = &tex->tex_chain; } - tex = r_notexture_mip; + tex = r_notexture_mip->render; tex->tex_chain = NULL; tex->tex_chain_tail = &tex->tex_chain; release_instsurfs (); - memset (gl_lightmap_polys, 0, sizeof (gl_lightmap_polys)); + gl_lightmap_polys = 0; } static inline void -chain_surface (msurface_t *surf, vec_t *transform, float *color) +chain_surface (glbspctx_t *bctx, msurface_t *surf) { instsurf_t *sc; if (surf->flags & SURF_DRAWTURB) { - CHAIN_SURF_B2F (surf, waterchain); + sc = CHAIN_SURF_B2F (surf, waterchain); } else if (surf->flags & SURF_DRAWSKY) { - CHAIN_SURF_F2B (surf, sky_chain); + sc = CHAIN_SURF_F2B (surf, sky_chain); } else { - texture_t *tex; + texture_t *tx; + gltex_t *tex; if (!surf->texinfo->texture->anim_total) - tex = surf->texinfo->texture; + tx = surf->texinfo->texture; else - tex = R_TextureAnimation (surf); - CHAIN_SURF_F2B (surf, tex->tex_chain); + tx = R_TextureAnimation (bctx->animation->frame, surf); + tex = tx->render; + sc = CHAIN_SURF_F2B (surf, tex->tex_chain); - R_AddToLightmapChain (surf); + R_AddToLightmapChain (bctx, surf, sc); } - if (!(sc = surf->instsurf)) - sc = surf->tinst; - sc->transform = transform; - sc->color = color; + sc->transform = bctx->transform; + sc->color = bctx->color; } void -gl_R_DrawBrushModel (entity_t *e) +gl_R_DrawBrushModel (entity_t e) { float dot, radius; - int i; - unsigned int k; - model_t *model; - plane_t *pplane; - msurface_t *psurf; - qboolean rotated; + transform_t transform = Entity_Transform (e); + msurface_t *surf; + bool rotated; vec3_t mins, maxs; + mat4f_t worldMatrix; + renderer_t *renderer = Ent_GetComponent (e.id, scene_renderer, e.reg); + model_t *model = renderer->model; + mod_brush_t *brush = &model->brush; + glbspctx_t bspctx = { + brush, + Ent_GetComponent (e.id, scene_animation, e.reg), + renderer->full_transform, + renderer->colormod, + }; - model = e->model; - - if (e->transform[0] != 1 || e->transform[5] != 1 || e->transform[10] != 1) { + Transform_GetWorldMatrix (transform, worldMatrix); + if (worldMatrix[0][0] != 1 || worldMatrix[1][1] != 1 + || worldMatrix[2][2] != 1) { rotated = true; radius = model->radius; #if 0 //QSG FIXME if (e->scale != 1.0) radius *= e->scale; #endif - if (R_CullSphere (e->origin, radius)) + if (R_CullSphere (r_refdef.frustum, (vec_t*)&worldMatrix[3], radius)) {//FIXME return; + } } else { rotated = false; - VectorAdd (e->origin, model->mins, mins); - VectorAdd (e->origin, model->maxs, maxs); + VectorAdd (worldMatrix[3], model->mins, mins); + VectorAdd (worldMatrix[3], model->maxs, maxs); #if 0 // QSG FIXME if (e->scale != 1.0) { VectorScale (mins, e->scale, mins); VectorScale (maxs, e->scale, maxs); } #endif - if (R_CullBox (mins, maxs)) + if (R_CullBox (r_refdef.frustum, mins, maxs)) return; } - VectorSubtract (r_refdef.vieworg, e->origin, modelorg); + vec4f_t relviewpos = r_refdef.frame.position - worldMatrix[3]; if (rotated) { - vec3_t temp; + vec4f_t temp = relviewpos; - VectorCopy (modelorg, temp); - modelorg[0] = DotProduct (temp, e->transform + 0); - modelorg[1] = DotProduct (temp, e->transform + 4); - modelorg[2] = DotProduct (temp, e->transform + 8); + relviewpos[0] = dotf (temp, worldMatrix[0])[0]; + relviewpos[1] = dotf (temp, worldMatrix[1])[0]; + relviewpos[2] = dotf (temp, worldMatrix[2])[0]; } // calculate dynamic lighting for bmodel if it's not an instanced model - if (model->firstmodelsurface != 0 && r_dlight_lightmap->int_val) { - vec3_t lightorigin; - - for (k = 0; k < r_maxdlights; k++) { + if (brush->firstmodelsurface != 0 && r_dlight_lightmap) { + for (unsigned k = 0; k < r_maxdlights; k++) { if ((r_dlights[k].die < vr_data.realtime) || (!r_dlights[k].radius)) continue; - VectorSubtract (r_dlights[k].origin, e->origin, lightorigin); - R_RecursiveMarkLights (lightorigin, &r_dlights[k], k, - model->nodes + model->hulls[0].firstclipnode); + vec4f_t lightorigin; + VectorSubtract (r_dlights[k].origin, worldMatrix[3], lightorigin); + lightorigin[3] = 1; + R_RecursiveMarkLights (brush, lightorigin, &r_dlights[k], k, + brush->hulls[0].firstclipnode); } } qfglPushMatrix (); - gl_R_RotateForEntity (e); - qfglGetFloatv (GL_MODELVIEW_MATRIX, e->full_transform); + gl_R_RotateForEntity (Transform_GetWorldMatrixPtr (transform)); + qfglGetFloatv (GL_MODELVIEW_MATRIX, (vec_t*)&renderer->full_transform[0]); qfglPopMatrix (); - psurf = &model->surfaces[model->firstmodelsurface]; + surf = &brush->surfaces[brush->firstmodelsurface]; // draw texture - for (i = 0; i < model->nummodelsurfaces; i++, psurf++) { + for (unsigned i = 0; i < brush->nummodelsurfaces; i++, surf++) { // find which side of the node we are on - pplane = psurf->plane; + plane_t *plane = surf->plane; - dot = DotProduct (modelorg, pplane->normal) - pplane->dist; + dot = DotProduct (relviewpos, plane->normal) - plane->dist; // draw the polygon - if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || - (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { - chain_surface (psurf, e->full_transform, e->colormod); + if (((surf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || + (!(surf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { + chain_surface (&bspctx, surf); } } } @@ -610,15 +599,13 @@ static inline int get_side (mnode_t *node) { // find which side of the node we are on - plane_t *plane = node->plane; + vec4f_t org = r_refdef.frame.position; - if (plane->type < 3) - return (modelorg[plane->type] - plane->dist) < 0; - return (DotProduct (modelorg, plane->normal) - plane->dist) < 0; + return dotf (org, node->plane)[0] < 0; } static inline void -visit_node (mnode_t *node, int side) +visit_node (glbspctx_t *bctx, mnode_t *node, int side) { int c; msurface_t *surf; @@ -627,118 +614,123 @@ visit_node (mnode_t *node, int side) side = (~side + 1) & SURF_PLANEBACK; // draw stuff if ((c = node->numsurfaces)) { - surf = r_worldentity.model->surfaces + node->firstsurface; - for (; c; c--, surf++) { - if (surf->visframe != r_visframecount) + int surf_id = node->firstsurface; + surf = bctx->brush->surfaces + surf_id; + for (; c; c--, surf++, surf_id++) { + if (r_face_visframes[surf_id] != r_visframecount) continue; // side is either 0 or SURF_PLANEBACK if (side ^ (surf->flags & SURF_PLANEBACK)) continue; // wrong side - chain_surface (surf, 0, 0); + chain_surface (bctx, surf); } } } static inline int -test_node (mnode_t *node) +test_node (glbspctx_t *bctx, int node_id) { - if (node->contents < 0) + if (node_id < 0) return 0; - if (node->visframe != r_visframecount) + if (r_node_visframes[node_id] != r_visframecount) return 0; - if (R_CullBox (node->minmaxs, node->minmaxs + 3)) + mnode_t *node = bctx->brush->nodes + node_id; + if (R_CullBox (r_refdef.frustum, node->minmaxs, node->minmaxs + 3)) return 0; return 1; } static void -R_VisitWorldNodes (model_t *model) +R_VisitWorldNodes (glbspctx_t *bctx) { typedef struct { - mnode_t *node; + int node_id; int side; } rstack_t; + mod_brush_t *brush = bctx->brush; rstack_t *node_ptr; rstack_t *node_stack; - mnode_t *node; - mnode_t *front; + int node_id; + int front; int side; - node = model->nodes; + node_id = 0; // +2 for paranoia - node_stack = alloca ((model->depth + 2) * sizeof (rstack_t)); + node_stack = alloca ((brush->depth + 2) * sizeof (rstack_t)); node_ptr = node_stack; while (1) { - while (test_node (node)) { + while (test_node (bctx, node_id)) { + mnode_t *node = bctx->brush->nodes + node_id; side = get_side (node); front = node->children[side]; - if (test_node (front)) { - node_ptr->node = node; + if (test_node (bctx, front)) { + node_ptr->node_id = node_id; node_ptr->side = side; node_ptr++; - node = front; + node_id = front; continue; } - if (front->contents < 0 && front->contents != CONTENTS_SOLID) - visit_leaf ((mleaf_t *) front); - visit_node (node, side); - node = node->children[!side]; + if (front < 0) { + mleaf_t *leaf = bctx->brush->leafs + ~front; + if (leaf->contents != CONTENTS_SOLID) { + visit_leaf (leaf); + } + } + visit_node (bctx, node, side); + node_id = node->children[side ^ 1]; + } + if (node_id < 0) { + mleaf_t *leaf = bctx->brush->leafs + ~node_id; + if (leaf->contents != CONTENTS_SOLID) { + visit_leaf (leaf); + } } - if (node->contents < 0 && node->contents != CONTENTS_SOLID) - visit_leaf ((mleaf_t *) node); if (node_ptr != node_stack) { node_ptr--; - node = node_ptr->node; + node_id = node_ptr->node_id; + mnode_t *node = bctx->brush->nodes + node_id; side = node_ptr->side; - visit_node (node, side); - node = node->children[!side]; + visit_node (bctx, node, side); + node_id = node->children[side ^ 1]; continue; } break; } - if (node->contents < 0 && node->contents != CONTENTS_SOLID) - visit_leaf ((mleaf_t *) node); } void gl_R_DrawWorld (void) { - entity_t worldent; - - memset (&worldent, 0, sizeof (worldent)); - worldent.model = r_worldentity.model; - - VectorCopy (r_refdef.vieworg, modelorg); - - currententity = &worldent; + animation_t animation = {}; + glbspctx_t bctx = { }; sky_chain = 0; sky_chain_tail = &sky_chain; - if (!gl_sky_clip->int_val) { + if (!gl_sky_clip) { gl_R_DrawSky (); } - R_VisitWorldNodes (r_worldentity.model); - if (r_drawentities->int_val) { - entity_t *ent; - for (ent = r_ent_queue; ent; ent = ent->next) { - if (ent->model->type != mod_brush) - continue; - currententity = ent; + bctx.brush = &r_refdef.worldmodel->brush; + bctx.animation = &animation; - gl_R_DrawBrushModel (currententity); - } - } - - gl_R_CalcLightmaps (); + R_VisitWorldNodes (&bctx); gl_R_DrawSkyChain (sky_chain); - if (!gl_Fog_GetDensity () - || (gl_fb_bmodels->int_val && gl_mtex_fullbright) + if (r_drawentities) { + for (size_t i = 0; i < r_ent_queue->ent_queues[mod_brush].size; i++) { \ + entity_t ent = r_ent_queue->ent_queues[mod_brush].a[i]; \ + gl_R_DrawBrushModel (ent); + } + } + + gl_R_FlushLightmaps (); + + if (!Fog_GetDensity () + || (gl_fb_bmodels && gl_mtex_fullbright) || gl_mtex_active_tmus > 1) { // we have enough active TMUs to render everything in one go // or we're not doing fog @@ -747,7 +739,7 @@ gl_R_DrawWorld (void) if (gl_mtex_active_tmus <= 1) gl_R_BlendLightmaps (); - if (gl_fb_bmodels->int_val && !gl_mtex_fullbright) + if (gl_fb_bmodels && !gl_mtex_fullbright) R_RenderFullbrights (); } else { if (gl_mtex_active_tmus > 1) { @@ -807,65 +799,63 @@ gl_R_DrawWorld (void) model_t *gl_currentmodel; void -GL_BuildSurfaceDisplayList (msurface_t *fa) +GL_BuildSurfaceDisplayList (mod_brush_t *brush, msurface_t *surf) { float s, t; float *vec; int lindex, lnumverts, i; glpoly_t *poly; medge_t *pedges, *r_pedge; + mvertex_t *vertex_base = brush->vertexes; // reconstruct the polygon - pedges = gl_currentmodel->edges; - lnumverts = fa->numedges; + pedges = gl_currentmodel->brush.edges; + lnumverts = surf->numedges; // draw texture - poly = Hunk_Alloc (sizeof (glpoly_t) + (lnumverts - 4) * + poly = Hunk_Alloc (0, sizeof (glpoly_t) + (lnumverts - 4) * VERTEXSIZE * sizeof (float)); - poly->next = fa->polys; - poly->flags = fa->flags; - fa->polys = poly; + poly->next = surf->polys; + poly->flags = surf->flags; + surf->polys = poly; poly->numverts = lnumverts; + mtexinfo_t *texinfo = surf->texinfo; for (i = 0; i < lnumverts; i++) { - lindex = gl_currentmodel->surfedges[fa->firstedge + i]; + lindex = gl_currentmodel->brush.surfedges[surf->firstedge + i]; if (lindex > 0) { r_pedge = &pedges[lindex]; - vec = r_pcurrentvertbase[r_pedge->v[0]].position; + vec = vertex_base[r_pedge->v[0]].position; } else { r_pedge = &pedges[-lindex]; - vec = r_pcurrentvertbase[r_pedge->v[1]].position; + vec = vertex_base[r_pedge->v[1]].position; } - s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; - s /= fa->texinfo->texture->width; + s = DotProduct (vec, texinfo->vecs[0]) + texinfo->vecs[0][3]; + s /= texinfo->texture->width; - t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; - t /= fa->texinfo->texture->height; + t = DotProduct (vec, texinfo->vecs[1]) + texinfo->vecs[1][3]; + t /= texinfo->texture->height; VectorCopy (vec, poly->verts[i]); poly->verts[i][3] = s; poly->verts[i][4] = t; // lightmap texture coordinates - s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; - s -= fa->texturemins[0]; - s += fa->light_s * 16; - s += 8; - s /= BLOCK_WIDTH * 16; // fa->texinfo->texture->width; - - t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; - t -= fa->texturemins[1]; - t += fa->light_t * 16; - t += 8; - t /= BLOCK_HEIGHT * 16; // fa->texinfo->texture->height; - - poly->verts[i][5] = s; - poly->verts[i][6] = t; + s = DotProduct (vec, texinfo->vecs[0]) + texinfo->vecs[0][3]; + t = DotProduct (vec, texinfo->vecs[1]) + texinfo->vecs[1][3]; + s -= surf->texturemins[0]; + t -= surf->texturemins[1]; + s += surf->lightpic->rect->x * 16 + 8; + t += surf->lightpic->rect->y * 16 + 8; + s /= 16; + t /= 16; + poly->verts[i][5] = s * surf->lightpic->size; + poly->verts[i][6] = t * surf->lightpic->size; } // remove co-linear points - Ed - if (!gl_keeptjunctions->int_val && !(fa->flags & SURF_UNDERWATER)) { + if (!gl_keeptjunctions) { for (i = 0; i < lnumverts; ++i) { vec3_t v1, v2; float *prev, *this, *next; diff --git a/libs/video/renderer/gl/gl_screen.c b/libs/video/renderer/gl/gl_screen.c deleted file mode 100644 index d463880d8..000000000 --- a/libs/video/renderer/gl/gl_screen.c +++ /dev/null @@ -1,269 +0,0 @@ -/* - gl_screen.c - - master for refresh, status bar, console, chat, notify, etc - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif - -#include - -#include "QF/cvar.h" -#include "QF/draw.h" -#include "QF/dstring.h" -#include "QF/image.h" -#include "QF/quakefs.h" -#include "QF/render.h" -#include "QF/screen.h" -#include "QF/sys.h" -#include "QF/tga.h" -#include "QF/va.h" -#include "QF/GL/defines.h" -#include "QF/GL/funcs.h" -#include "QF/GL/qf_draw.h" -#include "QF/GL/qf_rmain.h" -#include "QF/GL/qf_vid.h" - -#include "compat.h" -#include "r_internal.h" -#include "sbar.h" - -/* SCREEN SHOTS */ - -tex_t * -gl_SCR_CaptureBGR (void) -{ - int count; - tex_t *tex; - - count = vid.width * vid.height; - tex = malloc (field_offset (tex_t, data[count * 3])); - SYS_CHECKMEM (tex); - tex->width = vid.width; - tex->height = vid.height; - tex->format = tex_rgb; - tex->palette = 0; - qfglReadPixels (0, 0, tex->width, tex->height, GL_BGR_EXT, - GL_UNSIGNED_BYTE, tex->data); - return tex; -} - -tex_t * -gl_SCR_ScreenShot (int width, int height) -{ - unsigned char *src, *dest, *snap; - float fracw, frach; - int count, dex, dey, dx, dy, nx, r, g, b, x, y, w, h; - tex_t *tex; - - snap = Hunk_TempAlloc (vid.width * vid.height * 3); - - qfglReadPixels (0, 0, vid.width, vid.height, GL_RGB, GL_UNSIGNED_BYTE, - snap); - - w = (vid.width < width) ? vid.width : width; - h = (vid.height < height) ? vid.height : height; - - fracw = (float) vid.width / (float) w; - frach = (float) vid.height / (float) h; - - tex = malloc (field_offset (tex_t, data[w * h])); - if (!tex) - return 0; - - tex->width = w; - tex->height = h; - tex->palette = vid.palette; - - for (y = 0; y < h; y++) { - dest = tex->data + (w * y); - - for (x = 0; x < w; x++) { - r = g = b = 0; - - dx = x * fracw; - dex = (x + 1) * fracw; - if (dex == dx) - dex++; // at least one - dy = y * frach; - dey = (y + 1) * frach; - if (dey == dy) - dey++; // at least one - - count = 0; - for (; dy < dey; dy++) { - src = snap + (vid.width * 3 * dy) + dx * 3; - for (nx = dx; nx < dex; nx++) { - r += *src++; - g += *src++; - b += *src++; - count++; - } - } - r /= count; - g /= count; - b /= count; - *dest++ = MipColor (r, g, b); - } - } - - return tex; -} - -void -gl_SCR_ScreenShot_f (void) -{ - dstring_t *pcxname = dstring_new (); - - // find a file name to save it to - if (!QFS_NextFilename (pcxname, - va ("%s/qf", qfs_gamedir->dir.shots), ".tga")) { - Sys_Printf ("SCR_ScreenShot_f: Couldn't create a TGA file\n"); - } else { - tex_t *tex; - - tex = gl_SCR_CaptureBGR (); - WriteTGAfile (pcxname->str, tex->data, tex->width, tex->height); - free (tex); - Sys_Printf ("Wrote %s/%s\n", qfs_userpath, pcxname->str); - } - dstring_delete (pcxname); -} - -static void -SCR_TileClear (void) -{ - if (r_refdef.vrect.x > 0) { - // left - Draw_TileClear (0, 0, r_refdef.vrect.x, vid.height - vr_data.lineadj); - // right - Draw_TileClear (r_refdef.vrect.x + r_refdef.vrect.width, 0, - vid.width - r_refdef.vrect.x + r_refdef.vrect.width, - vid.height - vr_data.lineadj); - } - if (r_refdef.vrect.y > 0) { - // top - Draw_TileClear (r_refdef.vrect.x, 0, - r_refdef.vrect.x + r_refdef.vrect.width, - r_refdef.vrect.y); - // bottom - Draw_TileClear (r_refdef.vrect.x, - r_refdef.vrect.y + r_refdef.vrect.height, - r_refdef.vrect.width, - vid.height - vr_data.lineadj - - (r_refdef.vrect.height + r_refdef.vrect.y)); - } -} - -/* - SCR_UpdateScreen - - This is called every frame, and can also be called explicitly to flush - text to the screen. - - WARNING: be very careful calling this from elsewhere, because the refresh - needs almost the entire 256k of stack space! -*/ -void -gl_SCR_UpdateScreen (double realtime, SCR_Func scr_3dfunc, SCR_Func *scr_funcs) -{ - double time1 = 0, time2; - static int begun = 0; - - if (scr_skipupdate) - return; - - if (begun) - vid.end_rendering (); - - vr_data.realtime = realtime; - - vid.numpages = 2 + gl_triplebuffer->int_val; - - scr_copytop = 0; - vr_data.scr_copyeverything = 1; - - if (!scr_initialized) - return; // not initialized yet - - begun = 1; - - if (r_speeds->int_val) { - time1 = Sys_DoubleTime (); - gl_c_brush_polys = 0; - gl_c_alias_polys = 0; - } - - if (oldfov != scr_fov->value) { // determine size of refresh window - oldfov = scr_fov->value; - vid.recalc_refdef = true; - } - - if (vid.recalc_refdef) - SCR_CalcRefdef (); - - // do 3D refresh drawing, and then update the screen - scr_3dfunc (); - - SCR_SetUpToDrawConsole (); - GL_Set2D (); - GL_DrawReset (); - - // draw any areas not covered by the refresh - SCR_TileClear (); - - GL_Set2DScaled (); - - while (*scr_funcs) { - (*scr_funcs)(); - scr_funcs++; - } - - if (r_speeds->int_val) { -// qfglFinish (); - time2 = Sys_DoubleTime (); - Sys_MaskPrintf (SYS_DEV, "%3i ms %4i wpoly %4i epoly %4i parts\n", - (int) ((time2 - time1) * 1000), gl_c_brush_polys, - gl_c_alias_polys, numparticles); - } - - GL_FlushText (); - qfglFlush (); - - if (gl_finish->int_val) { - vid.end_rendering (); - begun = 0; - } -} diff --git a/libs/video/renderer/gl/gl_sky.c b/libs/video/renderer/gl/gl_sky.c index a4d325f09..0a26adc7b 100644 --- a/libs/video/renderer/gl/gl_sky.c +++ b/libs/video/renderer/gl/gl_sky.c @@ -28,9 +28,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -59,11 +56,11 @@ // cube from the outside on the -ve y axis with +x to the right, +y going in, // +z up, and front is the nearest face. static const char *suf[6] = { "rt", "bk", "lf", "ft", "up", "dn" }; -int gl_solidskytexture; -int gl_alphaskytexture; +GLuint gl_solidskytexture; +GLuint gl_alphaskytexture; // Set to true if a valid skybox is loaded --KB -qboolean gl_skyloaded = false; +bool gl_skyloaded = false; vec5_t gl_skyvec[6][4] = { @@ -118,7 +115,7 @@ gl_R_LoadSkys (const char *skyname) int i; // j if (!skyname || !*skyname) - skyname = r_skyname->string; + skyname = r_skyname; if (!*skyname || strcasecmp (skyname, "none") == 0) { gl_skyloaded = false; @@ -131,13 +128,14 @@ gl_R_LoadSkys (const char *skyname) qfglBindTexture (GL_TEXTURE_2D, SKY_TEX + i); - targa = LoadImage (name = va ("env/%s%s", skyname, suf[i])); + targa = LoadImage (name = va (0, "env/%s%s", skyname, suf[i]), 1); if (!targa || targa->format < 3) { // FIXME Can't do PCX right now - Sys_MaskPrintf (SYS_DEV, "Couldn't load %s\n", name); + Sys_MaskPrintf (SYS_dev, "Couldn't load %s\n", name); // also look in gfx/env, where Darkplaces looks for skies - targa = LoadImage (name = va ("gfx/env/%s%s", skyname, suf[i])); + targa = LoadImage (name = va (0, "gfx/env/%s%s", skyname, + suf[i]), 1); if (!targa) { - Sys_MaskPrintf (SYS_DEV, "Couldn't load %s\n", name); + Sys_MaskPrintf (SYS_dev, "Couldn't load %s\n", name); gl_skyloaded = false; continue; } @@ -188,9 +186,9 @@ R_DrawSkyBox (void) float *v = (float *) gl_skyvec[i][j]; qfglTexCoord2fv (v); - qfglVertex3f (r_refdef.vieworg[0] + v[2], - r_refdef.vieworg[1] + v[3], - r_refdef.vieworg[2] + v[4]); + qfglVertex3f (r_refdef.frame.position[0] + v[2], + r_refdef.frame.position[1] + v[3], + r_refdef.frame.position[2] + v[4]); } qfglEnd (); } @@ -218,7 +216,7 @@ skydome_vertex (const vec3_t v, float speedscale) s = speedscale + dir[0]; t = speedscale + dir[1]; - VectorAdd (r_refdef.vieworg, v, point); + VectorAdd (r_refdef.frame.position, v, point); qfglTexCoord2f (s, t); qfglVertex3fv (point); @@ -241,7 +239,7 @@ skydome_debug (void) h = 1; t = 0; - VectorAdd (zenith, r_refdef.vieworg, v[0]); + VectorAdd (zenith, r_refdef.frame.position, v[0]); for (b = 1; b <= 8; b++) { x = gl_bubble_costable[b + 8]; y = -gl_bubble_sintable[b + 8]; @@ -249,7 +247,7 @@ skydome_debug (void) v[h][0] = a1x * x; v[h][1] = a1y * x; v[h][2] = y * domescale[2]; - VectorAdd (v[h], r_refdef.vieworg, v[h]); + VectorAdd (v[h], r_refdef.frame.position, v[h]); for (i = t; i != h; i = (i + 1) % 3) { qfglVertex3fv (v[i]); qfglVertex3fv (v[h]); @@ -261,7 +259,7 @@ skydome_debug (void) v[h][0] = a2x * x; v[h][1] = a2y * x; v[h][2] = y * domescale[2]; - VectorAdd (v[h], r_refdef.vieworg, v[h]); + VectorAdd (v[h], r_refdef.frame.position, v[h]); for (i = t; i != h; i = (i + 1) % 3) { qfglVertex3fv (v[i]); qfglVertex3fv (v[h]); @@ -273,7 +271,7 @@ skydome_debug (void) h = 1; t = 0; - VectorAdd (nadir, r_refdef.vieworg, v[0]); + VectorAdd (nadir, r_refdef.frame.position, v[0]); for (b = 15; b >= 8; b--) { x = gl_bubble_costable[b + 8]; y = -gl_bubble_sintable[b + 8]; @@ -281,7 +279,7 @@ skydome_debug (void) v[h][0] = a2x * x; v[h][1] = a2y * x; v[h][2] = y * domescale[2]; - VectorAdd (v[h], r_refdef.vieworg, v[h]); + VectorAdd (v[h], r_refdef.frame.position, v[h]); for (i = t; i != h; i = (i + 1) % 3) { qfglVertex3fv (v[i]); qfglVertex3fv (v[h]); @@ -293,7 +291,7 @@ skydome_debug (void) v[h][0] = a1x * x; v[h][1] = a1y * x; v[h][2] = y * domescale[2]; - VectorAdd (v[h], r_refdef.vieworg, v[h]); + VectorAdd (v[h], r_refdef.frame.position, v[h]); for (i = t; i != h; i = (i + 1) % 3) { qfglVertex3fv (v[i]); qfglVertex3fv (v[h]); @@ -372,13 +370,13 @@ R_DrawSkyDome (void) qfglEnable (GL_BLEND); // clouds - if (gl_sky_multipass->int_val) { + if (gl_sky_multipass) { qfglBindTexture (GL_TEXTURE_2D, gl_alphaskytexture); speedscale = vr_data.realtime / 8.0; speedscale -= floor (speedscale); R_DrawSkyLayer (speedscale); } - if (gl_sky_debug->int_val) { + if (gl_sky_debug) { skydome_debug (); } } @@ -415,7 +413,7 @@ gl_R_InitSky (texture_t *mt) // make an average value for the back to avoid a fringe on the top level r = g = b = 0; - for (i = 0; i < 128; i++) + for (i = 0; i < 128; i++) { for (j = 0; j < 128; j++) { p = src[i * 256 + j + 128]; rgba = &d_8to24table[p]; @@ -424,14 +422,17 @@ gl_R_InitSky (texture_t *mt) g += ((byte *) rgba)[1]; b += ((byte *) rgba)[2]; } + } + r /= 128 * 128; + g /= 128 * 128; + b /= 128 * 128; - ((byte *) & transpix)[0] = r / (128 * 128); - ((byte *) & transpix)[1] = g / (128 * 128); - ((byte *) & transpix)[2] = b / (128 * 128); - ((byte *) & transpix)[3] = 0; + //FIXME assumes little endian + transpix = ((b << 16) | (g << 8) | (r << 0)) & 0x00ffffff; - if (!gl_solidskytexture) - gl_solidskytexture = gl_texture_number++; + if (!gl_solidskytexture) { + qfglGenTextures (1, &gl_solidskytexture); + } qfglBindTexture (GL_TEXTURE_2D, gl_solidskytexture); qfglTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); @@ -450,8 +451,9 @@ gl_R_InitSky (texture_t *mt) trans[(i * 128) + j] = d_8to24table[p]; } - if (!gl_alphaskytexture) - gl_alphaskytexture = gl_texture_number++; + if (!gl_alphaskytexture) { + qfglGenTextures (1, &gl_alphaskytexture); + } qfglBindTexture (GL_TEXTURE_2D, gl_alphaskytexture); qfglTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); diff --git a/libs/video/renderer/gl/gl_sky_clip.c b/libs/video/renderer/gl/gl_sky_clip.c index 613e8b177..64aea23f4 100644 --- a/libs/video/renderer/gl/gl_sky_clip.c +++ b/libs/video/renderer/gl/gl_sky_clip.c @@ -28,9 +28,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #include #ifdef HAVE_STRING_H # include @@ -220,14 +217,14 @@ find_cube_vertex (int face1, int face2, int face3, vec3_t v) set_vertex add the vertex to the polygon describing the face of the cube. Offsets - the vertex relative to r_refdef.vieworg so the cube is always centered + the vertex relative to r_refdef.frame.position so the cube is always centered on the player and also calculates the texture coordinates of the vertex (wish I could find a cleaner way of calculating s and t). */ static void set_vertex (struct box_def *box, int face, int ind, const vec3_t v) { - VectorAdd (v, r_refdef.vieworg, box->face[face].poly.verts[ind]); + VectorAdd (v, r_refdef.frame.position, box->face[face].poly.verts[ind]); switch (face) { case 0: box->face[face].poly.verts[ind][3] = (1024 - v[1] + 4) / BOX_WIDTH; @@ -370,7 +367,6 @@ process_corners (struct box_def *box) for (i = 0; i < 6; i++) { if (max_visit < box->face_visits[i]) { max_visit = box->face_visits[i]; - center = i; } } @@ -602,14 +598,14 @@ R_DrawSkyBoxPoly (const glpoly_t *poly) Sys_Error ("too many verts!"); } - VectorSubtract (poly->verts[poly->numverts - 1], r_refdef.vieworg, last_v); + VectorSubtract (poly->verts[poly->numverts - 1], r_refdef.frame.position, last_v); prev_face = determine_face (last_v); box.visited_faces[0].face = prev_face; box.face_count = 1; for (i = 0; i < poly->numverts; i++) { - VectorSubtract (poly->verts[i], r_refdef.vieworg, v); + VectorSubtract (poly->verts[i], r_refdef.frame.position, v); face = determine_face (v); if (face != prev_face) { if ((face_axis[face]) == (face_axis[prev_face])) { @@ -645,13 +641,14 @@ EmitSkyPolys (float speedscale, const instsurf_t *sc) int i; glpoly_t *p; vec3_t dir; - msurface_t *fa = sc->surface; + msurface_t *surf = sc->surface; + vec4f_t origin = r_refdef.frame.position; //FIXME transform/color - for (p = fa->polys; p; p = p->next) { + for (p = surf->polys; p; p = p->next) { qfglBegin (GL_POLYGON); for (i = 0, v = p->verts[0]; i < p->numverts; i++, v += VERTEXSIZE) { - VectorSubtract (v, r_origin, dir); + VectorSubtract (v, origin, dir); dir[2] *= 3.0; // flatten the sphere length = DotProduct (dir, dir); @@ -695,7 +692,7 @@ draw_black_sky_polys (const instsurf_t *sky_chain) if (sc->transform) { qfglPushMatrix (); - qfglLoadMatrixf (sc->transform); + qfglLoadMatrixf ((vec_t*)&sc->transform[0]);//FIXME } while (p) { draw_poly (p); @@ -753,7 +750,7 @@ draw_id_sky_polys (const instsurf_t *sky_chain) sc = sc->tex_chain; } - if (gl_sky_multipass->int_val) { + if (gl_sky_multipass) { sc = sky_chain; speedscale = vr_data.realtime / 8; @@ -781,7 +778,7 @@ draw_z_sky_polys (const instsurf_t *sky_chain) if (sc->transform) { qfglPushMatrix (); - qfglLoadMatrixf (sc->transform); + qfglLoadMatrixf ((vec_t*)&sc->transform[0]);//FIXME } while (p) { draw_poly (p); @@ -800,19 +797,19 @@ draw_z_sky_polys (const instsurf_t *sky_chain) void gl_R_DrawSkyChain (const instsurf_t *sky_chain) { - if (gl_sky_clip->int_val > 2) { + if (gl_sky_clip > 2) { draw_black_sky_polys (sky_chain); return; } if (gl_skyloaded) { - if (gl_sky_clip->int_val) { + if (gl_sky_clip) { draw_skybox_sky_polys (sky_chain); } draw_z_sky_polys (sky_chain); - } else if (gl_sky_clip->int_val == 2) { + } else if (gl_sky_clip == 2) { draw_id_sky_polys (sky_chain); - } else if (gl_sky_clip->int_val) { + } else if (gl_sky_clip) { // XXX not properly implemented draw_skydome_sky_polys (sky_chain); //draw_z_sky_polys (sky_chain); @@ -820,11 +817,11 @@ gl_R_DrawSkyChain (const instsurf_t *sky_chain) draw_z_sky_polys (sky_chain); } - if (gl_sky_debug->int_val) { + if (gl_sky_debug) { const instsurf_t *sc; qfglDisable (GL_TEXTURE_2D); - if (gl_sky_debug->int_val & 1) { + if (gl_sky_debug & 1) { sc = sky_chain; qfglColor3ub (255, 255, 255); while (sc) { @@ -832,7 +829,7 @@ gl_R_DrawSkyChain (const instsurf_t *sky_chain) if (sc->transform) { qfglPushMatrix (); - qfglLoadMatrixf (sc->transform); + qfglLoadMatrixf ((vec_t*)&sc->transform[0]);//FIXME } while (p) { int i; @@ -849,7 +846,7 @@ gl_R_DrawSkyChain (const instsurf_t *sky_chain) sc = sc->tex_chain; } } - if (gl_sky_debug->int_val & 2) { + if (gl_sky_debug & 2) { sc = sky_chain; qfglColor3ub (0, 255, 0); qfglBegin (GL_POINTS); @@ -858,18 +855,18 @@ gl_R_DrawSkyChain (const instsurf_t *sky_chain) if (sc->transform) { qfglPushMatrix (); - qfglLoadMatrixf (sc->transform); + qfglLoadMatrixf ((vec_t*)&sc->transform[0]);//FIXME } while (p) { int i; vec3_t x, c = { 0, 0, 0 }; for (i = 0; i < p->numverts; i++) { - VectorSubtract (p->verts[i], r_refdef.vieworg, x); + VectorSubtract (p->verts[i], r_refdef.frame.position, x); VectorAdd (x, c, c); } VectorScale (c, 1.0 / p->numverts, c); - VectorAdd (c, r_refdef.vieworg, c); + VectorAdd (c, r_refdef.frame.position, c); qfglVertex3fv (c); p = p->next; } @@ -879,7 +876,7 @@ gl_R_DrawSkyChain (const instsurf_t *sky_chain) } qfglEnd (); } - if (gl_sky_debug->int_val & 4) { + if (gl_sky_debug & 4) { if (gl_skyloaded) { int i, j; @@ -890,7 +887,7 @@ gl_R_DrawSkyChain (const instsurf_t *sky_chain) qfglBegin (GL_LINE_LOOP); for (j = 0; j < 4; j++) { VectorScale (&gl_skyvec[i][j][2], 1.0 / 128.0, v); - VectorAdd (v, r_refdef.vieworg, v); + VectorAdd (v, r_refdef.frame.position, v); qfglVertex3fv (v); } qfglEnd (); diff --git a/libs/video/renderer/gl/gl_textures.c b/libs/video/renderer/gl/gl_textures.c index 00a3d47c4..5cabff506 100644 --- a/libs/video/renderer/gl/gl_textures.c +++ b/libs/video/renderer/gl/gl_textures.c @@ -29,9 +29,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -45,25 +42,41 @@ #include "QF/crc.h" #include "QF/cvar.h" #include "QF/draw.h" +#include "QF/image.h" #include "QF/mathlib.h" #include "QF/sys.h" #include "QF/GL/defines.h" #include "QF/GL/funcs.h" #include "QF/GL/qf_textures.h" #include "QF/GL/qf_vid.h" +#include "QF/ui/vrect.h" #include "compat.h" #include "r_internal.h" -#include "sbar.h" +#include "r_scrap.h" #include "vid_internal.h" +struct scrap_s { + rscrap_t rscrap; + GLuint tnum; + int format; + int bpp; + byte *data; // local copy of the texture so updates can be batched + vrect_t *batch; + subpic_t *subpics; + struct scrap_s *next; +}; + +static scrap_t *scrap_list; +static int max_tex_size; + typedef struct { - int texnum; + GLuint texnum; char identifier[64]; int width, height; int bytesperpixel; // int texelformat; // eventually replace bytesperpixel - qboolean mipmap; + bool mipmap; unsigned short crc; // LordHavoc: CRC for texure validation } gltexture_t; @@ -364,8 +377,8 @@ GL_MipMap8Bit (byte *in, int width, int height) } static void -GL_Upload32 (unsigned int *data, int width, int height, qboolean mipmap, - qboolean alpha) +GL_Upload32 (unsigned int *data, int width, int height, bool mipmap, + bool alpha) { int scaled_width, scaled_height, intformat; unsigned int *scaled; @@ -377,11 +390,11 @@ GL_Upload32 (unsigned int *data, int width, int height, qboolean mipmap, for (scaled_width = 1; scaled_width < width; scaled_width <<= 1); for (scaled_height = 1; scaled_height < height; scaled_height <<= 1); - scaled_width >>= gl_picmip->int_val; - scaled_height >>= gl_picmip->int_val; + scaled_width >>= gl_picmip; + scaled_height >>= gl_picmip; - scaled_width = min (scaled_width, gl_max_size->int_val); - scaled_height = min (scaled_height, gl_max_size->int_val); + scaled_width = min (scaled_width, gl_max_size); + scaled_height = min (scaled_height, gl_max_size); if (!(scaled = malloc (scaled_width * scaled_height * sizeof (GLuint)))) Sys_Error ("GL_LoadTexture: too big"); @@ -424,7 +437,7 @@ GL_Upload32 (unsigned int *data, int width, int height, qboolean mipmap, } else { qfglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); - if (gl_picmip->int_val) + if (gl_picmip) qfglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); else @@ -445,8 +458,8 @@ GL_Upload32 (unsigned int *data, int width, int height, qboolean mipmap, If we don't, this function does nothing. */ void -GL_Upload8_EXT (const byte *data, int width, int height, qboolean mipmap, - qboolean alpha) +GL_Upload8_EXT (const byte *data, int width, int height, bool mipmap, + bool alpha) { byte *scaled; int scaled_width, scaled_height; @@ -455,11 +468,11 @@ GL_Upload8_EXT (const byte *data, int width, int height, qboolean mipmap, for (scaled_width = 1; scaled_width < width; scaled_width <<= 1); for (scaled_height = 1; scaled_height < height; scaled_height <<= 1); - scaled_width >>= gl_picmip->int_val; - scaled_height >>= gl_picmip->int_val; + scaled_width >>= gl_picmip; + scaled_height >>= gl_picmip; - scaled_width = min (scaled_width, gl_max_size->int_val); - scaled_height = min (scaled_height, gl_max_size->int_val); + scaled_width = min (scaled_width, gl_max_size); + scaled_height = min (scaled_height, gl_max_size); if (!(scaled = malloc (scaled_width * scaled_height))) Sys_Error ("GL_LoadTexture: too big"); @@ -501,7 +514,7 @@ GL_Upload8_EXT (const byte *data, int width, int height, qboolean mipmap, } else { qfglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); - if (gl_picmip->int_val) + if (gl_picmip) qfglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); else @@ -516,8 +529,8 @@ GL_Upload8_EXT (const byte *data, int width, int height, qboolean mipmap, } void -GL_Upload8 (const byte *data, int width, int height, qboolean mipmap, - qboolean alpha) +GL_Upload8 (const byte *data, int width, int height, bool mipmap, + bool alpha) { int i, s, p; unsigned int *trans; @@ -555,7 +568,7 @@ GL_Upload8 (const byte *data, int width, int height, qboolean mipmap, int GL_LoadTexture (const char *identifier, int width, int height, const byte *data, - qboolean mipmap, qboolean alpha, int bytesperpixel) + bool mipmap, bool alpha, int bytesperpixel) { int crc, i; gltexture_t *glt; @@ -587,8 +600,7 @@ GL_LoadTexture (const char *identifier, int width, int height, const byte *data, strncpy (glt->identifier, identifier, sizeof (glt->identifier) - 1); glt->identifier[sizeof (glt->identifier) - 1] = '\0'; - glt->texnum = gl_texture_number; - gl_texture_number++; + qfglGenTextures (1, &glt->texnum); SetupTexture: glt->crc = crc; @@ -629,3 +641,294 @@ SetupTexture: return glt->texnum; } + +int +GL_LoadTex (const char *identifier, int mips, tex_t *tex) +{ + GLuint tnum; + qfglGenTextures (1, &tnum); + int format = GL_RGB; + + switch (tex->format) { + case tex_l: + case tex_a: + format = tex->format; + break; + case tex_la: + format = GL_LUMINANCE_ALPHA; + break; + case tex_rgb: + format = GL_RGB; + break; + case tex_rgba: + format = GL_RGBA; + break; + default: + Sys_Error ("GL_CreateScrap: Invalid texture format"); + } + + qfglBindTexture (GL_TEXTURE_2D, tnum); + qfglTexImage2D (GL_TEXTURE_2D, 0, format, tex->width, tex->height, + 0, format, GL_UNSIGNED_BYTE, tex->data); + qfglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + qfglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (mips) { + qfglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + qfglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + qfglGenerateMipmap (GL_TEXTURE_2D); + } else { + qfglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + qfglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + return tnum; +} + +void +GL_ReleaseTexture (int tex) +{ + GLuint tnum = tex; + qfglDeleteTextures (1, &tnum); +} + +static void +gl_scraps_f (void) +{ + scrap_t *scrap; + int area; + int size; + int count; + + if (!scrap_list) { + Sys_Printf ("No scraps\n"); + return; + } + for (scrap = scrap_list; scrap; scrap = scrap->next) { + area = R_ScrapArea (&scrap->rscrap, &count); + // always square + size = scrap->rscrap.width; + Sys_Printf ("tnum=%u size=%d format=%04x bpp=%d free=%d%% rects=%d\n", + scrap->tnum, size, scrap->format, scrap->bpp, + area * 100 / (size * size), count); + if (Cmd_Argc () > 1) { + R_ScrapDump (&scrap->rscrap); + } + } +} + +void +GL_TextureInit (void) +{ + qfglGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_tex_size); + Sys_MaskPrintf (SYS_glt, "max texture size: %d\n", max_tex_size); + + Cmd_AddCommand ("gl_scraps", gl_scraps_f, "Dump GL scrap stats"); +} + +scrap_t * +GL_CreateScrap (int size, int format, int linear) +{ + int i; + int bpp; + scrap_t *scrap; + + for (i = 0; i < 16; i++) + if (size <= 1 << i) + break; + size = 1 << i; + size = min (size, max_tex_size); + switch (format) { + case GL_ALPHA: + case GL_LUMINANCE: + bpp = 1; + break; + case GL_LUMINANCE_ALPHA: + bpp = 2; + break; + case GL_RGB: + bpp = 3; + break; + case GL_RGBA: + bpp = 4; + break; + default: + Sys_Error ("GL_CreateScrap: Invalid texture format"); + } + scrap = malloc (sizeof (scrap_t)); + qfglGenTextures (1, &scrap->tnum); + R_ScrapInit (&scrap->rscrap, size, size); + scrap->format = format; + scrap->bpp = bpp; + scrap->subpics = 0; + scrap->next = scrap_list; + scrap_list = scrap; + + scrap->data = calloc (1, size * size * bpp); + scrap->batch = 0; + + qfglBindTexture (GL_TEXTURE_2D, scrap->tnum); + qfglTexImage2D (GL_TEXTURE_2D, 0, format, + size, size, 0, format, GL_UNSIGNED_BYTE, scrap->data); + qfglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + qfglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (linear) { + qfglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + qfglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } else { + qfglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + qfglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + qfglGenerateMipmap (GL_TEXTURE_2D); + + return scrap; +} + +void +GL_ScrapClear (scrap_t *scrap) +{ + subpic_t *sp; + while (scrap->subpics) { + sp = scrap->subpics; + scrap->subpics = (subpic_t *) sp->next; + free (sp); + } + R_ScrapClear (&scrap->rscrap); +} + +void +GL_DestroyScrap (scrap_t *scrap) +{ + scrap_t **s; + + for (s = &scrap_list; *s; s = &(*s)->next) { + if (*s == scrap) { + *s = scrap->next; + break; + } + } + GL_ScrapClear (scrap); + R_ScrapDelete (&scrap->rscrap); + GL_ReleaseTexture (scrap->tnum); + free (scrap->data); + free (scrap); +} + +int +GL_ScrapTexture (scrap_t *scrap) +{ + return scrap->tnum; +} + +subpic_t * +GL_ScrapSubpic (scrap_t *scrap, int width, int height) +{ + vrect_t *rect; + subpic_t *subpic; + + rect = R_ScrapAlloc (&scrap->rscrap, width, height); + if (!rect) { + return 0; + } + + subpic = malloc (sizeof (subpic_t)); + *((subpic_t **) &subpic->next) = scrap->subpics; + scrap->subpics = subpic; + *((scrap_t **) &subpic->scrap) = scrap; + *((vrect_t **) &subpic->rect) = rect; + *((int *) &subpic->width) = width; + *((int *) &subpic->height) = height; + *((float *) &subpic->size) = 1.0 / scrap->rscrap.width; + return subpic; +} + +void +GL_SubpicDelete (subpic_t *subpic) +{ + scrap_t *scrap = (scrap_t *) subpic->scrap; + vrect_t *rect = (vrect_t *) subpic->rect; + subpic_t **sp; + + for (sp = &scrap->subpics; *sp; sp = (subpic_t **) &(*sp)->next) + if (*sp == subpic) + break; + if (*sp != subpic) + Sys_Error ("GL_ScrapDelSubpic: broken subpic"); + *sp = (subpic_t *) subpic->next; + free (subpic); + R_ScrapFree (&scrap->rscrap, rect); +} + +void +GL_SubpicUpdate (subpic_t *subpic, byte *data, int batch) +{ + scrap_t *scrap = (scrap_t *) subpic->scrap; + vrect_t *rect = (vrect_t *) subpic->rect; + byte *dest; + int step, sbytes; + int i; + + if (batch) { + /*if (scrap->batch) { + vrect_t *r = scrap->batch; + scrap->batch = VRect_Union (r, rect); + VRect_Delete (r); + } else { + scrap->batch = VRect_New (rect->x, rect->y, + rect->width, rect->height); + }*/ + vrect_t *r = VRect_New (rect->x, rect->y, + rect->width, rect->height); + r->next = scrap->batch; + scrap->batch = r; + + step = scrap->rscrap.width * scrap->bpp; + sbytes = subpic->width * scrap->bpp; + dest = scrap->data + rect->y * step + rect->x * scrap->bpp; + for (i = 0; i < subpic->height; i++, dest += step, data += sbytes) + memcpy (dest, data, sbytes); + } else { + qfglBindTexture (GL_TEXTURE_2D, scrap->tnum); + qfglTexSubImage2D (GL_TEXTURE_2D, 0, rect->x, rect->y, + subpic->width, subpic->height, scrap->format, + GL_UNSIGNED_BYTE, data); + } +} + +void +GL_ScrapFlush (scrap_t *scrap) +{ + vrect_t *rect = scrap->batch; + int size = scrap->rscrap.width; + + if (!rect) + return; + //FIXME: it seems gl (as opposed to egl) allows row step to be specified. + //should update to not update the entire horizontal block + qfglBindTexture (GL_TEXTURE_2D, scrap->tnum); + qfglPixelStorei(GL_UNPACK_ROW_LENGTH, size); + //qfglPixelStorei(GL_UNPACK_ALIGNMENT, 1); + while (rect) { + vrect_t *next = rect->next; +#if 1 + int x = rect->x; + int y = rect->y; + int w = rect->width; + int h = rect->height; + qfglTexSubImage2D (GL_TEXTURE_2D, 0, x, y, w, h, scrap->format, + GL_UNSIGNED_BYTE, + scrap->data + (y * size + x) * scrap->bpp); +#else + for (int i = 0; i < rect->height; i++) { + int y = rect->y + i; + qfglTexSubImage2D (GL_TEXTURE_2D, 0, rect->x, y, + rect->width, 1, scrap->format, + GL_UNSIGNED_BYTE, + scrap->data + (y * size + rect->x) * scrap->bpp); + } +#endif + VRect_Delete (rect); + rect = next; + } + qfglPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + //qfglPixelStorei(GL_UNPACK_ALIGNMENT, 4); + scrap->batch = 0; +} diff --git a/libs/video/renderer/gl/gl_warp.c b/libs/video/renderer/gl/gl_warp.c index d73e11f2a..f7a0b9ee4 100644 --- a/libs/video/renderer/gl/gl_warp.c +++ b/libs/video/renderer/gl/gl_warp.c @@ -28,18 +28,16 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #include "QF/cvar.h" #include "QF/sys.h" -#include "r_internal.h" - #include "QF/GL/defines.h" #include "QF/GL/funcs.h" #include "QF/GL/qf_rsurf.h" +#include "r_internal.h" +#include "vid_gl.h" + // speed up sin calculations - Ed static float turbsin[] = { # include "gl_warp_sin.h" @@ -54,7 +52,7 @@ static float turbsin[] = { Does a water warp on the pre-fragmented glpoly_t chain */ void -GL_EmitWaterPolys (msurface_t *fa) +GL_EmitWaterPolys (msurface_t *surf) { float os, ot, s, t, timetemp; float *v; @@ -63,7 +61,7 @@ GL_EmitWaterPolys (msurface_t *fa) timetemp = vr_data.realtime * TURBSCALE; - for (p = fa->polys; p; p = p->next) { + for (p = surf->polys; p; p = p->next) { qfglBegin (GL_POLYGON); for (i = 0, v = p->verts[0]; i < p->numverts; i++, v += VERTEXSIZE) { os = turbsin[(int) (v[3] * TURBFRAC + timetemp) & 255]; @@ -72,11 +70,11 @@ GL_EmitWaterPolys (msurface_t *fa) t = (v[4] + os) * (1.0 / 64.0); qfglTexCoord2f (s, t); - if (r_waterripple->value != 0) { + if (r_waterripple != 0) { vec3_t nv; VectorCopy (v, nv); - nv[2] += r_waterripple->value * os * ot * (1.0 / 64.0); + nv[2] += r_waterripple * os * ot * (1.0 / 64.0); qfglVertex3fv (nv); } else qfglVertex3fv (v); @@ -84,3 +82,59 @@ GL_EmitWaterPolys (msurface_t *fa) qfglEnd (); } } + +const float S = 0.15625; +const float F = 2.5; +const float A = 0.01; + +static void +warp_uv (float *uv, float time) +{ + float p1 = (time * S + F * uv[1]) * 2 * M_PI; + float p2 = (time * S + F * uv[0]) * 2 * M_PI; + uv[0] = uv[0] * (1 - 2 * A) + A * (1 + sin (p1)); + uv[1] = uv[1] * (1 - 2 * A) + A * (1 + sin (p2)); +} + +void +gl_WarpScreen (framebuffer_t *fb) +{ + gl_framebuffer_t *buffer = fb->buffer; + + qfglMatrixMode (GL_PROJECTION); + qfglLoadIdentity (); + qfglOrtho (0, r_refdef.vrect.width, r_refdef.vrect.height, 0, -9999, 9999); + qfglMatrixMode (GL_MODELVIEW); + qfglLoadIdentity (); + qfglColor3ubv (color_white); + + qfglDisable (GL_DEPTH_TEST); + qfglFrontFace (GL_CCW); + + qfglBindTexture (GL_TEXTURE_2D, buffer->color); + + int base_x = r_refdef.vrect.x; + int base_y = r_refdef.vrect.y; + int width = r_refdef.vrect.width; + int height = r_refdef.vrect.height; + + float time = vr_data.realtime; + + for (int y = 0; y < height; y += 40) { + qfglBegin (GL_QUAD_STRIP); + for (int x = 0; x <= width; x+= 40) { + int y2 = y + 40; + int xy1[] = { base_x + x, base_y + y }; + int xy2[] = { base_x + x, base_y + y2}; + float uv1[] = { (float) x / width, 1 - (float) y / height }; + float uv2[] = { (float) x / width, 1 - (float) y2 / height }; + warp_uv (uv1, time); + warp_uv (uv2, time); + qfglTexCoord2fv (uv2); + qfglVertex2iv (xy2); + qfglTexCoord2fv (uv1); + qfglVertex2iv (xy1); + } + qfglEnd (); + } +} diff --git a/libs/video/renderer/gl/namehack.h b/libs/video/renderer/gl/namehack.h deleted file mode 100644 index 075e31836..000000000 --- a/libs/video/renderer/gl/namehack.h +++ /dev/null @@ -1,134 +0,0 @@ -#ifdef NH_DEFINE -#undef NH_DEFINE -#define Draw_Init gl_Draw_Init -#define Draw_Character gl_Draw_Character -#define Draw_String gl_Draw_String -#define Draw_nString gl_Draw_nString -#define Draw_AltString gl_Draw_AltString -#define Draw_ConsoleBackground gl_Draw_ConsoleBackground -#define Draw_Crosshair gl_Draw_Crosshair -#define Draw_CrosshairAt gl_Draw_CrosshairAt -#define Draw_TileClear gl_Draw_TileClear -#define Draw_Fill gl_Draw_Fill -#define Draw_TextBox gl_Draw_TextBox -#define Draw_FadeScreen gl_Draw_FadeScreen -#define Draw_BlendScreen gl_Draw_BlendScreen -#define Draw_CachePic gl_Draw_CachePic -#define Draw_UncachePic gl_Draw_UncachePic -#define Draw_MakePic gl_Draw_MakePic -#define Draw_DestroyPic gl_Draw_DestroyPic -#define Draw_PicFromWad gl_Draw_PicFromWad -#define Draw_Pic gl_Draw_Pic -#define Draw_Picf gl_Draw_Picf -#define Draw_SubPic gl_Draw_SubPic -#define Fog_DisableGFog gl_Fog_DisableGFog -#define Fog_EnableGFog gl_Fog_EnableGFog -#define Fog_GetColor gl_Fog_GetColor -#define Fog_GetDensity gl_Fog_GetDensity -#define Fog_Init gl_Fog_Init -#define Fog_ParseWorldspawn gl_Fog_ParseWorldspawn -#define Fog_SetupFrame gl_Fog_SetupFrame -#define Fog_StartAdditive gl_Fog_StartAdditive -#define Fog_StopAdditive gl_Fog_StopAdditive -#define Fog_Update gl_Fog_Update -#define R_AddTexture gl_R_AddTexture -#define R_BlendLightmaps gl_R_BlendLightmaps -#define R_BuildLightMap gl_R_BuildLightMap -#define R_CalcLightmaps gl_R_CalcLightmaps -#define R_ClearParticles gl_R_ClearParticles -#define R_ClearState gl_R_ClearState -#define R_ClearTextures gl_R_ClearTextures -#define R_DrawAliasModel gl_R_DrawAliasModel -#define R_DrawBrushModel gl_R_DrawBrushModel -#define R_DrawParticles gl_R_DrawParticles -#define R_DrawSky gl_R_DrawSky -#define R_DrawSkyChain gl_R_DrawSkyChain -#define R_DrawSpriteModel gl_R_DrawSpriteModel -#define R_DrawWaterSurfaces gl_R_DrawWaterSurfaces -#define R_DrawWorld gl_R_DrawWorld -#define R_InitBubble gl_R_InitBubble -#define R_InitGraphTextures gl_R_InitGraphTextures -#define R_InitParticles gl_R_InitParticles -#define R_InitSky gl_R_InitSky -#define R_InitSprites gl_R_InitSprites -#define R_InitSurfaceChains gl_R_InitSurfaceChains -#define R_LineGraph gl_R_LineGraph -#define R_LoadSky_f gl_R_LoadSky_f -#define R_LoadSkys gl_R_LoadSkys -#define R_NewMap gl_R_NewMap -#define R_Particle_New gl_R_Particle_New -#define R_Particle_NewRandom gl_R_Particle_NewRandom -#define R_Particles_Init_Cvars gl_R_Particles_Init_Cvars -#define R_ReadPointFile_f gl_R_ReadPointFile_f -#define R_RenderDlights gl_R_RenderDlights -#define R_RenderView gl_R_RenderView -#define R_RotateForEntity gl_R_RotateForEntity -#define R_SetupFrame gl_R_SetupFrame -#define R_TimeRefresh_f gl_R_TimeRefresh_f -#define R_ViewChanged gl_R_ViewChanged -#define SCR_CaptureBGR gl_SCR_CaptureBGR -#define SCR_ScreenShot gl_SCR_ScreenShot -#define SCR_ScreenShot_f gl_SCR_ScreenShot_f -#define SCR_UpdateScreen gl_SCR_UpdateScreen -#define c_alias_polys gl_c_alias_polys -#define c_brush_polys gl_c_brush_polys -#define r_easter_eggs_f gl_r_easter_eggs_f -#define r_particles_style_f gl_r_particles_style_f -#define r_world_matrix gl_r_world_matrix -#else -#undef Fog_DisableGFog -#undef Fog_EnableGFog -#undef Fog_GetColor -#undef Fog_GetDensity -#undef Fog_Init -#undef Fog_ParseWorldspawn -#undef Fog_SetupFrame -#undef Fog_StartAdditive -#undef Fog_StopAdditive -#undef Fog_Update -#undef R_AddTexture -#undef R_BlendLightmaps -#undef R_BuildLightMap -#undef R_CalcLightmaps -#undef R_ClearParticles -#undef R_ClearState -#undef R_ClearTextures -#undef R_DrawAliasModel -#undef R_DrawBrushModel -#undef R_DrawParticles -#undef R_DrawSky -#undef R_DrawSkyChain -#undef R_DrawSpriteModel -#undef R_DrawWaterSurfaces -#undef R_DrawWorld -#undef R_Init -#undef R_InitBubble -#undef R_InitGraphTextures -#undef R_InitParticles -#undef R_InitSky -#undef R_InitSprites -#undef R_InitSurfaceChains -#undef R_LineGraph -#undef R_LoadSky_f -#undef R_LoadSkys -#undef R_NewMap -#undef R_Particle_New -#undef R_Particle_NewRandom -#undef R_Particles_Init_Cvars -#undef R_ReadPointFile_f -#undef R_RenderDlights -#undef R_RenderView -#undef R_RotateForEntity -#undef R_SetupFrame -#undef R_TimeRefresh_f -#undef R_ViewChanged -#undef SCR_CaptureBGR -#undef SCR_ScreenShot -#undef SCR_ScreenShot_f -#undef SCR_UpdateScreen -#undef c_alias_polys -#undef c_brush_polys -#undef r_easter_eggs_f -#undef r_particles_style_f -#undef r_world_matrix -#endif diff --git a/libs/video/renderer/gl/qfgl_ext.c b/libs/video/renderer/gl/qfgl_ext.c index fa28797d5..e019fcc79 100644 --- a/libs/video/renderer/gl/qfgl_ext.c +++ b/libs/video/renderer/gl/qfgl_ext.c @@ -61,6 +61,7 @@ #include "QF/GL/funcs.h" #include "r_internal.h" +#include "vid_gl.h" // First we need to get all the function pointers declared. #define QFGL_WANT(ret, name, args) \ @@ -71,13 +72,13 @@ #undef QFGL_NEED #undef QFGL_WANT -qboolean +bool GLF_FindFunctions (void) { #define QFGL_WANT(ret, name, args) \ - qf##name = vid.get_proc_address (#name, false); + qf##name = gl_ctx->get_proc_address (#name, false); #define QFGL_NEED(ret, name, args) \ - qf##name = vid.get_proc_address (#name, true); + qf##name = gl_ctx->get_proc_address (#name, true); #include "QF/GL/qf_funcs_list.h" #undef QFGL_NEED #undef QFGL_WANT @@ -91,7 +92,7 @@ GLF_FindFunctions (void) It takes a bit of care to be fool-proof about parsing an OpenGL extensions string. Don't be fooled by sub-strings, etc. */ -static qboolean +static __attribute__((pure)) bool QFGL_ParseExtensionList (const GLubyte *list, const char *name) { const char *start; @@ -119,7 +120,7 @@ QFGL_ParseExtensionList (const GLubyte *list, const char *name) return 0; } -qboolean +bool QFGL_ExtensionPresent (const char *name) { static const GLubyte *gl_extensions = NULL; @@ -135,6 +136,6 @@ void * QFGL_ExtensionAddress (const char *name) { if (name) - return vid.get_proc_address (name, false); + return gl_ctx->get_proc_address (name, false); return NULL; } diff --git a/libs/video/renderer/gl/vid_common_gl.c b/libs/video/renderer/gl/vid_common_gl.c index ec21df3e5..83e6431b5 100644 --- a/libs/video/renderer/gl/vid_common_gl.c +++ b/libs/video/renderer/gl/vid_common_gl.c @@ -40,7 +40,6 @@ #endif #include "QF/cvar.h" -#include "QF/input.h" #include "QF/qargs.h" #include "QF/quakefs.h" #include "QF/sys.h" @@ -49,12 +48,13 @@ #include "QF/GL/defines.h" #include "QF/GL/extensions.h" #include "QF/GL/funcs.h" +#include "QF/GL/qf_rmain.h" +#include "QF/GL/qf_textures.h" #include "QF/GL/qf_vid.h" #include "compat.h" #include "d_iface.h" #include "r_internal.h" -#include "sbar.h" #define WARP_WIDTH 320 #define WARP_HEIGHT 200 @@ -79,105 +79,391 @@ int gl_use_bgra; int gl_va_capable; static int driver_vaelements; int vaelements; -int gl_texture_number = 1; int gl_filter_min = GL_LINEAR_MIPMAP_LINEAR; int gl_filter_max = GL_LINEAR; float gldepthmin, gldepthmax; // Multitexture -qboolean gl_mtex_capable = false; +bool gl_mtex_capable = false; static int gl_mtex_tmus = 0; GLenum gl_mtex_enum; int gl_mtex_active_tmus = 0; -qboolean gl_mtex_fullbright = false; +bool gl_mtex_fullbright = false; // Combine -qboolean gl_combine_capable = false; +bool gl_combine_capable = false; int lm_src_blend, lm_dest_blend; float gl_rgb_scale = 1.0; QF_glColorTableEXT qglColorTableEXT = NULL; -qboolean gl_feature_mach64 = false; +bool gl_feature_mach64 = false; // GL_EXT_texture_filter_anisotropic -qboolean gl_Anisotropy; +bool gl_Anisotropy; static float aniso_max; float gl_aniso; // GL_ATI_pn_triangles -static qboolean TruForm; +static bool TruForm; static int tess_max; int gl_tess; // GL_LIGHT int gl_max_lights; -cvar_t *gl_anisotropy; -cvar_t *gl_doublebright; -cvar_t *gl_fb_bmodels; -cvar_t *gl_finish; -cvar_t *gl_max_size; -cvar_t *gl_multitexture; -cvar_t *gl_tessellate; -cvar_t *gl_textures_bgra; -cvar_t *gl_vaelements_max; -cvar_t *gl_vector_light; -cvar_t *gl_screenshot_byte_swap; - -cvar_t *gl_affinemodels; -cvar_t *gl_clear; -cvar_t *gl_conspin; -cvar_t *gl_constretch; -cvar_t *gl_dlight_polyblend; -cvar_t *gl_dlight_smooth; -cvar_t *gl_driver; -cvar_t *gl_fb_models; -cvar_t *gl_keeptjunctions; -cvar_t *gl_lerp_anim; -cvar_t *gl_lightmap_align; -cvar_t *gl_lightmap_subimage; -cvar_t *gl_nocolors; -cvar_t *gl_overbright; -cvar_t *gl_particle_mip; -cvar_t *gl_particle_size; -cvar_t *gl_picmip; -cvar_t *gl_playermip; -cvar_t *gl_reporttjunctions; -cvar_t *gl_sky_clip; -cvar_t *gl_sky_debug; -cvar_t *gl_sky_multipass; -cvar_t *gl_texsort; -cvar_t *gl_triplebuffer; -static cvar_t *vid_use8bit; - -void gl_multitexture_f (cvar_t *var); +float gl_anisotropy; +static cvar_t gl_anisotropy_cvar = { + .name = "gl_anisotropy", + .description = 0, + .default_value = "1.0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &gl_anisotropy }, +}; +int gl_fb_bmodels; +static cvar_t gl_fb_bmodels_cvar = { + .name = "gl_fb_bmodels", + .description = + "Toggles fullbright color support for bmodels", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &gl_fb_bmodels }, +}; +int gl_finish; +static cvar_t gl_finish_cvar = { + .name = "gl_finish", + .description = + "wait for rendering to finish", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &gl_finish }, +}; +int gl_max_size; +static cvar_t gl_max_size_cvar = { + .name = "gl_max_size", + .description = + "Texture dimension", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &gl_max_size }, +}; +int gl_multitexture; +static cvar_t gl_multitexture_cvar = { + .name = "gl_multitexture", + .description = + "Use multitexture when available.", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &gl_multitexture }, +}; +int gl_tessellate; +static cvar_t gl_tessellate_cvar = { + .name = "gl_tessellate", + .description = 0, + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &gl_tessellate }, +}; +int gl_textures_bgra; +static cvar_t gl_textures_bgra_cvar = { + .name = "gl_textures_bgra", + .description = + "If set to 1, try to use BGR & BGRA textures instead of RGB & RGBA.", + .default_value = "0", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &gl_textures_bgra }, +}; +int gl_vaelements_max; +static cvar_t gl_vaelements_max_cvar = { + .name = "gl_vaelements_max", + .description = + "Limit the vertex array size for buggy drivers. 0 (default) uses " + "driver provided limit, -1 disables use of vertex arrays.", + .default_value = "0", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &gl_vaelements_max }, +}; +int gl_vector_light; +static cvar_t gl_vector_light_cvar = { + .name = "gl_vector_light", + .description = + "Enable use of GL vector lighting. 0 = flat lighting.", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &gl_vector_light }, +}; +int gl_screenshot_byte_swap; +static cvar_t gl_screenshot_byte_swap_cvar = { + .name = "gl_screenshot_byte_swap", + .description = + "Swap the bytes for gl screenshots. Needed if you get screenshots with" + " red and blue swapped.", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &gl_screenshot_byte_swap }, +}; +int gl_affinemodels; +static cvar_t gl_affinemodels_cvar = { + .name = "gl_affinemodels", + .description = + "Makes texture rendering quality better if set to 1", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &gl_affinemodels }, +}; +int gl_clear; +static cvar_t gl_clear_cvar = { + .name = "gl_clear", + .description = + "Set to 1 to make background black. Useful for removing HOM effect", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &gl_clear }, +}; +float gl_conspin; +static cvar_t gl_conspin_cvar = { + .name = "gl_conspin", + .description = + "speed at which the console spins", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &gl_conspin }, +}; +int gl_constretch; +static cvar_t gl_constretch_cvar = { + .name = "gl_constretch", + .description = + "toggle console between slide and stretch", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &gl_constretch }, +}; +int gl_dlight_polyblend; +static cvar_t gl_dlight_polyblend_cvar = { + .name = "gl_dlight_polyblend", + .description = + "Set to 1 to use a dynamic light effect faster on GL", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &gl_dlight_polyblend }, +}; +int gl_dlight_smooth; +static cvar_t gl_dlight_smooth_cvar = { + .name = "gl_dlight_smooth", + .description = + "Smooth dynamic vertex lighting", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &gl_dlight_smooth }, +}; +int gl_fb_models; +static cvar_t gl_fb_models_cvar = { + .name = "gl_fb_models", + .description = + "Toggles fullbright color support for models", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &gl_fb_models }, +}; +int gl_keeptjunctions; +static cvar_t gl_keeptjunctions_cvar = { + .name = "gl_keeptjunctions", + .description = + "Set to 0 to turn off colinear vertexes upon level load.", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &gl_keeptjunctions }, +}; +int gl_lerp_anim; +static cvar_t gl_lerp_anim_cvar = { + .name = "gl_lerp_anim", + .description = + "Toggles model animation interpolation", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &gl_lerp_anim }, +}; +int gl_lightmap_align; +static cvar_t gl_lightmap_align_cvar = { + .name = "gl_lightmap_align", + .description = + "Workaround for nvidia slow path. Set to 4 or 16 if you have an nvidia" + " 3d accelerator, set to 1 otherwise.", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &gl_lightmap_align }, +}; +int gl_lightmap_subimage; +static cvar_t gl_lightmap_subimage_cvar = { + .name = "gl_lightmap_subimage", + .description = + "Lightmap Update method. Default 2 updates a minimum 'dirty rectangle'" + " around the area changed. 1 updates every line that changed. 0 " + "updates the entire lightmap.", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &gl_lightmap_subimage }, +}; +int gl_nocolors; +static cvar_t gl_nocolors_cvar = { + .name = "gl_nocolors", + .description = + "Set to 1, turns off all player colors", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &gl_nocolors }, +}; +int gl_overbright; +static cvar_t gl_overbright_cvar = { + .name = "gl_overbright", + .description = + "Darken lightmaps so that dynamic lights can be overbright. 1 = 0.75 " + "brightness, 2 = 0.5 brightness.", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &gl_overbright }, +}; +int gl_particle_mip; +static cvar_t gl_particle_mip_cvar = { + .name = "gl_particle_mip", + .description = + "Toggles particle texture mipmapping.", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &gl_particle_mip }, +}; +int gl_particle_size; +static cvar_t gl_particle_size_cvar = { + .name = "gl_particle_size", + .description = + "Vertical and horizontal size of particle textures as a power of 2. " + "Default is 5 (32 texel square).", + .default_value = "5", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &gl_particle_size }, +}; +int gl_picmip; +static cvar_t gl_picmip_cvar = { + .name = "gl_picmip", + .description = + "Dimensions of textures. 0 is normal, 1 is half, 2 is 1/4", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &gl_picmip }, +}; +int gl_playermip; +static cvar_t gl_playermip_cvar = { + .name = "gl_playermip", + .description = + "Detail of player skins. 0 best, 4 worst.", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &gl_playermip }, +}; +int gl_reporttjunctions; +static cvar_t gl_reporttjunctions_cvar = { + .name = "gl_reporttjunctions", + .description = + "None", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &gl_reporttjunctions }, +}; +int gl_sky_clip; +static cvar_t gl_sky_clip_cvar = { + .name = "gl_sky_clip", + .description = + "controls amount of sky overdraw", + .default_value = "2", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &gl_sky_clip }, +}; +int gl_sky_debug; +static cvar_t gl_sky_debug_cvar = { + .name = "gl_sky_debug", + .description = + "debugging `info' for sky clipping", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &gl_sky_debug }, +}; +int gl_sky_divide; +static cvar_t gl_sky_divide_cvar = { + .name = "gl_sky_divide", + .description = + "subdivide sky polys", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &gl_sky_divide }, +}; +int gl_sky_multipass; +static cvar_t gl_sky_multipass_cvar = { + .name = "gl_sky_multipass", + .description = + "controls whether the skydome is single or double pass", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &gl_sky_multipass }, +}; +int gl_texsort; +static cvar_t gl_texsort_cvar = { + .name = "gl_texsort", + .description = + "None", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &gl_texsort }, +}; +int gl_triplebuffer; +static cvar_t gl_triplebuffer_cvar = { + .name = "gl_triplebuffer", + .description = + "Set to 1 by default. Fixes status bar flicker on some hardware", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &gl_triplebuffer }, +}; +static int vid_use8bit; +static cvar_t vid_use8bit_cvar = { + .name = "vid_use8bit", + .description = + "Use 8-bit shared palettes.", + .default_value = "0", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &vid_use8bit }, +}; static void -gl_max_size_f (cvar_t *var) +gl_triplebuffer_f (void *data, const cvar_t *cvar) +{ + vid.numpages = gl_triplebuffer ? 3 : 2; +} + +static void +gl_max_size_f (void *data, const cvar_t *cvar) { GLint texSize; - if (!var) + if (!cvar) return; // Check driver's max texture size qfglGetIntegerv (GL_MAX_TEXTURE_SIZE, &texSize); - if (var->int_val < 1) { - Cvar_SetValue (var, texSize); + if (gl_max_size < 1) { + gl_max_size = texSize; } else { - Cvar_SetValue (var, bound (1, var->int_val, texSize)); + gl_max_size = bound (1, gl_max_size, texSize); } } static void -gl_textures_bgra_f (cvar_t *var) +gl_textures_bgra_f (void *data, const cvar_t *cvar) { - if (!var) + if (!cvar) return; - if (var->int_val && gl_bgra_capable) { + if (gl_textures_bgra && gl_bgra_capable) { gl_use_bgra = 1; } else { gl_use_bgra = 0; @@ -185,12 +471,12 @@ gl_textures_bgra_f (cvar_t *var) } static void -gl_fb_bmodels_f (cvar_t *var) +gl_fb_bmodels_f (void *data, const cvar_t *cvar) { - if (!var) + if (!cvar) return; - if (var->int_val && gl_mtex_tmus >= 3) { + if (gl_fb_bmodels && gl_mtex_tmus >= 3) { gl_mtex_fullbright = true; } else { gl_mtex_fullbright = false; @@ -198,16 +484,13 @@ gl_fb_bmodels_f (cvar_t *var) } void -gl_multitexture_f (cvar_t *var) +gl_multitexture_f (void *data, const cvar_t *cvar) { - if (!var) - return; - - if (var->int_val && gl_mtex_capable) { + if (gl_multitexture && gl_mtex_capable) { gl_mtex_active_tmus = gl_mtex_tmus; if (gl_fb_bmodels) { - if (gl_fb_bmodels->int_val) { + if (gl_fb_bmodels) { if (gl_mtex_tmus >= 3) { gl_mtex_fullbright = true; @@ -218,7 +501,7 @@ gl_multitexture_f (cvar_t *var) qfglDisable (GL_TEXTURE_2D); } else { gl_mtex_fullbright = false; - Sys_MaskPrintf (SYS_VID, + Sys_MaskPrintf (SYS_vid, "Not enough TMUs for BSP fullbrights.\n"); } } @@ -230,7 +513,7 @@ gl_multitexture_f (cvar_t *var) qglActiveTexture (gl_mtex_enum + 1); qfglEnable (GL_TEXTURE_2D); if (gl_overbright) { - if (gl_combine_capable && gl_overbright->int_val) { + if (gl_combine_capable && gl_overbright) { qfglTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); qfglTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); qfglTexEnvf (GL_TEXTURE_ENV, GL_RGB_SCALE, gl_rgb_scale); @@ -251,25 +534,25 @@ gl_multitexture_f (cvar_t *var) } static void -gl_screenshot_byte_swap_f (cvar_t *var) +gl_screenshot_byte_swap_f (void *data, const cvar_t *cvar) { - if (var) + if (cvar) qfglPixelStorei (GL_PACK_SWAP_BYTES, - var->int_val ? GL_TRUE : GL_FALSE); + gl_screenshot_byte_swap ? GL_TRUE : GL_FALSE); } static void -gl_anisotropy_f (cvar_t * var) +gl_anisotropy_f (void *data, const cvar_t *var) { if (gl_Anisotropy) { if (var) - gl_aniso = (bound (1.0, var->value, aniso_max)); + gl_aniso = (bound (1.0, gl_anisotropy, aniso_max)); else gl_aniso = 1.0; } else { gl_aniso = 1.0; if (var) - Sys_MaskPrintf (SYS_VID, + Sys_MaskPrintf (SYS_vid, "Anisotropy (GL_EXT_texture_filter_anisotropic) " "is not supported by your hardware and/or " "drivers.\n"); @@ -277,134 +560,87 @@ gl_anisotropy_f (cvar_t * var) } static void -gl_tessellate_f (cvar_t * var) +gl_tessellate_f (void *data, const cvar_t *var) { if (TruForm) { if (var) - gl_tess = (bound (0, var->int_val, tess_max)); + gl_tess = (bound (0, gl_tessellate, tess_max)); else gl_tess = 0; qfglPNTrianglesiATI (GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI, gl_tess); } else { gl_tess = 0; if (var) - Sys_MaskPrintf (SYS_VID, + Sys_MaskPrintf (SYS_vid, "TruForm (GL_ATI_pn_triangles) is not supported " "by your hardware and/or drivers.\n"); } } static void -gl_vaelements_max_f (cvar_t *var) +gl_vaelements_max_f (void *data, const cvar_t *cvar) { - if (var->int_val) - vaelements = min (var->int_val, driver_vaelements); + if (gl_vaelements_max) + vaelements = min (gl_vaelements_max, driver_vaelements); else vaelements = driver_vaelements; } +static void +gl_sky_divide_f (void *data, const cvar_t *cvar) +{ + mod_sky_divide = gl_sky_divide; +} + static void GL_Common_Init_Cvars (void) { - vid_use8bit = Cvar_Get ("vid_use8bit", "0", CVAR_ROM, NULL, "Use 8-bit " - "shared palettes."); - gl_textures_bgra = Cvar_Get ("gl_textures_bgra", "0", CVAR_ROM, - gl_textures_bgra_f, "If set to 1, try to use " - "BGR & BGRA textures instead of RGB & RGBA."); - gl_fb_bmodels = Cvar_Get ("gl_fb_bmodels", "1", CVAR_ARCHIVE, - gl_fb_bmodels_f, "Toggles fullbright color " - "support for bmodels"); - gl_finish = Cvar_Get ("gl_finish", "1", CVAR_ARCHIVE, NULL, - "wait for rendering to finish"); - gl_max_size = Cvar_Get ("gl_max_size", "0", CVAR_NONE, gl_max_size_f, - "Texture dimension"); - gl_multitexture = Cvar_Get ("gl_multitexture", "1", CVAR_ARCHIVE, - gl_multitexture_f, "Use multitexture when " - "available."); - gl_screenshot_byte_swap = - Cvar_Get ("gl_screenshot_byte_swap", "0", CVAR_NONE, - gl_screenshot_byte_swap_f, "Swap the bytes for gl " - "screenshots. Needed if you get screenshots with red and " - "blue swapped."); - gl_anisotropy = - Cvar_Get ("gl_anisotropy", "1.0", CVAR_NONE, gl_anisotropy_f, - nva ("Specifies degree of anisotropy, from 1.0 to %f. " - "Higher anisotropy means less distortion of textures " - "at shallow angles to the viewer.", aniso_max)); - gl_tessellate = - Cvar_Get ("gl_tessellate", "0", CVAR_NONE, gl_tessellate_f, - nva ("Specifies tessellation level from 0 to %i. Higher " - "tessellation level means more triangles.", tess_max)); - gl_vaelements_max = Cvar_Get ("gl_vaelements_max", "0", CVAR_ROM, - gl_vaelements_max_f, - "Limit the vertex array size for buggy " - "drivers. 0 (default) uses driver provided " - "limit, -1 disables use of vertex arrays."); - gl_vector_light = Cvar_Get ("gl_vector_light", "1", CVAR_NONE, NULL, - "Enable use of GL vector lighting. 0 = flat lighting."); - gl_affinemodels = Cvar_Get ("gl_affinemodels", "0", CVAR_ARCHIVE, NULL, - "Makes texture rendering quality better if " - "set to 1"); - gl_clear = Cvar_Get ("gl_clear", "0", CVAR_NONE, NULL, "Set to 1 to make " - "background black. Useful for removing HOM effect"); - gl_conspin = Cvar_Get ("gl_conspin", "0", CVAR_ARCHIVE, NULL, - "speed at which the console spins"); - gl_constretch = Cvar_Get ("gl_constretch", "0", CVAR_ARCHIVE, NULL, - "toggle console between slide and stretch"); - gl_dlight_polyblend = Cvar_Get ("gl_dlight_polyblend", "0", CVAR_ARCHIVE, - NULL, "Set to 1 to use a dynamic light " - "effect faster on GL"); - gl_dlight_smooth = Cvar_Get ("gl_dlight_smooth", "1", CVAR_ARCHIVE, NULL, - "Smooth dynamic vertex lighting"); - gl_fb_models = Cvar_Get ("gl_fb_models", "1", CVAR_ARCHIVE, NULL, - "Toggles fullbright color support for models"); - gl_keeptjunctions = Cvar_Get ("gl_keeptjunctions", "1", CVAR_ARCHIVE, NULL, - "Set to 0 to turn off colinear vertexes " - "upon level load."); - gl_lerp_anim = Cvar_Get ("gl_lerp_anim", "1", CVAR_ARCHIVE, NULL, - "Toggles model animation interpolation"); + Cvar_Register (&vid_use8bit_cvar, 0, 0); + Cvar_Register (&gl_textures_bgra_cvar, gl_textures_bgra_f, 0); + Cvar_Register (&gl_fb_bmodels_cvar, gl_fb_bmodels_f, 0); + Cvar_Register (&gl_finish_cvar, 0, 0); + Cvar_Register (&gl_max_size_cvar, gl_max_size_f, 0); + Cvar_Register (&gl_multitexture_cvar, gl_multitexture_f, 0); + Cvar_Register (&gl_screenshot_byte_swap_cvar, gl_screenshot_byte_swap_f, 0); + Cvar_Register (&gl_anisotropy_cvar, gl_anisotropy_f, 0); + gl_anisotropy_cvar.description = nva ( + "Specifies degree of anisotropy, from 1.0 to %f. Higher anisotropy " + "means less distortion of textures at shallow angles to the viewer.", + aniso_max); + Cvar_Register (&gl_tessellate_cvar, gl_tessellate_f, 0); + gl_tessellate_cvar.description = nva ( + "Specifies tessellation level from 0 to %i. Higher tessellation level " + "means more triangles.", + tess_max); + Cvar_Register (&gl_vaelements_max_cvar, gl_vaelements_max_f, 0); + Cvar_Register (&gl_vector_light_cvar, 0, 0); + Cvar_Register (&gl_affinemodels_cvar, 0, 0); + Cvar_Register (&gl_clear_cvar, 0, 0); + Cvar_Register (&gl_conspin_cvar, 0, 0); + Cvar_Register (&gl_constretch_cvar, 0, 0); + Cvar_Register (&gl_dlight_polyblend_cvar, 0, 0); + Cvar_Register (&gl_dlight_smooth_cvar, 0, 0); + Cvar_Register (&gl_fb_models_cvar, 0, 0); + Cvar_Register (&gl_keeptjunctions_cvar, 0, 0); + Cvar_Register (&gl_lerp_anim_cvar, 0, 0); - gl_lightmap_align = Cvar_Get ("gl_lightmap_align", "1", CVAR_NONE, NULL, - "Workaround for nvidia slow path. Set to 4 " - "or 16 if you have an nvidia 3d " - "accelerator, set to 1 otherwise."); - gl_lightmap_subimage = Cvar_Get ("gl_lightmap_subimage", "1", CVAR_NONE, - NULL, "Lightmap Update method. Default 2 " - "updates a minimum 'dirty rectangle' " - "around the area changed. 1 updates " - "every line that changed. 0 updates the " - "entire lightmap."); - gl_nocolors = Cvar_Get ("gl_nocolors", "0", CVAR_NONE, NULL, - "Set to 1, turns off all player colors"); - gl_overbright = Cvar_Get ("gl_overbright", "0", CVAR_NONE, - gl_overbright_f, "Darken lightmaps so that " - "dynamic lights can be overbright. 1 = 0.75 " - "brightness, 2 = 0.5 brightness."); - gl_particle_mip = Cvar_Get ("gl_particle_mip", "0", CVAR_NONE, NULL, - "Toggles particle texture mipmapping."); - gl_particle_size = Cvar_Get ("gl_particle_size", "5", CVAR_NONE, NULL, - "Vertical and horizontal size of particle " - "textures as a power of 2. Default is 5 " - "(32 texel square)."); - gl_picmip = Cvar_Get ("gl_picmip", "0", CVAR_NONE, NULL, "Dimensions of " - "textures. 0 is normal, 1 is half, 2 is 1/4"); - gl_playermip = Cvar_Get ("gl_playermip", "0", CVAR_NONE, NULL, - "Detail of player skins. 0 best, 4 worst."); - gl_reporttjunctions = Cvar_Get ("gl_reporttjunctions", "0", CVAR_NONE, - NULL, "None"); - gl_sky_clip = Cvar_Get ("gl_sky_clip", "2", CVAR_ARCHIVE, NULL, - "controls amount of sky overdraw"); - gl_sky_debug = Cvar_Get ("gl_sky_debug", "0", CVAR_NONE, NULL, - "debugging `info' for sky clipping"); - gl_sky_divide = Cvar_Get ("gl_sky_divide", "1", CVAR_ARCHIVE, NULL, - "subdivide sky polys"); - gl_sky_multipass = Cvar_Get ("gl_sky_multipass", "1", CVAR_ARCHIVE, NULL, - "controls whether the skydome is single or " - "double pass"); - gl_texsort = Cvar_Get ("gl_texsort", "1", CVAR_NONE, NULL, "None"); - gl_triplebuffer = Cvar_Get ("gl_triplebuffer", "1", CVAR_ARCHIVE, NULL, - "Set to 1 by default. Fixes status bar " - "flicker on some hardware"); + Cvar_Register (&gl_lightmap_align_cvar, 0, 0); + Cvar_Register (&gl_lightmap_subimage_cvar, 0, 0); + Cvar_Register (&gl_nocolors_cvar, 0, 0); + Cvar_Register (&gl_overbright_cvar, gl_overbright_f, 0); + Cvar_Register (&gl_particle_mip_cvar, 0, 0); + Cvar_Register (&gl_particle_size_cvar, 0, 0); + Cvar_Register (&gl_picmip_cvar, 0, 0); + Cvar_Register (&gl_playermip_cvar, 0, 0); + Cvar_Register (&gl_reporttjunctions_cvar, 0, 0); + Cvar_Register (&gl_sky_clip_cvar, 0, 0); + Cvar_Register (&gl_sky_debug_cvar, 0, 0); + Cvar_Register (&gl_sky_divide_cvar, gl_sky_divide_f, 0); + Cvar_Register (&gl_sky_multipass_cvar, 0, 0); + Cvar_Register (&gl_texsort_cvar, 0, 0); + Cvar_Register (&gl_triplebuffer_cvar, gl_triplebuffer_f, 0); + + Cvar_AddListener (&gl_overbright_cvar, gl_multitexture_f, 0); } static void @@ -432,14 +668,14 @@ CheckGLVersionString (void) } else { Sys_Error ("Malformed OpenGL version string!"); } - Sys_MaskPrintf (SYS_VID, "GL_VERSION: %s\n", gl_version); + Sys_MaskPrintf (SYS_vid, "GL_VERSION: %s\n", gl_version); gl_vendor = (char *) qfglGetString (GL_VENDOR); - Sys_MaskPrintf (SYS_VID, "GL_VENDOR: %s\n", gl_vendor); + Sys_MaskPrintf (SYS_vid, "GL_VENDOR: %s\n", gl_vendor); gl_renderer = (char *) qfglGetString (GL_RENDERER); - Sys_MaskPrintf (SYS_VID, "GL_RENDERER: %s\n", gl_renderer); + Sys_MaskPrintf (SYS_vid, "GL_RENDERER: %s\n", gl_renderer); gl_extensions = (char *) qfglGetString (GL_EXTENSIONS); - Sys_MaskPrintf (SYS_VID, "GL_EXTENSIONS: %s\n", gl_extensions); + Sys_MaskPrintf (SYS_vid, "GL_EXTENSIONS: %s\n", gl_extensions); if (strstr (gl_renderer, "Mesa DRI Mach64")) gl_feature_mach64 = true; @@ -474,17 +710,14 @@ CheckCombineExtensions (void) { if (gl_major >= 1 && gl_minor >= 3) { gl_combine_capable = true; - Sys_MaskPrintf (SYS_VID, "COMBINE active, multitextured doublebright " + Sys_MaskPrintf (SYS_vid, "COMBINE active, multitextured doublebright " "enabled.\n"); } else if (QFGL_ExtensionPresent ("GL_ARB_texture_env_combine")) { gl_combine_capable = true; - Sys_MaskPrintf (SYS_VID, "COMBINE_ARB active, multitextured " + Sys_MaskPrintf (SYS_vid, "COMBINE_ARB active, multitextured " "doublebright enabled.\n"); } else { gl_combine_capable = false; - Sys_MaskPrintf (SYS_VID, "GL_ARB_texture_env_combine not found. " - "gl_doublebright will have no effect with " - "gl_multitexture on.\n"); } } @@ -496,15 +729,15 @@ CheckCombineExtensions (void) static void CheckMultiTextureExtensions (void) { - Sys_MaskPrintf (SYS_VID, "Checking for multitexture: "); + Sys_MaskPrintf (SYS_vid, "Checking for multitexture: "); if (COM_CheckParm ("-nomtex")) { - Sys_MaskPrintf (SYS_VID, "disabled.\n"); + Sys_MaskPrintf (SYS_vid, "disabled.\n"); return; } if (gl_major >= 1 && gl_minor >= 3) { qfglGetIntegerv (GL_MAX_TEXTURE_UNITS, &gl_mtex_tmus); if (gl_mtex_tmus >= 2) { - Sys_MaskPrintf (SYS_VID, "enabled, %d TMUs.\n", gl_mtex_tmus); + Sys_MaskPrintf (SYS_vid, "enabled, %d TMUs.\n", gl_mtex_tmus); qglMultiTexCoord2f = QFGL_ExtensionAddress ("glMultiTexCoord2f"); qglMultiTexCoord2fv = @@ -514,16 +747,16 @@ CheckMultiTextureExtensions (void) if (qglMultiTexCoord2f && gl_mtex_enum) gl_mtex_capable = true; else - Sys_MaskPrintf (SYS_VID, "Multitexture disabled, could not " + Sys_MaskPrintf (SYS_vid, "Multitexture disabled, could not " "find required functions\n"); } else { - Sys_MaskPrintf (SYS_VID, + Sys_MaskPrintf (SYS_vid, "Multitexture disabled, not enough TMUs.\n"); } } else if (QFGL_ExtensionPresent ("GL_ARB_multitexture")) { qfglGetIntegerv (GL_MAX_TEXTURE_UNITS_ARB, &gl_mtex_tmus); if (gl_mtex_tmus >= 2) { - Sys_MaskPrintf (SYS_VID, "enabled, %d TMUs.\n", gl_mtex_tmus); + Sys_MaskPrintf (SYS_vid, "enabled, %d TMUs.\n", gl_mtex_tmus); qglMultiTexCoord2f = QFGL_ExtensionAddress ("glMultiTexCoord2fARB"); qglMultiTexCoord2fv = @@ -533,14 +766,14 @@ CheckMultiTextureExtensions (void) if (qglMultiTexCoord2f && gl_mtex_enum) gl_mtex_capable = true; else - Sys_MaskPrintf (SYS_VID, "Multitexture disabled, could not " + Sys_MaskPrintf (SYS_vid, "Multitexture disabled, could not " "find required functions\n"); } else { - Sys_MaskPrintf (SYS_VID, + Sys_MaskPrintf (SYS_vid, "Multitexture disabled, not enough TMUs.\n"); } } else { - Sys_MaskPrintf (SYS_VID, "not found.\n"); + Sys_MaskPrintf (SYS_vid, "not found.\n"); } } @@ -582,7 +815,7 @@ CheckLights (void) specular[4] = {0.1, 0.1, 0.1, 1.0}; qfglGetIntegerv (GL_MAX_LIGHTS, &gl_max_lights); - Sys_MaskPrintf (SYS_VID, "Max GL Lights %d.\n", gl_max_lights); + Sys_MaskPrintf (SYS_vid, "Max GL Lights %d.\n", gl_max_lights); qfglEnable (GL_LIGHTING); qfglLightModelfv (GL_LIGHT_MODEL_AMBIENT, dark); @@ -619,11 +852,11 @@ Tdfx_Init8bitPalette (void) if (!(qgl3DfxSetPaletteEXT = QFGL_ExtensionAddress ("gl3DfxSetPaletteEXT"))) { - Sys_MaskPrintf (SYS_VID, "3DFX_set_global_palette not found.\n"); + Sys_MaskPrintf (SYS_vid, "3DFX_set_global_palette not found.\n"); return; } - Sys_MaskPrintf (SYS_VID, "3DFX_set_global_palette.\n"); + Sys_MaskPrintf (SYS_vid, "3DFX_set_global_palette.\n"); oldpal = (char *) d_8to24table; // d_8to24table3dfx; for (i = 0; i < 256; i++) { @@ -637,7 +870,7 @@ Tdfx_Init8bitPalette (void) qgl3DfxSetPaletteEXT ((GLuint *) table); vr_data.vid->is8bit = true; } else { - Sys_MaskPrintf (SYS_VID, "\n 3DFX_set_global_palette not found."); + Sys_MaskPrintf (SYS_vid, "\n 3DFX_set_global_palette not found."); } } @@ -662,11 +895,11 @@ Shared_Init8bitPalette (void) if (QFGL_ExtensionPresent ("GL_EXT_shared_texture_palette")) { if (!(qglColorTableEXT = QFGL_ExtensionAddress ("glColorTableEXT"))) { - Sys_MaskPrintf (SYS_VID, "glColorTableEXT not found.\n"); + Sys_MaskPrintf (SYS_vid, "glColorTableEXT not found.\n"); return; } - Sys_MaskPrintf (SYS_VID, "GL_EXT_shared_texture_palette\n"); + Sys_MaskPrintf (SYS_vid, "GL_EXT_shared_texture_palette\n"); qfglEnable (GL_SHARED_TEXTURE_PALETTE_EXT); oldPalette = (GLubyte *) d_8to24table; // d_8to24table3dfx; @@ -681,7 +914,7 @@ Shared_Init8bitPalette (void) GL_UNSIGNED_BYTE, (GLvoid *) thePalette); vr_data.vid->is8bit = true; } else { - Sys_MaskPrintf (SYS_VID, + Sys_MaskPrintf (SYS_vid, "\n GL_EXT_shared_texture_palette not found."); } } @@ -689,19 +922,19 @@ Shared_Init8bitPalette (void) static void VID_Init8bitPalette (void) { - Sys_MaskPrintf (SYS_VID, "Checking for 8-bit extension: "); - if (vid_use8bit->int_val) { + Sys_MaskPrintf (SYS_vid, "Checking for 8-bit extension: "); + if (vid_use8bit) { Tdfx_Init8bitPalette (); Shared_Init8bitPalette (); if (!vr_data.vid->is8bit) - Sys_MaskPrintf (SYS_VID, "\n 8-bit extension not found.\n"); + Sys_MaskPrintf (SYS_vid, "\n 8-bit extension not found.\n"); } else { - Sys_MaskPrintf (SYS_VID, "disabled.\n"); + Sys_MaskPrintf (SYS_vid, "disabled.\n"); } } void -GL_SetPalette (const byte *palette) +GL_SetPalette (void *data, const byte *palette) { const byte *pal; char s[255]; @@ -710,7 +943,7 @@ GL_SetPalette (const byte *palette) unsigned int r, g, b, v; unsigned short i; unsigned int *table; - static qboolean palflag = false; + static bool palflag = false; QFile *f; static int inited_8 = 0; @@ -720,7 +953,7 @@ GL_SetPalette (const byte *palette) VID_Init8bitPalette (); } // 8 8 8 encoding - Sys_MaskPrintf (SYS_VID, "Converting 8to24\n"); + Sys_MaskPrintf (SYS_vid, "Converting 8to24\n"); pal = palette; table = d_8to24table; @@ -798,6 +1031,8 @@ GL_Init_Common (void) GL_Common_Init_Cvars (); + GL_TextureInit (); + qfglClearColor (0, 0, 0, 0); qfglEnable (GL_TEXTURE_2D); diff --git a/libs/video/renderer/gl/vtxarray.c b/libs/video/renderer/gl/vtxarray.c index 78e245a55..dda46974b 100644 --- a/libs/video/renderer/gl/vtxarray.c +++ b/libs/video/renderer/gl/vtxarray.c @@ -445,7 +445,6 @@ qfgl_InterleavedArrays (GLenum format, GLsizei stride, const GLvoid *pointer) case GL_C4UB_V2F: qfgl_VertexPointer (2, GL_FLOAT, stride - 2 * sizeof (GLfloat), ptr); - ptr += 2 * sizeof (GLfloat); break; case GL_V3F: case GL_C4UB_V3F: @@ -459,13 +458,11 @@ qfgl_InterleavedArrays (GLenum format, GLsizei stride, const GLvoid *pointer) case GL_T2F_C4F_N3F_V3F: qfgl_VertexPointer (3, GL_FLOAT, stride - 3 * sizeof (GLfloat), ptr); - ptr += 3 * sizeof (GLfloat); break; case GL_T4F_V4F: case GL_T4F_C4F_N3F_V4F: qfgl_VertexPointer (4, GL_FLOAT, stride - 4 * sizeof (GLfloat), ptr); - ptr += 4 * sizeof (GLfloat); break; default: break; diff --git a/libs/video/renderer/glsl/Makefile.am b/libs/video/renderer/glsl/Makefile.am deleted file mode 100644 index 71c9d37e2..000000000 --- a/libs/video/renderer/glsl/Makefile.am +++ /dev/null @@ -1,28 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CFLAGS= @PREFER_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include $(GLX_CFLAGS) - -shader_src= sgustavson.glsl quakeforge.glsl -shader_gen= sgustavson.slc quakeforge.slc - -glsl_src = \ - glsl_alias.c glsl_bsp.c glsl_draw.c glsl_fog.c glsl_iqm.c glsl_lightmap.c \ - glsl_main.c glsl_particles.c glsl_screen.c glsl_shader.c glsl_sprite.c \ - glsl_textures.c qfglsl.c vid_common_glsl.c - -noinst_LTLIBRARIES= libglsl.la -BUILT_SOURCES= $(shader_gen) - -SUFFICES=.frag .vert .fc .vc .slc .glsl -.glsl.slc: - sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/\\n"/' $< > $@ -.frag.fc: - sed -e 's/^/"/' -e 's/$$/\\n"/' $< > $@ -.vert.vc: - sed -e 's/^/"/' -e 's/$$/\\n"/' $< > $@ - -libglsl_la_SOURCES= $(glsl_src) - -EXTRA_DIST = $(glsl_src) $(shader_src) namehack.h -CLEANFILES= *.vc *.fc *.slc diff --git a/libs/video/renderer/glsl/glsl_alias.c b/libs/video/renderer/glsl/glsl_alias.c index 3fdd50b66..002d78b17 100644 --- a/libs/video/renderer/glsl/glsl_alias.c +++ b/libs/video/renderer/glsl/glsl_alias.c @@ -31,9 +31,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -47,6 +44,8 @@ #include "QF/skin.h" #include "QF/sys.h" +#include "QF/scene/entity.h" + #include "QF/GLSL/defines.h" #include "QF/GLSL/funcs.h" #include "QF/GLSL/qf_alias.h" @@ -111,7 +110,7 @@ static struct { {"fog", 1}, }; -static mat4_t alias_vp; +static mat4f_t alias_vp; void glsl_R_InitAlias (void) @@ -150,7 +149,7 @@ glsl_R_InitAlias (void) } static void -calc_lighting (entity_t *ent, float *ambient, float *shadelight, +calc_lighting (entity_t ent, float *ambient, float *shadelight, vec3_t lightvec) { unsigned i; @@ -158,14 +157,19 @@ calc_lighting (entity_t *ent, float *ambient, float *shadelight, vec3_t dist; int light; + transform_t transform = Entity_Transform (ent); + vec4f_t entorigin = Transform_GetWorldPosition (transform); + VectorSet ( -1, 0, 0, lightvec); //FIXME - light = R_LightPoint (ent->origin); - *ambient = max (light, max (ent->model->min_light, ent->min_light) * 128); + light = R_LightPoint (&r_refdef.worldmodel->brush, entorigin); + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); + *ambient = max (light, max (renderer->model->min_light, + renderer->min_light) * 128); *shadelight = *ambient; for (i = 0; i < r_maxdlights; i++) { if (r_dlights[i].die >= vr_data.realtime) { - VectorSubtract (ent->origin, r_dlights[i].origin, dist); + VectorSubtract (entorigin, r_dlights[i].origin, dist); add = r_dlights[i].radius - VectorLength (dist); if (add > 0) *ambient += add; @@ -183,7 +187,7 @@ set_arrays (const shaderparam_t *vert, const shaderparam_t *norm, { byte *pose_offs = (byte *) pose; - if (developer->int_val & SYS_GLSL) { + if (developer & SYS_glsl) { GLint size; qfeglGetBufferParameteriv (GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &size); @@ -205,7 +209,7 @@ set_arrays (const shaderparam_t *vert, const shaderparam_t *norm, } //#define TETRAHEDRON void -glsl_R_DrawAlias (void) +glsl_R_DrawAlias (entity_t ent) { #ifdef TETRAHEDRON static aliasvrt_t debug_verts[] = { @@ -227,51 +231,56 @@ glsl_R_DrawAlias (void) float shadelight; float skin_size[2]; float blend; - entity_t *ent = currententity; - model_t *model = ent->model; aliashdr_t *hdr; vec_t norm_mat[9]; - mat4_t mvp_mat; int skin_tex; int colormap; aliasvrt_t *pose1 = 0; // VBO's are null based aliasvrt_t *pose2 = 0; // VBO's are null based - - if (!(hdr = model->aliashdr)) - hdr = Cache_Get (&model->cache); + mat4f_t worldMatrix; calc_lighting (ent, &ambient, &shadelight, lightvec); + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); + model_t *model = renderer->model; + if (!(hdr = model->aliashdr)) + hdr = Cache_Get (&model->cache); + + transform_t transform = Entity_Transform (ent); + Transform_GetWorldMatrix (transform, worldMatrix); // we need only the rotation for normals. - VectorCopy (ent->transform + 0, norm_mat + 0); - VectorCopy (ent->transform + 4, norm_mat + 3); - VectorCopy (ent->transform + 8, norm_mat + 6); + VectorCopy (worldMatrix[0], norm_mat + 0); + VectorCopy (worldMatrix[1], norm_mat + 3); + VectorCopy (worldMatrix[2], norm_mat + 6); // ent model scaling and offset - Mat4Zero (mvp_mat); - mvp_mat[0] = hdr->mdl.scale[0]; - mvp_mat[5] = hdr->mdl.scale[1]; - mvp_mat[10] = hdr->mdl.scale[2]; - mvp_mat[15] = 1; - VectorCopy (hdr->mdl.scale_origin, mvp_mat + 12); - Mat4Mult (ent->transform, mvp_mat, mvp_mat); - Mat4Mult (alias_vp, mvp_mat, mvp_mat); + mat4f_t mvp_mat = { + { hdr->mdl.scale[0], 0, 0, 0 }, + { 0, hdr->mdl.scale[1], 0, 0 }, + { 0, 0, hdr->mdl.scale[2], 0 }, + { hdr->mdl.scale_origin[0], hdr->mdl.scale_origin[1], + hdr->mdl.scale_origin[2], 1 }, + }; + mmulf (mvp_mat, worldMatrix, mvp_mat); + mmulf (mvp_mat, alias_vp, mvp_mat); + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, + ent.reg); colormap = glsl_colormap; - if (ent->skin && ent->skin->auxtex) - colormap = ent->skin->auxtex; - if (ent->skin && ent->skin->texnum) { - skin_t *skin = ent->skin; + if (renderer->skin && renderer->skin->auxtex) + colormap = renderer->skin->auxtex; + if (renderer->skin && renderer->skin->texnum) { + skin_t *skin = renderer->skin; skin_tex = skin->texnum; } else { maliasskindesc_t *skindesc; - skindesc = R_AliasGetSkindesc (ent->skinnum, hdr); + skindesc = R_AliasGetSkindesc (animation, renderer->skinnum, hdr); skin_tex = skindesc->texnum; } - blend = R_AliasGetLerpedFrames (ent, hdr); + blend = R_AliasGetLerpedFrames (animation, hdr); - pose1 += ent->pose1 * hdr->poseverts; - pose2 += ent->pose2 * hdr->poseverts; + pose1 += animation->pose1 * hdr->poseverts; + pose2 += animation->pose2 * hdr->poseverts; skin_size[0] = hdr->mdl.skinwidth; skin_size[1] = hdr->mdl.skinheight; @@ -293,7 +302,8 @@ glsl_R_DrawAlias (void) qfeglUniform1f (quake_mdl.shadelight.location, shadelight); qfeglUniform3fv (quake_mdl.lightvec.location, 1, lightvec); qfeglUniform2fv (quake_mdl.skin_size.location, 1, skin_size); - qfeglUniformMatrix4fv (quake_mdl.mvp_matrix.location, 1, false, mvp_mat); + qfeglUniformMatrix4fv (quake_mdl.mvp_matrix.location, 1, false, + (vec_t*)&mvp_mat[0]);//FIXME qfeglUniformMatrix3fv (quake_mdl.norm_matrix.location, 1, false, norm_mat); #ifndef TETRAHEDRON @@ -321,7 +331,7 @@ glsl_R_AliasBegin (void) quat_t fog; // pre-multiply the view and projection matricies - Mat4Mult (glsl_projection, glsl_view, alias_vp); + mmulf (alias_vp, glsl_projection, glsl_view); qfeglUseProgram (quake_mdl.program); qfeglEnableVertexAttribArray (quake_mdl.vertexa.location); @@ -333,8 +343,8 @@ glsl_R_AliasBegin (void) qfeglDisableVertexAttribArray (quake_mdl.colora.location); qfeglDisableVertexAttribArray (quake_mdl.colorb.location); - VectorCopy (glsl_Fog_GetColor (), fog); - fog[3] = glsl_Fog_GetDensity () / 64.0; + Fog_GetColor (fog); + fog[3] = Fog_GetDensity () / 64.0; qfeglUniform4fv (quake_mdl.fog.location, 1, fog); qfeglUniform1i (quake_mdl.colormap.location, 1); diff --git a/libs/video/renderer/glsl/glsl_bsp.c b/libs/video/renderer/glsl/glsl_bsp.c index e52402660..6d16857e8 100644 --- a/libs/video/renderer/glsl/glsl_bsp.c +++ b/libs/video/renderer/glsl/glsl_bsp.c @@ -31,9 +31,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -50,7 +47,8 @@ #include "QF/render.h" #include "QF/sys.h" #include "QF/va.h" -#include "QF/vrect.h" + +#include "QF/scene/entity.h" #include "QF/GLSL/defines.h" #include "QF/GLSL/funcs.h" @@ -73,7 +71,7 @@ static instsurf_t **waterchain_tail = &waterchain; static instsurf_t *sky_chain; static instsurf_t **sky_chain_tail = &sky_chain; -static texture_t **r_texture_chains; +static glsltex_t **r_texture_chains; static int r_num_texture_chains; static int max_texture_chains; @@ -94,10 +92,10 @@ static instsurf_t **instsurfs_tail = &instsurfs; static instsurf_t *free_instsurfs; static GLuint bsp_vbo; -static mat4_t bsp_vp; +static mat4f_t bsp_vp; static GLuint skybox_tex; -static qboolean skybox_loaded; +static bool skybox_loaded; static quat_t sky_rotation[2]; static quat_t sky_velocity; static quat_t sky_fix; @@ -105,6 +103,7 @@ static double sky_time; static quat_t default_color = { 1, 1, 1, 1 }; static quat_t last_color; +static glsltex_t glsl_notexture = { }; static const char *bsp_vert_effects[] = { @@ -237,26 +236,36 @@ static struct { shaderparam_t *fog; } sky_params; +typedef struct glslbspctx_s { + mod_brush_t *brush; + animation_t *animation; + vec4f_t *transform; + float *color; +} glslbspctx_t; + + #define CHAIN_SURF_F2B(surf,chain) \ - do { \ + ({ \ instsurf_t *inst = (surf)->instsurf; \ if (__builtin_expect(!inst, 1)) \ - (surf)->tinst = inst = get_instsurf (); \ + inst = get_instsurf (); \ inst->surface = (surf); \ *(chain##_tail) = inst; \ (chain##_tail) = &inst->tex_chain; \ *(chain##_tail) = 0; \ - } while (0) + inst; \ + }) #define CHAIN_SURF_B2F(surf,chain) \ - do { \ + ({ \ instsurf_t *inst = (surf)->instsurf; \ if (__builtin_expect(!inst, 1)) \ - (surf)->tinst = inst = get_instsurf (); \ + inst = get_instsurf (); \ inst->surface = (surf); \ inst->tex_chain = (chain); \ (chain) = inst; \ - } while (0) + inst; \ + }) #define GET_RELEASE(type,name) \ static inline type * \ @@ -292,17 +301,18 @@ GET_RELEASE (elements_t, elements) GET_RELEASE (instsurf_t, static_instsurf) GET_RELEASE (instsurf_t, instsurf) -void -glsl_R_AddTexture (texture_t *tex) +static void +glsl_R_AddTexture (texture_t *tx) { int i; if (r_num_texture_chains == max_texture_chains) { max_texture_chains += 64; r_texture_chains = realloc (r_texture_chains, - max_texture_chains * sizeof (texture_t *)); + max_texture_chains * sizeof (glsltex_t *)); for (i = r_num_texture_chains; i < max_texture_chains; i++) r_texture_chains[i] = 0; } + glsltex_t *tex = tx->render; r_texture_chains[r_num_texture_chains++] = tex; tex->tex_chain = 0; tex->tex_chain_tail = &tex->tex_chain; @@ -310,22 +320,20 @@ glsl_R_AddTexture (texture_t *tex) tex->elechain_tail = &tex->elechain; } -void -glsl_R_InitSurfaceChains (model_t *model) +static void +glsl_R_InitSurfaceChains (mod_brush_t *brush) { - int i; - release_static_instsurfs (); release_instsurfs (); - for (i = 0; i < model->nummodelsurfaces; i++) { - model->surfaces[i].instsurf = get_static_instsurf (); - model->surfaces[i].instsurf->surface = &model->surfaces[i]; + for (unsigned i = 0; i < brush->nummodelsurfaces; i++) { + brush->surfaces[i].instsurf = get_static_instsurf (); + brush->surfaces[i].instsurf->surface = &brush->surfaces[i]; } } static inline void -clear_tex_chain (texture_t *tex) +clear_tex_chain (glsltex_t *tex) { tex->tex_chain = 0; tex->tex_chain_tail = &tex->tex_chain; @@ -343,7 +351,7 @@ clear_texture_chains (void) continue; clear_tex_chain (r_texture_chains[i]); } - clear_tex_chain (r_notexture_mip); + clear_tex_chain (r_notexture_mip->render); release_elechains (); release_elementss (); release_instsurfs (); @@ -357,7 +365,7 @@ glsl_R_ClearElements (void) } static void -update_lightmap (msurface_t *surf) +update_lightmap (glslbspctx_t *bctx, msurface_t *surf) { int maps; @@ -367,52 +375,52 @@ update_lightmap (msurface_t *surf) if ((surf->dlightframe == r_framecount) || surf->cached_dlight) { dynamic: - if (r_dynamic->int_val) - glsl_R_BuildLightMap (surf); + if (r_dynamic) + glsl_R_BuildLightMap (bctx->transform, bctx->brush, surf); } } static inline void -chain_surface (msurface_t *surf, vec_t *transform, float *color) +chain_surface (glslbspctx_t *bctx, msurface_t *surf) { instsurf_t *is; if (surf->flags & SURF_DRAWSKY) { - CHAIN_SURF_F2B (surf, sky_chain); - } else if ((surf->flags & SURF_DRAWTURB) || (color && color[3] < 1.0)) { - CHAIN_SURF_B2F (surf, waterchain); + is = CHAIN_SURF_F2B (surf, sky_chain); + } else if ((surf->flags & SURF_DRAWTURB) + || (bctx->color && bctx->color[3] < 1.0)) { + is = CHAIN_SURF_B2F (surf, waterchain); } else { - texture_t *tex; + texture_t *tx; + glsltex_t *tex; if (!surf->texinfo->texture->anim_total) - tex = surf->texinfo->texture; + tx = surf->texinfo->texture; else - tex = R_TextureAnimation (surf); - CHAIN_SURF_F2B (surf, tex->tex_chain); + tx = R_TextureAnimation (bctx->animation->frame, surf); + tex = tx->render; + is = CHAIN_SURF_F2B (surf, tex->tex_chain); - update_lightmap (surf); + update_lightmap (bctx, surf); } - if (!(is = surf->instsurf)) - is = surf->tinst; - is->transform = transform; - is->color = color; + is->transform = bctx->transform; + is->color = bctx->color; } static void -register_textures (model_t *model) +register_textures (mod_brush_t *brush) { - int i; texture_t *tex; - for (i = 0; i < model->numtextures; i++) { - tex = model->textures[i]; + for (unsigned i = 0; i < brush->numtextures; i++) { + tex = brush->textures[i]; if (!tex) continue; glsl_R_AddTexture (tex); } } -void +static void glsl_R_ClearTextures (void) { r_num_texture_chains = 0; @@ -423,34 +431,36 @@ glsl_R_RegisterTextures (model_t **models, int num_models) { int i; model_t *m; + mod_brush_t *brush; glsl_R_ClearTextures (); - glsl_R_InitSurfaceChains (r_worldentity.model); + glsl_R_InitSurfaceChains (&r_refdef.worldmodel->brush); glsl_R_AddTexture (r_notexture_mip); - register_textures (r_worldentity.model); + register_textures (&r_refdef.worldmodel->brush); for (i = 0; i < num_models; i++) { m = models[i]; if (!m) continue; // sub-models are done as part of the main model - if (*m->name == '*') + if (*m->path == '*') continue; // world has already been done, not interested in non-brush models - if (m == r_worldentity.model || m->type != mod_brush) + if (m == r_refdef.worldmodel || m->type != mod_brush) continue; - m->numsubmodels = 1; // no support for submodels in non-world model - register_textures (m); + brush = &m->brush; + brush->numsubmodels = 1; // no support for submodels in non-world model + register_textures (brush); } } static elechain_t * -add_elechain (texture_t *tex, int ec_index) +add_elechain (glsltex_t *tex, int model_index) { elechain_t *ec; ec = get_elechain (); ec->elements = get_elements (); - ec->index = ec_index; + ec->model_index = model_index; ec->transform = 0; ec->color = 0; *tex->elechain_tail = ec; @@ -459,76 +469,72 @@ add_elechain (texture_t *tex, int ec_index) } static void -build_surf_displist (model_t **models, msurface_t *fa, int base, +build_surf_displist (model_t **models, msurface_t *surf, int base, dstring_t *vert_list) { - int numverts; - int numtris; - int numindices; - int i; - vec_t *vec; - mvertex_t *vertices; - medge_t *edges; - int *surfedges; - int index; - bspvert_t *verts; - glslpoly_t *poly; - GLushort *ind; - float s, t; - - if (fa->ec_index < 0) { - vertices = models[-fa->ec_index - 1]->vertexes; - edges = models[-fa->ec_index - 1]->edges; - surfedges = models[-fa->ec_index - 1]->surfedges; + mod_brush_t *brush; + if (surf->model_index < 0) { + brush = &models[-surf->model_index - 1]->brush; } else { - vertices = r_worldentity.model->vertexes; - edges = r_worldentity.model->edges; - surfedges = r_worldentity.model->surfedges; + brush = &r_refdef.worldmodel->brush; } - numverts = fa->numedges; - numtris = numverts - 2; - numindices = numtris * 3; - verts = alloca (numverts * sizeof (bspvert_t)); - poly = malloc (field_offset (glslpoly_t, indices[numindices])); + mvertex_t *vertices = brush->vertexes; + medge_t *edges = brush->edges; + int *surfedges = brush->surfedges; + + // create triangle soup for the polygon (this was written targeting + // GLES 2, which didn't have primitive restart) + int numverts = surf->numedges; + int numtris = numverts - 2; + int numindices = numtris * 3; + glslpoly_t *poly = malloc (field_offset (glslpoly_t, indices[numindices])); poly->count = numindices; - for (i = 0, ind = poly->indices; i < numtris; i++) { - *ind++ = base; - *ind++ = base + i + 1; - *ind++ = base + i + 2; + for (int i = 0, ind = 0; i < numtris; i++) { + // pretend we can use a triangle fan + poly->indices[ind++] = base; + poly->indices[ind++] = base + i + 1; + poly->indices[ind++] = base + i + 2; } - fa->polys = (glpoly_t *) poly; + surf->polys = (glpoly_t *) poly; - for (i = 0; i < numverts; i++) { - index = surfedges[fa->firstedge + i]; - if (index > 0) + bspvert_t *verts = alloca (numverts * sizeof (bspvert_t)); + mtexinfo_t *texinfo = surf->texinfo; + for (int i = 0; i < numverts; i++) { + vec_t *vec; + int index = surfedges[surf->firstedge + i]; + if (index > 0) { + // forward edge vec = vertices[edges[index].v[0]].position; - else + } else { + // reverse edge vec = vertices[edges[-index].v[1]].position; - - s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; - t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; + } VectorCopy (vec, verts[i].vertex); - verts[i].vertex[3] = 1; - verts[i].tlst[0] = s / fa->texinfo->texture->width; - verts[i].tlst[1] = t / fa->texinfo->texture->height; + verts[i].vertex[3] = 1; // homogeneous coord - //lightmap texture coordinates - if (!fa->lightpic) { - // sky and water textures don't have lightmaps + vec2f_t st = { + DotProduct (vec, texinfo->vecs[0]) + texinfo->vecs[0][3], + DotProduct (vec, texinfo->vecs[1]) + texinfo->vecs[1][3], + }; + verts[i].tlst[0] = st[0] / texinfo->texture->width; + verts[i].tlst[1] = st[1] / texinfo->texture->height; + + if (surf->lightpic) { + //lightmap texture coordinates + //every lit surface has its own lighmap at a 1/16 resolution + //(ie, 16 albedo pixels for every lightmap pixel) + const vrect_t *rect = surf->lightpic->rect; + vec2f_t lmorg = (vec2f_t) { VEC2_EXP (&rect->x) } * 16 + 8; + vec2f_t texorg = { VEC2_EXP (surf->texturemins) }; + st = ((st - texorg + lmorg) / 16) * surf->lightpic->size; + verts[i].tlst[2] = st[0]; + verts[i].tlst[3] = st[1]; + } else { + // no lightmap for this surface (probably sky or water), so + // make the lightmap texture polygone degenerate verts[i].tlst[2] = 0; verts[i].tlst[3] = 0; - continue; } - s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; - t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; - s -= fa->texturemins[0]; - t -= fa->texturemins[1]; - s += fa->lightpic->rect->x * 16 + 8; - t += fa->lightpic->rect->y * 16 + 8; - s /= 16; - t /= 16; - verts[i].tlst[2] = s * fa->lightpic->size; - verts[i].tlst[3] = t * fa->lightpic->size; } dstring_append (vert_list, (char *) verts, numverts * sizeof (bspvert_t)); } @@ -536,15 +542,15 @@ build_surf_displist (model_t **models, msurface_t *fa, int base, void glsl_R_BuildDisplayLists (model_t **models, int num_models) { - int i, j; int vertex_index_base; model_t *m; dmodel_t *dm; msurface_t *surf; dstring_t *vertices; + mod_brush_t *brush; - QuatSet (sqrt(0.5), 0, 0, sqrt(0.5), sky_fix); // proper skies - QuatSet (1, 0, 0, 0, sky_rotation[0]); + QuatSet (0, 0, sqrt(0.5), sqrt(0.5), sky_fix); // proper skies + QuatSet (0, 0, 0, 1, sky_rotation[0]); QuatCopy (sky_rotation[0], sky_rotation[1]); QuatSet (0, 0, 0, 0, sky_velocity); QuatExp (sky_velocity, sky_velocity); @@ -553,32 +559,33 @@ glsl_R_BuildDisplayLists (model_t **models, int num_models) // now run through all surfaces, chaining them to their textures, thus // effectively sorting the surfaces by texture (without worrying about // surface order on the same texture chain). - for (i = 0; i < num_models; i++) { + for (int i = 0; i < num_models; i++) { m = models[i]; if (!m) continue; // sub-models are done as part of the main model - if (*m->name == '*') + if (*m->path == '*' || m->type != mod_brush) continue; + brush = &m->brush; // non-bsp models don't have surfaces. - dm = m->submodels; - for (j = 0; j < m->numsurfaces; j++) { - texture_t *tex; + dm = brush->submodels; + for (uint32_t j = 0; j < brush->numsurfaces; j++) { + glsltex_t *tex; if (j == dm->firstface + dm->numfaces) { dm++; - if (dm - m->submodels == m->numsubmodels) { + if (dm == brush->submodels + brush->numsubmodels) { // limit the surfaces // probably never hit Sys_Printf ("R_BuildDisplayLists: too many surfaces\n"); - m->numsurfaces = j; + brush->numsurfaces = j; break; } } - surf = m->surfaces + j; - surf->ec_index = dm - m->submodels; - if (!surf->ec_index && m != r_worldentity.model) - surf->ec_index = -1 - i; // instanced model - tex = surf->texinfo->texture; + surf = brush->surfaces + j; + surf->model_index = dm - brush->submodels; + if (!surf->model_index && m != r_refdef.worldmodel) + surf->model_index = -1 - i; // instanced model + tex = surf->texinfo->texture->render; CHAIN_SURF_F2B (surf, tex->tex_chain); } } @@ -589,8 +596,8 @@ glsl_R_BuildDisplayLists (model_t **models, int num_models) // Run through the textures, using their chains to build display maps. // For animated textures, if a surface is on one texture of the group, it // will be on all. - for (i = 0; i < r_num_texture_chains; i++) { - texture_t *tex; + for (int i = 0; i < r_num_texture_chains; i++) { + glsltex_t *tex; instsurf_t *is; elechain_t *ec = 0; elements_t *el = 0; @@ -600,13 +607,13 @@ glsl_R_BuildDisplayLists (model_t **models, int num_models) for (is = tex->tex_chain; is; is = is->tex_chain) { msurface_t *surf = is->surface; if (!tex->elechain) { - ec = add_elechain (tex, surf->ec_index); + ec = add_elechain (tex, surf->model_index); el = ec->elements; el->base = (byte *) (intptr_t) vertices->size; vertex_index_base = 0; } - if (surf->ec_index != ec->index) { // next sub-model - ec = add_elechain (tex, surf->ec_index); + if (surf->model_index != ec->model_index) { // next sub-model + ec = add_elechain (tex, surf->model_index); el = ec->elements; el->base = (byte *) (intptr_t) vertices->size; vertex_index_base = 0; @@ -629,7 +636,7 @@ glsl_R_BuildDisplayLists (model_t **models, int num_models) } } clear_texture_chains (); - Sys_MaskPrintf (SYS_GLSL, "R_BuildDisplayLists: %ld verts total\n", + Sys_MaskPrintf (SYS_glsl, "R_BuildDisplayLists: %ld verts total\n", (long) (vertices->size / sizeof (bspvert_t))); if (!bsp_vbo) qfeglGenBuffers (1, &bsp_vbo); @@ -641,59 +648,70 @@ glsl_R_BuildDisplayLists (model_t **models, int num_models) } static void -R_DrawBrushModel (entity_t *e) +R_DrawBrushModel (entity_t e) { float dot, radius; - int i; unsigned k; - model_t *model; + renderer_t *renderer = Ent_GetComponent (e.id, scene_renderer, e.reg); + model_t *model = renderer->model; + mod_brush_t *brush = &model->brush; plane_t *plane; msurface_t *surf; - qboolean rotated; - vec3_t mins, maxs, org; + bool rotated; + vec3_t mins, maxs; + vec4f_t org; + glslbspctx_t bctx = { + brush, + Ent_GetComponent (e.id, scene_animation, e.reg), + renderer->full_transform, + renderer->colormod, + }; - model = e->model; - if (e->transform[0] != 1 || e->transform[5] != 1 || e->transform[10] != 1) { + mat4f_t mat; + transform_t transform = Entity_Transform (e); + Transform_GetWorldMatrix (transform, mat); + memcpy (renderer->full_transform, mat, sizeof (mat));//FIXME + if (mat[0][0] != 1 || mat[1][1] != 1 || mat[2][2] != 1) { rotated = true; radius = model->radius; - if (R_CullSphere (e->origin, radius)) + if (R_CullSphere (r_refdef.frustum, (vec_t*)&mat[3], radius)) { // FIXME return; + } } else { rotated = false; - VectorAdd (e->origin, model->mins, mins); - VectorAdd (e->origin, model->maxs, maxs); - if (R_CullBox (mins, maxs)) + VectorAdd (mat[3], model->mins, mins); + VectorAdd (mat[3], model->maxs, maxs); + if (R_CullBox (r_refdef.frustum, mins, maxs)) return; } - VectorSubtract (r_refdef.vieworg, e->origin, org); + org = r_refdef.frame.position - mat[3]; if (rotated) { - vec3_t temp; + vec4f_t temp = org; - VectorCopy (org, temp); - org[0] = DotProduct (temp, e->transform + 0); - org[1] = DotProduct (temp, e->transform + 4); - org[2] = DotProduct (temp, e->transform + 8); + org[0] = dotf (temp, mat[0])[0]; + org[1] = dotf (temp, mat[1])[0]; + org[2] = dotf (temp, mat[2])[0]; } // calculate dynamic lighting for bmodel if it's not an instanced model - if (model->firstmodelsurface != 0 && r_dlight_lightmap->int_val) { - vec3_t lightorigin; - + if (brush->firstmodelsurface != 0 && r_dlight_lightmap) { for (k = 0; k < r_maxdlights; k++) { if ((r_dlights[k].die < vr_data.realtime) || (!r_dlights[k].radius)) continue; - VectorSubtract (r_dlights[k].origin, e->origin, lightorigin); - R_RecursiveMarkLights (lightorigin, &r_dlights[k], k, - model->nodes + model->hulls[0].firstclipnode); + vec4f_t lightorigin; + VectorSubtract (r_dlights[k].origin, mat[3], lightorigin); + lightorigin[3] = 1; + R_RecursiveMarkLights (brush, lightorigin, &r_dlights[k], k, + brush->hulls[0].firstclipnode); } } - surf = &model->surfaces[model->firstmodelsurface]; + surf = &brush->surfaces[brush->firstmodelsurface]; - for (i = 0; i < model->nummodelsurfaces; i++, surf++) { + for (unsigned i = 0; i < brush->nummodelsurfaces; i++, surf++) { // find the node side on which we are plane = surf->plane; @@ -702,7 +720,7 @@ R_DrawBrushModel (entity_t *e) // enqueue the polygon if (((surf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || (!(surf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { - chain_surface (surf, e->transform, e->colormod); + chain_surface (&bctx, surf); } } } @@ -715,108 +733,122 @@ visit_leaf (mleaf_t *leaf) R_StoreEfrags (leaf->efrags); } +// 1 = back side, 0 = front side static inline int get_side (mnode_t *node) { // find the node side on which we are - plane_t *plane = node->plane; + vec4f_t org = r_refdef.frame.position; - if (plane->type < 3) - return (r_origin[plane->type] - plane->dist) < 0; - return (DotProduct (r_origin, plane->normal) - plane->dist) < 0; + return dotf (org, node->plane)[0] < 0; } static inline void -visit_node (mnode_t *node, int side) +visit_node (glslbspctx_t *bctx, mnode_t *node, int side) { int c; msurface_t *surf; // sneaky hack for side = side ? SURF_PLANEBACK : 0; - side = (~side + 1) & SURF_PLANEBACK; - // draw stuff + // seems to be microscopically faster even on modern hardware + side = (-side) & SURF_PLANEBACK; + // chain any visible surfaces on the node that face the camera. + // not all nodes have any surfaces to draw (purely a split plane) if ((c = node->numsurfaces)) { - surf = r_worldentity.model->surfaces + node->firstsurface; - for (; c; c--, surf++) { - if (surf->visframe != r_visframecount) + int surf_id = node->firstsurface; + surf = bctx->brush->surfaces + surf_id; + for (; c; c--, surf++, surf_id++) { + if (r_face_visframes[surf_id] != r_visframecount) continue; // side is either 0 or SURF_PLANEBACK + // if side and the surface facing differ, then the camera is + // on backside of the surface if (side ^ (surf->flags & SURF_PLANEBACK)) continue; // wrong side - chain_surface (surf, 0, 0); + chain_surface (bctx, surf); } } } static inline int -test_node (mnode_t *node) +test_node (glslbspctx_t *bctx, int node_id) { - if (node->contents < 0) + if (node_id < 0) return 0; - if (node->visframe != r_visframecount) + if (r_node_visframes[node_id] != r_visframecount) return 0; - if (R_CullBox (node->minmaxs, node->minmaxs + 3)) + mnode_t *node = bctx->brush->nodes + node_id; + if (R_CullBox (r_refdef.frustum, node->minmaxs, node->minmaxs + 3)) return 0; return 1; } static void -R_VisitWorldNodes (model_t *model) +R_VisitWorldNodes (glslbspctx_t *bctx) { typedef struct { - mnode_t *node; + int node_id; int side; } rstack_t; + mod_brush_t *brush = bctx->brush; rstack_t *node_ptr; rstack_t *node_stack; - mnode_t *node; - mnode_t *front; + int node_id; + int front; int side; - node = model->nodes; + node_id = 0; // +2 for paranoia - node_stack = alloca ((model->depth + 2) * sizeof (rstack_t)); + node_stack = alloca ((brush->depth + 2) * sizeof (rstack_t)); node_ptr = node_stack; while (1) { - while (test_node (node)) { + while (test_node (bctx, node_id)) { + mnode_t *node = bctx->brush->nodes + node_id; side = get_side (node); front = node->children[side]; - if (test_node (front)) { - node_ptr->node = node; + if (test_node (bctx, front)) { + node_ptr->node_id = node_id; node_ptr->side = side; node_ptr++; - node = front; + node_id = front; continue; } - if (front->contents < 0 && front->contents != CONTENTS_SOLID) - visit_leaf ((mleaf_t *) front); - visit_node (node, side); - node = node->children[!side]; + if (front < 0) { + mleaf_t *leaf = bctx->brush->leafs + ~front; + if (leaf->contents != CONTENTS_SOLID) { + visit_leaf (leaf); + } + } + visit_node (bctx, node, side); + node_id = node->children[side ^ 1]; + } + if (node_id < 0) { + mleaf_t *leaf = bctx->brush->leafs + ~node_id; + if (leaf->contents != CONTENTS_SOLID) { + visit_leaf (leaf); + } } - if (node->contents < 0 && node->contents != CONTENTS_SOLID) - visit_leaf ((mleaf_t *) node); if (node_ptr != node_stack) { node_ptr--; - node = node_ptr->node; + node_id = node_ptr->node_id; side = node_ptr->side; - visit_node (node, side); - node = node->children[!side]; + mnode_t *node = bctx->brush->nodes + node_id; + visit_node (bctx, node, side); + node_id = node->children[side ^ 1]; continue; } break; } - if (node->contents < 0 && node->contents != CONTENTS_SOLID) - visit_leaf ((mleaf_t *) node); } static void draw_elechain (elechain_t *ec, int matloc, int vertloc, int tlstloc, int colloc) { - mat4_t mat; + mat4f_t mat; elements_t *el; int count; float *color; @@ -831,10 +863,10 @@ draw_elechain (elechain_t *ec, int matloc, int vertloc, int tlstloc, } } if (ec->transform) { - Mat4Mult (bsp_vp, ec->transform, mat); - qfeglUniformMatrix4fv (matloc, 1, false, mat); + mmulf (mat, bsp_vp, ec->transform); + qfeglUniformMatrix4fv (matloc, 1, false, (vec_t*)&mat[0]);//FIXME } else { - qfeglUniformMatrix4fv (matloc, 1, false, bsp_vp); + qfeglUniformMatrix4fv (matloc, 1, false, (vec_t*)&bsp_vp[0]);//FIXME } for (el = ec->elements; el; el = el->next) { if (!el->list->size) @@ -862,7 +894,7 @@ bsp_begin (void) QuatCopy (default_color, last_color); qfeglVertexAttrib4fv (quake_bsp.color.location, default_color); - Mat4Mult (glsl_projection, glsl_view, bsp_vp); + mmulf (bsp_vp, glsl_projection, glsl_view); qfeglUseProgram (quake_bsp.program); qfeglEnableVertexAttribArray (quake_bsp.vertex.location); @@ -871,8 +903,8 @@ bsp_begin (void) qfeglVertexAttrib4fv (quake_bsp.color.location, default_color); - VectorCopy (glsl_Fog_GetColor (), fog); - fog[3] = glsl_Fog_GetDensity () / 64.0; + Fog_GetColor (fog); + fog[3] = Fog_GetDensity () / 64.0; qfeglUniform4fv (quake_bsp.fog.location, 1, fog); qfeglUniform1i (quake_bsp.colormap.location, 2); @@ -913,11 +945,11 @@ turb_begin (void) { quat_t fog; - default_color[3] = bound (0, r_wateralpha->value, 1); + default_color[3] = bound (0, r_wateralpha, 1); QuatCopy (default_color, last_color); qfeglVertexAttrib4fv (quake_bsp.color.location, default_color); - Mat4Mult (glsl_projection, glsl_view, bsp_vp); + mmulf (bsp_vp, glsl_projection, glsl_view); qfeglUseProgram (quake_turb.program); qfeglEnableVertexAttribArray (quake_turb.vertex.location); @@ -926,8 +958,8 @@ turb_begin (void) qfeglVertexAttrib4fv (quake_turb.color.location, default_color); - VectorCopy (glsl_Fog_GetColor (), fog); - fog[3] = glsl_Fog_GetDensity () / 64.0; + Fog_GetColor (fog); + fog[3] = Fog_GetDensity () / 64.0; qfeglUniform4fv (quake_turb.fog.location, 1, fog); qfeglUniform1i (quake_turb.palette.location, 1); @@ -975,7 +1007,7 @@ spin (mat4_t mat) QuatBlend (sky_rotation[0], sky_rotation[1], blend, q); QuatMult (sky_fix, q, q); Mat4Identity (mat); - VectorNegate (r_origin, mat + 12); + VectorNegate (r_refdef.frame.position, mat + 12); QuatToMatrix (q, m, 1, 1); Mat4Mult (m, mat, mat); } @@ -990,7 +1022,7 @@ sky_begin (void) QuatCopy (default_color, last_color); qfeglVertexAttrib4fv (quake_bsp.color.location, default_color); - Mat4Mult (glsl_projection, glsl_view, bsp_vp); + mmulf (bsp_vp, glsl_projection, glsl_view); if (skybox_loaded) { sky_params.mvp_matrix = &quake_skybox.mvp_matrix; @@ -1030,8 +1062,8 @@ sky_begin (void) qfeglEnable (GL_TEXTURE_2D); } - VectorCopy (glsl_Fog_GetColor (), fog); - fog[3] = glsl_Fog_GetDensity () / 64.0; + Fog_GetColor (fog); + fog[3] = Fog_GetDensity () / 64.0; qfeglUniform4fv (sky_params.fog->location, 1, fog); spin (mat); @@ -1057,14 +1089,14 @@ sky_end (void) } static inline void -add_surf_elements (texture_t *tex, instsurf_t *is, +add_surf_elements (glsltex_t *tex, instsurf_t *is, elechain_t **ec, elements_t **el) { msurface_t *surf = is->surface; glslpoly_t *poly = (glslpoly_t *) surf->polys; if (!tex->elechain) { - (*ec) = add_elechain (tex, surf->ec_index); + (*ec) = add_elechain (tex, surf->model_index); (*ec)->transform = is->transform; (*ec)->color = is->color; (*el) = (*ec)->elements; @@ -1074,7 +1106,7 @@ add_surf_elements (texture_t *tex, instsurf_t *is, dstring_clear ((*el)->list); } if (is->transform != (*ec)->transform || is->color != (*ec)->color) { - (*ec) = add_elechain (tex, surf->ec_index); + (*ec) = add_elechain (tex, surf->model_index); (*ec)->transform = is->transform; (*ec)->color = is->color; (*el) = (*ec)->elements; @@ -1096,7 +1128,7 @@ add_surf_elements (texture_t *tex, instsurf_t *is, } static void -build_tex_elechain (texture_t *tex) +build_tex_elechain (glsltex_t *tex) { instsurf_t *is; elechain_t *ec = 0; @@ -1110,24 +1142,19 @@ build_tex_elechain (texture_t *tex) void glsl_R_DrawWorld (void) { - entity_t worldent; + animation_t animation = {}; + glslbspctx_t bctx = { }; int i; clear_texture_chains (); // do this first for water and skys - memset (&worldent, 0, sizeof (worldent)); - worldent.model = r_worldentity.model; - - currententity = &worldent; - - R_VisitWorldNodes (worldent.model); - if (r_drawentities->int_val) { - entity_t *ent; - for (ent = r_ent_queue; ent; ent = ent->next) { - if (ent->model->type != mod_brush) - continue; - currententity = ent; + bctx.brush = &r_refdef.worldmodel->brush; + bctx.animation = &animation; + R_VisitWorldNodes (&bctx); + if (r_drawentities) { + for (size_t i = 0; i < r_ent_queue->ent_queues[mod_brush].size; i++) { + entity_t ent = r_ent_queue->ent_queues[mod_brush].a[i]; R_DrawBrushModel (ent); } } @@ -1136,7 +1163,7 @@ glsl_R_DrawWorld (void) bsp_begin (); qfeglActiveTexture (GL_TEXTURE0 + 0); for (i = 0; i < r_num_texture_chains; i++) { - texture_t *tex; + glsltex_t *tex; elechain_t *ec = 0; tex = r_texture_chains[i]; @@ -1159,11 +1186,11 @@ glsl_R_DrawWorld (void) } void -glsl_R_DrawWaterSurfaces () +glsl_R_DrawWaterSurfaces (void) { instsurf_t *is; msurface_t *surf; - texture_t *tex = 0; + glsltex_t *tex = 0; elechain_t *ec = 0; elements_t *el = 0; @@ -1173,7 +1200,7 @@ glsl_R_DrawWaterSurfaces () turb_begin (); for (is = waterchain; is; is = is->tex_chain) { surf = is->surface; - if (tex != surf->texinfo->texture) { + if (tex != surf->texinfo->texture->render) { if (tex) { qfeglBindTexture (GL_TEXTURE_2D, tex->gl_texturenum); for (ec = tex->elechain; ec; ec = ec->next) @@ -1184,7 +1211,7 @@ glsl_R_DrawWaterSurfaces () tex->elechain = 0; tex->elechain_tail = &tex->elechain; } - tex = surf->texinfo->texture; + tex = surf->texinfo->texture->render; } add_surf_elements (tex, is, &ec, &el); } @@ -1209,7 +1236,7 @@ glsl_R_DrawSky (void) { instsurf_t *is; msurface_t *surf; - texture_t *tex = 0; + glsltex_t *tex = 0; elechain_t *ec = 0; elements_t *el = 0; @@ -1219,7 +1246,7 @@ glsl_R_DrawSky (void) sky_begin (); for (is = sky_chain; is; is = is->tex_chain) { surf = is->surface; - if (tex != surf->texinfo->texture) { + if (tex != surf->texinfo->texture->render) { if (tex) { if (!skybox_loaded) { qfeglActiveTexture (GL_TEXTURE0 + 0); @@ -1233,7 +1260,7 @@ glsl_R_DrawSky (void) tex->elechain = 0; tex->elechain_tail = &tex->elechain; } - tex = surf->texinfo->texture; + tex = surf->texinfo->texture->render; } add_surf_elements (tex, is, &ec, &el); } @@ -1263,6 +1290,8 @@ glsl_R_InitBsp (void) int vert; int frag; + r_notexture_mip->render = &glsl_notexture; + vert_shader = GLSL_BuildShader (bsp_vert_effects); frag_shader = GLSL_BuildShader (bsp_lit_effects); vert = GLSL_CompileShader ("quakebsp.vert", vert_shader, @@ -1321,7 +1350,14 @@ glsl_R_InitBsp (void) GLSL_FreeShader (frag_shader); } -static inline int +void +glsl_R_ShutdownBsp (void) +{ + free (r_texture_chains); + r_texture_chains = 0; +} + +static inline __attribute__((const)) int is_pow2 (unsigned x) { int count; @@ -1369,7 +1405,7 @@ glsl_R_LoadSkys (const char *sky) // a -90 degree rotation on the (quake) z-axis. This is taken care of in // the sky_matrix setup code. // However, from the player's perspective, skymaps have lf and rt - // swapped, but everythink makes sense if looking at the cube from outside + // swapped, but everything makes sense if looking at the cube from outside // along the positive y axis, with the front of the cube being the nearest // face. This matches nicely with Blender's default cube in front (num-1) // view. @@ -1384,7 +1420,7 @@ glsl_R_LoadSkys (const char *sky) }; if (!sky || !*sky) - sky = r_skyname->string; + sky = r_skyname; if (!*sky || !strcasecmp (sky, "none")) { skybox_loaded = false; @@ -1399,14 +1435,15 @@ glsl_R_LoadSkys (const char *sky) //blender envmap // bk rt ft // dn up lt - tex = LoadImage (name = va ("env/%s_map", sky)); + tex = LoadImage (name = va (0, "env/%s_map", sky), 1); if (tex && tex->format >= 3 && tex->height * 3 == tex->width * 2 && is_pow2 (tex->height)) { tex_t *sub; int size = tex->height / 2; skybox_loaded = true; - sub = malloc (field_offset (tex_t, data[size * size * tex->format])); + sub = malloc (sizeof (tex_t) + size * size * tex->format); + sub->data = (byte *) (sub + 1); sub->width = size; sub->height = size; sub->format = tex->format; @@ -1426,19 +1463,19 @@ glsl_R_LoadSkys (const char *sky) } else { skybox_loaded = true; for (i = 0; i < 6; i++) { - tex = LoadImage (name = va ("env/%s%s", sky, sky_suffix[i])); + tex = LoadImage (name = va (0, "env/%s%s", sky, sky_suffix[i]), 1); if (!tex || tex->format < 3) { // FIXME pcx support - Sys_MaskPrintf (SYS_GLSL, "Couldn't load %s\n", name); + Sys_MaskPrintf (SYS_glsl, "Couldn't load %s\n", name); // also look in gfx/env, where Darkplaces looks for skies - tex = LoadImage (name = va ("gfx/env/%s%s", sky, - sky_suffix[i])); + tex = LoadImage (name = va (0, "gfx/env/%s%s", sky, + sky_suffix[i]), 1); if (!tex || tex->format < 3) { // FIXME pcx support - Sys_MaskPrintf (SYS_GLSL, "Couldn't load %s\n", name); + Sys_MaskPrintf (SYS_glsl, "Couldn't load %s\n", name); skybox_loaded = false; continue; } } - Sys_MaskPrintf (SYS_GLSL, "Loaded %s\n", name); + Sys_MaskPrintf (SYS_glsl, "Loaded %s\n", name); qfeglTexImage2D (GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, tex->format == 3 ? GL_RGB : GL_RGBA, tex->width, tex->height, 0, diff --git a/libs/video/renderer/glsl/glsl_draw.c b/libs/video/renderer/glsl/glsl_draw.c index 6a8ef0268..2ac5ef635 100644 --- a/libs/video/renderer/glsl/glsl_draw.c +++ b/libs/video/renderer/glsl/glsl_draw.c @@ -31,9 +31,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -45,9 +42,12 @@ #include "QF/draw.h" #include "QF/dstring.h" #include "QF/hash.h" +#include "QF/image.h" #include "QF/quakefs.h" #include "QF/sys.h" #include "QF/vid.h" +#include "QF/ui/font.h" +#include "QF/ui/view.h" #include "QF/GLSL/defines.h" #include "QF/GLSL/funcs.h" @@ -57,9 +57,9 @@ #include "r_internal.h" -typedef struct { +typedef struct pic_data_s { subpic_t *subpic; -} glpic_t; +} picdata_t; typedef struct cachepic_s { struct cachepic_s *next; @@ -72,6 +72,16 @@ typedef struct { float color[4]; } drawvert_t; +typedef struct glslfont_s { + font_t *font; + GLuint texid; +} glslfont_t; + +typedef struct glfontset_s + DARRAY_TYPE (glslfont_t) glslfontset_t; + +static glslfontset_t glsl_fonts = DARRAY_STATIC_INIT (16); + static const char *twod_vert_effects[] = { "QuakeForge.Vertex.2d", @@ -85,6 +95,12 @@ static const char *twod_frag_effects[] = 0 }; +static const char *twod_alpha_frag_effects[] = +{ + "QuakeForge.Fragment.2d.alpha", + 0 +}; + static float proj_matrix[16]; static struct { @@ -95,39 +111,60 @@ static struct { shaderparam_t vertex; shaderparam_t color; } quake_2d = { - 0, - {"texture", 1}, - {"palette", 1}, - {"mvp_mat", 1}, - {"vertex", 0}, - {"vcolor", 0}, + .texture = {"texture", 1}, + .palette = {"palette", 1}, + .matrix = {"mvp_mat", 1}, + .vertex = {"vertex", 0}, + .color = {"vcolor", 0}, +}; + +static struct { + int program; + shaderparam_t texture; + shaderparam_t matrix; + shaderparam_t vertex; + shaderparam_t color; +} alpha_2d = { + .texture = {"texture", 1}, + .matrix = {"mvp_mat", 1}, + .vertex = {"vertex", 0}, + .color = {"vcolor", 0}, }; static scrap_t *draw_scrap; // hold all 2d images static byte white_block[8 * 8]; static dstring_t *draw_queue; +static dstring_t *glyph_queue; +static dstring_t *line_queue; static qpic_t *conchars; static int conback_texture; static qpic_t *crosshair_pic; static qpic_t *white_pic; static qpic_t *backtile_pic; static hashtab_t *pic_cache; -static cvar_t *glsl_conback_texnum; +static int glsl_conback_texnum; +static int glsl_2d_scale = 1; +static cvar_t glsl_conback_texnum_cvar = { + .name = "glsl_conback_texnum", + .description = + "bind conback to this texture for debugging", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &glsl_conback_texnum }, +}; static qpic_t * make_glpic (const char *name, qpic_t *p) { qpic_t *pic = 0; - glpic_t *gl; if (p) { - // FIXME is alignment ok? - pic = malloc (sizeof (qpic_t) + sizeof (glpic_t)); + pic = malloc (field_offset (qpic_t, data[sizeof (picdata_t)])); pic->width = p->width; pic->height = p->height; - gl = (glpic_t *) pic->data; - gl->subpic = GLSL_ScrapSubpic (draw_scrap, pic->width, pic->height); - GLSL_SubpicUpdate (gl->subpic, p->data, 1); + __auto_type pd = (picdata_t *) pic->data; + pd->subpic = GLSL_ScrapSubpic (draw_scrap, pic->width, pic->height); + GLSL_SubpicUpdate (pd->subpic, p->data, 1); } return pic; } @@ -135,9 +172,13 @@ make_glpic (const char *name, qpic_t *p) static void pic_free (qpic_t *pic) { - glpic_t *gl = (glpic_t *) pic->data; + if (!pic) { + return; + } - GLSL_SubpicDelete (gl->subpic); + __auto_type pd = (picdata_t *) pic->data; + + GLSL_SubpicDelete (pd->subpic); free (pic); } @@ -188,13 +229,10 @@ make_quad (qpic_t *pic, float x, float y, int w, int h, int srcx, int srcy, int srcw, int srch, drawvert_t verts[6], float *color) { - glpic_t *gl; - subpic_t *sp; + __auto_type pd = (picdata_t *) pic->data; + subpic_t *sp = pd->subpic; float sl, sh, tl, th; - gl = (glpic_t *) pic->data; - sp = gl->subpic; - srcx += sp->rect->x; srcy += sp->rect->y; sl = (srcx) * sp->size; @@ -275,7 +313,7 @@ glsl_Draw_PicFromWad (const char *name) } qpic_t * -glsl_Draw_CachePic (const char *path, qboolean alpha) +glsl_Draw_CachePic (const char *path, bool alpha) { qpic_t *p, *pic; cachepic_t *cpic; @@ -359,7 +397,7 @@ glsl_Draw_TextBox (int x, int y, int width, int lines, byte alpha) } static void -Draw_ClearCache (int phase) +Draw_ClearCache (int phase, void *data) { if (phase) return; @@ -373,15 +411,16 @@ glsl_Draw_Init (void) int i; int frag, vert; qpic_t *pic; - //FIXME glpic_t *gl; - pic_cache = Hash_NewTable (127, cachepic_getkey, cachepic_free, 0); - QFS_GamedirCallback (Draw_ClearCache); + pic_cache = Hash_NewTable (127, cachepic_getkey, cachepic_free, 0, 0); + QFS_GamedirCallback (Draw_ClearCache, 0); //FIXME temporary work around for the timing of cvar creation and palette //loading - crosshaircolor->callback (crosshaircolor); + //crosshaircolor->callback (crosshaircolor); draw_queue = dstring_new (); + line_queue = dstring_new (); + glyph_queue = dstring_new (); vert_shader = GLSL_BuildShader (twod_vert_effects); frag_shader = GLSL_BuildShader (twod_frag_effects); @@ -398,14 +437,36 @@ glsl_Draw_Init (void) GLSL_FreeShader (vert_shader); GLSL_FreeShader (frag_shader); + vert_shader = GLSL_BuildShader (twod_vert_effects); + frag_shader = GLSL_BuildShader (twod_alpha_frag_effects); + vert = GLSL_CompileShader ("quakeico.vert", vert_shader, + GL_VERTEX_SHADER); + frag = GLSL_CompileShader ("alpha2d.frag", frag_shader, + GL_FRAGMENT_SHADER); + alpha_2d.program = GLSL_LinkProgram ("alpha2d", vert, frag); + GLSL_ResolveShaderParam (alpha_2d.program, &alpha_2d.texture); + GLSL_ResolveShaderParam (alpha_2d.program, &alpha_2d.matrix); + GLSL_ResolveShaderParam (alpha_2d.program, &alpha_2d.vertex); + GLSL_ResolveShaderParam (alpha_2d.program, &alpha_2d.color); + GLSL_FreeShader (vert_shader); + GLSL_FreeShader (frag_shader); + draw_scrap = GLSL_CreateScrap (2048, GL_LUMINANCE, 0); draw_chars = W_GetLumpName ("conchars"); - for (i = 0; i < 256 * 64; i++) - if (draw_chars[i] == 0) - draw_chars[i] = 255; // proper transparent color - - conchars = pic_data ("conchars", 128, 128, draw_chars); + if (draw_chars) { + for (i = 0; i < 256 * 64; i++) { + if (draw_chars[i] == 0) { + draw_chars[i] = 255; // proper transparent color + } + } + conchars = pic_data ("conchars", 128, 128, draw_chars); + } else { + qpic_t *charspic = Draw_Font8x8Pic (); + conchars = pic_data ("conchars", charspic->width, + charspic->height, charspic->data); + free (charspic); + } pic = (qpic_t *) QFS_LoadFile (QFS_FOpenFile ("gfx/conback.lmp"), 0); if (pic) { @@ -424,14 +485,31 @@ glsl_Draw_Init (void) white_pic = pic_data ("white_block", 8, 8, white_block); backtile_pic = glsl_Draw_PicFromWad ("backtile"); - //FIXME gl = (glpic_t *) backtile_pic->data; - //FIXME qfeglBindTexture (GL_TEXTURE_2D, gl->texnum); - //FIXME qfeglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - //FIXME qfeglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + if (!backtile_pic) { + backtile_pic = white_pic; + } - glsl_conback_texnum = Cvar_Get ("glsl_conback_texnum", "0", CVAR_NONE, - NULL, "bind conback to this texture for " - "debugging"); + Cvar_Register (&glsl_conback_texnum_cvar, 0, 0); +} + +void +glsl_Draw_Shutdown (void) +{ + pic_free (conchars); + pic_free (crosshair_pic); + if (backtile_pic != white_pic) { + pic_free (backtile_pic); + } + pic_free (white_pic); + + dstring_delete (draw_queue); + dstring_delete (line_queue); + dstring_delete (glyph_queue); + + Hash_DelTable (pic_cache); + + GLSL_DestroyScrap (draw_scrap); + DARRAY_CLEAR (&glsl_fonts); } static inline void @@ -449,14 +527,41 @@ static void flush_2d (void) { GLSL_ScrapFlush (draw_scrap); - qfeglBindTexture (GL_TEXTURE_2D, GLSL_ScrapTexture (draw_scrap)); - qfeglVertexAttribPointer (quake_2d.vertex.location, 4, GL_FLOAT, - 0, 32, draw_queue->str); - qfeglVertexAttribPointer (quake_2d.color.location, 4, GL_FLOAT, - 0, 32, draw_queue->str + 16); - qfeglDrawArrays (GL_TRIANGLES, 0, draw_queue->size / 32); + qfeglBindTexture (GL_TEXTURE_2D, GLSL_ScrapTexture (draw_scrap)); + if (draw_queue->size) { + qfeglVertexAttribPointer (quake_2d.vertex.location, 4, GL_FLOAT, + 0, 32, draw_queue->str); + qfeglVertexAttribPointer (quake_2d.color.location, 4, GL_FLOAT, + 0, 32, draw_queue->str + 16); + + qfeglDrawArrays (GL_TRIANGLES, 0, draw_queue->size / 32); + } + if (line_queue->size) { + qfeglVertexAttribPointer (quake_2d.vertex.location, 4, GL_FLOAT, + 0, 32, line_queue->str); + qfeglVertexAttribPointer (quake_2d.color.location, 4, GL_FLOAT, + 0, 32, line_queue->str + 16); + + qfeglDrawArrays (GL_LINES, 0, line_queue->size / 32); + } draw_queue->size = 0; + line_queue->size = 0; +} + +void +glsl_Draw_CharBuffer (int x, int y, draw_charbuffer_t *buffer) +{ + const byte *line = (byte *) buffer->chars; + int width = buffer->width; + int height = buffer->height; + while (height-- > 0) { + for (int i = 0; i < width; i++) { + glsl_Draw_Character (x + i * 8, y, line[i]); + } + line += width; + y += 8; + } } void @@ -541,8 +646,8 @@ draw_crosshair_pic (int ch, int x, int y) }; const int *p = pos[ch - 1]; - draw_pic (x - CROSSHAIR_WIDTH + 1, y - CROSSHAIR_HEIGHT + 1, - CROSSHAIR_WIDTH * 2, CROSSHAIR_HEIGHT * 2, crosshair_pic, + draw_pic (x - CROSSHAIR_WIDTH / 2 + 1, y - CROSSHAIR_HEIGHT / 2 + 1, + CROSSHAIR_WIDTH, CROSSHAIR_HEIGHT, crosshair_pic, p[0], p[1], p[2], p[3], crosshair_color); } @@ -569,11 +674,12 @@ void glsl_Draw_Crosshair (void) { int x, y; + int s = 2 * glsl_2d_scale; - x = vid.conwidth / 2 + cl_crossx->int_val; - y = vid.conheight / 2 + cl_crossy->int_val; + x = vid.width / s + cl_crossx; + y = vid.height / s + cl_crossy; - glsl_Draw_CrosshairAt (crosshair->int_val, x, y); + glsl_Draw_CrosshairAt (crosshair, x, y); } void @@ -584,6 +690,13 @@ glsl_Draw_Pic (int x, int y, qpic_t *pic) 0, 0, pic->width, pic->height, color); } +void +glsl_Draw_FitPic (int x, int y, int width, int height, qpic_t *pic) +{ + static quat_t color = { 1, 1, 1, 1}; + draw_pic (x, y, width, height, pic, 0, 0, pic->width, pic->height, color); +} + void glsl_Draw_Picf (float x, float y, qpic_t *pic) { @@ -603,15 +716,16 @@ glsl_Draw_SubPic (int x, int y, qpic_t *pic, int srcx, int srcy, int width, void glsl_Draw_ConsoleBackground (int lines, byte alpha) { - float ofs = (vid.conheight - lines) / (float) vid.conheight; + int s = glsl_2d_scale; + float ofs = (vid.height - s * lines) / (float) vid.height; quat_t color = {1, 1, 1, bound (0, alpha, 255) / 255.0}; drawvert_t verts[] = { - {{ 0, 0, 0, ofs}}, - {{vid.conwidth, 0, 1, ofs}}, - {{vid.conwidth, lines, 1, 1}}, - {{ 0, 0, 0, ofs}}, - {{vid.conwidth, lines, 1, 1}}, - {{ 0, lines, 0, 1}}, + {{ 0, 0, 0, ofs}}, + {{vid.width / s, 0, 1, ofs}}, + {{vid.width / s, lines, 1, 1}}, + {{ 0, 0, 0, ofs}}, + {{vid.width / s, lines, 1, 1}}, + {{ 0, lines, 0, 1}}, }; GLSL_FlushText (); // Flush text that should be rendered before the console @@ -623,8 +737,8 @@ glsl_Draw_ConsoleBackground (int lines, byte alpha) QuatCopy (color, verts[4].color); QuatCopy (color, verts[5].color); - if (glsl_conback_texnum->int_val) - qfeglBindTexture (GL_TEXTURE_2D, glsl_conback_texnum->int_val); + if (glsl_conback_texnum) + qfeglBindTexture (GL_TEXTURE_2D, glsl_conback_texnum); else qfeglBindTexture (GL_TEXTURE_2D, conback_texture); @@ -640,12 +754,12 @@ void glsl_Draw_TileClear (int x, int y, int w, int h) { static quat_t color = { 1, 1, 1, 1 }; - vrect_t *tile_rect = VRect_New (x, y, w, h); - vrect_t *sub = VRect_New (0, 0, 0, 0); // filled in later; - glpic_t *gl = (glpic_t *) backtile_pic->data; - subpic_t *sp = gl->subpic; - int sub_sx, sub_sy, sub_ex, sub_ey; - int i, j; + vrect_t *tile_rect = VRect_New (x, y, w, h); + vrect_t *sub = VRect_New (0, 0, 0, 0); // filled in later; + __auto_type pd = (picdata_t *) backtile_pic->data; + subpic_t *sp = pd->subpic; + int sub_sx, sub_sy, sub_ex, sub_ey; + int i, j; sub_sx = x / sp->width; sub_sy = y / sp->height; @@ -681,10 +795,50 @@ glsl_Draw_Fill (int x, int y, int w, int h, int c) draw_pic (x, y, w, h, white_pic, 0, 0, 8, 8, color); } +void +glsl_Draw_Line (int x0, int y0, int x1, int y1, int c) +{ + __auto_type pd = (picdata_t *) white_pic->data; + subpic_t *sp = pd->subpic; + float sl = sp->rect->x * sp->size; + float sh = sp->rect->x * sp->size; + float tl = sp->rect->y * sp->size; + float th = sp->rect->y * sp->size; + + quat_t color = { VectorExpand (vid.palette + c * 3), 255 }; + QuatScale (color, 1/255.0, color); + drawvert_t verts[2] = { + { .xyst = { x0, y0, sl, tl }, .color = { QuatExpand (color) }, }, + { .xyst = { x1, y1, sh, th }, .color = { QuatExpand (color) }, }, + }; + + void *v; + int size = sizeof (verts); + + line_queue->size += size; + dstring_adjust (line_queue); + v = line_queue->str + line_queue->size - size; + memcpy (v, verts, size); +} + +void +glsl_LineGraph (int x, int y, int *h_vals, int count, int height) +{ + static int colors[] = { 0xd0, 0x4f, 0x6f }; + + while (count-- > 0) { + int h = *h_vals++; + int c = h < 9998 || h > 10000 ? 0xfe : colors[h - 9998]; + h = min (h, height); + glsl_Draw_Line (x, y, x, y - h, c); + x++; + } +} + static inline void draw_blendscreen (quat_t color) { - draw_pic (0, 0, vid.conwidth, vid.conheight, white_pic, 0, 0, 8, 8, color); + draw_pic (0, 0, vid.width, vid.height, white_pic, 0, 0, 8, 8, color); } void @@ -757,7 +911,13 @@ GLSL_Set2D (void) void GLSL_Set2DScaled (void) { - set_2d (vid.conwidth, vid.conheight); + set_2d (vid.width / glsl_2d_scale, vid.height / glsl_2d_scale); +} + +void +glsl_Draw_SetScale (int scale) +{ + glsl_2d_scale = scale; } void @@ -771,12 +931,13 @@ void GLSL_DrawReset (void) { draw_queue->size = 0; + line_queue->size = 0; } void GLSL_FlushText (void) { - if (draw_queue->size) + if (draw_queue->size || line_queue->size) flush_2d (); } @@ -787,3 +948,109 @@ glsl_Draw_BlendScreen (quat_t color) return; draw_blendscreen (color); } + +int +glsl_Draw_AddFont (font_t *rfont) +{ + int fontid = glsl_fonts.size; + DARRAY_OPEN_AT (&glsl_fonts, fontid, 1); + glslfont_t *font = &glsl_fonts.a[fontid]; + + font->font = rfont; + tex_t tex = { + .width = rfont->scrap.width, + .height = rfont->scrap.height, + .format = tex_a, + .loaded = 1, + .data = rfont->scrap_bitmap, + }; + font->texid = GLSL_LoadTex ("", 1, &tex); + return fontid; +} +#if 0 +typedef struct { + vrect_t *glyph_rects; + dstring_t *batch; + int width; + int height; + float color[4]; +} glslrgctx_t; + +static void +glsl_render_glyph (uint32_t glyphid, int x, int y, void *_rgctx) +{ + glslrgctx_t *rgctx = _rgctx; + vrect_t *rect = &rgctx->glyph_rects[glyphid]; + dstring_t *batch = rgctx->batch; + +} +#endif +void +glsl_Draw_Glyph (int x, int y, int fontid, int glyphid, int c) +{ + if (fontid < 0 || (unsigned) fontid > glsl_fonts.size) { + return; + } + glslfont_t *font = &glsl_fonts.a[fontid]; + font_t *rfont = font->font; + vrect_t *rect = &rfont->glyph_rects[glyphid]; + + dstring_t *batch = glyph_queue; + int size = 6 * sizeof (drawvert_t); + batch->size += size; + dstring_adjust (batch); + drawvert_t *verts = (drawvert_t *) (batch->str + batch->size - size); + + float w = rect->width; + float h = rect->height; + float u = rect->x; + float v = rect->y; + float s = 1.0 / rfont->scrap.width; + float t = 1.0 / rfont->scrap.height; + + verts[0] = (drawvert_t) { + .xyst = { x, y, u * s, v * t }, + }; + verts[1] = (drawvert_t) { + .xyst = { x + w, y, (u + w) * s, v * t }, + }; + verts[2] = (drawvert_t) { + .xyst = { x + w, y + h, (u + w) * s, (v + h) * t }, + }; + verts[3] = (drawvert_t) { + .xyst = { x, y, u * s, v * t }, + }; + verts[4] = (drawvert_t) { + .xyst = { x + w, y + h, (u + w) * s, (v + h) * t }, + }; + verts[5] = (drawvert_t) { + .xyst = { x, y + h, u * s, (v + h) * t }, + }; + + quat_t color = { VectorExpand (vid.palette + c * 3), 255 }; + QuatScale (color, 1.0f/255.0f, color); + QuatCopy (color, verts[0].color); + QuatCopy (color, verts[1].color); + QuatCopy (color, verts[2].color); + QuatCopy (color, verts[3].color); + QuatCopy (color, verts[4].color); + QuatCopy (color, verts[5].color); + + qfeglUseProgram (alpha_2d.program); + qfeglEnableVertexAttribArray (alpha_2d.vertex.location); + qfeglEnableVertexAttribArray (alpha_2d.color.location); + qfeglUniformMatrix4fv (alpha_2d.matrix.location, 1, false, proj_matrix); + qfeglBindTexture (GL_TEXTURE_2D, font->texid); + qfeglBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + if (glyph_queue->size) { + qfeglVertexAttribPointer (alpha_2d.vertex.location, 4, GL_FLOAT, + 0, 32, glyph_queue->str); + qfeglVertexAttribPointer (alpha_2d.color.location, 4, GL_FLOAT, + 0, 32, glyph_queue->str + 16); + + qfeglDrawArrays (GL_TRIANGLES, 0, glyph_queue->size / 32); + } + glyph_queue->size = 0; + qfeglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + qfeglUseProgram (quake_2d.program); +} diff --git a/libs/video/renderer/glsl/glsl_fisheye.c b/libs/video/renderer/glsl/glsl_fisheye.c new file mode 100644 index 000000000..2291ccdc6 --- /dev/null +++ b/libs/video/renderer/glsl/glsl_fisheye.c @@ -0,0 +1,123 @@ +/* + glsl_fisheye.c + + GLSL screen fisheye + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/3/25 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include + +#include "QF/cvar.h" +#include "QF/render.h" +#include "QF/sys.h" + +#include "QF/GLSL/defines.h" +#include "QF/GLSL/funcs.h" +#include "QF/GLSL/qf_vid.h" +#include "QF/GLSL/qf_fisheye.h" + +#include "r_internal.h" +#include "vid_gl.h" + +static const char *fisheye_vert_effects[] = +{ + "QuakeForge.version.130", + "QuakeForge.Vertex.fstri", + 0 +}; + +static const char *fisheye_frag_effects[] = +{ + "QuakeForge.version.130", + "QuakeForge.Math.const", + "QuakeForge.Fragment.screen.fisheye", + 0 +}; + +static struct { + int program; + GLuint vao; + shaderparam_t screenTex; + shaderparam_t fov; + shaderparam_t aspect; +} fisheye = { + .screenTex = {"screenTex", 1}, + .fov = {"fov", 1}, + .aspect = {"aspect", 1}, +}; + +void +glsl_InitFisheye (void) +{ + shader_t *vert_shader, *frag_shader; + int vert; + int frag; + + vert_shader = GLSL_BuildShader (fisheye_vert_effects); + frag_shader = GLSL_BuildShader (fisheye_frag_effects); + vert = GLSL_CompileShader ("scrfisheye.vert", vert_shader, + GL_VERTEX_SHADER); + frag = GLSL_CompileShader ("scrfisheye.frag", frag_shader, + GL_FRAGMENT_SHADER); + fisheye.program = GLSL_LinkProgram ("scrfisheye", vert, frag); + GLSL_ResolveShaderParam (fisheye.program, &fisheye.screenTex); + GLSL_ResolveShaderParam (fisheye.program, &fisheye.fov); + GLSL_ResolveShaderParam (fisheye.program, &fisheye.aspect); + GLSL_FreeShader (vert_shader); + GLSL_FreeShader (frag_shader); + + qfeglCreateVertexArrays (1, &fisheye.vao); +} + +void +glsl_FisheyeScreen (framebuffer_t *fb) +{ + qfeglUseProgram (fisheye.program); + + qfeglUniform1f (fisheye.fov.location, scr_ffov * M_PI / 360); + qfeglUniform1f (fisheye.aspect.location, + (float) r_refdef.vrect.height / r_refdef.vrect.width); + + gl_framebuffer_t *buffer = fb->buffer; + qfeglActiveTexture (GL_TEXTURE0 + 0); + qfeglBindTexture (GL_TEXTURE_CUBE_MAP, buffer->color); + + qfeglBindVertexArray (fisheye.vao); + qfeglDrawArrays (GL_TRIANGLES, 0, 3); + qfeglBindVertexArray (0); + + qfeglActiveTexture (GL_TEXTURE0 + 0); + qfeglDisable (GL_TEXTURE_2D); +} diff --git a/libs/video/renderer/glsl/glsl_iqm.c b/libs/video/renderer/glsl/glsl_iqm.c index d189a4cbe..44b0ccbf9 100644 --- a/libs/video/renderer/glsl/glsl_iqm.c +++ b/libs/video/renderer/glsl/glsl_iqm.c @@ -31,9 +31,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -47,6 +44,8 @@ #include "QF/skin.h" #include "QF/sys.h" +#include "QF/scene/entity.h" + #include "QF/GLSL/defines.h" #include "QF/GLSL/funcs.h" #include "QF/GLSL/qf_iqm.h" @@ -137,7 +136,7 @@ static struct va_attr_s { {&iqm_shader.vcolor, 4, GL_UNSIGNED_BYTE, 1}, }; -static mat4_t iqm_vp; +static mat4f_t iqm_vp; void glsl_R_InitIQM (void) @@ -203,32 +202,40 @@ set_arrays (iqm_t *iqm) } void -glsl_R_DrawIQM (void) +glsl_R_DrawIQM (entity_t ent) { + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); + model_t *model = renderer->model; static quat_t color = { 1, 1, 1, 1}; - entity_t *ent = currententity; - model_t *model = ent->model; iqm_t *iqm = (iqm_t *) model->aliashdr; glsliqm_t *glsl = (glsliqm_t *) iqm->extra_data; dlight_t *lights[MAX_IQM_LIGHTS]; int i; vec_t norm_mat[9]; - mat4_t mvp_mat; + vec4f_t entorigin; + mat4f_t mvp_mat; float blend; iqmframe_t *frame; - R_LightPoint (ent->origin); //FIXME min_light? - VectorScale (ambientcolor, 1/255.0, ambientcolor); - R_FindNearLights (ent->origin, MAX_IQM_LIGHTS, lights); - // we need only the rotation for normals. - VectorCopy (ent->transform + 0, norm_mat + 0); - VectorCopy (ent->transform + 4, norm_mat + 3); - VectorCopy (ent->transform + 8, norm_mat + 6); - Mat4Mult (iqm_vp, ent->transform, mvp_mat); + mat4f_t mat; + transform_t transform = Entity_Transform (ent); + Transform_GetWorldMatrix (transform, mat); + VectorCopy (mat[0], norm_mat + 0); + VectorCopy (mat[1], norm_mat + 3); + VectorCopy (mat[2], norm_mat + 6); + entorigin = mat[3]; + mmulf (mvp_mat, iqm_vp, mat); - blend = R_IQMGetLerpedFrames (ent, iqm); - frame = R_IQMBlendFrames (iqm, ent->pose1, ent->pose2, blend, 0); + R_LightPoint (&r_refdef.worldmodel->brush, entorigin);//FIXME min_light? + VectorScale (ambientcolor, 1/255.0, ambientcolor); + R_FindNearLights (entorigin, MAX_IQM_LIGHTS, lights); + + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, + ent.reg); + blend = R_IQMGetLerpedFrames (animation, iqm); + frame = R_IQMBlendFrames (iqm, animation->pose1, animation->pose2, + blend, 0); qfeglUniform3fv (iqm_shader.ambient.location, 1, ambientcolor); for (i = 0; i < MAX_IQM_LIGHTS; i++) { @@ -236,7 +243,7 @@ glsl_R_DrawIQM (void) lightpar_t *l = &iqm_shader.lights[i]; if (!lights[i]) break; - VectorSubtract (lights[i]->origin, ent->origin, val); + VectorSubtract (lights[i]->origin, entorigin, val); val[3] = lights[i]->radius; qfeglUniform4fv (l->position.location, 1, val); qfeglUniform4fv (l->color.location, 1, lights[i]->color); @@ -249,7 +256,8 @@ glsl_R_DrawIQM (void) qfeglBindBuffer (GL_ARRAY_BUFFER, glsl->vertex_array); qfeglBindBuffer (GL_ELEMENT_ARRAY_BUFFER, glsl->element_array); - qfeglUniformMatrix4fv (iqm_shader.mvp_matrix.location, 1, false, mvp_mat); + qfeglUniformMatrix4fv (iqm_shader.mvp_matrix.location, 1, false, + (vec_t*)&mvp_mat[0]);//FIXME qfeglUniformMatrix3fv (iqm_shader.norm_matrix.location, 1, false, norm_mat); qfeglUniformMatrix4fv (iqm_shader.bonemats.location, iqm->num_joints, @@ -274,12 +282,12 @@ glsl_R_IQMBegin (void) quat_t fog; // pre-multiply the view and projection matricies - Mat4Mult (glsl_projection, glsl_view, iqm_vp); + mmulf (iqm_vp, glsl_projection, glsl_view); qfeglUseProgram (iqm_shader.program); - VectorCopy (glsl_Fog_GetColor (), fog); - fog[3] = glsl_Fog_GetDensity () / 64.0; + Fog_GetColor (fog); + fog[3] = Fog_GetDensity () / 64.0; qfeglUniform4fv (iqm_shader.fog.location, 1, fog); qfeglUniform1i (iqm_shader.texture.location, 0); diff --git a/libs/video/renderer/glsl/glsl_lightmap.c b/libs/video/renderer/glsl/glsl_lightmap.c index 11ae46ca8..d324626ea 100644 --- a/libs/video/renderer/glsl/glsl_lightmap.c +++ b/libs/video/renderer/glsl/glsl_lightmap.c @@ -33,9 +33,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -47,6 +44,8 @@ #include "QF/render.h" #include "QF/sys.h" +#include "QF/scene/entity.h" + #include "QF/GLSL/defines.h" #include "QF/GLSL/funcs.h" #include "QF/GLSL/qf_lightmap.h" @@ -58,19 +57,20 @@ #define BLOCK_SIZE (BLOCK_WIDTH * BLOCK_HEIGHT) static scrap_t *light_scrap; -static byte *light_data; static unsigned *blocklights; static int bl_extents[2]; -void (*glsl_R_BuildLightMap) (msurface_t *surf); +void (*glsl_R_BuildLightMap) (const vec4f_t *transform, mod_brush_t *brush, + msurface_t *surf); static void -R_AddDynamicLights_1 (msurface_t *surf) +R_AddDynamicLights_1 (const vec4f_t *transform, msurface_t *surf) { unsigned lnum; int sd, td; float dist, rad, minlight; vec3_t impact, local, lightorigin; + vec4f_t entorigin = { 0, 0, 0, 1}; int s, t; int smax, tmax; mtexinfo_t *tex; @@ -79,12 +79,16 @@ R_AddDynamicLights_1 (msurface_t *surf) tmax = (surf->extents[1] >> 4) + 1; tex = surf->texinfo; + if (transform) { + //FIXME give world entity a transform + entorigin = transform[3]; + } + for (lnum = 0; lnum < r_maxdlights; lnum++) { if (!(surf->dlightbits[lnum / 32] & (1 << (lnum % 32)))) continue; // not lit by this light - VectorSubtract (r_dlights[lnum].origin, currententity->origin, - lightorigin); + VectorSubtract (r_dlights[lnum].origin, entorigin, lightorigin); rad = r_dlights[lnum].radius; dist = DotProduct (lightorigin, surf->plane->normal) - surf->plane->dist; @@ -122,7 +126,8 @@ R_AddDynamicLights_1 (msurface_t *surf) } static void -R_BuildLightMap_1 (msurface_t *surf) +R_BuildLightMap_1 (const vec4f_t *transform, mod_brush_t *brush, + msurface_t *surf) { int smax, tmax, size; unsigned scale; @@ -139,7 +144,7 @@ R_BuildLightMap_1 (msurface_t *surf) // clear to no light memset (blocklights, 0, size * sizeof (blocklights[0])); - if (!r_worldentity.model->lightdata) { + if (!brush->lightdata) { // because we by-pass the inversion, "no light" = "full bright" GLSL_SubpicUpdate (surf->lightpic, (byte *) blocklights, 1); return; @@ -163,7 +168,7 @@ R_BuildLightMap_1 (msurface_t *surf) } // add all the dynamic lights if (surf->dlightframe == r_framecount) - R_AddDynamicLights_1 (surf); + R_AddDynamicLights_1 (transform, surf); // bound, invert, and shift out = (byte *) blocklights; @@ -195,31 +200,31 @@ create_surf_lightmap (msurface_t *surf) void glsl_R_BuildLightmaps (model_t **models, int num_models) { - int i, j, size; + int size; model_t *m; + mod_brush_t *brush; //FIXME RGB support if (!light_scrap) { - light_scrap = GLSL_CreateScrap (2048, GL_LUMINANCE, 1); - light_data = malloc (BLOCK_SIZE * MAX_LIGHTMAPS); + light_scrap = GLSL_CreateScrap (4096, GL_LUMINANCE, 1); } else { GLSL_ScrapClear (light_scrap); - memset (light_data, 0, BLOCK_SIZE * MAX_LIGHTMAPS); } glsl_R_BuildLightMap = R_BuildLightMap_1; bl_extents[1] = bl_extents[0] = 0; - for (j = 1; j < num_models; j++) { + for (int j = 1; j < num_models; j++) { m = models[j]; if (!m) break; - if (m->name[0] == '*') { + if (m->path[0] == '*' || m->type != mod_brush) { // sub model surfaces are processed as part of the main model continue; } + brush = &m->brush; // non-bsp models don't have surfaces. - for (i = 0; i < m->numsurfaces; i++) { - msurface_t *surf = m->surfaces + i; + for (unsigned i = 0; i < brush->numsurfaces; i++) { + msurface_t *surf = brush->surfaces + i; surf->lightpic = 0; // paranoia if (surf->flags & SURF_DRAWTURB) continue; @@ -230,19 +235,20 @@ glsl_R_BuildLightmaps (model_t **models, int num_models) } size = bl_extents[0] * bl_extents[1] * 3; // * 3 for rgb support blocklights = realloc (blocklights, size * sizeof (blocklights[0])); - for (j = 1; j < num_models; j++) { + for (int j = 1; j < num_models; j++) { m = models[j]; if (!m) break; - if (m->name[0] == '*') { + if (m->path[0] == '*' || m->type != mod_brush) { // sub model surfaces are processed as part of the main model continue; } + brush = &m->brush; // non-bsp models don't have surfaces. - for (i = 0; i < m->numsurfaces; i++) { - msurface_t *surf = m->surfaces + i; + for (unsigned i = 0; i < brush->numsurfaces; i++) { + msurface_t *surf = brush->surfaces + i; if (surf->lightpic) - glsl_R_BuildLightMap (surf); + glsl_R_BuildLightMap (0, brush, surf); } } } @@ -258,3 +264,12 @@ glsl_R_FlushLightmaps (void) { GLSL_ScrapFlush (light_scrap); } + +void +glsl_Lightmap_Shutdown (void) +{ + if (light_scrap) { + GLSL_DestroyScrap (light_scrap); + } + free (blocklights); +} diff --git a/libs/video/renderer/glsl/glsl_main.c b/libs/video/renderer/glsl/glsl_main.c index 9fd3a9c0c..e5128a229 100644 --- a/libs/video/renderer/glsl/glsl_main.c +++ b/libs/video/renderer/glsl/glsl_main.c @@ -31,9 +31,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include "string.h" #endif @@ -48,126 +45,63 @@ #include "QF/screen.h" #include "QF/sys.h" +#include "QF/scene/entity.h" + #include "QF/GLSL/defines.h" #include "QF/GLSL/funcs.h" #include "QF/GLSL/qf_alias.h" #include "QF/GLSL/qf_bsp.h" +#include "QF/GLSL/qf_draw.h" +#include "QF/GLSL/qf_fisheye.h" #include "QF/GLSL/qf_iqm.h" #include "QF/GLSL/qf_lightmap.h" +#include "QF/GLSL/qf_main.h" +#include "QF/GLSL/qf_particles.h" +#include "QF/GLSL/qf_sprite.h" #include "QF/GLSL/qf_textures.h" +#include "QF/GLSL/qf_warp.h" #include "mod_internal.h" #include "r_internal.h" +#include "vid_gl.h" -mat4_t glsl_projection; -mat4_t glsl_view; - -void -glsl_R_ViewChanged (float aspect) -{ - double xmin, xmax, ymin, ymax; - float fovx, fovy, neard, fard; - vec_t *proj = glsl_projection; - - fovx = r_refdef.fov_x; - fovy = r_refdef.fov_y; - neard = r_nearclip->value; - fard = r_farclip->value; - - ymax = neard * tan (fovy * M_PI / 360); // fov_2 / 2 - ymin = -ymax; - xmax = neard * tan (fovx * M_PI / 360); // fov_2 / 2 - xmin = -xmax; - - proj[0] = (2 * neard) / (xmax - xmin); - proj[4] = 0; - proj[8] = (xmax + xmin) / (xmax - xmin); - proj[12] = 0; - - proj[1] = 0; - proj[5] = (2 * neard) / (ymax - ymin); - proj[9] = (ymax + ymin) / (ymax - ymin); - proj[13] = 0; - - proj[2] = 0; - proj[6] = 0; - proj[10] = (fard + neard) / (neard - fard); - proj[14] = (2 * fard * neard) / (neard - fard); - - proj[3] = 0; - proj[7] = 0; - proj[11] = -1; - proj[15] = 0; -} - -void -glsl_R_SetupFrame (void) -{ - R_AnimateLight (); - R_ClearEnts (); - r_framecount++; - - VectorCopy (r_refdef.vieworg, r_origin); - AngleVectors (r_refdef.viewangles, vpn, vright, vup); - R_SetFrustum (); - - r_viewleaf = Mod_PointInLeaf (r_origin, r_worldentity.model); -} +mat4f_t glsl_projection; +mat4f_t glsl_view; static void R_SetupView (void) { - float x, y, w, h; - mat4_t mat; - static mat4_t z_up = { - 0, 0, -1, 0, - -1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 0, 1, + static mat4f_t z_up = { + { 0, 0, 1, 0}, + {-1, 0, 0, 0}, + { 0, 1, 0, 0}, + { 0, 0, 0, 1}, }; - x = r_refdef.vrect.x; - y = (vid.height - (r_refdef.vrect.y + r_refdef.vrect.height)); - w = r_refdef.vrect.width; - h = r_refdef.vrect.height; - qfeglViewport (x, y, w, h); - - Mat4Zero (mat); - VectorCopy (vpn, mat + 0); - VectorNegate (vright, mat + 4); // we want vleft - VectorCopy (vup, mat + 8); - mat[15] = 1; - Mat4Transpose (mat, mat);//AngleVectors gives the transpose of what we want - Mat4Mult (z_up, mat, glsl_view); - - Mat4Identity (mat); - VectorNegate (r_refdef.vieworg, mat + 12); - Mat4Mult (glsl_view, mat, glsl_view); + mmulf (glsl_view, z_up, r_refdef.camera_inverse); qfeglEnable (GL_CULL_FACE); qfeglEnable (GL_DEPTH_TEST); } -static void -R_RenderEntities (void) +void +glsl_R_RenderEntities (entqueue_t *queue) { - entity_t *ent; int begun; - if (!r_drawentities->int_val) + if (!r_drawentities) return; #define RE_LOOP(type_name, Type) \ do { \ begun = 0; \ - for (ent = r_ent_queue; ent; ent = ent->next) { \ - if (ent->model->type != mod_##type_name) \ - continue; \ + for (size_t i = 0; i < queue->ent_queues[mod_##type_name].size; \ + i++) { \ + entity_t ent = queue->ent_queues[mod_##type_name].a[i]; \ if (!begun) { \ glsl_R_##Type##Begin (); \ begun = 1; \ } \ - currententity = ent; \ - glsl_R_Draw##Type (); \ + glsl_R_Draw##Type (ent); \ } \ if (begun) \ glsl_R_##Type##End (); \ @@ -181,17 +115,21 @@ R_RenderEntities (void) static void R_DrawViewModel (void) { - currententity = vr_data.view_model; + entity_t ent = vr_data.view_model; + if (!Entity_Valid (ent)) { + return; + } + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); if (vr_data.inhibit_viewmodel - || !r_drawviewmodel->int_val - || !r_drawentities->int_val - || !currententity->model) + || !r_drawviewmodel + || !r_drawentities + || !renderer->model) return; // hack the depth range to prevent view model from poking into walls qfeglDepthRangef (0, 0.3); glsl_R_AliasBegin (); - glsl_R_DrawAlias (); + glsl_R_DrawAlias (ent); glsl_R_AliasEnd (); qfeglDepthRangef (0, 1); } @@ -199,59 +137,48 @@ R_DrawViewModel (void) void glsl_R_RenderView (void) { - double t[10] = {}; - int speeds = r_speeds->int_val; - - if (speeds) - t[0] = Sys_DoubleTime (); - glsl_R_SetupFrame (); - R_SetupView (); - if (speeds) - t[1] = Sys_DoubleTime (); - R_MarkLeaves (); - if (speeds) - t[2] = Sys_DoubleTime (); - R_PushDlights (vec3_origin); - if (speeds) - t[3] = Sys_DoubleTime (); - glsl_R_DrawWorld (); - if (speeds) - t[4] = Sys_DoubleTime (); - glsl_R_DrawSky (); - if (speeds) - t[5] = Sys_DoubleTime (); - R_RenderEntities (); - if (speeds) - t[6] = Sys_DoubleTime (); - glsl_R_DrawWaterSurfaces (); - if (speeds) - t[7] = Sys_DoubleTime (); - glsl_R_DrawParticles (); - if (speeds) - t[8] = Sys_DoubleTime (); - R_DrawViewModel (); - if (speeds) - t[9] = Sys_DoubleTime (); - if (speeds) { - Sys_Printf ("frame: %g, setup: %g, mark: %g, pushdl: %g, world: %g," - " sky: %g, ents: %g, water: %g, part: %g, view: %g\n", - (t[9] - t[0]) * 1000, (t[1] - t[0]) * 1000, - (t[2] - t[1]) * 1000, (t[3] - t[2]) * 1000, - (t[4] - t[3]) * 1000, (t[5] - t[4]) * 1000, - (t[6] - t[5]) * 1000, (t[7] - t[6]) * 1000, - (t[8] - t[7]) * 1000, (t[9] - t[8]) * 1000); + if (!r_refdef.worldmodel) { + return; } + + memcpy (glsl_projection, glsl_ctx->projection, sizeof (mat4f_t)); + + R_SetupView (); + glsl_R_DrawWorld (); + glsl_R_DrawSky (); + if (Entity_Valid (vr_data.view_model)) { + R_DrawViewModel (); + } +} + +static void +glsl_R_TimeRefresh_f (void) +{ +/* FIXME update for simd + double start, stop, time; + int i; + + glsl_ctx->end_rendering (); + + start = Sys_DoubleTime (); + for (i = 0; i < 128; i++) { + r_refdef.viewangles[1] = i * (360.0 / 128.0); + glsl_R_RenderView (); + glsl_ctx->end_rendering (); + } + + stop = Sys_DoubleTime (); + time = stop - start; + Sys_Printf ("%g seconds (%g fps)\n", time, 128 / time); +*/ } void glsl_R_Init (void) { - Cmd_AddCommand ("pointfile", glsl_R_ReadPointFile_f, - "Load a pointfile to determine map leaks."); Cmd_AddCommand ("timerefresh", glsl_R_TimeRefresh_f, "Test the current refresh rate for the current location."); R_Init_Cvars (); - glsl_R_Particles_Init_Cvars (); glsl_Draw_Init (); SCR_Init (); glsl_R_InitBsp (); @@ -259,61 +186,46 @@ glsl_R_Init (void) glsl_R_InitIQM (); glsl_R_InitSprites (); glsl_R_InitParticles (); - glsl_Fog_Init (); + glsl_InitFisheye (); + glsl_InitWarp (); Skin_Init (); } void -glsl_R_NewMap (model_t *worldmodel, struct model_s **models, int num_models) +glsl_R_Shutdown (void) +{ + Skin_Shutdown(); + + glsl_R_ShutdownParticles (); + glsl_Lightmap_Shutdown (); + glsl_R_ShutdownBsp (); + SCR_Shutdown (); + glsl_Draw_Shutdown (); +} + +void +glsl_R_NewScene (scene_t *scene) { int i; for (i = 0; i < 256; i++) d_lightstylevalue[i] = 264; // normal light value - memset (&r_worldentity, 0, sizeof (r_worldentity)); - r_worldentity.model = worldmodel; + r_refdef.worldmodel = scene->worldmodel; // Force a vis update - r_viewleaf = NULL; - R_MarkLeaves (); + R_MarkLeaves (0, 0, 0, 0); - R_FreeAllEntities (); - glsl_R_ClearParticles (); - glsl_R_RegisterTextures (models, num_models); - glsl_R_BuildLightmaps (models, num_models); - glsl_R_BuildDisplayLists (models, num_models); -} - -void -glsl_R_LineGraph (int x, int y, int *h_vals, int count) -{ + R_ClearParticles (); + glsl_R_RegisterTextures (scene->models, scene->num_models); + glsl_R_BuildLightmaps (scene->models, scene->num_models); + glsl_R_BuildDisplayLists (scene->models, scene->num_models); } void glsl_R_ClearState (void) { - R_ClearEfrags (); + r_refdef.worldmodel = 0; R_ClearDlights (); - glsl_R_ClearParticles (); -} - -void -glsl_R_TimeRefresh_f (void) -{ - double start, stop, time; - int i; - - vid.end_rendering (); - - start = Sys_DoubleTime (); - for (i = 0; i < 128; i++) { - r_refdef.viewangles[1] = i * (360.0 / 128.0); - glsl_R_RenderView (); - vid.end_rendering (); - } - - stop = Sys_DoubleTime (); - time = stop - start; - Sys_Printf ("%g seconds (%g fps)\n", time, 128 / time); + R_ClearParticles (); } diff --git a/libs/video/renderer/glsl/glsl_particles.c b/libs/video/renderer/glsl/glsl_particles.c index 1da8add27..f86d7afb6 100644 --- a/libs/video/renderer/glsl/glsl_particles.c +++ b/libs/video/renderer/glsl/glsl_particles.c @@ -1,5 +1,5 @@ /* - gl_dyn_part.c + glsl_particles.c OpenGL particle system. @@ -28,9 +28,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -50,6 +47,9 @@ #include "QF/render.h" #include "QF/sys.h" #include "QF/va.h" + +#include "QF/scene/entity.h" + #include "QF/GLSL/defines.h" #include "QF/GLSL/funcs.h" //#include "QF/GL/qf_explosions.h" @@ -59,21 +59,12 @@ #include "r_internal.h" -static mtstate_t mt; // private PRNG state - //FIXME not part of GLES, but needed for GL #ifndef GL_VERTEX_PROGRAM_POINT_SIZE # define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 #endif -//FIXME should not be here -static int ramp1[8] = { 0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61 }; -//static int ramp2[8] = { 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66 }; -static int ramp3[8] = { 0x6d, 0x6b, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 }; -static vec3_t vertex_normals[NUMVERTEXNORMALS] = { -#include "anorms.h" -}; - +static uint32_t maxparticles; static GLushort *pVAindices; static partvert_t *particleVertexArray; @@ -186,7 +177,7 @@ static struct { {"vcolora", 0}, {"vcolorb", 0}, }; - +#if 0 typedef struct trail_s { struct trail_s *next; struct trail_s **prev; @@ -204,9 +195,9 @@ typedef struct trailvtx_s { quat_t colorb; } trailvtx_t; -static trail_t *trails_freelist; +ALLOC_STATE (trail_t, trails); +ALLOC_STATE (particle_t, points); static trail_t *trails_active; -static particle_t *points_freelist; static trail_t * new_trail (void) @@ -236,103 +227,63 @@ free_point (particle_t *point) { FREE (points, point); } - -inline static void -particle_new (const char *type, int texnum, const vec3_t org, float scale, - const vec3_t vel, float die, int color, float alpha, float ramp) +#endif +static void +alloc_arrays (psystem_t *ps) { - particle_t *part; + if (ps->maxparticles > maxparticles) { + maxparticles = ps->maxparticles; + if (particleVertexArray) + free (particleVertexArray); + printf ("alloc_arrays: %d\n", ps->maxparticles); + particleVertexArray = calloc (ps->maxparticles * 4, + sizeof (partvert_t)); -/* - // Uncomment this for particle debugging! - if (numparticles >= r_maxparticles) { - Sys_Error ("FAILED PARTICLE ALLOC!"); - return NULL; + if (pVAindices) + free (pVAindices); + pVAindices = calloc (ps->maxparticles * 6, sizeof (GLushort)); + for (uint32_t i = 0; i < ps->maxparticles; i++) { + pVAindices[i * 6 + 0] = i * 4 + 0; + pVAindices[i * 6 + 1] = i * 4 + 1; + pVAindices[i * 6 + 2] = i * 4 + 2; + pVAindices[i * 6 + 3] = i * 4 + 0; + pVAindices[i * 6 + 4] = i * 4 + 2; + pVAindices[i * 6 + 5] = i * 4 + 3; + } } -*/ - - part = &particles[numparticles++]; - - VectorCopy (org, part->org); - part->color = color; - part->tex = texnum; - part->scale = scale; - part->alpha = alpha; - VectorCopy (vel, part->vel); - part->die = die; - part->ramp = ramp; - part->physics = R_ParticlePhysics (type); } -/* - particle_new_random - - note that org_fuzz & vel_fuzz should be ints greater than 0 if you are - going to bother using this function. -*/ -inline static void -particle_new_random (const char *type, int texnum, const vec3_t org, - int org_fuzz, float scale, int vel_fuzz, float die, - int color, float alpha, float ramp) +static void +glsl_particles_f (void *data, const cvar_t *cvar) { - float o_fuzz = org_fuzz, v_fuzz = vel_fuzz; - int rnd; - vec3_t porg, pvel; - - rnd = mtwist_rand (&mt); - porg[0] = o_fuzz * ((rnd & 63) - 31.5) / 63.0 + org[0]; - porg[1] = o_fuzz * (((rnd >> 6) & 63) - 31.5) / 63.0 + org[1]; - porg[2] = o_fuzz * (((rnd >> 10) & 63) - 31.5) / 63.0 + org[2]; - rnd = mtwist_rand (&mt); - pvel[0] = v_fuzz * ((rnd & 63) - 31.5) / 63.0; - pvel[1] = v_fuzz * (((rnd >> 6) & 63) - 31.5) / 63.0; - pvel[2] = v_fuzz * (((rnd >> 10) & 63) - 31.5) / 63.0; - - particle_new (type, texnum, porg, scale, pvel, die, color, alpha, ramp); + alloc_arrays (&r_psystem);//FIXME } -/* -inline static void -particle_new_veryrandom (ptype_t type, int texnum, const vec3_t org, - int org_fuzz, float scale, int vel_fuzz, float die, - int color, float alpha, float ramp) -{ - vec3_t porg, pvel; - - porg[0] = qfrandom (org_fuzz * 2) - org_fuzz + org[0]; - porg[1] = qfrandom (org_fuzz * 2) - org_fuzz + org[1]; - porg[2] = qfrandom (org_fuzz * 2) - org_fuzz + org[2]; - pvel[0] = qfrandom (vel_fuzz * 2) - vel_fuzz; - pvel[1] = qfrandom (vel_fuzz * 2) - vel_fuzz; - pvel[2] = qfrandom (vel_fuzz * 2) - vel_fuzz; - particle_new (type, texnum, porg, scale, pvel, die, color, alpha, ramp); -} -*/ - void -glsl_R_ClearParticles (void) +glsl_R_ShutdownParticles (void) { - numparticles = 0; + free (particleVertexArray); + free (pVAindices); } void glsl_R_InitParticles (void) { shader_t *vert_shader, *frag_shader; - unsigned i; int vert; int frag; float v[2] = {0, 0}; byte data[64][64][2]; tex_t *tex; - +#if 0 R_LoadParticles (); - - mtwist_seed (&mt, 0xdeadbeef); +#endif + Cvar_AddListener (Cvar_FindVar ("r_particles"), glsl_particles_f, 0); + Cvar_AddListener (Cvar_FindVar ("r_particles_max"), glsl_particles_f, 0); qfeglEnable (GL_VERTEX_PROGRAM_POINT_SIZE); qfeglGetFloatv (GL_ALIASED_POINT_SIZE_RANGE, v); - Sys_MaskPrintf (SYS_GLSL, "point size: %g - %g\n", v[0], v[1]); + Sys_MaskPrintf (SYS_glsl, "point size: %g - %g\n", v[0], v[1]); vert_shader = GLSL_BuildShader (particle_point_vert_effects); frag_shader = GLSL_BuildShader (particle_point_frag_effects); @@ -404,342 +355,9 @@ glsl_R_InitParticles (void) GL_UNSIGNED_BYTE, tex->data); free (tex); - if (particleVertexArray) - free (particleVertexArray); - particleVertexArray = calloc (r_maxparticles * 4, sizeof (partvert_t)); - - if (pVAindices) - free (pVAindices); - pVAindices = calloc (r_maxparticles * 6, sizeof (GLushort)); - for (i = 0; i < r_maxparticles; i++) { - pVAindices[i * 6 + 0] = i * 4 + 0; - pVAindices[i * 6 + 1] = i * 4 + 1; - pVAindices[i * 6 + 2] = i * 4 + 2; - pVAindices[i * 6 + 3] = i * 4 + 0; - pVAindices[i * 6 + 4] = i * 4 + 2; - pVAindices[i * 6 + 5] = i * 4 + 3; - } + alloc_arrays (&r_psystem); } - -void -glsl_R_ReadPointFile_f (void) -{ - const char *name; - char *mapname; - int c, r; - vec3_t org; - QFile *f; - - mapname = strdup (r_worldentity.model->name); - if (!mapname) - Sys_Error ("Can't duplicate mapname!"); - QFS_StripExtension (mapname, mapname); - - name = va ("%s.pts", mapname); - free (mapname); - - f = QFS_FOpenFile (name); - if (!f) { - Sys_Printf ("couldn't open %s\n", name); - return; - } - - Sys_MaskPrintf (SYS_DEV, "Reading %s...\n", name); - c = 0; - for (;;) { - char buf[64]; - - Qgets (f, buf, sizeof (buf)); - r = sscanf (buf, "%f %f %f\n", &org[0], &org[1], &org[2]); - if (r != 3) - break; - c++; - - if (numparticles >= r_maxparticles) { - Sys_MaskPrintf (SYS_DEV, "Not enough free particles\n"); - break; - } else { - particle_new ("pt_static", part_tex_dot, org, 1.5, vec3_origin, - 99999, (-c) & 15, 1.0, 0.0); - } - } - Qclose (f); - Sys_MaskPrintf (SYS_DEV, "%i points read\n", c); -} - -static void -R_ParticleExplosion_QF (const vec3_t org) -{ -// R_NewExplosion (org); - if (numparticles >= r_maxparticles) - return; - particle_new_random ("pt_smokecloud", part_tex_smoke, org, 4, 30, 8, - vr_data.realtime + 5.0, (mtwist_rand (&mt) & 7) + 8, - 0.5 + qfrandom (0.25), 0.0); -} - -static void -R_ParticleExplosion2_QF (const vec3_t org, int colorStart, int colorLength) -{ - unsigned int i, j = 512; - - if (numparticles >= r_maxparticles) - return; - else if (numparticles + j >= r_maxparticles) - j = r_maxparticles - numparticles; - - for (i = 0; i < j; i++) { - particle_new_random ("pt_blob", part_tex_dot, org, 16, 2, 256, - vr_data.realtime + 0.3, - colorStart + (i % colorLength), 1.0, 0.0); - } -} - -static void -R_BlobExplosion_QF (const vec3_t org) -{ - unsigned int i; - unsigned int j = 1024; - - if (numparticles >= r_maxparticles) - return; - else if (numparticles + j >= r_maxparticles) - j = r_maxparticles - numparticles; - - for (i = 0; i < j >> 1; i++) { - particle_new_random ("pt_blob", part_tex_dot, org, 12, 2, 256, - vr_data.realtime + 1.0 + (mtwist_rand (&mt) & 7) * 0.05, - 66 + i % 6, 1.0, 0.0); - } - for (i = 0; i < j / 2; i++) { - particle_new_random ("pt_blob2", part_tex_dot, org, 12, 2, 256, - vr_data.realtime + 1.0 + (mtwist_rand (&mt) & 7) * 0.05, - 150 + i % 6, 1.0, 0.0); - } -} - -static inline void -R_RunSparkEffect_QF (const vec3_t org, int count, int ofuzz) -{ - if (numparticles >= r_maxparticles) - return; - particle_new ("pt_smokecloud", part_tex_smoke, org, ofuzz * 0.08, - vec3_origin, vr_data.realtime + 9, 12 + (mtwist_rand (&mt) & 3), - 0.25 + qfrandom (0.125), 0.0); - - if (numparticles + count >= r_maxparticles) - count = r_maxparticles - numparticles; - if (count) { - int orgfuzz = ofuzz * 3 / 4; - if (orgfuzz < 1) - orgfuzz = 1; - - while (count--) { - int color = mtwist_rand (&mt) & 7; - - particle_new_random ("pt_fallfadespark", part_tex_dot, org, orgfuzz, - 0.7, 96, vr_data.realtime + 5.0, ramp1[color], - 1.0, color); - } - } -} - -static inline void -R_BloodPuff_QF (const vec3_t org, int count) -{ - if (numparticles >= r_maxparticles) - return; - - particle_new ("pt_bloodcloud", part_tex_smoke, org, count / 5, vec3_origin, - vr_data.realtime + 99.0, 70 + (mtwist_rand (&mt) & 3), 0.5, 0.0); -} - -static void -R_BloodPuffEffect_QF (const vec3_t org, int count) -{ - R_BloodPuff_QF (org, count); -} - -static void -R_GunshotEffect_QF (const vec3_t org, int count) -{ - int scale = 16; - - scale += count / 15; - R_RunSparkEffect_QF (org, count >> 1, scale); -} - -static void -R_LightningBloodEffect_QF (const vec3_t org) -{ - int count = 7; - - R_BloodPuff_QF (org, 50); - - if (numparticles >= r_maxparticles) - return; - particle_new ("pt_smokecloud", part_tex_smoke, org, 3.0, vec3_origin, - vr_data.realtime + 9.0, 12 + (mtwist_rand (&mt) & 3), - 0.25 + qfrandom (0.125), 0.0); - - if (numparticles + count >= r_maxparticles) - count = r_maxparticles - numparticles; - while (count--) - particle_new_random ("pt_fallfade", part_tex_spark, org, 12, 2.0, 128, - vr_data.realtime + 5.0, 244 + (count % 3), - 1.0, 0.0); -} - -static void -R_RunParticleEffect_QF (const vec3_t org, const vec3_t dir, int color, - int count) -{ - float scale; - int i; - vec3_t porg; - - if (numparticles >= r_maxparticles) - return; - - scale = pow (count, 0.23); // calculate scale before clipping to part. max - - if (numparticles + count >= r_maxparticles) - count = r_maxparticles - numparticles; - - for (i = 0; i < count; i++) { - int rnd = mtwist_rand (&mt); - - porg[0] = org[0] + scale * (((rnd >> 3) & 15) - 7.5); - porg[1] = org[1] + scale * (((rnd >> 7) & 15) - 7.5); - porg[2] = org[2] + scale * (((rnd >> 11) & 15) - 7.5); - // Note that ParseParticleEffect handles (dir * 15) - particle_new ("pt_grav", part_tex_dot, porg, 1.5, dir, - vr_data.realtime + 0.1 * (i % 5), - (color & ~7) + (rnd & 7), 1.0, 0.0); - } -} - -static void -R_SpikeEffect_QF (const vec3_t org) -{ - R_RunSparkEffect_QF (org, 5, 8); -} - -static void -R_SuperSpikeEffect_QF (const vec3_t org) -{ - R_RunSparkEffect_QF (org, 10, 8); -} - -static void -R_KnightSpikeEffect_QF (const vec3_t org) -{ - unsigned int count = 10; - - if (numparticles >= r_maxparticles) - return; - particle_new ("pt_smokecloud", part_tex_smoke, org, 1.0, vec3_origin, - vr_data.realtime + 9.0, 234, 0.25 + qfrandom (0.125), 0.0); - - if (numparticles + count >= r_maxparticles) - count = r_maxparticles - numparticles; - while (count--) - particle_new_random ("pt_fallfade", part_tex_dot, org, 6, 0.7, 96, - vr_data.realtime + 5.0, 234, 1.0, 0.0); -} - -static void -R_WizSpikeEffect_QF (const vec3_t org) -{ - unsigned int count = 15; - - if (numparticles >= r_maxparticles) - return; - particle_new ("pt_smokecloud", part_tex_smoke, org, 2.0, vec3_origin, - vr_data.realtime + 9.0, 63, 0.25 + qfrandom (0.125), 0.0); - - if (numparticles + count >= r_maxparticles) - count = r_maxparticles - numparticles; - while (count--) - particle_new_random ("pt_fallfade", part_tex_dot, org, 12, 0.7, 96, - vr_data.realtime + 5.0, 63, 1.0, 0.0); -} - -static void -R_LavaSplash_QF (const vec3_t org) -{ - float vel; - int rnd, i, j; - int k = 256; - vec3_t dir, porg, pvel; - - if (numparticles + k >= r_maxparticles) { - return; - } // else if (numparticles + k >= r_maxparticles) { -// k = r_maxparticles - numparticles; -// } - - dir[2] = 256; - for (i = -128; i < 128; i += 16) { - for (j = -128; j < 128; j += 16) { - rnd = mtwist_rand (&mt); - dir[0] = j + (rnd & 7); - dir[1] = i + ((rnd >> 6) & 7); - - porg[0] = org[0] + dir[0]; - porg[1] = org[1] + dir[1]; - porg[2] = org[2] + ((rnd >> 9) & 63); - - VectorNormalize (dir); - rnd = mtwist_rand (&mt); - vel = 50.0 + 0.5 * (float) (rnd & 127); - VectorScale (dir, vel, pvel); - particle_new ("pt_grav", part_tex_dot, porg, 3, pvel, - vr_data.realtime + 2.0 + ((rnd >> 7) & 31) * 0.02, - 224 + ((rnd >> 12) & 7), 0.75, 0.0); - } - } -} - -static void -R_TeleportSplash_QF (const vec3_t org) -{ - float vel; - int rnd, i, j, k; - int l = 896; - vec3_t dir, pdir, porg, pvel; - - if (numparticles + l >= r_maxparticles) { - return; - } // else if (numparticles + l >= r_maxparticles) { -// l = r_maxparticles - numparticles; -// } - - for (k = -24; k < 32; k += 4) { - dir[2] = k * 8; - for (i = -16; i < 16; i += 4) { - dir[1] = i * 8; - for (j = -16; j < 16; j += 4) { - dir[0] = j * 8; - - VectorCopy (dir, pdir); - VectorNormalize (pdir); - - rnd = mtwist_rand (&mt); - porg[0] = org[0] + i + (rnd & 3); - porg[1] = org[1] + j + ((rnd >> 2) & 3); - porg[2] = org[2] + k + ((rnd >> 4) & 3); - - vel = 50 + ((rnd >> 6) & 63); - VectorScale (pdir, vel, pvel); - particle_new ("pt_grav", part_tex_spark, porg, 0.6, pvel, - (vr_data.realtime + 0.2 + (mtwist_rand (&mt) & 15) * 0.01), - (7 + ((rnd >> 12) & 7)), 1.0, 0.0); - } - } - } -} - +#if 0 static void add_trail_to_ent (entity_t *ent) { @@ -813,941 +431,6 @@ R_RocketTrail_trail (entity_t *ent) } } -static void -R_RocketTrail_QF (entity_t *ent) -{ - float dist, maxlen, origlen, percent, pscale, pscalenext; - float len = 0.0; - vec3_t old_origin, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - origlen = vr_data.frametime / maxlen; - pscale = 1.5 + qfrandom (1.5); - - while (len < maxlen) { - pscalenext = 1.5 + qfrandom (1.5); - dist = (pscale + pscalenext) * 3.0; - percent = len * origlen; - - particle_new ("pt_smoke", part_tex_smoke, old_origin, - pscale + percent * 4.0, vec3_origin, - vr_data.realtime + 2.0 - percent * 2.0, - 12 + (mtwist_rand (&mt) & 3), - 0.5 + qfrandom (0.125) - percent * 0.40, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorMultAdd (old_origin, len, vec, old_origin); - pscale = pscalenext; - } -} - -static void -R_GrenadeTrail_QF (entity_t *ent) -{ - float dist, maxlen, origlen, percent, pscale, pscalenext; - float len = 0.0; - vec3_t old_origin, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - origlen = vr_data.frametime / maxlen; - pscale = 6.0 + qfrandom (7.0); - - while (len < maxlen) { - pscalenext = 6.0 + qfrandom (7.0); - dist = (pscale + pscalenext) * 2.0; - percent = len * origlen; - - particle_new ("pt_smoke", part_tex_smoke, old_origin, - pscale + percent * 4.0, vec3_origin, - vr_data.realtime + 2.0 - percent * 2.0, - 1 + (mtwist_rand (&mt) & 3), - 0.625 + qfrandom (0.125) - percent * 0.40, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorMultAdd (old_origin, len, vec, old_origin); - pscale = pscalenext; - } -} - -static void -R_BloodTrail_QF (entity_t *ent) -{ - float dist, maxlen, origlen, percent, pscale, pscalenext; - float len = 0.0; - int j; - vec3_t old_origin, porg, pvel, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - origlen = vr_data.frametime / maxlen; - pscale = 5.0 + qfrandom (10.0); - - while (len < maxlen) { - pscalenext = 5.0 + qfrandom (10.0); - dist = (pscale + pscalenext) * 1.5; - - for (j = 0; j < 3; j++) { - pvel[j] = qfrandom (24.0) - 12.0; - porg[j] = old_origin[j] + qfrandom (3.0) - 1.5; - } - - percent = len * origlen; - pvel[2] -= percent * 40.0; - - particle_new ("pt_grav", part_tex_smoke, porg, pscale, pvel, - vr_data.realtime + 2.0 - percent * 2.0, - 68 + (mtwist_rand (&mt) & 3), 1.0, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorMultAdd (old_origin, len, vec, old_origin); - pscale = pscalenext; - } -} - -static void -R_SlightBloodTrail_QF (entity_t *ent) -{ - float dist, maxlen, origlen, percent, pscale, pscalenext; - float len = 0.0; - int j; - vec3_t old_origin, porg, pvel, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - origlen = vr_data.frametime / maxlen; - pscale = 1.5 + qfrandom (7.5); - - while (len < maxlen) { - pscalenext = 1.5 + qfrandom (7.5); - dist = (pscale + pscalenext) * 1.5; - - for (j = 0; j < 3; j++) { - pvel[j] = (qfrandom (12.0) - 6.0); - porg[j] = old_origin[j] + qfrandom (3.0) - 1.5; - } - - percent = len * origlen; - pvel[2] -= percent * 40; - - particle_new ("pt_grav", part_tex_smoke, porg, pscale, pvel, - vr_data.realtime + 1.5 - percent * 1.5, - 68 + (mtwist_rand (&mt) & 3), 0.75, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorMultAdd (old_origin, len, vec, old_origin); - pscale = pscalenext; - } -} - -static void -R_WizTrail_QF (entity_t *ent) -{ - float maxlen, origlen, percent; - float dist = 3.0, len = 0.0; - static int tracercount; - vec3_t old_origin, pvel, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - origlen = vr_data.frametime / maxlen; - VectorScale (vec, maxlen - dist, subtract); - - while (len < maxlen) { - percent = len * origlen; - - tracercount++; - if (tracercount & 1) { - pvel[0] = 30.0 * vec[1]; - pvel[1] = 30.0 * -vec[0]; - } else { - pvel[0] = 30.0 * -vec[1]; - pvel[1] = 30.0 * vec[0]; - } - pvel[2] = 0.0; - - particle_new ("pt_flame", part_tex_smoke, old_origin, - 2.0 + qfrandom (1.0) - percent * 2.0, pvel, - vr_data.realtime + 0.5 - percent * 0.5, - 52 + (mtwist_rand (&mt) & 4), 1.0 - percent * 0.125, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - -static void -R_FlameTrail_QF (entity_t *ent) -{ - float maxlen, origlen, percent; - float dist = 3.0, len = 0.0; - static int tracercount; - vec3_t old_origin, pvel, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - origlen = vr_data.frametime / maxlen; - VectorScale (vec, maxlen - dist, subtract); - - while (len < maxlen) { - percent = len * origlen; - - tracercount++; - if (tracercount & 1) { - pvel[0] = 30.0 * vec[1]; - pvel[1] = 30.0 * -vec[0]; - } else { - pvel[0] = 30.0 * -vec[1]; - pvel[1] = 30.0 * vec[0]; - } - pvel[2] = 0.0; - - particle_new ("pt_flame", part_tex_smoke, old_origin, - 2.0 + qfrandom (1.0) - percent * 2.0, pvel, - vr_data.realtime + 0.5 - percent * 0.5, 234, - 1.0 - percent * 0.125, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - -static void -R_VoorTrail_QF (entity_t *ent) -{ - float maxlen, origlen, percent; - float dist = 3.0, len = 0.0; - int j; - vec3_t subtract, old_origin, porg, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - origlen = vr_data.frametime / maxlen; - VectorScale (vec, maxlen - dist, subtract); - - while (len < maxlen) { - percent = len * origlen; - - for (j = 0; j < 3; j++) - porg[j] = old_origin[j] + qfrandom (16.0) - 8.0; - - particle_new ("pt_static", part_tex_dot, porg, 1.0 + qfrandom (1.0), - vec3_origin, vr_data.realtime + 0.3 - percent * 0.3, - 9 * 16 + 8 + (mtwist_rand (&mt) & 3), 1.0, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - -static void -R_GlowTrail_QF (entity_t *ent, int glow_color) -{ - float maxlen, origlen, percent; - float dist = 3.0, len = 0.0; - int rnd; - vec3_t old_origin, org, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - origlen = vr_data.frametime / maxlen; - VectorScale (vec, (maxlen - dist), subtract); - - while (len < maxlen) { - percent = len * origlen; - - rnd = mtwist_rand (&mt); - org[0] = old_origin[0] + ((rnd >> 12) & 7) * (5.0/7.0) - 2.5; - org[1] = old_origin[1] + ((rnd >> 9) & 7) * (5.0/7.0) - 2.5; - org[2] = old_origin[2] + ((rnd >> 6) & 7) * (5.0/7.0) - 2.5; - - particle_new ("pt_smoke", part_tex_dot, org, 1.0, vec3_origin, - vr_data.realtime + 2.0 - percent * 0.2, glow_color, - 1.0, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - -static void -R_ParticleExplosion_EE (const vec3_t org) -{ -/* - R_NewExplosion (org); -*/ - if (numparticles >= r_maxparticles) - return; - particle_new_random ("pt_smokecloud", part_tex_smoke, org, 4, 30, 8, - vr_data.realtime + 5.0, mtwist_rand (&mt) & 255, - 0.5 + qfrandom (0.25), 0.0); -} - -static void -R_TeleportSplash_EE (const vec3_t org) -{ - float vel; - int rnd, i, j, k; - int l = 896; - vec3_t dir, porg, pvel; - - if (numparticles + l >= r_maxparticles) { - return; - } // else if (numparticles + l >= r_maxparticles) { -// l = r_maxparticles - numparticles; -// } - - for (k = -24; k < 32; k += 4) { - dir[2] = k * 8; - for (i = -16; i < 16; i += 4) { - dir[1] = i * 8; - for (j = -16; j < 16; j += 4) { - dir[0] = j * 8; - - rnd = mtwist_rand (&mt); - porg[0] = org[0] + i + (rnd & 3); - porg[1] = org[1] + j + ((rnd >> 2) & 3); - porg[2] = org[2] + k + ((rnd >> 4) & 3); - - VectorNormalize (dir); - vel = 50 + ((rnd >> 6) & 63); - VectorScale (dir, vel, pvel); - particle_new ("pt_grav", part_tex_spark, porg, 0.6, pvel, - (vr_data.realtime + 0.2 + (mtwist_rand (&mt) & 15) * 0.01), - qfrandom (1.0), 1.0, 0.0); - } - } - } -} - -static void -R_RocketTrail_EE (entity_t *ent) -{ - float dist, maxlen, origlen, percent, pscale, pscalenext; - float len = 0.0; - vec3_t old_origin, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - origlen = vr_data.frametime / maxlen; - pscale = 1.5 + qfrandom (1.5); - - while (len < maxlen) { - pscalenext = 1.5 + qfrandom (1.5); - dist = (pscale + pscalenext) * 3.0; - percent = len * origlen; - - particle_new ("pt_smoke", part_tex_smoke, old_origin, - pscale + percent * 4.0, vec3_origin, - vr_data.realtime + 2.0 - percent * 2.0, - mtwist_rand (&mt) & 255, - 0.5 + qfrandom (0.125) - percent * 0.40, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorScale (vec, len, subtract); - VectorAdd (old_origin, subtract, old_origin); - pscale = pscalenext; - } -} - -static void -R_GrenadeTrail_EE (entity_t *ent) -{ - float dist, maxlen, origlen, percent, pscale, pscalenext; - float len = 0.0; - vec3_t old_origin, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, ent->old_origin, vec); - maxlen = VectorNormalize (vec); - origlen = vr_data.frametime / maxlen; - pscale = 6.0 + qfrandom (7.0); - - while (len < maxlen) { - pscalenext = 6.0 + qfrandom (7.0); - dist = (pscale + pscalenext) * 2.0; - percent = len * origlen; - - particle_new ("pt_smoke", part_tex_smoke, old_origin, - pscale + percent * 4.0, vec3_origin, - vr_data.realtime + 2.0 - percent * 2.0, - mtwist_rand (&mt) & 255, - 0.625 + qfrandom (0.125) - percent * 0.40, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorScale (vec, len, subtract); - VectorAdd (old_origin, subtract, old_origin); - pscale = pscalenext; - } -} - -static void -R_ParticleExplosion_ID (const vec3_t org) -{ - unsigned int i; - unsigned int j = 1024; - - if (numparticles >= r_maxparticles) - return; - else if (numparticles + j >= r_maxparticles) - j = r_maxparticles - numparticles; - - for (i = 0; i < j >> 1; i++) { - particle_new_random ("pt_explode", part_tex_dot, org, 16, 1.0, 256, - vr_data.realtime + 5.0, ramp1[0], 1.0, i & 3); - } - for (i = 0; i < j / 2; i++) { - particle_new_random ("pt_explode2", part_tex_dot, org, 16, 1.0, 256, - vr_data.realtime + 5.0, ramp1[0], 1.0, i & 3); - } -} - -static void -R_BlobExplosion_ID (const vec3_t org) -{ - unsigned int i; - unsigned int j = 1024; - - if (numparticles >= r_maxparticles) - return; - else if (numparticles + j >= r_maxparticles) - j = r_maxparticles - numparticles; - - for (i = 0; i < j >> 1; i++) { - particle_new_random ("pt_blob", part_tex_dot, org, 12, 1.0, 256, - vr_data.realtime + 1.0 + (mtwist_rand (&mt) & 8) * 0.05, - 66 + i % 6, 1.0, 0.0); - } - for (i = 0; i < j / 2; i++) { - particle_new_random ("pt_blob2", part_tex_dot, org, 12, 1.0, 256, - vr_data.realtime + 1.0 + (mtwist_rand (&mt) & 8) * 0.05, - 150 + i % 6, 1.0, 0.0); - } -} - -static inline void // FIXME: inline? -R_RunParticleEffect_ID (const vec3_t org, const vec3_t dir, int color, - int count) -{ - float scale; - int i; - vec3_t porg; - - if (numparticles >= r_maxparticles) - return; - - if (count > 130) // calculate scale before clipping to particle max - scale = 3.0; - else if (count > 20) - scale = 2.0; - else - scale = 1.0; - - if (numparticles + count >= r_maxparticles) - count = r_maxparticles - numparticles; - - for (i = 0; i < count; i++) { - int rnd = mtwist_rand (&mt); - - porg[0] = org[0] + scale * (((rnd >> 3) & 15) - 8); - porg[1] = org[1] + scale * (((rnd >> 7) & 15) - 8); - porg[2] = org[2] + scale * (((rnd >> 11) & 15) - 8); - - // Note that ParseParticleEffect handles (dir * 15) - particle_new ("pt_grav", part_tex_dot, porg, 1.0, dir, - vr_data.realtime + 0.1 * (i % 5), - (color & ~7) + (rnd & 7), 1.0, 0.0); - } -} - -static void -R_BloodPuffEffect_ID (const vec3_t org, int count) -{ - R_RunParticleEffect_ID (org, vec3_origin, 73, count); -} - -static void -R_GunshotEffect_ID (const vec3_t org, int count) -{ - R_RunParticleEffect_ID (org, vec3_origin, 0, count); -} - -static void -R_LightningBloodEffect_ID (const vec3_t org) -{ - R_RunParticleEffect_ID (org, vec3_origin, 225, 50); -} - -static void -R_SpikeEffect_ID (const vec3_t org) -{ - R_RunParticleEffect_ID (org, vec3_origin, 0, 10); -} - -static void -R_SuperSpikeEffect_ID (const vec3_t org) -{ - R_RunParticleEffect_ID (org, vec3_origin, 0, 20); -} - -static void -R_KnightSpikeEffect_ID (const vec3_t org) -{ - R_RunParticleEffect_ID (org, vec3_origin, 226, 20); -} - -static void -R_WizSpikeEffect_ID (const vec3_t org) -{ - R_RunParticleEffect_ID (org, vec3_origin, 20, 30); -} - -static void -R_LavaSplash_ID (const vec3_t org) -{ - float vel; - int rnd, i, j; - int k = 256; - vec3_t dir, porg, pvel; - - if (numparticles + k >= r_maxparticles) { - return; - } // else if (numparticles + k >= r_maxparticles) { -// k = r_maxparticles - numparticles; -// } - - dir[2] = 256; - for (i = -128; i < 128; i += 16) { - for (j = -128; j < 128; j += 16) { - rnd = mtwist_rand (&mt); - dir[0] = j + (rnd & 7); - dir[1] = i + ((rnd >> 6) & 7); - - porg[0] = org[0] + dir[0]; - porg[1] = org[1] + dir[1]; - porg[2] = org[2] + ((rnd >> 9) & 63); - - VectorNormalize (dir); - rnd = mtwist_rand (&mt); - vel = 50 + (rnd & 63); - VectorScale (dir, vel, pvel); - particle_new ("pt_grav", part_tex_dot, porg, 1.0, pvel, - vr_data.realtime + 2 + ((rnd >> 7) & 31) * 0.02, - 224 + ((rnd >> 12) & 7), 1.0, 0.0); - } - } -} - -static void -R_TeleportSplash_ID (const vec3_t org) -{ - float vel; - int rnd, i, j, k; - int l = 896; - vec3_t dir, pdir, porg, pvel; - - if (numparticles + l >= r_maxparticles) { - return; - } // else if (numparticles + l >= r_maxparticles) { -// l = r_maxparticles - numparticles; -// } - - for (k = -24; k < 32; k += 4) { - dir[2] = k * 8; - for (i = -16; i < 16; i += 4) { - dir[1] = i * 8; - for (j = -16; j < 16; j += 4) { - dir[0] = j * 8; - - VectorCopy (dir, pdir); - VectorNormalize (pdir); - - rnd = mtwist_rand (&mt); - porg[0] = org[0] + i + (rnd & 3); - porg[1] = org[1] + j + ((rnd >> 2) & 3); - porg[2] = org[2] + k + ((rnd >> 4) & 3); - - vel = 50 + ((rnd >> 6) & 63); - VectorScale (pdir, vel, pvel); - particle_new ("pt_grav", part_tex_dot, porg, 1.0, pvel, - (vr_data.realtime + 0.2 + (mtwist_rand (&mt) & 7) * 0.02), - (7 + ((rnd >> 12) & 7)), 1.0, 0.0); - } - } - } -} - -static void -R_DarkFieldParticles_ID (entity_t *ent) -{ - int i, j, k, l = 64; - unsigned int rnd; - float vel; - vec3_t dir, org, porg, pvel; - - if (numparticles + l >= r_maxparticles) { - return; - } // else if (numparticles + l >= r_maxparticles) { -// l = r_maxparticles - numparticles; -// } - - VectorCopy (ent->origin, org); - - for (i = -16; i < 16; i += 8) { - dir [1] = i * 8; - for (j = -16; j < 16; j += 8) { - dir [0] = j * 8; - for (k = 0; k < 32; k += 8) { - dir [2] = k * 8; - rnd = mtwist_rand (&mt); - - porg[0] = org[0] + i + ((rnd >> 3) & 3); - porg[1] = org[1] + j + ((rnd >> 5) & 3); - porg[2] = org[2] + k + ((rnd >> 7) & 3); - - VectorNormalize (dir); - vel = 50 + ((rnd >> 9) & 63); - VectorScale (dir, vel, pvel); - particle_new ("pt_slowgrav", part_tex_dot, porg, 1.5, pvel, - (vr_data.realtime + 0.2 + (rnd & 7) * 0.02), - (150 + mtwist_rand (&mt) % 6), 1.0, 0.0); - } - } - } -} - -static vec3_t avelocities[NUMVERTEXNORMALS]; - -static void -R_EntityParticles_ID (entity_t *ent) -{ - int i, j = NUMVERTEXNORMALS; - float angle, sp, sy, cp, cy; // cr, sr - float beamlength = 16.0, dist = 64.0; - vec3_t forward, porg; - - if (numparticles + j >= r_maxparticles) { - return; - } else if (numparticles + j >= r_maxparticles) { - j = r_maxparticles - numparticles; - } - - for (i = 0; i < NUMVERTEXNORMALS; i++) { - int k; - for (k = 0; k < 3; k++) { - avelocities[i][k] = (mtwist_rand (&mt) & 255) * 0.01; - } - } - - for (i = 0; i < j; i++) { - angle = vr_data.realtime * avelocities[i][0]; - cy = cos (angle); - sy = sin (angle); - angle = vr_data.realtime * avelocities[i][1]; - cp = cos (angle); - sp = sin (angle); -// Next 3 lines results aren't currently used, may be in future. --Despair -// angle = vr_data.realtime * avelocities[i][2]; -// sr = sin (angle); -// cr = cos (angle); - - forward[0] = cp * cy; - forward[1] = cp * sy; - forward[2] = -sp; - - porg[0] = ent->origin[0] + vertex_normals[i][0] * dist + - forward[0] * beamlength; - porg[1] = ent->origin[1] + vertex_normals[i][1] * dist + - forward[1] * beamlength; - porg[2] = ent->origin[2] + vertex_normals[i][2] * dist + - forward[2] * beamlength; - particle_new ("pt_explode", part_tex_dot, porg, 1.0, vec3_origin, - vr_data.realtime + 0.01, 0x6f, 1.0, 0); - } -} - -static void -R_RocketTrail_ID (entity_t *ent) -{ - float maxlen; - float dist = 3.0, len = 0.0; - int ramp, rnd; - vec3_t old_origin, org, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, ent->old_origin, vec); - maxlen = VectorNormalize (vec); - VectorScale (vec, (maxlen - dist), subtract); - - while (len < maxlen) { - rnd = mtwist_rand (&mt); - org[0] = old_origin[0] + ((rnd >> 12) & 7) * (5.0/7.0) - 2.5; - org[1] = old_origin[1] + ((rnd >> 9) & 7) * (5.0/7.0) - 2.5; - org[2] = old_origin[2] + ((rnd >> 6) & 7) * (5.0/7.0) - 2.5; - ramp = rnd & 3; - - particle_new ("pt_fire", part_tex_dot, org, 1.0, vec3_origin, - vr_data.realtime + 2.0, ramp3[ramp], 1.0, ramp); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - -static void -R_GrenadeTrail_ID (entity_t *ent) -{ - float maxlen; - float dist = 3.0, len = 0.0; - unsigned int ramp, rnd; - vec3_t old_origin, org, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, ent->old_origin, vec); - maxlen = VectorNormalize (vec); - VectorScale (vec, maxlen - dist, subtract); - - while (len < maxlen) { - rnd = mtwist_rand (&mt); - org[0] = old_origin[0] + ((rnd >> 12) & 7) * (5.0/7.0) - 2.5; - org[1] = old_origin[1] + ((rnd >> 9) & 7) * (5.0/7.0) - 2.5; - org[2] = old_origin[2] + ((rnd >> 6) & 7) * (5.0/7.0) - 2.5; - ramp = (rnd & 3) + 2; - - particle_new ("pt_fire", part_tex_dot, org, 1.0, vec3_origin, - vr_data.realtime + 2.0, ramp3[ramp], 1.0, ramp); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - -static void -R_BloodTrail_ID (entity_t *ent) -{ - float maxlen; - float dist = 3.0, len = 0.0; - unsigned int rnd; - vec3_t old_origin, subtract, vec, porg; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - VectorScale (vec, maxlen - dist, subtract); - - while (len < maxlen) { - rnd = mtwist_rand (&mt); - porg[0] = old_origin[0] + ((rnd >> 12) & 7) * (5.0/7.0) - 2.5; - porg[1] = old_origin[1] + ((rnd >> 9) & 7) * (5.0/7.0) - 2.5; - porg[2] = old_origin[2] + ((rnd >> 6) & 7) * (5.0/7.0) - 2.5; - - particle_new ("pt_grav", part_tex_dot, porg, 1.0, vec3_origin, - vr_data.realtime + 2.0, 67 + (rnd & 3), 1.0, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - -static void -R_SlightBloodTrail_ID (entity_t *ent) -{ - float maxlen; - float dist = 6.0, len = 0.0; - unsigned int rnd; - vec3_t old_origin, porg, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - VectorScale (vec, maxlen - dist, subtract); - - while (len < maxlen) { - rnd = mtwist_rand (&mt); - porg[0] = old_origin[0] + ((rnd >> 12) & 7) * (5.0/7.0) - 2.5; - porg[1] = old_origin[1] + ((rnd >> 9) & 7) * (5.0/7.0) - 2.5; - porg[2] = old_origin[2] + ((rnd >> 6) & 7) * (5.0/7.0) - 2.5; - - particle_new ("pt_grav", part_tex_dot, porg, 1.0, vec3_origin, - vr_data.realtime + 1.5, 67 + (rnd & 3), 1.0, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - -static void -R_WizTrail_ID (entity_t *ent) -{ - float maxlen; - float dist = 3.0, len = 0.0; - static int tracercount; - vec3_t old_origin, pvel, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - VectorScale (vec, maxlen - dist, subtract); - - while (len < maxlen) { - tracercount++; - if (tracercount & 1) { - pvel[0] = 30.0 * vec[1]; - pvel[1] = 30.0 * -vec[0]; - } else { - pvel[0] = 30.0 * -vec[1]; - pvel[1] = 30.0 * vec[0]; - } - pvel[2] = 0.0; - - particle_new ("pt_static", part_tex_dot, old_origin, 1.0, pvel, - vr_data.realtime + 0.5, 52 + ((tracercount & 4) << 1), - 1.0, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - -static void -R_FlameTrail_ID (entity_t *ent) -{ - float maxlen; - float dist = 3.0, len = 0.0; - static int tracercount; - vec3_t old_origin, pvel, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - VectorScale (vec, maxlen - dist, subtract); - - while (len < maxlen) { - tracercount++; - if (tracercount & 1) { - pvel[0] = 30.0 * vec[1]; - pvel[1] = 30.0 * -vec[0]; - } else { - pvel[0] = 30.0 * -vec[1]; - pvel[1] = 30.0 * vec[0]; - } - pvel[2] = 0.0; - - particle_new ("pt_static", part_tex_dot, old_origin, 1.0, pvel, - vr_data.realtime + 0.5, 230 + ((tracercount & 4) << 1), - 1.0, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - -static void -R_VoorTrail_ID (entity_t *ent) -{ - float maxlen; - float dist = 3.0, len = 0.0; - unsigned int rnd; - vec3_t old_origin, porg, subtract, vec; - - if (numparticles >= r_maxparticles) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - maxlen = VectorNormalize (vec); - VectorScale (vec, maxlen - dist, subtract); - - while (len < maxlen) { - rnd = mtwist_rand (&mt); - porg[0] = old_origin[0] + ((rnd >> 3) & 15) - 7.5; - porg[1] = old_origin[1] + ((rnd >> 7) & 15) - 7.5; - porg[2] = old_origin[2] + ((rnd >> 11) & 15) - 7.5; - - particle_new ("pt_static", part_tex_dot, porg, 1.0, vec3_origin, - vr_data.realtime + 0.3, 9 * 16 + 8 + (rnd & 3), - 1.0, 0.0); - if (numparticles >= r_maxparticles) - break; - len += dist; - VectorAdd (old_origin, subtract, old_origin); - } -} - static inline void set_vertex (trailvtx_t *v, const particle_t *point, float w, const vec3_t bary, float off) @@ -1905,21 +588,19 @@ draw_trails (void) expire_trails (); } - +#endif static void -draw_qf_particles (void) +draw_qf_particles (psystem_t *psystem) { byte *at; - int activeparticles, maxparticle, j, vacount; - unsigned k; + int vacount; float minparticledist, scale; - particle_t *part; vec3_t up_scale, right_scale, up_right_scale, down_right_scale; partvert_t *VA; - mat4_t vp_mat; + mat4f_t vp_mat; quat_t fog; - Mat4Mult (glsl_projection, glsl_view, vp_mat); + mmulf (vp_mat, glsl_projection, glsl_view); qfeglDepthMask (GL_FALSE); qfeglUseProgram (quake_part.program); @@ -1927,11 +608,12 @@ draw_qf_particles (void) qfeglEnableVertexAttribArray (quake_part.color.location); qfeglEnableVertexAttribArray (quake_part.st.location); - VectorCopy (glsl_Fog_GetColor (), fog); - fog[3] = glsl_Fog_GetDensity () / 64.0; + Fog_GetColor (fog); + fog[3] = Fog_GetDensity () / 64.0; qfeglUniform4fv (quake_part.fog.location, 1, fog); - qfeglUniformMatrix4fv (quake_part.mvp_matrix.location, 1, false, vp_mat); + qfeglUniformMatrix4fv (quake_part.mvp_matrix.location, 1, false, + (vec_t*)&vp_mat[0]);//FIXME qfeglUniform1i (quake_part.texture.location, 0); qfeglActiveTexture (GL_TEXTURE0 + 0); @@ -1941,29 +623,28 @@ draw_qf_particles (void) // LordHavoc: particles should not affect zbuffer qfeglDepthMask (GL_FALSE); - minparticledist = DotProduct (r_refdef.vieworg, vpn) + - r_particles_nearclip->value; + minparticledist = DotProduct (r_refdef.frame.position, + r_refdef.frame.forward) + + r_particles_nearclip; - activeparticles = 0; vacount = 0; VA = particleVertexArray; - maxparticle = -1; - j = 0; - for (k = 0, part = particles; k < numparticles; k++, part++) { + for (unsigned i = 0; i < psystem->numparticles; i++) { + particle_t *p = &psystem->particles[i]; // Don't render particles too close to us. // Note, we must still do physics and such on them. - if (!(DotProduct (part->org, vpn) < minparticledist)) { - at = (byte *) &d_8to24table[(byte) part->color]; + if (!(DotProduct (p->pos, r_refdef.frame.forward) < minparticledist)) { + at = (byte *) &d_8to24table[(byte) p->icolor]; VA[0].color[0] = at[0]; VA[0].color[1] = at[1]; VA[0].color[2] = at[2]; - VA[0].color[3] = part->alpha * 255; + VA[0].color[3] = p->alpha * 255; memcpy (VA[1].color, VA[0].color, sizeof (VA[0].color)); memcpy (VA[2].color, VA[0].color, sizeof (VA[0].color)); memcpy (VA[3].color, VA[0].color, sizeof (VA[0].color)); - switch (part->tex) { + switch (p->tex) { case part_tex_dot: VA[0].texcoord[0] = 0.0; VA[0].texcoord[1] = 0.0; @@ -1996,34 +677,24 @@ draw_qf_particles (void) break; } - scale = part->scale; + scale = p->scale; - VectorScale (vup, scale, up_scale); - VectorScale (vright, scale, right_scale); + VectorScale (r_refdef.frame.up, scale, up_scale); + VectorScale (r_refdef.frame.right, scale, right_scale); VectorAdd (right_scale, up_scale, up_right_scale); VectorSubtract (right_scale, up_scale, down_right_scale); - VectorAdd (part->org, down_right_scale, VA[0].vertex); - VectorSubtract (part->org, up_right_scale, VA[1].vertex); - VectorSubtract (part->org, down_right_scale, VA[2].vertex); - VectorAdd (part->org, up_right_scale, VA[3].vertex); + VectorAdd (p->pos, down_right_scale, VA[0].vertex); + VectorSubtract (p->pos, up_right_scale, VA[1].vertex); + VectorSubtract (p->pos, down_right_scale, VA[2].vertex); + VectorAdd (p->pos, up_right_scale, VA[3].vertex); VA += 4; vacount += 6; } - - R_RunParticlePhysics (part); - - // LordHavoc: immediate removal of unnecessary particles (must be done - // to ensure compactor below operates properly in all cases) - if (part->die < vr_data.realtime) { - freeparticles[j++] = part; - } else { - maxparticle = k; - activeparticles++; - } } + qfeglVertexAttribPointer (quake_part.vertex.location, 3, GL_FLOAT, 0, sizeof (partvert_t), &particleVertexArray[0].vertex); @@ -2035,15 +706,6 @@ draw_qf_particles (void) &particleVertexArray[0].texcoord); qfeglDrawElements (GL_TRIANGLES, vacount, GL_UNSIGNED_SHORT, pVAindices); - k = 0; - while (maxparticle >= activeparticles) { - *freeparticles[k++] = particles[maxparticle--]; - while (maxparticle >= activeparticles && - particles[maxparticle].die <= vr_data.realtime) - maxparticle--; - } - numparticles = activeparticles; - qfeglDepthMask (GL_TRUE); qfeglDisableVertexAttribArray (quake_part.vertex.location); qfeglDisableVertexAttribArray (quake_part.color.location); @@ -2053,17 +715,15 @@ draw_qf_particles (void) } static void -draw_id_particles (void) +draw_id_particles (psystem_t *psystem) { - int activeparticles, maxparticle, j, vacount; - unsigned k; + int vacount; float minparticledist; - particle_t *part; partvert_t *VA; - mat4_t vp_mat; + mat4f_t vp_mat; quat_t fog; - Mat4Mult (glsl_projection, glsl_view, vp_mat); + mmulf (vp_mat, glsl_projection, glsl_view); // LordHavoc: particles should not affect zbuffer qfeglDepthMask (GL_FALSE); @@ -2071,10 +731,11 @@ draw_id_particles (void) qfeglEnableVertexAttribArray (quake_point.vertex.location); qfeglEnableVertexAttribArray (quake_point.color.location); - qfeglUniformMatrix4fv (quake_point.mvp_matrix.location, 1, false, vp_mat); + qfeglUniformMatrix4fv (quake_point.mvp_matrix.location, 1, false, + (vec_t*)&vp_mat[0]);//FIXME - VectorCopy (glsl_Fog_GetColor (), fog); - fog[3] = glsl_Fog_GetDensity () / 64.0; + Fog_GetColor (fog); + fog[3] = Fog_GetDensity () / 64.0; qfeglUniform4fv (quake_point.fog.location, 1, fog); qfeglUniform1i (quake_point.palette.location, 0); @@ -2082,36 +743,25 @@ draw_id_particles (void) qfeglEnable (GL_TEXTURE_2D); qfeglBindTexture (GL_TEXTURE_2D, glsl_palette); - minparticledist = DotProduct (r_refdef.vieworg, vpn) + - r_particles_nearclip->value; + minparticledist = DotProduct (r_refdef.frame.position, + r_refdef.frame.forward) + + r_particles_nearclip; - activeparticles = 0; vacount = 0; VA = particleVertexArray; - maxparticle = -1; - j = 0; - for (k = 0, part = particles; k < numparticles; k++, part++) { + for (unsigned i = 0; i < psystem->numparticles; i++) { + particle_t *p = &psystem->particles[i]; // Don't render particles too close to us. // Note, we must still do physics and such on them. - if (!(DotProduct (part->org, vpn) < minparticledist)) { - VA[0].color[0] = (byte) part->color; - VectorCopy (part->org, VA[0].vertex); + if (!(DotProduct (p->pos, r_refdef.frame.forward) < minparticledist)) { + VA[0].color[0] = (byte) p->icolor; + VectorCopy (p->pos, VA[0].vertex); VA++; vacount++; } - - R_RunParticlePhysics (part); - - // LordHavoc: immediate removal of unnecessary particles (must be done - // to ensure compactor below operates properly in all cases) - if (part->die < vr_data.realtime) { - freeparticles[j++] = part; - } else { - maxparticle = k; - activeparticles++; - } } + qfeglVertexAttribPointer (quake_point.vertex.location, 3, GL_FLOAT, 0, sizeof (partvert_t), &particleVertexArray[0].vertex); @@ -2120,15 +770,6 @@ draw_id_particles (void) &particleVertexArray[0].color); qfeglDrawArrays (GL_POINTS, 0, vacount); - k = 0; - while (maxparticle >= activeparticles) { - *freeparticles[k++] = particles[maxparticle--]; - while (maxparticle >= activeparticles && - particles[maxparticle].die <= vr_data.realtime) - maxparticle--; - } - numparticles = activeparticles; - qfeglDepthMask (GL_TRUE); qfeglDisableVertexAttribArray (quake_point.vertex.location); qfeglDisableVertexAttribArray (quake_point.color.location); @@ -2137,250 +778,23 @@ draw_id_particles (void) } void -glsl_R_DrawParticles (void) +glsl_R_DrawParticles (psystem_t *psystem) { +#if 0 draw_trails (); - if (!r_particles->int_val || !numparticles) +#endif + if (!psystem->numparticles) { return; - if (r_particles_style->int_val) { - draw_qf_particles (); + } + if (!psystem->points_only) { + draw_qf_particles (psystem); } else { - draw_id_particles (); + draw_id_particles (psystem); } } -static vid_particle_funcs_t particles_trail = { - R_RocketTrail_trail, - R_GrenadeTrail_QF, - R_BloodTrail_QF, - R_SlightBloodTrail_QF, - R_WizTrail_QF, - R_FlameTrail_QF, - R_VoorTrail_QF, - R_GlowTrail_QF, - R_RunParticleEffect_QF, - R_BloodPuffEffect_QF, - R_GunshotEffect_QF, - R_LightningBloodEffect_QF, - R_SpikeEffect_QF, - R_KnightSpikeEffect_QF, - R_SuperSpikeEffect_QF, - R_WizSpikeEffect_QF, - R_BlobExplosion_QF, - R_ParticleExplosion_QF, - R_ParticleExplosion2_QF, - R_LavaSplash_QF, - R_TeleportSplash_QF, - R_DarkFieldParticles_ID, - R_EntityParticles_ID, - R_Particle_New, - R_Particle_NewRandom, -}; - -static vid_particle_funcs_t particles_QF = { - R_RocketTrail_QF, - R_GrenadeTrail_QF, - R_BloodTrail_QF, - R_SlightBloodTrail_QF, - R_WizTrail_QF, - R_FlameTrail_QF, - R_VoorTrail_QF, - R_GlowTrail_QF, - R_RunParticleEffect_QF, - R_BloodPuffEffect_QF, - R_GunshotEffect_QF, - R_LightningBloodEffect_QF, - R_SpikeEffect_QF, - R_KnightSpikeEffect_QF, - R_SuperSpikeEffect_QF, - R_WizSpikeEffect_QF, - R_BlobExplosion_QF, - R_ParticleExplosion_QF, - R_ParticleExplosion2_QF, - R_LavaSplash_QF, - R_TeleportSplash_QF, - R_DarkFieldParticles_ID, - R_EntityParticles_ID, - R_Particle_New, - R_Particle_NewRandom, -}; - -static vid_particle_funcs_t particles_ID = { - R_RocketTrail_ID, - R_GrenadeTrail_ID, - R_BloodTrail_ID, - R_SlightBloodTrail_ID, - R_WizTrail_ID, - R_FlameTrail_ID, - R_VoorTrail_ID, - R_GlowTrail_QF, - R_RunParticleEffect_ID, - R_BloodPuffEffect_ID, - R_GunshotEffect_ID, - R_LightningBloodEffect_ID, - R_SpikeEffect_ID, - R_KnightSpikeEffect_ID, - R_SuperSpikeEffect_ID, - R_WizSpikeEffect_ID, - R_BlobExplosion_ID, - R_ParticleExplosion_ID, - R_ParticleExplosion2_QF, - R_LavaSplash_ID, - R_TeleportSplash_ID, - R_DarkFieldParticles_ID, - R_EntityParticles_ID, - R_Particle_New, - R_Particle_NewRandom, -}; - -static vid_particle_funcs_t particles_QF_egg = { - R_RocketTrail_EE, - R_GrenadeTrail_EE, - R_BloodTrail_QF, - R_SlightBloodTrail_QF, - R_WizTrail_QF, - R_FlameTrail_QF, - R_VoorTrail_QF, - R_GlowTrail_QF, - R_RunParticleEffect_QF, - R_BloodPuffEffect_QF, - R_GunshotEffect_QF, - R_LightningBloodEffect_QF, - R_SpikeEffect_QF, - R_KnightSpikeEffect_QF, - R_SuperSpikeEffect_QF, - R_WizSpikeEffect_QF, - R_BlobExplosion_QF, - R_ParticleExplosion_EE, - R_ParticleExplosion2_QF, - R_LavaSplash_QF, - R_TeleportSplash_EE, - R_DarkFieldParticles_ID, - R_EntityParticles_ID, - R_Particle_New, - R_Particle_NewRandom, -}; - -static vid_particle_funcs_t particles_ID_egg = { - R_RocketTrail_EE, - R_GrenadeTrail_EE, - R_BloodTrail_ID, - R_SlightBloodTrail_ID, - R_WizTrail_ID, - R_FlameTrail_ID, - R_VoorTrail_ID, - R_GlowTrail_QF, - R_RunParticleEffect_ID, - R_BloodPuffEffect_ID, - R_GunshotEffect_ID, - R_LightningBloodEffect_ID, - R_SpikeEffect_ID, - R_KnightSpikeEffect_ID, - R_SuperSpikeEffect_ID, - R_WizSpikeEffect_ID, - R_BlobExplosion_ID, - R_ParticleExplosion_EE, - R_ParticleExplosion2_QF, - R_LavaSplash_ID, - R_TeleportSplash_EE, - R_DarkFieldParticles_ID, - R_EntityParticles_ID, - R_Particle_New, - R_Particle_NewRandom, -}; - -void -glsl_r_easter_eggs_f (cvar_t *var) +psystem_t * __attribute__((const))//FIXME +glsl_ParticleSystem (void) { - if (easter_eggs) { - if (easter_eggs->int_val) { - if (r_particles_style->int_val) { - glsl_vid_render_funcs.particles = &particles_QF_egg; - } else { - glsl_vid_render_funcs.particles = &particles_ID_egg; - } - } else if (r_particles_style) { - if (r_particles_style->int_val > 1) { - glsl_vid_render_funcs.particles = &particles_QF; - } else if (r_particles_style->int_val) { - glsl_vid_render_funcs.particles = &particles_trail; - } else { - glsl_vid_render_funcs.particles = &particles_ID; - } - } - } -} - -void -glsl_r_particles_style_f (cvar_t *var) -{ - glsl_r_easter_eggs_f (easter_eggs); -} - -static void -R_ParticleFunctionInit (void) -{ - glsl_r_particles_style_f (r_particles_style); - glsl_r_easter_eggs_f (easter_eggs); -} - -static void -r_particles_nearclip_f (cvar_t *var) -{ - Cvar_SetValue (r_particles_nearclip, bound (r_nearclip->value, var->value, - r_farclip->value)); -} - -static void -r_particles_f (cvar_t *var) -{ - R_MaxParticlesCheck (var, r_particles_max); -} - -static void -r_particles_max_f (cvar_t *var) -{ - R_MaxParticlesCheck (r_particles, var); -} - -void -glsl_R_Particles_Init_Cvars (void) -{ - easter_eggs = Cvar_Get ("easter_eggs", "0", CVAR_NONE, r_easter_eggs_f, - "Enables easter eggs."); - r_particles = Cvar_Get ("r_particles", "1", CVAR_ARCHIVE, r_particles_f, - "Toggles drawing of particles."); - r_particles_max = Cvar_Get ("r_particles_max", "2048", CVAR_ARCHIVE, - r_particles_max_f, "Maximum amount of " - "particles to display. No maximum, minimum " - "is 0."); - r_particles_nearclip = Cvar_Get ("r_particles_nearclip", "32", - CVAR_ARCHIVE, r_particles_nearclip_f, - "Distance of the particle near clipping " - "plane from the player."); - r_particles_style = Cvar_Get ("r_particles_style", "1", CVAR_ARCHIVE, - r_particles_style_f, "Sets particle style. " - "0 for Id, 1 for QF."); - R_ParticleFunctionInit (); -} - -void -glsl_R_Particle_New (const char *type, int texnum, const vec3_t org, - float scale, const vec3_t vel, float die, int color, - float alpha, float ramp) -{ - if (numparticles >= r_maxparticles) - return; - particle_new (type, texnum, org, scale, vel, die, color, alpha, ramp); -} - -void -glsl_R_Particle_NewRandom (const char *type, int texnum, const vec3_t org, - int org_fuzz, float scale, int vel_fuzz, float die, - int color, float alpha, float ramp) -{ - if (numparticles >= r_maxparticles) - return; - particle_new_random (type, texnum, org, org_fuzz, scale, vel_fuzz, die, - color, alpha, ramp); + return &r_psystem; } diff --git a/libs/video/renderer/glsl/glsl_screen.c b/libs/video/renderer/glsl/glsl_screen.c deleted file mode 100644 index 56e481c41..000000000 --- a/libs/video/renderer/glsl/glsl_screen.c +++ /dev/null @@ -1,254 +0,0 @@ -/* - glsl_main.c - - GLSL rendering - - Copyright (C) 2011 Bill Currie - - Author: Bill Currie - Date: 2011/12/23 - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#ifdef HAVE_STRING_H -# include "string.h" -#endif -#ifdef HAVE_STRINGS_H -# include "strings.h" -#endif - -#include "QF/cvar.h" -#include "QF/draw.h" -#include "QF/dstring.h" -#include "QF/image.h" -#include "QF/png.h" -#include "QF/quakefs.h" -#include "QF/render.h" -#include "QF/screen.h" -#include "QF/skin.h" -#include "QF/sys.h" -#include "QF/va.h" - -#include "QF/GLSL/defines.h" -#include "QF/GLSL/funcs.h" -#include "QF/GLSL/qf_draw.h" -#include "QF/GLSL/qf_textures.h" -#include "QF/GLSL/qf_vid.h" - -#include "r_internal.h" - -/* Unknown renamed to GLErr_Unknown to solve conflict with winioctl.h */ -static unsigned int GLErr_InvalidEnum; -static unsigned int GLErr_InvalidValue; -static unsigned int GLErr_InvalidOperation; -static unsigned int GLErr_OutOfMemory; -static unsigned int GLErr_Unknown; - -extern void (*R_DrawSpriteModel) (struct entity_s *ent); - - -static unsigned int -R_TestErrors (unsigned int numerous) -{ - switch (qfeglGetError ()) { - case GL_NO_ERROR: - return numerous; - break; - case GL_INVALID_ENUM: - GLErr_InvalidEnum++; - R_TestErrors (numerous++); - break; - case GL_INVALID_VALUE: - GLErr_InvalidValue++; - R_TestErrors (numerous++); - break; - case GL_INVALID_OPERATION: - GLErr_InvalidOperation++; - R_TestErrors (numerous++); - break; - case GL_OUT_OF_MEMORY: - GLErr_OutOfMemory++; - R_TestErrors (numerous++); - break; - default: - GLErr_Unknown++; - R_TestErrors (numerous++); - break; - } - - return numerous; -} - -static void -R_DisplayErrors (void) -{ - if (GLErr_InvalidEnum) - printf ("%d OpenGL errors: Invalid Enum!\n", GLErr_InvalidEnum); - if (GLErr_InvalidValue) - printf ("%d OpenGL errors: Invalid Value!\n", GLErr_InvalidValue); - if (GLErr_InvalidOperation) - printf ("%d OpenGL errors: Invalid Operation!\n", GLErr_InvalidOperation); - if (GLErr_OutOfMemory) - printf ("%d OpenGL errors: Out Of Memory!\n", GLErr_OutOfMemory); - if (GLErr_Unknown) - printf ("%d Unknown OpenGL errors!\n", GLErr_Unknown); -} - -static void -R_ClearErrors (void) -{ - GLErr_InvalidEnum = 0; - GLErr_InvalidValue = 0; - GLErr_InvalidOperation = 0; - GLErr_OutOfMemory = 0; - GLErr_Unknown = 0; -} - -static void -SCR_TileClear (void) -{ - if (r_refdef.vrect.x > 0) { - // left - Draw_TileClear (0, 0, r_refdef.vrect.x, vid.height - vr_data.lineadj); - // right - Draw_TileClear (r_refdef.vrect.x + r_refdef.vrect.width, 0, - vid.width - r_refdef.vrect.x + r_refdef.vrect.width, - vid.height - vr_data.lineadj); - } - if (r_refdef.vrect.y > 0) { - // top - Draw_TileClear (r_refdef.vrect.x, 0, - r_refdef.vrect.x + r_refdef.vrect.width, - r_refdef.vrect.y); - // bottom - Draw_TileClear (r_refdef.vrect.x, - r_refdef.vrect.y + r_refdef.vrect.height, - r_refdef.vrect.width, - vid.height - vr_data.lineadj - - (r_refdef.vrect.height + r_refdef.vrect.y)); - } -} - -void -glsl_SCR_UpdateScreen (double realtime, SCR_Func scr_3dfunc, - SCR_Func *scr_funcs) -{ - static int begun = 0; - - if (R_TestErrors (0)) - R_DisplayErrors (); - R_ClearErrors (); - - if (begun) { - begun = 0; - vid.end_rendering (); - } - - vr_data.realtime = realtime; - vr_data.scr_copyeverything = 1; - //FIXME useless cvar? vid.numpages = 2 + gl_triplebuffer->int_val; - - if (!scr_initialized) - return; - - qfeglClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - begun = 1; - - if (oldfov != scr_fov->value) { - oldfov = scr_fov->value; - vid.recalc_refdef = true; - } - if (vid.recalc_refdef) - SCR_CalcRefdef (); - - scr_3dfunc (); - - SCR_SetUpToDrawConsole (); - GLSL_Set2D (); - GLSL_DrawReset (); - SCR_TileClear (); - GLSL_Set2DScaled (); - while (*scr_funcs) { - (*scr_funcs)(); - scr_funcs++; - GLSL_FlushText (); - } - GLSL_End2D (); - qfeglFlush (); -} - -tex_t * -glsl_SCR_CaptureBGR (void) -{ - byte *r, *b; - int count, i; - tex_t *tex; - - count = vid.width * vid.height; - tex = malloc (field_offset (tex_t, data[count * 3])); - SYS_CHECKMEM (tex); - tex->width = vid.width; - tex->height = vid.height; - tex->format = tex_rgb; - tex->palette = 0; - qfeglReadPixels (0, 0, vid.width, vid.height, GL_RGB, - GL_UNSIGNED_BYTE, tex->data); - for (i = 0, r = tex->data, b = tex->data + 2; i < count; - i++, r += 3, b += 3) { - byte t = *b; - *b = *r; - *r = t; - } - return tex; -} - -tex_t * -glsl_SCR_ScreenShot (int width, int height) -{ - return 0; -} - -void -glsl_SCR_ScreenShot_f (void) -{ - dstring_t *name = dstring_new (); - - // find a file name to save it to - if (!QFS_NextFilename (name, - va ("%s/qf", qfs_gamedir->dir.shots), ".png")) { - Sys_Printf ("SCR_ScreenShot_f: Couldn't create a PNG file\n"); - } else { - tex_t *tex; - - tex = glsl_SCR_CaptureBGR (); - WritePNGqfs (name->str, tex->data, tex->width, tex->height); - free (tex); - Sys_Printf ("Wrote %s/%s\n", qfs_userpath, name->str); - } - dstring_delete (name); -} diff --git a/libs/video/renderer/glsl/glsl_shader.c b/libs/video/renderer/glsl/glsl_shader.c index cddb2a617..3f52def57 100644 --- a/libs/video/renderer/glsl/glsl_shader.c +++ b/libs/video/renderer/glsl/glsl_shader.c @@ -48,7 +48,7 @@ typedef struct glsl_effect_s { } glsl_effect_t; static hashtab_t *effect_tab; -static glsl_effect_t *effects_freelist; +ALLOC_STATE (glsl_effect_t, effects); static glsl_effect_t * new_effect (void) @@ -65,6 +65,22 @@ effect_get_key (const void *e, void *unused) return effect->name; } +static void +effect_free (void *e, void *unused) +{ + glsl_effect_t *effect = e; + puts (effect->name); + free ((char *) effect->name); + Segtext_delete (effect->text); +} + +void +GLSL_ShaderShutdown (void) +{ + Hash_DelTable (effect_tab); + ALLOC_FREE_BLOCKS (effects); +} + int GLSL_RegisterEffect (const char *name, const char *src) { @@ -72,7 +88,7 @@ GLSL_RegisterEffect (const char *name, const char *src) segtext_t *text; if (!effect_tab) - effect_tab = Hash_NewTable (61, effect_get_key, 0, 0); + effect_tab = Hash_NewTable (61, effect_get_key, effect_free, 0, 0); if (Hash_Find (effect_tab, name)) { Sys_Printf ("WARNING: ignoring duplicate '%s' effect\n", name); @@ -129,8 +145,17 @@ GLSL_BuildShader (const char **effect_keys) Sys_Printf ("Unknown shader key: '%s'\n", dot); goto error; } - shader->strings[num] = nva ("#line %d\n%s", chunk->start_line - 1, - chunk->text); + if (strncmp ("#version ", chunk->text, 9) == 0) { + const char *eol = strchr (chunk->text, '\n'); + int vline_len = eol ? eol - chunk->text + 1 : 0; + shader->strings[num] = nva ("%.*s#line %d\n%s", vline_len, + chunk->text, + chunk->start_line + 1, + chunk->text + vline_len); + } else { + shader->strings[num] = nva ("#line %d\n%s", chunk->start_line - 1, + chunk->text); + } shader->src[num] = strdup (ekey->str); } dstring_delete (ekey); diff --git a/libs/video/renderer/glsl/glsl_sprite.c b/libs/video/renderer/glsl/glsl_sprite.c index 83f116ee3..6ea37b01a 100644 --- a/libs/video/renderer/glsl/glsl_sprite.c +++ b/libs/video/renderer/glsl/glsl_sprite.c @@ -31,9 +31,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -48,8 +45,11 @@ #include "QF/sys.h" #include "QF/vid.h" +#include "QF/scene/entity.h" + #include "QF/GLSL/defines.h" #include "QF/GLSL/funcs.h" +#include "QF/GLSL/qf_sprite.h" #include "QF/GLSL/qf_textures.h" #include "QF/GLSL/qf_vid.h" @@ -128,10 +128,12 @@ glsl_R_InitSprites (void) } static void -R_GetSpriteFrames (entity_t *ent, msprite_t *sprite, mspriteframe_t **frame1, +R_GetSpriteFrames (entity_t ent, msprite_t *sprite, mspriteframe_t **frame1, mspriteframe_t **frame2, float *blend) { - int framenum = currententity->frame; + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, + ent.reg); + int framenum = animation->frame; int pose; int i, numframes; float *intervals; @@ -143,18 +145,17 @@ R_GetSpriteFrames (entity_t *ent, msprite_t *sprite, mspriteframe_t **frame1, if (framenum >= sprite->numframes || framenum < 0) framenum = 0; - numframes = sprite->numframes; framedesc = &sprite->frames[framenum]; if (framedesc->type == SPR_SINGLE) { frame_interval = 0.1; pose = framenum; } else { - group = (mspritegroup_t *) framedesc->frameptr; + group = framedesc->group; intervals = group->intervals; numframes = group->numframes; fullinterval = intervals[numframes - 1]; - time = vr_data.realtime + currententity->syncbase; + time = vr_data.realtime + animation->syncbase; targettime = time - ((int) (time / fullinterval)) * fullinterval; for (i = 0; i < numframes - 1; i++) { @@ -169,52 +170,51 @@ R_GetSpriteFrames (entity_t *ent, msprite_t *sprite, mspriteframe_t **frame1, //FIXME this will break if the sprite changes between single frames and //group frames. - *blend = R_EntityBlend (ent, pose, frame_interval); + *blend = R_EntityBlend (animation, pose, frame_interval); if (group) { - *frame1 = group->frames[ent->pose1]; - *frame2 = group->frames[ent->pose2]; + *frame1 = group->frames[animation->pose1]; + *frame2 = group->frames[animation->pose2]; } else { - *frame1 = sprite->frames[ent->pose1].frameptr; - *frame2 = sprite->frames[ent->pose2].frameptr; + *frame1 = sprite->frames[animation->pose1].frame; + *frame2 = sprite->frames[animation->pose2].frame; } } static void -make_quad (mspriteframe_t *frame, const vec3_t vpn, const vec3_t vright, - const vec3_t vup, float verts[6][3]) +make_quad (mspriteframe_t *frame, vec4f_t origin, vec4f_t sright, vec4f_t sup, float verts[6][3]) { - vec3_t left, up, right, down; - vec3_t ul, ur, ll, lr; + vec4f_t left, up, right, down; + vec4f_t ul, ur, ll, lr; // build the sprite poster in worldspace // first, rotate the sprite axes into world space - VectorScale (vright, frame->right, right); - VectorScale (vup, frame->up, up); - VectorScale (vright, frame->left, left); - VectorScale (vup, frame->down, down); + right = frame->right * sright; + up = frame->up * sup; + left = frame->left * sright; + down = frame->down * sup; // next, build the sprite corners from the axes - VectorAdd (up, left, ul); - VectorAdd (up, right, ur); - VectorAdd (down, left, ll); - VectorAdd (down, right, lr); + ul = up + left; + ur = up + right; + ll = down + left; + lr = down + right; // finally, translate the sprite corners, creating two triangles - VectorAdd (currententity->origin, ul, verts[0]); // first triangle - VectorAdd (currententity->origin, ur, verts[1]); - VectorAdd (currententity->origin, lr, verts[2]); - VectorAdd (currententity->origin, ul, verts[3]); // second triangle - VectorAdd (currententity->origin, lr, verts[4]); - VectorAdd (currententity->origin, ll, verts[5]); + VectorAdd (origin, ul, verts[0]); // first triangle + VectorAdd (origin, ur, verts[1]); + VectorAdd (origin, lr, verts[2]); + VectorAdd (origin, ul, verts[3]); // second triangle + VectorAdd (origin, lr, verts[4]); + VectorAdd (origin, ll, verts[5]); } void -R_DrawSprite (void) +glsl_R_DrawSprite (entity_t ent) { - entity_t *ent = currententity; - msprite_t *sprite = (msprite_t *) ent->model->cache.data; + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); + msprite_t *sprite = (msprite_t *) renderer->model->cache.data; mspriteframe_t *frame1, *frame2; - float blend, sr, cr, dot, angle; - vec3_t tvec; - vec3_t svpn, svright, svup; + float blend; + vec4f_t cameravec = {}; + vec4f_t spn = {}, sright = {}, sup = {}; static quat_t color = { 1, 1, 1, 1}; float vertsa[6][3], vertsb[6][3]; static float uvab[6][4] = { @@ -226,75 +226,14 @@ R_DrawSprite (void) { 0, 1, 0, 1 }, }; - switch (sprite->type) { - case SPR_FACING_UPRIGHT: - // generate the sprite's exes with svup straight up in worldspace - // and svright perpendicular to r_origin. This will not work if the - // view direction is very close to straight up or down because the - // cross product will be between two nearly parallel vectors and - // starts to approach an undefined staate, so we don't draw if the - // two vectors are less than 1 degree apart - VectorNegate (r_origin, tvec); - VectorNormalize (tvec); - dot = tvec[2]; // same as DotProcut (tvec, svup) because - // svup is 0, 0, 1 - if ((dot > 0.999848) || (dot < -0.99848)) // cos (1 degree) - return; - VectorSet (0, 0, 1, svup); - // CrossProduct (svup, -r_origin, svright) - VectorSet (tvec[1], -tvec[0], 0, svright); - VectorNormalize (svright); - // CrossProduct (svright, svup, svpn); - VectorSet (-svright[1], svright[0], 0, svpn); - break; - case SPR_VP_PARALLEL: - // generate the prite's axes completely parallel to the viewplane. - // There are no problem situations, because the prite is always in - // the same position relative to the viewer. - VectorCopy (vup, svup); - VectorCopy (vright, svright); - VectorCopy (vpn, svpn); - break; - case SPR_VP_PARALLEL_UPRIGHT: - // generate the sprite's axes with svup straight up in worldspace, - // and svright parallel to the viewplane. This will not work if the - // view diretion iss very close to straight up or down because the - // cross prodcut will be between two nearly parallel vectors and - // starts to approach an undefined state, so we don't draw if the - // two vectros are less that 1 degree apart - dot = vpn[2]; - if ((dot > 0.999848) || (dot < -0.99848)) // cos (1 degree) - return; - VectorSet (0, 0, 1, svup); - // CrossProduct (svup, -r_origin, svright) - VectorSet (vpn[1], -vpn[0], 0, svright); - VectorNormalize (svright); - // CrossProduct (svright, svup, svpn); - VectorSet (-svright[1], svright[0], 0, svpn); - break; - case SPR_ORIENTED: - // generate the prite's axes according to the sprite's world - // orientation - VectorCopy (currententity->transform + 0, svpn); - VectorNegate (currententity->transform + 4, svright); - VectorCopy (currententity->transform + 8, svup); - break; - case SPR_VP_PARALLEL_ORIENTED: - // generate the sprite's axes parallel to the viewplane, but - // rotated in that plane round the center according to the sprite - // entity's roll angle. Thus svpn stays the same, but svright and - // svup rotate - angle = currententity->angles[ROLL] * (M_PI / 180); - sr = sin (angle); - cr = cos (angle); - VectorCopy (vpn, svpn); - VectorScale (vright, cr, svright); - VectorMultAdd (svright, sr, vup, svright); - VectorScale (vup, cr, svup); - VectorMultAdd (svup, -sr, vright, svup); - break; - default: - Sys_Error ("R_DrawSprite: Bad sprite type %d", sprite->type); + transform_t transform = Entity_Transform (ent); + vec4f_t origin = Transform_GetWorldPosition (transform); + cameravec = r_refdef.frame.position - origin; + + if (!R_BillboardFrame (transform, sprite->type, cameravec, + &sup, &sright, &spn)) { + // the orientation is undefined so can't draw the sprite + return; } R_GetSpriteFrames (ent, sprite, &frame1, &frame2, &blend); @@ -309,8 +248,8 @@ R_DrawSprite (void) qfeglVertexAttrib4fv (quake_sprite.colorb.location, color); qfeglVertexAttrib1f (quake_sprite.blend.location, blend); - make_quad (frame1, svpn, svright, svup, vertsa); - make_quad (frame2, svpn, svright, svup, vertsb); + make_quad (frame1, origin, sright, sup, vertsa); + make_quad (frame2, origin, sright, sup, vertsb); qfeglVertexAttribPointer (quake_sprite.vertexa.location, 3, GL_FLOAT, 0, 0, vertsa); @@ -323,9 +262,9 @@ R_DrawSprite (void) // All sprites are drawn in a batch, so avoid thrashing the gl state void -R_SpriteBegin (void) +glsl_R_SpriteBegin (void) { - mat4_t mat; + mat4f_t mat; quat_t fog; qfeglUseProgram (quake_sprite.program); @@ -336,8 +275,8 @@ R_SpriteBegin (void) qfeglDisableVertexAttribArray (quake_sprite.colorb.location); qfeglDisableVertexAttribArray (quake_sprite.blend.location); - VectorCopy (glsl_Fog_GetColor (), fog); - fog[3] = glsl_Fog_GetDensity () / 64.0; + Fog_GetColor (fog); + fog[3] = Fog_GetDensity () / 64.0; qfeglUniform4fv (quake_sprite.fog.location, 1, fog); qfeglUniform1i (quake_sprite.spritea.location, 0); @@ -353,12 +292,12 @@ R_SpriteBegin (void) qfeglEnable (GL_TEXTURE_2D); qfeglBindTexture (GL_TEXTURE_2D, glsl_palette); - Mat4Mult (glsl_projection, glsl_view, mat); - qfeglUniformMatrix4fv (quake_sprite.matrix.location, 1, false, mat); + mmulf (mat, glsl_projection, glsl_view); + qfeglUniformMatrix4fv (quake_sprite.matrix.location, 1, false, (vec_t*)&mat[0]);//FIXME } void -R_SpriteEnd (void) +glsl_R_SpriteEnd (void) { qfeglDisableVertexAttribArray (quake_sprite.vertexa.location); qfeglDisableVertexAttribArray (quake_sprite.vertexb.location); diff --git a/libs/video/renderer/glsl/glsl_textures.c b/libs/video/renderer/glsl/glsl_textures.c index 62b331662..a33cb86fa 100644 --- a/libs/video/renderer/glsl/glsl_textures.c +++ b/libs/video/renderer/glsl/glsl_textures.c @@ -29,9 +29,6 @@ # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -42,24 +39,27 @@ #include #include "QF/cmd.h" +#include "QF/image.h" #include "QF/mathlib.h" #include "QF/model.h" +#include "QF/render.h" #include "QF/sys.h" -#include "QF/vrect.h" #include "QF/GLSL/defines.h" #include "QF/GLSL/funcs.h" #include "QF/GLSL/qf_textures.h" +#include "QF/ui/vrect.h" + +#include "r_scrap.h" + struct scrap_s { + rscrap_t rscrap; GLuint tnum; - int size; // in pixels, for now, always square, power of 2 int format; int bpp; byte *data; // local copy of the texture so updates can be batched vrect_t *batch; - vrect_t *free_rects; - vrect_t *rects; subpic_t *subpics; struct scrap_s *next; }; @@ -70,7 +70,7 @@ static int max_tex_size; int GLSL_LoadQuakeTexture (const char *identifier, int width, int height, - byte *data) + const byte *data) { GLuint tnum; @@ -202,7 +202,8 @@ GLSL_LoadQuakeMipTex (const texture_t *tex) } int -GLSL_LoadRGBTexture (const char *identifier, int width, int height, byte *data) +GLSL_LoadRGBTexture (const char *identifier, int width, int height, + const byte *data) { GLuint tnum; @@ -220,7 +221,8 @@ GLSL_LoadRGBTexture (const char *identifier, int width, int height, byte *data) } int -GLSL_LoadRGBATexture (const char *identifier, int width, int height, byte *data) +GLSL_LoadRGBATexture (const char *identifier, int width, int height, + const byte *data) { GLuint tnum; @@ -237,6 +239,47 @@ GLSL_LoadRGBATexture (const char *identifier, int width, int height, byte *data) return tnum; } +int +GLSL_LoadTex (const char *identifier, int linear, tex_t *tex) +{ + GLuint tnum; + qfeglGenTextures (1, &tnum); + int format = GL_RGB; + + switch (tex->format) { + case tex_l: + case tex_a: + format = GL_LUMINANCE; + break; + case tex_la: + format = GL_LUMINANCE_ALPHA; + break; + case tex_rgb: + format = GL_RGB; + break; + case tex_rgba: + format = GL_RGBA; + break; + default: + Sys_Error ("GL_CreateScrap: Invalid texture format"); + } + + qfeglBindTexture (GL_TEXTURE_2D, tnum); + qfeglTexImage2D (GL_TEXTURE_2D, 0, format, tex->width, tex->height, + 0, format, GL_UNSIGNED_BYTE, tex->data); + qfeglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + qfeglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (linear) { + qfeglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + qfeglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } else { + qfeglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + qfeglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + qfeglGenerateMipmap (GL_TEXTURE_2D); + return tnum; +} + void GLSL_ReleaseTexture (int tex) { @@ -248,23 +291,23 @@ static void glsl_scraps_f (void) { scrap_t *scrap; - vrect_t *rect; int area; + int size; + int count; if (!scrap_list) { Sys_Printf ("No scraps\n"); return; } for (scrap = scrap_list; scrap; scrap = scrap->next) { - for (rect = scrap->free_rects, area = 0; rect; rect = rect->next) - area += rect->width * rect->height; - Sys_Printf ("tnum=%u size=%d format=%04x bpp=%d free=%d%%\n", - scrap->tnum, scrap->size, scrap->format, scrap->bpp, - area * 100 / (scrap->size * scrap->size)); + area = R_ScrapArea (&scrap->rscrap, &count); + // always square + size = scrap->rscrap.width; + Sys_Printf ("tnum=%u size=%d format=%04x bpp=%d free=%d%% rects=%d\n", + scrap->tnum, size, scrap->format, scrap->bpp, + area * 100 / (size * size), count); if (Cmd_Argc () > 1) { - for (rect = scrap->rects, area = 0; rect; rect = rect->next) - Sys_Printf ("%d %d %d %d\n", rect->x, rect->y, - rect->width, rect->height); + R_ScrapDump (&scrap->rscrap); } } } @@ -273,7 +316,7 @@ void GLSL_TextureInit (void) { qfeglGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_tex_size); - Sys_MaskPrintf (SYS_GLSL, "max texture size: %d\n", max_tex_size); + Sys_MaskPrintf (SYS_glsl, "max texture size: %d\n", max_tex_size); Cmd_AddCommand ("glsl_scraps", glsl_scraps_f, "Dump GLSL scrap stats"); } @@ -309,11 +352,9 @@ GLSL_CreateScrap (int size, int format, int linear) } scrap = malloc (sizeof (scrap_t)); qfeglGenTextures (1, &scrap->tnum); - scrap->size = size; + R_ScrapInit (&scrap->rscrap, size, size); scrap->format = format; scrap->bpp = bpp; - scrap->free_rects = VRect_New (0, 0, size, size); - scrap->rects = 0; scrap->subpics = 0; scrap->next = scrap_list; scrap_list = scrap; @@ -341,31 +382,21 @@ GLSL_CreateScrap (int size, int format, int linear) void GLSL_ScrapClear (scrap_t *scrap) { - vrect_t *t; subpic_t *sp; - - while (scrap->free_rects) { - t = scrap->free_rects; - scrap->free_rects = t->next; - VRect_Delete (t); - } - while (scrap->rects) { - t = scrap->rects; - scrap->rects = t->next; - VRect_Delete (t); - } while (scrap->subpics) { sp = scrap->subpics; scrap->subpics = (subpic_t *) sp->next; free (sp); } - - scrap->free_rects = VRect_New (0, 0, scrap->size, scrap->size); + R_ScrapClear (&scrap->rscrap); } void GLSL_DestroyScrap (scrap_t *scrap) { + if (!scrap) { + return; + } scrap_t **s; for (s = &scrap_list; *s; s = &(*s)->next) { @@ -375,7 +406,7 @@ GLSL_DestroyScrap (scrap_t *scrap) } } GLSL_ScrapClear (scrap); - VRect_Delete (scrap->free_rects); + R_ScrapDelete (&scrap->rscrap); GLSL_ReleaseTexture (scrap->tnum); free (scrap->data); free (scrap); @@ -390,57 +421,22 @@ GLSL_ScrapTexture (scrap_t *scrap) subpic_t * GLSL_ScrapSubpic (scrap_t *scrap, int width, int height) { - int i, w, h; - vrect_t **t, **best; - vrect_t *old, *frags, *rect; + vrect_t *rect; subpic_t *subpic; - for (i = 0; i < 16; i++) - if (width <= (1 << i)) - break; - w = 1 << i; - for (i = 0; i < 16; i++) - if (height <= (1 << i)) - break; - h = 1 << i; - - best = 0; - for (t = &scrap->free_rects; *t; t = &(*t)->next) { - if ((*t)->width < w || (*t)->height < h) - continue; // won't fit - if (!best) { - best = t; - continue; - } - if ((*t)->width <= (*best)->width || (*t)->height <= (*best)->height) - best = t; + rect = R_ScrapAlloc (&scrap->rscrap, width, height); + if (!rect) { + return 0; } - if (!best) - return 0; // couldn't find a spot - old = *best; - *best = old->next; - rect = VRect_New (old->x, old->y, w, h); - frags = VRect_Difference (old, rect); - VRect_Delete (old); - if (frags) { - // old was bigger than the requested size - for (old = frags; old->next; old = old->next) - ; - old->next = scrap->free_rects; - scrap->free_rects = frags; - } - rect->next = scrap->rects; - scrap->rects = rect; subpic = malloc (sizeof (subpic_t)); *((subpic_t **) &subpic->next) = scrap->subpics; scrap->subpics = subpic; *((scrap_t **) &subpic->scrap) = scrap; *((vrect_t **) &subpic->rect) = rect; - *((int *) &subpic->tnum) = scrap->tnum; *((int *) &subpic->width) = width; *((int *) &subpic->height) = height; - *((float *) &subpic->size) = 1.0 / scrap->size; + *((float *) &subpic->size) = 1.0 / scrap->rscrap.width; return subpic; } @@ -449,8 +445,6 @@ GLSL_SubpicDelete (subpic_t *subpic) { scrap_t *scrap = (scrap_t *) subpic->scrap; vrect_t *rect = (vrect_t *) subpic->rect; - vrect_t *old, *merge; - vrect_t **t; subpic_t **sp; for (sp = &scrap->subpics; *sp; sp = (subpic_t **) &(*sp)->next) @@ -460,29 +454,7 @@ GLSL_SubpicDelete (subpic_t *subpic) Sys_Error ("GLSL_ScrapDelSubpic: broken subpic"); *sp = (subpic_t *) subpic->next; free (subpic); - for (t = &scrap->rects; *t; t = &(*t)->next) - if (*t == rect) - break; - if (*t != rect) - Sys_Error ("GLSL_ScrapDelSubpic: broken subpic"); - *t = rect->next; - - do { - merge = 0; - for (t = &scrap->free_rects; *t; t = &(*t)->next) { - merge = VRect_Merge (*t, rect); - if (merge) { - old = *t; - *t = (*t)->next; - VRect_Delete (old); - VRect_Delete (rect); - rect = merge; - break; - } - } - } while (merge); - rect->next = scrap->free_rects; - scrap->free_rects = rect; + R_ScrapFree (&scrap->rscrap, rect); } void @@ -503,7 +475,7 @@ GLSL_SubpicUpdate (subpic_t *subpic, byte *data, int batch) scrap->batch = VRect_New (rect->x, rect->y, rect->width, rect->height); } - step = scrap->size * scrap->bpp; + step = scrap->rscrap.width * scrap->bpp; sbytes = subpic->width * scrap->bpp; dest = scrap->data + rect->y * step + rect->x * scrap->bpp; for (i = 0; i < subpic->height; i++, dest += step, data += sbytes) @@ -519,7 +491,8 @@ GLSL_SubpicUpdate (subpic_t *subpic, byte *data, int batch) void GLSL_ScrapFlush (scrap_t *scrap) { - vrect_t *rect = scrap->batch;; + vrect_t *rect = scrap->batch; + int size = scrap->rscrap.width; if (!rect) return; @@ -527,9 +500,9 @@ GLSL_ScrapFlush (scrap_t *scrap) //should update to not update the entire horizontal block qfeglBindTexture (GL_TEXTURE_2D, scrap->tnum); qfeglTexSubImage2D (GL_TEXTURE_2D, 0, 0, rect->y, - scrap->size, rect->height, scrap->format, + size, rect->height, scrap->format, GL_UNSIGNED_BYTE, - scrap->data + rect->y * scrap->size * scrap->bpp); + scrap->data + rect->y * size * scrap->bpp); VRect_Delete (rect); scrap->batch = 0; } diff --git a/libs/video/renderer/glsl/glsl_warp.c b/libs/video/renderer/glsl/glsl_warp.c new file mode 100644 index 000000000..b4579b606 --- /dev/null +++ b/libs/video/renderer/glsl/glsl_warp.c @@ -0,0 +1,116 @@ +/* + glsl_warp.c + + GLSL screen warp + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/3/24 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include + +#include "QF/cvar.h" +#include "QF/render.h" +#include "QF/sys.h" + +#include "QF/GLSL/defines.h" +#include "QF/GLSL/funcs.h" +#include "QF/GLSL/qf_vid.h" +#include "QF/GLSL/qf_warp.h" + +#include "r_internal.h" +#include "vid_gl.h" + +static const char *warp_vert_effects[] = +{ + "QuakeForge.version.130", + "QuakeForge.Vertex.fstri", + 0 +}; + +static const char *warp_frag_effects[] = +{ + "QuakeForge.version.130", + "QuakeForge.Math.const", + "QuakeForge.Fragment.screen.warp", + 0 +}; + +static struct { + int program; + GLuint vao; + shaderparam_t screenTex; + shaderparam_t time; +} warp = { + .screenTex = {"screenTex", 1}, + .time = {"time", 1}, +}; + +void +glsl_InitWarp (void) +{ + shader_t *vert_shader, *frag_shader; + int vert; + int frag; + + vert_shader = GLSL_BuildShader (warp_vert_effects); + frag_shader = GLSL_BuildShader (warp_frag_effects); + vert = GLSL_CompileShader ("scrwarp.vert", vert_shader, GL_VERTEX_SHADER); + frag = GLSL_CompileShader ("scrwarp.frag", frag_shader, GL_FRAGMENT_SHADER); + warp.program = GLSL_LinkProgram ("scrwarp", vert, frag); + GLSL_ResolveShaderParam (warp.program, &warp.screenTex); + GLSL_ResolveShaderParam (warp.program, &warp.time); + GLSL_FreeShader (vert_shader); + GLSL_FreeShader (frag_shader); + + qfeglCreateVertexArrays (1, &warp.vao); +} + +void +glsl_WarpScreen (framebuffer_t *fb) +{ + qfeglUseProgram (warp.program); + + qfeglUniform1f (warp.time.location, vr_data.realtime); + + gl_framebuffer_t *buffer = fb->buffer; + qfeglActiveTexture (GL_TEXTURE0 + 0); + qfeglBindTexture (GL_TEXTURE_2D, buffer->color); + + qfeglBindVertexArray (warp.vao); + qfeglDrawArrays (GL_TRIANGLES, 0, 3); + qfeglBindVertexArray (0); + + qfeglActiveTexture (GL_TEXTURE0 + 0); + qfeglDisable (GL_TEXTURE_2D); +} diff --git a/libs/video/renderer/glsl/namehack.h b/libs/video/renderer/glsl/namehack.h deleted file mode 100644 index 7edaed944..000000000 --- a/libs/video/renderer/glsl/namehack.h +++ /dev/null @@ -1,137 +0,0 @@ -#ifdef NH_DEFINE -#undef NH_DEFINE -#define Draw_Init glsl_Draw_Init -#define Draw_Character glsl_Draw_Character -#define Draw_String glsl_Draw_String -#define Draw_nString glsl_Draw_nString -#define Draw_AltString glsl_Draw_AltString -#define Draw_ConsoleBackground glsl_Draw_ConsoleBackground -#define Draw_Crosshair glsl_Draw_Crosshair -#define Draw_CrosshairAt glsl_Draw_CrosshairAt -#define Draw_TileClear glsl_Draw_TileClear -#define Draw_Fill glsl_Draw_Fill -#define Draw_TextBox glsl_Draw_TextBox -#define Draw_FadeScreen glsl_Draw_FadeScreen -#define Draw_BlendScreen glsl_Draw_BlendScreen -#define Draw_CachePic glsl_Draw_CachePic -#define Draw_UncachePic glsl_Draw_UncachePic -#define Draw_MakePic glsl_Draw_MakePic -#define Draw_DestroyPic glsl_Draw_DestroyPic -#define Draw_PicFromWad glsl_Draw_PicFromWad -#define Draw_Pic glsl_Draw_Pic -#define Draw_Picf glsl_Draw_Picf -#define Draw_SubPic glsl_Draw_SubPic -#define Fog_DisableGFog glsl_Fog_DisableGFog -#define Fog_EnableGFog glsl_Fog_EnableGFog -#define Fog_GetColor glsl_Fog_GetColor -#define Fog_GetDensity glsl_Fog_GetDensity -#define Fog_Init glsl_Fog_Init -#define Fog_ParseWorldspawn glsl_Fog_ParseWorldspawn -#define Fog_SetupFrame glsl_Fog_SetupFrame -#define Fog_StartAdditive glsl_Fog_StartAdditive -#define Fog_StopAdditive glsl_Fog_StopAdditive -#define Fog_Update glsl_Fog_Update -#define R_AddTexture glsl_R_AddTexture -#define R_BlendLightmaps glsl_R_BlendLightmaps -#define R_BuildLightMap glsl_R_BuildLightMap -#define R_CalcLightmaps glsl_R_CalcLightmaps -#define R_ClearParticles glsl_R_ClearParticles -#define R_ClearState glsl_R_ClearState -#define R_ClearTextures glsl_R_ClearTextures -#define R_DrawAliasModel glsl_R_DrawAliasModel -#define R_DrawBrushModel glsl_R_DrawBrushModel -#define R_DrawParticles glsl_R_DrawParticles -#define R_DrawSky glsl_R_DrawSky -#define R_DrawSkyChain glsl_R_DrawSkyChain -#define R_DrawSprite glsl_R_DrawSprite -#define R_DrawSpriteModel glsl_R_DrawSpriteModel -#define R_DrawWaterSurfaces glsl_R_DrawWaterSurfaces -#define R_DrawWorld glsl_R_DrawWorld -#define R_InitBubble glsl_R_InitBubble -#define R_InitGraphTextures glsl_R_InitGraphTextures -#define R_InitParticles glsl_R_InitParticles -#define R_InitSky glsl_R_InitSky -#define R_InitSprites glsl_R_InitSprites -#define R_InitSurfaceChains glsl_R_InitSurfaceChains -#define R_LineGraph glsl_R_LineGraph -#define R_LoadSky_f glsl_R_LoadSky_f -#define R_LoadSkys glsl_R_LoadSkys -#define R_NewMap glsl_R_NewMap -#define R_Particle_New glsl_R_Particle_New -#define R_Particle_NewRandom glsl_R_Particle_NewRandom -#define R_Particles_Init_Cvars glsl_R_Particles_Init_Cvars -#define R_ReadPointFile_f glsl_R_ReadPointFile_f -#define R_RenderDlights glsl_R_RenderDlights -#define R_RenderView glsl_R_RenderView -#define R_RotateForEntity glsl_R_RotateForEntity -#define R_SetupFrame glsl_R_SetupFrame -#define R_SpriteBegin glsl_R_SpriteBegin -#define R_SpriteEnd glsl_R_SpriteEnd -#define R_TimeRefresh_f glsl_R_TimeRefresh_f -#define R_ViewChanged glsl_R_ViewChanged -#define SCR_CaptureBGR glsl_SCR_CaptureBGR -#define SCR_ScreenShot glsl_SCR_ScreenShot -#define SCR_ScreenShot_f glsl_SCR_ScreenShot_f -#define SCR_UpdateScreen glsl_SCR_UpdateScreen -#define c_alias_polys glsl_c_alias_polys -#define c_brush_polys glsl_c_brush_polys -#define r_easter_eggs_f glsl_r_easter_eggs_f -#define r_particles_style_f glsl_r_particles_style_f -#define r_world_matrix glsl_r_world_matrix -#else -#undef Fog_DisableGFog -#undef Fog_EnableGFog -#undef Fog_GetColor -#undef Fog_GetDensity -#undef Fog_Init -#undef Fog_ParseWorldspawn -#undef Fog_SetupFrame -#undef Fog_StartAdditive -#undef Fog_StopAdditive -#undef Fog_Update -#undef R_AddTexture -#undef R_BlendLightmaps -#undef R_BuildLightMap -#undef R_CalcLightmaps -#undef R_ClearParticles -#undef R_ClearState -#undef R_ClearTextures -#undef R_DrawAliasModel -#undef R_DrawBrushModel -#undef R_DrawParticles -#undef R_DrawSky -#undef R_DrawSkyChain -#undef R_DrawSpriteModel -#undef R_DrawWaterSurfaces -#undef R_DrawWorld -#undef R_Init -#undef R_InitBubble -#undef R_InitGraphTextures -#undef R_InitParticles -#undef R_InitSky -#undef R_InitSprites -#undef R_InitSurfaceChains -#undef R_LineGraph -#undef R_LoadSky_f -#undef R_LoadSkys -#undef R_NewMap -#undef R_Particle_New -#undef R_Particle_NewRandom -#undef R_Particles_Init_Cvars -#undef R_ReadPointFile_f -#undef R_RenderDlights -#undef R_RenderView -#undef R_RotateForEntity -#undef R_SetupFrame -#undef R_TimeRefresh_f -#undef R_ViewChanged -#undef SCR_CaptureBGR -#undef SCR_ScreenShot -#undef SCR_ScreenShot_f -#undef SCR_UpdateScreen -#undef c_alias_polys -#undef c_brush_polys -#undef r_easter_eggs_f -#undef r_particles_style_f -#undef r_world_matrix -#endif diff --git a/libs/video/renderer/glsl/qfglsl.c b/libs/video/renderer/glsl/qfglsl.c index af3a2f839..3860b1231 100644 --- a/libs/video/renderer/glsl/qfglsl.c +++ b/libs/video/renderer/glsl/qfglsl.c @@ -60,6 +60,7 @@ #include "QF/GLSL/funcs.h" #include "r_internal.h" +#include "vid_gl.h" // First we need to get all the function pointers declared. #define QFGL_WANT(ret, name, args) \ @@ -70,13 +71,13 @@ #undef QFGL_NEED #undef QFGL_WANT -qboolean +bool EGLF_FindFunctions (void) { #define QFGL_WANT(ret, name, args) \ - qfe##name = vid.get_proc_address (#name, false); + qfe##name = glsl_ctx->get_proc_address (#name, false); #define QFGL_NEED(ret, name, args) \ - qfe##name = vid.get_proc_address (#name, true); + qfe##name = glsl_ctx->get_proc_address (#name, true); #include "QF/GLSL/qf_funcs_list.h" #undef QFGL_NEED #undef QFGL_WANT diff --git a/libs/video/renderer/glsl/quakeforge.glsl b/libs/video/renderer/glsl/quakeforge.glsl index e9bfa4d69..0922eff30 100644 --- a/libs/video/renderer/glsl/quakeforge.glsl +++ b/libs/video/renderer/glsl/quakeforge.glsl @@ -553,6 +553,23 @@ main (void) gl_FragColor = palettedColor (pix) * color; } +-- Fragment.2d.alpha + +uniform sampler2D texture; +varying vec4 color; +varying vec2 st; + +void +main (void) +{ + float alpha; + + alpha = texture2D (texture, st).r; + if (alpha == 0.0) + discard; + gl_FragColor = alpha * color; +} + -- Vertex.iqm uniform mat4 mvp_mat; @@ -585,8 +602,8 @@ main (void) m += bonemats[int (vbones.z)] * vweights.z; m += bonemats[int (vbones.w)] * vweights.w; #if 0 - q0 = m[0].yzwx; //swizzle for conversion betwen QF and GL - qe = m[1].yzwx; //swizzle for conversion betwen QF and GL + q0 = m[0]; + qe = m[1]; sh = m[2].xyz; sc = m[3].xyz; @@ -737,3 +754,60 @@ main (void) { gl_FragColor = vec4 (vec3 (edgeFactor ()), 0.5); } +-- version.130 +#version 130 +-- Vertex.fstri + +out vec2 uv; + +void +main () +{ + // quake uses clockwise triangle order + float x = (gl_VertexID & 2); + float y = (gl_VertexID & 1); + uv = vec2(x, y*2); + gl_Position = vec4 (2, 4, 0, 0) * vec4 (x, y, 0, 0) - vec4 (1, 1, 0, -1); +} + +-- Fragment.screen.warp + +uniform sampler2D screenTex; +uniform float time; + +in vec2 uv; + +const float S = 0.15625; +const float F = 2.5; +const float A = 0.01; +const vec2 B = vec2 (1, 1); + +void +main () +{ + vec2 st; + st = uv * (1.0 - 2.0*A) + A * (B + sin ((time * S + F * uv.yx) * 2.0*PI)); + vec4 c = texture2D (screenTex, st); + gl_FragColor = c;//vec4(uv, c.x, 1); +} + +-- Fragment.screen.fisheye + +uniform samplerCube screenTex; +uniform float fov; +uniform float aspect; + +in vec2 uv; + +void +main () +{ + // slight offset on y is to avoid the singularity straight ahead + vec2 xy = (2.0 * uv - vec2 (1, 1.00002)) * (vec2(1, -aspect)); + float r = sqrt (dot (xy, xy)); + vec2 cs = vec2 (cos (r * fov), sin (r * fov)); + vec3 dir = vec3 (cs.y * xy / r, cs.x); + + vec4 c = textureCube(screenTex, dir); + gl_FragColor = c;// * 0.001 + vec4(dir, 1); +} diff --git a/libs/video/renderer/glsl/vid_common_glsl.c b/libs/video/renderer/glsl/vid_common_glsl.c index 05c430f93..e03060f68 100644 --- a/libs/video/renderer/glsl/vid_common_glsl.c +++ b/libs/video/renderer/glsl/vid_common_glsl.c @@ -41,7 +41,6 @@ #include "QF/cvar.h" #include "QF/dstring.h" -#include "QF/input.h" #include "QF/qargs.h" #include "QF/quakefs.h" #include "QF/sys.h" @@ -57,12 +56,12 @@ #include "r_internal.h" static const char quakeforge_effect[] = -#include "quakeforge.slc" +#include "libs/video/renderer/glsl/quakeforge.slc" "--\n" // ensure the last block of the previous file doesn't merge with // the first block of the next file // Include Stefan Gustavson's noise functions in the QuakeForge shader // effect "file". -#include "sgustavson.slc" +#include "libs/video/renderer/glsl/sgustavson.slc" ; int glsl_palette; @@ -74,7 +73,7 @@ GLSL_Common_Init_Cvars (void) } void -GLSL_SetPalette (const byte *palette) +GLSL_SetPalette (void *data, const byte *palette) { const byte *col, *ip; byte *pal, *op; @@ -83,7 +82,7 @@ GLSL_SetPalette (const byte *palette) unsigned *table; // 8 8 8 encoding - Sys_MaskPrintf (SYS_VID, "Converting 8to24\n"); + Sys_MaskPrintf (SYS_vid, "Converting 8to24\n"); table = d_8to24table; for (i = 0; i < 255; i++) { // used to be i<256, see d_8to24table below @@ -99,7 +98,7 @@ GLSL_SetPalette (const byte *palette) } d_8to24table[255] = 0; // 255 is transparent - Sys_MaskPrintf (SYS_VID, "Converting palette/colormap to RGBA textures\n"); + Sys_MaskPrintf (SYS_vid, "Converting palette/colormap to RGBA textures\n"); pal = malloc (256 * VID_GRADES * 4); for (i = 0, col = vr_data.vid->colormap8, op = pal; i < 256 * VID_GRADES; i++) { @@ -148,6 +147,12 @@ GLSL_SetPalette (const byte *palette) free (pal); } +void +GLSL_Shutdown_Common (void) +{ + GLSL_ShaderShutdown (); +} + void GLSL_Init_Common (void) { @@ -157,7 +162,7 @@ GLSL_Init_Common (void) GLSL_TextureInit (); - if (developer->int_val & SYS_GLSL) { + if (developer & SYS_glsl) { GLint max; qfeglGetIntegerv (GL_MAX_VERTEX_UNIFORM_VECTORS, &max); @@ -204,7 +209,7 @@ GLSL_CompileShader (const char *name, const shader_t *shader, int type) qfeglShaderSource (sid, shader->num_strings, shader->strings, 0); qfeglCompileShader (sid); qfeglGetShaderiv (sid, GL_COMPILE_STATUS, &compiled); - if (!compiled || (developer->int_val & SYS_GLSL)) { + if (!compiled || (developer & SYS_glsl)) { dstring_t *log = dstring_new (); int size; qfeglGetShaderiv (sid, GL_INFO_LOG_LENGTH, &size); @@ -273,7 +278,7 @@ type_name (GLenum type) case GL_FIXED: return "fixed"; } - return va("%x", type); + return va (0, "%x", type); } static void @@ -327,7 +332,7 @@ GLSL_LinkProgram (const char *name, int vert, int frag) qfeglLinkProgram (program); qfeglGetProgramiv (program, GL_LINK_STATUS, &linked); - if (!linked || (developer->int_val & SYS_GLSL)) { + if (!linked || (developer & SYS_glsl)) { dstring_t *log = dstring_new (); int size; qfeglGetProgramiv (program, GL_INFO_LOG_LENGTH, &size); @@ -342,7 +347,7 @@ GLSL_LinkProgram (const char *name, int vert, int frag) if (!linked) return 0; } - if (developer->int_val & SYS_GLSL) + if (developer & SYS_glsl) dump_program (name, program); return program; } @@ -359,7 +364,7 @@ GLSL_ResolveShaderParam (int program, shaderparam_t *param) Sys_Printf ("could not resolve %s %s\n", param->uniform ? "uniform" : "attribute", param->name); } else { - Sys_MaskPrintf (SYS_GLSL, "Resolved %s %s @ %d\n", + Sys_MaskPrintf (SYS_glsl, "Resolved %s %s @ %d\n", param->uniform ? "uniform" : "attribute", param->name, param->location); } diff --git a/libs/video/renderer/r_alias.c b/libs/video/renderer/r_alias.c index aa1541ca2..bd3b7b931 100644 --- a/libs/video/renderer/r_alias.c +++ b/libs/video/renderer/r_alias.c @@ -29,6 +29,7 @@ #endif #include "QF/sys.h" +#include "QF/scene/entity.h" #include "r_internal.h" @@ -37,13 +38,13 @@ float r_avertexnormals[NUMVERTEXNORMALS][3] = { }; maliasskindesc_t * -R_AliasGetSkindesc (int skinnum, aliashdr_t *ahdr) +R_AliasGetSkindesc (animation_t *animation, int skinnum, aliashdr_t *ahdr) { maliasskindesc_t *pskindesc; maliasskingroup_t *paliasskingroup; if ((skinnum >= ahdr->mdl.numskins) || (skinnum < 0)) { - Sys_MaskPrintf (SYS_DEV, "R_AliasSetupSkin: no such skin # %d\n", + Sys_MaskPrintf (SYS_dev, "R_AliasSetupSkin: no such skin # %d\n", skinnum); skinnum = 0; } @@ -63,7 +64,7 @@ R_AliasGetSkindesc (int skinnum, aliashdr_t *ahdr) numskins = paliasskingroup->numskins; fullskininterval = pskinintervals[numskins - 1]; - skintime = vr_data.realtime + currententity->syncbase; + skintime = vr_data.realtime + animation->syncbase; // when loading in Mod_LoadAliasSkinGroup, we guaranteed all interval // values are positive, so we don't have to worry about division by 0 @@ -81,8 +82,10 @@ R_AliasGetSkindesc (int skinnum, aliashdr_t *ahdr) } static maliasframedesc_t * -alias_get_frame (int framenum, aliashdr_t *hdr, float *frame_interval) +alias_get_frame (const animation_t *animation, aliashdr_t *hdr, + float *frame_interval) { + int framenum = animation->frame; float *intervals; float fullinterval, time, targettime; maliasframedesc_t *frame; @@ -91,7 +94,7 @@ alias_get_frame (int framenum, aliashdr_t *hdr, float *frame_interval) int i; if ((framenum >= hdr->mdl.numframes) || (framenum < 0)) { - Sys_MaskPrintf (SYS_DEV, "R_AliasSetupFrame: no such frame %d\n", + Sys_MaskPrintf (SYS_dev, "R_AliasSetupFrame: no such frame %d\n", framenum); framenum = 0; } @@ -118,7 +121,7 @@ alias_get_frame (int framenum, aliashdr_t *hdr, float *frame_interval) numframes = group->numframes; fullinterval = intervals[numframes - 1]; - time = vr_data.realtime + currententity->syncbase; + time = vr_data.realtime + animation->syncbase; // when loading in Mod_LoadAliasGroup, we guaranteed all interval values // are positive, so we don't have to worry about division by 0 @@ -137,17 +140,17 @@ alias_get_frame (int framenum, aliashdr_t *hdr, float *frame_interval) } maliasframedesc_t * -R_AliasGetFramedesc (int framenum, aliashdr_t *hdr) +R_AliasGetFramedesc (animation_t *animation, aliashdr_t *hdr) { - return alias_get_frame (framenum, hdr, 0); + return alias_get_frame (animation, hdr, 0); } float -R_AliasGetLerpedFrames (entity_t *ent, aliashdr_t *hdr) +R_AliasGetLerpedFrames (animation_t *animation, aliashdr_t *hdr) { maliasframedesc_t *frame; float interval; - frame = alias_get_frame (ent->frame, hdr, &interval); - return R_EntityBlend (ent, frame->firstpose, interval); + frame = alias_get_frame (animation, hdr, &interval); + return R_EntityBlend (animation, frame->firstpose, interval); } diff --git a/libs/video/renderer/r_billboard.c b/libs/video/renderer/r_billboard.c new file mode 100644 index 000000000..aacf5112d --- /dev/null +++ b/libs/video/renderer/r_billboard.c @@ -0,0 +1,118 @@ +/* + r_billboard.c + + Billboard frame setup + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "QF/render.h" +#include "QF/sys.h" + +#include "QF/scene/entity.h" + +#include "r_internal.h" + + +int +R_BillboardFrame (transform_t transform, int orientation, vec4f_t cameravec, + vec4f_t *bbup, vec4f_t *bbright, vec4f_t *bbfwd) +{ + vec4f_t tvec; + float dot; + + switch (orientation) { + case SPR_FACING_UPRIGHT: + // the billboard has its up vector parallel with world up, and + // its right vector perpendicular to cameravec. + // Undefined if the camera is too close to the entity. + tvec = -normalf (cameravec); + dot = tvec[2]; // DotProduct (tvec, world up) + if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) + return 0; + *bbup = (vec4f_t) { 0, 0, 1, 0 }; + //CrossProduct(bbup, -cameravec, bbright) + *bbright = normalf ((vec4f_t) { tvec[1], -tvec[0], 0, 0 }); + //CrossProduct (bbright, bbup, bbfwd) + *bbfwd = (vec4f_t) { -(*bbright)[1], (*bbright)[0], 0, 0}; + break; + case SPR_VP_PARALLEL: + // the billboard always has the same orientation as the camera + *bbup = r_refdef.frame.up; + *bbright = r_refdef.frame.right; + *bbfwd = r_refdef.frame.forward; + break; + case SPR_VP_PARALLEL_UPRIGHT: + // the billboar has its up vector parallel with world up, and + // its right vector parallel with the view plane. + // Undefined if the camera is looking straight up or down. + dot = r_refdef.frame.forward[2]; + if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) + return 0; + *bbup = (vec4f_t) { 0, 0, 1, 0 }; + *bbright = normalf ((vec4f_t) {r_refdef.frame.forward[1], + -r_refdef.frame.forward[0], 0, 0 }); + *bbfwd = (vec4f_t) { -(*bbright)[1], (*bbright)[0], 0, 0}; + break; + case SPR_ORIENTED: + { + // The billboard's orientation is fully specified by the + // entity's orientation. + mat4f_t mat; + Transform_GetWorldMatrix (transform, mat); + *bbfwd = mat[0]; + *bbright = mat[1]; + *bbup = mat[2]; + } + break; + case SPR_VP_PARALLEL_ORIENTED: + { + // The billboard is rotated relative to the camera using + // the entity's local rotation. + mat4f_t entmat; + mat4f_t spmat; + Transform_GetLocalMatrix (transform, entmat); + // FIXME needs proper testing (need to find, make, or fake a + // parallel oriented sprite) + mmulf (spmat, r_refdef.camera, entmat); + *bbfwd = spmat[0]; + *bbright = spmat[1]; + *bbup = spmat[2]; + } + break; + default: + Sys_Error ("R_BillboardFrame: Bad orientation %d", orientation); + } + return 1; +} diff --git a/libs/video/renderer/r_bsp.c b/libs/video/renderer/r_bsp.c index a6fdc961d..19b5475e9 100644 --- a/libs/video/renderer/r_bsp.c +++ b/libs/video/renderer/r_bsp.c @@ -36,58 +36,63 @@ #endif #include "QF/cvar.h" +#include "QF/set.h" #include "QF/sys.h" +#include "QF/scene/entity.h" + #include "r_internal.h" -mvertex_t *r_pcurrentvertbase; -mleaf_t *r_viewleaf; static mleaf_t *r_oldviewleaf; +static set_t *solid; void -R_MarkLeaves (void) +R_MarkLeaves (mleaf_t *viewleaf, int *node_visframes, int *leaf_visframes, + int *face_visframes) { - byte solid[8192]; - byte *vis; + set_t *vis; int c; - unsigned int i; mleaf_t *leaf; - mnode_t *node; msurface_t **mark; + mod_brush_t *brush = &r_refdef.worldmodel->brush; - if (r_oldviewleaf == r_viewleaf && !r_novis->int_val) + if (r_oldviewleaf == viewleaf && !r_novis) return; r_visframecount++; - r_oldviewleaf = r_viewleaf; - if (!r_viewleaf) + r_oldviewleaf = viewleaf; + if (!viewleaf) return; - if (r_novis->int_val) { + if (r_novis) { r_oldviewleaf = 0; // so vis will be recalcualted when novis gets // turned off + if (!solid) { + solid = set_new (); + set_everything (solid); + } vis = solid; - memset (solid, 0xff, (r_worldentity.model->numleafs + 7) >> 3); } else - vis = Mod_LeafPVS (r_viewleaf, r_worldentity.model); + vis = Mod_LeafPVS (viewleaf, r_refdef.worldmodel); - for (i = 0; (int) i < r_worldentity.model->numleafs; i++) { - if (vis[i >> 3] & (1 << (i & 7))) { - leaf = &r_worldentity.model->leafs[i + 1]; + for (unsigned i = 0; i < brush->visleafs; i++) { + if (set_is_member (vis, i)) { + leaf = &brush->leafs[i + 1]; if ((c = leaf->nummarksurfaces)) { - mark = leaf->firstmarksurface; + mark = brush->marksurfaces + leaf->firstmarksurface; do { - (*mark)->visframe = r_visframecount; + face_visframes[*mark - brush->surfaces] = r_visframecount; mark++; } while (--c); } - node = (mnode_t *) leaf; - do { - if (node->visframe == r_visframecount) + leaf_visframes[i + 1] = r_visframecount; + int node_id = brush->leaf_parents[leaf - brush->leafs]; + while (node_id >= 0) { + if (node_visframes[node_id] == r_visframecount) break; - node->visframe = r_visframecount; - node = node->parent; - } while (node); + node_visframes[node_id] = r_visframecount; + node_id = brush->node_parents[node_id]; + } } } } @@ -98,12 +103,12 @@ R_MarkLeaves (void) Returns the proper texture for a given time and base texture */ texture_t * -R_TextureAnimation (msurface_t *surf) +R_TextureAnimation (int frame, msurface_t *surf) { texture_t *base = surf->texinfo->texture; int count, relative; - if (currententity->frame) { + if (frame) { if (base->alternate_anims) base = base->alternate_anims; } @@ -111,7 +116,7 @@ R_TextureAnimation (msurface_t *surf) if (!base->anim_total) return base; - relative = (int) (vr_data.realtime * 10) % base->anim_total; + relative = (int) (r_data->realtime * 10) % base->anim_total; count = 0; while (base->anim_min > relative || base->anim_max <= relative) { diff --git a/libs/video/renderer/r_cvar.c b/libs/video/renderer/r_cvar.c index d9f3e14d2..41c666d0a 100644 --- a/libs/video/renderer/r_cvar.c +++ b/libs/video/renderer/r_cvar.c @@ -41,84 +41,470 @@ #include "compat.h" #include "r_internal.h" -cvar_t *easter_eggs; +int cl_crossx; +static cvar_t cl_crossx_cvar = { + .name = "cl_crossx", + .description = + "Sets the position of the crosshair on the X-axis.", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &cl_crossx }, +}; +int cl_crossy; +static cvar_t cl_crossy_cvar = { + .name = "cl_crossy", + .description = + "Sets the position of the crosshair on the Y-axis.", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &cl_crossy }, +}; +char *cl_verstring; +static cvar_t cl_verstring_cvar = { + .name = "cl_verstring", + .description = + "Client version string", + .default_value = PACKAGE_VERSION, + .flags = CVAR_NONE, + .value = { .type = 0, .value = &cl_verstring }, +}; +int crosshair; +static cvar_t crosshair_cvar = { + .name = "crosshair", + .description = + "Crosshair type. 0 off, 1 old white, 2 new with colors", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &crosshair }, +}; +int crosshaircolor; +static cvar_t crosshaircolor_cvar = { + .name = "crosshaircolor", + .description = + "Color of the new crosshair", + .default_value = "79", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &crosshaircolor }, +}; -cvar_t *cl_crossx; -cvar_t *cl_crossy; -cvar_t *cl_verstring; -cvar_t *crosshair; -cvar_t *crosshaircolor; +float d_mipcap; +static cvar_t d_mipcap_cvar = { + .name = "d_mipcap", + .description = + "Detail level. 0 is highest, 3 is lowest.", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &d_mipcap }, +}; +float d_mipscale; +static cvar_t d_mipscale_cvar = { + .name = "d_mipscale", + .description = + "Detail level of objects. 0 is highest, 3 is lowest.", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &d_mipscale }, +}; -cvar_t *d_mipcap; -cvar_t *d_mipscale; +int r_aliasstats; +static cvar_t r_aliasstats_cvar = { + .name = "r_polymodelstats", + .description = + "Toggles the displays of number of polygon models current being viewed", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_aliasstats }, +}; +float r_aliastransadj; +static cvar_t r_aliastransadj_cvar = { + .name = "r_aliastransadj", + .description = + "Determines how much of an alias model is clipped away and how much is" + " viewable.", + .default_value = "100", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &r_aliastransadj }, +}; +float r_aliastransbase; +static cvar_t r_aliastransbase_cvar = { + .name = "r_aliastransbase", + .description = + "Determines how much of an alias model is clipped away and how much is" + " viewable", + .default_value = "200", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &r_aliastransbase }, +}; +int r_clearcolor; +static cvar_t r_clearcolor_cvar = { + .name = "r_clearcolor", + .description = + "This sets the color for areas outside of the current map", + .default_value = "2", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_clearcolor }, +}; +int r_dlight_lightmap; +static cvar_t r_dlight_lightmap_cvar = { + .name = "r_dlight_lightmap", + .description = + "Set to 1 for high quality dynamic lighting.", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &r_dlight_lightmap }, +}; +int r_dlight_max; +static cvar_t r_dlight_max_cvar = { + .name = "r_dlight_max", + .description = + "Number of dynamic lights.", + .default_value = "32", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &r_dlight_max }, +}; +int r_drawentities; +static cvar_t r_drawentities_cvar = { + .name = "r_drawentities", + .description = + "Toggles drawing of entities (almost everything but the world)", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_drawentities }, +}; +int r_drawexplosions; +static cvar_t r_drawexplosions_cvar = { + .name = "r_drawexplosions", + .description = + "Draw explosions.", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &r_drawexplosions }, +}; +int r_drawviewmodel; +static cvar_t r_drawviewmodel_cvar = { + .name = "r_drawviewmodel", + .description = + "Toggles view model drawing (your weapons)", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &r_drawviewmodel }, +}; +int r_dspeeds; +static cvar_t r_dspeeds_cvar = { + .name = "r_dspeeds", + .description = + "Toggles the display of drawing speed information", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_dspeeds }, +}; +int r_dynamic; +static cvar_t r_dynamic_cvar = { + .name = "r_dynamic", + .description = + "Set to 0 to disable lightmap changes", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_dynamic }, +}; +int r_explosionclip; +static cvar_t r_explosionclip_cvar = { + .name = "r_explosionclip", + .description = + "Clip explosions.", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &r_explosionclip }, +}; +float r_farclip; +static cvar_t r_farclip_cvar = { + .name = "r_farclip", + .description = + "Distance of the far clipping plane from the player.", + .default_value = "4096", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &r_farclip }, +}; +vec4f_t r_firecolor; +static cvar_t r_firecolor_cvar = { + .name = "r_firecolor", + .description = + "color of rocket and lava ball fires", + .default_value = "[0.9, 0.7, 0.0]", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_vector, .value = &r_firecolor }, +}; +int r_flatlightstyles; +static cvar_t r_flatlightstyles_cvar = { + .name = "r_flatlightstyles", + .description = + "Disable animated lightmaps. 2 = use peak, 1 = use average, anything " + "else = normal", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_flatlightstyles }, +}; +int r_graphheight; +static cvar_t r_graphheight_cvar = { + .name = "r_graphheight", + .description = + "Set the number of lines displayed in the various graphs", + .default_value = "32", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_graphheight }, +}; +int r_lightmap_components; +static cvar_t r_lightmap_components_cvar = { + .name = "r_lightmap_components", + .description = + "Lightmap texture components. 1 is greyscale, 3 is RGB, 4 is RGBA.", + .default_value = "3", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &r_lightmap_components }, +}; +int r_maxedges; +static cvar_t r_maxedges_cvar = { + .name = "r_maxedges", + .description = + "Sets the maximum number of edges", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_maxedges }, +}; +int r_maxsurfs; +static cvar_t r_maxsurfs_cvar = { + .name = "r_maxsurfs", + .description = + "Sets the maximum number of surfaces", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_maxsurfs }, +}; +float r_mirroralpha; +static cvar_t r_mirroralpha_cvar = { + .name = "r_mirroralpha", + .description = + "None", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &r_mirroralpha }, +}; +float r_nearclip; +static cvar_t r_nearclip_cvar = { + .name = "r_nearclip", + .description = + "Distance of the near clipping plane from the player.", + .default_value = "4", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &r_nearclip }, +}; +int r_norefresh; +static cvar_t r_norefresh_cvar = { + .name = "r_norefresh_", + .description = + "Set to 1 to disable display refresh", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_norefresh }, +}; +int r_novis; +static cvar_t r_novis_cvar = { + .name = "r_novis", + .description = + "Set to 1 to enable runtime visibility checking (SLOW)", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_novis }, +}; +int r_numedges; +static cvar_t r_numedges_cvar = { + .name = "r_numedges", + .description = + "Toggles the displaying of number of edges currently being viewed", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_numedges }, +}; +int r_numsurfs; +static cvar_t r_numsurfs_cvar = { + .name = "r_numsurfs", + .description = + "Toggles the displaying of number of surfaces currently being viewed", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_numsurfs }, +}; +int r_reportedgeout; +static cvar_t r_reportedgeout_cvar = { + .name = "r_reportedgeout", + .description = + "Toggle the display of how many edges were not displayed", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_reportedgeout }, +}; +int r_reportsurfout; +static cvar_t r_reportsurfout_cvar = { + .name = "r_reportsurfout", + .description = + "Toggle the display of how many surfaces were not displayed", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_reportsurfout }, +}; +int r_shadows; +static cvar_t r_shadows_cvar = { + .name = "r_shadows", + .description = + "Set to 1 to enable shadows for entities", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &r_shadows }, +}; +char *r_skyname; +static cvar_t r_skyname_cvar = { + .name = "r_skyname", + .description = + "name of the current skybox", + .default_value = "none", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &r_skyname }, +}; +int r_speeds; +static cvar_t r_speeds_cvar = { + .name = "r_speeds", + .description = + "Display drawing time and statistics of what is being viewed", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_speeds }, +}; +int r_timegraph; +static cvar_t r_timegraph_cvar = { + .name = "r_timegraph", + .description = + "Toggle the display of a performance graph", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_timegraph }, +}; +float r_wateralpha; +static cvar_t r_wateralpha_cvar = { + .name = "r_wateralpha", + .description = + "Determine the opacity of liquids. 1 = opaque, 0 = transparent, " + "otherwise translucent.", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &r_wateralpha }, +}; +float r_waterripple; +static cvar_t r_waterripple_cvar = { + .name = "r_waterripple", + .description = + "Set to make liquids ripple, try setting to 5", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &r_waterripple }, +}; +int r_waterwarp; +static cvar_t r_waterwarp_cvar = { + .name = "r_waterwarp", + .description = + "Toggles whether surfaces are warped in liquid.", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_waterwarp }, +}; +int r_zgraph; +static cvar_t r_zgraph_cvar = { + .name = "r_zgraph", + .description = + "Toggle the graph that reports the changes of z-axis position", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_zgraph }, +}; -cvar_t *r_aliasstats; -cvar_t *r_aliastransadj; -cvar_t *r_aliastransbase; -cvar_t *r_ambient; -cvar_t *r_clearcolor; -cvar_t *r_dlight_lightmap; -cvar_t *r_dlight_max; -cvar_t *r_drawentities; -cvar_t *r_drawexplosions; -cvar_t *r_drawflat; -cvar_t *r_drawviewmodel; -cvar_t *r_dspeeds; -cvar_t *r_dynamic; -cvar_t *r_explosionclip; -cvar_t *r_farclip; -cvar_t *r_firecolor; -cvar_t *r_flatlightstyles; -cvar_t *r_graphheight; -cvar_t *r_lightmap_components; -cvar_t *r_maxedges; -cvar_t *r_maxsurfs; -cvar_t *r_mirroralpha; -cvar_t *r_nearclip; -cvar_t *r_norefresh; -cvar_t *r_novis; -cvar_t *r_numedges; -cvar_t *r_numsurfs; -cvar_t *r_particles; -cvar_t *r_particles_style; -cvar_t *r_particles_max; -cvar_t *r_particles_nearclip; -cvar_t *r_reportedgeout; -cvar_t *r_reportsurfout; -cvar_t *r_shadows; -cvar_t *r_skyname; -cvar_t *r_speeds; -cvar_t *r_timegraph; -cvar_t *r_wateralpha; -cvar_t *r_waterripple; -cvar_t *r_waterwarp; -cvar_t *r_zgraph; - -cvar_t *scr_fov; -cvar_t *scr_fisheye; -cvar_t *scr_fviews; -cvar_t *scr_ffov; -cvar_t *scr_showpause; -cvar_t *scr_showram; -cvar_t *scr_showturtle; -cvar_t *scr_viewsize; +float scr_fov; +static cvar_t scr_fov_cvar = { + .name = "fov", + .description = + "Your field of view in degrees. Smaller than 90 zooms in. Don't touch " + "in fisheye mode, use ffov instead.", + .default_value = "90", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &scr_fov }, +}; +int scr_fisheye; +static cvar_t scr_fisheye_cvar = { + .name = "fisheye", + .description = + "Toggles fisheye mode.", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &scr_fisheye }, +}; +int scr_fviews; +static cvar_t scr_fviews_cvar = { + .name = "fviews", + .description = + "The number of fisheye views.", + .default_value = "6", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &scr_fviews }, +}; +float scr_ffov; +static cvar_t scr_ffov_cvar = { + .name = "ffov", + .description = + "Your field of view in degrees in fisheye mode.", + .default_value = "180", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &scr_ffov }, +}; +int scr_viewsize; +static cvar_t scr_viewsize_cvar = { + .name = "viewsize", + .description = + "Set the screen size 30 minimum, 120 maximum", + .default_value = "100", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &scr_viewsize }, +}; int r_viewsize; quat_t crosshair_color; static void -crosshaircolor_f (cvar_t *var) +set_crosshair_color (int col, const viddef_t *vid) { byte *color; - color = (byte *) &d_8to24table[bound (0, var->int_val, 255)]; + color = &vid->palette32[bound (0, col, 255) * 4]; QuatScale (color, 1.0 / 255, crosshair_color); } static void -r_lightmap_components_f (cvar_t *var) +crosshaircolor_update (void *data, const viddef_t *vid) { - switch (var->int_val) { + set_crosshair_color (crosshaircolor, vid); +} + +static void +crosshaircolor_f (void *data, const cvar_t *cvar) +{ + if (!r_data->vid->palette32) { + // palette not initialized yet + return; + } + set_crosshair_color (crosshaircolor, r_data->vid); +} + +static void +r_lightmap_components_f (void *data, const cvar_t *cvar) +{ + switch (r_lightmap_components) { case 1: mod_lightmap_bytes = 1; break; @@ -131,184 +517,117 @@ r_lightmap_components_f (cvar_t *var) } static void -r_farclip_f (cvar_t *var) +r_farclip_f (void *data, const cvar_t *cvar) { - Cvar_SetValue (r_farclip, bound (8.0, var->value, Q_MAXFLOAT)); - if (r_particles_nearclip && r_nearclip) - Cvar_SetValue (r_particles_nearclip, - bound (r_nearclip->value, r_particles_nearclip->value, - r_farclip->value)); - vid.recalc_refdef = true; + r_farclip = bound (8.0, r_farclip, Q_MAXFLOAT); + r_particles_nearclip = bound (r_nearclip, r_particles_nearclip, r_farclip); + r_data->vid->recalc_refdef = true; } static void -r_nearclip_f (cvar_t *var) +r_nearclip_f (void *data, const cvar_t *cvar) { - Cvar_SetValue (r_nearclip, bound (0.01, var->value, 4.0)); - if (r_particles_nearclip && r_farclip) - Cvar_SetValue (r_particles_nearclip, - bound (r_nearclip->value, r_particles_nearclip->value, - r_farclip->value)); - vid.recalc_refdef = true; + r_nearclip = bound (0.01, r_nearclip, 4.0); + r_particles_nearclip = bound (r_nearclip, r_particles_nearclip, r_farclip); + r_data->vid->recalc_refdef = true; } static void -scr_fisheye_f (cvar_t *var) +scr_fov_f (void *data, const cvar_t *cvar) { - if (var->int_val) - Cvar_Set (scr_fov, "90"); + // bound field of view + scr_fov = bound (0, scr_fov, 170); + SCR_SetFOV (scr_fov); } static void -scr_ffov_f (cvar_t *var) +scr_fisheye_f (void *data, const cvar_t *cvar) { - if (var->value < 130) - Cvar_Set (scr_fviews, "3"); - else if (var->value < 220) - Cvar_Set (scr_fviews, "5"); + if (scr_fisheye) + Cvar_Set ("fov", "90"); +} + +static void +scr_ffov_f (void *data, const cvar_t *cvar) +{ + if (scr_ffov < 130) + Cvar_Set ("fviews", "3"); + else if (scr_ffov < 220) + Cvar_Set ("fviews", "5"); else - Cvar_Set (scr_fviews, "6"); + Cvar_Set ("fviews", "6"); } static void -viewsize_f (cvar_t *var) +viewsize_f (void *data, const cvar_t *cvar) { - if (var->int_val < 30 || var->int_val > 120) { - Cvar_SetValue (var, bound (30, var->int_val, 120)); - } else { - vid.recalc_refdef = true; - r_viewsize = bound (0, var->int_val, 100); - if (vr_data.viewsize_callback) - vr_data.viewsize_callback (var); - } + scr_viewsize = bound (30, scr_viewsize, 120); + r_data->vid->recalc_refdef = true; + r_viewsize = bound (0, scr_viewsize, 100); + if (r_data->viewsize_callback) + r_data->viewsize_callback (scr_viewsize); +} + +static void +r_dlight_max_f (void *data, const cvar_t *cvar) +{ + R_MaxDlightsCheck (r_dlight_max); } void R_Init_Cvars (void) { - cl_crossx = Cvar_Get ("cl_crossx", "0", CVAR_ARCHIVE, NULL, - "Sets the position of the crosshair on the X-axis."); - cl_crossy = Cvar_Get ("cl_crossy", "0", CVAR_ARCHIVE, NULL, - "Sets the position of the crosshair on the Y-axis."); - cl_verstring = Cvar_Get ("cl_verstring", PACKAGE_VERSION, CVAR_NONE, - NULL, "Client version string"); - crosshair = Cvar_Get ("crosshair", "0", CVAR_ARCHIVE, NULL, "Crosshair " - "type. 0 off, 1 old white, 2 new with colors"); - crosshaircolor = Cvar_Get ("crosshaircolor", "79", CVAR_ARCHIVE, - crosshaircolor_f, "Color of the new crosshair"); - d_mipcap = Cvar_Get ("d_mipcap", "0", CVAR_NONE, NULL, - "Detail level. 0 is highest, 3 is lowest."); - d_mipscale = Cvar_Get ("d_mipscale", "1", CVAR_NONE, NULL, "Detail level " - "of objects. 0 is highest, 3 is lowest."); - r_aliasstats = Cvar_Get ("r_polymodelstats", "0", CVAR_NONE, NULL, - "Toggles the displays of number of polygon " - "models current being viewed"); - r_aliastransadj = Cvar_Get ("r_aliastransadj", "100", CVAR_NONE, NULL, - "Determines how much of an alias model is " - "clipped away and how much is viewable."); - r_aliastransbase = Cvar_Get ("r_aliastransbase", "200", CVAR_NONE, NULL, - "Determines how much of an alias model is " - "clipped away and how much is viewable"); - r_ambient = Cvar_Get ("r_ambient", "0", CVAR_NONE, NULL, - "Determines the ambient lighting for a level"); - r_clearcolor = Cvar_Get ("r_clearcolor", "2", CVAR_NONE, NULL, - "This sets the color for areas outside of the " - "current map"); - r_dlight_lightmap = Cvar_Get ("r_dlight_lightmap", "1", CVAR_ARCHIVE, - NULL, "Set to 1 for high quality dynamic " - "lighting."); - r_dlight_max = Cvar_Get ("r_dlight_max", "32", CVAR_ARCHIVE, - R_MaxDlightsCheck, "Number of dynamic lights."); - r_drawentities = Cvar_Get ("r_drawentities", "1", CVAR_NONE, NULL, - "Toggles drawing of entities (almost " - "everything but the world)"); - r_drawexplosions = Cvar_Get ("r_drawexplosions", "1", CVAR_ARCHIVE, NULL, - "Draw explosions."); - r_drawflat = Cvar_Get ("r_drawflat", "0", CVAR_NONE, NULL, - "Toggles the drawing of textures"); - r_drawviewmodel = Cvar_Get ("r_drawviewmodel", "1", CVAR_ARCHIVE, NULL, - "Toggles view model drawing (your weapons)"); - r_dspeeds = Cvar_Get ("r_dspeeds", "0", CVAR_NONE, NULL, - "Toggles the display of drawing speed information"); - r_dynamic = Cvar_Get ("r_dynamic", "1", CVAR_NONE, NULL, - "Set to 0 to disable lightmap changes"); - r_explosionclip = Cvar_Get ("r_explosionclip", "0", CVAR_ARCHIVE, NULL, - "Clip explosions."); - r_farclip = Cvar_Get ("r_farclip", "4096", CVAR_ARCHIVE, r_farclip_f, - "Distance of the far clipping plane from the " - "player."); - r_firecolor = Cvar_Get ("r_firecolor", "0.9 0.7 0.0", CVAR_ARCHIVE, NULL, - "color of rocket and lava ball fires"); - r_flatlightstyles = Cvar_Get ("r_flatlightstyles", "0", CVAR_NONE, NULL, - "Disable animated lightmaps. 2 = use peak, " - "1 = use average, anything else = normal"); - r_graphheight = Cvar_Get ("r_graphheight", "32", CVAR_NONE, NULL, - "Set the number of lines displayed in the " - "various graphs"); - r_lightmap_components = Cvar_Get ("r_lightmap_components", "3", CVAR_ROM, - r_lightmap_components_f, - "Lightmap texture components. 1 " - "is greyscale, 3 is RGB, 4 is RGBA."); - r_maxedges = Cvar_Get ("r_maxedges", "0", CVAR_NONE, NULL, - "Sets the maximum number of edges"); - r_maxsurfs = Cvar_Get ("r_maxsurfs", "0", CVAR_NONE, NULL, - "Sets the maximum number of surfaces"); - r_mirroralpha = Cvar_Get ("r_mirroralpha", "1", CVAR_NONE, NULL, "None"); - r_nearclip = Cvar_Get ("r_nearclip", "4", CVAR_ARCHIVE, r_nearclip_f, - "Distance of the near clipping plane from the " - "player."); - r_norefresh = Cvar_Get ("r_norefresh_", "0", CVAR_NONE, NULL, - "Set to 1 to disable display refresh"); - r_novis = Cvar_Get ("r_novis", "0", CVAR_NONE, NULL, "Set to 1 to enable " - "runtime visibility checking (SLOW)"); - r_numedges = Cvar_Get ("r_numedges", "0", CVAR_NONE, NULL, - "Toggles the displaying of number of edges " - "currently being viewed"); - r_numsurfs = Cvar_Get ("r_numsurfs", "0", CVAR_NONE, NULL, - "Toggles the displaying of number of surfaces " - "currently being viewed"); - r_reportedgeout = Cvar_Get ("r_reportedgeout", "0", CVAR_NONE, NULL, - "Toggle the display of how many edges were " - "not displayed"); - r_reportsurfout = Cvar_Get ("r_reportsurfout", "0", CVAR_NONE, NULL, - "Toggle the display of how many surfaces " - "were not displayed"); - r_shadows = Cvar_Get ("r_shadows", "0", CVAR_ARCHIVE, NULL, - "Set to 1 to enable shadows for entities"); - r_skyname = Cvar_Get ("r_skyname", "none", CVAR_NONE, NULL, - "name of the current skybox"); - r_speeds = Cvar_Get ("r_speeds", "0", CVAR_NONE, NULL, "Display drawing " - "time and statistics of what is being viewed"); - r_timegraph = Cvar_Get ("r_timegraph", "0", CVAR_NONE, NULL, - "Toggle the display of a performance graph"); - r_wateralpha = Cvar_Get ("r_wateralpha", "1", CVAR_ARCHIVE, NULL, - "Determine the opacity of liquids. 1 = opaque, " - "0 = transparent, otherwise translucent."); - r_waterripple = Cvar_Get ("r_waterripple", "0", CVAR_NONE, NULL, - "Set to make liquids ripple, try setting to 5"); - r_waterwarp = Cvar_Get ("r_waterwarp", "1", CVAR_NONE, NULL, - "Toggles whether surfaces are warped in liquid."); - r_zgraph = Cvar_Get ("r_zgraph", "0", CVAR_NONE, NULL, - "Toggle the graph that reports the changes of " - "z-axis position"); - scr_fov = Cvar_Get ("fov", "90", CVAR_NONE, NULL, "Your field of view in " - "degrees. Smaller than 90 zooms in. Don't touch in " - "fisheye mode, use ffov instead."); - scr_fisheye = Cvar_Get ("fisheye", "0", CVAR_NONE, scr_fisheye_f, - "Toggles fisheye mode."); - scr_fviews = Cvar_Get ("fviews", "6", CVAR_NONE, NULL, "The number of " - "fisheye views."); - scr_ffov = Cvar_Get ("ffov", "180", CVAR_NONE, scr_ffov_f, "Your field of " - "view in degrees in fisheye mode."); - scr_showpause = Cvar_Get ("showpause", "1", CVAR_NONE, NULL, - "Toggles display of pause graphic"); - scr_showram = Cvar_Get ("showram", "1", CVAR_NONE, NULL, - "Show RAM icon if game is running low on memory"); - scr_showturtle = Cvar_Get ("showturtle", "0", CVAR_NONE, NULL, - "Show a turtle icon if your fps is below 10"); - scr_viewsize = Cvar_Get ("viewsize", "100", CVAR_ARCHIVE, viewsize_f, - "Set the screen size 30 minimum, 120 maximum"); + Cvar_Register (&cl_crossx_cvar, 0, 0); + Cvar_Register (&cl_crossy_cvar, 0, 0); + Cvar_Register (&cl_verstring_cvar, 0, 0); + Cvar_Register (&crosshair_cvar, 0, 0); + Cvar_Register (&crosshaircolor_cvar, crosshaircolor_f, 0); + VID_OnPaletteChange_AddListener (crosshaircolor_update, 0); + Cvar_Register (&d_mipcap_cvar, 0, 0); + Cvar_Register (&d_mipscale_cvar, 0, 0); + Cvar_Register (&r_aliasstats_cvar, 0, 0); + Cvar_Register (&r_aliastransadj_cvar, 0, 0); + Cvar_Register (&r_aliastransbase_cvar, 0, 0); + Cvar_Register (&r_clearcolor_cvar, 0, 0); + Cvar_Register (&r_dlight_lightmap_cvar, 0, 0); + Cvar_Register (&r_dlight_max_cvar, r_dlight_max_f, 0); + Cvar_Register (&r_drawentities_cvar, 0, 0); + Cvar_Register (&r_drawexplosions_cvar, 0, 0); + Cvar_Register (&r_drawviewmodel_cvar, 0, 0); + Cvar_Register (&r_dspeeds_cvar, 0, 0); + Cvar_Register (&r_dynamic_cvar, 0, 0); + Cvar_Register (&r_explosionclip_cvar, 0, 0); + Cvar_Register (&r_farclip_cvar, r_farclip_f, 0); + Cvar_Register (&r_firecolor_cvar, 0, 0); + Cvar_Register (&r_flatlightstyles_cvar, 0, 0); + Cvar_Register (&r_graphheight_cvar, 0, 0); + Cvar_Register (&r_lightmap_components_cvar, r_lightmap_components_f, 0); + Cvar_Register (&r_maxedges_cvar, 0, 0); + Cvar_Register (&r_maxsurfs_cvar, 0, 0); + Cvar_Register (&r_mirroralpha_cvar, 0, 0); + Cvar_Register (&r_nearclip_cvar, r_nearclip_f, 0); + Cvar_Register (&r_norefresh_cvar, 0, 0); + Cvar_Register (&r_novis_cvar, 0, 0); + Cvar_Register (&r_numedges_cvar, 0, 0); + Cvar_Register (&r_numsurfs_cvar, 0, 0); + Cvar_Register (&r_reportedgeout_cvar, 0, 0); + Cvar_Register (&r_reportsurfout_cvar, 0, 0); + Cvar_Register (&r_shadows_cvar, 0, 0); + Cvar_Register (&r_skyname_cvar, 0, 0); + Cvar_Register (&r_speeds_cvar, 0, 0); + Cvar_Register (&r_timegraph_cvar, 0, 0); + Cvar_Register (&r_wateralpha_cvar, 0, 0); + Cvar_Register (&r_waterripple_cvar, 0, 0); + Cvar_Register (&r_waterwarp_cvar, 0, 0); + Cvar_Register (&r_zgraph_cvar, 0, 0); + Cvar_Register (&scr_fov_cvar, scr_fov_f, 0); + Cvar_Register (&scr_fisheye_cvar, scr_fisheye_f, 0); + Cvar_Register (&scr_fviews_cvar, 0, 0); + Cvar_Register (&scr_ffov_cvar, scr_ffov_f, 0); + Cvar_Register (&scr_viewsize_cvar, viewsize_f, 0); - vr_data.graphheight = r_graphheight; - vr_data.scr_viewsize = scr_viewsize; + r_data->graphheight = &r_graphheight; + r_data->scr_viewsize = &scr_viewsize; + + R_Particles_Init_Cvars (); } diff --git a/libs/video/renderer/r_draw.c b/libs/video/renderer/r_draw.c new file mode 100644 index 000000000..a86a61aa5 --- /dev/null +++ b/libs/video/renderer/r_draw.c @@ -0,0 +1,157 @@ +/* + r_draw.c + + Renderer general draw support + + Copyright (C) 2022 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include + +#include "QF/draw.h" +#include "QF/render.h" + +#include "QF/plugin/vid_render.h" + +VISIBLE draw_charbuffer_t * +Draw_CreateBuffer (int width, int height) +{ + size_t size = width * height; + draw_charbuffer_t *buffer = malloc (sizeof (draw_charbuffer_t) + size); + buffer->width = width; + buffer->height = height; + buffer->chars = (char *) (buffer + 1); + buffer->cursx = buffer->cursy = 0; + return buffer; +} + +VISIBLE void +Draw_DestroyBuffer (draw_charbuffer_t *buffer) +{ + free (buffer); +} + +VISIBLE void +Draw_ClearBuffer (draw_charbuffer_t *buffer) +{ + memset (buffer->chars, ' ', buffer->height * buffer->width); + buffer->cursx = buffer->cursy = 0; +} + +VISIBLE void +Draw_ScrollBuffer (draw_charbuffer_t *buffer, int lines) +{ + int down = 0; + if (lines < 0) { + lines = -lines; + down = 1; + } + if (lines > buffer->height) { + lines = buffer->height; + } + if (down) { + memmove (buffer->chars + lines * buffer->width, buffer->chars, + (buffer->height - lines) * buffer->width); + memset (buffer->chars, ' ', lines * buffer->width); + } else { + memmove (buffer->chars, buffer->chars + lines * buffer->width, + (buffer->height - lines) * buffer->width); + memset (buffer->chars + (buffer->height - lines) * buffer->width, ' ', + lines * buffer->width); + } +} + +VISIBLE void +Draw_CharBuffer (int x, int y, draw_charbuffer_t *buffer) +{ + r_funcs->Draw_CharBuffer (x, y, buffer); +} + +#define TAB 8 + +VISIBLE int +Draw_PrintBuffer (draw_charbuffer_t *buffer, const char *str) +{ + char *dst = buffer->chars; + char c; + int tab; + int lines = 0; + dst += buffer->cursy * buffer->width + buffer->cursx; + while ((c = *str++)) { + switch (c) { + case '\r': + dst -= buffer->cursx; + buffer->cursx = 0; + break; + case '\n': + dst -= buffer->cursx; + buffer->cursx = 0; + dst += buffer->width; + buffer->cursy++; + lines++; + break; + case '\f': + dst += buffer->width; + buffer->cursy++; + lines++; + break; + case '\t': + tab = TAB - buffer->cursx % TAB; + buffer->cursx += tab; + dst += tab; + break; + default: + *dst++ = c; + buffer->cursx++; + break; + } + if (buffer->cursx >= buffer->width) { + buffer->cursx -= buffer->width; + buffer->cursy++; + lines++; + } + if (buffer->cursy >= buffer->height) { + int excess = buffer->cursy - buffer->height + 1; + Draw_ScrollBuffer (buffer, excess); + dst -= excess * buffer->width; + buffer->cursy -= excess; + } + } + return lines; +} + +void +Draw_SetScale (int scale) +{ + if (r_funcs->Draw_SetScale) { + r_funcs->Draw_SetScale (scale); + } +} + +int +Draw_MaxScale (void) +{ + return r_funcs->Draw_SetScale ? 20 : 1; +} diff --git a/libs/video/renderer/r_dyn_textures.c b/libs/video/renderer/r_dyn_textures.c index 167027fad..67b0862c6 100644 --- a/libs/video/renderer/r_dyn_textures.c +++ b/libs/video/renderer/r_dyn_textures.c @@ -50,7 +50,8 @@ R_DotParticleTexture (void) int x, y, dx2, dy, d; tex_t *tex; - tex = malloc (field_offset (tex_t, data[sizeof (*data)])); + tex = malloc (sizeof (tex_t) + sizeof (*data)); + tex->data = (byte *) (tex + 1); tex->width = 32; tex->height = 32; tex->format = tex_la; @@ -79,7 +80,8 @@ R_SparkParticleTexture (void) int x, y, dx2, dy, d; tex_t *tex; - tex = malloc (field_offset (tex_t, data[sizeof (*data)])); + tex = malloc (sizeof (tex_t) + sizeof (*data)); + tex->data = (byte*) (tex + 1); tex->width = 32; tex->height = 32; tex->format = tex_la; @@ -113,7 +115,8 @@ R_SmokeParticleTexture (void) int x, y, c; tex_t *tex; - tex = malloc (field_offset (tex_t, data[sizeof (*data)])); + tex = malloc (sizeof (tex_t) + sizeof (*data)); + tex->data = (byte *) (tex + 1); tex->width = 32; tex->height = 32; tex->format = tex_la; diff --git a/libs/video/renderer/r_ent.c b/libs/video/renderer/r_ent.c index e72d10399..df5374e58 100644 --- a/libs/video/renderer/r_ent.c +++ b/libs/video/renderer/r_ent.c @@ -38,110 +38,36 @@ #include #include -#include "QF/model.h" -#include "QF/msg.h" -#include "QF/render.h" -#include "QF/sound.h" -#include "QF/sys.h" +#include "QF/scene/entity.h" #include "r_internal.h" -#define ENT_POOL_SIZE 32 - -typedef struct entity_pool_s { - struct entity_pool_s *next; - entity_t entities[ENT_POOL_SIZE]; -} entity_pool_t; - -entity_t *r_ent_queue; -static entity_t **vis_tail = &r_ent_queue; - -static entity_pool_t *entity_pools = 0; -static entity_pool_t **entpool_tail = &entity_pools; -static entity_t *free_entities; - -entity_t * -R_AllocEntity (void) -{ - entity_pool_t *pool; - entity_t *ent; - int i; - - if ((ent = free_entities)) { - free_entities = ent->next; - ent->next = 0; - return ent; - } - - pool = malloc (sizeof (entity_pool_t)); - pool->next = 0; - *entpool_tail = pool; - entpool_tail = &pool->next; - - for (ent = pool->entities, i = 0; i < ENT_POOL_SIZE - 1; i++, ent++) - ent->next = ent + 1; - ent->next = 0; - free_entities = pool->entities; - - return R_AllocEntity (); -} - -void -R_FreeAllEntities (void) -{ - entity_pool_t *pool; - entity_t *ent; - int i; - - for (pool = entity_pools; pool; pool = pool->next) { - for (ent = pool->entities, i = 0; i < ENT_POOL_SIZE - 1; i++, ent++) - ent->next = ent + 1; - ent->next = pool->next ? pool->next->entities : 0; - } - free_entities = entity_pools ? entity_pools->entities : 0; -} - -void -R_ClearEnts (void) -{ - r_ent_queue = 0; - vis_tail = &r_ent_queue; -} - -void -R_EnqueueEntity (entity_t *ent) -{ - ent->next = 0; - *vis_tail = ent; - vis_tail = &ent->next; -} - float -R_EntityBlend (entity_t *ent, int pose, float interval) +R_EntityBlend (animation_t *animation, int pose, float interval) { float blend; - if (ent->pose_model != ent->model) { - ent->pose_model = ent->model; - ent->pose1 = pose; - ent->pose2 = pose; + if (animation->nolerp) { + animation->nolerp = 0; + animation->pose1 = pose; + animation->pose2 = pose; return 0.0; } - ent->frame_interval = interval; - if (ent->pose2 != pose) { - ent->frame_start_time = vr_data.realtime; - if (ent->pose2 == -1) { - ent->pose1 = pose; + animation->frame_interval = interval; + if (animation->pose2 != pose) { + animation->frame_start_time = vr_data.realtime; + if (animation->pose2 == -1) { + animation->pose1 = pose; } else { - ent->pose1 = ent->pose2; + animation->pose1 = animation->pose2; } - ent->pose2 = pose; + animation->pose2 = pose; blend = 0.0; } else if (vr_data.paused) { blend = 1.0; } else { - blend = (vr_data.realtime - ent->frame_start_time) - / ent->frame_interval; + blend = (vr_data.realtime - animation->frame_start_time) + / animation->frame_interval; blend = min (blend, 1.0); } return blend; diff --git a/libs/video/renderer/glsl/glsl_fog.c b/libs/video/renderer/r_fog.c similarity index 81% rename from libs/video/renderer/glsl/glsl_fog.c rename to libs/video/renderer/r_fog.c index 8e246b6b7..51595cfe6 100644 --- a/libs/video/renderer/glsl/glsl_fog.c +++ b/libs/video/renderer/r_fog.c @@ -17,15 +17,12 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -//gl_fog.c -- global and volumetric fog +//r_fog.c -- global and volumetric fog #ifdef HAVE_CONFIG_H # include "config.h" #endif -#define NH_DEFINE -#include "namehack.h" - #ifdef HAVE_STRING_H # include #endif @@ -34,13 +31,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif #include "QF/cmd.h" -#include "QF/qfplist.h" +#include "QF/plist.h" #include "QF/render.h" #include "QF/sys.h" -#include "QF/GLSL/defines.h" -#include "QF/GLSL/funcs.h" - #include "compat.h" #include "r_internal.h" @@ -69,15 +63,15 @@ static float fade_done; //time when fade will be done update internal variables */ void -glsl_Fog_Update (float density, float red, float green, float blue, float time) +Fog_Update (float density, float red, float green, float blue, float time) { //save previous settings for fade if (time > 0) { //check for a fade in progress - if (fade_done > vr_data.realtime) { + if (fade_done > r_data->realtime) { float f; - f = (fade_done - vr_data.realtime) / fade_time; + f = (fade_done - r_data->realtime) / fade_time; old_density = f * old_density + (1.0 - f) * fog_density; old_red = f * old_red + (1.0 - f) * fog_red; old_green = f * old_green + (1.0 - f) * fog_green; @@ -95,7 +89,7 @@ glsl_Fog_Update (float density, float red, float green, float blue, float time) fog_green = green; fog_blue = blue; fade_time = time; - fade_done = vr_data.realtime + time; + fade_done = r_data->realtime + time; } /* @@ -155,7 +149,7 @@ Fog_FogCommand_f (void) red = bound (0.0, red, 1.0); green = bound (0.0, green, 1.0); blue = bound (0.0, blue, 1.0); - glsl_Fog_Update (density, red, green, blue, time); + Fog_Update (density, red, green, blue, time); } /* @@ -164,7 +158,7 @@ Fog_FogCommand_f (void) called at map load */ void -glsl_Fog_ParseWorldspawn (plitem_t *worldspawn) +Fog_ParseWorldspawn (plitem_t *worldspawn) { plitem_t *fog; const char *value; @@ -189,32 +183,29 @@ glsl_Fog_ParseWorldspawn (plitem_t *worldspawn) calculates fog color for this frame, taking into account fade times */ -float * -glsl_Fog_GetColor (void) +void +Fog_GetColor (quat_t fogcolor) { - static float c[4]; float f; int i; - if (fade_done > vr_data.realtime) { - f = (fade_done - vr_data.realtime) / fade_time; - c[0] = f * old_red + (1.0 - f) * fog_red; - c[1] = f * old_green + (1.0 - f) * fog_green; - c[2] = f * old_blue + (1.0 - f) * fog_blue; - c[3] = 1.0; + if (fade_done > r_data->realtime) { + f = (fade_done - r_data->realtime) / fade_time; + fogcolor[0] = f * old_red + (1.0 - f) * fog_red; + fogcolor[1] = f * old_green + (1.0 - f) * fog_green; + fogcolor[2] = f * old_blue + (1.0 - f) * fog_blue; + fogcolor[3] = 1.0; } else { - c[0] = fog_red; - c[1] = fog_green; - c[2] = fog_blue; - c[3] = 1.0; + fogcolor[0] = fog_red; + fogcolor[1] = fog_green; + fogcolor[2] = fog_blue; + fogcolor[3] = 1.0; } //find closest 24-bit RGB value, so solid-colored sky can match the fog //perfectly for (i = 0; i < 3; i++) - c[i] = (float) (rint (c[i] * 255)) / 255.0f; - - return c; + fogcolor[i] = (float) (rint (fogcolor[i] * 255)) / 255.0f; } /* @@ -223,12 +214,12 @@ glsl_Fog_GetColor (void) returns current density of fog */ float -glsl_Fog_GetDensity (void) +Fog_GetDensity (void) { float f; - if (fade_done > vr_data.realtime) { - f = (fade_done - vr_data.realtime) / fade_time; + if (fade_done > r_data->realtime) { + f = (fade_done - r_data->realtime) / fade_time; return f * old_density + (1.0 - f) * fog_density; } else { return fog_density; @@ -241,7 +232,7 @@ glsl_Fog_GetDensity (void) called when quake initializes */ void -glsl_Fog_Init (void) +Fog_Init (void) { Cmd_AddCommand ("fog", Fog_FogCommand_f, ""); diff --git a/libs/video/renderer/r_graph.c b/libs/video/renderer/r_graph.c index 4234ca11a..52bbfe570 100644 --- a/libs/video/renderer/r_graph.c +++ b/libs/video/renderer/r_graph.c @@ -27,16 +27,20 @@ #ifdef HAVE_CONFIG_H # include "config.h" #endif +#include +#include "QF/cvar.h" #include "QF/draw.h" #include "QF/render.h" #include "QF/plugin.h" #include "QF/sys.h" +#include "QF/ui/view.h" + #include "r_internal.h" -#define MAX_TIMINGS 100 +#define MAX_TIMINGS 200 int graphval; @@ -46,42 +50,51 @@ int graphval; Performance monitoring tool */ void -R_TimeGraph (void) +R_TimeGraph (view_pos_t abs, view_pos_t len) { static int timex; int a; int l; - //XXX float r_time2; - static int r_timings[MAX_TIMINGS]; - int x; + double r_time2; + static int r_timings[MAX_TIMINGS]; + int timings[MAX_TIMINGS]; + int o; - //XXX r_time2 = Sys_DoubleTime (); + r_time2 = Sys_DoubleTime (); - a = graphval; - - r_timings[timex] = a; + r_timings[timex] = (r_time2 - r_time1) * 10000; + //printf ("%d %g\n", r_timings[timex], r_time2 - r_time1); l = MAX_TIMINGS; if (l > r_refdef.vrect.width) l = r_refdef.vrect.width; - x = r_refdef.vrect.width - l; + o = 0; a = timex - l; if (a < 0) { - vr_funcs->R_LineGraph (x, r_refdef.vrect.height - 2, - &r_timings[a + MAX_TIMINGS], -a); - x -= a; + memcpy (timings + o, r_timings + a + MAX_TIMINGS, + -a * sizeof (timings[0])); + o -= a; l += a; a = 0; } - vr_funcs->R_LineGraph (x, r_refdef.vrect.height - 2, &r_timings[a], l); + memcpy (timings + o, r_timings + a, l * sizeof (timings[0])); + r_funcs->R_LineGraph (abs.x, abs.y, r_timings, MAX_TIMINGS, 200); + r_funcs->Draw_Line (abs.x, abs.y, abs.x + MAX_TIMINGS, abs.y, 0x0f); + r_funcs->Draw_Line (abs.x, abs.y - 10, abs.x + MAX_TIMINGS, abs.y - 10, 0x3f); + r_funcs->Draw_Line (abs.x, abs.y - 20, abs.x + MAX_TIMINGS, abs.y - 20, 0x3f); + r_funcs->Draw_Line (abs.x, abs.y - 25, abs.x + MAX_TIMINGS, abs.y - 25, 0x3f); + r_funcs->Draw_Line (abs.x, abs.y - 33, abs.x + MAX_TIMINGS, abs.y - 33, 0x3f); + r_funcs->Draw_Line (abs.x, abs.y - 50, abs.x + MAX_TIMINGS, abs.y - 50, 0x3f); + r_funcs->Draw_Line (abs.x, abs.y - 100, abs.x + MAX_TIMINGS, abs.y - 100, 0x3f); + //r_data->graphheight->int_val); timex = (timex + 1) % MAX_TIMINGS; } void -R_ZGraph (void) +R_ZGraph (view_pos_t abs, view_pos_t len) { - int x, w; + int w; static int height[256]; if (r_refdef.vrect.width <= 256) @@ -89,8 +102,7 @@ R_ZGraph (void) else w = 256; - height[r_framecount & 255] = ((int) r_origin[2]) & 31; + height[r_framecount & 255] = ((int) r_refdef.frame.position[2]) & 31; - x = 0; - vr_funcs->R_LineGraph (x, r_refdef.vrect.height - 2, height, w); + r_funcs->R_LineGraph (abs.x, abs.y, height, w, *r_data->graphheight); } diff --git a/libs/video/renderer/r_init.c b/libs/video/renderer/r_init.c index d4a7e91cd..ea4f7b353 100644 --- a/libs/video/renderer/r_init.c +++ b/libs/video/renderer/r_init.c @@ -46,8 +46,18 @@ #include "QF/plugin/general.h" #include "r_internal.h" +#include "r_scrap.h" +#include "vid_internal.h" -cvar_t *vidrend_plugin; +char *vidrend_plugin; +static cvar_t vidrend_plugin_cvar = { + .name = "vid_render", + .description = + "Video Render Plugin to use", + .default_value = VID_RENDER_DEFAULT, + .flags = CVAR_ROM, + .value = { .type = 0, .value = &vidrend_plugin }, +}; plugin_t *vidrendmodule = NULL; VID_RENDER_PLUGIN_PROTOS @@ -60,30 +70,40 @@ vid_render_funcs_t *r_funcs; #define U __attribute__ ((used)) static U void (*const r_progs_init)(struct progs_s *) = R_Progs_Init; +static U void (*const r_scrapdelete)(rscrap_t *) = R_ScrapDelete; #undef U +static void +R_shutdown (void *data) +{ + R_ShutdownEfrags (); + R_MaxDlightsCheck (0); // frees memory + PI_UnloadPlugin (vidrendmodule); +} + VISIBLE void -R_LoadModule (void (*load_gl)(void), void (*set_palette) (const byte *palette)) +R_LoadModule (vid_internal_t *vid_internal) { PI_RegisterPlugins (vidrend_plugin_list); - vidrend_plugin = Cvar_Get ("vid_render", VID_RENDER_DEFAULT, CVAR_ROM, 0, - "Video Render Plugin to use"); - vidrendmodule = PI_LoadPlugin ("vid_render", vidrend_plugin->string); + Cvar_Register (&vidrend_plugin_cvar, 0, 0); + vidrendmodule = PI_LoadPlugin ("vid_render", vidrend_plugin); if (!vidrendmodule) { Sys_Error ("Loading of video render module: %s failed!\n", - vidrend_plugin->string); + vidrend_plugin); } r_funcs = vidrendmodule->functions->vid_render; mod_funcs = r_funcs->model_funcs; r_data = vidrendmodule->data->vid_render; - r_data->vid->load_gl = load_gl; - r_data->vid->set_palette = set_palette; + r_data->vid->vid_internal = vid_internal; - vidrendmodule->functions->general->p_Init (); + r_funcs->init (); + Sys_RegisterShutdown (R_shutdown, 0); } VISIBLE void R_Init (void) { r_funcs->R_Init (); + R_ClearEfrags (); //FIXME force link of r_efrag.o for qwaq + Fog_Init (); } diff --git a/libs/video/renderer/r_iqm.c b/libs/video/renderer/r_iqm.c index 4fa961228..488deb051 100644 --- a/libs/video/renderer/r_iqm.c +++ b/libs/video/renderer/r_iqm.c @@ -43,29 +43,30 @@ #include "QF/render.h" #include "QF/skin.h" #include "QF/sys.h" +#include "QF/scene/entity.h" #include "r_internal.h" float -R_IQMGetLerpedFrames (entity_t *ent, iqm_t *iqm) +R_IQMGetLerpedFrames (animation_t *animation, iqm_t *iqm) { - int frame = ent->frame; + int frame = animation->frame; float time, fullinterval; iqmanim *anim; if (!iqm->num_anims) - return R_EntityBlend (ent, 0, 1.0 / 25.0); + return R_EntityBlend (animation, 0, 1.0 / 25.0); if (frame >= iqm->num_anims || frame < 0) { - Sys_MaskPrintf (SYS_DEV, "R_IQMGetLerpedFrames: no such frame %d\n", + Sys_MaskPrintf (SYS_dev, "R_IQMGetLerpedFrames: no such frame %d\n", frame); frame = 0; } anim = &iqm->anims[frame]; fullinterval = anim->num_frames / anim->framerate; - time = vr_data.realtime + currententity->syncbase; + time = vr_data.realtime + animation->syncbase; time -= ((int) (time / fullinterval)) * fullinterval; frame = (int) (time * anim->framerate) + anim->first_frame; - return R_EntityBlend (ent, frame, 1.0 / anim->framerate); + return R_EntityBlend (animation, frame, 1.0 / anim->framerate); } iqmframe_t * @@ -75,7 +76,7 @@ R_IQMBlendFrames (const iqm_t *iqm, int frame1, int frame2, float blend, iqmframe_t *frame; int i; - frame = Hunk_TempAlloc (iqm->num_joints * sizeof (iqmframe_t) + extra); + frame = Hunk_TempAlloc (0, iqm->num_joints * sizeof (iqmframe_t) + extra); if (iqm->num_frames) { #if 0 for (i = 0; i < iqm->num_joints; i++) { @@ -99,7 +100,7 @@ R_IQMBlendFrames (const iqm_t *iqm, int frame1, int frame2, float blend, } else { #if 0 for (i = 0; i < iqm->num_joints; i++) { - QuatSet (1, 0, 0, 0, frame[i].rt.q0.q); + QuatSet (0, 0, 0, 1, frame[i].rt.q0.q); QuatSet (0, 0, 0, 0, frame[i].rt.qe.q); QuatSet (0, 0, 0, 0, frame[i].shear); QuatSet (1, 1, 1, 0, frame[i].scale); diff --git a/libs/video/renderer/r_light.c b/libs/video/renderer/r_light.c index bd242e7bc..1b9cae06c 100644 --- a/libs/video/renderer/r_light.c +++ b/libs/video/renderer/r_light.c @@ -50,9 +50,10 @@ dlight_t *r_dlights; vec3_t ambientcolor; unsigned int r_maxdlights; +static int r_dlightframecount; void -R_FindNearLights (const vec3_t pos, int count, dlight_t **lights) +R_FindNearLights (vec4f_t pos, int count, dlight_t **lights) { float *scores = alloca (count * sizeof (float)); float score; @@ -63,7 +64,7 @@ R_FindNearLights (const vec3_t pos, int count, dlight_t **lights) dl = r_dlights; for (i = 0; i < r_maxdlights; i++, dl++) { - if (dl->die < vr_data.realtime || !dl->radius) + if (dl->die < r_data->realtime || !dl->radius) continue; VectorSubtract (dl->origin, pos, d); score = DotProduct (d, d) / dl->radius; @@ -106,14 +107,14 @@ R_FindNearLights (const vec3_t pos, int count, dlight_t **lights) } void -R_MaxDlightsCheck (cvar_t *var) +R_MaxDlightsCheck (int max_dlights) { - r_maxdlights = bound (0, var->int_val, MAX_DLIGHTS); + r_maxdlights = bound (0, max_dlights, MAX_DLIGHTS); if (r_dlights) free (r_dlights); - r_dlights=0; + r_dlights = 0; if (r_maxdlights) r_dlights = (dlight_t *) calloc (r_maxdlights, sizeof (dlight_t)); @@ -126,28 +127,32 @@ R_AnimateLight (void) { int i, j, k; + if (!r_data->lightstyle) { + return; + } + // light animations // 'm' is normal light, 'a' is no light, 'z' is double bright - i = (int) (vr_data.realtime * 10); + i = (int) (r_data->realtime * 10); for (j = 0; j < MAX_LIGHTSTYLES; j++) { - if (!vr_data.lightstyle[j].length) { + if (!r_data->lightstyle[j].length) { d_lightstylevalue[j] = 256; continue; } - if (r_flatlightstyles->int_val == 2) { - k = vr_data.lightstyle[j].peak - 'a'; - } else if (r_flatlightstyles->int_val == 1) { - k = vr_data.lightstyle[j].average - 'a'; + if (r_flatlightstyles == 2) { + k = r_data->lightstyle[j].peak - 'a'; + } else if (r_flatlightstyles == 1) { + k = r_data->lightstyle[j].average - 'a'; } else { - k = i % vr_data.lightstyle[j].length; - k = vr_data.lightstyle[j].map[k] - 'a'; + k = i % r_data->lightstyle[j].length; + k = r_data->lightstyle[j].map[k] - 'a'; } d_lightstylevalue[j] = k * 22; } } static inline void -real_mark_surfaces (float dist, msurface_t *surf, const vec3_t lightorigin, +real_mark_surfaces (float dist, msurface_t *surf, vec4f_t lightorigin, dlight_t *light, unsigned lightnum) { float dist2, is, it; @@ -175,9 +180,9 @@ real_mark_surfaces (float dist, msurface_t *surf, const vec3_t lightorigin, if (is * is + it * it > dist2) return; - if (surf->dlightframe != r_framecount) { + if (surf->dlightframe != r_dlightframecount) { memset (surf->dlightbits, 0, sizeof (surf->dlightbits)); - surf->dlightframe = r_framecount; + surf->dlightframe = r_dlightframecount; } ind = lightnum / 32; bit = 1 << (lightnum % 32); @@ -185,7 +190,7 @@ real_mark_surfaces (float dist, msurface_t *surf, const vec3_t lightorigin, } static inline void -mark_surfaces (msurface_t *surf, const vec3_t lightorigin, dlight_t *light, +mark_surfaces (msurface_t *surf, vec4f_t lightorigin, dlight_t *light, int lightnum) { float dist; @@ -203,74 +208,71 @@ mark_surfaces (msurface_t *surf, const vec3_t lightorigin, dlight_t *light, // LordHavoc: heavily modified, to eliminate unnecessary texture uploads, // and support bmodel lighting better void -R_RecursiveMarkLights (const vec3_t lightorigin, dlight_t *light, int lightnum, - mnode_t *node) +R_RecursiveMarkLights (mod_brush_t *brush, vec4f_t lightorigin, + dlight_t *light, int lightnum, int node_id) { unsigned i; float ndist, maxdist; - plane_t *splitplane; msurface_t *surf; - //XXX mvertex_t *vertices; - //XXX vertices = r_worldentity.model->vertexes; maxdist = light->radius; loc0: - if (node->contents < 0) + if (node_id < 0) return; - splitplane = node->plane; - ndist = DotProduct (lightorigin, splitplane->normal) - splitplane->dist; + mnode_t *node = brush->nodes + node_id; + ndist = dotf (lightorigin, node->plane)[0]; if (ndist > maxdist * maxdist) { // Save time by not pushing another stack frame. - if (node->children[0]->contents >= 0) { - node = node->children[0]; + if (node->children[0] >= 0) { + node_id = node->children[0]; goto loc0; } return; } if (ndist < -maxdist * maxdist) { - // Save time by not pushing another stack frame. - if (node->children[1]->contents >= 0) { - node = node->children[1]; + if (node->children[1] >= 0) { + node_id = node->children[1]; goto loc0; } return; } // mark the polygons - surf = r_worldentity.model->surfaces + node->firstsurface; + surf = brush->surfaces + node->firstsurface; for (i = 0; i < node->numsurfaces; i++, surf++) { mark_surfaces (surf, lightorigin, light, lightnum); } - if (node->children[0]->contents >= 0) { - if (node->children[1]->contents >= 0) - R_RecursiveMarkLights (lightorigin, light, lightnum, + if (node->children[0] >= 0) { + if (node->children[1] >= 0) + R_RecursiveMarkLights (brush, lightorigin, light, lightnum, node->children[1]); - node = node->children[0]; + node_id = node->children[0]; goto loc0; - } else if (node->children[1]->contents >= 0) { - node = node->children[1]; + } else if (node->children[1] >= 0) { + node_id = node->children[1]; goto loc0; } } -void -R_MarkLights (const vec3_t lightorigin, dlight_t *light, int lightnum, +static void +R_MarkLights (vec4f_t lightorigin, dlight_t *light, int lightnum, model_t *model) { + mod_brush_t *brush = &model->brush; mleaf_t *pvsleaf = Mod_PointInLeaf (lightorigin, model); if (!pvsleaf->compressed_vis) { - mnode_t *node = model->nodes + model->hulls[0].firstclipnode; - R_RecursiveMarkLights (lightorigin, light, lightnum, node); + int node_id = brush->hulls[0].firstclipnode; + R_RecursiveMarkLights (brush, lightorigin, light, lightnum, node_id); } else { float radius = light->radius; vec3_t mins, maxs; - int leafnum = 0; + unsigned leafnum = 0; byte *in = pvsleaf->compressed_vis; byte vis_bits; @@ -280,29 +282,29 @@ R_MarkLights (const vec3_t lightorigin, dlight_t *light, int lightnum, maxs[0] = lightorigin[0] + radius; maxs[1] = lightorigin[1] + radius; maxs[2] = lightorigin[2] + radius; - while (leafnum < model->numleafs) { + while (leafnum < brush->visleafs) { int b; if (!(vis_bits = *in++)) { leafnum += (*in++) * 8; continue; } - for (b = 1; b < 256 && leafnum < model->numleafs; + for (b = 1; b < 256 && leafnum < brush->visleafs; b <<= 1, leafnum++) { int m; - mleaf_t *leaf = &model->leafs[leafnum + 1]; + mleaf_t *leaf = &brush->leafs[leafnum + 1]; if (!(vis_bits & b)) continue; - if (leaf->visframe != r_visframecount) + if (r_leaf_visframes[leafnum + 1] != r_visframecount) continue; if (leaf->mins[0] > maxs[0] || leaf->maxs[0] < mins[0] || leaf->mins[1] > maxs[1] || leaf->maxs[1] < mins[1] || leaf->mins[2] > maxs[2] || leaf->maxs[2] < mins[2]) continue; - if (R_CullBox (leaf->mins, leaf->maxs)) - continue; + msurface_t **msurf = brush->marksurfaces + leaf->firstmarksurface; for (m = 0; m < leaf->nummarksurfaces; m++) { - msurface_t *surf = leaf->firstmarksurface[m]; - if (surf->visframe != r_visframecount) + msurface_t *surf = *msurf++; + int surf_id = surf - brush->surfaces; + if (r_face_visframes[surf_id] != r_visframecount) continue; mark_surfaces (surf, lightorigin, light, lightnum); } @@ -316,24 +318,26 @@ R_PushDlights (const vec3_t entorigin) { unsigned int i; dlight_t *l; - vec3_t lightorigin; - if (!r_dlight_lightmap->int_val) + r_dlightframecount = r_framecount; + + if (!r_dlight_lightmap) return; l = r_dlights; for (i = 0; i < r_maxdlights; i++, l++) { - if (l->die < vr_data.realtime || !l->radius) + if (l->die < r_data->realtime || !l->radius) continue; + vec4f_t lightorigin; VectorSubtract (l->origin, entorigin, lightorigin); - R_MarkLights (lightorigin, l, i, r_worldentity.model); + lightorigin[3] = 1; + R_MarkLights (lightorigin, l, i, r_refdef.worldmodel); } } /* LIGHT SAMPLING */ -plane_t *lightplane; vec3_t lightspot; static int @@ -400,37 +404,34 @@ calc_lighting_3 (msurface_t *surf, int ds, int dt) } static int -RecursiveLightPoint (mnode_t *node, const vec3_t start, const vec3_t end) +RecursiveLightPoint (mod_brush_t *brush, int node_id, vec4f_t start, + vec4f_t end) { unsigned i; int r, s, t, ds, dt, side; float front, back, frac; - plane_t *plane; msurface_t *surf; mtexinfo_t *tex; - vec3_t mid; loop: - if (node->contents < 0) + if (node_id < 0) return -1; // didn't hit anything // calculate mid point - plane = node->plane; - front = DotProduct (start, plane->normal) - plane->dist; - back = DotProduct (end, plane->normal) - plane->dist; + mnode_t *node = brush->nodes + node_id; + front = dotf (start, node->plane)[0]; + back = dotf (end, node->plane)[0]; side = front < 0; if ((back < 0) == side) { - node = node->children[side]; + node_id = node->children[side]; goto loop; } frac = front / (front - back); - mid[0] = start[0] + (end[0] - start[0]) * frac; - mid[1] = start[1] + (end[1] - start[1]) * frac; - mid[2] = start[2] + (end[2] - start[2]) * frac; + vec4f_t mid = start + (end - start) * frac; // go down front side - r = RecursiveLightPoint (node->children[side], start, mid); + r = RecursiveLightPoint (brush, node->children[side], start, mid); if (r >= 0) return r; // hit something @@ -439,9 +440,8 @@ loop: // check for impact on this node VectorCopy (mid, lightspot); - lightplane = plane; - surf = r_worldentity.model->surfaces + node->firstsurface; + surf = brush->surfaces + node->firstsurface; for (i = 0; i < node->numsurfaces; i++, surf++) { if (surf->flags & SURF_DRAWTILED) continue; // no lightmaps @@ -472,26 +472,20 @@ loop: } // go down back side - return RecursiveLightPoint (node->children[!side], mid, end); + return RecursiveLightPoint (brush, node->children[side ^ 1], mid, end); } int -R_LightPoint (const vec3_t p) +R_LightPoint (mod_brush_t *brush, vec4f_t p) { - vec3_t end; - int r; - - if (!r_worldentity.model->lightdata) { + if (!brush->lightdata) { // allow dlights to have some effect, so don't go /quite/ fullbright ambientcolor[2] = ambientcolor[1] = ambientcolor[0] = 200; return 200; } - end[0] = p[0]; - end[1] = p[1]; - end[2] = p[2] - 2048; - - r = RecursiveLightPoint (r_worldentity.model->nodes, p, end); + vec4f_t end = p - (vec4f_t) { 0, 0, 2048, 0 }; + int r = RecursiveLightPoint (brush, 0, p, end); if (r == -1) r = 0; @@ -524,7 +518,7 @@ R_AllocDlight (int key) // then look for anything else dl = r_dlights; for (i = 0; i < r_maxdlights; i++, dl++) { - if (dl->die < vr_data.realtime) { + if (dl->die < r_data->realtime) { memset (dl, 0, sizeof (*dl)); dl->key = key; dl->color[0] = dl->color[1] = dl->color[2] = 1; @@ -546,7 +540,7 @@ R_DecayLights (double frametime) dl = r_dlights; for (i = 0; i < r_maxdlights; i++, dl++) { - if (dl->die < vr_data.realtime || !dl->radius) + if (dl->die < r_data->realtime || !dl->radius) continue; dl->radius -= frametime * dl->decay; diff --git a/libs/video/renderer/r_main.c b/libs/video/renderer/r_main.c index 0d38123d3..6e4dde6c1 100644 --- a/libs/video/renderer/r_main.c +++ b/libs/video/renderer/r_main.c @@ -45,39 +45,25 @@ #include "r_internal.h" -qboolean r_inhibit_viewmodel; -qboolean r_force_fullscreen; -qboolean r_paused; +bool r_dowarp, r_dowarpold; +bool r_inhibit_viewmodel; +bool r_force_fullscreen; +bool r_paused; double r_realtime; double r_frametime; -entity_t *r_view_model; -entity_t *r_player_entity; -float r_time1; +double r_time1; int r_lineadj; -qboolean r_active; +bool r_active; int r_init; -entity_t *currententity; - int r_visframecount; // bumped when going to a new PVS int r_framecount = 1; // so frame counts initialized to 0 don't match vec3_t modelorg; // modelorg is the viewpoint relative to // the currently rendering entity vec3_t base_modelorg; -vec3_t r_entorigin; // the currently rendering entity in world +vec4f_t r_entorigin; // the currently rendering entity in world // coordinates -entity_t *currententity; -entity_t r_worldentity; - -qboolean r_cache_thrash; // set if surface cache is thrashing - -// view origin -vec3_t vup, base_vup; -vec3_t vpn, base_vpn; -vec3_t vright, base_vright; -vec3_t r_origin; - // screen size info refdef_t r_refdef; @@ -102,26 +88,30 @@ SignbitsForPlane (plane_t *out) } void -R_SetFrustum (void) +R_SetFrustum (plane_t *frustum, const refframe_t *frame, + float fov_x, float fov_y) { int i; + vec4f_t right = frame->right; + vec4f_t fwd = frame->forward; + vec4f_t up = frame->up; - // rotate VPN right by FOV_X/2 degrees - RotatePointAroundVector (frustum[0].normal, vup, vpn, - -(90 - r_refdef.fov_x / 2)); - // rotate VPN left by FOV_X/2 degrees - RotatePointAroundVector (frustum[1].normal, vup, vpn, - 90 - r_refdef.fov_x / 2); - // rotate VPN up by FOV_Y/2 degrees - RotatePointAroundVector (frustum[2].normal, vright, vpn, - 90 - r_refdef.fov_y / 2); - // rotate VPN down by FOV_Y/2 degrees - RotatePointAroundVector (frustum[3].normal, vright, vpn, - -(90 - r_refdef.fov_y / 2)); + fov_x = 90 - fov_x / 2; + fov_y = 90 - fov_y / 2; + // rotate FWD right by FOV_X/2 degrees + RotatePointAroundVector (frustum[0].normal, (vec_t*)&up, (vec_t*)&fwd, -fov_x);//FIXME + // rotate FWD left by FOV_X/2 degrees + RotatePointAroundVector (frustum[1].normal, (vec_t*)&up, (vec_t*)&fwd, fov_x);//FIXME + // rotate FWD up by FOV_Y/2 degrees + RotatePointAroundVector (frustum[2].normal, (vec_t*)&right, (vec_t*)&fwd, fov_y);//FIXME + // rotate FWD down by FOV_Y/2 degrees + RotatePointAroundVector (frustum[3].normal, (vec_t*)&right, (vec_t*)&fwd, -fov_y);//FIXME + + vec4f_t origin = frame->position; for (i = 0; i < 4; i++) { frustum[i].type = PLANE_ANYZ; - frustum[i].dist = DotProduct (r_origin, frustum[i].normal); + frustum[i].dist = -DotProduct (origin, frustum[i].normal); frustum[i].signbits = SignbitsForPlane (&frustum[i]); } } diff --git a/libs/video/renderer/r_part.c b/libs/video/renderer/r_part.c index cfb7baac9..0a73882e9 100644 --- a/libs/video/renderer/r_part.c +++ b/libs/video/renderer/r_part.c @@ -28,104 +28,42 @@ # include "config.h" #endif -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif - -#include "QF/alloc.h" -#include "QF/cbuf.h" #include "QF/cvar.h" -#include "QF/dstring.h" -#include "QF/hash.h" #include "QF/qargs.h" #include "QF/render.h" -#include "QF/script.h" -#include "QF/segtext.h" #include "QF/sys.h" #include "compat.h" #include "r_internal.h" -typedef enum { - pt_phys_done, - pt_phys_add_vel, - pt_phys_sub_slowgrav, - pt_phys_add_grav, - pt_phys_sub_grav, - pt_phys_add_fastgrav, - pt_phys_add_ramp, // die - pt_phys_fade_alpha, // die - pt_phys_color_ramp1, - pt_phys_color_ramp2, - pt_phys_color_ramp3, - pt_phys_alpha_ramp, - pt_phys_explode_vel, - pt_phys_damp_vel, - pt_phys_grow_scale, - pt_phys_shrink_scale, // die - pt_phys_custom, // die -} pt_phys_e; - -typedef struct { - const char *name; - pt_phys_e opcode; - int num_params; -} pt_phys_opcode_t; - -typedef struct { - char *name; - qboolean (*func) (particle_t *part, void *data); - void *data; -} pt_phys_func_t; - -typedef union pt_phys_op_s { - int32_t op; - float parm; -} pt_phys_op_t; - -typedef struct pt_phys_script_s { - struct pt_phys_script_s *next; - char *name; - pt_phys_op_t *script; -} pt_phys_script_t; - -const char particle_types[] = -#include "particles.pc" -; - -static pt_phys_func_t *phys_funcs; -static int num_phys_funcs; -static int max_phys_funcs; -static pt_phys_script_t *phys_script_freelist; -static hashtab_t *phys_scripts; - -static pt_phys_opcode_t part_phys_opcodes[] = -{ - {"add_vel", pt_phys_add_vel, 0}, - {"sub_slowgrav", pt_phys_sub_slowgrav, 0}, - {"add_grav", pt_phys_add_grav, 0}, - {"sub_grav", pt_phys_sub_grav, 0}, - {"add_fastgrav", pt_phys_add_fastgrav, 0}, - {"add_ramp", pt_phys_add_ramp, 2}, - {"fade_alpha", pt_phys_fade_alpha, 1}, - {"color_ramp1", pt_phys_color_ramp1, 0}, - {"color_ramp2", pt_phys_color_ramp2, 0}, - {"color_ramp3", pt_phys_color_ramp3, 0}, - {"alpha_ramp", pt_phys_alpha_ramp, 1}, - {"explode_vel", pt_phys_explode_vel, 1}, - {"damp_vel", pt_phys_damp_vel, 1}, - {"grow_scale", pt_phys_grow_scale, 1}, - {"shrink_scale", pt_phys_shrink_scale, 1}, - - {0, pt_phys_done, 0}, +psystem_t r_psystem; //FIXME singleton +int r_particles; +static cvar_t r_particles_cvar = { + .name = "r_particles", + .description = + "Toggles drawing of particles.", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &r_particles }, +}; +int r_particles_max; +static cvar_t r_particles_max_cvar = { + .name = "r_particles_max", + .description = + "Maximum amount of particles to display. No maximum, minimum is 0.", + .default_value = "2048", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &r_particles_max }, +}; +float r_particles_nearclip; +static cvar_t r_particles_nearclip_cvar = { + .name = "r_particles_nearclip", + .description = + "Distance of the particle near clipping plane from the player.", + .default_value = "32", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &r_particles_nearclip }, }; - -unsigned int r_maxparticles, numparticles; -particle_t *active_particles, *free_particles, *particles, **freeparticles; -vec3_t r_pright, r_pup, r_ppn; /* R_MaxParticlesCheck @@ -134,420 +72,99 @@ vec3_t r_pright, r_pup, r_ppn; Thanks to a LOT of help from Taniwha, Deek, Mercury, Lordhavoc, and lots of others. */ -void -R_MaxParticlesCheck (cvar_t *r_particles, cvar_t *r_particles_max) +static void +R_MaxParticlesCheck (void) { -/* - Catchall. If the user changed the setting to a number less than zero *or* - if we had a wacky cfg get past the init code check, this will make sure we - don't have problems. Also note that grabbing the var->int_val is IMPORTANT: - Prevents a segfault since if we grabbed the int_val of r_particles_max - we'd sig11 right here at startup. -*/ - if (r_particles && r_particles->int_val) - r_maxparticles = r_particles_max ? r_particles_max->int_val : 0; - else - r_maxparticles = 0; + psystem_t *ps = &r_psystem;//FIXME + unsigned maxparticles = 0; -/* - Be very careful the next time we do something like this. calloc/free are - IMPORTANT and the compiler doesn't know when we do bad things with them. -*/ - if (particles) - free (particles); - if (freeparticles) - free (freeparticles); + maxparticles = r_particles ? r_particles_max : 0; - particles = 0; - freeparticles = 0; - - if (r_maxparticles) { - particles = (particle_t *) calloc (r_maxparticles, sizeof (particle_t)); - freeparticles = (particle_t **) calloc (r_maxparticles, - sizeof (particle_t *)); - } - - vr_funcs->R_ClearParticles (); - - if (r_init) - vr_funcs->R_InitParticles (); -} - -static int ramp1[8] = { 0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61 }; -static int ramp2[8] = { 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66 }; -static int ramp3[8] = { 0x6d, 0x6b, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 }; - -static inline float -slow_grav (void) -{ - return -vr_data.frametime * vr_data.gravity * 0.0375; -} - -static inline float -grav (void) -{ - return -vr_data.frametime * vr_data.gravity * 0.05; -} - -static inline float -fast_grav (void) -{ - return -vr_data.frametime * vr_data.gravity; -} - -static inline void -add_vel (particle_t *part) -{ - VectorMultAdd (part->org, vr_data.frametime, part->vel, part->org); -} - -static inline void -sub_slowgrav (particle_t *part) -{ - part->vel[2] -= slow_grav (); -} - -static inline void -add_grav (particle_t *part) -{ - part->vel[2] += grav (); -} - -static inline void -sub_grav (particle_t *part) -{ - part->vel[2] -= grav (); -} - -static inline void -add_fastgrav (particle_t *part) -{ - part->vel[2] += fast_grav (); -} - -static inline qboolean -add_ramp (particle_t *part, float time, int max) -{ - part->ramp += vr_data.frametime * time; - if (part->ramp >= max) { - part->die = -1; - return true; - } - return false; -} - -static inline qboolean -fade_alpha (particle_t *part, float time) -{ - part->alpha -= vr_data.frametime * time; - if (part->alpha <= 0.0) { - part->die = -1; - return true; - } - return false; -} - -static inline qboolean -custom_func (particle_t *part, int op) -{ - int func; - func = op - pt_phys_custom; - if (func < 0 || func >= num_phys_funcs) { - Sys_Error ("invalid custom particle physics opcode: %d", op); - } - if (phys_funcs[func].func (part, phys_funcs[func].data)) { - part->die = -1; - return true; - } - return false; -} - -void -R_RunParticlePhysics (particle_t *part) -{ - const pt_phys_op_t *op; - float parm, max; - - if (!part->physics) { - // safety net for physicsless particles - add_ramp (part, 1, 4); + if (ps->maxparticles == maxparticles) { return; } - for (op = part->physics; ; op++) { - if (op->op >= pt_phys_custom) { - if (custom_func (part, op->op)) - return; - } - switch ((pt_phys_e) op->op) { - case pt_phys_done: - return; - case pt_phys_add_vel: - add_vel (part); - break; - case pt_phys_sub_slowgrav: - sub_slowgrav (part); - break; - case pt_phys_add_grav: - add_grav (part); - break; - case pt_phys_sub_grav: - sub_grav (part); - break; - case pt_phys_add_fastgrav: - add_fastgrav (part); - break; - case pt_phys_add_ramp: - parm = (++op)->parm; - max = (++op)->parm; - if (add_ramp (part, parm, max)) - return; - break; - case pt_phys_fade_alpha: - parm = (++op)->parm; - if (fade_alpha (part, parm)) - return; - break; - case pt_phys_color_ramp1: - part->color = ramp1[((int) part->ramp) % sizeof (ramp1)]; - break; - case pt_phys_color_ramp2: - part->color = ramp2[((int) part->ramp) % sizeof (ramp2)]; - break; - case pt_phys_color_ramp3: - part->color = ramp3[((int) part->ramp) % sizeof (ramp3)]; - break; - case pt_phys_alpha_ramp: - parm = (++op)->parm; - part->alpha = (parm - part->ramp) / parm; - break; - case pt_phys_explode_vel: - parm = (++op)->parm; - VectorMultAdd (part->vel, vr_data.frametime * parm, part->vel, - part->vel); - break; - case pt_phys_damp_vel: - parm = (++op)->parm; - part->vel[0] -= part->vel[0] * vr_data.frametime * parm; - part->vel[1] -= part->vel[1] * vr_data.frametime * parm; - break; - case pt_phys_grow_scale: - parm = (++op)->parm; - part->scale += vr_data.frametime * parm; - break; - case pt_phys_shrink_scale: - parm = (++op)->parm; - part->scale -= vr_data.frametime * parm; - if (part->scale <= 0.0) { - part->die = -1; - return; - } - break; - case pt_phys_custom: - // handled above - break; - } + size_t size = sizeof (particle_t) + sizeof (partparm_t) + + sizeof (int *); + + if (ps->particles) { + Sys_Free (ps->particles, ps->maxparticles * size); + ps->particles = 0; + ps->partparams = 0; + ps->partramps = 0; } -} + ps->maxparticles = maxparticles; -static pt_phys_opcode_t * -part_find_opcode (const char *name) -{ - pt_phys_opcode_t *op; - - for (op = part_phys_opcodes; op->name; op++) { - if (strcmp (op->name, name) == 0) - return op; + if (ps->maxparticles) { + ps->particles = Sys_Alloc (ps->maxparticles * size); + ps->partparams = (partparm_t *) &ps->particles[ps->maxparticles]; + ps->partramps = (const int **) &ps->partparams[ps->maxparticles]; } - return 0; -} - -static pt_phys_func_t * -part_find_function (const char *name) -{ - pt_phys_func_t *func; - - //FIXME is a hash table worth it? - for (func = phys_funcs; func - phys_funcs < num_phys_funcs; func++) { - if (strcmp (func->name, name) == 0) - return func; - } - return 0; -} - -static void -part_add_op (dstring_t *phys, int op) -{ - int offs = phys->size; - phys->size += sizeof (pt_phys_op_t); - dstring_adjust (phys); - ((pt_phys_op_t *) (phys->str + offs))->op = op; -} - -static void -part_add_parm (dstring_t *phys, float parm) -{ - int offs = phys->size; - phys->size += sizeof (pt_phys_op_t); - dstring_adjust (phys); - ((pt_phys_op_t *) (phys->str + offs))->parm = parm; -} - -static pt_phys_script_t * -new_phys_script (void) -{ - pt_phys_script_t *phys_script; - ALLOC (16, pt_phys_script_t, phys_script, phys_script); - return phys_script; -} - -static void -free_phys_script (pt_phys_script_t *phys_script) -{ - FREE (phys_script, phys_script); -} - -static const char * -part_script_get_key (const void *_s, void *unused) -{ - const pt_phys_script_t *s = (const pt_phys_script_t *) _s; - return s->name; -} - -static void -part_script_free (void *_s, void *unused) -{ - pt_phys_script_t *s = (pt_phys_script_t *) _s; - free (s->name); - free (s->script); - free_phys_script (s); -} - -qboolean -R_CompileParticlePhysics (const char *name, const char *code) -{ - cbuf_args_t *args = Cbuf_ArgsNew (); - script_t *script = Script_New (); - pt_phys_opcode_t *opcode; - pt_phys_script_t *phys; - pt_phys_func_t *func; - dstring_t *phys_build = dstring_new (); - int i; - qboolean ret = false; - - if (!phys_scripts) { - phys_scripts = Hash_NewTable (61, part_script_get_key, - part_script_free, 0); - } - Script_Start (script, "name", code); - while (Script_GetToken (script, 1)) { - args->argc = 0; - Cbuf_ArgsAdd (args, Script_Token (script)); - while (Script_TokenAvailable (script, 0)) { - Script_GetToken (script, 0); - Cbuf_ArgsAdd (args, Script_Token (script)); - } - Sys_Printf ("%s:%d: %s\n", name, script->line, args->argv[0]->str); - if ((func = part_find_function (args->argv[0]->str))) { - if (args->argc > 1) { - Sys_Printf ("%s:%d: warning: ignoring extra args\n", - name, script->line); - } - part_add_op (phys_build, pt_phys_custom + (func - phys_funcs)); - } else if ((opcode = part_find_opcode (args->argv[0]->str))) { - part_add_op (phys_build, opcode->opcode); - if (args->argc - 1 > opcode->num_params) { - Sys_Printf ("%s:%d: warning: ignoring extra args\n", - name, script->line); - args->argc = opcode->num_params + 1; - } - for (i = 1; i < args->argc; i++) { - char *end; - float parm = strtof (args->argv[i]->str, &end); - if (end == args->argv[i]->str) { - Sys_Printf ("%s:%d: warning: bad float\n", - name, script->line); - } else if (*end) { - Sys_Printf ("%s:%d: warning: ignoring extra chars\n", - name, script->line); - } - part_add_parm (phys_build, parm); - } - if (args->argc - 1 > opcode->num_params) { - Sys_Printf ("%s:%d: warning: setting parms %d+ to zero\n", - name, script->line, args->argc - 1); - for (i--; i < opcode->num_params; i++) - part_add_parm (phys_build, 0); - } - } else { - Sys_Printf ("%s:%d: warning: unknown opcode/function: %s\n", - name, script->line, args->argv[0]->str); - } - } - if (phys_build->size) { - part_add_op (phys_build, pt_phys_done); - phys = new_phys_script (); - phys->name = strdup (name); - phys->script = (pt_phys_op_t *) dstring_freeze (phys_build); - Hash_Add (phys_scripts, phys); - ret = true; - } else { - dstring_delete (phys_build); - } - - Script_Delete (script); - Cbuf_ArgsDelete (args); - return ret; -} - -const pt_phys_op_t * -R_ParticlePhysics (const char *type) -{ - pt_phys_script_t *phys; - - if (!phys_scripts) - return 0; - if ((phys = Hash_Find (phys_scripts, type))) - return phys->script; - return 0; -} - -qboolean -R_AddParticlePhysicsFunction (const char *name, - qboolean (*func) (particle_t *part, void *data), - void *data) -{ - int i; - - for (i = 0; i < num_phys_funcs; i++) { - if (strcmp (name, phys_funcs[i].name) == 0) - return false; - } - if (num_phys_funcs == max_phys_funcs) { - max_phys_funcs += 16; - phys_funcs = realloc (phys_funcs, - max_phys_funcs * sizeof (pt_phys_func_t)); - if (!phys_funcs) { - Sys_Error ("R_AddParticlePhysicsFunction: out of memory"); - } - } - phys_funcs[num_phys_funcs].name = strdup (name); - phys_funcs[num_phys_funcs].func = func; - phys_funcs[num_phys_funcs].data = data; - return true; + R_ClearParticles (); } void -R_LoadParticles (void) +R_ClearParticles (void) { - segtext_t *text; - segchunk_t *chunk; + psystem_t *ps = &r_psystem;//FIXME + ps->numparticles = 0; +} - text = Segtext_new (particle_types); - for (chunk = text->chunk_list; chunk; chunk = chunk->next) { - if (chunk->tag) { - Sys_Printf ("compiling %s\n", chunk->tag); - R_CompileParticlePhysics (chunk->tag, chunk->text); +void +R_RunParticles (float dT) +{ + psystem_t *ps = &r_psystem;//FIXME + vec4f_t gravity = ps->gravity; + + unsigned j = 0; + for (unsigned i = 0; i < ps->numparticles; i++) { + particle_t *p = &ps->particles[i]; + partparm_t *parm = &ps->partparams[i]; + + if (p->live <= 0 || p->ramp >= parm->ramp_max) { + continue; + } + const int *ramp = ps->partramps[i]; + if (i > j) { + ps->particles[j] = *p; + ps->partparams[j] = *parm; + ps->partramps[j] = ramp; + } + j += 1; + + p->pos += dT * p->vel; + p->vel += dT * (p->vel * parm->drag + gravity * parm->drag[3]); + p->ramp += dT * parm->ramp; + p->live -= dT; + if (ramp) { + p->icolor = ramp[(int)p->ramp]; } } + ps->numparticles = j; +} + +static void +r_particles_nearclip_f (void *data, const cvar_t *cvar) +{ + r_particles_nearclip = bound (r_nearclip, r_particles_nearclip, r_farclip); +} + +static void +r_particles_f (void *data, const cvar_t *cvar) +{ + R_MaxParticlesCheck (); +} + +static void +r_particles_max_f (void *data, const cvar_t *cvar) +{ + R_MaxParticlesCheck (); +} + +void +R_Particles_Init_Cvars (void) +{ + Cvar_Register (&r_particles_cvar, r_particles_f, 0); + Cvar_Register (&r_particles_max_cvar, r_particles_max_f, 0); + Cvar_Register (&r_particles_nearclip_cvar, r_particles_nearclip_f, 0); } diff --git a/libs/video/renderer/r_part_comp.c b/libs/video/renderer/r_part_comp.c new file mode 100644 index 000000000..a451d6459 --- /dev/null +++ b/libs/video/renderer/r_part_comp.c @@ -0,0 +1,511 @@ +/* + r_part.c + + Interface for particles + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/alloc.h" +#include "QF/cbuf.h" +#include "QF/cvar.h" +#include "QF/dstring.h" +#include "QF/hash.h" +#include "QF/qargs.h" +#include "QF/render.h" +#include "QF/script.h" +#include "QF/segtext.h" +#include "QF/sys.h" + +#include "compat.h" +#include "r_internal.h" + +typedef enum { + pt_phys_done, + pt_phys_add_vel, + pt_phys_sub_slowgrav, + pt_phys_add_grav, + pt_phys_sub_grav, + pt_phys_add_fastgrav, + pt_phys_add_ramp, // die + pt_phys_fade_alpha, // die + pt_phys_color_ramp1, + pt_phys_color_ramp2, + pt_phys_color_ramp3, + pt_phys_alpha_ramp, + pt_phys_explode_vel, + pt_phys_damp_vel, + pt_phys_grow_scale, + pt_phys_shrink_scale, // die + pt_phys_custom, // die +} pt_phys_e; + +typedef struct { + const char *name; + pt_phys_e opcode; + int num_params; +} pt_phys_opcode_t; + +typedef struct { + char *name; + qboolean (*func) (particle_t *part, void *data); + void *data; +} pt_phys_func_t; + +typedef union pt_phys_op_s { + int32_t op; + float parm; +} pt_phys_op_t; + +typedef struct pt_phys_script_s { + struct pt_phys_script_s *next; + char *name; + pt_phys_op_t *script; +} pt_phys_script_t; + +const char particle_types[] = +#include "particles.pc" +; + +static pt_phys_func_t *phys_funcs; +static int num_phys_funcs; +static int max_phys_funcs; +static pt_phys_script_t *phys_script_freelist; +static hashtab_t *phys_scripts; + +static pt_phys_opcode_t part_phys_opcodes[] = +{ + {"add_vel", pt_phys_add_vel, 0}, + {"sub_slowgrav", pt_phys_sub_slowgrav, 0}, + {"add_grav", pt_phys_add_grav, 0}, + {"sub_grav", pt_phys_sub_grav, 0}, + {"add_fastgrav", pt_phys_add_fastgrav, 0}, + {"add_ramp", pt_phys_add_ramp, 2}, + {"fade_alpha", pt_phys_fade_alpha, 1}, + {"color_ramp1", pt_phys_color_ramp1, 0}, + {"color_ramp2", pt_phys_color_ramp2, 0}, + {"color_ramp3", pt_phys_color_ramp3, 0}, + {"alpha_ramp", pt_phys_alpha_ramp, 1}, + {"explode_vel", pt_phys_explode_vel, 1}, + {"damp_vel", pt_phys_damp_vel, 1}, + {"grow_scale", pt_phys_grow_scale, 1}, + {"shrink_scale", pt_phys_shrink_scale, 1}, + + {0, pt_phys_done, 0}, +}; + +unsigned int r_maxparticles, numparticles; +particle_t *active_particles, *free_particles, *particles, **freeparticles; +vec3_t r_pright, r_pup, r_ppn; + +/* + R_MaxParticlesCheck + + Misty-chan: Dynamically change the maximum amount of particles on the fly. + Thanks to a LOT of help from Taniwha, Deek, Mercury, Lordhavoc, and lots of + others. +*/ +static void +R_MaxParticlesCheck (void) +{ + psystem_t *ps = &r_psystem;//FIXME + unsigned maxparticles = 0; + + maxparticles = r_particles ? r_particles_max : 0; + + if (ps->maxparticles == maxparticles) { + return; + } + + size_t size = sizeof (particle_t) + sizeof (partparm_t) + + sizeof (int *); + + if (ps->particles) { + Sys_Free (ps->particles, ps->maxparticles * size); + ps->particles = 0; + ps->partparams = 0; + ps->partramps = 0; + } + ps->maxparticles = maxparticles; + + if (ps->maxparticles) { + ps->particles = Sys_Alloc (ps->maxparticles * size); + ps->partparams = (partparm_t *) &ps->particles[ps->maxparticles]; + ps->partramps = (const int **) &ps->partparams[ps->maxparticles]; + } + R_ClearParticles (); +} + +void +R_ClearParticles (void) +{ + psystem_t *ps = &r_psystem;//FIXME + ps->numparticles = 0; +} + +void +R_RunParticles (float dT) +{ + psystem_t *ps = &r_psystem;//FIXME + vec4f_t gravity = ps->gravity; + + unsigned j = 0; + for (unsigned i = 0; i < ps->numparticles; i++) { + particle_t *p = &ps->particles[i]; + partparm_t *parm = &ps->partparams[i]; + + if (p->live <= 0 || p->ramp >= parm->ramp_max) { + continue; + } + const int *ramp = ps->partramps[i]; + if (i > j) { + ps->particles[j] = *p; + ps->partparams[j] = *parm; + ps->partramps[j] = ramp; + } + j += 1; + + p->pos += dT * p->vel; + p->vel += dT * (p->vel * parm->drag + gravity * parm->drag[3]); + p->ramp += dT * parm->ramp; + p->live -= dT; + if (ramp) { + p->icolor = ramp[(int)p->ramp]; + } + } + ps->numparticles = j; +} + +static inline qboolean +custom_func (particle_t *part, int op) +{ + int func; + func = op - pt_phys_custom; + if (func < 0 || func >= num_phys_funcs) { + Sys_Error ("invalid custom particle physics opcode: %d", op); + } + if (phys_funcs[func].func (part, phys_funcs[func].data)) { + part->die = -1; + return true; + } + return false; +} + +void +R_RunParticlePhysics (particle_t *part) +{ + const pt_phys_op_t *op; + float parm, max; + + if (!part->physics) { + // safety net for physicsless particles + add_ramp (part, 1, 4); + return; + } + for (op = part->physics; ; op++) { + if (op->op >= pt_phys_custom) { + if (custom_func (part, op->op)) + return; + } + + switch ((pt_phys_e) op->op) { + case pt_phys_done: + return; + case pt_phys_add_vel: + add_vel (part); + break; + case pt_phys_sub_slowgrav: + sub_slowgrav (part); + break; + case pt_phys_add_grav: + add_grav (part); + break; + case pt_phys_sub_grav: + sub_grav (part); + break; + case pt_phys_add_fastgrav: + add_fastgrav (part); + break; + case pt_phys_add_ramp: + parm = (++op)->parm; + max = (++op)->parm; + if (add_ramp (part, parm, max)) + return; + break; + case pt_phys_fade_alpha: + parm = (++op)->parm; + if (fade_alpha (part, parm)) + return; + break; + case pt_phys_color_ramp1: + part->color = ramp1[((int) part->ramp) % sizeof (ramp1)]; + break; + case pt_phys_color_ramp2: + part->color = ramp2[((int) part->ramp) % sizeof (ramp2)]; + break; + case pt_phys_color_ramp3: + part->color = ramp3[((int) part->ramp) % sizeof (ramp3)]; + break; + case pt_phys_alpha_ramp: + parm = (++op)->parm; + part->alpha = (parm - part->ramp) / parm; + break; + case pt_phys_explode_vel: + parm = (++op)->parm; + VectorMultAdd (part->vel, vr_data.frametime * parm, part->vel, + part->vel); + break; + case pt_phys_damp_vel: + parm = (++op)->parm; + part->vel[0] -= part->vel[0] * vr_data.frametime * parm; + part->vel[1] -= part->vel[1] * vr_data.frametime * parm; + break; + case pt_phys_grow_scale: + parm = (++op)->parm; + part->scale += vr_data.frametime * parm; + break; + case pt_phys_shrink_scale: + parm = (++op)->parm; + part->scale -= vr_data.frametime * parm; + if (part->scale <= 0.0) { + part->die = -1; + return; + } + break; + case pt_phys_custom: + // handled above + break; + } + } +} + +static pt_phys_opcode_t * +part_find_opcode (const char *name) +{ + pt_phys_opcode_t *op; + + for (op = part_phys_opcodes; op->name; op++) { + if (strcmp (op->name, name) == 0) + return op; + } + return 0; +} + +static pt_phys_func_t * +part_find_function (const char *name) +{ + pt_phys_func_t *func; + + //FIXME is a hash table worth it? + for (func = phys_funcs; func - phys_funcs < num_phys_funcs; func++) { + if (strcmp (func->name, name) == 0) + return func; + } + return 0; +} + +static void +part_add_op (dstring_t *phys, int op) +{ + int offs = phys->size; + phys->size += sizeof (pt_phys_op_t); + dstring_adjust (phys); + ((pt_phys_op_t *) (phys->str + offs))->op = op; +} + +static void +part_add_parm (dstring_t *phys, float parm) +{ + int offs = phys->size; + phys->size += sizeof (pt_phys_op_t); + dstring_adjust (phys); + ((pt_phys_op_t *) (phys->str + offs))->parm = parm; +} + +static pt_phys_script_t * +new_phys_script (void) +{ + pt_phys_script_t *phys_script; + ALLOC (16, pt_phys_script_t, phys_script, phys_script); + return phys_script; +} + +static void +free_phys_script (pt_phys_script_t *phys_script) +{ + FREE (phys_script, phys_script); +} + +static const char * +part_script_get_key (const void *_s, void *unused) +{ + const pt_phys_script_t *s = (const pt_phys_script_t *) _s; + return s->name; +} + +static void +part_script_free (void *_s, void *unused) +{ + pt_phys_script_t *s = (pt_phys_script_t *) _s; + free (s->name); + free (s->script); + free_phys_script (s); +} + +qboolean +R_CompileParticlePhysics (const char *name, const char *code) +{ + cbuf_args_t *args = Cbuf_ArgsNew (); + script_t *script = Script_New (); + pt_phys_opcode_t *opcode; + pt_phys_script_t *phys; + pt_phys_func_t *func; + dstring_t *phys_build = dstring_new (); + int i; + qboolean ret = false; + + if (!phys_scripts) { + phys_scripts = Hash_NewTable (61, part_script_get_key, + part_script_free, 0); + } + Script_Start (script, "name", code); + while (Script_GetToken (script, 1)) { + args->argc = 0; + Cbuf_ArgsAdd (args, Script_Token (script)); + while (Script_TokenAvailable (script, 0)) { + Script_GetToken (script, 0); + Cbuf_ArgsAdd (args, Script_Token (script)); + } + Sys_Printf ("%s:%d: %s\n", name, script->line, args->argv[0]->str); + if ((func = part_find_function (args->argv[0]->str))) { + if (args->argc > 1) { + Sys_Printf ("%s:%d: warning: ignoring extra args\n", + name, script->line); + } + part_add_op (phys_build, pt_phys_custom + (func - phys_funcs)); + } else if ((opcode = part_find_opcode (args->argv[0]->str))) { + part_add_op (phys_build, opcode->opcode); + if (args->argc - 1 > opcode->num_params) { + Sys_Printf ("%s:%d: warning: ignoring extra args\n", + name, script->line); + args->argc = opcode->num_params + 1; + } + for (i = 1; i < args->argc; i++) { + char *end; + float parm = strtof (args->argv[i]->str, &end); + if (end == args->argv[i]->str) { + Sys_Printf ("%s:%d: warning: bad float\n", + name, script->line); + } else if (*end) { + Sys_Printf ("%s:%d: warning: ignoring extra chars\n", + name, script->line); + } + part_add_parm (phys_build, parm); + } + if (args->argc - 1 > opcode->num_params) { + Sys_Printf ("%s:%d: warning: setting parms %d+ to zero\n", + name, script->line, args->argc - 1); + for (i--; i < opcode->num_params; i++) + part_add_parm (phys_build, 0); + } + } else { + Sys_Printf ("%s:%d: warning: unknown opcode/function: %s\n", + name, script->line, args->argv[0]->str); + } + } + if (phys_build->size) { + part_add_op (phys_build, pt_phys_done); + phys = new_phys_script (); + phys->name = strdup (name); + phys->script = (pt_phys_op_t *) dstring_freeze (phys_build); + Hash_Add (phys_scripts, phys); + ret = true; + } else { + dstring_delete (phys_build); + } + + Script_Delete (script); + Cbuf_ArgsDelete (args); + return ret; +} + +const pt_phys_op_t * +R_ParticlePhysics (const char *type) +{ + pt_phys_script_t *phys; + + if (!phys_scripts) + return 0; + if ((phys = Hash_Find (phys_scripts, type))) + return phys->script; + return 0; +} + +qboolean +R_AddParticlePhysicsFunction (const char *name, + qboolean (*func) (particle_t *part, void *data), + void *data) +{ + int i; + + for (i = 0; i < num_phys_funcs; i++) { + if (strcmp (name, phys_funcs[i].name) == 0) + return false; + } + if (num_phys_funcs == max_phys_funcs) { + max_phys_funcs += 16; + phys_funcs = realloc (phys_funcs, + max_phys_funcs * sizeof (pt_phys_func_t)); + if (!phys_funcs) { + Sys_Error ("R_AddParticlePhysicsFunction: out of memory"); + } + } + phys_funcs[num_phys_funcs].name = strdup (name); + phys_funcs[num_phys_funcs].func = func; + phys_funcs[num_phys_funcs].data = data; + return true; +} + +void +R_LoadParticles (void) +{ + segtext_t *text; + segchunk_t *chunk; + + text = Segtext_new (particle_types); + for (chunk = text->chunk_list; chunk; chunk = chunk->next) { + if (chunk->tag) { + Sys_Printf ("compiling %s\n", chunk->tag); + R_CompileParticlePhysics (chunk->tag, chunk->text); + } + } +} diff --git a/libs/video/renderer/r_progs.c b/libs/video/renderer/r_progs.c index 66ae9072f..cd065d3f7 100644 --- a/libs/video/renderer/r_progs.c +++ b/libs/video/renderer/r_progs.c @@ -39,9 +39,12 @@ #include "QF/draw.h" #include "QF/hash.h" #include "QF/progs.h" +#include "QF/quakefs.h" #include "QF/render.h" #include "QF/sys.h" +#include "QF/ui/view.h" + #include "r_internal.h" typedef struct { @@ -59,16 +62,23 @@ typedef struct qpic_res_s { char *name; } qpic_res_t; +typedef struct bi_charbuff_s { + struct bi_charbuff_s *next; + draw_charbuffer_t *buffer; +} bi_charbuff_t; + typedef struct { PR_RESMAP (qpic_res_t) qpic_map; + PR_RESMAP (bi_charbuff_t) charbuff_map; qpic_res_t *qpics; hashtab_t *pic_hash; + bi_charbuff_t *charbuffs; } draw_resources_t; static qpic_res_t * qpic_new (draw_resources_t *res) { - PR_RESNEW (qpic_res_t, res->qpic_map); + return PR_RESNEW (res->qpic_map); } static void @@ -86,31 +96,30 @@ static void qpic_free (draw_resources_t *res, qpic_res_t *qp) { bi_draw_free_qpic (qp); - PR_RESFREE (qpic_res_t, res->qpic_map, qp); + PR_RESFREE (res->qpic_map, qp); } static void qpic_reset (draw_resources_t *res) { - PR_RESRESET (qpic_res_t, res->qpic_map); + PR_RESRESET (res->qpic_map); } static inline qpic_res_t * qpic_get (draw_resources_t *res, int index) { - PR_RESGET (res->qpic_map, index); + return PR_RESGET (res->qpic_map, index); } -static inline int +static inline int __attribute__((pure)) qpic_index (draw_resources_t *res, qpic_res_t *qp) { - PR_RESINDEX (res->qpic_map, qp); + return PR_RESINDEX (res->qpic_map, qp); } -static qpic_res_t * -get_qpic (progs_t *pr, const char *name, int qpic_handle) +static qpic_res_t * __attribute__((pure)) +get_qpic (progs_t *pr, draw_resources_t *res, const char *name, int qpic_handle) { - draw_resources_t *res = PR_Resources_Find (pr, "Draw"); qpic_res_t *qp = qpic_get (res, qpic_handle); if (!qp) @@ -118,21 +127,62 @@ get_qpic (progs_t *pr, const char *name, int qpic_handle) return qp; } -static void -bi_Draw_FreePic (progs_t *pr) +static bi_charbuff_t * +charbuff_new (draw_resources_t *res) { - draw_resources_t *res = PR_Resources_Find (pr, "Draw"); + return PR_RESNEW (res->charbuff_map); +} + +static void +charbuff_free (draw_resources_t *res, bi_charbuff_t *cbuff) +{ + Draw_DestroyBuffer (cbuff->buffer); + PR_RESFREE (res->charbuff_map, cbuff); +} + +static void +charbuff_reset (draw_resources_t *res) +{ + PR_RESRESET (res->charbuff_map); +} + +static inline bi_charbuff_t * +charbuff_get (draw_resources_t *res, int index) +{ + return PR_RESGET (res->charbuff_map, index); +} + +static inline int __attribute__((pure)) +charbuff_index (draw_resources_t *res, bi_charbuff_t *qp) +{ + return PR_RESINDEX (res->charbuff_map, qp); +} + +static bi_charbuff_t * __attribute__((pure)) +get_charbuff (progs_t *pr, draw_resources_t *res, const char *name, int charbuff_handle) +{ + bi_charbuff_t *cbuff = charbuff_get (res, charbuff_handle); + + if (!cbuff) + PR_RunError (pr, "invalid charbuff handle passed to %s", name + 3); + return cbuff; +} + +static void +bi_Draw_FreePic (progs_t *pr, void *_res) +{ + draw_resources_t *res = _res; bi_qpic_t *bq = &P_STRUCT (pr, bi_qpic_t, 0); - qpic_res_t *qp = get_qpic (pr, __FUNCTION__, bq->pic_handle); + qpic_res_t *qp = get_qpic (pr, res, __FUNCTION__, bq->pic_handle); PR_Zone_Free (pr, qp->bq); qpic_free (res, qp); } static void -bi_Draw_MakePic (progs_t *pr) +bi_Draw_MakePic (progs_t *pr, void *_res) { - draw_resources_t *res = PR_Resources_Find (pr, "Draw"); + draw_resources_t *res = _res; int width = P_INT (pr, 0); int height = P_INT (pr, 1); byte *data = (byte *) P_GSTRING (pr, 2); @@ -144,7 +194,7 @@ bi_Draw_MakePic (progs_t *pr) qp = qpic_new (res); qp->name = 0; qp->pic = pic; - qp->cached = 1; + qp->cached = 0; bq = PR_Zone_Malloc (pr, sizeof (bi_qpic_t)); bq->width = pic->width; bq->height = pic->height; @@ -154,9 +204,9 @@ bi_Draw_MakePic (progs_t *pr) } static void -bi_Draw_CachePic (progs_t *pr) +bi_Draw_CachePic (progs_t *pr, void *_res) { - draw_resources_t *res = PR_Resources_Find (pr, "Draw"); + draw_resources_t *res = _res; const char *name = P_GSTRING (pr, 0); int alpha = P_INT (pr, 1); qpic_t *pic; @@ -183,36 +233,39 @@ bi_Draw_CachePic (progs_t *pr) } static void -bi_Draw_Pic (progs_t *pr) +bi_Draw_Pic (progs_t *pr, void *_res) { + draw_resources_t *res = _res; int x = P_INT (pr, 0); int y = P_INT (pr, 1); bi_qpic_t *bq = &P_STRUCT (pr, bi_qpic_t, 2); - qpic_res_t *qp = get_qpic (pr, __FUNCTION__, bq->pic_handle); + qpic_res_t *qp = get_qpic (pr, res, __FUNCTION__, bq->pic_handle); qpic_t *pic = qp->pic; r_funcs->Draw_Pic (x, y, pic); } static void -bi_Draw_Picf (progs_t *pr) +bi_Draw_Picf (progs_t *pr, void *_res) { + draw_resources_t *res = _res; float x = P_FLOAT (pr, 0); float y = P_FLOAT (pr, 1); bi_qpic_t *bq = &P_STRUCT (pr, bi_qpic_t, 2); - qpic_res_t *qp = get_qpic (pr, __FUNCTION__, bq->pic_handle); + qpic_res_t *qp = get_qpic (pr, res, __FUNCTION__, bq->pic_handle); qpic_t *pic = qp->pic; r_funcs->Draw_Picf (x, y, pic); } static void -bi_Draw_SubPic (progs_t *pr) +bi_Draw_SubPic (progs_t *pr, void *_res) { + draw_resources_t *res = _res; int x = P_INT (pr, 0); int y = P_INT (pr, 1); bi_qpic_t *bq = &P_STRUCT (pr, bi_qpic_t, 2); - qpic_res_t *qp = get_qpic (pr, __FUNCTION__, bq->pic_handle); + qpic_res_t *qp = get_qpic (pr, res, __FUNCTION__, bq->pic_handle); qpic_t *pic = qp->pic; int srcx = P_INT (pr, 3); int srcy = P_INT (pr, 4); @@ -223,19 +276,20 @@ bi_Draw_SubPic (progs_t *pr) } static void -bi_Draw_CenterPic (progs_t *pr) +bi_Draw_CenterPic (progs_t *pr, void *_res) { + draw_resources_t *res = _res; int x = P_INT (pr, 0); int y = P_INT (pr, 1); bi_qpic_t *bq = &P_STRUCT (pr, bi_qpic_t, 2); - qpic_res_t *qp = get_qpic (pr, __FUNCTION__, bq->pic_handle); + qpic_res_t *qp = get_qpic (pr, res, __FUNCTION__, bq->pic_handle); qpic_t *pic = qp->pic; r_funcs->Draw_Pic (x - pic->width / 2, y, pic); } static void -bi_Draw_Character (progs_t *pr) +bi_Draw_Character (progs_t *pr, void *_res) { int x = P_INT (pr, 0); int y = P_INT (pr, 1); @@ -245,7 +299,7 @@ bi_Draw_Character (progs_t *pr) } static void -bi_Draw_String (progs_t *pr) +bi_Draw_String (progs_t *pr, void *_res) { int x = P_INT (pr, 0); int y = P_INT (pr, 1); @@ -255,7 +309,7 @@ bi_Draw_String (progs_t *pr) } static void -bi_Draw_nString (progs_t *pr) +bi_Draw_nString (progs_t *pr, void *_res) { int x = P_INT (pr, 0); int y = P_INT (pr, 1); @@ -266,7 +320,7 @@ bi_Draw_nString (progs_t *pr) } static void -bi_Draw_AltString (progs_t *pr) +bi_Draw_AltString (progs_t *pr, void *_res) { int x = P_INT (pr, 0); int y = P_INT (pr, 1); @@ -282,7 +336,7 @@ bi_Draw_AltString (progs_t *pr) (only a wrapper function to original qf-api) */ static void -bi_Draw_Fill (progs_t *pr) +bi_Draw_Fill (progs_t *pr, void *_res) { int x = P_INT (pr, 0); int y = P_INT (pr, 1); @@ -294,7 +348,19 @@ bi_Draw_Fill (progs_t *pr) } static void -bi_Draw_Crosshair (progs_t *pr) +bi_Draw_Line (progs_t *pr, void *_res) +{ + int x0 = P_INT (pr, 0); + int y0 = P_INT (pr, 1); + int x1 = P_INT (pr, 2); + int y1 = P_INT (pr, 3); + int color = P_INT (pr, 4); + + r_funcs->Draw_Line (x0, y0, x1, y1, color); +} + +static void +bi_Draw_Crosshair (progs_t *pr, void *_res) { int ch = P_INT (pr, 0); int x = P_INT (pr, 1); @@ -303,6 +369,95 @@ bi_Draw_Crosshair (progs_t *pr) r_funcs->Draw_CrosshairAt (ch, x, y); } +static void +bi_Draw_Width (progs_t *pr, void *_res) +{ + R_INT (pr) = r_data->vid->width; +} + +static void +bi_Draw_Height (progs_t *pr, void *_res) +{ + R_INT (pr) = r_data->vid->height; +} + +static void +bi_Draw_CreateBuffer (progs_t *pr, void *_res) +{ + draw_resources_t *res = _res; + int width = P_INT (pr, 0); + int height = P_INT (pr, 1); + draw_charbuffer_t *buffer; + bi_charbuff_t *cbuff; + + buffer = Draw_CreateBuffer (width, height); + cbuff = charbuff_new (res); + cbuff->next = res->charbuffs; + res->charbuffs = cbuff; + cbuff->buffer = buffer; + R_INT (pr) = charbuff_index (res, cbuff); +} + +static void +bi_Draw_DestroyBuffer (progs_t *pr, void *_res) +{ + draw_resources_t *res = _res; + pr_ptr_t cbuff_handle = P_POINTER (pr, 0); + bi_charbuff_t *cbuff = get_charbuff (pr, res, __FUNCTION__, cbuff_handle); + + charbuff_free (res, cbuff); +} + +static void +bi_Draw_ClearBuffer (progs_t *pr, void *_res) +{ + draw_resources_t *res = _res; + pr_ptr_t cbuff_handle = P_POINTER (pr, 0); + bi_charbuff_t *cbuff = get_charbuff (pr, res, __FUNCTION__, cbuff_handle); + + Draw_ClearBuffer (cbuff->buffer); +} + +static void +bi_Draw_ScrollBuffer (progs_t *pr, void *_res) +{ + draw_resources_t *res = _res; + pr_ptr_t cbuff_handle = P_POINTER (pr, 0); + bi_charbuff_t *cbuff = get_charbuff (pr, res, __FUNCTION__, cbuff_handle); + int lines = P_INT (pr, 1); + + Draw_ScrollBuffer (cbuff->buffer, lines); +} + +static void +bi_Draw_CharBuffer (progs_t *pr, void *_res) +{ + draw_resources_t *res = _res; + int x = P_INT (pr, 0); + int y = P_INT (pr, 1); + pr_ptr_t cbuff_handle = P_POINTER (pr, 2); + bi_charbuff_t *cbuff = get_charbuff (pr, res, __FUNCTION__, cbuff_handle); + + Draw_CharBuffer (x, y, cbuff->buffer); +} + +static void +bi_Draw_PrintBuffer (progs_t *pr, void *_res) +{ + draw_resources_t *res = _res; + pr_ptr_t cbuff_handle = P_POINTER (pr, 0); + bi_charbuff_t *cbuff = get_charbuff (pr, res, __FUNCTION__, cbuff_handle); + const char *str = P_GSTRING (pr, 1); + + Draw_PrintBuffer (cbuff->buffer, str); +} + +static void +bi_Draw_SetScale (progs_t *pr, void *_res) +{ + Draw_SetScale (P_INT (pr, 0)); +} + static const char * bi_draw_get_key (const void *p, void *unused) { @@ -310,34 +465,69 @@ bi_draw_get_key (const void *p, void *unused) } static void -bi_draw_clear (progs_t *pr, void *data) +bi_draw_clear (progs_t *pr, void *_res) { - draw_resources_t *res = (draw_resources_t *) data; + draw_resources_t *res = (draw_resources_t *) _res; qpic_res_t *qp; + bi_charbuff_t *cbuff; for (qp = res->qpics; qp; qp = qp->next) { bi_draw_free_qpic (qp); } res->qpics = 0; + for (cbuff = res->charbuffs; cbuff; cbuff = cbuff->next) { + Draw_DestroyBuffer (cbuff->buffer); + } + res->charbuffs = 0; + qpic_reset (res); + charbuff_reset (res); Hash_FlushTable (res->pic_hash); } +static void +bi_draw_destroy (progs_t *pr, void *_res) +{ + draw_resources_t *res = (draw_resources_t *) _res; + Hash_DelTable (res->pic_hash); + PR_RESDELMAP (res->qpic_map); + PR_RESDELMAP (res->charbuff_map); + + free (res); +} + +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) +#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } static builtin_t builtins[] = { - {"Draw_FreePic", bi_Draw_FreePic, -1}, - {"Draw_MakePic", bi_Draw_MakePic, -1}, - {"Draw_CachePic", bi_Draw_CachePic, -1}, - {"Draw_Pic", bi_Draw_Pic, -1}, - {"Draw_Picf", bi_Draw_Picf, -1}, - {"Draw_SubPic", bi_Draw_SubPic, -1}, - {"Draw_CenterPic", bi_Draw_CenterPic, -1}, - {"Draw_Character", bi_Draw_Character, -1}, - {"Draw_String", bi_Draw_String, -1}, - {"Draw_nString", bi_Draw_nString, -1}, - {"Draw_AltString", bi_Draw_AltString, -1}, - {"Draw_Fill", bi_Draw_Fill, -1}, - {"Draw_Crosshair", bi_Draw_Crosshair, -1}, + bi(Draw_Width, 0), + bi(Draw_Height, 0), + bi(Draw_FreePic, 1, p(ptr)), + bi(Draw_MakePic, 3, p(int), p(int), p(string)), + bi(Draw_CachePic, 2, p(string), p(int)), + bi(Draw_Pic, 3, p(int), p(int), p(ptr)), + bi(Draw_Picf, 3, p(float), p(float), p(ptr)), + bi(Draw_SubPic, 7, p(int), p(int), p(ptr), + p(int), p(int), p(int), p(int)), + bi(Draw_CenterPic, 3, p(int), p(int), p(ptr)), + bi(Draw_Character, 3, p(int), p(int), p(int)), + bi(Draw_String, 3, p(int), p(int), p(string)), + bi(Draw_nString, 4, p(int), p(int), p(string), p(int)), + bi(Draw_AltString, 3, p(int), p(int), p(string)), + bi(Draw_Fill, 5, p(int), p(int), p(int), p(int), p(int)), + bi(Draw_Line, 5, p(int), p(int), p(int), p(int), p(int)), + bi(Draw_Crosshair, 5, p(int), p(int), p(int), p(int)), + + bi(Draw_CreateBuffer, 2, p(int), p(int)), + bi(Draw_DestroyBuffer, 1, p(ptr)), + bi(Draw_ClearBuffer, 1, p(ptr)), + bi(Draw_ScrollBuffer, 2, p(ptr), p(int)), + bi(Draw_CharBuffer, 3, p(int), p(int), p(ptr)), + bi(Draw_PrintBuffer, 2, p(ptr), p(string)), + + bi(Draw_SetScale, 1, p(int)), + {0} }; @@ -345,8 +535,8 @@ void R_Progs_Init (progs_t *pr) { draw_resources_t *res = calloc (1, sizeof (draw_resources_t)); - res->pic_hash = Hash_NewTable (61, bi_draw_get_key, 0, 0); + res->pic_hash = Hash_NewTable (61, bi_draw_get_key, 0, 0, pr->hashctx); - PR_Resources_Register (pr, "Draw", res, bi_draw_clear); - PR_RegisterBuiltins (pr, builtins); + PR_Resources_Register (pr, "Draw", res, bi_draw_clear, bi_draw_destroy); + PR_RegisterBuiltins (pr, builtins, res); } diff --git a/libs/video/renderer/r_scrap.c b/libs/video/renderer/r_scrap.c new file mode 100644 index 000000000..3c34f3670 --- /dev/null +++ b/libs/video/renderer/r_scrap.c @@ -0,0 +1,203 @@ +/* + r_scrap.c + + Renderer agnostic scrap management + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/1/12 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/sys.h" + +#include "QF/ui/vrect.h" + +#include "compat.h" +#include "r_scrap.h" + +static unsigned +pow2rup (unsigned x) +{ + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + x++; + return x; +} + +VISIBLE void +R_ScrapInit (rscrap_t *scrap, int width, int height) +{ + width = pow2rup (width); + height = pow2rup (height); + scrap->width = width; + scrap->height = height; + scrap->rects = 0; + scrap->free_rects = VRect_New (0, 0, width, height); +} + +VISIBLE void +R_ScrapDelete (rscrap_t *scrap) +{ + if (!scrap) { + return; + } + R_ScrapClear (scrap); + VRect_Delete (scrap->free_rects); +} + +VISIBLE vrect_t * +R_ScrapAlloc (rscrap_t *scrap, int width, int height) +{ + vrect_t **t, **best; + vrect_t *old, *frags, *rect; + + best = 0; + for (t = &scrap->free_rects; *t; t = &(*t)->next) { + if ((*t)->width < width || (*t)->height < height) + continue; // won't fit + if (!best) { + best = t; + continue; + } + if ((*t)->width == width && (*t)->height == height) { + // exact fit + best = t; + break; + } + if ((*best)->height == height) { + if ((*t)->height == height && (*t)->width < (*best)->width) { + best = t; + } + } else if ((*best)->width == width) { + if ((*t)->width == width && (*t)->height < (*best)->height) { + best = t; + } + } + if ((*t)->width <= (*best)->width || (*t)->height <= (*best)->height) { + best = t; + } + } + if (!best) + return 0; // couldn't find a spot + old = *best; + *best = old->next; + rect = VRect_SubRect (old, width, height); + frags = rect->next; + VRect_Delete (old); + if (frags) { + // old was bigger than the requested size + for (old = frags; old->next; old = old->next) + ; + old->next = scrap->free_rects; + scrap->free_rects = frags; + } + rect->next = scrap->rects; + scrap->rects = rect; + + return rect; +} + +VISIBLE void +R_ScrapFree (rscrap_t *scrap, vrect_t *rect) +{ + vrect_t *old, *merge; + vrect_t **t; + + for (t = &scrap->rects; *t; t = &(*t)->next) + if (*t == rect) + break; + if (*t != rect) + Sys_Error ("R_ScrapFree: broken rect"); + *t = rect->next; + + do { + merge = 0; + for (t = &scrap->free_rects; *t; t = &(*t)->next) { + merge = VRect_Merge (*t, rect); + if (merge) { + old = *t; + *t = (*t)->next; + VRect_Delete (old); + VRect_Delete (rect); + rect = merge; + break; + } + } + } while (merge); + rect->next = scrap->free_rects; + scrap->free_rects = rect; +} + +VISIBLE void +R_ScrapClear (rscrap_t *scrap) +{ + vrect_t *t; + + while (scrap->free_rects) { + t = scrap->free_rects; + scrap->free_rects = t->next; + VRect_Delete (t); + } + while (scrap->rects) { + t = scrap->rects; + scrap->rects = t->next; + VRect_Delete (t); + } + + scrap->free_rects = VRect_New (0, 0, scrap->width, scrap->height); +} + +VISIBLE size_t +R_ScrapArea (rscrap_t *scrap, int *count) +{ + vrect_t *rect; + size_t area; + int c = 0; + + for (rect = scrap->free_rects, area = 0; rect; rect = rect->next) { + area += rect->width * rect->height; + c++; + } + if (count) { + *count = c; + } + return area; +} + +VISIBLE void +R_ScrapDump (rscrap_t *scrap) +{ + vrect_t *rect; + + for (rect = scrap->rects; rect; rect = rect->next) { + Sys_Printf ("%d %d %d %d\n", rect->x, rect->y, + rect->width, rect->height); + } +} diff --git a/libs/video/renderer/r_screen.c b/libs/video/renderer/r_screen.c index a6d129674..caf91dfd1 100644 --- a/libs/video/renderer/r_screen.c +++ b/libs/video/renderer/r_screen.c @@ -38,88 +38,90 @@ #include "QF/cmd.h" #include "QF/cvar.h" #include "QF/draw.h" +#include "QF/dstring.h" #include "QF/image.h" +#include "QF/png.h" #include "QF/pcx.h" +#include "QF/quakefs.h" #include "QF/render.h" #include "QF/screen.h" #include "QF/sys.h" -#include "QF/view.h" +#include "QF/va.h" + +#include "QF/scene/entity.h" +#include "QF/scene/scene.h" +#include "QF/scene/transform.h" +#include "QF/ui/view.h" #include "compat.h" #include "r_internal.h" -#include "sbar.h" - -/* - background clear - rendering - turtle/net/ram icons - sbar - centerprint / slow centerprint - notify lines - intermission / finale overlay - loading plaque - console - menu - - required background clears - required update regions - - syncronous draw mode or async - One off screen buffer, with updates either copied or xblited - Need to double buffer? - - async draw will require the refresh area to be cleared, because it will be - xblited, but sync draw can just ignore it. - - sync - draw - - CenterPrint () - SlowPrint () - Screen_Update (); - Sys_Printf (); - - net - turn off messages option - - the refresh is always rendered, unless the console is full screen - - console is: - notify lines - half - full -*/ // only the refresh window will be updated unless these variables are flagged int scr_copytop; -byte *draw_chars; // 8*8 graphic characters FIXME location +byte *draw_chars; // 8*8 graphic characters FIXME location +bool r_cache_thrash; // set if surface cache is thrashing +int *r_node_visframes; //FIXME per renderer +int *r_leaf_visframes; //FIXME per renderer +int *r_face_visframes; //FIXME per renderer -float oldfov; -int oldsbar; +bool scr_skipupdate; +static bool scr_initialized;// ready to draw -qboolean scr_initialized; // ready to draw +static framebuffer_t *fisheye_cube_map; +static framebuffer_t *warp_buffer; -qpic_t *scr_ram; -qpic_t *scr_turtle; +static float fov_x, fov_y; +static float tan_fov_x, tan_fov_y; +static scene_t *scr_scene;//FIXME don't want this here -int clearconsole; +static mat4f_t box_rotations[] = { + [BOX_FRONT] = { + { 1, 0, 0, 0}, + { 0, 1, 0, 0}, + { 0, 0, 1, 0}, + { 0, 0, 0, 1} + }, + [BOX_RIGHT] = { + { 0,-1, 0, 0}, + { 1, 0, 0, 0}, + { 0, 0, 1, 0}, + { 0, 0, 0, 1} + }, + [BOX_BEHIND] = { + {-1, 0, 0, 0}, + { 0,-1, 0, 0}, + { 0, 0, 1, 0}, + { 0, 0, 0, 1} + }, + [BOX_LEFT] = { + { 0, 1, 0, 0}, + {-1, 0, 0, 0}, + { 0, 0, 1, 0}, + { 0, 0, 0, 1} + }, + [BOX_TOP] = { + { 0, 0, 1, 0}, + { 0, 1, 0, 0}, + {-1, 0, 0, 0}, + { 0, 0, 0, 1} + }, + [BOX_BOTTOM] = { + { 0, 0,-1, 0}, + { 0, 1, 0, 0}, + { 1, 0, 0, 0}, + { 0, 0, 0, 1} + }, +}; -viddef_t vid; // global video state - -vrect_t *pconupdate; -vrect_t scr_vrect; - -qboolean scr_skipupdate; - -void -R_SetVrect (vrect_t *vrectin, vrect_t *vrect, int lineadj) +static void +set_vrect (const vrect_t *vrectin, vrect_t *vrect, int lineadj) { float size; int h; - // intermission is always full screen size = min (r_viewsize, 100); - if (vr_data.force_fullscreen) { + if (r_data->force_fullscreen) { + // intermission is always full screen size = 100.0; lineadj = 0; } @@ -143,56 +145,265 @@ R_SetVrect (vrect_t *vrectin, vrect_t *vrect, int lineadj) vrect->y = (h - vrect->height) / 2; } +void//FIXME remove when sw warp is cleaned up +R_SetVrect (const vrect_t *vrectin, vrect_t *vrect, int lineadj) +{ + set_vrect (vrectin, vrect, lineadj); +} + void +SCR_SetFOV (float fov) +{ + + refdef_t *refdef = r_data->refdef; + + double f = fov * M_PI / 360; + double c = cos(f); + double s = sin(f); + double w = refdef->vrect.width; + double h = refdef->vrect.height; + + fov_x = fov; + fov_y = 360 * atan2 (s * h, c * w) / M_PI; + tan_fov_x = s / c; + tan_fov_y = (s * h) / (c * w); + r_funcs->set_fov (tan_fov_x, tan_fov_y); +} + +static void SCR_CalcRefdef (void) { + r_data->vid->recalc_refdef = 0; + + // force a background redraw + r_data->scr_fullupdate = 0; + if (r_funcs->bind_framebuffer) { + r_funcs->bind_framebuffer (0); + } +} + +static void +render_scene (void) +{ + EntQueue_Clear (r_ent_queue); + r_funcs->render_view (); + r_funcs->draw_particles (&r_psystem); + r_funcs->draw_transparent (); +} + +static void +render_side (int side) +{ + mat4f_t camera; + mat4f_t camera_inverse; + mat4f_t rotinv; + + memcpy (camera, r_refdef.camera, sizeof (camera)); + memcpy (camera_inverse, r_refdef.camera_inverse, sizeof (camera_inverse)); + mmulf (r_refdef.camera, camera, box_rotations[side]); + mat4ftranspose (rotinv, box_rotations[side]); + mmulf (r_refdef.camera_inverse, rotinv, camera_inverse); + + //FIXME see fixme in r_screen.c + r_refdef.frame.mat[0] = -r_refdef.camera[1]; + r_refdef.frame.mat[1] = r_refdef.camera[0]; + r_refdef.frame.mat[2] = r_refdef.camera[2]; + r_refdef.frame.mat[3] = r_refdef.camera[3]; + + refdef_t *refdef = r_data->refdef; + R_SetFrustum (refdef->frustum, &refdef->frame, 90, 90); + + r_funcs->bind_framebuffer (&fisheye_cube_map[side]); + render_scene (); + + memcpy (r_refdef.camera, camera, sizeof (camera)); + memcpy (r_refdef.camera_inverse, camera_inverse, sizeof (camera_inverse)); +} + +void +SCR_UpdateScreen_legacy (transform_t camera, double realtime, + SCR_Func *scr_funcs) +{ + if (scr_fisheye && !fisheye_cube_map) { + fisheye_cube_map = r_funcs->create_cube_map (r_data->vid->height); + } + if (r_dowarp && !warp_buffer) { + warp_buffer = r_funcs->create_frame_buffer (r_data->vid->width, + r_data->vid->height); + } + + r_funcs->begin_frame (); + if (r_dowarp) { + r_funcs->bind_framebuffer (warp_buffer); + } + if (scr_fisheye && fisheye_cube_map) { + int side = fisheye_cube_map->width; + vrect_t feye = { 0, 0, side, side }; + r_funcs->set_viewport (&feye); + r_funcs->set_fov (1, 1); //FIXME shouldn't be every frame (2d stuff) + switch (scr_fviews) { + case 6: render_side (BOX_BEHIND); + case 5: render_side (BOX_BOTTOM); + case 4: render_side (BOX_TOP); + case 3: render_side (BOX_LEFT); + case 2: render_side (BOX_RIGHT); + default:render_side (BOX_FRONT); + } + r_funcs->bind_framebuffer (0); + r_funcs->set_viewport (&r_refdef.vrect); + r_funcs->post_process (fisheye_cube_map); + } else { + r_funcs->set_viewport (&r_refdef.vrect); + render_scene (); + if (r_dowarp) { + r_funcs->bind_framebuffer (0); + r_funcs->post_process (warp_buffer); + } + } + r_funcs->set_2d (0); + //view_draw (r_data->scr_view); + r_funcs->set_2d (1); + while (*scr_funcs) { + (*scr_funcs) (); + scr_funcs++; + } + r_funcs->end_frame (); +} + +void +SCR_UpdateScreen (transform_t camera, double realtime, SCR_Func *scr_funcs) +{ + R_RunParticles (r_data->frametime); + + if (scr_skipupdate || !scr_initialized) { + return; + } + + if (r_timegraph || r_speeds || r_dspeeds) { + r_time1 = Sys_DoubleTime (); + } + + refdef_t *refdef = r_data->refdef; + if (Transform_Valid (camera)) { + Transform_GetWorldMatrix (camera, refdef->camera); + Transform_GetWorldInverse (camera, refdef->camera_inverse); + } else { + mat4fidentity (refdef->camera); + mat4fidentity (refdef->camera_inverse); + } + + // FIXME pre-rotate the camera 90 degrees about the z axis such that the + // camera forward vector (camera Y) points along the world +X axis and the + // camera right vector (camera X) points along the world -Y axis. This + // should not be necessary here but is due to AngleVectors (and thus + // AngleQuat for compatibility) treating X as forward and Y as left (or -Y + // as right). Fixing this would take an audit of the usage of both, but is + // probably worthwhile in the long run. + refdef->frame.mat[0] = -refdef->camera[1]; + refdef->frame.mat[1] = refdef->camera[0]; + refdef->frame.mat[2] = refdef->camera[2]; + refdef->frame.mat[3] = refdef->camera[3]; + R_SetFrustum (refdef->frustum, &refdef->frame, fov_x, fov_y); + + r_data->realtime = realtime; + scr_copytop = r_data->scr_copyeverything = 0; + + if (r_data->vid->recalc_refdef) { + SCR_CalcRefdef (); + } + + R_AnimateLight (); + if (scr_scene && scr_scene->worldmodel) { + scr_scene->viewleaf = 0; + vec4f_t position = refdef->frame.position; + scr_scene->viewleaf = Mod_PointInLeaf (position, scr_scene->worldmodel); + r_dowarpold = r_dowarp; + if (r_waterwarp) { + r_dowarp = scr_scene->viewleaf->contents <= CONTENTS_WATER; + } + R_MarkLeaves (scr_scene->viewleaf, r_node_visframes, r_leaf_visframes, + r_face_visframes); + } + r_framecount++; + R_PushDlights (vec3_origin); + r_funcs->UpdateScreen (camera, realtime, scr_funcs); +} + +static void +update_vrect (void) +{ + r_data->vid->recalc_refdef = 1; + vrect_t vrect; refdef_t *refdef = r_data->refdef; - // force a background redraw - vr_data.scr_fullupdate = 0; - vid.recalc_refdef = 0; - - // bound field of view - Cvar_SetValue (scr_fov, bound (1, scr_fov->value, 170)); - vrect.x = 0; vrect.y = 0; - vrect.width = vid.width; - vrect.height = vid.height; + vrect.width = r_data->vid->width; + vrect.height = r_data->vid->height; - R_SetVrect (&vrect, &scr_vrect, vr_data.lineadj); - - refdef->vrect = scr_vrect; - refdef->fov_x = scr_fov->value; - refdef->fov_y = - CalcFov (refdef->fov_x, refdef->vrect.width, refdef->vrect.height); - - // notify the refresh of the change - vr_funcs->R_ViewChanged (vid.aspect); + set_vrect (&vrect, &refdef->vrect, r_data->lineadj); + SCR_SetFOV (scr_fov); } -float -CalcFov (float fov_x, float width, float height) +void +SCR_SetFullscreen (bool fullscreen) { - float a, x; + if (r_data->force_fullscreen == fullscreen) { + return; + } - if (fov_x < 1 || fov_x > 179) - Sys_Error ("Bad fov: %f", fov_x); + r_data->force_fullscreen = fullscreen; + update_vrect (); +} - x = width / tan (fov_x * (M_PI / 360)); +void +SCR_SetBottomMargin (int lines) +{ + r_data->lineadj = lines; + update_vrect (); +} - a = (x == 0) ? 90 : atan (height / x); // 0 shouldn't happen +typedef struct scr_capture_s { + dstring_t *name; + QFile *file; +} scr_capture_t; - a = a * (360 / M_PI); +static void +scr_write_caputre (tex_t *tex, void *data) +{ + scr_capture_t *cap = data; - return a; + if (tex) { + WritePNG (cap->file, tex); + free (tex); + Sys_Printf ("Wrote %s/%s\n", qfs_userpath, cap->name->str); + } else { + Sys_Printf ("Capture failed\n"); + } + Qclose (cap->file); + dstring_delete (cap->name); + free (cap); } static void ScreenShot_f (void) { - vr_funcs->SCR_ScreenShot_f (); + dstring_t *name = dstring_new (); + QFile *file; + + // find a file name to save it to + if (!(file = QFS_NextFile (name, va (0, "%s/qf", qfs_gamedir->dir.shots), + ".png"))) { + Sys_Printf ("SCR_ScreenShot_f: Couldn't create a PNG file: %s\n", + name->str); + dstring_delete (name); + } else { + scr_capture_t *cap = malloc (sizeof (scr_capture_t)); + cap->file = file; + cap->name = name; + r_funcs->capture_screen (scr_write_caputre, cap); + } } /* @@ -203,9 +414,9 @@ ScreenShot_f (void) static void SCR_SizeUp_f (void) { - if (scr_viewsize->int_val < 120) { - Cvar_SetValue (scr_viewsize, scr_viewsize->int_val + 10); - vid.recalc_refdef = 1; + if (scr_viewsize < 120) { + scr_viewsize = scr_viewsize + 10; + r_data->vid->recalc_refdef = 1; } } @@ -217,154 +428,78 @@ SCR_SizeUp_f (void) static void SCR_SizeDown_f (void) { - Cvar_SetValue (scr_viewsize, scr_viewsize->int_val - 10); - vid.recalc_refdef = 1; + scr_viewsize = scr_viewsize - 10; + r_data->vid->recalc_refdef = 1; } -void -SCR_DrawRam (void) -{ - if (!scr_showram->int_val) - return; - - if (!r_cache_thrash) - return; - - vr_funcs->Draw_Pic (scr_vrect.x + 32, scr_vrect.y, scr_ram); -} - -void -SCR_DrawTurtle (void) -{ - static int count; - - if (!scr_showturtle->int_val) - return; - - if (vr_data.frametime < 0.1) { - count = 0; - return; - } - - count++; - if (count < 3) - return; - - vr_funcs->Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle); -} - -void -SCR_DrawPause (void) -{ - qpic_t *pic; - - if (!scr_showpause->int_val) // turn off for screenshots - return; - - if (!vr_data.paused) - return; - - pic = vr_funcs->Draw_CachePic ("gfx/pause.lmp", true); - vr_funcs->Draw_Pic ((vid.conwidth - pic->width) / 2, - (vid.conheight - 48 - pic->height) / 2, pic); -} - -void -SCR_SetUpToDrawConsole (void) -{ -} - -/* - Find closest color in the palette for named color -*/ -int -MipColor (int r, int g, int b) -{ - float bestdist, dist; - int r1, g1, b1, i; - int best = 0; - static int lastbest; - static int lr = -1, lg = -1, lb = -1; - - if (r == lr && g == lg && b == lb) - return lastbest; - - bestdist = 256 * 256 * 3; - - for (i = 0; i < 256; i++) { - int j; - j = i * 3; - r1 = vid.palette[j] - r; - g1 = vid.palette[j + 1] - g; - b1 = vid.palette[j + 2] - b; - dist = r1 * r1 + g1 * g1 + b1 * b1; - if (dist < bestdist) { - bestdist = dist; - best = i; - } - } - lr = r; - lg = g; - lb = b; - lastbest = best; - return best; -} - -// in draw.c - static void -SCR_DrawCharToSnap (int num, byte *dest, int width) +viewsize_listener (void *data, const cvar_t *cvar) { - byte *source; - int col, row, drawline, x; - - row = num >> 4; - col = num & 15; - source = draw_chars + (row << 10) + (col << 3); - - drawline = 8; - - while (drawline--) { - for (x = 0; x < 8; x++) - if (source[x]) - dest[x] = source[x]; - else - dest[x] = 98; - source += 128; - dest -= width; - } - + update_vrect (); } -void -SCR_DrawStringToSnap (const char *s, tex_t *tex, int x, int y) +static void +vidsize_listener (void *data, const viddef_t *vdef) { - byte *dest; - byte *buf = tex->data; - const unsigned char *p; - int width = tex->width; - - dest = buf + ((y * width) + x); - - p = (const unsigned char *) s; - while (*p) { - SCR_DrawCharToSnap (*p++, dest, width); - dest += 8; + update_vrect (); + if (fisheye_cube_map) { + r_funcs->destroy_frame_buffer (fisheye_cube_map); + fisheye_cube_map = 0; } + if (warp_buffer) { + r_funcs->destroy_frame_buffer (warp_buffer); + warp_buffer = 0; + } + r_funcs->set_fov (tan_fov_x, tan_fov_y); } void SCR_Init (void) { + //r_data->scr_view->xlen = r_data->vid->width; + //r_data->scr_view->ylen = r_data->vid->height; + // register our commands Cmd_AddCommand ("screenshot", ScreenShot_f, "Take a screenshot, " - "saves as qfxxx.pcx in the current directory"); + "saves as qfxxxx.png in the QF directory"); Cmd_AddCommand ("sizeup", SCR_SizeUp_f, "Increases the screen size"); Cmd_AddCommand ("sizedown", SCR_SizeDown_f, "Decreases the screen size"); - scr_ram = vr_funcs->Draw_PicFromWad ("ram"); - scr_turtle = vr_funcs->Draw_PicFromWad ("turtle"); - - vid = *vr_data.vid; // cache scr_initialized = true; + + r_ent_queue = EntQueue_New (mod_num_types); + + cvar_t *var = Cvar_FindVar ("viewsize"); + Cvar_AddListener (var, viewsize_listener, 0); + VID_OnVidResize_AddListener (vidsize_listener, 0); + update_vrect (); +} + +void +SCR_Shutdown (void) +{ + EntQueue_Delete (r_ent_queue); +} + +void +SCR_NewScene (scene_t *scene) +{ + if (scr_scene) { + ECS_RemoveEntities (scr_scene->reg, scene_visibility); + R_ClearEfrags (); + } + scr_scene = scene; + if (scene) { + mod_brush_t *brush = &scr_scene->worldmodel->brush; + int count = brush->numnodes + brush->modleafs + + brush->numsurfaces; + int size = count * sizeof (int); + r_node_visframes = Hunk_AllocName (0, size, "visframes"); + r_leaf_visframes = r_node_visframes + brush->numnodes; + r_face_visframes = r_leaf_visframes + brush->modleafs; + r_funcs->set_fov (tan_fov_x, tan_fov_y); + r_funcs->R_NewScene (scene); + } else { + r_funcs->R_ClearState (); + } } diff --git a/libs/video/renderer/r_sprite.c b/libs/video/renderer/r_sprite.c new file mode 100644 index 000000000..cfe0beb59 --- /dev/null +++ b/libs/video/renderer/r_sprite.c @@ -0,0 +1,74 @@ +/* + r_sprite.c + + Draw Sprite Model + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/sys.h" +#include "QF/scene/entity.h" + +#include "r_internal.h" + +mspriteframe_t * +R_GetSpriteFrame (const msprite_t *sprite, const animation_t *animation) +{ + mspritegroup_t *group; + mspriteframe_t *frame; + int i, numframes, frame_num; + float *intervals, fullinterval, targettime, time; + + frame_num = animation->frame; + + if ((frame_num >= sprite->numframes) || (frame_num < 0)) { + Sys_Printf ("R_DrawSprite: no such frame %d\n", frame_num); + frame_num = 0; + } + + if (sprite->frames[frame_num].type == SPR_SINGLE) { + frame = sprite->frames[frame_num].frame; + } else { + group = sprite->frames[frame_num].group; + intervals = group->intervals; + numframes = group->numframes; + fullinterval = intervals[numframes - 1]; + + time = vr_data.realtime + animation->syncbase; + + // when loading in Mod_LoadSpriteGroup, we guaranteed all interval + // values are positive, so we don't have to worry about division by 0 + targettime = time - ((int) (time / fullinterval)) * fullinterval; + + for (i = 0; i < (numframes - 1); i++) { + if (intervals[i] > targettime) + break; + } + + frame = group->frames[i]; + } + + return frame; +} diff --git a/libs/video/renderer/sw/Makefile.am b/libs/video/renderer/sw/Makefile.am deleted file mode 100644 index 62f64e689..000000000 --- a/libs/video/renderer/sw/Makefile.am +++ /dev/null @@ -1,33 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CFLAGS= @PREFER_PIC@ -CCASFLAGS+= @PREFER_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include - -asm = @swrend_libs@ - -noinst_LTLIBRARIES= libsw.la $(asm) -EXTRA_LTLIBRARIES=libswrend_asm.la - -asm_src= \ - d_copy.S d_draw.S d_parta.S d_polysa.S d_scana.S d_spr8.S d_varsa.S \ - fpua.S surf8.S sw_raclipa.S sw_raliasa.S sw_rdrawa.S sw_redgea.S \ - sw_rvarsa.S transform.S - -sw_src= \ - d_edge.c d_fill.c d_init.c d_modech.c d_part.c d_polyse.c d_scan.c \ - d_sky.c d_sprite.c d_surf.c d_vars.c d_zpoint.c draw.c fpu.c nonintel.c \ - screen.c sw_graph.c sw_raclip.c sw_ralias.c sw_rbsp.c sw_rdraw.c \ - sw_redge.c sw_riqm.c sw_rmain.c sw_rmisc.c sw_rpart.c sw_rsky.c \ - sw_rsprite.c sw_rsurf.c \ - vid_common_sw.c - -libswrend_asm_la_LDFLAGS= @STATIC@ -libswrend_asm_la_SOURCES= $(asm_src) - -libsw_la_LDFLAGS= @STATIC@ -libsw_la_SOURCES= $(sw_src) -libsw_la_LIBADD= $(asm) -libsw_la_DEPENDENCIES= $(asm) - -EXTRA_DIST= $(sw_src) $(asm_src) diff --git a/libs/video/renderer/sw/d_copy.S b/libs/video/renderer/sw/d_copy.S index 24f7ded26..722c64d5f 100644 --- a/libs/video/renderer/sw/d_copy.S +++ b/libs/video/renderer/sw/d_copy.S @@ -178,3 +178,7 @@ LLRowLoop: ret #endif /* USE_INTEL_ASM */ + +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/libs/video/renderer/sw/d_draw.S b/libs/video/renderer/sw/d_draw.S index 15e33d33c..cf97cacaa 100644 --- a/libs/video/renderer/sw/d_draw.S +++ b/libs/video/renderer/sw/d_draw.S @@ -194,7 +194,7 @@ LSpanLoop: movl C(tadjust),%edx movl C(sadjust),%esi - movl C(d_scantable)(,%eax,4),%edi // v * screenwidth + movl C(d_scantable)(,%eax,4),%edi // v * d_rowbytes addl %ecx,%edi movl espan_t_u(%ebx),%ecx addl %ecx,%edi // pdest = &pdestspan[scans->u]; @@ -821,7 +821,7 @@ LFSpanLoop: fildl espan_t_v(%esi) fildl espan_t_u(%esi) movl espan_t_v(%esi),%ecx - movl C(d_pzbuffer),%edi + movl C(d_zbuffer),%edi fmuls C(d_zistepu) fxch %st(1) fmuls C(d_zistepv) @@ -936,7 +936,7 @@ LFNegSpanLoop: fildl espan_t_v(%esi) fildl espan_t_u(%esi) movl espan_t_v(%esi),%ecx - movl C(d_pzbuffer),%edi + movl C(d_zbuffer),%edi fmuls C(d_zistepu) fxch %st(1) fmuls C(d_zistepv) @@ -1046,3 +1046,7 @@ LFDone: ret #endif // USE_INTEL_ASM + +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/libs/video/renderer/sw/d_edge.c b/libs/video/renderer/sw/d_edge.c index f510e2063..ea64dbfc4 100644 --- a/libs/video/renderer/sw/d_edge.c +++ b/libs/video/renderer/sw/d_edge.c @@ -31,6 +31,8 @@ #include "QF/cvar.h" #include "QF/render.h" +#include "QF/scene/entity.h" + #include "d_local.h" #include "r_internal.h" @@ -79,7 +81,7 @@ D_DrawSolidSurface (surf_t *surf, int color) pix = (color << 24) | (color << 16) | (color << 8) | color; for (span = surf->spans; span; span = span->pnext) { - pdest = (byte *) d_viewbuffer + screenwidth * span->v; + pdest = d_viewbuffer + d_rowbytes * span->v; u = span->u; u2 = span->u + span->count - 1; ((byte *) pdest)[u] = pix; @@ -141,6 +143,18 @@ D_CalcGradients (msurface_t *pface) bbextentt = ((pface->extents[1] << 16) >> miplevel) - 1; } +static void +transform_submodel_poly (surf_t *s) +{ + // FIXME: we don't want to do all this for every polygon! + // TODO: store once at start of frame + vec4f_t *transform = SW_COMP(scene_sw_matrix, s->render_id); + vec4f_t local_modelorg = r_refdef.frame.position - transform[3]; + TransformVector ((vec_t*)&local_modelorg, transformed_modelorg);//FIXME + + R_RotateBmodel (transform); // FIXME: don't mess with the + // frustum, make entity passed in +} void D_DrawSurfaces (void) @@ -149,14 +163,12 @@ D_DrawSurfaces (void) msurface_t *pface; surfcache_t *pcurrentcache; vec3_t world_transformed_modelorg; - vec3_t local_modelorg; - currententity = &r_worldentity; TransformVector (modelorg, transformed_modelorg); VectorCopy (transformed_modelorg, world_transformed_modelorg); // TODO: could preset a lot of this at mode set time - if (r_drawflat->int_val) { + if (r_refdef.drawflat) { for (s = &surfaces[1]; s < surface_p; s++) { if (!s->spans) continue; @@ -193,7 +205,7 @@ D_DrawSurfaces (void) d_zistepv = 0; d_ziorigin = -0.9; - D_DrawSolidSurface (s, r_clearcolor->int_val & 0xFF); + D_DrawSolidSurface (s, r_clearcolor & 0xFF); D_DrawZSpans (s->spans); } else if (s->flags & SURF_DRAWTURB) { pface = s->data; @@ -203,16 +215,7 @@ D_DrawSurfaces (void) cachewidth = 64; if (s->insubmodel) { - // FIXME: we don't want to do all this for every polygon! - // TODO: store once at start of frame - currententity = s->entity; // FIXME: make this passed in - // to R_RotateBmodel () - VectorSubtract (r_origin, currententity->origin, - local_modelorg); - TransformVector (local_modelorg, transformed_modelorg); - - R_RotateBmodel (); // FIXME: don't mess with the - // frustum, make entity passed in + transform_submodel_poly (s); } D_CalcGradients (pface); @@ -225,10 +228,9 @@ D_DrawSurfaces (void) // FIXME: we don't want to do this every time! // TODO: speed up - currententity = &r_worldentity; VectorCopy (world_transformed_modelorg, transformed_modelorg); - VectorCopy (base_vpn, vpn); + VectorCopy (base_vfwd, vfwd); VectorCopy (base_vup, vup); VectorCopy (base_vright, vright); VectorCopy (base_modelorg, modelorg); @@ -236,16 +238,7 @@ D_DrawSurfaces (void) } } else { if (s->insubmodel) { - // FIXME: we don't want to do all this for every polygon! - // TODO: store once at start of frame - currententity = s->entity; // FIXME: make this passed in - // to R_RotateBmodel () - VectorSubtract (r_origin, currententity->origin, - local_modelorg); - TransformVector (local_modelorg, transformed_modelorg); - - R_RotateBmodel (); // FIXME: don't mess with the - // frustum, make entity passed in + transform_submodel_poly (s); } pface = s->data; @@ -253,7 +246,7 @@ D_DrawSurfaces (void) * pface->texinfo->mipadjust); // FIXME: make this passed in to D_CacheSurface - pcurrentcache = D_CacheSurface (pface, miplevel); + pcurrentcache = D_CacheSurface (s->render_id, pface, miplevel); cacheblock = (byte *) pcurrentcache->data; cachewidth = pcurrentcache->width; @@ -271,12 +264,11 @@ D_DrawSurfaces (void) VectorCopy (world_transformed_modelorg, transformed_modelorg); - VectorCopy (base_vpn, vpn); + VectorCopy (base_vfwd, vfwd); VectorCopy (base_vup, vup); VectorCopy (base_vright, vright); VectorCopy (base_modelorg, modelorg); R_TransformFrustum (); - currententity = &r_worldentity; } } } diff --git a/libs/video/renderer/sw/d_fill.c b/libs/video/renderer/sw/d_fill.c index be9bfd3ab..d38a3def9 100644 --- a/libs/video/renderer/sw/d_fill.c +++ b/libs/video/renderer/sw/d_fill.c @@ -51,9 +51,9 @@ D_FillRect (vrect_t *rect, int color) rheight += ry; ry = 0; } - if (rx + rwidth > vid.width) + if ((unsigned) (rx + rwidth) > vid.width) rwidth = vid.width - rx; - if (ry + rheight > vid.height) + if ((unsigned) (ry + rheight) > vid.height) rheight = vid.height - rx; if (rwidth < 1 || rheight < 1) diff --git a/libs/video/renderer/sw/d_init.c b/libs/video/renderer/sw/d_init.c index 67e48e71b..52d6db6fa 100644 --- a/libs/video/renderer/sw/d_init.c +++ b/libs/video/renderer/sw/d_init.c @@ -39,15 +39,32 @@ #define NUM_MIPS 4 surfcache_t *d_initial_rover; -qboolean d_roverwrapped; +bool d_roverwrapped; int d_minmip; float d_scalemip[NUM_MIPS - 1]; static float basemip[NUM_MIPS - 1] = { 1.0, 0.5 * 0.8, 0.25 * 0.8 }; +static byte *surfcache; void (*d_drawspans) (espan_t *pspan); +static void +d_vidsize_listener (void *data, const viddef_t *vid) +{ + int cachesize = D_SurfaceCacheForRes (vid->width, vid->height); + + if (surfcache) { + D_FlushCaches (vid->vid_internal->ctx); + free (surfcache); + surfcache = 0; + } + surfcache = calloc (cachesize, 1); + vid->vid_internal->init_buffers (vid->vid_internal->ctx); + D_InitCaches (surfcache, cachesize); + + viddef.recalc_refdef = 1; +} void D_Init (void) @@ -56,17 +73,12 @@ D_Init (void) r_worldpolysbacktofront = false; r_recursiveaffinetriangles = true; - vr_data.vid->surf_cache_size = D_SurfaceCacheForRes; - vr_data.vid->flush_caches = D_FlushCaches; - vr_data.vid->init_caches = D_InitCaches; + viddef_t *vid = vr_data.vid; - VID_InitBuffers (); -} + vid->vid_internal->flush_caches = D_FlushCaches; -void -D_EnableBackBufferAccess (void) -{ - VID_LockBuffer (); + VID_OnVidResize_AddListener (d_vidsize_listener, 0); + d_vidsize_listener (0, vr_data.vid); } void @@ -75,36 +87,22 @@ D_TurnZOn (void) // not needed for software version } -void -D_DisableBackBufferAccess (void) -{ - VID_UnlockBuffer (); -} - void D_SetupFrame (void) { int i; - if (r_dowarp) - d_viewbuffer = r_warpbuffer; - else - d_viewbuffer = vid.buffer; - - if (r_dowarp) - screenwidth = WARP_WIDTH; - else - screenwidth = vid.rowbytes; - d_roverwrapped = false; d_initial_rover = sc_rover; - d_minmip = bound (0, d_mipcap->value, 3); + d_minmip = bound (0, d_mipcap, 3); for (i = 0; i < (NUM_MIPS - 1); i++) - d_scalemip[i] = basemip[i] * d_mipscale->value; + d_scalemip[i] = basemip[i] * d_mipscale; d_drawspans = D_DrawSpans8; + + d_skyoffs = r_skytime * r_skyspeed; } void diff --git a/libs/video/renderer/sw/d_modech.c b/libs/video/renderer/sw/d_modech.c index 1498829e7..69dc349bf 100644 --- a/libs/video/renderer/sw/d_modech.c +++ b/libs/video/renderer/sw/d_modech.c @@ -51,7 +51,7 @@ D_Patch (void) #ifdef USE_INTEL_ASM - static qboolean protectset8 = false; + static bool protectset8 = false; if (!protectset8) { Sys_MakeCodeWriteable ((int) D_PolysetAff8Start, @@ -65,20 +65,10 @@ D_Patch (void) void D_ViewChanged (void) { - int rowpixels; - - if (r_dowarp) - rowpixels = WARP_WIDTH; - else - rowpixels = vid.rowbytes; - scale_for_mip = xscale; if (yscale > xscale) scale_for_mip = yscale; - d_zrowbytes = vid.width * 2; - d_zwidth = vid.width; - d_pix_min = r_refdef.vrect.width / 320; if (d_pix_min < 1) d_pix_min = 1; @@ -99,14 +89,5 @@ D_ViewChanged (void) d_vrectbottom_particle = r_refdef.vrectbottom - (d_pix_max << d_y_aspect_shift); - { - int i; - - for (i = 0; i < vid.height; i++) { - d_scantable[i] = i * rowpixels; - zspantable[i] = d_pzbuffer + i * d_zwidth; - } - } - D_Patch (); } diff --git a/libs/video/renderer/sw/d_part.c b/libs/video/renderer/sw/d_part.c index 38de44a09..f79229b9f 100644 --- a/libs/video/renderer/sw/d_part.c +++ b/libs/video/renderer/sw/d_part.c @@ -47,7 +47,7 @@ D_DrawParticle (particle_t *pparticle) int i, izi, pix, count, u, v; // transform point - VectorSubtract (pparticle->org, r_origin, local); + VectorSubtract (pparticle->pos, r_porigin, local); transformed[0] = DotProduct (local, r_pright); transformed[1] = DotProduct (local, r_pup); @@ -67,7 +67,7 @@ D_DrawParticle (particle_t *pparticle) return; } - pz = d_pzbuffer + (d_zwidth * v) + u; + pz = d_zbuffer + (d_zwidth * v) + u; pdest = d_viewbuffer + d_scantable[v] + u; izi = (int) (zi * 0x8000); @@ -81,81 +81,81 @@ D_DrawParticle (particle_t *pparticle) switch (pix) { case 1: count = 1 << d_y_aspect_shift; - for (; count; count--, pz += d_zwidth, pdest += screenwidth) { + for (; count; count--, pz += d_zwidth, pdest += d_rowbytes) { if (pz[0] <= izi) { pz[0] = izi; - pdest[0] = pparticle->color; + pdest[0] = pparticle->icolor; } } break; case 2: count = 2 << d_y_aspect_shift; - for (; count; count--, pz += d_zwidth, pdest += screenwidth) { + for (; count; count--, pz += d_zwidth, pdest += d_rowbytes) { if (pz[0] <= izi) { pz[0] = izi; - pdest[0] = pparticle->color; + pdest[0] = pparticle->icolor; } if (pz[1] <= izi) { pz[1] = izi; - pdest[1] = pparticle->color; + pdest[1] = pparticle->icolor; } } break; case 3: count = 3 << d_y_aspect_shift; - for (; count; count--, pz += d_zwidth, pdest += screenwidth) { + for (; count; count--, pz += d_zwidth, pdest += d_rowbytes) { if (pz[0] <= izi) { pz[0] = izi; - pdest[0] = pparticle->color; + pdest[0] = pparticle->icolor; } if (pz[1] <= izi) { pz[1] = izi; - pdest[1] = pparticle->color; + pdest[1] = pparticle->icolor; } if (pz[2] <= izi) { pz[2] = izi; - pdest[2] = pparticle->color; + pdest[2] = pparticle->icolor; } } break; case 4: count = 4 << d_y_aspect_shift; - for (; count; count--, pz += d_zwidth, pdest += screenwidth) { + for (; count; count--, pz += d_zwidth, pdest += d_rowbytes) { if (pz[0] <= izi) { pz[0] = izi; - pdest[0] = pparticle->color; + pdest[0] = pparticle->icolor; } if (pz[1] <= izi) { pz[1] = izi; - pdest[1] = pparticle->color; + pdest[1] = pparticle->icolor; } if (pz[2] <= izi) { pz[2] = izi; - pdest[2] = pparticle->color; + pdest[2] = pparticle->icolor; } if (pz[3] <= izi) { pz[3] = izi; - pdest[3] = pparticle->color; + pdest[3] = pparticle->icolor; } } break; default: count = pix << d_y_aspect_shift; - for (; count; count--, pz += d_zwidth, pdest += screenwidth) { + for (; count; count--, pz += d_zwidth, pdest += d_rowbytes) { for (i = 0; i < pix; i++) { if (pz[i] <= izi) { pz[i] = izi; - pdest[i] = pparticle->color; + pdest[i] = pparticle->icolor; } } } diff --git a/libs/video/renderer/sw/d_parta.S b/libs/video/renderer/sw/d_parta.S index fb004638d..ab4b8c969 100644 --- a/libs/video/renderer/sw/d_parta.S +++ b/libs/video/renderer/sw/d_parta.S @@ -66,13 +66,13 @@ C(D_DrawParticle): // FIXME: better FP overlap in general here // transform point -// VectorSubtract (p->org, r_origin, local); - flds C(r_origin) - fsubrs pt_org(%edi) - flds pt_org+4(%edi) - fsubs C(r_origin)+4 - flds pt_org+8(%edi) - fsubs C(r_origin)+8 +// VectorSubtract (p->org, r_porigin, local); + flds C(r_porigin) + fsubrs pt_pos(%edi) + flds pt_pos+4(%edi) + fsubs C(r_porigin)+4 + flds pt_pos+8(%edi) + fsubs C(r_porigin)+8 fxch %st(2) // local[0] | local[1] | local[2] // transformed[2] = DotProduct(local, r_ppn); @@ -189,7 +189,7 @@ C(D_DrawParticle): imull C(d_zrowbytes),%edx // point to the z pixel leal (%edx,%eax,2),%edx - movl C(d_pzbuffer),%eax + movl C(d_zbuffer),%eax fistpl C(izi) @@ -245,7 +245,7 @@ C(DP_1x1): .globl C(DP_2x2) C(DP_2x2): pushl %esi - movl C(screenwidth),%ebx + movl C(d_rowbytes),%ebx movl C(d_zrowbytes),%esi cmpw %bp,(%edx) @@ -276,7 +276,7 @@ L2x2_4: .globl C(DP_3x3) C(DP_3x3): pushl %esi - movl C(screenwidth),%ebx + movl C(d_rowbytes),%ebx movl C(d_zrowbytes),%esi cmpw %bp,(%edx) @@ -335,7 +335,7 @@ L3x3_9: .globl C(DP_4x4) C(DP_4x4): pushl %esi - movl C(screenwidth),%ebx + movl C(d_rowbytes),%ebx movl C(d_zrowbytes),%esi cmpw %bp,(%edx) @@ -438,7 +438,7 @@ LDefault: movb C(d_y_aspect_shift),%cl shll %cl,%ebx -// for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) +// for ( ; count ; count--, pz += d_zwidth, pdest += d_rowbytes) // { // for (i=0 ; i d_pzbuffer[ofs]) { + if (new[5] > d_zbuffer[ofs]) { int pix; - d_pzbuffer[ofs] = new[5]; + d_zbuffer[ofs] = new[5]; pix = skintable[new[3] >> 16][new[2] >> 16]; // pix = ((byte *)acolormap)[pix + (new[4] & 0xFF00)]; d_viewbuffer[ofs] = pix; diff --git a/libs/video/renderer/sw/d_scan.c b/libs/video/renderer/sw/d_scan.c index 2522befbb..7b2a2b1d8 100644 --- a/libs/video/renderer/sw/d_scan.c +++ b/libs/video/renderer/sw/d_scan.c @@ -29,9 +29,11 @@ #endif #include "QF/render.h" +#include "QF/ui/view.h" #include "d_local.h" #include "r_internal.h" +#include "vid_sw.h" byte *r_turb_pbase; byte *r_turb_pdest; @@ -46,14 +48,21 @@ int r_turb_spancount; the sine warp, to keep the edges from wrapping */ void -D_WarpScreen (void) +D_WarpScreen (framebuffer_t *src) { + sw_framebuffer_t *buffer = src->buffer; int w, h; int u, v; + int scr_x = vr_data.refdef->vrect.x; + int scr_y = vr_data.refdef->vrect.y; + int scr_w = vr_data.refdef->vrect.width; + int scr_h = vr_data.refdef->vrect.height; byte *dest; int *turb; int *col; byte **row; + byte *color = buffer->color; + int rowbytes = buffer->rowbytes; /* FIXME: allocate these arrays properly */ byte *rowptr[MAXHEIGHT + AMP2 * 2]; @@ -63,26 +72,26 @@ D_WarpScreen (void) w = r_refdef.vrect.width; h = r_refdef.vrect.height; - wratio = w / (float) scr_vrect.width; - hratio = h / (float) scr_vrect.height; + wratio = w / (float) scr_w; + hratio = h / (float) scr_h; - for (v = 0; v < scr_vrect.height + AMP2 * 2; v++) { - rowptr[v] = d_viewbuffer + (r_refdef.vrect.y * screenwidth) + - (screenwidth * (int) ((float) v * hratio * h / (h + AMP2 * 2))); + for (v = 0; v < scr_h + AMP2 * 2; v++) { + rowptr[v] = color + (0*r_refdef.vrect.y * rowbytes) + + (d_rowbytes * (int) ((float) v * hratio * h / (h + AMP2 * 2))); } - for (u = 0; u < scr_vrect.width + AMP2 * 2; u++) { + for (u = 0; u < scr_w + AMP2 * 2; u++) { column[u] = r_refdef.vrect.x + (int) ((float) u * wratio * w / (w + AMP2 * 2)); } turb = intsintable + ((int) (vr_data.realtime * SPEED) & (CYCLE - 1)); - dest = ((byte*)vid.buffer) + scr_vrect.y * vid.rowbytes + scr_vrect.x; + dest = d_viewbuffer + scr_y * d_rowbytes + scr_x; - for (v = 0; v < scr_vrect.height; v++, dest += vid.rowbytes) { + for (v = 0; v < scr_h; v++, dest += d_rowbytes) { col = &column[turb[v]]; row = &rowptr[v]; - for (u = 0; u < scr_vrect.width; u += 4) { + for (u = 0; u < scr_w; u += 4) { dest[u + 0] = row[turb[u + 0]][col[u + 0]]; dest[u + 1] = row[turb[u + 1]][col[u + 1]]; dest[u + 2] = row[turb[u + 2]][col[u + 2]]; @@ -136,8 +145,7 @@ Turbulent (espan_t *pspan) zi16stepu = d_zistepu * 16; do { - r_turb_pdest = (byte *) d_viewbuffer + (screenwidth * pspan->v) + - pspan->u; + r_turb_pdest = d_viewbuffer + (d_rowbytes * pspan->v) + pspan->u; count = pspan->count; @@ -246,7 +254,7 @@ void D_DrawSpans8 (espan_t *pspan) { int count, spancount; - unsigned char *pbase, *pdest; + byte *pbase, *pdest; fixed16_t s, t, snext, tnext, sstep, tstep; float sdivz, tdivz, zi, z, du, dv, spancountminus1; float sdivz8stepu, tdivz8stepu, zi8stepu; @@ -261,8 +269,7 @@ D_DrawSpans8 (espan_t *pspan) zi8stepu = d_zistepu * 8; do { - pdest = (unsigned char *) ((byte *) d_viewbuffer + - (screenwidth * pspan->v) + pspan->u); + pdest = d_viewbuffer + (d_rowbytes * pspan->v) + pspan->u; count = pspan->count; @@ -384,7 +391,7 @@ D_DrawZSpans (espan_t *pspan) izistep = (int) (d_zistepu * 0x8000 * 0x10000); do { - pdest = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; + pdest = d_zbuffer + (d_zwidth * pspan->v) + pspan->u; count = pspan->count; diff --git a/libs/video/renderer/sw/d_scana.S b/libs/video/renderer/sw/d_scana.S index 4c9a62da4..6ca194e72 100644 --- a/libs/video/renderer/sw/d_scana.S +++ b/libs/video/renderer/sw/d_scana.S @@ -98,3 +98,6 @@ Llp: #endif // USE_INTEL_ASM +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/libs/video/renderer/sw/d_sky.c b/libs/video/renderer/sw/d_sky.c index 6bc7872aa..2671de132 100644 --- a/libs/video/renderer/sw/d_sky.c +++ b/libs/video/renderer/sw/d_sky.c @@ -36,46 +36,49 @@ #define SKY_SPAN_SHIFT 5 #define SKY_SPAN_MAX (1 << SKY_SPAN_SHIFT) +float d_skyoffs; static void D_Sky_uv_To_st (int u, int v, fixed16_t *s, fixed16_t *t) { float wu, wv, temp; vec3_t end; + int half_width = vid.width >> 1; + int half_height = vid.height >> 1; if (r_refdef.vrect.width >= r_refdef.vrect.height) temp = (float) r_refdef.vrect.width; else temp = (float) r_refdef.vrect.height; - wu = 8192.0 * (float) (u - (vid.width >> 1)) / temp; - wv = 8192.0 * (float) ((vid.height >> 1) - v) / temp; + wu = 8192.0 * (float) (u - half_width) / temp; + wv = 8192.0 * (float) (half_height - v) / temp; - end[0] = 4096 * vpn[0] + wu * vright[0] + wv * vup[0]; - end[1] = 4096 * vpn[1] + wu * vright[1] + wv * vup[1]; - end[2] = 4096 * vpn[2] + wu * vright[2] + wv * vup[2]; + end[0] = 4096 * vfwd[0] + wu * vright[0] + wv * vup[0]; + end[1] = 4096 * vfwd[1] + wu * vright[1] + wv * vup[1]; + end[2] = 4096 * vfwd[2] + wu * vright[2] + wv * vup[2]; end[2] *= 3; VectorNormalize (end); - temp = r_skytime * r_skyspeed; // TODO: add D_SetupFrame & set this there - *s = (int) ((temp + 6 * (SKYSIZE / 2 - 1) * end[0]) * 0x10000); - *t = (int) ((temp + 6 * (SKYSIZE / 2 - 1) * end[1]) * 0x10000); + *s = (int) ((d_skyoffs + 6 * (SKYSIZE / 2 - 1) * end[0]) * 0x10000); + *t = (int) ((d_skyoffs + 6 * (SKYSIZE / 2 - 1) * end[1]) * 0x10000); } void D_DrawSkyScans (espan_t *pspan) { int count, spancount, u, v; - unsigned char *pdest; + byte *pdest; fixed16_t s, t, snext, tnext, sstep, tstep; int spancountminus1; sstep = 0; // keep compiler happy tstep = 0; // ditto + snext = 0; // ditto + tnext = 0; // ditto do { - pdest = (unsigned char *) ((byte *) d_viewbuffer + - (screenwidth * pspan->v) + pspan->u); + pdest = d_viewbuffer + (d_rowbytes * pspan->v) + pspan->u; count = pspan->count; diff --git a/libs/video/renderer/sw/d_spr8.S b/libs/video/renderer/sw/d_spr8.S index 5739f7ee1..ebc005d9c 100644 --- a/libs/video/renderer/sw/d_spr8.S +++ b/libs/video/renderer/sw/d_spr8.S @@ -211,7 +211,7 @@ LSpanLoop: movl sspan_t_u(%ebx),%ebp imull C(d_zrowbytes) shll $1,%ebp // a word per pixel - addl C(d_pzbuffer),%eax + addl C(d_zbuffer),%eax addl %ebp,%eax movl %eax,C(pz) @@ -223,7 +223,7 @@ LSpanLoop: pushl %ebx // preserve spans pointer movl C(tadjust),%edx movl C(sadjust),%esi - movl C(d_scantable)(,%eax,4),%edi // v * screenwidth + movl C(d_scantable)(,%eax,4),%edi // v * d_rowbytes addl %ebp,%edi movl sspan_t_u(%ebx),%ebp addl %ebp,%edi // pdest = &pdestspan[scans->u]; @@ -909,3 +909,7 @@ LNextSpan: ret #endif // USE_INTEL_ASM + +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/libs/video/renderer/sw/d_sprite.c b/libs/video/renderer/sw/d_sprite.c index b4f35202f..c39a056a3 100644 --- a/libs/video/renderer/sw/d_sprite.c +++ b/libs/video/renderer/sw/d_sprite.c @@ -35,8 +35,6 @@ static int sprite_height; static int minindex, maxindex; -static sspan_t *sprite_spans; - #ifdef PIC #undef USE_INTEL_ASM //XXX asm pic hack @@ -69,8 +67,8 @@ D_SpriteDrawSpans (sspan_t *pspan) izistep = (int) (d_zistepu * 0x8000 * 0x10000); do { - pdest = (byte *) d_viewbuffer + (screenwidth * pspan->v) + pspan->u; - pz = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; + pdest = d_viewbuffer + (d_rowbytes * pspan->v) + pspan->u; + pz = d_zbuffer + (d_zwidth * pspan->v) + pspan->u; count = pspan->count; @@ -195,15 +193,13 @@ D_SpriteDrawSpans (sspan_t *pspan) #endif static void -D_SpriteScanLeftEdge (void) +D_SpriteScanLeftEdge (sspan_t *pspan) { int i, v, itop, ibottom, lmaxindex; emitpoint_t *pvert, *pnext; - sspan_t *pspan; float du, dv, vtop, vbottom, slope; fixed16_t u, u_step; - pspan = sprite_spans; i = minindex; if (i == 0) i = r_spritedesc.nump; @@ -249,15 +245,13 @@ D_SpriteScanLeftEdge (void) } static void -D_SpriteScanRightEdge (void) +D_SpriteScanRightEdge (sspan_t *pspan) { int i, v, itop, ibottom; emitpoint_t *pvert, *pnext; - sspan_t *pspan; float du, dv, vtop, vbottom, slope, uvert, unext, vvert, vnext; fixed16_t u, u_step; - pspan = sprite_spans; i = minindex; vvert = r_spritedesc.pverts[i].v; @@ -323,17 +317,17 @@ D_SpriteScanRightEdge (void) } static void -D_SpriteCalculateGradients (void) +D_SpriteCalculateGradients (const vec3_t relvieworg) { vec3_t p_normal, p_saxis, p_taxis, p_temp1; float distinv; - TransformVector (r_spritedesc.vpn, p_normal); + TransformVector (r_spritedesc.vfwd, p_normal); TransformVector (r_spritedesc.vright, p_saxis); TransformVector (r_spritedesc.vup, p_taxis); VectorNegate (p_taxis, p_taxis); - distinv = 1.0 / (-DotProduct (modelorg, r_spritedesc.vpn)); + distinv = 1.0 / (-DotProduct (relvieworg, r_spritedesc.vfwd)); d_sdivzstepu = p_saxis[0] * xscaleinv; d_tdivzstepu = p_taxis[0] * xscaleinv; @@ -351,7 +345,7 @@ D_SpriteCalculateGradients (void) d_ziorigin = p_normal[2] * distinv - xcenter * d_zistepu - ycenter * d_zistepv; - TransformVector (modelorg, p_temp1); + TransformVector (relvieworg, p_temp1); sadjust = ((fixed16_t) (DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) - (-(cachewidth >> 1) << 16); @@ -364,15 +358,13 @@ D_SpriteCalculateGradients (void) } void -D_DrawSprite (void) +D_DrawSprite (const vec3_t relvieworg) { int i, nump; float ymin, ymax; emitpoint_t *pverts; sspan_t spans[MAXHEIGHT + 1]; - sprite_spans = spans; - // find the top and bottom vertices, and make sure there's at least one // scan to draw ymin = 999999.9; @@ -409,8 +401,8 @@ D_DrawSprite (void) pverts = r_spritedesc.pverts; pverts[nump] = pverts[0]; - D_SpriteCalculateGradients (); - D_SpriteScanLeftEdge (); - D_SpriteScanRightEdge (); - D_SpriteDrawSpans (sprite_spans); + D_SpriteCalculateGradients (relvieworg); + D_SpriteScanLeftEdge (spans); + D_SpriteScanRightEdge (spans); + D_SpriteDrawSpans (spans); } diff --git a/libs/video/renderer/sw/d_surf.c b/libs/video/renderer/sw/d_surf.c index a2f01f95d..4fb553979 100644 --- a/libs/video/renderer/sw/d_surf.c +++ b/libs/video/renderer/sw/d_surf.c @@ -33,6 +33,7 @@ #include "QF/qargs.h" #include "QF/render.h" #include "QF/sys.h" +#include "QF/scene/entity.h" #include "compat.h" #include "d_local.h" @@ -98,7 +99,7 @@ D_ClearCacheGuard (void) void D_InitCaches (void *buffer, int size) { - Sys_MaskPrintf (SYS_DEV, "D_InitCaches: %ik surface cache\n", size/1024); + Sys_MaskPrintf (SYS_vid, "D_InitCaches: %ik surface cache\n", size/1024); sc_size = size - GUARDSIZE; sc_base = (surfcache_t *) buffer; @@ -108,13 +109,11 @@ D_InitCaches (void *buffer, int size) sc_base->owner = NULL; sc_base->size = sc_size; - d_pzbuffer = vid.zbuffer; - D_ClearCacheGuard (); } void -D_FlushCaches (void) +D_FlushCaches (void *data) { surfcache_t *c; @@ -136,7 +135,7 @@ static surfcache_t * D_SCAlloc (int width, int size) { surfcache_t *new; - qboolean wrapped_this_time; + bool wrapped_this_time; if ((width < 0) || (width > 512)) // FIXME shouldn't really have a max Sys_Error ("D_SCAlloc: bad cache width %d", width); @@ -226,12 +225,13 @@ D_SCDump (void) #endif surfcache_t * -D_CacheSurface (msurface_t *surface, int miplevel) +D_CacheSurface (uint32_t render_id, msurface_t *surface, int miplevel) { surfcache_t *cache; + byte frame = *(byte *) SW_COMP (scene_sw_frame, render_id); // if the surface is animating or flashing, flush the cache - r_drawsurf.texture = R_TextureAnimation (surface); + r_drawsurf.texture = R_TextureAnimation (frame, surface); r_drawsurf.lightadj[0] = d_lightstylevalue[surface->styles[0]]; r_drawsurf.lightadj[1] = d_lightstylevalue[surface->styles[1]]; r_drawsurf.lightadj[2] = d_lightstylevalue[surface->styles[2]]; @@ -282,7 +282,7 @@ D_CacheSurface (msurface_t *surface, int miplevel) r_drawsurf.surf = surface; c_surf++; - R_DrawSurface (); + R_DrawSurface (render_id); return surface->cachespots[miplevel]; } diff --git a/libs/video/renderer/sw/d_vars.c b/libs/video/renderer/sw/d_vars.c index 96a0b0f4b..5864d906b 100644 --- a/libs/video/renderer/sw/d_vars.c +++ b/libs/video/renderer/sw/d_vars.c @@ -53,8 +53,10 @@ fixed16_t sadjust, tadjust, bbextents, bbextentt; byte *cacheblock; int cachewidth; byte *d_viewbuffer; -short *d_pzbuffer; +short *d_zbuffer; +int d_rowbytes; int d_zrowbytes; int d_zwidth; +int d_height; #endif // !USE_INTEL_ASM diff --git a/libs/video/renderer/sw/d_varsa.S b/libs/video/renderer/sw/d_varsa.S index ece5dc2c1..dba80a3e6 100644 --- a/libs/video/renderer/sw/d_varsa.S +++ b/libs/video/renderer/sw/d_varsa.S @@ -80,14 +80,18 @@ C(bbextentt): .long 0 .globl C(cacheblock) .globl C(d_viewbuffer) +.globl C(d_rowbytes) +.globl C(d_height) .globl C(cachewidth) -.globl C(d_pzbuffer) +.globl C(d_zbuffer) .globl C(d_zrowbytes) .globl C(d_zwidth) C(cacheblock): .long 0 C(cachewidth): .long 0 C(d_viewbuffer): .long 0 -C(d_pzbuffer): .long 0 +C(d_rowbytes): .long 0 +C(d_height): .long 0 +C(d_zbuffer): .long 0 C(d_zrowbytes): .long 0 C(d_zwidth): .long 0 @@ -203,3 +207,6 @@ C(spr8entryvec_table): .long 0, C(Spr8Entry2_8), C(Spr8Entry3_8), C(Spr8Entry4_8 #endif // USE_INTEL_ASM +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/libs/video/renderer/sw/d_zpoint.c b/libs/video/renderer/sw/d_zpoint.c index b7ed9e2bf..05f57ad10 100644 --- a/libs/video/renderer/sw/d_zpoint.c +++ b/libs/video/renderer/sw/d_zpoint.c @@ -39,7 +39,7 @@ D_DrawZPoint (void) short *pz; int izi; - pz = d_pzbuffer + (d_zwidth * r_zpointdesc.v) + r_zpointdesc.u; + pz = d_zbuffer + (d_zwidth * r_zpointdesc.v) + r_zpointdesc.u; pdest = d_viewbuffer + d_scantable[r_zpointdesc.v] + r_zpointdesc.u; izi = (int) (r_zpointdesc.zi * 0x8000); diff --git a/libs/video/renderer/sw/draw.c b/libs/video/renderer/sw/draw.c index e3728af66..ef63a04f6 100644 --- a/libs/video/renderer/sw/draw.c +++ b/libs/video/renderer/sw/draw.c @@ -37,18 +37,25 @@ #include -#include "QF/cvar.h" -#include "QF/draw.h" #include "QF/quakefs.h" -#include "QF/sound.h" -#include "QF/sys.h" +#include "QF/ui/font.h" +#include "QF/ui/view.h" #include "d_iface.h" +#include "d_local.h" #include "r_internal.h" #include "vid_internal.h" +typedef struct swfont_s { + font_t *font; +} swfont_t; + +typedef struct swfontset_s + DARRAY_TYPE (swfont_t) swfontset_t; + +static swfontset_t sw_fonts = DARRAY_STATIC_INIT (16); + typedef struct { - vrect_t rect; int width; int height; byte *ptexbytes; @@ -120,7 +127,7 @@ Draw_PicFromWad (const char *name) qpic_t * -Draw_CachePic (const char *path, qboolean alpha) +Draw_CachePic (const char *path, bool alpha) { cachepic_t *pic; int i; @@ -236,13 +243,22 @@ Draw_Init (void) draw_disc = W_GetLumpName ("disc"); draw_backtile = W_GetLumpName ("backtile"); - r_rectdesc.width = draw_backtile->width; - r_rectdesc.height = draw_backtile->height; - r_rectdesc.ptexbytes = draw_backtile->data; - r_rectdesc.rowbytes = draw_backtile->width; + if (!draw_chars) { + qpic_t *pic = Draw_Font8x8Pic (); + draw_chars = pic->data; // FIXME indirect hold on the memory + //FIXME param to Draw_Font8x8Pic + for (int i = 0; i < pic->width * pic->height; i++) { + pic->data[i] = pic->data[i] == 255 ? 0 : pic->data[i]; + } + } + if (draw_backtile) { + r_rectdesc.width = draw_backtile->width; + r_rectdesc.height = draw_backtile->height; + r_rectdesc.ptexbytes = draw_backtile->data; + r_rectdesc.rowbytes = draw_backtile->width; + } } - /* Draw_Character @@ -263,7 +279,7 @@ Draw_Character (int x, int y, unsigned int chr) if (y <= -8) return; // totally off screen - if (y > vid.conheight - 8 || x < 0 || x > vid.conwidth - 8) + if (y > (int) vid.height - 8 || x < 0 || x > (int) vid.width - 8) return; if (chr > 255) return; @@ -279,7 +295,7 @@ Draw_Character (int x, int y, unsigned int chr) } else drawline = 8; - dest = ((byte*)vid.conbuffer) + y * vid.conrowbytes + x; + dest = d_viewbuffer + y * d_rowbytes + x; while (drawline--) { if (source[0]) @@ -299,7 +315,22 @@ Draw_Character (int x, int y, unsigned int chr) if (source[7]) dest[7] = source[7]; source += 128; - dest += vid.conrowbytes; + dest += d_rowbytes; + } +} + +void +sw_Draw_CharBuffer (int x, int y, draw_charbuffer_t *buffer) +{ + const byte *line = (byte *) buffer->chars; + int width = buffer->width; + int height = buffer->height; + while (height-- > 0) { + for (int i = 0; i < width; i++) { + Draw_Character (x + i * 8, y, line[i]); + } + line += width; + y += 8; } } @@ -339,7 +370,7 @@ Draw_Pixel (int x, int y, byte color) { byte *dest; - dest = ((byte*)vid.conbuffer) + y * vid.conrowbytes + x; + dest = d_viewbuffer + y * d_rowbytes + x; *dest = color; } @@ -352,7 +383,7 @@ crosshair_1 (int x, int y) static void crosshair_2 (int x, int y) { - byte c = crosshaircolor->int_val; + byte c = crosshaircolor; Draw_Pixel (x - 1, y, c); Draw_Pixel (x - 3, y, c); @@ -367,7 +398,7 @@ crosshair_2 (int x, int y) static void crosshair_3 (int x, int y) { - byte c = crosshaircolor->int_val; + byte c = crosshaircolor; Draw_Pixel (x - 3, y - 3, c); Draw_Pixel (x + 3, y - 3, c); @@ -382,7 +413,7 @@ crosshair_3 (int x, int y) static void crosshair_4 (int x, int y) { - //byte c = crosshaircolor->int_val; + //byte c = crosshaircolor; Draw_Pixel (x, y - 2, 8); Draw_Pixel (x + 1, y - 2, 9); @@ -416,7 +447,7 @@ crosshair_4 (int x, int y) static void crosshair_5 (int x, int y) { - byte c = crosshaircolor->int_val; + byte c = crosshaircolor; Draw_Pixel (x - 1, y - 3, c); Draw_Pixel (x + 0, y - 3, c); @@ -457,12 +488,12 @@ Draw_Crosshair (void) int x, y; int ch; - ch = crosshair->int_val - 1; + ch = crosshair - 1; if ((unsigned) ch >= sizeof (crosshair_func) / sizeof (crosshair_func[0])) return; - x = vid.conwidth / 2 + cl_crossx->int_val; - y = vid.conheight / 2 + cl_crossy->int_val; + x = vid.width / 2 + cl_crossx; + y = vid.height / 2 + cl_crossy; crosshair_func[ch] (x, y); } @@ -484,48 +515,75 @@ Draw_Pic (int x, int y, qpic_t *pic) byte *dest, *source, tbyte; int v, u; - if (x < 0 || (x + pic->width) > vid.conwidth - || y < 0 || (y + pic->height) > vid.conheight) { - Sys_MaskPrintf (SYS_VID, "Draw_Pic: bad coordinates"); + if (x < 0 || (x + pic->width) > (int) vid.width + || y < 0 || (y + pic->height) > (int) vid.height) { + Sys_MaskPrintf (SYS_vid, "Draw_Pic: bad coordinates"); Draw_SubPic (x, y, pic, 0, 0, pic->width, pic->height); return; } source = pic->data; - dest = ((byte*)vid.buffer) + y * vid.rowbytes + x; + dest = d_viewbuffer + y * d_rowbytes + x; - if (pic->width & 7) { // general - for (v = 0; v < pic->height; v++) { - for (u = 0; u < pic->width; u++) - if ((tbyte = source[u]) != TRANSPARENT_COLOR) - dest[u] = tbyte; + for (v = 0; v < pic->height; v++) { + for (u = 0; u < pic->width; u++) + if ((tbyte = source[u]) != TRANSPARENT_COLOR) + dest[u] = tbyte; - dest += vid.rowbytes; - source += pic->width; - } - } else { // unwound - for (v = 0; v < pic->height; v++) { - for (u = 0; u < pic->width; u += 8) { - if ((tbyte = source[u]) != TRANSPARENT_COLOR) - dest[u] = tbyte; - if ((tbyte = source[u + 1]) != TRANSPARENT_COLOR) - dest[u + 1] = tbyte; - if ((tbyte = source[u + 2]) != TRANSPARENT_COLOR) - dest[u + 2] = tbyte; - if ((tbyte = source[u + 3]) != TRANSPARENT_COLOR) - dest[u + 3] = tbyte; - if ((tbyte = source[u + 4]) != TRANSPARENT_COLOR) - dest[u + 4] = tbyte; - if ((tbyte = source[u + 5]) != TRANSPARENT_COLOR) - dest[u + 5] = tbyte; - if ((tbyte = source[u + 6]) != TRANSPARENT_COLOR) - dest[u + 6] = tbyte; - if ((tbyte = source[u + 7]) != TRANSPARENT_COLOR) - dest[u + 7] = tbyte; + dest += d_rowbytes; + source += pic->width; + } +} + +void +Draw_FitPic (int x, int y, int width, int height, qpic_t *pic) +{ + int v_width = vid.width; + int v_height = vid.height; + if (x > v_width || y > v_width || x + width <= 0 || y + height <= 0) { + return; + } + if (width == pic->width && height == pic->height) { + Draw_Pic (x, y, pic); + return; + } + int sstep = pic->width * 0x10000 / width; + int tstep = pic->height * 0x10000 / height; + int sx = 0, ex = width; + int sy = 0, ey = height; + + if (x < 0) { + sx -= x; + ex += x; + } + if (y < 0) { + sy -= y; + ey += y; + } + if (x + width > v_width) { + ex -= x + width - v_width; + } + if (y + height > v_height) { + ey -= y + height - v_height; + } + x += sx; + y += sy; + + byte *src, *dst; + + // draw the pic + dst = d_viewbuffer + y * d_rowbytes + x; + + for (int y = sy; y < sy + ey; y++, dst += d_rowbytes) { + src = pic->data + ((y * tstep) >> 16) * pic->width; + if (width == pic->width) + memcpy (dst, src, width); + else { + int f = sx * sstep; + for (int x = 0; x < ex; x++, f += sstep) { + dst[x] = src[f >> 16]; } - dest += vid.rowbytes; - source += pic->width; } } } @@ -543,9 +601,9 @@ Draw_SubPic (int x, int y, qpic_t *pic, int srcx, int srcy, int width, byte *dest, *source, tbyte; int u, v; - if ((x < 0) || (x + width > vid.conwidth) - || (y < 0) || (y + height > vid.conheight)) { - Sys_MaskPrintf (SYS_VID, "Draw_SubPic: bad coordinates"); + if ((x < 0) || (x + width > (int) vid.width) + || (y < 0) || (y + height > (int) vid.height)) { + Sys_MaskPrintf (SYS_vid, "Draw_SubPic: bad coordinates"); } // first, clip to screen if (x < 0) { @@ -553,7 +611,7 @@ Draw_SubPic (int x, int y, qpic_t *pic, int srcx, int srcy, int width, width += x; x = 0; } - if (x + width > vid.width) + if ((unsigned) (x + width) > vid.width) width = vid.width - x; if (width <= 0) return; @@ -562,7 +620,7 @@ Draw_SubPic (int x, int y, qpic_t *pic, int srcx, int srcy, int width, height += y; y = 0; } - if (y + height > vid.height) + if ((unsigned) (y + height) > vid.height) height = vid.height - y; if (height <= 0) return; @@ -571,7 +629,7 @@ Draw_SubPic (int x, int y, qpic_t *pic, int srcx, int srcy, int width, source = pic->data + srcy * pic->width + srcx; - dest = ((byte*)vid.buffer) + y * vid.rowbytes + x; + dest = d_viewbuffer + y * d_rowbytes + x; if (width & 7) { // general for (v = 0; v < height; v++) { @@ -579,7 +637,7 @@ Draw_SubPic (int x, int y, qpic_t *pic, int srcx, int srcy, int width, if ((tbyte = source[u]) != TRANSPARENT_COLOR) dest[u] = tbyte; - dest += vid.rowbytes; + dest += d_rowbytes; source += pic->width; } } else { // unwound @@ -602,7 +660,7 @@ Draw_SubPic (int x, int y, qpic_t *pic, int srcx, int srcy, int width, if ((tbyte = source[u + 7]) != TRANSPARENT_COLOR) dest[u + 7] = tbyte; } - dest += vid.rowbytes; + dest += d_rowbytes; source += pic->width; } } @@ -612,7 +670,6 @@ Draw_SubPic (int x, int y, qpic_t *pic, int srcx, int srcy, int width, void Draw_ConsoleBackground (int lines, byte alpha) { - int x, y, v; byte *src, *dest; int f, fstep; qpic_t *conback; @@ -620,31 +677,25 @@ Draw_ConsoleBackground (int lines, byte alpha) conback = Draw_CachePic ("gfx/conback.lmp", false); // draw the pic - dest = vid.conbuffer; + dest = d_viewbuffer; - for (y = 0; y < lines; y++, dest += vid.conrowbytes) { - v = (vid.conheight - lines + y) * 200 / vid.conheight; + for (int y = 0; y < lines; y++, dest += d_rowbytes) { + int v = (vid.height - lines + y) * 200 / vid.height; src = conback->data + v * 320; - if (vid.conwidth == 320) - memcpy (dest, src, vid.conwidth); + if (vid.width == 320) + memcpy (dest, src, vid.width); else { f = 0; - fstep = 320 * 0x10000 / vid.conwidth; - for (x = 0; x < vid.conwidth; x += 4) { + fstep = 320 * 0x10000 / vid.width; + for (unsigned x = 0; x < vid.width; x++) { dest[x] = src[f >> 16]; f += fstep; - dest[x + 1] = src[f >> 16]; - f += fstep; - dest[x + 2] = src[f >> 16]; - f += fstep; - dest[x + 3] = src[f >> 16]; - f += fstep; } } } - Draw_AltString (vid.conwidth - strlen (cl_verstring->string) * 8 - 11, - lines - 14, cl_verstring->string); + Draw_AltString (vid.width - strlen (cl_verstring) * 8 - 11, + lines - 14, cl_verstring); } static void @@ -654,10 +705,10 @@ R_DrawRect (vrect_t *prect, int rowbytes, byte * psrc, int transparent) int i, j, srcdelta, destdelta; byte *pdest; - pdest = ((byte*)vid.buffer) + (prect->y * vid.rowbytes) + prect->x; + pdest = d_viewbuffer + (prect->y * d_rowbytes) + prect->x; srcdelta = rowbytes - prect->width; - destdelta = vid.rowbytes - prect->width; + destdelta = d_rowbytes - prect->width; if (transparent) { for (i = 0; i < prect->height; i++) { @@ -678,7 +729,7 @@ R_DrawRect (vrect_t *prect, int rowbytes, byte * psrc, int transparent) for (i = 0; i < prect->height; i++) { memcpy (pdest, psrc, prect->width); psrc += rowbytes; - pdest += vid.rowbytes; + pdest += d_rowbytes; } } } @@ -696,26 +747,21 @@ Draw_TileClear (int x, int y, int w, int h) byte *psrc; vrect_t vr; - CLIP (x, y, w, h, vid.width, vid.height); + if (!r_rectdesc.height) { + return; + } + CLIP (x, y, w, h, (int) vid.width, (int) vid.height); - r_rectdesc.rect.x = x; - r_rectdesc.rect.y = y; - r_rectdesc.rect.width = w; - r_rectdesc.rect.height = h; - - vr.y = r_rectdesc.rect.y; - height = r_rectdesc.rect.height; + vr.y = y; + height = h; tileoffsety = vr.y % r_rectdesc.height; while (height > 0) { - vr.x = r_rectdesc.rect.x; - width = r_rectdesc.rect.width; + vr.x = x; + width = w; - if (tileoffsety != 0) - vr.height = r_rectdesc.height - tileoffsety; - else - vr.height = r_rectdesc.height; + vr.height = r_rectdesc.height - tileoffsety; if (vr.height > height) vr.height = height; @@ -723,11 +769,7 @@ Draw_TileClear (int x, int y, int w, int h) tileoffsetx = vr.x % r_rectdesc.width; while (width > 0) { - if (tileoffsetx != 0) - vr.width = r_rectdesc.width - tileoffsetx; - else - vr.width = r_rectdesc.width; - + vr.width = r_rectdesc.width - tileoffsetx; if (vr.width > width) vr.width = width; @@ -759,53 +801,205 @@ Draw_Fill (int x, int y, int w, int h, int c) byte *dest; int u, v; - if (x < 0 || x + w > vid.conwidth - || y < 0 || y + h > vid.conheight) { - Sys_MaskPrintf (SYS_VID, "Bad Draw_Fill(%d, %d, %d, %d, %c)\n", + if (x < 0 || x + w > (int) vid.width || y < 0 || y + h > (int) vid.height) { + Sys_MaskPrintf (SYS_vid, "Bad Draw_Fill(%d, %d, %d, %d, %c)\n", x, y, w, h, c); } - CLIP (x, y, w, h, vid.width, vid.height); + CLIP (x, y, w, h, (int) vid.width, (int) vid.height); - dest = ((byte*)vid.buffer) + y * vid.rowbytes + x; - for (v = 0; v < h; v++, dest += vid.rowbytes) + dest = d_viewbuffer + y * d_rowbytes + x; + for (v = 0; v < h; v++, dest += d_rowbytes) for (u = 0; u < w; u++) dest[u] = c; } +static inline byte * +draw_horizontal (byte *dest, int xs, int len, int c) +{ + while (len-- > 0) { + *dest = c; + dest += xs; + } + return dest; +} + +static inline byte * +draw_vertical (byte *dest, int len, int c) +{ + while (len-- > 0) { + *dest = c; + dest += d_rowbytes; + } + return dest; +} + +static void +draw_line (int x0, int y0, int x1, int y1, int c) +{ + // Bresenham's line slice algorith (ala Michael Abrash) + int dx, dy, sx; + // always go top to bottom + if (y1 < y0) { + int t; + t = y1; y1 = y0; y0 = t; + t = x1; x1 = x0; x0 = t; + } + dy = y1 - y0; + + if ((dx = x1 - x0) < 0) { + sx = -1; + dx = -dx; + } else { + sx = 1; + } + + byte *dest = d_viewbuffer + y0 * d_rowbytes + x0; + + if (!dx) { + draw_vertical (dest, dy, c); + return; + } + if (!dy) { + draw_horizontal (dest, sx, dx, c); + return; + } + if (dx == dy) { + while (dy-- > 0) { + *dest = c; + dest += d_rowbytes + sx; + } + return; + } + if (dx > dy) { + int step = dx / dy; + int adj_up = (dx % dy) * 2; + int adj_down = dy * 2; + int error = (dx % dy) - adj_down; + int initial = step / 2 + 1; + int final = initial; + + if (!adj_up && !(step & 1)) { + initial--; + } + if (step & 1) { + error += dy; + } + dest = draw_horizontal (dest, sx, initial, c); + dest += d_rowbytes; + while (dy-- > 1) { + int run = step; + if ((error += adj_up) > 0) { + run++; + error -= adj_down; + } + dest = draw_horizontal (dest, sx, run, c); + dest += d_rowbytes; + } + dest = draw_horizontal (dest, sx, final, c); + } else { + int step = dy / dx; + int adj_up = (dy % dx) * 2; + int adj_down = dx * 2; + int error = (dy % dx) - adj_down; + int initial = step / 2 + 1; + int final = initial; + + if (!adj_up && !(step & 1)) { + initial--; + } + if (step & 1) { + error += dx; + } + dest = draw_vertical (dest, initial, c); + dest += sx; + while (dx-- > 1) { + int run = step; + if ((error += adj_up) > 0) { + run++; + error -= adj_down; + } + dest = draw_vertical (dest, run, c); + dest += sx; + } + dest = draw_vertical (dest, final, c); + } +} + +static inline byte +test_point (int x, int y) +{ + byte c = 0; + + if (x < 0) { + c |= 1; + } else if (x >= (int) vid.width) { + c |= 2; + } + if (y < 0) { + c |= 4; + } else if (y >= (int) vid.height) { + c |= 8; + } + return c; +} + +void +Draw_Line (int x0, int y0, int x1, int y1, int c) +{ + byte c0 = test_point (x0, y0); + byte c1 = test_point (x1, y1); + int xmax = vid.width - 1; + int ymax = vid.height - 1; + + while (c0 | c1) { + // Cohen-Sutherland line clipping + if (c0 & c1) { + return; + } + int dx = x1 - x0; + int dy = y1 - y0; + if (c0 & 1) { y0 = (( 0 - x0) * dy + dx * y0) / dx; x0 = 0; } + if (c0 & 2) { y0 = ((xmax - x0) * dy + dx * y0) / dx; x0 = xmax; } + if (c1 & 1) { y1 = (( 0 - x1) * dy + dx * y1) / dx; x1 = 0; } + if (c1 & 2) { y1 = ((xmax - x1) * dy + dx * y1) / dx; x1 = xmax; } + + if (c0 & 4) { x0 = (( 0 - y0) * dx + dy * x0) / dy; y0 = 0; } + if (c0 & 8) { x0 = ((ymax - y0) * dx + dy * x0) / dy; y0 = ymax; } + if (c1 & 4) { x1 = (( 0 - y1) * dx + dy * x1) / dy; y1 = 0; } + if (c1 & 8) { x1 = ((ymax - y1) * dx + dy * x1) / dy; y1 = ymax; } + c0 = test_point (x0, y0); + c1 = test_point (x1, y1); + } + draw_line (x0, y0, x1, y1, c); +} void Draw_FadeScreen (void) { int x, y; - byte *pbuf; + int height = vid.height; + int width = vid.width / 4; + uint32_t *pbuf; - VID_UnlockBuffer (); - S_ExtraUpdate (); - VID_LockBuffer (); + for (y = 0; y < height; y++) { + uint32_t mask; - for (y = 0; y < vid.conheight; y++) { - unsigned int t; + pbuf = (uint32_t *) (d_viewbuffer + d_rowbytes * y); + mask = 0xff << ((y & 1) << 4); - pbuf = ((byte *)vid.buffer) + vid.rowbytes * y; - t = (y & 1) << 1; - - for (x = 0; x < vid.conwidth; x++) { - if ((x & 3) != t) - pbuf[x] = 0; + for (x = 0; x < width; x++) { + *pbuf++ &= mask; } } vr_data.scr_copyeverything = 1; - - VID_UnlockBuffer (); - S_ExtraUpdate (); - VID_LockBuffer (); } void Draw_BlendScreen (quat_t color) { int r, g, b, i; - byte *basepal, *newpal; + const byte *basepal; + byte *newpal; byte pal[768]; basepal = vid.basepal; newpal = pal; @@ -825,5 +1019,80 @@ Draw_BlendScreen (quat_t color) newpal[2] = vid.gammatable[b]; newpal += 3; } - vid.set_palette (pal); + vid.vid_internal->set_palette (vid.vid_internal->ctx, pal); +} + +int +Draw_AddFont (struct font_s *rfont) +{ + int fontid = sw_fonts.size; + DARRAY_OPEN_AT (&sw_fonts, fontid, 1); + swfont_t *font = &sw_fonts.a[fontid]; + + font->font = rfont; + return fontid; +} +#if 0 +typedef struct { + vrect_t *glyph_rects; + byte *bitmap; + int width; + byte color; +} swrgctx_t; + +static void +sw_render_glyph (uint32_t glyphid, int x, int y, void *_rgctx) +{ + swrgctx_t *rgctx = _rgctx; + + float w = rect->width; + float h = rect->height; + if (x < 0 || y < 0 || x + w > vid.width || y + h > vid.height) { + return; + } + int u = rect->x; + int v = rect->y; + byte c = rgctx->color; + byte *src = rgctx->bitmap + v * rgctx->width + u; + byte *dst = d_viewbuffer + y * d_rowbytes + x; + while (h-- > 0) { + for (int i = 0; i < w; i++) { + if (src[i] > 127) { + dst[i] = c; + } + } + src += rgctx->width; + dst += d_rowbytes; + } +} +#endif +void +Draw_Glyph (int x, int y, int fontid, int glyphid, int c) +{ + if (fontid < 0 || (unsigned) fontid > sw_fonts.size) { + return; + } + swfont_t *font = &sw_fonts.a[fontid]; + font_t *rfont = font->font; + vrect_t *rect = &rfont->glyph_rects[glyphid]; + int width = rfont->scrap.width; + + float w = rect->width; + float h = rect->height; + if (x < 0 || y < 0 || x + w > vid.width || y + h > vid.height) { + return; + } + int u = rect->x; + int v = rect->y; + byte *src = rfont->scrap_bitmap + v * width + u; + byte *dst = d_viewbuffer + y * d_rowbytes + x; + while (h-- > 0) { + for (int i = 0; i < w; i++) { + if (src[i] > 127) { + dst[i] = c; + } + } + src += width; + dst += d_rowbytes; + } } diff --git a/libs/video/renderer/sw/fpua.S b/libs/video/renderer/sw/fpua.S index bfe166ee4..df568dcac 100644 --- a/libs/video/renderer/sw/fpua.S +++ b/libs/video/renderer/sw/fpua.S @@ -153,3 +153,7 @@ F_BEGIN(R_SetFPCW) ret F_END(R_SetFPCW) #endif /* USE_INTEL_ASM */ + +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/libs/video/renderer/sw/screen.c b/libs/video/renderer/sw/screen.c deleted file mode 100644 index d425f6549..000000000 --- a/libs/video/renderer/sw/screen.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - screen.c - - master for refresh, status bar, console, chat, notify, etc - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif - -#include - -#include "QF/cvar.h" -#include "QF/draw.h" -#include "QF/dstring.h" -#include "QF/image.h" -#include "QF/pcx.h" -#include "QF/quakefs.h" -#include "QF/render.h" -#include "QF/screen.h" -#include "QF/sys.h" -#include "QF/va.h" - -#include "compat.h" -#include "r_internal.h" -#include "vid_internal.h" - -/* SCREEN SHOTS */ - -tex_t * -SCR_CaptureBGR (void) -{ - int count, x, y; - tex_t *tex; - const byte *src; - byte *dst; - - count = vid.width * vid.height; - tex = malloc (field_offset (tex_t, data[count * 3])); - SYS_CHECKMEM (tex); - tex->width = vid.width; - tex->height = vid.height; - tex->format = tex_rgb; - tex->palette = 0; - D_EnableBackBufferAccess (); - src = vid.buffer; - for (y = 0; y < tex->height; y++) { - dst = tex->data + (tex->height - 1 - y) * tex->width * 3; - for (x = 0; x < tex->width; x++) { - *dst++ = vid.basepal[*src * 3 + 2]; // blue - *dst++ = vid.basepal[*src * 3 + 1]; // green - *dst++ = vid.basepal[*src * 3 + 0]; // red - src++; - } - } - D_DisableBackBufferAccess (); - return tex; -} - -tex_t * -SCR_ScreenShot (int width, int height) -{ - unsigned char *src, *dest; - float fracw, frach; - int count, dex, dey, dx, dy, nx, r, g, b, x, y, w, h; - tex_t *tex; - - // enable direct drawing of console to back buffer - D_EnableBackBufferAccess (); - - w = (vid.width < width) ? vid.width : width; - h = (vid.height < height) ? vid.height : height; - - fracw = (float) vid.width / (float) w; - frach = (float) vid.height / (float) h; - - tex = malloc (field_offset (tex_t, data[w * h])); - if (!tex) - return 0; - - tex->width = w; - tex->height = h; - tex->palette = vid.palette; - - for (y = 0; y < h; y++) { - dest = tex->data + (w * (h - y - 1)); - - for (x = 0; x < w; x++) { - r = g = b = 0; - - dx = x * fracw; - dex = (x + 1) * fracw; - if (dex == dx) - dex++; // at least one - dy = y * frach; - dey = (y + 1) * frach; - if (dey == dy) - dey++; // at least one - - count = 0; - for (; dy < dey; dy++) { - src = ((byte*)vid.buffer) + (vid.rowbytes * dy) + dx; - for (nx = dx; nx < dex; nx++) { - r += vid.basepal[*src * 3]; - g += vid.basepal[*src * 3 + 1]; - b += vid.basepal[*src * 3 + 2]; - src++; - count++; - } - } - r /= count; - g /= count; - b /= count; - *dest++ = MipColor (r, g, b); - } - } - // for adapters that can't stay mapped in for linear writes all the time - D_DisableBackBufferAccess (); - - return tex; -} - -void -SCR_ScreenShot_f (void) -{ - dstring_t *pcxname = dstring_new (); - pcx_t *pcx; - int pcx_len; - - // find a file name to save it to - if (!QFS_NextFilename (pcxname, - va ("%s/qf", qfs_gamedir->dir.shots), ".pcx")) { - Sys_Printf ("SCR_ScreenShot_f: Couldn't create a PCX"); - } else { - // enable direct drawing of console to back buffer - D_EnableBackBufferAccess (); - - // save the pcx file - pcx = EncodePCX (vid.buffer, vid.width, vid.height, vid.rowbytes, - vid.basepal, false, &pcx_len); - QFS_WriteFile (pcxname->str, pcx, pcx_len); - - - // for adapters that can't stay mapped in for linear writes all the - // time - D_DisableBackBufferAccess (); - - Sys_Printf ("Wrote %s/%s\n", qfs_userpath, pcxname->str); - } - dstring_delete (pcxname); -} - -/* - SCR_UpdateScreen - - This is called every frame, and can also be called explicitly to flush - text to the screen. - - WARNING: be very careful calling this from elsewhere, because the refresh - needs almost the entire 256k of stack space! -*/ -void -SCR_UpdateScreen (double realtime, SCR_Func scr_3dfunc, SCR_Func *scr_funcs) -{ - vrect_t vrect; - - if (scr_skipupdate) - return; - - vr_data.realtime = realtime; - - scr_copytop = 0; - vr_data.scr_copyeverything = 0; - - if (!scr_initialized) - return; // not initialized yet - - if (oldfov != scr_fov->value) { // determine size of refresh window - oldfov = scr_fov->value; - vid.recalc_refdef = true; - } - - if (vid.recalc_refdef) - SCR_CalcRefdef (); - - // do 3D refresh drawing, and then update the screen - D_EnableBackBufferAccess (); // of all overlay stuff if drawing - // directly - - if (vr_data.scr_fullupdate++ < vid.numpages) { // clear the entire screen - vr_data.scr_copyeverything = 1; - Draw_TileClear (0, 0, vid.width, vid.height); - } - - pconupdate = NULL; - - SCR_SetUpToDrawConsole (); - - D_DisableBackBufferAccess (); // for adapters that can't stay mapped - // in for linear writes all the time - VID_LockBuffer (); - scr_3dfunc (); - VID_UnlockBuffer (); - - D_EnableBackBufferAccess (); // of all overlay stuff if drawing - // directly - - while (*scr_funcs) { - (*scr_funcs)(); - scr_funcs++; - } - - D_DisableBackBufferAccess (); // for adapters that can't stay mapped - // in for linear writes all the time - if (pconupdate) { - D_UpdateRects (pconupdate); - } - - // update one of three areas - if (vr_data.scr_copyeverything) { - vrect.x = 0; - vrect.y = 0; - vrect.width = vid.width; - vrect.height = vid.height; - vrect.next = 0; - } else if (scr_copytop) { - vrect.x = 0; - vrect.y = 0; - vrect.width = vid.width; - vrect.height = vid.height - vr_data.lineadj; - vrect.next = 0; - } else { - vrect.x = scr_vrect.x; - vrect.y = scr_vrect.y; - vrect.width = scr_vrect.width; - vrect.height = scr_vrect.height; - vrect.next = 0; - } - VID_Update (&vrect); -} diff --git a/libs/video/renderer/sw/surf8.S b/libs/video/renderer/sw/surf8.S index 59186143e..c01343525 100644 --- a/libs/video/renderer/sw/surf8.S +++ b/libs/video/renderer/sw/surf8.S @@ -778,7 +778,7 @@ LPatchTable8: C(R_SurfPatch): pushl %ebx - movl C(colormap),%eax + movl C(r_colormap),%eax movl $LPatchTable8,%ebx movl $32,%ecx LPatchLoop8: @@ -793,3 +793,7 @@ LPatchLoop8: ret #endif // USE_INTEL_ASM + +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/libs/video/renderer/sw/sw_fisheye.c b/libs/video/renderer/sw/sw_fisheye.c new file mode 100644 index 000000000..28500ee68 --- /dev/null +++ b/libs/video/renderer/sw/sw_fisheye.c @@ -0,0 +1,166 @@ +/* + sw_fisheye.c + + SW fisheye rendering + + Copyright (C) 2003 Arkadi Shishlov + Copyright (C) 2022 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_STDLIB_H +# include +#endif + +#include + +#include "QF/cvar.h" +#include "QF/render.h" + +#include "compat.h" +#include "r_internal.h" +#include "vid_internal.h" +#include "vid_sw.h" + +static void +fisheyelookuptable (byte **buf, int width, int height, framebuffer_t *cube, + double fov) +{ + int cube_size = cube->width; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + double dx = x - width / 2; + double dy = -(y - height / 2); + double yaw = sqrt (dx * dx + dy * dy) * fov / width; + double roll = atan2 (dy, dx); + double sx = sin (yaw) * cos (roll); + double sy = sin (yaw) * sin (roll); + double sz = cos (yaw); + + // determine which side of the box we need + double abs_x = fabs (sx); + double abs_y = fabs (sy); + double abs_z = fabs (sz); + int side; + double xs = 0, ys = 0; + + if (abs_x > abs_y) { + if (abs_x > abs_z) { + side = ((sx > 0.0) ? BOX_RIGHT : BOX_LEFT); + } else { + side = ((sz > 0.0) ? BOX_FRONT : BOX_BEHIND); + } + } else { + if (abs_y > abs_z) { + side = ((sy > 0.0) ? BOX_TOP : BOX_BOTTOM); + } else { + side = ((sz > 0.0) ? BOX_FRONT : BOX_BEHIND); + } + } + + #define RC(x) ((x / 2) + 0.5) + #define R2(x) ((x / 2) + 0.5) + + // scale up our vector [x,y,z] to the box + switch(side) { + case BOX_FRONT: xs = RC( sx / sz); ys = R2(-sy / sz); break; + case BOX_BEHIND: xs = RC(-sx / -sz); ys = R2(-sy / -sz); break; + case BOX_LEFT: xs = RC( sz / -sx); ys = R2(-sy / -sx); break; + case BOX_RIGHT: xs = RC(-sz / sx); ys = R2(-sy / sx); break; + case BOX_TOP: xs = RC( sx / sy); ys = R2( sz / sy); break; + case BOX_BOTTOM: xs = RC( sx / -sy); ys = R2( sz / sy); break; + } + + xs = bound (0, xs, 1); + ys = bound (0, ys, 1); + int sxs = xs * (cube_size - 1); + int sys = ys * (cube_size - 1); + sw_framebuffer_t *fb = cube[side].buffer; + *buf++ = fb->color + sys * fb->rowbytes + sxs; + } + } +} + +static void +renderlookup (byte **offs) +{ + framebuffer_t *fb = sw_ctx->framebuffer; + sw_framebuffer_t *swfb = fb->buffer; + byte *p = swfb->color; + unsigned x, y; + for (y = 0; y < fb->height; y++) { + for (x = 0; x < fb->width; x++, offs++) + p[x] = **offs; + p += swfb->rowbytes; + } +} + +void +R_RenderFisheye (framebuffer_t *cube) +{ + int width = r_refdef.vrect.width; + int height = r_refdef.vrect.height; + float fov = scr_ffov; + static int pwidth = -1; + static int pheight = -1; + static int pfov = -1; + static unsigned psize = -1; + static byte **offs = NULL; + + if (fov < 1) fov = 1; + + if (pwidth != width || pheight != height || pfov != fov + || psize != cube->width) { + if (offs) free (offs); + offs = malloc (width*height*sizeof(byte*)); + SYS_CHECKMEM (offs); + pwidth = width; + pheight = height; + pfov = fov; + psize = cube->width; + fisheyelookuptable (offs, width, height, cube, fov*M_PI/180.0); + } +#if 0 + sw_framebuffer_t *fb = cube[0].buffer; + int rowbytes = fb->rowbytes; + int csize = cube[0].width; + for (int s = 0; s < 6; s++) { + fb = cube[s].buffer; + memset (fb->color, 31, csize); + memset (fb->color + (csize - 1) * rowbytes, 31, csize); + for (int y = 0; y < csize; y++) { + fb->color[y * rowbytes] = 31; + fb->color[y * rowbytes + csize - 1] = 31; + } + } +#endif + renderlookup (offs); +} diff --git a/libs/video/renderer/sw/sw_graph.c b/libs/video/renderer/sw/sw_graph.c index 02f663cc2..da8d17a90 100644 --- a/libs/video/renderer/sw/sw_graph.c +++ b/libs/video/renderer/sw/sw_graph.c @@ -33,6 +33,7 @@ #include "QF/render.h" #include "r_internal.h" +#include "vid_sw.h" /* R_LineGraph @@ -40,16 +41,17 @@ Called by only R_DisplayTime */ void -R_LineGraph (int x, int y, int *h_vals, int count) +R_LineGraph (int x, int y, int *h_vals, int count, int height) { int h, i, s, color; byte *dest; + sw_framebuffer_t *fb = sw_ctx->framebuffer->buffer; // FIXME: disable on no-buffer adapters, or put in the driver - s = r_graphheight->int_val; + s = height; while (count--) { - dest = ((byte*)vid.buffer) + vid.rowbytes * y + x++; + dest = fb->color + fb->rowbytes * y + x++; h = *h_vals++; @@ -65,7 +67,7 @@ R_LineGraph (int x, int y, int *h_vals, int count) if (h > s) h = s; - for (i = 0; i < h; i++, dest -= vid.rowbytes) { + for (i = 0; i < h; i++, dest -= fb->rowbytes) { dest[0] = color; } } diff --git a/libs/video/renderer/sw/sw_raclip.c b/libs/video/renderer/sw/sw_raclip.c index 18a89bf0d..2b1ff56b1 100644 --- a/libs/video/renderer/sw/sw_raclip.c +++ b/libs/video/renderer/sw/sw_raclip.c @@ -80,9 +80,9 @@ R_Alias_clip_z (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) R_AliasProjectFinalVert (out, &avout); - if (out->v[0] < r_refdef.aliasvrect.x) + if (out->v[0] < r_refdef.aliasvrectleft) out->flags |= ALIAS_LEFT_CLIP; - if (out->v[1] < r_refdef.aliasvrect.y) + if (out->v[1] < r_refdef.aliasvrecttop) out->flags |= ALIAS_TOP_CLIP; if (out->v[0] > r_refdef.aliasvrectright) out->flags |= ALIAS_RIGHT_CLIP; @@ -99,12 +99,12 @@ R_Alias_clip_left (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) int i; if (pfv0->v[1] >= pfv1->v[1]) { - scale = (float) (r_refdef.aliasvrect.x - pfv0->v[0]) / + scale = (float) (r_refdef.aliasvrectleft - pfv0->v[0]) / (pfv1->v[0] - pfv0->v[0]); for (i = 0; i < 6; i++) out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i]) * scale + 0.5; } else { - scale = (float) (r_refdef.aliasvrect.x - pfv1->v[0]) / + scale = (float) (r_refdef.aliasvrectleft - pfv1->v[0]) / (pfv0->v[0] - pfv1->v[0]); for (i = 0; i < 6; i++) out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i]) * scale + 0.5; @@ -139,12 +139,12 @@ R_Alias_clip_top (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) int i; if (pfv0->v[1] >= pfv1->v[1]) { - scale = (float) (r_refdef.aliasvrect.y - pfv0->v[1]) / + scale = (float) (r_refdef.aliasvrecttop - pfv0->v[1]) / (pfv1->v[1] - pfv0->v[1]); for (i = 0; i < 6; i++) out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i]) * scale + 0.5; } else { - scale = (float) (r_refdef.aliasvrect.y - pfv1->v[1]) / + scale = (float) (r_refdef.aliasvrecttop - pfv1->v[1]) / (pfv0->v[1] - pfv1->v[1]); for (i = 0; i < 6; i++) out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i]) * scale + 0.5; @@ -195,9 +195,9 @@ R_AliasClip (finalvert_t *in, finalvert_t *out, int flag, int count, if (oldflags ^ flags) { clip (&in[j], &in[i], &out[k]); out[k].flags = 0; - if (out[k].v[0] < r_refdef.aliasvrect.x) + if (out[k].v[0] < r_refdef.aliasvrectleft) out[k].flags |= ALIAS_LEFT_CLIP; - if (out[k].v[1] < r_refdef.aliasvrect.y) + if (out[k].v[1] < r_refdef.aliasvrecttop) out[k].flags |= ALIAS_TOP_CLIP; if (out[k].v[0] > r_refdef.aliasvrectright) out[k].flags |= ALIAS_RIGHT_CLIP; @@ -291,13 +291,13 @@ R_AliasClipTriangle (mtriangle_t *ptri) } for (i = 0; i < k; i++) { - if (fv[pingpong][i].v[0] < r_refdef.aliasvrect.x) - fv[pingpong][i].v[0] = r_refdef.aliasvrect.x; + if (fv[pingpong][i].v[0] < r_refdef.aliasvrectleft) + fv[pingpong][i].v[0] = r_refdef.aliasvrectleft; else if (fv[pingpong][i].v[0] > r_refdef.aliasvrectright) fv[pingpong][i].v[0] = r_refdef.aliasvrectright; - if (fv[pingpong][i].v[1] < r_refdef.aliasvrect.y) - fv[pingpong][i].v[1] = r_refdef.aliasvrect.y; + if (fv[pingpong][i].v[1] < r_refdef.aliasvrecttop) + fv[pingpong][i].v[1] = r_refdef.aliasvrecttop; else if (fv[pingpong][i].v[1] > r_refdef.aliasvrectbottom) fv[pingpong][i].v[1] = r_refdef.aliasvrectbottom; diff --git a/libs/video/renderer/sw/sw_raclipa.S b/libs/video/renderer/sw/sw_raclipa.S index 6af9666b7..d97199866 100644 --- a/libs/video/renderer/sw/sw_raclipa.S +++ b/libs/video/renderer/sw/sw_raclipa.S @@ -219,7 +219,7 @@ C(R_Alias_clip_top): leal C(r_refdef),%eax movl C(float_point5),%edx #endif - movl rd_aliasvrect+4(%eax),%eax + movl rd_aliasvrecttop(%eax),%eax movl %edx,point5(%esp) jmp LDoForwardOrBackward #ifdef PIC @@ -310,7 +310,7 @@ C(R_Alias_clip_left): leal C(r_refdef),%eax movl C(float_point5),%edx #endif - movl rd_aliasvrect+0(%eax),%eax + movl rd_aliasvrectleft(%eax),%eax movl %edx,point5(%esp) jmp LRightLeftEntry #ifdef PIC @@ -322,3 +322,6 @@ C(R_Alias_clip_left): #endif // USE_INTEL_ASM +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/libs/video/renderer/sw/sw_ralias.c b/libs/video/renderer/sw/sw_ralias.c index a275873d3..f0e51073b 100644 --- a/libs/video/renderer/sw/sw_ralias.c +++ b/libs/video/renderer/sw/sw_ralias.c @@ -29,12 +29,15 @@ #endif #include +#include #include "QF/image.h" #include "QF/render.h" #include "QF/skin.h" #include "QF/sys.h" +#include "QF/scene/entity.h" + #include "d_ifacea.h" #include "r_internal.h" @@ -44,13 +47,13 @@ affinetridesc_t r_affinetridesc; -void *acolormap; // FIXME: should go away +const byte *acolormap; // FIXME: should go away trivertx_t *r_apverts; // TODO: these probably will go away with optimized rasterization static mdl_t *pmdl; -vec3_t r_plightvec; +vec3_t r_lightvec; int r_ambientlight; float r_shadelight; static aliashdr_t *paliashdr; @@ -59,7 +62,7 @@ auxvert_t *pauxverts; float ziscale; static model_t *pmodel; -static vec3_t alias_forward, alias_right, alias_up; +static vec3_t alias_forward, alias_left, alias_up; static maliasskindesc_t *pskindesc; @@ -80,8 +83,10 @@ static aedge_t aedges[12] = { {0, 5}, {1, 4}, {2, 7}, {3, 6} }; -qboolean -R_AliasCheckBBox (void) +static void R_AliasSetUpTransform (entity_t ent, int trivial_accept); + +bool +R_AliasCheckBBox (entity_t ent) { int i, flags, frame, numv; aliashdr_t *pahdr; @@ -89,24 +94,30 @@ R_AliasCheckBBox (void) finalvert_t *pv0, *pv1, viewpts[16]; auxvert_t *pa0, *pa1, viewaux[16]; maliasframedesc_t *pframedesc; - qboolean zclipped, zfullyclipped; + bool zclipped, zfullyclipped; unsigned int anyclip, allclip; int minz; // expand, rotate, and translate points into worldspace - currententity->trivial_accept = 0; - pmodel = currententity->model; + visibility_t *visibility = Ent_GetComponent (ent.id, scene_visibility, + ent.reg); + visibility->trivial_accept = 0; + + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); + pmodel = renderer->model; if (!(pahdr = pmodel->aliashdr)) pahdr = Cache_Get (&pmodel->cache); pmdl = (mdl_t *) ((byte *) pahdr + pahdr->model); - R_AliasSetUpTransform (0); + R_AliasSetUpTransform (ent, 0); // construct the base bounding box for this frame - frame = currententity->frame; + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, + ent.reg); + frame = animation->frame; // TODO: don't repeat this check when drawing? if ((frame >= pmdl->numframes) || (frame < 0)) { - Sys_MaskPrintf (SYS_DEV, "No such frame %d %s\n", frame, pmodel->name); + Sys_MaskPrintf (SYS_dev, "No such frame %d %s\n", frame, pmodel->path); frame = 0; } @@ -218,11 +229,11 @@ R_AliasCheckBBox (void) return false; // trivial reject off one side } - currententity->trivial_accept = !anyclip & !zclipped; + visibility->trivial_accept = !anyclip & !zclipped; - if (currententity->trivial_accept) { + if (visibility->trivial_accept) { if (minz > (r_aliastransition + (pmdl->size * r_resfudge))) { - currententity->trivial_accept |= 2; + visibility->trivial_accept |= 2; } } @@ -249,9 +260,9 @@ R_AliasClipAndProjectFinalVert (finalvert_t *fv, auxvert_t *av) R_AliasProjectFinalVert (fv, av); - if (fv->v[0] < r_refdef.aliasvrect.x) + if (fv->v[0] < r_refdef.aliasvrectleft) fv->flags |= ALIAS_LEFT_CLIP; - if (fv->v[1] < r_refdef.aliasvrect.y) + if (fv->v[1] < r_refdef.aliasvrecttop) fv->flags |= ALIAS_TOP_CLIP; if (fv->v[0] > r_refdef.aliasvrectright) fv->flags |= ALIAS_RIGHT_CLIP; @@ -348,52 +359,30 @@ R_AliasPreparePoints (void) } } -void -R_AliasSetUpTransform (int trivial_accept) +static void +R_AliasSetUpTransform (entity_t ent, int trivial_accept) { int i; - float rotationmatrix[3][4], t2matrix[3][4]; - static float tmatrix[3][4]; - static float viewmatrix[3][4]; + float rotationmatrix[3][4]; + transform_t transform = Entity_Transform (ent); - VectorCopy (currententity->transform + 0, alias_forward); - VectorNegate (currententity->transform + 4, alias_right); - VectorCopy (currententity->transform + 8, alias_up); - - tmatrix[0][0] = pmdl->scale[0]; - tmatrix[1][1] = pmdl->scale[1]; - tmatrix[2][2] = pmdl->scale[2]; - - tmatrix[0][3] = pmdl->scale_origin[0]; - tmatrix[1][3] = pmdl->scale_origin[1]; - tmatrix[2][3] = pmdl->scale_origin[2]; - -// TODO: can do this with simple matrix rearrangement + mat4f_t mat; + Transform_GetWorldMatrix (transform, mat); + VectorCopy (mat[0], alias_forward); + VectorCopy (mat[1], alias_left); + VectorCopy (mat[2], alias_up); for (i = 0; i < 3; i++) { - t2matrix[i][0] = alias_forward[i]; - t2matrix[i][1] = -alias_right[i]; - t2matrix[i][2] = alias_up[i]; + rotationmatrix[i][0] = pmdl->scale[0] * alias_forward[i]; + rotationmatrix[i][1] = pmdl->scale[1] * alias_left[i]; + rotationmatrix[i][2] = pmdl->scale[2] * alias_up[i]; + rotationmatrix[i][3] = pmdl->scale_origin[0] * alias_forward[i] + + pmdl->scale_origin[1] * alias_left[i] + + pmdl->scale_origin[2] * alias_up[i] + + r_entorigin[i] - r_refdef.frame.position[i]; } - t2matrix[0][3] = -modelorg[0]; - t2matrix[1][3] = -modelorg[1]; - t2matrix[2][3] = -modelorg[2]; - -// FIXME: can do more efficiently than full concatenation - R_ConcatTransforms (t2matrix, tmatrix, rotationmatrix); - -// TODO: should be global, set when vright, etc., set - VectorCopy (vright, viewmatrix[0]); - VectorCopy (vup, viewmatrix[1]); - VectorNegate (viewmatrix[1], viewmatrix[1]); - VectorCopy (vpn, viewmatrix[2]); - -// viewmatrix[0][3] = 0; -// viewmatrix[1][3] = 0; -// viewmatrix[2][3] = 0; - - R_ConcatTransforms (viewmatrix, rotationmatrix, aliastransform); + R_ConcatTransforms (r_viewmatrix, rotationmatrix, aliastransform); // do the scaling up of x and y to screen coordinates as part of the transform // for the unclipped case (it would mess up clipping in the clipped case). @@ -432,7 +421,7 @@ R_AliasTransformFinalVert (finalvert_t *fv, trivertx_t *pverts, // lighting plightnormal = r_avertexnormals[pverts->lightnormalindex]; - lightcos = DotProduct (plightnormal, r_plightvec); + lightcos = DotProduct (plightnormal, r_lightvec); temp = r_ambientlight; if (lightcos < 0) { @@ -482,7 +471,7 @@ R_AliasTransformAndProjectFinalVerts (finalvert_t *fv, stvert_t *pstverts) // lighting plightnormal = r_avertexnormals[pverts->lightnormalindex]; - lightcos = DotProduct (plightnormal, r_plightvec); + lightcos = DotProduct (plightnormal, r_lightvec); temp = r_ambientlight; if (lightcos < 0) { @@ -538,18 +527,19 @@ R_AliasPrepareUnclippedPoints (void) } static void -R_AliasSetupSkin (void) +R_AliasSetupSkin (entity_t ent) { - int skinnum; - - skinnum = currententity->skinnum; + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); + int skinnum = renderer->skinnum; if ((skinnum >= pmdl->numskins) || (skinnum < 0)) { - Sys_MaskPrintf (SYS_DEV, "R_AliasSetupSkin: no such skin # %d\n", + Sys_MaskPrintf (SYS_dev, "R_AliasSetupSkin: no such skin # %d\n", skinnum); skinnum = 0; } - pskindesc = R_AliasGetSkindesc (skinnum, paliashdr); + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, + ent.reg); + pskindesc = R_AliasGetSkindesc (animation, skinnum, paliashdr); a_skinwidth = pmdl->skinwidth; @@ -558,27 +548,27 @@ R_AliasSetupSkin (void) r_affinetridesc.seamfixupX16 = (a_skinwidth >> 1) << 16; r_affinetridesc.skinheight = pmdl->skinheight; - acolormap = vid.colormap8; - if (currententity->skin) { + acolormap = r_colormap; + if (renderer->skin) { tex_t *base; - base = currententity->skin->texels; + base = renderer->skin->texels; if (base) { r_affinetridesc.pskin = base->data; r_affinetridesc.skinwidth = base->width; r_affinetridesc.skinheight = base->height; } - acolormap = currententity->skin->colormap; + acolormap = renderer->skin->colormap; } } static void -R_AliasSetupLighting (alight_t *plighting) +R_AliasSetupLighting (alight_t *lighting) { // guarantee that no vertex will ever be lit below LIGHT_MIN, so we don't // have to clamp off the bottom - r_ambientlight = plighting->ambientlight; + r_ambientlight = lighting->ambientlight; if (r_ambientlight < LIGHT_MIN) r_ambientlight = LIGHT_MIN; @@ -588,7 +578,7 @@ R_AliasSetupLighting (alight_t *plighting) if (r_ambientlight < LIGHT_MIN) r_ambientlight = LIGHT_MIN; - r_shadelight = plighting->shadelight; + r_shadelight = lighting->shadelight; if (r_shadelight < 0) r_shadelight = 0; @@ -596,9 +586,9 @@ R_AliasSetupLighting (alight_t *plighting) r_shadelight *= VID_GRADES; // rotate the lighting vector into the model's frame of reference - r_plightvec[0] = DotProduct (plighting->plightvec, alias_forward); - r_plightvec[1] = -DotProduct (plighting->plightvec, alias_right); - r_plightvec[2] = DotProduct (plighting->plightvec, alias_up); + r_lightvec[0] = DotProduct (lighting->lightvec, alias_forward); + r_lightvec[1] = DotProduct (lighting->lightvec, alias_left); + r_lightvec[2] = DotProduct (lighting->lightvec, alias_up); } /* @@ -607,31 +597,34 @@ R_AliasSetupLighting (alight_t *plighting) set r_apverts */ static void -R_AliasSetupFrame (void) +R_AliasSetupFrame (entity_t ent) { maliasframedesc_t *frame; - frame = R_AliasGetFramedesc (currententity->frame, paliashdr); + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, + ent.reg); + frame = R_AliasGetFramedesc (animation, paliashdr); r_apverts = (trivertx_t *) ((byte *) paliashdr + frame->frame); } void -R_AliasDrawModel (alight_t *plighting) +R_AliasDrawModel (entity_t ent, alight_t *lighting) { int size; finalvert_t *finalverts; r_amodels_drawn++; - if (!(paliashdr = currententity->model->aliashdr)) - paliashdr = Cache_Get (¤tentity->model->cache); + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); + if (!(paliashdr = renderer->model->aliashdr)) + paliashdr = Cache_Get (&renderer->model->cache); pmdl = (mdl_t *) ((byte *) paliashdr + paliashdr->model); size = (CACHE_SIZE - 1) + sizeof (finalvert_t) * (pmdl->numverts + 1) + sizeof (auxvert_t) * pmdl->numverts; - finalverts = (finalvert_t *) Hunk_TempAlloc (size); + finalverts = (finalvert_t *) Hunk_TempAlloc (0, size); if (!finalverts) Sys_Error ("R_AliasDrawModel: out of memory"); @@ -640,16 +633,18 @@ R_AliasDrawModel (alight_t *plighting) (((intptr_t) &finalverts[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); pauxverts = (auxvert_t *) &pfinalverts[pmdl->numverts + 1]; - R_AliasSetupSkin (); - R_AliasSetUpTransform (currententity->trivial_accept); - R_AliasSetupLighting (plighting); - R_AliasSetupFrame (); + R_AliasSetupSkin (ent); + visibility_t *visibility = Ent_GetComponent (ent.id, scene_visibility, + ent.reg); + R_AliasSetUpTransform (ent, visibility->trivial_accept); + R_AliasSetupLighting (lighting); + R_AliasSetupFrame (ent); - r_affinetridesc.drawtype = (currententity->trivial_accept == 3) && - r_recursiveaffinetriangles; + r_affinetridesc.drawtype = ((visibility->trivial_accept == 3) + && r_recursiveaffinetriangles); if (!acolormap) - acolormap = vid.colormap8; + acolormap = r_colormap; if (r_affinetridesc.drawtype) { D_PolysetUpdateTables (); // FIXME: precalc... @@ -659,16 +654,19 @@ R_AliasDrawModel (alight_t *plighting) #endif } - if (currententity != vr_data.view_model) + //FIXME depth hack + if (ent.id != vr_data.view_model.id) ziscale = (float) 0x8000 *(float) 0x10000; else ziscale = (float) 0x8000 *(float) 0x10000 *3.0; - if (currententity->trivial_accept && pmdl->ident != HEADER_MDL16) + if (visibility->trivial_accept && pmdl->ident != HEADER_MDL16) { R_AliasPrepareUnclippedPoints (); - else + } else { R_AliasPreparePoints (); + } - if (!currententity->model->aliashdr) - Cache_Release (¤tentity->model->cache); + if (!renderer->model->aliashdr) { + Cache_Release (&renderer->model->cache); + } } diff --git a/libs/video/renderer/sw/sw_raliasa.S b/libs/video/renderer/sw/sw_raliasa.S index 6da8aa673..8840ad4f8 100644 --- a/libs/video/renderer/sw/sw_raliasa.S +++ b/libs/video/renderer/sw/sw_raliasa.S @@ -124,13 +124,13 @@ Lloop: fxch %st(3) // v[0] | v[2] | v[1] | zi -// lightcos = DotProduct (plightnormal, r_plightvec); +// lightcos = DotProduct (plightnormal, r_lightvec); flds C(r_avertexnormals)(,%eax,4) - fmuls C(r_plightvec) + fmuls C(r_lightvec) flds C(r_avertexnormals)+4(,%eax,4) - fmuls C(r_plightvec)+4 + fmuls C(r_lightvec)+4 flds C(r_avertexnormals)+8(,%eax,4) - fmuls C(r_plightvec)+8 + fmuls C(r_lightvec)+8 fxch %st(1) faddp %st(0),%st(2) fld %st(2) // v[0] | laccum | laccum2 | v[0] | v[2] | @@ -246,3 +246,6 @@ Lsavelight: #endif // USE_INTEL_ASM +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/libs/video/renderer/sw/sw_rbsp.c b/libs/video/renderer/sw/sw_rbsp.c index 17fcbf5bb..7fc2b467a 100644 --- a/libs/video/renderer/sw/sw_rbsp.c +++ b/libs/video/renderer/sw/sw_rbsp.c @@ -36,11 +36,19 @@ #include "QF/render.h" #include "QF/sys.h" +#include "QF/scene/entity.h" + #include "r_internal.h" +typedef struct glbspctx_s { + mod_brush_t *brush; + uint32_t render_id; +} swbspctx_t; + // current entity info -qboolean insubmodel; +bool insubmodel; vec3_t r_worldmodelorg; +mvertex_t *r_pcurrentvertbase; static float entity_rotation[3][3]; int r_currentbkey; @@ -59,7 +67,7 @@ static btofpoly_t *pbtofpolys; static mvertex_t *pfrontenter, *pfrontexit; -static qboolean makeclippededge; +static bool makeclippededge; static void @@ -75,15 +83,15 @@ R_EntityRotate (vec3_t vec) void -R_RotateBmodel (void) +R_RotateBmodel (vec4f_t *mat) { - VectorCopy (currententity->transform + 0, entity_rotation[0]); - VectorCopy (currententity->transform + 4, entity_rotation[1]); - VectorCopy (currententity->transform + 8, entity_rotation[2]); + VectorCopy (mat[0], entity_rotation[0]); + VectorCopy (mat[1], entity_rotation[1]); + VectorCopy (mat[2], entity_rotation[2]); // rotate modelorg and the transformation matrix R_EntityRotate (modelorg); - R_EntityRotate (vpn); + R_EntityRotate (vfwd); R_EntityRotate (vright); R_EntityRotate (vup); @@ -92,7 +100,8 @@ R_RotateBmodel (void) static void -R_RecursiveClipBPoly (bedge_t *pedges, mnode_t *pnode, msurface_t *psurf) +R_RecursiveClipBPoly (uint32_t render_id, bedge_t *pedges, mnode_t *pnode, + msurface_t *psurf) { bedge_t *psideedges[2], *pnextedge, *ptedge; int i, side, lastside; @@ -107,8 +116,8 @@ R_RecursiveClipBPoly (bedge_t *pedges, mnode_t *pnode, msurface_t *psurf) // transform the BSP plane into model space // FIXME: cache these? - splitplane = pnode->plane; - tplane.dist = splitplane->dist - + splitplane = (plane_t *) &pnode->plane; + tplane.dist = splitplane->dist + DotProduct (r_entorigin, splitplane->normal); tplane.normal[0] = DotProduct (entity_rotation[0], splitplane->normal); tplane.normal[1] = DotProduct (entity_rotation[1], splitplane->normal); @@ -121,7 +130,7 @@ R_RecursiveClipBPoly (bedge_t *pedges, mnode_t *pnode, msurface_t *psurf) // set the status for the last point as the previous point // FIXME: cache this stuff somehow? plastvert = pedges->v[0]; - lastdist = DotProduct (plastvert->position, tplane.normal) - + lastdist = DotProduct (plastvert->position, tplane.normal) + tplane.dist; if (lastdist > 0) @@ -131,7 +140,7 @@ R_RecursiveClipBPoly (bedge_t *pedges, mnode_t *pnode, msurface_t *psurf) pvert = pedges->v[1]; - dist = DotProduct (pvert->position, tplane.normal) - tplane.dist; + dist = DotProduct (pvert->position, tplane.normal) + tplane.dist; if (dist > 0) side = 0; @@ -217,18 +226,20 @@ R_RecursiveClipBPoly (bedge_t *pedges, mnode_t *pnode, msurface_t *psurf) if (psideedges[i]) { // draw if we've reached a non-solid leaf, done if all that's left // is a solid leaf, and continue down the tree if it's not a leaf - pn = pnode->children[i]; + int child_id = pnode->children[i]; + pn = r_refdef.worldmodel->brush.nodes + child_id; // we're done with this branch if the node or leaf isn't in the PVS - if (pn->visframe == r_visframecount) { - if (pn->contents < 0) { - if (pn->contents != CONTENTS_SOLID) { - r_currentbkey = ((mleaf_t *) pn)->key; - R_RenderBmodelFace (psideedges[i], psurf); - } - } else { - R_RecursiveClipBPoly (psideedges[i], pnode->children[i], - psurf); + if (child_id < 0) { + mleaf_t *leaf = r_refdef.worldmodel->brush.leafs + ~child_id; + if (r_leaf_visframes[~child_id] == r_visframecount + && leaf->contents != CONTENTS_SOLID) { + r_currentbkey = leaf->key; + R_RenderBmodelFace (render_id, psideedges[i], psurf); + } + } else { + if (r_node_visframes[child_id] == r_visframecount) { + R_RecursiveClipBPoly (render_id, psideedges[i], pn, psurf); } } } @@ -237,7 +248,8 @@ R_RecursiveClipBPoly (bedge_t *pedges, mnode_t *pnode, msurface_t *psurf) void -R_DrawSolidClippedSubmodelPolygons (model_t *pmodel) +R_DrawSolidClippedSubmodelPolygons (uint32_t render_id, mod_brush_t *brush, + mnode_t *topnode) { int i, j, lindex; vec_t dot; @@ -250,9 +262,9 @@ R_DrawSolidClippedSubmodelPolygons (model_t *pmodel) // FIXME: use bounding-box-based frustum clipping info? - psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; - numsurfaces = pmodel->nummodelsurfaces; - pedges = pmodel->edges; + psurf = &brush->surfaces[brush->firstmodelsurface]; + numsurfaces = brush->nummodelsurfaces; + pedges = brush->edges; for (i = 0; i < numsurfaces; i++, psurf++) { // find which side of the node we are on @@ -278,7 +290,7 @@ R_DrawSolidClippedSubmodelPolygons (model_t *pmodel) numbedges += psurf->numedges; for (j = 0; j < psurf->numedges; j++) { - lindex = pmodel->surfedges[psurf->firstedge + j]; + lindex = brush->surfedges[psurf->firstedge + j]; if (lindex > 0) { pedge = &pedges[lindex]; @@ -296,7 +308,7 @@ R_DrawSolidClippedSubmodelPolygons (model_t *pmodel) pbedge[j - 1].pnext = NULL; // mark end of edges - R_RecursiveClipBPoly (pbedge, currententity->topnode, psurf); + R_RecursiveClipBPoly (render_id, pbedge, topnode, psurf); } else { Sys_Error ("no edges in bmodel"); } @@ -306,7 +318,8 @@ R_DrawSolidClippedSubmodelPolygons (model_t *pmodel) void -R_DrawSubmodelPolygons (model_t *pmodel, int clipflags) +R_DrawSubmodelPolygons (uint32_t render_id, mod_brush_t *brush, int clipflags, + mleaf_t *topleaf) { int i; vec_t dot; @@ -316,8 +329,8 @@ R_DrawSubmodelPolygons (model_t *pmodel, int clipflags) // FIXME: use bounding-box-based frustum clipping info? - psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; - numsurfaces = pmodel->nummodelsurfaces; + psurf = &brush->surfaces[brush->firstmodelsurface]; + numsurfaces = brush->nummodelsurfaces; for (i = 0; i < numsurfaces; i++, psurf++) { // find which side of the node we are on @@ -328,10 +341,10 @@ R_DrawSubmodelPolygons (model_t *pmodel, int clipflags) // draw the polygon if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { - r_currentkey = ((mleaf_t *) currententity->topnode)->key; + r_currentkey = topleaf->key; // FIXME: use bounding-box-based frustum clipping info? - R_RenderFace (psurf, clipflags); + R_RenderFace (render_id, psurf, clipflags); } } } @@ -350,26 +363,27 @@ static inline int get_side (mnode_t *node) { // find which side of the node we are on - plane_t *plane = node->plane; + vec4f_t org = r_refdef.frame.position; - if (plane->type < 3) - return (modelorg[plane->type] - plane->dist) < 0; - return (DotProduct (modelorg, plane->normal) - plane->dist) < 0; + return dotf (org, node->plane)[0] < 0; } static void -visit_node (mnode_t *node, int side, int clipflags) +visit_node (swbspctx_t *bctx, mnode_t *node, int side, int clipflags) { int c; msurface_t *surf; + uint32_t render_id = bctx->render_id; + mod_brush_t *brush = bctx->brush; // sneaky hack for side = side ? SURF_PLANEBACK : 0; side = (~side + 1) & SURF_PLANEBACK; // draw stuff if ((c = node->numsurfaces)) { - surf = r_worldentity.model->surfaces + node->firstsurface; - for (; c; c--, surf++) { - if (surf->visframe != r_visframecount) + int surf_id = node->firstsurface; + surf = brush->surfaces + surf_id; + for (; c; c--, surf++, surf_id++) { + if (r_face_visframes[surf_id] != r_visframecount) continue; // side is either 0 or SURF_PLANEBACK @@ -384,10 +398,10 @@ visit_node (mnode_t *node, int side, int clipflags) numbtofpolys++; } } else { - R_RenderPoly (surf, clipflags); + R_RenderPoly (render_id, surf, clipflags); } } else { - R_RenderFace (surf, clipflags); + R_RenderFace (render_id, surf, clipflags); } } // all surfaces on the same node share the same sequence number @@ -396,20 +410,21 @@ visit_node (mnode_t *node, int side, int clipflags) } static inline int -test_node (mnode_t *node, int *clipflags) +test_node (swbspctx_t *bctx, int node_id, int *clipflags) { int i, *pindex; vec3_t acceptpt, rejectpt; double d; - if (node->contents < 0) + if (node_id < 0) return 0; - if (node->visframe != r_visframecount) + if (r_node_visframes[node_id] != r_visframecount) return 0; // cull the clipping planes if not trivial accept // FIXME: the compiler is doing a lousy job of optimizing here; it could be // twice as fast in ASM if (*clipflags) { + mnode_t *node = bctx->brush->nodes + node_id; for (i = 0; i < 4; i++) { if (!(*clipflags & (1 << i))) continue; // don't need to clip against it @@ -444,81 +459,92 @@ test_node (mnode_t *node, int *clipflags) } static void -R_VisitWorldNodes (model_t *model, int clipflags) +R_VisitWorldNodes (swbspctx_t *bctx, int clipflags) { typedef struct { - mnode_t *node; + int node_id; int side, clipflags; } rstack_t; rstack_t *node_ptr; rstack_t *node_stack; - mnode_t *node; - mnode_t *front; + int front; int side, cf; + int node_id; + mod_brush_t *brush = bctx->brush; - node = model->nodes; // +2 for paranoia - node_stack = alloca ((model->depth + 2) * sizeof (rstack_t)); + node_stack = alloca ((brush->depth + 2) * sizeof (rstack_t)); node_ptr = node_stack; + node_id = 0; cf = clipflags; while (1) { - while (test_node (node, &cf)) { + while (test_node (bctx, node_id, &cf)) { + mnode_t *node = bctx->brush->nodes + node_id; cf = clipflags; side = get_side (node); front = node->children[side]; - if (test_node (front, &cf)) { - node_ptr->node = node; + if (test_node (bctx, front, &cf)) { + node_ptr->node_id = node_id; node_ptr->side = side; node_ptr->clipflags = clipflags; node_ptr++; clipflags = cf; - node = front; + node_id = front; continue; } - if (front->contents < 0 && front->contents != CONTENTS_SOLID) - visit_leaf ((mleaf_t *) front); - visit_node (node, side, clipflags); - node = node->children[!side]; + if (front < 0) { + mleaf_t *leaf = bctx->brush->leafs + ~front; + if (leaf->contents != CONTENTS_SOLID) { + visit_leaf (leaf); + } + } + visit_node (bctx, node, side, clipflags); + node_id = node->children[side ^ 1]; + } + if (node_id < 0) { + mleaf_t *leaf = bctx->brush->leafs + ~node_id; + if (leaf->contents != CONTENTS_SOLID) { + visit_leaf (leaf); + } } - if (node->contents < 0 && node->contents != CONTENTS_SOLID) - visit_leaf ((mleaf_t *) node); if (node_ptr != node_stack) { node_ptr--; - node = node_ptr->node; + node_id = node_ptr->node_id; + mnode_t *node = bctx->brush->nodes + node_id; side = node_ptr->side; clipflags = node_ptr->clipflags; - visit_node (node, side, clipflags); - node = node->children[!side]; + visit_node (bctx, node, side, clipflags); + node_id = node->children[side ^ 1]; continue; } break; } - if (node->contents < 0 && node->contents != CONTENTS_SOLID) - visit_leaf ((mleaf_t *) node); } void R_RenderWorld (void) { int i; - model_t *clmodel; btofpoly_t btofpolys[MAX_BTOFPOLYS]; + mod_brush_t *brush = &r_refdef.worldmodel->brush; + swbspctx_t bspctx = { + brush, + 0, + }; pbtofpolys = btofpolys; - currententity = &r_worldentity; - VectorCopy (r_origin, modelorg); - clmodel = currententity->model; - r_pcurrentvertbase = clmodel->vertexes; + brush = &r_refdef.worldmodel->brush; + r_pcurrentvertbase = brush->vertexes; - R_VisitWorldNodes (clmodel, 15); + R_VisitWorldNodes (&bspctx, 15); // if the driver wants the polygons back to front, play the visible ones // back in that order if (r_worldpolysbacktofront) { for (i = numbtofpolys - 1; i >= 0; i--) { - R_RenderPoly (btofpolys[i].psurf, btofpolys[i].clipflags); + R_RenderPoly (0, btofpolys[i].psurf, btofpolys[i].clipflags); } } } diff --git a/libs/video/renderer/sw/sw_rdraw.c b/libs/video/renderer/sw/sw_rdraw.c index c5c943fd5..df778f1f5 100644 --- a/libs/video/renderer/sw/sw_rdraw.c +++ b/libs/video/renderer/sw/sw_rdraw.c @@ -29,6 +29,7 @@ #endif #include "QF/render.h" +#include "QF/scene/entity.h" #include "r_internal.h" @@ -48,11 +49,11 @@ static polydesc_t r_polydesc; clipplane_t view_clipplanes[4]; -medge_t *r_pedge; +medge_t *r_pedge; // FIXME used by asm -qboolean r_leftclipped, r_rightclipped; -static qboolean makeleftedge, makerightedge; -qboolean r_nearzionly; +bool r_leftclipped, r_rightclipped; +static bool makeleftedge, makerightedge; +bool r_nearzionly; int sintable[SIN_BUFFER_SIZE]; int intsintable[SIN_BUFFER_SIZE]; @@ -70,7 +71,7 @@ float r_nearzi; float r_u1, r_v1, r_lzi1; int r_ceilv1; -qboolean r_lastvertvalid; +bool r_lastvertvalid; #ifdef PIC @@ -214,8 +215,8 @@ R_EmitEdge (mvertex_t *pv0, mvertex_t *pv1) // causes it to incorrectly extend to the scan, and the extension of the // line goes off the edge of the screen // FIXME: is this actually needed? - if (edge->u < r_refdef.vrect_x_adj_shift20) - edge->u = r_refdef.vrect_x_adj_shift20; + if (edge->u < r_refdef.vrectx_adj_shift20) + edge->u = r_refdef.vrectx_adj_shift20; if (edge->u > r_refdef.vrectright_adj_shift20) edge->u = r_refdef.vrectright_adj_shift20; @@ -344,7 +345,7 @@ R_EmitCachedEdge (void) void -R_RenderFace (msurface_t *fa, int clipflags) +R_RenderFace (uint32_t render_id, msurface_t *fa, int clipflags) { int i, lindex; unsigned int mask; @@ -353,6 +354,7 @@ R_RenderFace (msurface_t *fa, int clipflags) vec3_t p_normal; medge_t *pedges, tedge; clipplane_t *pclip; + mod_brush_t *brush = *(mod_brush_t **) SW_COMP (scene_sw_brush, render_id); // skip out if no more surfs if ((surface_p) >= surf_max) { @@ -382,11 +384,11 @@ R_RenderFace (msurface_t *fa, int clipflags) r_nearzi = 0; r_nearzionly = false; makeleftedge = makerightedge = false; - pedges = currententity->model->edges; + pedges = brush->edges; r_lastvertvalid = false; for (i = 0; i < fa->numedges; i++) { - lindex = currententity->model->surfedges[fa->firstedge + i]; + lindex = brush->surfedges[fa->firstedge + i]; if (lindex > 0) { r_pedge = &pedges[lindex]; @@ -489,7 +491,7 @@ R_RenderFace (msurface_t *fa, int clipflags) surface_p->flags = fa->flags; surface_p->insubmodel = insubmodel; surface_p->spanstate = 0; - surface_p->entity = currententity; + surface_p->render_id = render_id; surface_p->key = r_currentkey++; surface_p->spans = NULL; @@ -504,20 +506,18 @@ R_RenderFace (msurface_t *fa, int clipflags) surface_p->d_ziorigin = p_normal[2] * distinv - xcenter * surface_p->d_zistepu - ycenter * surface_p->d_zistepv; -//JDC VectorCopy (r_worldmodelorg, surface_p->modelorg); surface_p++; } void -R_RenderBmodelFace (bedge_t *pedges, msurface_t *psurf) +R_RenderBmodelFace (uint32_t render_id, bedge_t *pedges, msurface_t *psurf) { int i; unsigned int mask; plane_t *pplane; float distinv; vec3_t p_normal; - medge_t tedge; clipplane_t *pclip; // skip out if no more surfs @@ -534,6 +534,7 @@ R_RenderBmodelFace (bedge_t *pedges, msurface_t *psurf) c_faceclip++; // this is a dummy to give the caching mechanism someplace to write to + static medge_t tedge; r_pedge = &tedge; // set up clip planes @@ -589,7 +590,7 @@ R_RenderBmodelFace (bedge_t *pedges, msurface_t *psurf) surface_p->flags = psurf->flags; surface_p->insubmodel = true; surface_p->spanstate = 0; - surface_p->entity = currententity; + surface_p->render_id = render_id; surface_p->key = r_currentbkey; surface_p->spans = NULL; @@ -604,13 +605,12 @@ R_RenderBmodelFace (bedge_t *pedges, msurface_t *psurf) surface_p->d_ziorigin = p_normal[2] * distinv - xcenter * surface_p->d_zistepu - ycenter * surface_p->d_zistepv; -//JDC VectorCopy (r_worldmodelorg, surface_p->modelorg); surface_p++; } void -R_RenderPoly (msurface_t *fa, int clipflags) +R_RenderPoly (uint32_t render_id, msurface_t *fa, int clipflags) { int i, lindex, lnumverts, s_axis, t_axis; float dist, lastdist, lzi, scale, u, v, frac; @@ -622,7 +622,8 @@ R_RenderPoly (msurface_t *fa, int clipflags) mvertex_t verts[2][100]; // FIXME: do real number polyvert_t pverts[100]; // FIXME: do real number, safely int vertpage, newverts, newpage, lastvert; - qboolean visible; + bool visible; + mod_brush_t *brush = *(mod_brush_t **) SW_COMP (scene_sw_brush, render_id); // FIXME: clean this up and make it faster // FIXME: guard against running out of vertices @@ -641,12 +642,12 @@ R_RenderPoly (msurface_t *fa, int clipflags) // reconstruct the polygon // FIXME: these should be precalculated and loaded off disk - pedges = currententity->model->edges; + pedges = brush->edges; lnumverts = fa->numedges; vertpage = 0; for (i = 0; i < lnumverts; i++) { - lindex = currententity->model->surfedges[fa->firstedge + i]; + lindex = brush->surfedges[fa->firstedge + i]; if (lindex > 0) { r_pedge = &pedges[lindex]; @@ -777,15 +778,15 @@ R_RenderPoly (msurface_t *fa, int clipflags) void -R_ZDrawSubmodelPolys (model_t *pmodel) +R_ZDrawSubmodelPolys (uint32_t render_id, mod_brush_t *brush) { int i, numsurfaces; msurface_t *psurf; float dot; plane_t *pplane; - psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; - numsurfaces = pmodel->nummodelsurfaces; + psurf = &brush->surfaces[brush->firstmodelsurface]; + numsurfaces = brush->nummodelsurfaces; for (i = 0; i < numsurfaces; i++, psurf++) { // find which side of the node we are on @@ -797,7 +798,7 @@ R_ZDrawSubmodelPolys (model_t *pmodel) if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { // FIXME: use bounding-box-based frustum clipping info? - R_RenderPoly (psurf, 15); + R_RenderPoly (render_id, psurf, 15); } } } diff --git a/libs/video/renderer/sw/sw_rdrawa.S b/libs/video/renderer/sw/sw_rdrawa.S index 8fdde816a..531b53367 100644 --- a/libs/video/renderer/sw/sw_rdrawa.S +++ b/libs/video/renderer/sw/sw_rdrawa.S @@ -412,12 +412,12 @@ LSideDone: // // causes it to incorrectly extend to the scan, and the extension of the // // line goes off the edge of the screen // // FIXME: is this actually needed? -// if (edge->u < r_refdef.vrect_x_adj_shift20) -// edge->u = r_refdef.vrect_x_adj_shift20; +// if (edge->u < r_refdef.vrectx_adj_shift20) +// edge->u = r_refdef.vrectx_adj_shift20; // if (edge->u > r_refdef.vrectright_adj_shift20) // edge->u = r_refdef.vrectright_adj_shift20; movl et_u(%edi),%eax - movl C(r_refdef)+rd_vrect_x_adj_shift20,%edx + movl C(r_refdef)+rd_vrectx_adj_shift20,%edx cmpl %edx,%eax jl LP4 movl C(r_refdef)+rd_vrectright_adj_shift20,%edx @@ -738,7 +738,7 @@ LTransformAndProject: // // lzi0 = 1.0 / transformed[2]; fld %st(0) // local[0] | local[0] | local[1] | local[2] - fmuls C(vpn)+0 // zm0 | local[0] | local[1] | local[2] + fmuls C(vfwd)+0 // zm0 | local[0] | local[1] | local[2] fld %st(1) // local[0] | zm0 | local[0] | local[1] | // local[2] fmuls C(vright)+0 // xm0 | zm0 | local[0] | local[1] | local[2] @@ -746,7 +746,7 @@ LTransformAndProject: fmuls C(vup)+0 // ym0 | zm0 | xm0 | local[1] | local[2] fld %st(3) // local[1] | ym0 | zm0 | xm0 | local[1] | // local[2] - fmuls C(vpn)+4 // zm1 | ym0 | zm0 | xm0 | local[1] | + fmuls C(vfwd)+4 // zm1 | ym0 | zm0 | xm0 | local[1] | // local[2] fld %st(4) // local[1] | zm1 | ym0 | zm0 | xm0 | // local[1] | local[2] @@ -763,7 +763,7 @@ LTransformAndProject: faddp %st(0),%st(4) // ym0 | zm2 | ym1 | xm2 | local[2] faddp %st(0),%st(2) // zm2 | ym2 | xm2 | local[2] fld %st(3) // local[2] | zm2 | ym2 | xm2 | local[2] - fmuls C(vpn)+8 // zm3 | zm2 | ym2 | xm2 | local[2] + fmuls C(vfwd)+8 // zm3 | zm2 | ym2 | xm2 | local[2] fld %st(4) // local[2] | zm3 | zm2 | ym2 | xm2 | local[2] fmuls C(vright)+8 // xm3 | zm3 | zm2 | ym2 | xm2 | local[2] fxch %st(5) // local[2] | zm3 | zm2 | ym2 | xm2 | xm3 @@ -847,3 +847,6 @@ LClampP3: #endif // USE_INTEL_ASM +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/libs/video/renderer/sw/sw_redge.c b/libs/video/renderer/sw/sw_redge.c index 173157677..df49f89ad 100644 --- a/libs/video/renderer/sw/sw_redge.c +++ b/libs/video/renderer/sw/sw_redge.c @@ -28,9 +28,6 @@ # include "config.h" #endif -#include "QF/render.h" -#include "QF/sound.h" - #include "d_ifacea.h" #include "r_internal.h" #include "vid_internal.h" @@ -85,8 +82,6 @@ R_DrawCulledPolys (void) surf_t *s; msurface_t *pface; - currententity = &r_worldentity; - if (r_worldpolysbacktofront) { for (s = surface_p - 1; s > &surfaces[1]; s--) { if (!s->spans) @@ -94,7 +89,7 @@ R_DrawCulledPolys (void) if (!(s->flags & SURF_DRAWBACKGROUND)) { pface = (msurface_t *) s->data; - R_RenderPoly (pface, 15); + R_RenderPoly (s->render_id, pface, 15); } } } else { @@ -104,7 +99,7 @@ R_DrawCulledPolys (void) if (!(s->flags & SURF_DRAWBACKGROUND)) { pface = (msurface_t *) s->data; - R_RenderPoly (pface, 15); + R_RenderPoly (s->render_id, pface, 15); } } } @@ -522,10 +517,6 @@ R_ScanEdges (void) // flush the span list if we can't be sure we have enough spans left // for the next scan if (span_p > max_span_p) { - VID_UnlockBuffer (); - S_ExtraUpdate (); // don't let sound get messed up if going slow - VID_LockBuffer (); - if (r_drawculledpolys) R_DrawCulledPolys (); else diff --git a/libs/video/renderer/sw/sw_redgea.S b/libs/video/renderer/sw/sw_redgea.S index c549d36f4..ec2b53eed 100644 --- a/libs/video/renderer/sw/sw_redgea.S +++ b/libs/video/renderer/sw/sw_redgea.S @@ -759,3 +759,6 @@ C(R_SurfacePatch): #endif // USE_INTEL_ASM +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/libs/video/renderer/sw/sw_riqm.c b/libs/video/renderer/sw/sw_riqm.c index 285342b3f..8ae3107b2 100644 --- a/libs/video/renderer/sw/sw_riqm.c +++ b/libs/video/renderer/sw/sw_riqm.c @@ -45,6 +45,8 @@ #include "QF/skin.h" #include "QF/sys.h" +#include "QF/scene/entity.h" + #include "d_ifacea.h" #include "r_internal.h" @@ -56,14 +58,14 @@ // avoid the need for inner-loop light // clamping -static vec3_t r_plightvec; +static vec3_t r_lightvec; static int r_ambientlight; static float r_shadelight; static inline int calc_light (float *normal) { - float lightcos = DotProduct (normal, r_plightvec); + float lightcos = DotProduct (normal, r_lightvec); int temp = r_ambientlight; if (lightcos < 0) { @@ -209,7 +211,7 @@ R_IQMPreparePoints (iqm_t *iqm, swiqm_t *sw, iqmframe_t *frame) } static void -R_IQMSetupLighting (entity_t *ent, alight_t *plighting) +R_IQMSetupLighting (entity_t ent, alight_t *plighting) { // guarantee that no vertex will ever be lit below LIGHT_MIN, so we don't // have to clamp off the bottom @@ -231,22 +233,28 @@ R_IQMSetupLighting (entity_t *ent, alight_t *plighting) r_shadelight *= VID_GRADES; // rotate the lighting vector into the model's frame of reference - r_plightvec[0] = DotProduct (plighting->plightvec, ent->transform + 0); - r_plightvec[1] = DotProduct (plighting->plightvec, ent->transform + 4); - r_plightvec[2] = DotProduct (plighting->plightvec, ent->transform + 8); + mat4f_t mat; + transform_t transform = Entity_Transform (ent); + Transform_GetWorldMatrix (transform, mat); + //FIXME vectorize + r_lightvec[0] = DotProduct (plighting->lightvec, mat[0]); + r_lightvec[1] = DotProduct (plighting->lightvec, mat[1]); + r_lightvec[2] = DotProduct (plighting->lightvec, mat[2]); } static void -R_IQMSetUpTransform (int trivial_accept) +R_IQMSetUpTransform (entity_t ent, int trivial_accept) { int i; float rotationmatrix[3][4]; - static float viewmatrix[3][4]; vec3_t forward, left, up; - VectorCopy (currententity->transform + 0, forward); - VectorCopy (currententity->transform + 4, left); - VectorCopy (currententity->transform + 8, up); + mat4f_t mat; + transform_t transform = Entity_Transform (ent); + Transform_GetWorldMatrix (transform, mat); + VectorCopy (mat[0], forward); + VectorCopy (mat[1], left); + VectorCopy (mat[2], up); // TODO: can do this with simple matrix rearrangement @@ -256,21 +264,11 @@ R_IQMSetUpTransform (int trivial_accept) rotationmatrix[i][2] = up[i]; } - rotationmatrix[0][3] = -modelorg[0]; - rotationmatrix[1][3] = -modelorg[1]; - rotationmatrix[2][3] = -modelorg[2]; + rotationmatrix[0][3] = r_entorigin[0] - r_refdef.frame.position[0]; + rotationmatrix[1][3] = r_entorigin[1] - r_refdef.frame.position[1]; + rotationmatrix[2][3] = r_entorigin[2] - r_refdef.frame.position[2]; -// TODO: should be global, set when vright, etc., set - VectorCopy (vright, viewmatrix[0]); - VectorCopy (vup, viewmatrix[1]); - VectorNegate (viewmatrix[1], viewmatrix[1]); - VectorCopy (vpn, viewmatrix[2]); - -// viewmatrix[0][3] = 0; -// viewmatrix[1][3] = 0; -// viewmatrix[2][3] = 0; - - R_ConcatTransforms (viewmatrix, rotationmatrix, aliastransform); + R_ConcatTransforms (r_viewmatrix, rotationmatrix, aliastransform); // do the scaling up of x and y to screen coordinates as part of the transform // for the unclipped case (it would mess up clipping in the clipped case). @@ -290,10 +288,10 @@ R_IQMSetUpTransform (int trivial_accept) } void -R_IQMDrawModel (alight_t *plighting) +R_IQMDrawModel (entity_t ent, alight_t *plighting) { - entity_t *ent = currententity; - model_t *model = ent->model; + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); + model_t *model = renderer->model; iqm_t *iqm = (iqm_t *) model->aliashdr; swiqm_t *sw = (swiqm_t *) iqm->extra_data; int size; @@ -303,30 +301,37 @@ R_IQMDrawModel (alight_t *plighting) size = (CACHE_SIZE - 1) + sizeof (finalvert_t) * (iqm->num_verts + 1) + sizeof (auxvert_t) * iqm->num_verts; - blend = R_IQMGetLerpedFrames (ent, iqm); - frame = R_IQMBlendPalette (iqm, ent->pose1, ent->pose2, blend, size, - sw->blend_palette, sw->palette_size); + + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, + ent.reg); + blend = R_IQMGetLerpedFrames (animation, iqm); + frame = R_IQMBlendPalette (iqm, animation->pose1, animation->pose2, + blend, size, sw->blend_palette, + sw->palette_size); pfinalverts = (finalvert_t *) &frame[sw->palette_size]; pfinalverts = (finalvert_t *) (((intptr_t) &pfinalverts[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); pauxverts = (auxvert_t *) &pfinalverts[iqm->num_verts + 1]; - R_IQMSetUpTransform (ent->trivial_accept); + visibility_t *visibility = Ent_GetComponent (ent.id, scene_visibility, + ent.reg); + R_IQMSetUpTransform (ent, visibility->trivial_accept); R_IQMSetupLighting (ent, plighting); - r_affinetridesc.drawtype = (ent->trivial_accept == 3) && + r_affinetridesc.drawtype = (visibility->trivial_accept == 3) && r_recursiveaffinetriangles; //if (!acolormap) - acolormap = vid.colormap8; + acolormap = r_colormap; - if (ent != vr_data.view_model) + //FIXME depth hack + if (ent.id != vr_data.view_model.id) ziscale = (float) 0x8000 *(float) 0x10000; else ziscale = (float) 0x8000 *(float) 0x10000 *3.0; - if (ent->trivial_accept) + if (visibility->trivial_accept) R_IQMPrepareUnclippedPoints (iqm, sw, frame); else R_IQMPreparePoints (iqm, sw, frame); diff --git a/libs/video/renderer/sw/sw_rmain.c b/libs/video/renderer/sw/sw_rmain.c index 0ed1655a7..f2a5f3fb3 100644 --- a/libs/video/renderer/sw/sw_rmain.c +++ b/libs/video/renderer/sw/sw_rmain.c @@ -41,46 +41,37 @@ #include #include "QF/cmd.h" -#include "QF/cvar.h" -#include "QF/locs.h" -#include "QF/mathlib.h" -#include "QF/render.h" -#include "QF/screen.h" -#include "QF/sound.h" -#include "QF/sys.h" + +#include "QF/scene/entity.h" #include "compat.h" #include "mod_internal.h" #include "r_internal.h" #include "vid_internal.h" +#include "vid_sw.h" #ifdef PIC # undef USE_INTEL_ASM //XXX asm pic hack #endif -void *colormap; -static vec3_t viewlightvec; -static alight_t r_viewlighting = { 128, 192, viewlightvec }; +const byte *r_colormap; int r_numallocatededges; -qboolean r_drawpolys; -qboolean r_drawculledpolys; -qboolean r_worldpolysbacktofront; -qboolean r_recursiveaffinetriangles = true; +bool r_drawpolys; +bool r_drawculledpolys; +bool r_worldpolysbacktofront; +bool r_recursiveaffinetriangles = true; int r_pixbytes = 1; -float r_aliasuvscale = 1.0; int r_outofsurfaces; int r_outofedges; -qboolean r_dowarp, r_dowarpold, r_viewchanged; +bool r_viewchanged; int c_surf; int r_maxsurfsseen, r_maxedgesseen; static int r_cnumsurfs; -static qboolean r_surfsonstack; +static bool r_surfsonstack; int r_clipflags; -byte *r_warpbuffer; - static byte *r_stack_start; // screen size info @@ -93,9 +84,6 @@ float aliasxscale, aliasyscale, aliasxcenter, aliasycenter; int screenwidth; float pixelAspect; -static float screenAspect; -static float verticalFieldOfView; -static float xOrigin, yOrigin; plane_t screenedge[4]; @@ -106,10 +94,12 @@ int r_drawnpolycount; int *pfrustum_indexes[4]; int r_frustum_indexes[4 * 6]; -float r_aliastransition, r_resfudge; +vec3_t vup, base_vup; +vec3_t vfwd, base_vfwd; +vec3_t vright, base_vright; +float r_viewmatrix[3][4]; -static float dp_time1, dp_time2, db_time1, db_time2, rw_time1, rw_time2; -static float se_time1, se_time2, de_time1, de_time2, dv_time1, dv_time2; +float r_aliastransition, r_resfudge; void sw_R_Init (void) @@ -120,7 +110,6 @@ sw_R_Init (void) r_stack_start = (byte *) & dummy; R_Init_Cvars (); - R_Particles_Init_Cvars (); Draw_Init (); SCR_Init (); @@ -128,18 +117,15 @@ sw_R_Init (void) #ifdef USE_INTEL_ASM R_InitVars (); #endif - R_InitParticles (); R_InitTurb (); Cmd_AddCommand ("timerefresh", R_TimeRefresh_f, "Tests the current " "refresh rate for the current location"); - Cmd_AddCommand ("pointfile", R_ReadPointFile_f, "Load a pointfile to " - "determine map leaks"); Cmd_AddCommand ("loadsky", R_LoadSky_f, "Load a skybox"); - Cvar_SetValue (r_maxedges, (float) NUMSTACKEDGES); - Cvar_SetValue (r_maxsurfs, (float) NUMSTACKSURFACES); + r_maxedges = NUMSTACKEDGES; + r_maxsurfs = NUMSTACKSURFACES; view_clipplanes[0].leftedge = true; view_clipplanes[1].rightedge = true; @@ -148,9 +134,6 @@ sw_R_Init (void) view_clipplanes[0].rightedge = view_clipplanes[2].rightedge = view_clipplanes[3].rightedge = false; - r_refdef.xOrigin = XCENTERING; - r_refdef.yOrigin = YCENTERING; - // TODO: collect 386-specific code in one place #ifdef USE_INTEL_ASM Sys_MakeCodeWriteable ((long) R_EdgeCodeStart, @@ -161,37 +144,78 @@ sw_R_Init (void) Skin_Init (); } -void -R_NewMap (model_t *worldmodel, struct model_s **models, int num_models) +uint32_t +SW_AddEntity (entity_t ent) { - int i; + // This takes advantage of the implicit (FIXME, make explicit) grouping of + // the sw components: as all entities that get added here will always have + // all three components, the three component pools are always in sync, thus + // the pool count can be used as a render id which can in turn be used to + // index the components within the pools. + ecs_registry_t *reg = ent.reg; + ecs_pool_t *pool = ®->comp_pools[scene_sw_matrix]; + uint32_t render_id = pool->count; - memset (&r_worldentity, 0, sizeof (r_worldentity)); - r_worldentity.model = worldmodel; + transform_t transform = Entity_Transform (ent); + Ent_SetComponent (ent.id, scene_sw_matrix, reg, + Transform_GetWorldMatrixPtr (transform)); + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, reg); + Ent_SetComponent (ent.id, scene_sw_frame, reg, &animation->frame); + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, reg); + mod_brush_t *brush = &renderer->model->brush; + Ent_SetComponent (ent.id, scene_sw_brush, reg, &brush); - R_FreeAllEntities (); + return render_id; +} - // clear out efrags in case the level hasn't been reloaded - // FIXME: is this one short? - for (i = 0; i < r_worldentity.model->numleafs; i++) - r_worldentity.model->leafs[i].efrags = NULL; +static void +reset_sw_components (ecs_registry_t *reg) +{ + static uint32_t sw_comps[] = { + scene_sw_matrix, + scene_sw_frame, + scene_sw_brush, + }; - if (worldmodel->skytexture) - R_InitSky (worldmodel->skytexture); + for (int i = 0; i < 3; i++) { + ecs_pool_t *pool = ®->comp_pools[sw_comps[i]]; + pool->count = 0; // remove component from every entity + // reserve first component object (render id 0) for the world + // pseudo-entity. + //FIXME takes advantage of the lack of checks for the validity of the + //entity id. + Ent_SetComponent (0, sw_comps[i], reg, 0); + // make sure entity 0 gets allocated a new component object as the + // world pseudo-entity currently has no actual entity (FIXME) + pool->dense[0] = nullent; + } +} + +void +R_NewScene (scene_t *scene) +{ + model_t *worldmodel = scene->worldmodel; + mod_brush_t *brush = &worldmodel->brush; + + r_refdef.registry = scene->reg; + r_refdef.worldmodel = worldmodel; + + if (brush->skytexture) + R_InitSky (brush->skytexture); // Force a vis update - r_viewleaf = NULL; - R_MarkLeaves (); + R_MarkLeaves (0, 0, 0, 0); R_ClearParticles (); - r_cnumsurfs = r_maxsurfs->int_val; + r_cnumsurfs = r_maxsurfs; if (r_cnumsurfs <= MINSURFACES) r_cnumsurfs = MINSURFACES; if (r_cnumsurfs > NUMSTACKSURFACES) { - surfaces = Hunk_AllocName (r_cnumsurfs * sizeof (surf_t), "surfaces"); + surfaces = Hunk_AllocName (0, r_cnumsurfs * sizeof (surf_t), + "surfaces"); surface_p = surfaces; surf_max = &surfaces[r_cnumsurfs]; @@ -207,7 +231,7 @@ R_NewMap (model_t *worldmodel, struct model_s **models, int num_models) r_maxedgesseen = 0; r_maxsurfsseen = 0; - r_numallocatededges = r_maxedges->int_val; + r_numallocatededges = r_maxedges; if (r_numallocatededges < MINEDGES) r_numallocatededges = MINEDGES; @@ -215,7 +239,7 @@ R_NewMap (model_t *worldmodel, struct model_s **models, int num_models) if (r_numallocatededges <= NUMSTACKEDGES) { auxedges = NULL; } else { - auxedges = Hunk_AllocName (r_numallocatededges * sizeof (edge_t), + auxedges = Hunk_AllocName (0, r_numallocatededges * sizeof (edge_t), "edges"); } @@ -223,236 +247,164 @@ R_NewMap (model_t *worldmodel, struct model_s **models, int num_models) r_viewchanged = false; } -/* - R_ViewChanged - - Called every time the vid structure or r_refdef changes. - Guaranteed to be called before the first refresh -*/ void -R_ViewChanged (float aspect) +R_SetColormap (const byte *cmap) { - int i; - float res_scale; - - r_viewchanged = true; - - r_refdef.horizontalFieldOfView = 2.0 * tan (r_refdef.fov_x / 360 * M_PI); - r_refdef.fvrectx = (float) r_refdef.vrect.x; - r_refdef.fvrectx_adj = (float) r_refdef.vrect.x - 0.5; - r_refdef.vrect_x_adj_shift20 = (r_refdef.vrect.x << 20) + (1 << 19) - 1; - r_refdef.fvrecty = (float) r_refdef.vrect.y; - r_refdef.fvrecty_adj = (float) r_refdef.vrect.y - 0.5; - r_refdef.vrectright = r_refdef.vrect.x + r_refdef.vrect.width; - r_refdef.vrectright_adj_shift20 = - (r_refdef.vrectright << 20) + (1 << 19) - 1; - r_refdef.fvrectright = (float) r_refdef.vrectright; - r_refdef.fvrectright_adj = (float) r_refdef.vrectright - 0.5; - r_refdef.vrectrightedge = (float) r_refdef.vrectright - 0.99; - r_refdef.vrectbottom = r_refdef.vrect.y + r_refdef.vrect.height; - r_refdef.fvrectbottom = (float) r_refdef.vrectbottom; - r_refdef.fvrectbottom_adj = (float) r_refdef.vrectbottom - 0.5; - - r_refdef.aliasvrect.x = (int) (r_refdef.vrect.x * r_aliasuvscale); - r_refdef.aliasvrect.y = (int) (r_refdef.vrect.y * r_aliasuvscale); - r_refdef.aliasvrect.width = (int) (r_refdef.vrect.width * r_aliasuvscale); - r_refdef.aliasvrect.height = (int) (r_refdef.vrect.height * - r_aliasuvscale); - r_refdef.aliasvrectright = r_refdef.aliasvrect.x + - r_refdef.aliasvrect.width; - r_refdef.aliasvrectbottom = r_refdef.aliasvrect.y + - r_refdef.aliasvrect.height; - - pixelAspect = vid.aspect; - xOrigin = r_refdef.xOrigin; - yOrigin = r_refdef.yOrigin; - - screenAspect = r_refdef.vrect.width * pixelAspect / r_refdef.vrect.height; - // 320*200 1.0 pixelAspect = 1.6 screenAspect - // 320*240 1.0 pixelAspect = 1.3333 screenAspect - // proper 320*200 pixelAspect = 0.8333333 - - verticalFieldOfView = r_refdef.horizontalFieldOfView / screenAspect; - - // values for perspective projection - // if math were exact, the values would range from 0.5 to to range+0.5 - // hopefully they wll be in the 0.000001 to range+.999999 and truncate - // the polygon rasterization will never render in the first row or column - // but will definately render in the [range] row and column, so adjust the - // buffer origin to get an exact edge to edge fill - xcenter = ((float) r_refdef.vrect.width * XCENTERING) + - r_refdef.vrect.x - 0.5; - aliasxcenter = xcenter * r_aliasuvscale; - ycenter = ((float) r_refdef.vrect.height * YCENTERING) + - r_refdef.vrect.y - 0.5; - aliasycenter = ycenter * r_aliasuvscale; - - xscale = r_refdef.vrect.width / r_refdef.horizontalFieldOfView; - aliasxscale = xscale * r_aliasuvscale; - xscaleinv = 1.0 / xscale; - yscale = xscale * pixelAspect; - aliasyscale = yscale * r_aliasuvscale; - yscaleinv = 1.0 / yscale; - xscaleshrink = (r_refdef.vrect.width - 6) / r_refdef.horizontalFieldOfView; - yscaleshrink = xscaleshrink * pixelAspect; - - // left side clip - screenedge[0].normal[0] = -1.0 / (xOrigin * - r_refdef.horizontalFieldOfView); - screenedge[0].normal[1] = 0; - screenedge[0].normal[2] = 1; - screenedge[0].type = PLANE_ANYZ; - - // right side clip - screenedge[1].normal[0] = 1.0 / ((1.0 - xOrigin) * - r_refdef.horizontalFieldOfView); - screenedge[1].normal[1] = 0; - screenedge[1].normal[2] = 1; - screenedge[1].type = PLANE_ANYZ; - - // top side clip - screenedge[2].normal[0] = 0; - screenedge[2].normal[1] = -1.0 / (yOrigin * verticalFieldOfView); - screenedge[2].normal[2] = 1; - screenedge[2].type = PLANE_ANYZ; - - // bottom side clip - screenedge[3].normal[0] = 0; - screenedge[3].normal[1] = 1.0 / ((1.0 - yOrigin) * verticalFieldOfView); - screenedge[3].normal[2] = 1; - screenedge[3].type = PLANE_ANYZ; - - for (i = 0; i < 4; i++) - VectorNormalize (screenedge[i].normal); - - res_scale = sqrt ((double) (r_refdef.vrect.width * r_refdef.vrect.height) / - (320.0 * 152.0)) * (2.0 / - r_refdef.horizontalFieldOfView); - r_aliastransition = r_aliastransbase->value * res_scale; - r_resfudge = r_aliastransadj->value * res_scale; - + r_colormap = cmap; // TODO: collect 386-specific code in one place #ifdef USE_INTEL_ASM Sys_MakeCodeWriteable ((long) R_Surf8Start, (long) R_Surf8End - (long) R_Surf8Start); - colormap = vid.colormap8; R_SurfPatch (); #endif // USE_INTEL_ASM - - D_ViewChanged (); } -static void -R_DrawEntitiesOnList (void) +static inline void +draw_sprite_entity (entity_t ent) { - int j; - unsigned int lnum; - alight_t lighting; - entity_t *ent; + R_DrawSprite (ent); +} +static inline void +setup_lighting (entity_t ent, alight_t *lighting) +{ + float minlight = 0; + int j; // FIXME: remove and do real lighting - float lightvec[3] = { -1, 0, 0 }; vec3_t dist; float add; - float minlight = 0; + float lightvec[3] = { -1, 0, 0 }; - if (!r_drawentities->int_val) - return; + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); + minlight = max (renderer->model->min_light, renderer->min_light); - for (ent = r_ent_queue; ent; ent = ent->next) { - currententity = ent; + // 128 instead of 255 due to clamping below + j = max (R_LightPoint (&r_refdef.worldmodel->brush, r_entorigin), + minlight * 128); - switch (currententity->model->type) { - case mod_sprite: - VectorCopy (currententity->origin, r_entorigin); - VectorSubtract (r_origin, r_entorigin, modelorg); - R_DrawSprite (); - break; + lighting->ambientlight = j; + lighting->shadelight = j; - case mod_alias: - case mod_iqm: - VectorCopy (currententity->origin, r_entorigin); - VectorSubtract (r_origin, r_entorigin, modelorg); + VectorCopy (lightvec, lighting->lightvec); - minlight = max (currententity->model->min_light, currententity->min_light); + for (unsigned lnum = 0; lnum < r_maxdlights; lnum++) { + if (r_dlights[lnum].die >= vr_data.realtime) { + VectorSubtract (r_entorigin, r_dlights[lnum].origin, dist); + add = r_dlights[lnum].radius - VectorLength (dist); - // see if the bounding box lets us trivially reject, also - // sets trivial accept status - currententity->trivial_accept = 0; //FIXME - if (currententity->model->type == mod_iqm//FIXME - || R_AliasCheckBBox ()) { - // 128 instead of 255 due to clamping below - j = max (R_LightPoint (currententity->origin), minlight * 128); - - lighting.ambientlight = j; - lighting.shadelight = j; - - lighting.plightvec = lightvec; - - for (lnum = 0; lnum < r_maxdlights; lnum++) { - if (r_dlights[lnum].die >= vr_data.realtime) { - VectorSubtract (currententity->origin, - r_dlights[lnum].origin, dist); - add = r_dlights[lnum].radius - VectorLength (dist); - - if (add > 0) - lighting.ambientlight += add; - } - } - - // clamp lighting so it doesn't overbright as much - if (lighting.ambientlight > 128) - lighting.ambientlight = 128; - if (lighting.ambientlight + lighting.shadelight > 192) - lighting.shadelight = 192 - lighting.ambientlight; - - if (currententity->model->type == mod_iqm) - R_IQMDrawModel (&lighting); - else - R_AliasDrawModel (&lighting); - } - - break; - - default: - break; + if (add > 0) + lighting->ambientlight += add; } } + + // clamp lighting so it doesn't overbright as much + if (lighting->ambientlight > 128) + lighting->ambientlight = 128; + if (lighting->ambientlight + lighting->shadelight > 192) + lighting->shadelight = 192 - lighting->ambientlight; +} + +static inline void +draw_alias_entity (entity_t ent) +{ + // see if the bounding box lets us trivially reject, also + // sets trivial accept status + visibility_t *visibility = Ent_GetComponent (ent.id, scene_visibility, + ent.reg); + visibility->trivial_accept = 0; //FIXME + if (R_AliasCheckBBox (ent)) { + alight_t lighting; + setup_lighting (ent, &lighting); + R_AliasDrawModel (ent, &lighting); + } +} + +static inline void +draw_iqm_entity (entity_t ent) +{ + // see if the bounding box lets us trivially reject, also + // sets trivial accept status + visibility_t *visibility = Ent_GetComponent (ent.id, scene_visibility, + ent.reg); + visibility->trivial_accept = 0; //FIXME + + alight_t lighting; + setup_lighting (ent, &lighting); + R_IQMDrawModel (ent, &lighting); +} + +void +R_DrawEntitiesOnList (entqueue_t *queue) +{ + if (!r_drawentities) + return; + + R_LowFPPrecision (); +#define RE_LOOP(type_name) \ + do { \ + for (size_t i = 0; i < queue->ent_queues[mod_##type_name].size; \ + i++) { \ + entity_t ent = queue->ent_queues[mod_##type_name].a[i]; \ + transform_t transform = Entity_Transform (ent); \ + r_entorigin = Transform_GetWorldPosition (transform); \ + draw_##type_name##_entity (ent); \ + } \ + } while (0) + + RE_LOOP (alias); + RE_LOOP (iqm); + RE_LOOP (sprite); + + R_HighFPPrecision (); } static void R_DrawViewModel (void) { // FIXME: remove and do real lighting - float lightvec[3] = { -1, 0, 0 }; int j; unsigned int lnum; vec3_t dist; float add; float minlight; dlight_t *dl; + entity_t viewent; + alight_t lighting; if (vr_data.inhibit_viewmodel - || !r_drawviewmodel->int_val - || !r_drawentities->int_val) + || !r_drawviewmodel + || !r_drawentities) return; - currententity = vr_data.view_model; - if (!currententity->model) + viewent = vr_data.view_model; + if (!Entity_Valid (viewent)) { + return; + } + + renderer_t *renderer = Ent_GetComponent (viewent.id, scene_renderer, + viewent.reg); + if (!renderer->model) return; - VectorCopy (currententity->origin, r_entorigin); - VectorSubtract (r_origin, r_entorigin, modelorg); + if (!Ent_HasComponent (viewent.id, scene_visibility, viewent.reg)) { + // ensure the view model has a visibility component because one won't + // be added automatically, and the model rendering code expects there + // to be one + Ent_SetComponent (viewent.id, scene_visibility, viewent.reg, 0); + } - VectorCopy (vup, viewlightvec); - VectorNegate (viewlightvec, viewlightvec); + transform_t transform = Entity_Transform (viewent); + VectorCopy (Transform_GetWorldPosition (transform), r_entorigin); - minlight = max (currententity->min_light, currententity->model->min_light); + VectorNegate (vup, lighting.lightvec); - j = max (R_LightPoint (currententity->origin), minlight * 128); + minlight = max (renderer->min_light, renderer->model->min_light); - r_viewlighting.ambientlight = j; - r_viewlighting.shadelight = j; + j = max (R_LightPoint (&r_refdef.worldmodel->brush, + r_entorigin), minlight * 128); + + lighting.ambientlight = j; + lighting.shadelight = j; // add dynamic lights for (lnum = 0; lnum < r_maxdlights; lnum++) { @@ -464,25 +416,23 @@ R_DrawViewModel (void) if (dl->die < vr_data.realtime) continue; - VectorSubtract (currententity->origin, dl->origin, dist); + VectorSubtract (r_entorigin, dl->origin, dist); add = dl->radius - VectorLength (dist); if (add > 0) - r_viewlighting.ambientlight += add; + lighting.ambientlight += add; } // clamp lighting so it doesn't overbright as much - if (r_viewlighting.ambientlight > 128) - r_viewlighting.ambientlight = 128; - if (r_viewlighting.ambientlight + r_viewlighting.shadelight > 192) - r_viewlighting.shadelight = 192 - r_viewlighting.ambientlight; + if (lighting.ambientlight > 128) + lighting.ambientlight = 128; + if (lighting.ambientlight + lighting.shadelight > 192) + lighting.shadelight = 192 - lighting.ambientlight; - r_viewlighting.plightvec = lightvec; - - R_AliasDrawModel (&r_viewlighting); + R_AliasDrawModel (viewent, &lighting); } static int -R_BmodelCheckBBox (model_t *clmodel, float *minmaxs) +R_BmodelCheckBBox (const vec4f_t *transform, float radius, float *minmaxs) { int i, *pindex, clipflags; vec3_t acceptpt, rejectpt; @@ -490,16 +440,15 @@ R_BmodelCheckBBox (model_t *clmodel, float *minmaxs) clipflags = 0; - if (currententity->transform[0] != 1 || currententity->transform[5] != 1 - || currententity->transform[10] != 1) { + if (transform[0][0] != 1 || transform[1][1] != 1 || transform[2][2] != 1) { for (i = 0; i < 4; i++) { - d = DotProduct (currententity->origin, view_clipplanes[i].normal); + d = DotProduct (transform[3], view_clipplanes[i].normal); d -= view_clipplanes[i].dist; - if (d <= -clmodel->radius) + if (d <= -radius) return BMODEL_FULLY_CLIPPED; - if (d <= clmodel->radius) + if (d <= radius) clipflags |= (1 << i); } } else { @@ -536,102 +485,99 @@ R_BmodelCheckBBox (model_t *clmodel, float *minmaxs) } static void -R_DrawBEntitiesOnList (void) +R_DrawBrushEntitiesOnList (entqueue_t *queue) { int j, clipflags; unsigned int k; - vec3_t oldorigin; - model_t *clmodel; + vec3_t origin; float minmaxs[6]; - entity_t *ent; - if (!r_drawentities->int_val) + if (!r_drawentities) return; - VectorCopy (modelorg, oldorigin); insubmodel = true; - for (ent = r_ent_queue; ent; ent = ent->next) { - currententity = ent; + for (size_t i = 0; i < queue->ent_queues[mod_brush].size; i++) { + entity_t ent = queue->ent_queues[mod_brush].a[i]; + uint32_t render_id = SW_AddEntity (ent); - switch (currententity->model->type) { - case mod_brush: - clmodel = currententity->model; + vec4f_t *transform = Ent_GetComponent (ent.id, scene_sw_matrix, + ent.reg); + VectorCopy (transform[3], origin); + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, + ent.reg); + model_t *model = renderer->model; - // see if the bounding box lets us trivially reject, also - // sets trivial accept status - for (j = 0; j < 3; j++) { - minmaxs[j] = currententity->origin[j] + clmodel->mins[j]; - minmaxs[3 + j] = currententity->origin[j] + - clmodel->maxs[j]; - } + // see if the bounding box lets us trivially reject, also + // sets trivial accept status + for (j = 0; j < 3; j++) { + minmaxs[j] = origin[j] + model->mins[j]; + minmaxs[3 + j] = origin[j] + model->maxs[j]; + } - clipflags = R_BmodelCheckBBox (clmodel, minmaxs); + clipflags = R_BmodelCheckBBox (transform, model->radius, minmaxs); - if (clipflags != BMODEL_FULLY_CLIPPED) { - VectorCopy (currententity->origin, r_entorigin); - VectorSubtract (r_origin, r_entorigin, modelorg); + if (clipflags != BMODEL_FULLY_CLIPPED) { + mod_brush_t *brush = &model->brush; + VectorCopy (origin, r_entorigin); + VectorSubtract (r_refdef.frame.position, r_entorigin, modelorg); - // FIXME: is this needed? - VectorCopy (modelorg, r_worldmodelorg); - r_pcurrentvertbase = clmodel->vertexes; + r_pcurrentvertbase = brush->vertexes; - // FIXME: stop transforming twice - R_RotateBmodel (); + // FIXME: stop transforming twice + R_RotateBmodel (transform); - // calculate dynamic lighting for bmodel if it's not an - // instanced model - if (clmodel->firstmodelsurface != 0) { - vec3_t lightorigin; - - for (k = 0; k < r_maxdlights; k++) { - if ((r_dlights[k].die < vr_data.realtime) || - (!r_dlights[k].radius)) continue; - - VectorSubtract (r_dlights[k].origin, - currententity->origin, - lightorigin); - R_RecursiveMarkLights (lightorigin, &r_dlights[k], - k, clmodel->nodes + - clmodel->hulls[0].firstclipnode); - } - } - // if the driver wants polygons, deliver those. - // Z-buffering is on at this point, so no clipping to the - // world tree is needed, just frustum clipping - if (r_drawpolys | r_drawculledpolys) { - R_ZDrawSubmodelPolys (clmodel); - } else { - if (currententity->topnode) { - mnode_t *topnode = currententity->topnode; - - if (topnode->contents >= 0) { - // not a leaf; has to be clipped to the world - // BSP - r_clipflags = clipflags; - R_DrawSolidClippedSubmodelPolygons (clmodel); - } else { - // falls entirely in one leaf, so we just put - // all the edges in the edge list and let 1/z - // sorting handle drawing order - R_DrawSubmodelPolygons (clmodel, clipflags); - } - - } + // calculate dynamic lighting for bmodel if it's not an + // instanced model + if (brush->firstmodelsurface != 0) { + for (k = 0; k < r_maxdlights; k++) { + if ((r_dlights[k].die < vr_data.realtime) || + (!r_dlights[k].radius)) { + continue; } - // put back world rotation and frustum clipping - // FIXME: R_RotateBmodel should just work off base_vxx - VectorCopy (base_vpn, vpn); - VectorCopy (base_vup, vup); - VectorCopy (base_vright, vright); - VectorCopy (base_modelorg, modelorg); - VectorCopy (oldorigin, modelorg); - R_TransformFrustum (); + vec4f_t lightorigin; + VectorSubtract (r_dlights[k].origin, origin, lightorigin); + lightorigin[3] = 1; + R_RecursiveMarkLights (brush, lightorigin, + &r_dlights[k], k, + brush->hulls[0].firstclipnode); } - break; - default: - break; + } + // if the driver wants polygons, deliver those. + // Z-buffering is on at this point, so no clipping to the + // world tree is needed, just frustum clipping + if (r_drawpolys | r_drawculledpolys) { + R_ZDrawSubmodelPolys (render_id, brush); + } else { + visibility_t *visibility = Ent_GetComponent (ent.id, + scene_visibility, + ent.reg); + int topnode_id = visibility->topnode_id; + mod_brush_t *world_brush = &r_refdef.worldmodel->brush; + + if (topnode_id >= 0) { + // not a leaf; has to be clipped to the world + // BSP + mnode_t *node = world_brush->nodes + topnode_id; + r_clipflags = clipflags; + R_DrawSolidClippedSubmodelPolygons (render_id, brush, node); + } else { + // falls entirely in one leaf, so we just put + // all the edges in the edge list and let 1/z + // sorting handle drawing order + mleaf_t *leaf = world_brush->leafs + ~topnode_id; + R_DrawSubmodelPolygons (render_id, brush, clipflags, leaf); + } + } + + // put back world rotation and frustum clipping + // FIXME: R_RotateBmodel should just work off base_vxx + VectorCopy (base_vfwd, vfwd); + VectorCopy (base_vup, vup); + VectorCopy (base_vright, vright); + VectorCopy (base_modelorg, modelorg); + R_TransformFrustum (); } } @@ -639,29 +585,7 @@ R_DrawBEntitiesOnList (void) } static void -R_PrintDSpeeds (void) -{ - float ms, dp_time, r_time2, rw_time, db_time, se_time, de_time, - - dv_time; - - r_time2 = Sys_DoubleTime (); - - dp_time = (dp_time2 - dp_time1) * 1000; - rw_time = (rw_time2 - rw_time1) * 1000; - db_time = (db_time2 - db_time1) * 1000; - se_time = (se_time2 - se_time1) * 1000; - de_time = (de_time2 - de_time1) * 1000; - dv_time = (dv_time2 - dv_time1) * 1000; - ms = (r_time2 - r_time1) * 1000; - - Sys_Printf ("%3i %4.1fp %3iw %4.1fb %3is %4.1fe %4.1fv\n", - (int) ms, dp_time, (int) rw_time, db_time, (int) se_time, - de_time, dv_time); -} - -static void -R_EdgeDrawing (void) +R_EdgeDrawing (entqueue_t *queue) { edge_t ledges[NUMSTACKEDGES + ((CACHE_SIZE - 1) / sizeof (edge_t)) + 1]; @@ -687,10 +611,6 @@ R_EdgeDrawing (void) R_BeginEdgeFrame (); - if (r_dspeeds->int_val) { - rw_time1 = Sys_DoubleTime (); - } - R_RenderWorld (); if (r_drawculledpolys) @@ -700,23 +620,7 @@ R_EdgeDrawing (void) // just z writes, so have the driver turn z compares on now D_TurnZOn (); - if (r_dspeeds->int_val) { - rw_time2 = Sys_DoubleTime (); - db_time1 = rw_time2; - } - - R_DrawBEntitiesOnList (); - - if (r_dspeeds->int_val) { - db_time2 = Sys_DoubleTime (); - se_time1 = db_time2; - } - - if (!r_dspeeds->int_val) { - VID_UnlockBuffer (); - S_ExtraUpdate (); // don't let sound get messed up if going slow - VID_LockBuffer (); - } + R_DrawBrushEntitiesOnList (queue); if (!(r_drawpolys | r_drawculledpolys)) R_ScanEdges (); @@ -730,99 +634,35 @@ R_EdgeDrawing (void) static void R_RenderView_ (void) { - byte warpbuffer[WARP_WIDTH * WARP_HEIGHT]; - - if (r_norefresh->int_val) + if (r_norefresh) return; + if (!r_refdef.worldmodel) { + return; + } - r_warpbuffer = warpbuffer; - - if (r_timegraph->int_val || r_speeds->int_val || r_dspeeds->int_val) - r_time1 = Sys_DoubleTime (); - + reset_sw_components (r_refdef.registry); + *(mod_brush_t **) SW_COMP (scene_sw_brush, 0) = &r_refdef.worldmodel->brush; R_SetupFrame (); - R_MarkLeaves (); // done here so we know if we're in water - - R_PushDlights (vec3_origin); - // make FDIV fast. This reduces timing precision after we've been running for a // while, so we don't do it globally. This also sets chop mode, and we do it // here so that setup stuff like the refresh area calculations match what's // done in screen.c R_LowFPPrecision (); - if (!r_worldentity.model) - Sys_Error ("R_RenderView: NULL worldmodel"); + R_EdgeDrawing (r_ent_queue); - if (!r_dspeeds->int_val) { - VID_UnlockBuffer (); - S_ExtraUpdate (); // don't let sound get messed up if going slow - VID_LockBuffer (); + if (Entity_Valid (vr_data.view_model)) { + R_DrawViewModel (); } - R_EdgeDrawing (); - - if (!r_dspeeds->int_val) { - VID_UnlockBuffer (); - S_ExtraUpdate (); // don't let sound get messed up if going slow - VID_LockBuffer (); - } - - if (r_dspeeds->int_val) { - se_time2 = Sys_DoubleTime (); - de_time1 = se_time2; - } - - R_DrawEntitiesOnList (); - - if (r_dspeeds->int_val) { - de_time2 = Sys_DoubleTime (); - dv_time1 = de_time2; - } - - R_DrawViewModel (); - - if (r_dspeeds->int_val) { - dv_time2 = Sys_DoubleTime (); - dp_time1 = Sys_DoubleTime (); - } - - R_DrawParticles (); - - if (r_dspeeds->int_val) - dp_time2 = Sys_DoubleTime (); - - if (r_dowarp) - D_WarpScreen (); - - if (r_timegraph->int_val) - R_TimeGraph (); - - if (r_zgraph->int_val) - R_ZGraph (); - - if (r_aliasstats->int_val) + if (r_aliasstats) R_PrintAliasStats (); - if (r_speeds->int_val) - R_PrintTimes (); - - if (r_dspeeds->int_val) - R_PrintDSpeeds (); - - if (r_reportsurfout->int_val && r_outofsurfaces) - Sys_Printf ("Short %d surfaces\n", r_outofsurfaces); - - if (r_reportedgeout->int_val && r_outofedges) - Sys_Printf ("Short roughly %d edges\n", r_outofedges * 2 / 3); - // back to high floating-point precision R_HighFPPrecision (); } -static void R_RenderViewFishEye (void); - void R_RenderView (void) { @@ -833,19 +673,16 @@ R_RenderView (void) if (delta < -10000 || delta > 10000) Sys_Error ("R_RenderView: called without enough stack"); - if (Hunk_LowMark () & 3) + if (Hunk_LowMark (0) & 3) Sys_Error ("Hunk is missaligned"); if ((intptr_t) (&dummy) & 3) Sys_Error ("Stack is missaligned"); - if ((intptr_t) (&r_warpbuffer) & 3) + if ((intptr_t) (&r_colormap) & 3) Sys_Error ("Globals are missaligned"); - if (!scr_fisheye->int_val) - R_RenderView_ (); - else - R_RenderViewFishEye (); + R_RenderView_ (); } void @@ -860,296 +697,10 @@ R_InitTurb (void) } } -#define BOX_FRONT 0 -#define BOX_BEHIND 2 -#define BOX_LEFT 3 -#define BOX_RIGHT 1 -#define BOX_TOP 4 -#define BOX_BOTTOM 5 - -#define DEG(x) (x / M_PI * 180.0) -#define RAD(x) (x * M_PI / 180.0) - -struct my_coords -{ - double x, y, z; -}; - -struct my_angles -{ - double yaw, pitch, roll; -}; - -static void -x_rot (struct my_coords *c, double pitch) -{ - double nx, ny, nz; - - nx = c->x; - ny = (c->y * cos(pitch)) - (c->z * sin(pitch)); - nz = (c->y * sin(pitch)) + (c->z * cos(pitch)); - - c->x = nx; c->y = ny; c->z = nz; -} - -static void -y_rot (struct my_coords *c, double yaw) -{ - double nx, ny, nz; - - nx = (c->x * cos(yaw)) - (c->z * sin(yaw)); - ny = c->y; - nz = (c->x * sin(yaw)) + (c->z * cos(yaw)); - - c->x = nx; c->y = ny; c->z = nz; -} - -static void -z_rot (struct my_coords *c, double roll) -{ - double nx, ny, nz; - - nx = (c->x * cos(roll)) - (c->y * sin(roll)); - ny = (c->x * sin(roll)) + (c->y * cos(roll)); - nz = c->z; - - c->x = nx; c->y = ny; c->z = nz; -} - -static void -my_get_angles (struct my_coords *in_o, struct my_coords *in_u, struct my_angles *a) -{ - double rad_yaw, rad_pitch; - struct my_coords o, u; - - a->pitch = 0.0; - a->yaw = 0.0; - a->roll = 0.0; - - // make a copy of the coords - o.x = in_o->x; o.y = in_o->y; o.z = in_o->z; - u.x = in_u->x; u.y = in_u->y; u.z = in_u->z; - - // special case when looking straight up or down - if ((o.x == 0.0) && (o.z == 0.0)) { - a->yaw = 0.0; - if (o.y > 0.0) { a->pitch = -90.0; a->roll = 180.0 - DEG(atan2(u.x, u.z)); } // down - else { a->pitch = 90.0; a->roll = DEG(atan2(u.x, u.z)); } // up - return; - } - - // get yaw angle and then rotate o and u so that yaw = 0 - rad_yaw = atan2 (-o.x, o.z); - a->yaw = DEG (rad_yaw); - - y_rot (&o, -rad_yaw); - y_rot (&u, -rad_yaw); - - // get pitch and then rotate o and u so that pitch = 0 - rad_pitch = atan2 (-o.y, o.z); - a->pitch = DEG (rad_pitch); - - x_rot (&o, -rad_pitch); - x_rot (&u, -rad_pitch); - - // get roll - a->roll = DEG (-atan2(u.x, u.y)); -} - -static void -get_ypr (double yaw, double pitch, double roll, int side, struct my_angles *a) -{ - struct my_coords o, u; - - // get 'o' (observer) and 'u' ('this_way_up') depending on box side - switch(side) { - case BOX_FRONT: - o.x = 0.0; o.y = 0.0; o.z = 1.0; - u.x = 0.0; u.y = 1.0; u.z = 0.0; - break; - case BOX_BEHIND: - o.x = 0.0; o.y = 0.0; o.z = -1.0; - u.x = 0.0; u.y = 1.0; u.z = 0.0; - break; - case BOX_LEFT: - o.x = -1.0; o.y = 0.0; o.z = 0.0; - u.x = -1.0; u.y = 1.0; u.z = 0.0; - break; - case BOX_RIGHT: - o.x = 1.0; o.y = 0.0; o.z = 0.0; - u.x = 0.0; u.y = 1.0; u.z = 0.0; - break; - case BOX_TOP: - o.x = 0.0; o.y = -1.0; o.z = 0.0; - u.x = 0.0; u.y = 0.0; u.z = -1.0; - break; - case BOX_BOTTOM: - o.x = 0.0; o.y = 1.0; o.z = 0.0; - u.x = 0.0; u.y = 0.0; u.z = -1.0; - break; - } - z_rot (&o, roll); z_rot (&u, roll); - x_rot (&o, pitch); x_rot (&u, pitch); - y_rot (&o, yaw); y_rot (&u, yaw); - - my_get_angles (&o, &u, a); - - // normalise angles - while (a->yaw < 0.0) a->yaw += 360.0; - while (a->yaw > 360.0) a->yaw -= 360.0; - while (a->pitch < 0.0) a->pitch += 360.0; - while (a->pitch > 360.0) a->pitch -= 360.0; - while (a->roll < 0.0) a->roll += 360.0; - while (a->roll > 360.0) a->roll -= 360.0; -} - -static void -fisheyelookuptable (byte **buf, int width, int height, byte *scrp, double fov) -{ - int x, y; - - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - double dx = x-width/2; - double dy = -(y-height/2); - double yaw = sqrt(dx*dx+dy*dy)*fov/((double)width); - double roll = -atan2(dy, dx); - double sx = sin(yaw) * cos(roll); - double sy = sin(yaw) * sin(roll); - double sz = cos(yaw); - - // determine which side of the box we need - double abs_x = fabs(sx); - double abs_y = fabs(sy); - double abs_z = fabs(sz); - int side; - double xs = 0, ys = 0; - if (abs_x > abs_y) { - if (abs_x > abs_z) { side = ((sx > 0.0) ? BOX_RIGHT : BOX_LEFT); } - else { side = ((sz > 0.0) ? BOX_FRONT : BOX_BEHIND); } - } else { - if (abs_y > abs_z) { side = ((sy > 0.0) ? BOX_TOP : BOX_BOTTOM); } - else { side = ((sz > 0.0) ? BOX_FRONT : BOX_BEHIND); } - } - - #define RC(x) ((x / 2.06) + 0.5) - #define R2(x) ((x / 2.03) + 0.5) - - // scale up our vector [x,y,z] to the box - switch(side) { - case BOX_FRONT: xs = RC( sx / sz); ys = R2( sy / sz); break; - case BOX_BEHIND: xs = RC(-sx / -sz); ys = R2( sy / -sz); break; - case BOX_LEFT: xs = RC( sz / -sx); ys = R2( sy / -sx); break; - case BOX_RIGHT: xs = RC(-sz / sx); ys = R2( sy / sx); break; - case BOX_TOP: xs = RC( sx / sy); ys = R2( sz / -sy); break; //bot - case BOX_BOTTOM: xs = RC(-sx / sy); ys = R2( sz / -sy); break; //top?? - } - - if (xs < 0.0) xs = 0.0; - if (xs >= 1.0) xs = 0.999; - if (ys < 0.0) ys = 0.0; - if (ys >= 1.0) ys = 0.999; - *buf++ = scrp+(((int)(xs*(double)width))+ - ((int)(ys*(double)height))*width)+ - side*width*height; - } - } -} - -static void -rendercopy (void *dest) -{ - void *p = vid.buffer; - // XXX - vid.buffer = dest; - R_RenderView_ (); - vid.buffer = p; -} - -static void -renderside (byte* bufs, double yaw, double pitch, double roll, int side) -{ - struct my_angles a; - - get_ypr (RAD(yaw), RAD(pitch), RAD(roll), side, &a); - if (side == BOX_RIGHT) { a.roll = -a.roll; a.pitch = -a.pitch; } - if (side == BOX_LEFT) { a.roll = -a.roll; a.pitch = -a.pitch; } - if (side == BOX_TOP) { a.yaw += 180.0; a.pitch = 180.0 - a.pitch; } - r_refdef.viewangles[YAW] = a.yaw; - r_refdef.viewangles[PITCH] = a.pitch; - r_refdef.viewangles[ROLL] = a.roll; - rendercopy (bufs); -} - -static void -renderlookup (byte **offs, byte* bufs) -{ - byte *p = (byte*)vid.buffer; - int x, y; - for (y = 0; y < vid.height; y++) { - for (x = 0; x < vid.width; x++, offs++) - p[x] = **offs; - p += vid.rowbytes; - } -} - -static void -R_RenderViewFishEye (void) -{ - int width = vid.width; //r_refdef.vrect.width; - int height = vid.height; //r_refdef.vrect.height; - int scrsize = width*height; - int fov = scr_ffov->int_val; - int views = scr_fviews->int_val; - double yaw = r_refdef.viewangles[YAW]; - double pitch = r_refdef.viewangles[PITCH]; - double roll = 0; //r_refdef.viewangles[ROLL]; - static int pwidth = -1; - static int pheight = -1; - static int pfov = -1; - static int pviews = -1; - static byte *scrbufs = NULL; - static byte **offs = NULL; - - if (fov < 1) fov = 1; - - if (pwidth != width || pheight != height || pfov != fov) { - if (scrbufs) free (scrbufs); - if (offs) free (offs); - scrbufs = malloc (scrsize*6); // front|right|back|left|top|bottom - SYS_CHECKMEM (scrbufs); - offs = malloc (scrsize*sizeof(byte*)); - SYS_CHECKMEM (offs); - pwidth = width; - pheight = height; - pfov = fov; - fisheyelookuptable (offs, width, height, scrbufs, ((double)fov)*M_PI/180.0); - } - - if (views != pviews) { - pviews = views; - memset (scrbufs, 0, scrsize*6); - } - - switch (views) { - case 6: renderside (scrbufs+scrsize*2, yaw, pitch, roll, BOX_BEHIND); - case 5: renderside (scrbufs+scrsize*5, yaw, pitch, roll, BOX_BOTTOM); - case 4: renderside (scrbufs+scrsize*4, yaw, pitch, roll, BOX_TOP); - case 3: renderside (scrbufs+scrsize*3, yaw, pitch, roll, BOX_LEFT); - case 2: renderside (scrbufs+scrsize, yaw, pitch, roll, BOX_RIGHT); - default: renderside (scrbufs, yaw, pitch, roll, BOX_FRONT); - } - - r_refdef.viewangles[YAW] = yaw; - r_refdef.viewangles[PITCH] = pitch; - r_refdef.viewangles[ROLL] = roll; - renderlookup (offs, scrbufs); -} - void R_ClearState (void) { - R_ClearEfrags (); + r_refdef.worldmodel = 0; R_ClearDlights (); R_ClearParticles (); } diff --git a/libs/video/renderer/sw/sw_rmisc.c b/libs/video/renderer/sw/sw_rmisc.c index f30200ffa..47818af22 100644 --- a/libs/video/renderer/sw/sw_rmisc.c +++ b/libs/video/renderer/sw/sw_rmisc.c @@ -33,16 +33,14 @@ #include "QF/draw.h" #include "QF/render.h" #include "QF/sys.h" +#include "QF/ui/view.h" + +#include "QF/scene/entity.h" #include "compat.h" #include "r_internal.h" #include "vid_internal.h" - - -static void -R_CheckVariables (void) -{ -} +#include "vid_sw.h" /* R_TimeRefresh_f @@ -52,6 +50,7 @@ R_CheckVariables (void) void R_TimeRefresh_f (void) { +/* FIXME update for simd int i; float start, stop, time; int startangle; @@ -63,24 +62,21 @@ R_TimeRefresh_f (void) for (i = 0; i < 128; i++) { r_refdef.viewangles[1] = i / 128.0 * 360.0; - VID_LockBuffer (); - R_RenderView (); - VID_UnlockBuffer (); - vr.x = r_refdef.vrect.x; vr.y = r_refdef.vrect.y; vr.width = r_refdef.vrect.width; vr.height = r_refdef.vrect.height; vr.next = NULL; - VID_Update (&vr); + sw_ctx->update (&vr); } stop = Sys_DoubleTime (); time = stop - start; Sys_Printf ("%g seconds (%g fps)\n", time, 128 / time); r_refdef.viewangles[1] = startangle; +*/ } void @@ -126,9 +122,9 @@ R_TransformFrustum (void) v[1] = -screenedge[i].normal[0]; v[2] = screenedge[i].normal[1]; - v2[0] = v[1] * vright[0] + v[2] * vup[0] + v[0] * vpn[0]; - v2[1] = v[1] * vright[1] + v[2] * vup[1] + v[0] * vpn[1]; - v2[2] = v[1] * vright[2] + v[2] * vup[2] + v[0] * vpn[2]; + v2[0] = v[1] * vright[0] + v[2] * vup[0] + v[0] * vfwd[0]; + v2[1] = v[1] * vright[1] + v[2] * vup[1] + v[0] * vfwd[1]; + v2[2] = v[1] * vright[2] + v[2] * vup[2] + v[0] * vfwd[2]; VectorCopy (v2, view_clipplanes[i].normal); @@ -146,21 +142,10 @@ TransformVector (const vec3_t in, vec3_t out) { out[0] = DotProduct (in, vright); out[1] = DotProduct (in, vup); - out[2] = DotProduct (in, vpn); + out[2] = DotProduct (in, vfwd); } #endif -void -R_TransformPlane (plane_t *p, float *normal, float *dist) -{ - float d; - - d = DotProduct (r_origin, p->normal); - *dist = p->dist - d; -// TODO: when we have rotating entities, this will need to use the view matrix - TransformVector (p->normal, normal); -} - static void R_SetUpFrustumIndexes (void) { @@ -188,119 +173,28 @@ R_SetUpFrustumIndexes (void) void R_SetupFrame (void) { - int edgecount; - vrect_t vrect; - float w, h; - - // don't allow cheats in multiplayer - Cvar_SetValue (r_ambient, 0); - Cvar_SetValue (r_drawflat, 0); - - if (r_numsurfs->int_val) { - if ((surface_p - surfaces) > r_maxsurfsseen) - r_maxsurfsseen = surface_p - surfaces; - - Sys_Printf ("Used %ld of %ld surfs; %d max\n", - (long)(surface_p - surfaces), - (long)(surf_max - surfaces), r_maxsurfsseen); - } - - if (r_numedges->int_val) { - edgecount = edge_p - r_edges; - - if (edgecount > r_maxedgesseen) - r_maxedgesseen = edgecount; - - Sys_Printf ("Used %d of %d edges; %d max\n", edgecount, - r_numallocatededges, r_maxedgesseen); - } - - r_refdef.ambientlight = max (r_ambient->value, 0); - - R_CheckVariables (); - - R_AnimateLight (); - R_ClearEnts (); - r_framecount++; - numbtofpolys = 0; - // debugging -#if 0 - r_refdef.vieworg[0] = 80; - r_refdef.vieworg[1] = 64; - r_refdef.vieworg[2] = 40; - r_refdef.viewangles[0] = 0; - r_refdef.viewangles[1] = 46.763641357; - r_refdef.viewangles[2] = 0; -#endif - // build the transformation matrix for the given view angles - VectorCopy (r_refdef.vieworg, modelorg); - VectorCopy (r_refdef.vieworg, r_origin); + VectorCopy (r_refdef.frame.position, modelorg); - AngleVectors (r_refdef.viewangles, vpn, vright, vup); - R_SetFrustum (); + VectorCopy (r_refdef.frame.right, vright); + VectorCopy (r_refdef.frame.forward, vfwd); + VectorCopy (r_refdef.frame.up, vup); - // current viewleaf - r_viewleaf = Mod_PointInLeaf (r_origin, r_worldentity.model); - - r_dowarpold = r_dowarp; - r_dowarp = r_waterwarp->int_val && (r_viewleaf->contents <= - CONTENTS_WATER); - - if ((r_dowarp != r_dowarpold) || r_viewchanged) { - if (r_dowarp) { - if ((vid.width <= WARP_WIDTH) - && (vid.height <= WARP_HEIGHT)) { - vrect.x = 0; - vrect.y = 0; - vrect.width = vid.width; - vrect.height = vid.height; - - R_SetVrect (&vrect, &r_refdef.vrect, vr_data.lineadj); - R_ViewChanged (vid.aspect); - } else { - w = vid.width; - h = vid.height; - - if (w > WARP_WIDTH) { - h *= (float) WARP_WIDTH / w; - w = WARP_WIDTH; - } - - if (h > WARP_HEIGHT) { - h = WARP_HEIGHT; - w *= (float) WARP_HEIGHT / h; - } - - vrect.x = 0; - vrect.y = 0; - vrect.width = (int) w; - vrect.height = (int) h; - - R_SetVrect (&vrect, &r_refdef.vrect, - (int) ((float) vr_data.lineadj * - (h / (float) vid.height))); - R_ViewChanged (vid.aspect * (h / w) * ((float) vid.width / - (float) vid.height)); - } - } else { - r_refdef.vrect = scr_vrect; - R_ViewChanged (vid.aspect); - } - - r_viewchanged = false; - } // start off with just the four screen edge clip planes R_TransformFrustum (); // save base values - VectorCopy (vpn, base_vpn); + VectorCopy (vfwd, base_vfwd); VectorCopy (vright, base_vright); VectorCopy (vup, base_vup); VectorCopy (modelorg, base_modelorg); + VectorCopy (vright, r_viewmatrix[0]); + VectorNegate (vup, r_viewmatrix[1]); + VectorCopy (vfwd, r_viewmatrix[2]); + R_SetSkyFrame (); R_SetUpFrustumIndexes (); diff --git a/libs/video/renderer/sw/sw_rpart.c b/libs/video/renderer/sw/sw_rpart.c index 71ee1fcf8..66f623165 100644 --- a/libs/video/renderer/sw/sw_rpart.c +++ b/libs/video/renderer/sw/sw_rpart.c @@ -44,864 +44,32 @@ #include "QF/sys.h" #include "QF/va.h" +#include "QF/scene/entity.h" + #include "compat.h" #include "r_internal.h" -static int ramp1[8] = { 0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61 }; -//static int ramp2[8] = { 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66 }; -static int ramp3[8] = { 0x6d, 0x6b, 6, 5, 4, 3 }; - -static mtstate_t mt; // private PRNG state +vec3_t r_pright, r_pup, r_ppn, r_porigin; void -R_InitParticles (void) +R_DrawParticles (psystem_t *psystem) { - R_LoadParticles (); - mtwist_seed (&mt, 0xdeadbeef); -} - -void -R_ClearParticles (void) -{ - unsigned int i; - - free_particles = &particles[0]; - active_particles = NULL; - - for (i = 0; i < r_maxparticles; i++) - particles[i].next = &particles[i + 1]; - if (r_maxparticles) - particles[r_maxparticles - 1].next = NULL; -} - -void -R_ReadPointFile_f (void) -{ - QFile *f; - vec3_t org; - int c, r; - particle_t *p; - const char *name; - char *mapname; - - mapname = strdup (r_worldentity.model->name); - if (!mapname) - Sys_Error ("Can't duplicate mapname!"); - QFS_StripExtension (mapname, mapname); - - name = va ("maps/%s.pts", mapname); - free (mapname); - - f = QFS_FOpenFile (name); - if (!f) { - Sys_Printf ("couldn't open %s\n", name); + if (!psystem->numparticles) { return; } - - Sys_Printf ("Reading %s...\n", name); - c = 0; - for (;;) { - char buf[64]; - - Qgets (f, buf, sizeof (buf)); - r = sscanf (buf, "%f %f %f\n", &org[0], &org[1], &org[2]); - if (r != 3) - break; - c++; - - if (!free_particles) { - Sys_Printf ("Not enough free particles\n"); - break; - } - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = 99999; - p->color = (-c) & 15; - p->physics = R_ParticlePhysics ("pt_static"); - VectorZero (p->vel); - VectorCopy (org, p->org); - } - - Qclose (f); - Sys_Printf ("%i points read\n", c); -} - -static void -R_ParticleExplosion_QF (const vec3_t org) -{ - int i, j; - particle_t *p; - - if (!r_particles->int_val) - return; - - for (i = 0; i < 1024; i++) { - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = vr_data.realtime + 5; - p->color = ramp1[0]; - p->ramp = mtwist_rand (&mt) & 3; - if (i & 1) - p->physics = R_ParticlePhysics ("pt_explode"); - else - p->physics = R_ParticlePhysics ("pt_explode2"); - for (j = 0; j < 3; j++) { - p->org[j] = org[j] + ((mtwist_rand (&mt) % 32) - 16); - p->vel[j] = (mtwist_rand (&mt) % 512) - 256; - } - } -} - -static void -R_ParticleExplosion2_QF (const vec3_t org, int colorStart, int colorLength) -{ - int i, j; - int colorMod = 0; - particle_t *p; - - for (i=0; i<512; i++) { - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = vr_data.realtime + 0.3; - p->color = colorStart + (colorMod % colorLength); - colorMod++; - - p->physics = R_ParticlePhysics ("pt_blob"); - for (j=0 ; j<3 ; j++) { - p->org[j] = org[j] + ((mtwist_rand (&mt)%32)-16); - p->vel[j] = (mtwist_rand (&mt)%512)-256; - } - } -} - -static void -R_BlobExplosion_QF (const vec3_t org) -{ - int i, j; - particle_t *p; - - if (!r_particles->int_val) - return; - - for (i = 0; i < 1024; i++) { - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = vr_data.realtime + 1 + (mtwist_rand (&mt) & 8) * 0.05; - - if (i & 1) { - p->color = 66 + mtwist_rand (&mt) % 6; - p->physics = R_ParticlePhysics ("pt_blob"); - } else { - p->color = 150 + mtwist_rand (&mt) % 6; - p->physics = R_ParticlePhysics ("pt_blob2"); - } - for (j = 0; j < 3; j++) { - p->org[j] = org[j] + ((mtwist_rand (&mt) % 32) - 16); - p->vel[j] = (mtwist_rand (&mt) % 512) - 256; - } - } -} - -static void -R_RunParticleEffect_QF (const vec3_t org, const vec3_t dir, int color, - int count) -{ - int i, j; - particle_t *p; - - if (!r_particles->int_val) - return; - - for (i = 0; i < count; i++) { - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = vr_data.realtime + 0.1 * (mtwist_rand (&mt) % 5); - p->color = (color & ~7) + (mtwist_rand (&mt) & 7); - p->physics = R_ParticlePhysics ("pt_grav"); - for (j = 0; j < 3; j++) { - p->org[j] = org[j] + ((mtwist_rand (&mt) & 15) - 8); - p->vel[j] = dir[j]; // + (mtwist_rand (&mt)%300)-150; - } - } -} - -static void -R_SpikeEffect_QF (const vec3_t org) -{ - R_RunParticleEffect_QF (org, vec3_origin, 0, 10); -} - -static void -R_SuperSpikeEffect_QF (const vec3_t org) -{ - R_RunParticleEffect_QF (org, vec3_origin, 0, 20); -} - -static void -R_KnightSpikeEffect_QF (const vec3_t org) -{ - R_RunParticleEffect_QF (org, vec3_origin, 226, 20); -} - -static void -R_WizSpikeEffect_QF (const vec3_t org) -{ - R_RunParticleEffect_QF (org, vec3_origin, 20, 30); -} - -static void -R_BloodPuffEffect_QF (const vec3_t org, int count) -{ - R_RunParticleEffect_QF (org, vec3_origin, 73, count); -} - -static void -R_GunshotEffect_QF (const vec3_t org, int count) -{ - R_RunParticleEffect_QF (org, vec3_origin, 0, count); -} - -static void -R_LightningBloodEffect_QF (const vec3_t org) -{ - R_RunParticleEffect_QF (org, vec3_origin, 225, 50); -} - -static void -R_LavaSplash_QF (const vec3_t org) -{ - int i, j, k; - particle_t *p; - float vel; - vec3_t dir; - - if (!r_particles->int_val) - return; - - for (i = -16; i < 16; i++) - for (j = -16; j < 16; j++) - for (k = 0; k < 1; k++) { - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = vr_data.realtime + 2 + (mtwist_rand (&mt) & 31) * 0.02; - p->color = 224 + (mtwist_rand (&mt) & 7); - p->physics = R_ParticlePhysics ("pt_grav"); - - dir[0] = j * 8 + (mtwist_rand (&mt) & 7); - dir[1] = i * 8 + (mtwist_rand (&mt) & 7); - dir[2] = 256; - - p->org[0] = org[0] + dir[0]; - p->org[1] = org[1] + dir[1]; - p->org[2] = org[2] + (mtwist_rand (&mt) & 63); - - VectorNormalize (dir); - vel = 50 + (mtwist_rand (&mt) & 63); - VectorScale (dir, vel, p->vel); - } -} - -static void -R_TeleportSplash_QF (const vec3_t org) -{ - int i, j, k; - particle_t *p; - float vel; - vec3_t dir; - - if (!r_particles->int_val) - return; - - for (i = -16; i < 16; i += 4) - for (j = -16; j < 16; j += 4) - for (k = -24; k < 32; k += 4) { - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = vr_data.realtime + 0.2 + (mtwist_rand (&mt) & 7) * 0.02; - p->color = 7 + (mtwist_rand (&mt) & 7); - p->physics = R_ParticlePhysics ("pt_grav"); - - dir[0] = j * 8; - dir[1] = i * 8; - dir[2] = k * 8; - - p->org[0] = org[0] + i + (mtwist_rand (&mt) & 3); - p->org[1] = org[1] + j + (mtwist_rand (&mt) & 3); - p->org[2] = org[2] + k + (mtwist_rand (&mt) & 3); - - VectorNormalize (dir); - vel = 50 + (mtwist_rand (&mt) & 63); - VectorScale (dir, vel, p->vel); - } -} - -static void -R_DarkFieldParticles_ID (entity_t *ent) -{ - int i, j, k; - unsigned int rnd; - float vel; - particle_t *p; - vec3_t dir, org; - - if (!r_particles->int_val) - return; - - org[0] = ent->origin[0]; - org[1] = ent->origin[1]; - org[2] = ent->origin[2]; - for (i = -16; i < 16; i += 8) { - for (j = -16; j < 16; j += 8) { - for (k = 0; k < 32; k += 8) { - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - rnd = mtwist_rand (&mt); - - p->die = vr_data.realtime + 0.2 + (rnd & 7) * 0.02; - p->color = 150 + mtwist_rand (&mt) % 6; - p->physics = R_ParticlePhysics ("pt_slowgrav"); - dir[0] = j * 8; - dir[1] = i * 8; - dir[2] = k * 8; - - p->org[0] = org[0] + i + ((rnd >> 3) & 3); - p->org[1] = org[1] + j + ((rnd >> 5) & 3); - p->org[2] = org[2] + k + ((rnd >> 7) & 3); - - VectorNormalize (dir); - vel = 50 + ((rnd >> 9) & 63); - VectorScale (dir, vel, p->vel); - } - } - } -} - -static vec3_t avelocities[NUMVERTEXNORMALS]; - -static void -R_EntityParticles_ID (entity_t *ent) -{ - int i; - float angle, sp, sy, cp, cy; // cr, sr - float beamlength = 16.0, dist = 64.0; - particle_t *p; - vec3_t forward; - - if (!r_particles->int_val) - return; - - for (i = 0; i < NUMVERTEXNORMALS; i++) { - int k; - for (k = 0; k < 3; k++) { - avelocities[i][k] = (mtwist_rand (&mt) & 255) * 0.01; - } - } - - for (i = 0; i < NUMVERTEXNORMALS; i++) { - angle = vr_data.realtime * avelocities[i][0]; - cy = cos (angle); - sy = sin (angle); - angle = vr_data.realtime * avelocities[i][1]; - cp = cos (angle); - sp = sin (angle); -// Next 3 lines results aren't currently used, may be in future. --Despair -// angle = vr_data.realtime * avelocities[i][2]; -// sr = sin (angle); -// cr = cos (angle); - - forward[0] = cp * cy; - forward[1] = cp * sy; - forward[2] = -sp; - - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = vr_data.realtime + 0.01; - p->color = 0x6f; - p->physics = R_ParticlePhysics ("pt_explode"); - - p->org[0] = ent->origin[0] + r_avertexnormals[i][0] * dist + - forward[0] * beamlength; - p->org[1] = ent->origin[1] + r_avertexnormals[i][1] * dist + - forward[1] * beamlength; - p->org[2] = ent->origin[2] + r_avertexnormals[i][2] * dist + - forward[2] * beamlength; - } -} - -static void -R_RocketTrail_QF (entity_t *ent) -{ - float len; - int j; - particle_t *p; - vec3_t old_origin, vec; - - if (!r_particles->int_val) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - len = VectorNormalize (vec); - - while (len > 0) { - len -= 3; - - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - VectorZero (p->vel); - - p->die = vr_data.realtime + 2; - p->ramp = (mtwist_rand (&mt) & 3); - p->color = ramp3[(int) p->ramp]; - p->physics = R_ParticlePhysics ("pt_fire"); - for (j = 0; j < 3; j++) - p->org[j] = old_origin[j] + ((mtwist_rand (&mt) % 6) - 3); - - VectorAdd (old_origin, vec, old_origin); - } -} - -static void -R_GrenadeTrail_QF (entity_t *ent) -{ - float len; - int j; - particle_t *p; - vec3_t old_origin, vec; - - if (!r_particles->int_val) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - len = VectorNormalize (vec); - - while (len > 0) { - len -= 3; - - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - VectorZero (p->vel); - - p->die = vr_data.realtime + 2; - p->ramp = (mtwist_rand (&mt) & 3) + 2; - p->color = ramp3[(int) p->ramp]; - p->physics = R_ParticlePhysics ("pt_fire"); - for (j = 0; j < 3; j++) - p->org[j] = old_origin[j] + ((mtwist_rand (&mt) % 6) - 3); - - VectorAdd (old_origin, vec, old_origin); - } -} - -static void -R_BloodTrail_QF (entity_t *ent) -{ - float len; - int j; - particle_t *p; - vec3_t old_origin, vec; - - if (!r_particles->int_val) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - len = VectorNormalize (vec); - - while (len > 0) { - len -= 3; - - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - VectorZero (p->vel); - - p->die = vr_data.realtime + 2; - p->physics = R_ParticlePhysics ("pt_slowgrav"); - p->color = 67 + (mtwist_rand (&mt) & 3); - for (j = 0; j < 3; j++) - p->org[j] = old_origin[j] + ((mtwist_rand (&mt) % 6) - 3); - break; - - VectorAdd (old_origin, vec, old_origin); - } -} - -static void -R_SlightBloodTrail_QF (entity_t *ent) -{ - float len; - int j; - particle_t *p; - vec3_t old_origin, vec; - - if (!r_particles->int_val) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - len = VectorNormalize (vec); - - while (len > 0) { - len -= 6; - - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - VectorZero (p->vel); - - p->die = vr_data.realtime + 2; - p->physics = R_ParticlePhysics ("pt_slowgrav"); - p->color = 67 + (mtwist_rand (&mt) & 3); - for (j = 0; j < 3; j++) - p->org[j] = old_origin[j] + ((mtwist_rand (&mt) % 6) - 3); - - VectorAdd (old_origin, vec, old_origin); - } -} - -static void -R_WizTrail_QF (entity_t *ent) -{ - float len; - particle_t *p; - vec3_t old_origin, vec; - - if (!r_particles->int_val) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - len = VectorNormalize (vec); - - while (len > 0) { - static int tracercount; - - len -= 3; - - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = vr_data.realtime + 0.5; - p->physics = R_ParticlePhysics ("pt_static"); - p->color = 52 + ((tracercount & 4) << 1); - - tracercount++; - - VectorCopy (old_origin, p->org); - if (tracercount & 1) { - p->vel[0] = 30.0 * vec[1]; - p->vel[1] = 30.0 * -vec[0]; - } else { - p->vel[0] = 30.0 * -vec[1]; - p->vel[1] = 30.0 * vec[0]; - } - p->vel[2] = 0.0; - - VectorAdd (old_origin, vec, old_origin); - } -} - -static void -R_FlameTrail_QF (entity_t *ent) -{ - float len; - particle_t *p; - vec3_t old_origin, vec; - - if (!r_particles->int_val) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - len = VectorNormalize (vec); - - while (len > 0) { - static int tracercount; - - len -= 3; - - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = vr_data.realtime + 0.5; - p->physics = R_ParticlePhysics ("pt_static"); - p->color = 230 + ((tracercount & 4) << 1); - - tracercount++; - - VectorCopy (old_origin, p->org); - if (tracercount & 1) { - p->vel[0] = 30 * vec[1]; - p->vel[1] = 30 * -vec[0]; - } else { - p->vel[0] = 30 * -vec[1]; - p->vel[1] = 30 * vec[0]; - } - p->vel[2] = 0.0; - - VectorAdd (old_origin, vec, old_origin); - } -} - -static void -R_VoorTrail_QF (entity_t *ent) -{ - float len; - int j; - particle_t *p; - vec3_t old_origin, vec; - - if (!r_particles->int_val) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - len = VectorNormalize (vec); - - while (len > 0) { - len -= 3; - - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - VectorZero (p->vel); - - p->die = vr_data.realtime + 0.3; - p->physics = R_ParticlePhysics ("pt_static"); - p->color = 9 * 16 + 8 + (mtwist_rand (&mt) & 3); - for (j = 0; j < 3; j++) - p->org[j] = old_origin[j] + ((mtwist_rand (&mt) & 15) - 8); - - VectorAdd (old_origin, vec, old_origin); - } -} - -void -R_DrawParticles (void) -{ - particle_t *p, **particle; - VectorScale (vright, xscaleshrink, r_pright); VectorScale (vup, yscaleshrink, r_pup); - VectorCopy (vpn, r_ppn); + VectorCopy (vfwd, r_ppn); + VectorCopy (r_refdef.frame.position, r_porigin); - for (particle = &active_particles; *particle;) { - if ((*particle)->die < vr_data.realtime) { - p = (*particle)->next; - (*particle)->next = free_particles; - free_particles = (*particle); - (*particle) = p; - } else { - p = *particle; - particle = &(*particle)->next; - - D_DrawParticle (p); - - R_RunParticlePhysics (p); - } + for (unsigned i = 0; i < psystem->numparticles; i++) { + particle_t *p = &psystem->particles[i]; + D_DrawParticle (p); } } -void -r_easter_eggs_f (cvar_t *var) +psystem_t * __attribute__((const))//FIXME +sw_ParticleSystem (void) { -} - -void -r_particles_style_f (cvar_t *var) -{ -} - -static vid_particle_funcs_t particles_QF = { - R_RocketTrail_QF, - R_GrenadeTrail_QF, - R_BloodTrail_QF, - R_SlightBloodTrail_QF, - R_WizTrail_QF, - R_FlameTrail_QF, - R_VoorTrail_QF, - 0,//R_GlowTrail_QF, - R_RunParticleEffect_QF, - R_BloodPuffEffect_QF, - R_GunshotEffect_QF, - R_LightningBloodEffect_QF, - R_SpikeEffect_QF, - R_KnightSpikeEffect_QF, - R_SuperSpikeEffect_QF, - R_WizSpikeEffect_QF, - R_BlobExplosion_QF, - R_ParticleExplosion_QF, - R_ParticleExplosion2_QF, - R_LavaSplash_QF, - R_TeleportSplash_QF, - R_DarkFieldParticles_ID, - R_EntityParticles_ID, - R_Particle_New, - R_Particle_NewRandom, -}; - -static void -R_ParticleFunctionInit (void) -{ - sw_vid_render_funcs.particles = &particles_QF; -} - -static void -r_particles_nearclip_f (cvar_t *var) -{ - Cvar_SetValue (r_particles_nearclip, bound (r_nearclip->value, var->value, - r_farclip->value)); -} - -static void -r_particles_f (cvar_t *var) -{ - R_MaxParticlesCheck (var, r_particles_max); -} - -static void -r_particles_max_f (cvar_t *var) -{ - R_MaxParticlesCheck (r_particles, var); -} - -void -R_Particles_Init_Cvars (void) -{ - easter_eggs = Cvar_Get ("easter_eggs", "0", CVAR_NONE, r_easter_eggs_f, - "Enables easter eggs."); - r_particles = Cvar_Get ("r_particles", "1", CVAR_ARCHIVE, r_particles_f, - "Toggles drawing of particles."); - r_particles_max = Cvar_Get ("r_particles_max", "2048", CVAR_ARCHIVE, - r_particles_max_f, "Maximum amount of " - "particles to display. No maximum, minimum " - "is 0."); - r_particles_nearclip = Cvar_Get ("r_particles_nearclip", "32", - CVAR_ARCHIVE, r_particles_nearclip_f, - "Distance of the particle near clipping " - "plane from the player."); - r_particles_style = Cvar_Get ("r_particles_style", "1", CVAR_ARCHIVE, - r_particles_style_f, "Sets particle style. " - "0 for Id, 1 for QF."); - R_ParticleFunctionInit (); -} - -void -R_Particle_New (const char *type, int texnum, const vec3_t org, float scale, - const vec3_t vel, float die, int color, float alpha, float ramp) -{ - particle_t *p; - - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - VectorCopy (org, p->org); - p->color = color; - p->tex = texnum; - p->scale = scale; - p->alpha = alpha; - VectorCopy (vel, p->vel); - p->physics = R_ParticlePhysics (type); - p->die = die; - p->ramp = ramp; -} - -void -R_Particle_NewRandom (const char *type, int texnum, const vec3_t org, - int org_fuzz, float scale, int vel_fuzz, float die, - int color, float alpha, float ramp) -{ - float o_fuzz = org_fuzz, v_fuzz = vel_fuzz; - int rnd; - vec3_t porg, pvel; - - rnd = mtwist_rand (&mt); - porg[0] = o_fuzz * ((rnd & 63) - 31.5) / 63.0 + org[0]; - porg[1] = o_fuzz * (((rnd >> 6) & 63) - 31.5) / 63.0 + org[1]; - porg[2] = o_fuzz * (((rnd >> 10) & 63) - 31.5) / 63.0 + org[2]; - rnd = mtwist_rand (&mt); - pvel[0] = v_fuzz * ((rnd & 63) - 31.5) / 63.0; - pvel[1] = v_fuzz * (((rnd >> 6) & 63) - 31.5) / 63.0; - pvel[2] = v_fuzz * (((rnd >> 10) & 63) - 31.5) / 63.0; - - R_Particle_New (type, texnum, porg, scale, pvel, die, color, alpha, ramp); + return &r_psystem; } diff --git a/libs/video/renderer/sw/sw_rsky.c b/libs/video/renderer/sw/sw_rsky.c index 5a5670c57..9a05d0e24 100644 --- a/libs/video/renderer/sw/sw_rsky.c +++ b/libs/video/renderer/sw/sw_rsky.c @@ -35,6 +35,7 @@ static int iskyspeed = 8; static int iskyspeed2 = 2; +static float r_skyperiod; float r_skyspeed; float r_skytime; @@ -64,6 +65,13 @@ R_InitSky (texture_t *mt) int i, j; byte *src; + int g = GreatestCommonDivisor (iskyspeed, iskyspeed2); + int s1 = iskyspeed / g; + int s2 = iskyspeed2 / g; + r_skyperiod = SKYSIZE * s1 * s2; + + r_skyspeed = iskyspeed; + src = (byte *) mt + mt->offsets[0]; for (i = 0; i < 128; i++) { @@ -94,7 +102,7 @@ R_MakeSky (void) int x, y; int ofs, baseofs; int xshift, yshift; - unsigned int *pnewsky; + byte *pnewsky; static int xlast = -1, ylast = -1; xshift = r_skytime * r_skyspeed; @@ -106,19 +114,17 @@ R_MakeSky (void) xlast = xshift; ylast = yshift; - pnewsky = (unsigned int *) &newsky[0]; + pnewsky = &newsky[0]; for (y = 0; y < SKYSIZE; y++) { baseofs = ((y + yshift) & SKYMASK) * 131; for (x = 0; x < SKYSIZE; x++) { ofs = baseofs + ((x + xshift) & SKYMASK); - *(byte *) pnewsky = (*((byte *) pnewsky + 128) & - *(byte *) & bottommask[ofs]) | - *(byte *) & bottomsky[ofs]; - pnewsky = (unsigned int *) ((byte *) pnewsky + 1); + *pnewsky = (*(pnewsky + 128) & bottommask[ofs]) | bottomsky[ofs]; + pnewsky = pnewsky + 1; } - pnewsky += 128 / sizeof (unsigned int); + pnewsky += 128; } r_skymade = 1; @@ -176,17 +182,8 @@ R_GenSkyTile (void *pdest) void R_SetSkyFrame (void) { - int g, s1, s2; - float temp; - - r_skyspeed = iskyspeed; - - g = GreatestCommonDivisor (iskyspeed, iskyspeed2); - s1 = iskyspeed / g; - s2 = iskyspeed2 / g; - temp = SKYSIZE * s1 * s2; - - r_skytime = vr_data.realtime - ((int) (vr_data.realtime / temp) * temp); + r_skytime = vr_data.realtime; + r_skytime -= trunc (vr_data.realtime / r_skyperiod) * r_skyperiod; r_skymade = 0; } diff --git a/libs/video/renderer/sw/sw_rsprite.c b/libs/video/renderer/sw/sw_rsprite.c index 4ee5d74c3..2cea869b2 100644 --- a/libs/video/renderer/sw/sw_rsprite.c +++ b/libs/video/renderer/sw/sw_rsprite.c @@ -40,6 +40,8 @@ #include "QF/render.h" #include "QF/sys.h" +#include "QF/scene/entity.h" + #include "r_internal.h" static int clip_current; @@ -50,16 +52,17 @@ spritedesc_t r_spritedesc; static void -R_RotateSprite (float beamlength) +R_RotateSprite (vec4f_t relvieworg, float beamlength, vec3_t org) { vec3_t vec; + VectorCopy (relvieworg, org); if (beamlength == 0.0) return; - VectorScale (r_spritedesc.vpn, -beamlength, vec); + VectorScale (r_spritedesc.vfwd, -beamlength, vec); VectorAdd (r_entorigin, vec, r_entorigin); - VectorSubtract (modelorg, vec, modelorg); + VectorSubtract (relvieworg, vec, org); } @@ -145,7 +148,7 @@ R_ClipSpriteFace (int nump, clipplane_t *pclipplane) static void -R_SetupAndDrawSprite (void) +R_SetupAndDrawSprite (const vec3_t relvieworg) { int i, nump; float dot, scale, *pv; @@ -153,7 +156,7 @@ R_SetupAndDrawSprite (void) vec3_t left, up, right, down, transformed, local; emitpoint_t outverts[MAXWORKINGVERTS + 1], *pout; - dot = DotProduct (r_spritedesc.vpn, modelorg); + dot = DotProduct (r_spritedesc.vfwd, relvieworg); // backface cull if (dot >= 0) @@ -208,7 +211,7 @@ R_SetupAndDrawSprite (void) r_spritedesc.nearzi = -999999; for (i = 0; i < nump; i++) { - VectorSubtract (pv, r_origin, local); + VectorSubtract (pv, r_refdef.frame.position, local); TransformVector (local, transformed); if (transformed[2] < NEAR_CLIP) @@ -234,154 +237,36 @@ R_SetupAndDrawSprite (void) // draw it r_spritedesc.nump = nump; r_spritedesc.pverts = outverts; - D_DrawSprite (); + D_DrawSprite (relvieworg); } - -static mspriteframe_t * -R_GetSpriteframe (msprite_t *psprite) -{ - mspritegroup_t *pspritegroup; - mspriteframe_t *pspriteframe; - int i, numframes, frame; - float *pintervals, fullinterval, targettime, time; - - frame = currententity->frame; - - if ((frame >= psprite->numframes) || (frame < 0)) { - Sys_Printf ("R_DrawSprite: no such frame %d\n", frame); - frame = 0; - } - - if (psprite->frames[frame].type == SPR_SINGLE) { - pspriteframe = psprite->frames[frame].frameptr; - } else { - pspritegroup = (mspritegroup_t *) psprite->frames[frame].frameptr; - pintervals = pspritegroup->intervals; - numframes = pspritegroup->numframes; - fullinterval = pintervals[numframes - 1]; - - time = vr_data.realtime + currententity->syncbase; - - // when loading in Mod_LoadSpriteGroup, we guaranteed all interval - // values are positive, so we don't have to worry about division by 0 - targettime = time - ((int) (time / fullinterval)) * fullinterval; - - for (i = 0; i < (numframes - 1); i++) { - if (pintervals[i] > targettime) - break; - } - - pspriteframe = pspritegroup->frames[i]; - } - - return pspriteframe; -} - - void -R_DrawSprite (void) +R_DrawSprite (entity_t ent) { - int i; - msprite_t *psprite; - vec3_t tvec; - float dot, angle, sr, cr; + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); + msprite_t *sprite = renderer->model->cache.data; - psprite = currententity->model->cache.data; - - r_spritedesc.pspriteframe = R_GetSpriteframe (psprite); + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, + ent.reg); + r_spritedesc.pspriteframe = R_GetSpriteFrame (sprite, animation); sprite_width = r_spritedesc.pspriteframe->width; sprite_height = r_spritedesc.pspriteframe->height; - // TODO: make this caller-selectable - if (psprite->type == SPR_FACING_UPRIGHT) { - // generate the sprite's axes, with vup straight up in worldspace, and - // r_spritedesc.vright perpendicular to modelorg. - // This will not work if the view direction is very close to straight - // up or down, because the cross product will be between two nearly - // parallel vectors and starts to approach an undefined state, so we - // don't draw if the two vectors are less than 1 degree apart - tvec[0] = -modelorg[0]; - tvec[1] = -modelorg[1]; - tvec[2] = -modelorg[2]; - VectorNormalize (tvec); - dot = tvec[2]; // same as DotProduct (tvec, - // r_spritedesc.vup) because - // r_spritedesc.vup is 0, 0, 1 - if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = - // 0.999848 - return; - r_spritedesc.vup[0] = 0; - r_spritedesc.vup[1] = 0; - r_spritedesc.vup[2] = 1; - r_spritedesc.vright[0] = tvec[1]; - //CrossProduct(r_spritedesc.vup, -modelorg, r_spritedesc.vright) - r_spritedesc.vright[1] = -tvec[0]; - r_spritedesc.vright[2] = 0; - VectorNormalize (r_spritedesc.vright); - r_spritedesc.vpn[0] = -r_spritedesc.vright[1]; - r_spritedesc.vpn[1] = r_spritedesc.vright[0]; - r_spritedesc.vpn[2] = 0; - //CrossProduct (r_spritedesc.vright, r_spritedesc.vup, r_spritedesc.vpn) - } else if (psprite->type == SPR_VP_PARALLEL) { - // generate the sprite's axes, completely parallel to the viewplane. - // There are no problem situations, because the sprite is always in the - // same position relative to the viewer - for (i = 0; i < 3; i++) { - r_spritedesc.vup[i] = vup[i]; - r_spritedesc.vright[i] = vright[i]; - r_spritedesc.vpn[i] = vpn[i]; - } - } else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT) { - // generate the sprite's axes, with vup straight up in worldspace, and - // r_spritedesc.vright parallel to the viewplane. - // This will not work if the view direction is very close to straight - // up or down, because the cross product will be between two nearly - // parallel vectors and starts to approach an undefined state, so we - // don't draw if the two vectors are less than 1 degree apart - dot = vpn[2]; // same as DotProduct (vpn, - // r_spritedesc.vup) because - // r_spritedesc.vup is 0, 0, 1 - if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = - // 0.999848 - return; - r_spritedesc.vup[0] = 0; - r_spritedesc.vup[1] = 0; - r_spritedesc.vup[2] = 1; - r_spritedesc.vright[0] = vpn[1]; - //CrossProduct (r_spritedesc.vup, vpn, - r_spritedesc.vright[1] = -vpn[0]; // r_spritedesc.vright) - r_spritedesc.vright[2] = 0; - VectorNormalize (r_spritedesc.vright); - r_spritedesc.vpn[0] = -r_spritedesc.vright[1]; - r_spritedesc.vpn[1] = r_spritedesc.vright[0]; - r_spritedesc.vpn[2] = 0; - //CrossProduct (r_spritedesc.vright, r_spritedesc.vup, r_spritedesc.vpn) - } else if (psprite->type == SPR_ORIENTED) { - // generate the sprite's axes, according to the sprite's world - // orientation - VectorCopy (currententity->transform + 0, r_spritedesc.vpn); - VectorNegate (currententity->transform + 4, r_spritedesc.vright); - VectorCopy (currententity->transform + 8, r_spritedesc.vup); - } else if (psprite->type == SPR_VP_PARALLEL_ORIENTED) { - // generate the sprite's axes, parallel to the viewplane, but rotated - // in that plane around the center according to the sprite entity's - // roll angle. So vpn stays the same, but vright and vup rotate - angle = currententity->angles[ROLL] * (M_PI * 2 / 360); - sr = sin (angle); - cr = cos (angle); - - for (i = 0; i < 3; i++) { - r_spritedesc.vpn[i] = vpn[i]; - r_spritedesc.vright[i] = vright[i] * cr + vup[i] * sr; - r_spritedesc.vup[i] = vright[i] * -sr + vup[i] * cr; - } - } else { - Sys_Error ("R_DrawSprite: Bad sprite type %d", psprite->type); + vec4f_t up = {}, right = {}, fwd = {}; + vec4f_t cameravec = r_refdef.frame.position - r_entorigin; + transform_t transform = Entity_Transform (ent); + if (!R_BillboardFrame (transform, sprite->type, cameravec, + &up, &right, &fwd)) { + // the orientation is undefined so can't draw the sprite + return; } + VectorCopy (up, r_spritedesc.vup);//FIXME + VectorCopy (right, r_spritedesc.vright); + VectorCopy (fwd, r_spritedesc.vfwd); - R_RotateSprite (psprite->beamlength); + vec3_t org; + R_RotateSprite (cameravec, sprite->beamlength, org); - R_SetupAndDrawSprite (); + R_SetupAndDrawSprite (org); } diff --git a/libs/video/renderer/sw/sw_rsurf.c b/libs/video/renderer/sw/sw_rsurf.c index e4d3fa853..0cb651c15 100644 --- a/libs/video/renderer/sw/sw_rsurf.c +++ b/libs/video/renderer/sw/sw_rsurf.c @@ -31,6 +31,8 @@ #include "QF/render.h" #include "QF/sys.h" +#include "QF/scene/entity.h" + #include "r_internal.h" #ifdef PIC @@ -48,15 +50,15 @@ int lightdelta, lightdeltastep; int lightright, lightleftstep, lightrightstep, blockdivshift; static unsigned int blockdivmask; void *prowdestbase; -unsigned char *pbasesource; +byte *pbasesource; int surfrowbytes; // used by ASM files unsigned int *r_lightptr; int r_stepback; int r_lightwidth; static int r_numhblocks; int r_numvblocks; -static unsigned char *r_source; -unsigned char *r_sourcemax; +static byte *r_source; +byte *r_sourcemax; void R_DrawSurfaceBlock_mip0 (void); void R_DrawSurfaceBlock_mip1 (void); @@ -71,13 +73,14 @@ static unsigned int blocklights[34 * 34]; //FIXME make dynamic static void -R_AddDynamicLights (void) +R_AddDynamicLights (uint32_t render_id) { msurface_t *surf; unsigned int lnum; int sd, td; float dist, rad, minlight; vec3_t impact, local, lightorigin; + vec4f_t entorigin = { 0, 0, 0, 1 }; int s, t; int i; int smax, tmax; @@ -88,12 +91,17 @@ R_AddDynamicLights (void) tmax = (surf->extents[1] >> 4) + 1; tex = surf->texinfo; + if (render_id) { + //FIXME give world entity a transform + vec4f_t *transform = SW_COMP (scene_sw_matrix, render_id); + entorigin = transform[3]; + } + for (lnum = 0; lnum < r_maxdlights; lnum++) { if (!(surf->dlightbits[lnum / 32] & (1 << (lnum % 32)))) continue; // not lit by this light - VectorSubtract (r_dlights[lnum].origin, currententity->origin, - lightorigin); + VectorSubtract (r_dlights[lnum].origin, entorigin, lightorigin); rad = r_dlights[lnum].radius; dist = DotProduct (lightorigin, surf->plane->normal) - surf->plane->dist; @@ -137,7 +145,7 @@ R_AddDynamicLights (void) Combine and scale multiple lightmaps into the 8.8 format in blocklights */ static void -R_BuildLightMap (void) +R_BuildLightMap (uint32_t render_id) { int smax, tmax; int t; @@ -154,7 +162,7 @@ R_BuildLightMap (void) size = smax * tmax; lightmap = surf->samples; - if (!r_worldentity.model->lightdata) { + if (!r_refdef.worldmodel->brush.lightdata) { for (i = 0; i < size; i++) blocklights[i] = 0; return; @@ -173,7 +181,7 @@ R_BuildLightMap (void) } // add all the dynamic lights if (surf->dlightframe == r_framecount) - R_AddDynamicLights (); + R_AddDynamicLights (render_id); // bound, invert, and shift for (i = 0; i < size; i++) { @@ -187,7 +195,7 @@ R_BuildLightMap (void) } void -R_DrawSurface (void) +R_DrawSurface (uint32_t render_id) { byte *basetptr; int smax, tmax, twidth; @@ -199,7 +207,7 @@ R_DrawSurface (void) texture_t *mt; // calculate the lightings - R_BuildLightMap (); + R_BuildLightMap (render_id); surfrowbytes = r_drawsurf.rowbytes; @@ -268,7 +276,7 @@ void R_DrawSurfaceBlock_mip0 (void) { int v, i, b, lightstep, lighttemp, light; - unsigned char pix, *psource, *prowdest; + byte pix, *psource, *prowdest; psource = pbasesource; prowdest = prowdestbase; @@ -290,8 +298,7 @@ R_DrawSurfaceBlock_mip0 (void) for (b = 15; b >= 0; b--) { pix = psource[b]; - prowdest[b] = ((unsigned char *) vid.colormap8) - [(light & 0xFF00) + pix]; + prowdest[b] = r_colormap[(light & 0xFF00) + pix]; light += lightstep; } @@ -310,7 +317,7 @@ void R_DrawSurfaceBlock_mip1 (void) { int v, i, b, lightstep, lighttemp, light; - unsigned char pix, *psource, *prowdest; + byte pix, *psource, *prowdest; psource = pbasesource; prowdest = prowdestbase; @@ -332,8 +339,7 @@ R_DrawSurfaceBlock_mip1 (void) for (b = 7; b >= 0; b--) { pix = psource[b]; - prowdest[b] = ((unsigned char *) vid.colormap8) - [(light & 0xFF00) + pix]; + prowdest[b] = r_colormap[(light & 0xFF00) + pix]; light += lightstep; } @@ -352,7 +358,7 @@ void R_DrawSurfaceBlock_mip2 (void) { int v, i, b, lightstep, lighttemp, light; - unsigned char pix, *psource, *prowdest; + byte pix, *psource, *prowdest; psource = pbasesource; prowdest = prowdestbase; @@ -374,8 +380,7 @@ R_DrawSurfaceBlock_mip2 (void) for (b = 3; b >= 0; b--) { pix = psource[b]; - prowdest[b] = ((unsigned char *) vid.colormap8) - [(light & 0xFF00) + pix]; + prowdest[b] = r_colormap[(light & 0xFF00) + pix]; light += lightstep; } @@ -394,7 +399,7 @@ void R_DrawSurfaceBlock_mip3 (void) { int v, i, b, lightstep, lighttemp, light; - unsigned char pix, *psource, *prowdest; + byte pix, *psource, *prowdest; psource = pbasesource; prowdest = prowdestbase; @@ -416,8 +421,7 @@ R_DrawSurfaceBlock_mip3 (void) for (b = 1; b >= 0; b--) { pix = psource[b]; - prowdest[b] = ((unsigned char *) vid.colormap8) - [(light & 0xFF00) + pix]; + prowdest[b] = r_colormap[(light & 0xFF00) + pix]; light += lightstep; } diff --git a/libs/video/renderer/sw/sw_rvarsa.S b/libs/video/renderer/sw/sw_rvarsa.S index c53d26cf1..523d0e71b 100644 --- a/libs/video/renderer/sw/sw_rvarsa.S +++ b/libs/video/renderer/sw/sw_rvarsa.S @@ -76,6 +76,7 @@ C(r_single_cw): .long 0 .globl C(r_bmodelactive) C(r_bmodelactive): .long 0 + .text .global C(R_InitVars) C(R_InitVars): movl C(ceil_cw), %eax @@ -86,3 +87,6 @@ C(R_InitVars): #endif // USE_INTEL_ASM +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/libs/video/renderer/sw/transform.S b/libs/video/renderer/sw/transform.S index b9b2f3d3f..2c0ec053c 100644 --- a/libs/video/renderer/sw/transform.S +++ b/libs/video/renderer/sw/transform.S @@ -77,31 +77,31 @@ C(TransformVector): flds (%eax) // in[0] | in[0]*vright[0] fmuls C(vup) // in[0]*vup[0] | in[0]*vright[0] flds (%eax) // in[0] | in[0]*vup[0] | in[0]*vright[0] - fmuls C(vpn) // in[0]*vpn[0] | in[0]*vup[0] | in[0]*vright[0] + fmuls C(vfwd) // in[0]*vfwd[0] | in[0]*vup[0] | in[0]*vright[0] flds 4(%eax) // in[1] | ... fmuls C(vright)+4 // in[1]*vright[1] | ... flds 4(%eax) // in[1] | in[1]*vright[1] | ... fmuls C(vup)+4 // in[1]*vup[1] | in[1]*vright[1] | ... flds 4(%eax) // in[1] | in[1]*vup[1] | in[1]*vright[1] | ... - fmuls C(vpn)+4 // in[1]*vpn[1] | in[1]*vup[1] | in[1]*vright[1] | ... - fxch %st(2) // in[1]*vright[1] | in[1]*vup[1] | in[1]*vpn[1] | ... + fmuls C(vfwd)+4 // in[1]*vfwd[1] | in[1]*vup[1] | in[1]*vright[1] | ... + fxch %st(2) // in[1]*vright[1] | in[1]*vup[1] | in[1]*vfwd[1] | ... - faddp %st(0),%st(5) // in[1]*vup[1] | in[1]*vpn[1] | ... - faddp %st(0),%st(3) // in[1]*vpn[1] | ... - faddp %st(0),%st(1) // vpn_accum | vup_accum | vright_accum + faddp %st(0),%st(5) // in[1]*vup[1] | in[1]*vfwd[1] | ... + faddp %st(0),%st(3) // in[1]*vfwd[1] | ... + faddp %st(0),%st(1) // vfwd | vup_accum | vright_accum flds 8(%eax) // in[2] | ... fmuls C(vright)+8 // in[2]*vright[2] | ... flds 8(%eax) // in[2] | in[2]*vright[2] | ... fmuls C(vup)+8 // in[2]*vup[2] | in[2]*vright[2] | ... flds 8(%eax) // in[2] | in[2]*vup[2] | in[2]*vright[2] | ... - fmuls C(vpn)+8 // in[2]*vpn[2] | in[2]*vup[2] | in[2]*vright[2] | ... - fxch %st(2) // in[2]*vright[2] | in[2]*vup[2] | in[2]*vpn[2] | ... + fmuls C(vfwd)+8 // in[2]*vfwd[2] | in[2]*vup[2] | in[2]*vright[2] | ... + fxch %st(2) // in[2]*vright[2] | in[2]*vup[2] | in[2]*vfwd[2] | ... - faddp %st(0),%st(5) // in[2]*vup[2] | in[2]*vpn[2] | ... - faddp %st(0),%st(3) // in[2]*vpn[2] | ... - faddp %st(0),%st(1) // vpn_accum | vup_accum | vright_accum + faddp %st(0),%st(5) // in[2]*vup[2] | in[2]*vfwd[2] | ... + faddp %st(0),%st(3) // in[2]*vfwd[2] | ... + faddp %st(0),%st(1) // vfwd | vup_accum | vright_accum fstps 8(%edx) // out[2] fstps 4(%edx) // out[1] @@ -110,3 +110,7 @@ C(TransformVector): ret #endif // USE_INTEL_ASM + +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/libs/video/renderer/sw/vid_common_sw.c b/libs/video/renderer/sw/vid_common_sw.c index a01992204..f67a4ce8b 100644 --- a/libs/video/renderer/sw/vid_common_sw.c +++ b/libs/video/renderer/sw/vid_common_sw.c @@ -31,7 +31,6 @@ #include #include -#include "QF/console.h" #include "QF/mathlib.h" #include "QF/sys.h" #include "QF/vid.h" diff --git a/libs/video/renderer/sw32/Makefile.am b/libs/video/renderer/sw32/Makefile.am deleted file mode 100644 index 5d078e90c..000000000 --- a/libs/video/renderer/sw32/Makefile.am +++ /dev/null @@ -1,18 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CFLAGS= @PREFER_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include - -noinst_LTLIBRARIES= libsw32.la - -sw32_src= \ - d_edge.c d_fill.c d_init.c d_modech.c d_part.c d_polyse.c d_scan.c \ - d_sky.c d_sprite.c d_surf.c d_vars.c d_zpoint.c draw.c screen.c \ - sw32_graph.c sw32_raclip.c sw32_ralias.c sw32_rbsp.c sw32_rdraw.c \ - sw32_redge.c sw32_riqm.c sw32_rmain.c sw32_rmisc.c sw32_rpart.c \ - sw32_rsky.c sw32_rsprite.c sw32_rsurf.c \ - vid_common_sw32.c - -libsw32_la_SOURCES= $(sw32_src) - -EXTRA_DIST= $(sw32_src) namehack.h diff --git a/libs/video/renderer/sw32/d_edge.c b/libs/video/renderer/sw32/d_edge.c deleted file mode 100644 index 6fa0b131a..000000000 --- a/libs/video/renderer/sw32/d_edge.c +++ /dev/null @@ -1,320 +0,0 @@ -/* - d_edge.c - - (description) - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include "QF/cvar.h" -#include "QF/render.h" -#include "QF/sys.h" - -#include "d_local.h" -#include "r_internal.h" -#include "vid_internal.h" - -static int miplevel; - -float sw32_scale_for_mip; - -static vec3_t transformed_modelorg; - - -void -sw32_D_DrawPoly (void) -{ -// this driver takes spans, not polygons -} - - -int -sw32_D_MipLevelForScale (float scale) -{ - int lmiplevel; - - if (scale >= sw32_d_scalemip[0]) - lmiplevel = 0; - else if (scale >= sw32_d_scalemip[1]) - lmiplevel = 1; - else if (scale >= sw32_d_scalemip[2]) - lmiplevel = 2; - else - lmiplevel = 3; - - if (lmiplevel < sw32_d_minmip) - lmiplevel = sw32_d_minmip; - - return lmiplevel; -} - -// FIXME: clean this up - -static void -D_DrawSolidSurface (surf_t *surf, int color) -{ - espan_t *span; - - switch(sw32_r_pixbytes) { - case 1: - { - byte *pdest, pix; - int u, u2; - - pix = color; - for (span = surf->spans; span; span = span->pnext) - { - pdest = (byte *) sw32_d_viewbuffer + sw32_screenwidth * span->v; - u = span->u; - u2 = span->u + span->count - 1; - for (;u <= u2; u++) - pdest[u] = pix; - } - } - break; - case 2: - { - short *pdest, pix; - int u, u2; - - pix = sw32_8to16table[color]; - for (span = surf->spans; span; span = span->pnext) - { - pdest = (short *) sw32_d_viewbuffer + sw32_screenwidth * span->v; - u = span->u; - u2 = span->u + span->count - 1; - for (;u <= u2; u++) - pdest[u] = pix; - } - } - break; - case 4: - { - int *pdest, pix; - int u, u2; - - pix = d_8to24table[color]; - for (span = surf->spans; span; span = span->pnext) - { - pdest = (int *) sw32_d_viewbuffer + sw32_screenwidth * span->v; - u = span->u; - u2 = span->u + span->count - 1; - for (;u <= u2; u++) - pdest[u] = pix; - } - } - break; - default: - Sys_Error("D_DrawSolidSurface: unsupported r_pixbytes %i", - sw32_r_pixbytes); - } -} - - -static void -D_CalcGradients (msurface_t *pface) -{ - float mipscale, t; - vec3_t p_temp1, p_saxis, p_taxis; - - mipscale = 1.0 / (float) (1 << miplevel); - - sw32_TransformVector (pface->texinfo->vecs[0], p_saxis); - sw32_TransformVector (pface->texinfo->vecs[1], p_taxis); - - t = sw32_xscaleinv * mipscale; - sw32_d_sdivzstepu = p_saxis[0] * t; - sw32_d_tdivzstepu = p_taxis[0] * t; - - t = sw32_yscaleinv * mipscale; - sw32_d_sdivzstepv = -p_saxis[1] * t; - sw32_d_tdivzstepv = -p_taxis[1] * t; - - sw32_d_sdivzorigin = p_saxis[2] * mipscale - - sw32_xcenter * sw32_d_sdivzstepu - - sw32_ycenter * sw32_d_sdivzstepv; - sw32_d_tdivzorigin = p_taxis[2] * mipscale - - sw32_xcenter * sw32_d_tdivzstepu - - sw32_ycenter * sw32_d_tdivzstepv; - - VectorScale (transformed_modelorg, mipscale, p_temp1); - - t = 0x10000 * mipscale; - sw32_sadjust = ((fixed16_t) (DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) - - ((pface->texturemins[0] << 16) >> miplevel) - + pface->texinfo->vecs[0][3] * t; - sw32_tadjust = ((fixed16_t) (DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) - - ((pface->texturemins[1] << 16) >> miplevel) - + pface->texinfo->vecs[1][3] * t; - - // -1 (-epsilon) so we never wander off the edge of the texture - sw32_bbextents = ((pface->extents[0] << 16) >> miplevel) - 1; - sw32_bbextentt = ((pface->extents[1] << 16) >> miplevel) - 1; -} - - -void -sw32_D_DrawSurfaces (void) -{ - surf_t *s; - msurface_t *pface; - surfcache_t *pcurrentcache; - vec3_t world_transformed_modelorg; - vec3_t local_modelorg; - - currententity = &r_worldentity; - sw32_TransformVector (modelorg, transformed_modelorg); - VectorCopy (transformed_modelorg, world_transformed_modelorg); - - // TODO: could preset a lot of this at mode set time - if (r_drawflat->int_val) { - for (s = &sw32_surfaces[1]; s < sw32_surface_p; s++) { - if (!s->spans) - continue; - - d_zistepu = s->d_zistepu; - d_zistepv = s->d_zistepv; - d_ziorigin = s->d_ziorigin; - - D_DrawSolidSurface (s, ((intptr_t) s->data & 0xFF)); - sw32_D_DrawZSpans (s->spans); - } - } else { - for (s = &sw32_surfaces[1]; s < sw32_surface_p; s++) { - if (!s->spans) - continue; - - sw32_r_drawnpolycount++; - - d_zistepu = s->d_zistepu; - d_zistepv = s->d_zistepv; - d_ziorigin = s->d_ziorigin; - - if (s->flags & SURF_DRAWSKY) { - if (!sw32_r_skymade) { - sw32_R_MakeSky (); - } - - sw32_D_DrawSkyScans (s->spans); - sw32_D_DrawZSpans (s->spans); - } else if (s->flags & SURF_DRAWBACKGROUND) { - // set up a gradient for the background surface that places - // it effectively at infinity distance from the viewpoint - d_zistepu = 0; - d_zistepv = 0; - d_ziorigin = -0.9; - - D_DrawSolidSurface (s, r_clearcolor->int_val & 0xFF); - sw32_D_DrawZSpans (s->spans); - } else if (s->flags & SURF_DRAWTURB) { - pface = s->data; - miplevel = 0; - sw32_cacheblock = ((byte *) pface->texinfo->texture + - pface->texinfo->texture->offsets[0]); - sw32_cachewidth = 64; - - if (s->insubmodel) { - // FIXME: we don't want to do all this for every polygon! - // TODO: store once at start of frame - currententity = s->entity; // FIXME: make this passed in - // to sw32_R_RotateBmodel () - VectorSubtract (r_origin, currententity->origin, - local_modelorg); - sw32_TransformVector (local_modelorg, transformed_modelorg); - - sw32_R_RotateBmodel (); // FIXME: don't mess with the - // frustum, make entity passed in - } - - D_CalcGradients (pface); - - sw32_Turbulent (s->spans); - sw32_D_DrawZSpans (s->spans); - - if (s->insubmodel) { - // restore the old drawing state - // FIXME: we don't want to do this every time! - // TODO: speed up - - currententity = &r_worldentity; - VectorCopy (world_transformed_modelorg, - transformed_modelorg); - VectorCopy (base_vpn, vpn); - VectorCopy (base_vup, vup); - VectorCopy (base_vright, vright); - VectorCopy (base_modelorg, modelorg); - sw32_R_TransformFrustum (); - } - } else { - if (s->insubmodel) { - // FIXME: we don't want to do all this for every polygon! - // TODO: store once at start of frame - currententity = s->entity; // FIXME: make this passed in - // to sw32_R_RotateBmodel () - VectorSubtract (r_origin, currententity->origin, - local_modelorg); - sw32_TransformVector (local_modelorg, transformed_modelorg); - - sw32_R_RotateBmodel (); // FIXME: don't mess with the - // frustum, make entity passed in - } - - pface = s->data; - miplevel = sw32_D_MipLevelForScale (s->nearzi * sw32_scale_for_mip - * pface->texinfo->mipadjust); - - // FIXME: make this passed in to D_CacheSurface - pcurrentcache = sw32_D_CacheSurface (pface, miplevel); - - sw32_cacheblock = (byte *) pcurrentcache->data; - sw32_cachewidth = pcurrentcache->width; - - D_CalcGradients (pface); - - sw32_D_DrawSpans (s->spans); - - sw32_D_DrawZSpans (s->spans); - - if (s->insubmodel) { - // restore the old drawing state - // FIXME: we don't want to do this every time! - // TODO: speed up - - VectorCopy (world_transformed_modelorg, - transformed_modelorg); - VectorCopy (base_vpn, vpn); - VectorCopy (base_vup, vup); - VectorCopy (base_vright, vright); - VectorCopy (base_modelorg, modelorg); - sw32_R_TransformFrustum (); - currententity = &r_worldentity; - } - } - } - } -} diff --git a/libs/video/renderer/sw32/d_fill.c b/libs/video/renderer/sw32/d_fill.c deleted file mode 100644 index 220806731..000000000 --- a/libs/video/renderer/sw32/d_fill.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - d_fill.c - - clears a specified rectangle to the specified color - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include "QF/sys.h" - -#include "d_iface.h" -#include "r_internal.h" -#include "vid_internal.h" - - -void -sw32_D_FillRect (vrect_t *rect, int color) -{ - switch (sw32_r_pixbytes) - { - case 1: - { - int rx, ry, rwidth, rheight; - byte *dest, pix; - - pix = color; - - rx = rect->x; - ry = rect->y; - rwidth = rect->width; - rheight = rect->height; - - if (rx < 0) { - rwidth += rx; - rx = 0; - } - if (ry < 0) { - rheight += ry; - ry = 0; - } - if (rx + rwidth > vid.width) - rwidth = vid.width - rx; - if (ry + rheight > vid.height) - rheight = vid.height - rx; - - if (rwidth < 1 || rheight < 1) - return; - - dest = (byte *) vid.buffer + ry * vid.rowbytes + rx; - - for (ry = 0; ry < rheight; ry++) - { - for (rx = 0; rx < rwidth; rx++) - dest[rx] = pix; - dest += vid.rowbytes; - } - } - break; - case 2: - { - int rx, ry, rwidth, rheight; - unsigned short *dest, pix; - - pix = sw32_8to16table[color]; - - rx = rect->x; - ry = rect->y; - rwidth = rect->width; - rheight = rect->height; - - if (rx < 0) { - rwidth += rx; - rx = 0; - } - if (ry < 0) { - rheight += ry; - ry = 0; - } - if (rx + rwidth > vid.width) - rwidth = vid.width - rx; - if (ry + rheight > vid.height) - rheight = vid.height - rx; - - if (rwidth < 1 || rheight < 1) - return; - - dest = (unsigned short *) vid.buffer + ry * (vid.rowbytes >> 1) + - rx; - - for (ry = 0; ry < rheight; ry++) - { - for (rx = 0; rx < rwidth; rx++) - dest[rx] = pix; - dest += (vid.rowbytes >> 1); - } - } - break; - case 4: - { - int rx, ry, rwidth, rheight; - unsigned int *dest, pix; - - pix = d_8to24table[color]; - - rx = rect->x; - ry = rect->y; - rwidth = rect->width; - rheight = rect->height; - - if (rx < 0) { - rwidth += rx; - rx = 0; - } - if (ry < 0) { - rheight += ry; - ry = 0; - } - if (rx + rwidth > vid.width) - rwidth = vid.width - rx; - if (ry + rheight > vid.height) - rheight = vid.height - rx; - - if (rwidth < 1 || rheight < 1) - return; - - dest = (unsigned int *) vid.buffer + ry * (vid.rowbytes >> 2) + rx; - - for (ry = 0; ry < rheight; ry++) - { - for (rx = 0; rx < rwidth; rx++) - dest[rx] = pix; - dest += (vid.rowbytes >> 2); - } - } - break; - default: - Sys_Error("D_FillRect: unsupported r_pixbytes %i", sw32_r_pixbytes); - } -} diff --git a/libs/video/renderer/sw32/d_init.c b/libs/video/renderer/sw32/d_init.c deleted file mode 100644 index ccb5f7392..000000000 --- a/libs/video/renderer/sw32/d_init.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - d_init.c - - rasterization driver initialization - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include "QF/cvar.h" -#include "QF/render.h" - -#include "compat.h" -#include "d_local.h" -#include "r_internal.h" -#include "vid_internal.h" - -#define NUM_MIPS 4 - -surfcache_t *sw32_d_initial_rover; -qboolean sw32_d_roverwrapped; -int sw32_d_minmip; -float sw32_d_scalemip[NUM_MIPS - 1]; - -static float basemip[NUM_MIPS - 1] = { 1.0, 0.5 * 0.8, 0.25 * 0.8 }; - - -float sw32_d_zitable[65536]; - - -void -sw32_D_Init (void) -{ - sw32_r_drawpolys = false; - sw32_r_worldpolysbacktofront = false; - - // LordHavoc: compute 1/zi table for use in rendering code everywhere - if (!sw32_d_zitable[1]) { - int i; - sw32_d_zitable[0] = 0; - for (i = 1;i < 65536;i++) - sw32_d_zitable[i] = (65536.0 * 65536.0 / (double) i); - } - - vr_data.vid->surf_cache_size = sw32_D_SurfaceCacheForRes; - vr_data.vid->flush_caches = sw32_D_FlushCaches; - vr_data.vid->init_caches = sw32_D_InitCaches; - - VID_InitBuffers (); - VID_MakeColormaps(); -} - -void -sw32_D_EnableBackBufferAccess (void) -{ - VID_LockBuffer (); -} - -void -sw32_D_TurnZOn (void) -{ - // not needed for software version -} - -void -sw32_D_DisableBackBufferAccess (void) -{ - VID_UnlockBuffer (); -} - -void -sw32_D_SetupFrame (void) -{ - int i; - - if (sw32_r_dowarp) - sw32_d_viewbuffer = sw32_r_warpbuffer; - else - sw32_d_viewbuffer = vid.buffer; - - if (sw32_r_dowarp) - sw32_screenwidth = WARP_WIDTH; - else - sw32_screenwidth = vid.rowbytes / sw32_r_pixbytes; - - sw32_d_roverwrapped = false; - sw32_d_initial_rover = sw32_sc_rover; - - sw32_d_minmip = bound (0, d_mipcap->value, 3); - - for (i = 0; i < (NUM_MIPS - 1); i++) - sw32_d_scalemip[i] = basemip[i] * d_mipscale->value; -} - -void -sw32_D_UpdateRects (vrect_t *prect) -{ - // the software driver draws these directly to the vid buffer -} diff --git a/libs/video/renderer/sw32/d_modech.c b/libs/video/renderer/sw32/d_modech.c deleted file mode 100644 index 7ca3efec3..000000000 --- a/libs/video/renderer/sw32/d_modech.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - d_modech.c - - called when mode has just changed - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include "QF/render.h" - -#include "d_local.h" -#include "r_internal.h" - -int sw32_d_vrectx, sw32_d_vrecty, sw32_d_vrectright_particle, sw32_d_vrectbottom_particle; - -int sw32_d_y_aspect_shift, sw32_d_pix_min, sw32_d_pix_max, sw32_d_pix_shift; - -int sw32_d_scantable[MAXHEIGHT]; -short *sw32_zspantable[MAXHEIGHT]; - - -static void -D_Patch (void) -{ -} - -void -sw32_D_ViewChanged (void) -{ - int rowpixels; - - if (sw32_r_dowarp) - rowpixels = WARP_WIDTH; - else - rowpixels = vid.rowbytes / sw32_r_pixbytes; - - sw32_scale_for_mip = sw32_xscale; - if (sw32_yscale > sw32_xscale) - sw32_scale_for_mip = sw32_yscale; - - sw32_d_zrowbytes = vid.width * 2; - sw32_d_zwidth = vid.width; - - sw32_d_pix_min = r_refdef.vrect.width / 320; - if (sw32_d_pix_min < 1) - sw32_d_pix_min = 1; - - sw32_d_pix_max = (int) ((float) r_refdef.vrect.width / (320.0 / 4.0) + 0.5); - sw32_d_pix_shift = 8 - (int) ((float) r_refdef.vrect.width / 320.0 + 0.5); - if (sw32_d_pix_max < 1) - sw32_d_pix_max = 1; - - if (sw32_pixelAspect > 1.4) - sw32_d_y_aspect_shift = 1; - else - sw32_d_y_aspect_shift = 0; - - sw32_d_vrectx = r_refdef.vrect.x; - sw32_d_vrecty = r_refdef.vrect.y; - sw32_d_vrectright_particle = r_refdef.vrectright - sw32_d_pix_max; - sw32_d_vrectbottom_particle = - r_refdef.vrectbottom - (sw32_d_pix_max << sw32_d_y_aspect_shift); - - { - int i; - - for (i = 0; i < vid.height; i++) { - sw32_d_scantable[i] = i * rowpixels; - sw32_zspantable[i] = sw32_d_pzbuffer + i * sw32_d_zwidth; - } - } - - D_Patch (); -} diff --git a/libs/video/renderer/sw32/d_part.c b/libs/video/renderer/sw32/d_part.c deleted file mode 100644 index 30b969a0f..000000000 --- a/libs/video/renderer/sw32/d_part.c +++ /dev/null @@ -1,373 +0,0 @@ -/* - d_part.c - - software driver module for drawing particles - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include "QF/sys.h" - -#include "d_local.h" -#include "r_internal.h" -#include "vid_internal.h" - - -void -sw32_D_DrawParticle (particle_t *pparticle) -{ - vec3_t local, transformed; - float zi; - short *pz; - int i, izi, pix, count, u, v; - - // transform point - VectorSubtract (pparticle->org, r_origin, local); - - transformed[0] = DotProduct (local, r_pright); - transformed[1] = DotProduct (local, r_pup); - transformed[2] = DotProduct (local, r_ppn); - - if (transformed[2] < PARTICLE_Z_CLIP) - return; - - // project the point - // FIXME: preadjust sw32_xcenter and sw32_ycenter - zi = 1.0 / transformed[2]; - u = (int) (sw32_xcenter + zi * transformed[0] + 0.5); - v = (int) (sw32_ycenter - zi * transformed[1] + 0.5); - - if ((v > sw32_d_vrectbottom_particle) - || (u > sw32_d_vrectright_particle) - || (v < sw32_d_vrecty) || (u < sw32_d_vrectx)) { - return; - } - - pz = sw32_d_pzbuffer + (sw32_d_zwidth * v) + u; - izi = (int) (zi * 0x8000); - - pix = izi >> sw32_d_pix_shift; - - if (pix < sw32_d_pix_min) - pix = sw32_d_pix_min; - else if (pix > sw32_d_pix_max) - pix = sw32_d_pix_max; - - switch(sw32_r_pixbytes) - { - case 1: - { - byte *pdest = (byte *) sw32_d_viewbuffer + sw32_d_scantable[v] + u, - pixcolor = pparticle->color; - switch (pix) { - case 1: - count = 1 << sw32_d_y_aspect_shift; - - for (; count; count--, pz += sw32_d_zwidth, - pdest += sw32_screenwidth) { - if (pz[0] <= izi) { - pz[0] = izi; - pdest[0] = pixcolor; - } - } - break; - case 2: - count = 2 << sw32_d_y_aspect_shift; - - for (; count; count--, pz += sw32_d_zwidth, - pdest += sw32_screenwidth) { - if (pz[0] <= izi) { - pz[0] = izi; - pdest[0] = pixcolor; - } - - if (pz[1] <= izi) { - pz[1] = izi; - pdest[1] = pixcolor; - } - } - break; - case 3: - count = 3 << sw32_d_y_aspect_shift; - - for (; count; count--, pz += sw32_d_zwidth, - pdest += sw32_screenwidth) { - if (pz[0] <= izi) { - pz[0] = izi; - pdest[0] = pixcolor; - } - - if (pz[1] <= izi) { - pz[1] = izi; - pdest[1] = pixcolor; - } - - if (pz[2] <= izi) { - pz[2] = izi; - pdest[2] = pixcolor; - } - } - break; - case 4: - count = 4 << sw32_d_y_aspect_shift; - - for (; count; count--, pz += sw32_d_zwidth, - pdest += sw32_screenwidth) { - if (pz[0] <= izi) { - pz[0] = izi; - pdest[0] = pixcolor; - } - - if (pz[1] <= izi) { - pz[1] = izi; - pdest[1] = pixcolor; - } - - if (pz[2] <= izi) { - pz[2] = izi; - pdest[2] = pixcolor; - } - - if (pz[3] <= izi) { - pz[3] = izi; - pdest[3] = pixcolor; - } - } - break; - default: - count = pix << sw32_d_y_aspect_shift; - - for (; count; count--, pz += sw32_d_zwidth, - pdest += sw32_screenwidth) { - for (i = 0; i < pix; i++) { - if (pz[i] <= izi) { - pz[i] = izi; - pdest[i] = pixcolor; - } - } - } - break; - } - } - break; - case 2: - { - unsigned short *pdest = (unsigned short *) sw32_d_viewbuffer + - sw32_d_scantable[v] + u, - pixcolor = sw32_8to16table[(int) pparticle->color]; - switch (pix) { - case 1: - count = 1 << sw32_d_y_aspect_shift; - - for (; count; count--, pz += sw32_d_zwidth, - pdest += sw32_screenwidth) { - if (pz[0] <= izi) { - pz[0] = izi; - pdest[0] = pixcolor; - } - } - break; - case 2: - count = 2 << sw32_d_y_aspect_shift; - - for (; count; count--, pz += sw32_d_zwidth, - pdest += sw32_screenwidth) { - if (pz[0] <= izi) { - pz[0] = izi; - pdest[0] = pixcolor; - } - - if (pz[1] <= izi) { - pz[1] = izi; - pdest[1] = pixcolor; - } - } - break; - case 3: - count = 3 << sw32_d_y_aspect_shift; - - for (; count; count--, pz += sw32_d_zwidth, - pdest += sw32_screenwidth) { - if (pz[0] <= izi) { - pz[0] = izi; - pdest[0] = pixcolor; - } - - if (pz[1] <= izi) { - pz[1] = izi; - pdest[1] = pixcolor; - } - - if (pz[2] <= izi) { - pz[2] = izi; - pdest[2] = pixcolor; - } - } - break; - case 4: - count = 4 << sw32_d_y_aspect_shift; - - for (; count; count--, pz += sw32_d_zwidth, - pdest += sw32_screenwidth) { - if (pz[0] <= izi) { - pz[0] = izi; - pdest[0] = pixcolor; - } - - if (pz[1] <= izi) { - pz[1] = izi; - pdest[1] = pixcolor; - } - - if (pz[2] <= izi) { - pz[2] = izi; - pdest[2] = pixcolor; - } - - if (pz[3] <= izi) { - pz[3] = izi; - pdest[3] = pixcolor; - } - } - break; - default: - count = pix << sw32_d_y_aspect_shift; - - for (; count; count--, pz += sw32_d_zwidth, - pdest += sw32_screenwidth) { - for (i = 0; i < pix; i++) { - if (pz[i] <= izi) { - pz[i] = izi; - pdest[i] = pixcolor; - } - } - } - break; - } - } - break; - case 4: - { - int *pdest = (int *) sw32_d_viewbuffer + sw32_d_scantable[v] + u, - pixcolor = d_8to24table[(int) pparticle->color]; - switch (pix) { - case 1: - count = 1 << sw32_d_y_aspect_shift; - - for (; count; count--, pz += sw32_d_zwidth, - pdest += sw32_screenwidth) { - if (pz[0] <= izi) { - pz[0] = izi; - pdest[0] = pixcolor; - } - } - break; - case 2: - count = 2 << sw32_d_y_aspect_shift; - - for (; count; count--, pz += sw32_d_zwidth, - pdest += sw32_screenwidth) { - if (pz[0] <= izi) { - pz[0] = izi; - pdest[0] = pixcolor; - } - - if (pz[1] <= izi) { - pz[1] = izi; - pdest[1] = pixcolor; - } - } - break; - case 3: - count = 3 << sw32_d_y_aspect_shift; - - for (; count; count--, pz += sw32_d_zwidth, - pdest += sw32_screenwidth) { - if (pz[0] <= izi) { - pz[0] = izi; - pdest[0] = pixcolor; - } - - if (pz[1] <= izi) { - pz[1] = izi; - pdest[1] = pixcolor; - } - - if (pz[2] <= izi) { - pz[2] = izi; - pdest[2] = pixcolor; - } - } - break; - case 4: - count = 4 << sw32_d_y_aspect_shift; - - for (; count; count--, pz += sw32_d_zwidth, - pdest += sw32_screenwidth) { - if (pz[0] <= izi) { - pz[0] = izi; - pdest[0] = pixcolor; - } - - if (pz[1] <= izi) { - pz[1] = izi; - pdest[1] = pixcolor; - } - - if (pz[2] <= izi) { - pz[2] = izi; - pdest[2] = pixcolor; - } - - if (pz[3] <= izi) { - pz[3] = izi; - pdest[3] = pixcolor; - } - } - break; - default: - count = pix << sw32_d_y_aspect_shift; - - for (; count; count--, pz += sw32_d_zwidth, - pdest += sw32_screenwidth) { - for (i = 0; i < pix; i++) { - if (pz[i] <= izi) { - pz[i] = izi; - pdest[i] = pixcolor; - } - } - } - break; - } - } - break; - default: - Sys_Error("D_DrawParticles: unsupported r_pixbytes %i", - sw32_r_pixbytes); - } -} diff --git a/libs/video/renderer/sw32/d_polyse.c b/libs/video/renderer/sw32/d_polyse.c deleted file mode 100644 index 25d6a3c58..000000000 --- a/libs/video/renderer/sw32/d_polyse.c +++ /dev/null @@ -1,796 +0,0 @@ -/* - d_polyse.c - - routines for drawing sets of polygons sharing the same texture - (used for Alias models) - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include "QF/sys.h" - -#include "d_local.h" -#include "r_internal.h" - -static int ubasestep, errorterm, erroradjustup, erroradjustdown; - -// TODO: put in span spilling to shrink list size -// !!! if this is changed, it must be changed in d_polysa.s too !!! -#define DPS_MAXSPANS MAXHEIGHT+1 // +1 for spanpackage marking end - -// !!! if this is changed, it must be changed in asm_draw.h too !!! -typedef struct { - int pdest; - short *pz; - int count; - byte *ptex; - int sfrac, tfrac, light, zi; -} spanpackage_t; - -typedef struct { - int isflattop; - int numleftedges; - int *pleftedgevert0; - int *pleftedgevert1; - int *pleftedgevert2; - int numrightedges; - int *prightedgevert0; - int *prightedgevert1; - int *prightedgevert2; -} edgetable; - -static int r_p0[6], r_p1[6], r_p2[6]; - -static int d_xdenom; - -static edgetable *pedgetable; - -static edgetable edgetables[12] = { - {0, 1, r_p0, r_p2, NULL, 2, r_p0, r_p1, r_p2}, - {0, 2, r_p1, r_p0, r_p2, 1, r_p1, r_p2, NULL}, - {1, 1, r_p0, r_p2, NULL, 1, r_p1, r_p2, NULL}, - {0, 1, r_p1, r_p0, NULL, 2, r_p1, r_p2, r_p0}, - {0, 2, r_p0, r_p2, r_p1, 1, r_p0, r_p1, NULL}, - {0, 1, r_p2, r_p1, NULL, 1, r_p2, r_p0, NULL}, - {0, 1, r_p2, r_p1, NULL, 2, r_p2, r_p0, r_p1}, - {0, 2, r_p2, r_p1, r_p0, 1, r_p2, r_p0, NULL}, - {0, 1, r_p1, r_p0, NULL, 1, r_p1, r_p2, NULL}, - {1, 1, r_p2, r_p1, NULL, 1, r_p0, r_p1, NULL}, - {1, 1, r_p1, r_p0, NULL, 1, r_p2, r_p0, NULL}, - {0, 1, r_p0, r_p2, NULL, 1, r_p0, r_p1, NULL}, -}; - -static int r_sstepx, r_tstepx, r_lstepx, r_lstepy, r_sstepy, r_tstepy; -static int r_zistepx, r_zistepy; -static int d_aspancount, d_countextrastep; - -static spanpackage_t *a_spans; -static spanpackage_t *d_pedgespanpackage; -static int ystart; -static int d_pdest; -static byte *d_ptex; -static short *d_pz; -static int d_sfrac, d_tfrac, d_light, d_zi; -static int d_ptexextrastep, d_sfracextrastep; -static int d_tfracextrastep, d_lightextrastep, d_pdestextrastep; -static int d_lightbasestep, d_pdestbasestep, d_ptexbasestep; -static int d_sfracbasestep, d_tfracbasestep; -static int d_ziextrastep, d_zibasestep; -static int d_pzextrastep, d_pzbasestep; - -typedef struct { - int quotient; - int remainder; -} adivtab_t; - -static adivtab_t adivtab[32 * 32] = { -#include "adivtab.h" -}; - - -void -sw32_D_PolysetSetEdgeTable (void) -{ - int edgetableindex; - - // assume the vertices are already in top to bottom order - edgetableindex = 0; - - // determine which edges are right & left, and the order in which - // to rasterize them - if (r_p0[1] >= r_p1[1]) { - if (r_p0[1] == r_p1[1]) { - if (r_p0[1] < r_p2[1]) - pedgetable = &edgetables[2]; - else - pedgetable = &edgetables[5]; - - return; - } else { - edgetableindex = 1; - } - } - - if (r_p0[1] == r_p2[1]) { - if (edgetableindex) - pedgetable = &edgetables[8]; - else - pedgetable = &edgetables[9]; - - return; - } else if (r_p1[1] == r_p2[1]) { - if (edgetableindex) - pedgetable = &edgetables[10]; - else - pedgetable = &edgetables[11]; - - return; - } - - if (r_p0[1] > r_p2[1]) - edgetableindex += 2; - - if (r_p1[1] > r_p2[1]) - edgetableindex += 4; - - pedgetable = &edgetables[edgetableindex]; -} - -static void -D_DrawNonSubdiv (void) -{ - mtriangle_t *ptri; - finalvert_t *pfv, *index0, *index1, *index2; - int i; - int lnumtriangles; - - pfv = sw32_r_affinetridesc.pfinalverts; - ptri = sw32_r_affinetridesc.ptriangles; - lnumtriangles = sw32_r_affinetridesc.numtriangles; - - for (i = 0; i < lnumtriangles; i++, ptri++) { - index0 = pfv + ptri->vertindex[0]; - index1 = pfv + ptri->vertindex[1]; - index2 = pfv + ptri->vertindex[2]; - - d_xdenom = - (index0->v[1] - index1->v[1]) * (index0->v[0] - index2->v[0]) - - (index0->v[0] - index1->v[0]) * (index0->v[1] - index2->v[1]); - - if (d_xdenom >= 0) - continue; - - r_p0[0] = index0->v[0]; // u - r_p0[1] = index0->v[1]; // v - r_p0[2] = index0->v[2]; // s - r_p0[3] = index0->v[3]; // t - r_p0[4] = index0->v[4]; // light - r_p0[5] = index0->v[5]; // iz - - r_p1[0] = index1->v[0]; - r_p1[1] = index1->v[1]; - r_p1[2] = index1->v[2]; - r_p1[3] = index1->v[3]; - r_p1[4] = index1->v[4]; - r_p1[5] = index1->v[5]; - - r_p2[0] = index2->v[0]; - r_p2[1] = index2->v[1]; - r_p2[2] = index2->v[2]; - r_p2[3] = index2->v[3]; - r_p2[4] = index2->v[4]; - r_p2[5] = index2->v[5]; - - if (!ptri->facesfront) { - if (index0->flags & ALIAS_ONSEAM) - r_p0[2] += sw32_r_affinetridesc.seamfixupX16; - if (index1->flags & ALIAS_ONSEAM) - r_p1[2] += sw32_r_affinetridesc.seamfixupX16; - if (index2->flags & ALIAS_ONSEAM) - r_p2[2] += sw32_r_affinetridesc.seamfixupX16; - } - - sw32_D_PolysetSetEdgeTable (); - sw32_D_RasterizeAliasPolySmooth (); - } -} - -void -sw32_D_PolysetDraw (void) -{ - spanpackage_t spans[DPS_MAXSPANS + 1 + - ((CACHE_SIZE - 1) / sizeof (spanpackage_t)) + 1]; - - // one extra because of cache line pretouching - - a_spans = (spanpackage_t *) - (((intptr_t) &spans[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); - - D_DrawNonSubdiv (); -} - -void -sw32_D_PolysetScanLeftEdge (int height) -{ - - do { - d_pedgespanpackage->pdest = d_pdest; - d_pedgespanpackage->pz = d_pz; - d_pedgespanpackage->count = d_aspancount; - d_pedgespanpackage->ptex = d_ptex; - - d_pedgespanpackage->sfrac = d_sfrac; - d_pedgespanpackage->tfrac = d_tfrac; - - // FIXME: need to clamp l, s, t, at both ends? - d_pedgespanpackage->light = d_light; - d_pedgespanpackage->zi = d_zi; - - d_pedgespanpackage++; - - errorterm += erroradjustup; - if (errorterm >= 0) { - d_pdest += d_pdestextrastep; - d_pz += d_pzextrastep; - d_aspancount += d_countextrastep; - d_ptex += d_ptexextrastep; - d_sfrac += d_sfracextrastep; - d_ptex += d_sfrac >> 16; - - d_sfrac &= 0xFFFF; - d_tfrac += d_tfracextrastep; - if (d_tfrac & 0x10000) { - d_ptex += sw32_r_affinetridesc.skinwidth; - d_tfrac &= 0xFFFF; - } - d_light += d_lightextrastep; - d_zi += d_ziextrastep; - errorterm -= erroradjustdown; - } else { - d_pdest += d_pdestbasestep; - d_pz += d_pzbasestep; - d_aspancount += ubasestep; - d_ptex += d_ptexbasestep; - d_sfrac += d_sfracbasestep; - d_ptex += d_sfrac >> 16; - d_sfrac &= 0xFFFF; - d_tfrac += d_tfracbasestep; - if (d_tfrac & 0x10000) { - d_ptex += sw32_r_affinetridesc.skinwidth; - d_tfrac &= 0xFFFF; - } - d_light += d_lightbasestep; - d_zi += d_zibasestep; - } - } while (--height); -} - -static void -D_PolysetSetUpForLineScan (fixed8_t startvertu, fixed8_t startvertv, - fixed8_t endvertu, fixed8_t endvertv) -{ - double dm, dn; - int tm, tn; - adivtab_t *ptemp; - - // TODO: implement x86 version - - errorterm = -1; - - tm = endvertu - startvertu; - tn = endvertv - startvertv; - - if (((tm <= 16) && (tm >= -15)) && ((tn <= 16) && (tn >= -15))) { - ptemp = &adivtab[((tm + 15) << 5) + (tn + 15)]; - ubasestep = ptemp->quotient; - erroradjustup = ptemp->remainder; - erroradjustdown = tn; - } else { - dm = (double) tm; - dn = (double) tn; - - FloorDivMod (dm, dn, &ubasestep, &erroradjustup); - - erroradjustdown = dn; - } -} - -void -sw32_D_PolysetCalcGradients (int skinwidth) -{ - float xstepdenominv, ystepdenominv, t0, t1; - float p01_minus_p21, p11_minus_p21, p00_minus_p20, p10_minus_p20; - - p00_minus_p20 = r_p0[0] - r_p2[0]; - p01_minus_p21 = r_p0[1] - r_p2[1]; - p10_minus_p20 = r_p1[0] - r_p2[0]; - p11_minus_p21 = r_p1[1] - r_p2[1]; - - xstepdenominv = 1.0 / (float) d_xdenom; - - ystepdenominv = -xstepdenominv; - - // ceil () for light so positive steps are exaggerated, negative steps - // diminished, pushing us away from underflow toward overflow. Underflow - // is very visible, overflow is very unlikely, because of ambient lighting - t0 = r_p0[4] - r_p2[4]; - t1 = r_p1[4] - r_p2[4]; - r_lstepx = (int) - ceil ((t1 * p01_minus_p21 - t0 * p11_minus_p21) * xstepdenominv); - r_lstepy = (int) - ceil ((t1 * p00_minus_p20 - t0 * p10_minus_p20) * ystepdenominv); - - t0 = r_p0[2] - r_p2[2]; - t1 = r_p1[2] - r_p2[2]; - r_sstepx = (int) ((t1 * p01_minus_p21 - t0 * p11_minus_p21) * - xstepdenominv); - r_sstepy = (int) ((t1 * p00_minus_p20 - t0 * p10_minus_p20) * - ystepdenominv); - - t0 = r_p0[3] - r_p2[3]; - t1 = r_p1[3] - r_p2[3]; - r_tstepx = (int) ((t1 * p01_minus_p21 - t0 * p11_minus_p21) * - xstepdenominv); - r_tstepy = (int) ((t1 * p00_minus_p20 - t0 * p10_minus_p20) * - ystepdenominv); - - t0 = r_p0[5] - r_p2[5]; - t1 = r_p1[5] - r_p2[5]; - r_zistepx = (int) ((t1 * p01_minus_p21 - t0 * p11_minus_p21) * - xstepdenominv); - r_zistepy = (int) ((t1 * p00_minus_p20 - t0 * p10_minus_p20) * - ystepdenominv); - -// a_sstepxfrac = r_sstepx & 0xFFFF; -// a_tstepxfrac = r_tstepx & 0xFFFF; - -// a_ststepxwhole = skinwidth * (r_tstepx >> 16) + (r_sstepx >> 16); -} - - -static void -D_PolysetDrawSpans (spanpackage_t * pspanpackage) -{ - int i, j, texscantable[2*MAX_LBM_HEIGHT], *texscan; - // LordHavoc: compute skin row table - for (i = 0, j = -sw32_r_affinetridesc.skinheight * sw32_r_affinetridesc.skinwidth; - i < sw32_r_affinetridesc.skinheight*2;i++, j += sw32_r_affinetridesc.skinwidth) - texscantable[i] = j; - texscan = texscantable + sw32_r_affinetridesc.skinheight; - - switch(sw32_r_pixbytes) { - case 1: - { - int lcount, count = 0; - byte *lpdest; - byte *lptex; - int lsfrac, ltfrac; - int llight; - int lzi; - short *lpz; - - do - { - lcount = d_aspancount - pspanpackage->count; - - errorterm += erroradjustup; - if (errorterm >= 0) - { - d_aspancount += d_countextrastep; - errorterm -= erroradjustdown; - } - else - d_aspancount += ubasestep; - - if (lcount) - { - lpdest = (byte *) sw32_d_viewbuffer + pspanpackage->pdest; - lptex = pspanpackage->ptex; - lpz = pspanpackage->pz; - lsfrac = pspanpackage->sfrac; - ltfrac = pspanpackage->tfrac; - llight = pspanpackage->light; - lzi = pspanpackage->zi; - - // LordHavoc: optimized zbuffer check (switchs between - // loops when state changes, and quickly skips groups - // of hidden pixels) - do - { - if ((lzi >> 16) < *lpz) // hidden - { - count = 0; - goto skiploop8; - } -drawloop8: - *lpz++ = lzi >> 16; - *lpdest++ = ((byte *)sw32_acolormap) - [(llight & 0xFF00) | lptex[texscan[ltfrac >> 16] + - (lsfrac >> 16)]]; - lzi += r_zistepx; - lsfrac += r_sstepx; - ltfrac += r_tstepx; - llight += r_lstepx; - } - while (--lcount); - goto done8; - - do - { - if ((lzi >> 16) >= *lpz) // draw - { - lsfrac += r_sstepx * count; - ltfrac += r_tstepx * count; - llight += r_lstepx * count; - lpdest += count; - goto drawloop8; - } -skiploop8: - count++; - lzi += r_zistepx; - lpz++; - } - while (--lcount); -done8: ; - } - - pspanpackage++; - } - while (pspanpackage->count != -999999); - } - break; - - case 2: - { - int lcount, count = 0; - short *lpdest; - byte *lptex; - int lsfrac, ltfrac; - int llight; - int lzi; - short *lpz; - - do - { - lcount = d_aspancount - pspanpackage->count; - - errorterm += erroradjustup; - if (errorterm >= 0) - { - d_aspancount += d_countextrastep; - errorterm -= erroradjustdown; - } - else - d_aspancount += ubasestep; - - if (lcount) - { - lpdest = (short *) sw32_d_viewbuffer + pspanpackage->pdest; - lptex = pspanpackage->ptex; - lpz = pspanpackage->pz; - lsfrac = pspanpackage->sfrac; - ltfrac = pspanpackage->tfrac; - llight = pspanpackage->light; - lzi = pspanpackage->zi; - - do - { - if ((lzi >> 16) < *lpz) // hidden - { - count = 0; - goto skiploop16; - } -drawloop16: - *lpz++ = lzi >> 16; - *lpdest++ = ((short *)sw32_acolormap)[(llight & 0xFF00) | lptex[texscan[ltfrac >> 16] + (lsfrac >> 16)]]; - lzi += r_zistepx; - lsfrac += r_sstepx; - ltfrac += r_tstepx; - llight += r_lstepx; - } - while (--lcount); - goto done16; - - do - { - if ((lzi >> 16) >= *lpz) // draw - { - lsfrac += r_sstepx * count; - ltfrac += r_tstepx * count; - llight += r_lstepx * count; - lpdest += count; - goto drawloop16; - } -skiploop16: - count++; - lzi += r_zistepx; - lpz++; - } - while (--lcount); -done16: ; - } - - pspanpackage++; - } - while (pspanpackage->count != -999999); - } - break; - - case 4: - { - int lcount, count = 0; - int *lpdest; - byte *lptex; - int lsfrac, ltfrac; - int llight; - int lzi; - short *lpz; - - do - { - lcount = d_aspancount - pspanpackage->count; - - errorterm += erroradjustup; - if (errorterm >= 0) - { - d_aspancount += d_countextrastep; - errorterm -= erroradjustdown; - } - else - d_aspancount += ubasestep; - - if (lcount) - { - lpdest = (int *) sw32_d_viewbuffer + pspanpackage->pdest; - lptex = pspanpackage->ptex; - lpz = pspanpackage->pz; - lsfrac = pspanpackage->sfrac; - ltfrac = pspanpackage->tfrac; - llight = pspanpackage->light; - lzi = pspanpackage->zi; - - do - { - if ((lzi >> 16) < *lpz) // hidden - { - count = 0; - goto skiploop32; - } -drawloop32: - *lpz++ = lzi >> 16; - *lpdest++ = - vid.colormap32[(llight & 0xFF00) | - lptex[texscan[ltfrac >> 16] + - (lsfrac >> 16)]]; - lzi += r_zistepx; - lsfrac += r_sstepx; - ltfrac += r_tstepx; - llight += r_lstepx; - } - while (--lcount); - goto done32; - - do - { - if ((lzi >> 16) >= *lpz) // draw - { - lsfrac += r_sstepx * count; - ltfrac += r_tstepx * count; - llight += r_lstepx * count; - lpdest += count; - goto drawloop32; - } -skiploop32: - count++; - lzi += r_zistepx; - lpz++; - } - while (--lcount); -done32: ; - } - - pspanpackage++; - } - while (pspanpackage->count != -999999); - } - break; - - default: - Sys_Error("D_PolysetDrawSpans: unsupported r_pixbytes %i", - sw32_r_pixbytes); - } -} - - -void -sw32_D_RasterizeAliasPolySmooth (void) -{ - int initialleftheight, initialrightheight; - int *plefttop, *prighttop, *pleftbottom, *prightbottom; - int working_lstepx, originalcount; - - plefttop = pedgetable->pleftedgevert0; - prighttop = pedgetable->prightedgevert0; - - pleftbottom = pedgetable->pleftedgevert1; - prightbottom = pedgetable->prightedgevert1; - - initialleftheight = pleftbottom[1] - plefttop[1]; - initialrightheight = prightbottom[1] - prighttop[1]; - - // set the s, t, and light gradients, which are consistent across the - // triangle, because being a triangle, things are affine - sw32_D_PolysetCalcGradients (sw32_r_affinetridesc.skinwidth); - -// rasterize the polygon - - // scan out the top (and possibly only) part of the left edge - D_PolysetSetUpForLineScan (plefttop[0], plefttop[1], - pleftbottom[0], pleftbottom[1]); - - d_pedgespanpackage = a_spans; - - ystart = plefttop[1]; - d_aspancount = plefttop[0] - prighttop[0]; - - d_ptex = (byte *) sw32_r_affinetridesc.pskin + (plefttop[2] >> 16) + - (plefttop[3] >> 16) * sw32_r_affinetridesc.skinwidth; - d_sfrac = plefttop[2] & 0xFFFF; - d_tfrac = plefttop[3] & 0xFFFF; - d_pzbasestep = sw32_d_zwidth + ubasestep; - d_pzextrastep = d_pzbasestep + 1; - d_light = plefttop[4]; - d_zi = plefttop[5]; - - d_pdestbasestep = sw32_screenwidth + ubasestep; - d_pdestextrastep = d_pdestbasestep + 1; - // LordHavoc: d_pdest has been changed to pixel offset - d_pdest = ystart * sw32_screenwidth + plefttop[0]; - d_pz = sw32_d_pzbuffer + ystart * sw32_d_zwidth + plefttop[0]; - -// TODO: can reuse partial expressions here - - // for negative steps in x along left edge, bias toward overflow rather - // than underflow (sort of turning the floor () we did in the gradient - // calcs into ceil (), but plus a little bit) - if (ubasestep < 0) - working_lstepx = r_lstepx - 1; - else - working_lstepx = r_lstepx; - - d_countextrastep = ubasestep + 1; - d_ptexbasestep = ((r_sstepy + r_sstepx * ubasestep) >> 16) + - ((r_tstepy + r_tstepx * ubasestep) >> 16) * sw32_r_affinetridesc.skinwidth; - d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) & 0xFFFF; - d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) & 0xFFFF; - d_lightbasestep = r_lstepy + working_lstepx * ubasestep; - d_zibasestep = r_zistepy + r_zistepx * ubasestep; - - d_ptexextrastep = ((r_sstepy + r_sstepx * d_countextrastep) >> 16) + - ((r_tstepy + r_tstepx * d_countextrastep) >> 16) * - sw32_r_affinetridesc.skinwidth; - d_sfracextrastep = (r_sstepy + r_sstepx * d_countextrastep) & 0xFFFF; - d_tfracextrastep = (r_tstepy + r_tstepx * d_countextrastep) & 0xFFFF; - d_lightextrastep = d_lightbasestep + working_lstepx; - d_ziextrastep = d_zibasestep + r_zistepx; - - sw32_D_PolysetScanLeftEdge (initialleftheight); - - // scan out the bottom part of the left edge, if it exists - if (pedgetable->numleftedges == 2) { - int height; - - plefttop = pleftbottom; - pleftbottom = pedgetable->pleftedgevert2; - - D_PolysetSetUpForLineScan (plefttop[0], plefttop[1], - pleftbottom[0], pleftbottom[1]); - - height = pleftbottom[1] - plefttop[1]; - -// TODO: make this a function; modularize this function in general - - ystart = plefttop[1]; - d_aspancount = plefttop[0] - prighttop[0]; - d_ptex = (byte *) sw32_r_affinetridesc.pskin + (plefttop[2] >> 16) + - (plefttop[3] >> 16) * sw32_r_affinetridesc.skinwidth; - d_sfrac = 0; - d_tfrac = 0; - d_light = plefttop[4]; - d_zi = plefttop[5]; - - d_pdestbasestep = sw32_screenwidth + ubasestep; - d_pdestextrastep = d_pdestbasestep + 1; - // LordHavoc: d_pdest and relatives have been changed to pixel - // offsets into framebuffer - d_pdest = ystart * sw32_screenwidth + plefttop[0]; - d_pzbasestep = sw32_d_zwidth + ubasestep; - d_pzextrastep = d_pzbasestep + 1; - d_pz = sw32_d_pzbuffer + ystart * sw32_d_zwidth + plefttop[0]; - - if (ubasestep < 0) - working_lstepx = r_lstepx - 1; - else - working_lstepx = r_lstepx; - - d_countextrastep = ubasestep + 1; - d_ptexbasestep = ((r_sstepy + r_sstepx * ubasestep) >> 16) + - ((r_tstepy + r_tstepx * ubasestep) >> 16) * - sw32_r_affinetridesc.skinwidth; - d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) & 0xFFFF; - d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) & 0xFFFF; - d_lightbasestep = r_lstepy + working_lstepx * ubasestep; - d_zibasestep = r_zistepy + r_zistepx * ubasestep; - - d_ptexextrastep = ((r_sstepy + r_sstepx * d_countextrastep) >> 16) + - ((r_tstepy + r_tstepx * d_countextrastep) >> 16) * - sw32_r_affinetridesc.skinwidth; - d_sfracextrastep = (r_sstepy + r_sstepx * d_countextrastep) & 0xFFFF; - d_tfracextrastep = (r_tstepy + r_tstepx * d_countextrastep) & 0xFFFF; - d_lightextrastep = d_lightbasestep + working_lstepx; - d_ziextrastep = d_zibasestep + r_zistepx; - - sw32_D_PolysetScanLeftEdge (height); - } - // scan out the top (and possibly only) part of the right edge, updating - // the count field - d_pedgespanpackage = a_spans; - - D_PolysetSetUpForLineScan (prighttop[0], prighttop[1], - prightbottom[0], prightbottom[1]); - d_aspancount = 0; - d_countextrastep = ubasestep + 1; - originalcount = a_spans[initialrightheight].count; - a_spans[initialrightheight].count = -999999; // mark end of the - // spanpackages - D_PolysetDrawSpans (a_spans); - - // scan out the bottom part of the right edge, if it exists - if (pedgetable->numrightedges == 2) { - int height; - spanpackage_t *pstart; - - pstart = a_spans + initialrightheight; - pstart->count = originalcount; - - d_aspancount = prightbottom[0] - prighttop[0]; - - prighttop = prightbottom; - prightbottom = pedgetable->prightedgevert2; - - height = prightbottom[1] - prighttop[1]; - - D_PolysetSetUpForLineScan (prighttop[0], prighttop[1], - prightbottom[0], prightbottom[1]); - - d_countextrastep = ubasestep + 1; - a_spans[initialrightheight + height].count = -999999; - // mark end of the spanpackages - D_PolysetDrawSpans (pstart); - } -} diff --git a/libs/video/renderer/sw32/d_scan.c b/libs/video/renderer/sw32/d_scan.c deleted file mode 100644 index c77f131df..000000000 --- a/libs/video/renderer/sw32/d_scan.c +++ /dev/null @@ -1,884 +0,0 @@ -/* - d_scan.c - - Portable C scan-level rasterization code, all pixel depths. - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include "QF/qendian.h" -#include "QF/render.h" -#include "QF/sys.h" - -#include "compat.h" -#include "d_local.h" -#include "r_internal.h" -#include "vid_internal.h" - -static byte *r_turb_pbase; -static void *r_turb_pdest; -static fixed16_t r_turb_s, r_turb_t, r_turb_sstep, r_turb_tstep; -static int *r_turb_turb; -static int r_turb_spancount; - -/* - D_WarpScreen - - this performs a slight compression of the screen at the same time as - the sine warp, to keep the edges from wrapping -*/ -void -sw32_D_WarpScreen (void) -{ - switch(sw32_r_pixbytes) { - case 1: - { - int w, h; - int u, v; - byte *dest; - int *turb; - int *col; - byte **row; - byte *rowptr[MAXHEIGHT]; - int column[MAXWIDTH]; - float wratio, hratio; - - w = r_refdef.vrect.width; - h = r_refdef.vrect.height; - - wratio = w / (float) scr_vrect.width; - hratio = h / (float) scr_vrect.height; - - for (v = 0; v < scr_vrect.height + AMP2 * 2; v++) { - rowptr[v] = (byte *) sw32_d_viewbuffer + (r_refdef.vrect.y * - sw32_screenwidth) + - (sw32_screenwidth * (int) ((float) v * hratio * h / - (h + AMP2 * 2))); - } - - for (u = 0; u < scr_vrect.width + AMP2 * 2; u++) { - column[u] = r_refdef.vrect.x + - (int) ((float) u * wratio * w / (w + AMP2 * 2)); - } - - turb = sw32_intsintable + ((int) (vr_data.realtime * SPEED) & (CYCLE - 1)); - dest = (byte *)vid.buffer + scr_vrect.y * vid.rowbytes + - scr_vrect.x; - - for (v = 0; v < scr_vrect.height; v++, dest += vid.rowbytes) { - col = &column[turb[v]]; - row = &rowptr[v]; - for (u = 0; u < scr_vrect.width; u += 4) { - dest[u + 0] = row[turb[u + 0]][col[u + 0]]; - dest[u + 1] = row[turb[u + 1]][col[u + 1]]; - dest[u + 2] = row[turb[u + 2]][col[u + 2]]; - dest[u + 3] = row[turb[u + 3]][col[u + 3]]; - } - } - } - break; - case 2: - { - int w, h; - int u, v; - short *dest; - int *turb; - int *col; - short **row; - short *rowptr[MAXHEIGHT]; - int column[MAXWIDTH]; - float wratio, hratio; - - w = r_refdef.vrect.width; - h = r_refdef.vrect.height; - - wratio = w / (float) scr_vrect.width; - hratio = h / (float) scr_vrect.height; - - for (v = 0; v < scr_vrect.height + AMP2 * 2; v++) { - rowptr[v] = (short *) sw32_d_viewbuffer + - (r_refdef.vrect.y * sw32_screenwidth) + - (sw32_screenwidth * (int) ((float) v * hratio * h / - (h + AMP2 * 2))); - } - - for (u = 0; u < scr_vrect.width + AMP2 * 2; u++) { - column[u] = r_refdef.vrect.x + - (int) ((float) u * wratio * w / (w + AMP2 * 2)); - } - - turb = sw32_intsintable + ((int) (vr_data.realtime * SPEED) & (CYCLE - 1)); - dest = (short *) vid.buffer + scr_vrect.y * (vid.rowbytes >> 1) + - scr_vrect.x; - - for (v = 0; v < scr_vrect.height; v++, dest += (vid.rowbytes >> 1)) { - col = &column[turb[v]]; - row = &rowptr[v]; - for (u = 0; u < scr_vrect.width; u += 4) { - dest[u + 0] = row[turb[u + 0]][col[u + 0]]; - dest[u + 1] = row[turb[u + 1]][col[u + 1]]; - dest[u + 2] = row[turb[u + 2]][col[u + 2]]; - dest[u + 3] = row[turb[u + 3]][col[u + 3]]; - } - } - } - break; - case 4: - { - int w, h; - int u, v; - int *dest; - int *turb; - int *col; - int **row; - int *rowptr[MAXHEIGHT]; - int column[MAXWIDTH]; - float wratio, hratio; - - w = r_refdef.vrect.width; - h = r_refdef.vrect.height; - - wratio = w / (float) scr_vrect.width; - hratio = h / (float) scr_vrect.height; - - for (v = 0; v < scr_vrect.height + AMP2 * 2; v++) { - rowptr[v] = (int *) sw32_d_viewbuffer + - (r_refdef.vrect.y * sw32_screenwidth) + - (sw32_screenwidth * (int) ((float) v * hratio * h / - (h + AMP2 * 2))); - } - - for (u = 0; u < scr_vrect.width + AMP2 * 2; u++) { - column[u] = r_refdef.vrect.x + - (int) ((float) u * wratio * w / (w + AMP2 * 2)); - } - - turb = sw32_intsintable + ((int) (vr_data.realtime * SPEED) & (CYCLE - 1)); - dest = (int *) vid.buffer + scr_vrect.y * (vid.rowbytes >> 2) + - scr_vrect.x; - - for (v = 0; v < scr_vrect.height; v++, dest += (vid.rowbytes >> 2)) { - col = &column[turb[v]]; - row = &rowptr[v]; - for (u = 0; u < scr_vrect.width; u += 4) { - dest[u + 0] = row[turb[u + 0]][col[u + 0]]; - dest[u + 1] = row[turb[u + 1]][col[u + 1]]; - dest[u + 2] = row[turb[u + 2]][col[u + 2]]; - dest[u + 3] = row[turb[u + 3]][col[u + 3]]; - } - } - } - break; - default: - Sys_Error("D_WarpScreen: unsupported r_pixbytes %i", sw32_r_pixbytes); - } -} - -static void -D_DrawTurbulentSpan (void) -{ - int sturb, tturb; - - switch (sw32_r_pixbytes) { - case 1: - { - byte *pdest = (byte *) r_turb_pdest; - do { - sturb = ((r_turb_s + r_turb_turb[(r_turb_t >> 16) & - (CYCLE - 1)]) >> 16) & 63; - tturb = ((r_turb_t + r_turb_turb[(r_turb_s >> 16) & - (CYCLE - 1)]) >> 16) & 63; - *pdest++ = r_turb_pbase[(tturb << 6) + sturb]; - r_turb_s += r_turb_sstep; - r_turb_t += r_turb_tstep; - } while (--r_turb_spancount > 0); - r_turb_pdest = (byte *)pdest; - } - break; - case 2: - { - short *pdest = (short *) r_turb_pdest; - do { - sturb = ((r_turb_s + r_turb_turb[(r_turb_t >> 16) & - (CYCLE - 1)]) >> 16) & 63; - tturb = ((r_turb_t + r_turb_turb[(r_turb_s >> 16) & - (CYCLE - 1)]) >> 16) & 63; - *pdest++ = sw32_8to16table[r_turb_pbase[(tturb << 6) + sturb]]; - r_turb_s += r_turb_sstep; - r_turb_t += r_turb_tstep; - } while (--r_turb_spancount > 0); - r_turb_pdest = (byte *)pdest; - } - break; - case 4: - { - int *pdest = (int *) r_turb_pdest; - do { - sturb = ((r_turb_s + r_turb_turb[(r_turb_t >> 16) & - (CYCLE - 1)]) >> 16) & 63; - tturb = ((r_turb_t + r_turb_turb[(r_turb_s >> 16) & - (CYCLE - 1)]) >> 16) & 63; - *pdest++ = d_8to24table[r_turb_pbase[(tturb << 6) + sturb]]; - r_turb_s += r_turb_sstep; - r_turb_t += r_turb_tstep; - } while (--r_turb_spancount > 0); - r_turb_pdest = (byte *)pdest; - } - break; - default: - Sys_Error("D_DrawTurbulentSpan: unsupported r_pixbytes %i", - sw32_r_pixbytes); - } -} - -void -sw32_Turbulent (espan_t *pspan) -{ - int count; - fixed16_t snext, tnext; - float sdivz, tdivz, zi, z, du, dv, spancountminus1; - float sdivz16stepu, tdivz16stepu, zi16stepu; - - r_turb_turb = sw32_sintable + ((int) (vr_data.realtime * SPEED) & (CYCLE - 1)); - - r_turb_sstep = 0; // keep compiler happy - r_turb_tstep = 0; // ditto - - r_turb_pbase = (byte *) sw32_cacheblock; - - sdivz16stepu = sw32_d_sdivzstepu * 16; - tdivz16stepu = sw32_d_tdivzstepu * 16; - zi16stepu = d_zistepu * 16 * 65536; - - do { - r_turb_pdest = (byte *) sw32_d_viewbuffer + ((sw32_screenwidth * pspan->v) + - pspan->u) * sw32_r_pixbytes; - - count = pspan->count; - - // calculate the initial s/z, t/z, 1/z, s, and t and clamp - du = (float) pspan->u; - dv = (float) pspan->v; - - sdivz = sw32_d_sdivzorigin + dv * sw32_d_sdivzstepv + du * sw32_d_sdivzstepu; - tdivz = sw32_d_tdivzorigin + dv * sw32_d_tdivzstepv + du * sw32_d_tdivzstepu; - zi = (d_ziorigin + dv * d_zistepv + du * d_zistepu) * 65536.0f; - z = sw32_d_zitable[(unsigned short) zi]; - - r_turb_s = (int) (sdivz * z) + sw32_sadjust; - if (r_turb_s > sw32_bbextents) - r_turb_s = sw32_bbextents; - else if (r_turb_s < 0) - r_turb_s = 0; - - r_turb_t = (int) (tdivz * z) + sw32_tadjust; - if (r_turb_t > sw32_bbextentt) - r_turb_t = sw32_bbextentt; - else if (r_turb_t < 0) - r_turb_t = 0; - - do { - // calculate s and t at the far end of the span - if (count >= 16) - r_turb_spancount = 16; - else - r_turb_spancount = count; - - count -= r_turb_spancount; - - if (count) { - // calculate s/z, t/z, zi->fixed s and t at far end of span, - // calculate s and t steps across span by shifting - sdivz += sdivz16stepu; - tdivz += tdivz16stepu; - zi += zi16stepu; - z = sw32_d_zitable[(unsigned short) zi]; - - snext = (int) (sdivz * z) + sw32_sadjust; - if (snext > sw32_bbextents) - snext = sw32_bbextents; - else if (snext < 16) - snext = 16; // prevent round-off error on <0 - // steps from - // from causing overstepping & running off the - // edge of the texture - - tnext = (int) (tdivz * z) + sw32_tadjust; - if (tnext > sw32_bbextentt) - tnext = sw32_bbextentt; - else if (tnext < 16) - tnext = 16; // guard against round-off error on - // <0 steps - - r_turb_sstep = (snext - r_turb_s) >> 4; - r_turb_tstep = (tnext - r_turb_t) >> 4; - } else { - // calculate s/z, t/z, zi->fixed s and t at last pixel in - // span (so can't step off polygon), clamp, calculate s and t - // steps across span by division, biasing steps low so we - // don't run off the texture - spancountminus1 = (float) (r_turb_spancount - 1); - sdivz += sw32_d_sdivzstepu * spancountminus1; - tdivz += sw32_d_tdivzstepu * spancountminus1; - zi += d_zistepu * 65536.0f * spancountminus1; - z = sw32_d_zitable[(unsigned short) zi]; - snext = (int) (sdivz * z) + sw32_sadjust; - if (snext > sw32_bbextents) - snext = sw32_bbextents; - else if (snext < 16) - snext = 16; // prevent round-off error on <0 steps - // from causing overstepping & running - // off the edge of the texture - - tnext = (int) (tdivz * z) + sw32_tadjust; - if (tnext > sw32_bbextentt) - tnext = sw32_bbextentt; - else if (tnext < 16) - tnext = 16; // guard against round-off error on - // <0 steps - - if (r_turb_spancount > 1) { - r_turb_sstep = (snext - r_turb_s) / (r_turb_spancount - 1); - r_turb_tstep = (tnext - r_turb_t) / (r_turb_spancount - 1); - } - } - - r_turb_s = r_turb_s & ((CYCLE << 16) - 1); - r_turb_t = r_turb_t & ((CYCLE << 16) - 1); - - D_DrawTurbulentSpan (); - - r_turb_s = snext; - r_turb_t = tnext; - - } while (count > 0); - - } while ((pspan = pspan->pnext) != NULL); -} - -void -sw32_D_DrawSpans (espan_t *pspan) -{ - switch(sw32_r_pixbytes) { - case 1: - { - byte *pbase = (byte *) sw32_cacheblock, *pdest; - int count; - fixed16_t s, t, snext, tnext, sstep, tstep; - float sdivz, tdivz, zi, z, du, dv; - float sdivz8stepu, tdivz8stepu, zi8stepu; - - sstep = 0; // keep compiler happy - tstep = 0; // ditto - - sdivz8stepu = sw32_d_sdivzstepu * 8; - tdivz8stepu = sw32_d_tdivzstepu * 8; - zi8stepu = d_zistepu * 8 * 65536; - - do { - pdest = (byte *) sw32_d_viewbuffer + (sw32_screenwidth * pspan->v) + - pspan->u; - - count = pspan->count; - - // calculate the initial s/z, t/z, 1/z, s, and t and clamp - du = (float) pspan->u; - dv = (float) pspan->v; - - sdivz = sw32_d_sdivzorigin + dv * sw32_d_sdivzstepv + du * sw32_d_sdivzstepu; - tdivz = sw32_d_tdivzorigin + dv * sw32_d_tdivzstepv + du * sw32_d_tdivzstepu; - zi = (d_ziorigin + dv * d_zistepv + du * d_zistepu) * 65536.0f; - z = sw32_d_zitable[(unsigned short) zi]; - - s = (int) (sdivz * z) + sw32_sadjust; - s = bound(0, s, sw32_bbextents); - t = (int) (tdivz * z) + sw32_tadjust; - t = bound(0, t, sw32_bbextentt); - - while(count >= 8) { - count -= 8; - // calculate s/z, t/z, zi->fixed s and t at far end of span, - // calculate s and t steps across span by shifting - sdivz += sdivz8stepu; - tdivz += tdivz8stepu; - zi += zi8stepu; - z = sw32_d_zitable[(unsigned short) zi]; - - // prevent round-off error on <0 steps from from causing - // overstepping & running off the edge of the texture - snext = (int) (sdivz * z) + sw32_sadjust; - snext = bound(8, snext, sw32_bbextents); - tnext = (int) (tdivz * z) + sw32_tadjust; - tnext = bound(8, tnext, sw32_bbextentt); - - sstep = (snext - s) >> 3; - tstep = (tnext - t) >> 3; - - pdest[0] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep;t += tstep; - pdest[1] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[2] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[3] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[4] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[5] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[6] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[7] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s = snext; - t = tnext; - pdest += 8; - } - if (count) - { - // calculate s/z, t/z, zi->fixed s and t at last pixel in span - // (so can't step off polygon), clamp, calculate s and t steps - // across span by division, biasing steps low so we don't run - // off the texture - //countminus1 = (float) (count - 1); - sdivz += sw32_d_sdivzstepu * count; //minus1; - tdivz += sw32_d_tdivzstepu * count; //minus1; - zi += d_zistepu * 65536.0f * count; //minus1; - z = sw32_d_zitable[(unsigned short) zi]; - - // prevent round-off error on <0 steps from from causing - // overstepping & running off the edge of the texture - snext = (int) (sdivz * z) + sw32_sadjust; - snext = bound(count, snext, sw32_bbextents); - tnext = (int) (tdivz * z) + sw32_tadjust; - tnext = bound(count, tnext, sw32_bbextentt); - - if (count > 1) { - sstep = (snext - s) / count; //(count - 1); - tstep = (tnext - t) / count; //(count - 1); - - if (count & 4) - { - pdest[0] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[1] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[2] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[3] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest += 4; - } - if (count & 2) - { - pdest[0] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[1] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest += 2; - } - if (count & 1) - pdest[0] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - } - else - { - pdest[0] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - } - } - } while ((pspan = pspan->pnext) != NULL); - } - break; - case 2: - { - short *pbase = (short *) sw32_cacheblock, *pdest; - int count; - fixed16_t s, t, snext, tnext, sstep, tstep; - float sdivz, tdivz, zi, z, du, dv; - float sdivz8stepu, tdivz8stepu, zi8stepu; - - sstep = 0; // keep compiler happy - tstep = 0; // ditto - - sdivz8stepu = sw32_d_sdivzstepu * 8; - tdivz8stepu = sw32_d_tdivzstepu * 8; - zi8stepu = d_zistepu * 8 * 65536; - - do { - pdest = (short *) sw32_d_viewbuffer + (sw32_screenwidth * pspan->v) + - pspan->u; - - count = pspan->count; - - // calculate the initial s/z, t/z, 1/z, s, and t and clamp - du = (float) pspan->u; - dv = (float) pspan->v; - - sdivz = sw32_d_sdivzorigin + dv * sw32_d_sdivzstepv + du * sw32_d_sdivzstepu; - tdivz = sw32_d_tdivzorigin + dv * sw32_d_tdivzstepv + du * sw32_d_tdivzstepu; - zi = (d_ziorigin + dv * d_zistepv + du * d_zistepu) * 65536.0f; - z = sw32_d_zitable[(unsigned short) zi]; - - s = (int) (sdivz * z) + sw32_sadjust; - s = bound(0, s, sw32_bbextents); - t = (int) (tdivz * z) + sw32_tadjust; - t = bound(0, t, sw32_bbextentt); - - while(count >= 8) { - count -= 8; - // calculate s/z, t/z, zi->fixed s and t at far end of span, - // calculate s and t steps across span by shifting - sdivz += sdivz8stepu; - tdivz += tdivz8stepu; - zi += zi8stepu; - z = sw32_d_zitable[(unsigned short) zi]; - - // prevent round-off error on <0 steps from from causing - // overstepping & running off the edge of the texture - snext = (int) (sdivz * z) + sw32_sadjust; - snext = bound(8, snext, sw32_bbextents); - tnext = (int) (tdivz * z) + sw32_tadjust; - tnext = bound(8, tnext, sw32_bbextentt); - - sstep = (snext - s) >> 3; - tstep = (tnext - t) >> 3; - - pdest[0] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[1] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[2] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[3] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[4] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[5] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[6] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[7] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s = snext;t = tnext; - pdest += 8; - } - if (count) - { - // calculate s/z, t/z, zi->fixed s and t at last pixel in span - // (so can't step off polygon), clamp, calculate s and t steps - // across span by division, biasing steps low so we don't run - // off the texture - //countminus1 = (float) (count - 1); - sdivz += sw32_d_sdivzstepu * count; //minus1; - tdivz += sw32_d_tdivzstepu * count; //minus1; - zi += d_zistepu * 65536.0f * count; //minus1; - z = sw32_d_zitable[(unsigned short) zi]; - - // prevent round-off error on <0 steps from from causing - // overstepping & running off the edge of the texture - snext = (int) (sdivz * z) + sw32_sadjust; - snext = bound(count, snext, sw32_bbextents); - tnext = (int) (tdivz * z) + sw32_tadjust; - tnext = bound(count, tnext, sw32_bbextentt); - - if (count > 1) { - sstep = (snext - s) / count; //(count - 1); - tstep = (tnext - t) / count; //(count - 1); - - if (count & 4) - { - pdest[0] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[1] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[2] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[3] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep;t += tstep; - pdest += 4; - } - if (count & 2) - { - pdest[0] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[1] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest += 2; - } - if (count & 1) - pdest[0] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - } - else - { - pdest[0] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - } - } - } while ((pspan = pspan->pnext) != NULL); - } - break; - case 4: - { - int *pbase = (int *) sw32_cacheblock, *pdest; - int count; - fixed16_t s, t, snext, tnext, sstep, tstep; - float sdivz, tdivz, zi, z, du, dv; - float sdivz8stepu, tdivz8stepu, zi8stepu; - - sstep = 0; // keep compiler happy - tstep = 0; // ditto - - sdivz8stepu = sw32_d_sdivzstepu * 8; - tdivz8stepu = sw32_d_tdivzstepu * 8; - zi8stepu = d_zistepu * 8 * 65536; - - do { - pdest = (int *) sw32_d_viewbuffer + (sw32_screenwidth * pspan->v) + pspan->u; - - count = pspan->count; - - // calculate the initial s/z, t/z, 1/z, s, and t and clamp - du = (float) pspan->u; - dv = (float) pspan->v; - - sdivz = sw32_d_sdivzorigin + dv * sw32_d_sdivzstepv + du * sw32_d_sdivzstepu; - tdivz = sw32_d_tdivzorigin + dv * sw32_d_tdivzstepv + du * sw32_d_tdivzstepu; - zi = (d_ziorigin + dv * d_zistepv + du * d_zistepu) * 65536.0f; - z = sw32_d_zitable[(unsigned short) zi]; - - s = (int) (sdivz * z) + sw32_sadjust; - s = bound(0, s, sw32_bbextents); - t = (int) (tdivz * z) + sw32_tadjust; - t = bound(0, t, sw32_bbextentt); - - while(count >= 8) { - count -= 8; - // calculate s/z, t/z, zi->fixed s and t at far end of span, - // calculate s and t steps across span by shifting - sdivz += sdivz8stepu; - tdivz += tdivz8stepu; - zi += zi8stepu; - z = sw32_d_zitable[(unsigned short) zi]; - - // prevent round-off error on <0 steps from from causing - // overstepping & running off the edge of the texture - snext = (int) (sdivz * z) + sw32_sadjust; - snext = bound(8, snext, sw32_bbextents); - tnext = (int) (tdivz * z) + sw32_tadjust; - tnext = bound(8, tnext, sw32_bbextentt); - - sstep = (snext - s) >> 3; - tstep = (tnext - t) >> 3; - - pdest[0] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[1] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[2] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[3] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[4] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[5] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[6] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[7] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s = snext; - t = tnext; - pdest += 8; - } - if (count) - { - // calculate s/z, t/z, zi->fixed s and t at last pixel in span - // (so can't step off polygon), clamp, calculate s and t steps - // across span by division, biasing steps low so we don't run - // off the texture - //countminus1 = (float) (count - 1); - sdivz += sw32_d_sdivzstepu * count; //minus1; - tdivz += sw32_d_tdivzstepu * count; //minus1; - zi += d_zistepu * 65536.0f * count; //minus1; - z = sw32_d_zitable[(unsigned short) zi]; - - // prevent round-off error on <0 steps from from causing - // overstepping & running off the edge of the texture - snext = (int) (sdivz * z) + sw32_sadjust; - snext = bound(count, snext, sw32_bbextents); - tnext = (int) (tdivz * z) + sw32_tadjust; - tnext = bound(count, tnext, sw32_bbextentt); - - if (count > 1) { - sstep = (snext - s) / count; //(count - 1); - tstep = (tnext - t) / count; //(count - 1); - - if (count & 4) - { - pdest[0] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[1] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[2] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[3] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest += 4; - } - if (count & 2) - { - pdest[0] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest[1] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - pdest += 2; - } - if (count & 1) - pdest[0] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - } - else - { - pdest[0] = pbase[(t >> 16) * sw32_cachewidth + (s >> 16)]; - s += sstep; - t += tstep; - } - } - } while ((pspan = pspan->pnext) != NULL); - } - break; - default: - Sys_Error("D_DrawSpans: unsupported r_pixbytes %i", sw32_r_pixbytes); - } -} - -void -sw32_D_DrawZSpans (espan_t *pspan) -{ - int count, doublecount, izistep; - int izi; - short *pdest; - unsigned int ltemp; - double zi; - float du, dv; - - // FIXME: check for clamping/range problems - // we count on FP exceptions being turned off to avoid range problems - izistep = (int) (d_zistepu * 0x8000 * 0x10000); - - do { - pdest = sw32_d_pzbuffer + (sw32_d_zwidth * pspan->v) + pspan->u; - - count = pspan->count; - - // calculate the initial 1/z - du = (float) pspan->u; - dv = (float) pspan->v; - - zi = d_ziorigin + dv * d_zistepv + du * d_zistepu; - // we count on FP exceptions being turned off to avoid range problems - izi = (int) (zi * 0x8000 * 0x10000); - - // LordHavoc: added big endian case, the old code is not correct on - // big-endian (results in swapped depth pairs), and is tuned more for - // x86, PowerPC compilers can probably do a good job with raw loop - // unrolling if it is even necessary... - if (bigendien) - { - do - { - *pdest++ = (short) (izi >> 16); - izi += izistep; - } - while(--count); - } - else - { - if ((intptr_t) pdest & 0x02) { - *pdest++ = (short) (izi >> 16); - izi += izistep; - count--; - } - - if ((doublecount = count >> 1) > 0) { - do { - ltemp = izi >> 16; - izi += izistep; - ltemp |= izi & 0xFFFF0000; - izi += izistep; - *(int *) pdest = ltemp; - pdest += 2; - } while (--doublecount > 0); - } - - if (count & 1) - *pdest = (short) (izi >> 16); - } - } while ((pspan = pspan->pnext) != NULL); -} diff --git a/libs/video/renderer/sw32/d_sky.c b/libs/video/renderer/sw32/d_sky.c deleted file mode 100644 index 20e2765b0..000000000 --- a/libs/video/renderer/sw32/d_sky.c +++ /dev/null @@ -1,277 +0,0 @@ -/* - d_sky.c - - (description) - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include "QF/render.h" -#include "QF/sys.h" - -#include "d_local.h" -#include "r_internal.h" - -#define SKY_SPAN_SHIFT 5 -#define SKY_SPAN_MAX (1 << SKY_SPAN_SHIFT) - - -static void -D_Sky_uv_To_st (int u, int v, fixed16_t *s, fixed16_t *t) -{ - float wu, wv, temp; - vec3_t end; - - if (r_refdef.vrect.width >= r_refdef.vrect.height) - temp = (float) r_refdef.vrect.width; - else - temp = (float) r_refdef.vrect.height; - - wu = 8192.0 * (float) (u - (vid.width >> 1)) / temp; - wv = 8192.0 * (float) ((vid.height >> 1) - v) / temp; - - end[0] = 4096 * vpn[0] + wu * vright[0] + wv * vup[0]; - end[1] = 4096 * vpn[1] + wu * vright[1] + wv * vup[1]; - end[2] = 4096 * vpn[2] + wu * vright[2] + wv * vup[2]; - end[2] *= 3; - VectorNormalize (end); - - temp = sw32_r_skytime * sw32_r_skyspeed; // TODO: add D_SetupFrame & set this there - *s = (int) ((temp + 6 * (SKYSIZE / 2 - 1) * end[0]) * 0x10000); - *t = (int) ((temp + 6 * (SKYSIZE / 2 - 1) * end[1]) * 0x10000); -} - -void -sw32_D_DrawSkyScans (espan_t *pspan) -{ - switch(sw32_r_pixbytes) { - case 1: - { - int count, spancount, u, v; - byte *pdest; - fixed16_t s, t, snext, tnext, sstep, tstep; - int spancountminus1; - - sstep = 0; // keep compiler happy - tstep = 0; // ditto - - do { - pdest = (byte *) sw32_d_viewbuffer + sw32_screenwidth * pspan->v + pspan->u; - - count = pspan->count; - - // calculate the initial s & t - u = pspan->u; - v = pspan->v; - D_Sky_uv_To_st (u, v, &s, &t); - - do { - if (count >= SKY_SPAN_MAX) - spancount = SKY_SPAN_MAX; - else - spancount = count; - - count -= spancount; - - if (count) { - u += spancount; - - // calculate s and t at far end of span, - // calculate s and t steps across span by shifting - D_Sky_uv_To_st (u, v, &snext, &tnext); - - sstep = (snext - s) >> SKY_SPAN_SHIFT; - tstep = (tnext - t) >> SKY_SPAN_SHIFT; - } else { - // calculate s and t at last pixel in span, - // calculate s and t steps across span by division - spancountminus1 = (float) (spancount - 1); - - if (spancountminus1 > 0) { - u += spancountminus1; - D_Sky_uv_To_st (u, v, &snext, &tnext); - - sstep = (snext - s) / spancountminus1; - tstep = (tnext - t) / spancountminus1; - } - } - - do { - *pdest++ = ((byte *) sw32_r_skysource) - [((t & R_SKY_TMASK) >> 8) + ((s & R_SKY_SMASK) >> 16)]; - s += sstep; - t += tstep; - } while (--spancount > 0); - - s = snext; - t = tnext; - - } while (count > 0); - - } while ((pspan = pspan->pnext) != NULL); - } - break; - - case 2: - { - int count, spancount, u, v; - short *pdest; - fixed16_t s, t, snext, tnext, sstep, tstep; - int spancountminus1; - - sstep = 0; // keep compiler happy - tstep = 0; // ditto - - do { - pdest = (short *) sw32_d_viewbuffer + sw32_screenwidth * pspan->v + pspan->u; - - count = pspan->count; - - // calculate the initial s & t - u = pspan->u; - v = pspan->v; - D_Sky_uv_To_st (u, v, &s, &t); - - do { - if (count >= SKY_SPAN_MAX) - spancount = SKY_SPAN_MAX; - else - spancount = count; - - count -= spancount; - - if (count) { - u += spancount; - - // calculate s and t at far end of span, - // calculate s and t steps across span by shifting - D_Sky_uv_To_st (u, v, &snext, &tnext); - - sstep = (snext - s) >> SKY_SPAN_SHIFT; - tstep = (tnext - t) >> SKY_SPAN_SHIFT; - } else { - // calculate s and t at last pixel in span, - // calculate s and t steps across span by division - spancountminus1 = (float) (spancount - 1); - - if (spancountminus1 > 0) { - u += spancountminus1; - D_Sky_uv_To_st (u, v, &snext, &tnext); - - sstep = (snext - s) / spancountminus1; - tstep = (tnext - t) / spancountminus1; - } - } - - do { - *pdest++ = ((short *) sw32_r_skysource) - [((t & R_SKY_TMASK) >> 8) + ((s & R_SKY_SMASK) >> 16)]; - s += sstep; - t += tstep; - } while (--spancount > 0); - - s = snext; - t = tnext; - - } while (count > 0); - - } while ((pspan = pspan->pnext) != NULL); - } - break; - - case 4: - { - int count, spancount, u, v; - int *pdest; - fixed16_t s, t, snext, tnext, sstep, tstep; - int spancountminus1; - - sstep = 0; // keep compiler happy - tstep = 0; // ditto - - do { - pdest = (int *) sw32_d_viewbuffer + sw32_screenwidth * pspan->v + pspan->u; - - count = pspan->count; - - // calculate the initial s & t - u = pspan->u; - v = pspan->v; - D_Sky_uv_To_st (u, v, &s, &t); - - do { - if (count >= SKY_SPAN_MAX) - spancount = SKY_SPAN_MAX; - else - spancount = count; - - count -= spancount; - - if (count) { - u += spancount; - - // calculate s and t at far end of span, - // calculate s and t steps across span by shifting - D_Sky_uv_To_st (u, v, &snext, &tnext); - - sstep = (snext - s) >> SKY_SPAN_SHIFT; - tstep = (tnext - t) >> SKY_SPAN_SHIFT; - } else { - // calculate s and t at last pixel in span, - // calculate s and t steps across span by division - spancountminus1 = (float) (spancount - 1); - - if (spancountminus1 > 0) { - u += spancountminus1; - D_Sky_uv_To_st (u, v, &snext, &tnext); - - sstep = (snext - s) / spancountminus1; - tstep = (tnext - t) / spancountminus1; - } - } - - do { - *pdest++ = ((int *) sw32_r_skysource) - [((t & R_SKY_TMASK) >> 8) + ((s & R_SKY_SMASK) >> 16)]; - s += sstep; - t += tstep; - } while (--spancount > 0); - - s = snext; - t = tnext; - - } while (count > 0); - - } while ((pspan = pspan->pnext) != NULL); - } - break; - - default: - Sys_Error("D_DrawSkyScans: unsupported r_pixbytes %i", sw32_r_pixbytes); - } -} diff --git a/libs/video/renderer/sw32/d_sprite.c b/libs/video/renderer/sw32/d_sprite.c deleted file mode 100644 index fd5fe73f7..000000000 --- a/libs/video/renderer/sw32/d_sprite.c +++ /dev/null @@ -1,731 +0,0 @@ -/* - d_sprite.c - - software top-level rasterization driver module for drawing sprites - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include "QF/render.h" -#include "QF/sys.h" - -#include "compat.h" -#include "d_local.h" -#include "r_internal.h" -#include "vid_internal.h" - -static int sprite_height; -static int minindex, maxindex; -static sspan_t *sprite_spans; - - - -void -sw32_D_SpriteDrawSpans (sspan_t *pspan) -{ - switch(sw32_r_pixbytes) { - case 1: - { - int count, spancount, izistep; - int izi; - byte *pbase; - byte *pdest; - fixed16_t s, t, snext, tnext, sstep, tstep; - float sdivz, tdivz, zi, z, du, dv, spancountminus1; - float sdivz8stepu, tdivz8stepu, zi8stepu; - byte btemp; - short *pz; - - sstep = 0; // keep compiler happy - tstep = 0; // ditto - - pbase = (byte *) sw32_cacheblock; - - sdivz8stepu = sw32_d_sdivzstepu * 8; - tdivz8stepu = sw32_d_tdivzstepu * 8; - zi8stepu = d_zistepu * 8 * 65536.0f; - - // we count on FP exceptions being turned off to avoid range problems - izistep = (int) (d_zistepu * 0x8000 * 0x10000); - - do { - pdest = (byte *) sw32_d_viewbuffer + sw32_screenwidth * pspan->v + pspan->u; - pz = sw32_d_pzbuffer + (sw32_d_zwidth * pspan->v) + pspan->u; - - count = pspan->count; - - if (count <= 0) - goto NextSpan1; - - // calculate the initial s/z, t/z, 1/z, s, and t and clamp - du = (float) pspan->u; - dv = (float) pspan->v; - - sdivz = sw32_d_sdivzorigin + dv * sw32_d_sdivzstepv + du * sw32_d_sdivzstepu; - tdivz = sw32_d_tdivzorigin + dv * sw32_d_tdivzstepv + du * sw32_d_tdivzstepu; - zi = (d_ziorigin + dv * d_zistepv + du * d_zistepu) * 65536.0f; - z = sw32_d_zitable[(int) zi]; - // we count on FP exceptions being turned off to avoid range - // problems - - izi = (int) (zi * 0x8000); - - s = (int) (sdivz * z) + sw32_sadjust; - if (s > sw32_bbextents) - s = sw32_bbextents; - else if (s < 0) - s = 0; - - t = (int) (tdivz * z) + sw32_tadjust; - if (t > sw32_bbextentt) - t = sw32_bbextentt; - else if (t < 0) - t = 0; - - do { - // calculate s and t at the far end of the span - if (count >= 8) - spancount = 8; - else - spancount = count; - - count -= spancount; - - if (count) { - // calculate s/z, t/z, zi->fixed s and t at far end of - // span, calculate s and t steps across span by shifting - sdivz += sdivz8stepu; - tdivz += tdivz8stepu; - zi += zi8stepu; - z = sw32_d_zitable[(int) zi]; - - snext = (int) (sdivz * z) + sw32_sadjust; - if (snext > sw32_bbextents) - snext = sw32_bbextents; - else if (snext < 8) - snext = 8; // prevent round-off error on <0 - // steps from causing overstepping - // & running off the texture's edge - - tnext = (int) (tdivz * z) + sw32_tadjust; - if (tnext > sw32_bbextentt) - tnext = sw32_bbextentt; - else if (tnext < 8) - tnext = 8; // guard against round-off - // error on <0 steps - - sstep = (snext - s) >> 3; - tstep = (tnext - t) >> 3; - } else { - // calculate s/z, t/z, zi->fixed s and t at last pixel - // in span (so can't step off polygon), clamp, - // calculate s and t steps across span by division, - // biasing steps low so we don't run off the texture - spancountminus1 = (float) (spancount - 1); - sdivz += sw32_d_sdivzstepu * spancountminus1; - tdivz += sw32_d_tdivzstepu * spancountminus1; - zi += d_zistepu * 65536.0f * spancountminus1; - z = sw32_d_zitable[(int) zi]; - snext = (int) (sdivz * z) + sw32_sadjust; - if (snext > sw32_bbextents) - snext = sw32_bbextents; - else if (snext < 8) - snext = 8; // prevent round-off error on <0 - // steps from from causing - // overstepping & running off the - // edge of the texture - - tnext = (int) (tdivz * z) + sw32_tadjust; - if (tnext > sw32_bbextentt) - tnext = sw32_bbextentt; - else if (tnext < 8) - tnext = 8; // guard against round-off error on - // <0 steps - - if (spancount > 1) { - sstep = (snext - s) / (spancount - 1); - tstep = (tnext - t) / (spancount - 1); - } - } - - do { - btemp = pbase[(s >> 16) + (t >> 16) * sw32_cachewidth]; - if (btemp != TRANSPARENT_COLOR) { - if (*pz <= (izi >> 16)) { - *pz = izi >> 16; - *pdest = btemp; - } - } - - izi += izistep; - pdest++; - pz++; - s += sstep; - t += tstep; - } while (--spancount > 0); - - s = snext; - t = tnext; - - } while (count > 0); - -NextSpan1: - pspan++; - - } while (pspan->count != DS_SPAN_LIST_END); - } - break; - - case 2: - { - int count, spancount, izistep; - int izi; - byte *pbase; - short *pdest; - fixed16_t s, t, snext, tnext, sstep, tstep; - float sdivz, tdivz, zi, z, du, dv, spancountminus1; - float sdivz8stepu, tdivz8stepu, zi8stepu; - byte btemp; - short *pz; - - sstep = 0; // keep compiler happy - tstep = 0; // ditto - - pbase = (byte *) sw32_cacheblock; - - sdivz8stepu = sw32_d_sdivzstepu * 8; - tdivz8stepu = sw32_d_tdivzstepu * 8; - zi8stepu = d_zistepu * 8 * 65536; - - // we count on FP exceptions being turned off to avoid range problems - izistep = (int) (d_zistepu * 0x8000 * 0x10000); - - do { - pdest = (short *) sw32_d_viewbuffer + sw32_screenwidth * pspan->v + pspan->u; - pz = sw32_d_pzbuffer + (sw32_d_zwidth * pspan->v) + pspan->u; - - count = pspan->count; - - if (count <= 0) - goto NextSpan2; - - // calculate the initial s/z, t/z, 1/z, s, and t and clamp - du = (float) pspan->u; - dv = (float) pspan->v; - - sdivz = sw32_d_sdivzorigin + dv * sw32_d_sdivzstepv + du * sw32_d_sdivzstepu; - tdivz = sw32_d_tdivzorigin + dv * sw32_d_tdivzstepv + du * sw32_d_tdivzstepu; - zi = (d_ziorigin + dv * d_zistepv + du * d_zistepu) * 65536.0f; - z = sw32_d_zitable[(int) zi]; - // we count on FP exceptions being turned off to avoid range - // problems - izi = (int) (zi * 0x8000); - - s = (int) (sdivz * z) + sw32_sadjust; - if (s > sw32_bbextents) - s = sw32_bbextents; - else if (s < 0) - s = 0; - - t = (int) (tdivz * z) + sw32_tadjust; - if (t > sw32_bbextentt) - t = sw32_bbextentt; - else if (t < 0) - t = 0; - - do { - // calculate s and t at the far end of the span - if (count >= 8) - spancount = 8; - else - spancount = count; - - count -= spancount; - - if (count) { - // calculate s/z, t/z, zi->fixed s and t at far end of - // span, calculate s and t steps across span by shifting - sdivz += sdivz8stepu; - tdivz += tdivz8stepu; - zi += zi8stepu; - z = sw32_d_zitable[(int) zi]; - - snext = (int) (sdivz * z) + sw32_sadjust; - if (snext > sw32_bbextents) - snext = sw32_bbextents; - else if (snext < 8) - snext = 8; // prevent round-off error on <0 - // steps from causing overstepping - // & running off the texture's edge - - tnext = (int) (tdivz * z) + sw32_tadjust; - if (tnext > sw32_bbextentt) - tnext = sw32_bbextentt; - else if (tnext < 8) - tnext = 8; // guard against round-off error on - // <0 steps - - sstep = (snext - s) >> 3; - tstep = (tnext - t) >> 3; - } else { - // calculate s/z, t/z, zi->fixed s and t at last pixel in - // span (so can't step off polygon), clamp, calculate s - // and t steps across span by division, biasing steps - // low so we don't run off the texture - spancountminus1 = (float) (spancount - 1); - sdivz += sw32_d_sdivzstepu * spancountminus1; - tdivz += sw32_d_tdivzstepu * spancountminus1; - zi += d_zistepu * 65536.0f * spancountminus1; - z = sw32_d_zitable[(int) zi]; - snext = (int) (sdivz * z) + sw32_sadjust; - if (snext > sw32_bbextents) - snext = sw32_bbextents; - else if (snext < 8) - snext = 8; // prevent round-off error on <0 - // steps from from causing - // overstepping & running off the - // edge of the texture - - tnext = (int) (tdivz * z) + sw32_tadjust; - if (tnext > sw32_bbextentt) - tnext = sw32_bbextentt; - else if (tnext < 8) - tnext = 8; // guard against round-off error on - // <0 steps - - if (spancount > 1) { - sstep = (snext - s) / (spancount - 1); - tstep = (tnext - t) / (spancount - 1); - } - } - - do { - btemp = pbase[(s >> 16) + (t >> 16) * sw32_cachewidth]; - if (btemp != TRANSPARENT_COLOR) { - if (*pz <= (izi >> 16)) { - *pz = izi >> 16; - *pdest = sw32_8to16table[btemp]; - } - } - - izi += izistep; - pdest++; - pz++; - s += sstep; - t += tstep; - } while (--spancount > 0); - - s = snext; - t = tnext; - - } while (count > 0); - -NextSpan2: - pspan++; - - } while (pspan->count != DS_SPAN_LIST_END); - } - break; - - case 4: - { - int count, spancount, izistep; - int izi; - byte *pbase; - int *pdest; - fixed16_t s, t, snext, tnext, sstep, tstep; - float sdivz, tdivz, zi, z, du, dv, spancountminus1; - float sdivz8stepu, tdivz8stepu, zi8stepu; - byte btemp; - short *pz; - - sstep = 0; // keep compiler happy - tstep = 0; // ditto - - pbase = (byte *) sw32_cacheblock; - - sdivz8stepu = sw32_d_sdivzstepu * 8; - tdivz8stepu = sw32_d_tdivzstepu * 8; - zi8stepu = d_zistepu * 8 * 65536; - - // we count on FP exceptions being turned off to avoid range problems - izistep = (int) (d_zistepu * 0x8000 * 0x10000); - - do { - pdest = (int *) sw32_d_viewbuffer + sw32_screenwidth * pspan->v + pspan->u; - pz = sw32_d_pzbuffer + (sw32_d_zwidth * pspan->v) + pspan->u; - - count = pspan->count; - - if (count <= 0) - goto NextSpan4; - - // calculate the initial s/z, t/z, 1/z, s, and t and clamp - du = (float) pspan->u; - dv = (float) pspan->v; - - sdivz = sw32_d_sdivzorigin + dv * sw32_d_sdivzstepv + du * sw32_d_sdivzstepu; - tdivz = sw32_d_tdivzorigin + dv * sw32_d_tdivzstepv + du * sw32_d_tdivzstepu; - zi = (d_ziorigin + dv * d_zistepv + du * d_zistepu) * 65536.0f; - z = sw32_d_zitable[(int) zi]; - // we count on FP exceptions being turned off to avoid range - // problems - izi = (int) (zi * 0x8000); - - s = (int) (sdivz * z) + sw32_sadjust; - if (s > sw32_bbextents) - s = sw32_bbextents; - else if (s < 0) - s = 0; - - t = (int) (tdivz * z) + sw32_tadjust; - if (t > sw32_bbextentt) - t = sw32_bbextentt; - else if (t < 0) - t = 0; - - do { - // calculate s and t at the far end of the span - if (count >= 8) - spancount = 8; - else - spancount = count; - - count -= spancount; - - if (count) { - // calculate s/z, t/z, zi->fixed s and t at far end of - // span, calculate s and t steps across span by shifting - sdivz += sdivz8stepu; - tdivz += tdivz8stepu; - zi += zi8stepu; - z = sw32_d_zitable[(int) zi]; - - snext = (int) (sdivz * z) + sw32_sadjust; - if (snext > sw32_bbextents) - snext = sw32_bbextents; - else if (snext < 8) - snext = 8; // prevent round-off error on <0 - // steps from causing overstepping - // & running off the texture's edge - - tnext = (int) (tdivz * z) + sw32_tadjust; - if (tnext > sw32_bbextentt) - tnext = sw32_bbextentt; - else if (tnext < 8) - tnext = 8; // guard against round-off error on - // <0 steps - - sstep = (snext - s) >> 3; - tstep = (tnext - t) >> 3; - } else { - // calculate s/z, t/z, zi->fixed s and t at last pixel in - // span (so can't step off polygon), clamp, calculate s - // and t steps across span by division, biasing steps low - // so we don't run off the texture - spancountminus1 = (float) (spancount - 1); - sdivz += sw32_d_sdivzstepu * spancountminus1; - tdivz += sw32_d_tdivzstepu * spancountminus1; - zi += d_zistepu * 65536.0f * spancountminus1; - z = sw32_d_zitable[(int) zi]; - snext = (int) (sdivz * z) + sw32_sadjust; - if (snext > sw32_bbextents) - snext = sw32_bbextents; - else if (snext < 8) - snext = 8; // prevent round-off error on <0 - // steps fromcausing overstepping - // & running off the texture's edge - - tnext = (int) (tdivz * z) + sw32_tadjust; - if (tnext > sw32_bbextentt) - tnext = sw32_bbextentt; - else if (tnext < 8) - tnext = 8; // guard against round-off error on - // <0 steps - - if (spancount > 1) { - sstep = (snext - s) / (spancount - 1); - tstep = (tnext - t) / (spancount - 1); - } - } - - do { - btemp = pbase[(s >> 16) + (t >> 16) * sw32_cachewidth]; - if (btemp != TRANSPARENT_COLOR) { - if (*pz <= (izi >> 16)) { - *pz = izi >> 16; - *pdest = d_8to24table[btemp]; - } - } - - izi += izistep; - pdest++; - pz++; - s += sstep; - t += tstep; - } while (--spancount > 0); - - s = snext; - t = tnext; - - } while (count > 0); - -NextSpan4: - pspan++; - - } while (pspan->count != DS_SPAN_LIST_END); - } - break; - - default: - Sys_Error("D_SpriteDrawSpans: unsupported r_pixbytes %i", - sw32_r_pixbytes); - } -} - -static void -D_SpriteScanLeftEdge (void) -{ - int i, v, itop, ibottom, lmaxindex; - emitpoint_t *pvert, *pnext; - sspan_t *pspan; - float du, dv, vtop, vbottom, slope; - fixed16_t u, u_step; - - pspan = sprite_spans; - i = minindex; - if (i == 0) - i = sw32_r_spritedesc.nump; - - lmaxindex = maxindex; - if (lmaxindex == 0) - lmaxindex = sw32_r_spritedesc.nump; - - vtop = ceil (sw32_r_spritedesc.pverts[i].v); - - do { - pvert = &sw32_r_spritedesc.pverts[i]; - pnext = pvert - 1; - - vbottom = ceil (pnext->v); - - if (vtop < vbottom) { - du = pnext->u - pvert->u; - dv = pnext->v - pvert->v; - slope = du / dv; - u_step = (int) (slope * 0x10000); - // adjust u to ceil the integer portion - u = (int) ((pvert->u + (slope * (vtop - pvert->v))) * 0x10000) + - (0x10000 - 1); - itop = (int) vtop; - ibottom = (int) vbottom; - - for (v = itop; v < ibottom; v++) { - pspan->u = u >> 16; - pspan->v = v; - u += u_step; - pspan++; - } - } - - vtop = vbottom; - - i--; - if (i == 0) - i = sw32_r_spritedesc.nump; - - } while (i != lmaxindex); -} - -static void -D_SpriteScanRightEdge (void) -{ - int i, v, itop, ibottom; - emitpoint_t *pvert, *pnext; - sspan_t *pspan; - float du, dv, vtop, vbottom, slope, uvert, unext, vvert, vnext; - fixed16_t u, u_step; - - pspan = sprite_spans; - i = minindex; - - vvert = sw32_r_spritedesc.pverts[i].v; - if (vvert < r_refdef.fvrecty_adj) - vvert = r_refdef.fvrecty_adj; - if (vvert > r_refdef.fvrectbottom_adj) - vvert = r_refdef.fvrectbottom_adj; - - vtop = ceil (vvert); - - do { - pvert = &sw32_r_spritedesc.pverts[i]; - pnext = pvert + 1; - - vnext = pnext->v; - if (vnext < r_refdef.fvrecty_adj) - vnext = r_refdef.fvrecty_adj; - if (vnext > r_refdef.fvrectbottom_adj) - vnext = r_refdef.fvrectbottom_adj; - - vbottom = ceil (vnext); - - if (vtop < vbottom) { - uvert = pvert->u; - if (uvert < r_refdef.fvrectx_adj) - uvert = r_refdef.fvrectx_adj; - if (uvert > r_refdef.fvrectright_adj) - uvert = r_refdef.fvrectright_adj; - - unext = pnext->u; - if (unext < r_refdef.fvrectx_adj) - unext = r_refdef.fvrectx_adj; - if (unext > r_refdef.fvrectright_adj) - unext = r_refdef.fvrectright_adj; - - du = unext - uvert; - dv = vnext - vvert; - slope = du / dv; - u_step = (int) (slope * 0x10000); - // adjust u to ceil the integer portion - u = (int) ((uvert + (slope * (vtop - vvert))) * 0x10000) + - (0x10000 - 1); - itop = (int) vtop; - ibottom = (int) vbottom; - - for (v = itop; v < ibottom; v++) { - pspan->count = (u >> 16) - pspan->u; - u += u_step; - pspan++; - } - } - - vtop = vbottom; - vvert = vnext; - - i++; - if (i == sw32_r_spritedesc.nump) - i = 0; - - } while (i != maxindex); - - pspan->count = DS_SPAN_LIST_END; // mark the end of the span list -} - -static void -D_SpriteCalculateGradients (void) -{ - vec3_t p_normal, p_saxis, p_taxis, p_temp1; - float distinv; - - sw32_TransformVector (sw32_r_spritedesc.vpn, p_normal); - sw32_TransformVector (sw32_r_spritedesc.vright, p_saxis); - sw32_TransformVector (sw32_r_spritedesc.vup, p_taxis); - VectorNegate (p_taxis, p_taxis); - - distinv = 1.0 / (-DotProduct (modelorg, sw32_r_spritedesc.vpn)); - distinv = min (distinv, 1.0); - - sw32_d_sdivzstepu = p_saxis[0] * sw32_xscaleinv; - sw32_d_tdivzstepu = p_taxis[0] * sw32_xscaleinv; - - sw32_d_sdivzstepv = -p_saxis[1] * sw32_yscaleinv; - sw32_d_tdivzstepv = -p_taxis[1] * sw32_yscaleinv; - - d_zistepu = p_normal[0] * sw32_xscaleinv * distinv; - d_zistepv = -p_normal[1] * sw32_yscaleinv * distinv; - - sw32_d_sdivzorigin = p_saxis[2] - sw32_xcenter * sw32_d_sdivzstepu - - sw32_ycenter * sw32_d_sdivzstepv; - sw32_d_tdivzorigin = p_taxis[2] - sw32_xcenter * sw32_d_tdivzstepu - - sw32_ycenter * sw32_d_tdivzstepv; - d_ziorigin = p_normal[2] * distinv - sw32_xcenter * d_zistepu - - sw32_ycenter * d_zistepv; - - sw32_TransformVector (modelorg, p_temp1); - - sw32_sadjust = ((fixed16_t) (DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) - - (-(sw32_cachewidth >> 1) << 16); - sw32_tadjust = ((fixed16_t) (DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) - - (-(sprite_height >> 1) << 16); - - // -1 (-epsilon) so we never wander off the edge of the texture - sw32_bbextents = (sw32_cachewidth << 16) - 1; - sw32_bbextentt = (sprite_height << 16) - 1; -} - -void -sw32_D_DrawSprite (void) -{ - int i, nump; - float ymin, ymax; - emitpoint_t *pverts; - sspan_t spans[MAXHEIGHT + 1]; - - sprite_spans = spans; - - // find the top and bottom vertices, and make sure there's at least one - // scan to draw - ymin = 999999.9; - ymax = -999999.9; - pverts = sw32_r_spritedesc.pverts; - - for (i = 0; i < sw32_r_spritedesc.nump; i++) { - if (pverts->v < ymin) { - ymin = pverts->v; - minindex = i; - } - - if (pverts->v > ymax) { - ymax = pverts->v; - maxindex = i; - } - - pverts++; - } - - ymin = ceil (ymin); - ymax = ceil (ymax); - - if (ymin >= ymax) - return; // doesn't cross any scans at all - - sw32_cachewidth = sw32_r_spritedesc.pspriteframe->width; - sprite_height = sw32_r_spritedesc.pspriteframe->height; - sw32_cacheblock = &sw32_r_spritedesc.pspriteframe->pixels[0]; - - // copy the first vertex to the last vertex, so we don't have to deal with - // wrapping - nump = sw32_r_spritedesc.nump; - pverts = sw32_r_spritedesc.pverts; - pverts[nump] = pverts[0]; - - D_SpriteCalculateGradients (); - D_SpriteScanLeftEdge (); - D_SpriteScanRightEdge (); - sw32_D_SpriteDrawSpans (sprite_spans); -} diff --git a/libs/video/renderer/sw32/d_surf.c b/libs/video/renderer/sw32/d_surf.c deleted file mode 100644 index a001bb871..000000000 --- a/libs/video/renderer/sw32/d_surf.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - d_surf.c - - rasterization driver surface heap manager - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include - -#include "QF/qargs.h" -#include "QF/render.h" -#include "QF/sys.h" - -#include "compat.h" -#include "d_local.h" -#include "r_internal.h" - -static float surfscale; - -static int sc_size; -surfcache_t *sw32_sc_rover; -static surfcache_t *sc_base; - -#define GUARDSIZE 4 - - -void * -sw32_D_SurfaceCacheAddress (void) -{ - return sc_base; -} - - -int -sw32_D_SurfaceCacheForRes (int width, int height) -{ - int size, pix; - - if (COM_CheckParm ("-surfcachesize")) { - size = atoi (com_argv[COM_CheckParm ("-surfcachesize") + 1]) * 1024; - return size; - } - - size = SURFCACHE_SIZE_AT_320X200; - - pix = width * height; - if (pix > 64000) - size += (pix - 64000) * 3; - - size *= sw32_r_pixbytes; - - return size; -} - - -static void -D_CheckCacheGuard (void) -{ - byte *s; - int i; - - s = (byte *) sc_base + sc_size; - for (i = 0; i < GUARDSIZE; i++) - if (s[i] != (byte) i) - Sys_Error ("D_CheckCacheGuard: failed"); -} - - -static void -D_ClearCacheGuard (void) -{ - byte *s; - int i; - - s = (byte *) sc_base + sc_size; - for (i = 0; i < GUARDSIZE; i++) - s[i] = (byte) i; -} - - -void -sw32_D_InitCaches (void *buffer, int size) -{ - Sys_MaskPrintf (SYS_DEV, "D_InitCaches: %ik surface cache\n", size/1024); - - sc_size = size - GUARDSIZE; - sc_base = (surfcache_t *) buffer; - sw32_sc_rover = sc_base; - - sc_base->next = NULL; - sc_base->owner = NULL; - sc_base->size = sc_size; - - sw32_d_pzbuffer = vid.zbuffer; - - D_ClearCacheGuard (); -} - - -void -sw32_D_FlushCaches (void) -{ - surfcache_t *c; - - if (!sc_base) - return; - - for (c = sc_base; c; c = c->next) { - if (c->owner) - *c->owner = NULL; - } - - sw32_sc_rover = sc_base; - sc_base->next = NULL; - sc_base->owner = NULL; - sc_base->size = sc_size; -} - - -static surfcache_t * -D_SCAlloc (int width, int size) -{ - surfcache_t *new; - qboolean wrapped_this_time; - - if ((width < 0) || (width > 512)) // FIXME shouldn't really have a max - Sys_Error ("D_SCAlloc: bad cache width %d", width); - - if ((size <= 0) || (size > (0x40000 * sw32_r_pixbytes))) //FIXME ditto - Sys_Error ("D_SCAlloc: bad cache size %d", size); - - /* This adds the offset of data[0] in the surfcache_t struct. */ - size += field_offset (surfcache_t, data); - -#define SIZE_ALIGN (sizeof(surfcache_t*)-1) - size = (size + SIZE_ALIGN) & ~SIZE_ALIGN; -#undef SIZE_ALIGN - size = (size + 3) & ~3; - if (size > sc_size) - Sys_Error ("D_SCAlloc: %i > cache size", size); - - // if there is not size bytes after the rover, reset to the start - wrapped_this_time = false; - - if (!sw32_sc_rover || (byte *) sw32_sc_rover - (byte *) sc_base > sc_size - size) { - if (sw32_sc_rover) { - wrapped_this_time = true; - } - sw32_sc_rover = sc_base; - } - // colect and free surfcache_t blocks until the rover block is large enough - new = sw32_sc_rover; - if (sw32_sc_rover->owner) - *sw32_sc_rover->owner = NULL; - - while (new->size < size) { - // free another - sw32_sc_rover = sw32_sc_rover->next; - if (!sw32_sc_rover) - Sys_Error ("D_SCAlloc: hit the end of memory"); - if (sw32_sc_rover->owner) - *sw32_sc_rover->owner = NULL; - - new->size += sw32_sc_rover->size; - new->next = sw32_sc_rover->next; - } - - // create a fragment out of any leftovers - if (new->size - size > 256) { - sw32_sc_rover = (surfcache_t *) ((byte *) new + size); - sw32_sc_rover->size = new->size - size; - sw32_sc_rover->next = new->next; - sw32_sc_rover->width = 0; - sw32_sc_rover->owner = NULL; - new->next = sw32_sc_rover; - new->size = size; - } else - sw32_sc_rover = new->next; - - new->width = width; -// DEBUG - if (width > 0) - new->height = (size - sizeof (*new) + sizeof (new->data)) / - (width * sw32_r_pixbytes); - - new->owner = NULL; // should be set properly after return - - if (sw32_d_roverwrapped) { - if (wrapped_this_time || (sw32_sc_rover >= sw32_d_initial_rover)) - r_cache_thrash = true; - } else if (wrapped_this_time) { - sw32_d_roverwrapped = true; - } - - D_CheckCacheGuard (); // DEBUG - return new; -} - - -surfcache_t * -sw32_D_CacheSurface (msurface_t *surface, int miplevel) -{ - surfcache_t *cache; - - // if the surface is animating or flashing, flush the cache - sw32_r_drawsurf.texture = R_TextureAnimation (surface); - sw32_r_drawsurf.lightadj[0] = d_lightstylevalue[surface->styles[0]]; - sw32_r_drawsurf.lightadj[1] = d_lightstylevalue[surface->styles[1]]; - sw32_r_drawsurf.lightadj[2] = d_lightstylevalue[surface->styles[2]]; - sw32_r_drawsurf.lightadj[3] = d_lightstylevalue[surface->styles[3]]; - - // see if the cache holds apropriate data - cache = surface->cachespots[miplevel]; - - if (cache && !cache->dlight && surface->dlightframe != r_framecount - && cache->texture == sw32_r_drawsurf.texture - && cache->lightadj[0] == sw32_r_drawsurf.lightadj[0] - && cache->lightadj[1] == sw32_r_drawsurf.lightadj[1] - && cache->lightadj[2] == sw32_r_drawsurf.lightadj[2] - && cache->lightadj[3] == sw32_r_drawsurf.lightadj[3]) - return cache; - - // determine shape of surface - surfscale = 1.0 / (1 << miplevel); - sw32_r_drawsurf.surfmip = miplevel; - sw32_r_drawsurf.surfwidth = surface->extents[0] >> miplevel; - sw32_r_drawsurf.rowbytes = sw32_r_drawsurf.surfwidth * sw32_r_pixbytes; - sw32_r_drawsurf.surfheight = surface->extents[1] >> miplevel; - - // allocate memory if needed - if (!cache) { - // if a texture just animated, don't reallocate it - cache = D_SCAlloc (sw32_r_drawsurf.surfwidth, - sw32_r_drawsurf.rowbytes * sw32_r_drawsurf.surfheight); - surface->cachespots[miplevel] = cache; - cache->owner = &surface->cachespots[miplevel]; - cache->mipscale = surfscale; - } - - if (surface->dlightframe == r_framecount) - cache->dlight = 1; - else - cache->dlight = 0; - - sw32_r_drawsurf.surfdat = (byte *) cache->data; - - cache->texture = sw32_r_drawsurf.texture; - cache->lightadj[0] = sw32_r_drawsurf.lightadj[0]; - cache->lightadj[1] = sw32_r_drawsurf.lightadj[1]; - cache->lightadj[2] = sw32_r_drawsurf.lightadj[2]; - cache->lightadj[3] = sw32_r_drawsurf.lightadj[3]; - - // draw and light the surface texture - sw32_r_drawsurf.surf = surface; - - sw32_c_surf++; - sw32_R_DrawSurface (); - - return surface->cachespots[miplevel]; -} diff --git a/libs/video/renderer/sw32/d_vars.c b/libs/video/renderer/sw32/d_vars.c deleted file mode 100644 index f5e2752e1..000000000 --- a/libs/video/renderer/sw32/d_vars.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - d_vars.c - - global refresh variables - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include "QF/qtypes.h" - -// all global and static refresh variables are collected in a contiguous block -// to avoid cache conflicts. - -// global refresh variables ----------------------------- - -// FIXME: make into one big structure, like cl or sv -// FIXME: do separately for refresh engine and driver - -float sw32_d_sdivzstepu, sw32_d_tdivzstepu, sw32_d_zistepu; -float sw32_d_sdivzstepv, sw32_d_tdivzstepv, sw32_d_zistepv; -float sw32_d_sdivzorigin, sw32_d_tdivzorigin, sw32_d_ziorigin; - -fixed16_t sw32_sadjust, sw32_tadjust, sw32_bbextents, sw32_bbextentt; - -byte *sw32_cacheblock; -int sw32_cachewidth; -byte *sw32_d_viewbuffer; -short *sw32_d_pzbuffer; -int sw32_d_zrowbytes; -int sw32_d_zwidth; diff --git a/libs/video/renderer/sw32/d_zpoint.c b/libs/video/renderer/sw32/d_zpoint.c deleted file mode 100644 index 24c8e4569..000000000 --- a/libs/video/renderer/sw32/d_zpoint.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - d_zpoint.c - - software driver module for drawing z-buffered points - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include "QF/sys.h" - -#include "d_local.h" -#include "r_internal.h" -#include "vid_internal.h" - - -void -sw32_D_DrawZPoint (void) -{ - short *pz; - int izi; - - pz = sw32_d_pzbuffer + (sw32_d_zwidth * sw32_r_zpointdesc.v) + sw32_r_zpointdesc.u; - izi = (int) (sw32_r_zpointdesc.zi * 0x8000); - - if (*pz <= izi) { - *pz = izi; - switch(sw32_r_pixbytes) - { - case 1: - ((byte *) sw32_d_viewbuffer) [sw32_d_scantable[sw32_r_zpointdesc.v] + - sw32_r_zpointdesc.u] = sw32_r_zpointdesc.color; - break; - case 2: - ((short *) sw32_d_viewbuffer) [sw32_d_scantable[sw32_r_zpointdesc.v] + - sw32_r_zpointdesc.u] = - sw32_8to16table[sw32_r_zpointdesc.color]; - break; - case 4: - ((int *) sw32_d_viewbuffer) [sw32_d_scantable[sw32_r_zpointdesc.v] + - sw32_r_zpointdesc.u] = - d_8to24table[sw32_r_zpointdesc.color]; - break; - default: - Sys_Error("D_DrawZPoint: unsupported r_pixbytes %i", sw32_r_pixbytes); - } - } -} diff --git a/libs/video/renderer/sw32/draw.c b/libs/video/renderer/sw32/draw.c deleted file mode 100644 index a420c8ac0..000000000 --- a/libs/video/renderer/sw32/draw.c +++ /dev/null @@ -1,1367 +0,0 @@ -/* - draw.c - - this is the only file outside the refresh that touches the vid buffer - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif - -#include - -#include "QF/cvar.h" -#include "QF/draw.h" -#include "QF/quakefs.h" -#include "QF/sound.h" -#include "QF/sys.h" - -#include "d_iface.h" -#include "r_internal.h" -#include "vid_internal.h" - -typedef struct { - vrect_t rect; - int width; - int height; - byte *ptexbytes; - int rowbytes; -} rectdesc_t; - -static rectdesc_t r_rectdesc; - -static qpic_t *draw_disc; -static qpic_t *draw_backtile; - - -/* Support Routines */ - -typedef struct cachepic_s { - char name[MAX_QPATH]; - cache_user_t cache; -} cachepic_t; - -#define MAX_CACHED_PICS 128 -static cachepic_t cachepics[MAX_CACHED_PICS]; -static int numcachepics; - -#define CLIP(x,y,w,h,mw,mh) \ - do { \ - if (y < 0) { \ - h += y; \ - y = 0; \ - } \ - if (y + h > mh) \ - h = mh - y; \ - if (h <= 0) \ - return; \ - if (x < 0) { \ - w += x; \ - x = 0; \ - } \ - if (x + w > mw) \ - w = mw - x; \ - if (w <= 0) \ - return; \ - } while (0) - - -qpic_t * -sw32_Draw_MakePic (int width, int height, const byte *data) -{ - qpic_t *pic; - int size = width * height; - - pic = malloc (field_offset (qpic_t, data[size])); - pic->width = width; - pic->height = height; - memcpy (pic->data, data, size); - return pic; -} - -void -sw32_Draw_DestroyPic (qpic_t *pic) -{ - free (pic); -} - -qpic_t * -sw32_Draw_PicFromWad (const char *name) -{ - return W_GetLumpName (name); -} - - -qpic_t * -sw32_Draw_CachePic (const char *path, qboolean alpha) -{ - cachepic_t *pic; - int i; - qpic_t *dat; - - for (pic = cachepics, i = 0; i < numcachepics; pic++, i++) - if (!strcmp (path, pic->name)) - break; - - if (i == numcachepics) { - for (pic = cachepics, i = 0; i < numcachepics; pic++, i++) - if (!pic->name[0]) - break; - if (i == numcachepics) { - if (numcachepics == MAX_CACHED_PICS) - Sys_Error ("numcachepics == MAX_CACHED_PICS"); - numcachepics++; - } - strcpy (pic->name, path); - } - - dat = Cache_Check (&pic->cache); - - if (dat) - return dat; - - // load the pic from disk - QFS_LoadCacheFile (QFS_FOpenFile (path), &pic->cache); - - dat = (qpic_t *) pic->cache.data; - if (!dat) { - Sys_Error ("Draw_CachePic: failed to load %s", path); - } - - SwapPic (dat); - - return dat; -} - -void -sw32_Draw_UncachePic (const char *path) -{ - cachepic_t *pic; - int i; - - for (pic = cachepics, i = 0; i < numcachepics; pic++, i++) { - if (!strcmp (path, pic->name)) { - Cache_Release (&pic->cache); - pic->name[0] = 0; - break; - } - } -} - - -void -sw32_Draw_TextBox (int x, int y, int width, int lines, byte alpha) -{ - qpic_t *p; - int cx, cy; - int n; - - // draw left side - cx = x; - cy = y; - p = sw32_Draw_CachePic ("gfx/box_tl.lmp", true); - sw32_Draw_Pic (cx, cy, p); - p = sw32_Draw_CachePic ("gfx/box_ml.lmp", true); - for (n = 0; n < lines; n++) { - cy += 8; - sw32_Draw_Pic (cx, cy, p); - } - p = sw32_Draw_CachePic ("gfx/box_bl.lmp", true); - sw32_Draw_Pic (cx, cy + 8, p); - - // draw middle - cx += 8; - while (width > 0) { - cy = y; - p = sw32_Draw_CachePic ("gfx/box_tm.lmp", true); - sw32_Draw_Pic (cx, cy, p); - p = sw32_Draw_CachePic ("gfx/box_mm.lmp", true); - for (n = 0; n < lines; n++) { - cy += 8; - if (n == 1) - p = sw32_Draw_CachePic ("gfx/box_mm2.lmp", true); - sw32_Draw_Pic (cx, cy, p); - } - p = sw32_Draw_CachePic ("gfx/box_bm.lmp", true); - sw32_Draw_Pic (cx, cy + 8, p); - width -= 2; - cx += 16; - } - - // draw right side - cy = y; - p = sw32_Draw_CachePic ("gfx/box_tr.lmp", true); - sw32_Draw_Pic (cx, cy, p); - p = sw32_Draw_CachePic ("gfx/box_mr.lmp", true); - for (n = 0; n < lines; n++) { - cy += 8; - sw32_Draw_Pic (cx, cy, p); - } - p = sw32_Draw_CachePic ("gfx/box_br.lmp", true); - sw32_Draw_Pic (cx, cy + 8, p); -} - - -void -sw32_Draw_Init (void) -{ - draw_chars = W_GetLumpName ("conchars"); - draw_disc = W_GetLumpName ("disc"); - draw_backtile = W_GetLumpName ("backtile"); - - r_rectdesc.width = draw_backtile->width; - r_rectdesc.height = draw_backtile->height; - r_rectdesc.ptexbytes = draw_backtile->data; - r_rectdesc.rowbytes = draw_backtile->width; -} - - -/* - Draw_Character - - Draws one 8*8 graphics character with 0 being transparent. - It can be clipped to the top of the screen to allow the console to be - smoothly scrolled off. -*/ -void -sw32_Draw_Character (int x, int y, unsigned int chr) -{ - byte *source; - int drawline; - int row, col; - - chr &= 255; - - if (y <= -8) - return; // totally off screen - - if (y > vid.conheight - 8 || x < 0 || x > vid.conwidth - 8) - return; - if (chr > 255) - return; - - row = chr >> 4; - col = chr & 15; - source = draw_chars + (row << 10) + (col << 3); - - if (y < 0) { // clipped - drawline = 8 + y; - source -= 128 * y; - y = 0; - } else - drawline = 8; - - - switch(sw32_r_pixbytes) { - case 1: - { - byte *dest = (byte *) vid.conbuffer + y * vid.conrowbytes + x; - - while (drawline--) { - if (source[0]) - dest[0] = source[0]; - if (source[1]) - dest[1] = source[1]; - if (source[2]) - dest[2] = source[2]; - if (source[3]) - dest[3] = source[3]; - if (source[4]) - dest[4] = source[4]; - if (source[5]) - dest[5] = source[5]; - if (source[6]) - dest[6] = source[6]; - if (source[7]) - dest[7] = source[7]; - source += 128; - dest += vid.conrowbytes; - } - } - break; - case 2: - { - unsigned short *dest = (unsigned short *) vid.conbuffer + y * - (vid.conrowbytes >> 1) + x; - - while (drawline--) { - if (source[0]) - dest[0] = sw32_8to16table[source[0]]; - if (source[1]) - dest[1] = sw32_8to16table[source[1]]; - if (source[2]) - dest[2] = sw32_8to16table[source[2]]; - if (source[3]) - dest[3] = sw32_8to16table[source[3]]; - if (source[4]) - dest[4] = sw32_8to16table[source[4]]; - if (source[5]) - dest[5] = sw32_8to16table[source[5]]; - if (source[6]) - dest[6] = sw32_8to16table[source[6]]; - if (source[7]) - dest[7] = sw32_8to16table[source[7]]; - - source += 128; - dest += (vid.conrowbytes >> 1); - } - } - break; - case 4: - { - unsigned int *dest = (unsigned int *) vid.conbuffer + y * - (vid.conrowbytes >> 2) + x; - - while (drawline--) { - if (source[0]) - dest[0] = d_8to24table[source[0]]; - if (source[1]) - dest[1] = d_8to24table[source[1]]; - if (source[2]) - dest[2] = d_8to24table[source[2]]; - if (source[3]) - dest[3] = d_8to24table[source[3]]; - if (source[4]) - dest[4] = d_8to24table[source[4]]; - if (source[5]) - dest[5] = d_8to24table[source[5]]; - if (source[6]) - dest[6] = d_8to24table[source[6]]; - if (source[7]) - dest[7] = d_8to24table[source[7]]; - - source += 128; - dest += (vid.conrowbytes >> 2); - } - } - break; - default: - Sys_Error("Draw_Character: unsupported r_pixbytes %i", sw32_r_pixbytes); - } -} - -void -sw32_Draw_String (int x, int y, const char *str) -{ - while (*str) { - sw32_Draw_Character (x, y, *str++); - x += 8; - } -} - -void -sw32_Draw_nString (int x, int y, const char *str, int count) -{ - while (count-- && *str) { - sw32_Draw_Character (x, y, *str++); - x += 8; - } -} - - -void -sw32_Draw_AltString (int x, int y, const char *str) -{ - while (*str) { - sw32_Draw_Character (x, y, (*str++) | 0x80); - x += 8; - } -} - - -static void -Draw_Pixel (int x, int y, byte color) -{ - switch(sw32_r_pixbytes) - { - case 1: - ((byte *) vid.conbuffer)[y * vid.conrowbytes + x] = color; - break; - case 2: - ((unsigned short *) vid.conbuffer)[y * (vid.conrowbytes >> 1) + x] = - sw32_8to16table[color]; - break; - case 4: - ((unsigned int *) vid.conbuffer)[y * (vid.conrowbytes >> 2) + x] = - d_8to24table[color]; - break; - default: - Sys_Error("Draw_Pixel: unsupported r_pixbytes %i", sw32_r_pixbytes); - } -} - -static void -crosshair_1 (int x, int y) -{ - sw32_Draw_Character (x - 4, y - 4, '+'); -} - -static void -crosshair_2 (int x, int y) -{ - byte c = crosshaircolor->int_val; - - Draw_Pixel (x - 1, y, c); - Draw_Pixel (x - 3, y, c); - Draw_Pixel (x + 1, y, c); - Draw_Pixel (x + 3, y, c); - Draw_Pixel (x, y - 1, c); - Draw_Pixel (x, y - 3, c); - Draw_Pixel (x, y + 1, c); - Draw_Pixel (x, y + 3, c); -} - -static void -crosshair_3 (int x, int y) -{ - byte c = crosshaircolor->int_val; - - Draw_Pixel (x - 3, y - 3, c); - Draw_Pixel (x + 3, y - 3, c); - Draw_Pixel (x - 2, y - 2, c); - Draw_Pixel (x + 2, y - 2, c); - Draw_Pixel (x - 3, y + 3, c); - Draw_Pixel (x + 2, y + 2, c); - Draw_Pixel (x - 2, y + 2, c); - Draw_Pixel (x + 3, y + 3, c); -} - -static void -crosshair_4 (int x, int y) -{ - //byte c = crosshaircolor->int_val; - - Draw_Pixel (x, y - 2, 8); - Draw_Pixel (x + 1, y - 2, 9); - - Draw_Pixel (x, y - 1, 6); - Draw_Pixel (x + 1, y - 1, 8); - Draw_Pixel (x + 2, y - 1, 2); - - Draw_Pixel (x - 2, y, 6); - Draw_Pixel (x - 1, y, 8); - Draw_Pixel (x, y, 8); - Draw_Pixel (x + 1, y, 6); - Draw_Pixel (x + 2, y, 8); - Draw_Pixel (x + 3, y, 8); - - Draw_Pixel (x - 1, y + 1, 2); - Draw_Pixel (x, y + 1, 8); - Draw_Pixel (x + 1, y + 1, 8); - Draw_Pixel (x + 2, y + 1, 2); - Draw_Pixel (x + 3, y + 1, 2); - Draw_Pixel (x + 4, y + 1, 2); - - Draw_Pixel (x, y + 2, 7); - Draw_Pixel (x + 1, y + 2, 8); - Draw_Pixel (x + 2, y + 2, 2); - - Draw_Pixel (x + 1, y + 3, 2); - Draw_Pixel (x + 2, y + 3, 2); -} - -static void -crosshair_5 (int x, int y) -{ - byte c = crosshaircolor->int_val; - - Draw_Pixel (x - 1, y - 3, c); - Draw_Pixel (x + 0, y - 3, c); - Draw_Pixel (x + 1, y - 3, c); - - Draw_Pixel (x - 2, y - 2, c); - Draw_Pixel (x + 2, y - 2, c); - - Draw_Pixel (x - 3, y - 1, c); - Draw_Pixel (x + 3, y - 1, c); - - Draw_Pixel (x - 3, y, c); - Draw_Pixel (x, y, c); - Draw_Pixel (x + 3, y, c); - - Draw_Pixel (x - 3, y + 1, c); - Draw_Pixel (x + 3, y + 1, c); - - Draw_Pixel (x - 2, y + 2, c); - Draw_Pixel (x + 2, y + 2, c); - - Draw_Pixel (x - 1, y + 3, c); - Draw_Pixel (x + 0, y + 3, c); - Draw_Pixel (x + 1, y + 3, c); -} - -static void (*crosshair_func[]) (int x, int y) = { - crosshair_1, - crosshair_2, - crosshair_3, - crosshair_4, - crosshair_5, -}; - -void -sw32_Draw_Crosshair (void) -{ - int x, y; - int ch; - - ch = crosshair->int_val - 1; - if ((unsigned) ch >= sizeof (crosshair_func) / sizeof (crosshair_func[0])) - return; - - x = vid.conwidth / 2 + cl_crossx->int_val; - y = vid.conheight / 2 + cl_crossy->int_val; - - crosshair_func[ch] (x, y); -} - -void -sw32_Draw_CrosshairAt (int ch, int x, int y) -{ - ch -= 1; - if ((unsigned) ch >= sizeof (crosshair_func) / sizeof (crosshair_func[0])) - return; - - crosshair_func[ch] (x, y); -} - -void -sw32_Draw_Pic (int x, int y, qpic_t *pic) -{ - byte *source, tbyte; - int v, u; - - if (x < 0 || (x + pic->width) > vid.conwidth - || y < 0 || (y + pic->height) > vid.conheight) { - Sys_MaskPrintf (SYS_VID, "Draw_Pic: bad coordinates"); - sw32_Draw_SubPic (x, y, pic, 0, 0, pic->width, pic->height); - return; - } - - source = pic->data; - - switch(sw32_r_pixbytes) { - case 1: - { - byte *dest = (byte *) vid.buffer + y * vid.rowbytes + x; - - if (pic->width & 7) { // general - for (v = 0; v < pic->height; v++) { - for (u = 0; u < pic->width; u++) - if ((tbyte = source[u]) != TRANSPARENT_COLOR) - dest[u] = tbyte; - - dest += vid.rowbytes; - source += pic->width; - } - } else { // unwound - for (v = 0; v < pic->height; v++) { - for (u = 0; u < pic->width; u += 8) { - if ((tbyte = source[u]) != TRANSPARENT_COLOR) - dest[u] = tbyte; - if ((tbyte = source[u + 1]) != TRANSPARENT_COLOR) - dest[u + 1] = tbyte; - if ((tbyte = source[u + 2]) != TRANSPARENT_COLOR) - dest[u + 2] = tbyte; - if ((tbyte = source[u + 3]) != TRANSPARENT_COLOR) - dest[u + 3] = tbyte; - if ((tbyte = source[u + 4]) != TRANSPARENT_COLOR) - dest[u + 4] = tbyte; - if ((tbyte = source[u + 5]) != TRANSPARENT_COLOR) - dest[u + 5] = tbyte; - if ((tbyte = source[u + 6]) != TRANSPARENT_COLOR) - dest[u + 6] = tbyte; - if ((tbyte = source[u + 7]) != TRANSPARENT_COLOR) - dest[u + 7] = tbyte; - } - dest += vid.rowbytes; - source += pic->width; - } - } - } - break; - case 2: - { - unsigned short *dest = (unsigned short *) vid.buffer + y * - (vid.rowbytes >> 1) + x; - - for (v = 0; v < pic->height; v++) { - for (u = 0; u < pic->width; u++) { - tbyte = source[u]; - if (tbyte != TRANSPARENT_COLOR) - dest[u] = sw32_8to16table[tbyte]; - } - - dest += vid.rowbytes >> 1; - source += pic->width; - } - } - break; - case 4: - { - unsigned int *dest = (unsigned int *) vid.buffer + y * - (vid.rowbytes >> 2) + x; - for (v = 0; v < pic->height; v++) { - for (u = 0; u < pic->width; u++) { - tbyte = source[u]; - if (tbyte != TRANSPARENT_COLOR) - dest[u] = d_8to24table[tbyte]; - } - dest += vid.rowbytes >> 2; - source += pic->width; - } - } - break; - default: - Sys_Error("Draw_Pic: unsupported r_pixbytes %i", sw32_r_pixbytes); - } -} - -void -sw32_Draw_Picf (float x, float y, qpic_t *pic) -{ - sw32_Draw_Pic (x, y, pic); -} - -void -sw32_Draw_SubPic (int x, int y, qpic_t *pic, int srcx, int srcy, int width, - int height) -{ - byte *source, tbyte; - int v, u; - - if ((x < 0) || (x + width > vid.conwidth) - || (y < 0) || (y + height > vid.conheight)) { - Sys_MaskPrintf (SYS_VID, "Draw_SubPic: bad coordinates"); - } - // first, clip to screen - if (x < 0) { - srcx += x; - width += x; - x = 0; - } - if (x + width > vid.width) - width = vid.width - x; - if (width <= 0) - return; - if (y < 0) { - srcy += y; - height += y; - y = 0; - } - if (y + height > vid.height) - height = vid.height - y; - if (height <= 0) - return; - // next, clip to pic - CLIP (srcx, srcy, width, height, pic->width, pic->height); - - source = pic->data + srcy * pic->width + srcx; - - switch (sw32_r_pixbytes) { - case 1: - { - byte *dest = (byte *) vid.buffer + y * vid.rowbytes + x; - - for (v = 0; v < height; v++) { - for (u = 0; u < width; u++) - if ((tbyte = source[u]) != TRANSPARENT_COLOR) - dest[u] = tbyte; - dest += vid.rowbytes; - source += pic->width; - } - } - break; - case 2: - { - unsigned short *dest = (unsigned short *) vid.buffer + y * - (vid.rowbytes >> 1) + x; - for (v = 0; v < height; v++, dest += vid.rowbytes >> 1, - source += pic->width) - for (u = 0; u < width; u++) - if ((tbyte = source[u]) != TRANSPARENT_COLOR) - dest[u] = sw32_8to16table[tbyte]; - } - break; - case 4: - { - unsigned int *dest = (unsigned int *) vid.buffer + y * - (vid.rowbytes >> 2) + x; - for (v = 0; v < height; v++, dest += vid.rowbytes >> 2, - source += pic->width) - for (u = 0; u < width; u++) - if ((tbyte = source[u]) != TRANSPARENT_COLOR) - dest[u] = d_8to24table[tbyte]; - } - break; - default: - Sys_Error("Draw_SubPic: unsupported r_pixbytes %i", sw32_r_pixbytes); - } -} - - -void -sw32_Draw_ConsoleBackground (int lines, byte alpha) -{ - int x, y, v; - byte *src; - int f, fstep; - qpic_t *conback; - - conback = sw32_Draw_CachePic ("gfx/conback.lmp", true); - - // draw the pic - switch(sw32_r_pixbytes) { - case 1: - { - byte *dest = vid.conbuffer; - - for (y = 0; y < lines; y++, dest += vid.conrowbytes) { - v = (vid.conheight - lines + y) * 200 / vid.conheight; - src = conback->data + v * 320; - if (vid.conwidth == 320) - memcpy (dest, src, vid.conwidth); - else { - f = 0; - fstep = 320 * 0x10000 / vid.conwidth; - for (x = 0; x < vid.conwidth; x += 4) { - dest[x] = src[f >> 16]; - f += fstep; - dest[x + 1] = src[f >> 16]; - f += fstep; - dest[x + 2] = src[f >> 16]; - f += fstep; - dest[x + 3] = src[f >> 16]; - f += fstep; - } - } - } - } - break; - case 2: - { - unsigned short *dest = (unsigned short *) vid.conbuffer; - - for (y = 0; y < lines; y++, dest += (vid.conrowbytes >> 1)) { - // FIXME: pre-expand to native format? - // FIXME: does the endian switching go away in production? - v = (vid.conheight - lines + y) * 200 / vid.conheight; - src = conback->data + v * 320; - f = 0; - fstep = 320 * 0x10000 / vid.conwidth; - for (x = 0; x < vid.conwidth; x += 4) { - dest[x] = sw32_8to16table[src[f >> 16]]; - f += fstep; - dest[x + 1] = sw32_8to16table[src[f >> 16]]; - f += fstep; - dest[x + 2] = sw32_8to16table[src[f >> 16]]; - f += fstep; - dest[x + 3] = sw32_8to16table[src[f >> 16]]; - f += fstep; - } - } - } - break; - case 4: - { - unsigned int *dest = (unsigned int *) vid.conbuffer; - for (y = 0; y < lines; y++, dest += (vid.conrowbytes >> 2)) { - v = (vid.conheight - lines + y) * 200 / vid.conheight; - src = conback->data + v * 320; - f = 0; - fstep = 320 * 0x10000 / vid.conwidth; - for (x = 0; x < vid.conwidth; x += 4) { - dest[x ] = d_8to24table[src[f >> 16]];f += fstep; - dest[x + 1] = d_8to24table[src[f >> 16]];f += fstep; - dest[x + 2] = d_8to24table[src[f >> 16]];f += fstep; - dest[x + 3] = d_8to24table[src[f >> 16]];f += fstep; - } - } - } - break; - - default: - Sys_Error("Draw_ConsoleBackground: unsupported r_pixbytes %i", - sw32_r_pixbytes); - } - -// if (!cls.download) - sw32_Draw_AltString (vid.conwidth - strlen (cl_verstring->string) * 8 - 11, - lines - 14, cl_verstring->string); -} - -static void -R_DrawRect (vrect_t *prect, int rowbytes, byte * psrc, int transparent) -{ - switch(sw32_r_pixbytes) { - case 1: - { - byte t; - int i, j, srcdelta, destdelta; - byte *pdest; - - pdest = (byte *) vid.buffer + prect->y * vid.rowbytes + prect->x; - - srcdelta = rowbytes - prect->width; - destdelta = vid.rowbytes - prect->width; - - if (transparent) - { - for (i = 0; i < prect->height; i++) - { - for (j = 0; j < prect->width; j++) - { - t = *psrc; - if (t != TRANSPARENT_COLOR) - *pdest = t; - psrc++; - pdest++; - } - - psrc += srcdelta; - pdest += destdelta; - } - } - else - { - for (i = 0; i < prect->height; i++) - { - memcpy (pdest, psrc, prect->width); - psrc += rowbytes; - pdest += vid.rowbytes; - } - } - } - break; - case 2: - { - int i, j, srcdelta, destdelta; - unsigned short *pdest; - - pdest = (unsigned short *) vid.buffer + - (prect->y * (vid.rowbytes >> 1)) + prect->x; - - srcdelta = rowbytes - prect->width; - destdelta = (vid.rowbytes >> 1) - prect->width; - - if (transparent) { - for (i = 0; i < prect->height; i++) - { - j = prect->width; - while(j >= 8) - { - j -= 8; - if (psrc[0] != TRANSPARENT_COLOR) - pdest[0] = sw32_8to16table[psrc[0]]; - if (psrc[1] != TRANSPARENT_COLOR) - pdest[1] = sw32_8to16table[psrc[1]]; - if (psrc[2] != TRANSPARENT_COLOR) - pdest[2] = sw32_8to16table[psrc[2]]; - if (psrc[3] != TRANSPARENT_COLOR) - pdest[3] = sw32_8to16table[psrc[3]]; - if (psrc[4] != TRANSPARENT_COLOR) - pdest[4] = sw32_8to16table[psrc[4]]; - if (psrc[5] != TRANSPARENT_COLOR) - pdest[5] = sw32_8to16table[psrc[5]]; - if (psrc[6] != TRANSPARENT_COLOR) - pdest[6] = sw32_8to16table[psrc[6]]; - if (psrc[7] != TRANSPARENT_COLOR) - pdest[7] = sw32_8to16table[psrc[7]]; - psrc += 8; - pdest += 8; - } - if (j & 4) - { - if (psrc[0] != TRANSPARENT_COLOR) - pdest[0] = sw32_8to16table[psrc[0]]; - if (psrc[1] != TRANSPARENT_COLOR) - pdest[1] = sw32_8to16table[psrc[1]]; - if (psrc[2] != TRANSPARENT_COLOR) - pdest[2] = sw32_8to16table[psrc[2]]; - if (psrc[3] != TRANSPARENT_COLOR) - pdest[3] = sw32_8to16table[psrc[3]]; - psrc += 4; - pdest += 4; - } - if (j & 2) - { - if (psrc[0] != TRANSPARENT_COLOR) - pdest[0] = sw32_8to16table[psrc[0]]; - if (psrc[1] != TRANSPARENT_COLOR) - pdest[1] = sw32_8to16table[psrc[1]]; - psrc += 2; - pdest += 2; - } - if (j & 1) - { - if (psrc[0] != TRANSPARENT_COLOR) - pdest[0] = sw32_8to16table[psrc[0]]; - psrc++; - pdest++; - } - - psrc += srcdelta; - pdest += destdelta; - } - } else { - for (i = 0; i < prect->height; i++, psrc += srcdelta, - pdest += destdelta) - { - j = prect->width; - while(j >= 8) - { - j -= 8; - pdest[0] = sw32_8to16table[psrc[0]]; - pdest[1] = sw32_8to16table[psrc[1]]; - pdest[2] = sw32_8to16table[psrc[2]]; - pdest[3] = sw32_8to16table[psrc[3]]; - pdest[4] = sw32_8to16table[psrc[4]]; - pdest[5] = sw32_8to16table[psrc[5]]; - pdest[6] = sw32_8to16table[psrc[6]]; - pdest[7] = sw32_8to16table[psrc[7]]; - psrc += 8; - pdest += 8; - } - if (j & 4) - { - pdest[0] = sw32_8to16table[psrc[0]]; - pdest[1] = sw32_8to16table[psrc[1]]; - pdest[2] = sw32_8to16table[psrc[2]]; - pdest[3] = sw32_8to16table[psrc[3]]; - psrc += 4; - pdest += 4; - } - if (j & 2) - { - pdest[0] = sw32_8to16table[psrc[0]]; - pdest[1] = sw32_8to16table[psrc[1]]; - psrc += 2; - pdest += 2; - } - if (j & 1) - { - pdest[0] = sw32_8to16table[psrc[0]]; - psrc++; - pdest++; - } - } - } - } - break; - case 4: - { - int i, j, srcdelta, destdelta; - int *pdest; - - pdest = (int *) vid.buffer + prect->y * (vid.rowbytes >> 2) + prect->x; - - srcdelta = rowbytes - prect->width; - destdelta = (vid.rowbytes >> 2) - prect->width; - - if (transparent) - { - for (i = 0; i < prect->height; i++) - { - j = prect->width; - while(j >= 8) - { - j -= 8; - if (psrc[0] != TRANSPARENT_COLOR) - pdest[0] = d_8to24table[psrc[0]]; - if (psrc[1] != TRANSPARENT_COLOR) - pdest[1] = d_8to24table[psrc[1]]; - if (psrc[2] != TRANSPARENT_COLOR) - pdest[2] = d_8to24table[psrc[2]]; - if (psrc[3] != TRANSPARENT_COLOR) - pdest[3] = d_8to24table[psrc[3]]; - if (psrc[4] != TRANSPARENT_COLOR) - pdest[4] = d_8to24table[psrc[4]]; - if (psrc[5] != TRANSPARENT_COLOR) - pdest[5] = d_8to24table[psrc[5]]; - if (psrc[6] != TRANSPARENT_COLOR) - pdest[6] = d_8to24table[psrc[6]]; - if (psrc[7] != TRANSPARENT_COLOR) - pdest[7] = d_8to24table[psrc[7]]; - psrc += 8; - pdest += 8; - } - if (j & 4) - { - if (psrc[0] != TRANSPARENT_COLOR) - pdest[0] = d_8to24table[psrc[0]]; - if (psrc[1] != TRANSPARENT_COLOR) - pdest[1] = d_8to24table[psrc[1]]; - if (psrc[2] != TRANSPARENT_COLOR) - pdest[2] = d_8to24table[psrc[2]]; - if (psrc[3] != TRANSPARENT_COLOR) - pdest[3] = d_8to24table[psrc[3]]; - psrc += 4; - pdest += 4; - } - if (j & 2) - { - if (psrc[0] != TRANSPARENT_COLOR) - pdest[0] = d_8to24table[psrc[0]]; - if (psrc[1] != TRANSPARENT_COLOR) - pdest[1] = d_8to24table[psrc[1]]; - psrc += 2; - pdest += 2; - } - if (j & 1) - { - if (psrc[0] != TRANSPARENT_COLOR) - pdest[0] = d_8to24table[psrc[0]]; - psrc++; - pdest++; - } - - psrc += srcdelta; - pdest += destdelta; - } - } - else - { - for (i = 0; i < prect->height; i++, psrc += srcdelta, - pdest += destdelta) - { - j = prect->width; - while(j >= 8) - { - j -= 8; - pdest[0] = d_8to24table[psrc[0]]; - pdest[1] = d_8to24table[psrc[1]]; - pdest[2] = d_8to24table[psrc[2]]; - pdest[3] = d_8to24table[psrc[3]]; - pdest[4] = d_8to24table[psrc[4]]; - pdest[5] = d_8to24table[psrc[5]]; - pdest[6] = d_8to24table[psrc[6]]; - pdest[7] = d_8to24table[psrc[7]]; - psrc += 8; - pdest += 8; - } - if (j & 4) - { - pdest[0] = d_8to24table[psrc[0]]; - pdest[1] = d_8to24table[psrc[1]]; - pdest[2] = d_8to24table[psrc[2]]; - pdest[3] = d_8to24table[psrc[3]]; - psrc += 4; - pdest += 4; - } - if (j & 2) - { - pdest[0] = d_8to24table[psrc[0]]; - pdest[1] = d_8to24table[psrc[1]]; - psrc += 2; - pdest += 2; - } - if (j & 1) - { - pdest[0] = d_8to24table[psrc[0]]; - psrc++; - pdest++; - } - } - } - } - break; - default: - Sys_Error("R_DrawRect: unsupported r_pixbytes %i", sw32_r_pixbytes); - } -} - -/* - Draw_TileClear - - This repeats a 64*64 tile graphic to fill the screen around a sized down - refresh window. -*/ -void -sw32_Draw_TileClear (int x, int y, int w, int h) -{ - int width, height, tileoffsetx, tileoffsety; - byte *psrc; - vrect_t vr; - - CLIP (x, y, w, h, vid.width, vid.height); - - r_rectdesc.rect.x = x; - r_rectdesc.rect.y = y; - r_rectdesc.rect.width = w; - r_rectdesc.rect.height = h; - - vr.y = r_rectdesc.rect.y; - height = r_rectdesc.rect.height; - - tileoffsety = vr.y % r_rectdesc.height; - - while (height > 0) { - vr.x = r_rectdesc.rect.x; - width = r_rectdesc.rect.width; - - if (tileoffsety != 0) - vr.height = r_rectdesc.height - tileoffsety; - else - vr.height = r_rectdesc.height; - - if (vr.height > height) - vr.height = height; - - tileoffsetx = vr.x % r_rectdesc.width; - - while (width > 0) { - if (tileoffsetx != 0) - vr.width = r_rectdesc.width - tileoffsetx; - else - vr.width = r_rectdesc.width; - - if (vr.width > width) - vr.width = width; - - psrc = r_rectdesc.ptexbytes + - (tileoffsety * r_rectdesc.rowbytes) + tileoffsetx; - - R_DrawRect (&vr, r_rectdesc.rowbytes, psrc, 0); - - vr.x += vr.width; - width -= vr.width; - tileoffsetx = 0; // only the left tile can be left-clipped - } - - vr.y += vr.height; - height -= vr.height; - tileoffsety = 0; // only the top tile can be top-clipped - } -} - - -/* - Draw_Fill - - Fills a box of pixels with a single color -*/ -void -sw32_Draw_Fill (int x, int y, int w, int h, int c) -{ - int u, v; - - if (x < 0 || x + w > vid.conwidth - || y < 0 || y + h > vid.conheight) { - Sys_MaskPrintf (SYS_VID, "Bad Draw_Fill(%d, %d, %d, %d, %c)\n", - x, y, w, h, c); - } - CLIP (x, y, w, h, vid.width, vid.height); - - switch (sw32_r_pixbytes) { - case 1: - { - byte *dest = (byte *) vid.buffer + y * vid.rowbytes + x; - for (v = 0; v < h; v++, dest += vid.rowbytes) - for (u = 0; u < w; u++) - dest[u] = c; - } - break; - case 2: - { - unsigned short *dest = (unsigned short *) vid.buffer + y * - (vid.rowbytes >> 1) + x; - c = sw32_8to16table[c]; - for (v = 0; v < h; v++, dest += (vid.rowbytes >> 1)) - for (u = 0; u < w; u++) - dest[u] = c; - } - break; - case 4: - { - unsigned int *dest = (unsigned int *) vid.buffer + y * - (vid.rowbytes >> 2) + x; - c = d_8to24table[c]; - for (v = 0; v < h; v++, dest += (vid.rowbytes >> 2)) - for (u = 0; u < w; u++) - dest[u] = c; - } - break; - default: - Sys_Error("Draw_Fill: unsupported r_pixbytes %i", sw32_r_pixbytes); - } -} - - -void -sw32_Draw_FadeScreen (void) -{ - int x, y; - - VID_UnlockBuffer (); - S_ExtraUpdate (); - VID_LockBuffer (); - - switch(sw32_r_pixbytes) { - case 1: - { - for (y = 0; y < vid.conheight; y++) { - unsigned int t; - byte *pbuf = (byte *) ((byte *) vid.buffer + vid.rowbytes * y); - t = (y & 1) << 1; - - for (x = 0; x < vid.conwidth; x++) { - if ((x & 3) != t) - pbuf[x] = 0; - } - } - } - break; - case 2: - { - for (y = 0; y < vid.conheight; y++) { - unsigned short *pbuf = (unsigned short *) - ((byte *) vid.buffer + vid.rowbytes * y); - pbuf = (unsigned short *) vid.buffer + (vid.rowbytes >> 1) * y; - for (x = 0; x < vid.conwidth; x++) - pbuf[x] = (pbuf[x] >> 1) & 0x7BEF; - } - } - break; - case 4: - { - for (y = 0; y < vid.conheight; y++) { - unsigned int *pbuf = (unsigned int *) - ((byte *) vid.buffer + vid.rowbytes * y); - for (x = 0; x < vid.conwidth; x++) - pbuf[x] = (pbuf[x] >> 1) & 0x7F7F7F7F; - } - } - break; - default: - Sys_Error("Draw_FadeScreen: unsupported r_pixbytes %i", sw32_r_pixbytes); - } - vr_data.scr_copyeverything = 1; - - VID_UnlockBuffer (); - S_ExtraUpdate (); - VID_LockBuffer (); -} - -void -sw32_Draw_BlendScreen (quat_t color) -{ - int r, g, b, i; - byte *basepal, *newpal; - byte pal[768]; - - switch(sw32_r_pixbytes) { - case 1: - { - basepal = vid.basepal; - newpal = pal; - - for (i = 0; i < 256; i++) { - r = basepal[0]; - g = basepal[1]; - b = basepal[2]; - basepal += 3; - - r += (int) (color[3] * (color[0] * 256 - r)); - g += (int) (color[3] * (color[1] * 256 - g)); - b += (int) (color[3] * (color[2] * 256 - b)); - - newpal[0] = vid.gammatable[r]; - newpal[1] = vid.gammatable[g]; - newpal[2] = vid.gammatable[b]; - newpal += 3; - } - vid.set_palette (pal); - } - break; - case 2: - { - int g1, g2, x, y; - unsigned short rramp[32], gramp[64], bramp[32], *temp; - for (i = 0; i < 32; i++) { - r = i << 3; - g1 = i << 3; - g2 = g1 + 4; - b = i << 3; - - r += (int) (color[3] * (color[0] * 256 - r)); - g1 += (int) (color[3] * (color[1] - g1)); - g2 += (int) (color[3] * (color[1] - g2)); - b += (int) (color[3] * (color[2] - b)); - - rramp[i] = (vid.gammatable[r] << 8) & 0xF800; - gramp[i*2+0] = (vid.gammatable[g1] << 3) & 0x07E0; - gramp[i*2+1] = (vid.gammatable[g2] << 3) & 0x07E0; - bramp[i] = (vid.gammatable[b] >> 3) & 0x001F; - } - temp = vid.buffer; - for (y = 0;y < vid.height;y++, temp += (vid.rowbytes >> 1)) - for (x = 0;x < vid.width;x++) - temp[x] = rramp[(temp[x] & 0xF800) >> 11] - + gramp[(temp[x] & 0x07E0) >> 5] + bramp[temp[x] & 0x001F]; - } - break; - case 4: - { - int x, y; - - byte ramp[256][4], *temp; - for (i = 0; i < 256; i++) { - r = i; - g = i; - b = i; - - r += (int) (color[3] * (color[0] * 256 - r)); - g += (int) (color[3] * (color[1] * 256 - g)); - b += (int) (color[3] * (color[2] * 256 - b)); - - ramp[i][0] = vid.gammatable[r]; - ramp[i][1] = vid.gammatable[g]; - ramp[i][2] = vid.gammatable[b]; - ramp[i][3] = 0; - } - temp = vid.buffer; - for (y = 0; y < vid.height; y++, temp += vid.rowbytes) - { - for (x = 0;x < vid.width;x++) - { - temp[x*4+0] = ramp[temp[x*4+0]][0]; - temp[x*4+1] = ramp[temp[x*4+1]][1]; - temp[x*4+2] = ramp[temp[x*4+2]][2]; - temp[x*4+3] = 0; - } - } - } - break; - default: - Sys_Error("V_UpdatePalette: unsupported r_pixbytes %i", sw32_r_pixbytes); - } -} diff --git a/libs/video/renderer/sw32/namehack.h b/libs/video/renderer/sw32/namehack.h deleted file mode 100644 index cbdb673ce..000000000 --- a/libs/video/renderer/sw32/namehack.h +++ /dev/null @@ -1,480 +0,0 @@ -#ifdef NH_DEFINE -#undef NH_DEFINE -#define D_CacheSurface sw32_D_CacheSurface -#define D_DisableBackBufferAccess sw32_D_DisableBackBufferAccess -#define D_DrawParticle sw32_D_DrawParticle -#define D_DrawPoly sw32_D_DrawPoly -#define D_DrawSkyScans sw32_D_DrawSkyScans -#define D_DrawSpans sw32_D_DrawSpans -#define D_DrawSprite sw32_D_DrawSprite -#define D_DrawSurfaces sw32_D_DrawSurfaces -#define D_DrawZPoint sw32_D_DrawZPoint -#define D_DrawZSpans sw32_D_DrawZSpans -#define D_EnableBackBufferAccess sw32_D_EnableBackBufferAccess -#define D_FillRect sw32_D_FillRect -#define D_FlushCaches sw32_D_FlushCaches -#define D_Init sw32_D_Init -#define D_InitCaches sw32_D_InitCaches -#define D_MipLevelForScale sw32_D_MipLevelForScale -#define D_PolysetCalcGradients sw32_D_PolysetCalcGradients -#define D_PolysetDraw sw32_D_PolysetDraw -#define D_PolysetScanLeftEdge sw32_D_PolysetScanLeftEdge -#define D_PolysetSetEdgeTable sw32_D_PolysetSetEdgeTable -#define D_RasterizeAliasPolySmooth sw32_D_RasterizeAliasPolySmooth -#define D_SetupFrame sw32_D_SetupFrame -#define D_SpriteDrawSpans sw32_D_SpriteDrawSpans -#define D_SurfaceCacheAddress sw32_D_SurfaceCacheAddress -#define D_SurfaceCacheForRes sw32_D_SurfaceCacheForRes -#define D_TurnZOn sw32_D_TurnZOn -#define D_UpdateRects sw32_D_UpdateRects -#define D_ViewChanged sw32_D_ViewChanged -#define D_WarpScreen sw32_D_WarpScreen -#define Draw_AltString sw32_Draw_AltString -#define Draw_BlendScreen sw32_Draw_BlendScreen -#define Draw_CachePic sw32_Draw_CachePic -#define Draw_Character sw32_Draw_Character -#define Draw_ConsoleBackground sw32_Draw_ConsoleBackground -#define Draw_Crosshair sw32_Draw_Crosshair -#define Draw_CrosshairAt sw32_Draw_CrosshairAt -#define Draw_DestroyPic sw32_Draw_DestroyPic -#define Draw_FadeScreen sw32_Draw_FadeScreen -#define Draw_Fill sw32_Draw_Fill -#define Draw_Init sw32_Draw_Init -#define Draw_MakePic sw32_Draw_MakePic -#define Draw_Pic sw32_Draw_Pic -#define Draw_PicFromWad sw32_Draw_PicFromWad -#define Draw_Picf sw32_Draw_Picf -#define Draw_String sw32_Draw_String -#define Draw_SubPic sw32_Draw_SubPic -#define Draw_TextBox sw32_Draw_TextBox -#define Draw_TileClear sw32_Draw_TileClear -#define Draw_UncachePic sw32_Draw_UncachePic -#define Draw_nString sw32_Draw_nString -#define R_AliasCheckBBox sw32_R_AliasCheckBBox -#define R_AliasClipTriangle sw32_R_AliasClipTriangle -#define R_AliasClipAndProjectFinalVert sw32_R_AliasClipAndProjectFinalVert -#define R_AliasDrawModel sw32_R_AliasDrawModel -#define R_AliasProjectFinalVert sw32_R_AliasProjectFinalVert -#define R_AliasSetUpTransform sw32_R_AliasSetUpTransform -#define R_AliasTransformAndProjectFinalVerts sw32_R_AliasTransformAndProjectFinalVerts -#define R_AliasTransformFinalVert sw32_R_AliasTransformFinalVert -#define R_AliasTransformVector sw32_R_AliasTransformVector -#define R_Alias_clip_bottom sw32_R_Alias_clip_bottom -#define R_Alias_clip_left sw32_R_Alias_clip_left -#define R_Alias_clip_right sw32_R_Alias_clip_right -#define R_Alias_clip_top sw32_R_Alias_clip_top -#define R_IQMDrawModel sw32_R_IQMDrawModel -#define R_BeginEdgeFrame sw32_R_BeginEdgeFrame -#define R_ClearParticles sw32_R_ClearParticles -#define R_ClearState sw32_R_ClearState -#define R_ClipEdge sw32_R_ClipEdge -#define R_DrawParticles sw32_R_DrawParticles -#define R_DrawSolidClippedSubmodelPolygons sw32_R_DrawSolidClippedSubmodelPolygons -#define R_DrawSprite sw32_R_DrawSprite -#define R_DrawSubmodelPolygons sw32_R_DrawSubmodelPolygons -#define R_DrawSurface sw32_R_DrawSurface -#define R_EmitEdge sw32_R_EmitEdge -#define R_GenerateSpans sw32_R_GenerateSpans -#define R_InitParticles sw32_R_InitParticles -#define R_InitSky sw32_R_InitSky -#define R_InitTurb sw32_R_InitTurb -#define R_InsertNewEdges sw32_R_InsertNewEdges -#define R_LineGraph sw32_R_LineGraph -#define R_LoadSky_f sw32_R_LoadSky_f -#define R_LoadSkys sw32_R_LoadSkys -#define R_MakeSky sw32_R_MakeSky -#define R_NewMap sw32_R_NewMap -#define R_Particle_New sw32_R_Particle_New -#define R_Particle_NewRandom sw32_R_Particle_NewRandom -#define R_Particles_Init_Cvars sw32_R_Particles_Init_Cvars -#define R_PrintAliasStats sw32_R_PrintAliasStats -#define R_PrintTimes sw32_R_PrintTimes -#define R_ReadPointFile_f sw32_R_ReadPointFile_f -#define R_RemoveEdges sw32_R_RemoveEdges -#define R_RenderBmodelFace sw32_R_RenderBmodelFace -#define R_RenderFace sw32_R_RenderFace -#define R_RenderPoly sw32_R_RenderPoly -#define R_RenderView sw32_R_RenderView -#define R_RenderWorld sw32_R_RenderWorld -#define R_RotateBmodel sw32_R_RotateBmodel -#define R_ScanEdges sw32_R_ScanEdges -#define R_SetSkyFrame sw32_R_SetSkyFrame -#define R_SetupFrame sw32_R_SetupFrame -#define R_StepActiveU sw32_R_StepActiveU -#define R_Textures_Init sw32_R_Textures_Init -#define R_TimeRefresh_f sw32_R_TimeRefresh_f -#define R_TransformFrustum sw32_R_TransformFrustum -#define R_TransformPlane sw32_R_TransformPlane -#define R_ViewChanged sw32_R_ViewChanged -#define R_ZDrawSubmodelPolys sw32_R_ZDrawSubmodelPolys -#define SCR_CaptureBGR sw32_SCR_CaptureBGR -#define SCR_ScreenShot sw32_SCR_ScreenShot -#define SCR_ScreenShot_f sw32_SCR_ScreenShot_f -#define SCR_UpdateScreen sw32_SCR_UpdateScreen -#define TransformVector sw32_TransformVector -#define Turbulent sw32_Turbulent -#define acolormap sw32_acolormap -#define aliastransform sw32_aliastransform -#define aliasxcenter sw32_aliasxcenter -#define aliasxscale sw32_aliasxscale -#define aliasycenter sw32_aliasycenter -#define aliasyscale sw32_aliasyscale -#define auxedges sw32_auxedges -#define bbextents sw32_bbextents -#define bbextentt sw32_bbextentt -#define c_faceclip sw32_c_faceclip -#define c_surf sw32_c_surf -#define cacheblock sw32_cacheblock -#define cachewidth sw32_cachewidth -#define d_initial_rover sw32_d_initial_rover -#define d_minmip sw32_d_minmip -#define d_pix_max sw32_d_pix_max -#define d_pix_min sw32_d_pix_min -#define d_pix_shift sw32_d_pix_shift -#define d_pzbuffer sw32_d_pzbuffer -#define d_roverwrapped sw32_d_roverwrapped -#define d_scalemip sw32_d_scalemip -#define d_scantable sw32_d_scantable -#define d_sdivzorigin sw32_d_sdivzorigin -#define d_sdivzstepu sw32_d_sdivzstepu -#define d_sdivzstepv sw32_d_sdivzstepv -#define d_tdivzorigin sw32_d_tdivzorigin -#define d_tdivzstepu sw32_d_tdivzstepu -#define d_tdivzstepv sw32_d_tdivzstepv -#define d_viewbuffer sw32_d_viewbuffer -#define d_vrectbottom_particle sw32_d_vrectbottom_particle -#define d_vrectright_particle sw32_d_vrectright_particle -#define d_vrectx sw32_d_vrectx -#define d_vrecty sw32_d_vrecty -#define d_y_aspect_shift sw32_d_y_aspect_shift -#define d_ziorigin sw32_d_ziorigin -#define d_zistepu sw32_d_zistepu -#define d_zistepv sw32_d_zistepv -#define d_zitable sw32_d_zitable -#define d_zrowbytes sw32_d_zrowbytes -#define d_zwidth sw32_d_zwidth -#define edge_max sw32_edge_max -#define edge_p sw32_edge_p -#define insubmodel sw32_insubmodel -#define intsintable sw32_intsintable -#define newedges sw32_newedges -#define numbtofpolys sw32_numbtofpolys -#define pauxverts sw32_pauxverts -#define pfinalverts sw32_pfinalverts -#define pfrustum_indexes sw32_pfrustum_indexes -#define pixelAspect sw32_pixelAspect -#define r_affinetridesc sw32_r_affinetridesc -#define r_aliastransition sw32_r_aliastransition -#define r_aliasuvscale sw32_r_aliasuvscale -#define r_ambientlight sw32_r_ambientlight -#define r_amodels_drawn sw32_r_amodels_drawn -#define r_apverts sw32_r_apverts -#define r_ceilv1 sw32_r_ceilv1 -#define r_clipflags sw32_r_clipflags -#define r_currentbkey sw32_r_currentbkey -#define r_currentkey sw32_r_currentkey -#define r_dowarp sw32_r_dowarp -#define r_dowarpold sw32_r_dowarpold -#define r_drawculledpolys sw32_r_drawculledpolys -#define r_drawnpolycount sw32_r_drawnpolycount -#define r_drawpolys sw32_r_drawpolys -#define r_drawsurf sw32_r_drawsurf -#define r_easter_eggs_f sw32_r_easter_eggs_f -#define r_edges sw32_r_edges -#define r_emitted sw32_r_emitted -#define r_frustum_indexes sw32_r_frustum_indexes -#define r_lastvertvalid sw32_r_lastvertvalid -#define r_leftclipped sw32_r_leftclipped -#define r_leftenter sw32_r_leftenter -#define r_leftexit sw32_r_leftexit -#define r_lzi1 sw32_r_lzi1 -#define r_maxedgesseen sw32_r_maxedgesseen -#define r_maxsurfsseen sw32_r_maxsurfsseen -#define r_nearzi sw32_r_nearzi -#define r_nearzionly sw32_r_nearzionly -#define r_numallocatededges sw32_r_numallocatededges -#define r_outofedges sw32_r_outofedges -#define r_outofsurfaces sw32_r_outofsurfaces -#define r_particles_style_f sw32_r_particles_style_f -#define r_pedge sw32_r_pedge -#define r_pixbytes sw32_r_pixbytes -#define r_plightvec sw32_r_plightvec -#define r_polycount sw32_r_polycount -#define r_resfudge sw32_r_resfudge -#define r_rightclipped sw32_r_rightclipped -#define r_rightenter sw32_r_rightenter -#define r_rightexit sw32_r_rightexit -#define r_shadelight sw32_r_shadelight -#define r_skymade sw32_r_skymade -#define r_skysource sw32_r_skysource -#define r_skyspeed sw32_r_skyspeed -#define r_skytime sw32_r_skytime -#define r_spritedesc sw32_r_spritedesc -#define r_u1 sw32_r_u1 -#define r_v1 sw32_r_v1 -#define r_viewchanged sw32_r_viewchanged -#define r_warpbuffer sw32_r_warpbuffer -#define r_worldmodelorg sw32_r_worldmodelorg -#define r_worldpolysbacktofront sw32_r_worldpolysbacktofront -#define r_zpointdesc sw32_r_zpointdesc -#define removeedges sw32_removeedges -#define sadjust sw32_sadjust -#define sc_rover sw32_sc_rover -#define scale_for_mip sw32_scale_for_mip -#define screenedge sw32_screenedge -#define screenwidth sw32_screenwidth -#define sintable sw32_sintable -#define surf_max sw32_surf_max -#define surface_p sw32_surface_p -extern struct surf_s *sw32_surfaces; -//#define surfaces sw32_surfaces -#define tadjust sw32_tadjust -#define view_clipplanes sw32_view_clipplanes -#define xcenter sw32_xcenter -#define xscale sw32_xscale -#define xscaleinv sw32_xscaleinv -#define xscaleshrink sw32_xscaleshrink -#define ycenter sw32_ycenter -#define yscale sw32_yscale -#define yscaleinv sw32_yscaleinv -#define yscaleshrink sw32_yscaleshrink -#define zspantable sw32_zspantable -#else -#undef D_CacheSurface -#undef D_DisableBackBufferAccess -#undef D_DrawParticle -#undef D_DrawPoly -#undef D_DrawSkyScans -#undef D_DrawSpans -#undef D_DrawSprite -#undef D_DrawSurfaces -#undef D_DrawZPoint -#undef D_DrawZSpans -#undef D_EnableBackBufferAccess -#undef D_FillRect -#undef D_FlushCaches -#undef D_Init -#undef D_InitCaches -#undef D_MipLevelForScale -#undef D_PolysetCalcGradients -#undef D_PolysetDraw -#undef D_PolysetScanLeftEdge -#undef D_PolysetSetEdgeTable -#undef D_RasterizeAliasPolySmooth -#undef D_SetupFrame -#undef D_SpriteDrawSpans -#undef D_SurfaceCacheAddress -#undef D_SurfaceCacheForRes -#undef D_TurnZOn -#undef D_UpdateRects -#undef D_ViewChanged -#undef D_WarpScreen -#undef Draw_AltString -#undef Draw_BlendScreen -#undef Draw_CachePic -#undef Draw_Character -#undef Draw_ConsoleBackground -#undef Draw_Crosshair -#undef Draw_CrosshairAt -#undef Draw_DestroyPic -#undef Draw_FadeScreen -#undef Draw_Fill -#undef Draw_Init -#undef Draw_MakePic -#undef Draw_Pic -#undef Draw_PicFromWad -#undef Draw_Picf -#undef Draw_String -#undef Draw_SubPic -#undef Draw_TextBox -#undef Draw_TileClear -#undef Draw_UncachePic -#undef Draw_nString -#undef R_AliasCheckBBox -#undef R_AliasClipTriangle -#undef R_AliasClipAndProjectFinalVert -#undef R_AliasDrawModel -#undef R_AliasProjectFinalVert -#undef R_AliasSetUpTransform -#undef R_AliasTransformAndProjectFinalVerts -#undef R_AliasTransformFinalVert -#undef R_AliasTransformVector -#undef R_Alias_clip_bottom -#undef R_Alias_clip_left -#undef R_Alias_clip_right -#undef R_Alias_clip_top -#undef R_IQMDrawModel -#undef R_BeginEdgeFrame -#undef R_ClearParticles -#undef R_ClearState -#undef R_ClipEdge -#undef R_DrawParticles -#undef R_DrawSolidClippedSubmodelPolygons -#undef R_DrawSprite -#undef R_DrawSubmodelPolygons -#undef R_DrawSurface -#undef R_EmitEdge -#undef R_GenerateSpans -#undef R_Init -#undef R_InitParticles -#undef R_InitSky -#undef R_InitTurb -#undef R_InsertNewEdges -#undef R_LineGraph -#undef R_LoadSky_f -#undef R_LoadSkys -#undef R_MakeSky -#undef R_NewMap -#undef R_Particle_New -#undef R_Particle_NewRandom -#undef R_Particles_Init_Cvars -#undef R_PrintAliasStats -#undef R_PrintTimes -#undef R_ReadPointFile_f -#undef R_RemoveEdges -#undef R_RenderBmodelFace -#undef R_RenderFace -#undef R_RenderPoly -#undef R_RenderView -#undef R_RenderWorld -#undef R_RotateBmodel -#undef R_ScanEdges -#undef R_SetSkyFrame -#undef R_SetupFrame -#undef R_StepActiveU -#undef R_Textures_Init -#undef R_TimeRefresh_f -#undef R_TransformFrustum -#undef R_TransformPlane -#undef R_ViewChanged -#undef R_ZDrawSubmodelPolys -#undef SCR_CaptureBGR -#undef SCR_ScreenShot -#undef SCR_ScreenShot_f -#undef SCR_UpdateScreen -#undef TransformVector -#undef Turbulent -#undef VID_InitBuffers -#undef VID_ShiftPalette -#undef acolormap -#undef aliasxcenter -#undef aliasxscale -#undef aliasycenter -#undef aliasyscale -#undef auxedges -#undef bbextents -#undef bbextentt -#undef c_faceclip -#undef c_surf -#undef cacheblock -#undef cachewidth -#undef d_initial_rover -#undef d_minmip -#undef d_pix_max -#undef d_pix_min -#undef d_pix_shift -#undef d_pzbuffer -#undef d_roverwrapped -#undef d_scalemip -#undef d_scantable -#undef d_sdivzorigin -#undef d_sdivzstepu -#undef d_sdivzstepv -#undef d_tdivzorigin -#undef d_tdivzstepu -#undef d_tdivzstepv -#undef d_viewbuffer -#undef d_vrectbottom_particle -#undef d_vrectright_particle -#undef d_vrectx -#undef d_vrecty -#undef d_y_aspect_shift -#undef d_zitable -#undef d_zrowbytes -#undef d_zwidth -#undef edge_max -#undef edge_p -#undef insubmodel -#undef intsintable -#undef newedges -#undef numbtofpolys -#undef pauxverts -#undef pfinalverts -#undef pfrustum_indexes -#undef pixelAspect -#undef r_affinetridesc -#undef r_aliastransition -#undef r_aliasuvscale -#undef r_ambientlight -#undef r_amodels_drawn -#undef r_apverts -#undef r_ceilv1 -#undef r_clipflags -#undef r_currentbkey -#undef r_currentkey -#undef r_dowarp -#undef r_dowarpold -#undef r_drawculledpolys -#undef r_drawnpolycount -#undef r_drawpolys -#undef r_drawsurf -#undef r_easter_eggs_f -#undef r_edges -#undef r_emitted -#undef r_frustum_indexes -#undef r_lastvertvalid -#undef r_leftclipped -#undef r_leftenter -#undef r_leftexit -#undef r_lzi1 -#undef r_maxedgesseen -#undef r_maxsurfsseen -#undef r_nearzi -#undef r_nearzionly -#undef r_numallocatededges -#undef r_outofedges -#undef r_outofsurfaces -#undef r_particles_style_f -#undef r_pedge -#undef r_pixbytes -#undef r_plightvec -#undef r_polycount -#undef r_resfudge -#undef r_rightclipped -#undef r_rightenter -#undef r_rightexit -#undef r_shadelight -#undef r_skymade -#undef r_skysource -#undef r_skyspeed -#undef r_skytime -#undef r_spritedesc -#undef r_u1 -#undef r_v1 -#undef r_viewchanged -#undef r_warpbuffer -#undef r_worldmodelorg -#undef r_worldpolysbacktofront -#undef r_zpointdesc -#undef removeedges -#undef sadjust -#undef sc_rover -#undef scale_for_mip -#undef screenedge -#undef screenwidth -#undef sintable -#undef surf_max -#undef surface_p -//#undef surfaces -#undef tadjust -#undef view_clipplanes -#undef xcenter -#undef xscale -#undef xscaleinv -#undef xscaleshrink -#undef ycenter -#undef yscale -#undef yscaleinv -#undef yscaleshrink -#undef zspantable -#endif diff --git a/libs/video/renderer/sw32/screen.c b/libs/video/renderer/sw32/screen.c deleted file mode 100644 index 63f0ce6e1..000000000 --- a/libs/video/renderer/sw32/screen.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - screen.c - - master for refresh, status bar, console, chat, notify, etc - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif - -#include - -#include "QF/cvar.h" -#include "QF/draw.h" -#include "QF/dstring.h" -#include "QF/image.h" -#include "QF/pcx.h" -#include "QF/quakefs.h" -#include "QF/render.h" -#include "QF/screen.h" -#include "QF/sys.h" -#include "QF/va.h" - -#include "compat.h" -#include "r_internal.h" -#include "vid_internal.h" - -/* SCREEN SHOTS */ - -tex_t * -sw32_SCR_CaptureBGR (void) -{ - int count, x, y; - tex_t *tex; - const byte *src; - byte *dst; - - count = vid.width * vid.height; - tex = malloc (field_offset (tex_t, data[count * 3])); - SYS_CHECKMEM (tex); - tex->width = vid.width; - tex->height = vid.height; - tex->format = tex_rgb; - tex->palette = 0; - sw32_D_EnableBackBufferAccess (); - src = vid.buffer; - for (y = 0; y < tex->height; y++) { - dst = tex->data + (tex->height - 1 - y) * tex->width * 3; - for (x = 0; x < tex->width; x++) { - *dst++ = vid.basepal[*src * 3 + 2]; // blue - *dst++ = vid.basepal[*src * 3 + 1]; // green - *dst++ = vid.basepal[*src * 3 + 0]; // red - src++; - } - } - sw32_D_DisableBackBufferAccess (); - return tex; -} - -tex_t * -sw32_SCR_ScreenShot (int width, int height) -{ - return 0; -} - -void -sw32_SCR_ScreenShot_f (void) -{ - dstring_t *pcxname = dstring_new (); - pcx_t *pcx = 0; - int pcx_len; - - // find a file name to save it to - if (!QFS_NextFilename (pcxname, - va ("%s/qf", qfs_gamedir->dir.shots), ".pcx")) { - Sys_Printf ("SCR_ScreenShot_f: Couldn't create a PCX"); - } else { - // enable direct drawing of console to back buffer - sw32_D_EnableBackBufferAccess (); - - // save the pcx file - switch(sw32_r_pixbytes) { - case 1: - pcx = EncodePCX (vid.buffer, vid.width, vid.height, vid.rowbytes, - vid.basepal, false, &pcx_len); - break; - case 2: - Sys_Printf("SCR_ScreenShot_f: FIXME - add 16bit support\n"); - break; - case 4: - Sys_Printf("SCR_ScreenShot_f: FIXME - add 32bit support\n"); - break; - default: - Sys_Error("SCR_ScreenShot_f: unsupported r_pixbytes %i", sw32_r_pixbytes); - } - - // for adapters that can't stay mapped in for linear writes all the time - sw32_D_DisableBackBufferAccess (); - - if (pcx) { - QFS_WriteFile (pcxname->str, pcx, pcx_len); - Sys_Printf ("Wrote %s/%s\n", qfs_userpath, pcxname->str); - } - } - dstring_delete (pcxname); -} - -/* - SCR_UpdateScreen - - This is called every frame, and can also be called explicitly to flush - text to the screen. - - WARNING: be very careful calling this from elsewhere, because the refresh - needs almost the entire 256k of stack space! -*/ -void -sw32_SCR_UpdateScreen (double realtime, SCR_Func scr_3dfunc, SCR_Func *scr_funcs) -{ - vrect_t vrect; - - if (scr_skipupdate) - return; - - vr_data.realtime = realtime; - - scr_copytop = 0; - vr_data.scr_copyeverything = 0; - - if (!scr_initialized) - return; // not initialized yet - - if (oldfov != scr_fov->value) { // determine size of refresh window - oldfov = scr_fov->value; - vid.recalc_refdef = true; - } - - if (vid.recalc_refdef) - SCR_CalcRefdef (); - - // do 3D refresh drawing, and then update the screen - sw32_D_EnableBackBufferAccess (); // of all overlay stuff if drawing - // directly - - if (vr_data.scr_fullupdate++ < vid.numpages) { // clear the entire screen - vr_data.scr_copyeverything = 1; - sw32_Draw_TileClear (0, 0, vid.width, vid.height); - } - - pconupdate = NULL; - - SCR_SetUpToDrawConsole (); - - sw32_D_DisableBackBufferAccess (); // for adapters that can't stay mapped - // in for linear writes all the time - VID_LockBuffer (); - scr_3dfunc (); - VID_UnlockBuffer (); - - sw32_D_EnableBackBufferAccess (); // of all overlay stuff if drawing - // directly - - while (*scr_funcs) { - (*scr_funcs)(); - scr_funcs++; - } - - sw32_D_DisableBackBufferAccess (); // for adapters that can't stay mapped - // in for linear writes all the time - if (pconupdate) { - sw32_D_UpdateRects (pconupdate); - } - - // update one of three areas - if (vr_data.scr_copyeverything) { - vrect.x = 0; - vrect.y = 0; - vrect.width = vid.width; - vrect.height = vid.height; - vrect.next = 0; - } else if (scr_copytop) { - vrect.x = 0; - vrect.y = 0; - vrect.width = vid.width; - vrect.height = vid.height - vr_data.lineadj; - vrect.next = 0; - } else { - vrect.x = scr_vrect.x; - vrect.y = scr_vrect.y; - vrect.width = scr_vrect.width; - vrect.height = scr_vrect.height; - vrect.next = 0; - } - VID_Update (&vrect); -} diff --git a/libs/video/renderer/sw32/sw32_graph.c b/libs/video/renderer/sw32/sw32_graph.c deleted file mode 100644 index def69d39f..000000000 --- a/libs/video/renderer/sw32/sw32_graph.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - sw32_graph.c - - (description) - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include "QF/cvar.h" -#include "QF/draw.h" -#include "QF/render.h" -#include "QF/sys.h" - -#include "r_internal.h" -#include "vid_internal.h" - -/* - R_LineGraph - - Called by only R_DisplayTime -*/ -void -sw32_R_LineGraph (int x, int y, int *h_vals, int count) -{ - int h, i, s, color; - - // FIXME: disable on no-buffer adapters, or put in the driver - s = r_graphheight->int_val; - - while (count--) { - h = *h_vals++; - - if (h == 10000) - color = 0x6f; // yellow - else if (h == 9999) - color = 0x4f; // red - else if (h == 9998) - color = 0xd0; // blue - else - color = 0xff; // pink - - if (h > s) - h = s; - - switch(sw32_r_pixbytes) { - case 1: - { - byte *dest = (byte *) vid.buffer + vid.rowbytes * y + x; - for (i = 0; i < h; i++, dest -= vid.rowbytes * 2) - *dest = color; - } - break; - case 2: - { - short *dest = (short *) vid.buffer + - (vid.rowbytes >> 1) * y + x; - color = sw32_8to16table[color]; - for (i = 0; i < h; i++, dest -= vid.rowbytes) - *dest = color; - } - break; - case 4: - { - int *dest = (int *) vid.buffer + - (vid.rowbytes >> 2) * y + x; - color = d_8to24table[color]; - for (i = 0; i < h; i++, dest -= (vid.rowbytes >> 1)) - *dest = color; - } - break; - default: - Sys_Error("R_LineGraph: unsupported r_pixbytes %i", - sw32_r_pixbytes); - } - } -} diff --git a/libs/video/renderer/sw32/sw32_raclip.c b/libs/video/renderer/sw32/sw32_raclip.c deleted file mode 100644 index 164008769..000000000 --- a/libs/video/renderer/sw32/sw32_raclip.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - r_aclip.c - - clip routines for drawing Alias models directly to the screen - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include "QF/render.h" - -#include "d_local.h" -#include "r_internal.h" - -static finalvert_t fv[2][8]; -static auxvert_t av[8]; - -/* - R_Alias_clip_z - - pfv0 is the unclipped vertex, pfv1 is the z-clipped vertex -*/ -static void -R_Alias_clip_z (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) -{ - float scale; - auxvert_t *pav0, *pav1, avout; - - pav0 = &av[pfv0 - &fv[0][0]]; - pav1 = &av[pfv1 - &fv[0][0]]; - - if (pfv0->v[1] >= pfv1->v[1]) { - scale = (ALIAS_Z_CLIP_PLANE - pav0->fv[2]) / - (pav1->fv[2] - pav0->fv[2]); - - avout.fv[0] = pav0->fv[0] + (pav1->fv[0] - pav0->fv[0]) * scale; - avout.fv[1] = pav0->fv[1] + (pav1->fv[1] - pav0->fv[1]) * scale; - avout.fv[2] = ALIAS_Z_CLIP_PLANE; - - out->v[2] = pfv0->v[2] + (pfv1->v[2] - pfv0->v[2]) * scale; - out->v[3] = pfv0->v[3] + (pfv1->v[3] - pfv0->v[3]) * scale; - out->v[4] = pfv0->v[4] + (pfv1->v[4] - pfv0->v[4]) * scale; - } else { - scale = (ALIAS_Z_CLIP_PLANE - pav1->fv[2]) / - (pav0->fv[2] - pav1->fv[2]); - - avout.fv[0] = pav1->fv[0] + (pav0->fv[0] - pav1->fv[0]) * scale; - avout.fv[1] = pav1->fv[1] + (pav0->fv[1] - pav1->fv[1]) * scale; - avout.fv[2] = ALIAS_Z_CLIP_PLANE; - - out->v[2] = pfv1->v[2] + (pfv0->v[2] - pfv1->v[2]) * scale; - out->v[3] = pfv1->v[3] + (pfv0->v[3] - pfv1->v[3]) * scale; - out->v[4] = pfv1->v[4] + (pfv0->v[4] - pfv1->v[4]) * scale; - } - - sw32_R_AliasProjectFinalVert (out, &avout); - - if (out->v[0] < r_refdef.aliasvrect.x) - out->flags |= ALIAS_LEFT_CLIP; - if (out->v[1] < r_refdef.aliasvrect.y) - out->flags |= ALIAS_TOP_CLIP; - if (out->v[0] > r_refdef.aliasvrectright) - out->flags |= ALIAS_RIGHT_CLIP; - if (out->v[1] > r_refdef.aliasvrectbottom) - out->flags |= ALIAS_BOTTOM_CLIP; -} - - -void -sw32_R_Alias_clip_left (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) -{ - float scale; - int i; - - if (pfv0->v[1] >= pfv1->v[1]) { - scale = (float) (r_refdef.aliasvrect.x - pfv0->v[0]) / - (pfv1->v[0] - pfv0->v[0]); - for (i = 0; i < 6; i++) - out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i]) * scale + 0.5; - } else { - scale = (float) (r_refdef.aliasvrect.x - pfv1->v[0]) / - (pfv0->v[0] - pfv1->v[0]); - for (i = 0; i < 6; i++) - out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i]) * scale + 0.5; - } -} - - -void -sw32_R_Alias_clip_right (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) -{ - float scale; - int i; - - if (pfv0->v[1] >= pfv1->v[1]) { - scale = (float) (r_refdef.aliasvrectright - pfv0->v[0]) / - (pfv1->v[0] - pfv0->v[0]); - for (i = 0; i < 6; i++) - out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i]) * scale + 0.5; - } else { - scale = (float) (r_refdef.aliasvrectright - pfv1->v[0]) / - (pfv0->v[0] - pfv1->v[0]); - for (i = 0; i < 6; i++) - out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i]) * scale + 0.5; - } -} - - -void -sw32_R_Alias_clip_top (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) -{ - float scale; - int i; - - if (pfv0->v[1] >= pfv1->v[1]) { - scale = (float) (r_refdef.aliasvrect.y - pfv0->v[1]) / - (pfv1->v[1] - pfv0->v[1]); - for (i = 0; i < 6; i++) - out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i]) * scale + 0.5; - } else { - scale = (float) (r_refdef.aliasvrect.y - pfv1->v[1]) / - (pfv0->v[1] - pfv1->v[1]); - for (i = 0; i < 6; i++) - out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i]) * scale + 0.5; - } -} - - -void -sw32_R_Alias_clip_bottom (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) -{ - float scale; - int i; - - if (pfv0->v[1] >= pfv1->v[1]) { - scale = (float) (r_refdef.aliasvrectbottom - pfv0->v[1]) / - (pfv1->v[1] - pfv0->v[1]); - - for (i = 0; i < 6; i++) - out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i]) * scale + 0.5; - } else { - scale = (float) (r_refdef.aliasvrectbottom - pfv1->v[1]) / - (pfv0->v[1] - pfv1->v[1]); - - for (i = 0; i < 6; i++) - out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i]) * scale + 0.5; - } -} - - -static int -R_AliasClip (finalvert_t *in, finalvert_t *out, int flag, int count, - void (*clip) (finalvert_t *pfv0, finalvert_t *pfv1, - finalvert_t *out)) -{ - int i, j, k; - int flags, oldflags; - - j = count - 1; - k = 0; - for (i = 0; i < count; j = i, i++) { - oldflags = in[j].flags & flag; - flags = in[i].flags & flag; - - if (flags && oldflags) - continue; - if (oldflags ^ flags) { - clip (&in[j], &in[i], &out[k]); - out[k].flags = 0; - if (out[k].v[0] < r_refdef.aliasvrect.x) - out[k].flags |= ALIAS_LEFT_CLIP; - if (out[k].v[1] < r_refdef.aliasvrect.y) - out[k].flags |= ALIAS_TOP_CLIP; - if (out[k].v[0] > r_refdef.aliasvrectright) - out[k].flags |= ALIAS_RIGHT_CLIP; - if (out[k].v[1] > r_refdef.aliasvrectbottom) - out[k].flags |= ALIAS_BOTTOM_CLIP; - k++; - } - if (!flags) { - out[k] = in[i]; - k++; - } - } - - return k; -} - - -void -sw32_R_AliasClipTriangle (mtriangle_t *ptri) -{ - int i, k, pingpong; - mtriangle_t mtri; - unsigned int clipflags; - - // copy vertexes and fix seam texture coordinates - if (ptri->facesfront) { - fv[0][0] = pfinalverts[ptri->vertindex[0]]; - fv[0][1] = pfinalverts[ptri->vertindex[1]]; - fv[0][2] = pfinalverts[ptri->vertindex[2]]; - } else { - for (i = 0; i < 3; i++) { - fv[0][i] = pfinalverts[ptri->vertindex[i]]; - - if (!ptri->facesfront && (fv[0][i].flags & ALIAS_ONSEAM)) - fv[0][i].v[2] += sw32_r_affinetridesc.seamfixupX16; - } - } - - // clip - clipflags = fv[0][0].flags | fv[0][1].flags | fv[0][2].flags; - - if (clipflags & ALIAS_Z_CLIP) { - for (i = 0; i < 3; i++) - av[i] = sw32_pauxverts[ptri->vertindex[i]]; - - k = R_AliasClip (fv[0], fv[1], ALIAS_Z_CLIP, 3, R_Alias_clip_z); - if (k == 0) - return; - - pingpong = 1; - clipflags = fv[1][0].flags | fv[1][1].flags | fv[1][2].flags; - } else { - pingpong = 0; - k = 3; - } - - if (clipflags & ALIAS_LEFT_CLIP) { - k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], - ALIAS_LEFT_CLIP, k, sw32_R_Alias_clip_left); - if (k == 0) - return; - - pingpong ^= 1; - } - - if (clipflags & ALIAS_RIGHT_CLIP) { - k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], - ALIAS_RIGHT_CLIP, k, sw32_R_Alias_clip_right); - if (k == 0) - return; - - pingpong ^= 1; - } - - if (clipflags & ALIAS_BOTTOM_CLIP) { - k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], - ALIAS_BOTTOM_CLIP, k, sw32_R_Alias_clip_bottom); - if (k == 0) - return; - - pingpong ^= 1; - } - - if (clipflags & ALIAS_TOP_CLIP) { - k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], - ALIAS_TOP_CLIP, k, sw32_R_Alias_clip_top); - if (k == 0) - return; - - pingpong ^= 1; - } - - for (i = 0; i < k; i++) { - if (fv[pingpong][i].v[0] < r_refdef.aliasvrect.x) - fv[pingpong][i].v[0] = r_refdef.aliasvrect.x; - else if (fv[pingpong][i].v[0] > r_refdef.aliasvrectright) - fv[pingpong][i].v[0] = r_refdef.aliasvrectright; - - if (fv[pingpong][i].v[1] < r_refdef.aliasvrect.y) - fv[pingpong][i].v[1] = r_refdef.aliasvrect.y; - else if (fv[pingpong][i].v[1] > r_refdef.aliasvrectbottom) - fv[pingpong][i].v[1] = r_refdef.aliasvrectbottom; - - fv[pingpong][i].flags = 0; - } - - // draw triangles - mtri.facesfront = ptri->facesfront; - sw32_r_affinetridesc.ptriangles = &mtri; - sw32_r_affinetridesc.pfinalverts = fv[pingpong]; - - // FIXME: do all at once as trifan? - mtri.vertindex[0] = 0; - for (i = 1; i < k - 1; i++) { - mtri.vertindex[1] = i; - mtri.vertindex[2] = i + 1; - sw32_D_PolysetDraw (); - } -} diff --git a/libs/video/renderer/sw32/sw32_ralias.c b/libs/video/renderer/sw32/sw32_ralias.c deleted file mode 100644 index 167730ab1..000000000 --- a/libs/video/renderer/sw32/sw32_ralias.c +++ /dev/null @@ -1,673 +0,0 @@ -/* - sw32_ralias.c - - routines for setting up to draw alias models - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include "QF/image.h" -#include "QF/render.h" -#include "QF/skin.h" -#include "QF/sys.h" - -#include "d_ifacea.h" -#include "r_internal.h" - -#include "stdlib.h" - -#define LIGHT_MIN 5 // lowest light value we'll allow, to - // avoid the need for inner-loop light - // clamping - -affinetridesc_t sw32_r_affinetridesc; - -void *sw32_acolormap; // FIXME: should go away - -trivertx_t *sw32_r_apverts; - -// TODO: these probably will go away with optimized rasterization -static mdl_t *pmdl; -vec3_t sw32_r_plightvec; -int sw32_r_ambientlight; -float sw32_r_shadelight; -static aliashdr_t *paliashdr; -finalvert_t *sw32_pfinalverts; -auxvert_t *sw32_pauxverts; -float sw32_ziscale; -static model_t *pmodel; - -static vec3_t alias_forward, alias_right, alias_up; - -static maliasskindesc_t *pskindesc; - -int sw32_r_amodels_drawn; -static int a_skinwidth; -static int r_anumverts; - -float sw32_aliastransform[3][4]; - -typedef struct { - int index0; - int index1; -} aedge_t; - -static aedge_t aedges[12] = { - {0, 1}, {1, 2}, {2, 3}, {3, 0}, - {4, 5}, {5, 6}, {6, 7}, {7, 4}, - {0, 5}, {1, 4}, {2, 7}, {3, 6} -}; - -qboolean -sw32_R_AliasCheckBBox (void) -{ - int i, flags, frame, numv; - aliashdr_t *pahdr; - float zi, basepts[8][3], v0, v1, frac; - finalvert_t *pv0, *pv1, viewpts[16]; - auxvert_t *pa0, *pa1, viewaux[16]; - maliasframedesc_t *pframedesc; - qboolean zclipped, zfullyclipped; - unsigned int anyclip, allclip; - int minz; - - // expand, rotate, and translate points into worldspace - currententity->trivial_accept = 0; - pmodel = currententity->model; - if (!(pahdr = pmodel->aliashdr)) - pahdr = Cache_Get (&pmodel->cache); - pmdl = (mdl_t *) ((byte *) pahdr + pahdr->model); - - sw32_R_AliasSetUpTransform (0); - - // construct the base bounding box for this frame - frame = currententity->frame; -// TODO: don't repeat this check when drawing? - if ((frame >= pmdl->numframes) || (frame < 0)) { - Sys_MaskPrintf (SYS_DEV, "No such frame %d %s\n", frame, pmodel->name); - frame = 0; - } - - pframedesc = &pahdr->frames[frame]; - - // x worldspace coordinates - basepts[0][0] = basepts[1][0] = basepts[2][0] = basepts[3][0] = - (float) pframedesc->bboxmin.v[0]; - basepts[4][0] = basepts[5][0] = basepts[6][0] = basepts[7][0] = - (float) pframedesc->bboxmax.v[0]; - - // y worldspace coordinates - basepts[0][1] = basepts[3][1] = basepts[5][1] = basepts[6][1] = - (float) pframedesc->bboxmin.v[1]; - basepts[1][1] = basepts[2][1] = basepts[4][1] = basepts[7][1] = - (float) pframedesc->bboxmax.v[1]; - - // z worldspace coordinates - basepts[0][2] = basepts[1][2] = basepts[4][2] = basepts[5][2] = - (float) pframedesc->bboxmin.v[2]; - basepts[2][2] = basepts[3][2] = basepts[6][2] = basepts[7][2] = - (float) pframedesc->bboxmax.v[2]; - - zclipped = false; - zfullyclipped = true; - - minz = 9999; - for (i = 0; i < 8; i++) { - sw32_R_AliasTransformVector (&basepts[i][0], &viewaux[i].fv[0]); - - if (viewaux[i].fv[2] < ALIAS_Z_CLIP_PLANE) { - // we must clip points that are closer than the near clip plane - viewpts[i].flags = ALIAS_Z_CLIP; - zclipped = true; - } else { - if (viewaux[i].fv[2] < minz) - minz = viewaux[i].fv[2]; - viewpts[i].flags = 0; - zfullyclipped = false; - } - } - - if (zfullyclipped) { - if (!pmodel->aliashdr) - Cache_Release (&pmodel->cache); - return false; // everything was near-z-clipped - } - - numv = 8; - - if (zclipped) { - // organize points by edges, use edges to get new points (possible - // trivial reject) - for (i = 0; i < 12; i++) { - // edge endpoints - pv0 = &viewpts[aedges[i].index0]; - pv1 = &viewpts[aedges[i].index1]; - pa0 = &viewaux[aedges[i].index0]; - pa1 = &viewaux[aedges[i].index1]; - - // if one end is clipped and the other isn't, make a new point - if (pv0->flags ^ pv1->flags) { - frac = (ALIAS_Z_CLIP_PLANE - pa0->fv[2]) / - (pa1->fv[2] - pa0->fv[2]); - viewaux[numv].fv[0] = pa0->fv[0] + - (pa1->fv[0] - pa0->fv[0]) * frac; - viewaux[numv].fv[1] = pa0->fv[1] + - (pa1->fv[1] - pa0->fv[1]) * frac; - viewaux[numv].fv[2] = ALIAS_Z_CLIP_PLANE; - viewpts[numv].flags = 0; - numv++; - } - } - } - // project the vertices that remain after clipping - anyclip = 0; - allclip = ALIAS_XY_CLIP_MASK; - -// TODO: probably should do this loop in ASM, especially if we use floats - for (i = 0; i < numv; i++) { - // we don't need to bother with vertices that were z-clipped - if (viewpts[i].flags & ALIAS_Z_CLIP) - continue; - - zi = 1.0 / viewaux[i].fv[2]; - - // FIXME: do with chop mode in ASM, or convert to float - v0 = (viewaux[i].fv[0] * sw32_xscale * zi) + sw32_xcenter; - v1 = (viewaux[i].fv[1] * sw32_yscale * zi) + sw32_ycenter; - - flags = 0; - - if (v0 < r_refdef.fvrectx) - flags |= ALIAS_LEFT_CLIP; - if (v1 < r_refdef.fvrecty) - flags |= ALIAS_TOP_CLIP; - if (v0 > r_refdef.fvrectright) - flags |= ALIAS_RIGHT_CLIP; - if (v1 > r_refdef.fvrectbottom) - flags |= ALIAS_BOTTOM_CLIP; - - anyclip |= flags; - allclip &= flags; - } - - if (allclip) { - if (!pmodel->aliashdr) - Cache_Release (&pmodel->cache); - return false; // trivial reject off one side - } - - currententity->trivial_accept = !anyclip & !zclipped; - - if (currententity->trivial_accept) { - if (minz > (sw32_r_aliastransition + (pmdl->size * sw32_r_resfudge))) { - currententity->trivial_accept |= 2; - } - } - - if (!pmodel->aliashdr) - Cache_Release (&pmodel->cache); - return true; -} - - -void -sw32_R_AliasTransformVector (vec3_t in, vec3_t out) -{ - out[0] = DotProduct (in, sw32_aliastransform[0]) - + sw32_aliastransform[0][3]; - out[1] = DotProduct (in, sw32_aliastransform[1]) - + sw32_aliastransform[1][3]; - out[2] = DotProduct (in, sw32_aliastransform[2]) - + sw32_aliastransform[2][3]; -} - - -void -sw32_R_AliasClipAndProjectFinalVert (finalvert_t *fv, auxvert_t *av) -{ - if (av->fv[2] < ALIAS_Z_CLIP_PLANE) { - fv->flags |= ALIAS_Z_CLIP; - return; - } - - sw32_R_AliasProjectFinalVert (fv, av); - - if (fv->v[0] < r_refdef.aliasvrect.x) - fv->flags |= ALIAS_LEFT_CLIP; - if (fv->v[1] < r_refdef.aliasvrect.y) - fv->flags |= ALIAS_TOP_CLIP; - if (fv->v[0] > r_refdef.aliasvrectright) - fv->flags |= ALIAS_RIGHT_CLIP; - if (fv->v[1] > r_refdef.aliasvrectbottom) - fv->flags |= ALIAS_BOTTOM_CLIP; -} - -static void -R_AliasTransformFinalVert16 (auxvert_t *av, trivertx_t *pverts) -{ - trivertx_t * pextra; - float vextra[3]; - - pextra = pverts + pmdl->numverts; - vextra[0] = pverts->v[0] + pextra->v[0] / (float)256; - vextra[1] = pverts->v[1] + pextra->v[1] / (float)256; - vextra[2] = pverts->v[2] + pextra->v[2] / (float)256; - av->fv[0] = DotProduct (vextra, sw32_aliastransform[0]) + - sw32_aliastransform[0][3]; - av->fv[1] = DotProduct (vextra, sw32_aliastransform[1]) + - sw32_aliastransform[1][3]; - av->fv[2] = DotProduct (vextra, sw32_aliastransform[2]) + - sw32_aliastransform[2][3]; -} - -static void -R_AliasTransformFinalVert8 (auxvert_t *av, trivertx_t *pverts) -{ - av->fv[0] = DotProduct (pverts->v, sw32_aliastransform[0]) + - sw32_aliastransform[0][3]; - av->fv[1] = DotProduct (pverts->v, sw32_aliastransform[1]) + - sw32_aliastransform[1][3]; - av->fv[2] = DotProduct (pverts->v, sw32_aliastransform[2]) + - sw32_aliastransform[2][3]; -} - -/* - R_AliasPreparePoints - - General clipped case -*/ -static void -R_AliasPreparePoints (void) -{ - int i; - stvert_t *pstverts; - finalvert_t *fv; - auxvert_t *av; - mtriangle_t *ptri; - finalvert_t *pfv[3]; - - pstverts = (stvert_t *) ((byte *) paliashdr + paliashdr->stverts); - r_anumverts = pmdl->numverts; - fv = pfinalverts; - av = sw32_pauxverts; - - if (pmdl->ident == HEADER_MDL16) { - for (i = 0; i < r_anumverts; i++, fv++, av++, sw32_r_apverts++, - pstverts++) { - R_AliasTransformFinalVert16 (av, sw32_r_apverts); - sw32_R_AliasTransformFinalVert (fv, sw32_r_apverts, pstverts); - R_AliasClipAndProjectFinalVert (fv, av); - } - } - else { - for (i = 0; i < r_anumverts; i++, fv++, av++, sw32_r_apverts++, - pstverts++) { - R_AliasTransformFinalVert8 (av, sw32_r_apverts); - sw32_R_AliasTransformFinalVert (fv, sw32_r_apverts, pstverts); - R_AliasClipAndProjectFinalVert (fv, av); - } - } - - // clip and draw all triangles - sw32_r_affinetridesc.numtriangles = 1; - - ptri = (mtriangle_t *) ((byte *) paliashdr + paliashdr->triangles); - for (i = 0; i < pmdl->numtris; i++, ptri++) { - pfv[0] = &pfinalverts[ptri->vertindex[0]]; - pfv[1] = &pfinalverts[ptri->vertindex[1]]; - pfv[2] = &pfinalverts[ptri->vertindex[2]]; - - if (pfv[0]->flags & pfv[1]->flags & pfv[2]-> - flags & (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP)) - continue; // completely clipped - - if (!((pfv[0]->flags | pfv[1]->flags | pfv[2]->flags) & - (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP))) { // totally unclipped - sw32_r_affinetridesc.pfinalverts = pfinalverts; - sw32_r_affinetridesc.ptriangles = ptri; - sw32_D_PolysetDraw (); - } else { // partially clipped - sw32_R_AliasClipTriangle (ptri); - } - } -} - - -void -sw32_R_AliasSetUpTransform (int trivial_accept) -{ - int i; - float rotationmatrix[3][4], t2matrix[3][4]; - static float tmatrix[3][4]; - static float viewmatrix[3][4]; - - VectorCopy (currententity->transform + 0, alias_forward); - VectorNegate (currententity->transform + 4, alias_right); - VectorCopy (currententity->transform + 8, alias_up); - - tmatrix[0][0] = pmdl->scale[0]; - tmatrix[1][1] = pmdl->scale[1]; - tmatrix[2][2] = pmdl->scale[2]; - - tmatrix[0][3] = pmdl->scale_origin[0]; - tmatrix[1][3] = pmdl->scale_origin[1]; - tmatrix[2][3] = pmdl->scale_origin[2]; - -// TODO: can do this with simple matrix rearrangement - - for (i = 0; i < 3; i++) { - t2matrix[i][0] = alias_forward[i]; - t2matrix[i][1] = -alias_right[i]; - t2matrix[i][2] = alias_up[i]; - } - - t2matrix[0][3] = -modelorg[0]; - t2matrix[1][3] = -modelorg[1]; - t2matrix[2][3] = -modelorg[2]; - -// FIXME: can do more efficiently than full concatenation - R_ConcatTransforms (t2matrix, tmatrix, rotationmatrix); - -// TODO: should be global, set when vright, etc., set - VectorCopy (vright, viewmatrix[0]); - VectorCopy (vup, viewmatrix[1]); - VectorNegate (viewmatrix[1], viewmatrix[1]); - VectorCopy (vpn, viewmatrix[2]); - -// viewmatrix[0][3] = 0; -// viewmatrix[1][3] = 0; -// viewmatrix[2][3] = 0; - - R_ConcatTransforms (viewmatrix, rotationmatrix, sw32_aliastransform); - -// do the scaling up of x and y to screen coordinates as part of the transform -// for the unclipped case (it would mess up clipping in the clipped case). -// Also scale down z, so 1/z is scaled 31 bits for free, and scale down x and y -// correspondingly so the projected x and y come out right -// FIXME: make this work for clipped case too? - if (trivial_accept) { - for (i = 0; i < 4; i++) { - sw32_aliastransform[0][i] *= sw32_aliasxscale * - (1.0 / ((float) 0x8000 * 0x10000)); - sw32_aliastransform[1][i] *= sw32_aliasyscale * - (1.0 / ((float) 0x8000 * 0x10000)); - sw32_aliastransform[2][i] *= 1.0 / ((float) 0x8000 * 0x10000); - } - } -} - -/* -sw32_R_AliasTransformFinalVert - -now this function just copies the texture coordinates and calculates lighting -actual 3D transform is done by R_AliasTransformFinalVert8/16 functions above -*/ -void -sw32_R_AliasTransformFinalVert (finalvert_t *fv, trivertx_t *pverts, - stvert_t *pstverts) -{ - int temp; - float lightcos, *plightnormal; - - fv->v[2] = pstverts->s; - fv->v[3] = pstverts->t; - - fv->flags = pstverts->onseam; - - // lighting - // LordHavoc: flipped lightcos so it is + for bright, not - - plightnormal = r_avertexnormals[pverts->lightnormalindex]; - lightcos = -DotProduct (plightnormal, sw32_r_plightvec); - temp = sw32_r_ambientlight; - - if (lightcos > 0) { - temp += (int) (sw32_r_shadelight * lightcos); - - // clamp; because we limited the minimum ambient and shading light, - // we don't have to clamp low light, just bright - if (temp < 0) - temp = 0; - } - - fv->v[4] = temp; -} - -void -sw32_R_AliasTransformAndProjectFinalVerts (finalvert_t *fv, stvert_t *pstverts) -{ - int i, temp; - float lightcos, *plightnormal, zi; - trivertx_t *pverts; - - pverts = sw32_r_apverts; - - for (i = 0; i < r_anumverts; i++, fv++, pverts++, pstverts++) { - // transform and project - zi = 1.0 / (DotProduct (pverts->v, sw32_aliastransform[2]) + - sw32_aliastransform[2][3]); - - // x, y, and z are scaled down by 1/2**31 in the transform, so 1/z is - // scaled up by 1/2**31, and the scaling cancels out for x and y in - // the projection - fv->v[5] = zi; - - fv->v[0] = ((DotProduct (pverts->v, sw32_aliastransform[0]) + - sw32_aliastransform[0][3]) * zi) + sw32_aliasxcenter; - fv->v[1] = ((DotProduct (pverts->v, sw32_aliastransform[1]) + - sw32_aliastransform[1][3]) * zi) + sw32_aliasycenter; - - fv->v[2] = pstverts->s; - fv->v[3] = pstverts->t; - fv->flags = pstverts->onseam; - - // lighting - // LordHavoc: flipped lightcos so it is + for bright, not - - plightnormal = r_avertexnormals[pverts->lightnormalindex]; - lightcos = -DotProduct (plightnormal, sw32_r_plightvec); - temp = sw32_r_ambientlight; - - if (lightcos > 0) { - temp += (int) (sw32_r_shadelight * lightcos); - - // clamp; because we limited the minimum ambient and shading - // light, we don't have to clamp low light, just bright - if (temp < 0) - temp = 0; - } - - fv->v[4] = temp; - } -} - -void -sw32_R_AliasProjectFinalVert (finalvert_t *fv, auxvert_t *av) -{ - float zi; - - // project points - zi = 1.0 / av->fv[2]; - - fv->v[5] = zi * sw32_ziscale; - - fv->v[0] = (av->fv[0] * sw32_aliasxscale * zi) + sw32_aliasxcenter; - fv->v[1] = (av->fv[1] * sw32_aliasyscale * zi) + sw32_aliasycenter; -} - - -static void -R_AliasPrepareUnclippedPoints (void) -{ - stvert_t *pstverts; - - pstverts = (stvert_t *) ((byte *) paliashdr + paliashdr->stverts); - r_anumverts = pmdl->numverts; - - sw32_R_AliasTransformAndProjectFinalVerts (pfinalverts, pstverts); - - sw32_r_affinetridesc.pfinalverts = pfinalverts; - sw32_r_affinetridesc.ptriangles = (mtriangle_t *) - ((byte *) paliashdr + paliashdr->triangles); - sw32_r_affinetridesc.numtriangles = pmdl->numtris; - - sw32_D_PolysetDraw (); -} - - -static void -R_AliasSetupSkin (void) -{ - int skinnum; - - skinnum = currententity->skinnum; - if ((skinnum >= pmdl->numskins) || (skinnum < 0)) { - Sys_MaskPrintf (SYS_DEV, "R_AliasSetupSkin: no such skin # %d\n", - skinnum); - skinnum = 0; - } - - pskindesc = R_AliasGetSkindesc (skinnum, paliashdr); - a_skinwidth = pmdl->skinwidth; - - sw32_r_affinetridesc.pskin = (void *) ((byte *) paliashdr + pskindesc->skin); - sw32_r_affinetridesc.skinwidth = a_skinwidth; - sw32_r_affinetridesc.seamfixupX16 = (a_skinwidth >> 1) << 16; - sw32_r_affinetridesc.skinheight = pmdl->skinheight; - - sw32_acolormap = vid.colormap8; - if (currententity->skin) { - tex_t *base; - - base = currententity->skin->texels; - if (base) { - sw32_r_affinetridesc.pskin = base->data; - sw32_r_affinetridesc.skinwidth = base->width; - sw32_r_affinetridesc.skinheight = base->height; - } - sw32_acolormap = currententity->skin->colormap; - } -} - -static void -R_AliasSetupLighting (alight_t *plighting) -{ - // guarantee that no vertex will ever be lit below LIGHT_MIN, so we don't - // have to clamp off the bottom - sw32_r_ambientlight = plighting->ambientlight; - - if (sw32_r_ambientlight < LIGHT_MIN) - sw32_r_ambientlight = LIGHT_MIN; - - sw32_r_ambientlight = (/*255 -*/ sw32_r_ambientlight) << VID_CBITS; - -// if (sw32_r_ambientlight < LIGHT_MIN) -// sw32_r_ambientlight = LIGHT_MIN; - - sw32_r_shadelight = plighting->shadelight; - - if (sw32_r_shadelight < 0) - sw32_r_shadelight = 0; - - sw32_r_shadelight *= VID_GRADES; - - // rotate the lighting vector into the model's frame of reference - sw32_r_plightvec[0] = DotProduct (plighting->plightvec, alias_forward); - sw32_r_plightvec[1] = -DotProduct (plighting->plightvec, alias_right); - sw32_r_plightvec[2] = DotProduct (plighting->plightvec, alias_up); -} - - -/* - R_AliasSetupFrame - - set sw32_r_apverts -*/ -static void -R_AliasSetupFrame (void) -{ - maliasframedesc_t *frame; - - frame = R_AliasGetFramedesc (currententity->frame, paliashdr); - sw32_r_apverts = (trivertx_t *) ((byte *) paliashdr + frame->frame); -} - - -void -sw32_R_AliasDrawModel (alight_t *plighting) -{ - int size; - finalvert_t *finalverts; - - sw32_r_amodels_drawn++; - - if (!(paliashdr = currententity->model->aliashdr)) - paliashdr = Cache_Get (¤tentity->model->cache); - pmdl = (mdl_t *) ((byte *) paliashdr + paliashdr->model); - - size = (CACHE_SIZE - 1) - + sizeof (finalvert_t) * (pmdl->numverts + 1) - + sizeof (auxvert_t) * pmdl->numverts; - finalverts = (finalvert_t *) Hunk_TempAlloc (size); - if (!finalverts) - Sys_Error ("R_AliasDrawModel: out of memory"); - - // cache align - pfinalverts = (finalvert_t *) - (((intptr_t) &finalverts[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); - sw32_pauxverts = (auxvert_t *) &pfinalverts[pmdl->numverts + 1]; - - R_AliasSetupSkin (); - sw32_R_AliasSetUpTransform (currententity->trivial_accept); - R_AliasSetupLighting (plighting); - R_AliasSetupFrame (); - - if (!sw32_acolormap) - sw32_acolormap = vid.colormap8; - if (sw32_acolormap == &vid.colormap8 && sw32_r_pixbytes != 1) - { - if (sw32_r_pixbytes == 2) - sw32_acolormap = vid.colormap16; - else if (sw32_r_pixbytes == 4) - sw32_acolormap = vid.colormap32; - else - Sys_Error("R_AliasDrawmodel: unsupported r_pixbytes %i", - sw32_r_pixbytes); - } - - if (currententity != vr_data.view_model) - sw32_ziscale = (float) 0x8000 *(float) 0x10000; - else - sw32_ziscale = (float) 0x8000 *(float) 0x10000 *3.0; - - if (currententity->trivial_accept) - R_AliasPrepareUnclippedPoints (); - else - R_AliasPreparePoints (); - - if (!currententity->model->aliashdr) - Cache_Release (¤tentity->model->cache); -} diff --git a/libs/video/renderer/sw32/sw32_rbsp.c b/libs/video/renderer/sw32/sw32_rbsp.c deleted file mode 100644 index 017268dbb..000000000 --- a/libs/video/renderer/sw32/sw32_rbsp.c +++ /dev/null @@ -1,527 +0,0 @@ -/* - sw32_r_bsp.c - - (description) - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include -#include - -#include "qfalloca.h" - -#include "QF/render.h" -#include "QF/sys.h" - -#include "r_internal.h" - -// current entity info -qboolean sw32_insubmodel; -vec3_t sw32_r_worldmodelorg; -static float entity_rotation[3][3]; - -int sw32_r_currentbkey; - -typedef enum { touchessolid, drawnode, nodrawnode } solidstate_t; - -#define MAX_BMODEL_VERTS 500 // 6K -#define MAX_BMODEL_EDGES 1000 // 12K - -static mvertex_t *pbverts; -static bedge_t *pbedges; -static int numbverts, numbedges; - -int sw32_numbtofpolys; -static btofpoly_t *pbtofpolys; - -static mvertex_t *pfrontenter, *pfrontexit; - -static qboolean makeclippededge; - - -static void -R_EntityRotate (vec3_t vec) -{ - vec3_t tvec; - - VectorCopy (vec, tvec); - vec[0] = DotProduct (entity_rotation[0], tvec); - vec[1] = DotProduct (entity_rotation[1], tvec); - vec[2] = DotProduct (entity_rotation[2], tvec); -} - - -void -sw32_R_RotateBmodel (void) -{ - VectorCopy (currententity->transform + 0, entity_rotation[0]); - VectorCopy (currententity->transform + 4, entity_rotation[1]); - VectorCopy (currententity->transform + 8, entity_rotation[2]); - - // rotate modelorg and the transformation matrix - R_EntityRotate (modelorg); - R_EntityRotate (vpn); - R_EntityRotate (vright); - R_EntityRotate (vup); - - sw32_R_TransformFrustum (); -} - - -static void -R_RecursiveClipBPoly (bedge_t *pedges, mnode_t *pnode, msurface_t *psurf) -{ - bedge_t *psideedges[2], *pnextedge, *ptedge; - int i, side, lastside; - float dist, frac, lastdist; - plane_t *splitplane, tplane; - mvertex_t *pvert, *plastvert, *ptvert; - mnode_t *pn; - - psideedges[0] = psideedges[1] = NULL; - - makeclippededge = false; - - // transform the BSP plane into model space - // FIXME: cache these? - splitplane = pnode->plane; - tplane.dist = splitplane->dist - - DotProduct (r_entorigin, splitplane->normal); - tplane.normal[0] = DotProduct (entity_rotation[0], splitplane->normal); - tplane.normal[1] = DotProduct (entity_rotation[1], splitplane->normal); - tplane.normal[2] = DotProduct (entity_rotation[2], splitplane->normal); - - // clip edges to BSP plane - for (; pedges; pedges = pnextedge) { - pnextedge = pedges->pnext; - - // set the status for the last point as the previous point - // FIXME: cache this stuff somehow? - plastvert = pedges->v[0]; - lastdist = DotProduct (plastvert->position, tplane.normal) - - tplane.dist; - - if (lastdist > 0) - lastside = 0; - else - lastside = 1; - - pvert = pedges->v[1]; - - dist = DotProduct (pvert->position, tplane.normal) - tplane.dist; - - if (dist > 0) - side = 0; - else - side = 1; - - if (side != lastside) { - // clipped - if (numbverts >= MAX_BMODEL_VERTS) - return; - - // generate the clipped vertex - frac = lastdist / (lastdist - dist); - ptvert = &pbverts[numbverts++]; - ptvert->position[0] = plastvert->position[0] + - frac * (pvert->position[0] - plastvert->position[0]); - ptvert->position[1] = plastvert->position[1] + - frac * (pvert->position[1] - plastvert->position[1]); - ptvert->position[2] = plastvert->position[2] + - frac * (pvert->position[2] - plastvert->position[2]); - - // split into two edges, one on each side, and remember entering - // and exiting points - // FIXME: share the clip edge by having a winding direction flag? - if (numbedges >= (MAX_BMODEL_EDGES - 1)) { - Sys_Printf ("Out of edges for bmodel\n"); - return; - } - - ptedge = &pbedges[numbedges]; - ptedge->pnext = psideedges[lastside]; - psideedges[lastside] = ptedge; - ptedge->v[0] = plastvert; - ptedge->v[1] = ptvert; - - ptedge = &pbedges[numbedges + 1]; - ptedge->pnext = psideedges[side]; - psideedges[side] = ptedge; - ptedge->v[0] = ptvert; - ptedge->v[1] = pvert; - - numbedges += 2; - - if (side == 0) { - // entering for front, exiting for back - pfrontenter = ptvert; - makeclippededge = true; - } else { - pfrontexit = ptvert; - makeclippededge = true; - } - } else { - // add the edge to the appropriate side - pedges->pnext = psideedges[side]; - psideedges[side] = pedges; - } - } - - // if anything was clipped, reconstitute and add the edges along the clip - // plane to both sides (but in opposite directions) - if (makeclippededge) { - if (numbedges >= (MAX_BMODEL_EDGES - 2)) { - Sys_Printf ("Out of edges for bmodel\n"); - return; - } - - ptedge = &pbedges[numbedges]; - ptedge->pnext = psideedges[0]; - psideedges[0] = ptedge; - ptedge->v[0] = pfrontexit; - ptedge->v[1] = pfrontenter; - - ptedge = &pbedges[numbedges + 1]; - ptedge->pnext = psideedges[1]; - psideedges[1] = ptedge; - ptedge->v[0] = pfrontenter; - ptedge->v[1] = pfrontexit; - - numbedges += 2; - } - // draw or recurse further - for (i = 0; i < 2; i++) { - if (psideedges[i]) { - // draw if we've reached a non-solid leaf, done if all that's left - // is a solid leaf, and continue down the tree if it's not a leaf - pn = pnode->children[i]; - - // we're done with this branch if the node or leaf isn't in the PVS - if (pn->visframe == r_visframecount) { - if (pn->contents < 0) { - if (pn->contents != CONTENTS_SOLID) { - sw32_r_currentbkey = ((mleaf_t *) pn)->key; - sw32_R_RenderBmodelFace (psideedges[i], psurf); - } - } else { - R_RecursiveClipBPoly (psideedges[i], pnode->children[i], - psurf); - } - } - } - } -} - - -void -sw32_R_DrawSolidClippedSubmodelPolygons (model_t *pmodel) -{ - int i, j, lindex; - vec_t dot; - msurface_t *psurf; - int numsurfaces; - plane_t *pplane; - mvertex_t bverts[MAX_BMODEL_VERTS]; - bedge_t bedges[MAX_BMODEL_EDGES], *pbedge; - medge_t *pedge, *pedges; - - // FIXME: use bounding-box-based frustum clipping info? - - psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; - numsurfaces = pmodel->nummodelsurfaces; - pedges = pmodel->edges; - - for (i = 0; i < numsurfaces; i++, psurf++) { - // find which side of the node we are on - pplane = psurf->plane; - - dot = DotProduct (modelorg, pplane->normal) - pplane->dist; - - // draw the polygon - if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || - (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { - // FIXME: use bounding-box-based frustum clipping info? - - // copy the edges to bedges, flipping if necessary so always - // clockwise winding - // FIXME: if edges and vertices get caches, these assignments must - // move outside the loop, and overflow checking must be done here - pbverts = bverts; - pbedges = bedges; - numbverts = numbedges = 0; - - if (psurf->numedges > 0) { - pbedge = &bedges[numbedges]; - numbedges += psurf->numedges; - - for (j = 0; j < psurf->numedges; j++) { - lindex = pmodel->surfedges[psurf->firstedge + j]; - - if (lindex > 0) { - pedge = &pedges[lindex]; - pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[0]]; - pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[1]]; - } else { - lindex = -lindex; - pedge = &pedges[lindex]; - pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[1]]; - pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[0]]; - } - - pbedge[j].pnext = &pbedge[j + 1]; - } - - pbedge[j - 1].pnext = NULL; // mark end of edges - - R_RecursiveClipBPoly (pbedge, currententity->topnode, psurf); - } else { - Sys_Error ("no edges in bmodel"); - } - } - } -} - - -void -sw32_R_DrawSubmodelPolygons (model_t *pmodel, int clipflags) -{ - int i; - vec_t dot; - msurface_t *psurf; - int numsurfaces; - plane_t *pplane; - - // FIXME: use bounding-box-based frustum clipping info? - - psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; - numsurfaces = pmodel->nummodelsurfaces; - - for (i = 0; i < numsurfaces; i++, psurf++) { - // find which side of the node we are on - pplane = psurf->plane; - - dot = DotProduct (modelorg, pplane->normal) - pplane->dist; - - // draw the polygon - if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || - (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { - sw32_r_currentkey = ((mleaf_t *) currententity->topnode)->key; - - // FIXME: use bounding-box-based frustum clipping info? - sw32_R_RenderFace (psurf, clipflags); - } - } -} - -static inline void -visit_leaf (mleaf_t *leaf) -{ - // deal with model fragments in this leaf - if (leaf->efrags) - R_StoreEfrags (leaf->efrags); - leaf->key = sw32_r_currentkey; - sw32_r_currentkey++; // all bmodels in a leaf share the same key -} - -static inline int -get_side (mnode_t *node) -{ - // find which side of the node we are on - plane_t *plane = node->plane; - - if (plane->type < 3) - return (modelorg[plane->type] - plane->dist) < 0; - return (DotProduct (modelorg, plane->normal) - plane->dist) < 0; -} - -static void -visit_node (mnode_t *node, int side, int clipflags) -{ - int c; - msurface_t *surf; - - // sneaky hack for side = side ? SURF_PLANEBACK : 0; - side = (~side + 1) & SURF_PLANEBACK; - // draw stuff - if ((c = node->numsurfaces)) { - surf = r_worldentity.model->surfaces + node->firstsurface; - for (; c; c--, surf++) { - if (surf->visframe != r_visframecount) - continue; - - // side is either 0 or SURF_PLANEBACK - if (side ^ (surf->flags & SURF_PLANEBACK)) - continue; // wrong side - - if (sw32_r_drawpolys) { - if (sw32_r_worldpolysbacktofront) { - if (sw32_numbtofpolys < MAX_BTOFPOLYS) { - pbtofpolys[sw32_numbtofpolys].clipflags = clipflags; - pbtofpolys[sw32_numbtofpolys].psurf = surf; - sw32_numbtofpolys++; - } - } else { - sw32_R_RenderPoly (surf, clipflags); - } - } else { - sw32_R_RenderFace (surf, clipflags); - } - } - // all surfaces on the same node share the same sequence number - sw32_r_currentkey++; - } -} - -static inline int -test_node (mnode_t *node, int *clipflags) -{ - int i, *pindex; - vec3_t acceptpt, rejectpt; - double d; - - if (node->contents < 0) - return 0; - if (node->visframe != r_visframecount) - return 0; - // cull the clipping planes if not trivial accept - // FIXME: the compiler is doing a lousy job of optimizing here; it could be - // twice as fast in ASM - if (*clipflags) { - for (i = 0; i < 4; i++) { - if (!(*clipflags & (1 << i))) - continue; // don't need to clip against it - - // generate accept and reject points - // FIXME: do with fast look-ups or integer tests based on the - // sign bit of the floating point values - - pindex = sw32_pfrustum_indexes[i]; - - rejectpt[0] = (float) node->minmaxs[pindex[0]]; - rejectpt[1] = (float) node->minmaxs[pindex[1]]; - rejectpt[2] = (float) node->minmaxs[pindex[2]]; - - d = DotProduct (rejectpt, sw32_view_clipplanes[i].normal); - d -= sw32_view_clipplanes[i].dist; - - if (d <= 0) - return 0; - - acceptpt[0] = (float) node->minmaxs[pindex[3 + 0]]; - acceptpt[1] = (float) node->minmaxs[pindex[3 + 1]]; - acceptpt[2] = (float) node->minmaxs[pindex[3 + 2]]; - - d = DotProduct (acceptpt, sw32_view_clipplanes[i].normal); - d -= sw32_view_clipplanes[i].dist; - if (d >= 0) - *clipflags &= ~(1 << i); // node is entirely on screen - } - } - return 1; -} - -static void -R_VisitWorldNodes (model_t *model, int clipflags) -{ - typedef struct { - mnode_t *node; - int side, clipflags; - } rstack_t; - rstack_t *node_ptr; - rstack_t *node_stack; - mnode_t *node; - mnode_t *front; - int side, cf; - - node = model->nodes; - // +2 for paranoia - node_stack = alloca ((model->depth + 2) * sizeof (rstack_t)); - node_ptr = node_stack; - - cf = clipflags; - while (1) { - while (test_node (node, &cf)) { - cf = clipflags; - side = get_side (node); - front = node->children[side]; - if (test_node (front, &cf)) { - node_ptr->node = node; - node_ptr->side = side; - node_ptr->clipflags = clipflags; - node_ptr++; - clipflags = cf; - node = front; - continue; - } - if (front->contents < 0 && front->contents != CONTENTS_SOLID) - visit_leaf ((mleaf_t *) front); - visit_node (node, side, clipflags); - node = node->children[!side]; - } - if (node->contents < 0 && node->contents != CONTENTS_SOLID) - visit_leaf ((mleaf_t *) node); - if (node_ptr != node_stack) { - node_ptr--; - node = node_ptr->node; - side = node_ptr->side; - clipflags = node_ptr->clipflags; - visit_node (node, side, clipflags); - node = node->children[!side]; - continue; - } - break; - } - if (node->contents < 0 && node->contents != CONTENTS_SOLID) - visit_leaf ((mleaf_t *) node); -} - -void -sw32_R_RenderWorld (void) -{ - int i; - model_t *clmodel; - btofpoly_t btofpolys[MAX_BTOFPOLYS]; - - pbtofpolys = btofpolys; - - currententity = &r_worldentity; - VectorCopy (r_origin, modelorg); - clmodel = currententity->model; - r_pcurrentvertbase = clmodel->vertexes; - - R_VisitWorldNodes (clmodel, 15); - - // if the driver wants the polygons back to front, play the visible ones - // back in that order - if (sw32_r_worldpolysbacktofront) { - for (i = sw32_numbtofpolys - 1; i >= 0; i--) { - sw32_R_RenderPoly (btofpolys[i].psurf, btofpolys[i].clipflags); - } - } -} diff --git a/libs/video/renderer/sw32/sw32_rdraw.c b/libs/video/renderer/sw32/sw32_rdraw.c deleted file mode 100644 index 6624917a7..000000000 --- a/libs/video/renderer/sw32/sw32_rdraw.c +++ /dev/null @@ -1,801 +0,0 @@ -/* - sw32_r_draw.c - - (description) - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include "QF/render.h" - -#include "compat.h" -#include "r_internal.h" - -#define MAXLEFTCLIPEDGES 100 - -// !!! if these are changed, they must be changed in asm_draw.h too !!! -#define FULLY_CLIPPED_CACHED 0x80000000 -#define FRAMECOUNT_MASK 0x7FFFFFFF - -static unsigned int cacheoffset; - -int sw32_c_faceclip; // number of faces clipped - -zpointdesc_t sw32_r_zpointdesc; - -static polydesc_t r_polydesc; - -clipplane_t sw32_view_clipplanes[4]; - -medge_t *sw32_r_pedge; - -qboolean sw32_r_leftclipped, sw32_r_rightclipped; -static qboolean makeleftedge, makerightedge; -qboolean sw32_r_nearzionly; - -int sw32_sintable[SIN_BUFFER_SIZE]; -int sw32_intsintable[SIN_BUFFER_SIZE]; - -mvertex_t sw32_r_leftenter, sw32_r_leftexit; -mvertex_t sw32_r_rightenter, sw32_r_rightexit; - -typedef struct { - float u, v; - int ceilv; -} evert_t; - -int sw32_r_emitted; -float sw32_r_nearzi; -float sw32_r_u1, sw32_r_v1, sw32_r_lzi1; -int sw32_r_ceilv1; - -qboolean sw32_r_lastvertvalid; - - -void -sw32_R_EmitEdge (mvertex_t *pv0, mvertex_t *pv1) -{ - edge_t *edge, *pcheck; - int u_check; - float u, u_step; - vec3_t local, transformed; - float *world; - int v, v2, ceilv0; - float scale, lzi0, u0, v0; - int side; - - if (sw32_r_lastvertvalid) { - u0 = sw32_r_u1; - v0 = sw32_r_v1; - lzi0 = sw32_r_lzi1; - ceilv0 = sw32_r_ceilv1; - } else { - world = &pv0->position[0]; - - // transform and project - VectorSubtract (world, modelorg, local); - sw32_TransformVector (local, transformed); - - if (transformed[2] < NEAR_CLIP) - transformed[2] = NEAR_CLIP; - - lzi0 = 1.0 / transformed[2]; - - // FIXME: build x/sw32_yscale into transform? - scale = sw32_xscale * lzi0; - u0 = (sw32_xcenter + scale * transformed[0]); - if (u0 < r_refdef.fvrectx_adj) - u0 = r_refdef.fvrectx_adj; - if (u0 > r_refdef.fvrectright_adj) - u0 = r_refdef.fvrectright_adj; - - scale = sw32_yscale * lzi0; - v0 = (sw32_ycenter - scale * transformed[1]); - if (v0 < r_refdef.fvrecty_adj) - v0 = r_refdef.fvrecty_adj; - if (v0 > r_refdef.fvrectbottom_adj) - v0 = r_refdef.fvrectbottom_adj; - - ceilv0 = (int) ceil (v0); - } - - world = &pv1->position[0]; - - // transform and project - VectorSubtract (world, modelorg, local); - sw32_TransformVector (local, transformed); - - if (transformed[2] < NEAR_CLIP) - transformed[2] = NEAR_CLIP; - - sw32_r_lzi1 = 1.0 / transformed[2]; - - scale = sw32_xscale * sw32_r_lzi1; - sw32_r_u1 = (sw32_xcenter + scale * transformed[0]); - if (sw32_r_u1 < r_refdef.fvrectx_adj) - sw32_r_u1 = r_refdef.fvrectx_adj; - if (sw32_r_u1 > r_refdef.fvrectright_adj) - sw32_r_u1 = r_refdef.fvrectright_adj; - - scale = sw32_yscale * sw32_r_lzi1; - sw32_r_v1 = (sw32_ycenter - scale * transformed[1]); - if (sw32_r_v1 < r_refdef.fvrecty_adj) - sw32_r_v1 = r_refdef.fvrecty_adj; - if (sw32_r_v1 > r_refdef.fvrectbottom_adj) - sw32_r_v1 = r_refdef.fvrectbottom_adj; - - if (sw32_r_lzi1 > lzi0) - lzi0 = sw32_r_lzi1; - - if (lzi0 > sw32_r_nearzi) // for mipmap finding - sw32_r_nearzi = lzi0; - - // for right edges, all we want is the effect on 1/z - if (sw32_r_nearzionly) - return; - - sw32_r_emitted = 1; - - sw32_r_ceilv1 = (int) ceil (sw32_r_v1); - - // create the edge - if (ceilv0 == sw32_r_ceilv1) { - // we cache unclipped horizontal edges as fully clipped - if (cacheoffset != 0x7FFFFFFF) { - cacheoffset = FULLY_CLIPPED_CACHED | - (r_framecount & FRAMECOUNT_MASK); - } - - return; // horizontal edge - } - - side = ceilv0 > sw32_r_ceilv1; - - edge = sw32_edge_p++; - - edge->owner = sw32_r_pedge; - - edge->nearzi = lzi0; - - if (side == 0) { - // trailing edge (go from p1 to p2) - v = ceilv0; - v2 = sw32_r_ceilv1 - 1; - - edge->surfs[0] = sw32_surface_p - sw32_surfaces; - edge->surfs[1] = 0; - - u_step = ((sw32_r_u1 - u0) / (sw32_r_v1 - v0)); - u = u0 + ((float) v - v0) * u_step; - } else { - // leading edge (go from p2 to p1) - v2 = ceilv0 - 1; - v = sw32_r_ceilv1; - - edge->surfs[0] = 0; - edge->surfs[1] = sw32_surface_p - sw32_surfaces; - - u_step = ((u0 - sw32_r_u1) / (v0 - sw32_r_v1)); - u = sw32_r_u1 + ((float) v - sw32_r_v1) * u_step; - } - - edge->u_step = u_step * 0x100000; - edge->u = u * 0x100000 + 0xFFFFF; - - // we need to do this to avoid stepping off the edges if a very nearly - // horizontal edge is less than epsilon above a scan, and numeric error - // causes it to incorrectly extend to the scan, and the extension of the - // line goes off the edge of the screen - // FIXME: is this actually needed? - if (edge->u < r_refdef.vrect_x_adj_shift20) - edge->u = r_refdef.vrect_x_adj_shift20; - if (edge->u > r_refdef.vrectright_adj_shift20) - edge->u = r_refdef.vrectright_adj_shift20; - - // sort the edge in normally - u_check = edge->u; - if (edge->surfs[0]) - u_check++; // sort trailers after leaders - - if (!sw32_newedges[v] || sw32_newedges[v]->u >= u_check) { - edge->next = sw32_newedges[v]; - sw32_newedges[v] = edge; - } else { - pcheck = sw32_newedges[v]; - while (pcheck->next && pcheck->next->u < u_check) - pcheck = pcheck->next; - edge->next = pcheck->next; - pcheck->next = edge; - } - - edge->nextremove = sw32_removeedges[v2]; - sw32_removeedges[v2] = edge; -} - - -void -sw32_R_ClipEdge (mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip) -{ - float d0, d1, f; - mvertex_t clipvert; - - if (clip) { - do { - d0 = DotProduct (pv0->position, clip->normal) - clip->dist; - d1 = DotProduct (pv1->position, clip->normal) - clip->dist; - - if (d0 >= 0) { - // point 0 is unclipped - if (d1 >= 0) { - // both points are unclipped - continue; - } - // only point 1 is clipped - - // we don't cache clipped edges - cacheoffset = 0x7FFFFFFF; - - f = d0 / (d0 - d1); - clipvert.position[0] = pv0->position[0] + - f * (pv1->position[0] - pv0->position[0]); - clipvert.position[1] = pv0->position[1] + - f * (pv1->position[1] - pv0->position[1]); - clipvert.position[2] = pv0->position[2] + - f * (pv1->position[2] - pv0->position[2]); - - if (clip->leftedge) { - sw32_r_leftclipped = true; - sw32_r_leftexit = clipvert; - } else if (clip->rightedge) { - sw32_r_rightclipped = true; - sw32_r_rightexit = clipvert; - } - - sw32_R_ClipEdge (pv0, &clipvert, clip->next); - return; - } else { - // point 0 is clipped - if (d1 < 0) { - // both points are clipped - // we do cache fully clipped edges - if (!sw32_r_leftclipped) - cacheoffset = FULLY_CLIPPED_CACHED | - (r_framecount & FRAMECOUNT_MASK); - return; - } - // only point 0 is clipped - sw32_r_lastvertvalid = false; - - // we don't cache partially clipped edges - cacheoffset = 0x7FFFFFFF; - - f = d0 / (d0 - d1); - clipvert.position[0] = pv0->position[0] + - f * (pv1->position[0] - pv0->position[0]); - clipvert.position[1] = pv0->position[1] + - f * (pv1->position[1] - pv0->position[1]); - clipvert.position[2] = pv0->position[2] + - f * (pv1->position[2] - pv0->position[2]); - - if (clip->leftedge) { - sw32_r_leftclipped = true; - sw32_r_leftenter = clipvert; - } else if (clip->rightedge) { - sw32_r_rightclipped = true; - sw32_r_rightenter = clipvert; - } - - sw32_R_ClipEdge (&clipvert, pv1, clip->next); - return; - } - } while ((clip = clip->next) != NULL); - } - // add the edge - sw32_R_EmitEdge (pv0, pv1); -} - - -static void -R_EmitCachedEdge (void) -{ - edge_t *pedge_t; - - pedge_t = (edge_t *) ((intptr_t) sw32_r_edges + sw32_r_pedge->cachededgeoffset); - - if (!pedge_t->surfs[0]) - pedge_t->surfs[0] = sw32_surface_p - sw32_surfaces; - else - pedge_t->surfs[1] = sw32_surface_p - sw32_surfaces; - - if (pedge_t->nearzi > sw32_r_nearzi) // for mipmap finding - sw32_r_nearzi = pedge_t->nearzi; - - sw32_r_emitted = 1; -} - - -void -sw32_R_RenderFace (msurface_t *fa, int clipflags) -{ - int i, lindex; - unsigned int mask; - plane_t *pplane; - float distinv; - vec3_t p_normal; - medge_t *pedges, tedge; - clipplane_t *pclip; - - // skip out if no more surfs - if ((sw32_surface_p) >= sw32_surf_max) { - sw32_r_outofsurfaces++; - return; - } - // ditto if not enough edges left, or switch to auxedges if possible - if ((sw32_edge_p + fa->numedges + 4) >= sw32_edge_max) { - sw32_r_outofedges += fa->numedges; - return; - } - - sw32_c_faceclip++; - - // set up clip planes - pclip = NULL; - - for (i = 3, mask = 0x08; i >= 0; i--, mask >>= 1) { - if (clipflags & mask) { - sw32_view_clipplanes[i].next = pclip; - pclip = &sw32_view_clipplanes[i]; - } - } - - // push the edges through - sw32_r_emitted = 0; - sw32_r_nearzi = 0; - sw32_r_nearzionly = false; - makeleftedge = makerightedge = false; - pedges = currententity->model->edges; - sw32_r_lastvertvalid = false; - - for (i = 0; i < fa->numedges; i++) { - lindex = currententity->model->surfedges[fa->firstedge + i]; - - if (lindex > 0) { - sw32_r_pedge = &pedges[lindex]; - - // if the edge is cached, we can just reuse the edge - if (!insubmodel) { - if (sw32_r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED) { - if ((sw32_r_pedge->cachededgeoffset & FRAMECOUNT_MASK) == - (unsigned int) r_framecount) { - sw32_r_lastvertvalid = false; - continue; - } - } else { - if ((((uintptr_t) sw32_edge_p - (uintptr_t) sw32_r_edges) > - sw32_r_pedge->cachededgeoffset) && - (((edge_t *) ((intptr_t) sw32_r_edges + - sw32_r_pedge->cachededgeoffset))->owner == - sw32_r_pedge)) { - R_EmitCachedEdge (); - sw32_r_lastvertvalid = false; - continue; - } - } - } - // assume it's cacheable - cacheoffset = (byte *) sw32_edge_p - (byte *) sw32_r_edges; - sw32_r_leftclipped = sw32_r_rightclipped = false; - sw32_R_ClipEdge (&r_pcurrentvertbase[sw32_r_pedge->v[0]], - &r_pcurrentvertbase[sw32_r_pedge->v[1]], pclip); - sw32_r_pedge->cachededgeoffset = cacheoffset; - - if (sw32_r_leftclipped) - makeleftedge = true; - if (sw32_r_rightclipped) - makerightedge = true; - sw32_r_lastvertvalid = true; - } else { - lindex = -lindex; - sw32_r_pedge = &pedges[lindex]; - // if the edge is cached, we can just reuse the edge - if (!insubmodel) { - if (sw32_r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED) { - if ((sw32_r_pedge->cachededgeoffset & FRAMECOUNT_MASK) == - (unsigned int) r_framecount) { - sw32_r_lastvertvalid = false; - continue; - } - } else { - // it's cached if the cached edge is valid and is owned - // by this medge_t - if ((((uintptr_t) sw32_edge_p - (uintptr_t) sw32_r_edges) > - sw32_r_pedge->cachededgeoffset) && - (((edge_t *) ((intptr_t) sw32_r_edges + - sw32_r_pedge->cachededgeoffset))->owner == - sw32_r_pedge)) { - R_EmitCachedEdge (); - sw32_r_lastvertvalid = false; - continue; - } - } - } - // assume it's cacheable - cacheoffset = (byte *) sw32_edge_p - (byte *) sw32_r_edges; - sw32_r_leftclipped = sw32_r_rightclipped = false; - sw32_R_ClipEdge (&r_pcurrentvertbase[sw32_r_pedge->v[1]], - &r_pcurrentvertbase[sw32_r_pedge->v[0]], pclip); - sw32_r_pedge->cachededgeoffset = cacheoffset; - - if (sw32_r_leftclipped) - makeleftedge = true; - if (sw32_r_rightclipped) - makerightedge = true; - sw32_r_lastvertvalid = true; - } - } - - // if there was a clip off the left edge, add that edge too - // FIXME: faster to do in screen space? - // FIXME: share clipped edges? - if (makeleftedge) { - sw32_r_pedge = &tedge; - sw32_r_lastvertvalid = false; - sw32_R_ClipEdge (&sw32_r_leftexit, &sw32_r_leftenter, pclip->next); - } - // if there was a clip off the right edge, get the right sw32_r_nearzi - if (makerightedge) { - sw32_r_pedge = &tedge; - sw32_r_lastvertvalid = false; - sw32_r_nearzionly = true; - sw32_R_ClipEdge (&sw32_r_rightexit, &sw32_r_rightenter, sw32_view_clipplanes[1].next); - } - // if no edges made it out, return without posting the surface - if (!sw32_r_emitted) - return; - - sw32_r_polycount++; - - sw32_surface_p->data = (void *) fa; - sw32_surface_p->nearzi = sw32_r_nearzi; - sw32_surface_p->flags = fa->flags; - sw32_surface_p->insubmodel = insubmodel; - sw32_surface_p->spanstate = 0; - sw32_surface_p->entity = currententity; - sw32_surface_p->key = sw32_r_currentkey++; - sw32_surface_p->spans = NULL; - - pplane = fa->plane; -// FIXME: cache this? - sw32_TransformVector (pplane->normal, p_normal); -// FIXME: cache this? - distinv = 1.0 / (pplane->dist - DotProduct (modelorg, pplane->normal)); - distinv = min (distinv, 1.0); - - sw32_surface_p->d_zistepu = p_normal[0] * sw32_xscaleinv * distinv; - sw32_surface_p->d_zistepv = -p_normal[1] * sw32_yscaleinv * distinv; - sw32_surface_p->d_ziorigin = p_normal[2] * distinv - - sw32_xcenter * sw32_surface_p->d_zistepu - sw32_ycenter * sw32_surface_p->d_zistepv; - -//JDC VectorCopy (r_worldmodelorg, sw32_surface_p->modelorg); - sw32_surface_p++; -} - - -void -sw32_R_RenderBmodelFace (bedge_t *pedges, msurface_t *psurf) -{ - int i; - unsigned int mask; - plane_t *pplane; - float distinv; - vec3_t p_normal; - medge_t tedge; - clipplane_t *pclip; - - // skip out if no more surfs - if (sw32_surface_p >= sw32_surf_max) { - sw32_r_outofsurfaces++; - return; - } - // ditto if not enough edges left, or switch to auxedges if possible - if ((sw32_edge_p + psurf->numedges + 4) >= sw32_edge_max) { - sw32_r_outofedges += psurf->numedges; - return; - } - - sw32_c_faceclip++; - - // this is a dummy to give the caching mechanism someplace to write to - sw32_r_pedge = &tedge; - - // set up clip planes - pclip = NULL; - - for (i = 3, mask = 0x08; i >= 0; i--, mask >>= 1) { - if (sw32_r_clipflags & mask) { - sw32_view_clipplanes[i].next = pclip; - pclip = &sw32_view_clipplanes[i]; - } - } - - // push the edges through - sw32_r_emitted = 0; - sw32_r_nearzi = 0; - sw32_r_nearzionly = false; - makeleftedge = makerightedge = false; - // FIXME: keep clipped bmodel edges in clockwise order so last vertex - // caching can be used? - sw32_r_lastvertvalid = false; - - for (; pedges; pedges = pedges->pnext) { - sw32_r_leftclipped = sw32_r_rightclipped = false; - sw32_R_ClipEdge (pedges->v[0], pedges->v[1], pclip); - - if (sw32_r_leftclipped) - makeleftedge = true; - if (sw32_r_rightclipped) - makerightedge = true; - } - - // if there was a clip off the left edge, add that edge too - // FIXME: faster to do in screen space? - // FIXME: share clipped edges? - if (makeleftedge) { - sw32_r_pedge = &tedge; - sw32_R_ClipEdge (&sw32_r_leftexit, &sw32_r_leftenter, pclip->next); - } - // if there was a clip off the right edge, get the right sw32_r_nearzi - if (makerightedge) { - sw32_r_pedge = &tedge; - sw32_r_nearzionly = true; - sw32_R_ClipEdge (&sw32_r_rightexit, &sw32_r_rightenter, sw32_view_clipplanes[1].next); - } - // if no edges made it out, return without posting the surface - if (!sw32_r_emitted) - return; - - sw32_r_polycount++; - - sw32_surface_p->data = (void *) psurf; - sw32_surface_p->nearzi = sw32_r_nearzi; - sw32_surface_p->flags = psurf->flags; - sw32_surface_p->insubmodel = true; - sw32_surface_p->spanstate = 0; - sw32_surface_p->entity = currententity; - sw32_surface_p->key = sw32_r_currentbkey; - sw32_surface_p->spans = NULL; - - pplane = psurf->plane; -// FIXME: cache this? - sw32_TransformVector (pplane->normal, p_normal); -// FIXME: cache this? - distinv = 1.0 / (pplane->dist - DotProduct (modelorg, pplane->normal)); - distinv = min (distinv, 1.0); - - sw32_surface_p->d_zistepu = p_normal[0] * sw32_xscaleinv * distinv; - sw32_surface_p->d_zistepv = -p_normal[1] * sw32_yscaleinv * distinv; - sw32_surface_p->d_ziorigin = p_normal[2] * distinv - - sw32_xcenter * sw32_surface_p->d_zistepu - sw32_ycenter * sw32_surface_p->d_zistepv; - -//JDC VectorCopy (r_worldmodelorg, sw32_surface_p->modelorg); - sw32_surface_p++; -} - - -void -sw32_R_RenderPoly (msurface_t *fa, int clipflags) -{ - int i, lindex, lnumverts, s_axis, t_axis; - float dist, lastdist, lzi, scale, u, v, frac; - unsigned int mask; - vec3_t local, transformed; - clipplane_t *pclip; - medge_t *pedges; - plane_t *pplane; - mvertex_t verts[2][100]; // FIXME: do real number - polyvert_t pverts[100]; // FIXME: do real number, safely - int vertpage, newverts, newpage, lastvert; - qboolean visible; - - // FIXME: clean this up and make it faster - // FIXME: guard against running out of vertices - - s_axis = t_axis = 0; // keep compiler happy - - // set up clip planes - pclip = NULL; - - for (i = 3, mask = 0x08; i >= 0; i--, mask >>= 1) { - if (clipflags & mask) { - sw32_view_clipplanes[i].next = pclip; - pclip = &sw32_view_clipplanes[i]; - } - } - - // reconstruct the polygon - // FIXME: these should be precalculated and loaded off disk - pedges = currententity->model->edges; - lnumverts = fa->numedges; - vertpage = 0; - - for (i = 0; i < lnumverts; i++) { - lindex = currententity->model->surfedges[fa->firstedge + i]; - - if (lindex > 0) { - sw32_r_pedge = &pedges[lindex]; - verts[0][i] = r_pcurrentvertbase[sw32_r_pedge->v[0]]; - } else { - sw32_r_pedge = &pedges[-lindex]; - verts[0][i] = r_pcurrentvertbase[sw32_r_pedge->v[1]]; - } - } - - // clip the polygon, done if not visible - while (pclip) { - lastvert = lnumverts - 1; - lastdist = DotProduct (verts[vertpage][lastvert].position, - pclip->normal) - pclip->dist; - - visible = false; - newverts = 0; - newpage = vertpage ^ 1; - - for (i = 0; i < lnumverts; i++) { - dist = DotProduct (verts[vertpage][i].position, pclip->normal) - - pclip->dist; - - if ((lastdist > 0) != (dist > 0)) { - frac = dist / (dist - lastdist); - verts[newpage][newverts].position[0] = - verts[vertpage][i].position[0] + - ((verts[vertpage][lastvert].position[0] - - verts[vertpage][i].position[0]) * frac); - verts[newpage][newverts].position[1] = - verts[vertpage][i].position[1] + - ((verts[vertpage][lastvert].position[1] - - verts[vertpage][i].position[1]) * frac); - verts[newpage][newverts].position[2] = - verts[vertpage][i].position[2] + - ((verts[vertpage][lastvert].position[2] - - verts[vertpage][i].position[2]) * frac); - newverts++; - } - - if (dist >= 0) { - verts[newpage][newverts] = verts[vertpage][i]; - newverts++; - visible = true; - } - - lastvert = i; - lastdist = dist; - } - - if (!visible || (newverts < 3)) - return; - - lnumverts = newverts; - vertpage ^= 1; - pclip = pclip->next; - } - - // transform and project, remembering the z values at the vertices and - // sw32_r_nearzi, and extract the s and t coordinates at the vertices - pplane = fa->plane; - switch (pplane->type) { - case PLANE_X: - case PLANE_ANYX: - s_axis = 1; - t_axis = 2; - break; - case PLANE_Y: - case PLANE_ANYY: - s_axis = 0; - t_axis = 2; - break; - case PLANE_Z: - case PLANE_ANYZ: - s_axis = 0; - t_axis = 1; - break; - } - - sw32_r_nearzi = 0; - - for (i = 0; i < lnumverts; i++) { - // transform and project - VectorSubtract (verts[vertpage][i].position, modelorg, local); - sw32_TransformVector (local, transformed); - - if (transformed[2] < NEAR_CLIP) - transformed[2] = NEAR_CLIP; - - lzi = 1.0 / transformed[2]; - - if (lzi > sw32_r_nearzi) // for mipmap finding - sw32_r_nearzi = lzi; - - // FIXME: build x/sw32_yscale into transform? - scale = sw32_xscale * lzi; - u = (sw32_xcenter + scale * transformed[0]); - if (u < r_refdef.fvrectx_adj) - u = r_refdef.fvrectx_adj; - if (u > r_refdef.fvrectright_adj) - u = r_refdef.fvrectright_adj; - - scale = sw32_yscale * lzi; - v = (sw32_ycenter - scale * transformed[1]); - if (v < r_refdef.fvrecty_adj) - v = r_refdef.fvrecty_adj; - if (v > r_refdef.fvrectbottom_adj) - v = r_refdef.fvrectbottom_adj; - - pverts[i].u = u; - pverts[i].v = v; - pverts[i].zi = lzi; - pverts[i].s = verts[vertpage][i].position[s_axis]; - pverts[i].t = verts[vertpage][i].position[t_axis]; - } - - // build the polygon descriptor, including fa, sw32_r_nearzi, and u, v, s, t, - // and z for each vertex - r_polydesc.numverts = lnumverts; - r_polydesc.nearzi = sw32_r_nearzi; - r_polydesc.pcurrentface = fa; - r_polydesc.pverts = pverts; - - // draw the polygon - sw32_D_DrawPoly (); -} - - -void -sw32_R_ZDrawSubmodelPolys (model_t *pmodel) -{ - int i, numsurfaces; - msurface_t *psurf; - float dot; - plane_t *pplane; - - psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; - numsurfaces = pmodel->nummodelsurfaces; - - for (i = 0; i < numsurfaces; i++, psurf++) { - // find which side of the node we are on - pplane = psurf->plane; - - dot = DotProduct (modelorg, pplane->normal) - pplane->dist; - - // draw the polygon - if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || - (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { - // FIXME: use bounding-box-based frustum clipping info? - sw32_R_RenderPoly (psurf, 15); - } - } -} diff --git a/libs/video/renderer/sw32/sw32_redge.c b/libs/video/renderer/sw32/sw32_redge.c deleted file mode 100644 index 91765fd5b..000000000 --- a/libs/video/renderer/sw32/sw32_redge.c +++ /dev/null @@ -1,546 +0,0 @@ -/* - sw32_redge.c - - (description) - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include "QF/render.h" -#include "QF/sound.h" - -#include "d_ifacea.h" -#include "r_internal.h" -#include "vid_internal.h" - -/* - FIXME - the complex cases add new polys on most lines, so dont optimize for - keeping them the same have multiple free span lists to try to get better - coherence ? low depth complexity-- 1 to 3 or so this breaks spans at every - edge, even hidden ones (bad) - - have a sentinal at both ends? -*/ - -edge_t *sw32_auxedges; -edge_t *sw32_r_edges, *sw32_edge_p, *sw32_edge_max; - -surf_t *sw32_surfaces, *sw32_surface_p, *sw32_surf_max; - -/* - surfaces are generated in back to front order by the bsp, so if a surf - pointer is greater than another one, it should be drawn in front - sw32_surfaces[1] is the background, and is used as the active surface stack -*/ - -edge_t *sw32_newedges[MAXHEIGHT]; -edge_t *sw32_removeedges[MAXHEIGHT]; - -static espan_t *span_p, *max_span_p; - -int sw32_r_currentkey; - - -static int current_iv; - -static int edge_head_u_shift20, edge_tail_u_shift20; - -static void (*pdrawfunc) (void); - -static edge_t edge_head; -static edge_t edge_tail; -static edge_t edge_aftertail; -static edge_t edge_sentinel; - -static float fv; - -static void -R_DrawCulledPolys (void) -{ - surf_t *s; - msurface_t *pface; - - currententity = &r_worldentity; - - if (sw32_r_worldpolysbacktofront) { - for (s = sw32_surface_p - 1; s > &sw32_surfaces[1]; s--) { - if (!s->spans) - continue; - - if (!(s->flags & SURF_DRAWBACKGROUND)) { - pface = (msurface_t *) s->data; - sw32_R_RenderPoly (pface, 15); - } - } - } else { - for (s = &sw32_surfaces[1]; s < sw32_surface_p; s++) { - if (!s->spans) - continue; - - if (!(s->flags & SURF_DRAWBACKGROUND)) { - pface = (msurface_t *) s->data; - sw32_R_RenderPoly (pface, 15); - } - } - } -} - -void -sw32_R_BeginEdgeFrame (void) -{ - int v; - - sw32_edge_p = sw32_r_edges; - sw32_edge_max = &sw32_r_edges[sw32_r_numallocatededges]; - - sw32_surface_p = &sw32_surfaces[2]; // background is surface 1, - // surface 0 is a dummy - sw32_surfaces[1].spans = NULL; // no background spans yet - sw32_surfaces[1].flags = SURF_DRAWBACKGROUND; - - // put the background behind everything in the world - pdrawfunc = sw32_R_GenerateSpans; - sw32_surfaces[1].key = 0x7FFFFFFF; - sw32_r_currentkey = 0; - - // FIXME: set with memset - for (v = r_refdef.vrect.y; v < r_refdef.vrectbottom; v++) { - sw32_newedges[v] = sw32_removeedges[v] = NULL; - } -} - -/* - sw32_R_InsertNewEdges - - Adds the edges in the linked list edgestoadd, adding them to the edges - in the linked list edgelist. edgestoadd is assumed to be sorted on u, - and non-empty (this is actually sw32_newedges[v]). edgelist is assumed to - be sorted on u, with a sentinel at the end (actually, this is the - active edge table starting at edge_head.next). -*/ -void -sw32_R_InsertNewEdges (edge_t *edgestoadd, edge_t *edgelist) -{ - edge_t *next_edge; - - do { - next_edge = edgestoadd->next; - edgesearch: - if (edgelist->u >= edgestoadd->u) - goto addedge; - edgelist = edgelist->next; - if (edgelist->u >= edgestoadd->u) - goto addedge; - edgelist = edgelist->next; - if (edgelist->u >= edgestoadd->u) - goto addedge; - edgelist = edgelist->next; - if (edgelist->u >= edgestoadd->u) - goto addedge; - edgelist = edgelist->next; - goto edgesearch; - - // insert edgestoadd before edgelist - addedge: - edgestoadd->next = edgelist; - edgestoadd->prev = edgelist->prev; - edgelist->prev->next = edgestoadd; - edgelist->prev = edgestoadd; - } while ((edgestoadd = next_edge) != NULL); -} - -void -sw32_R_RemoveEdges (edge_t *pedge) -{ - - do { - pedge->next->prev = pedge->prev; - pedge->prev->next = pedge->next; - } while ((pedge = pedge->nextremove) != NULL); -} - -void -sw32_R_StepActiveU (edge_t *pedge) -{ - edge_t *pnext_edge, *pwedge; - - while (1) { - nextedge: - pedge->u += pedge->u_step; - if (pedge->u < pedge->prev->u) - goto pushback; - pedge = pedge->next; - - pedge->u += pedge->u_step; - if (pedge->u < pedge->prev->u) - goto pushback; - pedge = pedge->next; - - pedge->u += pedge->u_step; - if (pedge->u < pedge->prev->u) - goto pushback; - pedge = pedge->next; - - pedge->u += pedge->u_step; - if (pedge->u < pedge->prev->u) - goto pushback; - pedge = pedge->next; - - goto nextedge; - - pushback: - if (pedge == &edge_aftertail) - return; - - // push it back to keep it sorted - pnext_edge = pedge->next; - - // pull the edge out of the edge list - pedge->next->prev = pedge->prev; - pedge->prev->next = pedge->next; - - // find out where the edge goes in the edge list - pwedge = pedge->prev->prev; - - while (pwedge->u > pedge->u) { - pwedge = pwedge->prev; - } - - // put the edge back into the edge list - pedge->next = pwedge->next; - pedge->prev = pwedge; - pedge->next->prev = pedge; - pwedge->next = pedge; - - pedge = pnext_edge; - if (pedge == &edge_tail) - return; - } -} - -static void -R_CleanupSpan (void) -{ - surf_t *surf; - int iu; - espan_t *span; - - // now that we've reached the right edge of the screen, we're done with any - // unfinished surfaces, so emit a span for whatever's on top - surf = sw32_surfaces[1].next; - iu = edge_tail_u_shift20; - if (iu > surf->last_u) { - span = span_p++; - span->u = surf->last_u; - span->count = iu - span->u; - span->v = current_iv; - span->pnext = surf->spans; - surf->spans = span; - } - // reset spanstate for all surfaces in the surface stack - do { - surf->spanstate = 0; - surf = surf->next; - } while (surf != &sw32_surfaces[1]); -} - -static void -R_TrailingEdge (surf_t *surf, edge_t *edge) -{ - espan_t *span; - int iu; - - // don't generate a span if this is an inverted span, with the end edge - // preceding the start edge (that is, we haven't seen the start edge yet) - if (--surf->spanstate == 0) { - if (surf == sw32_surfaces[1].next) { - // emit a span (current top going away) - iu = edge->u >> 20; - if (iu > surf->last_u) { - span = span_p++; - span->u = surf->last_u; - span->count = iu - span->u; - span->v = current_iv; - span->pnext = surf->spans; - surf->spans = span; - } - // set last_u on the surface below - surf->next->last_u = iu; - } - - surf->prev->next = surf->next; - surf->next->prev = surf->prev; - } -} - -static void -R_LeadingEdge (edge_t *edge) -{ - espan_t *span; - surf_t *surf, *surf2; - int iu; - double fu, newzi, testzi, newzitop, newzibottom; - - if (edge->surfs[1]) { - // it's adding a new surface in, so find the correct place - surf = &sw32_surfaces[edge->surfs[1]]; - - // don't start a span if this is an inverted span, with the end edge - // preceding the start edge (that is, we've already seen the end edge) - if (++surf->spanstate == 1) { - surf2 = sw32_surfaces[1].next; - - if (surf->key < surf2->key) - goto newtop; - - // if it's two surfaces on the same plane, the one that's already - // active is in front, so keep going unless it's a bmodel - if (surf->insubmodel && (surf->key == surf2->key)) { - // must be two bmodels in the same leaf; sort on 1/z - fu = (float) (edge->u - 0xFFFFF) * (1.0 / 0x100000); - newzi = surf->d_ziorigin + fv * surf->d_zistepv + - fu * surf->d_zistepu; - newzibottom = newzi * 0.99; - - testzi = surf2->d_ziorigin + fv * surf2->d_zistepv + - fu * surf2->d_zistepu; - - if (newzibottom >= testzi) { - goto newtop; - } - - newzitop = newzi * 1.01; - if (newzitop >= testzi) { - if (surf->d_zistepu >= surf2->d_zistepu) { - goto newtop; - } - } - } - - continue_search: - - do { - surf2 = surf2->next; - } while (surf->key > surf2->key); - - if (surf->key == surf2->key) { - // if it's two surfaces on the same plane, the already active - // one is in front, so keep going unless it's a bmodel - if (!surf->insubmodel) - goto continue_search; - - // must be two bmodels in the same leaf; sort on 1/z - fu = (float) (edge->u - 0xFFFFF) * (1.0 / 0x100000); - newzi = surf->d_ziorigin + fv * surf->d_zistepv + - fu * surf->d_zistepu; - newzibottom = newzi * 0.99; - - testzi = surf2->d_ziorigin + fv * surf2->d_zistepv + - fu * surf2->d_zistepu; - - if (newzibottom >= testzi) { - goto gotposition; - } - - newzitop = newzi * 1.01; - if (newzitop >= testzi) { - if (surf->d_zistepu >= surf2->d_zistepu) { - goto gotposition; - } - } - - goto continue_search; - } - - goto gotposition; - - newtop: - // emit a span (obscures current top) - iu = edge->u >> 20; - - if (iu > surf2->last_u) { - span = span_p++; - span->u = surf2->last_u; - span->count = iu - span->u; - span->v = current_iv; - span->pnext = surf2->spans; - surf2->spans = span; - } - // set last_u on the new span - surf->last_u = iu; - - gotposition: - // insert before surf2 - surf->next = surf2; - surf->prev = surf2->prev; - surf2->prev->next = surf; - surf2->prev = surf; - } - } -} - -void -sw32_R_GenerateSpans (void) -{ - edge_t *edge; - surf_t *surf; - - // clear active surfaces to just the background surface - sw32_surfaces[1].next = sw32_surfaces[1].prev = &sw32_surfaces[1]; - sw32_surfaces[1].last_u = edge_head_u_shift20; - - // generate spans - for (edge = edge_head.next; edge != &edge_tail; edge = edge->next) { - if (edge->surfs[0]) { - // it has a left surface, so a surface is going away for this span - surf = &sw32_surfaces[edge->surfs[0]]; - - R_TrailingEdge (surf, edge); - - if (!edge->surfs[1]) - continue; - } - - R_LeadingEdge (edge); - } - - R_CleanupSpan (); -} - -/* - R_ScanEdges - - Input: - sw32_newedges[] array - this has links to edges, which have links to surfaces - - Output: - Each surface has a linked list of its visible spans -*/ -void -sw32_R_ScanEdges (void) -{ - int iv, bottom; - byte basespans[MAXSPANS * sizeof (espan_t) + CACHE_SIZE]; - espan_t *basespan_p; - surf_t *s; - - basespan_p = (espan_t *) - ((intptr_t) (basespans + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); - max_span_p = &basespan_p[MAXSPANS - r_refdef.vrect.width]; - - span_p = basespan_p; - - // clear active edges to just the background edges around the whole screen - // FIXME: most of this needs to be set up only once - edge_head.u = r_refdef.vrect.x << 20; - edge_head_u_shift20 = edge_head.u >> 20; - edge_head.u_step = 0; - edge_head.prev = NULL; - edge_head.next = &edge_tail; - edge_head.surfs[0] = 0; - edge_head.surfs[1] = 1; - - edge_tail.u = (r_refdef.vrectright << 20) + 0xFFFFF; - edge_tail_u_shift20 = edge_tail.u >> 20; - edge_tail.u_step = 0; - edge_tail.prev = &edge_head; - edge_tail.next = &edge_aftertail; - edge_tail.surfs[0] = 1; - edge_tail.surfs[1] = 0; - - edge_aftertail.u = -1; // force a move - edge_aftertail.u_step = 0; - edge_aftertail.next = &edge_sentinel; - edge_aftertail.prev = &edge_tail; - - // FIXME: do we need this now that we clamp x in r_draw.c? - edge_sentinel.u = 32767 << 16; // make sure nothing sorts past this - edge_sentinel.prev = &edge_aftertail; - - // process all scan lines - bottom = r_refdef.vrectbottom - 1; - - for (iv = r_refdef.vrect.y; iv < bottom; iv++) { - current_iv = iv; - fv = (float) iv; - - // mark that the head (background start) span is pre-included - sw32_surfaces[1].spanstate = 1; - - if (sw32_newedges[iv]) { - sw32_R_InsertNewEdges (sw32_newedges[iv], edge_head.next); - } - - (*pdrawfunc) (); - - // flush the span list if we can't be sure we have enough spans left - // for the next scan - if (span_p > max_span_p) { - VID_UnlockBuffer (); - S_ExtraUpdate (); // don't let sound get messed up if going slow - VID_LockBuffer (); - - if (sw32_r_drawculledpolys) - R_DrawCulledPolys (); - else - sw32_D_DrawSurfaces (); - - // clear the surface span pointers - for (s = &sw32_surfaces[1]; s < sw32_surface_p; s++) - s->spans = NULL; - - span_p = basespan_p; - } - - if (sw32_removeedges[iv]) - sw32_R_RemoveEdges (sw32_removeedges[iv]); - - if (edge_head.next != &edge_tail) - sw32_R_StepActiveU (edge_head.next); - } - - // do the last scan (no need to step or sort or remove on the last scan) - current_iv = iv; - fv = (float) iv; - - // mark that the head (background start) span is pre-included - sw32_surfaces[1].spanstate = 1; - - if (sw32_newedges[iv]) - sw32_R_InsertNewEdges (sw32_newedges[iv], edge_head.next); - - (*pdrawfunc) (); - - // draw whatever's left in the span list - if (sw32_r_drawculledpolys) - R_DrawCulledPolys (); - else - sw32_D_DrawSurfaces (); -} diff --git a/libs/video/renderer/sw32/sw32_riqm.c b/libs/video/renderer/sw32/sw32_riqm.c deleted file mode 100644 index b53186bd9..000000000 --- a/libs/video/renderer/sw32/sw32_riqm.c +++ /dev/null @@ -1,322 +0,0 @@ -/* - sw32_riqm.c - - 32bit SW IQM rendering - - Copyright (C) 2012 Bill Currie - - Author: Bill Currie - Date: 2012/5/18 - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif -#include - -#define NH_DEFINE -#include "namehack.h" - -#include "QF/cvar.h" -#include "QF/image.h" -#include "QF/render.h" -#include "QF/skin.h" -#include "QF/sys.h" - -#include "d_ifacea.h" -#include "r_internal.h" - -#define LIGHT_MIN 5 // lowest light value we'll allow, to - // avoid the need for inner-loop light - // clamping - -static vec3_t r_plightvec; -static int r_ambientlight; -static float r_shadelight; - -static inline int -calc_light (float *normal) -{ - float lightcos = DotProduct (normal, r_plightvec); - int temp = r_ambientlight; - - if (lightcos < 0) { - temp += (int) (r_shadelight * lightcos); - - // clamp; because we limited the minimum ambient and shading - // light, we don't have to clamp low light, just bright - if (temp < 0) - temp = 0; - } - return temp; -} - -static void -R_IQMTransformAndProjectFinalVerts (iqm_t *iqm, swiqm_t *sw, iqmframe_t *frame) -{ - finalvert_t *fv = pfinalverts; - float zi; - int i; - - for (i = 0; i < iqm->num_verts; i++, fv++) { - byte *vert = iqm->vertices + i * iqm->stride; - uint32_t bind = *(uint32_t *) (vert + sw->bindices->offset); - vec_t *mat = (vec_t *) &frame[bind]; - float *position = (float *) (vert + sw->position->offset); - float *normal = (float *) (vert + sw->normal->offset); - int32_t *texcoord = (int32_t *) (vert + sw->texcoord->offset); - vec3_t tv, tn; - Mat4MultVec (mat, position, tv); - Mat4as3MultVec (mat, normal, tn); - zi = 1.0 / (DotProduct (tv, sw32_aliastransform[2]) - + sw32_aliastransform[2][3]); - fv->v[5] = zi; - fv->v[0] = (DotProduct (tv, sw32_aliastransform[0]) - + sw32_aliastransform[0][3]) * zi + aliasxcenter; - fv->v[1] = (DotProduct (tv, sw32_aliastransform[1]) - + sw32_aliastransform[1][3]) * zi + aliasxcenter; - fv->v[2] = texcoord[0]; - fv->v[3] = texcoord[1]; - fv->v[4] = calc_light (tn); - } -} - -static void -iqm_setup_skin (swiqm_t *sw, int skinnum) -{ - tex_t *skin = sw->skins[skinnum]; - - sw32_r_affinetridesc.pskin = skin->data; - sw32_r_affinetridesc.skinwidth = skin->width; - sw32_r_affinetridesc.skinheight = skin->height; - sw32_r_affinetridesc.seamfixupX16 = (skin->width >> 1) << 16; -} - -static void -R_IQMPrepareUnclippedPoints (iqm_t *iqm, swiqm_t *sw, iqmframe_t *frame) -{ - int i; - - R_IQMTransformAndProjectFinalVerts (iqm, sw, frame); - - sw32_r_affinetridesc.pfinalverts = pfinalverts; - for (i = 0; i < iqm->num_meshes; i++) { - iqmmesh *mesh = &iqm->meshes[i]; - uint16_t *tris; - - iqm_setup_skin (sw, i); - - tris = iqm->elements + mesh->first_triangle; - sw32_r_affinetridesc.ptriangles = (mtriangle_t *) tris; - sw32_r_affinetridesc.numtriangles = mesh->num_triangles; - sw32_D_PolysetDraw (); - } -} - -static void -R_IQMPreparePoints (iqm_t *iqm, swiqm_t *sw, iqmframe_t *frame) -{ - finalvert_t *fv = pfinalverts; - auxvert_t *av = pauxverts; - int i; - uint32_t j; - finalvert_t *pfv[3]; - - for (i = 0; i < iqm->num_verts; i++, fv++, av++) { - byte *vert = iqm->vertices + i * iqm->stride; - uint32_t bind = *(uint32_t *) (vert + sw->bindices->offset); - vec_t *mat = (vec_t *) &frame[bind]; - float *position = (float *) (vert + sw->position->offset); - float *normal = (float *) (vert + sw->normal->offset); - int32_t *texcoord = (int32_t *) (vert + sw->texcoord->offset); - vec3_t tv, tn; - Mat4MultVec (mat, position, tv); - Mat4as3MultVec (mat, normal, tn); - av->fv[0] = DotProduct (tv, sw32_aliastransform[0]) - + sw32_aliastransform[0][3]; - av->fv[1] = DotProduct (tv, sw32_aliastransform[1]) - + sw32_aliastransform[1][3]; - av->fv[2] = DotProduct (tv, sw32_aliastransform[2]) - + sw32_aliastransform[2][3]; - fv->v[2] = texcoord[0]; - fv->v[3] = texcoord[1]; - fv->flags = 0; - fv->v[4] = calc_light (tn); - sw32_R_AliasClipAndProjectFinalVert (fv, av); - } - - for (i = 0; i < iqm->num_meshes; i++) { - iqmmesh *mesh = &iqm->meshes[i]; - mtriangle_t *mtri; - - iqm_setup_skin (sw, i); - - mtri = (mtriangle_t *) iqm->elements + mesh->first_triangle; - sw32_r_affinetridesc.numtriangles = 1; - for (j = 0; j < mesh->num_triangles; j++, mtri++) { - pfv[0] = &pfinalverts[mtri->vertindex[0]]; - pfv[1] = &pfinalverts[mtri->vertindex[1]]; - pfv[2] = &pfinalverts[mtri->vertindex[2]]; - - if (pfv[0]->flags & pfv[1]->flags & pfv[2]->flags - & (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP)) - continue; // completely clipped - - if (!((pfv[0]->flags | pfv[1]->flags | pfv[2]->flags) - & (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP))) {// totally unclipped - sw32_r_affinetridesc.pfinalverts = pfinalverts; - sw32_r_affinetridesc.ptriangles = mtri; - sw32_D_PolysetDraw (); - } else { // partially clipped - sw32_R_AliasClipTriangle (mtri); - } - } - } -} - -static void -R_IQMSetupLighting (entity_t *ent, alight_t *plighting) -{ - // guarantee that no vertex will ever be lit below LIGHT_MIN, so we don't - // have to clamp off the bottom - r_ambientlight = plighting->ambientlight; - - if (r_ambientlight < LIGHT_MIN) - r_ambientlight = LIGHT_MIN; - - r_ambientlight = (255 - r_ambientlight) << VID_CBITS; - - if (r_ambientlight < LIGHT_MIN) - r_ambientlight = LIGHT_MIN; - - r_shadelight = plighting->shadelight; - - if (r_shadelight < 0) - r_shadelight = 0; - - r_shadelight *= VID_GRADES; - - // rotate the lighting vector into the model's frame of reference - r_plightvec[0] = DotProduct (plighting->plightvec, ent->transform + 0); - r_plightvec[1] = DotProduct (plighting->plightvec, ent->transform + 4); - r_plightvec[2] = DotProduct (plighting->plightvec, ent->transform + 8); -} - -static void -R_IQMSetUpTransform (int trivial_accept) -{ - int i; - float rotationmatrix[3][4]; - static float viewmatrix[3][4]; - vec3_t forward, left, up; - - VectorCopy (currententity->transform + 0, forward); - VectorCopy (currententity->transform + 4, left); - VectorCopy (currententity->transform + 8, up); - -// TODO: can do this with simple matrix rearrangement - - for (i = 0; i < 3; i++) { - rotationmatrix[i][0] = forward[i]; - rotationmatrix[i][1] = left[i]; - rotationmatrix[i][2] = up[i]; - } - - rotationmatrix[0][3] = -modelorg[0]; - rotationmatrix[1][3] = -modelorg[1]; - rotationmatrix[2][3] = -modelorg[2]; - -// TODO: should be global, set when vright, etc., set - VectorCopy (vright, viewmatrix[0]); - VectorCopy (vup, viewmatrix[1]); - VectorNegate (viewmatrix[1], viewmatrix[1]); - VectorCopy (vpn, viewmatrix[2]); - -// viewmatrix[0][3] = 0; -// viewmatrix[1][3] = 0; -// viewmatrix[2][3] = 0; - - R_ConcatTransforms (viewmatrix, rotationmatrix, sw32_aliastransform); - -// do the scaling up of x and y to screen coordinates as part of the transform -// for the unclipped case (it would mess up clipping in the clipped case). -// Also scale down z, so 1/z is scaled 31 bits for free, and scale down x and y -// correspondingly so the projected x and y come out right -// FIXME: make this work for clipped case too? - - if (trivial_accept) { - for (i = 0; i < 4; i++) { - sw32_aliastransform[0][i] *= aliasxscale * - (1.0 / ((float) 0x8000 * 0x10000)); - sw32_aliastransform[1][i] *= aliasyscale * - (1.0 / ((float) 0x8000 * 0x10000)); - sw32_aliastransform[2][i] *= 1.0 / ((float) 0x8000 * 0x10000); - } - } -} - -void -sw32_R_IQMDrawModel (alight_t *plighting) -{ - entity_t *ent = currententity; - model_t *model = ent->model; - iqm_t *iqm = (iqm_t *) model->aliashdr; - swiqm_t *sw = (swiqm_t *) iqm->extra_data; - int size; - float blend; - iqmframe_t *frame; - - size = (CACHE_SIZE - 1) - + sizeof (finalvert_t) * (iqm->num_verts + 1) - + sizeof (auxvert_t) * iqm->num_verts; - blend = R_IQMGetLerpedFrames (ent, iqm); - frame = R_IQMBlendPalette (iqm, ent->pose1, ent->pose2, blend, size, - sw->blend_palette, sw->palette_size); - - pfinalverts = (finalvert_t *) &frame[sw->palette_size]; - pfinalverts = (finalvert_t *) - (((intptr_t) &pfinalverts[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); - pauxverts = (auxvert_t *) &pfinalverts[iqm->num_verts + 1]; - - R_IQMSetUpTransform (ent->trivial_accept); - - R_IQMSetupLighting (ent, plighting); - - //if (!sw32_acolormap) - sw32_acolormap = vid.colormap8; - - if (ent != vr_data.view_model) - sw32_ziscale = (float) 0x8000 *(float) 0x10000; - else - sw32_ziscale = (float) 0x8000 *(float) 0x10000 *3.0; - - if (ent->trivial_accept) - R_IQMPrepareUnclippedPoints (iqm, sw, frame); - else - R_IQMPreparePoints (iqm, sw, frame); -} diff --git a/libs/video/renderer/sw32/sw32_rmain.c b/libs/video/renderer/sw32/sw32_rmain.c deleted file mode 100644 index 9ff2dc40f..000000000 --- a/libs/video/renderer/sw32/sw32_rmain.c +++ /dev/null @@ -1,863 +0,0 @@ -/* - sw32_rmain.c - - (description) - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif - -#include - -#include "QF/cmd.h" -#include "QF/cvar.h" -#include "QF/locs.h" -#include "QF/mathlib.h" -#include "QF/render.h" -#include "QF/screen.h" -#include "QF/sound.h" -#include "QF/sys.h" - -#include "compat.h" -#include "mod_internal.h" -#include "r_internal.h" -#include "vid_internal.h" - -//define PASSAGES - -static vec3_t viewlightvec; -static alight_t r_viewlighting = { 128, 192, viewlightvec }; -int sw32_r_numallocatededges; -qboolean sw32_r_drawpolys; -qboolean sw32_r_drawculledpolys; -qboolean sw32_r_worldpolysbacktofront; -int sw32_r_pixbytes = 1; -float sw32_r_aliasuvscale = 1.0; -int sw32_r_outofsurfaces; -int sw32_r_outofedges; - -qboolean sw32_r_dowarp, sw32_r_dowarpold, sw32_r_viewchanged; - -int sw32_c_surf; -int sw32_r_maxsurfsseen, sw32_r_maxedgesseen; -static int r_cnumsurfs; -static qboolean r_surfsonstack; -int sw32_r_clipflags; - -byte *sw32_r_warpbuffer; - -static byte *r_stack_start; - -// screen size info -float sw32_xcenter, sw32_ycenter; -float sw32_xscale, sw32_yscale; -float sw32_xscaleinv, sw32_yscaleinv; -float sw32_xscaleshrink, sw32_yscaleshrink; -float sw32_aliasxscale, sw32_aliasyscale, sw32_aliasxcenter, sw32_aliasycenter; - -int sw32_screenwidth; - -float sw32_pixelAspect; -static float screenAspect; -static float verticalFieldOfView; -static float xOrigin, yOrigin; - -plane_t sw32_screenedge[4]; - -// refresh flags -int sw32_r_polycount; -int sw32_r_drawnpolycount; - -int *sw32_pfrustum_indexes[4]; -int sw32_r_frustum_indexes[4 * 6]; - -float sw32_r_aliastransition, sw32_r_resfudge; - -static float dp_time1, dp_time2, db_time1, db_time2, rw_time1, rw_time2; -static float se_time1, se_time2, de_time1, de_time2, dv_time1, dv_time2; - -void -sw32_R_Textures_Init (void) -{ - int x, y, m; - byte *dest; - - // create a simple checkerboard texture for the default - r_notexture_mip = - Hunk_AllocName (sizeof (texture_t) + 16 * 16 + 8 * 8 + 4 * 4 + 2 * 2, - "notexture"); - - r_notexture_mip->width = r_notexture_mip->height = 16; - r_notexture_mip->offsets[0] = sizeof (texture_t); - - r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16 * 16; - r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8 * 8; - r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4 * 4; - - for (m = 0; m < 4; m++) { - dest = (byte *) r_notexture_mip + r_notexture_mip->offsets[m]; - for (y = 0; y < (16 >> m); y++) - for (x = 0; x < (16 >> m); x++) { - if ((y < (8 >> m)) ^ (x < (8 >> m))) - *dest++ = 0; - else - *dest++ = 0xff; - } - } -} - -void -sw32_R_Init (void) -{ - int dummy; - - // get stack position so we can guess if we are going to overflow - r_stack_start = (byte *) & dummy; - - R_Init_Cvars (); - sw32_R_Particles_Init_Cvars (); - - sw32_Draw_Init (); - SCR_Init (); - R_InitParticles (); - sw32_R_InitTurb (); - - Cmd_AddCommand ("timerefresh", sw32_R_TimeRefresh_f, "Tests the current " - "refresh rate for the current location"); - Cmd_AddCommand ("pointfile", sw32_R_ReadPointFile_f, "Load a pointfile to " - "determine map leaks"); - Cmd_AddCommand ("loadsky", sw32_R_LoadSky_f, "Load a skybox"); - - Cvar_SetValue (r_maxedges, (float) NUMSTACKEDGES); - Cvar_SetValue (r_maxsurfs, (float) NUMSTACKSURFACES); - - sw32_view_clipplanes[0].leftedge = true; - sw32_view_clipplanes[1].rightedge = true; - sw32_view_clipplanes[1].leftedge = sw32_view_clipplanes[2].leftedge = - sw32_view_clipplanes[3].leftedge = false; - sw32_view_clipplanes[0].rightedge = sw32_view_clipplanes[2].rightedge = - sw32_view_clipplanes[3].rightedge = false; - - r_refdef.xOrigin = XCENTERING; - r_refdef.yOrigin = YCENTERING; - - sw32_D_Init (); - - Skin_Init (); -} - -void -sw32_R_NewMap (model_t *worldmodel, struct model_s **models, int num_models) -{ - int i; - - memset (&r_worldentity, 0, sizeof (r_worldentity)); - r_worldentity.model = worldmodel; - - R_FreeAllEntities (); - - // clear out efrags in case the level hasn't been reloaded - // FIXME: is this one short? - for (i = 0; i < r_worldentity.model->numleafs; i++) - r_worldentity.model->leafs[i].efrags = NULL; - - if (worldmodel->skytexture) - sw32_R_InitSky (worldmodel->skytexture); - - // Force a vis update - r_viewleaf = NULL; - R_MarkLeaves (); - - sw32_R_ClearParticles (); - - r_cnumsurfs = r_maxsurfs->int_val; - - if (r_cnumsurfs <= MINSURFACES) - r_cnumsurfs = MINSURFACES; - - if (r_cnumsurfs > NUMSTACKSURFACES) { - sw32_surfaces = Hunk_AllocName (r_cnumsurfs * sizeof (surf_t), "surfaces"); - - sw32_surface_p = sw32_surfaces; - sw32_surf_max = &sw32_surfaces[r_cnumsurfs]; - r_surfsonstack = false; - // surface 0 doesn't really exist; it's just a dummy because index 0 - // is used to indicate no edge attached to surface - sw32_surfaces--; - } else { - r_surfsonstack = true; - } - - sw32_r_maxedgesseen = 0; - sw32_r_maxsurfsseen = 0; - - sw32_r_numallocatededges = r_maxedges->int_val; - - if (sw32_r_numallocatededges < MINEDGES) - sw32_r_numallocatededges = MINEDGES; - - if (sw32_r_numallocatededges <= NUMSTACKEDGES) { - sw32_auxedges = NULL; - } else { - sw32_auxedges = Hunk_AllocName (sw32_r_numallocatededges * sizeof (edge_t), - "edges"); - } - - sw32_r_dowarpold = false; - sw32_r_viewchanged = false; -} - -/* - R_ViewChanged - - Called every time the vid structure or r_refdef changes. - Guaranteed to be called before the first refresh -*/ -void -sw32_R_ViewChanged (float aspect) -{ - int i; - float res_scale; - - sw32_r_viewchanged = true; - - r_refdef.horizontalFieldOfView = 2.0 * tan (r_refdef.fov_x / 360 * M_PI); - r_refdef.fvrectx = (float) r_refdef.vrect.x; - r_refdef.fvrectx_adj = (float) r_refdef.vrect.x - 0.5; - r_refdef.vrect_x_adj_shift20 = (r_refdef.vrect.x << 20) + (1 << 19) - 1; - r_refdef.fvrecty = (float) r_refdef.vrect.y; - r_refdef.fvrecty_adj = (float) r_refdef.vrect.y - 0.5; - r_refdef.vrectright = r_refdef.vrect.x + r_refdef.vrect.width; - r_refdef.vrectright_adj_shift20 = - (r_refdef.vrectright << 20) + (1 << 19) - 1; - r_refdef.fvrectright = (float) r_refdef.vrectright; - r_refdef.fvrectright_adj = (float) r_refdef.vrectright - 0.5; - r_refdef.vrectrightedge = (float) r_refdef.vrectright - 0.99; - r_refdef.vrectbottom = r_refdef.vrect.y + r_refdef.vrect.height; - r_refdef.fvrectbottom = (float) r_refdef.vrectbottom; - r_refdef.fvrectbottom_adj = (float) r_refdef.vrectbottom - 0.5; - - r_refdef.aliasvrect.x = (int) (r_refdef.vrect.x * sw32_r_aliasuvscale); - r_refdef.aliasvrect.y = (int) (r_refdef.vrect.y * sw32_r_aliasuvscale); - r_refdef.aliasvrect.width = (int) (r_refdef.vrect.width * sw32_r_aliasuvscale); - r_refdef.aliasvrect.height = (int) (r_refdef.vrect.height * - sw32_r_aliasuvscale); - r_refdef.aliasvrectright = r_refdef.aliasvrect.x + - r_refdef.aliasvrect.width; - r_refdef.aliasvrectbottom = r_refdef.aliasvrect.y + - r_refdef.aliasvrect.height; - - sw32_pixelAspect = aspect; - xOrigin = r_refdef.xOrigin; - yOrigin = r_refdef.yOrigin; - - screenAspect = r_refdef.vrect.width * sw32_pixelAspect / r_refdef.vrect.height; - // 320*200 1.0 sw32_pixelAspect = 1.6 screenAspect - // 320*240 1.0 sw32_pixelAspect = 1.3333 screenAspect - // proper 320*200 sw32_pixelAspect = 0.8333333 - - verticalFieldOfView = r_refdef.horizontalFieldOfView / screenAspect; - - // values for perspective projection - // if math were exact, the values would range from 0.5 to to range+0.5 - // hopefully they wll be in the 0.000001 to range+.999999 and truncate - // the polygon rasterization will never render in the first row or column - // but will definately render in the [range] row and column, so adjust the - // buffer origin to get an exact edge to edge fill - sw32_xcenter = ((float) r_refdef.vrect.width * XCENTERING) + - r_refdef.vrect.x - 0.5; - sw32_aliasxcenter = sw32_xcenter * sw32_r_aliasuvscale; - sw32_ycenter = ((float) r_refdef.vrect.height * YCENTERING) + - r_refdef.vrect.y - 0.5; - sw32_aliasycenter = sw32_ycenter * sw32_r_aliasuvscale; - - sw32_xscale = r_refdef.vrect.width / r_refdef.horizontalFieldOfView; - sw32_aliasxscale = sw32_xscale * sw32_r_aliasuvscale; - sw32_xscaleinv = 1.0 / sw32_xscale; - sw32_yscale = sw32_xscale * sw32_pixelAspect; - sw32_aliasyscale = sw32_yscale * sw32_r_aliasuvscale; - sw32_yscaleinv = 1.0 / sw32_yscale; - sw32_xscaleshrink = (r_refdef.vrect.width - 6) / r_refdef.horizontalFieldOfView; - sw32_yscaleshrink = sw32_xscaleshrink * sw32_pixelAspect; - - // left side clip - sw32_screenedge[0].normal[0] = -1.0 / (xOrigin * - r_refdef.horizontalFieldOfView); - sw32_screenedge[0].normal[1] = 0; - sw32_screenedge[0].normal[2] = 1; - sw32_screenedge[0].type = PLANE_ANYZ; - - // right side clip - sw32_screenedge[1].normal[0] = 1.0 / ((1.0 - xOrigin) * - r_refdef.horizontalFieldOfView); - sw32_screenedge[1].normal[1] = 0; - sw32_screenedge[1].normal[2] = 1; - sw32_screenedge[1].type = PLANE_ANYZ; - - // top side clip - sw32_screenedge[2].normal[0] = 0; - sw32_screenedge[2].normal[1] = -1.0 / (yOrigin * verticalFieldOfView); - sw32_screenedge[2].normal[2] = 1; - sw32_screenedge[2].type = PLANE_ANYZ; - - // bottom side clip - sw32_screenedge[3].normal[0] = 0; - sw32_screenedge[3].normal[1] = 1.0 / ((1.0 - yOrigin) * verticalFieldOfView); - sw32_screenedge[3].normal[2] = 1; - sw32_screenedge[3].type = PLANE_ANYZ; - - for (i = 0; i < 4; i++) - VectorNormalize (sw32_screenedge[i].normal); - - res_scale = sqrt ((double) (r_refdef.vrect.width * r_refdef.vrect.height) / - (320.0 * 152.0)) * (2.0 / - r_refdef.horizontalFieldOfView); - sw32_r_aliastransition = r_aliastransbase->value * res_scale; - sw32_r_resfudge = r_aliastransadj->value * res_scale; - - sw32_D_ViewChanged (); -} - -static void -R_DrawEntitiesOnList (void) -{ - int j; - unsigned int lnum; - alight_t lighting; - entity_t *ent; - - // FIXME: remove and do real lighting - float lightvec[3] = { -1, 0, 0 }; - vec3_t dist; - float add; - float minlight; - - if (!r_drawentities->int_val) - return; - - for (ent = r_ent_queue; ent; ent = ent->next) { - currententity = ent; - - switch (currententity->model->type) { - case mod_sprite: - VectorCopy (currententity->origin, r_entorigin); - VectorSubtract (r_origin, r_entorigin, modelorg); - sw32_R_DrawSprite (); - break; - - case mod_alias: - case mod_iqm: - VectorCopy (currententity->origin, r_entorigin); - VectorSubtract (r_origin, r_entorigin, modelorg); - - minlight = max (currententity->min_light, currententity->model->min_light); - - // see if the bounding box lets us trivially reject, also - // sets trivial accept status - currententity->trivial_accept = 0; //FIXME - if (currententity->model->type == mod_iqm//FIXME - || sw32_R_AliasCheckBBox ()) { - // 128 instead of 255 due to clamping below - j = max (R_LightPoint (currententity->origin), minlight * 128); - - lighting.ambientlight = j; - lighting.shadelight = j; - - lighting.plightvec = lightvec; - - for (lnum = 0; lnum < r_maxdlights; lnum++) { - if (r_dlights[lnum].die >= vr_data.realtime) { - VectorSubtract (currententity->origin, - r_dlights[lnum].origin, dist); - add = r_dlights[lnum].radius - VectorLength (dist); - - if (add > 0) - lighting.ambientlight += add; - } - } - - // clamp lighting so it doesn't overbright as much - if (lighting.ambientlight > 128) - lighting.ambientlight = 128; - if (lighting.ambientlight + lighting.shadelight > 192) - lighting.shadelight = 192 - lighting.ambientlight; - - if (currententity->model->type == mod_iqm) - sw32_R_IQMDrawModel (&lighting); - else - sw32_R_AliasDrawModel (&lighting); - } - - break; - - default: - break; - } - } -} - -static void -R_DrawViewModel (void) -{ - // FIXME: remove and do real lighting - float lightvec[3] = { -1, 0, 0 }; - int j; - unsigned int lnum; - vec3_t dist; - float add; - float minlight; - dlight_t *dl; - - if (vr_data.inhibit_viewmodel || !r_drawviewmodel->int_val - || !r_drawentities->int_val) - return; - - currententity = vr_data.view_model; - if (!currententity->model) - return; - - VectorCopy (currententity->origin, r_entorigin); - VectorSubtract (r_origin, r_entorigin, modelorg); - - VectorCopy (vup, viewlightvec); - VectorNegate (viewlightvec, viewlightvec); - - minlight = max (currententity->min_light, currententity->model->min_light); - - j = max (R_LightPoint (currententity->origin), minlight * 128); - - r_viewlighting.ambientlight = j; - r_viewlighting.shadelight = j; - - // add dynamic lights - for (lnum = 0; lnum < r_maxdlights; lnum++) { - dl = &r_dlights[lnum]; - if (!dl->radius) - continue; - if (!dl->radius) - continue; - if (dl->die < vr_data.realtime) - continue; - - VectorSubtract (currententity->origin, dl->origin, dist); - add = dl->radius - VectorLength (dist); - if (add > 0) - r_viewlighting.ambientlight += add; - } - - // clamp lighting so it doesn't overbright as much - if (r_viewlighting.ambientlight > 128) - r_viewlighting.ambientlight = 128; - if (r_viewlighting.ambientlight + r_viewlighting.shadelight > 192) - r_viewlighting.shadelight = 192 - r_viewlighting.ambientlight; - - r_viewlighting.plightvec = lightvec; - - sw32_R_AliasDrawModel (&r_viewlighting); -} - -static int -R_BmodelCheckBBox (model_t *clmodel, float *minmaxs) -{ - int i, *pindex, clipflags; - vec3_t acceptpt, rejectpt; - double d; - - clipflags = 0; - - if (currententity->transform[0] != 1 || currententity->transform[5] != 1 - || currententity->transform[10] != 1) { - for (i = 0; i < 4; i++) { - d = DotProduct (currententity->origin, sw32_view_clipplanes[i].normal); - d -= sw32_view_clipplanes[i].dist; - - if (d <= -clmodel->radius) - return BMODEL_FULLY_CLIPPED; - - if (d <= clmodel->radius) - clipflags |= (1 << i); - } - } else { - for (i = 0; i < 4; i++) { - // generate accept and reject points - // FIXME: do with fast look-ups or integer tests based on the - // sign bit of the floating point values - - pindex = sw32_pfrustum_indexes[i]; - - rejectpt[0] = minmaxs[pindex[0]]; - rejectpt[1] = minmaxs[pindex[1]]; - rejectpt[2] = minmaxs[pindex[2]]; - - d = DotProduct (rejectpt, sw32_view_clipplanes[i].normal); - d -= sw32_view_clipplanes[i].dist; - - if (d <= 0) - return BMODEL_FULLY_CLIPPED; - - acceptpt[0] = minmaxs[pindex[3 + 0]]; - acceptpt[1] = minmaxs[pindex[3 + 1]]; - acceptpt[2] = minmaxs[pindex[3 + 2]]; - - d = DotProduct (acceptpt, sw32_view_clipplanes[i].normal); - d -= sw32_view_clipplanes[i].dist; - - if (d <= 0) - clipflags |= (1 << i); - } - } - - return clipflags; -} - -static void -R_DrawBEntitiesOnList (void) -{ - int j, clipflags; - unsigned int k; - vec3_t oldorigin; - model_t *clmodel; - float minmaxs[6]; - entity_t *ent; - - if (!r_drawentities->int_val) - return; - - VectorCopy (modelorg, oldorigin); - insubmodel = true; - - for (ent = r_ent_queue; ent; ent = ent->next) { - currententity = ent; - - switch (currententity->model->type) { - case mod_brush: - clmodel = currententity->model; - - // see if the bounding box lets us trivially reject, also - // sets trivial accept status - for (j = 0; j < 3; j++) { - minmaxs[j] = currententity->origin[j] + clmodel->mins[j]; - minmaxs[3 + j] = currententity->origin[j] + - clmodel->maxs[j]; - } - - clipflags = R_BmodelCheckBBox (clmodel, minmaxs); - - if (clipflags != BMODEL_FULLY_CLIPPED) { - VectorCopy (currententity->origin, r_entorigin); - VectorSubtract (r_origin, r_entorigin, modelorg); - - // FIXME: is this needed? - VectorCopy (modelorg, sw32_r_worldmodelorg); - r_pcurrentvertbase = clmodel->vertexes; - - // FIXME: stop transforming twice - sw32_R_RotateBmodel (); - - // calculate dynamic lighting for bmodel if it's not an - // instanced model - if (clmodel->firstmodelsurface != 0) { - vec3_t lightorigin; - - for (k = 0; k < r_maxdlights; k++) { - if ((r_dlights[k].die < vr_data.realtime) || - (!r_dlights[k].radius)) continue; - - VectorSubtract (r_dlights[k].origin, - currententity->origin, - lightorigin); - R_RecursiveMarkLights (lightorigin, &r_dlights[k], - k, clmodel->nodes + - clmodel->hulls[0].firstclipnode); - } - } - // if the driver wants polygons, deliver those. - // Z-buffering is on at this point, so no clipping to the - // world tree is needed, just frustum clipping - if (sw32_r_drawpolys | sw32_r_drawculledpolys) { - sw32_R_ZDrawSubmodelPolys (clmodel); - } else { - if (currententity->topnode) { - mnode_t *topnode = currententity->topnode; - - if (topnode->contents >= 0) { - // not a leaf; has to be clipped to the world - // BSP - sw32_r_clipflags = clipflags; - sw32_R_DrawSolidClippedSubmodelPolygons (clmodel); - } else { - // falls entirely in one leaf, so we just put - // all the edges in the edge list and let 1/z - // sorting handle drawing order - sw32_R_DrawSubmodelPolygons (clmodel, clipflags); - } - } - } - - // put back world rotation and frustum clipping - // FIXME: sw32_R_RotateBmodel should just work off base_vxx - VectorCopy (base_vpn, vpn); - VectorCopy (base_vup, vup); - VectorCopy (base_vright, vright); - VectorCopy (base_modelorg, modelorg); - VectorCopy (oldorigin, modelorg); - sw32_R_TransformFrustum (); - } - break; - default: - break; - } - } - - insubmodel = false; -} - -static void -R_PrintDSpeeds (void) -{ - float ms, dp_time, r_time2, rw_time, db_time, se_time, de_time, - - dv_time; - - r_time2 = Sys_DoubleTime (); - - dp_time = (dp_time2 - dp_time1) * 1000; - rw_time = (rw_time2 - rw_time1) * 1000; - db_time = (db_time2 - db_time1) * 1000; - se_time = (se_time2 - se_time1) * 1000; - de_time = (de_time2 - de_time1) * 1000; - dv_time = (dv_time2 - dv_time1) * 1000; - ms = (r_time2 - r_time1) * 1000; - - Sys_Printf ("%3i %4.1fp %3iw %4.1fb %3is %4.1fe %4.1fv\n", - (int) ms, dp_time, (int) rw_time, db_time, (int) se_time, - de_time, dv_time); -} - -static void -R_EdgeDrawing (void) -{ - edge_t ledges[NUMSTACKEDGES + - ((CACHE_SIZE - 1) / sizeof (edge_t)) + 1]; - surf_t lsurfs[NUMSTACKSURFACES + - ((CACHE_SIZE - 1) / sizeof (surf_t)) + 1]; - - if (sw32_auxedges) { - sw32_r_edges = sw32_auxedges; - } else { - sw32_r_edges = (edge_t *) - (((intptr_t) &ledges[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); - } - - if (r_surfsonstack) { - sw32_surfaces = (surf_t *) - (((intptr_t) &lsurfs[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); - sw32_surf_max = &sw32_surfaces[r_cnumsurfs]; - // surface 0 doesn't really exist; it's just a dummy because index 0 - // is used to indicate no edge attached to surface - sw32_surfaces--; - } - - sw32_R_BeginEdgeFrame (); - - if (r_dspeeds->int_val) { - rw_time1 = Sys_DoubleTime (); - } - - sw32_R_RenderWorld (); - - if (sw32_r_drawculledpolys) - sw32_R_ScanEdges (); - - // only the world can be drawn back to front with no z reads or compares, - // just z writes, so have the driver turn z compares on now - sw32_D_TurnZOn (); - - if (r_dspeeds->int_val) { - rw_time2 = Sys_DoubleTime (); - db_time1 = rw_time2; - } - - R_DrawBEntitiesOnList (); - - if (r_dspeeds->int_val) { - db_time2 = Sys_DoubleTime (); - se_time1 = db_time2; - } - - if (!r_dspeeds->int_val) { - VID_UnlockBuffer (); - S_ExtraUpdate (); // don't let sound get messed up if going slow - VID_LockBuffer (); - } - - if (!(sw32_r_drawpolys | sw32_r_drawculledpolys)) - sw32_R_ScanEdges (); -} - -// LordHavoc: took out of stack and made 4x size for 32bit capacity -static byte warpbuffer[WARP_WIDTH * WARP_HEIGHT * 4]; - -/* - R_RenderView - - r_refdef must be set before the first call -*/ -static void -R_RenderView_ (void) -{ - if (r_norefresh->int_val) - return; - - sw32_r_warpbuffer = warpbuffer; - - if (r_timegraph->int_val || r_speeds->int_val || r_dspeeds->int_val) - r_time1 = Sys_DoubleTime (); - - sw32_R_SetupFrame (); - -#ifdef PASSAGES - SetVisibilityByPassages (); -#else - R_MarkLeaves (); // done here so we know if we're in water -#endif - R_PushDlights (vec3_origin); - - if (!r_worldentity.model) - Sys_Error ("R_RenderView: NULL worldmodel"); - - if (!r_dspeeds->int_val) { - VID_UnlockBuffer (); - S_ExtraUpdate (); // don't let sound get messed up if going slow - VID_LockBuffer (); - } - - R_EdgeDrawing (); - - if (!r_dspeeds->int_val) { - VID_UnlockBuffer (); - S_ExtraUpdate (); // don't let sound get messed up if going slow - VID_LockBuffer (); - } - - if (r_dspeeds->int_val) { - se_time2 = Sys_DoubleTime (); - de_time1 = se_time2; - } - - R_DrawEntitiesOnList (); - - if (r_dspeeds->int_val) { - de_time2 = Sys_DoubleTime (); - dv_time1 = de_time2; - } - - R_DrawViewModel (); - - if (r_dspeeds->int_val) { - dv_time2 = Sys_DoubleTime (); - dp_time1 = Sys_DoubleTime (); - } - - sw32_R_DrawParticles (); - - if (r_dspeeds->int_val) - dp_time2 = Sys_DoubleTime (); - - if (sw32_r_dowarp) - sw32_D_WarpScreen (); - - if (r_timegraph->int_val) - R_TimeGraph (); - - if (r_zgraph->int_val) - R_ZGraph (); - - if (r_aliasstats->int_val) - sw32_R_PrintAliasStats (); - - if (r_speeds->int_val) - sw32_R_PrintTimes (); - - if (r_dspeeds->int_val) - R_PrintDSpeeds (); - - if (r_reportsurfout->int_val && sw32_r_outofsurfaces) - Sys_Printf ("Short %d surfaces\n", sw32_r_outofsurfaces); - - if (r_reportedgeout->int_val && sw32_r_outofedges) - Sys_Printf ("Short roughly %d edges\n", sw32_r_outofedges * 2 / 3); -} - -void -sw32_R_RenderView (void) -{ - int dummy; - int delta; - - delta = (byte *) & dummy - r_stack_start; - if (delta < -10000 || delta > 10000) - Sys_Error ("R_RenderView: called without enough stack"); - - if (Hunk_LowMark () & 3) - Sys_Error ("Hunk is missaligned"); - - if ((intptr_t) (&dummy) & 3) - Sys_Error ("Stack is missaligned"); - - if ((intptr_t) (&sw32_r_warpbuffer) & 3) - Sys_Error ("Globals are missaligned"); - - R_RenderView_ (); -} - -void -sw32_R_InitTurb (void) -{ - int i; - - for (i = 0; i < MAXWIDTH; i++) { - sw32_sintable[i] = AMP + sin (i * 3.14159 * 2 / CYCLE) * AMP; - sw32_intsintable[i] = AMP2 + sin (i * 3.14159 * 2 / CYCLE) * AMP2; - // AMP2 not 20 - } -} - -void -sw32_R_ClearState (void) -{ - R_ClearEfrags (); - R_ClearDlights (); - sw32_R_ClearParticles (); -} diff --git a/libs/video/renderer/sw32/sw32_rmisc.c b/libs/video/renderer/sw32/sw32_rmisc.c deleted file mode 100644 index cb12395d3..000000000 --- a/libs/video/renderer/sw32/sw32_rmisc.c +++ /dev/null @@ -1,321 +0,0 @@ -/* - sw32_rmisc.c - - (description) - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include "QF/cmd.h" -#include "QF/cvar.h" -#include "QF/draw.h" -#include "QF/render.h" -#include "QF/sys.h" - -#include "compat.h" -#include "r_internal.h" -#include "vid_internal.h" - - -static void -R_CheckVariables (void) -{ -} - -/* - R_TimeRefresh_f - - For program optimization -*/ -void -sw32_R_TimeRefresh_f (void) -{ - int i; - float start, stop, time; - int startangle; - vrect_t vr; - - startangle = r_refdef.viewangles[1]; - - start = Sys_DoubleTime (); - for (i = 0; i < 128; i++) { - r_refdef.viewangles[1] = i / 128.0 * 360.0; - - VID_LockBuffer (); - - sw32_R_RenderView (); - - VID_UnlockBuffer (); - - vr.x = r_refdef.vrect.x; - vr.y = r_refdef.vrect.y; - vr.width = r_refdef.vrect.width; - vr.height = r_refdef.vrect.height; - vr.next = NULL; - VID_Update (&vr); - } - stop = Sys_DoubleTime (); - time = stop - start; - Sys_Printf ("%g seconds (%g fps)\n", time, 128 / time); - - r_refdef.viewangles[1] = startangle; -} - -void -sw32_R_LoadSky_f (void) -{ - if (Cmd_Argc () != 2) { - Sys_Printf ("loadsky : load a skybox\n"); - return; - } - - sw32_R_LoadSkys (Cmd_Argv (1)); -} - -void -sw32_R_PrintTimes (void) -{ - float r_time2; - float ms; - - r_time2 = Sys_DoubleTime (); - - ms = 1000 * (r_time2 - r_time1); - - Sys_Printf ("%5.1f ms %3i/%3i/%3i poly %3i surf\n", - ms, sw32_c_faceclip, sw32_r_polycount, sw32_r_drawnpolycount, sw32_c_surf); - sw32_c_surf = 0; -} - -void -sw32_R_PrintAliasStats (void) -{ - Sys_Printf ("%3i polygon model drawn\n", sw32_r_amodels_drawn); -} - -void -sw32_R_TransformFrustum (void) -{ - int i; - vec3_t v, v2; - - for (i = 0; i < 4; i++) { - v[0] = sw32_screenedge[i].normal[2]; - v[1] = -sw32_screenedge[i].normal[0]; - v[2] = sw32_screenedge[i].normal[1]; - - v2[0] = v[1] * vright[0] + v[2] * vup[0] + v[0] * vpn[0]; - v2[1] = v[1] * vright[1] + v[2] * vup[1] + v[0] * vpn[1]; - v2[2] = v[1] * vright[2] + v[2] * vup[2] + v[0] * vpn[2]; - - VectorCopy (v2, sw32_view_clipplanes[i].normal); - - sw32_view_clipplanes[i].dist = DotProduct (modelorg, v2); - } -} - -void -sw32_TransformVector (const vec3_t in, vec3_t out) -{ - out[0] = DotProduct (in, vright); - out[1] = DotProduct (in, vup); - out[2] = DotProduct (in, vpn); -} - -void -sw32_R_TransformPlane (plane_t *p, float *normal, float *dist) -{ - float d; - - d = DotProduct (r_origin, p->normal); - *dist = p->dist - d; -// TODO: when we have rotating entities, this will need to use the view matrix - sw32_TransformVector (p->normal, normal); -} - -static void -R_SetUpFrustumIndexes (void) -{ - int i, j, *pindex; - - pindex = sw32_r_frustum_indexes; - - for (i = 0; i < 4; i++) { - for (j = 0; j < 3; j++) { - if (sw32_view_clipplanes[i].normal[j] < 0) { - pindex[j] = j; - pindex[j + 3] = j + 3; - } else { - pindex[j] = j + 3; - pindex[j + 3] = j; - } - } - - // FIXME: do just once at start - sw32_pfrustum_indexes[i] = pindex; - pindex += 6; - } -} - -void -sw32_R_SetupFrame (void) -{ - int edgecount; - vrect_t vrect; - float w, h; - - // don't allow cheats in multiplayer - Cvar_SetValue (r_ambient, 0); - Cvar_SetValue (r_drawflat, 0); - - if (r_numsurfs->int_val) { - if ((sw32_surface_p - sw32_surfaces) > sw32_r_maxsurfsseen) - sw32_r_maxsurfsseen = sw32_surface_p - sw32_surfaces; - - Sys_Printf ("Used %ld of %ld surfs; %d max\n", - (long) (sw32_surface_p - sw32_surfaces), - (long) (sw32_surf_max - sw32_surfaces), sw32_r_maxsurfsseen); - } - - if (r_numedges->int_val) { - edgecount = sw32_edge_p - sw32_r_edges; - - if (edgecount > sw32_r_maxedgesseen) - sw32_r_maxedgesseen = edgecount; - - Sys_Printf ("Used %d of %d edges; %d max\n", edgecount, - sw32_r_numallocatededges, sw32_r_maxedgesseen); - } - - r_refdef.ambientlight = max (r_ambient->value, 0); - - R_CheckVariables (); - - R_AnimateLight (); - R_ClearEnts (); - r_framecount++; - - sw32_numbtofpolys = 0; - - // debugging -#if 0 - r_refdef.vieworg[0] = 80; - r_refdef.vieworg[1] = 64; - r_refdef.vieworg[2] = 40; - r_refdef.viewangles[0] = 0; - r_refdef.viewangles[1] = 46.763641357; - r_refdef.viewangles[2] = 0; -#endif - - // build the transformation matrix for the given view angles - VectorCopy (r_refdef.vieworg, modelorg); - VectorCopy (r_refdef.vieworg, r_origin); - - AngleVectors (r_refdef.viewangles, vpn, vright, vup); - R_SetFrustum (); - - // current viewleaf - r_viewleaf = Mod_PointInLeaf (r_origin, r_worldentity.model); - - sw32_r_dowarpold = sw32_r_dowarp; - sw32_r_dowarp = r_waterwarp->int_val && (r_viewleaf->contents <= - CONTENTS_WATER); - - if ((sw32_r_dowarp != sw32_r_dowarpold) || sw32_r_viewchanged) { - if (sw32_r_dowarp) { - if ((vid.width <= WARP_WIDTH) - && (vid.height <= WARP_HEIGHT)) { - vrect.x = 0; - vrect.y = 0; - vrect.width = vid.width; - vrect.height = vid.height; - - R_SetVrect (&vrect, &r_refdef.vrect, vr_data.lineadj); - sw32_R_ViewChanged (vid.aspect); - } else { - w = vid.width; - h = vid.height; - - if (w > WARP_WIDTH) { - h *= (float) WARP_WIDTH / w; - w = WARP_WIDTH; - } - - if (h > WARP_HEIGHT) { - h = WARP_HEIGHT; - w *= (float) WARP_HEIGHT / h; - } - - vrect.x = 0; - vrect.y = 0; - vrect.width = (int) w; - vrect.height = (int) h; - - R_SetVrect (&vrect, &r_refdef.vrect, - (int) ((float) vr_data.lineadj * - (h / (float) vid.height))); - sw32_R_ViewChanged (vid.aspect * (h / w) * ((float) vid.width / - (float) vid.height)); - } - } else { - vrect.x = 0; - vrect.y = 0; - vrect.width = vid.width; - vrect.height = vid.height; - - r_refdef.vrect = scr_vrect; - sw32_R_ViewChanged (vid.aspect); - } - - sw32_r_viewchanged = false; - } - // start off with just the four screen edge clip planes - sw32_R_TransformFrustum (); - - // save base values - VectorCopy (vpn, base_vpn); - VectorCopy (vright, base_vright); - VectorCopy (vup, base_vup); - VectorCopy (modelorg, base_modelorg); - - sw32_R_SetSkyFrame (); - - R_SetUpFrustumIndexes (); - - r_cache_thrash = false; - - // clear frame counts - sw32_c_faceclip = 0; - sw32_r_polycount = 0; - sw32_r_drawnpolycount = 0; - sw32_r_amodels_drawn = 0; - sw32_r_outofsurfaces = 0; - sw32_r_outofedges = 0; - - sw32_D_SetupFrame (); -} diff --git a/libs/video/renderer/sw32/sw32_rpart.c b/libs/video/renderer/sw32/sw32_rpart.c deleted file mode 100644 index 24e6a7876..000000000 --- a/libs/video/renderer/sw32/sw32_rpart.c +++ /dev/null @@ -1,921 +0,0 @@ -/* - sw32_rpart.c - - 24 bit color software renderer particle effects. - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif - -#include "QF/cvar.h" -#include "QF/mersenne.h" -#include "QF/qargs.h" -#include "QF/quakefs.h" -#include "QF/render.h" -#include "QF/sys.h" -#include "QF/va.h" - -#include "compat.h" -#include "r_internal.h" - -static int ramp1[8] = { 0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61 }; -//static int ramp2[8] = { 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66 }; -static int ramp3[8] = { 0x6d, 0x6b, 6, 5, 4, 3 }; - -static mtstate_t mt; // private PRNG state - - -void -sw32_R_InitParticles (void) -{ - R_LoadParticles (); - mtwist_seed (&mt, 0xdeadbeef); -} - -void -sw32_R_ClearParticles (void) -{ - unsigned int i; - - free_particles = &particles[0]; - active_particles = NULL; - - for (i = 0; i < r_maxparticles; i++) - particles[i].next = &particles[i + 1]; - if (r_maxparticles) - particles[r_maxparticles - 1].next = NULL; -} - -void -sw32_R_ReadPointFile_f (void) -{ - QFile *f; - vec3_t org; - int r; - int c; - particle_t *p; - const char *name; - char *mapname; - - mapname = strdup (r_worldentity.model->name); - if (!mapname) - Sys_Error ("Can't duplicate mapname!"); - QFS_StripExtension (mapname, mapname); - - name = va ("maps/%s.pts", mapname); - free (mapname); - - f = QFS_FOpenFile (name); - if (!f) { - Sys_Printf ("couldn't open %s\n", name); - return; - } - - Sys_Printf ("Reading %s...\n", name); - c = 0; - for (;;) { - char buf[64]; - - Qgets (f, buf, sizeof (buf)); - r = sscanf (buf, "%f %f %f\n", &org[0], &org[1], &org[2]); - if (r != 3) - break; - c++; - - if (!free_particles) { - Sys_Printf ("Not enough free particles\n"); - break; - } - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = 99999; - p->color = (-c) & 15; - p->physics = R_ParticlePhysics ("pt_static"); - VectorZero (p->vel); - VectorCopy (org, p->org); - } - - Qclose (f); - Sys_Printf ("%i points read\n", c); -} - -static void -R_ParticleExplosion_QF (const vec3_t org) -{ - int i, j; - particle_t *p; - - if (!r_particles->int_val) - return; - - for (i = 0; i < 1024; i++) { - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = vr_data.realtime + 5; - p->color = ramp1[0]; - p->ramp = mtwist_rand (&mt) & 3; - if (i & 1) { - p->physics = R_ParticlePhysics ("pt_explode"); - for (j = 0; j < 3; j++) { - p->org[j] = org[j] + ((mtwist_rand (&mt) % 32) - 16); - p->vel[j] = (mtwist_rand (&mt) % 512) - 256; - } - } else { - p->physics = R_ParticlePhysics ("pt_explode2"); - for (j = 0; j < 3; j++) { - p->org[j] = org[j] + ((mtwist_rand (&mt) % 32) - 16); - p->vel[j] = (mtwist_rand (&mt) % 512) - 256; - } - } - } -} - -static void -R_ParticleExplosion2_QF (const vec3_t org, int colorStart, int colorLength) -{ - int i, j; - particle_t *p; - int colorMod = 0; - - for (i=0; i<512; i++) - { - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = vr_data.realtime + 0.3; - p->color = colorStart + (colorMod % colorLength); - colorMod++; - - p->physics = R_ParticlePhysics ("pt_blob"); - for (j=0 ; j<3 ; j++) - { - p->org[j] = org[j] + ((mtwist_rand (&mt)%32)-16); - p->vel[j] = (mtwist_rand (&mt)%512)-256; - } - } -} - -static void -R_BlobExplosion_QF (const vec3_t org) -{ - int i, j; - particle_t *p; - - if (!r_particles->int_val) - return; - - for (i = 0; i < 1024; i++) { - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = vr_data.realtime + 1 + (mtwist_rand (&mt) & 8) * 0.05; - - if (i & 1) { - p->physics = R_ParticlePhysics ("pt_blob"); - p->color = 66 + mtwist_rand (&mt) % 6; - for (j = 0; j < 3; j++) { - p->org[j] = org[j] + ((mtwist_rand (&mt) % 32) - 16); - p->vel[j] = (mtwist_rand (&mt) % 512) - 256; - } - } else { - p->physics = R_ParticlePhysics ("pt_blob2"); - p->color = 150 + mtwist_rand (&mt) % 6; - for (j = 0; j < 3; j++) { - p->org[j] = org[j] + ((mtwist_rand (&mt) % 32) - 16); - p->vel[j] = (mtwist_rand (&mt) % 512) - 256; - } - } - } -} - -static void -R_LavaSplash_QF (const vec3_t org) -{ - int i, j, k; - particle_t *p; - float vel; - vec3_t dir; - - if (!r_particles->int_val) - return; - - for (i = -16; i < 16; i++) - for (j = -16; j < 16; j++) - for (k = 0; k < 1; k++) { - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = vr_data.realtime + 2 + (mtwist_rand (&mt) & 31) * 0.02; - p->color = 224 + (mtwist_rand (&mt) & 7); - p->physics = R_ParticlePhysics ("pt_grav"); - - dir[0] = j * 8 + (mtwist_rand (&mt) & 7); - dir[1] = i * 8 + (mtwist_rand (&mt) & 7); - dir[2] = 256; - - p->org[0] = org[0] + dir[0]; - p->org[1] = org[1] + dir[1]; - p->org[2] = org[2] + (mtwist_rand (&mt) & 63); - - VectorNormalize (dir); - vel = 50 + (mtwist_rand (&mt) & 63); - VectorScale (dir, vel, p->vel); - } -} - -static void -R_TeleportSplash_QF (const vec3_t org) -{ - float vel; - int i, j, k; - particle_t *p; - vec3_t dir; - - if (!r_particles->int_val) - return; - - for (i = -16; i < 16; i += 4) { - for (j = -16; j < 16; j += 4) { - for (k = -24; k < 32; k += 4) { - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = vr_data.realtime + 0.2 + (mtwist_rand (&mt) & 7) * 0.02; - p->color = 7 + (mtwist_rand (&mt) & 7); - p->physics = R_ParticlePhysics ("pt_grav"); - - dir[0] = j * 8; - dir[1] = i * 8; - dir[2] = k * 8; - - p->org[0] = org[0] + i + (mtwist_rand (&mt) & 3); - p->org[1] = org[1] + j + (mtwist_rand (&mt) & 3); - p->org[2] = org[2] + k + (mtwist_rand (&mt) & 3); - - VectorNormalize (dir); - vel = 50 + (mtwist_rand (&mt) & 63); - VectorScale (dir, vel, p->vel); - } - } - } -} - -static void -R_DarkFieldParticles_ID (entity_t *ent) -{ - int i, j, k; - unsigned int rnd; - float vel; - particle_t *p; - vec3_t dir, org; - - org[0] = ent->origin[0]; - org[1] = ent->origin[1]; - org[2] = ent->origin[2]; - for (i = -16; i < 16; i += 8) { - for (j = -16; j < 16; j += 8) { - for (k = 0; k < 32; k += 8) { - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - rnd = mtwist_rand (&mt); - - p->die = vr_data.realtime + 0.2 + (rnd & 7) * 0.02; - p->color = 150 + mtwist_rand (&mt) % 6; - p->physics = R_ParticlePhysics ("pt_slowgrav"); - - dir[0] = j * 8; - dir[1] = i * 8; - dir[2] = k * 8; - - p->org[0] = org[0] + i + ((rnd >> 3) & 3); - p->org[1] = org[1] + j + ((rnd >> 5) & 3); - p->org[2] = org[2] + k + ((rnd >> 7) & 3); - - VectorNormalize (dir); - vel = 50 + ((rnd >> 9) & 63); - VectorScale (dir, vel, p->vel); - } - } - } -} - -static vec3_t avelocities[NUMVERTEXNORMALS]; - -static void -R_EntityParticles_ID (entity_t *ent) -{ - int i; - float angle, sp, sy, cp, cy; // cr, sr - float beamlength = 16.0, dist = 64.0; - particle_t *p; - vec3_t forward; - - for (i = 0; i < NUMVERTEXNORMALS; i++) { - int k; - for (k = 0; k < 3; k++) { - avelocities[i][k] = (mtwist_rand (&mt) & 255) * 0.01; - } - } - - for (i = 0; i < NUMVERTEXNORMALS; i++) { - angle = vr_data.realtime * avelocities[i][0]; - cy = cos (angle); - sy = sin (angle); - angle = vr_data.realtime * avelocities[i][1]; - cp = cos (angle); - sp = sin (angle); -// Next 3 lines results aren't currently used, may be in future. --Despair -// angle = vr_data.realtime * avelocities[i][2]; -// sr = sin (angle); -// cr = cos (angle); - - forward[0] = cp * cy; - forward[1] = cp * sy; - forward[2] = -sp; - - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = vr_data.realtime + 0.01; - p->color = 0x6f; - p->physics = R_ParticlePhysics ("pt_explode"); - - p->org[0] = ent->origin[0] + r_avertexnormals[i][0] * dist + - forward[0] * beamlength; - p->org[1] = ent->origin[1] + r_avertexnormals[i][1] * dist + - forward[1] * beamlength; - p->org[2] = ent->origin[2] + r_avertexnormals[i][2] * dist + - forward[2] * beamlength; - } -} - -static void -R_RunParticleEffect_QF (const vec3_t org, const vec3_t dir, int color, - int count) -{ - int i, j; - particle_t *p; - - if (!r_particles->int_val) - return; - - for (i = 0; i < count; i++) { - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = vr_data.realtime + 0.1 * (mtwist_rand (&mt) % 5); - p->color = (color & ~7) + (mtwist_rand (&mt) & 7); - p->physics = R_ParticlePhysics ("pt_slowgrav"); - for (j = 0; j < 3; j++) { - p->org[j] = org[j] + ((mtwist_rand (&mt) & 15) - 8); - p->vel[j] = dir[j]; // + (mtwist_rand (&mt)%300)-150; - } - } -} - -static void -R_SpikeEffect_QF (const vec3_t org) -{ - R_RunParticleEffect_QF (org, vec3_origin, 0, 10); -} - -static void -R_SuperSpikeEffect_QF (const vec3_t org) -{ - R_RunParticleEffect_QF (org, vec3_origin, 0, 20); -} - -static void -R_KnightSpikeEffect_QF (const vec3_t org) -{ - R_RunParticleEffect_QF (org, vec3_origin, 226, 20); -} - -static void -R_WizSpikeEffect_QF (const vec3_t org) -{ - R_RunParticleEffect_QF (org, vec3_origin, 20, 30); -} - -static void -R_BloodPuffEffect_QF (const vec3_t org, int count) -{ - R_RunParticleEffect_QF (org, vec3_origin, 73, count); -} - -static void -R_GunshotEffect_QF (const vec3_t org, int count) -{ - R_RunParticleEffect_QF (org, vec3_origin, 0, count); -} - -static void -R_LightningBloodEffect_QF (const vec3_t org) -{ - R_RunParticleEffect_QF (org, vec3_origin, 225, 50); -} - -static void -R_RocketTrail_QF (entity_t *ent) -{ - float len; - int j; - particle_t *p; - vec3_t old_origin, vec; - - if (!r_particles->int_val) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, ent->old_origin, vec); - len = VectorNormalize (vec); - - while (len > 0) { - len -= 3; - - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - VectorZero (p->vel); - - p->die = vr_data.realtime + 2; - p->ramp = (mtwist_rand (&mt) & 3); - p->color = ramp3[(int) p->ramp]; - p->physics = R_ParticlePhysics ("pt_fire"); - for (j = 0; j < 3; j++) - p->org[j] = old_origin[j] + ((mtwist_rand (&mt) % 6) - 3); - - VectorAdd (old_origin, vec, old_origin); - } -} - -static void -R_GrenadeTrail_QF (entity_t *ent) -{ - float len; - int j; - particle_t *p; - vec3_t old_origin, vec; - - if (!r_particles->int_val) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - len = VectorNormalize (vec); - - while (len > 0) { - len -= 3; - - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - VectorZero (p->vel); - - p->die = vr_data.realtime + 2; - p->ramp = (mtwist_rand (&mt) & 3) + 2; - p->color = ramp3[(int) p->ramp]; - p->physics = R_ParticlePhysics ("pt_fire"); - for (j = 0; j < 3; j++) - p->org[j] = old_origin[j] + ((mtwist_rand (&mt) % 6) - 3); - - VectorAdd (old_origin, vec, old_origin); - } -} - -static void -R_BloodTrail_QF (entity_t *ent) -{ - float len; - int j; - particle_t *p; - vec3_t old_origin, vec; - - if (!r_particles->int_val) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - len = VectorNormalize (vec); - - while (len > 0) { - len -= 3; - - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - VectorZero (p->vel); - - p->die = vr_data.realtime + 2; - p->physics = R_ParticlePhysics ("pt_slowgrav"); - p->color = 67 + (mtwist_rand (&mt) & 3); - for (j = 0; j < 3; j++) - p->org[j] = old_origin[j] + ((mtwist_rand (&mt) % 6) - 3); - break; - - VectorAdd (old_origin, vec, old_origin); - } -} - -static void -R_SlightBloodTrail_QF (entity_t *ent) -{ - float len; - int j; - particle_t *p; - vec3_t old_origin, vec; - - if (!r_particles->int_val) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - len = VectorNormalize (vec); - - while (len > 0) { - len -= 6; - - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - VectorZero (p->vel); - - p->die = vr_data.realtime + 2; - p->physics = R_ParticlePhysics ("pt_slowgrav"); - p->color = 67 + (mtwist_rand (&mt) & 3); - for (j = 0; j < 3; j++) - p->org[j] = old_origin[j] + ((mtwist_rand (&mt) % 6) - 3); - - VectorAdd (old_origin, vec, old_origin); - } -} - -static void -R_WizTrail_QF (entity_t *ent) -{ - float len; - particle_t *p; - vec3_t old_origin, vec; - - if (!r_particles->int_val) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - len = VectorNormalize (vec); - - while (len > 0) { - static int tracercount; - - len -= 3; - - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = vr_data.realtime + 0.5; - p->physics = R_ParticlePhysics ("pt_static"); - p->color = 52 + ((tracercount & 4) << 1); - - tracercount++; - - VectorCopy (old_origin, p->org); - if (tracercount & 1) { - p->vel[0] = 30.0 * vec[1]; - p->vel[1] = 30.0 * -vec[0]; - } else { - p->vel[0] = 30.0 * -vec[1]; - p->vel[1] = 30.0 * vec[0]; - } - p->vel[2] = 0.0; - - VectorAdd (old_origin, vec, old_origin); - } -} - -static void -R_FlameTrail_QF (entity_t *ent) -{ - float len; - particle_t *p; - vec3_t old_origin, vec; - - if (!r_particles->int_val) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - len = VectorNormalize (vec); - - while (len > 0) { - static int tracercount; - - len -= 3; - - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - p->die = vr_data.realtime + 0.5; - p->physics = R_ParticlePhysics ("pt_static"); - p->color = 230 + ((tracercount & 4) << 1); - - tracercount++; - - VectorCopy (old_origin, p->org); - if (tracercount & 1) { - p->vel[0] = 30.0 * vec[1]; - p->vel[1] = 30.0 * -vec[0]; - } else { - p->vel[0] = 30.0 * -vec[1]; - p->vel[1] = 30.0 * vec[0]; - } - p->vel[2] = 0.0; - - VectorAdd (old_origin, vec, old_origin); - } -} - -static void -R_VoorTrail_QF (entity_t *ent) -{ - float len; - int j; - particle_t *p; - vec3_t old_origin, vec; - - if (!r_particles->int_val) - return; - - VectorCopy (ent->old_origin, old_origin); - VectorSubtract (ent->origin, old_origin, vec); - len = VectorNormalize (vec); - - while (len > 0) { - len -= 3; - - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - VectorZero (p->vel); - - p->die = vr_data.realtime + 0.3; - p->physics = R_ParticlePhysics ("pt_static"); - p->color = 9 * 16 + 8 + (mtwist_rand (&mt) & 3); - for (j = 0; j < 3; j++) - p->org[j] = old_origin[j] + ((mtwist_rand (&mt) & 15) - 8); - - VectorAdd (old_origin, vec, old_origin); - } -} - -void -sw32_R_DrawParticles (void) -{ - particle_t *p, **particle; - - VectorScale (vright, sw32_xscaleshrink, r_pright); - VectorScale (vup, sw32_yscaleshrink, r_pup); - VectorCopy (vpn, r_ppn); - - for (particle = &active_particles; *particle;) { - if ((*particle)->die < vr_data.realtime) { - p = (*particle)->next; - (*particle)->next = free_particles; - free_particles = (*particle); - (*particle) = p; - } else { - p = *particle; - particle = &(*particle)->next; - - sw32_D_DrawParticle (p); - - R_RunParticlePhysics (p); - } - } -} - -void -sw32_r_easter_eggs_f (cvar_t *var) -{ -} - -void -sw32_r_particles_style_f (cvar_t *var) -{ -} - -static vid_particle_funcs_t particles_QF = { - R_RocketTrail_QF, - R_GrenadeTrail_QF, - R_BloodTrail_QF, - R_SlightBloodTrail_QF, - R_WizTrail_QF, - R_FlameTrail_QF, - R_VoorTrail_QF, - 0,//R_GlowTrail_QF, - R_RunParticleEffect_QF, - R_BloodPuffEffect_QF, - R_GunshotEffect_QF, - R_LightningBloodEffect_QF, - R_SpikeEffect_QF, - R_KnightSpikeEffect_QF, - R_SuperSpikeEffect_QF, - R_WizSpikeEffect_QF, - R_BlobExplosion_QF, - R_ParticleExplosion_QF, - R_ParticleExplosion2_QF, - R_LavaSplash_QF, - R_TeleportSplash_QF, - R_DarkFieldParticles_ID, - R_EntityParticles_ID, - R_Particle_New, - R_Particle_NewRandom, -}; - -static void -R_ParticleFunctionInit (void) -{ - sw32_vid_render_funcs.particles = &particles_QF; -} - -static void -r_particles_nearclip_f (cvar_t *var) -{ - Cvar_SetValue (r_particles_nearclip, bound (r_nearclip->value, var->value, - r_farclip->value)); -} - -static void -r_particles_f (cvar_t *var) -{ - R_MaxParticlesCheck (var, r_particles_max); -} - -static void -r_particles_max_f (cvar_t *var) -{ - R_MaxParticlesCheck (r_particles, var); -} - -void -sw32_R_Particles_Init_Cvars (void) -{ - easter_eggs = Cvar_Get ("easter_eggs", "0", CVAR_NONE, r_easter_eggs_f, - "Enables easter eggs."); - r_particles = Cvar_Get ("r_particles", "1", CVAR_ARCHIVE, r_particles_f, - "Toggles drawing of particles."); - r_particles_max = Cvar_Get ("r_particles_max", "2048", CVAR_ARCHIVE, - r_particles_max_f, "Maximum amount of " - "particles to display. No maximum, minimum " - "is 0."); - r_particles_nearclip = Cvar_Get ("r_particles_nearclip", "32", - CVAR_ARCHIVE, r_particles_nearclip_f, - "Distance of the particle near clipping " - "plane from the player."); - r_particles_style = Cvar_Get ("r_particles_style", "1", CVAR_ARCHIVE, - r_particles_style_f, "Sets particle style. " - "0 for Id, 1 for QF."); - R_ParticleFunctionInit (); -} - -void -sw32_R_Particle_New (const char *type, int texnum, const vec3_t org, - float scale, const vec3_t vel, float die, int color, - float alpha, float ramp) -{ - particle_t *p; - - if (!free_particles) - return; - p = free_particles; - free_particles = p->next; - p->next = active_particles; - active_particles = p; - - VectorCopy (org, p->org); - p->color = color; - p->tex = texnum; - p->scale = scale; - p->alpha = alpha; - VectorCopy (vel, p->vel); - p->physics = R_ParticlePhysics (type); - p->die = die; - p->ramp = ramp; -} - -void -sw32_R_Particle_NewRandom (const char *type, int texnum, const vec3_t org, - int org_fuzz, float scale, int vel_fuzz, float die, - int color, float alpha, float ramp) -{ - float o_fuzz = org_fuzz, v_fuzz = vel_fuzz; - int rnd; - vec3_t porg, pvel; - - rnd = mtwist_rand (&mt); - porg[0] = o_fuzz * ((rnd & 63) - 31.5) / 63.0 + org[0]; - porg[1] = o_fuzz * (((rnd >> 6) & 63) - 31.5) / 63.0 + org[1]; - porg[2] = o_fuzz * (((rnd >> 12) & 63) - 31.5) / 63.0 + org[2]; - rnd = mtwist_rand (&mt); - pvel[0] = v_fuzz * ((rnd & 63) - 31.5) / 63.0; - pvel[1] = v_fuzz * (((rnd >> 6) & 63) - 31.5) / 63.0; - pvel[2] = v_fuzz * (((rnd >> 12) & 63) - 31.5) / 63.0; - - sw32_R_Particle_New (type, texnum, porg, scale, pvel, die, color, alpha, ramp); -} diff --git a/libs/video/renderer/sw32/sw32_rsky.c b/libs/video/renderer/sw32/sw32_rsky.c deleted file mode 100644 index ddc2a42f0..000000000 --- a/libs/video/renderer/sw32/sw32_rsky.c +++ /dev/null @@ -1,223 +0,0 @@ -/* - r_sky.c - - (description) - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#ifdef HAVE_STRING_H -# include "string.h" -#endif -#ifdef HAVE_STRINGS_H -# include "strings.h" -#endif - -#include "QF/sys.h" -#include "QF/render.h" - -#include "r_internal.h" -#include "vid_internal.h" - - -static int iskyspeed = 8; -static int iskyspeed2 = 2; -float sw32_r_skyspeed; - -float sw32_r_skytime; - -byte *sw32_r_skysource; - -int sw32_r_skymade; - -// TODO: clean up these routines - -/* -byte bottomsky[128 * 131]; -byte bottommask[128 * 131]; -byte newsky[128 * 256]; // newsky and topsky both pack in here, - // 128 bytes of newsky on the left of - // each scan, 128 bytes of topsky on - // the right, because the low-level - // drawers need 256-byte scan widths -*/ -static byte skydata[128*256]; // sky layers for making skytex -static byte skytex[128*256*4]; // current sky texture - - -/* - R_InitSky - - A sky texture is 256*128, with the right side being a masked overlay -*/ -void -sw32_R_InitSky (texture_t *mt) -{ - /* - int i, j; - byte *src; - - src = (byte *) mt + mt->offsets[0]; - - for (i = 0; i < 128; i++) { - for (j = 0; j < 128; j++) { - newsky[(i * 256) + j + 128] = src[i * 256 + j + 128]; - } - } - - for (i = 0; i < 128; i++) { - for (j = 0; j < 131; j++) { - if (src[i * 256 + (j & 0x7F)]) { - bottomsky[(i * 131) + j] = src[i * 256 + (j & 0x7F)]; - bottommask[(i * 131) + j] = 0; - } else { - bottomsky[(i * 131) + j] = 0; - bottommask[(i * 131) + j] = 0xff; - } - } - } - - sw32_r_skysource = newsky; - */ - - // LordHavoc: save sky for use - memcpy(skydata, (byte *) mt + mt->offsets[0], 128*256); - sw32_r_skysource = skytex; -} - -void -sw32_R_MakeSky (void) -{ - int x, y, xshift1, yshift1, xshift2, yshift2; - byte *base1, *base2; - static int xlast = -1, ylast = -1; - - xshift2 = sw32_r_skytime * sw32_r_skyspeed * 2.0f; - yshift2 = sw32_r_skytime * sw32_r_skyspeed * 2.0f; - - if ((xshift2 == xlast) && (yshift2 == ylast)) - return; - - xlast = xshift2; - ylast = yshift2; - xshift1 = xshift2 >> 1; - yshift1 = yshift2 >> 1; - - switch(sw32_r_pixbytes) - { - case 1: - { - byte *out = (byte *) skytex; - for (y = 0;y < 128;y++) - { - base1 = &skydata[((y + yshift1) & 127) * 256]; - base2 = &skydata[((y + yshift2) & 127) * 256 + 128]; - for (x = 0;x < 128;x++) - { - if (base1[(x + xshift1) & 127]) - *out = base1[(x + xshift1) & 127]; - else - *out = base2[(x + xshift2) & 127]; - out++; - } - out += 128; - } - } - break; - case 2: - { - unsigned short *out = (unsigned short *) skytex; - for (y = 0;y < 128;y++) - { - base1 = &skydata[((y + yshift1) & 127) * 256]; - base2 = &skydata[((y + yshift2) & 127) * 256 + 128]; - for (x = 0;x < 128;x++) - { - if (base1[(x + xshift1) & 127]) - *out = sw32_8to16table[base1[(x + xshift1) & 127]]; - else - *out = sw32_8to16table[base2[(x + xshift2) & 127]]; - out++; - } - out += 128; - } - } - break; - case 4: - { - unsigned int *out = (unsigned int *) skytex; - for (y = 0;y < 128;y++) - { - base1 = &skydata[((y + yshift1) & 127) * 256]; - base2 = &skydata[((y + yshift2) & 127) * 256 + 128]; - for (x = 0;x < 128;x++) - { - if (base1[(x + xshift1) & 127]) - *out = d_8to24table[base1[(x + xshift1) & 127]]; - else - *out = d_8to24table[base2[(x + xshift2) & 127]]; - out++; - } - out += 128; - } - } - break; - default: - Sys_Error("R_MakeSky: unsupported r_pixbytes %i", sw32_r_pixbytes); - } - sw32_r_skymade = 1; -} - - -void -sw32_R_SetSkyFrame (void) -{ - int g, s1, s2; - float temp; - - sw32_r_skyspeed = iskyspeed; - - g = GreatestCommonDivisor (iskyspeed, iskyspeed2); - s1 = iskyspeed / g; - s2 = iskyspeed2 / g; - temp = SKYSIZE * s1 * s2; - - sw32_r_skytime = vr_data.realtime - ((int) (vr_data.realtime / temp) * temp); - - sw32_r_skymade = 0; -} - - -/* - Stub function for loading a skybox. Currently we have support for - skyboxes only in GL targets, so we just do nothing here. --KB -*/ -void -sw32_R_LoadSkys (const char *name) -{ -} diff --git a/libs/video/renderer/sw32/sw32_rsprite.c b/libs/video/renderer/sw32/sw32_rsprite.c deleted file mode 100644 index 92be9b51e..000000000 --- a/libs/video/renderer/sw32/sw32_rsprite.c +++ /dev/null @@ -1,383 +0,0 @@ -/* - sw32_rsprite.c - - (description) - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif - -#include - -#include "QF/render.h" -#include "QF/sys.h" - -#include "r_internal.h" - -static int clip_current; -static vec5_t clip_verts[2][MAXWORKINGVERTS]; -static int sprite_width, sprite_height; - -spritedesc_t sw32_r_spritedesc; - - -static void -R_RotateSprite (float beamlength) -{ - vec3_t vec; - - if (beamlength == 0.0) - return; - - VectorScale (sw32_r_spritedesc.vpn, -beamlength, vec); - VectorAdd (r_entorigin, vec, r_entorigin); - VectorSubtract (modelorg, vec, modelorg); -} - - -/* - R_ClipSpriteFace - - Clips the winding at clip_verts[clip_current] and changes clip_current - Throws out the back side -*/ -static int -R_ClipSpriteFace (int nump, clipplane_t *pclipplane) -{ - int i, outcount; - float dists[MAXWORKINGVERTS + 1]; - float frac, clipdist, *pclipnormal; - float *in, *instep, *outstep, *vert2; - - clipdist = pclipplane->dist; - pclipnormal = pclipplane->normal; - - // calc dists - if (clip_current) { - in = clip_verts[1][0]; - outstep = clip_verts[0][0]; - clip_current = 0; - } else { - in = clip_verts[0][0]; - outstep = clip_verts[1][0]; - clip_current = 1; - } - - instep = in; - for (i = 0; i < nump; i++, instep += sizeof (vec5_t) / sizeof (float)) { - dists[i] = DotProduct (instep, pclipnormal) - clipdist; - } - - // handle wraparound case - dists[nump] = dists[0]; - memcpy (instep, in, sizeof (vec5_t)); - - // clip the winding - instep = in; - outcount = 0; - - for (i = 0; i < nump; i++, instep += sizeof (vec5_t) / sizeof (float)) { - if (dists[i] >= 0) { - memcpy (outstep, instep, sizeof (vec5_t)); - outstep += sizeof (vec5_t) / sizeof (float); - outcount++; - } - - if (dists[i] == 0 || dists[i + 1] == 0) - continue; - - if ((dists[i] > 0) == (dists[i + 1] > 0)) - continue; - - // split it into a new vertex - frac = dists[i] / (dists[i] - dists[i + 1]); - - vert2 = instep + sizeof (vec5_t) / sizeof (float); - - outstep[0] = instep[0] + frac * (vert2[0] - instep[0]); - outstep[1] = instep[1] + frac * (vert2[1] - instep[1]); - outstep[2] = instep[2] + frac * (vert2[2] - instep[2]); - outstep[3] = instep[3] + frac * (vert2[3] - instep[3]); - outstep[4] = instep[4] + frac * (vert2[4] - instep[4]); - - outstep += sizeof (vec5_t) / sizeof (float); - - outcount++; - } - - return outcount; -} - - -static void -R_SetupAndDrawSprite (void) -{ - int i, nump; - float dot, scale, *pv; - vec5_t *pverts; - vec3_t left, up, right, down, transformed, local; - emitpoint_t outverts[MAXWORKINGVERTS + 1], *pout; - - dot = DotProduct (sw32_r_spritedesc.vpn, modelorg); - - // backface cull - if (dot >= 0) - return; - - // build the sprite poster in worldspace - VectorScale (sw32_r_spritedesc.vright, sw32_r_spritedesc.pspriteframe->right, right); - VectorScale (sw32_r_spritedesc.vup, sw32_r_spritedesc.pspriteframe->up, up); - VectorScale (sw32_r_spritedesc.vright, sw32_r_spritedesc.pspriteframe->left, left); - VectorScale (sw32_r_spritedesc.vup, sw32_r_spritedesc.pspriteframe->down, down); - - pverts = clip_verts[0]; - - pverts[0][0] = r_entorigin[0] + up[0] + left[0]; - pverts[0][1] = r_entorigin[1] + up[1] + left[1]; - pverts[0][2] = r_entorigin[2] + up[2] + left[2]; - pverts[0][3] = 0; - pverts[0][4] = 0; - - pverts[1][0] = r_entorigin[0] + up[0] + right[0]; - pverts[1][1] = r_entorigin[1] + up[1] + right[1]; - pverts[1][2] = r_entorigin[2] + up[2] + right[2]; - pverts[1][3] = sprite_width; - pverts[1][4] = 0; - - pverts[2][0] = r_entorigin[0] + down[0] + right[0]; - pverts[2][1] = r_entorigin[1] + down[1] + right[1]; - pverts[2][2] = r_entorigin[2] + down[2] + right[2]; - pverts[2][3] = sprite_width; - pverts[2][4] = sprite_height; - - pverts[3][0] = r_entorigin[0] + down[0] + left[0]; - pverts[3][1] = r_entorigin[1] + down[1] + left[1]; - pverts[3][2] = r_entorigin[2] + down[2] + left[2]; - pverts[3][3] = 0; - pverts[3][4] = sprite_height; - - // clip to the frustum in worldspace - nump = 4; - clip_current = 0; - - for (i = 0; i < 4; i++) { - nump = R_ClipSpriteFace (nump, &sw32_view_clipplanes[i]); - if (nump < 3) - return; - if (nump >= MAXWORKINGVERTS) - Sys_Error ("R_SetupAndDrawSprite: too many points"); - } - - // transform vertices into viewspace and project - pv = &clip_verts[clip_current][0][0]; - sw32_r_spritedesc.nearzi = -999999; - - for (i = 0; i < nump; i++) { - VectorSubtract (pv, r_origin, local); - sw32_TransformVector (local, transformed); - - if (transformed[2] < NEAR_CLIP) - transformed[2] = NEAR_CLIP; - - pout = &outverts[i]; - pout->zi = 1.0 / transformed[2]; - if (pout->zi > sw32_r_spritedesc.nearzi) - sw32_r_spritedesc.nearzi = pout->zi; - - pout->s = pv[3]; - pout->t = pv[4]; - - scale = sw32_xscale * pout->zi; - pout->u = (sw32_xcenter + scale * transformed[0]); - - scale = sw32_yscale * pout->zi; - pout->v = (sw32_ycenter - scale * transformed[1]); - - pv += sizeof (vec5_t) / sizeof (*pv); - } - - // draw it - sw32_r_spritedesc.nump = nump; - sw32_r_spritedesc.pverts = outverts; - sw32_D_DrawSprite (); -} - - -static mspriteframe_t * -R_GetSpriteframe (msprite_t *psprite) -{ - mspritegroup_t *pspritegroup; - mspriteframe_t *pspriteframe; - int i, numframes, frame; - float *pintervals, fullinterval, targettime, time; - - frame = currententity->frame; - - if ((frame >= psprite->numframes) || (frame < 0)) { - Sys_Printf ("R_DrawSprite: no such frame %d\n", frame); - frame = 0; - } - - if (psprite->frames[frame].type == SPR_SINGLE) { - pspriteframe = psprite->frames[frame].frameptr; - } else { - pspritegroup = (mspritegroup_t *) psprite->frames[frame].frameptr; - pintervals = pspritegroup->intervals; - numframes = pspritegroup->numframes; - fullinterval = pintervals[numframes - 1]; - - time = vr_data.realtime + currententity->syncbase; - - // when loading in Mod_LoadSpriteGroup, we guaranteed all interval - // values are positive, so we don't have to worry about division by 0 - targettime = time - ((int) (time / fullinterval)) * fullinterval; - - for (i = 0; i < (numframes - 1); i++) { - if (pintervals[i] > targettime) - break; - } - - pspriteframe = pspritegroup->frames[i]; - } - - return pspriteframe; -} - - -void -sw32_R_DrawSprite (void) -{ - int i; - msprite_t *psprite; - vec3_t tvec; - float dot, angle, sr, cr; - - psprite = currententity->model->cache.data; - - sw32_r_spritedesc.pspriteframe = R_GetSpriteframe (psprite); - - sprite_width = sw32_r_spritedesc.pspriteframe->width; - sprite_height = sw32_r_spritedesc.pspriteframe->height; - - // TODO: make this caller-selectable - if (psprite->type == SPR_FACING_UPRIGHT) { - // generate the sprite's axes, with vup straight up in worldspace, and - // sw32_r_spritedesc.vright perpendicular to modelorg. - // This will not work if the view direction is very close to straight - // up or down, because the cross product will be between two nearly - // parallel vectors and starts to approach an undefined state, so we - // don't draw if the two vectors are less than 1 degree apart - tvec[0] = -modelorg[0]; - tvec[1] = -modelorg[1]; - tvec[2] = -modelorg[2]; - VectorNormalize (tvec); - dot = tvec[2]; // same as DotProduct (tvec, - // sw32_r_spritedesc.vup) because - // sw32_r_spritedesc.vup is 0, 0, 1 - if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = - // 0.999848 - return; - sw32_r_spritedesc.vup[0] = 0; - sw32_r_spritedesc.vup[1] = 0; - sw32_r_spritedesc.vup[2] = 1; - sw32_r_spritedesc.vright[0] = tvec[1]; - //CrossProduct(sw32_r_spritedesc.vup, -modelorg, sw32_r_spritedesc.vright) - sw32_r_spritedesc.vright[1] = -tvec[0]; - sw32_r_spritedesc.vright[2] = 0; - VectorNormalize (sw32_r_spritedesc.vright); - sw32_r_spritedesc.vpn[0] = -sw32_r_spritedesc.vright[1]; - sw32_r_spritedesc.vpn[1] = sw32_r_spritedesc.vright[0]; - sw32_r_spritedesc.vpn[2] = 0; - //CrossProduct (sw32_r_spritedesc.vright, sw32_r_spritedesc.vup, sw32_r_spritedesc.vpn) - } else if (psprite->type == SPR_VP_PARALLEL) { - // generate the sprite's axes, completely parallel to the viewplane. - // There are no problem situations, because the sprite is always in the - // same position relative to the viewer - for (i = 0; i < 3; i++) { - sw32_r_spritedesc.vup[i] = vup[i]; - sw32_r_spritedesc.vright[i] = vright[i]; - sw32_r_spritedesc.vpn[i] = vpn[i]; - } - } else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT) { - // generate the sprite's axes, with vup straight up in worldspace, and - // sw32_r_spritedesc.vright parallel to the viewplane. - // This will not work if the view direction is very close to straight - // up or down, because the cross product will be between two nearly - // parallel vectors and starts to approach an undefined state, so we - // don't draw if the two vectors are less than 1 degree apart - dot = vpn[2]; // same as DotProduct (vpn, - // sw32_r_spritedesc.vup) because - // sw32_r_spritedesc.vup is 0, 0, 1 - if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = - // 0.999848 - return; - sw32_r_spritedesc.vup[0] = 0; - sw32_r_spritedesc.vup[1] = 0; - sw32_r_spritedesc.vup[2] = 1; - sw32_r_spritedesc.vright[0] = vpn[1]; - //CrossProduct (sw32_r_spritedesc.vup, vpn, - sw32_r_spritedesc.vright[1] = -vpn[0]; // sw32_r_spritedesc.vright) - sw32_r_spritedesc.vright[2] = 0; - VectorNormalize (sw32_r_spritedesc.vright); - sw32_r_spritedesc.vpn[0] = -sw32_r_spritedesc.vright[1]; - sw32_r_spritedesc.vpn[1] = sw32_r_spritedesc.vright[0]; - sw32_r_spritedesc.vpn[2] = 0; - //CrossProduct (sw32_r_spritedesc.vright, sw32_r_spritedesc.vup, sw32_r_spritedesc.vpn) - } else if (psprite->type == SPR_ORIENTED) { - // generate the sprite's axes, according to the sprite's world - // orientation - VectorCopy (currententity->transform + 0, sw32_r_spritedesc.vpn); - VectorNegate (currententity->transform + 4, sw32_r_spritedesc.vright); - VectorCopy (currententity->transform + 8, sw32_r_spritedesc.vup); - } else if (psprite->type == SPR_VP_PARALLEL_ORIENTED) { - // generate the sprite's axes, parallel to the viewplane, but rotated - // in that plane around the center according to the sprite entity's - // roll angle. So vpn stays the same, but vright and vup rotate - angle = currententity->angles[ROLL] * (M_PI * 2 / 360); - sr = sin (angle); - cr = cos (angle); - - for (i = 0; i < 3; i++) { - sw32_r_spritedesc.vpn[i] = vpn[i]; - sw32_r_spritedesc.vright[i] = vright[i] * cr + vup[i] * sr; - sw32_r_spritedesc.vup[i] = vright[i] * -sr + vup[i] * cr; - } - } else { - Sys_Error ("R_DrawSprite: Bad sprite type %d", psprite->type); - } - - R_RotateSprite (psprite->beamlength); - - R_SetupAndDrawSprite (); -} diff --git a/libs/video/renderer/sw32/sw32_rsurf.c b/libs/video/renderer/sw32/sw32_rsurf.c deleted file mode 100644 index 9badf0c96..000000000 --- a/libs/video/renderer/sw32/sw32_rsurf.c +++ /dev/null @@ -1,890 +0,0 @@ -/* - sw32_rsurf.c - - surface-related refresh code - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "namehack.h" - -#include "QF/render.h" -#include "QF/sys.h" - -#include "compat.h" -#include "r_internal.h" - -drawsurf_t sw32_r_drawsurf; - -static int lightleft, blocksize, sourcetstep; -static int lightright, lightleftstep, lightrightstep, blockdivshift; -static unsigned int blockdivmask; -static byte *prowdestbase; -static byte *psource; -static int surfrowbytes; -static int *r_lightptr; -static int r_stepback; -static int r_lightwidth; -static int r_numhblocks, r_numvblocks; -static byte *r_source, *r_sourcemax; - -static void R_DrawSurfaceBlock8_mip0 (void); -static void R_DrawSurfaceBlock8_mip1 (void); -static void R_DrawSurfaceBlock8_mip2 (void); -static void R_DrawSurfaceBlock8_mip3 (void); -static void R_DrawSurfaceBlock16_mip0 (void); -static void R_DrawSurfaceBlock16_mip1 (void); -static void R_DrawSurfaceBlock16_mip2 (void); -static void R_DrawSurfaceBlock16_mip3 (void); -static void R_DrawSurfaceBlock32_mip0 (void); -static void R_DrawSurfaceBlock32_mip1 (void); -static void R_DrawSurfaceBlock32_mip2 (void); -static void R_DrawSurfaceBlock32_mip3 (void); - -static void (*surfmiptable8[4]) (void) = { - R_DrawSurfaceBlock8_mip0, - R_DrawSurfaceBlock8_mip1, - R_DrawSurfaceBlock8_mip2, - R_DrawSurfaceBlock8_mip3 -}; - -static void (*surfmiptable16[4]) (void) = { - R_DrawSurfaceBlock16_mip0, - R_DrawSurfaceBlock16_mip1, - R_DrawSurfaceBlock16_mip2, - R_DrawSurfaceBlock16_mip3 -}; - -static void (*surfmiptable32[4]) (void) = { - R_DrawSurfaceBlock32_mip0, - R_DrawSurfaceBlock32_mip1, - R_DrawSurfaceBlock32_mip2, - R_DrawSurfaceBlock32_mip3 -}; - -static int blocklights[34 * 34]; //FIXME make dynamic - - -static void -R_AddDynamicLights (void) -{ - msurface_t *surf; - unsigned int lnum; - int sd, td; - float dist, rad, minlight; - vec3_t impact, local, lightorigin; - int s, t; - int i; - int smax, tmax; - mtexinfo_t *tex; - - surf = sw32_r_drawsurf.surf; - smax = (surf->extents[0] >> 4) + 1; - tmax = (surf->extents[1] >> 4) + 1; - tex = surf->texinfo; - - for (lnum = 0; lnum < r_maxdlights; lnum++) { - if (!(surf->dlightbits[lnum / 32] & (1 << (lnum % 32)))) - continue; // not lit by this light - - VectorSubtract (r_dlights[lnum].origin, currententity->origin, - lightorigin); - rad = r_dlights[lnum].radius; - dist = DotProduct (lightorigin, surf->plane->normal) - - surf->plane->dist; - rad -= fabs (dist); - minlight = r_dlights[lnum].minlight; - if (rad < minlight) - continue; - minlight = rad - minlight; - - for (i = 0; i < 3; i++) - impact[i] = lightorigin[i] - surf->plane->normal[i] * dist; - - local[0] = DotProduct (impact, tex->vecs[0]) + tex->vecs[0][3]; - local[1] = DotProduct (impact, tex->vecs[1]) + tex->vecs[1][3]; - - local[0] -= surf->texturemins[0]; - local[1] -= surf->texturemins[1]; - - for (t = 0; t < tmax; t++) { - td = local[1] - t * 16; - if (td < 0) - td = -td; - for (s = 0; s < smax; s++) { - sd = local[0] - s * 16; - if (sd < 0) - sd = -sd; - if (sd > td) - dist = sd + (td >> 1); - else - dist = td + (sd >> 1); - if (dist < minlight) - blocklights[t * smax + s] += (rad - dist) * 256; - } - } - } -} - -/* - R_BuildLightMap - - Combine and scale multiple lightmaps into the 8.8 format in blocklights -*/ -static void -R_BuildLightMap (void) -{ - int smax, tmax; - int t; - int i, size; - byte *lightmap; - unsigned int scale; - int maps; - msurface_t *surf; - - surf = sw32_r_drawsurf.surf; - - smax = (surf->extents[0] >> 4) + 1; - tmax = (surf->extents[1] >> 4) + 1; - size = smax * tmax; - lightmap = surf->samples; - - if (!r_worldentity.model->lightdata) { - for (i = 0; i < size; i++) - blocklights[i] = 0; - return; - } - // clear to ambient - for (i = 0; i < size; i++) - blocklights[i] = r_refdef.ambientlight << 8; - - // add all the lightmaps - if (lightmap) - for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) { - scale = sw32_r_drawsurf.lightadj[maps]; // 8.8 fraction - for (i = 0; i < size; i++) - blocklights[i] += lightmap[i] * scale; - lightmap += size; // skip to next lightmap - } - // add all the dynamic lights - if (surf->dlightframe == r_framecount) - R_AddDynamicLights (); - - /* - * JohnnyonFlame: - * 32 and 16bpp modes uses the positive lighting, unlike 8bpp - */ - switch (sw32_r_pixbytes) { - case 1: - // bound, invert, and shift - for (i = 0; i < size; i++) { - t = (255 * 256 - blocklights[i]) >> (8 - VID_CBITS); - - if (t < (1 << 6)) - t = (1 << 6); - - blocklights[i] = t; - } - break; - default: - // LordHavoc: changed to positive (not inverse) lighting - for (i = 0; i < size; i++) { - t = bound(256, blocklights[i] >> (8 - VID_CBITS), - 256 * (VID_GRADES - 1)); - blocklights[i] = t; - } - break; - } -} - -void -sw32_R_DrawSurface (void) -{ - byte *basetptr; - int smax, tmax, twidth; - int u; - int soffset, basetoffset, texwidth; - int horzblockstep; - byte *pcolumndest; - void (*pblockdrawer) (void); - texture_t *mt; - - // calculate the lightings - R_BuildLightMap (); - - surfrowbytes = sw32_r_drawsurf.rowbytes; - - mt = sw32_r_drawsurf.texture; - - r_source = (byte *) mt + mt->offsets[sw32_r_drawsurf.surfmip]; - - // the fractional light values should range from 0 to - // (VID_GRADES - 1) << 16 from a source range of 0 - 255 - - texwidth = mt->width >> sw32_r_drawsurf.surfmip; - - blocksize = 16 >> sw32_r_drawsurf.surfmip; - blockdivshift = 4 - sw32_r_drawsurf.surfmip; - blockdivmask = (1 << blockdivshift) - 1; - - r_lightwidth = (sw32_r_drawsurf.surf->extents[0] >> 4) + 1; - - r_numhblocks = sw32_r_drawsurf.surfwidth >> blockdivshift; - r_numvblocks = sw32_r_drawsurf.surfheight >> blockdivshift; - -//============================== - - smax = mt->width >> sw32_r_drawsurf.surfmip; - twidth = texwidth; - tmax = mt->height >> sw32_r_drawsurf.surfmip; - sourcetstep = texwidth; - r_stepback = tmax * twidth; - - soffset = sw32_r_drawsurf.surf->texturemins[0]; - basetoffset = sw32_r_drawsurf.surf->texturemins[1]; - - switch (sw32_r_pixbytes) { - case 1: - pblockdrawer = surfmiptable8[sw32_r_drawsurf.surfmip]; - break; - case 2: - pblockdrawer = surfmiptable16[sw32_r_drawsurf.surfmip]; - break; - case 4: - pblockdrawer = surfmiptable32[sw32_r_drawsurf.surfmip]; - break; - default: - Sys_Error("R_DrawSurface: unsupported r_pixbytes %i", sw32_r_pixbytes); - pblockdrawer = NULL; - } - - horzblockstep = blocksize * sw32_r_pixbytes; - - r_sourcemax = r_source + (tmax * smax); - - // << 16 components are to guarantee positive values for % - basetptr = r_source + (((basetoffset >> sw32_r_drawsurf.surfmip) + - (tmax << 16)) % tmax) * twidth; - soffset = (((soffset >> sw32_r_drawsurf.surfmip) + (smax << 16)) % smax); - - pcolumndest = (byte *) sw32_r_drawsurf.surfdat; - - for (u = 0; u < r_numhblocks; u++) { - r_lightptr = blocklights + u; - - prowdestbase = pcolumndest; - - psource = basetptr + soffset; - - (*pblockdrawer) (); - - soffset = soffset + blocksize; - if (soffset >= smax) - soffset = 0; - - pcolumndest += horzblockstep; - } -} - -//============================================================================= - -static void -R_DrawSurfaceBlock8_mip0 (void) -{ - int v, i, b, lightstep, light; - unsigned char pix, *prowdest; - - prowdest = prowdestbase; - - for (v = 0; v < r_numvblocks; v++) { - // FIXME: make these locals? - // FIXME: use delta rather than both right and left, like ASM? - lightleft = r_lightptr[0]; - lightright = r_lightptr[1]; - r_lightptr += r_lightwidth; - lightleftstep = (r_lightptr[0] - lightleft) >> 4; - lightrightstep = (r_lightptr[1] - lightright) >> 4; - - for (i = 0; i < 16; i++) { - lightstep = (lightleft - lightright) >> 4; - - light = lightright; - - for (b = 15; b >= 0; b--) { - pix = psource[b]; - prowdest[b] = vid.colormap8[(light & 0xFF00) + pix]; - light += lightstep; - } - - psource += sourcetstep; - lightright += lightrightstep; - lightleft += lightleftstep; - prowdest += surfrowbytes; - } - - if (psource >= r_sourcemax) - psource -= r_stepback; - } -} - -static void -R_DrawSurfaceBlock8_mip1 (void) -{ - int v, i, b, lightstep, light; - unsigned char pix, *prowdest; - - prowdest = prowdestbase; - - for (v = 0; v < r_numvblocks; v++) { - // FIXME: make these locals? - // FIXME: use delta rather than both right and left, like ASM? - lightleft = r_lightptr[0]; - lightright = r_lightptr[1]; - r_lightptr += r_lightwidth; - lightleftstep = (r_lightptr[0] - lightleft) >> 3; - lightrightstep = (r_lightptr[1] - lightright) >> 3; - - for (i = 0; i < 8; i++) { - lightstep = (lightleft - lightright) >> 3; - - light = lightright; - - for (b = 7; b >= 0; b--) { - pix = psource[b]; - prowdest[b] = vid.colormap8[(light & 0xFF00) + pix]; - light += lightstep; - } - - psource += sourcetstep; - lightright += lightrightstep; - lightleft += lightleftstep; - prowdest += surfrowbytes; - } - - if (psource >= r_sourcemax) - psource -= r_stepback; - } -} - -static void -R_DrawSurfaceBlock8_mip2 (void) -{ - int v, i, b, lightstep, light; - unsigned char pix, *prowdest; - - prowdest = prowdestbase; - - for (v = 0; v < r_numvblocks; v++) { - // FIXME: make these locals? - // FIXME: use delta rather than both right and left, like ASM? - lightleft = r_lightptr[0]; - lightright = r_lightptr[1]; - r_lightptr += r_lightwidth; - lightleftstep = (r_lightptr[0] - lightleft) >> 2; - lightrightstep = (r_lightptr[1] - lightright) >> 2; - - for (i = 0; i < 4; i++) { - lightstep = (lightleft - lightright) >> 2; - - light = lightright; - - for (b = 3; b >= 0; b--) { - pix = psource[b]; - prowdest[b] = vid.colormap8[(light & 0xFF00) + pix]; - light += lightstep; - } - - psource += sourcetstep; - lightright += lightrightstep; - lightleft += lightleftstep; - prowdest += surfrowbytes; - } - - if (psource >= r_sourcemax) - psource -= r_stepback; - } -} - -static void -R_DrawSurfaceBlock8_mip3 (void) -{ - int v, i, b, lightstep, light; - unsigned char pix, *prowdest; - - prowdest = prowdestbase; - - for (v = 0; v < r_numvblocks; v++) { - // FIXME: make these locals? - // FIXME: use delta rather than both right and left, like ASM? - lightleft = r_lightptr[0]; - lightright = r_lightptr[1]; - r_lightptr += r_lightwidth; - lightleftstep = (r_lightptr[0] - lightleft) >> 1; - lightrightstep = (r_lightptr[1] - lightright) >> 1; - - for (i = 0; i < 2; i++) { - lightstep = (lightleft - lightright) >> 1; - - light = lightright; - - for (b = 1; b >= 0; b--) { - pix = psource[b]; - prowdest[b] = vid.colormap8[(light & 0xFF00) + pix]; - light += lightstep; - } - - psource += sourcetstep; - lightright += lightrightstep; - lightleft += lightleftstep; - prowdest += surfrowbytes; - } - - if (psource >= r_sourcemax) - psource -= r_stepback; - } -} - -static void -R_DrawSurfaceBlock16_mip0 (void) -{ - int k, v; - int lightstep, light; - unsigned short *prowdest; - - prowdest = (unsigned short *) prowdestbase; - - for (v = 0; v < r_numvblocks; v++) - { - lightleft = r_lightptr[0]; - lightright = r_lightptr[1]; - r_lightptr += r_lightwidth; - lightleftstep = (r_lightptr[0] - lightleft) >> 4; - lightrightstep = (r_lightptr[1] - lightright) >> 4; - - for (k = 0; k < 16; k++) - { - light = lightleft; - lightstep = (lightright - lightleft) >> 4; - - prowdest[0] = vid.colormap16[(light & 0xFF00) + psource[0]]; - light += lightstep; - prowdest[1] = vid.colormap16[(light & 0xFF00) + psource[1]]; - light += lightstep; - prowdest[2] = vid.colormap16[(light & 0xFF00) + psource[2]]; - light += lightstep; - prowdest[3] = vid.colormap16[(light & 0xFF00) + psource[3]]; - light += lightstep; - prowdest[4] = vid.colormap16[(light & 0xFF00) + psource[4]]; - light += lightstep; - prowdest[5] = vid.colormap16[(light & 0xFF00) + psource[5]]; - light += lightstep; - prowdest[6] = vid.colormap16[(light & 0xFF00) + psource[6]]; - light += lightstep; - prowdest[7] = vid.colormap16[(light & 0xFF00) + psource[7]]; - light += lightstep; - prowdest[8] = vid.colormap16[(light & 0xFF00) + psource[8]]; - light += lightstep; - prowdest[9] = vid.colormap16[(light & 0xFF00) + psource[9]]; - light += lightstep; - prowdest[10] = vid.colormap16[(light & 0xFF00) + psource[10]]; - light += lightstep; - prowdest[11] = vid.colormap16[(light & 0xFF00) + psource[11]]; - light += lightstep; - prowdest[12] = vid.colormap16[(light & 0xFF00) + psource[12]]; - light += lightstep; - prowdest[13] = vid.colormap16[(light & 0xFF00) + psource[13]]; - light += lightstep; - prowdest[14] = vid.colormap16[(light & 0xFF00) + psource[14]]; - light += lightstep; - prowdest[15] = vid.colormap16[(light & 0xFF00) + psource[15]]; - - psource += sourcetstep; - lightright += lightrightstep; - lightleft += lightleftstep; - prowdest += (surfrowbytes >> 1); - } - - if (psource >= r_sourcemax) - psource -= r_stepback; - } -} - -static void -R_DrawSurfaceBlock16_mip1 (void) -{ - int k, v; - int lightstep, light; - unsigned short *prowdest; - - prowdest = (unsigned short *) prowdestbase; - - for (v = 0; v < r_numvblocks; v++) - { - lightleft = r_lightptr[0]; - lightright = r_lightptr[1]; - r_lightptr += r_lightwidth; - lightleftstep = (r_lightptr[0] - lightleft) >> 3; - lightrightstep = (r_lightptr[1] - lightright) >> 3; - - for (k = 0; k < 8; k++) - { - light = lightleft; - lightstep = (lightright - lightleft) >> 3; - - prowdest[0] = vid.colormap16[(light & 0xFF00) + psource[0]]; - light += lightstep; - prowdest[1] = vid.colormap16[(light & 0xFF00) + psource[1]]; - light += lightstep; - prowdest[2] = vid.colormap16[(light & 0xFF00) + psource[2]]; - light += lightstep; - prowdest[3] = vid.colormap16[(light & 0xFF00) + psource[3]]; - light += lightstep; - prowdest[4] = vid.colormap16[(light & 0xFF00) + psource[4]]; - light += lightstep; - prowdest[5] = vid.colormap16[(light & 0xFF00) + psource[5]]; - light += lightstep; - prowdest[6] = vid.colormap16[(light & 0xFF00) + psource[6]]; - light += lightstep; - prowdest[7] = vid.colormap16[(light & 0xFF00) + psource[7]]; - - psource += sourcetstep; - lightright += lightrightstep; - lightleft += lightleftstep; - prowdest += (surfrowbytes >> 1); - } - - if (psource >= r_sourcemax) - psource -= r_stepback; - } -} - -static void -R_DrawSurfaceBlock16_mip2 (void) -{ - int k, v; - int lightstep, light; - unsigned short *prowdest; - - prowdest = (unsigned short *) prowdestbase; - - for (v = 0; v < r_numvblocks; v++) - { - lightleft = r_lightptr[0]; - lightright = r_lightptr[1]; - r_lightptr += r_lightwidth; - lightleftstep = (r_lightptr[0] - lightleft) >> 2; - lightrightstep = (r_lightptr[1] - lightright) >> 2; - - for (k = 0; k < 4; k++) - { - light = lightleft; - lightstep = (lightright - lightleft) >> 2; - - prowdest[0] = vid.colormap16[(light & 0xFF00) + psource[0]]; - light += lightstep; - prowdest[1] = vid.colormap16[(light & 0xFF00) + psource[1]]; - light += lightstep; - prowdest[2] = vid.colormap16[(light & 0xFF00) + psource[2]]; - light += lightstep; - prowdest[3] = vid.colormap16[(light & 0xFF00) + psource[3]]; - - psource += sourcetstep; - lightright += lightrightstep; - lightleft += lightleftstep; - prowdest += (surfrowbytes >> 1); - } - - if (psource >= r_sourcemax) - psource -= r_stepback; - } -} - -static void -R_DrawSurfaceBlock16_mip3 (void) -{ - int v; - unsigned short *prowdest; - - prowdest = (unsigned short *) prowdestbase; - - for (v = 0; v < r_numvblocks; v++) - { - lightleft = r_lightptr[0]; - lightright = r_lightptr[1]; - r_lightptr += r_lightwidth; - lightleftstep = (r_lightptr[0] - lightleft) >> 1; - lightrightstep = (r_lightptr[1] - lightright) >> 1; - - prowdest[0] = vid.colormap16[(lightleft & 0xFF00) + psource[0]]; - prowdest[1] = vid.colormap16[(((lightleft + lightright) >> 1) & - 0xFF00) + psource[1]]; - psource += sourcetstep; - lightright += lightrightstep; - lightleft += lightleftstep; - prowdest += (surfrowbytes >> 1); - - prowdest[0] = vid.colormap16[(lightleft & 0xFF00) + psource[0]]; - prowdest[1] = vid.colormap16[(((lightleft + lightright) >> 1) & - 0xFF00) + psource[1]]; - psource += sourcetstep; - prowdest += (surfrowbytes >> 1); - - if (psource >= r_sourcemax) - psource -= r_stepback; - } -} - -static void -R_DrawSurfaceBlock32_mip0 (void) -{ - int k, v; - int lightstep, light; - unsigned int *prowdest; - - prowdest = (unsigned int *) prowdestbase; - - for (v = 0; v < r_numvblocks; v++) - { - lightleft = r_lightptr[0]; - lightright = r_lightptr[1]; - r_lightptr += r_lightwidth; - lightleftstep = (r_lightptr[0] - lightleft) >> 4; - lightrightstep = (r_lightptr[1] - lightright) >> 4; - - for (k = 0; k < 16; k++) - { - light = lightleft; - lightstep = (lightright - lightleft) >> 4; - - prowdest[0] = vid.colormap32[(light & 0xFF00) + psource[0]]; - light += lightstep; - prowdest[1] = vid.colormap32[(light & 0xFF00) + psource[1]]; - light += lightstep; - prowdest[2] = vid.colormap32[(light & 0xFF00) + psource[2]]; - light += lightstep; - prowdest[3] = vid.colormap32[(light & 0xFF00) + psource[3]]; - light += lightstep; - prowdest[4] = vid.colormap32[(light & 0xFF00) + psource[4]]; - light += lightstep; - prowdest[5] = vid.colormap32[(light & 0xFF00) + psource[5]]; - light += lightstep; - prowdest[6] = vid.colormap32[(light & 0xFF00) + psource[6]]; - light += lightstep; - prowdest[7] = vid.colormap32[(light & 0xFF00) + psource[7]]; - light += lightstep; - prowdest[8] = vid.colormap32[(light & 0xFF00) + psource[8]]; - light += lightstep; - prowdest[9] = vid.colormap32[(light & 0xFF00) + psource[9]]; - light += lightstep; - prowdest[10] = vid.colormap32[(light & 0xFF00) + psource[10]]; - light += lightstep; - prowdest[11] = vid.colormap32[(light & 0xFF00) + psource[11]]; - light += lightstep; - prowdest[12] = vid.colormap32[(light & 0xFF00) + psource[12]]; - light += lightstep; - prowdest[13] = vid.colormap32[(light & 0xFF00) + psource[13]]; - light += lightstep; - prowdest[14] = vid.colormap32[(light & 0xFF00) + psource[14]]; - light += lightstep; - prowdest[15] = vid.colormap32[(light & 0xFF00) + psource[15]]; - - psource += sourcetstep; - lightright += lightrightstep; - lightleft += lightleftstep; - prowdest += (surfrowbytes >> 2); - } - - if (psource >= r_sourcemax) - psource -= r_stepback; - } -} - -static void -R_DrawSurfaceBlock32_mip1 (void) -{ - int k, v; - int lightstep, light; - unsigned int *prowdest; - - prowdest = (unsigned int *) prowdestbase; - - for (v = 0; v < r_numvblocks; v++) - { - lightleft = r_lightptr[0]; - lightright = r_lightptr[1]; - r_lightptr += r_lightwidth; - lightleftstep = (r_lightptr[0] - lightleft) >> 3; - lightrightstep = (r_lightptr[1] - lightright) >> 3; - - for (k = 0; k < 8; k++) - { - light = lightleft; - lightstep = (lightright - lightleft) >> 3; - - prowdest[0] = vid.colormap32[(light & 0xFF00) + psource[0]]; - light += lightstep; - prowdest[1] = vid.colormap32[(light & 0xFF00) + psource[1]]; - light += lightstep; - prowdest[2] = vid.colormap32[(light & 0xFF00) + psource[2]]; - light += lightstep; - prowdest[3] = vid.colormap32[(light & 0xFF00) + psource[3]]; - light += lightstep; - prowdest[4] = vid.colormap32[(light & 0xFF00) + psource[4]]; - light += lightstep; - prowdest[5] = vid.colormap32[(light & 0xFF00) + psource[5]]; - light += lightstep; - prowdest[6] = vid.colormap32[(light & 0xFF00) + psource[6]]; - light += lightstep; - prowdest[7] = vid.colormap32[(light & 0xFF00) + psource[7]]; - - psource += sourcetstep; - lightright += lightrightstep; - lightleft += lightleftstep; - prowdest += (surfrowbytes >> 2); - } - - if (psource >= r_sourcemax) - psource -= r_stepback; - } -} - -static void -R_DrawSurfaceBlock32_mip2 (void) -{ - int k, v; - int lightstep, light; - unsigned int *prowdest; - - prowdest = (unsigned int *) prowdestbase; - - for (v = 0; v < r_numvblocks; v++) - { - lightleft = r_lightptr[0]; - lightright = r_lightptr[1]; - r_lightptr += r_lightwidth; - lightleftstep = (r_lightptr[0] - lightleft) >> 2; - lightrightstep = (r_lightptr[1] - lightright) >> 2; - - for (k = 0; k < 4; k++) - { - light = lightleft; - lightstep = (lightright - lightleft) >> 2; - - prowdest[0] = vid.colormap32[(light & 0xFF00) + psource[0]]; - light += lightstep; - prowdest[1] = vid.colormap32[(light & 0xFF00) + psource[1]]; - light += lightstep; - prowdest[2] = vid.colormap32[(light & 0xFF00) + psource[2]]; - light += lightstep; - prowdest[3] = vid.colormap32[(light & 0xFF00) + psource[3]]; - - psource += sourcetstep; - lightright += lightrightstep; - lightleft += lightleftstep; - prowdest += (surfrowbytes >> 2); - } - - if (psource >= r_sourcemax) - psource -= r_stepback; - } -} - -static void -R_DrawSurfaceBlock32_mip3 (void) -{ - int v; - unsigned int *prowdest; - - prowdest = (unsigned int *) prowdestbase; - - for (v = 0; v < r_numvblocks; v++) - { - lightleft = r_lightptr[0]; - lightright = r_lightptr[1]; - r_lightptr += r_lightwidth; - lightleftstep = (r_lightptr[0] - lightleft) >> 1; - lightrightstep = (r_lightptr[1] - lightright) >> 1; - - prowdest[0] = vid.colormap32[(lightleft & 0xFF00) + psource[0]]; - prowdest[1] = vid.colormap32[(((lightleft + lightright) >> 1) & - 0xFF00) + psource[1]]; - psource += sourcetstep; - lightright += lightrightstep; - lightleft += lightleftstep; - prowdest += (surfrowbytes >> 2); - - prowdest[0] = vid.colormap32[(lightleft & 0xFF00) + psource[0]]; - prowdest[1] = vid.colormap32[(((lightleft + lightright) >> 1) & - 0xFF00) + psource[1]]; - psource += sourcetstep; - lightright += lightrightstep; - lightleft += lightleftstep; - prowdest += (surfrowbytes >> 2); - - if (psource >= r_sourcemax) - psource -= r_stepback; - } -} - -/* -void -R_DrawSurfaceBlock32 (void) -{ - int k, v; - int lightstep, light; - unsigned int *prowdest; - - prowdest = prowdestbase; - - for (v = 0; v < r_numvblocks; v++) { - - lightleft = r_lightptr[0]; - lightright = r_lightptr[1]; - r_lightptr += r_lightwidth; - lightleftstep = (r_lightptr[0] - lightleft) >> blockdivshift; - lightrightstep = (r_lightptr[1] - lightright) >> blockdivshift; - - for (k = 0; k < blocksize; k++) { - int b; - - lightstep = (lightright - lightleft) >> blockdivshift; - - light = lightleft; - - for (b = 0;b < blocksize;b++, light += lightstep) - prowdest[b] = vid.colormap32[(light & 0xFF00) + psource[b]]; - - psource += sourcetstep; - lightright += lightrightstep; - lightleft += lightleftstep; - prowdest += (surfrowbytes >> 2); - } - - if (psource >= r_sourcemax) - psource -= r_stepback; - } -} -*/ diff --git a/libs/video/renderer/sw32/vid_common_sw32.c b/libs/video/renderer/sw32/vid_common_sw32.c deleted file mode 100644 index 279ee08d6..000000000 --- a/libs/video/renderer/sw32/vid_common_sw32.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - vid_common_sw32.c - - general video driver functions - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "QF/console.h" -#include "QF/cvar.h" -#include "QF/mathlib.h" -#include "QF/qargs.h" -#include "QF/sys.h" -#include "QF/va.h" -#include "QF/vid.h" - -#include "compat.h" -#include "r_internal.h" -#include "vid_internal.h" - -unsigned short sw32_8to16table[256]; - - -/* - VID_MakeColormap32 - - LordHavoc: makes a 32bit color*light table, RGBA order, no endian, - may need to be re-ordered to hardware at display time -*/ -static void -VID_MakeColormap32 (void *outcolormap, byte *pal) -{ - int c, l; - byte *out; - out = (byte *)&d_8to24table; - - /* - * Generates colors not affected by lighting, such as - * HUD pieces and general sprites (such as explosions) - */ - for (c = 0; c < 256; c++) { - *out++ = pal[c*3+2]; - *out++ = pal[c*3+1]; - *out++ = pal[c*3+0]; - *out++ = 255; - } - d_8to24table[255] = 0; // 255 is transparent - out = (byte *) outcolormap; - - /* - * Generates colors affected by lighting, such as the - * world and other models that give it life, like foes and pickups. - */ - for (l = 0;l < VID_GRADES;l++) - { - for (c = 0;c < vid.fullbright;c++) - { - out[(l*256+c)*4+0] = bound(0, (pal[c*3+2] * l) >> (VID_CBITS - 1), - 255); - out[(l*256+c)*4+1] = bound(0, (pal[c*3+1] * l) >> (VID_CBITS - 1), - 255); - out[(l*256+c)*4+2] = bound(0, (pal[c*3+0] * l) >> (VID_CBITS - 1), - 255); - out[(l*256+c)*4+3] = 255; - } - for (;c < 255;c++) - { - out[(l*256+c)*4+0] = pal[c*3+2]; - out[(l*256+c)*4+1] = pal[c*3+1]; - out[(l*256+c)*4+2] = pal[c*3+0]; - out[(l*256+c)*4+3] = 255; - } - out[(l*256+255)*4+0] = 0; - out[(l*256+255)*4+1] = 0; - out[(l*256+255)*4+2] = 0; - out[(l*256+255)*4+3] = 0; - } -} - -static unsigned short -lh24to16bit (int red, int green, int blue) -{ - red = bound(0, red, 255); - green = bound(0, green, 255); - blue = bound(0, blue, 255); - red >>= 3; - green >>= 2; - blue >>= 3; - red <<= 11; - green <<= 5; - return (unsigned short) (red | green | blue); -} - -/* - VID_MakeColormap16 - - LordHavoc: makes a 16bit color*light table, RGB order, native endian, - may need to be translated to hardware order at display time -*/ -static void -VID_MakeColormap16 (void *outcolormap, byte *pal) -{ - int c, l; - unsigned short *out; - out = (unsigned short *)&sw32_8to16table; - for (c = 0; c < 256; c++) - *out++ = lh24to16bit(pal[c*3+0], pal[c*3+1], pal[c*3+2]); - sw32_8to16table[255] = 0; // 255 is transparent - out = (unsigned short *) outcolormap; - for (l = 0;l < VID_GRADES;l++) - { - for (c = 0;c < vid.fullbright;c++) - out[l*256+c] = lh24to16bit( - (pal[c*3+0] * l) >> (VID_CBITS - 1), - (pal[c*3+1] * l) >> (VID_CBITS - 1), - (pal[c*3+2] * l) >> (VID_CBITS - 1)); - for (;c < 255;c++) - out[l*256+c] = lh24to16bit(pal[c*3+0], pal[c*3+1], pal[c*3+2]); - out[l*256+255] = 0; - } -} - -/* - VID_MakeColormaps - - LordHavoc: makes 8bit, 16bit, and 32bit colormaps and palettes -*/ -void -VID_MakeColormaps (void) -{ - vid.colormap16 = malloc (256*VID_GRADES * sizeof (short)); - vid.colormap32 = malloc (256*VID_GRADES * sizeof (int)); - SYS_CHECKMEM (vid.colormap16 && vid.colormap32); - VID_MakeColormap16(vid.colormap16, vid.palette); - VID_MakeColormap32(vid.colormap32, vid.palette); -} diff --git a/libs/video/renderer/vid_common.c b/libs/video/renderer/vid_common.c index 5a2cd9d43..52bc8ca06 100644 --- a/libs/video/renderer/vid_common.c +++ b/libs/video/renderer/vid_common.c @@ -31,22 +31,33 @@ #include "QF/plugin/general.h" #include "QF/plugin/vid_render.h" +#include "QF/scene/entity.h" + +#include "QF/ui/view.h" + #include "mod_internal.h" #include "r_internal.h" +viddef_t vid; // global video state + vid_render_data_t vid_render_data = { - &vid, &r_refdef, &scr_vrect, - 0, 0, 0, - 0, - 0, 0, - 0.0, - false, false, false, - 0, - 0, 0, - 0, - 0.0, 0.0, - 0, - r_origin, vpn, vright, vup + .vid = &vid, + .refdef = &r_refdef, + .scr_copytop = 0, + .scr_copyeverything = 0, + .scr_fullupdate = 0, + .viewsize_callback = 0, + .scr_viewsize = 0, + .graphheight = 0, + .min_wateralpha = 0.0, + .force_fullscreen = false, + .inhibit_viewmodel = false, + .paused = false, + .lineadj = 0, + .view_model = nullentity, + .frametime = 0.0, + .realtime = 0.0, + .lightstyle = 0, }; vid_render_funcs_t *vid_render_funcs; diff --git a/libs/video/renderer/vid_render_gl.c b/libs/video/renderer/vid_render_gl.c index d874a0258..c3870df6a 100644 --- a/libs/video/renderer/vid_render_gl.c +++ b/libs/video/renderer/vid_render_gl.c @@ -28,109 +28,173 @@ # include "config.h" #endif -#define NH_DEFINE -#include "gl/namehack.h" +#include "QF/cvar.h" +#include "QF/image.h" #include "QF/plugin/general.h" #include "QF/plugin/vid_render.h" +#include "QF/GL/funcs.h" +#include "QF/GL/qf_draw.h" +#include "QF/GL/qf_fisheye.h" +#include "QF/GL/qf_rmain.h" +#include "QF/GL/qf_rsurf.h" +#include "QF/GL/qf_particles.h" #include "QF/GL/qf_vid.h" #include "mod_internal.h" +#include "r_cvar.h" #include "r_internal.h" +#include "vid_internal.h" +#include "vid_gl.h" -#include "gl/namehack.h" +gl_ctx_t *gl_ctx; + +/* Unknown renamed to GLErr_Unknown to solve conflict with winioctl.h */ +static unsigned int GLErr_InvalidEnum; +static unsigned int GLErr_InvalidValue; +static unsigned int GLErr_InvalidOperation; +static unsigned int GLErr_OutOfMemory; +static unsigned int GLErr_StackOverflow; +static unsigned int GLErr_StackUnderflow; +static unsigned int GLErr_Unknown; + +static unsigned int +R_TestErrors (unsigned int numerous) +{ + switch (qfglGetError ()) { + case GL_NO_ERROR: + return numerous; + break; + case GL_INVALID_ENUM: + GLErr_InvalidEnum++; + R_TestErrors (numerous++); + break; + case GL_INVALID_VALUE: + GLErr_InvalidValue++; + R_TestErrors (numerous++); + break; + case GL_INVALID_OPERATION: + GLErr_InvalidOperation++; + R_TestErrors (numerous++); + break; + case GL_STACK_OVERFLOW: + GLErr_StackOverflow++; + R_TestErrors (numerous++); + break; + case GL_STACK_UNDERFLOW: + GLErr_StackUnderflow++; + R_TestErrors (numerous++); + break; + case GL_OUT_OF_MEMORY: + GLErr_OutOfMemory++; + R_TestErrors (numerous++); + break; + default: + GLErr_Unknown++; + R_TestErrors (numerous++); + break; + } + + return numerous; +} + +static void +R_DisplayErrors (void) +{ + if (GLErr_InvalidEnum) + printf ("%d OpenGL errors: Invalid Enum!\n", GLErr_InvalidEnum); + if (GLErr_InvalidValue) + printf ("%d OpenGL errors: Invalid Value!\n", GLErr_InvalidValue); + if (GLErr_InvalidOperation) + printf ("%d OpenGL errors: Invalid Operation!\n", GLErr_InvalidOperation); + if (GLErr_StackOverflow) + printf ("%d OpenGL errors: Stack Overflow!\n", GLErr_StackOverflow); + if (GLErr_StackUnderflow) + printf ("%d OpenGL errors: Stack Underflow\n!", GLErr_StackUnderflow); + if (GLErr_OutOfMemory) + printf ("%d OpenGL errors: Out Of Memory!\n", GLErr_OutOfMemory); + if (GLErr_Unknown) + printf ("%d Unknown OpenGL errors!\n", GLErr_Unknown); +} + +static void +R_ClearErrors (void) +{ + GLErr_InvalidEnum = 0; + GLErr_InvalidValue = 0; + GLErr_InvalidOperation = 0; + GLErr_OutOfMemory = 0; + GLErr_StackOverflow = 0; + GLErr_StackUnderflow = 0; + GLErr_Unknown = 0; +} + +void +gl_errors (const char *msg) +{ + if (R_TestErrors (0)) { + printf ("gl_errors: %s\n", msg); + R_DisplayErrors (); + } + R_ClearErrors (); +} + +static void +gl_vid_render_choose_visual (void *data) +{ + gl_ctx->choose_visual (gl_ctx); +} + +static void +gl_vid_render_create_context (void *data) +{ + gl_ctx->create_context (gl_ctx, 0); +} static vid_model_funcs_t model_funcs = { - gl_Mod_LoadExternalTextures, - gl_Mod_LoadLighting, - gl_Mod_SubdivideSurface, - gl_Mod_ProcessTexture, + .texture_render_size = sizeof (gltex_t), - Mod_LoadIQM, - Mod_LoadAliasModel, - Mod_LoadSpriteModel, + .Mod_LoadLighting = gl_Mod_LoadLighting, + .Mod_SubdivideSurface = gl_Mod_SubdivideSurface, + .Mod_ProcessTexture = gl_Mod_ProcessTexture, - gl_Mod_MakeAliasModelDisplayLists, - gl_Mod_LoadSkin, - gl_Mod_FinalizeAliasModel, - gl_Mod_LoadExternalSkins, - gl_Mod_IQMFinish, - 1, - gl_Mod_SpriteLoadTexture, + .Mod_LoadIQM = Mod_LoadIQM, + .Mod_LoadAliasModel = Mod_LoadAliasModel, + .Mod_LoadSpriteModel = Mod_LoadSpriteModel, - Skin_SetColormap, - Skin_SetSkin, - gl_Skin_SetupSkin, - Skin_SetTranslation, - gl_Skin_ProcessTranslation, - gl_Skin_InitTranslations, -}; + .Mod_MakeAliasModelDisplayLists = gl_Mod_MakeAliasModelDisplayLists, + .Mod_LoadAllSkins = gl_Mod_LoadAllSkins, + .Mod_FinalizeAliasModel = gl_Mod_FinalizeAliasModel, + .Mod_LoadExternalSkins = gl_Mod_LoadExternalSkins, + .Mod_IQMFinish = gl_Mod_IQMFinish, + .alias_cache = 1, + .Mod_SpriteLoadFrames = gl_Mod_SpriteLoadFrames, -vid_render_funcs_t gl_vid_render_funcs = { - gl_Draw_Init, - gl_Draw_Character, - gl_Draw_String, - gl_Draw_nString, - gl_Draw_AltString, - gl_Draw_ConsoleBackground, - gl_Draw_Crosshair, - gl_Draw_CrosshairAt, - gl_Draw_TileClear, - gl_Draw_Fill, - gl_Draw_TextBox, - gl_Draw_FadeScreen, - gl_Draw_BlendScreen, - gl_Draw_CachePic, - gl_Draw_UncachePic, - gl_Draw_MakePic, - gl_Draw_DestroyPic, - gl_Draw_PicFromWad, - gl_Draw_Pic, - gl_Draw_Picf, - gl_Draw_SubPic, - - gl_SCR_UpdateScreen, - SCR_DrawRam, - SCR_DrawTurtle, - SCR_DrawPause, - gl_SCR_CaptureBGR, - gl_SCR_ScreenShot, - SCR_DrawStringToSnap, - - gl_Fog_Update, - gl_Fog_ParseWorldspawn, - - gl_R_Init, - gl_R_ClearState, - gl_R_LoadSkys, - gl_R_NewMap, - R_AddEfrags, - R_RemoveEfrags, - R_EnqueueEntity, - gl_R_LineGraph, - R_AllocDlight, - R_AllocEntity, - gl_R_RenderView, - R_DecayLights, - gl_R_ViewChanged, - R_CompileParticlePhysics, - R_AddParticlePhysicsFunction, - gl_R_ClearParticles, - gl_R_InitParticles, - gl_SCR_ScreenShot_f, - gl_r_easter_eggs_f, - gl_r_particles_style_f, - 0, - &model_funcs + .Skin_Free = Skin_Free, + .Skin_SetColormap = Skin_SetColormap, + .Skin_SetSkin = Skin_SetSkin, + .Skin_SetupSkin = gl_Skin_SetupSkin, + .Skin_SetTranslation = Skin_SetTranslation, + .Skin_ProcessTranslation = gl_Skin_ProcessTranslation, + .Skin_InitTranslations = gl_Skin_InitTranslations, }; static void gl_vid_render_init (void) { - vr_data.vid->set_palette = GL_SetPalette; - vr_data.vid->init_gl = GL_Init_Common; - vr_data.vid->load_gl (); + if (!vr_data.vid->vid_internal->gl_context) { + Sys_Error ("Sorry, OpenGL not supported by this program."); + } + vid_internal_t *vi = vr_data.vid->vid_internal; + gl_ctx = vi->gl_context (vi); + gl_ctx->init_gl = GL_Init_Common; + gl_ctx->load_gl (gl_ctx); + + vi->set_palette = GL_SetPalette; + vi->choose_visual = gl_vid_render_choose_visual; + vi->create_context = gl_vid_render_create_context; + vr_funcs = &gl_vid_render_funcs; m_funcs = &model_funcs; } @@ -140,35 +204,376 @@ gl_vid_render_shutdown (void) { } +static void +gl_begin_frame (void) +{ + if (gl_ctx->begun) { + gl_ctx->end_rendering (); + gl_ctx->begun = 0; + } + + //FIXME forces the status bar to redraw. needed because it does not fully + //update in sw modes but must in gl mode + vr_data.scr_copyeverything = 1; + + if (gl_clear) { + qfglClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } else { + qfglClear (GL_DEPTH_BUFFER_BIT); + } + + gl_ctx->begun = 1; + + if (r_speeds) { + gl_ctx->start_time = Sys_DoubleTime (); + gl_ctx->brush_polys = 0; + gl_ctx->alias_polys = 0; + } + + GL_Set2D (); + GL_DrawReset (); + + // draw any areas not covered by the refresh + if (r_refdef.vrect.x > 0) { + // left + gl_Draw_TileClear (0, 0, r_refdef.vrect.x, + vid.height - vr_data.lineadj); + // right + gl_Draw_TileClear (r_refdef.vrect.x + r_refdef.vrect.width, 0, + vid.width - r_refdef.vrect.x + r_refdef.vrect.width, + vid.height - vr_data.lineadj); + } + if (r_refdef.vrect.y > 0) { + // top + gl_Draw_TileClear (r_refdef.vrect.x, 0, + r_refdef.vrect.x + r_refdef.vrect.width, + r_refdef.vrect.y); + // bottom + gl_Draw_TileClear (r_refdef.vrect.x, + r_refdef.vrect.y + r_refdef.vrect.height, + r_refdef.vrect.width, + vid.height - vr_data.lineadj - + (r_refdef.vrect.height + r_refdef.vrect.y)); + } + + gl_Fog_SetupFrame (); +} + +static void +gl_render_view (void) +{ + // do 3D refresh drawing, and then update the screen + qfglClear (GL_DEPTH_BUFFER_BIT); + gl_R_RenderView (); + gl_R_RenderEntities (r_ent_queue); +} + +static void +gl_draw_transparent (void) +{ + gl_R_DrawWaterSurfaces (); +} + +static void +gl_post_process (framebuffer_t *src) +{ + if (scr_fisheye) { + gl_FisheyeScreen (src); + } else if (r_dowarp) { + gl_WarpScreen (src); + } +} + +static void +gl_set_2d (int scaled) +{ + if (scaled) { + GL_Set2DScaled (); + } else { + GL_Set2D (); + } +} + +static void +gl_end_frame (void) +{ + if (r_speeds) { +// qfglFinish (); + double start_time = gl_ctx->start_time; + double end_time = Sys_DoubleTime (); + Sys_MaskPrintf (SYS_dev, "%3i ms %4i wpoly %4i epoly %4i parts\n", + (int) ((end_time - start_time) * 1000), + gl_ctx->brush_polys, gl_ctx->alias_polys, + r_psystem.numparticles); + } + + GL_FlushText (); + qfglFlush (); + + if (gl_finish) { + gl_ctx->end_rendering (); + gl_ctx->begun = 0; + } +} + +static framebuffer_t * +gl_create_cube_map (int side) +{ + GLuint tex[2]; + qfglGenTextures (2, tex); + + qfglBindTexture (GL_TEXTURE_CUBE_MAP_ARB, tex[0]); + for (int i = 0; i < 6; i++) { + qfglTexImage2D (GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + i, 0, GL_RGBA, + side, side, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + } + qfglTexParameteri (GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_EDGE); + qfglTexParameteri (GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_EDGE); + qfglTexParameteri (GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, + GL_LINEAR); + qfglTexParameteri (GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, + GL_LINEAR); + + qfglBindTexture (GL_TEXTURE_2D, tex[1]); + qfglTexImage2D (GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, side, side, 0, + GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0); + qfglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + qfglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + size_t size = sizeof (framebuffer_t) * 6; + size += sizeof (gl_framebuffer_t) * 6; + + framebuffer_t *cube = malloc (size); + __auto_type buffer_base = (gl_framebuffer_t *) &cube[6]; + for (int i = 0; i < 6; i++) { + cube[i].width = side; + cube[i].height = side; + __auto_type buffer = buffer_base + i; + cube[i].buffer = buffer; + + buffer->color = tex[0]; + buffer->depth = tex[1]; + qfglGenFramebuffers (1, &buffer->handle); + + qfglBindFramebuffer (GL_FRAMEBUFFER, buffer->handle); + qfglFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + i, + buffer->color, 0); + qfglFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_TEXTURE_2D, buffer->depth, 0); + } + qfglBindFramebuffer (GL_FRAMEBUFFER, 0); + return cube; +} + +static framebuffer_t * +gl_create_frame_buffer (int width, int height) +{ + size_t size = sizeof (framebuffer_t) + sizeof (gl_framebuffer_t); + + framebuffer_t *fb = malloc (size); + fb->width = width; + fb->height = height; + __auto_type buffer = (gl_framebuffer_t *) &fb[1]; + fb->buffer = buffer; + qfglGenFramebuffers (1, &buffer->handle); + + GLuint tex[2]; + qfglGenTextures (2, tex); + + buffer->color = tex[0]; + buffer->depth = tex[1]; + + qfglBindTexture (GL_TEXTURE_2D, buffer->color); + qfglTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, 0); + qfglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + qfglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + qfglBindTexture (GL_TEXTURE_2D, buffer->depth); + qfglTexImage2D (GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, + GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0); + qfglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + qfglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + qfglBindFramebuffer (GL_FRAMEBUFFER, buffer->handle); + qfglFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, buffer->color, 0); + qfglFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_TEXTURE_2D, buffer->depth, 0); + + qfglBindFramebuffer (GL_FRAMEBUFFER, 0); + return fb; +} + +static void +gl_destroy_frame_buffer (framebuffer_t *framebuffer) +{ + __auto_type fb = (gl_framebuffer_t *) framebuffer->buffer; + + qfglDeleteFramebuffers (1, &fb->handle); + + GLuint tex[2] = { fb->color, fb->depth }; + qfglDeleteTextures (2, tex); + free (framebuffer); +} + +static void +gl_bind_framebuffer (framebuffer_t *framebuffer) +{ + unsigned width = vr_data.vid->width; + unsigned height = vr_data.vid->height; + if (!framebuffer) { + qfglBindFramebuffer (GL_FRAMEBUFFER, 0); + } else { + gl_framebuffer_t *buffer = framebuffer->buffer; + qfglBindFramebuffer (GL_FRAMEBUFFER, buffer->handle); + + width = framebuffer->width; + height = framebuffer->height; + } + + vrect_t r = { 0, 0, width, height }; + R_SetVrect (&r, &r_refdef.vrect, 0); +} + +static void +gl_set_viewport (const vrect_t *view) +{ + int x = view->x; + int y = vid.height - (view->y + view->height); //FIXME vid.height + int w = view->width; + int h = view->height; + qfglViewport (x, y, w, h); +} + +static void +gl_set_fov (float x, float y) +{ + float neard, fard; + mat4f_t proj; + + neard = r_nearclip; + fard = r_farclip; + + // NOTE columns! + proj[0] = (vec4f_t) { 1/x, 0, 0, 0 }; + proj[1] = (vec4f_t) { 0, 1/y, 0, 0 }; + proj[2] = (vec4f_t) { 0, 0, (fard) / (fard - neard), 1 }; + proj[3] = (vec4f_t) { 0, 0, (fard * neard) / (neard - fard), 0 }; + + // convert 0..1 depth buffer range to -1..1 + static mat4f_t depth_range = { + { 1, 0, 0, 0}, + { 0, 1, 0, 0}, + { 0, 0, 2, 0}, + { 0, 0,-1, 1}, + }; + mmulf (gl_ctx->projection, depth_range, proj); +} + +static void +gl_capture_screen (capfunc_t callback, void *data) +{ + int count; + tex_t *tex; + + count = vid.width * vid.height; + tex = malloc (sizeof (tex_t) + count * 3); + if (tex) { + tex->data = (byte *) (tex + 1); + tex->width = vid.width; + tex->height = vid.height; + tex->format = tex_rgb; + tex->palette = 0; + tex->flagbits = 0; + tex->loaded = 1; + tex->bgr = 1; + tex->flipped = 1; + qfglReadPixels (0, 0, tex->width, tex->height, GL_BGR_EXT, + GL_UNSIGNED_BYTE, tex->data); + } + callback (tex, data); +} + +vid_render_funcs_t gl_vid_render_funcs = { + .init = gl_vid_render_init, + + .UpdateScreen = SCR_UpdateScreen_legacy, + + .Draw_CharBuffer = gl_Draw_CharBuffer, + .Draw_SetScale = gl_Draw_SetScale, + .Draw_Character = gl_Draw_Character, + .Draw_String = gl_Draw_String, + .Draw_nString = gl_Draw_nString, + .Draw_AltString = gl_Draw_AltString, + .Draw_ConsoleBackground = gl_Draw_ConsoleBackground, + .Draw_Crosshair = gl_Draw_Crosshair, + .Draw_CrosshairAt = gl_Draw_CrosshairAt, + .Draw_TileClear = gl_Draw_TileClear, + .Draw_Fill = gl_Draw_Fill, + .Draw_Line = gl_Draw_Line, + .Draw_TextBox = gl_Draw_TextBox, + .Draw_FadeScreen = gl_Draw_FadeScreen, + .Draw_BlendScreen = gl_Draw_BlendScreen, + .Draw_CachePic = gl_Draw_CachePic, + .Draw_UncachePic = gl_Draw_UncachePic, + .Draw_MakePic = gl_Draw_MakePic, + .Draw_DestroyPic = gl_Draw_DestroyPic, + .Draw_PicFromWad = gl_Draw_PicFromWad, + .Draw_Pic = gl_Draw_Pic, + .Draw_FitPic = gl_Draw_FitPic, + .Draw_Picf = gl_Draw_Picf, + .Draw_SubPic = gl_Draw_SubPic, + .Draw_AddFont = gl_Draw_AddFont, + .Draw_Glyph = gl_Draw_Glyph, + + .ParticleSystem = gl_ParticleSystem, + .R_Init = gl_R_Init, + .R_ClearState = gl_R_ClearState, + .R_LoadSkys = gl_R_LoadSkys, + .R_NewScene = gl_R_NewScene, + .R_LineGraph = gl_R_LineGraph, + .begin_frame = gl_begin_frame, + .render_view = gl_render_view, + .draw_particles = gl_R_DrawParticles, + .draw_transparent = gl_draw_transparent, + .post_process = gl_post_process, + .set_2d = gl_set_2d, + .end_frame = gl_end_frame, + + .create_cube_map = gl_create_cube_map, + .create_frame_buffer = gl_create_frame_buffer, + .destroy_frame_buffer = gl_destroy_frame_buffer, + .bind_framebuffer = gl_bind_framebuffer, + .set_viewport = gl_set_viewport, + .set_fov = gl_set_fov, + + .capture_screen = gl_capture_screen, + + .model_funcs = &model_funcs +}; + static general_funcs_t plugin_info_general_funcs = { - gl_vid_render_init, - gl_vid_render_shutdown, + .shutdown = gl_vid_render_shutdown, }; static general_data_t plugin_info_general_data; static plugin_funcs_t plugin_info_funcs = { - &plugin_info_general_funcs, - 0, - 0, - 0, - 0, - 0, - &gl_vid_render_funcs, + .general = &plugin_info_general_funcs, + .vid_render = &gl_vid_render_funcs, }; static plugin_data_t plugin_info_data = { - &plugin_info_general_data, - 0, - 0, - 0, - 0, - 0, - &vid_render_data, + .general = &plugin_info_general_data, + .vid_render = &vid_render_data, }; static plugin_t plugin_info = { - qfp_snd_render, + qfp_vid_render, 0, QFPLUGIN_VERSION, "0.1", diff --git a/libs/video/renderer/vid_render_glsl.c b/libs/video/renderer/vid_render_glsl.c index 635e7b415..38318ab1d 100644 --- a/libs/video/renderer/vid_render_glsl.c +++ b/libs/video/renderer/vid_render_glsl.c @@ -28,109 +28,83 @@ # include "config.h" #endif -#define NH_DEFINE -#include "glsl/namehack.h" +#include "QF/cvar.h" +#include "QF/image.h" #include "QF/plugin/general.h" #include "QF/plugin/vid_render.h" +#include "QF/GLSL/funcs.h" +#include "QF/GLSL/defines.h" +#include "QF/GLSL/qf_bsp.h" +#include "QF/GLSL/qf_draw.h" +#include "QF/GLSL/qf_fisheye.h" +#include "QF/GLSL/qf_main.h" +#include "QF/GLSL/qf_particles.h" #include "QF/GLSL/qf_vid.h" +#include "QF/GLSL/qf_warp.h" #include "mod_internal.h" #include "r_internal.h" +#include "vid_internal.h" +#include "vid_gl.h" -#include "glsl/namehack.h" +gl_ctx_t *glsl_ctx; + +static void +glsl_vid_render_choose_visual (void *data) +{ + glsl_ctx->choose_visual (glsl_ctx); +} + +static void +glsl_vid_render_create_context (void *data) +{ + glsl_ctx->create_context (glsl_ctx, 1); +} static vid_model_funcs_t model_funcs = { - glsl_Mod_LoadExternalTextures, - glsl_Mod_LoadLighting, - glsl_Mod_SubdivideSurface, - glsl_Mod_ProcessTexture, + .texture_render_size = sizeof (glsltex_t), - Mod_LoadIQM, - Mod_LoadAliasModel, - Mod_LoadSpriteModel, + .Mod_LoadLighting = glsl_Mod_LoadLighting, + //.Mod_SubdivideSurface = 0, + .Mod_ProcessTexture = glsl_Mod_ProcessTexture, - glsl_Mod_MakeAliasModelDisplayLists, - glsl_Mod_LoadSkin, - glsl_Mod_FinalizeAliasModel, - glsl_Mod_LoadExternalSkins, - glsl_Mod_IQMFinish, - 0, - glsl_Mod_SpriteLoadTexture, + .Mod_LoadIQM = Mod_LoadIQM, + .Mod_LoadAliasModel = Mod_LoadAliasModel, + .Mod_LoadSpriteModel = Mod_LoadSpriteModel, - Skin_SetColormap, - Skin_SetSkin, - glsl_Skin_SetupSkin, - Skin_SetTranslation, - glsl_Skin_ProcessTranslation, - glsl_Skin_InitTranslations, -}; + .Mod_MakeAliasModelDisplayLists = glsl_Mod_MakeAliasModelDisplayLists, + .Mod_LoadAllSkins = glsl_Mod_LoadAllSkins, + .Mod_FinalizeAliasModel = glsl_Mod_FinalizeAliasModel, + .Mod_LoadExternalSkins = glsl_Mod_LoadExternalSkins, + .Mod_IQMFinish = glsl_Mod_IQMFinish, + .alias_cache = 0, + .Mod_SpriteLoadFrames = glsl_Mod_SpriteLoadFrames, -vid_render_funcs_t glsl_vid_render_funcs = { - glsl_Draw_Init, - glsl_Draw_Character, - glsl_Draw_String, - glsl_Draw_nString, - glsl_Draw_AltString, - glsl_Draw_ConsoleBackground, - glsl_Draw_Crosshair, - glsl_Draw_CrosshairAt, - glsl_Draw_TileClear, - glsl_Draw_Fill, - glsl_Draw_TextBox, - glsl_Draw_FadeScreen, - glsl_Draw_BlendScreen, - glsl_Draw_CachePic, - glsl_Draw_UncachePic, - glsl_Draw_MakePic, - glsl_Draw_DestroyPic, - glsl_Draw_PicFromWad, - glsl_Draw_Pic, - glsl_Draw_Picf, - glsl_Draw_SubPic, - - glsl_SCR_UpdateScreen, - SCR_DrawRam, - SCR_DrawTurtle, - SCR_DrawPause, - glsl_SCR_CaptureBGR, - glsl_SCR_ScreenShot, - SCR_DrawStringToSnap, - - glsl_Fog_Update, - glsl_Fog_ParseWorldspawn, - - glsl_R_Init, - glsl_R_ClearState, - glsl_R_LoadSkys, - glsl_R_NewMap, - R_AddEfrags, - R_RemoveEfrags, - R_EnqueueEntity, - glsl_R_LineGraph, - R_AllocDlight, - R_AllocEntity, - glsl_R_RenderView, - R_DecayLights, - glsl_R_ViewChanged, - R_CompileParticlePhysics, - R_AddParticlePhysicsFunction, - glsl_R_ClearParticles, - glsl_R_InitParticles, - glsl_SCR_ScreenShot_f, - glsl_r_easter_eggs_f, - glsl_r_particles_style_f, - 0, - &model_funcs + .Skin_Free = Skin_Free, + .Skin_SetColormap = Skin_SetColormap, + .Skin_SetSkin = Skin_SetSkin, + .Skin_SetupSkin = glsl_Skin_SetupSkin, + .Skin_SetTranslation = Skin_SetTranslation, + .Skin_ProcessTranslation = glsl_Skin_ProcessTranslation, + .Skin_InitTranslations = glsl_Skin_InitTranslations, }; static void glsl_vid_render_init (void) { - vr_data.vid->set_palette = GLSL_SetPalette; - vr_data.vid->init_gl = GLSL_Init_Common; - vr_data.vid->load_gl (); + if (!vr_data.vid->vid_internal->gl_context) { + Sys_Error ("Sorry, OpenGL (GLSL) not supported by this program."); + } + vid_internal_t *vi = vr_data.vid->vid_internal; + glsl_ctx = vi->gl_context (vi); + glsl_ctx->init_gl = GLSL_Init_Common; + glsl_ctx->load_gl (glsl_ctx); + + vi->set_palette = GLSL_SetPalette; + vi->choose_visual = glsl_vid_render_choose_visual; + vi->create_context = glsl_vid_render_create_context; vr_funcs = &glsl_vid_render_funcs; m_funcs = &model_funcs; } @@ -138,37 +112,414 @@ glsl_vid_render_init (void) static void glsl_vid_render_shutdown (void) { + glsl_R_Shutdown (); + GLSL_Shutdown_Common (); } +static unsigned int GLErr_InvalidEnum; +static unsigned int GLErr_InvalidValue; +static unsigned int GLErr_InvalidOperation; +static unsigned int GLErr_OutOfMemory; +static unsigned int GLErr_Unknown; + +static unsigned int +R_TestErrors (unsigned int numerous) +{ + switch (qfeglGetError ()) { + case GL_NO_ERROR: + return numerous; + break; + case GL_INVALID_ENUM: + GLErr_InvalidEnum++; + R_TestErrors (numerous++); + break; + case GL_INVALID_VALUE: + GLErr_InvalidValue++; + R_TestErrors (numerous++); + break; + case GL_INVALID_OPERATION: + GLErr_InvalidOperation++; + R_TestErrors (numerous++); + break; + case GL_OUT_OF_MEMORY: + GLErr_OutOfMemory++; + R_TestErrors (numerous++); + break; + default: + GLErr_Unknown++; + R_TestErrors (numerous++); + break; + } + + return numerous; +} + +static void +R_DisplayErrors (void) +{ + if (GLErr_InvalidEnum) + printf ("%d OpenGL errors: Invalid Enum!\n", GLErr_InvalidEnum); + if (GLErr_InvalidValue) + printf ("%d OpenGL errors: Invalid Value!\n", GLErr_InvalidValue); + if (GLErr_InvalidOperation) + printf ("%d OpenGL errors: Invalid Operation!\n", GLErr_InvalidOperation); + if (GLErr_OutOfMemory) + printf ("%d OpenGL errors: Out Of Memory!\n", GLErr_OutOfMemory); + if (GLErr_Unknown) + printf ("%d Unknown OpenGL errors!\n", GLErr_Unknown); +} + +static void +R_ClearErrors (void) +{ + GLErr_InvalidEnum = 0; + GLErr_InvalidValue = 0; + GLErr_InvalidOperation = 0; + GLErr_OutOfMemory = 0; + GLErr_Unknown = 0; +} + +static void +glsl_begin_frame (void) +{ + if (R_TestErrors (0)) + R_DisplayErrors (); + R_ClearErrors (); + + if (glsl_ctx->begun) { + glsl_ctx->begun = 0; + glsl_ctx->end_rendering (); + } + + //FIXME forces the status bar to redraw. needed because it does not fully + //update in sw modes but must in glsl mode + vr_data.scr_copyeverything = 1; + + qfeglClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glsl_ctx->begun = 1; + + GLSL_Set2D (); + GLSL_DrawReset (); + + if (r_refdef.vrect.x > 0) { + int rx = r_refdef.vrect.x + r_refdef.vrect.width; + int vh = vid.height - vr_data.lineadj; + // left + glsl_Draw_TileClear (0, 0, r_refdef.vrect.x, vh); + // right + glsl_Draw_TileClear (rx, 0, vid.width - rx, vh); + } + if (r_refdef.vrect.y > 0) { + int lx = r_refdef.vrect.x; + int ty = r_refdef.vrect.y; + int rx = r_refdef.vrect.x + r_refdef.vrect.width; + int by = r_refdef.vrect.y + r_refdef.vrect.height; + int vh = vid.height - vr_data.lineadj; + // top + glsl_Draw_TileClear (lx, 0, rx, ty); + // bottom + glsl_Draw_TileClear (lx, by, r_refdef.vrect.width, vh - by); + } +} + +static void +glsl_render_view (void) +{ + qfeglClear (GL_DEPTH_BUFFER_BIT); + glsl_R_RenderView (); + glsl_R_RenderEntities (r_ent_queue); +} + +static void +glsl_draw_transparent (void) +{ + glsl_R_DrawWaterSurfaces (); +} + +static void glsl_bind_framebuffer (framebuffer_t *fb); + +static void +glsl_post_process (framebuffer_t *src) +{ + if (scr_fisheye) { + glsl_FisheyeScreen (src); + } else if (r_dowarp) { + glsl_WarpScreen (src); + } +} + +static void +glsl_set_2d (int scaled) +{ + if (scaled) { + GLSL_Set2DScaled (); + } else { + GLSL_Set2D (); + } +} + +static void +glsl_end_frame (void) +{ + GLSL_FlushText (); + GLSL_End2D (); + qfeglFlush (); +} + +static framebuffer_t * +glsl_create_cube_map (int side) +{ + GLuint tex[2]; + qfeglGenTextures (2, tex); + + qfeglBindTexture (GL_TEXTURE_CUBE_MAP, tex[0]); + for (int i = 0; i < 6; i++) { + qfeglTexImage2D (GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA, + side, side, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + } + qfeglTexParameteri (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_EDGE); + qfeglTexParameteri (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_EDGE); + qfeglTexParameteri (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + qfeglTexParameteri (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + qfeglBindTexture (GL_TEXTURE_2D, tex[1]); + qfeglTexImage2D (GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, side, side, 0, + GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0); + qfeglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + qfeglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + size_t size = sizeof (framebuffer_t) * 6; + size += sizeof (gl_framebuffer_t) * 6; + + framebuffer_t *cube = malloc (size); + __auto_type buffer_base = (gl_framebuffer_t *) &cube[6]; + for (int i = 0; i < 6; i++) { + cube[i].width = side; + cube[i].height = side; + __auto_type buffer = buffer_base + i; + cube[i].buffer = buffer; + + buffer->color = tex[0]; + buffer->depth = tex[1]; + qfeglGenFramebuffers (1, &buffer->handle); + + qfeglBindFramebuffer (GL_FRAMEBUFFER, buffer->handle); + qfeglFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, + buffer->color, 0); + qfeglFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_TEXTURE_2D, buffer->depth, 0); + } + qfeglBindFramebuffer (GL_FRAMEBUFFER, 0); + return cube; +} + +static framebuffer_t * +glsl_create_frame_buffer (int width, int height) +{ + size_t size = sizeof (framebuffer_t) + sizeof (gl_framebuffer_t); + + framebuffer_t *fb = malloc (size); + fb->width = width; + fb->height = height; + __auto_type buffer = (gl_framebuffer_t *) &fb[1]; + fb->buffer = buffer; + qfeglGenFramebuffers (1, &buffer->handle); + + GLuint tex[2]; + qfeglGenTextures (2, tex); + + buffer->color = tex[0]; + buffer->depth = tex[1]; + + qfeglBindTexture (GL_TEXTURE_2D, buffer->color); + qfeglTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, 0); + qfeglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + qfeglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + qfeglBindTexture (GL_TEXTURE_2D, buffer->depth); + qfeglTexImage2D (GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, + GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0); + qfeglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + qfeglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + qfeglBindFramebuffer (GL_FRAMEBUFFER, buffer->handle); + qfeglFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, buffer->color, 0); + qfeglFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_TEXTURE_2D, buffer->depth, 0); + + qfeglBindFramebuffer (GL_FRAMEBUFFER, 0); + return fb; +} + +static void +glsl_destroy_frame_buffer (framebuffer_t *framebuffer) +{ + __auto_type fb = (gl_framebuffer_t *) framebuffer->buffer; + + qfeglDeleteFramebuffers (1, &fb->handle); + + GLuint tex[2] = { fb->color, fb->depth }; + qfeglDeleteTextures (2, tex); + free (framebuffer); +} + +static void +glsl_bind_framebuffer (framebuffer_t *framebuffer) +{ + unsigned width = vr_data.vid->width; + unsigned height = vr_data.vid->height; + if (!framebuffer) { + qfeglBindFramebuffer (GL_FRAMEBUFFER, 0); + } else { + gl_framebuffer_t *buffer = framebuffer->buffer; + qfeglBindFramebuffer (GL_FRAMEBUFFER, buffer->handle); + + width = framebuffer->width; + height = framebuffer->height; + } + + vrect_t r = { 0, 0, width, height }; + R_SetVrect (&r, &r_refdef.vrect, 0); +} + +static void +glsl_set_viewport (const vrect_t *view) +{ + int x = view->x; + int y = vid.height - (view->y + view->height); //FIXME vid.height + int w = view->width; + int h = view->height; + qfeglViewport (x, y, w, h); +} + +static void +glsl_set_fov (float x, float y) +{ + float neard, fard; + mat4f_t proj; + + neard = r_nearclip; + fard = r_farclip; + + // NOTE columns! + proj[0] = (vec4f_t) { 1/x, 0, 0, 0 }; + proj[1] = (vec4f_t) { 0, 1/y, 0, 0 }; + proj[2] = (vec4f_t) { 0, 0, (fard) / (fard - neard), 1 }; + proj[3] = (vec4f_t) { 0, 0, (fard * neard) / (neard - fard), 0 }; + + // convert 0..1 depth buffer range to -1..1 + static mat4f_t depth_range = { + { 1, 0, 0, 0}, + { 0, 1, 0, 0}, + { 0, 0, 2, 0}, + { 0, 0,-1, 1}, + }; + mmulf (glsl_ctx->projection, depth_range, proj); +} + +static void +glsl_capture_screen (capfunc_t callback, void *data) +{ + int count = vid.width * vid.height; + tex_t *tex = malloc (sizeof (tex_t) + count * 3); + + if (tex) { + tex->data = (byte *) (tex + 1); + tex->width = vid.width; + tex->height = vid.height; + tex->format = tex_rgb; + tex->palette = 0; + tex->flagbits = 0; + tex->loaded = 1; + tex->flipped = 1; + qfeglReadPixels (0, 0, tex->width, tex->height, GL_RGB, + GL_UNSIGNED_BYTE, tex->data); + } + callback (tex, data); +} + +vid_render_funcs_t glsl_vid_render_funcs = { + .init = glsl_vid_render_init, + + .UpdateScreen = SCR_UpdateScreen_legacy, + + .Draw_CharBuffer = glsl_Draw_CharBuffer, + .Draw_SetScale = glsl_Draw_SetScale, + .Draw_Character = glsl_Draw_Character, + .Draw_String = glsl_Draw_String, + .Draw_nString = glsl_Draw_nString, + .Draw_AltString = glsl_Draw_AltString, + .Draw_ConsoleBackground = glsl_Draw_ConsoleBackground, + .Draw_Crosshair = glsl_Draw_Crosshair, + .Draw_CrosshairAt = glsl_Draw_CrosshairAt, + .Draw_TileClear = glsl_Draw_TileClear, + .Draw_Fill = glsl_Draw_Fill, + .Draw_Line = glsl_Draw_Line, + .Draw_TextBox = glsl_Draw_TextBox, + .Draw_FadeScreen = glsl_Draw_FadeScreen, + .Draw_BlendScreen = glsl_Draw_BlendScreen, + .Draw_CachePic = glsl_Draw_CachePic, + .Draw_UncachePic = glsl_Draw_UncachePic, + .Draw_MakePic = glsl_Draw_MakePic, + .Draw_DestroyPic = glsl_Draw_DestroyPic, + .Draw_PicFromWad = glsl_Draw_PicFromWad, + .Draw_Pic = glsl_Draw_Pic, + .Draw_FitPic = glsl_Draw_FitPic, + .Draw_Picf = glsl_Draw_Picf, + .Draw_SubPic = glsl_Draw_SubPic, + .Draw_AddFont = glsl_Draw_AddFont, + .Draw_Glyph = glsl_Draw_Glyph, + + .ParticleSystem = glsl_ParticleSystem, + .R_Init = glsl_R_Init, + .R_ClearState = glsl_R_ClearState, + .R_LoadSkys = glsl_R_LoadSkys, + .R_NewScene = glsl_R_NewScene, + .R_LineGraph = glsl_LineGraph, + .begin_frame = glsl_begin_frame, + .render_view = glsl_render_view, + .draw_particles = glsl_R_DrawParticles, + .draw_transparent = glsl_draw_transparent, + .post_process = glsl_post_process, + .set_2d = glsl_set_2d, + .end_frame = glsl_end_frame, + + .create_cube_map = glsl_create_cube_map, + .create_frame_buffer = glsl_create_frame_buffer, + .destroy_frame_buffer = glsl_destroy_frame_buffer, + .bind_framebuffer = glsl_bind_framebuffer, + .set_viewport = glsl_set_viewport, + .set_fov = glsl_set_fov, + + .capture_screen = glsl_capture_screen, + + .model_funcs = &model_funcs +}; + static general_funcs_t plugin_info_general_funcs = { - glsl_vid_render_init, - glsl_vid_render_shutdown, + .shutdown = glsl_vid_render_shutdown, }; static general_data_t plugin_info_general_data; static plugin_funcs_t plugin_info_funcs = { - &plugin_info_general_funcs, - 0, - 0, - 0, - 0, - 0, - &glsl_vid_render_funcs, + .general = &plugin_info_general_funcs, + .vid_render = &glsl_vid_render_funcs, }; static plugin_data_t plugin_info_data = { - &plugin_info_general_data, - 0, - 0, - 0, - 0, - 0, - &vid_render_data, + .general = &plugin_info_general_data, + .vid_render = &vid_render_data, }; static plugin_t plugin_info = { - qfp_snd_render, + qfp_vid_render, 0, QFPLUGIN_VERSION, "0.1", diff --git a/libs/video/renderer/vid_render_sw.c b/libs/video/renderer/vid_render_sw.c index 1e933a0dd..3602a2328 100644 --- a/libs/video/renderer/vid_render_sw.c +++ b/libs/video/renderer/vid_render_sw.c @@ -1,5 +1,5 @@ /* - vid_render_gl.c + vid_render_sw.c SW version of the renderer @@ -28,99 +28,91 @@ # include "config.h" #endif +#include + +#include "QF/cvar.h" +#include "QF/image.h" + #include "QF/plugin/general.h" #include "QF/plugin/vid_render.h" +#include "QF/ui/view.h" + +#include "d_local.h" #include "mod_internal.h" #include "r_internal.h" +#include "vid_internal.h" +#include "vid_sw.h" + +sw_ctx_t *sw_ctx; + +static float r_aliasuvscale = 1.0; + +static void +sw_vid_render_choose_visual (void *data) +{ + sw_ctx->choose_visual (sw_ctx); +} + +static void +sw_vid_render_create_context (void *data) +{ + sw_ctx->create_context (sw_ctx); +} + +static void +sw_vid_render_set_palette (void *data, const byte *palette) +{ + sw_ctx->set_palette (sw_ctx, palette); +} + +static void +sw_vid_render_set_colormap (void *data, const byte *colormap) +{ + R_SetColormap (colormap); +} static vid_model_funcs_t model_funcs = { - sw_Mod_LoadExternalTextures, - sw_Mod_LoadLighting, - sw_Mod_SubdivideSurface, - sw_Mod_ProcessTexture, + .texture_render_size = 0, - Mod_LoadIQM, - Mod_LoadAliasModel, - Mod_LoadSpriteModel, + .Mod_LoadLighting = sw_Mod_LoadLighting, + .Mod_SubdivideSurface = 0, + .Mod_ProcessTexture = 0, - sw_Mod_MakeAliasModelDisplayLists, - sw_Mod_LoadSkin, - sw_Mod_FinalizeAliasModel, - sw_Mod_LoadExternalSkins, - sw_Mod_IQMFinish, - 1, - sw_Mod_SpriteLoadTexture, + .Mod_LoadIQM = Mod_LoadIQM, + .Mod_LoadAliasModel = Mod_LoadAliasModel, + .Mod_LoadSpriteModel = Mod_LoadSpriteModel, - Skin_SetColormap, - Skin_SetSkin, - sw_Skin_SetupSkin, - Skin_SetTranslation, - sw_Skin_ProcessTranslation, - sw_Skin_InitTranslations, -}; + .Mod_MakeAliasModelDisplayLists = sw_Mod_MakeAliasModelDisplayLists, + .Mod_LoadAllSkins = sw_Mod_LoadAllSkins, + .Mod_FinalizeAliasModel = sw_Mod_FinalizeAliasModel, + .Mod_LoadExternalSkins = 0, + .Mod_IQMFinish = sw_Mod_IQMFinish, + .alias_cache = 1, + .Mod_SpriteLoadFrames = sw_Mod_SpriteLoadFrames, -vid_render_funcs_t sw_vid_render_funcs = { - Draw_Init, - Draw_Character, - Draw_String, - Draw_nString, - Draw_AltString, - Draw_ConsoleBackground, - Draw_Crosshair, - Draw_CrosshairAt, - Draw_TileClear, - Draw_Fill, - Draw_TextBox, - Draw_FadeScreen, - Draw_BlendScreen, - Draw_CachePic, - Draw_UncachePic, - Draw_MakePic, - Draw_DestroyPic, - Draw_PicFromWad, - Draw_Pic, - Draw_Picf, - Draw_SubPic, - - SCR_UpdateScreen, - SCR_DrawRam, - SCR_DrawTurtle, - SCR_DrawPause, - SCR_CaptureBGR, - SCR_ScreenShot, - SCR_DrawStringToSnap, - - 0, - 0, - - sw_R_Init, - R_ClearState, - R_LoadSkys, - R_NewMap, - R_AddEfrags, - R_RemoveEfrags, - R_EnqueueEntity, - R_LineGraph, - R_AllocDlight, - R_AllocEntity, - R_RenderView, - R_DecayLights, - R_ViewChanged, - R_CompileParticlePhysics, - R_AddParticlePhysicsFunction, - R_ClearParticles, - R_InitParticles, - SCR_ScreenShot_f, - r_easter_eggs_f, - r_particles_style_f, - 0, - &model_funcs + .Skin_Free = Skin_Free, + .Skin_SetColormap = Skin_SetColormap, + .Skin_SetSkin = Skin_SetSkin, + .Skin_SetupSkin = sw_Skin_SetupSkin, + .Skin_SetTranslation = Skin_SetTranslation, + .Skin_ProcessTranslation = sw_Skin_ProcessTranslation, + .Skin_InitTranslations = sw_Skin_InitTranslations, }; static void sw_vid_render_init (void) { + if (!vr_data.vid->vid_internal->sw_context) { + Sys_Error ("Sorry, software rendering not supported by this program."); + } + sw_ctx = vr_data.vid->vid_internal->sw_context (vr_data.vid->vid_internal); + + vr_data.vid->vid_internal->set_palette = sw_vid_render_set_palette; + vr_data.vid->vid_internal->set_colormap = sw_vid_render_set_colormap; + vr_data.vid->vid_internal->choose_visual = sw_vid_render_choose_visual; + vr_data.vid->vid_internal->create_context = sw_vid_render_create_context; + vr_funcs = &sw_vid_render_funcs; m_funcs = &model_funcs; } @@ -130,35 +122,414 @@ sw_vid_render_shutdown (void) { } +static void sw_bind_framebuffer (framebuffer_t *framebuffer); + +static void +sw_begin_frame (void) +{ + if (r_numsurfs) { + int surfcount = surface_p - surfaces; + int max_surfs = surf_max - surfaces; + if (surfcount > r_maxsurfsseen) + r_maxsurfsseen = surfcount; + + Sys_Printf ("Used %d of %d surfs; %d max\n", + surfcount, max_surfs, r_maxsurfsseen); + } + + if (r_numedges) { + int edgecount = edge_p - r_edges; + + if (edgecount > r_maxedgesseen) + r_maxedgesseen = edgecount; + + Sys_Printf ("Used %d of %d edges; %d max\n", edgecount, + r_numallocatededges, r_maxedgesseen); + } + + sw_bind_framebuffer (0); + + // do 3D refresh drawing, and then update the screen + if (vr_data.scr_fullupdate++ < vid.numpages) { + vr_data.scr_copyeverything = 1; + Draw_TileClear (0, 0, vid.width, vid.height); + } +} + +static void +sw_render_view (void) +{ + R_RenderView (); + R_DrawEntitiesOnList (r_ent_queue); +} + +static void +sw_draw_transparent (void) +{ +} + +static void +sw_post_process (framebuffer_t *src) +{ + if (scr_fisheye) { + R_RenderFisheye (src); + } else if (r_dowarp) { + D_WarpScreen (src); + } +} + +static void +sw_set_2d (int scaled) +{ +} + +static void +sw_end_frame (void) +{ + if (r_reportsurfout && r_outofsurfaces) + Sys_Printf ("Short %d surfaces\n", r_outofsurfaces); + + if (r_reportedgeout && r_outofedges) + Sys_Printf ("Short roughly %d edges\n", r_outofedges * 2 / 3); + + // update one of three areas + vrect_t vrect; + if (vr_data.scr_copyeverything) { + vrect.x = 0; + vrect.y = 0; + vrect.width = vid.width; + vrect.height = vid.height; + vrect.next = 0; + } else if (scr_copytop) { + vrect.x = 0; + vrect.y = 0; + vrect.width = vid.width; + vrect.height = vid.height - vr_data.lineadj; + vrect.next = 0; + } else { + vrect.x = vr_data.refdef->vrect.x; + vrect.y = vr_data.refdef->vrect.y; + vrect.width = vr_data.refdef->vrect.width; + vrect.height = vr_data.refdef->vrect.height; + vrect.next = 0; + } + sw_ctx->update (sw_ctx, &vrect); +} + +static framebuffer_t * +sw_create_cube_map (int side) +{ + size_t pixels = side * side; // per face + size_t size = sizeof (framebuffer_t) * 6; + size += sizeof (sw_framebuffer_t) * 6; + size += pixels * 6; // color buffer + // depth buffer, scan table and zspantable are shared between cube faces + // FIXME need *6 depth and zspan for multi-threaded + size += pixels * sizeof (short); // depth buffer + + framebuffer_t *cube = malloc (size); + __auto_type buffer_base = (sw_framebuffer_t *) &cube[6]; + byte *color_base = (byte *) &buffer_base[6]; + short *depth_base = (short *) (color_base + 6 * pixels); + for (int i = 0; i < 6; i++) { + cube[i].width = side; + cube[i].height = side; + __auto_type buffer = buffer_base + i; + cube[i].buffer = buffer; + buffer->color = color_base + i * pixels; + buffer->depth = depth_base; + buffer->rowbytes = side; + } + return cube; +} + +static framebuffer_t * +sw_create_frame_buffer (int width, int height) +{ + size_t pixels = width * height; + size_t size = sizeof (framebuffer_t); + size += sizeof (sw_framebuffer_t); + size += pixels; // color buffer + size += pixels * sizeof (short); // depth buffer + + framebuffer_t *fb = malloc (size); + fb->width = width; + fb->height = height; + __auto_type buffer = (sw_framebuffer_t *) &fb[1]; + fb->buffer = buffer; + buffer->color = (byte *) &buffer[1]; + buffer->depth = (short *) (buffer->color + pixels); + buffer->rowbytes = width; + return fb; +} + +static void +sw_destroy_frame_buffer (framebuffer_t *framebuffer) +{ + free (framebuffer); +} + +static void sw_set_viewport (const vrect_t *view); + +static void +sw_bind_framebuffer (framebuffer_t *framebuffer) +{ + int changed = 0; + + if (!framebuffer) { + framebuffer = sw_ctx->framebuffer; + } + sw_framebuffer_t *fb = framebuffer->buffer; + + if (!fb->depth) { + fb->depth = malloc (framebuffer->width * framebuffer->height + * sizeof (short)); + } + + if (d_zbuffer != fb->depth + || d_zwidth != framebuffer->width || d_height != framebuffer->height) { + d_zwidth = framebuffer->width; + d_zrowbytes = d_zwidth * sizeof (short); + for (unsigned i = 0; i < framebuffer->height; i++) { + zspantable[i] = fb->depth + i * d_zwidth; + } + changed = 1; + } + if (d_rowbytes != fb->rowbytes || d_height != framebuffer->height) { + d_rowbytes = fb->rowbytes; + d_height = framebuffer->height; + for (unsigned i = 0; i < framebuffer->height; i++) { + d_scantable[i] = i * d_rowbytes; + } + changed = 1; + } + d_viewbuffer = fb->color; + d_zbuffer = fb->depth; + + if (changed) { + vrect_t r = { 0, 0, framebuffer->width, framebuffer->height }; + sw_set_viewport (&r); + } +} + +static void +sw_set_viewport (const vrect_t *view) +{ +#define SHIFT20(x) (((x) << 20) + (1 << 19) - 1) + r_refdef.vrectright = view->x + view->width; + r_refdef.vrectbottom = view->y + view->height; + r_refdef.vrectx_adj_shift20 = SHIFT20 (view->x); + r_refdef.vrectright_adj_shift20 = SHIFT20 (r_refdef.vrectright); + + r_refdef.fvrectx = (float) view->x; + r_refdef.fvrecty = (float) view->y; + r_refdef.fvrectright = (float) r_refdef.vrectright; + r_refdef.fvrectbottom = (float) r_refdef.vrectbottom; + + r_refdef.fvrectx_adj = (float) view->x - 0.5; + r_refdef.fvrecty_adj = (float) view->y - 0.5; + r_refdef.fvrectright_adj = (float) r_refdef.vrectright - 0.5; + r_refdef.fvrectbottom_adj = (float) r_refdef.vrectbottom - 0.5; + + int aleft = view->x * r_aliasuvscale; + int atop = view->y * r_aliasuvscale; + int awidth = view->width * r_aliasuvscale; + int aheight = view->height * r_aliasuvscale; + r_refdef.aliasvrectleft = aleft; + r_refdef.aliasvrecttop = atop; + r_refdef.aliasvrectright = aleft + awidth; + r_refdef.aliasvrectbottom = atop + aheight; + + // values for perspective projection + // if math were exact, the values would range from 0.5 to to range+0.5 + // hopefully they wll be in the 0.000001 to range+.999999 and truncate + // the polygon rasterization will never render in the first row or column + // but will definately render in the [range] row and column, so adjust the + // buffer origin to get an exact edge to edge fill + xcenter = view->width * XCENTERING + view->x - 0.5; + ycenter = view->height * YCENTERING + view->y - 0.5; + aliasxcenter = xcenter * r_aliasuvscale; + aliasycenter = ycenter * r_aliasuvscale; + + r_refdef.vrect.x = view->x; + r_refdef.vrect.y = view->y; + r_refdef.vrect.width = view->width; + r_refdef.vrect.height = view->height; + + D_ViewChanged (); +} + +static void +sw_set_fov (float x, float y) +{ + int i; + float res_scale; + + r_viewchanged = true; + + // 320*200 1.0 pixelAspect = 1.6 aspect + // 320*240 1.0 pixelAspect = 1.3333 aspect + // proper 320*200 pixelAspect = 0.8333333 + pixelAspect = 1;//FIXME vid.aspect; + + float hFOV = 2 * x; + float vFOV = 2 * y * pixelAspect; + + // general perspective scaling + xscale = r_refdef.vrect.width / hFOV; + yscale = xscale * pixelAspect; + xscaleinv = 1.0 / xscale; + yscaleinv = 1.0 / yscale; + // perspective scaling for alias models + aliasxscale = xscale * r_aliasuvscale; + aliasyscale = yscale * r_aliasuvscale; + // perspective scaling for paricle position + xscaleshrink = (r_refdef.vrect.width - 6) / hFOV; + yscaleshrink = xscaleshrink * pixelAspect; + + // left side clip + screenedge[0].normal[0] = -1.0 / (XCENTERING * hFOV); + screenedge[0].normal[1] = 0; + screenedge[0].normal[2] = 1; + screenedge[0].type = PLANE_ANYZ; + + // right side clip + screenedge[1].normal[0] = 1.0 / ((1.0 - XCENTERING) * hFOV); + screenedge[1].normal[1] = 0; + screenedge[1].normal[2] = 1; + screenedge[1].type = PLANE_ANYZ; + + // top side clip + screenedge[2].normal[0] = 0; + screenedge[2].normal[1] = -1.0 / (YCENTERING * vFOV); + screenedge[2].normal[2] = 1; + screenedge[2].type = PLANE_ANYZ; + + // bottom side clip + screenedge[3].normal[0] = 0; + screenedge[3].normal[1] = 1.0 / ((1.0 - YCENTERING) * vFOV); + screenedge[3].normal[2] = 1; + screenedge[3].type = PLANE_ANYZ; + + for (i = 0; i < 4; i++) + VectorNormalize (screenedge[i].normal); + + res_scale = sqrt ((double) (r_refdef.vrect.width * r_refdef.vrect.height) / + (320.0 * 152.0)) * (2.0 / hFOV); + r_aliastransition = r_aliastransbase * res_scale; + r_resfudge = r_aliastransadj * res_scale; +} + +static void +sw_capture_screen (capfunc_t callback, void *data) +{ + int count, x, y; + tex_t *tex; + const byte *src; + byte *dst; + framebuffer_t *fb = sw_ctx->framebuffer; + + count = fb->width * fb->height; + tex = malloc (sizeof (tex_t) + count * 3); + if (tex) { + tex->data = (byte *) (tex + 1); + tex->width = fb->width; + tex->height = fb->height; + tex->format = tex_rgb; + tex->palette = 0; + tex->flagbits = 0; + tex->loaded = 1; + src = ((sw_framebuffer_t *) fb->buffer)->color; + int rowbytes = ((sw_framebuffer_t *) fb->buffer)->rowbytes; + for (y = 0; y < tex->height; y++) { + dst = tex->data + y * tex->width * 3; + for (x = 0; x < tex->width; x++) { + byte c = src[x]; + *dst++ = vid.basepal[c * 3 + 0]; + *dst++ = vid.basepal[c * 3 + 1]; + *dst++ = vid.basepal[c * 3 + 2]; + } + src += rowbytes; + } + } + callback (tex, data); +} + +vid_render_funcs_t sw_vid_render_funcs = { + .init = sw_vid_render_init, + + .UpdateScreen = SCR_UpdateScreen_legacy, + + .Draw_CharBuffer = sw_Draw_CharBuffer, + .Draw_Character = Draw_Character, + .Draw_String = Draw_String, + .Draw_nString = Draw_nString, + .Draw_AltString = Draw_AltString, + .Draw_ConsoleBackground = Draw_ConsoleBackground, + .Draw_Crosshair = Draw_Crosshair, + .Draw_CrosshairAt = Draw_CrosshairAt, + .Draw_TileClear = Draw_TileClear, + .Draw_Fill = Draw_Fill, + .Draw_Line = Draw_Line, + .Draw_TextBox = Draw_TextBox, + .Draw_FadeScreen = Draw_FadeScreen, + .Draw_BlendScreen = Draw_BlendScreen, + .Draw_CachePic = Draw_CachePic, + .Draw_UncachePic = Draw_UncachePic, + .Draw_MakePic = Draw_MakePic, + .Draw_DestroyPic = Draw_DestroyPic, + .Draw_PicFromWad = Draw_PicFromWad, + .Draw_Pic = Draw_Pic, + .Draw_FitPic = Draw_FitPic, + .Draw_Picf = Draw_Picf, + .Draw_SubPic = Draw_SubPic, + .Draw_AddFont = Draw_AddFont, + .Draw_Glyph = Draw_Glyph, + + .ParticleSystem = sw_ParticleSystem, + .R_Init = sw_R_Init, + .R_ClearState = R_ClearState, + .R_LoadSkys = R_LoadSkys, + .R_NewScene = R_NewScene, + .R_LineGraph = R_LineGraph, + .begin_frame = sw_begin_frame, + .render_view = sw_render_view, + .draw_particles = R_DrawParticles, + .draw_transparent = sw_draw_transparent, + .post_process = sw_post_process, + .set_2d = sw_set_2d, + .end_frame = sw_end_frame, + + .create_cube_map = sw_create_cube_map, + .create_frame_buffer = sw_create_frame_buffer, + .destroy_frame_buffer = sw_destroy_frame_buffer, + .bind_framebuffer = sw_bind_framebuffer, + .set_viewport = sw_set_viewport, + .set_fov = sw_set_fov, + + .capture_screen = sw_capture_screen, + + .model_funcs = &model_funcs +}; + static general_funcs_t plugin_info_general_funcs = { - sw_vid_render_init, - sw_vid_render_shutdown, + .shutdown = sw_vid_render_shutdown, }; static general_data_t plugin_info_general_data; static plugin_funcs_t plugin_info_funcs = { - &plugin_info_general_funcs, - 0, - 0, - 0, - 0, - 0, - &sw_vid_render_funcs, + .general = &plugin_info_general_funcs, + .vid_render = &sw_vid_render_funcs, }; static plugin_data_t plugin_info_data = { - &plugin_info_general_data, - 0, - 0, - 0, - 0, - 0, - &vid_render_data, + .general = &plugin_info_general_data, + .vid_render = &vid_render_data, }; static plugin_t plugin_info = { - qfp_snd_render, + qfp_vid_render, 0, QFPLUGIN_VERSION, "0.1", diff --git a/libs/video/renderer/vid_render_sw32.c b/libs/video/renderer/vid_render_sw32.c deleted file mode 100644 index 9660f5afb..000000000 --- a/libs/video/renderer/vid_render_sw32.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - vid_render_gl.c - - SW32 version of the renderer - - Copyright (C) 2012 Bill Currie - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define NH_DEFINE -#include "sw32/namehack.h" - -#include "QF/plugin/general.h" -#include "QF/plugin/vid_render.h" - -#include "mod_internal.h" -#include "r_internal.h" - -#include "sw32/namehack.h" - -static vid_model_funcs_t model_funcs = { - sw_Mod_LoadExternalTextures, - sw_Mod_LoadLighting, - sw_Mod_SubdivideSurface, - sw_Mod_ProcessTexture, - - Mod_LoadIQM, - Mod_LoadAliasModel, - Mod_LoadSpriteModel, - - sw_Mod_MakeAliasModelDisplayLists, - sw_Mod_LoadSkin, - sw_Mod_FinalizeAliasModel, - sw_Mod_LoadExternalSkins, - sw_Mod_IQMFinish, - 1, - sw_Mod_SpriteLoadTexture, - - Skin_SetColormap, - Skin_SetSkin, - sw_Skin_SetupSkin, - Skin_SetTranslation, - sw_Skin_ProcessTranslation, - sw_Skin_InitTranslations, -}; - -vid_render_funcs_t sw32_vid_render_funcs = { - sw32_Draw_Init, - sw32_Draw_Character, - sw32_Draw_String, - sw32_Draw_nString, - sw32_Draw_AltString, - sw32_Draw_ConsoleBackground, - sw32_Draw_Crosshair, - sw32_Draw_CrosshairAt, - sw32_Draw_TileClear, - sw32_Draw_Fill, - sw32_Draw_TextBox, - sw32_Draw_FadeScreen, - sw32_Draw_BlendScreen, - sw32_Draw_CachePic, - sw32_Draw_UncachePic, - sw32_Draw_MakePic, - sw32_Draw_DestroyPic, - sw32_Draw_PicFromWad, - sw32_Draw_Pic, - sw32_Draw_Picf, - sw32_Draw_SubPic, - - sw32_SCR_UpdateScreen, - SCR_DrawRam, - SCR_DrawTurtle, - SCR_DrawPause, - sw32_SCR_CaptureBGR, - sw32_SCR_ScreenShot, - SCR_DrawStringToSnap, - - 0, - 0, - - sw32_R_Init, - sw32_R_ClearState, - sw32_R_LoadSkys, - sw32_R_NewMap, - R_AddEfrags, - R_RemoveEfrags, - R_EnqueueEntity, - sw32_R_LineGraph, - R_AllocDlight, - R_AllocEntity, - sw32_R_RenderView, - R_DecayLights, - sw32_R_ViewChanged, - R_CompileParticlePhysics, - R_AddParticlePhysicsFunction, - sw32_R_ClearParticles, - sw32_R_InitParticles, - sw32_SCR_ScreenShot_f, - sw32_r_easter_eggs_f, - sw32_r_particles_style_f, - 0, - &model_funcs -}; - -static void -sw32_vid_render_init (void) -{ - vr_funcs = &sw32_vid_render_funcs; - m_funcs = &model_funcs; -} - -static void -sw32_vid_render_shutdown (void) -{ -} - -static general_funcs_t plugin_info_general_funcs = { - sw32_vid_render_init, - sw32_vid_render_shutdown, -}; - -static general_data_t plugin_info_general_data; - -static plugin_funcs_t plugin_info_funcs = { - &plugin_info_general_funcs, - 0, - 0, - 0, - 0, - 0, - &sw32_vid_render_funcs, -}; - -static plugin_data_t plugin_info_data = { - &plugin_info_general_data, - 0, - 0, - 0, - 0, - 0, - &vid_render_data, -}; - -static plugin_t plugin_info = { - qfp_snd_render, - 0, - QFPLUGIN_VERSION, - "0.1", - "SW32 Renderer", - "Copyright (C) 1996-1997 Id Software, Inc.\n" - "Copyright (C) 1999-2012 contributors of the QuakeForge project\n" - "Please see the file \"AUTHORS\" for a list of contributors", - &plugin_info_funcs, - &plugin_info_data, -}; - -PLUGIN_INFO(vid_render, sw32) -{ - return &plugin_info; -} diff --git a/libs/video/renderer/vid_render_vulkan.c b/libs/video/renderer/vid_render_vulkan.c new file mode 100644 index 000000000..ac3dc3ad0 --- /dev/null +++ b/libs/video/renderer/vid_render_vulkan.c @@ -0,0 +1,640 @@ +/* + vid_render_vulkan.c + + Vulkan version of the renderer + + Copyright (C) 2019 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "QF/cvar.h" +#include "QF/darray.h" +#include "QF/dstring.h" +#include "QF/quakefs.h" +#include "QF/sys.h" +#include "QF/va.h" + +#include "QF/plugin/general.h" +#include "QF/plugin/vid_render.h" + +#include "QF/Vulkan/qf_alias.h" +#include "QF/Vulkan/qf_bsp.h" +#include "QF/Vulkan/qf_compose.h" +#include "QF/Vulkan/qf_draw.h" +#include "QF/Vulkan/qf_iqm.h" +#include "QF/Vulkan/qf_lighting.h" +#include "QF/Vulkan/qf_lightmap.h" +#include "QF/Vulkan/qf_main.h" +#include "QF/Vulkan/qf_matrices.h" +#include "QF/Vulkan/qf_output.h" +#include "QF/Vulkan/qf_palette.h" +#include "QF/Vulkan/qf_particles.h" +#include "QF/Vulkan/qf_scene.h" +#include "QF/Vulkan/qf_sprite.h" +#include "QF/Vulkan/qf_texture.h" +#include "QF/Vulkan/qf_translucent.h" +#include "QF/Vulkan/qf_vid.h" +#include "QF/Vulkan/capture.h" +#include "QF/Vulkan/command.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/image.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/projection.h" +#include "QF/Vulkan/render.h" +#include "QF/Vulkan/staging.h" +#include "QF/Vulkan/swapchain.h" +#include "QF/ui/view.h" + +#include "QF/scene/entity.h" +#include "QF/scene/scene.h" + +#include "mod_internal.h" +#include "r_internal.h" +#include "vid_internal.h" +#include "vid_vulkan.h" +#include "vulkan/vkparse.h" + +static vulkan_ctx_t *vulkan_ctx; + +static struct psystem_s * +vulkan_ParticleSystem (void) +{ + return Vulkan_ParticleSystem (vulkan_ctx); +} + +static void +vulkan_R_Init (void) +{ + QFV_Render_Init (vulkan_ctx); + + Vulkan_CreateStagingBuffers (vulkan_ctx); + Vulkan_Texture_Init (vulkan_ctx); + + Vulkan_CreateSwapchain (vulkan_ctx); + + QFV_Capture_Init (vulkan_ctx); + Vulkan_Output_Init (vulkan_ctx); + + Vulkan_Matrix_Init (vulkan_ctx); + Vulkan_Scene_Init (vulkan_ctx); + Vulkan_Alias_Init (vulkan_ctx); + Vulkan_Bsp_Init (vulkan_ctx); + Vulkan_IQM_Init (vulkan_ctx); + Vulkan_Particles_Init (vulkan_ctx); + Vulkan_Sprite_Init (vulkan_ctx); + Vulkan_Draw_Init (vulkan_ctx); + Vulkan_Lighting_Init (vulkan_ctx); + Vulkan_Translucent_Init (vulkan_ctx); + Vulkan_Compose_Init (vulkan_ctx); + + QFV_LoadRenderInfo (vulkan_ctx, "main_def"); + QFV_LoadSamplerInfo (vulkan_ctx, "smp_quake"); + QFV_BuildRender (vulkan_ctx); + + Vulkan_Texture_Setup (vulkan_ctx); + Vulkan_Palette_Init (vulkan_ctx, vid.palette); + Vulkan_Alias_Setup (vulkan_ctx); + Vulkan_Bsp_Setup (vulkan_ctx); + Vulkan_IQM_Setup (vulkan_ctx); + Vulkan_Matrix_Setup (vulkan_ctx); + Vulkan_Scene_Setup (vulkan_ctx); + Vulkan_Sprite_Setup (vulkan_ctx); + Vulkan_Output_Setup (vulkan_ctx); + Vulkan_Compose_Setup (vulkan_ctx); + Vulkan_Draw_Setup (vulkan_ctx); + Vulkan_Particles_Setup (vulkan_ctx); + Vulkan_Translucent_Setup (vulkan_ctx); + Vulkan_Lighting_Setup (vulkan_ctx); + + Skin_Init (); + + SCR_Init (); +} + +static void +vulkan_R_ClearState (void) +{ + QFV_DeviceWaitIdle (vulkan_ctx->device); + //FIXME clear scene correctly + r_refdef.worldmodel = 0; + EntQueue_Clear (r_ent_queue); + R_ClearDlights (); + R_ClearParticles (); + Vulkan_LoadLights (0, vulkan_ctx); +} + +static void +vulkan_R_LoadSkys (const char *skyname) +{ + Vulkan_LoadSkys (skyname, vulkan_ctx); +} + +static void +vulkan_R_NewScene (scene_t *scene) +{ + Vulkan_NewScene (scene, vulkan_ctx); +} + +static void +vulkan_R_LineGraph (int x, int y, int *h_vals, int count, int height) +{ + Vulkan_LineGraph (x, y, h_vals, count, height, vulkan_ctx); +} + +static void +vulkan_Draw_CharBuffer (int x, int y, draw_charbuffer_t *buffer) +{ + Vulkan_Draw_CharBuffer (x, y, buffer, vulkan_ctx); +} + +static void +vulkan_Draw_SetScale (int scale) +{ + vulkan_ctx->twod_scale = max (1, scale); +} + +static void +vulkan_Draw_Character (int x, int y, unsigned ch) +{ + Vulkan_Draw_Character (x, y, ch, vulkan_ctx); +} + +static void +vulkan_Draw_String (int x, int y, const char *str) +{ + Vulkan_Draw_String (x, y, str, vulkan_ctx); +} + +static void +vulkan_Draw_nString (int x, int y, const char *str, int count) +{ + Vulkan_Draw_nString (x, y, str, count, vulkan_ctx); +} + +static void +vulkan_Draw_AltString (int x, int y, const char *str) +{ + Vulkan_Draw_AltString (x, y, str, vulkan_ctx); +} + +static void +vulkan_Draw_ConsoleBackground (int lines, byte alpha) +{ + Vulkan_Draw_ConsoleBackground (lines, alpha, vulkan_ctx); +} + +static void +vulkan_Draw_Crosshair (void) +{ + Vulkan_Draw_Crosshair (vulkan_ctx); +} + +static void +vulkan_Draw_CrosshairAt (int ch, int x, int y) +{ + Vulkan_Draw_CrosshairAt (ch, x, y, vulkan_ctx); +} + +static void +vulkan_Draw_TileClear (int x, int y, int w, int h) +{ + Vulkan_Draw_TileClear (x, y, w, h, vulkan_ctx); +} + +static void +vulkan_Draw_Fill (int x, int y, int w, int h, int c) +{ + Vulkan_Draw_Fill (x, y, w, h, c, vulkan_ctx); +} + +static void +vulkan_Draw_Line (int x0, int y0, int x1, int y1, int c) +{ + Vulkan_Draw_Line (x0, y0, x1, y1, c, vulkan_ctx); +} + +static void +vulkan_Draw_TextBox (int x, int y, int width, int lines, byte alpha) +{ + Vulkan_Draw_TextBox (x, y, width, lines, alpha, vulkan_ctx); +} + +static void +vulkan_Draw_FadeScreen (void) +{ + Vulkan_Draw_FadeScreen (vulkan_ctx); +} + +static void +vulkan_Draw_BlendScreen (quat_t color) +{ + Vulkan_Draw_BlendScreen (color, vulkan_ctx); +} + +static qpic_t * +vulkan_Draw_CachePic (const char *path, bool alpha) +{ + return Vulkan_Draw_CachePic (path, alpha, vulkan_ctx); +} + +static void +vulkan_Draw_UncachePic (const char *path) +{ + Vulkan_Draw_UncachePic (path, vulkan_ctx); +} + +static qpic_t * +vulkan_Draw_MakePic (int width, int height, const byte *data) +{ + return Vulkan_Draw_MakePic (width, height, data, vulkan_ctx); +} + +static void +vulkan_Draw_DestroyPic (qpic_t *pic) +{ + Vulkan_Draw_DestroyPic (pic, vulkan_ctx); +} + +static qpic_t * +vulkan_Draw_PicFromWad (const char *name) +{ + return Vulkan_Draw_PicFromWad (name, vulkan_ctx); +} + +static void +vulkan_Draw_Pic (int x, int y, qpic_t *pic) +{ + Vulkan_Draw_Pic (x, y, pic, vulkan_ctx); +} + +static void +vulkan_Draw_FitPic (int x, int y, int width, int height, qpic_t *pic) +{ + Vulkan_Draw_FitPic (x, y, width, height, pic, vulkan_ctx); +} + +static void +vulkan_Draw_Picf (float x, float y, qpic_t *pic) +{ + Vulkan_Draw_Picf (x, y, pic, vulkan_ctx); +} + +static void +vulkan_Draw_SubPic (int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height) +{ + Vulkan_Draw_SubPic (x, y, pic, srcx, srcy, width, height, vulkan_ctx); +} + +static int +vulkan_Draw_AddFont (struct font_s *font) +{ + return Vulkan_Draw_AddFont (font, vulkan_ctx); +} + +static void +vulkan_Draw_Glyph (int x, int y, int fontid, int glyphid, int c) +{ + Vulkan_Draw_Glyph (x, y, fontid, glyphid, c, vulkan_ctx); +} + +static void +vulkan_set_2d (int scaled) +{ + //FIXME this should not be done every frame + __auto_type mctx = vulkan_ctx->matrix_context; + __auto_type mat = &mctx->matrices; + int scale = vulkan_ctx->twod_scale; + + float left = 0; + float top = 0; + float right = left + vid.width / scale; + float bottom = top + vid.height / scale; + QFV_Orthographic (mat->Projection2d, left, right, top, bottom, 0, 99999); + mat->ScreenSize = (vec2f_t) { 1.0 / vid.width, 1.0 / vid.height }; + mctx->dirty = mctx->frames.size; +} + +static void +vulkan_UpdateScreen (transform_t camera, double realtime, SCR_Func *scr_funcs) +{ + vulkan_set_2d (1);//FIXME + Vulkan_SetScrFuncs (scr_funcs, vulkan_ctx); + QFV_RunRenderJob (vulkan_ctx); +} + +static void +vulkan_set_fov (float x, float y) +{ + if (!vulkan_ctx || !vulkan_ctx->matrix_context) { + return; + } + __auto_type mctx = vulkan_ctx->matrix_context; + __auto_type mat = &mctx->matrices; + + QFV_PerspectiveTan (mat->Projection3d, x, y); + + mctx->dirty = mctx->frames.size; +} + +static void +vulkan_capture_screen (capfunc_t callback, void *data) +{ + QFV_Capture_Screen (vulkan_ctx, callback, data); +} + +static void +vulkan_Mod_LoadLighting (model_t *mod, bsp_t *bsp) +{ + Vulkan_Mod_LoadLighting (mod, bsp, vulkan_ctx); +} + +static void +vulkan_Mod_SubdivideSurface (model_t *mod, msurface_t *fa) +{ +} + +static void +vulkan_Mod_ProcessTexture (model_t *mod, texture_t *tx) +{ + Vulkan_Mod_ProcessTexture (mod, tx, vulkan_ctx); +} + +static void +vulkan_Mod_MakeAliasModelDisplayLists (mod_alias_ctx_t *alias_ctx, + void *_m, int _s, int extra) +{ + Vulkan_Mod_MakeAliasModelDisplayLists (alias_ctx, _m, _s, extra, + vulkan_ctx); +} + +static void +vulkan_Mod_LoadAllSkins (mod_alias_ctx_t *alias_ctx) +{ + Vulkan_Mod_LoadAllSkins (alias_ctx, vulkan_ctx); +} + +static void +vulkan_Mod_FinalizeAliasModel (mod_alias_ctx_t *alias_ctx) +{ + Vulkan_Mod_FinalizeAliasModel (alias_ctx, vulkan_ctx); +} + +static void +vulkan_Mod_LoadExternalSkins (mod_alias_ctx_t *alias_ctx) +{ +} + +static void +vulkan_Mod_IQMFinish (model_t *mod) +{ + Vulkan_Mod_IQMFinish (mod, vulkan_ctx); +} + +static void +vulkan_Mod_SpriteLoadFrames (mod_sprite_ctx_t *sprite_ctx) +{ + Vulkan_Mod_SpriteLoadFrames (sprite_ctx, vulkan_ctx); +} + +static void +vulkan_Skin_SetupSkin (struct skin_s *skin, int cmap) +{ +} + +static void +vulkan_Skin_ProcessTranslation (int cmap, const byte *translation) +{ +} + +static void +vulkan_Skin_InitTranslations (void) +{ +} + +static void +set_palette (void *data, const byte *palette) +{ + if (vulkan_ctx->palette_context) { + Vulkan_Palette_Update (vulkan_ctx, palette); + } +} + +static void +vulkan_vid_render_choose_visual (void *data) +{ + Vulkan_CreateDevice (vulkan_ctx); + if (!vulkan_ctx->device) { + Sys_Error ("Unable to create Vulkan device.%s", + vulkan_use_validation ? "" + : "\nSet vulkan_use_validation for details"); + } + vulkan_ctx->choose_visual (vulkan_ctx); + vulkan_ctx->cmdpool = QFV_CreateCommandPool (vulkan_ctx->device, + vulkan_ctx->device->queue.queueFamily, + 0, 1); + Sys_MaskPrintf (SYS_vulkan, "vk choose visual %p %p %d %#zx\n", + vulkan_ctx->device->dev, vulkan_ctx->device->queue.queue, + vulkan_ctx->device->queue.queueFamily, + (size_t) vulkan_ctx->cmdpool); +} + +static void +vulkan_vid_render_create_context (void *data) +{ + vulkan_ctx->create_window (vulkan_ctx); + vulkan_ctx->surface = vulkan_ctx->create_surface (vulkan_ctx); + Sys_MaskPrintf (SYS_vulkan, "vk create context: surface:%#zx\n", + (size_t) vulkan_ctx->surface); +} + +static vid_model_funcs_t model_funcs = { + .texture_render_size = sizeof (vulktex_t) + 2 * sizeof (qfv_tex_t), + + .Mod_LoadLighting = vulkan_Mod_LoadLighting, + .Mod_SubdivideSurface = vulkan_Mod_SubdivideSurface, + .Mod_ProcessTexture = vulkan_Mod_ProcessTexture, + + .Mod_LoadIQM = Mod_LoadIQM, + .Mod_LoadAliasModel = Mod_LoadAliasModel, + .Mod_LoadSpriteModel = Mod_LoadSpriteModel, + + .Mod_MakeAliasModelDisplayLists = vulkan_Mod_MakeAliasModelDisplayLists, + .Mod_LoadAllSkins = vulkan_Mod_LoadAllSkins, + .Mod_FinalizeAliasModel = vulkan_Mod_FinalizeAliasModel, + .Mod_LoadExternalSkins = vulkan_Mod_LoadExternalSkins, + .Mod_IQMFinish = vulkan_Mod_IQMFinish, + .alias_cache = 0, + .Mod_SpriteLoadFrames = vulkan_Mod_SpriteLoadFrames, + + .Skin_Free = Skin_Free, + .Skin_SetColormap = Skin_SetColormap, + .Skin_SetSkin = Skin_SetSkin, + .Skin_SetupSkin = vulkan_Skin_SetupSkin, + .Skin_SetTranslation = Skin_SetTranslation, + .Skin_ProcessTranslation = vulkan_Skin_ProcessTranslation, + .Skin_InitTranslations = vulkan_Skin_InitTranslations, +}; + +static void +vulkan_vid_render_init (void) +{ + if (!vr_data.vid->vid_internal->vulkan_context) { + Sys_Error ("Sorry, Vulkan not supported by this program."); + } + vid_internal_t *vi = vr_data.vid->vid_internal; + vulkan_ctx = vi->vulkan_context (vi); + vulkan_ctx->load_vulkan (vulkan_ctx); + + Vulkan_Init_Common (vulkan_ctx); + + vi->set_palette = set_palette; + vi->choose_visual = vulkan_vid_render_choose_visual; + vi->create_context = vulkan_vid_render_create_context; + + vr_funcs = &vulkan_vid_render_funcs; + m_funcs = &model_funcs; +} + +static void +vulkan_vid_render_shutdown (void) +{ + if (!vulkan_ctx || !vulkan_ctx->device) { + return; + } + qfv_device_t *device = vulkan_ctx->device; + qfv_devfuncs_t *df = device->funcs; + VkDevice dev = device->dev; + QFV_DeviceWaitIdle (device); + + SCR_Shutdown (); + Mod_ClearAll (); + + Vulkan_Compose_Shutdown (vulkan_ctx); + Vulkan_Translucent_Shutdown (vulkan_ctx); + Vulkan_Lighting_Shutdown (vulkan_ctx); + Vulkan_Draw_Shutdown (vulkan_ctx); + Vulkan_Sprite_Shutdown (vulkan_ctx); + Vulkan_Particles_Shutdown (vulkan_ctx); + Vulkan_IQM_Shutdown (vulkan_ctx); + Vulkan_Bsp_Shutdown (vulkan_ctx); + Vulkan_Alias_Shutdown (vulkan_ctx); + Vulkan_Scene_Shutdown (vulkan_ctx); + Vulkan_Matrix_Shutdown (vulkan_ctx); + + QFV_Capture_Shutdown (vulkan_ctx); + Vulkan_Output_Shutdown (vulkan_ctx); + + Vulkan_Palette_Shutdown (vulkan_ctx); + Vulkan_Texture_Shutdown (vulkan_ctx); + + QFV_Render_Shutdown (vulkan_ctx); + + QFV_DestroyStagingBuffer (vulkan_ctx->staging); + df->vkDestroyCommandPool (dev, vulkan_ctx->cmdpool, 0); + + Vulkan_Shutdown_Common (vulkan_ctx); +} + +vid_render_funcs_t vulkan_vid_render_funcs = { + .init = vulkan_vid_render_init, + + .UpdateScreen = vulkan_UpdateScreen, + + .Draw_CharBuffer = vulkan_Draw_CharBuffer, + .Draw_SetScale = vulkan_Draw_SetScale, + .Draw_Character = vulkan_Draw_Character, + .Draw_String = vulkan_Draw_String, + .Draw_nString = vulkan_Draw_nString, + .Draw_AltString = vulkan_Draw_AltString, + .Draw_ConsoleBackground = vulkan_Draw_ConsoleBackground, + .Draw_Crosshair = vulkan_Draw_Crosshair, + .Draw_CrosshairAt = vulkan_Draw_CrosshairAt, + .Draw_TileClear = vulkan_Draw_TileClear, + .Draw_Fill = vulkan_Draw_Fill, + .Draw_Line = vulkan_Draw_Line, + .Draw_TextBox = vulkan_Draw_TextBox, + .Draw_FadeScreen = vulkan_Draw_FadeScreen, + .Draw_BlendScreen = vulkan_Draw_BlendScreen, + .Draw_CachePic = vulkan_Draw_CachePic, + .Draw_UncachePic = vulkan_Draw_UncachePic, + .Draw_MakePic = vulkan_Draw_MakePic, + .Draw_DestroyPic = vulkan_Draw_DestroyPic, + .Draw_PicFromWad = vulkan_Draw_PicFromWad, + .Draw_Pic = vulkan_Draw_Pic, + .Draw_FitPic = vulkan_Draw_FitPic, + .Draw_Picf = vulkan_Draw_Picf, + .Draw_SubPic = vulkan_Draw_SubPic, + .Draw_AddFont = vulkan_Draw_AddFont, + .Draw_Glyph = vulkan_Draw_Glyph, + + .ParticleSystem = vulkan_ParticleSystem, + .R_Init = vulkan_R_Init, + .R_ClearState = vulkan_R_ClearState, + .R_LoadSkys = vulkan_R_LoadSkys, + .R_NewScene = vulkan_R_NewScene, + .R_LineGraph = vulkan_R_LineGraph, + + .set_fov = vulkan_set_fov, + + .capture_screen = vulkan_capture_screen, + + .model_funcs = &model_funcs +}; + +static general_funcs_t plugin_info_general_funcs = { + .shutdown = vulkan_vid_render_shutdown, +}; + +static general_data_t plugin_info_general_data; + +static plugin_funcs_t plugin_info_funcs = { + .general = &plugin_info_general_funcs, + .vid_render = &vulkan_vid_render_funcs, +}; + +static plugin_data_t plugin_info_data = { + .general = &plugin_info_general_data, + .vid_render = &vid_render_data, +}; + +static plugin_t plugin_info = { + qfp_vid_render, + 0, + QFPLUGIN_VERSION, + "0.1", + "Vulkan Renderer", + "Copyright (C) 2019 Bill Currie \n" + "Please see the file \"AUTHORS\" for a list of contributors", + &plugin_info_funcs, + &plugin_info_data, +}; + +PLUGIN_INFO(vid_render, vulkan) +{ + return &plugin_info; +} diff --git a/libs/video/renderer/vulkan/barrier.c b/libs/video/renderer/vulkan/barrier.c new file mode 100644 index 000000000..37d45bdf0 --- /dev/null +++ b/libs/video/renderer/vulkan/barrier.c @@ -0,0 +1,265 @@ +/* + barrier.c + + Memory barrier helpers + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/Vulkan/barrier.h" + +const qfv_imagebarrier_t imageBarriers[] = { + [qfv_LT_Undefined_to_TransferDst] = { + .srcStages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + .dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT, + .barrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 0, + 0, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, 0, + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } + }, + }, + [qfv_LT_Undefined_to_General] = { + .srcStages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + .dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT, + .barrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 0, + 0, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_GENERAL, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, 0, + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } + }, + }, + [qfv_LT_TransferDst_to_TransferSrc] = { + .srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT, + .dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT, + .barrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 0, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_TRANSFER_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, 0, + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } + }, + }, + [qfv_LT_TransferDst_to_General] = { + .srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT, + .dstStages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + .barrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 0, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_GENERAL, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, 0, + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } + }, + }, + [qfv_LT_TransferDst_to_ShaderReadOnly] = { + .srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT, + .dstStages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + .barrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 0, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, 0, + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } + }, + }, + [qfv_LT_TransferSrc_to_ShaderReadOnly] = { + .srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT, + .dstStages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + .barrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 0, + VK_ACCESS_TRANSFER_READ_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, 0, + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } + }, + }, + [qfv_LT_ShaderReadOnly_to_TransferDst] = { + .srcStages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + .dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT, + .barrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 0, + VK_ACCESS_SHADER_READ_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, 0, + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } + }, + }, + [qfv_LT_Undefined_to_DepthStencil] = { + .srcStages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + .dstStages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, + .barrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 0, + 0, + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT + | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, 0, + { VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1 } + }, + }, + [qfv_LT_Undefined_to_Color] = { + .srcStages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + .dstStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .barrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 0, + 0, + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT + | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, 0, + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } + }, + }, +}; + +const qfv_bufferbarrier_t bufferBarriers[] = { + [qfv_BB_Unknown_to_TransferWrite] = { + .srcStages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + .dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT, + .barrier = { + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, 0, + 0, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + }, + }, + [qfv_BB_TransferWrite_to_VertexAttrRead] = { + .srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT, + .dstStages = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, + .barrier = { + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, 0, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + }, + }, + [qfv_BB_TransferWrite_to_IndexRead] = { + .srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT, + .dstStages = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, + .barrier = { + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, 0, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_INDEX_READ_BIT, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + }, + }, + [qfv_BB_TransferWrite_to_UniformRead] = { + // note: not necessarily optimal as it uses vertex shader for dst + .srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT, + .dstStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, + .barrier = { + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, 0, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_UNIFORM_READ_BIT, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + }, + }, + [qfv_BB_TransferWrite_to_ShaderRW] = { + .srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT, + .dstStages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + .barrier = { + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, 0, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + }, + }, + [qfv_BB_ShaderRW_to_ShaderRO] = { + .srcStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + .dstStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + .barrier = { + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, 0, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + }, + }, + [qfv_BB_ShaderRW_to_ShaderRO_VA] = { + .srcStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + .dstStages = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT + | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + .barrier = { + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, 0, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + }, + }, + [qfv_BB_ShaderRO_to_ShaderWrite] = { + .srcStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + .dstStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + .barrier = { + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, 0, + VK_ACCESS_SHADER_READ_BIT, + VK_ACCESS_SHADER_WRITE_BIT, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + }, + }, + [qfv_BB_ShaderRO_VA_to_ShaderWrite] = { + .srcStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT + | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, + .dstStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + .barrier = { + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, 0, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, + VK_ACCESS_SHADER_WRITE_BIT, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + }, + }, + [qfv_BB_ShaderWrite_to_ShaderRO] = { + .srcStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + .dstStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + .barrier = { + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, 0, + VK_ACCESS_SHADER_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + }, + }, + [qfv_BB_ShaderWrite_to_ShaderRW] = { + .srcStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + .dstStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + .barrier = { + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, 0, + VK_ACCESS_SHADER_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + }, + }, +}; diff --git a/libs/video/renderer/vulkan/buffer.c b/libs/video/renderer/vulkan/buffer.c new file mode 100644 index 000000000..95f5fea74 --- /dev/null +++ b/libs/video/renderer/vulkan/buffer.c @@ -0,0 +1,144 @@ +/* + buffer.c + + Vulkan buffer functions + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/mathlib.h" +#include "QF/Vulkan/buffer.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/instance.h" + +VkBuffer +QFV_CreateBuffer (qfv_device_t *device, VkDeviceSize size, + VkBufferUsageFlags usage) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + VkBufferCreateInfo createInfo = { + VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 0, + 0, + size, usage, VK_SHARING_MODE_EXCLUSIVE, + 0, 0 + }; + VkBuffer buffer; + dfunc->vkCreateBuffer (dev, &createInfo, 0, &buffer); + return buffer; +} + +VkDeviceMemory +QFV_AllocBufferMemory (qfv_device_t *device, + VkBuffer buffer, VkMemoryPropertyFlags properties, + VkDeviceSize size, VkDeviceSize offset) +{ + VkDevice dev = device->dev; + qfv_physdev_t *physdev = device->physDev; + VkPhysicalDeviceMemoryProperties *memprops = &physdev->memory_properties; + qfv_devfuncs_t *dfunc = device->funcs; + + VkMemoryRequirements requirements; + dfunc->vkGetBufferMemoryRequirements (dev, buffer, &requirements); + + size = max (size, offset + requirements.size); + VkDeviceMemory object = 0; + + for (uint32_t type = 0; type < memprops->memoryTypeCount; type++) { + if ((requirements.memoryTypeBits & (1 << type)) + && ((memprops->memoryTypes[type].propertyFlags & properties) + == properties)) { + VkMemoryAllocateInfo allocate_info = { + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, 0, + size, type + }; + + VkResult res = dfunc->vkAllocateMemory (dev, &allocate_info, + 0, &object); + if (res == VK_SUCCESS) { + break; + } + } + } + + return object; +} + +int +QFV_BindBufferMemory (qfv_device_t *device, + VkBuffer buffer, VkDeviceMemory object, + VkDeviceSize offset) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + VkResult res = dfunc->vkBindBufferMemory (dev, buffer, object, offset); + return res == VK_SUCCESS; +} + +qfv_bufferbarrierset_t * +QFV_CreateBufferTransitions (qfv_buffertransition_t *transitions, + int numTransitions) +{ + qfv_bufferbarrierset_t *barrierset; + barrierset = DARRAY_ALLOCFIXED (*barrierset, numTransitions, malloc); + + for (int i = 0; i < numTransitions; i++) { + barrierset->a[i].sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + barrierset->a[i].pNext = 0; + barrierset->a[i].srcAccessMask = transitions[i].srcAccess; + barrierset->a[i].dstAccessMask = transitions[i].dstAccess; + barrierset->a[i].srcQueueFamilyIndex = transitions[i].srcQueueFamily; + barrierset->a[i].dstQueueFamilyIndex = transitions[i].dstQueueFamily; + barrierset->a[i].buffer = transitions[i].buffer; + barrierset->a[i].offset = transitions[i].offset; + barrierset->a[i].size = transitions[i].size; + } + return barrierset; +} + +VkBufferView +QFV_CreateBufferView (qfv_device_t *device, VkBuffer buffer, VkFormat format, + VkDeviceSize offset, VkDeviceSize size) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + + VkBufferViewCreateInfo createInfo = { + VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, 0, + 0, + buffer, format, offset, size, + }; + + VkBufferView view; + dfunc->vkCreateBufferView (dev, &createInfo, 0, &view); + return view; +} + +VkDeviceSize +QFV_NextOffset (VkDeviceSize current, const VkMemoryRequirements *requirements) +{ + VkDeviceSize align = requirements->alignment - 1; + return ((current + align) & ~align); +} diff --git a/libs/video/renderer/vulkan/capture.c b/libs/video/renderer/vulkan/capture.c new file mode 100644 index 000000000..b83f061a4 --- /dev/null +++ b/libs/video/renderer/vulkan/capture.c @@ -0,0 +1,333 @@ +/* + capture.c + + Vulkan frame capture support + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/cexpr.h" +#include "QF/Vulkan/buffer.h" +#include "QF/Vulkan/capture.h" +#include "QF/Vulkan/command.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/image.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/render.h" +#include "QF/Vulkan/swapchain.h" + +#include "QF/plugin/vid_render.h" +#include "vid_vulkan.h" + +static void +capture_initiate (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + + auto cap = ctx->capture_context; + auto frame = &cap->frames.a[ctx->curFrame]; + + if (!frame->callback) { + return; + } + + auto device = ctx->device; + auto dfunc = device->funcs; + + auto cmd = QFV_GetCmdBuffer (ctx, false); + VkCommandBufferBeginInfo beginInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + dfunc->vkBeginCommandBuffer (cmd, &beginInfo); + + auto sc = ctx->swapchain; + auto scImage = sc->images->a[ctx->swapImageIndex]; + + VkBufferMemoryBarrier start_buffer_barriers[] = { + { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .buffer = frame->buffer, + .offset = 0, + .size = VK_WHOLE_SIZE, + }, + }; + VkImageMemoryBarrier start_image_barriers[] = { + { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_MEMORY_READ_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .image = scImage, + .subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }, + }, + }; + VkBufferMemoryBarrier end_buffer_barriers[] = { + { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, + .buffer = frame->buffer, + .offset = 0, + .size = VK_WHOLE_SIZE, + }, + }; + VkImageMemoryBarrier end_image_barriers[] = { + { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + .image = scImage, + .subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }, + }, + }; + + dfunc->vkCmdPipelineBarrier (cmd, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, 0, 0, + 1, start_buffer_barriers, + 1, start_image_barriers); + + VkBufferImageCopy copy = { + .bufferOffset = 0, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }, + .imageOffset = { }, + .imageExtent = { cap->extent.width, cap->extent.height, 1 }, + }; + dfunc->vkCmdCopyImageToBuffer (cmd, scImage, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + frame->buffer, 1, ©); + + dfunc->vkCmdPipelineBarrier (cmd, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, 0, 0, + 1, end_buffer_barriers, + 1, end_image_barriers); + dfunc->vkEndCommandBuffer (cmd); + QFV_AppendCmdBuffer (ctx, cmd); + + frame->initiated = true; + auto time = Sys_LongTime (); + printf ("capture_initiate: %zd.%03zd.%0zd\n", + time / 1000000, (time / 1000) % 1000, time % 1000); +} + +static int +is_bgr (VkFormat format) +{ + return (format >= VK_FORMAT_B8G8R8A8_UNORM + && format <= VK_FORMAT_B8G8R8A8_SRGB); +} + +static void +capture_finalize (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + + auto cap = ctx->capture_context; + auto frame = &cap->frames.a[ctx->curFrame]; + + if (!frame->callback || !frame->initiated) { + return; + } + auto time = Sys_LongTime (); + printf ("capture_finalize: %zd.%03zd.%0zd\n", + time / 1000000, (time / 1000) % 1000, time % 1000); + + auto device = ctx->device; + auto dfunc = device->funcs; + + VkMappedMemoryRange range = { + VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .memory = cap->memory, + .offset = frame->data - cap->data, + .size = cap->imgsize, + }; + dfunc->vkInvalidateMappedMemoryRanges (device->dev, 1, &range); + + int count = cap->extent.width * cap->extent.height; + tex_t *tex = malloc (sizeof (tex_t) + count * 3); + + if (tex) { + tex->data = (byte *) (tex + 1); + tex->flagbits = 0; + tex->width = cap->extent.width; + tex->height = cap->extent.height; + tex->format = tex_rgb; + tex->palette = 0; + tex->flagbits = 0; + tex->loaded = 1; + + if (is_bgr (ctx->swapchain->format)) { + tex->bgr = 1; + } + const byte *src = frame->data; + byte *dst = tex->data; + while (count-- > 0) { + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; + src++; + } + } + + frame->callback (tex, frame->callback_data);; + frame->callback = 0; + frame->callback_data = 0; + frame->initiated = false; +} + +static exprfunc_t capture_initiate_func[] = { + { .func = capture_initiate }, + {} +}; +static exprfunc_t capture_finalize_func[] = { + { .func = capture_finalize }, + {} +}; +static exprsym_t capture_task_syms[] = { + { "capture_initiate", &cexpr_function, capture_initiate_func }, + { "capture_finalize", &cexpr_function, capture_finalize_func }, + {} +}; + +void +QFV_Capture_Init (vulkan_ctx_t *ctx) +{ + QFV_Render_AddTasks (ctx, capture_task_syms); + + qfvPushDebug (ctx, "capture init"); + + auto device = ctx->device; + auto ifunc = device->physDev->instance->funcs; + auto dfunc = device->funcs; + + ctx->capture_context = calloc (1, sizeof (qfv_capturectx_t)); + auto cap = ctx->capture_context; + + auto swapchain = ctx->swapchain; + VkFormatProperties format_props; + ifunc->vkGetPhysicalDeviceFormatProperties (device->physDev->dev, + swapchain->format, + &format_props); + if (!(swapchain->usage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT)) { + Sys_Printf ("Swapchain does not support reading. FIXME\n"); + return; + } + + cap->device = device; + cap->extent = swapchain->extent; + + auto rctx = ctx->render_context; + size_t frames = rctx->frames.size; + DARRAY_INIT (&cap->frames, frames); + DARRAY_RESIZE (&cap->frames, frames); + cap->frames.grow = 0; + + //FIXME assumes the swapchain is 32bpp + cap->imgsize = swapchain->extent.width * swapchain->extent.height * 4; + + for (size_t i = 0; i < frames; i++) { + auto frame = &cap->frames.a[i]; + *frame = (qfv_capture_frame_t) { + .buffer = QFV_CreateBuffer (device, cap->imgsize, + VK_BUFFER_USAGE_TRANSFER_DST_BIT), + }; + } + VkMemoryRequirements req; + dfunc->vkGetBufferMemoryRequirements (device->dev, + cap->frames.a[0].buffer, + &req); + cap->imgsize = QFV_NextOffset (cap->imgsize, &req); + cap->memsize = frames * cap->imgsize; + cap->memory = QFV_AllocBufferMemory (device, + cap->frames.a[0].buffer, + VK_MEMORY_PROPERTY_HOST_CACHED_BIT, + cap->memsize, 0); + dfunc->vkMapMemory (device->dev, cap->memory, 0, cap->memsize, 0, + (void **) &cap->data); + + for (size_t i = 0; i < frames; i++) { + auto frame = &cap->frames.a[i]; + frame->data = cap->data + i * cap->imgsize; + QFV_BindBufferMemory (device, frame->buffer, cap->memory, + i * cap->imgsize); + } + qfvPopDebug (ctx); +} + +int shut_up_gcc = 1; +void +QFV_Capture_Renew (vulkan_ctx_t *ctx) +{ + if (shut_up_gcc) { + Sys_Error ("QFV_Capture_Renew not implemented"); + } +} + +void +QFV_Capture_Shutdown (vulkan_ctx_t *ctx) +{ + auto device = ctx->device; + auto dfunc = device->funcs; + auto cap = ctx->capture_context; + + for (size_t i = 0; i < cap->frames.size; i++) { + auto frame = &cap->frames.a[i]; + dfunc->vkDestroyBuffer (device->dev, frame->buffer, 0); + } + dfunc->vkUnmapMemory (device->dev, cap->memory); + dfunc->vkFreeMemory (device->dev, cap->memory, 0); + DARRAY_CLEAR (&cap->frames); + free (cap); +} + +void +QFV_Capture_Screen (vulkan_ctx_t *ctx, capfunc_t callback, void *data) +{ + auto cap = ctx->capture_context; + if (!cap->data) { + Sys_Printf ("Capture not supported\n"); + callback (0, data); + return; + } + auto frame = &cap->frames.a[ctx->curFrame]; + frame->callback = callback; + frame->callback_data = data; + auto time = Sys_LongTime (); + printf ("capture_request: %zd.%03zd.%0zd\n", + time / 1000000, (time / 1000) % 1000, time % 1000); +} diff --git a/libs/video/renderer/vulkan/command.c b/libs/video/renderer/vulkan/command.c new file mode 100644 index 000000000..73b443679 --- /dev/null +++ b/libs/video/renderer/vulkan/command.c @@ -0,0 +1,234 @@ +/* + vid_common_vulkan.c + + Common Vulkan video driver functions + + Copyright (C) 2019 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "QF/qtypes.h" +#include "QF/Vulkan/command.h" +#include "QF/Vulkan/device.h" + +qfv_cmdpoolmgr_t * +QFV_CmdPoolManager_Init (qfv_cmdpoolmgr_t *manager, qfv_device_t *device, + const char *name) +{ + *manager = (qfv_cmdpoolmgr_t) { + .primary = DARRAY_STATIC_INIT (16), + .secondary = DARRAY_STATIC_INIT (16), + .device = device, + }; + auto dfunc = device->funcs; + + VkCommandPoolCreateInfo poolCInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, + .queueFamilyIndex = device->queue.queueFamily, + }; + dfunc->vkCreateCommandPool (device->dev, &poolCInfo, 0, &manager->pool); + return manager; +} + +qfv_cmdpoolmgr_t * +QFV_CmdPoolManager_New (qfv_device_t *device, const char *name) +{ + return QFV_CmdPoolManager_Init (malloc (sizeof (qfv_cmdpoolmgr_t)), device, + name); +} + +void +QFV_CmdPoolManager_Shutdown (qfv_cmdpoolmgr_t *manager) +{ + auto device = manager->device; + auto dfunc = device->funcs; + dfunc->vkDestroyCommandPool (device->dev, manager->pool, 0); + DARRAY_CLEAR (&manager->primary); + DARRAY_CLEAR (&manager->secondary); +} + +void +QFV_CmdPoolManager_Delete (qfv_cmdpoolmgr_t *manager) +{ + QFV_CmdPoolManager_Shutdown (manager); + free (manager); +} + +void +QFV_CmdPoolManager_Reset (qfv_cmdpoolmgr_t *manager) +{ + auto device = manager->device; + auto dfunc = device->funcs; + dfunc->vkResetCommandPool (device->dev, manager->pool, 0); + manager->active_primary = 0; + manager->active_secondary = 0; +} + +VkCommandBuffer +QFV_CmdPoolManager_CmdBuffer (qfv_cmdpoolmgr_t *manager, bool secondary) +{ + auto device = manager->device; + auto dfunc = device->funcs; + + if (secondary) { + if (manager->active_secondary < manager->secondary.size) { + return manager->secondary.a[manager->active_secondary++]; + } + } else { + if (manager->active_primary < manager->primary.size) { + return manager->primary.a[manager->active_primary++]; + } + } + VkCommandBufferAllocateInfo cinfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = manager->pool, + .level = secondary ? VK_COMMAND_BUFFER_LEVEL_SECONDARY + : VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + }; + VkCommandBuffer cmd; + dfunc->vkAllocateCommandBuffers (device->dev, &cinfo, &cmd); + if (secondary) { + DARRAY_APPEND (&manager->secondary, cmd); + manager->active_secondary++; + } else { + DARRAY_APPEND (&manager->primary, cmd); + manager->active_primary++; + } + return cmd; +} + +VkCommandPool +QFV_CreateCommandPool (qfv_device_t *device, uint32_t queueFamily, + int transient, int reset) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + uint32_t flags = 0; + if (transient) { + flags |= VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; + } + if (reset) { + flags |= VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + } + VkCommandPoolCreateInfo createInfo = { + VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, 0, + flags, + queueFamily + }; + VkCommandPool pool; + dfunc->vkCreateCommandPool (dev, &createInfo, 0, &pool); + return pool; +} + +int +QFV_AllocateCommandBuffers (qfv_device_t *device, VkCommandPool pool, + int secondary, qfv_cmdbufferset_t *bufferset) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + uint32_t level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + if (secondary) { + level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; + } + VkCommandBufferAllocateInfo allocInfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, 0, + pool, level, bufferset->size + }; + int ret = dfunc->vkAllocateCommandBuffers (dev, &allocInfo, bufferset->a); + return ret == VK_SUCCESS; +} + +VkSemaphore +QFV_CreateSemaphore (qfv_device_t *device) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + + VkSemaphoreCreateInfo createInfo = { + VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, 0, + 0 + }; + + VkSemaphore semaphore; + dfunc->vkCreateSemaphore (dev, &createInfo, 0, &semaphore); + return semaphore; +} + +VkFence +QFV_CreateFence (qfv_device_t *device, int signaled) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + + VkFenceCreateInfo createInfo = { + VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, 0, + signaled ? VK_FENCE_CREATE_SIGNALED_BIT : 0, + }; + + VkFence fence; + dfunc->vkCreateFence (dev, &createInfo, 0, &fence); + return fence; +} + +int +QFV_QueueSubmit (qfv_queue_t *queue, qfv_semaphoreset_t *waitSemaphores, + VkPipelineStageFlags *stages, + qfv_cmdbufferset_t *buffers, + qfv_semaphoreset_t *signalSemaphores, VkFence fence) +{ + qfv_device_t *device = queue->device; + qfv_devfuncs_t *dfunc = device->funcs; + VkSubmitInfo submitInfo = { + VK_STRUCTURE_TYPE_SUBMIT_INFO, 0, + waitSemaphores->size, waitSemaphores->a, stages, + buffers->size, buffers->a, + signalSemaphores->size, signalSemaphores->a + }; + //FIXME multi-batch + return dfunc->vkQueueSubmit (queue->queue, 1, &submitInfo, fence) + == VK_SUCCESS; +} + +int +QFV_QueueWaitIdle (qfv_queue_t *queue) +{ + qfv_device_t *device = queue->device; + qfv_devfuncs_t *dfunc = device->funcs; + return dfunc->vkQueueWaitIdle (queue->queue) == VK_SUCCESS; +} + +void +QFV_PushConstants (qfv_device_t *device, VkCommandBuffer cmd, + VkPipelineLayout layout, uint32_t numPC, + const qfv_push_constants_t *constants) +{ + qfv_devfuncs_t *dfunc = device->funcs; + + for (uint32_t i = 0; i < numPC; i++) { + dfunc->vkCmdPushConstants (cmd, layout, constants[i].stageFlags, + constants[i].offset, constants[i].size, + constants[i].data); + } +} diff --git a/libs/video/renderer/vulkan/debug.c b/libs/video/renderer/vulkan/debug.c new file mode 100644 index 000000000..e8acfed71 --- /dev/null +++ b/libs/video/renderer/vulkan/debug.c @@ -0,0 +1,71 @@ +/* + debug.c + + Vulkan debug support + + Copyright (C) 2022 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/device.h" + +void +QFV_CmdBeginLabel (qfv_device_t *device, VkCommandBuffer cmd, + const char *name, vec4f_t color) +{ + qfv_devfuncs_t *dfunc = device->funcs; + if (dfunc->vkCmdBeginDebugUtilsLabelEXT) { + VkDebugUtilsLabelEXT label = { + VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, 0, + .pLabelName = name, + .color = { color[0], color[1], color[2], color[3] }, + }; + dfunc->vkCmdBeginDebugUtilsLabelEXT (cmd, &label); + } +} + +void +QFV_CmdEndLabel (qfv_device_t *device, VkCommandBuffer cmd) +{ + qfv_devfuncs_t *dfunc = device->funcs; + if (dfunc->vkCmdEndDebugUtilsLabelEXT) { + dfunc->vkCmdEndDebugUtilsLabelEXT (cmd); + } +} + +void +QFV_CmdInsertLabel (qfv_device_t *device, VkCommandBuffer cmd, + const char *name, vec4f_t color) +{ + qfv_devfuncs_t *dfunc = device->funcs; + if (dfunc->vkCmdBeginDebugUtilsLabelEXT) { + VkDebugUtilsLabelEXT label = { + VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, 0, + .pLabelName = name, + .color = { color[0], color[1], color[2], color[3] }, + }; + dfunc->vkCmdInsertDebugUtilsLabelEXT (cmd, &label); + } +} diff --git a/libs/video/renderer/vulkan/descriptor.c b/libs/video/renderer/vulkan/descriptor.c new file mode 100644 index 000000000..ca7791ea8 --- /dev/null +++ b/libs/video/renderer/vulkan/descriptor.c @@ -0,0 +1,170 @@ +/* + descriptor.c + + Vulkan descriptor functions + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/hash.h" + +#include "QF/Vulkan/descriptor.h" +#include "QF/Vulkan/device.h" + +VkSampler +QFV_CreateSampler (qfv_device_t *device, + VkFilter magFilter, VkFilter minFilter, + VkSamplerMipmapMode mipmapMode, + VkSamplerAddressMode addressModeU, + VkSamplerAddressMode addressModeV, + VkSamplerAddressMode addressModeW, + float mipLodBias, + VkBool32 anisotryEnable, float maxAnisotropy, + VkBool32 compareEnable, VkCompareOp compareOp, + float minLod, float maxLod, + VkBorderColor borderColor, + VkBool32 unnormalizedCoordinates) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + + VkSamplerCreateInfo createInfo = { + VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, 0, + 0, + magFilter, minFilter, mipmapMode, + addressModeU, addressModeV, addressModeW, + mipLodBias, + anisotryEnable, maxAnisotropy, + compareEnable, compareOp, + minLod, maxLod, + borderColor, unnormalizedCoordinates, + }; + + VkSampler sampler; + dfunc->vkCreateSampler (dev, &createInfo, 0, &sampler); + return sampler; +} + +VkDescriptorSetLayout +QFV_CreateDescriptorSetLayout (qfv_device_t *device, + qfv_bindingset_t *bindingset) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + + VkDescriptorSetLayoutCreateInfo createInfo = { + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, 0, + 0, + bindingset->size, bindingset->a, + }; + + VkDescriptorSetLayout layout; + dfunc->vkCreateDescriptorSetLayout (dev, &createInfo, 0, &layout); + return layout; +} + +// There are currently only 13 descriptor types, so 16 should be plenty +static VkDescriptorPoolSize poolsize_pool[16]; +static VkDescriptorPoolSize *poolsize_next; + +static uintptr_t +poolsize_gethash (const void *ele, void *unused) +{ + const VkDescriptorPoolSize *poolsize = ele; + return poolsize->type; +} + +static int +poolsize_compmare (const void *ele1, const void *ele2, void *unused) +{ + const VkDescriptorPoolSize *poolsize1 = ele1; + const VkDescriptorPoolSize *poolsize2 = ele2; + return poolsize1->type == poolsize2->type; +} + +//FIXME not thread-safe +VkDescriptorPool +QFV_CreateDescriptorPool (qfv_device_t *device, + VkDescriptorPoolCreateFlags flags, uint32_t maxSets, + qfv_bindingset_t *bindings) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + + static hashtab_t *poolsizes; + + if (!poolsizes) { + poolsizes = Hash_NewTable (16, 0, 0, 0, 0);//FIXME threads + Hash_SetHashCompare (poolsizes, poolsize_gethash, poolsize_compmare); + } else { + Hash_FlushTable (poolsizes); + } + poolsize_next = poolsize_pool; + + VkDescriptorPoolSize *ps; + for (size_t i = 0; i < bindings->size; i++) { + VkDescriptorPoolSize test = { bindings->a[i].descriptorType, 0 }; + ps = Hash_FindElement (poolsizes, &test); + if (!ps) { + ps = poolsize_next++; + if ((size_t) (poolsize_next - poolsize_pool) + > sizeof (poolsize_pool) / sizeof (poolsize_pool[0])) { + Sys_Error ("Too many descriptor types"); + } + Hash_AddElement (poolsizes, ps); + } + //XXX is descriptorCount correct? + //FIXME assumes only one layout is used with this pool + ps->descriptorCount += bindings->a[i].descriptorCount * maxSets; + } + + VkDescriptorPoolCreateInfo createInfo = { + VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, 0, + flags, maxSets, poolsize_next - poolsize_pool, poolsize_pool, + }; + + VkDescriptorPool pool; + dfunc->vkCreateDescriptorPool (dev, &createInfo, 0, &pool); + return pool; +} + +qfv_descriptorsets_t * +QFV_AllocateDescriptorSet (qfv_device_t *device, + VkDescriptorPool pool, + qfv_descriptorsetlayoutset_t *layouts) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + + VkDescriptorSetAllocateInfo allocateInfo = { + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, 0, + pool, layouts->size, layouts->a, + }; + + __auto_type descriptorsets + = QFV_AllocDescriptorSets (layouts->size, malloc); + dfunc->vkAllocateDescriptorSets (dev, &allocateInfo, descriptorsets->a); + return descriptorsets; +} diff --git a/libs/video/renderer/vulkan/device.c b/libs/video/renderer/vulkan/device.c new file mode 100644 index 000000000..5244a54fe --- /dev/null +++ b/libs/video/renderer/vulkan/device.c @@ -0,0 +1,226 @@ +/* + vid_common_vulkan.c + + Common Vulkan video driver functions + + Copyright (C) 2019 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "QF/sys.h" + +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/instance.h" + +#include "vid_vulkan.h" + +#include "util.h" + +static int __attribute__((const)) +count_bits (uint32_t val) +{ + int bits = 0; + while (val) { + bits += val & 1; + val >>= 1; + } + return bits; +} + +static int +find_queue_family (qfv_instance_t *instance, VkPhysicalDevice dev, + uint32_t flags) +{ + qfv_instfuncs_t *funcs = instance->funcs; + uint32_t numFamilies; + VkQueueFamilyProperties *queueFamilies; + int best_diff = 32; + uint32_t family = -1; + + funcs->vkGetPhysicalDeviceQueueFamilyProperties (dev, &numFamilies, 0); + queueFamilies = alloca (numFamilies * sizeof (*queueFamilies)); + funcs->vkGetPhysicalDeviceQueueFamilyProperties (dev, &numFamilies, + queueFamilies); + + for (uint32_t i = 0; i < numFamilies; i++) { + VkQueueFamilyProperties *queue = &queueFamilies[i]; + + if ((queue->queueFlags & flags) == flags) { + int diff = count_bits (queue->queueFlags & ~flags); + if (diff < best_diff) { + best_diff = diff; + family = i; + } + } + } + return family; +} + +static void +load_device_funcs (qfv_instance_t *inst, qfv_device_t *dev) +{ + qfv_instfuncs_t *ifunc = inst->funcs; + qfv_devfuncs_t *dfunc = dev->funcs; + VkDevice device = dev->dev; +#define DEVICE_LEVEL_VULKAN_FUNCTION(name) \ + dfunc->name = (PFN_##name) ifunc->vkGetDeviceProcAddr (device, #name); \ + if (!dfunc->name) { \ + Sys_Error ("Couldn't find device level function %s", #name); \ + } + +#define DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION(name, ext) \ + if (!ext || dev->extension_enabled (dev, ext)) { \ + dfunc->name = (PFN_##name) ifunc->vkGetDeviceProcAddr (device, #name); \ + if (!dfunc->name) { \ + Sys_MaskPrintf (SYS_vulkan_parse, \ + "Couldn't find device level function %s", #name); \ + } else { \ + Sys_MaskPrintf (SYS_vulkan_parse, \ + "Found device level function %s\n", #name); \ + } \ + } + +#include "QF/Vulkan/funclist.h" +} + +static int +device_extension_enabled (qfv_device_t *device, const char *ext) +{ + return strset_contains (device->enabled_extensions, ext); +} + +qfv_device_t * +QFV_CreateDevice (vulkan_ctx_t *ctx, const char **extensions) +{ + uint32_t nlay = 1; // ensure alloca doesn't see 0 and terminated + uint32_t next = count_strings (extensions) + 1; // ensure terminated + const char **lay = alloca (nlay * sizeof (const char *)); + const char **ext = alloca (next * sizeof (const char *)); + // ensure there are null pointers so merge_strings can act as append + // since it does not add a null, but also make sure the counts reflect + // actual numbers + memset (lay, 0, nlay-- * sizeof (const char *)); + memset (ext, 0, next-- * sizeof (const char *)); + merge_strings (ext, extensions, 0); + + qfv_instance_t *inst = ctx->instance; + qfv_instfuncs_t *ifunc = inst->funcs; + + for (uint32_t i = 0; i < inst->numDevices; i++) { + VkPhysicalDevice physdev = inst->devices[i].dev; + /* + if (!Vulkan_ExtensionsSupported (phys->extensions, phys->numExtensions, + ext)) { + continue; + } + */ + int family = find_queue_family (inst, physdev, VK_QUEUE_GRAPHICS_BIT); + if (family < 0) { + continue; + } + if (!ctx->get_presentation_support (ctx, physdev, family)) { + continue; + } + float priority = 1; + VkDeviceQueueCreateInfo qCreateInfo = { + VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, 0, 0, + family, 1, &priority + }; + VkPhysicalDeviceMultiviewFeatures multiview_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES, + .multiview = 1, + .multiviewGeometryShader = 1, + }; + VkPhysicalDeviceFeatures features = { + .geometryShader = 1, + .multiViewport = 1, + .fragmentStoresAndAtomics = 1, + }; + VkDeviceCreateInfo dCreateInfo = { + VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, &multiview_features, 0, + 1, &qCreateInfo, + nlay, lay, + next, ext, + &features + }; + qfv_device_t *device = calloc (1, sizeof (qfv_device_t) + + sizeof (qfv_devfuncs_t)); + device->funcs = (qfv_devfuncs_t *) (device + 1); + if (ifunc->vkCreateDevice (physdev, &dCreateInfo, 0, + &device->dev) == VK_SUCCESS) { + qfv_devfuncs_t *dfunc = device->funcs; + device->enabled_extensions = new_strset (ext); + device->extension_enabled = device_extension_enabled; + + device->physDev = &inst->devices[i]; + load_device_funcs (inst, device); + device->queue.device = device; + device->queue.queueFamily = family; + dfunc->vkGetDeviceQueue (device->dev, family, 0, + &device->queue.queue); + ctx->device = device; + return device; + } + free (device); + } + return 0; +} + +void +QFV_DestroyDevice (qfv_device_t *device) +{ + device->funcs->vkDestroyDevice (device->dev, 0); + del_strset (device->enabled_extensions); + free (device); +} + +int +QFV_DeviceWaitIdle (qfv_device_t *device) +{ + qfv_devfuncs_t *dfunc = device->funcs; + return dfunc->vkDeviceWaitIdle (device->dev) == VK_SUCCESS; +} + +VkFormat +QFV_FindSupportedFormat (qfv_device_t *device, + VkImageTiling tiling, VkFormatFeatureFlags features, + int numCandidates, const VkFormat *candidates) +{ + VkPhysicalDevice pdev = device->physDev->dev; + qfv_instfuncs_t *ifuncs = device->physDev->instance->funcs; + for (int i = 0; i < numCandidates; i++) { + VkFormat format = candidates[i]; + VkFormatProperties props; + ifuncs->vkGetPhysicalDeviceFormatProperties (pdev, format, &props); + if ((tiling == VK_IMAGE_TILING_LINEAR + && (props.linearTilingFeatures & features) == features) + || (tiling == VK_IMAGE_TILING_OPTIMAL + && (props.optimalTilingFeatures & features) == features)) { + return format; + } + } + Sys_Error ("no supported format"); +} diff --git a/libs/video/renderer/vulkan/dsmanager.c b/libs/video/renderer/vulkan/dsmanager.c new file mode 100644 index 000000000..b45485097 --- /dev/null +++ b/libs/video/renderer/vulkan/dsmanager.c @@ -0,0 +1,166 @@ +/* + dsmanager.c + + Vulkan descriptor set manager + + Copyright (C) 2023 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "QF/hash.h" +#include "QF/va.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/dsmanager.h" +#include "QF/Vulkan/render.h" +#include "QF/Vulkan/resource.h" + +#include "vid_vulkan.h" + +qfv_dsmanager_t * +QFV_DSManager_Create (const qfv_descriptorsetlayoutinfo_t *setLayoutInfo, + uint32_t maxSets, vulkan_ctx_t *ctx) +{ + VkDescriptorPoolCreateFlags poolFlags = setLayoutInfo->flags + & VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT; + size_t size = sizeof (qfv_dsmanager_t); + size += sizeof (VkDescriptorPoolSize[setLayoutInfo->num_bindings]); + qfv_dsmanager_t *setManager = malloc (size); + auto poolSizes = (VkDescriptorPoolSize *) &setManager[1]; + *setManager = (qfv_dsmanager_t) { + .name = setLayoutInfo->name, + .device = ctx->device, + .poolCreateInfo = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .flags = poolFlags, + .maxSets = maxSets, + .poolSizeCount = setLayoutInfo->num_bindings, + .pPoolSizes = poolSizes, + }, + .freePools = DARRAY_STATIC_INIT (4), + .usedPools = DARRAY_STATIC_INIT (4), + .freeSets = DARRAY_STATIC_INIT (4), + }; + for (uint32_t i = 0; i < setLayoutInfo->num_bindings; i++) { + auto binding = setLayoutInfo->bindings[i]; + poolSizes[i] = (VkDescriptorPoolSize) { + .type = binding.descriptorType, + .descriptorCount = maxSets * binding.descriptorCount, + }; + }; + + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = setManager->device->funcs; + + VkDescriptorSetLayoutCreateInfo cInfo = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .flags = setLayoutInfo->flags, + .bindingCount = setLayoutInfo->num_bindings, + .pBindings = setLayoutInfo->bindings, + }; + dfunc->vkCreateDescriptorSetLayout (device->dev, &cInfo, 0, + &setManager->layout); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, + setManager->layout, + va (ctx->va_ctx, "descriptorSetLayout:%s", + setLayoutInfo->name)); + + return setManager; +} + +void +QFV_DSManager_Destroy (qfv_dsmanager_t *setManager) +{ + if (!setManager) { + return; + } + VkDevice dev = setManager->device->dev; + qfv_devfuncs_t *dfunc = setManager->device->funcs; + + for (size_t i = 0; i < setManager->freePools.size; i++) { + dfunc->vkDestroyDescriptorPool (dev, setManager->freePools.a[i], 0); + } + for (size_t i = 0; i < setManager->usedPools.size; i++) { + dfunc->vkDestroyDescriptorPool (dev, setManager->usedPools.a[i], 0); + } + if (setManager->activePool) { + dfunc->vkDestroyDescriptorPool (dev, setManager->activePool, 0); + } + DARRAY_CLEAR (&setManager->freePools); + DARRAY_CLEAR (&setManager->usedPools); + DARRAY_CLEAR (&setManager->freeSets); + + dfunc->vkDestroyDescriptorSetLayout (dev, setManager->layout, 0); + free (setManager); +} + +VkDescriptorSet +QFV_DSManager_AllocSet (qfv_dsmanager_t *setManager) +{ + if (setManager->freeSets.size) { + uint32_t ind = --setManager->freeSets.size; + return setManager->freeSets.a[ind]; + } + VkDevice dev = setManager->device->dev; + qfv_devfuncs_t *dfunc = setManager->device->funcs; + VkResult res; +retry: + if (setManager->activePool) { + VkDescriptorSet set; + VkDescriptorSetAllocateInfo aInfo = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = setManager->activePool, + .descriptorSetCount = 1, + .pSetLayouts = &setManager->layout, + }; + res = dfunc->vkAllocateDescriptorSets (dev, &aInfo, &set); + if (res == VK_SUCCESS) { + return set; + } + if (res != VK_ERROR_OUT_OF_POOL_MEMORY) { + Sys_Error ("failed to allocate descriptor set: %d", res); + } + DARRAY_APPEND (&setManager->usedPools, setManager->activePool); + } + if (setManager->freePools.size) { + uint32_t ind = --setManager->freePools.size; + setManager->activePool = setManager->freePools.a[ind]; + goto retry; + } + + res = dfunc->vkCreateDescriptorPool (dev, &setManager->poolCreateInfo, 0, + &setManager->activePool); + if (res != VK_SUCCESS) { + Sys_Error ("failed to create descriptor set pool: %d", res); + } + goto retry; +} + +void +QFV_DSManager_FreeSet (qfv_dsmanager_t *setManager, VkDescriptorSet set) +{ + DARRAY_APPEND (&setManager->freeSets, set); +} diff --git a/libs/video/renderer/vulkan/image.c b/libs/video/renderer/vulkan/image.c new file mode 100644 index 000000000..9d9bfbb21 --- /dev/null +++ b/libs/video/renderer/vulkan/image.c @@ -0,0 +1,287 @@ +/* + image.c + + Vulkan image functions + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/mathlib.h" + +#include "QF/Vulkan/barrier.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/image.h" +#include "QF/Vulkan/instance.h" + +VkImage +QFV_CreateImage (qfv_device_t *device, int cubemap, + VkImageType type, + VkFormat format, + VkExtent3D size, + uint32_t num_mipmaps, + uint32_t num_layers, + VkSampleCountFlags samples, + VkImageUsageFlags usage_scenarios) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + VkImageCreateInfo createInfo = { + VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, 0, + cubemap ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0, + type, format, size, num_mipmaps, + cubemap ? 6 * num_layers : num_layers, + samples, + VK_IMAGE_TILING_OPTIMAL, + usage_scenarios, + VK_SHARING_MODE_EXCLUSIVE, + 0, 0, + VK_IMAGE_LAYOUT_UNDEFINED, + }; + VkImage image; + dfunc->vkCreateImage (dev, &createInfo, 0, &image); + return image; +} + +VkDeviceMemory +QFV_AllocImageMemory (qfv_device_t *device, + VkImage image, VkMemoryPropertyFlags properties, + VkDeviceSize size, VkDeviceSize offset) +{ + VkDevice dev = device->dev; + qfv_physdev_t *physdev = device->physDev; + VkPhysicalDeviceMemoryProperties *memprops = &physdev->memory_properties; + qfv_devfuncs_t *dfunc = device->funcs; + + VkMemoryRequirements requirements; + dfunc->vkGetImageMemoryRequirements (dev, image, &requirements); + + size = max (size, offset + requirements.size); + VkDeviceMemory object = 0; + + for (uint32_t type = 0; type < memprops->memoryTypeCount; type++) { + if ((requirements.memoryTypeBits & (1 << type)) + && ((memprops->memoryTypes[type].propertyFlags & properties) + == properties)) { + VkMemoryAllocateInfo allocate_info = { + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, 0, + size, type + }; + + VkResult res = dfunc->vkAllocateMemory (dev, &allocate_info, + 0, &object); + if (res == VK_SUCCESS) { + break; + } + } + } + + return object; +} + +int +QFV_BindImageMemory (qfv_device_t *device, + VkImage image, VkDeviceMemory object, VkDeviceSize offset) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + VkResult res = dfunc->vkBindImageMemory (dev, image, object, offset); + return res == VK_SUCCESS; +} + +qfv_imagebarrierset_t * +QFV_CreateImageTransitionSet (qfv_imagetransition_t *transitions, + int numTransitions) +{ + qfv_imagebarrierset_t *barrierset; + barrierset = DARRAY_ALLOCFIXED (*barrierset, numTransitions, malloc); + + for (int i = 0; i < numTransitions; i++) { + barrierset->a[i].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrierset->a[i].pNext = 0; + barrierset->a[i].srcAccessMask = transitions[i].srcAccess; + barrierset->a[i].dstAccessMask = transitions[i].dstAccess; + barrierset->a[i].oldLayout = transitions[i].oldLayout; + barrierset->a[i].newLayout = transitions[i].newLayout; + barrierset->a[i].srcQueueFamilyIndex = transitions[i].srcQueueFamily; + barrierset->a[i].dstQueueFamilyIndex = transitions[i].dstQueueFamily; + barrierset->a[i].image = transitions[i].image; + barrierset->a[i].subresourceRange.aspectMask = transitions[i].aspect; + barrierset->a[i].subresourceRange.baseMipLevel = 0; + barrierset->a[i].subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + barrierset->a[i].subresourceRange.baseArrayLayer = 0; + barrierset->a[i].subresourceRange.layerCount + = VK_REMAINING_ARRAY_LAYERS; + } + return barrierset; +} + +VkImageView +QFV_CreateImageView (qfv_device_t *device, VkImage image, + VkImageViewType type, VkFormat format, + VkImageAspectFlags aspect) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + + VkImageViewCreateInfo createInfo = { + VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 0, + 0, + image, type, format, + { + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + }, + { + aspect, + 0, VK_REMAINING_MIP_LEVELS, + 0, VK_REMAINING_ARRAY_LAYERS, + } + }; + + VkImageView view; + dfunc->vkCreateImageView (dev, &createInfo, 0, &view); + return view; +} + +size_t +QFV_GetImageSize (qfv_device_t *device, VkImage image) +{ + qfv_devfuncs_t *dfunc = device->funcs; + size_t size; + size_t align; + + VkMemoryRequirements requirements; + dfunc->vkGetImageMemoryRequirements (device->dev, image, &requirements); + size = requirements.size; + align = requirements.alignment - 1; + size = (size + align) & ~(align); + return size; +} + +void +QFV_GenerateMipMaps (qfv_device_t *device, VkCommandBuffer cmd, + VkImage image, unsigned mips, + unsigned width, unsigned height, unsigned layers) +{ + qfv_devfuncs_t *dfunc = device->funcs; + + qfv_imagebarrier_t pre=imageBarriers[qfv_LT_TransferDst_to_TransferSrc]; + qfv_imagebarrier_t pst=imageBarriers[qfv_LT_TransferSrc_to_ShaderReadOnly]; + qfv_imagebarrier_t fnl=imageBarriers[qfv_LT_TransferDst_to_ShaderReadOnly]; + + pre.barrier.image = image; + pre.barrier.subresourceRange.layerCount = layers; + pst.barrier.image = image; + pst.barrier.subresourceRange.layerCount = layers; + fnl.barrier.image = image; + fnl.barrier.subresourceRange.layerCount = layers; + + VkImageBlit blit = { + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, layers}, + {{0, 0, 0}, {width, height, 1}}, + {VK_IMAGE_ASPECT_COLOR_BIT, 1, 0, layers}, + {{0, 0, 0}, {max (width >> 1, 1), max (height >> 1, 1), 1}}, + }; + + while (--mips > 0) { + dfunc->vkCmdPipelineBarrier (cmd, pre.srcStages, pre.dstStages, 0, + 0, 0, 0, 0, + 1, &pre.barrier); + dfunc->vkCmdBlitImage (cmd, + image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, &blit, VK_FILTER_LINEAR); + + dfunc->vkCmdPipelineBarrier (cmd, pst.srcStages, pst.dstStages, 0, + 0, 0, 0, 0, + 1, &pst.barrier); + + blit.srcSubresource.mipLevel++; + blit.srcOffsets[1].x = blit.dstOffsets[1].x; + blit.srcOffsets[1].y = blit.dstOffsets[1].y; + blit.dstSubresource.mipLevel++; + blit.dstOffsets[1].x = max (blit.dstOffsets[1].x >> 1, 1); + blit.dstOffsets[1].y = max (blit.dstOffsets[1].y >> 1, 1); + pre.barrier.subresourceRange.baseMipLevel++; + pst.barrier.subresourceRange.baseMipLevel++; + fnl.barrier.subresourceRange.baseMipLevel++; + } + dfunc->vkCmdPipelineBarrier (cmd, fnl.srcStages, fnl.dstStages, 0, + 0, 0, 0, 0, + 1, &fnl.barrier); +} + +static int +ilog2 (unsigned x) +{ + unsigned o = x; + if (x > 0x7fffffff) { + // avoid overflow + return 31; + } + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + x++; + int y = 0; + y |= ((x & 0xffff0000) != 0) << 4; + y |= ((x & 0xff00ff00) != 0) << 3; + y |= ((x & 0xf0f0f0f0) != 0) << 2; + y |= ((x & 0xcccccccc) != 0) << 1; + y |= ((x & 0xaaaaaaaa) != 0) << 0; + return y - ((o & (x - 1)) != 0); +} + +int +QFV_MipLevels (int width, int height) +{ + return ilog2 (max (width, height)) + 1; +} + +VkFormat +QFV_ImageFormat (QFFormat format, int srgb) +{ + switch (format) { + case tex_palette: + return VK_FORMAT_R8_UINT; + case tex_l: + case tex_a: + return srgb ? VK_FORMAT_R8_SRGB : VK_FORMAT_R8_UNORM; + case tex_la: + return srgb ? VK_FORMAT_R8G8_SRGB : VK_FORMAT_R8G8_UNORM; + case tex_rgb: + return srgb ? VK_FORMAT_R8G8B8_SRGB : VK_FORMAT_R8G8B8_UNORM; + case tex_rgba: + return srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM; + case tex_frgba: + return VK_FORMAT_R32G32B32A32_SFLOAT; + } + return VK_FORMAT_R8_SRGB; +} diff --git a/libs/video/renderer/vulkan/instance.c b/libs/video/renderer/vulkan/instance.c new file mode 100644 index 000000000..562063e89 --- /dev/null +++ b/libs/video/renderer/vulkan/instance.c @@ -0,0 +1,324 @@ +/* + init.c + + Copyright (C) 2019 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "QF/cvar.h" +#include "QF/mathlib.h" + +#include "QF/Vulkan/instance.h" + +#include "vid_vulkan.h" + +#include "util.h" + +int vulkan_use_validation; + +static uint32_t numLayers; +static VkLayerProperties *instanceLayerProperties; +static strset_t *instanceLayers; + +static uint32_t numExtensions; +static VkExtensionProperties *instanceExtensionProperties; +static strset_t *instanceExtensions; + +const char * const vulkanValidationLayers[] = { + "VK_LAYER_KHRONOS_validation", + 0, +}; + +static const char * const debugExtensions[] = { + VK_EXT_DEBUG_UTILS_EXTENSION_NAME, + 0, +}; + +static void +get_instance_layers_and_extensions (vulkan_ctx_t *ctx) +{ + uint32_t i; + VkLayerProperties *layers; + VkExtensionProperties *extensions; + + ctx->vkEnumerateInstanceLayerProperties (&numLayers, 0); + layers = malloc (numLayers * sizeof (VkLayerProperties)); + ctx->vkEnumerateInstanceLayerProperties (&numLayers, layers); + instanceLayers = new_strset (0); + for (i = 0; i < numLayers; i++) { + strset_add (instanceLayers, layers[i].layerName); + } + + ctx->vkEnumerateInstanceExtensionProperties (0, &numExtensions, 0); + extensions = malloc (numExtensions * sizeof (VkLayerProperties)); + ctx->vkEnumerateInstanceExtensionProperties (0, &numExtensions, + extensions); + instanceExtensions = new_strset (0); + for (i = 0; i < numExtensions; i++) { + strset_add (instanceExtensions, extensions[i].extensionName); + } + + if (developer & SYS_vulkan) { + for (i = 0; i < numLayers; i++) { + Sys_Printf ("%s %x %u %s\n", + layers[i].layerName, + layers[i].specVersion, + layers[i].implementationVersion, + layers[i].description); + } + for (i = 0; i < numExtensions; i++) { + Sys_Printf ("%d %s\n", + extensions[i].specVersion, + extensions[i].extensionName); + } + } + instanceLayerProperties = layers; + instanceExtensionProperties = extensions; +} + +static int +instance_extension_enabled (qfv_instance_t *inst, const char *ext) +{ + return strset_contains (inst->enabled_extensions, ext); +} + +static int message_severities = + VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; +static int message_types = + VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + +static void +debug_breakpoint (VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity) +{ + if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { + exit(1); + } +} + +static VKAPI_ATTR VkBool32 VKAPI_CALL +debug_callback (VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* callbackData, + void *data) +{ + qfv_instance_t *instance = data; + if (!(messageSeverity & vulkan_use_validation)) { + return 0; + } + const char *msgSev = ""; + if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) { + msgSev = "verbose: "; + } + if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) { + msgSev = "info: "; + } + if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { + msgSev = "warning: "; + } + if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { + msgSev = "error: "; + } + fprintf (stderr, "validation layer: %s%s\n", msgSev, + callbackData->pMessage); + for (size_t i = instance->debug_stack.size; i-- > 0; ) { + fprintf (stderr, " %s\n", instance->debug_stack.a[i]); + } + debug_breakpoint (messageSeverity); + return VK_FALSE; +} + +static void +setup_debug_callback (qfv_instance_t *instance) +{ + VkDebugUtilsMessengerEXT debug_handle; + VkDebugUtilsMessengerCreateInfoEXT createInfo = { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + .messageSeverity = message_severities, + .messageType = message_types, + .pfnUserCallback = debug_callback, + .pUserData = instance, + }; + if (!instance->funcs->vkCreateDebugUtilsMessengerEXT) { + Sys_Printf ("Cound not set up Vulkan validation debug callback\n"); + return; + } + instance->funcs->vkCreateDebugUtilsMessengerEXT(instance->instance, + &createInfo, 0, + &debug_handle); + instance->debug_handle = debug_handle; +} + +static void +load_instance_funcs (vulkan_ctx_t *ctx) +{ + qfv_instance_t *instance = ctx->instance; + qfv_instfuncs_t *funcs = instance->funcs; + VkInstance inst = instance->instance; +#define INSTANCE_LEVEL_VULKAN_FUNCTION(name) \ + funcs->name = (PFN_##name) ctx->vkGetInstanceProcAddr (inst, #name); \ + if (!funcs->name) { \ + Sys_Error ("Couldn't find instance level function %s", #name); \ + } + +#define INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION(name, ext) \ + if (instance->extension_enabled (instance, ext)) { \ + funcs->name = (PFN_##name) ctx->vkGetInstanceProcAddr (inst, #name); \ + if (!funcs->name) { \ + Sys_Error ("Couldn't find instance level function %s", #name); \ + } \ + } + +#include "QF/Vulkan/funclist.h" +} + +qfv_instance_t * +QFV_CreateInstance (vulkan_ctx_t *ctx, + const char *appName, uint32_t appVersion, + const char **layers, const char **extensions) +{ + VkApplicationInfo appInfo = { + VK_STRUCTURE_TYPE_APPLICATION_INFO, 0, + appName, appVersion, + PACKAGE_STRING, 0x000702ff, //FIXME version + VK_API_VERSION_1_1, + }; + VkInstanceCreateInfo createInfo = { + VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, 0, 0, + &appInfo, + 0, 0, + 0, 0, + }; + VkResult res; + VkInstance instance; + + if (!instanceLayerProperties) { + get_instance_layers_and_extensions (ctx); + } + + uint32_t nlay = count_strings (layers) + 1; + uint32_t next = count_strings (extensions) + + count_strings (ctx->required_extensions) + 1; + if (vulkan_use_validation) { + nlay += count_strings (vulkanValidationLayers); + next += count_strings (debugExtensions); + } + const char **lay = alloca (nlay * sizeof (const char *)); + const char **ext = alloca (next * sizeof (const char *)); + // ensure there are null pointers so merge_strings can act as append + // since it does not add a null + memset (lay, 0, nlay-- * sizeof (const char *)); + memset (ext, 0, next-- * sizeof (const char *)); + merge_strings (lay, layers, 0); + merge_strings (ext, extensions, ctx->required_extensions); + if (vulkan_use_validation) { + merge_strings (lay, lay, vulkanValidationLayers); + merge_strings (ext, ext, debugExtensions); + } + prune_strings (instanceLayers, lay, &nlay); + prune_strings (instanceExtensions, ext, &next); + lay[nlay] = 0; + ext[next] = 0; + createInfo.enabledLayerCount = nlay; + createInfo.ppEnabledLayerNames = lay; + createInfo.enabledExtensionCount = next; + createInfo.ppEnabledExtensionNames = ext; + + res = ctx->vkCreateInstance (&createInfo, 0, &instance); + if (res != VK_SUCCESS) { + Sys_Error ("unable to create vulkan instance\n"); + } + qfv_instance_t *inst = calloc (1, sizeof(qfv_instance_t) + + sizeof (qfv_instfuncs_t)); + inst->instance = instance; + inst->funcs = (qfv_instfuncs_t *)(inst + 1); + inst->enabled_extensions = new_strset (ext); + inst->extension_enabled = instance_extension_enabled; + DARRAY_INIT (&inst->debug_stack, 8); + ctx->instance = inst; + load_instance_funcs (ctx); + + if (vulkan_use_validation) { + setup_debug_callback (inst); + } + + qfv_instfuncs_t *ifunc = inst->funcs; + ifunc->vkEnumeratePhysicalDevices (instance, &inst->numDevices, 0); + inst->devices = malloc (inst->numDevices * sizeof (*inst->devices)); + VkPhysicalDevice *devices = alloca (inst->numDevices * sizeof (*devices)); + ifunc->vkEnumeratePhysicalDevices (instance, &inst->numDevices, devices); + for (uint32_t i = 0; i < inst->numDevices; i++) { + VkPhysicalDevice physDev = devices[i]; + qfv_physdev_t *dev = &inst->devices[i]; + dev->instance = inst; + dev->dev = physDev; + dev->properties2 = (VkPhysicalDeviceProperties2) { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, + .pNext = &dev->multiViewProperties, + }; + dev->multiViewProperties = (VkPhysicalDeviceMultiviewProperties) { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES, + }; + ifunc->vkGetPhysicalDeviceProperties2 (physDev, &dev->properties2); + dev->properties = &dev->properties2.properties; + ifunc->vkGetPhysicalDeviceMemoryProperties (physDev, + &dev->memory_properties); + } + return inst; +} + +void +QFV_DestroyInstance (qfv_instance_t *instance) +{ + qfv_instfuncs_t *ifunc = instance->funcs; + + if (instance->debug_handle) { + ifunc->vkDestroyDebugUtilsMessengerEXT (instance->instance, + instance->debug_handle, 0); + } + instance->funcs->vkDestroyInstance (instance->instance, 0); + del_strset (instance->enabled_extensions); + free (instance->devices); + free (instance); +} + +VkSampleCountFlagBits +QFV_GetMaxSampleCount (qfv_physdev_t *physdev) +{ + VkSampleCountFlagBits maxSamples = VK_SAMPLE_COUNT_64_BIT; + VkSampleCountFlagBits counts; + counts = min (physdev->properties->limits.framebufferColorSampleCounts, + physdev->properties->limits.framebufferDepthSampleCounts); + while (maxSamples && maxSamples > counts) { + maxSamples >>= 1; + } + Sys_MaskPrintf (SYS_vulkan, "Max samples: %x (%d)\n", maxSamples, counts); + return maxSamples; +} diff --git a/libs/video/renderer/vulkan/memory.c b/libs/video/renderer/vulkan/memory.c new file mode 100644 index 000000000..12f6d4ab3 --- /dev/null +++ b/libs/video/renderer/vulkan/memory.c @@ -0,0 +1,62 @@ +/* + memory.c + + Vulkan memory functions + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/memory.h" + +void * +QFV_MapMemory (qfv_device_t *device, VkDeviceMemory object, + VkDeviceSize offset, VkDeviceSize size) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + void *map = 0; + + dfunc->vkMapMemory (dev, object, offset, size, 0, &map); + return map; +} + +void +QFV_FlushMemory (qfv_device_t *device, qfv_mappedmemrangeset_t *flushRanges) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + + VkMappedMemoryRange *ranges = alloca(sizeof (*ranges) * flushRanges->size); + + for (uint32_t i = 0; i < flushRanges->size; i++) { + ranges[i].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + ranges[i].pNext = 0; + ranges[i].memory = flushRanges->a[i].object; + ranges[i].offset = flushRanges->a[i].offset; + ranges[i].size = flushRanges->a[i].size; + } + dfunc->vkFlushMappedMemoryRanges (dev, flushRanges->size, ranges); +} diff --git a/libs/video/renderer/vulkan/pipeline.c b/libs/video/renderer/vulkan/pipeline.c new file mode 100644 index 000000000..c83ef4a3b --- /dev/null +++ b/libs/video/renderer/vulkan/pipeline.c @@ -0,0 +1,134 @@ +/* + pipeline.c + + Vulkan pipeline functions + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/dstring.h" + +#include "QF/Vulkan/descriptor.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/pipeline.h" + +VkPipelineCache +QFV_CreatePipelineCache (qfv_device_t *device, dstring_t *cacheData) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + + VkPipelineCacheCreateInfo createInfo = { + VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, 0, 0, + cacheData->size, cacheData->str, + }; + + VkPipelineCache cache; + dfunc->vkCreatePipelineCache (dev, &createInfo, 0, &cache); + return cache; +} + +dstring_t * +QFV_GetPipelineCacheData (qfv_device_t *device, VkPipelineCache cache) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + dstring_t *cacheData = dstring_new (); + + dfunc->vkGetPipelineCacheData (dev, cache, &cacheData->size, 0); + dstring_adjust (cacheData); + dfunc->vkGetPipelineCacheData (dev, cache, + &cacheData->size, cacheData->str); + return cacheData; +} + +void +QFV_MergePipelineCaches (qfv_device_t *device, + VkPipelineCache targetCache, + qfv_pipelinecacheset_t *sourceCaches) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + + dfunc->vkMergePipelineCaches (dev, targetCache, + sourceCaches->size, sourceCaches->a); +} + +VkPipelineLayout +QFV_CreatePipelineLayout (qfv_device_t *device, + qfv_descriptorsetlayoutset_t *layouts, + qfv_pushconstantrangeset_t *pushConstants) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + + VkPipelineLayoutCreateInfo createInfo = { + VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, 0, 0, + layouts->size, layouts->a, + pushConstants->size, pushConstants->a, + }; + + VkPipelineLayout layout; + dfunc->vkCreatePipelineLayout (dev, &createInfo, 0, &layout); + return layout; +} + +qfv_pipelineset_t * +QFV_CreateGraphicsPipelines (qfv_device_t *device, + VkPipelineCache cache, + qfv_graphicspipelinecreateinfoset_t *gpciSet) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + + __auto_type pipelines = QFV_AllocPipelineSet (gpciSet->size, malloc); + dfunc->vkCreateGraphicsPipelines (dev, cache, gpciSet->size, gpciSet->a, 0, + pipelines->a); + + return pipelines; +} + +qfv_pipelineset_t * +QFV_CreateComputePipelines (qfv_device_t *device, + VkPipelineCache cache, + qfv_computepipelinecreateinfoset_t *cpciSet) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + + __auto_type pipelines = QFV_AllocPipelineSet (cpciSet->size, malloc); + dfunc->vkCreateComputePipelines (dev, cache, cpciSet->size, cpciSet->a, 0, + pipelines->a); + return pipelines; +} + +void +QFV_DestroyPipeline (qfv_device_t *device, VkPipeline pipeline) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + + dfunc->vkDestroyPipeline (dev, pipeline, 0); +} diff --git a/libs/video/renderer/vulkan/projection.c b/libs/video/renderer/vulkan/projection.c new file mode 100644 index 000000000..260242f94 --- /dev/null +++ b/libs/video/renderer/vulkan/projection.c @@ -0,0 +1,89 @@ +/* + proejct.c + + Vulkan projection matrices + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "QF/cvar.h" +#include "QF/Vulkan/projection.h" + +#include "r_internal.h" + +void +QFV_Orthographic (mat4f_t proj, float xmin, float xmax, float ymin, float ymax, + float znear, float zfar) +{ + proj[0] = (vec4f_t) { + 2 / (xmax - xmin), + 0, + 0, + 0 + }; + proj[1] = (vec4f_t) { + 0, + 2 / (ymax - ymin), + 0, + 0 + }; + proj[2] = (vec4f_t) { + 0, + 0, + 1 / (znear - zfar), + 0 + }; + proj[3] = (vec4f_t) { + -(xmax + xmin) / (xmax - xmin), + -(ymax + ymin) / (ymax - ymin), + znear / (znear - zfar), + 1, + }; +} + +void +QFV_PerspectiveTan (mat4f_t proj, float fov_x, float fov_y) +{ + float neard, fard; + + neard = r_nearclip; + fard = r_farclip; + + proj[0] = (vec4f_t) { 1 / fov_x, 0, 0, 0 }; + proj[1] = (vec4f_t) { 0, 1 / fov_y, 0, 0 }; + proj[2] = (vec4f_t) { 0, 0, fard / (fard - neard), 1 }; + proj[3] = (vec4f_t) { 0, 0, (neard * fard) / (neard - fard), 0 }; +} + +void +QFV_PerspectiveCos (mat4f_t proj, float fov) +{ + // square first for auto-abs (no support for > 180 degree fov) + fov = fov * fov; + float t = sqrt ((1 - fov) / fov); + QFV_PerspectiveTan (proj, t, t); +} diff --git a/libs/video/renderer/vulkan/render.c b/libs/video/renderer/vulkan/render.c new file mode 100644 index 000000000..9c8b6e95f --- /dev/null +++ b/libs/video/renderer/vulkan/render.c @@ -0,0 +1,639 @@ +/* + render.c + + Vulkan render manager + + Copyright (C) 2023 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_MATH_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/cmem.h" +#include "QF/hash.h" +#include "QF/va.h" +#include "QF/Vulkan/command.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/dsmanager.h" +#include "QF/Vulkan/image.h" +#include "QF/Vulkan/pipeline.h" +#include "QF/Vulkan/render.h" +#include "QF/Vulkan/resource.h" +#include "QF/Vulkan/swapchain.h" +#include "vid_vulkan.h" + +#include "vkparse.h" + +VkCommandBuffer +QFV_GetCmdBuffer (vulkan_ctx_t *ctx, bool secondary) +{ + auto rctx = ctx->render_context; + auto rframe = &rctx->frames.a[ctx->curFrame]; + return QFV_CmdPoolManager_CmdBuffer (&rframe->cmdpool, secondary); +} + +void +QFV_AppendCmdBuffer (vulkan_ctx_t *ctx, VkCommandBuffer cmd) +{ + __auto_type rctx = ctx->render_context; + __auto_type job = rctx->job; + DARRAY_APPEND (&job->commands, cmd); +} + +static void +run_tasks (uint32_t task_count, qfv_taskinfo_t *tasks, qfv_taskctx_t *ctx) +{ + for (uint32_t i = 0; i < task_count; i++) { + tasks[i].func->func (tasks[i].params, 0, (exprctx_t *) ctx); + } +} + +static void +run_pipeline (qfv_pipeline_t *pipeline, qfv_taskctx_t *taskctx) +{ + if (pipeline->disabled) { + return; + } + qfv_device_t *device = taskctx->ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + auto cmd = taskctx->cmd; + dfunc->vkCmdBindPipeline (cmd, pipeline->bindPoint, pipeline->pipeline); + dfunc->vkCmdSetViewport (cmd, 0, 1, &pipeline->viewport); + dfunc->vkCmdSetScissor (cmd, 0, 1, &pipeline->scissor); + + taskctx->pipeline = pipeline; + run_tasks (pipeline->task_count, pipeline->tasks, taskctx); +} + +// https://themaister.net/blog/2019/08/14/yet-another-blog-explaining-vulkan-synchronization/ +static void +run_subpass (qfv_subpass_t *sp, qfv_taskctx_t *taskctx) +{ + qfv_device_t *device = taskctx->ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + dfunc->vkBeginCommandBuffer (taskctx->cmd, &sp->beginInfo); + + for (uint32_t i = 0; i < sp->pipeline_count; i++) { + __auto_type pipeline = &sp->pipelines[i]; + run_pipeline (pipeline, taskctx); + } + + dfunc->vkEndCommandBuffer (taskctx->cmd); +} + +static void +run_renderpass (qfv_renderpass_t *rp, vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + __auto_type rctx = ctx->render_context; + __auto_type job = rctx->job; + + VkCommandBuffer cmd = QFV_GetCmdBuffer (ctx, false); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_COMMAND_BUFFER, cmd, + va (ctx->va_ctx, "cmd:render:%s", rp->label.name)); + VkCommandBufferBeginInfo beginInfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + }; + dfunc->vkBeginCommandBuffer (cmd, &beginInfo); + QFV_duCmdBeginLabel (device, cmd, rp->label.name, + {VEC4_EXP (rp->label.color)}); + dfunc->vkCmdBeginRenderPass (cmd, &rp->beginInfo, rp->subpassContents); + for (uint32_t i = 0; i < rp->subpass_count; i++) { + __auto_type sp = &rp->subpasses[i]; + QFV_duCmdBeginLabel (device, cmd, sp->label.name, + {VEC4_EXP (sp->label.color)}); + qfv_taskctx_t taskctx = { + .ctx = ctx, + .renderpass = rp, + .cmd = QFV_GetCmdBuffer (ctx, true), + }; + run_subpass (sp, &taskctx); + dfunc->vkCmdExecuteCommands (cmd, 1, &taskctx.cmd); + QFV_duCmdEndLabel (device, cmd); + //FIXME comment is a bit off as exactly one buffer is always submitted + // + //Regardless of whether any commands were submitted for this + //subpass, must step through each and every subpass, otherwise + //the attachments won't be transitioned correctly. + //However, only if not the last (or only) subpass. + if (i < rp->subpass_count - 1) { + dfunc->vkCmdNextSubpass (cmd, rp->subpassContents); + } + } + dfunc->vkCmdEndRenderPass (cmd); + QFV_CmdEndLabel (device, cmd); + dfunc->vkEndCommandBuffer (cmd); + DARRAY_APPEND (&job->commands, cmd); +} + +static void +run_compute_pipeline (qfv_pipeline_t *pipeline, VkCommandBuffer cmd, + vulkan_ctx_t *ctx) +{ + if (pipeline->disabled) { + return; + } + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + dfunc->vkCmdBindPipeline (cmd, pipeline->bindPoint, pipeline->pipeline); + + qfv_taskctx_t taskctx = { + .ctx = ctx, + .pipeline = pipeline, + .cmd = cmd, + }; + run_tasks (pipeline->task_count, pipeline->tasks, &taskctx); + + vec4u_t d = pipeline->dispatch; + if (d[0] && d[1] && d[2]) { + dfunc->vkCmdDispatch (cmd, d[0], d[1], d[2]); + } +} + +static void +run_compute (qfv_compute_t *comp, vulkan_ctx_t *ctx, qfv_step_t *step) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + __auto_type rctx = ctx->render_context; + __auto_type job = rctx->job; + + VkCommandBuffer cmd = QFV_GetCmdBuffer (ctx, false); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_COMMAND_BUFFER, cmd, + va (ctx->va_ctx, "cmd:compute:%s", step->label.name)); + QFV_duCmdBeginLabel (device, cmd, step->label.name, + {VEC4_EXP (step->label.color)}); + + VkCommandBufferBeginInfo beginInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + dfunc->vkBeginCommandBuffer (cmd, &beginInfo); + for (uint32_t i = 0; i < comp->pipeline_count; i++) { + __auto_type pipeline = &comp->pipelines[i]; + run_compute_pipeline (pipeline, cmd, ctx); + } + QFV_duCmdEndLabel (device, cmd); + dfunc->vkEndCommandBuffer (cmd); + DARRAY_APPEND (&job->commands, cmd); +} + +static void +run_process (qfv_process_t *proc, vulkan_ctx_t *ctx) +{ + qfv_taskctx_t taskctx = { + .ctx = ctx, + }; + run_tasks (proc->task_count, proc->tasks, &taskctx); +} + +void +QFV_RunRenderJob (vulkan_ctx_t *ctx) +{ + auto rctx = ctx->render_context; + auto job = rctx->job; + + for (uint32_t i = 0; i < job->num_steps; i++) { + __auto_type step = &job->steps[i]; + if (step->render) { + run_renderpass (step->render->active, ctx); + } + if (step->compute) { + run_compute (step->compute, ctx, step); + } + if (step->process) { + run_process (step->process, ctx); + } + } + + auto device = ctx->device; + auto dfunc = device->funcs; + auto queue = &device->queue; + auto frame = &rctx->frames.a[ctx->curFrame]; + VkPipelineStageFlags waitStage + = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkSubmitInfo submitInfo = { + VK_STRUCTURE_TYPE_SUBMIT_INFO, 0, + 1, &frame->imageAvailableSemaphore, &waitStage, + job->commands.size, job->commands.a, + 1, &frame->renderDoneSemaphore, + }; + dfunc->vkResetFences (device->dev, 1, &frame->fence); + dfunc->vkQueueSubmit (queue->queue, 1, &submitInfo, frame->fence); + + VkPresentInfoKHR presentInfo = { + VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, 0, + 1, &frame->renderDoneSemaphore, + 1, &ctx->swapchain->swapchain, &ctx->swapImageIndex, + 0 + }; + dfunc->vkQueuePresentKHR (queue->queue, &presentInfo); + + if (++ctx->curFrame >= rctx->frames.size) { + ctx->curFrame = 0; + } +} + +static VkImageView __attribute__((pure)) +find_imageview (qfv_reference_t *ref, qfv_renderctx_t *rctx) +{ + __auto_type jinfo = rctx->jobinfo; + __auto_type job = rctx->job; + const char *name = ref->name; + + if (strncmp (name, "$imageviews.", 7) == 0) { + name += 7; + } + + for (uint32_t i = 0; i < jinfo->num_imageviews; i++) { + __auto_type vi = &jinfo->imageviews[i]; + __auto_type vo = &job->image_views[i]; + if (strcmp (name, vi->name) == 0) { + return vo->image_view.view; + } + } + Sys_Error ("%d:invalid imageview: %s", ref->line, ref->name); +} + +void +QFV_CreateFramebuffer (vulkan_ctx_t *ctx, qfv_renderpass_t *rp) +{ + __auto_type rctx = ctx->render_context; + + auto fb = rp->framebufferinfo; + auto attachments = rp->framebuffer.views; + VkFramebufferCreateInfo cInfo = { + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .attachmentCount = fb->num_attachments, + .pAttachments = attachments, + .renderPass = rp->beginInfo.renderPass, + .width = fb->width, + .height = fb->height, + .layers = fb->layers, + }; + for (uint32_t i = 0; i < fb->num_attachments; i++) { + if (fb->attachments[i].external) { + attachments[i] = 0; + if (!strcmp (fb->attachments[i].external, "$swapchain")) { + auto sc = ctx->swapchain; + attachments[i] = sc->imageViews->a[ctx->swapImageIndex]; + cInfo.width = sc->extent.width; + cInfo.height = sc->extent.height; + } + } else { + attachments[i] = find_imageview (&fb->attachments[i].view, rctx); + if (rp->outputref.name) { + rp->output = find_imageview (&rp->outputref, rctx); + } + } + } + + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + VkFramebuffer framebuffer; + dfunc->vkCreateFramebuffer (device->dev, &cInfo, 0, &framebuffer); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_FRAMEBUFFER, framebuffer, + va (ctx->va_ctx, "framebuffer:%s", rp->label.name)); + + rp->beginInfo.framebuffer = framebuffer; + for (uint32_t i = 0; i < rp->subpass_count; i++) { + __auto_type sp = &rp->subpasses[i]; + sp->inherit.framebuffer = framebuffer; + } +} + +static void +wait_on_fence (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto dev = device->dev; + auto rctx = ctx->render_context; + auto frame = &rctx->frames.a[ctx->curFrame]; + + dfunc->vkWaitForFences (dev, 1, &frame->fence, VK_TRUE, 2000000000); + + QFV_CmdPoolManager_Reset (&frame->cmdpool); + auto job = ctx->render_context->job; + DARRAY_RESIZE (&job->commands, 0); +} + +static void +renderpass_update_viewper_sissor (qfv_renderpass_t *rp, + const qfv_output_t *output) +{ + rp->beginInfo.renderArea.extent = output->extent; + for (uint32_t i = 0; i < rp->subpass_count; i++) { + auto sp = &rp->subpasses[i]; + for (uint32_t j = 0; j < sp->pipeline_count; j++) { + auto pl = &sp->pipelines[j]; + pl->viewport = (VkViewport) { + .width = output->extent.width, + .height = output->extent.height, + .minDepth = 0, + .maxDepth = 1, + }; + pl->scissor.extent = output->extent; + } + } +} + +static void +update_viewport_scissor (qfv_render_t *render, const qfv_output_t *output) +{ + for (uint32_t i = 0; i < render->num_renderpasses; i++) { + renderpass_update_viewper_sissor (&render->renderpasses[i], output); + } +} + +static void +update_framebuffer (const exprval_t **params, exprval_t *result, + exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + auto job = ctx->render_context->job; + auto step = QFV_GetStep (params[0], job); + auto render = step->render; + auto rp = render->active; + + qfv_output_t output = {}; + Vulkan_ConfigOutput (ctx, &output); + if (output.extent.width != render->output.extent.width + || output.extent.height != render->output.extent.height) { + //FIXME framebuffer image creation here + update_viewport_scissor (render, &output); + } + + if (!rp->beginInfo.framebuffer) { + QFV_CreateFramebuffer (ctx, rp); + } +} + +static exprfunc_t wait_on_fence_func[] = { + { .func = wait_on_fence }, + {} +}; + +static exprtype_t *update_framebuffer_params[] = { + &cexpr_string, +}; +static exprfunc_t update_framebuffer_func[] = { + { .func = update_framebuffer, .num_params = 1, update_framebuffer_params }, + {} +}; +static exprsym_t render_task_syms[] = { + { "wait_on_fence", &cexpr_function, wait_on_fence_func }, + { "update_framebuffer", &cexpr_function, update_framebuffer_func }, + {} +}; + +void +QFV_Render_Init (vulkan_ctx_t *ctx) +{ + qfv_renderctx_t *rctx = calloc (1, sizeof (*rctx)); + ctx->render_context = rctx; + + exprctx_t ectx = { .hashctx = &rctx->hashctx }; + exprsym_t syms[] = { {} }; + rctx->task_functions.symbols = syms; + cexpr_init_symtab (&rctx->task_functions, &ectx); + rctx->task_functions.symbols = 0; + + QFV_Render_AddTasks (ctx, render_task_syms); + + auto device = ctx->device; + size_t frames = vulkan_frame_count; + DARRAY_INIT (&rctx->frames, frames); + DARRAY_RESIZE (&rctx->frames, frames); + for (size_t i = 0; i < rctx->frames.size; i++) { + auto frame = &rctx->frames.a[i]; + frame->fence = QFV_CreateFence (device, 1); + frame->imageAvailableSemaphore = QFV_CreateSemaphore (device); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_SEMAPHORE, + frame->imageAvailableSemaphore, + va (ctx->va_ctx, "sc image:%zd", i)); + frame->renderDoneSemaphore = QFV_CreateSemaphore (device); + QFV_CmdPoolManager_Init (&frame->cmdpool, device, + va (ctx->va_ctx, "render pool:%zd", i)); + } +} + +void +QFV_Render_Shutdown (vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + __auto_type rctx = ctx->render_context; + if (rctx->job) { + __auto_type job = rctx->job; + for (uint32_t i = 0; i < job->num_renderpasses; i++) { + dfunc->vkDestroyRenderPass (device->dev, job->renderpasses[i], 0); + } + for (uint32_t i = 0; i < job->num_pipelines; i++) { + dfunc->vkDestroyPipeline (device->dev, job->pipelines[i], 0); + } + for (uint32_t i = 0; i < job->num_layouts; i++) { + dfunc->vkDestroyPipelineLayout (device->dev, job->layouts[i], 0); + } + if (job->resources) { + QFV_DestroyResource (ctx->device, job->resources); + free (job->resources); + } + for (uint32_t i = 0; i < job->num_steps; i++) { + if (job->steps[i].render) { + auto render = job->steps[i].render; + for (uint32_t j = 0; j < render->num_renderpasses; j++) { + auto bi = &render->renderpasses[j].beginInfo; + if (bi->framebuffer) { + dfunc->vkDestroyFramebuffer (device->dev, + bi->framebuffer, 0); + } + } + } + } + DARRAY_CLEAR (&job->commands); + for (uint32_t i = 0; i < job->num_dsmanagers; i++) { + QFV_DSManager_Destroy (job->dsmanager[i]); + } + free (rctx->job); + } + + for (uint32_t i = 0; i < rctx->frames.size; i++) { + auto dev = device->dev; + auto df = dfunc; + auto frame = &rctx->frames.a[i]; + df->vkDestroyFence (dev, frame->fence, 0); + df->vkDestroySemaphore (dev, frame->imageAvailableSemaphore, 0); + df->vkDestroySemaphore (dev, frame->renderDoneSemaphore, 0); + QFV_CmdPoolManager_Shutdown (&frame->cmdpool); + } + DARRAY_CLEAR (&rctx->frames); + + if (rctx->jobinfo) { + __auto_type jinfo = rctx->jobinfo; + for (uint32_t i = 0; i < jinfo->num_dslayouts; i++) { + __auto_type setLayout = jinfo->dslayouts[i].setLayout; + dfunc->vkDestroyDescriptorSetLayout (device->dev, setLayout, 0); + } + delete_memsuper (jinfo->memsuper); + } + if (rctx->task_functions.tab) { + Hash_DelTable (rctx->task_functions.tab); + } + if (rctx->samplerinfo) { + auto si = rctx->samplerinfo; + for (uint32_t i = 0; i < si->num_samplers; i++) { + auto sci = &si->samplers[i]; + if (sci->sampler) { + dfunc->vkDestroySampler (device->dev, sci->sampler, 0); + } + } + } + Hash_DelContext (rctx->hashctx); +} + +void +QFV_Render_AddTasks (vulkan_ctx_t *ctx, exprsym_t *task_syms) +{ + __auto_type rctx = ctx->render_context; + exprctx_t ectx = { .hashctx = &rctx->hashctx }; + for (exprsym_t *sym = task_syms; sym->name; sym++) { + Hash_Add (rctx->task_functions.tab, sym); + for (exprfunc_t *f = sym->value; f->func; f++) { + for (int i = 0; i < f->num_params; i++) { + exprenum_t *e = f->param_types[i]->data; + if (e && !e->symtab->tab) { + cexpr_init_symtab (e->symtab, &ectx); + } + } + } + } +} + +qfv_step_t * +QFV_FindStep (const char *name, qfv_job_t *job) +{ + for (uint32_t i = 0; i < job->num_steps; i++) { + auto step = &job->steps[i]; + if (!strcmp (step->label.name, name)) { + return step; + } + } + return 0; +} + +qfv_step_t * +QFV_GetStep (const exprval_t *param, qfv_job_t *job) +{ + // this is a little evil, but need to update the type after resolving + // the step name + auto stepref = (exprval_t *) param; + + // cache the render step referenced, using the parameter type as a flag + // for whether the caching has been performed. + if (stepref->type == &cexpr_string) { + if (cexpr_string.size != cexpr_voidptr.size) { + Sys_Error ("string and voidptr incompatible sizes"); + } + auto name = *(const char **)stepref->value; + stepref->type = &cexpr_voidptr; + *(qfv_step_t **)stepref->value = QFV_FindStep (name, job); + } + return *(qfv_step_t **)stepref->value; +} + +qfv_dsmanager_t * +QFV_Render_DSManager (vulkan_ctx_t *ctx, const char *setName) +{ + auto job = ctx->render_context->job; + for (uint32_t i = 0; i < job->num_dsmanagers; i++) { + auto ds = job->dsmanager[i]; + if (!strcmp (ds->name, setName)) { + return ds; + } + } + return 0; +} + +static void +create_sampler (vulkan_ctx_t *ctx, qfv_samplercreateinfo_t *sampler) +{ + VkSamplerCreateInfo create = { + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .flags = sampler->flags, + .magFilter = sampler->magFilter, + .minFilter = sampler->minFilter, + .mipmapMode = sampler->mipmapMode, + .addressModeU = sampler->addressModeU, + .addressModeV = sampler->addressModeV, + .addressModeW = sampler->addressModeW, + .mipLodBias = sampler->mipLodBias, + .anisotropyEnable = sampler->anisotropyEnable, + .maxAnisotropy = sampler->maxAnisotropy, + .compareEnable = sampler->compareEnable, + .compareOp = sampler->compareOp, + .minLod = sampler->minLod, + .maxLod = sampler->maxLod, + .borderColor = sampler->borderColor, + .unnormalizedCoordinates = sampler->unnormalizedCoordinates, + }; + auto device = ctx->device; + auto dfunc = device->funcs; + dfunc->vkCreateSampler (device->dev, &create, 0, &sampler->sampler); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_SAMPLER, sampler->sampler, + va (ctx->va_ctx, "sampler:%s", sampler->name)); +} + +VkSampler +QFV_Render_Sampler (vulkan_ctx_t *ctx, const char *name) +{ + auto si = ctx->render_context->samplerinfo; + if (!si) { + return 0; + } + for (uint32_t i = 0; i < si->num_samplers; i++) { + auto sci = &si->samplers[i]; + if (!strcmp (sci->name, name)) { + if (!sci->sampler) { + create_sampler (ctx, sci); + } + return sci->sampler; + } + } + printf ("sampler %s not found\n", name); + return 0; +} diff --git a/libs/video/renderer/vulkan/render_load.c b/libs/video/renderer/vulkan/render_load.c new file mode 100644 index 000000000..6e75292be --- /dev/null +++ b/libs/video/renderer/vulkan/render_load.c @@ -0,0 +1,1178 @@ +/* + render_load.c + + Vulkan render manager loading/creation + + Copyright (C) 2023 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_MATH_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/cmem.h" +#include "QF/hash.h" +#include "QF/va.h" +#include "QF/Vulkan/command.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/dsmanager.h" +#include "QF/Vulkan/pipeline.h" +#include "QF/Vulkan/render.h" +#include "QF/Vulkan/resource.h" +#include "QF/Vulkan/swapchain.h" +#include "vid_vulkan.h" + +#include "vkparse.h" + +static qfv_output_t +get_output (vulkan_ctx_t *ctx, plitem_t *item) +{ + qfv_output_t output = {}; + Vulkan_ConfigOutput (ctx, &output); + + plitem_t *output_def = PL_ObjectForKey (item, "output"); + if (output_def) { + // QFV_ParseOutput clears the structure, but extent and frames need to + // be preserved + qfv_output_t o = output; + QFV_ParseOutput (ctx, &o, output_def, item); + output.format = o.format; + output.finalLayout = o.finalLayout; + } + return output; +} + +void +QFV_LoadRenderInfo (vulkan_ctx_t *ctx, const char *name) +{ + auto rctx = ctx->render_context; + auto item = Vulkan_GetConfig (ctx, name); + auto output = get_output (ctx, item); + Vulkan_Script_SetOutput (ctx, &output); + rctx->jobinfo = QFV_ParseJobInfo (ctx, item, rctx); + if (rctx->jobinfo) { + rctx->jobinfo->plitem = item; + } +} + +void +QFV_LoadSamplerInfo (vulkan_ctx_t *ctx, const char *name) +{ + auto rctx = ctx->render_context; + auto item = Vulkan_GetConfig (ctx, name); + rctx->samplerinfo = QFV_ParseSamplerInfo (ctx, item, rctx); + if (rctx->samplerinfo) { + rctx->samplerinfo->plitem = item; + } +} + +typedef struct { + uint32_t num_images; + uint32_t num_imageviews; + uint32_t num_buffers; + uint32_t num_bufferviews; + uint32_t num_layouts; + + uint32_t num_steps; + uint32_t num_render; + uint32_t num_compute; + uint32_t num_process; + uint32_t num_tasks; + + uint32_t num_renderpasses; + uint32_t num_attachments; + uint32_t num_subpasses; + uint32_t num_dependencies; + uint32_t num_attachmentrefs; + uint32_t num_colorblend; + uint32_t num_preserve; + uint32_t num_graph_pipelines; + uint32_t num_comp_pipelines; + + uint32_t num_ds_indices; +} objcount_t; + +static void +count_as_stuff (qfv_attachmentsetinfo_t *as, objcount_t *counts) +{ + counts->num_attachmentrefs += as->num_input; + counts->num_attachmentrefs += as->num_color; + counts->num_colorblend += as->num_color; + if (as->resolve) { + counts->num_attachmentrefs += as->num_color; + } + if (as->depth) { + counts->num_attachmentrefs += 1; + } + counts->num_preserve += as->num_preserve; +} + +static void +count_sp_stuff (qfv_subpassinfo_t *spi, objcount_t *counts) +{ + counts->num_dependencies += spi->num_dependencies; + if (spi->attachments) { + count_as_stuff (spi->attachments, counts); + } + for (uint32_t i = 0; i < spi->num_pipelines; i++) { + __auto_type pli = &spi->pipelines[i]; + if (pli->num_graph_stages && !pli->compute_stage) { + counts->num_graph_pipelines++; + counts->num_tasks += pli->num_tasks; + } else { + Sys_Error ("%s:%s: invalid graphics pipeline", + spi->name, pli->name); + } + } +} + +static void +count_rp_stuff (qfv_renderpassinfo_t *rpi, objcount_t *counts) +{ + counts->num_attachments += rpi->framebuffer.num_attachments; + counts->num_subpasses += rpi->num_subpasses; + for (uint32_t i = 0; i < rpi->num_subpasses; i++) { + count_sp_stuff (&rpi->subpasses[i], counts); + } +} + +static void +count_comp_stuff (qfv_computeinfo_t *ci, objcount_t *counts) +{ + for (uint32_t i = 0; i < ci->num_pipelines; i++) { + __auto_type pli = &ci->pipelines[i]; + if (!pli->num_graph_stages && pli->compute_stage) { + counts->num_comp_pipelines++; + counts->num_tasks += pli->num_tasks; + } else { + Sys_Error ("%s:%s: invalid compute pipeline", + ci->name, pli->name); + } + } +} + +static void +count_step_stuff (qfv_stepinfo_t *step, objcount_t *counts) +{ + if ((step->render && step->compute) + || (step->render && step->process) + || (step->compute && step->process)) { + Sys_Error ("%s: invalid step: must be one of render/compute/process", + step->name); + } + if (step->render) { + __auto_type rinfo = step->render; + counts->num_renderpasses += rinfo->num_renderpasses; + for (uint32_t i = 0; i < rinfo->num_renderpasses; i++) { + count_rp_stuff (&rinfo->renderpasses[i], counts); + } + counts->num_render++; + } + if (step->compute) { + count_comp_stuff (step->compute, counts); + counts->num_compute++; + } + if (step->process) { + counts->num_process++; + counts->num_tasks += step->process->num_tasks; + } + counts->num_steps++; +} + +static void +count_stuff (qfv_jobinfo_t *jobinfo, objcount_t *counts) +{ + counts->num_images += jobinfo->num_images; + counts->num_imageviews += jobinfo->num_imageviews; + counts->num_buffers += jobinfo->num_buffers; + counts->num_bufferviews += jobinfo->num_bufferviews; + for (uint32_t i = 0; i < jobinfo->num_steps; i++) { + count_step_stuff (&jobinfo->steps[i], counts); + } +} + +static void +create_resources (vulkan_ctx_t *ctx, objcount_t *counts) +{ + auto rctx = ctx->render_context; + auto jinfo = rctx->jobinfo; + auto job = rctx->job; + + size_t size = sizeof (qfv_resource_t); + size += sizeof (qfv_resobj_t [counts->num_images]); + size += sizeof (qfv_resobj_t [counts->num_imageviews]); + + job->resources = malloc (size); + job->images = (qfv_resobj_t *) &job->resources[1]; + job->image_views = &job->images[counts->num_images]; + + job->resources[0] = (qfv_resource_t) { + .name = "render", + .va_ctx = ctx->va_ctx, + .memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + .num_objects = counts->num_images + counts->num_imageviews, + .objects = job->images, + }; + for (uint32_t i = 0; i < counts->num_images; i++) { + __auto_type img = &jinfo->images[i]; + job->images[i] = (qfv_resobj_t) { + .name = img->name, + .type = qfv_res_image, + .image = { + .flags = img->flags, + .type = img->imageType, + .format = img->format, + .extent = img->extent, + .num_mipmaps = img->mipLevels, + .num_layers = img->arrayLayers, + .samples = img->samples, + .tiling = img->tiling, + .usage = img->usage, + .initialLayout = img->initialLayout, + }, + }; + } + int error = 0; + for (uint32_t i = 0; i < counts->num_imageviews; i++) { + __auto_type view = &jinfo->imageviews[i]; + job->image_views[i] = (qfv_resobj_t) { + .name = view->name, + .type = qfv_res_image_view, + .image_view = { + .flags = view->flags, + .type = view->viewType, + .format = view->format, + .components = view->components, + .subresourceRange = view->subresourceRange, + }, + }; + if (strcmp (view->image.name, "$output.image") == 0) { + //__auto_type image = jinfo->output.image; + //job->image_views[i].image_view.external_image = image; + //job->image_views[i].image_view.image = -1; + } else { + qfv_resobj_t *img = 0; + for (uint32_t j = 0; j < jinfo->num_images; j++) { + if (strcmp (view->image.name, jinfo->images[j].name) == 0) { + img = &job->images[j]; + } + } + if (img) { + uint32_t ind = img - job->resources->objects; + job->image_views[i].image_view.image = ind; + } else { + Sys_Printf ("%d: unknown image reference: %s\n", + view->image.line, view->image.name); + error = 1; + } + } + } + if (error) { + free (job->resources); + job->resources = 0; + return; + } + QFV_CreateResource (ctx->device, job->resources); +} + +typedef struct { + VkRenderPassCreateInfo *rpCreate; + VkAttachmentDescription *attach; + VkClearValue *clear; + VkSubpassDescription *subpass; + VkSubpassDependency *depend; + VkAttachmentReference *attachref; + VkPipelineColorBlendAttachmentState *cbAttach; + uint32_t *preserve; + const char **rpName; + const char **plName; + VkComputePipelineCreateInfo *cplCreate; + VkGraphicsPipelineCreateInfo *gplCreate; + VkPipelineColorBlendStateCreateInfo *cbState; + qfv_layoutinfo_t *layouts; + + uint32_t *pl_counts; + + VkPipeline *gpl; + VkPipeline *cpl; + VkRenderPass *rp; +} objptr_t; + +typedef struct { + objcount_t inds; + objptr_t ptr; + + vulkan_ctx_t *ctx; + qfv_jobinfo_t *jinfo; + exprtab_t *symtab; + qfv_renderpassinfo_t *rpi; + VkRenderPassCreateInfo *rpc; + qfv_subpassinfo_t *spi; + VkSubpassDescription *spc; + qfv_pipelineinfo_t *pli; + VkGraphicsPipelineCreateInfo *plc; +} objstate_t; + +static uint32_t __attribute__((pure)) +find_subpass (qfv_dependencyinfo_t *d, uint32_t spind, + qfv_subpassinfo_t *subpasses) +{ + if (strcmp (d->name, "$external") == 0) { + return VK_SUBPASS_EXTERNAL; + } + for (uint32_t i = 0; i <= spind; i++) { + __auto_type s = &subpasses[i]; + if (strcmp (d->name, s->name) == 0) { + return i; + } + } + Sys_Error ("invalid dependency: [%d] %s", spind, d->name); +} + +static uint32_t __attribute__((pure)) +find_ds_index (const qfv_reference_t *ref, objstate_t *s) +{ + for (uint32_t i = 0; i < s->jinfo->num_dslayouts; i++) { + __auto_type ds = &s->jinfo->dslayouts[i]; + if (strcmp (ds->name, ref->name) == 0) { + return i; + } + } + Sys_Error ("%s.%s:%d: invalid descriptor set layout: %s", + s->rpi->name, s->spi->name, ref->line, ref->name); +} + +static VkDescriptorSetLayout +find_descriptorSet (const qfv_reference_t *ref, objstate_t *s) +{ + auto ds = &s->jinfo->dslayouts[find_ds_index (ref, s)]; + if (ds->setLayout) { + return ds->setLayout; + } + + VkDescriptorSetLayoutCreateInfo cInfo = { + .sType=VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .flags = ds->flags, + .bindingCount = ds->num_bindings, + .pBindings = ds->bindings, + }; + qfv_device_t *device = s->ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + dfunc->vkCreateDescriptorSetLayout (device->dev, &cInfo, 0, &ds->setLayout); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, + ds->setLayout, va (s->ctx->va_ctx, "descriptorSet:%s", + ds->name)); + return ds->setLayout; +} + +#define RUP(x,a) (((x) + ((a) - 1)) & ~((a) - 1)) + +static uint32_t +parse_pushconstantrange (VkPushConstantRange *range, + qfv_pushconstantrangeinfo_t *pushconstantrange, + uint32_t offset, objstate_t *s) +{ + uint32_t range_offset = ~0u; + for (uint32_t i = 0; i < pushconstantrange->num_pushconstants; i++) { + __auto_type pushconstant = & pushconstantrange->pushconstants[i]; + uint32_t size = 0; + if (pushconstant->offset != ~0u) { + offset = pushconstant->offset; + } + if (pushconstant->size != ~0u) { + size = pushconstant->size; + } else { + switch (pushconstant->type) { + case qfv_float: + case qfv_int: + case qfv_uint: + size = sizeof (int32_t); + offset = RUP (offset, sizeof (int32_t)); + break; + case qfv_vec3: + size = sizeof (vec3_t); + offset = RUP (offset, sizeof (vec4f_t)); + break; + case qfv_vec4: + size = sizeof (vec4f_t); + offset = RUP (offset, sizeof (vec4f_t)); + break; + case qfv_mat4: + size = sizeof (mat4f_t); + offset = RUP (offset, sizeof (vec4f_t)); + break; + default: + Sys_Error ("%s.%s:%s:%d invalid type: %d", + s->rpi->name, s->spi->name, pushconstant->name, + pushconstant->line, pushconstant->type); + } + } + if (range_offset == ~0u) { + range_offset = offset; + } + offset += size; + } + *range = (VkPushConstantRange) { + .stageFlags = pushconstantrange->stageFlags, + .offset = range_offset, + .size = offset - range_offset, + }; + return offset; +} + +static qfv_layoutinfo_t * +find_layout (const qfv_reference_t *ref, objstate_t *s) +{ + for (uint32_t i = 0; i < s->inds.num_layouts; i++) { + if (strcmp (s->ptr.layouts[i].name, ref->name) == 0) { + return &s->ptr.layouts[i]; + } + } + if (!QFV_ParseLayoutInfo (s->ctx, s->jinfo->memsuper, s->symtab, ref->name, + &s->ptr.layouts[s->inds.num_layouts])) { + Sys_Error ("%s.%s:%d: invalid layout: %s", + s->rpi->name, s->spi->name, ref->line, ref->name); + } + __auto_type li = &s->ptr.layouts[s->inds.num_layouts++]; + li->name = ref->name; + VkDescriptorSetLayout sets[li->num_sets]; + for (uint32_t i = 0; i < li->num_sets; i++) { + sets[i] = find_descriptorSet (&li->sets[i], s); + } + VkPushConstantRange ranges[li->num_pushconstantranges]; + uint32_t offset = 0; + for (uint32_t i = 0; i < li->num_pushconstantranges; i++) { + offset = parse_pushconstantrange (&ranges[i], + &li->pushconstantranges[i], + offset, s); + } + VkPipelineLayoutCreateInfo cInfo = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .setLayoutCount = li->num_sets, + .pSetLayouts = sets, + .pushConstantRangeCount = li->num_pushconstantranges, + .pPushConstantRanges = ranges, + }; + qfv_device_t *device = s->ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + dfunc->vkCreatePipelineLayout (device->dev, &cInfo, 0, &li->layout); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_PIPELINE_LAYOUT, li->layout, + va (s->ctx->va_ctx, "layout:%s", li->name)); + return li; +} + +static void +init_plCreate (VkGraphicsPipelineCreateInfo *plc, const qfv_pipelineinfo_t *pli, + objstate_t *s) +{ + if (pli->num_graph_stages) { + plc->stageCount = pli->num_graph_stages; + } + if (pli->graph_stages) { + plc->pStages = pli->graph_stages; + } + if (pli->vertexInput) { + plc->pVertexInputState = pli->vertexInput; + } + if (pli->inputAssembly) { + plc->pInputAssemblyState = pli->inputAssembly; + } + if (pli->tessellation) { + plc->pTessellationState = pli->tessellation; + } + if (pli->viewport) { + plc->pViewportState = pli->viewport; + } + if (pli->rasterization) { + plc->pRasterizationState = pli->rasterization; + } + if (pli->multisample) { + plc->pMultisampleState = pli->multisample; + } + if (pli->depthStencil) { + plc->pDepthStencilState = pli->depthStencil; + } + if (pli->colorBlend) { + VkPipelineColorBlendStateCreateInfo *cb; + cb = (VkPipelineColorBlendStateCreateInfo *) plc->pColorBlendState; + *cb = *pli->colorBlend; + } + if (pli->dynamic) { + plc->pDynamicState = pli->dynamic; + } + if (pli->layout.name) { + __auto_type li = find_layout (&pli->layout, s); + plc->layout = li->layout; + s->inds.num_ds_indices += li->num_sets; + } +} + +static uint32_t __attribute__((pure)) +find_attachment (qfv_reference_t *ref, objstate_t *s) +{ + for (uint32_t i = 0; i < s->rpi->framebuffer.num_attachments; i++) { + __auto_type a = &s->rpi->framebuffer.attachments[i]; + if (strcmp (ref->name, a->name) == 0) { + return i; + } + } + Sys_Error ("%s.%s:%d: invalid attachment: %s", + s->rpi->name, s->spi->name, ref->line, ref->name); +} + +static void +init_arCreate (const qfv_attachmentrefinfo_t *ari, objstate_t *s) +{ + __auto_type arc = &s->ptr.attachref[s->inds.num_attachmentrefs]; + qfv_reference_t ref = { + .name = ari->name, + .line = ari->line, + }; + + *arc = (VkAttachmentReference) { + .attachment = find_attachment (&ref, s), + .layout = ari->layout, + }; +} + +static void +init_cbCreate (const qfv_attachmentrefinfo_t *ari, objstate_t *s) +{ + __auto_type cbc = &s->ptr.cbAttach[s->inds.num_colorblend]; + + *cbc = ari->blend; +} + +static void +init_spCreate (uint32_t index, qfv_subpassinfo_t *sub, objstate_t *s) +{ + s->spi = &sub[index]; + s->plc = &s->ptr.gplCreate[s->inds.num_graph_pipelines]; + s->spc = &s->ptr.subpass[s->inds.num_subpasses]; + __auto_type pln = &s->ptr.plName[s->inds.num_graph_pipelines]; + __auto_type cbs = &s->ptr.cbState[s->inds.num_graph_pipelines]; + + s->ptr.pl_counts[s->inds.num_renderpasses] += s->spi->num_pipelines; + + *s->spc = (VkSubpassDescription) { + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + }; + for (uint32_t i = 0; i < s->spi->num_dependencies; i++) { + __auto_type d = &s->spi->dependencies[i]; + __auto_type dep = &s->ptr.depend[s->inds.num_dependencies++]; + *dep = (VkSubpassDependency) { + .srcSubpass = find_subpass (d, index, s->rpi->subpasses), + .dstSubpass = index, + .srcStageMask = d->src.stage, + .dstStageMask = d->dst.stage, + .srcAccessMask = d->src.access, + .dstAccessMask = d->dst.access, + .dependencyFlags = d->flags, + }; + } + + for (uint32_t i = 0; i < s->spi->num_pipelines; i++) { + s->plc[i] = (VkGraphicsPipelineCreateInfo) { + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pColorBlendState = &cbs[i], + .subpass = index, + }; + if (s->spi->base_pipeline) { + init_plCreate (&s->plc[i], s->spi->base_pipeline, s); + } + init_plCreate (&s->plc[i], &s->spi->pipelines[i], s); + pln[i] = s->spi->name; + s->inds.num_graph_pipelines++; + } + + __auto_type att = s->spi->attachments; + if (!att) { + return; + } + for (uint32_t i = 0; i < s->spi->num_pipelines; i++) { + cbs[i].attachmentCount = att->num_color; + cbs[i].pAttachments = &s->ptr.cbAttach[s->inds.num_colorblend]; + } + s->spc->inputAttachmentCount = att->num_input; + s->spc->pInputAttachments = &s->ptr.attachref[s->inds.num_attachmentrefs]; + for (uint32_t i = 0; i < att->num_input; i++) { + init_arCreate (&att->input[i], s); + s->inds.num_attachmentrefs++; + } + s->spc->colorAttachmentCount = att->num_color; + s->spc->pColorAttachments = &s->ptr.attachref[s->inds.num_attachmentrefs]; + for (uint32_t i = 0; i < att->num_color; i++) { + init_arCreate (&att->color[i], s); + s->inds.num_attachmentrefs++; + init_cbCreate (&att->color[i], s); + s->inds.num_colorblend++; + } + if (att->resolve) { + s->spc->pResolveAttachments + = &s->ptr.attachref[s->inds.num_attachmentrefs]; + for (uint32_t i = 0; i < att->num_color; i++) { + init_arCreate (&att->resolve[i], s); + s->inds.num_attachmentrefs++; + } + } + if (att->depth) { + s->spc->pDepthStencilAttachment + = &s->ptr.attachref[s->inds.num_attachmentrefs]; + init_arCreate (att->depth, s); + s->inds.num_attachmentrefs++; + } + s->spc->preserveAttachmentCount = att->num_preserve; + s->spc->pPreserveAttachments = &s->ptr.preserve[s->inds.num_preserve]; + for (uint32_t i = 0; i < att->num_preserve; i++) { + s->ptr.preserve[s->inds.num_preserve] + = find_attachment (&att->preserve[i], s); + s->inds.num_preserve++; + } +} + +static void +init_atCreate (uint32_t index, qfv_attachmentinfo_t *attachments, objstate_t *s) +{ + __auto_type ati = &attachments[index]; + __auto_type atc = &s->ptr.attach[s->inds.num_attachments]; + __auto_type cvc = &s->ptr.clear[s->inds.num_attachments]; + + if (ati->external) { + if (!strcmp (ati->external, "$swapchain")) { + *atc = (VkAttachmentDescription) { + .format = s->ctx->swapchain->format, + .samples = 1, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + }; + } + } else { + *atc = (VkAttachmentDescription) { + .flags = ati->flags, + .format = ati->format, + .samples = ati->samples, + .loadOp = ati->loadOp, + .storeOp = ati->storeOp, + .stencilLoadOp = ati->stencilLoadOp, + .stencilStoreOp = ati->stencilStoreOp, + .initialLayout = ati->initialLayout, + .finalLayout = ati->finalLayout, + }; + } + *cvc = ati->clearValue; +} + +static void +init_rpCreate (uint32_t index, const qfv_renderinfo_t *rinfo, objstate_t *s) +{ + s->rpi = &rinfo->renderpasses[index]; + s->rpc = &s->ptr.rpCreate[s->inds.num_renderpasses]; + s->ptr.rpName[s->inds.num_renderpasses] = s->rpi->name; + + __auto_type attachments = &s->ptr.attach[s->inds.num_attachments]; + __auto_type subpasses = &s->ptr.subpass[s->inds.num_subpasses]; + __auto_type dependencies = &s->ptr.depend[s->inds.num_dependencies]; + + for (uint32_t i = 0; i < s->rpi->framebuffer.num_attachments; i++) { + init_atCreate (i, s->rpi->framebuffer.attachments, s); + s->inds.num_attachments++; + } + + uint32_t num_dependencies = s->inds.num_dependencies; + for (uint32_t i = 0; i < s->rpi->num_subpasses; i++) { + init_spCreate (i, s->rpi->subpasses, s); + s->inds.num_subpasses++; + } + num_dependencies = s->inds.num_dependencies - num_dependencies; + + *s->rpc = (VkRenderPassCreateInfo) { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + .pNext = s->rpi->pNext, + .attachmentCount = s->rpi->framebuffer.num_attachments, + .pAttachments = attachments, + .subpassCount = s->rpi->num_subpasses, + .pSubpasses = subpasses, + .dependencyCount = num_dependencies, + .pDependencies = dependencies, + }; +} + +typedef struct { + qfv_step_t *steps; + qfv_render_t *renders; + qfv_compute_t *computes; + qfv_process_t *processes; + qfv_renderpass_t *renderpasses; + VkClearValue *clearvalues; + qfv_subpass_t *subpasses; + qfv_pipeline_t *pipelines; + qfv_taskinfo_t *tasks; + uint32_t *ds_indices; + VkImageView *attachment_views; +} jobptr_t; + +static void +init_pipeline (qfv_pipeline_t *pl, qfv_pipelineinfo_t *plinfo, + jobptr_t *jp, objstate_t *s, int is_compute) +{ + __auto_type li = find_layout (&plinfo->layout, s); + *pl = (qfv_pipeline_t) { + .label = { + .name = plinfo->name, + .color = plinfo->color, + }, + .disabled = plinfo->disabled, + .bindPoint = is_compute ? VK_PIPELINE_BIND_POINT_COMPUTE + : VK_PIPELINE_BIND_POINT_GRAPHICS, + .pipeline = is_compute ? s->ptr.cpl[s->inds.num_comp_pipelines] + : s->ptr.gpl[s->inds.num_graph_pipelines], + .layout = li->layout, + .task_count = plinfo->num_tasks, + .tasks = &jp->tasks[s->inds.num_tasks], + .num_indices = li->num_sets, + .ds_indices = &jp->ds_indices[s->inds.num_ds_indices], + }; + s->inds.num_tasks += plinfo->num_tasks; + s->inds.num_ds_indices += li->num_sets; + for (uint32_t i = 0; i < pl->num_indices; i++) { + pl->ds_indices[i] = find_ds_index (&li->sets[i], s); + } + for (uint32_t i = 0; i < pl->task_count; i++) { + pl->tasks[i] = plinfo->tasks[i]; + } +} + +static void +init_subpass (qfv_subpass_t *sp, qfv_subpassinfo_t *isp, + jobptr_t *jp, objstate_t *s) +{ + uint32_t np = s->inds.num_graph_pipelines + s->inds.num_comp_pipelines; + *sp = (qfv_subpass_t) { + .label = { + .name = isp->name, + .color = isp->color, + }, + .inherit = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, + }, + .beginInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT + | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT, + .pInheritanceInfo = &sp->inherit, + }, + .pipeline_count = isp->num_pipelines, + .pipelines = &jp->pipelines[np], + }; + for (uint32_t i = 0; i < isp->num_pipelines; i++) { + init_pipeline (&sp->pipelines[i], &isp->pipelines[i], jp, s, 0); + s->inds.num_graph_pipelines++; + } +} + +static void +init_renderpass (qfv_renderpass_t *rp, qfv_renderpassinfo_t *rpinfo, + jobptr_t *jp, objstate_t *s) +{ + *rp = (qfv_renderpass_t) { + .vulkan_ctx = s->ctx, + .label.name = rpinfo->name, + .label.color = rpinfo->color, + .beginInfo = (VkRenderPassBeginInfo) { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .renderPass = s->ptr.rp[s->inds.num_renderpasses], + .clearValueCount = rpinfo->framebuffer.num_attachments, + .pClearValues = &jp->clearvalues[s->inds.num_attachments], + }, + .subpassContents = VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS, + .subpass_count = rpinfo->num_subpasses, + .subpasses = &jp->subpasses[s->inds.num_subpasses], + .framebuffer = { + .num_attachments = rpinfo->framebuffer.num_attachments, + .views = &jp->attachment_views[s->inds.num_attachments], + }, + .framebufferinfo = &rpinfo->framebuffer, + .outputref = rpinfo->output, + }; + s->inds.num_attachments += rpinfo->framebuffer.num_attachments; + for (uint32_t i = 0; i < rpinfo->num_subpasses; i++) { + init_subpass (&rp->subpasses[i], &rpinfo->subpasses[i], jp, s); + rp->subpasses[i].inherit.renderPass = rp->beginInfo.renderPass; + rp->subpasses[i].inherit.subpass = i; + s->inds.num_subpasses++; + } +} + +static void +init_render (qfv_render_t *rend, qfv_renderinfo_t *rinfo, + jobptr_t *jp, objstate_t *s) +{ + *rend = (qfv_render_t) { + .label.color = rinfo->color, + .label.name = rinfo->name, + .num_renderpasses = rinfo->num_renderpasses, + .renderpasses = &jp->renderpasses[s->inds.num_renderpasses], + }; + for (uint32_t i = 0; i < rend->num_renderpasses; i++) { + init_renderpass (&rend->renderpasses[i], &rinfo->renderpasses[i], + jp, s); + s->inds.num_renderpasses++; + } + rend->active = &rend->renderpasses[0]; +} + +static void +init_compute (qfv_compute_t *comp, qfv_computeinfo_t *cinfo, + jobptr_t *jp, objstate_t *s) +{ + uint32_t np = s->inds.num_graph_pipelines + s->inds.num_comp_pipelines; + *comp = (qfv_compute_t) { + .label.color = cinfo->color, + .label.name = cinfo->name, + .pipelines = &jp->pipelines[np], + .pipeline_count = cinfo->num_pipelines, + }; + for (uint32_t i = 0; i < cinfo->num_pipelines; i++) { + init_pipeline (&comp->pipelines[i], &cinfo->pipelines[i], jp, s, 1); + s->inds.num_comp_pipelines++; + } +} + +static void +init_process (qfv_process_t *proc, qfv_processinfo_t *pinfo, + jobptr_t *jp, objstate_t *s) +{ + *proc = (qfv_process_t) { + .label.color = pinfo->color, + .label.name = pinfo->name, + .tasks = &jp->tasks[s->inds.num_tasks], + .task_count = pinfo->num_tasks, + }; + s->inds.num_tasks += pinfo->num_tasks; + for (uint32_t i = 0; i < proc->task_count; i++) { + proc->tasks[i] = pinfo->tasks[i]; + } +} + +static void +init_step (uint32_t ind, jobptr_t *jp, objstate_t *s) +{ + __auto_type step = &jp->steps[s->inds.num_steps++]; + __auto_type sinfo = &s->jinfo->steps[ind]; + + *step = (qfv_step_t) { + .label.name = sinfo->name, + .label.color = sinfo->color, + }; + if (sinfo->render) { + step->render = &jp->renders[s->inds.num_render++]; + init_render (step->render, sinfo->render, jp, s); + } + if (sinfo->compute) { + step->compute = &jp->computes[s->inds.num_compute++]; + init_compute (step->compute, sinfo->compute, jp, s); + } + if (sinfo->process) { + step->process = &jp->processes[s->inds.num_process++]; + init_process (step->process, sinfo->process, jp, s); + } +} + +static void +init_job (vulkan_ctx_t *ctx, objcount_t *counts, objstate_t s) +{ + auto rctx = ctx->render_context; + auto jobinfo = rctx->jobinfo; + + size_t size = sizeof (qfv_job_t); + + size += sizeof (qfv_step_t [counts->num_steps]); + size += sizeof (qfv_render_t [counts->num_render]); + size += sizeof (qfv_compute_t [counts->num_compute]); + size += sizeof (qfv_process_t [counts->num_process]); + size += sizeof (qfv_renderpass_t [counts->num_renderpasses]); + size += sizeof (qfv_subpass_t [counts->num_subpasses]); + size += sizeof (qfv_pipeline_t [counts->num_graph_pipelines]); + size += sizeof (qfv_pipeline_t [counts->num_comp_pipelines]); + size += sizeof (qfv_taskinfo_t [counts->num_tasks]); + + size += sizeof (VkClearValue [counts->num_attachments]); + size += sizeof (VkRenderPass [counts->num_renderpasses]); + size += sizeof (VkPipeline [counts->num_graph_pipelines]); + size += sizeof (VkPipeline [counts->num_comp_pipelines]); + size += sizeof (VkPipelineLayout [s.inds.num_layouts]); + size += sizeof (VkImageView [counts->num_attachments]); + size += sizeof (qfv_dsmanager_t *[jobinfo->num_dslayouts]); + size += sizeof (uint32_t [counts->num_ds_indices]); + + rctx->job = malloc (size); + auto job = rctx->job; + *job = (qfv_job_t) { + .num_renderpasses = counts->num_renderpasses, + .num_pipelines = counts->num_graph_pipelines + + counts->num_comp_pipelines, + .num_layouts = s.inds.num_layouts, + .num_steps = counts->num_steps, + .commands = DARRAY_STATIC_INIT (16), + .num_dsmanagers = jobinfo->num_dslayouts, + }; + job->steps = (qfv_step_t *) &job[1]; + auto rn = (qfv_render_t *) &job->steps[job->num_steps]; + auto cp = (qfv_compute_t *) &rn[counts->num_render]; + auto pr = (qfv_process_t *) &cp[counts->num_compute]; + auto rp = (qfv_renderpass_t *) &pr[counts->num_process]; + auto sp = (qfv_subpass_t *) &rp[counts->num_renderpasses]; + auto pl = (qfv_pipeline_t *) &sp[counts->num_subpasses]; + auto ti = (qfv_taskinfo_t *) &pl[job->num_pipelines]; + + auto cv = (VkClearValue *) &ti[counts->num_tasks]; + job->renderpasses = (VkRenderPass *) &cv[counts->num_attachments]; + job->pipelines = (VkPipeline *) &job->renderpasses[job->num_renderpasses]; + job->layouts = (VkPipelineLayout *) &job->pipelines[job->num_pipelines]; + auto av = (VkImageView *) &job->layouts[s.inds.num_layouts]; + job->dsmanager = (qfv_dsmanager_t **) &av[counts->num_attachments]; + auto ds = (uint32_t *) &job->dsmanager[jobinfo->num_dslayouts]; + + jobptr_t jp = { + .steps = job->steps, + .renders = rn, + .computes = cp, + .processes = pr, + .renderpasses = rp, + .clearvalues = cv, + .subpasses = sp, + .pipelines = pl, + .tasks = ti, + .ds_indices = ds, + .attachment_views = av, + }; + + for (uint32_t i = 0; i < job->num_renderpasses; i++) { + job->renderpasses[i] = s.ptr.rp[i]; + } + for (uint32_t i = 0; i < job->num_pipelines; i++) { + // compute pipelines come immediately after the graphics pipelines + job->pipelines[i] = s.ptr.gpl[i]; + } + for (uint32_t i = 0; i < s.inds.num_layouts; i++) { + job->layouts[i] = s.ptr.layouts[i].layout; + } + memcpy (cv, s.ptr.clear, sizeof (VkClearValue [counts->num_attachments ])); + + uint32_t num_layouts = s.inds.num_layouts; + s.inds = (objcount_t) {}; + s.inds.num_layouts = num_layouts; + for (uint32_t i = 0; i < job->num_steps; i++) { + init_step (i, &jp, &s); + } + for (uint32_t i = 0; i < job->num_dsmanagers; i++) { + auto layoutInfo = &jobinfo->dslayouts[i]; + job->dsmanager[i] = QFV_DSManager_Create (layoutInfo, 16, ctx); + } +} + +static void +create_step_render_objects (uint32_t index, const qfv_stepinfo_t *step, + objstate_t *s) +{ + __auto_type rinfo = step->render; + if (!rinfo) { + return; + } + for (uint32_t i = 0; i < rinfo->num_renderpasses; i++) { + s->ptr.pl_counts[s->inds.num_renderpasses] = 0; + init_rpCreate (i, rinfo, s); + s->inds.num_renderpasses++; + } +} + +static void +create_step_compute_objects (uint32_t index, const qfv_stepinfo_t *step, + objstate_t *s) +{ + __auto_type cinfo = step->compute; + if (!cinfo) { + return; + } + + uint32_t base = s->inds.num_graph_pipelines; + for (uint32_t i = 0; i < cinfo->num_pipelines; i++) { + __auto_type pli = &cinfo->pipelines[i]; + __auto_type plc = &s->ptr.cplCreate[s->inds.num_comp_pipelines]; + __auto_type li = find_layout (&pli->layout, s); + s->ptr.plName[base + s->inds.num_comp_pipelines] = pli->name; + *plc = (VkComputePipelineCreateInfo) { + .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, + .flags = pli->flags, + .stage = *pli->compute_stage, + .layout = li->layout, + }; + plc->stage.stage = VK_SHADER_STAGE_COMPUTE_BIT; + s->inds.num_comp_pipelines++; + s->inds.num_ds_indices += li->num_sets; + } +} + +static void +create_step_process_objects (uint32_t index, const qfv_stepinfo_t *step, + objstate_t *s) +{ + __auto_type pinfo = step->process; + if (!pinfo) { + return; + } + // nothing to create at this stage +} + +static void +create_objects (vulkan_ctx_t *ctx, objcount_t *counts) +{ + __auto_type rctx = ctx->render_context; + __auto_type jinfo = rctx->jobinfo; + + VkRenderPass renderpasses[counts->num_renderpasses]; + VkPipeline pipelines[counts->num_graph_pipelines + + counts->num_comp_pipelines]; + VkRenderPassCreateInfo rpCreate[counts->num_renderpasses]; + VkAttachmentDescription attach[counts->num_attachments]; + VkClearValue clear[counts->num_attachments]; + VkSubpassDescription subpass[counts->num_subpasses]; + VkSubpassDependency depend[counts->num_dependencies]; + VkAttachmentReference attachref[counts->num_attachmentrefs]; + VkPipelineColorBlendAttachmentState cbAttach[counts->num_colorblend]; + uint32_t preserve[counts->num_preserve]; + const char *rpName[counts->num_renderpasses]; + const char *plName[counts->num_graph_pipelines + + counts->num_comp_pipelines]; + VkComputePipelineCreateInfo cplCreate[counts->num_comp_pipelines]; + VkGraphicsPipelineCreateInfo gplCreate[counts->num_graph_pipelines]; + VkPipelineColorBlendStateCreateInfo cbState[counts->num_graph_pipelines]; + qfv_layoutinfo_t layouts[counts->num_graph_pipelines + + counts->num_comp_pipelines]; + uint32_t pl_counts[counts->num_renderpasses]; + + exprctx_t ectx = { .hashctx = &ctx->script_context->hashctx }; + objstate_t s = { + .ptr = { + .rpCreate = rpCreate, + .attach = attach, + .clear = clear, + .subpass = subpass, + .depend = depend, + .attachref = attachref, + .cbAttach = cbAttach, + .preserve = preserve, + .rpName = rpName, + .plName = plName, + .cplCreate = cplCreate, + .gplCreate = gplCreate, + .cbState = cbState, + .layouts = layouts, + .pl_counts = pl_counts, + .rp = renderpasses, + .gpl = pipelines, + .cpl = pipelines + counts->num_graph_pipelines, + }, + .ctx = ctx, + .jinfo = jinfo, + .symtab = QFV_CreateSymtab (jinfo->plitem, "properties", 0, 0, &ectx), + }; + for (uint32_t i = 0; i < jinfo->num_steps; i++) { + create_step_render_objects (i, &jinfo->steps[i], &s); + } + for (uint32_t i = 0; i < jinfo->num_steps; i++) { + create_step_compute_objects (i, &jinfo->steps[i], &s); + } + for (uint32_t i = 0; i < jinfo->num_steps; i++) { + create_step_process_objects (i, &jinfo->steps[i], &s); + } + if (s.inds.num_renderpasses != counts->num_renderpasses + || s.inds.num_attachments != counts->num_attachments + || s.inds.num_subpasses != counts->num_subpasses + || s.inds.num_dependencies != counts->num_dependencies + || s.inds.num_attachmentrefs != counts->num_attachmentrefs + || s.inds.num_colorblend != counts->num_colorblend + || s.inds.num_preserve != counts->num_preserve + || s.inds.num_graph_pipelines != counts->num_graph_pipelines + || s.inds.num_comp_pipelines != counts->num_comp_pipelines) { + Sys_Error ("create_objects: something was missed"); + } + + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + uint32_t plInd = 0; + for (uint32_t i = 0; i < counts->num_renderpasses; i++) { + dfunc->vkCreateRenderPass (device->dev, &s.ptr.rpCreate[i], 0, + &renderpasses[i]); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_RENDER_PASS, + renderpasses[i], + va (ctx->va_ctx, "renderpass:%s", rpName[i])); + for (uint32_t j = 0; j < pl_counts[i]; j++) { + s.ptr.gplCreate[plInd++].renderPass = renderpasses[i]; + } + } + if (s.inds.num_graph_pipelines) { + dfunc->vkCreateGraphicsPipelines (device->dev, 0, + s.inds.num_graph_pipelines, + s.ptr.gplCreate, 0, pipelines); + } + if (s.inds.num_comp_pipelines) { + __auto_type p = &pipelines[s.inds.num_graph_pipelines]; + dfunc->vkCreateComputePipelines (device->dev, 0, + s.inds.num_comp_pipelines, + s.ptr.cplCreate, 0, p); + } + for (uint32_t i = 0; + i < s.inds.num_graph_pipelines + s.inds.num_comp_pipelines; i++) { + QFV_duSetObjectName (device, VK_OBJECT_TYPE_PIPELINE, pipelines[i], + va (ctx->va_ctx, "pipeline:%s", plName[i])); + } + + counts->num_ds_indices = s.inds.num_ds_indices; + init_job (ctx, counts, s); +} + +void +QFV_BuildRender (vulkan_ctx_t *ctx) +{ + __auto_type rctx = ctx->render_context; + + objcount_t counts = {}; + count_stuff (rctx->jobinfo, &counts); + + create_objects (ctx, &counts); + create_resources (ctx, &counts); +} diff --git a/libs/video/renderer/vulkan/resource.c b/libs/video/renderer/vulkan/resource.c new file mode 100644 index 000000000..9641e160d --- /dev/null +++ b/libs/video/renderer/vulkan/resource.c @@ -0,0 +1,350 @@ +/* + resource.c + + Vulkan resource functions + + Copyright (C) 2022 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/mathlib.h" +#include "QF/va.h" + +#include "QF/Vulkan/buffer.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/image.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/resource.h" + +static void +create_image (qfv_device_t *device, qfv_resobj_t *image_obj) +{ + qfv_devfuncs_t *dfunc = device->funcs; + __auto_type image = &image_obj->image; + if (image->image) { + // the image was created externally and is being + return; + } + VkImageCreateInfo createInfo = { + VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, 0, + .flags = image->flags, + .imageType = image->type, + .format = image->format, + .extent = image->extent, + .mipLevels = image->num_mipmaps, + .arrayLayers = image->num_layers, + .samples = image->samples, + .tiling = image->tiling, + .usage = image->usage, + .sharingMode = image->sharing, + .queueFamilyIndexCount = image->num_queue_inds, + .pQueueFamilyIndices = image->queue_inds, + .initialLayout = image->initialLayout, + }; + dfunc->vkCreateImage (device->dev, &createInfo, 0, &image->image); +} + +static void +create_image_view (qfv_device_t *device, qfv_resobj_t *imgview_obj, + qfv_resobj_t *imgobj) +{ + qfv_devfuncs_t *dfunc = device->funcs; + __auto_type view = &imgview_obj->image_view; + VkImage image = view->external_image ? view->external_image + : imgobj->image.image; + + VkImageViewCreateInfo createInfo = { + VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 0, + .flags = view->flags, + .image = image, + .viewType = view->type, + .format = view->format, + .components = view->components, + .subresourceRange = view->subresourceRange, + }; + dfunc->vkCreateImageView (device->dev, &createInfo, 0, &view->view); +} + +int +QFV_CreateResource (qfv_device_t *device, qfv_resource_t *resource) +{ + qfv_devfuncs_t *dfunc = device->funcs; + qfv_physdev_t *physdev = device->physDev; + size_t atom = physdev->properties->limits.nonCoherentAtomSize; + VkPhysicalDeviceMemoryProperties *memprops = &physdev->memory_properties; + VkMemoryRequirements req; + VkDeviceSize size = 0; + + if (!(resource->memory_properties + & (VK_MEMORY_PROPERTY_HOST_CACHED_BIT + | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT + | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT))) { + // if the memory isn't host visible then there's no need to worry about + // alignment with nonCoherentAtomSize + atom = 0; + } + for (unsigned i = 0; i < resource->num_objects; i++) { + __auto_type obj = &resource->objects[i]; + switch (obj->type) { + case qfv_res_buffer: + { + __auto_type buffer = &obj->buffer; + buffer->buffer = QFV_CreateBuffer (device, + buffer->size, + buffer->usage); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER, + buffer->buffer, + va (resource->va_ctx, "buffer:%s:%s", + resource->name, obj->name)); + dfunc->vkGetBufferMemoryRequirements (device->dev, + buffer->buffer, &req); + } + break; + case qfv_res_buffer_view: + { + __auto_type buffview = &obj->buffer_view; + __auto_type buffobj = &resource->objects[buffview->buffer]; + if (buffview->buffer >= resource->num_objects + || buffobj->type != qfv_res_buffer) { + Sys_Error ("%s:%s invalid buffer for view", + resource->name, obj->name); + } + } + break; + case qfv_res_image: + { + create_image (device, obj); + __auto_type image = &obj->image; + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE, + image->image, + va (resource->va_ctx, "image:%s:%s", + resource->name, obj->name)); + dfunc->vkGetImageMemoryRequirements (device->dev, + image->image, &req); + } + break; + case qfv_res_image_view: + { + __auto_type imgview = &obj->image_view; + __auto_type imgobj = &resource->objects[imgview->image]; + if (!imgview->external_image + && (imgview->image >= resource->num_objects + || imgobj->type != qfv_res_image)) { + Sys_Error ("%s:%s invalid image for view", + resource->name, obj->name); + } + } + break; + default: + Sys_Error ("%s:%s invalid resource type %d", + resource->name, obj->name, obj->type); + } + req.alignment = max (req.alignment, atom); + size = QFV_NextOffset (size, &req); + size += QFV_NextOffset (req.size, &req); + } + VkMemoryPropertyFlags properties = resource->memory_properties; + for (uint32_t type = 0; type < memprops->memoryTypeCount; type++) { + if ((req.memoryTypeBits & (1 << type)) + && ((memprops->memoryTypes[type].propertyFlags & properties) + == properties)) { + VkMemoryAllocateInfo allocate_info = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = size, + .memoryTypeIndex = type, + }; + VkResult res = dfunc->vkAllocateMemory (device->dev, &allocate_info, + 0, &resource->memory); + if (res == VK_SUCCESS) { + break; + } + } + } + resource->size = size; + QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, + resource->memory, va (resource->va_ctx, "memory:%s", + resource->name)); + + VkDeviceSize offset = 0; + for (unsigned i = 0; i < resource->num_objects; i++) { + __auto_type obj = &resource->objects[i]; + switch (obj->type) { + case qfv_res_buffer: + { + __auto_type buffer = &obj->buffer; + dfunc->vkGetBufferMemoryRequirements (device->dev, + buffer->buffer, &req); + } + break; + case qfv_res_image: + { + __auto_type image = &obj->image; + dfunc->vkGetImageMemoryRequirements (device->dev, + image->image, &req); + } + break; + case qfv_res_buffer_view: + case qfv_res_image_view: + break; + } + + req.alignment = max (req.alignment, atom); + offset = QFV_NextOffset (offset, &req); + switch (obj->type) { + case qfv_res_buffer: + { + __auto_type buffer = &obj->buffer; + QFV_BindBufferMemory (device, buffer->buffer, + resource->memory, offset); + buffer->offset = offset; + } + break; + case qfv_res_image: + { + __auto_type image = &obj->image; + QFV_BindImageMemory (device, image->image, + resource->memory, offset); + image->offset = offset; + } + break; + case qfv_res_buffer_view: + case qfv_res_image_view: + break; + } + req.alignment = max (req.alignment, atom); + offset = QFV_NextOffset (offset, &req); + offset += QFV_NextOffset (req.size, &req); + } + + for (unsigned i = 0; i < resource->num_objects; i++) { + __auto_type obj = &resource->objects[i]; + switch (obj->type) { + case qfv_res_buffer: + case qfv_res_image: + break; + case qfv_res_buffer_view: + { + __auto_type buffview = &obj->buffer_view; + __auto_type buffobj = &resource->objects[buffview->buffer]; + __auto_type buffer = &buffobj->buffer; + buffview->view = QFV_CreateBufferView (device, + buffer->buffer, + buffview->format, + buffview->offset, + buffview->size); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER_VIEW, + buffview->view, + va (resource->va_ctx, "bview:%s:%s", + resource->name, obj->name)); + } + break; + case qfv_res_image_view: + { + __auto_type imgview = &obj->image_view; + __auto_type imgobj = &resource->objects[imgview->image]; + create_image_view (device, obj, imgobj); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE_VIEW, + imgview->view, + va (resource->va_ctx, "iview:%s:%s", + resource->name, obj->name)); + } + break; + } + } + return 0; +} + +void +QFV_DestroyResource (qfv_device_t *device, qfv_resource_t *resource) +{ + qfv_devfuncs_t *dfunc = device->funcs; + + for (unsigned i = 0; i < resource->num_objects; i++) { + __auto_type obj = &resource->objects[i]; + switch (obj->type) { + case qfv_res_buffer: + case qfv_res_image: + break; + case qfv_res_buffer_view: + { + __auto_type buffview = &obj->buffer_view; + dfunc->vkDestroyBufferView (device->dev, buffview->view, 0); + } + break; + case qfv_res_image_view: + { + __auto_type imgview = &obj->image_view; + dfunc->vkDestroyImageView (device->dev, imgview->view, 0); + } + break; + } + } + for (unsigned i = 0; i < resource->num_objects; i++) { + __auto_type obj = &resource->objects[i]; + switch (obj->type) { + case qfv_res_buffer: + { + __auto_type buffer = &obj->buffer; + dfunc->vkDestroyBuffer (device->dev, buffer->buffer, 0); + } + break; + case qfv_res_image: + { + __auto_type image = &obj->image; + dfunc->vkDestroyImage (device->dev, image->image, 0); + } + break; + case qfv_res_buffer_view: + case qfv_res_image_view: + break; + } + } + dfunc->vkFreeMemory (device->dev, resource->memory, 0); +} + +void +QFV_ResourceInitTexImage (qfv_resobj_t *image, const char *name, + int mips, const tex_t *tex) +{ + *image = (qfv_resobj_t) { + .name = name, + .type = qfv_res_image, + .image = { + .type = VK_IMAGE_TYPE_2D, + .format = QFV_ImageFormat (tex->format, 0), + .extent = { + .width = tex->width, + .height = tex->height, + .depth = 1, + }, + .num_mipmaps = mips ? QFV_MipLevels (tex->width, tex->height) : 1, + .num_layers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT + | VK_IMAGE_USAGE_TRANSFER_SRC_BIT + | VK_IMAGE_USAGE_SAMPLED_BIT, + }, + }; +} diff --git a/libs/video/renderer/vulkan/rp_main_def.plist b/libs/video/renderer/vulkan/rp_main_def.plist new file mode 100644 index 000000000..b4ba6d4c3 --- /dev/null +++ b/libs/video/renderer/vulkan/rp_main_def.plist @@ -0,0 +1,1693 @@ +properties = { + color = { + bsp = "[0.0, 0.5, 0.6, 1]"; + alias = "[0.6, 0.5, 0.0, 1]"; + iqm = "[0.6, 0.5, 0.0, 1]"; + sprite = "[0.6, 0.5, 0.0, 1]"; + particles = "[0.4, 0.5, 0.8, 1]"; + lights = "[0.8, 0.6, 0.2, 1]"; + compose = "[0.7, 0.7, 0.7, 1]"; + + output = "[0.0, 0.7, 0.7, 1]"; + waterwarp = "[0.0, 0.7, 0.7, 1]"; + fisheye = "[0.0, 0.7, 0.7, 1]"; + slice = "[0.8, 0.7, 0.2, 1]"; + lines = "[0.8, 0.7, 0.4, 1]"; + }; + color_dependency = { + src = { + stage = color_attachment_output; + access = color_attachment_write; + }; + dst = { + stage = fragment_shader; + access = input_attachment_read; + }; + flags = by_region; + }; + depth_dependency = { + src = { + stage = late_fragment_tests; + access = depth_stencil_attachment_write; + }; + dst = { + stage = fragment_shader|early_fragment_tests; + access = input_attachment_read|depth_stencil_attachment_read; + }; + flags = by_region; + }; + image_base = { + imageType = `2d; + samples = 1; + extent = { + width = $render_output.extent.width; + height = $render_output.extent.height; + depth = 1; + }; + mipLevels = 1; + arrayLayers = 1; + tiling = optimal; + usage = color_attachment|input_attachment|transient_attachment; + initialLayout = undefined; + }; + cube_image_base = { + @inherit = $image_base; + extent = { + width = "min($render_output.extent.width,$render_output.extent.height)"; + height = "min($render_output.extent.width,$render_output.extent.height)"; + depth = 1; + }; + arrayLayers = 6; + }; + view_base = { + viewType = `2d; + components = { + r = identity; + g = identity; + b = identity; + a = identity; + }; + subresourceRange = { + aspectMask = color; + levelCount = 1; + layerCount = 1; + }; + }; + cube_view_base = { + @inherit = $view_base; + viewType = `2d_array; + subresourceRange = { + layerCount = 6; + }; + }; + attachment_base = { + samples = 1; + loadOp = dont_care; + storeOp = dont_care; + stencilLoadOp = dont_care; + stencilStoreOp = dont_care; + initialLayout = undefined; + finalLayout = color_attachment_optimal; + clearValue = { color = "[0, 0, 0, 1]"; }; + }; + + cw_cull_back = { + depthClampEnable = false; + rasterizerDiscardEnable = false; + polygonMode = fill; + cullMode = back; + frontFace = clockwise; + depthBiasEnable = false; + lineWidth = 1; + }; + counter_cw_cull_back = { + depthClampEnable = false; + rasterizerDiscardEnable = false; + polygonMode = fill; + cullMode = back; + frontFace = counter_clockwise; + depthBiasEnable = false; + lineWidth = 1; + }; + depth_test_and_write = { + depthTestEnable = true; + depthWriteEnable = true; + depthCompareOp = less_or_equal; + depthBoundsTestEnable = false; + stencilTestEnable = false; + }; + depth_test_only = { + depthTestEnable = true; + depthWriteEnable = false; + depthCompareOp = less_or_equal; + depthBoundsTestEnable = false; + stencilTestEnable = false; + }; + depth_disable = { + depthTestEnable = false; + depthWriteEnable = false; + depthCompareOp = less_or_equal; + depthBoundsTestEnable = false; + stencilTestEnable = false; + }; + blend_disable = { + blendEnable = false; + srcColorBlendFactor = src_alpha; + dstColorBlendFactor = one_minus_src_alpha; + colorBlendOp = add; + srcAlphaBlendFactor = src_alpha; + dstAlphaBlendFactor = one_minus_src_alpha; + alphaBlendOp = add; + colorWriteMask = r|g|b|a; + }; + alpha_blend = { + blendEnable = true; + srcColorBlendFactor = one; + dstColorBlendFactor = one_minus_src_alpha; + colorBlendOp = add; + srcAlphaBlendFactor = one; + dstAlphaBlendFactor = one_minus_src_alpha; + alphaBlendOp = add; + colorWriteMask = r|g|b|a; + }; + pipeline_base = { + viewport = { + viewports = ( + { + x = 0; y = 0; + width = 640; height = 480; + minDepth = 0; maxDepth = 1; + } + ); + scissors = ( + { + offset = { x = 0; y = 0; }; + extent = { width = 640; height = 480; }; + }, + ); + }; + rasterization = $cw_cull_back; + multisample = { + rasterizationSamples = $msaaSamples; + sampleShadingEnable = false; + minSampleShading = 0.5f; + alphaToCoverageEnable = false; + alphaToOneEnable = false; + }; + depthStencil = $depth_test_only; + colorBlend = { + logicOpEnable = false; + }; + dynamic = { + dynamicState = ( viewport, scissor ); + }; + }; + compose_base = { + @inherit = $pipeline_base; + rasterization = $counter_cw_cull_back; + vertexInput = { + bindings = (); + attributes = (); + }; + inputAssembly = { + topology = triangle_list; + primitiveRestartEnable = false; + }; + }; + + brush = { + shader = { + depth_vertex = { + stage = vertex; + name = main; + module = $builtin/bsp_depth.vert; + }; + gbuf_vertex = { + stage = vertex; + name = main; + module = $builtin/bsp_gbuf.vert; + }; + gbuf_geometry = { + stage = geometry; + name = main; + module = $builtin/bsp_gbuf.geom; + }; + gbuf_fragment = { + stage = fragment; + name = main; + module = $builtin/bsp_gbuf.frag; + }; + quake_vertex = { + stage = vertex; + name = main; + module = $builtin/quakebsp.vert; + }; + sky_specialization = { + mapEntries = ( + // doSkyBox + { size = 4; offset = 0; constantID = 0; }, + // doSkySheet + { size = 4; offset = 4; constantID = 1; }, + ); + }; + skybox_fragment = { + stage = fragment; + name = main; + module = $builtin/bsp_sky.frag; + specializationInfo = { + @inherit = $brush.shader.sky_specialization; + // doSkyBox, doSkySheet + data = "array(1, 0)"; + }; + }; + skysheet_fragment = { + stage = fragment; + name = main; + module = $builtin/bsp_sky.frag; + specializationInfo = { + @inherit = $brush.shader.sky_specialization; + // doSkyBox, doSkySheet + data = "array(0, 1)"; + }; + }; + turb_fragment = { + stage = fragment; + name = main; + module = $builtin/bsp_turb.frag; + }; + }; + vertexInput = { + bindings = ( + { binding = 0; stride = "2 * 4 * 4"; inputRate = vertex; }, + { binding = 1; stride = "4"; inputRate = instance; }, + ); + attributes = ( + { location = 0; binding = 0; format = r32g32b32a32_sfloat; offset = 0; }, + { location = 1; binding = 0; format = r32g32b32a32_sfloat; offset = 16; }, + { location = 2; binding = 1; format = r32_uint; offset = 0; }, + ); + }; + inputAssembly = { + topology = triangle_fan; + primitiveRestartEnable = true; + }; + layout = { + descriptorSets = (matrix_set, entity_set, oit_set, + // surface skybox + texture_set, texture_set); + pushConstants = { + fragment = { + fog = vec4; + time = float; + alpha = float; + turb_scale = float; + }; + }; + }; + }; + alias = { + shader = { + depth_vertex = { + stage = vertex; + name = main; + module = $builtin/alias_depth.vert; + }; + gbuf_vertex = { + stage = vertex; + name = main; + module = $builtin/alias.vert; + }; + gbuf_fragment = { + stage = fragment; + name = main; + module = $builtin/alias_gbuf.frag; + }; + }; + vertexInput = { + bindings = ( + { binding = 0; stride = "2 * 4 * 4"; inputRate = vertex; }, + { binding = 1; stride = "2 * 4 * 4"; inputRate = vertex; }, + { binding = 2; stride = "2 * 4"; inputRate = vertex; }, + ); + attributes = ( + { location = 0; binding = 0; format = r32g32b32a32_sfloat; offset = 0; }, + { location = 1; binding = 0; format = r32g32b32a32_sfloat; offset = 16; }, + { location = 2; binding = 1; format = r32g32b32a32_sfloat; offset = 0; }, + { location = 3; binding = 1; format = r32g32b32a32_sfloat; offset = 16; }, + { location = 4; binding = 2; format = r32g32_sfloat; offset = 0; }, + ); + }; + inputAssembly = { + topology = triangle_list; + primitiveRestartEnable = false; + }; + layout = { + // palette skin + descriptorSets = (matrix_set, texture_set, texture_set); + pushConstants = { + vertex = { Model = mat4; blend = float; }; + fragment = { colors = uint; base_color = vec4; fog = vec4; }; + }; + }; + }; + iqm = { + shader = { + specialization = { + mapEntries = ( + // IQMDepthOnly + { size = 4; offset = 0; constantID = 0; }, + ); + }; + depth_vertex = { + stage = vertex; + name = main; + module = $builtin/iqm.vert; + specializationInfo = { + @inherit = $iqm.shader.specialization; + // IQMDepthOnly + data = "array(1)"; + }; + }; + gbuf_vertex = { + stage = vertex; + name = main; + module = $builtin/iqm.vert; + specializationInfo = { + @inherit = $iqm.shader.specialization; + // IQMDepthOnly + data = "array(0)"; + }; + }; + gbuf_fragment = { + stage = fragment; + name = main; + module = $builtin/iqm.frag; + }; + }; + vertexInput = { + bindings = ( + { binding = 0; stride = 20; inputRate = vertex; }, + { binding = 1; stride = 40; inputRate = vertex; }, + ); + attributes = ( + { location = 0; binding = 0; format = r32g32b32_sfloat; offset = 0; }, // position + { location = 1; binding = 0; format = r8g8b8a8_uint; offset = 12; }, // bonindices + { location = 2; binding = 0; format = r8g8b8a8_unorm; offset = 16; }, // boneweights + + { location = 3; binding = 1; format = r32g32_sfloat; offset = 0; }, // texcoord + { location = 4; binding = 1; format = r32g32b32_sfloat; offset = 8; }, // normal + { location = 5; binding = 1; format = r32g32b32a32_sfloat; offset = 20; }, // tangent + { location = 6; binding = 1; format = r8g8b8a8_unorm; offset = 36; }, // color + + ); + }; + inputAssembly = { + topology = triangle_list; + primitiveRestartEnable = false; + }; + layout = { + // skin + descriptorSets = (matrix_set, texture_set, bone_set); + pushConstants = { + vertex = { + Model = mat4; + blend = float; + }; + fragment = { + colorA = uint; + colorB = uint; + base_color = vec4; + fog = vec4; + }; + }; + }; + }; + sprite = { + shader = { + depth_vertex = { + stage = vertex; + name = main; + module = $builtin/sprite_depth.vert; + }; + depth_fragment = { + stage = fragment; + name = main; + module = $builtin/sprite_depth.frag; + }; + gbuf_vertex = { + stage = vertex; + name = main; + module = $builtin/sprite_gbuf.vert; + }; + gbuf_fragment = { + stage = fragment; + name = main; + module = $builtin/sprite_gbuf.frag; + }; + }; + vertexInput = { + bindings = (); + attributes = (); + }; + inputAssembly = { + topology = triangle_strip; + // never draw more than 4 verts + primitiveRestartEnable = false; + }; + layout = { + descriptorSets = (matrix_set, sprite_set); + pushConstants = { + vertex = { + Model = mat4; + frame = int; + }; + fragment = { + overlap = { offset = 64; type = int; }; + frame = int; + spriteind = int; + fog = vec4; + }; + }; + }; + }; + particle = { + shader = { + vertex = { + stage = vertex; + name = main; + module = $builtin/particle.vert; + }; + geometry = { + stage = geometry; + name = main; + module = $builtin/particle.geom; + }; + fragment = { + stage = fragment; + name = main; + module = $builtin/particle.frag; + }; + }; + vertexInput = { + bindings = ( + { binding = 0; stride = "4 * 4 * 4"; inputRate = instance; }, + ); + attributes = ( + { location = 0; binding = 0; format = r32g32b32a32_sfloat; offset = 0; }, + { location = 1; binding = 0; format = r32g32b32a32_sfloat; offset = 16; }, + { location = 2; binding = 0; format = r32g32b32a32_sfloat; offset = 32; }, + { location = 3; binding = 0; format = r32g32b32a32_sfloat; offset = 48; }, + ); + }; + inputAssembly = { + topology = point_list; + primitiveRestartEnable = false; + }; + layout = { + draw = { + // palette + descriptorSets = (matrix_set, texture_set, oit_set); + pushConstants = { + vertex = { Model = mat4; }; + }; + }; + physics = { + descriptorSets = (particle_set); + pushConstants = { + compute = { gravity = vec4; dT = float; }; + }; + }; + update = { + // cur in new + descriptorSets = (particle_set, particle_set, particle_set); + }; + }; + }; + fstriangle = { + shader = { + vertex = { + stage = vertex; + name = main; + module = $builtin/fstriangle.vert; + }; + vertexst = { + stage = vertex; + name = main; + module = $builtin/fstrianglest.vert; + }; + }; + }; + lighting = { + shader = { + fragment = { + stage = fragment; + name = main; + module = $builtin/lighting.frag; + }; + }; + layout = { + descriptorSets = (lighting_attach, lighting_lights, + lighting_shadow); + }; + }; + compose = { + shader = { + fragment = { + stage = fragment; + name = main; + module = $builtin/compose.frag; + }; + }; + layout = { + descriptorSets = (compose_attach, oit_set); + }; + }; + output = { + @inherit = $compose_base; + shader = { + fragment = { + stage = fragment; + name = main; + module = $builtin/output.frag; + }; + }; + layout = { + descriptorSets = (matrix_set, output_set); + }; + }; + waterwarp = { + @inherit = $compose_base; + shader = { + fragment = { + stage = fragment; + name = main; + module = $builtin/waterwarp.frag; + }; + }; + layout = { + descriptorSets = (matrix_set, output_set); + pushConstants = { + fragment = { time = float; }; + }; + }; + }; + fisheye = { + @inherit = $compose_base; + shader = { + fragment = { + stage = fragment; + name = main; + module = $builtin/fisheye.frag; + }; + }; + layout = { + descriptorSets = (matrix_set, output_set); + pushConstants = { + fragment = { fov = float; aspect = float; }; + }; + }; + }; + slice = { + shader = { + vertex = { + stage = vertex; + name = main; + module = $builtin/slice.vert; + }; + fragment = { + stage = fragment; + name = main; + module = $builtin/twod.frag; + }; + }; + vertexInput = { + bindings = ( + { binding = 0; stride = "4 + 4 + 4*4"; inputRate = instance; }, + ); + attributes = ( + // 9-slice index + { location = 0; binding = 0; format = r32_uint; offset = 0; }, + // 9-slice color + { location = 1; binding = 0; format = r8g8b8a8_unorm; offset = 4; }, + // 9-slice position (2d) + { location = 2; binding = 0; format = r32g32_sfloat; offset = 8; }, + // 9-slice size delta (2d) + { location = 3; binding = 0; format = r32g32_sfloat; offset = 16; }, + ); + }; + inputAssembly = { + topology = triangle_strip; + primitiveRestartEnable = true; + }; + layout = { + descriptorSets = (matrix_set, quad_data_set); + }; + }; + lines = { + shader = { + vertex = { + stage = vertex; + name = main; + module = $builtin/line.vert; + }; + fragment = { + stage = fragment; + name = main; + module = $builtin/line.frag; + }; + }; + vertexInput = { + bindings = ( + { binding = 0; stride = "2 * 4 + 4"; inputRate = vertex; }, + ); + attributes = ( + { location = 0; binding = 0; format = r32g32_sfloat; offset = 0; }, + { location = 1; binding = 0; format = r8g8b8a8_unorm; offset = 4; }, + ); + }; + inputAssembly = { + topology = line_list; + primitiveRestartEnable = false; + }; + layout = { + descriptorSets = (matrix_set); + }; + }; +}; +descriptorSetLayouts = { + matrix_set = { + bindings = ( + { + binding = 0; + descriptorType = uniform_buffer; + descriptorCount = 1; + stageFlags = vertex|geometry|fragment; + }, + ); + }; + quad_data_set = { + bindings = ( + { + // glyph texture data + binding = 0; + descriptorType = combined_image_sampler; + descriptorCount = 1; + stageFlags = fragment; + }, + { + // glyph geometry data (offset and uv) + binding = 1; + descriptorType = uniform_texel_buffer; + descriptorCount = 1; + stageFlags = vertex; + }, + ); + }; + texture_set = { + bindings = ( + { + binding = 0; + descriptorType = combined_image_sampler; + descriptorCount = 1; + stageFlags = fragment|vertex; + }, + ); + }; + oit_set = { + bindings = ( + { + binding = 0; + descriptorType = storage_buffer; + descriptorCount = 1; + stageFlags = fragment; + }, + { + binding = 1; + descriptorType = storage_buffer; + descriptorCount = 1; + stageFlags = fragment; + }, + { + binding = 2; + descriptorType = storage_image; + descriptorCount = 1; + stageFlags = fragment; + }, + ); + }; + entity_set = { + bindings = ( + { + binding = 0; + descriptorType = storage_buffer; + descriptorCount = 1; + stageFlags = vertex; + }, + ); + }; + bone_set = { + bindings = ( + { + binding = 0; + descriptorType = storage_buffer; + descriptorCount = 1; + stageFlags = vertex; + }, + ); + }; + sprite_set = { + bindings = ( + { + binding = 0; + descriptorType = uniform_buffer; + descriptorCount = 1; + stageFlags = vertex; + }, + { + binding = 1; + descriptorType = combined_image_sampler; + descriptorCount = 1; + stageFlags = fragment; + }, + ); + }; + lighting_attach = { + bindings = ( + { + binding = 0; + descriptorType = input_attachment; + descriptorCount = 1; + stageFlags = fragment; + }, + { + binding = 1; + descriptorType = input_attachment; + descriptorCount = 1; + stageFlags = fragment; + }, + { + binding = 2; + descriptorType = input_attachment; + descriptorCount = 1; + stageFlags = fragment; + }, + { + binding = 3; + descriptorType = input_attachment; + descriptorCount = 1; + stageFlags = fragment; + }, + { + binding = 4; + descriptorType = input_attachment; + descriptorCount = 1; + stageFlags = fragment; + }, + ); + }; + lighting_lights = { + bindings = ( + { + binding = 0; + descriptorType = uniform_buffer; + descriptorCount = 1; + stageFlags = fragment; + }, + ); + }; + lighting_shadow = { + bindings = ( + { + binding = 0; + descriptorType = combined_image_sampler; + descriptorCount = 32; + stageFlags = fragment; + }, + ); + }; + compose_attach = { + bindings = ( + { + binding = 0; + descriptorType = input_attachment; + descriptorCount = 1; + stageFlags = fragment; + }, + ); + }; + particle_set = { + bindings = ( + { + binding = 0; + descriptorType = storage_buffer; + descriptorCount = 1; + stageFlags = compute; + }, + { + binding = 1; + descriptorType = storage_buffer; + descriptorCount = 1; + stageFlags = compute; + }, + { + binding = 2; + descriptorType = storage_buffer; + descriptorCount = 1; + stageFlags = compute; + }, + ); + }; + particle_set = { + bindings = ( + { + binding = 0; + descriptorType = storage_buffer; + descriptorCount = 1; + stageFlags = compute; + }, + { + binding = 1; + descriptorType = storage_buffer; + descriptorCount = 1; + stageFlags = compute; + }, + { + binding = 2; + descriptorType = storage_buffer; + descriptorCount = 1; + stageFlags = compute; + }, + ); + }; + output_set = { + bindings = ( + { + binding = 0; + descriptorType = combined_image_sampler; + descriptorCount = 1; + stageFlags = fragment; + }, + ); + }; +}; +images = { + depth = { + @inherit = $image_base; + format = x8_d24_unorm_pack32; + usage = depth_stencil_attachment|input_attachment|transient_attachment; + }; + color = { + @inherit = $image_base; + format = r8g8b8a8_unorm; + }; + emission = { + @inherit = $image_base; + format = r16g16b16a16_sfloat; + }; + normal = { + @inherit = $image_base; + format = r16g16b16a16_sfloat; + }; + position = { + @inherit = $image_base; + format = r32g32b32a32_sfloat; + }; + opaque = { + @inherit = $image_base; + format = r16g16b16a16_sfloat; + }; + output = { + @inherit = $image_base; + usage = color_attachment|input_attachment|sampled; + format = $render_output.format; + }; + cube_depth = { + @inherit = $cube_image_base; + format = x8_d24_unorm_pack32; + usage = depth_stencil_attachment|input_attachment|transient_attachment; + }; + cube_color = { + @inherit = $cube_image_base; + format = r8g8b8a8_unorm; + }; + cube_emission = { + @inherit = $cube_image_base; + format = r16g16b16a16_sfloat; + }; + cube_normal = { + @inherit = $cube_image_base; + format = r16g16b16a16_sfloat; + }; + cube_position = { + @inherit = $cube_image_base; + format = r32g32b32a32_sfloat; + }; + cube_opaque = { + @inherit = $cube_image_base; + format = r16g16b16a16_sfloat; + }; + cube_output = { + @inherit = $cube_image_base; + flags = cube_compatible; + usage = color_attachment|input_attachment|sampled; + format = $render_output.format; + }; +}; +imageviews = { + depth = { + @inherit = $view_base; + image = depth; + format = $images.depth.format; + subresourceRange = { + aspectMask = depth; + }; + }; + color = { + @inherit = $view_base; + image = color; + format = $images.color.format; + }; + emission = { + @inherit = $view_base; + image = emission; + format = $images.emission.format; + }; + normal = { + @inherit = $view_base; + image = normal; + format = $images.normal.format; + }; + position = { + @inherit = $view_base; + image = position; + format = $images.position.format; + }; + opaque = { + @inherit = $view_base; + image = opaque; + format = $images.opaque.format; + }; + output = { + @inherit = $view_base; + image = output; + format = $render_output.format; + }; + cube_depth = { + @inherit = $cube_view_base; + image = cube_depth; + format = $images.cube_depth.format; + subresourceRange = { + aspectMask = depth; + }; + }; + cube_color = { + @inherit = $cube_view_base; + image = cube_color; + format = $images.cube_color.format; + }; + cube_emission = { + @inherit = $cube_view_base; + image = cube_emission; + format = $images.cube_emission.format; + }; + cube_normal = { + @inherit = $cube_view_base; + image = cube_normal; + format = $images.cube_normal.format; + }; + cube_position = { + @inherit = $cube_view_base; + image = cube_position; + format = $images.cube_position.format; + }; + cube_opaque = { + @inherit = $cube_view_base; + image = cube_opaque; + format = $images.cube_opaque.format; + }; + cube_output = { + @inherit = $cube_view_base; + viewType = cube; + image = cube_output; + format = $render_output.format; + }; +}; +output = { + view = $output; + format = r16g16b16a16_sfloat; + finalLayout = shader_read_only_optimal; +}; +renderpasses = { + deferred = { + color = "[0, 1, 0, 1]"; + framebuffer = { + width = $render_output.extent.width; + height = $render_output.extent.height; + layers = 1; + attachments = { + depth = { + @inherit = $attachment_base; + format = $images.depth.format; + loadOp = clear; + finalLayout = depth_stencil_attachment_optimal; + clearValue = { depthStencil = { depth = 1; stencil = 0; }; }; + view = depth; + }; + color = { + @inherit = $attachment_base; + format = $images.color.format; + loadOp = clear; + view = color; + }; + emission = { + @inherit = $attachment_base; + format = $images.emission.format; + loadOp = clear; + view = emission; + }; + normal = { + @inherit = $attachment_base; + format = $images.normal.format; + view = normal; + }; + position = { + @inherit = $attachment_base; + format = $images.position.format; + view = position; + }; + opaque = { + @inherit = $attachment_base; + format = $images.opaque.format; + view = opaque; + }; + output = { + @inherit = $attachment_base; + format = $render_output.format; + loadOp = clear; + storeOp = store; + finalLayout = $render_output.finalLayout; + view = output; + }; + }; + }; + subpasses = { + depth = { + color = "[ 0.5, 0.5, 0.5, 1]"; + attachments = { + depth = { + depth = depth_stencil_attachment_optimal; + }; + preserve = (color, emission, normal, position, output); + }; + + base_pipeline = { + @inherit = $pipeline_base; + depthStencil = $depth_test_and_write; + rasterization = $cw_cull_back; + }; + pipelines = { + bsp:depth = { + color = $color.bsp; + tasks = ( + { func = bsp_draw_queue; + params = (main, solid, 0); }, + { func = bsp_draw_queue; + params = (main, sky, 0); }, + ); + + stages = ( + $brush.shader.depth_vertex, + ); + vertexInput = { + bindings = ( + "$brush.vertexInput.bindings[0]", + "$brush.vertexInput.bindings[1]", + ); + attributes = ( + "$brush.vertexInput.attributes[0]", + "$brush.vertexInput.attributes[2]", + ); + }; + inputAssembly = $brush.inputAssembly; + layout = $brush.layout; + }; + alias:depth = { + color = $color.alias; + tasks = ( + { func = alias_draw; + params = (0); }, + ); + + stages = ( + $alias.shader.depth_vertex, + ); + vertexInput = { + // depth pass doesn't use UVs + bindings = ( + "$alias.vertexInput.bindings[0]", + "$alias.vertexInput.bindings[1]", + ); + attributes = ( + "$alias.vertexInput.attributes[0]", + "$alias.vertexInput.attributes[1]", + "$alias.vertexInput.attributes[2]", + "$alias.vertexInput.attributes[3]", + ); + }; + inputAssembly = $alias.inputAssembly; + layout = $alias.layout; + }; + iqm:depth = { + color = $color.iqm; + tasks = ( + { func = iqm_draw; + params = (0); }, + ); + + stages = ( + $iqm.shader.depth_vertex, + ); + vertexInput = $iqm.vertexInput; + inputAssembly = $iqm.inputAssembly; + layout = $iqm.layout; + }; + sprite:depth = { + color = $color.sprite; + tasks = ( + { func = sprite_draw; }, + ); + + stages = ( + $sprite.shader.depth_vertex, + $sprite.shader.depth_fragment, + ); + vertexInput = $sprite.vertexInput; + inputAssembly = $sprite.inputAssembly; + layout = $sprite.layout; + }; + }; + }; + translucent = { + color = "[ 0.25, 0.25, 0.6, 1]"; + dependencies = { + depth = $depth_dependency; + }; + attachments = { + depth = { + depth = depth_stencil_read_only_optimal; + }; + preserve = (color, emission, normal, position, output); + }; + base_pipeline = { + @inherit = $pipeline_base; + rasterization = $cw_cull_back; + }; + pipelines = { + bsp:skybox = { + color = $color.bsp; + tasks = ( + // FIXME sky should not use OIT + { func = bsp_draw_queue; + params = (main, sky, 1); }, + ); + + stages = ( + $brush.shader.quake_vertex, + $brush.shader.skybox_fragment, + ); + vertexInput = $brush.vertexInput; + inputAssembly = $brush.inputAssembly; + layout = $brush.layout; + }; + bsp:skysheet = { + color = $color.bsp; + tasks = ( + // FIXME sky should not use OIT + { func = bsp_draw_queue; + params = (main, sky, 1); }, + ); + + stages = ( + $brush.shader.quake_vertex, + $brush.shader.skysheet_fragment, + ); + vertexInput = $brush.vertexInput; + inputAssembly = $brush.inputAssembly; + layout = $brush.layout; + }; + bsp:trans = { + color = $color.bsp; + tasks = ( + { func = bsp_draw_queue; + params = (main, translucent, 2); }, + { func = bsp_draw_queue; + params = (main, turbulent, 2); }, + ); + + stages = ( + $brush.shader.quake_vertex, + $brush.shader.turb_fragment, + ); + vertexInput = $brush.vertexInput; + inputAssembly = $brush.inputAssembly; + layout = $brush.layout; + }; + particles:trans = { + color = $color.particles; + tasks = ( + { func = particles_draw; }, + ); + + stages = ( + $particle.shader.vertex, + $particle.shader.geometry, + $particle.shader.fragment, + ); + vertexInput = $particle.vertexInput; + inputAssembly = $particle.inputAssembly; + layout = $particle.layout.draw; + }; + }; + }; + gbuffer = { + color = "[ 0.3, 0.7, 0.3, 1]"; + dependencies = { + depth = $depth_dependency; + }; + attachments = { + color = { + color = { + layout = color_attachment_optimal; + blend = $blend_disable; + }; + emission = { + layout = color_attachment_optimal; + blend = $blend_disable; + }; + normal = { + layout = color_attachment_optimal; + blend = $blend_disable; + }; + position = { + layout = color_attachment_optimal; + blend = $blend_disable; + }; + }; + depth = { + depth = depth_stencil_read_only_optimal; + }; + preserve = (output); + }; + base_pipeline = { + @inherit = $pipeline_base; + rasterization = $cw_cull_back; + }; + pipelines = { + bsp:gbuffer = { + color = $color.bsp; + tasks = ( + { func = bsp_draw_queue; + params = (main, solid, 1); }, + { func = bsp_draw_queue; + params = (main, sky, 1); }, + ); + + stages = ( + $brush.shader.gbuf_vertex, + $brush.shader.gbuf_geometry, + $brush.shader.gbuf_fragment, + ); + vertexInput = $brush.vertexInput; + inputAssembly = $brush.inputAssembly; + layout = $brush.layout; + }; + alias:gbuffer = { + color = $color.alias; + tasks = ( + { func = alias_draw; + params = (1); }, + ); + + stages = ( + $alias.shader.gbuf_vertex, + $alias.shader.gbuf_fragment, + ); + vertexInput = $alias.vertexInput; + inputAssembly = $alias.inputAssembly; + layout = $alias.layout; + }; + iqm:gbuffer = { + color = $color.iqm; + tasks = ( + { func = iqm_draw; + params = (1); }, + ); + + stages = ( + $iqm.shader.gbuf_vertex, + $iqm.shader.gbuf_fragment, + ); + vertexInput = $iqm.vertexInput; + inputAssembly = $iqm.inputAssembly; + layout = $iqm.layout; + }; + sprite:gbuffer = { + color = $color.sprite; + tasks = ( + { func = sprite_draw; }, + ); + + stages = ( + $sprite.shader.gbuf_vertex, + $sprite.shader.gbuf_fragment, + ); + vertexInput = $sprite.vertexInput; + inputAssembly = $sprite.inputAssembly; + layout = $sprite.layout; + }; + }; + }; + lighting = { + color = "[ 0.8, 0.8, 0.8, 1]"; + dependencies = { + gbuffer = $color_dependency; + }; + attachments = { + input = { + depth = shader_read_only_optimal; + color = shader_read_only_optimal; + emission = shader_read_only_optimal; + normal = shader_read_only_optimal; + position = shader_read_only_optimal; + }; + color = { + opaque = { + layout = color_attachment_optimal; + blend = $blend_disable; + }; + }; + preserve = (output); + }; + pipelines = { + lights = { + @inherit = $compose_base; + + color = $color.lights; + tasks = ( + { func = lights_draw; }, + ); + + stages = ( + $fstriangle.shader.vertex, + $lighting.shader.fragment, + ); + layout = $lighting.layout; + }; + }; + }; + compose = { + color = "[ 0.7, 0.3, 0.3, 1]"; + dependencies = { + lighting = $color_dependency; + }; + attachments = { + input = { + opaque = shader_read_only_optimal; + }; + color = { + output = color_attachment_optimal; + }; + preserve = (depth, color, emission, normal, position); + }; + pipelines = { + compose = { + @inherit = $compose_base; + + color = $color.compose; + tasks = ( + { func = compose_draw; }, + ); + + stages = ( + $fstriangle.shader.vertex, + $compose.shader.fragment, + ); + layout = $compose.layout; + }; + }; + }; + }; + output = output; + }; + deferred_cube = { + @inherit = $renderpasses.deferred; + @next = (VkRenderPassMultiviewCreateInfo, { + viewMasks = (0x3fu, 0x3fu, 0x3fu, 0x3fu, 0x3fu); + }); + framebuffer = { + width = "min($render_output.extent.width,$render_output.extent.height)"; + height = "min($render_output.extent.width,$render_output.extent.height)"; + layers = 1; + attachments = { + depth = { + @inherit = $attachment_base; + format = $images.cube_depth.format; + loadOp = clear; + finalLayout = depth_stencil_attachment_optimal; + clearValue = { depthStencil = { depth = 1; stencil = 0; }; }; + view = cube_depth; + }; + color = { + @inherit = $attachment_base; + format = $images.cube_color.format; + loadOp = clear; + view = cube_color; + }; + emission = { + @inherit = $attachment_base; + format = $images.cube_emission.format; + loadOp = clear; + view = cube_emission; + }; + normal = { + @inherit = $attachment_base; + format = $images.cube_normal.format; + view = cube_normal; + }; + position = { + @inherit = $attachment_base; + format = $images.cube_position.format; + view = cube_position; + }; + opaque = { + @inherit = $attachment_base; + format = $images.cube_opaque.format; + view = cube_opaque; + }; + output = { + @inherit = $attachment_base; + format = $render_output.format; + loadOp = clear; + storeOp = store; + finalLayout = $render_output.finalLayout; + view = cube_output; + }; + }; + }; + output = cube_output; + }; + output = { + color = "[0, 1, 1, 1]"; + framebuffer = { + layers = 1; + attachments = { + output = $swapchain; + }; + }; + subpasses = { + compose = { + color = "[ 0, 0.5, 0.5, 1]"; + attachments = { + color = { + output = { + layout = color_attachment_optimal; + blend = $alpha_blend; + }; + }; + }; + pipelines = { + output = { + @inherit = $compose_base; + + color = $color.output; + tasks = ( + { func = output_draw_flat; }, + ); + + stages = ( + $fstriangle.shader.vertex, + $output.shader.fragment, + ); + layout = $output.layout; + }; + waterwarp = { + @inherit = $compose_base; + disabled = true; + + color = $color.output; + tasks = ( + { func = output_draw_waterwarp; }, + ); + + stages = ( + $fstriangle.shader.vertexst, + $waterwarp.shader.fragment, + ); + layout = $waterwarp.layout; + }; + fisheye = { + @inherit = $compose_base; + disabled = true; + + color = $color.output; + tasks = ( + { func = output_draw_fisheye; }, + ); + + stages = ( + $fstriangle.shader.vertexst, + $fisheye.shader.fragment, + ); + layout = $fisheye.layout; + }; + slice = { + @inherit = $compose_base; + + color = $color.slice; + tasks = ( + { func = slice_draw; }, + ); + + stages = ( + $slice.shader.vertex, + $slice.shader.fragment, + ); + vertexInput = $slice.vertexInput; + inputAssembly = $slice.inputAssembly; + layout = $slice.layout; + }; + lines = { + @inherit = $compose_base; + + color = $color.lines; + tasks = ( + { func = line_draw; }, + ); + + stages = ( + $lines.shader.vertex, + $lines.shader.fragment, + ); + vertexInput = $lines.vertexInput; + inputAssembly = $lines.inputAssembly; + layout = $lines.layout; + }; + }; + }; + }; + }; +}; +steps = { + wait_on_fence = { + process = { + tasks = ( + { func = wait_on_fence; }, + { func = capture_finalize; }, + { func = update_matrices; }, + { func = draw_scr_funcs; }, + ); + }; + }; + particles = { + dependencies = (wait_on_fence); + compute = { + pipelines = { + part:update = { + color = "[0.3, 0.8, 0.9]"; + tasks = ( + { func = update_particles; } + ); + stage = { + name = main; + module = $builtin/partupdate.comp; + }; + layout = $particle.layout.update; + }; + part:physics = { + color = "[0.6, 0.8, 0.9]"; + tasks = ( + { func = particle_physics; } + ); + stage = { + name = main; + module = $builtin/partphysics.comp; + }; + layout = $particle.layout.physics; + }; + }; + }; + }; + shadow = { + dependencies = (wait_on_fence); + //currently empty + }; + world = { + dependencies = (wait_on_fence); + process = { + tasks = ( + { func = bsp_visit_world; + params = (main); }, + { func = scene_draw_viewmodel; }, + ); + }; + }; + translucent = { + dependencies = (wait_on_fence); + process = { + tasks = ( + { func = clear_translucent; }, + ); + }; + }; + setup_main = { + dependencies = (wait_on_fence); + process = { + tasks = ( + { func = output_select_renderpass; + params = ("\"main\""); }, + { func = update_framebuffer; + params = ("\"main\""); }, + { func = particle_wait_physics; }, + ); + }; + }; + main = { + dependencies = (setup_main, particles, shadow, world, translucent); + render = { + renderpasses = { + deferred = $renderpasses.deferred; + deferred_cube = $renderpasses.deferred_cube; + }; + }; + }; + preoutput = { + dependencies = (wait_on_fence); + process = { + tasks = ( + { func = acquire_output; + params = ("\"output\""); }, + { func = update_input; + params = ("\"main\""); }, + { func = output_select_pipeline; + params = ("\"output\""); }, + { func = flush_draw; }, + ); + }; + }; + output = { + dependencies = (main, preoutput); + render = { + renderpasses = { + output = $renderpasses.output; + }; + }; + }; + capture = { + dependencies = (output); + process = { + tasks = ( + { func = capture_initiate; }, + ); + }; + }; +}; diff --git a/libs/video/renderer/vulkan/scrap.c b/libs/video/renderer/vulkan/scrap.c new file mode 100644 index 000000000..9e89fb050 --- /dev/null +++ b/libs/video/renderer/vulkan/scrap.c @@ -0,0 +1,345 @@ +/* + scrap.c + + Vulkan scrap manager + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "QF/dstring.h" +#include "QF/render.h" +#include "QF/ui/vrect.h" + +#include "QF/Vulkan/barrier.h" +#include "QF/Vulkan/buffer.h" +#include "QF/Vulkan/command.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/image.h" +#include "QF/Vulkan/scrap.h" +#include "QF/Vulkan/staging.h" + +#include "r_scrap.h" + +struct scrap_s { + rscrap_t rscrap; + VkImage image; + VkDeviceMemory memory; + VkImageView view; + size_t bpp; + qfv_packet_t *packet; + vrect_t *batch; + vrect_t **batch_tail; + vrect_t *batch_free; + size_t batch_count; + subpic_t *subpics; + qfv_device_t *device; +}; + +scrap_t * +QFV_CreateScrap (qfv_device_t *device, const char *name, int size, + QFFormat format, qfv_stagebuf_t *stage) +{ + qfv_devfuncs_t *dfunc = device->funcs; + int bpp = 0; + VkFormat fmt = VK_FORMAT_UNDEFINED; + dstring_t *str = dstring_new (); + + switch (format) { + case tex_l: + case tex_a: + case tex_palette: + bpp = 1; + fmt = VK_FORMAT_R8_UNORM; + break; + case tex_la: + bpp = 2; + fmt = VK_FORMAT_R8G8_UNORM; + break; + case tex_rgb: + bpp = 3; + fmt = VK_FORMAT_R8G8B8_UNORM; + break; + case tex_rgba: + bpp = 4; + fmt = VK_FORMAT_R8G8B8A8_UNORM; + break; + case tex_frgba: + bpp = 16; + fmt = VK_FORMAT_R32G32B32A32_SFLOAT; + break; + } + + scrap_t *scrap = malloc (sizeof (scrap_t)); + + R_ScrapInit (&scrap->rscrap, size, size); + + // R_ScrapInit rounds sizes up to next power of 2 + size = scrap->rscrap.width; + VkExtent3D extent = { size, size, 1 }; + scrap->image = QFV_CreateImage (device, 0, VK_IMAGE_TYPE_2D, fmt, + extent, 1, 1, VK_SAMPLE_COUNT_1_BIT, + VK_IMAGE_USAGE_TRANSFER_DST_BIT + | VK_IMAGE_USAGE_SAMPLED_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE, scrap->image, + dsprintf (str, "image:scrap:%s", name)); + scrap->memory = QFV_AllocImageMemory (device, scrap->image, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + 0, 0); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, scrap->memory, + dsprintf (str, "memory:scrap:%s", name)); + QFV_BindImageMemory (device, scrap->image, scrap->memory, 0); + scrap->view = QFV_CreateImageView (device, scrap->image, + VK_IMAGE_VIEW_TYPE_2D, fmt, + VK_IMAGE_ASPECT_COLOR_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE_VIEW, scrap->view, + dsprintf (str, "iview:scrap:%s", name)); + dstring_delete (str); + scrap->bpp = bpp; + scrap->subpics = 0; + scrap->device = device; + scrap->packet = 0; + scrap->batch = 0; + scrap->batch_tail = &scrap->batch; + scrap->batch_free = 0; + scrap->batch_count = 0; + + qfv_packet_t *packet = QFV_PacketAcquire (stage); + // no data for the packet + qfv_imagebarrier_t ib = imageBarriers[qfv_LT_Undefined_to_TransferDst]; + ib.barrier.image = scrap->image; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); + VkClearColorValue color = { + .float32 = {0xde/255.0, 0xad/255.0, 0xbe/255.0, 0xef/255.0}, + }; + VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + dfunc->vkCmdClearColorImage (packet->cmd, scrap->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + &color, 1, &range); + ib = imageBarriers[qfv_LT_TransferDst_to_ShaderReadOnly]; + ib.barrier.image = scrap->image; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); + QFV_PacketSubmit (packet); + return scrap; +} + +size_t +QFV_ScrapSize (scrap_t *scrap) +{ + return scrap->rscrap.width * scrap->rscrap.height * scrap->bpp; +} + +void +QFV_ScrapClear (scrap_t *scrap) +{ + subpic_t *sp; + while (scrap->subpics) { + sp = scrap->subpics; + scrap->subpics = (subpic_t *) sp->next; + free (sp); + } + R_ScrapClear (&scrap->rscrap); +} + +void +QFV_DestroyScrap (scrap_t *scrap) +{ + qfv_device_t *device = scrap->device; + qfv_devfuncs_t *dfunc = device->funcs; + + QFV_ScrapClear (scrap); + R_ScrapDelete (&scrap->rscrap); + dfunc->vkDestroyImageView (device->dev, scrap->view, 0); + dfunc->vkDestroyImage (device->dev, scrap->image, 0); + dfunc->vkFreeMemory (device->dev, scrap->memory, 0); + + while (scrap->batch_free) { + vrect_t *b = scrap->batch_free; + scrap->batch_free = b->next; + VRect_Delete (b); + } + free (scrap); +} + +VkImageView +QFV_ScrapImageView (scrap_t *scrap) +{ + return scrap->view; +} + +subpic_t * +QFV_ScrapSubpic (scrap_t *scrap, int width, int height) +{ + vrect_t *rect; + subpic_t *subpic; + + rect = R_ScrapAlloc (&scrap->rscrap, width, height); + if (!rect) { + return 0; + } + + subpic = malloc (sizeof (subpic_t)); + *((subpic_t **) &subpic->next) = scrap->subpics; + scrap->subpics = subpic; + *((scrap_t **) &subpic->scrap) = scrap; + *((vrect_t **) &subpic->rect) = rect; + *((int *) &subpic->width) = width; + *((int *) &subpic->height) = height; + *((float *) &subpic->size) = 1.0 / scrap->rscrap.width; + return subpic; +} + +void +QFV_SubpicDelete (subpic_t *subpic) +{ + scrap_t *scrap = (scrap_t *) subpic->scrap; + vrect_t *rect = (vrect_t *) subpic->rect; + subpic_t **sp; + + for (sp = &scrap->subpics; *sp; sp = (subpic_t **) &(*sp)->next) { + if (*sp == subpic) { + break; + } + } + if (*sp != subpic) { + Sys_Error ("QFV_SubpicDelete: broken subpic"); + } + *sp = (subpic_t *) subpic->next; + free (subpic); + R_ScrapFree (&scrap->rscrap, rect); +} + +void * +QFV_SubpicBatch (subpic_t *subpic, qfv_stagebuf_t *stage) +{ + scrap_t *scrap = (scrap_t *) subpic->scrap; + vrect_t *rect = (vrect_t *) subpic->rect; + vrect_t *batch; + byte *dest; + size_t size; + + if (!scrap->packet) { + scrap->packet = QFV_PacketAcquire (stage); + } + size = (subpic->width * subpic->height * scrap->bpp + 3) & ~3; + if (!(dest = QFV_PacketExtend (scrap->packet, size))) { + if (scrap->packet->length) { + QFV_ScrapFlush (scrap); + + scrap->packet = QFV_PacketAcquire (stage); + dest = QFV_PacketExtend (scrap->packet, size); + } + if (!dest) { + printf ("scrap: could not get space for update\n"); + return 0; + } + } + + if (scrap->batch_free) { + batch = scrap->batch_free; + scrap->batch_free = batch->next; + batch->x = rect->x; + batch->y = rect->y; + batch->width = subpic->width; + batch->height = subpic->height; + } else { + batch = VRect_New (rect->x, rect->y, subpic->width, subpic->height); + } + *scrap->batch_tail = batch; + scrap->batch_tail = &batch->next; + batch->next = 0; + scrap->batch_count++; + return dest; +} + +void +QFV_ScrapFlush (scrap_t *scrap) +{ + qfv_device_t *device = scrap->device; + qfv_devfuncs_t *dfunc = device->funcs; + + if (!scrap->batch_count) { + return; + } + + qfv_packet_t *packet = scrap->packet; + qfv_stagebuf_t *stage = packet->stage; + size_t i; + __auto_type copy = QFV_AllocBufferImageCopy (128, alloca); + memset (copy->a, 0, 128 * sizeof (copy->a[0])); + + for (i = 0; i < scrap->batch_count && i < 128; i++) { + copy->a[i].imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy->a[i].imageSubresource.layerCount = 1; + copy->a[i].imageExtent.depth = 1; + } + + qfv_imagebarrier_t ib = imageBarriers[qfv_LT_ShaderReadOnly_to_TransferDst]; + ib.barrier.image = scrap->image; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); + + size_t offset = packet->offset, size; + vrect_t *batch = scrap->batch; + while (scrap->batch_count) { + for (i = 0; i < scrap->batch_count && i < 128; i++) { + size = batch->width * batch->height * scrap->bpp; + + copy->a[i].bufferOffset = offset; + copy->a[i].imageOffset.x = batch->x; + copy->a[i].imageOffset.y = batch->y; + copy->a[i].imageExtent.width = batch->width; + copy->a[i].imageExtent.height = batch->height; + + offset += (size + 3) & ~3; + batch = batch->next; + } + dfunc->vkCmdCopyBufferToImage (packet->cmd, stage->buffer, scrap->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + i, copy->a); + scrap->batch_count -= i; + } + + ib = imageBarriers[qfv_LT_TransferDst_to_ShaderReadOnly]; + ib.barrier.image = scrap->image; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); + + *scrap->batch_tail = scrap->batch_free; + scrap->batch_free = scrap->batch; + scrap->batch = 0; + scrap->batch_tail = &scrap->batch; + + QFV_PacketSubmit (scrap->packet); + scrap->packet = 0; +} diff --git a/libs/video/renderer/vulkan/shader.c b/libs/video/renderer/vulkan/shader.c new file mode 100644 index 000000000..8331fb01d --- /dev/null +++ b/libs/video/renderer/vulkan/shader.c @@ -0,0 +1,248 @@ +/* + shader.c + + Vulkan shader manager + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "QF/dstring.h" +#include "QF/quakefs.h" +#include "QF/sys.h" + +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/shader.h" + +static +#include "libs/video/renderer/vulkan/shader/slice.vert.spvc" +static +#include "libs/video/renderer/vulkan/shader/line.vert.spvc" +static +#include "libs/video/renderer/vulkan/shader/line.frag.spvc" +static +#include "libs/video/renderer/vulkan/shader/particle.vert.spvc" +static +#include "libs/video/renderer/vulkan/shader/particle.geom.spvc" +static +#include "libs/video/renderer/vulkan/shader/particle.frag.spvc" +static +#include "libs/video/renderer/vulkan/shader/partphysics.comp.spvc" +static +#include "libs/video/renderer/vulkan/shader/partupdate.comp.spvc" +static +#include "libs/video/renderer/vulkan/shader/sprite_gbuf.vert.spvc" +static +#include "libs/video/renderer/vulkan/shader/sprite_gbuf.frag.spvc" +static +#include "libs/video/renderer/vulkan/shader/sprite_depth.vert.spvc" +static +#include "libs/video/renderer/vulkan/shader/sprite_depth.frag.spvc" +static +#include "libs/video/renderer/vulkan/shader/twod_depth.frag.spvc" +static +#include "libs/video/renderer/vulkan/shader/twod.vert.spvc" +static +#include "libs/video/renderer/vulkan/shader/twod.frag.spvc" +static +#include "libs/video/renderer/vulkan/shader/quakebsp.vert.spvc" +static +#include "libs/video/renderer/vulkan/shader/quakebsp.frag.spvc" +static +#include "libs/video/renderer/vulkan/shader/bsp_depth.vert.spvc" +static +#include "libs/video/renderer/vulkan/shader/bsp_gbuf.vert.spvc" +static +#include "libs/video/renderer/vulkan/shader/bsp_gbuf.geom.spvc" +static +#include "libs/video/renderer/vulkan/shader/bsp_gbuf.frag.spvc" +static +#include "libs/video/renderer/vulkan/shader/bsp_shadow.vert.spvc" +static +#include "libs/video/renderer/vulkan/shader/bsp_sky.frag.spvc" +static +#include "libs/video/renderer/vulkan/shader/bsp_turb.frag.spvc" +static +#include "libs/video/renderer/vulkan/shader/lighting.frag.spvc" +static +#include "libs/video/renderer/vulkan/shader/compose.frag.spvc" +static +#include "libs/video/renderer/vulkan/shader/alias.vert.spvc" +static +#include "libs/video/renderer/vulkan/shader/alias_depth.vert.spvc" +static +#include "libs/video/renderer/vulkan/shader/alias.frag.spvc" +static +#include "libs/video/renderer/vulkan/shader/alias_gbuf.frag.spvc" +static +#include "libs/video/renderer/vulkan/shader/alias_shadow.vert.spvc" +static +#include "libs/video/renderer/vulkan/shader/iqm.vert.spvc" +static +#include "libs/video/renderer/vulkan/shader/iqm.frag.spvc" +static +#include "libs/video/renderer/vulkan/shader/output.frag.spvc" +static +#include "libs/video/renderer/vulkan/shader/passthrough.vert.spvc" +static +#include "libs/video/renderer/vulkan/shader/fstriangle.vert.spvc" +static +#include "libs/video/renderer/vulkan/shader/fstrianglest.vert.spvc" +static +#include "libs/video/renderer/vulkan/shader/pushcolor.frag.spvc" +static +#include "libs/video/renderer/vulkan/shader/fisheye.frag.spvc" +static +#include "libs/video/renderer/vulkan/shader/waterwarp.frag.spvc" + +typedef struct shaderdata_s { + const char *name; + const uint32_t *data; + size_t size; +} shaderdata_t; + +static shaderdata_t builtin_shaders[] = { + { "slice.vert", slice_vert, sizeof (slice_vert) }, + { "line.vert", line_vert, sizeof (line_vert) }, + { "line.frag", line_frag, sizeof (line_frag) }, + { "particle.vert", particle_vert, sizeof (particle_vert) }, + { "particle.geom", particle_geom, sizeof (particle_geom) }, + { "particle.frag", particle_frag, sizeof (particle_frag) }, + { "partphysics.comp", partphysics_comp, sizeof (partphysics_comp) }, + { "partupdate.comp", partupdate_comp, sizeof (partupdate_comp) }, + { "sprite_gbuf.vert", sprite_gbuf_vert, sizeof (sprite_gbuf_vert) }, + { "sprite_gbuf.frag", sprite_gbuf_frag, sizeof (sprite_gbuf_frag) }, + { "sprite_depth.vert", sprite_depth_vert, sizeof (sprite_depth_vert) }, + { "sprite_depth.frag", sprite_depth_frag, sizeof (sprite_depth_frag) }, + { "twod_depth.frag", twod_depth_frag, sizeof (twod_depth_frag) }, + { "twod.vert", twod_vert, sizeof (twod_vert) }, + { "twod.frag", twod_frag, sizeof (twod_frag) }, + { "quakebsp.vert", quakebsp_vert, sizeof (quakebsp_vert) }, + { "quakebsp.frag", quakebsp_frag, sizeof (quakebsp_frag) }, + { "bsp_depth.vert", bsp_depth_vert, sizeof (bsp_depth_vert) }, + { "bsp_gbuf.vert", bsp_gbuf_vert, sizeof (bsp_gbuf_vert) }, + { "bsp_gbuf.geom", bsp_gbuf_geom, sizeof (bsp_gbuf_geom) }, + { "bsp_gbuf.frag", bsp_gbuf_frag, sizeof (bsp_gbuf_frag) }, + { "bsp_shadow.vert", bsp_shadow_vert, sizeof (bsp_shadow_vert) }, + { "bsp_sky.frag", bsp_sky_frag, sizeof (bsp_sky_frag) }, + { "bsp_turb.frag", bsp_turb_frag, sizeof (bsp_turb_frag) }, + { "lighting.frag", lighting_frag, sizeof (lighting_frag) }, + { "compose.frag", compose_frag, sizeof (compose_frag) }, + { "alias.vert", alias_vert, sizeof (alias_vert) }, + { "alias_depth.vert", alias_depth_vert, sizeof (alias_depth_vert) }, + { "alias.frag", alias_frag, sizeof (alias_frag) }, + { "alias_gbuf.frag", alias_gbuf_frag, sizeof (alias_gbuf_frag) }, + { "alias_shadow.vert", alias_shadow_vert, sizeof (alias_shadow_vert) }, + { "iqm.vert", iqm_vert, sizeof (iqm_vert) }, + { "iqm.frag", iqm_frag, sizeof (iqm_frag) }, + { "output.frag", output_frag, sizeof (output_frag) }, + { "passthrough.vert", passthrough_vert, sizeof (passthrough_vert) }, + { "fstriangle.vert", fstriangle_vert, sizeof (fstriangle_vert) }, + { "fstrianglest.vert", fstrianglest_vert, sizeof (fstrianglest_vert) }, + { "pushcolor.frag", pushcolor_frag, sizeof (pushcolor_frag) }, + { "fisheye.frag", fisheye_frag, sizeof (fisheye_frag) }, + { "waterwarp.frag", waterwarp_frag, sizeof (waterwarp_frag) }, + {} +}; + +#define BUILTIN "$builtin/" +#define BUILTIN_SIZE (sizeof (BUILTIN) - 1) +#define SHADER "$shader/" +#define SHADER_SIZE (sizeof (SHADER) - 1) + +VkShaderModule +QFV_CreateShaderModule (qfv_device_t *device, const char *shader_path) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + + shaderdata_t _data = {}; + shaderdata_t *data = 0; + dstring_t *path = 0; + QFile *file = 0; + VkShaderModule shader = 0; + + if (strncmp (shader_path, BUILTIN, BUILTIN_SIZE) == 0) { + const char *name = shader_path + BUILTIN_SIZE; + + for (int i = 0; builtin_shaders[i].name; i++) { + if (strcmp (builtin_shaders[i].name, name) == 0) { + data = &builtin_shaders[i]; + break; + } + } + } else if (strncmp (shader_path, SHADER, SHADER_SIZE)) { + path = dstring_new (); + dsprintf (path, "%s/%s", FS_SHADERPATH, shader_path + SHADER_SIZE); + file = Qopen (path->str, "rbz"); + } else { + file = QFS_FOpenFile (shader_path); + } + + if (file) { + _data.size = Qfilesize (file); + _data.data = malloc (_data.size); + Qread (file, (void *) _data.data, _data.size); + Qclose (file); + data = &_data; + } + + if (data) { + Sys_MaskPrintf (SYS_vulkan, + "QFV_CreateShaderModule: creating shader module %s\n", + shader_path); + VkShaderModuleCreateInfo createInfo = { + VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, 0, + 0, data->size, data->data + }; + + dfunc->vkCreateShaderModule (dev, &createInfo, 0, &shader); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_SHADER_MODULE, shader, + shader_path); + } else { + Sys_MaskPrintf (SYS_vulkan, + "QFV_CreateShaderModule: could not find shader %s\n", + shader_path); + } + + if (path) { + dstring_delete (path); + } + if (_data.data) { + free ((void *) _data.data); + } + return shader; +} + +void +QFV_DestroyShaderModule (qfv_device_t *device, VkShaderModule module) +{ + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + + dfunc->vkDestroyShaderModule (dev, module, 0); +} diff --git a/libs/video/renderer/vulkan/shader/alias.frag b/libs/video/renderer/vulkan/shader/alias.frag new file mode 100644 index 000000000..3b5c9fddd --- /dev/null +++ b/libs/video/renderer/vulkan/shader/alias.frag @@ -0,0 +1,76 @@ +#version 450 +layout (set = 0, binding = 2) uniform sampler2DArray Skin; +/* +layout (set = 2, binding = 0) uniform sampler2D Texture; +layout (set = 2, binding = 1) uniform sampler2D GlowMap; +layout (set = 2, binding = 2) uniform sampler2D ColorA; +layout (set = 2, binding = 3) uniform sampler2D ColorB; +*/ +struct LightData { + vec3 color; + float dist; + vec3 position; + int type; + vec3 direction; + float cone; +}; + +layout (constant_id = 0) const int MaxLights = 8; +//layout (set = 1, binding = 0) uniform Lights { +layout (set = 0, binding = 1) uniform Lights { + int light_count; + LightData lights[MaxLights]; +}; + +layout (push_constant) uniform PushConstants { + layout (offset = 68) + uint base_color; + uint colorA; + uint colorB; + vec4 fog; + vec4 color; +}; + +layout (location = 0) in vec2 st; +layout (location = 1) in vec3 position; +layout (location = 2) in vec3 normal; + +layout (location = 0) out vec4 frag_color; + +vec3 +calc_light (LightData light) +{ + if (light.type == 0) { + vec3 dist = light.position - position; + float dd = dot (dist, dist); + float mag = max (0.0, dot (dist, normal)); + return light.color * mag * light.dist / dd; + } else if (light.type == 1) { + } else if (light.type == 2) { + float mag = max (0.0, -dot (light.direction, normal)); + // position is ambient light + return light.color * dot (light.direction, normal) + light.position; + } +} + +void +main (void) +{ + vec4 c; + int i; + vec3 light = vec3 (0); + c = texture (Skin, vec3 (st, 0)) * unpackUnorm4x8(base_color); + c += texture (Skin, vec3 (st, 1)) * unpackUnorm4x8(colorA); + c += texture (Skin, vec3 (st, 2)) * unpackUnorm4x8(colorB); + + if (MaxLights > 0) { + for (i = 0; i < light_count; i++) { + light += calc_light (lights[i]); + } + } + c *= vec4 (light, 1); + + c += texture (Skin, vec3 (st, 3)); + //frag_color = vec4((normal + 1)/2, 1); + frag_color = c;//fogBlend (c); +} diff --git a/libs/video/renderer/vulkan/shader/alias.vert b/libs/video/renderer/vulkan/shader/alias.vert new file mode 100644 index 000000000..4309266f0 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/alias.vert @@ -0,0 +1,38 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#extension GL_EXT_multiview : enable + +layout (set = 0, binding = 0) uniform +#include "matrices.h" +; + +layout (push_constant) uniform PushConstants { + mat4 Model; + float blend; +}; + +layout (location = 0) in vec4 vertexa; +layout (location = 1) in vec3 normala; +layout (location = 2) in vec4 vertexb; +layout (location = 3) in vec3 normalb; +layout (location = 4) in vec2 uv; + +layout (location = 0) out vec2 st; +layout (location = 1) out vec4 position; +layout (location = 2) out vec3 normal; + +void +main (void) +{ + vec4 vertex; + vec3 norm; + vec4 pos; + + vertex = mix (vertexa, vertexb, blend); + norm = mix (normala, normalb, blend); + pos = (Model * vertex); + gl_Position = Projection3d * (View[gl_ViewIndex] * pos); + position = pos; + normal = normalize (mat3 (Model) * norm); + st = uv; +} diff --git a/libs/video/renderer/vulkan/shader/alias_depth.vert b/libs/video/renderer/vulkan/shader/alias_depth.vert new file mode 100644 index 000000000..7d01354f3 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/alias_depth.vert @@ -0,0 +1,28 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#extension GL_EXT_multiview : enable + +layout (set = 0, binding = 0) uniform +#include "matrices.h" +; + +layout (push_constant) uniform PushConstants { + mat4 Model; + float blend; +}; + +layout (location = 0) in vec4 vertexa; +layout (location = 1) in vec3 normala; +layout (location = 2) in vec4 vertexb; +layout (location = 3) in vec3 normalb; + +void +main (void) +{ + vec4 vertex; + vec4 pos; + + vertex = mix (vertexa, vertexb, blend); + pos = (Model * vertex); + gl_Position = Projection3d * (View[gl_ViewIndex] * pos); +} diff --git a/libs/video/renderer/vulkan/shader/alias_gbuf.frag b/libs/video/renderer/vulkan/shader/alias_gbuf.frag new file mode 100644 index 000000000..446219ad7 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/alias_gbuf.frag @@ -0,0 +1,38 @@ +#version 450 + +layout (set = 1, binding = 0) uniform sampler2D Palette; +layout (set = 2, binding = 0) uniform sampler2DArray Skin; + +layout (push_constant) uniform PushConstants { + layout (offset = 68) + uint colors; + vec4 base_color; + vec4 fog; +}; + +layout (location = 0) in vec2 st; +layout (location = 1) in vec4 position; +layout (location = 2) in vec3 normal; + +layout (location = 0) out vec4 frag_color; +layout (location = 1) out vec4 frag_emission; +layout (location = 2) out vec4 frag_normal; +layout (location = 3) out vec4 frag_position; + +void +main (void) +{ + vec4 c; + vec4 e; + c = texture (Skin, vec3 (st, 0)) * base_color; + e = texture (Skin, vec3 (st, 1)); + vec4 rows = unpackUnorm4x8(colors); + vec4 cmap = texture (Skin, vec3 (st, 2)); + c += texture (Palette, vec2 (cmap.x, rows.x)) * cmap.y; + c += texture (Palette, vec2 (cmap.z, rows.y)) * cmap.w; + + frag_color = c; + frag_emission = e; + frag_normal = vec4(normalize(normal), 1); + frag_position = position; +} diff --git a/libs/video/renderer/vulkan/shader/alias_shadow.vert b/libs/video/renderer/vulkan/shader/alias_shadow.vert new file mode 100644 index 000000000..d99c1b445 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/alias_shadow.vert @@ -0,0 +1,23 @@ +#version 450 + +layout (push_constant) uniform PushConstants { + mat4 Model; + float blend; +}; + +layout (location = 0) in vec4 vertexa; +layout (location = 1) in vec3 normala; +layout (location = 2) in vec4 vertexb; +layout (location = 3) in vec3 normalb; + +layout (location = 0) out int InstanceIndex; + +void +main (void) +{ + vec4 vertex; + + vertex = mix (vertexa, vertexb, blend); + gl_Position = Model * vertex; + InstanceIndex = gl_InstanceIndex; +} diff --git a/libs/video/renderer/vulkan/shader/bsp_depth.vert b/libs/video/renderer/vulkan/shader/bsp_depth.vert new file mode 100644 index 000000000..b454b8787 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/bsp_depth.vert @@ -0,0 +1,23 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#extension GL_EXT_multiview : enable + +#include "entity.h" + +layout (set = 0, binding = 0) uniform +#include "matrices.h" +; + +layout (set = 1, binding = 0) buffer Entities { + Entity entities[]; +}; + +layout (location = 0) in vec4 vertex; +layout (location = 2) in uint entid; + +void +main (void) +{ + vec3 vert = vertex * entities[entid].transform; + gl_Position = Projection3d * (View[gl_ViewIndex] * vec4 (vert, 1)); +} diff --git a/libs/video/renderer/vulkan/shader/bsp_gbuf.frag b/libs/video/renderer/vulkan/shader/bsp_gbuf.frag new file mode 100644 index 000000000..eeaff7db7 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/bsp_gbuf.frag @@ -0,0 +1,47 @@ +#version 450 + +layout (set = 3, binding = 0) uniform sampler2DArray Texture; + +layout (push_constant) uniform PushConstants { + vec4 fog; + float time; + float alpha; +}; + +layout (location = 0) in vec4 tl_st; +layout (location = 1) in vec3 direction; +layout (location = 2) in vec3 normal; +layout (location = 3) in vec4 position; +layout (location = 4) in vec4 color; + +layout (location = 0) out vec4 frag_color; +layout (location = 1) out vec4 frag_emission; +layout (location = 2) out vec4 frag_normal; +layout (location = 3) out vec4 frag_position; + +vec4 +fogBlend (vec4 color) +{ + float az = fog.a * gl_FragCoord.z / gl_FragCoord.w; + vec3 fog_color = fog.rgb; + float fog_factor = exp (-az * az); + + return vec4 (mix (fog_color.rgb, color.rgb, fog_factor), color.a); +} + +void +main (void) +{ + vec4 c = vec4 (0); + vec4 e; + vec3 t_st = vec3 (tl_st.xy, 0); + vec3 e_st = vec3 (tl_st.xy, 1); + vec2 l_st = vec2 (tl_st.zw); + + c = texture (Texture, t_st) * color; + e = texture (Texture, e_st); + frag_color = c;//fogBlend (c); + frag_emission = e; + frag_normal = vec4 (normal, 0); + frag_position = position; +} diff --git a/libs/video/renderer/vulkan/shader/bsp_gbuf.geom b/libs/video/renderer/vulkan/shader/bsp_gbuf.geom new file mode 100644 index 000000000..f5a32ebd2 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/bsp_gbuf.geom @@ -0,0 +1,41 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#extension GL_EXT_multiview : enable + +layout (set = 0, binding = 0) uniform +#include "matrices.h" +; + +layout (triangles) in; +layout (triangle_strip, max_vertices = 3) out; +layout (location = 0) in vec4 v_tl_st[]; +layout (location = 1) in vec3 v_direction[]; +layout (location = 2) in vec4 v_color[]; + +layout (location = 0) out vec4 tl_st; +layout (location = 1) out vec3 direction; +layout (location = 2) out vec3 normal; +layout (location = 3) out vec4 position; +layout (location = 4) out vec4 color; + +void +main() +{ + vec3 a = gl_in[0].gl_Position.xyz; + vec3 b = gl_in[1].gl_Position.xyz; + vec3 c = gl_in[2].gl_Position.xyz; + + vec3 n = normalize (cross (c - a, b - a)); + + for (int vert = 0; vert < 3; vert++) { + vec4 p = gl_in[vert].gl_Position; + gl_Position = Projection3d * (View[gl_ViewIndex] * (p)); + tl_st = v_tl_st[vert]; + direction = v_direction[vert]; + color = v_color[vert]; + normal = n; + position = p; + EmitVertex (); + } + EndPrimitive (); +} diff --git a/libs/video/renderer/vulkan/shader/bsp_gbuf.vert b/libs/video/renderer/vulkan/shader/bsp_gbuf.vert new file mode 100644 index 000000000..7b66575a4 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/bsp_gbuf.vert @@ -0,0 +1,31 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +#include "entity.h" + +layout (set = 0, binding = 0) uniform +#include "matrices.h" +; + +layout (set = 1, binding = 0) buffer Entities { + Entity entities[]; +}; + +layout (location = 0) in vec4 vertex; +layout (location = 1) in vec4 tl_uv; +layout (location = 2) in uint entid; + +layout (location = 0) out vec4 tl_st; +layout (location = 1) out vec3 direction; +layout (location = 2) out vec4 color; + +void +main (void) +{ + // geometry shader will take care of Projection and View + vec3 vert = vertex * entities[entid].transform; + gl_Position = vec4 (vert, 1); + direction = (Sky * vertex).xyz; + tl_st = tl_uv; + color = entities[entid].color; +} diff --git a/libs/video/renderer/vulkan/shader/bsp_shadow.vert b/libs/video/renderer/vulkan/shader/bsp_shadow.vert new file mode 100644 index 000000000..ea1dfb8d6 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/bsp_shadow.vert @@ -0,0 +1,21 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +#include "entity.h" + +layout (set = 1, binding = 0) buffer Entities { + Entity entities[]; +}; + +layout (location = 0) in vec4 vertex; +layout (location = 2) in uint entid; + +layout (location = 0) out int InstanceIndex; + +void +main (void) +{ + vec3 vert = vertex * entities[entid].transform; + gl_Position = vec4 (vert, 1);; + InstanceIndex = gl_InstanceIndex; +} diff --git a/libs/video/renderer/vulkan/shader/bsp_sky.frag b/libs/video/renderer/vulkan/shader/bsp_sky.frag new file mode 100644 index 000000000..a574bec37 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/bsp_sky.frag @@ -0,0 +1,105 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#extension GL_EXT_multiview : enable + +#include "oit_store.finc" + +layout (constant_id = 0) const bool doSkyBox = false; +layout (constant_id = 1) const bool doSkySheet = false; + +layout (set = 3, binding = 0) uniform sampler2DArray SkySheet; +layout (set = 4, binding = 0) uniform samplerCube SkyBox; + +layout (push_constant) uniform PushConstants { + vec4 fog; + float time; + float alpha; + float turb_scale; +}; + +layout (location = 0) in vec4 tl_st; +layout (location = 1) in vec3 direction; +layout (location = 2) in vec4 color; + +layout(early_fragment_tests) in; +//layout (location = 0) out vec4 frag_color; + +const float SCALE = 189.0 / 64.0; + +vec4 +fogBlend (vec4 color) +{ + float az = fog.a * gl_FragCoord.z / gl_FragCoord.w; + vec3 fog_color = fog.rgb; + float fog_factor = exp (-az * az); + + return vec4 (mix (fog_color.rgb, color.rgb, fog_factor), color.a); +} + +vec4 +sky_sheet (vec3 dir, float time) +{ + float len; + vec2 flow = vec2 (1.0, 1.0); + vec2 base; + vec3 st1, st2; + vec4 c1, c2, c; + + dir.z *= 3.0; + len = dot (dir, dir); + len = SCALE * inversesqrt (len); + base = dir.yx * vec2(1.0, -1.0) * len; + + st1 = vec3 (base + flow * time / 8.0, 0); + st2 = vec3 (base + flow * time / 16.0, 1); + + c1 = texture (SkySheet, st1); + c2 = texture (SkySheet, st2); + + c = vec4 (mix (c2.rgb, c1.rgb, c1.a), max (c1.a, c2.a)); + + return c; +} + +vec4 +sky_box (vec3 dir, float time) +{ + // NOTE: quake's world is right-handed with Z up and X forward, but + // Vulkan's cube maps are left-handed with Y up and Z forward. The + // rotation to X foward is done by the Sky matrix so all that's left + // to do here is swizzle the Y and Z coordinates + dir = normalize(dir); + //return vec4(dir.xyz, 1) * 0.5 + vec4(0.5); + return texture (SkyBox, dir.xzy); +} + +vec4 +sky_color (vec3 dir, float time) +{ + if (!doSkySheet) { + return sky_box (dir, time); + } if (!doSkyBox) { + return sky_sheet (dir, time); + } else { + // can see through the sheet (may look funny when looking down) + // maybe have 4 sheet layers instead of 2? + vec4 c1 = sky_sheet (dir, time); + vec4 c2 = sky_box (dir, time); + return vec4 (mix (c2.rgb, c1.rgb, c1.a), max (c1.a, c2.a)); + return vec4 (1, 0, 1, 1); + } +} + +void +main (void) +{ + vec4 c; + + if (doSkyBox || doSkySheet) { + c = sky_color (direction, time); + } else { + c = vec4 (0, 0, 0, 1); + } + //frag_color = c;//fogBlend (c); + StoreFrag (c, gl_FragCoord.z); +} diff --git a/libs/video/renderer/vulkan/shader/bsp_turb.frag b/libs/video/renderer/vulkan/shader/bsp_turb.frag new file mode 100644 index 000000000..7254eee87 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/bsp_turb.frag @@ -0,0 +1,59 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#extension GL_EXT_multiview : enable + +#include "oit_store.finc" + +layout (set = 3, binding = 0) uniform sampler2DArray Texture; + +layout (push_constant) uniform PushConstants { + vec4 fog; + float time; + float alpha; + float turb_scale; +}; + +layout (location = 0) in vec4 tl_st; +layout (location = 1) in vec3 direction; +layout (location = 2) in vec4 color; + +layout(early_fragment_tests) in; +//layout (location = 0) out vec4 frag_color; + +const float PI = 3.14159265; +const float SPEED = 20.0; +const float CYCLE = 128.0; +const float FACTOR = PI * 2.0 / CYCLE; +const vec2 BIAS = vec2 (1.0, 1.0); +const float SCALE = 8.0; + +vec2 +warp_st (vec2 st, float time) +{ + vec2 angle = st.ts * CYCLE / 2.0; + vec2 phase = vec2 (time, time) * SPEED; + return st + turb_scale * (sin ((angle + phase) * FACTOR) + BIAS) / SCALE; +} + +vec4 +fogBlend (vec4 color) +{ + float az = fog.a * gl_FragCoord.z / gl_FragCoord.w; + vec3 fog_color = fog.rgb; + float fog_factor = exp (-az * az); + + return vec4 (mix (fog_color.rgb, color.rgb, fog_factor), color.a); +} + +void +main (void) +{ + vec2 st = warp_st (tl_st.xy, time); + vec4 c = texture (Texture, vec3(st, 0)); + vec4 e = texture (Texture, vec3(st, 1)); + float a = c.a * e.a * alpha; + c += e; + c.a = a; + //frag_color = c * color;//fogBlend (c); + StoreFrag (c * color, gl_FragCoord.z); +} diff --git a/libs/video/renderer/vulkan/shader/compose.frag b/libs/video/renderer/vulkan/shader/compose.frag new file mode 100644 index 000000000..1373c849b --- /dev/null +++ b/libs/video/renderer/vulkan/shader/compose.frag @@ -0,0 +1,22 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#extension GL_EXT_multiview : enable + +#define OIT_SET 1 +#include "oit_blend.finc" + +layout (input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput opaque; + +layout (location = 0) out vec4 frag_color; + +void +main (void) +{ + vec3 o; + vec3 c; + + o = subpassLoad (opaque).rgb; + c = BlendFrags (vec4 (o, 1)).xyz; + c = pow (c, vec3(0.83));//FIXME make gamma correction configurable + frag_color = vec4 (c, 1); +} diff --git a/libs/video/renderer/vulkan/shader/entity.h b/libs/video/renderer/vulkan/shader/entity.h new file mode 100644 index 000000000..0da5a9d27 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/entity.h @@ -0,0 +1,4 @@ +struct Entity { + mat3x4 transform; + vec4 color; +}; diff --git a/libs/video/renderer/vulkan/shader/fisheye.frag b/libs/video/renderer/vulkan/shader/fisheye.frag new file mode 100644 index 000000000..a8623c87d --- /dev/null +++ b/libs/video/renderer/vulkan/shader/fisheye.frag @@ -0,0 +1,30 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +layout (set = 0, binding = 0) uniform +#include "matrices.h" +; + +layout (set = 1, binding = 0) uniform samplerCube Input; + +layout (push_constant) uniform PushConstants { + float fov; + float aspect; +}; + +layout (location = 0) in vec2 uv; + +layout (location = 0) out vec4 frag_color; + +void +main () +{ + // slight offset on y is to avoid the singularity straight ahead + vec2 xy = (2.0 * uv - vec2 (1, 1.00002)) * (vec2(1, -aspect)); + float r = sqrt (dot (xy, xy)); + vec2 cs = vec2 (cos (r * fov), sin (r * fov)); + vec3 dir = vec3 (cs.y * xy / r, cs.x); + + vec4 c = texture (Input, dir); + frag_color = c;// * 0.001 + vec4(dir, 1); +} diff --git a/libs/video/renderer/vulkan/shader/fstriangle.vert b/libs/video/renderer/vulkan/shader/fstriangle.vert new file mode 100644 index 000000000..66c55d6ac --- /dev/null +++ b/libs/video/renderer/vulkan/shader/fstriangle.vert @@ -0,0 +1,9 @@ +#version 450 + +void +main () +{ + float x = (gl_VertexIndex & 2); + float y = (gl_VertexIndex & 1); + gl_Position = vec4 (2, 4, 0, 1) * vec4 (x, y, 0, 1) - vec4 (1, 1, 0, 0); +} diff --git a/libs/video/renderer/vulkan/shader/fstrianglest.vert b/libs/video/renderer/vulkan/shader/fstrianglest.vert new file mode 100644 index 000000000..92a9c7d51 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/fstrianglest.vert @@ -0,0 +1,12 @@ +#version 450 + +layout (location = 0) out vec2 st; + +void +main () +{ + float x = (gl_VertexIndex & 2); + float y = (gl_VertexIndex & 1); + gl_Position = vec4 (2, 4, 0, 1) * vec4 (x, y, 0, 1) - vec4 (1, 1, 0, 0); + st = vec2(1, 2) * vec2(x, y); +} diff --git a/libs/video/renderer/vulkan/shader/iqm.frag b/libs/video/renderer/vulkan/shader/iqm.frag new file mode 100644 index 000000000..543ee0bdb --- /dev/null +++ b/libs/video/renderer/vulkan/shader/iqm.frag @@ -0,0 +1,48 @@ +#version 450 + +layout (set = 1, binding = 0) uniform sampler2D Skin; + +layout (push_constant) uniform PushConstants { + layout (offset = 68) + uint colorA; + uint colorB; + vec4 base_color; + vec4 fog; +}; + +layout (location = 0) in vec2 texcoord; +layout (location = 1) in vec4 position; +layout (location = 2) in vec3 fnormal; +layout (location = 3) in vec3 ftangent; +layout (location = 4) in vec3 fbitangent; +layout (location = 5) in vec4 color; + +layout (location = 0) out vec4 frag_color; +layout (location = 1) out vec4 frag_emission; +layout (location = 2) out vec4 frag_normal; +layout (location = 3) out vec4 frag_position; + +void +main (void) +{ + vec4 c; + vec4 e; + //vec3 n; + int i; + vec3 normal = normalize (fnormal); + vec3 tangent = normalize (ftangent); + vec3 bitangent = normalize (fbitangent); + //mat3 tbn = mat3 (tangent, bitangent, normal); + + c = texture (Skin, texcoord);// * color; + //c = texture (Skin, vec3 (texcoord, 0)) * color; + //c += texture (Skin, vec3 (texcoord, 1)) * unpackUnorm4x8(colorA); + //c += texture (Skin, vec3 (texcoord, 2)) * unpackUnorm4x8(colorB); + //e = texture (Skin, vec3 (texcoord, 3)); + //n = texture (Skin, vec3 (texcoord, 4)).xyz * 2 - 1; + + frag_color = c; + frag_emission = vec4(0,0,0,1);//e; + frag_normal = vec4(normal,1);//vec4(tbn * n, 1); + frag_position = position; +} diff --git a/libs/video/renderer/vulkan/shader/iqm.vert b/libs/video/renderer/vulkan/shader/iqm.vert new file mode 100644 index 000000000..4b5d06433 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/iqm.vert @@ -0,0 +1,58 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#extension GL_EXT_multiview : enable + +layout (constant_id = 0) const bool IQMDepthOnly = false; + +layout (set = 0, binding = 0) uniform +#include "matrices.h" +; + +layout (set = 2, binding = 0) buffer Bones { + // NOTE these are transposed, so v * m + mat3x4 bones[]; +}; + +layout (push_constant) uniform PushConstants { + mat4 Model; + float blend; +}; + +layout (location = 0) in vec3 vposition; +layout (location = 1) in uvec4 vbones; +layout (location = 2) in vec4 vweights; +layout (location = 3) in vec2 vtexcoord; +layout (location = 4) in vec3 vnormal; +layout (location = 5) in vec4 vtangent; +layout (location = 6) in vec4 vcolor; + +layout (location = 0) out vec2 texcoord; +layout (location = 1) out vec4 position; +layout (location = 2) out vec3 normal; +layout (location = 3) out vec3 tangent; +layout (location = 4) out vec3 bitangent; +layout (location = 5) out vec4 color; + +void +main (void) +{ + mat3x4 m = bones[vbones.x] * vweights.x; + m += bones[vbones.y] * vweights.y; + m += bones[vbones.z] * vweights.z; + m += bones[vbones.w] * vweights.w; + vec4 pos = Model * vec4 (vec4(vposition, 1) * m, 1); + gl_Position = Projection3d * (View[gl_ViewIndex] * pos); + + if (!IQMDepthOnly) { + position = pos; + mat3 adjTrans = mat3 (cross(m[1].xyz, m[2].xyz), + cross(m[2].xyz, m[0].xyz), + cross(m[0].xyz, m[1].xyz)); + normal = normalize (mat3 (Model) * vnormal * adjTrans); + tangent = mat3 (Model) * vtangent.xyz * adjTrans; + tangent = normalize (tangent - dot (tangent, normal) * normal); + bitangent = cross (normal, tangent) * vtangent.w; + texcoord = vtexcoord; + color = vcolor; + } +} diff --git a/libs/video/renderer/vulkan/shader/lighting.frag b/libs/video/renderer/vulkan/shader/lighting.frag new file mode 100644 index 000000000..3e100a55c --- /dev/null +++ b/libs/video/renderer/vulkan/shader/lighting.frag @@ -0,0 +1,122 @@ +#version 450 + +layout (input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput depth; +layout (input_attachment_index = 1, set = 0, binding = 1) uniform subpassInput color; +layout (input_attachment_index = 2, set = 0, binding = 2) uniform subpassInput emission; +layout (input_attachment_index = 3, set = 0, binding = 3) uniform subpassInput normal; +layout (input_attachment_index = 4, set = 0, binding = 4) uniform subpassInput position; + +struct LightData { + vec4 color; // .a is intensity + vec4 position; // .w = 0 -> directional, .w = 1 -> point/cone + vec4 direction; // .w = -cos(cone_angle/2) (1 for omni/dir) + vec4 attenuation; +}; + +#define StyleMask 0x07f +#define ModelMask 0x380 +#define ShadowMask 0xc00 + +#define LM_LINEAR (0 << 7) // light - dist (or radius + dist if -ve) +#define LM_INVERSE (1 << 7) // distFactor1 * light / dist +#define LM_INVERSE2 (2 << 7) // distFactor2 * light / (dist * dist) +#define LM_INFINITE (3 << 7) // light +#define LM_AMBIENT (4 << 7) // light +#define LM_INVERSE3 (5 << 7) // distFactor2 * light / (dist + distFactor2)**2 + +#define ST_NONE (0 << 10) // no shadows +#define ST_PLANE (1 << 10) // single plane shadow map (small spotlight) +#define ST_CASCADE (2 << 10) // cascaded shadow maps +#define ST_CUBE (3 << 10) // cubemap (omni, large spotlight) + +layout (constant_id = 0) const int MaxLights = 768; + +layout (set = 2, binding = 0) uniform sampler2DArrayShadow shadowCascade[MaxLights]; +layout (set = 2, binding = 0) uniform sampler2DShadow shadowPlane[MaxLights]; +layout (set = 2, binding = 0) uniform samplerCubeShadow shadowCube[MaxLights]; + +layout (set = 1, binding = 0) uniform Lights { + LightData lights[MaxLights]; + int lightCount; + //mat4 shadowMat[MaxLights]; + //vec4 shadowCascale[MaxLights]; +}; + +layout (location = 0) out vec4 frag_color; + +float +spot_cone (LightData light, vec3 incoming) +{ + vec3 dir = light.direction.xyz; + float cone = light.direction.w; + float spotdot = dot (incoming, dir); + return 1 - smoothstep (cone, .995 * cone + 0.005, spotdot); +} + +float +diffuse (vec3 incoming, vec3 normal) +{ + float lightdot = dot (incoming, normal); + return clamp (lightdot, 0, 1); +} + +float +shadow_cascade (sampler2DArrayShadow map) +{ + return 1; +} + +float +shadow_plane (sampler2DShadow map) +{ + return 1; +} + +float +shadow_cube (samplerCubeShadow map) +{ + return 1; +} + +void +main (void) +{ + //float d = subpassLoad (depth).r; + vec3 c = subpassLoad (color).rgb; + vec3 e = subpassLoad (emission).rgb; + vec3 n = subpassLoad (normal).rgb; + vec3 p = subpassLoad (position).rgb; + vec3 light = vec3 (0); + + //vec3 minLight = vec3 (0); + for (int i = 0; i < lightCount; i++) { + LightData l = lights[i]; + vec3 dir = l.position.xyz - l.position.w * p; + float r2 = dot (dir, dir); + vec4 a = l.attenuation; + + if (l.position.w * a.w * a.w * r2 >= 1) { + continue; + } + vec4 r = vec4 (r2, sqrt(r2), 1, 0); + vec3 incoming = dir / r.y; + float I = (1 - a.w * r.y) / dot (a, r); + + /*int shadow = lights[i].data & ShadowMask; + if (shadow == ST_CASCADE) { + I *= shadow_cascade (shadowCascade[i]); + } else if (shadow == ST_PLANE) { + I *= shadow_plane (shadowPlane[i]); + } else if (shadow == ST_CUBE) { + I *= shadow_cube (shadowCube[i]); + }*/ + + float namb = dot(l.direction.xyz, l.direction.xyz); + I *= spot_cone (l, incoming) * diffuse (incoming, n); + I = mix (1, I, namb); + light += I * l.color.w * l.color.xyz; + } + //light = max (light, minLight); + + frag_color = vec4 (c * light + e, 1); +} diff --git a/libs/video/renderer/vulkan/shader/line.frag b/libs/video/renderer/vulkan/shader/line.frag new file mode 100644 index 000000000..080248de6 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/line.frag @@ -0,0 +1,11 @@ +#version 450 + +layout (location = 0) in vec4 color; + +layout (location = 0) out vec4 frag_color; + +void +main (void) +{ + frag_color = color; +} diff --git a/libs/video/renderer/vulkan/shader/line.vert b/libs/video/renderer/vulkan/shader/line.vert new file mode 100644 index 000000000..3552215f7 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/line.vert @@ -0,0 +1,17 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +layout (set = 0, binding = 0) uniform +#include "matrices.h" +; +layout (location = 0) in vec2 vertex; +layout (location = 1) in vec4 vcolor; + +layout (location = 0) out vec4 color; + +void +main (void) +{ + gl_Position = Projection2d * vec4 (vertex.xy, 0.0, 1.0); + color = vcolor; +} diff --git a/libs/video/renderer/vulkan/shader/matrices.h b/libs/video/renderer/vulkan/shader/matrices.h new file mode 100644 index 000000000..e66bc265b --- /dev/null +++ b/libs/video/renderer/vulkan/shader/matrices.h @@ -0,0 +1,8 @@ +Matrices { + mat4 Projection3d; + mat4 View[6]; + mat4 Sky; + mat4 Projection2d; + + vec2 ScreenSize; +} diff --git a/libs/video/renderer/vulkan/shader/oit.h b/libs/video/renderer/vulkan/shader/oit.h new file mode 100644 index 000000000..0147adc77 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/oit.h @@ -0,0 +1,20 @@ +#ifndef OIT_SET +#define OIT_SET 2 +#endif + +struct FragData { + vec4 color; + float depth; + int next; +}; + +layout (set = OIT_SET, binding = 0) coherent buffer FragCount { + int numFragments; + int maxFragments; +}; + +layout (set = OIT_SET, binding = 1) buffer Fragments { + FragData fragments[]; +}; + +layout (set = OIT_SET, binding = 2, r32i) coherent uniform iimage2DArray heads; diff --git a/libs/video/renderer/vulkan/shader/oit_blend.finc b/libs/video/renderer/vulkan/shader/oit_blend.finc new file mode 100644 index 000000000..5e2024be6 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/oit_blend.finc @@ -0,0 +1,34 @@ +#include "oit.h" + +vec4 +BlendFrags (vec4 color) +{ + #define MAX_FRAGMENTS 64 + FragData frags[MAX_FRAGMENTS]; + int numFrags = 0; + ivec3 coord = ivec3(gl_FragCoord.xy, gl_ViewIndex); + int index = imageLoad (heads, coord).r; + + //FIXME use a heap and prioritize closer fragments + while (index != -1 && numFrags < MAX_FRAGMENTS) { + frags[numFrags] = fragments[index]; + numFrags++; + index = fragments[index].next; + } + //insertion sort + for (int i = 1; i < numFrags; i++) { + FragData toInsert = frags[i]; + int j = i; + while (j > 0 && toInsert.depth > frags[j - 1].depth) { + frags[j] = frags[j - 1]; + j--; + } + frags[j] = toInsert; + } + + for (int i = 0; i < numFrags; i++) { + color = mix (color, frags[i].color, + clamp (frags[i].color.a, 0.0f, 1.0f)); + } + return color; +} diff --git a/libs/video/renderer/vulkan/shader/oit_store.finc b/libs/video/renderer/vulkan/shader/oit_store.finc new file mode 100644 index 000000000..b51ba0d11 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/oit_store.finc @@ -0,0 +1,14 @@ +#include "oit.h" + +void +StoreFrag (vec4 color, float depth) +{ + int index = atomicAdd (numFragments, 1); + ivec3 coord = ivec3(gl_FragCoord.xy, gl_ViewIndex); + if (index < maxFragments) { + int prevIndex = imageAtomicExchange (heads, coord, index); + fragments[index].color = color; + fragments[index].depth = depth; + fragments[index].next = prevIndex; + } +} diff --git a/libs/video/renderer/vulkan/shader/output.frag b/libs/video/renderer/vulkan/shader/output.frag new file mode 100644 index 000000000..46822c257 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/output.frag @@ -0,0 +1,15 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +layout (set = 0, binding = 0) uniform +#include "matrices.h" +; + +layout (set = 1, binding = 0) uniform sampler2D Input; +layout (location = 0) out vec4 frag_color; + +void +main (void) +{ + frag_color = texture (Input, gl_FragCoord.xy * ScreenSize); +} diff --git a/libs/video/renderer/vulkan/shader/particle.frag b/libs/video/renderer/vulkan/shader/particle.frag new file mode 100644 index 000000000..304f07d2d --- /dev/null +++ b/libs/video/renderer/vulkan/shader/particle.frag @@ -0,0 +1,24 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#extension GL_EXT_multiview : enable + +#include "oit_store.finc" + +layout (location = 0) in vec4 uv_tr; +layout (location = 1) in vec4 color; + +layout(early_fragment_tests) in; + +void +main (void) +{ + vec4 c = color; + vec2 x = uv_tr.xy; + + float a = 1 - dot (x, x); + if (a <= 0) { + discard; + } + c *= (a); + StoreFrag (c, gl_FragCoord.z); +} diff --git a/libs/video/renderer/vulkan/shader/particle.geom b/libs/video/renderer/vulkan/shader/particle.geom new file mode 100644 index 000000000..3027f0908 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/particle.geom @@ -0,0 +1,55 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +layout (set = 0, binding = 0) uniform +#include "matrices.h" +; + +layout (points) in; +layout (triangle_strip, max_vertices = 4) out; +layout (location = 0) in vec4 velocity[]; +layout (location = 1) in vec4 color[]; +layout (location = 2) in vec4 ramp[]; + +layout (location = 0) out vec4 uv_tr; +layout (location = 1) out vec4 o_color; + +void +main() +{ + vec4 pos = gl_in[0].gl_Position; + vec4 tr = vec4 (0, 0, ramp[0].xy); + float s = ramp[0].z; + vec4 d, p; + vec4 c = color[0]; + + d = vec4 (-1, 1, 0, 0); + p = pos + s * d; + gl_Position = Projection3d * p; + uv_tr = d + tr; + o_color = c; + EmitVertex (); + + d = vec4 (-1, -1, 0, 0); + p = pos + s * d; + gl_Position = Projection3d * p; + uv_tr = d + tr; + o_color = c; + EmitVertex (); + + d = vec4 (1, 1, 0, 0); + p = pos + s * d; + gl_Position = Projection3d * p; + uv_tr = d + tr; + o_color = c; + EmitVertex (); + + d = vec4 (1, -1, 0, 0); + p = pos + s * d; + gl_Position = Projection3d * p; + uv_tr = d + tr; + o_color = c; + EmitVertex (); + + EndPrimitive (); +} diff --git a/libs/video/renderer/vulkan/shader/particle.vert b/libs/video/renderer/vulkan/shader/particle.vert new file mode 100644 index 000000000..4be3dec33 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/particle.vert @@ -0,0 +1,34 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#extension GL_EXT_multiview : enable + +layout (set = 0, binding = 0) uniform +#include "matrices.h" +; +layout (set = 1, binding = 0) uniform sampler2D Palette; + +layout (push_constant) uniform PushConstants { + mat4 Model; +}; + +layout (location = 0) in vec4 position; +layout (location = 1) in vec4 velocity; +layout (location = 2) in vec4 color; +layout (location = 3) in vec4 ramp; + +layout (location = 0) out vec4 o_velocity; +layout (location = 1) out vec4 o_color; +layout (location = 2) out vec4 o_ramp; + +void +main (void) +{ + // geometry shader will take care of Projection + gl_Position = View[gl_ViewIndex] * (Model * position); + o_velocity = View[gl_ViewIndex] * (Model * velocity); + uint c = floatBitsToInt (color.x); + uint x = c & 0x0f; + uint y = (c >> 4) & 0x0f; + o_color = texture (Palette, vec2 (x, y) / 15.0); + o_ramp = ramp; +} diff --git a/libs/video/renderer/vulkan/shader/partphysics.comp b/libs/video/renderer/vulkan/shader/partphysics.comp new file mode 100644 index 000000000..9c9a933f8 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/partphysics.comp @@ -0,0 +1,60 @@ +#version 450 + +layout (local_size_x = 1, local_size_y = 1) in; + +struct Particle { + vec4 pos; + vec4 vel; + vec4 color; + float tex; + float ramp; + float scale; + float live; +}; + +struct Parameters { + vec4 drag; // [dx, dy, dz, grav scale] + vec4 ramp; // [rate, max, alpha rate, scale rate] +}; + +layout(std140, set = 0, binding = 0) buffer ParticleStates { + Particle particles[]; +}; + +layout(std140, set = 0, binding = 1) buffer ParticleParameters { + Parameters parameters[]; +}; + +//doubles as VkDrawIndirectCommand +layout(std140, set = 0, binding = 2) buffer ParticleSystem { + uint vertexCount; + uint particleCount; //instanceCount + uint firstVertex; + uint firstInstance; +}; + +layout (push_constant) uniform PushConstants { + vec4 gravity; + float dT; +}; + +void +main () +{ + uint ind = gl_GlobalInvocationID.x; + if (ind >= particleCount) { + return; + } + Particle part = particles[ind]; + Parameters parm = parameters[ind]; + + part.pos += dT * part.vel; + part.vel += dT * (part.vel * parm.drag + gravity * parm.drag.w); + + part.ramp += dT * parm.ramp.x; + part.scale += dT * parm.ramp.z; + part.color.a -= dT * parm.ramp.a; + part.live -= dT; + + particles[ind] = part; +} diff --git a/libs/video/renderer/vulkan/shader/partupdate.comp b/libs/video/renderer/vulkan/shader/partupdate.comp new file mode 100644 index 000000000..5a5ccd56f --- /dev/null +++ b/libs/video/renderer/vulkan/shader/partupdate.comp @@ -0,0 +1,108 @@ +#version 450 + +layout (constant_id = 0) const int MaxParticles = 2048; + +layout (local_size_x = 1, local_size_y = 1) in; + +struct Particle { + vec4 pos; + vec4 vel; + vec4 color; + float tex; + float ramp; + float scale; + float live; +}; + +struct Parameters { + vec4 drag; // [dx, dy, dz, grav scale] + vec4 ramp; // [rate, max, alpha rate, scale rate] +}; + +layout(std140, set = 0, binding = 0) buffer OutStates { + Particle particles[]; +} outStates; + +layout(std140, set = 0, binding = 1) buffer OutParameters { + Parameters parameters[]; +} outParameters; + +//doubles as VkDrawIndirectCommand +layout(std140, set = 0, binding = 2) buffer OutSystem { + uint vertexCount; + uint particleCount; //instanceCount + uint firstVertex; + uint firstInstance; +} outSystem; + +layout(std140, set = 1, binding = 0) buffer InStates { + Particle particles[]; +} inStates; + +layout(std140, set = 1, binding = 1) buffer InParameters { + Parameters parameters[]; +} inParameters; + +//doubles as VkDrawIndirectCommand +layout(std140, set = 1, binding = 2) buffer InSystem { + uint vertexCount; + uint particleCount; //instanceCount + uint firstVertex; + uint firstInstance; +} inSystem; + +layout(std140, set = 2, binding = 0) buffer NewStates { + Particle particles[]; +} newStates; + +layout(std140, set = 2, binding = 1) buffer NewParameters { + Parameters parameters[]; +} newParameters; + +//doubles as VkDrawIndirectCommand +layout(std140, set = 2, binding = 2) buffer NewSystem { + uint vertexCount; + uint particleCount; //instanceCount + uint firstVertex; + uint firstInstance; +} newSystem; + +bool +is_dead (in Particle part, in Parameters parm) +{ + if (part.live <= 0) { + return true; + } + if (part.ramp >= parm.ramp.y || part.color.a <= 0 || part.scale <= 0) { + return true; + } + return false; +} + +void +main () +{ + uint j = 0; + // compact existing partles removing dead particles + for (uint i = 0; i < inSystem.particleCount; i++) { + if (is_dead (inStates.particles[i], inParameters.parameters[i])) { + continue; + } + outStates.particles[j] = inStates.particles[i]; + outParameters.parameters[j] = inParameters.parameters[i]; + j++; + } + // inject any new particles that aren't DOA + for (uint i = 0; i < newSystem.particleCount && j < MaxParticles; i++) { + if (is_dead (newStates.particles[i], newParameters.parameters[i])) { + continue; + } + outStates.particles[j] = newStates.particles[i]; + outParameters.parameters[j] = newParameters.parameters[i]; + j++; + } + outSystem.vertexCount = newSystem.vertexCount; + outSystem.particleCount = j; + outSystem.firstVertex = newSystem.firstVertex; + outSystem.firstInstance = newSystem.firstInstance; +} diff --git a/libs/video/renderer/vulkan/shader/passthrough.vert b/libs/video/renderer/vulkan/shader/passthrough.vert new file mode 100644 index 000000000..0283a53b3 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/passthrough.vert @@ -0,0 +1,8 @@ +#version 450 + +layout (location = 0) in vec4 app_position; + +void main() +{ + gl_Position = app_position; +} diff --git a/libs/video/renderer/vulkan/shader/pushcolor.frag b/libs/video/renderer/vulkan/shader/pushcolor.frag new file mode 100644 index 000000000..d19413fe1 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/pushcolor.frag @@ -0,0 +1,12 @@ +#version 450 + +layout (location = 0) out vec4 frag_color; + +layout (push_constant) uniform ColorBlock { + vec4 Color; +} PushConstant; + +void main() +{ + frag_color = PushConstant.Color; +} diff --git a/libs/video/renderer/vulkan/shader/quakebsp.frag b/libs/video/renderer/vulkan/shader/quakebsp.frag new file mode 100644 index 000000000..750395c83 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/quakebsp.frag @@ -0,0 +1,123 @@ +#version 450 + +layout (set = 0, binding = 1) uniform sampler2D Texture; +layout (set = 0, binding = 2) uniform sampler2D GlowMap; +layout (set = 0, binding = 3) uniform sampler2D LightMap; +layout (set = 0, binding = 4) uniform sampler2DArray SkySheet; +layout (set = 0, binding = 5) uniform samplerCube SkyCube; + +layout (push_constant) uniform PushConstants { + vec4 fog; + float time; + float alpha; +}; + +layout (location = 0) in vec4 tl_st; +layout (location = 1) in vec3 direction; + +layout (location = 0) out vec4 frag_color; + +layout (constant_id = 0) const bool doWarp = false; +layout (constant_id = 1) const bool doLight = true; +layout (constant_id = 2) const bool doSkyCube = false; +layout (constant_id = 3) const bool doSkySheet = false; + +const float PI = 3.14159265; +const float SPEED = 20.0; +const float CYCLE = 128.0; +const float FACTOR = PI * 2.0 / CYCLE; +const vec2 BIAS = vec2 (1.0, 1.0); +const float SCALE = 8.0; + +vec2 +warp_st (vec2 st, float time) +{ + vec2 angle = st.ts * CYCLE / 2.0; + vec2 phase = vec2 (time, time) * SPEED; + return st + (sin ((angle + phase) * FACTOR) + BIAS) / SCALE; +} + +vec4 +fogBlend (vec4 color) +{ + float az = fog.a * gl_FragCoord.z / gl_FragCoord.w; + vec3 fog_color = fog.rgb; + float fog_factor = exp (-az * az); + + return vec4 (mix (fog_color.rgb, color.rgb, fog_factor), color.a); +} + +vec4 +sky_sheet (vec3 dir, float time) +{ + float len; + vec2 flow = vec2 (1.0, 1.0); + vec2 base; + vec3 st1, st2; + vec4 c1, c2, c; + + dir.z *= 3.0; + len = dot (dir, dir); + len = SCALE * inversesqrt (len); + base = dir.yx * vec2(1.0, -1.0) * len; + + st1 = vec3 (base + flow * time / 8.0, 0); + st2 = vec3 (base + flow * time / 16.0, 1); + + c1 = texture (SkySheet, st1); + c2 = texture (SkySheet, st2); + + c = vec4 (mix (c2.rgb, c1.rgb, c1.a), max (c1.a, c2.a)); + + return c; +} + +vec4 +sky_cube (vec3 dir, float time) +{ + // NOTE: quake's world is right-handed with Z up and X forward, but + // Vulkan's cube maps are left-handed with Y up and Z forward. The + // rotation to X foward is done by the Sky matrix so all that's left + // to do here is swizzle the Y and Z coordinates + return texture (SkyCube, dir.xzy); +} + +vec4 +sky_color (vec3 dir, float time) +{ + if (!doSkySheet) { + return vec4 (1, 0, 1, 1); + //return sky_cube (dir, time); + } if (!doSkyCube) { + return sky_sheet (dir, time); + } else { + // can see through the sheet (may look funny when looking down) + // maybe have 4 sheet layers instead of 2? + vec4 c1 = sky_sheet (dir, time); + vec4 c2 = sky_cube (dir, time); + return vec4 (mix (c2.rgb, c1.rgb, c1.a), max (c1.a, c2.a)); + return vec4 (1, 0, 1, 1); + } +} + +void +main (void) +{ + vec4 c; + vec2 t_st = tl_st.xy; + vec2 l_st = tl_st.zw; + + if (doWarp) { + t_st = warp_st (t_st, time); + } + if (doSkyCube || doSkySheet) { + c = sky_color (direction, time); + } else { + c = texture (Texture, t_st); + if (doLight) { + c *= vec4 (texture (LightMap, l_st).xyz, 1); + } + c += texture (GlowMap, t_st); + } + frag_color = c;//fogBlend (c); +} diff --git a/libs/video/renderer/vulkan/shader/quakebsp.vert b/libs/video/renderer/vulkan/shader/quakebsp.vert new file mode 100644 index 000000000..d10bf89df --- /dev/null +++ b/libs/video/renderer/vulkan/shader/quakebsp.vert @@ -0,0 +1,31 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#extension GL_EXT_multiview : enable + +#include "entity.h" + +layout (set = 0, binding = 0) uniform +#include "matrices.h" +; + +layout (set = 1, binding = 0) buffer Entities { + Entity entities[]; +}; + +layout (location = 0) in vec4 vertex; +layout (location = 1) in vec4 tl_uv; +layout (location = 2) in uint entid; + +layout (location = 0) out vec4 tl_st; +layout (location = 1) out vec3 direction; +layout (location = 2) out vec4 color; + +void +main (void) +{ + vec3 vert = vertex * entities[entid].transform; + gl_Position = Projection3d * (View[gl_ViewIndex] * vec4 (vert, 1)); + direction = (Sky * vertex).xyz; + tl_st = tl_uv; + color = entities[entid].color; +} diff --git a/libs/video/renderer/vulkan/shader/shadow.geom b/libs/video/renderer/vulkan/shader/shadow.geom new file mode 100644 index 000000000..ed4f4de23 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/shadow.geom @@ -0,0 +1,25 @@ +#version 450 + +layout (triangles) in; +layout (triangle_strip, max_vertices = 3) out; + +layout (constant_id = 0) const int MaxLights = 128; + +layout (set = 0, binding = 0) uniform UBO { + mat4 vp[MaxLights]; +} ubo; + +layout (location = 0) in int InstanceIndex[]; + +void +main (void) +{ + int index = InstanceIndex[0]; + + for (int i = 0; i < gl_in.length(); i++) { + gl_Layer = index; + gl_Position = ubo.vp[index] * gl_in[i].gl_Position; + EmitVertex(); + } + EndPrimitive(); +} diff --git a/libs/video/renderer/vulkan/shader/slice.vert b/libs/video/renderer/vulkan/shader/slice.vert new file mode 100644 index 000000000..ca67404cd --- /dev/null +++ b/libs/video/renderer/vulkan/shader/slice.vert @@ -0,0 +1,32 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +layout (set = 0, binding = 0) uniform +#include "matrices.h" +; + +// rg -> offset x,y +// ba -> texture u,v +layout (set = 1, binding = 1) uniform textureBuffer glyph_data; + +// per instance data +layout (location = 0) in uint glyph_index; +layout (location = 1) in vec4 glyph_color; +layout (location = 2) in vec2 glyph_position; +layout (location = 3) in vec2 glyph_offset; // for 9-slice + +layout (location = 0) out vec2 uv; +layout (location = 1) out vec4 color; + +void +main (void) +{ + vec2 offset = vec2 ((gl_VertexIndex & 4) >> 2, (gl_VertexIndex & 8) >> 3); + vec4 glyph = texelFetch (glyph_data, int(glyph_index + gl_VertexIndex)); + // offset stored in glyph components 0 and 1 + vec2 position = glyph_position + glyph.xy + offset * glyph_offset; + gl_Position = Projection2d * vec4 (position.xy, 0.0, 1.0); + // texture uv stored in glyph components 2 and 3 + uv = glyph.pq; + color = glyph_color; +} diff --git a/libs/video/renderer/vulkan/shader/sprite_depth.frag b/libs/video/renderer/vulkan/shader/sprite_depth.frag new file mode 100644 index 000000000..d44ba3725 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/sprite_depth.frag @@ -0,0 +1,24 @@ +#version 450 + +layout (set = 1, binding = 1) uniform sampler2DArray Texture; + +layout (push_constant) uniform PushConstants { + layout (offset = 64) + int frame; + int spriteind; + // two slots + vec4 fog; +}; + +layout (location = 0) in vec2 st; + +void +main (void) +{ + vec4 pix; + + pix = texture (Texture, vec3 (st, frame)); + if (pix.a < 0.5) { + discard; + } +} diff --git a/libs/video/renderer/vulkan/shader/sprite_depth.vert b/libs/video/renderer/vulkan/shader/sprite_depth.vert new file mode 100644 index 000000000..1eca8e043 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/sprite_depth.vert @@ -0,0 +1,30 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#extension GL_EXT_multiview : enable + +layout (set = 0, binding = 0) uniform +#include "matrices.h" +; + +layout (set = 1, binding = 0) uniform Vertices { + vec4 xyuv[4]; +}; + +layout (push_constant) uniform PushConstants { + mat4 Model; + int frame; +}; + +layout (location = 0) out vec2 st; + +void +main (void) +{ + vec4 v = xyuv[frame * 4 + gl_VertexIndex]; + + vec4 pos = Model[3]; + pos += v.x * Model[1] + v.y * Model[2]; + + gl_Position = Projection3d * (View[gl_ViewIndex] * pos); + st = v.zw; +} diff --git a/libs/video/renderer/vulkan/shader/sprite_gbuf.frag b/libs/video/renderer/vulkan/shader/sprite_gbuf.frag new file mode 100644 index 000000000..29d9b65c9 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/sprite_gbuf.frag @@ -0,0 +1,37 @@ +#version 450 + +layout (set = 1, binding = 1) uniform sampler2DArray Texture; + +layout (push_constant) uniform PushConstants { + layout (offset = 64) + int frame; + int spriteind; + // two slots + vec4 fog; +}; + +layout (location = 0) in vec2 st; +layout (location = 1) in vec4 position; +layout (location = 2) in vec3 normal; + +layout(early_fragment_tests) in; + +layout (location = 0) out vec4 frag_color; +layout (location = 1) out vec4 frag_emission; +layout (location = 2) out vec4 frag_normal; +layout (location = 3) out vec4 frag_position; + +void +main (void) +{ + vec4 pix; + + pix = texture (Texture, vec3 (st, frame)); + if (pix.a < 0.5) { + discard; + } + frag_color = vec4(0,0,0,1); + frag_emission = pix; + frag_normal = vec4(normal, 1); + frag_position = position; +} diff --git a/libs/video/renderer/vulkan/shader/sprite_gbuf.vert b/libs/video/renderer/vulkan/shader/sprite_gbuf.vert new file mode 100644 index 000000000..e96d25848 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/sprite_gbuf.vert @@ -0,0 +1,34 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#extension GL_EXT_multiview : enable + +layout (set = 0, binding = 0) uniform +#include "matrices.h" +; + +layout (set = 1, binding = 0) uniform Vertices { + vec4 xyuv[4]; +}; + +layout (push_constant) uniform PushConstants { + mat4 Model; + int frame; +}; + +layout (location = 0) out vec2 st; +layout (location = 1) out vec4 position; +layout (location = 2) out vec3 normal; + +void +main (void) +{ + vec4 v = xyuv[frame * 4 + gl_VertexIndex]; + + vec4 pos = Model[3]; + pos += v.x * Model[1] + v.y * Model[2]; + + gl_Position = Projection3d * (View[gl_ViewIndex] * pos); + st = v.zw; + position = pos; + normal = -vec3(Model[0]); +} diff --git a/libs/video/renderer/vulkan/shader/twod.frag b/libs/video/renderer/vulkan/shader/twod.frag new file mode 100644 index 000000000..b705b270a --- /dev/null +++ b/libs/video/renderer/vulkan/shader/twod.frag @@ -0,0 +1,17 @@ +#version 450 + +layout (set = 1, binding = 0) uniform sampler2D Texture; + +layout (location = 0) in vec2 st; +layout (location = 1) in vec4 color; + +layout (location = 0) out vec4 frag_color; + +void +main (void) +{ + vec4 pix; + + pix = texture (Texture, st); + frag_color = pix * color; +} diff --git a/libs/video/renderer/vulkan/shader/twod.vert b/libs/video/renderer/vulkan/shader/twod.vert new file mode 100644 index 000000000..d6501a633 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/twod.vert @@ -0,0 +1,27 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +layout (set = 0, binding = 0) uniform +#include "matrices.h" +; +/** Vertex position. + + x, y, s, t + + \a vertex provides the onscreen location at which to draw the icon + (\a x, \a y) and texture coordinate for the icon (\a s=z, \a t=w). +*/ +layout (location = 0) in vec2 vertex; +layout (location = 1) in vec2 uv; +layout (location = 2) in vec4 vcolor; + +layout (location = 0) out vec2 st; +layout (location = 1) out vec4 color; + +void +main (void) +{ + gl_Position = Projection2d * vec4 (vertex.xy, 0.0, 1.0); + st = uv; + color = vcolor; +} diff --git a/libs/video/renderer/vulkan/shader/twod_depth.frag b/libs/video/renderer/vulkan/shader/twod_depth.frag new file mode 100644 index 000000000..14afa7412 --- /dev/null +++ b/libs/video/renderer/vulkan/shader/twod_depth.frag @@ -0,0 +1,17 @@ +#version 450 + +layout (set = 1, binding = 0) uniform sampler2D Texture; + +layout (location = 0) in vec2 st; +layout (location = 1) in vec4 color; + +void +main (void) +{ + vec4 pix; + + pix = texture (Texture, st); + if (pix.a * color.a < 1) { + discard; + } +} diff --git a/libs/video/renderer/vulkan/shader/waterwarp.frag b/libs/video/renderer/vulkan/shader/waterwarp.frag new file mode 100644 index 000000000..c0dce195f --- /dev/null +++ b/libs/video/renderer/vulkan/shader/waterwarp.frag @@ -0,0 +1,31 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +layout (set = 0, binding = 0) uniform +#include "matrices.h" +; + +layout (set = 1, binding = 0) uniform sampler2D Input; + +layout (push_constant) uniform PushConstants { + float time; +}; + +layout (location = 0) in vec2 uv; + +layout (location = 0) out vec4 frag_color; + +const float S = 0.15625; +const float F = 2.5; +const float A = 0.01; +const vec2 B = vec2 (1, 1); +const float PI = 3.14159265; + +void +main (void) +{ + vec2 st; + st = uv * (1.0 - 2.0*A) + A * (B + sin ((time * S + F * uv.yx) * 2.0*PI)); + vec4 c = texture (Input, st); + frag_color = c;//vec4(uv, c.x, 1); +} diff --git a/libs/video/renderer/vulkan/smp_quake.plist b/libs/video/renderer/vulkan/smp_quake.plist new file mode 100644 index 000000000..0c4db686f --- /dev/null +++ b/libs/video/renderer/vulkan/smp_quake.plist @@ -0,0 +1,138 @@ +samplers = { + quakepic = { + magFilter = nearest; + minFilter = nearest; + mipmapMode = nearest; + addressModeU = clamp_to_edge; + addressModeV = clamp_to_edge; + addressModeW = clamp_to_edge; + mipLodBias = 0; + anisotropyEnable = false; + maxAnisotropy = 0; + compareEnable = false; + compareOp = always; + minLod = 0; + maxLod = 0; + borderColor = float_transparent_black; + unnormalizedCoordinates = false; + }; + glyph = { + magFilter = linear; + minFilter = linear; + mipmapMode = linear; + addressModeU = clamp_to_edge; + addressModeV = clamp_to_edge; + addressModeW = clamp_to_edge; + mipLodBias = 0; + anisotropyEnable = false; + maxAnisotropy = 0; + compareEnable = false; + compareOp = always; + minLod = 0; + maxLod = 0; + borderColor = float_transparent_black; + unnormalizedCoordinates = false; + }; + palette_sampler = { + magFilter = nearest; + minFilter = nearest; + mipmapMode = nearest; + addressModeU = clamp_to_edge; + addressModeV = clamp_to_edge; + addressModeW = clamp_to_edge; + mipLodBias = 0; + anisotropyEnable = false; + maxAnisotropy = 0; + compareEnable = false; + compareOp = always; + minLod = 0; + maxLod = 4; + borderColor = float_transparent_black; + unnormalizedCoordinates = false; + }; + quakebsp_sampler = { + magFilter = linear; + minFilter = linear; + mipmapMode = linear; + addressModeU = repeat; + addressModeV = repeat; + addressModeW = repeat; + mipLodBias = 0; + anisotropyEnable = false; + maxAnisotropy = 0; + compareEnable = false; + compareOp = always; + minLod = 0; + maxLod = 4; + borderColor = float_transparent_black; + unnormalizedCoordinates = false; + }; + alias_sampler = { + magFilter = linear; + minFilter = linear; + mipmapMode = linear; + addressModeU = clamp_to_edge; + addressModeV = clamp_to_edge; + addressModeW = clamp_to_edge; + mipLodBias = 0; + anisotropyEnable = false; + maxAnisotropy = 0; + compareEnable = false; + compareOp = always; + minLod = 0; + maxLod = 1000; + borderColor = float_transparent_black; + unnormalizedCoordinates = false; + }; + sprite_sampler = { + magFilter = linear; + minFilter = linear; + mipmapMode = linear; + addressModeU = clamp_to_edge; + addressModeV = clamp_to_edge; + addressModeW = clamp_to_edge; + mipLodBias = 0; + anisotropyEnable = false; + maxAnisotropy = 0; + compareEnable = false; + compareOp = always; + minLod = 0; + maxLod = 1000; + borderColor = float_transparent_black; + unnormalizedCoordinates = false; + }; + shadow_sampler = { + magFilter = linear; + minFilter = linear; + mipmapMode = linear; + addressModeU = clamp_to_edge; + addressModeV = clamp_to_edge; + addressModeW = clamp_to_edge; + mipLodBias = 0; + anisotropyEnable = false; + maxAnisotropy = 0; + compareEnable = true; + compareOp = greater_or_equal; + minLod = 0; + maxLod = 1000; + borderColor = float_transparent_black; + unnormalizedCoordinates = false; + }; + linear = { + magFilter = linear; + minFilter = linear; + mipmapMode = linear; + addressModeU = clamp_to_edge; + addressModeV = clamp_to_edge; + addressModeW = clamp_to_edge; + mipLodBias = 0; + anisotropyEnable = false; + maxAnisotropy = 0; + compareEnable = false; + compareOp = always; + minLod = 0; + maxLod = 0; + borderColor = float_transparent_black; + unnormalizedCoordinates = false; + }; +}; diff --git a/libs/video/renderer/vulkan/staging.c b/libs/video/renderer/vulkan/staging.c new file mode 100644 index 000000000..f7f0b10f6 --- /dev/null +++ b/libs/video/renderer/vulkan/staging.c @@ -0,0 +1,342 @@ +/* + staging.c + + Vulkan staging buffer manager + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/dstring.h" + +#include "QF/Vulkan/barrier.h" +#include "QF/Vulkan/buffer.h" +#include "QF/Vulkan/command.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/staging.h" + +qfv_stagebuf_t * +QFV_CreateStagingBuffer (qfv_device_t *device, const char *name, size_t size, + VkCommandPool cmdPool) +{ + size_t atom = device->physDev->properties->limits.nonCoherentAtomSize; + qfv_devfuncs_t *dfunc = device->funcs; + dstring_t *str = dstring_new (); + + qfv_stagebuf_t *stage = calloc (1, sizeof (qfv_stagebuf_t)); + stage->atom_mask = atom - 1; + size = (size + stage->atom_mask) & ~stage->atom_mask; + stage->device = device; + stage->cmdPool = cmdPool; + stage->buffer = QFV_CreateBuffer (device, size, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT + //FIXME make a param + | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER, stage->buffer, + dsprintf (str, "staging:buffer:%s", name)); + stage->memory = QFV_AllocBufferMemory (device, stage->buffer, + VK_MEMORY_PROPERTY_HOST_CACHED_BIT, + size, 0); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, stage->memory, + dsprintf (str, "staging:memory:%s", name)); + stage->size = size; + stage->end = size; + + dfunc->vkMapMemory (device->dev, stage->memory, 0, size, 0, &stage->data); + QFV_BindBufferMemory (device, stage->buffer, stage->memory, 0); + + int count = RB_buffer_size (&stage->packets); + + __auto_type bufferset = QFV_AllocCommandBufferSet (count, alloca); + QFV_AllocateCommandBuffers (device, cmdPool, 0, bufferset); + for (int i = 0; i < count; i++) { + qfv_packet_t *packet = &stage->packets.buffer[i]; + packet->stage = stage; + packet->cmd = bufferset->a[i]; + packet->fence = QFV_CreateFence (device, 1); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_COMMAND_BUFFER, + packet->cmd, + dsprintf (str, "staging:packet:cmd:%s:%d", + name, i)); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_FENCE, + packet->fence, + dsprintf (str, "staging:packet:fence:%s:%d", + name, i)); + packet->offset = 0; + packet->length = 0; + } + dstring_delete (str); + return stage; +} + +void +QFV_DestroyStagingBuffer (qfv_stagebuf_t *stage) +{ + qfv_device_t *device = stage->device; + qfv_devfuncs_t *dfunc = device->funcs; + + int count = RB_buffer_size (&stage->packets); + __auto_type fences = QFV_AllocFenceSet (count, alloca); + __auto_type cmdBuf = QFV_AllocCommandBufferSet (count, alloca); + for (int i = 0; i < count; i++) { + fences->a[i] = stage->packets.buffer[i].fence; + cmdBuf->a[i] = stage->packets.buffer[i].cmd; + } + dfunc->vkWaitForFences (device->dev, fences->size, fences->a, VK_TRUE, + 5000000000ull); + for (int i = 0; i < count; i++) { + dfunc->vkDestroyFence (device->dev, fences->a[i], 0); + } + dfunc->vkFreeCommandBuffers (device->dev, stage->cmdPool, + cmdBuf->size, cmdBuf->a); + + dfunc->vkUnmapMemory (device->dev, stage->memory); + dfunc->vkFreeMemory (device->dev, stage->memory, 0); + dfunc->vkDestroyBuffer (device->dev, stage->buffer, 0); + free (stage); +} + +void +QFV_FlushStagingBuffer (qfv_stagebuf_t *stage, size_t offset, size_t size) +{ + qfv_device_t *device = stage->device; + qfv_devfuncs_t *dfunc = device->funcs; + + offset &= ~stage->atom_mask; + size = (size + stage->atom_mask) & ~stage->atom_mask; + VkMappedMemoryRange range = { + VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0, + stage->memory, offset, size + }; + dfunc->vkFlushMappedMemoryRanges (device->dev, 1, &range); +} + +static void +release_space (qfv_stagebuf_t *stage, size_t offset, size_t length) +{ + if (stage->space_end != offset + && offset != 0 + && stage->space_end != stage->end) { + Sys_Error ("staging: out of sequence packet release"); + } + if (stage->space_end == stage->end) { + stage->space_end = 0; + stage->end = stage->size; + } + stage->space_end += length; +} + +static void * +acquire_space (qfv_packet_t *packet, size_t size) +{ + qfv_stagebuf_t *stage = packet->stage; + qfv_device_t *device = stage->device; + qfv_devfuncs_t *dfunc = device->funcs; + + // clean up after any completed packets + while (RB_DATA_AVAILABLE (stage->packets) > 1) { + qfv_packet_t *p = RB_PEEK_DATA (stage->packets, 0); + if (dfunc->vkGetFenceStatus (device->dev, p->fence) != VK_SUCCESS) { + break; + } + release_space (stage, p->offset, p->length); + RB_RELEASE (stage->packets, 1); + } + + if (size > stage->size) { + // utterly impossible allocation + return 0; + } + + // if the staging buffer has been freed up and no data is assigned to the + // single existing packet, then ensure the packet starts at the beginning + // of the staging buffer in order to maximize the space available to it + // (some of the tests are redundant since if any space is assigned to a + // packet, the buffer cannot be fully freed up) + if (stage->space_end == stage->space_start + && RB_DATA_AVAILABLE (stage->packets) == 1 + && packet->length == 0) { + stage->space_end = 0; + stage->space_start = 0; + packet->offset = 0; + } + if (stage->space_start >= stage->space_end) { + // all the space to the actual end of the buffer is free + if (stage->space_start + size <= stage->size) { + void *data = (byte *) stage->data + stage->space_start; + stage->space_start += size; + return data; + } + // doesn't fit at the end of the buffer, try the beginning but only + // if the packet can be moved (no spaced has been allocated to it yet) + if (packet->length > 0) { + // can't move it + return 0; + } + // mark the unused end of the buffer such that it gets reclaimed + // properly when the preceeding packet is freed + stage->end = stage->space_start; + stage->space_start = 0; + packet->offset = 0; + } + while (stage->space_start + size > stage->space_end + && RB_DATA_AVAILABLE (stage->packets) > 1) { + packet = RB_PEEK_DATA (stage->packets, 0); + dfunc->vkWaitForFences (device->dev, 1, &packet->fence, VK_TRUE, + ~0ull); + release_space (stage, packet->offset, packet->length); + RB_RELEASE (stage->packets, 1); + } + if (stage->space_start + size > stage->space_end) { + return 0; + } + void *data = (byte *) stage->data + stage->space_start; + stage->space_start += size; + return data; +} + +qfv_packet_t * +QFV_PacketAcquire (qfv_stagebuf_t *stage) +{ + qfv_device_t *device = stage->device; + qfv_devfuncs_t *dfunc = device->funcs; + + qfv_packet_t *packet = 0; + if (!RB_SPACE_AVAILABLE (stage->packets)) { + // need to wait for a packet to become available + packet = RB_PEEK_DATA (stage->packets, 0); + dfunc->vkWaitForFences (device->dev, 1, &packet->fence, VK_TRUE, + ~0ull); + release_space (stage, packet->offset, packet->length); + RB_RELEASE (stage->packets, 1); + } + packet = RB_ACQUIRE (stage->packets, 1); + + packet->offset = stage->space_start; + packet->length = 0; + + dfunc->vkResetFences (device->dev, 1, &packet->fence); + dfunc->vkResetCommandBuffer (packet->cmd, 0); + VkCommandBufferBeginInfo beginInfo = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, 0, + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, 0, + }; + dfunc->vkBeginCommandBuffer (packet->cmd, &beginInfo); + + return packet; +} + +void * +QFV_PacketExtend (qfv_packet_t *packet, size_t size) +{ + void *data = acquire_space (packet, size); + if (data) { + packet->length += size; + } + return data; +} + +void +QFV_PacketSubmit (qfv_packet_t *packet) +{ + qfv_stagebuf_t *stage = packet->stage; + qfv_device_t *device = stage->device; + qfv_devfuncs_t *dfunc = device->funcs; + + if (packet->length) { + QFV_FlushStagingBuffer (stage, packet->offset, packet->length); + } + + dfunc->vkEndCommandBuffer (packet->cmd); + //XXX it may become necessary to pass in semaphores etc (maybe add to + //packet?) + VkSubmitInfo submitInfo = { + VK_STRUCTURE_TYPE_SUBMIT_INFO, 0, + 0, 0, 0, + 1, &packet->cmd, + 0, 0, + }; + // The fence was reset when the packet was acquired + dfunc->vkQueueSubmit (device->queue.queue, 1, &submitInfo, packet->fence); +} + +void +QFV_PacketCopyBuffer (qfv_packet_t *packet, + VkBuffer dstBuffer, VkDeviceSize offset, + const qfv_bufferbarrier_t *dstBarrier) +{ + qfv_devfuncs_t *dfunc = packet->stage->device->funcs; + qfv_bufferbarrier_t bb = bufferBarriers[qfv_BB_Unknown_to_TransferWrite]; + bb.barrier.buffer = dstBuffer; + bb.barrier.offset = offset; + bb.barrier.size = packet->length; + dfunc->vkCmdPipelineBarrier (packet->cmd, bb.srcStages, bb.dstStages, + 0, 0, 0, 1, &bb.barrier, 0, 0); + VkBufferCopy copy_region = { + .srcOffset = packet->offset, + .dstOffset = offset, + .size = packet->length, + }; + dfunc->vkCmdCopyBuffer (packet->cmd, packet->stage->buffer, dstBuffer, + 1, ©_region); + bb = *dstBarrier; + bb.barrier.buffer = dstBuffer; + bb.barrier.offset = offset; + bb.barrier.size = packet->length; + dfunc->vkCmdPipelineBarrier (packet->cmd, bb.srcStages, bb.dstStages, + 0, 0, 0, 1, &bb.barrier, 0, 0); +} + +void +QFV_PacketCopyImage (qfv_packet_t *packet, VkImage dstImage, + int width, int height, + const qfv_imagebarrier_t *dstBarrier) +{ + qfv_devfuncs_t *dfunc = packet->stage->device->funcs; + qfv_imagebarrier_t ib = imageBarriers[qfv_LT_Undefined_to_TransferDst]; + ib.barrier.image = dstImage; + ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, 1, &ib.barrier); + VkBufferImageCopy copy_region = { + .bufferOffset = packet->offset, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, + {0, 0, 0}, {width, height, 1}, + }; + dfunc->vkCmdCopyBufferToImage (packet->cmd, packet->stage->buffer, + dstImage, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, ©_region); + ib = *dstBarrier; + ib.barrier.image = dstImage; + ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, 1, &ib.barrier); +} diff --git a/libs/video/renderer/vulkan/swapchain.c b/libs/video/renderer/vulkan/swapchain.c new file mode 100644 index 000000000..c29606893 --- /dev/null +++ b/libs/video/renderer/vulkan/swapchain.c @@ -0,0 +1,203 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_MATH_H +//# include +#endif + +#include "QF/cvar.h" +#include "QF/mathlib.h" +#include "QF/va.h" + +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/image.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/swapchain.h" + +#include "vid_vulkan.h" + +qfv_swapchain_t * +QFV_CreateSwapchain (vulkan_ctx_t *ctx, VkSwapchainKHR old_swapchain) +{ + qfv_instfuncs_t *ifuncs = ctx->instance->funcs; + qfv_devfuncs_t *dfuncs = ctx->device->funcs; + qfv_queue_t *queue = &ctx->device->queue; + VkPhysicalDevice physDev = ctx->device->physDev->dev; + + VkBool32 supported; + ifuncs->vkGetPhysicalDeviceSurfaceSupportKHR (physDev, + queue->queueFamily, + ctx->surface, + &supported); + if (!supported) { + Sys_Error ("unsupported surface for swapchain"); + } + + uint32_t numModes; + VkPresentModeKHR *modes; + VkPresentModeKHR useMode = VK_PRESENT_MODE_FIFO_KHR; + ifuncs->vkGetPhysicalDeviceSurfacePresentModesKHR (physDev, + ctx->surface, + &numModes, 0); + modes = alloca (numModes * sizeof (VkPresentModeKHR)); + ifuncs->vkGetPhysicalDeviceSurfacePresentModesKHR (physDev, + ctx->surface, + &numModes, modes); + for (uint32_t i = 0; i < numModes; i++) { + if ((int) modes[i] == vulkan_presentation_mode) { + useMode = modes[i]; + } + } + Sys_MaskPrintf (SYS_vulkan, "presentation mode: %d (%d)\n", useMode, + vulkan_presentation_mode); + + VkSurfaceCapabilitiesKHR surfCaps; + ifuncs->vkGetPhysicalDeviceSurfaceCapabilitiesKHR (physDev, + ctx->surface, + &surfCaps); + uint32_t numImages = surfCaps.minImageCount + 1; + if (surfCaps.maxImageCount > 0 && numImages > surfCaps.maxImageCount) { + numImages = surfCaps.maxImageCount; + } + + VkExtent2D imageSize = {ctx->window_width, ctx->window_height}; + if (surfCaps.currentExtent.width == ~0u) { + imageSize.width = bound (surfCaps.minImageExtent.width, + imageSize.width, + surfCaps.maxImageExtent.width); + imageSize.height = bound (surfCaps.minImageExtent.height, + imageSize.height, + surfCaps.maxImageExtent.height); + } else { + imageSize = surfCaps.currentExtent; + } + Sys_MaskPrintf (SYS_vulkan, "swapchain: %d [%d, %d]\n", numImages, + imageSize.width, imageSize.height); + + VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + imageUsage &= surfCaps.supportedUsageFlags; + Sys_MaskPrintf (SYS_vulkan, " usage:%x supported:%x\n", imageUsage, + surfCaps.supportedUsageFlags); + + VkSurfaceTransformFlagBitsKHR surfTransform + = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + + uint32_t numFormats; + ifuncs->vkGetPhysicalDeviceSurfaceFormatsKHR (physDev, + ctx->surface, + &numFormats, 0); + VkSurfaceFormatKHR *formats = alloca (numFormats * sizeof (*formats)); + ifuncs->vkGetPhysicalDeviceSurfaceFormatsKHR (physDev, + ctx->surface, + &numFormats, formats); + VkSurfaceFormatKHR useFormat = {VK_FORMAT_B8G8R8A8_UNORM, + VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}; + if (numFormats > 1) { + uint32_t i; + for (i = 0; i < numFormats; i++) { + if (formats[i].format == useFormat.format + && formats[i].colorSpace == useFormat.colorSpace) { + break; + } + } + if (i == numFormats) { + useFormat = formats[0]; + } + } else if (numFormats == 1 && formats[0].format != VK_FORMAT_UNDEFINED) { + useFormat = formats[0]; + } + + VkSwapchainCreateInfoKHR createInfo = { + VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, 0, 0, + ctx->surface, + numImages, + useFormat.format, useFormat.colorSpace, + imageSize, + 1, // array layers + imageUsage, + VK_SHARING_MODE_EXCLUSIVE, + 0, 0, + surfTransform, + VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + useMode, + VK_TRUE, + old_swapchain + }; + + VkDevice dev = ctx->device->dev; + VkSwapchainKHR swapchain; + dfuncs->vkCreateSwapchainKHR (dev, &createInfo, 0, &swapchain); + + if (old_swapchain != swapchain) { + dfuncs->vkDestroySwapchainKHR (dev, old_swapchain, 0); + } + + dfuncs->vkGetSwapchainImagesKHR (dev, swapchain, &numImages, 0); + qfv_swapchain_t *sc = malloc (sizeof (qfv_swapchain_t)); + sc->device = ctx->device; + sc->surface = ctx->surface; + sc->swapchain = swapchain; + sc->format = useFormat.format; + sc->extent = imageSize; + sc->numImages = numImages; + sc->usage = imageUsage; + sc->images = DARRAY_ALLOCFIXED (qfv_imageset_t, numImages, malloc); + sc->imageViews = DARRAY_ALLOCFIXED (qfv_imageviewset_t, numImages, malloc); + dfuncs->vkGetSwapchainImagesKHR (dev, swapchain, &numImages, sc->images->a); + for (uint32_t i = 0; i < numImages; i++) { + QFV_duSetObjectName (ctx->device, VK_OBJECT_TYPE_IMAGE, + sc->images->a[i], + va (ctx->va_ctx, "image:swapchain:%d", i)); + sc->imageViews->a[i] + = QFV_CreateImageView (ctx->device, sc->images->a[i], + VK_IMAGE_VIEW_TYPE_2D, sc->format, + VK_IMAGE_ASPECT_COLOR_BIT); + QFV_duSetObjectName (ctx->device, VK_OBJECT_TYPE_IMAGE_VIEW, + sc->imageViews->a[i], + va (ctx->va_ctx, "iview:swapchain:%d", i)); + } + return sc; +} + +void +QFV_DestroySwapchain (qfv_swapchain_t *swapchain) +{ + qfv_device_t *device = swapchain->device; + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + for (size_t i = 0; i < swapchain->imageViews->size; i++) { + dfunc->vkDestroyImageView (dev, swapchain->imageViews->a[i], 0); + } + free (swapchain->images); + free (swapchain->imageViews); + dfunc->vkDestroySwapchainKHR (dev, swapchain->swapchain, 0); + free (swapchain); +} + +int +QFV_AcquireNextImage (qfv_swapchain_t *swapchain, VkSemaphore semaphore, + VkFence fence, uint32_t *imageIndex) +{ + qfv_device_t *device = swapchain->device; + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + uint64_t timeout = 2000000000; + *imageIndex = ~0u; + VkResult res = dfunc->vkAcquireNextImageKHR (dev, swapchain->swapchain, + timeout, semaphore, fence, + imageIndex); + switch (res) { + case VK_SUCCESS: + case VK_TIMEOUT: + case VK_NOT_READY: + return 1; + case VK_SUBOPTIMAL_KHR: + case VK_ERROR_OUT_OF_DATE_KHR: + return 0; + default: + Sys_Error ("vkAcquireNextImageKHR failed: %d", res); + } +} diff --git a/libs/video/renderer/vulkan/test/Makemodule.am b/libs/video/renderer/vulkan/test/Makemodule.am new file mode 100644 index 000000000..8d4f8f77a --- /dev/null +++ b/libs/video/renderer/vulkan/test/Makemodule.am @@ -0,0 +1,20 @@ +libs_video_renderer_vulkan_tests = \ + libs/video/renderer/vulkan/test/test-staging + +if TEST_VULKAN +TESTS += $(libs_video_renderer_vulkan_tests) +check_PROGRAMS += $(libs_video_renderer_vulkan_tests) +endif + +EXTRA_PROGRAMS += $(libs_video_renderer_vulkan_tests) + +libs_video_renderer_vulkan_test_libs= \ + libs/video/renderer/librender_vulkan.la \ + libs/util/libQFutil.la + +libs_video_renderer_vulkan_test_test_staging_SOURCES= \ + libs/video/renderer/vulkan/test/test-staging.c +libs_video_renderer_vulkan_test_test_staging_LDADD= \ + $(libs_video_renderer_vulkan_test_libs) +libs_video_renderer_vulkan_test_test_staging_DEPENDENCIES= \ + $(libs_video_renderer_vulkan_test_libs) diff --git a/libs/video/renderer/vulkan/test/test-staging.c b/libs/video/renderer/vulkan/test/test-staging.c new file mode 100644 index 000000000..93ae15ce4 --- /dev/null +++ b/libs/video/renderer/vulkan/test/test-staging.c @@ -0,0 +1,244 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#include "QF/Vulkan/qf_vid.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/staging.h" + +void *stage_memory; + +static VkResult VKAPI_CALL +vkMapMemory (VkDevice device, VkDeviceMemory memory, + VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, + void **data) +{ + char *buf = calloc (1, size); + stage_memory = buf; + *data = buf; + return VK_SUCCESS; +} + +static VkResult VKAPI_CALL +vkBindBufferMemory (VkDevice device, VkBuffer buffer, VkDeviceMemory memory, + VkDeviceSize offset) +{ + return VK_SUCCESS; +} + +static VkResult VKAPI_CALL +vkCreateBuffer (VkDevice device, const VkBufferCreateInfo *cinfo, + const VkAllocationCallbacks *allocator, VkBuffer *buffer) +{ + *buffer = 0; + return VK_SUCCESS; +} + +static void VKAPI_CALL +vkGetBufferMemoryRequirements (VkDevice device, VkBuffer buffer, + VkMemoryRequirements *requirements) +{ + memset (requirements, 0, sizeof (*requirements)); +} + +static VkResult VKAPI_CALL +vkAllocateCommandBuffers (VkDevice device, + const VkCommandBufferAllocateInfo *info, + VkCommandBuffer *buffers) +{ + static size_t cmdBuffer = 0; + + for (uint32_t i = 0; i < info->commandBufferCount; i++) { + buffers[i] = (VkCommandBuffer) ++cmdBuffer; + } + return VK_SUCCESS; +} + +static VkResult VKAPI_CALL +vkCreateFence (VkDevice device, const VkFenceCreateInfo *info, + const VkAllocationCallbacks *allocator, VkFence *fence) +{ + int *f = malloc (sizeof (int)); + *f = info->flags & VK_FENCE_CREATE_SIGNALED_BIT ? 1 : 0; + *(int **)fence = f; + return VK_SUCCESS; +} + +int wait_count = 0; + +static VkResult VKAPI_CALL +vkWaitForFences (VkDevice device, uint32_t fenceCount, const VkFence *fences, + VkBool32 waitAll, uint64_t timeout) +{ + for (uint32_t i = 0; i < fenceCount; i++) { + int *f = (int *) (intptr_t) fences[i]; + if (*f) { + wait_count++; + } + *f = 0; + } + return VK_SUCCESS; +} + +static VkResult VKAPI_CALL +vkResetFences (VkDevice device, uint32_t fenceCount, const VkFence *fences) +{ + for (uint32_t i = 0; i < fenceCount; i++) { + int *f = (int *) (intptr_t) fences[i]; + *f = 0; + } + return VK_SUCCESS; +} + +static VkResult VKAPI_CALL +vkGetFenceStatus (VkDevice device, VkFence fence) +{ + int *f = (int *) (intptr_t) fence; + return *f ? VK_SUCCESS : VK_NOT_READY; +} + +static VkResult VKAPI_CALL +vkResetCommandBuffer (VkCommandBuffer buffer, VkCommandBufferResetFlags flags) +{ + return VK_SUCCESS; +} + +static VkResult VKAPI_CALL +vkBeginCommandBuffer (VkCommandBuffer buffer, + const VkCommandBufferBeginInfo *info) +{ + return VK_SUCCESS; +} + +static VkResult VKAPI_CALL +vkEndCommandBuffer (VkCommandBuffer buffer) +{ + return VK_SUCCESS; +} + +static VkResult VKAPI_CALL +vkFlushMappedMemoryRanges (VkDevice device, uint32_t count, + const VkMappedMemoryRange *ranges) +{ + return VK_SUCCESS; +} + +static VkResult VKAPI_CALL +vkQueueSubmit (VkQueue queue, uint32_t count, const VkSubmitInfo *submits, + VkFence fence) +{ + int *f = (int *) (intptr_t) fence; + *f = 1; + return VK_SUCCESS; +} + +qfv_devfuncs_t dfuncs = { + .vkCreateBuffer = vkCreateBuffer, + .vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements, + .vkMapMemory = vkMapMemory, + .vkBindBufferMemory = vkBindBufferMemory, + .vkAllocateCommandBuffers = vkAllocateCommandBuffers, + .vkCreateFence = vkCreateFence, + .vkWaitForFences = vkWaitForFences, + .vkResetFences = vkResetFences, + .vkGetFenceStatus = vkGetFenceStatus, + .vkResetCommandBuffer = vkResetCommandBuffer, + .vkBeginCommandBuffer = vkBeginCommandBuffer, + .vkEndCommandBuffer = vkEndCommandBuffer, + .vkFlushMappedMemoryRanges = vkFlushMappedMemoryRanges, + .vkQueueSubmit = vkQueueSubmit, +}; +qfv_physdev_t physDev = { + .properties2 = { + .properties = { + .limits = { + .nonCoherentAtomSize = 256, + }, + }, + }, + .properties = &physDev.properties2.properties, +}; +qfv_device_t device = { + .physDev = &physDev, + .funcs = &dfuncs, +}; + +static void __attribute__ ((format (PRINTF, 2, 3), noreturn)) +_error (int line, const char *fmt, ...) +{ + va_list args; + + va_start (args, fmt); + fprintf (stderr, "%d: ", line); + vfprintf (stderr, fmt, args); + fprintf (stderr, "\n"); + va_end (args); + exit(1); +} +#define error(fmt...) _error(__LINE__, fmt) + +int +main (void) +{ + qfv_stagebuf_t *stage = QFV_CreateStagingBuffer (&device, "", 1024, 0); + + if (stage->size != 1024) { + error ("stage has incorrect size: %zd", stage->size); + } + if (stage->end != stage->size) { + error ("stage has incorrect end: %zd", stage->end); + } + if (!stage->data || stage->data != stage_memory) { + error ("stage memory not mapped: d:%p, m:%p", + stage->data, stage_memory); + } + + for (size_t i = 0; i < RB_buffer_size (&stage->packets); i++) { + qfv_packet_t *p = &stage->packets.buffer[i]; + if (p->stage != stage) { + error ("packet[%zd] stage not set: ps:%p s:%p", i, + p->stage, stage); + } + if (!p->cmd) { + error ("packet[%zd] has no command buffer", i); + } + if (!p->fence) { + error ("packet[%zd] has no fence", i); + } + if (vkGetFenceStatus (device.dev, p->fence) != VK_SUCCESS) { + error ("packet[%zd].fence is not signaled", i); + } + if (p->offset || p->length) { + error ("packet[%zd] size/length not initialized: o:%zd l:%zd", + i, p->offset, p->length); + } + } + + qfv_packet_t *packet = QFV_PacketAcquire (stage); + if (!packet) { + error ("could not get a packet"); + } + if (RB_DATA_AVAILABLE (stage->packets) != 1) { + error ("in flight packet count incorrect"); + } + if (vkGetFenceStatus (device.dev, packet->fence) != VK_NOT_READY) { + error ("packet.fence is signaled"); + } + if (QFV_PacketExtend (packet, 2048)) { + error ("2048 byte request did not return null"); + } + if (!QFV_PacketExtend (packet, 1024)) { + error ("1024 byte request returned null"); + } + if (QFV_PacketExtend (packet, 1)) { + error ("1 byte request did not return null"); + } + + return 0; +} diff --git a/libs/video/renderer/vulkan/util.c b/libs/video/renderer/vulkan/util.c new file mode 100644 index 000000000..edd5f5b0f --- /dev/null +++ b/libs/video/renderer/vulkan/util.c @@ -0,0 +1,110 @@ +/* + util.c + + Copyright (C) 2019 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "QF/hash.h" + +#include "util.h" + +static const char * +strset_get_key (const void *_str, void *unused) +{ + return (const char *)_str; +} + +strset_t * +new_strset (const char * const *strings) +{ + hashtab_t *tab = Hash_NewTable (61, strset_get_key, 0, 0, 0);//FIXME threads + for ( ; strings && *strings; strings++) { + Hash_Add (tab, (void *) *strings); + } + return (strset_t *) tab; +} + +void +del_strset (strset_t *strset) +{ + Hash_DelTable ((hashtab_t *) strset); +} + +void +strset_add (strset_t *strset, const char *str) +{ + hashtab_t *tab = (hashtab_t *) strset; + Hash_Add (tab, (void *) str); +} + +int +strset_contains (strset_t *strset, const char *str) +{ + return Hash_Find ((hashtab_t *) strset, str) != 0; +} + +int +count_strings (const char * const *str) +{ + int count = 0; + + if (str) { + while (*str++) { + count++; + } + } + return count; +} + +void +merge_strings (const char **out, const char * const *in1, + const char * const *in2) +{ + if (in1) { + while (*in1) { + *out++ = *in1++; + } + } + if (in2) { + while (*in2) { + *out++ = *in2++; + } + } +} + +void +prune_strings (strset_t *strset, const char **strings, uint32_t *count) +{ + hashtab_t *tab = (hashtab_t *) strset; + + for (uint32_t i = *count; i-- > 0; ) { + if (!Hash_Find (tab, strings[i])) { + memmove (strings + i, strings + i + 1, + (--(*count) - i) * sizeof (const char **)); + } + } +} diff --git a/libs/video/renderer/vulkan/util.h b/libs/video/renderer/vulkan/util.h new file mode 100644 index 000000000..944e34cfe --- /dev/null +++ b/libs/video/renderer/vulkan/util.h @@ -0,0 +1,18 @@ +#ifndef __util_h +#define __util_h + +#include + +typedef struct strset_s strset_t; + +int count_strings (const char * const *str) __attribute__((pure)); +void merge_strings (const char **out, const char * const *in1, + const char * const *in2); +void prune_strings (strset_t *strset, const char **strings, uint32_t *count); + +strset_t *new_strset (const char * const *strings); +void del_strset (strset_t *strset); +void strset_add (strset_t *strset, const char *str); +int strset_contains (strset_t *strset, const char *str); + +#endif//__util_h diff --git a/libs/video/renderer/vulkan/vkgen/Makemodule.am b/libs/video/renderer/vulkan/vkgen/Makemodule.am new file mode 100644 index 000000000..8bec1de68 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/Makemodule.am @@ -0,0 +1,74 @@ +vkgen = vkgen.dat$(EXEEXT) +noinst_PROGRAMS += @VKGEN_TARGETS@ + +EXTRA_PROGRAMS += vkgen.dat$(EXEEXT) + +vkgen_dat_src= \ + libs/video/renderer/vulkan/vkgen/vkalias.r \ + libs/video/renderer/vulkan/vkgen/vkenum.r \ + libs/video/renderer/vulkan/vkgen/vkfieldarray.r \ + libs/video/renderer/vulkan/vkgen/vkfieldauto.r \ + libs/video/renderer/vulkan/vkgen/vkfieldcustom.r \ + libs/video/renderer/vulkan/vkgen/vkfielddata.r \ + libs/video/renderer/vulkan/vkgen/vkfielddef.r \ + libs/video/renderer/vulkan/vkgen/vkfieldignore.r \ + libs/video/renderer/vulkan/vkgen/vkfieldlabeledarray.r \ + libs/video/renderer/vulkan/vkgen/vkfieldlabeledsingle.r \ + libs/video/renderer/vulkan/vkgen/vkfieldreadonly.r \ + libs/video/renderer/vulkan/vkgen/vkfieldsingle.r \ + libs/video/renderer/vulkan/vkgen/vkfieldstring.r \ + libs/video/renderer/vulkan/vkgen/vkfieldtype.r \ + libs/video/renderer/vulkan/vkgen/vkfixedarray.r \ + libs/video/renderer/vulkan/vkgen/vkgen.r \ + libs/video/renderer/vulkan/vkgen/vkstring.r \ + libs/video/renderer/vulkan/vkgen/vkstruct.r \ + libs/video/renderer/vulkan/vkgen/vktype.r \ + libs/video/renderer/vulkan/vkgen/vulkan.r + +VKGENFLAGS = -I$(top_srcdir)/libs/video/renderer/vulkan/vkgen $(VULKAN_QCFLAGS) + +vkgen_dat_SOURCES=$(vkgen_dat_src) +vkgen_obj=$(vkgen_dat_SOURCES:.r=.o) +vkgen_dep=$(call qcautodep,$(vkgen_dat_SOURCES:.o=.Qo)) +vkgen.dat$(EXEEXT): $(vkgen_obj) $(QFCC_DEP) ruamoko/lib/libr.a + $(V_QFCCLD)$(QLINK) -o $@ $(vkgen_obj) -lr +include $(vkgen_dep) # am--include-marker +r_depfiles_remade += $(vkgen_dep) + +libs/video/renderer/vulkan/vkgen/vkgen.o: $(top_srcdir)/libs/video/renderer/vulkan/vkgen/vkgen.r + $(V_QFCC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ + $(QCOMPILE) ${VKGENFLAGS} -MT $@ -MD -MP -MF $$depbase.Tqo -c -o $@ $< &&\ + sed -i -e '1s@:@: $(QFCC_DEP)@' $$depbase.Tqo &&\ + $(am__mv) $$depbase.Tqo $$depbase.Qo + +libs/video/renderer/vulkan/vkgen/vulkan.o: $(top_srcdir)/libs/video/renderer/vulkan/vkgen/vulkan.r + $(V_QFCC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ + $(QCOMPILE) ${VKGENFLAGS} -MT $@ -MD -MP -MF $$depbase.Tqo -c -o $@ $< &&\ + sed -i -e '1s@:@: $(QFCC_DEP)@' $$depbase.Tqo &&\ + $(am__mv) $$depbase.Tqo $$depbase.Qo + +EXTRA_DIST += \ + libs/video/renderer/vulkan/vkgen/stddef.h \ + libs/video/renderer/vulkan/vkgen/stdint.h \ + libs/video/renderer/vulkan/vkgen/vkalias.h \ + libs/video/renderer/vulkan/vkgen/vkenum.h \ + libs/video/renderer/vulkan/vkgen/vkfieldarray.h \ + libs/video/renderer/vulkan/vkgen/vkfieldauto.h \ + libs/video/renderer/vulkan/vkgen/vkfieldcustom.h \ + libs/video/renderer/vulkan/vkgen/vkfielddata.h \ + libs/video/renderer/vulkan/vkgen/vkfielddef.h \ + libs/video/renderer/vulkan/vkgen/vkfieldignore.h \ + libs/video/renderer/vulkan/vkgen/vkfieldlabeledarray.h \ + libs/video/renderer/vulkan/vkgen/vkfieldlabeledsingle.h \ + libs/video/renderer/vulkan/vkgen/vkfieldreadonly.h \ + libs/video/renderer/vulkan/vkgen/vkfieldsingle.h \ + libs/video/renderer/vulkan/vkgen/vkfieldstring.h \ + libs/video/renderer/vulkan/vkgen/vkfieldtype.h \ + libs/video/renderer/vulkan/vkgen/vkfixedarray.h \ + libs/video/renderer/vulkan/vkgen/vkgen.h \ + libs/video/renderer/vulkan/vkgen/vkstring.h \ + libs/video/renderer/vulkan/vkgen/vkstruct.h \ + libs/video/renderer/vulkan/vkgen/vktype.h + +CLEANFILES += \ + libs/video/renderer/vkgen/*.sym diff --git a/libs/video/renderer/vulkan/vkgen/stddef.h b/libs/video/renderer/vulkan/vkgen/stddef.h new file mode 100644 index 000000000..5252efc1d --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/stddef.h @@ -0,0 +1,2 @@ +#define const +#define __attribute__(x) diff --git a/libs/video/renderer/vulkan/vkgen/stdint.h b/libs/video/renderer/vulkan/vkgen/stdint.h new file mode 100644 index 000000000..5b598953c --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/stdint.h @@ -0,0 +1,14 @@ +#ifndef __vkgen_stdint_h +#define __vkgen_stdint_h +typedef int uint8_t; +typedef int uint16_t; +typedef int uint32_t; +typedef int uint64_t; +typedef int int8_t; +typedef int int16_t; +typedef int int32_t; +typedef int int64_t; +typedef int size_t; +typedef struct char { int x; } char;//FIXME add char to qfcc +typedef struct bool { int x; } bool;//FIXME add bool to qfcc +#endif//__vkgen_stdint_h diff --git a/libs/video/renderer/vulkan/vkgen/vkalias.h b/libs/video/renderer/vulkan/vkgen/vkalias.h new file mode 100644 index 000000000..fadf6619f --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkalias.h @@ -0,0 +1,11 @@ +#ifndef __renderer_vulkan_vkgen_vkalias_h +#define __renderer_vulkan_vkgen_vkalias_h + +#include "vktype.h" + +@interface Alias: Type +{ +} +@end + +#endif//__renderer_vulkan_vkgen_vkalias_h diff --git a/libs/video/renderer/vulkan/vkgen/vkalias.r b/libs/video/renderer/vulkan/vkgen/vkalias.r new file mode 100644 index 000000000..477b3a455 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkalias.r @@ -0,0 +1,171 @@ +#include + +#include "vkalias.h" +#include "vkenum.h" +#include "vkgen.h" +#include "vkstruct.h" + +@implementation Alias +-initWithType: (qfot_type_t *) type +{ + if (!(self = [super initWithType: type])) { + return nil; + } + [[self resolveType] setAlias: self]; + return self; +} + +-(string) name +{ + return type.alias.name; +} + +-(Type *) resolveType +{ + return [Type findType: type.alias.aux_type]; +} + +-(void)addToQueue +{ + Type *alias = [Type findType:type.alias.full_type]; + string name = [self name]; + + if ([alias name] == "VkFlags") { + if (str_mid (name, -5) == "Flags") { + string tag = str_mid (name, 0, -1) + "Bits"; + id enumObj = [(id) Hash_Find (available_types, tag) resolveType]; + [enumObj addToQueue]; + } + } else if (name == "VkBool32") { + id enumObj = [(id) Hash_Find (available_types, name) resolveType]; + [enumObj addToQueue]; + } else if ([alias class] == [Enum class] + || [alias class] == [Struct class]) { + [alias addToQueue]; + } else if (alias.type.meta == ty_basic && alias.type.type == ev_ptr) { + Type *type = [Type findType:alias.type.fldptr.aux_type]; + if (!type) { + // pointer to opaque struct. Probably + // VK_DEFINE_NON_DISPATCHABLE_HANDLE or VK_DEFINE_HANDLE + //string createInfo = name + "CreateInfo"; + //id structObj = (id) Hash_Find (available_types, createInfo); + //if (structObj) { + // [structObj addToQueue]; + //} + } else if ([type class] == [Alias class]) { + type = [type resolveType]; + if ([type class] == [Struct class]) { + [type addToQueue]; + } + } + } +} + +-(string) cexprType +{ + Type *alias = [Type findType:type.alias.full_type]; + string name = [self name]; + + if ([alias name] == "VkFlags") { + if (str_mid (name, -5) == "Flags") { + string tag = str_mid (name, 0, -1) + "Bits"; + id enumObj = [(id) Hash_Find (available_types, tag) resolveType]; + return [enumObj cexprType]; + } + } + if (name == "VkBool32") { + id enumObj = [(id) Hash_Find (available_types, name) resolveType]; + return [enumObj cexprType]; + } + if (name == "bool") { + return "cexpr_bool"; + } + if (name == "uint32_t") { + return "cexpr_uint"; + } + if (name == "vec4f_t") { + return "cexpr_vector"; + } + if (name == "size_t") { + return "cexpr_size_t"; + } + return [alias cexprType]; +} + +-(string) parseType +{ + Type *alias = [Type findType:type.alias.full_type]; + string name = [self name]; + + if ([alias name] == "VkFlags") { + if (str_mid (name, -5) == "Flags") { + string tag = str_mid (name, 0, -1) + "Bits"; + id enumObj = [(id) Hash_Find (available_types, tag) resolveType]; + return [enumObj parseType]; + } + } + switch (name) { + case "VkBool32": + id enumObj = [(id) Hash_Find (available_types, name) resolveType]; + return [enumObj parseType]; + case "uint32_t": + case "size_t": + case "vec4f_t": + case "bool": + return "QFString"; + } + return [alias parseType]; +} + +-(string) parseFunc +{ + Type *alias = [Type findType:type.alias.full_type]; + string name = [self name]; + + if ([alias name] == "VkFlags") { + if (str_mid (name, -5) == "Flags") { + string tag = str_mid (name, 0, -1) + "Bits"; + id enumObj = [(id) Hash_Find (available_types, tag) resolveType]; + return [enumObj parseFunc]; + } + } + switch (name) { + case "VkBool32": + id enumObj = [(id) Hash_Find (available_types, name) resolveType]; + return [enumObj parseFunc]; + case "bool": + return "parse_enum"; + case "uint32_t": + return "parse_uint32_t"; + } + return [alias parseFunc]; +} + +-(string) parseData +{ + Type *alias = [Type findType:type.alias.full_type]; + string name = [self name]; + + if ([alias name] == "VkFlags") { + if (str_mid (name, -5) == "Flags") { + string tag = str_mid (name, 0, -1) + "Bits"; + id enumObj = [(id) Hash_Find (available_types, tag) resolveType]; + return [enumObj parseData]; + } + } + switch (name) { + case "VkBool32": + id enumObj = [(id) Hash_Find (available_types, name) resolveType]; + return [enumObj parseData]; + case "uint32_t": + return "0"; + case "bool": + return "&cexpr_bool_enum"; + case "vec4f_t": + return "&cexpr_vector"; + case "size_t": + return "&cexpr_size_t"; + } + return [alias parseData]; +} +@end diff --git a/libs/video/renderer/vulkan/vkgen/vkenum.h b/libs/video/renderer/vulkan/vkgen/vkenum.h new file mode 100644 index 000000000..72a711f1c --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkenum.h @@ -0,0 +1,18 @@ +#ifndef __renderer_vulkan_vkgen_vkenum_h +#define __renderer_vulkan_vkgen_vkenum_h + +#include "vktype.h" + +@class PLItem; + +@interface Enum: Type +{ + int prefix_length; +} +-(void) writeForward; +-(void) writeTable; +-(void) writeSymtabInit; +-(void) writeSymtabEntry; +@end + +#endif//__renderer_vulkan_vkgen_vkenum_h diff --git a/libs/video/renderer/vulkan/vkgen/vkenum.r b/libs/video/renderer/vulkan/vkgen/vkenum.r new file mode 100644 index 000000000..1995426fa --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkenum.r @@ -0,0 +1,223 @@ +#include + +#include "vkenum.h" +#include "vkgen.h" + +typedef enum VkBool32 { + VK_FALSE, + VK_TRUE, + VK_MAX_ENUM = VK_TRUE +} VkBool32; + +@implementation Enum +-(void)process +{ + string end = "_MAX_ENUM"; + int len; + string prefix = nil; + + if (str_mid([self name], -8) == "FlagBits") { + end = "_FLAG_BITS_MAX_ENUM"; + } else if (str_mid([self name], -11) == "FlagBitsEXT") { + end = "_FLAG_BITS_MAX_ENUM_EXT"; + } else if (str_mid([self name], -3) == "KHR") { + end = "_MAX_ENUM_KHR"; + } else if (str_mid([self name], 0, 4) == "qfv_") { + prefix = "qfv_"; + } + len = -strlen (end); + if (!prefix) { + for (int i = 0; i < type.strct.num_fields; i++) { + qfot_var_t *var = &type.strct.fields[i]; + if (str_mid (var.name, len) == end) { + // len is negative so +1 consumes 1 more char (_) + prefix = str_hold (str_mid (var.name, 0, len + 1)); + } + } + } + if (prefix) { + prefix_length = strlen (prefix); + } +} + +-initWithType: (qfot_type_t *) type +{ + if (!(self = [super initWithType: type])) { + return nil; + } + [self process]; + return self; +} + +-(string) name +{ + return str_mid(type.strct.tag, 4); +} + +-(void) addToQueue +{ + string name = [self name]; + if (!Hash_Find (processed_types, name)) { + //printf (" +%s\n", name); + Hash_Add (processed_types, (void *) name); + [queue addObject: self]; + } +} + +static int +skip_value(string name) +{ + return (str_str (name, "_MAX_ENUM") >= 0 + || str_str (name, "_BEGIN_RANGE") >= 0 + || str_str (name, "_END_RANGE") >= 0 + || str_str (name, "_RANGE_SIZE") >= 0); +} + +-(int) isEmpty +{ + int num_values = 0; + + for (int i = 0, index = 0; i < type.strct.num_fields; i++) { + qfot_var_t *var = &type.strct.fields[i]; + if (skip_value (var.name)) { + continue; + } + num_values++; + } + return !num_values; +} + +-(void) writeForward +{ + fprintf (output_file, "static exprenum_t %s_enum;\n", [self name]); +} + +-(void) writeTable +{ + int strip_bit = 0; + int strip_khr = 0; + if (str_mid([self name], -8) == "FlagBits" + || str_mid([self name], -11) == "FlagBitsEXT") { + strip_bit = 1; + } + if (str_mid([self name], -3) == "KHR") { + strip_khr = 1; + } + + fprintf (output_file, "exprtype_t %s_type = {\n", [self name]); + fprintf (output_file, "\t.name = \"%s\",\n", [self name]); + fprintf (output_file, "\t.size = sizeof (int),\n"); + if (strip_bit) { + fprintf (output_file, "\t.binops = flag_binops,\n"); + fprintf (output_file, "\t.unops = flag_unops,\n"); + fprintf (output_file, "\t.get_string = cexpr_flags_get_string,\n"); + } else { + fprintf (output_file, "\t.binops = enum_binops,\n"); + fprintf (output_file, "\t.unops = 0,\n"); + fprintf (output_file, "\t.get_string = cexpr_enum_get_string,\n"); + } + fprintf (output_file, "\t.data = &%s_enum,\n", [self name]); + fprintf (output_file, "};\n"); + + if (![self isEmpty]) { + fprintf (output_file, "static %s %s_values[] = {\n", [self name], [self name]); + for (int i = 0, index = 0; i < type.strct.num_fields; i++) { + qfot_var_t *var = &type.strct.fields[i]; + if (skip_value (var.name)) { + continue; + } + fprintf (output_file, "\t%s, // %d 0x%x\n", + var.name, var.offset, var.offset); + index++; + } + fprintf (output_file, "};\n"); + } + fprintf (output_file, "static exprsym_t %s_symbols[] = {\n", [self name]); + for (int i = 0, index = 0; i < type.strct.num_fields; i++) { + qfot_var_t *var = &type.strct.fields[i]; + if (skip_value (var.name)) { + continue; + } + fprintf (output_file, "\t{\"%s\", &%s_type, %s_values + %d},\n", + var.name, [self name], [self name], index); + if (prefix_length) { + string shortname = str_mid (var.name, prefix_length); + if (strip_bit) { + int bit_pos = str_str (shortname, "_BIT"); + if (bit_pos >= 0) { + shortname = str_mid (shortname, 0, bit_pos); + } + } else if (strip_khr) { + int khr_pos = str_str (shortname, "_KHR"); + if (khr_pos >= 0) { + shortname = str_mid (shortname, 0, khr_pos); + } + } + fprintf (output_file, "\t{\"%s\", &%s_type, %s_values + %d},\n", + str_lower(shortname), [self name], [self name], index); + } + index++; + } + fprintf (output_file, "\t{ }\n"); + fprintf (output_file, "};\n"); + fprintf (output_file, "static exprtab_t %s_symtab = {\n", [self name]); + fprintf (output_file, "\t%s_symbols,\n", [self name]); + fprintf (output_file, "};\n"); + fprintf (output_file, "static exprenum_t %s_enum = {\n", [self name]); + fprintf (output_file, "\t&%s_type,\n", [self name]); + fprintf (output_file, "\t&%s_symtab,\n", [self name]); + fprintf (output_file, "};\n"); + + fprintf (output_file, "static plfield_t %s_field = { 0, 0, QFString," + " parse_enum, &%s_enum};\n", + [self name], [self name]); + fprintf (output_file, "int parse_%s (const plfield_t *field," + " const plitem_t *item, void *data, plitem_t *messages," + " void *context)\n", + [self name]); + fprintf (output_file, "{\n"); + fprintf (output_file, + "\treturn parse_enum (&%s_field, item, data, messages," + " context);\n", + [self name]); + fprintf (output_file, "}\n"); + + fprintf (header_file, "int parse_%s (const plfield_t *field," + " const plitem_t *item, void *data, plitem_t *messages," + " void *context);\n", + [self name]); + fprintf (header_file, "extern exprtype_t %s_type;\n", [self name]); +} + +-(void) writeSymtabInit +{ + fprintf (output_file, "\tcexpr_init_symtab (&%s_symtab, context);\n", + [self name]); +} + +-(void) writeSymtabEntry +{ + fprintf (output_file, "\tHash_Add (enum_symtab, &%s_enum);\n", + [self name]); +} + +-(string) cexprType +{ + return [self name] + "_type"; +} + +-(string) parseType +{ + return "QFString"; +} + +-(string) parseFunc +{ + return "parse_enum"; +} + +-(string) parseData +{ + return "&" + [self name] + "_enum"; +} +@end diff --git a/libs/video/renderer/vulkan/vkgen/vkfieldarray.h b/libs/video/renderer/vulkan/vkgen/vkfieldarray.h new file mode 100644 index 000000000..6f97f0aa7 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfieldarray.h @@ -0,0 +1,14 @@ +#ifndef __renderer_vulkan_vkgen_vkfieldarray_h +#define __renderer_vulkan_vkgen_vkfieldarray_h + +#include "vkfielddef.h" + +@class FieldType; + +@interface ArrayField: FieldDef +{ + FieldType *type; +} +@end + +#endif//__renderer_vulkan_vkgen_vkfieldarray_h diff --git a/libs/video/renderer/vulkan/vkgen/vkfieldarray.r b/libs/video/renderer/vulkan/vkgen/vkfieldarray.r new file mode 100644 index 000000000..b2900dfb5 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfieldarray.r @@ -0,0 +1,58 @@ +#include +#include + +#include "vkfieldarray.h" +#include "vkfieldtype.h" +#include "vkgen.h" +#include "vktype.h" + +@implementation ArrayField + +-init:(PLItem *) item struct:(Struct *)strct field:(string)fname +{ + self = [super init:item struct:strct field:fname]; + if (!self) { + return self; + } + + PLItem *desc = [item getObjectForKey:"type"]; + type = [[FieldType fieldType:[desc getObjectAtIndex:1]] retain]; + + value_field = str_hold ([[item getObjectForKey:"values"] string]); + size_field = str_hold ([[item getObjectForKey:"size"] string]); + return self; +} + +-(void) dealloc +{ + [type release]; + str_free (value_field); + str_free (size_field); + [super dealloc]; +} + +-writeParseData +{ + fprintf (output_file, "static parse_array_t parse_%s_%s_data = {\n", + struct_name, field_name); + [type writeParseData]; + fprintf (output_file, "\tfield_offset (%s, %s),\n", + struct_name, value_field); + if (size_field) { + fprintf (output_file, "\tfield_offset (%s, %s),\n", + struct_name, size_field); + } else { + fprintf (output_file, "\t-1,\n"); + } + fprintf (output_file, "};\n"); + return self; +} + +-writeField +{ + fprintf (output_file, "\t{\"%s\", 0, %s, parse_%s, &parse_%s_%s_data},\n", + field_name, "QFArray", "array", struct_name, field_name); + return self; +} + +@end diff --git a/libs/video/renderer/vulkan/vkgen/vkfieldauto.h b/libs/video/renderer/vulkan/vkgen/vkfieldauto.h new file mode 100644 index 000000000..a2e05deb8 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfieldauto.h @@ -0,0 +1,9 @@ +#ifndef __renderer_vulkan_vkgen_vkfieldauto_h +#define __renderer_vulkan_vkgen_vkfieldauto_h + +#include "vkfielddef.h" + +@interface AutoField: FieldDef +@end + +#endif//__renderer_vulkan_vkgen_vkfieldauto_h diff --git a/libs/video/renderer/vulkan/vkgen/vkfieldauto.r b/libs/video/renderer/vulkan/vkgen/vkfieldauto.r new file mode 100644 index 000000000..de4a347c9 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfieldauto.r @@ -0,0 +1,46 @@ +#include + +#include "vkfieldauto.h" +#include "vkgen.h" +#include "vkstruct.h" + +@implementation AutoField + +-init:(PLItem *) item struct:(Struct *)strct field:(string)fname +{ + self = [super init:item struct:strct field:fname]; + if (!self) { + return self; + } + string real_name = [[item getObjectForKey:"field"] string]; + field = [strct findField:real_name ? real_name : field_name]; + + return self; +} + +-writeParseData +{ + //printf("FieldDef: '%s' '%s'\n", struct_name, field_name); + return self; +} + +-writeField +{ + Type *field_type = [Type findType: field.type]; + fprintf (output_file, "\t{\"%s\", field_offset (%s, %s), %s, %s, %s},\n", + field_name, struct_name, field.name, + [field_type parseType], [field_type parseFunc], + [field_type parseData]); + return self; +} + +-writeSymbol +{ + Type *field_type = [Type findType: field.type]; + fprintf (output_file, + "\t{\"%s\", &%s, (void *) field_offset (%s, %s)},\n", + field_name, [field_type cexprType], struct_name, field.name); + return self; +} + +@end diff --git a/libs/video/renderer/vulkan/vkgen/vkfieldcustom.h b/libs/video/renderer/vulkan/vkgen/vkfieldcustom.h new file mode 100644 index 000000000..4b7672a9e --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfieldcustom.h @@ -0,0 +1,14 @@ +#ifndef __renderer_vulkan_vkgen_vkfieldcustom_h +#define __renderer_vulkan_vkgen_vkfieldcustom_h + +#include "vkfielddef.h" + +@interface CustomField: FieldDef +{ + string pltype; + string parser; + PLItem *fields; +} +@end + +#endif//__renderer_vulkan_vkgen_vkfieldcustom_h diff --git a/libs/video/renderer/vulkan/vkgen/vkfieldcustom.r b/libs/video/renderer/vulkan/vkgen/vkfieldcustom.r new file mode 100644 index 000000000..2473a91db --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfieldcustom.r @@ -0,0 +1,75 @@ +#include +#include + +#include "vkfieldcustom.h" +#include "vkfieldtype.h" +#include "vkgen.h" +#include "vktype.h" + +@implementation CustomField + +-init:(PLItem *) item struct:(Struct *)strct field:(string)fname +{ + self = [super init:item struct:strct field:fname]; + if (!self) { + return self; + } + + PLItem *desc = [item getObjectForKey:"type"]; + pltype = str_hold (parseItemType ([desc getObjectAtIndex:1])); + parser = str_hold ([[desc getObjectAtIndex:2] string]); + + fields = [[item getObjectForKey:"fields"] retain]; + return self; +} + +-(void)dealloc +{ + [fields release]; + str_free (pltype); + str_free (parser); + [super dealloc]; +} + +-writeParseData +{ + fprintf (output_file, "static size_t parse_%s_%s_offsets[] = {\n", + struct_name, field_name); + for (int i = 0, count = [fields count]; i < count; i++) { + string field = [[fields getObjectAtIndex:i] string]; + fprintf (output_file, "\tfield_offset (%s, %s),\n", + struct_name, field); + } + fprintf (output_file, "};\n"); + + fprintf (output_file, "static parse_custom_t parse_%s_%s_data = {\n", + struct_name, field_name); + fprintf (output_file, "\t%s,\n", parser); + fprintf (output_file, "\tparse_%s_%s_offsets,\n", + struct_name, field_name); + fprintf (output_file, "\t%d,\n", [fields count]); + fprintf (output_file, "};\n"); + return self; +} + +-writeField +{ + fprintf (output_file, "\t{\"%s\", 0, %s, parse_%s, &parse_%s_%s_data},\n", + field_name, pltype, "custom", struct_name, field_name); + return self; +} + +-writeSymbol +{ + fprintf (output_file, + "\t{\"%s\", 0/*FIXME*/, 0/*(void *) field_offset (%s, %s)*/},\n", + field_name, struct_name, "FIXME"); + return self; +} + +-(int) searchType +{ + return 0; +} + +@end diff --git a/libs/video/renderer/vulkan/vkgen/vkfielddata.h b/libs/video/renderer/vulkan/vkgen/vkfielddata.h new file mode 100644 index 000000000..cf8356262 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfielddata.h @@ -0,0 +1,9 @@ +#ifndef __renderer_vulkan_vkgen_vkfielddata_h +#define __renderer_vulkan_vkgen_vkfielddata_h + +#include "vkfielddef.h" + +@interface DataField: FieldDef +@end + +#endif//__renderer_vulkan_vkgen_vkfielddata_h diff --git a/libs/video/renderer/vulkan/vkgen/vkfielddata.r b/libs/video/renderer/vulkan/vkgen/vkfielddata.r new file mode 100644 index 000000000..d13ff8965 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfielddata.r @@ -0,0 +1,45 @@ +#include + +#include "vkfielddata.h" +#include "vkgen.h" +#include "vkstruct.h" +#include "vktype.h" + +@implementation DataField + +-init:(PLItem *) item struct:(Struct *)strct field:(string)fname +{ + self = [super init:item struct:strct field:fname]; + if (!self) { + return self; + } + + value_field = [[item getObjectForKey:"data"] string]; + size_field = [[item getObjectForKey:"size"] string]; + return self; +} + +-writeParseData +{ + fprintf (output_file, "static parse_data_t parse_%s_%s_data = {\n", + struct_name, field_name); + fprintf (output_file, "\tfield_offset (%s, %s),\n", + struct_name, value_field); + if (size_field) { + fprintf (output_file, "\tfield_offset (%s, %s),\n", + struct_name, size_field); + } else { + fprintf (output_file, "\tt-1,\n"); + } + fprintf (output_file, "};\n"); + return self; +} + +-writeField +{ + fprintf (output_file, "\t{\"%s\", 0, %s, parse_%s, &parse_%s_%s_data},\n", + field_name, "QFBinary", "data", struct_name, field_name); + return self; +} + +@end diff --git a/libs/video/renderer/vulkan/vkgen/vkfielddef.h b/libs/video/renderer/vulkan/vkgen/vkfielddef.h new file mode 100644 index 000000000..328604c8e --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfielddef.h @@ -0,0 +1,30 @@ +#ifndef __renderer_vulkan_vkgen_vkfielddef_h +#define __renderer_vulkan_vkgen_vkfielddef_h + +#include +#include + +@class PLItem; +@class Struct; +@class Type; + +@interface FieldDef: Object +{ + int line; + qfot_var_t *field; + string struct_name; + string field_name; + string value_field; + string size_field; +} ++fielddef:(PLItem *)item struct:(Struct *)strct field:(string)fname; +-init:(PLItem *)item struct:(Struct *)strct field:(string)fname; +-writeParseData; +-writeParse; +-writeField; +-writeSymbol; +-(string) name; +-(int) searchType; +@end + +#endif//__renderer_vulkan_vkgen_vkfielddef_h diff --git a/libs/video/renderer/vulkan/vkgen/vkfielddef.r b/libs/video/renderer/vulkan/vkgen/vkfielddef.r new file mode 100644 index 000000000..29e9e4867 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfielddef.r @@ -0,0 +1,122 @@ +#include +#include + +#include "vkfieldarray.h" +#include "vkfieldauto.h" +#include "vkfieldcustom.h" +#include "vkfielddata.h" +#include "vkfielddef.h" +#include "vkfieldignore.h" +#include "vkfieldlabeledarray.h" +#include "vkfieldlabeledsingle.h" +#include "vkfieldreadonly.h" +#include "vkfieldsingle.h" +#include "vkfieldstring.h" +#include "vkstruct.h" + +@implementation FieldDef + ++fielddef:(PLItem *)item struct:(Struct *)strct field:(string)fname +{ + string record = [item string]; + PLItem *type_desc = [item getObjectForKey:"type"]; + + if (!item) { + record = "auto"; + } + if (!record) { + if (item && !type_desc) { + return nil; + } + record = [type_desc string]; + if (!record) { + record = [[type_desc getObjectAtIndex:0] string]; + } + } + switch (record) { + case "auto": + return [[[AutoField alloc] init:item struct:strct field:fname] autorelease]; + case "custom": + return [[[CustomField alloc] init:item struct:strct field:fname] autorelease]; + case "string": + return [[[StringField alloc] init:item struct:strct field:fname] autorelease]; + case "data": + return [[[DataField alloc] init:item struct:strct field:fname] autorelease]; + case "single": + return [[[SingleField alloc] init:item struct:strct field:fname] autorelease]; + case "array": + return [[[ArrayField alloc] init:item struct:strct field:fname] autorelease]; + case "labeledarray": + return [[[LabeledArrayField alloc] init:item struct:strct field:fname] autorelease]; + case "labeledsingle": + return [[[LabeledSingleField alloc] init:item struct:strct field:fname] autorelease]; + case "readonly": + return [[[ReadOnlyField alloc] init:item struct:strct field:fname] autorelease]; + case "ignore": + return [[[IgnoreField alloc] init:item struct:strct field:fname] autorelease]; + } + return nil; +} + +-init:(PLItem *)item struct:(Struct *)strct field:(string)fname +{ + self = [super init]; + if (!self) { + return self; + } + + line = [item line]; + struct_name = str_hold ([strct outname]); + field_name = str_hold (fname); + return self; +} + +-fromField:(qfot_var_t *)field struct:(Struct *)strct +{ + return self; +} + +-(void)dealloc +{ + str_free (struct_name); + str_free (field_name); + [super dealloc]; +} + +-writeParseData +{ + fprintf (output_file, "undefined record type parse data: %d\n", line); + return self; +} + +-writeParse +{ + fprintf (output_file, "undefined record type parse: %d\n", line); + return self; +} + +-writeField +{ + fprintf (output_file, "undefined record type field: %d\n", line); + return self; +} + +-writeSymbol +{ + fprintf (output_file, + "\t{\"%s\", 0/*FIXME*/, (void *) field_offset (%s, %s)},\n", + field_name, struct_name, value_field); + return self; +} + +-(string) name +{ + return field_name; +} + +-(int) searchType +{ + return 1; +} + +@end diff --git a/libs/video/renderer/vulkan/vkgen/vkfieldignore.h b/libs/video/renderer/vulkan/vkgen/vkfieldignore.h new file mode 100644 index 000000000..fd2b34374 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfieldignore.h @@ -0,0 +1,14 @@ +#ifndef __renderer_vulkan_vkgen_vkfieldignore_h +#define __renderer_vulkan_vkgen_vkfieldignore_h + +#include "vkfielddef.h" + +@class FieldType; + +@interface IgnoreField: FieldDef +{ + FieldType *type; +} +@end + +#endif//__renderer_vulkan_vkgen_vkfieldignore_h diff --git a/libs/video/renderer/vulkan/vkgen/vkfieldignore.r b/libs/video/renderer/vulkan/vkgen/vkfieldignore.r new file mode 100644 index 000000000..edd5e66a0 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfieldignore.r @@ -0,0 +1,54 @@ +#include +#include + +#include "vkfieldignore.h" +#include "vkfieldtype.h" +#include "vkstruct.h" + +@implementation IgnoreField + +-init:(PLItem *)item struct:(Struct *)strct field:(string)fname +{ + self = [super init:item struct:strct field:fname]; + if (!self) { + return self; + } + + line = [item line]; + return self; +} + +-fromField:(qfot_var_t *)field struct:(Struct *)strct +{ + return self; +} + +-writeParseData +{ + return self; +} + +-writeField +{ + string parse_type = [FieldType anyType]; + fprintf (output_file, "\t{\"%s\", 0, %s, parse_%s, 0},\n", + field_name, parse_type, "ignore"); + return self; +} + +-writeSymbol +{ + return self; +} + +-(string) name +{ + return field_name; +} + +-(int) searchType +{ + return 1; +} + +@end diff --git a/libs/video/renderer/vulkan/vkgen/vkfieldlabeledarray.h b/libs/video/renderer/vulkan/vkgen/vkfieldlabeledarray.h new file mode 100644 index 000000000..db3ec6b15 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfieldlabeledarray.h @@ -0,0 +1,11 @@ +#ifndef __renderer_vulkan_vkgen_vkfieldlabeledarray_h +#define __renderer_vulkan_vkgen_vkfieldlabeledarray_h + +#include "vkfieldarray.h" + +@interface LabeledArrayField: ArrayField +{ +} +@end + +#endif//__renderer_vulkan_vkgen_vkfieldlabeledarray_h diff --git a/libs/video/renderer/vulkan/vkgen/vkfieldlabeledarray.r b/libs/video/renderer/vulkan/vkgen/vkfieldlabeledarray.r new file mode 100644 index 000000000..14ade0186 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfieldlabeledarray.r @@ -0,0 +1,52 @@ +#include + +#include "vkfieldlabeledarray.h" +#include "vkfieldtype.h" +#include "vkgen.h" +#include "vkstruct.h" +#include "vktype.h" + +@implementation LabeledArrayField + +-init:(PLItem *) item struct:(Struct *)strct field:(string)fname +{ + self = [super init:item struct:strct field:fname]; + if (!self) { + return self; + } + + PLItem *desc = [item getObjectForKey:"type"]; + string label_field = [[desc getObjectAtIndex:2] string]; + Type *t = [[Type lookup:[type type]] resolveType]; + if ([t isKindOfClass:[Struct class]]) { + Struct *s = (Struct *) t; + [s setLabelField:label_field]; + } + + return self; +} + +-writeParse +{ + fprintf (output_file, "\t\tplfield_t %s_field = {\n", field_name); + fprintf (output_file, "\t\t\t.name = \"%s\",\n", field_name); + fprintf (output_file, "\t\t\t.type = QFDictionary,\n"); + fprintf (output_file, "\t\t\t.data = &parse_%s_%s_data,\n", + struct_name, field_name); + fprintf (output_file, "\t\t};\n"); + fprintf (output_file, "\t\tif (!parse_labeledarray (&%s_field, item, " + "data, messages, context)) {\n", field_name); + fprintf (output_file, "\t\t\treturn 0;\n"); + fprintf (output_file, "\t\t}\n"); + return self; +} + +-writeField +{ + fprintf (output_file, "\t{\"%s\", 0, %s, parse_%s, &parse_%s_%s_data},\n", + field_name, "QFDictionary", "labeledarray", struct_name, + field_name); + return self; +} + +@end diff --git a/libs/video/renderer/vulkan/vkgen/vkfieldlabeledsingle.h b/libs/video/renderer/vulkan/vkgen/vkfieldlabeledsingle.h new file mode 100644 index 000000000..7685c366c --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfieldlabeledsingle.h @@ -0,0 +1,11 @@ +#ifndef __renderer_vulkan_vkgen_vkfieldlabeledsingle_h +#define __renderer_vulkan_vkgen_vkfieldlabeledsingle_h + +#include "vkfieldsingle.h" + +@interface LabeledSingleField: SingleField +{ +} +@end + +#endif//__renderer_vulkan_vkgen_vkfieldlabeledsingle_h diff --git a/libs/video/renderer/vulkan/vkgen/vkfieldlabeledsingle.r b/libs/video/renderer/vulkan/vkgen/vkfieldlabeledsingle.r new file mode 100644 index 000000000..fe3fd9dce --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfieldlabeledsingle.r @@ -0,0 +1,37 @@ +#include + +#include "vkfieldlabeledsingle.h" +#include "vkfieldtype.h" +#include "vkgen.h" +#include "vkstruct.h" +#include "vktype.h" + +@implementation LabeledSingleField + +-init:(PLItem *) item struct:(Struct *)strct field:(string)fname +{ + self = [super init:item struct:strct field:fname]; + if (!self) { + return self; + } + + PLItem *desc = [item getObjectForKey:"type"]; + string label_field = [[desc getObjectAtIndex:2] string]; + Type *t = [[Type lookup:[type type]] resolveType]; + if ([t isKindOfClass:[Struct class]]) { + Struct *s = (Struct *) t; + [s setLabelField:label_field]; + } + + return self; +} + +-writeField +{ + fprintf (output_file, "\t{\"%s\", 0, %s, parse_%s, &parse_%s_%s_data},\n", + field_name, "QFDictionary", "labeledsingle", struct_name, + field_name); + return self; +} + +@end diff --git a/libs/video/renderer/vulkan/vkgen/vkfieldreadonly.h b/libs/video/renderer/vulkan/vkgen/vkfieldreadonly.h new file mode 100644 index 000000000..3489a7b36 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfieldreadonly.h @@ -0,0 +1,14 @@ +#ifndef __renderer_vulkan_vkgen_vkfieldreadonly_h +#define __renderer_vulkan_vkgen_vkfieldreadonly_h + +#include "vkfielddef.h" + +@class FieldType; + +@interface ReadOnlyField: FieldDef +{ + FieldType *type; +} +@end + +#endif//__renderer_vulkan_vkgen_vkfieldreadonly_h diff --git a/libs/video/renderer/vulkan/vkgen/vkfieldreadonly.r b/libs/video/renderer/vulkan/vkgen/vkfieldreadonly.r new file mode 100644 index 000000000..5ae8f7df3 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfieldreadonly.r @@ -0,0 +1,53 @@ +#include +#include + +#include "vkfieldreadonly.h" +#include "vkfieldtype.h" +#include "vkgen.h" +#include "vktype.h" + +@implementation ReadOnlyField + +-init:(PLItem *) item struct:(Struct *)strct field:(string)fname +{ + self = [super init:item struct:strct field:fname]; + if (!self) { + return self; + } + + PLItem *desc = [item getObjectForKey:"type"]; + type = [[FieldType fieldType:[desc getObjectAtIndex:1]] retain]; + + value_field = str_hold ([[item getObjectForKey:"value"] string]); + return self; +} + +-(void) dealloc +{ + [type release]; + str_free (value_field); + [super dealloc]; +} + +-writeParseData +{ + return self; +} + +-writeField +{ + string parse_type = [FieldType anyType]; + fprintf (output_file, "\t{\"%s\", 0, %s, parse_%s, 0},\n", + field_name, parse_type, "ignore"); + return self; +} + +-writeSymbol +{ + fprintf (output_file, + "\t{\"%s\", %s, (void *) field_offset (%s, %s)},\n", + field_name, [type exprType], struct_name, value_field); + return self; +} + +@end diff --git a/libs/video/renderer/vulkan/vkgen/vkfieldsingle.h b/libs/video/renderer/vulkan/vkgen/vkfieldsingle.h new file mode 100644 index 000000000..83a3dad77 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfieldsingle.h @@ -0,0 +1,14 @@ +#ifndef __renderer_vulkan_vkgen_vkfieldsingle_h +#define __renderer_vulkan_vkgen_vkfieldsingle_h + +#include "vkfielddef.h" + +@class FieldType; + +@interface SingleField: FieldDef +{ + FieldType *type; +} +@end + +#endif//__renderer_vulkan_vkgen_vkfieldsingle_h diff --git a/libs/video/renderer/vulkan/vkgen/vkfieldsingle.r b/libs/video/renderer/vulkan/vkgen/vkfieldsingle.r new file mode 100644 index 000000000..c09788a37 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfieldsingle.r @@ -0,0 +1,51 @@ +#include +#include + +#include "vkfieldsingle.h" +#include "vkfieldtype.h" +#include "vkgen.h" +#include "vktype.h" + +@implementation SingleField + +-init:(PLItem *) item struct:(Struct *)strct field:(string)fname +{ + self = [super init:item struct:strct field:fname]; + if (!self) { + return self; + } + + PLItem *desc = [item getObjectForKey:"type"]; + type = [[FieldType fieldType:[desc getObjectAtIndex:1]] retain]; + + value_field = str_hold ([[item getObjectForKey:"value"] string]); + return self; +} + +-(void) dealloc +{ + [type release]; + str_free (value_field); + [super dealloc]; +} + +-writeParseData +{ + fprintf (output_file, "static parse_single_t parse_%s_%s_data = {\n", + struct_name, field_name); + [type writeParseData]; + fprintf (output_file, "\tfield_offset (%s, %s),\n", + struct_name, value_field); + fprintf (output_file, "};\n"); + return self; +} + +-writeField +{ + string parse_type = [type parseType]; + fprintf (output_file, "\t{\"%s\", 0, %s, parse_%s, &parse_%s_%s_data},\n", + field_name, parse_type, "single", struct_name, field_name); + return self; +} + +@end diff --git a/libs/video/renderer/vulkan/vkgen/vkfieldstring.h b/libs/video/renderer/vulkan/vkgen/vkfieldstring.h new file mode 100644 index 000000000..2214d7130 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfieldstring.h @@ -0,0 +1,10 @@ +#ifndef __renderer_vulkan_vkgen_vkfieldstring_h +#define __renderer_vulkan_vkgen_vkfieldstring_h + +#include "vkfielddef.h" + +@interface StringField: FieldDef ++fielddef:(PLItem *)item struct:(Struct *)strct field:(string)fname; +@end + +#endif//__renderer_vulkan_vkgen_vkfieldstring_h diff --git a/libs/video/renderer/vulkan/vkgen/vkfieldstring.r b/libs/video/renderer/vulkan/vkgen/vkfieldstring.r new file mode 100644 index 000000000..59a89012f --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfieldstring.r @@ -0,0 +1,45 @@ +#include + +#include "vkfieldstring.h" +#include "vkgen.h" + +@implementation StringField + +-init:(PLItem *) item struct:(Struct *)strct field:(string)fname +{ + self = [super init:item struct:strct field:fname]; + if (!self) { + return self; + } + + value_field = fname; + if (item) { + value_field = [[item getObjectForKey:"string"] string]; + } + return self; +} + ++fielddef:(PLItem *)item struct:(Struct *)strct field:(string)fname +{ + return [[[StringField alloc] init:item struct:strct field:fname] + autorelease]; +} + +-writeParseData +{ + fprintf (output_file, "static parse_string_t parse_%s_%s_data = {\n", + struct_name, field_name); + fprintf (output_file, "\tfield_offset (%s, %s),\n", + struct_name, value_field); + fprintf (output_file, "};\n"); + return self; +} + +-writeField +{ + fprintf (output_file, "\t{\"%s\", 0, %s, parse_%s, &parse_%s_%s_data},\n", + field_name, "QFString", "string", struct_name, field_name); + return self; +} + +@end diff --git a/libs/video/renderer/vulkan/vkgen/vkfieldtype.h b/libs/video/renderer/vulkan/vkgen/vkfieldtype.h new file mode 100644 index 000000000..c361d44fe --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfieldtype.h @@ -0,0 +1,26 @@ +#ifndef __renderer_vulkan_vkgen_vkfieldtype_h +#define __renderer_vulkan_vkgen_vkfieldtype_h + +#include + +@class PLItem; + +@interface FieldType: Object +{ + string parse_type; + string type; + string parser; + string data; +} ++fieldType:(PLItem *)item; +-initWithItem:(PLItem *)item; +-writeParseData; +-(string)type; +-(string)exprType; +-(string)parseType; ++(string)anyType; +@end + +string parseItemType (PLItem *item); + +#endif//__renderer_vulkan_vkgen_vkfieldtype_h diff --git a/libs/video/renderer/vulkan/vkgen/vkfieldtype.r b/libs/video/renderer/vulkan/vkgen/vkfieldtype.r new file mode 100644 index 000000000..0986a8e31 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfieldtype.r @@ -0,0 +1,100 @@ +#include +#include + +#include "vkfieldtype.h" +#include "vkgen.h" +#include "vktype.h" + +string +parseItemType (PLItem *item) +{ + string str = [item string]; + if (str) { + return str; + } + string mask = "QFMultiType"; + for (int i = [item count]; i-- > 0; ) { + str = [[item getObjectAtIndex:i] string]; + mask = mask + " | (1 << " + str + ")"; + } + return mask; +} + +@implementation FieldType + ++fieldType:(PLItem *)item +{ + return [[[self alloc] initWithItem:item] autorelease]; +} + +-(void)dealloc +{ + str_free (type); + str_free (parser); + str_free (data); + str_free (parse_type); + [super dealloc]; +} + +-initWithItem:(PLItem *)item +{ + if (!(self = [super init])) { + return nil; + } + + type = [item string]; + if (type) { + Type *field_type = [[Type lookup:type] dereference]; + type = str_hold (type); + parse_type = [field_type parseType]; + parser = str_hold ("parse_" + type); + } else { + PLItem *typeItem = [item getObjectForKey:"parse_type"]; + parse_type = str_hold (parseItemType(typeItem)); + type = str_hold ([[item getObjectForKey:"type"] string]); + parser = str_hold ([[item getObjectForKey:"parser"] string]); + data = str_hold ([[item getObjectForKey:"data"] string]); + } + + return self; +} + +-writeParseData +{ + fprintf (output_file, "\t%s,\n", parse_type); + fprintf (output_file, "\tsizeof (%s),\n", type); + fprintf (output_file, "\t%s,\n", parser); + if (data) { + fprintf (output_file, "\t&%s,\n", data); + } else { + fprintf (output_file, "\t0,\n"); + } + return self; +} + +-(string) type +{ + return type; +} + +-(string) exprType +{ + return "&" + type + "_type"; +} + +-(string) parseType +{ + return parse_type; +} + ++(string) anyType +{ + string mask = "QFMultiType" + " | (1 << QFString)" + " | (1 << QFBinary)" + " | (1 << QFArray)" + " | (1 << QFDictionary)"; + return mask; +} + +@end diff --git a/libs/video/renderer/vulkan/vkgen/vkfixedarray.h b/libs/video/renderer/vulkan/vkgen/vkfixedarray.h new file mode 100644 index 000000000..7e3af6b75 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfixedarray.h @@ -0,0 +1,20 @@ +#ifndef __renderer_vulkan_vkgen_vkfixedarray_h +#define __renderer_vulkan_vkgen_vkfixedarray_h + +#include + +#include "vkgen.h" +#include "vktype.h" + +@interface FixedArray: Type +{ + Type *ele_type; + int ele_count; +} +-(void) writeForward; +-(void) writeTable; +-(void) writeSymtabInit; +-(void) writeSymtabEntry; +@end + +#endif//__renderer_vulkan_vkgen_vkfixedarray_h diff --git a/libs/video/renderer/vulkan/vkgen/vkfixedarray.r b/libs/video/renderer/vulkan/vkgen/vkfixedarray.r new file mode 100644 index 000000000..8f466bdf2 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkfixedarray.r @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "vkfielddef.h" +#include "vkgen.h" +#include "vkfixedarray.h" + +@implementation FixedArray + +-initWithType: (qfot_type_t *) type +{ + if (!(self = [super initWithType: type])) { + return nil; + } + ele_type = [Type fromType: type.array.type]; + ele_count = type.array.size; + return self; +} + +-(string) name +{ + return sprintf ("%s_array_%d", [ele_type name], ele_count); +} + +-(void) addToQueue +{ + string name = [self name]; + if (!Hash_Find (processed_types, name)) { + //printf (" +%s\n", name); + Hash_Add (processed_types, (void *) name); + [queue addObject: self]; + } +} + +-(void) writeForward +{ +} + +-(void) writeTable +{ + fprintf (output_file, "static parse_fixed_array_t parse_%s_data = {\n", + [self name]); + fprintf (output_file, "\t.type = %s,\n", [ele_type parseType]); + fprintf (output_file, "\t.stride = sizeof (%s),\n", [ele_type name]); + fprintf (output_file, "\t.parser = %s,\n", [ele_type parseFunc]); + fprintf (output_file, "\t.size = %d,\n", ele_count); + fprintf (output_file, "};\n"); + + fprintf (output_file, "exprarray_t %s_array = {\n", [self name]); + fprintf (output_file, "\t.type = &%s,\n", [ele_type cexprType]); + fprintf (output_file, "\t.size = %d,\n", ele_count); + fprintf (output_file, "};\n"); + fprintf (output_file, "exprtype_t %s_type = {\n", [self name]); + fprintf (output_file, "\t.name = \"%s[%d]\",\n", [ele_type name], + ele_count); + fprintf (output_file, "\t.size = %d * sizeof (%s),\n", ele_count, + [ele_type name]); + fprintf (output_file, "\t.binops = cexpr_array_binops,\n"); + fprintf (output_file, "\t.unops = 0,\n"); + fprintf (output_file, "\t.data = &%s_array,\n", [self name]); + fprintf (output_file, "};\n"); + fprintf (output_file, "\n"); + fprintf (header_file, "extern exprtype_t %s_type;\n", [self name]); +} + +-(void) writeSymtabInit +{ +} + +-(void) writeSymtabEntry +{ +} + +-(string) cexprType +{ + return [self name] + "_type"; +} + +-(string) parseType +{ + return "QFMultiType | (1 << QFString) | (1 << QFArray)"; +} + +-(string) parseFunc +{ + return "parse_fixed_array"; +} + +-(string) parseData +{ + return "&parse_" + [self name] + "_data";; +} +@end diff --git a/libs/video/renderer/vulkan/vkgen/vkgen.h b/libs/video/renderer/vulkan/vkgen/vkgen.h new file mode 100644 index 000000000..8a9171014 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkgen.h @@ -0,0 +1,22 @@ +#ifndef __renderer_vulkan_vkgen_vkgen_h +#define __renderer_vulkan_vkgen_vkgen_h + +#include +#include +#include +#include +#include + +typedef void varfunc (qfot_var_t *var); + +void printf (string fmt, ...); +void fprintf (QFile file, string format, ...); +extern Array *queue; +extern Array *output_types; +extern PLItem *parse; +extern QFile output_file; +extern QFile header_file; +extern hashtab_t *processed_types; +extern hashtab_t *available_types; + +#endif//__renderer_vulkan_vkgen_vkgen_h diff --git a/libs/video/renderer/vulkan/vkgen/vkgen.r b/libs/video/renderer/vulkan/vkgen/vkgen.r new file mode 100644 index 000000000..2c4618d8a --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkgen.r @@ -0,0 +1,277 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vkgen.h" +#include "vkstruct.h" +#include "vkfixedarray.h" +#include "vkenum.h" + +static AutoreleasePool *autorelease_pool; +static void +arp_start (void) +{ + autorelease_pool = [[AutoreleasePool alloc] init]; +} + +static void +arp_end (void) +{ + [autorelease_pool release]; + autorelease_pool = nil; +} + +void printf (string fmt, ...) = #0; + +void fprintf (QFile file, string format, ...) +{ + Qputs (file, vsprintf (format, va_copy (@args))); +} + +hashtab_t *available_types; +hashtab_t *processed_types; +Array *queue; +Array *output_types; + +PLItem *parse; +QFile output_file; +QFile header_file; + +qfot_type_encodings_t *encodings; + +qfot_type_t * +next_type (qfot_type_t *type) +{ + int size = type.size; + if (!size) + size = 4; + return (qfot_type_t *) ((int *) type + size); +} + +int +type_is_null (qfot_type_t *type) +{ + return type.size == 0; +} + +void print_type (qfot_type_t *type) +{ + //printf ("type: %p %d %d %s", type, type.meta, type.size, type.encoding); + switch (type.meta) { + case ty_basic: + //printf (" %d", type.type); + switch (type.type) { + case ev_ptr: + case ev_field: + //printf (" "); + print_type (type.fldptr.aux_type); + break; + case ev_func: + //printf (" %p %d\n", type.func.return_type, + // type.func.num_params); + default: + //printf ("\n"); + break; + } + break; + case ty_struct: + case ty_union: + case ty_enum: + //printf (" %s %d\n", type.strct.tag, type.strct.num_fields); + break; + case ty_array: + //printf (" %p %d %d\n", type.array.type, type.array.base, + // type.array.size); + break; + case ty_class: + //printf (" %s\n", type.class); + break; + case ty_alias: + //printf (" %d %s ", type.alias.type, type.alias.name); + print_type (type.alias.aux_type); + break; + case ty_handle: + //printf (" %s\n", type.handle.tag); + break; + } +} + +void +scan_types (void) +{ + qfot_type_t *type; + + for (type = encodings.types; + ((int *)type - (int *) encodings.types) < encodings.size; + type = next_type (type)) { + if (type.size) { + string tag = str_mid(type.strct.tag, 4); + Type *avail_type = [Type fromType: type]; + if (avail_type) { + if (!Hash_Find (available_types, [avail_type name])) { + //printf ("scan: %s %s\n", tag, [avail_type name]); + Hash_Add (available_types, avail_type); + } + } + } + } +} + +static string +get_string_key (void *str, void *unused) +{ + return (string) str; +} + +static string +get_object_key (void *obj, void *unused) +{ + return [(id)obj name]; +} + +static void +free_object (void *obj, void *unused) +{ + [(id)obj release]; +} + +void +usage (string name) +{ + printf ("%s [plist file] [output file] [header file]\n", name); +} + +int +main(int argc, string *argv) +{ + int do_struct = 0; + int do_enum = 0; + string plist_filename; + QFile plist_file; + PLItem *plist; + PLItem *search; + PLItem *handles; + + arp_start (); + + if (argc != 4) { + usage (argv[0]); + return 1; + } + plist_filename = argv[1]; + plist_file = Qopen (plist_filename, "rt"); + if (!plist_file) { + printf ("could not open property list file: %s\n", plist_filename); + return 1; + } + plist = [[PLDictionary fromFile: plist_file] retain]; + if (!plist) { + printf ("error parsing: %s\n", plist_filename); + return 1; + } + Qclose (plist_file); + if ([plist class] != [PLDictionary class]) { + printf ("%s not a dictionary\n", plist_filename); + return 1; + } + search = [[plist getObjectForKey: "search"] retain]; + handles = [[plist getObjectForKey: "handles"] retain]; + parse = [[plist getObjectForKey: "parse"] retain]; + + encodings = PR_FindGlobal (".type_encodings"); + if (!encodings) { + printf ("Can't find encodings\n"); + return 1; + } + queue = [[Array array] retain]; + output_types = [[Array array] retain]; + available_types = Hash_NewTable (127, get_object_key, free_object, nil); + processed_types = Hash_NewTable (127, get_string_key, nil, nil); + scan_types (); + + for (int i = [search numObjects]; i-- > 0; ) { + PLString *str = (PLString *) [search getObjectAtIndex:i]; + string search_name = [str string]; + id obj = (id) Hash_Find (available_types, search_name); + obj = [obj resolveType]; + //printf("obj: %d %s\n", obj, class_get_class_name([obj class])); + if (obj && ([obj class] == [Struct class] + || [obj class] == [Enum class])) { + [obj addToQueue]; + } + } + + while ([queue count]) { + id obj = [queue objectAtIndex:0]; + [queue removeObjectAtIndex:0]; + if ([obj class] == [Struct class]) { + string name = [obj name]; + if (name == "char" // char type faked via a struct + || name == "bool" // bool type faked via a struct + || [[parse getObjectForKey:name] string] == "skip") { + continue; + } + [obj queueFieldTypes]; + } + [output_types addObject:obj]; + } + + for (int i = 0; i < argc; i++) { + //printf ("vkgen %d %s\n", i, argv[i]); + } + + arp_end (); + + output_file = Qopen (argv[2], "wt"); + header_file = Qopen (argv[3], "wt"); + for (int i = [output_types count]; i-- > 0; ) { + id obj = [output_types objectAtIndex:i]; + if ([obj name] == "VkStructureType") { + continue; + } + + arp_start (); + [obj writeForward]; + [obj initParse:[parse getObjectForKey:[obj name]]]; + arp_end (); + } + for (int i = [output_types count]; i-- > 0; ) { + id obj = [output_types objectAtIndex:i]; + if ([obj name] == "VkStructureType") { + continue; + } + + arp_start (); + [obj writeTable]; + arp_end (); + } + fprintf (output_file, "static void\n"); + fprintf (output_file, "vkgen_init_symtabs (exprctx_t *context)\n"); + fprintf (output_file, "{\n"); + for (int i = [output_types count]; i-- > 0; ) { + id obj = [output_types objectAtIndex:i]; + if ([obj name] == "VkStructureType") { + continue; + } + arp_start (); + [obj writeSymtabInit]; + [obj writeSymtabEntry]; + arp_end (); + } + fprintf (output_file, "}\n"); + Qclose (output_file); + Qclose (header_file); + Hash_DelTable (available_types); + [plist release]; + [search release]; + [handles release]; + [parse release]; + [queue release]; + [output_types release]; + return 0; +} diff --git a/libs/video/renderer/vulkan/vkgen/vkstring.h b/libs/video/renderer/vulkan/vkgen/vkstring.h new file mode 100644 index 000000000..4d9f22486 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkstring.h @@ -0,0 +1,9 @@ +#ifndef __renderer_vulkan_vkgen_vkstring_h +#define __renderer_vulkan_vkgen_vkstring_h + +#include "vktype.h" + +@interface String: Type +@end + +#endif//__renderer_vulkan_vkgen_vkstring_h diff --git a/libs/video/renderer/vulkan/vkgen/vkstring.r b/libs/video/renderer/vulkan/vkgen/vkstring.r new file mode 100644 index 000000000..f0c44c37b --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkstring.r @@ -0,0 +1,28 @@ +#include + +#include "vkfieldstring.h" +#include "vkstring.h" +#include "vkgen.h" + +@implementation String + +-(string) name +{ + return "string"; +} + +-(FieldDef *)fielddef:(Struct *)strct field:(string)fname +{ + return [StringField fielddef:nil struct:strct field:fname]; +} + +-(string) cexprType +{ + return [self name] + "_type"; +} + +-(string) parseType +{ + return "QFString"; +} +@end diff --git a/libs/video/renderer/vulkan/vkgen/vkstruct.h b/libs/video/renderer/vulkan/vkgen/vkstruct.h new file mode 100644 index 000000000..5e3290379 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkstruct.h @@ -0,0 +1,34 @@ +#ifndef __renderer_vulkan_vkgen_vkstruct_h +#define __renderer_vulkan_vkgen_vkstruct_h + +#include + +#include "vkgen.h" +#include "vktype.h" + +@class PLItem; +@class FieldDef; + +@interface Struct: Type +{ + string outname; + string label_field; + int write_symtab; + int skip; + + Array *field_defs; + FieldDef *parse_def; + PLItem *field_dict; + PLItem *only; +} +-(void) queueFieldTypes; +-(qfot_var_t *)findField:(string) fieldName; +-(void) setLabelField:(string) label_field; +-(void) writeForward; +-(void) writeTable; +-(void) writeSymtabInit; +-(void) writeSymtabEntry; +-(string) outname; +@end + +#endif//__renderer_vulkan_vkgen_vkstruct_h diff --git a/libs/video/renderer/vulkan/vkgen/vkstruct.r b/libs/video/renderer/vulkan/vkgen/vkstruct.r new file mode 100644 index 000000000..f7257de8b --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vkstruct.r @@ -0,0 +1,564 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "vkfielddef.h" +#include "vkfieldtype.h" +#include "vkgen.h" +#include "vkstruct.h" + +@implementation Struct + +-(void) dealloc +{ + [field_dict release]; + [field_defs release]; + [parse_def release]; + [only release]; + str_free (outname); + str_free (label_field); + [super dealloc]; +} + +-(string) name +{ + return str_mid(type.strct.tag, 4); +} + +-(void) addToQueue +{ + string name = [self name]; + if (!Hash_Find (processed_types, name)) { + //printf (" +%s\n", name); + Hash_Add (processed_types, (void *) name); + [queue addObject: self]; + } +} + +-(void) queueFieldTypes +{ + qfot_struct_t *strct =&type.strct; + PLItem *field_dict = [parse getObjectForKey:[self name]]; + int readonly = [field_dict string] == "readonly"; + + if (readonly) { + return; + } + + for (int i = 0; i < strct.num_fields; i++) { + qfot_var_t *var = &strct.fields[i]; + if (field_dict) { + PLItem *item = [field_dict getObjectForKey:var.name]; + FieldDef *def = [FieldDef fielddef:item + struct:self + field:var.name]; + if (![def searchType]) { + continue; + } + } + Type *type = [Type findType:var.type]; + [type addToQueue]; + } +} + +-(qfot_var_t *)findField:(string) fieldName +{ + for (int i = 0; i < type.strct.num_fields; i++) { + qfot_var_t *var = &type.strct.fields[i]; + if (var.name == fieldName) { + return var; + } + } + return nil; +} + +-(string)sTypeName +{ + string s = "VK_STRUCTURE_TYPE"; + string name = [self outname]; + int length = strlen (name); + int start, end, c; + for (start = 2; start < length; start = end) { + for (end = start + 1; end < length; end++) { + c = str_char (name, end); + if (c >= 'A' && c <= 'Z') { + break; + } + } + s += "_" + str_mid (name, start, end); + } + str_free (name); + return str_upper (s); +} + +-(void) setLabelField:(string)label_field +{ + self.label_field = label_field; +} + +-(void) writeForward +{ + PLItem *field_dict = [parse getObjectForKey:[self name]]; + int readonly = [field_dict string] == "readonly"; + if (!readonly) { + fprintf (output_file, "static int %s (const plfield_t *field," + " const plitem_t *item, void *data, plitem_t *messages," + " void *context);\n", + [self parseFunc]); + } +} + +static void +write_function_head (Struct *self) +{ + fprintf (output_file, "static int %s (const plfield_t *field," + " const plitem_t *item, void *data, plitem_t *messages," + " void *context)\n", + [self parseFunc]); + fprintf (output_file, "{\n"); +} + +static void +write_function_tail (Struct *self) +{ + fprintf (output_file, "}\n"); +} + +static void +write_parse_type (Struct *self, PLItem *item) +{ + if ([item string] == "auto") { + fprintf (output_file, "\t\tif (!PL_ParseStruct (%s_fields, item, " + "data, messages, context)) {\n", [self outname]); + fprintf (output_file, "\t\t\treturn 0;\n"); + fprintf (output_file, "\t\t}\n"); + } else { + //FieldDef *def = [FieldDef fielddef:item struct:self field:".parse"]; + [self.parse_def writeParse]; + } +} + +static void +write_auto_parse (Struct *self, string field, int name) +{ + string item = name ? "name" : "item"; + + fprintf (output_file, "\t\tdo {\n"); + fprintf (output_file, "\t\t\tplfield_t *f = find_field (%s_fields, %s, " + "item, messages);\n", [self outname], sprintf ("\"%s\"", field)); + fprintf (output_file, "\t\t\tif (!f) {\n"); + fprintf (output_file, "\t\t\t\treturn 0;\n"); + fprintf (output_file, "\t\t\t}\n"); + if (name) { + fprintf (output_file, "\t\t\tplitem_t *name = " + "PL_NewString (field->name);\n"); + } + fprintf (output_file, "\t\t\tf->parser (f, %s, &%s, messages, context);\n", + item, sprintf ("((%s *) data)->%s", [self outname], field)); + if (name) { + fprintf (output_file, "\t\t\tPL_Release (name);\n"); + } + fprintf (output_file, "\t\t} while (0);\n"); +} + +static int +check_need_table (Struct *self, PLItem *field_dict, string type) +{ + string key = nil; + switch (type) { + case "QFDictionary": key = ".dictionary"; break; + case "QFArray": key = ".array"; break; + case "QFBinary": key = ".binary"; break; + case "QFString": key = ".string"; break; + } + PLItem *type_obj = [field_dict getObjectForKey:key]; + int count = [type_obj numKeys]; + if (!count) { + return 0; + } + for (int i = 0; i < count; i++) { + string field = [type_obj keyAtIndex:i]; + PLItem *item = [type_obj getObjectForKey:field]; + string str = [item string]; + + if (field == ".parse") { + if (str != "auto") { + self.parse_def = [[FieldDef fielddef:item + struct:self + field:"parse"] retain]; + } + return 1; + } + if (str == "$auto") { + return 1; + } + } + return 0; +} + +static void +write_type (Struct *self, PLItem *field_dict, string type) +{ + string key = nil; + switch (type) { + case "QFDictionary": key = ".dictionary"; break; + case "QFArray": key = ".array"; break; + case "QFBinary": key = ".binary"; break; + case "QFString": key = ".string"; break; + } + PLItem *type_obj = [field_dict getObjectForKey:key]; + int count = [type_obj numKeys]; + if (!count) { + //FIXME errors + return; + } + fprintf (output_file, "\tif (type == %s) {\n", type); + for (int i = 0; i < count; i++) { + string field = [type_obj keyAtIndex:i]; + PLItem *item = [type_obj getObjectForKey:field]; + string str = [item string]; + + if (field == ".parse") { + write_parse_type (self, item); + continue; + } + + switch (str) { + case "$item.string": + str = "vkstrdup (context, PL_String (item))"; + break; + case "$item.line": + str = "PL_Line (item)"; + break; + case "$name": + str = "vkstrdup (context, field->name)"; + break; + case "$index": + str = "field->offset"; + break; + case "$auto": + write_auto_parse (self, field, 0); + continue; + case "$name.auto": + write_auto_parse (self, field, 1); + continue; + } + fprintf (output_file, "\t\t((%s *) data)->%s = %s;\n", [self outname], + field, str); + } + fprintf (output_file, "\t\treturn 1;\n"); + fprintf (output_file, "\t}\n"); +} + +static void +write_parser (Struct *self, int have_sType, PLItem *only) +{ + write_function_head (self); + if (have_sType) { + fprintf (output_file, "\t((%s *) data)->sType", [self outname]); + fprintf (output_file, " = %s;\n", [self sTypeName]); + } + if (self.label_field) { + fprintf (output_file, "\t((%s *) data)->%s", [self outname], + self.label_field); + fprintf (output_file, " = vkstrdup (context, field->name);\n"); + } + if (only) { + fprintf (output_file, "\tplfield_t *f = &%s_fields[0];\n", + [self outname]); + fprintf (output_file, + "\tif (!PL_CheckType (PL_Type (item), f->type)) {\n" + "\t\tPL_TypeMismatch (messages, item, " + "f->name, f->type, PL_Type (item));\n" + "\t\treturn 0;\n" + "\t}\n" + "\tvoid *flddata = (byte *)data + f->offset;\n" + "\treturn f->parser (f, item, flddata, messages, " + "context);\n"); + } else { + fprintf (output_file, + "\tif (PL_Type (item) == QFString\n" + "\t\t&& !(item = parse_reference (item, \"%s\", " + "messages, context))) {\n" + "\t\treturn 0;\n" + "\t}\n" + "\treturn PL_ParseStruct (%s_fields, item, data, " + "messages, context);\n", + [self outname], [self outname]); + } + write_function_tail (self); +} + +static void +write_cexpr (Struct *self, Array *field_defs) +{ + fprintf (output_file, "static exprsym_t %s_symbols[] = {\n", + [self outname]); + if (field_defs) { + PLItem *field_def; + qfot_var_t *field; + + for (int i = [field_defs count]; i-- > 0; ) { + FieldDef *field_def = [field_defs objectAtIndex:i]; + [field_def writeSymbol]; + } + } else { + for (int i = 0; i < self.type.strct.num_fields; i++) { + qfot_var_t *field = &self.type.strct.fields[i]; + if (field.name == "sType" || field.name == "pNext") { + continue; + } + Type *field_type = [Type findType: field.type]; + fprintf (output_file, + "\t{\"%s\", &%s, (void *) field_offset (%s, %s)},\n", + field.name, [field_type cexprType], [self outname], + field.name); + } + } + fprintf (output_file, "\t{ }\n"); + fprintf (output_file, "};\n"); + + fprintf (output_file, "static exprtab_t %s_symtab = {\n", [self outname]); + fprintf (output_file, "\t%s_symbols,\n", [self outname]); + fprintf (output_file, "};\n"); + + fprintf (output_file, "exprtype_t %s_type = {\n", [self outname]); + fprintf (output_file, "\t.name = \"%s\",\n", [self outname]); + fprintf (output_file, "\t.size = sizeof (%s),\n", [self outname]); + fprintf (output_file, "\t.binops = cexpr_struct_binops,\n"); + fprintf (output_file, "\t.unops = 0,\n"); + fprintf (output_file, "\t.data = &%s_symtab,\n", [self outname]); + fprintf (output_file, "};\n"); + fprintf (output_file, "\n"); + fprintf (header_file, "extern exprtype_t %s_type;\n", [self outname]); +} + +static void +write_table (Struct *self, PLItem *field_dict, Array *field_defs, + PLItem *only, int need_parser) +{ + qfot_type_t *type = self.type; + int have_sType = 0; + int have_pNext = 0; + int readonly = [field_dict string] == "readonly"; + + for (int i = 0; i < type.strct.num_fields; i++) { + qfot_var_t *field = &type.strct.fields[i]; + if (field.name == "sType") { + have_sType = 1; + } + if (field.name == "pNext") { + have_pNext = 1; + self.write_symtab = 1; + } + } + + for (int i = [field_defs count]; i-- > 0; ) { + FieldDef *field_def = [field_defs objectAtIndex:i]; + [field_def writeParseData]; + } + [self.parse_def writeParseData]; + + if (!readonly) { + fprintf (output_file, "static plfield_t %s_fields[] = {\n", + [self outname]); + if (!only) { + fprintf (output_file, + "\t{\"@inherit\", 0, QFString, parse_inherit, " + "&%s_fields},\n", [self outname]); + } + if (have_pNext) { + fprintf (output_file, + "\t{\"@next\", field_offset (%s, pNext), " + "QFArray, parse_next, 0},\n", [self outname]); + } + for (int i = [field_defs count]; i-- > 0; ) { + FieldDef *field_def = [field_defs objectAtIndex:i]; + [field_def writeField]; + } + fprintf (output_file, "\t{ }\n"); + fprintf (output_file, "};\n"); + + if (need_parser) { + write_parser (self, have_sType, only); + } + if (have_pNext) { + fprintf (output_file, "static parserref_t %s_parser = ", + [self outname]); + fprintf (output_file, "{\"%s\", %s, sizeof(%s)};\n", + [self outname], [self parseFunc], [self outname]); + } + } +} + +-(void) initParse:(PLItem *)parse +{ + if ([parse string] == "skip") { + skip = 1; + return; + } + + field_dict = [parse retain]; + + PLItem *new_name = [field_dict getObjectForKey:".name"]; + if (new_name) { + outname = str_hold ([new_name string]); + } + + field_defs = [[Array array] retain]; + + only = [[field_dict getObjectForKey:".only"] retain]; + if (only) { + string field_name = [only string]; + qfot_var_t *field = nil; + for (int i = 0; i < type.strct.num_fields; i++) { + qfot_var_t *f = &type.strct.fields[i]; + if (f.name == field_name) { + field = f; + break; + } + } + Type *field_type = [Type findType: field.type]; + FieldDef *field_def = [field_type fielddef:self field:field.name]; + if (!field_def) { + field_def = [FieldDef fielddef:nil + struct:self + field:field.name]; + } + [field_defs addObject: field_def]; + } else if (field_dict) { + PLItem *field_keys = [field_dict allKeys]; + + for (int i = [field_keys count]; i-- > 0; ) { + string field_name = [[field_keys getObjectAtIndex:i] string]; + + if (str_mid(field_name, 0, 1) == ".") { + continue; + } + PLItem *field_item = [field_dict getObjectForKey:field_name]; + FieldDef *field_def = [FieldDef fielddef:field_item + struct:self + field:field_name]; + [field_defs addObject: field_def]; + } + } else { + for (int i = 0; i < type.strct.num_fields; i++) { + qfot_var_t *field = &type.strct.fields[i]; + if (field.name == "sType" || field.name == "pNext") { + continue; + } + Type *field_type = [Type findType: field.type]; + FieldDef *field_def = [field_type fielddef:self field:field.name]; + if (!field_def) { + field_def = [FieldDef fielddef:nil + struct:self + field:field.name]; + } + [field_defs addObject: field_def]; + } + } +} + +-(void) writeTable +{ + if (skip) { + return; + } + + if ([field_dict getObjectForKey:".type"]) { + PLItem *type = [field_dict getObjectForKey:".type"]; + string str = [type string]; + int need_table = 0; + + if (str) { + need_table |= check_need_table (self, field_dict, str); + } else { + for (int i = [type count]; i-- > 0; ) { + string str = [[type getObjectAtIndex:i] string]; + need_table |= check_need_table (self, field_dict, str); + } + } + if (need_table) { + write_table (self, field_dict, field_defs, only, 0); + } + write_function_head (self); + fprintf (output_file, "\tpltype_t type = PL_Type (item);\n"); + if (str) { + write_type (self, field_dict, str); + } else { + for (int i = [type count]; i-- > 0; ) { + str = [[type getObjectAtIndex:i] string]; + write_type (self, field_dict, str); + } + } + fprintf (output_file, + "\tPL_TypeMismatch (messages, item, field->name, %s, type);\n", + parseItemType (type)); + fprintf (output_file, "\treturn 0;\n"); + write_function_tail (self); + write_cexpr (self, field_defs); + return; + } + + write_table (self, field_dict, field_defs, only, 1); + + write_cexpr (self, field_defs); +} + +-(void) writeSymtabInit +{ + PLItem *field_dict = [parse getObjectForKey:[self outname]]; + + if ([parse string] == "skip") { + return; + } + + fprintf (output_file, "\tcexpr_init_symtab (&%s_symtab, context);\n", + [self outname]); +} + +-(void) writeSymtabEntry +{ + if (!write_symtab || [parse string] == "skip") { + return; + } + fprintf (output_file, + "\tHash_Add (parser_table, &%s_parser);\n", + [self outname]); +} + +-(string) outname +{ + if (outname) { + return outname; + } + if (alias) { + return [alias name]; + } + return [self name]; +} + +-(string) cexprType +{ + return [self outname] + "_type"; +} + +-(string) parseType +{ + return "QFMultiType | (1 << QFString) | (1 << QFDictionary)"; +} + +-(string) parseFunc +{ + return "parse_" + [self outname]; +} + +-(string) parseData +{ + return "0"; +} +@end diff --git a/libs/video/renderer/vulkan/vkgen/vktype.h b/libs/video/renderer/vulkan/vkgen/vktype.h new file mode 100644 index 000000000..ed3965639 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vktype.h @@ -0,0 +1,40 @@ +#ifndef __renderer_vulkan_vkgen_vktype_h +#define __renderer_vulkan_vkgen_vktype_h + +#include +#include + +@class FieldDef; +@class Struct; +@class PLItem; + +@interface Type: Object +{ + qfot_type_t *type; + Type *alias; +} ++fromType: (qfot_type_t *) type; +/** \warning returned string is ephemeral +*/ +-(string) key; +/** \warning returned string is ephemeral +*/ +-(string) name; +-(void) setAlias: (Type *) alias; +-(void) addToQueue; +-(void) initParse:(PLItem *)parse; +-(Type *) resolveType; ++(Type *) findType: (qfot_type_t *) type; ++(Type *) lookup: (string) name; +-(string) cexprType; +-(string) parseType; +-(string) parseFunc; +-(string) parseData; + +-(FieldDef *)fielddef:(Struct *)strct field:(string)fname; + +-(int) isPointer; +-(Type *) dereference; +@end + +#endif//__renderer_vulkan_vkgen_vktype_h diff --git a/libs/video/renderer/vulkan/vkgen/vktype.r b/libs/video/renderer/vulkan/vkgen/vktype.r new file mode 100644 index 000000000..8c3d7f3f7 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vktype.r @@ -0,0 +1,182 @@ +#include + +#include "vkalias.h" +#include "vkenum.h" +#include "vkfixedarray.h" +#include "vkgen.h" +#include "vkstring.h" +#include "vkstruct.h" +#include "vktype.h" + +@implementation Type + +static hashtab_t *registered_types; + +static string get_type_key (void *type, void *unused) +{ + return ((Type *) type).type.encoding; +} + ++(void)initialize +{ + registered_types = Hash_NewTable (127, get_type_key, nil, nil); +} + +-initWithType: (qfot_type_t *) type +{ + if (!(self = [super init])) { + return nil; + } + self.type = type; + Hash_Add (registered_types, self); + return self; +} + ++(Type *) findType: (qfot_type_t *) type +{ + if (type.meta == ty_alias && !type.alias.name) { + type = type.alias.full_type; + } + return (Type *) Hash_Find (registered_types, type.encoding); +} + ++(Type *) lookup: (string) name +{ + return (Type *) Hash_Find (available_types, name); +} + ++fromType: (qfot_type_t *) type +{ + if (type.size == 0) { + return nil; + } + switch (type.meta) { + case ty_basic: + if (type.type == ev_ptr) { + Type *tgt = [Type findType: type.fldptr.aux_type]; + + if (tgt.type.meta == ty_alias + && tgt.type.alias.name == "char") { + return [[String alloc] initWithType: type]; + } + } + // fallthrough + case ty_handle: + case ty_class: + return [[Type alloc] initWithType: type]; + case ty_array: + return [[FixedArray alloc] initWithType: type]; + case ty_enum: + return [[Enum alloc] initWithType: type]; + case ty_struct: + case ty_union: + return [[Struct alloc] initWithType: type]; + case ty_alias: + if (type.alias.name) { + return [[Alias alloc] initWithType: type]; + } + return [Type fromType: type.alias.full_type]; + } + return nil; +} + +-(string) key +{ + return type.encoding; +} + +-(string) name +{ + if (type.meta == ty_basic) { + if (type.type == ev_int) { + return "int"; + } + return pr_type_name[type.type]; + } + //FIXME extract alias name and return proper type name + return type.encoding; +} + +-(void) setAlias: (Type *) alias +{ + if (!self.alias) { + self.alias = alias; + } +} + +-(void) addToQueue +{ + string name = [self name]; + if (type.meta == ty_basic && type.type == ev_ptr) { + [[Type findType: type.fldptr.aux_type] addToQueue]; + } +} + +-(void) initParse:(PLItem *)parse +{ +} + +-(Type *) resolveType +{ + return self; +} + +-(string) cexprType +{ + return "cexpr_" + [self name]; +} + +-(string) parseType +{ + if (type.meta == ty_basic) { + return "QFString"; + } + return "no parse " + [self name]; +} + +-(string) parseFunc +{ + if (type.meta == ty_basic) { + return "parse_basic"; + } + return "0"; +} + +-(string) parseData +{ + if (type.meta == ty_basic) { + if (type.type == ev_int) { + return "&cexpr_int"; + } + return "&cexpr_" + pr_type_name[type.type]; + } + return "0"; +} + +-(FieldDef *)fielddef:(Struct *)strct field:(string)fname +{ + return nil; +} + +-(int) isPointer +{ + if ((type.meta == ty_basic || type.meta == ty_alias) + && type.type == ev_ptr) { + return 1; + } + return 0; +} + +-(Type *) dereference +{ + qfot_type_t *t = type; + if (t.meta == ty_alias && t.type == ev_ptr) { + t = type.alias.full_type; + } + if (t.meta == ty_basic && t.type == ev_ptr) { + t = type.fldptr.aux_type; + } + return [Type findType:t]; +} + +@end diff --git a/libs/video/renderer/vulkan/vkgen/vulkan.r b/libs/video/renderer/vulkan/vkgen/vulkan.r new file mode 100644 index 000000000..e8e9a0470 --- /dev/null +++ b/libs/video/renderer/vulkan/vkgen/vulkan.r @@ -0,0 +1,13 @@ +#define __x86_64__ +#include + +typedef vec4 vec4f_t; +#define __QF_simd_types_h +#include "QF/Vulkan/render.h" + +//FIXME copy of qfv_subpass_t in qf_renderpass.h +//except it doesn't really matter because a custom spec is used +typedef struct qfv_osubpass_s { + vec4 color; + string name; +} qfv_osubpass_t; diff --git a/libs/video/renderer/vulkan/vkparse.c b/libs/video/renderer/vulkan/vkparse.c new file mode 100644 index 000000000..2c395bee2 --- /dev/null +++ b/libs/video/renderer/vulkan/vkparse.c @@ -0,0 +1,1546 @@ +/* + vkparse.c + + Parser for scripted vulkan structs + + Copyright (C) 2020 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "QF/cmem.h" +#include "QF/cvar.h" +#include "QF/dstring.h" +#include "QF/hash.h" +#include "QF/mathlib.h" +#include "QF/va.h" + +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/image.h" +#include "QF/Vulkan/pipeline.h" +#include "QF/Vulkan/render.h" +#include "QF/Vulkan/shader.h" + +#include "vid_vulkan.h" + +#include "vkparse.h" +#include "libs/video/renderer/vulkan/vkparse.hinc" + +typedef struct parseres_s { + const char *name; + plfield_t *field; + size_t offset; +} parseres_t; + +typedef struct parseref_s { + const char *name; + plparser_t parse; + size_t size; +} parserref_t; + +typedef struct handleref_s { + char *name; + uint64_t handle; +} handleref_t; + +static void flag_or (const exprval_t *val1, const exprval_t *val2, + exprval_t *result, exprctx_t *ctx) +{ + *(int *) (result->value) = *(int *) (val1->value) | *(int *) (val2->value); +} + +static void flag_and (const exprval_t *val1, const exprval_t *val2, + exprval_t *result, exprctx_t *ctx) +{ + *(int *) (result->value) = *(int *) (val1->value) & *(int *) (val2->value); +} + +static void flag_cast_int (const exprval_t *val1, const exprval_t *val2, + exprval_t *result, exprctx_t *ctx) +{ + // FIXME should check value is valid + *(int *) (result->value) = *(int *) (val2->value); +} + +static void flag_not (const exprval_t *val, exprval_t *result, exprctx_t *ctx) +{ + *(int *) (result->value) = ~(*(int *) (val->value)); +} + +binop_t flag_binops[] = { + { '|', 0, 0, flag_or }, + { '&', 0, 0, flag_and }, + { '=', &cexpr_int, 0, flag_cast_int }, + { '=', &cexpr_plitem, 0, cexpr_cast_plitem }, + {} +}; + +binop_t enum_binops[] = { + { '=', &cexpr_plitem, 0, cexpr_cast_plitem }, + {} +}; + +unop_t flag_unops[] = { + { '~', 0, flag_not }, + {} +}; + +typedef struct parse_single_s { + pltype_t type; + size_t stride; + plparser_t parser; + void *data; + size_t value_offset; +} parse_single_t; + +typedef struct parse_array_s { + pltype_t type; + size_t stride; + plparser_t parser; + void *data; + size_t value_offset; + size_t size_offset; +} parse_array_t; + +typedef struct parse_fixed_array_s { + pltype_t type; + size_t stride; + plparser_t parser; + void *data; + size_t size; +} parse_fixed_array_t; + +typedef struct parse_data_s { + size_t value_offset; + size_t size_offset; +} parse_data_t; + +typedef struct parse_string_s { + size_t value_offset; +} parse_string_t; + +typedef struct parse_custom_s { + int (*parse) (const plitem_t *item, void **data, + plitem_t *messages, parsectx_t *context); + size_t *offsets; + size_t num_offsets; +} parse_custom_t; + +static plfield_t *__attribute__((used)) +find_field (plfield_t *fields, const char *field_name, + const plitem_t *item, plitem_t *messages) +{ + for (plfield_t *f = fields; f->name; f++) { + if (strcmp (f->name, field_name) == 0) { + return f; + } + } + PL_Message (messages, item, "error: unknown field '%s'", field_name); + return 0; +} + +static int +parse_basic (const plfield_t *field, const plitem_t *item, + void *data, plitem_t *messages, void *context) +{ + int ret = 1; + parsectx_t *pctx = context; + __auto_type etype = (exprtype_t *) field->data; + exprctx_t ectx = *pctx->ectx; + exprval_t result = { etype, data }; + ectx.result = &result; + ectx.item = item; + const char *valstr = PL_String (item); + //Sys_MaskPrintf (SYS_vulkan_parse, "parse_basic: %s %zd %d %p %p: %s\n", + // field->name, field->offset, field->type, field->parser, + // field->data, valstr); + if (strcmp (valstr, "VK_SUBPASS_EXTERNAL") == 0) { + //FIXME handle subpass in a separate parser? + *(uint32_t *) data = VK_SUBPASS_EXTERNAL; + } else { + ret = !cexpr_eval_string (valstr, &ectx); + if (!ret) { + PL_Message (messages, item, "error parsing %s: %s", + field->name, valstr); + } + } + //Sys_MaskPrintf (SYS_vulkan_parse, " %x\n", *(uint32_t *)data); + + return ret; +} + +static int +parse_int32_t (const plfield_t *field, const plitem_t *item, + void *data, plitem_t *messages, void *context) +{ + int ret = 1; + // use size_t (and cexpr_size_t) for val so references to array sizes + // can be used + size_t val = 0; + parsectx_t *pctx = context; + exprval_t result = { &cexpr_size_t, &val }; + exprctx_t ectx = *pctx->ectx; + ectx.result = &result; + ectx.item = item; + const char *valstr = PL_String (item); + //Sys_MaskPrintf (SYS_vulkan_parse, + // "parse_int32_t: %s %zd %d %p %p %s\n", + // field->name, field->offset, field->type, field->parser, + // field->data, valstr); + ret = !cexpr_eval_string (valstr, &ectx); + if (!ret) { + PL_Message (messages, item, "error parsing %s: %s", + field->name, valstr); + } + *(int32_t *) data = val; + //Sys_MaskPrintf (SYS_vulkan_parse, " %d\n", *(int32_t *)data); + + return ret; +} + +static int +parse_uint32_t (const plfield_t *field, const plitem_t *item, + void *data, plitem_t *messages, void *context) +{ + int ret = 1; + // use size_t (and cexpr_size_t) for val so references to array sizes + // can be used + size_t val = 0; + parsectx_t *pctx = context; + exprval_t result = { &cexpr_size_t, &val }; + exprctx_t ectx = *pctx->ectx; + ectx.result = &result; + ectx.item = item; + const char *valstr = PL_String (item); + //Sys_MaskPrintf (SYS_vulkan_parse, "parse_uint32_t: %s %zd %d %p %p: %s\n", + // field->name, field->offset, field->type, field->parser, + // field->data, valstr); + if (strcmp (valstr, "VK_SUBPASS_EXTERNAL") == 0) { + //FIXME handle subpass in a separate parser? + *(uint32_t *) data = VK_SUBPASS_EXTERNAL; + } else { + //Sys_MaskPrintf (SYS_vulkan_parse, + // "parse_uint32_t: %s %zd %d %p %p %s\n", + // field->name, field->offset, field->type, field->parser, + // field->data, valstr); + ret = !cexpr_eval_string (valstr, &ectx); + if (!ret) { + PL_Message (messages, item, "error parsing %s: %s", + field->name, valstr); + } + *(uint32_t *) data = val; + //Sys_MaskPrintf (SYS_vulkan_parse, " %d\n", *(uint32_t *)data); + } + + return ret; +} + +static int +parse_enum (const plfield_t *field, const plitem_t *item, + void *data, plitem_t *messages, void *context) +{ + int ret = 1; + __auto_type enm = (exprenum_t *) field->data; + parsectx_t *pctx = context; + exprctx_t ectx = *pctx->ectx; + exprval_t result = { enm->type, data }; + ectx.parent = pctx->ectx; + ectx.symtab = enm->symtab; + ectx.result = &result; + const char *valstr = PL_String (item); + //Sys_MaskPrintf (SYS_vulkan_parse, "parse_enum: %s %zd %d %p %p %s\n", + // field->name, field->offset, field->type, field->parser, + // field->data, valstr); + ret = !cexpr_parse_enum (enm, valstr, &ectx, data); + if (!ret) { + PL_Message (messages, item, "error parsing enum: %s", valstr); + } + //Sys_MaskPrintf (SYS_vulkan_parse, " %d\n", *(int *)data); + return ret; +} + +static const plitem_t * +parse_reference (const plitem_t *item, const char *type, plitem_t *messages, + parsectx_t *pctx) +{ + exprctx_t ectx = *pctx->ectx; + plitem_t *refItem = 0; + exprval_t result = { &cexpr_plitem, &refItem }; + ectx.result = &result; + ectx.item = item; + const char *name = PL_String (item); + if (cexpr_eval_string (name, &ectx)) { + PL_Message (messages, item, "not a %s reference", type); + return 0; + } + return refItem; +} + +static void * +vkparse_alloc (void *context, size_t size) +{ + parsectx_t *pctx = context; + return cmemalloc (pctx->ectx->memsuper, size); +} + +static int +parse_ignore (const plfield_t *field, const plitem_t *item, + void *data, plitem_t *messages, void *context) +{ + return 1; +} + +static int __attribute__((used)) +parse_labeledsingle (const plfield_t *field, const plitem_t *item, + void *data, plitem_t *messages, void *context) +{ + __auto_type single = (parse_single_t *) field->data; + void *flddata = (byte *)data + single->value_offset; + + //Sys_MaskPrintf (SYS_vulkan_parse,"parse_labeledsingle: %s %zd %d %p %p\n", + // field->name, field->offset, + // field->type, field->parser, field->data); + + const char *key = PL_KeyAtIndex (item, 0); + if (!key) { + PL_Message (messages, item, "missing item"); + return 0; + } + item = PL_ObjectForKey (item, key); + + if (!PL_CheckType (single->type, PL_Type (item))) { + PL_TypeMismatch (messages, item, field->name, single->type, + PL_Type (item)); + return 0; + } + + plfield_t f = { field->name, 0, single->type, single->parser, 0 }; + void *value = vkparse_alloc (context, single->stride); + memset (value, 0, single->stride); + if (!single->parser (&f, item, value, messages, context)) { + return 0; + } + + *(void **) flddata = value; + return 1; +} + +static int +parse_single (const plfield_t *field, const plitem_t *item, + void *data, plitem_t *messages, void *context) +{ + __auto_type single = (parse_single_t *) field->data; + void *flddata = (byte *)data + single->value_offset; + + //Sys_MaskPrintf (SYS_vulkan_parse, "parse_single: %s %zd %d %p %p\n", + // field->name, field->offset, + // field->type, field->parser, field->data); + + if (!PL_CheckType (single->type, PL_Type (item))) { + PL_TypeMismatch (messages, item, field->name, single->type, + PL_Type (item)); + return 0; + } + + plfield_t f = { field->name, 0, single->type, single->parser, 0 }; + void *value = vkparse_alloc (context, single->stride); + memset (value, 0, single->stride); + if (!single->parser (&f, item, value, messages, context)) { + return 0; + } + + *(void **) flddata = value; + return 1; +} + +static int __attribute__((used)) +parse_labeledarray (const plfield_t *field, const plitem_t *item, + void *data, plitem_t *messages, void *context) +{ + __auto_type array = (parse_array_t *) field->data; + __auto_type value = (void **) ((byte *)data + array->value_offset); + __auto_type size = (uint32_t *) ((byte *)data + array->size_offset); + + plelement_t element = { + array->type, + array->stride, + vkparse_alloc, + array->parser, + array->data, + }; + plfield_t f = { 0, 0, 0, 0, &element }; + + typedef struct arr_s DARRAY_TYPE(byte) arr_t; + arr_t *arr; + + //Sys_MaskPrintf (SYS_vulkan_parse, "parse_array: %s %zd %d %p %p %p\n", + // field->name, field->offset, field->type, field->parser, + // field->data, data); + //Sys_MaskPrintf (SYS_vulkan_parse, " %d %zd %p %zd %zd\n", array->type, + // array->stride, array->parser, array->value_offset, + // array->size_offset); + if (!PL_ParseLabeledArray (&f, item, &arr, messages, context)) { + return 0; + } + *value = vkparse_alloc (context, array->stride * arr->size); + memcpy (*value, arr->a, array->stride * arr->size); + if ((void *) size >= data) { + *size = arr->size; + } + return 1; +} + +static int +parse_array (const plfield_t *field, const plitem_t *item, + void *data, plitem_t *messages, void *context) +{ + __auto_type array = (parse_array_t *) field->data; + __auto_type value = (void **) ((byte *)data + array->value_offset); + __auto_type size = (uint32_t *) ((byte *)data + array->size_offset); + + plelement_t element = { + array->type, + array->stride, + vkparse_alloc, + array->parser, + array->data, + }; + plfield_t f = { 0, 0, 0, 0, &element }; + + typedef struct arr_s DARRAY_TYPE(byte) arr_t; + arr_t *arr; + + //Sys_MaskPrintf (SYS_vulkan_parse, "parse_array: %s %zd %d %p %p %p\n", + // field->name, field->offset, field->type, field->parser, + // field->data, data); + //Sys_MaskPrintf (SYS_vulkan_parse, " %d %zd %p %zd %zd\n", array->type, + // array->stride, array->parser, array->value_offset, + // array->size_offset); + if (!PL_ParseArray (&f, item, &arr, messages, context)) { + return 0; + } + *value = vkparse_alloc (context, array->stride * arr->size); + memcpy (*value, arr->a, array->stride * arr->size); + if ((void *) size >= data) { + *size = arr->size; + } + return 1; +} + +static int __attribute__((used)) +parse_fixed_array (const plfield_t *field, const plitem_t *item, + void *data, plitem_t *messages, void *context) +{ + __auto_type array = (parse_fixed_array_t *) field->data; + + plelement_t element = { + array->type, + array->stride, + vkparse_alloc, + array->parser, + array->data, + }; + plfield_t f = { 0, 0, 0, 0, &element }; + + typedef struct arr_s DARRAY_TYPE(byte) arr_t; + arr_t *arr; + + if (!PL_ParseArray (&f, item, &arr, messages, context)) { + return 0; + } + memset (data, 0, array->stride * array->size); + size_t size = min (array->size, arr->size); + memcpy (data, arr->a, array->stride * size); + return 1; +} + +static char * +vkstrdup (parsectx_t *context, const char *str) +{ + size_t len = strlen (str) + 1; + char *dup = vkparse_alloc (context, len); + memcpy (dup, str, len); + return dup; +} + +static __attribute__((used)) parse_string_t parse_string_array = { 0 }; + +static int +parse_string (const plfield_t *field, const plitem_t *item, + void *data, plitem_t *messages, void *context) +{ + __auto_type string = (parse_string_t *) field->data; + __auto_type value = (char **) ((byte *)data + string->value_offset); + + const char *str = PL_String (item); + + //Sys_MaskPrintf (SYS_vulkan_parse, "parse_string: %s %zd %d %p %p %p\n", + // field->name, field->offset, field->type, field->parser, + // field->data, data); + //Sys_MaskPrintf (SYS_vulkan_parse, " %zd\n", string->value_offset); + //Sys_MaskPrintf (SYS_vulkan_parse, " %s\n", str); + + *value = vkstrdup (context, str); + return 1; +} + +static int +parse_custom (const plfield_t *field, const plitem_t *item, + void *data, plitem_t *messages, void *context) +{ + __auto_type custom = (parse_custom_t *) field->data; + void **offsets = alloca (custom->num_offsets * sizeof (void *)); + for (size_t i = 0; i < custom->num_offsets; i++) { + offsets[i] = data + custom->offsets[i]; + } + return custom->parse (item, offsets, messages, context); +} + +static int +parse_inherit (const plfield_t *field, const plitem_t *item, + void *data, plitem_t *messages, void *context) +{ + parsectx_t *pctx = context; + exprctx_t ectx = *pctx->ectx; + plitem_t *inheritItem = 0; + exprval_t result = { &cexpr_plitem, &inheritItem }; + ectx.result = &result; + ectx.item = item; + const char *inheritstr = PL_String (item); + //Sys_MaskPrintf (SYS_vulkan_parse, "parse_inherit: %s\n", inheritstr); + int ret = !cexpr_eval_string (inheritstr, &ectx); + if (ret) { + ret = PL_ParseStruct (field->data, inheritItem, data, messages, + context); + } + return ret; +} + +static hashtab_t *parser_table; + +static int +parse_next (const plfield_t *field, const plitem_t *item, void *data, + plitem_t *messages, void *context) +{ + const char *type_name = PL_String (PL_ObjectAtIndex (item, 0)); + plitem_t *next_def = PL_ObjectAtIndex (item, 1); + + if (!type_name || PL_Type (next_def) != QFDictionary) { + PL_Message (messages, item, "invalid @next"); + return 0; + } + parserref_t *parser = Hash_Find (parser_table, type_name); + if (!parser) { + PL_Message (messages, item, "Invalid type for @next: %s", type_name); + return 0; + } + void *data_ptr = vkparse_alloc (context, parser->size); + memset (data_ptr, 0, parser->size); + if (!parser->parse (field, next_def, data_ptr, messages, context)) { + return 0; + } + *(void **) data = data_ptr; + return 1; +} + +static int +parse_RGBA (const plitem_t *item, void **data, + plitem_t *messages, parsectx_t *context) +{ + int ret = 1; + exprctx_t ectx = *context->ectx; + exprval_t result = { &cexpr_vector, data[0] }; + ectx.result = &result; + ectx.item = item; + const char *valstr = PL_String (item); + //Sys_MaskPrintf (SYS_vulkan_parse, "parse_RGBA: %s\n", valstr); + ret = !cexpr_eval_string (valstr, &ectx); + //Sys_MaskPrintf (SYS_vulkan_parse, " "VEC4F_FMT"\n", + // VEC4_EXP (*(vec4f_t *)data[0])); + return ret; +} + +uint64_t +QFV_GetHandle (hashtab_t *tab, const char *name) +{ + handleref_t *hr = Hash_Find (tab, name); + if (hr) { + return hr->handle; + } + return 0; +} + +void +QFV_AddHandle (hashtab_t *tab, const char *name, uint64_t handle) +{ + handleref_t *hr = malloc (sizeof (handleref_t)); + hr->name = strdup (name); + hr->handle = handle; + Hash_Add (tab, hr); +} + +static int +parse_VkShaderModule (const plitem_t *item, void **data, + plitem_t *messages, parsectx_t *pctx) +{ + __auto_type handle = (VkShaderModule *) data[0]; + vulkan_ctx_t *ctx = pctx->vctx; + qfv_device_t *device = ctx->device; + scriptctx_t *sctx = ctx->script_context; + + const char *name = PL_String (item); + *handle = (VkShaderModule) QFV_GetHandle (sctx->shaderModules, name); + if (*handle) { + return 1; + } + qfvPushDebug (ctx, va (ctx->va_ctx, "parse_VkShaderModule: %d", PL_Line (item))); + *handle = QFV_CreateShaderModule (device, name); + qfvPopDebug (ctx); + if (!*handle) { + PL_Message (messages, item, "could not find shader %s", name); + return 0; + } + QFV_AddHandle (sctx->shaderModules, name, (uint64_t) *handle); + return 1; +} + +exprtype_t VkImage_type = { + .name = "VkImage", + .size = sizeof (VkImage), + .binops = 0, + .unops = 0, + .data = 0 +}; + +exprtype_t VkImageView_type = { + .name = "VkImageView", + .size = sizeof (VkImageView), + .binops = 0, + .unops = 0, + .data = 0 +}; + +static const char * +handleref_getkey (const void *hr, void *unused) +{ + return ((handleref_t *)hr)->name; +} + +static void +handleref_free (void *hr, void *_ctx) +{ + __auto_type handleref = (handleref_t *) hr; + free (handleref->name); + free (handleref); +} + +static void +setLayout_free (void *hr, void *_ctx) +{ + scriptctx_t *sctx = _ctx; + __auto_type handleref = (handleref_t *) hr; + __auto_type layout = (VkDescriptorSetLayout) handleref->handle; + __auto_type ctx = sctx->vctx; + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + + if (layout) { + dfunc->vkDestroyDescriptorSetLayout (device->dev, layout, 0); + } + handleref_free (handleref, ctx); +} + +static void +shaderModule_free (void *hr, void *_ctx) +{ + scriptctx_t *sctx = _ctx; + __auto_type handleref = (handleref_t *) hr; + __auto_type module = (VkShaderModule) handleref->handle; + __auto_type ctx = sctx->vctx; + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + + if (module) { + dfunc->vkDestroyShaderModule (device->dev, module, 0); + } + handleref_free (handleref, ctx); +} + +static void +pipelineLayout_free (void *hr, void *_ctx) +{ + scriptctx_t *sctx = _ctx; + __auto_type handleref = (handleref_t *) hr; + __auto_type layout = (VkPipelineLayout) handleref->handle; + __auto_type ctx = sctx->vctx; + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + + if (layout) { + dfunc->vkDestroyPipelineLayout (device->dev, layout, 0); + }; + handleref_free (handleref, ctx); +} + +static void +descriptorPool_free (void *hr, void *_ctx) +{ + scriptctx_t *sctx = _ctx; + __auto_type handleref = (handleref_t *) hr; + __auto_type pool = (VkDescriptorPool) handleref->handle; + __auto_type ctx = sctx->vctx; + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + + if (pool) { + dfunc->vkDestroyDescriptorPool (device->dev, pool, 0); + }; + handleref_free (handleref, ctx); +} + +static void +sampler_free (void *hr, void *_ctx) +{ + scriptctx_t *sctx = _ctx; + __auto_type handleref = (handleref_t *) hr; + __auto_type sampler = (VkSampler) handleref->handle; + __auto_type ctx = sctx->vctx; + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + + if (sampler) { + dfunc->vkDestroySampler (device->dev, sampler, 0); + }; + handleref_free (handleref, ctx); +} + +static void +image_free (void *hr, void *_ctx) +{ + scriptctx_t *sctx = _ctx; + __auto_type handleref = (handleref_t *) hr; + __auto_type image = (VkImage) handleref->handle; + __auto_type ctx = sctx->vctx; + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + + if (image) { + dfunc->vkDestroyImage (device->dev, image, 0); + }; + handleref_free (handleref, ctx); +} + +static void +imageView_free (void *hr, void *_ctx) +{ + scriptctx_t *sctx = _ctx; + __auto_type handleref = (handleref_t *) hr; + __auto_type imageView = (VkImageView) handleref->handle; + __auto_type ctx = sctx->vctx; + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + + if (imageView) { + dfunc->vkDestroyImageView (device->dev, imageView, 0); + }; + handleref_free (handleref, ctx); +} + +static void +renderpass_free (void *hr, void *_ctx) +{ + scriptctx_t *sctx = _ctx; + __auto_type handleref = (handleref_t *) hr; + __auto_type renderpass = (VkRenderPass) handleref->handle; + __auto_type ctx = sctx->vctx; + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + + if (renderpass) { + dfunc->vkDestroyRenderPass (device->dev, renderpass, 0); + }; + handleref_free (handleref, ctx); +} + +static hashtab_t *enum_symtab; + +typedef struct data_array_s DARRAY_TYPE(byte) data_array_t; +static void +data_array (const exprval_t **params, exprval_t *result, exprctx_t *context) +{ + size_t offset = 0; + // params are in reverse order, but this works for calculating the size + // of the buffer + for (const exprval_t **param = params; *param; param++) { + offset += (*param)->type->size; + } + __auto_type data = DARRAY_ALLOCFIXED_OBJ (data_array_t, offset, + cmemalloc, context->memsuper); + for (const exprval_t **param = params; *param; param++) { + size_t size = (*param)->type->size; + // pre-decrement offset because params are in reverse order + offset -= size; + memcpy (data->a + offset, (*param)->value, size); + } + *(data_array_t **) result->value = data; +} + +static exprtype_t data_array_type = { + .name = "array", + .size = sizeof (data_array_t *), + .binops = 0, + .unops = 0, +}; +static exprfunc_t data_array_func[] = { + { &data_array_type, -1, 0, data_array }, + {} +}; +static exprsym_t data_array_symbols[] = { + { "array", &cexpr_function, data_array_func }, + {} +}; +static exprtab_t data_array_symtab = { + data_array_symbols, +}; + +static int +parse_specialization_data (const plitem_t *item, void **data, + plitem_t *messages, parsectx_t *pctx) +{ + size_t *size_ptr = (size_t *) data[0]; + void **data_ptr = (void **) data[1]; + + if (PL_Type (item) == QFBinary) { + const void *bindata = PL_BinaryData (item); + size_t binsize = PL_BinarySize (item); + + *data_ptr = vkparse_alloc (pctx, binsize); + memcpy (*data_ptr, bindata, binsize); + *size_ptr = binsize; + return 1; + } + + data_array_t *da= 0; + exprctx_t ectx = *pctx->ectx; + exprval_t result = { &data_array_type, &da }; + ectx.parent = pctx->ectx; + ectx.symtab = &data_array_symtab; + ectx.result = &result; + ectx.item = item; + const char *valstr = PL_String (item); + //Sys_MaskPrintf (SYS_vulkan_parse, + // "parse_specialization_data: %s %zd %d %p %p %s\n", + // field->name, field->offset, field->type, field->parser, + // field->data, valstr); + int ret = !cexpr_eval_string (valstr, &ectx); + if (!ret) { + PL_Message (messages, item, "error parsing specialization data: %s", + valstr); + } else { + *size_ptr = da->size; + *data_ptr = da->a; + //for (size_t i = 0; i < da->size; i++) { + // Sys_Printf (" %02x", da->a[i]); + //} + //Sys_Printf ("\n"); + } + return ret; +} + +static int +parse_task_function (const plitem_t *item, void **data, + plitem_t *messages, parsectx_t *pctx) +{ + qfv_renderctx_t *rctx = pctx->data; + const char *fname = PL_String (item); + exprsym_t *fsym = Hash_Find (rctx->task_functions.tab, fname); + + if (!fsym) { + PL_Message (messages, item, "undefined task function %s", fname); + return 0; + } + if (fsym->type != &cexpr_function) { + PL_Message (messages, item, "not a function type %s", fname); + return 0; + } + exprfunc_t *func; + for (func = fsym->value; func->func; func++) { + if (!func->result) { + break; + } + } + if (!func->func) { + PL_Message (messages, item, "%s does not have a void implementation", + fname); + return 0; + } + size_t size = func->num_params * sizeof (exprval_t); + size += func->num_params * sizeof (exprval_t *); + size_t base = size; + for (int i = 0; i < func->num_params; i++) { + exprtype_t *type = func->param_types[i]; + size = ((size + type->size - 1) & ~(type->size - 1)); + if (i == 0) { + base = size; + } + } + exprval_t **param_ptrs = vkparse_alloc (pctx, size); + exprval_t *params = (exprval_t *) ¶m_ptrs[func->num_params]; + byte *param_data = (byte *) param_ptrs + base; + memset (params, 0, size); + size_t offs = 0; + for (int i = 0; i < func->num_params; i++) { + exprtype_t *type = func->param_types[i]; + param_ptrs[i] = ¶ms[i]; + params[i] = (exprval_t) { + .type = type, + .value = param_data + offs, + }; + offs = ((offs + type->size - 1) & ~(type->size - 1)); + offs += type->size; + } + *(exprfunc_t **) data[0] = func; + *(exprval_t ***) data[1] = param_ptrs; + *(void **) data[2] = param_data; + return 1; +} + +static int +parse_task_params (const plitem_t *item, void **data, + plitem_t *messages, parsectx_t *pctx) +{ + exprfunc_t *func = *(exprfunc_t **) data[0]; + exprval_t **params = *(exprval_t ***) data[1]; + if (!func) { + PL_Message (messages, item, "task function not set"); + return 0; + } + if (PL_A_NumObjects (item) != func->num_params) { + PL_Message (messages, item, "incorrect number of parameters"); + return 0; + } + for (int i = 0; i < func->num_params; i++) { + const char *paramstr = PL_String (PL_ObjectAtIndex (item, i)); + exprval_t *param = params[func->num_params - i - 1]; + exprctx_t ectx = *pctx->ectx; + if (param->type->data) { + ectx.parent = pctx->ectx; + ectx.symtab = ((exprenum_t *) param->type->data)->symtab; + } + // cexpr params are in reverse order + ectx.result = param; + + if (cexpr_eval_string (paramstr, &ectx)) { + PL_Message (messages, item, "error parsing param %d", i); + return 0; + } + } + return 1; +} + +#include "libs/video/renderer/vulkan/vkparse.cinc" + +static exprsym_t qfv_renderframeset_t_symbols[] = { + {"size", &cexpr_size_t, (void *)field_offset (qfv_renderframeset_t, size)}, + { } +}; +static exprtab_t qfv_renderframeset_t_symtab = { + qfv_renderframeset_t_symbols, +}; +exprtype_t qfv_renderframeset_t_type = { + .name = "frameset", + .size = sizeof (qfv_renderframeset_t *), + .binops = cexpr_struct_binops, + .unops = 0, + .data = &qfv_renderframeset_t_symtab, +}; + +static hashtab_t * +handlref_symtab (void (*free_func)(void*,void*), scriptctx_t *sctx) +{ + return Hash_NewTable (23, handleref_getkey, free_func, sctx, + &sctx->hashctx); +} + +static const char * +enum_symtab_getkey (const void *e, void *unused) +{ + __auto_type enm = (const exprenum_t *) e; + return enm->type->name; +} + +static const char * +parser_getkey (const void *e, void *unused) +{ + __auto_type parser = (const parserref_t *) e; + return parser->name; +} + +static exprtab_t root_symtab = { + .symbols = cexpr_lib_symbols, +}; + +static void +root_symtab_shutdown (void *data) +{ + Hash_DelTable (root_symtab.tab); +} + +static void __attribute__((constructor)) +root_symtab_init (void) +{ + // using a null hashctx here is safe because this function is run before + // main and thus before any possibility of threading. + exprctx_t root_context = { .symtab = &root_symtab }; + cexpr_init_symtab (&root_symtab, &root_context); + Sys_RegisterShutdown (root_symtab_shutdown, 0); +} + +exprenum_t * +QFV_GetEnum (const char *name) +{ + return Hash_Find (enum_symtab, name); +} + +static int +parse_object (vulkan_ctx_t *ctx, memsuper_t *memsuper, plitem_t *plist, + plparser_t parser, void *object, plitem_t *properties) +{ + scriptctx_t *sctx = ctx->script_context; + plitem_t *messages = PL_NewArray (); + exprctx_t exprctx = { .symtab = &root_symtab }; + parsectx_t parsectx = { &exprctx, ctx, properties }; + auto rctx = ctx->render_context; + exprsym_t var_syms[] = { + {"output", &qfv_output_t_type, &sctx->output}, + {"frames", &qfv_renderframeset_t_type, &rctx->frames}, + {"msaaSamples", &VkSampleCountFlagBits_type, &ctx->msaaSamples}, + {"physDevLimits", &VkPhysicalDeviceLimits_type, + &ctx->device->physDev->properties->limits }, + {QFV_PROPERTIES, &cexpr_plitem, &parsectx.properties}, + {} + }; + exprtab_t vars_tab = { var_syms, 0 }; + + exprctx.external_variables = &vars_tab; + exprctx.messages = messages; + exprctx.hashctx = &sctx->hashctx; + exprctx.memsuper = memsuper; + + cexpr_init_symtab (&vars_tab, &exprctx); + + + if (!parser (0, plist, object, messages, &parsectx)) { + for (int i = 0; i < PL_A_NumObjects (messages); i++) { + Sys_Printf ("%s\n", PL_String (PL_ObjectAtIndex (messages, i))); + } + return 0; + } + Hash_DelTable (vars_tab.tab); + PL_Release (messages); + + return 1; +} + +typedef struct { + uint32_t count; + VkImageCreateInfo *info; +} imagecreate_t; + +typedef struct { + uint32_t count; + VkImageViewCreateInfo *info; +} imageviewcreate_t; + +int +QFV_ParseOutput (vulkan_ctx_t *ctx, qfv_output_t *output, plitem_t *plist, + plitem_t *properties) +{ + memsuper_t *memsuper = new_memsuper (); + int ret = 0; + qfv_output_t op = {}; + + if (parse_object (ctx, memsuper, plist, parse_qfv_output_t, &op, + properties)) { + memcpy (output, &op, sizeof (*output)); + ret = 1; + } + delete_memsuper (memsuper); + return ret; +} + +int vulkan_frame_count; +static cvar_t vulkan_frame_count_cvar = { + .name = "vulkan_frame_count", + .description = + "Number of frames to render in the background. More frames can " + "increase performance, but at the cost of latency. The default of 3 is" + " recommended.", + .default_value = "3", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &vulkan_frame_count }, +}; +int vulkan_presentation_mode; +static cvar_t vulkan_presentation_mode_cvar = { + .name = "vulkan_presentation_mode", + .description = + "desired presentation mode (may fall back to fifo).", + .default_value = "mailbox", + .flags = CVAR_NONE, + .value = { + .type = &VkPresentModeKHR_type, + .value = &vulkan_presentation_mode, + }, +}; +int msaaSamples; +static cvar_t msaaSamples_cvar = { + .name = "msaaSamples", + .description = + "desired MSAA sample size.", + .default_value = "VK_SAMPLE_COUNT_1_BIT", + .flags = CVAR_NONE, + .value = { .type = &VkSampleCountFlagBits_type, .value = &msaaSamples }, +}; +static exprenum_t validation_enum; +static exprtype_t validation_type = { + .name = "vulkan_use_validation", + .size = sizeof (int), + .binops = cexpr_flag_binops, + .unops = cexpr_flag_unops, + .data = &validation_enum, + .get_string = cexpr_flags_get_string, +}; + +static int validation_values[] = { + 0, + VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT, +}; +static exprsym_t validation_symbols[] = { + {"none", &validation_type, validation_values + 0}, + {"all", &validation_type, validation_values + 1}, + {} +}; +static exprtab_t validation_symtab = { + validation_symbols, +}; +static exprenum_t validation_enum = { + &validation_type, + &validation_symtab, +}; +static cvar_t vulkan_use_validation_cvar = { + .name = "vulkan_use_validation", + .description = + "enable KRONOS Validation Layer if available (requires instance " + "restart).", + .default_value = "error|warning", + .flags = CVAR_NONE, + .value = { .type = &validation_type, .value = &vulkan_use_validation }, +}; + +static void +vulkan_frame_count_f (void *data, const cvar_t *cvar) +{ + if (vulkan_frame_count < 1) { + Sys_Printf ("Invalid frame count: %d. Setting to 1\n", + vulkan_frame_count); + vulkan_frame_count = 1; + } +} + +void +Vulkan_Init_Cvars (void) +{ + int num_syms = 0; + for (exprsym_t *sym = VkDebugUtilsMessageSeverityFlagBitsEXT_symbols; + sym->name; sym++, num_syms++) { + } + for (exprsym_t *sym = validation_symbols; sym->name; sym++, num_syms++) { + } + validation_symtab.symbols = calloc (num_syms + 1, sizeof (exprsym_t)); + num_syms = 0; + for (exprsym_t *sym = VkDebugUtilsMessageSeverityFlagBitsEXT_symbols; + sym->name; sym++, num_syms++) { + validation_symtab.symbols[num_syms] = *sym; + validation_symtab.symbols[num_syms].type = &validation_type; + } + for (exprsym_t *sym = validation_symbols; sym->name; sym++, num_syms++) { + validation_symtab.symbols[num_syms] = *sym; + } + Cvar_Register (&vulkan_use_validation_cvar, 0, 0); + // FIXME implement fallback choices (instead of just fifo) + Cvar_Register (&vulkan_presentation_mode_cvar, 0, 0); + Cvar_Register (&vulkan_frame_count_cvar, vulkan_frame_count_f, 0); + Cvar_Register (&msaaSamples_cvar, 0, 0); +} + +static exprsym_t builtin_plist_syms[] = { + { .name = "main_def", + .value = (void *) +#include "libs/video/renderer/vulkan/rp_main_def.plc" + }, + { .name = "smp_quake", + .value = (void *) +#include "libs/video/renderer/vulkan/smp_quake.plc" + }, + {} +}; +static plitem_t **builtin_plists; +static exprtab_t builtin_configs = { .symbols = builtin_plist_syms }; + +static void +build_configs (scriptctx_t *sctx) +{ + int num_plists = 0; + for (exprsym_t *sym = builtin_plist_syms; sym->name; sym++) { + num_plists++; + } + builtin_plists = malloc (num_plists * sizeof (plitem_t *)); + num_plists = 0; + for (exprsym_t *sym = builtin_plist_syms; sym->name; sym++) { + plitem_t *item = PL_GetDictionary (sym->value, &sctx->hashctx); + if (!item) { + // Syntax errors in the compiled-in plists are unrecoverable + Sys_Error ("Error parsing plist for %s", sym->name); + } + builtin_plists[num_plists] = item; + sym->value = &builtin_plists[num_plists]; + sym->type = &cexpr_plitem; + num_plists++; + } + exprctx_t ectx = { .hashctx = &sctx->hashctx }; + cexpr_init_symtab (&builtin_configs, &ectx); +} + +static void +delete_configs (void) +{ + int num_plists = 0; + for (exprsym_t *sym = builtin_plist_syms; sym->name; sym++) { + PL_Release (builtin_plists[num_plists]); + num_plists++; + } + free (builtin_plists); +} + +plitem_t * +Vulkan_GetConfig (vulkan_ctx_t *ctx, const char *name) +{ + scriptctx_t *sctx = ctx->script_context; + if (!builtin_configs.tab) { + build_configs (sctx); + } + + plitem_t *config = 0; + exprval_t result = { .type = &cexpr_plitem, .value = &config }; + exprctx_t ectx = { + .result = &result, + .symtab = &builtin_configs, + .memsuper = new_memsuper (), + .hashctx = &sctx->hashctx, + .messages = PL_NewArray (), + }; + if (cexpr_eval_string (name, &ectx)) { + dstring_t *msg = dstring_newstr (); + + for (int i = 0; i < PL_A_NumObjects (ectx.messages); i++) { + dasprintf (msg, "%s\n", + PL_String (PL_ObjectAtIndex (ectx.messages, i))); + } + Sys_Printf ("%s", msg->str); + dstring_delete (msg); + config = 0; + } + PL_Release (ectx.messages); + delete_memsuper (ectx.memsuper); + return config; +} + +void Vulkan_Script_Init (vulkan_ctx_t *ctx) +{ + scriptctx_t *sctx = calloc (1, sizeof (scriptctx_t)); + sctx->vctx = ctx; + ctx->script_context = sctx; + + exprctx_t ectx = {}; + enum_symtab = Hash_NewTable (61, enum_symtab_getkey, 0, 0, &sctx->hashctx); + parser_table = Hash_NewTable (61, parser_getkey, 0, 0, &sctx->hashctx); + //FIXME using the script context's hashctx causes the cvar system to access + //freed hash links due to shutdown order issues. The proper fix would be to + //create a symtabs shutdown (for thread safety), but this works for now. + ectx.hashctx = 0;//&sctx->hashctx; + vkgen_init_symtabs (&ectx); + cexpr_init_symtab (&qfv_output_t_symtab, &ectx); + cexpr_init_symtab (&qfv_renderframeset_t_symtab, &ectx); + cexpr_init_symtab (&data_array_symtab, &ectx); + + sctx->shaderModules = handlref_symtab (shaderModule_free, sctx); + sctx->setLayouts = handlref_symtab (setLayout_free, sctx); + sctx->pipelineLayouts = handlref_symtab (pipelineLayout_free, sctx); + sctx->descriptorPools = handlref_symtab (descriptorPool_free, sctx); + sctx->samplers = handlref_symtab (sampler_free, sctx); + sctx->images = handlref_symtab (image_free, sctx); + sctx->imageViews = handlref_symtab (imageView_free, sctx); + sctx->renderpasses = handlref_symtab (renderpass_free, sctx); +} + +static void +clear_table (hashtab_t **table) +{ + if (*table) { + hashtab_t *tab = *table; + *table = 0; + Hash_DelTable (tab); + } +} + +void Vulkan_Script_Shutdown (vulkan_ctx_t *ctx) +{ + scriptctx_t *sctx = ctx->script_context; + + clear_table (&sctx->pipelineLayouts); + clear_table (&sctx->setLayouts); + clear_table (&sctx->shaderModules); + clear_table (&sctx->descriptorPools); + clear_table (&sctx->samplers); + + delete_configs (); + + Hash_DelContext (sctx->hashctx); + + free (sctx); +} + +void Vulkan_Script_SetOutput (vulkan_ctx_t *ctx, qfv_output_t *output) +{ + scriptctx_t *sctx = ctx->script_context; + sctx->output = *output; +} + +exprtab_t * +QFV_CreateSymtab (plitem_t *dict, const char *properties, + const char **extra_items, exprsym_t *extra_syms, + exprctx_t *ectx) +{ + plitem_t *props = PL_ObjectForKey (dict, properties); + int num_keys = PL_D_NumKeys (props); + int num_extra = 0; + int num_syms = 0; + + if (extra_items) { + for (const char **e = extra_items; *e; e++) { + if (PL_ObjectForKey (dict, *e)) { + num_extra++; + } + } + } + if (extra_syms) { + for (exprsym_t *sym = extra_syms; sym->name; sym++, num_syms++) { } + } + + int total_items = num_keys + num_extra; + int total_syms = total_items + num_syms; + size_t size = (sizeof (exprtab_t) + + (total_syms + 1) * sizeof (exprsym_t) + + total_items * sizeof (plitem_t *)); + exprtab_t *symtab = malloc (size); + *symtab = (exprtab_t) { + .symbols = (exprsym_t *) &symtab[1], + }; + plitem_t **items = (plitem_t **) &symtab->symbols[total_syms + 1]; + for (int i = 0; i < num_keys; i++) { + symtab->symbols[i] = (exprsym_t) { + .name = PL_KeyAtIndex (props, i), + .type = &cexpr_plitem, + .value = items + i, + }; + items[i] = PL_ObjectForKey (props, symtab->symbols[i].name); + } + for (int i = 0, j = 0; num_extra && extra_items[i]; i++) { + plitem_t *val = PL_ObjectForKey (dict, extra_items[i]); + if (val) { + symtab->symbols[num_keys + j] = (exprsym_t) { + .name = extra_items[i], + .type = &cexpr_plitem, + .value = items + num_keys + j, + }; + items[num_keys + j] = val; + j++; + } + } + for (int i = 0; i < num_syms; i++) { + symtab->symbols[total_items + i] = extra_syms[i]; + } + symtab->symbols[total_syms] = (exprsym_t) { }; + + cexpr_init_symtab (symtab, ectx); + return symtab; +} + +void +QFV_DestroySymtab (exprtab_t *tab) +{ + Hash_DelTable (tab->tab); + free (tab); +} + +struct qfv_jobinfo_s * +QFV_ParseJobInfo (vulkan_ctx_t *ctx, plitem_t *item, qfv_renderctx_t *rctx) +{ + memsuper_t *memsuper = new_memsuper (); + qfv_jobinfo_t *ji = cmemalloc (memsuper, sizeof (qfv_jobinfo_t)); + *ji = (qfv_jobinfo_t) { .memsuper = memsuper }; + + scriptctx_t *sctx = ctx->script_context; + plitem_t *messages = PL_NewArray (); + + exprctx_t exprctx = { + .symtab = &root_symtab, + .messages = messages, + .hashctx = &sctx->hashctx, + .memsuper = memsuper, + }; + parsectx_t parsectx = { + .ectx = &exprctx, + .vctx = ctx, + .data = rctx, + }; + + static const char *extra_items[] = { + "images", + "imageviews", + "renderpasses", + 0 + }; + exprsym_t var_syms[] = { + {"render_output", &qfv_output_t_type, &sctx->output}, + {"frames", &qfv_renderframeset_t_type, &rctx->frames}, + {"msaaSamples", &VkSampleCountFlagBits_type, &ctx->msaaSamples}, + {"physDevLimits", &VkPhysicalDeviceLimits_type, + &ctx->device->physDev->properties->limits }, + {} + }; + exprctx.external_variables = QFV_CreateSymtab (item, "properties", + extra_items, var_syms, + &exprctx); + + int ret; + if (!(ret = parse_qfv_jobinfo_t (0, item, ji, messages, &parsectx))) { + for (int i = 0; i < PL_A_NumObjects (messages); i++) { + Sys_Printf ("%s\n", PL_String (PL_ObjectAtIndex (messages, i))); + } + } + QFV_DestroySymtab (exprctx.external_variables); + PL_Release (messages); + if (!ret) { + delete_memsuper (memsuper); + ji = 0; + } + + return ji; +} + +struct qfv_samplerinfo_s * +QFV_ParseSamplerInfo (vulkan_ctx_t *ctx, plitem_t *item, qfv_renderctx_t *rctx) +{ + memsuper_t *memsuper = new_memsuper (); + qfv_samplerinfo_t *si = cmemalloc (memsuper, sizeof (qfv_samplerinfo_t)); + *si = (qfv_samplerinfo_t) { .memsuper = memsuper }; + + scriptctx_t *sctx = ctx->script_context; + plitem_t *messages = PL_NewArray (); + + exprctx_t exprctx = { + .symtab = &root_symtab, + .messages = messages, + .hashctx = &sctx->hashctx, + .memsuper = memsuper, + }; + parsectx_t parsectx = { + .ectx = &exprctx, + .vctx = ctx, + .data = rctx, + }; + + exprsym_t var_syms[] = { + {"physDevLimits", &VkPhysicalDeviceLimits_type, + &ctx->device->physDev->properties->limits }, + {} + }; + exprctx.external_variables = QFV_CreateSymtab (item, "properties", + 0, var_syms, &exprctx); + + int ret; + if (!(ret = parse_qfv_samplerinfo_t (0, item, si, messages, &parsectx))) { + for (int i = 0; i < PL_A_NumObjects (messages); i++) { + Sys_Printf ("%s\n", PL_String (PL_ObjectAtIndex (messages, i))); + } + } + QFV_DestroySymtab (exprctx.external_variables); + PL_Release (messages); + if (!ret) { + delete_memsuper (memsuper); + si = 0; + } + + return si; +} + +int +QFV_ParseLayoutInfo (vulkan_ctx_t *ctx, memsuper_t *memsuper, + exprtab_t *symtab, const char *ref, + qfv_layoutinfo_t *layout) +{ + *layout = (qfv_layoutinfo_t) {}; + + scriptctx_t *sctx = ctx->script_context; + plitem_t *messages = PL_NewArray (); + + exprctx_t exprctx = { + .symtab = &root_symtab, + .messages = messages, + .hashctx = &sctx->hashctx, + .memsuper = memsuper, + .external_variables = symtab, + }; + parsectx_t parsectx = { + .ectx = &exprctx, + .vctx = ctx, + }; + + plitem_t *item = PL_NewString (ref); + int ret; + if (!(ret = parse_qfv_layoutinfo_t (0, item, layout, messages, + &parsectx))) { + for (int i = 0; i < PL_A_NumObjects (messages); i++) { + Sys_Printf ("%s\n", PL_String (PL_ObjectAtIndex (messages, i))); + } + } + PL_Release (messages); + PL_Release (item); + + return ret; +} diff --git a/libs/video/renderer/vulkan/vkparse.h b/libs/video/renderer/vulkan/vkparse.h new file mode 100644 index 000000000..edb86615a --- /dev/null +++ b/libs/video/renderer/vulkan/vkparse.h @@ -0,0 +1,62 @@ +#ifndef __vkparse_h +#define __vkparse_h + +#include "QF/Vulkan/render.h" + +typedef struct parsectx_s { + struct exprctx_s *ectx; + struct vulkan_ctx_s *vctx; + struct plitem_s *properties; + void *data; +} parsectx_t; + +typedef struct scriptctx_s { + struct vulkan_ctx_s *vctx; + struct hashctx_s *hashctx; + + struct plitem_s *pipelineDef; + struct hashtab_s *shaderModules; + struct hashtab_s *setLayouts; + struct hashtab_s *pipelineLayouts; + struct hashtab_s *descriptorPools; + struct hashtab_s *samplers; + struct hashtab_s *images; + struct hashtab_s *imageViews; + struct hashtab_s *renderpasses; + + qfv_output_t output; +} scriptctx_t; + +void Vulkan_Init_Cvars (void); +void Vulkan_Script_Init (struct vulkan_ctx_s *ctx); +void Vulkan_Script_Shutdown (struct vulkan_ctx_s *ctx); +void Vulkan_Script_SetOutput (struct vulkan_ctx_s *ctx, qfv_output_t *output); + +#include "QF/cexpr.h" +#include "QF/plist.h" + +#define QFV_PROPERTIES "properties" + +exprenum_t *QFV_GetEnum (const char *name); + +uint64_t QFV_GetHandle (struct hashtab_s *tab, const char *name); +void QFV_AddHandle (struct hashtab_s *tab, const char *name, uint64_t handle); + +int QFV_ParseOutput (vulkan_ctx_t *ctx, qfv_output_t *output, plitem_t *plist, + plitem_t *properties); +exprtab_t *QFV_CreateSymtab (plitem_t *dict, const char *properties, + const char **extra_items, exprsym_t *extra_syms, + exprctx_t *ectx); +void QFV_DestroySymtab (exprtab_t *tab); + +struct qfv_renderctx_s; +struct memsuper_s; +int QFV_ParseLayoutInfo (vulkan_ctx_t *ctx, struct memsuper_s *memsuper, + exprtab_t *symtab, const char *ref, + qfv_layoutinfo_t *layout); +struct qfv_jobinfo_s *QFV_ParseJobInfo (vulkan_ctx_t *ctx, plitem_t *item, + struct qfv_renderctx_s *rctx); +struct qfv_samplerinfo_s *QFV_ParseSamplerInfo (vulkan_ctx_t *ctx, + plitem_t *item, + struct qfv_renderctx_s *rctx); +#endif//__vkparse_h diff --git a/libs/video/renderer/vulkan/vkparse.plist b/libs/video/renderer/vulkan/vkparse.plist new file mode 100644 index 000000000..2efc22846 --- /dev/null +++ b/libs/video/renderer/vulkan/vkparse.plist @@ -0,0 +1,600 @@ +search = ( + VkDebugUtilsMessageSeverityFlagBitsEXT, + VkPresentModeKHR, + VkSpecializationInfo, + VkPipelineShaderStageCreateInfo, + VkPipelineVertexInputStateCreateInfo, + VkPipelineInputAssemblyStateCreateInfo, + VkPipelineViewportStateCreateInfo, + VkPipelineRasterizationStateCreateInfo, + VkPipelineMultisampleStateCreateInfo, + VkPipelineDepthStencilStateCreateInfo, + VkPipelineColorBlendStateCreateInfo, + VkPipelineDynamicStateCreateInfo, + VkDescriptorSetLayoutBinding, + VkClearValue, + VkPhysicalDeviceLimits, + VkRenderPassMultiviewCreateInfo, + + qfv_output_t, + + qfv_descriptorsetlayoutinfo_t, + qfv_imageinfo_t, + qfv_imageviewinfo_t, + qfv_bufferinfo_t, + qfv_bufferviewinfo_t, + qfv_dependencyinfo_t, + qfv_attachmentinfo_t, + qfv_attachmentrefinfo_t, + qfv_attachmentsetinfo_t, + qfv_taskinfo_t, + qfv_pushconstantinfo_t, + qfv_pushconstantrangeinfo_t, + qfv_layoutinfo_t, + qfv_pipelineinfo_t, + qfv_subpassinfo_t, + qfv_framebufferinfo_t, + qfv_renderpassinfo_t, + qfv_renderinfo_t, + qfv_computeinfo_t, + qfv_processinfo_t, + qfv_stepinfo_t, + qfv_jobinfo_t, + qfv_samplercreateinfo_t, + qfv_samplerinfo_t, +); +parse = { + VkPhysicalDeviceLimits = readonly; + VkSpecializationInfo = { + mapEntries = { + type = (array, VkSpecializationMapEntry); + size = mapEntryCount; + values = pMapEntries; + }; + data = { + type = (custom, (QFBinary, QFString), + parse_specialization_data); + fields = (dataSize, pData); + }; + }; + VkPipelineShaderStageCreateInfo = { + //flags = auto; reserved for future use (Bits enum does not exist) + stage = auto; + name = { + type = string; + string = pName; + }; + module = { + type = (custom, QFString, parse_VkShaderModule); + fields = (module); + }; + specializationInfo = { + type = (single, VkSpecializationInfo); + value = pSpecializationInfo; + }; + }; + VkShaderModuleCreateInfo = skip; + VkDescriptorSetLayoutBinding = { + binding = auto; + descriptorType = auto; + descriptorCount = auto; + stageFlags = auto; + // skip pImmutableSamplers (default to 0) until I know how it works + }; + VkPipelineVertexInputStateCreateInfo = { + //flags = auto; reserved for future use (Bits enum does not exist) + bindings = { + type = (array, VkVertexInputBindingDescription); + size = vertexBindingDescriptionCount; + values = pVertexBindingDescriptions; + }; + attributes = { + type = (array, VkVertexInputAttributeDescription); + size = vertexAttributeDescriptionCount; + values = pVertexAttributeDescriptions; + }; + }; + VkPipelineInputAssemblyStateCreateInfo = { + //flags = auto; reserved for future use (Bits enum does not exist) + topology = auto; + primitiveRestartEnable = auto; + }; + VkPipelineViewportStateCreateInfo = { + //flags = auto; reserved for future use (Bits enum does not exist) + //FIXME redo as one array + viewports = { + type = (array, VkViewport); + size = viewportCount; + values = pViewports; + }; + scissors = { + type = (array, VkRect2D); + size = scissorCount; + values = pScissors; + }; + }; + VkPipelineRasterizationStateCreateInfo = { + //flags = auto; reserved for future use (Bits enum does not exist) + depthClampEnable = auto; + rasterizerDiscardEnable = auto; + polygonMode = auto; + cullMode = auto; + frontFace = auto; + depthBiasEnable = auto; + depthBiasConstantFactor = auto; + depthBiasClamp = auto; + depthBiasSlopeFactor = auto; + lineWidth = auto; + }; + VkPipelineMultisampleStateCreateInfo = { + //flags = auto; reserved for future use (Bits enum does not exist) + rasterizationSamples = auto; + sampleShadingEnable = auto; + minSampleShading = auto; + //pSampleMask = auto; FIXME disabled until correct size is known + alphaToCoverageEnable = auto; + alphaToOneEnable = auto; + }; + VkPipelineDepthStencilStateCreateInfo = { + //flags = auto; reserved for future use (Bits enum does not exist) + depthTestEnable = auto; + depthWriteEnable = auto; + depthCompareOp = auto; + depthBoundsTestEnable = auto; + stencilTestEnable = auto; + front = auto; + back = auto; + minDepthBounds = auto; + maxDepthBounds = auto; + }; + VkPipelineColorBlendStateCreateInfo = { + //flags = auto; reserved for future use (Bits enum does not exist) + logicOpEnable = auto; + logicOp = auto; + attachments = { + type = (array, VkPipelineColorBlendAttachmentState); + size = attachmentCount; + values = pAttachments; + }; + blendConstants = { + type = (custom, QFString, parse_RGBA); + fields = (blendConstants); + }; + }; + VkPipelineDynamicStateCreateInfo = { + //flags = auto; reserved for future use (Bits enum does not exist) + dynamicState = { + type = (array, VkDynamicState); + size = dynamicStateCount; + values = pDynamicStates; + }; + }; + VkPipelineTessellationStateCreateInfo = { + //flags = auto; reserved for future use (Bits enum does not exist) + patchControlPoints = auto; + }; + VkClearColorValue = skip; + VkClearValue = { + color = { + type = (custom, QFString, parse_RGBA); + fields = (color); + }; + depthStencil = auto; + }; + VkRenderPassMultiviewCreateInfo = { + viewMasks = { + type = (array, uint32_t); + size = subpassCount; + values = pViewMasks; + }; + viewOffsets = { + type = (array, int32_t); + size = dependencyCount; + values = pViewOffsets; + }; + correlationMasks = { + type = (array, uint32_t); + size = correlationMaskCount; + values = pCorrelationMasks; + }; + }; + + qfv_output_s = { + .name = qfv_output_t; + extent = auto; + image = ignore; + view = ignore; +/* image = { + type = (readonly, VkImage); + value = image; + }; + view = { + type = (readonly, VkImageView); + value = view; + };*/ + format = auto; + frames = auto; + finalLayout = auto; + }; + + qfv_taskinfo_s = { + .name = qfv_taskinfo_t; + func = { + type = (custom, QFString, parse_task_function); + fields = (func, params, param_data); + }; + params = { + type = (custom, QFArray, parse_task_params); + fields = (func, params, param_data); + }; + }; + qfv_attachmentrefinfo_s = { + .name = qfv_attachmentrefinfo_t; + .type = (QFString, QFDictionary); + .string = { + name = $name; + line = $item.line; + layout = $auto; + blend = "(VkPipelineColorBlendAttachmentState) { .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT }" + }; + .dictionary = { + .parse = auto; + name = $name; + line = $item.line; + }; + layout = auto; + blend = auto; + }; + qfv_attachmentsetinfo_s = { + .name = qfv_attachmentsetinfo_t; + input = { + type = (labeledarray, qfv_attachmentrefinfo_t, name); + size = num_input; + values = input; + }; + color = { + type = (labeledarray, qfv_attachmentrefinfo_t, name); + size = num_color; + values = color; + }; + resolve = { + type = (labeledarray, qfv_attachmentrefinfo_t, name); + values = resolve; + matchSize = color; + }; + depth = { + type = (labeledsingle, qfv_attachmentrefinfo_t, name); + value = depth; + }; + preserve = { + type = (array, qfv_reference_t); + size = num_preserve; + values = preserve; + }; + }; + qfv_pushconstantinfo_s = { + .name = qfv_pushconstantinfo_t; + .type = (QFString, QFDictionary); + .dictionary = { + offset = -1; + size = -1; + .parse = auto; + name = $name; + line = $item.line; + }; + .string = { + offset = -1; + size = -1; + name = $name; + line = $item.line; + type = $auto; + }; + type = auto; + //stageFlags = auto; + offset = auto; + size = auto; + }; + qfv_pushconstantrangeinfo_s = { + .name = qfv_pushconstantrangeinfo_t; + .type = (QFDictionary); + .dictionary = { + .parse = { + type = (labeledarray, qfv_pushconstantinfo_t, name); + size = num_pushconstants; + values = pushconstants; + }; + stageFlags = $name.auto; + }; + stageFlags = auto; + }; + qfv_layoutinfo_s = { + .name = qfv_layoutinfo_t; + descriptorSets = { + type = (array, qfv_reference_t); + size = num_sets; + values = sets; + }; + pushConstants = { + type = (labeledarray, qfv_pushconstantrangeinfo_t, stageFlags); + size = num_pushconstantranges; + values = pushconstantranges; + }; + }; + qfv_pipelineinfo_s = { + .name = qfv_pipelineinfo_t; + color = auto; + name = { + type = string; + string = name; + }; + disabled = auto; + tasks = { + type = (array, qfv_taskinfo_t); + size = num_tasks; + values = tasks; + }; + + flags = auto; + stages = { + type = (array, VkPipelineShaderStageCreateInfo); + size = num_graph_stages; + values = graph_stages; + }; + stage = { + type = (single, VkPipelineShaderStageCreateInfo); + value = compute_stage; + }; + dispatch = auto; + vertexInput = { + type = (single, VkPipelineVertexInputStateCreateInfo); + value = vertexInput; + }; + inputAssembly = { + type = (single, VkPipelineInputAssemblyStateCreateInfo); + value = inputAssembly; + }; + tessellation = { + type = (single, VkPipelineTessellationStateCreateInfo); + value = tessellation; + }; + viewport = { + type = (single, VkPipelineViewportStateCreateInfo); + value = viewport; + }; + rasterization = { + type = (single, VkPipelineRasterizationStateCreateInfo); + value = rasterization; + }; + multisample = { + type = (single, VkPipelineMultisampleStateCreateInfo); + value = multisample; + }; + depthStencil = { + type = (single, VkPipelineDepthStencilStateCreateInfo); + value = depthStencil; + }; + colorBlend = { + type = (single, VkPipelineColorBlendStateCreateInfo); + value = colorBlend; + }; + dynamic = { + type = (single, VkPipelineDynamicStateCreateInfo); + value = dynamic; + }; + layout = auto; + }; + qfv_subpassinfo_s = { + .name = qfv_subpassinfo_t; + name = { + type = string; + string = name; + }; + color = auto; + dependencies = { + type = (labeledarray, qfv_dependencyinfo_t, name); + size = num_dependencies; + values = dependencies; + }; + attachments = { + type = (single, qfv_attachmentsetinfo_t); + value = attachments; + }; + pipelines = { + type = (labeledarray, qfv_pipelineinfo_t, name); + size = num_pipelines; + values = pipelines; + }; + base_pipeline = { + //type = (labeledsingle, qfv_pipelineinfo_t, name); + type = (single, qfv_pipelineinfo_t, name); + value = base_pipeline; + }; + }; + qfv_attachmentinfo_s = { + .name = qfv_attachmentinfo_t; + .type = (QFString, QFDictionary); + .string = { + name = $name; + line = $item.line; + external = $item.string; + }; + .dictionary = { + .parse = auto; + name = $name; + line = $item.line; + }; + flags = auto; + format = auto; + samples = auto; + loadOp = auto; + storeOp = auto; + stencilLoadOp = auto; + stencilStoreOp = auto; + initialLayout = auto; + finalLayout = auto; + clearValue = auto; + view = auto; + }; + qfv_framebufferinfo_s = { + .name = qfv_framebufferinfo_t; + width = auto; + height = auto; + layers = auto; + attachments = { + type = (labeledarray, qfv_attachmentinfo_t, name); + size = num_attachments; + values = attachments; + }; + }; + qfv_reference_s = { + .name = qfv_reference_t; + .type = QFString; + .string = { + name = $item.string; + line = $item.line; + }; + }; + qfv_renderpassinfo_s = { + .name = qfv_renderpassinfo_t; + color = auto; + framebuffer = auto; + subpasses = { + type = (labeledarray, qfv_subpassinfo_t, name); + size = num_subpasses; + values = subpasses; + }; + output = auto; + }; + qfv_descriptorsetlayoutinfo_s = { + .name = qfv_descriptorsetlayoutinfo_t; + flags = auto; + bindings = { + type = (array, VkDescriptorSetLayoutBinding); + size = num_bindings; + values = bindings; + }; + }; + qfv_bufferviewinfo_s = { + .name = qfv_bufferviewinfo_t; + buffer = auto; + format = auto; + offset = auto; + range = auto; + }; + qfv_computeinfo_s = { + .name = qfv_computeinfo_t; + color = auto; + pipelines = { + type = (labeledarray, qfv_pipelineinfo_t, name); + size = num_pipelines; + values = pipelines; + }; + }; + qfv_renderinfo_s = { + .name = qfv_renderinfo_t; + color = auto; + renderpasses = { + type = (labeledarray, qfv_renderpassinfo_t, name); + size = num_renderpasses; + values = renderpasses; + }; + }; + qfv_processinfo_s = { + .name = qfv_processinfo_t; + color = auto; + tasks = { + type = (array, qfv_taskinfo_t); + size = num_tasks; + values = tasks; + }; + }; + qfv_stepinfo_s = { + .name = qfv_stepinfo_t; + color = auto; + dependencies = { + type = (array, qfv_reference_t); + size = num_dependencies; + values = dependencies; + }; + render = { + type = (single, qfv_renderinfo_t, name); + value = render; + }; + compute = { + type = (single, qfv_computeinfo_t, name); + value = compute; + }; + process = { + type = (single, qfv_processinfo_t, name); + value = process; + }; + }; + qfv_jobinfo_s = { + .name = qfv_jobinfo_t; + properties = ignore; + output = ignore; + images = ignore; + views = ignore; + renderpasses = ignore; + images = { + type = (labeledarray, qfv_imageinfo_t, name); + size = num_images; + values = images; + }; + imageviews = { + type = (labeledarray, qfv_imageviewinfo_t, name); + size = num_imageviews; + values = imageviews; + }; + buffers = { + type = (labeledarray, qfv_bufferinfo_t, name); + size = num_buffers; + values = buffers; + }; + bufferviews = { + type = (labeledarray, qfv_bufferviewinfo_t, name); + size = num_bufferviews; + values = bufferviews; + }; + steps = { + type = (labeledarray, qfv_stepinfo_t, name); + size = num_steps; + values = steps; + }; + descriptorSetLayouts = { + type = (labeledarray, qfv_descriptorsetlayoutinfo_t, name); + size = num_dslayouts; + values = dslayouts; + }; + plitem = ignore; + }; + qfv_samplercreateinfo_s = { + .name = qfv_samplercreateinfo_t; + flags = auto; + magFilter = auto; + minFilter = auto; + mipmapMode = auto; + addressModeU = auto; + addressModeV = auto; + addressModeW = auto; + mipLodBias = auto; + anisotropyEnable = auto; + maxAnisotropy = auto; + compareEnable = auto; + compareOp = auto; + minLod = auto; + maxLod = auto; + borderColor = auto; + unnormalizedCoordinates = auto; + sampler = ignore; + }; + qfv_samplerinfo_s = { + .name = qfv_samplerinfo_t; + samplers = { + type = (labeledarray, qfv_samplercreateinfo_t, name); + size = num_samplers; + values = samplers; + }; + plitem = ignore; + }; +}; diff --git a/libs/video/renderer/vulkan/vulkan_alias.c b/libs/video/renderer/vulkan/vulkan_alias.c new file mode 100644 index 000000000..914e1ae28 --- /dev/null +++ b/libs/video/renderer/vulkan/vulkan_alias.c @@ -0,0 +1,267 @@ +/* + vulkan_alias.c + + Vulkan alias model pipeline + + Copyright (C) 2012 Bill Currie + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2012/1/7 + Date: 2021/1/26 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "QF/cvar.h" +#include "QF/va.h" + +#include "QF/scene/entity.h" + +#include "QF/Vulkan/qf_alias.h" +#include "QF/Vulkan/qf_matrices.h" +#include "QF/Vulkan/qf_palette.h" +#include "QF/Vulkan/qf_texture.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/render.h" + +#include "r_internal.h" +#include "vid_vulkan.h" + +typedef struct { + mat4f_t mat; + float blend; + byte colors[4]; + vec4f_t base_color; + vec4f_t fog; +} alias_push_constants_t; + +static void +emit_commands (VkCommandBuffer cmd, int pose1, int pose2, + qfv_alias_skin_t *skin, + uint32_t numPC, qfv_push_constants_t *constants, + aliashdr_t *hdr, qfv_taskctx_t *taskctx, entity_t ent) +{ + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto layout = taskctx->pipeline->layout; + + __auto_type mesh = (qfv_alias_mesh_t *) ((byte *) hdr + hdr->commands); + + VkDeviceSize offsets[] = { + pose1 * hdr->poseverts * sizeof (aliasvrt_t), + pose2 * hdr->poseverts * sizeof (aliasvrt_t), + 0, + }; + VkBuffer buffers[] = { + mesh->vertex_buffer, + mesh->vertex_buffer, + mesh->uv_buffer, + }; + int bindingCount = skin ? 3 : 2; + + Vulkan_BeginEntityLabel (ctx, cmd, ent); + + dfunc->vkCmdBindVertexBuffers (cmd, 0, bindingCount, buffers, offsets); + dfunc->vkCmdBindIndexBuffer (cmd, mesh->index_buffer, 0, + VK_INDEX_TYPE_UINT32); + QFV_PushConstants (device, cmd, layout, numPC, constants); + if (skin) { + VkDescriptorSet sets[] = { + skin->descriptor, + }; + dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + layout, 2, 1, sets, 0, 0); + } + dfunc->vkCmdDrawIndexed (cmd, 3 * hdr->mdl.numtris, 1, 0, 0, 0); + + QFV_CmdEndLabel (device, cmd); +} + +static void +alias_depth_range (qfv_taskctx_t *taskctx, float minDepth, float maxDepth) +{ + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + + auto viewport = taskctx->pipeline->viewport; + viewport.minDepth = minDepth; + viewport.maxDepth = maxDepth; + + dfunc->vkCmdSetViewport (taskctx->cmd, 0, 1, &viewport); +} + +void +Vulkan_AliasAddSkin (vulkan_ctx_t *ctx, qfv_alias_skin_t *skin) +{ + aliasctx_t *actx = ctx->alias_context; + skin->descriptor = Vulkan_CreateCombinedImageSampler (ctx, skin->view, + actx->sampler); +} + +void +Vulkan_AliasRemoveSkin (vulkan_ctx_t *ctx, qfv_alias_skin_t *skin) +{ + Vulkan_FreeTexture (ctx, skin->descriptor); + skin->descriptor = 0; +} + +static void +alias_draw_ent (qfv_taskctx_t *taskctx, entity_t ent, bool pass) +{ + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); + auto model = renderer->model; + aliashdr_t *hdr; + qfv_alias_skin_t *skin; + alias_push_constants_t constants = {}; + + if (!(hdr = model->aliashdr)) { + hdr = Cache_Get (&model->cache); + } + + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, + ent.reg); + constants.blend = R_AliasGetLerpedFrames (animation, hdr); + + transform_t transform = Entity_Transform (ent); + qfv_push_constants_t push_constants[] = { + { VK_SHADER_STAGE_VERTEX_BIT, + field_offset (alias_push_constants_t, mat), + sizeof (mat4f_t), Transform_GetWorldMatrixPtr (transform) }, + { VK_SHADER_STAGE_VERTEX_BIT, + field_offset (alias_push_constants_t, blend), + sizeof (float), &constants.blend }, + { VK_SHADER_STAGE_FRAGMENT_BIT, + field_offset (alias_push_constants_t, colors), + sizeof (constants.colors), constants.colors }, + { VK_SHADER_STAGE_FRAGMENT_BIT, + field_offset (alias_push_constants_t, base_color), + sizeof (constants.base_color), &constants.base_color }, + { VK_SHADER_STAGE_FRAGMENT_BIT, + field_offset (alias_push_constants_t, fog), + sizeof (constants.fog), &constants.fog }, + }; + + if (0/*XXX ent->skin && ent->skin->tex*/) { + //skin = ent->skin->tex; + } else { + maliasskindesc_t *skindesc; + skindesc = R_AliasGetSkindesc (animation, renderer->skinnum, hdr); + skin = (qfv_alias_skin_t *) ((byte *) hdr + skindesc->skin); + } + QuatCopy (renderer->colormod, constants.base_color); + QuatCopy (skin->colors, constants.colors); + if (Ent_HasComponent (ent.id, scene_colormap, ent.reg)) { + colormap_t *colormap=Ent_GetComponent (ent.id, scene_colormap, ent.reg); + constants.colors[0] = colormap->top * 16 + 8; + constants.colors[1] = colormap->bottom * 16 + 8; + } + QuatZero (constants.fog); + + emit_commands (taskctx->cmd, animation->pose1, animation->pose2, + pass ? skin : 0, + pass ? 5 : 2, push_constants, + hdr, taskctx, ent); +} + +static void +alias_draw (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + bool vmod = Entity_Valid (vr_data.view_model); + int pass = *(int *) params[0]->value; + + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto layout = taskctx->pipeline->layout; + auto cmd = taskctx->cmd; + VkDescriptorSet sets[] = { + Vulkan_Matrix_Descriptors (ctx, ctx->curFrame), + Vulkan_Palette_Descriptor (ctx), + }; + dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + layout, 0, 2, sets, 0, 0); + + auto queue = r_ent_queue; //FIXME fetch from scene + for (size_t i = 0; i < queue->ent_queues[mod_alias].size; i++) { + entity_t ent = queue->ent_queues[mod_alias].a[i]; + // FIXME hack the depth range to prevent view model + // from poking into walls + if (vmod && ent.id == vr_data.view_model.id) { + alias_depth_range (taskctx, 0, 0.3); + } + alias_draw_ent (taskctx, ent, pass); + // unhack in case the view_model is not the last + if (vmod && ent.id == vr_data.view_model.id) { + alias_depth_range (taskctx, 0, 1); + } + } +} + +static exprtype_t *alias_draw_params[] = { + &cexpr_int, +}; +static exprfunc_t alias_draw_func[] = { + { .func = alias_draw, .num_params = 1, .param_types = alias_draw_params }, + {} +}; +static exprsym_t alias_task_syms[] = { + { "alias_draw", &cexpr_function, alias_draw_func }, + {} +}; + +void +Vulkan_Alias_Init (vulkan_ctx_t *ctx) +{ + qfvPushDebug (ctx, "alias init"); + QFV_Render_AddTasks (ctx, alias_task_syms); + + aliasctx_t *actx = calloc (1, sizeof (aliasctx_t)); + ctx->alias_context = actx; + + qfvPopDebug (ctx); +} + +void +Vulkan_Alias_Setup (vulkan_ctx_t *ctx) +{ + auto actx = ctx->alias_context; + actx->sampler = QFV_Render_Sampler (ctx, "alias_sampler"); +} + +void +Vulkan_Alias_Shutdown (vulkan_ctx_t *ctx) +{ + //qfv_device_t *device = ctx->device; + //qfv_devfuncs_t *dfunc = device->funcs; + aliasctx_t *actx = ctx->alias_context; + + free (actx); +} diff --git a/libs/video/renderer/vulkan/vulkan_bsp.c b/libs/video/renderer/vulkan/vulkan_bsp.c new file mode 100644 index 000000000..7cfd3481f --- /dev/null +++ b/libs/video/renderer/vulkan/vulkan_bsp.c @@ -0,0 +1,1493 @@ +/* + vulkan_bsp.c + + Vulkan bsp + + Copyright (C) 2012 Bill Currie + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2012/1/7 + Date: 2021/1/18 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "qfalloca.h" + +#include "QF/cvar.h" +#include "QF/darray.h" +#include "QF/heapsort.h" +#include "QF/image.h" +#include "QF/render.h" +#include "QF/sys.h" +#include "QF/va.h" + +#include "QF/math/bitop.h" +#include "QF/scene/entity.h" + +#include "QF/Vulkan/qf_bsp.h" +#include "QF/Vulkan/qf_lightmap.h" +#include "QF/Vulkan/qf_matrices.h" +#include "QF/Vulkan/qf_scene.h" +#include "QF/Vulkan/qf_texture.h" +#include "QF/Vulkan/qf_translucent.h" +#include "QF/Vulkan/buffer.h" +#include "QF/Vulkan/barrier.h" +#include "QF/Vulkan/command.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/descriptor.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/image.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/render.h" +#include "QF/Vulkan/scrap.h" +#include "QF/Vulkan/staging.h" + +#include "QF/simd/types.h" + +#include "r_internal.h" +#include "vid_vulkan.h" + +#define TEX_SET 3 +#define SKYBOX_SET 4 + +typedef struct bsp_push_constants_s { + quat_t fog; + float time; + float alpha; + float turb_scale; +} bsp_push_constants_t; + +static const char *bsp_pass_names[] = { + "depth", + "g-buffer", + "sky", + "turb", +}; + +static void +add_texture (texture_t *tx, vulkan_ctx_t *ctx) +{ + bspctx_t *bctx = ctx->bsp_context; + + vulktex_t *tex = tx->render; + if (tex->tex) { + tex->tex_id = bctx->registered_textures.size; + DARRAY_APPEND (&bctx->registered_textures, tex); + tex->descriptor = Vulkan_CreateTextureDescriptor (ctx, tex->tex, + bctx->sampler); + } +} + +static inline void +chain_surface (const bsp_face_t *face, bsp_pass_t *pass, const bspctx_t *bctx) +{ + int ent_frame = pass->ent_frame; + // if the texture has no alt animations, anim_alt holds the sama data + // as anim_main + const texanim_t *anim = ent_frame ? &bctx->texdata.anim_alt[face->tex_id] + : &bctx->texdata.anim_main[face->tex_id]; + int anim_ind = (bctx->anim_index + anim->offset) % anim->count; + int tex_id = bctx->texdata.frame_map[anim->base + anim_ind]; + DARRAY_APPEND (&pass->face_queue[tex_id], + ((instface_t) { pass->inst_id, face - bctx->faces })); +} + +static void +register_textures (mod_brush_t *brush, vulkan_ctx_t *ctx) +{ + texture_t *tex; + + for (unsigned i = 0; i < brush->numtextures; i++) { + tex = brush->textures[i]; + if (!tex) + continue; + add_texture (tex, ctx); + } +} + +static void +clear_textures (vulkan_ctx_t *ctx) +{ + bspctx_t *bctx = ctx->bsp_context; + + if (bctx->main_pass.face_queue) { + for (size_t i = 0; i < bctx->registered_textures.size; i++) { + DARRAY_CLEAR (&bctx->main_pass.face_queue[i]); + } + free (bctx->main_pass.face_queue); + bctx->main_pass.face_queue = 0; + } + + bctx->registered_textures.size = 0; +} + +void +Vulkan_RegisterTextures (model_t **models, int num_models, vulkan_ctx_t *ctx) +{ + clear_textures (ctx); + add_texture (r_notexture_mip, ctx); + { + // FIXME make worldmodel non-special. needs smarter handling of + // textures on sub-models but not on main model. + mod_brush_t *brush = &r_refdef.worldmodel->brush; + register_textures (brush, ctx); + } + for (int i = 0; i < num_models; i++) { + model_t *m = models[i]; + if (!m) + continue; + // sub-models are done as part of the main model + if (*m->path == '*') + continue; + // world has already been done, not interested in non-brush models + // FIXME see above + if (m == r_refdef.worldmodel || m->type != mod_brush) + continue; + mod_brush_t *brush = &m->brush; + brush->numsubmodels = 1; // no support for submodels in non-world model + register_textures (brush, ctx); + } + + bspctx_t *bctx = ctx->bsp_context; + int num_tex = bctx->registered_textures.size; + + texture_t **textures = alloca (num_tex * sizeof (texture_t *)); + textures[0] = r_notexture_mip; + for (int i = 0, t = 1; i < num_models; i++) { + model_t *m = models[i]; + // sub-models are done as part of the main model + if (!m || *m->path == '*') { + continue; + } + mod_brush_t *brush = &m->brush; + for (unsigned j = 0; j < brush->numtextures; j++) { + if (brush->textures[j]) { + textures[t++] = brush->textures[j]; + } + } + } + + // 2.5 for two texanim_t structs (32-bits each) and 1 uint16_t for each + // element + size_t texdata_size = 2.5 * num_tex * sizeof (texanim_t); + texanim_t *texdata = Hunk_AllocName (0, texdata_size, "texdata"); + bctx->texdata.anim_main = texdata; + bctx->texdata.anim_alt = texdata + num_tex; + bctx->texdata.frame_map = (uint16_t *) (texdata + 2 * num_tex); + int16_t map_index = 0; + for (int i = 0; i < num_tex; i++) { + texanim_t *anim = bctx->texdata.anim_main + i; + if (anim->count) { + // already done as part of an animation group + continue; + } + *anim = (texanim_t) { .base = map_index, .offset = 0, .count = 1 }; + bctx->texdata.frame_map[anim->base] = i; + + if (textures[i]->anim_total > 1) { + // bsp loader multiplies anim_total by ANIM_CYCLE to slow the + // frame rate + anim->count = textures[i]->anim_total / ANIM_CYCLE; + texture_t *tx = textures[i]->anim_next; + for (int j = 1; j < anim->count; j++) { + if (!tx) { + Sys_Error ("broken cycle"); + } + vulktex_t *vtex = tx->render; + texanim_t *a = bctx->texdata.anim_main + vtex->tex_id; + if (a->count) { + Sys_Error ("crossed cycle"); + } + *a = *anim; + a->offset = j; + bctx->texdata.frame_map[a->base + a->offset] = vtex->tex_id; + tx = tx->anim_next; + } + if (tx != textures[i]) { + Sys_Error ("infinite cycle"); + } + }; + map_index += bctx->texdata.anim_main[i].count; + } + for (int i = 0; i < num_tex; i++) { + texanim_t *alt = bctx->texdata.anim_alt + i; + if (textures[i]->alternate_anims) { + texture_t *tx = textures[i]->alternate_anims; + vulktex_t *vtex = tx->render; + *alt = bctx->texdata.anim_main[vtex->tex_id]; + } else { + *alt = bctx->texdata.anim_main[i]; + } + } + + // create face queue arrays + bctx->main_pass.face_queue = malloc (num_tex * sizeof (bsp_instfaceset_t)); + for (int i = 0; i < num_tex; i++) { + bctx->main_pass.face_queue[i] + = (bsp_instfaceset_t) DARRAY_STATIC_INIT (128); + } +} + +typedef struct { + msurface_t *face; + model_t *model; + int model_face_base; +} faceref_t; + +typedef struct DARRAY_TYPE (faceref_t) facerefset_t; + +static void +count_verts_inds (const faceref_t *faceref, uint32_t *verts, uint32_t *inds) +{ + msurface_t *surf = faceref->face; + *verts = surf->numedges; + *inds = surf->numedges + 1; +} + +typedef struct bspvert_s { + quat_t vertex; + quat_t tlst; +} bspvert_t; + +typedef struct { + bsp_face_t *faces; + uint32_t *indices; + bspvert_t *vertices; + uint32_t index_base; + uint32_t vertex_base; + int tex_id; +} buildctx_t; + +static void +build_surf_displist (const faceref_t *faceref, buildctx_t *build) +{ + msurface_t *surf = faceref->face; + mod_brush_t *brush = &faceref->model->brush;; + + int facenum = surf - brush->surfaces; + bsp_face_t *face = &build->faces[facenum + faceref->model_face_base]; + // create a triangle fan + int numverts = surf->numedges; + face->first_index = build->index_base; + face->index_count = numverts + 1; // +1 for primitive restart + face->tex_id = build->tex_id; + face->flags = surf->flags; + build->index_base += face->index_count; + for (int i = 0; i < numverts; i++) { + build->indices[face->first_index + i] = build->vertex_base + i; + } + build->indices[face->first_index + numverts] = -1; // primitive restart + + bspvert_t *verts = build->vertices + build->vertex_base; + build->vertex_base += numverts; + mtexinfo_t *texinfo = surf->texinfo; + mvertex_t *vertices = brush->vertexes; + medge_t *edges = brush->edges; + int *surfedges = brush->surfedges; + for (int i = 0; i < numverts; i++) { + vec_t *vec; + int index = surfedges[surf->firstedge + i]; + if (index > 0) { + // forward edge + vec = vertices[edges[index].v[0]].position; + } else { + // reverse edge + vec = vertices[edges[-index].v[1]].position; + } + VectorCopy (vec, verts[i].vertex); + verts[i].vertex[3] = 1; // homogeneous coord + + vec2f_t st = { + DotProduct (vec, texinfo->vecs[0]) + texinfo->vecs[0][3], + DotProduct (vec, texinfo->vecs[1]) + texinfo->vecs[1][3], + }; + verts[i].tlst[0] = st[0] / texinfo->texture->width; + verts[i].tlst[1] = st[1] / texinfo->texture->height; + + if (surf->lightpic) { + //lightmap texture coordinates + //every lit surface has its own lighmap at a 1/16 resolution + //(ie, 16 albedo pixels for every lightmap pixel) + const vrect_t *rect = surf->lightpic->rect; + vec2f_t lmorg = (vec2f_t) { VEC2_EXP (&rect->x) } * 16 + 8; + vec2f_t texorg = { VEC2_EXP (surf->texturemins) }; + st = ((st - texorg + lmorg) / 16) * surf->lightpic->size; + verts[i].tlst[2] = st[0]; + verts[i].tlst[3] = st[1]; + } else { + // no lightmap for this surface (probably sky or water), so + // make the lightmap texture polygone degenerate + verts[i].tlst[2] = 0; + verts[i].tlst[3] = 0; + } + } +} + +void +Vulkan_BuildDisplayLists (model_t **models, int num_models, vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + bspctx_t *bctx = ctx->bsp_context; + + if (!num_models) { + return; + } + + facerefset_t *face_sets = alloca (bctx->registered_textures.size + * sizeof (facerefset_t)); + for (size_t i = 0; i < bctx->registered_textures.size; i++) { + face_sets[i] = (facerefset_t) DARRAY_STATIC_INIT (1024); + } + + for (int i = 0; i < bctx->num_models; i++) { + DARRAY_CLEAR (&bctx->main_pass.instances[i].entities); + } + free (bctx->main_pass.instances); + bctx->num_models = 0; + + // run through all surfaces, chaining them to their textures, thus + // effectively sorting the surfaces by texture (without worrying about + // surface order on the same texture chain). + int face_base = 0; + for (int i = 0; i < num_models; i++) { + model_t *m = models[i]; + // sub-models are done as part of the main model + // and non-bsp models don't have surfaces. + if (!m || m->type != mod_brush) { + continue; + } + m->render_id = bctx->num_models++; + if (*m->path == '*') { + continue; + } + mod_brush_t *brush = &m->brush; + dmodel_t *dm = brush->submodels; + for (unsigned j = 0; j < brush->numsurfaces; j++) { + if (j == dm->firstface + dm->numfaces) { + // move on to the next sub-model + dm++; + if (dm == brush->submodels + brush->numsubmodels) { + // limit the surfaces + // probably never hit + Sys_Printf ("Vulkan_BuildDisplayLists: too many faces\n"); + brush->numsurfaces = j; + break; + } + } + msurface_t *surf = brush->surfaces + j; + // append surf to the texture chain + vulktex_t *tex = surf->texinfo->texture->render; + DARRAY_APPEND (&face_sets[tex->tex_id], + ((faceref_t) { surf, m, face_base })); + } + face_base += brush->numsurfaces; + } + bctx->main_pass.instances = malloc (bctx->num_models + * sizeof (bsp_instance_t)); + for (int i = 0; i < bctx->num_models; i++) { + DARRAY_INIT (&bctx->main_pass.instances[i].entities, 16); + } + // All vertices from all brush models go into one giant vbo. + uint32_t vertex_count = 0; + uint32_t index_count = 0; + uint32_t poly_count = 0; + // This is not optimal as counted vertices are not shared between faces, + // however this greatly simplifies display list creation as no care needs + // to be taken when it comes to UVs, and every vertex needs a unique light + // map UV anyway (when lightmaps are used). + for (size_t i = 0; i < bctx->registered_textures.size; i++) { + for (size_t j = 0; j < face_sets[i].size; j++) { + faceref_t *faceref = &face_sets[i].a[j]; + uint32_t verts, inds; + count_verts_inds (faceref, &verts, &inds); + vertex_count += verts; + index_count += inds; + poly_count++; + } + } + + size_t atom = device->physDev->properties->limits.nonCoherentAtomSize; + size_t atom_mask = atom - 1; + size_t frames = bctx->frames.size; + size_t index_buffer_size = index_count * frames * sizeof (uint32_t); + size_t vertex_buffer_size = vertex_count * sizeof (bspvert_t); + + index_buffer_size = (index_buffer_size + atom_mask) & ~atom_mask; + qfv_stagebuf_t *stage = QFV_CreateStagingBuffer (device, "bsp", + vertex_buffer_size, + ctx->cmdpool); + qfv_packet_t *packet = QFV_PacketAcquire (stage); + bspvert_t *vertices = QFV_PacketExtend (packet, vertex_buffer_size); + // holds all the polygon definitions: vertex indices + poly_count + // primitive restart markers. The primitive restart markers are included + // in index_count. + // so each polygon within the list: + // index count-1 indices + // index + // ... + // "end of primitive" (~0u) + free (bctx->faces); + free (bctx->poly_indices); + free (bctx->models); + bctx->models = malloc (bctx->num_models * sizeof (bsp_model_t)); + bctx->faces = malloc (face_base * sizeof (bsp_face_t)); + bctx->poly_indices = malloc (index_count * sizeof (uint32_t)); + + face_base = 0; + for (int i = 0; i < num_models; i++) { + if (!models[i] || models[i]->type != mod_brush) { + continue; + } + int num_faces = models[i]->brush.numsurfaces; + bsp_model_t *m = &bctx->models[models[i]->render_id]; + m->first_face = face_base + models[i]->brush.firstmodelsurface; + m->face_count = models[i]->brush.nummodelsurfaces; + while (i < num_models - 1 && models[i + 1] + && models[i + 1]->path[0] == '*') { + i++; + m = &bctx->models[models[i]->render_id]; + m->first_face = face_base + models[i]->brush.firstmodelsurface; + m->face_count = models[i]->brush.nummodelsurfaces; + } + face_base += num_faces;; + } + + // All usable surfaces have been chained to the (base) texture they use. + // Run through the textures, using their chains to build display lists. + // For animated textures, if a surface is on one texture of the group, it + // will effectively be on all (just one at a time). + buildctx_t build = { + .faces = bctx->faces, + .indices = bctx->poly_indices, + .vertices = vertices, + .index_base = 0, + .vertex_base = 0, + }; + for (size_t i = 0; i < bctx->registered_textures.size; i++) { + build.tex_id = i; + for (size_t j = 0; j < face_sets[i].size; j++) { + faceref_t *faceref = &face_sets[i].a[j]; + build_surf_displist (faceref, &build); + } + } + Sys_MaskPrintf (SYS_vulkan, + "R_BuildDisplayLists: verts:%u, inds:%u, polys:%u\n", + vertex_count, index_count, poly_count); + if (index_buffer_size > bctx->index_buffer_size) { + if (bctx->index_buffer) { + dfunc->vkUnmapMemory (device->dev, bctx->index_memory); + dfunc->vkDestroyBuffer (device->dev, bctx->index_buffer, 0); + dfunc->vkFreeMemory (device->dev, bctx->index_memory, 0); + } + bctx->index_buffer + = QFV_CreateBuffer (device, index_buffer_size, + VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_INDEX_BUFFER_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER, bctx->index_buffer, + "buffer:bsp:index"); + bctx->index_memory + = QFV_AllocBufferMemory (device, bctx->index_buffer, + VK_MEMORY_PROPERTY_HOST_CACHED_BIT, + index_buffer_size, 0); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, + bctx->index_memory, "memory:bsp:index"); + QFV_BindBufferMemory (device, + bctx->index_buffer, bctx->index_memory, 0); + bctx->index_buffer_size = index_buffer_size; + void *data; + dfunc->vkMapMemory (device->dev, bctx->index_memory, 0, + index_buffer_size, 0, &data); + uint32_t *index_data = data; + for (size_t i = 0; i < frames; i++) { + uint32_t offset = index_count * i; + bctx->frames.a[i].index_data = index_data + offset; + bctx->frames.a[i].index_offset = offset * sizeof (uint32_t); + bctx->frames.a[i].index_count = 0; + } + } + if (vertex_buffer_size > bctx->vertex_buffer_size) { + if (bctx->vertex_buffer) { + dfunc->vkDestroyBuffer (device->dev, bctx->vertex_buffer, 0); + dfunc->vkFreeMemory (device->dev, bctx->vertex_memory, 0); + } + bctx->vertex_buffer + = QFV_CreateBuffer (device, vertex_buffer_size, + VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER, + bctx->vertex_buffer, "buffer:bsp:vertex"); + bctx->vertex_memory + = QFV_AllocBufferMemory (device, bctx->vertex_buffer, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + vertex_buffer_size, 0); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, + bctx->vertex_memory, "memory:bsp:vertex"); + QFV_BindBufferMemory (device, + bctx->vertex_buffer, bctx->vertex_memory, 0); + bctx->vertex_buffer_size = vertex_buffer_size; + } + + qfv_bufferbarrier_t bb = bufferBarriers[qfv_BB_Unknown_to_TransferWrite]; + bb.barrier.buffer = bctx->vertex_buffer; + bb.barrier.size = vertex_buffer_size; + dfunc->vkCmdPipelineBarrier (packet->cmd, bb.srcStages, bb.dstStages, + 0, 0, 0, 1, &bb.barrier, 0, 0); + VkBufferCopy copy_region = { packet->offset, 0, vertex_buffer_size }; + dfunc->vkCmdCopyBuffer (packet->cmd, stage->buffer, + bctx->vertex_buffer, 1, ©_region); + bb = bufferBarriers[qfv_BB_TransferWrite_to_VertexAttrRead]; + bb.barrier.buffer = bctx->vertex_buffer; + bb.barrier.size = vertex_buffer_size; + dfunc->vkCmdPipelineBarrier (packet->cmd, bb.srcStages, bb.dstStages, + 0, 0, 0, 1, &bb.barrier, 0, 0); + QFV_PacketSubmit (packet); + QFV_DestroyStagingBuffer (stage); + for (size_t i = 0; i < bctx->registered_textures.size; i++) { + DARRAY_CLEAR (&face_sets[i]); + } + +} + +static int +R_DrawBrushModel (entity_t ent, bsp_pass_t *pass, vulkan_ctx_t *ctx) +{ + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); + model_t *model = renderer->model; + bspctx_t *bctx = ctx->bsp_context; + + if (Vulkan_Scene_AddEntity (ctx, ent) < 0) { + return 0; + } + + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, + ent.reg); + pass->ent_frame = animation->frame & 1; + pass->inst_id = model->render_id; + pass->inst_id |= renderer->colormod[3] < 1 ? INST_ALPHA : 0; + if (!pass->instances[model->render_id].entities.size) { + bsp_model_t *m = &bctx->models[model->render_id]; + bsp_face_t *face = &bctx->faces[m->first_face]; + for (unsigned i = 0; i < m->face_count; i++, face++) { + // enqueue the polygon + chain_surface (face, pass, bctx); + } + } + DARRAY_APPEND (&pass->instances[model->render_id].entities, + renderer->render_id); + return 1; +} + +static inline void +visit_leaf (mleaf_t *leaf) +{ + // since this leaf will be rendered, any entities in the leaf also need + // to be rendered (the bsp tree doubles as an entity cull structure) + if (leaf->efrags) + R_StoreEfrags (leaf->efrags); +} + +// 1 = back side, 0 = front side +static inline int +get_side (const bsp_pass_t *pass, const mnode_t *node) +{ + // find the node side on which we are + vec4f_t org = pass->position; + + return dotf (org, node->plane)[0] < 0; +} + +static inline void +visit_node (bsp_pass_t *pass, const mnode_t *node, int side) +{ + bspctx_t *bctx = pass->bsp_context; + int c; + + // sneaky hack for side = side ? SURF_PLANEBACK : 0; + // seems to be microscopically faster even on modern hardware + side = (-side) & SURF_PLANEBACK; + // chain any visible surfaces on the node that face the camera. + // not all nodes have any surfaces to draw (purely a split plane) + if ((c = node->numsurfaces)) { + const bsp_face_t *face = bctx->faces + node->firstsurface; + const int *frame = pass->face_frames + node->firstsurface; + int vis_frame = pass->vis_frame; + for (; c; c--, face++, frame++) { + if (*frame != vis_frame) + continue; + + // side is either 0 or SURF_PLANEBACK + // if side and the surface facing differ, then the camera is + // on backside of the surface + if (side ^ (face->flags & SURF_PLANEBACK)) + continue; // wrong side + + chain_surface (face, pass, bctx); + } + } +} + +static inline int +test_node (const bsp_pass_t *pass, int node_id) +{ + if (node_id < 0) + return 0; + if (pass->node_frames[node_id] != pass->vis_frame) + return 0; + return 1; +} + +static void +R_VisitWorldNodes (bsp_pass_t *pass, vulkan_ctx_t *ctx) +{ + const mod_brush_t *brush = pass->brush; + typedef struct { + int node_id; + int side; + } rstack_t; + rstack_t *node_ptr; + rstack_t *node_stack; + int node_id; + int front; + int side; + + node_id = 0; + // +2 for paranoia + node_stack = alloca ((brush->depth + 2) * sizeof (rstack_t)); + node_ptr = node_stack; + + while (1) { + while (test_node (pass, node_id)) { + mnode_t *node = brush->nodes + node_id; + side = get_side (pass, node); + front = node->children[side]; + if (test_node (pass, front)) { + node_ptr->node_id = node_id; + node_ptr->side = side; + node_ptr++; + node_id = front; + continue; + } + // front is either not a node (ie, is a leaf) or is not visible + // if node is visible, then at least one of its child nodes + // must also be visible, and a leaf child in front of the node + // will be visible, so no need for vis checks on a leaf + if (front < 0) { + mleaf_t *leaf = brush->leafs + ~front; + if (leaf->contents != CONTENTS_SOLID) { + visit_leaf (leaf); + } + } + visit_node (pass, node, side); + node_id = node->children[side ^ 1]; + } + if (node_id < 0) { + mleaf_t *leaf = brush->leafs + ~node_id; + if (leaf->contents != CONTENTS_SOLID) { + visit_leaf (leaf); + } + } + if (node_ptr != node_stack) { + node_ptr--; + node_id = node_ptr->node_id; + side = node_ptr->side; + mnode_t *node = brush->nodes + node_id; + visit_node (pass, node, side); + node_id = node->children[side ^ 1]; + continue; + } + break; + } +} + +static void +bind_texture (vulktex_t *tex, uint32_t setnum, VkPipelineLayout layout, + qfv_devfuncs_t *dfunc, VkCommandBuffer cmd) +{ + VkDescriptorSet sets[] = { + tex->descriptor, + }; + dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + layout, setnum, 1, sets, 0, 0); +} + +static void +push_fragconst (bsp_push_constants_t *constants, VkPipelineLayout layout, + qfv_device_t *device, VkCommandBuffer cmd) +{ + qfv_push_constants_t push_constants[] = { + //{ VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof (mat), mat }, + { VK_SHADER_STAGE_FRAGMENT_BIT, + field_offset (bsp_push_constants_t, fog), + sizeof (constants->fog), &constants->fog }, + { VK_SHADER_STAGE_FRAGMENT_BIT, + field_offset (bsp_push_constants_t, time), + sizeof (constants->time), &constants->time }, + { VK_SHADER_STAGE_FRAGMENT_BIT, + field_offset (bsp_push_constants_t, alpha), + sizeof (constants->alpha), &constants->alpha }, + { VK_SHADER_STAGE_FRAGMENT_BIT, + field_offset (bsp_push_constants_t, turb_scale), + sizeof (constants->turb_scale), &constants->turb_scale }, + }; + QFV_PushConstants (device, cmd, layout, 4, push_constants); +} + +static void +clear_queues (bspctx_t *bctx, bsp_pass_t *pass) +{ + for (size_t i = 0; i < bctx->registered_textures.size; i++) { + DARRAY_RESIZE (&pass->face_queue[i], 0); + } + for (int i = 0; i < pass->num_queues; i++) { + DARRAY_RESIZE (&pass->draw_queues[i], 0); + } + for (int i = 0; i < bctx->num_models; i++) { + pass->instances[i].first_instance = -1; + DARRAY_RESIZE (&pass->instances[i].entities, 0); + } + pass->index_count = 0; +} + +static void +queue_faces (bsp_pass_t *pass, const bspctx_t *bctx, bspframe_t *bframe) +{ + pass->indices = bframe->index_data + bframe->index_count; + for (size_t i = 0; i < bctx->registered_textures.size; i++) { + auto queue = &pass->face_queue[i]; + if (!queue->size) { + continue; + } + for (size_t j = 0; j < queue->size; j++) { + auto is = queue->a[j]; + auto f = bctx->faces[is.face]; + + f.flags |= ((is.inst_id & INST_ALPHA) + >> (BITOP_LOG2(INST_ALPHA) + - BITOP_LOG2(SURF_DRAWALPHA))) & SURF_DRAWALPHA; + is.inst_id &= ~INST_ALPHA; + if (pass->instances[is.inst_id].first_instance == -1) { + uint32_t count = pass->instances[is.inst_id].entities.size; + pass->instances[is.inst_id].first_instance = pass->entid_count; + memcpy (pass->entid_data + pass->entid_count, + pass->instances[is.inst_id].entities.a, + count * sizeof (uint32_t)); + pass->entid_count += count; + } + + int dq = 0; + if (f.flags & SURF_DRAWSKY) { + dq = 1; + } + if (f.flags & SURF_DRAWALPHA) { + dq = 2; + } + if (f.flags & SURF_DRAWTURB) { + dq = 3; + } + + size_t dq_size = pass->draw_queues[dq].size; + bsp_draw_t *draw = &pass->draw_queues[dq].a[dq_size - 1]; + if (!pass->draw_queues[dq].size + || draw->tex_id != i + || draw->inst_id != is.inst_id) { + bsp_instance_t *instance = &pass->instances[is.inst_id]; + DARRAY_APPEND (&pass->draw_queues[dq], ((bsp_draw_t) { + .tex_id = i, + .inst_id = is.inst_id, + .instance_count = instance->entities.size, + .first_index = pass->index_count, + .first_instance = instance->first_instance, + })); + dq_size = pass->draw_queues[dq].size; + draw = &pass->draw_queues[dq].a[dq_size - 1]; + } + + memcpy (pass->indices + pass->index_count, + bctx->poly_indices + f.first_index, + f.index_count * sizeof (uint32_t)); + draw->index_count += f.index_count; + pass->index_count += f.index_count; + } + } + bframe->index_count += pass->index_count; +} + +static void +draw_queue (bsp_pass_t *pass, QFV_BspQueue queue, VkPipelineLayout layout, + qfv_device_t *device, VkCommandBuffer cmd) +{ + qfv_devfuncs_t *dfunc = device->funcs; + + for (size_t i = 0; i < pass->draw_queues[queue].size; i++) { + auto d = pass->draw_queues[queue].a[i]; + if (pass->textures) { + vulktex_t *tex = pass->textures->a[d.tex_id]; + bind_texture (tex, TEX_SET, layout, dfunc, cmd); + } + dfunc->vkCmdDrawIndexed (cmd, d.index_count, d.instance_count, + d.first_index, 0, d.first_instance); + } +} + +static int +ent_model_cmp (const void *_a, const void *_b) +{ + const entity_t *a = _a; + const entity_t *b = _b; + renderer_t *ra = Ent_GetComponent (a->id, scene_renderer, a->reg); + renderer_t *rb = Ent_GetComponent (b->id, scene_renderer, b->reg); + return ra->model->render_id - rb->model->render_id; +} + +static void +bsp_flush (vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + bspctx_t *bctx = ctx->bsp_context; + bspframe_t *bframe = &bctx->frames.a[ctx->curFrame]; + size_t atom = device->physDev->properties->limits.nonCoherentAtomSize; + size_t atom_mask = atom - 1; + size_t index_offset = bframe->index_offset; + size_t index_size = bframe->index_count * sizeof (uint32_t); + size_t entid_offset = bframe->entid_offset; + size_t entid_size = bframe->entid_count * sizeof (uint32_t); + + if (!bframe->index_count) { + return; + } + index_offset &= ~atom_mask; + index_size = (index_size + atom_mask) & ~atom_mask; + entid_offset &= ~atom_mask; + entid_size = (entid_size + atom_mask) & ~atom_mask; + + VkMappedMemoryRange ranges[] = { + { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0, + bctx->index_memory, index_offset, index_size + }, + { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0, + bctx->entid_memory, entid_offset, entid_size + }, + }; + dfunc->vkFlushMappedMemoryRanges (device->dev, 2, ranges); +} + +static void +create_default_skys (vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + bspctx_t *bctx = ctx->bsp_context; + VkImage skybox; + VkImage skysheet; + VkDeviceMemory memory; + VkImageView boxview; + VkImageView sheetview; + + bctx->default_skybox = calloc (2, sizeof (qfv_tex_t)); + bctx->default_skysheet = bctx->default_skybox + 1; + + VkExtent3D extents = { 1, 1, 1 }; + skybox = QFV_CreateImage (device, 1, VK_IMAGE_TYPE_2D, + VK_FORMAT_B8G8R8A8_UNORM, extents, 1, 1, + VK_SAMPLE_COUNT_1_BIT, + VK_IMAGE_USAGE_SAMPLED_BIT + | VK_IMAGE_USAGE_TRANSFER_DST_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE, skybox, + "bsp:image:default_skybox"); + + skysheet = QFV_CreateImage (device, 0, VK_IMAGE_TYPE_2D, + VK_FORMAT_B8G8R8A8_UNORM, extents, 1, 2, + VK_SAMPLE_COUNT_1_BIT, + VK_IMAGE_USAGE_SAMPLED_BIT + | VK_IMAGE_USAGE_TRANSFER_DST_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE, skysheet, + "bsp:image:default_skysheet"); + + VkMemoryRequirements requirements; + dfunc->vkGetImageMemoryRequirements (device->dev, skybox, &requirements); + size_t boxsize = requirements.size; + dfunc->vkGetImageMemoryRequirements (device->dev, skysheet, &requirements); + size_t sheetsize = requirements.size; + + memory = QFV_AllocImageMemory (device, skybox, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + boxsize + sheetsize, + VK_IMAGE_USAGE_TRANSFER_DST_BIT + | VK_IMAGE_USAGE_SAMPLED_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, memory, + "bsp:memory:default_skys"); + + QFV_BindImageMemory (device, skybox, memory, 0); + QFV_BindImageMemory (device, skysheet, memory, boxsize); + + boxview = QFV_CreateImageView (device, skybox, VK_IMAGE_VIEW_TYPE_CUBE, + VK_FORMAT_B8G8R8A8_UNORM, + VK_IMAGE_ASPECT_COLOR_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE_VIEW, boxview, + "bsp:iview:default_skybox"); + + sheetview = QFV_CreateImageView (device, skysheet, + VK_IMAGE_VIEW_TYPE_2D_ARRAY, + VK_FORMAT_B8G8R8A8_UNORM, + VK_IMAGE_ASPECT_COLOR_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE_VIEW, sheetview, + "bsp:iview:default_skysheet"); + + bctx->default_skybox->image = skybox; + bctx->default_skybox->view = boxview; + bctx->default_skybox->memory = memory; + bctx->default_skysheet->image = skysheet; + bctx->default_skysheet->view = sheetview; + + // temporarily commandeer the light map's staging buffer + qfv_packet_t *packet = QFV_PacketAcquire (bctx->light_stage); + + qfv_imagebarrier_t ib = imageBarriers[qfv_LT_Undefined_to_TransferDst]; + ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + VkImageMemoryBarrier barriers[2] = { ib.barrier, ib.barrier }; + barriers[0].image = skybox; + barriers[1].image = skysheet; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 2, barriers); + + VkClearColorValue color = {}; + VkImageSubresourceRange range = { + VK_IMAGE_ASPECT_COLOR_BIT, + 0, VK_REMAINING_MIP_LEVELS, + 0, VK_REMAINING_ARRAY_LAYERS + }; + dfunc->vkCmdClearColorImage (packet->cmd, skybox, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + &color, 1, &range); + dfunc->vkCmdClearColorImage (packet->cmd, skysheet, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + &color, 1, &range); + + ib = imageBarriers[qfv_LT_TransferDst_to_ShaderReadOnly]; + ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + barriers[0] = ib.barrier; + barriers[1] = ib.barrier; + barriers[0].image = skybox; + barriers[1].image = skysheet; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 2, barriers); + QFV_PacketSubmit (packet); +} + +static void +create_notexture (vulkan_ctx_t *ctx) +{ + const char *missing = "Missing"; + byte data[2][64 * 64 * 4]; // 2 * 64x64 rgba (8x8 chars) + tex_t tex[2] = { + { .width = 64, + .height = 64, + .format = tex_rgba, + .loaded = 1, + .data = data[0], + }, + { .width = 64, + .height = 64, + .format = tex_rgba, + .loaded = 1, + .data = data[1], + }, + }; + + for (int i = 0; i < 64 * 64; i++) { + data[0][i * 4 + 0] = 0x20; + data[0][i * 4 + 1] = 0x20; + data[0][i * 4 + 2] = 0x20; + data[0][i * 4 + 3] = 0xff; + + data[1][i * 4 + 0] = 0x00; + data[1][i * 4 + 1] = 0x00; + data[1][i * 4 + 2] = 0x00; + data[1][i * 4 + 3] = 0xff; + } + int x = 4; + int y = 4; + for (const char *c = missing; *c; c++) { + byte *bitmap = font8x8_data + *c * 8; + for (int l = 0; l < 8; l++) { + byte d = *bitmap++; + for (int b = 0; b < 8; b++) { + if (d & 0x80) { + int base = ((y + l) * 64 + x + b) * 4; + data[0][base + 0] = 0x00; + data[0][base + 1] = 0x00; + data[0][base + 2] = 0x00; + data[0][base + 3] = 0xff; + + data[1][base + 0] = 0xff; + data[1][base + 1] = 0x00; + data[1][base + 2] = 0xff; + data[1][base + 3] = 0xff; + } + d <<= 1; + } + } + x += 8; + } + for (int i = 1; i < 7; i++) { + y += 8; + memcpy (data[0] + y * 64 * 4, data[0] + 4 * 64 * 4, 8 * 64 * 4); + memcpy (data[1] + y * 64 * 4, data[1] + 4 * 64 * 4, 8 * 64 * 4); + } + + bspctx_t *bctx = ctx->bsp_context; + bctx->notexture.tex = Vulkan_LoadTexArray (ctx, tex, 2, 1, "notexture"); +} + +static void +bsp_draw_queue (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto bctx = ctx->bsp_context; + auto layout = taskctx->pipeline->layout; + auto cmd = taskctx->cmd; + + if (!bctx->vertex_buffer) { + return; + } + + // params are in reverse order + auto stage = *(int *) params[2]->value; + auto queue = *(QFV_BspQueue *) params[1]->value; + auto pass = *(int *) params[0]->value; + + if (stage) { + Sys_Error ("bps stages not implemented"); + } + auto mpass = &bctx->main_pass; + if (!mpass->draw_queues[queue].size) { + return; + } + + auto bframe = &bctx->frames.a[ctx->curFrame]; + VkBuffer buffers[] = { bctx->vertex_buffer, bctx->entid_buffer }; + VkDeviceSize offsets[] = { 0, bframe->entid_offset }; + dfunc->vkCmdBindVertexBuffers (cmd, 0, 2, buffers, offsets); + dfunc->vkCmdBindIndexBuffer (cmd, bctx->index_buffer, bframe->index_offset, + VK_INDEX_TYPE_UINT32); + + VkDescriptorSet sets[] = { + Vulkan_Matrix_Descriptors (ctx, ctx->curFrame), + Vulkan_Scene_Descriptors (ctx), + Vulkan_Translucent_Descriptors (ctx, ctx->curFrame), + }; + dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + layout, 0, 3, sets, 0, 0); + + //XXX glsl_Fog_GetColor (fog); + //XXX fog[3] = glsl_Fog_GetDensity () / 64.0; + bsp_push_constants_t frag_constants = { + .time = vr_data.realtime, + .alpha = queue == QFV_bspTurb ? r_wateralpha : 1, + .turb_scale = queue == QFV_bspTurb ? 1 : 0, + }; + push_fragconst (&frag_constants, layout, device, cmd); + if (queue == QFV_bspSky) { + vulktex_t skybox = { .descriptor = bctx->skybox_descriptor }; + bind_texture (&skybox, SKYBOX_SET, layout, dfunc, cmd); + } + + mpass->textures = pass ? &bctx->registered_textures : 0; + draw_queue (mpass, queue, layout, device, cmd); +} + +static void +bsp_visit_world (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + auto stage = *(int *) params[0]->value; + + if (stage) { + Sys_Error ("bps stages not implemented"); + } + + EntQueue_Clear (r_ent_queue); + Vulkan_Scene_Flush (ctx); + + auto bctx = ctx->bsp_context; + clear_queues (bctx, &bctx->main_pass); // do this first for water and skys + + if (!r_refdef.worldmodel) { + return; + } + + auto bframe = &bctx->frames.a[ctx->curFrame]; + + bctx->main_pass.bsp_context = bctx; + bctx->main_pass.position = r_refdef.frame.position; + bctx->main_pass.vis_frame = r_visframecount; + bctx->main_pass.face_frames = r_face_visframes; + bctx->main_pass.leaf_frames = r_leaf_visframes; + bctx->main_pass.node_frames = r_node_visframes; + bctx->main_pass.entid_data = bframe->entid_data; + bctx->main_pass.entid_count = 0; + + bctx->anim_index = r_data->realtime * 5; + + bframe->index_count = 0; + + entity_t worldent = nullentity; + + int world_id = Vulkan_Scene_AddEntity (ctx, worldent); + + bctx->main_pass.ent_frame = 0; // world is always frame 0 + bctx->main_pass.inst_id = world_id; + bctx->main_pass.brush = &r_refdef.worldmodel->brush; + if (bctx->main_pass.instances) { + DARRAY_APPEND (&bctx->main_pass.instances[world_id].entities, world_id); + } + R_VisitWorldNodes (&bctx->main_pass, ctx); + + if (r_drawentities) { + heapsort (r_ent_queue->ent_queues[mod_brush].a, + r_ent_queue->ent_queues[mod_brush].size, + sizeof (entity_t), ent_model_cmp); + for (size_t i = 0; i < r_ent_queue->ent_queues[mod_brush].size; i++) { + entity_t ent = r_ent_queue->ent_queues[mod_brush].a[i]; + if (!R_DrawBrushModel (ent, &bctx->main_pass, ctx)) { + Sys_Printf ("Too many entities!\n"); + break; + } + } + } + bframe->entid_count = bctx->main_pass.entid_count; + + queue_faces (&bctx->main_pass, bctx, bframe); + + bsp_flush (ctx); +} + +static exprenum_t bsp_stage_enum; +static exprtype_t bsp_stage_type = { + .name = "bsp_stage", + .size = sizeof (int), + .get_string = cexpr_enum_get_string, + .data = &bsp_stage_enum, +}; +static int bsp_stage_values[] = { 0, }; +static exprsym_t bsp_stage_symbols[] = { + {"main", &bsp_stage_type, bsp_stage_values + 0}, + {} +}; +static exprtab_t bsp_stage_symtab = { .symbols = bsp_stage_symbols }; +static exprenum_t bsp_stage_enum = { + &bsp_stage_type, + &bsp_stage_symtab, +}; + +static exprenum_t bsp_queue_enum; +static exprtype_t bsp_queue_type = { + .name = "bsp_queue", + .size = sizeof (int), + .get_string = cexpr_enum_get_string, + .data = &bsp_queue_enum, +}; +static int bsp_queue_values[] = { + QFV_bspSolid, + QFV_bspSky, + QFV_bspTrans, + QFV_bspTurb, +}; +static exprsym_t bsp_queue_symbols[] = { + {"solid", &bsp_queue_type, bsp_queue_values + 0}, + {"sky", &bsp_queue_type, bsp_queue_values + 1}, + {"translucent", &bsp_queue_type, bsp_queue_values + 2}, + {"turbulent", &bsp_queue_type, bsp_queue_values + 3}, + {} +}; +static exprtab_t bsp_queue_symtab = { .symbols = bsp_queue_symbols }; +static exprenum_t bsp_queue_enum = { + &bsp_queue_type, + &bsp_queue_symtab, +}; + +static exprtype_t *bsp_visit_world_params[] = { + &bsp_stage_type, +}; + +static exprtype_t *bsp_draw_queue_params[] = { + &cexpr_int, + &bsp_queue_type, + &bsp_stage_type, +}; + +static exprfunc_t bsp_visit_world_func[] = { + { 0, 1, bsp_visit_world_params, bsp_visit_world }, + {} +}; +static exprfunc_t bsp_draw_queue_func[] = { + { 0, 3, bsp_draw_queue_params, bsp_draw_queue }, + {} +}; +static exprsym_t bsp_task_syms[] = { + { "bsp_visit_world", &cexpr_function, bsp_visit_world_func }, + { "bsp_draw_queue", &cexpr_function, bsp_draw_queue_func }, + {} +}; + +void +Vulkan_Bsp_Init (vulkan_ctx_t *ctx) +{ + QFV_Render_AddTasks (ctx, bsp_task_syms); + + bspctx_t *bctx = calloc (1, sizeof (bspctx_t)); + ctx->bsp_context = bctx; +} + +void +Vulkan_Bsp_Setup (vulkan_ctx_t *ctx) +{ + qfvPushDebug (ctx, "bsp init"); + + auto device = ctx->device; + auto dfunc = device->funcs; + + auto bctx = ctx->bsp_context; + + bctx->sampler = QFV_Render_Sampler (ctx, "quakebsp_sampler"); + + bctx->light_scrap = QFV_CreateScrap (device, "lightmap_atlas", 2048, + tex_frgba, ctx->staging); + size_t size = QFV_ScrapSize (bctx->light_scrap); + bctx->light_stage = QFV_CreateStagingBuffer (device, "lightmap", size, + ctx->cmdpool); + + create_default_skys (ctx); + create_notexture (ctx); + + DARRAY_INIT (&bctx->registered_textures, 64); + + bctx->main_pass.num_queues = 4;//solid, sky, water, transparent + bctx->main_pass.draw_queues = malloc (bctx->main_pass.num_queues + * sizeof (bsp_drawset_t)); + for (int i = 0; i < bctx->main_pass.num_queues; i++) { + DARRAY_INIT (&bctx->main_pass.draw_queues[i], 64); + } + + auto rctx = ctx->render_context; + size_t frames = rctx->frames.size; + DARRAY_INIT (&bctx->frames, frames); + DARRAY_RESIZE (&bctx->frames, frames); + bctx->frames.grow = 0; + + size_t entid_count = Vulkan_Scene_MaxEntities (ctx); + size_t entid_size = entid_count * sizeof (uint32_t); + size_t atom = device->physDev->properties->limits.nonCoherentAtomSize; + size_t atom_mask = atom - 1; + entid_size = (entid_size + atom_mask) & ~atom_mask; + bctx->entid_buffer + = QFV_CreateBuffer (device, frames * entid_size, + VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER, bctx->entid_buffer, + "buffer:bsp:entid"); + bctx->entid_memory + = QFV_AllocBufferMemory (device, bctx->entid_buffer, + VK_MEMORY_PROPERTY_HOST_CACHED_BIT, + frames * entid_size, 0); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, + bctx->entid_memory, "memory:bsp:entid"); + QFV_BindBufferMemory (device, + bctx->entid_buffer, bctx->entid_memory, 0); + uint32_t *entid_data; + dfunc->vkMapMemory (device->dev, bctx->entid_memory, 0, + frames * entid_size, 0, (void **) &entid_data); + + for (size_t i = 0; i < frames; i++) { + auto bframe = &bctx->frames.a[i]; + + DARRAY_INIT (&bframe->cmdSet, QFV_bspNumPasses); + DARRAY_RESIZE (&bframe->cmdSet, QFV_bspNumPasses); + bframe->cmdSet.grow = 0; + + QFV_AllocateCommandBuffers (device, ctx->cmdpool, 1, &bframe->cmdSet); + + for (int j = 0; j < QFV_bspNumPasses; j++) { + QFV_duSetObjectName (device, VK_OBJECT_TYPE_COMMAND_BUFFER, + bframe->cmdSet.a[j], + va (ctx->va_ctx, "cmd:bsp:%zd:%s", i, + bsp_pass_names[j])); + } + bframe->entid_data = entid_data + i * entid_count; + bframe->entid_offset = i * entid_size; + } + + bctx->skybox_descriptor + = Vulkan_CreateTextureDescriptor (ctx, bctx->default_skybox, + bctx->sampler); + bctx->notexture.descriptor + = Vulkan_CreateTextureDescriptor (ctx, bctx->notexture.tex, + bctx->sampler); + + r_notexture_mip->render = &bctx->notexture; + + qfvPopDebug (ctx); +} + +void +Vulkan_Bsp_Shutdown (struct vulkan_ctx_s *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + bspctx_t *bctx = ctx->bsp_context; + + for (size_t i = 0; i < bctx->frames.size; i++) { + auto bframe = &bctx->frames.a[i]; + free (bframe->cmdSet.a); + } + + DARRAY_CLEAR (&bctx->registered_textures); + for (int i = 0; i < bctx->main_pass.num_queues; i++) { + DARRAY_CLEAR (&bctx->main_pass.draw_queues[i]); + } + + free (bctx->faces); + free (bctx->models); + + free (bctx->main_pass.draw_queues); + for (int i = 0; i < bctx->num_models; i++) { + DARRAY_CLEAR (&bctx->main_pass.instances[i].entities); + } + free (bctx->main_pass.instances); + DARRAY_CLEAR (&bctx->frames); + + QFV_DestroyStagingBuffer (bctx->light_stage); + QFV_DestroyScrap (bctx->light_scrap); + if (bctx->vertex_buffer) { + dfunc->vkDestroyBuffer (device->dev, bctx->vertex_buffer, 0); + dfunc->vkFreeMemory (device->dev, bctx->vertex_memory, 0); + } + if (bctx->index_buffer) { + dfunc->vkDestroyBuffer (device->dev, bctx->index_buffer, 0); + dfunc->vkFreeMemory (device->dev, bctx->index_memory, 0); + } + dfunc->vkDestroyBuffer (device->dev, bctx->entid_buffer, 0); + dfunc->vkFreeMemory (device->dev, bctx->entid_memory, 0); + + if (bctx->skybox_tex) { + Vulkan_UnloadTex (ctx, bctx->skybox_tex); + } + if (bctx->notexture.tex) { + Vulkan_UnloadTex (ctx, bctx->notexture.tex); + } + + dfunc->vkDestroyImageView (device->dev, bctx->default_skysheet->view, 0); + dfunc->vkDestroyImage (device->dev, bctx->default_skysheet->image, 0); + + dfunc->vkDestroyImageView (device->dev, bctx->default_skybox->view, 0); + dfunc->vkDestroyImage (device->dev, bctx->default_skybox->image, 0); + dfunc->vkFreeMemory (device->dev, bctx->default_skybox->memory, 0); + free (bctx->default_skybox); +} + +void +Vulkan_LoadSkys (const char *sky, vulkan_ctx_t *ctx) +{ + bspctx_t *bctx = ctx->bsp_context; + + const char *name; + int i; + tex_t *tex; + static const char *sky_suffix[] = { "ft", "bk", "up", "dn", "rt", "lf"}; + + if (bctx->skybox_tex) { + Vulkan_UnloadTex (ctx, bctx->skybox_tex); + Vulkan_FreeTexture (ctx, bctx->skybox_descriptor); + } + bctx->skybox_tex = 0; + + if (!sky || !*sky) { + sky = r_skyname; + } + + if (!*sky || !strcasecmp (sky, "none")) { + Sys_MaskPrintf (SYS_vulkan, "Skybox unloaded\n"); + bctx->skybox_descriptor + = Vulkan_CreateTextureDescriptor (ctx, bctx->default_skybox, + bctx->sampler); + return; + } + + name = va (ctx->va_ctx, "env/%s_map", sky); + tex = LoadImage (name, 1); + if (tex) { + bctx->skybox_tex = Vulkan_LoadEnvMap (ctx, tex, sky); + Sys_MaskPrintf (SYS_vulkan, "Loaded %s\n", name); + } else { + int failed = 0; + tex_t *sides[6] = { }; + + for (i = 0; i < 6; i++) { + name = va (ctx->va_ctx, "env/%s%s", sky, sky_suffix[i]); + tex = LoadImage (name, 1); + if (!tex) { + Sys_MaskPrintf (SYS_vulkan, "Couldn't load %s\n", name); + // also look in gfx/env, where Darkplaces looks for skies + name = va (ctx->va_ctx, "gfx/env/%s%s", sky, sky_suffix[i]); + tex = LoadImage (name, 1); + if (!tex) { + Sys_MaskPrintf (SYS_vulkan, "Couldn't load %s\n", name); + failed = 1; + continue; + } + } + //FIXME find a better way (also, assumes data and struct together) + sides[i] = malloc (ImageSize (tex, 1)); + memcpy (sides[i], tex, ImageSize (tex, 1)); + sides[i]->data = (byte *)(sides[i] + 1); + Sys_MaskPrintf (SYS_vulkan, "Loaded %s\n", name); + } + if (!failed) { + bctx->skybox_tex = Vulkan_LoadEnvSides (ctx, sides, sky); + } + for (i = 0; i < 6; i++) { + free (sides[i]); + } + } + if (bctx->skybox_tex) { + bctx->skybox_descriptor + = Vulkan_CreateTextureDescriptor (ctx, bctx->skybox_tex, + bctx->sampler); + Sys_MaskPrintf (SYS_vulkan, "Skybox %s loaded\n", sky); + } +} diff --git a/libs/video/renderer/vulkan/vulkan_compose.c b/libs/video/renderer/vulkan/vulkan_compose.c new file mode 100644 index 000000000..9e96e2c95 --- /dev/null +++ b/libs/video/renderer/vulkan/vulkan_compose.c @@ -0,0 +1,152 @@ +/* + vulkan_compose.c + + Vulkan compose pass pipeline + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/2/23 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include + +#include "qfalloca.h" + +#include "QF/cvar.h" +#include "QF/sys.h" + +#include "QF/Vulkan/qf_compose.h" +#include "QF/Vulkan/qf_translucent.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/descriptor.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/dsmanager.h" +#include "QF/Vulkan/image.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/render.h" + +#include "r_internal.h" +#include "vid_vulkan.h" + +static VkDescriptorImageInfo base_image_info = { + 0, 0, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL +}; +static VkWriteDescriptorSet base_image_write = { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, 0, + 0, 0, 1, + VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, + 0, 0, 0 +}; + +static void +compose_draw (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + + auto cctx = ctx->compose_context; + auto cframe = &cctx->frames.a[ctx->curFrame]; + auto layout = taskctx->pipeline->layout; + auto cmd = taskctx->cmd; + + auto fb = &taskctx->renderpass->framebuffer; + cframe->imageInfo[0].imageView = fb->views[QFV_attachOpaque]; + dfunc->vkUpdateDescriptorSets (device->dev, COMPOSE_IMAGE_INFOS, + cframe->descriptors, 0, 0); + + VkDescriptorSet sets[] = { + cframe->descriptors[0].dstSet, + Vulkan_Translucent_Descriptors (ctx, ctx->curFrame), + }; + dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + layout, 0, 2, sets, 0, 0); + + dfunc->vkCmdDraw (cmd, 3, 1, 0, 0); +} + +static exprfunc_t compose_draw_func[] = { + { .func = compose_draw }, + {} +}; +static exprsym_t compose_task_syms[] = { + { "compose_draw", &cexpr_function, compose_draw_func }, + {} +}; + +void +Vulkan_Compose_Init (vulkan_ctx_t *ctx) +{ + QFV_Render_AddTasks (ctx, compose_task_syms); + + composectx_t *cctx = calloc (1, sizeof (composectx_t)); + ctx->compose_context = cctx; +} + +void +Vulkan_Compose_Setup (vulkan_ctx_t *ctx) +{ + qfvPushDebug (ctx, "compose init"); + + auto cctx = ctx->compose_context; + + auto rctx = ctx->render_context; + size_t frames = rctx->frames.size; + DARRAY_INIT (&cctx->frames, frames); + DARRAY_RESIZE (&cctx->frames, frames); + cctx->frames.grow = 0; + + auto dsmanager = QFV_Render_DSManager (ctx, "compose_attach"); + for (size_t i = 0; i < frames; i++) { + __auto_type cframe = &cctx->frames.a[i]; + + for (int j = 0; j < COMPOSE_IMAGE_INFOS; j++) { + cframe->imageInfo[j] = base_image_info; + cframe->imageInfo[j].sampler = 0; + cframe->descriptors[j] = base_image_write; + cframe->descriptors[j].dstSet = QFV_DSManager_AllocSet (dsmanager); + cframe->descriptors[j].dstBinding = j; + cframe->descriptors[j].pImageInfo = &cframe->imageInfo[j]; + } + } + qfvPopDebug (ctx); +} + +void +Vulkan_Compose_Shutdown (vulkan_ctx_t *ctx) +{ + composectx_t *cctx = ctx->compose_context; + + free (cctx->frames.a); + free (cctx); +} diff --git a/libs/video/renderer/vulkan/vulkan_draw.c b/libs/video/renderer/vulkan/vulkan_draw.c new file mode 100644 index 000000000..80ce3d92a --- /dev/null +++ b/libs/video/renderer/vulkan/vulkan_draw.c @@ -0,0 +1,1733 @@ +/* + vulkan_draw.c + + 2D drawing support for Vulkan + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/1/10 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/cmem.h" +#include "QF/cvar.h" +#include "QF/draw.h" +#include "QF/dstring.h" +#include "QF/hash.h" +#include "QF/quakefs.h" +#include "QF/render.h" +#include "QF/sys.h" +#include "QF/va.h" +#include "QF/vid.h" + +#include "compat.h" +#include "QF/Vulkan/qf_draw.h" +#include "QF/Vulkan/qf_matrices.h" +#include "QF/Vulkan/qf_texture.h" +#include "QF/Vulkan/qf_vid.h" +#include "QF/Vulkan/barrier.h" +#include "QF/Vulkan/buffer.h" +#include "QF/Vulkan/command.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/descriptor.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/dsmanager.h" +#include "QF/Vulkan/image.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/render.h" +#include "QF/Vulkan/resource.h" +#include "QF/Vulkan/scrap.h" +#include "QF/Vulkan/staging.h" +#include "QF/ui/font.h" +#include "QF/ui/view.h" + +#include "r_internal.h" +#include "vid_vulkan.h" + +typedef struct pic_data_s { + uint32_t vert_index; + uint32_t slice_index; + uint32_t descid; + subpic_t *subpic; +} picdata_t; + +typedef struct descbatch_s { + int32_t descid; // texture or font descriptor id + uint32_t count; // number of objects in batch +} descbatch_t; + +typedef struct descbatchset_s + DARRAY_TYPE (descbatch_t) descbatchset_t; + +typedef struct { + float xy[2]; + byte color[4]; +} linevert_t; + +typedef struct { + uint32_t index; + byte color[4]; + float position[2]; + float offset[2]; +} quadinst_t; + +typedef struct { + float offset[2]; + float uv[2]; +} quadvert_t; + +typedef struct linequeue_s { + linevert_t *verts; + int count; + int size; +} linequeue_t; + +typedef struct quadqueue_s { + quadinst_t *quads; + int count; + int size; +} quadqueue_t; + +typedef struct cachepic_s { + char *name; + qpic_t *pic; +} cachepic_t; + +// core pic atlas + static verts +#define CORE_DESC 0 +// FIXME make dynamic +#define MAX_DESCIPTORS 64 + +typedef struct descpool_s { + VkDescriptorSet sets[MAX_DESCIPTORS]; + struct drawctx_s *dctx; + uint32_t users[MAX_DESCIPTORS];// picdata_t.descid + int in_use; +} descpool_t; + +typedef struct drawframe_s { + size_t instance_offset; + size_t dvert_offset; + size_t line_offset; + VkBuffer instance_buffer; + VkBuffer dvert_buffer; + VkBuffer line_buffer; + VkBufferView dvert_view; + + uint32_t dvertex_index; + uint32_t dvertex_max; + descbatchset_t quad_batch; + quadqueue_t quad_insts; + linequeue_t line_verts; + descpool_t dyn_descs; +} drawframe_t; + +typedef struct drawframeset_s + DARRAY_TYPE (drawframe_t) drawframeset_t; + +typedef struct drawfontres_s { + qfv_resource_t resource; + qfv_resobj_t glyph_data; + qfv_resobj_t glyph_bview; + qfv_resobj_t glyph_image; + qfv_resobj_t glyph_iview; +} drawfontres_t; + +typedef struct drawfont_s { + VkDescriptorSet set; + drawfontres_t *resource; +} drawfont_t; + +typedef struct drawfontset_s + DARRAY_TYPE (drawfont_t) drawfontset_t; + +typedef struct drawctx_s { + VkSampler pic_sampler; + VkSampler glyph_sampler; + scrap_t *scrap; + qfv_stagebuf_t *stage; + int *crosshair_inds; + qpic_t *crosshair; + int *conchar_inds; + qpic_t *conchars; + qpic_t *conback; + qpic_t *white_pic; + qpic_t *backtile_pic; + // use two separate cmem blocks for pics and strings (cachepic names) + // to ensure the names are never in the same cacheline as a pic since the + // names are used only for lookup + memsuper_t *pic_memsuper; + memsuper_t *string_memsuper; + hashtab_t *pic_cache; + qfv_dsmanager_t *dsmanager; + qfv_resource_t *draw_resource; + qfv_resobj_t *index_object; + qfv_resobj_t *svertex_objects; + qfv_resobj_t *instance_objects; + qfv_resobj_t *dvertex_objects; + qfv_resobj_t *lvertex_objects; + uint32_t svertex_index; + uint32_t svertex_max; + VkDescriptorSet core_quad_set; + drawframeset_t frames; + drawfontset_t fonts; + SCR_Func *scr_funcs; +} drawctx_t; + +#define MAX_QUADS (32768) +#define VERTS_PER_QUAD (4) +#define BYTES_PER_QUAD (VERTS_PER_QUAD * sizeof (quadvert_t)) +#define VERTS_PER_SLICE (16) +#define BYTES_PER_SLICE (VERTS_PER_SLICE * sizeof (quadvert_t)) +#define INDS_PER_QUAD (4) +#define INDS_PER_SLICE (26) + +#define MAX_INSTANCES (1024*1024) + +#define MAX_LINES (32768) +#define VERTS_PER_LINE (2) +#define BYTES_PER_LINE (VERTS_PER_LINE * sizeof (linevert_t)) + +static int +get_dyn_descriptor (descpool_t *pool, qpic_t *pic, VkBufferView buffer_view, + vulkan_ctx_t *ctx) +{ + auto device = ctx->device; + auto dfunc = device->funcs; + auto dctx = ctx->draw_context; + auto pd = (picdata_t *) pic->data; + uint32_t id = pd->descid; + + for (int i = 0; i < pool->in_use; i++) { + if (pool->users[i] == id) { + return ~i; + } + } + if (pool->in_use >= MAX_DESCIPTORS) { + Sys_Error ("get_dyn_descriptor: out of dynamic descriptors"); + } + int descid = pool->in_use++; + pool->users[descid] = id; + if (!pool->sets[descid]) { + pool->sets[descid] = QFV_DSManager_AllocSet (dctx->dsmanager); + } + VkWriteDescriptorSet write[] = { + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, + pool->sets[descid], 1, 0, 1, + VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, + 0, 0, &buffer_view }, + }; + VkCopyDescriptorSet copy[] = { + { VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET, 0, + pool->dctx->fonts.a[id].set, 0, 0, + pool->sets[descid], 0, 0, 1 }, + }; + dfunc->vkUpdateDescriptorSets (device->dev, 1, write, 1, copy); + return ~descid; +} + +static void +generate_slice_indices (qfv_stagebuf_t *staging, qfv_resobj_t *ind_buffer) +{ + qfv_packet_t *packet = QFV_PacketAcquire (staging); + uint32_t *ind = QFV_PacketExtend (packet, ind_buffer->buffer.size); + for (int i = 0; i < 8; i++) { + ind[i] = i; + ind[i + 9] = i + 1 + (i & 1) * 6; + ind[i + 18] = i + 8; + } + ind[8] = ind[17] = ~0; + QFV_PacketCopyBuffer (packet, ind_buffer->buffer.buffer, 0, + &bufferBarriers[qfv_BB_TransferWrite_to_IndexRead]); + QFV_PacketSubmit (packet); +} + +static void +create_buffers (vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + drawctx_t *dctx = ctx->draw_context; + auto rctx = ctx->render_context; + size_t frames = rctx->frames.size; + + dctx->draw_resource = malloc (2 * sizeof (qfv_resource_t) + // index buffer + + sizeof (qfv_resobj_t) + // svertex buffer and view + + 2 * sizeof (qfv_resobj_t) + // frames dynamic vertex buffers and views + + (frames) * 2 * sizeof (qfv_resobj_t) + // frames line vertex buffers + + (frames) * sizeof (qfv_resobj_t) + // frames instance buffers + + (frames) * sizeof (qfv_resobj_t)); + dctx->index_object = (qfv_resobj_t *) &dctx->draw_resource[2]; + dctx->svertex_objects = &dctx->index_object[1]; + dctx->dvertex_objects = &dctx->svertex_objects[2]; + dctx->lvertex_objects = &dctx->dvertex_objects[2 * frames]; + dctx->instance_objects = &dctx->lvertex_objects[frames]; + + dctx->svertex_index = 0; + dctx->svertex_max = MAX_QUADS * VERTS_PER_QUAD; + + dctx->draw_resource[0] = (qfv_resource_t) { + .name = "draw", + .va_ctx = ctx->va_ctx, + .memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + .num_objects = 1 + 2, // quad and 9-slice indices, and static verts + .objects = dctx->index_object, + }; + dctx->draw_resource[1] = (qfv_resource_t) { + .name = "draw", + .va_ctx = ctx->va_ctx, + .memory_properties = VK_MEMORY_PROPERTY_HOST_CACHED_BIT, + .num_objects = (2 * frames) + (frames) + (frames), + .objects = dctx->dvertex_objects, + }; + + dctx->index_object[0] = (qfv_resobj_t) { + .name = "quads.index", + .type = qfv_res_buffer, + .buffer = { + .size = INDS_PER_SLICE * sizeof (uint32_t), + .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + }, + }; + dctx->svertex_objects[0] = (qfv_resobj_t) { + .name = "sverts", + .type = qfv_res_buffer, + .buffer = { + .size = MAX_QUADS * BYTES_PER_QUAD, + .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, + }, + }; + dctx->svertex_objects[1] = (qfv_resobj_t) { + .name = "sverts", + .type = qfv_res_buffer_view, + .buffer_view = { + .buffer = 1, + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .offset = 0, + .size = dctx->svertex_objects[0].buffer.size, + }, + }; + + for (size_t i = 0; i < frames; i++) { + dctx->dvertex_objects[i * 2 + 0] = (qfv_resobj_t) { + .name = "dverts", + .type = qfv_res_buffer, + .buffer = { + .size = MAX_QUADS * BYTES_PER_QUAD, + .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, + }, + }; + dctx->dvertex_objects[i * 2 + 1] = (qfv_resobj_t) { + .name = "dverts", + .type = qfv_res_buffer_view, + .buffer_view = { + .buffer = &dctx->dvertex_objects[i * 2 + 0] + - dctx->draw_resource[1].objects, + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .offset = 0, + .size = dctx->dvertex_objects[i * 2 + 0].buffer.size, + }, + }; + dctx->lvertex_objects[i] = (qfv_resobj_t) { + .name = "line", + .type = qfv_res_buffer, + .buffer = { + .size = MAX_LINES * BYTES_PER_LINE, + .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + }, + }; + dctx->instance_objects[i] = (qfv_resobj_t) { + .name = "inst", + .type = qfv_res_buffer, + .buffer = { + .size = MAX_INSTANCES * sizeof (quadinst_t), + .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + }, + }; + } + QFV_CreateResource (device, &dctx->draw_resource[0]); + QFV_CreateResource (device, &dctx->draw_resource[1]); + + void *data; + VkDeviceMemory memory = dctx->draw_resource[1].memory; + dfunc->vkMapMemory (device->dev, memory, 0, VK_WHOLE_SIZE, 0, &data); + + for (size_t f = 0; f < frames; f++) { + drawframe_t *frame = &dctx->frames.a[f]; + frame->instance_buffer = dctx->instance_objects[f].buffer.buffer; + frame->instance_offset = dctx->instance_objects[f].buffer.offset; + frame->dvert_buffer = dctx->dvertex_objects[f * 2 + 0].buffer.buffer; + frame->dvert_view = dctx->dvertex_objects[f * 2 + 1].buffer_view.view; + frame->line_buffer = dctx->lvertex_objects[f].buffer.buffer; + frame->line_offset = dctx->lvertex_objects[f].buffer.offset; + + frame->dvertex_index = 0; + frame->dvertex_max = MAX_QUADS * VERTS_PER_QUAD; + + DARRAY_INIT (&frame->quad_batch, 16); + frame->quad_insts = (quadqueue_t) { + .quads = (quadinst_t *) ((byte *)data + frame->instance_offset), + .size = MAX_INSTANCES, + }; + + frame->line_verts = (linequeue_t) { + .verts = (linevert_t *) ((byte *)data + frame->line_offset), + .size = MAX_INSTANCES, + }; + } + + // The indices will never change so pre-generate and stash them + generate_slice_indices (ctx->staging, &dctx->index_object[0]); +} + +static void +flush_draw_scrap (vulkan_ctx_t *ctx) +{ + QFV_ScrapFlush (ctx->draw_context->scrap); +} + +static void +pic_free (drawctx_t *dctx, qpic_t *pic) +{ + __auto_type pd = (picdata_t *) pic->data; + if (pd->subpic) { + QFV_SubpicDelete (pd->subpic); + } + cmemfree (dctx->pic_memsuper, pic); +} + +static cachepic_t * +new_cachepic (drawctx_t *dctx, const char *name, qpic_t *pic) +{ + cachepic_t *cp; + size_t size = strlen (name) + 1; + + cp = cmemalloc (dctx->pic_memsuper, sizeof (cachepic_t)); + cp->name = cmemalloc (dctx->string_memsuper, size); + memcpy (cp->name, name, size); + cp->pic = pic; + return cp; +} + +static void +cachepic_free (void *_cp, void *_dctx) +{ + drawctx_t *dctx = _dctx; + cachepic_t *cp = (cachepic_t *) _cp; + pic_free (dctx, cp->pic); + cmemfree (dctx->string_memsuper, cp->name); + cmemfree (dctx->pic_memsuper, cp); +} + +static const char * +cachepic_getkey (const void *_cp, void *unused) +{ + return ((cachepic_t *) _cp)->name; +} + +static uint32_t +create_slice (vec4i_t rect, vec4i_t border, qpic_t *pic, + uint32_t *vertex_index, VkBuffer buffer, vulkan_ctx_t *ctx) +{ + __auto_type pd = (picdata_t *) pic->data; + + int x = rect[0]; + int y = rect[1]; + int w = rect[2]; + int h = rect[3]; + int l = border[0]; + int t = border[1]; + int r = w - border[2]; + int b = h - border[3]; + + float sx = 1.0 / pic->width; + float sy = 1.0 / pic->height; + if (pd->subpic) { + x += pd->subpic->rect->x; + y += pd->subpic->rect->y; + sx = sy = pd->subpic->size; + } + + vec4f_t p[16] = { + { 0, 0, 0, 0 }, { 0, t, 0, t }, { l, 0, l, 0 }, { l, t, l, t }, + { r, 0, r, 0 }, { r, t, r, t }, { w, 0, w, 0 }, { w, t, w, t }, + { 0, b, 0, b }, { 0, h, 0, h }, { l, b, l, b }, { l, h, l, h }, + { r, b, r, b }, { r, h, r, h }, { w, b, w, b }, { w, h, w, h }, + }; + + vec4f_t size = { 1, 1, sx, sy }; + qfv_packet_t *packet = QFV_PacketAcquire (ctx->staging); + quadvert_t *verts = QFV_PacketExtend (packet, BYTES_PER_SLICE); + for (int i = 0; i < VERTS_PER_SLICE; i++) { + vec4f_t v = ((vec4f_t) {0, 0, x, y} + p[i]) * size; + verts[i] = (quadvert_t) { {v[0], v[1]}, {v[2], v[3]} }; + } + + int ind = *vertex_index; + *vertex_index += VERTS_PER_SLICE; + QFV_PacketCopyBuffer (packet, buffer, ind * sizeof (quadvert_t), + &bufferBarriers[qfv_BB_TransferWrite_to_UniformRead]); + QFV_PacketSubmit (packet); + + return ind; +} + +static uint32_t +make_static_slice (vec4i_t rect, vec4i_t border, qpic_t *pic, vulkan_ctx_t *ctx) +{ + drawctx_t *dctx = ctx->draw_context; + VkBuffer buffer = dctx->svertex_objects[0].buffer.buffer; + + return create_slice (rect, border, pic, &dctx->svertex_index, buffer, ctx); +} + +static uint32_t +create_quad (int x, int y, int w, int h, qpic_t *pic, uint32_t *vertex_index, + VkBuffer buffer, vulkan_ctx_t *ctx) +{ + __auto_type pd = (picdata_t *) pic->data; + + float sl = 0, sr = 1, st = 0, sb = 1; + + if (pd->subpic) { + x += pd->subpic->rect->x; + y += pd->subpic->rect->y; + float size = pd->subpic->size; + sl = (x + 0) * size; + sr = (x + w) * size; + st = (y + 0) * size; + sb = (y + h) * size; + } + + qfv_packet_t *packet = QFV_PacketAcquire (ctx->staging); + quadvert_t *verts = QFV_PacketExtend (packet, BYTES_PER_QUAD); + verts[0] = (quadvert_t) { {0, 0}, {sl, st} }; + verts[1] = (quadvert_t) { {0, h}, {sl, sb} }; + verts[2] = (quadvert_t) { {w, 0}, {sr, st} }; + verts[3] = (quadvert_t) { {w, h}, {sr, sb} }; + + int ind = *vertex_index; + *vertex_index += VERTS_PER_QUAD; + QFV_PacketCopyBuffer (packet, buffer, ind * sizeof (quadvert_t), + &bufferBarriers[qfv_BB_TransferWrite_to_UniformRead]); + QFV_PacketSubmit (packet); + + return ind; +} + +static uint32_t +make_static_quad (int w, int h, qpic_t *pic, vulkan_ctx_t *ctx) +{ + drawctx_t *dctx = ctx->draw_context; + + return create_quad (0, 0, w, h, pic, &dctx->svertex_index, + dctx->svertex_objects[0].buffer.buffer, ctx); +} + +static int +make_dyn_quad (int x, int y, int w, int h, qpic_t *pic, vulkan_ctx_t *ctx) +{ + drawctx_t *dctx = ctx->draw_context; + drawframe_t *frame = &dctx->frames.a[ctx->curFrame]; + + return create_quad (x, y, w, h, pic, &frame->dvertex_index, + frame->dvert_buffer, ctx); +} + +static qpic_t * +pic_data (const char *name, int w, int h, const byte *data, vulkan_ctx_t *ctx) +{ + drawctx_t *dctx = ctx->draw_context; + qpic_t *pic; + byte *picdata; + + pic = cmemalloc (dctx->pic_memsuper, + field_offset (qpic_t, data[sizeof (picdata_t)])); + pic->width = w; + pic->height = h; + __auto_type pd = (picdata_t *) pic->data; + pd->subpic = QFV_ScrapSubpic (dctx->scrap, w, h); + pd->vert_index = make_static_quad (w, h, pic, ctx); + pd->slice_index = ~0; + pd->descid = CORE_DESC; + + picdata = QFV_SubpicBatch (pd->subpic, dctx->stage); + size_t size = w * h; + for (size_t i = 0; i < size; i++) { + byte pix = *data++; + byte *col = vid.palette + pix * 3; + byte alpha = (pix == 255) - 1; + // pre-multiply alpha. + *picdata++ = *col++ & alpha; + *picdata++ = *col++ & alpha; + *picdata++ = *col++ & alpha; + *picdata++ = alpha; + } + return pic; +} + +qpic_t * +Vulkan_Draw_MakePic (int width, int height, const byte *data, + vulkan_ctx_t *ctx) +{ + return pic_data (0, width, height, data, ctx); +} + +void +Vulkan_Draw_DestroyPic (qpic_t *pic, vulkan_ctx_t *ctx) +{ +} + +qpic_t * +Vulkan_Draw_PicFromWad (const char *name, vulkan_ctx_t *ctx) +{ + qpic_t *wadpic = W_GetLumpName (name); + + if (!wadpic) { + return 0; + } + return pic_data (name, wadpic->width, wadpic->height, wadpic->data, ctx); +} + +static qpic_t * +load_lmp (const char *path, vulkan_ctx_t *ctx) +{ + qpic_t *p; + if (strlen (path) < 4 || strcmp (path + strlen (path) - 4, ".lmp") + || !(p = (qpic_t *) QFS_LoadFile (QFS_FOpenFile (path), 0))) { + return 0; + } + + if (p->width < 32 && p->height < 32) { + qpic_t *pic = pic_data (path, p->width, p->height, p->data, ctx); + free (p); + return pic; + } + + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + drawctx_t *dctx = ctx->draw_context; + int fontid = dctx->fonts.size; + DARRAY_OPEN_AT (&dctx->fonts, fontid, 1); + drawfont_t *font = &dctx->fonts.a[fontid]; + + font->resource = malloc (sizeof (drawfontres_t)); + font->resource->resource = (qfv_resource_t) { + .name = va (ctx->va_ctx, "cachepic:%d", fontid), + .va_ctx = ctx->va_ctx, + .memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + .num_objects = 2, + .objects = &font->resource->glyph_image, + }; + + tex_t tex = { + .width = p->width, + .height = p->height, + .format = tex_rgba, + .loaded = 1, + .data = p->data, + }; + QFV_ResourceInitTexImage (&font->resource->glyph_image, "image", 0, &tex); + __auto_type cache_image = &font->resource->glyph_image; + + font->resource->glyph_iview = (qfv_resobj_t) { + .name = "image_view", + .type = qfv_res_image_view, + .image_view = { + .image = 0, + .type = VK_IMAGE_VIEW_TYPE_2D, + .format = font->resource->glyph_image.image.format, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .levelCount = VK_REMAINING_MIP_LEVELS, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + .components = { + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }, + }, + }; + __auto_type cache_iview = &font->resource->glyph_iview; + + QFV_CreateResource (ctx->device, &font->resource->resource); + + __auto_type packet = QFV_PacketAcquire (ctx->staging); + int count = tex.width * tex.height; + byte *texels = QFV_PacketExtend (packet, 4 * count); + byte palette[256 * 4]; + memcpy (palette, vid.palette32, sizeof (palette)); + palette[255*4 + 0] = 0; + palette[255*4 + 1] = 0; + palette[255*4 + 2] = 0; + Vulkan_ExpandPalette (texels, tex.data, palette, 2, count); + QFV_PacketCopyImage (packet, cache_image->image.image, + tex.width, tex.height, + &imageBarriers[qfv_LT_TransferDst_to_ShaderReadOnly]); + QFV_PacketSubmit (packet); + + font->set = QFV_DSManager_AllocSet (dctx->dsmanager);; + VkDescriptorImageInfo imageInfo = { + dctx->pic_sampler, + cache_iview->image_view.view, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }; + VkWriteDescriptorSet write[] = { + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, + font->set, 0, 0, 1, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + &imageInfo, 0, 0 }, + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, + font->set, 1, 0, 1, + VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, + 0, 0, &dctx->svertex_objects[1].buffer_view.view }, + }; + dfunc->vkUpdateDescriptorSets (device->dev, 2, write, 0, 0); + + qpic_t *pic; + pic = cmemalloc (dctx->pic_memsuper, + field_offset (qpic_t, data[sizeof (picdata_t)])); + pic->width = p->width; + pic->height = p->height; + __auto_type pd = (picdata_t *) pic->data; + pd->subpic = 0; + pd->vert_index = make_static_quad (p->width, p->height, pic, ctx); + pd->slice_index = ~0; + pd->descid = fontid; + + free (p); + return pic; +} + +qpic_t * +Vulkan_Draw_CachePic (const char *path, bool alpha, vulkan_ctx_t *ctx) +{ + cachepic_t *cpic; + drawctx_t *dctx = ctx->draw_context; + + if ((cpic = Hash_Find (dctx->pic_cache, path))) { + return cpic->pic; + } + qpic_t *pic = load_lmp (path, ctx); + cpic = new_cachepic (dctx, path, pic); + Hash_Add (dctx->pic_cache, cpic); + return pic; +} + +void +Vulkan_Draw_UncachePic (const char *path, vulkan_ctx_t *ctx) +{ + drawctx_t *dctx = ctx->draw_context; + Hash_Free (dctx->pic_cache, Hash_Del (dctx->pic_cache, path)); +} + +void +Vulkan_Draw_Shutdown (vulkan_ctx_t *ctx) +{ + auto device = ctx->device; + auto dctx = ctx->draw_context; + + QFV_DestroyResource (device, &dctx->draw_resource[0]); + QFV_DestroyResource (device, &dctx->draw_resource[1]); + for (size_t i = 0; i < dctx->fonts.size; i++) { + if (dctx->fonts.a[i].resource) { + QFV_DestroyResource (device, &dctx->fonts.a[i].resource->resource); + free (dctx->fonts.a[i].resource); + } + } + + Hash_DelTable (dctx->pic_cache); + delete_memsuper (dctx->pic_memsuper); + delete_memsuper (dctx->string_memsuper); + QFV_DestroyScrap (dctx->scrap); + QFV_DestroyStagingBuffer (dctx->stage); +} + +static void +load_conchars (vulkan_ctx_t *ctx) +{ + drawctx_t *dctx = ctx->draw_context; + + draw_chars = W_GetLumpName ("conchars"); + if (draw_chars) { + for (int i = 0; i < 256 * 64; i++) { + if (draw_chars[i] == 0) { + draw_chars[i] = 255; // proper transparent color + } + } + dctx->conchars = pic_data ("conchars", 128, 128, draw_chars, ctx); + } else { + qpic_t *charspic = Draw_Font8x8Pic (); + dctx->conchars = pic_data ("conchars", charspic->width, + charspic->height, charspic->data, ctx); + free (charspic); + } + dctx->conchar_inds = malloc (256 * sizeof (int)); + VkBuffer buffer = dctx->svertex_objects[0].buffer.buffer; + for (int i = 0; i < 256; i++) { + int cx = i % 16; + int cy = i / 16; + dctx->conchar_inds[i] = create_quad (cx * 8, cy * 8, 8, 8, + dctx->conchars, + &dctx->svertex_index, buffer, ctx); + } +} + +static void +load_crosshairs (vulkan_ctx_t *ctx) +{ + drawctx_t *dctx = ctx->draw_context; + qpic_t *hairpic = Draw_CrosshairPic (); + dctx->crosshair = pic_data ("crosshair", hairpic->width, + hairpic->height, hairpic->data, ctx); + free (hairpic); + + dctx->crosshair_inds = malloc (4 * sizeof (int)); + VkBuffer buffer = dctx->svertex_objects[0].buffer.buffer; +#define W CROSSHAIR_WIDTH +#define H CROSSHAIR_HEIGHT + dctx->crosshair_inds[0] = create_quad (0, 0, W, H, dctx->crosshair, + &dctx->svertex_index, buffer, ctx); + dctx->crosshair_inds[1] = create_quad (W, 0, W, H, dctx->crosshair, + &dctx->svertex_index, buffer, ctx); + dctx->crosshair_inds[2] = create_quad (0, H, W, H, dctx->crosshair, + &dctx->svertex_index, buffer, ctx); + dctx->crosshair_inds[3] = create_quad (W, H, W, H, dctx->crosshair, + &dctx->svertex_index, buffer, ctx); +#undef W +#undef H +} + +static void +load_white_pic (vulkan_ctx_t *ctx) +{ + drawctx_t *dctx = ctx->draw_context; + byte white_block = 0xfe; + + dctx->white_pic = pic_data ("white", 1, 1, &white_block, ctx); + __auto_type pd = (picdata_t *) dctx->white_pic->data; + pd->slice_index = make_static_slice ((vec4i_t) {0, 0, 1, 1}, + (vec4i_t) {0, 0, 0, 0}, + dctx->white_pic, ctx); +} + +static void +draw_quads (qfv_taskctx_t *taskctx) +{ + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto dctx = ctx->draw_context; + auto dframe = &dctx->frames.a[ctx->curFrame]; + auto layout = taskctx->pipeline->layout; + auto cmd = taskctx->cmd; + + VkBuffer instance_buffer = dframe->instance_buffer; + VkDeviceSize offsets[] = {0}; + dfunc->vkCmdBindVertexBuffers (cmd, 0, 1, &instance_buffer, offsets); + + VkBuffer ind_buffer = dctx->index_object[0].buffer.buffer; + dfunc->vkCmdBindIndexBuffer (cmd, ind_buffer, 0, VK_INDEX_TYPE_UINT32); + + uint32_t inst_start = 0; + for (size_t i = 0; i < dframe->quad_batch.size; i++) { + int fontid = dframe->quad_batch.a[i].descid; + uint32_t inst_count = dframe->quad_batch.a[i].count; + uint32_t ind_count = inst_count >> 24; + inst_count &= 0xffffff; + VkDescriptorSet set[2] = { + Vulkan_Matrix_Descriptors (ctx, ctx->curFrame), + fontid < 0 ? dframe->dyn_descs.sets[~fontid] + : dctx->fonts.a[fontid].set, + }; + dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + layout, 0, 2, set, 0, 0); + + dfunc->vkCmdDrawIndexed (cmd, ind_count, inst_count, 0, 0, inst_start); + inst_start += inst_count; + } + DARRAY_RESIZE (&dframe->quad_batch, 0); +} + +static void +draw_lines (qfv_taskctx_t *taskctx) +{ + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto dctx = ctx->draw_context; + auto dframe = &dctx->frames.a[ctx->curFrame]; + auto layout = taskctx->pipeline->layout; + auto cmd = taskctx->cmd; + + VkBuffer line_buffer = dframe->line_buffer; + VkDeviceSize offsets[] = {0}; + dfunc->vkCmdBindVertexBuffers (cmd, 0, 1, &line_buffer, offsets); + VkDescriptorSet set[1] = { + Vulkan_Matrix_Descriptors (ctx, ctx->curFrame), + }; + dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + layout, 0, 1, set, 0, 0); + dfunc->vkCmdDraw (cmd, dframe->line_verts.count * VERTS_PER_LINE, + 1, 0, 0); +} + +static void +flush_draw (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + flush_draw_scrap (ctx); +} + +static void +slice_draw (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto dctx = ctx->draw_context; + auto dframe = &dctx->frames.a[ctx->curFrame]; + if (!dframe->quad_insts.count) { + return; + } + + VkDeviceMemory memory = dctx->draw_resource[1].memory; + size_t atom = device->physDev->properties->limits.nonCoherentAtomSize; + size_t atom_mask = atom - 1; +#define a(x) (((x) + atom_mask) & ~atom_mask) + VkMappedMemoryRange ranges[] = { + { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0, + memory, dframe->instance_offset, + a(dframe->quad_insts.count * BYTES_PER_QUAD) }, + { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0, + memory, dframe->dvert_offset, + a(dframe->dvertex_index * sizeof (quadvert_t)) }, + }; +#undef a + dfunc->vkFlushMappedMemoryRanges (device->dev, 2, ranges); + + draw_quads (taskctx); + + dframe->quad_insts.count = 0; + dframe->dvertex_index = 0; + dframe->dyn_descs.in_use = 0; +} + +static void +line_draw (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto dctx = ctx->draw_context; + auto dframe = &dctx->frames.a[ctx->curFrame]; + + if (!dframe->line_verts.count) { + return; + } + + VkDeviceMemory memory = dctx->draw_resource[1].memory; + size_t atom = device->physDev->properties->limits.nonCoherentAtomSize; + size_t atom_mask = atom - 1; +#define a(x) (((x) + atom_mask) & ~atom_mask) + VkMappedMemoryRange ranges[] = { + { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0, + memory, dframe->line_offset, + a(dframe->line_verts.count * BYTES_PER_LINE) }, + }; +#undef a + dfunc->vkFlushMappedMemoryRanges (device->dev, 1, ranges); + + draw_lines (taskctx); + + dframe->line_verts.count = 0; +} + +static void +draw_scr_funcs (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + auto dctx = ctx->draw_context; + auto scr_funcs = dctx->scr_funcs; + if (!scr_funcs) { + return; + } + while (*scr_funcs) { + (*scr_funcs) (); + scr_funcs++; + } + dctx->scr_funcs = 0; +} + +static exprfunc_t flush_draw_func[] = { + { .func = flush_draw }, + {} +}; +static exprfunc_t slice_draw_func[] = { + { .func = slice_draw }, + {} +}; +static exprfunc_t line_draw_func[] = { + { .func = line_draw }, + {} +}; +static exprfunc_t draw_scr_funcs_func[] = { + { .func = draw_scr_funcs }, + {} +}; +static exprsym_t draw_task_syms[] = { + { "flush_draw", &cexpr_function, flush_draw_func }, + { "slice_draw", &cexpr_function, slice_draw_func }, + { "line_draw", &cexpr_function, line_draw_func }, + { "draw_scr_funcs", &cexpr_function, draw_scr_funcs_func }, + {} +}; + +void +Vulkan_Draw_Init (vulkan_ctx_t *ctx) +{ + QFV_Render_AddTasks (ctx, draw_task_syms); + + drawctx_t *dctx = calloc (1, sizeof (drawctx_t)); + ctx->draw_context = dctx; +} + +void +Vulkan_Draw_Setup (vulkan_ctx_t *ctx) +{ + qfvPushDebug (ctx, "draw init"); + + auto device = ctx->device; + auto dfunc = device->funcs; + auto dctx = ctx->draw_context; + + dctx->pic_sampler = QFV_Render_Sampler (ctx, "quakepic"); + dctx->glyph_sampler = QFV_Render_Sampler (ctx, "glyph"); + + dctx->dsmanager = QFV_Render_DSManager (ctx, "quad_data_set"); + + auto rctx = ctx->render_context; + size_t frames = rctx->frames.size; + DARRAY_INIT (&dctx->frames, frames); + DARRAY_RESIZE (&dctx->frames, frames); + dctx->frames.grow = 0; + memset (dctx->frames.a, 0, dctx->frames.size * sizeof (drawframe_t)); + + DARRAY_INIT (&dctx->fonts, 16); + DARRAY_RESIZE (&dctx->fonts, 16); + dctx->fonts.size = 0; + + dctx->pic_memsuper = new_memsuper (); + dctx->string_memsuper = new_memsuper (); + dctx->pic_cache = Hash_NewTable (127, cachepic_getkey, cachepic_free, + dctx, 0); + + create_buffers (ctx); + dctx->stage = QFV_CreateStagingBuffer (device, "draw", 4 * 1024 * 1024, + ctx->cmdpool); + dctx->scrap = QFV_CreateScrap (device, "draw_atlas", 2048, tex_rgba, + dctx->stage); + + load_conchars (ctx); + load_crosshairs (ctx); + load_white_pic (ctx); + + dctx->backtile_pic = Vulkan_Draw_PicFromWad ("backtile", ctx); + if (!dctx->backtile_pic) { + dctx->backtile_pic = dctx->white_pic; + } + + flush_draw_scrap (ctx); + + // core set + dynamic sets + + VkDescriptorImageInfo imageInfo = { + dctx->pic_sampler, + QFV_ScrapImageView (dctx->scrap), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }; + + for (size_t i = 0; i < frames; i++) { + __auto_type frame = &dctx->frames.a[i]; + frame->dyn_descs = (descpool_t) { .dctx = dctx }; + } + dctx->core_quad_set = QFV_DSManager_AllocSet (dctx->dsmanager); + + VkWriteDescriptorSet write[] = { + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, + dctx->core_quad_set, 0, 0, 1, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + &imageInfo, 0, 0 }, + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, + dctx->core_quad_set, 1, 0, 1, + VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, + 0, 0, &dctx->svertex_objects[1].buffer_view.view }, + }; + dfunc->vkUpdateDescriptorSets (device->dev, 2, write, 0, 0); + + DARRAY_APPEND (&dctx->fonts, (drawfont_t) { .set = dctx->core_quad_set }); + + qfvPopDebug (ctx); +} + +static inline descbatch_t * +get_desc_batch (drawframe_t *frame, int descid, uint32_t ind_count) +{ + descbatch_t *batch = &frame->quad_batch.a[frame->quad_batch.size - 1]; + if (!frame->quad_batch.size || batch->descid != descid + || ((batch->count & (0xff << 24)) != (ind_count << 24))) { + DARRAY_APPEND(&frame->quad_batch, ((descbatch_t) { .descid = descid })); + batch = &frame->quad_batch.a[frame->quad_batch.size - 1]; + batch->count = ind_count << 24; + } + + return batch; +} + +static inline void +draw_slice (float x, float y, float ox, float oy, int descid, uint32_t vertid, + const byte *color, drawframe_t *frame) +{ + __auto_type queue = &frame->quad_insts; + if (queue->count >= queue->size) { + return; + } + + __auto_type batch = get_desc_batch (frame, descid, INDS_PER_SLICE); + batch->count++; + + quadinst_t *quad = &queue->quads[queue->count++]; + *quad = (quadinst_t) { + .index = vertid, + .color = { QuatExpand (color) }, + .position = { x, y }, + .offset = { ox, oy }, + }; +} + +static inline void +draw_quad (float x, float y, int descid, uint32_t vertid, const byte *color, + drawframe_t *frame) +{ + __auto_type queue = &frame->quad_insts; + if (queue->count >= queue->size) { + return; + } + + __auto_type batch = get_desc_batch (frame, descid, INDS_PER_QUAD); + batch->count++; + + quadinst_t *quad = &queue->quads[queue->count++]; + *quad = (quadinst_t) { + .index = vertid, + .color = { QuatExpand (color) }, + .position = { x, y }, + .offset = { 0, 0 }, + }; +} + +static inline void +queue_character (int x, int y, byte chr, vulkan_ctx_t *ctx) +{ + drawctx_t *dctx = ctx->draw_context; + drawframe_t *frame = &dctx->frames.a[ctx->curFrame]; + + byte color[4] = {255, 255, 255, 255}; + draw_quad (x, y, CORE_DESC, dctx->conchar_inds[chr], color, frame); +} + +void +Vulkan_Draw_CharBuffer (int x, int y, draw_charbuffer_t *buffer, + vulkan_ctx_t *ctx) +{ + const byte *line = (byte *) buffer->chars; + int width = buffer->width; + int height = buffer->height; + while (height-- > 0) { + for (int i = 0; i < width; i++) { + Vulkan_Draw_Character (x + i * 8, y, line[i], ctx); + } + line += width; + y += 8; + } +} + +void +Vulkan_Draw_Character (int x, int y, unsigned int chr, vulkan_ctx_t *ctx) +{ + if (chr == ' ') { + return; + } + if (y <= -8 || y >= (int) vid.height) { + return; + } + if (x <= -8 || x >= (int) vid.width) { + return; + } + queue_character (x, y, chr, ctx); +} + +void +Vulkan_Draw_String (int x, int y, const char *str, vulkan_ctx_t *ctx) +{ + byte chr; + + if (!str || !str[0]) { + return; + } + if (y <= -8 || y >= (int) vid.height) { + return; + } + while (*str) { + if ((chr = *str++) != ' ' && x >= -8 && x < (int) vid.width) { + queue_character (x, y, chr, ctx); + } + x += 8; + } +} + +void +Vulkan_Draw_nString (int x, int y, const char *str, int count, + vulkan_ctx_t *ctx) +{ + byte chr; + + if (!str || !str[0]) { + return; + } + if (y <= -8 || y >= (int) vid.height) { + return; + } + while (count-- > 0 && *str) { + if ((chr = *str++) != ' ' && x >= -8 && x < (int) vid.width) { + queue_character (x, y, chr, ctx); + } + x += 8; + } +} + +void +Vulkan_Draw_AltString (int x, int y, const char *str, vulkan_ctx_t *ctx) +{ + byte chr; + + if (!str || !str[0]) { + return; + } + if (y <= -8 || y >= (int) vid.height) { + return; + } + while (*str) { + if ((chr = *str++ | 0x80) != (' ' | 0x80) + && x >= -8 && x < (int) vid.width) { + queue_character (x, y, chr, ctx); + } + x += 8; + } +} + +static void +draw_crosshair_plus (int ch, int x, int y, vulkan_ctx_t *ctx) +{ + Vulkan_Draw_Character (x - 4, y - 4, '+', ctx); +} + +static void +draw_crosshair_pic (int ch, int x, int y, vulkan_ctx_t *ctx) +{ + drawctx_t *dctx = ctx->draw_context; + drawframe_t *frame = &dctx->frames.a[ctx->curFrame]; + + byte *color = &vid.palette32[bound (0, crosshaircolor, 255) * 4]; + draw_quad (x, y, CORE_DESC, dctx->crosshair_inds[ch - 1], color, frame); +} + +static void (*crosshair_func[]) (int ch, int x, int y, vulkan_ctx_t *ctx) = { + draw_crosshair_plus, + draw_crosshair_pic, + draw_crosshair_pic, + draw_crosshair_pic, + draw_crosshair_pic, +}; + +void +Vulkan_Draw_CrosshairAt (int ch, int x, int y, vulkan_ctx_t *ctx) +{ + unsigned c = ch - 1; + + if (c >= sizeof (crosshair_func) / sizeof (crosshair_func[0])) + return; + + crosshair_func[c] (c, x, y, ctx); +} + +void +Vulkan_Draw_Crosshair (vulkan_ctx_t *ctx) +{ + int x, y; + int s = 2 * ctx->twod_scale; + + x = vid.width / s + cl_crossx; + y = vid.height / s + cl_crossy; + + Vulkan_Draw_CrosshairAt (crosshair, x, y, ctx); +} + +void +Vulkan_Draw_TextBox (int x, int y, int width, int lines, byte alpha, + vulkan_ctx_t *ctx) +{ + drawctx_t *dctx = ctx->draw_context; + drawframe_t *frame = &dctx->frames.a[ctx->curFrame]; + + byte color[4] = {255, 255, 255, 255}; + qpic_t *p; + int cx, cy, n; +#define draw(px, py, pp) \ + do { \ + __auto_type pd = (picdata_t *) pp->data; \ + draw_quad (px, py, pd->descid, pd->vert_index, color, frame); \ + } while (0) + + color[3] = alpha; + // draw left side + cx = x; + cy = y; + p = Vulkan_Draw_CachePic ("gfx/box_tl.lmp", true, ctx); + draw (cx, cy, p); + p = Vulkan_Draw_CachePic ("gfx/box_ml.lmp", true, ctx); + for (n = 0; n < lines; n++) { + cy += 8; + draw (cx, cy, p); + } + p = Vulkan_Draw_CachePic ("gfx/box_bl.lmp", true, ctx); + draw (cx, cy + 8, p); + + // draw middle + cx += 8; + while (width > 0) { + cy = y; + p = Vulkan_Draw_CachePic ("gfx/box_tm.lmp", true, ctx); + draw (cx, cy, p); + p = Vulkan_Draw_CachePic ("gfx/box_mm.lmp", true, ctx); + for (n = 0; n < lines; n++) { + cy += 8; + if (n == 1) + p = Vulkan_Draw_CachePic ("gfx/box_mm2.lmp", true, ctx); + draw (cx, cy, p); + } + p = Vulkan_Draw_CachePic ("gfx/box_bm.lmp", true, ctx); + draw (cx, cy + 8, p); + width -= 2; + cx += 16; + } + + // draw right side + cy = y; + p = Vulkan_Draw_CachePic ("gfx/box_tr.lmp", true, ctx); + draw (cx, cy, p); + p = Vulkan_Draw_CachePic ("gfx/box_mr.lmp", true, ctx); + for (n = 0; n < lines; n++) { + cy += 8; + draw (cx, cy, p); + } + p = Vulkan_Draw_CachePic ("gfx/box_br.lmp", true, ctx); + draw (cx, cy + 8, p); +#undef draw +} + +void +Vulkan_Draw_Pic (int x, int y, qpic_t *pic, vulkan_ctx_t *ctx) +{ + drawctx_t *dctx = ctx->draw_context; + drawframe_t *frame = &dctx->frames.a[ctx->curFrame]; + + static byte color[4] = { 255, 255, 255, 255}; + __auto_type pd = (picdata_t *) pic->data; + draw_quad (x, y, pd->descid, pd->vert_index, color, frame); +} + +void +Vulkan_Draw_FitPic (int x, int y, int width, int height, qpic_t *pic, + vulkan_ctx_t *ctx) +{ + drawctx_t *dctx = ctx->draw_context; + drawframe_t *frame = &dctx->frames.a[ctx->curFrame]; + __auto_type pd = (picdata_t *) pic->data; + if (pd->slice_index == ~0u) { + vec4i_t rect = (vec4i_t) {0, 0, pic->width, pic->height}; + vec4i_t border = (vec4i_t) {0, 0, 0, 0}; + pd->slice_index = make_static_slice (rect, border, pic, ctx); + } + static byte color[4] = { 255, 255, 255, 255}; + draw_slice (x, y, width - pic->width, height - pic->height, + pd->descid, pd->slice_index, color, frame); +} + +void +Vulkan_Draw_Picf (float x, float y, qpic_t *pic, vulkan_ctx_t *ctx) +{ + drawctx_t *dctx = ctx->draw_context; + drawframe_t *frame = &dctx->frames.a[ctx->curFrame]; + + static byte color[4] = { 255, 255, 255, 255}; + __auto_type pd = (picdata_t *) pic->data; + draw_quad (x, y, pd->descid, pd->vert_index, color, frame); +} + +void +Vulkan_Draw_SubPic (int x, int y, qpic_t *pic, + int srcx, int srcy, int width, int height, + vulkan_ctx_t *ctx) +{ + drawctx_t *dctx = ctx->draw_context; + drawframe_t *frame = &dctx->frames.a[ctx->curFrame]; + + uint32_t vind = make_dyn_quad (srcx, srcy, width, height, pic, ctx); + static byte color[4] = { 255, 255, 255, 255}; + int descid = get_dyn_descriptor (&frame->dyn_descs, pic, + frame->dvert_view, ctx); + draw_quad (x, y, descid, vind, color, frame); +} + +void +Vulkan_Draw_ConsoleBackground (int lines, byte alpha, vulkan_ctx_t *ctx) +{ + //FIXME fitpic with color + //float a = bound (0, alpha, 255) / 255.0; + // use pre-multiplied alpha + //quat_t color = { a, a, a, a}; + qpic_t *cpic; + cpic = Vulkan_Draw_CachePic ("gfx/conback.lmp", false, ctx); + float s = 1.0 / ctx->twod_scale; + int y = lines - vid.height * s; + Vulkan_Draw_FitPic (0, y, vid.width * s, vid.height * s, cpic, ctx); +} + +void +Vulkan_Draw_TileClear (int x, int y, int w, int h, vulkan_ctx_t *ctx) +{ + drawctx_t *dctx = ctx->draw_context; + drawframe_t *frame = &dctx->frames.a[ctx->curFrame]; + + static byte color[4] = { 255, 255, 255, 255}; + vrect_t *tile_rect = VRect_New (x, y, w, h); + vrect_t *sub = VRect_New (0, 0, 0, 0); // filled in later + qpic_t *pic = dctx->backtile_pic; + int sub_sx, sub_sy, sub_ex, sub_ey; + int descid = get_dyn_descriptor (&frame->dyn_descs, pic, + frame->dvert_view, ctx); + + sub_sx = x / pic->width; + sub_sy = y / pic->height; + sub_ex = (x + w + pic->width - 1) / pic->width; + sub_ey = (y + h + pic->height - 1) / pic->height; + for (int j = sub_sy; j < sub_ey; j++) { + for (int i = sub_sx; i < sub_ex; i++) { + vrect_t *t = sub; + + sub->x = i * pic->width; + sub->y = j * pic->height; + sub->width = pic->width; + sub->height = pic->height; + sub = VRect_Intersect (sub, tile_rect); + VRect_Delete (t); + int sx = sub->x % pic->width; + int sy = sub->y % pic->height; + int sw = sub->width; + int sh = sub->height; + uint32_t vind = make_dyn_quad (sx, sy, sw, sh, pic, ctx); + draw_quad (sub->x, sub->y, descid, vind, color, frame); + } + } +} + +void +Vulkan_Draw_Fill (int x, int y, int w, int h, int c, vulkan_ctx_t *ctx) +{ + drawctx_t *dctx = ctx->draw_context; + drawframe_t *frame = &dctx->frames.a[ctx->curFrame]; + + byte color[4] = {VectorExpand (vid.palette + c * 3), 255 }; + __auto_type pd = (picdata_t *) dctx->white_pic->data; + draw_slice (x, y, w - 1, h - 1, pd->descid, pd->slice_index, color, frame); +} + +void +Vulkan_Draw_Line (int x0, int y0, int x1, int y1, int c, vulkan_ctx_t *ctx) +{ + drawctx_t *dctx = ctx->draw_context; + drawframe_t *frame = &dctx->frames.a[ctx->curFrame]; + linequeue_t *queue = &frame->line_verts; + + if (queue->count >= queue->size) { + return; + } + + linevert_t *verts = queue->verts + queue->count * VERTS_PER_LINE; + verts[0] = (linevert_t) { + .xy = { x0, y0 }, + .color = { VectorExpand (vid.palette + c * 3), 255 }, + }; + verts[1] = (linevert_t) { + .xy = { x1, y1 }, + .color = { VectorExpand (vid.palette + c * 3), 255 }, + }; + + queue->count++; +} + +static inline void +draw_blendscreen (const byte *color, vulkan_ctx_t *ctx) +{ + drawctx_t *dctx = ctx->draw_context; + drawframe_t *frame = &dctx->frames.a[ctx->curFrame]; + float s = 1.0 / ctx->twod_scale; + + __auto_type pd = (picdata_t *) dctx->white_pic->data; + draw_slice (0, 0, vid.width * s - 1, vid.height * s - 1, + pd->descid, pd->slice_index, color, frame); +} + +void +Vulkan_Draw_FadeScreen (vulkan_ctx_t *ctx) +{ + static byte color[4] = { 0, 0, 0, 179 }; + draw_blendscreen (color, ctx); +} + +void +Vulkan_Draw_BlendScreen (quat_t color, vulkan_ctx_t *ctx) +{ + if (color[3]) { + byte c[4]; + // pre-multiply alpha. + // FIXME this is kind of silly because q1source pre-multiplies alpha + // for blends, but this was un-done early in QF's history in order + // to avoid a pair of state changes + VectorScale (color, color[3] * 255, c); + c[3] = color[3] * 255; + draw_blendscreen (c, ctx); + } +} + +int +Vulkan_Draw_AddFont (font_t *rfont, vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + drawctx_t *dctx = ctx->draw_context; + int fontid = dctx->fonts.size; + DARRAY_OPEN_AT (&dctx->fonts, fontid, 1); + drawfont_t *font = &dctx->fonts.a[fontid]; + + font->resource = malloc (sizeof (drawfontres_t)); + font->resource->resource = (qfv_resource_t) { + .name = va (ctx->va_ctx, "glyph_data:%d", fontid), + .va_ctx = ctx->va_ctx, + .memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + .num_objects = 4, + .objects = &font->resource->glyph_data, + }; + + font->resource->glyph_data = (qfv_resobj_t) { + .name = "geom", + .type = qfv_res_buffer, + .buffer = { + .size = rfont->num_glyphs * 4 * sizeof (quadvert_t), + .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, + }, + }; + __auto_type glyph_data = &font->resource->glyph_data; + + font->resource->glyph_bview = (qfv_resobj_t) { + .name = "geom_view", + .type = qfv_res_buffer_view, + .buffer_view = { + .buffer = 0, + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .offset = 0, + .size = font->resource->glyph_data.buffer.size, + }, + }; + __auto_type glyph_bview = &font->resource->glyph_bview; + + tex_t tex = { + .width = rfont->scrap.width, + .height = rfont->scrap.height, + .format = tex_a, + .loaded = 1, + .data = rfont->scrap_bitmap, + }; + QFV_ResourceInitTexImage (&font->resource->glyph_image, "image", 0, &tex); + __auto_type glyph_image = &font->resource->glyph_image; + + font->resource->glyph_iview = (qfv_resobj_t) { + .name = "image_view", + .type = qfv_res_image_view, + .image_view = { + .image = 2, + .type = VK_IMAGE_VIEW_TYPE_2D, + .format = font->resource->glyph_image.image.format, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .levelCount = VK_REMAINING_MIP_LEVELS, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + .components = { + .r = VK_COMPONENT_SWIZZLE_R, + .g = VK_COMPONENT_SWIZZLE_R, + .b = VK_COMPONENT_SWIZZLE_R, + .a = VK_COMPONENT_SWIZZLE_R, + }, + }, + }; + __auto_type glyph_iview = &font->resource->glyph_iview; + + QFV_CreateResource (ctx->device, &font->resource->resource); + + qfv_packet_t *packet = QFV_PacketAcquire (ctx->staging); + quadvert_t *verts = QFV_PacketExtend (packet, glyph_data->buffer.size); + for (FT_Long i = 0; i < rfont->num_glyphs; i++) { + vrect_t *rect = &rfont->glyph_rects[i]; + float x = 0; + float y = 0; + float w = rect->width; + float h = rect->height; + float u = rect->x; + float v = rect->y; + float s = 1.0 / rfont->scrap.width; + float t = 1.0 / rfont->scrap.height; + verts[i * 4 + 0] = (quadvert_t) { + .offset = { x, y }, + .uv = { u * s, v * t }, + }; + verts[i * 4 + 1] = (quadvert_t) { + .offset = { x, y + h }, + .uv = { u * s, (v + h) * t }, + }; + verts[i * 4 + 2] = (quadvert_t) { + .offset = { x + w, y }, + .uv = {(u + w) * s, v * t }, + }; + verts[i * 4 + 3] = (quadvert_t) { + .offset = { x + w, y + h }, + .uv = {(u + w) * s, (v + h) * t }, + }; + } + QFV_PacketCopyBuffer (packet, glyph_data->buffer.buffer, 0, + &bufferBarriers[qfv_BB_TransferWrite_to_UniformRead]); + QFV_PacketSubmit (packet); + + packet = QFV_PacketAcquire (ctx->staging); + byte *texels = QFV_PacketExtend (packet, tex.width * tex.height); + memcpy (texels, tex.data, tex.width * tex.height); + QFV_PacketCopyImage (packet, glyph_image->image.image, + tex.width, tex.height, + &imageBarriers[qfv_LT_TransferDst_to_ShaderReadOnly]); + QFV_PacketSubmit (packet); + + font->set = QFV_DSManager_AllocSet (dctx->dsmanager);; + VkDescriptorImageInfo imageInfo = { + dctx->glyph_sampler, + glyph_iview->image_view.view, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }; + VkWriteDescriptorSet write[] = { + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, + font->set, 0, 0, 1, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + &imageInfo, 0, 0 }, + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, + font->set, 1, 0, 1, + VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, + 0, 0, &glyph_bview->buffer_view.view }, + }; + dfunc->vkUpdateDescriptorSets (device->dev, 2, write, 0, 0); + + return fontid; +} + +void +Vulkan_Draw_Glyph (int x, int y, int fontid, int glyph, int c, + vulkan_ctx_t *ctx) +{ + drawctx_t *dctx = ctx->draw_context; + drawframe_t *frame = &dctx->frames.a[ctx->curFrame]; + + quadqueue_t *queue = &frame->quad_insts; + if (queue->count >= queue->size) { + return; + } + + byte color[4] = { VectorExpand (vid.palette + c * 3), 255 }; + draw_quad (x, y, fontid, glyph * 4, color, frame); +} + +void +Vulkan_LineGraph (int x, int y, int *h_vals, int count, int height, + vulkan_ctx_t *ctx) +{ + static int colors[] = { 0xd0, 0x4f, 0x6f }; + + while (count-- > 0) { + int h = *h_vals++; + int c = h < 9998 || h > 10000 ? 0xfe : colors[h - 9998]; + h = min (h, height); + Vulkan_Draw_Line (x, y, x, y - h, c, ctx); + x++; + } +} + +void +Vulkan_SetScrFuncs (SCR_Func *scr_funcs, vulkan_ctx_t *ctx) +{ + auto dctx = ctx->draw_context; + dctx->scr_funcs = scr_funcs; +} diff --git a/libs/video/renderer/vulkan/vulkan_iqm.c b/libs/video/renderer/vulkan/vulkan_iqm.c new file mode 100644 index 000000000..1fc653a95 --- /dev/null +++ b/libs/video/renderer/vulkan/vulkan_iqm.c @@ -0,0 +1,333 @@ +/* + vulkan_iqm.c + + Vulkan IQM model pipeline + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/5/3 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "QF/cvar.h" +#include "QF/iqm.h" +#include "QF/va.h" + +#include "QF/scene/entity.h" + +#include "QF/Vulkan/qf_iqm.h" +#include "QF/Vulkan/qf_matrices.h" +#include "QF/Vulkan/qf_texture.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/descriptor.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/dsmanager.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/resource.h" +#include "QF/Vulkan/render.h" + +#include "r_internal.h" +#include "vid_vulkan.h" + +typedef struct { + mat4f_t mat; + float blend; + byte colorA[4]; + byte colorB[4]; + vec4f_t base_color; + vec4f_t fog; +} iqm_push_constants_t; + +static void +emit_commands (VkCommandBuffer cmd, int pose1, int pose2, + qfv_iqm_skin_t *skins, + uint32_t numPC, qfv_push_constants_t *constants, + iqm_t *iqm, qfv_taskctx_t *taskctx, entity_t ent) +{ + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto layout = taskctx->pipeline->layout; + + auto mesh = (qfv_iqm_t *) iqm->extra_data; + + VkDeviceSize offsets[] = { 0, 0, }; + VkBuffer buffers[] = { + mesh->geom_buffer, + mesh->rend_buffer, + }; + int bindingCount = 2;//skins ? 2 : 1; + + Vulkan_BeginEntityLabel (ctx, cmd, ent); + + dfunc->vkCmdBindVertexBuffers (cmd, 0, bindingCount, buffers, offsets); + dfunc->vkCmdBindIndexBuffer (cmd, mesh->index_buffer, 0, + VK_INDEX_TYPE_UINT16); + QFV_PushConstants (device, cmd, layout, numPC, constants); + for (int i = 0; i < iqm->num_meshes; i++) { + if (skins) { + VkDescriptorSet sets[] = { + skins[i].descriptor, + mesh->bones_descriptors[ctx->curFrame], + }; + dfunc->vkCmdBindDescriptorSets (cmd, + VK_PIPELINE_BIND_POINT_GRAPHICS, + layout, 1, 2, sets, 0, 0); + } else { + VkDescriptorSet sets[] = { + mesh->bones_descriptors[ctx->curFrame], + }; + dfunc->vkCmdBindDescriptorSets (cmd, + VK_PIPELINE_BIND_POINT_GRAPHICS, + layout, 2, 1, sets, 0, 0); + } + dfunc->vkCmdDrawIndexed (cmd, 3 * iqm->meshes[i].num_triangles, 1, + 3 * iqm->meshes[i].first_triangle, 0, 0); + } + + QFV_CmdEndLabel (device, cmd); +} + +static VkWriteDescriptorSet base_buffer_write = { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, 0, + 0, 0, 1, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + 0, 0, 0 +}; + +void +Vulkan_IQMAddBones (vulkan_ctx_t *ctx, iqm_t *iqm) +{ + qfvPushDebug (ctx, "Vulkan_IQMAddBones"); + + auto device = ctx->device; + auto dfunc = device->funcs; + auto ictx = ctx->iqm_context; + auto mesh = (qfv_iqm_t *) iqm->extra_data; + int num_sets = ictx->frames.size; + + if (!ictx->dsmanager) { + ictx->dsmanager = QFV_Render_DSManager (ctx, "bone_set"); + } + + for (int i = 0; i < num_sets; i++) { + auto set = QFV_DSManager_AllocSet (ictx->dsmanager); + mesh->bones_descriptors[i] = set; + } + + VkDescriptorBufferInfo bufferInfo[num_sets]; + size_t bones_size = sizeof (vec4f_t[iqm->num_joints * 3]); + for (int i = 0; i < num_sets; i++) { + bufferInfo[i].buffer = mesh->bones_buffer; + bufferInfo[i].offset = i * bones_size; + bufferInfo[i].range = bones_size; + }; + VkWriteDescriptorSet write[num_sets]; + for (int i = 0; i < num_sets; i++) { + write[i] = base_buffer_write; + write[i].dstSet = mesh->bones_descriptors[i]; + write[i].pBufferInfo = &bufferInfo[i]; + } + dfunc->vkUpdateDescriptorSets (device->dev, num_sets, write, 0, 0); + + qfvPopDebug (ctx); +} + +void +Vulkan_IQMRemoveBones (vulkan_ctx_t *ctx, iqm_t *iqm) +{ + auto ictx = ctx->iqm_context; + auto mesh = (qfv_iqm_t *) iqm->extra_data; + int num_sets = ictx->frames.size; + + for (int i = 0; i < num_sets; i++) { + QFV_DSManager_FreeSet (ictx->dsmanager, mesh->bones_descriptors[i]); + } +} + +void +Vulkan_IQMAddSkin (vulkan_ctx_t *ctx, qfv_iqm_skin_t *skin) +{ + iqmctx_t *ictx = ctx->iqm_context; + skin->descriptor = Vulkan_CreateCombinedImageSampler (ctx, skin->view, + ictx->sampler); +} + +void +Vulkan_IQMRemoveSkin (vulkan_ctx_t *ctx, qfv_iqm_skin_t *skin) +{ + Vulkan_FreeTexture (ctx, skin->descriptor); + skin->descriptor = 0; +} + +static void +iqm_draw_ent (qfv_taskctx_t *taskctx, entity_t ent, bool pass) +{ + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); + auto model = renderer->model; + auto iqm = (iqm_t *) model->aliashdr; + qfv_iqm_t *mesh = iqm->extra_data; + auto skins = mesh->skins; + iqm_push_constants_t constants = {}; + iqmframe_t *frame; + + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, + ent.reg); + constants.blend = R_IQMGetLerpedFrames (animation, iqm); + frame = R_IQMBlendFrames (iqm, animation->pose1, animation->pose2, + constants.blend, 0); + + vec4f_t *bone_data; + dfunc->vkMapMemory (device->dev, mesh->bones->memory, 0, VK_WHOLE_SIZE, + 0, (void **)&bone_data); + for (int i = 0; i < iqm->num_joints; i++) { + vec4f_t *b = bone_data + (ctx->curFrame * iqm->num_joints + i) * 3; + mat4f_t f; + // R_IQMBlendFrames sets up the frame as a 4x4 matrix for m * v, but + // the shader wants a 3x4 (column x row) matrix for v * m, which is + // just a transpose (and drop of the 4th column) away. + mat4ftranspose (f, (vec4f_t *) &frame[i]); + // copy only the first 3 columns + memcpy (b, f, 3 * sizeof (vec4f_t)); + } +#define a(x) ((x) & ~0x3f) + VkMappedMemoryRange range = { + VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0, + mesh->bones->memory, + a(3 * ctx->curFrame * iqm->num_joints * sizeof (vec4f_t)), + a(3 * iqm->num_joints * sizeof (vec4f_t) + 0x3f), + }; +#undef a + dfunc->vkFlushMappedMemoryRanges (device->dev, 1, &range); + dfunc->vkUnmapMemory (device->dev, mesh->bones->memory); + + transform_t transform = Entity_Transform (ent); + qfv_push_constants_t push_constants[] = { + { VK_SHADER_STAGE_VERTEX_BIT, + field_offset (iqm_push_constants_t, mat), + sizeof (mat4f_t), Transform_GetWorldMatrixPtr (transform) }, + { VK_SHADER_STAGE_VERTEX_BIT, + field_offset (iqm_push_constants_t, blend), + sizeof (float), &constants.blend }, + { VK_SHADER_STAGE_FRAGMENT_BIT, + field_offset (iqm_push_constants_t, colorA), + sizeof (constants.colorA), constants.colorA }, + { VK_SHADER_STAGE_FRAGMENT_BIT, + field_offset (iqm_push_constants_t, colorB), + sizeof (constants.colorB), constants.colorB }, + { VK_SHADER_STAGE_FRAGMENT_BIT, + field_offset (iqm_push_constants_t, base_color), + sizeof (constants.base_color), &constants.base_color }, + { VK_SHADER_STAGE_FRAGMENT_BIT, + field_offset (iqm_push_constants_t, fog), + sizeof (constants.fog), &constants.fog }, + }; + + QuatCopy (renderer->colormod, constants.base_color); + QuatCopy (skins[0].colora, constants.colorA); + QuatCopy (skins[0].colorb, constants.colorB); + QuatZero (constants.fog); + + emit_commands (taskctx->cmd, animation->pose1, animation->pose2, + pass ? skins : 0, + pass ? 6 : 2, push_constants, + iqm, taskctx, ent); +} + +static void +iqm_draw (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + int pass = *(int *) params[0]->value; + + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto layout = taskctx->pipeline->layout; + auto cmd = taskctx->cmd; + + VkDescriptorSet sets[] = { + Vulkan_Matrix_Descriptors (ctx, ctx->curFrame), + }; + dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + layout, 0, 1, sets, 0, 0); + + auto queue = r_ent_queue; //FIXME fetch from scene + for (size_t i = 0; i < queue->ent_queues[mod_iqm].size; i++) { + entity_t ent = queue->ent_queues[mod_iqm].a[i]; + iqm_draw_ent (taskctx, ent, pass); + } +} + +static exprtype_t *iqm_draw_params[] = { + &cexpr_int, +}; +static exprfunc_t iqm_draw_func[] = { + { .func = iqm_draw, .num_params = 1, .param_types = iqm_draw_params }, + {} +}; +static exprsym_t iqm_task_syms[] = { + { "iqm_draw", &cexpr_function, iqm_draw_func }, + {} +}; + +void +Vulkan_IQM_Init (vulkan_ctx_t *ctx) +{ + qfvPushDebug (ctx, "iqm init"); + QFV_Render_AddTasks (ctx, iqm_task_syms); + + iqmctx_t *ictx = calloc (1, sizeof (iqmctx_t)); + ctx->iqm_context = ictx; + + auto rctx = ctx->render_context; + size_t frames = rctx->frames.size; + DARRAY_INIT (&ictx->frames, frames); + DARRAY_RESIZE (&ictx->frames, frames); + ictx->frames.grow = 0; + + qfvPopDebug (ctx); +} + +void +Vulkan_IQM_Setup (vulkan_ctx_t *ctx) +{ + auto ictx = ctx->iqm_context; + ictx->sampler = QFV_Render_Sampler (ctx, "alias_sampler"); +} + +void +Vulkan_IQM_Shutdown (vulkan_ctx_t *ctx) +{ + iqmctx_t *ictx = ctx->iqm_context; + + free (ictx->frames.a); + free (ictx); +} diff --git a/libs/video/renderer/vulkan/vulkan_lighting.c b/libs/video/renderer/vulkan/vulkan_lighting.c new file mode 100644 index 000000000..9249f46c5 --- /dev/null +++ b/libs/video/renderer/vulkan/vulkan_lighting.c @@ -0,0 +1,798 @@ +/* + vulkan_lighting.c + + Vulkan lighting pass pipeline + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/2/23 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include + +#include "qfalloca.h" + +#include "QF/cvar.h" +#include "QF/dstring.h" +#include "QF/heapsort.h" +#include "QF/plist.h" +#include "QF/progs.h" +#include "QF/script.h" +#include "QF/set.h" +#include "QF/sys.h" +#include "QF/va.h" + +#include "QF/scene/scene.h" +#include "QF/ui/view.h" + +#include "QF/Vulkan/qf_draw.h" +#include "QF/Vulkan/qf_lighting.h" +#include "QF/Vulkan/qf_texture.h" +#include "QF/Vulkan/barrier.h" +#include "QF/Vulkan/buffer.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/descriptor.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/dsmanager.h" +#include "QF/Vulkan/image.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/projection.h" +#include "QF/Vulkan/render.h" +#include "QF/Vulkan/resource.h" +#include "QF/Vulkan/staging.h" + +#include "compat.h" + +#include "r_internal.h" +#include "vid_vulkan.h" +#include "vkparse.h" + +static void +update_lights (vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + lightingctx_t *lctx = ctx->lighting_context; + lightingdata_t *ldata = lctx->ldata; + lightingframe_t *lframe = &lctx->frames.a[ctx->curFrame]; + + Light_FindVisibleLights (ldata); + + dlight_t *lights[MaxLights]; + qfv_packet_t *packet = QFV_PacketAcquire (ctx->staging); + qfv_light_buffer_t *light_data = QFV_PacketExtend (packet, + sizeof (*light_data)); + + float style_intensities[NumStyles]; + for (int i = 0; i < NumStyles; i++) { + style_intensities[i] = d_lightstylevalue[i] / 65536.0; + } + + light_data->lightCount = 0; + R_FindNearLights (r_refdef.frame.position, MaxLights - 1, lights); + for (int i = 0; i < MaxLights - 1; i++) { + if (!lights[i]) { + break; + } + light_data->lightCount++; + VectorCopy (lights[i]->color, light_data->lights[i].color); + // dynamic lights seem a tad faint, so 16x map lights + light_data->lights[i].color[3] = lights[i]->radius / 16; + VectorCopy (lights[i]->origin, light_data->lights[i].position); + // dlights are local point sources + light_data->lights[i].position[3] = 1; + light_data->lights[i].attenuation = + (vec4f_t) { 0, 0, 1, 1/lights[i]->radius }; + // full sphere, normal light (not ambient) + light_data->lights[i].direction = (vec4f_t) { 0, 0, 1, 1 }; + } + for (size_t i = 0; (i < ldata->lightvis.size + && light_data->lightCount < MaxLights); i++) { + if (ldata->lightvis.a[i]) { + light_t *light = &light_data->lights[light_data->lightCount++]; + *light = ldata->lights.a[i]; + light->color[3] *= style_intensities[ldata->lightstyles.a[i]]; + } + } + if (developer & SYS_lighting) { + Vulkan_Draw_String (vid.width - 32, 8, + va (ctx->va_ctx, "%3d", light_data->lightCount), + ctx); + } + + qfv_bufferbarrier_t bb = bufferBarriers[qfv_BB_Unknown_to_TransferWrite]; + bb.barrier.buffer = lframe->light_buffer; + bb.barrier.size = sizeof (qfv_light_buffer_t); + dfunc->vkCmdPipelineBarrier (packet->cmd, bb.srcStages, bb.dstStages, + 0, 0, 0, 1, &bb.barrier, 0, 0); + VkBufferCopy copy_region[] = { + { packet->offset, 0, sizeof (qfv_light_buffer_t) }, + }; + dfunc->vkCmdCopyBuffer (packet->cmd, ctx->staging->buffer, + lframe->light_buffer, 1, ©_region[0]); + bb = bufferBarriers[qfv_BB_TransferWrite_to_UniformRead]; + bb.barrier.buffer = lframe->light_buffer; + bb.barrier.size = sizeof (qfv_light_buffer_t); + dfunc->vkCmdPipelineBarrier (packet->cmd, bb.srcStages, bb.dstStages, + 0, 0, 0, 1, &bb.barrier, 0, 0); + QFV_PacketSubmit (packet); +} +#if 0 +static void +lighting_draw_maps (qfv_orenderframe_t *rFrame) +{ + vulkan_ctx_t *ctx = rFrame->vulkan_ctx; + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + lightingctx_t *lctx = ctx->lighting_context; + + if (rFrame->subpassCmdSets[0].size) { + __auto_type sets = &rFrame->subpassCmdSets[0]; + dfunc->vkFreeCommandBuffers (device->dev, lctx->cmdpool, + sets->size, sets->a); + sets->size = 0; + } + + if (!lctx->ldata || !lctx->ldata->lights.size) { + return; + } + if (!lctx->light_renderers.a[0].renderPass) { + //FIXME goes away when lighting implemented properly + return; + } + + __auto_type bufferset = QFV_AllocCommandBufferSet (1, alloca); + QFV_AllocateCommandBuffers (device, lctx->cmdpool, 0, bufferset); + VkCommandBuffer cmd = bufferset->a[0]; + QFV_duSetObjectName (device, VK_OBJECT_TYPE_COMMAND_BUFFER, + cmd, va (ctx->va_ctx, "lighting:%d", ctx->curFrame)); + + VkCommandBufferBeginInfo beginInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + }; + dfunc->vkBeginCommandBuffer (cmd, &beginInfo); + + __auto_type rp = rFrame->renderpass; + QFV_CmdBeginLabel (device, cmd, rp->name, rp->color); + + __auto_type lr = &lctx->light_renderers.a[0]; + VkRenderPassBeginInfo renderPassInfo = { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .renderArea = { {0, 0}, {lr->size, lr->size} }, + .framebuffer = lr->framebuffer, + .renderPass = lr->renderPass, + .pClearValues = lctx->qfv_renderpass->clearValues->a, + }; + __auto_type subpassContents = VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS; + if (renderPassInfo.renderPass) { + dfunc->vkCmdBeginRenderPass (cmd, &renderPassInfo, subpassContents); + //... + dfunc->vkCmdEndRenderPass (cmd); + } + QFV_CmdEndLabel (device, cmd); + dfunc->vkEndCommandBuffer (cmd); + + DARRAY_APPEND (&rFrame->subpassCmdSets[0], cmd); +} + +void +Vulkan_Lighting_CreateRenderPasses (vulkan_ctx_t *ctx) +{ + + // extents are dynamic and filled in for each light + // frame buffers are highly dynamic + __auto_type rp = QFV_RenderPass_New (ctx, "shadow", lighting_draw_maps); + QFV_RenderPass_CreateRenderPass (rp); + rp->primary_commands = 1; + rp->order = QFV_rp_shadowmap; + DARRAY_APPEND (&ctx->renderPasses, rp); + + lctx->qfv_renderpass = rp; +} +#endif +static VkDescriptorBufferInfo base_buffer_info = { + 0, 0, VK_WHOLE_SIZE +}; +static VkDescriptorImageInfo base_image_info = { + 0, 0, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL +}; +static VkWriteDescriptorSet base_buffer_write = { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, 0, + 0, 0, 1, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + 0, 0, 0 +}; +static VkWriteDescriptorSet base_attachment_write = { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, 0, + 0, 0, 1, + VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, + 0, 0, 0 +}; +static VkWriteDescriptorSet base_image_write = { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, 0, + 0, 0, 1, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + 0, 0, 0 +}; + +static void +lights_draw (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto lctx = ctx->lighting_context; + auto cmd = taskctx->cmd; + auto layout = taskctx->pipeline->layout; + + if (!lctx->scene) { + return; + } + if (lctx->scene->lights) { + update_lights (ctx); + } + + lightingframe_t *lframe = &lctx->frames.a[ctx->curFrame]; + + auto fb = &taskctx->renderpass->framebuffer; + lframe->bufferInfo[0].buffer = lframe->light_buffer; + lframe->attachInfo[0].imageView = fb->views[QFV_attachDepth]; + lframe->attachInfo[1].imageView = fb->views[QFV_attachColor]; + lframe->attachInfo[2].imageView = fb->views[QFV_attachEmission]; + lframe->attachInfo[3].imageView = fb->views[QFV_attachNormal]; + lframe->attachInfo[4].imageView = fb->views[QFV_attachPosition]; + dfunc->vkUpdateDescriptorSets (device->dev, + LIGHTING_DESCRIPTORS, + lframe->descriptors, 0, 0); + + VkDescriptorSet sets[] = { + lframe->attachWrite[0].dstSet, + lframe->bufferWrite[0].dstSet, + lframe->shadowWrite.dstSet, + }; + dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + layout, 0, 3, sets, 0, 0); + + dfunc->vkCmdDraw (cmd, 3, 1, 0, 0); +} + +static exprfunc_t lights_draw_func[] = { + { .func = lights_draw }, + {} +}; +static exprsym_t lighting_task_syms[] = { + { "lights_draw", &cexpr_function, lights_draw_func }, + {} +}; + +void +Vulkan_Lighting_Init (vulkan_ctx_t *ctx) +{ + lightingctx_t *lctx = calloc (1, sizeof (lightingctx_t)); + ctx->lighting_context = lctx; + + QFV_Render_AddTasks (ctx, lighting_task_syms); +} + +void +Vulkan_Lighting_Setup (vulkan_ctx_t *ctx) +{ + qfvPushDebug (ctx, "lighting init"); + + auto device = ctx->device; + auto dfunc = device->funcs; + auto lctx = ctx->lighting_context; + + lctx->sampler = QFV_Render_Sampler (ctx, "shadow_sampler"); + + Vulkan_Script_SetOutput (ctx, + &(qfv_output_t) { .format = VK_FORMAT_X8_D24_UNORM_PACK32 }); +#if 0 + plitem_t *rp_def = lctx->qfv_renderpass->renderpassDef; + plitem_t *rp_cfg = PL_ObjectForKey (rp_def, "renderpass_6"); + lctx->renderpass_6 = QFV_ParseRenderPass (ctx, rp_cfg, rp_def); + rp_cfg = PL_ObjectForKey (rp_def, "renderpass_4"); + lctx->renderpass_4 = QFV_ParseRenderPass (ctx, rp_cfg, rp_def); + rp_cfg = PL_ObjectForKey (rp_def, "renderpass_1"); + lctx->renderpass_1 = QFV_ParseRenderPass (ctx, rp_cfg, rp_def); +#endif + + DARRAY_INIT (&lctx->light_mats, 16); + DARRAY_INIT (&lctx->light_images, 16); + DARRAY_INIT (&lctx->light_renderers, 16); + + auto rctx = ctx->render_context; + size_t frames = rctx->frames.size; + DARRAY_INIT (&lctx->frames, frames); + DARRAY_RESIZE (&lctx->frames, frames); + lctx->frames.grow = 0; + + __auto_type lbuffers = QFV_AllocBufferSet (frames, alloca); + for (size_t i = 0; i < frames; i++) { + lbuffers->a[i] = QFV_CreateBuffer (device, sizeof (qfv_light_buffer_t), + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT + | VK_BUFFER_USAGE_TRANSFER_DST_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER, + lbuffers->a[i], + va (ctx->va_ctx, "buffer:lighting:%zd", i)); + } + VkMemoryRequirements requirements; + dfunc->vkGetBufferMemoryRequirements (device->dev, lbuffers->a[0], + &requirements); + size_t light_size = QFV_NextOffset (requirements.size, &requirements); + lctx->light_memory = QFV_AllocBufferMemory (device, lbuffers->a[0], + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + frames * light_size, 0); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, + lctx->light_memory, "memory:lighting"); + + + auto attach_mgr = QFV_Render_DSManager (ctx, "lighting_attach"); + auto lights_mgr = QFV_Render_DSManager (ctx, "lighting_lights"); + auto shadow_mgr = QFV_Render_DSManager (ctx, "lighting_shadow"); + VkDeviceSize light_offset = 0; + for (size_t i = 0; i < frames; i++) { + __auto_type lframe = &lctx->frames.a[i]; + + auto attach = QFV_DSManager_AllocSet (attach_mgr); + auto lights = QFV_DSManager_AllocSet (lights_mgr); + auto shadow = QFV_DSManager_AllocSet (shadow_mgr); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_DESCRIPTOR_SET, attach, + va (ctx->va_ctx, "lighting:attach_set:%zd", i)); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_DESCRIPTOR_SET, lights, + va (ctx->va_ctx, "lighting:lights_set:%zd", i)); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_DESCRIPTOR_SET, shadow, + va (ctx->va_ctx, "lighting:shadow_set:%zd", i)); + + lframe->light_buffer = lbuffers->a[i]; + QFV_BindBufferMemory (device, lbuffers->a[i], lctx->light_memory, + light_offset); + light_offset += light_size; + + for (int j = 0; j < LIGHTING_BUFFER_INFOS; j++) { + lframe->bufferInfo[j] = base_buffer_info; + lframe->bufferWrite[j] = base_buffer_write; + lframe->bufferWrite[j].dstSet = lights; + lframe->bufferWrite[j].dstBinding = j; + lframe->bufferWrite[j].pBufferInfo = &lframe->bufferInfo[j]; + } + for (int j = 0; j < LIGHTING_ATTACH_INFOS; j++) { + lframe->attachInfo[j] = base_image_info; + lframe->attachInfo[j].sampler = 0; + lframe->attachWrite[j] = base_attachment_write; + lframe->attachWrite[j].dstSet = attach; + lframe->attachWrite[j].dstBinding = j; + lframe->attachWrite[j].pImageInfo = &lframe->attachInfo[j]; + } + for (int j = 0; j < LIGHTING_SHADOW_INFOS; j++) { + lframe->shadowInfo[j] = base_image_info; + lframe->shadowInfo[j].sampler = lctx->sampler; + lframe->shadowInfo[j].imageView = ctx->default_black->view; + } + lframe->shadowWrite = base_image_write; + lframe->shadowWrite.dstSet = shadow; + lframe->shadowWrite.dstBinding = 0; + lframe->shadowWrite.descriptorCount = LIGHTING_SHADOW_INFOS; + lframe->shadowWrite.pImageInfo = lframe->shadowInfo; + } + qfvPopDebug (ctx); +} + +static void +clear_shadows (vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + lightingctx_t *lctx = ctx->lighting_context; + + for (size_t i = 0; i < lctx->light_renderers.size; i++) { + __auto_type lr = &lctx->light_renderers.a[i]; + dfunc->vkDestroyFramebuffer (device->dev, lr->framebuffer, 0); + dfunc->vkDestroyImageView (device->dev, lr->view, 0); + } + if (lctx->shadow_resources) { + QFV_DestroyResource (device, lctx->shadow_resources); + free (lctx->shadow_resources); + lctx->shadow_resources = 0; + } + lctx->light_images.size = 0; + lctx->light_renderers.size = 0; +} + +void +Vulkan_Lighting_Shutdown (vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + lightingctx_t *lctx = ctx->lighting_context; + + clear_shadows (ctx); + + dfunc->vkDestroyRenderPass (device->dev, lctx->renderpass_6, 0); + dfunc->vkDestroyRenderPass (device->dev, lctx->renderpass_4, 0); + dfunc->vkDestroyRenderPass (device->dev, lctx->renderpass_1, 0); + + for (size_t i = 0; i < lctx->frames.size; i++) { + lightingframe_t *lframe = &lctx->frames.a[i]; + dfunc->vkDestroyBuffer (device->dev, lframe->light_buffer, 0); + } + dfunc->vkFreeMemory (device->dev, lctx->light_memory, 0); + dfunc->vkDestroyPipeline (device->dev, lctx->pipeline, 0); + DARRAY_CLEAR (&lctx->light_mats); + DARRAY_CLEAR (&lctx->light_images); + DARRAY_CLEAR (&lctx->light_renderers); + free (lctx->frames.a); + free (lctx); +} + +static vec4f_t ref_direction = { 0, 0, 1, 0 }; + +static void +create_light_matrices (lightingctx_t *lctx) +{ + lightingdata_t *ldata = lctx->ldata; + DARRAY_RESIZE (&lctx->light_mats, ldata->lights.size); + for (size_t i = 0; i < ldata->lights.size; i++) { + light_t *light = &ldata->lights.a[i]; + mat4f_t view; + mat4f_t proj; + int mode = ST_NONE; + + if (!light->position[3]) { + mode = ST_CASCADE; + } else { + if (light->direction[3] > -0.5) { + mode = ST_CUBE; + } else { + mode = ST_PLANE; + } + } + switch (mode) { + default: + case ST_NONE: + case ST_CUBE: + mat4fidentity (view); + break; + case ST_CASCADE: + case ST_PLANE: + //FIXME will fail for -ref_direction + vec4f_t dir = light->direction; + dir[3] = 0; + mat4fquat (view, qrotf (dir, ref_direction)); + break; + } + VectorNegate (light->position, view[3]); + + switch (mode) { + case ST_NONE: + mat4fidentity (proj); + break; + case ST_CUBE: + QFV_PerspectiveTan (proj, 1, 1); + break; + case ST_CASCADE: + // dependent on view fustrum and cascade level + mat4fidentity (proj); + break; + case ST_PLANE: + QFV_PerspectiveCos (proj, -light->direction[3]); + break; + } + mmulf (lctx->light_mats.a[i], proj, view); + } +} + +static int +light_compare (const void *_li2, const void *_li1, void *_ldata) +{ + const int *li1 = _li1; + const int *li2 = _li2; + lightingdata_t *ldata = _ldata; + const light_t *l1 = &ldata->lights.a[*li1]; + const light_t *l2 = &ldata->lights.a[*li2]; + int s1 = abs ((int) l1->color[3]); + int s2 = abs ((int) l2->color[3]); + + if (s1 == s2) { + return (l1->position[3] == l2->position[3]) + && (l1->direction[3] > -0.5) == (l2->direction[3] > -0.5); + } + return s1 - s2; +} + +static VkImageView +create_view (const light_renderer_t *lr, int id, vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + + VkImageViewType type = 0; + const char *viewtype = 0; + + switch (lr->mode) { + case ST_NONE: + return 0; + case ST_PLANE: + type = VK_IMAGE_VIEW_TYPE_2D; + viewtype = "plane"; + break; + case ST_CASCADE: + type = VK_IMAGE_VIEW_TYPE_2D_ARRAY; + viewtype = "cascade"; + break; + case ST_CUBE: + type = VK_IMAGE_VIEW_TYPE_CUBE; + viewtype = "cube"; + break; + } + + VkImageViewCreateInfo createInfo = { + VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 0, + 0, + lr->image, type, VK_FORMAT_X8_D24_UNORM_PACK32, + { + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + }, + { VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, lr->layer, lr->numLayers } + }; + + VkImageView view; + dfunc->vkCreateImageView (device->dev, &createInfo, 0, &view); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE_VIEW, view, + va (ctx->va_ctx, "iview:shadowmap:%s:%d", + viewtype, id)); + (void) viewtype;//silence unused warning when vulkan debug disabled + return view; +} + +static VkFramebuffer +create_framebuffer (const light_renderer_t *lr, vulkan_ctx_t *ctx) +{ + return 0;//FIXME + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + + VkFramebuffer framebuffer; + VkFramebufferCreateInfo cInfo = { + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .renderPass = lr->renderPass, + .attachmentCount = 1, + .pAttachments = &lr->view, + .width = lr->size, + .height = lr->size, + .layers = 1, + }; + dfunc->vkCreateFramebuffer (device->dev, &cInfo, 0, &framebuffer); + return framebuffer; +} + +static void +build_shadow_maps (lightingctx_t *lctx, vulkan_ctx_t *ctx) +{ + typedef struct { + int size; + int layers; + int cube; + } mapdesc_t; + + qfv_device_t *device = ctx->device; + qfv_physdev_t *physDev = device->physDev; + int maxLayers = physDev->properties->limits.maxImageArrayLayers; + lightingdata_t *ldata = lctx->ldata; + light_t *lights = ldata->lights.a; + int numLights = ldata->lights.size; + int size = -1; + int numLayers = 0; + int totalLayers = 0; + int *imageMap = alloca (numLights * sizeof (int)); + int *lightMap = alloca (numLights * sizeof (int)); + int numMaps = 0; + mapdesc_t *maps = alloca (numLights * sizeof (mapdesc_t)); + + for (int i = 0; i < numLights; i++) { + lightMap[i] = i; + } + heapsort_r (lightMap, numLights, sizeof (int), light_compare, ldata); + + DARRAY_RESIZE (&lctx->light_renderers, numLights); + for (int i = 0; i < numLights; i++) { + int layers = 1; + int li = lightMap[i]; + __auto_type lr = &lctx->light_renderers.a[li]; + *lr = (light_renderer_t) {}; + + if (!lights[li].position[3]) { + if (!VectorIsZero (lights[li].direction)) { + lr->mode = ST_CASCADE; + } + } else { + if (lights[li].direction[3] > -0.5) { + lr->mode = ST_CUBE; + } else { + lr->mode = ST_PLANE; + } + } + if (lr->mode == ST_CASCADE || lr->mode == ST_NONE) { + // cascade shadows will be handled separately, and "none" has no + // shadow map at all + imageMap[li] = -1; + continue; + } + if (lr->mode == ST_CUBE) { + layers = 6; + } + if (size != abs ((int) lights[li].color[3]) + || numLayers + layers > maxLayers) { + if (numLayers) { + maps[numMaps++] = (mapdesc_t) { + .size = size, + .layers = numLayers, + .cube = 1, + }; + numLayers = 0; + } + size = abs ((int) lights[li].color[3]); + } + imageMap[li] = numMaps; + lr->size = size; + lr->layer = numLayers; + lr->numLayers = layers; + numLayers += layers; + totalLayers += layers; + } + if (numLayers) { + maps[numMaps++] = (mapdesc_t) { + .size = size, + .layers = numLayers, + .cube = 1, + }; + } + + numLayers = 0; + size = 1024; + for (int i = 0; i < numLights; i++) { + int layers = 4; + int li = lightMap[i]; + __auto_type lr = &lctx->light_renderers.a[li]; + + if (lr->mode != ST_CASCADE) { + continue; + } + if (numLayers + layers > maxLayers) { + maps[numMaps++] = (mapdesc_t) { + .size = size, + .layers = numLayers, + .cube = 0, + }; + numLayers = 0; + } + imageMap[li] = numMaps; + lr->size = size; + lr->layer = numLayers; + lr->numLayers = layers; + numLayers += layers; + totalLayers += layers; + } + if (numLayers) { + maps[numMaps++] = (mapdesc_t) { + .size = size, + .layers = numLayers, + .cube = 0, + }; + } + + if (numMaps) { + qfv_resource_t *shad = calloc (1, sizeof (qfv_resource_t) + + numMaps * sizeof (qfv_resobj_t)); + lctx->shadow_resources = shad; + *shad = (qfv_resource_t) { + .name = "shadow", + .va_ctx = ctx->va_ctx, + .memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + .num_objects = numMaps, + .objects = (qfv_resobj_t *) &shad[1], + }; + for (int i = 0; i < numMaps; i++) { + int cube = maps[i].layers < 6 ? 0 : maps[i].cube; + shad->objects[i] = (qfv_resobj_t) { + .name = "map", + .type = qfv_res_image, + .image = { + .flags = cube ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0, + .type = VK_IMAGE_TYPE_2D, + .format = VK_FORMAT_X8_D24_UNORM_PACK32, + .extent = { maps[i].size, maps[i].size, 1 }, + .num_mipmaps = 1, + .num_layers = maps[i].layers, + .samples = VK_SAMPLE_COUNT_1_BIT, + .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT + | VK_IMAGE_USAGE_SAMPLED_BIT, + }, + }; + } + QFV_CreateResource (device, shad); + for (int i = 0; i < numMaps; i++) { + DARRAY_APPEND (&lctx->light_images, shad->objects[i].image.image); + } + } + + for (int i = 0; i < numLights; i++) { + int li = lightMap[i]; + __auto_type lr = &lctx->light_renderers.a[li]; + + if (imageMap[li] == -1) { + continue; + } + + switch (lr->numLayers) { + case 6: + lr->renderPass = lctx->renderpass_6; + break; + case 4: + lr->renderPass = lctx->renderpass_4; + break; + case 1: + lr->renderPass = lctx->renderpass_1; + break; + default: + Sys_Error ("build_shadow_maps: invalid light layer count: %u", + lr->numLayers); + } + lr->image = lctx->light_images.a[imageMap[li]]; + lr->view = create_view (lr, li, ctx); + lr->framebuffer = create_framebuffer(lr, ctx); + } + Sys_MaskPrintf (SYS_vulkan, + "shadow maps: %d layers in %zd images: %"PRId64"\n", + totalLayers, lctx->light_images.size, + lctx->shadow_resources->size); +} + +void +Vulkan_LoadLights (scene_t *scene, vulkan_ctx_t *ctx) +{ + lightingctx_t *lctx = ctx->lighting_context; + + lctx->scene = scene; + lctx->ldata = scene ? scene->lights : 0; + + clear_shadows (ctx); + + if (lctx->ldata && lctx->ldata->lights.size) { + build_shadow_maps (lctx, ctx); + create_light_matrices (lctx); + } +} diff --git a/libs/video/renderer/vulkan/vulkan_lightmap.c b/libs/video/renderer/vulkan/vulkan_lightmap.c new file mode 100644 index 000000000..9eecc8616 --- /dev/null +++ b/libs/video/renderer/vulkan/vulkan_lightmap.c @@ -0,0 +1,290 @@ +/* + vulkan_lightmap.c + + surface-related refresh code + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Joseph Carter + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include + +#include "QF/cvar.h" +#include "QF/render.h" +#include "QF/sys.h" +#include "QF/Vulkan/qf_bsp.h" +#include "QF/Vulkan/qf_lightmap.h" +#include "QF/Vulkan/qf_main.h" +#include "QF/Vulkan/scrap.h" + +#include "QF/scene/entity.h" + +#include "compat.h" +#include "r_internal.h" +#include "vid_vulkan.h" + +#define LUXEL_SIZE 4 + +static inline void +add_dynamic_lights (const transform_t *transform, msurface_t *surf, + float *block) +{ + unsigned lnum; + int sd, td; + float dist, rad, minlight; + vec3_t impact, local, lightorigin; + vec4f_t entorigin = { 0, 0, 0, 1 }; + int smax, tmax; + int s, t; + mtexinfo_t *tex; + plane_t *plane; + + smax = (surf->extents[0] >> 4) + 1; + tmax = (surf->extents[1] >> 4) + 1; + tex = surf->texinfo; + plane = surf->plane; + + if (transform) { + //FIXME give world entity a transform + entorigin = Transform_GetWorldPosition (transform); + } + + for (lnum = 0; lnum < r_maxdlights; lnum++) { + if (!(surf->dlightbits[lnum / 32] & (1 << (lnum % 32)))) + continue; // not lit by this light + + dlight_t *light = &r_dlights[lnum]; + + VectorSubtract (light->origin, entorigin, lightorigin); + rad = light->radius; + dist = DotProduct (lightorigin, plane->normal) - plane->dist; + rad -= fabs (dist); + + minlight = light->minlight; + if (rad < minlight) { + continue; + } + VectorMultSub (light->origin, dist, plane->normal, impact); + + local[0] = DotProduct (impact, tex->vecs[0]) + tex->vecs[0][3]; + local[1] = DotProduct (impact, tex->vecs[1]) + tex->vecs[1][3]; + + local[0] -= surf->texturemins[0]; + local[1] -= surf->texturemins[1]; + + for (t = 0; t < tmax; t++) { + td = local[1] - t * 16; + if (td < 0) { + td = -td; + } + for (s = 0; s < smax; s++) { + sd = local[0] - s * 16; + if (sd < 0) { + sd = -sd; + } + if (sd > td) { + dist = sd + (td >> 1); + } else { + dist = td + (sd >> 1); + } + if (dist < minlight) { + float *out = block + (t * smax + s) * LUXEL_SIZE; + float l = (rad - dist); + VectorMultAdd (out, l, light->color, out); + out[3] = 1; + out += LUXEL_SIZE; + } + } + } + } +} + +void +Vulkan_BuildLightMap (const transform_t *transform, mod_brush_t *brush, + msurface_t *surf, vulkan_ctx_t *ctx) +{ + bspctx_t *bctx = ctx->bsp_context; + int smax, tmax, size; + unsigned scale; + int i; + float *out, *block; + + surf->cached_dlight = (surf->dlightframe == r_framecount); + + smax = (surf->extents[0] >> 4) + 1; + tmax = (surf->extents[1] >> 4) + 1; + size = smax * tmax * LUXEL_SIZE; + + block = QFV_SubpicBatch (surf->lightpic, bctx->light_stage); + + // set to full bright if no light data + if (!brush->lightdata) { + out = block; + while (size-- > 0) { + *out++ = 1; + } + return; + } + + // clear to no light + memset (block, 0, size * sizeof(float)); + + // add all the lightmaps + if (surf->samples) { + byte *lightmap; + + lightmap = surf->samples; + for (int maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; + maps++) { + scale = d_lightstylevalue[surf->styles[maps]]; + surf->cached_light[maps] = scale; // 8.8 fraction + out = block; + for (i = 0; i < smax * tmax; i++) { + *out++ += *lightmap++ * scale / 65536.0; + *out++ += *lightmap++ * scale / 65536.0; + *out++ += *lightmap++ * scale / 65536.0; + out++; + } + } + } + // add all the dynamic lights + if (surf->dlightframe == r_framecount) { + add_dynamic_lights (transform, surf, block); + } +} + +void +Vulkan_CalcLightmaps (vulkan_ctx_t *ctx) +{ +/* int i; + + for (i = 0; i < MAX_LIGHTMAPS; i++) { + if (!gl_lightmap_polys[i]) + continue; + if (gl_lightmap_modified[i]) { + qfglBindTexture (GL_TEXTURE_2D, gl_lightmap_textures + i); + GL_UploadLightmap (i); + gl_lightmap_modified[i] = false; + } + }*/ +} + +static void +vulkan_create_surf_lightmap (msurface_t *surf, vulkan_ctx_t *ctx) +{ + bspctx_t *bctx = ctx->bsp_context; + int smax, tmax; + + smax = (surf->extents[0] >> 4) + 1; + tmax = (surf->extents[1] >> 4) + 1; + + surf->lightpic = QFV_ScrapSubpic (bctx->light_scrap, smax, tmax); + if (!surf->lightpic) { + Sys_Error ("FIXME taniwha is being lazy"); + } +} + +/* + GL_BuildLightmaps + + Builds the lightmap texture with all the surfaces from all brush models +*/ +void +Vulkan_BuildLightmaps (model_t **models, int num_models, vulkan_ctx_t *ctx) +{ + bspctx_t *bctx = ctx->bsp_context; + int i, j; + model_t *m; + mod_brush_t *brush; + + QFV_ScrapClear (bctx->light_scrap); + + r_framecount = 1; // no dlightcache + + for (j = 1; j < num_models; j++) { + m = models[j]; + if (!m) + break; + if (m->path[0] == '*' || m->type != mod_brush) { + // sub model surfaces are processed as part of the main model + continue; + } + brush = &m->brush; + // non-bsp models don't have surfaces. + for (i = 0; i < brush->numsurfaces; i++) { + msurface_t *surf = brush->surfaces + i; + surf->lightpic = 0; // paranoia + if (surf->flags & SURF_DRAWTURB) { + continue; + } + if (surf->flags & SURF_DRAWSKY) { + continue; + } + vulkan_create_surf_lightmap (surf, ctx); + } + } + + for (j = 1; j < num_models; j++) { + m = models[j]; + if (!m) { + break; + } + if (m->path[0] == '*' || m->type != mod_brush) { + // sub model surfaces are processed as part of the main model + continue; + } + brush = &m->brush; + // non-bsp models don't have surfaces. + for (i = 0; i < brush->numsurfaces; i++) { + msurface_t *surf = brush->surfaces + i; + if (surf->lightpic) { + Vulkan_BuildLightMap (0, brush, surf, ctx); + } + } + } +} + +VkImageView +Vulkan_LightmapImageView (vulkan_ctx_t *ctx) +{ + bspctx_t *bctx = ctx->bsp_context; + return QFV_ScrapImageView (bctx->light_scrap); +} + +void +Vulkan_FlushLightmaps (vulkan_ctx_t *ctx) +{ + bspctx_t *bctx = ctx->bsp_context; + QFV_ScrapFlush (bctx->light_scrap); +} diff --git a/libs/video/renderer/vulkan/vulkan_main.c b/libs/video/renderer/vulkan/vulkan_main.c new file mode 100644 index 000000000..618da813a --- /dev/null +++ b/libs/video/renderer/vulkan/vulkan_main.c @@ -0,0 +1,90 @@ +/* + vulkan_main.c + + Vulkan rendering + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/1/19 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include "string.h" +#endif +#ifdef HAVE_STRINGS_H +# include "strings.h" +#endif + +#include "QF/cmd.h" +#include "QF/cvar.h" +#include "QF/image.h" +#include "QF/render.h" +#include "QF/screen.h" +#include "QF/sys.h" + +#include "QF/scene/entity.h" + +#include "QF/Vulkan/qf_alias.h" +#include "QF/Vulkan/qf_bsp.h" +#include "QF/Vulkan/qf_compose.h" +#include "QF/Vulkan/qf_iqm.h" +#include "QF/Vulkan/qf_lighting.h" +#include "QF/Vulkan/qf_lightmap.h" +#include "QF/Vulkan/qf_main.h" +#include "QF/Vulkan/qf_matrices.h" +#include "QF/Vulkan/qf_output.h" +#include "QF/Vulkan/qf_particles.h" +#include "QF/Vulkan/qf_scene.h" +#include "QF/Vulkan/qf_sprite.h" +#include "QF/Vulkan/qf_translucent.h" +#include "QF/Vulkan/qf_vid.h" +#include "QF/Vulkan/swapchain.h" + +#include "mod_internal.h" +#include "r_internal.h" +#include "vid_vulkan.h" + +void +Vulkan_NewScene (scene_t *scene, vulkan_ctx_t *ctx) +{ + int i; + + for (i = 0; i < 256; i++) { + d_lightstylevalue[i] = 264; // normal light value + } + + r_refdef.worldmodel = scene->worldmodel; + EntQueue_Clear (r_ent_queue); + + // Force a vis update + R_MarkLeaves (0, 0, 0, 0); + + R_ClearParticles (); + Vulkan_RegisterTextures (scene->models, scene->num_models, ctx); + //Vulkan_BuildLightmaps (scene->models, scene->num_models, ctx); + Vulkan_BuildDisplayLists (scene->models, scene->num_models, ctx); + Vulkan_LoadLights (scene, ctx); +} diff --git a/libs/video/renderer/vulkan/vulkan_matrices.c b/libs/video/renderer/vulkan/vulkan_matrices.c new file mode 100644 index 000000000..1555f0573 --- /dev/null +++ b/libs/video/renderer/vulkan/vulkan_matrices.c @@ -0,0 +1,336 @@ +/* + vid_common_vulkan.c + + Common Vulkan video driver functions + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#ifdef HAVE_MATH_H +# include +#endif + +#include "QF/cvar.h" +#include "QF/sys.h" +#include "QF/va.h" +#include "QF/Vulkan/qf_matrices.h" +#include "QF/Vulkan/barrier.h" +#include "QF/Vulkan/buffer.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/descriptor.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/dsmanager.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/projection.h" +#include "QF/Vulkan/render.h" +#include "QF/Vulkan/resource.h" +#include "QF/Vulkan/staging.h" + +#include "r_internal.h" +#include "vid_vulkan.h" + +//FIXME? The box rotations (in particular top/bottom) for vulkan are not +//compatible with the other renderers, so need a local version +static mat4f_t box_rotations[] = { + [BOX_FRONT] = { + { 1, 0, 0, 0}, + { 0, 1, 0, 0}, + { 0, 0, 1, 0}, + { 0, 0, 0, 1} + }, + [BOX_RIGHT] = { + { 0,-1, 0, 0}, + { 1, 0, 0, 0}, + { 0, 0, 1, 0}, + { 0, 0, 0, 1} + }, + [BOX_BEHIND] = { + {-1, 0, 0, 0}, + { 0,-1, 0, 0}, + { 0, 0, 1, 0}, + { 0, 0, 0, 1} + }, + [BOX_LEFT] = { + { 0, 1, 0, 0}, + {-1, 0, 0, 0}, + { 0, 0, 1, 0}, + { 0, 0, 0, 1} + }, + [BOX_BOTTOM] = { + { 0, 0, 1, 0}, + { 0, 1, 0, 0}, + {-1, 0, 0, 0}, + { 0, 0, 0, 1} + }, + [BOX_TOP] = { + { 0, 0,-1, 0}, + { 0, 1, 0, 0}, + { 1, 0, 0, 0}, + { 0, 0, 0, 1} + }, +}; + +// Quake's world is z-up, x-forward, y-left, but Vulkan's world is +// z-forward, x-right, y-down. +static mat4f_t z_up = { + { 0, 0, 1, 0}, + {-1, 0, 0, 0}, + { 0,-1, 0, 0}, + { 0, 0, 0, 1}, +}; + +static void +setup_view (vulkan_ctx_t *ctx) +{ + //FIXME this should check for cube map rather than fisheye + if (scr_fisheye) { + __auto_type mctx = ctx->matrix_context; + QFV_PerspectiveTan (mctx->matrices.Projection3d, 1, 1); + mat4f_t views[6]; + for (int i = 0; i < 6; i++) { + mat4f_t rotinv; + mat4ftranspose (rotinv, box_rotations[i]); + mmulf (views[i], rotinv, r_refdef.camera_inverse); + mmulf (views[i], z_up, views[i]); + } + Vulkan_SetViewMatrices (ctx, views, 6); + } else { + mat4f_t view; + mmulf (view, z_up, r_refdef.camera_inverse); + Vulkan_SetViewMatrices (ctx, &view, 1); + } +} + +static void +setup_sky (vulkan_ctx_t *ctx) +{ + __auto_type mctx = ctx->matrix_context; + vec4f_t q; + mat4f_t m; + float blend; + mat4f_t mat; + + while (vr_data.realtime - mctx->sky_time > 1) { + mctx->sky_rotation[0] = mctx->sky_rotation[1]; + mctx->sky_rotation[1] = qmulf (mctx->sky_velocity, + mctx->sky_rotation[0]); + mctx->sky_time += 1; + } + blend = bound (0, (vr_data.realtime - mctx->sky_time), 1); + + q = Blend (mctx->sky_rotation[0], mctx->sky_rotation[1], blend); + q = normalf (qmulf (mctx->sky_fix, q)); + mat4fidentity (mat); + VectorNegate (r_refdef.frame.position, mat[3]); + mat4fquat (m, q); + mmulf (mat, m, mat); + Vulkan_SetSkyMatrix (ctx, mat); +} + +void +Vulkan_SetViewMatrices (vulkan_ctx_t *ctx, mat4f_t views[], int count) +{ + __auto_type mctx = ctx->matrix_context; + + memcpy (mctx->matrices.View, views, count * sizeof (mat4f_t)); + mctx->dirty = mctx->frames.size; +} + +void +Vulkan_SetSkyMatrix (vulkan_ctx_t *ctx, mat4f_t sky) +{ + __auto_type mctx = ctx->matrix_context; + + if (memcmp (mctx->matrices.Sky, sky, sizeof (mat4f_t))) { + memcpy (mctx->matrices.Sky, sky, sizeof (mat4f_t)); + mctx->dirty = mctx->frames.size; + } +} + +static void +update_matrices (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto mctx = ctx->matrix_context; + auto mframe = &mctx->frames.a[ctx->curFrame]; + + setup_view (ctx); + setup_sky (ctx); + + if (mctx->dirty <= 0) { + mctx->dirty = 0; + return; + } + + mctx->dirty--; + + qfv_packet_t *packet = QFV_PacketAcquire (mctx->stage); + qfv_matrix_buffer_t *m = QFV_PacketExtend (packet, sizeof (*m)); + *m = mctx->matrices; + + qfv_bufferbarrier_t bb = bufferBarriers[qfv_BB_Unknown_to_TransferWrite]; bb.barrier.buffer = mframe->buffer; + bb.barrier.size = packet->length; + + dfunc->vkCmdPipelineBarrier (packet->cmd, bb.srcStages, bb.dstStages, + 0, 0, 0, 1, &bb.barrier, 0, 0); + + VkBufferCopy copy_region = { packet->offset, 0, packet->length }; + dfunc->vkCmdCopyBuffer (packet->cmd, mctx->stage->buffer, + mframe->buffer, 1, ©_region); + + bb = bufferBarriers[qfv_LT_TransferDst_to_ShaderReadOnly]; + bb.barrier.buffer = mframe->buffer; + bb.barrier.size = packet->length; + + dfunc->vkCmdPipelineBarrier (packet->cmd, bb.srcStages, bb.dstStages, + 0, 0, 0, 1, &bb.barrier, 0, 0); + + QFV_PacketSubmit (packet); +} + +static exprfunc_t update_matrices_func[] = { + { .func = update_matrices }, + {} +}; +static exprsym_t matrix_task_syms[] = { + { "update_matrices", &cexpr_function, update_matrices_func }, + {} +}; + +void +Vulkan_Matrix_Init (vulkan_ctx_t *ctx) +{ + QFV_Render_AddTasks (ctx, matrix_task_syms); + + matrixctx_t *mctx = calloc (1, sizeof (matrixctx_t)); + ctx->matrix_context = mctx; +} + +void +Vulkan_Matrix_Setup (vulkan_ctx_t *ctx) +{ + qfvPushDebug (ctx, "matrix init"); + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + + auto mctx = ctx->matrix_context; + auto rctx = ctx->render_context; + size_t frames = rctx->frames.size; + DARRAY_INIT (&mctx->frames, frames); + DARRAY_RESIZE (&mctx->frames, frames); + mctx->frames.grow = 0; + + mctx->resource = malloc (sizeof (qfv_resource_t) + + sizeof (qfv_resobj_t[frames])); // buffers + auto buffers = (qfv_resobj_t *) &mctx->resource[1]; + *mctx->resource = (qfv_resource_t) { + .name = "matrix", + .va_ctx = ctx->va_ctx, + .memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + .num_objects = frames, + .objects = buffers, + }; + for (size_t i = 0; i < frames; i++) { + buffers[i] = (qfv_resobj_t) { + .name = va (ctx->va_ctx, "%zd", i), + .type = qfv_res_buffer, + .buffer = { + .size = sizeof (qfv_matrix_buffer_t), + .usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT + | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + }, + }; + } + QFV_CreateResource (device, mctx->resource); + + auto dsmanager = QFV_Render_DSManager (ctx, "matrix_set"); + for (size_t i = 0; i < frames; i++) { + auto mframe = &mctx->frames.a[i]; + mframe->buffer = buffers[i].buffer.buffer; + mframe->descriptors = QFV_DSManager_AllocSet (dsmanager); + VkDescriptorBufferInfo bufferInfo = { + mframe->buffer, 0, VK_WHOLE_SIZE + }; + VkWriteDescriptorSet write[] = { + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, + mframe->descriptors, 0, 0, 1, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + 0, &bufferInfo, 0 }, + }; + dfunc->vkUpdateDescriptorSets (device->dev, 1, write, 0, 0); + } + + mctx->sky_fix = (vec4f_t) { 0, 0, 1, 1 } * sqrtf (0.5); + mctx->sky_rotation[0] = (vec4f_t) { 0, 0, 0, 1}; + mctx->sky_rotation[1] = mctx->sky_rotation[0]; + mctx->sky_velocity = (vec4f_t) { }; + mctx->sky_velocity = qexpf (mctx->sky_velocity); + mctx->sky_time = vr_data.realtime; + + mat4fidentity (mctx->matrices.Projection3d); + for (int i = 0; i < 6; i++) { + mat4fidentity (mctx->matrices.View[i]); + } + mat4fidentity (mctx->matrices.Sky); + mat4fidentity (mctx->matrices.Projection2d); + + mctx->dirty = mctx->frames.size; + + mctx->stage = QFV_CreateStagingBuffer (device, "matrix", + frames * sizeof (qfv_matrix_buffer_t), + ctx->cmdpool); + + qfvPopDebug (ctx); +} + +void +Vulkan_Matrix_Shutdown (vulkan_ctx_t *ctx) +{ + qfvPushDebug (ctx, "matrix shutdown"); + auto device = ctx->device; + auto mctx = ctx->matrix_context; + + QFV_DestroyStagingBuffer (mctx->stage); + QFV_DestroyResource (device, mctx->resource); + + qfvPopDebug (ctx); +} + +VkDescriptorSet +Vulkan_Matrix_Descriptors (vulkan_ctx_t *ctx, int frame) +{ + auto mctx = ctx->matrix_context; + return mctx->frames.a[frame].descriptors; +} diff --git a/libs/video/renderer/vulkan/vulkan_output.c b/libs/video/renderer/vulkan/vulkan_output.c new file mode 100644 index 000000000..5306c3797 --- /dev/null +++ b/libs/video/renderer/vulkan/vulkan_output.c @@ -0,0 +1,365 @@ +/* + vulkan_main.c + + Vulkan output + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/11/21 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include "string.h" +#endif +#ifdef HAVE_STRINGS_H +# include "strings.h" +#endif + +#include "QF/cvar.h" +#include "QF/render.h" +#include "QF/sys.h" +#include "QF/va.h" + +#include "QF/Vulkan/qf_draw.h" +#include "QF/Vulkan/qf_matrices.h" +#include "QF/Vulkan/qf_output.h" +#include "QF/Vulkan/qf_vid.h" +#include "QF/Vulkan/capture.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/descriptor.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/dsmanager.h" +#include "QF/Vulkan/image.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/render.h" +#include "QF/Vulkan/swapchain.h" + +#include "r_local.h" +#include "r_internal.h" +#include "vid_vulkan.h" +#include "vkparse.h"//FIXME + +static void +acquire_output (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto rctx = ctx->render_context; + auto frame = &rctx->frames.a[ctx->curFrame]; + auto octx = ctx->output_context; + auto sc = ctx->swapchain; + + uint32_t imageIndex = 0; + while (!QFV_AcquireNextImage (sc, frame->imageAvailableSemaphore, + 0, &imageIndex)) { + QFV_DeviceWaitIdle (device); + for (uint32_t i = 0; i < sc->imageViews->size; i++) { + dfunc->vkDestroyFramebuffer (device->dev, + octx->framebuffers[i], 0); + } + octx->framebuffers = 0; + Vulkan_CreateSwapchain (ctx); + sc = ctx->swapchain; + QFV_Capture_Renew (ctx); + + dfunc->vkDestroySemaphore (device->dev, frame->imageAvailableSemaphore, + 0); + frame->imageAvailableSemaphore = QFV_CreateSemaphore (device); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_SEMAPHORE, + frame->imageAvailableSemaphore, + va (ctx->va_ctx, "sc image:%d", ctx->curFrame)); + } + + //FIXME clean this up + auto step = QFV_GetStep (params[0], ctx->render_context->job); + auto render = step->render; + auto rp = &render->renderpasses[0]; + if (!octx->framebuffers) { + uint32_t count = ctx->swapchain->imageViews->size; + octx->framebuffers = malloc (sizeof (VkFramebuffer [count])); + for (uint32_t i = 0; i < count; i++) { + rp->beginInfo.framebuffer = 0; + //FIXME come up with a better mechanism + ctx->swapImageIndex = i; + QFV_CreateFramebuffer (ctx, rp); + octx->framebuffers[i] = rp->beginInfo.framebuffer; + QFV_duSetObjectName (device, VK_OBJECT_TYPE_FRAMEBUFFER, + octx->framebuffers[i], + va (ctx->va_ctx, "sc fb:%d", i)); + } + rp->beginInfo.renderArea.extent = sc->extent; + for (uint32_t i = 0; i < rp->subpass_count; i++) { + auto sp = &rp->subpasses[i]; + for (uint32_t j = 0; j < sp->pipeline_count; j++) { + auto pl = &sp->pipelines[j]; + pl->viewport.width = sc->extent.width; + pl->viewport.height = sc->extent.height; + pl->scissor.extent = sc->extent; + } + } + } + ctx->swapImageIndex = imageIndex; + rp->beginInfo.framebuffer = octx->framebuffers[imageIndex]; + for (uint32_t i = 0; i < rp->subpass_count; i++) { + auto sp = &rp->subpasses[i]; + sp->inherit.framebuffer = rp->beginInfo.framebuffer; + } +} + +static void +update_input (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto octx = ctx->output_context; + auto oframe = &octx->frames.a[ctx->curFrame]; + auto input = QFV_GetStep (params[0], ctx->render_context->job); + + if (oframe->input == input->render->active->output) { + return; + } + oframe->input = input->render->active->output; + + VkDescriptorImageInfo imageInfo = { + octx->sampler, oframe->input, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }; + VkWriteDescriptorSet write[] = { + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, + oframe->set, 0, 0, 1, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + &imageInfo, 0, 0 } + }; + dfunc->vkUpdateDescriptorSets (device->dev, 1, write, 0, 0); +} + +static void +output_select_pipeline (const exprval_t **params, exprval_t *result, + exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + auto output = QFV_GetStep (params[0], ctx->render_context->job); + // FIXME the output render pass has only one subpass + auto sp = output->render->active->subpasses; + + // FIXME the output render pass pipelines are in the order + // output, waterwarp, fisheye, followed by any additional pipelines + if (scr_fisheye) { + sp->pipelines[0].disabled = true; + sp->pipelines[1].disabled = true; + sp->pipelines[2].disabled = false; + } else if (r_dowarp) { + sp->pipelines[0].disabled = true; + sp->pipelines[1].disabled = false; + sp->pipelines[2].disabled = true; + } else { + sp->pipelines[0].disabled = false; + sp->pipelines[1].disabled = true; + sp->pipelines[2].disabled = true; + } +} + +static void +output_select_renderpass (const exprval_t **params, exprval_t *result, + exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + auto main = QFV_GetStep (params[0], ctx->render_context->job); + // FIXME the main render step has only two renderpasses + auto render = main->render; + + if (scr_fisheye) { + render->active = &render->renderpasses[1]; + } else { + render->active = &render->renderpasses[0]; + } +} + +static void +output_draw (qfv_taskctx_t *taskctx, + int num_push_constants, qfv_push_constants_t *push_constants) +{ + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto octx = ctx->output_context; + auto oframe = &octx->frames.a[ctx->curFrame]; + auto layout = taskctx->pipeline->layout; + auto cmd = taskctx->cmd; + + VkDescriptorSet set[] = { + Vulkan_Matrix_Descriptors (ctx, ctx->curFrame), + oframe->set, + }; + dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + layout, 0, 2, set, 0, 0); + if (num_push_constants) { + QFV_PushConstants (device, cmd, layout, + num_push_constants, push_constants); + } + + dfunc->vkCmdDraw (cmd, 3, 1, 0, 0); +} + +static void +output_draw_flat (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + output_draw (taskctx, 0, 0); +} + +static void +output_draw_waterwarp (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + float time = vr_data.realtime; + qfv_push_constants_t push_constants[] = { + { VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof (float), &time }, + }; + output_draw (taskctx, 1, push_constants); +} + +static void +output_draw_fisheye (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + float width = r_refdef.vrect.width; + float height = r_refdef.vrect.height; + + float ffov = scr_ffov * M_PI / 360; + float aspect = height / width; + qfv_push_constants_t push_constants[] = { + { VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof (float), &ffov }, + { VK_SHADER_STAGE_FRAGMENT_BIT, 4, sizeof (float), &aspect }, + }; + output_draw (taskctx, 2, push_constants); +} + +static exprtype_t *stepref_param[] = { + &cexpr_string, +}; +static exprfunc_t acquire_output_func[] = { + { .func = acquire_output, .num_params = 1, .param_types = stepref_param }, + {} +}; +static exprfunc_t update_input_func[] = { + { .func = update_input, .num_params = 1, .param_types = stepref_param }, + {} +}; +static exprfunc_t output_select_pipeline_func[] = { + { .func = output_select_pipeline, + .num_params = 1, .param_types = stepref_param }, + {} +}; +static exprfunc_t output_select_renderpass_func[] = { + { .func = output_select_renderpass, + .num_params = 1, .param_types = stepref_param }, + {} +}; +static exprfunc_t output_draw_flat_func[] = { + { .func = output_draw_flat }, + {} +}; +static exprfunc_t output_draw_waterwarp_func[] = { + { .func = output_draw_waterwarp }, + {} +}; +static exprfunc_t output_draw_fisheye_func[] = { + { .func = output_draw_fisheye }, + {} +}; +static exprsym_t output_task_syms[] = { + { "acquire_output", &cexpr_function, acquire_output_func }, + { "update_input", &cexpr_function, update_input_func }, + { "output_select_pipeline", &cexpr_function, output_select_pipeline_func }, + { "output_select_renderpass", &cexpr_function, + output_select_renderpass_func }, + { "output_draw_flat", &cexpr_function, output_draw_flat_func }, + { "output_draw_waterwarp", &cexpr_function, output_draw_waterwarp_func }, + { "output_draw_fisheye", &cexpr_function, output_draw_fisheye_func }, + {} +}; + +void +Vulkan_Output_Init (vulkan_ctx_t *ctx) +{ + outputctx_t *octx = calloc (1, sizeof (outputctx_t)); + ctx->output_context = octx; + + QFV_Render_AddTasks (ctx, output_task_syms); +} + +void +Vulkan_Output_Setup (vulkan_ctx_t *ctx) +{ + qfvPushDebug (ctx, "output init"); + + auto octx = ctx->output_context; + + auto rctx = ctx->render_context; + size_t frames = rctx->frames.size; + DARRAY_INIT (&octx->frames, frames); + DARRAY_RESIZE (&octx->frames, frames); + octx->frames.grow = 0; + + octx->sampler = QFV_Render_Sampler (ctx, "linear"); + + auto dsmanager = QFV_Render_DSManager (ctx, "output_set"); + + for (size_t i = 0; i < frames; i++) { + auto oframe = &octx->frames.a[i]; + oframe->input = 0; + oframe->set = QFV_DSManager_AllocSet (dsmanager); + } + + qfvPopDebug (ctx); +} + +void +Vulkan_Output_Shutdown (vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + outputctx_t *octx = ctx->output_context; + + for (uint32_t i = 0; i < ctx->swapchain->imageViews->size; i++) { + dfunc->vkDestroyFramebuffer (device->dev, octx->framebuffers[i], 0); + } + free (octx->framebuffers); + auto step = QFV_FindStep ("output", ctx->render_context->job); + auto render = step->render; + auto rp = &render->renderpasses[0]; + rp->beginInfo.framebuffer = 0; + + free (octx->frames.a); + free (octx); +} diff --git a/libs/video/renderer/vulkan/vulkan_palette.c b/libs/video/renderer/vulkan/vulkan_palette.c new file mode 100644 index 000000000..4d3712d3e --- /dev/null +++ b/libs/video/renderer/vulkan/vulkan_palette.c @@ -0,0 +1,108 @@ +/* + vulkan_palette.c + + Vulkan palette image + + Copyright (C) 2022 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#ifdef HAVE_MATH_H +# include +#endif + +#include "QF/sys.h" +#include "QF/va.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/render.h" +#include "QF/Vulkan/qf_palette.h" +#include "QF/Vulkan/qf_texture.h" + +#include "vid_vulkan.h" + +void +Vulkan_Palette_Update (vulkan_ctx_t *ctx, const byte *palette) +{ + palettectx_t *pctx = ctx->palette_context; + tex_t tex = { + .width = 16, + .height = 16, + .format = tex_rgb, + .loaded = 1, + .data = (byte *) palette, + }; + Vulkan_UpdateTex (ctx, pctx->palette, &tex, 0, 0, 0, 0); +} + +void +Vulkan_Palette_Init (vulkan_ctx_t *ctx, const byte *palette) +{ + qfvPushDebug (ctx, "palette init"); + + palettectx_t *pctx = calloc (1, sizeof (palettectx_t)); + ctx->palette_context = pctx; + + pctx->sampler = QFV_Render_Sampler (ctx, "palette_sampler"); + + tex_t tex = { + .width = 16, + .height = 16, + .format = tex_rgb, + .loaded = 1, + .data = (byte *) palette, + }; + pctx->palette = Vulkan_LoadTex (ctx, &tex, 0, "palette"); + pctx->descriptor = Vulkan_CreateCombinedImageSampler (ctx, + pctx->palette->view, + pctx->sampler); + + qfvPopDebug (ctx); +} + +void +Vulkan_Palette_Shutdown (vulkan_ctx_t *ctx) +{ + qfvPushDebug (ctx, "palette shutdown"); + + __auto_type pctx = ctx->palette_context; + + Vulkan_UnloadTex (ctx, pctx->palette); + free (pctx); + + qfvPopDebug (ctx); +} + +VkDescriptorSet +Vulkan_Palette_Descriptor (struct vulkan_ctx_s *ctx) +{ + __auto_type pctx = ctx->palette_context; + return pctx->descriptor; +} diff --git a/libs/video/renderer/vulkan/vulkan_particles.c b/libs/video/renderer/vulkan/vulkan_particles.c new file mode 100644 index 000000000..90e002c61 --- /dev/null +++ b/libs/video/renderer/vulkan/vulkan_particles.c @@ -0,0 +1,497 @@ +/* + vulkan_particles.c + + Quake specific Vulkan particle manager + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_MATH_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/cvar.h" +#include "QF/render.h" +#include "QF/va.h" + +#include "QF/Vulkan/buffer.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/descriptor.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/dsmanager.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/render.h" +#include "QF/Vulkan/resource.h" +#include "QF/Vulkan/staging.h" +#include "QF/Vulkan/qf_matrices.h" +#include "QF/Vulkan/qf_palette.h" +#include "QF/Vulkan/qf_particles.h" +#include "QF/Vulkan/qf_translucent.h" + +#include "r_internal.h" +#include "vid_vulkan.h" + +//FIXME make dynamic +#define MaxParticles 2048 + +typedef struct { + vec4f_t gravity; + float dT; +} particle_push_constants_t; + +static void +create_buffers (vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + particlectx_t *pctx = ctx->particle_context; + size_t mp = MaxParticles; + size_t frames = pctx->frames.size; + + pctx->resources = malloc (sizeof (qfv_resource_t) + // states buffer + + frames * sizeof (qfv_resobj_t) + // params buffer + + frames * sizeof (qfv_resobj_t) + // system buffer + + frames * sizeof (qfv_resobj_t)); + __auto_type state_objs = (qfv_resobj_t *) &pctx->resources[1]; + __auto_type param_objs = &state_objs[frames]; + __auto_type system_objs = ¶m_objs[frames]; + pctx->resources[0] = (qfv_resource_t) { + .name = "particles", + .va_ctx = ctx->va_ctx, + .memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + .num_objects = 3 * frames, + .objects = state_objs, + }; + for (size_t i = 0; i < frames; i++) { + state_objs[i] = (qfv_resobj_t) { + .name = va (ctx->va_ctx, "states:%zd", i), + .type = qfv_res_buffer, + .buffer = { + .size = mp * sizeof (qfv_particle_t), + .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT + | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + }, + }; + param_objs[i] = (qfv_resobj_t) { + .name = va (ctx->va_ctx, "param:%zd", i), + .type = qfv_res_buffer, + .buffer = { + .size = mp * sizeof (qfv_parameters_t), + .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + }, + }; + system_objs[i] = (qfv_resobj_t) { + .name = va (ctx->va_ctx, "system:%zd", i), + .type = qfv_res_buffer, + .buffer = { + .size = sizeof (qfv_particle_system_t), + .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT + | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, + }, + }; + } + QFV_CreateResource (device, pctx->resources); + + size_t stageSize = (pctx->resources->size / frames)*(frames + 1); + pctx->stage = QFV_CreateStagingBuffer (device, "particles", stageSize, + ctx->cmdpool); + for (size_t i = 0; i < frames; i++) { + __auto_type pframe = &pctx->frames.a[i]; + pframe->states = state_objs[i].buffer.buffer; + pframe->params = param_objs[i].buffer.buffer; + pframe->system = system_objs[i].buffer.buffer; + } + + for (size_t i = 0; i < frames; i++) { + __auto_type curr = &pctx->frames.a[i]; + __auto_type prev = &pctx->frames.a[(i + frames - 1) % frames]; + VkDescriptorBufferInfo bufferInfo[] = { + { curr->states, 0, VK_WHOLE_SIZE }, + { curr->params, 0, VK_WHOLE_SIZE }, + { curr->system, 0, VK_WHOLE_SIZE }, + { prev->states, 0, VK_WHOLE_SIZE }, + { prev->params, 0, VK_WHOLE_SIZE }, + { prev->system, 0, VK_WHOLE_SIZE }, + }; + VkWriteDescriptorSet write[] = { + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, + curr->curDescriptors, 0, 0, 3, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .pBufferInfo = bufferInfo + 0 + }, + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, + curr->inDescriptors, 0, 0, 3, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .pBufferInfo = bufferInfo + 3 + }, + }; + dfunc->vkUpdateDescriptorSets (device->dev, 2, write, 0, 0); + } +} + +static void +particles_draw (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto pctx = ctx->particle_context; + auto pframe = &pctx->frames.a[ctx->curFrame]; + auto layout = taskctx->pipeline->layout; + auto cmd = taskctx->cmd; + + VkDescriptorSet sets[] = { + Vulkan_Matrix_Descriptors (ctx, ctx->curFrame), + Vulkan_Palette_Descriptor (ctx), + Vulkan_Translucent_Descriptors (ctx, ctx->curFrame), + }; + dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + layout, 0, 3, sets, 0, 0); + + mat4f_t mat; + mat4fidentity (mat); + qfv_push_constants_t push_constants[] = { + { VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof (mat4f_t), &mat }, + }; + QFV_PushConstants (device, cmd, layout, 1, push_constants); + VkDeviceSize offsets[] = { 0 }; + VkBuffer buffers[] = { + pframe->states, + }; + dfunc->vkCmdBindVertexBuffers (cmd, 0, 1, buffers, offsets); + dfunc->vkCmdDrawIndirect (cmd, pframe->system, 0, 1, + sizeof (qfv_particle_system_t)); +} + +static void +update_particles (const exprval_t **p, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto pctx = ctx->particle_context; + auto pframe = &pctx->frames.a[ctx->curFrame]; + auto layout = taskctx->pipeline->layout; + + qfv_packet_t *packet = QFV_PacketAcquire (pctx->stage); + + __auto_type limits = &device->physDev->properties->limits; + VkMemoryRequirements req = { + .alignment = limits->minStorageBufferOffsetAlignment + }; + uint32_t numParticles = min (MaxParticles, pctx->psystem->numparticles); + size_t syssize = sizeof (qfv_particle_system_t); + size_t partoffs = QFV_NextOffset (syssize, &req); + size_t partsize = sizeof (qfv_particle_t) * numParticles; + size_t paramoffs = QFV_NextOffset (partoffs + partsize, &req); + size_t paramsize = sizeof (qfv_parameters_t) * numParticles; + size_t size = paramoffs + paramsize; + + qfv_particle_system_t *system = QFV_PacketExtend (packet, size); + *system = (qfv_particle_system_t) { + .vertexCount = 1, + .particleCount = numParticles, + }; + auto particles = (qfv_particle_t *) ((byte *) system + partoffs); + memcpy (particles, pctx->psystem->particles, partsize); + auto params = (qfv_parameters_t *) ((byte *) system + paramoffs); + memcpy (params, pctx->psystem->partparams, paramsize); + + if (!numParticles) { + // if there are no particles, then no space for the particle states or + // parameters has been allocated in the staging buffer, so map the + // two buffers over the system buffer. This avoids either buffer being + // just past the end of the staging buffer (which the validation layers + // (correctly) do not like). + // This is fine because the two buffers are only read by the compute + // shader. + partsize = paramsize = syssize; + partoffs = paramoffs = 0; + } + + size_t sysoffs = packet->offset; + VkDescriptorBufferInfo bufferInfo[] = { + { packet->stage->buffer, sysoffs + partoffs, partsize}, + { packet->stage->buffer, sysoffs + paramoffs, paramsize}, + { packet->stage->buffer, sysoffs, syssize }, + }; + VkWriteDescriptorSet write[] = { + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, + pframe->newDescriptors, 0, 0, 3, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .pBufferInfo = bufferInfo + }, + }; + dfunc->vkUpdateDescriptorSets (device->dev, 1, write, 0, 0); + + dfunc->vkResetEvent (device->dev, pframe->updateEvent); + + VkBufferMemoryBarrier pl_barrier[] = { + { .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_HOST_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, + .buffer = packet->stage->buffer, + .offset = sysoffs, + .size = paramoffs + paramsize, + }, + }; + dfunc->vkCmdPipelineBarrier (packet->cmd, + VK_PIPELINE_STAGE_HOST_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + 0, + 0, 0, + 1, pl_barrier, + 0, 0); + + dfunc->vkCmdBindPipeline (packet->cmd, VK_PIPELINE_BIND_POINT_COMPUTE, + taskctx->pipeline->pipeline); + VkDescriptorSet set[3] = { + pframe->curDescriptors, + pframe->inDescriptors, + pframe->newDescriptors, + }; + dfunc->vkCmdBindDescriptorSets (packet->cmd, VK_PIPELINE_BIND_POINT_COMPUTE, + layout, 0, 3, set, 0, 0); + dfunc->vkCmdDispatch (packet->cmd, 1, 1, 1); + dfunc->vkCmdSetEvent (packet->cmd, pframe->updateEvent, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + QFV_PacketSubmit (packet); + + pctx->psystem->numparticles = 0; +} + +static void +wait_on_event (VkBuffer states, VkBuffer params, VkBuffer system, + VkEvent event, bool draw, VkCommandBuffer cmd, + qfv_devfuncs_t *dfunc) +{ + VkStructureType type = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + VkAccessFlags srcAccess = draw + ? VK_ACCESS_SHADER_READ_BIT + | VK_ACCESS_SHADER_WRITE_BIT + : VK_ACCESS_SHADER_WRITE_BIT; + VkAccessFlags dstAccess = draw + ? VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT + : VK_ACCESS_SHADER_READ_BIT + | VK_ACCESS_SHADER_WRITE_BIT; + VkBufferMemoryBarrier barrier[] = { + { .sType = type, + .srcAccessMask = srcAccess, .dstAccessMask = dstAccess, + .buffer = states, .offset = 0, .size = VK_WHOLE_SIZE }, + { .sType = type, + .srcAccessMask = srcAccess, .dstAccessMask = dstAccess, + .buffer = params, .offset = 0, .size = VK_WHOLE_SIZE }, + { .sType = type, + .srcAccessMask = srcAccess, .dstAccessMask = dstAccess, + .buffer = system, .offset = 0, .size = VK_WHOLE_SIZE }, + }; + VkAccessFlags srcStage = draw + ? VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT + : VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + VkAccessFlags dstStage = draw + ? VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT + | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT + : VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + dfunc->vkCmdWaitEvents (cmd, 1, &event, srcStage, dstStage, + 0, 0, 3, barrier, 0, 0); +} + +static void +particle_physics (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto pctx = ctx->particle_context; + auto pframe = &pctx->frames.a[ctx->curFrame]; + auto layout = taskctx->pipeline->layout; + auto cmd = taskctx->cmd; + + dfunc->vkResetEvent (device->dev, pframe->physicsEvent); + wait_on_event (pframe->states, pframe->params, pframe->system, + pframe->updateEvent, false, cmd, dfunc); + + VkDescriptorSet set[] = { + pframe->curDescriptors, + }; + dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_COMPUTE, + layout, 0, 1, set, 0, 0); + + particle_push_constants_t constants = { + .gravity = pctx->psystem->gravity, + .dT = vr_data.frametime, + }; + qfv_push_constants_t push_constants[] = { + { VK_SHADER_STAGE_COMPUTE_BIT, + field_offset (particle_push_constants_t, gravity), + sizeof (vec4f_t), &constants.gravity }, + { VK_SHADER_STAGE_COMPUTE_BIT, + field_offset (particle_push_constants_t, dT), + sizeof (float), &constants.dT }, + }; + QFV_PushConstants (device, cmd, layout, 2, push_constants); + dfunc->vkCmdDispatch (cmd, MaxParticles, 1, 1); + dfunc->vkCmdSetEvent (cmd, pframe->physicsEvent, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); +} + +static void +particle_wait_physics (const exprval_t **params, exprval_t *result, + exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto pctx = ctx->particle_context; + auto pframe = &pctx->frames.a[ctx->curFrame]; + + auto cmd = QFV_GetCmdBuffer (ctx, false); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_COMMAND_BUFFER, cmd, + va (ctx->va_ctx, "cmd:particle_wait_physics:%d", + ctx->curFrame)); + QFV_AppendCmdBuffer (ctx, cmd); + VkCommandBufferBeginInfo beginInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + dfunc->vkBeginCommandBuffer (cmd, &beginInfo); + + wait_on_event (pframe->states, pframe->params, pframe->system, + pframe->physicsEvent, true, cmd, dfunc); + + dfunc->vkEndCommandBuffer (cmd); +} + +static exprfunc_t particles_draw_func[] = { + { .func = particles_draw }, + {} +}; +static exprfunc_t update_particles_func[] = { + { .func = update_particles }, + {} +}; +static exprfunc_t particle_physics_func[] = { + { .func = particle_physics }, + {} +}; +static exprfunc_t particle_wait_physics_func[] = { + { .func = particle_wait_physics }, + {} +}; +static exprsym_t particles_task_syms[] = { + { "particles_draw", &cexpr_function, particles_draw_func }, + { "update_particles", &cexpr_function, update_particles_func }, + { "particle_physics", &cexpr_function, particle_physics_func }, + { "particle_wait_physics", &cexpr_function, particle_wait_physics_func }, + {} +}; + +void +Vulkan_Particles_Init (vulkan_ctx_t *ctx) +{ + QFV_Render_AddTasks (ctx, particles_task_syms); + + particlectx_t *pctx = calloc (1, sizeof (particlectx_t)); + ctx->particle_context = pctx; + pctx->psystem = &r_psystem; +} + +void +Vulkan_Particles_Setup (vulkan_ctx_t *ctx) +{ + qfvPushDebug (ctx, "particles init"); + + auto device = ctx->device; + auto dfunc = device->funcs; + auto pctx = ctx->particle_context; + + size_t frames = ctx->render_context->frames.size; + DARRAY_INIT (&pctx->frames, frames); + DARRAY_RESIZE (&pctx->frames, frames); + pctx->frames.grow = 0; + + auto dsmanager = QFV_Render_DSManager (ctx, "particle_set"); + + for (size_t i = 0; i < frames; i++) { + __auto_type pframe = &pctx->frames.a[i]; + + pframe->curDescriptors = QFV_DSManager_AllocSet (dsmanager); + pframe->inDescriptors = QFV_DSManager_AllocSet (dsmanager); + pframe->newDescriptors = QFV_DSManager_AllocSet (dsmanager); + + VkEventCreateInfo event = { VK_STRUCTURE_TYPE_EVENT_CREATE_INFO }; + dfunc->vkCreateEvent (device->dev, &event, 0, &pframe->physicsEvent); + dfunc->vkCreateEvent (device->dev, &event, 0, &pframe->updateEvent); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_EVENT, + pframe->physicsEvent, + va (ctx->va_ctx, "event:particle:physics:%zd", i)); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_EVENT, + pframe->updateEvent, + va (ctx->va_ctx, "event:particle:update:%zd", i)); + } + create_buffers (ctx); + qfvPopDebug (ctx); +} + +void +Vulkan_Particles_Shutdown (vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + particlectx_t *pctx = ctx->particle_context; + size_t frames = pctx->frames.size; + + for (size_t i = 0; i < frames; i++) { + __auto_type pframe = &pctx->frames.a[i]; + dfunc->vkDestroyEvent (device->dev, pframe->updateEvent, 0); + dfunc->vkDestroyEvent (device->dev, pframe->physicsEvent, 0); + } + + QFV_DestroyStagingBuffer (pctx->stage); + QFV_DestroyResource (device, pctx->resources); + free (pctx->resources); + + free (pctx->frames.a); + free (pctx); +} + +psystem_t *__attribute__((pure))//FIXME? +Vulkan_ParticleSystem (vulkan_ctx_t *ctx) +{ + return ctx->particle_context->psystem; //FIXME support more +} diff --git a/libs/video/renderer/vulkan/vulkan_scene.c b/libs/video/renderer/vulkan/vulkan_scene.c new file mode 100644 index 000000000..67874fa26 --- /dev/null +++ b/libs/video/renderer/vulkan/vulkan_scene.c @@ -0,0 +1,257 @@ +/* + vulkan_scene.c + + Vulkan scene handling + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/5/24 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "QF/mathlib.h" +#include "QF/set.h" +#include "QF/va.h" + +#include "QF/scene/entity.h" + +#include "QF/Vulkan/qf_scene.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/descriptor.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/dsmanager.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/render.h" +#include "QF/Vulkan/resource.h" + +#include "r_internal.h" +#include "vid_vulkan.h" + +static const int qfv_max_entities = 4096; //FIXME should make dynamic + +int +Vulkan_Scene_MaxEntities (vulkan_ctx_t *ctx) +{ + scenectx_t *sctx = ctx->scene_context; + return sctx->max_entities; +} + +VkDescriptorSet +Vulkan_Scene_Descriptors (vulkan_ctx_t *ctx) +{ + scenectx_t *sctx = ctx->scene_context; + scnframe_t *sframe = &sctx->frames.a[ctx->curFrame]; + return sframe->descriptors; +} + +int +Vulkan_Scene_AddEntity (vulkan_ctx_t *ctx, entity_t entity) +{ + scenectx_t *sctx = ctx->scene_context; + scnframe_t *sframe = &sctx->frames.a[ctx->curFrame]; + + entdata_t *entdata = 0; + int render_id = -1; + //lock + int ent_id = Ent_Index (entity.id + 1);//nullent -> 0 + if (!set_is_member (sframe->pooled_entities, ent_id)) { + if (sframe->entity_pool.size < sframe->entity_pool.maxSize) { + set_add (sframe->pooled_entities, ent_id); + render_id = sframe->entity_pool.size++; + entdata = sframe->entity_pool.a + render_id; + } + } + if (Entity_Valid (entity)) { + renderer_t *renderer = Ent_GetComponent (entity.id, scene_renderer, + entity.reg); + renderer->render_id = render_id; + } + //unlock + if (entdata) { + mat4f_t f; + vec4f_t color; + if (Entity_Valid (entity)) { //FIXME give world entity an entity :P + transform_t transform = Entity_Transform (entity); + renderer_t *renderer = Ent_GetComponent (entity.id, scene_renderer, + entity.reg); + mat4ftranspose (f, Transform_GetWorldMatrixPtr (transform)); + entdata->xform[0] = f[0]; + entdata->xform[1] = f[1]; + entdata->xform[2] = f[2]; + color = (vec4f_t) { QuatExpand (renderer->colormod) }; + } else { + entdata->xform[0] = (vec4f_t) { 1, 0, 0, 0 }; + entdata->xform[1] = (vec4f_t) { 0, 1, 0, 0 }; + entdata->xform[2] = (vec4f_t) { 0, 0, 1, 0 }; + color = (vec4f_t) { 1, 1, 1, 1 }; + } + entdata->color = color; + } + return render_id; +} + +void +Vulkan_Scene_Flush (vulkan_ctx_t *ctx) +{ + scenectx_t *sctx = ctx->scene_context; + scnframe_t *sframe = &sctx->frames.a[ctx->curFrame]; + + set_empty (sframe->pooled_entities); + sframe->entity_pool.size = 0; +} + +static VkWriteDescriptorSet base_buffer_write = { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, 0, + 0, 0, 1, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + 0, 0, 0 +}; + +static void +scene_draw_viewmodel (const exprval_t **params, exprval_t *result, + exprctx_t *ectx) +{ + entity_t ent = vr_data.view_model; + if (!Entity_Valid (ent)) { + return; + } + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); + if (vr_data.inhibit_viewmodel + || !r_drawviewmodel + || !r_drawentities + || !renderer->model) + return; + + EntQueue_AddEntity (r_ent_queue, ent, renderer->model->type); +} + +static exprfunc_t scene_draw_viewmodel_func[] = { + { .func = scene_draw_viewmodel }, + {} +}; +static exprsym_t scene_task_syms[] = { + { "scene_draw_viewmodel", &cexpr_function, scene_draw_viewmodel_func }, + {} +}; + +void +Vulkan_Scene_Init (vulkan_ctx_t *ctx) +{ + QFV_Render_AddTasks (ctx, scene_task_syms); + + scenectx_t *sctx = calloc (1, sizeof (scenectx_t) + + sizeof (qfv_resource_t) + + sizeof (qfv_resobj_t)); + ctx->scene_context = sctx; + sctx->max_entities = qfv_max_entities; +} + +void +Vulkan_Scene_Setup (vulkan_ctx_t *ctx) +{ + qfvPushDebug (ctx, "scene init"); + + auto device = ctx->device; + auto dfunc = device->funcs; + + auto sctx = ctx->scene_context; + + auto rctx = ctx->render_context; + size_t frames = rctx->frames.size; + DARRAY_INIT (&sctx->frames, frames); + DARRAY_RESIZE (&sctx->frames, frames); + sctx->frames.grow = 0; + + sctx->entities = (qfv_resource_t *) &sctx[1]; + *sctx->entities = (qfv_resource_t) { + .name = "scene", + .va_ctx = ctx->va_ctx, + .memory_properties = VK_MEMORY_PROPERTY_HOST_CACHED_BIT, + .num_objects = 1, + .objects = (qfv_resobj_t *) &sctx->entities[1], + }; + sctx->entities->objects[0] = (qfv_resobj_t) { + .name = "entities", + .type = qfv_res_buffer, + .buffer = { + .size = frames * qfv_max_entities * sizeof (entdata_t), + .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + }, + }; + + QFV_CreateResource (device, sctx->entities); + + auto dsmanager = QFV_Render_DSManager (ctx, "entity_set"); + + entdata_t *entdata; + dfunc->vkMapMemory (device->dev, sctx->entities->memory, 0, VK_WHOLE_SIZE, + 0, (void **) &entdata); + + VkBuffer buffer = sctx->entities->objects[0].buffer.buffer; + size_t entdata_size = qfv_max_entities * sizeof (entdata_t); + for (size_t i = 0; i < frames; i++) { + __auto_type sframe = &sctx->frames.a[i]; + + sframe->descriptors = QFV_DSManager_AllocSet (dsmanager);; + VkDescriptorBufferInfo bufferInfo = { + buffer, i * entdata_size, entdata_size + }; + VkWriteDescriptorSet write[1]; + write[0] = base_buffer_write; + write[0].dstSet = sframe->descriptors; + write[0].pBufferInfo = &bufferInfo; + dfunc->vkUpdateDescriptorSets (device->dev, 1, write, 0, 0); + + sframe->entity_pool = (entdataset_t) { + .maxSize = qfv_max_entities, + .a = entdata + i * qfv_max_entities, + }; + sframe->pooled_entities = set_new (); + } + qfvPopDebug (ctx); +} + +void +Vulkan_Scene_Shutdown (vulkan_ctx_t *ctx) +{ + qfvPushDebug (ctx, "scene shutdown"); + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + scenectx_t *sctx = ctx->scene_context; + + for (size_t i = 0; i < sctx->frames.size; i++) { + __auto_type sframe = &sctx->frames.a[i]; + set_delete (sframe->pooled_entities); + } + + dfunc->vkUnmapMemory (device->dev, sctx->entities->memory); + QFV_DestroyResource (device, sctx->entities); + + free (sctx->frames.a); + free (sctx); + qfvPopDebug (ctx); +} diff --git a/libs/video/renderer/vulkan/vulkan_sprite.c b/libs/video/renderer/vulkan/vulkan_sprite.c new file mode 100644 index 000000000..964920044 --- /dev/null +++ b/libs/video/renderer/vulkan/vulkan_sprite.c @@ -0,0 +1,239 @@ +/* + vulkan_sprite.c + + Vulkan sprite model pipeline + + Copyright (C) 2012 Bill Currie + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2012/1/7 + Date: 2021/12/14 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include + +#include "qfalloca.h" + +#include "QF/cvar.h" +#include "QF/darray.h" +#include "QF/image.h" +#include "QF/render.h" +#include "QF/skin.h" +#include "QF/sys.h" +#include "QF/va.h" + +#include "QF/scene/entity.h" + +#include "QF/Vulkan/qf_matrices.h" +#include "QF/Vulkan/qf_sprite.h" +#include "QF/Vulkan/qf_texture.h" +#include "QF/Vulkan/buffer.h" +#include "QF/Vulkan/command.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/descriptor.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/dsmanager.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/render.h" + +#include "r_internal.h" +#include "vid_vulkan.h" + +static void +emit_commands (VkCommandBuffer cmd, qfv_sprite_t *sprite, + int numPC, qfv_push_constants_t *constants, + qfv_taskctx_t *taskctx, entity_t ent) +{ + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto layout = taskctx->pipeline->layout; + + Vulkan_BeginEntityLabel (ctx, cmd, ent); + + QFV_PushConstants (device, cmd, layout, numPC, constants); + VkDescriptorSet sets[] = { + sprite->descriptors, + }; + dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + layout, 1, 1, sets, 0, 0); + dfunc->vkCmdDraw (cmd, 4, 1, 0, 0); + + QFV_CmdEndLabel (device, cmd); +} + +static VkDescriptorBufferInfo base_buffer_info = { + .range = VK_WHOLE_SIZE, +}; +static VkDescriptorImageInfo base_image_info = { + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, +}; +static VkWriteDescriptorSet base_buffer_write = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstBinding = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, +}; +static VkWriteDescriptorSet base_image_write = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstBinding = 1, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, +}; + +void +Vulkan_Sprite_DescriptorSet (vulkan_ctx_t *ctx, qfv_sprite_t *sprite) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + spritectx_t *sctx = ctx->sprite_context; + + if (!sctx->dsmanager) { + sctx->dsmanager = QFV_Render_DSManager (ctx, "sprite_set"); + } + sprite->descriptors = QFV_DSManager_AllocSet (sctx->dsmanager); + + VkDescriptorBufferInfo bufferInfo[1]; + bufferInfo[0] = base_buffer_info; + bufferInfo[0].buffer = sprite->verts; + + VkDescriptorImageInfo imageInfo[1]; + imageInfo[0] = base_image_info; + imageInfo[0].sampler = sctx->sampler; + imageInfo[0].imageView = sprite->view; + + VkWriteDescriptorSet write[2]; + write[0] = base_buffer_write; + write[0].dstSet = sprite->descriptors; + write[0].pBufferInfo = bufferInfo; + write[1] = base_image_write; + write[1].dstSet = sprite->descriptors; + write[1].pImageInfo = imageInfo; + dfunc->vkUpdateDescriptorSets (device->dev, 2, write, 0, 0); +} + +void +Vulkan_Sprite_FreeDescriptors (vulkan_ctx_t *ctx, qfv_sprite_t *sprite) +{ + spritectx_t *sctx = ctx->sprite_context; + QFV_DSManager_FreeSet (sctx->dsmanager, sprite->descriptors); +} + +static void +sprite_draw_ent (qfv_taskctx_t *taskctx, entity_t ent) +{ + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); + auto model = renderer->model; + msprite_t *sprite = model->cache.data; + + mat4f_t mat = {}; + uint32_t frame; + qfv_push_constants_t push_constants[] = { + { VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof (mat), mat }, + { VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + 64, sizeof (frame), &frame }, + }; + + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, + ent.reg); + frame = (ptrdiff_t) R_GetSpriteFrame (sprite, animation); + + transform_t transform = Entity_Transform (ent); + mat[3] = Transform_GetWorldPosition (transform); + vec4f_t cameravec = r_refdef.frame.position - mat[3]; + R_BillboardFrame (transform, sprite->type, cameravec, + &mat[2], &mat[1], &mat[0]); + mat[0] = -mat[0]; + + emit_commands (taskctx->cmd, + (qfv_sprite_t *) ((byte *) sprite + sprite->data), + 2, push_constants, taskctx, ent); +} + +static void +sprite_draw (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + auto taskctx = (qfv_taskctx_t *) ectx; + auto ctx = taskctx->ctx; + auto device = ctx->device; + auto dfunc = device->funcs; + auto layout = taskctx->pipeline->layout; + auto cmd = taskctx->cmd; + + VkDescriptorSet sets[] = { + Vulkan_Matrix_Descriptors (ctx, ctx->curFrame), + }; + dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + layout, 0, 1, sets, 0, 0); + + auto queue = r_ent_queue; //FIXME fetch from scene + for (size_t i = 0; i < queue->ent_queues[mod_sprite].size; i++) { + entity_t ent = queue->ent_queues[mod_sprite].a[i]; + sprite_draw_ent (taskctx, ent); + } +} + +static exprfunc_t sprite_draw_func[] = { + { .func = sprite_draw }, + {} +}; +static exprsym_t sprite_task_syms[] = { + { "sprite_draw", &cexpr_function, sprite_draw_func }, + {} +}; + +void +Vulkan_Sprite_Init (vulkan_ctx_t *ctx) +{ + qfvPushDebug (ctx, "sprite init"); + QFV_Render_AddTasks (ctx, sprite_task_syms); + + spritectx_t *sctx = calloc (1, sizeof (spritectx_t)); + ctx->sprite_context = sctx; + + qfvPopDebug (ctx); +} + +void +Vulkan_Sprite_Setup (vulkan_ctx_t *ctx) +{ + auto sctx = ctx->sprite_context; + sctx->sampler = QFV_Render_Sampler (ctx, "sprite_sampler"); +} + +void +Vulkan_Sprite_Shutdown (vulkan_ctx_t *ctx) +{ + spritectx_t *sctx = ctx->sprite_context; + + free (sctx); +} diff --git a/libs/video/renderer/vulkan/vulkan_texture.c b/libs/video/renderer/vulkan/vulkan_texture.c new file mode 100644 index 000000000..94b956d04 --- /dev/null +++ b/libs/video/renderer/vulkan/vulkan_texture.c @@ -0,0 +1,605 @@ +/* + vulkan_texuture.c + + Quake specific Vulkan texuture manager + + Copyright (C) 2021 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_MATH_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/va.h" +#include "QF/Vulkan/qf_texture.h" +#include "QF/Vulkan/barrier.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/descriptor.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/dsmanager.h" +#include "QF/Vulkan/image.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/render.h" +#include "QF/Vulkan/staging.h" + +#include "vid_vulkan.h" + +void +Vulkan_ExpandPalette (byte *dst, const byte *src, const byte *palette, + int alpha, int count) +{ + if (alpha > 1) { + while (count-- > 0) { + int pix = *src++; + const byte *col = palette + pix * 4; + *dst++ = *col++; + *dst++ = *col++; + *dst++ = *col++; + *dst++ = *col++; + } + } else if (alpha) { + while (count-- > 0) { + byte pix = *src++; + const byte *col = palette + pix * 3; + *dst++ = *col++; + *dst++ = *col++; + *dst++ = *col++; + *dst++ = 0xff; + } + } else { + while (count-- > 0) { + byte pix = *src++; + const byte *col = palette + pix * 3; + *dst++ = *col++; + *dst++ = *col++; + *dst++ = *col++; + } + } +} + +static int +tex_format (const tex_t *tex, VkFormat *format, int *bpp) +{ + switch (tex->format) { + case tex_l: + case tex_a: + *format = VK_FORMAT_R8_UNORM; + *bpp = 1; + return 1; + case tex_la: + *format = VK_FORMAT_R8G8_UNORM; + *bpp = 2; + return 1; + case tex_palette: + if (!tex->palette) { + return 0; + } + *format = VK_FORMAT_R8G8B8A8_UNORM; + *bpp = 4; + return 1; + case tex_rgb: + *format = VK_FORMAT_R8G8B8A8_UNORM; + *bpp = 4; + return 1; + case tex_rgba: + *format = VK_FORMAT_R8G8B8A8_UNORM; + *bpp = 4; + return 1; + case tex_frgba: + *format = VK_FORMAT_R32G32B32A32_SFLOAT; + *bpp = 16; + return 1; + } + return 0; +} + +static size_t +stage_tex_data (qfv_packet_t *packet, tex_t *tex, int bpp) +{ + size_t texels = tex->width * tex->height; + byte *tex_data = QFV_PacketExtend (packet, bpp * texels); + + if (tex->format == tex_palette) { + Vulkan_ExpandPalette (tex_data, tex->data, tex->palette, 1, texels); + } else { + if (tex->format == 3) { + byte *in = tex->data; + byte *out = tex_data; + while (texels-- > 0) { + *out++ = *in++; + *out++ = *in++; + *out++ = *in++; + *out++ = 255; + } + } else { + memcpy (tex_data, tex->data, bpp * texels); + } + } + return tex_data - (byte *) packet->stage->data; +} + +qfv_tex_t * +Vulkan_LoadTexArray (vulkan_ctx_t *ctx, tex_t *tex, int layers, int mip, + const char *name) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + int bpp; + VkFormat format; + + if (!tex_format (tex, &format, &bpp)) { + return 0; + } + + for (int i = 1; i < layers; i++) { + if (tex[i].width != tex[0].width || tex[i].height != tex[0].height + || tex[i].format != tex[0].format) { + return 0; + } + } + + if (mip) { + mip = QFV_MipLevels (tex[0].width, tex[0].height); + } else { + mip = 1; + } + + //qfv_devfuncs_t *dfunc = device->funcs; + //FIXME this whole thing is ineffiecient, especially for small textures + qfv_tex_t *qtex = malloc (sizeof (qfv_tex_t)); + + VkExtent3D extent = { tex[0].width, tex[0].height, 1 }; + VkImageType itype = layers > 0 ? VK_IMAGE_TYPE_2D : VK_IMAGE_TYPE_2D; + VkImageViewType vtype = layers > 0 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY + : VK_IMAGE_VIEW_TYPE_2D; + if (layers < 1) { + layers = 1; + } + qtex->image = QFV_CreateImage (device, 0, itype, format, extent, + mip, layers, VK_SAMPLE_COUNT_1_BIT, + VK_IMAGE_USAGE_TRANSFER_DST_BIT + | VK_IMAGE_USAGE_TRANSFER_SRC_BIT + | VK_IMAGE_USAGE_SAMPLED_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE, qtex->image, + va (ctx->va_ctx, "image:%s", name)); + qtex->memory = QFV_AllocImageMemory (device, qtex->image, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + 0, 0); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, qtex->memory, + va (ctx->va_ctx, "memory:%s", name)); + QFV_BindImageMemory (device, qtex->image, qtex->memory, 0); + qtex->view = QFV_CreateImageView (device, qtex->image, vtype, + format, VK_IMAGE_ASPECT_COLOR_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE_VIEW, qtex->view, + va (ctx->va_ctx, "iview:%s", name)); + + qfv_packet_t *packet = QFV_PacketAcquire (ctx->staging); + + VkBufferImageCopy copy[layers]; + copy[0] = (VkBufferImageCopy) { + 0, 0, 0, + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, + {0, 0, 0}, {tex[0].width, tex[0].height, 1}, + }; + for (int i = 0; i < layers; i++) { + copy[i] = copy[0]; + copy[i].bufferOffset = stage_tex_data (packet, &tex[i], bpp); + copy[i].imageSubresource.baseArrayLayer = i; + } + + qfv_imagebarrier_t ib = imageBarriers[qfv_LT_Undefined_to_TransferDst]; + ib.barrier.image = qtex->image; + ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); + dfunc->vkCmdCopyBufferToImage (packet->cmd, packet->stage->buffer, + qtex->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + layers, copy); + if (mip == 1) { + ib = imageBarriers[qfv_LT_TransferDst_to_ShaderReadOnly]; + ib.barrier.image = qtex->image; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); + } else { + QFV_GenerateMipMaps (device, packet->cmd, qtex->image, + mip, tex->width, tex->height, layers); + } + QFV_PacketSubmit (packet); + return qtex; +} + +qfv_tex_t * +Vulkan_LoadTex (vulkan_ctx_t *ctx, tex_t *tex, int mip, const char *name) +{ + return Vulkan_LoadTexArray (ctx, tex, 0, mip, name); +} + +static qfv_tex_t * +create_cubetex (vulkan_ctx_t *ctx, int size, VkFormat format, + const char *name) +{ + qfv_device_t *device = ctx->device; + + qfv_tex_t *qtex = malloc (sizeof (qfv_tex_t)); + + VkExtent3D extent = { size, size, 1 }; + qtex->image = QFV_CreateImage (device, 1, VK_IMAGE_TYPE_2D, format, extent, + 1, 1, VK_SAMPLE_COUNT_1_BIT, + VK_IMAGE_USAGE_SAMPLED_BIT + | VK_IMAGE_USAGE_TRANSFER_DST_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE, qtex->image, + va (ctx->va_ctx, "image:envmap:%s", name)); + qtex->memory = QFV_AllocImageMemory (device, qtex->image, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + 0, 0); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, qtex->memory, + va (ctx->va_ctx, "memory:%s", name)); + QFV_BindImageMemory (device, qtex->image, qtex->memory, 0); + qtex->view = QFV_CreateImageView (device, qtex->image, + VK_IMAGE_VIEW_TYPE_CUBE, format, + VK_IMAGE_ASPECT_COLOR_BIT); + QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE_VIEW, qtex->view, + va (ctx->va_ctx, "iview:envmap:%s", name)); + + return qtex; +} + +qfv_tex_t * +Vulkan_LoadEnvMap (vulkan_ctx_t *ctx, tex_t *tex, const char *name) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + int bpp; + VkFormat format; + + static int env_coords[][2] = { + {2, 0}, // right + {0, 0}, // left + {1, 1}, // top + {0, 1}, // bottom + {2, 1}, // front + {1, 0}, // back + }; + + if (!tex_format (tex, &format, &bpp)) { + return 0; + } + if (tex->height * 3 != tex->width * 2) { + return 0; + } + + int size = tex->height / 2; + qfv_tex_t *qtex = create_cubetex (ctx, size, format, name); + + qfv_packet_t *packet = QFV_PacketAcquire (ctx->staging); + stage_tex_data (packet, tex, bpp); + + qfv_imagebarrier_t ib = imageBarriers[qfv_LT_Undefined_to_TransferDst]; + ib.barrier.image = qtex->image; + ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); + + VkBufferImageCopy copy[6] = { + { + 0, tex->width, 0, + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, + {0, 0, 0}, {size, size, 1}, + }, + }; + for (int i = 0; i < 6; i++) { + int x = env_coords[i][0] * size; + int y = env_coords[i][1] * size; + int offset = x + y * tex->width; + copy[i] = copy[0]; + copy[i].bufferOffset = packet->offset + bpp * offset; + copy[i].imageSubresource.baseArrayLayer = i; + } + dfunc->vkCmdCopyBufferToImage (packet->cmd, packet->stage->buffer, + qtex->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 6, copy); + ib = imageBarriers[qfv_LT_TransferDst_to_ShaderReadOnly]; + ib.barrier.image = qtex->image; + ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); + QFV_PacketSubmit (packet); + return qtex; +} + +qfv_tex_t * +Vulkan_LoadEnvSides (vulkan_ctx_t *ctx, tex_t **tex, const char *name) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + int bpp; + VkFormat format; + + if (!tex_format (tex[0], &format, &bpp)) { + return 0; + } + if (tex[0]->height != tex[0]->width) { + return 0; + } + for (int i = 1; i < 6; i++) { + if (tex[i]->format != tex[0]->format + || tex[i]->width != tex[0]->width + || tex[i]->height != tex[0]->height) { + return 0; + } + } + + int size = tex[0]->height; + qfv_tex_t *qtex = create_cubetex (ctx, size, format, name); + + qfv_packet_t *packet = QFV_PacketAcquire (ctx->staging); + + qfv_imagebarrier_t ib = imageBarriers[qfv_LT_Undefined_to_TransferDst]; + ib.barrier.image = qtex->image; + ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); + + VkBufferImageCopy copy[6] = { + { + 0, 0, 0, + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, + {0, 0, 0}, {size, size, 1}, + }, + }; + for (int i = 0; i < 6; i++) { + copy[i] = copy[0]; + copy[i].bufferOffset = stage_tex_data (packet, tex[i], bpp); + copy[i].imageSubresource.baseArrayLayer = i; + } + dfunc->vkCmdCopyBufferToImage (packet->cmd, packet->stage->buffer, + qtex->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 6, copy); + ib = imageBarriers[qfv_LT_TransferDst_to_ShaderReadOnly]; + ib.barrier.image = qtex->image; + ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); + QFV_PacketSubmit (packet); + return qtex; +} + +VkImageView +Vulkan_TexImageView (qfv_tex_t *tex) +{ + return tex->view; +} + +void +Vulkan_UpdateTex (vulkan_ctx_t *ctx, qfv_tex_t *tex, tex_t *src, + int x, int y, int layer, int mip) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + + int bpp; + VkFormat format; + if (!tex_format (src, &format, &bpp)) { + return; + } + qfv_packet_t *packet = QFV_PacketAcquire (ctx->staging); + + qfv_imagebarrier_t ib = imageBarriers[qfv_LT_ShaderReadOnly_to_TransferDst]; + ib.barrier.image = tex->image; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); + + VkBufferImageCopy copy = { + .bufferOffset = stage_tex_data (packet, src, bpp), + .imageSubresource = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = mip, + .baseArrayLayer = layer, + .layerCount = 1, + }, + .imageOffset = { .x = x, .y = y, .z = 0 }, + .imageExtent = { + .width = src->width, + .height = src->height, + .depth = 1, + }, + }; + dfunc->vkCmdCopyBufferToImage (packet->cmd, packet->stage->buffer, + tex->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, ©); + ib = imageBarriers[qfv_LT_TransferDst_to_ShaderReadOnly]; + ib.barrier.image = tex->image; + ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); + QFV_PacketSubmit (packet); +} + +void +Vulkan_UnloadTex (vulkan_ctx_t *ctx, qfv_tex_t *tex) +{ + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + + if (tex->view) { + dfunc->vkDestroyImageView (device->dev, tex->view, 0); + } + if (tex->image) { + dfunc->vkDestroyImage (device->dev, tex->image, 0); + } + if (tex->memory) { + dfunc->vkFreeMemory (device->dev, tex->memory, 0); + } + free (tex); +} + +static byte black_data[] = {0, 0, 0, 0}; +static byte white_data[] = {255, 255, 255, 255}; +static byte magenta_data[] = {255, 0, 255, 255}; +static tex_t default_black_tex = { + .width = 1, + .height = 1, + .format = tex_rgba, + .loaded = 1, + .palette =0, + .data = black_data, +}; +static tex_t default_white_tex = { + .width = 1, + .height = 1, + .format = tex_rgba, + .loaded = 1, + .palette =0, + .data = white_data, +}; +static tex_t default_magenta_tex = { + .width = 1, + .height = 1, + .format = tex_rgba, + .loaded = 1, + .palette =0, + .data = magenta_data, +}; + +void +Vulkan_Texture_Init (vulkan_ctx_t *ctx) +{ + texturectx_t *tctx = calloc (1, sizeof (texturectx_t)); + ctx->texture_context = tctx; +} + +void +Vulkan_Texture_Setup (vulkan_ctx_t *ctx) +{ + qfvPushDebug (ctx, "texture init"); + + auto tctx = ctx->texture_context; + + tctx->dsmanager = QFV_Render_DSManager (ctx, "texture_set"); + + ctx->default_black = Vulkan_LoadTex (ctx, &default_black_tex, 1, + "default_black"); + ctx->default_white = Vulkan_LoadTex (ctx, &default_white_tex, 1, + "default_white"); + ctx->default_magenta = Vulkan_LoadTex (ctx, &default_magenta_tex, 1, + "default_magenta"); + qfv_tex_t *tex; + tex = ctx->default_magenta_array = malloc (sizeof (qfv_tex_t)); + tex->memory = 0; + tex->image = 0; + tex->view = QFV_CreateImageView (ctx->device, ctx->default_magenta->image, + VK_IMAGE_VIEW_TYPE_2D_ARRAY, + VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_ASPECT_COLOR_BIT); + QFV_duSetObjectName (ctx->device, VK_OBJECT_TYPE_IMAGE_VIEW, tex->view, + "iview:default_magenta_array"); + qfvPopDebug (ctx); +} + +void +Vulkan_Texture_Shutdown (vulkan_ctx_t *ctx) +{ + Vulkan_UnloadTex (ctx, ctx->default_black); + Vulkan_UnloadTex (ctx, ctx->default_white); + Vulkan_UnloadTex (ctx, ctx->default_magenta); + Vulkan_UnloadTex (ctx, ctx->default_magenta_array); +} + +static VkDescriptorImageInfo base_image_info = { + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, +}; +static VkWriteDescriptorSet base_image_write = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstBinding = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, +}; + +VkDescriptorSet +Vulkan_CreateCombinedImageSampler (vulkan_ctx_t *ctx, VkImageView view, + VkSampler sampler) +{ + qfvPushDebug (ctx, "Vulkan_CreateCombinedImageSampler"); + + auto device = ctx->device; + auto dfunc = device->funcs; + auto tctx = ctx->texture_context; + + auto descriptor = QFV_DSManager_AllocSet (tctx->dsmanager); + + VkDescriptorImageInfo imageInfo[1]; + imageInfo[0] = base_image_info; + imageInfo[0].sampler = sampler; + imageInfo[0].imageView = view; + + VkWriteDescriptorSet write[1]; + write[0] = base_image_write; + write[0].dstSet = descriptor; + write[0].pImageInfo = imageInfo; + dfunc->vkUpdateDescriptorSets (device->dev, 1, write, 0, 0); + + qfvPopDebug (ctx); + + return descriptor; +} + +VkDescriptorSet +Vulkan_CreateTextureDescriptor (vulkan_ctx_t *ctx, qfv_tex_t *tex, + VkSampler sampler) +{ + return Vulkan_CreateCombinedImageSampler (ctx, tex->view, sampler); +} + +void +Vulkan_FreeTexture (vulkan_ctx_t *ctx, VkDescriptorSet texture) +{ + auto tctx = ctx->texture_context; + + QFV_DSManager_FreeSet (tctx->dsmanager, texture); +} diff --git a/libs/video/renderer/vulkan/vulkan_translucent.c b/libs/video/renderer/vulkan/vulkan_translucent.c new file mode 100644 index 000000000..fdae8fc21 --- /dev/null +++ b/libs/video/renderer/vulkan/vulkan_translucent.c @@ -0,0 +1,321 @@ +/* + vulkan_translucent.c + + Vulkan translucent pass pipeline + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/11/30 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include + +#include "qfalloca.h" + +#include "QF/cvar.h" +#include "QF/sys.h" +#include "QF/va.h" + +#include "QF/Vulkan/qf_translucent.h" +#include "QF/Vulkan/barrier.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/descriptor.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/dsmanager.h" +#include "QF/Vulkan/image.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/render.h" +#include "QF/Vulkan/resource.h" +#include "QF/Vulkan/staging.h" +#include "QF/Vulkan/swapchain.h" + +#include "r_internal.h" +#include "vid_vulkan.h" + +static const char * __attribute__((used)) translucent_pass_names[] = { + "clear", + "blend", +}; + +static void +clear_translucent (const exprval_t **params, exprval_t *result, exprctx_t *ectx) +{ + __auto_type taskctx = (qfv_taskctx_t *) ectx; + vulkan_ctx_t *ctx = taskctx->ctx; + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + translucentctx_t *tctx = ctx->translucent_context; + __auto_type tframe = &tctx->frames.a[ctx->curFrame]; + + VkCommandBuffer cmd = QFV_GetCmdBuffer (ctx, false); + + VkCommandBufferBeginInfo beginInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + dfunc->vkBeginCommandBuffer (cmd, &beginInfo); + + auto image = scr_fisheye ? tframe->cube_heads : tframe->heads; + qfv_imagebarrier_t ib = imageBarriers[qfv_LT_Undefined_to_TransferDst]; + ib.barrier.image = image; + ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + dfunc->vkCmdPipelineBarrier (cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); + VkClearColorValue clear_color[] = { + { .int32 = {-1, -1, -1, -1} }, + }; + VkImageSubresourceRange ranges[] = { + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, VK_REMAINING_ARRAY_LAYERS }, + }; + dfunc->vkCmdClearColorImage (cmd, image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + clear_color, 1, ranges); + ib = imageBarriers[qfv_LT_TransferDst_to_General]; + ib.barrier.image = image; + ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + dfunc->vkCmdPipelineBarrier (cmd, ib.srcStages, ib.dstStages, + 0, 0, 0, 0, 0, + 1, &ib.barrier); + + dfunc->vkEndCommandBuffer (cmd); + QFV_AppendCmdBuffer (ctx, cmd); + + qfv_packet_t *packet = QFV_PacketAcquire (ctx->staging); + qfv_transtate_t *state = QFV_PacketExtend (packet, 2 * sizeof (*state)); + *state = (qfv_transtate_t) { 0, tctx->maxFragments }; + __auto_type bb = &bufferBarriers[qfv_BB_TransferWrite_to_ShaderRW]; + QFV_PacketCopyBuffer (packet, tframe->state, 0, bb); + QFV_PacketSubmit (packet); +} + +static exprfunc_t clear_translucent_func[] = { + { .func = clear_translucent }, + {} +}; +static exprsym_t translucent_task_syms[] = { + { "clear_translucent", &cexpr_function, clear_translucent_func }, + {} +}; + +void +Vulkan_Translucent_Init (vulkan_ctx_t *ctx) +{ + QFV_Render_AddTasks (ctx, translucent_task_syms); + + translucentctx_t *tctx = calloc (1, sizeof (translucentctx_t)); + ctx->translucent_context = tctx; +} + +void +Vulkan_Translucent_Setup (vulkan_ctx_t *ctx) +{ + qfvPushDebug (ctx, "translucent init"); + + auto tctx = ctx->translucent_context; + + auto rctx = ctx->render_context; + size_t frames = rctx->frames.size; + DARRAY_INIT (&tctx->frames, frames); + DARRAY_RESIZE (&tctx->frames, frames); + tctx->frames.grow = 0; + + tctx->maxFragments = vulkan_oit_fragments * 1024 * 1024; + + auto dsmanager = QFV_Render_DSManager (ctx, "oit_set"); + for (size_t i = 0; i < frames; i++) { + tctx->frames.a[i] = (translucentframe_t) { + .flat = QFV_DSManager_AllocSet (dsmanager), + .cube = QFV_DSManager_AllocSet (dsmanager), + }; + } + Vulkan_Translucent_CreateBuffers (ctx, ctx->swapchain->extent);//FIXME + qfvPopDebug (ctx); +} + +void +Vulkan_Translucent_Shutdown (vulkan_ctx_t *ctx) +{ + qfv_device_t *device = ctx->device; + translucentctx_t *tctx = ctx->translucent_context; + + if (tctx->resources) { + QFV_DestroyResource (device, tctx->resources); + free (tctx->resources); + tctx->resources = 0; + } + + free (tctx->frames.a); + free (tctx); +} + +VkDescriptorSet +Vulkan_Translucent_Descriptors (vulkan_ctx_t *ctx, int frame) +{ + auto tctx = ctx->translucent_context; + auto tframe = &tctx->frames.a[frame]; + return scr_fisheye ? tframe->cube : tframe->flat; +} + +void +Vulkan_Translucent_CreateBuffers (vulkan_ctx_t *ctx, VkExtent2D extent) +{ + if (!ctx->translucent_context) {//FIXME + Vulkan_Translucent_Init (ctx); + } + qfv_device_t *device = ctx->device; + qfv_devfuncs_t *dfunc = device->funcs; + __auto_type tctx = ctx->translucent_context; + size_t frames = tctx->frames.size; + + if (tctx->resources) { + QFV_DestroyResource (device, tctx->resources); + free (tctx->resources); + tctx->resources = 0; + } + tctx->resources = malloc (sizeof (qfv_resource_t) + // heads images (flat + cube) + + sizeof (qfv_resobj_t[frames]) * 2 + // heads image views (flat + cube) + + sizeof (qfv_resobj_t[frames]) * 2 + // fragment buffer + + sizeof (qfv_resobj_t[frames]) + // fragment count + + sizeof (qfv_resobj_t[frames])); + auto heads_objs = (qfv_resobj_t *) &tctx->resources[1]; + auto cube_heads_objs = &heads_objs[frames]; + auto head_views_objs = &cube_heads_objs[frames]; + auto cube_head_views_objs = &head_views_objs[frames]; + auto buffer_objs = &cube_head_views_objs[frames]; + auto count_objs = &buffer_objs[frames]; + tctx->resources[0] = (qfv_resource_t) { + .name = "oit", + .va_ctx = ctx->va_ctx, + .memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + .num_objects = 6 * frames, + .objects = heads_objs, + }; + for (size_t i = 0; i < frames; i++) { + heads_objs[i] = (qfv_resobj_t) { + .name = va (ctx->va_ctx, "heads:%zd", i), + .type = qfv_res_image, + .image = { + .type = VK_IMAGE_TYPE_2D, + .format = VK_FORMAT_R32_SINT, + .extent = { extent.width, extent.height, 1 }, + .num_mipmaps = 1, + .num_layers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .usage = VK_IMAGE_USAGE_STORAGE_BIT + | VK_IMAGE_USAGE_TRANSFER_DST_BIT, + }, + }; + auto e = min (extent.width, extent.height); + cube_heads_objs[i] = heads_objs[i]; + cube_heads_objs[i].name = va (ctx->va_ctx, "cube_heads:%zd", i); + cube_heads_objs[i].image.extent = (VkExtent3D) { e, e, 1 }; + cube_heads_objs[i].image.num_layers = 6; + head_views_objs[i] = (qfv_resobj_t) { + .name = heads_objs[i].name, + .type = qfv_res_image_view, + .image_view = { + .image = i, + .type = VK_IMAGE_VIEW_TYPE_2D_ARRAY, + .format = VK_FORMAT_R32_SINT, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .levelCount = VK_REMAINING_MIP_LEVELS, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }, + }; + cube_head_views_objs[i] = head_views_objs[i]; + cube_head_views_objs[i].name = cube_heads_objs[i].name; + cube_head_views_objs[i].image_view.image = i + frames; + buffer_objs[i] = (qfv_resobj_t) { + .name = va (ctx->va_ctx, "frags:%zd", i), + .type = qfv_res_buffer, + .buffer = { + .size = sizeof (qfv_transfrag_t) * tctx->maxFragments, + .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + }, + }; + count_objs[i] = (qfv_resobj_t) { + .name = va (ctx->va_ctx, "count:%zd", i), + .type = qfv_res_buffer, + .buffer = { + .size = 2 * sizeof (qfv_transtate_t), + .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT + | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + }, + }; + } + QFV_CreateResource (device, tctx->resources); + + for (size_t i = 0; i < frames; i++) { + __auto_type tframe = &tctx->frames.a[i]; + tframe->heads = heads_objs[i].image.image; + tframe->cube_heads = cube_heads_objs[i].image.image; + tframe->state = count_objs[i].buffer.buffer; + + VkDescriptorImageInfo flat_imageInfo[] = { + { 0, head_views_objs[i].image_view.view, VK_IMAGE_LAYOUT_GENERAL }, + }; + VkDescriptorImageInfo cube_imageInfo[] = { + { 0, cube_head_views_objs[i].image_view.view, + VK_IMAGE_LAYOUT_GENERAL }, + }; + VkDescriptorBufferInfo bufferInfo[] = { + { count_objs[i].buffer.buffer, 0, VK_WHOLE_SIZE }, + { buffer_objs[i].buffer.buffer, 0, VK_WHOLE_SIZE }, + }; + VkWriteDescriptorSet write[] = { + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, + tframe->flat, 2, 0, 1, + VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .pImageInfo = flat_imageInfo }, + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, + tframe->flat, 0, 0, 2, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .pBufferInfo = bufferInfo }, + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, + tframe->cube, 2, 0, 1, + VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .pImageInfo = cube_imageInfo }, + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, + tframe->cube, 0, 0, 2, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .pBufferInfo = bufferInfo }, + }; + dfunc->vkUpdateDescriptorSets (device->dev, 4, write, 0, 0); + } +} diff --git a/libs/video/renderer/vulkan/vulkan_vid_common.c b/libs/video/renderer/vulkan/vulkan_vid_common.c new file mode 100644 index 000000000..7cbb5a1cb --- /dev/null +++ b/libs/video/renderer/vulkan/vulkan_vid_common.c @@ -0,0 +1,218 @@ +/* + vid_common_vulkan.c + + Common Vulkan video driver functions + + Copyright (C) 2019 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_MATH_H +# include +#endif + +#include + +#include "QF/cexpr.h" +#include "QF/cmem.h" +#include "QF/cvar.h" +#include "QF/dstring.h" +#include "QF/hash.h" +#include "QF/heapsort.h" +#include "QF/plist.h" +#include "QF/va.h" +#include "QF/scene/entity.h" +#include "QF/Vulkan/capture.h" +#include "QF/Vulkan/command.h" +#include "QF/Vulkan/debug.h" +#include "QF/Vulkan/device.h" +#include "QF/Vulkan/instance.h" +#include "QF/Vulkan/staging.h" +#include "QF/Vulkan/swapchain.h" + +#include "QF/Vulkan/qf_lighting.h" +#include "QF/Vulkan/qf_main.h" +#include "QF/Vulkan/qf_output.h" +#include "QF/Vulkan/qf_particles.h" +#include "QF/Vulkan/qf_translucent.h" +#include "QF/Vulkan/qf_vid.h" + +#include "r_internal.h" +#include "vid_vulkan.h" +#include "vkparse.h" + +int vulkan_frame_width; +static cvar_t vulkan_frame_width_cvar = { + .name = "vulkan_frame_width", + .description = + "Width of 3D view buffer. Set to 0 for automatic sizing.", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &vulkan_frame_width }, +}; + +int vulkan_frame_height; +static cvar_t vulkan_frame_height_cvar = { + .name = "vulkan_frame_height", + .description = + "Height of 3D view buffer. Set to 0 for automatic sizing.", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &vulkan_frame_height }, +}; +int vulkan_oit_fragments; +static cvar_t vulkan_oit_fragments_cvar = { + .name = "vulkan_oit_fragments", + .description = + "Size of fragment buffer (M) for order independent transparency.", + .default_value = "16", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &vulkan_oit_fragments }, +}; + +static const char *instance_extensions[] = { + VK_KHR_SURFACE_EXTENSION_NAME, + VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, + 0, +}; + +static const char *device_extensions[] = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + 0, +}; + +void +Vulkan_Init_Common (vulkan_ctx_t *ctx) +{ + Sys_MaskPrintf (SYS_vulkan, "Vulkan_Init_Common\n"); + + Cvar_Register (&vulkan_frame_width_cvar, 0, 0); + Cvar_Register (&vulkan_frame_height_cvar, 0, 0); + Cvar_Register (&vulkan_oit_fragments_cvar, 0, 0); + Vulkan_Init_Cvars (); + R_Init_Cvars (); + Vulkan_Script_Init (ctx); + ctx->instance = QFV_CreateInstance (ctx, PACKAGE_STRING, 0x000702ff, 0, + instance_extensions);//FIXME version + DARRAY_INIT (&ctx->renderPasses, 4); +} + +void +Vulkan_Shutdown_Common (vulkan_ctx_t *ctx) +{ + if (ctx->swapchain) { + QFV_DestroySwapchain (ctx->swapchain); + } + ctx->instance->funcs->vkDestroySurfaceKHR (ctx->instance->instance, + ctx->surface, 0); + Vulkan_Script_Shutdown (ctx); + if (ctx->device) { + QFV_DestroyDevice (ctx->device); + } + if (ctx->instance) { + QFV_DestroyInstance (ctx->instance); + } + ctx->instance = 0; + ctx->unload_vulkan (ctx); +} + +void +Vulkan_CreateDevice (vulkan_ctx_t *ctx) +{ + ctx->device = QFV_CreateDevice (ctx, device_extensions); + + //FIXME msaa and deferred rendering... + //also, location + ctx->msaaSamples = 1; + /*ctx->msaaSamples = min ((VkSampleCountFlagBits) msaaSamples, + QFV_GetMaxSampleCount (device->physDev)); + if (ctx->msaaSamples > 1) { + name = "renderpass_msaa"; + }*/ +} + +void +Vulkan_CreateStagingBuffers (vulkan_ctx_t *ctx) +{ + // FIXME configurable? + ctx->staging = QFV_CreateStagingBuffer (ctx->device, "vulkan_ctx", + 32*1024*1024, ctx->cmdpool); +} + +void +Vulkan_CreateSwapchain (vulkan_ctx_t *ctx) +{ + VkSwapchainKHR old_swapchain = 0; + if (ctx->swapchain) { + //FIXME this shouldn't be here + qfv_device_t *device = ctx->swapchain->device; + VkDevice dev = device->dev; + qfv_devfuncs_t *dfunc = device->funcs; + old_swapchain = ctx->swapchain->swapchain; + for (size_t i = 0; i < ctx->swapchain->imageViews->size; i++) { + dfunc->vkDestroyImageView(dev, ctx->swapchain->imageViews->a[i], 0); + } + free (ctx->swapchain->images); + free (ctx->swapchain->imageViews); + free (ctx->swapchain); + } + ctx->swapchain = QFV_CreateSwapchain (ctx, old_swapchain); +} + +void +Vulkan_BeginEntityLabel (vulkan_ctx_t *ctx, VkCommandBuffer cmd, entity_t ent) +{ + qfv_device_t *device = ctx->device; + uint32_t entgen = Ent_Generation (ent.id); + uint32_t entind = Ent_Index (ent.id); + transform_t transform = Entity_Transform (ent); + vec4f_t pos = Transform_GetWorldPosition (transform); + vec4f_t dir = normalf (pos - (vec4f_t) { 0, 0, 0, 1 }); + vec4f_t color = 0.5 * dir + (vec4f_t) {0.5, 0.5, 0.5, 1 }; + + QFV_CmdBeginLabel (device, cmd, + va (ctx->va_ctx, "ent %03x.%05x [%g, %g, %g]", + entgen, entind, VectorExpand (pos)), color); +} + +void +Vulkan_ConfigOutput (vulkan_ctx_t *ctx, qfv_output_t *output) +{ + *output = (qfv_output_t) { + .extent = ctx->swapchain->extent, + .frames = ctx->swapchain->numImages, + }; + if (vulkan_frame_width > 0) { + output->extent.width = vulkan_frame_width; + } + if (vulkan_frame_height > 0) { + output->extent.height = vulkan_frame_height; + } + if (scr_fisheye) {//FIXME + auto w = output->extent.width; + auto h = output->extent.height; + output->extent.width = min (w, h); + output->extent.height = min (w, h); + } +} diff --git a/libs/video/targets/Makefile.am b/libs/video/targets/Makefile.am deleted file mode 100644 index b883b676b..000000000 --- a/libs/video/targets/Makefile.am +++ /dev/null @@ -1,117 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -AM_CPPFLAGS= -I$(top_srcdir)/include - -lib_ldflags=-version-info $(QUAKE_LIBRARY_VERSION_INFO) \ - -rpath $(libdir) -no-undefined - -lib_LTLIBRARIES= @JOY_TARGETS@ - -noinst_LTLIBRARIES= @VID_TARGETS@ @vid_libs@ - -EXTRA_LTLIBRARIES= \ - libQFjs.la libQFfbdev.la libQFsvga.la libQFx11.la libQFsdl.la libQFwgl.la \ - libvid_common.la libvid_sdl.la \ - libvid_svga.la libvid_x11.la - -joy_linux_src= joy_linux.c -joy_win_src= joy_win.c -joy_null_src= joy_null.c -if JOYTYPE_LINUX -joy_src= $(joy_linux_src) -else -if JOYTYPE_WIN32 -joy_src= $(joy_win_src) -else -joy_src= $(joy_null_src) -endif -endif - -js_libs=$(top_builddir)/libs/util/libQFutil.la - -libQFjs_la_LDFLAGS= $(lib_ldflags) -libQFjs_la_LIBADD= $(js_libs) -libQFjs_la_DEPENDENCIES=$(js_libs) -libQFjs_la_CFLAGS= @PREFER_PIC@ $(JOY_CFLAGS) -libQFjs_la_SOURCES= joy.c $(joy_src) -EXTRA_libQFjs_la_SOURCES= $(joy_linux_src) $(joy_win_src) $(joy_null_src) - -libvid_common_la_SOURCES= \ - in_common.c in_event.c keys.c old_keys.c pr_keys.c vid.c -libvid_common_la_CFLAGS= @PREFER_NON_PIC@ -libvid_common_la_LDFLAGS= @STATIC@ - -libvid_x11_la_SOURCES= in_x11.c context_x11.c dga_check.c -libvid_x11_la_CFLAGS= @PREFER_NON_PIC@ $(X_CFLAGS) -libvid_x11_la_LDFLAGS= @STATIC@ - -libvid_svga_la_SOURCES= in_svgalib.c -libvid_svga_la_CFLAGS= @PREFER_NON_PIC@ $(SVGA_CFLAGS) -libvid_svga_la_LDFLAGS= @STATIC@ - -libvid_sdl_la_SOURCES= in_sdl.c context_sdl.c -libvid_sdl_la_CFLAGS= @PREFER_NON_PIC@ $(SDL_CFLAGS) -libvid_sdl_la_LDFLAGS= @STATIC@ - -# -# Linux FBdev -# -fbdev_c= fbset_modes_y.c fbset_modes_l.c -fbdev_h= fbset_modes_y.h -YFLAGS = -d -YACCLEX_CLEANFILES= $(fbdev_c) $(fbdev_h) -BUILT_SOURCES= $(fbdev_c) $(fbdev_h) - -fbdev_libs=libvid_common.la -libQFfbdev_la_CFLAGS= @PREFER_NON_PIC@ -libQFfbdev_la_SOURCES= fbset.c fbset_modes_y.y fbset_modes_l.l \ - in_fbdev.c vid_fbdev.c -libQFfbdev_la_LDFLAGS= @STATIC@ -libQFfbdev_la_LIBADD= $(fbdev_libs) -libQFfbdev_la_DEPENDENCIES= $(fbdev_libs) - - -# -# Simple DirectMedia Library -# -sdl_libs=libvid_common.la libvid_sdl.la -libQFsdl_la_CFLAGS= @PREFER_NON_PIC@ $(SDL_CFLAGS) -libQFsdl_la_SOURCES= vid_sdl.c -libQFsdl_la_LDFLAGS= @STATIC@ -libQFsdl_la_LIBADD= $(sdl_libs) -libQFsdl_la_DEPENDENCIES= $(sdl_libs) - -# -# SVGAlib -# -svga_libs=libvid_common.la libvid_svga.la -libQFsvga_la_CFLAGS= @PREFER_NON_PIC@ $(SVGA_CFLAGS) -libQFsvga_la_SOURCES= vid_svgalib.c -libQFsvga_la_LDFLAGS= @STATIC@ -libQFsvga_la_LIBADD= $(svga_libs) -libQFsvga_la_DEPENDENCIES= $(svga_libs) - -# -# OpenGL in Win32 -# -wgl_libs=libvid_common.la libvid_gl.la -libQFwgl_la_CFLAGS= @PREFER_NON_PIC@ $(WGL_CFLAGS) -libQFwgl_la_SOURCES= in_win.c vid_wgl.c -libQFwgl_la_LDFLAGS= @STATIC@ -libQFwgl_la_LIBADD= $(wgl_libs) -libQFwgl_la_DEPENDENCIES= $(wgl_libs) - -# -# X11 software rendering -# -x11_libs=libvid_common.la libvid_x11.la -libQFx11_la_CFLAGS= @PREFER_NON_PIC@ $(X_CFLAGS) -libQFx11_la_SOURCES= vid_x11.c -libQFx11_la_LDFLAGS= @STATIC@ -libQFx11_la_LIBADD= $(x11_libs) -libQFx11_la_DEPENDENCIES= $(x11_libs) - -# Kill the temp files, hopefully. -CLEANFILES = *.i *.s $(YACCLEX_CLEANFILES) - -EXTRA_DIST= $(fbdev_c) $(fbdev_h) diff --git a/libs/video/targets/Makemodule.am b/libs/video/targets/Makemodule.am new file mode 100644 index 000000000..0dbeb4882 --- /dev/null +++ b/libs/video/targets/Makemodule.am @@ -0,0 +1,131 @@ +#lib_LTLIBRARIES += + +noinst_LTLIBRARIES += @VID_TARGETS@ @vid_libs@ + +EXTRA_LTLIBRARIES += \ + libs/video/targets/libQFfbdev.la \ + libs/video/targets/libQFsvga.la \ + libs/video/targets/libQFx11.la \ + libs/video/targets/libQFsdl.la \ + libs/video/targets/libQFwin.la \ + libs/video/targets/libvid_common.la \ + libs/video/targets/libvid_sdl.la \ + libs/video/targets/libvid_svga.la \ + libs/video/targets/libvid_win.la \ + libs/video/targets/libvid_x11.la + +win_gl_src = libs/video/targets/vid_win_gl.c +win_sw_src = libs/video/targets/vid_win_sw.c +win_vulkan_src = libs/video/targets/vid_win_vulkan.c +if WIN_VULKAN +win_src = $(win_gl_src) $(win_sw_src) $(win_vulkan_src) +else +win_src = $(win_gl_src) $(win_sw_src) +endif + +x11_gl_src = libs/video/targets/vid_x11_gl.c +x11_sw_src = libs/video/targets/vid_x11_sw.c +x11_vulkan_src = libs/video/targets/vid_x11_vulkan.c +if X11_VULKAN +x11_src = $(x11_gl_src) $(x11_sw_src) $(x11_vulkan_src) +else +x11_src = $(x11_gl_src) $(x11_sw_src) +endif + +js_libs = libs/util/libQFutil.la + +libs_video_targets_libvid_common_la_SOURCES = \ + libs/video/targets/vid.c +libs_video_targets_libvid_common_la_CFLAGS= @PREFER_NON_PIC@ +libs_video_targets_libvid_common_la_LDFLAGS= @STATIC@ + +libs_video_targets_libvid_x11_la_SOURCES = \ + libs/video/targets/in_x11.c \ + libs/video/targets/context_x11.c \ + libs/video/targets/dga_check.c \ + $(x11_src) +libs_video_targets_libvid_x11_la_CFLAGS= @PREFER_NON_PIC@ $(X_CFLAGS) +libs_video_targets_libvid_x11_la_LDFLAGS= @STATIC@ +EXTRA_libs_video_targets_libvid_x11_la_SOURCES= $(x11_vulkan_src) + +libs_video_targets_libvid_win_la_SOURCES = \ + libs/video/targets/in_win.c \ + libs/video/targets/context_win.c \ + $(win_src) +libs_video_targets_libvid_win_la_CFLAGS= @PREFER_NON_PIC@ $(X_CFLAGS) +libs_video_targets_libvid_win_la_LDFLAGS= @STATIC@ +EXTRA_libs_video_targets_libvid_win_la_SOURCES= $(win_vulkan_src) + +libs_video_targets_libvid_svga_la_SOURCES= libs/video/targets/in_svgalib.c +libs_video_targets_libvid_svga_la_CFLAGS= @PREFER_NON_PIC@ $(SVGA_CFLAGS) +libs_video_targets_libvid_svga_la_LDFLAGS= @STATIC@ + +libs_video_targets_libvid_sdl_la_SOURCES = \ + libs/video/targets/in_sdl.c \ + libs/video/targets/context_sdl.c \ + libs/video/targets/vid_sdl_gl.c \ + libs/video/targets/vid_sdl_sw.c +libs_video_targets_libvid_sdl_la_CFLAGS= @PREFER_NON_PIC@ $(SDL_CFLAGS) +libs_video_targets_libvid_sdl_la_LDFLAGS= @STATIC@ + +# +# Linux FBdev +# +fbdev_c= libs/video/targets/fbset_modes_y.c libs/video/targets/fbset_modes_l.c +fbdev_h= libs/video/targets/fbset_modes_y.h +BUILT_SOURCES += $(fbdev_c) $(fbdev_h) + +fbdev_libs=libs/video/targets/libvid_common.la +libs_video_targets_libQFfbdev_la_CFLAGS= @PREFER_NON_PIC@ +libs_video_targets_libQFfbdev_la_SOURCES = \ + libs/video/targets/fbset.c \ + libs/video/targets/fbset_modes_y.y \ + libs/video/targets/fbset_modes_l.l \ + libs/video/targets/in_fbdev.c \ + libs/video/targets/vid_fbdev.c +libs_video_targets_libQFfbdev_la_LDFLAGS= @STATIC@ +libs_video_targets_libQFfbdev_la_LIBADD= $(fbdev_libs) +libs_video_targets_libQFfbdev_la_DEPENDENCIES= $(fbdev_libs) + + +# +# Simple DirectMedia Library +# +sdl_libs=libs/video/targets/libvid_common.la libs/video/targets/libvid_sdl.la +libs_video_targets_libQFsdl_la_CFLAGS= @PREFER_NON_PIC@ $(SDL_CFLAGS) +libs_video_targets_libQFsdl_la_SOURCES= libs/video/targets/vid_sdl.c +libs_video_targets_libQFsdl_la_LDFLAGS= @STATIC@ +libs_video_targets_libQFsdl_la_LIBADD= $(sdl_libs) +libs_video_targets_libQFsdl_la_DEPENDENCIES= $(sdl_libs) + +# +# SVGAlib +# +svga_libs=libs/video/targets/libvid_common.la libs/video/targets/libvid_svga.la +libs_video_targets_libQFsvga_la_CFLAGS= @PREFER_NON_PIC@ $(SVGA_CFLAGS) +libs_video_targets_libQFsvga_la_SOURCES= libs/video/targets/vid_svgalib.c +libs_video_targets_libQFsvga_la_LDFLAGS= @STATIC@ +libs_video_targets_libQFsvga_la_LIBADD= $(svga_libs) +libs_video_targets_libQFsvga_la_DEPENDENCIES= $(svga_libs) + +# +# MS Windows +# +win_libs=libs/video/targets/libvid_common.la libs/video/targets/libvid_win.la +libs_video_targets_libQFwin_la_CFLAGS= @PREFER_NON_PIC@ $(WGL_CFLAGS) +libs_video_targets_libQFwin_la_SOURCES= libs/video/targets/vid_win.c +libs_video_targets_libQFwin_la_LDFLAGS= @STATIC@ +libs_video_targets_libQFwin_la_LIBADD= $(win_libs) +libs_video_targets_libQFwin_la_DEPENDENCIES= $(win_libs) + +# +# X11 software rendering +# +x11_libs=libs/video/targets/libvid_common.la libs/video/targets/libvid_x11.la +libs_video_targets_libQFx11_la_CFLAGS= @PREFER_NON_PIC@ $(X_CFLAGS) +libs_video_targets_libQFx11_la_SOURCES= libs/video/targets/vid_x11.c +libs_video_targets_libQFx11_la_LDFLAGS= @STATIC@ +libs_video_targets_libQFx11_la_LIBADD= $(x11_libs) +libs_video_targets_libQFx11_la_DEPENDENCIES= $(x11_libs) + +EXTRA_DIST += $(fbdev_c) $(fbdev_h) diff --git a/libs/video/targets/context_sdl.c b/libs/video/targets/context_sdl.c index 105d7e1f6..2a0851a39 100644 --- a/libs/video/targets/context_sdl.c +++ b/libs/video/targets/context_sdl.c @@ -21,11 +21,6 @@ #include "context_sdl.h" #include "vid_internal.h" -cvar_t *vid_bitdepth; - -extern SDL_Surface *screen; - - void VID_SDL_GammaCheck (void) { @@ -43,33 +38,27 @@ VID_SetCaption (const char *text) if (text && *text) { char *temp = strdup (text); - SDL_WM_SetCaption (va ("%s: %s", PACKAGE_STRING, temp), NULL); + SDL_WM_SetCaption (va (0, "%s: %s", PACKAGE_STRING, temp), NULL); free (temp); } else { - SDL_WM_SetCaption (va ("%s", PACKAGE_STRING), NULL); + SDL_WM_SetCaption (va (0, "%s", PACKAGE_STRING), NULL); } } -qboolean +bool VID_SetGamma (double gamma) { return SDL_SetGamma((float) gamma, (float) gamma, (float) gamma); } -void -VID_Shutdown (void) -{ - SDL_Quit (); -} - static void -VID_UpdateFullscreen (cvar_t *vid_fullscreen) +VID_UpdateFullscreen (void *data, const cvar_t *cvar) { if (!r_data || !viddef.initialized) return; - if ((vid_fullscreen->int_val && !(screen->flags & SDL_FULLSCREEN)) - || (!vid_fullscreen->int_val && screen->flags & SDL_FULLSCREEN)) - if (!SDL_WM_ToggleFullScreen (screen)) + if ((cvar && !(sdl_screen->flags & SDL_FULLSCREEN)) + || (!cvar && sdl_screen->flags & SDL_FULLSCREEN)) + if (!SDL_WM_ToggleFullScreen (sdl_screen)) Sys_Printf ("VID_UpdateFullscreen: error setting fullscreen\n"); IN_UpdateGrab (in_grab); } @@ -77,12 +66,6 @@ VID_UpdateFullscreen (cvar_t *vid_fullscreen) void SDL_Init_Cvars (void) { - vid_fullscreen = Cvar_Get ("vid_fullscreen", "0", CVAR_ARCHIVE, - VID_UpdateFullscreen, - "Toggles fullscreen mode"); - vid_system_gamma = Cvar_Get ("vid_system_gamma", "1", CVAR_ARCHIVE, NULL, - "Use system gamma control if available"); -// FIXME: vid_colorbpp in common GL setup, make consistent with sdl32 scheme - vid_bitdepth = Cvar_Get ("vid_bitdepth", "8", CVAR_ROM, NULL, "Sets " - "display bitdepth (supported modes: 8 16 32)"); + Cvar_Register (&vid_fullscreen_cvar, VID_UpdateFullscreen, 0); + Cvar_Register (&vid_system_gamma_cvar, 0, 0); } diff --git a/libs/video/targets/context_win.c b/libs/video/targets/context_win.c new file mode 100644 index 000000000..fb037692d --- /dev/null +++ b/libs/video/targets/context_win.c @@ -0,0 +1,1170 @@ +/* + vid_win.c + + Win32 vid component + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "winquake.h" +#include + +#include "QF/cmd.h" +#include "QF/cvar.h" +#include "QF/input.h" +#include "QF/qargs.h" +#include "QF/sys.h" +#include "QF/va.h" +#include "QF/vid.h" + +#include "context_win.h" +#include "r_shared.h" +#include "vid_internal.h" +#include "vid_sw.h" + +HWND win_mainwindow; +HDC win_maindc; +int win_palettized; +int win_minimized; +int win_canalttab = 0; +sw_ctx_t *win_sw_context; + +#define MODE_WINDOWED 0 +#define MODE_SETTABLE_WINDOW 2 +#define NO_MODE (MODE_WINDOWED - 1) +#define MODE_FULLSCREEN_DEFAULT (MODE_WINDOWED + 3) + +int vid_ddraw; +static cvar_t vid_ddraw_cvar = { + .name = "vid_ddraw", + .description = + "", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &vid_ddraw }, +}; + +// Note that 0 is MODE_WINDOWED +static int vid_mode; +static cvar_t vid_mode_cvar = { + .name = "vid_mode", + .description = + "", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &vid_mode }, +}; + +// Note that 0 is MODE_WINDOWED +int _vid_default_mode; +static cvar_t _vid_default_mode_cvar = { + .name = "_vid_default_mode", + .description = + "", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &_vid_default_mode }, +}; + +// Note that 3 is MODE_FULLSCREEN_DEFAULT +static int _vid_default_mode_win; +static cvar_t _vid_default_mode_win_cvar = { + .name = "_vid_default_mode_win", + .description = + "", + .default_value = "3", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &_vid_default_mode_win }, +}; +int vid_wait; +static cvar_t vid_wait_cvar = { + .name = "vid_wait", + .description = + "", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &vid_wait }, +}; +int vid_nopageflip; +static cvar_t vid_nopageflip_cvar = { + .name = "vid_nopageflip", + .description = + "", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &vid_nopageflip }, +}; +int _vid_wait_override; +static cvar_t _vid_wait_override_cvar = { + .name = "_vid_wait_override", + .description = + "", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &_vid_wait_override }, +}; +int vid_config_x; +static cvar_t vid_config_x_cvar = { + .name = "vid_config_x", + .description = + "", + .default_value = "800", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &vid_config_x }, +}; +int vid_config_y; +static cvar_t vid_config_y_cvar = { + .name = "vid_config_y", + .description = + "", + .default_value = "600", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &vid_config_y }, +}; +int vid_stretch_by_2; +static cvar_t vid_stretch_by_2_cvar = { + .name = "vid_stretch_by_2", + .description = + "", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &vid_stretch_by_2 }, +}; +static int _windowed_mouse; +static cvar_t _windowed_mouse_cvar = { + .name = "_windowed_mouse", + .description = + "", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &_windowed_mouse }, +}; +static int vid_fullscreen_mode; +static cvar_t vid_fullscreen_mode_cvar = { + .name = "vid_fullscreen_mode", + .description = + "", + .default_value = "3", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &vid_fullscreen_mode }, +}; +static int vid_windowed_mode; +static cvar_t vid_windowed_mode_cvar = { + .name = "vid_windowed_mode", + .description = + "", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &vid_windowed_mode }, +}; +int block_switch; +static cvar_t block_switch_cvar = { + .name = "block_switch", + .description = + "", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &block_switch }, +}; +static int vid_window_x; +static cvar_t vid_window_x_cvar = { + .name = "vid_window_x", + .description = + "", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &vid_window_x }, +}; +static int vid_window_y; +static cvar_t vid_window_y_cvar = { + .name = "vid_window_y", + .description = + "", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &vid_window_y }, +}; + +//FIXME?int yeahimconsoled; + +#define MAX_MODE_LIST 36 +#define VID_ROW_SIZE 3 + +static DWORD WindowStyle, ExWindowStyle; + +int win_center_x, win_center_y; +RECT win_rect; + +DEVMODE win_gdevmode; +static bool startwindowed = 0; +//static bool windowed_mode_set; +//static int vid_fulldib_on_focus_mode; +static bool force_minimized; +static bool force_mode_set; +static bool vid_mode_set; +//static HICON hIcon; + + +int vid_modenum = NO_MODE; +int vid_testingmode, vid_realmode; +double vid_testendtime; +int vid_default = MODE_WINDOWED; +static int windowed_default; + +modestate_t modestate = MS_UNINIT; + +byte vid_curpal[256 * 3]; + +int mode; + +typedef struct { + modestate_t type; + int width; + int height; + int modenum; + int fullscreen; + char modedesc[13]; +} vmode_t; + +static vmode_t modelist[MAX_MODE_LIST] = { + { + .type = MS_WINDOWED, + .width = 320, + .height = 240, + .modedesc = "320x240", + .modenum = MODE_WINDOWED, + .fullscreen = 0, + }, + { + .type = MS_WINDOWED, + .width = 640, + .height = 480, + .modedesc = "640x480", + .modenum = MODE_WINDOWED + 1, + .fullscreen = 0, + }, + { + .type = MS_WINDOWED, + .width = 800, + .height = 600, + .modedesc = "800x600", + .modenum = MODE_WINDOWED + 2, + .fullscreen = 0, + } +}; +static int nummodes; + +int aPage; // Current active display page +int vPage; // Current visible display page +int waitVRT = true; // True to wait for retrace on flip + +static vmode_t badmode = { + .modedesc = "Bad mode", +}; + +static int VID_SetMode (int modenum, const byte *palette); + +static void __attribute__ ((used)) +VID_RememberWindowPos (void) +{ + RECT rect; + + if (GetWindowRect (win_mainwindow, &rect)) { + if ((rect.left < GetSystemMetrics (SM_CXSCREEN)) && + (rect.top < GetSystemMetrics (SM_CYSCREEN)) && + (rect.right > 0) && (rect.bottom > 0)) { + vid_window_x = rect.left; + vid_window_y = rect.top; + } + } +} + +#if 0 +static void +VID_CheckWindowXY (void) +{ + if ((vid_window_x > (GetSystemMetrics (SM_CXSCREEN) - 160)) || + (vid_window_y > (GetSystemMetrics (SM_CYSCREEN) - 120)) || + (vid_window_x < 0) || (vid_window_y < 0)) { + vid_window_x = 0.0; + vid_window_y = 0.0; + } +} +#endif + +void +Win_UpdateWindowStatus (int window_x, int window_y) +{ + win_rect.left = window_x; + win_rect.top = window_y; + win_rect.right = window_x + viddef.width; + win_rect.bottom = window_y + viddef.height; + win_center_x = (win_rect.left + win_rect.right) / 2; + win_center_y = (win_rect.top + win_rect.bottom) / 2; + IN_UpdateClipCursor (); +} + + +static bool +VID_CheckAdequateMem (int width, int height) +{ + // there will always be enough ;) + return true; +} + + +static void +VID_InitModes (HINSTANCE hInstance) +{ + WNDCLASS wc; + HDC hdc; + +//FIXME hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ICON2)); + + /* Register the frame class */ + wc.style = CS_OWNDC; + wc.lpfnWndProc = (WNDPROC) MainWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = 0; + wc.hCursor = LoadCursor (NULL, IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = 0; + wc.lpszClassName = "WinQuake"; + + if (!RegisterClass (&wc)) + Sys_Error ("Couldn't register window class"); + + // automatically stretch the default mode up if > 640x480 desktop + // resolution + hdc = GetDC (NULL); + + if ((GetDeviceCaps (hdc, HORZRES) > 800) + && !COM_CheckParm ("-noautostretch")) { + vid_default = MODE_WINDOWED + 2; + } else if ((GetDeviceCaps (hdc, HORZRES) > 640) + && !COM_CheckParm ("-noautostretch")) { + vid_default = MODE_WINDOWED + 1; + } else { + vid_default = MODE_WINDOWED; + } + + // always start at the lowest mode then switch to the higher one if + // selected + vid_default = MODE_WINDOWED; + + windowed_default = vid_default; + ReleaseDC (NULL, hdc); + nummodes = 3; // reserve space for windowed mode +} + + +static void +VID_GetDisplayModes (void) +{ + DEVMODE devmode; + int i, modenum, existingmode, originalnummodes, lowestres; + BOOL stat; + + // enumerate > 8 bpp modes + originalnummodes = nummodes; + modenum = 0; + lowestres = 99999; + + do { + stat = EnumDisplaySettings (NULL, modenum, &devmode); + + if ((devmode.dmPelsWidth <= MAXWIDTH) + && (devmode.dmPelsHeight <= MAXHEIGHT) + && (devmode.dmPelsWidth >= 320) + && (devmode.dmPelsHeight >= 240) + && (nummodes < MAX_MODE_LIST)) { + devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + + if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == + DISP_CHANGE_SUCCESSFUL) { + modelist[nummodes].type = MS_FULLDIB; + modelist[nummodes].width = devmode.dmPelsWidth; + modelist[nummodes].height = devmode.dmPelsHeight; + modelist[nummodes].modenum = 0; + modelist[nummodes].fullscreen = 1; + sprintf (modelist[nummodes].modedesc, "%dx%d", + (int) devmode.dmPelsWidth, + (int) devmode.dmPelsHeight); + + // see is the mode already there + // (same dimensions but different refresh rate) + for (i = originalnummodes, existingmode = 0; + i < nummodes; i++) { + if ((modelist[nummodes].width == modelist[i].width) + && (modelist[nummodes].height == modelist[i].height)) { + existingmode = 1; + break; + } + } + + // if it's not add it to the list + if (!existingmode) { + if (modelist[nummodes].width < lowestres) + lowestres = modelist[nummodes].width; + + nummodes++; + } + } + } + + modenum++; + } while (stat); + + if (nummodes != originalnummodes) + vid_default = MODE_FULLSCREEN_DEFAULT; + else + Sys_Printf ("No fullscreen DIB modes found\n"); +} + +void +Win_OpenDisplay (void) +{ + VID_InitModes (global_hInstance); + VID_GetDisplayModes (); + + vid_testingmode = 0; + + // if (COM_CheckParm("-startwindowed")) + { + startwindowed = 1; + vid_default = windowed_default; + } + +#ifdef SPLASH_SCREEN + if (hwnd_dialog) + DestroyWindow (hwnd_dialog); +#endif +} + +void +Win_CloseDisplay (void) +{ + if (viddef.initialized) { + if (modestate == MS_FULLDIB) { + ChangeDisplaySettings (NULL, CDS_FULLSCREEN); + } + + PostMessage (HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM) win_mainwindow, + (LPARAM) 0); + PostMessage (HWND_BROADCAST, WM_SYSCOLORCHANGE, (WPARAM) 0, (LPARAM) 0); + Win_Activate (false, false); + +//FIXME? if (hwnd_dialog) DestroyWindow (hwnd_dialog); + if (win_mainwindow) + DestroyWindow (win_mainwindow); + + vid_testingmode = 0; + viddef.initialized = false; + } +} + +void +Win_SetVidMode (int width, int height) +{ +//FIXME SCR_StretchInit(); + + force_mode_set = true; + //VID_SetMode (vid_default, palette); + force_mode_set = false; + vid_realmode = vid_modenum; +} +#if 0 +static void +VID_DestroyWindow (void) +{ + if (modestate == MS_FULLDIB) + ChangeDisplaySettings (NULL, CDS_FULLSCREEN); + + Win_UnloadAllDrivers (); +} +#endif +static void +VID_CheckModedescFixup (int mode) +{ +} + +static bool +VID_SetWindowedMode (int modenum) +{ +#if 0 + if (!windowed_mode_set) { + if (COM_CheckParm ("-resetwinpos")) { + vid_window_x = 0.0; + vid_window_y = 0.0; + } + + windowed_mode_set = true; + } + + VID_CheckModedescFixup (modenum); + VID_DestroyWindow (); + + DIBWidth = modelist[modenum].width; + DIBHeight = modelist[modenum].height; + + WindowStyle = WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_SYSMENU | WS_SIZEBOX | + WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPSIBLINGS | + WS_CLIPCHILDREN | WS_THICKFRAME; + + // WindowStyle = WS_OVERLAPPEDWINDOW|WS_VISIBLE; + ExWindowStyle = 0; + + // the first time we're called to set the mode, create the window we'll use + // for the rest of the session + if (!vid_mode_set) { + } else { + SetWindowLong (win_mainwindow, GWL_STYLE, WindowStyle | WS_VISIBLE); + SetWindowLong (win_mainwindow, GWL_EXSTYLE, ExWindowStyle); + } + + if (!SetWindowPos (win_mainwindow, + NULL, + 0, 0, + WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, + SWP_NOCOPYBITS | SWP_NOZORDER | SWP_HIDEWINDOW)) { + Sys_Error ("Couldn't resize DIB window"); + } + + // position and show the DIB window + VID_CheckWindowXY (); + SetWindowPos (win_mainwindow, NULL, vid_window_x, + vid_window_y, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); + + if (force_minimized) + ShowWindow (win_mainwindow, SW_MINIMIZE); + else + ShowWindow (win_mainwindow, SW_SHOWDEFAULT); + + UpdateWindow (win_mainwindow); + modestate = MS_WINDOWED; + vid_fulldib_on_focus_mode = 0; + + viddef.numpages = 1; + +// viddef.height = viddef.conheight = DIBHeight; +// viddef.width = viddef.conwidth = DIBWidth; + + viddef.height = viddef.conheight = DIBHeight; + viddef.width = viddef.conwidth = DIBWidth; +//FIXME? if (!yeahimconsoled){ +//FIXME? viddef.vconheight = DIBHeight; +//FIXME? viddef.vconwidth = DIBWidth; +//FIXME? } + SendMessage (win_mainwindow, WM_SETICON, (WPARAM) TRUE, (LPARAM) hIcon); + SendMessage (win_mainwindow, WM_SETICON, (WPARAM) FALSE, (LPARAM) hIcon); +#endif + return true; +} + + +static bool +VID_SetFullDIBMode (int modenum) +{ +#if 0 + VID_DestroyWindow (); + + win_gdevmode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; + win_gdevmode.dmPelsWidth = modelist[modenum].width; + win_gdevmode.dmPelsHeight = modelist[modenum].height; + win_gdevmode.dmSize = sizeof (win_gdevmode); + + if (ChangeDisplaySettings (&win_gdevmode, CDS_FULLSCREEN) != + DISP_CHANGE_SUCCESSFUL) + Sys_Error ("Couldn't set fullscreen DIB mode"); + + modestate = MS_FULLDIB; + vid_fulldib_on_focus_mode = modenum; + + WindowRect.top = WindowRect.left = 0; + WindowRect.right = modelist[modenum].width; + WindowRect.bottom = modelist[modenum].height; + + DIBWidth = modelist[modenum].width; + DIBHeight = modelist[modenum].height; + + WindowStyle = WS_POPUP | WS_SYSMENU | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + ExWindowStyle = 0; + + AdjustWindowRectEx (&WindowRect, WindowStyle, FALSE, 0); + + SetWindowLong (win_mainwindow, GWL_STYLE, WindowStyle | WS_VISIBLE); + SetWindowLong (win_mainwindow, GWL_EXSTYLE, ExWindowStyle); + + if (!SetWindowPos (win_mainwindow, + NULL, + 0, 0, + WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, + SWP_NOCOPYBITS | SWP_NOZORDER)) { + Sys_Error ("Couldn't resize DIB window"); + } + // position and show the DIB window + SetWindowPos (win_mainwindow, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOSIZE | SWP_SHOWWINDOW | SWP_DRAWFRAME); + ShowWindow (win_mainwindow, SW_SHOWDEFAULT); + UpdateWindow (win_mainwindow); + + viddef.numpages = 1; + +#ifdef SCALED2D + viddef.height = viddef.conheight = DIBHeight; + viddef.width = viddef.conwidth = DIBWidth; + // viddef.vconwidth = 320; + // viddef.vconheight = 200; +//FIXME? if (!yeahimconsoled){ +//FIXME? viddef.vconheight = DIBHeight; +//FIXME? viddef.vconwidth = DIBWidth; +//FIXME? } +#else + viddef.height = viddef.conheight = DIBHeight; + viddef.width = viddef.conwidth = DIBWidth; +#endif +#endif + return true; +} + +static void +VID_RestoreOldMode (int original_mode) +{ + static bool inerror = false; + + if (inerror) + return; + + inerror = true; + // make sure mode set happens (video mode changes) + vid_modenum = original_mode - 1; + + if (!VID_SetMode (original_mode, vid_curpal)) { + vid_modenum = MODE_WINDOWED - 1; + + if (!VID_SetMode (windowed_default, vid_curpal)) + Sys_Error ("Can't set any video mode"); + } + + inerror = false; +} + + +static void __attribute__ ((used)) +VID_SetDefaultMode (void) +{ + if (viddef.initialized) + VID_SetMode (0, vid_curpal); + + IN_DeactivateMouse (); +} + +static vmode_t * +VID_GetModePtr (int modenum) +{ + if ((modenum >= 0) && (modenum < nummodes)) + return &modelist[modenum]; + else + return &badmode; +} + +static char * +VID_GetModeDescription (int mode) +{ + char *pinfo; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + VID_CheckModedescFixup (mode); + pv = VID_GetModePtr (mode); + pinfo = pv->modedesc; + return pinfo; +} + +static int +VID_SetMode (int modenum, const byte *palette) +{ + int original_mode; // FIXME, temp; + bool stat; + MSG msg; + HDC hdc; + + while ((modenum >= nummodes) || (modenum < 0)) { + if (vid_modenum == NO_MODE) { + if (modenum == vid_default) { + modenum = windowed_default; + } else { + modenum = vid_default; + } + + vid_mode = modenum; + } else { + vid_mode = vid_modenum; + return 0; + } + } + + if (!force_mode_set && (modenum == vid_modenum)) + return true; + +//FIXME CDAudio_Pause (); +//FIXME S_ClearBuffer (); + + if (vid_modenum == NO_MODE) + original_mode = windowed_default; + else + original_mode = vid_modenum; + + // Set either the fullscreen or windowed mode + if (modelist[modenum].type == MS_WINDOWED) { + if (_windowed_mouse) { + stat = VID_SetWindowedMode (modenum); + IN_ActivateMouse (); + IN_HideMouse (); + } else { + IN_DeactivateMouse (); + IN_ShowMouse (); + stat = VID_SetWindowedMode (modenum); + } + } else { + stat = VID_SetFullDIBMode (modenum); + IN_ActivateMouse (); + IN_HideMouse (); + } + + Win_UpdateWindowStatus (0, 0); // FIXME right numbers? +//FIXME CDAudio_Resume (); + + if (!stat) { + VID_RestoreOldMode (original_mode); + return false; + } + + // now we try to make sure we get the focus on the mode switch, because + // sometimes in some systems we don't. We grab the foreground, then + // finish setting up, pump all our messages, and sleep for a little while + // to let messages finish bouncing around the system, then we put + // ourselves at the top of the z order, then grab the foreground again, + // Who knows if it helps, but it probably doesn't hurt + if (!force_minimized) + SetForegroundWindow (win_mainwindow); + + hdc = GetDC (NULL); + if (GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) { + win_palettized = true; + } else { + win_palettized = false; + } + ReleaseDC (NULL, hdc); + + vid_modenum = modenum; + vid_mode = vid_modenum; + + while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + + Sleep (100); + + if (!force_minimized) { + SetWindowPos (win_mainwindow, HWND_TOP, 0, 0, 0, 0, + SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | + SWP_NOCOPYBITS); + + SetForegroundWindow (win_mainwindow); + } + // fix the leftover Alt from any Alt-Tab or the like that switched us away + IN_ClearStates (); + + Sys_Printf ("%s\n", VID_GetModeDescription (vid_modenum)); + + viddef.recalc_refdef = 1; + +//FIXME SCR_StretchInit(); +//FIXME SCR_StretchRefresh(); +//FIXME SCR_CvarCheck(); + return true; +} + + +void +Win_CreateWindow (int width, int height) +{ + RECT rect = { + .top = 0, + .left = 0, + .right = width, + .bottom = height, + }; + AdjustWindowRectEx (&rect, WindowStyle, FALSE, 0); + // sound initialization has to go here, preceded by a windowed mode set, + // so there's a window for DirectSound to work with but we're not yet + // fullscreen so the "hardware already in use" dialog is visible if it + // gets displayed + // keep the window minimized until we're ready for the first real mode set + win_mainwindow = CreateWindowEx (ExWindowStyle, + "WinQuake", + "WinQuake", + WindowStyle, + 0, 0, + rect.right - rect.left, + rect.bottom - rect.top, + NULL, NULL, global_hInstance, NULL); + + if (!win_mainwindow) + Sys_Error ("Couldn't create DIB window"); + + // done + vid_mode_set = true; +//FIXME if (firsttime) S_Init (); + Win_UpdateWindowStatus (0, 0); // FIXME right numbers? + + HDC hdc = GetDC (NULL); + if (GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) { + win_palettized = true; + } else { + win_palettized = false; + } + ReleaseDC (NULL, hdc); + + //vid_modenum = modenum; + //Cvar_SetValue (vid_mode, (float) vid_modenum); + + MSG msg; + while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + + Sleep (100); + + if (!force_minimized) { + SetWindowPos (win_mainwindow, HWND_TOP, 0, 0, 0, 0, + SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | + SWP_NOCOPYBITS); + + SetForegroundWindow (win_mainwindow); + } + // fix the leftover Alt from any Alt-Tab or the like that switched us away + IN_ClearStates (); + + Sys_Printf ("%s\n", VID_GetModeDescription (vid_modenum)); + + viddef.recalc_refdef = 1; +} + + + +//========================================================================== + + +/* +================ +VID_HandlePause +================ +*/ +static void __attribute__ ((used)) +VID_HandlePause (bool pause) +{ + if ((modestate == MS_WINDOWED) && _windowed_mouse) { + if (pause) { + IN_DeactivateMouse (); + IN_ShowMouse (); + } else { + IN_ActivateMouse (); + IN_HideMouse (); + } + } +} + + +/* +=================================================================== + +MAIN WINDOW + +=================================================================== +*/ + +typedef struct { + int modenum; + char *desc; + int iscur; + int width; +} modedesc_t; + +#define MAX_COLUMN_SIZE 5 +#define MODE_AREA_HEIGHT (MAX_COLUMN_SIZE + 6) +#define MAX_MODEDESCS (MAX_COLUMN_SIZE*3) + +//static modedesc_t modedescs[MAX_MODEDESCS]; + +static int +VID_NumModes (void) +{ + return nummodes; +} + +static char * __attribute__((used)) +VID_GetModeDescriptionMemCheck (int mode) +{ + char *pinfo; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + VID_CheckModedescFixup (mode); + pv = VID_GetModePtr (mode); + pinfo = pv->modedesc; + + if (VID_CheckAdequateMem (pv->width, pv->height)) { + return pinfo; + } else { + return NULL; + } +} + + +// Tacks on "windowed" or "fullscreen" +static const char * __attribute__((used)) +VID_GetModeDescription2 (int mode) +{ + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + VID_CheckModedescFixup (mode); + pv = VID_GetModePtr (mode); + + if (modelist[mode].type == MS_FULLSCREEN) { + return va (0, "%s fullscreen", pv->modedesc); + } else if (modelist[mode].type == MS_FULLDIB) { + return va (0, "%s fullscreen", pv->modedesc); + } else { + return va (0, "%s windowed", pv->modedesc); + } +} + + +// KJB: Added this to return the mode driver name in description for console +static char * +VID_GetExtModeDescription (int mode) +{ + static char pinfo[40]; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + VID_CheckModedescFixup (mode); + pv = VID_GetModePtr (mode); + + if (modelist[mode].type == MS_FULLDIB) { + sprintf (pinfo, "%s fullscreen", pv->modedesc); + } else { + sprintf (pinfo, "%s windowed", pv->modedesc); + } + + return pinfo; +} +static void +VID_DescribeCurrentMode_f (void) +{ + Sys_Printf ("%s\n", VID_GetExtModeDescription (vid_modenum)); +} + +static void +VID_NumModes_f (void) +{ + if (nummodes == 1) + Sys_Printf ("%d video mode is available\n", nummodes); + else + Sys_Printf ("%d video modes are available\n", nummodes); +} + + +static void +VID_DescribeMode_f (void) +{ + int modenum; + + modenum = atoi (Cmd_Argv (1)); + Sys_Printf ("%s\n", VID_GetExtModeDescription (modenum)); +} + + +static void +VID_DescribeModes_f (void) +{ + int i, lnummodes; + char *pinfo; + bool na; + vmode_t *pv; + + na = false; + lnummodes = VID_NumModes (); + + for (i = 0; i < lnummodes; i++) { + pv = VID_GetModePtr (i); + pinfo = VID_GetExtModeDescription (i); + + if (VID_CheckAdequateMem (pv->width, pv->height)) { + Sys_Printf ("%2d: %s\n", i, pinfo); + } else { + Sys_Printf ("**: %s\n", pinfo); + na = true; + } + } + + if (na) { + Sys_Printf ("\n[**: not enough system RAM for mode]\n"); + } +} + +static void +VID_TestMode_f (void) +{ + int modenum; + double testduration; + + if (!vid_testingmode) { + modenum = atoi (Cmd_Argv (1)); + + if (VID_SetMode (modenum, vid_curpal)) { + vid_testingmode = 1; + testduration = atof (Cmd_Argv (2)); + + if (testduration == 0) + testduration = 5.0; + + vid_testendtime = Sys_DoubleTime () + testduration; + } + } +} + +static void +VID_Windowed_f (void) +{ + VID_SetMode (vid_windowed_mode, vid_curpal); +} + +static void +VID_Fullscreen_f (void) +{ + VID_SetMode (vid_fullscreen_mode, vid_curpal); +} + +static void +VID_Minimize_f (void) +{ + // we only support minimizing windows; if you're fullscreen, + // switch to windowed first + if (modestate == MS_WINDOWED) + ShowWindow (win_mainwindow, SW_MINIMIZE); +} + +static void +VID_ForceMode_f (void) +{ + int modenum; + + if (!vid_testingmode) { + modenum = atoi (Cmd_Argv (1)); + force_mode_set = 1; + VID_SetMode (modenum, vid_curpal); + force_mode_set = 0; + } +} + +void +Win_SetCaption (const char *text) +{ + if (win_mainwindow) { + SetWindowText (win_mainwindow, text); + } +} + +//static WORD systemgammaramps[3][256]; +static WORD currentgammaramps[3][256]; + +bool +Win_SetGamma (double gamma) +{ + int i; + HDC hdc = GetDC (NULL); + + for (i = 0; i < 256; i++) { + currentgammaramps[2][i] = currentgammaramps[1][i] = + currentgammaramps[0][i] = viddef.gammatable[i] * 256; + } + + i = SetDeviceGammaRamp (hdc, ¤tgammaramps[0][0]); + ReleaseDC (NULL, hdc); + return i; +} + +void +Win_Init_Cvars (void) +{ + Cvar_Register (&vid_ddraw_cvar, 0, 0); + Cvar_Register (&vid_mode_cvar, 0, 0); + Cvar_Register (&vid_wait_cvar, 0, 0); + Cvar_Register (&vid_nopageflip_cvar, 0, 0); + Cvar_Register (&_vid_wait_override_cvar, 0, 0); + Cvar_Register (&_vid_default_mode_cvar, 0, 0); + Cvar_Register (&_vid_default_mode_win_cvar, 0, 0); + Cvar_Register (&vid_config_x_cvar, 0, 0); + Cvar_Register (&vid_config_y_cvar, 0, 0); + Cvar_Register (&vid_stretch_by_2_cvar, 0, 0); + Cvar_Register (&_windowed_mouse_cvar, 0, 0); + Cvar_Register (&vid_fullscreen_mode_cvar, 0, 0); + Cvar_Register (&vid_windowed_mode_cvar, 0, 0); + Cvar_Register (&block_switch_cvar, 0, 0); + Cvar_Register (&vid_window_x_cvar, 0, 0); + Cvar_Register (&vid_window_y_cvar, 0, 0); + + Cmd_AddCommand ("vid_testmode", VID_TestMode_f, ""); + Cmd_AddCommand ("vid_nummodes", VID_NumModes_f, ""); + Cmd_AddCommand ("vid_describecurrentmode", VID_DescribeCurrentMode_f, ""); + Cmd_AddCommand ("vid_describemode", VID_DescribeMode_f, ""); + Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f, ""); + Cmd_AddCommand ("vid_forcemode", VID_ForceMode_f, ""); + Cmd_AddCommand ("vid_windowed", VID_Windowed_f, ""); + Cmd_AddCommand ("vid_fullscreen", VID_Fullscreen_f, ""); + Cmd_AddCommand ("vid_minimize", VID_Minimize_f, ""); +} + +extern int win_force_link; +static __attribute__((used)) int *context_win_force_link = &win_force_link; diff --git a/libs/video/targets/context_x11.c b/libs/video/targets/context_x11.c index dc9ae9a0c..63b66222d 100644 --- a/libs/video/targets/context_x11.c +++ b/libs/video/targets/context_x11.c @@ -55,11 +55,6 @@ #ifdef HAVE_VIDMODE # include -# ifdef DGA_OLD_HEADERS -# include -# else -# include -# endif #endif #include "QF/cmd.h" @@ -71,12 +66,15 @@ #include "QF/va.h" #include "QF/vid.h" +#include "QF/input/event.h" + #include "context_x11.h" #include "dga_check.h" +#include "in_x11.h" #include "vid_internal.h" static void (*event_handlers[LASTEvent]) (XEvent *); -qboolean oktodraw = false; +bool oktodraw = false; int x_shmeventtype; static int x_disp_ref_count = 0; @@ -84,6 +82,8 @@ static Cursor nullcursor = None; Display *x_disp = NULL; int x_screen; +int x_width; +int x_height; Window x_root = None; XVisualInfo *x_visinfo; Visual *x_vis; @@ -92,19 +92,19 @@ Colormap x_cmap; Time x_time; Time x_mouse_time; -qboolean x_have_focus = false; +bool x_have_focus = false; #ifdef HAVE_VIDMODE static XF86VidModeModeInfo **vidmodes; static int nummodes; static int original_mode = 0; static vec3_t x_gamma = {-1, -1, -1}; -static qboolean vidmode_avail = false; +static bool vidmode_avail = false; #endif -static qboolean vidmode_active = false; +static bool vidmode_active = false; -static qboolean vid_context_created = false; +static bool vid_context_created = false; static int pos_x, pos_y; #ifdef HAVE_VIDMODE @@ -114,7 +114,7 @@ static int xss_blanking; static int xss_exposures; #endif -static qboolean accel_saved = false; +static bool accel_saved = false; static int accel_numerator; static int accel_denominator; static int accel_threshold; @@ -125,6 +125,17 @@ static int accel_threshold; static Atom x_net_state; static Atom x_net_fullscreen; +static int x11_vidmode; +static cvar_t x11_vidmode_cvar = { + .name = "x11_vidmode", + .description = + "Use x11 vidmode extension to set video mode (not recommended for " + "modern systems)", + .default_value = "0", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &x11_vidmode }, +}; + static void set_fullscreen (int full) { @@ -160,19 +171,31 @@ configure_notify (XEvent *event) if (vidmode_active) X11_ForceViewPort (); #endif - Sys_MaskPrintf (SYS_VID, + Sys_MaskPrintf (SYS_vid, "ConfigureNotify: %ld %d %ld %ld %d,%d (%d,%d) " "%d %ld %d\n", c->serial, c->send_event, c->event, c->window, c->x, c->y, c->width, c->height, c->border_width, c->above, c->override_redirect); + IE_event_t ie_event = { + .type = ie_app_window, + .when = Sys_LongTime (), + .app_window = { + .xpos = c->x, + .ypos = c->y, + .xlen = c->width, + .ylen = c->height, + }, + }; + IE_Send_Event (&ie_event); + VID_SetWindowSize (c->width, c->height); } -qboolean +bool X11_AddEvent (int event, void (*event_handler) (XEvent *)) { if (event >= LASTEvent) { - Sys_MaskPrintf (SYS_VID, "event: %d, LASTEvent: %d\n", event, + Sys_MaskPrintf (SYS_vid, "event: %d, LASTEvent: %d\n", event, LASTEvent); return false; } @@ -184,7 +207,7 @@ X11_AddEvent (int event, void (*event_handler) (XEvent *)) return true; } -qboolean +bool X11_RemoveEvent (int event, void (*event_handler) (XEvent *)) { if (event >= LASTEvent) @@ -272,6 +295,8 @@ X11_OpenDisplay (void) False); x_screen = DefaultScreen (x_disp); + x_width = DisplayWidth (x_disp, x_screen); + x_height = DisplayHeight (x_disp, x_screen); x_root = RootWindow (x_disp, x_screen); XSynchronize (x_disp, true); // only for debugging @@ -309,16 +334,16 @@ void X11_CreateNullCursor (void) { Pixmap cursormask; - XGCValues xgc; + XGCValues xgc = { }; GC gc; - XColor dummycolour; + XColor dummycolour = { }; if (nullcursor != None) return; cursormask = XCreatePixmap (x_disp, x_root, 1, 1, 1); - xgc.function = GXclear; + xgc.function = GXclear; gc = XCreateGC (x_disp, cursormask, GCFunction, &xgc); XFillRectangle (x_disp, cursormask, gc, 0, 0, 1, 1); @@ -333,29 +358,6 @@ X11_CreateNullCursor (void) XDefineCursor (x_disp, x_win, nullcursor); } -static Bool -check_mouse_event (Display *disp, XEvent *ev, XPointer arg) -{ - XMotionEvent *me = &ev->xmotion; - if (ev->type != MotionNotify) - return False; - if (me->x != viddef.width / 2 || me->y != viddef.height / 2) - return False; - return True; -} - -static void -X11_SetMouse (void) -{ - XEvent ev; - - XWarpPointer (x_disp, None, x_win, 0, 0, 0, 0, 0, 0); - XWarpPointer (x_disp, None, x_win, 0, 0, 0, 0, - viddef.width / 2, viddef.height / 2); - XPeekIfEvent (x_disp, &ev, check_mouse_event, 0); - x_mouse_time = ev.xmotion.time; -} - #ifdef HAVE_VIDMODE static vec3_t * X11_GetGamma (void) @@ -363,7 +365,7 @@ X11_GetGamma (void) XF86VidModeGamma xgamma; vec3_t *temp; - if (vid_gamma_avail && vid_system_gamma->int_val) { + if (vid_gamma_avail && vid_system_gamma) { if (XF86VidModeGetGamma (x_disp, x_screen, &xgamma)) { if ((temp = malloc (sizeof (vec3_t)))) { (*temp)[0] = xgamma.red; @@ -387,8 +389,12 @@ X11_SetVidMode (int width, int height) if (vidmode_active) return; + if (!x11_vidmode) { + return; + } + if (str && (tolower (*str) == 'f')) { - Cvar_Set (vid_fullscreen, "1"); + Cvar_Set ("vid_fullscreen", "1"); } #ifdef HAVE_VIDMODE @@ -406,7 +412,7 @@ X11_SetVidMode (int width, int height) vid_gamma_avail = true; temp = X11_GetGamma (); - if (temp && temp[0] > 0) { + if (temp && (*temp)[0] > 0) { x_gamma[0] = (*temp)[0]; x_gamma[1] = (*temp)[1]; x_gamma[2] = (*temp)[2]; @@ -416,29 +422,33 @@ X11_SetVidMode (int width, int height) } - if (vid_fullscreen->int_val && vidmode_avail) { + if (vid_fullscreen && vidmode_avail) { int i, dotclock; int best_mode = 0; - qboolean found_mode = false; + bool found_mode = false; XF86VidModeModeLine orig_data; XF86VidModeGetAllModeLines (x_disp, x_screen, &nummodes, &vidmodes); XF86VidModeGetModeLine (x_disp, x_screen, &dotclock, &orig_data); - if (developer->int_val & SYS_VID) { - Sys_Printf ("VID: %d modes\n", nummodes); - for (i = 0; i < nummodes; i++) { - Sys_Printf ("VID: %xx%d\n", vidmodes[i]->hdisplay, - vidmodes[i]->vdisplay); - } - } - + Sys_MaskPrintf (SYS_vid, "VID: %d modes\n", nummodes); + original_mode = -1; for (i = 0; i < nummodes; i++) { - if ((vidmodes[i]->hdisplay == orig_data.hdisplay) && - (vidmodes[i]->vdisplay == orig_data.vdisplay)) { + if (original_mode == -1 + && (vidmodes[i]->hdisplay == orig_data.hdisplay) && + (vidmodes[i]->vdisplay == orig_data.vdisplay)) { original_mode = i; - break; + } + if (developer & SYS_vid) { + Sys_Printf ("VID:%c%dx%d\n", + original_mode == i ? '*' : ' ', + vidmodes[i]->hdisplay, vidmodes[i]->vdisplay); + Sys_Printf ("\t%d %d %d %d:%d %d %d:%d\n", + vidmodes[i]->hsyncstart, vidmodes[i]->hsyncend, + vidmodes[i]->htotal, vidmodes[i]->hskew, + vidmodes[i]->vsyncstart, vidmodes[i]->vsyncend, + vidmodes[i]->vtotal, vidmodes[i]->flags); } } @@ -452,11 +462,13 @@ X11_SetVidMode (int width, int height) } if (found_mode) { - Sys_MaskPrintf (SYS_VID, "VID: Chose video mode: %dx%d\n", + Sys_MaskPrintf (SYS_vid, "VID: Chose video mode: %dx%d\n", viddef.width, viddef.height); + if (0) { XF86VidModeSwitchToMode (x_disp, x_screen, vidmodes[best_mode]); + } vidmode_active = true; X11_SetScreenSaver (); } else { @@ -469,22 +481,19 @@ X11_SetVidMode (int width, int height) #endif } -static void -X11_UpdateFullscreen (cvar_t *fullscreen) +void +X11_UpdateFullscreen (int fullscreen) { if (!vid_context_created) return; - if (!fullscreen->int_val) { + if (!fullscreen) { X11_RestoreVidMode (); set_fullscreen (0); IN_UpdateGrab (in_grab); - X11_SetMouse (); - return; } else { set_fullscreen (1); X11_SetVidMode (viddef.width, viddef.height); - X11_SetMouse (); IN_UpdateGrab (in_grab); } } @@ -501,11 +510,7 @@ X11_Init_Cvars (void) { Cmd_AddCommand ("vid_center", VID_Center_f, "Center the view port on the " "quake window in a virtual desktop.\n"); - vid_fullscreen = Cvar_Get ("vid_fullscreen", "0", CVAR_ARCHIVE, - &X11_UpdateFullscreen, - "Toggles fullscreen game mode"); - vid_system_gamma = Cvar_Get ("vid_system_gamma", "1", CVAR_ARCHIVE, NULL, - "Use system gamma control if available"); + Cvar_Register (&x11_vidmode_cvar, 0, 0); } void @@ -515,7 +520,6 @@ X11_CreateWindow (int width, int height) unsigned long mask; XSetWindowAttributes attr; XClassHint *ClassHint; - XSizeHints *SizeHints; X11_AddEvent (ConfigureNotify, configure_notify); @@ -523,28 +527,28 @@ X11_CreateWindow (int width, int height) attr.background_pixel = 0; attr.border_pixel = 0; attr.colormap = x_cmap; - if (!attr.colormap) + if (!attr.colormap) { attr.colormap = XCreateColormap (x_disp, x_root, x_vis, AllocNone); - attr.event_mask = X11_MASK; + } + attr.event_mask = X11_MASK | IN_X11_Preinit (); + mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; + x_win = XCreateWindow (x_disp, x_root, 0, 0, width, height, 0, x_visinfo->depth, InputOutput, x_vis, mask, &attr); + IN_X11_Postinit (); // Set window size hints - SizeHints = XAllocSizeHints (); - if (SizeHints) { - SizeHints->flags = (PMinSize | PMaxSize); - SizeHints->min_width = width; - SizeHints->min_height = height; - SizeHints->max_width = width; - SizeHints->max_height = height; - XSetWMNormalHints (x_disp, x_win, SizeHints); + XSizeHints SizeHints = { + .flags = PMinSize, + .min_width = 32, + .min_height = 20, + }; + XSetWMNormalHints (x_disp, x_win, &SizeHints); - XFree (SizeHints); - } // Set window title - X11_SetCaption (va ("%s", PACKAGE_STRING)); + X11_SetCaption (va (0, "%s", PACKAGE_STRING)); // Set icon name XSetIconName (x_disp, x_win, PACKAGE_NAME); @@ -565,10 +569,9 @@ X11_CreateWindow (int width, int height) X11_WaitForEvent (ConfigureNotify); vid_context_created = true; - if (vid_fullscreen->int_val) { - X11_UpdateFullscreen (vid_fullscreen); - } XRaiseWindow (x_disp, x_win); + X11_WaitForEvent (VisibilityNotify); + X11_UpdateFullscreen (vid_fullscreen); } void @@ -577,7 +580,7 @@ X11_RestoreVidMode (void) #ifdef HAVE_VIDMODE if (vidmode_active) { X11_RestoreScreenSaver (); - XF86VidModeSwitchToMode (x_disp, x_screen, vidmodes[original_mode]); + //XF86VidModeSwitchToMode (x_disp, x_screen, vidmodes[original_mode]); XFree (vidmodes); vidmode_active = false; } @@ -602,19 +605,19 @@ X11_ForceViewPort (void) #endif } -qboolean +bool X11_SetGamma (double gamma) { #ifdef HAVE_VIDMODE -# ifdef X_XF86VidModeSetGamma XF86VidModeGamma xgamma; - if (vid_gamma_avail && vid_system_gamma->int_val && x_have_focus) { + if (vid_gamma_avail && vid_system_gamma && x_have_focus) { xgamma.red = xgamma.green = xgamma.blue = (float) gamma; if (XF86VidModeSetGamma (x_disp, x_screen, &xgamma)) return true; } -# endif +#else + Sys_MaskPrintf (SYS_vid, "X11_SetGamma: cannot adjust gamma\n"); #endif return false; } @@ -623,7 +626,6 @@ void X11_RestoreGamma (void) { #ifdef HAVE_VIDMODE -# ifdef X_XF86VidModeSetGamma XF86VidModeGamma xgamma; if (vid_gamma_avail && x_gamma[0] > 0) { @@ -632,7 +634,8 @@ X11_RestoreGamma (void) xgamma.blue = x_gamma[2]; XF86VidModeSetGamma (x_disp, x_screen, &xgamma); } -# endif +#else + Sys_MaskPrintf (SYS_vid, "X11_RestoreGamma: cannot adjust gamma\n"); #endif } @@ -661,3 +664,6 @@ X11_RestoreMouseAcceleration (void) accel_denominator, accel_threshold); accel_saved = false; } + +extern int x11_force_link; +static __attribute__((used)) int *context_x11_force_link = &x11_force_link; diff --git a/libs/video/targets/dga_check.c b/libs/video/targets/dga_check.c index a3b023308..bc60af5cc 100644 --- a/libs/video/targets/dga_check.c +++ b/libs/video/targets/dga_check.c @@ -69,7 +69,7 @@ Check for the presence of the XFree86-DGA X server extension */ -qboolean +bool VID_CheckDGA (Display * dpy, int *maj_ver, int *min_ver, int *hasvideo) { #ifdef HAVE_DGA @@ -94,11 +94,11 @@ VID_CheckDGA (Display * dpy, int *maj_ver, int *min_ver, int *hasvideo) } if ((!maj_ver) || (*maj_ver != XDGA_MAJOR_VERSION)) { - Sys_MaskPrintf (SYS_VID, "VID: Incorrect DGA version: %d.%d, \n", + Sys_MaskPrintf (SYS_vid, "VID: Incorrect DGA version: %d.%d, \n", *maj_ver, *min_ver); return false; } - Sys_MaskPrintf (SYS_VID, "VID: DGA version: %d.%d\n", *maj_ver, *min_ver); + Sys_MaskPrintf (SYS_vid, "VID: DGA version: %d.%d\n", *maj_ver, *min_ver); if (!hasvideo) hasvideo = &dummy_video; @@ -125,7 +125,7 @@ VID_CheckDGA (Display * dpy, int *maj_ver, int *min_ver, int *hasvideo) Check for the presence of the XFree86-VidMode X server extension */ -qboolean +bool VID_CheckVMode (Display * dpy, int *maj_ver, int *min_ver) { #ifdef HAVE_VIDMODE @@ -148,12 +148,12 @@ VID_CheckVMode (Display * dpy, int *maj_ver, int *min_ver) return false; if ((!maj_ver) || (*maj_ver != XF86VIDMODE_MAJOR_VERSION)) { - Sys_MaskPrintf (SYS_VID, "VID: Incorrect VidMode version: %d.%d\n", + Sys_MaskPrintf (SYS_vid, "VID: Incorrect VidMode version: %d.%d\n", *maj_ver, *min_ver); return false; } - Sys_MaskPrintf (SYS_VID, "VID: VidMode version: %d.%d\n", + Sys_MaskPrintf (SYS_vid, "VID: VidMode version: %d.%d\n", *maj_ver, *min_ver); return true; #else diff --git a/libs/video/targets/fbset.c b/libs/video/targets/fbset.c index 51866326a..c24a5db7a 100644 --- a/libs/video/targets/fbset.c +++ b/libs/video/targets/fbset.c @@ -1,7 +1,7 @@ /* * Linux Frame Buffer Device Configuration * - * © Copyright 1995-1999 by Geert Uytterhoeven + * © Copyright 1995-1999 by Geert Uytterhoeven * (Geert.Uytterhoeven@cs.kuleuven.ac.be) * * -------------------------------------------------------------------------- diff --git a/libs/video/targets/in_common.c b/libs/video/targets/in_common.c deleted file mode 100644 index d5bdf8bda..000000000 --- a/libs/video/targets/in_common.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - in_common.c - - general input driver - - Copyright (C) 1996-1997 Id Software, Inc. - Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] - Copyright (C) 1999,2000 contributors of the QuakeForge project - Please see the file "AUTHORS" for a list of contributors - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif -#ifdef HAVE_UNISTD_H -# include -#endif - -#define _BSD -#include -#include -#include -#include - -#include "QF/cbuf.h" -#include "QF/cvar.h" -#include "QF/in_event.h" -#include "QF/input.h" -#include "QF/joystick.h" -#include "QF/keys.h" -#include "QF/mathlib.h" -#include "QF/sys.h" -#include "QF/vid.h" - -VISIBLE viewdelta_t viewdelta; - -cvar_t *in_grab; -VISIBLE cvar_t *in_amp; -VISIBLE cvar_t *in_pre_amp; -cvar_t *in_freelook; -cvar_t *in_mouse_filter; -cvar_t *in_mouse_amp; -cvar_t *in_mouse_pre_amp; -cvar_t *lookstrafe; - -kbutton_t in_mlook, in_klook; -kbutton_t in_strafe; -kbutton_t in_speed; - -qboolean in_mouse_avail; -float in_mouse_x, in_mouse_y; -static float in_old_mouse_x, in_old_mouse_y; - -void -IN_UpdateGrab (cvar_t *var) // called from context_*.c -{ - if (var) { - IN_LL_Grab_Input (var->int_val); - } -} - -void -IN_ProcessEvents (void) -{ - /* Get events from environment. */ - JOY_Command (); - IN_LL_ProcessEvents (); -} - -void -IN_Move (void) -{ - JOY_Move (); - - if (!in_mouse_avail) - return; - - in_mouse_x *= in_mouse_pre_amp->value * in_pre_amp->value; - in_mouse_y *= in_mouse_pre_amp->value * in_pre_amp->value; - - if (in_mouse_filter->int_val) { - in_mouse_x = (in_mouse_x + in_old_mouse_x) * 0.5; - in_mouse_y = (in_mouse_y + in_old_mouse_y) * 0.5; - - in_old_mouse_x = in_mouse_x; - in_old_mouse_y = in_mouse_y; - } - - in_mouse_x *= in_mouse_amp->value * in_amp->value; - in_mouse_y *= in_mouse_amp->value * in_amp->value; - - if ((in_strafe.state & 1) || (lookstrafe->int_val && freelook)) - viewdelta.position[0] += in_mouse_x; - else - viewdelta.angles[YAW] -= in_mouse_x; - - if (freelook && !(in_strafe.state & 1)) { - viewdelta.angles[PITCH] += in_mouse_y; - } else { - viewdelta.position[2] -= in_mouse_y; - } - in_mouse_x = in_mouse_y = 0.0; -} - -/* Called at shutdown */ -void -IN_Shutdown (void) -{ - JOY_Shutdown (); - - Sys_MaskPrintf (SYS_VID, "IN_Shutdown\n"); - IN_LL_Shutdown (); - - IE_Shutdown (); -} - -void -IN_Init (cbuf_t *cbuf) -{ - IE_Init (); - IN_LL_Init (); - Key_Init (cbuf); - JOY_Init (); - - in_mouse_x = in_mouse_y = 0.0; -} - -void -IN_Init_Cvars (void) -{ - IE_Init_Cvars (); - Key_Init_Cvars (); - JOY_Init_Cvars (); - in_grab = Cvar_Get ("in_grab", "0", CVAR_ARCHIVE, IN_UpdateGrab, - "With this set to 1, quake will grab the mouse, " - "preventing loss of input focus."); - in_amp = Cvar_Get ("in_amp", "1", CVAR_ARCHIVE, NULL, - "global in_amp multiplier"); - in_pre_amp = Cvar_Get ("in_pre_amp", "1", CVAR_ARCHIVE, NULL, - "global in_pre_amp multiplier"); - in_freelook = Cvar_Get ("freelook", "0", CVAR_ARCHIVE, NULL, - "force +mlook"); - in_mouse_filter = Cvar_Get ("in_mouse_filter", "0", CVAR_ARCHIVE, NULL, - "Toggle mouse input filtering."); - in_mouse_amp = Cvar_Get ("in_mouse_amp", "15", CVAR_ARCHIVE, NULL, - "mouse in_mouse_amp multiplier"); - in_mouse_pre_amp = Cvar_Get ("in_mouse_pre_amp", "1", CVAR_ARCHIVE, NULL, - "mouse in_mouse_pre_amp multiplier"); - lookstrafe = Cvar_Get ("lookstrafe", "0", CVAR_ARCHIVE, NULL, - "when mlook/klook on player will strafe"); - IN_LL_Init_Cvars (); -} - -void -IN_ClearStates (void) -{ - IN_LL_ClearStates (); - Key_ClearStates (); -} diff --git a/libs/video/targets/in_event.c b/libs/video/targets/in_event.c deleted file mode 100644 index 28ee17c98..000000000 --- a/libs/video/targets/in_event.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - in_event.c - - input event handling - - Copyright (C) 2001 Bill Currie - - Author: Bill Currie - Date: 2001/8/9 - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif -#include - -#include "QF/in_event.h" - -static int (**event_handler_list)(const IE_event_t*); -static int eh_list_size; -static int focus; - -void -IE_Init (void) -{ - eh_list_size = 8; // start with 8 slots. will grow dynamicly if needed - event_handler_list = calloc (eh_list_size, sizeof (event_handler_list[0])); -} - -void -IE_Init_Cvars (void) -{ -} - -void -IE_Shutdown (void) -{ -} - -int -IE_Send_Event (const IE_event_t *event) -{ - if (event_handler_list[focus]) - return event_handler_list[focus](event); - return 0; -} - -int -IE_Add_Handler (int (*event_handler)(const IE_event_t*)) -{ - int i; - - while (1) { - int (**t)(const IE_event_t*); - for (i = 0; i < eh_list_size; i++) { - if (!event_handler_list[i]) { - event_handler_list[i] = event_handler; - return i; - } - } - if (!(t = realloc (event_handler_list, eh_list_size + 8))) - return -1; - event_handler_list = t; - memset (event_handler_list + eh_list_size, 0, - 8 * sizeof (event_handler_list[0])); - eh_list_size += 8; - } -} - -void -IE_Remove_Handler (int handle) -{ - if (handle >= 0 && handle < eh_list_size) - event_handler_list[handle] = 0; -} - -void -IE_Set_Focus (int handle) -{ - if (handle >= 0 && handle < eh_list_size - && event_handler_list[handle] - && focus != handle) { - IE_event_t event; - event.type = ie_lose_focus; - IE_Send_Event (&event); - focus = handle; - event.type = ie_gain_focus; - IE_Send_Event (&event); - } -} diff --git a/libs/video/targets/in_sdl.c b/libs/video/targets/in_sdl.c index 990a74b77..d61a3a5e7 100644 --- a/libs/video/targets/in_sdl.c +++ b/libs/video/targets/in_sdl.c @@ -43,7 +43,15 @@ #include "compat.h" -cvar_t *in_snd_block; +int in_snd_block; +static cvar_t in_snd_block_cvar = { + .name = "in_snd_block", + .description = + "block sound output on window focus loss", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &in_snd_block }, +}; static int have_focus = 1; @@ -53,7 +61,7 @@ event_focusout (void) { if (have_focus) { have_focus = 0; - if (in_snd_block->int_val) { + if (in_snd_block) { S_BlockSound (); CDAudio_Pause (); } @@ -64,14 +72,14 @@ static void event_focusin (void) { have_focus = 1; - if (in_snd_block->int_val) { + if (in_snd_block) { S_UnblockSound (); CDAudio_Resume (); } } static void -sdl_keydest_callback (keydest_t key_dest) +sdl_keydest_callback (keydest_t key_dest, void *data) { if (key_dest == key_game) SDL_EnableKeyRepeat (0, SDL_DEFAULT_REPEAT_INTERVAL); @@ -588,7 +596,7 @@ IN_LL_Init (void) { SDL_EnableUNICODE (1); // Enable UNICODE translation for keyboard input - Key_KeydestCallback (sdl_keydest_callback); + Key_KeydestCallback (sdl_keydest_callback, 0); if (COM_CheckParm ("-nomouse")) return; @@ -599,8 +607,7 @@ IN_LL_Init (void) void IN_LL_Init_Cvars (void) { - in_snd_block = Cvar_Get ("in_snd_block", "0", CVAR_ARCHIVE, NULL, - "block sound output on window focus loss"); + Cvar_Register (&in_snd_block_cvar, 0, 0); } void diff --git a/libs/video/targets/in_svgalib.c b/libs/video/targets/in_svgalib.c index a9a5f307f..159b3a9ab 100644 --- a/libs/video/targets/in_svgalib.c +++ b/libs/video/targets/in_svgalib.c @@ -112,7 +112,7 @@ keyhandler (int scancode, int state) default: break; } - //Sys_MaskPrintf (SYS_VID, "%d %02x %02lx %04x %c\n", sc, press, shifts, + //Sys_MaskPrintf (SYS_vid, "%d %02x %02lx %04x %c\n", sc, press, shifts, // key, ascii > 32 && ascii < 127 ? ascii : '#'); Key_Event (key, ascii, press); } @@ -383,14 +383,14 @@ IN_InitMouse (void) mouserate = atoi (com_argv[COM_CheckParm ("-mrate") + 1]); } #if 0 - Sys_MaskPrintf (SYS_VID, "Mouse: dev=%s,type=%s,speed=%d\n", + Sys_MaskPrintf (SYS_vid, "Mouse: dev=%s,type=%s,speed=%d\n", mousedev, mice[mtype].name, mouserate); #endif //FIXME: vga_init() opens the mouse automoatically // closing it to ensure its opened how we want it mouse_close(); if (mouse_init ((char *)mousedev, mtype, mouserate)) { - Sys_MaskPrintf (SYS_VID, + Sys_MaskPrintf (SYS_vid, "No mouse found. Check your libvga.conf mouse settings" " and that the mouse\n" "device has appropriate permission settings.\n"); @@ -404,7 +404,7 @@ IN_InitMouse (void) void IN_LL_Shutdown (void) { - Sys_MaskPrintf (SYS_VID, "IN_LL_Shutdown\n"); + Sys_MaskPrintf (SYS_vid, "IN_LL_Shutdown\n"); if (UseMouse) mouse_close (); diff --git a/libs/video/targets/in_win.c b/libs/video/targets/in_win.c index 2a14d7179..9de639f8a 100644 --- a/libs/video/targets/in_win.c +++ b/libs/video/targets/in_win.c @@ -43,9 +43,13 @@ #include "QF/keys.h" #include "QF/qargs.h" #include "QF/screen.h" +#include "QF/sound.h" #include "QF/sys.h" +#include "QF/input/event.h" + #include "compat.h" +#include "context_win.h" #include "in_win.h" #define DINPUT_BUFFERSIZE 16 @@ -55,21 +59,16 @@ HRESULT (WINAPI * pDirectInputCreate) (HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUT * lplpDirectInput, LPUNKNOWN punkOuter); -extern qboolean win_canalttab; -extern DEVMODE win_gdevmode; - // mouse local variables static unsigned uiWheelMessage; static unsigned mouse_buttons; -static unsigned mouse_oldbuttonstate; static POINT current_pos; -static float mx_accum, my_accum; -static qboolean mouseinitialized; -static qboolean restore_spi; +static bool mouseinitialized; +static bool restore_spi; static int originalmouseparms[3], newmouseparms[3] = { 0, 0, 1 }; -static qboolean mouseparmsvalid, mouseactivatetoggle; -static qboolean mouseshowtoggle = 1; -static qboolean dinput_acquired; +static bool mouseparmsvalid, mouseactivatetoggle; +static bool mouseshowtoggle = 1; +static bool dinput_acquired; static unsigned int mstate_di; // misc locals @@ -78,9 +77,317 @@ static LPDIRECTINPUTDEVICE g_pMouse; static HINSTANCE hInstDI; -static qboolean dinput; +static bool dinput; -static qboolean vid_wassuspended = false; +static bool vid_wassuspended = false; +static bool win_in_game = false; + +typedef struct win_device_s { + const char *name; + int num_axes; + int num_buttons; + in_axisinfo_t *axes; + in_buttoninfo_t *buttons; + void *event_data; + int devid; +} win_device_t; + +static int in_mouse_avail; + +#define WIN_MOUSE_BUTTONS 32 + +static int win_driver_handle = -1; +static in_buttoninfo_t win_key_buttons[256]; +static in_axisinfo_t win_mouse_axes[2]; +static in_buttoninfo_t win_mouse_buttons[WIN_MOUSE_BUTTONS]; +static const char *win_mouse_axis_names[] = {"M_X", "M_Y"}; +static const char *win_mouse_button_names[] = { + "M_BUTTON1", "M_BUTTON2", "M_BUTTON3", "M_WHEEL_UP", + "M_WHEEL_DOWN", "M_BUTTON6", "M_BUTTON7", "M_BUTTON8", + "M_BUTTON9", "M_BUTTON10", "M_BUTTON11", "M_BUTTON12", + "M_BUTTON13", "M_BUTTON14", "M_BUTTON15", "M_BUTTON16", + "M_BUTTON17", "M_BUTTON18", "M_BUTTON19", "M_BUTTON20", + "M_BUTTON21", "M_BUTTON22", "M_BUTTON23", "M_BUTTON24", + "M_BUTTON25", "M_BUTTON26", "M_BUTTON27", "M_BUTTON28", + "M_BUTTON29", "M_BUTTON30", "M_BUTTON31", "M_BUTTON32", +}; + +#define SIZE(x) (sizeof (x) / sizeof (x[0])) + +static unsigned short scantokey[128] = { +// 0 1 2 3 +// 4 5 6 7 +// 8 9 A B +// C D E F + 0, QFK_ESCAPE, '1', '2', + '3', '4', '5', '6', + '7', '8', '9', '0', + '-', '=', QFK_BACKSPACE, QFK_TAB, // 0 + 'q', 'w', 'e', 'r', + 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', + QFK_RETURN, QFK_LCTRL, 'a', 's', // 1 + 'd', 'f', 'g', 'h', + 'j', 'k', 'l', ';', + '\'', '`', QFK_LSHIFT, '\\', + 'z', 'x', 'c', 'v', // 2 + 'b', 'n', 'm', ',', + '.', '/', QFK_RSHIFT, QFK_KP_MULTIPLY, + QFK_LALT, ' ', QFK_CAPSLOCK, QFK_F1, + QFK_F2, QFK_F3, QFK_F4, QFK_F5, // 3 + QFK_F6, QFK_F7, QFK_F8, QFK_F9, + QFK_F10, QFK_PAUSE, QFK_SCROLLOCK, QFK_KP7, + QFK_KP8, QFK_KP9, QFK_KP_MINUS, QFK_KP4, + QFK_KP5, QFK_KP6, QFK_KP_PLUS, QFK_KP1, // 4 + QFK_KP2, QFK_KP3, QFK_KP0, QFK_KP_PERIOD, + 0, 0, 0, QFK_F11, + QFK_F12, 0, 0, 0, + 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static unsigned short shift_scantokey[128] = { +// 0 1 2 3 +// 4 5 6 7 +// 8 9 A B +// C D E F + 0, QFK_ESCAPE, '!', '@', // 0 + '#', '$', '%', '^', // 0 + '&', '*', '(', ')', // 0 + '_', '+', QFK_BACKSPACE, QFK_TAB, // 0 + 'Q', 'W', 'E', 'R', // 1 + 'T', 'Y', 'U', 'I', // 1 + 'O', 'P', '{', '}', // 1 + QFK_RETURN, QFK_LCTRL, 'A', 'S', // 1 + 'D', 'F', 'G', 'H', // 2 + 'J', 'K', 'L', ':', // 2 + '"', '~', QFK_LSHIFT, '|', // 2 + 'Z', 'X', 'C', 'V', // 2 + 'B', 'N', 'M', '<', // 3 + '>', '?', QFK_RSHIFT, QFK_KP_MULTIPLY,// 3 + QFK_LALT, ' ', QFK_CAPSLOCK, QFK_F1, // 3 + QFK_F2, QFK_F3, QFK_F4, QFK_F5, // 3 + QFK_F6, QFK_F7, QFK_F8, QFK_F9, // 4 + QFK_F10, QFK_PAUSE, QFK_SCROLLOCK, QFK_KP7, // 4 + QFK_KP8, QFK_KP9, QFK_KP_MINUS, QFK_KP4, // 4 + QFK_KP5, QFK_KP6, QFK_KP_PLUS, QFK_KP1, // 4 + QFK_KP2, QFK_KP3, QFK_KP0, QFK_KP_PERIOD,//5 + 0, 0, 0, QFK_F11, // 5 + QFK_F12, 0, 0, 0, // 5 + 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 +}; + +static unsigned short ext_scantokey[128] = { +// 0 1 2 3 +// 4 5 6 7 +// 8 9 A B +// C D E F + 0, QFK_ESCAPE, '1', '2', + '3', '4', '5', '6',// 0 + '7', '8', '9', '0', + '-', '=', QFK_BACKSPACE, QFK_TAB, + 'q', 'w', 'e', 'r', + 't', 'y', 'u', 'i', // 1 + 'o', 'p', '[', ']', + QFK_KP_ENTER, QFK_RCTRL, 'a', 's', + 'd', 'f', 'g', 'h', + 'j', 'k', 'l', ';', // 2 + '\'', '`', QFK_LSHIFT, '\\', + 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', + '.', QFK_KP_DIVIDE, QFK_RSHIFT, '*', // 3 + QFK_RALT, ' ', QFK_CAPSLOCK, QFK_F1, + QFK_F2, QFK_F3, QFK_F4, QFK_F5, + QFK_F6, QFK_F7, QFK_F8, QFK_F9, + QFK_F10, QFK_NUMLOCK, 0, QFK_HOME, // 4 + QFK_UP, QFK_PAGEUP, '-', QFK_LEFT, + '5', QFK_RIGHT, '+', QFK_END, + QFK_DOWN, QFK_PAGEDOWN, QFK_INSERT, QFK_DELETE, + 0, 0, 0, QFK_F11, // 5 + QFK_F12, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static unsigned short shift_ext_scantokey[128] = { +// 0 1 2 3 +// 4 5 6 7 +// 8 9 A B +// C D E F + 0, QFK_ESCAPE, '!', '@', + '#', '$', '%', '^', + '&', '*', '(', ')', + '_', '+', QFK_BACKSPACE, QFK_ESCAPE, // 0 + 'Q', 'W', 'E', 'R', + 'T', 'Y', 'U', 'I', + 'O', 'P', '{', '}', + QFK_KP_ENTER, QFK_RCTRL, 'A', 'S', // 1 + 'D', 'F', 'G', 'H', + 'J', 'K', 'L', ':', + '"', '~', QFK_LSHIFT, '|', + 'Z', 'X', 'C', 'V', // 2 + 'B', 'N', 'M', '<', + '>', QFK_KP_DIVIDE, QFK_RSHIFT, '*', + QFK_RALT, ' ', QFK_CAPSLOCK, QFK_F1, + QFK_F2, QFK_F3, QFK_F4, QFK_F5, + QFK_F6, QFK_F7, QFK_F8, QFK_F9, + QFK_F10, QFK_NUMLOCK, 0, QFK_HOME, // 4 + QFK_UP, QFK_PAGEUP, '-', QFK_LEFT, + '5', QFK_RIGHT, '+', QFK_END, + QFK_DOWN, QFK_PAGEDOWN, QFK_INSERT, QFK_DELETE, + 0, 0, 0, QFK_F11, // 5 + QFK_F12, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +#define ROTL(x,n) (((x)<<(n))|(x)>>(32-n)) + +/* + MapKey + + Map from windows to quake keynums +*/ +static void +MapKey (unsigned int keycode, int press, int *k, int *u) +{ + int extended; + int scan; + int key; + int uc; + unsigned long mask = ~1L; + static unsigned long shifts; + + extended = (keycode >> 24) & 1; + scan = (keycode >> 16) & 255; + + if (scan > 127) { + *u = *k = 0; + return; + } + + if (extended) + key = ext_scantokey[scan]; + else + key = scantokey[scan]; + + if (shifts & 0x03) { + if (extended) + uc = shift_ext_scantokey[scan]; + else + uc = shift_scantokey[scan]; + } else { + if (extended) + uc = ext_scantokey[scan]; + else + uc = scantokey[scan]; + } + + if (uc > 255) + uc = 0; + + switch (key) { + case QFK_RSHIFT: + shifts &= mask; + shifts |= press; + break; + case QFK_LSHIFT: + shifts &= ROTL(mask, 1); + shifts |= ROTL(press, 1); + break; + case QFK_RCTRL: + shifts &= ROTL(mask, 2); + shifts |= ROTL(press, 2); + break; + case QFK_LCTRL: + shifts &= ROTL(mask, 3); + shifts |= ROTL(press, 3); + break; + default: + break; + } + + Sys_MaskPrintf (SYS_vid, "%08x %d %02x %02lx %04x %c\n", + keycode, press, scan, shifts, + key, uc > 32 && uc < 127 ? uc : '#'); + *k = key; + *u = uc; +} + +static win_device_t win_keyboard_device = { + "core:keyboard", + 0, SIZE (win_key_buttons), + 0, win_key_buttons, +}; + +static win_device_t win_mouse_device = { + "core:mouse", + SIZE (win_mouse_axes), SIZE (win_mouse_buttons), + win_mouse_axes, win_mouse_buttons, +}; + +static IE_mouse_event_t win_mouse; +static IE_key_event_t win_key; + +static void +in_win_send_axis_event (int devid, in_axisinfo_t *axis) +{ + IE_event_t event = { + .type = ie_axis, + .when = Sys_LongTime (), + .axis = { + .data = win_mouse_device.event_data, + .devid = devid, + .axis = axis->axis, + .value = axis->value, + }, + }; + IE_Send_Event (&event); +} + +static int +in_win_send_mouse_event (IE_mouse_type type) +{ + IE_event_t event = { + .type = ie_mouse, + .when = Sys_LongTime (), + .mouse = win_mouse, + }; + event.mouse.type = type; + return IE_Send_Event (&event); +} + +static int +in_win_send_key_event (void) +{ + IE_event_t event = { + .type = ie_key, + .when = Sys_LongTime (), + .key = win_key, + }; + return IE_Send_Event (&event); +} + +static void +in_win_send_button_event (int devid, in_buttoninfo_t *button, void *event_data) +{ + IE_event_t event = { + .type = ie_button, + .when = Sys_LongTime (), + .button = { + .data = event_data, + .devid = devid, + .button = button->button, + .state = button->state, + }, + }; + IE_Send_Event (&event); +} typedef struct MYDATA { LONG lX; // X axis goes here @@ -118,13 +425,11 @@ static DIDATAFORMAT df = { rgodf, // and here they are }; -// forward-referenced functions, joy - void IN_UpdateClipCursor (void) { if (mouseinitialized && in_mouse_avail && !dinput) { - ClipCursor (&window_rect); + ClipCursor (&win_rect); } } @@ -166,9 +471,9 @@ IN_ActivateMouse (void) restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0); - SetCursorPos (window_center_x, window_center_y); - SetCapture (mainwindow); - ClipCursor (&window_rect); + SetCursorPos (win_center_x, win_center_y); + SetCapture (win_mainwindow); + ClipCursor (&win_rect); } in_mouse_avail = true; @@ -201,7 +506,7 @@ IN_DeactivateMouse (void) } } -static qboolean +static bool IN_InitDInput (void) { HRESULT hr; @@ -254,7 +559,7 @@ IN_InitDInput (void) return false; } // set the cooperativity level. - hr = IDirectInputDevice_SetCooperativeLevel (g_pMouse, mainwindow, + hr = IDirectInputDevice_SetCooperativeLevel (g_pMouse, win_mainwindow, DISCL_EXCLUSIVE | DISCL_FOREGROUND); @@ -276,13 +581,13 @@ IN_InitDInput (void) return true; } -static void +static int IN_StartupMouse (void) { // HDC hdc; if (COM_CheckParm ("-nomouse")) - return; + return 0; mouseinitialized = true; @@ -317,12 +622,13 @@ IN_StartupMouse (void) } } - mouse_buttons = 32; + mouse_buttons = WIN_MOUSE_BUTTONS; // if a fullscreen video mode was set before the mouse was initialized, // set the mouse state appropriately if (mouseactivatetoggle) IN_ActivateMouse (); + return 1; } static void @@ -337,10 +643,11 @@ in_paste_buffer_f (void) if (th) { clipText = GlobalLock (th); if (clipText) { + win_key.code = 0; for (i = 0; clipText[i] && !strchr ("\n\r\b", clipText[i]); i++) { - Key_Event (QFK_UNKNOWN, clipText[i], 1); - Key_Event (QFK_UNKNOWN, 0, 0); + win_key.unicode = clipText[i]; + in_win_send_key_event (); } } GlobalUnlock (th); @@ -348,24 +655,121 @@ in_paste_buffer_f (void) CloseClipboard (); } } +#if 0 +static void +win_keydest_callback (keydest_t key_dest, void *data) +{ + win_in_game = key_dest == key_game; + if (win_in_game) { + IN_ActivateMouse (); + IN_HideMouse (); + } else { + IN_DeactivateMouse (); + IN_ShowMouse (); + } +} +#endif -void -IN_LL_Init (void) +static void +win_add_device (win_device_t *dev) +{ + for (int i = 0; i < dev->num_axes; i++) { + dev->axes[i].axis = i; + } + for (int i = 0; i < dev->num_buttons; i++) { + dev->buttons[i].button = i; + } + dev->devid = IN_AddDevice (win_driver_handle, dev, dev->name, dev->name); +} + +static void +in_win_init (void *data) { uiWheelMessage = RegisterWindowMessage ("MSWHEEL_ROLLMSG"); - IN_StartupMouse (); + win_add_device (&win_keyboard_device); + + if (IN_StartupMouse ()) { + win_add_device (&win_mouse_device); + } + + //Key_KeydestCallback (win_keydest_callback, 0); Cmd_AddCommand ("in_paste_buffer", in_paste_buffer_f, "Paste the contents of the C&P buffer to the console"); } -void -IN_LL_Init_Cvars (void) +static const char * +in_win_get_axis_name (void *data, void *device, int axis_num) { + win_device_t *dev = device; + const char *name = 0; + + if (dev == &win_keyboard_device) { + // keyboards don't have axes... + } else if (dev == &win_mouse_device) { + if ((unsigned) axis_num < SIZE (win_mouse_axis_names)) { + name = win_mouse_axis_names[axis_num]; + } + } + return name; } -void -IN_LL_Shutdown (void) +static const char * +in_win_get_button_name (void *data, void *device, int button_num) +{ + win_device_t *dev = device; + const char *name = 0; + + if (dev == &win_keyboard_device) { + // FIXME + } else if (dev == &win_mouse_device) { + if ((unsigned) button_num < SIZE (win_mouse_button_names)) { + name = win_mouse_button_names[button_num]; + } + } + return name; +} + +static int +in_win_get_axis_num (void *data, void *device, const char *axis_name) +{ + win_device_t *dev = device; + int num = -1; + + if (dev == &win_keyboard_device) { + // keyboards don't have axes... + } else if (dev == &win_mouse_device) { + for (size_t i = 0; i < SIZE (win_mouse_axis_names); i++) { + if (strcasecmp (axis_name, win_mouse_axis_names[i]) == 0) { + num = i; + break; + } + } + } + return num; +} + +static int +in_win_get_button_num (void *data, void *device, const char *button_name) +{ + win_device_t *dev = device; + int num = -1; + + if (dev == &win_keyboard_device) { + // FIXME + } else if (dev == &win_mouse_device) { + for (size_t i = 0; i < SIZE (win_mouse_button_names); i++) { + if (strcasecmp (button_name, win_mouse_button_names[i]) == 0) { + num = i; + break; + } + } + } + return num; +} + +static void +in_win_shutdown (void *data) { IN_DeactivateMouse (); @@ -383,48 +787,94 @@ IN_LL_Shutdown (void) } static void -IN_MouseEvent (unsigned mstate) +in_win_set_device_event_data (void *device, void *event_data, void *data) { - int i; + win_device_t *dev = device; + dev->event_data = event_data; +} - if (in_mouse_avail && !dinput) { - // perform button actions - for (i = 0; i < mouse_buttons; i++) { - if ((mstate & (1 << i)) && !(mouse_oldbuttonstate & (1 << i))) { - Key_Event (QFM_BUTTON1 + i, -1, true); - } +static void * +in_win_get_device_event_data (void *device, void *data) +{ + win_device_t *dev = device; + return dev->event_data; +} - if (!(mstate & (1 << i)) && (mouse_oldbuttonstate & (1 << i))) { - Key_Event (QFM_BUTTON1 + i, -1, false); +static void +event_motion (int dmx, int dmy, int mx, int my) +{ + win_mouse_axes[0].value = dmx; + win_mouse_axes[1].value = dmy; + + win_mouse.shift = win_key.shift; + win_mouse.x = mx; + win_mouse.x = my; + if (!in_win_send_mouse_event (ie_mousemove)) { + in_win_send_axis_event (win_mouse_device.devid, &win_mouse_axes[0]); + in_win_send_axis_event (win_mouse_device.devid, &win_mouse_axes[1]); + } +} + +static void +event_button (unsigned buttons) +{ + unsigned mask = win_mouse.buttons ^ buttons; + + if (!mask) { + // no change + return; + } + + // FIXME this won't be right if multiple buttons change state + int press = buttons & mask; + + for (int i = 0; i < WIN_MOUSE_BUTTONS; i++) { + win_mouse_buttons[i].state = buttons & (1 << i); + } + + win_mouse.buttons = buttons; + if (!in_win_send_mouse_event (press ? ie_mousedown : ie_mouseup)) { + for (int i = 0; i < WIN_MOUSE_BUTTONS; i++) { + if (!(mask & (1 << i))) { + continue; } + in_win_send_button_event (win_mouse_device.devid, + &win_mouse_buttons[i], + win_mouse_device.event_data); } - - mouse_oldbuttonstate = mstate; } } -void -IN_LL_Grab_Input (int grab) +static void +event_key (LPARAM keydata, int pressed) { -} - -void -IN_LL_ClearStates (void) -{ - if (in_mouse_avail) { - mx_accum = 0; - my_accum = 0; - mouse_oldbuttonstate = 0; + int extended = (keydata >> 24) & 1; + // This assumes windows key codes are really only 7 bits (should be, since + // they seem to be regular scan codes) + int scan = (keydata >> 16) & 0x7f; + int key = (extended << 7) | scan; + MapKey (keydata, pressed, &win_key.code, &win_key.unicode); + //FIXME windows key codes and x11 key code's don't match, so binding + //configs are not cross-platform (is this actually a problem?) + win_key_buttons[key].state = pressed; + if (!pressed || !in_win_send_key_event ()) { + in_win_send_button_event (win_keyboard_device.devid, + &win_key_buttons[key], + win_keyboard_device.event_data); } } -void -IN_LL_ProcessEvents (void) +static void +in_win_clear_states (void *data) +{ +} + +static void +in_win_process_events (void *data) { MSG msg; int mx, my; // HDC hdc; - int i; DIDEVICEOBJECTDATA od; DWORD dwElements; HRESULT hr; @@ -495,223 +945,78 @@ IN_LL_ProcessEvents (void) } } - // perform button actions - for (i = 0; i < mouse_buttons; i++) { - if ((mstate_di & (1 << i)) && !(mouse_oldbuttonstate & (1 << i))) { - Key_Event (QFM_BUTTON1 + i, -1, true); - } - - if (!(mstate_di & (1 << i)) && (mouse_oldbuttonstate & (1 << i))) { - Key_Event (QFM_BUTTON1 + i, -1, false); - } - } - - mouse_oldbuttonstate = mstate_di; + event_button (mstate_di); } else { GetCursorPos (¤t_pos); - mx = current_pos.x - window_center_x + mx_accum; - my = current_pos.y - window_center_y + my_accum; - mx_accum = 0; - my_accum = 0; + mx = current_pos.x - win_center_x; + my = current_pos.y - win_center_y; } - in_mouse_x = mx; - in_mouse_y = my; - // if the mouse has moved, force it to the center, so there's room to move if (mx || my) { - SetCursorPos (window_center_x, window_center_y); + //FIXME abs pos + event_motion (mx, my, 0, 0); + SetCursorPos (win_center_x, win_center_y); } } +static void +in_win_axis_info (void *data, void *device, in_axisinfo_t *axes, int *numaxes) +{ + win_device_t *dev = device; + if (!axes) { + *numaxes = dev->num_axes; + return; + } + if (*numaxes > dev->num_axes) { + *numaxes = dev->num_axes; + } + memcpy (axes, dev->axes, *numaxes * sizeof (in_axisinfo_t)); +} + +static void +in_win_button_info (void *data, void *device, in_buttoninfo_t *buttons, + int *numbuttons) +{ + win_device_t *dev = device; + if (!buttons) { + *numbuttons = dev->num_buttons; + return; + } + if (*numbuttons > dev->num_buttons) { + *numbuttons = dev->num_buttons; + } + memcpy (buttons, dev->buttons, *numbuttons * sizeof (in_buttoninfo_t)); +} + //========================================================================== -static unsigned short scantokey[128] = { -// 0 1 2 3 4 5 6 7 -// 8 9 A B C D E F - 0, 27, '1', '2', '3', '4', '5', '6', - '7', '8', '9', '0', '-', '=', QFK_BACKSPACE, 9, // 0 - 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', - 'o', 'p', '[', ']', 13, QFK_LCTRL, 'a', 's', // 1 - 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', - '\'', '`', QFK_LSHIFT, '\\', 'z', 'x', 'c', 'v', // 2 - 'b', 'n', 'm', ',', '.', '/', QFK_RSHIFT, QFK_KP_MULTIPLY, - QFK_LALT, ' ', QFK_CAPSLOCK, QFK_F1, QFK_F2, QFK_F3, QFK_F4, QFK_F5, // 3 - QFK_F6, QFK_F7, QFK_F8, QFK_F9, QFK_F10, QFK_PAUSE, QFK_SCROLLOCK, QFK_KP7, - QFK_KP8, QFK_KP9, QFK_KP_MINUS, QFK_KP4, QFK_KP5, QFK_KP6, QFK_KP_PLUS, QFK_KP1, // 4 - QFK_KP2, QFK_KP3, QFK_KP0, QFK_KP_PERIOD, 0, 0, 0, QFK_F11, - QFK_F12, 0, 0, 0, 0, 0, 0, 0, // 5 - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 -}; - -static unsigned short shift_scantokey[128] = { -// 0 1 2 3 4 5 6 7 -// 8 9 A B C D E F - 0, 27, '!', '@', '#', '$', '%', '^', - '&', '*', '(', ')', '_', '+', QFK_BACKSPACE, 9, // 0 - 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', - 'O', 'P', '{', '}', 13, QFK_LCTRL, 'A', 'S', // 1 - 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', - '"', '~', QFK_LSHIFT, '|', 'Z', 'X', 'C', 'V', // 2 - 'B', 'N', 'M', '<', '>', '?', QFK_RSHIFT, QFK_KP_MULTIPLY, - QFK_LALT, ' ', QFK_CAPSLOCK, QFK_F1, QFK_F2, QFK_F3, QFK_F4, QFK_F5, // 3 - QFK_F6, QFK_F7, QFK_F8, QFK_F9, QFK_F10, QFK_PAUSE, QFK_SCROLLOCK, QFK_KP7, - QFK_KP8, QFK_KP9, QFK_KP_MINUS, QFK_KP4, QFK_KP5, QFK_KP6, QFK_KP_PLUS, QFK_KP1, // 4 - QFK_KP2, QFK_KP3, QFK_KP0, QFK_KP_PERIOD, 0, 0, 0, QFK_F11, - QFK_F12, 0, 0, 0, 0, 0, 0, 0, // 5 - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 -}; - -static unsigned short ext_scantokey[128] = { -// 0 1 2 3 4 5 6 7 -// 8 9 A B C D E F - 0, 27, '1', '2', '3', '4', '5', '6', // 0 - '7', '8', '9', '0', '-', '=', QFK_BACKSPACE, 9, - 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 1 - 'o', 'p', '[', ']', QFK_KP_ENTER, QFK_RCTRL, 'a', 's', - 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 2 - '\'', '`', QFK_LSHIFT, '\\', 'z', 'x', 'c', 'v', - 'b', 'n', 'm', ',', '.', QFK_KP_DIVIDE, QFK_RSHIFT, '*', // 3 - QFK_RALT, ' ', QFK_CAPSLOCK, QFK_F1, QFK_F2, QFK_F3, QFK_F4, QFK_F5, - QFK_F6, QFK_F7, QFK_F8, QFK_F9, QFK_F10, QFK_NUMLOCK, 0, QFK_HOME, // 4 - QFK_UP, QFK_PAGEUP, '-', QFK_LEFT, '5', QFK_RIGHT, '+', QFK_END, - QFK_DOWN, QFK_PAGEDOWN, QFK_INSERT, QFK_DELETE, 0, 0, 0, QFK_F11, // 5 - QFK_F12, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 -}; - -static unsigned short shift_ext_scantokey[128] = { -// 0 1 2 3 4 5 6 7 -// 8 9 A B C D E F - 0, 27, '!', '@', '#', '$', '%', '^', - '&', '*', '(', ')', '_', '+', QFK_BACKSPACE, 9, // 0 - 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', - 'O', 'P', '{', '}', QFK_KP_ENTER, QFK_RCTRL, 'A', 'S', // 1 - 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', - '"', '~', QFK_LSHIFT, '|', 'Z', 'X', 'C', 'V', // 2 - 'B', 'N', 'M', '<', '>', QFK_KP_DIVIDE, QFK_RSHIFT, '*', - QFK_RALT, ' ', QFK_CAPSLOCK, QFK_F1, QFK_F2, QFK_F3, QFK_F4, QFK_F5, // 3 - QFK_F6, QFK_F7, QFK_F8, QFK_F9, QFK_F10, QFK_NUMLOCK, 0, QFK_HOME, - QFK_UP, QFK_PAGEUP, '-', QFK_LEFT, '5', QFK_RIGHT, '+', QFK_END, // 4 - QFK_DOWN, QFK_PAGEDOWN, QFK_INSERT, QFK_DELETE, 0, 0, 0, QFK_F11, - QFK_F12, 0, 0, 0, 0, 0, 0, 0, // 5 - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 -}; - -#define ROTL(x,n) (((x)<<(n))|(x)>>(32-n)) - -/* - MapKey - - Map from windows to quake keynums -*/ -static void -MapKey (unsigned int keycode, int press, int *k, int *u) -{ - int extended; - int scan; - int key; - int uc; - unsigned long mask = ~1L; - static unsigned long shifts; - - extended = (keycode >> 24) & 1; - scan = (keycode >> 16) & 255; - - if (scan > 127) { - *u = *k = 0; - return; - } - - if (extended) - key = ext_scantokey[scan]; - else - key = scantokey[scan]; - - if (shifts & 0x03) { - if (extended) - uc = shift_ext_scantokey[scan]; - else - uc = shift_scantokey[scan]; - } else { - if (extended) - uc = ext_scantokey[scan]; - else - uc = scantokey[scan]; - } - - if (uc > 255) - uc = 0; - - switch (key) { - case QFK_RSHIFT: - shifts &= mask; - shifts |= press; - break; - case QFK_LSHIFT: - shifts &= ROTL(mask, 1); - shifts |= ROTL(press, 1); - break; - case QFK_RCTRL: - shifts &= ROTL(mask, 2); - shifts |= ROTL(press, 2); - break; - case QFK_LCTRL: - shifts &= ROTL(mask, 3); - shifts |= ROTL(press, 3); - break; - default: - break; - } - - Sys_MaskPrintf (SYS_VID, "%08x %d %02x %02lx %04x %c\n", - keycode, press, scan, shifts, - key, uc > 32 && uc < 127 ? uc : '#'); - *k = key; - *u = uc; -} - /* MAIN WINDOW */ /* - AppActivate - fActive - True if app is activating If the application is activating, then swap the system into SYSPAL_NOSTATIC mode so that our palettes will display correctly. */ void -AppActivate (BOOL fActive, BOOL minimize) +Win_Activate (BOOL active, BOOL minimize) { static BOOL sound_active; - ActiveApp = fActive; - Minimized = minimize; + win_minimized = minimize; // enable/disable sound on focus gain/loss - if (!ActiveApp && sound_active) { + if (!active && sound_active) { S_BlockSound (); sound_active = false; - } else if (ActiveApp && !sound_active) { + } else if (active && !sound_active) { S_UnblockSound (); sound_active = true; } - if (fActive) { + if (active) { if (modestate == MS_FULLDIB) { IN_ActivateMouse (); IN_HideMouse (); @@ -725,15 +1030,15 @@ AppActivate (BOOL fActive, BOOL minimize) "(try upgrading your video drivers)\n (%lx)", GetLastError()); } - ShowWindow (mainwindow, SW_SHOWNORMAL); + ShowWindow (win_mainwindow, SW_SHOWNORMAL); // Fix for alt-tab bug in NVidia drivers - MoveWindow(mainwindow, 0, 0, win_gdevmode.dmPelsWidth, + MoveWindow(win_mainwindow, 0, 0, win_gdevmode.dmPelsWidth, win_gdevmode.dmPelsHeight, false); } } - else if ((modestate == MS_WINDOWED) && in_grab->int_val - && key_dest == key_game) { + else if ((modestate == MS_WINDOWED) && in_grab + && win_in_game) { IN_ActivateMouse (); IN_HideMouse (); } @@ -745,7 +1050,7 @@ AppActivate (BOOL fActive, BOOL minimize) ChangeDisplaySettings (NULL, 0); vid_wassuspended = true; } - } else if ((modestate == MS_WINDOWED) && in_grab->int_val) { + } else if ((modestate == MS_WINDOWED) && in_grab) { IN_DeactivateMouse (); IN_ShowMouse (); } @@ -758,34 +1063,35 @@ MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LONG lRet = 1; int fActive, fMinimized, temp; - int key, unicode; if (uMsg == uiWheelMessage) uMsg = WM_MOUSEWHEEL; switch (uMsg) { + case WM_SETFOCUS: + //Key_FocusEvent (1); + break; case WM_KILLFOCUS: if (modestate == MS_FULLDIB) - ShowWindow (mainwindow, SW_SHOWMINNOACTIVE); + ShowWindow (win_mainwindow, SW_SHOWMINNOACTIVE); + //Key_FocusEvent (0); break; case WM_CREATE: break; case WM_MOVE: - VID_UpdateWindowStatus ((int) LOWORD (lParam), + Win_UpdateWindowStatus ((int) LOWORD (lParam), (int) HIWORD (lParam)); break; case WM_KEYDOWN: case WM_SYSKEYDOWN: - MapKey (lParam, 1, &key, &unicode); - Key_Event (key, unicode, true); + event_key (lParam, 1); break; case WM_KEYUP: case WM_SYSKEYUP: - MapKey (lParam, 0, &key, &unicode); - Key_Event (key, unicode, false); + event_key (lParam, 0); break; case WM_SYSCHAR: @@ -810,7 +1116,7 @@ MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) temp |= 2; if (wParam & MK_MBUTTON) temp |= 4; - IN_MouseEvent (temp); + event_button (temp); break; @@ -818,13 +1124,13 @@ MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) // It's delta is either positive or neg, and we generate the proper // Event. case WM_MOUSEWHEEL: + temp = win_mouse.buttons & ~((1 << 3) | (1 << 4));; if ((short) HIWORD (wParam) > 0) { - Key_Event (QFM_WHEEL_UP, -1, true); - Key_Event (QFM_WHEEL_UP, -1, false); + event_button (temp | (1 << 3)); } else { - Key_Event (QFM_WHEEL_DOWN, -1, true); - Key_Event (QFM_WHEEL_DOWN, -1, false); + event_button (temp | (1 << 4)); } + event_button (temp); break; case WM_SIZE: @@ -832,7 +1138,8 @@ MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case WM_CLOSE: if (MessageBox - (mainwindow, "Are you sure you want to quit?", "Confirm Exit", + (win_mainwindow, + "Are you sure you want to quit?", "Confirm Exit", MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES) { Sys_Quit (); } @@ -841,15 +1148,15 @@ MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case WM_ACTIVATE: fActive = LOWORD (wParam); fMinimized = (BOOL) HIWORD (wParam); - AppActivate (!(fActive == WA_INACTIVE), fMinimized); + Win_Activate (!(fActive == WA_INACTIVE), fMinimized); // fix leftover Alt from any Alt-Tab or the like that switched us // away IN_ClearStates (); break; case WM_DESTROY: - if (mainwindow) - DestroyWindow (mainwindow); + if (win_mainwindow) + DestroyWindow (win_mainwindow); PostQuitMessage (0); break; @@ -866,3 +1173,30 @@ MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) /* return 1 if handled message, 0 if not */ return lRet; } + +static in_driver_t in_win_driver = { + .init = in_win_init, + .shutdown = in_win_shutdown, + .set_device_event_data = in_win_set_device_event_data, + .get_device_event_data = in_win_get_device_event_data, + .process_events = in_win_process_events, + .clear_states = in_win_clear_states, + //.grab_input = in_win_grab_input, + + .axis_info = in_win_axis_info, + .button_info = in_win_button_info, + + .get_axis_name = in_win_get_axis_name, + .get_button_name = in_win_get_button_name, + .get_axis_num = in_win_get_axis_num, + .get_button_num = in_win_get_button_num, +}; + +static void __attribute__((constructor)) +in_win_register_driver (void) +{ + win_driver_handle = IN_RegisterDriver (&in_win_driver, 0); +} + + +int win_force_link; diff --git a/libs/video/targets/in_x11.c b/libs/video/targets/in_x11.c index dc689a7d5..310ae91c7 100644 --- a/libs/video/targets/in_x11.c +++ b/libs/video/targets/in_x11.c @@ -49,17 +49,23 @@ #include #include #include +#include #include #include #include #ifdef HAVE_DGA -# include # ifdef DGA_OLD_HEADERS # include # else # include +# endif #endif +#ifdef HAVE_XI2 +# include +#endif +#ifdef HAVE_XFIXES +# include #endif #include "QF/cdaudio.h" @@ -73,20 +79,120 @@ #include "QF/sound.h" #include "QF/sys.h" +#include "QF/input/event.h" + #include "compat.h" #include "context_x11.h" #include "dga_check.h" +#include "in_x11.h" +#include "qfselect.h" #include "vid_internal.h" -cvar_t *in_snd_block; -cvar_t *in_dga; -cvar_t *in_mouse_accel; +typedef struct x11_device_s { + const char *name; + int num_axes; + int num_buttons; + in_axisinfo_t *axes; + in_buttoninfo_t *buttons; + void *event_data; + int devid; +} x11_device_t; -static qboolean dga_avail; -static qboolean dga_active; -static int p_mouse_x, p_mouse_y; +#define X11_MOUSE_BUTTONS 32 +#define X11_MOUSE_AXES 2 +// The X11 protocol supports only 256 keys +static in_buttoninfo_t x11_key_buttons[256]; +// X11 mice have only two axes (FIXME NOT always true (XInput says otherwise!) +static in_axisinfo_t x11_mouse_axes[X11_MOUSE_AXES]; +// FIXME assume up to 32 mouse buttons (see X11_MOUSE_BUTTONS) +static in_buttoninfo_t x11_mouse_buttons[X11_MOUSE_BUTTONS]; +static const char *x11_mouse_axis_names[] = {"M_X", "M_Y"}; +static const char *x11_mouse_button_names[] = { + "M_BUTTON1", "M_BUTTON2", "M_BUTTON3", "M_WHEEL_UP", + "M_WHEEL_DOWN", "M_BUTTON6", "M_BUTTON7", "M_BUTTON8", + "M_BUTTON9", "M_BUTTON10", "M_BUTTON11", "M_BUTTON12", + "M_BUTTON13", "M_BUTTON14", "M_BUTTON15", "M_BUTTON16", + "M_BUTTON17", "M_BUTTON18", "M_BUTTON19", "M_BUTTON20", + "M_BUTTON21", "M_BUTTON22", "M_BUTTON23", "M_BUTTON24", + "M_BUTTON25", "M_BUTTON26", "M_BUTTON27", "M_BUTTON28", + "M_BUTTON29", "M_BUTTON30", "M_BUTTON31", "M_BUTTON32", +}; + +#define SIZE(x) (sizeof (x) / sizeof (x[0])) + +static x11_device_t x11_keyboard_device = { + "core:keyboard", + 0, SIZE (x11_key_buttons), + 0, x11_key_buttons, +}; + +static x11_device_t x11_mouse_device = { + "core:mouse", + SIZE (x11_mouse_axes), SIZE (x11_mouse_buttons), + x11_mouse_axes, x11_mouse_buttons, +}; + +int in_auto_focus; +static cvar_t in_auto_focus_cvar = { + .name = "in_auto_focus", + .description = + "grab input focus when the mouse enters the window when using xinput2 " + "with certain window managers using focus-follows-mouse (eg, openbox)", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &in_auto_focus }, +}; +int in_snd_block; +static cvar_t in_snd_block_cvar = { + .name = "in_snd_block", + .description = + "block sound output on window focus loss", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &in_snd_block }, +}; +int in_dga; +static cvar_t in_dga_cvar = { + .name = "in_dga", + .description = + "DGA Input support", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &in_dga }, +}; +int in_mouse_accel; +static cvar_t in_mouse_accel_cvar = { + .name = "in_mouse_accel", + .description = + "set to 0 to remove mouse acceleration", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &in_mouse_accel }, +}; + +static bool dga_avail; +static bool dga_active; +static IE_mouse_event_t x11_mouse; +static IE_key_event_t x11_key; static int input_grabbed = 0; +#ifdef HAVE_XI2 +static int xi_opcode; +#endif +#ifdef HAVE_XFIXES +static int xf_opcode; +static PointerBarrier x11_left_barrier; +static PointerBarrier x11_right_barrier; +static PointerBarrier x11_top_barrier; +static PointerBarrier x11_bottom_barrier; +#endif +static IE_app_window_event_t x11_aw; +static int x11_have_xi; +static int x11_fd; +static int x11_driver_handle = -1; +static int x11_event_handler_id; +static bool x11_have_pointer; + static void dga_on (void) { @@ -94,8 +200,8 @@ dga_on (void) if (dga_avail && !dga_active) { int ret; ret = XF86DGADirectVideo (x_disp, DefaultScreen (x_disp), - XF86DGADirectMouse); - Sys_MaskPrintf (SYS_VID, "XF86DGADirectVideo returned %d\n", ret); + XF86DGADirectMouse); + Sys_MaskPrintf (SYS_vid, "XF86DGADirectVideo returned %d\n", ret); if (ret) dga_active = true; } @@ -109,7 +215,7 @@ dga_off (void) if (dga_avail && dga_active) { int ret; ret = XF86DGADirectVideo (x_disp, DefaultScreen (x_disp), 0); - Sys_MaskPrintf (SYS_VID, "XF86DGADirectVideo returned %d\n", ret); + Sys_MaskPrintf (SYS_vid, "XF86DGADirectVideo returned %d\n", ret); if (ret) dga_active = false; } @@ -117,21 +223,21 @@ dga_off (void) } static void -in_dga_f (cvar_t *var) +in_dga_f (void *data, const cvar_t *cvar) { - if (var->int_val && input_grabbed) { - Sys_MaskPrintf (SYS_VID, "VID: in_dga_f on\n"); + if (in_dga && input_grabbed) { + Sys_MaskPrintf (SYS_vid, "VID: in_dga_f on\n"); dga_on (); } else { - Sys_MaskPrintf (SYS_VID, "VID: in_dga_f off\n"); + Sys_MaskPrintf (SYS_vid, "VID: in_dga_f off\n"); dga_off (); } } static void -in_mouse_accel_f (cvar_t *var) +in_mouse_accel_f (void *data, const cvar_t *cvar) { - if (var->int_val) { + if (in_mouse_accel) { X11_RestoreMouseAcceleration (); } else { X11_SaveMouseAcceleration (); @@ -151,50 +257,27 @@ in_paste_buffer_f (void) XFlush (x_disp); } -static void -selection_notify (XEvent *event) -{ - unsigned char *data, *p; - unsigned long num_bytes; - unsigned long tmp, len; - int format; - Atom type, property; - - x_time = event->xselection.time; - - if ((property = event->xselection.property) == None) - return; - - XGetWindowProperty (x_disp, x_win, property, 0, 0, False, AnyPropertyType, - &type, &format, &len, &num_bytes, &data); - if (num_bytes <= 0) - return; - if (XGetWindowProperty (x_disp, x_win, property, 0, num_bytes, True, - AnyPropertyType, &type, &format, &len, &tmp, &data) - != Success) { - XFree (data); // FIXME is this correct for this instance? - return; - } - - // get bytes to keys.c - for (p = data; num_bytes && *p; p++, num_bytes--) { - Key_Event (QFK_UNKNOWN, *p, 1); - Key_Event (QFK_UNKNOWN, 0, 0); - } - XFree (data); -} - static void enter_notify (XEvent *event) { + x11_have_pointer = true; x_time = event->xcrossing.time; - p_mouse_x = event->xmotion.x; - p_mouse_y = event->xmotion.y; + x11_mouse.x = event->xmotion.x; + x11_mouse.y = event->xmotion.y; + if (!x_have_focus && in_auto_focus) { + XSetInputFocus (x_disp, x_win, RevertToPointerRoot, CurrentTime); + } } static void -XLateKey (XKeyEvent * ev, int *k, int *u) +leave_notify (XEvent *event) +{ + x11_have_pointer = false; +} + +static void +XLateKey (XKeyEvent *ev, int *k, int *u) { char buffer[4]; int unicode; @@ -203,8 +286,7 @@ XLateKey (XKeyEvent * ev, int *k, int *u) XComposeStatus compose; keysym = XLookupKeysym (ev, 0); - XLookupString (ev, buffer, sizeof(buffer), &shifted_keysym, - &compose); + XLookupString (ev, buffer, sizeof(buffer), &shifted_keysym, &compose); unicode = (byte) buffer[0]; switch (keysym) { @@ -597,82 +679,19 @@ XLateKey (XKeyEvent * ev, int *k, int *u) } static void -x11_keydest_callback (keydest_t key_dest) +in_x11_send_focus_event (int gain) { - if (key_dest == key_game) { - XAutoRepeatOff (x_disp); - } else { - XAutoRepeatOn (x_disp); - } -} - -static void -event_key (XEvent *event) -{ - int key, unicode; - - x_time = event->xkey.time; - XLateKey (&event->xkey, &key, &unicode); - Key_Event (key, unicode, event->type == KeyPress); -} - -static void -event_button (XEvent *event) -{ - unsigned but; - - x_time = event->xbutton.time; - - but = event->xbutton.button; - if (but > 32) - return; - if (but == 2) - but = 3; - else if (but == 3) - but = 2; - switch (but) { - default: - Key_Event (QFM_BUTTON1 + but - 1, 0, event->type == ButtonPress); - break; - case 4: - Key_Event (QFM_WHEEL_UP, 0, event->type == ButtonPress); - break; - case 5: - Key_Event (QFM_WHEEL_DOWN, 0, event->type == ButtonPress); - break; - } -} - -static void -event_focusout (XEvent *event) -{ - Key_FocusEvent (0); - if (x_have_focus) { - x_have_focus = false; - if (in_snd_block->int_val) { - S_BlockSound (); - CDAudio_Pause (); - } - X11_RestoreGamma (); - } -} - -static void -event_focusin (XEvent *event) -{ - x_have_focus = true; - Key_FocusEvent (1); - if (in_snd_block->int_val) { - S_UnblockSound (); - CDAudio_Resume (); - } - VID_UpdateGamma (vid_gamma); + IE_event_t event = { + .type = gain ? ie_app_gain_focus : ie_app_lose_focus, + .when = Sys_LongTime (), + }; + IE_Send_Event (&event); } static void center_pointer (void) { - XEvent event; + XEvent event = {}; event.type = MotionNotify; event.xmotion.display = x_disp; @@ -684,39 +703,361 @@ center_pointer (void) viddef.width / 2, viddef.height / 2); } +static void +in_x11_send_axis_event (int devid, in_axisinfo_t *axis) +{ + IE_event_t event = { + .type = ie_axis, + .when = Sys_LongTime (), + .axis = { + .data = x11_mouse_device.event_data, + .devid = devid, + .axis = axis->axis, + .value = axis->value, + }, + }; + IE_Send_Event (&event); +} + +static int +in_x11_send_mouse_event (IE_mouse_type type) +{ + IE_event_t event = { + .type = ie_mouse, + .when = Sys_LongTime (), + .mouse = x11_mouse, + }; + event.mouse.type = type; + return IE_Send_Event (&event); +} + +static int +in_x11_send_key_event (void) +{ + IE_event_t event = { + .type = ie_key, + .when = Sys_LongTime (), + .key = x11_key, + }; + return IE_Send_Event (&event); +} + +static void +in_x11_send_button_event (int devid, in_buttoninfo_t *button, void *event_data) +{ + IE_event_t event = { + .type = ie_button, + .when = Sys_LongTime (), + .button = { + .data = event_data, + .devid = devid, + .button = button->button, + .state = button->state, + }, + }; + IE_Send_Event (&event); +} + +static void +selection_notify (XEvent *event) +{ + unsigned char *data, *p; + unsigned long num_bytes; + unsigned long tmp, len; + int format; + Atom type, property; + + x_time = event->xselection.time; + + if ((property = event->xselection.property) == None) + return; + + XGetWindowProperty (x_disp, x_win, property, 0, 0, False, AnyPropertyType, + &type, &format, &len, &num_bytes, &data); + if (num_bytes <= 0) + return; + if (XGetWindowProperty (x_disp, x_win, property, 0, num_bytes, True, + AnyPropertyType, &type, &format, &len, &tmp, &data) + != Success) { + XFree (data); // FIXME is this correct for this instance? + return; + } + + //FIXME send paste event instead of key presses? + x11_key.code = 0; + for (p = data; num_bytes && *p; p++, num_bytes--) { + x11_key.unicode = *p; + in_x11_send_key_event (); + } + XFree (data); +} + static void event_motion (XEvent *event) { x_time = event->xmotion.time; if (x_time <= x_mouse_time) { - p_mouse_x = event->xmotion.x; - p_mouse_y = event->xmotion.y; + x11_mouse.x = event->xmotion.x; + x11_mouse.y = event->xmotion.y; return; } if (dga_active) { - in_mouse_x += event->xmotion.x_root; - in_mouse_y += event->xmotion.y_root; + x11_mouse_axes[0].value = event->xmotion.x_root; + x11_mouse_axes[1].value = event->xmotion.y_root; } else { - if (vid_fullscreen->int_val || input_grabbed) { + if (vid_fullscreen || input_grabbed) { if (!event->xmotion.send_event) { - int dist_x = abs (viddef.width / 2 - event->xmotion.x); - int dist_y = abs (viddef.height / 2 - event->xmotion.y); - in_mouse_x += (event->xmotion.x - p_mouse_x); - in_mouse_y += (event->xmotion.y - p_mouse_y); + int center_x = viddef.width / 2; + int center_y = viddef.height / 2; + unsigned dist_x = abs (center_x - event->xmotion.x); + unsigned dist_y = abs (center_y - event->xmotion.y); + + x11_mouse_axes[0].value = event->xmotion.x - x11_mouse.x; + x11_mouse_axes[1].value = event->xmotion.y - x11_mouse.y; if (dist_x > viddef.width / 4 || dist_y > viddef.height / 4) { center_pointer (); } } } else { - in_mouse_x += (event->xmotion.x - p_mouse_x); - in_mouse_y += (event->xmotion.y - p_mouse_y); + x11_mouse_axes[0].value = event->xmotion.x - x11_mouse.x; + x11_mouse_axes[1].value = event->xmotion.y - x11_mouse.y; } - p_mouse_x = event->xmotion.x; - p_mouse_y = event->xmotion.y; + } + + x11_mouse.shift = event->xmotion.state & 0xff; + x11_mouse.x = event->xmotion.x; + x11_mouse.y = event->xmotion.y; + if (!in_x11_send_mouse_event (ie_mousemove)) { + in_x11_send_axis_event (x11_mouse_device.devid, &x11_mouse_axes[0]); + in_x11_send_axis_event (x11_mouse_device.devid, &x11_mouse_axes[1]); } } +static void +event_button (XEvent *event) +{ + unsigned but; + + x_time = event->xbutton.time; + + // x11 buttons are 1-based + but = event->xbutton.button - 1; + if (but > X11_MOUSE_BUTTONS) { + return; + } + + int press = event->type == ButtonPress; + + x11_mouse_buttons[but].state = press; + + x11_mouse.shift = event->xmotion.state & 0xff; + x11_mouse.x = event->xmotion.x; + x11_mouse.y = event->xmotion.y; + if (press) { + x11_mouse.buttons |= 1 << but; + } else { + x11_mouse.buttons &= ~(1 << but); + } + if (!in_x11_send_mouse_event (press ? ie_mousedown : ie_mouseup)) { + in_x11_send_button_event (x11_mouse_device.devid, + &x11_mouse_buttons[but], + x11_mouse_device.event_data); + } +} + +static unsigned +in_x11_process_key_button (unsigned keycode, int press) +{ + // X11 protocol supports only 256 keys. The key codes are the AT scan codes + // offset by 8 (so Esc is 9 instead of 1). + unsigned key = (keycode - 8) & 0xff; + x11_key_buttons[key].state = press; + return key; +} + +static void +event_key (XEvent *event) +{ + unsigned key = 0; + + x_time = event->xkey.time; + if (!x11_have_xi) { + key = in_x11_process_key_button (event->xkey.keycode, + event->type == KeyPress); + } + x11_key.shift = event->xmotion.state & 0xff; + XLateKey (&event->xkey, &x11_key.code, &x11_key.unicode); + if (!(event->type == KeyPress && in_x11_send_key_event ()) + && !x11_have_xi) { + in_x11_send_button_event (x11_keyboard_device.devid, + &x11_key_buttons[key], + x11_keyboard_device.event_data); + } +} + +#ifdef HAVE_XI2 +static void +xi_raw_key (void *event, int press) +{ + if (!x_have_focus) { + //avoid being a keylogger + return; + } + XIRawEvent *re = event; + unsigned key = in_x11_process_key_button (re->detail, press); + + // Send only the button press: event_key takes care of the UI key event, + // which includes character translation and repeat (XInput2 raw key events + // do not repeat) + in_x11_send_button_event (x11_keyboard_device.devid, + &x11_key_buttons[key], + x11_keyboard_device.event_data); +} + +static void +xi_raw_key_press (void *event) +{ + xi_raw_key (event, 1); +} + +static void +xi_raw_key_resease (void *event) +{ + xi_raw_key (event, 0); +} + +static void +xi_raw_motion (void *event) +{ + int root_x, root_y; + Window root_w, child_w; + XIRawEvent *re = event; + + //FIXME it turns out mice can have multiple axes (my trackball has 4: + //usual x and y, +scroll up/down and left/right (clicky and icky, but + //still...) + unsigned mask = re->valuators.mask[0] & 3; + if (!mask) { + return; + } + + x11_mouse_axes[0].value = 0; + x11_mouse_axes[1].value = 0; + for (int axis = 0, ind = 0; mask; axis++, mask >>= 1) { + if (mask & 1) { + x11_mouse_axes[axis].value = re->raw_values[ind++]; + } + } + XQueryPointer (x_disp, x_win, &root_w, &child_w, &root_x, &root_y, + &x11_mouse.x, &x11_mouse.y, &x11_mouse.shift); + // want only the modifier state + x11_mouse.shift &= 0xff; + if (!in_x11_send_mouse_event (ie_mousemove)) { + in_x11_send_axis_event (x11_mouse_device.devid, + &x11_mouse_axes[0]); + in_x11_send_axis_event (x11_mouse_device.devid, + &x11_mouse_axes[1]); + } +} + +static void +xi_raw_button (void *event, int press) +{ + int root_x, root_y; + Window root_w, child_w; + unsigned button; + XIRawEvent *re = event; + + // x11 buttons are 1-based + button = re->detail - 1; + if (button > X11_MOUSE_BUTTONS) { + return; + } + XQueryPointer (x_disp, x_win, &root_w, &child_w, &root_x, &root_y, + &x11_mouse.x, &x11_mouse.y, &x11_mouse.shift); + // want only the modifier state + x11_mouse.shift &= 0xff; + if (press) { + x11_mouse.buttons |= 1 << button; + } else { + x11_mouse.buttons &= ~(1 << button); + } + x11_mouse_buttons[button].state = press; + if (!in_x11_send_mouse_event (press ? ie_mousedown : ie_mouseup)) { + in_x11_send_button_event (x11_mouse_device.devid, + &x11_mouse_buttons[button], + x11_mouse_device.event_data); + } +} + +static void +xi_raw_button_press (void *event) +{ + xi_raw_button (event, 1); +} + +static void +xi_raw_button_resease (void *event) +{ + xi_raw_button (event, 0); +} + +#ifdef HAVE_XFIXES +static void +xi_barrier_hit (void *event) +{ + __auto_type be = *(XIBarrierEvent *) event; + + if (be.barrier != x11_left_barrier + && be.barrier != x11_right_barrier + && be.barrier != x11_top_barrier + && be.barrier != x11_bottom_barrier) { + Sys_MaskPrintf (SYS_vid, "xi_barrier_hit: bad barrier %ld\n", + be.barrier); + return; + } + + center_pointer (); +} + +static void +xi_barrier_leave (void *event) +{ +} +#endif + +static void +event_generic (XEvent *event) +{ + // XI_LASTEVENT is the actual last event, not +1 + static void (*xi_event_handlers[XI_LASTEVENT + 1]) (void *) = { + [XI_RawKeyPress] = xi_raw_key_press, + [XI_RawKeyRelease] = xi_raw_key_resease, + [XI_RawMotion] = xi_raw_motion, + [XI_RawButtonPress] = xi_raw_button_press, + [XI_RawButtonRelease] = xi_raw_button_resease, +#ifdef HAVE_XFIXES + [XI_BarrierHit] = xi_barrier_hit, + [XI_BarrierLeave] = xi_barrier_leave, +#endif + }; + XGenericEventCookie *cookie = &event->xcookie; + + if (cookie->type != GenericEvent || cookie->extension != xi_opcode + || !XGetEventData (x_disp, cookie)) { + return; + } + + if ((unsigned) cookie->evtype <= XI_LASTEVENT + && xi_event_handlers[cookie->evtype]) { + xi_event_handlers[cookie->evtype] (cookie->data); + } + + XFreeEventData (x_disp, cookie); +} +#endif + static void grab_error (int code, const char *device) { @@ -742,14 +1083,108 @@ grab_error (int code, const char *device) Sys_Printf ("failed to grab %s: %s\n", device, reason); } -void -IN_LL_Grab_Input (int grab) +#ifdef HAVE_XFIXES +static void +in_x11_remove_barriers (void) +{ + if (x11_left_barrier > 0) { + XFixesDestroyPointerBarrier (x_disp, x11_left_barrier); + x11_left_barrier = 0; + } + if (x11_right_barrier > 0) { + XFixesDestroyPointerBarrier (x_disp, x11_right_barrier); + x11_right_barrier = 0; + } + if (x11_top_barrier > 0) { + XFixesDestroyPointerBarrier (x_disp, x11_top_barrier); + x11_top_barrier = 0; + } + if (x11_bottom_barrier > 0) { + XFixesDestroyPointerBarrier (x_disp, x11_bottom_barrier); + x11_bottom_barrier = 0; + } +} + +static void +in_x11_setup_barriers (int xpos, int ypos, int xlen, int ylen) +{ + in_x11_remove_barriers (); + + int lx = bound (0, xpos, x_width - 1); + int ty = bound (0, ypos, x_height - 1); + int rx = bound (0, xpos + xlen - 1, x_width - 1); + int by = bound (0, ypos + ylen - 1, x_height - 1); + x11_left_barrier = XFixesCreatePointerBarrier (x_disp, x_root, + lx, ty-1, lx, by+1, + BarrierPositiveX, 0, 0); + x11_right_barrier = XFixesCreatePointerBarrier (x_disp, x_root, + rx, ty-1, rx, by+1, + BarrierNegativeX, 0, 0); + x11_top_barrier = XFixesCreatePointerBarrier (x_disp, x_root, + lx-1, ty, rx+1, ty, + BarrierPositiveY, 0, 0); + x11_bottom_barrier = XFixesCreatePointerBarrier (x_disp, x_root, + lx-1, by, rx+1, by, + BarrierNegativeY, 0, 0); +} +#endif + +static void +event_focusout (XEvent *event) +{ + if (x_have_focus) { + x_have_focus = false; +#ifdef HAVE_XFIXES + in_x11_remove_barriers (); +#endif + in_x11_send_focus_event (0); + if (in_snd_block) { + S_BlockSound (); + CDAudio_Pause (); + } + X11_RestoreGamma (); + } +} + +static void +event_focusin (XEvent *event) +{ + in_x11_send_focus_event (1); + x_have_focus = true; + if (in_snd_block) { + S_UnblockSound (); + CDAudio_Resume (); + } + if (input_grabbed) { +#ifdef HAVE_XFIXES + in_x11_setup_barriers (x11_aw.xpos, x11_aw.ypos, + x11_aw.xlen, x11_aw.ylen); +#endif + } + VID_UpdateGamma (); +} + +static void +in_x11_grab_input (void *data, int grab) { if (!x_disp || !x_win) return; +#ifdef HAVE_XFIXES + if (xf_opcode) { + input_grabbed = grab; + if (input_grabbed) { + in_x11_setup_barriers (x11_aw.xpos, x11_aw.ypos, + x11_aw.xlen, x11_aw.ylen); + } else { + in_x11_remove_barriers (); + } + return; + } +#endif + if (vid_fullscreen) - grab = grab || vid_fullscreen->int_val; + grab = grab || vid_fullscreen; if ((input_grabbed && grab) || (!input_grabbed && !grab)) return; @@ -771,96 +1206,444 @@ IN_LL_Grab_Input (int grab) return; } input_grabbed = 1; - in_dga_f (in_dga); + in_dga_f (0, &in_dga_cvar); } else { XUngrabPointer (x_disp, CurrentTime); XUngrabKeyboard (x_disp, CurrentTime); input_grabbed = 0; - in_dga_f (in_dga); + in_dga_f (0, &in_dga_cvar); + } +} +#ifdef X11_USE_SELECT +static void +in_x11_add_select (qf_fd_set *fdset, int *maxfd, void *data) +{ + QF_FD_SET (x11_fd, fdset); + if (*maxfd < x11_fd) { + *maxfd = x11_fd; } } -void -IN_LL_ProcessEvents (void) +static void +in_x11_check_select (qf_fd_set *fdset, void *data) +{ + if (QF_FD_ISSET (x11_fd, fdset)) { + Sys_Printf ("in_x11_check_select\n"); + X11_ProcessEvents (); // Get events from X server. + } +} +#else +static void +in_x11_process_events (void *data) { X11_ProcessEvents (); // Get events from X server. } - -void -IN_LL_Shutdown (void) +#endif +static void +in_x11_axis_info (void *data, void *device, in_axisinfo_t *axes, int *numaxes) { - Sys_MaskPrintf (SYS_VID, "IN_LL_Shutdown\n"); - in_mouse_avail = 0; - if (x_disp) { - XAutoRepeatOn (x_disp); - dga_off (); + x11_device_t *dev = device; + if (!axes) { + *numaxes = dev->num_axes; + return; } - if (in_mouse_accel && !in_mouse_accel->int_val) - X11_RestoreMouseAcceleration (); - X11_CloseDisplay (); + if (*numaxes > dev->num_axes) { + *numaxes = dev->num_axes; + } + memcpy (axes, dev->axes, *numaxes * sizeof (in_axisinfo_t)); } -void -IN_LL_Init (void) +static void +in_x11_button_info (void *data, void *device, in_buttoninfo_t *buttons, + int *numbuttons) +{ + x11_device_t *dev = device; + if (!buttons) { + *numbuttons = dev->num_buttons; + return; + } + if (*numbuttons > dev->num_buttons) { + *numbuttons = dev->num_buttons; + } + memcpy (buttons, dev->buttons, *numbuttons * sizeof (in_buttoninfo_t)); +} + +static const char * +in_x11_get_axis_name (void *data, void *device, int axis_num) +{ + x11_device_t *dev = device; + const char *name = 0; + + if (dev == &x11_keyboard_device) { + // keyboards don't have axes... + } else if (dev == &x11_mouse_device) { + if ((unsigned) axis_num < SIZE (x11_mouse_axis_names)) { + name = x11_mouse_axis_names[axis_num]; + } + } + return name; +} + +static const char * +in_x11_get_button_name (void *data, void *device, int button_num) +{ + x11_device_t *dev = device; + const char *name = 0; + + if (dev == &x11_keyboard_device) { + // X11 protocol supports only 256 keys. The key codes are the AT scan + // codes offset by 8 (so Esc is 9 instead of 1). + KeyCode keycode = (button_num + 8) & 0xff; + KeySym keysym = XkbKeycodeToKeysym (x_disp, keycode, 0, 0); + if (keysym != NoSymbol) { + name = XKeysymToString (keysym); + } + } else if (dev == &x11_mouse_device) { + if ((unsigned) button_num < SIZE (x11_mouse_button_names)) { + name = x11_mouse_button_names[button_num]; + } + } + return name; +} + +static int +in_x11_get_axis_info (void *data, void *device, int axis_num, + in_axisinfo_t *info) +{ + x11_device_t *dev = device; + if (axis_num < 0 || axis_num > dev->num_axes) { + return 0; + } + *info = dev->axes[axis_num]; + return 1; +} + +static int +in_x11_get_button_info (void *data, void *device, int button_num, + in_buttoninfo_t *info) +{ + x11_device_t *dev = device; + if (button_num < 0 || button_num > dev->num_buttons) { + return 0; + } + *info = dev->buttons[button_num]; + return 1; +} + +static int +in_x11_get_axis_num (void *data, void *device, const char *axis_name) +{ + x11_device_t *dev = device; + int num = -1; + + if (dev == &x11_keyboard_device) { + // keyboards don't have axes... + } else if (dev == &x11_mouse_device) { + for (size_t i = 0; i < SIZE (x11_mouse_axis_names); i++) { + if (strcasecmp (axis_name, x11_mouse_axis_names[i]) == 0) { + num = i; + break; + } + } + } + return num; +} + +static int +in_x11_get_button_num (void *data, void *device, const char *button_name) +{ + x11_device_t *dev = device; + int num = -1; + + if (dev == &x11_keyboard_device) { + KeySym keysym = XStringToKeysym (button_name); + if (keysym != NoSymbol) { + KeyCode keycode = XKeysymToKeycode (x_disp, keysym); + if (keycode != 0) { + // X11 protocol supports only 256 keys. The key codes are the + // AT scan codes offset by 8 (so Esc is 9 instead of 1). + num = (keycode - 8) & 0xff; + } + } + } else if (dev == &x11_mouse_device) { + for (size_t i = 0; i < SIZE (x11_mouse_button_names); i++) { + if (strcasecmp (button_name, x11_mouse_button_names[i]) == 0) { + num = i; + break; + } + } + } + return num; +} + +static void +in_x11_shutdown (void *data) +{ + Sys_MaskPrintf (SYS_vid, "in_x11_shutdown\n"); + if (x_disp) { +// XAutoRepeatOn (x_disp); + dga_off (); + } + if (!in_mouse_accel) + X11_RestoreMouseAcceleration (); +} + +static void +in_x11_set_device_event_data (void *device, void *event_data, void *data) +{ + x11_device_t *dev = device; + dev->event_data = event_data; +} + +static void * +in_x11_get_device_event_data (void *device, void *data) +{ + x11_device_t *dev = device; + return dev->event_data; +} + +static void +in_x11_init_cvars (void *data) +{ + Cvar_Register (&in_auto_focus_cvar, 0, 0); + Cvar_Register (&in_snd_block_cvar, 0, 0); + Cvar_Register (&in_dga_cvar, in_dga_f, 0); + Cvar_Register (&in_mouse_accel_cvar, in_mouse_accel_f, 0); +} + +static void +x11_add_device (x11_device_t *dev) +{ + for (int i = 0; i < dev->num_axes; i++) { + dev->axes[i].axis = i; + } + for (int i = 0; i < dev->num_buttons; i++) { + dev->buttons[i].button = i; + } + dev->devid = IN_AddDevice (x11_driver_handle, dev, dev->name, dev->name); +} + +#ifdef HAVE_XI2 +static int +in_x11_check_xi2 (void) +{ + // Support XI 2.2 + int major = 2, minor = 2; + int event, error; + int ret; + + if (!XQueryExtension (x_disp, "XInputExtension", + &xi_opcode, &event, &error)) { + Sys_MaskPrintf (SYS_vid, "X Input extenions not available.\n"); + return 0; + } + if ((ret = XIQueryVersion (x_disp, &major, &minor)) == BadRequest) { + Sys_MaskPrintf (SYS_vid, + "No XI2 support. Server supports only %d.%d\n", + major, minor); + return 0; + } else if (ret != Success) { + Sys_MaskPrintf (SYS_vid, "in_x11_check_xi2: Xlib return bad: %d\n", + ret); + } + Sys_MaskPrintf (SYS_vid, "XI2 supported: version %d.%d, op: %d err: %d\n", + major, minor, xi_opcode, error); + +#ifdef HAVE_XFIXES + if (!XQueryExtension (x_disp, "XFIXES", &xf_opcode, &event, &error)) { + Sys_MaskPrintf (SYS_vid, "X fixes extenions not available.\n"); + return 0; + } + major = 5, minor = 0; + if (!XFixesQueryVersion (x_disp, &major, &minor) + || (major * 10 + minor) < 50) { + Sys_MaskPrintf (SYS_vid, "Need X fixes 5.0.\n"); + return 0; + } + Sys_MaskPrintf (SYS_vid, + "XFixes supported: version %d.%d, op: %d err: %d\n", + major, minor, xf_opcode, error); +#endif + return 1; +} + +static void +in_x11_xi_select_events (void) +{ + byte mask[(XI_LASTEVENT + 7) / 8] = {}; + XIEventMask evmask = { + .deviceid = XIAllMasterDevices, + .mask_len = sizeof (mask), + .mask = mask, + }; + XISetMask (mask, XI_BarrierHit); + XISetMask (mask, XI_BarrierLeave); + XISetMask (mask, XI_RawKeyPress); + XISetMask (mask, XI_RawKeyRelease); + XISelectEvents (x_disp, x_root, &evmask, 1); +} + +static void +in_x11_xi_setup_grabs (void) +{ + // FIXME normal apps aren't supposed to care about the client pointer, + // but grabbing all master devices grabs the keyboard, too, which blocks + // alt-tab and the like + int dev; + XIGetClientPointer (x_disp, x_win, &dev); + + byte mask[(XI_LASTEVENT + 7) / 8] = {}; + XIEventMask evmask = { + .deviceid = dev, + .mask_len = sizeof (mask), + .mask = mask, + }; + XISetMask (mask, XI_RawMotion); + XISetMask (mask, XI_RawButtonPress); + XISetMask (mask, XI_RawButtonRelease); + + XIGrabModifiers modif = { + .modifiers = XIAnyModifier, + }; + + // Grab mouse events on the app window so window manager actions (eg, + // alt-middle-click) don't interfere. However, the keyboard is not grabbed + // so the user always has a way to take control (assuming the window + // manager provides such, but most do). + XIGrabEnter (x_disp, dev, x_win, None, XIGrabModeAsync, XIGrabModeAsync, + 0, &evmask, 1, &modif); +} +#endif + +static void +x11_app_window (const IE_event_t *ie_event) +{ + x11_aw = ie_event->app_window; + Sys_MaskPrintf (SYS_vid, "window: %d %d %d %d\n", + x11_aw.xpos, x11_aw.ypos, x11_aw.xlen, x11_aw.ylen); +#ifdef HAVE_XFIXES + if (input_grabbed) { + in_x11_setup_barriers (x11_aw.xpos, x11_aw.ypos, + x11_aw.xlen, x11_aw.ylen); + } +#endif +} + +static int +x11_event_handler (const IE_event_t *ie_event, void *unused) +{ + static void (*handlers[ie_event_count]) (const IE_event_t *ie_event) = { + [ie_app_window] = x11_app_window, + }; + if ((unsigned) ie_event->type >= ie_event_count + || !handlers[ie_event->type]) { + return 0; + } + handlers[ie_event->type] (ie_event); + return 1; +} + +long +IN_X11_Preinit (void) { - // open the display if (!x_disp) Sys_Error ("IN: No display!!"); - if (!x_win) - Sys_Error ("IN: No window!!"); - X11_OpenDisplay (); // call to increment the reference counter - - { - int attribmask = CWEventMask; - - XWindowAttributes attribs_1; - XSetWindowAttributes attribs_2; - - XGetWindowAttributes (x_disp, x_win, &attribs_1); - - attribs_2.event_mask = attribs_1.your_event_mask | X11_INPUT_MASK; - - XChangeWindowAttributes (x_disp, x_win, attribmask, &attribs_2); - } + long event_mask = X11_INPUT_MASK; + x11_event_handler_id = IE_Add_Handler (x11_event_handler, 0); +#ifdef HAVE_XI2 + x11_have_xi = in_x11_check_xi2 (); +#endif X11_AddEvent (KeyPress, &event_key); X11_AddEvent (KeyRelease, &event_key); X11_AddEvent (FocusIn, &event_focusin); X11_AddEvent (FocusOut, &event_focusout); X11_AddEvent (SelectionNotify, &selection_notify); X11_AddEvent (EnterNotify, &enter_notify); + X11_AddEvent (LeaveNotify, &leave_notify); - if (!COM_CheckParm ("-nomouse")) { - dga_avail = VID_CheckDGA (x_disp, NULL, NULL, NULL); - Sys_MaskPrintf (SYS_VID, "VID_CheckDGA returned %d\n", dga_avail); - + if (x11_have_xi) { +#ifdef HAVE_XI2 + X11_AddEvent (GenericEvent, &event_generic); +#endif + } else { + X11_AddEvent (MotionNotify, &event_motion); X11_AddEvent (ButtonPress, &event_button); X11_AddEvent (ButtonRelease, &event_button); - X11_AddEvent (MotionNotify, &event_motion); - - in_mouse_avail = 1; } - Key_KeydestCallback (x11_keydest_callback); - Cmd_AddCommand ("in_paste_buffer", in_paste_buffer_f, "Paste the contents of X's C&P buffer to the console"); + return event_mask; } void -IN_LL_Init_Cvars (void) +IN_X11_Postinit (void) { - in_snd_block = Cvar_Get ("in_snd_block", "0", CVAR_ARCHIVE, NULL, - "block sound output on window focus loss"); - in_dga = Cvar_Get ("in_dga", "0", CVAR_ARCHIVE, in_dga_f, //FIXME 0 until X fixed - "DGA Input support"); - in_mouse_accel = Cvar_Get ("in_mouse_accel", "1", CVAR_ARCHIVE, - in_mouse_accel_f, - "set to 0 to remove mouse acceleration"); + if (!x_disp) + Sys_Error ("IN: No display!!"); + if (!x_win) + Sys_Error ("IN: No window!!"); + + if (x11_have_xi) { +#ifdef HAVE_XI2 + in_x11_xi_select_events (); + in_x11_xi_setup_grabs (); +#endif + } else { + dga_avail = VID_CheckDGA (x_disp, NULL, NULL, NULL); + Sys_MaskPrintf (SYS_vid, "VID_CheckDGA returned %d\n", + dga_avail); + } } -void -IN_LL_ClearStates (void) +static void +in_x11_init (void *data) +{ + x11_fd = ConnectionNumber (x_disp); + + x11_add_device (&x11_keyboard_device); + x11_add_device (&x11_mouse_device); +} + +static void +in_x11_clear_states (void *data) { } + +static in_driver_t in_x11_driver = { + .init_cvars = in_x11_init_cvars, + .init = in_x11_init, + .shutdown = in_x11_shutdown, + .set_device_event_data = in_x11_set_device_event_data, + .get_device_event_data = in_x11_get_device_event_data, +#ifdef X11_USE_SELECT + .add_select = in_x11_add_select, + .check_select = in_x11_check_select, +#else + .process_events = in_x11_process_events, +#endif + .clear_states = in_x11_clear_states, + .grab_input = in_x11_grab_input, + + .axis_info = in_x11_axis_info, + .button_info = in_x11_button_info, + + .get_axis_name = in_x11_get_axis_name, + .get_button_name = in_x11_get_button_name, + + .get_axis_num = in_x11_get_axis_num, + .get_button_num = in_x11_get_button_num, + + .get_axis_info = in_x11_get_axis_info, + .get_button_info = in_x11_get_button_info, +}; + +static void __attribute__((constructor)) +in_x11_register_driver (void) +{ + x11_driver_handle = IN_RegisterDriver (&in_x11_driver, 0); +} + +int x11_force_link; diff --git a/libs/video/targets/joy.c b/libs/video/targets/joy.c index 0a209cdfe..827f96574 100644 --- a/libs/video/targets/joy.c +++ b/libs/video/targets/joy.c @@ -42,13 +42,45 @@ #include "compat.h" #include -cvar_t *joy_device; // Joystick device name -cvar_t *joy_enable; // Joystick enabling flag -cvar_t *joy_amp; // Joystick amplification -cvar_t *joy_pre_amp; // Joystick pre-amplification +char *joy_device; +static cvar_t joy_device_cvar = { + .name = "joy_device", + .description = + "Joystick device", + .default_value = "none", + .flags = CVAR_ROM, + .value = { .type = 0, .value = &joy_device }, +}; +int joy_enable; +static cvar_t joy_enable_cvar = { + .name = "joy_enable", + .description = + "Joystick enable flag", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &joy_enable }, +}; +float joy_amp; +static cvar_t joy_amp_cvar = { + .name = "joy_amp", + .description = + "Joystick amplification", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &joy_amp }, +}; +float joy_pre_amp; +static cvar_t joy_pre_amp_cvar = { + .name = "joy_pre_amp", + .description = + "Joystick pre-amplification", + .default_value = "0.01", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &joy_pre_amp }, +}; -qboolean joy_found = false; -qboolean joy_active = false; +bool joy_found = false; +bool joy_active = false; struct joy_axis joy_axes[JOY_MAX_AXES]; struct joy_button joy_buttons[JOY_MAX_BUTTONS]; @@ -117,11 +149,11 @@ JOY_Move (void) { struct joy_axis *ja; float value; - float amp = joy_amp->value * in_amp->value; - float pre = joy_pre_amp->value * in_pre_amp->value; + float amp = joy_amp * in_amp; + float pre = joy_pre_amp * in_pre_amp; int i; - if (!joy_active || !joy_enable->int_val) + if (!joy_active || !joy_enable) return; for (i = 0; i < JOY_MAX_AXES; i++) { @@ -156,7 +188,7 @@ JOY_Init (void) int i; if (JOY_Open () == -1) { - Sys_MaskPrintf (SYS_VID, "JOY: Joystick not found.\n"); + Sys_MaskPrintf (SYS_vid, "JOY: Joystick not found.\n"); joy_found = false; joy_active = false; return; @@ -164,13 +196,13 @@ JOY_Init (void) joy_found = true; - if (!joy_enable->int_val) { - Sys_MaskPrintf (SYS_VID, "JOY: Joystick found, but not enabled.\n"); + if (!joy_enable) { + Sys_MaskPrintf (SYS_vid, "JOY: Joystick found, but not enabled.\n"); joy_active = false; JOY_Close (); } - Sys_MaskPrintf (SYS_VID, "JOY: Joystick found and activated.\n"); + Sys_MaskPrintf (SYS_vid, "JOY: Joystick found and activated.\n"); // Initialize joystick if found and enabled for (i = 0; i < JOY_MAX_BUTTONS; i++) { @@ -181,9 +213,9 @@ JOY_Init (void) } static void -joyamp_f (cvar_t *var) +joyamp_f (void *data, const cvar_t *cvar) { - Cvar_Set (var, va ("%g", max (0.0001, var->value))); + Cvar_SetVar (cvar, va (0, "%g", max (0.0001, *(float *)data))); } typedef struct { @@ -465,14 +497,10 @@ JOY_Init_Cvars (void) { int i; - joy_device = Cvar_Get ("joy_device", "/dev/input/js0", - CVAR_NONE | CVAR_ROM, 0, "Joystick device"); - joy_enable = Cvar_Get ("joy_enable", "1", CVAR_NONE | CVAR_ARCHIVE, 0, - "Joystick enable flag"); - joy_amp = Cvar_Get ("joy_amp", "1", CVAR_NONE | CVAR_ARCHIVE, joyamp_f, - "Joystick amplification"); - joy_pre_amp = Cvar_Get ("joy_pre_amp", "0.01", CVAR_NONE | CVAR_ARCHIVE, - joyamp_f, "Joystick pre-amplification"); + Cvar_Register (&joy_device_cvar, 0, 0); + Cvar_Register (&joy_enable_cvar, 0, 0); + Cvar_Register (&joy_amp_cvar, joyamp_f, &joy_amp); + Cvar_Register (&joy_pre_amp_cvar, joyamp_f, &joy_pre_amp); Cmd_AddCommand ("in_joy", in_joy_f, "Configures the joystick behaviour"); diff --git a/libs/video/targets/joy_linux.c b/libs/video/targets/joy_linux.c index e99abc8da..3a7e65a09 100644 --- a/libs/video/targets/joy_linux.c +++ b/libs/video/targets/joy_linux.c @@ -48,7 +48,7 @@ JOY_Read (void) { struct js_event event; - if (!joy_active || !joy_enable->int_val) + if (!joy_active || !joy_enable) return; while (read (joy_handle, &event, sizeof (struct js_event)) > -1) { @@ -82,7 +82,7 @@ int JOY_Open (void) { // Open joystick device - joy_handle = open (joy_device->string, O_RDONLY | O_NONBLOCK); + joy_handle = open (joy_device, O_RDONLY | O_NONBLOCK); if (joy_handle < 0) { return -1; } @@ -96,8 +96,8 @@ JOY_Close (void) i = close (joy_handle); if (i) { - Sys_MaskPrintf (SYS_VID, "JOY: Failed to close joystick device!\n"); + Sys_MaskPrintf (SYS_vid, "JOY: Failed to close joystick device!\n"); } else { - Sys_MaskPrintf (SYS_VID, "JOY_Shutdown\n"); + Sys_MaskPrintf (SYS_vid, "JOY_Shutdown\n"); } } diff --git a/libs/video/targets/joy_win.c b/libs/video/targets/joy_win.c index babd2e270..3bc2c9d13 100644 --- a/libs/video/targets/joy_win.c +++ b/libs/video/targets/joy_win.c @@ -46,7 +46,15 @@ #include "compat.h" // Joystick variables and structures -cvar_t *joy_sensitivity; // Joystick sensitivity +float joy_sensitivity; +static cvar_t joy_sensitivity_cvar = { + .name = "joy_sensitivity", + .description = + "Joystick sensitivity", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &joy_sensitivity }, +}; // joystick defines and variables // where should defines be moved? @@ -82,29 +90,189 @@ JOYINFOEX ji; // when changing from one controller to another. this way at least something // works. -cvar_t *in_joystick; -cvar_t *joy_name; -cvar_t *joy_advanced; -cvar_t *joy_advaxisx; -cvar_t *joy_advaxisy; -cvar_t *joy_advaxisz; -cvar_t *joy_advaxisr; -cvar_t *joy_advaxisu; -cvar_t *joy_advaxisv; -cvar_t *joy_forwardthreshold; -cvar_t *joy_sidethreshold; -cvar_t *joy_pitchthreshold; -cvar_t *joy_yawthreshold; -cvar_t *joy_forwardsensitivity; -cvar_t *joy_sidesensitivity; -cvar_t *joy_pitchsensitivity; -cvar_t *joy_yawsensitivity; -cvar_t *joy_wwhack1; -cvar_t *joy_wwhack2; +char *in_joystick; +static cvar_t in_joystick_cvar = { + .name = "joystick", + .description = + "FIXME: No Description", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = 0, .value = &in_joystick }, +}; +char *joy_name; +static cvar_t joy_name_cvar = { + .name = "joyname", + .description = + "FIXME: No Description", + .default_value = "joystick", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &joy_name }, +}; +int joy_advanced; +static cvar_t joy_advanced_cvar = { + .name = "joyadvanced", + .description = + "FIXME: No Description", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &joy_advanced }, +}; +int joy_advaxisx; +static cvar_t joy_advaxisx_cvar = { + .name = "joyadvaxisx", + .description = + "FIXME: No Description", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &joy_advaxisx }, +}; +int joy_advaxisy; +static cvar_t joy_advaxisy_cvar = { + .name = "joyadvaxisy", + .description = + "FIXME: No Description", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &joy_advaxisy }, +}; +int joy_advaxisz; +static cvar_t joy_advaxisz_cvar = { + .name = "joyadvaxisz", + .description = + "FIXME: No Description", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &joy_advaxisz }, +}; +int joy_advaxisr; +static cvar_t joy_advaxisr_cvar = { + .name = "joyadvaxisr", + .description = + "FIXME: No Description", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &joy_advaxisr }, +}; +int joy_advaxisu; +static cvar_t joy_advaxisu_cvar = { + .name = "joyadvaxisu", + .description = + "FIXME: No Description", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &joy_advaxisu }, +}; +int joy_advaxisv; +static cvar_t joy_advaxisv_cvar = { + .name = "joyadvaxisv", + .description = + "FIXME: No Description", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &joy_advaxisv }, +}; +float joy_forwardthreshold; +static cvar_t joy_forwardthreshold_cvar = { + .name = "joyforwardthreshold", + .description = + "FIXME: No Description", + .default_value = "0.15", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &joy_forwardthreshold }, +}; +float joy_sidethreshold; +static cvar_t joy_sidethreshold_cvar = { + .name = "joysidethreshold", + .description = + "FIXME: No Description", + .default_value = "0.15", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &joy_sidethreshold }, +}; +float joy_pitchthreshold; +static cvar_t joy_pitchthreshold_cvar = { + .name = "joypitchthreshold", + .description = + "FIXME: No Description", + .default_value = "0.15", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &joy_pitchthreshold }, +}; +float joy_yawthreshold; +static cvar_t joy_yawthreshold_cvar = { + .name = "joyyawthreshold", + .description = + "FIXME: No Description", + .default_value = "0.15", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &joy_yawthreshold }, +}; +float joy_forwardsensitivity; +static cvar_t joy_forwardsensitivity_cvar = { + .name = "joyforwardsensitivity", + .description = + "FIXME: No Description", + .default_value = "-1.0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &joy_forwardsensitivity }, +}; +float joy_sidesensitivity; +static cvar_t joy_sidesensitivity_cvar = { + .name = "joysidesensitivity", + .description = + "FIXME: No Description", + .default_value = "-1.0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &joy_sidesensitivity }, +}; +float joy_pitchsensitivity; +static cvar_t joy_pitchsensitivity_cvar = { + .name = "joypitchsensitivity", + .description = + "FIXME: No Description", + .default_value = "1.0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &joy_pitchsensitivity }, +}; +float joy_yawsensitivity; +static cvar_t joy_yawsensitivity_cvar = { + .name = "joyyawsensitivity", + .description = + "FIXME: No Description", + .default_value = "-1.0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &joy_yawsensitivity }, +}; +int joy_wwhack1; +static cvar_t joy_wwhack1_cvar = { + .name = "joywwhack1", + .description = + "FIXME: No Description", + .default_value = "0.0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &joy_wwhack1 }, +}; +float joy_wwhack2; +static cvar_t joy_wwhack2_cvar = { + .name = "joywwhack2", + .description = + "FIXME: No Description", + .default_value = "0.0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &joy_wwhack2 }, +}; -cvar_t *joy_debug; +int joy_debug; +static cvar_t joy_debug_cvar = { + .name = "joy_debug", + .description = + "FIXME: No Description", + .default_value = "0.0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &joy_debug }, +}; -qboolean joy_advancedinit, joy_haspov; +bool joy_advancedinit, joy_haspov; DWORD joy_oldbuttonstate, joy_oldpovstate; int joy_id; DWORD joy_flags; @@ -130,7 +298,7 @@ RawValuePointer (int axis) return NULL; } -static qboolean +static bool _JOY_Read (void) { memset (&ji, 0, sizeof (ji)); @@ -142,10 +310,10 @@ _JOY_Read (void) // DInput driver that causes it to make 32668 the center point // instead // of 32768 - if (joy_wwhack1->int_val) { + if (joy_wwhack1) { ji.dwUpos += 100; } - if (joy_debug->int_val) { + if (joy_debug) { if (ji.dwXpos) Sys_Printf("X: %ld\n",ji.dwXpos); if (ji.dwYpos) Sys_Printf("Y: %ld\n",ji.dwYpos); if (ji.dwZpos) Sys_Printf("Z: %ld\n",ji.dwZpos); @@ -306,7 +474,7 @@ JOY_AdvancedUpdate_f (void) pdwRawValue[i] = RawValuePointer (i); } - if (joy_advanced->int_val) { + if (joy_advanced) { // default joystick initialization // only 2 axes with joystick control dwAxisMap[JOY_AXIS_X] = AxisTurn; @@ -314,28 +482,28 @@ JOY_AdvancedUpdate_f (void) dwAxisMap[JOY_AXIS_Y] = AxisForward; // dwControlMap[JOY_AXIS_Y] = JOY_ABSOLUTE_AXIS; } else { - if (strcmp (joy_name->string, "joystick") != 0) { + if (strcmp (joy_name, "joystick") != 0) { // notify user of advanced controller - Sys_Printf ("\n%s configured\n\n", joy_name->string); + Sys_Printf ("\n%s configured\n\n", joy_name); } // advanced initialization here // data supplied by user via joy_axisn cvars - dwTemp = joy_advaxisx->int_val; + dwTemp = joy_advaxisx; dwAxisMap[JOY_AXIS_X] = dwTemp & 0x0000000f; dwControlMap[JOY_AXIS_X] = dwTemp & JOY_RELATIVE_AXIS; - dwTemp = joy_advaxisy->int_val; + dwTemp = joy_advaxisy; dwAxisMap[JOY_AXIS_Y] = dwTemp & 0x0000000f; dwControlMap[JOY_AXIS_Y] = dwTemp & JOY_RELATIVE_AXIS; - dwTemp = joy_advaxisz->int_val; + dwTemp = joy_advaxisz; dwAxisMap[JOY_AXIS_Z] = dwTemp & 0x0000000f; dwControlMap[JOY_AXIS_Z] = dwTemp & JOY_RELATIVE_AXIS; - dwTemp = joy_advaxisr->int_val; + dwTemp = joy_advaxisr; dwAxisMap[JOY_AXIS_R] = dwTemp & 0x0000000f; dwControlMap[JOY_AXIS_R] = dwTemp & JOY_RELATIVE_AXIS; - dwTemp = joy_advaxisu->int_val; + dwTemp = joy_advaxisu; dwAxisMap[JOY_AXIS_U] = dwTemp & 0x0000000f; dwControlMap[JOY_AXIS_U] = dwTemp & JOY_RELATIVE_AXIS; - dwTemp = joy_advaxisv->int_val; + dwTemp = joy_advaxisv; dwAxisMap[JOY_AXIS_V] = dwTemp & 0x0000000f; dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS; } @@ -353,53 +521,30 @@ void JOY_Init_Cvars(void) { // joystick variables - joy_device = Cvar_Get ("joy_device", "none", CVAR_NONE | CVAR_ROM, 0, - "Joystick device"); - joy_enable = Cvar_Get ("joy_enable", "1", CVAR_NONE | CVAR_ARCHIVE, 0, - "Joystick enable flag"); - joy_sensitivity = Cvar_Get ("joy_sensitivity", "1", CVAR_NONE | - CVAR_ARCHIVE, 0, "Joystick sensitivity"); - in_joystick = Cvar_Get ("joystick", "0", CVAR_ARCHIVE, 0, "FIXME: No " - "Description"); - joy_name = Cvar_Get ("joyname", "joystick", CVAR_NONE, 0, "FIXME: No " - "Description"); - joy_advanced = Cvar_Get ("joyadvanced", "0", CVAR_NONE, 0, "FIXME: No " - "Description"); - joy_advaxisx = Cvar_Get ("joyadvaxisx", "0", CVAR_NONE, 0, "FIXME: No " - "Description"); - joy_advaxisy = Cvar_Get ("joyadvaxisy", "0", CVAR_NONE, 0, "FIXME: No " - "Description"); - joy_advaxisz = Cvar_Get ("joyadvaxisz", "0", CVAR_NONE, 0, "FIXME: No " - "Description"); - joy_advaxisr = Cvar_Get ("joyadvaxisr", "0", CVAR_NONE, 0, "FIXME: No " - "Description"); - joy_advaxisu = Cvar_Get ("joyadvaxisu", "0", CVAR_NONE, 0, "FIXME: No " - "Description"); - joy_advaxisv = Cvar_Get ("joyadvaxisv", "0", CVAR_NONE, 0, "FIXME: No " - "Description"); - joy_forwardthreshold = Cvar_Get ("joyforwardthreshold", "0.15", CVAR_NONE, - 0, "FIXME: No Description"); - joy_sidethreshold = Cvar_Get ("joysidethreshold", "0.15", CVAR_NONE, 0, - "FIXME: No Description"); - joy_pitchthreshold = Cvar_Get ("joypitchthreshold", "0.15", CVAR_NONE, 0, - "FIXME: No Description"); - joy_yawthreshold = Cvar_Get ("joyyawthreshold", "0.15", CVAR_NONE, 0, - "FIXME: No Description"); - joy_forwardsensitivity = Cvar_Get ("joyforwardsensitivity", "-1.0", - CVAR_NONE, 0, "FIXME: No Description"); - joy_sidesensitivity = Cvar_Get ("joysidesensitivity", "-1.0", CVAR_NONE, - 0, "FIXME: No Description"); - joy_pitchsensitivity = Cvar_Get ("joypitchsensitivity", "1.0", CVAR_NONE, - 0, "FIXME: No Description"); - joy_yawsensitivity = Cvar_Get ("joyyawsensitivity", "-1.0", CVAR_NONE, 0, - "FIXME: No Description"); - joy_wwhack1 = Cvar_Get ("joywwhack1", "0.0", CVAR_NONE, 0, "FIXME: No " - "Description"); - joy_wwhack2 = Cvar_Get ("joywwhack2", "0.0", CVAR_NONE, 0, "FIXME: No " - "Description"); + Cvar_Register (&joy_device_cvar, 0, 0); + Cvar_Register (&joy_enable_cvar, 0, 0); + Cvar_Register (&joy_sensitivity_cvar, 0, 0); + Cvar_Register (&in_joystick_cvar, 0, 0); + Cvar_Register (&joy_name_cvar, 0, 0); + Cvar_Register (&joy_advanced_cvar, 0, 0); + Cvar_Register (&joy_advaxisx_cvar, 0, 0); + Cvar_Register (&joy_advaxisy_cvar, 0, 0); + Cvar_Register (&joy_advaxisz_cvar, 0, 0); + Cvar_Register (&joy_advaxisr_cvar, 0, 0); + Cvar_Register (&joy_advaxisu_cvar, 0, 0); + Cvar_Register (&joy_advaxisv_cvar, 0, 0); + Cvar_Register (&joy_forwardthreshold_cvar, 0, 0); + Cvar_Register (&joy_sidethreshold_cvar, 0, 0); + Cvar_Register (&joy_pitchthreshold_cvar, 0, 0); + Cvar_Register (&joy_yawthreshold_cvar, 0, 0); + Cvar_Register (&joy_forwardsensitivity_cvar, 0, 0); + Cvar_Register (&joy_sidesensitivity_cvar, 0, 0); + Cvar_Register (&joy_pitchsensitivity_cvar, 0, 0); + Cvar_Register (&joy_yawsensitivity_cvar, 0, 0); + Cvar_Register (&joy_wwhack1_cvar, 0, 0); + Cvar_Register (&joy_wwhack2_cvar, 0, 0); - joy_debug = Cvar_Get ("joy_debug", "0.0", CVAR_NONE, 0, "FIXME: No " - "Description"); + Cvar_Register (&joy_debug_cvar, 0, 0); return; } #endif diff --git a/libs/video/targets/keys.c b/libs/video/targets/keys.c deleted file mode 100644 index ecf3aa4ce..000000000 --- a/libs/video/targets/keys.c +++ /dev/null @@ -1,1339 +0,0 @@ -/* - keys.c - - (description) - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif - -#include -#include -#include - -#include "QF/cbuf.h" -#include "QF/cmd.h" -#include "QF/console.h" -#include "QF/csqc.h" -#include "QF/cvar.h" -#include "QF/keys.h" -#include "QF/screen.h" -#include "QF/sys.h" -#include "QF/zone.h" -#include "QF/gib.h" - -#include "compat.h" -#include "old_keys.h" - -#define U __attribute__ ((used)) -static U void (*const key_progs_init)(struct progs_s *) = Key_Progs_Init; -#undef U - -/* key up events are sent even if in console mode */ - -static keydest_t key_dest = key_console; -static keytarget_t key_targets[key_last]; -VISIBLE knum_t key_togglemenu = QFK_ESCAPE; -VISIBLE knum_t key_toggleconsole = QFK_BACKQUOTE; - -#define KEYDEST_CALLBACK_CHUNK 16 -static keydest_callback_t **keydest_callbacks; -static int num_keydest_callbacks; -static int max_keydest_callbacks; - -VISIBLE int keydown[QFK_LAST]; - -static int keyhelp; -static cbuf_t *cbuf; - -static const char *keydest_names[] = { - "key_unfocused", - "key_game", - "key_demo", - "key_console", - "key_message", - "key_menu", - - "key_last" -}; - - -typedef struct { - const char *name; - imt_t imtnum; -} imtname_t; - -typedef struct { - const char *name; - knum_t keynum; -} keyname_t; - -keyname_t keynames[] = { - { "K_UNKNOWN", QFK_UNKNOWN }, - { "K_FIRST", QFK_FIRST }, - { "K_BACKSPACE", QFK_BACKSPACE }, - { "K_TAB", QFK_TAB }, - { "K_CLEAR", QFK_CLEAR }, - { "K_RETURN", QFK_RETURN }, - { "K_PAUSE", QFK_PAUSE }, - { "K_ESCAPE", QFK_ESCAPE }, - { "K_SPACE", QFK_SPACE }, - { "K_EXCLAIM", QFK_EXCLAIM }, - { "K_QUOTEDBL", QFK_QUOTEDBL }, - { "K_HASH", QFK_HASH }, - { "K_DOLLAR", QFK_DOLLAR }, - { "K_PERCENT", QFK_PERCENT }, - { "K_AMPERSAND", QFK_AMPERSAND }, - { "K_QUOTE", QFK_QUOTE }, - { "K_LEFTPAREN", QFK_LEFTPAREN }, - { "K_RIGHTPAREN", QFK_RIGHTPAREN }, - { "K_ASTERISK", QFK_ASTERISK }, - { "K_PLUS", QFK_PLUS }, - { "K_COMMA", QFK_COMMA }, - { "K_MINUS", QFK_MINUS }, - { "K_PERIOD", QFK_PERIOD }, - { "K_SLASH", QFK_SLASH }, - { "K_0", QFK_0 }, - { "K_1", QFK_1 }, - { "K_2", QFK_2 }, - { "K_3", QFK_3 }, - { "K_4", QFK_4 }, - { "K_5", QFK_5 }, - { "K_6", QFK_6 }, - { "K_7", QFK_7 }, - { "K_8", QFK_8 }, - { "K_9", QFK_9 }, - { "K_COLON", QFK_COLON }, - { "K_SEMICOLON", QFK_SEMICOLON }, - { "K_LESS", QFK_LESS }, - { "K_EQUALS", QFK_EQUALS }, - { "K_GREATER", QFK_GREATER }, - { "K_QUESTION", QFK_QUESTION }, - { "K_AT", QFK_AT }, - { "K_LEFTBRACKET", QFK_LEFTBRACKET }, - { "K_BACKSLASH", QFK_BACKSLASH }, - { "K_RIGHTBRACKET", QFK_RIGHTBRACKET }, - { "K_CARET", QFK_CARET }, - { "K_UNDERSCORE", QFK_UNDERSCORE }, - { "K_BACKQUOTE", QFK_BACKQUOTE }, - { "K_a", QFK_a }, - { "K_b", QFK_b }, - { "K_c", QFK_c }, - { "K_d", QFK_d }, - { "K_e", QFK_e }, - { "K_f", QFK_f }, - { "K_g", QFK_g }, - { "K_h", QFK_h }, - { "K_i", QFK_i }, - { "K_j", QFK_j }, - { "K_k", QFK_k }, - { "K_l", QFK_l }, - { "K_m", QFK_m }, - { "K_n", QFK_n }, - { "K_o", QFK_o }, - { "K_p", QFK_p }, - { "K_q", QFK_q }, - { "K_r", QFK_r }, - { "K_s", QFK_s }, - { "K_t", QFK_t }, - { "K_u", QFK_u }, - { "K_v", QFK_v }, - { "K_w", QFK_w }, - { "K_x", QFK_x }, - { "K_y", QFK_y }, - { "K_z", QFK_z }, - { "K_BRACELEFT", QFK_BRACELEFT }, - { "K_BAR", QFK_BAR }, - { "K_BRACERIGHT", QFK_BRACERIGHT }, - { "K_ASCIITILDE", QFK_ASCIITILDE }, - { "K_DELETE", QFK_DELETE }, - { "K_WORLD_0", QFK_WORLD_0 }, - { "K_WORLD_1", QFK_WORLD_1 }, - { "K_WORLD_2", QFK_WORLD_2 }, - { "K_WORLD_3", QFK_WORLD_3 }, - { "K_WORLD_4", QFK_WORLD_4 }, - { "K_WORLD_5", QFK_WORLD_5 }, - { "K_WORLD_6", QFK_WORLD_6 }, - { "K_WORLD_7", QFK_WORLD_7 }, - { "K_WORLD_8", QFK_WORLD_8 }, - { "K_WORLD_9", QFK_WORLD_9 }, - { "K_WORLD_10", QFK_WORLD_10 }, - { "K_WORLD_11", QFK_WORLD_11 }, - { "K_WORLD_12", QFK_WORLD_12 }, - { "K_WORLD_13", QFK_WORLD_13 }, - { "K_WORLD_14", QFK_WORLD_14 }, - { "K_WORLD_15", QFK_WORLD_15 }, - { "K_WORLD_16", QFK_WORLD_16 }, - { "K_WORLD_17", QFK_WORLD_17 }, - { "K_WORLD_18", QFK_WORLD_18 }, - { "K_WORLD_19", QFK_WORLD_19 }, - { "K_WORLD_20", QFK_WORLD_20 }, - { "K_WORLD_21", QFK_WORLD_21 }, - { "K_WORLD_22", QFK_WORLD_22 }, - { "K_WORLD_23", QFK_WORLD_23 }, - { "K_WORLD_24", QFK_WORLD_24 }, - { "K_WORLD_25", QFK_WORLD_25 }, - { "K_WORLD_26", QFK_WORLD_26 }, - { "K_WORLD_27", QFK_WORLD_27 }, - { "K_WORLD_28", QFK_WORLD_28 }, - { "K_WORLD_29", QFK_WORLD_29 }, - { "K_WORLD_30", QFK_WORLD_30 }, - { "K_WORLD_31", QFK_WORLD_31 }, - { "K_WORLD_32", QFK_WORLD_32 }, - { "K_WORLD_33", QFK_WORLD_33 }, - { "K_WORLD_34", QFK_WORLD_34 }, - { "K_WORLD_35", QFK_WORLD_35 }, - { "K_WORLD_36", QFK_WORLD_36 }, - { "K_WORLD_37", QFK_WORLD_37 }, - { "K_WORLD_38", QFK_WORLD_38 }, - { "K_WORLD_39", QFK_WORLD_39 }, - { "K_WORLD_40", QFK_WORLD_40 }, - { "K_WORLD_41", QFK_WORLD_41 }, - { "K_WORLD_42", QFK_WORLD_42 }, - { "K_WORLD_43", QFK_WORLD_43 }, - { "K_WORLD_44", QFK_WORLD_44 }, - { "K_WORLD_45", QFK_WORLD_45 }, - { "K_WORLD_46", QFK_WORLD_46 }, - { "K_WORLD_47", QFK_WORLD_47 }, - { "K_WORLD_48", QFK_WORLD_48 }, - { "K_WORLD_49", QFK_WORLD_49 }, - { "K_WORLD_50", QFK_WORLD_50 }, - { "K_WORLD_51", QFK_WORLD_51 }, - { "K_WORLD_52", QFK_WORLD_52 }, - { "K_WORLD_53", QFK_WORLD_53 }, - { "K_WORLD_54", QFK_WORLD_54 }, - { "K_WORLD_55", QFK_WORLD_55 }, - { "K_WORLD_56", QFK_WORLD_56 }, - { "K_WORLD_57", QFK_WORLD_57 }, - { "K_WORLD_58", QFK_WORLD_58 }, - { "K_WORLD_59", QFK_WORLD_59 }, - { "K_WORLD_60", QFK_WORLD_60 }, - { "K_WORLD_61", QFK_WORLD_61 }, - { "K_WORLD_62", QFK_WORLD_62 }, - { "K_WORLD_63", QFK_WORLD_63 }, - { "K_WORLD_64", QFK_WORLD_64 }, - { "K_WORLD_65", QFK_WORLD_65 }, - { "K_WORLD_66", QFK_WORLD_66 }, - { "K_WORLD_67", QFK_WORLD_67 }, - { "K_WORLD_68", QFK_WORLD_68 }, - { "K_WORLD_69", QFK_WORLD_69 }, - { "K_WORLD_70", QFK_WORLD_70 }, - { "K_WORLD_71", QFK_WORLD_71 }, - { "K_WORLD_72", QFK_WORLD_72 }, - { "K_WORLD_73", QFK_WORLD_73 }, - { "K_WORLD_74", QFK_WORLD_74 }, - { "K_WORLD_75", QFK_WORLD_75 }, - { "K_WORLD_76", QFK_WORLD_76 }, - { "K_WORLD_77", QFK_WORLD_77 }, - { "K_WORLD_78", QFK_WORLD_78 }, - { "K_WORLD_79", QFK_WORLD_79 }, - { "K_WORLD_80", QFK_WORLD_80 }, - { "K_WORLD_81", QFK_WORLD_81 }, - { "K_WORLD_82", QFK_WORLD_82 }, - { "K_WORLD_83", QFK_WORLD_83 }, - { "K_WORLD_84", QFK_WORLD_84 }, - { "K_WORLD_85", QFK_WORLD_85 }, - { "K_WORLD_86", QFK_WORLD_86 }, - { "K_WORLD_87", QFK_WORLD_87 }, - { "K_WORLD_88", QFK_WORLD_88 }, - { "K_WORLD_89", QFK_WORLD_89 }, - { "K_WORLD_90", QFK_WORLD_90 }, - { "K_WORLD_91", QFK_WORLD_91 }, - { "K_WORLD_92", QFK_WORLD_92 }, - { "K_WORLD_93", QFK_WORLD_93 }, - { "K_WORLD_94", QFK_WORLD_94 }, - { "K_WORLD_95", QFK_WORLD_95 }, - { "K_KP0", QFK_KP0 }, - { "K_KP1", QFK_KP1 }, - { "K_KP2", QFK_KP2 }, - { "K_KP3", QFK_KP3 }, - { "K_KP4", QFK_KP4 }, - { "K_KP5", QFK_KP5 }, - { "K_KP6", QFK_KP6 }, - { "K_KP7", QFK_KP7 }, - { "K_KP8", QFK_KP8 }, - { "K_KP9", QFK_KP9 }, - { "K_KP_PERIOD", QFK_KP_PERIOD }, - { "K_KP_DIVIDE", QFK_KP_DIVIDE }, - { "K_KP_MULTIPLY", QFK_KP_MULTIPLY }, - { "K_KP_MINUS", QFK_KP_MINUS }, - { "K_KP_PLUS", QFK_KP_PLUS }, - { "K_KP_ENTER", QFK_KP_ENTER }, - { "K_KP_EQUALS", QFK_KP_EQUALS }, - { "K_UP", QFK_UP }, - { "K_DOWN", QFK_DOWN }, - { "K_RIGHT", QFK_RIGHT }, - { "K_LEFT", QFK_LEFT }, - { "K_INSERT", QFK_INSERT }, - { "K_HOME", QFK_HOME }, - { "K_END", QFK_END }, - { "K_PAGEUP", QFK_PAGEUP }, - { "K_PAGEDOWN", QFK_PAGEDOWN }, - { "K_F1", QFK_F1 }, - { "K_F2", QFK_F2 }, - { "K_F3", QFK_F3 }, - { "K_F4", QFK_F4 }, - { "K_F5", QFK_F5 }, - { "K_F6", QFK_F6 }, - { "K_F7", QFK_F7 }, - { "K_F8", QFK_F8 }, - { "K_F9", QFK_F9 }, - { "K_F10", QFK_F10 }, - { "K_F11", QFK_F11 }, - { "K_F12", QFK_F12 }, - { "K_F13", QFK_F13 }, - { "K_F14", QFK_F14 }, - { "K_F15", QFK_F15 }, - { "K_NUMLOCK", QFK_NUMLOCK }, - { "K_CAPSLOCK", QFK_CAPSLOCK }, - { "K_SCROLLOCK", QFK_SCROLLOCK }, - { "K_RSHIFT", QFK_RSHIFT }, - { "K_LSHIFT", QFK_LSHIFT }, - { "K_RCTRL", QFK_RCTRL }, - { "K_LCTRL", QFK_LCTRL }, - { "K_RALT", QFK_RALT }, - { "K_LALT", QFK_LALT }, - { "K_RMETA", QFK_RMETA }, - { "K_LMETA", QFK_LMETA }, - { "K_LSUPER", QFK_LSUPER }, - { "K_RSUPER", QFK_RSUPER }, - { "K_MODE", QFK_MODE }, - { "K_COMPOSE", QFK_COMPOSE }, - { "K_HELP", QFK_HELP }, - { "K_PRINT", QFK_PRINT }, - { "K_SYSREQ", QFK_SYSREQ }, - { "K_BREAK", QFK_BREAK }, - { "K_MENU", QFK_MENU }, - { "K_POWER", QFK_POWER }, - { "K_EURO", QFK_EURO }, - - { "K_KANJI", QFK_KANJI }, - { "K_MUHENKAN", QFK_MUHENKAN }, - { "K_HENKAN", QFK_HENKAN }, - { "K_ROMAJI", QFK_ROMAJI }, - { "K_HIRAGANA", QFK_HIRAGANA }, - { "K_KATAKANA", QFK_KATAKANA }, - { "K_HIRAGANA_KATAKANA", QFK_HIRAGANA_KATAKANA }, - { "K_ZENKAKU", QFK_ZENKAKU }, - { "K_HANKAKU", QFK_HANKAKU }, - { "K_ZENKAKU_HANKAKU", QFK_ZENKAKU_HANKAKU }, - { "K_TOUROKU", QFK_TOUROKU }, - { "K_MASSYO", QFK_MASSYO }, - { "K_KANA_LOCK", QFK_KANA_LOCK }, - { "K_KANA_SHIFT", QFK_KANA_SHIFT }, - { "K_EISU_SHIFT", QFK_EISU_SHIFT }, - { "K_EISU_TOGGLE", QFK_EISU_TOGGLE }, - { "K_KANJI_BANGOU", QFK_KANJI_BANGOU }, - { "K_ZEN_KOHO", QFK_ZEN_KOHO }, - { "K_MAE_KOHO", QFK_MAE_KOHO }, - - { "K_HOMEPAGE", QFK_HOMEPAGE }, - { "K_SEARCH", QFK_SEARCH }, - { "K_MAIL", QFK_MAIL }, - { "K_FAVORITES", QFK_FAVORITES }, - { "K_AUDIOMUTE", QFK_AUDIOMUTE }, - { "K_AUDIOLOWERVOLUME", QFK_AUDIOLOWERVOLUME }, - { "K_AUDIORAISEVOLUME", QFK_AUDIORAISEVOLUME }, - { "K_AUDIOPLAY", QFK_AUDIOPLAY }, - { "K_CALCULATOR", QFK_CALCULATOR }, - { "K_UNDO", QFK_UNDO }, - { "K_REDO", QFK_REDO }, - { "K_NEW", QFK_NEW }, - { "K_RELOAD", QFK_RELOAD }, - { "K_OPEN", QFK_OPEN }, - { "K_CLOSE", QFK_CLOSE }, - { "K_REPLY", QFK_REPLY }, - { "K_MAILFORWARD", QFK_MAILFORWARD }, - { "K_SEND", QFK_SEND }, - { "K_SAVE", QFK_SAVE }, - { "K_BACK", QFK_BACK }, - { "K_FORWARD", QFK_FORWARD }, - - { "M_BUTTON1", QFM_BUTTON1 }, - { "M_BUTTON2", QFM_BUTTON2 }, - { "M_BUTTON3", QFM_BUTTON3 }, - { "M_WHEEL_UP", QFM_WHEEL_UP }, - { "M_WHEEL_DOWN", QFM_WHEEL_DOWN }, - { "M_BUTTON6", QFM_BUTTON6 }, - { "M_BUTTON7", QFM_BUTTON7 }, - { "M_BUTTON8", QFM_BUTTON8 }, - { "M_BUTTON9", QFM_BUTTON9 }, - { "M_BUTTON10", QFM_BUTTON10 }, - { "M_BUTTON11", QFM_BUTTON11 }, - { "M_BUTTON12", QFM_BUTTON12 }, - { "M_BUTTON13", QFM_BUTTON13 }, - { "M_BUTTON14", QFM_BUTTON14 }, - { "M_BUTTON15", QFM_BUTTON15 }, - { "M_BUTTON16", QFM_BUTTON16 }, - { "M_BUTTON17", QFM_BUTTON17 }, - { "M_BUTTON18", QFM_BUTTON18 }, - { "M_BUTTON19", QFM_BUTTON19 }, - { "M_BUTTON20", QFM_BUTTON20 }, - { "M_BUTTON21", QFM_BUTTON21 }, - { "M_BUTTON22", QFM_BUTTON22 }, - { "M_BUTTON23", QFM_BUTTON23 }, - { "M_BUTTON24", QFM_BUTTON24 }, - { "M_BUTTON25", QFM_BUTTON25 }, - { "M_BUTTON26", QFM_BUTTON26 }, - { "M_BUTTON27", QFM_BUTTON27 }, - { "M_BUTTON28", QFM_BUTTON28 }, - { "M_BUTTON29", QFM_BUTTON29 }, - { "M_BUTTON30", QFM_BUTTON30 }, - { "M_BUTTON31", QFM_BUTTON31 }, - { "M_BUTTON32", QFM_BUTTON32 }, - - { "J_BUTTON1", QFJ_BUTTON1 }, - { "J_BUTTON2", QFJ_BUTTON2 }, - { "J_BUTTON3", QFJ_BUTTON3 }, - { "J_BUTTON4", QFJ_BUTTON4 }, - { "J_BUTTON5", QFJ_BUTTON5 }, - { "J_BUTTON6", QFJ_BUTTON6 }, - { "J_BUTTON7", QFJ_BUTTON7 }, - { "J_BUTTON8", QFJ_BUTTON8 }, - { "J_BUTTON9", QFJ_BUTTON9 }, - { "J_BUTTON10", QFJ_BUTTON10 }, - { "J_BUTTON11", QFJ_BUTTON11 }, - { "J_BUTTON12", QFJ_BUTTON12 }, - { "J_BUTTON13", QFJ_BUTTON13 }, - { "J_BUTTON14", QFJ_BUTTON14 }, - { "J_BUTTON15", QFJ_BUTTON15 }, - { "J_BUTTON16", QFJ_BUTTON16 }, - { "J_BUTTON17", QFJ_BUTTON17 }, - { "J_BUTTON18", QFJ_BUTTON18 }, - { "J_BUTTON19", QFJ_BUTTON19 }, - { "J_BUTTON20", QFJ_BUTTON20 }, - { "J_BUTTON21", QFJ_BUTTON21 }, - { "J_BUTTON22", QFJ_BUTTON22 }, - { "J_BUTTON23", QFJ_BUTTON23 }, - { "J_BUTTON24", QFJ_BUTTON24 }, - { "J_BUTTON25", QFJ_BUTTON25 }, - { "J_BUTTON26", QFJ_BUTTON26 }, - { "J_BUTTON27", QFJ_BUTTON27 }, - { "J_BUTTON28", QFJ_BUTTON28 }, - { "J_BUTTON29", QFJ_BUTTON29 }, - { "J_BUTTON30", QFJ_BUTTON30 }, - { "J_BUTTON31", QFJ_BUTTON31 }, - { "J_BUTTON32", QFJ_BUTTON32 }, - { "J_BUTTON33", QFJ_BUTTON33 }, - { "J_BUTTON34", QFJ_BUTTON34 }, - { "J_BUTTON35", QFJ_BUTTON35 }, - { "J_BUTTON36", QFJ_BUTTON36 }, - { "J_BUTTON37", QFJ_BUTTON37 }, - { "J_BUTTON38", QFJ_BUTTON38 }, - { "J_BUTTON39", QFJ_BUTTON39 }, - { "J_BUTTON40", QFJ_BUTTON40 }, - { "J_BUTTON41", QFJ_BUTTON41 }, - { "J_BUTTON42", QFJ_BUTTON42 }, - { "J_BUTTON43", QFJ_BUTTON43 }, - { "J_BUTTON44", QFJ_BUTTON44 }, - { "J_BUTTON45", QFJ_BUTTON45 }, - { "J_BUTTON46", QFJ_BUTTON46 }, - { "J_BUTTON47", QFJ_BUTTON47 }, - { "J_BUTTON48", QFJ_BUTTON48 }, - { "J_BUTTON49", QFJ_BUTTON49 }, - { "J_BUTTON50", QFJ_BUTTON50 }, - { "J_BUTTON51", QFJ_BUTTON51 }, - { "J_BUTTON52", QFJ_BUTTON52 }, - { "J_BUTTON53", QFJ_BUTTON53 }, - { "J_BUTTON54", QFJ_BUTTON54 }, - { "J_BUTTON55", QFJ_BUTTON55 }, - { "J_BUTTON56", QFJ_BUTTON56 }, - { "J_BUTTON57", QFJ_BUTTON57 }, - { "J_BUTTON58", QFJ_BUTTON58 }, - { "J_BUTTON59", QFJ_BUTTON59 }, - { "J_BUTTON60", QFJ_BUTTON60 }, - { "J_BUTTON61", QFJ_BUTTON61 }, - { "J_BUTTON62", QFJ_BUTTON62 }, - { "J_BUTTON63", QFJ_BUTTON63 }, - { "J_BUTTON64", QFJ_BUTTON64 }, - - { "J_AXIS1", QFJ_AXIS1 }, - { "J_AXIS2", QFJ_AXIS2 }, - { "J_AXIS3", QFJ_AXIS3 }, - { "J_AXIS4", QFJ_AXIS4 }, - { "J_AXIS5", QFJ_AXIS5 }, - { "J_AXIS6", QFJ_AXIS6 }, - { "J_AXIS7", QFJ_AXIS7 }, - { "J_AXIS8", QFJ_AXIS8 }, - { "J_AXIS9", QFJ_AXIS9 }, - { "J_AXIS10", QFJ_AXIS10 }, - { "J_AXIS11", QFJ_AXIS11 }, - { "J_AXIS12", QFJ_AXIS12 }, - { "J_AXIS13", QFJ_AXIS13 }, - { "J_AXIS14", QFJ_AXIS14 }, - { "J_AXIS15", QFJ_AXIS15 }, - { "J_AXIS16", QFJ_AXIS16 }, - { "J_AXIS17", QFJ_AXIS17 }, - { "J_AXIS18", QFJ_AXIS18 }, - { "J_AXIS19", QFJ_AXIS19 }, - { "J_AXIS20", QFJ_AXIS20 }, - { "J_AXIS21", QFJ_AXIS21 }, - { "J_AXIS22", QFJ_AXIS22 }, - { "J_AXIS23", QFJ_AXIS23 }, - { "J_AXIS24", QFJ_AXIS24 }, - { "J_AXIS25", QFJ_AXIS25 }, - { "J_AXIS26", QFJ_AXIS26 }, - { "J_AXIS27", QFJ_AXIS27 }, - { "J_AXIS28", QFJ_AXIS28 }, - { "J_AXIS29", QFJ_AXIS29 }, - { "J_AXIS30", QFJ_AXIS30 }, - { "J_AXIS31", QFJ_AXIS31 }, - { "J_AXIS32", QFJ_AXIS32 }, - - {NULL, 0} -}; - -static imt_t * -key_target_find_imt (keytarget_t *kt, const char *imt_name) -{ - imt_t *imt; - for (imt = kt->imts; imt; imt = imt->next) { - if (!strcasecmp (imt->name, imt_name)) { - return imt; - } - } - return 0; -} - -VISIBLE imt_t * -Key_FindIMT (const char *imt_name) -{ - keydest_t kd; - imt_t *imt = 0; - - for (kd = key_unfocused; !imt && kd < key_last; kd++) { - imt = key_target_find_imt (&key_targets[kd], imt_name); - } - return imt; -} - -void -Key_CreateIMT (keydest_t kd, const char *imt_name, const char *chain_imt_name) -{ - imt_t *imt; - imt_t *chain_imt = 0; - keytarget_t *kt = &key_targets[kd]; - - imt = Key_FindIMT (imt_name); - if (imt) { - Sys_Printf ("imt error: imt %s already exists\n", imt_name); - return; - } - if (chain_imt_name) { - chain_imt = Key_FindIMT (chain_imt_name); - if (!chain_imt) { - Sys_Printf ("imt error: chain imt %s does not exist\n", - chain_imt_name); - return; - } - imt = key_target_find_imt (kt, chain_imt_name); - if (!imt) { - Sys_Printf ("imt error: chain imt %s not on target key " - "destination\n", chain_imt_name); - return; - } - } - imt = calloc (1, sizeof (imt_t)); - imt->name = strdup (imt_name); - imt->chain = chain_imt; - imt->next = kt->imts; - kt->imts = imt; - if (!kt->active) { - kt->active = imt; - } -} - -VISIBLE void -Key_SetBinding (imt_t *imt, knum_t keynum, const char *binding) -{ - if (keynum == (knum_t) -1) - return; - - if (imt->bindings[keynum].str) { - free (imt->bindings[keynum].str); - imt->bindings[keynum].str = NULL; - } - if (binding) { - imt->bindings[keynum].str = strdup(binding); - } -} - -static void -Key_CallDestCallbacks (keydest_t kd) -{ - int i; - - for (i = 0; i < num_keydest_callbacks; i++) - keydest_callbacks[i] (kd); -} - -static void -process_binding (knum_t key, const char *kb) -{ - char cmd[1024]; - - if (kb[0] == '+') { - if (keydown[key]) - snprintf (cmd, sizeof (cmd), "%s %d\n", kb, key); - else - snprintf (cmd, sizeof (cmd), "-%s %d\n", kb + 1, key); - } else { - if (!keydown[key]) - return; - snprintf (cmd, sizeof (cmd), "%s\n", kb); - } - Cbuf_AddText (cbuf, cmd); -} - -/* - Key_Game - - Game key handling. -*/ -static qboolean -Key_Game (knum_t key, short unicode) -{ - const char *kb; - imt_t *imt = key_targets[key_dest].active; - - while (imt) { - kb = imt->bindings[key].str; - if (kb) { - if (keydown[key] <= 1) - process_binding (key, kb); - return true; - } - imt = imt->chain; - } - return false; -} - -/* - Key_Console - - Interactive line editing and console scrollback -*/ -static void -Key_Console (knum_t key, short unicode) -{ - // escape is un-bindable - if (keydown[key] == 1 && key && Key_Game (key, unicode)) - return; - - Con_KeyEvent (key, unicode, keydown[key]); -} - -VISIBLE int -Key_StringToKeynum (const char *str) -{ - keyname_t *kn; - - if (!str || !str[0]) - return -1; - - for (kn = keynames; kn->name; kn++) { - if (!strcasecmp (str, kn->name)) - return kn->keynum; - } - return -1; -} - -VISIBLE const char * -Key_KeynumToString (knum_t keynum) -{ - keyname_t *kn; - - if (keynum == (knum_t) -1) - return ""; - - for (kn = keynames; kn->name; kn++) - if (keynum == kn->keynum) - return kn->name; - - return ""; -} - -static void -Key_In_Unbind (const char *imt_name, const char *key_name) -{ - imt_t *imt; - int key; - - imt = Key_FindIMT (imt_name); - if (!imt) { - Sys_Printf ("\"%s\" is not a valid imt\n", imt_name); - return; - } - - key = Key_StringToKeynum (key_name); - if (key == -1) { - Sys_Printf ("\"%s\" is not a valid key\n", key_name); - return; - } - - Key_SetBinding (imt, key, NULL); -} - -static void -Key_In_Unbind_f (void) -{ - if (Cmd_Argc () != 3) { - Sys_Printf ("in_unbind : remove commands from a key\n"); - return; - } - Key_In_Unbind (Cmd_Argv (1), Cmd_Argv (2)); -} - -static void -Key_Unbindall_f (void) -{ - keydest_t kd; - imt_t *imt; - int i; - - for (kd = key_unfocused; kd < key_last; kd++) { - for (imt = key_targets[kd].imts; imt; imt = imt->next) { - for (i = 0; i < QFK_LAST; i++) { - Key_SetBinding (imt, i, 0); - } - } - } -} - -static void -Key_In_Type_f (void) -{ - const char *str, *p; - if (Cmd_Argc () != 2) { - Sys_Printf ("in_type \n"); - Sys_Printf (" Send the given string as simulated key presses.\n"); - return; - } - str = Cmd_Argv (1); - for (p = str; *p; p++) { - Key_Event (QFK_UNKNOWN, *p, 1); - Key_Event (QFK_UNKNOWN, 0, 0); - } -} - -static void -Key_In_Clear (void) -{ - int err = 0; - imt_t *imt; - int i, j; - - if (Cmd_Argc () == 1) { - Sys_Printf ("in_clear ...\n"); - return; - } - for (i = 1; i < Cmd_Argc (); i++) { - if (!Key_FindIMT (Cmd_Argv (i))) { - Sys_Printf ("\"%s\" is not a valid imt\n", Cmd_Argv (i)); - err = 1; - } - } - if (err) - return; - for (i = 1; i < Cmd_Argc (); i++) { - imt = Key_FindIMT (Cmd_Argv (i)); - for (j = 0; j < QFK_LAST; j++) - Key_SetBinding (imt, j, NULL); - } -} - -static void -Key_IMT_Create_f (void) -{ - const char *keydest; - const char *imt_name; - const char *chain_imt_name = 0; - keydest_t kd; - - if (Cmd_Argc () < 3 || Cmd_Argc () > 4) { - Sys_Printf ("see help imt_create\n"); - return; - } - keydest = Cmd_Argv (1); - imt_name = Cmd_Argv (2); - if (Cmd_Argc () == 4) { - chain_imt_name = Cmd_Argv (3); - } - for (kd = key_game; kd < key_last; kd++) { - if (!strcasecmp (keydest_names[kd], keydest)) { - break; - } - } - if (kd == key_last) { - Sys_Printf ("imt error: invalid keydest: %s\n", keydest); - return; - } - Key_CreateIMT (kd, imt_name, chain_imt_name); -} - -static void -Key_IMT_Drop_All_f (void) -{ - keydest_t kd; - imt_t *imt; - - for (kd = key_unfocused; kd < key_last; kd++) { - while (key_targets[kd].imts) { - imt = key_targets[kd].imts; - key_targets[kd].imts = imt->next; - free ((char *) imt->name); - free (imt); - } - key_targets[kd].active = 0; - } -} - -static void -Key_In_Bind (const char *imt_name, const char *key_name, const char *cmd) -{ - imt_t *imt; - int key; - - imt = Key_FindIMT (imt_name); - if (!imt) { - Sys_Printf ("\"%s\" is not a valid imt\n", imt_name); - return; - } - - key = Key_StringToKeynum (key_name); - if (key == -1) { - Sys_Printf ("\"%s\" is not a valid key\n", key_name); - return; - } - - if (!cmd) { - if (imt->bindings[key].str) - Sys_Printf ("%s %s \"%s\"\n", imt_name, key_name, - imt->bindings[key].str); - else - Sys_Printf ("%s %s is not bound\n", imt_name, key_name); - return; - } - Key_SetBinding (imt, key, cmd); -} - -static void -Key_In_Bind_f (void) -{ - int c, i; - const char *imt, *key, *cmd = 0; - char cmd_buf[1024]; - - c = Cmd_Argc (); - - if (c < 3) { - Sys_Printf ("in_bind [command] : attach a command to a " - "key\n"); - return; - } - - imt = Cmd_Argv (1); - - key = Cmd_Argv (2); - - if (c >= 4) { - cmd = cmd_buf; - cmd_buf[0] = 0; - for (i = 3; i < c; i++) { - strncat (cmd_buf, Cmd_Argv (i), sizeof (cmd_buf) - - strlen (cmd_buf)); - if (i != (c - 1)) - strncat (cmd_buf, " ", sizeof (cmd_buf) - strlen (cmd_buf)); - } - } - - Key_In_Bind (imt, key, cmd); -} - -static void -Key_Unbind_f (void) -{ - const char *key; - - if (Cmd_Argc () != 2) { - Sys_Printf ("unbind : remove commands from a key\n"); - return; - } - key = OK_TranslateKeyName (Cmd_Argv (1)); - Key_In_Unbind ("imt_mod", key); -} - -static void -Key_Bind_f (void) -{ - int c, i; - const char *key, *cmd = 0; - char cmd_buf[1024]; - - c = Cmd_Argc (); - - if (c < 2) { - Sys_Printf ("bind [command] : attach a command to a key\n"); - return; - } - - key = OK_TranslateKeyName (Cmd_Argv (1)); - - if (c >= 3) { - cmd = cmd_buf; - cmd_buf[0] = 0; - for (i = 2; i < c; i++) { - strncat (cmd_buf, Cmd_Argv (i), sizeof (cmd_buf) - - strlen (cmd_buf)); - if (i != (c - 1)) - strncat (cmd_buf, " ", sizeof (cmd_buf) - strlen (cmd_buf)); - } - } - - Key_In_Bind ("imt_mod", key, cmd); -} - -static void -Key_GIB_Bind_Get_f (void) -{ - const char *key, *cmd; - imt_t *imt; - int k; - - if (GIB_Argc () != 2) { - GIB_USAGE ("key"); - return; - } - - key = OK_TranslateKeyName (GIB_Argv (1)); - - if ((k = Key_StringToKeynum (key)) == -1) { - GIB_Error ("bind", "bind::get: invalid key %s", key); - return; - } - - imt = Key_FindIMT ("imt_mod"); - if (!imt || !(cmd = Key_GetBinding (imt, k))) - GIB_Return (""); - else - GIB_Return (cmd); -} - -static void -in_key_togglemenu_f (cvar_t *var) -{ - int k; - - if (!*var->string) { - key_togglemenu = QFK_ESCAPE; - return; - } - if ((k = Key_StringToKeynum (var->string)) == -1) { - k = QFK_ESCAPE; - Sys_Printf ("\"%s\" is not a valid key. setting to \"K_ESCAPE\"\n", - var->string); - } - key_togglemenu = k; -} - -static void -in_key_toggleconsole_f (cvar_t *var) -{ - int k; - - if (!*var->string) { - key_toggleconsole = -1; - return; - } - if ((k = Key_StringToKeynum (var->string)) == -1) { - Sys_Printf ("\"%s\" is not a valid key. not setting\n", - var->string); - return; - } - key_toggleconsole = k; -} - -static void -Key_InputMappingTable_f (void) -{ - int c; - imt_t *imt; - - c = Cmd_Argc (); - - if (c != 2) { - Sys_Printf ("Current imt is %s\n", key_targets[key_game].active->name); - Sys_Printf ("imt : set to a specific input mapping table\n"); - return; - } - - imt = Key_FindIMT (Cmd_Argv (1)); - if (!imt) { - Sys_Printf ("\"%s\" is not a valid imt\n", Cmd_Argv (1)); - return; - } - - key_targets[key_game].active = imt; -} - -static void -Key_IMT_Keydest_f (void) -{ - int c; - imt_t *imt; - const char *imt_name = 0; - const char *keydest; - keydest_t kd; - - c = Cmd_Argc (); - switch (c) { - case 3: - imt_name = Cmd_Argv (2); - case 2: - keydest = Cmd_Argv (1); - break; - default: - return; - } - for (kd = key_game; kd < key_last; kd++) { - if (!strcasecmp (keydest_names[kd], keydest)) { - break; - } - } - if (kd == key_last) { - Sys_Printf ("imt error: invalid keydest: %s\n", keydest); - return; - } - - if (!imt_name) { - Sys_Printf ("Current imt is %s\n", key_targets[key_game].active->name); - Sys_Printf ("imt : set to a specific input mapping table\n"); - return; - } - - imt = key_target_find_imt (&key_targets[kd], imt_name); - if (!imt) { - Sys_Printf ("\"%s\" is not an imt on %s\n", imt_name, keydest); - return; - } - - key_targets[kd].active = imt; -} - -static void __attribute__((format(printf,2,3))) -key_printf (QFile *f, const char *fmt, ...) -{ - va_list args; - static dstring_t *string; - - if (!string) { - string = dstring_new (); - } - va_start (args, fmt); - dvsprintf (string, fmt, args); - va_end (args); - - if (f) { - Qprintf (f, "%s", string->str); - } else { - Sys_Printf ("%s", string->str); - } -} - -static void -key_write_imt (QFile *f, keydest_t kd, imt_t *imt) -{ - int i; - const char *bind; - - if (!imt || imt->written) { - return; - } - imt->written = 1; - key_write_imt (f, kd, imt->chain); - if (imt->chain) { - key_printf (f, "imt_create %s %s %s\n", keydest_names[kd], - imt->name, imt->chain->name); - } else { - key_printf (f, "imt_create %s %s\n", keydest_names[kd], imt->name); - } - for (i = 0; i < QFK_LAST; i++) { - bind = imt->bindings[i].str; - if (bind) { - key_printf (f, "in_bind %s %s \"%s\"\n", imt->name, - Key_KeynumToString (i), bind); - } - } -} - -void -Key_WriteBindings (QFile *f) -{ - keydest_t kd; - imt_t *imt; - - for (kd = key_unfocused; kd < key_last; kd++) { - for (imt = key_targets[kd].imts; imt; imt = imt->next) { - imt->written = 0; - } - } - key_printf (f, "imt_drop_all\n"); - for (kd = key_unfocused; kd < key_last; kd++) { - if (key_targets[kd].imts) { - for (imt = key_targets[kd].imts; imt; imt = imt->next) { - key_write_imt (f, kd, imt); - } - key_printf (f, "imt_keydest %s %s\n", keydest_names[kd], - key_targets[kd].active->name); - } - } -} - -static void -Key_Bindlist_f (void) -{ - Key_WriteBindings (0); -} - -static void -keyhelp_f (void) -{ - keyhelp = 1; -} - -/* - Key_Event - - Called by the system between frames for both key up and key down events - Should NOT be called during an interrupt! -*/ -VISIBLE void -Key_Event (knum_t key, short unicode, qboolean down) -{ -// Sys_Printf ("%d %d %d : %d\n", key_target, key_dest, key, down); //@@@ - - if (down) { - keydown[key]++; - if (keyhelp) { - Sys_Printf ("Key name for that key is \"%s\"\n", - Key_KeynumToString (key)); - keyhelp = 0; - return; // gobble the key - } - } else { - keydown[key] = 0; - } - - // handle escape specially, so the user can never unbind it - if (key == key_togglemenu || key == key_toggleconsole) { - Key_Console (key, unicode); - return; - } - - if (!down && Key_Game (key, unicode)) - return; - - // if not a consolekey, send to the interpreter no matter what mode is - switch (key_dest) { - case key_game: - case key_demo: - Key_Game (key, unicode); - return; - case key_message: - case key_menu: - case key_console: - Key_Console (key, unicode); - return; - case key_unfocused: - return; - case key_last: - break; // should not happen, so hit the error - } - Sys_Error ("Bad key_dest"); -} - -VISIBLE void -Key_FocusEvent (int gain) -{ - if (gain) { - Key_CallDestCallbacks (key_dest); - } else { - Key_CallDestCallbacks (key_unfocused); - } -} - -void -Key_ClearStates (void) -{ - int i; - - for (i = 0; i < QFK_LAST; i++) { - if (keydown[i]) - Key_Event (i, 0, false); - keydown[i] = false; - } -} - -static struct { - keydest_t kd; - const char *imt_name; - const char *chain_imt_name; -} default_imts[] = { - {key_game, "imt_mod", 0}, - {key_game, "imt_0", "imt_mod"}, - {key_game, "imt_1", "imt_0"}, - {key_game, "imt_2", "imt_0"}, - {key_game, "imt_3", "imt_0"}, - {key_game, "imt_4", "imt_0"}, - {key_game, "imt_5", "imt_0"}, - {key_game, "imt_6", "imt_0"}, - {key_game, "imt_7", "imt_0"}, - {key_game, "imt_8", "imt_0"}, - {key_game, "imt_9", "imt_0"}, - {key_game, "imt_10", "imt_0"}, - {key_game, "imt_11", "imt_0"}, - {key_game, "imt_12", "imt_0"}, - {key_game, "imt_13", "imt_0"}, - {key_game, "imt_14", "imt_0"}, - {key_game, "imt_15", "imt_0"}, - {key_game, "imt_16", "imt_0"}, - {key_demo, "imt_demo", 0}, - {key_console, "imt_console", 0}, - {key_message, "imt_message", 0}, - {key_menu, "imt_menu", 0}, - {key_last, 0, 0}, -}; - -static void -Key_CreateDefaultIMTs (void) -{ - int i; - - for (i = 0; default_imts[i].kd != key_last; i++) { - Key_CreateIMT (default_imts[i].kd, default_imts[i].imt_name, - default_imts[i].chain_imt_name); - } -} - -void -Key_Init (cbuf_t *cb) -{ - cbuf = cb; - - Key_CreateDefaultIMTs (); - - OK_Init (); - - // register our functions - Cmd_AddCommand ("in_bind", Key_In_Bind_f, "Assign a command or a set of " - "commands to a key.\n" - "Note: To bind multiple commands to a key, enclose the " - "commands in quotes and separate with semi-colons."); - Cmd_AddCommand ("in_unbind", Key_In_Unbind_f, - "Remove the bind from the the selected key"); - Cmd_AddCommand ("unbindall", Key_Unbindall_f, - "Remove all binds (USE CAUTIOUSLY!!!)"); - Cmd_AddCommand ("in_clear", Key_In_Clear, - "Remove all binds from the specified imts"); - Cmd_AddCommand ("in_type", Key_In_Type_f, - "Send the given string as simulated key presses."); - Cmd_AddCommand ("imt", Key_InputMappingTable_f, ""); - Cmd_AddCommand ("imt_keydest", Key_IMT_Keydest_f, ""); - Cmd_AddCommand ("imt_create", Key_IMT_Create_f, - "create a new imt table:\n" - " imt_create [chain_name]\n" - "\n" - "The new table will be attached to the specified keydest\n" - "imt_name must not already exist.\n" - "If given, chain_name must already exist and be on " - "keydest.\n"); - Cmd_AddCommand ("imt_drop_all", Key_IMT_Drop_All_f, - "delete all imt tables\n"); - Cmd_AddCommand ("bind", Key_Bind_f, "wrapper for in_bind that uses " - "in_bind_imt for the imt parameter"); - Cmd_AddCommand ("unbind", Key_Unbind_f, - "wrapper for in_unbind that uses in_bind_imt for the imt " - "parameter"); - Cmd_AddCommand ("bindlist", Key_Bindlist_f, "list all of the key bindings"); - Cmd_AddCommand ("keyhelp", keyhelp_f, "display the keyname for the next " - "RECOGNIZED key-press. If the key pressed produces no " - "output, " PACKAGE_NAME " does not recognise that key."); - - GIB_Builtin_Add ("bind::get", Key_GIB_Bind_Get_f); - -} - -void -Key_Init_Cvars (void) -{ - Cvar_Get ("in_key_togglemenu", "", CVAR_NONE, in_key_togglemenu_f, - "Key for toggling the menu."); - Cvar_Get ("in_key_toggleconsole", "K_BACKQUOTE", CVAR_NONE, - in_key_toggleconsole_f, - "Key for toggling the console."); -} - -const char * -Key_GetBinding (imt_t *imt, knum_t key) -{ - if (imt) { - return imt->bindings[key].str; - } - return 0; -} - -VISIBLE void -Key_SetKeyDest(keydest_t kd) -{ - if ((int) kd < key_unfocused || kd >= key_last) { - Sys_Error ("Bad key_dest"); - } - key_dest = kd; - Key_CallDestCallbacks (key_dest); -} - -VISIBLE void -Key_KeydestCallback (keydest_callback_t *callback) -{ - if (num_keydest_callbacks == max_keydest_callbacks) { - size_t size = (max_keydest_callbacks + KEYDEST_CALLBACK_CHUNK) - * sizeof (keydest_callback_t *); - keydest_callbacks = realloc (keydest_callbacks, size); - if (!keydest_callbacks) - Sys_Error ("Too many keydest callbacks!"); - max_keydest_callbacks += KEYDEST_CALLBACK_CHUNK; - } - - if (!callback) - Sys_Error ("null keydest callback"); - - keydest_callbacks[num_keydest_callbacks++] = callback; -} diff --git a/libs/video/targets/old_keys.c b/libs/video/targets/old_keys.c deleted file mode 100644 index 99b87f4c4..000000000 --- a/libs/video/targets/old_keys.c +++ /dev/null @@ -1,257 +0,0 @@ -/* - old_keys.c - - translations from old to new keynames - - Copyright (C) 2001 Bill Currie - - Author: Bill Currie - Date: 2001/8/16 - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif - -#if defined(_WIN32) && defined(HAVE_MALLOC_H) -#include -#endif - -#include -#include - -#include "qfalloca.h" - -#include "QF/hash.h" -#include "QF/qtypes.h" -#include "QF/sys.h" - -#include "old_keys.h" - -typedef struct { - const char *old_name; - const char *new_name; -} old_keyname_t; - -old_keyname_t old_keynames[] = { - {"TAB", "K_TAB"}, - {"ENTER", "K_RETURN"}, - {"ESCAPE", "K_ESCAPE"}, - {"SPACE", "K_SPACE"}, - {"BACKSPACE", "K_BACKSPACE"}, - - {"CAPSLOCK", "K_CAPSLOCK"}, - {"PRINTSCR", "K_PRINT"}, - {"SCRLCK", "K_SCROLLOCK"}, - {"PAUSE", "K_PAUSE"}, - - {"UPARROW", "K_UP"}, - {"DOWNARROW", "K_DOWN"}, - {"LEFTARROW", "K_LEFT"}, - {"RIGHTARROW", "K_RIGHT"}, - - {"ALT", "K_LALT"}, - {"CTRL", "K_LCTRL"}, - {"SHIFT", "K_LSHIFT"}, - - {"NUMLOCK", "K_NUMLOCK"}, - {"KP_NUMLCK", "K_NUMLOCK"}, - {"KP_NUMLOCK", "K_NUMLOCK"}, - {"KP_SLASH", "K_KP_DIVIDE"}, - {"KP_DIVIDE", "K_KP_DIVIDE"}, - {"KP_STAR", "K_KP_MULTIPLY"}, - {"KP_MULTIPLY", "K_KP_MULTIPLY"}, - {"KP_MINUS", "K_KP_MINUS"}, - - {"KP_HOME", "K_KP7"}, - {"KP_UPARROW", "K_KP8"}, - {"KP_PGUP", "K_KP9"}, - {"KP_PLUS", "K_KP_PLUS"}, - - {"KP_LEFTARROW", "K_KP4"}, - {"KP_5", "K_KP5"}, - {"KP_RIGHTARROW", "K_KP6"}, - - {"KP_END", "K_KP1"}, - {"KP_DOWNARROW", "K_KP2"}, - {"KP_PGDN", "K_KP3"}, - - {"KP_INS", "K_KP0"}, - {"KP_DEL", "K_KP_PERIOD"}, - {"KP_ENTER", "K_KP_ENTER"}, - - {"F1", "K_F1"}, - {"F2", "K_F2"}, - {"F3", "K_F3"}, - {"F4", "K_F4"}, - {"F5", "K_F5"}, - {"F6", "K_F6"}, - {"F7", "K_F7"}, - {"F8", "K_F8"}, - {"F9", "K_F9"}, - {"F10", "K_F10"}, - {"F11", "K_F11"}, - {"F12", "K_F12"}, - - {"INS", "K_INSERT"}, - {"DEL", "K_DELETE"}, - {"PGDN", "K_PAGEDOWN"}, - {"PGUP", "K_PAGEUP"}, - {"HOME", "K_HOME"}, - {"END", "K_END"}, - - {"MOUSE1", "M_BUTTON1"}, - {"MOUSE2", "M_BUTTON2"}, - {"MOUSE3", "M_BUTTON3"}, - {"MOUSE6", "M_BUTTON6"}, - {"MOUSE7", "M_BUTTON7"}, - - {"JOY1", "J_BUTTON1"}, - {"JOY2", "J_BUTTON2"}, - {"JOY3", "J_BUTTON3"}, - {"JOY4", "J_BUTTON4"}, - - {"MWHEELUP", "M_WHEEL_UP"}, - {"MWHEELDOWN", "M_WHEEL_DOWN"}, - - {"ASC178", "K_WORLD_18"}, - {"ASC233", "K_WORLD_73"}, - {"ASC167", "K_WORLD_7"}, - {"ASC232", "K_WORLD_72"}, - {"ASC231", "K_WORLD_71"}, - {"ASC224", "K_WORLD_64"}, - - {"0", "K_0"}, - {"1", "K_1"}, - {"2", "K_2"}, - {"3", "K_3"}, - {"4", "K_4"}, - {"5", "K_5"}, - {"6", "K_6"}, - {"7", "K_7"}, - {"8", "K_8"}, - {"9", "K_9"}, - - {"A", "K_a"}, - {"B", "K_b"}, - {"C", "K_c"}, - {"D", "K_d"}, - {"E", "K_e"}, - {"F", "K_f"}, - {"G", "K_g"}, - {"H", "K_h"}, - {"I", "K_i"}, - {"J", "K_j"}, - {"K", "K_k"}, - {"L", "K_l"}, - {"M", "K_m"}, - {"N", "K_n"}, - {"O", "K_o"}, - {"P", "K_p"}, - {"Q", "K_q"}, - {"R", "K_r"}, - {"S", "K_s"}, - {"T", "K_t"}, - {"U", "K_u"}, - {"V", "K_v"}, - {"W", "K_w"}, - {"X", "K_x"}, - {"Y", "K_y"}, - {"Z", "K_z"}, - - {" ", "K_SPACE"}, - {"!", "K_EXCLAIM"}, - {"DOUBLEQUOTE", "K_QUOTEDBL"}, - {"#", "K_HASH"}, - {"$", "K_DOLLAR"}, - {"&", "K_AMPERSAND"}, - {"'", "K_QUOTE"}, - {"(", "K_LEFTPAREN"}, - {")", "K_RIGHTPAREN"}, - {"*", "K_ASTERISK"}, - {"+", "K_PLUS"}, - {",", "K_COMMA"}, - {"-", "K_MINUS"}, - {".", "K_PERIOD"}, - {"/", "K_SLASH"}, - - {":", "K_COLON"}, - {"SEMICOLON", "K_SEMICOLON"}, - {"<", "K_LESS"}, - {"=", "K_EQUALS"}, - {">", "K_GREATER"}, - {"?", "K_QUESTION"}, - {"@", "K_AT"}, - - {"[", "K_LEFTBRACKET"}, - {"\\", "K_BACKSLASH"}, - {"]", "K_RIGHTBRACKET"}, - {"^", "K_CARET"}, - {"_", "K_UNDERSCORE"}, - {"`", "K_BACKQUOTE"}, - {"~", "K_BACKQUOTE"}, - - {0, 0} -}; - -hashtab_t *old_key_table; - -static const char * -ok_get_key (const void *_ok, void *unused) -{ - old_keyname_t *ok = (old_keyname_t *)_ok; - return ok->old_name; -} - -void -OK_Init (void) -{ - old_keyname_t *ok; - - old_key_table = Hash_NewTable (1021, ok_get_key, 0, 0); - for (ok = old_keynames; ok->old_name; ok++) - Hash_Add (old_key_table, ok); -} - -const char * -OK_TranslateKeyName (const char *name) -{ - old_keyname_t *ok; - char *uname = alloca (strlen (name) + 1); - const char *s = name; - char *d = uname; - - while ((*d++ = toupper ((byte) *s))) - s++; - ok = Hash_Find (old_key_table, uname); - if (!ok) { - Sys_Printf ("unknown old keyname: %s\n", uname); - return name; - } - return ok->new_name; -} diff --git a/libs/video/targets/vid.c b/libs/video/targets/vid.c index 46507e26d..825433180 100644 --- a/libs/video/targets/vid.c +++ b/libs/video/targets/vid.c @@ -36,77 +36,88 @@ #endif #include -#include "QF/console.h" #include "QF/cvar.h" #include "QF/mathlib.h" #include "QF/qargs.h" #include "QF/sys.h" #include "QF/va.h" +#include "QF/ui/view.h" #include "compat.h" +#include "d_iface.h" #include "vid_internal.h" /* Software and hardware gamma support */ #define viddef (*r_data->vid) -byte *vid_colormap; -cvar_t *vid_gamma; -cvar_t *vid_system_gamma; -cvar_t *con_width; // FIXME: Try to move with rest of con code -cvar_t *con_height; // FIXME: Try to move with rest of con code -qboolean vid_gamma_avail; // hardware gamma availability +#define vi (viddef.vid_internal) +float vid_gamma; +static cvar_t vid_gamma_cvar = { + .name = "vid_gamma", + .description = + "Gamma correction", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &vid_gamma }, +}; +int vid_system_gamma; +static cvar_t vid_system_gamma_cvar = { + .name = "vid_system_gamma", + .description = + "Use system gamma control if available", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &vid_system_gamma }, +}; +bool vid_gamma_avail; // hardware gamma availability VISIBLE unsigned int d_8to24table[256]; /* Screen size */ -cvar_t *vid_width; -cvar_t *vid_height; -cvar_t *vid_aspect; +int vid_width; +static cvar_t vid_width_cvar = { + .name = "vid_width", + .description = + "screen width", + .default_value = 0, + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &vid_width }, +}; +int vid_height; +static cvar_t vid_height_cvar = { + .name = "vid_height", + .description = + "screen height", + .default_value = 0, + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &vid_height }, +}; -cvar_t *vid_fullscreen; - -static void -vid_aspect_f (cvar_t *var) -{ - const char *p = strchr (var->string, ':'); - float w, h; - - if (p) { - w = atof (var->string); - h = atof (p + 1); - if (w > 0.0 && h > 0.0) { - var->vec[0] = w; - var->vec[1] = h; - return; - } - } - Sys_Printf ("badly formed aspect ratio: %s. Using default 4:3\n", - var->string); - var->vec[0] = 4.0; - var->vec[1] = 3.0; -} +int vid_fullscreen; +static cvar_t vid_fullscreen_cvar = { + .name = "vid_fullscreen", + .description = + "Toggles fullscreen mode", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &vid_fullscreen }, +}; void VID_GetWindowSize (int def_w, int def_h) { - int pnum, conheight; + int pnum; - vid_width = Cvar_Get ("vid_width", va ("%d", def_w), CVAR_NONE, NULL, - "screen width"); - vid_height = Cvar_Get ("vid_height", va ("%d", def_h), CVAR_NONE, NULL, - "screen height"); - vid_aspect = Cvar_Get ("vid_aspect", "4:3", CVAR_ROM, vid_aspect_f, - "Physical screen aspect ratio in \"width:height\" format. " - "Common values are 4:3, 5:3, 8:5, 16:9, but any width:height " - "measurement will do (eg, 475:296.875 the approximate dimentions " - "in mm of the display area of a certain monitor)"); + vid_width_cvar.default_value = nva ("%d", def_w); + vid_height_cvar.default_value = nva ("%d", def_h); + Cvar_Register (&vid_width_cvar, 0, 0); + Cvar_Register (&vid_height_cvar, 0, 0); if ((pnum = COM_CheckParm ("-width"))) { if (pnum >= com_argc - 1) Sys_Error ("VID: -width "); - Cvar_Set (vid_width, com_argv[pnum + 1]); - - if (!vid_width->int_val) + vid_width = atoi (com_argv[pnum + 1]); + if (!vid_width) Sys_Error ("VID: Bad window width"); } @@ -114,9 +125,8 @@ VID_GetWindowSize (int def_w, int def_h) if (pnum >= com_argc - 1) Sys_Error ("VID: -height "); - Cvar_Set (vid_height, com_argv[pnum + 1]); - - if (!vid_height->int_val) + vid_height = atoi (com_argv[pnum + 1]); + if (!vid_height) Sys_Error ("VID: Bad window height"); } @@ -124,48 +134,34 @@ VID_GetWindowSize (int def_w, int def_h) if (pnum >= com_argc - 2) Sys_Error ("VID: -winsize "); - Cvar_Set (vid_width, com_argv[pnum + 1]); - Cvar_Set (vid_height, com_argv[pnum + 2]); + vid_width = atoi (com_argv[pnum + 1]); + vid_height = atoi (com_argv[pnum + 2]); - if (!vid_width->int_val || !vid_height->int_val) + if (!vid_width || !vid_height) Sys_Error ("VID: Bad window width/height"); } - Cvar_SetFlags (vid_width, vid_width->flags | CVAR_ROM); - Cvar_SetFlags (vid_height, vid_height->flags | CVAR_ROM); - viddef.width = vid_width->int_val; - viddef.height = vid_height->int_val; + // viddef.maxlowwidth = LOW_WIDTH; + // viddef.maxlowheight = LOW_HEIGHT; - viddef.aspect = ((vid_aspect->vec[0] * viddef.height) - / (vid_aspect->vec[1] * viddef.width)); + viddef.width = vid_width; + viddef.height = vid_height; +} - con_width = Cvar_Get ("con_width", va ("%d", viddef.width), CVAR_NONE, - NULL, "console effective width (GL only)"); - if ((pnum = COM_CheckParm ("-conwidth"))) { - if (pnum >= com_argc - 1) - Sys_Error ("VID: -conwidth "); - Cvar_Set (con_width, com_argv[pnum + 1]); +void +VID_SetWindowSize (int width, int height) +{ + if (width < 0 || height < 0) { + Sys_Error ("VID_SetWindowSize: invalid size: %d, %d", width, height); } - // make con_width a multiple of 8 and >= 320 - Cvar_Set (con_width, va ("%d", max (con_width->int_val & ~7, 320))); - Cvar_SetFlags (con_width, con_width->flags | CVAR_ROM); - viddef.conwidth = con_width->int_val; - - conheight = (viddef.conwidth * vid_aspect->vec[1]) / vid_aspect->vec[0]; - con_height = Cvar_Get ("con_height", va ("%d", conheight), CVAR_NONE, NULL, - "console effective height (GL only)"); - if ((pnum = COM_CheckParm ("-conheight"))) { - if (pnum >= com_argc - 1) - Sys_Error ("VID: -conheight "); - Cvar_Set (con_height, com_argv[pnum + 1]); + if (width != (int) viddef.width || height != (int) viddef.height) { + viddef.width = width; + viddef.height = height; + if (viddef.onVidResize) { + LISTENER_INVOKE (viddef.onVidResize, &viddef); + } } - // make con_height >= 200 - Cvar_Set (con_height, va ("%d", max (con_height->int_val, 200))); - Cvar_SetFlags (con_height, con_height->flags | CVAR_ROM); - viddef.conheight = con_height->int_val; - - Con_CheckResize (); // Now that we have a window size, fix console } /* GAMMA FUNCTIONS */ @@ -190,118 +186,185 @@ VID_BuildGammaTable (double gamma) } } -/* - VID_UpdateGamma - - This is a callback to update the palette or system gamma whenever the - vid_gamma Cvar is changed. -*/ void -VID_UpdateGamma (cvar_t *vid_gamma) +VID_UpdateGamma (void) { - double gamma = bound (0.1, vid_gamma->value, 9.9); + byte *p24; + byte *p32; + const byte *col; + int i; viddef.recalc_refdef = 1; // force a surface cache flush - if (vid_gamma_avail && vid_system_gamma->int_val) { // Have system, use it - Sys_MaskPrintf (SYS_VID, "Setting hardware gamma to %g\n", gamma); + if (vid_gamma_avail && vid_system_gamma) { // Have system, use it + Sys_MaskPrintf (SYS_vid, "Setting hardware gamma to %g\n", vid_gamma); VID_BuildGammaTable (1.0); // hardware gamma wants a linear palette - VID_SetGamma (gamma); - memcpy (viddef.palette, viddef.basepal, 256 * 3); + VID_SetGamma (vid_gamma); + p24 = viddef.palette; + p32 = viddef.palette32; + col = viddef.basepal; + for (i = 0; i < 256; i++) { + *p32++ = *p24++ = *col++; + *p32++ = *p24++ = *col++; + *p32++ = *p24++ = *col++; + *p32++ = 255; + } + p32[-1] = 0; // color 255 is transparent } else { // We have to hack the palette - int i; - Sys_MaskPrintf (SYS_VID, "Setting software gamma to %g\n", gamma); - VID_BuildGammaTable (gamma); - for (i = 0; i < 256 * 3; i++) - viddef.palette[i] = viddef.gammatable[viddef.basepal[i]]; - viddef.set_palette (viddef.palette); // update with the new palette + Sys_MaskPrintf (SYS_vid, "Setting software gamma to %g\n", vid_gamma); + VID_BuildGammaTable (vid_gamma); + p24 = viddef.palette; + p32 = viddef.palette32; + col = viddef.basepal; + for (i = 0; i < 256; i++) { + *p32++ = *p24++ = viddef.gammatable[*col++]; + *p32++ = *p24++ = viddef.gammatable[*col++]; + *p32++ = *p24++ = viddef.gammatable[*col++]; + *p32++ = 255; + } + p32[-1] = 0; // color 255 is transparent + // update with the new palette + vi->set_palette (vi->ctx, viddef.palette); } } +static void +vid_gamma_f (void *data, const cvar_t *cvar) +{ + vid_gamma = bound (0.1, vid_gamma, 9.9); + VID_UpdateGamma (); +} + /* VID_InitGamma Initialize the vid_gamma Cvar, and set up the palette */ void -VID_InitGamma (unsigned char *pal) +VID_InitGamma (const byte *pal) { int i; double gamma = 1.0; + static int cvar_initialized; + free (viddef.gammatable); + free (viddef.palette); + free (viddef.palette32); viddef.gammatable = malloc (256); viddef.basepal = pal; viddef.palette = malloc (256 * 3); + viddef.palette32 = malloc (256 * 4); if ((i = COM_CheckParm ("-gamma"))) { gamma = atof (com_argv[i + 1]); } gamma = bound (0.1, gamma, 9.9); - vid_gamma = Cvar_Get ("vid_gamma", va ("%f", gamma), CVAR_ARCHIVE, - VID_UpdateGamma, "Gamma correction"); + if (!cvar_initialized) { + cvar_initialized = 1; + Cvar_Register (&vid_gamma_cvar, vid_gamma_f, 0); + } - VID_BuildGammaTable (vid_gamma->value); + //VID_BuildGammaTable (vid_gamma); + VID_UpdateGamma (); + + if (viddef.onPaletteChanged) { + LISTENER_INVOKE (viddef.onPaletteChanged, &viddef); + } } void -VID_InitBuffers (void) +VID_ClearMemory (void) { - int buffersize, zbuffersize, cachesize = 1; - - // No console scaling in the sw renderer - viddef.conwidth = viddef.width; - viddef.conheight = viddef.height; - Con_CheckResize (); - - // Calculate the sizes we want first - buffersize = viddef.rowbytes * viddef.height; - zbuffersize = viddef.width * viddef.height * sizeof (*viddef.zbuffer); - if (viddef.surf_cache_size) - cachesize = viddef.surf_cache_size (viddef.width, viddef.height); - - // Free the old z-buffer - if (viddef.zbuffer) { - free (viddef.zbuffer); - viddef.zbuffer = NULL; + if (vi->flush_caches) { + vi->flush_caches (vi->ctx); } - // Free the old surface cache - if (viddef.surfcache) { - if (viddef.flush_caches) - viddef.flush_caches (); - free (viddef.surfcache); - viddef.surfcache = NULL; - } - if (viddef.do_screen_buffer) { - viddef.do_screen_buffer (); - } else { - // Free the old screen buffer - if (viddef.buffer) { - free (viddef.buffer); - viddef.conbuffer = viddef.buffer = NULL; - } - // Allocate the new screen buffer - viddef.conbuffer = viddef.buffer = calloc (buffersize, 1); - if (!viddef.conbuffer) { - Sys_Error ("Not enough memory for video mode"); - } - } - // Allocate the new z-buffer - viddef.zbuffer = calloc (zbuffersize, 1); - if (!viddef.zbuffer) { - free (viddef.buffer); - viddef.conbuffer = viddef.buffer = NULL; - Sys_Error ("Not enough memory for video mode"); - } - // Allocate the new surface cache; free the z-buffer if we fail - viddef.surfcache = calloc (cachesize, 1); - if (!viddef.surfcache) { - free (viddef.buffer); - free (viddef.zbuffer); - viddef.conbuffer = viddef.buffer = NULL; - viddef.zbuffer = NULL; - Sys_Error ("Not enough memory for video mode"); - } - - if (viddef.init_caches) - viddef.init_caches (viddef.surfcache, cachesize); +} + +VISIBLE void +VID_OnPaletteChange_AddListener (viddef_listener_t listener, void *data) +{ + if (!viddef.onPaletteChanged) { + viddef.onPaletteChanged = malloc (sizeof (*viddef.onPaletteChanged)); + LISTENER_SET_INIT (viddef.onPaletteChanged, 8); + } + LISTENER_ADD (viddef.onPaletteChanged, listener, data); +} + +VISIBLE void +VID_OnPaletteChange_RemoveListener (viddef_listener_t listener, void *data) +{ + if (viddef.onPaletteChanged) { + LISTENER_REMOVE (viddef.onPaletteChanged, listener, data); + } +} + +VISIBLE void +VID_OnVidResize_AddListener (viddef_listener_t listener, void *data) +{ + if (!viddef.onVidResize) { + viddef.onVidResize = malloc (sizeof (*viddef.onVidResize)); + LISTENER_SET_INIT (viddef.onVidResize, 8); + } + LISTENER_ADD (viddef.onVidResize, listener, data); +} + +VISIBLE void +VID_OnVidResize_RemoveListener (viddef_listener_t listener, void *data) +{ + if (viddef.onVidResize) { + LISTENER_REMOVE (viddef.onVidResize, listener, data); + } +} + +static void +VID_shutdown (void *data) +{ + if (vid_system.shutdown) { + vid_system.shutdown (); + } + free ((char *) vid_width_cvar.default_value); + free ((char *) vid_height_cvar.default_value); + free (viddef.gammatable); + free (viddef.palette); + free (viddef.palette32); + + if (viddef.onPaletteChanged) { + DARRAY_CLEAR (viddef.onPaletteChanged); + free (viddef.onPaletteChanged); + } + if (viddef.onVidResize) { + DARRAY_CLEAR (viddef.onVidResize); + free (viddef.onVidResize); + } +} + +VISIBLE void +VID_Init (byte *palette, byte *colormap) +{ + Sys_RegisterShutdown (VID_shutdown, 0); + vid_system.init (palette, colormap); +} + +VISIBLE void +VID_SetPalette (byte *palette, byte *colormap) +{ + vid_system.set_palette (palette, colormap); +} + +static void +vid_fullscreen_f (void *data, const cvar_t *var) +{ + vid_system.update_fullscreen (vid_fullscreen); +} + +VISIBLE void +VID_Init_Cvars (void) +{ + if (vid_system.update_fullscreen) { + // A bit of a hack, but windows registers a vid_fullscreen command + // and does fullscreen handling differently. + Cvar_Register (&vid_fullscreen_cvar, vid_fullscreen_f, 0); + } + Cvar_Register (&vid_system_gamma_cvar, 0, 0); + vid_system.init_cvars (); } diff --git a/libs/video/targets/vid_3dfxsvga.c b/libs/video/targets/vid_3dfxsvga.c index e7df1f342..a51dd09aa 100644 --- a/libs/video/targets/vid_3dfxsvga.c +++ b/libs/video/targets/vid_3dfxsvga.c @@ -121,8 +121,8 @@ QFGL_LoadLibrary (void) { void *handle; - if (!(handle = dlopen (gl_driver->string, RTLD_NOW))) { - Sys_Error ("Couldn't load OpenGL library %s: %s", gl_driver->string, + if (!(handle = dlopen (gl_driver, RTLD_NOW))) { + Sys_Error ("Couldn't load OpenGL library %s: %s", gl_driver, dlerror ()); } glGetProcAddress = dlsym (handle, "glXGetProcAddress"); @@ -150,8 +150,8 @@ QFGL_LoadLibrary (void) #endif // HAVE_DLOPEN -void -VID_Shutdown (void) +static void +VID_shutdown (void) { if (!fc) return; @@ -173,20 +173,20 @@ GL_Init (void) if (!(dither_select = QFGL_ExtensionAddress ("gl3DfxSetDitherModeEXT"))) return; - Sys_MaskPrintf (SYS_VID, "Dithering: "); + Sys_MaskPrintf (SYS_vid, "Dithering: "); if ((p = COM_CheckParm ("-dither")) && p < com_argc) { if (strequal (com_argv[p+1], "2x2")) { dither_select (GR_DITHER_2x2); - Sys_MaskPrintf (SYS_VID, "2x2.\n"); + Sys_MaskPrintf (SYS_vid, "2x2.\n"); } if (strequal (com_argv[p+1], "4x4")) { dither_select (GR_DITHER_4x4); - Sys_MaskPrintf (SYS_VID, "4x4.\n"); + Sys_MaskPrintf (SYS_vid, "4x4.\n"); } } else { qfglDisable (GL_DITHER); - Sys_MaskPrintf (SYS_VID, "disabled.\n"); + Sys_MaskPrintf (SYS_vid, "disabled.\n"); } } @@ -271,6 +271,8 @@ VID_Init (byte *palette, byte *colormap) { GLint attribs[32]; + Sys_RegisterShutdown (VID_shutdown); + GLF_Init (); qf_fxMesaCreateContext = QFGL_ProcAddress (libgl_handle, @@ -320,7 +322,7 @@ VID_Init (byte *palette, byte *colormap) vid.initialized = true; - Sys_MaskPrintf (SYS_VID, "Video mode %dx%d initialized.\n", + Sys_MaskPrintf (SYS_vid, "Video mode %dx%d initialized.\n", vid.width, vid.height); vid.recalc_refdef = 1; // force a surface cache flush @@ -329,8 +331,7 @@ VID_Init (byte *palette, byte *colormap) void VID_Init_Cvars (void) { - vid_system_gamma = Cvar_Get ("vid_system_gamma", "1", CVAR_ARCHIVE, NULL, - "Use system gamma control if available"); + Cvar_Register (&vid_system_gamma_cvar, 0, 0); } void @@ -338,7 +339,7 @@ VID_SetCaption (const char *text) { } -qboolean +bool VID_SetGamma (double gamma) { return true; diff --git a/libs/video/targets/vid_fbdev.c b/libs/video/targets/vid_fbdev.c index 5cd81cc47..bef6149c3 100644 --- a/libs/video/targets/vid_fbdev.c +++ b/libs/video/targets/vid_fbdev.c @@ -90,8 +90,24 @@ static byte vid_current_palette[768]; static int fbdev_inited = 0; static int fbdev_backgrounded = 0; -static cvar_t *vid_redrawfull; -static cvar_t *vid_waitforrefresh; +static int vid_redrawfull; +static cvar_t vid_redrawfull_cvar = { + .name = "vid_redrawfull", + .description = + "Redraw entire screen each frame instead of just dirty areas", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &vid_redrawfull }, +}; +static int vid_waitforrefresh; +static cvar_t vid_waitforrefresh_cvar = { + .name = "vid_waitforrefresh", + .description = + "Wait for vertical retrace before drawing next frame", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &vid_waitforrefresh }, +}; static byte *framebuffer_ptr; @@ -209,10 +225,10 @@ static unsigned long fb_map_length = 0; static struct fb_var_screeninfo orig_var; -void -VID_Shutdown (void) +static void +VID_shutdown (void) { - Sys_MaskPrintf (SYS_VID, "VID_Shutdown\n"); + Sys_MaskPrintf (SYS_vid, "VID_Shutdown\n"); if (!fbdev_inited) return; @@ -406,6 +422,8 @@ VID_Init (byte *palette, byte *colormap) if (fbdev_inited) return; + Sys_RegisterShutdown (VID_shutdown); + R_LoadModule (0, VID_SetPalette); if (COM_CheckParm ("-novideo")) { @@ -444,7 +462,7 @@ VID_Init (byte *palette, byte *colormap) Cmd_AddCommand ("vid_fbset", VID_fbset_f, "No Description"); /* Interpret command-line params */ - VID_GetWindowSize (320, 200); + VID_GetWindowSize (640, 480); modestr = get_mode (viddef.width, viddef.height, 8); @@ -465,10 +483,8 @@ VID_Init (byte *palette, byte *colormap) void VID_Init_Cvars () { - vid_redrawfull = Cvar_Get ("vid_redrawfull", "0", CVAR_NONE, NULL, - "Redraw entire screen each frame instead of just dirty areas"); - vid_waitforrefresh = Cvar_Get ("vid_waitforrefresh", "0", CVAR_ARCHIVE, - NULL, "Wait for vertical retrace before drawing next frame"); + Cvar_Register (&vid_redrawfull_cvar, 0, 0); + Cvar_Register (&vid_waitforrefresh_cvar, 0, 0); } void @@ -490,11 +506,11 @@ VID_Update (vrect_t *rects) } } - if (vid_waitforrefresh->int_val) { + if (vid_waitforrefresh) { // ??? } - if (vid_redrawfull->int_val) { + if (vid_redrawfull) { double *d = (double *)framebuffer_ptr, *s = (double *)viddef.buffer; double *ends = (double *)(viddef.buffer + viddef.height*viddef.rowbytes); @@ -523,22 +539,12 @@ VID_Update (vrect_t *rects) } } -void -VID_LockBuffer (void) -{ -} - -void -VID_UnlockBuffer (void) -{ -} - void VID_SetCaption (const char *text) { } -qboolean +bool VID_SetGamma (double gamma) { return false; diff --git a/libs/video/targets/vid_sdl.c b/libs/video/targets/vid_sdl.c index b32efbee1..b653aa684 100644 --- a/libs/video/targets/vid_sdl.c +++ b/libs/video/targets/vid_sdl.c @@ -51,209 +51,45 @@ #ifdef _WIN32 // FIXME: evil hack to get full DirectSound support with SDL #include #include -HWND mainwindow; +HWND win_mainwindow; #endif -// The original defaults -#define BASEWIDTH 320 -#define BASEHEIGHT 200 +SDL_Surface *sdl_screen = NULL; -byte *VGA_pagebase; -int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes = 0; +static vid_internal_t vid_internal; -SDL_Surface *screen = NULL; - -// Define GLAPIENTRY to a useful value -#ifndef GLAPIENTRY -# ifdef _WIN32 -# include -# define GLAPIENTRY WINAPI -# undef LoadImage -# else -# ifdef APIENTRY -# define GLAPIENTRY APIENTRY -# else -# define GLAPIENTRY -# endif -# endif -#endif - -static void (*set_vid_mode) (Uint32 flags); - -static void (GLAPIENTRY *qfglFinish) (void); -static int use_gl_procaddress = 0; -static cvar_t *gl_driver; - -static byte cached_palette[256 * 3]; -static int update_palette; - -static void * -QFGL_ProcAddress (const char *name, qboolean crit) -{ - void *glfunc = NULL; - - Sys_MaskPrintf (SYS_VID, "DEBUG: Finding symbol %s ... ", name); - - glfunc = SDL_GL_GetProcAddress (name); - if (glfunc) { - Sys_MaskPrintf (SYS_VID, "found [%p]\n", glfunc); - return glfunc; - } - Sys_MaskPrintf (SYS_VID, "not found\n"); - - if (crit) { - if (strncmp ("fxMesa", name, 6) == 0) { - Sys_Printf ("This target requires a special version of Mesa with " - "support for Glide and SVGAlib.\n"); - Sys_Printf ("If you are in X, try using a GLX or SGL target.\n"); - } - Sys_Error ("Couldn't load critical OpenGL function %s, exiting...", - name); - } - return NULL; -} +uint32_t sdl_flags; static void -sdlgl_set_vid_mode (Uint32 flags) +VID_shutdown (void *data) { - int i, j; - - flags |= SDL_OPENGL; - - // Setup GL Attributes - SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1); -// SDL_GL_SetAttribute (SDL_GL_STENCIL_SIZE, 0); // Try for 0, 8 -// SDL_GL_SetAttribute (SDL_GL_STEREO, 1); // Someday... - - for (i = 0; i < 5; i++) { - int k; - int color[5] = {32, 24, 16, 15, 0}; - int rgba[5][4] = { - {8, 8, 8, 0}, - {8, 8, 8, 8}, - {5, 6, 5, 0}, - {5, 5, 5, 0}, - {5, 5, 5, 1}, - }; - - SDL_GL_SetAttribute (SDL_GL_RED_SIZE, rgba[i][0]); - SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, rgba[i][1]); - SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, rgba[i][2]); - SDL_GL_SetAttribute (SDL_GL_ALPHA_SIZE, rgba[i][3]); - - for (j = 0; j < 5; j++) { - for (k = 32; k >= 16; k -= 8) { - SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, k); - if ((screen = SDL_SetVideoMode (viddef.width, viddef.height, - color[j], flags))) - goto success; - } - } - } - - Sys_Error ("Couldn't set video mode: %s", SDL_GetError ()); SDL_Quit (); - -success: - viddef.numpages = 2; - - viddef.init_gl (); -} - -static void -sdlgl_end_rendering (void) -{ - qfglFinish (); - SDL_GL_SwapBuffers (); -} - -static void -sdl_load_gl (void) -{ - viddef.get_proc_address = QFGL_ProcAddress; - viddef.end_rendering = sdlgl_end_rendering; - set_vid_mode = sdlgl_set_vid_mode; - - if (SDL_GL_LoadLibrary (gl_driver->string) != 0) - Sys_Error ("Couldn't load OpenGL library %s!", gl_driver->string); - - use_gl_procaddress = 1; - - qfglFinish = QFGL_ProcAddress ("glFinish", true); -} - -static void -sdl_update_palette (const byte *palette) -{ - SDL_Color colors[256]; - int i; - - for (i = 0; i < 256; ++i) { - colors[i].r = *palette++; - colors[i].g = *palette++; - colors[i].b = *palette++; - } - SDL_SetColors (screen, colors, 0, 256); -} - -static void -VID_SetPalette (const byte *palette) -{ - if (memcmp (cached_palette, palette, sizeof (cached_palette))) { - memcpy (cached_palette, palette, sizeof (cached_palette)); - update_palette = 1; - } -} - -static void -do_screen_buffer (void) -{ -} - -static void -sdl_set_vid_mode (Uint32 flags) -{ - // Initialize display - if (!(screen = SDL_SetVideoMode (viddef.width, viddef.height, 8, flags))) - Sys_Error ("VID: Couldn't set video mode: %s", SDL_GetError ()); - - // now know everything we need to know about the buffer - VGA_width = viddef.width; - VGA_height = viddef.height; - viddef.do_screen_buffer = do_screen_buffer; - VGA_pagebase = viddef.buffer = screen->pixels; - VGA_rowbytes = viddef.rowbytes = screen->pitch; - viddef.conbuffer = viddef.buffer; - viddef.conrowbytes = viddef.rowbytes; - viddef.direct = 0; - - VID_InitBuffers (); // allocate z buffer and surface cache } void VID_Init (byte *palette, byte *colormap) { - Uint32 flags; - - set_vid_mode = sdl_set_vid_mode; + Sys_RegisterShutdown (VID_shutdown, 0); + vid_internal.gl_context = SDL_GL_Context; + vid_internal.sw_context = SDL_SW_Context; // Load the SDL library if (SDL_Init (SDL_INIT_VIDEO) < 0) Sys_Error ("VID: Couldn't load SDL: %s", SDL_GetError ()); - R_LoadModule (sdl_load_gl, VID_SetPalette); + R_LoadModule (&vid_internal); viddef.numpages = 1; viddef.colormap8 = colormap; viddef.fullbright = 256 - viddef.colormap8[256 * VID_GRADES]; // Set up display mode (width and height) - VID_GetWindowSize (BASEWIDTH, BASEHEIGHT); + VID_GetWindowSize (640, 480); // Set video width, height and flags - flags = (SDL_SWSURFACE | SDL_HWPALETTE); - if (vid_fullscreen->int_val) { - flags |= SDL_FULLSCREEN; + sdl_flags = (SDL_SWSURFACE | SDL_HWPALETTE); + if (vid_fullscreen) { + sdl_flags |= SDL_FULLSCREEN; #ifndef _WIN32 // Don't annoy Mesa/3dfx folks // doesn't hurt if not using a gl renderer // FIXME: Maybe this could be put in a different spot, but I don't @@ -265,11 +101,10 @@ VID_Init (byte *palette, byte *colormap) #endif } - set_vid_mode (flags); + vid_internal.create_context (); VID_SDL_GammaCheck (); VID_InitGamma (palette); - viddef.set_palette (viddef.palette); viddef.initialized = true; @@ -281,7 +116,7 @@ VID_Init (byte *palette, byte *colormap) // SDL_GetWMInfo(&info); // mainwindow=info.window; - mainwindow=GetActiveWindow(); + win_mainwindow=GetActiveWindow(); #endif viddef.recalc_refdef = 1; // force a surface cache flush @@ -291,46 +126,8 @@ void VID_Init_Cvars () { SDL_Init_Cvars (); - gl_driver = Cvar_Get ("gl_driver", GL_DRIVER, CVAR_ROM, NULL, - "The OpenGL library to use. (path optional)"); -} - -void -VID_Update (vrect_t *rects) -{ - static SDL_Rect *sdlrects; - static int num_sdlrects; - int i, n; - vrect_t *rect; - - if (update_palette) { - update_palette = 0; - sdl_update_palette (cached_palette); - } - // Two-pass system, since Quake doesn't do it the SDL way... - - // First, count the number of rectangles - n = 0; - for (rect = rects; rect; rect = rect->next) - ++n; - - if (n > num_sdlrects) { - num_sdlrects = n; - sdlrects = realloc (sdlrects, n * sizeof (SDL_Rect)); - if (!sdlrects) - Sys_Error ("Out of memory!"); - } - - // Second, copy them to SDL rectangles and update - i = 0; - for (rect = rects; rect; rect = rect->next) { - sdlrects[i].x = rect->x; - sdlrects[i].y = rect->y; - sdlrects[i].w = rect->width; - sdlrects[i].h = rect->height; - ++i; - } - SDL_UpdateRects (screen, n, sdlrects); + SDL_GL_Init_Cvars (); + SDL_SW_Init_Cvars (); } void @@ -338,14 +135,14 @@ D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) { Uint8 *offset; - if (!screen) + if (!sdl_screen) return; if (x < 0) - x = screen->w + x - 1; - offset = (Uint8 *) screen->pixels + y * screen->pitch + x; + x = sdl_screen->w + x - 1; + offset = (Uint8 *) sdl_screen->pixels + y * sdl_screen->pitch + x; while (height--) { memcpy (offset, pbitmap, width); - offset += screen->pitch; + offset += sdl_screen->pitch; pbitmap += width; } } @@ -353,19 +150,9 @@ D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) void D_EndDirectRect (int x, int y, int width, int height) { - if (!screen) + if (!sdl_screen) return; if (x < 0) - x = screen->w + x - 1; - SDL_UpdateRect (screen, x, y, width, height); -} - -void -VID_LockBuffer (void) -{ -} - -void -VID_UnlockBuffer (void) -{ + x = sdl_screen->w + x - 1; + SDL_UpdateRect (sdl_screen, x, y, width, height); } diff --git a/libs/video/targets/vid_sdl_gl.c b/libs/video/targets/vid_sdl_gl.c new file mode 100644 index 000000000..b47febbaa --- /dev/null +++ b/libs/video/targets/vid_sdl_gl.c @@ -0,0 +1,185 @@ +/* + vid_sdl.c + + Video driver for Sam Lantinga's Simple DirectMedia Layer + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include + +#include "QF/console.h" +#include "QF/cvar.h" +#include "QF/qendian.h" +#include "QF/sys.h" +#include "QF/vid.h" + +#include "context_sdl.h" +#include "d_iface.h" +#include "vid_internal.h" +#include "vid_gl.h" + +// Define GLAPIENTRY to a useful value +#ifndef GLAPIENTRY +# ifdef _WIN32 +# include +# define GLAPIENTRY WINAPI +# undef LoadImage +# else +# ifdef APIENTRY +# define GLAPIENTRY APIENTRY +# else +# define GLAPIENTRY +# endif +# endif +#endif + +static void (GLAPIENTRY *qfglFinish) (void); +static int use_gl_procaddress = 0; +static char *gl_driver; +static cvar_t gl_driver_cvar = { + .name = "gl_driver", + .description = + "The OpenGL library to use. (path optional)", + .default_value = GL_DRIVER, + .flags = CVAR_ROM, + .value = { .type = 0, .value = &gl_driver }, +}; + +static void * +QFGL_ProcAddress (const char *name, bool crit) +{ + void *glfunc = NULL; + + Sys_MaskPrintf (SYS_vid, "DEBUG: Finding symbol %s ... ", name); + + glfunc = SDL_GL_GetProcAddress (name); + if (glfunc) { + Sys_MaskPrintf (SYS_vid, "found [%p]\n", glfunc); + return glfunc; + } + Sys_MaskPrintf (SYS_vid, "not found\n"); + + if (crit) { + if (strncmp ("fxMesa", name, 6) == 0) { + Sys_Printf ("This target requires a special version of Mesa with " + "support for Glide and SVGAlib.\n"); + Sys_Printf ("If you are in X, try using a GLX or SGL target.\n"); + } + Sys_Error ("Couldn't load critical OpenGL function %s, exiting...", + name); + } + return NULL; +} + +static void +sdlgl_set_vid_mode (gl_ctx_t *ctx) +{ + int i, j; + + sdl_flags |= SDL_OPENGL; + + // Setup GL Attributes + SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1); +// SDL_GL_SetAttribute (SDL_GL_STENCIL_SIZE, 0); // Try for 0, 8 +// SDL_GL_SetAttribute (SDL_GL_STEREO, 1); // Someday... + + for (i = 0; i < 5; i++) { + int k; + int color[5] = {32, 24, 16, 15, 0}; + int rgba[5][4] = { + {8, 8, 8, 0}, + {8, 8, 8, 8}, + {5, 6, 5, 0}, + {5, 5, 5, 0}, + {5, 5, 5, 1}, + }; + + SDL_GL_SetAttribute (SDL_GL_RED_SIZE, rgba[i][0]); + SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, rgba[i][1]); + SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, rgba[i][2]); + SDL_GL_SetAttribute (SDL_GL_ALPHA_SIZE, rgba[i][3]); + + for (j = 0; j < 5; j++) { + for (k = 32; k >= 16; k -= 8) { + SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, k); + if ((sdl_screen = SDL_SetVideoMode (viddef.width, viddef.height, + color[j], sdl_flags))) + goto success; + } + } + } + + Sys_Error ("Couldn't set video mode: %s", SDL_GetError ()); + SDL_Quit (); + +success: + viddef.numpages = 2; + + ctx->init_gl (); +} + +static void +sdlgl_end_rendering (void) +{ + qfglFinish (); + SDL_GL_SwapBuffers (); +} + +static void +sdl_load_gl (void) +{ + if (SDL_GL_LoadLibrary (gl_driver) != 0) + Sys_Error ("Couldn't load OpenGL library %s!", gl_driver); + + use_gl_procaddress = 1; + + qfglFinish = QFGL_ProcAddress ("glFinish", true); +} + +gl_ctx_t * +SDL_GL_Context (void) +{ + gl_ctx_t *ctx = calloc (1, sizeof (gl_ctx_t)); + ctx->load_gl = sdl_load_gl; + ctx->create_context = sdlgl_set_vid_mode; + ctx->get_proc_address = QFGL_ProcAddress; + ctx->end_rendering = sdlgl_end_rendering; + return ctx; +} + +void +SDL_GL_Init_Cvars () +{ + Cvar_Register (&gl_driver_cvar, 0, 0); +} diff --git a/libs/video/targets/vid_sdl_sw.c b/libs/video/targets/vid_sdl_sw.c new file mode 100644 index 000000000..9fa138aac --- /dev/null +++ b/libs/video/targets/vid_sdl_sw.c @@ -0,0 +1,175 @@ +/* + vid_sdl.c + + Video driver for Sam Lantinga's Simple DirectMedia Layer + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include + +#include "QF/console.h" +#include "QF/cvar.h" +#include "QF/qendian.h" +#include "QF/sys.h" +#include "QF/vid.h" + +#include "context_sdl.h" +#include "d_iface.h" +#include "vid_internal.h" +#include "vid_sw.h" + +#ifdef _WIN32 // FIXME: evil hack to get full DirectSound support with SDL +#include +#include +#endif + +// The original defaults +#define BASEWIDTH 320 +#define BASEHEIGHT 200 + +// Define GLAPIENTRY to a useful value +#ifndef GLAPIENTRY +# ifdef _WIN32 +# include +# define GLAPIENTRY WINAPI +# undef LoadImage +# else +# ifdef APIENTRY +# define GLAPIENTRY APIENTRY +# else +# define GLAPIENTRY +# endif +# endif +#endif + +static byte cached_palette[256 * 3]; +static int update_palette; + +static void +sdl_update_palette (const byte *palette) +{ + SDL_Color colors[256]; + int i; + + for (i = 0; i < 256; ++i) { + colors[i].r = *palette++; + colors[i].g = *palette++; + colors[i].b = *palette++; + } + SDL_SetColors (sdl_screen, colors, 0, 256); +} + +static void +sdl_set_palette (const byte *palette) +{ + if (memcmp (cached_palette, palette, sizeof (cached_palette))) { + memcpy (cached_palette, palette, sizeof (cached_palette)); + update_palette = 1; + } +} + +static void +sdl_init_buffers (void) +{ + viddef.buffer = sdl_screen->pixels; + viddef.rowbytes = sdl_screen->pitch; + viddef.conbuffer = viddef.buffer; + viddef.conrowbytes = viddef.rowbytes; + viddef.direct = 0; +} + +static void +sdl_create_context (sw_ctx_t *ctx) +{ + // Initialize display + if (!(sdl_screen = SDL_SetVideoMode (viddef.width, viddef.height, 8, + sdl_flags))) + Sys_Error ("VID: Couldn't set video mode: %s", SDL_GetError ()); + + // now know everything we need to know about the buffer + viddef.vid_internal->init_buffers = sdl_init_buffers; +} + +static void +sdl_sw_update (vrect_t *rects) +{ + static SDL_Rect *sdlrects; + static int num_sdlrects; + int i, n; + vrect_t *rect; + + if (update_palette) { + update_palette = 0; + sdl_update_palette (cached_palette); + } + // Two-pass system, since Quake doesn't do it the SDL way... + + // First, count the number of rectangles + n = 0; + for (rect = rects; rect; rect = rect->next) + ++n; + + if (n > num_sdlrects) { + num_sdlrects = n; + sdlrects = realloc (sdlrects, n * sizeof (SDL_Rect)); + if (!sdlrects) + Sys_Error ("Out of memory!"); + } + + // Second, copy them to SDL rectangles and update + i = 0; + for (rect = rects; rect; rect = rect->next) { + sdlrects[i].x = rect->x; + sdlrects[i].y = rect->y; + sdlrects[i].w = rect->width; + sdlrects[i].h = rect->height; + ++i; + } + SDL_UpdateRects (sdl_screen, n, sdlrects); +} + +sw_ctx_t * +SDL_SW_Context (void) +{ + sw_ctx_t *ctx = calloc (1, sizeof (sw_ctx_t)); + ctx->set_palette = sdl_set_palette; + ctx->create_context = sdl_create_context; + ctx->update = sdl_sw_update; + return ctx; +} + +void +SDL_SW_Init_Cvars () +{ +} diff --git a/libs/video/targets/vid_svgalib.c b/libs/video/targets/vid_svgalib.c index 8849a4eb5..cd035228a 100644 --- a/libs/video/targets/vid_svgalib.c +++ b/libs/video/targets/vid_svgalib.c @@ -72,8 +72,24 @@ static byte *framebuffer_ptr; static int svgalib_inited = 0; static int svgalib_backgrounded = 0; -static cvar_t *vid_redrawfull; -static cvar_t *vid_waitforrefresh; +static int vid_redrawfull; +static cvar_t vid_redrawfull_cvar = { + .name = "vid_redrawfull", + .description = + "Redraw entire screen each frame instead of just dirty areas", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &vid_redrawfull }, +}; +static int vid_waitforrefresh; +static cvar_t vid_waitforrefresh_cvar = { + .name = "vid_waitforrefresh", + .description = + "Wait for vertical retrace before drawing next frame", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &vid_waitforrefresh }, +}; int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes, VGA_planar; byte *VGA_pagebase; @@ -239,10 +255,10 @@ get_mode (int width, int height, int depth) return i; } -void -VID_Shutdown (void) +static void +VID_shutdown (void) { - Sys_MaskPrintf (SYS_VID, "VID_Shutdown\n"); + Sys_MaskPrintf (SYS_vid, "VID_Shutdown\n"); if (!svgalib_inited) return; @@ -358,6 +374,8 @@ VID_Init (byte *palette, byte *colormap) if (svgalib_inited) return; + Sys_RegisterShutdown (VID_shutdown); + err = vga_init (); if (err) Sys_Error ("SVGALib failed to allocate a new VC"); @@ -374,7 +392,7 @@ VID_Init (byte *palette, byte *colormap) VID_InitModes (); /* Interpret command-line params */ - VID_GetWindowSize (320, 200); + VID_GetWindowSize (640, 480); current_mode = get_mode (vid.width, vid.height, 8); @@ -391,14 +409,9 @@ VID_Init (byte *palette, byte *colormap) void VID_Init_Cvars () { - vid_redrawfull = Cvar_Get ("vid_redrawfull", "0", CVAR_NONE, NULL, - "Redraw entire screen each frame instead of " - "just dirty areas"); - vid_waitforrefresh = Cvar_Get ("vid_waitforrefresh", "0", CVAR_ARCHIVE, - NULL, "Wait for vertical retrace before " - "drawing next frame"); - vid_system_gamma = Cvar_Get ("vid_system_gamma", "1", CVAR_ARCHIVE, NULL, - "Use system gamma control if available"); + Cvar_Register (&vid_redrawfull_cvar, 0, 0); + Cvar_Register (&vid_waitforrefresh_cvar, 0, 0); + Cvar_Register (&vid_system_gamma_cvar, 0, 0); } void @@ -412,13 +425,13 @@ VID_Update (vrect_t *rects) return; } - if (vid_waitforrefresh->int_val) { + if (vid_waitforrefresh) { vga_waitretrace (); } if (VGA_planar) { VGA_UpdatePlanarScreen (vid.buffer); - } else if (vid_redrawfull->int_val) { + } else if (vid_redrawfull) { int total = vid.rowbytes * vid.height; int offset; @@ -460,22 +473,12 @@ VID_Update (vrect_t *rects) } } -void -VID_LockBuffer (void) -{ -} - -void -VID_UnlockBuffer (void) -{ -} - void VID_SetCaption (const char *text) { } -qboolean +bool VID_SetGamma (double gamma) { return false; diff --git a/libs/video/targets/vid_wgl.c b/libs/video/targets/vid_wgl.c deleted file mode 100644 index bec07fe19..000000000 --- a/libs/video/targets/vid_wgl.c +++ /dev/null @@ -1,637 +0,0 @@ -/* - vid_wgl.c - - Win32 WGL vid component - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "winquake.h" - -#include "QF/cdaudio.h" -#include "QF/cmd.h" -#include "QF/console.h" -#include "QF/cvar.h" -#include "QF/input.h" -#include "QF/keys.h" -#include "QF/qargs.h" -#include "QF/qendian.h" -#include "QF/quakefs.h" -#include "QF/screen.h" -#include "QF/sound.h" -#include "QF/sys.h" -#include "QF/va.h" -#include "QF/vid.h" -#include "QF/GL/funcs.h" -#include "QF/GL/qf_vid.h" - -#include "compat.h" -#include "in_win.h" -#include "r_cvar.h" -#include "win32/resources/resource.h" -#include "sbar.h" - -extern const char *gl_renderer; - -HWND mainwindow; -qboolean win_canalttab = false; -modestate_t modestate = MS_UNINIT; -RECT window_rect; -DEVMODE win_gdevmode; -int window_center_x, window_center_y, window_x, window_y, window_width, - window_height; - -static HGLRC (GLAPIENTRY *qf_wglCreateContext) (HDC); -static BOOL (GLAPIENTRY *qf_wglDeleteContext) (HGLRC); -static HGLRC (GLAPIENTRY *qf_wglGetCurrentContext) (void); -static HDC (GLAPIENTRY *qf_wglGetCurrentDC) (void); -static BOOL (GLAPIENTRY *qf_wglMakeCurrent) (HDC, HGLRC); - -#define MAX_MODE_LIST 30 -#define VID_ROW_SIZE 3 -#define WARP_WIDTH 320 -#define WARP_HEIGHT 200 -#define MAXWIDTH 10000 -#define MAXHEIGHT 10000 -#define BASEWIDTH 320 -#define BASEHEIGHT 200 - -#define MODE_WINDOWED 0 -#define NO_MODE (MODE_WINDOWED - 1) -#define MODE_FULLSCREEN_DEFAULT (MODE_WINDOWED + 1) - -static int windowed_mouse; - -static HICON hIcon; -static RECT WindowRect; -static DWORD WindowStyle; - -static qboolean fullsbardraw = true; - -static HDC maindc; - - -static void GL_Init (void); -static void * (WINAPI *glGetProcAddress) (const char *symbol) = NULL; - - -void * -QFGL_GetProcAddress (void *handle, const char *name) -{ - void *glfunc = NULL; - - if (glGetProcAddress) - glfunc = glGetProcAddress (name); - if (!glfunc) - glfunc = GetProcAddress (handle, name); - return glfunc; -} - -void * -QFGL_LoadLibrary (void) -{ - void *handle; - - if (!(handle = LoadLibrary (gl_driver->string))) - Sys_Error ("Couldn't load OpenGL library %s!", gl_driver->string); - glGetProcAddress = (void *(WINAPI *)(const char*)) GetProcAddress (handle, "wglGetProcAddress"); - return handle; -} - -//==================================== - -static void -CenterWindow (HWND hWndCenter, int width, int height, BOOL lefttopjustify) -{ - int CenterX, CenterY; - - CenterX = (GetSystemMetrics (SM_CXSCREEN) - width) / 2; - CenterY = (GetSystemMetrics (SM_CYSCREEN) - height) / 2; - if (CenterX > CenterY * 2) - CenterX >>= 1; // dual screens - CenterX = (CenterX < 0) ? 0 : CenterX; - CenterY = (CenterY < 0) ? 0 : CenterY; - SetWindowPos (hWndCenter, NULL, CenterX, CenterY, 0, 0, - SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); -} - -static qboolean -VID_SetWindowedMode ( void ) -{ - HDC hdc; - int width, height; - RECT rect; - - WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | - WS_MINIMIZEBOX | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; - - rect = WindowRect; - AdjustWindowRect (&rect, WindowStyle, FALSE); - - width = rect.right - rect.left; - height = rect.bottom - rect.top; - - // Create the window - mainwindow = CreateWindow ("QuakeForge", PACKAGE_NAME, - WindowStyle, rect.left, rect.top, width, height, - NULL, NULL, global_hInstance, NULL); - - if (!mainwindow) - Sys_Error ("Couldn't create DIB window (%lx)", GetLastError ()); - - // Center and show the window - CenterWindow (mainwindow, WindowRect.right - WindowRect.left, - WindowRect.bottom - WindowRect.top, false); - - ShowWindow (mainwindow, SW_SHOWDEFAULT); - UpdateWindow (mainwindow); - - modestate = MS_WINDOWED; - - // because we have set the background brush for the window to NULL - // (to avoid flickering when re-sizing the window on the desktop), - // we clear the window to black when created, otherwise it will be - // empty while Quake starts up. - hdc = GetDC (mainwindow); - PatBlt (hdc, 0, 0, WindowRect.right, WindowRect.bottom, BLACKNESS); - ReleaseDC (mainwindow, hdc); - - vid.numpages = 2; - - if(hIcon) { - SendMessage (mainwindow, WM_SETICON, (WPARAM) TRUE, (LPARAM) hIcon); - SendMessage (mainwindow, WM_SETICON, (WPARAM) FALSE, (LPARAM) hIcon); - } - - return true; -} - -static qboolean -VID_SetFullDIBMode ( void ) -{ - HDC hdc; - int width, height; - RECT rect; - - if (ChangeDisplaySettings (&win_gdevmode, CDS_FULLSCREEN) - != DISP_CHANGE_SUCCESSFUL) - Sys_Error ("Couldn't set fullscreen DIB mode (%lx)", GetLastError()); - - // FIXME: some drivers have broken FS popup window handling until I find a - // way around it, or find some other cause for it this is a way to avoid it - if (COM_CheckParm ("-brokenpopup")) - WindowStyle = 0; - else - WindowStyle = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; - - rect = WindowRect; - AdjustWindowRect (&rect, WindowStyle, FALSE); - - width = rect.right - rect.left; - height = rect.bottom - rect.top; - - // Create the DIB window - mainwindow = CreateWindow ("QuakeForge", "GLQuakeWorld", WindowStyle, - rect.left, rect.top, width, height, NULL, NULL, - global_hInstance, NULL); - - if (!mainwindow) - Sys_Error ("Couldn't create DIB window (%lx)",GetLastError()); - - ShowWindow (mainwindow, SW_SHOWDEFAULT); - UpdateWindow (mainwindow); - - // Because we have set the background brush for the window to NULL - // (to avoid flickering when re-sizing the window on the desktop), we - // clear the window to black when created, otherwise it will be empty - // while Quake starts up. - hdc = GetDC (mainwindow); - PatBlt (hdc, 0, 0, WindowRect.right, WindowRect.bottom, BLACKNESS); - ReleaseDC (mainwindow, hdc); - - vid.numpages = 2; - - // needed because we're not getting WM_MOVE messages fullscreen on NT - window_x = 0; - window_y = 0; - - if (hIcon) { - SendMessage (mainwindow, WM_SETICON, (WPARAM) TRUE, (LPARAM) hIcon); - SendMessage (mainwindow, WM_SETICON, (WPARAM) FALSE, (LPARAM) hIcon); - } - - return true; -} - -static int -VID_SetMode (unsigned char *palette) -{ - qboolean stat = 0; - MSG msg; - - // so Con_Printfs don't mess us up by forcing vid and snd updates - CDAudio_Pause (); - - WindowRect.top = WindowRect.left = 0; - - WindowRect.right = vid_width->int_val; - WindowRect.bottom = vid_height->int_val; - - window_width = vid_width->int_val; - window_height = vid_height->int_val; - - // Set either the fullscreen or windowed mode - if (!vid_fullscreen->int_val) { - if (in_grab->int_val && key_dest == key_game) { - stat = VID_SetWindowedMode (); - IN_ActivateMouse (); - IN_HideMouse (); - } else { - IN_DeactivateMouse (); - IN_ShowMouse (); - stat = VID_SetWindowedMode (); - } - } else { - stat = VID_SetFullDIBMode (); - IN_ActivateMouse (); - IN_HideMouse (); - } - - VID_UpdateWindowStatus (window_x, window_y); - - CDAudio_Resume (); - - if (!stat) { - Sys_Error ("Couldn't set video mode"); - } - - // Now we try to make sure we get the focus on the mode switch, because - // sometimes in some systems we don't. We grab the foreground, then - // finish setting up, pump all our messages, and sleep for a little while - // to let messages finish bouncing around the system, then we put - // ourselves at the top of the z order, then grab the foreground again, - // Who knows if it helps, but it probably doesn't hurt - SetForegroundWindow (mainwindow); - VID_SetPalette (palette); - - while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) { - TranslateMessage (&msg); - DispatchMessage (&msg); - } - - Sleep (100); - - SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, - SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | - SWP_NOCOPYBITS); - - SetForegroundWindow (mainwindow); - - // fix the leftover Alt from any Alt-Tab or the like that switched us away - IN_ClearStates (); - - Sys_Printf ("Video mode %ix%i initialized.\n", vid_width->int_val, - vid_height->int_val); - - VID_SetPalette (palette); - - vid.recalc_refdef = 1; - - return true; -} - -void -VID_UpdateWindowStatus (int w_x, int w_y) -{ - window_rect.left = window_x = w_x; - window_rect.top = window_y = w_y; - window_rect.right = window_x + window_width; - window_rect.bottom = window_y + window_height; - window_center_x = (window_rect.left + window_rect.right) / 2; - window_center_y = (window_rect.top + window_rect.bottom) / 2; - - IN_UpdateClipCursor (); -} - -static void -GL_Init (void) -{ - GL_Init_Common (); - if (strnicmp (gl_renderer, "PowerVR", 7) == 0) - fullsbardraw = true; -} - -void -GL_EndRendering (void) -{ - if (!scr_skipupdate) { - qfglFinish (); - SwapBuffers (maindc); - } - - // handle the mouse state when windowed if that's changed - if (!vid_fullscreen->int_val) { - if (!in_grab->int_val) { - if (windowed_mouse) { - IN_DeactivateMouse (); - IN_ShowMouse (); - windowed_mouse = false; - } - } else { - windowed_mouse = true; - if (key_dest == key_game && !in_mouse_avail && ActiveApp) { - IN_ActivateMouse (); - IN_HideMouse (); - } else if (in_mouse_avail && key_dest != key_game) { - IN_DeactivateMouse (); - IN_ShowMouse (); - } - } - } -} - -void -VID_Shutdown (void) -{ - HGLRC hRC; - HDC hDC; - int i; - GLuint temp[8192]; - -#ifdef SPLASH_SCREEN - if(hwnd_dialog) - DestroyWindow (hwnd_dialog); -#endif - - if (vid.initialized) { - win_canalttab = false; - hRC = qf_wglGetCurrentContext (); - hDC = qf_wglGetCurrentDC (); - - qf_wglMakeCurrent (NULL, NULL); - - // LordHavoc: free textures before closing (may help NVIDIA) - for (i = 0; i < 8192; i++) - temp[i] = i + 1; - qfglDeleteTextures (8192, temp); - - if (hRC) - qf_wglDeleteContext (hRC); - - if (hDC && mainwindow) - ReleaseDC (mainwindow, hDC); - - if (vid_fullscreen->int_val) - ChangeDisplaySettings (NULL, 0); - - if (maindc && mainwindow) - ReleaseDC (mainwindow, maindc); - - AppActivate (false, false); - } -} - -//========================================================================== - -static BOOL -bSetupPixelFormat (HDC hDC) -{ - PIXELFORMATDESCRIPTOR pfd ; - int pixelformat; - - memset (&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); - pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); - pfd.nVersion = 1; - pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW; - pfd.iPixelType = PFD_TYPE_RGBA; - - pfd.cColorBits = win_gdevmode.dmBitsPerPel ; - - pfd.cDepthBits = 32; - pfd.iLayerType = PFD_MAIN_PLANE; - - if ((pixelformat = ChoosePixelFormat (hDC, &pfd)) == 0) { - MessageBox (NULL, "ChoosePixelFormat failed", "Error", MB_OK); - return FALSE; - } - - if (SetPixelFormat (hDC, pixelformat, &pfd) == FALSE) { - MessageBox (NULL, "SetPixelFormat failed", "Error", MB_OK); - return FALSE; - } - return TRUE; -} - -void -VID_Init (byte *palette, byte *colormap) -{ - BOOL stat; - WORD bpp, vid_mode; - HDC hdc; - HGLRC baseRC; - DWORD lasterror; - WNDCLASS wc; - - vid_fullscreen = Cvar_Get ("vid_fullscreen", "0", CVAR_ROM | CVAR_ARCHIVE, - NULL, "Run WGL client at fullscreen"); - GLF_Init (); - - qf_wglCreateContext = QFGL_ProcAddress (libgl_handle, "wglCreateContext", - true); - qf_wglDeleteContext = QFGL_ProcAddress (libgl_handle, "wglDeleteContext", - true); - qf_wglGetCurrentContext = QFGL_ProcAddress (libgl_handle, - "wglGetCurrentContext", true); - qf_wglGetCurrentDC = QFGL_ProcAddress (libgl_handle, "wglGetCurrentDC", - true); - qf_wglMakeCurrent = QFGL_ProcAddress (libgl_handle, "wglMakeCurrent", - true); - - memset (&win_gdevmode, 0, sizeof (win_gdevmode)); - - hIcon = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON1)); - - - // Register the frame class - wc.style = 0; - wc.lpfnWndProc = (WNDPROC) MainWndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hInstance = global_hInstance; - wc.hIcon = 0; - wc.hCursor = LoadCursor (NULL, IDC_ARROW); - wc.hbrBackground = NULL; - wc.lpszMenuName = 0; - wc.lpszClassName = "QuakeForge"; - - if (!RegisterClass (&wc)) - Sys_Error ("Couldn't register window class (%lx)", GetLastError ()); - - VID_GetWindowSize (640, 480); - - if (!vid_fullscreen->int_val) { - hdc = GetDC (NULL); - - if (GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) { - Sys_Error ("Can't run in non-RGB mode"); - } - - ReleaseDC (NULL, hdc); - } else { - if (COM_CheckParm ("-bpp")) { - bpp = atoi (com_argv[COM_CheckParm ("-bpp") + 1]); - } else { - bpp = 16; - } - vid_mode = 0; - do { - stat = EnumDisplaySettings (NULL, vid_mode, &win_gdevmode); - - if ((win_gdevmode.dmBitsPerPel == bpp) - && (win_gdevmode.dmPelsWidth == vid.width) - && (win_gdevmode.dmPelsHeight == vid.height)) { - win_gdevmode.dmFields = (DM_BITSPERPEL | DM_PELSWIDTH - | DM_PELSHEIGHT); - - if (ChangeDisplaySettings (&win_gdevmode, - CDS_TEST | CDS_FULLSCREEN) - == DISP_CHANGE_SUCCESSFUL) { - break; - } - } - - vid_mode++; - } while (stat); - if (!stat) - Sys_Error ("Couldn't get requested resolution (%i, %i, %i)", - vid_width->int_val, vid_height->int_val, bpp); - } - - vid.maxwarpwidth = WARP_WIDTH; - vid.maxwarpheight = WARP_HEIGHT; - vid.colormap8 = vid_colormap = colormap; - vid.fullbright = 256 - vid.colormap8[256 * VID_GRADES]; - -#ifdef SPLASH_SCREEN - if(hwnd_dialog) - DestroyWindow (hwnd_dialog); -#endif - - VID_SetMode (palette); - - maindc = GetDC (mainwindow); - bSetupPixelFormat (maindc); - - baseRC = qf_wglCreateContext (maindc); - if (!baseRC) { - lasterror=GetLastError(); - if (maindc && mainwindow) - ReleaseDC (mainwindow, maindc); - Sys_Error ("Could not initialize GL (wglCreateContext failed).\n\n" - "Make sure you are in 65535 color mode, and try running " - "with -window.\n" - "Error code: (%lx)", lasterror); - } - - if (!qf_wglMakeCurrent (maindc, baseRC)) { - lasterror = GetLastError (); - if (baseRC) - qf_wglDeleteContext (baseRC); - if (maindc && mainwindow) - ReleaseDC (mainwindow, maindc); - Sys_Error ("wglMakeCurrent failed (%lx)", lasterror); - } - - GL_Init (); - - VID_InitGamma (palette); - VID_Init8bitPalette (); - VID_SetPalette (vid.palette); - - vid.initialized = true; - - win_canalttab = true; - - if (COM_CheckParm ("-nofullsbar")) - fullsbardraw = false; -} - -void -VID_Init_Cvars () -{ -} - -void -VID_SetCaption (const char *text) -{ - if (text && *text) { - char *temp = strdup (text); - - SetWindowText (mainwindow, - (LPSTR) va ("%s: %s", PACKAGE_STRING, temp)); - free (temp); - } else { - SetWindowText (mainwindow, (LPSTR) va ("%s", PACKAGE_STRING)); - } -} - -//static WORD systemgammaramps[3][256]; -static WORD currentgammaramps[3][256]; - -qboolean -VID_SetGamma (double gamma) -{ - int i; - HDC hdc = GetDC (NULL); - - for (i = 0; i < 256; i++) { - currentgammaramps[2][i] = currentgammaramps[1][i] = - currentgammaramps[0][i] = gammatable[i] * 256; - } - - i = SetDeviceGammaRamp (hdc, ¤tgammaramps[0][0]); - ReleaseDC (NULL, hdc); - return i; -} - -#if 0 -static void -VID_SaveGamma (void) -{ - HDC hdc = GetDC (NULL); - - GetDeviceGammaRamp (hdc, &systemgammaramps[0][0]); - ReleaseDC (NULL, hdc); -} - -static void -VID_RestoreGamma (void) -{ - HDC hdc = GetDC (NULL); - - SetDeviceGammaRamp (hdc, &systemgammaramps[0][0]); - ReleaseDC (NULL, hdc); -} -#endif diff --git a/libs/video/targets/vid_win.c b/libs/video/targets/vid_win.c new file mode 100644 index 000000000..ba530fc75 --- /dev/null +++ b/libs/video/targets/vid_win.c @@ -0,0 +1,292 @@ +/* + vid_win.c + + Win32 vid component + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/sys.h" +#include "QF/va.h" +#include "QF/vid.h" + +#include "context_win.h" +#include "d_iface.h" +#include "vid_internal.h" +#include "vid_sw.h" + +static vid_internal_t vid_internal; + +#if 0 +static byte backingbuf[48 * 24]; +#endif + +void +D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ +#if 0 + int i, j, reps = 1, repshift = 0; + vrect_t rect; + + if (!viddef.initialized || !win_sw_context) + return; + + if (!viddef.buffer) + return; + + for (i = 0; i < (height << repshift); i += reps) { + for (j = 0; j < reps; j++) { + memcpy (&backingbuf[(i + j) * 24], + viddef.buffer + x + ((y << repshift) + i + + j) * viddef.rowbytes, width); + + memcpy (viddef.buffer + x + + ((y << repshift) + i + j) * viddef.rowbytes, + &pbitmap[(i >> repshift) * width], width); + } + } + + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height << repshift; + rect.next = NULL; + + win_sw_context->update (win_sw_context, &rect); +#endif +} + + +void +D_EndDirectRect (int x, int y, int width, int height) +{ +#if 0 + int i, j, reps = 1, repshift = 0; + vrect_t rect; + + if (!viddef.initialized || !win_sw_context) + return; + + if (!viddef.buffer) + return; + + for (i = 0; i < (height << repshift); i += reps) { + for (j = 0; j < reps; j++) { + memcpy (viddef.buffer + x + + ((y << repshift) + i + j) * viddef.rowbytes, + &backingbuf[(i + j) * 24], width); + } + } + + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height << repshift; + rect.next = NULL; + + win_sw_context->update (win_sw_context, &rect); +#endif +} + +static void +VID_shutdown (void *data) +{ + Sys_MaskPrintf (SYS_vid, "VID_shutdown\n"); + Win_CloseDisplay (); +} + +static void +Win_VID_SetPalette (byte *palette, byte *colormap) +{ + viddef.colormap8 = colormap; + viddef.fullbright = 256 - viddef.colormap8[256 * VID_GRADES]; + if (vid_internal.set_colormap) { + vid_internal.set_colormap (vid_internal.ctx, colormap); + } + + VID_InitGamma (palette); + viddef.vid_internal->set_palette (win_sw_context, palette); +} + +static void +Win_VID_Init (byte *palette, byte *colormap) +{ + Sys_RegisterShutdown (VID_shutdown, 0); + + vid_internal.gl_context = Win_GL_Context; + vid_internal.sw_context = Win_SW_Context; +#ifdef HAVE_VULKAN + vid_internal.vulkan_context = Win_Vulkan_Context; +#endif + + R_LoadModule (&vid_internal); + + viddef.numpages = 1; + + VID_GetWindowSize (640, 480); + Win_OpenDisplay (); + vid_internal.choose_visual (win_sw_context); + Win_SetVidMode (viddef.width, viddef.height); + Win_CreateWindow (viddef.width, viddef.height); + vid_internal.create_context (win_sw_context); + + Win_VID_SetPalette (palette, colormap); + + Sys_MaskPrintf (SYS_vid, "Video mode %dx%d initialized.\n", + viddef.width, viddef.height); + + viddef.initialized = true; +} + +static void +Win_VID_Init_Cvars (void) +{ + Win_Init_Cvars (); +#ifdef HAVE_VULKAN + Win_Vulkan_Init_Cvars (); +#endif + Win_GL_Init_Cvars (); + Win_SW_Init_Cvars (); +} + +vid_system_t vid_system = { + .init = Win_VID_Init, + .set_palette = Win_VID_SetPalette, + .init_cvars = Win_VID_Init_Cvars, +}; + +void +VID_SetCaption (const char *text) +{ + if (text && *text) { + char *temp = strdup (text); + + Win_SetCaption (va (0, "%s: %s", PACKAGE_STRING, temp)); + free (temp); + } else { + Win_SetCaption (va (0, "%s", PACKAGE_STRING)); + } +} + +bool +VID_SetGamma (double gamma) +{ + return Win_SetGamma (gamma); +} + + +#if 0 +void +VID_Update (vrect_t *rects) +{ + vrect_t rect; + RECT trect; + + if (firstupdate) { + if (modestate == MS_WINDOWED) { + GetWindowRect (win_mainwindow, &trect); + + if ((trect.left != vid_window_x) || + (trect.top != vid_window_y)) { + if (COM_CheckParm ("-resetwinpos")) { + vid_window_x = 0.0; + vid_window_y = 0.0; + } + + VID_CheckWindowXY (); + SetWindowPos (win_mainwindow, NULL, vid_window_x, + vid_window_y, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | + SWP_DRAWFRAME); + } + } + + if ((_vid_default_mode_win != vid_default) && + (!startwindowed + || (_vid_default_mode_win < MODE_FULLSCREEN_DEFAULT))) { + firstupdate = 0; + + if (COM_CheckParm ("-resetwinpos")) { + vid_window_x = 0.0; + vid_window_y = 0.0; + } + + if ((_vid_default_mode_win < 0) || + (_vid_default_mode_win >= nummodes)) { + _vid_default_mode_win = windowed_default; + } + + vid_mode = _vid_default_mode_win; + } + } + // We've drawn the frame; copy it to the screen + FlipScreen (rects); + + // check for a driver change + if ((vid_ddraw && !vid_usingddraw) + || (!vid_ddraw && vid_usingddraw)) { + // reset the mode + force_mode_set = true; + VID_SetMode (vid_mode, vid_curpal); + force_mode_set = false; + + // store back + if (vid_usingddraw) + Sys_Printf ("loaded DirectDraw driver\n"); + else + Sys_Printf ("loaded GDI driver\n"); + } + + if (vid_testingmode) { + if (Sys_DoubleTime () >= vid_testendtime) { + VID_SetMode (vid_realmode, vid_curpal); + vid_testingmode = 0; + } + } else { + if (vid_mode != vid_realmode) { + VID_SetMode (vid_mode, vid_curpal); + vid_mode = vid_modenum; + // so if mode set fails, we don't keep on + // trying to set that mode + vid_realmode = vid_modenum; + } + } + + // handle the mouse state when windowed if that's changed + if (modestate == MS_WINDOWED) { + if (_windowed_mouse != windowed_mouse) { + if (_windowed_mouse) { + IN_ActivateMouse (); + IN_HideMouse (); + } else { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + + windowed_mouse = _windowed_mouse; + } + } +} +#endif diff --git a/libs/video/targets/vid_win_gl.c b/libs/video/targets/vid_win_gl.c new file mode 100644 index 000000000..f8c14f31d --- /dev/null +++ b/libs/video/targets/vid_win_gl.c @@ -0,0 +1,218 @@ +/* + vid_win_gl.c + + Win32 GL vid component + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "winquake.h" +#include + +#include "QF/cvar.h" +#include "QF/sys.h" +#include "QF/vid.h" + +#include "context_win.h" +#include "r_internal.h" +#include "vid_internal.h" +#include "vid_gl.h" + +// Define GLAPIENTRY to a useful value +#ifndef GLAPIENTRY +# define GLAPIENTRY WINAPI +#endif +static void *libgl_handle; +static HGLRC (GLAPIENTRY * qfwglCreateContext) (HDC); +static BOOL (GLAPIENTRY * qfwglDeleteContext) (HGLRC); +static HGLRC (GLAPIENTRY * qfwglGetCurrentContext) (void); +static HDC (GLAPIENTRY * qfwglGetCurrentDC) (void); +static BOOL (GLAPIENTRY * qfwglMakeCurrent) (HDC, HGLRC); +static void (GLAPIENTRY *qfglFinish) (void); +static void *(WINAPI * glGetProcAddress) (const char *symbol) = NULL; +static int use_gl_proceaddress = 0; + +static char *gl_driver; +static cvar_t gl_driver_cvar = { + .name = "gl_driver", + .description = + "The OpenGL library to use. (path optional)", + .default_value = GL_DRIVER, + .flags = CVAR_ROM, + .value = { .type = 0, .value = &gl_driver }, +}; +static HGLRC baseRC;//FIXME should be in gl_ctx_t, but that's GLXContext... +static void * +QFGL_GetProcAddress (void *handle, const char *name) +{ + void *glfunc = NULL; + + if (use_gl_proceaddress && glGetProcAddress) + glfunc = glGetProcAddress (name); + if (!glfunc) + glfunc = GetProcAddress (handle, name); + return glfunc; +} + +static void * +QFGL_ProcAddress (const char *name, bool crit) +{ + void *glfunc = NULL; + + Sys_MaskPrintf (SYS_vid, "DEBUG: Finding symbol %s ... ", name); + + glfunc = QFGL_GetProcAddress (libgl_handle, name); + if (glfunc) { + Sys_MaskPrintf (SYS_vid, "found [%p]\n", glfunc); + return glfunc; + } + Sys_MaskPrintf (SYS_vid, "not found\n"); + + if (crit) { + Sys_Error ("Couldn't load critical OpenGL function %s, exiting...", + name); + } + return NULL; +} + +static void +wgl_choose_visual (gl_ctx_t *ctx) +{ +} + +static void +wgl_set_pixel_format (void) +{ + int pixelformat; + PIXELFORMATDESCRIPTOR pfd = { + .nSize = sizeof(PIXELFORMATDESCRIPTOR), + .nVersion = 1, + .dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW, + .iPixelType = PFD_TYPE_RGBA, + .cColorBits = win_gdevmode.dmBitsPerPel, + .cDepthBits = 32, + .iLayerType = PFD_MAIN_PLANE, + }; + + if (!(pixelformat = ChoosePixelFormat (win_maindc, &pfd))) { + Sys_Error ("ChoosePixelFormat failed"); + } + if (!SetPixelFormat (win_maindc, pixelformat, &pfd)) { + Sys_Error ("SetPixelFormat failed"); + } +} + +static void +wgl_create_context (gl_ctx_t *ctx, int core) +{ + DWORD lasterror; + + win_maindc = GetDC (win_mainwindow); + + wgl_set_pixel_format (); + baseRC = qfwglCreateContext (win_maindc); + if (!baseRC) { + lasterror=GetLastError(); + if (win_maindc && win_mainwindow) + ReleaseDC (win_mainwindow, win_maindc); + Sys_Error ("Could not initialize GL (wglCreateContext failed).\n\n" + "Make sure you are in 65535 color mode, and try running " + "with -window.\n" + "Error code: (%lx)", lasterror); + } + + if (!qfwglMakeCurrent (win_maindc, baseRC)) { + lasterror = GetLastError (); + if (baseRC) + qfwglDeleteContext (baseRC); + if (win_maindc && win_mainwindow) + ReleaseDC (win_mainwindow, win_maindc); + Sys_Error ("wglMakeCurrent failed (%lx)", lasterror); + } + + ctx->init_gl (); +} + +static void +wgl_end_rendering (void) +{ + if (!scr_skipupdate) { + qfglFinish (); + SwapBuffers (win_maindc); + } + // handle the mouse state when windowed if that's changed + if (!vid_fullscreen) { +//FIXME if (!in_grab) { +//FIXME if (windowed_mouse) { +//FIXME IN_DeactivateMouse (); +//FIXME IN_ShowMouse (); +//FIXME windowed_mouse = false; +//FIXME } +//FIXME } else { +//FIXME windowed_mouse = true; +//FIXME } + } +} + +static void +wgl_load_gl (gl_ctx_t *ctx) +{ + libgl_handle = LoadLibrary (gl_driver); + if (!libgl_handle) { + Sys_Error ("Couldn't load OpenGL library %s!", gl_driver); + } + glGetProcAddress = + (void *) GetProcAddress (libgl_handle, "wglGetProcAddress"); + + qfwglCreateContext = QFGL_ProcAddress ("wglCreateContext", true); + qfwglDeleteContext = QFGL_ProcAddress ("wglDeleteContext", true); + qfwglGetCurrentContext = QFGL_ProcAddress ("wglGetCurrentContext", true); + qfwglGetCurrentDC = QFGL_ProcAddress ("wglGetCurrentDC", true); + qfwglMakeCurrent = QFGL_ProcAddress ("wglMakeCurrent", true); + + use_gl_proceaddress = 1; + + qfglFinish = QFGL_ProcAddress ("glFinish", true); +} + +gl_ctx_t * +Win_GL_Context (vid_internal_t *vi) +{ + gl_ctx_t *ctx = calloc (1, sizeof (gl_ctx_t)); + ctx->load_gl = wgl_load_gl; + ctx->choose_visual = wgl_choose_visual; + ctx->create_context = wgl_create_context; + ctx->get_proc_address = QFGL_ProcAddress; + ctx->end_rendering = wgl_end_rendering; + + vi->ctx = ctx; + return ctx; +} + +void +Win_GL_Init_Cvars (void) +{ + Cvar_Register (&gl_driver_cvar, 0, 0); +} diff --git a/libs/video/targets/vid_win_sw.c b/libs/video/targets/vid_win_sw.c new file mode 100644 index 000000000..e338d2ede --- /dev/null +++ b/libs/video/targets/vid_win_sw.c @@ -0,0 +1,496 @@ +/* + vid_win.c + + Win32 SW vid component + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "winquake.h" +#include + +#include "QF/cvar.h" +#include "QF/qargs.h" +#include "QF/sys.h" +#include "QF/vid.h" + +#include "context_win.h" +#include "r_internal.h" +#include "vid_internal.h" +#include "vid_sw.h" + +typedef union { + byte bgra[4]; + uint32_t value; +} win_palette_t; + +static win_palette_t st2d_8to32table[256]; +static byte current_palette[768]; +static int palette_changed; + +static LPDIRECTDRAW dd_Object; +static HINSTANCE hInstDDraw; +static LPDIRECTDRAWSURFACE dd_frontbuffer; +static LPDIRECTDRAWSURFACE dd_backbuffer; +static RECT src_rect; +static RECT dst_rect; +static HDC win_gdi; +static HDC dib_section; +static HBITMAP dib_bitmap; +static HGDIOBJ previous_dib; +static int using_ddraw; + +static LPDIRECTDRAWCLIPPER dd_Clipper; + +typedef HRESULT (WINAPI *ddCreateProc_t) (GUID FAR *, + LPDIRECTDRAW FAR *, + IUnknown FAR *); +static ddCreateProc_t ddCreate; + +static int dd_window_width = 640; +static int dd_window_height = 480; + +static sw_framebuffer_t swfb; +static framebuffer_t fb = { .buffer = &swfb }; + +static void +DD_UpdateRects (int width, int height) +{ + POINT p = { .x = 0, .y = 0 }; + // first we need to figure out where on the primary surface our window + // lives + ClientToScreen (win_mainwindow, &p); + GetClientRect (win_mainwindow, &dst_rect); + OffsetRect (&dst_rect, p.x, p.y); + SetRect (&src_rect, 0, 0, width, height); +} + + +static void +VID_CreateDDrawDriver (int width, int height) +{ + DDSURFACEDESC ddsd; + + using_ddraw = false; + dd_window_width = width; + dd_window_height = height; + + fb.width = width; + fb.height = height; + swfb.color = malloc (width * height); + swfb.rowbytes = width; + + if (!(hInstDDraw = LoadLibrary ("ddraw.dll"))) { + return; + } + if (!(ddCreate = (ddCreateProc_t) GetProcAddress (hInstDDraw, + "DirectDrawCreate"))) { + return; + } + + if (FAILED (ddCreate (0, &dd_Object, 0))) { + return; + } + if (FAILED (dd_Object->lpVtbl->SetCooperativeLevel (dd_Object, + win_mainwindow, + DDSCL_NORMAL))) { + return; + } + + // the primary surface in windowed mode is the full screen + memset (&ddsd, 0, sizeof (ddsd)); + ddsd.dwSize = sizeof (ddsd); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_VIDEOMEMORY; + + // ...and create it + if (FAILED (dd_Object->lpVtbl->CreateSurface (dd_Object, &ddsd, + &dd_frontbuffer, 0))) { + return; + } + + // not using a clipper will slow things down and switch aero off + if (FAILED (IDirectDraw_CreateClipper (dd_Object, 0, &dd_Clipper, 0))) { + return; + } + if (FAILED (IDirectDrawClipper_SetHWnd (dd_Clipper, 0, win_mainwindow))) { + return; + } + if (FAILED (IDirectDrawSurface_SetClipper (dd_frontbuffer, + dd_Clipper))) { + return; + } + + // the secondary surface is an offscreen surface that is the currect + // dimensions + // this will be blitted to the correct location on the primary surface + // (which is the full screen) during our draw op + memset (&ddsd, 0, sizeof (ddsd)); + ddsd.dwSize = sizeof (ddsd); + ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; + ddsd.dwWidth = width; + ddsd.dwHeight = height; + + if (FAILED (IDirectDraw_CreateSurface (dd_Object, &ddsd, + &dd_backbuffer, 0))) { + return; + } + + // direct draw is working now + using_ddraw = true; + + // create initial rects + DD_UpdateRects (dd_window_width, dd_window_height); +} + +static void +VID_CreateGDIDriver (int width, int height, const byte *palette, byte **buffer, + int *rowbytes) +{ + // common bitmap definition + typedef struct dibinfo { + BITMAPINFOHEADER header; + RGBQUAD acolors[256]; + } dibinfo_t; + + dibinfo_t dibheader; + BITMAPINFO *pbmiDIB = (BITMAPINFO *) & dibheader; + int i; + byte *dib_base = 0; + + win_gdi = GetDC (win_mainwindow); + memset (&dibheader, 0, sizeof (dibheader)); + + // fill in the bitmap info + pbmiDIB->bmiHeader.biSize = sizeof (BITMAPINFOHEADER); + pbmiDIB->bmiHeader.biWidth = width; + pbmiDIB->bmiHeader.biHeight = height; + pbmiDIB->bmiHeader.biPlanes = 1; + pbmiDIB->bmiHeader.biCompression = BI_RGB; + pbmiDIB->bmiHeader.biSizeImage = 0; + pbmiDIB->bmiHeader.biXPelsPerMeter = 0; + pbmiDIB->bmiHeader.biYPelsPerMeter = 0; + pbmiDIB->bmiHeader.biClrUsed = 256; + pbmiDIB->bmiHeader.biClrImportant = 256; + pbmiDIB->bmiHeader.biBitCount = 8; + + // fill in the palette + for (i = 0; i < 256; i++) { + // d_8to24table isn't filled in yet so this is just for testing + dibheader.acolors[i].rgbRed = palette[i * 3]; + dibheader.acolors[i].rgbGreen = palette[i * 3 + 1]; + dibheader.acolors[i].rgbBlue = palette[i * 3 + 2]; + } + + // create the DIB section + dib_bitmap = CreateDIBSection (win_gdi, + pbmiDIB, + DIB_RGB_COLORS, + (void **) &dib_base, 0, 0); + + // set video buffers + if (pbmiDIB->bmiHeader.biHeight > 0) { + // bottom up + buffer[0] = dib_base + (height - 1) * width; + rowbytes[0] = -width; + } else { + // top down + buffer[0] = dib_base; + rowbytes[0] = width; + } + + // clear the buffer + memset (dib_base, 0xff, width * height); + + if ((dib_section = CreateCompatibleDC (win_gdi)) == 0) + Sys_Error ("DIB_Init() - CreateCompatibleDC failed\n"); + + if ((previous_dib = SelectObject (dib_section, dib_bitmap)) == 0) + Sys_Error ("DIB_Init() - SelectObject failed\n"); + + // create a palette + VID_InitGamma (palette); + viddef.vid_internal->set_palette (viddef.vid_internal->ctx, palette); +} + +void +Win_UnloadAllDrivers (void) +{ + // shut down ddraw + if (swfb.color) { + free (swfb.color); + swfb.color = 0; + } + if (swfb.depth) { + free (swfb.depth); + swfb.depth = 0; + } + + if (dd_Clipper) { + IDirectDrawClipper_Release (dd_Clipper); + dd_Clipper = 0; + } + + if (dd_frontbuffer) { + IDirectDrawSurface_Release (dd_frontbuffer); + dd_frontbuffer = 0; + } + + if (dd_backbuffer) { + IDirectDrawSurface_Release (dd_backbuffer); + dd_backbuffer = 0; + } + + if (dd_Object) { + IDirectDraw_Release (dd_Object); + dd_Object = 0; + } + + if (hInstDDraw) { + FreeLibrary (hInstDDraw); + hInstDDraw = 0; + } + + ddCreate = 0; + + // shut down gdi + if (dib_section) { + SelectObject (dib_section, previous_dib); + DeleteDC (dib_section); + dib_section = 0; + } + + if (dib_bitmap) { + DeleteObject (dib_bitmap); + dib_bitmap = 0; + } + + if (win_gdi) { + // if win_gdi exists then win_mainwindow must also be valid + ReleaseDC (win_mainwindow, win_gdi); + win_gdi = 0; + } + // not using ddraw now + using_ddraw = false; +} + +static void +Win_CreateDriver (void) +{ + if (vid_ddraw) { + VID_CreateDDrawDriver (viddef.width, viddef.height); + } + if (!using_ddraw) { + // directdraw failed or was not requested + // + // if directdraw failed, it may be partially initialized, so make sure + // the slate is clean + Win_UnloadAllDrivers (); + + VID_CreateGDIDriver (viddef.width, viddef.height, viddef.palette, + &swfb.color, &swfb.rowbytes); + } +} + +static void +win_init_bufers (void *data) +{ + sw_ctx_t *ctx = data; + + ctx->framebuffer = &fb; + + Win_UnloadAllDrivers (); + Win_CreateDriver (); +} + +static void +win_set_palette (sw_ctx_t *ctx, const byte *palette) +{ + palette_changed = 1; + if (palette != current_palette) { + memcpy (current_palette, palette, sizeof (current_palette)); + } + for (int i = 0; i < 256; i++) { + const byte *pal = palette + 3 * i; + st2d_8to32table[i].bgra[0] = viddef.gammatable[pal[2]]; + st2d_8to32table[i].bgra[1] = viddef.gammatable[pal[1]]; + st2d_8to32table[i].bgra[2] = viddef.gammatable[pal[0]]; + st2d_8to32table[i].bgra[3] = 255; + } + if (!win_minimized && !using_ddraw && dib_section) { + RGBQUAD colors[256]; + memcpy (colors, st2d_8to32table, sizeof (colors)); + for (int i = 0; i < 256; i++) { + colors[i].rgbReserved = 0; + } + colors[0].rgbRed = 0; + colors[0].rgbGreen = 0; + colors[0].rgbBlue = 0; + colors[255].rgbRed = 0xff; + colors[255].rgbGreen = 0xff; + colors[255].rgbBlue = 0xff; + + if (SetDIBColorTable (dib_section, 0, 256, colors) == 0) { + Sys_Printf ("win_set_palette() - SetDIBColorTable failed\n"); + } + } +} + +static void +dd_blit_rect (vrect_t *rect) +{ + RECT TheRect; + RECT sRect, dRect; + DDSURFACEDESC ddsd; + + memset (&ddsd, 0, sizeof (ddsd)); + ddsd.dwSize = sizeof (DDSURFACEDESC); + + // lock the correct subrect + TheRect.left = rect->x; + TheRect.right = rect->x + rect->width; + TheRect.top = rect->y; + TheRect.bottom = rect->y + rect->height; + + if (IDirectDrawSurface_Lock (dd_backbuffer, &TheRect, &ddsd, + DDLOCK_WRITEONLY | DDLOCK_SURFACEMEMORYPTR, + 0) == DDERR_WASSTILLDRAWING) { + return; + } + + // convert pitch to 32-bit addressable + ddsd.lPitch >>= 2; + + byte *src = swfb.color + rect->y * swfb.rowbytes + rect->x; + unsigned *dst = ddsd.lpSurface; + for (int y = rect->height; y-- > 0; ) { + for (int x = rect->width; x-- > 0; ) { + *dst++ = st2d_8to32table[*src++].value; + } + src += swfb.rowbytes - rect->width; + dst += ddsd.lPitch - rect->width; + } + + IDirectDrawSurface_Unlock (dd_backbuffer, 0); + + // correctly offset source + sRect.left = src_rect.left + rect->x; + sRect.right = src_rect.left + rect->x + rect->width; + sRect.top = src_rect.top + rect->y; + sRect.bottom = src_rect.top + rect->y + rect->height; + + // correctly offset dest + dRect.left = dst_rect.left + rect->x; + dRect.right = dst_rect.left + rect->x + rect->width; + dRect.top = dst_rect.top + rect->y; + dRect.bottom = dst_rect.top + rect->y + rect->height; + + // copy to front buffer + IDirectDrawSurface_Blt (dd_frontbuffer, &dRect, dd_backbuffer, + &sRect, 0, 0); +} + +static void +win_sw_update (sw_ctx_t *ctx, vrect_t *rects) +{ + vrect_t full_rect; + if (!win_palettized && palette_changed) { + palette_changed = false; + full_rect.x = 0; + full_rect.y = 0; + full_rect.width = viddef.width; + full_rect.height = viddef.height; + full_rect.next = 0; + rects = &full_rect; + } + + if (using_ddraw) { + while (rects) { + dd_blit_rect (rects); + rects = rects->next; + } + } else if (dib_section) { + while (rects) { + BitBlt (win_gdi, rects->x, rects->y, + rects->x + rects->width, rects->y + rects->height, + dib_section, rects->x, rects->y, SRCCOPY); + rects = rects->next; + } + } +} + + +static void +win_choose_visual (sw_ctx_t *ctx) +{ +} + +static void +win_set_background (void) +{ + // because we have set the background brush for the window to 0 (to + // avoid flickering when re-sizing the window on the desktop), we clear + // the window to black when created, otherwise it will be empty while + // Quake starts up. This also prevents a screen flash to white when + // switching drivers. it still flashes, but at least it's black now + HDC hdc = GetDC (win_mainwindow); + PatBlt (hdc, 0, 0, win_rect.right, win_rect.bottom, BLACKNESS); + ReleaseDC (win_mainwindow, hdc); +} + +static void +win_create_context (sw_ctx_t *ctx) +{ + // shutdown any old driver that was active + Win_UnloadAllDrivers (); + + win_sw_context = ctx; + + win_set_background (); + + // create the new driver + using_ddraw = false; + + viddef.vid_internal->init_buffers = win_init_bufers; +} + +sw_ctx_t * +Win_SW_Context (vid_internal_t *vi) +{ + sw_ctx_t *ctx = calloc (1, sizeof (sw_ctx_t)); + ctx->set_palette = win_set_palette; + ctx->choose_visual = win_choose_visual; + ctx->create_context = win_create_context; + ctx->update = win_sw_update; + + vi->ctx = ctx; + return ctx; +} + +void +Win_SW_Init_Cvars (void) +{ +} diff --git a/libs/video/targets/vid_win_vulkan.c b/libs/video/targets/vid_win_vulkan.c new file mode 100644 index 000000000..99ee645ab --- /dev/null +++ b/libs/video/targets/vid_win_vulkan.c @@ -0,0 +1,198 @@ +/* + vid_win.c + + Win32 vid component + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "winquake.h" +#define VK_NO_PROTOTYPES +#define VK_USE_PLATFORM_WIN32_KHR +#include + +#include "QF/cvar.h" +#include "QF/set.h" +#include "QF/sys.h" +#include "QF/va.h" + +#include "QF/Vulkan/instance.h" + +#include "context_win.h" +#include "vid_internal.h" +#include "vid_vulkan.h" + +static char *vulkan_library_name; +static cvar_t vulkan_library_name_cvar = { + .name = "vulkan_library", + .description = + "the name of the vulkan shared library", + .default_value = "vulkan-1.dll", + .flags = CVAR_ROM, + .value = { .type = 0, .value = &vulkan_library_name }, +}; + +typedef struct vulkan_presentation_s { +#define PRESENTATION_VULKAN_FUNCTION_FROM_EXTENSION(name,ext) PFN_##name name; +#include "QF/Vulkan/funclist.h" + + HINSTANCE instance; + HWND window; + +} vulkan_presentation_t; + +static const char *required_extensions[] = { + VK_KHR_WIN32_SURFACE_EXTENSION_NAME, + 0 +}; + +static HMODULE vulkan_library; + +static void +load_vulkan_library (vulkan_ctx_t *ctx) +{ + vulkan_library = LoadLibrary (vulkan_library_name); + if (!vulkan_library) { + DWORD errcode = GetLastError (); + Sys_Error ("Couldn't load vulkan library %s: %ld", + vulkan_library_name, errcode); + } + + #define EXPORTED_VULKAN_FUNCTION(name) \ + ctx->name = (PFN_##name) GetProcAddress (vulkan_library, #name); \ + if (!ctx->name) { \ + Sys_Error ("Couldn't find exported vulkan function %s", #name); \ + } + + #define GLOBAL_LEVEL_VULKAN_FUNCTION(name) \ + ctx->name = (PFN_##name) ctx->vkGetInstanceProcAddr (0, #name); \ + if (!ctx->name) { \ + Sys_Error ("Couldn't find global-level function %s", #name); \ + } + + #include "QF/Vulkan/funclist.h" +} + +static void +unload_vulkan_library (vulkan_ctx_t *ctx) +{ + FreeLibrary (vulkan_library); + vulkan_library = 0; +} + +static void +win_vulkan_init_presentation (vulkan_ctx_t *ctx) +{ + ctx->presentation = calloc (1, sizeof (vulkan_presentation_t)); + vulkan_presentation_t *pres = ctx->presentation; + qfv_instance_t *instance = ctx->instance; + VkInstance inst = instance->instance; + +#define PRESENTATION_VULKAN_FUNCTION_FROM_EXTENSION(name, ext) \ + if (instance->extension_enabled (instance, ext)) { \ + pres->name = (PFN_##name) ctx->vkGetInstanceProcAddr (inst, #name); \ + if (!pres->name) { \ + Sys_Error ("Couldn't find instance-level function %s", #name); \ + } \ + } +#include "QF/Vulkan/funclist.h" + + pres->instance = GetModuleHandle (0); +} + +static int +win_vulkan_get_presentation_support (vulkan_ctx_t *ctx, + VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex) +{ + if (!ctx->presentation) { + win_vulkan_init_presentation (ctx); + } + vulkan_presentation_t *pres = ctx->presentation; + if (pres->vkGetPhysicalDeviceWin32PresentationSupportKHR ( + physicalDevice, queueFamilyIndex)) { + return 1; + } + return 0; +} + +static void +win_vulkan_choose_visual (vulkan_ctx_t *ctx) +{ +} + +static void +win_vulkan_create_window (vulkan_ctx_t *ctx) +{ + vulkan_presentation_t *pres = ctx->presentation; + pres->window = win_mainwindow; +} + +static VkSurfaceKHR +win_vulkan_create_surface (vulkan_ctx_t *ctx) +{ + vulkan_presentation_t *pres = ctx->presentation; + VkInstance inst = ctx->instance->instance; + VkSurfaceKHR surface; + VkWin32SurfaceCreateInfoKHR createInfo = { + .sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, + .flags = 0, + .hinstance = pres->instance, + .hwnd = pres->window, + }; + + ctx->window_width = viddef.width; + ctx->window_height = viddef.height; + + if (pres->vkCreateWin32SurfaceKHR (inst, &createInfo, 0, &surface) + != VK_SUCCESS) { + return 0; + } + return surface; +} + +vulkan_ctx_t * +Win_Vulkan_Context (vid_internal_t *vi) +{ + vulkan_ctx_t *ctx = calloc (1, sizeof (vulkan_ctx_t)); + ctx->load_vulkan = load_vulkan_library; + ctx->unload_vulkan = unload_vulkan_library; + ctx->get_presentation_support = win_vulkan_get_presentation_support; + ctx->choose_visual = win_vulkan_choose_visual; + ctx->create_window = win_vulkan_create_window; + ctx->create_surface = win_vulkan_create_surface; + ctx->required_extensions = required_extensions; + ctx->va_ctx = va_create_context (4); + ctx->twod_scale = 1; + + vi->ctx = ctx; + return ctx; +} + +void +Win_Vulkan_Init_Cvars (void) +{ + Cvar_Register (&vulkan_library_name_cvar, 0, 0); +} diff --git a/libs/video/targets/vid_x11.c b/libs/video/targets/vid_x11.c index 6430b9c06..50decd208 100644 --- a/libs/video/targets/vid_x11.c +++ b/libs/video/targets/vid_x11.c @@ -44,30 +44,17 @@ # include #endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include #include -#include #ifdef HAVE_VIDMODE # include #endif #include "QF/cmd.h" -#include "QF/console.h" #include "QF/cvar.h" -#include "QF/qargs.h" #include "QF/qendian.h" #include "QF/screen.h" #include "QF/sys.h" @@ -76,342 +63,12 @@ #include "compat.h" #include "context_x11.h" #include "d_iface.h" -#include "dga_check.h" #include "vid_internal.h" -int XShmGetEventBase (Display *x); // for broken X11 headers - -static GC x_gc; - -static qboolean doShm; -static XShmSegmentInfo x_shminfo[2]; - -static int current_framebuffer; -static XImage *x_framebuffer[2] = { 0, 0 }; +static vid_internal_t vid_internal; int VID_options_items = 1; -static byte current_palette[768]; - -typedef unsigned char PIXEL8; -typedef unsigned short PIXEL16; -typedef unsigned int PIXEL24; - -static PIXEL16 st2d_8to16table[256]; -static PIXEL24 st2d_8to24table[256]; -static int shiftmask_fl = 0; -static long r_shift, g_shift, b_shift; -static unsigned long r_mask, g_mask, b_mask; - -#define GLX_RGBA 4 // true if RGBA mode -#define GLX_DOUBLEBUFFER 5 // double buffering supported -#define GLX_RED_SIZE 8 // number of red component bits -#define GLX_GREEN_SIZE 9 // number of green component bits -#define GLX_BLUE_SIZE 10 // number of blue component bits -#define GLX_DEPTH_SIZE 12 // number of depth bits - -// GLXContext is a pointer to opaque data -typedef struct __GLXcontextRec *GLXContext; -typedef XID GLXDrawable; - -// Define GLAPIENTRY to a useful value -#ifndef GLAPIENTRY -# ifdef _WIN32 -# include -# define GLAPIENTRY WINAPI -# undef LoadImage -# else -# ifdef APIENTRY -# define GLAPIENTRY APIENTRY -# else -# define GLAPIENTRY -# endif -# endif -#endif -static GLXContext ctx = NULL; -static void *libgl_handle; -static void (*qfglXSwapBuffers) (Display *dpy, GLXDrawable drawable); -static XVisualInfo* (*qfglXChooseVisual) (Display *dpy, int screen, - int *attribList); -static GLXContext (*qfglXCreateContext) (Display *dpy, XVisualInfo *vis, - GLXContext shareList, Bool direct); -static Bool (*qfglXMakeCurrent) (Display *dpy, GLXDrawable drawable, - GLXContext ctx); -static void (GLAPIENTRY *qfglFinish) (void); -static void *(*glGetProcAddress) (const char *symbol) = NULL; -static int use_gl_procaddress = 0; -static cvar_t *gl_driver; - -static void (*choose_visual) (void); -static void (*create_context) (void); - -static void * -QFGL_GetProcAddress (void *handle, const char *name) -{ - void *glfunc = NULL; - - if (use_gl_procaddress && glGetProcAddress) - glfunc = glGetProcAddress (name); - if (!glfunc) - glfunc = dlsym (handle, name); - return glfunc; -} - -static void * -QFGL_ProcAddress (const char *name, qboolean crit) -{ - void *glfunc = NULL; - - Sys_MaskPrintf (SYS_VID, "DEBUG: Finding symbol %s ... ", name); - - glfunc = QFGL_GetProcAddress (libgl_handle, name); - if (glfunc) { - Sys_MaskPrintf (SYS_VID, "found [%p]\n", glfunc); - return glfunc; - } - Sys_MaskPrintf (SYS_VID, "not found\n"); - - if (crit) { - if (strncmp ("fxMesa", name, 6) == 0) { - Sys_Printf ("This target requires a special version of Mesa with " - "support for Glide and SVGAlib.\n"); - Sys_Printf ("If you are in X, try using a GLX or SGL target.\n"); - } - Sys_Error ("Couldn't load critical OpenGL function %s, exiting...", - name); - } - return NULL; -} - -static void -glx_choose_visual (void) -{ - int attrib[] = { - GLX_RGBA, - GLX_RED_SIZE, 1, - GLX_GREEN_SIZE, 1, - GLX_BLUE_SIZE, 1, - GLX_DOUBLEBUFFER, - GLX_DEPTH_SIZE, 1, - None - }; - - x_visinfo = qfglXChooseVisual (x_disp, x_screen, attrib); - if (!x_visinfo) { - Sys_Error ("Error couldn't get an RGB, Double-buffered, Depth visual"); - } - x_vis = x_visinfo->visual; -} - -static void -glx_create_context (void) -{ - XSync (x_disp, 0); - ctx = qfglXCreateContext (x_disp, x_visinfo, NULL, True); - qfglXMakeCurrent (x_disp, x_win, ctx); - viddef.init_gl (); -} - -static void -glx_end_rendering (void) -{ - qfglFinish (); - qfglXSwapBuffers (x_disp, x_win); -} - -static void -glx_load_gl (void) -{ - int flags = RTLD_NOW; - - choose_visual = glx_choose_visual; - create_context = glx_create_context; - - viddef.get_proc_address = QFGL_ProcAddress; - viddef.end_rendering = glx_end_rendering; - -#ifdef RTLD_GLOBAL - flags |= RTLD_GLOBAL; -#endif - if (!(libgl_handle = dlopen (gl_driver->string, flags))) { - Sys_Error ("Couldn't load OpenGL library %s: %s", gl_driver->string, - dlerror ()); - } - glGetProcAddress = dlsym (libgl_handle, "glXGetProcAddress"); - if (!glGetProcAddress) - glGetProcAddress = dlsym (libgl_handle, "glXGetProcAddressARB"); - - qfglXSwapBuffers = QFGL_ProcAddress ("glXSwapBuffers", true); - qfglXChooseVisual = QFGL_ProcAddress ("glXChooseVisual", true); - qfglXCreateContext = QFGL_ProcAddress ("glXCreateContext", true); - qfglXMakeCurrent = QFGL_ProcAddress ("glXMakeCurrent", true); - - use_gl_procaddress = 1; - - qfglFinish = QFGL_ProcAddress ("glFinish", true); -} - -static void -shiftmask_init (void) -{ - unsigned long long x; - - r_mask = x_vis->red_mask; - g_mask = x_vis->green_mask; - b_mask = x_vis->blue_mask; - - for (r_shift = -8, x = 1; x < r_mask; x <<= 1) - r_shift++; - for (g_shift = -8, x = 1; x < g_mask; x <<= 1) - g_shift++; - for (b_shift = -8, x = 1; x < b_mask; x <<= 1) - b_shift++; - shiftmask_fl = 1; -} - -static PIXEL16 -xlib_rgb16 (int r, int g, int b) -{ - PIXEL16 p = 0; - - if (!shiftmask_fl) - shiftmask_init (); - - if (r_shift > 0) { - p = (r << (r_shift)) & r_mask; - } else { - if (r_shift < 0) { - p = (r >> (-r_shift)) & r_mask; - } else { - p |= (r & r_mask); - } - } - - if (g_shift > 0) { - p |= (g << (g_shift)) & g_mask; - } else { - if (g_shift < 0) { - p |= (g >> (-g_shift)) & g_mask; - } else { - p |= (g & g_mask); - } - } - - if (b_shift > 0) { - p |= (b << (b_shift)) & b_mask; - } else { - if (b_shift < 0) { - p |= (b >> (-b_shift)) & b_mask; - } else { - p |= (b & b_mask); - } - } - - return p; -} - -static PIXEL24 -xlib_rgb24 (int r, int g, int b) -{ - PIXEL24 p = 0; - - if (!shiftmask_fl) - shiftmask_init (); - - if (r_shift > 0) { - p = (r << (r_shift)) & r_mask; - } else { - if (r_shift < 0) { - p = (r >> (-r_shift)) & r_mask; - } else { - p |= (r & r_mask); - } - } - - if (g_shift > 0) { - p |= (g << (g_shift)) & g_mask; - } else { - if (g_shift < 0) { - p |= (g >> (-g_shift)) & g_mask; - } else { - p |= (g & g_mask); - } - } - - if (b_shift > 0) { - p |= (b << (b_shift)) & b_mask; - } else { - if (b_shift < 0) { - p |= (b >> (-b_shift)) & b_mask; - } else { - p |= (b & b_mask); - } - } - - return p; -} - -static void -st2_fixup (XImage *framebuf, int x, int y, int width, int height) -{ - int xi, yi; - unsigned char *src; - PIXEL16 *dest; - - if (x < 0 || y < 0) - return; - - for (yi = y; yi < (y + height); yi++) { - src = &((byte *)viddef.buffer)[yi * viddef.width]; - dest = (PIXEL16 *) &framebuf->data[yi * framebuf->bytes_per_line]; - for (xi = x; xi < x + width; xi++) { - dest[xi] = st2d_8to16table[src[xi]]; - } - } -} - -static void -st3_fixup (XImage * framebuf, int x, int y, int width, int height) -{ - int yi; - unsigned char *src; - PIXEL24 *dest; - register int count, n; - - if (x < 0 || y < 0) - return; - - for (yi = y; yi < (y + height); yi++) { - src = &((byte *)viddef.buffer)[yi * viddef.width + x]; - dest = (PIXEL24 *) &framebuf->data[yi * framebuf->bytes_per_line + x]; - - // Duff's Device - count = width; - n = (count + 7) / 8; - - switch (count % 8) { - case 0: - do { - *dest++ = st2d_8to24table[*src++]; - case 7: - *dest++ = st2d_8to24table[*src++]; - case 6: - *dest++ = st2d_8to24table[*src++]; - case 5: - *dest++ = st2d_8to24table[*src++]; - case 4: - *dest++ = st2d_8to24table[*src++]; - case 3: - *dest++ = st2d_8to24table[*src++]; - case 2: - *dest++ = st2d_8to24table[*src++]; - case 1: - *dest++ = st2d_8to24table[*src++]; - } while (--n > 0); - } - } -} - void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) { @@ -425,239 +82,26 @@ D_EndDirectRect (int x, int y, int width, int height) } static void -ResetFrameBuffer (void) +X11_VID_Shutdown (void) { - int mem, pwidth; - char *buf; - - if (x_framebuffer[0]) { - XDestroyImage (x_framebuffer[0]); - } - - pwidth = x_visinfo->depth / 8; - - if (pwidth == 3) - pwidth = 4; - mem = ((viddef.width * pwidth + 7) & ~7) * viddef.height; - buf = malloc (mem); - SYS_CHECKMEM (buf); - - // allocate new screen buffer - x_framebuffer[0] = XCreateImage (x_disp, x_vis, x_visinfo->depth, - ZPixmap, 0, buf, viddef.width, - viddef.height, 32, 0); - - if (!x_framebuffer[0]) { - Sys_Error ("VID: XCreateImage failed"); + Sys_MaskPrintf (SYS_vid, "X11_VID_shutdown\n"); + X11_CloseDisplay (); + if (vid_internal.unload) { + vid_internal.unload (vid_internal.ctx); } } static void -ResetSharedFrameBuffers (void) +X11_VID_SetPalette (byte *palette, byte *colormap) { - int size; - int key; - int minsize = getpagesize (); - int frm; - - for (frm = 0; frm < 2; frm++) { - - // free up old frame buffer memory - if (x_framebuffer[frm]) { - XShmDetach (x_disp, &x_shminfo[frm]); - free (x_framebuffer[frm]); - shmdt (x_shminfo[frm].shmaddr); - } - // create the image - x_framebuffer[frm] = XShmCreateImage (x_disp, x_vis, x_visinfo->depth, - ZPixmap, 0, &x_shminfo[frm], - viddef.width, viddef.height); - - // grab shared memory - size = x_framebuffer[frm]->bytes_per_line * x_framebuffer[frm]->height; - - if (size < minsize) - Sys_Error ("VID: Window must use at least %d bytes", minsize); - - key = random (); - x_shminfo[frm].shmid = shmget ((key_t) key, size, IPC_CREAT | 0777); - if (x_shminfo[frm].shmid == -1) - Sys_Error ("VID: Could not get any shared memory (%s)", - strerror (errno)); - - // attach to the shared memory segment - x_shminfo[frm].shmaddr = (void *) shmat (x_shminfo[frm].shmid, 0, 0); - - Sys_MaskPrintf (SYS_VID, "VID: shared memory id=%d, addr=0x%lx\n", - x_shminfo[frm].shmid, (long) x_shminfo[frm].shmaddr); - - x_framebuffer[frm]->data = x_shminfo[frm].shmaddr; - - // get the X server to attach to it - if (!XShmAttach (x_disp, &x_shminfo[frm])) - Sys_Error ("VID: XShmAttach() failed"); - XSync (x_disp, 0); - shmctl (x_shminfo[frm].shmid, IPC_RMID, 0); - - } -} - -static void -x11_init_buffers (void) -{ - if (doShm) - ResetSharedFrameBuffers (); - else - ResetFrameBuffer (); - - current_framebuffer = 0; - - viddef.direct = 0; - viddef.rowbytes = viddef.width; - if (x_visinfo->depth != 8) { - if (viddef.buffer) - free (viddef.buffer); - viddef.buffer = calloc (viddef.width, viddef.height); - if (!viddef.buffer) - Sys_Error ("Not enough memory for video mode"); - } else { - viddef.buffer = x_framebuffer[current_framebuffer]->data; - } - viddef.conbuffer = viddef.buffer; - - viddef.conrowbytes = viddef.rowbytes; -} - -static void -x11_choose_visual (void) -{ - int pnum, i; - XVisualInfo template; - int num_visuals; - int template_mask; - - template_mask = 0; - - // specify a visual id - if ((pnum = COM_CheckParm ("-visualid"))) { - if (pnum >= com_argc - 1) - Sys_Error ("VID: -visualid "); - template.visualid = atoi (com_argv[pnum + 1]); - template_mask = VisualIDMask; - } else { // If not specified, use default - // visual - template.visualid = - XVisualIDFromVisual (XDefaultVisual (x_disp, x_screen)); - template_mask = VisualIDMask; + viddef.colormap8 = colormap; + viddef.fullbright = 256 - viddef.colormap8[256 * VID_GRADES]; + if (vid_internal.set_colormap) { + vid_internal.set_colormap (vid_internal.ctx, colormap); } - // pick a visual -- warn if more than one was available - x_visinfo = XGetVisualInfo (x_disp, template_mask, &template, - &num_visuals); - - if (x_visinfo->depth == 8 && x_visinfo->class == PseudoColor) - x_cmap = XCreateColormap (x_disp, x_win, x_vis, AllocAll); - x_vis = x_visinfo->visual; - - if (num_visuals > 1) { - Sys_MaskPrintf (SYS_VID, - "Found more than one visual id at depth %d:\n", - template.depth); - for (i = 0; i < num_visuals; i++) - Sys_MaskPrintf (SYS_VID, " -visualid %d\n", - (int) x_visinfo[i].visualid); - } else { - if (num_visuals == 0) { - if (template_mask == VisualIDMask) { - Sys_Error ("VID: Bad visual ID %ld", template.visualid); - } else { - Sys_Error ("VID: No visuals at depth %d", template.depth); - } - } - } - - Sys_MaskPrintf (SYS_VID, "Using visualid %d:\n", - (int) x_visinfo->visualid); - Sys_MaskPrintf (SYS_VID, " class %d\n", x_visinfo->class); - Sys_MaskPrintf (SYS_VID, " screen %d\n", x_visinfo->screen); - Sys_MaskPrintf (SYS_VID, " depth %d\n", x_visinfo->depth); - Sys_MaskPrintf (SYS_VID, " red_mask 0x%x\n", - (int) x_visinfo->red_mask); - Sys_MaskPrintf (SYS_VID, " green_mask 0x%x\n", - (int) x_visinfo->green_mask); - Sys_MaskPrintf (SYS_VID, " blue_mask 0x%x\n", - (int) x_visinfo->blue_mask); - Sys_MaskPrintf (SYS_VID, " colormap_size %d\n", - x_visinfo->colormap_size); - Sys_MaskPrintf (SYS_VID, " bits_per_rgb %d\n", - x_visinfo->bits_per_rgb); -} - -static void -x11_create_context (void) -{ - // create the GC - { - XGCValues xgcvalues; - int valuemask = GCGraphicsExposures; - - xgcvalues.graphics_exposures = False; - x_gc = XCreateGC (x_disp, x_win, valuemask, &xgcvalues); - } - - // even if MITSHM is available, make sure it's a local connection - if (XShmQueryExtension (x_disp)) { - char *displayname; - char *d; - - doShm = true; - - if ((displayname = XDisplayName (NULL))) { - if ((d = strchr (displayname, ':'))) - *d = '\0'; - - if (!(!strcasecmp (displayname, "unix") || !*displayname)) - doShm = false; - } - } - - if (doShm) { - x_shmeventtype = XShmGetEventBase (x_disp) + ShmCompletion; - } - - viddef.do_screen_buffer = x11_init_buffers; - VID_InitBuffers (); - -// XSynchronize (x_disp, False); -// X11_AddEvent (x_shmeventtype, event_shm); -} - -static void -VID_SetPalette (const byte *palette) -{ - int i; - XColor colors[256]; - - for (i = 0; i < 256; i++) { - st2d_8to16table[i] = xlib_rgb16 (palette[i * 3], palette[i * 3 + 1], - palette[i * 3 + 2]); - st2d_8to24table[i] = xlib_rgb24 (palette[i * 3], palette[i * 3 + 1], - palette[i * 3 + 2]); - } - - if (x_visinfo->class == PseudoColor && x_visinfo->depth == 8) { - if (palette != current_palette) { - memcpy (current_palette, palette, 768); - } - for (i = 0; i < 256; i++) { - colors[i].pixel = i; - colors[i].flags = DoRed | DoGreen | DoBlue; - colors[i].red = palette[(i * 3)] << 8; - colors[i].green = palette[(i * 3) + 1] << 8; - colors[i].blue = palette[(i * 3) + 2] << 8; - } - XStoreColors (x_disp, x_cmap, colors, 256); - } + VID_InitGamma (palette); + vid_internal.set_palette (vid_internal.ctx, viddef.palette); } /* @@ -665,69 +109,64 @@ VID_SetPalette (const byte *palette) palette. Palette data will go away after the call, so copy it if you'll need it later. */ -void -VID_Init (byte *palette, byte *colormap) +static void +X11_VID_Init (byte *palette, byte *colormap) { - choose_visual = x11_choose_visual; - create_context = x11_create_context; + vid_internal.gl_context = X11_GL_Context; + vid_internal.sw_context = X11_SW_Context; +#ifdef HAVE_VULKAN + vid_internal.vulkan_context = X11_Vulkan_Context; +#endif - R_LoadModule (glx_load_gl, VID_SetPalette); + R_LoadModule (&vid_internal); viddef.numpages = 2; - viddef.colormap8 = colormap; - viddef.fullbright = 256 - viddef.colormap8[256 * VID_GRADES]; srandom (getpid ()); - VID_GetWindowSize (320, 200); + VID_GetWindowSize (640, 480); X11_OpenDisplay (); - choose_visual (); + vid_internal.choose_visual (vid_internal.ctx); X11_SetVidMode (viddef.width, viddef.height); X11_CreateWindow (viddef.width, viddef.height); X11_CreateNullCursor (); // hide mouse pointer - create_context (); + vid_internal.create_context (vid_internal.ctx); - VID_InitGamma (palette); - viddef.set_palette (viddef.palette); + X11_VID_SetPalette (palette, colormap); - Sys_MaskPrintf (SYS_VID, "Video mode %dx%d initialized.\n", + Sys_MaskPrintf (SYS_vid, "Video mode %dx%d initialized.\n", viddef.width, viddef.height); viddef.initialized = true; viddef.recalc_refdef = 1; // force a surface cache flush } -void -VID_Init_Cvars () +static void +X11_VID_Init_Cvars (void) { X11_Init_Cvars (); - gl_driver = Cvar_Get ("gl_driver", GL_DRIVER, CVAR_ROM, NULL, - "The OpenGL library to use. (path optional)"); +#ifdef HAVE_VULKAN + X11_Vulkan_Init_Cvars (); +#endif + X11_GL_Init_Cvars (); + X11_SW_Init_Cvars (); } -/* - VID_Shutdown - - Restore video mode -*/ -void -VID_Shutdown (void) -{ - Sys_MaskPrintf (SYS_VID, "VID_Shutdown\n"); - X11_CloseDisplay (); -} +vid_system_t vid_system = { + .init = X11_VID_Init, + .shutdown = X11_VID_Shutdown, + .init_cvars = X11_VID_Init_Cvars, + .update_fullscreen = X11_UpdateFullscreen, + .set_palette = X11_VID_SetPalette, +}; +#if 0 static int config_notify = 0; static int config_notify_width; static int config_notify_height; -/* - VID_Update - - Flush the given rectangles from the view buffer to the screen. -*/ -void -VID_Update (vrect_t *rects) +static void +update () { /* If the window changes dimension, skip this frame. */ if (config_notify) { @@ -742,53 +181,8 @@ VID_Update (vrect_t *rects) Con_CheckResize (); return; } - - while (rects) { - switch (x_visinfo->depth) { - case 16: - st2_fixup (x_framebuffer[current_framebuffer], - rects->x, rects->y, rects->width, rects->height); - break; - case 24: - st3_fixup (x_framebuffer[current_framebuffer], - rects->x, rects->y, rects->width, rects->height); - break; - } - if (doShm) { - if (!XShmPutImage (x_disp, x_win, x_gc, - x_framebuffer[current_framebuffer], - rects->x, rects->y, rects->x, rects->y, - rects->width, rects->height, True)) { - Sys_Error ("VID_Update: XShmPutImage failed"); - } - oktodraw = false; - while (!oktodraw) - X11_ProcessEvent (); - rects = rects->next; - - current_framebuffer = !current_framebuffer; - } else { - if (XPutImage (x_disp, x_win, x_gc, x_framebuffer[0], - rects->x, rects->y, rects->x, rects->y, - rects->width, rects->height)) { - Sys_Error ("VID_Update: XPutImage failed"); - } - rects = rects->next; - } - } - XSync (x_disp, False); - r_data->scr_fullupdate = 0; -} - -void -VID_LockBuffer (void) -{ -} - -void -VID_UnlockBuffer (void) -{ } +#endif void VID_SetCaption (const char *text) @@ -796,14 +190,14 @@ VID_SetCaption (const char *text) if (text && *text) { char *temp = strdup (text); - X11_SetCaption (va ("%s: %s", PACKAGE_STRING, temp)); + X11_SetCaption (va (0, "%s: %s", PACKAGE_STRING, temp)); free (temp); } else { - X11_SetCaption (va ("%s", PACKAGE_STRING)); + X11_SetCaption (va (0, "%s", PACKAGE_STRING)); } } -qboolean +bool VID_SetGamma (double gamma) { return X11_SetGamma (gamma); diff --git a/libs/video/targets/vid_x11_gl.c b/libs/video/targets/vid_x11_gl.c new file mode 100644 index 000000000..71dd105af --- /dev/null +++ b/libs/video/targets/vid_x11_gl.c @@ -0,0 +1,283 @@ +/* + vid_x11_gl.c + + GLX X11 video driver + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999-2000 contributors of the QuakeForge project + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include + +#include "QF/cvar.h" +#include "QF/sys.h" +#include "QF/vid.h" + +#include "context_x11.h" +#include "r_internal.h" +#include "vid_internal.h" +#include "vid_gl.h" + +#define GLX_RGBA 4 // true if RGBA mode +#define GLX_DOUBLEBUFFER 5 // double buffering supported +#define GLX_RED_SIZE 8 // number of red component bits +#define GLX_GREEN_SIZE 9 // number of green component bits +#define GLX_BLUE_SIZE 10 // number of blue component bits +#define GLX_ALPHA_SIZE 11 // number of alpha component bits +#define GLX_DEPTH_SIZE 12 // number of depth bits +#define GLX_STENCIL_SIZE 13 // number of stencil bits +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define GLX_CONTEXT_FLAGS_ARB 0x2094 +#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define GLX_CONTEXT_DEBUG_BIT_ARB 0x0001 +#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002 +#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 + +#define GLX_X_RENDERABLE 0x8012 +#define GLX_DRAWABLE_TYPE 0x8010 +#define GLX_WINDOW_BIT 0x00000001 +#define GLX_RENDER_TYPE 0x8011 +#define GLX_RGBA_BIT 0x00000001 +#define GLX_X_VISUAL_TYPE 0x22 +#define GLX_TRUE_COLOR 0x8002 + + +typedef XID GLXDrawable; + +// GLXContext is a pointer to opaque data +typedef struct __GLXcontextRec *GLXContext; +typedef struct __GLXFBConfigRec *GLXFBConfig; + +// Define GLAPIENTRY to a useful value +#ifndef GLAPIENTRY +# ifdef APIENTRY +# define GLAPIENTRY APIENTRY +# else +# define GLAPIENTRY +# endif +#endif +static void *libgl_handle; +static void (*qfglXSwapBuffers) (Display *dpy, GLXDrawable drawable); +static GLXContext (*qfglXCreateContextAttribsARB) (Display *dpy, + GLXFBConfig cfg, + GLXContext ctx, + Bool direct, + const int *attribs); +static void (*qfglXDestroyContext) ( Display *dpy, GLXContext ctx ); +static GLXFBConfig *(*qfglXChooseFBConfig) (Display *dpy, int screen, + const int *attribs, int *nitems); +static XVisualInfo *(*qfglXGetVisualFromFBConfig) (Display *dpy, + GLXFBConfig config); +static int (*qfglXGetFBConfigAttrib) (Display *dpy, GLXFBConfig config, + int attribute, int *value); +static Bool (*qfglXMakeCurrent) (Display *dpy, GLXDrawable drawable, + GLXContext ctx); +static void (GLAPIENTRY *qfglFinish) (void); +static void *(*glGetProcAddress) (const char *symbol) = NULL; +static int use_gl_procaddress = 0; + +static GLXFBConfig glx_cfg; +static char *gl_driver; +static cvar_t gl_driver_cvar = { + .name = "gl_driver", + .description = + "The OpenGL library to use. (path optional)", + .default_value = GL_DRIVER, + .flags = CVAR_ROM, + .value = { .type = 0, .value = &gl_driver }, +}; + +static void * +QFGL_GetProcAddress (void *handle, const char *name) +{ + void *glfunc = NULL; + + if (use_gl_procaddress && glGetProcAddress) + glfunc = glGetProcAddress (name); + if (!glfunc) + glfunc = dlsym (handle, name); + return glfunc; +} + +static void * +QFGL_ProcAddress (const char *name, bool crit) +{ + void *glfunc = NULL; + + Sys_MaskPrintf (SYS_vid, "DEBUG: Finding symbol %s ... ", name); + + glfunc = QFGL_GetProcAddress (libgl_handle, name); + if (glfunc) { + Sys_MaskPrintf (SYS_vid, "found [%p]\n", glfunc); + return glfunc; + } + Sys_MaskPrintf (SYS_vid, "not found\n"); + + if (crit) { + if (strncmp ("fxMesa", name, 6) == 0) { + Sys_Printf ("This target requires a special version of Mesa with " + "support for Glide and SVGAlib.\n"); + Sys_Printf ("If you are in X, try using a GLX or SGL target.\n"); + } + Sys_Error ("Couldn't load critical OpenGL function %s, exiting...", + name); + } + return NULL; +} + +static void +glx_choose_visual (gl_ctx_t *ctx) +{ + int attrib[] = { + GLX_X_RENDERABLE, True, + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, 8, + GLX_DEPTH_SIZE, 24, + //I can't tell if this makes any difference to performance, but it's + //currently not needed + //GLX_STENCIL_SIZE, 8, + GLX_DOUBLEBUFFER, True, + None + }; + int fbcount; + GLXFBConfig *fbc = qfglXChooseFBConfig (x_disp, x_screen, attrib, &fbcount); + + if (!fbc) { + Sys_Error ("Failed to retrieve a framebuffer config"); + } + Sys_MaskPrintf (SYS_vid, "Found %d matching FB configs.\n", fbcount); + glx_cfg = fbc[0]; + x_visinfo = qfglXGetVisualFromFBConfig (x_disp, glx_cfg); + XFree (fbc); + if (!x_visinfo) { + Sys_Error ("Error couldn't get an RGB, Double-buffered, Depth visual"); + } + x_vis = x_visinfo->visual; +} + +static void +glx_create_context (gl_ctx_t *ctx, int core) +{ + int attribs[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 0, + GLX_CONTEXT_PROFILE_MASK_ARB, + core ? GLX_CONTEXT_CORE_PROFILE_BIT_ARB + : GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + None + }; + XSync (x_disp, 0); + ctx->context = (GL_context) qfglXCreateContextAttribsARB (x_disp, glx_cfg, + 0, True, attribs); + qfglXMakeCurrent (x_disp, x_win, (GLXContext) ctx->context); + ctx->init_gl (); +} + +static void +glx_end_rendering (void) +{ + qfglFinish (); + qfglXSwapBuffers (x_disp, x_win); +} + +static void +glx_load_gl (gl_ctx_t *ctx) +{ + libgl_handle = dlopen (gl_driver, RTLD_NOW); + if (!libgl_handle) { + Sys_Error ("Couldn't load OpenGL library %s: %s", gl_driver, + dlerror ()); + } + glGetProcAddress = dlsym (libgl_handle, "glXGetProcAddress"); + if (!glGetProcAddress) + glGetProcAddress = dlsym (libgl_handle, "glXGetProcAddressARB"); + + qfglXSwapBuffers = QFGL_ProcAddress ("glXSwapBuffers", true); + qfglXChooseFBConfig = QFGL_ProcAddress ("glXChooseFBConfig", true); + qfglXGetVisualFromFBConfig = QFGL_ProcAddress ("glXGetVisualFromFBConfig", true); + qfglXGetFBConfigAttrib = QFGL_ProcAddress ("glXGetFBConfigAttrib", true); + qfglXCreateContextAttribsARB = QFGL_ProcAddress ("glXCreateContextAttribsARB", true); + qfglXDestroyContext = QFGL_ProcAddress ("glXDestroyContext", true); + qfglXMakeCurrent = QFGL_ProcAddress ("glXMakeCurrent", true); + + use_gl_procaddress = 1; + + qfglFinish = QFGL_ProcAddress ("glFinish", true); +} + +static void +glx_unload_gl (void *_ctx) +{ + gl_ctx_t *ctx = _ctx; + if (libgl_handle) { + dlclose (libgl_handle); + libgl_handle = 0; + } + free (ctx); +} + +gl_ctx_t * +X11_GL_Context (vid_internal_t *vi) +{ + gl_ctx_t *ctx = calloc (1, sizeof (gl_ctx_t)); + ctx->load_gl = glx_load_gl; + ctx->choose_visual = glx_choose_visual; + ctx->create_context = glx_create_context; + ctx->get_proc_address = QFGL_ProcAddress; + ctx->end_rendering = glx_end_rendering; + + vi->unload = glx_unload_gl; + vi->ctx = ctx; + return ctx; +} + +void +X11_GL_Init_Cvars (void) +{ + Cvar_Register (&gl_driver_cvar, 0, 0); +} diff --git a/libs/video/targets/vid_x11_sw.c b/libs/video/targets/vid_x11_sw.c new file mode 100644 index 000000000..010199951 --- /dev/null +++ b/libs/video/targets/vid_x11_sw.c @@ -0,0 +1,711 @@ +/* + vid_x11_sw.c + + Software X11 video driver (8/32 bit) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999-2000 contributors of the QuakeForge project + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "QF/cvar.h" +#include "QF/qargs.h" +#include "QF/sys.h" +#include "QF/vid.h" + +#include "QF/ui/view.h" + +#include "context_x11.h" +#include "r_internal.h" +#include "vid_internal.h" +#include "vid_sw.h" + +int XShmGetEventBase (Display *x); // for broken X11 headers + +static GC x_gc; + +static bool doShm; +static XShmSegmentInfo x_shminfo[2]; + +static int current_framebuffer; +static XImage *x_framebuffer[2] = { 0, 0 }; + +typedef unsigned char PIXEL8; +typedef unsigned short PIXEL16; +typedef unsigned int PIXEL24; + +static PIXEL16 st2d_8to16table[256]; +static PIXEL24 st2d_8to24table[256]; + +static byte current_palette[768]; +static int shiftmask_fl = 0; +static long r_shift, g_shift, b_shift; +static unsigned long r_mask, g_mask, b_mask; + +static void +shiftmask_init (void) +{ + unsigned long long x; + + r_mask = x_vis->red_mask; + g_mask = x_vis->green_mask; + b_mask = x_vis->blue_mask; + + for (r_shift = -8, x = 1; x < r_mask; x <<= 1) + r_shift++; + for (g_shift = -8, x = 1; x < g_mask; x <<= 1) + g_shift++; + for (b_shift = -8, x = 1; x < b_mask; x <<= 1) + b_shift++; + shiftmask_fl = 1; +} + +static PIXEL16 +xlib_rgb16 (int r, int g, int b) +{ + PIXEL16 p = 0; + + if (!shiftmask_fl) + shiftmask_init (); + + if (r_shift > 0) { + p = (r << (r_shift)) & r_mask; + } else { + if (r_shift < 0) { + p = (r >> (-r_shift)) & r_mask; + } else { + p |= (r & r_mask); + } + } + + if (g_shift > 0) { + p |= (g << (g_shift)) & g_mask; + } else { + if (g_shift < 0) { + p |= (g >> (-g_shift)) & g_mask; + } else { + p |= (g & g_mask); + } + } + + if (b_shift > 0) { + p |= (b << (b_shift)) & b_mask; + } else { + if (b_shift < 0) { + p |= (b >> (-b_shift)) & b_mask; + } else { + p |= (b & b_mask); + } + } + + return p; +} + +static PIXEL24 +xlib_rgb24 (int r, int g, int b) +{ + PIXEL24 p = 0; + + if (!shiftmask_fl) + shiftmask_init (); + + if (r_shift > 0) { + p = (r << (r_shift)) & r_mask; + } else { + if (r_shift < 0) { + p = (r >> (-r_shift)) & r_mask; + } else { + p |= (r & r_mask); + } + } + + if (g_shift > 0) { + p |= (g << (g_shift)) & g_mask; + } else { + if (g_shift < 0) { + p |= (g >> (-g_shift)) & g_mask; + } else { + p |= (g & g_mask); + } + } + + if (b_shift > 0) { + p |= (b << (b_shift)) & b_mask; + } else { + if (b_shift < 0) { + p |= (b >> (-b_shift)) & b_mask; + } else { + p |= (b & b_mask); + } + } + + return p; +} + +static void +x11_set_palette (sw_ctx_t *ctx, const byte *palette) +{ + int i; + XColor colors[256]; + + for (i = 0; i < 256; i++) { + st2d_8to16table[i] = xlib_rgb16 (palette[i * 3], palette[i * 3 + 1], + palette[i * 3 + 2]); + st2d_8to24table[i] = xlib_rgb24 (palette[i * 3], palette[i * 3 + 1], + palette[i * 3 + 2]); + } + + if (x_visinfo->class == PseudoColor && x_visinfo->depth == 8) { + if (palette != current_palette) { + memcpy (current_palette, palette, 768); + } + for (i = 0; i < 256; i++) { + colors[i].pixel = i; + colors[i].flags = DoRed | DoGreen | DoBlue; + colors[i].red = palette[(i * 3)] << 8; + colors[i].green = palette[(i * 3) + 1] << 8; + colors[i].blue = palette[(i * 3) + 2] << 8; + } + XStoreColors (x_disp, x_cmap, colors, 256); + } +} + +static void +st2_fixup (sw_ctx_t *ctx, XImage *framebuf, int x, int y, int width, int height) +{ + int xi, yi; + unsigned char *src; + PIXEL16 *dest; + sw_framebuffer_t *fb = ctx->framebuffer->buffer; + + if (x < 0 || y < 0) + return; + + for (yi = y; yi < (y + height); yi++) { + src = &fb->color[yi * fb->rowbytes]; + dest = (PIXEL16 *) &framebuf->data[yi * framebuf->bytes_per_line]; + for (xi = x; xi < x + width; xi++) { + dest[xi] = st2d_8to16table[src[xi]]; + } + } +} + +static void +st3_fixup (sw_ctx_t *ctx, XImage *framebuf, int x, int y, int width, int height) +{ + int yi; + unsigned char *src; + PIXEL24 *dest; + sw_framebuffer_t *fb = ctx->framebuffer->buffer; + register int count, n; + + if (x < 0 || y < 0) + return; + + for (yi = y; yi < (y + height); yi++) { + src = &fb->color[yi * fb->rowbytes + x]; + dest = (PIXEL24 *) &framebuf->data[yi * framebuf->bytes_per_line + x]; + + // Duff's Device + count = width; + n = (count + 7) / 8; + + switch (count % 8) { + case 0: + do { + *dest++ = st2d_8to24table[*src++]; + case 7: + *dest++ = st2d_8to24table[*src++]; + case 6: + *dest++ = st2d_8to24table[*src++]; + case 5: + *dest++ = st2d_8to24table[*src++]; + case 4: + *dest++ = st2d_8to24table[*src++]; + case 3: + *dest++ = st2d_8to24table[*src++]; + case 2: + *dest++ = st2d_8to24table[*src++]; + case 1: + *dest++ = st2d_8to24table[*src++]; + } while (--n > 0); + } + } +} + +static void +x11_put_image (vrect_t *rect) +{ + if (doShm) { + if (!XShmPutImage (x_disp, x_win, x_gc, + x_framebuffer[current_framebuffer], + rect->x, rect->y, rect->x, rect->y, + rect->width, rect->height, True)) { + Sys_Error ("VID_Update: XShmPutImage failed"); + } + oktodraw = false; + while (!oktodraw) + X11_ProcessEvent (); + + current_framebuffer = !current_framebuffer; + } else { + if (XPutImage (x_disp, x_win, x_gc, x_framebuffer[0], + rect->x, rect->y, rect->x, rect->y, + rect->width, rect->height)) { + Sys_Error ("VID_Update: XPutImage failed"); + } + } +} + +/* + Flush the given rectangles from the view buffer to the screen. +*/ +static void +x11_sw8_8_update (sw_ctx_t *ctx, vrect_t *rects) +{ + vrect_t urect = *rects; + while (rects->next) { + rects = rects->next; + int minx = min (VRect_MinX (&urect), VRect_MinX (rects)); + int miny = min (VRect_MinY (&urect), VRect_MinY (rects)); + int maxx = max (VRect_MaxX (&urect), VRect_MaxX (rects)); + int maxy = max (VRect_MaxY (&urect), VRect_MaxY (rects)); + urect.x = minx; + urect.y = miny; + urect.width = maxx - minx; + urect.height = maxy - miny; + } + x11_put_image (&urect); + XSync (x_disp, False); + r_data->scr_fullupdate = 0; + + sw_framebuffer_t *fb = ctx->framebuffer->buffer; + fb->color = (byte *) x_framebuffer[current_framebuffer]->data; +} + +static void +x11_sw8_16_update (sw_ctx_t *ctx, vrect_t *rects) +{ + vrect_t urect = *rects; + st2_fixup (ctx, x_framebuffer[current_framebuffer], + rects->x, rects->y, rects->width, rects->height); + while (rects->next) { + rects = rects->next; + st2_fixup (ctx, x_framebuffer[current_framebuffer], + rects->x, rects->y, rects->width, rects->height); + int minx = min (VRect_MinX (&urect), VRect_MinX (rects)); + int miny = min (VRect_MinY (&urect), VRect_MinY (rects)); + int maxx = max (VRect_MaxX (&urect), VRect_MaxX (rects)); + int maxy = max (VRect_MaxY (&urect), VRect_MaxY (rects)); + urect.x = minx; + urect.y = miny; + urect.width = maxx - minx; + urect.height = maxy - miny; + } + x11_put_image (&urect); + XSync (x_disp, False); + r_data->scr_fullupdate = 0; +} + +static void +x11_sw8_24_update (sw_ctx_t *ctx, vrect_t *rects) +{ + vrect_t urect = *rects; + st3_fixup (ctx, x_framebuffer[current_framebuffer], + rects->x, rects->y, rects->width, rects->height); + while (rects->next) { + rects = rects->next; + st3_fixup (ctx, x_framebuffer[current_framebuffer], + rects->x, rects->y, rects->width, rects->height); + int minx = min (VRect_MinX (&urect), VRect_MinX (rects)); + int miny = min (VRect_MinY (&urect), VRect_MinY (rects)); + int maxx = max (VRect_MaxX (&urect), VRect_MaxX (rects)); + int maxy = max (VRect_MaxY (&urect), VRect_MaxY (rects)); + urect.x = minx; + urect.y = miny; + urect.width = maxx - minx; + urect.height = maxy - miny; + } + x11_put_image (&urect); + XSync (x_disp, False); + r_data->scr_fullupdate = 0; +} +#if 0 +static void +x11_sw16_16_update (sw_ctx_t *ctx, vrect_t *rects) +{ + uint16_t *buffer = (uint16_t *) viddef.buffer; + XImage *framebuf = x_framebuffer[current_framebuffer]; + int rowbytes = framebuf->bytes_per_line; + + while (rects) { + int x = rects->x; + int y = rects->y; + int width = rects->width; + int height = rects->height; + for (int yi = y; yi < (y + height); yi++) { + uint16_t *src = buffer + yi * viddef.width + x; + PIXEL16 *dest = (PIXEL16 *) (framebuf->data + yi * rowbytes) + x; + + for (int count = width; count-- > 0; ) { + *dest++ = *src++; + } + } + x11_put_image (rects); + rects = rects->next; + } + XSync (x_disp, False); + r_data->scr_fullupdate = 0; +} + +static void +x11_sw16_32_update (sw_ctx_t *ctx, vrect_t *rects) +{ + uint16_t *buffer = (uint16_t *) viddef.buffer; + XImage *framebuf = x_framebuffer[current_framebuffer]; + int rowbytes = framebuf->bytes_per_line; + + while (rects) { + int x = rects->x; + int y = rects->y; + int width = rects->width; + int height = rects->height; + for (int yi = y; yi < (y + height); yi++) { + uint16_t *src = buffer + yi * viddef.width + x; + PIXEL24 *dest = (PIXEL24 *) (framebuf->data + yi * rowbytes) + x; + + for (int count = width; count-- > 0; ) { + uint32_t c = *src++; + *dest++ = 0xff000000 + | ((c & 0xf800) << 8) + | ((c & 0x07e0) << 5) + | ((c & 0x001f) << 3); + } + } + x11_put_image (rects); + rects = rects->next; + } + XSync (x_disp, False); + r_data->scr_fullupdate = 0; +} + +static void +x11_sw32_update (sw_ctx_t *ctx, vrect_t *rects) +{ + uint32_t *buffer = (uint32_t *) viddef.buffer; + XImage *framebuf = x_framebuffer[current_framebuffer]; + int rowbytes = framebuf->bytes_per_line; + + while (rects) { + int x = rects->x; + int y = rects->y; + int width = rects->width; + int height = rects->height; + for (int yi = y; yi < (y + height); yi++) { + uint32_t *src = buffer + yi * viddef.width + x; + PIXEL24 *dest = (PIXEL24 *) (framebuf->data + yi * rowbytes) + x; + + for (int count = width; count-- > 0; ) { + *dest++ = *src++; + } + } + x11_put_image (rects); + rects = rects->next; + } + XSync (x_disp, False); + r_data->scr_fullupdate = 0; +} +#endif +static void +x11_choose_visual (sw_ctx_t *ctx) +{ + int pnum, i; + XVisualInfo template; + int num_visuals; + int template_mask; + + // specify a visual id + if ((pnum = COM_CheckParm ("-visualid"))) { + if (pnum >= com_argc - 1) + Sys_Error ("VID: -visualid "); + template.visualid = atoi (com_argv[pnum + 1]); + template_mask = VisualIDMask; + } else { // If not specified, use default + // visual + template.visualid = + XVisualIDFromVisual (XDefaultVisual (x_disp, x_screen)); + template_mask = VisualIDMask; + } + + // pick a visual -- warn if more than one was available + x_visinfo = XGetVisualInfo (x_disp, template_mask, &template, + &num_visuals); + + if (x_visinfo->depth == 8 && x_visinfo->class == PseudoColor) + x_cmap = XCreateColormap (x_disp, x_win, x_vis, AllocAll); + x_vis = x_visinfo->visual; + + ctx->update = x11_sw8_8_update; + switch (x_visinfo->depth) { + case 8: + ctx->update = x11_sw8_8_update; + break; + case 16: + ctx->update = x11_sw8_16_update; + break; + case 24: + ctx->update = x11_sw8_24_update; + break; + } + + if (num_visuals > 1) { + Sys_MaskPrintf (SYS_vid, + "Found more than one visual id at depth %d:\n", + template.depth); + for (i = 0; i < num_visuals; i++) + Sys_MaskPrintf (SYS_vid, " -visualid %d\n", + (int) x_visinfo[i].visualid); + } else { + if (num_visuals == 0) { + if (template_mask == VisualIDMask) { + Sys_Error ("VID: Bad visual ID %ld", template.visualid); + } else { + Sys_Error ("VID: No visuals at depth %d", template.depth); + } + } + } + + Sys_MaskPrintf (SYS_vid, "Using visualid %d:\n", + (int) x_visinfo->visualid); + Sys_MaskPrintf (SYS_vid, " class %d\n", x_visinfo->class); + Sys_MaskPrintf (SYS_vid, " screen %d\n", x_visinfo->screen); + Sys_MaskPrintf (SYS_vid, " depth %d\n", x_visinfo->depth); + Sys_MaskPrintf (SYS_vid, " red_mask 0x%x\n", + (int) x_visinfo->red_mask); + Sys_MaskPrintf (SYS_vid, " green_mask 0x%x\n", + (int) x_visinfo->green_mask); + Sys_MaskPrintf (SYS_vid, " blue_mask 0x%x\n", + (int) x_visinfo->blue_mask); + Sys_MaskPrintf (SYS_vid, " colormap_size %d\n", + x_visinfo->colormap_size); + Sys_MaskPrintf (SYS_vid, " bits_per_rgb %d\n", + x_visinfo->bits_per_rgb); +} + +static void +ResetFrameBuffer (void) +{ + int mem, pwidth; + char *buf; + + if (x_framebuffer[0]) { + XDestroyImage (x_framebuffer[0]); + } + + pwidth = x_visinfo->depth / 8; + + if (pwidth == 3) + pwidth = 4; + mem = ((viddef.width * pwidth + 7) & ~7) * viddef.height; + buf = malloc (mem); + SYS_CHECKMEM (buf); + + // allocate new screen buffer + x_framebuffer[0] = XCreateImage (x_disp, x_vis, x_visinfo->depth, + ZPixmap, 0, buf, viddef.width, + viddef.height, 32, 0); + + if (!x_framebuffer[0]) { + Sys_Error ("VID: XCreateImage failed"); + } +} + +static void +ResetSharedFrameBuffers (void) +{ + int size; + int key; + int minsize = getpagesize (); + int frm; + + for (frm = 0; frm < 2; frm++) { + + // free up old frame buffer memory + if (x_framebuffer[frm]) { + XShmDetach (x_disp, &x_shminfo[frm]); + free (x_framebuffer[frm]); + shmdt (x_shminfo[frm].shmaddr); + } + // create the image + x_framebuffer[frm] = XShmCreateImage (x_disp, x_vis, x_visinfo->depth, + ZPixmap, 0, &x_shminfo[frm], + viddef.width, viddef.height); + + // grab shared memory + size = x_framebuffer[frm]->bytes_per_line * x_framebuffer[frm]->height; + + if (size < minsize) + Sys_Error ("VID: Window must use at least %d bytes", minsize); + + key = random (); + x_shminfo[frm].shmid = shmget ((key_t) key, size, IPC_CREAT | 0777); + if (x_shminfo[frm].shmid == -1) + Sys_Error ("VID: Could not get any shared memory (%s)", + strerror (errno)); + + // attach to the shared memory segment + x_shminfo[frm].shmaddr = (void *) shmat (x_shminfo[frm].shmid, 0, 0); + + Sys_MaskPrintf (SYS_vid, "VID: shared memory id=%d, addr=0x%lx\n", + x_shminfo[frm].shmid, (long) x_shminfo[frm].shmaddr); + + x_framebuffer[frm]->data = x_shminfo[frm].shmaddr; + + // get the X server to attach to it + if (!XShmAttach (x_disp, &x_shminfo[frm])) + Sys_Error ("VID: XShmAttach() failed"); + XSync (x_disp, 0); + shmctl (x_shminfo[frm].shmid, IPC_RMID, 0); + + } +} + +static sw_framebuffer_t swfb; +static framebuffer_t fb = { .buffer = &swfb }; + +static void +x11_init_buffers (void *data) +{ + sw_ctx_t *ctx = data; + + ctx->framebuffer = &fb; + + if (doShm) + ResetSharedFrameBuffers (); + else + ResetFrameBuffer (); + + current_framebuffer = 0; + + fb.width = viddef.width; + fb.height = viddef.height; + if (x_visinfo->depth != 8) { + if (swfb.color) + free (swfb.color); + if (swfb.depth) + free (swfb.depth); + swfb.rowbytes = viddef.width; + swfb.color = calloc (swfb.rowbytes, viddef.height); + swfb.depth = 0; + if (!swfb.color) + Sys_Error ("Not enough memory for video mode"); + } else { + swfb.rowbytes = x_framebuffer[current_framebuffer]->bytes_per_line; + swfb.color = (byte *) x_framebuffer[current_framebuffer]->data; + if (swfb.depth) + free (swfb.depth); + swfb.depth = 0; + } +} + +static void +x11_create_context (sw_ctx_t *ctx) +{ + // create the GC + { + XGCValues xgcvalues; + int valuemask = GCGraphicsExposures; + + xgcvalues.graphics_exposures = False; + x_gc = XCreateGC (x_disp, x_win, valuemask, &xgcvalues); + } + + // even if MITSHM is available, make sure it's a local connection + if (XShmQueryExtension (x_disp)) { + char *displayname; + char *d; + + doShm = true; + + if ((displayname = XDisplayName (NULL))) { + if ((d = strchr (displayname, ':'))) + *d = '\0'; + + if (!(!strcasecmp (displayname, "unix") || !*displayname)) + doShm = false; + } + } + + if (doShm) { + x_shmeventtype = XShmGetEventBase (x_disp) + ShmCompletion; + } + + viddef.vid_internal->init_buffers = x11_init_buffers; +// XSynchronize (x_disp, False); +// X11_AddEvent (x_shmeventtype, event_shm); +} + +sw_ctx_t * +X11_SW_Context (vid_internal_t *vi) +{ + sw_ctx_t *ctx = calloc (1, sizeof (sw_ctx_t)); + ctx->set_palette = x11_set_palette; + ctx->choose_visual = x11_choose_visual; + ctx->create_context = x11_create_context; + ctx->update = x11_sw8_8_update; + + vi->ctx = ctx; + return ctx; +} + +void +X11_SW_Init_Cvars (void) +{ +} diff --git a/libs/video/targets/vid_x11_vulkan.c b/libs/video/targets/vid_x11_vulkan.c new file mode 100644 index 000000000..66579b3ed --- /dev/null +++ b/libs/video/targets/vid_x11_vulkan.c @@ -0,0 +1,237 @@ +/* + vid_x11_vulkan.c + + Vulkan X11 video driver + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999-2000 contributors of the QuakeForge project + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include +#define VK_NO_PROTOTYPES +#define VK_USE_PLATFORM_XLIB_KHR +#include + +#include "QF/cvar.h" +#include "QF/set.h" +#include "QF/sys.h" +#include "QF/va.h" + +#include "QF/Vulkan/instance.h" + +#include "context_x11.h" +#include "vid_internal.h" +#include "vid_vulkan.h" + +static char *vulkan_library_name; +static cvar_t vulkan_library_name_cvar = { + .name = "vulkan_library", + .description = + "the name of the vulkan shared library", + .default_value = "libvulkan.so.1", + .flags = CVAR_ROM, + .value = { .type = 0, .value = &vulkan_library_name }, +}; + +typedef struct vulkan_presentation_s { +#define PRESENTATION_VULKAN_FUNCTION_FROM_EXTENSION(name,ext) PFN_##name name; +#include "QF/Vulkan/funclist.h" + + Display *display; + Window window; + int num_visuals; + XVisualInfo *visinfo; + set_t *usable_visuals; +} vulkan_presentation_t; + +static const char *required_extensions[] = { + VK_KHR_XLIB_SURFACE_EXTENSION_NAME, + 0 +}; + +static void *vulkan_library; + +static void +load_vulkan_library (vulkan_ctx_t *ctx) +{ + vulkan_library = dlopen (vulkan_library_name, + RTLD_DEEPBIND | RTLD_NOW); + if (!vulkan_library) { + Sys_Error ("Couldn't load vulkan library %s: %s", + vulkan_library_name, dlerror ()); + } + + #define EXPORTED_VULKAN_FUNCTION(name) \ + ctx->name = (PFN_##name) dlsym (vulkan_library, #name); \ + if (!ctx->name) { \ + Sys_Error ("Couldn't find exported vulkan function %s", #name); \ + } + + #define GLOBAL_LEVEL_VULKAN_FUNCTION(name) \ + ctx->name = (PFN_##name) ctx->vkGetInstanceProcAddr (0, #name); \ + if (!ctx->name) { \ + Sys_Error ("Couldn't find global-level function %s", #name); \ + } + + #include "QF/Vulkan/funclist.h" +} + +static void +unload_vulkan_library (vulkan_ctx_t *ctx) +{ + dlclose (vulkan_library); + vulkan_library = 0; +} + +static void +x11_vulkan_init_presentation (vulkan_ctx_t *ctx) +{ + ctx->presentation = calloc (1, sizeof (vulkan_presentation_t)); + vulkan_presentation_t *pres = ctx->presentation; + qfv_instance_t *instance = ctx->instance; + VkInstance inst = instance->instance; + +#define PRESENTATION_VULKAN_FUNCTION_FROM_EXTENSION(name, ext) \ + if (instance->extension_enabled (instance, ext)) { \ + pres->name = (PFN_##name) ctx->vkGetInstanceProcAddr (inst, #name); \ + if (!pres->name) { \ + Sys_Error ("Couldn't find instance-level function %s", #name); \ + } \ + } +#include "QF/Vulkan/funclist.h" + + XVisualInfo template; + Visual *defaultVisual = XDefaultVisual (x_disp, x_screen); + template.visualid = XVisualIDFromVisual (defaultVisual); + int template_mask = VisualIDMask; + pres->display = x_disp; + pres->usable_visuals = set_new (); + pres->visinfo = XGetVisualInfo (x_disp, template_mask, &template, + &pres->num_visuals); +} + +static int +x11_vulkan_get_presentation_support (vulkan_ctx_t *ctx, + VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex) +{ + if (!ctx->presentation) { + x11_vulkan_init_presentation (ctx); + } + vulkan_presentation_t *pres = ctx->presentation; + + set_empty (pres->usable_visuals); + for (int i = 0; i < pres->num_visuals; i++) { + VisualID visID = pres->visinfo[i].visualid; + + if (pres->vkGetPhysicalDeviceXlibPresentationSupportKHR ( + physicalDevice, queueFamilyIndex, pres->display, visID)) { + set_add (pres->usable_visuals, i); + } + } + return !set_is_empty (pres->usable_visuals); +} + +static void +x11_vulkan_choose_visual (vulkan_ctx_t *ctx) +{ + vulkan_presentation_t *pres = ctx->presentation; + set_iter_t *first = set_first (pres->usable_visuals); + if (first) { + x_visinfo = pres->visinfo + first->element; + x_vis = x_visinfo->visual; + set_del_iter (first); + } +} + +static void +x11_vulkan_create_window (vulkan_ctx_t *ctx) +{ + vulkan_presentation_t *pres = ctx->presentation; + pres->window = x_win; +} + +static VkSurfaceKHR +x11_vulkan_create_surface (vulkan_ctx_t *ctx) +{ + vulkan_presentation_t *pres = ctx->presentation; + VkInstance inst = ctx->instance->instance; + VkSurfaceKHR surface; + VkXlibSurfaceCreateInfoKHR createInfo = { + .sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, + .flags = 0, + .dpy = pres->display, + .window = pres->window + }; + + ctx->window_width = viddef.width; + ctx->window_height = viddef.height; + + if (pres->vkCreateXlibSurfaceKHR (inst, &createInfo, 0, &surface) + != VK_SUCCESS) { + return 0; + } + return surface; +} + +vulkan_ctx_t * +X11_Vulkan_Context (vid_internal_t *vi) +{ + vulkan_ctx_t *ctx = calloc (1, sizeof (vulkan_ctx_t)); + ctx->load_vulkan = load_vulkan_library; + ctx->unload_vulkan = unload_vulkan_library; + ctx->get_presentation_support = x11_vulkan_get_presentation_support; + ctx->choose_visual = x11_vulkan_choose_visual; + ctx->create_window = x11_vulkan_create_window; + ctx->create_surface = x11_vulkan_create_surface; + ctx->required_extensions = required_extensions; + ctx->va_ctx = va_create_context (32); + ctx->twod_scale = 1; + + vi->ctx = ctx; + return ctx; +} + +void +X11_Vulkan_Init_Cvars (void) +{ + Cvar_Register (&vulkan_library_name_cvar, 0, 0); +} diff --git a/m4/libFLAC.m4 b/m4/libFLAC.m4 index 8f9ef741c..0db77857e 100644 --- a/m4/libFLAC.m4 +++ b/m4/libFLAC.m4 @@ -8,10 +8,10 @@ AC_DEFUN([AM_PATH_LIBFLAC], [dnl dnl Get the cflags and libraries dnl -AC_ARG_WITH(libFLAC,[ --with-libFLAC=PFX Prefix where libFLAC is installed (optional)], libFLAC_prefix="$withval", libFLAC_prefix="") -AC_ARG_WITH(libFLAC-libraries,[ --with-libFLAC-libraries=DIR Directory where libFLAC library is installed (optional)], libFLAC_libraries="$withval", libFLAC_libraries="") -AC_ARG_WITH(libFLAC-includes,[ --with-libFLAC-includes=DIR Directory where libFLAC header files are installed (optional)], libFLAC_includes="$withval", libFLAC_includes="") -AC_ARG_ENABLE(libFLACtest, [ --disable-libFLACtest Do not try to compile and run a test libFLAC program],, enable_libFLACtest=yes) +AC_ARG_WITH(libFLAC,AS_HELP_STRING([--with-libFLAC=PFX], [prefix where libFLAC is installed (optional)]), libFLAC_prefix="$withval", libFLAC_prefix="") +AC_ARG_WITH(libFLAC-libraries,AS_HELP_STRING([--with-libFLAC-libraries=DIR], [directory where libFLAC library is installed (optional)]), libFLAC_libraries="$withval", libFLAC_libraries="") +AC_ARG_WITH(libFLAC-includes,AS_HELP_STRING([--with-libFLAC-includes=DIR], [directory where libFLAC header files are installed (optional)]), libFLAC_includes="$withval", libFLAC_includes="") +AC_ARG_ENABLE(libFLACtest, AS_HELP_STRING([--disable-libFLACtest], [do not try to compile and run a test libFLAC program]),, enable_libFLACtest=yes) if test "x$libFLAC_libraries" != "x" ; then LIBFLAC_LIBS="-L$libFLAC_libraries" @@ -46,7 +46,7 @@ dnl dnl Now check if the installed libFLAC is sufficiently new. dnl rm -f conf.libFLACtest - AC_TRY_RUN([ + AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include #include #include @@ -58,7 +58,7 @@ int main () return 0; } -],, no_libFLAC=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) +]])],[],[no_libFLAC=yes],[echo $ac_n "cross compiling; assumed OK... $ac_c"]) CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi @@ -74,11 +74,10 @@ int main () echo "*** Could not run libFLAC test program, checking why..." CFLAGS="$CFLAGS $LIBFLAC_CFLAGS" LIBS="$LIBS $LIBFLAC_LIBS" - AC_TRY_LINK([ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include -], [ return 0; ], - [ echo "*** The test program compiled, but did not run. This usually means" +]], [[ return 0; ]])],[ echo "*** The test program compiled, but did not run. This usually means" echo "*** that the run-time linker is not finding libFLAC or finding the wrong" echo "*** version of libFLAC. If it is not finding libFLAC, you'll need to set your" echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" @@ -86,8 +85,7 @@ int main () echo "*** is required on your system" echo "***" echo "*** If you have an old version installed, it is best to remove it, although" - echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], - [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],[ echo "*** The test program failed to compile or link. See the file config.log for the" echo "*** exact error that occured. This usually means libFLAC was incorrectly installed" echo "*** or that you have moved libFLAC since it was installed. In the latter case, you" echo "*** may want to edit the libFLAC-config script: $LIBFLAC_CONFIG" ]) diff --git a/m4/libOggFLAC.m4 b/m4/libOggFLAC.m4 index a04623f6c..673f737da 100644 --- a/m4/libOggFLAC.m4 +++ b/m4/libOggFLAC.m4 @@ -8,10 +8,10 @@ AC_DEFUN([AM_PATH_LIBOGGFLAC], [dnl dnl Get the cflags and libraries dnl -AC_ARG_WITH(libOggFLAC,[ --with-libOggFLAC=PFX Prefix where libOggFLAC is installed (optional)], libOggFLAC_prefix="$withval", libOggFLAC_prefix="") -AC_ARG_WITH(libOggFLAC-libraries,[ --with-libOggFLAC-libraries=DIR Directory where libOggFLAC library is installed (optional)], libOggFLAC_libraries="$withval", libOggFLAC_libraries="") -AC_ARG_WITH(libOggFLAC-includes,[ --with-libOggFLAC-includes=DIR Directory where libOggFLAC header files are installed (optional)], libOggFLAC_includes="$withval", libOggFLAC_includes="") -AC_ARG_ENABLE(libOggFLACtest, [ --disable-libOggFLACtest Do not try to compile and run a test libOggFLAC program],, enable_libOggFLACtest=yes) +AC_ARG_WITH(libOggFLAC,AS_HELP_STRING([--with-libOggFLAC=PFX], [prefix where libOggFLAC is installed (optional)]), libOggFLAC_prefix="$withval", libOggFLAC_prefix="") +AC_ARG_WITH(libOggFLAC-libraries,AS_HELP_STRING([--with-libOggFLAC-libraries=DIR], [directory where libOggFLAC library is installed (optional)]), libOggFLAC_libraries="$withval", libOggFLAC_libraries="") +AC_ARG_WITH(libOggFLAC-includes,AS_HELP_STRING([--with-libOggFLAC-includes=DIR], [directory where libOggFLAC header files are installed (optional)]), libOggFLAC_includes="$withval", libOggFLAC_includes="") +AC_ARG_ENABLE(libOggFLACtest, AS_HELP_STRING([--disable-libOggFLACtest] ,[do not try to compile and run a test libOggFLAC program]),, enable_libOggFLACtest=yes) if test "x$libOggFLAC_libraries" != "x" ; then LIBOGGFLAC_LIBS="-L$libOggFLAC_libraries" @@ -46,7 +46,7 @@ dnl dnl Now check if the installed libOggFLAC is sufficiently new. dnl rm -f conf.libOggFLACtest - AC_TRY_RUN([ + AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include #include #include @@ -58,7 +58,7 @@ int main () return 0; } -],, no_libOggFLAC=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) +]])],[],[no_libOggFLAC=yes],[echo $ac_n "cross compiling; assumed OK... $ac_c"]) CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi @@ -74,11 +74,10 @@ int main () echo "*** Could not run libOggFLAC test program, checking why..." CFLAGS="$CFLAGS $LIBOGGFLAC_CFLAGS" LIBS="$LIBS $LIBOGGFLAC_LIBS" - AC_TRY_LINK([ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include -], [ return 0; ], - [ echo "*** The test program compiled, but did not run. This usually means" +]], [[ return 0; ]])],[ echo "*** The test program compiled, but did not run. This usually means" echo "*** that the run-time linker is not finding libOggFLAC or finding the wrong" echo "*** version of libOggFLAC. If it is not finding libOggFLAC, you'll need to set your" echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" @@ -86,8 +85,7 @@ int main () echo "*** is required on your system" echo "***" echo "*** If you have an old version installed, it is best to remove it, although" - echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], - [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],[ echo "*** The test program failed to compile or link. See the file config.log for the" echo "*** exact error that occured. This usually means libOggFLAC was incorrectly installed" echo "*** or that you have moved libOggFLAC since it was installed. In the latter case, you" echo "*** may want to edit the libOggFLAC-config script: $LIBOGGFLAC_CONFIG" ]) diff --git a/m4/libcurl.m4 b/m4/libcurl.m4 index 01a0575cc..66857d6e7 100644 --- a/m4/libcurl.m4 +++ b/m4/libcurl.m4 @@ -61,7 +61,7 @@ AC_DEFUN([LIBCURL_CHECK_CONFIG], AH_TEMPLATE([LIBCURL_PROTOCOL_SMTP],[Defined if libcurl supports SMTP]) AC_ARG_WITH(libcurl, - AC_HELP_STRING([--with-libcurl=PREFIX],[look for the curl library in PREFIX/lib and headers in PREFIX/include]), + AS_HELP_STRING([--with-libcurl=PREFIX],[look for the curl library in PREFIX/lib and headers in PREFIX/include]), [_libcurl_with=$withval],[_libcurl_with=ifelse([$1],,[yes],[$1])]) if test "$_libcurl_with" != "no" ; then diff --git a/m4/ogg.m4 b/m4/ogg.m4 index 0e1f1abf5..3dca22e83 100644 --- a/m4/ogg.m4 +++ b/m4/ogg.m4 @@ -9,10 +9,10 @@ AC_DEFUN([XIPH_PATH_OGG], [dnl dnl Get the cflags and libraries dnl -AC_ARG_WITH(ogg,[ --with-ogg=PFX Prefix where libogg is installed (optional)], ogg_prefix="$withval", ogg_prefix="") -AC_ARG_WITH(ogg-libraries,[ --with-ogg-libraries=DIR Directory where libogg library is installed (optional)], ogg_libraries="$withval", ogg_libraries="") -AC_ARG_WITH(ogg-includes,[ --with-ogg-includes=DIR Directory where libogg header files are installed (optional)], ogg_includes="$withval", ogg_includes="") -AC_ARG_ENABLE(oggtest, [ --disable-oggtest Do not try to compile and run a test Ogg program],, enable_oggtest=yes) +AC_ARG_WITH(ogg,AS_HELP_STRING([--with-ogg=PFX], [prefix where libogg is installed (optional)]), ogg_prefix="$withval", ogg_prefix="") +AC_ARG_WITH(ogg-libraries,AS_HELP_STRING([--with-ogg-libraries=DIR], [directory where libogg library is installed (optional)]), ogg_libraries="$withval", ogg_libraries="") +AC_ARG_WITH(ogg-includes,AS_HELP_STRING([--with-ogg-includes=DIR], [directory where libogg header files are installed (optional)]), ogg_includes="$withval", ogg_includes="") +AC_ARG_ENABLE(oggtest, AS_HELP_STRING([--disable-oggtest], [do not try to compile and run a test Ogg program]),, enable_oggtest=yes) if test "x$ogg_libraries" != "x" ; then OGG_LIBS="-L$ogg_libraries" @@ -45,7 +45,7 @@ dnl dnl Now check if the installed Ogg is sufficiently new. dnl rm -f conf.oggtest - AC_TRY_RUN([ + AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include #include #include @@ -57,7 +57,7 @@ int main () return 0; } -],, no_ogg=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) +]])],[],[no_ogg=yes],[echo $ac_n "cross compiling; assumed OK... $ac_c"]) CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi @@ -73,11 +73,10 @@ int main () echo "*** Could not run Ogg test program, checking why..." CFLAGS="$CFLAGS $OGG_CFLAGS" LIBS="$LIBS $OGG_LIBS" - AC_TRY_LINK([ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include -], [ return 0; ], - [ echo "*** The test program compiled, but did not run. This usually means" +]], [[ return 0; ]])],[ echo "*** The test program compiled, but did not run. This usually means" echo "*** that the run-time linker is not finding Ogg or finding the wrong" echo "*** version of Ogg. If it is not finding Ogg, you'll need to set your" echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" @@ -85,8 +84,7 @@ int main () echo "*** is required on your system" echo "***" echo "*** If you have an old version installed, it is best to remove it, although" - echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], - [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],[ echo "*** The test program failed to compile or link. See the file config.log for the" echo "*** exact error that occured. This usually means Ogg was incorrectly installed" echo "*** or that you have moved Ogg since it was installed." ]) CFLAGS="$ac_save_CFLAGS" diff --git a/m4/quakeforge.m4 b/m4/quakeforge.m4 index 2c3139733..c5cbc59e2 100644 --- a/m4/quakeforge.m4 +++ b/m4/quakeforge.m4 @@ -5,9 +5,7 @@ dnl AC_HAVE_STRUCT_FIELD(struct, field, headers) AC_DEFUN([AC_HAVE_STRUCT_FIELD], [ define(cache_val, translit(ac_cv_type_$1_$2, [A-Z ], [a-z_])) AC_CACHE_CHECK([for $2 in $1], cache_val,[ -AC_TRY_COMPILE([$3],[$1 x; x.$2;], -cache_val=yes, -cache_val=no)]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[$3]], [[$1 x; x.$2;]])],[cache_val=yes],[cache_val=no])]) if test "$cache_val" = yes; then define(foo, translit(HAVE_$1_$2, [a-z ], [A-Z_])) AC_DEFINE(foo, 1, [Define if $1 has field $2.]) @@ -20,18 +18,15 @@ dnl Checks if function/macro va_copy() is available dnl Defines HAVE_VA_COPY on success. AC_DEFUN([AC_FUNC_VA_COPY], [AC_CACHE_CHECK([for va_copy], ac_cv_func_va_copy, - [AC_TRY_LINK([ + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #ifdef HAVE_STDARG_H # include #else # include -#endif], - [ +#endif]], [[ va_list a, b; -va_copy(a, b);], - [ac_cv_func_va_copy=yes], - [ac_cv_func_va_copy=no])]) +va_copy(a, b);]])],[ac_cv_func_va_copy=yes],[ac_cv_func_va_copy=no])]) if test $ac_cv_func_va_copy = yes; then AC_DEFINE(HAVE_VA_COPY, 1, [Define if va_copy is available]) fi]) @@ -40,18 +35,15 @@ dnl Checks if function/macro __va_copy() is available dnl Defines HAVE__VA_COPY on success. AC_DEFUN([AC_FUNC__VA_COPY], [AC_CACHE_CHECK([for __va_copy], ac_cv_func__va_copy, - [AC_TRY_LINK([ + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #ifdef HAVE_STDARG_H # include #else # include -#endif], - [ +#endif]], [[ va_list a, b; -__va_copy(a, b);], - [ac_cv_func__va_copy=yes], - [ac_cv_func__va_copy=no])]) +__va_copy(a, b);]])],[ac_cv_func__va_copy=yes],[ac_cv_func__va_copy=no])]) if test $ac_cv_func__va_copy = yes; then AC_DEFINE(HAVE__VA_COPY, 1, [Define if __va_copy is available]) fi]) @@ -60,19 +52,16 @@ dnl Checks if va_list is an array dnl Defines VA_LIST_IS_ARRAY on success. AC_DEFUN([AC_TYPE_VA_LIST], [AC_CACHE_CHECK([if va_list is an array], ac_cv_type_va_list_array, - [AC_TRY_LINK([ + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #ifdef HAVE_STDARG_H # include #else # include #endif -], - [ +]], [[ va_list a, b; -a = b;], - [ac_cv_type_va_list_array=no], - [ac_cv_type_va_list_array=yes])]) +a = b;]])],[ac_cv_type_va_list_array=no],[ac_cv_type_va_list_array=yes])]) if test $ac_cv_type_va_list_array = yes; then AC_DEFINE(VA_LIST_IS_ARRAY, 1, [Define if va_list is an array]) fi]) @@ -93,7 +82,7 @@ AC_DEFUN([QF_NEED], [m4_map_args_w([$2], [$1_need_], [=yes], [;])]) AC_DEFUN([QF_PROCESS_NEED_subroutine], [m4_foreach_w([qfn_need], [$5], [if test x"${$2[_need_]qfn_need}" = xyes; then - $4="${$4} $1[]qfn_need[]$3" + $4="${$4} m4_join([/],[$6],[$1])[]qfn_need[]$3" fi ])]) @@ -105,13 +94,13 @@ fi ])]) AC_DEFUN([QF_PROCESS_NEED_LIBS], -[m4_define([qfn_ext], m4_default($3,[la])) -QF_PROCESS_NEED_subroutine([lib$1_],[$1],[.]qfn_ext,[$1_libs],[$2]) +[m4_define([qfn_ext], m4_default($4,[la])) +QF_PROCESS_NEED_subroutine([lib$1_],[$1],[.]qfn_ext,[$1_libs],[$2],[$3]) QF_SUBST([$1_libs])]) -AC_DEFUN([QF_PROCESS_NEED_DIRS], -[QF_PROCESS_NEED_subroutine([],[$1],[],[$1_dirs],[$2]) -QF_SUBST([$1_dirs])]) +AC_DEFUN([QF_PROCESS_NEED_LIST], +[QF_PROCESS_NEED_subroutine([],[$1],[],[$1_list],[$2]) +QF_SUBST([$1_list])]) AC_DEFUN([QF_DEFAULT_PLUGIN], [m4_define([qfn_default], m4_default($3,$1)[_default]) @@ -122,11 +111,11 @@ AC_DEFINE_UNQUOTED(m4_toupper(qfn_default), ["${qfn_default}"], [Define to defau ]) AC_DEFUN([QF_PROCESS_NEED_PLUGINS], -[QF_PROCESS_NEED_subroutine([$1_],[$1],[.la],[$1_plugins],[$2]) +[QF_PROCESS_NEED_subroutine([$1_],[$1],[.la],[$1_plugins],[$2],[$3]) QF_SUBST([$1_plugins]) -QF_DEFAULT_PLUGIN([$1],[$2],[$3]) -AC_DEFINE_UNQUOTED(m4_toupper(m4_default($3,$1)[_plugin_protos]), [], [list of $1 plugin prototypes]) -AC_DEFINE_UNQUOTED(m4_toupper(m4_default($3,$1)[_plugin_list]), [{0, 0}], [list of $1 plugins]) +QF_DEFAULT_PLUGIN([$1],[$2],[$4]) +AC_DEFINE_UNQUOTED(m4_toupper(m4_default($4,$1)[_plugin_protos]), [], [list of $1 plugin prototypes]) +AC_DEFINE_UNQUOTED(m4_toupper(m4_default($4,$1)[_plugin_list]), [{0, 0}], [list of $1 plugins]) ]) AC_DEFUN([QF_STATIC_PLUGIN_LIBS], @@ -147,7 +136,7 @@ fi AC_DEFINE_UNQUOTED(m4_toupper([$1_plugin_list]), [${$1_plugin_list}], [list of $1 plugins])]) AC_DEFUN([QF_PROCESS_NEED_STATIC_PLUGINS], -[QF_PROCESS_NEED_subroutine([$1_],[$1],[.la],m4_default($4,$1)[_static_plugins],[$2]) +[QF_PROCESS_NEED_subroutine([$1_],[$1],[.la],m4_default($4,$1)[_static_plugins],[$2],[$3]) QF_SUBST(m4_default($4,$1)[_static_plugins]) QF_DEFAULT_PLUGIN([$1],[$2],[$4]) QF_STATIC_PLUGIN_LIBS(m4_default($4,$1),[$1],[$2],[$3]) @@ -155,10 +144,8 @@ QF_STATIC_PLUGIN_PROTOS(m4_default($4,$1),[$1],[$2]) QF_STATIC_PLUGIN_LIST(m4_default($4,$1),[$1],[$2])]) AC_DEFUN([QF_WITH_TARGETS], [ -AC_ARG_WITH($1, - [$2] - [$3], $1="$withval", $1=all -) +AC_ARG_WITH($1, AS_HELP_STRING([--with-$1=], [$2:] [$3]), + $1="$withval", $1=all) if test "x${$1}" = "xall"; then for qf_t in `echo '$3' | sed -e "s/,/ /g"`''; do eval ENABLE_$1_${qf_t}=yes @@ -179,13 +166,10 @@ AC_MSG_CHECKING(whether $1 works) save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $1" qf_opt_ok=no -AC_TRY_COMPILE( - [], - [], - qf_opt_ok=yes - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) -) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[void func(void);]], [[func();]])], +[qf_opt_ok=yes + AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no) +]) CFLAGS="$save_CFLAGS" if test "x$qf_opt_ok" = xyes; then true diff --git a/m4/sdl.m4 b/m4/sdl.m4 index 0928a9ddf..6b7117e2e 100644 --- a/m4/sdl.m4 +++ b/m4/sdl.m4 @@ -12,11 +12,11 @@ AC_DEFUN([AM_PATH_SDL], [dnl dnl Get the cflags and libraries from the sdl-config script dnl -AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)], +AC_ARG_WITH(sdl-prefix,AS_HELP_STRING([--with-sdl-prefix=PFX], [prefix where SDL is installed (optional)]), sdl_prefix="$withval", sdl_prefix="") -AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)], +AC_ARG_WITH(sdl-exec-prefix,AS_HELP_STRING([--with-sdl-exec-prefix=PFX], [exec prefix where SDL is installed (optional)]), sdl_exec_prefix="$withval", sdl_exec_prefix="") -AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run a test SDL program], +AC_ARG_ENABLE(sdltest, AS_HELP_STRING([--disable-sdltest] ,[do not try to compile and run a test SDL program]), , enable_sdltest=yes) if test x$sdl_exec_prefix != x ; then @@ -63,7 +63,7 @@ dnl Now check if the installed SDL is sufficiently new. (Also sanity dnl checks the results of sdl-config to some extent dnl rm -f conf.sdltest - AC_TRY_RUN([ + AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include #include #include @@ -120,7 +120,7 @@ int main (int argc, char *argv[]) } } -],, no_sdl=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) +]])],[],[no_sdl=yes],[echo $ac_n "cross compiling; assumed OK... $ac_c"]) CFLAGS="$ac_save_CFLAGS" CXXFLAGS="$ac_save_CXXFLAGS" LIBS="$ac_save_LIBS" @@ -144,7 +144,7 @@ int main (int argc, char *argv[]) CFLAGS="$CFLAGS $SDL_CFLAGS" CXXFLAGS="$CXXFLAGS $SDL_CFLAGS" LIBS="$LIBS $SDL_LIBS" - AC_TRY_LINK([ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include "SDL.h" @@ -152,8 +152,7 @@ int main(int argc, char *argv[]) { return 0; } #undef main #define main K_and_R_C_main -], [ return 0; ], - [ echo "*** The test program compiled, but did not run. This usually means" +]], [[ return 0; ]])],[ echo "*** The test program compiled, but did not run. This usually means" echo "*** that the run-time linker is not finding SDL or finding the wrong" echo "*** version of SDL. If it is not finding SDL, you'll need to set your" echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" @@ -161,8 +160,7 @@ int main(int argc, char *argv[]) echo "*** is required on your system" echo "***" echo "*** If you have an old version installed, it is best to remove it, although" - echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], - [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],[ echo "*** The test program failed to compile or link. See the file config.log for the" echo "*** exact error that occured. This usually means SDL was incorrectly installed" echo "*** or that you have moved SDL since it was installed. In the latter case, you" echo "*** may want to edit the sdl-config script: $SDL_CONFIG" ]) diff --git a/m4/vorbis.m4 b/m4/vorbis.m4 index 300cc6c7d..b504b5d4c 100644 --- a/m4/vorbis.m4 +++ b/m4/vorbis.m4 @@ -10,10 +10,10 @@ AC_DEFUN([XIPH_PATH_VORBIS], [dnl dnl Get the cflags and libraries dnl -AC_ARG_WITH(vorbis,[ --with-vorbis=PFX Prefix where libvorbis is installed (optional)], vorbis_prefix="$withval", vorbis_prefix="") -AC_ARG_WITH(vorbis-libraries,[ --with-vorbis-libraries=DIR Directory where libvorbis library is installed (optional)], vorbis_libraries="$withval", vorbis_libraries="") -AC_ARG_WITH(vorbis-includes,[ --with-vorbis-includes=DIR Directory where libvorbis header files are installed (optional)], vorbis_includes="$withval", vorbis_includes="") -AC_ARG_ENABLE(vorbistest, [ --disable-vorbistest Do not try to compile and run a test Vorbis program],, enable_vorbistest=yes) +AC_ARG_WITH(vorbis,AS_HELP_STRING([--with-vorbis=PFX], [prefix where libvorbis is installed (optional)]), vorbis_prefix="$withval", vorbis_prefix="") +AC_ARG_WITH(vorbis-libraries, AS_HELP_STRING([--with-vorbis-libraries=DIR]. [directory where libvorbis library is installed (optional)]), vorbis_libraries="$withval", vorbis_libraries="") +AC_ARG_WITH(vorbis-includes,AS_HELP_STRING([--with-vorbis-includes=DIR], [directory where libvorbis header files are installed (optional)]), vorbis_includes="$withval", vorbis_includes="") +AC_ARG_ENABLE(vorbistest, AS_HELP_STRING([--disable-vorbistest], [do not try to compile and run a test Vorbis program]),, enable_vorbistest=yes) if test "x$vorbis_libraries" != "x" ; then VORBIS_LIBS="-L$vorbis_libraries" @@ -49,7 +49,7 @@ dnl dnl Now check if the installed Vorbis is sufficiently new. dnl rm -f conf.vorbistest - AC_TRY_RUN([ + AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include #include #include @@ -73,7 +73,7 @@ int main () return 0; } -],, no_vorbis=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) +]])],[],[no_vorbis=yes],[echo $ac_n "cross compiling; assumed OK... $ac_c"]) CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi @@ -89,11 +89,10 @@ int main () echo "*** Could not run Vorbis test program, checking why..." CFLAGS="$CFLAGS $VORBIS_CFLAGS" LIBS="$LIBS $VORBIS_LIBS $OGG_LIBS" - AC_TRY_LINK([ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include -], [ return 0; ], - [ echo "*** The test program compiled, but did not run. This usually means" +]], [[ return 0; ]])],[ echo "*** The test program compiled, but did not run. This usually means" echo "*** that the run-time linker is not finding Vorbis or finding the wrong" echo "*** version of Vorbis. If it is not finding Vorbis, you'll need to set your" echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" @@ -101,8 +100,7 @@ int main () echo "*** is required on your system" echo "***" echo "*** If you have an old version installed, it is best to remove it, although" - echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], - [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],[ echo "*** The test program failed to compile or link. See the file config.log for the" echo "*** exact error that occured. This usually means Vorbis was incorrectly installed" echo "*** or that you have moved Vorbis since it was installed." ]) CFLAGS="$ac_save_CFLAGS" diff --git a/m4/xmms.m4 b/m4/xmms.m4 index f4c03a73b..2f52673f2 100644 --- a/m4/xmms.m4 +++ b/m4/xmms.m4 @@ -61,9 +61,9 @@ function vercmp(ver1, ver2, ver1arr, ver2arr, \ AC_DEFUN([AM_PATH_XMMS], [ -AC_ARG_WITH(xmms-prefix,[ --with-xmms-prefix=PFX Prefix where XMMS is installed (optional)], +AC_ARG_WITH(xmms-prefix,AS_HELP_STRING([--with-xmms-prefix=PFX], [prefix where XMMS is installed (optional)]), xmms_config_prefix="$withval", xmms_config_prefix="") -AC_ARG_WITH(xmms-exec-prefix,[ --with-xmms-exec-prefix=PFX Exec prefix where XMMS is installed (optional)], +AC_ARG_WITH(xmms-exec-prefix,AS_HELP_STRING([--with-xmms-exec-prefix=PFX], [exec prefix where XMMS is installed (optional)]), xmms_config_exec_prefix="$withval", xmms_config_exec_prefix="") if test x$xmms_config_exec_prefix != x; then diff --git a/nq/Makefile.am b/nq/Makefile.am deleted file mode 100644 index 77e3cbf5b..000000000 --- a/nq/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -## Process this file with automake to produce Makefile.in -AUTOMAKE_OPTIONS= foreign - -SUBDIRS= include source diff --git a/nq/Makemodule.am b/nq/Makemodule.am new file mode 100644 index 000000000..7e28f6401 --- /dev/null +++ b/nq/Makemodule.am @@ -0,0 +1,3 @@ +## Process this file with automake to produce Makefile.in +include nq/include/Makemodule.am +include nq/source/Makemodule.am diff --git a/nq/include/Makefile.am b/nq/include/Makefile.am deleted file mode 100644 index 60374efe3..000000000 --- a/nq/include/Makefile.am +++ /dev/null @@ -1,5 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -## Process this file with automake to produce Makefile.in -EXTRA_DIST= chase.h cl_skin.h client.h game.h host.h protocol.h server.h \ - sv_pr_cmds.h sv_progs.h diff --git a/nq/include/Makemodule.am b/nq/include/Makemodule.am new file mode 100644 index 000000000..6956660cc --- /dev/null +++ b/nq/include/Makemodule.am @@ -0,0 +1,10 @@ +## Process this file with automake to produce Makefile.in +EXTRA_DIST += \ + nq/include/cl_skin.h \ + nq/include/client.h \ + nq/include/game.h \ + nq/include/host.h \ + nq/include/protocol.h \ + nq/include/server.h \ + nq/include/sv_pr_cmds.h \ + nq/include/sv_progs.h diff --git a/nq/include/client.h b/nq/include/client.h index e6dc80e97..0114410ef 100644 --- a/nq/include/client.h +++ b/nq/include/client.h @@ -30,39 +30,24 @@ #include -#include "QF/input.h" #include "QF/mathlib.h" #include "QF/model.h" #include "QF/quakefs.h" -#include "QF/sound.h" #include "QF/render.h" +#include "QF/scene/entity.h" + +#include "client/chase.h" #include "client/entities.h" +#include "client/input.h" +#include "client/state.h" +#include "client/view.h" #include "game.h" #include "netmain.h" #include "protocol.h" -typedef struct usercmd_s { - vec3_t viewangles; - -// intended velocities - float forwardmove; - float sidemove; - float upmove; -} usercmd_t; - -typedef struct { - struct info_s *info; - struct info_key_s *name; - float entertime; - int frags; - int topcolor; - int bottomcolor; -} scoreboard_t; - - // client_state_t should hold all pieces of the client state typedef enum { @@ -77,7 +62,6 @@ typedef enum { #define MAX_DEMONAME 16 typedef enum { - ca_dedicated, // a dedicated server with no ability to start a client ca_disconnected, // full screen console with no connection ca_connected, // talking to a server ca_active, // everything is in, so frames can be rendered @@ -109,11 +93,11 @@ typedef struct { char demos[MAX_DEMOS][MAX_DEMONAME]; // when not playing QFile *demofile; - qboolean demorecording; - qboolean demo_capture; - qboolean demoplayback; + int demo_capture; int forcetrack; // -1 = use normal cd track - qboolean timedemo; + bool demorecording; + bool demoplayback; + bool timedemo; int td_lastframe; // to meter out one message a frame int td_startframe; // host_framecount at start double td_starttime; // realtime at second frame of timedemo @@ -142,9 +126,7 @@ extern client_static_t cls; /* the client_state_t structure is wiped completely at every server signon */ -typedef struct { - qboolean loading; - +typedef struct client_state_s { int movemessages; // Since connecting to this server throw out // the first couple, so the player doesn't // accidentally do something the first frame @@ -153,39 +135,27 @@ typedef struct { // information for local display int stats[MAX_CL_STATS]; // Health, etc float item_gettime[32]; // cl.time of aquiring item, for blinking - float faceanimtime; // Use anim frame if cl.time < this - - cshift_t cshifts[NUM_CSHIFTS]; // Color shifts for damage, powerups - cshift_t prev_cshifts[NUM_CSHIFTS]; // and content types + float faceanimtime; // Use anim frame if cl.time < this // The client maintains its own idea of view angles, which are sent to the // server each frame. The server sets punchangle when the view is temporarily // offset, and an angle reset commands at the start of each level and after // teleporting. - int mindex; - vec3_t mviewangles[2]; // During demo playback viewangles is lerped + int frameIndex; + vec3_t frameViewAngles[2]; // During demo playback viewangles is lerped // between these - vec3_t viewangles; - vec3_t mvelocity[2]; // Update by server, used for lean+bob + vec4f_t frameVelocity[2]; // Update by server, used for lean+bob // (0 is newest) - vec3_t velocity; // Lerped between mvelocity[0] and [1] - vec3_t punchangle; // Temporary offset + viewstate_t viewstate; + movestate_t movestate; + chasestate_t chasestate; -// pitch drifting vars - float idealpitch; - float pitchvel; - qboolean nodrift; - float driftmove; - double laststop; - - qboolean paused; // Sent over by server - int onground; - float viewheight; + bool paused; // Sent over by server + bool inwater; float crouch; // Local amount for smoothing stepups - qboolean inwater; int intermission; // Don't change view angle, full screen, etc - int completed_time; // Latched at intermission start + int completed_time; // Latched from time at intermission start double mtime[2]; // The timestamp of last two messages double time; // Clients view of time, should be between @@ -194,89 +164,60 @@ typedef struct { double oldtime; // Previous cl.time, time-oldtime is used // to decay light values and smooth step ups - float last_received_message; // (realtime) for net trouble icon + double last_ping_request; // while showing scoreboard /* information that is static for the entire time connected to a server */ - struct model_s *model_precache[MAX_MODELS]; struct sfx_s *sound_precache[MAX_SOUNDS]; - int nummodels; int numsounds; - struct plitem_s *edicts; - struct plitem_s *worldspawn; - char levelname[40]; // for display on solo scoreboard int spectator; + int playernum; int viewentity; // cl_entitites[cl.viewentity] = player unsigned protocol; - int gametype; + float stdver; int maxclients; - int chase; + // serverinfo mirrors int sv_cshifts; - int watervis; + int no_pogo_stick; + int teamplay; int fpd; + int fbskins; // refresh related state - struct model_s *worldmodel; // cl_entitites[0].model int num_entities; // held in cl_entities array - entity_t viewent; // the weapon model int cdtrack; // cd audio -// frag scoreboard - scoreboard_t *scores; // [cl.maxclients] +// all player information + player_info_t *players; lightstyle_t lightstyle[MAX_LIGHTSTYLES]; } client_state_t; // cvars -extern struct cvar_s *cl_name; -extern struct cvar_s *cl_color; +extern char *cl_name; +extern int cl_color; -extern struct cvar_s *cl_upspeed; -extern struct cvar_s *cl_forwardspeed; -extern struct cvar_s *cl_backspeed; -extern struct cvar_s *cl_sidespeed; +extern int cl_shownet; +extern int cl_nolerp; -extern struct cvar_s *cl_movespeedkey; +extern char *cl_name; +extern int cl_writecfg; -extern struct cvar_s *cl_yawspeed; -extern struct cvar_s *cl_pitchspeed; +extern int cl_cshift_bonus; +extern int cl_cshift_contents; +extern int cl_cshift_damage; +extern int cl_cshift_powerup; -extern struct cvar_s *cl_anglespeedkey; - -extern struct cvar_s *cl_autofire; - -extern struct cvar_s *cl_shownet; -extern struct cvar_s *cl_nolerp; - -extern struct cvar_s *hud_sbar; - -extern struct cvar_s *cl_pitchdriftspeed; -extern struct cvar_s *lookspring; - -extern struct cvar_s *m_pitch; -extern struct cvar_s *m_yaw; -extern struct cvar_s *m_forward; -extern struct cvar_s *m_side; - -extern struct cvar_s *cl_name; -extern struct cvar_s *cl_writecfg; - -extern struct cvar_s *cl_cshift_bonus; -extern struct cvar_s *cl_cshift_contents; -extern struct cvar_s *cl_cshift_damage; -extern struct cvar_s *cl_cshift_powerup; - -extern struct cvar_s *noskins; +extern int noskins; extern client_state_t cl; -// FIXME, allocate dynamically -extern entity_t cl_entities[MAX_EDICTS]; +extern struct entity_s cl_entities[MAX_EDICTS]; extern double cl_msgtime[MAX_EDICTS]; -extern byte cl_forcelink[MAX_EDICTS]; +extern struct set_s cl_forcelink; extern int fps_count; @@ -286,7 +227,10 @@ extern void (*write_angles) (sizebuf_t *sb, const vec3_t angles); struct cbuf_s; void CL_Init (struct cbuf_s *cbuf); void CL_InitCvars (void); -void CL_Shutdown (void); +void CL_ClearMemory (void); +void CL_PreFrame (void); +void CL_Frame (void); +int CL_ReadConfiguration (const char *cfg_name); void CL_EstablishConnection (const char *host); void CL_Signon1 (void); @@ -300,22 +244,17 @@ void CL_NextDemo (void); // cl_input -void CL_Input_Init (void); +void CL_Init_Input (struct cbuf_s *cbuf); +void CL_Init_Input_Cvars (void); void CL_SendCmd (void); void CL_SendMove (usercmd_t *cmd); -void CL_ParseParticleEffect (void); -void CL_ParseTEnt (void); -void CL_UpdateTEnts (void); - void CL_ClearState (void); int CL_ReadFromServer (void); void CL_WriteToServer (usercmd_t *cmd); void CL_BaseMove (usercmd_t *cmd); -float CL_KeyState (kbutton_t *key); - // cl_demo.c void CL_StopPlayback (void); void CL_StopRecording (void); @@ -323,8 +262,8 @@ void CL_Record (const char *argv1, int track); int CL_GetMessage (void); void CL_Demo_Init (void); -extern struct cvar_s *demo_gzip; -extern struct cvar_s *demo_speed; +extern int demo_gzip; +extern float demo_speed; // cl_parse.c struct skin_s; @@ -332,40 +271,18 @@ void CL_ParseServerMessage (void); void CL_NewTranslation (int slot, struct skin_s *skin); -// view -void V_StartPitchDrift (void); -void V_StopPitchDrift (void); - -void V_RenderView (void); -void V_UpdatePalette (void); -void V_Register (void); -void V_ParseDamage (void); -void V_SetContentsColor (int contents); -void V_PrepBlend (void); - // cl_tent -void CL_TEnts_Init (void); -void CL_ClearTEnts (void); -void CL_Init_Entity (struct entity_s *ent); -void CL_ParseTEnt (void); void CL_SignonReply (void); -void CL_TransformEntity (struct entity_s *ent, const vec3_t - angles, qboolean force); void CL_RelinkEntities (void); void CL_ClearEnts (void); - -extern kbutton_t in_left, in_right, in_forward, in_back; -extern kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright; -extern kbutton_t in_use, in_jump, in_attack; -extern kbutton_t in_up, in_down; +struct entity_s CL_GetEntity (int num); extern double realtime; -extern qboolean recording; +extern bool recording; -void Cvar_Info (struct cvar_s *var); - -void CL_UpdateScreen (double realtime); +struct cvar_s; +void Cvar_Info (void *data, const struct cvar_s *cvar); void CL_SetState (cactive_t state); diff --git a/nq/include/game.h b/nq/include/game.h index 44eb7ee19..bf7238efc 100644 --- a/nq/include/game.h +++ b/nq/include/game.h @@ -59,93 +59,7 @@ #define SAVEGAME_COMMENT_LENGTH 39 -// -// stats are integers communicated to the client by the server -// -#define MAX_CL_STATS 32 -#define STAT_HEALTH 0 -#define STAT_FRAGS 1 -#define STAT_WEAPON 2 -#define STAT_AMMO 3 -#define STAT_ARMOR 4 -#define STAT_WEAPONFRAME 5 -#define STAT_SHELLS 6 -#define STAT_NAILS 7 -#define STAT_ROCKETS 8 -#define STAT_CELLS 9 -#define STAT_ACTIVEWEAPON 10 -#define STAT_TOTALSECRETS 11 -#define STAT_TOTALMONSTERS 12 -#define STAT_SECRETS 13 // bumped on client side by svc_foundsecret -#define STAT_MONSTERS 14 // bumped by svc_killedmonster -#define STAT_ITEMS 15 - -// stock defines - -#define IT_SHOTGUN 1 -#define IT_SUPER_SHOTGUN 2 -#define IT_NAILGUN 4 -#define IT_SUPER_NAILGUN 8 -#define IT_GRENADE_LAUNCHER 16 -#define IT_ROCKET_LAUNCHER 32 -#define IT_LIGHTNING 64 -#define IT_SUPER_LIGHTNING 128 -#define IT_SHELLS 256 -#define IT_NAILS 512 -#define IT_ROCKETS 1024 -#define IT_CELLS 2048 -#define IT_AXE 4096 -#define IT_ARMOR1 8192 -#define IT_ARMOR2 16384 -#define IT_ARMOR3 32768 -#define IT_SUPERHEALTH 65536 -#define IT_KEY1 131072 -#define IT_KEY2 262144 -#define IT_INVISIBILITY 524288 -#define IT_INVULNERABILITY 1048576 -#define IT_SUIT 2097152 -#define IT_QUAD 4194304 -#define IT_SIGIL1 (1<<28) -#define IT_SIGIL2 (1<<29) -#define IT_SIGIL3 (1<<30) -#define IT_SIGIL4 (1<<31) - -//=========================================== -//rogue changed and added defines - -#define RIT_SHELLS 128 -#define RIT_NAILS 256 -#define RIT_ROCKETS 512 -#define RIT_CELLS 1024 -#define RIT_AXE 2048 -#define RIT_LAVA_NAILGUN 4096 -#define RIT_LAVA_SUPER_NAILGUN 8192 -#define RIT_MULTI_GRENADE 16384 -#define RIT_MULTI_ROCKET 32768 -#define RIT_PLASMA_GUN 65536 -#define RIT_ARMOR1 8388608 -#define RIT_ARMOR2 16777216 -#define RIT_ARMOR3 33554432 -#define RIT_LAVA_NAILS 67108864 -#define RIT_PLASMA_AMMO 134217728 -#define RIT_MULTI_ROCKETS 268435456 -#define RIT_SHIELD 536870912 -#define RIT_ANTIGRAV 1073741824 -#define RIT_SUPERHEALTH 2147483648 - -//MED 01/04/97 added hipnotic defines -//=========================================== -//hipnotic added defines -#define HIT_PROXIMITY_GUN_BIT 16 -#define HIT_MJOLNIR_BIT 7 -#define HIT_LASER_CANNON_BIT 23 -#define HIT_PROXIMITY_GUN (1<edata)) #define EDICT_FROM_AREA(l) (STRUCT_FROM_LINK(l,sv_data_t,area)->edict) static inline void -sv_pr_touch (edict_t *self, edict_t *other) +sv_pr_exec (edict_t *self, edict_t *other, pr_func_t func) { pr_int_t this; *sv_globals.self = EDICT_TO_PROG (&sv_pr_state, self); *sv_globals.other = EDICT_TO_PROG (&sv_pr_state, other); if ((this = sv_pr_state.fields.this) != -1) { + PR_PushFrame (&sv_pr_state); PR_RESET_PARAMS (&sv_pr_state); P_INT (&sv_pr_state, 0) = E_POINTER (self, this); P_INT (&sv_pr_state, 1) = 0; - P_INT (&sv_pr_state, 2) = E_POINTER (other, this); + P_INT (&sv_pr_state, 2) = other ? E_POINTER (other, this) : 0; } - PR_ExecuteProgram (&sv_pr_state, SVfunc (self, touch)); + PR_ExecuteProgram (&sv_pr_state, func); + if ((this = sv_pr_state.fields.this) != -1) { + PR_PopFrame (&sv_pr_state); + } +} + +static inline void +sv_pr_touch (edict_t *self, edict_t *other) +{ + sv_pr_exec (self, other, SVfunc (self, touch)); } static inline void @@ -253,33 +242,13 @@ sv_pr_use (edict_t *self, edict_t *other) static inline void sv_pr_think (edict_t *self) { - pr_int_t this; - - *sv_globals.self = EDICT_TO_PROG (&sv_pr_state, self); - *sv_globals.other = 0; - if ((this = sv_pr_state.fields.this) != -1) { - PR_RESET_PARAMS (&sv_pr_state); - P_INT (&sv_pr_state, 0) = E_POINTER (self, this); - P_INT (&sv_pr_state, 1) = 0; - P_INT (&sv_pr_state, 2) = 0; - } - PR_ExecuteProgram (&sv_pr_state, SVfunc (self, think)); + sv_pr_exec (self, 0, SVfunc (self, think)); } static inline void sv_pr_blocked (edict_t *self, edict_t *other) { - pr_int_t this; - - *sv_globals.self = EDICT_TO_PROG (&sv_pr_state, self); - *sv_globals.other = EDICT_TO_PROG (&sv_pr_state, other); - if ((this = sv_pr_state.fields.this) != -1) { - PR_RESET_PARAMS (&sv_pr_state); - P_INT (&sv_pr_state, 0) = E_POINTER (self, this); - P_INT (&sv_pr_state, 1) = 0; - P_INT (&sv_pr_state, 2) = E_POINTER (other, this); - } - PR_ExecuteProgram (&sv_pr_state, SVfunc (self, blocked)); + sv_pr_exec (self, other, SVfunc (self, blocked)); } #endif // __sv_progs_h diff --git a/nq/source/Makefile.am b/nq/source/Makefile.am deleted file mode 100644 index 71cb290e8..000000000 --- a/nq/source/Makefile.am +++ /dev/null @@ -1,176 +0,0 @@ -## Process this file with automake to produce Makefile.in -# -# Makefile.am -# -# Automake-using build system for QuakeForge -# -# Copyright (C) 2000 Jeff Teunissen -# -# This Makefile is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# -# See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to: -# -# Free Software Foundation, Inc. -# 59 Temple Place - Suite 330 -# Boston, MA 02111-1307, USA -# -# $Id$ -# -AUTOMAKE_OPTIONS= foreign - -# Stuff that is common to both client and server -# -AM_CPPFLAGS= -I$(top_srcdir)/include -I$(top_srcdir)/nq/include -SDL_LIBS = @SDL_LIBS@ -AM_CFLAGS= @PTHREAD_CFLAGS@ - -bin_PROGRAMS= @NQ_TARGETS@ - -EXTRA_PROGRAMS= nq-fbdev nq-sdl nq-svga nq-wgl nq-x11 nq-server - -noinst_LIBRARIES= @nq_libs@ -EXTRA_LIBRARIES=libnq_client.a libnq_common.a libnq_sdl.a libnq_server.a - -libnq_common_a_SOURCES=game.c world.c -libnq_sdl_a_SOURCES= sys_sdl.c -libnq_sdl_a_CFLAGS= $(SDL_CFLAGS) - -common_ldflags= -export-dynamic @PTHREAD_LDFLAGS@ - -cl_plugin_LIBS= \ - @server_static_plugin_libs@ \ - @client_static_plugin_libs@ - -client_LIBFILES= \ - $(top_builddir)/libs/video/targets/libQFjs.la \ - $(top_builddir)/libs/audio/libQFcd.la \ - $(top_builddir)/libs/audio/libQFsound.la - -server_LIBFILES= \ - @server_static_plugin_libs@ \ - $(top_builddir)/libs/models/libQFmodels.la - -common_LIBFILES= \ - $(top_builddir)/libs/net/libnet_main.la \ - $(top_builddir)/libs/console/libQFconsole.la \ - $(top_builddir)/libs/image/libQFimage.la \ - $(top_builddir)/libs/gib/libQFgib.la \ - $(top_builddir)/libs/ruamoko/libQFruamoko.la \ - $(top_builddir)/libs/util/libQFutil.la - -client_LIBS= $(client_LIBFILES) $(common_LIBFILES) - -server_LIBS= $(server_LIBFILES) $(common_LIBFILES) $(NET_LIBS) -server_LIB_DEPS=$(server_LIBFILES) $(common_LIBFILES) - -libnq_client_a_SOURCES= \ - cl_chase.c cl_cmd.c cl_demo.c cl_ents.c cl_input.c cl_main.c \ - cl_screen.c cl_parse.c cl_tent.c cl_view.c sbar.c - -libnq_server_a_SOURCES= \ - host.c host_cmd.c sv_cl_phys.c sv_cvar.c sv_main.c \ - sv_move.c sv_phys.c sv_pr_cmds.c sv_progs.c sv_user.c - -client_libs= libnq_server.a libnq_client.a libnq_common.a \ - $(top_builddir)/libs/client/libQFclient.la -server_libs = libnq_server.a libnq_common.a - -# Software-rendering targets - -# ... Linux FBDev -nq_fbdev_libs= \ - $(client_libs) \ - $(cl_plugin_LIBS) \ - $(top_builddir)/libs/video/renderer/libQFrenderer.la \ - $(top_builddir)/libs/models/libQFmodels.la \ - $(top_builddir)/libs/video/targets/libQFfbdev.la \ - $(client_LIBS) -nq_fbdev_SOURCES= sys_unix.c -nq_fbdev_LDADD= $(nq_fbdev_libs) $(NET_LIBS) -nq_fbdev_LDFLAGS= $(common_ldflags) -nq_fbdev_DEPENDENCIES= $(nq_fbdev_libs) - -# ... SDL, version 1.2 and higher -nq_sdl_libs= \ - libnq_sdl.a \ - $(client_libs) \ - $(cl_plugin_LIBS) \ - $(top_builddir)/libs/video/renderer/libQFrenderer.la \ - $(top_builddir)/libs/models/libQFmodels.la \ - $(top_builddir)/libs/video/targets/libQFsdl.la \ - $(client_LIBS) -nq_sdl_SOURCES=sdl_link.c -nq_sdl_LDADD= $(nq_sdl_libs) $(SDL_LIBS) $(NET_LIBS) -nq_sdl_LDFLAGS= $(common_ldflags) -nq_sdl_DEPENDENCIES= $(nq_sdl_libs) - -# ... Linux SVGAlib -nq_svga_libs= \ - $(client_libs) \ - $(cl_plugin_LIBS) \ - $(top_builddir)/libs/video/renderer/libQFrenderer.la \ - $(top_builddir)/libs/models/libQFmodels.la \ - $(top_builddir)/libs/video/targets/libQFsvga.la \ - $(client_LIBS) -nq_svga_SOURCES= sys_unix.c -nq_svga_LDADD= $(nq_svga_libs) $(SVGA_LIBS) $(NET_LIBS) -nq_svga_LDFLAGS= $(common_ldflags) -nq_svga_DEPENDENCIES= $(nq_svga_libs) - -# ... X11 -nq_x11_libs= \ - $(client_libs) \ - $(cl_plugin_LIBS) \ - $(top_builddir)/libs/video/renderer/libQFrenderer.la \ - $(top_builddir)/libs/models/libQFmodels.la \ - $(top_builddir)/libs/video/targets/libQFx11.la \ - $(client_LIBS) -nq_x11_SOURCES= sys_unix.c -nq_x11_LDADD= $(nq_x11_libs) \ - $(VIDMODE_LIBS) $(DGA_LIBS) $(X_LIBS) -lX11 \ - $(X_EXTRA_LIBS) $(X_SHM_LIB) $(NET_LIBS) $(DL_LIBS) -nq_x11_LDFLAGS= $(common_ldflags) -nq_x11_DEPENDENCIES= $(nq_x11_libs) - -# OpenGL-using targets - -# ... SGI/Microsoft WGL (Windows OpenGL) -nq_wgl_libs= \ - $(client_libs) \ - $(cl_plugin_LIBS) \ - $(opengl_QFLIBS) \ - $(top_builddir)/libs/video/targets/libQFwgl.la \ - $(client_LIBS) -nq_wgl_SOURCES= sys_win.c -nq_wgl_LDADD= $(nq_wgl_libs) -lgdi32 -lcomctl32 -lwinmm $(NET_LIBS) -nq_wgl_LDFLAGS= $(common_ldflags) -nq_wgl_DEPENDENCIES= $(nq_wgl_libs) - -# Dedicated Server -if SYSTYPE_WIN32 -ded_sources= sys_wind.c sv_ded.c -else -ded_sources= sys_unixd.c sv_ded.c -endif -EXTRA_DIST=sys_wind.c sys_unixd.c sv_ded.c - -nq_server_LDFLAGS= $(common_ldflags) -nq_server_SOURCES= $(ded_sources) -nq_server_LDADD= $(server_libs) $(server_LIBS) -nq_server_DEPENDENCIES= $(server_libs) $(server_LIB_DEPS) - -# Stuff that doesn't get linked into an executable NEEDS to be mentioned here, -# or it won't be distributed with 'make dist' - -# Kill the temp files, hopefully. -CLEANFILES = *.i *.s $(YACCLEX_CLEANFILES) diff --git a/nq/source/Makemodule.am b/nq/source/Makemodule.am new file mode 100644 index 000000000..e4dd9a47f --- /dev/null +++ b/nq/source/Makemodule.am @@ -0,0 +1,169 @@ +## Process this file with automake to produce Makefile.in +# +# Makefile.am +# +# Automake-using build system for QuakeForge +# +# Copyright (C) 2000 Jeff Teunissen +# +# This Makefile is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to: +# +# Free Software Foundation, Inc. +# 59 Temple Place - Suite 330 +# Boston, MA 02111-1307, USA +# +# $Id$ +# + +# Stuff that is common to both client and server +# +bin_PROGRAMS += @NQ_TARGETS@ + +EXTRA_PROGRAMS += nq-fbdev nq-sdl nq-svga nq-win nq-x11 nq-server + +noinst_LIBRARIES += @nq_libs@ +EXTRA_LIBRARIES += nq/source/libnq_client.a nq/source/libnq_common.a nq/source/libnq_sdl.a nq/source/libnq_server.a + +nq_source_libnq_common_a_SOURCES= nq/source/game.c nq/source/world.c +nq_source_libnq_sdl_a_SOURCES= nq/source/sys_sdl.c +nq_source_libnq_sdl_a_CFLAGS= $(SDL_CFLAGS) + +nq_cl_plugin_LIBS= \ + @server_static_plugin_libs@ \ + @client_static_plugin_libs@ + +nq_client_LIBFILES= \ + libs/gib/libQFgib_client.la \ + libs/input/libQFinput.la \ + libs/audio/libQFcd.la \ + libs/audio/libQFsound.la + +nq_server_LIBFILES= \ + @server_static_plugin_libs@ \ + libs/models/libQFmodels.la + +nq_common_LIBFILES= \ + libs/scene/libQFscene.la \ + libs/net/libnet_main.la \ + libs/console/libQFconsole.la \ + libs/image/libQFimage.la \ + libs/gib/libQFgib.la \ + libs/ruamoko/libQFruamoko.la \ + libs/gamecode/libQFgamecode.la \ + libs/ui/libQFui.la \ + libs/ecs/libQFecs.la \ + libs/util/libQFutil.la + +nq_client_LIBS= $(nq_client_LIBFILES) $(nq_common_LIBFILES) + +nq_server_LIBS= $(nq_server_LIBFILES) $(nq_common_LIBFILES) $(NET_LIBS) +nq_server_LIB_DEPS=$(nq_server_LIBFILES) $(nq_common_LIBFILES) + +nq_source_libnq_client_a_SOURCES= \ + nq/source/cl_cmd.c nq/source/cl_demo.c nq/source/cl_ents.c nq/source/cl_input.c nq/source/cl_main.c \ + nq/source/cl_parse.c + +nq_source_libnq_server_a_SOURCES= \ + nq/source/host.c nq/source/host_cmd.c nq/source/sv_cl_phys.c nq/source/sv_cvar.c nq/source/sv_main.c \ + nq/source/sv_move.c nq/source/sv_phys.c nq/source/sv_pr_cmds.c nq/source/sv_progs.c nq/source/sv_user.c + +nq_client_libs= nq/source/libnq_server.a nq/source/libnq_client.a nq/source/libnq_common.a \ + libs/client/libQFclient.la +server_libs = nq/source/libnq_server.a nq/source/libnq_common.a + +# Software-rendering targets + +# ... Linux FBDev +nq_fbdev_libs= \ + $(nq_client_libs) \ + $(nq_cl_plugin_LIBS) \ + libs/video/renderer/libQFrenderer.la \ + libs/models/libQFmodels.la \ + libs/video/targets/libQFfbdev.la \ + $(nq_client_LIBS) +nq_fbdev_SOURCES= nq/source/sys_unix.c +nq_fbdev_LDADD= $(nq_fbdev_libs) $(NET_LIBS) +nq_fbdev_LDFLAGS= $(common_ldflags) +nq_fbdev_DEPENDENCIES= $(nq_fbdev_libs) + +# ... SDL, version 1.2 and higher +nq_sdl_libs= \ + nq/source/libnq_sdl.a \ + $(nq_client_libs) \ + $(nq_cl_plugin_LIBS) \ + libs/video/renderer/libQFrenderer.la \ + libs/models/libQFmodels.la \ + libs/video/targets/libQFsdl.la \ + $(nq_client_LIBS) +nq_sdl_SOURCES=nq/source/sdl_link.c +nq_sdl_LDADD= $(nq_sdl_libs) $(SDL_LIBS) $(NET_LIBS) +nq_sdl_LDFLAGS= $(common_ldflags) +nq_sdl_DEPENDENCIES= $(nq_sdl_libs) + +# ... Linux SVGAlib +nq_svga_libs= \ + $(nq_client_libs) \ + $(nq_cl_plugin_LIBS) \ + libs/video/renderer/libQFrenderer.la \ + libs/models/libQFmodels.la \ + libs/video/targets/libQFsvga.la \ + $(nq_client_LIBS) +nq_svga_SOURCES= nq/source/sys_unix.c +nq_svga_LDADD= $(nq_svga_libs) $(SVGA_LIBS) $(NET_LIBS) +nq_svga_LDFLAGS= $(common_ldflags) +nq_svga_DEPENDENCIES= $(nq_svga_libs) + +# ... X11 +nq_x11_libs= \ + $(nq_client_libs) \ + $(nq_cl_plugin_LIBS) \ + libs/video/renderer/libQFrenderer.la \ + libs/models/libQFmodels.la \ + libs/video/targets/libQFx11.la \ + $(nq_client_LIBS) +nq_x11_SOURCES= nq/source/sys_unix.c +nq_x11_LDADD= $(nq_x11_libs) \ + $(VIDMODE_LIBS) $(DGA_LIBS) ${XFIXES_LIBS} $(XI2_LIBS) $(X_LIBS) \ + -lX11 $(X_EXTRA_LIBS) $(X_SHM_LIB) $(NET_LIBS) $(DL_LIBS) +nq_x11_LDFLAGS= $(common_ldflags) +nq_x11_DEPENDENCIES= $(nq_x11_libs) + +# OpenGL-using targets + +# ... SGI/Microsoft WGL (Windows OpenGL) +nq_win_libs= \ + $(nq_client_libs) \ + $(nq_cl_plugin_LIBS) \ + libs/video/renderer/libQFrenderer.la \ + libs/models/libQFmodels.la \ + libs/video/targets/libQFwin.la \ + $(nq_client_LIBS) +nq_win_SOURCES= nq/source/sys_win.c +nq_win_LDADD= $(nq_win_libs) -lgdi32 -lcomctl32 -lwinmm $(NET_LIBS) +nq_win_LDFLAGS= $(common_ldflags) +nq_win_DEPENDENCIES= $(nq_win_libs) + +# Dedicated Server +if SYSTYPE_WIN32 +ded_sources= nq/source/sys_wind.c nq/source/sv_ded.c +else +ded_sources= nq/source/sys_unixd.c nq/source/sv_ded.c +endif +EXTRA_DIST += nq/source/sys_wind.c nq/source/sys_unixd.c nq/source/sv_ded.c + +nq_server_LDFLAGS= $(common_ldflags) +nq_server_SOURCES= $(ded_sources) +nq_server_LDADD= $(server_libs) $(nq_server_LIBS) +nq_server_DEPENDENCIES= $(server_libs) $(nq_server_LIB_DEPS) diff --git a/nq/source/cl_chase.c b/nq/source/cl_chase.c deleted file mode 100644 index c492ff609..000000000 --- a/nq/source/cl_chase.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - cl_chase.c - - chase camera support - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif - -#include "QF/cvar.h" -#include "QF/keys.h" -#include "QF/input.h" -#include "QF/mathlib.h" - -#include "QF/plugin/vid_render.h" - -#include "chase.h" -#include "client.h" -#include "world.h" - -vec3_t camera_origin = {0,0,0}; -vec3_t camera_angles = {0,0,0}; -vec3_t player_origin = {0,0,0}; -vec3_t player_angles = {0,0,0}; - -vec3_t chase_angles; -vec3_t chase_dest; -vec3_t chase_dest_angles; -vec3_t chase_pos; - -cvar_t *chase_back; -cvar_t *chase_up; -cvar_t *chase_right; -cvar_t *chase_active; - - -void -Chase_Init_Cvars (void) -{ - chase_back = Cvar_Get ("chase_back", "100", CVAR_NONE, NULL, "None"); - chase_up = Cvar_Get ("chase_up", "16", CVAR_NONE, NULL, "None"); - chase_right = Cvar_Get ("chase_right", "0", CVAR_NONE, NULL, "None"); - chase_active = Cvar_Get ("chase_active", "0", CVAR_NONE, NULL, "None"); -} - -void -Chase_Reset (void) -{ - // for respawning and teleporting - // start position 12 units behind head -} - -static inline void -TraceLine (vec3_t start, vec3_t end, vec3_t impact) -{ - trace_t trace; - - memset (&trace, 0, sizeof (trace)); - trace.fraction = 1; - MOD_TraceLine (cl.worldmodel->hulls, 0, start, end, &trace); - - VectorCopy (trace.endpos, impact); -} - -void -Chase_Update (void) -{ - float pitch, yaw, fwd; - int i; - usercmd_t cmd; // movement direction - vec3_t forward, up, right, stop, dir; - - // lazy camera, look toward player entity - - if (chase_active->int_val == 2 || chase_active->int_val == 3) { - // control camera angles with key/mouse/joy-look - - camera_angles[PITCH] += cl.viewangles[PITCH] - player_angles[PITCH]; - camera_angles[YAW] += cl.viewangles[YAW] - player_angles[YAW]; - camera_angles[ROLL] += cl.viewangles[ROLL] - player_angles[ROLL]; - - if (chase_active->int_val == 2) { - if (camera_angles[PITCH] < -60) - camera_angles[PITCH] = -60; - if (camera_angles[PITCH] > 60) - camera_angles[PITCH] = 60; - } - - // move camera, it's not enough to just change the angles because - // the angles are automatically changed to look toward the player - - if (chase_active->int_val == 3) - VectorCopy (r_data->refdef->vieworg, player_origin); - - AngleVectors (camera_angles, forward, right, up); - VectorScale (forward, chase_back->value, forward); - VectorSubtract (player_origin, forward, camera_origin); - - if (chase_active->int_val == 2) { - VectorCopy (r_data->refdef->vieworg, player_origin); - - // don't let camera get too low - if (camera_origin[2] < player_origin[2] + chase_up->value) - camera_origin[2] = player_origin[2] + chase_up->value; - } - - // don't let camera get too far from player - - VectorSubtract (camera_origin, player_origin, dir); - VectorCopy (dir, forward); - VectorNormalize (forward); - - if (VectorLength (dir) > chase_back->value) { - VectorScale (forward, chase_back->value, dir); - VectorAdd (player_origin, dir, camera_origin); - } - - // check for walls between player and camera - - VectorScale (forward, 8, forward); - VectorAdd (camera_origin, forward, camera_origin); - TraceLine (player_origin, camera_origin, stop); - if (VectorLength (stop) != 0) - VectorSubtract (stop, forward, camera_origin); - - VectorSubtract (camera_origin, r_data->refdef->vieworg, dir); - VectorCopy (dir, forward); - VectorNormalize (forward); - - if (chase_active->int_val == 2) { - if (dir[1] == 0 && dir[0] == 0) { - // look straight up or down -// camera_angles[YAW] = r_data->refdef->viewangles[YAW]; - if (dir[2] > 0) - camera_angles[PITCH] = 90; - else - camera_angles[PITCH] = 270; - } else { - yaw = (atan2 (dir[1], dir[0]) * 180 / M_PI); - if (yaw < 0) - yaw += 360; - if (yaw < 180) - yaw += 180; - else - yaw -= 180; - camera_angles[YAW] = yaw; - - fwd = sqrt (dir[0] * dir[0] + dir[1] * dir[1]); - pitch = (atan2 (dir[2], fwd) * 180 / M_PI); - if (pitch < 0) - pitch += 360; - camera_angles[PITCH] = pitch; - } - } - - VectorCopy (camera_angles, r_data->refdef->viewangles);// rotate camera - VectorCopy (camera_origin, r_data->refdef->vieworg); // move camera - - // get basic movement from keyboard - - memset (&cmd, 0, sizeof (cmd)); -// VectorCopy (cl.viewangles, cmd.angles); - - if (in_strafe.state & 1) { - cmd.sidemove += cl_sidespeed->value * CL_KeyState (&in_right); - cmd.sidemove -= cl_sidespeed->value * CL_KeyState (&in_left); - } - cmd.sidemove += cl_sidespeed->value * CL_KeyState (&in_moveright); - cmd.sidemove -= cl_sidespeed->value * CL_KeyState (&in_moveleft); - - if (!(in_klook.state & 1)) { - cmd.forwardmove += cl_forwardspeed->value - * CL_KeyState (&in_forward); - cmd.forwardmove -= cl_backspeed->value * CL_KeyState (&in_back); - } - if (in_speed.state & 1) { - cmd.forwardmove *= cl_movespeedkey->value; - cmd.sidemove *= cl_movespeedkey->value; - } - - // mouse and joystick controllers add to movement - VectorSet (0, cl.viewangles[1] - camera_angles[1], 0, dir); - AngleVectors (dir, forward, right, up); - VectorScale (forward, viewdelta.position[2] * m_forward->value, - forward); - VectorScale (right, viewdelta.position[0] * m_side->value, right); - VectorAdd (forward, right, dir); - cmd.forwardmove += dir[0]; - cmd.sidemove -= dir[1]; - - VectorSet (0, camera_angles[1], 0, dir); - AngleVectors (dir, forward, right, up); - - VectorScale (forward, cmd.forwardmove, forward); - VectorScale (right, cmd.sidemove, right); - VectorAdd (forward, right, dir); - - if (dir[1] || dir[0]) { - cl.viewangles[YAW] = (atan2 (dir[1], dir[0]) * 180 / M_PI); - if (cl.viewangles[YAW] < 0) cl.viewangles[YAW] += 360; -// if (cl.viewangles[YAW] < 180) -// cl.viewangles[YAW] += 180; -// else -// cl.viewangles[YAW] -= 180; - } - - cl.viewangles[PITCH] = 0; - - // remember the new angle to calculate the difference next frame - VectorCopy (cl.viewangles, player_angles); - - return; - } - - // regular camera, faces same direction as player - - AngleVectors (cl.viewangles, forward, right, up); - - // calc exact destination - for (i = 0; i < 3; i++) - camera_origin[i] = r_data->refdef->vieworg[i] - - forward[i] * chase_back->value - right[i] * chase_right->value; - camera_origin[2] += chase_up->value; - - // check for walls between player and camera - TraceLine (r_data->refdef->vieworg, camera_origin, stop); - if (VectorLength (stop) != 0) - for (i = 0; i < 3; i++) - camera_origin[i] = stop[i] + forward[i] * 8; - - VectorCopy (camera_origin, r_data->refdef->vieworg); -} diff --git a/nq/source/cl_cmd.c b/nq/source/cl_cmd.c index fabc6e226..33689dc5e 100644 --- a/nq/source/cl_cmd.c +++ b/nq/source/cl_cmd.c @@ -40,7 +40,7 @@ #include "QF/sizebuf.h" #include "QF/sys.h" -#include "client.h" +#include "nq/include/client.h" /* diff --git a/nq/source/cl_demo.c b/nq/source/cl_demo.c index 026c59fbb..2dc647d9f 100644 --- a/nq/source/cl_demo.c +++ b/nq/source/cl_demo.c @@ -46,9 +46,14 @@ #include "QF/sys.h" #include "QF/va.h" -#include "client.h" +#include "QF/scene/scene.h" + #include "compat.h" -#include "host.h" + +#include "client/world.h" + +#include "nq/include/client.h" +#include "nq/include/host.h" typedef struct { int frames; @@ -56,25 +61,59 @@ typedef struct { double fps; } td_stats_t; -int demo_timeframes_isactive; -int demo_timeframes_index; -char demoname[1024]; -double *demo_timeframes_array; +static int demo_timeframes_isactive; +static int demo_timeframes_index; +static dstring_t *demoname; +static double *demo_timeframes_array; #define CL_TIMEFRAMES_ARRAYBLOCK 4096 -int timedemo_count; -int timedemo_runs; -td_stats_t *timedemo_data; +static int timedemo_count; +static int timedemo_runs; +static td_stats_t *timedemo_data; static void CL_FinishTimeDemo (void); static void CL_TimeFrames_DumpLog (void); static void CL_TimeFrames_AddTimestamp (void); static void CL_TimeFrames_Reset (void); -cvar_t *demo_gzip; -cvar_t *demo_speed; -cvar_t *demo_quit; -cvar_t *demo_timeframes; +int demo_gzip; +static cvar_t demo_gzip_cvar = { + .name = "demo_gzip", + .description = + "Compress demos using gzip. 0 = none, 1 = least compression, 9 = most " + "compression. Compressed demos (1-9) will have .gz appended to the " + "name", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &demo_gzip }, +}; +float demo_speed; +static cvar_t demo_speed_cvar = { + .name = "demo_speed", + .description = + "adjust demo playback speed. 1.0 = normal, < 1 slow-mo, > 1 timelapse", + .default_value = "1.0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &demo_speed }, +}; +int demo_quit; +static cvar_t demo_quit_cvar = { + .name = "demo_quit", + .description = + "automaticly quit after a timedemo has finished", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &demo_quit }, +}; +int demo_timeframes; +static cvar_t demo_timeframes_cvar = { + .name = "demo_timeframes", + .description = + "write timestamps for every frame", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &demo_timeframes }, +}; #define MAX_DEMMSG (MAX_MSGLEN) @@ -104,7 +143,7 @@ CL_WriteDemoMessage (sizebuf_t *msg) len = LittleLong (msg->cursize); Qwrite (cls.demofile, &len, 4); for (i = 0; i < 3; i++) { - f = LittleFloat (cl.viewangles[i]); + f = LittleFloat (cl.viewstate.player_angles[i]); Qwrite (cls.demofile, &f, 4); } Qwrite (cls.demofile, msg->data, msg->cursize); @@ -128,6 +167,7 @@ CL_StopPlayback (void) CL_SetState (ca_disconnected); cls.demo_capture = 0; cls.demoplayback = 0; + cl.viewstate.demoplayback = 0; if (cls.timedemo) CL_FinishTimeDemo (); @@ -172,7 +212,8 @@ check_next_demopacket (void) static int read_demopacket (void) { - int i, r; + int i; + unsigned r; float f; Qread (cls.demofile, &net_message->message->cursize, 4); @@ -181,10 +222,14 @@ read_demopacket (void) if (net_message->message->cursize > MAX_DEMMSG) Host_Error ("Demo message > MAX_DEMMSG: %d/%d", net_message->message->cursize, MAX_DEMMSG); - VectorCopy (cl.mviewangles[0], cl.mviewangles[1]); + VectorCopy (cl.frameViewAngles[0], cl.frameViewAngles[1]); for (i = 0; i < 3; i++) { r = Qread (cls.demofile, &f, 4); - cl.mviewangles[0][i] = LittleFloat (f); + if (r != 4) { + CL_StopPlayback (); + return 0; + } + cl.frameViewAngles[0][i] = LittleFloat (f); } r = Qread (cls.demofile, net_message->message->data, net_message->message->cursize); @@ -307,7 +352,7 @@ CL_Record_f (void) // start up the map if (c > 2) - Cmd_ExecuteString (va ("map %s", Cmd_Argv (2)), src_command); + Cmd_ExecuteString (va (0, "map %s", Cmd_Argv (2)), src_command); CL_Record (Cmd_Argv (1), track); } @@ -332,8 +377,8 @@ demo_default_name (const char *argv1) time (&tim); strftime (timestring, 19, "%Y-%m-%d-%H-%M", localtime (&tim)); - // the leading path-name is to be removed from cl.worldmodel->name - mapname = QFS_SkipPath (cl.worldmodel->name); + // the leading path-name is to be removed from cl_world.worldmodel->name + mapname = QFS_SkipPath (cl_world.scene->worldmodel->path); // the map name is cut off after any "." because this would prevent // an extension being appended @@ -362,9 +407,9 @@ CL_Record (const char *argv1, int track) // open the demo file #ifdef HAVE_ZLIB - if (demo_gzip->int_val) { + if (demo_gzip) { QFS_DefaultExtension (name, ".dem.gz"); - cls.demofile = QFS_WOpen (name->str, demo_gzip->int_val); + cls.demofile = QFS_WOpen (name->str, demo_gzip); } else #endif { @@ -444,13 +489,13 @@ CL_StartDemo (void) { dstring_t *name; int c; - qboolean neg = false; + bool neg = false; // disconnect from server CL_Disconnect (); // open the demo file - name = dstring_strdup (demoname); + name = dstring_strdup (demoname->str); QFS_DefaultExtension (name, ".dem"); Sys_Printf ("Playing demo from %s.\n", name->str); @@ -471,9 +516,9 @@ CL_StartDemo (void) } cls.demoplayback = true; + cl.viewstate.demoplayback = 1; CL_SetState (ca_connected); cls.forcetrack = 0; - Key_SetKeyDest (key_demo); while ((c = Qgetc (cls.demofile)) != '\n') if (c == '-') @@ -499,7 +544,7 @@ CL_PlayDemo_f (void) switch (Cmd_Argc ()) { case 1: - if (!demoname[0]) + if (!demoname->str[0]) goto playdemo_error; // fall through case 2: @@ -519,7 +564,7 @@ playdemo_error: timedemo_runs = timedemo_count = 1; // make sure looped timedemos stop if (Cmd_Argc () > 1) - strncpy (demoname, Cmd_Argv (1), sizeof (demoname)); + dstring_copystr (demoname, Cmd_Argv (1)); CL_StartDemo (); } @@ -537,7 +582,7 @@ CL_StartTimeDemo (void) cls.td_lastframe = -1; // get a new message this frame CL_TimeFrames_Reset (); - if (demo_timeframes->int_val) + if (demo_timeframes) demo_timeframes_isactive = 1; } @@ -596,7 +641,7 @@ CL_FinishTimeDemo (void) Sys_Printf (" min/max fps: %.3f/%.3f\n", min, max); Sys_Printf ("std deviation: %.3f fps\n", sqrt (variance)); } - if (demo_quit->int_val) + if (demo_quit) Cbuf_InsertText (host_cbuf, "quit\n"); } } @@ -626,8 +671,7 @@ CL_TimeDemo_f (void) free (timedemo_data); timedemo_data = 0; } - timedemo_data = calloc (timedemo_runs, sizeof (td_stats_t)); - strncpy (demoname, Cmd_Argv (1), sizeof (demoname)); + dstring_copystr (demoname, Cmd_Argv (1)); CL_StartTimeDemo (); timedemo_runs = timedemo_count = max (count, 1); timedemo_data = calloc (timedemo_runs, sizeof (td_stats_t)); @@ -636,21 +680,15 @@ CL_TimeDemo_f (void) void CL_Demo_Init (void) { + demoname = dstring_newstr (); demo_timeframes_isactive = 0; demo_timeframes_index = 0; demo_timeframes_array = NULL; - demo_gzip = Cvar_Get ("demo_gzip", "0", CVAR_ARCHIVE, NULL, - "Compress demos using gzip. 0 = none, 1 = least " - "compression, 9 = most compression. Compressed " - " demos (1-9) will have .gz appended to the name"); - demo_speed = Cvar_Get ("demo_speed", "1.0", CVAR_NONE, NULL, - "adjust demo playback speed. 1.0 = normal, " - "< 1 slow-mo, > 1 timelapse"); - demo_quit = Cvar_Get ("demo_quit", "0", CVAR_NONE, NULL, - "automaticly quit after a timedemo has finished"); - demo_timeframes = Cvar_Get ("demo_timeframes", "0", CVAR_NONE, NULL, - "write timestamps for every frame"); + Cvar_Register (&demo_gzip_cvar, 0, 0); + Cvar_Register (&demo_speed_cvar, 0, 0); + Cvar_Register (&demo_quit_cvar, 0, 0); + Cvar_Register (&demo_timeframes_cvar, 0, 0); Cmd_AddCommand ("record", CL_Record_f, "Record a demo, if no filename " "argument is given\n" "the demo will be called Year-Month-Day-Hour-Minute-" diff --git a/nq/source/cl_ents.c b/nq/source/cl_ents.c index b6bac93cd..b09a2977e 100644 --- a/nq/source/cl_ents.c +++ b/nq/source/cl_ents.c @@ -35,7 +35,7 @@ #include "QF/input.h" #include "QF/keys.h" #include "QF/msg.h" -#include "QF/qfplist.h" +#include "QF/plist.h" #include "QF/render.h" #include "QF/screen.h" #include "QF/skin.h" @@ -43,99 +43,67 @@ #include "QF/va.h" #include "QF/plugin/vid_render.h" +#include "QF/scene/entity.h" +#include "QF/scene/scene.h" -#include "chase.h" -#include "client.h" #include "compat.h" -#include "host.h" -#include "host.h" -#include "server.h" -entity_t cl_entities[MAX_EDICTS]; -double cl_msgtime[MAX_EDICTS]; -byte cl_forcelink[MAX_EDICTS]; +#include "client/effects.h" +#include "client/temp_entities.h" +#include "client/world.h" + +#include "client/chase.h" + +#include "nq/include/client.h" +#include "nq/include/host.h" +#include "nq/include/host.h" +#include "nq/include/server.h" + +entity_t cl_entities[MAX_EDICTS]; +double cl_msgtime[MAX_EDICTS]; +static byte forcelink_bytes[SET_SIZE(MAX_EDICTS)]; +#define alloc_forcelink(s) (set_bits_t *)forcelink_bytes +set_t cl_forcelink = SET_STATIC_INIT (MAX_EDICTS, alloc_forcelink); +#undef alloc_forcelink void CL_ClearEnts (void) { size_t i; + for (i = 0; i < MAX_EDICTS; i++) { + if (Entity_Valid (cl_entities[i])) { + entity_t ent = cl_entities[i]; + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); + if (renderer && renderer->skin) { + mod_funcs->Skin_Free (renderer->skin); + } + Scene_DestroyEntity (cl_world.scene, cl_entities[i]); + cl_entities[i] = nullentity; + } + } + // clear other arrays - memset (cl_entities, 0, sizeof (cl_entities)); i = nq_entstates.num_frames * nq_entstates.num_entities; memset (nq_entstates.frame[0], 0, i * sizeof (entity_state_t)); - memset (cl_msgtime, 0, sizeof (cl_msgtime)); - memset (cl_forcelink, 0, sizeof (cl_forcelink)); - - for (i = 0; i < MAX_EDICTS; i++) - CL_Init_Entity (cl_entities + i); + memset (cl_msgtime, -1, sizeof (cl_msgtime)); + set_empty (&cl_forcelink); } -static void -CL_NewDlight (int key, vec3_t org, int effects, byte glow_size, - byte glow_color) +static entity_t +CL_GetInvalidEntity (int num) { - float radius; - dlight_t *dl; - static quat_t normal = {0.4, 0.2, 0.05, 0.7}; - static quat_t red = {0.5, 0.05, 0.05, 0.7}; - static quat_t blue = {0.05, 0.05, 0.5, 0.7}; - static quat_t purple = {0.5, 0.05, 0.5, 0.7}; + return cl_entities[num]; +} - effects &= EF_BLUE | EF_RED | EF_BRIGHTLIGHT | EF_DIMLIGHT; - if (!effects) { - if (!glow_size) - return; - } - - dl = r_funcs->R_AllocDlight (key); - if (!dl) - return; - VectorCopy (org, dl->origin); - - if (effects & (EF_BLUE | EF_RED | EF_BRIGHTLIGHT | EF_DIMLIGHT)) { - radius = 200 + (rand () & 31); - if (effects & EF_BRIGHTLIGHT) { - radius += 200; - dl->origin[2] += 16; - } - if (effects & EF_DIMLIGHT) - if (effects & ~EF_DIMLIGHT) - radius -= 100; - dl->radius = radius; - dl->die = cl.time + 0.1; - - switch (effects & (EF_RED | EF_BLUE)) { - case EF_RED | EF_BLUE: - QuatCopy (purple, dl->color); - break; - case EF_RED: - QuatCopy (red, dl->color); - break; - case EF_BLUE: - QuatCopy (blue, dl->color); - break; - default: - QuatCopy (normal, dl->color); - break; - } - } - - if (glow_size) { - dl->radius += glow_size < 128 ? glow_size * 8.0 : - (glow_size - 256) * 8.0; - dl->die = cl.time + 0.1; - if (glow_color) { - if (glow_color == 255) { - dl->color[0] = dl->color[1] = dl->color[2] = 1.0; - } else { - byte *tempcolor; - - tempcolor = (byte *) &d_8to24table[glow_color]; - VectorScale (tempcolor, 1 / 255.0, dl->color); - } - } +entity_t +CL_GetEntity (int num) +{ + if (!Entity_Valid (cl_entities[num])) { + cl_entities[num] = Scene_CreateEntity (cl_world.scene); + CL_Init_Entity (cl_entities[num]); } + return cl_entities[num]; } /* @@ -151,7 +119,7 @@ CL_LerpPoint (void) f = cl.mtime[0] - cl.mtime[1]; - if (!f || cl_nolerp->int_val || cls.timedemo || sv.active) { + if (!f || cl_nolerp || cls.timedemo || sv.active) { cl.time = cl.mtime[0]; return 1; } @@ -175,230 +143,154 @@ CL_LerpPoint (void) return frac; } -void -CL_TransformEntity (entity_t *ent, const vec3_t angles, qboolean force) -{ - vec3_t ang; - vec_t *forward, *left, *up; - - if (VectorIsZero (angles)) { - VectorSet (1, 0, 0, ent->transform + 0); - VectorSet (0, 1, 0, ent->transform + 4); - VectorSet (0, 0, 1, ent->transform + 8); - } else if (force || !VectorCompare (angles, ent->angles)) { - forward = ent->transform + 0; - left = ent->transform + 4; - up = ent->transform + 8; - VectorCopy (angles, ang); - if (ent->model && ent->model->type == mod_alias) { - // stupid quake bug - // why, oh, why, do alias models pitch in the opposite direction - // to everything else? - ang[PITCH] = -ang[PITCH]; - } - AngleVectors (ang, forward, left, up); - VectorNegate (left, left); // AngleVectors is right-handed - } - VectorCopy (angles, ent->angles); - ent->transform[3] = 0; - ent->transform[7] = 0; - ent->transform[11] = 0; - VectorCopy (ent->origin, ent->transform + 12); - ent->transform[15] = 1; -} - static void -CL_ModelEffects (entity_t *ent, int num, int glow_color) +set_entity_model (int ent_ind, int modelindex) { - dlight_t *dl; - model_t *model = ent->model; - - if (model->flags & EF_ROCKET) { - dl = r_funcs->R_AllocDlight (num); - if (dl) { - VectorCopy (ent->origin, dl->origin); - dl->radius = 200.0; - dl->die = cl.time + 0.1; - //FIXME VectorCopy (r_firecolor->vec, dl->color); - VectorSet (0.9, 0.7, 0.0, dl->color); - dl->color[3] = 0.7; - } - r_funcs->particles->R_RocketTrail (ent); - } else if (model->flags & EF_GRENADE) - r_funcs->particles->R_GrenadeTrail (ent); - else if (model->flags & EF_GIB) - r_funcs->particles->R_BloodTrail (ent); - else if (model->flags & EF_ZOMGIB) - r_funcs->particles->R_SlightBloodTrail (ent); - else if (model->flags & EF_TRACER) - r_funcs->particles->R_WizTrail (ent); - else if (model->flags & EF_TRACER2) - r_funcs->particles->R_FlameTrail (ent); - else if (model->flags & EF_TRACER3) - r_funcs->particles->R_VoorTrail (ent); - else if (model->flags & EF_GLOWTRAIL) - if (r_funcs->particles->R_GlowTrail) - r_funcs->particles->R_GlowTrail (ent, glow_color); -} - -static void -CL_EntityEffects (int num, entity_t *ent, entity_state_t *state) -{ - dlight_t *dl; - - if (state->effects & EF_BRIGHTFIELD) - r_funcs->particles->R_EntityParticles (ent); - if (state->effects & EF_MUZZLEFLASH) { - vec_t *fv = ent->transform; - - dl = r_funcs->R_AllocDlight (num); - if (dl) { - VectorMultAdd (ent->origin, 18, fv, dl->origin); - dl->origin[2] += 16; - dl->radius = 200 + (rand () & 31); - dl->die = cl.time + 0.1; - dl->minlight = 32; - dl->color[0] = 0.2; - dl->color[1] = 0.1; - dl->color[2] = 0.05; - dl->color[3] = 0.7; - } - } -} - -static void -set_entity_model (entity_t *ent, int modelindex) -{ - int i = ent - cl_entities; - ent->model = cl.model_precache[modelindex]; + entity_t ent = cl_entities[ent_ind]; + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, ent.reg); + renderer->model = cl_world.models.a[modelindex]; // automatic animation (torches, etc) can be either all together // or randomized - if (ent->model) { - if (ent->model->synctype == ST_RAND) - ent->syncbase = (float) (rand () & 0x7fff) / 0x7fff; - else - ent->syncbase = 0.0; - } else { - cl_forcelink[i] = true; // hack to make null model players work + if (renderer->model) { + if (renderer->model->synctype == ST_RAND) { + animation->syncbase = (float) (rand () & 0x7fff) / 0x7fff; + } else { + animation->syncbase = 0.0; + } + } + // Changing the model can change the visibility of the entity and even + // the model type + SET_ADD (&cl_forcelink, ent_ind); + animation->nolerp = 1; // don't try to lerp when the model has changed + if (ent_ind <= cl.maxclients) { + renderer->skin = mod_funcs->Skin_SetColormap (renderer->skin, ent_ind); } - if (i <= cl.maxclients) - ent->skin = mod_funcs->Skin_SetColormap (ent->skin, i); } void CL_RelinkEntities (void) { - entity_t *ent; + entity_t ent; entity_state_t *new, *old; - float bobjrotate, frac, f, d; + float bobjrotate, frac, f; int i, j; - int entvalid; - vec3_t delta; int model_flags; - r_data->player_entity = &cl_entities[cl.viewentity]; - // determine partial update time frac = CL_LerpPoint (); // interpolate player info - for (i = 0; i < 3; i++) - cl.velocity[i] = cl.mvelocity[1][i] + - frac * (cl.mvelocity[0][i] - cl.mvelocity[1][i]); + cl.viewstate.velocity = cl.frameVelocity[1] + + frac * (cl.frameVelocity[0] - cl.frameVelocity[1]); if (cls.demoplayback) { // interpolate the angles + vec3_t d; + VectorSubtract (cl.frameViewAngles[0], cl.frameViewAngles[1], d); for (j = 0; j < 3; j++) { - d = cl.mviewangles[0][j] - cl.mviewangles[1][j]; - if (d > 180) - d -= 360; - else if (d < -180) - d += 360; - cl.viewangles[j] = cl.mviewangles[1][j] + frac * d; + if (d[j] > 180) { + d[j] -= 360; + } else if (d[j] < -180) { + d[j] += 360; + } } + VectorMultAdd (cl.frameViewAngles[1], frac, d, + cl.viewstate.player_angles); } bobjrotate = anglemod (100 * cl.time); // start on the entity after the world for (i = 1; i < cl.num_entities; i++) { - new = &nq_entstates.frame[0 + cl.mindex][i]; - old = &nq_entstates.frame[1 - cl.mindex][i]; - ent = &cl_entities[i]; - // if the object wasn't included in the last packet, remove it - entvalid = cl_msgtime[i] == cl.mtime[0]; - if (entvalid && !new->modelindex) { - VectorCopy (new->origin, ent->origin); - VectorCopy (new->angles, ent->angles); - entvalid = 0; - } - if (!entvalid) { - ent->model = NULL; - ent->pose1 = ent->pose2 = -1; - if (ent->efrag) - r_funcs->R_RemoveEfrags (ent); // just became empty + new = &nq_entstates.frame[0 + cl.frameIndex][i]; + old = &nq_entstates.frame[1 - cl.frameIndex][i]; + + ent = CL_GetInvalidEntity (i); + // if the object wasn't included in the last packet, or the model + // has been removed, remove the entity + if (cl_msgtime[i] != cl.mtime[0] || !new->modelindex) { + if (Entity_Valid (ent)) { + Scene_DestroyEntity (cl_world.scene, ent); + } continue; } - if (cl_forcelink[i]) - *old = *new; + if (!Entity_Valid (ent)) { + ent = CL_GetEntity (i); + SET_ADD (&cl_forcelink, i); + } + transform_t transform = Entity_Transform (ent); + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, ent.reg); + vec4f_t *old_origin = Ent_GetComponent (ent.id, scene_old_origin, ent.reg); - if (cl_forcelink[i] || new->modelindex != old->modelindex) { + if (SET_TEST_MEMBER (&cl_forcelink, i)) { + *old = *new; + } + + if (SET_TEST_MEMBER (&cl_forcelink, i) + || new->modelindex != old->modelindex) { old->modelindex = new->modelindex; - set_entity_model (ent, new->modelindex); + set_entity_model (i, new->modelindex); } - ent->frame = new->frame; - if (cl_forcelink[i] || new->colormap != old->colormap) { + animation->frame = new->frame; + if (SET_TEST_MEMBER (&cl_forcelink, i) + || new->colormap != old->colormap) { old->colormap = new->colormap; - ent->skin = mod_funcs->Skin_SetColormap (ent->skin, new->colormap); + renderer->skin = mod_funcs->Skin_SetColormap (renderer->skin, + new->colormap); } - if (cl_forcelink[i] || new->skinnum != old->skinnum) { + if (SET_TEST_MEMBER (&cl_forcelink, i) + || new->skinnum != old->skinnum) { old->skinnum = new->skinnum; - ent->skinnum = new->skinnum; + renderer->skinnum = new->skinnum; if (i <= cl.maxclients) { - ent->skin = mod_funcs->Skin_SetColormap (ent->skin, i); - mod_funcs->Skin_SetTranslation (i, cl.scores[i - 1].topcolor, - cl.scores[i - 1].bottomcolor); + colormap_t colormap = { + .top = cl.players[i - 1].topcolor, + .bottom = cl.players[i - 1].bottomcolor, + }; + Ent_SetComponent (ent.id, scene_colormap, ent.reg, &colormap); + renderer->skin = mod_funcs->Skin_SetColormap (renderer->skin, + i); + mod_funcs->Skin_SetTranslation (i, cl.players[i - 1].topcolor, + cl.players[i - 1].bottomcolor); } } - ent->scale = new->scale / 16.0; - VectorCopy (ent_colormod[new->colormod], ent->colormod); - ent->colormod[3] = ENTALPHA_DECODE (new->alpha); + VectorCopy (ent_colormod[new->colormod], renderer->colormod); + renderer->colormod[3] = ENTALPHA_DECODE (new->alpha); model_flags = 0; - if (ent->model) - model_flags = ent->model->flags; + if (renderer->model) { + model_flags = renderer->model->flags; + } - if (cl_forcelink[i]) { + if (SET_TEST_MEMBER (&cl_forcelink, i)) { // The entity was not updated in the last message so move to the // final spot - ent->pose1 = ent->pose2 = -1; - VectorCopy (new->origin, ent->origin); - if (!(model_flags & EF_ROTATE)) - CL_TransformEntity (ent, new->angles, true); - if (i != cl.viewentity || chase_active->int_val) { - if (ent->efrag) - r_funcs->R_RemoveEfrags (ent); - r_funcs->R_AddEfrags (ent); + animation->pose1 = animation->pose2 = -1; + CL_TransformEntity (ent, new->scale / 16.0, new->angles, + new->origin); + if (i != cl.viewentity || chase_active) { + R_AddEfrags (&cl_world.scene->worldmodel->brush, ent); } - VectorCopy (ent->origin, ent->old_origin); + *old_origin = new->origin; } else { + vec4f_t delta = new->origin - old->origin; f = frac; - VectorCopy (ent->origin, ent->old_origin); - VectorSubtract (new->origin, old->origin, delta); + *old_origin = Transform_GetWorldPosition (transform); // If the delta is large, assume a teleport and don't lerp - if (fabs (delta[0]) > 100 || fabs (delta[1] > 100) + if (fabs (delta[0]) > 100 || fabs (delta[1]) > 100 || fabs (delta[2]) > 100) { // assume a teleportation, not a motion - VectorCopy (new->origin, ent->origin); - if (!(model_flags & EF_ROTATE)) - CL_TransformEntity (ent, new->angles, true); - ent->pose1 = ent->pose2 = -1; + CL_TransformEntity (ent, new->scale / 16.0, new->angles, + new->origin); + animation->pose1 = animation->pose2 = -1; } else { - vec3_t angles, d; // interpolate the origin and angles - VectorMultAdd (old->origin, f, delta, ent->origin); + vec3_t angles, d; + vec4f_t origin = old->origin + f * delta; if (!(model_flags & EF_ROTATE)) { VectorSubtract (new->angles, old->angles, d); for (j = 0; j < 3; j++) { @@ -408,17 +300,13 @@ CL_RelinkEntities (void) d[j] += 360; } VectorMultAdd (old->angles, f, d, angles); - CL_TransformEntity (ent, angles, false); } + CL_TransformEntity (ent, new->scale / 16.0, angles, origin); } - if (i != cl.viewentity || chase_active->int_val) { - if (ent->efrag) { - if (!VectorCompare (ent->origin, ent->old_origin)) { - r_funcs->R_RemoveEfrags (ent); - r_funcs->R_AddEfrags (ent); - } - } else { - r_funcs->R_AddEfrags (ent); + if (i != cl.viewentity || chase_active) { + vec4f_t org = Transform_GetWorldPosition (transform); + if (!VectorCompare (org, *old_origin)) {//FIXME + R_AddEfrags (&cl_world.scene->worldmodel->brush, ent); } } } @@ -428,16 +316,37 @@ CL_RelinkEntities (void) vec3_t angles; VectorCopy (new->angles, angles); angles[YAW] = bobjrotate; - CL_TransformEntity (ent, angles, false); + CL_TransformEntity (ent, new->scale / 16.0, angles, new->origin); + } + CL_EntityEffects (i, ent, new, cl.time); + vec4f_t org = Transform_GetWorldPosition (transform); + int effects = new->effects; + if (cl.maxclients == 1 && effects) { + // Enable blue and red lights for quad and invulnerability, + // but only for single-player as such information isn't provided + // by the server for other players and thus it would probably be + // confusing if the player could see the colored effects but not + // for others. + int it = cl.stats[STAT_ITEMS]; + effects |= (it & IT_QUAD) + >> (BITOP_LOG2(IT_QUAD) - BITOP_LOG2 (EF_BLUE)); + effects |= (it & IT_INVULNERABILITY) + >> (BITOP_LOG2(IT_INVULNERABILITY) - BITOP_LOG2 (EF_RED)); + } + CL_NewDlight (i, org, effects, new->glow_size, + new->glow_color, cl.time); + if (VectorDistance_fast (old->origin, org) > (256 * 256)) { + old->origin = org; } - CL_EntityEffects (i, ent, new); - CL_NewDlight (i, ent->origin, new->effects, new->glow_size, - new->glow_color); - if (VectorDistance_fast (old->origin, ent->origin) > (256 * 256)) - VectorCopy (ent->origin, old->origin); if (model_flags & ~EF_ROTATE) - CL_ModelEffects (ent, i, new->glow_color); + CL_ModelEffects (ent, i, new->glow_color, cl.time); - cl_forcelink[i] = false; + SET_REMOVE (&cl_forcelink, i); + } + { + entity_t player_entity = CL_GetEntity (cl.viewentity); + transform_t transform = Entity_Transform (player_entity); + cl.viewstate.player_entity = player_entity; + cl.viewstate.player_origin = Transform_GetWorldPosition (transform); } } diff --git a/nq/source/cl_input.c b/nq/source/cl_input.c index f4d175723..a2e039730 100644 --- a/nq/source/cl_input.c +++ b/nq/source/cl_input.c @@ -36,438 +36,33 @@ #endif #include "QF/cmd.h" +#include "QF/console.h" #include "QF/cvar.h" #include "QF/input.h" #include "QF/keys.h" #include "QF/msg.h" #include "QF/sys.h" +#include "QF/input/event.h" #include "QF/plugin/vid_render.h" -#include "chase.h" -#include "client.h" #include "compat.h" -#include "host.h" -/* - KEY BUTTONS +#include "client/input.h" - Continuous button event tracking is complicated by the fact that two - different input sources (say, mouse button 1 and the control key) can - both press the same button, but the button should be released only when - both of the pressing key have been released. - - When a key event issues a button command (+forward, +attack, etc), it - appends its key number as a parameter to the command so it can be - matched up with the release. - - state bit 0 is the current state of the key - state bit 1 is edge triggered on the up to down transition - state bit 2 is edge triggered on the down to up transition -*/ - -kbutton_t in_left, in_right, in_forward, in_back; -kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright; -kbutton_t in_use, in_jump, in_attack; -kbutton_t in_up, in_down; +#include "nq/include/client.h" +#include "nq/include/host.h" int in_impulse; void (*write_angles) (sizebuf_t *sb, const vec3_t angles); static void -KeyPress (kbutton_t *b) -{ - const char *c; - int k; - - c = Cmd_Argv (1); - if (c[0]) - k = atoi (c); - else - k = -1; // typed manually at the console for - // continuous down - - if (k == b->down[0] || k == b->down[1]) - return; // repeating key - - if (!b->down[0]) - b->down[0] = k; - else if (!b->down[1]) - b->down[1] = k; - else { - Sys_Printf ("Three keys down for a button!\n"); - return; - } - - if (b->state & 1) - return; // still down - b->state |= 1 + 2; // down + impulse down -} - -static void -KeyRelease (kbutton_t *b) -{ - const char *c; - int k; - - c = Cmd_Argv (1); - if (c[0]) - k = atoi (c); - else { // typed manually at the console, - // assume for unsticking, so clear - // all - b->down[0] = b->down[1] = 0; - b->state = 4; // impulse up - return; - } - - if (b->down[0] == k) - b->down[0] = 0; - else if (b->down[1] == k) - b->down[1] = 0; - else - return; // key up without coresponding down - // (menu pass through) - if (b->down[0] || b->down[1]) - return; // some other key is still holding it - // down - - if (!(b->state & 1)) - return; // still up (this should not happen) - b->state &= ~1; // now up - b->state |= 4; // impulse up -} - -static void -IN_KLookPress (void) -{ - KeyPress (&in_klook); -} - -static void -IN_KLookRelease (void) -{ - KeyRelease (&in_klook); -} - -static void -IN_MLookPress (void) -{ - KeyPress (&in_mlook); -} - -static void -IN_MLookRelease (void) -{ - KeyRelease (&in_mlook); - if (!freelook && lookspring->int_val) - V_StartPitchDrift (); -} - -static void -IN_UpPress (void) -{ - KeyPress (&in_up); -} - -static void -IN_UpRelease (void) -{ - KeyRelease (&in_up); -} - -static void -IN_DownPress (void) -{ - KeyPress (&in_down); -} - -static void -IN_DownRelease (void) -{ - KeyRelease (&in_down); -} - -static void -IN_LeftPress (void) -{ - KeyPress (&in_left); -} - -static void -IN_LeftRelease (void) -{ - KeyRelease (&in_left); -} - -static void -IN_RightPress (void) -{ - KeyPress (&in_right); -} - -static void -IN_RightRelease (void) -{ - KeyRelease (&in_right); -} - -static void -IN_ForwardPress (void) -{ - KeyPress (&in_forward); -} - -static void -IN_ForwardRelease (void) -{ - KeyRelease (&in_forward); -} - -static void -IN_BackPress (void) -{ - KeyPress (&in_back); -} - -static void -IN_BackRelease (void) -{ - KeyRelease (&in_back); -} - -static void -IN_LookupPress (void) -{ - KeyPress (&in_lookup); -} - -static void -IN_LookupRelease (void) -{ - KeyRelease (&in_lookup); -} - -static void -IN_LookdownPress (void) -{ - KeyPress (&in_lookdown); -} - -static void -IN_LookdownRelease (void) -{ - KeyRelease (&in_lookdown); -} - -static void -IN_MoveleftPress (void) -{ - KeyPress (&in_moveleft); -} - -static void -IN_MoveleftRelease (void) -{ - KeyRelease (&in_moveleft); -} - -static void -IN_MoverightPress (void) -{ - KeyPress (&in_moveright); -} - -static void -IN_MoverightRelease (void) -{ - KeyRelease (&in_moveright); -} - -static void -IN_SpeedPress (void) -{ - KeyPress (&in_speed); -} - -static void -IN_SpeedRelease (void) -{ - KeyRelease (&in_speed); -} - -static void -IN_StrafePress (void) -{ - KeyPress (&in_strafe); -} - -static void -IN_StrafeRelease (void) -{ - KeyRelease (&in_strafe); -} - -static void -IN_AttackPress (void) -{ - KeyPress (&in_attack); -} - -static void -IN_AttackRelease (void) -{ - KeyRelease (&in_attack); -} - -static void -IN_UsePress (void) -{ - KeyPress (&in_use); -} - -static void -IN_UseRelease (void) -{ - KeyRelease (&in_use); -} - -static void -IN_JumpPress (void) -{ - KeyPress (&in_jump); -} - -static void -IN_JumpRelease (void) -{ - KeyRelease (&in_jump); -} - -static void -IN_Impulse (void) +IN_Impulse (void *data) { in_impulse = atoi (Cmd_Argv (1)); } -/* - CL_KeyState - - Returns 0.25 if a key was pressed and released during the frame, - 0.5 if it was pressed and held - 0 if held then released, and - 1.0 if held for the entire time -*/ -float -CL_KeyState (kbutton_t *key) -{ - float val; - qboolean impulsedown, impulseup, down; - - impulsedown = key->state & 2; - impulseup = key->state & 4; - down = key->state & 1; - val = 0; - - if (impulsedown && !impulseup) { - if (down) - val = 0.5; // pressed and held this frame - else - val = 0; // I_Error (); - } - if (impulseup && !impulsedown) { - if (down) - val = 0; // I_Error (); - else - val = 0; // released this frame - } - if (!impulsedown && !impulseup) { - if (down) - val = 1.0; // held the entire frame - else - val = 0; // up the entire frame - } - if (impulsedown && impulseup) { - if (down) - val = 0.75; // released and re-pressed this frame - else - val = 0.25; // pressed and released this frame - } - - key->state &= 1; // clear impulses - - return val; -} - -cvar_t *cl_anglespeedkey; -cvar_t *cl_backspeed; -cvar_t *cl_forwardspeed; -cvar_t *cl_movespeedkey; -cvar_t *cl_pitchspeed; -cvar_t *cl_sidespeed; -cvar_t *cl_upspeed; -cvar_t *cl_yawspeed; - -/* - CL_AdjustAngles - - Moves the local angle positions -*/ -static void -CL_AdjustAngles (void) -{ - float down, up; - float pitchspeed, yawspeed; - - pitchspeed = cl_pitchspeed->value; - yawspeed = cl_yawspeed->value; - - if (in_speed.state & 1) { - pitchspeed *= cl_anglespeedkey->value; - yawspeed *= cl_anglespeedkey->value; - } - - if ((cl.fpd & FPD_LIMIT_PITCH) && pitchspeed > FPD_MAXPITCH) - pitchspeed = FPD_MAXPITCH; - if ((cl.fpd & FPD_LIMIT_YAW) && yawspeed > FPD_MAXYAW) - yawspeed = FPD_MAXYAW; - - pitchspeed *= host_frametime; - yawspeed *= host_frametime; - - if (!(in_strafe.state & 1)) { - cl.viewangles[YAW] -= yawspeed * CL_KeyState (&in_right); - cl.viewangles[YAW] += yawspeed * CL_KeyState (&in_left); - cl.viewangles[YAW] = anglemod (cl.viewangles[YAW]); - } - if (in_klook.state & 1) { - V_StopPitchDrift (); - cl.viewangles[PITCH] -= pitchspeed * CL_KeyState (&in_forward); - cl.viewangles[PITCH] += pitchspeed * CL_KeyState (&in_back); - } - - up = CL_KeyState (&in_lookup); - down = CL_KeyState (&in_lookdown); - - cl.viewangles[PITCH] -= pitchspeed * up; - cl.viewangles[PITCH] += pitchspeed * down; - - if (up || down) - V_StopPitchDrift (); - - // FIXME: Need to clean up view angle limits - if (cl.viewangles[PITCH] > 80) - cl.viewangles[PITCH] = 80; - if (cl.viewangles[PITCH] < -70) - cl.viewangles[PITCH] = -70; - - if (cl.viewangles[ROLL] > 50) - cl.viewangles[ROLL] = 50; - if (cl.viewangles[ROLL] < -50) - cl.viewangles[ROLL] = -50; -} - /* CL_BaseMove @@ -476,72 +71,18 @@ CL_AdjustAngles (void) void CL_BaseMove (usercmd_t *cmd) { - if (cls.state != ca_active) + if (cls.state != ca_active) { return; - - CL_AdjustAngles (); + } + VectorCopy (cl.viewstate.player_angles, cl.movestate.angles);//FIXME + CL_Input_BuildMove (host_frametime, &cl.movestate, &cl.viewstate); + VectorCopy (cl.movestate.angles, cl.viewstate.player_angles);//FIXME memset (cmd, 0, sizeof (*cmd)); - - if (in_strafe.state & 1) { - cmd->sidemove += cl_sidespeed->value * CL_KeyState (&in_right); - cmd->sidemove -= cl_sidespeed->value * CL_KeyState (&in_left); - } - - cmd->sidemove += cl_sidespeed->value * CL_KeyState (&in_moveright); - cmd->sidemove -= cl_sidespeed->value * CL_KeyState (&in_moveleft); - - cmd->upmove += cl_upspeed->value * CL_KeyState (&in_up); - cmd->upmove -= cl_upspeed->value * CL_KeyState (&in_down); - - if (!(in_klook.state & 1)) { - cmd->forwardmove += cl_forwardspeed->value * CL_KeyState (&in_forward); - cmd->forwardmove -= cl_backspeed->value * CL_KeyState (&in_back); - } - - // adjust for speed key - if (in_speed.state & 1) { - cmd->forwardmove *= cl_movespeedkey->value; - cmd->sidemove *= cl_movespeedkey->value; - cmd->upmove *= cl_movespeedkey->value; - } - - if (freelook) - V_StopPitchDrift (); - - viewdelta.angles[0] = viewdelta.angles[1] = viewdelta.angles[2] = 0; - viewdelta.position[0] = viewdelta.position[1] = viewdelta.position[2] = 0; - - IN_Move (); - - // adjust for chase camera angles - if (cl.chase - && (chase_active->int_val == 2 || chase_active->int_val == 3)) { - vec3_t forward, right, up, f, r; - vec3_t dir = {0, 0, 0}; - - dir[1] = r_data->refdef->viewangles[1] - cl.viewangles[1]; - AngleVectors (dir, forward, right, up); - VectorScale (forward, cmd->forwardmove, f); - VectorScale (right, cmd->sidemove, r); - cmd->forwardmove = f[0] + r[0]; - cmd->sidemove = f[1] + r[1]; - VectorScale (forward, viewdelta.position[2], f); - VectorScale (right, viewdelta.position[0], r); - viewdelta.position[2] = f[0] + r[0]; - viewdelta.position[0] = (f[1] + r[1]) * -1; - } - - cmd->forwardmove += viewdelta.position[2] * m_forward->value; - cmd->sidemove += viewdelta.position[0] * m_side->value; - cmd->upmove += viewdelta.position[1]; - cl.viewangles[PITCH] += viewdelta.angles[PITCH] * m_pitch->value; - cl.viewangles[YAW] += viewdelta.angles[YAW] * m_yaw->value; - cl.viewangles[ROLL] += viewdelta.angles[ROLL]; - - if (freelook && !(in_strafe.state & 1)) { - cl.viewangles[PITCH] = bound (-70, cl.viewangles[PITCH], 80); - } + cmd->forwardmove = cl.movestate.move[FORWARD]; + cmd->sidemove = cl.movestate.move[SIDE]; + cmd->upmove = cl.movestate.move[UP]; + cl.viewstate.movecmd = cl.movestate.move; } @@ -563,7 +104,7 @@ CL_SendMove (usercmd_t *cmd) MSG_WriteFloat (&buf, cl.mtime[0]); // so server can get ping times - write_angles (&buf, cl.viewangles); + write_angles (&buf, cl.viewstate.player_angles); MSG_WriteShort (&buf, cmd->forwardmove); MSG_WriteShort (&buf, cmd->sidemove); @@ -572,13 +113,9 @@ CL_SendMove (usercmd_t *cmd) // send button bits bits = 0; - if (in_attack.state & 3) - bits |= 1; - in_attack.state &= ~2; - - if (in_jump.state & 3) - bits |= 2; - in_jump.state &= ~2; + bits |= IN_ButtonPressed (&in_attack) << 0; + bits |= IN_ButtonPressed (&in_jump) << 1; + bits |= IN_ButtonPressed (&in_use) << 2; MSG_WriteByte (&buf, bits); @@ -601,78 +138,15 @@ CL_SendMove (usercmd_t *cmd) } void -CL_Input_Init (void) +CL_Init_Input (cbuf_t *cbuf) { - Cmd_AddCommand ("+moveup", IN_UpPress, "When active the player is " - "swimming up in a liquid"); - Cmd_AddCommand ("-moveup", IN_UpRelease, "When active the player is not " - "swimming up in a liquid"); - Cmd_AddCommand ("+movedown", IN_DownPress, "When active the player is " - "swimming down in a liquid"); - Cmd_AddCommand ("-movedown", IN_DownRelease, "When active the player is " - "not swimming down in a liquid"); - Cmd_AddCommand ("+left", IN_LeftPress, "When active the player is turning " - "left"); - Cmd_AddCommand ("-left", IN_LeftRelease, "When active the player is not " - "turning left"); - Cmd_AddCommand ("+right", IN_RightPress, "When active the player is " - "turning right"); - Cmd_AddCommand ("-right", IN_RightRelease, "When active the player is not " - "turning right"); - Cmd_AddCommand ("+forward", IN_ForwardPress, "When active the player is " - "moving forward"); - Cmd_AddCommand ("-forward", IN_ForwardRelease, "When active the player is " - "not moving forward"); - Cmd_AddCommand ("+back", IN_BackPress, "When active the player is moving " - "backwards"); - Cmd_AddCommand ("-back", IN_BackRelease, "When active the player is not " - "moving backwards"); - Cmd_AddCommand ("+lookup", IN_LookupPress, "When active the player's view " - "is looking up"); - Cmd_AddCommand ("-lookup", IN_LookupRelease, "When active the player's " - "view is not looking up"); - Cmd_AddCommand ("+lookdown", IN_LookdownPress, "When active the player's " - "view is looking down"); - Cmd_AddCommand ("-lookdown", IN_LookdownRelease, "When active the " - "player's view is not looking up"); - Cmd_AddCommand ("+strafe", IN_StrafePress, "When active, +left and +right " - "function like +moveleft and +moveright"); - Cmd_AddCommand ("-strafe", IN_StrafeRelease, "When active, +left and " - "+right stop functioning like +moveleft and +moveright"); - Cmd_AddCommand ("+moveleft", IN_MoveleftPress, "When active the player is " - "strafing left"); - Cmd_AddCommand ("-moveleft", IN_MoveleftRelease, "When active the player " - "is not strafing left"); - Cmd_AddCommand ("+moveright", IN_MoverightPress, "When active the player " - "is strafing right"); - Cmd_AddCommand ("-moveright", IN_MoverightRelease, "When active the " - "player is not strafing right"); - Cmd_AddCommand ("+speed", IN_SpeedPress, "When active the player is " - "running"); - Cmd_AddCommand ("-speed", IN_SpeedRelease, "When active the player is not " - "running"); - Cmd_AddCommand ("+attack", IN_AttackPress, "When active player is " - "firing/using current weapon"); - Cmd_AddCommand ("-attack", IN_AttackRelease, "When active player is not " - "firing/using current weapon"); - Cmd_AddCommand ("+use", IN_UsePress, "Non-functional. Left over command " - "for opening doors and triggering switches"); - Cmd_AddCommand ("-use", IN_UseRelease, "Non-functional. Left over command " - "for opening doors and triggering switches"); - Cmd_AddCommand ("+jump", IN_JumpPress, "When active the player is " - "jumping"); - Cmd_AddCommand ("-jump", IN_JumpRelease, "When active the player is not " - "jumping"); - Cmd_AddCommand ("impulse", IN_Impulse, "Call a game function or QuakeC " - "function."); - Cmd_AddCommand ("+klook", IN_KLookPress, "When active, +forward and +back " - "perform +lookup and +lookdown"); - Cmd_AddCommand ("-klook", IN_KLookRelease, "When active, +forward and " - "+back don't perform +lookup and +lookdown"); - Cmd_AddCommand ("+mlook", IN_MLookPress, "When active moving the mouse or " - "joystick forwards and backwards performs +lookup and " - "+lookdown"); - Cmd_AddCommand ("-mlook", IN_MLookRelease, "When active moving the mouse " - "or joystick forwards and backwards doesn't perform " - "+lookup and +lookdown"); + CL_Input_Init (cbuf); + Cmd_AddDataCommand ("impulse", IN_Impulse, 0, + "Call a game function or QuakeC function."); +} + +void +CL_Init_Input_Cvars (void) +{ + CL_Input_Init_Cvars (); } diff --git a/nq/source/cl_main.c b/nq/source/cl_main.c index 7f6dd9e82..1316a051e 100644 --- a/nq/source/cl_main.c +++ b/nq/source/cl_main.c @@ -34,29 +34,39 @@ #include "QF/console.h" #include "QF/cvar.h" #include "QF/draw.h" +#include "QF/gib.h" #include "QF/input.h" +#include "QF/image.h" #include "QF/joystick.h" #include "QF/keys.h" #include "QF/msg.h" -#include "QF/qfplist.h" +#include "QF/png.h" +#include "QF/plist.h" #include "QF/render.h" #include "QF/screen.h" #include "QF/skin.h" +#include "QF/sound.h" #include "QF/sys.h" #include "QF/va.h" #include "QF/plugin/console.h" #include "QF/plugin/vid_render.h" +#include "QF/scene/entity.h" +#include "QF/scene/scene.h" -#include "chase.h" -#include "cl_skin.h" -#include "client.h" -#include "clview.h" #include "compat.h" -#include "host.h" -#include "host.h" -#include "server.h" -#include "sbar.h" + +#include "client/chase.h" +#include "client/particles.h" +#include "client/sbar.h" +#include "client/screen.h" +#include "client/temp_entities.h" +#include "client/world.h" + +#include "nq/include/cl_skin.h" +#include "nq/include/client.h" +#include "nq/include/host.h" +#include "nq/include/server.h" CLIENT_PLUGIN_PROTOS static plugin_list_t client_plugin_list[] = { @@ -64,30 +74,72 @@ static plugin_list_t client_plugin_list[] = { }; // these two are not intended to be set directly -cvar_t *cl_name; -cvar_t *cl_color; +char *cl_name; +static cvar_t cl_name_cvar = { + .name = "_cl_name", + .description = + "Player name", + .default_value = "player", + .flags = CVAR_ARCHIVE, + .value = { .type = 0, .value = &cl_name }, +}; +int cl_color; +static cvar_t cl_color_cvar = { + .name = "_cl_color", + .description = + "Player color", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &cl_color }, +}; -cvar_t *cl_writecfg; +int cl_writecfg; +static cvar_t cl_writecfg_cvar = { + .name = "cl_writecfg", + .description = + "write config files?", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_writecfg }, +}; -cvar_t *cl_shownet; -cvar_t *cl_nolerp; +int cl_shownet; +static cvar_t cl_shownet_cvar = { + .name = "cl_shownet", + .description = + "show network packets. 0=off, 1=basic, 2=verbose", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_shownet }, +}; +int cl_nolerp; +static cvar_t cl_nolerp_cvar = { + .name = "cl_nolerp", + .description = + "linear motion interpolation", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_nolerp }, +}; -cvar_t *cl_cshift_bonus; -cvar_t *cl_cshift_contents; -cvar_t *cl_cshift_damage; -cvar_t *cl_cshift_powerup; - -cvar_t *lookspring; - -cvar_t *m_pitch; -cvar_t *m_yaw; -cvar_t *m_forward; -cvar_t *m_side; - -cvar_t *hud_fps; -cvar_t *hud_time; - -int fps_count; +static int r_ambient; +static cvar_t r_ambient_cvar = { + .name = "r_ambient", + .description = + "Determines the ambient lighting for a level", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_ambient }, +}; +static int r_drawflat; +static cvar_t r_drawflat_cvar = { + .name = "r_drawflat", + .description = + "Toggles the drawing of textures", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &r_drawflat }, +}; client_static_t cls; client_state_t cl; @@ -100,34 +152,91 @@ client_state_t cl; static void CL_WriteConfiguration (void) { - QFile *f; - // dedicated servers initialize the host but don't parse and set the // config.cfg cvars - if (host_initialized && !isDedicated && cl_writecfg->int_val) { - char *path = va ("%s/config.cfg", qfs_gamedir->dir.def); - f = QFS_WOpen (path, 0); + if (host_initialized && !isDedicated && cl_writecfg) { + plitem_t *config = PL_NewDictionary (0); + Cvar_SaveConfig (config); + IN_SaveConfig (config); + + const char *path = va (0, "%s/quakeforge.cfg", qfs_gamedir->dir.def); + QFile *f = QFS_WOpen (path, 0); + if (!f) { - Sys_Printf ("Couldn't write config.cfg.\n"); - return; + Sys_Printf ("Couldn't write quakeforge.cfg.\n"); + } else { + char *cfg = PL_WritePropertyList (config); + Qputs (f, cfg); + free (cfg); + Qclose (f); } - - Key_WriteBindings (f); - Cvar_WriteVariables (f); - Joy_WriteBindings (f); - - Qclose (f); + PL_Release (config); } } -void -CL_Shutdown (void) +int +CL_ReadConfiguration (const char *cfg_name) +{ + QFile *cfg_file = QFS_FOpenFile (cfg_name); + if (!cfg_file) { + return 0; + } + size_t len = Qfilesize (cfg_file); + char *cfg = malloc (len + 1); + Qread (cfg_file, cfg, len); + cfg[len] = 0; + Qclose (cfg_file); + + plitem_t *config = PL_GetPropertyList (cfg, 0); + free (cfg); + + if (!config) { + return 0; + } + + Cvar_LoadConfig (config); + IN_LoadConfig (config); + + PL_Release (config); + return 1; +} + +static void +CL_Shutdown (void *data) { CL_WriteConfiguration (); - CDAudio_Shutdown (); - S_Shutdown (); - IN_Shutdown (); - VID_Shutdown (); +} + +void +CL_ClearMemory (void) +{ + VID_ClearMemory (); + SCR_SetFullscreen (0); + + cls.signon = 0; + SZ_Clear (&cls.message); + + if (Entity_Valid (cl.viewstate.weapon_entity)) { + Scene_DestroyEntity (cl_world.scene, cl.viewstate.weapon_entity); + cl.viewstate.weapon_entity = nullentity; + } + if (cl.players) { + int i; + + for (i = 0; i < cl.maxclients; i++) + Info_Destroy (cl.players[i].userinfo); + } + // wipe the entire cl structure + __auto_type cam = cl.viewstate.camera_transform; + memset (&cl, 0, sizeof (cl)); + Sbar_Intermission (cl.intermission = 0, cl.time); + cl.viewstate.camera_transform = cam; + cl.viewstate.demoplayback = cls.demoplayback; + + CL_ClearTEnts (); + CL_ClearEnts (); + + SCR_NewScene (0); } void @@ -139,92 +248,42 @@ CL_InitCvars (void) S_Init_Cvars (); CL_Demo_Init (); + CL_Init_Input_Cvars (); Chase_Init_Cvars (); V_Init_Cvars (); - cl_cshift_bonus = Cvar_Get ("cl_cshift_bonus", "1", CVAR_ARCHIVE, NULL, - "Show bonus flash on item pickup"); - cl_cshift_contents = Cvar_Get ("cl_cshift_content", "1", CVAR_ARCHIVE, - NULL, "Shift view colors for contents " - "(water, slime, etc)"); - cl_cshift_damage = Cvar_Get ("cl_cshift_damage", "1", CVAR_ARCHIVE, NULL, - "Shift view colors on damage"); - cl_cshift_powerup = Cvar_Get ("cl_cshift_powerup", "1", CVAR_ARCHIVE, NULL, "Shift view colors for powerups"); - cl_name = Cvar_Get ("_cl_name", "player", CVAR_ARCHIVE, NULL, - "Player name"); - cl_color = Cvar_Get ("_cl_color", "0", CVAR_ARCHIVE, NULL, "Player color"); - cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", CVAR_NONE, NULL, - "turn `run' speed multiplier"); - cl_backspeed = Cvar_Get ("cl_backspeed", "200", CVAR_ARCHIVE, NULL, - "backward speed"); - cl_forwardspeed = Cvar_Get ("cl_forwardspeed", "200", CVAR_ARCHIVE, NULL, - "forward speed"); - cl_movespeedkey = Cvar_Get ("cl_movespeedkey", "2.0", CVAR_NONE, NULL, - "move `run' speed multiplier"); - cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "150", CVAR_NONE, NULL, - "look up/down speed"); - cl_sidespeed = Cvar_Get ("cl_sidespeed", "350", CVAR_NONE, NULL, - "strafe speed"); - cl_upspeed = Cvar_Get ("cl_upspeed", "200", CVAR_NONE, NULL, - "swim/fly up/down speed"); - cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", CVAR_NONE, NULL, - "turning speed"); - cl_writecfg = Cvar_Get ("cl_writecfg", "1", CVAR_NONE, NULL, - "write config files?"); - cl_shownet = Cvar_Get ("cl_shownet", "0", CVAR_NONE, NULL, - "show network packets. 0=off, 1=basic, 2=verbose"); - cl_nolerp = Cvar_Get ("cl_nolerp", "0", CVAR_NONE, NULL, - "linear motion interpolation"); - lookspring = Cvar_Get ("lookspring", "0", CVAR_ARCHIVE, NULL, "Snap view " - "to center when moving and no mlook/klook"); - m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE, NULL, - "mouse pitch (up/down) multipier"); - m_yaw = Cvar_Get ("m_yaw", "0.022", CVAR_ARCHIVE, NULL, - "mouse yaw (left/right) multipiler"); - m_forward = Cvar_Get ("m_forward", "1", CVAR_ARCHIVE, NULL, - "mouse forward/back speed"); - m_side = Cvar_Get ("m_side", "0.8", CVAR_ARCHIVE, NULL, - "mouse strafe speed"); - hud_fps = Cvar_Get ("hud_fps", "0", CVAR_ARCHIVE, NULL, - "display realtime frames per second"); - Cvar_MakeAlias ("show_fps", hud_fps); - hud_time = Cvar_Get ("hud_time", "0", CVAR_ARCHIVE, NULL, - "display the current time"); + Cvar_Register (&cl_name_cvar, 0, 0); + Cvar_Register (&cl_color_cvar, 0, 0); + Cvar_Register (&cl_writecfg_cvar, 0, 0); + Cvar_Register (&cl_shownet_cvar, 0, 0); + Cvar_Register (&cl_nolerp_cvar, 0, 0); + + //FIXME not hooked up (don't do anything), but should not work in + //multi-player + Cvar_Register (&r_ambient_cvar, 0, 0); + Cvar_Register (&r_drawflat_cvar, 0, 0); } void CL_ClearState (void) { + CL_ClearMemory (); if (!sv.active) Host_ClearMemory (); - if (cl.edicts) - PL_Free (cl.edicts); - - if (cl.scores) { - int i; - - for (i = 0; i < cl.maxclients; i++) - Info_Destroy (cl.scores[i].info); - } - - // wipe the entire cl structure - memset (&cl, 0, sizeof (cl)); - cl.chase = 1; - cl.watervis = 1; - r_data->force_fullscreen = 0; + cl.viewstate.player_origin = (vec4f_t) {0, 0, 0, 1}; + cl.viewstate.chase = 1; + cl.viewstate.chasestate = &cl.chasestate; + cl.chasestate.viewstate = &cl.viewstate; + cl.viewstate.watervis = 1; + SCR_SetFullscreen (0); r_data->lightstyle = cl.lightstyle; - CL_Init_Entity (&cl.viewent); - r_data->view_model = &cl.viewent; + cl.viewstate.weapon_entity = Scene_CreateEntity (cl_world.scene); + CL_Init_Entity (cl.viewstate.weapon_entity); + r_data->view_model = cl.viewstate.weapon_entity; - SZ_Clear (&cls.message); - - CL_ClearTEnts (); - - r_funcs->R_ClearState (); - - CL_ClearEnts (); + CL_TEnts_Precache (); } /* @@ -238,7 +297,7 @@ CL_StopCshifts (void) int i; for (i = 0; i < NUM_CSHIFTS; i++) - cl.cshifts[i].percent = 0; + cl.viewstate.cshifts[i].percent = 0; for (i = 0; i < MAX_CL_STATS; i++) cl.stats[i] = 0; } @@ -258,9 +317,6 @@ CL_Disconnect (void) // Clean the Cshifts CL_StopCshifts (); - // bring the console down and fade the colors back to normal -// SCR_BringDownConsole (); - // if running a local server, shut it down if (cls.demoplayback) CL_StopPlayback (); @@ -268,7 +324,7 @@ CL_Disconnect (void) if (cls.demorecording) CL_StopRecording (); - Sys_MaskPrintf (SYS_DEV, "Sending clc_disconnect\n"); + Sys_MaskPrintf (SYS_dev, "Sending clc_disconnect\n"); SZ_Clear (&cls.message); MSG_WriteByte (&cls.message, clc_disconnect); NET_SendUnreliableMessage (cls.netcon, &cls.message); @@ -280,8 +336,9 @@ CL_Disconnect (void) Host_ShutdownServer (false); } - cl.worldmodel = NULL; - cl.intermission = 0; + cl_world.scene->worldmodel = NULL; + Sbar_Intermission (cl.intermission = 0, cl.time); + cl.viewstate.intermission = 0; } void @@ -300,7 +357,7 @@ CL_Disconnect_f (void) void CL_EstablishConnection (const char *host) { - if (cls.state == ca_dedicated) + if (net_is_dedicated) return; if (cls.demoplayback) @@ -311,12 +368,11 @@ CL_EstablishConnection (const char *host) cls.netcon = NET_Connect (host); if (!cls.netcon) Host_Error ("CL_Connect: connect failed\n"); - Sys_MaskPrintf (SYS_DEV, "CL_EstablishConnection: connected to %s\n", + Sys_MaskPrintf (SYS_dev, "CL_EstablishConnection: connected to %s\n", host); cls.demonum = -1; // not in the demo loop now CL_SetState (ca_connected); - Key_SetKeyDest (key_game); } /* @@ -327,7 +383,7 @@ CL_EstablishConnection (const char *host) void CL_SignonReply (void) { - Sys_MaskPrintf (SYS_DEV, "CL_SignonReply: %i\n", cls.signon); + Sys_MaskPrintf (SYS_dev, "CL_SignonReply: %i\n", cls.signon); switch (cls.signon) { case so_none: @@ -339,11 +395,12 @@ CL_SignonReply (void) case so_spawn: MSG_WriteByte (&cls.message, clc_stringcmd); - MSG_WriteString (&cls.message, va ("name \"%s\"\n", cl_name->string)); + MSG_WriteString (&cls.message, va (0, "name \"%s\"\n", + cl_name)); MSG_WriteByte (&cls.message, clc_stringcmd); MSG_WriteString (&cls.message, - va ("color %i %i\n", (cl_color->int_val) >> 4, - (cl_color->int_val) & 15)); + va (0, "color %i %i\n", (cl_color) >> 4, + (cl_color) & 15)); MSG_WriteByte (&cls.message, clc_stringcmd); MSG_WriteString (&cls.message, "spawn"); break; @@ -355,7 +412,7 @@ CL_SignonReply (void) break; case so_active: - cl.loading = false; + cl.viewstate.loading = false; CL_SetState (ca_active); break; } @@ -372,8 +429,10 @@ CL_NextDemo (void) if (cls.demonum == -1) return; // don't play demos - cl.loading = true; - CL_UpdateScreen(cl.time); + cl.viewstate.loading = true; + cl.viewstate.time = cl.time; + cl.viewstate.realtime = realtime; + CL_UpdateScreen(&cl.viewstate); if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS) { cls.demonum = 0; @@ -384,25 +443,37 @@ CL_NextDemo (void) } } - Cbuf_InsertText (host_cbuf, va ("playdemo %s\n", cls.demos[cls.demonum])); + Cbuf_InsertText (host_cbuf, va (0, "playdemo %s\n", + cls.demos[cls.demonum])); cls.demonum++; } +static void +pointfile_f (void) +{ + CL_LoadPointFile (cl_world.scene->worldmodel); +} + static void CL_PrintEntities_f (void) { - entity_t *ent; int i; - for (i = 0, ent = cl_entities; i < cl.num_entities; i++, ent++) { + for (i = 0; i < cl.num_entities; i++) { + entity_t ent = cl_entities[i]; + transform_t transform = Entity_Transform (ent); + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, cl_world.scene->reg); + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, cl_world.scene->reg); Sys_Printf ("%3i:", i); - if (!ent->model) { + if (!Entity_Valid (ent) || !renderer->model) { Sys_Printf ("EMPTY\n"); continue; } - Sys_Printf ("%s:%2i (%5.1f,%5.1f,%5.1f) [%5.1f %5.1f %5.1f]\n", - ent->model->name, ent->frame, VectorExpand (ent->origin), - VectorExpand (ent->angles)); + vec4f_t org = Transform_GetWorldPosition (transform); + vec4f_t rot = Transform_GetWorldRotation (transform); + Sys_Printf ("%s:%2i "VEC4F_FMT" "VEC4F_FMT"\n", + renderer->model->path, animation->frame, + VEC4_EXP (org), VEC4_EXP (rot)); } } @@ -415,9 +486,15 @@ int CL_ReadFromServer (void) { int ret; + TEntContext_t tentCtx = { + cl.viewstate.player_origin, + cl.viewentity + }; cl.oldtime = cl.time; cl.time += host_frametime; + cl.viewstate.frametime = host_frametime; + cl.viewstate.time = cl.time; do { ret = CL_GetMessage (); @@ -426,15 +503,14 @@ CL_ReadFromServer (void) if (!ret) break; - cl.last_received_message = realtime; CL_ParseServerMessage (); } while (ret && cls.state >= ca_connected); - if (cl_shownet->int_val) + if (cl_shownet) Sys_Printf ("\n"); CL_RelinkEntities (); - CL_UpdateTEnts (); + CL_UpdateTEnts (cl.time, &tentCtx); // bring the links up to date return 0; @@ -465,7 +541,7 @@ CL_SendCmd (void) return; // no message at all if (!NET_CanSendMessage (cls.netcon)) { - Sys_MaskPrintf (SYS_DEV, "CL_WriteToServer: can't send\n"); + Sys_MaskPrintf (SYS_dev, "CL_WriteToServer: can't send\n"); return; } @@ -480,48 +556,133 @@ CL_SetState (cactive_t state) { cactive_t old_state = cls.state; cls.state = state; - Sys_MaskPrintf (SYS_NET, "CL_SetState: %d -> %d\n", old_state, state); + cl.viewstate.active = cls.state == ca_active; + cl.viewstate.drift_enabled = !cls.demoplayback; + Sys_MaskPrintf (SYS_net, "CL_SetState: %d -> %d\n", old_state, state); if (old_state != state) { - if (old_state == ca_active) { + if (old_state == ca_active && state != ca_disconnected) { // leaving active state - Key_SetKeyDest (key_console); S_AmbientOff (); + SCR_NewScene (0); } switch (state) { - case ca_dedicated: - break; case ca_disconnected: + CL_ClearState (); cls.signon = so_none; - cl.loading = false; + cl.viewstate.loading = false; VID_SetCaption ("Disconnected"); break; case ca_connected: cls.signon = so_none; // need all the signon messages // before playing - cl.loading = true; - Key_SetKeyDest (key_game); + cl.viewstate.loading = true; IN_ClearStates (); VID_SetCaption ("Connected"); break; case ca_active: // entering active state - cl.loading = false; - Key_SetKeyDest (key_game); + cl.viewstate.loading = false; IN_ClearStates (); VID_SetCaption (""); S_AmbientOn (); break; } - CL_UpdateScreen (cl.time); + Sbar_SetActive (state == ca_active); + CL_UpdateScreen (&cl.viewstate); + } + host_in_game = 0; + Con_SetState (state == ca_active ? con_inactive : con_fullscreen); + if (state != old_state && state == ca_active) { + CL_Input_Activate (host_in_game = !cls.demoplayback); + } +} + +static void +write_capture (tex_t *tex, void *data) +{ + QFile *file = QFS_Open (va (0, "%s/qfmv%06d.png", + qfs_gamedir->dir.shots, + cls.demo_capture++), "wb"); + if (file) { + WritePNG (file, tex); + Qclose (file); + } + free (tex); +} + +void +CL_PreFrame (void) +{ + IN_ProcessEvents (); + + GIB_Thread_Execute (); + cmd_source = src_command; + Cbuf_Execute_Stack (host_cbuf); + + CL_SendCmd (); +} + +void +CL_Frame (void) +{ + static double time1 = 0, time2 = 0, time3 = 0; + int pass1, pass2, pass3; + + // fetch results from server + if (cls.state >= ca_connected) + CL_ReadFromServer (); + + // update video + if (host_speeds) + time1 = Sys_DoubleTime (); + + r_data->inhibit_viewmodel = (chase_active + || (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) + || cl.stats[STAT_HEALTH] <= 0); + r_data->frametime = host_frametime; + + cl.viewstate.intermission = cl.intermission != 0; + Sbar_Update (cl.time); + CL_UpdateScreen (&cl.viewstate); + + if (host_speeds) + time2 = Sys_DoubleTime (); + + // update audio + if (cls.state == ca_active) { + mleaf_t *l; + byte *asl = 0; + vec4f_t origin; + + origin = Transform_GetWorldPosition (cl.viewstate.camera_transform); + l = Mod_PointInLeaf (origin, cl_world.scene->worldmodel); + if (l) + asl = l->ambient_sound_level; + S_Update (cl.viewstate.camera_transform, asl); + R_DecayLights (host_frametime); + } else + S_Update (nulltransform, 0); + + CDAudio_Update (); + + if (host_speeds) { + pass1 = (time1 - time3) * 1000; + time3 = Sys_DoubleTime (); + pass2 = (time2 - time1) * 1000; + pass3 = (time3 - time2) * 1000; + Sys_Printf ("%3i tot %3i server %3i gfx %3i snd\n", + pass1 + pass2 + pass3, pass1, pass2, pass3); + } + + if (cls.demo_capture) { + r_funcs->capture_screen (write_capture, 0); } - if (con_module) - con_module->data->console->force_commandline = (state < ca_connected); } static void Force_CenterView_f (void) { - cl.viewangles[PITCH] = 0; + cl.viewstate.player_angles[PITCH] = 0; } void @@ -536,27 +697,35 @@ CL_Init (cbuf_t *cbuf) if (!colormap) Sys_Error ("Couldn't load gfx/colormap.lmp"); + Host_OnServerSpawn (CL_ClearMemory); + W_LoadWadFile ("gfx.wad"); VID_Init (basepal, colormap); - IN_Init (cbuf); + IN_Init (); + GIB_Key_Init (); R_Init (); r_data->lightstyle = cl.lightstyle; - S_Init (&cl.viewentity, &host_frametime); PI_RegisterPlugins (client_plugin_list); - Con_Init ("client"); + Con_Load ("client"); + CL_Init_Screen (); + Con_Init (); CDAudio_Init (); - Sbar_Init (); + Sbar_Init (cl.stats, cl.item_gettime); - CL_Input_Init (); + CL_Init_Input (cbuf); + CL_Particles_Init (); CL_TEnts_Init (); + CL_World_Init (); CL_ClearState (); - V_Init (); + V_Init (&cl.viewstate); + Cmd_AddCommand ("pointfile", pointfile_f, + "Load a pointfile to determine map leaks."); Cmd_AddCommand ("entities", CL_PrintEntities_f, "No Description"); Cmd_AddCommand ("disconnect", CL_Disconnect_f, "No Description"); Cmd_AddCommand ("maplist", Con_Maplist_f, "List available maps"); @@ -565,6 +734,8 @@ CL_Init (cbuf_t *cbuf) Cmd_AddCommand ("force_centerview", Force_CenterView_f, "force the view " "to be level"); + Sys_RegisterShutdown (CL_Shutdown, 0); + SZ_Alloc (&cls.message, 1024); CL_SetState (ca_disconnected); } diff --git a/nq/source/cl_parse.c b/nq/source/cl_parse.c index ea12152ff..02eacdefa 100644 --- a/nq/source/cl_parse.c +++ b/nq/source/cl_parse.c @@ -44,21 +44,29 @@ #include "QF/idparse.h" #include "QF/input.h" #include "QF/msg.h" -#include "QF/qfplist.h" +#include "QF/plist.h" #include "QF/sys.h" #include "QF/screen.h" #include "QF/skin.h" -#include "QF/sound.h" // FIXME: DEFAULT_SOUND_PACKET_* +#include "QF/sound.h" #include "QF/va.h" -#include "QF/plugin/vid_render.h" +#include "QF/scene/scene.h" + +#include "QF/plugin/vid_render.h" +#include "QF/scene/entity.h" + +#include "client/particles.h" +#include "client/sbar.h" +#include "client/temp_entities.h" +#include "client/world.h" -#include "client.h" #include "compat.h" -#include "host.h" -#include "sbar.h" -#include "server.h" -#include "game.h" + +#include "nq/include/client.h" +#include "nq/include/host.h" +#include "nq/include/server.h" +#include "nq/include/game.h" const char *svc_strings[] = { "svc_bad", @@ -119,26 +127,6 @@ const char *svc_strings[] = { "svc_spawnstaticsound2", }; -dstring_t *centerprint; - -static void -CL_LoadSky (void) -{ - plitem_t *item; - const char *name = 0; - - if (!cl.worldspawn) { - r_funcs->R_LoadSkys (0); - return; - } - if ((item = PL_ObjectForKey (cl.worldspawn, "sky")) // Q2/DarkPlaces - || (item = PL_ObjectForKey (cl.worldspawn, "skyname")) // old QF - || (item = PL_ObjectForKey (cl.worldspawn, "qlsky"))) /* QuakeLives */ { - name = PL_String (item); - } - r_funcs->R_LoadSkys (name); -} - /* CL_EntityNum @@ -160,7 +148,6 @@ CL_ParseStartSoundPacket (void) { float attenuation; int channel, ent, field_mask, sound_num, volume; - vec3_t pos; field_mask = MSG_ReadByte (net_message); @@ -193,7 +180,8 @@ CL_ParseStartSoundPacket (void) if (ent > MAX_EDICTS) Host_Error ("CL_ParseStartSoundPacket: ent = %i", ent); - MSG_ReadCoordV (net_message, pos); + vec4f_t pos = { 0, 0, 0, 1}; + MSG_ReadCoordV (net_message, (vec_t*)&pos);//FIXME S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume / 255.0, attenuation); @@ -259,72 +247,16 @@ CL_KeepaliveMessage (void) SZ_Clear (&cls.message); } -static void -map_cfg (const char *mapname, int all) -{ - char *name = malloc (strlen (mapname) + 4 + 1); - cbuf_t *cbuf = Cbuf_New (&id_interp); - QFile *f; - - QFS_StripExtension (mapname, name); - strcat (name, ".cfg"); - f = QFS_FOpenFile (name); - if (f) { - Qclose (f); - Cmd_Exec_File (cbuf, name, 1); - } else { - Cmd_Exec_File (cbuf, "maps_default.cfg", 1); - } - if (all) { - Cbuf_Execute_Stack (cbuf); - } else { - Cbuf_Execute_Sets (cbuf); - } - free (name); - Cbuf_Delete (cbuf); -} - -static plitem_t * -map_ent (const char *mapname) -{ - static progs_t edpr; - char *name = malloc (strlen (mapname) + 4 + 1); - char *buf; - plitem_t *edicts = 0; - QFile *ent_file; - - QFS_StripExtension (mapname, name); - strcat (name, ".ent"); - ent_file = QFS_VOpenFile (name, 0, cl.model_precache[1]->vpath); - if ((buf = (char *) QFS_LoadFile (ent_file, 0))) { - edicts = ED_Parse (&edpr, buf); - free (buf); - } else { - edicts = ED_Parse (&edpr, cl.model_precache[1]->entities); - } - free (name); - return edicts; -} - static void CL_NewMap (const char *mapname) { - r_funcs->R_NewMap (cl.worldmodel, cl.model_precache, cl.nummodels); + CL_World_NewMap (mapname, 0); + cl.chasestate.worldmodel = cl_world.scene->worldmodel; + Con_NewMap (); - Hunk_Check (); // make sure nothing is hurt Sbar_CenterPrint (0); - if (cl.model_precache[1] && cl.model_precache[1]->entities) { - cl.edicts = map_ent (mapname); - if (cl.edicts) { - cl.worldspawn = PL_ObjectAtIndex (cl.edicts, 0); - CL_LoadSky (); - if (r_funcs->Fog_ParseWorldspawn) - r_funcs->Fog_ParseWorldspawn (cl.worldspawn); - } - } - - map_cfg (mapname, 1); + Hunk_Check (0); // make sure nothing is hurt } static void @@ -334,8 +266,9 @@ CL_ParseServerInfo (void) char sound_precache[MAX_SOUNDS][MAX_QPATH]; const char *str; int i; + int nummodels; - Sys_MaskPrintf (SYS_DEV, "Serverinfo packet received.\n"); + Sys_MaskPrintf (SYS_dev, "Serverinfo packet received.\n"); S_BlockSound (); S_StopAllSounds (); @@ -361,20 +294,26 @@ CL_ParseServerInfo (void) Sys_Printf ("Bad maxclients (%u) from server\n", cl.maxclients); goto done; } - cl.scores = Hunk_AllocName (cl.maxclients * sizeof (*cl.scores), "scores"); + cl.players = Hunk_AllocName (0, cl.maxclients * sizeof (*cl.players), + "players"); + cl.viewstate.voffs_enabled = cl.maxclients == 1; + cl.viewstate.bob_enabled = 1; for (i = 0; i < cl.maxclients; i++) { - cl.scores[i].info = Info_ParseString ("name\\", 0, 0); - cl.scores[i].name = Info_Key (cl.scores[i].info, "name"); - cl.scores[i].topcolor = 0; - cl.scores[i].bottomcolor = 0; + cl.players[i].userinfo = Info_ParseString ("name\\", 0, 0); + cl.players[i].name = Info_Key (cl.players[i].userinfo, "name"); + cl.players[i].topcolor = 0; + cl.players[i].bottomcolor = 0; } + Sbar_SetPlayers (cl.players, cl.maxclients); + Sbar_SetTeamplay (teamplay);//FIXME updates? // parse gametype - cl.gametype = MSG_ReadByte (net_message); + Sbar_SetGameType (MSG_ReadByte (net_message)); // parse signon message str = MSG_ReadString (net_message); strncpy (cl.levelname, str, sizeof (cl.levelname) - 1); + Sbar_SetLevelName (cl.levelname, 0); // separate the printfs so the server message can have a color Sys_Printf ("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36" @@ -387,16 +326,17 @@ CL_ParseServerInfo (void) // needlessly purge it // precache models - memset (cl.model_precache, 0, sizeof (cl.model_precache)); - for (cl.nummodels = 1;; cl.nummodels++) { + cl_world.models.size = 0; + DARRAY_APPEND (&cl_world.models, 0); // ind 0 is null model + for (nummodels = 1;; nummodels++) { str = MSG_ReadString (net_message); if (!str[0]) break; - if (cl.nummodels >= MAX_MODELS) { + if (nummodels >= MAX_MODELS) { Sys_Printf ("Server sent too many model precaches\n"); goto done; } - strcpy (model_precache[cl.nummodels], str); + strcpy (model_precache[nummodels], str); Mod_TouchModel (str); } @@ -413,13 +353,13 @@ CL_ParseServerInfo (void) strcpy (sound_precache[cl.numsounds], str); } - // now we try to load everything else until a cache allocation fails - if (model_precache[1]) - map_cfg (model_precache[1], 0); + CL_MapCfg (model_precache[1]); - for (i = 1; i < cl.nummodels; i++) { - cl.model_precache[i] = Mod_ForName (model_precache[i], false); - if (cl.model_precache[i] == NULL) { + // now we try to load everything else until a cache allocation fails + for (i = 1; i < nummodels; i++) { + DARRAY_APPEND (&cl_world.models, + Mod_ForName (model_precache[i], false)); + if (cl_world.models.a[i] == NULL) { Sys_Printf ("Model %s not found\n", model_precache[i]); goto done; } @@ -432,17 +372,11 @@ CL_ParseServerInfo (void) } // local state - cl_entities[0].model = cl.worldmodel = cl.model_precache[1]; - if (!centerprint) - centerprint = dstring_newstr (); - else - dstring_clearstr (centerprint); + Sbar_CenterPrint (0); CL_NewMap (model_precache[1]); - Hunk_Check (); // make sure nothing is hurt - noclip_anglehack = false; // noclip is turned off at start - r_data->gravity = 800.0; // Set up gravity for renderer effects + CL_ParticlesGravity (800); // Set up gravity for renderer effects done: S_UnblockSound (); } @@ -462,7 +396,7 @@ CL_ParseUpdate (int bits) entity_state_t *baseline; entity_state_t *state; int modnum, num, i; - qboolean forcelink; + bool forcelink; if (cls.signon == so_begin) { // first update is the final signon stage @@ -488,7 +422,7 @@ CL_ParseUpdate (int bits) num = MSG_ReadByte (net_message); baseline = CL_EntityNum (num); - state = &nq_entstates.frame[0 + cl.mindex][num]; + state = &nq_entstates.frame[0 + cl.frameIndex][num]; for (i = 0; i < 16; i++) if (bits & (1 << i)) @@ -590,45 +524,12 @@ CL_ParseUpdate (int bits) //VectorCopy (state->msg_origins[0], state->msg_origins[1]); //VectorCopy (state->msg_origins[0], ent->origin); //VectorCopy (state->msg_angles[0], state->msg_angles[1]); - //CL_TransformEntity (ent, state->msg_angles[0], true); + //CL_TransformEntity (ent, state->msg_angles[0]); //state->forcelink = true; - cl_forcelink[num] = true; + SET_ADD (&cl_forcelink, num); } } -static void -CL_ParseBaseline (entity_state_t *baseline, int version) -{ - int bits = 0; - - if (version == 2) - bits = MSG_ReadByte (net_message); - - if (bits & B_LARGEMODEL) - baseline->modelindex = MSG_ReadShort (net_message); - else - baseline->modelindex = MSG_ReadByte (net_message); - - if (bits & B_LARGEFRAME) - baseline->frame = MSG_ReadShort (net_message); - else - baseline->frame = MSG_ReadByte (net_message); - - baseline->colormap = MSG_ReadByte (net_message); - baseline->skinnum = MSG_ReadByte (net_message); - - MSG_ReadCoordAngleV (net_message, baseline->origin, baseline->angles); - - if (bits & B_ALPHA) - baseline->alpha = MSG_ReadByte (net_message); - else - baseline->alpha = 255;//FIXME alpha - baseline->scale = 16; - baseline->glow_size = 0; - baseline->glow_color = 254; - baseline->colormod = 255; -} - /* CL_ParseClientdata @@ -647,27 +548,28 @@ CL_ParseClientdata (void) bits |= MSG_ReadByte (net_message) << 24; if (bits & SU_VIEWHEIGHT) - cl.viewheight = ((signed char) MSG_ReadByte (net_message)); + cl.viewstate.height = ((signed char) MSG_ReadByte (net_message)); else - cl.viewheight = DEFAULT_VIEWHEIGHT; + cl.viewstate.height = DEFAULT_VIEWHEIGHT; if (bits & SU_IDEALPITCH) - cl.idealpitch = ((signed char) MSG_ReadByte (net_message)); + cl.viewstate.idealpitch = ((signed char) MSG_ReadByte (net_message)); else - cl.idealpitch = 0; + cl.viewstate.idealpitch = 0; - VectorCopy (cl.mvelocity[0], cl.mvelocity[1]); + cl.frameVelocity[1] = cl.frameVelocity[0]; + vec3_t punchangle = { }; for (i = 0; i < 3; i++) { - if (bits & (SU_PUNCH1 << i)) - cl.punchangle[i] = ((signed char) MSG_ReadByte (net_message)); - else - cl.punchangle[i] = 0; + if (bits & (SU_PUNCH1 << i)) { + punchangle[i] = ((signed char) MSG_ReadByte (net_message)); + } if (bits & (SU_VELOCITY1 << i)) - cl.mvelocity[0][i] = ((signed char) MSG_ReadByte (net_message)) + cl.frameVelocity[0][i] = ((signed char) MSG_ReadByte (net_message)) * 16; else - cl.mvelocity[0][i] = 0; + cl.frameVelocity[0][i] = 0; } + AngleQuat (punchangle, (vec_t*)&cl.viewstate.punchangle);//FIXME //FIXME //if (!VectorCompare (v_punchangles[0], cl.punchangle[0])) { @@ -679,20 +581,23 @@ CL_ParseClientdata (void) i = MSG_ReadLong (net_message); if (cl.stats[STAT_ITEMS] != i) { // set flash times - Sbar_Changed (); for (j = 0; j < 32; j++) if ((i & (1 << j)) && !(cl.stats[STAT_ITEMS] & (1 << j))) cl.item_gettime[j] = cl.time; cl.stats[STAT_ITEMS] = i; +#define IT_POWER (IT_QUAD | IT_SUIT | IT_INVULNERABILITY | IT_INVISIBILITY) + cl.viewstate.powerup_index = (cl.stats[STAT_ITEMS] & IT_POWER) >> 19; + Sbar_UpdateStats (STAT_ITEMS); } - cl.onground = (bits & SU_ONGROUND) ? 0 : -1; + cl.viewstate.onground = (bits & SU_ONGROUND) ? 0 : -1; cl.inwater = (bits & SU_INWATER) != 0; if (bits & SU_WEAPONFRAME) cl.stats[STAT_WEAPONFRAME] = MSG_ReadByte (net_message); else cl.stats[STAT_WEAPONFRAME] = 0; + cl.viewstate.weaponframe = cl.stats[STAT_WEAPONFRAME]; if (bits & SU_ARMOR) i = MSG_ReadByte (net_message); @@ -700,7 +605,7 @@ CL_ParseClientdata (void) i = 0; if (cl.stats[STAT_ARMOR] != i) { cl.stats[STAT_ARMOR] = i; - Sbar_Changed (); + Sbar_UpdateStats (STAT_ARMOR); } if (bits & SU_WEAPON) @@ -709,26 +614,27 @@ CL_ParseClientdata (void) i = 0; if (cl.stats[STAT_WEAPON] != i) { cl.stats[STAT_WEAPON] = i; - Sbar_Changed (); + cl.viewstate.weapon_model = cl_world.models.a[cl.stats[STAT_WEAPON]]; + Sbar_UpdateStats (STAT_WEAPON); } i = (short) MSG_ReadShort (net_message); if (cl.stats[STAT_HEALTH] != i) { cl.stats[STAT_HEALTH] = i; - Sbar_Changed (); + Sbar_UpdateStats (STAT_HEALTH); } i = MSG_ReadByte (net_message); if (cl.stats[STAT_AMMO] != i) { cl.stats[STAT_AMMO] = i; - Sbar_Changed (); + Sbar_UpdateStats (STAT_AMMO); } for (i = 0; i < 4; i++) { j = MSG_ReadByte (net_message); if (cl.stats[STAT_SHELLS + i] != j) { cl.stats[STAT_SHELLS + i] = j; - Sbar_Changed (); + Sbar_UpdateStats (STAT_SHELLS + i); } } @@ -737,18 +643,20 @@ CL_ParseClientdata (void) if (standard_quake) { if (cl.stats[STAT_ACTIVEWEAPON] != i) { cl.stats[STAT_ACTIVEWEAPON] = i; - Sbar_Changed (); + Sbar_UpdateStats (STAT_ACTIVEWEAPON); } } else { // hipnotic/rogue weapon "bit field" (stupid idea) if (cl.stats[STAT_ACTIVEWEAPON] != (1 << i)) { cl.stats[STAT_ACTIVEWEAPON] = (1 << i); - Sbar_Changed (); + Sbar_UpdateStats (STAT_ACTIVEWEAPON); } } - if (bits & SU_WEAPON2) + if (bits & SU_WEAPON2) { cl.stats[STAT_WEAPON] |= MSG_ReadByte (net_message) << 8; + cl.viewstate.weapon_model = cl_world.models.a[cl.stats[STAT_WEAPON]]; + } if (bits & SU_ARMOR2) cl.stats[STAT_ARMOR] |= MSG_ReadByte (net_message) << 8; if (bits & SU_AMMO2) @@ -763,53 +671,31 @@ CL_ParseClientdata (void) cl.stats[STAT_CELLS] |= MSG_ReadByte (net_message) << 8; if (bits & SU_WEAPONFRAME2) cl.stats[STAT_WEAPONFRAME] |= MSG_ReadByte (net_message) << 8; + + renderer_t *renderer = Ent_GetComponent (cl.viewstate.weapon_entity.id, scene_renderer, cl_world.scene->reg); if (bits & SU_WEAPONALPHA) { byte alpha = MSG_ReadByte (net_message); - cl.viewent.colormod[3] = ENTALPHA_DECODE (alpha); + float a = ENTALPHA_DECODE (alpha); + renderer->colormod[3] = a; } else { - cl.viewent.colormod[3] = 1.0; + renderer->colormod[3] = 1; } } -static void -CL_ParseStatic (int version) -{ - entity_state_t baseline; - entity_t *ent; - - ent = r_funcs->R_AllocEntity (); - CL_Init_Entity (ent); - - CL_ParseBaseline (&baseline, version); - - // copy it to the current state - //FIXME alpha & lerp - ent->model = cl.model_precache[baseline.modelindex]; - ent->frame = baseline.frame; - ent->skin = 0; - ent->skinnum = baseline.skinnum; - VectorCopy (ent_colormod[baseline.colormod], ent->colormod); - ent->colormod[3] = ENTALPHA_DECODE (baseline.alpha); - ent->scale = baseline.scale / 16.0; - VectorCopy (baseline.origin, ent->origin); - CL_TransformEntity (ent, baseline.angles, true); - - r_funcs->R_AddEfrags (ent); -} - static void CL_ParseStaticSound (int version) { - int sound_num, vol, atten; - vec3_t org; + int sound_num; + float vol, atten; + vec4f_t org = { 0, 0, 0, 1 }; - MSG_ReadCoordV (net_message, org); + MSG_ReadCoordV (net_message, (vec_t*)&org);//FIXME if (version == 2) sound_num = MSG_ReadShort (net_message); else sound_num = MSG_ReadByte (net_message); - vol = MSG_ReadByte (net_message); - atten = MSG_ReadByte (net_message); + vol = MSG_ReadByte (net_message) / 255.0; + atten = MSG_ReadByte (net_message) / 64.0; S_StaticSound (cl.sound_precache[sound_num], org, vol, atten); } @@ -820,10 +706,11 @@ CL_SetStat (int stat, int value) if (stat < 0 || stat >= MAX_CL_STATS) Host_Error ("CL_SetStat: %i is invalid", stat); cl.stats[stat] = value; + Sbar_UpdateStats (stat); } #define SHOWNET(x) \ - if (cl_shownet->int_val == 2) \ + if (cl_shownet == 2) \ Sys_Printf ("%3i:%s\n", net_message->readcount - 1, x); void @@ -834,13 +721,19 @@ CL_ParseServerMessage (void) static dstring_t *stuffbuf; signon_t so; + cl.viewstate.last_servermessage = cl.time; + TEntContext_t tentCtx = { + cl.viewstate.player_origin, + cl.viewentity + }; + // if recording demos, copy the message out - if (cl_shownet->int_val == 1) + if (cl_shownet == 1) Sys_Printf ("%i ", net_message->message->cursize); - else if (cl_shownet->int_val == 2) + else if (cl_shownet == 2) Sys_Printf ("------------------\n"); - cl.onground = -1; // unless the server says otherwise + cl.viewstate.onground = -1; // unless the server says otherwise // parse the message MSG_BeginReading (net_message); @@ -865,7 +758,7 @@ CL_ParseServerMessage (void) continue; } - SHOWNET (va ("%s(%d)", svc_strings[cmd], cmd)); + SHOWNET (va (0, "%s(%d)", svc_strings[cmd], cmd)); // other commands switch (cmd) { @@ -910,7 +803,7 @@ CL_ParseServerMessage (void) case svc_time: cl.mtime[1] = cl.mtime[0]; cl.mtime[0] = MSG_ReadFloat (net_message); - cl.mindex = !cl.mindex; + cl.frameIndex = !cl.frameIndex; break; case svc_print: @@ -921,16 +814,16 @@ CL_ParseServerMessage (void) str = MSG_ReadString (net_message); if (str[strlen (str) - 1] == '\n') { if (stuffbuf && stuffbuf->str[0]) { - Sys_MaskPrintf (SYS_DEV, "stufftext: %s%s\n", + Sys_MaskPrintf (SYS_dev, "stufftext: %s%s\n", stuffbuf->str, str); Cbuf_AddText (host_cbuf, stuffbuf->str); dstring_clearstr (stuffbuf); } else { - Sys_MaskPrintf (SYS_DEV, "stufftext: %s\n", str); + Sys_MaskPrintf (SYS_dev, "stufftext: %s\n", str); } Cbuf_AddText (host_cbuf, str); } else { - Sys_MaskPrintf (SYS_DEV, "partial stufftext: %s\n", str); + Sys_MaskPrintf (SYS_dev, "partial stufftext: %s\n", str); if (!stuffbuf) stuffbuf = dstring_newstr (); dstring_appendstr (stuffbuf, str); @@ -939,7 +832,7 @@ CL_ParseServerMessage (void) case svc_setangle: { - vec_t *dest = cl.viewangles; + vec_t *dest = cl.viewstate.player_angles; MSG_ReadAngleV (net_message, dest); break; @@ -952,8 +845,6 @@ CL_ParseServerMessage (void) } Cbuf_Execute_Stack (host_cbuf); CL_ParseServerInfo (); - // leave full screen intermission - r_data->vid->recalc_refdef = true; break; case svc_lightstyle: @@ -980,22 +871,22 @@ CL_ParseServerMessage (void) break; case svc_updatename: - Sbar_Changed (); i = MSG_ReadByte (net_message); if (i >= cl.maxclients) Host_Error ("CL_ParseServerMessage: svc_updatename > " "MAX_SCOREBOARD"); - Info_SetValueForKey (cl.scores[i].info, "name", + Info_SetValueForKey (cl.players[i].userinfo, "name", MSG_ReadString (net_message), 0); + Sbar_UpdateInfo (i); break; case svc_updatefrags: - Sbar_Changed (); i = MSG_ReadByte (net_message); if (i >= cl.maxclients) Host_Error ("CL_ParseServerMessage: svc_updatefrags > " "MAX_SCOREBOARD"); - cl.scores[i].frags = (short) MSG_ReadShort (net_message); + cl.players[i].frags = (short) MSG_ReadShort (net_message); + Sbar_UpdateFrags (i); break; case svc_clientdata: @@ -1008,35 +899,39 @@ CL_ParseServerMessage (void) break; case svc_updatecolors: - Sbar_Changed (); i = MSG_ReadByte (net_message); if (i >= cl.maxclients) { Host_Error ("CL_ParseServerMessage: svc_updatecolors > " "MAX_SCOREBOARD"); } else { - entity_t *ent = &cl_entities[i+1]; + entity_t ent = CL_GetEntity (i + 1); + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, cl_world.scene->reg); byte col = MSG_ReadByte (net_message); byte top = col >> 4; byte bot = col & 0xf; - if (top != cl.scores[i].topcolor - || bot != cl.scores[i].bottomcolor) + if (top != cl.players[i].topcolor + || bot != cl.players[i].bottomcolor) mod_funcs->Skin_SetTranslation (i + 1, top, bot); - cl.scores[i].topcolor = top; - cl.scores[i].bottomcolor = bot; - ent->skin = mod_funcs->Skin_SetColormap (ent->skin, i + 1); + cl.players[i].topcolor = top; + cl.players[i].bottomcolor = bot; + renderer->skin + = mod_funcs->Skin_SetColormap (renderer->skin, i + 1); + Sbar_UpdateInfo (i); } break; case svc_particle: - CL_ParseParticleEffect (); + CL_ParseParticleEffect (net_message); break; case svc_damage: - V_ParseDamage (); + V_ParseDamage (net_message, &cl.viewstate); + // put sbar face into pain frame + Sbar_Damage (cl.time); break; case svc_spawnstatic: - CL_ParseStatic (1); + CL_ParseStatic (net_message, 1); break; // svc_spawnbinary @@ -1044,11 +939,11 @@ CL_ParseServerMessage (void) case svc_spawnbaseline: i = MSG_ReadShort (net_message); // must use CL_EntityNum () to force cl.num_entities up - CL_ParseBaseline (CL_EntityNum (i), 1); + CL_ParseBaseline (net_message, CL_EntityNum (i), 1); break; case svc_temp_entity: - CL_ParseTEnt (); + CL_ParseTEnt_nq (net_message, cl.time, &tentCtx); break; case svc_setpause: @@ -1070,19 +965,17 @@ CL_ParseServerMessage (void) case svc_centerprint: str = MSG_ReadString (net_message); - if (strcmp (str, centerprint->str)) { - dstring_copystr (centerprint, str); - //FIXME logging - } Sbar_CenterPrint (str); break; case svc_killedmonster: cl.stats[STAT_MONSTERS]++; + Sbar_UpdateStats (STAT_MONSTERS); break; case svc_foundsecret: cl.stats[STAT_SECRETS]++; + Sbar_UpdateStats (STAT_SECRETS); break; case svc_spawnstaticsound: @@ -1090,23 +983,15 @@ CL_ParseServerMessage (void) break; case svc_intermission: - cl.intermission = 1; - r_data->force_fullscreen = 1; - cl.completed_time = cl.time; - r_data->vid->recalc_refdef = true; // go to full screen + Sbar_Intermission (cl.intermission = 1, cl.time); + SCR_SetFullscreen (1); break; case svc_finale: - cl.intermission = 2; - r_data->force_fullscreen = 1; - cl.completed_time = cl.time; - r_data->vid->recalc_refdef = true; // go to full screen str = MSG_ReadString (net_message); - if (strcmp (str, centerprint->str)) { - dstring_copystr (centerprint, str); - //FIXME logging - } Sbar_CenterPrint (str); + Sbar_Intermission (cl.intermission = 2, cl.time); + SCR_SetFullscreen (1); break; case svc_cdtrack: @@ -1124,16 +1009,10 @@ CL_ParseServerMessage (void) break; case svc_cutscene: - cl.intermission = 3; - r_data->force_fullscreen = 1; - cl.completed_time = cl.time; - r_data->vid->recalc_refdef = true; // go to full screen str = MSG_ReadString (net_message); - if (strcmp (str, centerprint->str)) { - dstring_copystr (centerprint, str); - //FIXME logging - } Sbar_CenterPrint (str); + Sbar_Intermission (cl.intermission = 3, cl.time); + SCR_SetFullscreen (1); break; // svc_smallkick (same value as svc_cutscene) @@ -1173,17 +1052,16 @@ CL_ParseServerMessage (void) blue = MSG_ReadByte (net_message) / 255.0; time = (short) MSG_ReadShort (net_message) / 100.0; time = max (0.0, time); - if (r_funcs->Fog_Update) - r_funcs->Fog_Update (density, red, green, blue, time); + Fog_Update (density, red, green, blue, time); } break; case svc_spawnbaseline2: i = MSG_ReadShort (net_message); // must use CL_EntityNum() to force cl.num_entities up - CL_ParseBaseline (CL_EntityNum(i), 2); + CL_ParseBaseline (net_message, CL_EntityNum(i), 2); break; case svc_spawnstatic2: - CL_ParseStatic (2); + CL_ParseStatic (net_message, 2); break; case svc_spawnstaticsound2: CL_ParseStaticSound (2); diff --git a/nq/source/cl_screen.c b/nq/source/cl_screen.c deleted file mode 100644 index 1a0f8e7be..000000000 --- a/nq/source/cl_screen.c +++ /dev/null @@ -1,149 +0,0 @@ -/* - cl_screen.c - - master for refresh, status bar, console, chat, notify, etc - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif - -#include - -#include "QF/console.h" -#include "QF/cvar.h" -#include "QF/draw.h" -#include "QF/image.h" -#include "QF/pcx.h" -#include "QF/screen.h" - -#include "QF/plugin/vid_render.h" - -#include "client.h" -#include "sbar.h" - -static qpic_t *scr_net; - -static void -SCR_DrawNet (void) -{ - if (realtime - cl.last_received_message < 0.3) - return; - if (cls.demoplayback) - return; - - if (!scr_net) - scr_net = r_funcs->Draw_PicFromWad ("net"); - - r_funcs->Draw_Pic (r_data->scr_vrect->x + 64, r_data->scr_vrect->y, - scr_net); -} - -static void -SCR_DrawLoading (void) -{ - qpic_t *pic; - - if (!cl.loading) - return; - pic = r_funcs->Draw_CachePic ("gfx/loading.lmp", 1); - r_funcs->Draw_Pic ((r_data->vid->conwidth - pic->width) / 2, - (r_data->vid->conheight - 48 - pic->height) / 2, pic); -} - -static void -SCR_CShift (void) -{ - mleaf_t *leaf; - int contents = CONTENTS_EMPTY; - - if (cls.state == ca_active && cl.worldmodel) { - leaf = Mod_PointInLeaf (r_data->refdef->vieworg, cl.worldmodel); - contents = leaf->contents; - } - V_SetContentsColor (contents); - r_funcs->Draw_BlendScreen (r_data->vid->cshift_color); -} - -static SCR_Func scr_funcs_normal[] = { - 0, //Draw_Crosshair, - 0, //SCR_DrawRam, - 0, //SCR_DrawTurtle, - 0, //SCR_DrawPause, - SCR_DrawNet, - Sbar_Draw, - SCR_CShift, - Sbar_DrawCenterPrint, - Con_DrawConsole, - SCR_DrawLoading, - 0 -}; - -static SCR_Func scr_funcs_intermission[] = { - Sbar_IntermissionOverlay, - Con_DrawConsole, - 0 -}; - -static SCR_Func scr_funcs_finale[] = { - Sbar_FinaleOverlay, - Con_DrawConsole, - 0, -}; - -static SCR_Func *scr_funcs[] = { - scr_funcs_normal, - scr_funcs_intermission, - scr_funcs_finale, -}; - -void -CL_UpdateScreen (double realtime) -{ - unsigned index = cl.intermission; - - if (index >= sizeof (scr_funcs) / sizeof (scr_funcs[0])) - index = 0; - - //FIXME not every time - if (cls.state == ca_active) { - if (cl.watervis) - r_data->min_wateralpha = 0.0; - else - r_data->min_wateralpha = 1.0; - } - scr_funcs_normal[0] = r_funcs->Draw_Crosshair; - scr_funcs_normal[1] = r_funcs->SCR_DrawRam; - scr_funcs_normal[2] = r_funcs->SCR_DrawTurtle; - scr_funcs_normal[3] = r_funcs->SCR_DrawPause; - - V_PrepBlend (); - r_funcs->SCR_UpdateScreen (realtime, V_RenderView, scr_funcs[index]); -} diff --git a/nq/source/cl_tent.c b/nq/source/cl_tent.c deleted file mode 100644 index 11c5c9fde..000000000 --- a/nq/source/cl_tent.c +++ /dev/null @@ -1,622 +0,0 @@ -/* - cl_tent.c - - client side temporary entities - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif - -#include -#include - -#include "QF/model.h" -#include "QF/msg.h" -#include "QF/sound.h" -#include "QF/sys.h" - -#include "QF/plugin/vid_render.h" - -#include "client.h" -#include "compat.h" - -typedef struct tent_s { - struct tent_s *next; - entity_t ent; -} tent_t; - -#define TEMP_BATCH 64 -static tent_t *temp_entities = 0; - -typedef struct { - int entity; - struct model_s *model; - float endtime; - vec3_t start, end; - tent_t *tents; - int seed; -} beam_t; - -#define BEAM_SEED_INTERVAL 72 -#define BEAM_SEED_PRIME 3191 - -typedef struct { - float start; - tent_t *tent; -} explosion_t; - -typedef struct tent_obj_s { - struct tent_obj_s *next; - union { - beam_t beam; - explosion_t ex; - } to; -} tent_obj_t; - -static tent_obj_t *tent_objects; -static tent_obj_t *cl_beams; -static tent_obj_t *cl_explosions; - -static sfx_t *cl_sfx_wizhit; -static sfx_t *cl_sfx_knighthit; -static sfx_t *cl_sfx_tink1; -static sfx_t *cl_sfx_ric1; -static sfx_t *cl_sfx_ric2; -static sfx_t *cl_sfx_ric3; -static sfx_t *cl_sfx_r_exp3; - -static model_t *cl_mod_beam; -static model_t *cl_mod_bolt; -static model_t *cl_mod_bolt2; -static model_t *cl_mod_bolt3; -static model_t *cl_spr_explod; - -static void -CL_TEnts_Precache (int phase) -{ - if (!phase) - return; - cl_sfx_wizhit = S_PrecacheSound ("wizard/hit.wav"); - cl_sfx_knighthit = S_PrecacheSound ("hknight/hit.wav"); - cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav"); - cl_sfx_ric1 = S_PrecacheSound ("weapons/ric1.wav"); - cl_sfx_ric2 = S_PrecacheSound ("weapons/ric2.wav"); - cl_sfx_ric3 = S_PrecacheSound ("weapons/ric3.wav"); - cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav"); - - cl_mod_bolt = Mod_ForName ("progs/bolt.mdl", true); - cl_mod_bolt2 = Mod_ForName ("progs/bolt2.mdl", true); - cl_mod_bolt3 = Mod_ForName ("progs/bolt3.mdl", true); - cl_spr_explod = Mod_ForName ("progs/s_explod.spr", true); - cl_mod_beam = Mod_ForName ("progs/beam.mdl", false); - if (!cl_mod_beam) - cl_mod_beam = cl_mod_bolt; -} - -void -CL_TEnts_Init (void) -{ - QFS_GamedirCallback (CL_TEnts_Precache); - CL_TEnts_Precache (1); -} - -void -CL_Init_Entity (entity_t *ent) -{ - memset (ent, 0, sizeof (*ent)); - - ent->skin = 0; - QuatSet (1.0, 1.0, 1.0, 1.0, ent->colormod); - ent->scale = 1.0; - ent->pose1 = ent->pose2 = -1; -} - -static tent_t * -new_temp_entity (void) -{ - tent_t *tent; - if (!temp_entities) { - int i; - - temp_entities = malloc (TEMP_BATCH * sizeof (tent_t)); - for (i = 0; i < TEMP_BATCH - 1; i++) { - temp_entities[i].next = &temp_entities[i + 1]; - } - temp_entities[i].next = 0; - } - tent = temp_entities; - temp_entities = tent->next; - tent->next = 0; - CL_Init_Entity (&tent->ent); - return tent; -} - -static void -free_temp_entities (tent_t *tents) -{ - tent_t **t = &tents; - - while (*t) - t = &(*t)->next; - *t = temp_entities; - temp_entities = tents; -} - -static tent_obj_t * -new_tent_object (void) -{ - tent_obj_t *tobj; - if (!tent_objects) { - int i; - - tent_objects = malloc (TEMP_BATCH * sizeof (tent_t)); - for (i = 0; i < TEMP_BATCH - 1; i++) - tent_objects[i].next = &tent_objects[i + 1]; - tent_objects[i].next = 0; - } - tobj = tent_objects; - tent_objects = tobj->next; - tobj->next = 0; - return tobj; -} - -static void -free_tent_objects (tent_obj_t *tobjs) -{ - tent_obj_t **t = &tobjs; - - while (*t) - t = &(*t)->next; - *t = tent_objects; - tent_objects = tobjs; -} - -void -CL_ClearTEnts (void) -{ - tent_t *t; - tent_obj_t *to; - - for (to = cl_beams; to; to = to->next) { - for (t = to->to.beam.tents; t; t = t->next) - t->ent.efrag = 0; - free_temp_entities (to->to.beam.tents); - } - free_tent_objects (cl_beams); - cl_beams = 0; - - for (to = cl_explosions; to; to = to->next) { - for (t = to->to.ex.tent; t; t = t->next) - t->ent.efrag = 0; - free_temp_entities (to->to.ex.tent); - } - free_tent_objects (cl_explosions); - cl_explosions = 0; -} - -static inline void -beam_clear (beam_t *b) -{ - if (b->tents) { - tent_t *t; - - for (t = b->tents; t; t = t->next) { - r_funcs->R_RemoveEfrags (&t->ent); - t->ent.efrag = 0; - } - free_temp_entities (b->tents); - b->tents = 0; - } -} - -static inline void -beam_setup (beam_t *b, qboolean transform) -{ - tent_t *tent; - float forward, pitch, yaw, d; - int ent_count; - vec3_t dist, org, ang; - unsigned seed; - - // calculate pitch and yaw - VectorSubtract (b->end, b->start, dist); - - if (dist[1] == 0 && dist[0] == 0) { - yaw = 0; - if (dist[2] > 0) - pitch = 90; - else - pitch = 270; - } else { - yaw = (int) (atan2 (dist[1], dist[0]) * 180 / M_PI); - if (yaw < 0) - yaw += 360; - - forward = sqrt (dist[0] * dist[0] + dist[1] * dist[1]); - pitch = (int) (atan2 (dist[2], forward) * 180 / M_PI); - if (pitch < 0) - pitch += 360; - } - - // add new entities for the lightning - VectorCopy (b->start, org); - d = VectorNormalize (dist); - VectorScale (dist, 30, dist); - ent_count = ceil (d / 30); - d = 0; - - seed = b->seed + ((int) (cl.time * BEAM_SEED_INTERVAL) % - BEAM_SEED_INTERVAL); - - ang[ROLL] = 0; - while (ent_count--) { - tent = new_temp_entity (); - tent->next = b->tents; - b->tents = tent; - - VectorMultAdd (org, d, dist, tent->ent.origin); - d += 1.0; - tent->ent.model = b->model; - ang[PITCH] = pitch; - ang[YAW] = yaw; - if (transform) { - seed = seed * BEAM_SEED_PRIME; - ang[ROLL] = seed % 360; - CL_TransformEntity (&tent->ent, ang, true); - } - VectorCopy (ang, tent->ent.angles); - r_funcs->R_AddEfrags (&tent->ent); - } -} - -static void -CL_ParseBeam (model_t *m) -{ - tent_obj_t *to; - beam_t *b; - int ent; - vec3_t start, end; - - ent = MSG_ReadShort (net_message); - - MSG_ReadCoordV (net_message, start); - MSG_ReadCoordV (net_message, end); - - to = 0; - if (ent) { - for (to = cl_beams; to; to = to->next) - if (to->to.beam.entity == ent) - break; - } - if (!to) { - to = new_tent_object (); - to->next = cl_beams; - cl_beams = to; - to->to.beam.tents = 0; - to->to.beam.entity = ent; - } - b = &to->to.beam; - - beam_clear (b); - b->model = m; - b->endtime = cl.time + 0.2; - b->seed = rand (); - VectorCopy (end, b->end); - if (b->entity != cl.viewentity) { - // this will be done in CL_UpdateBeams - VectorCopy (start, b->start); - beam_setup (b, true); - } -} - -void -CL_ParseTEnt (void) -{ - byte type; - dlight_t *dl; - tent_obj_t *to; - explosion_t *ex; - int colorStart, colorLength; - quat_t col; - vec3_t pos; - sfx_t *spike_sound[] = { - cl_sfx_ric3, cl_sfx_ric3, cl_sfx_ric2, cl_sfx_ric1, - }; - - type = MSG_ReadByte (net_message); - switch (type) { - case TE_WIZSPIKE: // spike hitting wall - MSG_ReadCoordV (net_message, pos); - r_funcs->particles->R_WizSpikeEffect (pos); - S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1); - break; - - case TE_KNIGHTSPIKE: // spike hitting wall - MSG_ReadCoordV (net_message, pos); - r_funcs->particles->R_KnightSpikeEffect (pos); - S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1); - break; - - case TE_SPIKE: // spike hitting wall - MSG_ReadCoordV (net_message, pos); - r_funcs->particles->R_SpikeEffect (pos); - { - int i; - sfx_t *sound; - - i = (rand () % 20) - 16; - if (i >= 0) - sound = spike_sound[i]; - else - sound = cl_sfx_tink1; - S_StartSound (-1, 0, sound, pos, 1, 1); - } - break; - - case TE_SUPERSPIKE: // super spike hitting wall - MSG_ReadCoordV (net_message, pos); - r_funcs->particles->R_SuperSpikeEffect (pos); - { - int i; - sfx_t *sound; - - i = (rand () % 20) - 16; - if (i >= 0) - sound = spike_sound[i]; - else - sound = cl_sfx_tink1; - S_StartSound (-1, 0, sound, pos, 1, 1); - } - break; - - case TE_EXPLOSION: // rocket explosion - // particles - MSG_ReadCoordV (net_message, pos); - r_funcs->particles->R_ParticleExplosion (pos); - - // light - dl = r_funcs->R_AllocDlight (0); - if (dl) { - VectorCopy (pos, dl->origin); - dl->radius = 350; - dl->die = cl.time + 0.5; - dl->decay = 300; - QuatSet (1.0, 0.5, 0.25, 0.7, dl->color); - } - - // sound - S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); - - // sprite - to = new_tent_object (); - to->next = cl_explosions; - cl_explosions = to; - ex = &to->to.ex; - ex->tent = new_temp_entity (); - - VectorCopy (pos, ex->tent->ent.origin); - ex->start = cl.time; - //FIXME need better model management - if (!cl_spr_explod->cache.data) - cl_spr_explod = Mod_ForName ("progs/s_explod.spr", true); - ex->tent->ent.model = cl_spr_explod; - CL_TransformEntity (&ex->tent->ent, ex->tent->ent.angles, true); - break; - - case TE_TAREXPLOSION: // tarbaby explosion - MSG_ReadCoordV (net_message, pos); - r_funcs->particles->R_BlobExplosion (pos); - - S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); - break; - - case TE_LIGHTNING1: // lightning bolts - CL_ParseBeam (cl_mod_bolt); - break; - - case TE_LIGHTNING2: // lightning bolts - CL_ParseBeam (cl_mod_bolt2); - break; - - case TE_LIGHTNING3: // lightning bolts - CL_ParseBeam (cl_mod_bolt3); - break; - - case TE_LIGHTNING4NEH: // Nehahra lightning - CL_ParseBeam (Mod_ForName (MSG_ReadString (net_message), true)); - break; - - // PGM 01/21/97 - case TE_BEAM: // grappling hook beam - CL_ParseBeam (cl_mod_beam); - break; - // PGM 01/21/97 - - case TE_LAVASPLASH: - MSG_ReadCoordV (net_message, pos); - r_funcs->particles->R_LavaSplash (pos); - break; - - case TE_TELEPORT: - MSG_ReadCoordV (net_message, pos); - r_funcs->particles->R_TeleportSplash (pos); - break; - - case TE_EXPLOSION2: // color mapped explosion - MSG_ReadCoordV (net_message, pos); - colorStart = MSG_ReadByte (net_message); - colorLength = MSG_ReadByte (net_message); - S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); - r_funcs->particles->R_ParticleExplosion2 (pos, colorStart, - colorLength); - dl = r_funcs->R_AllocDlight (0); - if (!dl) - break; - VectorCopy (pos, dl->origin); - dl->radius = 350; - dl->die = cl.time + 0.5; - dl->decay = 300; - colorStart = (colorStart + (rand () % colorLength)) * 3; - VectorScale (&r_data->vid->palette[colorStart], 1.0 / 255.0, - dl->color); - dl->color[3] = 0.7; - break; - - case TE_EXPLOSION3: // Nehahra colored light explosion - MSG_ReadCoordV (net_message, pos); - MSG_ReadCoordV (net_message, col); // OUCH! - col[3] = 0.7; - r_funcs->particles->R_ParticleExplosion (pos); - S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); - dl = r_funcs->R_AllocDlight (0); - if (dl) { - VectorCopy (pos, dl->origin); - dl->radius = 350; - dl->die = cl.time + 0.5; - dl->decay = 300; - QuatCopy (col, dl->color); - } - break; - - case TE_GUNSHOT: // bullet hitting wall - MSG_ReadCoordV (net_message, pos); - r_funcs->particles->R_GunshotEffect (pos, 20); - break; - - default: - Sys_Error ("CL_ParseTEnt: bad type %d", type); - } -} - -static void -CL_UpdateBeams (void) -{ - tent_obj_t **to; - beam_t *b; - unsigned seed; - tent_t *t; - - // update lightning - for (to = &cl_beams; *to; ) { - b = &(*to)->to.beam; - if (!b->endtime) - continue; - if (!b->model || b->endtime < cl.time) { - tent_obj_t *_to; - b->endtime = 0; - beam_clear (b); - _to = *to; - *to = _to->next; - _to->next = tent_objects; - tent_objects = _to; - continue; - } - to = &(*to)->next; - - // if coming from the player, update the start position - if (b->entity == cl.viewentity) { - beam_clear (b); - VectorCopy (cl_entities[cl.viewentity].origin, b->start); - beam_setup (b, false); - } - - seed = b->seed + ((int) (cl.time * BEAM_SEED_INTERVAL) % - BEAM_SEED_INTERVAL); - - // add new entities for the lightning - for (t = b->tents; t; t = t->next) { - seed = seed * BEAM_SEED_PRIME; - t->ent.angles[ROLL] = seed % 360; - CL_TransformEntity (&t->ent, t->ent.angles, true); - } - } -} - -static void -CL_UpdateExplosions (void) -{ - int f; - tent_obj_t **to; - explosion_t *ex; - entity_t *ent; - - for (to = &cl_explosions; *to; ) { - ex = &(*to)->to.ex; - ent = &ex->tent->ent; - f = 10 * (cl.time - ex->start); - if (f >= ent->model->numframes) { - tent_obj_t *_to; - r_funcs->R_RemoveEfrags (ent); - ent->efrag = 0; - free_temp_entities (ex->tent); - _to = *to; - *to = _to->next; - _to->next = tent_objects; - tent_objects = _to; - continue; - } - to = &(*to)->next; - - ent->frame = f; - if (!ent->efrag) - r_funcs->R_AddEfrags (ent); - } -} - -void -CL_UpdateTEnts (void) -{ - CL_UpdateBeams (); - CL_UpdateExplosions (); -} - -/* - CL_ParseParticleEffect - - Parse an effect out of the server message -*/ -void -CL_ParseParticleEffect (void) -{ - int i, count, color; - vec3_t org, dir; - - MSG_ReadCoordV (net_message, org); - for (i = 0; i < 3; i++) - dir[i] = ((signed char) MSG_ReadByte (net_message)) * (15.0 / 16.0); - count = MSG_ReadByte (net_message); - color = MSG_ReadByte (net_message); - - if (count == 255) - r_funcs->particles->R_ParticleExplosion (org); - else - r_funcs->particles->R_RunParticleEffect (org, dir, color, count); -} diff --git a/nq/source/cl_view.c b/nq/source/cl_view.c deleted file mode 100644 index 6def74958..000000000 --- a/nq/source/cl_view.c +++ /dev/null @@ -1,819 +0,0 @@ -/* - cl_view.c - - player eye positioning - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "QF/cmd.h" -#include "QF/cvar.h" -#include "QF/msg.h" -#include "QF/screen.h" - -#include "QF/plugin/vid_render.h" - -#include "chase.h" -#include "client.h" -#include "compat.h" -#include "host.h" -#include "clview.h" - -/* - The view is allowed to move slightly from it's true position for bobbing, - but if it exceeds 8 pixels linear distance (spherical, not box), the list - of entities sent from the server may not include everything in the pvs, - especially when crossing a water boudnary. -*/ - -cvar_t *scr_ofsx; -cvar_t *scr_ofsy; -cvar_t *scr_ofsz; - -cvar_t *cl_rollspeed; -cvar_t *cl_rollangle; - -cvar_t *cl_bob; -cvar_t *cl_bobcycle; -cvar_t *cl_bobup; - -cvar_t *v_centermove; -cvar_t *v_centerspeed; - -cvar_t *v_kicktime; -cvar_t *v_kickroll; -cvar_t *v_kickpitch; - -cvar_t *v_iyaw_cycle; -cvar_t *v_iroll_cycle; -cvar_t *v_ipitch_cycle; -cvar_t *v_iyaw_level; -cvar_t *v_iroll_level; -cvar_t *v_ipitch_level; - -cvar_t *v_idlescale; - -float v_dmg_time, v_dmg_roll, v_dmg_pitch; - -cshift_t cshift_empty = { {130, 80, 50}, 0}; -cshift_t cshift_water = { {130, 80, 50}, 128}; -cshift_t cshift_slime = { {0, 25, 5}, 150}; -cshift_t cshift_lava = { {255, 80, 0}, 150}; -cshift_t cshift_bonus = { {215, 186, 60}, 50}; - -#define sqr(x) ((x) * (x)) - -float -V_CalcRoll (const vec3_t angles, const vec3_t velocity) -{ - float side, sign, value; - vec3_t forward, right, up; - - AngleVectors (angles, forward, right, up); - side = DotProduct (velocity, right); - sign = side < 0 ? -1 : 1; - side = fabs (side); - - value = cl_rollangle->value; - - if (side < cl_rollspeed->value) - side = side * value / cl_rollspeed->value; - else - side = value; - - return side * sign; -} - -static float -V_CalcBob (void) -{ - vec_t *velocity = cl.velocity; - float cycle; - static double bobtime; - static float bob; - - if (cl.spectator) - return 0; - - if (cl.onground == -1) - return bob; // just use old value - - bobtime += host_frametime; - cycle = bobtime - (int) (bobtime / cl_bobcycle->value) * - cl_bobcycle->value; - cycle /= cl_bobcycle->value; - if (cycle < cl_bobup->value) - cycle = cycle / cl_bobup->value; - else - cycle = 1 + (cycle - cl_bobup->value) / (1.0 - cl_bobup->value); - - // bob is proportional to velocity in the xy plane - // (don't count Z, or jumping messes it up) - - bob = sqrt (sqr (velocity[0]) + sqr (velocity[1])) * cl_bob->value; - bob = bob * 0.3 + bob * 0.7 * sin (cycle * M_PI); - if (bob > 4) - bob = 4; - else if (bob < -7) - bob = -7; - return bob; -} - -void -V_StartPitchDrift (void) -{ - if (cl.laststop == cl.time) { - return; // something else is keeping it from drifting - } - - if (cl.nodrift || !cl.pitchvel) { - cl.pitchvel = v_centerspeed->value; - cl.nodrift = false; - cl.driftmove = 0; - } -} - -void -V_StopPitchDrift (void) -{ - cl.laststop = cl.time; - cl.nodrift = true; - cl.pitchvel = 0; -} - -/* - V_DriftPitch - - Moves the client pitch angle towards cl.idealpitch sent by the server. - - If the user is adjusting pitch manually, either with lookup/lookdown, - mlook and mouse, or klook and keyboard, pitch drifting is constantly - stopped. - - Drifting is enabled when the center view key is hit, mlook is released - and lookspring is non 0, or when -*/ -static void -V_DriftPitch (void) -{ - float delta, move; - usercmd_t *cmd = &cl.cmd; - - if (noclip_anglehack || cl.onground == -1 || cls.demoplayback) { - cl.driftmove = 0; - cl.pitchvel = 0; - return; - } - - // don't count small mouse motion - if (cl.nodrift) { - if (fabs (cmd->forwardmove) < cl_forwardspeed->value) - cl.driftmove = 0; - else - cl.driftmove += host_frametime; - - if (cl.driftmove > v_centermove->value) { - V_StartPitchDrift (); - } - return; - } - - delta = cl.idealpitch - cl.viewangles[PITCH]; - - if (!delta) { - cl.pitchvel = 0; - return; - } - - move = host_frametime * cl.pitchvel; - cl.pitchvel += host_frametime * v_centerspeed->value; - - if (delta > 0) { - if (move > delta) { - cl.pitchvel = 0; - move = delta; - } - cl.viewangles[PITCH] += move; - } else if (delta < 0) { - if (move > -delta) { - cl.pitchvel = 0; - move = -delta; - } - cl.viewangles[PITCH] -= move; - } -} - -/* PALETTE FLASHES */ - -void -V_ParseDamage (void) -{ - entity_t *ent = &cl_entities[cl.viewentity]; - float count, side; - int armor, blood; - vec_t *origin = ent->origin; - vec_t *angles = ent->angles; - vec3_t from, forward, right, up; - - armor = MSG_ReadByte (net_message); - blood = MSG_ReadByte (net_message); - MSG_ReadCoordV (net_message, from); - - count = blood * 0.5 + armor * 0.5; - if (count < 10) - count = 10; - - cl.faceanimtime = cl.time + 0.2; // but sbar face into pain frame - - if (cl_cshift_damage->int_val - || (cl.sv_cshifts & INFO_CSHIFT_DAMAGE)) { - cshift_t *cshift = &cl.cshifts[CSHIFT_DAMAGE]; - - cshift->percent += 3 * count; - cshift->percent = - bound (0, cshift->percent, 150); - - if (armor > blood) { - cshift->destcolor[0] = 200; - cshift->destcolor[1] = 100; - cshift->destcolor[2] = 100; - } else if (armor) { - cshift->destcolor[0] = 220; - cshift->destcolor[1] = 50; - cshift->destcolor[2] = 50; - } else { - cshift->destcolor[0] = 255; - cshift->destcolor[1] = 0; - cshift->destcolor[2] = 0; - } - cshift->initialpct = cshift->percent; - cshift->time = cl.time; - } - - // calculate view angle kicks - VectorSubtract (from, origin, from); - VectorNormalize (from); - - AngleVectors (angles, forward, right, up); - - side = DotProduct (from, right); - v_dmg_roll = count * side * v_kickroll->value; - - side = DotProduct (from, forward); - v_dmg_pitch = count * side * v_kickpitch->value; - - v_dmg_time = v_kicktime->value; -} - -static void -V_cshift_f (void) -{ - cshift_empty.destcolor[0] = atoi (Cmd_Argv (1)); - cshift_empty.destcolor[1] = atoi (Cmd_Argv (2)); - cshift_empty.destcolor[2] = atoi (Cmd_Argv (3)); - cshift_empty.percent = atoi (Cmd_Argv (4)); -} - -/* - V_BonusFlash_f - - When you run over an item, the server sends this command -*/ -static void -V_BonusFlash_f (void) -{ - if (!cl_cshift_bonus->int_val - && !(cl.sv_cshifts & INFO_CSHIFT_BONUS)) - return; - - cl.cshifts[CSHIFT_BONUS] = cshift_bonus; - cl.cshifts[CSHIFT_BONUS].initialpct = cl.cshifts[CSHIFT_BONUS].percent; - cl.cshifts[CSHIFT_BONUS].time = cl.time; -} - -/* - V_SetContentsColor - - Underwater, lava, etc each has a color shift -*/ -void -V_SetContentsColor (int contents) -{ - if (!cl_cshift_contents->int_val - && !(cl.sv_cshifts & INFO_CSHIFT_CONTENTS)) { - cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; - return; - } - - switch (contents) { - case CONTENTS_EMPTY: - cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; - break; - case CONTENTS_LAVA: - cl.cshifts[CSHIFT_CONTENTS] = cshift_lava; - break; - case CONTENTS_SOLID: - case CONTENTS_SLIME: - cl.cshifts[CSHIFT_CONTENTS] = cshift_slime; - break; - default: - cl.cshifts[CSHIFT_CONTENTS] = cshift_water; - } -} - -static void -V_CalcPowerupCshift (void) -{ - if (!cl.stats[STAT_ITEMS] & (IT_SUIT || IT_INVISIBILITY || IT_QUAD - || IT_INVULNERABILITY)) - { - cl.cshifts[CSHIFT_POWERUP].percent = 0; - return; - } - - if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY && - cl.stats[STAT_ITEMS] & IT_QUAD) { - cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255; - cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0; - cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255; - cl.cshifts[CSHIFT_POWERUP].percent = 30; - } else if (cl.stats[STAT_ITEMS] & IT_QUAD) { - cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0; - cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0; - cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255; - cl.cshifts[CSHIFT_POWERUP].percent = 30; - } else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) { - cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255; - cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255; - cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0; - cl.cshifts[CSHIFT_POWERUP].percent = 30; - } else if (cl.stats[STAT_ITEMS] & IT_SUIT) { - cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0; - cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255; - cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0; - cl.cshifts[CSHIFT_POWERUP].percent = 20; - } else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) { - cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100; - cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100; - cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100; - cl.cshifts[CSHIFT_POWERUP].percent = 100; - } else { - cl.cshifts[CSHIFT_POWERUP].percent = 0; - } -} - -/* - V_CalcBlend - - LordHavoc made this a real, true alpha blend. Cleaned it up - a bit, but otherwise this is his code. --KB -*/ -void -V_CalcBlend (void) -{ - float a2, a3; - float r = 0, g = 0, b = 0, a = 0; - int i; - - for (i = 0; i < NUM_CSHIFTS; i++) { - a2 = cl.cshifts[i].percent / 255.0; - - if (!a2) - continue; - - a2 = min (a2, 1.0); - r += (cl.cshifts[i].destcolor[0] - r) * a2; - g += (cl.cshifts[i].destcolor[1] - g) * a2; - b += (cl.cshifts[i].destcolor[2] - b) * a2; - - a3 = (1.0 - a) * (1.0 - a2); - a = 1.0 - a3; - } - - // LordHavoc: saturate color - if (a) { - a2 = 1.0 / a; - r *= a2; - g *= a2; - b *= a2; - } - - r_data->vid->cshift_color[0] = min (r, 255.0) / 255.0; - r_data->vid->cshift_color[1] = min (g, 255.0) / 255.0; - r_data->vid->cshift_color[2] = min (b, 255.0) / 255.0; - r_data->vid->cshift_color[3] = bound (0.0, a, 1.0); -} - -static void -V_DropCShift (cshift_t *cs, float droprate) -{ - if (cs->time < 0) { - cs->percent = 0; - } else { - cs->percent = cs->initialpct - (cl.time - cs->time) * droprate; - if (cs->percent <= 0) { - cs->percent = 0; - cs->time = -1; - } - } -} - -void -V_PrepBlend (void) -{ - int i, j; - - if (cl_cshift_powerup->int_val - || (cl.sv_cshifts & INFO_CSHIFT_POWERUP)) - V_CalcPowerupCshift (); - - r_data->vid->cshift_changed = false; - - for (i = 0; i < NUM_CSHIFTS; i++) { - if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent) { - r_data->vid->cshift_changed = true; - cl.prev_cshifts[i].percent = cl.cshifts[i].percent; - } - for (j = 0; j < 3; j++) { - if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j]) - { - r_data->vid->cshift_changed = true; - cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j]; - } - } - } - - // drop the damage value - V_DropCShift (&cl.cshifts[CSHIFT_DAMAGE], 150); - // drop the bonus value - V_DropCShift (&cl.cshifts[CSHIFT_BONUS], 100); - - if (!r_data->vid->cshift_changed && !r_data->vid->recalc_refdef) - return; - - V_CalcBlend (); -} - -/* VIEW RENDERING */ - -static float -angledelta (float a) -{ - a = anglemod (a); - if (a > 180) - a -= 360; - return a; -} - -static void -CalcGunAngle (void) -{ - float yaw, pitch, move; - static float oldpitch = 0, oldyaw = 0; - - yaw = r_data->refdef->viewangles[YAW]; - pitch = -r_data->refdef->viewangles[PITCH]; - - yaw = angledelta (yaw - r_data->refdef->viewangles[YAW]) * 0.4; - yaw = bound (-10, yaw, 10); - pitch = angledelta (-pitch - r_data->refdef->viewangles[PITCH]) * 0.4; - pitch = bound (-10, pitch, 10); - - move = host_frametime * 20; - if (yaw > oldyaw) { - if (oldyaw + move < yaw) - yaw = oldyaw + move; - } else { - if (oldyaw - move > yaw) - yaw = oldyaw - move; - } - - if (pitch > oldpitch) { - if (oldpitch + move < pitch) - pitch = oldpitch + move; - } else { - if (oldpitch - move > pitch) - pitch = oldpitch - move; - } - - oldyaw = yaw; - oldpitch = pitch; - - cl.viewent.angles[YAW] = r_data->refdef->viewangles[YAW] + yaw; - cl.viewent.angles[PITCH] = -(r_data->refdef->viewangles[PITCH] + pitch); -} - -static void -V_BoundOffsets (void) -{ - entity_t *ent = &cl_entities[cl.viewentity]; - vec_t *origin = ent->origin; - - // absolutely bound refresh reletive to entity clipping hull - // so the view can never be inside a solid wall - - if (r_data->refdef->vieworg[0] < origin[0] - 14) - r_data->refdef->vieworg[0] = origin[0] - 14; - else if (r_data->refdef->vieworg[0] > origin[0] + 14) - r_data->refdef->vieworg[0] = origin[0] + 14; - if (r_data->refdef->vieworg[1] < origin[1] - 14) - r_data->refdef->vieworg[1] = origin[1] - 14; - else if (r_data->refdef->vieworg[1] > origin[1] + 14) - r_data->refdef->vieworg[1] = origin[1] + 14; - if (r_data->refdef->vieworg[2] < origin[2] - 22) - r_data->refdef->vieworg[2] = origin[2] - 22; - else if (r_data->refdef->vieworg[2] > origin[2] + 30) - r_data->refdef->vieworg[2] = origin[2] + 30; -} - -/* - V_AddIdle - - Idle swaying -*/ -static void -V_AddIdle (void) -{ - r_data->refdef->viewangles[ROLL] += v_idlescale->value * - sin (cl.time * v_iroll_cycle->value) * v_iroll_level->value; - r_data->refdef->viewangles[PITCH] += v_idlescale->value * - sin (cl.time * v_ipitch_cycle->value) * v_ipitch_level->value; - r_data->refdef->viewangles[YAW] += v_idlescale->value * - sin (cl.time * v_iyaw_cycle->value) * v_iyaw_level->value; - - cl.viewent.angles[ROLL] -= v_idlescale->value * - sin (cl.time * v_iroll_cycle->value) * v_iroll_level->value; - cl.viewent.angles[PITCH] -= v_idlescale->value * - sin (cl.time * v_ipitch_cycle->value) * v_ipitch_level->value; - cl.viewent.angles[YAW] -= v_idlescale->value * - sin (cl.time * v_iyaw_cycle->value) * v_iyaw_level->value; -} - -/* - V_CalcViewRoll - - Roll is induced by movement and damage -*/ -static void -V_CalcViewRoll (void) -{ - float side; - vec_t *angles = cl_entities[cl.viewentity].angles; - vec_t *velocity = cl.velocity; - - side = V_CalcRoll (angles, velocity); - r_data->refdef->viewangles[ROLL] += side; - - if (v_dmg_time > 0) { - r_data->refdef->viewangles[ROLL] += - v_dmg_time / v_kicktime->value * v_dmg_roll; - r_data->refdef->viewangles[PITCH] += - v_dmg_time / v_kicktime->value * v_dmg_pitch; - v_dmg_time -= host_frametime; - } - - if (cl.stats[STAT_HEALTH] <= 0) - r_data->refdef->viewangles[ROLL] = 80; // dead view angle -} - -static void -V_CalcIntermissionRefdef (void) -{ - // ent is the player model (visible when out of body) - entity_t *ent = &cl_entities[cl.viewentity]; - entity_t *view; - float old; - vec_t *origin = ent->origin; - vec_t *angles = ent->angles; - - // view is the weapon model (visible only from inside body) - view = &cl.viewent; - - VectorCopy (origin, r_data->refdef->vieworg); - VectorCopy (angles, r_data->refdef->viewangles); - view->model = NULL; - - // always idle in intermission - old = v_idlescale->value; - Cvar_SetValue (v_idlescale, 1); - V_AddIdle (); - Cvar_SetValue (v_idlescale, old); -} - -static void -V_CalcRefdef (void) -{ - // ent is the player model (visible when out of body) - entity_t *ent = &cl_entities[cl.viewentity]; - // view is the weapon model (visible only from inside body) - entity_t *view = &cl.viewent; - float bob; - static float oldz = 0; - int i; - vec3_t forward, right, up; - vec_t *origin = ent->origin; - vec_t *viewangles = cl.viewangles; - - V_DriftPitch (); - - bob = V_CalcBob (); - - // refresh position - VectorCopy (origin, r_data->refdef->vieworg); - r_data->refdef->vieworg[2] += cl.viewheight + bob; - - // never let it sit exactly on a node line, because a water plane can - // disappear when viewed with the eye exactly on it. - // server protocol specifies to only 1/8 pixel, so add 1/16 in each axis - r_data->refdef->vieworg[0] += 1.0 / 16; - r_data->refdef->vieworg[1] += 1.0 / 16; - r_data->refdef->vieworg[2] += 1.0 / 16; - - VectorCopy (viewangles, r_data->refdef->viewangles); - V_CalcViewRoll (); - V_AddIdle (); - - // offsets - AngleVectors (viewangles, forward, right, up); - - // don't allow cheats in multiplayer - // FIXME check for dead - if (cl.maxclients == 1) { - for (i = 0; i < 3; i++) { - r_data->refdef->vieworg[i] += scr_ofsx->value * forward[i] + - scr_ofsy->value * right[i] + - scr_ofsz->value * up[i]; - } - } - - V_BoundOffsets (); - - // set up gun position - VectorCopy (viewangles, view->angles); - - CalcGunAngle (); - - VectorCopy (origin, view->origin); - view->origin[2] += cl.viewheight; - - for (i = 0; i < 3; i++) { - view->origin[i] += forward[i] * bob * 0.4; -// view->origin[i] += right[i] * bob * 0.4; -// view->origin[i] += up[i] * bob * 0.8; - } - view->origin[2] += bob; - - // fudge position around to keep amount of weapon visible - // roughly equal with different FOV - if (hud_sbar->int_val == 0 && r_data->scr_viewsize->int_val >= 100) - ; - else if (r_data->scr_viewsize->int_val == 110) - view->origin[2] += 1; - else if (r_data->scr_viewsize->int_val == 100) - view->origin[2] += 2; - else if (r_data->scr_viewsize->int_val == 90) - view->origin[2] += 1; - else if (r_data->scr_viewsize->int_val == 80) - view->origin[2] += 0.5; - - view->model = cl.model_precache[cl.stats[STAT_WEAPON]]; - view->frame = cl.stats[STAT_WEAPONFRAME]; - view->skin = 0; - - // set up the refresh position - VectorAdd (r_data->refdef->viewangles, cl.punchangle, - r_data->refdef->viewangles); - - // smooth out stair step ups - if ((cl.onground != -1) && (origin[2] - oldz > 0)) { - float steptime; - - steptime = cl.time - cl.oldtime; - if (steptime < 0) - steptime = 0; - - oldz += steptime * 80; - if (oldz > origin[2]) - oldz = origin[2]; - if (origin[2] - oldz > 12) - oldz = origin[2] - 12; - r_data->refdef->vieworg[2] += oldz - origin[2]; - view->origin[2] += oldz - origin[2]; - } else - oldz = origin[2]; - - if (cl.chase && chase_active->int_val) - Chase_Update (); - - CL_TransformEntity (view, view->angles, true); -} - -/* - V_RenderView - - The player's clipping box goes from (-16 -16 -24) to (16 16 32) from - the entity origin, so any view position inside that will be valid -*/ -void -V_RenderView (void) -{ - if (cls.state != ca_active) - return; - - if (cl.intermission) { // intermission / finale rendering - V_CalcIntermissionRefdef (); - } else { - V_CalcRefdef (); - } - - r_funcs->R_RenderView (); -} - -void -V_Init (void) -{ - Cmd_AddCommand ("bf", V_BonusFlash_f, "Background flash, used when you " - "pick up an item"); - Cmd_AddCommand ("centerview", V_StartPitchDrift, "Centers the player's " - "view ahead after +lookup or +lookdown\n" - "Will not work while mlook is active or freelook is 1."); - Cmd_AddCommand ("v_cshift", V_cshift_f, "This adjusts all of the colors " - "currently being displayed.\n" - "Used when you are underwater, hit, have the Ring of " - "Shadows, or Quad Damage. (v_cshift r g b intensity)"); -} - -void -V_Init_Cvars (void) -{ - v_centermove = Cvar_Get ("v_centermove", "0.15", CVAR_NONE, NULL, - "How far the player must move forward before the " - "view re-centers"); - v_centerspeed = Cvar_Get ("v_centerspeed", "500", CVAR_NONE, NULL, - "How quickly you return to a center view after " - "a lookup or lookdown"); - v_iyaw_cycle = Cvar_Get ("v_iyaw_cycle", "2", CVAR_NONE, NULL, - "How far you tilt right and left when " - "v_idlescale is enabled"); - v_iroll_cycle = Cvar_Get ("v_iroll_cycle", "0.5", CVAR_NONE, NULL, - "How quickly you tilt right and left when " - "v_idlescale is enabled"); - v_ipitch_cycle = Cvar_Get ("v_ipitch_cycle", "1", CVAR_NONE, NULL, - "How quickly you lean forwards and backwards " - "when v_idlescale is enabled"); - v_iyaw_level = Cvar_Get ("v_iyaw_level", "0.3", CVAR_NONE, NULL, - "How far you tilt right and left when " - "v_idlescale is enabled"); - v_iroll_level = Cvar_Get ("v_iroll_level", "0.1", CVAR_NONE, NULL, - "How far you tilt right and left when " - "v_idlescale is enabled"); - v_ipitch_level = Cvar_Get ("v_ipitch_level", "0.3", CVAR_NONE, NULL, - "How far you lean forwards and backwards when " - "v_idlescale is enabled"); - v_idlescale = Cvar_Get ("v_idlescale", "0", CVAR_NONE, NULL, - "Toggles whether the view remains idle"); - - scr_ofsx = Cvar_Get ("scr_ofsx", "0", CVAR_NONE, NULL, "None"); - scr_ofsy = Cvar_Get ("scr_ofsy", "0", CVAR_NONE, NULL, "None"); - scr_ofsz = Cvar_Get ("scr_ofsz", "0", CVAR_NONE, NULL, "None"); - cl_rollspeed = Cvar_Get ("cl_rollspeed", "200", CVAR_NONE, NULL, - "How quickly you straighten out after strafing"); - cl_rollangle = Cvar_Get ("cl_rollangle", "2.0", CVAR_NONE, NULL, - "How much your screen tilts when strafing"); - cl_bob = Cvar_Get ("cl_bob", "0.02", CVAR_NONE, NULL, - "How much your weapon moves up and down when walking"); - cl_bobcycle = Cvar_Get ("cl_bobcycle", "0.6", CVAR_NONE, NULL, - "How quickly your weapon moves up and down when " - "walking"); - cl_bobup = Cvar_Get ("cl_bobup", "0.5", CVAR_NONE, NULL, - "How long your weapon stays up before cycling when " - "walking"); - v_kicktime = Cvar_Get ("v_kicktime", "0.5", CVAR_NONE, NULL, - "How long the kick from an attack lasts"); - v_kickroll = Cvar_Get ("v_kickroll", "0.6", CVAR_NONE, NULL, - "How much you lean when hit"); - v_kickpitch = Cvar_Get ("v_kickpitch", "0.6", CVAR_NONE, NULL, - "How much you look up when hit"); -} diff --git a/nq/source/game.c b/nq/source/game.c index f67104472..e39e9ada4 100644 --- a/nq/source/game.c +++ b/nq/source/game.c @@ -35,13 +35,29 @@ #include "QF/quakefs.h" #include "QF/sys.h" -#include "game.h" -#include "server.h" +#include "nq/include/game.h" +#include "nq/include/server.h" -qboolean standard_quake = false; +bool standard_quake = false; -cvar_t *registered; -cvar_t *cmdline; +float registered; +static cvar_t registered_cvar = { + .name = "registered", + .description = + "Is the game the registered version. 1 yes 0 no", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = ®istered }, +}; +char *cmdline; +static cvar_t cmdline_cvar = { + .name = "cmdline", + .description = + "None", + .default_value = "0", + .flags = CVAR_SERVERINFO, + .value = { .type = 0, .value = &cmdline }, +}; int static_registered = 1; /* @@ -66,18 +82,20 @@ Game_CheckRegistered (void) } if (static_registered) { - Cvar_Set (registered, "1"); + Cvar_Set ("registered", "1"); Sys_Printf ("Playing registered version.\n"); } } void -Game_Init (void) +Game_Init (memhunk_t *hunk) { int i; const char *game = "nq"; // FIXME: make this dependant on QF metadata in the mission packs + // better yet, make its actions part of the metadata and remove + // entirely standard_quake = true; if ((i = COM_CheckParm ("-hipnotic"))) { @@ -89,11 +107,10 @@ Game_Init (void) } else if ((i = COM_CheckParm ("-abyss"))) { game = "abyss"; } - QFS_Init (game); + QFS_Init (hunk, game); - registered = Cvar_Get ("registered", "0", CVAR_NONE, NULL, - "Is the game the registered version. 1 yes 0 no"); - cmdline = Cvar_Get ("cmdline", "0", CVAR_SERVERINFO, Cvar_Info, "None"); + Cvar_Register (®istered_cvar, 0, 0); + Cvar_Register (&cmdline_cvar, Cvar_Info, &cmdline); Game_CheckRegistered (); } diff --git a/nq/source/host.c b/nq/source/host.c index f6b59cf8b..f155d2699 100644 --- a/nq/source/host.c +++ b/nq/source/host.c @@ -29,38 +29,25 @@ #endif #ifdef HAVE_UNISTD_H -# include "unistd.h" +# include #endif #include "QF/cbuf.h" -#include "QF/cdaudio.h" -#include "QF/draw.h" -#include "QF/idparse.h" +#include "QF/dstring.h" #include "QF/cmd.h" -#include "QF/console.h" #include "QF/cvar.h" -#include "QF/image.h" -#include "QF/input.h" -#include "QF/keys.h" -#include "QF/msg.h" -#include "QF/png.h" -#include "QF/progs.h" -#include "QF/qargs.h" -#include "QF/screen.h" -#include "QF/sys.h" -#include "QF/va.h" -#include "QF/vid.h" +#include "QF/dstring.h" #include "QF/gib.h" +#include "QF/idparse.h" +#include "QF/qargs.h" #include "QF/plugin/console.h" -#include "QF/plugin/vid_render.h" #include "buildnum.h" -#include "chase.h" -#include "compat.h" -#include "host.h" -#include "server.h" -#include "sv_progs.h" + +#include "nq/include/client.h" +#include "nq/include/host.h" +#include "nq/include/server.h" /* @@ -77,7 +64,7 @@ static plugin_list_t server_plugin_list[] = { SERVER_PLUGIN_LIST }; -qboolean host_initialized; // true if into command execution +bool host_initialized; // true if into command execution quakeparms_t host_parms; @@ -93,48 +80,186 @@ double con_realtime; double oldcon_realtime; int host_framecount; -int host_hunklevel; +size_t host_hunklevel; int host_in_game; -int minimum_memory; +size_t minimum_memory; client_t *host_client; // current client jmp_buf host_abortserver; -cvar_t *host_mem_size; +float host_mem_size; +static cvar_t host_mem_size_cvar = { + .name = "host_mem_size", + .description = + "Amount of memory (in MB) to allocate for the " + PACKAGE_NAME + " heap", + .default_value = "40", + .flags = CVAR_ROM, + .value = { .type = &cexpr_float, .value = &host_mem_size }, +}; -cvar_t *host_framerate; -cvar_t *host_speeds; -cvar_t *max_edicts; +float host_framerate; +static cvar_t host_framerate_cvar = { + .name = "host_framerate", + .description = + "set for slow motion", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &host_framerate }, +}; +int host_speeds; +static cvar_t host_speeds_cvar = { + .name = "host_speeds", + .description = + "set for running times", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &host_speeds }, +}; +int max_edicts; +static cvar_t max_edicts_cvar = { + .name = "max_edicts", + .description = + "maximum server edicts", + .default_value = "1024", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &max_edicts }, +}; -cvar_t *sys_ticrate; -cvar_t *serverprofile; +float sys_ticrate; +static cvar_t sys_ticrate_cvar = { + .name = "sys_ticrate", + .description = + "None", + .default_value = "0.05", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sys_ticrate }, +}; +int serverprofile; +static cvar_t serverprofile_cvar = { + .name = "serverprofile", + .description = + "None", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &serverprofile }, +}; -cvar_t *cl_quakerc; +int cl_quakerc; +static cvar_t cl_quakerc_cvar = { + .name = "cl_quakerc", + .description = + "exec quake.rc on startup", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_quakerc }, +}; -cvar_t *fraglimit; -cvar_t *timelimit; -cvar_t *teamplay; -cvar_t *noexit; -cvar_t *samelevel; +float fraglimit; +static cvar_t fraglimit_cvar = { + .name = "fraglimit", + .description = + "None", + .default_value = "0", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_float, .value = &fraglimit }, +}; +int timelimit; +static cvar_t timelimit_cvar = { + .name = "timelimit", + .description = + "None", + .default_value = "0", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_int, .value = &timelimit }, +}; +int teamplay; +static cvar_t teamplay_cvar = { + .name = "teamplay", + .description = + "None", + .default_value = "0", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_int, .value = &teamplay }, +}; +float noexit; +static cvar_t noexit_cvar = { + .name = "noexit", + .description = + "None", + .default_value = "0", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_float, .value = &noexit }, +}; +float samelevel; +static cvar_t samelevel_cvar = { + .name = "samelevel", + .description = + "None", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &samelevel }, +}; -cvar_t *skill; -cvar_t *coop; -cvar_t *deathmatch; +int skill; +static cvar_t skill_cvar = { + .name = "skill", + .description = + "0 - 3", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &skill }, +}; +int coop; +static cvar_t coop_cvar = { + .name = "coop", + .description = + "0 or 1", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &coop }, +}; +int deathmatch; +static cvar_t deathmatch_cvar = { + .name = "deathmatch", + .description = + "0, 1, or 2", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &deathmatch }, +}; -cvar_t *pausable; - -cvar_t *temp1; -cvar_t *cl_usleep; - -static int cl_usleep_cache; - -static void -cl_usleep_f (cvar_t *var) -{ - cl_usleep_cache = var->int_val; -} +int pausable; +static cvar_t pausable_cvar = { + .name = "pausable", + .description = + "None", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &pausable }, +}; +float temp1; +static cvar_t temp1_cvar = { + .name = "temp1", + .description = + "None", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &temp1 }, +}; +int cl_usleep; +static cvar_t cl_usleep_cvar = { + .name = "cl_usleep", + .description = + "Turn this on to save cpu when fps limited. May affect frame rate " + "adversely depending on local machine/os conditions", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &cl_usleep }, +}; void Host_EndGame (const char *message, ...) @@ -148,12 +273,12 @@ Host_EndGame (const char *message, ...) va_start (argptr, message); dvsprintf (str, message, argptr); va_end (argptr); - Sys_MaskPrintf (SYS_DEV, "Host_EndGame: %s\n", str->str); + Sys_MaskPrintf (SYS_dev, "Host_EndGame: %s\n", str->str); if (sv.active) Host_ShutdownServer (false); - if (cls.state == ca_dedicated) + if (net_is_dedicated) Sys_Error ("Host_EndGame: %s", str->str); // dedicated servers exit if (cls.demonum != -1) @@ -173,7 +298,7 @@ void Host_Error (const char *error, ...) { static dstring_t *str; - static qboolean inerror = false; + static bool inerror = false; va_list argptr; if (inerror) @@ -184,7 +309,7 @@ Host_Error (const char *error, ...) inerror = true; - cl.loading = false; + cl.viewstate.loading = false; va_start (argptr, error); dvsprintf (str, error, argptr); @@ -193,7 +318,7 @@ Host_Error (const char *error, ...) if (sv.active) Host_ShutdownServer (false); - if (cls.state == ca_dedicated) + if (net_is_dedicated) Sys_Error ("Host_Error: %s", str->str); // dedicated servers exit Sys_Printf ("Host_Error: %s\n", str->str); @@ -215,17 +340,17 @@ Host_FindMaxClients (void) i = COM_CheckParm ("-dedicated"); if (i) { - cls.state = ca_dedicated; if (i != (com_argc - 1)) { svs.maxclients = atoi (com_argv[i + 1]); } else svs.maxclients = 8; - } else - cls.state = ca_disconnected; + } + cls.state = ca_disconnected; + net_is_dedicated = i; i = COM_CheckParm ("-listen"); if (i) { - if (cls.state == ca_dedicated) + if (net_is_dedicated) Sys_Error ("Only one of -dedicated or -listen can be specified"); if (i != (com_argc - 1)) svs.maxclients = atoi (com_argv[i + 1]); @@ -241,12 +366,12 @@ Host_FindMaxClients (void) if (svs.maxclientslimit < 4) svs.maxclientslimit = 4; svs.clients = - Hunk_AllocName (svs.maxclientslimit * sizeof (client_t), "clients"); + Hunk_AllocName (0, svs.maxclientslimit * sizeof (client_t), "clients"); if (svs.maxclients > 1) - Cvar_SetValue (deathmatch, 1.0); + deathmatch = 1.0; else - Cvar_SetValue (deathmatch, 0.0); + deathmatch = 0.0; } static void @@ -254,37 +379,26 @@ Host_InitLocal (void) { Host_InitCommands (); - host_framerate = - Cvar_Get ("host_framerate", "0", CVAR_NONE, NULL, - "set for slow motion"); - host_speeds = - Cvar_Get ("host_speeds", "0", CVAR_NONE, NULL, - "set for running times"); - max_edicts = Cvar_Get ("max_edicts", "1024", CVAR_NONE, NULL, - "maximum server edicts"); + Cvar_Register (&host_framerate_cvar, 0, 0); + Cvar_Register (&host_speeds_cvar, 0, 0); + Cvar_Register (&max_edicts_cvar, 0, 0); - sys_ticrate = Cvar_Get ("sys_ticrate", "0.05", CVAR_NONE, NULL, "None"); - serverprofile = Cvar_Get ("serverprofile", "0", CVAR_NONE, NULL, "None"); + Cvar_Register (&sys_ticrate_cvar, 0, 0); + Cvar_Register (&serverprofile_cvar, 0, 0); - cl_quakerc = Cvar_Get ("cl_quakerc", "1", CVAR_NONE, NULL, - "exec quake.rc on startup"); + Cvar_Register (&cl_quakerc_cvar, 0, 0); - fraglimit = Cvar_Get ("fraglimit", "0", CVAR_SERVERINFO, Cvar_Info, - "None"); - timelimit = Cvar_Get ("timelimit", "0", CVAR_SERVERINFO, Cvar_Info, - "None"); - teamplay = Cvar_Get ("teamplay", "0", CVAR_SERVERINFO, Cvar_Info, "None"); - samelevel = Cvar_Get ("samelevel", "0", CVAR_NONE, NULL, "None"); - noexit = Cvar_Get ("noexit", "0", CVAR_SERVERINFO, Cvar_Info, "None"); - skill = Cvar_Get ("skill", "1", CVAR_NONE, NULL, "0 - 3"); - deathmatch = Cvar_Get ("deathmatch", "0", CVAR_NONE, NULL, "0, 1, or 2"); - coop = Cvar_Get ("coop", "0", CVAR_NONE, NULL, "0 or 1"); - pausable = Cvar_Get ("pausable", "1", CVAR_NONE, NULL, "None"); - temp1 = Cvar_Get ("temp1", "0", CVAR_NONE, NULL, "None"); - cl_usleep = Cvar_Get ("cl_usleep", "1", CVAR_ARCHIVE, cl_usleep_f, - "Turn this on to save cpu when fps limited. " - "May affect frame rate adversely depending on " - "local machine/os conditions"); + Cvar_Register (&fraglimit_cvar, Cvar_Info, &fraglimit); + Cvar_Register (&timelimit_cvar, Cvar_Info, &timelimit); + Cvar_Register (&teamplay_cvar, Cvar_Info, &teamplay); + Cvar_Register (&samelevel_cvar, 0, 0); + Cvar_Register (&noexit_cvar, Cvar_Info, &noexit); + Cvar_Register (&skill_cvar, 0, 0); + Cvar_Register (&deathmatch_cvar, 0, 0); + Cvar_Register (&coop_cvar, 0, 0); + Cvar_Register (&pausable_cvar, 0, 0); + Cvar_Register (&temp1_cvar, 0, 0); + Cvar_Register (&cl_usleep_cvar, 0, 0); Host_FindMaxClients (); @@ -322,7 +436,6 @@ void SV_BroadcastPrintf (const char *fmt, ...) { static dstring_t *str; - int i; va_list argptr; if (!str) @@ -332,7 +445,7 @@ SV_BroadcastPrintf (const char *fmt, ...) dvsprintf (str, fmt, argptr); va_end (argptr); - for (i = 0; i < svs.maxclients; i++) + for (unsigned i = 0; i < svs.maxclients; i++) if (svs.clients[i].active && svs.clients[i].spawned) { MSG_WriteByte (&svs.clients[i].message, svc_print); MSG_WriteString (&svs.clients[i].message, str->str); @@ -368,10 +481,11 @@ Host_ClientCommands (const char *fmt, ...) if (crash = true), don't bother sending signofs */ void -SV_DropClient (qboolean crash) +SV_DropClient (bool crash) { client_t *client; - int saveSelf, i; + unsigned i; + pr_uint_t saveSelf; if (!crash) { // send any final messages (don't check for errors) @@ -385,8 +499,7 @@ SV_DropClient (qboolean crash) // call the prog function for removing a client // this will set the body to a dead frame, among other things saveSelf = *sv_globals.self; - *sv_globals.self = - EDICT_TO_PROG (&sv_pr_state, host_client->edict); + *sv_globals.self = EDICT_TO_PROG (&sv_pr_state, host_client->edict); PR_ExecuteProgram (&sv_pr_state, sv_funcs.ClientDisconnect); *sv_globals.self = saveSelf; } @@ -394,7 +507,7 @@ SV_DropClient (qboolean crash) Sys_Printf ("Client %s removed\n", host_client->name); } // break the net connection - Sys_MaskPrintf (SYS_NET, "dropping client\n"); + Sys_MaskPrintf (SYS_net, "dropping client\n"); NET_Close (host_client->netconnection); host_client->netconnection = NULL; @@ -405,7 +518,8 @@ SV_DropClient (qboolean crash) net_activeconnections--; // send notification to all clients - for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++) { + for (i = 0, client = svs.clients; i < svs.maxclients; + i++, client++) { if (!client->active) continue; MSG_WriteByte (&client->message, svc_updatename); @@ -426,11 +540,11 @@ SV_DropClient (qboolean crash) This happens only at the end of a game, not between levels */ void -Host_ShutdownServer (qboolean crash) +Host_ShutdownServer (bool crash) { byte message[4]; double start; - int count, i; + unsigned count, i; sizebuf_t buf; if (!sv.active) @@ -494,18 +608,26 @@ Host_ShutdownServer (qboolean crash) void Host_ClearMemory (void) { - Sys_MaskPrintf (SYS_DEV, "Clearing memory\n"); - if (viddef.flush_caches) - viddef.flush_caches (); + Sys_MaskPrintf (SYS_dev, "Clearing memory\n"); Mod_ClearAll (); if (host_hunklevel) - Hunk_FreeToLowMark (host_hunklevel); + Hunk_FreeToLowMark (0, host_hunklevel); +} - cls.signon = 0; - memset (&sv, 0, sizeof (sv)); - memset (&cl, 0, sizeof (cl)); - if (r_data) - r_data->force_fullscreen = 0; +static struct LISTENER_SET_TYPE(void) host_server_spawn = LISTENER_SET_STATIC_INIT(4); + +void +Host_SpawnServer (void) +{ + LISTENER_INVOKE (&host_server_spawn, NULL); + Host_ClearMemory (); +} + +void +Host_OnServerSpawn (void (*onSpawn) (void)) +{ + LISTENER_ADD (&host_server_spawn, + (void(*)(void *, const void*)) onSpawn, 0); } /* @@ -522,7 +644,7 @@ Host_FilterTime (float time) con_realtime += time; if (cls.demoplayback) { - timescale = max (0, demo_speed->value); + timescale = max (0, demo_speed); time *= timescale; } @@ -531,7 +653,7 @@ Host_FilterTime (float time) //FIXME not having the framerate cap is nice, but it breaks net play timedifference = (timescale / 72.0) - (realtime - oldrealtime); - if (!cls.timedemo && (timedifference > 0)) + if (!cls.demoplayback && (timedifference > 0)) return timedifference; // framerate is too high host_frametime = realtime - oldrealtime; @@ -540,95 +662,14 @@ Host_FilterTime (float time) con_frametime = con_realtime - oldcon_realtime; oldcon_realtime = con_realtime; - if (host_framerate->value > 0) - host_frametime = host_framerate->value; + if (host_framerate > 0) + host_frametime = host_framerate; else // don't allow really long or short frames - host_frametime = bound (0.001, host_frametime, 0.1); + host_frametime = bound (0.000, host_frametime, 0.1); return 0; } -void -Host_ServerFrame (void) -{ - *sv_globals.frametime = sv_frametime = host_frametime; - - // set the time and clear the general datagram - SV_ClearDatagram (); - - SV_CheckForNewClients (); - - // read client messages - SV_RunClients (); - - // move things around and think - // always pause in single player if in console or menus - if (!sv.paused && (svs.maxclients > 1 || host_in_game)) { - SV_Physics (); - sv.time += host_frametime; - } - - // send all messages to the clients - SV_SendClientMessages (); -} - -static void -Host_ClientFrame (void) -{ - static double time1 = 0, time2 = 0, time3 = 0; - int pass1, pass2, pass3; - - // if running the server remotely, send intentions now after - // the incoming messages have been read - if (!sv.active) - CL_SendCmd (); - - host_time += host_frametime; - - // fetch results from server - if (cls.state >= ca_connected) - CL_ReadFromServer (); - - // update video - if (host_speeds->int_val) - time1 = Sys_DoubleTime (); - - r_data->inhibit_viewmodel = (chase_active->int_val - || (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) - || cl.stats[STAT_HEALTH] <= 0); - r_data->frametime = host_frametime; - - CL_UpdateScreen (cl.time); - - if (host_speeds->int_val) - time2 = Sys_DoubleTime (); - - // update audio - if (cls.state == ca_active) { - mleaf_t *l; - byte *asl = 0; - - l = Mod_PointInLeaf (r_data->origin, cl.worldmodel); - if (l) - asl = l->ambient_sound_level; - S_Update (r_data->origin, r_data->vpn, r_data->vright, r_data->vup, - asl); - r_funcs->R_DecayLights (host_frametime); - } else - S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin, 0); - - CDAudio_Update (); - - if (host_speeds->int_val) { - pass1 = (time1 - time3) * 1000; - time3 = Sys_DoubleTime (); - pass2 = (time2 - time1) * 1000; - pass3 = (time3 - time2) * 1000; - Sys_Printf ("%3i tot %3i server %3i gfx %3i snd\n", - pass1 + pass2 + pass3, pass1, pass2, pass3); - } -} - /* Host_Frame @@ -653,21 +694,12 @@ _Host_Frame (float time) if ((sleeptime = Host_FilterTime (time)) != 0) { // don't run too fast, or packet will flood outs #ifdef HAVE_USLEEP - if (cl_usleep_cache && sleeptime > 0.002) // minimum sleep time + if (cl_usleep && sleeptime > 0.002) // minimum sleep time usleep ((unsigned long) (sleeptime * 1000000 / 2)); #endif return; } - - if (cls.state != ca_dedicated) - IN_ProcessEvents (); - - if (cls.state == ca_dedicated) - Con_ProcessInput (); - - GIB_Thread_Execute (); - cmd_source = src_command; - Cbuf_Execute_Stack (host_cbuf); + host_time += host_frametime; //FIXME is this needed? vcr stuff if (first) { first = 0; @@ -680,32 +712,26 @@ _Host_Frame (float time) } else { Con_NewMap (); } - - CL_UpdateScreen (cl.time); } NET_Poll (); - if (sv.active) { - CL_SendCmd (); - Host_ServerFrame (); + if (!net_is_dedicated) { + // Whether or not the server is active, if this is not a dedicated + // server, then the client always needs to be able to process input + // and send commands to the server before the server runs a frame. + CL_PreFrame (); } - if (cls.state != ca_dedicated) - Host_ClientFrame (); - else - host_time += host_frametime; //FIXME is this needed? vcr stuff + if (sv.active) { + SV_Frame (); + } - if (cls.demo_capture) { - tex_t *tex = r_funcs->SCR_CaptureBGR (); - WritePNGqfs (va ("%s/qfmv%06d.png", qfs_gamedir->dir.shots, - cls.demo_capture++), - tex->data, tex->width, tex->height); - free (tex); + if (!net_is_dedicated) { + CL_Frame (); } host_framecount++; - fps_count++; } void @@ -713,10 +739,11 @@ Host_Frame (float time) { double time1, time2; static double timetotal; - int i, c, m; + int c; + double m; static int timecount; - if (!serverprofile->int_val) { + if (!serverprofile) { _Host_Frame (time); return; } @@ -735,12 +762,12 @@ Host_Frame (float time) timecount = 0; timetotal = 0; c = 0; - for (i = 0; i < svs.maxclients; i++) { + for (unsigned i = 0; i < svs.maxclients; i++) { if (svs.clients[i].active) c++; } - Sys_Printf ("serverprofile: %2i clients %2i msec\n", c, m); + Sys_Printf ("serverprofile: %2i clients %5.3g msec\n", c, m); } @@ -810,33 +837,11 @@ Host_InitVCR (quakeparms_t *parms) } -static int -check_quakerc (void) -{ - const char *l, *p; - int ret = 1; - QFile *f; - - f = QFS_FOpenFile ("quake.rc"); - if (!f) - return 1; - while ((l = Qgetline (f))) { - if ((p = strstr (l, "stuffcmds"))) { - if (p == l) { // only known case so far - ret = 0; - break; - } - } - } - Qclose (f); - return ret; -} - -static void +static memhunk_t * Host_Init_Memory (void) { int mem_parm = COM_CheckParm ("-mem"); - int mem_size; + size_t mem_size; void *mem_base; if (standard_quake) @@ -844,43 +849,62 @@ Host_Init_Memory (void) else minimum_memory = MINIMUM_MEMORY_LEVELPAK; - host_mem_size = Cvar_Get ("host_mem_size", "40", CVAR_NONE, NULL, - "Amount of memory (in MB) to allocate for the " - PACKAGE_NAME " heap"); + Cvar_Register (&host_mem_size_cvar, 0, 0); if (mem_parm) - Cvar_Set (host_mem_size, com_argv[mem_parm + 1]); + Cvar_Set ("host_mem_size", com_argv[mem_parm + 1]); if (COM_CheckParm ("-minmemory")) - Cvar_SetValue (host_mem_size, minimum_memory / (1024 * 1024.0)); + host_mem_size = minimum_memory / (1024 * 1024.0); - Cvar_SetFlags (host_mem_size, host_mem_size->flags | CVAR_ROM); - mem_size = (int) (host_mem_size->value * 1024 * 1024); + mem_size = ((size_t) host_mem_size * 1024 * 1024); if (mem_size < minimum_memory) Sys_Error ("Only %4.1f megs of memory reported, can't execute game", mem_size / (float) 0x100000); - mem_base = malloc (mem_size); + mem_base = Sys_Alloc (mem_size); if (!mem_base) - Sys_Error ("Can't allocate %d", mem_size); + Sys_Error ("Can't allocate %zd", mem_size); Sys_PageIn (mem_base, mem_size); - Memory_Init (mem_base, mem_size); + memhunk_t *hunk = Memory_Init (mem_base, mem_size); - Sys_Printf ("%4.1f megabyte heap\n", host_mem_size->value); + Sys_Printf ("%4.1f megabyte heap\n", host_mem_size); + return hunk; } static void -host_keydest_callback (keydest_t kd) +Host_ExecConfig (cbuf_t *cbuf, int skip_quakerc) { - host_in_game = kd == key_game; + // quakeforge.cfg overrides quake.rc as it contains quakeforge-specific + // commands. If it doesn't exist, then this is the first time quakeforge + // has been used in this installation, thus any existing legacy config + // should be used to set up defaults on the assumption that the user has + // things set up to work with another (hopefully compatible) client + if (CL_ReadConfiguration ("quakeforge.cfg")) { + Cmd_Exec_File (cbuf, fs_usercfg, 0); + Cmd_StuffCmds (cbuf); + COM_Check_quakerc ("startdemos", cbuf); + } else { + if (!skip_quakerc) { + Cbuf_InsertText (cbuf, "exec quake.rc\n"); + } + Cmd_Exec_File (cbuf, fs_usercfg, 0); + // Reparse the command line for + commands. + // (sets still done, but it doesn't matter) + // (Note, no non-base commands exist yet) + if (skip_quakerc || !COM_Check_quakerc ("stuffcmds", 0)) { + Cmd_StuffCmds (cbuf); + } + } } void Host_Init (void) { + Sys_RegisterShutdown (Host_Shutdown, 0); Sys_Printf ("Host_Init\n"); host_cbuf = Cbuf_New (&id_interp); @@ -888,61 +912,49 @@ Host_Init (void) Sys_Init (); GIB_Init (true); - COM_ParseConfig (); - Host_Init_Memory (); + COM_ParseConfig (host_cbuf); + + memhunk_t *hunk = Host_Init_Memory (); PI_Init (); - Game_Init (); - - PR_Init_Cvars (); - SV_Progs_Init_Cvars (); + Game_Init (hunk); if (!isDedicated) CL_InitCvars (); - PR_Init (); - if (isDedicated) { PI_RegisterPlugins (server_plugin_list); - Con_Init ("server"); + Con_Load ("server"); + Con_Init (); } Host_InitVCR (&host_parms); Host_InitLocal (); - NET_Init (); + NET_Init (host_cbuf); Mod_Init (); - Key_KeydestCallback (host_keydest_callback); - SV_Init (); - if (cls.state != ca_dedicated) + if (!net_is_dedicated) CL_Init (host_cbuf); if (con_module) { con_module->data->console->realtime = &con_realtime; con_module->data->console->frametime = &con_frametime; con_module->data->console->quit = Host_Quit_f; + //FIXME need to rethink cbuf connections (they can form a stack) + Cbuf_DeleteStack (con_module->data->console->cbuf); con_module->data->console->cbuf = host_cbuf; } - CL_UpdateScreen (cl.time); - CL_UpdateScreen (cl.time); + Host_ExecConfig (host_cbuf, isDedicated || !cl_quakerc); - if (!isDedicated && cl_quakerc->int_val) - Cbuf_InsertText (host_cbuf, "exec quake.rc\n"); - Cmd_Exec_File (host_cbuf, fs_usercfg->string, 0); - // reparse the command line for + commands other than set - // (sets still done, but it doesn't matter) - if (isDedicated || (cl_quakerc->int_val && check_quakerc ())) - Cmd_StuffCmds (host_cbuf); - - Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); - host_hunklevel = Hunk_LowMark (); + Hunk_AllocName (0, 0, "-HOST_HUNKLEVEL-"); + host_hunklevel = Hunk_LowMark (0); Sys_Printf ("\nVersion %s (build %04d)\n\n", PACKAGE_VERSION, build_number ()); @@ -951,8 +963,6 @@ Host_Init (void) PACKAGE_NAME); host_initialized = true; - - CL_UpdateScreen (cl.time); } /* @@ -962,20 +972,13 @@ Host_Init (void) better to run quit through here before final handoff to the sys code. */ void -Host_Shutdown (void) +Host_Shutdown (void *data) { - static qboolean isdown = false; + static bool isdown = false; if (isdown) { printf ("recursive shutdown\n"); return; } isdown = true; - - - NET_Shutdown (); - if (cls.state != ca_dedicated) { - CL_Shutdown (); - } - Con_Shutdown (); } diff --git a/nq/source/host_cmd.c b/nq/source/host_cmd.c index 1a827f15b..07d56104e 100644 --- a/nq/source/host_cmd.c +++ b/nq/source/host_cmd.c @@ -44,19 +44,23 @@ #include "QF/keys.h" #include "QF/model.h" #include "QF/msg.h" -#include "QF/qfplist.h" +#include "QF/plist.h" #include "QF/screen.h" #include "QF/script.h" #include "QF/sys.h" #include "QF/va.h" -#include "client.h" +#include "client/screen.h" +#include "client/world.h" + #include "compat.h" -#include "host.h" -#include "server.h" -#include "sv_progs.h" #include "world.h" +#include "nq/include/client.h" +#include "nq/include/host.h" +#include "nq/include/server.h" +#include "nq/include/sv_progs.h" + int current_skill; void @@ -77,8 +81,8 @@ Host_Status_f (void) int seconds; int minutes; int hours = 0; - int j; - void (*print) (const char *fmt, ...); + unsigned j; + __attribute__((format(PRINTF, 1, 2))) void (*print) (const char *fmt, ...); if (cmd_source == src_command) { if (!sv.active) { @@ -89,8 +93,8 @@ Host_Status_f (void) } else print = SV_ClientPrintf; - print ("host: %s\n", Cvar_VariableString ("hostname")); - print ("version: %4.2f\n", PACKAGE_VERSION); + print ("host: %s\n", hostname); + print ("version: %4.2s\n", PACKAGE_VERSION); if (tcpipAvailable) print ("tcp/ip: %s\n", my_tcpip_address); print ("map: %s\n", sv.name); @@ -157,7 +161,7 @@ Host_Notarget_f (void) SV_ClientPrintf ("notarget ON\n"); } -qboolean noclip_anglehack; +bool noclip_anglehack; static void Host_Noclip_f (void) @@ -209,7 +213,7 @@ Host_Fly_f (void) static void Host_Ping_f (void) { - int i, j; + unsigned i, j; float total; client_t *client; @@ -238,13 +242,13 @@ nice_time (float time) int t = time + 0.5; if (t < 60) { - return va ("%ds", t); + return va (0, "%ds", t); } else if (t < 3600) { - return va ("%dm%02ds", t / 60, t % 60); + return va (0, "%dm%02ds", t / 60, t % 60); } else if (t < 86400) { - return va ("%dh%02dm%02ds", t / 3600, (t / 60) % 60, t % 60); + return va (0, "%dh%02dm%02ds", t / 3600, (t / 60) % 60, t % 60); } else { - return va ("%dd%02dh%02dm%02ds", + return va (0, "%dd%02dh%02dm%02ds", t / 86400, (t / 3600) % 24, (t / 60) % 60, t % 60); } } @@ -278,7 +282,7 @@ Host_Map_f (void) } // check to make sure the level exists - expanded = va ("maps/%s.bsp", Cmd_Argv (1)); + expanded = va (0, "maps/%s.bsp", Cmd_Argv (1)); f = QFS_FOpenFile (expanded); if (!f) { Sys_Printf ("Can't find %s\n", expanded); @@ -291,8 +295,10 @@ Host_Map_f (void) CL_Disconnect (); Host_ShutdownServer (false); - cl.loading = true; - CL_UpdateScreen (cl.time); + cl.viewstate.loading = true; + cl.viewstate.time = cl.time; + cl.viewstate.realtime = realtime; + CL_UpdateScreen (&cl.viewstate); svs.serverflags = 0; // haven't completed an episode yet strcpy (name, Cmd_Argv (1)); @@ -300,7 +306,7 @@ Host_Map_f (void) if (!sv.active) return; - if (cls.state != ca_dedicated) { + if (!net_is_dedicated) { Cmd_ExecuteString ("connect local", src_command); } } @@ -394,7 +400,7 @@ spawn_parms_array (void) const char *parm; for (i = 0; i < NUM_SPAWN_PARMS; i++) { - parm = va ("%.9g", svs.clients->spawn_parms[i]); + parm = va (0, "%.9g", svs.clients->spawn_parms[i]); PL_A_AddObject (parms, PL_NewString (parm)); } return parms; @@ -420,7 +426,7 @@ static plitem_t * entities_array (void) { plitem_t *entities = PL_NewArray (); - int i; + pr_uint_t i; for (i = 0; i < sv.num_edicts; i++) { PL_A_AddObject (entities, @@ -433,18 +439,18 @@ entities_array (void) static plitem_t * game_dict (void) { - plitem_t *game = PL_NewDictionary (); + plitem_t *game = PL_NewDictionary (0); PL_D_AddObject (game, "comment", - PL_NewString (va ("%-21s kills:%3i/%3i", cl.levelname, + PL_NewString (va (0, "%-21s kills:%3i/%3i", cl.levelname, cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]))); PL_D_AddObject (game, "spawn_parms", spawn_parms_array ()); PL_D_AddObject (game, "current_skill", - PL_NewString (va ("%d", current_skill))); + PL_NewString (va (0, "%d", current_skill))); PL_D_AddObject (game, "name", PL_NewString (sv.name)); // sv.time is a double, so it gets 17 digits - PL_D_AddObject (game, "time", PL_NewString (va ("%.17g", sv.time))); + PL_D_AddObject (game, "time", PL_NewString (va (0, "%.17g", sv.time))); PL_D_AddObject (game, "lightstyles", lightstyles_array ()); PL_D_AddObject (game, "globals", ED_GlobalsDict (&sv_pr_state)); PL_D_AddObject (game, "entities", entities_array ()); @@ -454,7 +460,7 @@ game_dict (void) static plitem_t * convert_to_game_dict (script_t *script) { - plitem_t *game = PL_NewDictionary (); + plitem_t *game = PL_NewDictionary (0); plitem_t *item; plitem_t *list; int skill; @@ -477,7 +483,7 @@ convert_to_game_dict (script_t *script) // values Script_GetToken (script, 1); skill = (int) (atof (script->token->str) + 0.1); - PL_D_AddObject (game, "current_skill", PL_NewString (va ("%d", skill))); + PL_D_AddObject (game, "current_skill", PL_NewString (va (0, "%d", skill))); Script_GetToken (script, 1); PL_D_AddObject (game, "name", PL_NewString (script->token->str)); @@ -492,17 +498,18 @@ convert_to_game_dict (script_t *script) PL_A_AddObject (item, PL_NewString (script->token->str)); //char *s; - //s = Hunk_Alloc (strlen (script->token->str) + 1); + //s = Hunk_Alloc (0, strlen (script->token->str) + 1); //strcpy (s, script->token->str); //sv.lightstyles[i] = s; } PL_D_AddObject (game, "lightstyles", item); // load the edicts out of the savegame file - list = ED_ConvertToPlist (script, 0); - item = PL_RemoveObjectAtIndex (list, 0); + list = ED_ConvertToPlist (script, 0, 0); + item = PL_ObjectAtIndex (list, 0); PL_D_AddObject (game, "globals", item); PL_D_AddObject (game, "entities", list); + PL_RemoveObjectAtIndex (list, 0); return game; } @@ -516,7 +523,6 @@ Host_Savegame_f (void) const char *save_name; char *save_text; QFile *f; - int i; char *bup1, *bup2 = 0; @@ -548,7 +554,7 @@ Host_Savegame_f (void) return; } - for (i = 0; i < svs.maxclients; i++) { + for (unsigned i = 0; i < svs.maxclients; i++) { if (svs.clients[i].active && (SVfloat (svs.clients[i].edict, health) <= 0)) { Sys_Printf ("Can't savegame with a dead player\n"); @@ -561,7 +567,7 @@ Host_Savegame_f (void) if (strcmp (save_name, "quick") == 0) { bup2 = nva ("%s/%s%d.sav", qfs_gamedir->dir.def, save_name, MAX_QUICK); QFS_Remove (bup2); - for (i = MAX_QUICK - 1; i > 0; i--) { + for (int i = MAX_QUICK - 1; i > 0; i--) { bup1 = nva ("%s/%s%d.sav", qfs_gamedir->dir.def, save_name, i); QFS_Rename (bup1, bup2); free (bup2); @@ -622,8 +628,10 @@ Host_Loadgame_f (void) dsprintf (name, "%s/%s", qfs_gamedir->dir.def, Cmd_Argv (1)); QFS_DefaultExtension (name, ".sav"); - cl.loading = true; - CL_UpdateScreen (cl.time); + cl.viewstate.loading = true; + cl.viewstate.time = cl.time; + cl.viewstate.realtime = realtime; + CL_UpdateScreen (&cl.viewstate); Sys_Printf ("Loading game from %s...\n", name->str); f = QFS_Open (name->str, "rz"); @@ -646,7 +654,7 @@ Host_Loadgame_f (void) Sys_Printf ("Unexpected EOF reading %s\n", name->str); goto end; } - game = PL_GetPropertyList (script->p); + game = PL_GetPropertyList (script->p, 0); } else { sscanf (script->token->str, "%i", &version); if (version != SAVEGAME_VERSION) { @@ -657,6 +665,7 @@ Host_Loadgame_f (void) game = convert_to_game_dict (script); } + memset (spawn_parms, 0, sizeof (spawn_parms)); item = PL_ObjectForKey (game, "spawn_parms"); for (i = 0; i < NUM_SPAWN_PARMS; i++) { if (i >= PL_A_NumObjects (item)) @@ -664,7 +673,7 @@ Host_Loadgame_f (void) spawn_parms[i] = atof (PL_String (PL_ObjectAtIndex (item, i))); } current_skill = atoi (PL_String (PL_ObjectForKey (game, "current_skill"))); - Cvar_SetValue (skill, current_skill); + skill = current_skill; mapname = strdup (PL_String (PL_ObjectForKey (game, "name"))); CL_Disconnect_f (); @@ -685,14 +694,13 @@ Host_Loadgame_f (void) break; item = PL_ObjectAtIndex (list, i); style = PL_String (item); - sv.lightstyles[i] = str = Hunk_Alloc (strlen (style) + 1); + sv.lightstyles[i] = str = Hunk_Alloc (0, strlen (style) + 1); strcpy (str, style); } ED_InitGlobals (&sv_pr_state, PL_ObjectForKey (game, "globals")); list = PL_ObjectForKey (game, "entities"); - entnum = 0; count = PL_A_NumObjects (list); if (count > sv.max_edicts) Host_Error ("too many entities in saved game. adjust max_edicts\n"); @@ -700,7 +708,7 @@ Host_Loadgame_f (void) plitem_t *entity = PL_ObjectAtIndex (list, entnum); edict_t *ent = EDICT_NUM (&sv_pr_state, entnum); - memset (&ent->v, 0, sv_pr_state.progs->entityfields * 4); + memset (&E_fld (ent, 0), 0, sv_pr_state.progs->entityfields * 4); ent->free = false; ED_InitEntity (&sv_pr_state, entity, ent); @@ -715,13 +723,13 @@ Host_Loadgame_f (void) for (i = 0; i < NUM_SPAWN_PARMS; i++) svs.clients->spawn_parms[i] = spawn_parms[i]; - if (cls.state != ca_dedicated) { + if (!net_is_dedicated) { CL_EstablishConnection ("local"); Host_Reconnect_f (); } end: if (game) - PL_Free (game); + PL_Release (game); if (mapname) free (mapname); if (script) @@ -740,7 +748,7 @@ Host_Name_f (void) const char *newName; if (Cmd_Argc () == 1) { - Sys_Printf ("\"name\" is \"%s\"\n", cl_name->string); + Sys_Printf ("\"name\" is \"%s\"\n", cl_name); return; } if (Cmd_Argc () == 2) @@ -749,9 +757,9 @@ Host_Name_f (void) newName = Cmd_Args (1); if (cmd_source == src_command) { - if (strcmp (cl_name->string, newName) == 0) + if (strcmp (cl_name, newName) == 0) return; - Cvar_Set (cl_name, va ("%.15s", newName)); + Cvar_Set ("_cl_name", va (0, "%.15s", newName)); if (cls.state >= ca_connected) CL_Cmd_ForwardToServer (); return; @@ -778,17 +786,17 @@ Host_Version_f (void) } static void -Host_Say (qboolean teamonly) +Host_Say (bool teamonly) { client_t *client; client_t *save; - int j; + unsigned j; char *p; char text[64]; - qboolean fromServer = false; + bool fromServer = false; if (cmd_source == src_command) { - if (cls.state == ca_dedicated) { + if (net_is_dedicated) { fromServer = true; teamonly = false; } else { @@ -802,7 +810,7 @@ Host_Say (qboolean teamonly) save = host_client; - p = Hunk_TempAlloc (strlen (Cmd_Args (1)) + 1); + p = Hunk_TempAlloc (0, strlen (Cmd_Args (1)) + 1); strcpy (p, Cmd_Args (1)); // remove quotes if present if (*p == '"') { @@ -813,10 +821,10 @@ Host_Say (qboolean teamonly) if (!fromServer) snprintf (text, sizeof (text), "%c%s: ", 1, save->name); else - snprintf (text, sizeof (text), "%c<%s> ", 1, hostname->string); + snprintf (text, sizeof (text), "%c<%s> ", 1, hostname); j = sizeof (text) - 2 - strlen (text); // -2 for /n and null terminator - if ((int) strlen (p) > j) + if (strlen (p) > j) p[j] = 0; strcat (text, p); @@ -825,7 +833,7 @@ Host_Say (qboolean teamonly) for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) { if (!client || !client->active || !client->spawned) continue; - if (teamplay->int_val && teamonly && SVfloat (client->edict, team) != + if (teamplay && teamonly && SVfloat (client->edict, team) != SVfloat (save->edict, team)) continue; host_client = client; @@ -853,7 +861,7 @@ Host_Tell_f (void) { client_t *client; client_t *save; - int j; + unsigned j; char *p; char text[64]; @@ -868,7 +876,7 @@ Host_Tell_f (void) strcpy (text, host_client->name); strcat (text, ": "); - p = Hunk_TempAlloc (strlen (Cmd_Args (1)) + 1); + p = Hunk_TempAlloc (0, strlen (Cmd_Args (1)) + 1); strcpy (p, Cmd_Args (1)); // remove quotes if present @@ -878,7 +886,7 @@ Host_Tell_f (void) } // check length & truncate if necessary j = sizeof (text) - 2 - strlen (text); // -2 for /n and null terminator - if ((int) strlen (p) > j) + if (strlen (p) > j) p[j] = 0; strcat (text, p); @@ -923,7 +931,7 @@ Host_Pause_f (void) CL_Cmd_ForwardToServer (); return; } - if (!pausable->int_val) + if (!pausable) SV_ClientPrintf ("Pause not allowed.\n"); else { sv.paused ^= 1; @@ -968,7 +976,7 @@ Host_PreSpawn_f (void) static void Host_Spawn_f (void) { - int i; + unsigned i; client_t *client; edict_t *ent; float *sendangles; @@ -989,7 +997,7 @@ Host_Spawn_f (void) } else { // set up the edict ent = host_client->edict; - memset (&ent->v, 0, sv_pr_state.progs->entityfields * 4); + memset (&E_fld (ent, 0), 0, sv_pr_state.progs->entityfields * 4); SVfloat (ent, colormap) = NUM_FOR_EDICT (&sv_pr_state, ent); SVfloat (ent, team) = (host_client->colors & 15) + 1; SVstring (ent, netname) = PR_SetString (&sv_pr_state, @@ -1098,8 +1106,8 @@ Host_Kick_f (void) const char *who; const char *message = NULL; client_t *save; - int i; - qboolean byNumber = false; + unsigned i; + bool byNumber = false; if (cmd_source == src_command) { if (!sv.active) { @@ -1114,7 +1122,7 @@ Host_Kick_f (void) if (Cmd_Argc () > 2 && strcmp (Cmd_Argv (1), "#") == 0) { i = atof (Cmd_Argv (2)) - 1; - if (i < 0 || i >= svs.maxclients) + if (i >= svs.maxclients) return; if (!svs.clients[i].active) return; @@ -1132,10 +1140,10 @@ Host_Kick_f (void) if (i < svs.maxclients) { if (cmd_source == src_command) - if (cls.state == ca_dedicated) + if (net_is_dedicated) who = "Console"; else - who = cl_name->string; + who = cl_name; else who = save->name; @@ -1290,7 +1298,7 @@ Host_Give_f (void) static edict_t * FindViewthing (void) { - int i; + pr_uint_t i; edict_t *e; for (i = 0; i < sv.num_edicts; i++) { @@ -1320,7 +1328,7 @@ Host_Viewmodel_f (void) } SVfloat (e, frame) = 0; - cl.model_precache[(int) SVfloat (e, modelindex)] = m; + cl_world.models.a[(int) SVfloat (e, modelindex)] = m; } static void @@ -1333,7 +1341,7 @@ Host_Viewframe_f (void) e = FindViewthing (); if (!e) return; - m = cl.model_precache[(int) SVfloat (e, modelindex)]; + m = cl_world.models.a[(int) SVfloat (e, modelindex)]; f = atoi (Cmd_Argv (1)); if (f >= m->numframes) @@ -1366,7 +1374,7 @@ Host_Viewnext_f (void) e = FindViewthing (); if (!e) return; - m = cl.model_precache[(int) SVfloat (e, modelindex)]; + m = cl_world.models.a[(int) SVfloat (e, modelindex)]; SVfloat (e, frame) = SVfloat (e, frame) + 1; if (SVfloat (e, frame) >= m->numframes) @@ -1385,7 +1393,7 @@ Host_Viewprev_f (void) if (!e) return; - m = cl.model_precache[(int) SVfloat (e, modelindex)]; + m = cl_world.models.a[(int) SVfloat (e, modelindex)]; SVfloat (e, frame) = SVfloat (e, frame) - 1; if (SVfloat (e, frame) < 0) @@ -1401,7 +1409,7 @@ Host_Startdemos_f (void) { int i, c; - if (cls.state == ca_dedicated) { + if (net_is_dedicated) { if (!sv.active) Cbuf_AddText (host_cbuf, "map start\n"); return; @@ -1432,7 +1440,7 @@ Host_Startdemos_f (void) static void Host_Demos_f (void) { - if (cls.state == ca_dedicated) + if (net_is_dedicated) return; if (cls.demonum == -1) cls.demonum = 1; @@ -1448,7 +1456,7 @@ Host_Demos_f (void) static void Host_Stopdemo_f (void) { - if (cls.state == ca_dedicated) + if (net_is_dedicated) return; if (!cls.demoplayback) return; diff --git a/nq/source/sbar.c b/nq/source/sbar.c deleted file mode 100644 index e77ff103a..000000000 --- a/nq/source/sbar.c +++ /dev/null @@ -1,1812 +0,0 @@ -/* - sbar.c - - Status bar - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif - -#include - -#include "QF/cmd.h" -#include "QF/console.h" -#include "QF/cvar.h" -#include "QF/draw.h" -#include "QF/dstring.h" -#include "QF/gib.h" -#include "QF/screen.h" -#include "QF/sys.h" -#include "QF/va.h" -#include "QF/vid.h" -#include "QF/view.h" -#include "QF/wad.h" - -#include "QF/plugin/console.h" -#include "QF/plugin/vid_render.h" - -#include "client.h" -#include "compat.h" -#include "game.h" -#include "sbar.h" -#include "server.h" - -int sb_updates; // if >= vid.numpages, no update needed - -#define STAT_MINUS 10 // num frame for '-' stats digit - -qpic_t *sb_nums[2][11]; -qpic_t *sb_colon, *sb_slash; -qpic_t *sb_ibar; -qpic_t *sb_sbar; -qpic_t *sb_scorebar; - -qpic_t *sb_weapons[7][8]; // 0 is active, 1 is owned, 2-5 are flashes -qpic_t *sb_ammo[4]; -qpic_t *sb_sigil[4]; -qpic_t *sb_armor[3]; -qpic_t *sb_items[32]; - -qpic_t *sb_faces[7][2]; // 0 is gibbed, 1 is dead, 2-6 are alive - // 0 is static, 1 is temporary animation -qpic_t *sb_face_invis; -qpic_t *sb_face_quad; -qpic_t *sb_face_invuln; -qpic_t *sb_face_invis_invuln; - -qboolean sb_showscores; - -int sb_lines; // scan lines to draw -qboolean hudswap; - -qpic_t *rsb_invbar[2]; -qpic_t *rsb_weapons[5]; -qpic_t *rsb_items[2]; -qpic_t *rsb_ammo[3]; -qpic_t *rsb_teambord; // PGM 01/19/97 - team color border - - // MED 01/04/97 added two more weapons + 3 - // alternates for grenade launcher -qpic_t *hsb_weapons[7][5]; // 0 is active, 1 is owned, 2-5 are flashes - - // MED 01/04/97 added array to simplify - // weapon parsing -int hipweapons[4] = - { HIT_LASER_CANNON_BIT, HIT_MJOLNIR_BIT, 4, HIT_PROXIMITY_GUN_BIT }; -qpic_t *hsb_items[2]; // MED 01/04/97 added hipnotic items array - -cvar_t *hud_sbar; -cvar_t *hud_swap; -cvar_t *hud_scoreboard_gravity; -cvar_t *scr_centertime; -cvar_t *scr_printspeed; - -static view_t *sbar_view; -static view_t *sbar_inventory_view; -static view_t *sbar_frags_view; - -static view_t *hud_view; -static view_t *hud_inventory_view; -static view_t *hud_armament_view; -static view_t *hud_frags_view; - -static view_t *overlay_view; -static view_t *stuff_view; -static view_t *main_view; - -static void -hud_swap_f (cvar_t *var) -{ - hudswap = var->int_val; - if (var->int_val) { - hud_armament_view->gravity = grav_southwest; - hud_armament_view->children[0]->gravity = grav_northwest; - hud_armament_view->children[1]->gravity = grav_southeast; - stuff_view->gravity = grav_southeast; - } else { - hud_armament_view->gravity = grav_southeast; - hud_armament_view->children[0]->gravity = grav_northeast; - hud_armament_view->children[1]->gravity = grav_southwest; - stuff_view->gravity = grav_southwest; - } - view_move (hud_armament_view, hud_armament_view->xpos, - hud_armament_view->ypos); - view_move (stuff_view, stuff_view->xpos, stuff_view->ypos); -} - -static void -hud_scoreboard_gravity_f (cvar_t *var) -{ - grav_t grav; - - if (strequal (var->string, "center")) - grav = grav_center; - else if (strequal (var->string, "northwest")) - grav = grav_northwest; - else if (strequal (var->string, "north")) - grav = grav_north; - else if (strequal (var->string, "northeast")) - grav = grav_northeast; - else if (strequal (var->string, "west")) - grav = grav_west; - else if (strequal (var->string, "east")) - grav = grav_east; - else if (strequal (var->string, "southwest")) - grav = grav_southwest; - else if (strequal (var->string, "south")) - grav = grav_south; - else if (strequal (var->string, "southeast")) - grav = grav_southeast; - else - grav = grav_center; - overlay_view->gravity = grav; - view_move (overlay_view, overlay_view->xpos, overlay_view->ypos); -} - -static void -calc_sb_lines (cvar_t *var) -{ - int stuff_y; - - if (var->int_val >= 120) { - sb_lines = 0; - stuff_y = 0; - } else if (var->int_val >= 110) { - sb_lines = 24; - sbar_inventory_view->visible = 0; - hud_inventory_view->visible = 0; - hud_armament_view->visible = 0; - stuff_y = 32; - } else { - sb_lines = 48; - sbar_inventory_view->visible = 1; - hud_inventory_view->visible = 1; - hud_armament_view->visible = 1; - stuff_y = 48; - } - if (sb_lines) { - sbar_view->visible = 1; - hud_view->visible = 1; - view_resize (sbar_view, sbar_view->xlen, sb_lines); - view_resize (hud_view, hud_view->xlen, sb_lines); - } else { - sbar_view->visible = 0; - hud_view->visible = 0; - } - view_move (stuff_view, stuff_view->xpos, stuff_y); -} - -static void -hud_sbar_f (cvar_t *var) -{ - r_data->vid->recalc_refdef = true; - if (r_data->scr_viewsize) - calc_sb_lines (r_data->scr_viewsize); - r_data->lineadj = var->int_val ? sb_lines : 0; - if (var->int_val) { - view_remove (main_view, main_view->children[0]); - view_insert (main_view, sbar_view, 0); - } else { - view_remove (main_view, main_view->children[0]); - view_insert (main_view, hud_view, 0); - } -} - -static void -viewsize_f (cvar_t *var) -{ - calc_sb_lines (var); - if (hud_sbar) - r_data->lineadj = hud_sbar->int_val ? sb_lines : 0; -} - - -static int -Sbar_ColorForMap (int m) -{ - return (bound (0, m, 13) * 16) + 8; -} - -static void -Sbar_ShowScores (void) -{ - if (sb_showscores) - return; - - sb_showscores = true; - sb_updates = 0; -} - -static void -Sbar_DontShowScores (void) -{ - sb_showscores = false; - sb_updates = 0; -} - -void -Sbar_Changed (void) -{ - sb_updates = 0; // update next frame -} - -static void -draw_pic (view_t *view, int x, int y, qpic_t *pic) -{ - r_funcs->Draw_Pic (view->xabs + x, view->yabs + y, pic); -} - -static inline void -draw_cachepic (view_t *view, int x, int y, const char *name, int cent) -{ - qpic_t *pic = r_funcs->Draw_CachePic (name, true); - if (cent) - x += (view->xlen - pic->width) / 2; - r_funcs->Draw_Pic (view->xabs + x, view->yabs + y, pic); -} - -static inline void -draw_subpic (view_t *view, int x, int y, qpic_t *pic, - int srcx, int srcy, int width, int height) -{ - r_funcs->Draw_SubPic (view->xabs + x, view->yabs + y, pic, - srcx, srcy, width, height); -} - -static inline void -draw_transpic (view_t *view, int x, int y, qpic_t *pic) -{ - r_funcs->Draw_Pic (view->xabs + x, view->yabs + y, pic); -} - - -// drawing routines are reletive to the status bar location - -static inline void -draw_character (view_t *view, int x, int y, int c) -{ - r_funcs->Draw_Character (view->xabs + x, view->yabs + y, c); -} - -static inline void -draw_string (view_t *view, int x, int y, const char *str) -{ - r_funcs->Draw_String (view->xabs + x, view->yabs + y, str); -} - -static inline void -draw_altstring (view_t *view, int x, int y, const char *str) -{ - r_funcs->Draw_AltString (view->xabs + x, view->yabs + y, str); -} - -static inline void -draw_nstring (view_t *view, int x, int y, const char *str, int n) -{ - r_funcs->Draw_nString (view->xabs + x, view->yabs + y, str, n); -} - -static inline void -draw_fill (view_t *view, int x, int y, int w, int h, int col) -{ - r_funcs->Draw_Fill (view->xabs + x, view->yabs + y, w, h, col); -} - -static void -draw_num (view_t *view, int x, int y, int num, int digits, int color) -{ - char str[12]; - char *ptr; - int l, frame; - - if (num > 999999999) - num = 999999999; - - l = snprintf (str, sizeof (str), "%d", num); - ptr = str; - if (l > digits) - ptr += (l - digits); - if (l < digits) - x += (digits - l) * 24; - - while (*ptr) { - if (*ptr == '-') - frame = STAT_MINUS; - else - frame = *ptr - '0'; - - draw_transpic (view, x, y, sb_nums[color][frame]); - x += 24; - ptr++; - } -} - -static inline void -draw_smallnum (view_t *view, int x, int y, int n, int packed, int colored) -{ - char num[4]; - - packed = packed != 0; // ensure 0 or 1 - - if (n > 999) - n = 999; - - snprintf (num, sizeof (num), "%3d", n); - if (colored) { - if (num[0] != ' ') - num[0] = 18 + num[0] - '0'; - if (num[1] != ' ') - num[1] = 18 + num[1] - '0'; - if (num[2] != ' ') - num[2] = 18 + num[2] - '0'; - } - draw_character (view, x + packed, y, num[0]); - draw_character (view, x + 8, y, num[1]); - draw_character (view, x + 16 - packed, y, num[2]); -} - -static void -draw_tile (view_t *view) -{ - r_funcs->Draw_TileClear (view->xabs, view->yabs, view->xlen, view->ylen); -} - -static void -draw_ammo_sbar (view_t *view) -{ - int i, count; - - // ammo counts - for (i = 0; i < 4; i++) { - count = cl.stats[STAT_SHELLS + i]; - draw_smallnum (view, (6 * i + 1) * 8 + 2, 0, count, 0, 1); - } -} - -static void -draw_ammo_hud (view_t *view) -{ - int i, count; - - // ammo counts - for (i = 0; i < 4; i++) { - count = cl.stats[STAT_SHELLS + i]; - draw_subpic (view, 0, i * 11, sb_ibar, 3 + (i * 48), 0, 42, 11); - draw_smallnum (view, 7, i * 11, count, 0, 1); - } -} - -static int -calc_flashon (float time, int mask) -{ - int flashon; - - flashon = (int) ((cl.time - time) * 10); - if (flashon < 0) - flashon = 0; - if (flashon >= 10) { - if (cl.stats[STAT_ACTIVEWEAPON] == mask) - flashon = 1; - else - flashon = 0; - } else - flashon = (flashon % 5) + 2; - return flashon; -} - -static void -draw_weapons_sbar (view_t *view) -{ - int flashon, i; - - for (i = 0; i < 7; i++) { - if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN << i)) { - flashon = calc_flashon (cl.item_gettime[i], IT_SHOTGUN << i); - draw_pic (view, i * 24, 0, sb_weapons[flashon][i]); - if (flashon > 1) - sb_updates = 0; // force update to remove flash - } - } -} - -static void -draw_weapons_hud (view_t *view) -{ - int flashon, i, x = 0; - - if (view->parent->gravity == grav_southeast) - x = view->xlen - 24; - - for (i = r_data->vid->conheight < 204; i < 7; i++) { - if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN << i)) { - flashon = calc_flashon (cl.item_gettime[i], IT_SHOTGUN << i); - draw_subpic (view, x, i * 16, sb_weapons[flashon][i], 0, 0, 24, 16); - if (flashon > 1) - sb_updates = 0; // force update to remove flash - } - } -} - -static void -draw_items (view_t *view) -{ - float time; - int flashon = 0, i; - - for (i = 0; i < 6; i++) { - if (cl.stats[STAT_ITEMS] & (1 << (17 + i))) { - time = cl.item_gettime[17 + i]; - if (time && time > cl.time - 2 && flashon) { // Flash frame - sb_updates = 0; - } else { - draw_pic (view, i * 16, 0, sb_items[i]); - } - if (time && time > cl.time - 2) - sb_updates = 0; - } - } -} - -static void -draw_sigils (view_t *view) -{ - float time; - int flashon = 0, i; - - for (i = 0; i < 4; i++) { - if (cl.stats[STAT_ITEMS] & (1 << (28 + i))) { - time = cl.item_gettime[28 + i]; - if (time && time > cl.time - 2 && flashon) { // flash frame - sb_updates = 0; - } else { - draw_pic (view, i * 8, 0, sb_sigil[i]); - } - if (time && time > cl.time - 2) - sb_updates = 0; - } - } -} - -static void -draw_inventory_sbar (view_t *view) -{ - draw_pic (view, 0, 0, sb_ibar); - view_draw (view); -} - - -int fragsort[MAX_SCOREBOARD]; -char scoreboardtext[MAX_SCOREBOARD][20]; -int scoreboardtop[MAX_SCOREBOARD]; -int scoreboardbottom[MAX_SCOREBOARD]; -int scoreboardcount[MAX_SCOREBOARD]; -int scoreboardlines; - - -static void -Sbar_SortFrags (void) -{ - int i, j, k; - - // sort by frags - scoreboardlines = 0; - for (i = 0; i < cl.maxclients; i++) { - if (cl.scores[i].name->value[0]) { - fragsort[scoreboardlines] = i; - scoreboardlines++; - } - } - - for (i = 0; i < scoreboardlines; i++) { - for (j = 0; j < (scoreboardlines - 1 - i); j++) { - if (cl.scores[fragsort[j]].frags < cl.scores[fragsort[j + 1]].frags) { - k = fragsort[j]; - fragsort[j] = fragsort[j + 1]; - fragsort[j + 1] = k; - } - } - } -} - - -static void -draw_solo (view_t *view) -{ - char str[80]; - int minutes, seconds, tens, units; - int l; - - draw_pic (view, 0, 0, sb_scorebar); - - snprintf (str, sizeof (str), "Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], - cl.stats[STAT_TOTALMONSTERS]); - draw_string (view, 8, 4, str); - - snprintf (str, sizeof (str), "Secrets :%3i /%3i", cl.stats[STAT_SECRETS], - cl.stats[STAT_TOTALSECRETS]); - draw_string (view, 8, 12, str); - - // time - minutes = cl.time / 60; - seconds = cl.time - (60 * minutes); - tens = seconds / 10; - units = seconds - (10 * tens); - snprintf (str, sizeof (str), "Time :%3i:%i%i", minutes, tens, units); - draw_string (view, 184, 4, str); - - // draw level name - l = strlen (cl.levelname); - l = 232 - l * 4; - draw_string (view, max (l, 152), 12, cl.levelname); -} - -static void -draw_frags (view_t *view) -{ - int i, k, l, p = -1; - int top, bottom; - int x; - scoreboard_t *s; - - if (cl.maxclients == 1) - return; - - Sbar_SortFrags (); - - // draw the text - l = scoreboardlines <= 4 ? scoreboardlines : 4; - - x = 0; - - for (i = 0; i < l; i++) { - k = fragsort[i]; - s = &cl.scores[k]; - if (!s->name->value[0]) - continue; - - // draw background - top = Sbar_ColorForMap (s->topcolor); - bottom = Sbar_ColorForMap (s->bottomcolor); - - draw_fill (view, x + 4, 1, 28, 4, top); - draw_fill (view, x + 4, 5, 28, 3, bottom); - - draw_smallnum (view, x + 6, 0, s->frags, 0, 0); - - if (k == cl.viewentity - 1) - p = i; - - x += 32; - } - if (p != -1) { - draw_character (view, p * 32, 0, 16); - draw_character (view, p * 32 + 26, 0, 17); - } -} - -static void -draw_face (view_t *view) -{ - int f, anim; - - if ((cl.stats[STAT_ITEMS] & (IT_INVISIBILITY | IT_INVULNERABILITY)) - == (IT_INVISIBILITY | IT_INVULNERABILITY)) { - draw_pic (view, 112, 0, sb_face_invis_invuln); - return; - } - if (cl.stats[STAT_ITEMS] & IT_QUAD) { - draw_pic (view, 112, 0, sb_face_quad); - return; - } - if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) { - draw_pic (view, 112, 0, sb_face_invis); - return; - } - if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) { - draw_pic (view, 112, 0, sb_face_invuln); - return; - } - - if (cl.stats[STAT_HEALTH] >= 100) - f = 4; - else - f = cl.stats[STAT_HEALTH] / 20; - - if (cl.time <= cl.faceanimtime) { - anim = 1; - sb_updates = 0; // make sure the anim gets drawn over - } else - anim = 0; - draw_pic (view, 112, 0, sb_faces[f][anim]); -} - -static void -draw_status_bar (view_t *view) -{ - draw_pic (view, 0, 0, sb_sbar); -} - -static inline void -draw_armor (view_t *view) -{ - if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) { - draw_num (view, 24, 0, 666, 3, 1); - } else { - draw_num (view, 24, 0, cl.stats[STAT_ARMOR], 3, - cl.stats[STAT_ARMOR] <= 25); - if (cl.stats[STAT_ITEMS] & IT_ARMOR3) - draw_pic (view, 0, 0, sb_armor[2]); - else if (cl.stats[STAT_ITEMS] & IT_ARMOR2) - draw_pic (view, 0, 0, sb_armor[1]); - else if (cl.stats[STAT_ITEMS] & IT_ARMOR1) - draw_pic (view, 0, 0, sb_armor[0]); - } -} - -static inline void -draw_health (view_t *view) -{ - draw_num (view, 136, 0, cl.stats[STAT_HEALTH], 3, - cl.stats[STAT_HEALTH] <= 25); -} - -static inline void -draw_ammo (view_t *view) -{ - if (cl.stats[STAT_ITEMS] & IT_SHELLS) - draw_pic (view, 224, 0, sb_ammo[0]); - else if (cl.stats[STAT_ITEMS] & IT_NAILS) - draw_pic (view, 224, 0, sb_ammo[1]); - else if (cl.stats[STAT_ITEMS] & IT_ROCKETS) - draw_pic (view, 224, 0, sb_ammo[2]); - else if (cl.stats[STAT_ITEMS] & IT_CELLS) - draw_pic (view, 224, 0, sb_ammo[3]); - - draw_num (view, 248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10); -} - -static void -draw_status (view_t *view) -{ - if (sb_showscores || cl.stats[STAT_HEALTH] <= 0) { - draw_solo (view); - return; - } - - draw_armor (view); - draw_face (view); - draw_health (view); - draw_ammo (view); -} - -static void -draw_rogue_weapons_sbar (view_t *view) -{ - int i; - - draw_weapons_sbar (view); - - // check for powered up weapon. - if (cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN) { - for (i = 0; i < 5; i++) { - if (cl.stats[STAT_ACTIVEWEAPON] == (RIT_LAVA_NAILGUN << i)) { - draw_pic (view, (i + 2) * 24, 0, rsb_weapons[i]); - } - } - } -} - -static void -draw_rogue_weapons_hud (view_t *view) -{ - int flashon, i, j; - qpic_t *pic; - - for (i = r_data->vid->conheight < 204; i < 7; i++) { - if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN << i)) { - flashon = calc_flashon (cl.item_gettime[i], IT_SHOTGUN << i); - if (i >= 2) { - j = i - 2; - if (cl.stats[STAT_ACTIVEWEAPON] == (RIT_LAVA_NAILGUN << j)) - pic = rsb_weapons[j]; - else - pic = sb_weapons[flashon][i]; - } else { - pic = sb_weapons[flashon][i]; - } - draw_subpic (view, 0, i * 16, pic, 0, 0, 24, 16); - if (flashon > 1) - sb_updates = 0; // force update to remove flash - } - } -} - -static void -draw_rogue_ammo_hud (view_t *view) -{ - int i, count; - qpic_t *pic; - - if (cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN) - pic = rsb_invbar[0]; - else - pic = rsb_invbar[1]; - - for (i = 0; i < 4; i++) { - count = cl.stats[STAT_SHELLS + i]; - draw_subpic (view, 0, i * 11, pic, 3 + (i * 48), 0, 42, 11); - draw_smallnum (view, 7, i * 11, count, 0, 1); - } -} - -static void -draw_rogue_items (view_t *view) -{ - int i; - float time; - - draw_items (view); - - for (i = 0; i < 2; i++) { - if (cl.stats[STAT_ITEMS] & (1 << (29 + i))) { - time = cl.item_gettime[29 + i]; - draw_pic (view, 96 + i * 16, 0, rsb_items[i]); - if (time && time > (cl.time - 2)) - sb_updates = 0; - } - } -} - -static void -draw_rogue_inventory_sbar (view_t *view) -{ - if (cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN) - draw_pic (view, 0, 0, rsb_invbar[0]); - else - draw_pic (view, 0, 0, rsb_invbar[1]); - - view_draw (view); -} - -static void -draw_rogue_face (view_t *view) -{ - int top, bottom; - scoreboard_t *s; - - // PGM 01/19/97 - team color drawing - - s = &cl.scores[cl.viewentity - 1]; - - top = Sbar_ColorForMap (s->topcolor); - bottom = Sbar_ColorForMap (s->bottomcolor); - - draw_pic (view, 112, 0, rsb_teambord); - draw_fill (view, 113, 3, 22, 9, top); - draw_fill (view, 113, 12, 22, 9, bottom); - - draw_smallnum (view, 108, 3, s->frags, 1, top == 8); -} - -static void -draw_rogue_status (view_t *view) -{ - if (sb_showscores || cl.stats[STAT_HEALTH] <= 0) { - draw_solo (view); - return; - } - - draw_num (view, 24, 0, cl.stats[STAT_ARMOR], 3, - cl.stats[STAT_ARMOR] <= 25); - if (cl.stats[STAT_ITEMS] & RIT_ARMOR3) - draw_pic (view, 0, 0, sb_armor[2]); - else if (cl.stats[STAT_ITEMS] & RIT_ARMOR2) - draw_pic (view, 0, 0, sb_armor[1]); - else if (cl.stats[STAT_ITEMS] & RIT_ARMOR1) - draw_pic (view, 0, 0, sb_armor[0]); - - // PGM 03/02/97 - fixed so color swatch appears in only CTF modes - if (cl.maxclients != 1 && teamplay->int_val > 3 && teamplay->int_val < 7) - draw_rogue_face (view); - else - draw_face (view); - - draw_health (view); - - if (cl.stats[STAT_ITEMS] & RIT_SHELLS) - draw_pic (view, 224, 0, sb_ammo[0]); - else if (cl.stats[STAT_ITEMS] & RIT_NAILS) - draw_pic (view, 224, 0, sb_ammo[1]); - else if (cl.stats[STAT_ITEMS] & RIT_ROCKETS) - draw_pic (view, 224, 0, sb_ammo[2]); - else if (cl.stats[STAT_ITEMS] & RIT_CELLS) - draw_pic (view, 224, 0, sb_ammo[3]); - else if (cl.stats[STAT_ITEMS] & RIT_LAVA_NAILS) - draw_pic (view, 224, 0, rsb_ammo[0]); - else if (cl.stats[STAT_ITEMS] & RIT_PLASMA_AMMO) - draw_pic (view, 224, 0, rsb_ammo[1]); - else if (cl.stats[STAT_ITEMS] & RIT_MULTI_ROCKETS) - draw_pic (view, 224, 0, rsb_ammo[2]); - draw_num (view, 248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10); -} - -static void -draw_hipnotic_weapons_sbar (view_t *view) -{ - static int x[] = {0, 24, 48, 72, 96, 120, 144, 176, 200, 96}; - static int h[] = {0, 1, 3}; - int flashon, i; - qpic_t *pic; - int mask; - float time; - - for (i = 0; i < 10; i++) { - if (i < 7) { - mask = IT_SHOTGUN << i; - time = cl.item_gettime[i]; - } else { - mask = 1 << hipweapons[h[i - 7]]; - time = cl.item_gettime[hipweapons[h[i - 7]]]; - } - if (cl.stats[STAT_ITEMS] & mask) { - flashon = calc_flashon (time, mask); - - if (i == 4 && cl.stats[STAT_ACTIVEWEAPON] == (1 << hipweapons[3])) - continue; - if (i == 9 && cl.stats[STAT_ACTIVEWEAPON] != (1 << hipweapons[3])) - continue; - if (i < 7) { - pic = sb_weapons[flashon][i]; - } else { - pic = hsb_weapons[flashon][h[i - 7]]; - } - - draw_subpic (view, x[i], 0, pic, 0, 0, 24, 16); - - if (flashon > 1) - sb_updates = 0; // force update to remove flash - } - } -} - -static void -draw_hipnotic_weapons_hud (view_t *view) -{ - int flashon, i; - static int y[] = {0, 16, 32, 48, 64, 96, 112, 128, 144, 80}; - static int h[] = {0, 1, 3}; - qpic_t *pic; - int mask; - float time; - - for (i = 0; i < 10; i++) { - if (i < 7) { - mask = IT_SHOTGUN << i; - time = cl.item_gettime[i]; - } else { - mask = 1 << hipweapons[h[i - 7]]; - time = cl.item_gettime[hipweapons[h[i - 7]]]; - } - if (cl.stats[STAT_ITEMS] & mask) { - flashon = calc_flashon (time, mask); - - if (i < 7) { - pic = sb_weapons[flashon][i]; - } else { - pic = hsb_weapons[flashon][h[i - 7]]; - } - - draw_subpic (view, 0, y[i], pic, 0, 0, 24, 16); - - if (flashon > 1) - sb_updates = 0; // force update to remove flash - } - } -} - -static void -draw_hipnotic_items (view_t *view) -{ - int i; - float time; - - // items - for (i = 2; i < 6; i++) { - if (cl.stats[STAT_ITEMS] & (1 << (17 + i))) { - time = cl.item_gettime[17 + i]; - draw_pic (view, 192 + i * 16, 0, sb_items[i]); - if (time && time > cl.time - 2) - sb_updates = 0; - } - } - - // hipnotic items - for (i = 0; i < 2; i++) { - if (cl.stats[STAT_ITEMS] & (1 << (24 + i))) { - time = cl.item_gettime[24 + i]; - draw_pic (view, 288 + i * 16, 0, hsb_items[i]); - if (time && time > (cl.time - 2)) - sb_updates = 0; - } - } -} - -static void -draw_hipnotic_inventory_sbar (view_t *view) -{ - draw_pic (view, 0, 0, sb_ibar); - view_draw (view); -} - -static void -draw_hipnotic_status (view_t *view) -{ - if (sb_showscores || cl.stats[STAT_HEALTH] <= 0) { - draw_solo (view); - return; - } - - draw_armor (view); - draw_face (view); - draw_health (view); - draw_ammo (view); - - if (cl.stats[STAT_ITEMS] & IT_KEY1) - draw_pic (view, 209, 3, sb_items[0]); - if (cl.stats[STAT_ITEMS] & IT_KEY2) - draw_pic (view, 209, 12, sb_items[1]); -} - -static void -sbar_update_vis (void) -{ - qboolean headsup; - - if (r_data->scr_copyeverything) - Sbar_Changed (); - - sbar_view->visible = 0; - - headsup = !(hud_sbar->int_val || r_data->scr_viewsize->int_val < 100); - - if ((sb_updates >= r_data->vid->numpages) && !headsup) - return; - - if (con_module && - con_module->data->console->lines == r_data->vid->conheight) - return; // console is full screen - - if (cls.state == ca_active - && ((cl.stats[STAT_HEALTH] <= 0) || sb_showscores)) - overlay_view->visible = 1; - else - overlay_view->visible = 0; - - if (!sb_lines) - return; - - sbar_view->visible = 1; - - r_data->scr_copyeverything = 1; - sb_updates++; -} - -void -Sbar_Draw (void) -{ - sbar_update_vis (); - main_view->draw (main_view); -} - -static void -Sbar_DeathmatchOverlay (view_t *view) -{ - int i, k, l; - int top, bottom; - int x, y; - scoreboard_t *s; - - r_data->scr_copyeverything = 1; - r_data->scr_fullupdate = 0; - - draw_cachepic (view, 0, 0, "gfx/ranking.lmp", 1); - - // scores - Sbar_SortFrags (); - - // draw the text - l = scoreboardlines; - - x = 80; - y = 40; - for (i = 0; i < l; i++) { - k = fragsort[i]; - s = &cl.scores[k]; - if (!s->name->value[0]) - continue; - - // draw background - top = Sbar_ColorForMap (s->topcolor); - bottom = Sbar_ColorForMap (s->bottomcolor); - - draw_fill (view, x, y, 40, 4, top); - draw_fill (view, x, y + 4, 40, 4, bottom); - - draw_smallnum (view, x + 12, y, s->frags, 0, 0); - - if (k == cl.viewentity - 1) - draw_character (view, x - 4, y, 12); - - // draw name - draw_string (view, x + 64, y, s->name->value); - - y += 10; - } -} - -static void -Sbar_DrawScoreboard (void) -{ - //Sbar_SoloScoreboard (); - if (cl.gametype == GAME_DEATHMATCH) - Sbar_DeathmatchOverlay (overlay_view); -} - -static void -draw_overlay (view_t *view) -{ - if (sb_showscores || cl.stats[STAT_HEALTH] <= 0) { - Sbar_DrawScoreboard (); - } -} - -static void -draw_time (view_t *view) -{ - struct tm *local = NULL; - time_t utc = 0; - const char *timefmt = NULL; - char st[80]; //FIXME: overflow - - // Get local time - utc = time (NULL); - local = localtime (&utc); - - if (hud_time->int_val == 1) { // Use international format - timefmt = "%k:%M"; - } else if (hud_time->int_val >= 2) { // US AM/PM display - timefmt = "%l:%M %P"; - } - - strftime (st, sizeof (st), timefmt, local); - draw_string (view, 8, 0, st); -} - -static void -draw_fps (view_t *view) -{ - static char st[80]; - double t; - static double lastframetime; - static double lastfps; - - t = Sys_DoubleTime (); - if ((t - lastframetime) >= 0.2) { - lastfps = fps_count / (t - lastframetime); - fps_count = 0; - lastframetime = t; - snprintf (st, sizeof (st), "%5.1f FPS", lastfps); - } - draw_string (view, 80, 8, st); -} - -static void -draw_stuff (view_t *view) -{ - if (hud_time->int_val > 0) - draw_time (view); - if (hud_fps->int_val > 0) - draw_fps (view); -} - -static void -draw_intermission (view_t *view) -{ - int dig; - int num; - - r_data->scr_copyeverything = 1; - r_data->scr_fullupdate = 0; - - draw_cachepic (view, 64, 24, "gfx/complete.lmp", 0); - - draw_cachepic (view, 0, 56, "gfx/inter.lmp", 0); - - // time - dig = cl.completed_time / 60; - draw_num (view, 160, 64, dig, 3, 0); - num = cl.completed_time - dig * 60; - draw_pic (view, 234, 64, sb_colon); - draw_pic (view, 246, 64, sb_nums[0][num / 10]); - draw_pic (view, 266, 64, sb_nums[0][num % 10]); - - draw_num (view, 160, 104, cl.stats[STAT_SECRETS], 3, 0); - draw_pic (view, 232, 104, sb_slash); - draw_num (view, 240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0); - - draw_num (view, 160, 144, cl.stats[STAT_MONSTERS], 3, 0); - draw_pic (view, 232, 144, sb_slash); - draw_num (view, 240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0); -} - -void -Sbar_IntermissionOverlay (void) -{ - r_data->scr_copyeverything = 1; - r_data->scr_fullupdate = 0; - - if (cl.gametype == GAME_DEATHMATCH) { - Sbar_DeathmatchOverlay (overlay_view); - return; - } - draw_intermission (overlay_view); -} - -/* CENTER PRINTING */ -static dstring_t center_string = {&dstring_default_mem}; -static float centertime_start; // for slow victory printing -static float centertime_off; -static int center_lines; - -/* - Called for important messages that should stay in the center of the screen - for a few moments -*/ -void -Sbar_CenterPrint (const char *str) -{ - if (!str) { - centertime_off = 0; - dstring_clearstr (¢er_string); - return; - } - - dstring_copystr (¢er_string, str); - - centertime_off = scr_centertime->value; - centertime_start = realtime; - - // count the number of lines for centering - center_lines = 1; - while (*str) { - if (*str == '\n') - center_lines++; - str++; - } -} - -static void -Sbar_DrawCenterString (view_t *view, int remaining) -{ - const char *start; - int j, l, x, y; - - start = center_string.str; - - if (center_lines <= 4) - y = view->yabs + view->ylen * 0.35; - else - y = view->yabs + 48; - - do { - // scan the width of the line - for (l = 0; l < 40; l++) - if (start[l] == '\n' || !start[l]) - break; - x = view->xabs + (view->xlen - l * 8) / 2; - for (j = 0; j < l; j++, x += 8) { - r_funcs->Draw_Character (x, y, start[j]); - if (!remaining--) - return; - } - - y += 8; - - while (*start && *start != '\n') - start++; - if (!*start) - break; - start++; // skip the \n - } while (1); -} - -void -Sbar_FinaleOverlay (void) -{ - int remaining; - - r_data->scr_copyeverything = 1; - - draw_cachepic (overlay_view, 0, 16, "gfx/finale.lmp", 1); - // the finale prints the characters one at a time - remaining = scr_printspeed->value * (realtime - centertime_start); - Sbar_DrawCenterString (overlay_view, remaining); -} - -void -Sbar_DrawCenterPrint (void) -{ - r_data->scr_copytop = 1; - - centertime_off -= r_data->frametime; - if (centertime_off <= 0) - return; - - Sbar_DrawCenterString (overlay_view, -1); -} - -static void -init_sbar_views (void) -{ - view_t *view; - - sbar_view = view_new (0, 0, 320, 48, grav_south); - - sbar_frags_view = view_new (0, 0, 130, 8, grav_northeast); - sbar_frags_view->draw = draw_frags; - - sbar_inventory_view = view_new (0, 0, 320, 24, grav_northwest); - sbar_inventory_view->draw = draw_inventory_sbar; - - view = view_new (0, 0, 32, 16, grav_southwest); - view->draw = draw_weapons_sbar; - view_add (sbar_inventory_view, view); - - view = view_new (0, 0, 32, 8, grav_northwest); - view->draw = draw_ammo_sbar; - view_add (sbar_inventory_view, view); - - view = view_new (32, 0, 96, 16, grav_southeast); - view->draw = draw_items; - view_add (sbar_inventory_view, view); - - view = view_new (0, 0, 32, 16, grav_southeast); - view->draw = draw_sigils; - view_add (sbar_inventory_view, view); - - if (sbar_frags_view) - view_add (sbar_inventory_view, sbar_frags_view); - - view_add (sbar_view, sbar_inventory_view); - - view = view_new (0, 0, 320, 24, grav_southwest); - view->draw = draw_status_bar; - view_add (sbar_view, view); - - view = view_new (0, 0, 320, 24, grav_southwest); - view->draw = draw_status; - view_add (sbar_view, view); - - if (r_data->vid->conwidth > 320) { - int l = (r_data->vid->conwidth - 320) / 2; - - view = view_new (-l, 0, l, 48, grav_southwest); - view->draw = draw_tile; - view->resize_y = 1; - view_add (sbar_view, view); - - view = view_new (-l, 0, l, 48, grav_southeast); - view->draw = draw_tile; - view->resize_y = 1; - view_add (sbar_view, view); - } -} - -static void -init_hud_views (void) -{ - view_t *view; - - hud_view = view_new (0, 0, 320, 48, grav_south); - hud_frags_view = view_new (0, 0, 130, 8, grav_northeast); - hud_frags_view->draw = draw_frags; - - hud_view->resize_y = 1; - - hud_armament_view = view_new (0, 48, 42, 156, grav_southeast); - - view = view_new (0, 0, 24, 112, grav_northeast); - view->draw = draw_weapons_hud; - view_add (hud_armament_view, view); - - view = view_new (0, 0, 42, 44, grav_southeast); - view->draw = draw_ammo_hud; - view_add (hud_armament_view, view); - - hud_inventory_view = view_new (0, 0, 320, 24, grav_northwest); - view_add (hud_view, hud_inventory_view); - - view = view_new (0, 0, 320, 24, grav_southwest); - view->draw = draw_status; - view_add (hud_view, view); - - view = view_new (32, 0, 96, 16, grav_southeast); - view->draw = draw_items; - view_add (hud_inventory_view, view); - - view = view_new (0, 0, 32, 16, grav_southeast); - view->draw = draw_sigils; - view_add (hud_inventory_view, view); - - if (hud_frags_view) - view_add (hud_inventory_view, hud_frags_view); - - view = view_new (0, 0, r_data->vid->conwidth, 48, grav_south); - view_add (view, hud_view); - hud_view = view; - - view_add (hud_view, hud_armament_view); - - view_insert (main_view, hud_view, 0); -} - -static void -init_hipnotic_sbar_views (void) -{ - view_t *view; - - sbar_view = view_new (0, 0, 320, 48, grav_south); - - sbar_frags_view = view_new (0, 0, 130, 8, grav_northeast); - sbar_frags_view->draw = draw_frags; - - sbar_inventory_view = view_new (0, 0, 320, 24, grav_northwest); - sbar_inventory_view->draw = draw_hipnotic_inventory_sbar; - - view = view_new (0, 0, 224, 16, grav_southwest); - view->draw = draw_hipnotic_weapons_sbar; - view_add (sbar_inventory_view, view); - - view = view_new (0, 0, 32, 8, grav_northwest); - view->draw = draw_ammo_sbar; - view_add (sbar_inventory_view, view); - - view = view_new (0, 0, 96, 16, grav_southeast); - view->draw = draw_hipnotic_items; - view_add (sbar_inventory_view, view); - - view = view_new (0, 0, 32, 16, grav_southeast); - view->draw = draw_sigils; - view_add (sbar_inventory_view, view); - - if (sbar_frags_view) - view_add (sbar_inventory_view, sbar_frags_view); - - view_add (sbar_view, sbar_inventory_view); - - view = view_new (0, 0, 320, 24, grav_southwest); - view->draw = draw_status_bar; - view_add (sbar_view, view); - - view = view_new (0, 0, 320, 24, grav_southwest); - view->draw = draw_hipnotic_status; - view_add (sbar_view, view); - - if (r_data->vid->conwidth > 320) { - int l = (r_data->vid->conwidth - 320) / 2; - - view = view_new (-l, 0, l, 48, grav_southwest); - view->draw = draw_tile; - view->resize_y = 1; - view_add (sbar_view, view); - - view = view_new (-l, 0, l, 48, grav_southeast); - view->draw = draw_tile; - view->resize_y = 1; - view_add (sbar_view, view); - } -} - -static void -init_hipnotic_hud_views (void) -{ - view_t *view; - - hud_view = view_new (0, 0, 320, 48, grav_south); - hud_frags_view = view_new (0, 0, 130, 8, grav_northeast); - hud_frags_view->draw = draw_frags; - - hud_view->resize_y = 1; - - if (r_data->vid->conheight < 252) { - hud_armament_view = view_new (0, - min (r_data->vid->conheight - 160, 48), - 66, 160, grav_southeast); - } else { - hud_armament_view = view_new (0, 48, 42, 204, grav_southeast); - } - - view = view_new (0, 0, 24, 160, grav_northeast); - view->draw = draw_hipnotic_weapons_hud; - view_add (hud_armament_view, view); - - view = view_new (0, 0, 42, 44, grav_southeast); - view->draw = draw_ammo_hud; - view_add (hud_armament_view, view); - - hud_inventory_view = view_new (0, 0, 320, 24, grav_northwest); - view_add (hud_view, hud_inventory_view); - - view = view_new (0, 0, 320, 24, grav_southwest); - view->draw = draw_hipnotic_status; - view_add (hud_view, view); - - view = view_new (0, 0, 96, 16, grav_southeast); - view->draw = draw_hipnotic_items; - view_add (hud_inventory_view, view); - - view = view_new (0, 0, 32, 16, grav_southeast); - view->draw = draw_sigils; - view_add (hud_inventory_view, view); - - if (hud_frags_view) - view_add (hud_inventory_view, hud_frags_view); - - view = view_new (0, 0, r_data->vid->conwidth, 48, grav_south); - view_add (view, hud_view); - hud_view = view; - - view_add (hud_view, hud_armament_view); - - view_insert (main_view, hud_view, 0); -} - -static void -init_rogue_sbar_views (void) -{ - view_t *view; - - sbar_view = view_new (0, 0, 320, 48, grav_south); - - sbar_frags_view = view_new (0, 0, 130, 8, grav_northeast); - sbar_frags_view->draw = draw_frags; - - sbar_inventory_view = view_new (0, 0, 320, 24, grav_northwest); - sbar_inventory_view->draw = draw_rogue_inventory_sbar; - - view = view_new (0, 0, 224, 16, grav_southwest); - view->draw = draw_rogue_weapons_sbar; - view_add (sbar_inventory_view, view); - - view = view_new (0, 0, 32, 8, grav_northwest); - view->draw = draw_ammo_sbar; - view_add (sbar_inventory_view, view); - - view = view_new (0, 0, 128, 16, grav_southeast); - view->draw = draw_rogue_items; - view_add (sbar_inventory_view, view); - - if (sbar_frags_view) - view_add (sbar_inventory_view, sbar_frags_view); - - view_add (sbar_view, sbar_inventory_view); - - view = view_new (0, 0, 320, 24, grav_southwest); - view->draw = draw_status_bar; - view_add (sbar_view, view); - - view = view_new (0, 0, 320, 24, grav_southwest); - view->draw = draw_rogue_status; - view_add (sbar_view, view); - - if (r_data->vid->conwidth > 320) { - int l = (r_data->vid->conwidth - 320) / 2; - - view = view_new (-l, 0, l, 48, grav_southwest); - view->draw = draw_tile; - view->resize_y = 1; - view_add (sbar_view, view); - - view = view_new (-l, 0, l, 48, grav_southeast); - view->draw = draw_tile; - view->resize_y = 1; - view_add (sbar_view, view); - } -} - -static void -init_rogue_hud_views (void) -{ - view_t *view; - - hud_view = view_new (0, 0, 320, 48, grav_south); - hud_frags_view = view_new (0, 0, 130, 8, grav_northeast); - hud_frags_view->draw = draw_frags; - - hud_view->resize_y = 1; - - hud_armament_view = view_new (0, 48, 42, 156, grav_southeast); - - view = view_new (0, 0, 24, 112, grav_northeast); - view->draw = draw_rogue_weapons_hud; - view_add (hud_armament_view, view); - - view = view_new (0, 0, 42, 44, grav_southeast); - view->draw = draw_rogue_ammo_hud; - view_add (hud_armament_view, view); - - hud_inventory_view = view_new (0, 0, 320, 24, grav_northwest); - view_add (hud_view, hud_inventory_view); - - view = view_new (0, 0, 320, 24, grav_southwest); - view->draw = draw_rogue_status; - view_add (hud_view, view); - - view = view_new (0, 0, 128, 16, grav_southeast); - view->draw = draw_rogue_items; - view_add (hud_inventory_view, view); - - if (hud_frags_view) - view_add (hud_inventory_view, hud_frags_view); - - view = view_new (0, 0, r_data->vid->conwidth, 48, grav_south); - view_add (view, hud_view); - hud_view = view; - - view_add (hud_view, hud_armament_view); - - view_insert (main_view, hud_view, 0); -} - -static void -init_views (void) -{ - main_view = view_new (0, 0, r_data->vid->conwidth, r_data->vid->conheight, - grav_northwest); - if (con_module) - view_insert (con_module->data->console->view, main_view, 0); - main_view->resize_x = 1; // get resized if the 2d view resizes - main_view->resize_y = 1; - main_view->visible = 0; // but don't let the console draw our stuff - if (r_data->vid->conheight > 300) - overlay_view = view_new (0, 0, 320, 300, grav_center); - else - overlay_view = view_new (0, 0, 320, r_data->vid->conheight, - grav_center); - overlay_view->draw = draw_overlay; - overlay_view->visible = 0; - - stuff_view = view_new (0, 48, 152, 16, grav_southwest); - stuff_view->draw = draw_stuff; - - view_insert (main_view, overlay_view, 0); - view_insert (main_view, stuff_view, 0); - - if (!strcmp (qfs_gamedir->hudtype, "hipnotic")) { - init_hipnotic_sbar_views (); - init_hipnotic_hud_views (); - } else if (!strcmp (qfs_gamedir->hudtype, "rogue")) { - init_rogue_sbar_views (); - init_rogue_hud_views (); - } else { - init_sbar_views (); - init_hud_views (); - } -} - -static void -Sbar_GIB_Print_Center_f (void) -{ - if (GIB_Argc () != 2) { - GIB_USAGE ("text"); - } else - Sbar_CenterPrint (GIB_Argv(1)); -} - -static void -sbar_keydest_callback (keydest_t kd) -{ - overlay_view->visible = kd == key_game; -} - -void -Sbar_Init (void) -{ - int i; - - init_views (); - - Key_KeydestCallback (sbar_keydest_callback); - - for (i = 0; i < 10; i++) { - sb_nums[0][i] = r_funcs->Draw_PicFromWad (va ("num_%i", i)); - sb_nums[1][i] = r_funcs->Draw_PicFromWad (va ("anum_%i", i)); - } - - sb_nums[0][10] = r_funcs->Draw_PicFromWad ("num_minus"); - sb_nums[1][10] = r_funcs->Draw_PicFromWad ("anum_minus"); - - sb_colon = r_funcs->Draw_PicFromWad ("num_colon"); - sb_slash = r_funcs->Draw_PicFromWad ("num_slash"); - - sb_weapons[0][0] = r_funcs->Draw_PicFromWad ("inv_shotgun"); - sb_weapons[0][1] = r_funcs->Draw_PicFromWad ("inv_sshotgun"); - sb_weapons[0][2] = r_funcs->Draw_PicFromWad ("inv_nailgun"); - sb_weapons[0][3] = r_funcs->Draw_PicFromWad ("inv_snailgun"); - sb_weapons[0][4] = r_funcs->Draw_PicFromWad ("inv_rlaunch"); - sb_weapons[0][5] = r_funcs->Draw_PicFromWad ("inv_srlaunch"); - sb_weapons[0][6] = r_funcs->Draw_PicFromWad ("inv_lightng"); - - sb_weapons[1][0] = r_funcs->Draw_PicFromWad ("inv2_shotgun"); - sb_weapons[1][1] = r_funcs->Draw_PicFromWad ("inv2_sshotgun"); - sb_weapons[1][2] = r_funcs->Draw_PicFromWad ("inv2_nailgun"); - sb_weapons[1][3] = r_funcs->Draw_PicFromWad ("inv2_snailgun"); - sb_weapons[1][4] = r_funcs->Draw_PicFromWad ("inv2_rlaunch"); - sb_weapons[1][5] = r_funcs->Draw_PicFromWad ("inv2_srlaunch"); - sb_weapons[1][6] = r_funcs->Draw_PicFromWad ("inv2_lightng"); - - for (i = 0; i < 5; i++) { - sb_weapons[2 + i][0] = - r_funcs->Draw_PicFromWad (va ("inva%i_shotgun", i + 1)); - sb_weapons[2 + i][1] = - r_funcs->Draw_PicFromWad (va ("inva%i_sshotgun", i + 1)); - sb_weapons[2 + i][2] = - r_funcs->Draw_PicFromWad (va ("inva%i_nailgun", i + 1)); - sb_weapons[2 + i][3] = - r_funcs->Draw_PicFromWad (va ("inva%i_snailgun", i + 1)); - sb_weapons[2 + i][4] = - r_funcs->Draw_PicFromWad (va ("inva%i_rlaunch", i + 1)); - sb_weapons[2 + i][5] = - r_funcs->Draw_PicFromWad (va ("inva%i_srlaunch", i + 1)); - sb_weapons[2 + i][6] = - r_funcs->Draw_PicFromWad (va ("inva%i_lightng", i + 1)); - } - - sb_ammo[0] = r_funcs->Draw_PicFromWad ("sb_shells"); - sb_ammo[1] = r_funcs->Draw_PicFromWad ("sb_nails"); - sb_ammo[2] = r_funcs->Draw_PicFromWad ("sb_rocket"); - sb_ammo[3] = r_funcs->Draw_PicFromWad ("sb_cells"); - - sb_armor[0] = r_funcs->Draw_PicFromWad ("sb_armor1"); - sb_armor[1] = r_funcs->Draw_PicFromWad ("sb_armor2"); - sb_armor[2] = r_funcs->Draw_PicFromWad ("sb_armor3"); - - sb_items[0] = r_funcs->Draw_PicFromWad ("sb_key1"); - sb_items[1] = r_funcs->Draw_PicFromWad ("sb_key2"); - sb_items[2] = r_funcs->Draw_PicFromWad ("sb_invis"); - sb_items[3] = r_funcs->Draw_PicFromWad ("sb_invuln"); - sb_items[4] = r_funcs->Draw_PicFromWad ("sb_suit"); - sb_items[5] = r_funcs->Draw_PicFromWad ("sb_quad"); - - sb_sigil[0] = r_funcs->Draw_PicFromWad ("sb_sigil1"); - sb_sigil[1] = r_funcs->Draw_PicFromWad ("sb_sigil2"); - sb_sigil[2] = r_funcs->Draw_PicFromWad ("sb_sigil3"); - sb_sigil[3] = r_funcs->Draw_PicFromWad ("sb_sigil4"); - - sb_faces[4][0] = r_funcs->Draw_PicFromWad ("face1"); - sb_faces[4][1] = r_funcs->Draw_PicFromWad ("face_p1"); - sb_faces[3][0] = r_funcs->Draw_PicFromWad ("face2"); - sb_faces[3][1] = r_funcs->Draw_PicFromWad ("face_p2"); - sb_faces[2][0] = r_funcs->Draw_PicFromWad ("face3"); - sb_faces[2][1] = r_funcs->Draw_PicFromWad ("face_p3"); - sb_faces[1][0] = r_funcs->Draw_PicFromWad ("face4"); - sb_faces[1][1] = r_funcs->Draw_PicFromWad ("face_p4"); - sb_faces[0][0] = r_funcs->Draw_PicFromWad ("face5"); - sb_faces[0][1] = r_funcs->Draw_PicFromWad ("face_p5"); - - sb_face_invis = r_funcs->Draw_PicFromWad ("face_invis"); - sb_face_invuln = r_funcs->Draw_PicFromWad ("face_invul2"); - sb_face_invis_invuln = r_funcs->Draw_PicFromWad ("face_inv2"); - sb_face_quad = r_funcs->Draw_PicFromWad ("face_quad"); - - Cmd_AddCommand ("+showscores", Sbar_ShowScores, - "Display information on everyone playing"); - Cmd_AddCommand ("-showscores", Sbar_DontShowScores, - "Stop displaying information on everyone playing"); - - sb_sbar = r_funcs->Draw_PicFromWad ("sbar"); - sb_ibar = r_funcs->Draw_PicFromWad ("ibar"); - sb_scorebar = r_funcs->Draw_PicFromWad ("scorebar"); - - // MED 01/04/97 added new hipnotic weapons - if (!strcmp (qfs_gamedir->hudtype, "hipnotic")) { - hsb_weapons[0][0] = r_funcs->Draw_PicFromWad ("inv_laser"); - hsb_weapons[0][1] = r_funcs->Draw_PicFromWad ("inv_mjolnir"); - hsb_weapons[0][2] = r_funcs->Draw_PicFromWad ("inv_gren_prox"); - hsb_weapons[0][3] = r_funcs->Draw_PicFromWad ("inv_prox_gren"); - hsb_weapons[0][4] = r_funcs->Draw_PicFromWad ("inv_prox"); - - hsb_weapons[1][0] = r_funcs->Draw_PicFromWad ("inv2_laser"); - hsb_weapons[1][1] = r_funcs->Draw_PicFromWad ("inv2_mjolnir"); - hsb_weapons[1][2] = r_funcs->Draw_PicFromWad ("inv2_gren_prox"); - hsb_weapons[1][3] = r_funcs->Draw_PicFromWad ("inv2_prox_gren"); - hsb_weapons[1][4] = r_funcs->Draw_PicFromWad ("inv2_prox"); - - for (i = 0; i < 5; i++) { - hsb_weapons[2 + i][0] = - r_funcs->Draw_PicFromWad (va ("inva%i_laser", i + 1)); - hsb_weapons[2 + i][1] = - r_funcs->Draw_PicFromWad (va ("inva%i_mjolnir", i + 1)); - hsb_weapons[2 + i][2] = - r_funcs->Draw_PicFromWad (va ("inva%i_gren_prox", i + 1)); - hsb_weapons[2 + i][3] = - r_funcs->Draw_PicFromWad (va ("inva%i_prox_gren", i + 1)); - hsb_weapons[2 + i][4] = - r_funcs->Draw_PicFromWad (va ("inva%i_prox", i + 1)); - } - - hsb_items[0] = r_funcs->Draw_PicFromWad ("sb_wsuit"); - hsb_items[1] = r_funcs->Draw_PicFromWad ("sb_eshld"); - } - - // FIXME: MISSIONHUD - if (!strcmp (qfs_gamedir->hudtype, "rogue")) { - rsb_invbar[0] = r_funcs->Draw_PicFromWad ("r_invbar1"); - rsb_invbar[1] = r_funcs->Draw_PicFromWad ("r_invbar2"); - - rsb_weapons[0] = r_funcs->Draw_PicFromWad ("r_lava"); - rsb_weapons[1] = r_funcs->Draw_PicFromWad ("r_superlava"); - rsb_weapons[2] = r_funcs->Draw_PicFromWad ("r_gren"); - rsb_weapons[3] = r_funcs->Draw_PicFromWad ("r_multirock"); - rsb_weapons[4] = r_funcs->Draw_PicFromWad ("r_plasma"); - - rsb_items[0] = r_funcs->Draw_PicFromWad ("r_shield1"); - rsb_items[1] = r_funcs->Draw_PicFromWad ("r_agrav1"); - - // PGM 01/19/97 - team color border - rsb_teambord = r_funcs->Draw_PicFromWad ("r_teambord"); - // PGM 01/19/97 - team color border - - rsb_ammo[0] = r_funcs->Draw_PicFromWad ("r_ammolava"); - rsb_ammo[1] = r_funcs->Draw_PicFromWad ("r_ammomulti"); - rsb_ammo[2] = r_funcs->Draw_PicFromWad ("r_ammoplasma"); - } - - r_data->viewsize_callback = viewsize_f; - hud_sbar = Cvar_Get ("hud_sbar", "0", CVAR_ARCHIVE, hud_sbar_f, - "status bar mode: 0 = hud, 1 = oldstyle"); - hud_swap = Cvar_Get ("hud_swap", "0", CVAR_ARCHIVE, hud_swap_f, - "new HUD on left side?"); - hud_scoreboard_gravity = Cvar_Get ("hud_scoreboard_gravity", "center", - CVAR_ARCHIVE, hud_scoreboard_gravity_f, - "control placement of scoreboard " - "overlay: center, northwest, north, " - "northeast, west, east, southwest, " - "south, southeast"); - scr_centertime = Cvar_Get ("scr_centertime", "2", CVAR_NONE, NULL, "How " - "long in seconds screen hints are displayed"); - scr_printspeed = Cvar_Get ("scr_printspeed", "8", CVAR_NONE, NULL, - "How fast the text is displayed at the end of " - "the single player episodes"); - - // register GIB builtins - GIB_Builtin_Add ("print::center", Sbar_GIB_Print_Center_f); -} diff --git a/nq/source/sv_cl_phys.c b/nq/source/sv_cl_phys.c index accf26af9..373f3f02c 100644 --- a/nq/source/sv_cl_phys.c +++ b/nq/source/sv_cl_phys.c @@ -31,14 +31,13 @@ #include "QF/cvar.h" #include "QF/sys.h" -#include "host.h" -#include "server.h" -#include "sv_progs.h" #include "world.h" -#define sv_frametime host_frametime +#include "nq/include/host.h" +#include "nq/include/server.h" +#include "nq/include/sv_progs.h" -cvar_t *sv_nostep; +#define sv_frametime host_frametime // CLIENT MOVEMENT ============================================================ @@ -62,7 +61,7 @@ SV_CheckStuck (edict_t *ent) VectorCopy (SVvector (ent, origin), org); VectorCopy (SVvector (ent, oldorigin), SVvector (ent, origin)); if (!SV_TestEntityPosition (ent)) { - Sys_MaskPrintf (SYS_DEV, "Unstuck.\n"); + Sys_MaskPrintf (SYS_dev, "Unstuck.\n"); SV_LinkEdict (ent, true); return; } @@ -74,17 +73,17 @@ SV_CheckStuck (edict_t *ent) SVvector (ent, origin)[1] = org[1] + j; SVvector (ent, origin)[2] = org[2] + z; if (!SV_TestEntityPosition (ent)) { - Sys_MaskPrintf (SYS_DEV, "Unstuck.\n"); + Sys_MaskPrintf (SYS_dev, "Unstuck.\n"); SV_LinkEdict (ent, true); return; } } VectorCopy (org, SVvector (ent, origin)); - Sys_MaskPrintf (SYS_DEV, "player is stuck.\n"); + Sys_MaskPrintf (SYS_dev, "player is stuck.\n"); } -static qboolean +static bool SV_CheckWater (edict_t *ent) { int cont; @@ -204,7 +203,7 @@ SV_TryUnstick (edict_t *ent, vec3_t oldvel) if (fabs (oldorg[1] - SVvector (ent, origin)[1]) > 4 || fabs (oldorg[0] - SVvector (ent, origin)[0]) > 4) { -// Sys_MaskPrintf (SYS_DEV, "unstuck!\n"); +// Sys_MaskPrintf (SYS_dev, "unstuck!\n"); return clip; } // go back to the original pos and try again @@ -247,7 +246,7 @@ SV_WalkMove (edict_t *ent) if (SVfloat (ent, movetype) != MOVETYPE_WALK) return; // gibbed by a trigger - if (sv_nostep->int_val) + if (sv_nostep) return; if ((int) SVfloat (sv_player, flags) & FL_WATERJUMP) diff --git a/nq/source/sv_cvar.c b/nq/source/sv_cvar.c index 4d9082c96..ea2bfbfd5 100644 --- a/nq/source/sv_cvar.c +++ b/nq/source/sv_cvar.c @@ -32,14 +32,14 @@ #include "QF/cvar.h" -#include "server.h" +#include "nq/include/server.h" void -Cvar_Info (cvar_t *var) +Cvar_Info (void *data, const cvar_t *cvar) { - if (var->flags & CVAR_SERVERINFO) { + if (cvar->flags & CVAR_SERVERINFO) { if (sv.active) SV_BroadcastPrintf ("\"%s\" changed to \"%s\"\n", - var->name, var->string); + cvar->name, Cvar_VarString (cvar)); } } diff --git a/nq/source/sv_ded.c b/nq/source/sv_ded.c index 37fd93f2b..e37476539 100644 --- a/nq/source/sv_ded.c +++ b/nq/source/sv_ded.c @@ -36,25 +36,40 @@ #include "QF/plugin/vid_render.h" -#include "host.h" -#include "server.h" +#include "client/world.h" + +#include "client/screen.h" +#include "nq/include/client.h" +#include "nq/include/host.h" +#include "nq/include/server.h" client_state_t cl; client_static_t cls; -cvar_t *cl_name; -cvar_t *cl_writecfg; -cvar_t *demo_speed; -cvar_t *chase_active; +worldscene_t cl_world; -int fps_count; -int viewentity; - -vid_render_data_t *r_data; -vid_render_funcs_t *r_funcs; +char *cl_name; +int cl_writecfg; +float demo_speed; +int chase_active; void -Key_KeydestCallback (keydest_callback_t *callback) +CL_PreFrame (void) +{ +} + +void +CL_Frame (void) +{ +} + +void +CL_Init (struct cbuf_s *cbuf) +{ +} + +void +CL_InitCvars (void) { } @@ -64,7 +79,7 @@ CL_SetState (cactive_t state) } void -CL_UpdateScreen (double realtime) +CL_UpdateScreen (struct viewstate_s *) { } @@ -73,11 +88,6 @@ CL_Cmd_ForwardToServer (void) { } -void -CDAudio_Update (void) -{ -} - void CL_Disconnect (void) { @@ -93,28 +103,13 @@ CL_EstablishConnection (const char *host) { } -void -CL_Shutdown () -{ -} - -void -CL_Init (struct cbuf_s *cbuf) -{ -} - -void -CL_InitCvars (void) -{ -} - void CL_NextDemo (void) { } -int -CL_ReadFromServer (void) +__attribute__((const)) int +CL_ReadConfiguration (const char *cfg_name) { return 0; } @@ -128,36 +123,3 @@ void CL_StopPlayback (void) { } - -void -IN_ProcessEvents (void) -{ -} - -void -Key_WriteBindings (QFile *f) -{ -} - -void -S_Update (const vec3_t origin, const vec3_t v_forward, const vec3_t v_right, - const vec3_t v_up, const byte *ambient_sound_level) -{ -} - -void -S_BlockSound (void) -{ -} - -void -S_UnblockSound (void) -{ -} - -plugin_t *console_client_PluginInfo (void); -plugin_t * -console_client_PluginInfo (void) -{ - return 0; -} diff --git a/nq/source/sv_main.c b/nq/source/sv_main.c index 25def54ac..b68af9659 100644 --- a/nq/source/sv_main.c +++ b/nq/source/sv_main.c @@ -29,23 +29,30 @@ #endif #include "QF/cmd.h" +#include "QF/console.h" #include "QF/cvar.h" +#include "QF/gib.h" #include "QF/msg.h" #include "QF/mathlib.h" +#include "QF/quakefs.h" +#include "QF/set.h" #include "QF/sys.h" #include "QF/va.h" +#include "QF/simd/vec4f.h" + #include "compat.h" -#include "host.h" -#include "server.h" -#include "sv_progs.h" #include "world.h" +#include "nq/include/host.h" +#include "nq/include/server.h" +#include "nq/include/sv_progs.h" + server_t sv; server_static_t svs; double sv_frametime; -char localmodels[MAX_MODELS][5]; // inline model names for precache +char localmodels[MAX_MODELS][6]; // inline model names for precache int sv_protocol = PROTOCOL_FITZQUAKE; @@ -83,28 +90,8 @@ SV_Init (void) SV_Progs_Init (); - sv_maxvelocity = Cvar_Get ("sv_maxvelocity", "2000", CVAR_NONE, NULL, - "None"); - sv_gravity = Cvar_Get ("sv_gravity", "800", CVAR_SERVERINFO, Cvar_Info, - "None"); - sv_jump_any = Cvar_Get ("sv_jump_any", "1", CVAR_NONE, NULL, "None"); - sv_friction = Cvar_Get ("sv_friction", "4", CVAR_SERVERINFO, Cvar_Info, - "None"); - //NOTE: the cl/sv clash is deliberate: dedicated server will use the right - //vars, but client/server combo will use the one. - sv_rollspeed = Cvar_Get ("cl_rollspeed", "200", CVAR_NONE, NULL, - "How quickly you straighten out after strafing"); - sv_rollangle = Cvar_Get ("cl_rollangle", "2.0", CVAR_NONE, NULL, - "How much your screen tilts when strafing"); - sv_edgefriction = Cvar_Get ("edgefriction", "2", CVAR_NONE, NULL, "None"); - sv_stopspeed = Cvar_Get ("sv_stopspeed", "100", CVAR_NONE, NULL, "None"); - sv_maxspeed = Cvar_Get ("sv_maxspeed", "320", CVAR_SERVERINFO, Cvar_Info, - "None"); - sv_accelerate = Cvar_Get ("sv_accelerate", "10", CVAR_NONE, NULL, "None"); - sv_idealpitchscale = Cvar_Get ("sv_idealpitchscale", "0.8", CVAR_NONE, - NULL, "None"); - sv_aim = Cvar_Get ("sv_aim", "0.93", CVAR_NONE, NULL, "None"); - sv_nostep = Cvar_Get ("sv_nostep", "0", CVAR_NONE, NULL, "None"); + SV_Physics_Init_Cvars (); + SV_User_Init_Cvars (); Cmd_AddCommand ("sv_protocol", SV_Protocol_f, "set the protocol to be " "used after the next map load"); @@ -252,7 +239,7 @@ SV_SendServerinfo (client_t *client) MSG_WriteLong (&client->message, sv.protocol); MSG_WriteByte (&client->message, svs.maxclients); - if (!coop->int_val && deathmatch->int_val) + if (!coop && deathmatch) MSG_WriteByte (&client->message, GAME_DEATHMATCH); else MSG_WriteByte (&client->message, GAME_COOP); @@ -309,7 +296,7 @@ SV_ConnectClient (int clientnum) client = svs.clients + clientnum; - Sys_MaskPrintf (SYS_DEV, "Client %s connected\n", + Sys_MaskPrintf (SYS_dev, "Client %s connected\n", client->netconnection->address); edictnum = clientnum + 1; @@ -349,7 +336,7 @@ SV_ConnectClient (int clientnum) void SV_CheckForNewClients (void) { - int i; + unsigned i; struct qsocket_s *ret; // check for new connections @@ -387,37 +374,32 @@ SV_ClearDatagram (void) crosses a waterline. */ -int fatbytes; -byte fatpvs[MAX_MAP_LEAFS / 8]; +static set_t *fatpvs; static void -SV_AddToFatPVS (vec3_t org, mnode_t *node) +SV_AddToFatPVS (vec4f_t org, int node_id) { - int i; float d; - byte *pvs; - plane_t *plane; while (1) { // if this is a leaf, accumulate the pvs bits - if (node->contents < 0) { - if (node->contents != CONTENTS_SOLID) { - pvs = Mod_LeafPVS ((mleaf_t *) node, sv.worldmodel); - for (i = 0; i < fatbytes; i++) - fatpvs[i] |= pvs[i]; + if (node_id < 0) { + mleaf_t *leaf = sv.worldmodel->brush.leafs + ~node_id; + if (leaf->contents != CONTENTS_SOLID) { + set_union (fatpvs, Mod_LeafPVS (leaf, sv.worldmodel)); } return; } + mnode_t *node = sv.worldmodel->brush.nodes + node_id; - plane = node->plane; - d = DotProduct (org, plane->normal) - plane->dist; + d = dotf (node->plane, org)[0]; if (d > 8) - node = node->children[0]; + node_id = node->children[0]; else if (d < -8) - node = node->children[1]; + node_id = node->children[1]; else { // go down both SV_AddToFatPVS (org, node->children[0]); - node = node->children[1]; + node_id = node->children[1]; } } } @@ -428,12 +410,15 @@ SV_AddToFatPVS (vec3_t org, mnode_t *node) Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the given point. */ -static byte * -SV_FatPVS (vec3_t org) +static set_t * +SV_FatPVS (vec4f_t org) { - fatbytes = (sv.worldmodel->numleafs + 31) >> 3; - memset (fatpvs, 0, fatbytes); - SV_AddToFatPVS (org, sv.worldmodel->nodes); + if (!fatpvs) { + fatpvs = set_new_size (sv.worldmodel->brush.visleafs); + } + set_expand (fatpvs, sv.worldmodel->brush.visleafs); + set_empty (fatpvs); + SV_AddToFatPVS (org, 0); return fatpvs; } @@ -442,16 +427,17 @@ SV_FatPVS (vec3_t org) static void SV_WriteEntitiesToClient (edict_t *clent, sizebuf_t *msg) { - int bits, e, i; - byte *pvs; + pr_uint_t bits, e; + set_t *pvs; float miss; - vec3_t org; + vec4f_t org; edict_t *ent; entity_state_t *baseline; edict_leaf_t *el; // find the client's PVS VectorAdd (SVvector (clent, origin), SVvector (clent, view_ofs), org); + org[3] = 1; pvs = SV_FatPVS (org); // send over all entities (excpet the client) that touch the pvs @@ -472,7 +458,7 @@ SV_WriteEntitiesToClient (edict_t *clent, sizebuf_t *msg) continue; for (el = SVdata (ent)->leafs; el; el = el->next) { - if (pvs[el->leafnum >> 3] & (1 << (el->leafnum & 7))) + if (set_is_member (pvs, el->leafnum)) break; } @@ -488,7 +474,7 @@ SV_WriteEntitiesToClient (edict_t *clent, sizebuf_t *msg) bits = 0; - for (i = 0; i < 3; i++) { + for (int i = 0; i < 3; i++) { miss = SVvector (ent, origin)[i] - baseline->origin[i]; if (miss < -0.1 || miss > 0.1) bits |= U_ORIGIN1 << i; @@ -602,7 +588,7 @@ SV_WriteEntitiesToClient (edict_t *clent, sizebuf_t *msg) static void SV_CleanupEnts (void) { - int e; + pr_uint_t e; edict_t *ent; ent = NEXT_EDICT (&sv_pr_state, sv.edicts); @@ -788,7 +774,7 @@ SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg) MSG_WriteByte (msg, SVdata (ent)->alpha); //for now, weaponalpha = client entity alpha } -static qboolean +static bool SV_SendClientDatagram (client_t *client) { byte buf[MAX_DATAGRAM]; @@ -825,7 +811,7 @@ SV_SendClientDatagram (client_t *client) static void SV_UpdateToReliableMessages (void) { - int i, j; + unsigned i, j; client_t *client; // check for changes to be sent over the reliable streams @@ -883,7 +869,7 @@ SV_SendNop (client_t *client) void SV_SendClientMessages (void) { - int i; + unsigned i; // update frags, names, etc SV_UpdateToReliableMessages (); @@ -963,7 +949,7 @@ SV_ModelIndex (const char *name) static void SV_CreateBaseline (void) { - int entnum; + pr_uint_t entnum; edict_t *svent; entity_state_t *baseline; int bits; @@ -1036,7 +1022,8 @@ SV_CreateBaseline (void) MSG_WriteByte (&sv.signon, baseline->colormap); MSG_WriteByte (&sv.signon, baseline->skinnum); - MSG_WriteCoordAngleV (&sv.signon, baseline->origin, baseline->angles); + MSG_WriteCoordAngleV (&sv.signon, (vec_t*)&baseline->origin,//FIXME + baseline->angles); if (bits & B_ALPHA) MSG_WriteByte (&sv.signon, baseline->alpha); @@ -1062,7 +1049,7 @@ SV_SendReconnect (void) MSG_WriteString (&msg, "reconnect\n"); NET_SendToAll (&msg, 5.0); - if (cls.state != ca_dedicated) + if (!net_is_dedicated) Cmd_ExecuteString ("reconnect\n", src_command); } @@ -1075,7 +1062,7 @@ SV_SendReconnect (void) void SV_SaveSpawnparms (void) { - int i, j; + unsigned i, j; svs.serverflags = *sv_globals.serverflags; @@ -1085,8 +1072,7 @@ SV_SaveSpawnparms (void) continue; // call the progs to get default spawn parms for the new client - *sv_globals.self = - EDICT_TO_PROG (&sv_pr_state, host_client->edict); + *sv_globals.self = EDICT_TO_PROG (&sv_pr_state, host_client->edict); PR_ExecuteProgram (&sv_pr_state, sv_funcs.SetChangeParms); for (j = 0; j < NUM_SPAWN_PARMS; j++) host_client->spawn_parms[j] = sv_globals.parms[j]; @@ -1103,16 +1089,14 @@ SV_SpawnServer (const char *server) { byte *buf; QFile *ent_file; - int i; edict_t *ent; - S_BlockSound (); // let's not have any servers with no name - if (hostname->string[0] == 0) - Cvar_Set (hostname, "UNNAMED"); + if (hostname[0] == 0) + Cvar_Set ("hostname", "UNNAMED"); - Sys_MaskPrintf (SYS_DEV, "SpawnServer: %s\n", server); + Sys_MaskPrintf (SYS_dev, "SpawnServer: %s\n", server); svs.changelevel_issued = false; // now safe to issue another svs.phys_client = SV_Physics_Client; @@ -1122,18 +1106,18 @@ SV_SpawnServer (const char *server) } // make cvars consistant - if (coop->int_val) - Cvar_SetValue (deathmatch, 0); - current_skill = skill->int_val; + if (coop) + deathmatch = 0; + current_skill = skill; if (current_skill < 0) current_skill = 0; if (current_skill > 3) current_skill = 3; - Cvar_SetValue (skill, (float) current_skill); + skill = current_skill; // set up the new server - Host_ClearMemory (); + Host_SpawnServer (); memset (&sv, 0, sizeof (sv)); @@ -1142,7 +1126,7 @@ SV_SpawnServer (const char *server) sv.protocol = sv_protocol; // load progs to get entity field count - sv.max_edicts = bound (MIN_EDICTS, max_edicts->int_val, MAX_EDICTS); + sv.max_edicts = bound (MIN_EDICTS, max_edicts, MAX_EDICTS); SV_LoadProgs (); SV_FreeAllEdictLeafs (); @@ -1160,7 +1144,7 @@ SV_SpawnServer (const char *server) // leave slots at start for only clients sv.num_edicts = svs.maxclients + 1; - for (i = 0; i < svs.maxclients; i++) { + for (unsigned i = 0; i < svs.maxclients; i++) { ent = EDICT_NUM (&sv_pr_state, i + 1); svs.clients[i].edict = ent; } @@ -1176,7 +1160,6 @@ SV_SpawnServer (const char *server) if (!sv.worldmodel) { Sys_Printf ("Couldn't spawn server %s\n", sv.modelname); sv.active = false; - S_UnblockSound (); return; } sv.models[1] = sv.worldmodel; @@ -1188,24 +1171,24 @@ SV_SpawnServer (const char *server) sv.model_precache[0] = sv_pr_state.pr_strings; sv.model_precache[1] = sv.modelname; - for (i = 1; i < sv.worldmodel->numsubmodels; i++) { + for (unsigned i = 1; i < sv.worldmodel->brush.numsubmodels; i++) { sv.model_precache[1 + i] = localmodels[i]; sv.models[i + 1] = Mod_ForName (localmodels[i], false); } // load the rest of the entities ent = EDICT_NUM (&sv_pr_state, 0); - memset (&ent->v, 0, sv_pr_state.progs->entityfields * 4); + memset (&E_fld (ent, 0), 0, sv_pr_state.progs->entityfields * 4); ent->free = false; - SVstring (ent, model) = PR_SetString (&sv_pr_state, sv.worldmodel->name); + SVstring (ent, model) = PR_SetString (&sv_pr_state, sv.worldmodel->path); SVfloat (ent, modelindex) = 1; // world model SVfloat (ent, solid) = SOLID_BSP; SVfloat (ent, movetype) = MOVETYPE_PUSH; - if (coop->int_val) - *sv_globals.coop = coop->int_val; + if (coop) + *sv_globals.coop = coop; else - *sv_globals.deathmatch = deathmatch->int_val; + *sv_globals.deathmatch = deathmatch; *sv_globals.mapname = PR_SetString (&sv_pr_state, sv.name); @@ -1213,13 +1196,13 @@ SV_SpawnServer (const char *server) *sv_globals.serverflags = svs.serverflags; *sv_globals.time = sv.time; - ent_file = QFS_VOpenFile (va ("maps/%s.ent", server), 0, + ent_file = QFS_VOpenFile (va (0, "maps/%s.ent", server), 0, sv.worldmodel->vpath); if ((buf = QFS_LoadFile (ent_file, 0))) { ED_LoadFromFile (&sv_pr_state, (char *) buf); free (buf); } else { - ED_LoadFromFile (&sv_pr_state, sv.worldmodel->entities); + ED_LoadFromFile (&sv_pr_state, sv.worldmodel->brush.entities); } sv.active = true; @@ -1242,11 +1225,44 @@ SV_SpawnServer (const char *server) sv.signon.cursize); // send serverinfo to all connected clients - for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, - host_client++) - if (host_client->active) + for (unsigned i = 0; i < svs.maxclients; i++) { + host_client = svs.clients + i; + if (host_client->active) { SV_SendServerinfo (host_client); + } + } - Sys_MaskPrintf (SYS_DEV, "Server spawned.\n"); - S_UnblockSound (); + Sys_MaskPrintf (SYS_dev, "Server spawned.\n"); +} + +void +SV_Frame (void) +{ + if (net_is_dedicated) { + Con_ProcessInput (); + + GIB_Thread_Execute (); + cmd_source = src_command; + Cbuf_Execute_Stack (host_cbuf); + } + + *sv_globals.frametime = sv_frametime = host_frametime; + + // set the time and clear the general datagram + SV_ClearDatagram (); + + SV_CheckForNewClients (); + + // read client messages + SV_RunClients (); + + // move things around and think + // always pause in single player if in console or menus + if (!sv.paused && (svs.maxclients > 1 || host_in_game)) { + SV_Physics (); + sv.time += host_frametime; + } + + // send all messages to the clients + SV_SendClientMessages (); } diff --git a/nq/source/sv_move.c b/nq/source/sv_move.c index 013e4d48e..229307b57 100644 --- a/nq/source/sv_move.c +++ b/nq/source/sv_move.c @@ -28,10 +28,11 @@ # include "config.h" #endif -#include "server.h" -#include "sv_progs.h" #include "world.h" +#include "nq/include/server.h" +#include "nq/include/sv_progs.h" + #define STEPSIZE 18 int c_yes, c_no; @@ -43,7 +44,7 @@ int c_yes, c_no; Returns false if any part of the bottom of the entity is off an edge that is not a staircase. */ -qboolean +bool SV_CheckBottom (edict_t *ent) { float mid, bottom; @@ -112,8 +113,8 @@ SV_CheckBottom (edict_t *ent) possible, no move is done, false is returned, and pr_global_struct->trace_normal is set to the normal of the blocking wall */ -qboolean -SV_movestep (edict_t *ent, const vec3_t move, qboolean relink) +bool +SV_movestep (edict_t *ent, const vec3_t move, bool relink) { edict_t *enemy; float dz; @@ -221,14 +222,14 @@ SV_movestep (edict_t *ent, const vec3_t move, qboolean relink) Turns to the movement direction, and walks the current distance if facing it. */ -static qboolean +static bool SV_StepDirection (edict_t *ent, float yaw, float dist) { float delta; vec3_t move, oldorigin; SVfloat (ent, ideal_yaw) = yaw; - PF_changeyaw (&sv_pr_state); + PF_changeyaw (&sv_pr_state, 0); yaw = yaw * M_PI * 2 / 360; move[0] = cos (yaw) * dist; @@ -293,7 +294,7 @@ SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist) return; } // try other directions - if (((rand () & 3) & 1) || abs (deltay) > abs (deltax)) { + if (((rand () & 3) & 1) || fabsf (deltay) > fabsf (deltax)) { tdir = d[1]; d[1] = d[2]; d[2] = tdir; @@ -330,7 +331,7 @@ SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist) SV_FixCheckBottom (actor); } -static qboolean +static bool SV_CloseEnough (edict_t *ent, edict_t *goal, float dist) { int i; @@ -345,7 +346,7 @@ SV_CloseEnough (edict_t *ent, edict_t *goal, float dist) } void -SV_MoveToGoal (progs_t *pr) +SV_MoveToGoal (progs_t *pr, void *data) { edict_t *ent, *goal; float dist; diff --git a/nq/source/sv_phys.c b/nq/source/sv_phys.c index 795fc0f6d..ed26fcad5 100644 --- a/nq/source/sv_phys.c +++ b/nq/source/sv_phys.c @@ -31,10 +31,11 @@ #include "QF/cvar.h" #include "QF/sys.h" -#include "server.h" -#include "sv_progs.h" #include "world.h" +#include "nq/include/server.h" +#include "nq/include/sv_progs.h" + /* pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal @@ -53,11 +54,52 @@ solid_edge items clip against only bsp models. */ -cvar_t *sv_friction; -cvar_t *sv_gravity; -cvar_t *sv_jump_any; -cvar_t *sv_maxvelocity; -cvar_t *sv_stopspeed; +float sv_friction; +static cvar_t sv_friction_cvar = { + .name = "sv_friction", + .description = + "Sets the friction value for the players", + .default_value = "4", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_float, .value = &sv_friction }, +}; +float sv_gravity; +static cvar_t sv_gravity_cvar = { + .name = "sv_gravity", + .description = + "Sets the global value for the amount of gravity", + .default_value = "800", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_float, .value = &sv_gravity }, +}; +int sv_jump_any; +static cvar_t sv_jump_any_cvar = { + .name = "sv_jump_any", + .description = + "None", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_jump_any }, +}; +float sv_maxvelocity; +static cvar_t sv_maxvelocity_cvar = { + .name = "sv_maxvelocity", + .description = + "Sets the maximum velocity an object can travel", + .default_value = "2000", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_maxvelocity }, +}; +float sv_stopspeed; +static cvar_t sv_stopspeed_cvar = { + .name = "sv_stopspeed", + .description = + "Sets the value that determines how fast the player should come to a " + "complete stop", + .default_value = "100", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_stopspeed }, +}; #define MOVE_EPSILON 0.01 #if 0 @@ -87,18 +129,17 @@ void SV_CheckVelocity (edict_t *ent) { float wishspeed; -// int i; // bound velocity #if 0 - for (i = 0; i < 3; i++) { - if (IS_NAN (SVvector (ent, velocity)[i])) { + for (int i = 0; i < 3; i++) { + if (isnan (SVvector (ent, velocity)[i])) { Sys_Printf ("Got a NaN velocity on %s\n", PR_GetString (&sv_pr_state, SVstring (ent, classname))); SVvector (ent, velocity)[i] = 0; } - if (IS_NAN (SVvector (ent, origin)[i])) { + if (isnan (SVvector (ent, origin)[i])) { Sys_Printf ("Got a NaN origin on %s\n", PR_GetString (&sv_pr_state, SVstring (ent, classname))); @@ -107,8 +148,8 @@ SV_CheckVelocity (edict_t *ent) } #endif wishspeed = VectorLength (SVvector (ent, velocity)); - if (wishspeed > sv_maxvelocity->value) { - VectorScale (SVvector (ent, velocity), sv_maxvelocity->value / + if (wishspeed > sv_maxvelocity) { + VectorScale (SVvector (ent, velocity), sv_maxvelocity / wishspeed, SVvector (ent, velocity)); } } @@ -121,7 +162,7 @@ SV_CheckVelocity (edict_t *ent) in a frame. Not used for pushmove objects, because they must be exact. Returns false if the entity removed itself. */ -qboolean +bool SV_RunThink (edict_t *ent) { float thinktime; @@ -208,7 +249,7 @@ SV_EntCanSupportJump (edict_t *ent) int solid = SVfloat (ent, solid); if (solid == SOLID_BSP) return 1; - if (!sv_jump_any->int_val) + if (!sv_jump_any) return 0; if (solid == SOLID_NOT || solid == SOLID_SLIDEBOX) return 0; @@ -352,7 +393,7 @@ SV_AddGravity (edict_t *ent) ent_grav = SVfloat (ent, gravity); else ent_grav = 1.0; - SVvector (ent, velocity)[2] -= ent_grav * sv_gravity->value * sv_frametime; + SVvector (ent, velocity)[2] -= ent_grav * sv_gravity * sv_frametime; SVdata (ent)->add_grav = true; } @@ -365,7 +406,7 @@ SV_FinishGravity (edict_t *ent, vec3_t move) ent_grav = SVfloat (ent, gravity); else ent_grav = 1.0; - ent_grav *= sv_gravity->value; + ent_grav *= sv_gravity; move[2] += ent_grav * sv_frametime * sv_frametime / 2; } @@ -409,11 +450,11 @@ SV_PushEntity (edict_t *ent, vec3_t push) return trace; } -static qboolean +static bool SV_Push (edict_t *pusher, const vec3_t tmove, const vec3_t amove) { float solid_save; - int num_moved, i, e; + int num_moved, i; edict_t *check, *block; edict_t **moved_edict; vec3_t move, org, org2; @@ -422,7 +463,7 @@ SV_Push (edict_t *pusher, const vec3_t tmove, const vec3_t amove) vec3_t forward = {1, 0, 0}; vec3_t left = {0, 1, 0}; vec3_t up = {0, 0, 1}; - int mark; + size_t mark; int c_flags, c_movetype, c_groundentity, c_solid; vec_t *c_absmin, *c_absmax, *c_origin, *c_angles, *c_mins, *c_maxs; vec_t *p_origin, *p_angles; @@ -447,14 +488,14 @@ SV_Push (edict_t *pusher, const vec3_t tmove, const vec3_t amove) VectorAdd (p_angles, amove, p_angles); SV_LinkEdict (pusher, false); - mark = Hunk_LowMark (); - moved_edict = Hunk_Alloc (sv.num_edicts * sizeof (edict_t *)); - moved_from = Hunk_Alloc (sv.num_edicts * sizeof (vec_t)); + mark = Hunk_LowMark (0); + moved_edict = Hunk_Alloc (0, sv.num_edicts * sizeof (edict_t *)); + moved_from = Hunk_Alloc (0, sv.num_edicts * sizeof (vec_t)); // see if any solid entities are inside the final position num_moved = 0; check = NEXT_EDICT (&sv_pr_state, sv.edicts); - for (e = 1; e < sv.num_edicts; + for (unsigned e = 1; e < sv.num_edicts; e++, check = NEXT_EDICT (&sv_pr_state, check)) { if (check->free) continue; @@ -483,8 +524,8 @@ SV_Push (edict_t *pusher, const vec3_t tmove, const vec3_t amove) // entity? c_absmin = SVvector (check, absmin); c_absmax = SVvector (check, absmax); - if (VectorCompCompare (c_absmin, >=, maxs) - || VectorCompCompare (c_absmax, <=, mins)) + if (VectorCompCompareAll (c_absmin, >=, maxs) + || VectorCompCompareAll (c_absmax, <=, mins)) continue; if (!SV_TestEntityPosition (check)) @@ -549,10 +590,10 @@ SV_Push (edict_t *pusher, const vec3_t tmove, const vec3_t amove) VectorSubtract (m_angles, amove, m_angles); SV_LinkEdict (moved_edict[i], false); } - Hunk_FreeToLowMark (mark); + Hunk_FreeToLowMark (0, mark); return false; } - Hunk_FreeToLowMark (mark); + Hunk_FreeToLowMark (0, mark); return true; } @@ -764,11 +805,11 @@ SV_Physics_Toss (edict_t *ent) static void SV_Physics_Step (edict_t *ent) { - qboolean hitsound; + bool hitsound; // freefall if not on ground if (!((int) SVfloat (ent, flags) & (FL_ONGROUND | FL_FLY | FL_SWIM))) { - if (SVvector (ent, velocity)[2] < sv_gravity->value * -0.1) + if (SVvector (ent, velocity)[2] < sv_gravity * -0.1) hitsound = true; else hitsound = false; @@ -853,14 +894,14 @@ void SV_Physics (void) { edict_t *ent; - int i; SV_ProgStartFrame (); // treat each object in turn // even the world gets a chance to think ent = sv.edicts; - for (i = 0; i < sv.num_edicts; i++, ent = NEXT_EDICT (&sv_pr_state, ent)) { + for (unsigned i = 0; i < sv.num_edicts; + i++, ent = NEXT_EDICT (&sv_pr_state, ent)) { if (ent->free) continue; @@ -889,3 +930,13 @@ SV_Physics (void) PR_ExecuteProgram (&sv_pr_state, sv_funcs.EndFrame); } } + +void +SV_Physics_Init_Cvars (void) +{ + Cvar_Register (&sv_friction_cvar, Cvar_Info, &sv_friction); + Cvar_Register (&sv_gravity_cvar, Cvar_Info, &sv_gravity); + Cvar_Register (&sv_jump_any_cvar, 0, 0); + Cvar_Register (&sv_maxvelocity_cvar, 0, 0); + Cvar_Register (&sv_stopspeed_cvar, 0, 0); +} diff --git a/nq/source/sv_pr_cmds.c b/nq/source/sv_pr_cmds.c index c95669eb4..63c6fd3f0 100644 --- a/nq/source/sv_pr_cmds.c +++ b/nq/source/sv_pr_cmds.c @@ -42,15 +42,17 @@ #include "QF/cvar.h" #include "QF/msg.h" #include "QF/ruamoko.h" +#include "QF/set.h" #include "QF/sys.h" #include "QF/va.h" #include "compat.h" -#include "host.h" -#include "server.h" -#include "sv_progs.h" #include "world.h" +#include "nq/include/host.h" +#include "nq/include/server.h" +#include "nq/include/sv_progs.h" + /* BUILT-IN FUNCTIONS */ @@ -64,16 +66,16 @@ // void (string e) error */ static void -PF_error (progs_t *pr) +PF_error (progs_t *pr, void *data) { const char *s; edict_t *ed; - s = PF_VarString (pr, 0); + s = PF_VarString (pr, 0, 1); Sys_Printf ("======SERVER ERROR in %s:\n%s\n", - PR_GetString (pr, pr->pr_xfunction->descriptor->s_name), s); + PR_GetString (pr, pr->pr_xfunction->descriptor->name), s); ed = PROG_TO_EDICT (pr, *sv_globals.self); - ED_Print (pr, ed); + ED_Print (pr, ed, 0); Host_Error ("Program error"); } @@ -88,16 +90,16 @@ PF_error (progs_t *pr) // void (string e) objerror */ static void -PF_objerror (progs_t *pr) +PF_objerror (progs_t *pr, void *data) { const char *s; edict_t *ed; - s = PF_VarString (pr, 0); + s = PF_VarString (pr, 0, 1); Sys_Printf ("======OBJECT ERROR in %s:\n%s\n", - PR_GetString (pr, pr->pr_xfunction->descriptor->s_name), s); + PR_GetString (pr, pr->pr_xfunction->descriptor->name), s); ed = PROG_TO_EDICT (pr, *sv_globals.self); - ED_Print (pr, ed); + ED_Print (pr, ed, 0); ED_Free (pr, ed); Host_Error ("Program error"); @@ -110,7 +112,7 @@ PF_objerror (progs_t *pr) void (vector angles) makevectors */ static void -PF_makevectors (progs_t *pr) +PF_makevectors (progs_t *pr, void *data) { AngleVectors (P_VECTOR (pr, 0), *sv_globals.v_forward, *sv_globals.v_right, *sv_globals.v_up); @@ -129,7 +131,7 @@ PF_makevectors (progs_t *pr) // void (entity e, vector o) setorigin */ static void -PF_setorigin (progs_t *pr) +PF_setorigin (progs_t *pr, void *data) { edict_t *e; float *org; @@ -142,7 +144,7 @@ PF_setorigin (progs_t *pr) static void SetMinMaxSize (progs_t *pr, edict_t *e, const vec3_t min, const vec3_t max, - qboolean rotate) + bool rotate) { float a; float bounds[2][3]; @@ -219,7 +221,7 @@ SetMinMaxSize (progs_t *pr, edict_t *e, const vec3_t min, const vec3_t max, // void (entity e, vector min, vector max) setsize */ static void -PF_setsize (progs_t *pr) +PF_setsize (progs_t *pr, void *data) { edict_t *e; float *min, *max; @@ -237,7 +239,7 @@ PF_setsize (progs_t *pr) // void (entity e, string m) setmodel */ static void -PF_setmodel (progs_t *pr) +PF_setmodel (progs_t *pr, void *data) { edict_t *e; const char *m, **check; @@ -281,11 +283,11 @@ PF_setmodel (progs_t *pr) // void (string s) bprint */ static void -PF_bprint (progs_t *pr) +PF_bprint (progs_t *pr, void *data) { const char *s; - s = PF_VarString (pr, 0); + s = PF_VarString (pr, 0, 1); SV_BroadcastPrintf ("%s", s); } @@ -298,14 +300,14 @@ PF_bprint (progs_t *pr) // void (entity client, string s) sprint */ static void -PF_sprint (progs_t *pr) +PF_sprint (progs_t *pr, void *data) { const char *s; client_t *client; - int entnum; + unsigned entnum; entnum = P_EDICTNUM (pr, 0); - s = PF_VarString (pr, 1); + s = PF_VarString (pr, 1, 2); if (entnum < 1 || entnum > svs.maxclients) { Sys_Printf ("tried to sprint to a non-client\n"); @@ -327,14 +329,14 @@ PF_sprint (progs_t *pr) // void (...) centerprint */ static void -PF_centerprint (progs_t *pr) +PF_centerprint (progs_t *pr, void *data) { const char *s; client_t *cl; - int entnum; + unsigned entnum; entnum = P_EDICTNUM (pr, 0); - s = PF_VarString (pr, 1); + s = PF_VarString (pr, 1, 2); if (entnum < 1 || entnum > svs.maxclients) { Sys_Printf ("tried to sprint to a non-client\n"); @@ -349,7 +351,7 @@ PF_centerprint (progs_t *pr) // void (vector o, vector d, float color, float count) particle static void -PF_particle (progs_t *pr) +PF_particle (progs_t *pr, void *data) { float *org, *dir; float color; @@ -367,7 +369,7 @@ PF_particle (progs_t *pr) // void (vector pos, string samp, float vol, float atten) ambientsound */ static void -PF_ambientsound (progs_t *pr) +PF_ambientsound (progs_t *pr, void *data) { const char **check; const char *samp; @@ -423,7 +425,7 @@ PF_ambientsound (progs_t *pr) // void (entity e, float chan, string samp) sound */ static void -PF_sound (progs_t *pr) +PF_sound (progs_t *pr, void *data) { const char *sample; edict_t *entity; @@ -457,7 +459,7 @@ PF_sound (progs_t *pr) // float (vector v1, vector v2, float tryents) traceline */ static void -PF_traceline (progs_t *pr) +PF_traceline (progs_t *pr, void *data) { float *v1, *v2; edict_t *ent; @@ -495,7 +497,7 @@ PF_traceline (progs_t *pr) redundant. */ static void -PF_tracebox (progs_t *pr) +PF_tracebox (progs_t *pr, void *data) { edict_t *ent; float *start, *end, *mins, *maxs; @@ -534,20 +536,18 @@ PF_tracebox (progs_t *pr) scalar checkpos (entity, vector) */ static void __attribute__ ((used)) -PF_checkpos (progs_t *pr) +PF_checkpos (progs_t *pr, void *data) { } -byte checkpvs[MAX_MAP_LEAFS / 8]; +static set_t *checkpvs; -static int -PF_newcheckclient (progs_t *pr, int check) +static unsigned +PF_newcheckclient (progs_t *pr, unsigned check) { - byte *pvs; edict_t *ent; - int i; + unsigned i; mleaf_t *leaf; - vec3_t org; // cycle to the next one if (check < 1) @@ -581,10 +581,14 @@ PF_newcheckclient (progs_t *pr, int check) } // get the PVS for the entity + vec4f_t org; VectorAdd (SVvector (ent, origin), SVvector (ent, view_ofs), org); + org[3] = 1; leaf = Mod_PointInLeaf (org, sv.worldmodel); - pvs = Mod_LeafPVS (leaf, sv.worldmodel); - memcpy (checkpvs, pvs, (sv.worldmodel->numleafs + 7) >> 3); + if (!checkpvs) { + checkpvs = set_new_size (sv.worldmodel->brush.visleafs); + } + set_assign (checkpvs, Mod_LeafPVS (leaf, sv.worldmodel)); return i; } @@ -607,12 +611,11 @@ int c_invis, c_notvis; // entity () clientlist */ static void -PF_checkclient (progs_t *pr) +PF_checkclient (progs_t *pr, void *data) { edict_t *ent, *self; int l; mleaf_t *leaf; - vec3_t view; // find a new check if on a new frame if (sv.time - sv.lastchecktime >= 0.1) { @@ -627,10 +630,12 @@ PF_checkclient (progs_t *pr) } // if current entity can't possibly see the check entity, return 0 self = PROG_TO_EDICT (pr, *sv_globals.self); + vec4f_t view; VectorAdd (SVvector (self, origin), SVvector (self, view_ofs), view); + view[3] = 1; leaf = Mod_PointInLeaf (view, sv.worldmodel); - l = (leaf - sv.worldmodel->leafs) - 1; - if ((l < 0) || !(checkpvs[l >> 3] & (1 << (l & 7)))) { + l = (leaf - sv.worldmodel->brush.leafs) - 1; + if (!set_is_member (checkpvs, l)) { c_notvis++; RETURN_EDICT (pr, sv.edicts); return; @@ -649,11 +654,11 @@ PF_checkclient (progs_t *pr) // void (entity client, string s) stuffcmd */ static void -PF_stuffcmd (progs_t *pr) +PF_stuffcmd (progs_t *pr, void *data) { const char *str; client_t *old; - int entnum; + pr_uint_t entnum; entnum = P_EDICTNUM (pr, 0); if (entnum < 1 || entnum > svs.maxclients) @@ -675,7 +680,7 @@ PF_stuffcmd (progs_t *pr) // void (string s) localcmd */ static void -PF_localcmd (progs_t *pr) +PF_localcmd (progs_t *pr, void *data) { const char *str; @@ -692,12 +697,12 @@ PF_localcmd (progs_t *pr) // entity (vector org, float rad) findradius */ static void -PF_findradius (progs_t *pr) +PF_findradius (progs_t *pr, void *data) { edict_t *ent, *chain; float rsqr; vec_t *emins, *emaxs, *org; - int i, j; + pr_uint_t i; vec3_t eorg; chain = (edict_t *) sv.edicts; @@ -714,7 +719,7 @@ PF_findradius (progs_t *pr) continue; emins = SVvector (ent, absmin); emaxs = SVvector (ent, absmax); - for (j = 0; j < 3; j++) + for (int j = 0; j < 3; j++) eorg[j] = org[j] - 0.5 * (emins[j] + emaxs[j]); if (DotProduct (eorg, eorg) > rsqr) continue; @@ -728,7 +733,7 @@ PF_findradius (progs_t *pr) // entity () spawn static void -PF_Spawn (progs_t *pr) +PF_spawn (progs_t *pr, void *data) { edict_t *ed; @@ -738,7 +743,7 @@ PF_Spawn (progs_t *pr) // void (entity e) remove static void -PF_Remove (progs_t *pr) +PF_remove (progs_t *pr, void *data) { edict_t *ed; @@ -766,7 +771,7 @@ do_precache (progs_t *pr, const char **cache, int max, const char *name, PR_CheckEmptyString (pr, name); - s = Hunk_TempAlloc (strlen (name) + 1); + s = Hunk_TempAlloc (0, strlen (name) + 1); for (i = 0; *name; i++, name++) { int c = (byte) *name; s[i] = tolower (c); @@ -775,10 +780,10 @@ do_precache (progs_t *pr, const char **cache, int max, const char *name, for (i = 0; i < MAX_SOUNDS; i++) { if (!cache[i]) { - char *c = Hunk_Alloc (strlen (s) + 1); + char *c = Hunk_Alloc (0, strlen (s) + 1); strcpy (c, s); cache[i] = c; // blah, const - Sys_MaskPrintf (SYS_DEV, "%s: %3d %s\n", func, i, s); + Sys_MaskPrintf (SYS_dev, "%s: %3d %s\n", func, i, s); return i; } if (!strcmp (cache[i], s)) @@ -790,26 +795,28 @@ do_precache (progs_t *pr, const char **cache, int max, const char *name, // string (string s) precache_file // string (string s) precache_file2 static void -PF_precache_file (progs_t *pr) +PF_precache_file (progs_t *pr, void *data) { // precache_file is used only to copy files with qcc, it does nothing R_INT (pr) = P_INT (pr, 0); } +#define PF_precache_file2 PF_precache_file // void (string s) precache_sound // string (string s) precache_sound2 static void -PF_precache_sound (progs_t *pr) +PF_precache_sound (progs_t *pr, void *data) { do_precache (pr, sv.sound_precache, MAX_SOUNDS, P_GSTRING (pr, 0), "precache_sound"); R_INT (pr) = P_INT (pr, 0); } +#define PF_precache_sound2 PF_precache_sound // void (string s) precache_model // string (string s) precache_model2 static void -PF_precache_model (progs_t *pr) +PF_precache_model (progs_t *pr, void *data) { int ind; const char *mod = P_GSTRING (pr, 0); @@ -818,6 +825,7 @@ PF_precache_model (progs_t *pr) sv.models[ind] = Mod_ForName (mod, true); R_INT (pr) = P_INT (pr, 0); } +#define PF_precache_model2 PF_precache_model /* PF_walkmove @@ -826,7 +834,7 @@ PF_precache_model (progs_t *pr) // float (float yaw, float dist) walkmove */ static void -PF_walkmove (progs_t *pr) +PF_walkmove (progs_t *pr, void *data) { edict_t *ent; float yaw, dist; @@ -864,7 +872,7 @@ PF_walkmove (progs_t *pr) // float () droptofloor */ static void -PF_droptofloor (progs_t *pr) +PF_droptofloor (progs_t *pr, void *data) { edict_t *ent; trace_t trace; @@ -896,11 +904,12 @@ PF_droptofloor (progs_t *pr) // void (float style, string value) lightstyle */ static void -PF_lightstyle (progs_t *pr) +PF_lightstyle (progs_t *pr, void *data) { const char *val; client_t *cl; - int style, j; + int style; + unsigned j; style = P_FLOAT (pr, 0); val = P_GSTRING (pr, 1); @@ -922,7 +931,7 @@ PF_lightstyle (progs_t *pr) // float (entity e) checkbottom static void -PF_checkbottom (progs_t *pr) +PF_checkbottom (progs_t *pr, void *data) { edict_t *ent; @@ -933,7 +942,7 @@ PF_checkbottom (progs_t *pr) // float (vector v) pointcontents static void -PF_pointcontents (progs_t *pr) +PF_pointcontents (progs_t *pr, void *data) { float *v; @@ -942,7 +951,15 @@ PF_pointcontents (progs_t *pr) R_FLOAT (pr) = SV_PointContents (v); } -cvar_t *sv_aim; +float sv_aim; +static cvar_t sv_aim_cvar = { + .name = "sv_aim", + .description = + "None", + .default_value = "0.93", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_aim }, +}; /* PF_aim @@ -952,12 +969,12 @@ cvar_t *sv_aim; // vector (entity e, float speed) aim */ static void -PF_aim (progs_t *pr) +PF_aim (progs_t *pr, void *data) { edict_t *ent, *check, *bestent; float dist, bestdist, speed; float *mins, *maxs, *org; - int i, j; + pr_uint_t i; trace_t tr; vec3_t start, dir, end, bestdir; @@ -973,7 +990,7 @@ PF_aim (progs_t *pr) VectorMultAdd (start, 2048, dir, end); tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent); if (tr.ent && SVfloat (tr.ent, takedamage) == DAMAGE_AIM - && (!teamplay->int_val || SVfloat (ent, team) <= 0 + && (!teamplay || SVfloat (ent, team) <= 0 || SVfloat (ent, team) != SVfloat (tr.ent, team))) { VectorCopy (*sv_globals.v_forward, R_VECTOR (pr)); return; @@ -981,7 +998,7 @@ PF_aim (progs_t *pr) // try all possible entities VectorCopy (dir, bestdir); - bestdist = sv_aim->value; + bestdist = sv_aim; bestent = NULL; check = NEXT_EDICT (pr, sv.edicts); @@ -990,14 +1007,14 @@ PF_aim (progs_t *pr) continue; if (check == ent) continue; - if (teamplay->int_val && SVfloat (ent, team) > 0 + if (teamplay && SVfloat (ent, team) > 0 && SVfloat (ent, team) == SVfloat (check, team)) continue; // don't aim at teammate mins = SVvector (check, mins); maxs = SVvector (check, maxs); org = SVvector (check, origin); - for (j = 0; j < 3; j++) + for (int j = 0; j < 3; j++) end[j] = org[j] + 0.5 * (mins[j] + maxs[j]); VectorSubtract (end, start, dir); VectorNormalize (dir); @@ -1031,7 +1048,7 @@ PF_aim (progs_t *pr) // void () ChangeYaw */ void -PF_changeyaw (progs_t *pr) +PF_changeyaw (progs_t *pr, void *data) { edict_t *ent; float ideal, current, move, speed; @@ -1069,10 +1086,10 @@ PF_changeyaw (progs_t *pr) #define MSG_ALL 2 // reliable to all #define MSG_INIT 3 // write to the init string -static sizebuf_t * +static __attribute__((pure)) sizebuf_t * WriteDest (progs_t *pr) { - int entnum; + pr_uint_t entnum; int dest; edict_t *ent; @@ -1104,62 +1121,76 @@ WriteDest (progs_t *pr) // void (float to, ...) WriteBytes static void -PF_WriteBytes (progs_t *pr) +PF_WriteBytes (progs_t *pr, void *data) { int i, p; + int argc = pr->pr_argc - 1; + pr_type_t **argv = pr->pr_params + 1; sizebuf_t *msg = WriteDest (pr); - for (i = 1; i < pr->pr_argc; i++) { - p = P_FLOAT (pr, i); + if (pr->progs->version == PROG_VERSION) { + __auto_type va_list = &P_PACKED (pr, pr_va_list_t, 1); + argc = va_list->count; + if (argc) { + argv = alloca (argc * sizeof (pr_type_t *)); + for (int i = 0; i < argc; i++) { + argv[i] = &pr->pr_globals[va_list->list + i * 4]; + } + } else { + argv = 0; + } + } + for (i = 0; i < argc; i++) { + p = PR_PTR (float, argv[i]); MSG_WriteByte (msg, p); } } // void (float to, float f) WriteByte static void -PF_WriteByte (progs_t *pr) +PF_WriteByte (progs_t *pr, void *data) { MSG_WriteByte (WriteDest (pr), P_FLOAT (pr, 1)); } // void (float to, float f) WriteChar static void -PF_WriteChar (progs_t *pr) +PF_WriteChar (progs_t *pr, void *data) { MSG_WriteByte (WriteDest (pr), P_FLOAT (pr, 1)); } // void (float to, float f) WriteShort static void -PF_WriteShort (progs_t *pr) +PF_WriteShort (progs_t *pr, void *data) { MSG_WriteShort (WriteDest (pr), P_FLOAT (pr, 1)); } // void (float to, float f) WriteLong static void -PF_WriteLong (progs_t *pr) +PF_WriteLong (progs_t *pr, void *data) { MSG_WriteLong (WriteDest (pr), P_FLOAT (pr, 1)); } // void (float to, float f) WriteAngle static void -PF_WriteAngle (progs_t *pr) +PF_WriteAngle (progs_t *pr, void *data) { MSG_WriteAngle (WriteDest (pr), P_FLOAT (pr, 1)); } // void (float to, float f) WriteCoord static void -PF_WriteCoord (progs_t *pr) +PF_WriteCoord (progs_t *pr, void *data) { MSG_WriteCoord (WriteDest (pr), P_FLOAT (pr, 1)); } // void (float to, vector v) WriteAngleV static void -PF_WriteAngleV (progs_t *pr) +PF_WriteAngleV (progs_t *pr, void *data) { float *ang = P_VECTOR (pr, 1); @@ -1168,7 +1199,7 @@ PF_WriteAngleV (progs_t *pr) // void (float to, vector v) WriteCoordV static void -PF_WriteCoordV (progs_t *pr) +PF_WriteCoordV (progs_t *pr, void *data) { float *coord = P_VECTOR (pr, 1); @@ -1177,21 +1208,21 @@ PF_WriteCoordV (progs_t *pr) // void (float to, string s) WriteString static void -PF_WriteString (progs_t *pr) +PF_WriteString (progs_t *pr, void *data) { MSG_WriteString (WriteDest (pr), P_GSTRING (pr, 1)); } // void (float to, entity s) WriteEntity static void -PF_WriteEntity (progs_t *pr) +PF_WriteEntity (progs_t *pr, void *data) { MSG_WriteShort (WriteDest (pr), P_EDICTNUM (pr, 1)); } // void (entity e) makestatic static void -PF_makestatic (progs_t *pr) +PF_makestatic (progs_t *pr, void *data) { const char *model; edict_t *ent; @@ -1249,11 +1280,11 @@ nosend: // void (entity e) setspawnparms static void -PF_setspawnparms (progs_t *pr) +PF_setspawnparms (progs_t *pr, void *data) { client_t *client; edict_t *ent; - int i; + unsigned i; ent = P_EDICT (pr, 0); i = NUM_FOR_EDICT (pr, ent); @@ -1269,7 +1300,7 @@ PF_setspawnparms (progs_t *pr) // void (string s) changelevel static void -PF_changelevel (progs_t *pr) +PF_changelevel (progs_t *pr, void *data) { const char *s; @@ -1279,12 +1310,12 @@ PF_changelevel (progs_t *pr) svs.changelevel_issued = true; s = P_GSTRING (pr, 0); - Cbuf_AddText (host_cbuf, va ("changelevel %s\n", s)); + Cbuf_AddText (host_cbuf, va (0, "changelevel %s\n", s)); } // entity (entity ent) testentitypos static void -PF_testentitypos (progs_t *pr) +PF_testentitypos (progs_t *pr, void *data) { edict_t *ent = P_EDICT (pr, 0); ent = SV_TestEntityPosition (ent); @@ -1296,7 +1327,7 @@ clip_hull_t *pf_hull_list[MAX_PF_HULLS]; // integer (entity ent, vector point) hullpointcontents static void -PF_hullpointcontents (progs_t *pr) +PF_hullpointcontents (progs_t *pr, void *data) { edict_t *edict = P_EDICT (pr, 0); float *mins = P_VECTOR (pr, 1); @@ -1312,7 +1343,7 @@ PF_hullpointcontents (progs_t *pr) // vector (integer hull, integer max) getboxbounds static void -PF_getboxbounds (progs_t *pr) +PF_getboxbounds (progs_t *pr, void *data) { clip_hull_t *ch; int h = P_INT (pr, 0) - 1; @@ -1329,7 +1360,7 @@ PF_getboxbounds (progs_t *pr) // integer () getboxhull static void -PF_getboxhull (progs_t *pr) +PF_getboxhull (progs_t *pr, void *data) { clip_hull_t *ch = 0; int i; @@ -1354,7 +1385,7 @@ PF_getboxhull (progs_t *pr) // void (integer hull) freeboxhull static void -PF_freeboxhull (progs_t *pr) +PF_freeboxhull (progs_t *pr, void *data) { int h = P_INT (pr, 0) - 1; clip_hull_t *ch; @@ -1384,7 +1415,7 @@ calc_dist (vec3_t p, vec3_t n, vec3_t *offsets) // void (integer hull, vector right, vector forward, vector up, vector mins, vector maxs) rotate_bbox static void -PF_rotate_bbox (progs_t *pr) +PF_rotate_bbox (progs_t *pr, void *data) { clip_hull_t *ch; float l; @@ -1463,94 +1494,101 @@ PF_rotate_bbox (progs_t *pr) // float () checkextension static void -PF_checkextension (progs_t *pr) +PF_checkextension (progs_t *pr, void *data) { R_FLOAT (pr) = 0; // FIXME: make this function actually useful } #define QF (PR_RANGE_QF << PR_RANGE_SHIFT) | +#define bi(x,n,np,params...) {#x, PF_##x, n, np, {params}} +#define p(type) PR_PARAM(type) static builtin_t builtins[] = { - {"makevectors", PF_makevectors, 1}, - {"setorigin", PF_setorigin, 2}, - {"setmodel", PF_setmodel, 3}, - {"setsize", PF_setsize, 4}, + bi(makevectors, 1, 1, p(vector)), + bi(setorigin, 2, 2, p(entity), p(vector)), + bi(setmodel, 3, 2, p(entity), p(string)), + bi(setsize, 4, 3, p(entity), p(vector), p(vector)), - {"sound", PF_sound, 8}, + bi(sound, 8, 3, p(entity), p(float), p(string)), - {"error", PF_error, 10}, - {"objerror", PF_objerror, 11}, - {"spawn", PF_Spawn, 14}, - {"remove", PF_Remove, 15}, - {"traceline", PF_traceline, 16}, - {"checkclient", PF_checkclient, 17}, + bi(error, 10, -1), // (...) + bi(objerror, 11, -1), // (...) + bi(spawn, 14, 0), // (void) + bi(remove, 15, 1, p(entity)), + bi(traceline, 16, 3, p(vector), p(vector), p(float)), + bi(checkclient, 17, 0), // (void) - {"precache_sound", PF_precache_sound, 19}, - {"precache_model", PF_precache_model, 20}, - {"stuffcmd", PF_stuffcmd, 21}, - {"findradius", PF_findradius, 22}, - {"bprint", PF_bprint, 23}, - {"sprint", PF_sprint, 24}, + bi(precache_sound, 19, 1, p(string)), + bi(precache_model, 20, 1, p(string)), + bi(stuffcmd, 21, 2, p(entity), p(string)), + bi(findradius, 22, 2, p(vector), p(float)), + bi(bprint, 23, -1), // (...) + bi(sprint, 24, -2, p(entity)), // (entity, string...) - {"walkmove", PF_walkmove, 32}, + bi(walkmove, 32, 2, p(float), p(float)), - {"droptofloor", PF_droptofloor, 34}, - {"lightstyle", PF_lightstyle, 35}, + bi(droptofloor, 34, 0), // (void) + bi(lightstyle, 35, 2, p(float), p(string)), - {"checkbottom", PF_checkbottom, 40}, - {"pointcontents", PF_pointcontents, 41}, + bi(checkbottom, 40, 1, p(entity)), + bi(pointcontents, 41, 1, p(vector)), - {"aim", PF_aim, 44}, + bi(aim, 44, 2, p(entity), p(float)), - {"localcmd", PF_localcmd, 46}, + bi(localcmd, 46, 1, p(string)), - {"particle", PF_particle, 48}, - {"changeyaw", PF_changeyaw, 49}, + bi(particle, 48, 4, p(vector), p(vector), p(float), p(float)), + bi(changeyaw, 49, 0), // (void) - {"writebyte", PF_WriteByte, 52}, - {"WriteBytes", PF_WriteBytes, -1}, - {"writechar", PF_WriteChar, 53}, - {"writeshort", PF_WriteShort, 54}, - {"writelong", PF_WriteLong, 55}, - {"writecoord", PF_WriteCoord, 56}, - {"writeangle", PF_WriteAngle, 57}, - {"WriteCoordV", PF_WriteCoordV, -1}, - {"WriteAngleV", PF_WriteAngleV, -1}, - {"writestring", PF_WriteString, 58}, - {"writeentity", PF_WriteEntity, 59}, + bi(WriteByte, 52, 2, p(float), p(float)), + bi(WriteBytes, -1, -2, p(float)), // (float, float...) + bi(WriteChar, 53, 2, p(float), p(float)), + bi(WriteShort, 54, 2, p(float), p(float)), + bi(WriteLong, 55, 2, p(float), p(float)), + bi(WriteCoord, 56, 2, p(float), p(float)), + bi(WriteAngle, 57, 2, p(float), p(float)), + bi(WriteCoordV, -1, 2, p(float), p(vector)), + bi(WriteAngleV, -1, 2, p(float), p(vector)), + bi(WriteString, 58, 2, p(float), p(string)), + bi(WriteEntity, 59, 2, p(float), p(entity)), +#define PF_movetogoal SV_MoveToGoal + bi(movetogoal, 67, 0), // (void) +#undef PF_movetogoal + bi(precache_file, 68, 1, p(string)), + bi(makestatic, 69, 1, p(entity)), + bi(changelevel, 70, 1, p(string)), - {"movetogoal", SV_MoveToGoal, 67}, - {"precache_file", PF_precache_file, 68}, - {"makestatic", PF_makestatic, 69}, - {"changelevel", PF_changelevel, 70}, - - {"centerprint", PF_centerprint, 73}, - {"ambientsound", PF_ambientsound, 74}, - {"precache_model2", PF_precache_model, 75}, - {"precache_sound2", PF_precache_sound, 76}, - {"precache_file2", PF_precache_file, 77}, - {"setspawnparms", PF_setspawnparms, 78}, - - {"testentitypos", PF_testentitypos, QF 92}, - {"hullpointcontents", PF_hullpointcontents, QF 93}, - {"getboxbounds", PF_getboxbounds, QF 94}, - {"getboxhull", PF_getboxhull, QF 95}, - {"freeboxhull", PF_freeboxhull, QF 96}, - {"rotate_bbox", PF_rotate_bbox, QF 97}, - {"tracebox", PF_tracebox, QF 98}, - {"checkextension", PF_checkextension, QF 99}, - - {"EntityParseFunction", ED_EntityParseFunction, -1}, + bi(centerprint, 73, -1), // (...) + bi(ambientsound, 74, 4, p(vector), p(string), p(float), p(float)), + bi(precache_model2, 75, 1, p(string)), + bi(precache_sound2, 76, 1, p(string)), + bi(precache_file2, 77, 1, p(string)), + bi(setspawnparms, 78, 1, p(entity)), + bi(testentitypos, QF 92, 1, p(entity)), + bi(hullpointcontents, QF 93, 2, p(entity), p(vector)), + bi(getboxbounds, QF 94, 2, p(int), p(int)), + bi(getboxhull, QF 95, 0), // (void) + bi(freeboxhull, QF 96, 1, p(int)), + bi(rotate_bbox, QF 97, 6, p(int), p(vector), p(vector), p(vector), + p(vector), p(vector)), + bi(tracebox, QF 98, 6, p(vector), p(vector), p(vector), p(vector), + p(float), p(entity)), + bi(checkextension, QF 99, -1, {}), //FIXME correct params? +#define PF_EntityParseFunction ED_EntityParseFunction + bi(EntityParseFunction, -1, 1, p(func)), +#undef PF_EntityParseFunction {0} }; void SV_PR_Cmds_Init () { + Cvar_Register (&sv_aim_cvar, 0, 0); + RUA_Init (&sv_pr_state, 1); PR_Cmds_Init (&sv_pr_state); - PR_RegisterBuiltins (&sv_pr_state, builtins); + PR_RegisterBuiltins (&sv_pr_state, builtins, 0); } diff --git a/nq/source/sv_progs.c b/nq/source/sv_progs.c index 37b915e9c..2637c4992 100644 --- a/nq/source/sv_progs.c +++ b/nq/source/sv_progs.c @@ -41,34 +41,165 @@ #include "QF/sys.h" #include "compat.h" -#include "host.h" -#include "server.h" -#include "sv_progs.h" #include "world.h" +#include "nq/include/host.h" +#include "nq/include/server.h" +#include "nq/include/sv_progs.h" + progs_t sv_pr_state; sv_globals_t sv_globals; sv_funcs_t sv_funcs; sv_fields_t sv_fields; +edict_t sv_edicts[MAX_EDICTS]; sv_data_t sv_data[MAX_EDICTS]; -cvar_t *sv_progs; -cvar_t *sv_progs_zone; -cvar_t *sv_progs_ext; -cvar_t *pr_checkextensions; +char *sv_progs; +static cvar_t sv_progs_cvar = { + .name = "sv_progs", + .description = + "Override the default game progs.", + .default_value = "", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &sv_progs }, +}; +int sv_progs_zone; +static cvar_t sv_progs_zone_cvar = { + .name = "sv_progs_zone", + .description = + "size of the zone for progs in kb", + .default_value = "256", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_progs_zone }, +}; +int sv_progs_stack; +static cvar_t sv_progs_stack_cvar = { + .name = "sv_progs_stack", + .description = + "size of the stack for progs in kb", + .default_value = "256", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_progs_stack }, +}; +char *sv_progs_ext; +static cvar_t sv_progs_ext_cvar = { + .name = "sv_progs_ext", + .description = + "extention mapping to use: none, id, qf", + .default_value = "qf", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &sv_progs_ext }, +}; +float pr_checkextensions; +static cvar_t pr_checkextensions_cvar = { + .name = "pr_checkextensions", + .description = + "indicate the presence of the checkextentions qc function", + .default_value = "1", + .flags = CVAR_ROM, + .value = { .type = &cexpr_float, .value = &pr_checkextensions }, +}; -cvar_t *nomonsters; -cvar_t *gamecfg; -cvar_t *scratch1; -cvar_t *scratch2; -cvar_t *scratch3; -cvar_t *scratch4; -cvar_t *savedgamecfg; -cvar_t *saved1; -cvar_t *saved2; -cvar_t *saved3; -cvar_t *saved4; +float nomonsters; +static cvar_t nomonsters_cvar = { + .name = "nomonsters", + .description = + "No Description", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &nomonsters }, +}; +float gamecfg; +static cvar_t gamecfg_cvar = { + .name = "gamecfg", + .description = + "No Description", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &gamecfg }, +}; +float scratch1; +static cvar_t scratch1_cvar = { + .name = "scratch1", + .description = + "No Description", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &scratch1 }, +}; +float scratch2; +static cvar_t scratch2_cvar = { + .name = "scratch2", + .description = + "No Description", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &scratch2 }, +}; +float scratch3; +static cvar_t scratch3_cvar = { + .name = "scratch3", + .description = + "No Description", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &scratch3 }, +}; +float scratch4; +static cvar_t scratch4_cvar = { + .name = "scratch4", + .description = + "No Description", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &scratch4 }, +}; +float savedgamecfg; +static cvar_t savedgamecfg_cvar = { + .name = "savedgamecfg", + .description = + "No Description", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &savedgamecfg }, +}; +float saved1; +static cvar_t saved1_cvar = { + .name = "saved1", + .description = + "No Description", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &saved1 }, +}; +float saved2; +static cvar_t saved2_cvar = { + .name = "saved2", + .description = + "No Description", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &saved2 }, +}; +float saved3; +static cvar_t saved3_cvar = { + .name = "saved3", + .description = + "No Description", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &saved3 }, +}; +float saved4; +static cvar_t saved4_cvar = { + .name = "saved4", + .description = + "No Description", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &saved4 }, +}; static int sv_range; @@ -90,7 +221,7 @@ static int prune_edict (progs_t *pr, edict_t *ent) { // remove things from different skill levels or deathmatch - if (deathmatch->int_val) { + if (deathmatch) { if (((int) SVfloat (ent, spawnflags) & SPAWNFLAG_NOT_DEATHMATCH)) { return 1; @@ -124,10 +255,17 @@ static void ED_PrintEdict_f (void) { int i; + const char *fieldname = 0; + if (Cmd_Argc () < 2) { + Sys_Printf ("edict num [fieldname]\n"); + return; + } + if (Cmd_Argc () >= 3) { + fieldname = Cmd_Argv (2); + } i = atoi (Cmd_Argv (1)); - Sys_Printf ("\n EDICT %i:\n", i); - ED_PrintNum (&sv_pr_state, i); + ED_PrintNum (&sv_pr_state, i, fieldname); } static void @@ -255,7 +393,6 @@ static sv_def_t nq_fields[] = { {ev_vector, 36, "maxs", &sv_fields.maxs}, {ev_vector, 39, "size", &sv_fields.size}, {ev_func, 42, "touch", &sv_fields.touch}, - {ev_func, 43, "use", &sv_fields.use}, {ev_func, 44, "think", &sv_fields.think}, {ev_func, 45, "blocked", &sv_fields.blocked}, {ev_float, 46, "nextthink", &sv_fields.nextthink}, @@ -273,7 +410,6 @@ static sv_def_t nq_fields[] = { {ev_float, 58, "items", &sv_fields.items}, {ev_float, 59, "takedamage", &sv_fields.takedamage}, {ev_entity, 60, "chain", &sv_fields.chain}, - {ev_float, 61, "deadflag", &sv_fields.deadflag}, {ev_vector, 62, "view_ofs", &sv_fields.view_ofs}, {ev_float, 65, "button0", &sv_fields.button0}, {ev_float, 66, "button1", &sv_fields.button1}, @@ -287,19 +423,14 @@ static sv_def_t nq_fields[] = { {ev_float, 76, "flags", &sv_fields.flags}, {ev_float, 77, "colormap", &sv_fields.colormap}, {ev_float, 78, "team", &sv_fields.team}, - {ev_float, 79, "max_health", &sv_fields.max_health}, {ev_float, 80, "teleport_time", &sv_fields.teleport_time}, - {ev_float, 81, "armortype", &sv_fields.armortype}, {ev_float, 82, "armorvalue", &sv_fields.armorvalue}, {ev_float, 83, "waterlevel", &sv_fields.waterlevel}, {ev_float, 84, "watertype", &sv_fields.watertype}, {ev_float, 85, "ideal_yaw", &sv_fields.ideal_yaw}, {ev_float, 86, "yaw_speed", &sv_fields.yaw_speed}, - {ev_entity, 87, "aiment", &sv_fields.aiment}, {ev_entity, 88, "goalentity", &sv_fields.goalentity}, {ev_float, 89, "spawnflags", &sv_fields.spawnflags}, - {ev_string, 90, "target", &sv_fields.target}, - {ev_string, 91, "targetname", &sv_fields.targetname}, {ev_float, 92, "dmg_take", &sv_fields.dmg_take}, {ev_float, 93, "dmg_save", &sv_fields.dmg_save}, {ev_entity, 94, "dmg_inflictor", &sv_fields.dmg_inflictor}, @@ -307,10 +438,6 @@ static sv_def_t nq_fields[] = { {ev_vector, 96, "movedir", &sv_fields.movedir}, {ev_string, 99, "message", &sv_fields.message}, {ev_float, 100, "sounds", &sv_fields.sounds}, - {ev_string, 101, "noise", &sv_fields.noise}, - {ev_string, 102, "noise1", &sv_fields.noise1}, - {ev_string, 103, "noise2", &sv_fields.noise2}, - {ev_string, 104, "noise3", &sv_fields.noise3}, {ev_void, 0, 0}, }; @@ -325,22 +452,10 @@ static sv_def_t nq_opt_funcs[] = { }; static sv_def_t nq_opt_fields[] = { - {ev_integer, 0, "rotated_bbox", &sv_fields.rotated_bbox}, + {ev_int, 0, "rotated_bbox", &sv_fields.rotated_bbox}, {ev_float, 0, "alpha", &sv_fields.alpha}, {ev_float, 0, "gravity", &sv_fields.gravity}, - // Quake 2 fields? - {ev_float, 0, "dmg", &sv_fields.dmg}, - {ev_float, 0, "dmgtime", &sv_fields.dmgtime}, - {ev_float, 0, "air_finished", &sv_fields.air_finished}, - {ev_float, 0, "pain_finished", &sv_fields.pain_finished}, - {ev_float, 0, "radsuit_finished", &sv_fields.radsuit_finished}, - {ev_float, 0, "speed", &sv_fields.speed}, - {ev_float, 0, "basevelocity", &sv_fields.basevelocity}, - {ev_float, 0, "drawPercent", &sv_fields.drawPercent}, - {ev_float, 0, "mass", &sv_fields.mass}, - {ev_float, 0, "light_level", &sv_fields.light_level}, {ev_float, 0, "items2", &sv_fields.items2}, - {ev_float, 0, "pitch_speed", &sv_fields.pitch_speed}, {ev_float, 0, "lastruntime", &sv_fields.lastruntime}, {ev_void, 0, 0}, }; @@ -353,22 +468,30 @@ set_address (sv_def_t *def, void *address) switch (def->type) { case ev_void: case ev_short: + case ev_ushort: case ev_invalid: case ev_type_count: break; case ev_float: case ev_vector: - case ev_quat: + case ev_quaternion: *(float **)def->field = (float *) address; break; + case ev_double: + *(double **)def->field = (double *) address; + break; case ev_string: case ev_entity: case ev_field: case ev_func: - case ev_pointer: - case ev_integer: - case ev_uinteger: - *(int **)def->field = (int *) address; + case ev_ptr: + case ev_int: + case ev_uint: + *(pr_int_t **)def->field = (pr_int_t *) address; + break; + case ev_long: + case ev_ulong: + *(pr_long_t **)def->field = (pr_long_t *) address; break; } } @@ -376,7 +499,7 @@ set_address (sv_def_t *def, void *address) static int resolve_globals (progs_t *pr, sv_def_t *def, int mode) { - ddef_t *ddef; + pr_def_t *ddef; int ret = 1; if (mode == 2) { @@ -404,13 +527,13 @@ resolve_functions (progs_t *pr, sv_def_t *def, int mode) if (mode == 2) { for (; def->name; def++) - *(func_t *) def->field = G_FUNCTION (pr, def->offset); + *(pr_func_t *) def->field = G_FUNCTION (pr, def->offset); return 1; } for (; def->name; def++) { dfunc = PR_FindFunction (pr, def->name); if (dfunc) { - *(func_t *) def->field = dfunc - pr->pr_functions; + *(pr_func_t *) def->field = dfunc - pr->pr_functions; } else if (mode) { PR_Undefined (pr, "function", def->name); ret = 0; @@ -422,7 +545,7 @@ resolve_functions (progs_t *pr, sv_def_t *def, int mode) static int resolve_fields (progs_t *pr, sv_def_t *def, int mode) { - ddef_t *ddef; + pr_def_t *ddef; int ret = 1; if (mode == 2) { @@ -467,58 +590,74 @@ resolve (progs_t *pr) resolve_fields (pr, nq_opt_fields, 0); // progs engine needs these globals anyway sv_pr_state.globals.self = sv_globals.self; - sv_pr_state.globals.time = sv_globals.time; + sv_pr_state.globals.ftime = sv_globals.time;//FIXME double time return ret; } +static int +sv_init_edicts (progs_t *pr) +{ + int i; + + memset (sv_edicts, 0, sizeof (sv_edicts)); + memset (sv_data, 0, sizeof (sv_data)); + + // init the data field of the edicts + for (i = 0; i < sv.max_edicts; i++) { + edict_t *ent = EDICT_NUM (&sv_pr_state, i); + ent->pr = &sv_pr_state; + ent->entnum = i; + ent->edict = EDICT_TO_PROG (&sv_pr_state, ent); + ent->edata = &sv_data[i]; + SVdata (ent)->edict = ent; + } + + return 1; +} + void SV_LoadProgs (void) { const char *progs_name = "progs.dat"; const char *range; - int i; - if (strequal (sv_progs_ext->string, "qf")) { + if (strequal (sv_progs_ext, "qf")) { sv_range = PR_RANGE_QF; range = "QF"; - } else if (strequal (sv_progs_ext->string, "id")) { + } else if (strequal (sv_progs_ext, "id")) { sv_range = PR_RANGE_ID; range = "ID"; } else { sv_range = PR_RANGE_NONE; range = "None"; } - Sys_MaskPrintf (SYS_DEV, "Using %s builtin extention mapping\n", range); + Sys_MaskPrintf (SYS_dev, "Using %s builtin extention mapping\n", range); memset (&sv_globals, 0, sizeof (sv_funcs)); memset (&sv_funcs, 0, sizeof (sv_funcs)); if (qfs_gamedir->gamecode && *qfs_gamedir->gamecode) progs_name = qfs_gamedir->gamecode; - if (*sv_progs->string) - progs_name = sv_progs->string; + if (*sv_progs) + progs_name = sv_progs; - PR_LoadProgs (&sv_pr_state, progs_name, sv.max_edicts, - sv_progs_zone->int_val * 1024); + sv_pr_state.max_edicts = sv.max_edicts; + sv_pr_state.zone_size = sv_progs_zone * 1024; + sv_pr_state.stack_size = sv_progs_stack * 1024; + sv.edicts = sv_edicts; + + PR_LoadProgs (&sv_pr_state, progs_name); if (!sv_pr_state.progs) Host_Error ("SV_LoadProgs: couldn't load %s", progs_name); - - memset (sv_data, 0, sizeof (sv_data)); - - // init the data field of the edicts - for (i = 0; i < sv.max_edicts; i++) { - edict_t *ent = EDICT_NUM (&sv_pr_state, i); - ent->entnum = i; - ent->edata = &sv_data[i]; - SVdata (ent)->edict = ent; - } } void SV_Progs_Init (void) { + SV_Progs_Init_Cvars (); + pr_gametype = "netquake"; - sv_pr_state.edicts = &sv.edicts; + sv_pr_state.pr_edicts = &sv.edicts; sv_pr_state.num_edicts = &sv.num_edicts; sv_pr_state.reserved_edicts = &svs.maxclients; sv_pr_state.unlink = SV_UnlinkEdict; @@ -527,6 +666,9 @@ SV_Progs_Init (void) sv_pr_state.bi_map = bi_map; sv_pr_state.resolve = resolve; + PR_AddLoadFunc (&sv_pr_state, sv_init_edicts); + PR_Init (&sv_pr_state); + SV_PR_Cmds_Init (); Cmd_AddCommand ("edict", ED_PrintEdict_f, "Report information on a given " @@ -544,28 +686,22 @@ SV_Progs_Init (void) void SV_Progs_Init_Cvars (void) { - sv_progs = Cvar_Get ("sv_progs", "", CVAR_NONE, NULL, - "Override the default game progs."); - sv_progs_zone = Cvar_Get ("sv_progs_zone", "256", CVAR_NONE, NULL, - "size of the zone for progs in kb"); - sv_progs_ext = Cvar_Get ("sv_progs_ext", "qf", CVAR_NONE, NULL, - "extention mapping to use: " - "none, id, qf"); - pr_checkextensions = Cvar_Get ("pr_checkextensions", "1", CVAR_ROM, NULL, - "indicate the presence of the " - "checkextentions qc function"); + PR_Init_Cvars (); + Cvar_Register (&sv_progs_cvar, 0, 0); + Cvar_Register (&sv_progs_zone_cvar, 0, 0); + Cvar_Register (&sv_progs_stack_cvar, 0, 0); + Cvar_Register (&sv_progs_ext_cvar, 0, 0); + Cvar_Register (&pr_checkextensions_cvar, 0, 0); - nomonsters = Cvar_Get ("nomonsters", "0", CVAR_NONE, NULL, - "No Description"); - gamecfg = Cvar_Get ("gamecfg", "0", CVAR_NONE, NULL, "No Description"); - scratch1 = Cvar_Get ("scratch1", "0", CVAR_NONE, NULL, "No Description"); - scratch2 = Cvar_Get ("scratch2", "0", CVAR_NONE, NULL, "No Description"); - scratch3 = Cvar_Get ("scratch3", "0", CVAR_NONE, NULL, "No Description"); - scratch4 = Cvar_Get ("scratch4", "0", CVAR_NONE, NULL, "No Description"); - savedgamecfg = Cvar_Get ("savedgamecfg", "0", CVAR_ARCHIVE, NULL, - "No Description"); - saved1 = Cvar_Get ("saved1", "0", CVAR_ARCHIVE, NULL, "No Description"); - saved2 = Cvar_Get ("saved2", "0", CVAR_ARCHIVE, NULL, "No Description"); - saved3 = Cvar_Get ("saved3", "0", CVAR_ARCHIVE, NULL, "No Description"); - saved4 = Cvar_Get ("saved4", "0", CVAR_ARCHIVE, NULL, "No Description"); + Cvar_Register (&nomonsters_cvar, 0, 0); + Cvar_Register (&gamecfg_cvar, 0, 0); + Cvar_Register (&scratch1_cvar, 0, 0); + Cvar_Register (&scratch2_cvar, 0, 0); + Cvar_Register (&scratch3_cvar, 0, 0); + Cvar_Register (&scratch4_cvar, 0, 0); + Cvar_Register (&savedgamecfg_cvar, 0, 0); + Cvar_Register (&saved1_cvar, 0, 0); + Cvar_Register (&saved2_cvar, 0, 0); + Cvar_Register (&saved3_cvar, 0, 0); + Cvar_Register (&saved4_cvar, 0, 0); } diff --git a/nq/source/sv_user.c b/nq/source/sv_user.c index c1db350a8..c2473fabd 100644 --- a/nq/source/sv_user.c +++ b/nq/source/sv_user.c @@ -42,17 +42,52 @@ #include "QF/msg.h" #include "QF/sys.h" -#include "host.h" -#include "server.h" -#include "sv_progs.h" #include "world.h" -cvar_t *sv_rollangle; -cvar_t *sv_rollspeed; +#include "nq/include/host.h" +#include "nq/include/server.h" +#include "nq/include/sv_progs.h" + +int sv_nostep; +static cvar_t sv_nostep_cvar = { + .name = "sv_nostep", + .description = + "None", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_nostep }, +}; + +float sv_rollangle; +static cvar_t sv_rollangle_cvar = { + .name = "cl_rollangle", + .description = + "How much your screen tilts when strafing", + .default_value = "2.0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_rollangle }, +}; +float sv_rollspeed; +static cvar_t sv_rollspeed_cvar = { + .name = "cl_rollspeed", + .description = + "How quickly you straighten out after strafing", + .default_value = "200", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_rollspeed }, +}; edict_t *sv_player; -cvar_t *sv_edgefriction; +float sv_edgefriction; +static cvar_t sv_edgefriction_cvar = { + .name = "edgefriction", + .description = + "None", + .default_value = "2", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_edgefriction }, +}; vec3_t forward, right, up; @@ -64,11 +99,19 @@ float *angles; float *origin; float *velocity; -qboolean onground; +bool onground; usercmd_t cmd; -cvar_t *sv_idealpitchscale; +float sv_idealpitchscale; +static cvar_t sv_idealpitchscale_cvar = { + .name = "sv_idealpitchscale", + .description = + "None", + .default_value = "0.8", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_idealpitchscale }, +}; #define MAX_FORWARD 6 @@ -85,12 +128,12 @@ SV_CalcRoll (const vec3_t angles, const vec3_t velocity) sign = side < 0 ? -1 : 1; side = fabs (side); - value = sv_rollangle->value; + value = sv_rollangle; // if (cl.inwater) // value *= 6; - if (side < sv_rollspeed->value) - side = side * value / sv_rollspeed->value; + if (side < sv_rollspeed) + side = side * value / sv_rollspeed; else side = value; @@ -153,7 +196,7 @@ SV_SetIdealPitch (void) if (steps < 2) return; - SVfloat (sv_player, idealpitch) = -dir * sv_idealpitchscale->value; + SVfloat (sv_player, idealpitch) = -dir * sv_idealpitchscale; } static void @@ -179,12 +222,12 @@ SV_UserFriction (void) trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, sv_player); if (trace.fraction == 1.0) - friction = sv_friction->value * sv_edgefriction->value; + friction = sv_friction * sv_edgefriction; else - friction = sv_friction->value; + friction = sv_friction; // apply friction - control = speed < sv_stopspeed->value ? sv_stopspeed->value : speed; + control = speed < sv_stopspeed ? sv_stopspeed : speed; newspeed = speed - host_frametime * control * friction; if (newspeed < 0) @@ -196,8 +239,24 @@ SV_UserFriction (void) vel[2] = vel[2] * newspeed; } -cvar_t *sv_maxspeed; -cvar_t *sv_accelerate; +float sv_maxspeed; +static cvar_t sv_maxspeed_cvar = { + .name = "sv_maxspeed", + .description = + "None", + .default_value = "320", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_float, .value = &sv_maxspeed }, +}; +float sv_accelerate; +static cvar_t sv_accelerate_cvar = { + .name = "sv_accelerate", + .description = + "None", + .default_value = "10", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_accelerate }, +}; #if 0 void @@ -213,7 +272,7 @@ SV_Accelerate (vec3_t wishvel) VectorSubtract (wishvel, velocity, pushvec); addspeed = VectorNormalize (pushvec); - accelspeed = sv_accelerate->value * host_frametime * addspeed; + accelspeed = sv_accelerate * host_frametime * addspeed; if (accelspeed > addspeed) accelspeed = addspeed; @@ -232,7 +291,7 @@ SV_Accelerate (void) addspeed = wishspeed - currentspeed; if (addspeed <= 0) return; - accelspeed = sv_accelerate->value * host_frametime * wishspeed; + accelspeed = sv_accelerate * host_frametime * wishspeed; if (accelspeed > addspeed) accelspeed = addspeed; @@ -253,8 +312,8 @@ SV_AirAccelerate (vec3_t wishveloc) addspeed = wishspd - currentspeed; if (addspeed <= 0) return; -// accelspeed = sv_accelerate->value * host_frametime; - accelspeed = sv_accelerate->value * wishspeed * host_frametime; +// accelspeed = sv_accelerate * host_frametime; + accelspeed = sv_accelerate * wishspeed * host_frametime; if (accelspeed > addspeed) accelspeed = addspeed; @@ -295,16 +354,16 @@ SV_WaterMove (void) wishvel[2] += cmd.upmove; wishspeed = VectorLength (wishvel); - if (wishspeed > sv_maxspeed->value) { - VectorScale (wishvel, sv_maxspeed->value / wishspeed, wishvel); - wishspeed = sv_maxspeed->value; + if (wishspeed > sv_maxspeed) { + VectorScale (wishvel, sv_maxspeed / wishspeed, wishvel); + wishspeed = sv_maxspeed; } wishspeed *= 0.7; // water friction speed = VectorLength (velocity); if (speed) { - newspeed = speed - host_frametime * speed * sv_friction->value; + newspeed = speed - host_frametime * speed * sv_friction; if (newspeed < 0) newspeed = 0; VectorScale (velocity, newspeed / speed, velocity); @@ -320,7 +379,7 @@ SV_WaterMove (void) return; VectorNormalize (wishvel); - accelspeed = sv_accelerate->value * wishspeed * host_frametime; + accelspeed = sv_accelerate * wishspeed * host_frametime; if (accelspeed > addspeed) accelspeed = addspeed; @@ -367,9 +426,9 @@ SV_AirMove (void) VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize (wishdir); - if (wishspeed > sv_maxspeed->value) { - VectorScale (wishvel, sv_maxspeed->value / wishspeed, wishvel); - wishspeed = sv_maxspeed->value; + if (wishspeed > sv_maxspeed) { + VectorScale (wishvel, sv_maxspeed / wishspeed, wishvel); + wishspeed = sv_maxspeed; } if (SVfloat (sv_player, movetype) == MOVETYPE_NOCLIP) { // noclip @@ -474,7 +533,7 @@ SV_ReadClientMove (usercmd_t *move) Returns false if the client should be killed */ -static qboolean +static bool SV_ReadClientMessage (void) { int cmd, ret; @@ -563,7 +622,7 @@ SV_ReadClientMessage (void) else if (ret == 1) Cmd_ExecuteString (s, src_client); else - Sys_MaskPrintf (SYS_DEV, "%s tried to %s\n", + Sys_MaskPrintf (SYS_dev, "%s tried to %s\n", host_client->name, s); break; @@ -583,7 +642,7 @@ SV_ReadClientMessage (void) void SV_RunClients (void) { - int i; + unsigned i; for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { @@ -607,3 +666,40 @@ SV_RunClients (void) SV_ClientThink (); } } + +static void +sv_rollspeed_f (void *data, const cvar_t *cvar) +{ + sv_rollspeed = *(float *) cvar->value.value; +} + +static void +sv_rollangle_f (void *data, const cvar_t *cvar) +{ + sv_rollangle = *(float *) cvar->value.value; +} + +void +SV_User_Init_Cvars (void) +{ + //NOTE: the cl/sv clash is deliberate: dedicated server will use the right + //vars, but client/server combo will use the one. + if (isDedicated) { + Cvar_Register (&sv_rollspeed_cvar, 0, 0); + Cvar_Register (&sv_rollangle_cvar, 0, 0); + } else { + cvar_t *var; + var = Cvar_FindVar ("cl_rollspeed"); + Cvar_AddListener (var, sv_rollspeed_f, 0); + sv_rollspeed = *(float *) var->value.value; + var = Cvar_FindVar ("cl_rollangle"); + Cvar_AddListener (var, sv_rollangle_f, 0); + sv_rollangle = *(float *) var->value.value; + } + Cvar_Register (&sv_edgefriction_cvar, 0, 0); + Cvar_Register (&sv_maxspeed_cvar, Cvar_Info, &sv_maxspeed); + Cvar_Register (&sv_accelerate_cvar, 0, 0); + Cvar_Register (&sv_idealpitchscale_cvar, 0, 0); + + Cvar_Register (&sv_nostep_cvar, 0, 0); +} diff --git a/nq/source/sys_sdl.c b/nq/source/sys_sdl.c index 3f4fa726e..b99dba284 100644 --- a/nq/source/sys_sdl.c +++ b/nq/source/sys_sdl.c @@ -51,15 +51,15 @@ #include "QF/qargs.h" #include "QF/sys.h" -#include "client.h" -#include "host.h" +#include "nq/include/client.h" +#include "nq/include/host.h" #ifdef _WIN32 # include "winquake.h" #endif int qf_sdl_link; -qboolean isDedicated = false; +bool isDedicated = false; static void startup (void) @@ -85,7 +85,7 @@ startup (void) } static void -shutdown_f (void) +shutdown_f (void *data) { #ifndef _WIN32 // change stdin to blocking @@ -113,16 +113,20 @@ SDL_main (int argc, char *argv[]) isDedicated = (COM_CheckParm ("-dedicated") != 0); - Sys_RegisterShutdown (Host_Shutdown); - Sys_RegisterShutdown (shutdown_f); + Sys_RegisterShutdown (shutdown_f, 0); Host_Init (); #ifndef _WIN32 - if (!sys_nostdout->int_val) { + if (!sys_nostdout) { fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) | O_NONBLOCK); Sys_Printf ("Quake -- Version %s\n", NQ_VERSION); } +#else + // hack to prevent gcc suggesting noreturn + if (!sys_nostdout) { + return 1; + } #endif oldtime = Sys_DoubleTime () - 0.1; @@ -131,15 +135,15 @@ SDL_main (int argc, char *argv[]) newtime = Sys_DoubleTime (); time = newtime - oldtime; - if (cls.state == ca_dedicated) { // play vcrfiles at max speed - if (time < sys_ticrate->value && (!vcrFile || recording)) { + if (net_is_dedicated) { // play vcrfiles at max speed + if (time < sys_ticrate && (!vcrFile || recording)) { usleep (1); continue; // not time to run a server-only tic yet } - time = sys_ticrate->value; + time = sys_ticrate; } - if (time > sys_ticrate->value * 2) + if (time > sys_ticrate * 2) oldtime = newtime; else oldtime += time; diff --git a/nq/source/sys_unix.c b/nq/source/sys_unix.c index ec21a1d7b..5a6e350c7 100644 --- a/nq/source/sys_unix.c +++ b/nq/source/sys_unix.c @@ -49,13 +49,13 @@ #include "QF/qargs.h" #include "QF/sys.h" -#include "client.h" -#include "host.h" +#include "nq/include/client.h" +#include "nq/include/host.h" -qboolean isDedicated = false; +bool isDedicated = false; static void -shutdown_f (void) +shutdown_f (void *data) { // change stdin to blocking fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~O_NONBLOCK); @@ -75,12 +75,11 @@ main (int argc, const char **argv) isDedicated = (COM_CheckParm ("-dedicated") != 0); - Sys_RegisterShutdown (Host_Shutdown); - Sys_RegisterShutdown (shutdown_f); + Sys_RegisterShutdown (shutdown_f, 0); Host_Init (); - if (!sys_nostdout->int_val) { + if (!sys_nostdout) { fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) | O_NONBLOCK); Sys_Printf ("Quake -- Version %s\n", NQ_VERSION); } @@ -91,15 +90,15 @@ main (int argc, const char **argv) newtime = Sys_DoubleTime (); time = newtime - oldtime; - if (cls.state == ca_dedicated) { // play vcrfiles at max speed - if (time < sys_ticrate->value && (!vcrFile || recording)) { + if (net_is_dedicated) { // play vcrfiles at max speed + if (time < sys_ticrate && (!vcrFile || recording)) { usleep (1); continue; // not time to run a server-only tic yet } - time = sys_ticrate->value; + time = sys_ticrate; } - if (time > sys_ticrate->value * 2) + if (time > sys_ticrate * 2) oldtime = newtime; else oldtime += time; diff --git a/nq/source/sys_unixd.c b/nq/source/sys_unixd.c index 55d956268..e23a713a0 100644 --- a/nq/source/sys_unixd.c +++ b/nq/source/sys_unixd.c @@ -48,12 +48,12 @@ #include "QF/qargs.h" #include "QF/sys.h" -#include "host.h" +#include "nq/include/host.h" -qboolean isDedicated = true; +bool isDedicated = true; static void -shutdown_f (void) +shutdown_f (void *data) { // change stdin to blocking fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~O_NONBLOCK); @@ -86,8 +86,7 @@ main (int argc, const char **argv) host_parms.argc = com_argc; host_parms.argv = com_argv; - Sys_RegisterShutdown (Host_Shutdown); - Sys_RegisterShutdown (shutdown_f); + Sys_RegisterShutdown (shutdown_f, 0); Host_Init (); @@ -100,13 +99,13 @@ main (int argc, const char **argv) // find time spent rendering last frame newtime = Sys_DoubleTime (); time = newtime - oldtime; - if (time < sys_ticrate->value) { + if (time < sys_ticrate) { usleep (1); continue; // not time to run a server-only tic yet } - time = sys_ticrate->value; + time = sys_ticrate; - if (time > sys_ticrate->value * 2) + if (time > sys_ticrate * 2) oldtime = newtime; else oldtime += time; diff --git a/nq/source/sys_win.c b/nq/source/sys_win.c index 421690748..439458f7d 100644 --- a/nq/source/sys_win.c +++ b/nq/source/sys_win.c @@ -36,10 +36,10 @@ #include "QF/screen.h" #include "QF/sys.h" -#include "client.h" -#include "host.h" +#include "nq/include/client.h" +#include "nq/include/host.h" -qboolean isDedicated = false; +bool isDedicated = false; #define MINIMUM_WIN_MEMORY 0x0880000 #define MAXIMUM_WIN_MEMORY 0x1000000 @@ -49,8 +49,8 @@ qboolean isDedicated = false; #define PAUSE_SLEEP 50 // sleep time on pause or minimization #define NOT_FOCUS_SLEEP 20 // sleep time when not focus -qboolean ActiveApp, Minimized; -qboolean WinNT; +bool ActiveApp, Minimized; +bool WinNT; static double pfreq; static int lowshift; @@ -104,7 +104,7 @@ startup (void) } static void -shutdown_f (void) +shutdown_f (void *data) { if (tevent) CloseHandle (tevent); @@ -207,8 +207,7 @@ WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, if (!isDedicated) init_handles (hInstance); - Sys_RegisterShutdown (Host_Shutdown); - Sys_RegisterShutdown (shutdown_f); + Sys_RegisterShutdown (shutdown_f, 0); Host_Init (); @@ -228,15 +227,15 @@ WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, newtime = Sys_DoubleTime (); time = newtime - oldtime; - if (cls.state == ca_dedicated) { // play vcrfiles at max speed - if (time < sys_ticrate->value && (!vcrFile || recording)) { + if (net_is_dedicated) { // play vcrfiles at max speed + if (time < sys_ticrate && (!vcrFile || recording)) { Sleep (1); continue; // not time to run a server-only tic yet } - time = sys_ticrate->value; + time = sys_ticrate; } - if (time > sys_ticrate->value * 2) + if (time > sys_ticrate * 2) oldtime = newtime; else oldtime += time; diff --git a/nq/source/sys_wind.c b/nq/source/sys_wind.c index b38461448..8ab700325 100644 --- a/nq/source/sys_wind.c +++ b/nq/source/sys_wind.c @@ -34,12 +34,12 @@ #include "QF/qargs.h" #include "QF/sys.h" -#include "host.h" +#include "nq/include/host.h" -qboolean isDedicated = true; +bool isDedicated = true; static void -shutdown_f (void) +shutdown_f (void *data) { } @@ -69,8 +69,7 @@ main (int argc, const char **argv) host_parms.argc = com_argc; host_parms.argv = com_argv; - Sys_RegisterShutdown (Host_Shutdown); - Sys_RegisterShutdown (shutdown_f); + Sys_RegisterShutdown (shutdown_f, 0); Host_Init (); @@ -79,13 +78,13 @@ main (int argc, const char **argv) // find time spent rendering last frame newtime = Sys_DoubleTime (); time = newtime - oldtime; - if (time < sys_ticrate->value) { + if (time < sys_ticrate) { Sleep (1); continue; // not time to run a server-only tic yet } - time = sys_ticrate->value; + time = sys_ticrate; - if (time > sys_ticrate->value * 2) + if (time > sys_ticrate * 2) oldtime = newtime; else oldtime += time; diff --git a/nq/source/world.c b/nq/source/world.c index e00208900..081cb28b1 100644 --- a/nq/source/world.c +++ b/nq/source/world.c @@ -38,15 +38,15 @@ #include #include "QF/clip_hull.h" -#include "QF/console.h" #include "QF/crc.h" #include "QF/sys.h" #include "compat.h" -#include "server.h" -#include "sv_progs.h" #include "world.h" +#include "nq/include/server.h" +#include "nq/include/sv_progs.h" + #define always_inline inline __attribute__((__always_inline__)) #define EDICT_LEAFS 32 @@ -134,7 +134,7 @@ typedef struct { /* HULL BOXES */ static hull_t box_hull; -static mclipnode_t box_clipnodes[6]; +static dclipnode_t box_clipnodes[6]; static plane_t box_planes[6]; @@ -145,7 +145,7 @@ static plane_t box_planes[6]; can just be stored out and get a proper hull_t structure. */ void -SV_InitHull (hull_t *hull, mclipnode_t *clipnodes, plane_t *planes) +SV_InitHull (hull_t *hull, dclipnode_t *clipnodes, plane_t *planes) { int side, i; @@ -214,7 +214,7 @@ SV_HullForEntity (edict_t *ent, const vec3_t mins, const vec3_t maxs, vec3_t hullmins, hullmaxs, size; if ((sv_fields.rotated_bbox != -1 - && SVinteger (ent, rotated_bbox)) + && SVint (ent, rotated_bbox)) || SVfloat (ent, solid) == SOLID_BSP) { VectorSubtract (maxs, mins, size); if (size[0] < 3) @@ -225,8 +225,8 @@ SV_HullForEntity (edict_t *ent, const vec3_t mins, const vec3_t maxs, hull_index = 2; } if (sv_fields.rotated_bbox != -1 - && SVinteger (ent, rotated_bbox)) { - int h = SVinteger (ent, rotated_bbox) - 1; + && SVint (ent, rotated_bbox)) { + int h = SVint (ent, rotated_bbox) - 1; hull_list = pf_hull_list[h]->hulls; } if (SVfloat (ent, solid) == SOLID_BSP) { // explicit hulls in the BSP model @@ -244,7 +244,7 @@ SV_HullForEntity (edict_t *ent, const vec3_t mins, const vec3_t maxs, PR_GetString (&sv_pr_state, SVstring (ent, classname))); - hull_list = model->hull_list; + hull_list = model->brush.hull_list; } if (hull_list) { // decide which clipping hull to use, based on the size @@ -409,29 +409,28 @@ SV_TouchLinks (edict_t *ent, areanode_t *node) } static void -SV_FindTouchedLeafs (edict_t *ent, mnode_t *node) +SV_FindTouchedLeafs (edict_t *ent, int node_id) { int sides; - mleaf_t *leaf; plane_t *splitplane; edict_leaf_t *edict_leaf; - if (node->contents == CONTENTS_SOLID) - return; - - // add an efrag if the node is a leaf - if (node->contents < 0) { - leaf = (mleaf_t *) node; + // add an efrag if the node is a non-solid leaf + if (node_id < 0) { + mleaf_t *leaf = sv.worldmodel->brush.leafs + ~node_id; + if (leaf->contents == CONTENTS_SOLID) + return; edict_leaf = alloc_edict_leaf (); - edict_leaf->leafnum = leaf - sv.worldmodel->leafs - 1; + edict_leaf->leafnum = leaf - sv.worldmodel->brush.leafs - 1; edict_leaf->next = SVdata (ent)->leafs; SVdata (ent)->leafs = edict_leaf; return; } + mnode_t *node = sv.worldmodel->brush.nodes + node_id; // NODE_MIXED - splitplane = node->plane; + splitplane = (plane_t *) &node->plane; sides = BOX_ON_PLANE_SIDE (SVvector (ent, absmin), SVvector (ent, absmax), splitplane); @@ -444,7 +443,7 @@ SV_FindTouchedLeafs (edict_t *ent, mnode_t *node) } void -SV_LinkEdict (edict_t *ent, qboolean touch_triggers) +SV_LinkEdict (edict_t *ent, bool touch_triggers) { areanode_t *node; @@ -496,7 +495,7 @@ SV_LinkEdict (edict_t *ent, qboolean touch_triggers) // link to PVS leafs free_edict_leafs (&SVdata (ent)->leafs); if (SVfloat (ent, modelindex)) - SV_FindTouchedLeafs (ent, sv.worldmodel->nodes); + SV_FindTouchedLeafs (ent, 0); if (SVfloat (ent, solid) == SOLID_NOT) return; @@ -531,7 +530,7 @@ int SV_HullPointContents (hull_t *hull, int num, const vec3_t p) { float d; - mclipnode_t *node; + dclipnode_t *node; plane_t *plane; while (num >= 0) { @@ -559,7 +558,7 @@ SV_PointContents (const vec3_t p) { int cont; - cont = SV_HullPointContents (&sv.worldmodel->hulls[0], 0, p); + cont = SV_HullPointContents (&sv.worldmodel->brush.hulls[0], 0, p); if (cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN) cont = CONTENTS_WATER; return cont; @@ -568,7 +567,7 @@ SV_PointContents (const vec3_t p) int SV_TruePointContents (const vec3_t p) { - return SV_HullPointContents (&sv.worldmodel->hulls[0], 0, p); + return SV_HullPointContents (&sv.worldmodel->brush.hulls[0], 0, p); } /* @@ -705,7 +704,7 @@ ctl_pretest_triggers (edict_t *touch, moveclip_t *clip) return 1; } -static always_inline int +static always_inline __attribute__((pure)) int ctl_pretest_other (edict_t *touch, moveclip_t *clip) { if (SVfloat (touch, solid) == SOLID_NOT) @@ -779,11 +778,10 @@ SV_ClipToLinks (areanode_t *node, moveclip_t *clip) edict_t *touch; link_t *l, *next; trace_t trace; - int i; if (clip->type == TL_EVERYTHING) { touch = NEXT_EDICT (&sv_pr_state, sv.edicts); - for (i = 1; i < sv.num_edicts; i++, + for (unsigned i = 1; i < sv.num_edicts; i++, touch = NEXT_EDICT (&sv_pr_state, touch)) { if (clip->trace.allsolid) return; @@ -896,13 +894,12 @@ SV_Move (const vec3_t start, const vec3_t mins, const vec3_t maxs, edict_t * SV_TestPlayerPosition (edict_t *ent, const vec3_t origin) { - int e; edict_t *check; hull_t *hull; vec3_t boxmins, boxmaxs, offset; // check world first - hull = &sv.worldmodel->hulls[1]; + hull = &sv.worldmodel->brush.hulls[1]; if (SV_HullPointContents (hull, hull->firstclipnode, origin) != CONTENTS_EMPTY) return sv.edicts; @@ -911,8 +908,8 @@ SV_TestPlayerPosition (edict_t *ent, const vec3_t origin) VectorAdd (origin, SVvector (ent, maxs), boxmaxs); check = NEXT_EDICT (&sv_pr_state, sv.edicts); - for (e = 1; e < sv.num_edicts; e++, check = NEXT_EDICT (&sv_pr_state, - check)) { + for (unsigned e = 1; e < sv.num_edicts; + e++, check = NEXT_EDICT (&sv_pr_state, check)) { if (check->free) continue; if (check == ent) diff --git a/pkg-config/Makefile.am b/pkg-config/Makefile.am deleted file mode 100644 index 48f4005f1..000000000 --- a/pkg-config/Makefile.am +++ /dev/null @@ -1,5 +0,0 @@ -pkgdatadir=@libdir@/pkgconfig - -pkgdata_DATA=quakeforge.pc qfcc.pc - -EXTRA_DIST=quakeforge.pc.in qfcc.pc.in diff --git a/pkg-config/Makemodule.am b/pkg-config/Makemodule.am new file mode 100644 index 000000000..dca513fae --- /dev/null +++ b/pkg-config/Makemodule.am @@ -0,0 +1,5 @@ +pkgconfigdir = @libdir@/pkgconfig + +pkgconfig_DATA = pkg-config/quakeforge.pc pkg-config/qfcc.pc + +EXTRA_DIST += pkg-config/quakeforge.pc.in pkg-config/qfcc.pc.in diff --git a/qtv/Makefile.am b/qtv/Makefile.am deleted file mode 100644 index 77e3cbf5b..000000000 --- a/qtv/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -## Process this file with automake to produce Makefile.in -AUTOMAKE_OPTIONS= foreign - -SUBDIRS= include source diff --git a/qtv/Makemodule.am b/qtv/Makemodule.am new file mode 100644 index 000000000..562039b04 --- /dev/null +++ b/qtv/Makemodule.am @@ -0,0 +1,2 @@ +include qtv/include/Makemodule.am +include qtv/source/Makemodule.am diff --git a/qtv/include/Makefile.am b/qtv/include/Makefile.am deleted file mode 100644 index 19df055f3..000000000 --- a/qtv/include/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -## Process this file with automake to produce Makefile.in -AUTOMAKE_OPTIONS= foreign - -EXTRA_DIST = client.h connection.h qtv.h server.h diff --git a/qtv/include/Makemodule.am b/qtv/include/Makemodule.am new file mode 100644 index 000000000..346c1716d --- /dev/null +++ b/qtv/include/Makemodule.am @@ -0,0 +1,5 @@ +EXTRA_DIST += \ + qtv/include/client.h \ + qtv/include/connection.h \ + qtv/include/qtv.h \ + qtv/include/server.h diff --git a/qtv/include/client.h b/qtv/include/client.h index 1c130fcd0..fe0cb5cba 100644 --- a/qtv/include/client.h +++ b/qtv/include/client.h @@ -50,7 +50,7 @@ typedef struct client_s { backbuf_t backbuf; sizebuf_t datagram; byte datagram_buf[MAX_DATAGRAM]; - qboolean send_message; + bool send_message; frame_t frames[UPDATE_BACKUP]; entity_state_t packet_entities[UPDATE_BACKUP][MAX_PACKET_ENTITIES]; diff --git a/qtv/include/connection.h b/qtv/include/connection.h index d02e4b971..18bbbc277 100644 --- a/qtv/include/connection.h +++ b/qtv/include/connection.h @@ -36,7 +36,7 @@ /** \defgroup qtv_connection Connection Management \ingroup qtv */ -//@{ +///@{ typedef struct connection_s { netadr_t address; ///< Address of the remote end. @@ -77,6 +77,6 @@ void Connection_Del (connection_t *con); */ connection_t *Connection_Find (netadr_t *address); -//@} +///@} #endif//__connection_h diff --git a/qtv/include/qtv.h b/qtv/include/qtv.h index 57947af16..c6afcf39c 100644 --- a/qtv/include/qtv.h +++ b/qtv/include/qtv.h @@ -40,7 +40,7 @@ /** \defgroup qtv_general General Functions \ingroup qtv */ -//@{ +///@{ #define PORT_QTV 27501 ///< Default port to listen for connecting clients. @@ -54,7 +54,7 @@ extern double realtime; extern struct cbuf_s *qtv_cbuf; extern struct cbuf_args_s *qtv_args; -extern struct cvar_s *sv_timeout; +extern float sv_timeout; struct client_s; @@ -65,7 +65,7 @@ struct client_s; Calling qtv_begin_redirect() before, and qtv_end_redirect() after a series of calls will redirect output. */ -void qtv_printf (const char *fmt, ...) __attribute__((format(printf,1,2))); +void qtv_printf (const char *fmt, ...) __attribute__((format(PRINTF,1,2))); /** Begin redirection of console printing. @@ -91,6 +91,6 @@ void qtv_end_redirect (void); void qtv_sbar_init (void); -//@} +///@} #endif//__qtv_h diff --git a/qtv/include/server.h b/qtv/include/server.h index 7752228a1..8517a1672 100644 --- a/qtv/include/server.h +++ b/qtv/include/server.h @@ -52,11 +52,23 @@ typedef struct player_s { typedef struct frame_s { int delta_sequence; - qboolean invalid; + bool invalid; packet_players_t players; packet_entities_t entities; } frame_t; +typedef struct qtv_leaf_s { + struct qtv_leaf_s *next; + int num; +} qtv_leaf_t; + +typedef struct { + entity_state_t e; + int model_index; // 1-based + struct model_s *model; + qtv_leaf_t *leafs; +} qtv_entity_t; + #define MAX_SV_PLAYERS 32 #define MAX_SV_ENTITIES 512 #define MAX_SIGNON_BUFFERS 8 @@ -84,6 +96,8 @@ typedef struct server_s { char *soundlist[MAX_SOUNDS + 1]; char *modellist[MAX_MODELS + 1]; char *lightstyles[MAX_LIGHTSTYLES]; + struct model_s *worldmodel; + struct set_s *fatpvs; int playermodel; int num_signon_buffers; int signon_buffer_size[MAX_SIGNON_BUFFERS]; @@ -95,7 +109,7 @@ typedef struct server_s { int validsequence; frame_t frames[UPDATE_BACKUP]; - entity_state_t entities[MAX_SV_ENTITIES]; + qtv_entity_t entities[MAX_SV_ENTITIES]; byte ent_valid[MAX_SV_ENTITIES]; entity_state_t baselines[MAX_SV_ENTITIES]; player_t players[MAX_SV_PLAYERS]; diff --git a/qtv/source/Makefile.am b/qtv/source/Makemodule.am similarity index 61% rename from qtv/source/Makefile.am rename to qtv/source/Makemodule.am index fa73f8de8..5997596fe 100644 --- a/qtv/source/Makefile.am +++ b/qtv/source/Makemodule.am @@ -27,27 +27,20 @@ # $Id$ # -AUTOMAKE_OPTIONS= foreign +bin_PROGRAMS += @QTV_TARGETS@ -AM_CPPFLAGS= -I$(top_srcdir)/include -I$(top_srcdir)/qtv/include - -bin_PROGRAMS= @QTV_TARGETS@ - -EXTRA_PROGRAMS= qtv - -common_ldflags= -export-dynamic +EXTRA_PROGRAMS += qtv-server qtv_LIBS= \ @server_static_plugin_libs@ \ - $(top_builddir)/libs/qw/libqw.a \ - $(top_builddir)/libs/net/libnet_chan.la \ - $(top_builddir)/libs/console/libQFconsole.la \ - $(top_builddir)/libs/util/libQFutil.la + libs/qw/libqw.a \ + libs/net/libnet_chan.la \ + libs/models/libQFmodels.la \ + libs/console/libQFconsole.la \ + libs/ui/libQFui.la \ + libs/util/libQFutil.la -qtv_SOURCES= client.c connection.c qtv.c sbar.c server.c sv_parse.c -qtv_LDADD= $(qtv_LIBS) $(NET_LIBS) $(DL_LIBS) $(CURSES_LIBS) -qtv_LDFLAGS= $(common_ldflags) -qtv_DEPENDENCIES= $(qtv_LIBS) - -# Kill the temp files, hopefully. -CLEANFILES = *.i *.s +qtv_server_SOURCES= qtv/source/client.c qtv/source/connection.c qtv/source/qtv.c qtv/source/sbar.c qtv/source/server.c qtv/source/sv_parse.c +qtv_server_LDADD= $(qtv_LIBS) $(NET_LIBS) $(DL_LIBS) $(CURSES_LIBS) +qtv_server_LDFLAGS= $(common_ldflags) +qtv_server_DEPENDENCIES= $(qtv_LIBS) diff --git a/qtv/source/client.c b/qtv/source/client.c index d53849f7f..ff59fa443 100644 --- a/qtv/source/client.c +++ b/qtv/source/client.c @@ -48,17 +48,20 @@ #include "QF/hash.h" #include "QF/idparse.h" #include "QF/info.h" +#include "QF/set.h" #include "QF/sys.h" #include "QF/va.h" +#include "QF/simd/vec4f.h" + #include "qw/bothdefs.h" #include "qw/msg_ucmd.h" #include "qw/protocol.h" -#include "client.h" -#include "connection.h" -#include "qtv.h" -#include "server.h" +#include "qtv/include/client.h" +#include "qtv/include/connection.h" +#include "qtv/include/qtv.h" +#include "qtv/include/server.h" int client_count; static client_t *clients; @@ -177,9 +180,9 @@ cl_prespawn_f (client_t *cl, void *unused) if (buf >= sv->num_signon_buffers) buf = 0; if (buf == sv->num_signon_buffers - 1) - cmd = va ("cmd spawn %i 0\n", cl->server->spawncount); + cmd = va (0, "cmd spawn %i 0\n", cl->server->spawncount); else - cmd = va ("cmd prespawn %i %i\n", cl->server->spawncount, buf + 1); + cmd = va (0, "cmd prespawn %i %i\n", cl->server->spawncount, buf + 1); size = sv->signon_buffer_size[buf] + 1 + strlen (cmd) + 1; msg = MSG_ReliableCheckBlock (&cl->backbuf, size); SZ_Write (msg, sv->signon_buffers[buf], sv->signon_buffer_size[buf]); @@ -446,9 +449,9 @@ spectator_move (client_t *cl, usercmd_t *ucmd) AngleVectors (cl->state.cmd.angles, forward, right, up); - speed = DotProduct (cl->state.velocity, cl->state.velocity); + speed = DotProduct (cl->state.es.velocity, cl->state.es.velocity); if (speed < 1) { - VectorZero (cl->state.velocity); + VectorZero (cl->state.es.velocity); } else { speed = sqrt (speed); drop = 0; @@ -462,7 +465,7 @@ spectator_move (client_t *cl, usercmd_t *ucmd) newspeed = 0; newspeed /= speed; - VectorScale (cl->state.velocity, newspeed, cl->state.velocity); + VectorScale (cl->state.es.velocity, newspeed, cl->state.es.velocity); } fmove = ucmd->forwardmove; @@ -484,7 +487,7 @@ spectator_move (client_t *cl, usercmd_t *ucmd) wishspeed = sv->movevars.spectatormaxspeed; } - currentspeed = DotProduct (cl->state.velocity, wishdir); + currentspeed = DotProduct (cl->state.es.velocity, wishdir); addspeed = wishspeed - currentspeed; if (addspeed <= 0) return; @@ -492,10 +495,10 @@ spectator_move (client_t *cl, usercmd_t *ucmd) if (accelspeed > addspeed) accelspeed = addspeed; - VectorMultAdd (cl->state.velocity, accelspeed, wishdir, - cl->state.velocity); - VectorMultAdd (cl->state.origin, frametime, cl->state.velocity, - cl->state.origin); + VectorMultAdd (cl->state.es.velocity, accelspeed, wishdir, + cl->state.es.velocity); + VectorMultAdd (cl->state.es.origin, frametime, cl->state.es.velocity, + cl->state.es.origin); } static void @@ -526,7 +529,7 @@ client_parse_message (client_t *cl) usercmd_t oldest, oldcmd, newcmd; byte checksum, calculatedChecksum; int checksumIndex, seq_hash; - qboolean move_issued = false; + bool move_issued = false; // make sure the reply sequence number matches the incoming // sequence number @@ -580,7 +583,7 @@ client_parse_message (client_t *cl) MSG_GetReadCount (net_message) - checksumIndex - 1, seq_hash); if (calculatedChecksum != checksum) { - Sys_MaskPrintf (SYS_DEV, + Sys_MaskPrintf (SYS_dev, "Failed command checksum for %s(%d) " "(%d != %d)\n", Info_ValueForKey (cl->userinfo, "name"), @@ -612,7 +615,7 @@ client_parse_message (client_t *cl) break; case clc_tmove: MSG_ReadCoordV (net_message, o); - VectorCopy (o, cl->state.origin); + VectorCopy (o, cl->state.es.origin); break; case clc_upload: size = MSG_ReadShort (net_message); @@ -627,18 +630,18 @@ static void write_player (int num, plent_state_t *pl, server_t *sv, sizebuf_t *msg) { int i; - int pflags = (pl->flags & (PF_GIB | PF_DEAD)) + int pflags = (pl->es.flags & (PF_GIB | PF_DEAD)) | PF_MSEC | PF_COMMAND; int qf_bits = 0; - if (pl->modelindex != sv->playermodel) + if (pl->es.modelindex != sv->playermodel) pflags |= PF_MODEL; for (i = 0; i < 3; i++) - if (pl->velocity[i]) + if (pl->es.velocity[i]) pflags |= PF_VELOCITY1 << i; - if (pl->effects & 0xff) + if (pl->es.effects & 0xff) pflags |= PF_EFFECTS; - if (pl->skinnum) + if (pl->es.skinnum) pflags |= PF_SKINNUM; qf_bits = 0; @@ -670,21 +673,22 @@ write_player (int num, plent_state_t *pl, server_t *sv, sizebuf_t *msg) // } else if (ent == clent) { // // don't send a lot of data on personal entity // pflags &= ~(PF_MSEC | PF_COMMAND); -// if (pl->weaponframe) +// if (pl->es.weaponframe) // pflags |= PF_WEAPONFRAME; // } // if (client->spec_track && client->spec_track - 1 == j -// && pl->weaponframe) +// && pl->es.weaponframe) // pflags |= PF_WEAPONFRAME; MSG_WriteByte (msg, svc_playerinfo); MSG_WriteByte (msg, num); MSG_WriteShort (msg, pflags); - MSG_WriteCoordV (msg, pl->origin); + MSG_WriteCoordV (msg, (vec_t*)&pl->es.origin);//FIXME + pl->es.origin[3] = 1; - MSG_WriteByte (msg, pl->frame); + MSG_WriteByte (msg, pl->es.frame); if (pflags & PF_MSEC) { //msec = 1000 * (sv.time - cl->localtime); @@ -699,36 +703,36 @@ write_player (int num, plent_state_t *pl, server_t *sv, sizebuf_t *msg) for (i = 0; i < 3; i++) if (pflags & (PF_VELOCITY1 << i)) - MSG_WriteShort (msg, pl->velocity[i]); + MSG_WriteShort (msg, pl->es.velocity[i]); if (pflags & PF_MODEL) - MSG_WriteByte (msg, pl->modelindex); + MSG_WriteByte (msg, pl->es.modelindex); if (pflags & PF_SKINNUM) - MSG_WriteByte (msg, pl->skinnum); + MSG_WriteByte (msg, pl->es.skinnum); if (pflags & PF_EFFECTS) - MSG_WriteByte (msg, pl->effects); + MSG_WriteByte (msg, pl->es.effects); if (pflags & PF_WEAPONFRAME) - MSG_WriteByte (msg, pl->weaponframe); + MSG_WriteByte (msg, pl->es.weaponframe); if (pflags & PF_QF) { MSG_WriteByte (msg, qf_bits); if (qf_bits & PF_ALPHA) - MSG_WriteByte (msg, pl->alpha); + MSG_WriteByte (msg, pl->es.alpha); if (qf_bits & PF_SCALE) - MSG_WriteByte (msg, pl->scale); + MSG_WriteByte (msg, pl->es.scale); if (qf_bits & PF_EFFECTS2) - MSG_WriteByte (msg, pl->effects >> 8); + MSG_WriteByte (msg, pl->es.effects >> 8); if (qf_bits & PF_GLOWSIZE) - MSG_WriteByte (msg, pl->scale); + MSG_WriteByte (msg, pl->es.glow_size); if (qf_bits & PF_GLOWCOLOR) - MSG_WriteByte (msg, pl->glow_color); + MSG_WriteByte (msg, pl->es.glow_color); if (qf_bits & PF_COLORMOD) - MSG_WriteByte (msg, pl->colormod); + MSG_WriteByte (msg, pl->es.colormod); if (qf_bits & PF_FRAME2) - MSG_WriteByte (msg, pl->frame >> 8); + MSG_WriteByte (msg, pl->es.frame >> 8); } } #if 0 @@ -738,7 +742,7 @@ int numnails; int nailcount; static void -emit_nails (sizebuf_t *msg, qboolean recorder) +emit_nails (sizebuf_t *msg, bool recorder) { byte *buf; // [48 bits] xyzpy 12 12 12 4 8 int n, p, x, y, z, yaw; @@ -780,7 +784,7 @@ emit_nails (sizebuf_t *msg, qboolean recorder) #endif static void write_delta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg, - qboolean force)//, int stdver) + bool force)//, int stdver) { int bits, i; float miss; @@ -975,15 +979,54 @@ emit_entities (client_t *client, packet_entities_t *to, sizebuf_t *msg) MSG_WriteShort (msg, 0); // end of packetentities } +static void +add_to_fat_pvs (vec4f_t org, int node_id, server_t *sv) +{ + while (1) { + // if this is a leaf, accumulate the pvs bits + if (node_id < 0) { + mleaf_t *leaf = sv->worldmodel->brush.leafs + ~node_id; + if (leaf->contents != CONTENTS_SOLID) { + set_union (sv->fatpvs, Mod_LeafPVS (leaf, sv->worldmodel)); + } + return; + } + + mnode_t *node = sv->worldmodel->brush.nodes + node_id; + float d = dotf (node->plane, org)[0]; + if (d > 8) + node_id = node->children[0]; + else if (d < -8) + node_id = node->children[1]; + else { // go down both + add_to_fat_pvs (org, node->children[0], sv); + node_id = node->children[1]; + } + } +} + +static set_t * +fat_pvs (vec4f_t org, server_t *sv) +{ + if (!sv->fatpvs) { + sv->fatpvs = set_new_size (sv->worldmodel->brush.visleafs); + } + set_expand (sv->fatpvs, sv->worldmodel->brush.visleafs); + set_empty (sv->fatpvs); + + add_to_fat_pvs (org, 0, sv); + return sv->fatpvs; +} + static void write_entities (client_t *client, sizebuf_t *msg) { - //byte *pvs = 0; - //int i; + set_t *pvs = 0; int e; - //vec3_t org; + vec4f_t org; frame_t *frame; - entity_state_t *ent; + qtv_entity_t *ent; + qtv_leaf_t *el; entity_state_t *state; packet_entities_t *pack; server_t *sv = client->server; @@ -992,9 +1035,9 @@ write_entities (client_t *client, sizebuf_t *msg) frame = &client->frames[client->netchan.incoming_sequence & UPDATE_MASK]; // find the client's PVS - //clent = client->edict; - //VectorAdd (SVvector (clent, origin), SVvector (clent, view_ofs), org); - //pvs = SV_FatPVS (org); + org = client->state.es.origin; + org[2] += 22; //XXX standard spectator view offset + pvs = fat_pvs (org, sv); // put other visible entities into either a packet_entities or a nails // message @@ -1007,19 +1050,18 @@ write_entities (client_t *client, sizebuf_t *msg) e++, ent++) { if (!sv->ent_valid[e]) continue; - if (ent->number && ent->number != e) - qtv_printf ("%d %d\n", e, ent->number); -#if 0 + if (ent->e.number && ent->e.number != e) + qtv_printf ("%d %d\n", e, ent->e.number); if (pvs) { // ignore if not touching a PV leaf - for (i = 0; i < ent->num_leafs; i++) - if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i] & 7))) + for (el = ent->leafs; el; el = el->next) { + if (set_is_member (pvs, el->num)) break; + } - if (i == ent->num_leafs) + if (!el) continue; // not visible } -#endif // if (SV_AddNailUpdate (ent)) // continue; // added to the special update list @@ -1031,7 +1073,7 @@ write_entities (client_t *client, sizebuf_t *msg) state = &pack->entities[pack->num_entities]; pack->num_entities++; - *state = *ent; + *state = ent->e; state->flags = 0; } // encode the packet entities as a delta from the @@ -1204,7 +1246,7 @@ Client_Init (void) { size_t i; - ucmd_table = Hash_NewTable (251, ucmds_getkey, 0, 0); + ucmd_table = Hash_NewTable (251, ucmds_getkey, 0, 0, 0); for (i = 0; i < sizeof (ucmds) / sizeof (ucmds[0]); i++) Hash_Add (ucmd_table, &ucmds[i]); } @@ -1234,7 +1276,7 @@ Client_New (client_t *cl) MSG_WriteByte (&cl->netchan.message, sv->cdtrack); MSG_WriteByte (&cl->netchan.message, svc_stufftext); MSG_WriteString (&cl->netchan.message, - va ("fullserverinfo \"%s\"\n", + va (0, "fullserverinfo \"%s\"\n", Info_MakeString (sv->info, 0))); } diff --git a/qtv/source/connection.c b/qtv/source/connection.c index 0ff5d4464..e670880f3 100644 --- a/qtv/source/connection.c +++ b/qtv/source/connection.c @@ -46,7 +46,7 @@ #include "QF/hash.h" #include "QF/sys.h" -#include "connection.h" +#include "qtv/include/connection.h" static hashtab_t *connections; @@ -79,7 +79,7 @@ connection_compare (const void *_c1, const void *_c2, void *unused) void Connection_Init (void) { - connections = Hash_NewTable (1023, 0, connection_free, 0); + connections = Hash_NewTable (1023, 0, connection_free, 0, 0); Hash_SetHashCompare (connections, connection_get_hash, connection_compare); } diff --git a/qtv/source/qtv.c b/qtv/source/qtv.c index 7f12d013f..220d17c91 100644 --- a/qtv/source/qtv.c +++ b/qtv/source/qtv.c @@ -56,14 +56,15 @@ #include "QF/plugin/console.h" +#include "compat.h" +#include "netchan.h" + #include "qw/protocol.h" -#include "client.h" -#include "compat.h" -#include "connection.h" -#include "netchan.h" -#include "qtv.h" -#include "server.h" +#include "qtv/include/client.h" +#include "qtv/include/connection.h" +#include "qtv/include/qtv.h" +#include "qtv/include/server.h" #undef qtv_print @@ -74,20 +75,55 @@ static plugin_list_t server_plugin_list[] = { double realtime; -cvar_t *sv_timeout; +float sv_timeout; +static cvar_t sv_timeout_cvar = { + .name = "timeout", + .description = + "Sets the amount of time in seconds before a client is considered " + "disconnected if the server does not receive a packet", + .default_value = "60", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_timeout }, +}; cbuf_t *qtv_cbuf; cbuf_args_t *qtv_args; -static cvar_t *qtv_console_plugin; -static cvar_t *qtv_port; -static cvar_t *qtv_mem_size; +static char *qtv_console_plugin; +static cvar_t qtv_console_plugin_cvar = { + .name = "qtv_console_plugin", + .description = + "Plugin used for the console", + .default_value = "server", + .flags = CVAR_ROM, + .value = { .type = 0, .value = &qtv_console_plugin }, +}; +static int qtv_port; +static cvar_t qtv_port_cvar = { + .name = "qtv_port", + .description = + "udp port to use", + .default_value = 0, + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &qtv_port }, +}; +static float qtv_mem_size; +static cvar_t qtv_mem_size_cvar = { + .name = "qtv_mem_size", + .description = + "Amount of memory (in MB) to allocate for the " + PACKAGE_NAME + " heap", + .default_value = "8", + .flags = CVAR_ROM, + .value = { .type = &cexpr_float, .value = &qtv_mem_size }, +}; redirect_t qtv_redirected; client_t *qtv_redirect_client; dstring_t outputbuf = {&dstring_default_mem}; -static void +static __attribute__((format(PRINTF, 1, 0))) void qtv_print (const char *fmt, va_list args) { static int pending; @@ -201,29 +237,24 @@ qtv_end_redirect (void) qtv_redirect_client = 0; } -static void +static memhunk_t * qtv_memory_init (void) { int mem_size; void *mem_base; - qtv_mem_size = Cvar_Get ("qtv_mem_size", "8", CVAR_NONE, NULL, - "Amount of memory (in MB) to allocate for the " - PACKAGE_NAME " heap"); + Cvar_Register (&qtv_mem_size_cvar, 0, 0); - Cvar_SetFlags (qtv_mem_size, qtv_mem_size->flags | CVAR_ROM); - mem_size = (int) (qtv_mem_size->value * 1024 * 1024); - mem_base = malloc (mem_size); + mem_size = (int) (qtv_mem_size * 1024 * 1024); + mem_base = Sys_Alloc (mem_size); if (!mem_base) Sys_Error ("Can't allocate %d", mem_size); - Memory_Init (mem_base, mem_size); + return Memory_Init (mem_base, mem_size); } static void -qtv_shutdown (void) +qtv_shutdown (void *data) { - NET_Shutdown (); - Con_Shutdown (); Cbuf_Delete (qtv_cbuf); Cbuf_ArgsDelete (qtv_args); } @@ -238,10 +269,10 @@ qtv_quit_f (void) static void qtv_net_init (void) { - qtv_port = Cvar_Get ("qtv_port", va ("%d", PORT_QTV), 0, 0, - "udp port to use"); - sv_timeout = Cvar_Get ("sv_timeout", "60", 0, 0, "server timeout"); - NET_Init (qtv_port->int_val); + qtv_port_cvar.default_value = nva ("%d", PORT_QTV); + Cvar_Register (&qtv_port_cvar, 0, 0); + Cvar_Register (&sv_timeout_cvar, 0, 0); + NET_Init (qtv_port); Connection_Init (); net_realtime = &realtime; Netchan_Init (); @@ -253,23 +284,23 @@ qtv_init (void) qtv_cbuf = Cbuf_New (&id_interp); qtv_args = Cbuf_ArgsNew (); - Sys_RegisterShutdown (qtv_shutdown); + Sys_RegisterShutdown (qtv_shutdown, 0); Sys_Init (); - COM_ParseConfig (); - Cvar_Get ("cmd_warncmd", "1", CVAR_NONE, NULL, NULL); + COM_ParseConfig (qtv_cbuf); + cmd_warncmd = 1; - qtv_memory_init (); + memhunk_t *hunk = qtv_memory_init (); - QFS_Init ("qw"); + QFS_Init (hunk, "qw"); PI_Init (); - qtv_console_plugin = Cvar_Get ("qtv_console_plugin", "server", - CVAR_ROM, 0, "Plugin used for the console"); + Cvar_Register (&qtv_console_plugin_cvar, 0, 0); PI_RegisterPlugins (server_plugin_list); - Con_Init (qtv_console_plugin->string); + Con_Load (qtv_console_plugin); if (con_module) con_module->data->console->cbuf = qtv_cbuf; + Con_Init (); Sys_SetStdPrintf (qtv_print); qtv_sbar_init (); @@ -367,10 +398,10 @@ main (int argc, const char *argv[]) Sys_Printf ("Ohayou gozaimasu\n"); while (1) { + realtime = Sys_DoubleTime () + 1; Cbuf_Execute_Stack (qtv_cbuf); Sys_CheckInput (1, net_socket); - realtime = Sys_DoubleTime () + 1; qtv_read_packets (); diff --git a/qtv/source/sbar.c b/qtv/source/sbar.c index 73015d00a..e9d511dfd 100644 --- a/qtv/source/sbar.c +++ b/qtv/source/sbar.c @@ -32,61 +32,77 @@ #endif #include "QF/console.h" -#include "QF/view.h" #include "QF/va.h" #include "QF/plugin/console.h" -#include "client.h" -#include "server.h" +#include "QF/ui/view.h" + #include "sv_console.h" -#include "qtv.h" + +#include "qtv/include/client.h" +#include "qtv/include/server.h" +#include "qtv/include/qtv.h" static void -draw_clients (view_t *view) +draw_clients (view_t view) { - sv_view_t *sv_view = view->data; + sv_view_t *sv_view = Ent_GetComponent (view.id, server_view, view.reg); sv_sbar_t *sb = sv_view->obj; + view_pos_t rel = View_GetRel (view); const char *str; const char *s; char *d; - str = va ("[CL: %3d]", client_count); - for (s = str, d = sb->text + view->xrel; *s; s++) + str = va (0, "[CL: %3d]", client_count); + for (s = str, d = sb->text + rel.x; *s; s++) *d++ = *s; } static void -draw_servers (view_t *view) +draw_servers (view_t view) { - sv_view_t *sv_view = view->data; + sv_view_t *sv_view = Ent_GetComponent (view.id, server_view, view.reg); sv_sbar_t *sb = sv_view->obj; + view_pos_t rel = View_GetRel (view); const char *str; const char *s; char *d; - str = va ("[SV: %2d]", server_count); - for (s = str, d = sb->text + view->xrel; *s; s++) + str = va (0, "[SV: %2d]", server_count); + for (s = str, d = sb->text + rel.x; *s; s++) *d++ = *s; } void qtv_sbar_init (void) { - view_t *status; - view_t *view; + view_t status; + view_t view; if (!con_module || !con_module->data->console->status_view) return; - status = con_module->data->console->status_view; - view = view_new (0, 0, 8, 1, grav_northwest); - view->draw = draw_servers; - view->data = status->data; - view_add (status, view); + status = *con_module->data->console->status_view; + void *comp = Ent_GetComponent (status.id, server_window, status.reg); + sv_view_t sv_view = *(sv_view_t *) comp; + sv_view.setgeometry = 0; - view = view_new (8, 0, 9, 1, grav_northwest); - view->draw = draw_clients; - view->data = status->data; - view_add (status, view); + ecs_system_t viewsys = { .reg = status.reg, + .base = status.comp - view_href }; + view = View_New (viewsys, status); + View_SetPos (view, 0, 0); + View_SetLen (view, 8, 1); + View_SetGravity (view, grav_northwest); + sv_view.draw = draw_servers; + Ent_SetComponent (view.id, server_view, view.reg, &sv_view); + + view = View_New (viewsys, status); + View_SetPos (view, 8, 0); + View_SetLen (view, 9, 1); + View_SetGravity (view, grav_northwest); + sv_view.draw = draw_clients; + Ent_SetComponent (view.id, server_view, view.reg, &sv_view); + + View_UpdateHierarchy (status); } diff --git a/qtv/source/server.c b/qtv/source/server.c index 6375177a2..6e25723a8 100644 --- a/qtv/source/server.c +++ b/qtv/source/server.c @@ -54,10 +54,10 @@ #include "qw/protocol.h" -#include "client.h" -#include "connection.h" -#include "qtv.h" -#include "server.h" +#include "qtv/include/client.h" +#include "qtv/include/connection.h" +#include "qtv/include/qtv.h" +#include "qtv/include/server.h" int server_count; static hashtab_t *server_hash; @@ -124,7 +124,7 @@ server_compare (const void *a, const void *b) } static void -setup_sub_message (qmsg_t *msg, qmsg_t *sub, sizebuf_t *buf, int len) +setup_sub_message (qmsg_t *msg, qmsg_t *sub, sizebuf_t *buf, unsigned len) { memset (sub, 0, sizeof (qmsg_t)); memset (buf, 0, sizeof (sizebuf_t)); @@ -274,7 +274,7 @@ server_handler (connection_t *con, void *object) if (!Netchan_Process (&sv->netchan)) return; if (0) { - int i; + unsigned i; for (i = 0; i < net_message->message->cursize; i++) qtv_printf ("%c%02x", (i % 16) ? ' ' : '\n', @@ -449,10 +449,11 @@ sv_new_f (void) sv->name = strdup (name); sv->address = strdup (address); sv->adr = adr; - sv->qport = qport->int_val; + sv->qport = qport; sv->info = Info_ParseString ("", MAX_INFO_STRING, 0); Info_SetValueForStarKey (sv->info, "*ver", - va ("%s QTV %s", QW_VERSION, PACKAGE_VERSION), 0); + va (0, "%s QTV %s", QW_VERSION, PACKAGE_VERSION), + 0); Info_SetValueForStarKey (sv->info, "*qsg_version", QW_QSG_VERSION, 0); Info_SetValueForKey (sv->info, "name", "QTV Proxy", 0); Hash_Add (server_hash, sv); @@ -493,7 +494,7 @@ sv_list_f (void) qtv_printf ("no servers\n"); return; } - list = malloc (count * sizeof (server_t **)); + list = malloc (count * sizeof (server_t *)); for (l = &servers, count = 0; *l; l = &(*l)->next, count++) list[count] = *l; qsort (list, count, sizeof (*list), server_compare); @@ -508,7 +509,7 @@ sv_list_f (void) } static void -server_shutdown (void) +server_shutdown (void *data) { Hash_FlushTable (server_hash); Hash_DelTable (server_hash); @@ -540,8 +541,8 @@ server_run (server_t *sv) void Server_Init (void) { - Sys_RegisterShutdown (server_shutdown); - server_hash = Hash_NewTable (61, server_get_key, server_free, 0); + Sys_RegisterShutdown (server_shutdown, 0); + server_hash = Hash_NewTable (61, server_get_key, server_free, 0, 0); Cmd_AddCommand ("sv_new", sv_new_f, "Add a new server"); Cmd_AddCommand ("sv_del", sv_del_f, "Remove an existing server"); Cmd_AddCommand ("sv_list", sv_list_f, "List available servers"); @@ -553,7 +554,7 @@ Server_Frame (void) server_t *sv; for (sv = servers; sv; sv = sv->next) { - if (realtime - sv->netchan.last_received > sv_timeout->value) { + if (realtime - sv->netchan.last_received > sv_timeout) { qtv_printf ("Server %s timed out\n", sv->name); server_drop (sv); return; // chain has changed, avoid segfaulting @@ -628,7 +629,7 @@ Server_Broadcast (server_t *sv, int reliable, int all, const byte *msg, void Server_BroadcastCommand (server_t *sv, const char *cmd) { - const char *msg = va ("%c%s", svc_stufftext, cmd); + const char *msg = va (0, "%c%s", svc_stufftext, cmd); int len = strlen (msg) + 1; Server_Broadcast (sv, 1, 1, (const byte *) msg, len); } diff --git a/qtv/source/sv_parse.c b/qtv/source/sv_parse.c index 48198e809..b0a080a5a 100644 --- a/qtv/source/sv_parse.c +++ b/qtv/source/sv_parse.c @@ -56,10 +56,108 @@ #include "qw/msg_ucmd.h" #include "qw/protocol.h" -#include "client.h" -#include "connection.h" -#include "qtv.h" -#include "server.h" +#include "qtv/include/client.h" +#include "qtv/include/connection.h" +#include "qtv/include/qtv.h" +#include "qtv/include/server.h" + +#define QTV_LEAFS 32 +typedef struct qtv_leaf_bucket_s { + struct qtv_leaf_bucket_s *next; + qtv_leaf_t qtv_leafs[QTV_LEAFS]; +} qtv_leaf_bucket_t; + +static qtv_leaf_bucket_t *qtv_leaf_buckets; +static qtv_leaf_bucket_t **qtv_leaf_bucket_tail = &qtv_leaf_buckets; +static qtv_leaf_t *free_qtv_leaf_list; + +static qtv_leaf_t * +alloc_qtv_leaf (void) +{ + qtv_leaf_bucket_t *bucket; + qtv_leaf_t *leaf; + int i; + + if ((leaf = free_qtv_leaf_list)) { + free_qtv_leaf_list = leaf->next; + leaf->next = 0; + return leaf; + } + + bucket = malloc (sizeof (qtv_leaf_bucket_t)); + bucket->next = 0; + *qtv_leaf_bucket_tail = bucket; + qtv_leaf_bucket_tail = &bucket->next; + + for (leaf = bucket->qtv_leafs, i = 0; i < QTV_LEAFS - 1; i++, leaf++) + leaf->next = leaf + 1; + leaf->next = 0; + free_qtv_leaf_list = bucket->qtv_leafs; + + return alloc_qtv_leaf (); +} + +static void +free_qtv_leafs (qtv_leaf_t **leafs) +{ + qtv_leaf_t **l; + + for (l = leafs; *l; l = &(*l)->next) + ; + *l = free_qtv_leaf_list; + free_qtv_leaf_list = *leafs; + *leafs = 0; +} + +static void +sv_unlink_entity (server_t *sv, qtv_entity_t *ent) +{ + free_qtv_leafs (&ent->leafs); +} + +static void +sv_find_touched_leafs (server_t *sv, qtv_entity_t *ent, int node_id) +{ + // add an efrag if the node is a leaf + if (node_id < 0) { + mleaf_t *leaf = sv->worldmodel->brush.leafs + ~node_id; + if (leaf->contents == CONTENTS_SOLID) { + return; + } + qtv_leaf_t *ent_leaf = alloc_qtv_leaf (); + ent_leaf->num = ~node_id - 1; + ent_leaf->next = ent->leafs; + ent->leafs = ent_leaf; + return; + } + + vec3_t emins, emaxs; + VectorAdd (ent->e.origin, ent->model->mins, emins); + VectorAdd (ent->e.origin, ent->model->maxs, emaxs); + + mnode_t *node = sv->worldmodel->brush.nodes + node_id; + plane_t *splitplane = (plane_t *) &node->plane; + int sides = BOX_ON_PLANE_SIDE (emins, emaxs, splitplane); + if (sides & 1) { + sv_find_touched_leafs (sv, ent, node->children[0]); + } + if (sides & 2) { + sv_find_touched_leafs (sv, ent, node->children[1]); + } +} + +static void +sv_link_entity (server_t *sv, qtv_entity_t *ent) +{ + sv_unlink_entity (sv, ent); + if (ent->model_index != ent->e.modelindex) { + ent->model_index = ent->e.modelindex; + ent->model = Mod_ForName (sv->modellist[ent->model_index - 1], false); + } + if (ent->model) { + sv_find_touched_leafs (sv, ent, 0); + } +} static void sv_serverdata (server_t *sv, qmsg_t *msg) @@ -118,7 +216,7 @@ sv_serverdata (server_t *sv, qmsg_t *msg) MSG_WriteByte (&sv->netchan.message, qtv_stringcmd); MSG_WriteString (&sv->netchan.message, - va ("soundlist %i %i", sv->spawncount, 0)); + va (0, "soundlist %i %i", sv->spawncount, 0)); sv->next_run = realtime; } @@ -147,11 +245,11 @@ sv_soundlist (server_t *sv, qmsg_t *msg) if (n) { MSG_WriteByte (&sv->netchan.message, qtv_stringcmd); MSG_WriteString (&sv->netchan.message, - va ("soundlist %d %d", sv->spawncount, n)); + va (0, "soundlist %d %d", sv->spawncount, n)); } else { MSG_WriteByte (&sv->netchan.message, qtv_stringcmd); MSG_WriteString (&sv->netchan.message, - va ("modellist %d %d", sv->spawncount, 0)); + va (0, "modellist %d %d", sv->spawncount, 0)); } sv->next_run = realtime; } @@ -177,17 +275,17 @@ sv_modellist (server_t *sv, qmsg_t *msg) } sv->modellist[n] = strdup (str); if (!strcmp (sv->modellist[n], "progs/player.mdl")) - sv->playermodel = n; + sv->playermodel = n + 1; } n = MSG_ReadByte (msg); if (n) { MSG_WriteByte (&sv->netchan.message, qtv_stringcmd); MSG_WriteString (&sv->netchan.message, - va ("modellist %d %d", sv->spawncount, n)); + va (0, "modellist %d %d", sv->spawncount, n)); } else { MSG_WriteByte (&sv->netchan.message, qtv_stringcmd); MSG_WriteString (&sv->netchan.message, - va ("prespawn %d 0 0", sv->spawncount)); + va (0, "prespawn %d 0 0", sv->spawncount)); sv->signon = 1; } sv->next_run = realtime; @@ -213,7 +311,7 @@ sv_skins_f (server_t *sv) // to get everything ready at the last miniute before we start getting // actual in-game update messages MSG_WriteByte (&sv->netchan.message, qtv_stringcmd); - MSG_WriteString (&sv->netchan.message, va ("begin %d", sv->spawncount)); + MSG_WriteString (&sv->netchan.message, va (0, "begin %d", sv->spawncount)); sv->next_run = realtime; sv->connected = 2; sv->delta = -1; @@ -429,7 +527,8 @@ sv_packetentities (server_t *sv, qmsg_t *msg, int delta) } newp->entities[newindex] = oldp->entities[oldindex]; num = newp->entities[newindex].number; - sv->entities[num] = newp->entities[newindex]; + sv->entities[num].e = newp->entities[newindex]; + sv_link_entity (sv, &sv->entities[num]); sv->ent_valid[num] = 1; newindex++; oldindex++; @@ -456,7 +555,8 @@ sv_packetentities (server_t *sv, qmsg_t *msg, int delta) } newp->entities[newindex] = oldp->entities[oldindex]; num = newp->entities[newindex].number; - sv->entities[num] = newp->entities[newindex]; + sv->entities[num].e = newp->entities[newindex]; + sv_link_entity (sv, &sv->entities[num]); sv->ent_valid[num] = 1; newindex++; oldindex++; @@ -483,7 +583,8 @@ sv_packetentities (server_t *sv, qmsg_t *msg, int delta) } newp->entities[newindex] = sv->baselines[newnum]; sv_parse_delta (msg, word, &newp->entities[newindex]); - sv->entities[newnum] = newp->entities[newindex]; + sv->entities[newnum].e = newp->entities[newindex]; + sv_link_entity (sv, &sv->entities[newnum]); newindex++; continue; } @@ -499,7 +600,8 @@ sv_packetentities (server_t *sv, qmsg_t *msg, int delta) } newp->entities[newindex] = oldp->entities[oldindex]; sv_parse_delta (msg, word, &newp->entities[newindex]); - sv->entities[newnum] = newp->entities[newindex]; + sv->entities[newnum].e = newp->entities[newindex]; + sv_link_entity (sv, &sv->entities[newnum]); sv->ent_valid[newnum] = 1; newindex++; oldindex++; @@ -514,9 +616,9 @@ parse_player_delta (qmsg_t *msg, plent_state_t *from, plent_state_t *to) int i; int flags; - flags = to->flags = MSG_ReadShort (msg); - MSG_ReadCoordV (msg, to->origin); - to->frame = (to->frame & 0xff00) | MSG_ReadByte (msg); + flags = to->es.flags = MSG_ReadShort (msg); + MSG_ReadCoordV (msg, (vec_t*)&to->es.origin);//FIXME + to->es.frame = (to->es.frame & 0xff00) | MSG_ReadByte (msg); if (flags & PF_MSEC) to->msec = MSG_ReadByte (msg); // qtv_printf ("%02x\n", msg->message->data[msg->readcount]); @@ -524,36 +626,36 @@ parse_player_delta (qmsg_t *msg, plent_state_t *from, plent_state_t *to) MSG_ReadDeltaUsercmd (msg, &from->cmd, &to->cmd); for (i = 0; i < 3; i++) { if (flags & (PF_VELOCITY1 << i)) - to->velocity[i] = (short) MSG_ReadShort (msg); + to->es.velocity[i] = (short) MSG_ReadShort (msg); } if (flags & PF_MODEL) - to->modelindex = MSG_ReadByte (msg); + to->es.modelindex = MSG_ReadByte (msg); if (flags & PF_SKINNUM) - to->skinnum = MSG_ReadByte (msg); + to->es.skinnum = MSG_ReadByte (msg); if (flags & PF_EFFECTS) - to->effects = (to->effects & 0xff00) | MSG_ReadByte (msg);; + to->es.effects = (to->es.effects & 0xff00) | MSG_ReadByte (msg); if (flags & PF_WEAPONFRAME) - to->weaponframe = MSG_ReadByte (msg); + to->es.weaponframe = MSG_ReadByte (msg); if (flags & PF_QF) { int bits; bits = MSG_ReadByte (msg); if (bits & PF_ALPHA) - to->alpha = MSG_ReadByte (msg); + to->es.alpha = MSG_ReadByte (msg); if (bits & PF_SCALE) - to->scale = MSG_ReadByte (msg); + to->es.scale = MSG_ReadByte (msg); if (bits & PF_EFFECTS2) - to->effects = (to->effects & 0x00ff) - | (MSG_ReadByte (msg) << 8); + to->es.effects = (to->es.effects & 0x00ff) + | (MSG_ReadByte (msg) << 8); if (bits & PF_GLOWSIZE) - to->glow_size = MSG_ReadByte (msg); + to->es.glow_size = MSG_ReadByte (msg); if (bits & PF_GLOWCOLOR) - to->glow_color = MSG_ReadByte (msg); + to->es.glow_color = MSG_ReadByte (msg); if (bits & PF_COLORMOD) - to->colormod = MSG_ReadByte (msg); + to->es.colormod = MSG_ReadByte (msg); if (bits & PF_FRAME2) - to->frame = (to->frame & 0xff) - | (MSG_ReadByte (msg) << 8); + to->es.frame = (to->es.frame & 0xff) + | (MSG_ReadByte (msg) << 8); } } @@ -567,12 +669,12 @@ sv_playerinfo (server_t *sv, qmsg_t *msg) int fromind, toind; static plent_state_t null_player_state; - if (!null_player_state.alpha) { - null_player_state.alpha = 255; - null_player_state.scale = 16; - null_player_state.glow_size = 0; - null_player_state.glow_color = 254; - null_player_state.colormod = 255; + if (!null_player_state.es.alpha) { + null_player_state.es.alpha = 255; + null_player_state.es.scale = 16; + null_player_state.es.glow_size = 0; + null_player_state.es.glow_color = 254; + null_player_state.es.colormod = 255; } fromind = MSG_ReadByte (msg); toind = sv->netchan.incoming_sequence & UPDATE_MASK; @@ -752,7 +854,8 @@ parse_baseline (qmsg_t *msg, entity_state_t *ent) ent->frame = MSG_ReadByte (msg); ent->colormap = MSG_ReadByte (msg); ent->skinnum = MSG_ReadByte (msg); - MSG_ReadCoordAngleV (msg, ent->origin, ent->angles); + MSG_ReadCoordAngleV (msg, (vec_t*)&ent->origin, ent->angles); //FIXME + ent->origin[3] = 1; ent->colormod = 255; ent->alpha = 255; ent->scale = 16; @@ -1055,6 +1158,7 @@ sv_parse (server_t *sv, qmsg_t *msg, int reliable) break; case svc_modellist: sv_modellist (sv, msg); + sv->worldmodel = Mod_ForName (sv->modellist[0], false); send = 0; break; diff --git a/qw/Makefile.am b/qw/Makefile.am deleted file mode 100644 index 77e3cbf5b..000000000 --- a/qw/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -## Process this file with automake to produce Makefile.in -AUTOMAKE_OPTIONS= foreign - -SUBDIRS= include source diff --git a/qw/Makemodule.am b/qw/Makemodule.am new file mode 100644 index 000000000..9fca70d9b --- /dev/null +++ b/qw/Makemodule.am @@ -0,0 +1,2 @@ +include qw/include/Makemodule.am +include qw/source/Makemodule.am diff --git a/qw/include/Makefile.am b/qw/include/Makefile.am deleted file mode 100644 index 67535b195..000000000 --- a/qw/include/Makefile.am +++ /dev/null @@ -1,9 +0,0 @@ -## Process this file with automake to produce Makefile.in -AUTOMAKE_OPTIONS= foreign - -EXTRA_DIST = \ - chase.h cl_cam.h cl_chat.h cl_demo.h cl_ents.h cl_http.h cl_input.h \ - cl_main.h cl_parse.h cl_pred.h cl_skin.h cl_slist.h cl_tent.h \ - client.h crudefile.h game.h host.h map_cfg.h server.h sv_gib.h \ - sv_demo.h sv_pr_cmds.h sv_pr_cpqw.h sv_pr_qwe.h sv_progs.h sv_qtv.h \ - sv_recorder.h diff --git a/qw/include/Makemodule.am b/qw/include/Makemodule.am new file mode 100644 index 000000000..4a937f07b --- /dev/null +++ b/qw/include/Makemodule.am @@ -0,0 +1,26 @@ +EXTRA_DIST += \ + qw/include/cl_cam.h \ + qw/include/cl_chat.h \ + qw/include/cl_demo.h \ + qw/include/cl_ents.h \ + qw/include/cl_http.h \ + qw/include/cl_input.h \ + qw/include/cl_main.h \ + qw/include/cl_parse.h \ + qw/include/cl_pred.h \ + qw/include/cl_skin.h \ + qw/include/cl_slist.h \ + qw/include/client.h \ + qw/include/crudefile.h \ + qw/include/game.h \ + qw/include/host.h \ + qw/include/map_cfg.h \ + qw/include/server.h \ + qw/include/sv_gib.h \ + qw/include/sv_demo.h \ + qw/include/sv_pr_cmds.h \ + qw/include/sv_pr_cpqw.h \ + qw/include/sv_pr_qwe.h \ + qw/include/sv_progs.h \ + qw/include/sv_qtv.h \ + qw/include/sv_recorder.h diff --git a/qw/include/cl_cam.h b/qw/include/cl_cam.h index 2bd74f644..cd7d7aeb2 100644 --- a/qw/include/cl_cam.h +++ b/qw/include/cl_cam.h @@ -43,9 +43,9 @@ extern int spec_track; // player# of who we are tracking extern int ideal_track; void Cam_Lock (int playernum); -int Cam_TrackNum (void); -qboolean Cam_DrawViewModel(void); -qboolean Cam_DrawPlayer(int playernum); +int Cam_TrackNum (void) __attribute__((pure)); +bool Cam_DrawViewModel(void) __attribute__((pure)); +bool Cam_DrawPlayer(int playernum) __attribute__((pure)); void Cam_Track(usercmd_t *cmd); void Cam_FinishMove(usercmd_t *cmd); void Cam_Reset(void); diff --git a/qw/include/cl_chat.h b/qw/include/cl_chat.h index 2a26c075a..585c21b93 100644 --- a/qw/include/cl_chat.h +++ b/qw/include/cl_chat.h @@ -33,7 +33,7 @@ typedef struct ignore_s { const char *lastname; } ignore_t; -qboolean CL_Chat_Allow_Message (const char *str); +bool CL_Chat_Allow_Message (const char *str); void CL_Chat_User_Disconnected (int uid); void CL_Chat_Check_Name (const char *name, int slot); void CL_Chat_Flush_Ignores (void); diff --git a/qw/include/cl_demo.h b/qw/include/cl_demo.h index 190444785..b11f89583 100644 --- a/qw/include/cl_demo.h +++ b/qw/include/cl_demo.h @@ -40,7 +40,7 @@ void CL_Record (const char *argv1, int track); // track ignored void CL_Demo_Init (void); -extern struct cvar_s *demo_speed; -extern struct cvar_s *demo_gzip; +extern float demo_speed; +extern int demo_gzip; #endif diff --git a/qw/include/cl_ents.h b/qw/include/cl_ents.h index 275cd04f6..6aef9b6d6 100644 --- a/qw/include/cl_ents.h +++ b/qw/include/cl_ents.h @@ -33,22 +33,17 @@ void CL_SetSolidPlayers (int playernum); void CL_ClearPredict (void); -void CL_SetUpPlayerPrediction(qboolean dopred); -void CL_TransformEntity (struct entity_s * ent, const vec3_t angles, - qboolean force); +void CL_SetUpPlayerPrediction(bool dopred); +void CL_ClearEnts (void); void CL_EmitEntities (void); void CL_ClearProjectiles (void); -void CL_ParseProjectiles (qboolean nail2); -void CL_ParsePacketEntities (qboolean delta); +void CL_ParsePacketEntities (bool delta); void CL_SetSolidEntities (void); void CL_ParsePlayerinfo (void); void CL_Ents_Init (void); +struct entity_s CL_GetEntity (int num); -extern struct cvar_s *cl_deadbodyfilter; -extern struct cvar_s *cl_gibfilter; - -extern entity_t cl_player_ents[]; -extern entity_t cl_flag_ents[]; -extern entity_t cl_packet_ents[]; +extern int cl_deadbodyfilter; +extern int cl_gibfilter; #endif diff --git a/qw/include/cl_input.h b/qw/include/cl_input.h index a722967d6..95f1aef98 100644 --- a/qw/include/cl_input.h +++ b/qw/include/cl_input.h @@ -28,12 +28,12 @@ #ifndef _CL_INPUT_H #define _CL_INPUT_H -#include "QF/input.h" - #include "qw/protocol.h" -void CL_Input_Init (void); -void CL_Input_Init_Cvars (void); +struct cbuf_s; + +void CL_Init_Input (struct cbuf_s *cbuf); +void CL_Init_Input_Cvars (void); void CL_ClearStates (void); void CL_SendCmd (void); void CL_SendMove (usercmd_t *cmd); @@ -46,12 +46,6 @@ int CL_ReadFromServer (void); void CL_WriteToServer (usercmd_t *cmd); void CL_BaseMove (usercmd_t *cmd); -float CL_KeyState (kbutton_t *key); - -extern kbutton_t in_left, in_right, in_forward, in_back; -extern kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright; -extern kbutton_t in_use, in_jump, in_attack; -extern kbutton_t in_up, in_down; -extern int in_impulse; +extern int in_impulse; #endif diff --git a/qw/include/cl_main.h b/qw/include/cl_main.h index f7cd463f1..1caf66067 100644 --- a/qw/include/cl_main.h +++ b/qw/include/cl_main.h @@ -34,26 +34,31 @@ void CL_Init (void); void Host_WriteConfiguration (void); +int Host_ReadConfiguration (const char *cfg_name); void CL_EstablishConnection (const char *host); void CL_Disconnect (void); void CL_Disconnect_f (void); void CL_NextDemo (void); -qboolean CL_DemoBehind(void); +bool CL_DemoBehind(void); void CL_BeginServerConnect(void); -extern char emodel_name[], pmodel_name[], prespawn_name[], modellist_name[], soundlist_name[]; +#define emodel_name "emodel" +#define pmodel_name "pmodel" +#define prespawn_name "prespawn %i 0 %i" +#define modellist_name "modellist %i %i" +#define soundlist_name "soundlist %i %i" -extern struct cvar_s *cl_timeframes; -extern struct cvar_s *cl_predict_players; -extern struct cvar_s *cl_solid_players; -extern struct cvar_s *cl_autoexec; -extern struct cvar_s *cl_cshift_bonus; -extern struct cvar_s *cl_cshift_contents; -extern struct cvar_s *cl_cshift_damage; -extern struct cvar_s *cl_cshift_powerup; + +extern int cl_predict_players; +extern int cl_solid_players; +extern int cl_autoexec; +extern int cl_cshift_bonus; +extern int cl_cshift_contents; +extern int cl_cshift_damage; +extern int cl_cshift_powerup; extern struct gib_event_s *cl_player_health_e, *cl_chat_e; diff --git a/qw/include/cl_parse.h b/qw/include/cl_parse.h index 12bc89e12..9c67e6aa9 100644 --- a/qw/include/cl_parse.h +++ b/qw/include/cl_parse.h @@ -40,7 +40,6 @@ extern int parsecountmod; extern double parsecounttime; extern int cl_playerindex; extern int cl_flagindex; -extern int cl_spikeindex; extern int viewentity; extern int cl_h_playerindex; extern int cl_gib1index; @@ -51,8 +50,8 @@ int CL_CalcNet (void); void CL_ParseServerMessage (void); void CL_ParseClientdata (void); void CL_NewTranslation (int slot, struct skin_s *skin); -qboolean CL_CheckOrDownloadFile (const char *filename); -qboolean CL_IsUploading(void); +bool CL_CheckOrDownloadFile (const char *filename); +bool CL_IsUploading(void) __attribute__((pure)); void CL_NextUpload(void); void CL_FinishDownload (void); void CL_FailDownload (void); diff --git a/qw/include/cl_pred.h b/qw/include/cl_pred.h index 4136e9abd..e906f12a5 100644 --- a/qw/include/cl_pred.h +++ b/qw/include/cl_pred.h @@ -32,7 +32,7 @@ void CL_Prediction_Init_Cvars (void); void CL_PredictMove (void); -void CL_PredictUsercmd (player_state_t *from, player_state_t *to, usercmd_t *u, qboolean spectator); +void CL_PredictUsercmd (player_state_t *from, player_state_t *to, usercmd_t *u, bool spectator); #endif diff --git a/qw/include/client.h b/qw/include/client.h index 23d22e893..2dd37c878 100644 --- a/qw/include/client.h +++ b/qw/include/client.h @@ -34,8 +34,13 @@ #include "QF/zone.h" #include "QF/plugin/vid_render.h" +#include "QF/scene/entity.h" +#include "client/chase.h" #include "client/entities.h" +#include "client/input.h" +#include "client/state.h" +#include "client/view.h" #include "netchan.h" #include "qw/bothdefs.h" @@ -62,31 +67,6 @@ typedef struct player_state_s { } player_state_t; -typedef struct player_info_s { - int userid; - struct info_s *userinfo; - - // scoreboard information - struct info_key_s *name; - struct info_key_s *team; - struct info_key_s *chat; - float entertime; - int frags; - int ping; - byte pl; - - // skin information - int topcolor; - int bottomcolor; - struct info_key_s *skinname; - struct skin_s *skin; - - int spectator; - int stats[MAX_CL_STATS]; // health, etc - int prevcount; -} player_info_t; - - typedef struct { // generated on client side usercmd_t cmd; // cmd that generated the frame @@ -99,7 +79,7 @@ typedef struct { // reflects performing the // usercmd packet_entities_t packet_entities; - qboolean invalid; // true if the packet_entities delta was invalid + bool invalid; // true if the packet_entities delta was invalid } frame_t; #define MAX_DEMOS 8 @@ -155,16 +135,16 @@ typedef struct { char demos[MAX_DEMOS][MAX_DEMONAME]; // when not playing QFile *demofile; - qboolean demorecording; - qboolean demo_capture; - qboolean demoplayback; - qboolean demoplayback2; - qboolean findtrack; + bool demorecording; + int demo_capture; + bool demoplayback; + bool demoplayback2; + bool findtrack; int lastto; int lasttype; int prevtime; double basetime; - qboolean timedemo; + bool timedemo; double td_lastframe; // to meter out one message a frame int td_startframe; // host_framecount at start double td_starttime; // realtime at second frame of timedemo @@ -195,9 +175,7 @@ extern client_static_t cls; /* the client_state_t structure is wiped completely at every server signon */ -typedef struct { - qboolean loading; - +typedef struct client_state_s { int movemessages; // Since connecting to this server throw out // the first couple, so the player doesn't // accidentally do something the first frame @@ -207,40 +185,25 @@ typedef struct { int prev_sequence; // information for local display - int stats[MAX_CL_STATS]; // health, etc + int stats[MAX_CL_STATS]; // Health, etc float item_gettime[32]; // cl.time of aquiring item, for blinking float faceanimtime; // Use anim frame if cl.time < this - cshift_t cshifts[NUM_CSHIFTS]; // Color shifts for damage, powerups - cshift_t prev_cshifts[NUM_CSHIFTS]; // and content types - -// the client maintains its own idea of view angles, which are sent to the -// server each frame. And reset only at level change and teleport times - vec3_t viewangles; - // the client simulates or interpolates movement to get these values double time; // this is the time value that the client // is rendering at. always <= realtime - vec3_t simorg; - vec3_t simvel; - vec3_t simangles; +// the client maintains its own idea of view angles, which are sent to the +// server each frame. And reset only at level change and teleport times + viewstate_t viewstate; + movestate_t movestate; + chasestate_t chasestate; - vec3_t punchangle; // temporary view kick from weapon firing + bool paused; // Sent over by server + float crouch; // Local amount for smoothing stepups + bool inwater; -// pitch drifting vars - float idealpitch; - float pitchvel; - qboolean nodrift; - float driftmove; - double laststop; - - qboolean paused; // Sent over by server - int onground; // -1 when in air - float viewheight; - float crouch; // local amount for smoothing stepups - - int intermission; // don't change view angle, full screen, etc - int completed_time; // latched from time at intermission start + int intermission; // Don't change view angle, full screen, etc + int completed_time; // Latched from time at intermission start int servercount; // server identification for prespawns struct info_s *serverinfo; @@ -250,45 +213,38 @@ typedef struct { // can't render a frame yet double last_ping_request; // while showing scoreboard - double last_servermessage; /* information that is static for the entire time connected to a server */ char model_name[MAX_MODELS][MAX_QPATH]; char sound_name[MAX_SOUNDS][MAX_QPATH]; - struct model_s *model_precache[MAX_MODELS]; struct sfx_s *sound_precache[MAX_SOUNDS]; int nummodels; int numsounds; - struct plitem_s *edicts; - struct plitem_s *worldspawn; - char levelname[40]; // for display on solo scoreboard int spectator; int playernum; - int viewentity; + int viewentity; // cl_entitites[cl.viewentity] = player + unsigned protocol; float stdver; + int gametype; int maxclients; // serverinfo mirrors - int chase; int sv_cshifts; int no_pogo_stick; int teamplay; - int watervis; int fpd; int fbskins; // refresh related state - struct model_s *worldmodel; // cl_entitites[0].model - int num_entities; // stored bottom up in cl_entities array - entity_t viewent; // the weapon model + int num_entities; // held in cl_entities array int cdtrack; // cd audio // all player information - player_info_t players[MAX_CLIENTS]; + player_info_t *players; lightstyle_t lightstyle[MAX_LIGHTSTYLES]; } client_state_t; @@ -297,55 +253,32 @@ typedef struct { /* cvars */ -extern struct cvar_s *r_netgraph; -extern struct cvar_s *r_netgraph_alpha; -extern struct cvar_s *r_netgraph_box; -extern struct cvar_s *cl_upspeed; -extern struct cvar_s *cl_forwardspeed; -extern struct cvar_s *cl_backspeed; -extern struct cvar_s *cl_sidespeed; +extern int cl_netgraph; +extern int cl_netgraph_height; +extern float cl_netgraph_alpha; +extern int cl_netgraph_box; -extern struct cvar_s *cl_movespeedkey; +extern int cl_draw_locs; +extern int cl_shownet; -extern struct cvar_s *cl_yawspeed; -extern struct cvar_s *cl_pitchspeed; +extern char *cl_name; -extern struct cvar_s *cl_anglespeedkey; +extern int cl_model_crcs; -extern struct cvar_s *cl_draw_locs; -extern struct cvar_s *cl_shownet; -extern struct cvar_s *hud_sbar; -extern struct cvar_s *hud_sbar_separator; -extern struct cvar_s *hud_swap; +extern float rate; -extern struct cvar_s *cl_pitchdriftspeed; -extern struct cvar_s *lookspring; +extern char *skin; -extern struct cvar_s *m_pitch; -extern struct cvar_s *m_yaw; -extern struct cvar_s *m_forward; -extern struct cvar_s *m_side; +extern float cl_fb_players; -extern struct cvar_s *cl_name; - -extern struct cvar_s *cl_model_crcs; - -extern struct cvar_s *rate; - -extern struct cvar_s *hud_ping; -extern struct cvar_s *hud_pl; - -extern struct cvar_s *skin; - -extern struct cvar_s *cl_fb_players; +extern int hud_scoreboard_uid; extern client_state_t cl; -extern entity_t *cl_static_entities; -extern entity_t cl_entities[512]; -extern byte cl_entity_valid[2][512]; +extern entity_t cl_entities[512]; +extern byte cl_entity_valid[2][512]; -extern qboolean nomaster; +extern bool nomaster; extern char *server_version; // version of server we connected to extern double realtime; @@ -354,17 +287,15 @@ extern int fps_count; extern struct cbuf_s *cl_cbuf; extern struct cbuf_s *cl_stbuf; -void Cvar_Info (struct cvar_s *var); +struct cvar_s; +void Cvar_Info (void *data, const struct cvar_s *cvar); -void CL_NetGraph (void); -void CL_UpdateScreen (double realtime); +void CL_NetGraph_Init (void); +void CL_NetGraph_Init_Cvars (void); +void CL_NetUpdate (void); void CL_SetState (cactive_t state); -void V_ParseDamage (void); - -void V_PrepBlend (void); - void CL_Cmd_ForwardToServer (void); void CL_Cmd_Init (void); @@ -373,6 +304,4 @@ void CL_RSShot_f (void); #define RSSHOT_WIDTH 320 #define RSSHOT_HEIGHT 200 -extern struct dstring_s *centerprint; - #endif // _CLIENT_H diff --git a/qw/include/crudefile.h b/qw/include/crudefile.h index 384b6126c..be827be33 100644 --- a/qw/include/crudefile.h +++ b/qw/include/crudefile.h @@ -35,6 +35,6 @@ void CF_Close (int desc); const char * CF_Read (int desc); int CF_Write (int desc, const char *buf); int CF_EOF (int desc); -int CF_Quota (void); +int CF_Quota (void) __attribute__((pure)); #endif // _CRUDEFILE_H diff --git a/qw/include/game.h b/qw/include/game.h index 003855f4e..95b1bed1b 100644 --- a/qw/include/game.h +++ b/qw/include/game.h @@ -33,7 +33,6 @@ #include "QF/qtypes.h" #include "QF/qdefs.h" -extern struct cvar_s *registered; void Game_Init (void); void Game_Init_Cvars (void); diff --git a/qw/include/host.h b/qw/include/host.h index 87404b019..beda8bec1 100644 --- a/qw/include/host.h +++ b/qw/include/host.h @@ -38,10 +38,8 @@ #define MAX_NUM_ARGVS 50 -extern qboolean noclip_anglehack; - -extern struct cvar_s *sys_ticrate; -extern struct cvar_s *password; +extern float sys_ticrate; +extern char *password; extern double host_frametime; // Tonik @@ -50,13 +48,11 @@ extern int host_framecount; // incremented every frame, never reset void Host_ServerFrame (void); void Host_InitCommands (void); void Host_Init (void); -void Host_Shutdown(void); -void Host_Error (const char *error, ...) __attribute__((format(printf,1,2))); -void Host_EndGame (const char *message, ...) __attribute__((format(printf,1,2))); +void Host_Error (const char *error, ...) __attribute__((format(PRINTF,1,2), noreturn)); +void Host_EndGame (const char *message, ...) __attribute__((format(PRINTF,1,2), noreturn)); void Host_Frame (float time); void Host_Quit_f (void); -void Host_ClientCommands (const char *fmt, ...) __attribute__((format(printf,1,2))); -void Host_ShutdownServer (qboolean crash); +void Host_ClientCommands (const char *fmt, ...) __attribute__((format(PRINTF,1,2))); typedef struct { diff --git a/qw/include/server.h b/qw/include/server.h index dbda77e12..a5d0be5f8 100644 --- a/qw/include/server.h +++ b/qw/include/server.h @@ -59,7 +59,7 @@ typedef enum { // initializing (precache commands, static sounds / objects, etc) typedef struct { - qboolean active; // false when server is going down + bool active; // false when server is going down server_state_t state; // precache commands are valid only during load double time; @@ -67,13 +67,13 @@ typedef struct { int lastcheck; // used by PF_checkclient double lastchecktime; // for monster ai - qboolean paused; // are we paused? + bool paused; // are we paused? //check player/eyes models for hacks unsigned int model_player_checksum; unsigned int eyes_player_checksum; - char name[64]; // map name + char *name; // map name char modelname[MAX_QPATH]; // maps/.bsp, for model_precache[0] struct model_s *worldmodel; const char *model_precache[MAX_MODELS]; // NULL terminated @@ -81,12 +81,12 @@ typedef struct { const char *lightstyles[MAX_LIGHTSTYLES]; struct model_s *models[MAX_MODELS]; - int num_edicts; // increases towards MAX_EDICTS + unsigned num_edicts; // increases towards MAX_EDICTS struct edict_s *edicts; // can NOT be array indexed, because // struct edict_s is variable sized, but can // be used to reference the world ent - byte *pvs, *phs; // fully expanded and decompressed + struct set_s *pvs, *phs; // fully expanded and decompressed //antilag float lagentsfrac; @@ -142,7 +142,7 @@ typedef struct { double senttime; float ping_time; vec3_t playerpositions[MAX_CLIENTS]; - qboolean playerpresent[MAX_CLIENTS]; + bool playerpresent[MAX_CLIENTS]; packet_entities_t entities; packet_players_t players; } client_frame_t; @@ -183,17 +183,17 @@ typedef enum { typedef struct client_s { sv_client_state_t state; int ping; // fake ping for server clients - qboolean prespawned; - qboolean spawned; + bool prespawned; + bool spawned; int spectator; // non-interactive - qboolean sendinfo; // at end of frame, send info to all + bool sendinfo; // at end of frame, send info to all // this prevents malicious multiple // broadcasts float lastnametime; // time of last name change int lastnamecount; // time of last name change - qboolean drop; // lose this guy next opportunity + bool drop; // lose this guy next opportunity int lossage; // loss percentage int userid; // identifying number @@ -223,7 +223,7 @@ typedef struct client_s { char stufftext_buf[MAX_STUFFTEXT]; double connection_started; // or time of disconnect for zombies - qboolean send_message; // set on frames a datagram arived on + bool send_message; // set on frames a datagram arived on //antilag stuff laggedentinfo_t laggedents[MAX_CLIENTS]; @@ -252,8 +252,9 @@ typedef struct client_s { QFile *upload; struct dstring_s *uploadfn; + int upload_started; netadr_t snap_from; - qboolean remote_snap; + bool remote_snap; //===== NETWORK ============ int chokecount; @@ -270,7 +271,7 @@ typedef struct client_s { // getting kicked off by the server operator // a program error, like an overflowed reliable buffer -extern qboolean rcon_from_user; // current command is a from a user +extern bool rcon_from_user; // current command is a from a user //============================================================================ @@ -304,8 +305,8 @@ typedef struct { int spawncount; // number of servers spawned since start, // used to check late spawns client_t clients[MAX_CLIENTS]; - int maxclients; - int num_clients; + unsigned maxclients; + unsigned num_clients; int serverflags; // episode completion information void (*phys_client) (struct edict_s *ent, int num); @@ -425,21 +426,20 @@ typedef enum { //============================================================================ // FIXME: declare exported variables in their own relevant .h -extern struct cvar_s *sv_hide_version_info; -extern struct cvar_s *sv_highchars; +extern int sv_hide_version_info; +extern int sv_highchars; -extern struct cvar_s *sv_mintic, *sv_maxtic; -extern struct cvar_s *sv_maxspeed; +extern float sv_mintic; +extern float sv_maxtic; +extern float sv_maxspeed; -extern struct cvar_s *sv_timeout; +extern float sv_timeout; extern netadr_t master_adr[MAX_MASTERS]; // address of the master server -extern struct cvar_s *spawn; -extern struct cvar_s *teamplay; -extern struct cvar_s *deathmatch; -extern struct cvar_s *fraglimit; -extern struct cvar_s *timelimit; +extern int teamplay; +extern int deathmatch; +extern int timelimit; extern server_static_t svs; // persistant server info extern server_t sv; // local server @@ -452,7 +452,7 @@ extern char localmodels[MAX_MODELS][5]; // inline model names for precache extern struct info_s *localinfo; -extern int host_hunklevel; +extern size_t host_hunklevel; extern QFile *sv_logfile; extern QFile *sv_fraglogfile; @@ -474,8 +474,8 @@ void SV_Progs_Init_Cvars (void); void SV_PR_Cmds_Init (void); void SV_LoadProgs (void); -void Con_Printf (const char *fmt, ...) __attribute__((format(printf,1,2))); -void Con_DPrintf (const char *fmt, ...) __attribute__((format(printf,1,2))); +void Con_Printf (const char *fmt, ...) __attribute__((format(PRINTF,1,2))); +void Con_DPrintf (const char *fmt, ...) __attribute__((format(PRINTF,1,2))); extern struct clip_hull_s *pf_hull_list[]; @@ -483,36 +483,40 @@ extern struct clip_hull_s *pf_hull_list[]; // sv_main.c // +void SV_OutOfBand (netadr_t adr, unsigned length, byte *data); +void SV_OutOfBandPrint (netadr_t adr, const char *format, ...) + __attribute__ ((format (PRINTF,2,3))); + + client_t *SV_AllocClient (int spectator, int server); void SV_SavePenaltyFilter (client_t *cl, filtertype_t type, double pentime); double SV_RestorePenaltyFilter (client_t *cl, filtertype_t type); -void SV_Shutdown (void); +void SV_Shutdown (void *data); void SV_Frame (float time); void SV_FinalMessage (const char *message); void SV_DropClient (client_t *drop); -int SV_CalcPing (client_t *cl); +int SV_CalcPing (client_t *cl) __attribute__((pure)); void SV_FullClientUpdate (client_t *client, sizebuf_t *buf); void SV_FullClientUpdateToClient (client_t *client, backbuf_t *backbuf); -int SV_ModelIndex (const char *name); +int SV_ModelIndex (const char *name) __attribute__((pure)); -qboolean SV_CheckBottom (struct edict_s *ent); -qboolean SV_movestep (struct edict_s *ent, const vec3_t move, - qboolean relink); +bool SV_CheckBottom (struct edict_s *ent); +bool SV_movestep (struct edict_s *ent, const vec3_t move, bool relink); void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg); struct progs_s; -void SV_MoveToGoal (struct progs_s *pr); +void SV_MoveToGoal (struct progs_s *pr, void *data); void SV_SaveSpawnparms (void); void SV_Physics_Client (struct edict_s *ent); void SV_PreRunCmd (void); -void SV_RunCmd (usercmd_t *ucmd, qboolean inside); +void SV_RunCmd (usercmd_t *ucmd, bool inside); void SV_PostRunCmd (void); void SV_SetupUserCommands (void); void SV_ExecuteUserCommand (const char *s); @@ -538,10 +542,11 @@ void SV_FlushSignon (void); // void SV_ProgStartFrame (void); void SV_Physics (void); +void SV_Physics_Init_Cvars (void); void SV_CheckVelocity (struct edict_s *ent); void SV_AddGravity (struct edict_s *ent); void SV_FinishGravity (struct edict_s *ent, vec3_t move); -qboolean SV_RunThink (struct edict_s *ent); +bool SV_RunThink (struct edict_s *ent); void SV_Physics_Toss (struct edict_s *ent); void SV_RunNewmis (void); void SV_SetMoveVars(void); @@ -549,22 +554,22 @@ struct trace_s; int SV_FlyMove (struct edict_s *ent, float time, struct trace_s *steptrace); struct trace_s SV_PushEntity (struct edict_s *ent, vec3_t push, unsigned traceflags); -int SV_EntCanSupportJump (struct edict_s *ent); +int SV_EntCanSupportJump (struct edict_s *ent) __attribute__((pure)); // // sv_send.c // -void SV_Print (const char *fmt, va_list args); -void SV_Printf (const char *fmt, ...) __attribute__((format(printf,1,2))); +void SV_Print (const char *fmt, va_list args) __attribute__((format(PRINTF, 1, 0))); +void SV_Printf (const char *fmt, ...) __attribute__((format(PRINTF,1,2))); void SV_SendClientMessages (void); void SV_GetStats (struct edict_s *ent, int spectator, int stats[]); void SV_Multicast (const vec3_t origin, int to); void SV_StartSound (struct edict_s *entity, int channel, const char *sample, int volume, float attenuation); -void SV_ClientPrintf (int recorder, client_t *cl, int level, const char *fmt, ...) __attribute__((format(printf,4,5))); -void SV_BroadcastPrintf (int level, const char *fmt, ...) __attribute__((format(printf,2,3))); -void SV_BroadcastCommand (const char *fmt, ...) __attribute__((format(printf,1,2))); +void SV_ClientPrintf (int recorder, client_t *cl, int level, const char *fmt, ...) __attribute__((format(PRINTF,4,5))); +void SV_BroadcastPrintf (int level, const char *fmt, ...) __attribute__((format(PRINTF,2,3))); +void SV_BroadcastCommand (const char *fmt, ...) __attribute__((format(PRINTF,1,2))); void SV_SendMessagesToAll (void); void SV_FindModelNumbers (void); @@ -611,7 +616,7 @@ extern struct dstring_s outputbuf; // sv_ccmds.c // void SV_Status_f (void); -const char *SV_Current_Map (void); +const char *SV_Current_Map (void) __attribute__((pure)); void SV_SetLocalinfo (const char *key, const char *value); @@ -624,39 +629,40 @@ void SV_WriteEntitiesToClient (delta_t *delta, sizebuf_t *msg); // sv_nchan.c // -void Cvar_Info (struct cvar_s *var); +struct cvar_s; +void Cvar_Info (void *data, const struct cvar_s *cvar); -extern struct cvar_s *sv_antilag; -extern struct cvar_s *sv_antilag_frac; -extern struct cvar_s *sv_timecheck_fuzz; -extern struct cvar_s *sv_timecheck_decay; -extern struct cvar_s *sv_maxrate; -extern struct cvar_s *sv_timestamps; -extern struct cvar_s *sv_timefmt; -extern struct cvar_s *sv_phs; -extern struct cvar_s *sv_maxvelocity; -extern struct cvar_s *sv_gravity; -extern struct cvar_s *sv_jump_any; -extern struct cvar_s *sv_aim; -extern struct cvar_s *sv_stopspeed; -extern struct cvar_s *sv_spectatormaxspeed; -extern struct cvar_s *sv_accelerate; -extern struct cvar_s *sv_airaccelerate; -extern struct cvar_s *sv_wateraccelerate; -extern struct cvar_s *sv_friction; -extern struct cvar_s *sv_waterfriction; -extern struct cvar_s *pr_double_remove; -extern struct cvar_s *allow_download; -extern struct cvar_s *allow_download_skins; -extern struct cvar_s *allow_download_models; -extern struct cvar_s *allow_download_sounds; -extern struct cvar_s *allow_download_maps; +extern int sv_antilag; +extern float sv_antilag_frac; +extern int sv_timecheck_fuzz; +extern int sv_timecheck_decay; +extern int sv_maxrate; +extern int sv_timestamps; +extern char *sv_timefmt; +extern int sv_phs; +extern float sv_maxvelocity; +extern float sv_gravity; +extern int sv_jump_any; +extern float sv_aim; +extern float sv_stopspeed; +extern float sv_spectatormaxspeed; +extern float sv_accelerate; +extern float sv_airaccelerate; +extern float sv_wateraccelerate; +extern float sv_friction; +extern float sv_waterfriction; +extern int pr_double_remove; +extern int allow_download; +extern int allow_download_skins; +extern int allow_download_models; +extern int allow_download_sounds; +extern int allow_download_maps; extern int fp_messages; extern int fp_persecond; extern int fp_secondsdead; -extern struct cvar_s *pausable; -extern qboolean nouse; +extern int pausable; +extern bool nouse; extern char fp_msg[255]; diff --git a/qw/include/sv_demo.h b/qw/include/sv_demo.h index 463d63a49..c1e608909 100644 --- a/qw/include/sv_demo.h +++ b/qw/include/sv_demo.h @@ -22,9 +22,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define __sv_demo_h extern struct recorder_s demo; -extern struct cvar_s *sv_demofps; -extern struct cvar_s *sv_demoPings; -extern struct cvar_s *sv_demoMaxSize; +extern float sv_demofps; +extern float sv_demoPings; void SV_Stop (int reason); void Demo_Init (void); diff --git a/qw/include/sv_pr_cmds.h b/qw/include/sv_pr_cmds.h index 0ef2d00ac..6cfb999d7 100644 --- a/qw/include/sv_pr_cmds.h +++ b/qw/include/sv_pr_cmds.h @@ -28,6 +28,6 @@ #ifndef __sv_pr_cmds_h #define __sv_pr_cmds_h -void PF_changeyaw (progs_t *pr); +void PF_changeyaw (progs_t *pr, void *data); #endif // __sv_pr_cmds_h diff --git a/qw/include/sv_progs.h b/qw/include/sv_progs.h index ca96f7de5..e50ff3b0e 100644 --- a/qw/include/sv_progs.h +++ b/qw/include/sv_progs.h @@ -38,14 +38,14 @@ #include "sv_pr_cmds.h" typedef struct { - pr_int_t *self; - pr_int_t *other; - pr_int_t *world; + pr_uint_t *self; + pr_uint_t *other; + pr_uint_t *world; float *time; float *frametime; - pr_int_t *newmis; + pr_uint_t *newmis; float *force_retouch; - string_t *mapname; + pr_string_t *mapname; float *serverflags; float *total_secrets; float *total_monsters; @@ -61,35 +61,35 @@ typedef struct { vec3_t *trace_endpos; vec3_t *trace_plane_normal; float *trace_plane_dist; - pr_int_t *trace_ent; + pr_uint_t *trace_ent; float *trace_inopen; float *trace_inwater; - pr_int_t *msg_entity; + pr_uint_t *msg_entity; float *skill; } sv_globals_t; extern sv_globals_t sv_globals; typedef struct { - func_t main; - func_t StartFrame; - func_t PlayerPreThink; - func_t PlayerPostThink; - func_t ClientKill; - func_t ClientConnect; - func_t PutClientInServer; - func_t ClientDisconnect; - func_t SetNewParms; - func_t SetChangeParms; + pr_func_t main; + pr_func_t StartFrame; + pr_func_t PlayerPreThink; + pr_func_t PlayerPostThink; + pr_func_t ClientKill; + pr_func_t ClientConnect; + pr_func_t PutClientInServer; + pr_func_t ClientDisconnect; + pr_func_t SetNewParms; + pr_func_t SetChangeParms; - func_t EndFrame; - func_t SpectatorConnect; - func_t SpectatorThink; - func_t SpectatorDisconnect; - func_t UserInfoCallback; - func_t UserInfoChanged; - func_t ChatMessage; - func_t LocalinfoChanged; + pr_func_t EndFrame; + pr_func_t SpectatorConnect; + pr_func_t SpectatorThink; + pr_func_t SpectatorDisconnect; + pr_func_t UserInfoCallback; + pr_func_t UserInfoChanged; + pr_func_t ChatMessage; + pr_func_t LocalinfoChanged; } sv_funcs_t; extern sv_funcs_t sv_funcs; @@ -108,23 +108,23 @@ typedef struct pr_int_t velocity; //vec3_t pr_int_t angles; //vec3_t pr_int_t avelocity; //vec3_t - pr_int_t classname; //string_t - pr_int_t model; //string_t + pr_int_t classname; //pr_string_t + pr_int_t model; //pr_string_t pr_int_t frame; //float pr_int_t skin; //float pr_int_t effects; //float pr_int_t mins; //vec3_t pr_int_t maxs; //vec3_t pr_int_t size; //vec3_t - pr_int_t touch; //func_t - pr_int_t think; //func_t - pr_int_t blocked; //func_t + pr_int_t touch; //pr_func_t + pr_int_t think; //pr_func_t + pr_int_t blocked; //pr_func_t pr_int_t nextthink; //float pr_int_t groundentity; //int pr_int_t health; //float pr_int_t frags; //float pr_int_t weapon; //float - pr_int_t weaponmodel; //string_t + pr_int_t weaponmodel; //pr_string_t pr_int_t weaponframe; //float pr_int_t currentammo; //float pr_int_t ammo_shells; //float @@ -141,7 +141,7 @@ typedef struct pr_int_t impulse; //float pr_int_t fixangle; //float pr_int_t v_angle; //vec3_t - pr_int_t netname; //string_t + pr_int_t netname; //pr_string_t pr_int_t enemy; //int pr_int_t flags; //float pr_int_t colormap; //float @@ -158,7 +158,7 @@ typedef struct pr_int_t dmg_save; //float pr_int_t dmg_inflictor; //int pr_int_t owner; //int - pr_int_t message; //string_t + pr_int_t message; //pr_string_t pr_int_t sounds; //float pr_int_t rotated_bbox; //int @@ -193,8 +193,9 @@ extern progs_t sv_pr_state; #define SVstring(e,f) SVFIELD (e, f, string) #define SVfunc(e,f) SVFIELD (e, f, func) #define SVentity(e,f) SVFIELD (e, f, entity) -#define SVvector(e,f) SVFIELD (e, f, vector) -#define SVinteger(e,f) SVFIELD (e, f, integer) +#define SVvector(e,f) (&SVFIELD (e, f, float)) +#define SVint(e,f) SVFIELD (e, f, int) +#define SVdouble(e,f) SVFIELD (e, f, double) typedef struct edict_leaf_s { struct edict_leaf_s *next; @@ -206,26 +207,36 @@ typedef struct sv_data_s { link_t area; ///< linked to a division node or leaf edict_leaf_t *leafs; entity_state_t state; - qboolean add_grav; + bool add_grav; } sv_data_t; #define SVdata(e) ((sv_data_t *) ((e)->edata)) #define EDICT_FROM_AREA(l) (STRUCT_FROM_LINK(l,sv_data_t,area)->edict) static inline void -sv_pr_touch (edict_t *self, edict_t *other) +sv_pr_exec (edict_t *self, edict_t *other, pr_func_t func) { pr_int_t this; *sv_globals.self = EDICT_TO_PROG (&sv_pr_state, self); *sv_globals.other = EDICT_TO_PROG (&sv_pr_state, other); if ((this = sv_pr_state.fields.this) != -1) { + PR_PushFrame (&sv_pr_state); PR_RESET_PARAMS (&sv_pr_state); P_INT (&sv_pr_state, 0) = E_POINTER (self, this); P_INT (&sv_pr_state, 1) = 0; - P_INT (&sv_pr_state, 2) = E_POINTER (other, this); + P_INT (&sv_pr_state, 2) = other ? E_POINTER (other, this) : 0; } - PR_ExecuteProgram (&sv_pr_state, SVfunc (self, touch)); + PR_ExecuteProgram (&sv_pr_state, func); + if ((this = sv_pr_state.fields.this) != -1) { + PR_PopFrame (&sv_pr_state); + } +} + +static inline void +sv_pr_touch (edict_t *self, edict_t *other) +{ + sv_pr_exec (self, other, SVfunc (self, touch)); } static inline void @@ -236,33 +247,13 @@ sv_pr_use (edict_t *self, edict_t *other) static inline void sv_pr_think (edict_t *self) { - pr_int_t this; - - *sv_globals.self = EDICT_TO_PROG (&sv_pr_state, self); - *sv_globals.other = 0; - if ((this = sv_pr_state.fields.this) != -1) { - PR_RESET_PARAMS (&sv_pr_state); - P_INT (&sv_pr_state, 0) = E_POINTER (self, this); - P_INT (&sv_pr_state, 1) = 0; - P_INT (&sv_pr_state, 2) = 0; - } - PR_ExecuteProgram (&sv_pr_state, SVfunc (self, think)); + sv_pr_exec (self, 0, SVfunc (self, think)); } static inline void sv_pr_blocked (edict_t *self, edict_t *other) { - pr_int_t this; - - *sv_globals.self = EDICT_TO_PROG (&sv_pr_state, self); - *sv_globals.other = EDICT_TO_PROG (&sv_pr_state, other); - if ((this = sv_pr_state.fields.this) != -1) { - PR_RESET_PARAMS (&sv_pr_state); - P_INT (&sv_pr_state, 0) = E_POINTER (self, this); - P_INT (&sv_pr_state, 1) = 0; - P_INT (&sv_pr_state, 2) = E_POINTER (other, this); - } - PR_ExecuteProgram (&sv_pr_state, SVfunc (self, blocked)); + sv_pr_exec (self, other, SVfunc (self, blocked)); } #endif // __sv_progs_h diff --git a/qw/include/sv_recorder.h b/qw/include/sv_recorder.h index 80b55d419..a2875360a 100644 --- a/qw/include/sv_recorder.h +++ b/qw/include/sv_recorder.h @@ -41,13 +41,13 @@ recorder_t *SVR_AddUser (void (*writer)(void *, struct sizebuf_s *, int), void (*finish)(void *, struct sizebuf_s *), int demo, void *user); void SVR_RemoveUser (recorder_t *r); -struct sizebuf_s *SVR_WriteBegin (byte type, int to, int size); -struct sizebuf_s *SVR_Datagram (void); +struct sizebuf_s *SVR_WriteBegin (byte type, int to, unsigned size); +struct sizebuf_s *SVR_Datagram (void) __attribute__((const)); void SVR_ForceFrame (void); void SVR_Pause (recorder_t *r); void SVR_Continue (recorder_t *r); void SVR_SetDelta (recorder_t *r, int delta, int in_frame); void SVR_SendMessages (void); -int SVR_NumRecorders (void); +int SVR_NumRecorders (void) __attribute__((pure)); #endif//__sv_recorder_h diff --git a/qw/source/Makefile.am b/qw/source/Makefile.am deleted file mode 100644 index 9afe77ae1..000000000 --- a/qw/source/Makefile.am +++ /dev/null @@ -1,191 +0,0 @@ -## Process this file with automake to produce Makefile.in -# -# Makefile.am -# -# Automake-using build system for QuakeForge -# -# Copyright (C) 2000 Jeff Teunissen -# -# This Makefile is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# -# See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to: -# -# Free Software Foundation, Inc. -# 59 Temple Place - Suite 330 -# Boston, MA 02111-1307, USA -# -# $Id$ -# - -AUTOMAKE_OPTIONS= foreign - -# Stuff that is common to both client and server -AM_CPPFLAGS= -I$(top_srcdir)/include -I$(top_srcdir)/qw/include @LIBCURL_CFLAGS@ -SDL_LIBS = @SDL_LIBS@ -AM_CFLAGS= @PTHREAD_CFLAGS@ - -bin_PROGRAMS= @QW_TARGETS@ - -EXTRA_PROGRAMS= \ - qw-client-fbdev qw-client-sdl qw-client-svga qw-client-wgl qw-client-x11 \ - qw-server qw-master - -noinst_LIBRARIES= @qw_libs@ -EXTRA_LIBRARIES=libqw_client.a libqw_common.a libqw_sdl.a libqw_server.a - - -libqw_common_a_SOURCES=\ - game.c map_cfg.c pmove.c pmovetst.c net_packetlog.c -libqw_sdl_a_SOURCES=cl_sys_sdl.c -libqw_sdl_a_CFLAGS=$(SDL_CFLAGS) - -common_ldflags= -export-dynamic @PTHREAD_LDFLAGS@ - -# Server builds -# -# ... System type -if SYSTYPE_WIN32 -syssv_SRC= sv_sys_win.c -else -syssv_SRC= sv_sys_unix.c -endif - -EXTRA_DIST=sv_sys_win.c sv_sys_unix.c - -libqw_server_a_SOURCES= \ - crudefile.c sv_ccmds.c sv_demo.c sv_ents.c sv_gib.c sv_init.c sv_main.c \ - sv_move.c sv_phys.c sv_pr_cmds.c sv_pr_cpqw.c sv_pr_qwe.c sv_progs.c \ - sv_qtv.c sv_recorder.c sv_sbar.c sv_send.c sv_user.c world.c - -qw_server_LIBS= \ - @server_static_plugin_libs@ \ - $(top_builddir)/libs/qw/libqw.a \ - $(top_builddir)/libs/net/libnet_chan.la \ - $(top_builddir)/libs/models/libQFmodels.la \ - $(top_builddir)/libs/gib/libQFgib.la \ - $(top_builddir)/libs/ruamoko/libQFruamoko.la \ - $(top_builddir)/libs/console/libQFconsole.la \ - $(top_builddir)/libs/util/libQFutil.la - -qw_server_deps=libqw_server.a libqw_common.a $(qw_server_LIBS) -qw_server_SOURCES= $(syssv_SRC) -qw_server_LDADD= $(qw_server_deps) $(NET_LIBS) $(DL_LIBS) $(CURSES_LIBS) -qw_server_LDFLAGS= $(common_ldflags) -qw_server_DEPENDENCIES= $(qw_server_deps) - -qw_master_deps=$(top_builddir)/libs/util/libQFutil.la -qw_master_SOURCES= master.c -qw_master_LDADD= $(qw_master_deps) $(NET_LIBS) -qw_master_DEPENDENCIES= $(qw_master_deps) -qw_master_LDFLAGS= $(common_ldflags) - -cl_plugin_LIBS= \ - @client_static_plugin_libs@ - -qw_client_LIBS= \ - $(top_builddir)/libs/qw/libqw.a \ - $(top_builddir)/libs/net/libnet_chan.la \ - $(top_builddir)/libs/console/libQFconsole.la \ - $(top_builddir)/libs/video/targets/libQFjs.la \ - $(top_builddir)/libs/audio/libQFcd.la \ - $(top_builddir)/libs/audio/libQFsound.la \ - $(top_builddir)/libs/image/libQFimage.la \ - $(top_builddir)/libs/gib/libQFgib.la \ - $(top_builddir)/libs/ruamoko/libQFruamoko.la \ - $(top_builddir)/libs/util/libQFutil.la - -client_LIBS= $(qw_client_LIBS) -client_libs= libqw_client.a libqw_common.a \ - $(top_builddir)/libs/client/libQFclient.la - -libqw_client_a_SOURCES= \ - cl_cam.c cl_chase.c cl_chat.c cl_cmd.c cl_cvar.c cl_demo.c \ - cl_entparse.c cl_ents.c cl_http.c cl_input.c cl_main.c cl_ngraph.c \ - cl_parse.c cl_pred.c cl_rss.c cl_screen.c cl_skin.c cl_slist.c \ - cl_tent.c cl_view.c \ - locs.c sbar.c teamplay.c - -# Software-rendering clients - -# ... Linux FBDev -qw_client_fbdev_libs= \ - $(client_libs) \ - $(cl_plugin_LIBS) \ - $(top_builddir)/libs/video/renderer/libQFrenderer.la \ - $(top_builddir)/libs/models/libQFmodels.la \ - $(top_builddir)/libs/video/targets/libQFfbdev.la \ - $(client_LIBS) -qw_client_fbdev_SOURCES= cl_sys_unix.c -qw_client_fbdev_LDADD= $(qw_client_fbdev_libs) $(NET_LIBS) $(LIBCURL_LIBS) -qw_client_fbdev_LDFLAGS= $(common_ldflags) -qw_client_fbdev_DEPENDENCIES= $(qw_client_fbdev_libs) - -# ... Simple DirectMedia Layer, version 1.2 and higher -qw_client_sdl_libs= \ - libqw_sdl.a \ - $(client_libs) \ - $(cl_plugin_LIBS) \ - $(top_builddir)/libs/video/renderer/libQFrenderer.la \ - $(top_builddir)/libs/models/libQFmodels.la \ - $(top_builddir)/libs/video/targets/libQFsdl.la \ - $(client_LIBS) -qw_client_sdl_SOURCES=sdl_link.c -qw_client_sdl_LDADD= libqw_sdl.a $(qw_client_sdl_libs) $(SDL_LIBS) $(NET_LIBS) $(LIBCURL_LIBS) -qw_client_sdl_LDFLAGS= $(common_ldflags) -qw_client_sdl_DEPENDENCIES= libqw_sdl.a $(qw_client_sdl_libs) - -# ... Linux SVGAlib -qw_client_svga_libs= \ - $(client_libs) \ - $(cl_plugin_LIBS) \ - $(top_builddir)/libs/video/renderer/libQFrenderer.la \ - $(top_builddir)/libs/models/libQFmodels.la \ - $(top_builddir)/libs/video/targets/libQFsvga.la \ - $(client_LIBS) -qw_client_svga_SOURCES= cl_sys_unix.c -qw_client_svga_LDADD= $(qw_client_svga_libs) $(SVGA_LIBS) $(NET_LIBS) $(LIBCURL_LIBS) -qw_client_svga_LDFLAGS= $(common_ldflags) -qw_client_svga_DEPENDENCIES= $(qw_client_svga_libs) - -# ... X11 -qw_client_x11_libs= \ - $(client_libs) \ - $(cl_plugin_LIBS) \ - $(top_builddir)/libs/video/renderer/libQFrenderer.la \ - $(top_builddir)/libs/models/libQFmodels.la \ - $(top_builddir)/libs/video/targets/libQFx11.la \ - $(client_LIBS) -qw_client_x11_SOURCES= cl_sys_unix.c -qw_client_x11_LDADD= $(qw_client_x11_libs) \ - $(VIDMODE_LIBS) $(DGA_LIBS) $(X_LIBS) -lX11 \ - $(X_EXTRA_LIBS) $(X_SHM_LIB) $(NET_LIBS) $(LIBCURL_LIBS) $(DL_LIBS) -qw_client_x11_LDFLAGS= $(common_ldflags) -qw_client_x11_DEPENDENCIES= $(qw_client_x11_libs) - -# ... SGI/Microsoft WGL (Windows OpenGL) -qw_client_wgl_libs= \ - $(client_libs) \ - $(cl_plugin_LIBS) \ - $(opengl_LIBS) \ - $(top_builddir)/libs/video/targets/libQFwgl.la \ - $(client_LIBS) -qw_client_wgl_SOURCES= cl_sys_win.c -qw_client_wgl_LDADD= $(qw_client_wgl_libs) -lgdi32 -lwinmm $(NET_LIBS) $(LIBCURL_LIBS) -qw_client_wgl_LDFLAGS= $(common_ldflags) -qw_client_wgl_DEPENDENCIES= $(qw_client_wgl_libs) - -# Stuff that doesn't get linked into an executable NEEDS to be mentioned here, -# or it won't be distributed with 'make dist' - -# Kill the temp files, hopefully. -CLEANFILES = *.i *.s $(YACCLEX_CLEANFILES) diff --git a/qw/source/Makemodule.am b/qw/source/Makemodule.am new file mode 100644 index 000000000..be7530c18 --- /dev/null +++ b/qw/source/Makemodule.am @@ -0,0 +1,181 @@ +## Process this file with automake to produce Makefile.in +# +# Makefile.am +# +# Automake-using build system for QuakeForge +# +# Copyright (C) 2000 Jeff Teunissen +# +# This Makefile is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to: +# +# Free Software Foundation, Inc. +# 59 Temple Place - Suite 330 +# Boston, MA 02111-1307, USA +# +# $Id$ +# + +bin_PROGRAMS += @QW_TARGETS@ + +EXTRA_PROGRAMS += \ + qw-client-fbdev qw-client-sdl qw-client-svga qw-client-win qw-client-x11 \ + qw-server qw-master + +noinst_LIBRARIES += @qw_libs@ +EXTRA_LIBRARIES += qw/source/libqw_client.a qw/source/libqw_common.a qw/source/libqw_sdl.a qw/source/libqw_server.a + + +qw_source_libqw_common_a_SOURCES=\ + qw/source/game.c qw/source/map_cfg.c qw/source/pmove.c qw/source/pmovetst.c qw/source/net_packetlog.c +qw_source_libqw_sdl_a_SOURCES=qw/source/cl_sys_sdl.c +qw_source_libqw_sdl_a_CFLAGS=$(SDL_CFLAGS) + +# Server builds +# +# ... System type +if SYSTYPE_WIN32 +syssv_SRC= qw/source/sv_sys_win.c +else +syssv_SRC= qw/source/sv_sys_unix.c +endif + +EXTRA_DIST+=qw/source/sv_sys_win.c qw/source/sv_sys_unix.c + +qw_source_libqw_server_a_SOURCES= \ + qw/source/crudefile.c qw/source/sv_ccmds.c qw/source/sv_demo.c qw/source/sv_ents.c qw/source/sv_gib.c qw/source/sv_init.c qw/source/sv_main.c \ + qw/source/sv_move.c qw/source/sv_phys.c qw/source/sv_pr_cmds.c qw/source/sv_pr_cpqw.c qw/source/sv_pr_qwe.c qw/source/sv_progs.c \ + qw/source/sv_qtv.c qw/source/sv_recorder.c qw/source/sv_sbar.c qw/source/sv_send.c qw/source/sv_user.c qw/source/world.c + +qw_server_LIBS= \ + @server_static_plugin_libs@ \ + libs/qw/libqw.a \ + libs/net/libnet_chan.la \ + libs/models/libQFmodels.la \ + libs/gib/libQFgib.la \ + libs/ruamoko/libQFruamoko.la \ + libs/gamecode/libQFgamecode.la \ + libs/console/libQFconsole.la \ + libs/ui/libQFui.la \ + libs/util/libQFutil.la + +qw_server_deps=qw/source/libqw_server.a qw/source/libqw_common.a $(qw_server_LIBS) +qw_server_SOURCES= $(syssv_SRC) +qw_server_LDADD= $(qw_server_deps) $(NET_LIBS) $(DL_LIBS) $(CURSES_LIBS) +qw_server_LDFLAGS= $(common_ldflags) +qw_server_DEPENDENCIES= $(qw_server_deps) + +qw_master_deps=libs/util/libQFutil.la +qw_master_SOURCES= qw/source/master.c +qw_master_LDADD= $(qw_master_deps) $(NET_LIBS) +qw_master_DEPENDENCIES= $(qw_master_deps) +qw_master_LDFLAGS= $(common_ldflags) + +qw_cl_plugin_LIBS= \ + @client_static_plugin_libs@ + +qw_client_LIBS= \ + libs/qw/libqw.a \ + libs/gib/libQFgib_client.la \ + libs/net/libnet_chan.la \ + libs/scene/libQFscene.la \ + libs/console/libQFconsole.la \ + libs/audio/libQFcd.la \ + libs/audio/libQFsound.la \ + libs/image/libQFimage.la \ + libs/gib/libQFgib.la \ + libs/ruamoko/libQFruamoko.la \ + libs/ui/libQFui.la \ + libs/input/libQFinput.la \ + libs/ecs/libQFecs.la \ + libs/util/libQFutil.la +qw_client_libs= qw/source/libqw_client.a qw/source/libqw_common.a \ + libs/client/libQFclient.la + +qw_source_libqw_client_a_SOURCES= \ + qw/source/cl_cam.c qw/source/cl_chat.c qw/source/cl_cmd.c qw/source/cl_cvar.c qw/source/cl_demo.c \ + qw/source/cl_entparse.c qw/source/cl_ents.c qw/source/cl_http.c qw/source/cl_input.c qw/source/cl_main.c qw/source/cl_ngraph.c \ + qw/source/cl_parse.c qw/source/cl_pred.c qw/source/cl_rss.c qw/source/cl_skin.c qw/source/cl_slist.c \ + qw/source/teamplay.c + +# Software-rendering clients + +# ... Linux FBDev +qw_client_fbdev_libs= \ + $(qw_client_libs) \ + $(qw_cl_plugin_LIBS) \ + libs/video/renderer/libQFrenderer.la \ + libs/models/libQFmodels.la \ + libs/video/targets/libQFfbdev.la \ + $(qw_client_LIBS) +qw_client_fbdev_SOURCES= qw/source/cl_sys_unix.c +qw_client_fbdev_LDADD= $(qw_client_fbdev_libs) $(NET_LIBS) $(LIBCURL_LIBS) +qw_client_fbdev_LDFLAGS= $(common_ldflags) +qw_client_fbdev_DEPENDENCIES= $(qw_client_fbdev_libs) + +# ... Simple DirectMedia Layer, version 1.2 and higher +qw_client_sdl_libs= \ + qw/source/libqw_sdl.a \ + $(qw_client_libs) \ + $(qw_cl_plugin_LIBS) \ + libs/video/renderer/libQFrenderer.la \ + libs/models/libQFmodels.la \ + libs/video/targets/libQFsdl.la \ + $(qw_client_LIBS) +qw_client_sdl_SOURCES=qw/source/sdl_link.c +qw_client_sdl_LDADD= qw/source/libqw_sdl.a $(qw_client_sdl_libs) $(SDL_LIBS) $(NET_LIBS) $(LIBCURL_LIBS) +qw_client_sdl_LDFLAGS= $(common_ldflags) +qw_client_sdl_DEPENDENCIES= qw/source/libqw_sdl.a $(qw_client_sdl_libs) + +# ... Linux SVGAlib +qw_client_svga_libs= \ + $(qw_client_libs) \ + $(qw_cl_plugin_LIBS) \ + libs/video/renderer/libQFrenderer.la \ + libs/models/libQFmodels.la \ + libs/video/targets/libQFsvga.la \ + $(qw_client_LIBS) +qw_client_svga_SOURCES= qw/source/cl_sys_unix.c +qw_client_svga_LDADD= $(qw_client_svga_libs) $(SVGA_LIBS) $(NET_LIBS) $(LIBCURL_LIBS) +qw_client_svga_LDFLAGS= $(common_ldflags) +qw_client_svga_DEPENDENCIES= $(qw_client_svga_libs) + +# ... X11 +qw_client_x11_libs= \ + $(qw_client_libs) \ + $(qw_cl_plugin_LIBS) \ + libs/video/renderer/libQFrenderer.la \ + libs/models/libQFmodels.la \ + libs/video/targets/libQFx11.la \ + $(qw_client_LIBS) +qw_client_x11_SOURCES= qw/source/cl_sys_unix.c +qw_client_x11_LDADD= $(qw_client_x11_libs) \ + $(VIDMODE_LIBS) $(DGA_LIBS) ${XFIXES_LIBS} $(XI2_LIBS) $(X_LIBS) \ + -lX11 $(X_EXTRA_LIBS) $(X_SHM_LIB) $(NET_LIBS) $(LIBCURL_LIBS) \ + $(DL_LIBS) +qw_client_x11_LDFLAGS= $(common_ldflags) +qw_client_x11_DEPENDENCIES= $(qw_client_x11_libs) + +# ... SGI/Microsoft WGL (Windows OpenGL) +qw_client_win_libs= \ + $(qw_client_libs) \ + $(qw_cl_plugin_LIBS) \ + libs/video/renderer/libQFrenderer.la \ + libs/models/libQFmodels.la \ + libs/video/targets/libQFwin.la \ + $(qw_client_LIBS) +qw_client_win_SOURCES= qw/source/cl_sys_win.c +qw_client_win_LDADD= $(qw_client_win_libs) -lgdi32 -lwinmm $(NET_LIBS) $(LIBCURL_LIBS) +qw_client_win_LDFLAGS= $(common_ldflags) +qw_client_win_DEPENDENCIES= $(qw_client_win_libs) diff --git a/qw/source/cl_cam.c b/qw/source/cl_cam.c index f7e9adf44..bf6683a64 100644 --- a/qw/source/cl_cam.c +++ b/qw/source/cl_cam.c @@ -45,13 +45,14 @@ #include "QF/cvar.h" #include "QF/msg.h" -#include "chase.h" -#include "cl_cam.h" -#include "cl_input.h" -#include "client.h" #include "compat.h" + +#include "client/sbar.h" + +#include "qw/include/cl_cam.h" +#include "qw/include/cl_input.h" +#include "qw/include/client.h" #include "qw/pmove.h" -#include "sbar.h" #define PM_SPECTATORMAXSPEED 500 #define PM_STOPSPEED 100 @@ -66,17 +67,51 @@ #include "QF/mathlib.h" #include "world.h" -cvar_t *cl_hightrack; // track high fragger -cvar_t *cl_chasecam; -cvar_t *cl_camera_maxpitch; -cvar_t *cl_camera_maxyaw; +int cl_hightrack; +static cvar_t cl_hightrack_cvar = { + .name = "cl_hightrack", + .description = + "view the player who has the most frags while you are in spectator " + "mode.", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_hightrack }, +}; +int cl_chasecam; +static cvar_t cl_chasecam_cvar = { + .name = "cl_chasecam", + .description = + "get first person view of the person you are tracking in spectator " + "mode", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_chasecam }, +}; +float cl_camera_maxpitch; +static cvar_t cl_camera_maxpitch_cvar = { + .name = "cl_camera_maxpitch", + .description = + "highest camera pitch in spectator mode", + .default_value = "10", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &cl_camera_maxpitch }, +}; +float cl_camera_maxyaw; +static cvar_t cl_camera_maxyaw_cvar = { + .name = "cl_camera_maxyaw", + .description = + "highest camera yaw in spectator mode", + .default_value = "30", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &cl_camera_maxyaw }, +}; static vec3_t desired_position; // where the camera wants to be -static qboolean locked = false; +static bool locked = false; static int oldbuttons; double cam_lastviewtime; -qboolean cam_forceview; +bool cam_forceview; vec3_t cam_viewangles; int spec_track = 0; // player# of who we are tracking @@ -113,35 +148,35 @@ vectoangles (vec3_t vec, vec3_t ang) } // returns true if weapon model should be drawn in camera mode -qboolean +bool Cam_DrawViewModel (void) { - if (cl.chase && chase_active->int_val) + if (cl.viewstate.chase && chase_active) return false; if (!cl.spectator) return true; - if (autocam && locked && cl_chasecam->int_val) + if (autocam && locked && cl_chasecam) return true; return false; } // returns true if we should draw this player, we don't if we are chase camming -qboolean +bool Cam_DrawPlayer (int playernum) { if (playernum == cl.playernum) { // client player - if (cl.chase == 0 || chase_active->int_val == 0) + if (cl.viewstate.chase == 0 || chase_active == 0) return false; if (!cl.spectator) return true; } else { - if (!cl_chasecam->int_val) + if (!cl_chasecam) return true; if (cl.spectator && autocam && locked && spec_track == playernum) return false; - if (cl.chase == 0 || chase_active->int_val == 0) + if (cl.viewstate.chase == 0 || chase_active == 0) return true; } return false; @@ -165,7 +200,7 @@ Cam_Unlock (void) } autocam = CAM_NONE; locked = false; - Sbar_Changed (); + Sbar_SetAutotrack (-1); } } @@ -187,11 +222,11 @@ Cam_Lock (int playernum) last_lock = realtime; cam_forceview = true; locked = false; - Sbar_Changed (); + Sbar_SetAutotrack (spec_track); } static trace_t -Cam_DoTrace (vec3_t vec1, vec3_t vec2) +Cam_DoTrace (vec4f_t vec1, vec3_t vec2)//FIXME vec2 type { #if 0 memset (&pmove, 0, sizeof (pmove)); @@ -208,7 +243,7 @@ Cam_DoTrace (vec3_t vec1, vec3_t vec2) // Returns distance or 9999 if invalid for some reason static float Cam_TryFlyby (player_state_t * self, player_state_t * player, vec3_t vec, - qboolean checkvis) + bool checkvis) { float len; trace_t trace; @@ -217,47 +252,47 @@ Cam_TryFlyby (player_state_t * self, player_state_t * player, vec3_t vec, vectoangles (vec, v); VectorCopy (v, pmove.angles); VectorNormalize (vec); - VectorMultAdd (player->pls.origin, 800, vec, v); + VectorMultAdd (player->pls.es.origin, 800, vec, v); // v is endpos // fake a player move - trace = Cam_DoTrace (player->pls.origin, v); + trace = Cam_DoTrace (player->pls.es.origin, v); if ( /* trace.inopen || */ trace.inwater) return 9999; VectorCopy (trace.endpos, vec); - len = VectorDistance (trace.endpos, player->pls.origin); + len = VectorDistance (trace.endpos, player->pls.es.origin); if (len < 32 || len > 800) return 9999; if (checkvis) { - trace = Cam_DoTrace (self->pls.origin, vec); + trace = Cam_DoTrace (self->pls.es.origin, vec); if (trace.fraction != 1 || trace.inwater) return 9999; - len = VectorDistance (trace.endpos, self->pls.origin); + len = VectorDistance (trace.endpos, self->pls.es.origin); } return len; } // Is player visible? -static qboolean -Cam_IsVisible (player_state_t * player, vec3_t vec) +static bool +Cam_IsVisible (player_state_t *player, vec3_t vec) { float d; trace_t trace; vec3_t v; - trace = Cam_DoTrace (player->pls.origin, vec); + trace = Cam_DoTrace (player->pls.es.origin, vec); if (trace.fraction != 1 || /* trace.inopen || */ trace.inwater) return false; // check distance, don't let the player get too far away or too close - VectorSubtract (player->pls.origin, vec, v); + VectorSubtract (player->pls.es.origin, vec, v); d = VectorLength (v); return (d > 16.0); } -static qboolean +static bool InitFlyby (player_state_t * self, player_state_t * player, int checkvis) { float f, max; @@ -384,17 +419,18 @@ Cam_Track (usercmd_t *cmd) if (!cl.spectator) return; - if (cl_hightrack->int_val && !locked) + if (cl_hightrack && !locked) Cam_CheckHighTarget (); if (!autocam || cls.state != ca_active) return; if (locked - && (!cl.players[spec_track].name->value[0] + && (!cl.players[spec_track].name + || !cl.players[spec_track].name->value[0] || cl.players[spec_track].spectator)) { locked = false; - if (cl_hightrack->int_val) + if (cl_hightrack) Cam_CheckHighTarget (); else Cam_Unlock (); @@ -435,26 +471,26 @@ Cam_Track (usercmd_t *cmd) if (!locked || !autocam) return; - if (cl_chasecam->int_val) { + if (cl_chasecam) { cmd->forwardmove = cmd->sidemove = cmd->upmove = 0; - VectorCopy (player->viewangles, cl.viewangles); - VectorCopy (player->pls.origin, desired_position); - if (memcmp (&desired_position, &self->pls.origin, + VectorCopy (player->viewangles, cl.viewstate.player_angles); + VectorCopy (player->pls.es.origin, desired_position); + if (memcmp (&desired_position, &self->pls.es.origin, sizeof (desired_position)) != 0) { if (!cls.demoplayback) { MSG_WriteByte (&cls.netchan.message, clc_tmove); MSG_WriteCoordV (&cls.netchan.message, desired_position); } // move there locally immediately - VectorCopy (desired_position, self->pls.origin); + VectorCopy (desired_position, self->pls.es.origin); } - self->pls.weaponframe = player->pls.weaponframe; + self->pls.es.weaponframe = player->pls.es.weaponframe; } else { // Ok, move to our desired position and set our angles to view // the player - VectorSubtract (desired_position, self->pls.origin, vec); + VectorSubtract (desired_position, self->pls.es.origin, vec); len = VectorLength (vec); cmd->forwardmove = cmd->sidemove = cmd->upmove = 0; if (len > 16) { // close enough? @@ -464,11 +500,11 @@ Cam_Track (usercmd_t *cmd) } } // move there locally immediately - VectorCopy (desired_position, self->pls.origin); + VectorCopy (desired_position, self->pls.es.origin); - VectorSubtract (player->pls.origin, desired_position, vec); - vectoangles (vec, cl.viewangles); - cl.viewangles[0] = -cl.viewangles[0]; + VectorSubtract (player->pls.es.origin, desired_position, vec); + vectoangles (vec, cl.viewstate.player_angles); + cl.viewstate.player_angles[0] = -cl.viewstate.player_angles[0]; } } @@ -519,7 +555,7 @@ Cam_SetView (void) player = frame->playerstate + spec_track; self = frame->playerstate + cl.playernum; - VectorSubtract (player->pls.origin, cl.simorg, vec); + VectorSubtract (player->pls.es.origin, cl.simorg, vec); if (cam_forceview) { cam_forceview = false; vectoangles (vec, cam_viewangles); @@ -530,13 +566,13 @@ Cam_SetView (void) cam_viewangles[PITCH] = adjustang (cam_viewangles[PITCH], vec2[PITCH], - cl_camera_maxpitch->value); + cl_camera_maxpitch); cam_viewangles[YAW] = adjustang (cam_viewangles[YAW], vec2[YAW], - cl_camera_maxyaw->value); + cl_camera_maxyaw); } - VectorCopy (cam_viewangles, cl.viewangles); - VectorCopy (cl.viewangles, cl.simangles); + VectorCopy (cam_viewangles, cl.viewstate.player_angles); + VectorCopy (cl.viewstate.player_angles, cl.simangles); cl.simangles[ROLL] = 0; // FIXME @@@ } #endif @@ -559,7 +595,7 @@ Cam_FinishMove (usercmd_t *cmd) player = frame->playerstate + spec_track; self = frame->playerstate + cl.playernum; - VectorSubtract (player->pls.origin, self->pls.origin, vec); + VectorSubtract (player->pls.es.origin, self->pls.es.origin, vec); if (cam_forceview) { cam_forceview = false; vectoangles (vec, cam_viewangles); @@ -570,12 +606,12 @@ Cam_FinishMove (usercmd_t *cmd) cam_viewangles[PITCH] = adjustang (cam_viewangles[PITCH], vec2[PITCH], - cl_camera_maxpitch->value); + cl_camera_maxpitch); cam_viewangles[YAW] = adjustang (cam_viewangles[YAW], vec2[YAW], - cl_camera_maxyaw->value); + cl_camera_maxyaw); } - VectorCopy (cam_viewangles, cl.viewangles); + VectorCopy (cam_viewangles, cl.viewstate.player_angles); } #endif @@ -586,7 +622,7 @@ Cam_FinishMove (usercmd_t *cmd) if (autocam > CAM_TRACK) { Cam_Unlock (); - VectorCopy (cl.viewangles, cmd->angles); + VectorCopy (cl.viewstate.player_angles, cmd->angles); return; } } else @@ -597,7 +633,7 @@ Cam_FinishMove (usercmd_t *cmd) return; } - if (autocam && cl_hightrack->int_val) { + if (autocam && cl_hightrack) { Cam_CheckHighTarget (); return; } @@ -646,19 +682,14 @@ Cam_Reset (void) autocam = CAM_NONE; spec_track = 0; ideal_track = 0; + Sbar_SetAutotrack (-1); } void CL_Cam_Init_Cvars (void) { - cl_camera_maxpitch = Cvar_Get ("cl_camera_maxpitch", "10", CVAR_NONE, NULL, - "highest camera pitch in spectator mode"); - cl_camera_maxyaw = Cvar_Get ("cl_camera_maxyaw", "30", CVAR_NONE, NULL, - "highest camera yaw in spectator mode"); - cl_chasecam = Cvar_Get ("cl_chasecam", "0", CVAR_NONE, NULL, "get first " - "person view of the person you are tracking in " - "spectator mode"); - cl_hightrack = Cvar_Get ("cl_hightrack", "0", CVAR_NONE, NULL, "view the " - "player who has the most frags while you are in " - "spectator mode."); + Cvar_Register (&cl_camera_maxpitch_cvar, 0, 0); + Cvar_Register (&cl_camera_maxyaw_cvar, 0, 0); + Cvar_Register (&cl_chasecam_cvar, 0, 0); + Cvar_Register (&cl_hightrack_cvar, 0, 0); } diff --git a/qw/source/cl_chase.c b/qw/source/cl_chase.c deleted file mode 100644 index 26f5b02b4..000000000 --- a/qw/source/cl_chase.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - cl_chase.c - - chase camera support - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif - -#include "QF/cvar.h" -#include "QF/keys.h" -#include "QF/input.h" -#include "QF/mathlib.h" - -#include "QF/plugin/vid_render.h" - -#include "chase.h" -#include "cl_input.h" -#include "client.h" -#include "world.h" - -vec3_t camera_origin = {0,0,0}; -vec3_t camera_angles = {0,0,0}; -vec3_t player_origin = {0,0,0}; -vec3_t player_angles = {0,0,0}; - -vec3_t chase_angles; -vec3_t chase_dest; -vec3_t chase_dest_angles; -vec3_t chase_pos; - -cvar_t *chase_back; -cvar_t *chase_up; -cvar_t *chase_right; -cvar_t *chase_active; - - -void -Chase_Init_Cvars (void) -{ - chase_back = Cvar_Get ("chase_back", "100", CVAR_NONE, NULL, "None"); - chase_up = Cvar_Get ("chase_up", "16", CVAR_NONE, NULL, "None"); - chase_right = Cvar_Get ("chase_right", "0", CVAR_NONE, NULL, "None"); - chase_active = Cvar_Get ("chase_active", "0", CVAR_NONE, NULL, "None"); -} - -void -Chase_Reset (void) -{ - // for respawning and teleporting - // start position 12 units behind head -} - -static inline void -TraceLine (vec3_t start, vec3_t end, vec3_t impact) -{ - trace_t trace; - - memset (&trace, 0, sizeof (trace)); - trace.fraction = 1; - MOD_TraceLine (cl.worldmodel->hulls, 0, start, end, &trace); - - VectorCopy (trace.endpos, impact); -} - -void -Chase_Update (void) -{ - float pitch, yaw, fwd; - int i; - usercmd_t cmd; // movement direction - vec3_t forward, up, right, stop, dir; - - // lazy camera, look toward player entity - - if (chase_active->int_val == 2 || chase_active->int_val == 3) { - // control camera angles with key/mouse/joy-look - - camera_angles[PITCH] += cl.viewangles[PITCH] - player_angles[PITCH]; - camera_angles[YAW] += cl.viewangles[YAW] - player_angles[YAW]; - camera_angles[ROLL] += cl.viewangles[ROLL] - player_angles[ROLL]; - - if (chase_active->int_val == 2) { - if (camera_angles[PITCH] < -60) - camera_angles[PITCH] = -60; - if (camera_angles[PITCH] > 60) - camera_angles[PITCH] = 60; - } - - // move camera, it's not enough to just change the angles because - // the angles are automatically changed to look toward the player - - if (chase_active->int_val == 3) - VectorCopy (r_data->refdef->vieworg, player_origin); - - AngleVectors (camera_angles, forward, right, up); - VectorScale (forward, chase_back->value, forward); - VectorSubtract (player_origin, forward, camera_origin); - - if (chase_active->int_val == 2) { - VectorCopy (r_data->refdef->vieworg, player_origin); - - // don't let camera get too low - if (camera_origin[2] < player_origin[2] + chase_up->value) - camera_origin[2] = player_origin[2] + chase_up->value; - } - - // don't let camera get too far from player - - VectorSubtract (camera_origin, player_origin, dir); - VectorCopy (dir, forward); - VectorNormalize (forward); - - if (VectorLength (dir) > chase_back->value) { - VectorScale (forward, chase_back->value, dir); - VectorAdd (player_origin, dir, camera_origin); - } - - // check for walls between player and camera - - VectorScale (forward, 8, forward); - VectorAdd (camera_origin, forward, camera_origin); - TraceLine (player_origin, camera_origin, stop); - if (VectorLength (stop) != 0) - VectorSubtract (stop, forward, camera_origin); - - VectorSubtract (camera_origin, r_data->refdef->vieworg, dir); - VectorCopy (dir, forward); - VectorNormalize (forward); - - if (chase_active->int_val == 2) { - if (dir[1] == 0 && dir[0] == 0) { - // look straight up or down -// camera_angles[YAW] = r_data->refdef->viewangles[YAW]; - if (dir[2] > 0) - camera_angles[PITCH] = 90; - else - camera_angles[PITCH] = 270; - } else { - yaw = (atan2 (dir[1], dir[0]) * 180 / M_PI); - if (yaw < 0) - yaw += 360; - if (yaw < 180) - yaw += 180; - else - yaw -= 180; - camera_angles[YAW] = yaw; - - fwd = sqrt (dir[0] * dir[0] + dir[1] * dir[1]); - pitch = (atan2 (dir[2], fwd) * 180 / M_PI); - if (pitch < 0) - pitch += 360; - camera_angles[PITCH] = pitch; - } - } - - VectorCopy (camera_angles, r_data->refdef->viewangles);// rotate camera - VectorCopy (camera_origin, r_data->refdef->vieworg); // move camera - - // get basic movement from keyboard - - memset (&cmd, 0, sizeof (cmd)); -// VectorCopy (cl.viewangles, cmd.angles); - - if (in_strafe.state & 1) { - cmd.sidemove += cl_sidespeed->value * CL_KeyState (&in_right); - cmd.sidemove -= cl_sidespeed->value * CL_KeyState (&in_left); - } - cmd.sidemove += cl_sidespeed->value * CL_KeyState (&in_moveright); - cmd.sidemove -= cl_sidespeed->value * CL_KeyState (&in_moveleft); - - if (!(in_klook.state & 1)) { - cmd.forwardmove += cl_forwardspeed->value - * CL_KeyState (&in_forward); - cmd.forwardmove -= cl_backspeed->value * CL_KeyState (&in_back); - } - if (in_speed.state & 1) { - cmd.forwardmove *= cl_movespeedkey->value; - cmd.sidemove *= cl_movespeedkey->value; - } - - // mouse and joystick controllers add to movement - VectorSet (0, cl.viewangles[1] - camera_angles[1], 0, dir); - AngleVectors (dir, forward, right, up); - VectorScale (forward, viewdelta.position[2] * m_forward->value, - forward); - VectorScale (right, viewdelta.position[0] * m_side->value, right); - VectorAdd (forward, right, dir); - cmd.forwardmove += dir[0]; - cmd.sidemove -= dir[1]; - - VectorSet (0, camera_angles[1], 0, dir); - AngleVectors (dir, forward, right, up); - - VectorScale (forward, cmd.forwardmove, forward); - VectorScale (right, cmd.sidemove, right); - VectorAdd (forward, right, dir); - - if (dir[1] || dir[0]) { - cl.viewangles[YAW] = (atan2 (dir[1], dir[0]) * 180 / M_PI); - if (cl.viewangles[YAW] < 0) cl.viewangles[YAW] += 360; -// if (cl.viewangles[YAW] < 180) -// cl.viewangles[YAW] += 180; -// else -// cl.viewangles[YAW] -= 180; - } - - cl.viewangles[PITCH] = 0; - - // remember the new angle to calculate the difference next frame - VectorCopy (cl.viewangles, player_angles); - - return; - } - - // regular camera, faces same direction as player - - AngleVectors (cl.viewangles, forward, right, up); - - // calc exact destination - for (i = 0; i < 3; i++) - camera_origin[i] = r_data->refdef->vieworg[i] - - forward[i] * chase_back->value - right[i] * chase_right->value; - camera_origin[2] += chase_up->value; - - // check for walls between player and camera - TraceLine (r_data->refdef->vieworg, camera_origin, stop); - if (VectorLength (stop) != 0) - for (i = 0; i < 3; i++) - camera_origin[i] = stop[i] + forward[i] * 8; - - VectorCopy (camera_origin, r_data->refdef->vieworg); -} diff --git a/qw/source/cl_chat.c b/qw/source/cl_chat.c index 2b40e64dd..ed5569fe4 100644 --- a/qw/source/cl_chat.c +++ b/qw/source/cl_chat.c @@ -44,8 +44,10 @@ #include "QF/sys.h" #include "QF/va.h" -#include "client.h" -#include "cl_chat.h" +#include "client/input.h" + +#include "qw/include/client.h" +#include "qw/include/cl_chat.h" llist_t *ignore_list, *dead_ignore_list; @@ -58,13 +60,13 @@ CL_Ignore_Free (void *ele, void *unused) free (ig); } -static qboolean +static bool CL_Ignore_Compare (const void *ele, const void *cmp, void *unused) { return *(int *)cmp == ((ignore_t *) ele)->uid; } -static qboolean +static bool isc_iterator (ignore_t *ig, llist_node_t *node) { if (cl.players[ig->slot].userid != ig->uid) // We got out of sync somehow @@ -78,13 +80,14 @@ CL_Ignore_Sanity_Check (void) llist_iterate (ignore_list, LLIST_ICAST (isc_iterator)); } -static qboolean live_iterator (ignore_t *ig, llist_node_t *node) +static bool live_iterator (ignore_t *ig, llist_node_t *node) { - Sys_Printf ("%5i - %s\n", ig->uid, Info_ValueForKey (cl.players[ig->slot].userinfo, "name")); + Sys_Printf ("%5i - %s\n", ig->uid, + Info_ValueForKey (cl.players[ig->slot].userinfo, "name")); return true; } -static qboolean dead_iterator (ignore_t *ig, llist_node_t *node) +static bool dead_iterator (ignore_t *ig, llist_node_t *node) { Sys_Printf ("%s\n", ig->lastname); return true; @@ -115,7 +118,8 @@ CL_Ignore_f (void) new->slot = i; new->uid = uid; llist_append (ignore_list, new); - Sys_Printf ("User %i (%s) is now ignored.\n", uid, Info_ValueForKey (cl.players[i].userinfo, "name")); + Sys_Printf ("User %i (%s) is now ignored.\n", uid, + Info_ValueForKey (cl.players[i].userinfo, "name")); return; } } @@ -136,11 +140,14 @@ CL_Unignore_f (void) else { uid = atoi (Cmd_Argv (1)); if ((node = llist_findnode (ignore_list, &uid))) { - Sys_Printf ("User %i (%s) is no longer ignored.\n", uid, Info_ValueForKey (cl.players[LLIST_DATA (node, ignore_t)->slot].userinfo, "name")); + int slot = LLIST_DATA (node, ignore_t)->slot; + Sys_Printf ("User %i (%s) is no longer ignored.\n", uid, + Info_ValueForKey (cl.players[slot].userinfo, "name")); CL_Ignore_Free (llist_remove (node), 0); return; } - Sys_Printf ("User %i does not exist or is not presently ignored.\n", uid); + Sys_Printf ("User %i does not exist or is not presently ignored.\n", + uid); } } @@ -151,22 +158,23 @@ CL_Unignore_f (void) */ static const char *g_cam_str; static dstring_t *g_cam_test; -static qboolean g_cam_allowed; +static bool g_cam_allowed; -static qboolean cam_iterator (ignore_t *ig, llist_node_t *node) +static bool cam_iterator (ignore_t *ig, llist_node_t *node) { if (cl.players[ig->slot].userid != ig->uid) { // We got out of sync somehow llist_remove (node); return true; } - dsprintf (g_cam_test, "%s: ", Info_ValueForKey (cl.players[ig->slot].userinfo, "name")); + dsprintf (g_cam_test, "%s: ", + Info_ValueForKey (cl.players[ig->slot].userinfo, "name")); if (!strncmp (g_cam_test->str, g_cam_str, g_cam_test->size - 1)) { return g_cam_allowed = false; } else return true; } -qboolean +bool CL_Chat_Allow_Message (const char *str) { g_cam_str = str; @@ -187,16 +195,18 @@ CL_Chat_User_Disconnected (int uid) if ((ig = llist_remove (llist_findnode (ignore_list, &uid)))) { if (ig->lastname) free ((void *)ig->lastname); - ig->lastname = strdup (Info_ValueForKey (cl.players[ig->slot].userinfo, "name")); + ig->lastname = strdup (Info_ValueForKey (cl.players[ig->slot].userinfo, + "name")); llist_append (dead_ignore_list, ig); - Sys_Printf ("Ignored user %i (%s) left the server. Now ignoring by name...\n", ig->uid, ig->lastname); + Sys_Printf ("Ignored user %i (%s) left the server. " + "Now ignoring by name...\n", ig->uid, ig->lastname); } } static const char *g_ccn_name; static ignore_t *g_ccn_found; -static qboolean ccn_iterator (ignore_t *ig, llist_node_t *node) +static bool ccn_iterator (ignore_t *ig, llist_node_t *node) { if (!strcmp (ig->lastname, g_ccn_name)) { g_ccn_found = ig; @@ -215,8 +225,12 @@ CL_Chat_Check_Name (const char *name, int slot) if (g_ccn_found) { g_ccn_found->slot = slot; g_ccn_found->uid = cl.players[slot].userid; - llist_append (ignore_list, llist_remove (llist_getnode (dead_ignore_list, g_ccn_found))); - Sys_Printf ("User %i (%s) is using an ignored name. Now ignoring by user id...\n", g_ccn_found->uid, g_ccn_found->lastname); + llist_append (ignore_list, + llist_remove (llist_getnode (dead_ignore_list, + g_ccn_found))); + Sys_Printf ("User %i (%s) is using an ignored name. " + "Now ignoring by user id...\n", g_ccn_found->uid, + g_ccn_found->lastname); } } @@ -233,30 +247,18 @@ CL_ChatInfo (int val) val = 0; if (cls.chat != val) { cls.chat = val; - Cbuf_AddText(cl_cbuf, va ("setinfo chat \"%d\"\n", val)); + Cbuf_AddText(cl_cbuf, va (0, "setinfo chat \"%d\"\n", val)); } } static void -cl_chat_keydest (keydest_t keydest) +cl_chat_on_focus_change (int game) { - switch (keydest) { - case key_game: - case key_demo: - CL_ChatInfo (0); - break; - case key_message: - CL_ChatInfo (1); - break; - case key_console: - case key_menu: - case key_unfocused: - case key_last: // should not happen - CL_ChatInfo (2); - break; - } + //FIXME afk mode + CL_ChatInfo (!game); } + void CL_Chat_Init (void) { @@ -265,5 +267,5 @@ CL_Chat_Init (void) Cmd_AddCommand ("ignore", CL_Ignore_f, "Ignores chat and name-change messages from a user."); Cmd_AddCommand ("unignore", CL_Unignore_f, "Removes a previously ignored user from the ignore list."); - Key_KeydestCallback (cl_chat_keydest); + CL_OnFocusChange (cl_chat_on_focus_change); } diff --git a/qw/source/cl_cmd.c b/qw/source/cl_cmd.c index 1b02d4581..3b0f7b072 100644 --- a/qw/source/cl_cmd.c +++ b/qw/source/cl_cmd.c @@ -37,12 +37,33 @@ #include "QF/cbuf.h" #include "QF/cmd.h" +#include "QF/dstring.h" #include "QF/msg.h" #include "QF/sys.h" #include "QF/teamplay.h" -#include "client.h" +#include "qw/include/client.h" +static void +send_say (const char *str) +{ + static dstring_t *teambuf; + const char *s; + + if (!teambuf) { + teambuf = dstring_newstr (); + } + + s = Team_ParseSay (teambuf, Cmd_Args (1)); + if (*s && *s < 32 && *s != 10) { + // otherwise the server would eat leading characters + // less than 32 or greater than 127 + SZ_Print (&cls.netchan.message, "\""); + SZ_Print (&cls.netchan.message, s); + SZ_Print (&cls.netchan.message, "\""); + } else + SZ_Print (&cls.netchan.message, s); +} /* CL_Cmd_ForwardToServer @@ -70,21 +91,10 @@ CL_Cmd_ForwardToServer (void) if (!strcasecmp (Cmd_Argv (0), "say") || !strcasecmp (Cmd_Argv (0), "say_team")) { - const char *s; - - s = Team_ParseSay (Cmd_Args (1)); - if (*s && *s < 32 && *s != 10) { - // otherwise the server would eat leading characters - // less than 32 or greater than 127 - SZ_Print (&cls.netchan.message, "\""); - SZ_Print (&cls.netchan.message, s); - SZ_Print (&cls.netchan.message, "\""); - } else - SZ_Print (&cls.netchan.message, s); - return; + send_say(Cmd_Args (1)); + } else { + SZ_Print (&cls.netchan.message, Cmd_Args (1)); } - - SZ_Print (&cls.netchan.message, Cmd_Args (1)); } } diff --git a/qw/source/cl_cvar.c b/qw/source/cl_cvar.c index c92322c7c..78d6290ec 100644 --- a/qw/source/cl_cvar.c +++ b/qw/source/cl_cvar.c @@ -39,21 +39,23 @@ #include "QF/msg.h" #include "QF/va.h" -#include "client.h" #include "compat.h" +#include "qw/include/client.h" + void -Cvar_Info (cvar_t *var) +Cvar_Info (void *data, const cvar_t *cvar) { - if (var->flags & CVAR_USERINFO) { - Info_SetValueForKey (cls.userinfo, var->name, var->string, - ((!strequal(var->name, "name")) - |(strequal(var->name, "team") << 1))); + if (cvar->flags & CVAR_USERINFO) { + const char *cvar_str = Cvar_VarString (cvar); + Info_SetValueForKey (cls.userinfo, cvar->name, cvar_str, + ((!strequal(cvar->name, "name")) + |(strequal(cvar->name, "team") << 1))); if (cls.state >= ca_connected && !cls.demoplayback) { MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, - va ("setinfo \"%s\" \"%s\"\n", var->name, - var->string)); + va (0, "setinfo \"%s\" \"%s\"\n", cvar->name, + cvar_str)); } } } diff --git a/qw/source/cl_demo.c b/qw/source/cl_demo.c index 31ce11620..a114c6db5 100644 --- a/qw/source/cl_demo.c +++ b/qw/source/cl_demo.c @@ -54,13 +54,18 @@ #include "QF/sys.h" #include "QF/va.h" -#include "cl_cam.h" -#include "cl_demo.h" -#include "cl_ents.h" -#include "cl_main.h" -#include "client.h" +#include "QF/scene/scene.h" + #include "compat.h" -#include "host.h" + +#include "client/world.h" + +#include "qw/include/cl_cam.h" +#include "qw/include/cl_demo.h" +#include "qw/include/cl_ents.h" +#include "qw/include/cl_main.h" +#include "qw/include/client.h" +#include "qw/include/host.h" #include "qw/pmove.h" typedef struct { @@ -69,14 +74,14 @@ typedef struct { double fps; } td_stats_t; -int demo_timeframes_isactive; -int demo_timeframes_index; +static int demo_timeframes_isactive; +static int demo_timeframes_index; static int demotime_cached; static float cached_demotime; static byte cached_newtime; -float nextdemotime; -char demoname[1024]; -double *demo_timeframes_array; +static float nextdemotime; +static dstring_t *demoname; +static double *demo_timeframes_array; #define CL_TIMEFRAMES_ARRAYBLOCK 4096 int timedemo_count; @@ -88,10 +93,44 @@ static void CL_TimeFrames_DumpLog (void); static void CL_TimeFrames_AddTimestamp (void); static void CL_TimeFrames_Reset (void); -cvar_t *demo_gzip; -cvar_t *demo_speed; -cvar_t *demo_quit; -cvar_t *demo_timeframes; +int demo_gzip; +static cvar_t demo_gzip_cvar = { + .name = "demo_gzip", + .description = + "Compress demos using gzip. 0 = none, 1 = least compression, 9 = most " + "compression. Compressed demos (1-9) will have .gz appended to the " + "name", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &demo_gzip }, +}; +float demo_speed; +static cvar_t demo_speed_cvar = { + .name = "demo_speed", + .description = + "adjust demo playback speed. 1.0 = normal, < 1 slow-mo, > 1 timelapse", + .default_value = "1.0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &demo_speed }, +}; +int demo_quit; +static cvar_t demo_quit_cvar = { + .name = "demo_quit", + .description = + "automaticly quit after a timedemo has finished", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &demo_quit }, +}; +int demo_timeframes; +static cvar_t demo_timeframes_cvar = { + .name = "demo_timeframes", + .description = + "write timestamps for every frame", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &demo_timeframes }, +}; #define MAX_DEMMSG (MAX_MSGLEN + 8) //+8 for header @@ -148,6 +187,7 @@ CL_StopPlayback (void) cls.demo_capture = 0; cls.demoplayback = 0; cls.demoplayback2 = 0; + cl.viewstate.demoplayback = 0; demotime_cached = 0; net_blocksend = 0; @@ -270,7 +310,7 @@ check_next_demopacket (void) static int read_demopacket (void) { - int r; + unsigned r; Qread (cls.demofile, &net_message->message->cursize, 4); net_message->message->cursize = @@ -332,7 +372,7 @@ nextdemomessage: cls.netchan.outgoing_sequence++; for (i = 0; i < 3; i++) { Qread (cls.demofile, &f, 4); - cl.viewangles[i] = LittleFloat (f); + cl.viewstate.player_angles[i] = LittleFloat (f); } break; @@ -419,9 +459,9 @@ CL_GetMessage (void) if (!CL_GetPacket ()) return 0; - if (net_packetlog->int_val) + if (net_packetlog) Log_Incoming_Packet (net_message->message->data, - net_message->message->cursize, 1, 0); + net_message->message->cursize, 1); if (cls.demorecording) @@ -461,7 +501,7 @@ CL_WriteDemoCmd (usercmd_t *pcmd) Qwrite (cls.demofile, &cmd, sizeof (cmd)); for (i = 0; i < 3; i++) { - fl = LittleFloat (cl.viewangles[i]); + fl = LittleFloat (cl.viewstate.player_angles[i]); Qwrite (cls.demofile, &fl, 4); } @@ -632,8 +672,8 @@ demo_default_name (const char *argv1) time (&tim); strftime (timestring, 19, "%Y-%m-%d-%H-%M", localtime (&tim)); - // the leading path-name is to be removed from cl.worldmodel->name - mapname = QFS_SkipPath (cl.worldmodel->name); + // the leading path-name is to be removed from worldmodel->name + mapname = QFS_SkipPath (cl_world.scene->worldmodel->path); // the map name is cut off after any "." because this would prevent // an extension being appended @@ -651,9 +691,8 @@ demo_start_recording (int track) { byte buf_data[MAX_MSGLEN + 10]; // + 10 for header char *s; - int n, i, j; + int n, i; int seq = 1; - entity_t *ent; entity_state_t *es, blankes; player_info_t *player; sizebuf_t buf; @@ -696,7 +735,7 @@ demo_start_recording (int track) // send server info string MSG_WriteByte (&buf, svc_stufftext); - MSG_WriteString (&buf, va ("fullserverinfo \"%s\"\n", + MSG_WriteString (&buf, va (0, "fullserverinfo \"%s\"\n", Info_MakeString (cl.serverinfo, 0))); // flush packet @@ -754,21 +793,18 @@ demo_start_recording (int track) SZ_Clear (&buf); } // spawnstatic - for (ent = cl_static_entities; ent; ent = ent->unext) { + for (size_t staticIndex = 0; staticIndex < cl_static_entities.size; + staticIndex++) { + entity_state_t *es = &cl_static_entities.a[staticIndex]; + MSG_WriteByte (&buf, svc_spawnstatic); - for (j = 1; j < cl.nummodels; j++) - if (ent->model == cl.model_precache[j]) - break; - if (j == cl.nummodels) - MSG_WriteByte (&buf, 0); - else - MSG_WriteByte (&buf, j); + MSG_WriteByte (&buf, es->modelindex); - MSG_WriteByte (&buf, ent->frame); + MSG_WriteByte (&buf, es->frame); MSG_WriteByte (&buf, 0); - MSG_WriteByte (&buf, ent->skinnum); - MSG_WriteCoordAngleV (&buf, ent->origin, ent->angles); + MSG_WriteByte (&buf, es->skinnum); + MSG_WriteCoordAngleV (&buf, (vec_t*)&es->origin, es->angles);//FIXME if (buf.cursize > MAX_MSGLEN / 2) { CL_WriteRecordDemoMessage (&buf, seq++); @@ -792,7 +828,7 @@ demo_start_recording (int track) MSG_WriteByte (&buf, es->frame); MSG_WriteByte (&buf, es->colormap); MSG_WriteByte (&buf, es->skinnum); - MSG_WriteCoordAngleV (&buf, es->origin, es->angles); + MSG_WriteCoordAngleV (&buf, (vec_t*)&es->origin, es->angles);//FIXME if (buf.cursize > MAX_MSGLEN / 2) { CL_WriteRecordDemoMessage (&buf, seq++); @@ -802,7 +838,7 @@ demo_start_recording (int track) } MSG_WriteByte (&buf, svc_stufftext); - MSG_WriteString (&buf, va ("cmd spawn %i 0\n", cl.servercount)); + MSG_WriteString (&buf, va (0, "cmd spawn %i 0\n", cl.servercount)); if (buf.cursize) { CL_WriteRecordDemoMessage (&buf, seq++); @@ -862,7 +898,7 @@ demo_start_recording (int track) // get the client to check and download skins // when that is completed, a begin command will be issued MSG_WriteByte (&buf, svc_stufftext); - MSG_WriteString (&buf, va ("skins\n")); + MSG_WriteString (&buf, va (0, "skins\n")); CL_WriteRecordDemoMessage (&buf, seq++); @@ -880,9 +916,9 @@ CL_Record (const char *argv1, int track) // open the demo file #ifdef HAVE_ZLIB - if (demo_gzip->int_val) { + if (demo_gzip) { QFS_DefaultExtension (name, ".qwd.gz"); - cls.demofile = QFS_WOpen (name->str, demo_gzip->int_val); + cls.demofile = QFS_WOpen (name->str, demo_gzip); } else #endif { @@ -990,12 +1026,12 @@ CL_StartDemo (void) int type; // open the demo file - name = dstring_strdup (demoname); + name = dstring_strdup (demoname->str); QFS_DefaultExtension (name, ".mvd"); cls.demofile = QFS_FOpenFile (name->str); if (!cls.demofile) { - dstring_copystr (name, demoname); + dstring_copystr (name, demoname->str); QFS_DefaultExtension (name, ".qwd"); cls.demofile = QFS_FOpenFile (name->str); } @@ -1017,13 +1053,13 @@ CL_StartDemo (void) Sys_Printf ("Playing demo from %s.\n", name->str); cls.demoplayback = true; - Key_SetKeyDest (key_demo); + cl.viewstate.demoplayback = 1; net_blocksend = 1; if (type == 2) { cls.demoplayback2 = true; - Sys_MaskPrintf (SYS_DEV, "mvd\n"); + Sys_MaskPrintf (SYS_dev, "mvd\n"); } else { - Sys_MaskPrintf (SYS_DEV, "qwd\n"); + Sys_MaskPrintf (SYS_dev, "qwd\n"); } CL_SetState (ca_demostart); Netchan_Setup (&cls.netchan, net_from, 0, NC_QPORT_SEND); @@ -1050,7 +1086,7 @@ CL_PlayDemo_f (void) { switch (Cmd_Argc ()) { case 1: - if (!demoname[0]) + if (!demoname->str[0]) goto playdemo_error; // fall through case 2: @@ -1072,7 +1108,7 @@ playdemo_error: CL_Disconnect (); if (Cmd_Argc () > 1) - strncpy (demoname, Cmd_Argv (1), sizeof (demoname)); + dstring_copystr (demoname, Cmd_Argv (1)); CL_StartDemo (); } @@ -1093,7 +1129,7 @@ CL_StartTimeDemo (void) cls.td_lastframe = -1; // get a new message this frame CL_TimeFrames_Reset (); - if (demo_timeframes->int_val) + if (demo_timeframes) demo_timeframes_isactive = 1; } @@ -1152,7 +1188,7 @@ CL_FinishTimeDemo (void) Sys_Printf (" min/max fps: %.3f/%.3f\n", min, max); Sys_Printf ("std deviation: %.3f fps\n", sqrt (variance)); } - if (demo_quit->int_val) + if (demo_quit) Cbuf_InsertText (cl_cbuf, "quit\n"); } } @@ -1182,7 +1218,7 @@ CL_TimeDemo_f (void) timedemo_data = 0; } timedemo_data = calloc (timedemo_runs, sizeof (td_stats_t)); - strncpy (demoname, Cmd_Argv (1), sizeof (demoname)); + dstring_copystr (demoname, Cmd_Argv (1)); CL_StartTimeDemo (); timedemo_runs = timedemo_count = max (count, 1); timedemo_data = calloc (timedemo_runs, sizeof (td_stats_t)); @@ -1191,21 +1227,16 @@ CL_TimeDemo_f (void) void CL_Demo_Init (void) { + demoname = dstring_newstr (); + demo_timeframes_isactive = 0; demo_timeframes_index = 0; demo_timeframes_array = NULL; - demo_gzip = Cvar_Get ("demo_gzip", "0", CVAR_ARCHIVE, NULL, - "Compress demos using gzip. 0 = none, 1 = least " - "compression, 9 = most compression. Compressed " - " demos (1-9) will have .gz appended to the name"); - demo_speed = Cvar_Get ("demo_speed", "1.0", CVAR_NONE, NULL, - "adjust demo playback speed. 1.0 = normal, " - "< 1 slow-mo, > 1 timelapse"); - demo_quit = Cvar_Get ("demo_quit", "0", CVAR_NONE, NULL, - "automaticly quit after a timedemo has finished"); - demo_timeframes = Cvar_Get ("demo_timeframes", "0", CVAR_NONE, NULL, - "write timestamps for every frame"); + Cvar_Register (&demo_gzip_cvar, 0, 0); + Cvar_Register (&demo_speed_cvar, 0, 0); + Cvar_Register (&demo_quit_cvar, 0, 0); + Cvar_Register (&demo_timeframes_cvar, 0, 0); Cmd_AddCommand ("record", CL_Record_f, "Record a demo, if no filename " "argument is given\n" "the demo will be called Year-Month-Day-Hour-Minute-" diff --git a/qw/source/cl_entparse.c b/qw/source/cl_entparse.c index ffad98174..a8f454410 100644 --- a/qw/source/cl_entparse.c +++ b/qw/source/cl_entparse.c @@ -36,30 +36,33 @@ #endif #include "QF/cvar.h" -#include "QF/locs.h" #include "QF/msg.h" #include "QF/render.h" #include "QF/skin.h" #include "QF/sys.h" -#include "qw/msg_ucmd.h" +#include "QF/scene/entity.h" -#include "qw/bothdefs.h" -#include "cl_cam.h" -#include "cl_ents.h" -#include "cl_main.h" -#include "cl_parse.h" -#include "cl_pred.h" -#include "cl_tent.h" #include "compat.h" -#include "d_iface.h" -#include "host.h" + +#include "client/temp_entities.h" +#include "client/view.h" +#include "client/world.h" + +#include "qw/msg_ucmd.h" #include "qw/pmove.h" -#include "clview.h" +#include "qw/bothdefs.h" + +#include "qw/include/cl_cam.h" +#include "qw/include/cl_ents.h" +#include "qw/include/cl_main.h" +#include "qw/include/cl_parse.h" +#include "qw/include/cl_pred.h" +#include "qw/include/host.h" static struct predicted_player { int flags; - qboolean active; + bool active; vec3_t origin; // predicted origin } predicted_players[MAX_CLIENTS]; @@ -130,6 +133,8 @@ CL_ParseDelta (entity_state_t *from, entity_state_t *to, int bits) if (bits & U_ANGLE3) to->angles[2] = MSG_ReadAngle (net_message); + to->origin[3] = 1; + if (bits & U_SOLID) { // FIXME } @@ -167,7 +172,7 @@ FlushEntityPacket (void) entity_state_t olde, newe; int word; - Sys_MaskPrintf (SYS_DEV, "FlushEntityPacket\n"); + Sys_MaskPrintf (SYS_dev, "FlushEntityPacket\n"); memset (&olde, 0, sizeof (olde)); @@ -199,12 +204,12 @@ copy_state (packet_entities_t *newp, packet_entities_t *oldp, int newindex, } void -CL_ParsePacketEntities (qboolean delta) +CL_ParsePacketEntities (bool delta) { byte from; int oldindex, newindex, newnum, oldnum, oldpacket, word; packet_entities_t *oldp, *newp, dummy; - qboolean full; + bool full; cl.prev_sequence = cl.link_sequence; cl.link_sequence = cls.netchan.incoming_sequence; @@ -218,7 +223,7 @@ CL_ParsePacketEntities (qboolean delta) if (cls.demoplayback2) from = oldpacket = (cls.netchan.incoming_sequence - 1); if ((from & UPDATE_MASK) != (oldpacket & UPDATE_MASK)) - Sys_MaskPrintf (SYS_DEV, "WARNING: from mismatch\n"); + Sys_MaskPrintf (SYS_dev, "WARNING: from mismatch\n"); } else oldpacket = -1; @@ -360,7 +365,7 @@ CL_ParseDemoPlayerinfo (int num) info = &cl.players[num]; state = &cl.frames[parsecountmod].playerstate[num]; - state->pls.number = num; + state->pls.es.number = num; if (info->prevcount > cl.parsecount || !cl.parsecount) { prevstate = &dummy; @@ -383,25 +388,25 @@ CL_ParseDemoPlayerinfo (int num) memcpy (state, prevstate, sizeof (player_state_t)); flags = MSG_ReadShort (net_message); - state->pls.flags = TranslateFlags (flags); + state->pls.es.flags = TranslateFlags (flags); state->messagenum = cl.parsecount; state->pls.cmd.msec = 0; - state->pls.frame = MSG_ReadByte (net_message); + state->pls.es.frame = MSG_ReadByte (net_message); state->state_time = parsecounttime; for (i=0; i <3; i++) if (flags & (DF_ORIGIN << i)) - state->pls.origin[i] = MSG_ReadCoord (net_message); + state->pls.es.origin[i] = MSG_ReadCoord (net_message); for (i=0; i <3; i++) if (flags & (DF_ANGLES << i)) state->pls.cmd.angles[i] = MSG_ReadAngle16 (net_message); if (flags & DF_MODEL) - state->pls.modelindex = MSG_ReadByte (net_message); + state->pls.es.modelindex = MSG_ReadByte (net_message); if (flags & DF_SKINNUM) - state->pls.skinnum = MSG_ReadByte (net_message); + state->pls.es.skinnum = MSG_ReadByte (net_message); if (flags & DF_EFFECTS) - state->pls.effects = MSG_ReadByte (net_message); + state->pls.es.effects = MSG_ReadByte (net_message); if (flags & DF_WEAPONFRAME) - state->pls.weaponframe = MSG_ReadByte (net_message); + state->pls.es.weaponframe = MSG_ReadByte (net_message); VectorCopy (state->pls.cmd.angles, state->viewangles); } @@ -422,14 +427,15 @@ CL_ParsePlayerinfo (void) state = &cl.frames[parsecountmod].playerstate[num]; - state->pls.number = num; + state->pls.es.number = num; - flags = state->pls.flags = MSG_ReadShort (net_message); + flags = state->pls.es.flags = MSG_ReadShort (net_message); state->messagenum = cl.parsecount; - MSG_ReadCoordV (net_message, state->pls.origin); + MSG_ReadCoordV (net_message, (vec_t*)&state->pls.es.origin);//FIXME + state->pls.es.origin[3] = 1; - state->pls.frame = MSG_ReadByte (net_message); + state->pls.es.frame = MSG_ReadByte (net_message); // the other player's last move was likely some time // before the packet was sent out, so accurately track @@ -445,30 +451,30 @@ CL_ParsePlayerinfo (void) for (i = 0; i < 3; i++) { if (flags & (PF_VELOCITY1 << i)) - state->pls.velocity[i] = (short) MSG_ReadShort (net_message); + state->pls.es.velocity[i] = (short) MSG_ReadShort (net_message); else - state->pls.velocity[i] = 0; + state->pls.es.velocity[i] = 0; } if (flags & PF_MODEL) i = MSG_ReadByte (net_message); else i = cl_playerindex; - state->pls.modelindex = i; + state->pls.es.modelindex = i; if (flags & PF_SKINNUM) - state->pls.skinnum = MSG_ReadByte (net_message); + state->pls.es.skinnum = MSG_ReadByte (net_message); else - state->pls.skinnum = 0; + state->pls.es.skinnum = 0; if (flags & PF_EFFECTS) - state->pls.effects = MSG_ReadByte (net_message); + state->pls.es.effects = MSG_ReadByte (net_message); else - state->pls.effects = 0; + state->pls.es.effects = 0; if (flags & PF_WEAPONFRAME) - state->pls.weaponframe = MSG_ReadByte (net_message); + state->pls.es.weaponframe = MSG_ReadByte (net_message); else - state->pls.weaponframe = 0; + state->pls.es.weaponframe = 0; VectorCopy (state->pls.cmd.angles, state->viewangles); @@ -476,26 +482,28 @@ CL_ParsePlayerinfo (void) // QSG2 int bits; byte val; - entity_t *ent; + entity_t ent; - ent = &cl_player_ents[num]; + ent = CL_GetEntity (num + 1); + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, + cl_world.scene->reg); bits = MSG_ReadByte (net_message); if (bits & PF_ALPHA) { val = MSG_ReadByte (net_message); - ent->colormod[3] = val / 255.0; + renderer->colormod[3] = val / 255.0; } if (bits & PF_SCALE) { val = MSG_ReadByte (net_message); - ent->scale = val / 16.0; + state->pls.es.scale = val; } if (bits & PF_EFFECTS2) { - state->pls.effects |= MSG_ReadByte (net_message) << 8; + state->pls.es.effects |= MSG_ReadByte (net_message) << 8; } if (bits & PF_GLOWSIZE) { - state->pls.glow_size = MSG_ReadByte (net_message); + state->pls.es.glow_size = MSG_ReadByte (net_message); } if (bits & PF_GLOWCOLOR) { - state->pls.glow_color = MSG_ReadByte (net_message); + state->pls.es.glow_color = MSG_ReadByte (net_message); } if (bits & PF_COLORMOD) { float r = 1.0, g = 1.0, b = 1.0; @@ -505,10 +513,10 @@ CL_ParsePlayerinfo (void) g = (float) ((val >> 2) & 7) * (1.0 / 7.0); b = (float) (val & 3) * (1.0 / 3.0); } - VectorSet (r, g, b, ent->colormod); + VectorSet (r, g, b, renderer->colormod); } if (bits & PF_FRAME2) { - state->pls.frame |= MSG_ReadByte (net_message) << 8; + state->pls.es.frame |= MSG_ReadByte (net_message) << 8; } } } @@ -526,7 +534,7 @@ CL_SetSolidEntities (void) frame_t *frame; packet_entities_t *pak; - pmove.physents[0].model = cl.worldmodel; + pmove.physents[0].model = cl_world.scene->worldmodel; VectorZero (pmove.physents[0].origin); VectorZero (pmove.physents[0].angles); pmove.physents[0].info = 0; @@ -540,17 +548,17 @@ CL_SetSolidEntities (void) if (!state->modelindex) continue; - if (!cl.model_precache[state->modelindex]) + if (!cl_world.models.a[state->modelindex]) continue; - if (cl.model_precache[state->modelindex]->hulls[1].firstclipnode - || cl.model_precache[state->modelindex]->clipbox) { + if (cl_world.models.a[state->modelindex]->brush.hulls[1].firstclipnode + || cl_world.models.a[state->modelindex]->clipbox) { if (pmove.numphysent == MAX_PHYSENTS) { Sys_Printf ("WARNING: entity physent overflow, email " "quakeforge-devel@lists.quakeforge.net\n"); break; } pmove.physents[pmove.numphysent].model = - cl.model_precache[state->modelindex]; + cl_world.models.a[state->modelindex]; VectorCopy (state->origin, pmove.physents[pmove.numphysent].origin); VectorCopy (state->angles, @@ -576,7 +584,7 @@ CL_ClearPredict (void) This sets up the first phase. */ void -CL_SetUpPlayerPrediction (qboolean dopred) +CL_SetUpPlayerPrediction (bool dopred) { double playertime; frame_t *frame; @@ -599,30 +607,31 @@ CL_SetUpPlayerPrediction (qboolean dopred) if (state->messagenum != cl.parsecount) continue; // not present this frame - if (!state->pls.modelindex) + if (!state->pls.es.modelindex) continue; pplayer->active = true; - pplayer->flags = state->pls.flags; + pplayer->flags = state->pls.es.flags; // note that the local player is special, since he moves locally // we use his last predicted postition if (j == cl.playernum) { VectorCopy (cl.frames[cls.netchan.outgoing_sequence & UPDATE_MASK]. - playerstate[cl.playernum].pls.origin, pplayer->origin); + playerstate[cl.playernum].pls.es.origin, + pplayer->origin); } else { // predict only half the move to minimize overruns msec = 500 * (playertime - state->state_time); if (msec <= 0 || !dopred) { - VectorCopy (state->pls.origin, pplayer->origin); -// Sys_MaskPrintf (SYS_DEV, "nopredict\n"); + VectorCopy (state->pls.es.origin, pplayer->origin); +// Sys_MaskPrintf (SYS_dev, "nopredict\n"); } else { // predict players movement state->pls.cmd.msec = msec = min (msec, 255); -// Sys_MaskPrintf (SYS_DEV, "predict: %i\n", msec); +// Sys_MaskPrintf (SYS_dev, "predict: %i\n", msec); CL_PredictUsercmd (state, &exact, &state->pls.cmd, false); - VectorCopy (exact.pls.origin, pplayer->origin); + VectorCopy (exact.pls.es.origin, pplayer->origin); } } } @@ -643,7 +652,7 @@ CL_SetSolidPlayers (int playernum) physent_t *pent; struct predicted_player *pplayer; - if (!cl_solid_players->int_val) + if (!cl_solid_players) return; pent = pmove.physents + pmove.numphysent; diff --git a/qw/source/cl_ents.c b/qw/source/cl_ents.c index 5661b1b0f..7a4be0705 100644 --- a/qw/source/cl_ents.c +++ b/qw/source/cl_ents.c @@ -36,29 +36,32 @@ #endif #include "QF/cvar.h" -#include "QF/locs.h" #include "QF/msg.h" #include "QF/render.h" #include "QF/skin.h" #include "QF/sys.h" -#include "qw/msg_ucmd.h" +#include "QF/scene/entity.h" + +#include "compat.h" + +#include "client/effects.h" +#include "client/locs.h" +#include "client/temp_entities.h" +#include "client/view.h" +#include "client/world.h" #include "qw/bothdefs.h" -#include "chase.h" -#include "cl_cam.h" -#include "cl_ents.h" -#include "cl_main.h" -#include "cl_parse.h" -#include "cl_pred.h" -#include "cl_tent.h" -#include "compat.h" -#include "d_iface.h" -#include "host.h" +#include "qw/msg_ucmd.h" #include "qw/pmove.h" -#include "clview.h" -entity_t cl_player_ents[MAX_CLIENTS]; +#include "qw/include/cl_cam.h" +#include "qw/include/cl_ents.h" +#include "qw/include/cl_main.h" +#include "qw/include/cl_parse.h" +#include "qw/include/cl_pred.h" +#include "qw/include/host.h" + entity_t cl_flag_ents[MAX_CLIENTS]; entity_t cl_entities[512]; // FIXME: magic number byte cl_entity_valid[2][512]; @@ -68,82 +71,48 @@ CL_ClearEnts (void) { size_t i; + for (i = 0; i < MAX_CLIENTS; i++) { + if (Entity_Valid (cl_flag_ents[i])) { + Scene_DestroyEntity (cl_world.scene, cl_flag_ents[i]); + cl_flag_ents[i] = nullentity; + } + } + + for (i = 0; i < 512; i++) { + if (Entity_Valid (cl_entities[i])) { + Scene_DestroyEntity (cl_world.scene, cl_entities[i]); + cl_entities[i] = nullentity; + } + } i = qw_entstates.num_frames * qw_entstates.num_entities; memset (qw_entstates.frame[0], 0, i * sizeof (entity_state_t)); memset (cl_entity_valid, 0, sizeof (cl_entity_valid)); - for (i = 0; i < sizeof (cl_entities) / sizeof (cl_entities[0]); i++) - CL_Init_Entity (&cl_entities[i]); - for (i = 0; i < sizeof (cl_flag_ents) / sizeof (cl_flag_ents[0]); i++) - CL_Init_Entity (&cl_flag_ents[i]); - for (i = 0; i < sizeof (cl_player_ents) / sizeof (cl_player_ents[0]); i++) - CL_Init_Entity (&cl_player_ents[i]); } -static void -CL_NewDlight (int key, vec3_t org, int effects, byte glow_size, - byte glow_color) +static entity_t +CL_GetInvalidEntity (int num) { - float radius; - dlight_t *dl; - static quat_t normal = {0.4, 0.2, 0.05, 0.7}; - static quat_t red = {0.5, 0.05, 0.05, 0.7}; - static quat_t blue = {0.05, 0.05, 0.5, 0.7}; - static quat_t purple = {0.5, 0.05, 0.5, 0.7}; + return cl_entities[num]; +} - effects &= EF_BLUE | EF_RED | EF_BRIGHTLIGHT | EF_DIMLIGHT; - if (!effects) { - if (!glow_size) - return; +entity_t +CL_GetEntity (int num) +{ + if (!Entity_Valid (cl_entities[num])) { + cl_entities[num] = Scene_CreateEntity (cl_world.scene); + CL_Init_Entity (cl_entities[num]); } + return cl_entities[num]; +} - dl = r_funcs->R_AllocDlight (key); - if (!dl) - return; - VectorCopy (org, dl->origin); - - if (effects & (EF_BLUE | EF_RED | EF_BRIGHTLIGHT | EF_DIMLIGHT)) { - radius = 200 + (rand () & 31); - if (effects & EF_BRIGHTLIGHT) { - radius += 200; - dl->origin[2] += 16; - } - if (effects & EF_DIMLIGHT) - if (effects & ~EF_DIMLIGHT) - radius -= 100; - dl->radius = radius; - dl->die = cl.time + 0.1; - - switch (effects & (EF_RED | EF_BLUE)) { - case EF_RED | EF_BLUE: - QuatCopy (purple, dl->color); - break; - case EF_RED: - QuatCopy (red, dl->color); - break; - case EF_BLUE: - QuatCopy (blue, dl->color); - break; - default: - QuatCopy (normal, dl->color); - break; - } - } - - if (glow_size) { - dl->radius += glow_size < 128 ? glow_size * 8.0 : - (glow_size - 256) * 8.0; - dl->die = cl.time + 0.1; - if (glow_color) { - if (glow_color == 255) { - dl->color[0] = dl->color[1] = dl->color[2] = 1.0; - } else { - byte *tempcolor; - - tempcolor = (byte *) &d_8to24table[glow_color]; - VectorScale (tempcolor, 1 / 255.0, dl->color); - } - } +static entity_t +CL_GetFlagEnt (int key) +{ + if (!Entity_Valid (cl_flag_ents[key])) { + cl_flag_ents[key] = Scene_CreateEntity (cl_world.scene); + CL_Init_Entity (cl_flag_ents[key]); } + return cl_flag_ents[key]; } // Hack hack hack @@ -168,85 +137,24 @@ is_gib (entity_state_t *s1) return 0; } -void -CL_TransformEntity (entity_t *ent, const vec3_t angles, qboolean force) -{ - vec3_t ang; - vec_t *forward, *left, *up; - - if (VectorIsZero (angles)) { - VectorSet (1, 0, 0, ent->transform + 0); - VectorSet (0, 1, 0, ent->transform + 4); - VectorSet (0, 0, 1, ent->transform + 8); - } else if (force || !VectorCompare (angles, ent->angles)) { - forward = ent->transform + 0; - left = ent->transform + 4; - up = ent->transform + 8; - VectorCopy (angles, ang); - if (ent->model && ent->model->type == mod_alias) { - // stupid quake bug - // why, oh, why, do alias models pitch in the opposite direction - // to everything else? - ang[PITCH] = -ang[PITCH]; - } - AngleVectors (ang, forward, left, up); - VectorNegate (left, left); // AngleVectors is right-handed - } - VectorCopy (angles, ent->angles); - ent->transform[3] = 0; - ent->transform[7] = 0; - ent->transform[11] = 0; - VectorCopy (ent->origin, ent->transform + 12); - ent->transform[15] = 1; -} - static void -CL_ModelEffects (entity_t *ent, int num, int glow_color) +set_entity_model (entity_t ent, int modelindex) { - dlight_t *dl; - model_t *model = ent->model; - - // add automatic particle trails - if (model->flags & EF_ROCKET) { - dl = r_funcs->R_AllocDlight (num); - if (dl) { - VectorCopy (ent->origin, dl->origin); - dl->radius = 200.0; - dl->die = cl.time + 0.1; - //FIXME VectorCopy (r_firecolor->vec, dl->color); - VectorSet (0.9, 0.7, 0.0, dl->color); - dl->color[3] = 0.7; - } - r_funcs->particles->R_RocketTrail (ent); - } else if (model->flags & EF_GRENADE) - r_funcs->particles->R_GrenadeTrail (ent); - else if (model->flags & EF_GIB) - r_funcs->particles->R_BloodTrail (ent); - else if (model->flags & EF_ZOMGIB) - r_funcs->particles->R_SlightBloodTrail (ent); - else if (model->flags & EF_TRACER) - r_funcs->particles->R_WizTrail (ent); - else if (model->flags & EF_TRACER2) - r_funcs->particles->R_FlameTrail (ent); - else if (model->flags & EF_TRACER3) - r_funcs->particles->R_VoorTrail (ent); - else if (model->flags & EF_GLOWTRAIL) - if (r_funcs->particles->R_GlowTrail) - r_funcs->particles->R_GlowTrail (ent, glow_color); -} - -static void -set_entity_model (entity_t *ent, int modelindex) -{ - ent->model = cl.model_precache[modelindex]; + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, + cl_world.scene->reg); + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, + cl_world.scene->reg); + renderer->model = cl_world.models.a[modelindex]; // automatic animation (torches, etc) can be either all together // or randomized - if (ent->model) { - if (ent->model->synctype == ST_RAND) - ent->syncbase = (float) (rand () & 0x7fff) / 0x7fff; - else - ent->syncbase = 0.0; + if (renderer->model) { + if (renderer->model->synctype == ST_RAND) { + animation->syncbase = (float) (rand () & 0x7fff) / 0x7fff; + } else { + animation->syncbase = 0.0; + } } + animation->nolerp = 1; // don't try to lerp when the model has changed } static void @@ -254,38 +162,43 @@ CL_LinkPacketEntities (void) { int i, j, forcelink; float frac, f; - entity_t *ent; + entity_t ent; entity_state_t *new, *old; - vec3_t delta; frac = 1; - for (i = 0; i < 512; i++) { + for (i = MAX_CLIENTS + 1; i < 512; i++) { new = &qw_entstates.frame[cl.link_sequence & UPDATE_MASK][i]; old = &qw_entstates.frame[cl.prev_sequence & UPDATE_MASK][i]; - ent = &cl_entities[i]; + + ent = CL_GetInvalidEntity (i); forcelink = cl_entity_valid[0][i] != cl_entity_valid[1][i]; cl_entity_valid[1][i] = cl_entity_valid[0][i]; - // if the object wasn't included in the last packet, remove it - if (!cl_entity_valid[0][i]) { - ent->model = NULL; - ent->pose1 = ent->pose2 = -1; - if (ent->efrag) - r_funcs->R_RemoveEfrags (ent); // just became empty + // if the object wasn't included in the last packet, or set + // to invisible, remove it + if (!cl_entity_valid[0][i] + || !new->modelindex + || (cl_deadbodyfilter && is_dead_body (new)) + || (cl_gibfilter && is_gib (new))) { + if (Entity_Valid (ent)) { + Scene_DestroyEntity (cl_world.scene, ent); + } continue; } + if (!Entity_Valid (ent)) { + ent = CL_GetEntity (i); + forcelink = true; + } + transform_t transform = Entity_Transform (ent); + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, + ent.reg); + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, + ent.reg); + vec4f_t *old_origin = Ent_GetComponent (ent.id, scene_old_origin, + ent.reg); // spawn light flashes, even ones coming from invisible objects CL_NewDlight (i, new->origin, new->effects, new->glow_size, - new->glow_color); - - // if set to invisible, skip - if (!new->modelindex - || (cl_deadbodyfilter->int_val && is_dead_body (new)) - || (cl_gibfilter->int_val && is_gib (new))) { - if (ent->efrag) - r_funcs->R_RemoveEfrags (ent); - continue; - } + new->glow_color, cl.time); if (forcelink) *old = *new; @@ -294,158 +207,185 @@ CL_LinkPacketEntities (void) old->modelindex = new->modelindex; set_entity_model (ent, new->modelindex); } - ent->frame = new->frame; + animation->frame = new->frame; if (forcelink || new->colormap != old->colormap || new->skinnum != old->skinnum) { old->skinnum = new->skinnum; - ent->skinnum = new->skinnum; + renderer->skinnum = new->skinnum; old->colormap = new->colormap; if (new->colormap && (new->colormap <= MAX_CLIENTS) && cl.players[new->colormap - 1].name && cl.players[new->colormap - 1].name->value[0] && new->modelindex == cl_playerindex) { player_info_t *player = &cl.players[new->colormap - 1]; - ent->skin = mod_funcs->Skin_SetSkin (ent->skin, new->colormap, - player->skinname->value); - ent->skin = mod_funcs->Skin_SetColormap (ent->skin, - new->colormap); + colormap_t colormap = { + .top = player->topcolor, + .bottom = player->bottomcolor, + }; + Ent_SetComponent (ent.id, scene_colormap, ent.reg, &colormap); + renderer->skin + = mod_funcs->Skin_SetSkin (renderer->skin, new->colormap, + player->skinname->value); + renderer->skin = mod_funcs->Skin_SetColormap (renderer->skin, + new->colormap); } else { - ent->skin = mod_funcs->Skin_SetColormap (ent->skin, 0); + renderer->skin = mod_funcs->Skin_SetColormap (renderer->skin, + 0); + Ent_RemoveComponent (ent.id, scene_colormap, ent.reg); } } - ent->scale = new->scale / 16.0; - VectorCopy (ent_colormod[new->colormod], ent->colormod); - ent->colormod[3] = new->alpha / 255.0; + VectorCopy (ent_colormod[new->colormod], renderer->colormod); + renderer->colormod[3] = new->alpha / 255.0; - ent->min_light = 0; - ent->fullbright = 0; + renderer->min_light = 0; + renderer->fullbright = 0; if (new->modelindex == cl_playerindex) { - ent->min_light = min (cl.fbskins, cl_fb_players->value); - if (ent->min_light >= 1.0) - ent->fullbright = 1; + renderer->min_light = min (cl.fbskins, cl_fb_players); + if (renderer->min_light >= 1.0) { + renderer->fullbright = 1; + } } + *old_origin = Transform_GetWorldPosition (transform); if (forcelink) { - ent->pose1 = ent->pose2 = -1; - VectorCopy (new->origin, ent->origin); - if (!(ent->model->flags & EF_ROTATE)) - CL_TransformEntity (ent, new->angles, true); - if (i != cl.viewentity || chase_active->int_val) { - if (ent->efrag) - r_funcs->R_RemoveEfrags (ent); - r_funcs->R_AddEfrags (ent); + animation->pose1 = animation->pose2 = -1; + CL_TransformEntity (ent, new->scale / 16, new->angles, + new->origin); + if (i != cl.viewentity || chase_active) { + R_AddEfrags (&cl_world.scene->worldmodel->brush, ent); } - VectorCopy (ent->origin, ent->old_origin); } else { + vec4f_t delta = new->origin - old->origin; f = frac; - VectorCopy (ent->origin, ent->old_origin); - VectorSubtract (new->origin, old->origin, delta); // If the delta is large, assume a teleport and don't lerp - if (fabs (delta[0]) > 100 || fabs (delta[1] > 100) + if (fabs (delta[0]) > 100 || fabs (delta[1]) > 100 || fabs (delta[2]) > 100) { // assume a teleportation, not a motion - VectorCopy (new->origin, ent->origin); - if (!(ent->model->flags & EF_ROTATE)) - CL_TransformEntity (ent, new->angles, true); - ent->pose1 = ent->pose2 = -1; - } else { + CL_TransformEntity (ent, new->scale / 16, new->angles, + new->origin); + animation->pose1 = animation->pose2 = -1; + } else if (!(renderer->model->flags & EF_ROTATE)) { vec3_t angles, d; + vec4f_t origin = old->origin + f * delta; // interpolate the origin and angles - VectorMultAdd (old->origin, f, delta, ent->origin); - if (!(ent->model->flags & EF_ROTATE)) { - VectorSubtract (new->angles, old->angles, d); - for (j = 0; j < 3; j++) { - if (d[j] > 180) - d[j] -= 360; - else if (d[j] < -180) - d[j] += 360; - } - VectorMultAdd (old->angles, f, d, angles); - CL_TransformEntity (ent, angles, false); + VectorSubtract (new->angles, old->angles, d); + for (j = 0; j < 3; j++) { + if (d[j] > 180) + d[j] -= 360; + else if (d[j] < -180) + d[j] += 360; } + VectorMultAdd (old->angles, f, d, angles); + CL_TransformEntity (ent, new->scale / 16.0, angles, origin); } - if (i != cl.viewentity || chase_active->int_val) { - if (ent->efrag) { - if (!VectorCompare (ent->origin, ent->old_origin)) { - r_funcs->R_RemoveEfrags (ent); - r_funcs->R_AddEfrags (ent); - } - } else { - r_funcs->R_AddEfrags (ent); + if (i != cl.viewentity || chase_active) { + vec4f_t org = Transform_GetWorldPosition (transform); + if (!VectorCompare (org, *old_origin)) {//FIXME + R_AddEfrags (&cl_world.scene->worldmodel->brush, ent); } } } - if (!ent->efrag) - r_funcs->R_AddEfrags (ent); // rotate binary objects locally - if (ent->model->flags & EF_ROTATE) { + if (renderer->model->flags & EF_ROTATE) { vec3_t angles; angles[PITCH] = 0; angles[YAW] = anglemod (100 * cl.time); angles[ROLL] = 0; - CL_TransformEntity (ent, angles, false); + CL_TransformEntity (ent, new->scale / 16.0, angles, new->origin); } //CL_EntityEffects (i, ent, new); - //CL_NewDlight (i, ent->origin, new->effects, 0, 0); - if (VectorDistance_fast (old->origin, ent->origin) > (256 * 256)) - VectorCopy (ent->origin, old->origin); - if (ent->model->flags & ~EF_ROTATE) - CL_ModelEffects (ent, -new->number, new->glow_color); + //CL_NewDlight (i, ent->origin, new->effects, 0, 0, cl.time); + vec4f_t org = Transform_GetWorldPosition (transform); + if (VectorDistance_fast (old->origin, org) > (256 * 256)) + old->origin = org; + if (renderer->model->flags & ~EF_ROTATE) { + CL_ModelEffects (ent, -new->number, new->glow_color, cl.time); + } } } -/* - CL_AddFlagModels - - Called when the CTF flags are set. Flags are effectively temp entities. - - NOTE: this must be called /after/ the entity has been transformed as it - uses the entity's transform matrix to get the frame vectors -*/ static void -CL_AddFlagModels (entity_t *ent, int team, int key) +CL_UpdateFlagModels (entity_t ent, int key) { static float flag_offsets[] = { 16.0, 22.0, 26.0, 25.0, 24.0, 18.0, // 29-34 axpain 16.0, 24.0, 24.0, 22.0, 18.0, 16.0, // 35-40 pain }; float f; - entity_t *fent; - vec_t *v_forward, *v_left; - vec3_t ang; + entity_t fent = CL_GetFlagEnt (key); + byte *active = Ent_GetComponent (fent.id, scene_active, + cl_world.scene->reg); - if (cl_flagindex == -1) + if (!*active) { return; + } + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, + cl_world.scene->reg); f = 14.0; - if (ent->frame >= 29 && ent->frame <= 40) { - f = flag_offsets[ent->frame - 29]; - } else if (ent->frame >= 103 && ent->frame <= 118) { - if (ent->frame <= 106) // 103-104 nailattack - f = 20.0; // 105-106 light - else // 107-112 rocketattack - f = 21.0; // 112-118 shotattack + if (animation->frame >= 29 && animation->frame <= 40) { + f = flag_offsets[animation->frame - 29]; + } else if (animation->frame >= 103 && animation->frame <= 118) { + if (animation->frame <= 106) { // 103-104 nailattack + f = 20.0; // 105-106 light + } else { // 107-112 rocketattack + f = 21.0; // 112-118 shotattack + } } - fent = &cl_flag_ents[key]; - fent->model = cl.model_precache[cl_flagindex]; - fent->skinnum = team; + vec4f_t scale = { 1, 1, 1, 1 }; + // -45 degree roll (x is forward) + vec4f_t rotation = { -0.382683432, 0, 0, 0.923879533 }; + vec4f_t position = { -f, -22, -16, 1}; - v_forward = ent->transform + 0; - v_left = ent->transform + 4; + transform_t transform = Entity_Transform (fent); + Transform_SetLocalTransform (transform, scale, rotation, position); +} - VectorMultAdd (ent->origin, -f, v_forward, fent->origin); - VectorMultAdd (fent->origin, -22, v_left, fent->origin); - fent->origin[2] -= 16.0; +static entity_t +CL_AddFlagModels (entity_t ent, int team, int key) +{ + entity_t fent; - VectorCopy (ent->angles, ang); - ang[2] -= 45.0; - CL_TransformEntity (fent, ang, false); + fent = CL_GetFlagEnt (key); + byte *active = Ent_GetComponent (fent.id, scene_active, + cl_world.scene->reg); - r_funcs->R_EnqueueEntity (fent);//FIXME should use efrag (needs smarter - // handling //in the player code) + if (cl_flagindex == -1) { + *active = 0; + return nullentity; + } + + *active = 1; + + transform_t ftransform = Entity_Transform (fent); + transform_t transform = Entity_Transform (ent); + if (!Transform_Valid (Transform_GetParent (ftransform))) { + Transform_SetParent (ftransform, transform); + } + CL_UpdateFlagModels (ent, key); + + renderer_t *renderer = Ent_GetComponent (fent.id, scene_renderer, + cl_world.scene->reg); + renderer->model = cl_world.models.a[cl_flagindex]; + renderer->skinnum = team; + + return fent; +} + +static void +CL_RemoveFlagModels (int key) +{ + entity_t fent; + + fent = CL_GetFlagEnt (key); + byte *active = Ent_GetComponent (fent.id, scene_active, + cl_world.scene->reg); + transform_t transform = Entity_Transform (fent); + *active = 0; + Transform_SetParent (transform, nulltransform); } /* @@ -458,14 +398,15 @@ static void CL_LinkPlayers (void) { double playertime; - int msec, oldphysent, i, j; - entity_t *ent; + int msec, oldphysent, j; + entity_t ent; frame_t *frame; - player_info_t *info; + player_info_t *player; player_state_t exact; player_state_t *state; - qboolean clientplayer; - vec3_t org, ang = {0, 0, 0}; + bool clientplayer; + vec3_t ang = {0, 0, 0}; + vec4f_t org; playertime = realtime - cls.latency + 0.02; if (playertime > realtime) @@ -473,105 +414,138 @@ CL_LinkPlayers (void) frame = &cl.frames[cl.parsecount & UPDATE_MASK]; - for (j = 0, info = cl.players, state = frame->playerstate; j < MAX_CLIENTS; - j++, info++, state++) { - ent = &cl_player_ents[j]; - if (ent->efrag) - r_funcs->R_RemoveEfrags (ent); - if (state->messagenum != cl.parsecount) - continue; // not present this frame - - if (!info->name || !info->name->value[0]) + for (j = 0, player = cl.players, state = frame->playerstate; + j < MAX_CLIENTS; j++, player++, state++) { + ent = CL_GetInvalidEntity (j + 1); + if (state->messagenum != cl.parsecount + || !player->name || !player->name->value[0]) { + // not present this frame + if (Entity_Valid (ent)) { + Scene_DestroyEntity (cl_world.scene, ent); + } continue; + } + + if (!Entity_Valid (ent)) { + ent = CL_GetEntity (j + 1); + } + renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, + cl_world.scene->reg); + animation_t *animation = Ent_GetComponent (ent.id, scene_animation, + cl_world.scene->reg); // spawn light flashes, even ones coming from invisible objects if (j == cl.playernum) { - VectorCopy (cl.simorg, org); - r_data->player_entity = &cl_player_ents[j]; + org = cl.viewstate.player_origin; + cl.viewstate.player_entity = ent; clientplayer = true; } else { - VectorCopy (state->pls.origin, org); + org = state->pls.es.origin; clientplayer = false; } - if (info->chat && info->chat->value[0] != '0') { - dlight_t *dl = r_funcs->R_AllocDlight (j + 1); + if (player->chat && player->chat->value[0] != '0') { + dlight_t *dl = R_AllocDlight (j + 1); VectorCopy (org, dl->origin); dl->radius = 100; dl->die = cl.time + 0.1; QuatSet (0.0, 1.0, 0.0, 1.0, dl->color); } else { - CL_NewDlight (j + 1, org, state->pls.effects, state->pls.glow_size, - state->pls.glow_color); + CL_NewDlight (j + 1, org, state->pls.es.effects, + state->pls.es.glow_size, state->pls.es.glow_color, + cl.time); } // Draw player? if (!Cam_DrawPlayer (j)) continue; - if (!state->pls.modelindex) + if (!state->pls.es.modelindex) continue; // Hack hack hack - if (cl_deadbodyfilter->int_val - && state->pls.modelindex == cl_playerindex - && ((i = state->pls.frame) == 49 || i == 60 || i == 69 || i == 84 - || i == 93 || i == 102)) + if (cl_deadbodyfilter + && state->pls.es.modelindex == cl_playerindex + && is_dead_body (&state->pls.es)) continue; + colormap_t colormap = { + .top = player->topcolor, + .bottom = player->bottomcolor, + }; + Ent_SetComponent (ent.id, scene_colormap, ent.reg, &colormap); + // predict only half the move to minimize overruns msec = 500 * (playertime - state->state_time); - if (msec <= 0 || (!cl_predict_players->int_val) || cls.demoplayback2) { - VectorCopy (state->pls.origin, ent->origin); + if (msec <= 0 || (!cl_predict_players) || cls.demoplayback2) { + Sys_Printf("a\n"); + exact.pls.es.origin = state->pls.es.origin; } else { // predict players movement state->pls.cmd.msec = msec = min (msec, 255); oldphysent = pmove.numphysent; CL_SetSolidPlayers (j); + exact.pls.es.origin[3] = 1;//FIXME should be done by prediction CL_PredictUsercmd (state, &exact, &state->pls.cmd, clientplayer); pmove.numphysent = oldphysent; - VectorCopy (exact.pls.origin, ent->origin); } // angles if (j == cl.playernum) { - ang[PITCH] = -cl.viewangles[PITCH] / 3.0; - ang[YAW] = cl.viewangles[YAW]; + ang[PITCH] = -cl.viewstate.player_angles[PITCH] / 3.0; + ang[YAW] = cl.viewstate.player_angles[YAW]; } else { ang[PITCH] = -state->viewangles[PITCH] / 3.0; ang[YAW] = state->viewangles[YAW]; } - ang[ROLL] = V_CalcRoll (ang, state->pls.velocity) * 4.0; + ang[ROLL] = V_CalcRoll (ang, state->pls.es.velocity) * 4.0; - ent->model = cl.model_precache[state->pls.modelindex]; - ent->frame = state->pls.frame; - ent->skinnum = state->pls.skinnum; + if (renderer->model + != cl_world.models.a[state->pls.es.modelindex]) { + renderer->model = cl_world.models.a[state->pls.es.modelindex]; + animation->nolerp = 1; + } + animation->frame = state->pls.es.frame; + renderer->skinnum = state->pls.es.skinnum; - CL_TransformEntity (ent, ang, false); + //FIXME scale + CL_TransformEntity (ent, 1, ang, exact.pls.es.origin); - ent->min_light = 0; - ent->fullbright = 0; + renderer->min_light = 0; + renderer->fullbright = 0; - if (state->pls.modelindex == cl_playerindex) { //XXX + if (state->pls.es.modelindex == cl_playerindex) { //XXX // use custom skin - ent->skin = info->skin; + renderer->skin = player->skin; - ent->min_light = min (cl.fbskins, cl_fb_players->value); + renderer->min_light = min (cl.fbskins, cl_fb_players); - if (ent->min_light >= 1.0) - ent->fullbright = 1; + if (renderer->min_light >= 1.0) { + renderer->fullbright = 1; + } } else { // FIXME no team colors on nonstandard player models - ent->skin = 0; + renderer->skin = 0; + } + + int flag_state = state->pls.es.effects & (EF_FLAG1 | EF_FLAG2); + if (Entity_Valid (player->flag_ent) && !flag_state) { + CL_RemoveFlagModels (j); + player->flag_ent = (entity_t) nullentity; + } else if (!Entity_Valid (player->flag_ent) && flag_state) { + if (flag_state & EF_FLAG1) + player->flag_ent = CL_AddFlagModels (ent, 0, j); + else if (flag_state & EF_FLAG2) + player->flag_ent = CL_AddFlagModels (ent, 1, j); } // stuff entity in map - r_funcs->R_AddEfrags (ent); - - if (state->pls.effects & EF_FLAG1) - CL_AddFlagModels (ent, 0, j); - else if (state->pls.effects & EF_FLAG2) - CL_AddFlagModels (ent, 1, j); + R_AddEfrags (&cl_world.scene->worldmodel->brush, ent); + if (Entity_Valid (player->flag_ent)) { + CL_UpdateFlagModels (ent, j); + entity_t fent = player->flag_ent; + R_AddEfrags (&cl_world.scene->worldmodel->brush, fent); + } } } @@ -590,44 +564,19 @@ CL_EmitEntities (void) if (!cl.validsequence) return; + TEntContext_t tentCtx = { + cl.viewstate.player_origin, cl.viewentity + }; + CL_LinkPlayers (); CL_LinkPacketEntities (); - CL_UpdateTEnts (); - if (cl_draw_locs->int_val) { - //FIXME custom ent rendering code would be nice - dlight_t *dl; - location_t *nearloc; - vec3_t trueloc; - int i; - - nearloc = locs_find (cl.simorg); - if (nearloc) { - dl = r_funcs->R_AllocDlight (4096); - if (dl) { - VectorCopy (nearloc->loc, dl->origin); - dl->radius = 200; - dl->die = r_data->realtime + 0.1; - dl->color[0] = 0; - dl->color[1] = 1; - dl->color[2] = 0; - dl->color[3] = 0.7; - } - VectorCopy (nearloc->loc, trueloc); - r_funcs->particles->R_Particle_New ("pt_smokecloud", part_tex_smoke, - trueloc, 2.0, - vec3_origin, r_data->realtime + 9.0, 254, - 0.25 + qfrandom (0.125), 0.0); - for (i = 0; i < 15; i++) - r_funcs->particles->R_Particle_NewRandom ("pt_fallfade", - part_tex_dot, trueloc, 12, - 0.7, 96, r_data->realtime + 5.0, - 104 + (rand () & 7), 1.0, 0.0); - } + CL_UpdateTEnts (cl.time, &tentCtx); + if (cl_draw_locs) { + locs_draw (cl.time, cl.viewstate.player_origin); } } void CL_Ents_Init (void) { - r_data->view_model = &cl.viewent; } diff --git a/qw/source/cl_http.c b/qw/source/cl_http.c index 38bd9f204..6ee223943 100644 --- a/qw/source/cl_http.c +++ b/qw/source/cl_http.c @@ -38,17 +38,17 @@ #include "QF/dstring.h" #include "QF/sys.h" -#include "cl_http.h" -#include "cl_parse.h" -#include "client.h" +#include "qw/include/cl_http.h" +#include "qw/include/cl_parse.h" +#include "qw/include/client.h" static int curl_borked; static CURL *easy_handle; static CURLM *multi_handle; static int -http_progress (void *clientp, double dltotal, double dlnow, - double ultotal, double uplow) +http_progress (void *clientp, curl_off_t dltotal, curl_off_t dlnow, + curl_off_t ultotal, curl_off_t uplow) { return 0; //non-zero = abort } @@ -87,7 +87,7 @@ CL_HTTP_StartDownload (void) curl_easy_setopt (easy_handle, CURLOPT_NOPROGRESS, 1L); curl_easy_setopt (easy_handle, CURLOPT_NOSIGNAL, 1L); - curl_easy_setopt (easy_handle, CURLOPT_PROGRESSFUNCTION, http_progress); + curl_easy_setopt (easy_handle, CURLOPT_XFERINFOFUNCTION, http_progress); curl_easy_setopt (easy_handle, CURLOPT_WRITEFUNCTION, http_write); curl_easy_setopt (easy_handle, CURLOPT_URL, cls.downloadurl->str); @@ -129,7 +129,7 @@ CL_HTTP_Reset (void) #else -#include "cl_http.h" +#include "qw/include/cl_http.h" void CL_HTTP_Init (void) {} void CL_HTTP_Shutdown (void) {} diff --git a/qw/source/cl_input.c b/qw/source/cl_input.c index e80ea4714..7096948ae 100644 --- a/qw/source/cl_input.c +++ b/qw/source/cl_input.c @@ -37,327 +37,67 @@ #include "QF/checksum.h" #include "QF/cmd.h" +#include "QF/console.h" #include "QF/cvar.h" #include "QF/input.h" #include "QF/keys.h" +#include "QF/listener.h" #include "QF/msg.h" #include "QF/sys.h" #include "QF/teamplay.h" #include "QF/va.h" +#include "QF/input/event.h" + +#include "compat.h" + +#include "client/input.h" +#include "client/view.h" + #include "qw/msg_ucmd.h" -#include "chase.h" -#include "cl_cam.h" -#include "cl_demo.h" -#include "cl_input.h" -#include "cl_parse.h" -#include "client.h" -#include "compat.h" -#include "host.h" -#include "clview.h" +#include "qw/include/cl_cam.h" +#include "qw/include/cl_demo.h" +#include "qw/include/cl_input.h" +#include "qw/include/cl_parse.h" +#include "qw/include/client.h" +#include "qw/include/host.h" -cvar_t *cl_nodelta; -cvar_t *cl_maxnetfps; -cvar_t *cl_spamimpulse; - -/* - KEY BUTTONS - - Continuous button event tracking is complicated by the fact that two - different input sources (say, mouse button 1 and the control key) can - both press the same button, but the button should be released only when - both of the pressing key have been released. - - When a key event issues a button command (+forward, +attack, etc), it - appends its key number as a parameter to the command so it can be - matched up with the release. - - state bit 0 is the current state of the key - state bit 1 is edge triggered on the up to down transition - state bit 2 is edge triggered on the down to up transition -*/ - -kbutton_t in_left, in_right, in_forward, in_back; -kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright; -kbutton_t in_use, in_jump, in_attack; -kbutton_t in_up, in_down; +int cl_nodelta; +static cvar_t cl_nodelta_cvar = { + .name = "cl_nodelta", + .description = + "Disable player delta compression. Set to 1 if you have a poor ISP and" + " get many U_REMOVE warnings.", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_nodelta }, +}; +int cl_maxnetfps; +static cvar_t cl_maxnetfps_cvar = { + .name = "cl_maxnetfps", + .description = + "Controls number of command packets sent per second. Default 0 is " + "unlimited.", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &cl_maxnetfps }, +}; +int cl_spamimpulse; +static cvar_t cl_spamimpulse_cvar = { + .name = "cl_spamimpulse", + .description = + "Controls number of duplicate packets sent if an impulse is being " + "sent. Default (id behavior) is 3.", + .default_value = "3", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_spamimpulse }, +}; int in_impulse; - static void -KeyPress (kbutton_t *b) -{ - const char *c; - int k; - - c = Cmd_Argv (1); - if (c[0]) - k = atoi (c); - else - k = -1; // typed manually at the console for - // continuous down - - if (k == b->down[0] || k == b->down[1]) - return; // repeating key - - if (!b->down[0]) - b->down[0] = k; - else if (!b->down[1]) - b->down[1] = k; - else { - Sys_Printf ("Three keys down for a button!\n"); - return; - } - - if (b->state & 1) - return; // still down - b->state |= 1 + 2; // down + impulse down -} - -static void -KeyRelease (kbutton_t *b) -{ - const char *c; - int k; - - c = Cmd_Argv (1); - if (c[0]) - k = atoi (c); - else { // typed manually at the console, - // assume for unsticking, so clear - // all - b->down[0] = b->down[1] = 0; - b->state = 4; // impulse up - return; - } - - if (b->down[0] == k) - b->down[0] = 0; - else if (b->down[1] == k) - b->down[1] = 0; - else - return; // key up without coresponding down - // (menu pass through) - if (b->down[0] || b->down[1]) - return; // some other key is still holding it - // down - - if (!(b->state & 1)) - return; // still up (this should not happen) - b->state &= ~1; // now up - b->state |= 4; // impulse up -} - -static void -IN_KLookPress (void) -{ - KeyPress (&in_klook); -} - -static void -IN_KLookRelease (void) -{ - KeyRelease (&in_klook); -} - -static void -IN_MLookPress (void) -{ - KeyPress (&in_mlook); -} - -static void -IN_MLookRelease (void) -{ - KeyRelease (&in_mlook); - if (!freelook && lookspring->int_val) - V_StartPitchDrift (); -} - -static void -IN_UpPress (void) -{ - KeyPress (&in_up); -} - -static void -IN_UpRelease (void) -{ - KeyRelease (&in_up); -} - -static void -IN_DownPress (void) -{ - KeyPress (&in_down); -} - -static void -IN_DownRelease (void) -{ - KeyRelease (&in_down); -} - -static void -IN_LeftPress (void) -{ - KeyPress (&in_left); -} - -static void -IN_LeftRelease (void) -{ - KeyRelease (&in_left); -} - -static void -IN_RightPress (void) -{ - KeyPress (&in_right); -} - -static void -IN_RightRelease (void) -{ - KeyRelease (&in_right); -} - -static void -IN_ForwardPress (void) -{ - KeyPress (&in_forward); -} - -static void -IN_ForwardRelease (void) -{ - KeyRelease (&in_forward); -} - -static void -IN_BackPress (void) -{ - KeyPress (&in_back); -} - -static void -IN_BackRelease (void) -{ - KeyRelease (&in_back); -} - -static void -IN_LookupPress (void) -{ - KeyPress (&in_lookup); -} - -static void -IN_LookupRelease (void) -{ - KeyRelease (&in_lookup); -} - -static void -IN_LookdownPress (void) -{ - KeyPress (&in_lookdown); -} - -static void -IN_LookdownRelease (void) -{ - KeyRelease (&in_lookdown); -} - -static void -IN_MoveleftPress (void) -{ - KeyPress (&in_moveleft); -} - -static void -IN_MoveleftRelease (void) -{ - KeyRelease (&in_moveleft); -} - -static void -IN_MoverightPress (void) -{ - KeyPress (&in_moveright); -} - -static void -IN_MoverightRelease (void) -{ - KeyRelease (&in_moveright); -} - -static void -IN_SpeedPress (void) -{ - KeyPress (&in_speed); -} - -static void -IN_SpeedRelease (void) -{ - KeyRelease (&in_speed); -} - -static void -IN_StrafePress (void) -{ - KeyPress (&in_strafe); -} - -static void -IN_StrafeRelease (void) -{ - KeyRelease (&in_strafe); -} - -static void -IN_AttackPress (void) -{ - KeyPress (&in_attack); -} - -static void -IN_AttackRelease (void) -{ - KeyRelease (&in_attack); -} - -static void -IN_UsePress (void) -{ - KeyPress (&in_use); -} - -static void -IN_UseRelease (void) -{ - KeyRelease (&in_use); -} - -static void -IN_JumpPress (void) -{ - KeyPress (&in_jump); -} - -static void -IN_JumpRelease (void) -{ - KeyRelease (&in_jump); -} - -static void -IN_Impulse (void) +IN_Impulse (void *data) { in_impulse = atoi (Cmd_Argv (1)); if (Cmd_Argc () <= 2) @@ -366,123 +106,6 @@ IN_Impulse (void) Team_BestWeaponImpulse (); // HACK HACK HACK } -/* - CL_KeyState - - Returns 0.25 if a key was pressed and released during the frame, - 0.5 if it was pressed and held - 0 if held then released, and - 1.0 if held for the entire time -*/ -float -CL_KeyState (kbutton_t *key) -{ - float val; - qboolean impulsedown, impulseup, down; - - impulsedown = key->state & 2; - impulseup = key->state & 4; - down = key->state & 1; - val = 0; - - if (impulsedown && !impulseup) { - if (down) - val = 0.5; // pressed and held this frame - else - val = 0; // I_Error (); - } - if (impulseup && !impulsedown) { - if (down) - val = 0; // I_Error (); - else - val = 0; // released this frame - } - if (!impulsedown && !impulseup) { - if (down) - val = 1.0; // held the entire frame - else - val = 0; // up the entire frame - } - if (impulsedown && impulseup) { - if (down) - val = 0.75; // released and re-pressed this frame - else - val = 0.25; // pressed and released this frame - } - - key->state &= 1; // clear impulses - - return val; -} - -cvar_t *cl_anglespeedkey; -cvar_t *cl_backspeed; -cvar_t *cl_forwardspeed; -cvar_t *cl_movespeedkey; -cvar_t *cl_pitchspeed; -cvar_t *cl_sidespeed; -cvar_t *cl_upspeed; -cvar_t *cl_yawspeed; - -/* - CL_AdjustAngles - - Moves the local angle positions -*/ -static void -CL_AdjustAngles (void) -{ - float down, up; - float pitchspeed, yawspeed; - - pitchspeed = cl_pitchspeed->value; - yawspeed = cl_yawspeed->value; - - if (in_speed.state & 1) { - pitchspeed *= cl_anglespeedkey->value; - yawspeed *= cl_anglespeedkey->value; - } - - if ((cl.fpd & FPD_LIMIT_PITCH) && pitchspeed > FPD_MAXPITCH) - pitchspeed = FPD_MAXPITCH; - if ((cl.fpd & FPD_LIMIT_YAW) && yawspeed > FPD_MAXYAW) - yawspeed = FPD_MAXYAW; - - pitchspeed *= host_frametime; - yawspeed *= host_frametime; - - if (!(in_strafe.state & 1)) { - cl.viewangles[YAW] -= yawspeed * CL_KeyState (&in_right); - cl.viewangles[YAW] += yawspeed * CL_KeyState (&in_left); - cl.viewangles[YAW] = anglemod (cl.viewangles[YAW]); - } - if (in_klook.state & 1) { - V_StopPitchDrift (); - cl.viewangles[PITCH] -= pitchspeed * CL_KeyState (&in_forward); - cl.viewangles[PITCH] += pitchspeed * CL_KeyState (&in_back); - } - - up = CL_KeyState (&in_lookup); - down = CL_KeyState (&in_lookdown); - - cl.viewangles[PITCH] -= pitchspeed * up; - cl.viewangles[PITCH] += pitchspeed * down; - - if (up || down) - V_StopPitchDrift (); - - // FIXME: Need to clean up view angle limits - if (cl.viewangles[PITCH] > 80) - cl.viewangles[PITCH] = 80; - if (cl.viewangles[PITCH] < -70) - cl.viewangles[PITCH] = -70; - - if (cl.viewangles[ROLL] > 50) - cl.viewangles[ROLL] = 50; - if (cl.viewangles[ROLL] < -50) - cl.viewangles[ROLL] = -50; -} - /* CL_BaseMove @@ -491,70 +114,18 @@ CL_AdjustAngles (void) void CL_BaseMove (usercmd_t *cmd) { - CL_AdjustAngles (); + if (cls.state != ca_active) { + return; + } + VectorCopy (cl.viewstate.player_angles, cl.movestate.angles);//FIXME + CL_Input_BuildMove (host_frametime, &cl.movestate, &cl.viewstate); + VectorCopy (cl.movestate.angles, cl.viewstate.player_angles);//FIXME memset (cmd, 0, sizeof (*cmd)); - - VectorCopy (cl.viewangles, cmd->angles); - if (in_strafe.state & 1) { - cmd->sidemove += cl_sidespeed->value * CL_KeyState (&in_right); - cmd->sidemove -= cl_sidespeed->value * CL_KeyState (&in_left); - } - - cmd->sidemove += cl_sidespeed->value * CL_KeyState (&in_moveright); - cmd->sidemove -= cl_sidespeed->value * CL_KeyState (&in_moveleft); - - cmd->upmove += cl_upspeed->value * CL_KeyState (&in_up); - cmd->upmove -= cl_upspeed->value * CL_KeyState (&in_down); - - if (!(in_klook.state & 1)) { - cmd->forwardmove += cl_forwardspeed->value * CL_KeyState (&in_forward); - cmd->forwardmove -= cl_backspeed->value * CL_KeyState (&in_back); - } - - // adjust for speed key - if (in_speed.state & 1) { - cmd->forwardmove *= cl_movespeedkey->value; - cmd->sidemove *= cl_movespeedkey->value; - cmd->upmove *= cl_movespeedkey->value; - } - - if (freelook) - V_StopPitchDrift (); - - viewdelta.angles[0] = viewdelta.angles[1] = viewdelta.angles[2] = 0; - viewdelta.position[0] = viewdelta.position[1] = viewdelta.position[2] = 0; - - IN_Move (); - - // adjust for chase camera angles - if (cl.chase - && (chase_active->int_val == 2 || chase_active->int_val == 3)) { - vec3_t forward, right, up, f, r; - vec3_t dir = {0, 0, 0}; - - dir[1] = r_data->refdef->viewangles[1] - cl.viewangles[1]; - AngleVectors (dir, forward, right, up); - VectorScale (forward, cmd->forwardmove, f); - VectorScale (right, cmd->sidemove, r); - cmd->forwardmove = f[0] + r[0]; - cmd->sidemove = f[1] + r[1]; - VectorScale (forward, viewdelta.position[2], f); - VectorScale (right, viewdelta.position[0], r); - viewdelta.position[2] = f[0] + r[0]; - viewdelta.position[0] = (f[1] + r[1]) * -1; - } - - cmd->forwardmove += viewdelta.position[2] * m_forward->value; - cmd->sidemove += viewdelta.position[0] * m_side->value; - cmd->upmove += viewdelta.position[1]; - cl.viewangles[PITCH] += viewdelta.angles[PITCH] * m_pitch->value; - cl.viewangles[YAW] += viewdelta.angles[YAW] * m_yaw->value; - cl.viewangles[ROLL] += viewdelta.angles[ROLL]; - - if (freelook && !(in_strafe.state & 1)) { - cl.viewangles[PITCH] = bound (-70, cl.viewangles[PITCH], 80); - } + cmd->forwardmove = cl.movestate.move[FORWARD]; + cmd->sidemove = cl.movestate.move[SIDE]; + cmd->upmove = cl.movestate.move[UP]; + VectorCopy (cl.movestate.angles, cmd->angles); } static int @@ -580,19 +151,9 @@ CL_FinishMove (usercmd_t *cmd) return; // figure button bits - if (in_attack.state & 3) - cmd->buttons |= 1; - in_attack.state &= ~2; - - if (in_jump.state & 3) - cmd->buttons |= 2; - in_jump.state &= ~2; - -// 1999-10-29 +USE fix by Maddes start - if (in_use.state & 3) - cmd->buttons |= 4; - in_use.state &= ~2; -// 1999-10-29 +USE fix by Maddes end + cmd->buttons |= IN_ButtonPressed (&in_attack) << 0; + cmd->buttons |= IN_ButtonPressed (&in_jump) << 1; + cmd->buttons |= IN_ButtonPressed (&in_use) << 2; // send milliseconds of time to apply the move accum += (host_frametime * 1000.0); @@ -605,7 +166,7 @@ CL_FinishMove (usercmd_t *cmd) } cmd->msec = ms; - VectorCopy (cl.viewangles, cmd->angles); + VectorCopy (cl.viewstate.player_angles, cmd->angles); cmd->impulse = in_impulse; in_impulse = 0; @@ -632,8 +193,8 @@ pps_check (int dontdrop) if (pps_balance > 0.0 || dropcount >= 2 || dontdrop) { float pps; - if (!(pps = cl_maxnetfps->int_val)) - pps = rate->value / 80.0; + if (!(pps = cl_maxnetfps)) + pps = rate / 80.0; pps = bound (1, pps, 72); @@ -673,7 +234,7 @@ CL_SendCmd (void) { byte data[128]; int checksumIndex, lost, seq_hash, frame; - qboolean dontdrop; // FIXME: needed without cl_c2sImpulseBackup? + bool dontdrop; // FIXME: needed without cl_c2sImpulseBackup? sizebuf_t buf; usercmd_t *cmd, *oldcmd; @@ -716,21 +277,21 @@ CL_SendCmd (void) frame = (cls.netchan.outgoing_sequence - 2) & UPDATE_MASK; cmd = &cl.frames[frame].cmd; - if (cl_spamimpulse->int_val >= 2) + if (cl_spamimpulse >= 2) dontdrop = dontdrop || cmd->impulse; MSG_WriteDeltaUsercmd (&buf, &nullcmd, cmd); oldcmd = cmd; frame = (cls.netchan.outgoing_sequence - 1) & UPDATE_MASK; cmd = &cl.frames[frame].cmd; - if (cl_spamimpulse->int_val >= 3) + if (cl_spamimpulse >= 3) dontdrop = dontdrop || cmd->impulse; MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd); oldcmd = cmd; frame = (cls.netchan.outgoing_sequence) & UPDATE_MASK; cmd = &cl.frames[frame].cmd; - if (cl_spamimpulse->int_val >= 1) + if (cl_spamimpulse >= 1) dontdrop = dontdrop || cmd->impulse; MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd); @@ -743,7 +304,7 @@ CL_SendCmd (void) if (cls.netchan.outgoing_sequence - cl.validsequence >= UPDATE_BACKUP - 1) cl.validsequence = 0; - if (cl.validsequence && !cl_nodelta->int_val && cls.state == ca_active + if (cl.validsequence && !cl_nodelta && cls.state == ca_active && !cls.demorecording) { cl.frames[frame].delta_sequence = cl.validsequence; MSG_WriteByte (&buf, clc_delta); @@ -755,99 +316,26 @@ CL_SendCmd (void) if (cls.demorecording) CL_WriteDemoCmd (cmd); + cl.viewstate.movecmd[FORWARD] = cmd->forwardmove; + // deliver the message if (pps_check (dontdrop)) Netchan_Transmit (&cls.netchan, buf.cursize, buf.data); } void -CL_Input_Init (void) +CL_Init_Input (cbuf_t *cbuf) { - Cmd_AddCommand ("+moveup", IN_UpPress, "When active the player is " - "swimming up in a liquid"); - Cmd_AddCommand ("-moveup", IN_UpRelease, "When active the player is not " - "swimming up in a liquid"); - Cmd_AddCommand ("+movedown", IN_DownPress, "When active the player is " - "swimming down in a liquid"); - Cmd_AddCommand ("-movedown", IN_DownRelease, "When active the player is " - "not swimming down in a liquid"); - Cmd_AddCommand ("+left", IN_LeftPress, "When active the player is turning " - "left"); - Cmd_AddCommand ("-left", IN_LeftRelease, "When active the player is not " - "turning left"); - Cmd_AddCommand ("+right", IN_RightPress, "When active the player is " - "turning right"); - Cmd_AddCommand ("-right", IN_RightRelease, "When active the player is not " - "turning right"); - Cmd_AddCommand ("+forward", IN_ForwardPress, "When active the player is " - "moving forward"); - Cmd_AddCommand ("-forward", IN_ForwardRelease, "When active the player is " - "not moving forward"); - Cmd_AddCommand ("+back", IN_BackPress, "When active the player is moving " - "backwards"); - Cmd_AddCommand ("-back", IN_BackRelease, "When active the player is not " - "moving backwards"); - Cmd_AddCommand ("+lookup", IN_LookupPress, "When active the player's view " - "is looking up"); - Cmd_AddCommand ("-lookup", IN_LookupRelease, "When active the player's " - "view is not looking up"); - Cmd_AddCommand ("+lookdown", IN_LookdownPress, "When active the player's " - "view is looking down"); - Cmd_AddCommand ("-lookdown", IN_LookdownRelease, "When active the " - "player's view is not looking up"); - Cmd_AddCommand ("+strafe", IN_StrafePress, "When active, +left and +right " - "function like +moveleft and +moveright"); - Cmd_AddCommand ("-strafe", IN_StrafeRelease, "When active, +left and " - "+right stop functioning like +moveleft and +moveright"); - Cmd_AddCommand ("+moveleft", IN_MoveleftPress, "When active the player is " - "strafing left"); - Cmd_AddCommand ("-moveleft", IN_MoveleftRelease, "When active the player " - "is not strafing left"); - Cmd_AddCommand ("+moveright", IN_MoverightPress, "When active the player " - "is strafing right"); - Cmd_AddCommand ("-moveright", IN_MoverightRelease, "When active the " - "player is not strafing right"); - Cmd_AddCommand ("+speed", IN_SpeedPress, "When active the player is " - "running"); - Cmd_AddCommand ("-speed", IN_SpeedRelease, "When active the player is not " - "running"); - Cmd_AddCommand ("+attack", IN_AttackPress, "When active player is " - "firing/using current weapon"); - Cmd_AddCommand ("-attack", IN_AttackRelease, "When active player is not " - "firing/using current weapon"); - Cmd_AddCommand ("+use", IN_UsePress, "Non-functional. Left over command " - "for opening doors and triggering switches"); - Cmd_AddCommand ("-use", IN_UseRelease, "Non-functional. Left over command " - "for opening doors and triggering switches"); - Cmd_AddCommand ("+jump", IN_JumpPress, "When active the player is " - "jumping"); - Cmd_AddCommand ("-jump", IN_JumpRelease, "When active the player is not " - "jumping"); - Cmd_AddCommand ("impulse", IN_Impulse, "Call a game function or QuakeC " - "function."); - Cmd_AddCommand ("+klook", IN_KLookPress, "When active, +forward and +back " - "perform +lookup and +lookdown"); - Cmd_AddCommand ("-klook", IN_KLookRelease, "When active, +forward and " - "+back don't perform +lookup and +lookdown"); - Cmd_AddCommand ("+mlook", IN_MLookPress, "When active moving the mouse or " - "joystick forwards and backwards performs +lookup and " - "+lookdown"); - Cmd_AddCommand ("-mlook", IN_MLookRelease, "When active moving the mouse " - "or joystick forwards and backwards doesn't perform " - "+lookup and +lookdown"); + CL_Input_Init (cbuf); + Cmd_AddDataCommand ("impulse", IN_Impulse, 0, + "Call a game function or QuakeC function."); } void -CL_Input_Init_Cvars (void) +CL_Init_Input_Cvars (void) { - cl_nodelta = Cvar_Get ("cl_nodelta", "0", CVAR_NONE, NULL, - "Disable player delta compression. Set to 1 if you " - "have a poor ISP and get many U_REMOVE warnings."); - cl_maxnetfps = Cvar_Get ("cl_maxnetfps", "0", CVAR_ARCHIVE, NULL, - "Controls number of command packets sent per " - "second. Default 0 is unlimited."); - cl_spamimpulse = Cvar_Get ("cl_spamimpulse", "3", CVAR_NONE, NULL, - "Controls number of duplicate packets sent if " - "an impulse is being sent. Default (id " - "behavior) is 3."); + CL_Input_Init_Cvars (); + Cvar_Register (&cl_nodelta_cvar, 0, 0); + Cvar_Register (&cl_maxnetfps_cvar, 0, 0); + Cvar_Register (&cl_spamimpulse_cvar, 0, 0); } diff --git a/qw/source/cl_main.c b/qw/source/cl_main.c index 09fb1aec2..f6ada55eb 100644 --- a/qw/source/cl_main.c +++ b/qw/source/cl_main.c @@ -49,9 +49,6 @@ #ifdef HAVE_WINSOCK_H # include #endif -#ifdef HAVE_RPC_TYPES_H -# include -#endif #if defined(_WIN32) && defined(HAVE_MALLOC_H) #include @@ -70,8 +67,10 @@ #include "QF/console.h" #include "QF/cvar.h" #include "QF/draw.h" +#include "QF/dstring.h" #include "QF/image.h" #include "QF/input.h" +#include "QF/joystick.h" #include "QF/keys.h" #include "QF/model.h" #include "QF/msg.h" @@ -80,7 +79,9 @@ #include "QF/qargs.h" #include "QF/qendian.h" #include "QF/quakefs.h" +#include "QF/quakeio.h" #include "QF/ruamoko.h" +#include "QF/plist.h" #include "QF/screen.h" #include "QF/sound.h" #include "QF/sys.h" @@ -90,29 +91,36 @@ #include "QF/gib.h" #include "QF/plugin/console.h" +#include "QF/scene/transform.h" + +#include "buildnum.h" +#include "compat.h" + +#include "client/particles.h" +#include "client/sbar.h" +#include "client/screen.h" +#include "client/temp_entities.h" +#include "client/view.h" +#include "client/world.h" #include "qw/bothdefs.h" -#include "buildnum.h" -#include "cl_cam.h" -#include "cl_chat.h" -#include "cl_demo.h" -#include "cl_ents.h" -#include "cl_http.h" -#include "cl_input.h" -#include "cl_main.h" -#include "cl_parse.h" -#include "cl_pred.h" -#include "cl_skin.h" -#include "cl_slist.h" -#include "cl_tent.h" -#include "client.h" -#include "compat.h" -#include "game.h" -#include "host.h" -#include "netchan.h" #include "qw/pmove.h" -#include "sbar.h" -#include "clview.h" + +#include "qw/include/cl_cam.h" +#include "qw/include/cl_chat.h" +#include "qw/include/cl_demo.h" +#include "qw/include/cl_ents.h" +#include "qw/include/cl_http.h" +#include "qw/include/cl_input.h" +#include "qw/include/cl_main.h" +#include "qw/include/cl_parse.h" +#include "qw/include/cl_pred.h" +#include "qw/include/cl_skin.h" +#include "qw/include/cl_slist.h" +#include "qw/include/client.h" +#include "qw/include/game.h" +#include "qw/include/host.h" +#include "netchan.h" CLIENT_PLUGIN_PROTOS static plugin_list_t client_plugin_list[] = { @@ -122,71 +130,290 @@ static plugin_list_t client_plugin_list[] = { // we need to declare some mouse variables here, because the menu system // references them even when on a unix system. -qboolean noclip_anglehack; // remnant from old quake +bool noclip_anglehack; // remnant from old quake cbuf_t *cl_cbuf; cbuf_t *cl_stbuf; -cvar_t *cl_mem_size; +float cl_mem_size; +static cvar_t cl_mem_size_cvar = { + .name = "cl_mem_size", + .description = + "Amount of memory (in MB) to allocate for the " + PACKAGE_NAME + " heap", + .default_value = "32", + .flags = CVAR_ROM, + .value = { .type = &cexpr_float, .value = &cl_mem_size }, +}; -cvar_t *rcon_password; +char *rcon_password; +static cvar_t rcon_password_cvar = { + .name = "rcon_password", + .description = + "Set the password for rcon 'root' commands", + .default_value = "", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &rcon_password }, +}; -cvar_t *rcon_address; +char *rcon_address; +static cvar_t rcon_address_cvar = { + .name = "rcon_address", + .description = + "server IP address when client not connected - for sending rcon " + "commands", + .default_value = "", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &rcon_address }, +}; -cvar_t *cl_writecfg; -cvar_t *cl_allow_cmd_pkt; -cvar_t *cl_cmd_pkt_adr; -cvar_t *cl_paranoid; +int cl_writecfg; +static cvar_t cl_writecfg_cvar = { + .name = "cl_writecfg", + .description = + "write config files?", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_writecfg }, +}; +int cl_allow_cmd_pkt; +static cvar_t cl_allow_cmd_pkt_cvar = { + .name = "cl_allow_cmd_pkt", + .description = + "enables packets from the likes of gamespy", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_allow_cmd_pkt }, +}; +char *cl_cmd_pkt_adr; +static cvar_t cl_cmd_pkt_adr_cvar = { + .name = "cl_cmd_pkt_adr", + .description = + "allowed address for non-local command packet", + .default_value = "", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &cl_cmd_pkt_adr }, +}; +int cl_paranoid; +static cvar_t cl_paranoid_cvar = { + .name = "cl_paranoid", + .description = + "print source address of connectionless packets even when coming from " + "the server being connected to.", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_paranoid }, +}; -cvar_t *cl_timeout; +float cl_timeout; +static cvar_t cl_timeout_cvar = { + .name = "cl_timeout", + .description = + "server connection timeout (since last packet received)", + .default_value = "60", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &cl_timeout }, +}; -cvar_t *cl_draw_locs; -cvar_t *cl_shownet; -cvar_t *cl_autoexec; -cvar_t *cl_quakerc; -cvar_t *cl_maxfps; -cvar_t *cl_usleep; +int cl_draw_locs; +static cvar_t cl_draw_locs_cvar = { + .name = "cl_draw_locs", + .description = + "Draw location markers.", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_draw_locs }, +}; +int cl_shownet; +static cvar_t cl_shownet_cvar = { + .name = "cl_shownet", + .description = + "show network packets. 0=off, 1=basic, 2=verbose", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_shownet }, +}; +int cl_autoexec; +static cvar_t cl_autoexec_cvar = { + .name = "cl_autoexec", + .description = + "exec autoexec.cfg on gamedir change", + .default_value = "0", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &cl_autoexec }, +}; +int cl_quakerc; +static cvar_t cl_quakerc_cvar = { + .name = "cl_quakerc", + .description = + "exec quake.rc on startup", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_quakerc }, +}; +float cl_maxfps; +static cvar_t cl_maxfps_cvar = { + .name = "cl_maxfps", + .description = + "maximum frames rendered in one second. 0 == 72", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &cl_maxfps }, +}; +int cl_usleep; +static cvar_t cl_usleep_cvar = { + .name = "cl_usleep", + .description = + "Turn this on to save cpu when fps limited. May affect frame rate " + "adversely depending on local machine/os conditions", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &cl_usleep }, +}; -cvar_t *cl_cshift_bonus; -cvar_t *cl_cshift_contents; -cvar_t *cl_cshift_damage; -cvar_t *cl_cshift_powerup; +int cl_model_crcs; +static cvar_t cl_model_crcs_cvar = { + .name = "cl_model_crcs", + .description = + "Controls setting of emodel and pmodel info vars. Required by some " + "servers, but clearing this can make the difference between connecting" + " and not connecting on some others.", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &cl_model_crcs }, +}; -cvar_t *cl_model_crcs; +int cl_predict_players; +static cvar_t cl_predict_players_cvar = { + .name = "cl_predict_players", + .description = + "If this is 0, no player prediction is done", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_predict_players }, +}; +int cl_solid_players; +static cvar_t cl_solid_players_cvar = { + .name = "cl_solid_players", + .description = + "Are players solid? If off, you can walk through them with difficulty", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_solid_players }, +}; -cvar_t *lookspring; +char *localid; +static cvar_t localid_cvar = { + .name = "localid", + .description = + "Used by gamespy+others to authenticate when sending commands to the " + "client", + .default_value = "", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &localid }, +}; -cvar_t *m_pitch; -cvar_t *m_yaw; -cvar_t *m_forward; -cvar_t *m_side; +int cl_port; +static cvar_t cl_port_cvar = { + .name = "cl_port", + .description = + "UDP Port for client to use.", + .default_value = PORT_CLIENT, + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_port }, +}; +int cl_autorecord; +static cvar_t cl_autorecord_cvar = { + .name = "cl_autorecord", + .description = + "Turn this on, if you want to record every game", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &cl_autorecord }, +}; -cvar_t *cl_predict_players; -cvar_t *cl_solid_players; +float cl_fb_players; +static cvar_t cl_fb_players_cvar = { + .name = "cl_fb_players", + .description = + "fullbrightness of player models. server must allow (via fbskins " + "serverinfo).", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &cl_fb_players }, +}; -cvar_t *localid; - -cvar_t *cl_port; -cvar_t *cl_autorecord; - -cvar_t *cl_fb_players; - -static qboolean allowremotecmd = true; +static bool allowremotecmd = true; /* info mirrors */ -cvar_t *password; -cvar_t *spectator; -cvar_t *cl_name; -cvar_t *team; -cvar_t *rate; -cvar_t *noaim; -cvar_t *msg; +char *password; +static cvar_t password_cvar = { + .name = "password", + .description = + "Set the server password for players", + .default_value = "", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &password }, +}; +int spectator; +static cvar_t spectator_cvar = { + .name = "spectator", + .description = + "Set to 1 before connecting to become a spectator", + .default_value = "", + .flags = CVAR_USERINFO, + .value = { .type = &cexpr_int, .value = &spectator }, +}; +char *cl_name; +static cvar_t cl_name_cvar = { + .name = "_cl_name", + .description = + "Player name", + .default_value = "player", + .flags = CVAR_ARCHIVE, + .value = { .type = 0, .value = &cl_name }, +}; +float team; +static cvar_t team_cvar = { + .name = "team", + .description = + "Team player is on.", + .default_value = "", + .flags = CVAR_ARCHIVE | CVAR_USERINFO, + .value = { .type = &cexpr_float, .value = &team }, +}; +float rate; +static cvar_t rate_cvar = { + .name = "rate", + .description = + "Amount of bytes per second server will send/download to you", + .default_value = "10000", + .flags = CVAR_ARCHIVE | CVAR_USERINFO, + .value = { .type = &cexpr_float, .value = &rate }, +}; +int noaim; +static cvar_t noaim_cvar = { + .name = "noaim", + .description = + "Auto aim off switch. Set to 1 to turn off.", + .default_value = "0", + .flags = CVAR_ARCHIVE | CVAR_USERINFO, + .value = { .type = &cexpr_int, .value = &noaim }, +}; +int msg; +static cvar_t msg_cvar = { + .name = "msg", + .description = + "Determines the type of messages reported 0 is maximum, 4 is none", + .default_value = "1", + .flags = CVAR_ARCHIVE | CVAR_USERINFO, + .value = { .type = &cexpr_int, .value = &msg }, +}; /* GIB events */ gib_event_t *cl_player_health_e, *cl_chat_e; -static int cl_usleep_cache; - client_static_t cls; client_state_t cl; @@ -195,8 +422,8 @@ entity_state_t cl_baselines[MAX_EDICTS]; double connect_time = -1; // for connection retransmits quakeparms_t host_parms; -qboolean host_initialized; // true if into command execution -qboolean nomaster; +bool host_initialized; // true if into command execution +bool nomaster; double host_frametime; double realtime; // without any filtering or bounding @@ -207,13 +434,17 @@ double con_frametime; double con_realtime; double oldcon_realtime; -int host_hunklevel; +size_t host_hunklevel; -cvar_t *host_speeds; -cvar_t *hud_fps; -cvar_t *hud_ping; -cvar_t *hud_pl; -cvar_t *hud_time; +int host_speeds; +static cvar_t host_speeds_cvar = { + .name = "host_speeds", + .description = + "set for running times", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &host_speeds }, +}; int fps_count; @@ -221,13 +452,6 @@ jmp_buf host_abort; char *server_version = NULL; // version of server we connected to -char emodel_name[] = "emodel"; -char pmodel_name[] = "pmodel"; -char prespawn_name[] = "prespawn %i 0 %i"; -char modellist_name[] = "modellist %i %i"; -char soundlist_name[] = "soundlist %i %i"; - -extern cvar_t *hud_scoreboard_uid; static netadr_t cl_cmd_packet_address; static void @@ -239,6 +463,12 @@ CL_Quit_f (void) Sys_Quit (); } +static void +pointfile_f (void) +{ + CL_LoadPointFile (cl_world.scene->worldmodel); +} + static void CL_Version_f (void) { @@ -254,7 +484,6 @@ CL_Version_f (void) static void CL_SendConnectPacket (void) { - dstring_t *data; double t1, t2; // JACK: Fixed bug where DNS lookups would cause two connects real fast @@ -278,14 +507,13 @@ CL_SendConnectPacket (void) connect_time = realtime + t2 - t1; // for retransmit requests - cls.qport = qport->int_val; + cls.qport = qport; - data = dstring_new (); - dsprintf (data, "%c%c%c%cconnect %i %i %i \"%s\"\n", - 255, 255, 255, 255, PROTOCOL_VERSION, cls.qport, cls.challenge, - Info_MakeString (cls.userinfo, 0)); - Netchan_SendPacket (strlen (data->str), data->str, cls.server_addr); - dstring_delete (data); + const char *data = va (0, "%c%c%c%cconnect %i %i %i \"%s\"\n", + 255, 255, 255, 255, PROTOCOL_VERSION, + cls.qport, cls.challenge, + Info_MakeString (cls.userinfo, 0)); + Netchan_SendPacket (strlen (data), data, cls.server_addr); } /* @@ -319,7 +547,7 @@ CL_CheckForResend (void) connect_time = realtime + t2 - t1; // for retransmit requests - VID_SetCaption (va ("Connecting to %s", cls.servername->str)); + VID_SetCaption (va (0, "Connecting to %s", cls.servername->str)); Sys_Printf ("Connecting to %s...\n", cls.servername->str); Netchan_SendPacket (strlen (getchallenge), (void *) getchallenge, cls.server_addr); @@ -360,29 +588,25 @@ CL_Connect_f (void) static void CL_Rcon_f (void) { - static dstring_t *message; netadr_t to; - if (!message) - message = dstring_new (); - - dsprintf (message, "\377\377\377\377rcon %s %s", rcon_password->string, - Cmd_Args (1)); - if (cls.state >= ca_connected) to = cls.netchan.remote_address; else { - if (!rcon_address->string[0]) { + if (!rcon_address[0]) { Sys_Printf ("You must either be connected, or set the " "'rcon_address' cvar to issue rcon commands\n"); return; } - NET_StringToAdr (rcon_address->string, &to); + NET_StringToAdr (rcon_address, &to); if (to.port == 0) to.port = BigShort (27500); } - Netchan_SendPacket (strlen (message->str) + 1, message->str, to); + + const char *message; + message = va (0, "\377\377\377\377rcon %s %s", rcon_password, Cmd_Args (1)); + Netchan_SendPacket (strlen (message) + 1, message, to); } void @@ -392,13 +616,24 @@ CL_ClearState (void) S_StopAllSounds (); + if (Entity_Valid (cl.viewstate.weapon_entity)) { + Scene_DestroyEntity (cl_world.scene, cl.viewstate.weapon_entity); + } // wipe the entire cl structure if (cl.serverinfo) Info_Destroy (cl.serverinfo); + __auto_type players = cl.players; + __auto_type cam = cl.viewstate.camera_transform; memset (&cl, 0, sizeof (cl)); - r_data->force_fullscreen = 0; + cl.viewstate.camera_transform = cam; + cl.players = players; + SCR_SetFullscreen (0); cl.maxclients = MAX_CLIENTS; + cl.viewstate.voffs_enabled = 0; + cl.viewstate.chasestate = &cl.chasestate; + cl.chasestate.viewstate = &cl.viewstate; + cl.viewstate.punchangle = (vec4f_t) {0, 0, 0, 1}; // Note: we should probably hack around this and give diff values for // diff gamedirs @@ -409,24 +644,26 @@ CL_ClearState (void) cl.frames[i].packet_entities.entities = qw_entstates.frame[i]; cl.serverinfo = Info_ParseString ("", MAX_INFO_STRING, 0); - CL_Init_Entity (&cl.viewent); - - Sys_MaskPrintf (SYS_DEV, "Clearing memory\n"); - if (viddef.flush_caches) - viddef.flush_caches (); + Sys_MaskPrintf (SYS_dev, "Clearing memory\n"); + VID_ClearMemory (); Mod_ClearAll (); if (host_hunklevel) // FIXME: check this... - Hunk_FreeToLowMark (host_hunklevel); + Hunk_FreeToLowMark (0, host_hunklevel); CL_ClearEnts (); CL_ClearTEnts (); - r_funcs->R_ClearState (); + cl.viewstate.weapon_entity = Scene_CreateEntity (cl_world.scene); + CL_Init_Entity (cl.viewstate.weapon_entity); + r_data->view_model = cl.viewstate.weapon_entity; + + CL_TEnts_Precache (); + + SCR_NewScene (0); SZ_Clear (&cls.netchan.message); - if (centerprint) - dstring_clearstr (centerprint); + Sbar_CenterPrint (0); } /* @@ -440,7 +677,7 @@ CL_StopCshifts (void) int i; for (i = 0; i < NUM_CSHIFTS; i++) - cl.cshifts[i].percent = 0; + cl.viewstate.cshifts[i].percent = 0; for (i = 0; i < MAX_CL_STATS; i++) cl.stats[i] = 0; } @@ -516,7 +753,7 @@ CL_Disconnect (void) Info_Destroy (cl.players[i].userinfo); memset (&cl.players[i], 0, sizeof (cl.players[i])); } - cl.worldmodel = NULL; + cl_world.scene->worldmodel = NULL; cl.validsequence = 0; } @@ -596,10 +833,10 @@ CL_FullServerinfo_f (void) return; } - Sys_MaskPrintf (SYS_DEV, "Cmd_Argv (1): '%s'\n", Cmd_Argv (1)); + Sys_MaskPrintf (SYS_dev, "Cmd_Argv (1): '%s'\n", Cmd_Argv (1)); Info_Destroy (cl.serverinfo); cl.serverinfo = Info_ParseString (Cmd_Argv (1), MAX_SERVERINFO_STRING, 0); - Sys_MaskPrintf (SYS_DEV, "cl.serverinfo: '%s'\n", + Sys_MaskPrintf (SYS_dev, "cl.serverinfo: '%s'\n", Info_MakeString (cl.serverinfo, 0)); if ((p = Info_ValueForKey (cl.serverinfo, "*qf_version")) && *p) { @@ -619,10 +856,11 @@ CL_FullServerinfo_f (void) Sys_Printf ("Invalid QSG Protocol number: %s", p); } - cl.chase = cl.sv_cshifts = cl.no_pogo_stick = cl.teamplay = 0; + cl.viewstate.chase = cl.sv_cshifts = cl.no_pogo_stick = cl.teamplay = 0; if ((p = Info_ValueForKey (cl.serverinfo, "chase")) && *p) { - cl.chase = atoi (p); + cl.viewstate.chase = atoi (p); } + cl.viewstate.chase |= cls.demoplayback; if ((p = Info_ValueForKey (cl.serverinfo, "cshifts")) && *p) { cl.sv_cshifts = atoi (p); } @@ -631,10 +869,10 @@ CL_FullServerinfo_f (void) } if ((p = Info_ValueForKey (cl.serverinfo, "teamplay")) && *p) { cl.teamplay = atoi (p); - Sbar_DMO_Init_f (hud_scoreboard_uid); // HUD setup, cl.teamplay changed + Sbar_SetTeamplay (cl.teamplay); } if ((p = Info_ValueForKey (cl.serverinfo, "watervis")) && *p) { - cl.watervis = atoi (p); + cl.viewstate.watervis = atoi (p); } if ((p = Info_ValueForKey (cl.serverinfo, "fpd")) && *p) { cl.fpd = atoi (p); @@ -784,7 +1022,7 @@ CL_NextDemo (void) } } - Cbuf_InsertText (cl_cbuf, va ("playdemo %s\n", cls.demos[cls.demonum])); + Cbuf_InsertText (cl_cbuf, va (0, "playdemo %s\n", cls.demos[cls.demonum])); cls.demonum++; } @@ -802,7 +1040,8 @@ CL_Changing_f (void) S_StopAllSounds (); cl.intermission = 0; - r_data->force_fullscreen = 0; + cl.viewstate.intermission = 0; + SCR_SetFullscreen (0); CL_SetState (ca_connected); // not active anymore, but not // disconnected Sys_Printf ("\nChanging map...\n"); @@ -858,7 +1097,7 @@ CL_ConnectionlessPacket (void) if (net_message->badread) return; if (!cls.demoplayback - && (cl_paranoid->int_val + && (cl_paranoid || !NET_CompareAdr (net_from, cls.server_addr))) Sys_Printf ("%s: ", NET_AdrToString (net_from)); if (c == S2C_CONNECTION) { @@ -884,16 +1123,16 @@ CL_ConnectionlessPacket (void) Sys_Printf ("client command\n"); - if (!cl_allow_cmd_pkt->int_val + if (!cl_allow_cmd_pkt || (!NET_CompareBaseAdr (net_from, net_local_adr) && !NET_CompareBaseAdr (net_from, net_loopback_adr) - && (!cl_cmd_pkt_adr->string[0] + && (!cl_cmd_pkt_adr[0] || !NET_CompareBaseAdr (net_from, cl_cmd_packet_address)))) { Sys_Printf ("Command packet from remote host. Ignored.\n"); return; } - if (cl_cmd_pkt_adr->string[0] + if (cl_cmd_pkt_adr[0] && NET_CompareBaseAdr (net_from, cl_cmd_packet_address)) allowremotecmd = false; // force password checking s = MSG_ReadString (net_message); @@ -909,10 +1148,10 @@ CL_ConnectionlessPacket (void) while (len && isspace ((byte) s[len - 1])) len--; - if (!allowremotecmd && (!*localid->string || - (int) strlen (localid->string) > len || - strncmp (localid->string, s, len))) { - if (!*localid->string) { + if (!allowremotecmd && (!*localid || + (int) strlen (localid) > len || + strncmp (localid, s, len))) { + if (!*localid) { Sys_Printf ("===========================\n"); Sys_Printf ("Command packet received from local host, but no " "localid has been set. You may need to upgrade " @@ -925,9 +1164,9 @@ CL_ConnectionlessPacket (void) ("Invalid localid on command packet received from local host. " "\n|%s| != |%s|\n" "You may need to reload your server browser and %s.\n", s, - localid->string, PACKAGE_NAME); + localid, PACKAGE_NAME); Sys_Printf ("===========================\n"); - Cvar_Set (localid, ""); + Cvar_Set ("localid", ""); return; } @@ -944,7 +1183,7 @@ CL_ConnectionlessPacket (void) Sys_Printf ("status response\n"); return; } else if (!cls.demoplayback - && (cl_paranoid->int_val + && (cl_paranoid || !NET_CompareAdr (net_from, cls.server_addr))) { Sys_Printf ("print\n"); } @@ -1014,20 +1253,23 @@ CL_ReadPackets (void) { while (CL_GetMessage ()) { - if (net_message->message->cursize == -1) + // non-packet set up by the demo reader + if ((int) net_message->message->cursize == -1) { continue; + } - if (cls.demoplayback && net_packetlog->int_val) + if (cls.demoplayback && net_packetlog) Log_Incoming_Packet (net_message->message->data, - net_message->message->cursize, 0, 0); + net_message->message->cursize, 0); // remote command packet if (*(int *) net_message->message->data == -1) { CL_ConnectionlessPacket (); continue; } - if (*(char *) net_message->message->data == A2A_ACK) - { + if (net_message->message->cursize == 1 + && *(char *) net_message->message->data == A2A_ACK) { + Sys_Printf ("ping ack\n"); SL_CheckPing (NET_AdrToString (net_from)); continue; } @@ -1039,7 +1281,7 @@ CL_ReadPackets (void) // packet from server if (!cls.demoplayback && !NET_CompareAdr (net_from, cls.netchan.remote_address)) { - Sys_MaskPrintf (SYS_DEV, + Sys_MaskPrintf (SYS_dev, "%s:sequenced packet without connection\n", NET_AdrToString (net_from)); continue; @@ -1056,7 +1298,7 @@ CL_ReadPackets (void) // check timeout if (!cls.demoplayback && cls.state >= ca_connected - && realtime - cls.netchan.last_received > cl_timeout->value) { + && realtime - cls.netchan.last_received > cl_timeout) { Sys_Printf ("\nServer connection timed out.\n"); CL_Disconnect (); return; @@ -1088,7 +1330,7 @@ CL_Download_f (void) cls.downloadtype = dl_single; MSG_WriteByte (&cls.netchan.message, clc_stringcmd); - SZ_Print (&cls.netchan.message, va ("download %s\n", Cmd_Argv (1))); + SZ_Print (&cls.netchan.message, va (0, "download %s\n", Cmd_Argv (1))); } else { Sys_Printf ("error downloading %s: %s\n", Cmd_Argv (1), strerror (errno)); @@ -1098,7 +1340,7 @@ CL_Download_f (void) static void Force_CenterView_f (void) { - cl.viewangles[PITCH] = 0; + cl.viewstate.player_angles[PITCH] = 0; } static void @@ -1107,12 +1349,12 @@ CL_PRotate_f (void) if ((cl.fpd & FPD_LIMIT_PITCH) || Cmd_Argc() < 2) return; - cl.viewangles[PITCH] += atoi (Cmd_Argv (1)); + cl.viewstate.player_angles[PITCH] += atoi (Cmd_Argv (1)); - if (cl.viewangles[PITCH] < -70) - cl.viewangles[PITCH] = -70; - else if (cl.viewangles[PITCH] > 80) - cl.viewangles[PITCH] = 80; + if (cl.viewstate.player_angles[PITCH] < -70) + cl.viewstate.player_angles[PITCH] = -70; + else if (cl.viewstate.player_angles[PITCH] > 80) + cl.viewstate.player_angles[PITCH] = 80; } static void @@ -1121,8 +1363,8 @@ CL_Rotate_f (void) if ((cl.fpd & FPD_LIMIT_YAW) || Cmd_Argc() < 2) return; - cl.viewangles[YAW] += atoi (Cmd_Argv (1)); - cl.viewangles[YAW] = anglemod (cl.viewangles[YAW]); + cl.viewstate.player_angles[YAW] += atoi (Cmd_Argv (1)); + cl.viewstate.player_angles[YAW] = anglemod(cl.viewstate.player_angles[YAW]); } void @@ -1137,31 +1379,66 @@ CL_SetState (cactive_t state) }; cactive_t old_state = cls.state; - Sys_MaskPrintf (SYS_DEV, "CL_SetState (%s)\n", state_names[state]); + Sys_MaskPrintf (SYS_dev, "CL_SetState (%s)\n", state_names[state]); cls.state = state; + cl.viewstate.active = cls.state == ca_active; + cl.viewstate.drift_enabled = !cls.demoplayback; if (old_state != state) { if (old_state == ca_active) { // leaving active state IN_ClearStates (); - Key_SetKeyDest (key_console); + CL_ClearState (); // Auto demo recorder stops here - if (cl_autorecord->int_val && cls.demorecording) + if (cl_autorecord && cls.demorecording) CL_StopRecording (); + + SCR_NewScene (0); } else if (state == ca_active) { // entering active state VID_SetCaption (cls.servername->str); IN_ClearStates (); - Key_SetKeyDest (key_game); // Auto demo recorder starts here - if (cl_autorecord->int_val && !cls.demoplayback + if (cl_autorecord && !cls.demoplayback && !cls.demorecording) CL_Record (0, -1); } + Sbar_SetActive (state == ca_active); } - if (con_module) - con_module->data->console->force_commandline = (state != ca_active); + Con_SetState (state == ca_active ? con_inactive : con_fullscreen); + if (state != old_state && state == ca_active) { + CL_Input_Activate (!cls.demoplayback); + } +} + +static void +CL_Shutdown (void *data) +{ + static bool isdown = false; + + if (isdown) { + printf ("recursive shutdown\n"); + return; + } + isdown = true; + + SL_Shutdown (); + + Host_WriteConfiguration (); + + CL_HTTP_Shutdown (); + + if (cl.serverinfo) { + Info_Destroy (cl.serverinfo); + } + Info_Destroy (cls.userinfo); + Cbuf_DeleteStack (cl_stbuf); + dstring_delete (cls.servername); + dstring_delete (cls.downloadtempname); + dstring_delete (cls.downloadname); + dstring_delete (cls.downloadurl); + free (cl.players); } void @@ -1178,30 +1455,37 @@ CL_Init (void) W_LoadWadFile ("gfx.wad"); VID_Init (basepal, colormap); - IN_Init (cl_cbuf); + IN_Init (); Mod_Init (); R_Init (); r_data->lightstyle = cl.lightstyle; PI_RegisterPlugins (client_plugin_list); - Con_Init ("client"); + Con_Load ("client"); + CL_Init_Screen (); if (con_module) { con_module->data->console->dl_name = cls.downloadname; con_module->data->console->dl_percent = &cls.downloadpercent; con_module->data->console->realtime = &con_realtime; con_module->data->console->frametime = &con_frametime; con_module->data->console->quit = CL_Quit_f; + //FIXME need to rethink cbuf connections (they can form a stack) + Cbuf_DeleteStack (con_module->data->console->cbuf); con_module->data->console->cbuf = cl_cbuf; } + Con_Init (); + CL_NetGraph_Init (); S_Init (&cl.viewentity, &host_frametime); CDAudio_Init (); - Sbar_Init (); + Sbar_Init (cl.stats, cl.item_gettime); - CL_Input_Init (); + CL_Init_Input (cl_cbuf); CL_Ents_Init (); + CL_Particles_Init (); CL_TEnts_Init (); + CL_World_Init (); CL_ClearState (); Pmove_Init (); @@ -1209,18 +1493,25 @@ CL_Init (void) CL_Skin_Init (); Locs_Init (); - V_Init (); + V_Init (&cl.viewstate); + + Sys_RegisterShutdown (CL_Shutdown, 0); Info_SetValueForStarKey (cls.userinfo, "*ver", QW_VERSION, 0); - centerprint = dstring_newstr (); cls.servername = dstring_newstr (); cls.downloadtempname = dstring_newstr (); cls.downloadname = dstring_newstr (); cls.downloadurl = dstring_newstr (); + Info_Destroy (cl.serverinfo); cl.serverinfo = Info_ParseString ("", MAX_INFO_STRING, 0); + free (cl.players); + cl.players = calloc (MAX_CLIENTS, sizeof (player_info_t)); + Sbar_SetPlayers (cl.players, MAX_CLIENTS); // register our commands + Cmd_AddCommand ("pointfile", pointfile_f, + "Load a pointfile to determine map leaks."); Cmd_AddCommand ("version", CL_Version_f, "Report version information"); Cmd_AddCommand ("changing", CL_Changing_f, "Used when maps are changing"); Cmd_AddCommand ("disconnect", CL_Disconnect_f, "Disconnect from server"); @@ -1282,20 +1573,31 @@ CL_Init (void) } static void -cl_usleep_f (cvar_t *var) +cl_cmd_pkt_adr_f (void *data, const cvar_t *cvar) { - cl_usleep_cache = var->int_val; -} - -static void -cl_cmd_pkt_adr_f (cvar_t *var) -{ - if (var->string[0]) - NET_StringToAdr (var->string, &cl_cmd_packet_address); + if (cl_cmd_pkt_adr[0]) + NET_StringToAdr (cl_cmd_pkt_adr, &cl_cmd_packet_address); else memset (&cl_cmd_packet_address, 0, sizeof (cl_cmd_packet_address)); } +static void +cl_pitchspeed_f (void *data, const cvar_t *var) +{ + if ((cl.fpd & FPD_LIMIT_PITCH) && cl_pitchspeed > FPD_MAXPITCH) { + cl_pitchspeed = FPD_MAXPITCH; + } +} + +static void +cl_yawspeed_f (void *data, const cvar_t *var) +{ + if ((cl.fpd & FPD_LIMIT_YAW) && cl_yawspeed > FPD_MAXYAW) { + cl_yawspeed = FPD_MAXYAW; + } +} + + static void CL_Init_Cvars (void) { @@ -1305,138 +1607,52 @@ CL_Init_Cvars (void) S_Init_Cvars (); CL_Cam_Init_Cvars (); - CL_Input_Init_Cvars (); + CL_Init_Input_Cvars (); CL_Prediction_Init_Cvars (); + CL_NetGraph_Init_Cvars (); Game_Init_Cvars (); Pmove_Init_Cvars (); Team_Init_Cvars (); + Chase_Init_Cvars (); V_Init_Cvars (); - r_netgraph = Cvar_Get ("r_netgraph", "0", CVAR_NONE, NULL, - "Toggle the display of a graph showing network " - "performance"); - r_netgraph_alpha = Cvar_Get ("r_netgraph_alpha", "0.5", CVAR_ARCHIVE, NULL, - "Net graph translucency"); - r_netgraph_box = Cvar_Get ("r_netgraph_box", "1", CVAR_ARCHIVE, NULL, - "Draw box around net graph"); + cvar_t *var; + var = Cvar_FindVar ("cl_pitchspeed"); + Cvar_AddListener (var, cl_pitchspeed_f, 0); + var = Cvar_FindVar ("cl_yawspeed"); + Cvar_AddListener (var, cl_yawspeed_f, 0); cls.userinfo = Info_ParseString ("", MAX_INFO_STRING, 0); - cl_model_crcs = Cvar_Get ("cl_model_crcs", "1", CVAR_ARCHIVE, NULL, - "Controls setting of emodel and pmodel info " - "vars. Required by some servers, but clearing " - "this can make the difference between " - "connecting and not connecting on some others."); - cl_allow_cmd_pkt = Cvar_Get ("cl_allow_cmd_pkt", "1", CVAR_NONE, NULL, - "enables packets from the likes of gamespy"); - cl_cmd_pkt_adr = Cvar_Get ("cl_cmd_pkt_adr", "", CVAR_NONE, - cl_cmd_pkt_adr_f, - "allowed address for non-local command packet"); - cl_paranoid = Cvar_Get ("cl_paranoid", "1", CVAR_NONE, NULL, - "print source address of connectionless packets" - " even when coming from the server being connected" - " to."); - cl_autoexec = Cvar_Get ("cl_autoexec", "0", CVAR_ROM, NULL, - "exec autoexec.cfg on gamedir change"); - cl_quakerc = Cvar_Get ("cl_quakerc", "1", CVAR_NONE, NULL, - "exec quake.rc on startup"); - cl_cshift_bonus = Cvar_Get ("cl_cshift_bonus", "1", CVAR_ARCHIVE, NULL, - "Show bonus flash on item pickup"); - cl_cshift_contents = Cvar_Get ("cl_cshift_content", "1", CVAR_ARCHIVE, - NULL, "Shift view colors for contents " - "(water, slime, etc)"); - cl_cshift_damage = Cvar_Get ("cl_cshift_damage", "1", CVAR_ARCHIVE, NULL, - "Shift view colors on damage"); - cl_cshift_powerup = Cvar_Get ("cl_cshift_powerup", "1", CVAR_ARCHIVE, NULL, - "Shift view colors for powerups"); - cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", CVAR_NONE, NULL, - "turn `run' speed multiplier"); - cl_backspeed = Cvar_Get ("cl_backspeed", "200", CVAR_ARCHIVE, NULL, - "backward speed"); - cl_fb_players = Cvar_Get ("cl_fb_players", "0", CVAR_ARCHIVE, NULL, "fullbrightness of player models. " - "server must allow (via fbskins serverinfo)."); - cl_forwardspeed = Cvar_Get ("cl_forwardspeed", "200", CVAR_ARCHIVE, NULL, - "forward speed"); - cl_movespeedkey = Cvar_Get ("cl_movespeedkey", "2.0", CVAR_NONE, NULL, - "move `run' speed multiplier"); - cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "150", CVAR_NONE, NULL, - "look up/down speed"); - cl_sidespeed = Cvar_Get ("cl_sidespeed", "350", CVAR_NONE, NULL, - "strafe speed"); - cl_upspeed = Cvar_Get ("cl_upspeed", "200", CVAR_NONE, NULL, - "swim/fly up/down speed"); - cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", CVAR_NONE, NULL, - "turning speed"); - cl_writecfg = Cvar_Get ("cl_writecfg", "1", CVAR_NONE, NULL, - "write config files?"); - cl_draw_locs = Cvar_Get ("cl_draw_locs", "0", CVAR_NONE, NULL, - "Draw location markers."); - cl_shownet = Cvar_Get ("cl_shownet", "0", CVAR_NONE, NULL, - "show network packets. 0=off, 1=basic, 2=verbose"); - cl_maxfps = Cvar_Get ("cl_maxfps", "0", CVAR_ARCHIVE, NULL, - "maximum frames rendered in one second. 0 == 72"); - cl_timeout = Cvar_Get ("cl_timeout", "60", CVAR_ARCHIVE, NULL, "server " - "connection timeout (since last packet received)"); - host_speeds = Cvar_Get ("host_speeds", "0", CVAR_NONE, NULL, - "display host processing times"); - lookspring = Cvar_Get ("lookspring", "0", CVAR_ARCHIVE, NULL, "Snap view " - "to center when moving and no mlook/klook"); - m_forward = Cvar_Get ("m_forward", "1", CVAR_ARCHIVE, NULL, - "mouse forward/back speed"); - m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE, NULL, - "mouse pitch (up/down) multipier"); - m_side = Cvar_Get ("m_side", "0.8", CVAR_ARCHIVE, NULL, - "mouse strafe speed"); - m_yaw = Cvar_Get ("m_yaw", "0.022", CVAR_ARCHIVE, NULL, - "mouse yaw (left/right) multiplier"); - rcon_password = Cvar_Get ("rcon_password", "", CVAR_NONE, NULL, - "remote control password"); - rcon_address = Cvar_Get ("rcon_address", "", CVAR_NONE, NULL, "server IP " - "address when client not connected - for " - "sending rcon commands"); - hud_fps = Cvar_Get ("hud_fps", "0", CVAR_ARCHIVE, NULL, - "display realtime frames per second"); - Cvar_MakeAlias ("show_fps", hud_fps); - hud_ping = Cvar_Get ("hud_ping", "0", CVAR_ARCHIVE, NULL, - "display current ping to server"); - hud_pl = Cvar_Get ("hud_pl", "0", CVAR_ARCHIVE, NULL, - "display current packet loss to server"); - hud_time = Cvar_Get ("hud_time", "0", CVAR_ARCHIVE, NULL, - "Display the current time, 1 24hr, 2 AM/PM"); - cl_predict_players = Cvar_Get ("cl_predict_players", "1", CVAR_NONE, NULL, - "If this is 0, no player prediction is " - "done"); - cl_solid_players = Cvar_Get ("cl_solid_players", "1", CVAR_NONE, NULL, - "Are players solid? If off, you can walk " - "through them with difficulty"); - localid = Cvar_Get ("localid", "", CVAR_NONE, NULL, "Used by " - "gamespy+others to authenticate when sending " - "commands to the client"); + Cvar_Register (&cl_model_crcs_cvar, 0, 0); + Cvar_Register (&cl_allow_cmd_pkt_cvar, 0, 0); + Cvar_Register (&cl_cmd_pkt_adr_cvar, cl_cmd_pkt_adr_f, 0); + Cvar_Register (&cl_paranoid_cvar, 0, 0); + Cvar_Register (&cl_autoexec_cvar, 0, 0); + Cvar_Register (&cl_quakerc_cvar, 0, 0); + Cvar_Register (&cl_fb_players_cvar, 0, 0); + Cvar_Register (&cl_writecfg_cvar, 0, 0); + Cvar_Register (&cl_draw_locs_cvar, 0, 0); + Cvar_Register (&cl_shownet_cvar, 0, 0); + Cvar_Register (&cl_maxfps_cvar, 0, 0); + Cvar_Register (&cl_timeout_cvar, 0, 0); + Cvar_Register (&host_speeds_cvar, 0, 0); + Cvar_Register (&rcon_password_cvar, 0, 0); + Cvar_Register (&rcon_address_cvar, 0, 0); + Cvar_Register (&cl_predict_players_cvar, 0, 0); + Cvar_Register (&cl_solid_players_cvar, 0, 0); + Cvar_Register (&localid_cvar, 0, 0); // info mirrors - cl_name = Cvar_Get ("name", "unnamed", CVAR_ARCHIVE | CVAR_USERINFO, - Cvar_Info, "Player name"); - password = Cvar_Get ("password", "", CVAR_USERINFO, Cvar_Info, - "Server password"); - spectator = Cvar_Get ("spectator", "", CVAR_USERINFO, Cvar_Info, - "Set to 1 before connecting to become a spectator"); - team = Cvar_Get ("team", "", CVAR_ARCHIVE | CVAR_USERINFO, Cvar_Info, - "Team player is on."); - rate = Cvar_Get ("rate", "10000", CVAR_ARCHIVE | CVAR_USERINFO, Cvar_Info, - "Amount of bytes per second server will send/download " - "to you"); - msg = Cvar_Get ("msg", "1", CVAR_ARCHIVE | CVAR_USERINFO, Cvar_Info, - "Determines the type of messages reported 0 is maximum, " - "4 is none"); - noaim = Cvar_Get ("noaim", "0", CVAR_ARCHIVE | CVAR_USERINFO, Cvar_Info, - "Auto aim off switch. Set to 1 to turn off."); - cl_port = Cvar_Get ("cl_port", PORT_CLIENT, CVAR_NONE, Cvar_Info, - "UDP Port for client to use."); - cl_usleep = Cvar_Get ("cl_usleep", "1", CVAR_ARCHIVE, cl_usleep_f, - "Turn this on to save cpu when fps limited. " - "May affect frame rate adversely depending on " - "local machine/os conditions"); - cl_autorecord = Cvar_Get ("cl_autorecord", "0", CVAR_ARCHIVE, NULL, "Turn " - "this on, if you want to record every game"); + Cvar_Register (&cl_name_cvar, 0, 0); + Cvar_Register (&password_cvar, 0, 0); + Cvar_Register (&spectator_cvar, Cvar_Info, &spectator); + Cvar_Register (&team_cvar, Cvar_Info, &team); + Cvar_Register (&rate_cvar, Cvar_Info, &rate); + Cvar_Register (&msg_cvar, Cvar_Info, &msg); + Cvar_Register (&noaim_cvar, Cvar_Info, &noaim); + Cvar_Register (&cl_port_cvar, Cvar_Info, &cl_port); + Cvar_Register (&cl_usleep_cvar, 0, 0); + Cvar_Register (&cl_autorecord_cvar, 0, 0); } /* @@ -1447,18 +1663,16 @@ CL_Init_Cvars (void) void Host_EndGame (const char *message, ...) { - static dstring_t *str; va_list argptr; - if (!str) - str = dstring_new (); - + dstring_t *str = dstring_new (); va_start (argptr, message); dvsprintf (str, message, argptr); va_end (argptr); Sys_Printf ("\n===========================\n"); Sys_Printf ("Host_EndGame: %s\n", str->str); Sys_Printf ("===========================\n\n"); + dstring_delete (str); CL_Disconnect (); @@ -1473,18 +1687,15 @@ Host_EndGame (const char *message, ...) void Host_Error (const char *error, ...) { - static dstring_t *str; - static qboolean inerror = false; + static bool inerror = false; va_list argptr; if (inerror) Sys_Error ("Host_Error: recursively entered"); - if (!str) - str = dstring_new (); - inerror = true; + dstring_t *str = dstring_new (); va_start (argptr, error); dvsprintf (str, error, argptr); va_end (argptr); @@ -1501,31 +1712,82 @@ Host_Error (const char *error, ...) } else { Sys_Error ("Host_Error: %s", str->str); } + dstring_delete (str); } -/* - Host_WriteConfiguration - - Writes key bindings and archived cvars to config.cfg -*/ void Host_WriteConfiguration (void) { - QFile *f; + if (host_initialized && cl_writecfg) { + plitem_t *config = PL_NewDictionary (0); + Cvar_SaveConfig (config); + IN_SaveConfig (config); - if (host_initialized && cl_writecfg->int_val) { - char *path = va ("%s/config.cfg", qfs_gamedir->dir.def); + const char *path = va (0, "%s/quakeforge.cfg", qfs_gamedir->dir.def); + QFile *f = QFS_WOpen (path, 0); - f = QFS_WOpen (path, 0); if (!f) { - Sys_Printf ("Couldn't write config.cfg.\n"); - return; + Sys_Printf ("Couldn't write quakeforge.cfg.\n"); + } else { + char *cfg = PL_WritePropertyList (config); + Qputs (f, cfg); + free (cfg); + Qclose (f); } + PL_Release (config); + } +} - Key_WriteBindings (f); - Cvar_WriteVariables (f); +int +Host_ReadConfiguration (const char *cfg_name) +{ + QFile *cfg_file = QFS_FOpenFile (cfg_name); + if (!cfg_file) { + return 0; + } + size_t len = Qfilesize (cfg_file); + char *cfg = malloc (len + 1); + Qread (cfg_file, cfg, len); + cfg[len] = 0; + Qclose (cfg_file); - Qclose (f); + plitem_t *config = PL_GetPropertyList (cfg, 0); + free (cfg); + + if (!config) { + return 0; + } + + Cvar_LoadConfig (config); + IN_LoadConfig (config); + + PL_Release (config); + return 1; +} + +static void +Host_ExecConfig (cbuf_t *cbuf, int skip_quakerc) +{ + // quakeforge.cfg overrides quake.rc as it contains quakeforge-specific + // commands. If it doesn't exist, then this is the first time quakeforge + // has been used in this installation, thus any existing legacy config + // should be used to set up defaults on the assumption that the user has + // things set up to work with another (hopefully compatible) client + if (Host_ReadConfiguration ("quakeforge.cfg")) { + Cmd_Exec_File (cbuf, fs_usercfg, 0); + Cmd_StuffCmds (cbuf); + COM_Check_quakerc ("startdemos", cbuf); + } else { + if (!skip_quakerc) { + Cbuf_InsertText (cbuf, "exec quake.rc\n"); + } + Cmd_Exec_File (cbuf, fs_usercfg, 0); + // Reparse the command line for + commands. + // (sets still done, but it doesn't matter) + // (Note, no non-base commands exist yet) + if (skip_quakerc || !COM_Check_quakerc ("stuffcmds", 0)) { + Cmd_StuffCmds (cbuf); + } } } @@ -1544,7 +1806,7 @@ Host_SimulationTime (float time) con_realtime += time; if (cls.demoplayback) { - timescale = max (0, demo_speed->value); + timescale = max (0, demo_speed); time *= timescale; } @@ -1555,10 +1817,10 @@ Host_SimulationTime (float time) if (cls.demoplayback) return 0; - if (cl_maxfps->value <= 0) + if (cl_maxfps <= 0) fps = 72; else - fps = min (cl_maxfps->value, 72); + fps = min (cl_maxfps, 72); timedifference = (timescale / fps) - (realtime - oldrealtime); @@ -1567,6 +1829,19 @@ Host_SimulationTime (float time) return 0; } +static void +write_capture (tex_t *tex, void *data) +{ + QFile *file = QFS_Open (va (0, "%s/qfmv%06d.png", + qfs_gamedir->dir.shots, + cls.demo_capture++), "wb"); + if (file) { + WritePNG (file, tex); + Qclose (file); + } + free (tex); +} + int nopacketcount; /* @@ -1593,7 +1868,7 @@ Host_Frame (float time) // decide the simulation time if ((sleeptime = Host_SimulationTime (time)) != 0) { #ifdef HAVE_USLEEP - if (cl_usleep_cache && sleeptime > 0.002) // minimum sleep time + if (cl_usleep && sleeptime > 0.002) // minimum sleep time usleep ((unsigned long) (sleeptime * 1000000 / 2)); #endif return; // framerate is too high @@ -1603,6 +1878,8 @@ Host_Frame (float time) oldrealtime = realtime; host_frametime = min (host_frametime, 0.2); + cl.viewstate.frametime = host_frametime; + con_frametime = con_realtime - oldcon_realtime; oldcon_realtime = con_realtime; @@ -1629,8 +1906,8 @@ Host_Frame (float time) oldself = &cl.frames[(cls.netchan.outgoing_sequence - 1) & UPDATE_MASK].playerstate[cl.playernum]; self->messagenum = cl.parsecount; - VectorCopy (oldself->pls.origin, self->pls.origin); - VectorCopy (oldself->pls.velocity, self->pls.velocity); + VectorCopy (oldself->pls.es.origin, self->pls.es.origin); + VectorCopy (oldself->pls.es.velocity, self->pls.es.velocity); VectorCopy (oldself->viewangles, self->viewangles); CL_ParseClientdata (); @@ -1652,14 +1929,14 @@ Host_Frame (float time) CL_PredictMove (); // Set up prediction for other players - CL_SetUpPlayerPrediction (cl_predict_players->int_val); + CL_SetUpPlayerPrediction (cl_predict_players); // build a refresh entity list CL_EmitEntities (); } // update video - if (host_speeds->int_val) + if (host_speeds) time1 = Sys_DoubleTime (); r_data->inhibit_viewmodel = (!Cam_DrawViewModel () @@ -1667,28 +1944,35 @@ Host_Frame (float time) || cl.stats[STAT_HEALTH] <= 0); r_data->frametime = host_frametime; - CL_UpdateScreen (realtime); + cl.viewstate.time = realtime; + cl.viewstate.realtime = realtime; + if (!cls.demoplayback && cls.state == ca_active) { + CL_NetUpdate (); + } + Sbar_Update (cl.time); + CL_UpdateScreen (&cl.viewstate); - if (host_speeds->int_val) + if (host_speeds) time2 = Sys_DoubleTime (); // update audio if (cls.state == ca_active) { mleaf_t *l; byte *asl = 0; + vec4f_t origin; - l = Mod_PointInLeaf (r_data->origin, cl.worldmodel); + origin = Transform_GetWorldPosition (cl.viewstate.camera_transform); + l = Mod_PointInLeaf (origin, cl_world.scene->worldmodel); if (l) asl = l->ambient_sound_level; - S_Update (r_data->origin, r_data->vpn, r_data->vright, r_data->vup, - asl); - r_funcs->R_DecayLights (host_frametime); + S_Update (cl.viewstate.camera_transform, asl); + R_DecayLights (host_frametime); } else - S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin, 0); + S_Update (nulltransform, 0); CDAudio_Update (); - if (host_speeds->int_val) { + if (host_speeds) { pass1 = (time1 - time3) * 1000; time3 = Sys_DoubleTime (); pass2 = (time2 - time1) * 1000; @@ -1698,90 +1982,64 @@ Host_Frame (float time) } if (cls.demo_capture) { - tex_t *tex = r_funcs->SCR_CaptureBGR (); - WritePNGqfs (va ("%s/qfmv%06d.png", qfs_gamedir->dir.shots, - cls.demo_capture++), - tex->data, tex->width, tex->height); - free (tex); + r_funcs->capture_screen (write_capture, 0); } host_framecount++; fps_count++; } -static int -check_quakerc (void) -{ - const char *l, *p; - int ret = 1; - QFile *f; - - f = QFS_FOpenFile ("quake.rc"); - if (!f) - return 1; - while ((l = Qgetline (f))) { - if ((p = strstr (l, "stuffcmds"))) { - if (p == l) { // only known case so far - ret = 0; - break; - } - } - } - Qclose (f); - return ret; -} - -static void +static memhunk_t * CL_Init_Memory (void) { int mem_parm = COM_CheckParm ("-mem"); - int mem_size; + size_t mem_size; void *mem_base; - cl_mem_size = Cvar_Get ("cl_mem_size", "32", CVAR_NONE, NULL, - "Amount of memory (in MB) to allocate for the " - PACKAGE_NAME " heap"); + Cvar_Register (&cl_mem_size_cvar, 0, 0); if (mem_parm) - Cvar_Set (cl_mem_size, com_argv[mem_parm + 1]); + Cvar_Set ("cl_mem_size", com_argv[mem_parm + 1]); if (COM_CheckParm ("-minmemory")) - Cvar_SetValue (cl_mem_size, MINIMUM_MEMORY / (1024 * 1024.0)); + cl_mem_size = MINIMUM_MEMORY / (1024 * 1024.0); - Cvar_SetFlags (cl_mem_size, cl_mem_size->flags | CVAR_ROM); - mem_size = (int) (cl_mem_size->value * 1024 * 1024); + mem_size = ((size_t) cl_mem_size * 1024 * 1024); if (mem_size < MINIMUM_MEMORY) Sys_Error ("Only %4.1f megs of memory reported, can't execute game", mem_size / (float) 0x100000); - mem_base = malloc (mem_size); + mem_base = Sys_Alloc (mem_size); if (!mem_base) - Sys_Error ("Can't allocate %d", mem_size); + Sys_Error ("Can't allocate %zd", mem_size); Sys_PageIn (mem_base, mem_size); - Memory_Init (mem_base, mem_size); + memhunk_t *hunk = Memory_Init (mem_base, mem_size); - Sys_Printf ("%4.1f megabyte heap.\n", cl_mem_size->value); + Sys_Printf ("%4.1f megabyte heap.\n", cl_mem_size); + return hunk; } static void -CL_Autoexec (int phase) +CL_Autoexec (int phase, void *data) { - int cmd_warncmd_val = cmd_warncmd->int_val; - if (!phase) return; - Cbuf_AddText (cl_cbuf, "cmd_warncmd 0\n"); - Cbuf_AddText (cl_cbuf, "exec config.cfg\n"); - Cbuf_AddText (cl_cbuf, "exec frontend.cfg\n"); + if (!Host_ReadConfiguration ("quakeforge.cfg")) { + int cmd_warncmd_val = cmd_warncmd; - if (cl_autoexec->int_val) { - Cbuf_AddText (cl_cbuf, "exec autoexec.cfg\n"); + Cbuf_AddText (cl_cbuf, "cmd_warncmd 0\n"); + Cbuf_AddText (cl_cbuf, "exec config.cfg\n"); + Cbuf_AddText (cl_cbuf, "exec frontend.cfg\n"); + + Cbuf_AddText (cl_cbuf, va (0, "cmd_warncmd %d\n", cmd_warncmd_val)); } - Cbuf_AddText (cl_cbuf, va ("cmd_warncmd %d\n", cmd_warncmd_val)); + if (cl_autoexec) { + Cbuf_AddText (cl_cbuf, "exec autoexec.cfg\n"); + } } void @@ -1792,30 +2050,31 @@ Host_Init (void) Sys_Init (); GIB_Init (true); - COM_ParseConfig (); + GIB_Key_Init (); + COM_ParseConfig (cl_cbuf); - CL_Init_Memory (); + memhunk_t *hunk = CL_Init_Memory (); pr_gametype = "quakeworld"; - QFS_Init ("qw"); - QFS_GamedirCallback (CL_Autoexec); + QFS_Init (hunk, "qw"); + QFS_GamedirCallback (CL_Autoexec, 0); PI_Init (); + Sys_RegisterShutdown (Net_LogStop, 0); + Netchan_Init_Cvars (); - PR_Init_Cvars (); + PR_Init_Cvars (); // FIXME location CL_Init_Cvars (); - PR_Init (); - CL_Chat_Init (); CL_Cmd_Init (); Game_Init (); - NET_Init (cl_port->int_val); + NET_Init (cl_port); Netchan_Init (); net_realtime = &realtime; { @@ -1824,7 +2083,7 @@ Host_Init (void) for (i = 0; i < MAX_MODELS; i++) sound_precache[i] = cl.sound_name[i]; - Net_Log_Init (sound_precache); + Net_Log_Init (sound_precache, 0); } CL_HTTP_Init (); @@ -1832,34 +2091,30 @@ Host_Init (void) CL_Init (); - CL_UpdateScreen (realtime); - CL_UpdateScreen (realtime); + cl.viewstate.time = realtime; + cl.viewstate.realtime = realtime; + CL_UpdateScreen (&cl.viewstate); + CL_UpdateScreen (&cl.viewstate); - if (cl_quakerc->int_val) - Cbuf_InsertText (cl_cbuf, "exec quake.rc\n"); - Cmd_Exec_File (cl_cbuf, fs_usercfg->string, 0); - // Reparse the command line for + commands. - // (Note, no non-base commands exist yet) - if (!cl_quakerc->int_val || check_quakerc ()) - Cmd_StuffCmds (cl_cbuf); + Host_ExecConfig (cl_cbuf, !cl_quakerc); - Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); - host_hunklevel = Hunk_LowMark (); + // make sure all + commands have been executed + Cbuf_Execute_Stack (cl_cbuf); + + Hunk_AllocName (0, 0, "-HOST_HUNKLEVEL-"); + host_hunklevel = Hunk_LowMark (0); Sys_Printf ("\nClient version %s (build %04d)\n\n", PACKAGE_VERSION, build_number ()); Sys_Printf ("\x80\x81\x81\x82 %s initialized \x80\x81\x81\x82\n", PACKAGE_NAME); - // make sure all + commands have been executed - Cbuf_Execute_Stack (cl_cbuf); - host_initialized = true; - CL_UpdateScreen (realtime); + CL_UpdateScreen (&cl.viewstate); Con_NewMap (); // force the menus to be loaded - CL_UpdateScreen (realtime); - CL_UpdateScreen (realtime); + CL_UpdateScreen (&cl.viewstate); + CL_UpdateScreen (&cl.viewstate); if (connect_time == -1) { Cbuf_AddText (cl_cbuf, "echo Type connect or use a " @@ -1867,26 +2122,3 @@ Host_Init (void) } Cbuf_AddText (cl_cbuf, "set cmd_warncmd 1\n"); } - -void -Host_Shutdown (void) -{ - static qboolean isdown = false; - - if (isdown) { - printf ("recursive shutdown\n"); - return; - } - isdown = true; - - SL_Shutdown (); - - Host_WriteConfiguration (); - - CDAudio_Shutdown (); - CL_HTTP_Shutdown (); - NET_Shutdown (); - S_Shutdown (); - IN_Shutdown (); - VID_Shutdown (); -} diff --git a/qw/source/cl_ngraph.c b/qw/source/cl_ngraph.c index 2503c8ee4..ae734e210 100644 --- a/qw/source/cl_ngraph.c +++ b/qw/source/cl_ngraph.c @@ -39,58 +39,159 @@ #include "QF/draw.h" #include "QF/render.h" #include "QF/screen.h" +#include "QF/va.h" +#include "QF/ui/canvas.h" +#include "QF/ui/view.h" + +#include "client/hud.h" +#include "client/screen.h" #include "compat.h" -#include "cl_parse.h" -#include "client.h" -#include "sbar.h" -cvar_t *r_netgraph; -cvar_t *r_netgraph_alpha; -cvar_t *r_netgraph_box; +#include "qw/include/cl_parse.h" +#include "qw/include/client.h" +#include "client/sbar.h" -void -CL_NetGraph (void) +int cl_netgraph; +static cvar_t cl_netgraph_cvar = { + .name = "cl_netgraph", + .description = + "Toggle the display of a graph showing network performance", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_netgraph }, +}; +float cl_netgraph_alpha; +static cvar_t cl_netgraph_alpha_cvar = { + .name = "cl_netgraph_alpha", + .description = + "Net graph translucency", + .default_value = "0.5", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_float, .value = &cl_netgraph_alpha }, +}; +int cl_netgraph_box; +static cvar_t cl_netgraph_box_cvar = { + .name = "cl_netgraph_box", + .description = + " Draw box around net graph", + .default_value = "1", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &cl_netgraph_box }, +}; +int cl_netgraph_height; +static cvar_t cl_netgraph_height_cvar = { + .name = "cl_netgraph_height", + .description = + "Set the fullscale (1s) height of the graph", + .default_value = "32", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &cl_netgraph_height }, +}; + +static view_t cl_netgraph_view; + +static void +cl_netgraph_f (void *data, const cvar_t *cvar) { - char st[80]; - int lost, a, l, x, y, i; + if (View_Valid (cl_netgraph_view)) { + View_SetVisible (cl_netgraph_view, cl_netgraph != 0); + } +} - if (!r_netgraph->int_val) - return; +static void +cl_netgraph_height_f (void *data, const cvar_t *cvar) +{ + cl_netgraph_height = max (32, cl_netgraph_height); + if (View_Valid (cl_netgraph_view)) { + view_pos_t len = View_GetLen (cl_netgraph_view); + View_SetLen (cl_netgraph_view, len.x, cl_netgraph_height + 25); + View_UpdateHierarchy (cl_netgraph_view); + } +} - x = hudswap ? r_data->vid->conwidth - (NET_TIMINGS + 16): 0; - y = r_data->vid->conheight - sb_lines - 24 - - r_data->graphheight->int_val - 1; +static void +CL_NetGraph (view_pos_t abs, view_pos_t len) +{ + int lost, a, l, x, y, i, o; + int timings[NET_TIMINGS]; - if (r_netgraph_box->int_val) - r_funcs->Draw_TextBox (x, y, NET_TIMINGS / 8, - r_data->graphheight->int_val / 8 + 1, - r_netgraph_alpha->value * 255); + if (cl_netgraph_box) { + r_funcs->Draw_TextBox (abs.x, abs.y, NET_TIMINGS / 8, + cl_netgraph_height / 8 + 1, + cl_netgraph_alpha * 255); + } lost = CL_CalcNet (); - x = hudswap ? r_data->vid->conwidth - (NET_TIMINGS + 8) : 8; - y = r_data->vid->conheight - sb_lines - 9; + x = abs.x + 8; + y = abs.y + len.y - 9; l = NET_TIMINGS; - if (l > r_data->refdef->vrect.width - 8) - l = r_data->refdef->vrect.width - 8; + if (l > len.x - 8) + l = len.x - 8; i = cls.netchan.outgoing_sequence & NET_TIMINGSMASK; a = i - l; + o = 0; if (a < 0) { - r_funcs->R_LineGraph (x, y, &packet_latency[a + NET_TIMINGS], -a); - x -= a; + memcpy (timings + o, packet_latency + a + NET_TIMINGS, + -a * sizeof (timings[0])); + o -= a; l += a; a = 0; } - r_funcs->R_LineGraph (x, y, &packet_latency[a], l); + memcpy (timings + o, packet_latency + a, l * sizeof (timings[0])); + r_funcs->R_LineGraph (x, y, timings, + NET_TIMINGS, cl_netgraph_height); - y = r_data->vid->conheight - sb_lines - 24 - - r_data->graphheight->int_val + 7; - snprintf (st, sizeof (st), "%3i%% packet loss", lost); - if (hudswap) { - r_funcs->Draw_String (r_data->vid->conwidth - ((strlen (st) * 8) + 8), - y, st); - } else { - r_funcs->Draw_String (8, y, st); + x = abs.x + 8; + y = abs.y + 8; + r_funcs->Draw_String (x, y, va (0, "%3i%% packet loss", lost)); +/* + //FIXME don't do every frame + view_move (cl_netgraph_view, cl_netgraph_view->xpos, hud_sb_lines); + view_setgravity (cl_netgraph_view, + hud_swap ? grav_southeast : grav_southwest); +*/ +} + +void +CL_NetGraph_Init (void) +{ + ecs_system_t vsys = { + .reg = cl_canvas_sys.reg, + .base = cl_canvas_sys.view_base, + }; + cl_netgraph_view = View_New (vsys, cl_screen_view); + View_SetPos (cl_netgraph_view, 0, 64); + View_SetLen (cl_netgraph_view, NET_TIMINGS + 16, cl_netgraph_height + 25); + View_SetGravity (cl_netgraph_view, grav_southwest); + void *f = CL_NetGraph; + Ent_SetComponent (cl_netgraph_view.id, canvas_func, + cl_netgraph_view.reg, &f); + View_SetVisible (cl_netgraph_view, cl_netgraph); +} + +void +CL_NetGraph_Init_Cvars (void) +{ + Cvar_Register (&cl_netgraph_cvar, cl_netgraph_f, 0); + Cvar_Register (&cl_netgraph_alpha_cvar, 0, 0); + Cvar_Register (&cl_netgraph_box_cvar, 0, 0); + Cvar_Register (&cl_netgraph_height_cvar, cl_netgraph_height_f, 0); +} + +void +CL_NetUpdate (void) +{ + if ((hud_ping || sbar_showscores) + && realtime - cl.last_ping_request > 2) { + // FIXME this should be on a timer + cl.last_ping_request = realtime; + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + SZ_Print (&cls.netchan.message, "pings"); + } + + if (hud_pl) { + Sbar_UpdatePL (CL_CalcNet ()); } } diff --git a/qw/source/cl_parse.c b/qw/source/cl_parse.c index a271c1197..6e999d26c 100644 --- a/qw/source/cl_parse.c +++ b/qw/source/cl_parse.c @@ -52,7 +52,7 @@ #include "QF/idparse.h" #include "QF/msg.h" #include "QF/progs.h" -#include "QF/qfplist.h" +#include "QF/plist.h" #include "QF/quakeio.h" #include "QF/screen.h" #include "QF/skin.h" @@ -61,24 +61,33 @@ #include "QF/teamplay.h" #include "QF/va.h" -#include "qw/bothdefs.h" -#include "cl_cam.h" -#include "cl_chat.h" -#include "cl_ents.h" -#include "cl_http.h" -#include "cl_input.h" -#include "cl_main.h" -#include "cl_parse.h" -#include "cl_skin.h" -#include "cl_tent.h" -#include "client.h" +#include "QF/scene/scene.h" + #include "compat.h" -#include "host.h" -#include "map_cfg.h" + +#include "client/effects.h" +#include "client/particles.h" +#include "client/sbar.h" +#include "client/screen.h" +#include "client/temp_entities.h" +#include "client/view.h" +#include "client/world.h" + +#include "qw/bothdefs.h" #include "qw/pmove.h" #include "qw/protocol.h" -#include "sbar.h" -#include "clview.h" + +#include "qw/include/cl_cam.h" +#include "qw/include/cl_chat.h" +#include "qw/include/cl_ents.h" +#include "qw/include/cl_http.h" +#include "qw/include/cl_input.h" +#include "qw/include/cl_main.h" +#include "qw/include/cl_parse.h" +#include "qw/include/cl_skin.h" +#include "qw/include/client.h" +#include "qw/include/host.h" +#include "qw/include/map_cfg.h" const char *svc_strings[] = { "svc_bad", @@ -154,39 +163,15 @@ const char *svc_strings[] = { "NEW PROTOCOL" }; -dstring_t *centerprint; int oldparsecountmod; int parsecountmod; double parsecounttime; -int cl_spikeindex, cl_playerindex, cl_flagindex; +int cl_playerindex, cl_flagindex; int cl_h_playerindex, cl_gib1index, cl_gib2index, cl_gib3index; int packet_latency[NET_TIMINGS]; -extern cvar_t *hud_scoreboard_uid; - -entity_t *cl_static_entities; -static entity_t **cl_static_tail; - -static void -CL_LoadSky (void) -{ - plitem_t *item; - const char *name = 0; - - if (!cl.worldspawn) { - r_funcs->R_LoadSkys (0); - return; - } - if ((item = PL_ObjectForKey (cl.worldspawn, "sky")) // Q2/DarkPlaces - || (item = PL_ObjectForKey (cl.worldspawn, "skyname")) // old QF - || (item = PL_ObjectForKey (cl.worldspawn, "qlsky"))) /* QuakeLives */ { - name = PL_String (item); - } - r_funcs->R_LoadSkys (name); -} - int CL_CalcNet (void) { @@ -196,15 +181,18 @@ CL_CalcNet (void) for (i = cls.netchan.outgoing_sequence - UPDATE_BACKUP + 1; i <= cls.netchan.outgoing_sequence; i++) { frame = &cl.frames[i & UPDATE_MASK]; - if (frame->receivedtime == -1) + if (frame->receivedtime == -1) { packet_latency[i & NET_TIMINGSMASK] = 9999; // dropped - else if (frame->receivedtime == -2) + } else if (frame->receivedtime == -2) { packet_latency[i & NET_TIMINGSMASK] = 10000; // choked - else if (frame->invalid) + } else if (frame->invalid) { packet_latency[i & NET_TIMINGSMASK] = 9998; // invalid delta - else - packet_latency[i & NET_TIMINGSMASK] = - (frame->receivedtime - frame->senttime) * 20; + } else { + double d = frame->receivedtime - frame->senttime; + d = log (d * 1000 + 1) / log (1000); + d *= d * cl_netgraph_height; + packet_latency[i & NET_TIMINGSMASK] = d; + } } lost = 0; @@ -222,7 +210,7 @@ CL_CalcNet (void) Returns true if the file exists, otherwise it attempts to start a download from the server. */ -qboolean +bool CL_CheckOrDownloadFile (const char *filename) { QFile *f; @@ -263,57 +251,29 @@ CL_CheckOrDownloadFile (const char *filename) MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, - va ("download \"%s\"", cls.downloadname->str)); + va (0, "download \"%s\"", cls.downloadname->str)); cls.downloadnumber++; return false; } -static plitem_t * -map_ent (const char *mapname) -{ - static progs_t edpr; - char *name = malloc (strlen (mapname) + 4 + 1); - char *buf; - plitem_t *edicts = 0; - QFile *ent_file; - - QFS_StripExtension (mapname, name); - strcat (name, ".ent"); - ent_file = QFS_VOpenFile (name, 0, cl.model_precache[1]->vpath); - if ((buf = (char *) QFS_LoadFile (ent_file, 0))) { - edicts = ED_Parse (&edpr, buf); - free (buf); - } else { - edicts = ED_Parse (&edpr, cl.model_precache[1]->entities); - } - free (name); - return edicts; -} - static void CL_NewMap (const char *mapname) { - cl_static_entities = 0; - cl_static_tail = &cl_static_entities; - r_funcs->R_NewMap (cl.worldmodel, cl.model_precache, cl.nummodels); + const char *skyname = 0; + // R_LoadSkys does the right thing with null pointers. + if (cl.serverinfo) { + skyname = Info_ValueForKey (cl.serverinfo, "sky"); + } + CL_World_NewMap (mapname, skyname); + cl.chasestate.worldmodel = cl_world.scene->worldmodel; + Team_NewMap (); Con_NewMap (); - Hunk_Check (); // make sure nothing is hurt Sbar_CenterPrint (0); - if (cl.model_precache[1] && cl.model_precache[1]->entities) { - cl.edicts = map_ent (mapname); - if (cl.edicts) { - cl.worldspawn = PL_ObjectAtIndex (cl.edicts, 0); - CL_LoadSky (); - if (r_funcs->Fog_ParseWorldspawn) - r_funcs->Fog_ParseWorldspawn (cl.worldspawn); - } - } - - map_cfg (mapname, 1); + Hunk_Check (0); // make sure nothing is hurt } static void @@ -324,7 +284,9 @@ Model_NextDownload (void) if (cls.downloadnumber == 0) { Sys_Printf ("Checking models...\n"); - CL_UpdateScreen (realtime); + cl.viewstate.time = realtime; + cl.viewstate.realtime = realtime; + CL_UpdateScreen (&cl.viewstate); cls.downloadnumber = 1; } @@ -337,18 +299,18 @@ Model_NextDownload (void) return; // started a download } - if (cl.model_name[1]) - map_cfg (cl.model_name[1], 0); + CL_MapCfg (cl.model_name[1]); for (i = 1; i < cl.nummodels; i++) { - char *info_key = 0; + const char *info_key = 0; if (!cl.model_name[i][0]) break; - cl.model_precache[i] = Mod_ForName (cl.model_name[i], false); + DARRAY_APPEND (&cl_world.models, + Mod_ForName (cl.model_name[i], false)); - if (!cl.model_precache[i]) { + if (!cl_world.models.a[i]) { Sys_Printf ("\nThe required model file '%s' could not be found or " "downloaded.\n\n", cl.model_name[i]); Sys_Printf ("You may need to download or purchase a %s client " @@ -359,33 +321,34 @@ Model_NextDownload (void) } if (strequal (cl.model_name[i], "progs/player.mdl") - && cl.model_precache[i]->type == mod_alias) { + && cl_world.models.a[i]->type == mod_alias) { info_key = pmodel_name; - //XXX mod_funcs->Skin_Player_Model (cl.model_precache[i]); + //XXX mod_funcs->Skin_Player_Model (cl_world.models.a[i]); } if (strequal (cl.model_name[i], "progs/eyes.mdl") - && cl.model_precache[i]->type == mod_alias) + && cl_world.models.a[i]->type == mod_alias) info_key = emodel_name; - if (info_key && cl_model_crcs->int_val) { - aliashdr_t *ahdr = cl.model_precache[i]->aliashdr; + if (info_key && cl_model_crcs) { + aliashdr_t *ahdr = cl_world.models.a[i]->aliashdr; if (!ahdr) - ahdr = Cache_Get (&cl.model_precache[i]->cache); - Info_SetValueForKey (cls.userinfo, info_key, va ("%d", ahdr->crc), + ahdr = Cache_Get (&cl_world.models.a[i]->cache); + Info_SetValueForKey (cls.userinfo, info_key, va (0, "%d", + ahdr->crc), 0); if (!cls.demoplayback) { MSG_WriteByte (&cls.netchan.message, clc_stringcmd); - SZ_Print (&cls.netchan.message, va ("setinfo %s %d", info_key, - ahdr->crc)); + SZ_Print (&cls.netchan.message, va (0, "setinfo %s %d", + info_key, ahdr->crc)); } - if (!cl.model_precache[i]->aliashdr) - Cache_Release (&cl.model_precache[i]->cache); + if (!cl_world.models.a[i]->aliashdr) + Cache_Release (&cl_world.models.a[i]->cache); } } // Something went wrong (probably in the server, probably a TF server) // We need to disconnect gracefully. - if (!cl.model_precache[1]) { + if (!cl_world.models.a[1]) { Sys_Printf ("\nThe server has failed to provide the map name.\n\n"); Sys_Printf ("Disconnecting to prevent a crash.\n\n"); CL_Disconnect (); @@ -393,15 +356,14 @@ Model_NextDownload (void) } // all done - cl.worldmodel = cl.model_precache[1]; CL_NewMap (cl.model_name[1]); // done with modellist, request first of static signon messages if (!cls.demoplayback) { MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, - va (prespawn_name, cl.servercount, - cl.worldmodel->checksum2)); + va (0, prespawn_name, cl.servercount, + cl_world.scene->worldmodel->brush.checksum2)); } } @@ -413,7 +375,9 @@ Sound_NextDownload (void) if (cls.downloadnumber == 0) { Sys_Printf ("Checking sounds...\n"); - CL_UpdateScreen (realtime); + cl.viewstate.time = realtime; + cl.viewstate.realtime = realtime; + CL_UpdateScreen (&cl.viewstate); cls.downloadnumber = 1; } @@ -421,7 +385,7 @@ Sound_NextDownload (void) for (; cl.sound_name[cls.downloadnumber][0]; cls.downloadnumber++) { s = cl.sound_name[cls.downloadnumber]; - if (!CL_CheckOrDownloadFile (va ("sound/%s", s))) + if (!CL_CheckOrDownloadFile (va (0, "sound/%s", s))) return; // started a download } @@ -432,16 +396,16 @@ Sound_NextDownload (void) } // done with sounds, request models now - memset (cl.model_precache, 0, sizeof (cl.model_precache)); + cl_world.models.size = 0; + DARRAY_APPEND (&cl_world.models, 0);// ind 0 is null model cl_playerindex = -1; - cl_spikeindex = -1; cl_flagindex = -1; cl_h_playerindex = -1; cl_gib1index = cl_gib2index = cl_gib3index = -1; if (!cls.demoplayback) { MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, - va (modellist_name, cl.servercount, 0)); + va (0, modellist_name, cl.servercount, 0)); } } @@ -462,7 +426,7 @@ CL_RequestNextDownload (void) break; case dl_none: default: - Sys_MaskPrintf (SYS_DEV, "Unknown download type.\n"); + Sys_MaskPrintf (SYS_dev, "Unknown download type.\n"); } } @@ -470,7 +434,7 @@ void CL_FinishDownload (void) { Qclose (cls.download); - VID_SetCaption (va ("Connecting to %s", cls.servername->str)); + VID_SetCaption (va (0, "Connecting to %s", cls.servername->str)); // rename the temp file to it's final name if (strcmp (cls.downloadtempname->str, cls.downloadname->str)) { @@ -660,8 +624,8 @@ CL_ParseDownload (void) if (percent != 100) { // request next block if (percent != cls.downloadpercent) - VID_SetCaption (va ("Downloading %s %d%%", cls.downloadname->str, - percent)); + VID_SetCaption (va (0, "Downloading %s %d%%", + cls.downloadname->str, percent)); cls.downloadpercent = percent; MSG_WriteByte (&cls.netchan.message, clc_stringcmd); @@ -698,7 +662,7 @@ CL_NextUpload (void) MSG_WriteByte (&cls.netchan.message, percent); SZ_Write (&cls.netchan.message, buffer, r); - Sys_MaskPrintf (SYS_DEV, "UPLOAD: %6d: %d written\n", upload_pos - r, r); + Sys_MaskPrintf (SYS_dev, "UPLOAD: %6d: %d written\n", upload_pos - r, r); if (upload_pos != upload_size) return; @@ -720,7 +684,7 @@ CL_StartUpload (byte * data, int size) if (upload_data) free (upload_data); - Sys_MaskPrintf (SYS_DEV, "Upload starting of %d...\n", size); + Sys_MaskPrintf (SYS_dev, "Upload starting of %d...\n", size); upload_data = malloc (size); memcpy (upload_data, data, size); @@ -730,7 +694,7 @@ CL_StartUpload (byte * data, int size) CL_NextUpload (); } -qboolean +bool CL_IsUploading (void) { if (upload_data) @@ -770,9 +734,9 @@ CL_ParseServerData (void) { const char *str; int protover; - //FIXME qboolean cflag = false; + //FIXME bool cflag = false; - Sys_MaskPrintf (SYS_DEV, "Serverdata packet received.\n"); + Sys_MaskPrintf (SYS_dev, "Serverdata packet received.\n"); // wipe the client_state_t struct CL_ClearState (); @@ -812,14 +776,18 @@ CL_ParseServerData (void) } cl.viewentity = cl.playernum + 1; + cl.viewstate.bob_enabled = !cl.spectator; + Sbar_SetPlayerNum (cl.playernum, cl.spectator); // get the full level name str = MSG_ReadString (net_message); strncpy (cl.levelname, str, sizeof (cl.levelname) - 1); + Sbar_SetLevelName (cl.levelname, cls.servername->str); + Sbar_SetGameType (1); // get the movevars movevars.gravity = MSG_ReadFloat (net_message); - r_data->gravity = movevars.gravity; // Gravity for renderer effects + CL_ParticlesGravity (movevars.gravity);// Gravity for renderer effects movevars.stopspeed = MSG_ReadFloat (net_message); movevars.maxspeed = MSG_ReadFloat (net_message); movevars.spectatormaxspeed = MSG_ReadFloat (net_message); @@ -840,7 +808,7 @@ CL_ParseServerData (void) if (!cls.demoplayback) { MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, - va (soundlist_name, cl.servercount, 0)); + va (0, soundlist_name, cl.servercount, 0)); } // now waiting for downloads, etc @@ -876,7 +844,7 @@ CL_ParseSoundlist (void) if (n && !cls.demoplayback) { MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, - va (soundlist_name, cl.servercount, n)); + va (0, soundlist_name, cl.servercount, n)); return; } @@ -903,9 +871,7 @@ CL_ParseModellist (void) Host_Error ("Server sent too many model_precache"); strcpy (cl.model_name[cl.nummodels], str); - if (!strcmp (cl.model_name[cl.nummodels], "progs/spike.mdl")) - cl_spikeindex = cl.nummodels; - else if (!strcmp (cl.model_name[cl.nummodels], "progs/player.mdl")) + if (!strcmp (cl.model_name[cl.nummodels], "progs/player.mdl")) cl_playerindex = cl.nummodels; else if (!strcmp (cl.model_name[cl.nummodels], "progs/flag.mdl")) cl_flagindex = cl.nummodels; @@ -927,7 +893,7 @@ CL_ParseModellist (void) if (!cls.demoplayback) { MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, - va (modellist_name, cl.servercount, n)); + va (0, modellist_name, cl.servercount, n)); } return; } @@ -937,65 +903,17 @@ CL_ParseModellist (void) Model_NextDownload (); } -static void -CL_ParseBaseline (entity_state_t *es) -{ - es->modelindex = MSG_ReadByte (net_message); - es->frame = MSG_ReadByte (net_message); - es->colormap = MSG_ReadByte (net_message); - es->skinnum = MSG_ReadByte (net_message); - - MSG_ReadCoordAngleV (net_message, es->origin, es->angles); - - // LordHavoc: set up baseline to for new effects (alpha, colormod, etc) - es->colormod = 255; - es->alpha = 255; - es->scale = 16; - es->glow_size = 0; - es->glow_color = 254; -} - -/* - CL_ParseStatic - - Static entities are non-interactive world objects - like torches -*/ -static void -CL_ParseStatic (void) -{ - entity_t *ent; - entity_state_t es; - - CL_ParseBaseline (&es); - - ent = r_funcs->R_AllocEntity (); - CL_Init_Entity (ent); - - *cl_static_tail = ent; - cl_static_tail = &ent->unext; - - // copy it to the current state - ent->model = cl.model_precache[es.modelindex]; - ent->frame = es.frame; - ent->skinnum = es.skinnum; - - VectorCopy (es.origin, ent->origin); - CL_TransformEntity (ent, es.angles, true); - - r_funcs->R_AddEfrags (ent); -} - static void CL_ParseStaticSound (void) { - int sound_num, vol, atten; - vec3_t org; + int sound_num; + float vol, atten; + vec4f_t org = { 0, 0, 0, 1 }; - MSG_ReadCoordV (net_message, org); + MSG_ReadCoordV (net_message, (vec_t*)&org);//FIXME sound_num = MSG_ReadByte (net_message); - vol = MSG_ReadByte (net_message); - atten = MSG_ReadByte (net_message); + vol = MSG_ReadByte (net_message) / 255.0; + atten = MSG_ReadByte (net_message) / 64.0; S_StaticSound (cl.sound_precache[sound_num], org, vol, atten); } @@ -1007,7 +925,6 @@ CL_ParseStartSoundPacket (void) { float attenuation; int bits, channel, ent, sound_num, volume; - vec3_t pos; bits = MSG_ReadShort (net_message); @@ -1023,7 +940,8 @@ CL_ParseStartSoundPacket (void) sound_num = MSG_ReadByte (net_message); - MSG_ReadCoordV (net_message, pos); + vec4f_t pos = { 0, 0, 0, 1 }; + MSG_ReadCoordV (net_message, (vec_t*)&pos);//FIXME ent = (bits >> 3) & 1023; channel = bits & 7; @@ -1093,7 +1011,8 @@ CL_ProcessUserInfo (int slot, player_info_t *player) while (!(player->name = Info_Key (player->userinfo, "name"))) { if (player->userid) Info_SetValueForKey (player->userinfo, "name", - va ("user-%i [exploit]", player->userid), 1); + va (0, "user-%i [exploit]", + player->userid), 1); else Info_SetValueForKey (player->userinfo, "name", "", 1); } @@ -1119,7 +1038,7 @@ CL_ProcessUserInfo (int slot, player_info_t *player) player->skinname->value); player->skin = mod_funcs->Skin_SetColormap (player->skin, slot + 1); - Sbar_Changed (); + Sbar_UpdateInfo (slot); } static void @@ -1181,7 +1100,7 @@ CL_SetInfo (void) CL_ProcessUserInfo (slot, player); - Sys_MaskPrintf (SYS_DEV, "SETINFO %s: %s=%s\n", player->name->value, key, + Sys_MaskPrintf (SYS_dev, "SETINFO %s: %s=%s\n", player->name->value, key, value); } @@ -1195,21 +1114,21 @@ CL_ServerInfo (void) strncpy (value, MSG_ReadString (net_message), sizeof (value) - 1); key[sizeof (value) - 1] = 0; - Sys_MaskPrintf (SYS_DEV, "SERVERINFO: %s=%s\n", key, value); + Sys_MaskPrintf (SYS_dev, "SERVERINFO: %s=%s\n", key, value); Info_SetValueForKey (cl.serverinfo, key, value, 0); if (strequal (key, "chase")) { - cl.chase = atoi (value); + cl.viewstate.chase = atoi (value); } else if (strequal (key, "cshifts")) { cl.sv_cshifts = atoi (value); } else if (strequal (key, "no_pogo_stick")) { - Cvar_Set (no_pogo_stick, value); - cl.no_pogo_stick = no_pogo_stick->int_val; + Cvar_Set ("no_pogo_stick", value); + cl.no_pogo_stick = no_pogo_stick; } else if (strequal (key, "teamplay")) { cl.teamplay = atoi (value); - Sbar_DMO_Init_f (hud_scoreboard_uid); // HUD setup, cl.teamplay changed + Sbar_SetTeamplay (cl.teamplay); } else if (strequal (key, "watervis")) { - cl.watervis = atoi (value); + cl.viewstate.watervis = atoi (value); } else if (strequal (key, "fpd")) { cl.fpd = atoi (value); } else if (strequal (key, "fbskins")) { @@ -1238,29 +1157,38 @@ CL_SetStat (int stat, int value) return; } - Sbar_Changed (); - switch (stat) { case STAT_ITEMS: - Sbar_Changed (); +#define IT_POWER (IT_QUAD | IT_SUIT | IT_INVULNERABILITY | IT_INVISIBILITY) + cl.viewstate.powerup_index = (cl.stats[STAT_ITEMS]&IT_POWER) >> 19; break; case STAT_HEALTH: if (cl_player_health_e->func) - GIB_Event_Callback (cl_player_health_e, 1, va ("%i", value)); + GIB_Event_Callback (cl_player_health_e, 1, + va (0, "%i", value)); if (value <= 0) Team_Dead (); break; } cl.stats[stat] = value; + cl.viewstate.weapon_model = cl_world.models.a[cl.stats[STAT_WEAPON]]; + if (cl.stdver) { + cl.viewstate.height = cl.stats[STAT_VIEWHEIGHT]; + } else { + cl.viewstate.height = DEFAULT_VIEWHEIGHT; // view height + } + Sbar_UpdateStats (stat); } static void -CL_MuzzleFlash (void) +CL_ParseMuzzleFlash (void) { - dlight_t *dl; + //FIXME this should just enable the effect on the relevant entity and + //then automatic entity updates take care of the rest int i; player_state_t *pl; - vec3_t fv, rv, uv; + vec3_t f, r, u; + vec4f_t position = { 0, 0, 0, 1}, fv = {}; i = MSG_ReadShort (net_message); @@ -1269,27 +1197,18 @@ CL_MuzzleFlash (void) pl = &cl.frames[parsecountmod].playerstate[i - 1]; - dl = r_funcs->R_AllocDlight (i); - if (!dl) - return; - if (i - 1 == cl.playernum) - AngleVectors (cl.viewangles, fv, rv, uv); + AngleVectors (cl.viewstate.player_angles, f, r, u); else - AngleVectors (pl->viewangles, fv, rv, uv); + AngleVectors (pl->viewangles, f, r, u); - VectorMultAdd (pl->pls.origin, 18, fv, dl->origin); - dl->radius = 200 + (rand () & 31); - dl->die = cl.time + 0.1; - dl->minlight = 32; - dl->color[0] = 0.2; - dl->color[1] = 0.1; - dl->color[2] = 0.05; - dl->color[3] = 0.7; + VectorCopy (f, fv); + VectorCopy (pl->pls.es.origin, position); + CL_MuzzleFlash (position, fv, 0, i, cl.time); } #define SHOWNET(x) \ - if (cl_shownet->int_val == 2) \ + if (cl_shownet == 2) \ Sys_Printf ("%3i:%s\n", net_message->readcount - 1, x); int received_framecount; @@ -1298,17 +1217,21 @@ void CL_ParseServerMessage (void) { int cmd = 0, i, j; + int update_pings = 0; const char *str; static dstring_t *stuffbuf; + TEntContext_t tentCtx = { + cl.viewstate.player_origin, cl.viewentity + }; received_framecount = host_framecount; - cl.last_servermessage = realtime; + cl.viewstate.last_servermessage = realtime; CL_ClearProjectiles (); // if recording demos, copy the message out - if (cl_shownet->int_val == 1) + if (cl_shownet == 1) Sys_Printf ("%i ", net_message->message->cursize); - else if (cl_shownet->int_val == 2) + else if (cl_shownet == 2) Sys_Printf ("------------------ %d\n", cls.netchan.incoming_acknowledged); @@ -1329,7 +1252,7 @@ CL_ParseServerMessage (void) break; // end of message } - SHOWNET (va ("%s(%d)", svc_strings[cmd], cmd)); + SHOWNET (va (0, "%s(%d)", svc_strings[cmd], cmd)); // other commands switch (cmd) { @@ -1374,7 +1297,7 @@ CL_ParseServerMessage (void) break; // TODO: cl_nofake 2 -- accept fake messages from teammates - if (cl_nofake->int_val) { + if (cl_nofake) { char *c; p = dstring_strdup (str); @@ -1400,16 +1323,16 @@ CL_ParseServerMessage (void) str = MSG_ReadString (net_message); if (str[strlen (str) - 1] == '\n') { if (stuffbuf && stuffbuf->str[0]) { - Sys_MaskPrintf (SYS_DEV, "stufftext: %s%s\n", + Sys_MaskPrintf (SYS_dev, "stufftext: %s%s\n", stuffbuf->str, str); Cbuf_AddText (cl_stbuf, stuffbuf->str); dstring_clearstr (stuffbuf); } else { - Sys_MaskPrintf (SYS_DEV, "stufftext: %s\n", str); + Sys_MaskPrintf (SYS_dev, "stufftext: %s\n", str); } Cbuf_AddText (cl_stbuf, str); } else { - Sys_MaskPrintf (SYS_DEV, "partial stufftext: %s\n", str); + Sys_MaskPrintf (SYS_dev, "partial stufftext: %s\n", str); if (!stuffbuf) stuffbuf = dstring_newstr (); dstring_appendstr (stuffbuf, str); @@ -1418,7 +1341,7 @@ CL_ParseServerMessage (void) case svc_setangle: { - vec_t *dest = cl.viewangles; + vec_t *dest = cl.viewstate.player_angles; vec3_t dummy; if (cls.demoplayback2) { @@ -1438,8 +1361,6 @@ CL_ParseServerMessage (void) } Cbuf_Execute_Stack (cl_stbuf); CL_ParseServerData (); - // leave full screen intermission - r_data->vid->recalc_refdef = true; break; case svc_lightstyle: @@ -1468,12 +1389,12 @@ CL_ParseServerMessage (void) // svc_updatename case svc_updatefrags: - Sbar_Changed (); i = MSG_ReadByte (net_message); if (i >= MAX_CLIENTS) Host_Error ("CL_ParseServerMessage: svc_updatefrags > " "MAX_SCOREBOARD"); cl.players[i].frags = (short) MSG_ReadShort (net_message); + Sbar_UpdateFrags (i); break; // svc_clientdata @@ -1487,22 +1408,24 @@ CL_ParseServerMessage (void) // svc_particle case svc_damage: - V_ParseDamage (); + V_ParseDamage (net_message, &cl.viewstate); + // put sbar face into pain frame + Sbar_Damage (cl.time); break; case svc_spawnstatic: - CL_ParseStatic (); + CL_ParseStatic (net_message, 1); break; // svc_spawnbinary case svc_spawnbaseline: i = MSG_ReadShort (net_message); - CL_ParseBaseline (&qw_entstates.baseline[i]); + CL_ParseBaseline (net_message, &qw_entstates.baseline[i], 1); break; case svc_temp_entity: - CL_ParseTEnt (); + CL_ParseTEnt_qw (net_message, cl.time, &tentCtx); break; case svc_setpause: @@ -1517,19 +1440,17 @@ CL_ParseServerMessage (void) case svc_centerprint: str = MSG_ReadString (net_message); - if (strcmp (str, centerprint->str)) { - dstring_copystr (centerprint, str); - //FIXME logging - } Sbar_CenterPrint (str); break; case svc_killedmonster: cl.stats[STAT_MONSTERS]++; + Sbar_UpdateStats (STAT_MONSTERS); break; case svc_foundsecret: cl.stats[STAT_SECRETS]++; + Sbar_UpdateStats (STAT_SECRETS); break; case svc_spawnstaticsound: @@ -1537,40 +1458,35 @@ CL_ParseServerMessage (void) break; case svc_intermission: - Sys_MaskPrintf (SYS_DEV, "svc_intermission\n"); + Sys_MaskPrintf (SYS_dev, "svc_intermission\n"); cl.intermission = 1; - r_data->force_fullscreen = 1; + SCR_SetFullscreen (1); cl.completed_time = realtime; - r_data->vid->recalc_refdef = true; // go to full screen - Sys_MaskPrintf (SYS_DEV, "intermission simorg: "); - MSG_ReadCoordV (net_message, cl.simorg); - for (i = 0; i < 3; i++) - Sys_MaskPrintf (SYS_DEV, "%f ", cl.simorg[i]); - Sys_MaskPrintf (SYS_DEV, "\nintermission simangles: "); - MSG_ReadAngleV (net_message, cl.simangles); - cl.simangles[ROLL] = 0; // FIXME @@@ - for (i = 0; i < 3; i++) - Sys_MaskPrintf (SYS_DEV, "%f ", cl.simangles[i]); - Sys_MaskPrintf (SYS_DEV, "\n"); - VectorZero (cl.simvel); + Sys_MaskPrintf (SYS_dev, "intermission simorg: "); + MSG_ReadCoordV (net_message, (vec_t*)&cl.viewstate.player_origin);//FIXME + cl.viewstate.player_origin[3] = 1; + Sys_MaskPrintf (SYS_dev, VEC4F_FMT, + VEC4_EXP (cl.viewstate.player_origin)); + Sys_MaskPrintf (SYS_dev, "\nintermission simangles: "); + MSG_ReadAngleV (net_message, cl.viewstate.player_angles); + cl.viewstate.player_angles[ROLL] = 0; // FIXME @@@ + Sys_MaskPrintf (SYS_dev, "%f %f %f", + VectorExpand (cl.viewstate.player_angles)); + Sys_MaskPrintf (SYS_dev, "\n"); + cl.viewstate.velocity = (vec4f_t) { }; // automatic fraglogging (by elmex) // XXX: Should this _really_ called here? if (!cls.demoplayback) - Sbar_LogFrags (); + Sbar_LogFrags (cl.time); break; case svc_finale: cl.intermission = 2; - r_data->force_fullscreen = 1; + SCR_SetFullscreen (1); cl.completed_time = realtime; - r_data->vid->recalc_refdef = true; // go to full screen str = MSG_ReadString (net_message); - if (strcmp (str, centerprint->str)) { - dstring_copystr (centerprint, str); - //FIXME logging - } Sbar_CenterPrint (str); break; @@ -1586,11 +1502,17 @@ CL_ParseServerMessage (void) // svc_cutscene (same value as svc_smallkick) case svc_smallkick: - cl.punchangle[PITCH] = -2; + cl.viewstate.punchangle = (vec4f_t) { + // -2 degrees pitch + 0, -0.0174524064, 0, 0.999847695 + }; break; case svc_bigkick: - cl.punchangle[PITCH] = -4; + cl.viewstate.punchangle = (vec4f_t) { + // -4 degrees pitch + 0, -0.0348994967, 0, 0.999390827 + }; break; case svc_updateping: @@ -1599,6 +1521,7 @@ CL_ParseServerMessage (void) Host_Error ("CL_ParseServerMessage: svc_updateping > " "MAX_SCOREBOARD"); cl.players[i].ping = MSG_ReadShort (net_message); + update_pings = 1; break; case svc_updateentertime: @@ -1618,7 +1541,7 @@ CL_ParseServerMessage (void) break; case svc_muzzleflash: - CL_MuzzleFlash (); + CL_ParseMuzzleFlash (); break; case svc_updateuserinfo: @@ -1634,7 +1557,7 @@ CL_ParseServerMessage (void) break; case svc_nails: - CL_ParseProjectiles (false); + CL_ParseProjectiles (net_message, false, &tentCtx); break; case svc_chokecount: // some preceding packets were choked @@ -1682,13 +1605,17 @@ CL_ParseServerMessage (void) Host_Error ("CL_ParseServerMessage: svc_updatepl > " "MAX_SCOREBOARD"); cl.players[i].pl = MSG_ReadByte (net_message); + update_pings = 1; break; case svc_nails2: // FIXME from qwex - CL_ParseProjectiles (true); + CL_ParseProjectiles (net_message, true, &tentCtx); break; } } + if (update_pings) { + Sbar_UpdatePings (); + } CL_SetSolidEntities (); } diff --git a/qw/source/cl_pred.c b/qw/source/cl_pred.c index 6b110e46c..2dbf45249 100644 --- a/qw/source/cl_pred.c +++ b/qw/source/cl_pred.c @@ -34,26 +34,43 @@ #include "QF/cvar.h" #include "QF/keys.h" -#include "qw/bothdefs.h" #include "compat.h" -#include "cl_ents.h" -#include "cl_pred.h" -#include "client.h" + +#include "qw/bothdefs.h" +#include "qw/include/cl_ents.h" +#include "qw/include/cl_pred.h" +#include "qw/include/client.h" #include "qw/pmove.h" -cvar_t *cl_predict; -cvar_t *cl_pushlatency; +int cl_predict; +static cvar_t cl_predict_cvar = { + .name = "cl_predict", + .description = + "Set to enable client prediction", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_predict }, +}; +float cl_pushlatency; +static cvar_t cl_pushlatency_cvar = { + .name = "pushlatency", + .description = + "How much prediction should the client make", + .default_value = "-999", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &cl_pushlatency }, +}; void -CL_PredictUsercmd (player_state_t * from, player_state_t * to, usercmd_t *u, - qboolean clientplayer) +CL_PredictUsercmd (player_state_t *from, player_state_t *to, usercmd_t *u, + bool clientplayer) { if (!clientplayer) { - if (VectorIsZero (from->pls.velocity)) { - VectorCopy (from->pls.origin, to->pls.origin); + if (VectorIsZero (from->pls.es.velocity)) { + VectorCopy (from->pls.es.origin, to->pls.es.origin); VectorCopy (u->angles, to->viewangles); - VectorCopy (from->pls.velocity, to->pls.velocity); + VectorCopy (from->pls.es.velocity, to->pls.es.velocity); return; } } @@ -71,9 +88,9 @@ CL_PredictUsercmd (player_state_t * from, player_state_t * to, usercmd_t *u, return; } - VectorCopy (from->pls.origin, pmove.origin); + VectorCopy (from->pls.es.origin, pmove.origin); VectorCopy (u->angles, pmove.angles); - VectorCopy (from->pls.velocity, pmove.velocity); + VectorCopy (from->pls.es.velocity, pmove.velocity); pmove.oldbuttons = from->oldbuttons; pmove.oldonground = from->oldonground; @@ -91,11 +108,11 @@ CL_PredictUsercmd (player_state_t * from, player_state_t * to, usercmd_t *u, to->waterjumptime = pmove.waterjumptime; to->oldbuttons = pmove.oldbuttons; // Tonik to->oldonground = pmove.oldonground; - VectorCopy (pmove.origin, to->pls.origin); + VectorCopy (pmove.origin, to->pls.es.origin); VectorCopy (pmove.angles, to->viewangles); - VectorCopy (pmove.velocity, to->pls.velocity); + VectorCopy (pmove.velocity, to->pls.es.velocity); to->onground = onground; - to->pls.weaponframe = from->pls.weaponframe; + to->pls.es.weaponframe = from->pls.es.weaponframe; } void @@ -104,19 +121,22 @@ CL_PredictMove (void) float f; int oldphysent, i; frame_t *from, *to = NULL; + entity_state_t *fromes; + entity_state_t *toes; - if (cl_pushlatency->value > 0) - Cvar_Set (cl_pushlatency, "0"); + if (cl_pushlatency > 0) + Cvar_Set ("pushlatency", "0"); if (cl.paused) return; // assume on ground unless prediction says different - cl.onground = 0; + cl.viewstate.onground = 0; - cl.time = realtime - cls.latency - cl_pushlatency->value * 0.001; + cl.time = realtime - cls.latency - cl_pushlatency * 0.001; if (cl.time > realtime) cl.time = realtime; + cl.viewstate.time = cl.time; if (cl.intermission) { return; @@ -129,15 +149,16 @@ CL_PredictMove (void) UPDATE_BACKUP - 1) return; - VectorCopy (cl.viewangles, cl.simangles); - cl.simangles[ROLL] = 0; // FIXME @@@ + //VectorCopy (cl.viewstate.angles, cl.viewstate.angles); + cl.viewstate.player_angles[ROLL] = 0; // FIXME @@@ // this is the last frame received from the server from = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + fromes = &from->playerstate[cl.playernum].pls.es; - if (!cl_predict->int_val) { - VectorCopy (from->playerstate[cl.playernum].pls.velocity, cl.simvel); - VectorCopy (from->playerstate[cl.playernum].pls.origin, cl.simorg); + if (!cl_predict) { + cl.viewstate.velocity = fromes->velocity; + cl.viewstate.player_origin = fromes->origin; return; } @@ -153,7 +174,7 @@ CL_PredictMove (void) CL_PredictUsercmd (&from->playerstate[cl.playernum], &to->playerstate[cl.playernum], &to->cmd, true); - cl.onground = onground; + cl.viewstate.onground = onground; if (to->senttime >= cl.time) break; from = to; @@ -164,6 +185,7 @@ CL_PredictMove (void) if (i == UPDATE_BACKUP - 1 || !to) return; // net hasn't deliver packets in a // long time... + toes = &to->playerstate[cl.playernum].pls.es; // now interpolate some fraction of the final frame if (to->senttime == from->senttime) @@ -174,29 +196,20 @@ CL_PredictMove (void) } for (i = 0; i < 3; i++) - if (fabs (from->playerstate[cl.playernum].pls.origin[i] - - to->playerstate[cl.playernum].pls.origin[i]) > 128) { + if (fabs (fromes->origin[i] - toes->origin[i]) > 128) { // teleported, so don't lerp - VectorCopy (to->playerstate[cl.playernum].pls.velocity, cl.simvel); - VectorCopy (to->playerstate[cl.playernum].pls.origin, cl.simorg); + cl.viewstate.velocity = toes->velocity; + cl.viewstate.player_origin = toes->origin; return; } - for (i = 0; i < 3; i++) { - cl.simorg[i] = from->playerstate[cl.playernum].pls.origin[i] + - f * (to->playerstate[cl.playernum].pls.origin[i] - - from->playerstate[cl.playernum].pls.origin[i]); - cl.simvel[i] = from->playerstate[cl.playernum].pls.velocity[i] + - f * (to->playerstate[cl.playernum].pls.velocity[i] - - from->playerstate[cl.playernum].pls.velocity[i]); - } + cl.viewstate.player_origin = fromes->origin + + f * (toes->origin - fromes->origin); } void CL_Prediction_Init_Cvars (void) { - cl_predict = Cvar_Get ("cl_predict", "1", CVAR_NONE, NULL, - "Set to enable client prediction"); - cl_pushlatency = Cvar_Get ("pushlatency", "-999", CVAR_NONE, NULL, - "How much prediction should the client make"); + Cvar_Register (&cl_predict_cvar, 0, 0); + Cvar_Register (&cl_pushlatency_cvar, 0, 0); } diff --git a/qw/source/cl_rss.c b/qw/source/cl_rss.c index 447e25dcc..8e0025454 100644 --- a/qw/source/cl_rss.c +++ b/qw/source/cl_rss.c @@ -42,47 +42,183 @@ #include "QF/pcx.h" #include "QF/sys.h" -#include "cl_parse.h" -#include "client.h" +#include "qw/include/cl_parse.h" +#include "qw/include/client.h" -void -CL_RSShot_f (void) +/* + Find closest color in the palette for named color +*/ +static int +rgb_palette (int r, int g, int b, int bgr) +{ + float bestdist, dist; + int r1, g1, b1, i; + int best = 0; + static int lastbest; + static int lr = -1, lg = -1, lb = -1; + + if (bgr) { + int t = r; + r = b; + b = t; + } + + if (r == lr && g == lg && b == lb) + return lastbest; + + bestdist = 256 * 256 * 3; + + for (i = 0; i < 256; i++) { + int j; + j = i * 3; + r1 = r_data->vid->palette[j] - r; + g1 = r_data->vid->palette[j + 1] - g; + b1 = r_data->vid->palette[j + 2] - b; + dist = r1 * r1 + g1 * g1 + b1 * b1; + if (dist < bestdist) { + bestdist = dist; + best = i; + } + } + lr = r; + lg = g; + lb = b; + lastbest = best; + return best; +} + +static void +snap_drawchar (int num, byte *dest, int width) +{ + byte *source; + int col, row, drawline, x; + + row = num >> 4; + col = num & 15; + source = draw_chars + (row << 10) + (col << 3); + + drawline = 8; + + while (drawline--) { + for (x = 0; x < 8; x++) + if (source[x]) + dest[x] = source[x]; + else + dest[x] = 98; + source += 128; + dest -= width; + } + +} + +static void +snap_drawstring (const char *s, tex_t *tex, int x, int y) +{ + byte *dest; + byte *buf = tex->data; + const byte *p; + int width = tex->width; + + dest = buf + ((y * width) + x); + + p = (const byte *) s; + while (*p) { + snap_drawchar (*p++, dest, width); + dest += 8; + } +} + +static tex_t * +cruch_snap (tex_t *snap, unsigned width, unsigned height) +{ + byte *src, *dest; + float fracw, frach; + unsigned count, dex, dey, dx, dy, nx, r, g, b, x, y, w, h; + tex_t *tex; + + //FIXME casts + w = ((unsigned)snap->width < width) ? (unsigned)snap->width : width; + h = ((unsigned)snap->height < height) ? (unsigned)snap->height : height; + + fracw = (float) snap->width / (float) w; + frach = (float) snap->height / (float) h; + + tex = malloc (sizeof (tex_t) + w * h); + tex->data = (byte *) (tex + 1); + if (!tex) + return 0; + + tex->width = w; + tex->height = h; + tex->flagbits = 0; + tex->loaded = 1; + tex->flipped = snap->flipped; + tex->palette = r_data->vid->palette; + + for (y = 0; y < h; y++) { + dest = tex->data + (w * y); + + for (x = 0; x < w; x++) { + r = g = b = 0; + + dx = x * fracw; + dex = (x + 1) * fracw; + if (dex == dx) + dex++; // at least one + dy = y * frach; + dey = (y + 1) * frach; + if (dey == dy) + dey++; // at least one + + count = 0; + for (; dy < dey; dy++) { + src = snap->data + (snap->width * 3 * dy) + dx * 3; + for (nx = dx; nx < dex; nx++) { + r += *src++; + g += *src++; + b += *src++; + count++; + } + } + r /= count; + g /= count; + b /= count; + *dest++ = rgb_palette (r, g, b, snap->bgr); + } + } + free (snap); + + return tex; +} + +static int snap_pending; + +static void +snap_capture (tex_t *snap, void *data) { - dstring_t *st; int pcx_len; pcx_t *pcx = 0; - tex_t *tex = 0; - time_t now; - - if (CL_IsUploading ()) - return; // already one pending - if (cls.state < ca_onserver) - return; // must be connected - - Sys_Printf ("Remote screen shot requested.\n"); - - tex = r_funcs->SCR_ScreenShot (RSSHOT_WIDTH, RSSHOT_HEIGHT); + tex_t *tex = cruch_snap (snap, RSSHOT_WIDTH, RSSHOT_HEIGHT); + snap_pending = 0; if (tex) { + time_t now; time (&now); - st = dstring_strdup (ctime (&now)); + dstring_t *st = dstring_strdup (ctime (&now)); dstring_snip (st, strlen (st->str) - 1, 1); - r_funcs->SCR_DrawStringToSnap (st->str, tex, - tex->width - strlen (st->str) * 8, - tex->height - 1); + snap_drawstring (st->str, tex, tex->width - strlen (st->str) * 8, + tex->height - 1); dstring_copystr (st, cls.servername->str); - r_funcs->SCR_DrawStringToSnap (st->str, tex, - tex->width - strlen (st->str) * 8, - tex->height - 11); + snap_drawstring (st->str, tex, tex->width - strlen (st->str) * 8, + tex->height - 11); - dstring_copystr (st, cl_name->string); - r_funcs->SCR_DrawStringToSnap (st->str, tex, - tex->width - strlen (st->str) * 8, - tex->height - 21); + dstring_copystr (st, cl_name); + snap_drawstring (st->str, tex, tex->width - strlen (st->str) * 8, + tex->height - 21); pcx = EncodePCX (tex->data, tex->width, tex->height, tex->width, - r_data->vid->basepal, true, &pcx_len); + r_data->vid->basepal, tex->flipped, &pcx_len); free (tex); } if (pcx) { @@ -91,3 +227,17 @@ CL_RSShot_f (void) Sys_Printf ("Sending shot to server...\n"); } } + +void +CL_RSShot_f (void) +{ + if (snap_pending || CL_IsUploading ()) + return; // already one pending + if (cls.state < ca_onserver) + return; // must be connected + + Sys_Printf ("Remote screen shot requested.\n"); + + snap_pending = 1; + r_funcs->capture_screen (snap_capture, 0); +} diff --git a/qw/source/cl_screen.c b/qw/source/cl_screen.c deleted file mode 100644 index adf20f7e4..000000000 --- a/qw/source/cl_screen.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - cl_screen.c - - master for refresh, status bar, console, chat, notify, etc - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif - -#include - -#include "QF/console.h" -#include "QF/cvar.h" -#include "QF/draw.h" -#include "QF/image.h" -#include "QF/pcx.h" -#include "QF/screen.h" - -#include "client.h" -#include "clview.h" -#include "sbar.h" - -static qpic_t *scr_net; - -static void -SCR_DrawNet (void) -{ - if (cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged < - UPDATE_BACKUP - 1) - return; - if (cls.demoplayback) - return; - - if (!scr_net) - scr_net = r_funcs->Draw_PicFromWad ("net"); - - r_funcs->Draw_Pic (r_data->scr_vrect->x + 64, r_data->scr_vrect->y, - scr_net); -} - -static void -SCR_DrawLoading (void) -{ - qpic_t *pic; - - if (!cl.loading) - return; - pic = r_funcs->Draw_CachePic ("gfx/loading.lmp", 1); - r_funcs->Draw_Pic ((r_data->vid->conwidth - pic->width) / 2, - (r_data->vid->conheight - 48 - pic->height) / 2, pic); -} - -static void -SCR_CShift (void) -{ - mleaf_t *leaf; - int contents = CONTENTS_EMPTY; - - if (cls.state == ca_active && cl.worldmodel) { - leaf = Mod_PointInLeaf (r_data->refdef->vieworg, cl.worldmodel); - contents = leaf->contents; - } - V_SetContentsColor (contents); - r_funcs->Draw_BlendScreen (r_data->vid->cshift_color); -} - -static SCR_Func scr_funcs_normal[] = { - 0, //Draw_Crosshair, - 0, //SCR_DrawRam, - 0, //SCR_DrawTurtle, - 0, //SCR_DrawPause, - SCR_DrawNet, - CL_NetGraph, - Sbar_Draw, - SCR_CShift, - Sbar_DrawCenterPrint, - Con_DrawConsole, - SCR_DrawLoading, - 0 -}; - -static SCR_Func scr_funcs_intermission[] = { - Sbar_IntermissionOverlay, - Con_DrawConsole, - 0 -}; - -static SCR_Func scr_funcs_finale[] = { - Sbar_FinaleOverlay, - Con_DrawConsole, - 0, -}; - -static SCR_Func *scr_funcs[] = { - scr_funcs_normal, - scr_funcs_intermission, - scr_funcs_finale, -}; - -void -CL_UpdateScreen (double realtime) -{ - unsigned index = cl.intermission; - - if (index >= sizeof (scr_funcs) / sizeof (scr_funcs[0])) - index = 0; - - //FIXME not every time - if (cls.state == ca_active) { - if (cl.watervis) - r_data->min_wateralpha = 0.0; - else - r_data->min_wateralpha = 1.0; - } - scr_funcs_normal[0] = r_funcs->Draw_Crosshair; - scr_funcs_normal[1] = r_funcs->SCR_DrawRam; - scr_funcs_normal[2] = r_funcs->SCR_DrawTurtle; - scr_funcs_normal[3] = r_funcs->SCR_DrawPause; - - V_PrepBlend (); - r_funcs->SCR_UpdateScreen (realtime, V_RenderView, scr_funcs[index]); -} diff --git a/qw/source/cl_skin.c b/qw/source/cl_skin.c index d533a58d3..03cb7f309 100644 --- a/qw/source/cl_skin.c +++ b/qw/source/cl_skin.c @@ -43,16 +43,50 @@ #include "QF/sys.h" #include "QF/va.h" -#include "cl_parse.h" -#include "cl_skin.h" -#include "client.h" +#include "client/screen.h" #include "compat.h" -#include "host.h" -cvar_t *noskins; //XXX FIXME -cvar_t *skin; -cvar_t *topcolor; -cvar_t *bottomcolor; +#include "qw/include/cl_parse.h" +#include "qw/include/cl_skin.h" +#include "qw/include/client.h" +#include "qw/include/host.h" + +int noskins; +static cvar_t noskins_cvar = { + .name = "noskins", + .description = + "set to 1 to not download new skins", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &noskins }, +}; +char *skin; +static cvar_t skin_cvar = { + .name = "skin", + .description = + "Players skin", + .default_value = "", + .flags = CVAR_ARCHIVE | CVAR_USERINFO, + .value = { .type = 0, .value = &skin }, +}; +int topcolor; +static cvar_t topcolor_cvar = { + .name = "topcolor", + .description = + "Players color on top", + .default_value = "0", + .flags = CVAR_ARCHIVE | CVAR_USERINFO, + .value = { .type = &cexpr_int, .value = &topcolor }, +}; +int bottomcolor; +static cvar_t bottomcolor_cvar = { + .name = "bottomcolor", + .description = + "Players color on bottom", + .default_value = "0", + .flags = CVAR_ARCHIVE | CVAR_USERINFO, + .value = { .type = &cexpr_int, .value = &bottomcolor }, +}; void @@ -66,7 +100,9 @@ Skin_NextDownload (void) if (cls.downloadnumber == 0) { Sys_Printf ("Checking skins...\n"); - CL_UpdateScreen (realtime); + cl.viewstate.time = realtime; + cl.viewstate.realtime = realtime; + CL_UpdateScreen (&cl.viewstate); } cls.downloadtype = dl_skin; @@ -75,9 +111,10 @@ Skin_NextDownload (void) if (!sc->name || !sc->name->value[0]) continue; //XXX Skin_Find (sc); - if (noskins->int_val) //XXX FIXME + if (noskins) //XXX FIXME continue; - //XXX if (!CL_CheckOrDownloadFile (va ("skins/%s.pcx", sc->skin->name))) + //XXX if (!CL_CheckOrDownloadFile (va (0, "skins/%s.pcx", + // sc->skin->name))) //XXX return; // started a download } @@ -98,7 +135,7 @@ Skin_NextDownload (void) // get next signon phase MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, - va ("begin %i", cl.servercount)); + va (0, "begin %i", cl.servercount)); Cache_Report (); // print remaining memory } CL_SetState (ca_active); @@ -173,18 +210,19 @@ CL_Color_f (void) bottom = 13; snprintf (num, sizeof (num), "%i", top); - Cvar_Set (topcolor, num); + Cvar_Set ("topcolor", num); snprintf (num, sizeof (num), "%i", bottom); - Cvar_Set (bottomcolor, num); + Cvar_Set ("bottomcolor", num); } static void -skin_f (cvar_t *var) +skin_f (void *data, const cvar_t *cvar) { - char *s = Hunk_TempAlloc (strlen (var->string) + 1); - QFS_StripExtension (var->string, s); - Cvar_Set (var, s); - Cvar_Info (var); + char *s = Hunk_TempAlloc (0, strlen (skin) + 1); + QFS_StripExtension (skin, s); + free (skin); // cvar allocated one FIXME do in validator? + skin = strdup (s); + Cvar_Info (0, cvar); } void @@ -198,12 +236,8 @@ CL_Skin_Init (void) "shirt pants) Note that if only shirt color is given, " "pants will match"); - noskins = Cvar_Get ("noskins", "0", CVAR_ARCHIVE, NULL, //XXX FIXME - "set to 1 to not download new skins"); - skin = Cvar_Get ("skin", "", CVAR_ARCHIVE | CVAR_USERINFO, skin_f, - "Players skin"); - topcolor = Cvar_Get ("topcolor", "0", CVAR_ARCHIVE | CVAR_USERINFO, - Cvar_Info, "Players color on top"); - bottomcolor = Cvar_Get ("bottomcolor", "0", CVAR_ARCHIVE | CVAR_USERINFO, - Cvar_Info, "Players color on bottom"); + Cvar_Register (&noskins_cvar, 0, 0); + Cvar_Register (&skin_cvar, skin_f, 0); + Cvar_Register (&topcolor_cvar, Cvar_Info, &topcolor); + Cvar_Register (&bottomcolor_cvar, Cvar_Info, &bottomcolor); } diff --git a/qw/source/cl_slist.c b/qw/source/cl_slist.c index 569b711ce..9dd89e9a1 100644 --- a/qw/source/cl_slist.c +++ b/qw/source/cl_slist.c @@ -68,9 +68,9 @@ #include "QF/va.h" #include "qw/bothdefs.h" -#include "cl_main.h" -#include "cl_slist.h" -#include "client.h" +#include "qw/include/cl_main.h" +#include "qw/include/cl_slist.h" +#include "qw/include/client.h" typedef struct server_entry_s { char *server; @@ -90,10 +90,42 @@ server_entry_t *fav_slist; int which_slist; int slist_last_details; -cvar_t *sl_sortby; -cvar_t *sl_filter; -cvar_t *sl_game; -cvar_t *sl_ping; +int sl_sortby; +static cvar_t sl_sortby_cvar = { + .name = "sl_sortby", + .description = + "0 = sort by name, 1 = sort by ping", + .default_value = "0", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &sl_sortby }, +}; +int sl_filter; +static cvar_t sl_filter_cvar = { + .name = "sl_filter", + .description = + "enable server filter", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sl_filter }, +}; +char *sl_game; +static cvar_t sl_game_cvar = { + .name = "sl_game", + .description = + "sets the serverlist game filter", + .default_value = "", + .flags = CVAR_ARCHIVE, + .value = { .type = 0, .value = &sl_game }, +}; +int sl_ping; +static cvar_t sl_ping_cvar = { + .name = "sl_ping", + .description = + "sets the serverlist ping filter", + .default_value = "", + .flags = CVAR_ARCHIVE, + .value = { .type = &cexpr_int, .value = &sl_ping }, +}; static void @@ -119,7 +151,6 @@ SL_Add (server_entry_t *start, const char *ip, const char *desc) { server_entry_t *p; - p = start; if (!start) { // Nothing at beginning of list, // create it start = calloc (1, sizeof (server_entry_t)); @@ -226,19 +257,19 @@ SL_Swap (server_entry_t *swap1, server_entry_t *swap2) static int SL_CheckFilter (server_entry_t *sl_filteritem) { - if (!sl_filter->int_val) + if (!sl_filter) return (1); if (!sl_filteritem->status) return (0); - if (strlen (sl_game->string)) { + if (strlen (sl_game)) { if (strcasecmp (Info_ValueForKey (sl_filteritem->status, "*gamedir"), - sl_game->string) != 0) + sl_game) != 0) return (0); } - if (sl_ping->int_val) { + if (sl_ping) { if (!sl_filteritem->pongback) return (0); - if (((int) (sl_filteritem->pongback * 1000)) >= sl_ping->int_val) + if (((int) (sl_filteritem->pongback * 1000)) >= sl_ping) return (0); } return (1); @@ -315,7 +346,7 @@ SL_Shutdown (void) SL_Del_All (all_slist); } -static char * +static __attribute__((pure)) char * gettokstart (char *str, int req, char delim) { char *start = str; @@ -325,7 +356,7 @@ gettokstart (char *str, int req, char delim) start++; } if (*start == '\0') - return '\0'; + return 0; while (tok < req) { // Stop when we get to the requested // token if (*++start == delim) { // Increment pointer and test @@ -335,20 +366,20 @@ gettokstart (char *str, int req, char delim) tok++; } if (*start == '\0') { - return '\0'; + return 0; } } return start; } -static int +static __attribute__((pure)) int gettoklen (char *str, int req, char delim) { char *start = 0; int len = 0; start = gettokstart (str, req, delim); - if (start == '\0') { + if (*start == '\0') { return 0; } while (*start != delim && *start != '\0') { @@ -374,7 +405,7 @@ SL_SortEntry (server_entry_t *start) return; for (q = start->next; q; q = q->next) { - if (sl_sortby->int_val) { + if (sl_sortby) { if ((q->pongback) && (start->pongback) && (start->pongback > q->pongback)) { SL_Swap (start, q); @@ -397,7 +428,7 @@ SL_SortEntry (server_entry_t *start) } static void -SL_Sort (cvar_t *var) +SL_Sort (void *data, const cvar_t *cvar) { server_entry_t *p; @@ -414,7 +445,7 @@ SL_Con_List (server_entry_t *sldata) int serv; server_entry_t *cp; - SL_Sort (sl_sortby); + SL_Sort (0, 0); for (serv = 0; serv < SL_Len (sldata); serv++) { cp = SL_Get_By_Num (sldata, serv); @@ -521,7 +552,7 @@ SL_Switch (void) slist = fav_slist; which_slist = 0; } - SL_Sort (sl_sortby); + SL_Sort (0, 0); return (which_slist); } @@ -576,7 +607,7 @@ MSL_ParseServerList (const char *msl_data) unsigned int msl_ptr; for (msl_ptr = 0; msl_ptr < strlen (msl_data); msl_ptr = msl_ptr + 6) { - slist = SL_Add (slist, va ("%i.%i.%i.%i:%i", + slist = SL_Add (slist, va (0, "%i.%i.%i.%i:%i", (byte) msl_data[msl_ptr], (byte) msl_data[msl_ptr+1], (byte) msl_data[msl_ptr+2], @@ -639,14 +670,10 @@ SL_Init (void) which_slist = 0; Cmd_AddCommand ("slist", SL_Command, "console commands to access server " "list\n"); - sl_sortby = Cvar_Get ("sl_sortby", "0", CVAR_ARCHIVE, SL_Sort, "0 = sort " - "by name, 1 = sort by ping"); - sl_filter = Cvar_Get ("sl_filter", "0", CVAR_NONE, NULL, "enable server " - "filter"); - sl_game = Cvar_Get ("sl_game", "", CVAR_ARCHIVE, NULL, "sets the " - "serverlist game filter"); - sl_ping = Cvar_Get ("sl_ping", "", CVAR_ARCHIVE, NULL, "sets the " - "serverlist ping filter"); + Cvar_Register (&sl_sortby_cvar, SL_Sort, 0); + Cvar_Register (&sl_filter_cvar, 0, 0); + Cvar_Register (&sl_game_cvar, 0, 0); + Cvar_Register (&sl_ping_cvar, 0, 0); } int diff --git a/qw/source/cl_sys_sdl.c b/qw/source/cl_sys_sdl.c index 75f7c0024..3b610bb59 100644 --- a/qw/source/cl_sys_sdl.c +++ b/qw/source/cl_sys_sdl.c @@ -50,19 +50,16 @@ # include #endif -#ifndef _WIN32 -# include -#endif - #include #include #include "QF/qargs.h" #include "QF/sys.h" -#include "host.h" #include "netchan.h" +#include "qw/include/host.h" + #ifdef _WIN32 # include "winquake.h" #endif @@ -93,7 +90,7 @@ startup (void) } static void -shutdown_f (void) +shutdown_f (void *data) { #ifndef _WIN32 // change stdin to blocking @@ -121,10 +118,13 @@ SDL_main (int argc, char *argv[]) #ifndef _WIN32 if (!COM_CheckParm ("-noconinput")) fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) | O_NONBLOCK); +#else + // hack to prevent gcc suggesting noreturn + if (sys_nostdout) { + return 1; + } #endif - Sys_RegisterShutdown (Host_Shutdown); - Sys_RegisterShutdown (Net_LogStop); - Sys_RegisterShutdown (shutdown_f); + Sys_RegisterShutdown (shutdown_f, 0); Host_Init (); diff --git a/qw/source/cl_sys_unix.c b/qw/source/cl_sys_unix.c index 05aedf9ff..956be135e 100644 --- a/qw/source/cl_sys_unix.c +++ b/qw/source/cl_sys_unix.c @@ -48,11 +48,12 @@ #include "QF/qargs.h" #include "QF/sys.h" -#include "host.h" #include "netchan.h" +#include "qw/include/host.h" + static void -shutdown_f (void) +shutdown_f (void *data) { // change stdin to blocking fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~O_NONBLOCK); @@ -73,9 +74,7 @@ main (int argc, const char **argv) if (!COM_CheckParm ("-noconinput")) fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) | O_NONBLOCK); - Sys_RegisterShutdown (Host_Shutdown); - Sys_RegisterShutdown (Net_LogStop); - Sys_RegisterShutdown (shutdown_f); + Sys_RegisterShutdown (shutdown_f, 0); Host_Init (); diff --git a/qw/source/cl_sys_win.c b/qw/source/cl_sys_win.c index 44d7c8afb..959a3b58a 100644 --- a/qw/source/cl_sys_win.c +++ b/qw/source/cl_sys_win.c @@ -34,8 +34,8 @@ #include "QF/screen.h" #include "QF/sys.h" -#include "client.h" -#include "host.h" +#include "qw/include/client.h" +#include "qw/include/host.h" #define MAXIMUM_WIN_MEMORY 0x1000000 #define MINIMUM_WIN_MEMORY 0x0c00000 @@ -44,7 +44,7 @@ // minimization #define NOT_FOCUS_SLEEP 20 // sleep time when not focus -qboolean ActiveApp, Minimized, WinNT; +bool ActiveApp, Minimized, WinNT; HWND hwnd_dialog; // startup dialog box @@ -93,7 +93,7 @@ startup (void) } static void -shutdown_f (void) +shutdown_f (void *data) { if (tevent) CloseHandle (tevent); @@ -181,9 +181,7 @@ WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, if (!tevent) Sys_Error ("Couldn't create event"); - Sys_RegisterShutdown (Host_Shutdown); - Sys_RegisterShutdown (Net_LogStop); - Sys_RegisterShutdown (shutdown_f); + Sys_RegisterShutdown (shutdown_f, 0); Host_Init (); diff --git a/qw/source/cl_tent.c b/qw/source/cl_tent.c deleted file mode 100644 index c401defa2..000000000 --- a/qw/source/cl_tent.c +++ /dev/null @@ -1,665 +0,0 @@ -/* - cl_tent.c - - client side temporary entities - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif - -#include -#include - -#include "QF/console.h" -#include "QF/model.h" -#include "QF/msg.h" -#include "QF/sound.h" -#include "QF/sys.h" - -#include "cl_ents.h" -#include "cl_main.h" -#include "cl_parse.h" -#include "cl_tent.h" -#include "client.h" -#include "compat.h" - -typedef struct tent_s { - struct tent_s *next; - entity_t ent; -} tent_t; - -#define TEMP_BATCH 64 -static tent_t *temp_entities = 0; - -typedef struct { - int entity; - struct model_s *model; - float endtime; - vec3_t start, end; - tent_t *tents; - int seed; -} beam_t; - -#define BEAM_SEED_INTERVAL 72 -#define BEAM_SEED_PRIME 3191 - -typedef struct { - float start; - tent_t *tent; -} explosion_t; - -typedef struct tent_obj_s { - struct tent_obj_s *next; - union { - beam_t beam; - explosion_t ex; - } to; -} tent_obj_t; - -static tent_obj_t *tent_objects; -static tent_obj_t *cl_beams; -static tent_obj_t *cl_explosions; -static tent_t *cl_projectiles; - -static sfx_t *cl_sfx_wizhit; -static sfx_t *cl_sfx_knighthit; -static sfx_t *cl_sfx_tink1; -static sfx_t *cl_sfx_ric1; -static sfx_t *cl_sfx_ric2; -static sfx_t *cl_sfx_ric3; -static sfx_t *cl_sfx_r_exp3; - -static model_t *cl_mod_beam; -static model_t *cl_mod_bolt; -static model_t *cl_mod_bolt2; -static model_t *cl_mod_bolt3; -static model_t *cl_spr_explod; - -static void -CL_TEnts_Precache (int phase) -{ - if (!phase) - return; - cl_sfx_wizhit = S_PrecacheSound ("wizard/hit.wav"); - cl_sfx_knighthit = S_PrecacheSound ("hknight/hit.wav"); - cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav"); - cl_sfx_ric1 = S_PrecacheSound ("weapons/ric1.wav"); - cl_sfx_ric2 = S_PrecacheSound ("weapons/ric2.wav"); - cl_sfx_ric3 = S_PrecacheSound ("weapons/ric3.wav"); - cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav"); - - cl_mod_bolt = Mod_ForName ("progs/bolt.mdl", true); - cl_mod_bolt2 = Mod_ForName ("progs/bolt2.mdl", true); - cl_mod_bolt3 = Mod_ForName ("progs/bolt3.mdl", true); - cl_spr_explod = Mod_ForName ("progs/s_explod.spr", true); - cl_mod_beam = Mod_ForName ("progs/beam.mdl", false); - if (!cl_mod_beam) - cl_mod_beam = cl_mod_bolt; -} - -void -CL_TEnts_Init (void) -{ - QFS_GamedirCallback (CL_TEnts_Precache); - CL_TEnts_Precache (1); -} - -void -CL_Init_Entity (entity_t *ent) -{ - memset (ent, 0, sizeof (*ent)); - - ent->skin = 0; - QuatSet (1.0, 1.0, 1.0, 1.0, ent->colormod); - ent->scale = 1.0; - ent->pose1 = ent->pose2 = -1; -} - -static tent_t * -new_temp_entity (void) -{ - tent_t *tent; - if (!temp_entities) { - int i; - - temp_entities = malloc (TEMP_BATCH * sizeof (tent_t)); - for (i = 0; i < TEMP_BATCH - 1; i++) { - temp_entities[i].next = &temp_entities[i + 1]; - } - temp_entities[i].next = 0; - } - tent = temp_entities; - temp_entities = tent->next; - tent->next = 0; - CL_Init_Entity (&tent->ent); - return tent; -} - -static void -free_temp_entities (tent_t *tents) -{ - tent_t **t = &tents; - - while (*t) - t = &(*t)->next; - *t = temp_entities; - temp_entities = tents; -} - -static tent_obj_t * -new_tent_object (void) -{ - tent_obj_t *tobj; - if (!tent_objects) { - int i; - - tent_objects = malloc (TEMP_BATCH * sizeof (tent_t)); - for (i = 0; i < TEMP_BATCH - 1; i++) - tent_objects[i].next = &tent_objects[i + 1]; - tent_objects[i].next = 0; - } - tobj = tent_objects; - tent_objects = tobj->next; - tobj->next = 0; - return tobj; -} - -static void -free_tent_objects (tent_obj_t *tobjs) -{ - tent_obj_t **t = &tobjs; - - while (*t) - t = &(*t)->next; - *t = tent_objects; - tent_objects = tobjs; -} - -void -CL_ClearTEnts (void) -{ - tent_t *t; - tent_obj_t *to; - - for (to = cl_beams; to; to = to->next) { - for (t = to->to.beam.tents; t; t = t->next) - t->ent.efrag = 0; - free_temp_entities (to->to.beam.tents); - } - free_tent_objects (cl_beams); - cl_beams = 0; - - for (to = cl_explosions; to; to = to->next) { - for (t = to->to.ex.tent; t; t = t->next) - t->ent.efrag = 0; - free_temp_entities (to->to.ex.tent); - } - free_tent_objects (cl_explosions); - cl_explosions = 0; -} - -static inline void -beam_clear (beam_t *b) -{ - if (b->tents) { - tent_t *t; - - for (t = b->tents; t; t = t->next) { - r_funcs->R_RemoveEfrags (&t->ent); - t->ent.efrag = 0; - } - free_temp_entities (b->tents); - b->tents = 0; - } -} - -static inline void -beam_setup (beam_t *b, qboolean transform) -{ - tent_t *tent; - float forward, pitch, yaw, d; - int ent_count; - vec3_t dist, org, ang; - unsigned seed; - - // calculate pitch and yaw - VectorSubtract (b->end, b->start, dist); - - if (dist[1] == 0 && dist[0] == 0) { - yaw = 0; - if (dist[2] > 0) - pitch = 90; - else - pitch = 270; - } else { - yaw = (int) (atan2 (dist[1], dist[0]) * 180 / M_PI); - if (yaw < 0) - yaw += 360; - - forward = sqrt (dist[0] * dist[0] + dist[1] * dist[1]); - pitch = (int) (atan2 (dist[2], forward) * 180 / M_PI); - if (pitch < 0) - pitch += 360; - } - - // add new entities for the lightning - VectorCopy (b->start, org); - d = VectorNormalize (dist); - VectorScale (dist, 30, dist); - ent_count = ceil (d / 30); - d = 0; - - seed = b->seed + ((int) (cl.time * BEAM_SEED_INTERVAL) % - BEAM_SEED_INTERVAL); - - ang[ROLL] = 0; - while (ent_count--) { - tent = new_temp_entity (); - tent->next = b->tents; - b->tents = tent; - - VectorMultAdd (org, d, dist, tent->ent.origin); - d += 1.0; - tent->ent.model = b->model; - ang[PITCH] = pitch; - ang[YAW] = yaw; - if (transform) { - seed = seed * BEAM_SEED_PRIME; - ang[ROLL] = seed % 360; - CL_TransformEntity (&tent->ent, ang, true); - } - VectorCopy (ang, tent->ent.angles); - r_funcs->R_AddEfrags (&tent->ent); - } -} - -static void -CL_ParseBeam (model_t *m) -{ - tent_obj_t *to; - beam_t *b; - int ent; - vec3_t start, end; - - ent = MSG_ReadShort (net_message); - - MSG_ReadCoordV (net_message, start); - MSG_ReadCoordV (net_message, end); - - to = 0; - if (ent) { - for (to = cl_beams; to; to = to->next) - if (to->to.beam.entity == ent) - break; - } - if (!to) { - to = new_tent_object (); - to->next = cl_beams; - cl_beams = to; - to->to.beam.tents = 0; - to->to.beam.entity = ent; - } - b = &to->to.beam; - - beam_clear (b); - b->model = m; - b->endtime = cl.time + 0.2; - b->seed = rand (); - VectorCopy (end, b->end); - if (b->entity != cl.viewentity) { - // this will be done in CL_UpdateBeams - VectorCopy (start, b->start); - beam_setup (b, true); - } -} - -void -CL_ParseTEnt (void) -{ - byte type; - dlight_t *dl; - tent_obj_t *to; - explosion_t *ex; - int colorStart, colorLength; - int cnt = -1; - vec3_t pos; - sfx_t *spike_sound[] = { - cl_sfx_ric3, cl_sfx_ric3, cl_sfx_ric2, cl_sfx_ric1, - }; - - type = MSG_ReadByte (net_message); - switch (type) { - case TE_WIZSPIKE: // spike hitting wall - MSG_ReadCoordV (net_message, pos); - r_funcs->particles->R_WizSpikeEffect (pos); - S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1); - break; - - case TE_KNIGHTSPIKE: // spike hitting wall - MSG_ReadCoordV (net_message, pos); - r_funcs->particles->R_KnightSpikeEffect (pos); - S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1); - break; - - case TE_SPIKE: // spike hitting wall - MSG_ReadCoordV (net_message, pos); - r_funcs->particles->R_SpikeEffect (pos); - { - int i; - sfx_t *sound; - - i = (rand () % 20) - 16; - if (i >= 0) - sound = spike_sound[i]; - else - sound = cl_sfx_tink1; - S_StartSound (-1, 0, sound, pos, 1, 1); - } - break; - - case TE_SUPERSPIKE: // super spike hitting wall - MSG_ReadCoordV (net_message, pos); - r_funcs->particles->R_SuperSpikeEffect (pos); - { - int i; - sfx_t *sound; - - i = (rand () % 20) - 16; - if (i >= 0) - sound = spike_sound[i]; - else - sound = cl_sfx_tink1; - S_StartSound (-1, 0, sound, pos, 1, 1); - } - break; - - case TE_EXPLOSION: // rocket explosion - // particles - MSG_ReadCoordV (net_message, pos); - r_funcs->particles->R_ParticleExplosion (pos); - - // light - dl = r_funcs->R_AllocDlight (0); - if (dl) { - VectorCopy (pos, dl->origin); - dl->radius = 350; - dl->die = cl.time + 0.5; - dl->decay = 300; - QuatSet (0.86, 0.31, 0.24, 0.7, dl->color); - } - - // sound - S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); - - // sprite - to = new_tent_object (); - to->next = cl_explosions; - cl_explosions = to; - ex = &to->to.ex; - ex->tent = new_temp_entity (); - - VectorCopy (pos, ex->tent->ent.origin); - ex->start = cl.time; - //FIXME need better model management - if (!cl_spr_explod->cache.data) - cl_spr_explod = Mod_ForName ("progs/s_explod.spr", true); - ex->tent->ent.model = cl_spr_explod; - CL_TransformEntity (&ex->tent->ent, ex->tent->ent.angles, true); - break; - - case TE_TAREXPLOSION: // tarbaby explosion - MSG_ReadCoordV (net_message, pos); - r_funcs->particles->R_BlobExplosion (pos); - - S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); - break; - - case TE_LIGHTNING1: // lightning bolts - CL_ParseBeam (cl_mod_bolt); - break; - - case TE_LIGHTNING2: // lightning bolts - CL_ParseBeam (cl_mod_bolt2); - break; - - case TE_LIGHTNING3: // lightning bolts - CL_ParseBeam (cl_mod_bolt3); - break; - - // PGM 01/21/97 - case TE_BEAM: // grappling hook beam - CL_ParseBeam (cl_mod_beam); - break; - // PGM 01/21/97 - - case TE_LAVASPLASH: - MSG_ReadCoordV (net_message, pos); - r_funcs->particles->R_LavaSplash (pos); - break; - - case TE_TELEPORT: - MSG_ReadCoordV (net_message, pos); - r_funcs->particles->R_TeleportSplash (pos); - break; - - case TE_EXPLOSION2: // color mapped explosion - MSG_ReadCoordV (net_message, pos); - colorStart = MSG_ReadByte (net_message); - colorLength = MSG_ReadByte (net_message); - S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); - r_funcs->particles->R_ParticleExplosion2 (pos, colorStart, - colorLength); - dl = r_funcs->R_AllocDlight (0); - if (!dl) - break; - VectorCopy (pos, dl->origin); - dl->radius = 350; - dl->die = cl.time + 0.5; - dl->decay = 300; - colorStart = (colorStart + (rand () % colorLength)) * 3; - VectorScale (&r_data->vid->palette[colorStart], 1.0 / 255.0, - dl->color); - dl->color[3] = 0.7; - break; - - case TE_GUNSHOT: // bullet hitting wall - cnt = MSG_ReadByte (net_message) * 20; - MSG_ReadCoordV (net_message, pos); - r_funcs->particles->R_GunshotEffect (pos, cnt); - break; - - case TE_BLOOD: // bullet hitting body - cnt = MSG_ReadByte (net_message) * 20; - MSG_ReadCoordV (net_message, pos); - r_funcs->particles->R_BloodPuffEffect (pos, cnt); - break; - - case TE_LIGHTNINGBLOOD: // lightning hitting body - MSG_ReadCoordV (net_message, pos); - - // light - dl = r_funcs->R_AllocDlight (0); - if (dl) { - VectorCopy (pos, dl->origin); - dl->radius = 150; - dl->die = cl.time + 0.1; - dl->decay = 200; - QuatSet (0.25, 0.40, 0.65, 1, dl->color); - } - - r_funcs->particles->R_LightningBloodEffect (pos); - break; - - default: - Sys_Error ("CL_ParseTEnt: bad type %d", type); - } -} - -static void -CL_UpdateBeams (void) -{ - tent_obj_t **to; - beam_t *b; - unsigned seed; - tent_t *t; - - // update lightning - for (to = &cl_beams; *to; ) { - b = &(*to)->to.beam; - if (!b->endtime) - continue; - if (!b->model || b->endtime < cl.time) { - tent_obj_t *_to; - b->endtime = 0; - beam_clear (b); - _to = *to; - *to = _to->next; - _to->next = tent_objects; - tent_objects = _to; - continue; - } - to = &(*to)->next; - - // if coming from the player, update the start position - if (b->entity == cl.viewentity) { - beam_clear (b); - VectorCopy (cl.simorg, b->start); - beam_setup (b, false); - } - - seed = b->seed + ((int) (cl.time * BEAM_SEED_INTERVAL) % - BEAM_SEED_INTERVAL); - - // add new entities for the lightning - for (t = b->tents; t; t = t->next) { - seed = seed * BEAM_SEED_PRIME; - t->ent.angles[ROLL] = seed % 360; - CL_TransformEntity (&t->ent, t->ent.angles, true); - } - } -} - -static void -CL_UpdateExplosions (void) -{ - int f; - tent_obj_t **to; - explosion_t *ex; - entity_t *ent; - - for (to = &cl_explosions; *to; ) { - ex = &(*to)->to.ex; - ent = &ex->tent->ent; - f = 10 * (cl.time - ex->start); - if (f >= ent->model->numframes) { - tent_obj_t *_to; - r_funcs->R_RemoveEfrags (ent); - ent->efrag = 0; - free_temp_entities (ex->tent); - _to = *to; - *to = _to->next; - _to->next = tent_objects; - tent_objects = _to; - continue; - } - to = &(*to)->next; - - ent->frame = f; - if (!ent->efrag) - r_funcs->R_AddEfrags (ent); - } -} - -void -CL_UpdateTEnts (void) -{ - CL_UpdateBeams (); - CL_UpdateExplosions (); -} - -void -CL_ClearProjectiles (void) -{ - tent_t *tent; - - for (tent = cl_projectiles; tent; tent = tent->next) { - r_funcs->R_RemoveEfrags (&tent->ent); - tent->ent.efrag = 0; - } - free_temp_entities (cl_projectiles); - cl_projectiles = 0; -} - -/* - Nails are passed as efficient temporary entities -*/ -void -CL_ParseProjectiles (qboolean nail2) -{ - tent_t *tent; - tent_t *head = 0, **tail = &head; - byte bits[6]; - int i, c, j, num; - entity_t *pr; - - c = MSG_ReadByte (net_message); - - for (i = 0; i < c; i++) { - if (nail2) - num = MSG_ReadByte (net_message); - else - num = 0; - (void) num; //FIXME - - for (j = 0; j < 6; j++) - bits[j] = MSG_ReadByte (net_message); - - tent = new_temp_entity (); - *tail = tent; - tail = &tent->next; - - pr = &tent->ent; - pr->model = cl.model_precache[cl_spikeindex]; - pr->skin = 0; - pr->origin[0] = ((bits[0] + ((bits[1] & 15) << 8)) << 1) - 4096; - pr->origin[1] = (((bits[1] >> 4) + (bits[2] << 4)) << 1) - 4096; - pr->origin[2] = ((bits[3] + ((bits[4] & 15) << 8)) << 1) - 4096; - pr->angles[0] = (bits[4] >> 4) * (360.0 / 16.0); - pr->angles[1] = bits[5] * (360.0 / 256.0); - pr->angles[2] = 0; - CL_TransformEntity (&tent->ent, tent->ent.angles, true); - - r_funcs->R_AddEfrags (&tent->ent); - } - - *tail = cl_projectiles; - cl_projectiles = head; -} diff --git a/qw/source/cl_view.c b/qw/source/cl_view.c deleted file mode 100644 index 09633388b..000000000 --- a/qw/source/cl_view.c +++ /dev/null @@ -1,842 +0,0 @@ -/* - cl_view.c - - player eye positioning - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "QF/cmd.h" -#include "QF/cvar.h" -#include "QF/msg.h" -#include "QF/screen.h" - -#include "qw/bothdefs.h" -#include "chase.h" -#include "cl_cam.h" -#include "cl_ents.h" -#include "cl_main.h" -#include "client.h" -#include "compat.h" -#include "host.h" -#include "clview.h" - -/* - The view is allowed to move slightly from it's true position for bobbing, - but if it exceeds 8 pixels linear distance (spherical, not box), the list - of entities sent from the server may not include everything in the pvs, - especially when crossing a water boudnary. -*/ - -cvar_t *scr_ofsx; -cvar_t *scr_ofsy; -cvar_t *scr_ofsz; - -cvar_t *cl_rollspeed; -cvar_t *cl_rollangle; - -cvar_t *cl_bob; -cvar_t *cl_bobcycle; -cvar_t *cl_bobup; - -cvar_t *v_centermove; -cvar_t *v_centerspeed; - -cvar_t *v_kicktime; -cvar_t *v_kickroll; -cvar_t *v_kickpitch; - -cvar_t *v_iyaw_cycle; -cvar_t *v_iroll_cycle; -cvar_t *v_ipitch_cycle; -cvar_t *v_iyaw_level; -cvar_t *v_iroll_level; -cvar_t *v_ipitch_level; - -cvar_t *v_idlescale; - -float v_dmg_time, v_dmg_roll, v_dmg_pitch; - -frame_t *view_frame; -player_state_t *view_message; - -cshift_t cshift_empty = { {130, 80, 50}, 0}; -cshift_t cshift_water = { {130, 80, 50}, 128}; -cshift_t cshift_slime = { {0, 25, 5}, 150}; -cshift_t cshift_lava = { {255, 80, 0}, 150}; -cshift_t cshift_bonus = { {215, 186, 60}, 50}; - -#define sqr(x) ((x) * (x)) - -float -V_CalcRoll (const vec3_t angles, const vec3_t velocity) -{ - float side, sign, value; - vec3_t forward, right, up; - - AngleVectors (angles, forward, right, up); - side = DotProduct (velocity, right); - sign = side < 0 ? -1 : 1; - side = fabs (side); - - value = cl_rollangle->value; - - if (side < cl_rollspeed->value) - side = side * value / cl_rollspeed->value; - else - side = value; - - return side * sign; -} - -static float -V_CalcBob (void) -{ - vec_t *velocity = cl.simvel; - float cycle; - static double bobtime; - static float bob; - - if (cl.spectator) - return 0; - - if (cl.onground == -1) - return bob; // just use old value - - bobtime += host_frametime; - cycle = bobtime - (int) (bobtime / cl_bobcycle->value) * - cl_bobcycle->value; - cycle /= cl_bobcycle->value; - if (cycle < cl_bobup->value) - cycle = cycle / cl_bobup->value; - else - cycle = 1 + (cycle - cl_bobup->value) / (1.0 - cl_bobup->value); - - // bob is proportional to velocity in the xy plane - // (don't count Z, or jumping messes it up) - - bob = sqrt (sqr (velocity[0]) + sqr (velocity[1])) * cl_bob->value; - bob = bob * 0.3 + bob * 0.7 * sin (cycle * M_PI); - if (bob > 4) - bob = 4; - else if (bob < -7) - bob = -7; - return bob; -} - -void -V_StartPitchDrift (void) -{ - if (cl.laststop == cl.time) { - return; // something else is keeping it from drifting - } - - if (cl.nodrift || !cl.pitchvel) { - cl.pitchvel = v_centerspeed->value; - cl.nodrift = false; - cl.driftmove = 0; - } -} - -void -V_StopPitchDrift (void) -{ - cl.laststop = cl.time; - cl.nodrift = true; - cl.pitchvel = 0; -} - -/* - V_DriftPitch - - Moves the client pitch angle towards cl.idealpitch sent by the server. - - If the user is adjusting pitch manually, either with lookup/lookdown, - mlook and mouse, or klook and keyboard, pitch drifting is constantly - stopped. - - Drifting is enabled when the center view key is hit, mlook is released - and lookspring is non 0, or when -*/ -static void -V_DriftPitch (void) -{ - float delta, move; - int frameno = (cls.netchan.outgoing_sequence - 1) & UPDATE_MASK; - usercmd_t *cmd = &cl.frames[frameno].cmd; - - if (view_message->onground == -1 || cls.demoplayback) { - cl.driftmove = 0; - cl.pitchvel = 0; - return; - } - - // don't count small mouse motion - if (cl.nodrift) { - if (fabs (cmd->forwardmove) < cl_forwardspeed->value) - cl.driftmove = 0; - else - cl.driftmove += host_frametime; - - if (cl.driftmove > v_centermove->value) { - V_StartPitchDrift (); - } - return; - } - - delta = cl.idealpitch - cl.viewangles[PITCH]; - - if (!delta) { - cl.pitchvel = 0; - return; - } - - move = host_frametime * cl.pitchvel; - cl.pitchvel += host_frametime * v_centerspeed->value; - - if (delta > 0) { - if (move > delta) { - cl.pitchvel = 0; - move = delta; - } - cl.viewangles[PITCH] += move; - } else if (delta < 0) { - if (move > -delta) { - cl.pitchvel = 0; - move = -delta; - } - cl.viewangles[PITCH] -= move; - } -} - -/* PALETTE FLASHES */ - -void -V_ParseDamage (void) -{ - float count, side; - int armor, blood; - vec_t *origin = cl.simorg; - vec_t *angles = cl.simangles; - vec3_t from, forward, right, up; - - armor = MSG_ReadByte (net_message); - blood = MSG_ReadByte (net_message); - MSG_ReadCoordV (net_message, from); - - count = blood * 0.5 + armor * 0.5; - if (count < 10) - count = 10; - - cl.faceanimtime = cl.time + 0.2; // but sbar face into pain frame - - if (cl_cshift_damage->int_val - || (cl.sv_cshifts & INFO_CSHIFT_DAMAGE)) { - cshift_t *cshift = &cl.cshifts[CSHIFT_DAMAGE]; - - cshift->percent += 3 * count; - cshift->percent = - bound (0, cshift->percent, 150); - - if (armor > blood) { - cshift->destcolor[0] = 200; - cshift->destcolor[1] = 100; - cshift->destcolor[2] = 100; - } else if (armor) { - cshift->destcolor[0] = 220; - cshift->destcolor[1] = 50; - cshift->destcolor[2] = 50; - } else { - cshift->destcolor[0] = 255; - cshift->destcolor[1] = 0; - cshift->destcolor[2] = 0; - } - cshift->initialpct = cshift->percent; - cshift->time = cl.time; - } - - // calculate view angle kicks - VectorSubtract (from, origin, from); - VectorNormalize (from); - - AngleVectors (angles, forward, right, up); - - side = DotProduct (from, right); - v_dmg_roll = count * side * v_kickroll->value; - - side = DotProduct (from, forward); - v_dmg_pitch = count * side * v_kickpitch->value; - - v_dmg_time = v_kicktime->value; -} - -static void -V_cshift_f (void) -{ - cshift_empty.destcolor[0] = atoi (Cmd_Argv (1)); - cshift_empty.destcolor[1] = atoi (Cmd_Argv (2)); - cshift_empty.destcolor[2] = atoi (Cmd_Argv (3)); - cshift_empty.percent = atoi (Cmd_Argv (4)); -} - -/* - V_BonusFlash_f - - When you run over an item, the server sends this command -*/ -static void -V_BonusFlash_f (void) -{ - if (!cl_cshift_bonus->int_val - && !(cl.sv_cshifts & INFO_CSHIFT_BONUS)) - return; - - cl.cshifts[CSHIFT_BONUS] = cshift_bonus; - cl.cshifts[CSHIFT_BONUS].initialpct = cl.cshifts[CSHIFT_BONUS].percent; - cl.cshifts[CSHIFT_BONUS].time = cl.time; -} - -/* - V_SetContentsColor - - Underwater, lava, etc each has a color shift -*/ -void -V_SetContentsColor (int contents) -{ - if (!cl_cshift_contents->int_val - && !(cl.sv_cshifts & INFO_CSHIFT_CONTENTS)) { - cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; - return; - } - - switch (contents) { - case CONTENTS_EMPTY: - cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; - break; - case CONTENTS_LAVA: - cl.cshifts[CSHIFT_CONTENTS] = cshift_lava; - break; - case CONTENTS_SOLID: - case CONTENTS_SLIME: - cl.cshifts[CSHIFT_CONTENTS] = cshift_slime; - break; - default: - cl.cshifts[CSHIFT_CONTENTS] = cshift_water; - } -} - -static void -V_CalcPowerupCshift (void) -{ - if (!cl.stats[STAT_ITEMS] & (IT_SUIT || IT_INVISIBILITY || IT_QUAD - || IT_INVULNERABILITY)) - { - cl.cshifts[CSHIFT_POWERUP].percent = 0; - return; - } - - if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY && - cl.stats[STAT_ITEMS] & IT_QUAD) { - cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255; - cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0; - cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255; - cl.cshifts[CSHIFT_POWERUP].percent = 30; - } else if (cl.stats[STAT_ITEMS] & IT_QUAD) { - cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0; - cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0; - cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255; - cl.cshifts[CSHIFT_POWERUP].percent = 30; - } else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) { - cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255; - cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255; - cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0; - cl.cshifts[CSHIFT_POWERUP].percent = 30; - } else if (cl.stats[STAT_ITEMS] & IT_SUIT) { - cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0; - cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255; - cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0; - cl.cshifts[CSHIFT_POWERUP].percent = 20; - } else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) { - cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100; - cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100; - cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100; - cl.cshifts[CSHIFT_POWERUP].percent = 100; - } else { - cl.cshifts[CSHIFT_POWERUP].percent = 0; - } -} - -/* - V_CalcBlend - - LordHavoc made this a real, true alpha blend. Cleaned it up - a bit, but otherwise this is his code. --KB -*/ -void -V_CalcBlend (void) -{ - float a2, a3; - float r = 0, g = 0, b = 0, a = 0; - int i; - - for (i = 0; i < NUM_CSHIFTS; i++) { - a2 = cl.cshifts[i].percent / 255.0; - - if (!a2) - continue; - - a2 = min (a2, 1.0); - r += (cl.cshifts[i].destcolor[0] - r) * a2; - g += (cl.cshifts[i].destcolor[1] - g) * a2; - b += (cl.cshifts[i].destcolor[2] - b) * a2; - - a3 = (1.0 - a) * (1.0 - a2); - a = 1.0 - a3; - } - - // LordHavoc: saturate color - if (a) { - a2 = 1.0 / a; - r *= a2; - g *= a2; - b *= a2; - } - - r_data->vid->cshift_color[0] = min (r, 255.0) / 255.0; - r_data->vid->cshift_color[1] = min (g, 255.0) / 255.0; - r_data->vid->cshift_color[2] = min (b, 255.0) / 255.0; - r_data->vid->cshift_color[3] = bound (0.0, a, 1.0); -} - -static void -V_DropCShift (cshift_t *cs, float droprate) -{ - if (cs->time < 0) { - cs->percent = 0; - } else { - cs->percent = cs->initialpct - (cl.time - cs->time) * droprate; - if (cs->percent <= 0) { - cs->percent = 0; - cs->time = -1; - } - } -} - -void -V_PrepBlend (void) -{ - int i, j; - - if (cl_cshift_powerup->int_val - || (cl.sv_cshifts & INFO_CSHIFT_POWERUP)) - V_CalcPowerupCshift (); - - r_data->vid->cshift_changed = false; - - for (i = 0; i < NUM_CSHIFTS; i++) { - if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent) { - r_data->vid->cshift_changed = true; - cl.prev_cshifts[i].percent = cl.cshifts[i].percent; - } - for (j = 0; j < 3; j++) { - if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j]) - { - r_data->vid->cshift_changed = true; - cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j]; - } - } - } - - // drop the damage value - V_DropCShift (&cl.cshifts[CSHIFT_DAMAGE], 150); - // drop the bonus value - V_DropCShift (&cl.cshifts[CSHIFT_BONUS], 100); - - if (!r_data->vid->cshift_changed && !r_data->vid->recalc_refdef) - return; - - V_CalcBlend (); -} - -/* VIEW RENDERING */ - -static float -angledelta (float a) -{ - a = anglemod (a); - if (a > 180) - a -= 360; - return a; -} - -static void -CalcGunAngle (void) -{ - float yaw, pitch, move; - static float oldpitch = 0, oldyaw = 0; - - yaw = r_data->refdef->viewangles[YAW]; - pitch = -r_data->refdef->viewangles[PITCH]; - - yaw = angledelta (yaw - r_data->refdef->viewangles[YAW]) * 0.4; - yaw = bound (-10, yaw, 10); - pitch = angledelta (-pitch - r_data->refdef->viewangles[PITCH]) * 0.4; - pitch = bound (-10, pitch, 10); - - move = host_frametime * 20; - if (yaw > oldyaw) { - if (oldyaw + move < yaw) - yaw = oldyaw + move; - } else { - if (oldyaw - move > yaw) - yaw = oldyaw - move; - } - - if (pitch > oldpitch) { - if (oldpitch + move < pitch) - pitch = oldpitch + move; - } else { - if (oldpitch - move > pitch) - pitch = oldpitch - move; - } - - oldyaw = yaw; - oldpitch = pitch; - - cl.viewent.angles[YAW] = r_data->refdef->viewangles[YAW] + yaw; - cl.viewent.angles[PITCH] = -(r_data->refdef->viewangles[PITCH] + pitch); -} - -static void -V_BoundOffsets (void) -{ - vec_t *origin = cl.simorg; - - // absolutely bound refresh reletive to entity clipping hull - // so the view can never be inside a solid wall - - if (r_data->refdef->vieworg[0] < origin[0] - 14) - r_data->refdef->vieworg[0] = origin[0] - 14; - else if (r_data->refdef->vieworg[0] > origin[0] + 14) - r_data->refdef->vieworg[0] = origin[0] + 14; - if (r_data->refdef->vieworg[1] < origin[1] - 14) - r_data->refdef->vieworg[1] = origin[1] - 14; - else if (r_data->refdef->vieworg[1] > origin[1] + 14) - r_data->refdef->vieworg[1] = origin[1] + 14; - if (r_data->refdef->vieworg[2] < origin[2] - 22) - r_data->refdef->vieworg[2] = origin[2] - 22; - else if (r_data->refdef->vieworg[2] > origin[2] + 30) - r_data->refdef->vieworg[2] = origin[2] + 30; -} - -/* - V_AddIdle - - Idle swaying -*/ -static void -V_AddIdle (void) -{ - r_data->refdef->viewangles[ROLL] += v_idlescale->value * - sin (cl.time * v_iroll_cycle->value) * v_iroll_level->value; - r_data->refdef->viewangles[PITCH] += v_idlescale->value * - sin (cl.time * v_ipitch_cycle->value) * v_ipitch_level->value; - r_data->refdef->viewangles[YAW] += v_idlescale->value * - sin (cl.time * v_iyaw_cycle->value) * v_iyaw_level->value; - - cl.viewent.angles[ROLL] -= v_idlescale->value * - sin (cl.time * v_iroll_cycle->value) * v_iroll_level->value; - cl.viewent.angles[PITCH] -= v_idlescale->value * - sin (cl.time * v_ipitch_cycle->value) * v_ipitch_level->value; - cl.viewent.angles[YAW] -= v_idlescale->value * - sin (cl.time * v_iyaw_cycle->value) * v_iyaw_level->value; -} - -/* - V_CalcViewRoll - - Roll is induced by movement and damage -*/ -static void -V_CalcViewRoll (void) -{ - float side; - vec_t *angles = cl.simangles; - vec_t *velocity = cl.simvel; - - side = V_CalcRoll (angles, velocity); - r_data->refdef->viewangles[ROLL] += side; - - if (v_dmg_time > 0) { - r_data->refdef->viewangles[ROLL] += - v_dmg_time / v_kicktime->value * v_dmg_roll; - r_data->refdef->viewangles[PITCH] += - v_dmg_time / v_kicktime->value * v_dmg_pitch; - v_dmg_time -= host_frametime; - } - - if (view_message->pls.flags & PF_DEAD) // PF_GIB will also set PF_DEAD - r_data->refdef->viewangles[ROLL] = 80; // dead view angle -} - -static void -V_CalcIntermissionRefdef (void) -{ - entity_t *view; - float old; - vec_t *origin = cl.simorg; - vec_t *angles = cl.simangles; - - // view is the weapon model (visible only from inside body) - view = &cl.viewent; - - VectorCopy (origin, r_data->refdef->vieworg); - VectorCopy (angles, r_data->refdef->viewangles); - view->model = NULL; - - // always idle in intermission - old = v_idlescale->value; - Cvar_SetValue (v_idlescale, 1); - V_AddIdle (); - Cvar_SetValue (v_idlescale, old); -} - -static void -V_CalcRefdef (void) -{ - // view is the weapon model (visible only from inside body) - entity_t *view = &cl.viewent; - float bob; - static float oldz = 0; - int i; - vec3_t forward, right, up; - vec_t *origin = cl.simorg; - vec_t *viewangles = cl.simangles; - - V_DriftPitch (); - - bob = V_CalcBob (); - - // refresh position - VectorCopy (origin, r_data->refdef->vieworg); - r_data->refdef->vieworg[2] += cl.viewheight + bob; - - // never let it sit exactly on a node line, because a water plane can - // disappear when viewed with the eye exactly on it. - // server protocol specifies to only 1/8 pixel, so add 1/16 in each axis - r_data->refdef->vieworg[0] += 1.0 / 16; - r_data->refdef->vieworg[1] += 1.0 / 16; - r_data->refdef->vieworg[2] += 1.0 / 16; - - VectorCopy (viewangles, r_data->refdef->viewangles); - V_CalcViewRoll (); - V_AddIdle (); - - // offsets - AngleVectors (viewangles, forward, right, up); - - // don't allow cheats in multiplayer - // FIXME check for dead - if (cl.maxclients == 1) { - for (i = 0; i < 3; i++) { - r_data->refdef->vieworg[i] += scr_ofsx->value * forward[i] + - scr_ofsy->value * right[i] + - scr_ofsz->value * up[i]; - } - } - - V_BoundOffsets (); - - // set up gun position - VectorCopy (viewangles, view->angles); - - CalcGunAngle (); - - VectorCopy (origin, view->origin); - view->origin[2] += cl.viewheight; - - for (i = 0; i < 3; i++) { - view->origin[i] += forward[i] * bob * 0.4; -// view->origin[i] += right[i] * bob * 0.4; -// view->origin[i] += up[i] * bob * 0.8; - } - view->origin[2] += bob; - - // fudge position around to keep amount of weapon visible - // roughly equal with different FOV - if (hud_sbar->int_val == 0 && r_data->scr_viewsize->int_val >= 100) - ; - else if (r_data->scr_viewsize->int_val == 110) - view->origin[2] += 1; - else if (r_data->scr_viewsize->int_val == 100) - view->origin[2] += 2; - else if (r_data->scr_viewsize->int_val == 90) - view->origin[2] += 1; - else if (r_data->scr_viewsize->int_val == 80) - view->origin[2] += 0.5; - - if (view_message->pls.flags & (PF_GIB | PF_DEAD)) - view->model = NULL; - else - view->model = cl.model_precache[cl.stats[STAT_WEAPON]]; - view->frame = view_message->pls.weaponframe; - view->skin = 0; - - // set up the refresh position - VectorAdd (r_data->refdef->viewangles, cl.punchangle, - r_data->refdef->viewangles); - - // smooth out stair step ups - if ((cl.onground != -1) && (origin[2] - oldz > 0)) { - float steptime; - - steptime = host_frametime; - - oldz += steptime * 80; - if (oldz > origin[2]) - oldz = origin[2]; - if (origin[2] - oldz > 12) - oldz = origin[2] - 12; - r_data->refdef->vieworg[2] += oldz - origin[2]; - view->origin[2] += oldz - origin[2]; - } else - oldz = origin[2]; - - if (cl.chase && chase_active->int_val) - Chase_Update (); - - CL_TransformEntity (view, view->angles, true); -} - -static void -DropPunchAngle (void) -{ - cl.punchangle[PITCH] -= 10 * host_frametime; - if (cl.punchangle[PITCH] < 0) - cl.punchangle[PITCH] = 0; -} - -/* - V_RenderView - - The player's clipping box goes from (-16 -16 -24) to (16 16 32) from - the entity origin, so any view position inside that will be valid -*/ -void -V_RenderView (void) -{ - if (cls.state != ca_active) - return; - - view_frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; - view_message = &view_frame->playerstate[cl.playernum]; - - if (view_message->pls.flags & PF_GIB) - cl.viewheight = 8; // gib view height - else if (view_message->pls.flags & PF_DEAD) - cl.viewheight = -16; // corpse view height - else { - cl.viewheight = DEFAULT_VIEWHEIGHT; // view height - if (cl.stdver) - cl.viewheight = cl.stats[STAT_VIEWHEIGHT]; - } - - DropPunchAngle (); - if (cl.intermission) { // intermission / finale rendering - V_CalcIntermissionRefdef (); - } else { - V_CalcRefdef (); - } - - r_funcs->R_RenderView (); -} - -void -V_Init (void) -{ - Cmd_AddCommand ("bf", V_BonusFlash_f, "Background flash, used when you " - "pick up an item"); - Cmd_AddCommand ("centerview", V_StartPitchDrift, "Centers the player's " - "view ahead after +lookup or +lookdown\n" - "Will not work while mlook is active or freelook is 1."); - Cmd_AddCommand ("v_cshift", V_cshift_f, "This adjusts all of the colors " - "currently being displayed.\n" - "Used when you are underwater, hit, have the Ring of " - "Shadows, or Quad Damage. (v_cshift r g b intensity)"); -} - -void -V_Init_Cvars (void) -{ - v_centermove = Cvar_Get ("v_centermove", "0.15", CVAR_NONE, NULL, - "How far the player must move forward before the " - "view re-centers"); - v_centerspeed = Cvar_Get ("v_centerspeed", "500", CVAR_NONE, NULL, - "How quickly you return to a center view after " - "a lookup or lookdown"); - v_iyaw_cycle = Cvar_Get ("v_iyaw_cycle", "2", CVAR_NONE, NULL, - "How far you tilt right and left when " - "v_idlescale is enabled"); - v_iroll_cycle = Cvar_Get ("v_iroll_cycle", "0.5", CVAR_NONE, NULL, - "How quickly you tilt right and left when " - "v_idlescale is enabled"); - v_ipitch_cycle = Cvar_Get ("v_ipitch_cycle", "1", CVAR_NONE, NULL, - "How quickly you lean forwards and backwards " - "when v_idlescale is enabled"); - v_iyaw_level = Cvar_Get ("v_iyaw_level", "0.3", CVAR_NONE, NULL, - "How far you tilt right and left when " - "v_idlescale is enabled"); - v_iroll_level = Cvar_Get ("v_iroll_level", "0.1", CVAR_NONE, NULL, - "How far you tilt right and left when " - "v_idlescale is enabled"); - v_ipitch_level = Cvar_Get ("v_ipitch_level", "0.3", CVAR_NONE, NULL, - "How far you lean forwards and backwards when " - "v_idlescale is enabled"); - v_idlescale = Cvar_Get ("v_idlescale", "0", CVAR_NONE, NULL, - "Toggles whether the view remains idle"); - - scr_ofsx = Cvar_Get ("scr_ofsx", "0", CVAR_NONE, NULL, "None"); - scr_ofsy = Cvar_Get ("scr_ofsy", "0", CVAR_NONE, NULL, "None"); - scr_ofsz = Cvar_Get ("scr_ofsz", "0", CVAR_NONE, NULL, "None"); - cl_rollspeed = Cvar_Get ("cl_rollspeed", "200", CVAR_NONE, NULL, - "How quickly you straighten out after strafing"); - cl_rollangle = Cvar_Get ("cl_rollangle", "2.0", CVAR_NONE, NULL, - "How much your screen tilts when strafing"); - cl_bob = Cvar_Get ("cl_bob", "0.02", CVAR_NONE, NULL, - "How much your weapon moves up and down when walking"); - cl_bobcycle = Cvar_Get ("cl_bobcycle", "0.6", CVAR_NONE, NULL, - "How quickly your weapon moves up and down when " - "walking"); - cl_bobup = Cvar_Get ("cl_bobup", "0.5", CVAR_NONE, NULL, - "How long your weapon stays up before cycling when " - "walking"); - v_kicktime = Cvar_Get ("v_kicktime", "0.5", CVAR_NONE, NULL, - "How long the kick from an attack lasts"); - v_kickroll = Cvar_Get ("v_kickroll", "0.6", CVAR_NONE, NULL, - "How much you lean when hit"); - v_kickpitch = Cvar_Get ("v_kickpitch", "0.6", CVAR_NONE, NULL, - "How much you look up when hit"); -} diff --git a/qw/source/crudefile.c b/qw/source/crudefile.c index 58426ad4d..7ef0cad74 100644 --- a/qw/source/crudefile.c +++ b/qw/source/crudefile.c @@ -50,7 +50,8 @@ #include "QF/zone.h" #include "compat.h" -#include "crudefile.h" + +#include "qw/include/crudefile.h" int cf_maxsize; // max combined file size (eg quota) int cf_cursize; // current combined file size @@ -65,7 +66,16 @@ typedef struct cf_file_s { } cf_file_t; cf_file_t *cf_filep; -cvar_t *crudefile_quota; +int crudefile_quota; +static cvar_t crudefile_quota_cvar = { + .name = "crudefile_quota", + .description = + "Maximum space available to the Crude File system, -1 to totally " + "disable file writing", + .default_value = "-1", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &crudefile_quota }, +}; int cf_filepcount; // elements in array int cf_openfiles; // used elements @@ -149,7 +159,7 @@ CF_BuildQuota (void) cf_cursize = 0; while ((i = readdir (dir))) { - cf_cursize += CF_GetFileSize (va ("%s/%s", path->str, i->d_name)); + cf_cursize += CF_GetFileSize (va (0, "%s/%s", path->str, i->d_name)); } closedir (dir); } @@ -163,10 +173,8 @@ void CF_Init (void) { CF_BuildQuota (); - crudefile_quota = Cvar_Get ("crudefile_quota", "-1", CVAR_ROM, NULL, - "Maximum space available to the Crude File " - "system, -1 to totally disable file writing"); - cf_maxsize = crudefile_quota->int_val; + Cvar_Register (&crudefile_quota_cvar, 0, 0); + cf_maxsize = crudefile_quota; } /* @@ -181,7 +189,7 @@ CF_CloseAllFiles () for (i = 0; i < cf_filepcount; i++) if (cf_filep[i].file) { - Sys_MaskPrintf (SYS_DEV, "Warning: closing Crude File %d left " + Sys_MaskPrintf (SYS_dev, "Warning: closing Crude File %d left " "over from last map\n", i); CF_Close (i); } diff --git a/qw/source/game.c b/qw/source/game.c index ac69d83ea..fdd9842a2 100644 --- a/qw/source/game.c +++ b/qw/source/game.c @@ -43,10 +43,18 @@ #include "QF/quakefs.h" #include "QF/sys.h" -#include "game.h" -#include "server.h" +#include "qw/include/game.h" +#include "qw/include/server.h" -cvar_t *registered; +float registered; +static cvar_t registered_cvar = { + .name = "registered", + .description = + "Is the game the registered version. 1 yes 0 no", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = ®istered }, +}; int static_registered = 1; // only for startup check, then set /* @@ -71,7 +79,7 @@ Game_CheckRegistered (void) } if (static_registered) { - Cvar_Set (registered, "1"); + Cvar_Set ("registered", "1"); Sys_Printf ("Playing registered version.\n"); } } @@ -110,8 +118,7 @@ SV_Gamedir_f (void) void Game_Init (void) { - registered = Cvar_Get ("registered", "0", CVAR_NONE, NULL, - "Is the game the registered version. 1 yes 0 no"); + Cvar_Register (®istered_cvar, 0, 0); Game_CheckRegistered (); Cmd_AddCommand ("gamedir", SV_Gamedir_f, "Specifies the directory to be used while playing."); diff --git a/qw/source/map_cfg.c b/qw/source/map_cfg.c index 10b4a5cd5..296bad56b 100644 --- a/qw/source/map_cfg.c +++ b/qw/source/map_cfg.c @@ -44,7 +44,7 @@ #include "QF/idparse.h" #include "QF/quakefs.h" -#include "map_cfg.h" +#include "qw/include/map_cfg.h" void map_cfg (const char *mapname, int all) diff --git a/qw/source/master.c b/qw/source/master.c index 91347ff0a..f682f4467 100644 --- a/qw/source/master.c +++ b/qw/source/master.c @@ -76,7 +76,7 @@ # endif #endif -static void __attribute__ ((format (printf, 1, 2))) +static void __attribute__ ((format (PRINTF, 1, 2))) ma_log (const char *fmt, ...); #ifdef HAVE_IN_PKTINFO @@ -161,12 +161,12 @@ typedef struct sockaddr_in msghdr_t; typedef struct { struct sockaddr_in addr; time_t updated; - qboolean notimeout; + bool notimeout; } server_t; static int QW_AddHeartbeat (server_t **servers_p, int slen, - struct sockaddr_in *addr, const char *buf, qboolean notimeout) + struct sockaddr_in *addr, const char *buf, bool notimeout) { server_t *servers = *servers_p; int freeslot = -1; @@ -288,12 +288,14 @@ QW_SendHearts (int sock, msghdr_t *msghdr, server_t *servers, int serverlen) for (i = 0; i < serverlen; i++) { if (servers[i].updated != 0) { unsigned char *p = out + (cpos * 6); - p[0] = ((unsigned char *) &servers[i].addr.sin_addr.s_addr)[0]; - p[1] = ((unsigned char *) &servers[i].addr.sin_addr.s_addr)[1]; - p[2] = ((unsigned char *) &servers[i].addr.sin_addr.s_addr)[2]; - p[3] = ((unsigned char *) &servers[i].addr.sin_addr.s_addr)[3]; - p[4] = (unsigned char) (ntohs (servers[i].addr.sin_port) >> 8); - p[5] = (unsigned char) (ntohs (servers[i].addr.sin_port) & 0xFF); + in_addr_t addr = ntohl (servers[i].addr.sin_addr.s_addr); + in_port_t port = ntohs (servers[i].addr.sin_port); + p[0] = addr >> 24; + p[1] = addr >> 16; + p[2] = addr >> 8; + p[3] = addr >> 0; + p[4] = port >> 8; + p[5] = port >> 0; ++cpos; } } @@ -514,7 +516,7 @@ main (int argc, char **argv) return 0; } -static void __attribute__ ((format (printf, 1, 2))) +static void __attribute__ ((format (PRINTF, 1, 2))) ma_log (const char *fmt, ...) { va_list args; diff --git a/qw/source/net_packetlog.c b/qw/source/net_packetlog.c index 87edf8e45..4ec36d0df 100644 --- a/qw/source/net_packetlog.c +++ b/qw/source/net_packetlog.c @@ -54,12 +54,29 @@ #include "QF/va.h" #include "compat.h" + #include "netchan.h" #include "qw/protocol.h" -#include "server.h" +#include "qw/include/server.h" -cvar_t *net_packetlog; -cvar_t *net_loglevel; +int net_packetlog; +static cvar_t net_packetlog_cvar = { + .name = "net_packetlog", + .description = + "enable/disable packet logging", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &net_packetlog }, +}; +int net_loglevel; +static cvar_t net_loglevel_cvar = { + .name = "net_loglevel", + .description = + "Packet logging/parsing", + .default_value = "2", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &net_loglevel }, +}; // note: this is SUPPOSED to be duplicate, like many others const char *svc_string[] = { @@ -82,6 +99,8 @@ const char *svc_string[] = { "svc_updatename", // [byte] [string] "svc_updatefrags", // [byte] [short] "svc_clientdata", // + + //0x10 "svc_stopsound", // "svc_updatecolors", // [byte] [byte] "svc_particle", // [vec3] @@ -99,6 +118,8 @@ const char *svc_string[] = { "svc_spawnstaticsound", "svc_intermission", "svc_finale", // [string] music [string] text + + //0x20 "svc_cdtrack", // [byte] track [byte] looptrack "svc_sellscreen", "svc_smallkick", // Quake svc_cutscene @@ -115,6 +136,8 @@ const char *svc_string[] = { "svc_modellist", "svc_soundlist", "svc_packetentities", + + //0x30 "svc_deltapacketentities", "svc_maxspeed", "svc_entgravity", @@ -131,9 +154,7 @@ const char *svc_string[] = { "NEW PROTOCOL", "NEW PROTOCOL", "NEW PROTOCOL", - "NEW PROTOCOL", - "NEW PROTOCOL", - "NEW PROTOCOL" + }; const char *clc_string[] = { @@ -165,6 +186,7 @@ static QFile *Net_PacketLog; static const char **Net_sound_precache; static sizebuf_t _packet; static qmsg_t packet = {0, 0, &_packet}; +static int is_server = 0; static int Net_LogStart (const char *fname) @@ -177,7 +199,7 @@ Net_LogStart (const char *fname) } void -Net_LogStop (void) +Net_LogStop (void *data) { if (Net_PacketLog) Qclose (Net_PacketLog); @@ -223,48 +245,48 @@ ascii_dump_buf (unsigned char *buf, int len) } */ void -Log_Incoming_Packet (const byte *p, int len, int has_sequence, int is_server) +Log_Incoming_Packet (const byte *p, int len, int has_sequence) { - if (!net_loglevel->int_val) + if (!net_loglevel) return; if (is_server) { Net_LogPrintf ("\n<<<<<<<<<<<<<<<<<<<<< client to server %d bytes: " "<<<<<<<<<<<<<<<<<<<<<<<<\n", len); - if (net_loglevel->int_val != 3) + if (net_loglevel != 3) hex_dump_buf ((unsigned char *) p, len); - if (net_loglevel->int_val > 1) + if (net_loglevel > 1) Analyze_Client_Packet (p, len, has_sequence); } else { Net_LogPrintf ("\n>>>>>>>>>>>>>>>>>>>>> server to client %d bytes: " ">>>>>>>>>>>>>>>>>>>>>>>>\n", len); - if (net_loglevel->int_val != 3) + if (net_loglevel != 3) hex_dump_buf ((unsigned char *) p, len); - if (net_loglevel->int_val > 1) + if (net_loglevel > 1) Analyze_Server_Packet (p, len, has_sequence); } return; } void -Log_Outgoing_Packet (const byte *p, int len, int has_sequence, int is_server) +Log_Outgoing_Packet (const byte *p, int len, int has_sequence) { - if (!net_loglevel->int_val) + if (!net_loglevel) return; if (is_server) { Net_LogPrintf ("\n>>>>>>>>>>>>>>>>>>>>> server to client %d bytes: " ">>>>>>>>>>>>>>>>>>>>>>>>\n", len); - if (net_loglevel->int_val != 3) + if (net_loglevel != 3) hex_dump_buf ((unsigned char *) p, len); - if (net_loglevel->int_val > 1) + if (net_loglevel > 1) Analyze_Server_Packet (p, len, has_sequence); } else { Net_LogPrintf ("\n<<<<<<<<<<<<<<<<<<<<< client to server %d bytes: " "<<<<<<<<<<<<<<<<<<<<<<<<\n", len); - if (net_loglevel->int_val != 3) + if (net_loglevel != 3) hex_dump_buf ((unsigned char *) p, len); - if (net_loglevel->int_val > 1) + if (net_loglevel > 1) Analyze_Client_Packet (p, len, has_sequence); } return; @@ -387,7 +409,7 @@ Parse_Server_Packet (int has_sequence) break; Net_LogPrintf ("<%06x> [0x%02x] ", MSG_GetReadCount (&packet), c); - if (c < 53) + if (c < 0x40) Net_LogPrintf ("%s: ", svc_string[c]); if (MSG_GetReadCount (&packet) > packet.message->cursize) @@ -519,6 +541,7 @@ Parse_Server_Packet (int has_sequence) break; case svc_temp_entity: i = MSG_ReadByte (&packet); + Net_LogPrintf (" type %d", i); switch (i) { case 0: case 1: @@ -635,7 +658,7 @@ Parse_Server_Packet (int has_sequence) break; case svc_playerinfo: Net_LogPrintf ("\n\tPlayer: %d", MSG_ReadByte (&packet)); - mask2 = mask1 = MSG_ReadShort (&packet); + mask1 = MSG_ReadShort (&packet); Net_LogPrintf (" Mask1: %d", mask1); #if 1 Net_LogPrintf (" Origin:"); @@ -949,12 +972,20 @@ Analyze_Client_Packet (const byte * data, int len, int has_sequence) } static void -Net_PacketLog_f (cvar_t *var) +net_packet_log_f (int length, const void *data, netadr_t to) { - if (var->int_val) { + Log_Outgoing_Packet (data, length, 1); +} + +static void +Net_PacketLog_f (void *data, const cvar_t *cvar) +{ + if (net_packetlog) { Net_LogStart ("qfpacket.log"); + net_log_packet = net_packet_log_f; } else { - Net_LogStop (); + Net_LogStop (0); + net_log_packet = 0; } } @@ -972,14 +1003,14 @@ Net_PacketLog_Zap_f (void) } int -Net_Log_Init (const char **sound_precache) +Net_Log_Init (const char **sound_precache, int server) { + is_server = server; Net_sound_precache = sound_precache; _stdout = Qdopen (1, "wt"); // create a QFile of stdout - net_packetlog = Cvar_Get ("net_packetlog", "0", CVAR_NONE, Net_PacketLog_f, - "enable/disable packet logging"); + Cvar_Register (&net_packetlog_cvar, Net_PacketLog_f, 0); // 0 = no logging // 1 = hex dump only @@ -987,8 +1018,7 @@ Net_Log_Init (const char **sound_precache) // 3 = just parse // 4 = parse/hexdump, skip movement/empty messages - net_loglevel = Cvar_Get ("net_loglevel", "2", CVAR_NONE, NULL, - "Packet logging/parsing"); + Cvar_Register (&net_loglevel_cvar, 0, 0); Cmd_AddCommand ("net_packetlog_zap", Net_PacketLog_Zap_f, "clear the packet log file"); diff --git a/qw/source/pmove.c b/qw/source/pmove.c index 31702c2f3..0d208f947 100644 --- a/qw/source/pmove.c +++ b/qw/source/pmove.c @@ -34,11 +34,21 @@ #include "QF/qtypes.h" #include "QF/sys.h" -#include "client.h" #include "compat.h" + +#include "qw/include/client.h" #include "qw/pmove.h" -cvar_t *no_pogo_stick; +int no_pogo_stick; +static cvar_t no_pogo_stick_cvar = { + .name = "no_pogo_stick", + .description = + "disable the ability to pogo stick: 0 pogo allowed, 1 no pogo, 2 pogo " + "but high friction, 3 high friction and no pogo", + .default_value = "0", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_int, .value = &no_pogo_stick }, +}; movevars_t movevars; playermove_t pmove; @@ -62,10 +72,7 @@ Pmove_Init (void) void Pmove_Init_Cvars (void) { - no_pogo_stick = Cvar_Get ("no_pogo_stick", "0", CVAR_SERVERINFO, Cvar_Info, - "disable the ability to pogo stick: 0 pogo " - "allowed, 1 no pogo, 2 pogo but high friction, " - "3 high friction and no pogo"); + Cvar_Register (&no_pogo_stick_cvar, Cvar_Info, &no_pogo_stick); } #define STEPSIZE 18 @@ -636,7 +643,7 @@ JumpButton (void) } if (onground == -1) { - if (no_pogo_stick->int_val & 1) + if (no_pogo_stick & 1) pmove.oldbuttons |= BUTTON_JUMP; // don't jump again until // released return; // in air, so no effect @@ -723,7 +730,7 @@ NudgePosition (void) } } VectorCopy (base, pmove.origin); -// Sys_MaskPrintf (SYS_DEV, "NudgePosition: stuck\n"); +// Sys_MaskPrintf (SYS_dev, "NudgePosition: stuck\n"); } static void @@ -737,7 +744,7 @@ SpectatorMove (void) // friction speed = DotProduct (pmove.velocity, pmove.velocity); if (speed < 1) { - VectorZero (pmove.velocity) + VectorZero (pmove.velocity); } else { speed = sqrt (speed); drop = 0; @@ -819,9 +826,9 @@ PlayerMove (void) // set onground, watertype, and waterlevel PM_CategorizePosition (); - if (((pmove.cmd.buttons & BUTTON_JUMP) || (no_pogo_stick->int_val & 1)) + if (((pmove.cmd.buttons & BUTTON_JUMP) || (no_pogo_stick & 1)) && onground != -1 && pmove.oldonground == -1 // just landed - && (no_pogo_stick->int_val & 2)) { + && (no_pogo_stick & 2)) { float save = movevars.friction; pmove.waterjumptime = 0; diff --git a/qw/source/pmovetst.c b/qw/source/pmovetst.c index da23aef9d..3dba93e1f 100644 --- a/qw/source/pmovetst.c +++ b/qw/source/pmovetst.c @@ -35,17 +35,17 @@ # include #endif -#include "QF/console.h" #include "QF/model.h" #include "QF/qtypes.h" #include "QF/sys.h" #include "compat.h" + #include "qw/pmove.h" #include "world.h" static hull_t box_hull; -static mclipnode_t box_clipnodes[6]; +static dclipnode_t box_clipnodes[6]; static plane_t box_planes[6]; @@ -106,7 +106,7 @@ inline int PM_HullPointContents (hull_t *hull, int num, const vec3_t p) { float d; - mclipnode_t *node; + dclipnode_t *node; plane_t *plane; while (num >= 0) { @@ -131,11 +131,11 @@ PM_PointContents (const vec3_t p) { float d; int num; - mclipnode_t *node; + dclipnode_t *node; hull_t *hull; plane_t *plane; - hull = &pmove.physents[0].model->hulls[0]; + hull = &pmove.physents[0].model->brush.hulls[0]; num = hull->firstclipnode; @@ -161,7 +161,7 @@ PM_PointContents (const vec3_t p) Returns false if the given player position is not valid (in solid) */ -qboolean +bool PM_TestPlayerPosition (const vec3_t pos) { hull_t *hull; @@ -173,7 +173,7 @@ PM_TestPlayerPosition (const vec3_t pos) pe = &pmove.physents[i]; // get the clipping hull if (pe->model) - hull = &pmove.physents[i].model->hulls[1]; + hull = &pmove.physents[i].model->brush.hulls[1]; else { VectorSubtract (pe->mins, player_maxs, mins); VectorSubtract (pe->maxs, player_mins, maxs); @@ -234,7 +234,7 @@ PM_PlayerMove (const vec3_t start, const vec3_t end) } else { check_box = 1; if (pe->model) { - hull = &pe->model->hulls[1]; + hull = &pe->model->brush.hulls[1]; VectorSubtract (pe->model->mins, player_maxs, mins); VectorSubtract (pe->model->maxs, player_mins, maxs); } else { diff --git a/qw/source/sbar.c b/qw/source/sbar.c deleted file mode 100644 index 7234c7f51..000000000 --- a/qw/source/sbar.c +++ /dev/null @@ -1,2073 +0,0 @@ -/* - sbar.c - - Status bar - - Copyright (C) 1996-1997 Id Software, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif - -#include -#include - -#include "QF/cmd.h" -#include "QF/console.h" -#include "QF/cvar.h" -#include "QF/draw.h" -#include "QF/dstring.h" -#include "QF/gib.h" -#include "QF/msg.h" -#include "QF/quakefs.h" -#include "QF/screen.h" -#include "QF/sys.h" -#include "QF/va.h" -#include "QF/vid.h" -#include "QF/view.h" - -#include "QF/plugin/console.h" - -#include "qw/bothdefs.h" -#include "cl_cam.h" -#include "cl_parse.h" -#include "client.h" -#include "compat.h" -#include "sbar.h" - -int sb_updates; // if >= vid.numpages, no update needed - -#define STAT_MINUS 10 // num frame for '-' stats digit - -qpic_t *sb_nums[2][11]; -qpic_t *sb_colon, *sb_slash; -qpic_t *sb_ibar; -qpic_t *sb_sbar; -qpic_t *sb_scorebar; - -qpic_t *sb_weapons[7][8]; // 0 is active, 1 is owned, 2-5 are flashes -qpic_t *sb_ammo[4]; -qpic_t *sb_sigil[4]; -qpic_t *sb_armor[3]; -qpic_t *sb_items[32]; - -qpic_t *sb_faces[7][2]; // 0 is gibbed, 1 is dead, 2-6 are alive - // 0 is static, 1 is temporary animation -qpic_t *sb_face_invis; -qpic_t *sb_face_quad; -qpic_t *sb_face_invuln; -qpic_t *sb_face_invis_invuln; - -qboolean sb_showscores; -qboolean sb_showteamscores; - -int sb_lines; // scan lines to draw -qboolean hudswap; - -static qboolean largegame = false; - -cvar_t *fs_fraglog; -cvar_t *cl_fraglog; -cvar_t *hud_sbar; -cvar_t *hud_swap; -cvar_t *hud_scoreboard_gravity; -cvar_t *hud_scoreboard_uid; -cvar_t *scr_centertime; -cvar_t *scr_printspeed; - -static view_t *sbar_view; -static view_t *sbar_inventory_view; -static view_t *sbar_frags_view; - -static view_t *hud_view; -static view_t *hud_inventory_view; -static view_t *hud_armament_view; -static view_t *hud_frags_view; - -static view_t *overlay_view; -static view_t *stuff_view; -static view_t *main_view; - -static void (*Sbar_Draw_DMO_func) (view_t *view, int l, int y, int skip); - -static void -hud_swap_f (cvar_t *var) -{ - hudswap = var->int_val; - if (var->int_val) { - hud_armament_view->gravity = grav_southwest; - stuff_view->gravity = grav_southeast; - } else { - hud_armament_view->gravity = grav_southeast; - stuff_view->gravity = grav_southwest; - } - view_move (hud_armament_view, hud_armament_view->xpos, - hud_armament_view->ypos); - view_move (stuff_view, stuff_view->xpos, stuff_view->ypos); -} - -static void -hud_scoreboard_gravity_f (cvar_t *var) -{ - grav_t grav; - - if (strequal (var->string, "center")) - grav = grav_center; - else if (strequal (var->string, "northwest")) - grav = grav_northwest; - else if (strequal (var->string, "north")) - grav = grav_north; - else if (strequal (var->string, "northeast")) - grav = grav_northeast; - else if (strequal (var->string, "west")) - grav = grav_west; - else if (strequal (var->string, "east")) - grav = grav_east; - else if (strequal (var->string, "southwest")) - grav = grav_southwest; - else if (strequal (var->string, "south")) - grav = grav_south; - else if (strequal (var->string, "southeast")) - grav = grav_southeast; - else - grav = grav_center; - overlay_view->gravity = grav; - view_move (overlay_view, overlay_view->xpos, overlay_view->ypos); -} - -static void -calc_sb_lines (cvar_t *var) -{ - int stuff_y; - - if (var->int_val >= 120) { - sb_lines = 0; - stuff_y = 0; - } else if (var->int_val >= 110) { - sb_lines = 24; - sbar_inventory_view->visible = 0; - hud_inventory_view->visible = 0; - hud_armament_view->visible = 0; - stuff_y = 32; - } else { - sb_lines = 48; - sbar_inventory_view->visible = 1; - hud_inventory_view->visible = 1; - hud_armament_view->visible = 1; - stuff_y = 48; - } - if (sb_lines) { - sbar_view->visible = 1; - hud_view->visible = 1; - view_resize (sbar_view, sbar_view->xlen, sb_lines); - view_resize (hud_view, hud_view->xlen, sb_lines); - } else { - sbar_view->visible = 0; - hud_view->visible = 0; - } - view_move (stuff_view, stuff_view->xpos, stuff_y); -} - -static void -hud_sbar_f (cvar_t *var) -{ - r_data->vid->recalc_refdef = true; - if (r_data->scr_viewsize) - calc_sb_lines (r_data->scr_viewsize); - r_data->lineadj = var->int_val ? sb_lines : 0; - if (var->int_val) { - view_remove (main_view, main_view->children[0]); - view_insert (main_view, sbar_view, 0); - } else { - view_remove (main_view, main_view->children[0]); - view_insert (main_view, hud_view, 0); - } -} - -static void -viewsize_f (cvar_t *var) -{ - calc_sb_lines (var); - if (hud_sbar) - r_data->lineadj = hud_sbar->int_val ? sb_lines : 0; -} - - -static int -Sbar_ColorForMap (int m) -{ - return (bound (0, m, 13) * 16) + 8; -} - -static void -Sbar_ShowTeamScores (void) -{ - if (sb_showteamscores) - return; - - sb_showteamscores = true; - sb_updates = 0; -} - -static void -Sbar_DontShowTeamScores (void) -{ - sb_showteamscores = false; - sb_updates = 0; -} - -static void -Sbar_ShowScores (void) -{ - if (sb_showscores) - return; - - sb_showscores = true; - sb_updates = 0; -} - -static void -Sbar_DontShowScores (void) -{ - sb_showscores = false; - sb_updates = 0; -} - -void -Sbar_Changed (void) -{ - sb_updates = 0; // update next frame -} - -static void -draw_pic (view_t *view, int x, int y, qpic_t *pic) -{ - r_funcs->Draw_Pic (view->xabs + x, view->yabs + y, pic); -} - -static inline void -draw_cachepic (view_t *view, int x, int y, const char *name, int cent) -{ - qpic_t *pic = r_funcs->Draw_CachePic (name, true); - if (cent) - x += (view->xlen - pic->width) / 2; - r_funcs->Draw_Pic (view->xabs + x, view->yabs + y, pic); -} - -static inline void -draw_subpic (view_t *view, int x, int y, qpic_t *pic, - int srcx, int srcy, int width, int height) -{ - r_funcs->Draw_SubPic (view->xabs + x, view->yabs + y, pic, - srcx, srcy, width, height); -} - -static inline void -draw_transpic (view_t *view, int x, int y, qpic_t *pic) -{ - r_funcs->Draw_Pic (view->xabs + x, view->yabs + y, pic); -} - - -// drawing routines are reletive to the status bar location - -static inline void -draw_character (view_t *view, int x, int y, int c) -{ - r_funcs->Draw_Character (view->xabs + x, view->yabs + y, c); -} - -static inline void -draw_string (view_t *view, int x, int y, const char *str) -{ - r_funcs->Draw_String (view->xabs + x, view->yabs + y, str); -} - -static inline void -draw_altstring (view_t *view, int x, int y, const char *str) -{ - r_funcs->Draw_AltString (view->xabs + x, view->yabs + y, str); -} - -static inline void -draw_nstring (view_t *view, int x, int y, const char *str, int n) -{ - r_funcs->Draw_nString (view->xabs + x, view->yabs + y, str, n); -} - -static inline void -draw_fill (view_t *view, int x, int y, int w, int h, int col) -{ - r_funcs->Draw_Fill (view->xabs + x, view->yabs + y, w, h, col); -} - -static void -draw_num (view_t *view, int x, int y, int num, int digits, int color) -{ - char str[12]; - char *ptr; - int l, frame; - - if (num > 999999999) - num = 999999999; - - l = snprintf (str, sizeof (str), "%d", num); - ptr = str; - if (l > digits) - ptr += (l - digits); - if (l < digits) - x += (digits - l) * 24; - - while (*ptr) { - if (*ptr == '-') - frame = STAT_MINUS; - else - frame = *ptr - '0'; - - draw_transpic (view, x, y, sb_nums[color][frame]); - x += 24; - ptr++; - } -} - -typedef struct { - char team[16 + 1]; - int frags; - int players; - int plow, phigh, ptotal; -} team_t; - -team_t teams[MAX_CLIENTS]; -int teamsort[MAX_CLIENTS]; -int fragsort[MAX_CLIENTS]; // ZOID changed this from [MAX_SCOREBOARD] -int scoreboardlines, scoreboardteams; - -static void -Sbar_SortFrags (qboolean includespec) -{ - int i, j, k; - - // sort by frags - scoreboardlines = 0; - for (i = 0; i < MAX_CLIENTS; i++) { - if (cl.players[i].name && cl.players[i].name->value[0] - && (!cl.players[i].spectator || includespec)) { - fragsort[scoreboardlines] = i; - scoreboardlines++; - if (cl.players[i].spectator) - cl.players[i].frags = -999; - } - } - - for (i = 0; i < scoreboardlines; i++) - for (j = 0; j < scoreboardlines - 1 - i; j++) - if (cl.players[fragsort[j]].frags < - cl.players[fragsort[j + 1]].frags) { - k = fragsort[j]; - fragsort[j] = fragsort[j + 1]; - fragsort[j + 1] = k; - } -} - -static void -Sbar_SortTeams (void) -{ - char t[16 + 1]; - int i, j, k; - player_info_t *s; - - // request new ping times every two second - scoreboardteams = 0; - - if (!cl.teamplay) - return; - - // sort the teams - memset (teams, 0, sizeof (teams)); - for (i = 0; i < MAX_CLIENTS; i++) - teams[i].plow = 999; - - for (i = 0; i < MAX_CLIENTS; i++) { - s = &cl.players[i]; - if (!s->name || !s->name->value[0]) - continue; - if (s->spectator) - continue; - - // find his team in the list - t[16] = 0; - strncpy (t, s->team->value, 16); - if (!t[0]) - continue; // not on team - for (j = 0; j < scoreboardteams; j++) - if (!strcmp (teams[j].team, t)) { - teams[j].frags += s->frags; - teams[j].players++; - goto addpinginfo; - } - if (j == scoreboardteams) { // must add him - j = scoreboardteams++; - strcpy (teams[j].team, t); - teams[j].frags = s->frags; - teams[j].players = 1; - addpinginfo: - if (teams[j].plow > s->ping) - teams[j].plow = s->ping; - if (teams[j].phigh < s->ping) - teams[j].phigh = s->ping; - teams[j].ptotal += s->ping; - } - } - - // sort - for (i = 0; i < scoreboardteams; i++) - teamsort[i] = i; - - // good 'ol bubble sort - for (i = 0; i < scoreboardteams - 1; i++) - for (j = i + 1; j < scoreboardteams; j++) - if (teams[teamsort[i]].frags < teams[teamsort[j]].frags) { - k = teamsort[i]; - teamsort[i] = teamsort[j]; - teamsort[j] = k; - } -} - -static void -draw_solo (view_t *view) -{ - char str[80]; - int minutes, seconds; - - draw_pic (view, 0, 0, sb_scorebar); - - minutes = cl.time / 60; - seconds = cl.time - 60 * minutes; - snprintf (str, sizeof (str), "Time :%3i:%02i", minutes, seconds); - draw_string (view, 184, 4, str); -} - -static inline void -draw_smallnum (view_t *view, int x, int y, int n, int packed, int colored) -{ - char num[4]; - - packed = packed != 0; // ensure 0 or 1 - - if (n > 999) - n = 999; - - snprintf (num, sizeof (num), "%3d", n); - if (colored) { - if (num[0] != ' ') - num[0] = 18 + num[0] - '0'; - if (num[1] != ' ') - num[1] = 18 + num[1] - '0'; - if (num[2] != ' ') - num[2] = 18 + num[2] - '0'; - } - draw_character (view, x + packed, y, num[0]); - draw_character (view, x + 8, y, num[1]); - draw_character (view, x + 16 - packed, y, num[2]); -} - -static void -draw_tile (view_t *view) -{ - r_funcs->Draw_TileClear (view->xabs, view->yabs, view->xlen, view->ylen); -} - -static void -draw_ammo_sbar (view_t *view) -{ - int i, count; - - // ammo counts - for (i = 0; i < 4; i++) { - count = cl.stats[STAT_SHELLS + i]; - draw_smallnum (view, (6 * i + 1) * 8 + 2, 0, count, 0, 1); - } -} - -static void -draw_ammo_hud (view_t *view) -{ - int i, count; - - // ammo counts - for (i = 0; i < 4; i++) { - count = cl.stats[STAT_SHELLS + i]; - draw_subpic (view, 0, i * 11, sb_ibar, 3 + (i * 48), 0, 42, 11); - draw_smallnum (view, 7, i * 11, count, 0, 1); - } -} - -static int -calc_flashon (float time, int mask) -{ - int flashon; - - flashon = (int) ((cl.time - time) * 10); - if (flashon < 0) - flashon = 0; - if (flashon >= 10) { - if (cl.stats[STAT_ACTIVEWEAPON] == mask) - flashon = 1; - else - flashon = 0; - } else - flashon = (flashon % 5) + 2; - return flashon; -} - -static void -draw_weapons_sbar (view_t *view) -{ - int flashon, i; - - for (i = 0; i < 7; i++) { - if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN << i)) { - flashon = calc_flashon (cl.item_gettime[i], IT_SHOTGUN << i); - draw_pic (view, i * 24, 0, sb_weapons[flashon][i]); - if (flashon > 1) - sb_updates = 0; // force update to remove flash - } - } -} - -static void -draw_weapons_hud (view_t *view) -{ - int flashon, i, x = 0; - - if (view->parent->gravity == grav_southeast) - x = view->xlen - 24; - - for (i = r_data->vid->conheight < 204; i < 7; i++) { - if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN << i)) { - flashon = calc_flashon (cl.item_gettime[i], IT_SHOTGUN << i); - draw_subpic (view, x, i * 16, sb_weapons[flashon][i], 0, 0, 24, 16); - if (flashon > 1) - sb_updates = 0; // force update to remove flash - } - } -} - -static void -draw_items (view_t *view) -{ - float time; - int flashon = 0, i; - - for (i = 0; i < 6; i++) { - if (cl.stats[STAT_ITEMS] & (1 << (17 + i))) { - time = cl.item_gettime[17 + i]; - if (time && time > cl.time - 2 && flashon) { // Flash frame - sb_updates = 0; - } else { - draw_pic (view, i * 16, 0, sb_items[i]); - } - if (time && time > cl.time - 2) - sb_updates = 0; - } - } -} - -static void -draw_sigils (view_t *view) -{ - float time; - int flashon = 0, i; - - for (i = 0; i < 4; i++) { - if (cl.stats[STAT_ITEMS] & (1 << (28 + i))) { - time = cl.item_gettime[28 + i]; - if (time && time > cl.time - 2 && flashon) { // flash frame - sb_updates = 0; - } else { - draw_pic (view, i * 8, 0, sb_sigil[i]); - } - if (time && time > cl.time - 2) - sb_updates = 0; - } - } -} - -static void -draw_inventory_sbar (view_t *view) -{ - if (cl.spectator && autocam == CAM_TRACK) { - if (sbar_frags_view) - sbar_frags_view->draw (sbar_frags_view); - return; - } - draw_pic (view, 0, 0, sb_ibar); - view_draw (view); -} - -static inline void -dmo_ping (view_t *view, int x, int y, player_info_t *s) -{ - int p; - - p = s->ping; - if (p < 0 || p > 999) - p = 999; - draw_smallnum (view, x + 8, y, p, 0, 0); -} - -static inline void -dmo_uid (view_t *view, int x, int y, player_info_t *s) -{ - char num[12]; - int p; - - p = s->userid; - snprintf (num, sizeof (num), "%4i", p); - draw_string (view, x, y, num); -} - -static inline void -dmo_pl (view_t *view, int x, int y, player_info_t *s) -{ - int p; - - // draw pl - p = s->pl; - draw_smallnum (view, x, y, p, 0, p > 25); -} - -static inline int -calc_fph (int frags, int total) -{ - int fph; - - if (total != 0) { - fph = (3600 * frags) / total; - - if (fph >= 999) - fph = 999; - else if (fph <= -999) - fph = -999; - } else { - fph = 0; - } - - return fph; -} - -static inline void -dmo_main (view_t *view, int x, int y, player_info_t *s, int is_client) -{ - char num[12]; - int fph, minutes, total, top, bottom, f; - - // get time - if (cl.intermission) - total = cl.completed_time - s->entertime; - else - total = realtime - s->entertime; - minutes = total / 60; - - // get frags - f = s->frags; - - // draw fph - fph = calc_fph (f, total); - snprintf (num, sizeof (num), "%3i", fph); - draw_string (view, x, y, num); - - //draw time - snprintf (num, sizeof (num), "%4i", minutes); - draw_string (view, x + 32, y, num); - - // draw background - top = Sbar_ColorForMap (s->topcolor); - bottom = Sbar_ColorForMap (s->bottomcolor); - if (largegame) - draw_fill (view, x + 72, y + 1, 40, 3, top); - else - draw_fill (view, x + 72, y, 40, 4, top); - draw_fill (view, x + 72, y + 4, 40, 4, bottom); - - // draw frags - if (!is_client) { - snprintf (num, sizeof (num), " %3i ", f); - } else { - snprintf (num, sizeof (num), "\x10%3i\x11", f); - } - draw_nstring (view, x + 72, y, num, 5); -} - -static inline void -dmo_team (view_t *view, int x, int y, player_info_t *s) -{ - draw_nstring (view, x, y, s->team->value, 4); -} - -static inline void -dmo_name (view_t *view, int x, int y, player_info_t *s) -{ - draw_string (view, x, y, s->name->value); -} - -static void -draw_frags (view_t *view) -{ - int i, k, l, p = -1; - int top, bottom; - int x; - player_info_t *s; - - Sbar_SortFrags (false); - - // draw the text - l = scoreboardlines <= 4 ? scoreboardlines : 4; - - x = 0; - - for (i = 0; i < l; i++) { - k = fragsort[i]; - s = &cl.players[k]; - if (!s->name || !s->name->value[0]) - continue; - if (s->spectator) - continue; - - // draw background - top = bound (0, s->topcolor, 13); - bottom = bound (0, s->bottomcolor, 13); - - top = Sbar_ColorForMap (top); - bottom = Sbar_ColorForMap (bottom); - - draw_fill (view, x + 4, 1, 28, 4, top); - draw_fill (view, x + 4, 5, 28, 3, bottom); - - draw_smallnum (view, x + 6, 0, s->frags, 0, 0); - - if (k == cl.playernum) - p = i; - - x += 32; - } - if (p != -1) { - draw_character (view, p * 32, 0, 16); - draw_character (view, p * 32 + 26, 0, 17); - } -} - -static void -draw_face (view_t *view) -{ - int f, anim; - - if ((cl.stats[STAT_ITEMS] & (IT_INVISIBILITY | IT_INVULNERABILITY)) - == (IT_INVISIBILITY | IT_INVULNERABILITY)) { - draw_pic (view, 112, 0, sb_face_invis_invuln); - return; - } - if (cl.stats[STAT_ITEMS] & IT_QUAD) { - draw_pic (view, 112, 0, sb_face_quad); - return; - } - if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) { - draw_pic (view, 112, 0, sb_face_invis); - return; - } - if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) { - draw_pic (view, 112, 0, sb_face_invuln); - return; - } - - if (cl.stats[STAT_HEALTH] >= 100) - f = 4; - else - f = cl.stats[STAT_HEALTH] / 20; - - if (cl.time <= cl.faceanimtime) { - anim = 1; - sb_updates = 0; // make sure the anim gets drawn over - } else - anim = 0; - draw_pic (view, 112, 0, sb_faces[f][anim]); -} - -static void -draw_spectator (view_t *view) -{ - char st[512]; - - if (autocam != CAM_TRACK) { - draw_string (view, 160 - 7 * 8, 4, "SPECTATOR MODE"); - draw_string (view, 160 - 14 * 8 + 4, 12, - "Press [ATTACK] for AutoCamera"); - } else { -// Sbar_DrawString (160-14*8+4,4, "SPECTATOR MODE - TRACK CAMERA"); - snprintf (st, sizeof (st), "Tracking %-.13s, [JUMP] for next", - cl.players[spec_track].name->value); - draw_string (view, 0, -8, st); - } -} - -static void -draw_status_bar (view_t *view) -{ - if (cl.spectator && autocam != CAM_TRACK) - draw_pic (view, 0, 0, sb_scorebar); - else - draw_pic (view, 0, 0, sb_sbar); -} - -static void -draw_status (view_t *view) -{ - if (cl.spectator) { - draw_spectator (view); - if (autocam != CAM_TRACK) - return; - } if (sb_showscores || cl.stats[STAT_HEALTH] <= 0) { - draw_solo (view); - return; - } - // armor - if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) { - draw_num (view, 24, 0, 666, 3, 1); - } else { - draw_num (view, 24, 0, cl.stats[STAT_ARMOR], 3, - cl.stats[STAT_ARMOR] <= 25); - if (cl.stats[STAT_ITEMS] & IT_ARMOR3) - draw_pic (view, 0, 0, sb_armor[2]); - else if (cl.stats[STAT_ITEMS] & IT_ARMOR2) - draw_pic (view, 0, 0, sb_armor[1]); - else if (cl.stats[STAT_ITEMS] & IT_ARMOR1) - draw_pic (view, 0, 0, sb_armor[0]); - } - - // face - draw_face (view); - - // health - draw_num (view, 136, 0, cl.stats[STAT_HEALTH], 3, - cl.stats[STAT_HEALTH] <= 25); - - // ammo icon - if (cl.stats[STAT_ITEMS] & IT_SHELLS) - draw_pic (view, 224, 0, sb_ammo[0]); - else if (cl.stats[STAT_ITEMS] & IT_NAILS) - draw_pic (view, 224, 0, sb_ammo[1]); - else if (cl.stats[STAT_ITEMS] & IT_ROCKETS) - draw_pic (view, 224, 0, sb_ammo[2]); - else if (cl.stats[STAT_ITEMS] & IT_CELLS) - draw_pic (view, 224, 0, sb_ammo[3]); - - draw_num (view, 248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10); -} - -/* - Sbar_DeathmatchOverlay - - ping time frags name -*/ -static void -Sbar_DeathmatchOverlay (view_t *view, int start) -{ - int l, y; - int skip = 10; - - if (r_data->vid->conwidth < 244) // FIXME: magic number, gained through experimentation - return; - - if (largegame) - skip = 8; - - // request new ping times every two second - if (realtime - cl.last_ping_request > 2.0) { - cl.last_ping_request = realtime; - if (!cls.demoplayback) { - MSG_WriteByte (&cls.netchan.message, clc_stringcmd); - SZ_Print (&cls.netchan.message, "pings"); - } - } - - r_data->scr_copyeverything = 1; - r_data->scr_fullupdate = 0; - - if (!start) { - draw_cachepic (view, 0, 0, "gfx/ranking.lmp", 1); - y = 24; - } else - y = start; - - // scores - Sbar_SortFrags (true); - - // draw the text - l = scoreboardlines; - - // func ptr, avoids absurd if testing - Sbar_Draw_DMO_func (view, l, y, skip); - - if (y >= view->ylen - 10) // we ran over the screen size, squish - largegame = true; -} - -/* - Sbar_TeamOverlay - - team frags - added by Zoid -*/ -static void -Sbar_TeamOverlay (view_t *view) -{ - char num[20]; - int pavg, plow, phigh, i, k, x, y; - team_t *tm; - info_key_t *player_team = cl.players[cl.playernum].team; - - if (!cl.teamplay) { // FIXME: if not teamplay, why teamoverlay? - Sbar_DeathmatchOverlay (view, 0); - return; - } - - r_data->scr_copyeverything = 1; - r_data->scr_fullupdate = 0; - - draw_cachepic (view, 0, 0, "gfx/ranking.lmp", 1); - - y = 24; - x = 36; - draw_string (view, x, y, "low/avg/high team total players"); - y += 8; - draw_string (view, x, y, "\x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f " - "\x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1f " - "\x1d\x1e\x1e\x1e\x1e\x1e\x1f"); - y += 8; - - // sort the teams - Sbar_SortTeams (); - - // draw the text - for (i = 0; i < scoreboardteams && y <= view->ylen - 10; i++) { - k = teamsort[i]; - tm = teams + k; - - // draw pings - plow = tm->plow; - if (plow < 0 || plow > 999) - plow = 999; - phigh = tm->phigh; - if (phigh < 0 || phigh > 999) - phigh = 999; - if (!tm->players) - pavg = 999; - else - pavg = tm->ptotal / tm->players; - if (pavg < 0 || pavg > 999) - pavg = 999; - - snprintf (num, sizeof (num), "%3i/%3i/%3i", plow, pavg, phigh); - draw_string (view, x, y, num); - - // draw team - draw_nstring (view, x + 104, y, tm->team, 4); - - // draw total - snprintf (num, sizeof (num), "%5i", tm->frags); - draw_string (view, x + 104 + 40, y, num); - - // draw players - snprintf (num, sizeof (num), "%5i", tm->players); - draw_string (view, x + 104 + 88, y, num); - - if (player_team && strnequal (player_team->value, tm->team, 16)) { - draw_character (view, x + 104 - 8, y, 16); - draw_character (view, x + 104 + 32, y, 17); - } - - y += 8; - } - y += 8; - Sbar_DeathmatchOverlay (view, y); -} - -static void -draw_overlay (view_t *view) -{ - if (cls.state != ca_active - || !((cl.stats[STAT_HEALTH] <= 0 && !cl.spectator) - || sb_showscores || sb_showteamscores)) - return; - // main screen deathmatch rankings - // if we're dead show team scores in team games - if (cl.stats[STAT_HEALTH] <= 0 && !cl.spectator) - if (cl.teamplay > 0 && !sb_showscores) - Sbar_TeamOverlay (view); - else - Sbar_DeathmatchOverlay (view, 0); - else if (sb_showscores) - Sbar_DeathmatchOverlay (view, 0); - else if (sb_showteamscores) - Sbar_TeamOverlay (view); -} - -static void -sbar_update_vis (void) -{ - qboolean headsup; - - if (r_data->scr_copyeverything) - Sbar_Changed (); - - sbar_view->visible = 0; - - headsup = !(hud_sbar->int_val || r_data->scr_viewsize->int_val < 100); - - if ((sb_updates >= r_data->vid->numpages) && !headsup) - return; - - if (con_module - && con_module->data->console->lines == r_data->vid->conheight) - return; // console is full screen - - if (!sb_lines) - return; - - sbar_view->visible = 1; - - r_data->scr_copyeverything = 1; - sb_updates++; - - if (sb_showscores || sb_showteamscores || cl.stats[STAT_HEALTH] <= 0) - sb_updates = 0; -} - -void -Sbar_Draw (void) -{ - sbar_update_vis (); - main_view->draw (main_view); -} - -/* - Sbar_LogFrags - - autologging of frags after a match ended - (called by recived network packet with command scv_intermission) - TODO: Find a new and better place for this function - (i am nearly shure this is wrong place) - added by Elmex -*/ -void -Sbar_LogFrags (void) -{ - char *name; - char *team; - byte *cp = NULL; - QFile *file = NULL; - int minutes, fph, total, d, f, i, k, l, p; - player_info_t *s = NULL; - const char *t = NULL; - time_t tt = time (NULL); - - if (!cl_fraglog->int_val) - return; - - if ((file = QFS_Open (fs_fraglog->string, "a")) == NULL) - return; - - t = ctime (&tt); - if (t) - Qwrite (file, t, strlen (t)); - - Qprintf (file, "%s\n%s %s\n", cls.servername->str, cl.worldmodel->name, - cl.levelname); - - // scores - Sbar_SortFrags (true); - - // draw the text - l = scoreboardlines; - - if (cl.teamplay) { - // TODO: test if the teamplay does correct output - Qwrite (file, "pl fph time frags team name\n", - strlen ("pl fph time frags team name\n")); - } else { - Qwrite (file, "pl fph time frags name\n", - strlen ("pl fph time frags name\n")); - } - - for (i = 0; i < l; i++) { - k = fragsort[i]; - s = &cl.players[k]; - if (!s->name || !s->name->value[0]) - continue; - - // draw pl - p = s->pl; - (void) p; //FIXME - - // get time - if (cl.intermission) - total = cl.completed_time - s->entertime; - else - total = realtime - s->entertime; - minutes = total / 60; - - // get frags - f = s->frags; - - fph = calc_fph (f, total); - - name = malloc (strlen (s->name->value) + 1); - for (cp = (byte *) s->name->value, d = 0; *cp; cp++, d++) - name[d] = sys_char_map[*cp]; - name[d] = 0; - - if (s->spectator) { - Qprintf (file, "%-3i%% %s (spectator)", s->pl, name); - } else { - if (cl.teamplay) { - team = malloc (strlen (s->team->value) + 1); - for (cp = (byte *) s->team->value, d = 0; *cp; cp++, d++) - team[d] = sys_char_map[*cp]; - team[d] = 0; - - Qprintf (file, "%-3i%% %-3i %-4i %-3i %-4s %s", - s->pl, fph, minutes, f, team, name); - free (team); - } else { - Qprintf (file, "%-3i%% %-3i %-4i %-3i %s", - s->pl, fph, minutes, f, name); - } - } - free (name); - Qwrite (file, "\n\n", 1); - } - - Qclose (file); -} - -static void -Sbar_Draw_DMO_Team_Ping (view_t *view, int l, int y, int skip) -{ - int i, k, x; - player_info_t *s; - - x = 4; -// 0 40 64 104 152 192 224 - draw_string (view, x, y, "ping pl fph time frags team name"); - y += 8; - draw_string (view, x, y, "\x1d\x1e\x1e\x1f \x1d\x1f \x1d\x1e\x1f " - "\x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1f \x1d\x1e\x1e\x1f " - "\x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f"); - y += 8; - - for (i = 0; i < l && y <= view->ylen - 10; i++) { - k = fragsort[i]; - s = &cl.players[k]; - if (!s->name || !s->name->value[0]) - continue; - - dmo_ping (view, x + 0, y, s); - dmo_pl (view, x + 32, y, s); - - if (s->spectator) { - draw_string (view, x + 72, y, "(spectator)"); - dmo_name (view, x + 224, y, s); - y += skip; - continue; - } - - dmo_main (view, x + 64, y, s, k == cl.playernum); - dmo_team (view, x + 184, y, s); - dmo_name (view, x + 224, y, s); - - y += skip; - } -} - -static void -Sbar_Draw_DMO_Team_UID (view_t *view, int l, int y, int skip) -{ - int i, k, x; - player_info_t *s; - - x = 4; -// 0 40 64 104 152 192 - draw_string (view, x, y, " uid pl fph time frags team name"); - y += 8; - draw_string (view, x, y, "\x1d\x1e\x1e\x1f \x1d\x1f \x1d\x1e\x1f " - "\x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1f \x1d\x1e\x1e\x1f " - "\x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f"); - y += 8; - - for (i = 0; i < l && y <= view->ylen - 10; i++) { - k = fragsort[i]; - s = &cl.players[k]; - if (!s->name || !s->name->value[0]) - continue; - - dmo_uid (view, x + 0, y, s); - dmo_ping (view, x + 32, y, s); - - if (s->spectator) { - draw_string (view, x + 72, y, "(spectator)"); - dmo_name (view, x + 224, y, s); - y += skip; - continue; - } - - dmo_main (view, x + 64, y, s, k == cl.playernum); - dmo_team (view, x + 184, y, s); - dmo_name (view, x + 224, y, s); - - y += skip; - } -} - -static void -Sbar_Draw_DMO_Team_Ping_UID (view_t *view, int l, int y, int skip) -{ - int i, k, x; - player_info_t *s; - - x = 4; -// 0 40 64 104 152 192 - draw_string (view, x, y, "ping pl fph time frags team uid name"); - y += 8; - draw_string (view, x, y, "\x1d\x1e\x1e\x1f \x1d\x1f \x1d\x1e\x1f " - "\x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1f " - "\x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1f " - "\x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f"); - y += 8; - - for (i = 0; i < l && y <= view->ylen - 10; i++) { - k = fragsort[i]; - s = &cl.players[k]; - if (!s->name || !s->name->value[0]) - continue; - - dmo_ping (view, x + 0, y, s); - dmo_pl (view, x + 32, y, s); - - if (s->spectator) { - draw_string (view, x + 72, y, "(spectator)"); - dmo_uid (view, x + 224, y, s); - dmo_name (view, x + 264, y, s); - y += skip; - continue; - } - - dmo_main (view, x + 64, y, s, k == cl.playernum); - dmo_team (view, x + 184, y, s); - dmo_uid (view, x + 224, y, s); - dmo_name (view, x + 264, y, s); - - y += skip; - } -} - -static void -Sbar_Draw_DMO_Ping (view_t *view, int l, int y, int skip) -{ - int i, k, x; - player_info_t *s; - - x = 16; -// 0 40 64 104 152 - draw_string (view, x, y, "ping pl fph time frags name"); - y += 8; - draw_string (view, x, y, "\x1d\x1e\x1e\x1f \x1d\x1f \x1d\x1e\x1f " - "\x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1f " - "\x1d\x1e\x1e\x1e\x1e\x1e\x1e" - "\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f"); - y += 8; - - for (i = 0; i < l && y <= view->ylen - 10; i++) { - k = fragsort[i]; - s = &cl.players[k]; - if (!s->name || !s->name->value[0]) - continue; - - dmo_ping (view, x + 0, y, s); - dmo_pl (view, x + 32, y, s); - - if (s->spectator) { - draw_string (view, x + 72, y, "(spectator)"); - dmo_name (view, x + 184, y, s); - y += skip; - continue; - } - - dmo_main (view, x + 64, y, s, k == cl.playernum); - dmo_name (view, x + 184, y, s); - - y += skip; - } -} - -static void -Sbar_Draw_DMO_UID (view_t *view, int l, int y, int skip) -{ - int i, k, x; - player_info_t *s; - - x = 16; - draw_string (view, x, y, " uid pl fph time frags name"); - y += 8; - draw_string (view, x, y, "\x1d\x1e\x1e\x1f \x1d\x1f \x1d\x1e\x1f " - "\x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1f " - "\x1d\x1e\x1e\x1e\x1e\x1e\x1e" - "\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f"); - y += 8; - - for (i = 0; i < l && y <= view->ylen - 10; i++) { - k = fragsort[i]; - s = &cl.players[k]; - if (!s->name || !s->name->value[0]) - continue; - - dmo_uid (view, x + 0, y, s); - dmo_pl (view, x + 32, y, s); - - if (s->spectator) { - draw_string (view, x + 72, y, "(spectator)"); - dmo_name (view, x + 184, y, s); - y += skip; - continue; - } - - dmo_main (view, x + 64, y, s, k == cl.playernum); - dmo_name (view, x + 184, y, s); - - y += skip; - } -} - -static void -Sbar_Draw_DMO_Ping_UID (view_t *view, int l, int y, int skip) -{ - int i, k, x; - player_info_t *s; - - x = 16; - draw_string (view, x, y, "ping pl fph time frags uid name"); - y += 8; - draw_string (view, x, y, "\x1d\x1e\x1e\x1f \x1d\x1f \x1d\x1e\x1f " - "\x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1f " - "\x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1e\x1e\x1e" - "\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f"); - y += 8; - - for (i = 0; i < l && y <= view->ylen - 10; i++) { - k = fragsort[i]; - s = &cl.players[k]; - if (!s->name || !s->name->value[0]) - continue; - - dmo_ping (view, x + 0, y, s); - dmo_pl (view, x + 32, y, s); - - if (s->spectator) { - draw_string (view, x + 72, y, "(spectator)"); - dmo_name (view, x + 184, y, s); - y += skip; - continue; - } - - dmo_main (view, x + 64, y, s, k == cl.playernum); - dmo_uid (view, x + 184, y, s); - dmo_name (view, x + 224, y, s); - - y += skip; - } -} - -void -Sbar_DMO_Init_f (cvar_t *var) -{ - // "var" is "hud_scoreboard_uid" - if (!var) { - Sbar_Draw_DMO_func = Sbar_Draw_DMO_Ping; - return; - } - - if (cl.teamplay) - if (var->int_val > 1) - Sbar_Draw_DMO_func = Sbar_Draw_DMO_Team_Ping_UID; - else if (var->int_val > 0) - Sbar_Draw_DMO_func = Sbar_Draw_DMO_Team_UID; - else - Sbar_Draw_DMO_func = Sbar_Draw_DMO_Team_Ping; - else - if (var->int_val > 1) - Sbar_Draw_DMO_func = Sbar_Draw_DMO_Ping_UID; - else if (var->int_val > 0) - Sbar_Draw_DMO_func = Sbar_Draw_DMO_UID; - else - Sbar_Draw_DMO_func = Sbar_Draw_DMO_Ping; -} - -/* - draw_minifrags - - frags name - frags team name - displayed to right of status bar if there's room -*/ -static void -draw_minifrags (view_t *view) -{ - int numlines, top, bottom, f, i, k, x, y; - char num[20]; - player_info_t *s; - - r_data->scr_copyeverything = 1; - r_data->scr_fullupdate = 0; - - // scores - Sbar_SortFrags (false); - - if (!scoreboardlines) - return; // no one there? - - numlines = view->ylen / 8; - if (numlines < 3) - return; // not enough room - - // find us - for (i = 0; i < scoreboardlines; i++) - if (fragsort[i] == cl.playernum) - break; - - if (i == scoreboardlines) // we're not there, we are probably a - // spectator, just display top - i = 0; - else // figure out start - i = i - numlines / 2; - - if (i > scoreboardlines - numlines) - i = scoreboardlines - numlines; - if (i < 0) - i = 0; - - x = 4; - y = 0; - - for (; i < scoreboardlines && y < view->ylen - 8 + 1; i++) { - k = fragsort[i]; - s = &cl.players[k]; - if (!s->name || !s->name->value[0]) - continue; - - // draw ping - top = s->topcolor; - bottom = s->bottomcolor; - top = Sbar_ColorForMap (top); - bottom = Sbar_ColorForMap (bottom); - - draw_fill (view, x + 2, y + 1, 37, 3, top); - draw_fill (view, x + 2, y + 4, 37, 4, bottom); - - // draw number - f = s->frags; - if (k != cl.playernum) { - snprintf (num, sizeof (num), " %3i ", f); - } else { - snprintf (num, sizeof (num), "\x10%3i\x11", f); - } - - draw_nstring (view, x, y, num, 5); - - // team - if (cl.teamplay) { - draw_nstring (view, x + 48, y, s->team->value, 4); - draw_nstring (view, x + 48 + 40, y, s->name->value, 16); - } else - draw_nstring (view, x + 48, y, s->name->value, 16); - y += 8; - } -} - -static void -draw_miniteam (view_t *view) -{ - int i, k, x, y; - char num[12]; - info_key_t *player_team = cl.players[cl.playernum].team; - team_t *tm; - - if (!cl.teamplay) - return; - Sbar_SortTeams (); - - x = 0; - y = 0; - for (i = 0; i < scoreboardteams && y <= view->ylen; i++) { - k = teamsort[i]; - tm = teams + k; - - // draw pings - draw_nstring (view, x, y, tm->team, 4); - // draw total - snprintf (num, sizeof (num), "%5i", tm->frags); - draw_string (view, x + 40, y, num); - - if (player_team && strnequal (player_team->value, tm->team, 16)) { - draw_character (view, x - 8, y, 16); - draw_character (view, x + 32, y, 17); - } - - y += 8; - } -} - -static void -draw_time (view_t *view) -{ - struct tm *local = NULL; - time_t utc = 0; - const char *timefmt = NULL; - char st[80]; - - // Get local time - utc = time (NULL); - local = localtime (&utc); - - if (hud_time->int_val == 1) { // Use international format - timefmt = "%k:%M"; - } else if (hud_time->int_val >= 2) { // US AM/PM display - timefmt = "%l:%M %P"; - } - - strftime (st, sizeof (st), timefmt, local); - draw_string (view, 8, 8, st); -} - -static void -draw_fps (view_t *view) -{ - static char st[80]; - double t; - static double lastframetime; - static double lastfps; - - t = Sys_DoubleTime (); - if ((t - lastframetime) >= 0.2) { - lastfps = fps_count / (t - lastframetime); - fps_count = 0; - lastframetime = t; - snprintf (st, sizeof (st), "%5.1f FPS", lastfps); - } - draw_string (view, 80, 8, st); -} - -static void -draw_net (view_t *view) -{ - // request new ping times every two seconds - if (!cls.demoplayback && realtime - cl.last_ping_request > 2) { - cl.last_ping_request = realtime; - MSG_WriteByte (&cls.netchan.message, clc_stringcmd); - SZ_Print (&cls.netchan.message, "pings"); - } - if (hud_ping->int_val) { - int ping = cl.players[cl.playernum].ping; - - ping = bound (0, ping, 999); - draw_string (view, 0, 0, va ("%3d ms ", ping)); - } - - if (hud_ping->int_val && hud_pl->int_val) - draw_character (view, 48, 0, '/'); - - if (hud_pl->int_val) { - int lost = CL_CalcNet (); - - lost = bound (0, lost, 999); - draw_string (view, 56, 0, va ("%3d pl", lost)); - } -} - -static void -draw_stuff (view_t *view) -{ - if (hud_time->int_val > 0) - draw_time (view); - if (hud_fps->int_val > 0) - draw_fps (view); - if (cls.state == ca_active && (hud_ping->int_val || hud_pl->int_val)) - draw_net (view); -} - -void -Sbar_IntermissionOverlay (void) -{ - r_data->scr_copyeverything = 1; - r_data->scr_fullupdate = 0; - - if (cl.teamplay > 0 && !sb_showscores) - Sbar_TeamOverlay (overlay_view); - else - Sbar_DeathmatchOverlay (overlay_view, 0); -} - -/* CENTER PRINTING */ -static dstring_t center_string = {&dstring_default_mem}; -static float centertime_start; // for slow victory printing -static float centertime_off; -static int center_lines; - -/* - Called for important messages that should stay in the center of the screen - for a few moments -*/ -void -Sbar_CenterPrint (const char *str) -{ - if (!str) { - centertime_off = 0; - dstring_clearstr (¢er_string); - return; - } - - dstring_copystr (¢er_string, str); - - centertime_off = scr_centertime->value; - centertime_start = realtime; - - // count the number of lines for centering - center_lines = 1; - while (*str) { - if (*str == '\n') - center_lines++; - str++; - } -} - -static void -Sbar_DrawCenterString (view_t *view, int remaining) -{ - const char *start; - int j, l, x, y; - - start = center_string.str; - - if (center_lines <= 4) - y = view->yabs + view->ylen * 0.35; - else - y = view->yabs + 48; - - do { - // scan the width of the line - for (l = 0; l < 40; l++) - if (start[l] == '\n' || !start[l]) - break; - x = view->xabs + (view->xlen - l * 8) / 2; - for (j = 0; j < l; j++, x += 8) { - r_funcs->Draw_Character (x, y, start[j]); - if (!remaining--) - return; - } - - y += 8; - - while (*start && *start != '\n') - start++; - if (!*start) - break; - start++; // skip the \n - } while (1); -} - -void -Sbar_FinaleOverlay (void) -{ - int remaining; - - r_data->scr_copyeverything = 1; - - draw_cachepic (overlay_view, 0, 16, "gfx/finale.lmp", 1); - // the finale prints the characters one at a time - remaining = scr_printspeed->value * (realtime - centertime_start); - Sbar_DrawCenterString (overlay_view, remaining); -} - -void -Sbar_DrawCenterPrint (void) -{ - r_data->scr_copytop = 1; - - centertime_off -= r_data->frametime; - if (centertime_off <= 0) - return; - - Sbar_DrawCenterString (overlay_view, -1); -} - -static void -init_sbar_views (void) -{ - view_t *view; - view_t *minifrags_view = 0; - view_t *miniteam_view = 0; - - if (r_data->vid->conwidth < 512) { - sbar_view = view_new (0, 0, 320, 48, grav_south); - - sbar_frags_view = view_new (0, 0, 130, 8, grav_northeast); - sbar_frags_view->draw = draw_frags; - } else if (r_data->vid->conwidth < 640) { - sbar_view = view_new (0, 0, 512, 48, grav_south); - minifrags_view = view_new (320, 0, 192, 48, grav_southwest); - minifrags_view->draw = draw_minifrags; - minifrags_view->resize_y = 1; - - view = view_new (0, 0, 192, 48, grav_southeast); - view->draw = draw_tile; - view->resize_y = 1; - view_add (sbar_view, view); - } else { - sbar_view = view_new (0, 0, 640, 48, grav_south); - minifrags_view = view_new (320, 0, 192, 48, grav_southwest); - minifrags_view->draw = draw_minifrags; - minifrags_view->resize_y = 1; - miniteam_view = view_new (0, 0, 96, 48, grav_southeast); - miniteam_view->draw = draw_miniteam; - miniteam_view->resize_y = 1; - - view = view_new (0, 0, 320, 48, grav_southeast); - view->draw = draw_tile; - view->resize_y = 1; - view_add (sbar_view, view); - } - - sbar_inventory_view = view_new (0, 0, 320, 24, grav_northwest); - sbar_inventory_view->draw = draw_inventory_sbar; - - view = view_new (0, 0, 32, 16, grav_southwest); - view->draw = draw_weapons_sbar; - view_add (sbar_inventory_view, view); - - view = view_new (0, 0, 32, 8, grav_northwest); - view->draw = draw_ammo_sbar; - view_add (sbar_inventory_view, view); - - view = view_new (32, 0, 96, 16, grav_southeast); - view->draw = draw_items; - view_add (sbar_inventory_view, view); - - view = view_new (0, 0, 32, 16, grav_southeast); - view->draw = draw_sigils; - view_add (sbar_inventory_view, view); - - if (sbar_frags_view) - view_add (sbar_inventory_view, sbar_frags_view); - - view_add (sbar_view, sbar_inventory_view); - - view = view_new (0, 0, 320, 24, grav_southwest); - view->draw = draw_status_bar; - view_add (sbar_view, view); - - view = view_new (0, 0, 320, 24, grav_southwest); - view->draw = draw_status; - view_add (sbar_view, view); - - if (minifrags_view) - view_add (sbar_view, minifrags_view); - if (miniteam_view) - view_add (sbar_view, miniteam_view); - - if (r_data->vid->conwidth > 640) { - int l = (r_data->vid->conwidth - 640) / 2; - - view = view_new (-l, 0, l, 48, grav_southwest); - view->draw = draw_tile; - view->resize_y = 1; - view_add (sbar_view, view); - - view = view_new (-l, 0, l, 48, grav_southeast); - view->draw = draw_tile; - view->resize_y = 1; - view_add (sbar_view, view); - } -} - -static void -init_hud_views (void) -{ - view_t *view; - view_t *minifrags_view = 0; - view_t *miniteam_view = 0; - - if (r_data->vid->conwidth < 512) { - hud_view = view_new (0, 0, 320, 48, grav_south); - - hud_frags_view = view_new (0, 0, 130, 8, grav_northeast); - hud_frags_view->draw = draw_frags; - } else if (r_data->vid->conwidth < 640) { - hud_view = view_new (0, 0, 512, 48, grav_south); - - minifrags_view = view_new (320, 0, 192, 48, grav_southwest); - minifrags_view->draw = draw_minifrags; - minifrags_view->resize_y = 1; - } else { - hud_view = view_new (0, 0, 640, 48, grav_south); - - minifrags_view = view_new (320, 0, 192, 48, grav_southwest); - minifrags_view->draw = draw_minifrags; - minifrags_view->resize_y = 1; - - miniteam_view = view_new (0, 0, 96, 48, grav_southeast); - miniteam_view->draw = draw_miniteam; - miniteam_view->resize_y = 1; - } - hud_view->resize_y = 1; - - hud_armament_view = view_new (0, 48, 42, 156, grav_southeast); - - view = view_new (0, 0, 42, 44, grav_northeast); - view->draw = draw_weapons_hud; - view_add (hud_armament_view, view); - - view = view_new (0, 0, 42, 44, grav_southeast); - view->draw = draw_ammo_hud; - view_add (hud_armament_view, view); - - hud_inventory_view = view_new (0, 0, 320, 24, grav_northwest); - view_add (hud_view, hud_inventory_view); - - view = view_new (0, 0, 320, 24, grav_southwest); - view->draw = draw_status; - view_add (hud_view, view); - - view = view_new (32, 0, 96, 16, grav_southeast); - view->draw = draw_items; - view_add (hud_inventory_view, view); - - view = view_new (0, 0, 32, 16, grav_southeast); - view->draw = draw_sigils; - view_add (hud_inventory_view, view); - - if (hud_frags_view) - view_add (hud_inventory_view, hud_frags_view); - - if (minifrags_view) - view_add (hud_view, minifrags_view); - if (miniteam_view) - view_add (hud_view, miniteam_view); - - view = view_new (0, 0, r_data->vid->conwidth, 48, grav_south); - view_add (view, hud_view); - hud_view = view; - - view_add (hud_view, hud_armament_view); - - view_insert (main_view, hud_view, 0); -} - -static void -init_views (void) -{ - main_view = view_new (0, 0, r_data->vid->conwidth, r_data->vid->conheight, - grav_northwest); - if (con_module) - view_insert (con_module->data->console->view, main_view, 0); - main_view->resize_x = 1; // get resized if the 2d view resizes - main_view->resize_y = 1; - main_view->visible = 0; // but don't let the console draw our stuff - if (r_data->vid->conheight > 300) - overlay_view = view_new (0, 0, 320, 300, grav_center); - else - overlay_view = view_new (0, 0, 320, r_data->vid->conheight, - grav_center); - overlay_view->draw = draw_overlay; - overlay_view->visible = 0; - - stuff_view = view_new (0, 48, 152, 16, grav_southwest); - stuff_view->draw = draw_stuff; - - view_insert (main_view, overlay_view, 0); - view_insert (main_view, stuff_view, 0); - - init_sbar_views (); - init_hud_views (); -} - -static void -Sbar_GIB_Print_Center_f (void) -{ - if (GIB_Argc () != 2) { - GIB_USAGE ("text"); - } else - Sbar_CenterPrint (GIB_Argv(1)); -} - -static void -sbar_keydest_callback (keydest_t kd) -{ - overlay_view->visible = kd == key_game; -} - -void -Sbar_Init (void) -{ - int i; - - init_views (); - - Key_KeydestCallback (sbar_keydest_callback); - - for (i = 0; i < 10; i++) { - sb_nums[0][i] = r_funcs->Draw_PicFromWad (va ("num_%i", i)); - sb_nums[1][i] = r_funcs->Draw_PicFromWad (va ("anum_%i", i)); - } - - sb_nums[0][10] = r_funcs->Draw_PicFromWad ("num_minus"); - sb_nums[1][10] = r_funcs->Draw_PicFromWad ("anum_minus"); - - sb_colon = r_funcs->Draw_PicFromWad ("num_colon"); - sb_slash = r_funcs->Draw_PicFromWad ("num_slash"); - - sb_weapons[0][0] = r_funcs->Draw_PicFromWad ("inv_shotgun"); - sb_weapons[0][1] = r_funcs->Draw_PicFromWad ("inv_sshotgun"); - sb_weapons[0][2] = r_funcs->Draw_PicFromWad ("inv_nailgun"); - sb_weapons[0][3] = r_funcs->Draw_PicFromWad ("inv_snailgun"); - sb_weapons[0][4] = r_funcs->Draw_PicFromWad ("inv_rlaunch"); - sb_weapons[0][5] = r_funcs->Draw_PicFromWad ("inv_srlaunch"); - sb_weapons[0][6] = r_funcs->Draw_PicFromWad ("inv_lightng"); - - sb_weapons[1][0] = r_funcs->Draw_PicFromWad ("inv2_shotgun"); - sb_weapons[1][1] = r_funcs->Draw_PicFromWad ("inv2_sshotgun"); - sb_weapons[1][2] = r_funcs->Draw_PicFromWad ("inv2_nailgun"); - sb_weapons[1][3] = r_funcs->Draw_PicFromWad ("inv2_snailgun"); - sb_weapons[1][4] = r_funcs->Draw_PicFromWad ("inv2_rlaunch"); - sb_weapons[1][5] = r_funcs->Draw_PicFromWad ("inv2_srlaunch"); - sb_weapons[1][6] = r_funcs->Draw_PicFromWad ("inv2_lightng"); - - for (i = 0; i < 5; i++) { - sb_weapons[2 + i][0] = r_funcs->Draw_PicFromWad (va ("inva%i_shotgun", - i + 1)); - sb_weapons[2 + i][1] = r_funcs->Draw_PicFromWad (va ("inva%i_sshotgun", - i + 1)); - sb_weapons[2 + i][2] = r_funcs->Draw_PicFromWad (va ("inva%i_nailgun", - i + 1)); - sb_weapons[2 + i][3] = r_funcs->Draw_PicFromWad (va ("inva%i_snailgun", - i + 1)); - sb_weapons[2 + i][4] = r_funcs->Draw_PicFromWad (va ("inva%i_rlaunch", - i + 1)); - sb_weapons[2 + i][5] = r_funcs->Draw_PicFromWad (va ("inva%i_srlaunch", - i + 1)); - sb_weapons[2 + i][6] = r_funcs->Draw_PicFromWad (va ("inva%i_lightng", - i + 1)); - } - - sb_ammo[0] = r_funcs->Draw_PicFromWad ("sb_shells"); - sb_ammo[1] = r_funcs->Draw_PicFromWad ("sb_nails"); - sb_ammo[2] = r_funcs->Draw_PicFromWad ("sb_rocket"); - sb_ammo[3] = r_funcs->Draw_PicFromWad ("sb_cells"); - - sb_armor[0] = r_funcs->Draw_PicFromWad ("sb_armor1"); - sb_armor[1] = r_funcs->Draw_PicFromWad ("sb_armor2"); - sb_armor[2] = r_funcs->Draw_PicFromWad ("sb_armor3"); - - sb_items[0] = r_funcs->Draw_PicFromWad ("sb_key1"); - sb_items[1] = r_funcs->Draw_PicFromWad ("sb_key2"); - sb_items[2] = r_funcs->Draw_PicFromWad ("sb_invis"); - sb_items[3] = r_funcs->Draw_PicFromWad ("sb_invuln"); - sb_items[4] = r_funcs->Draw_PicFromWad ("sb_suit"); - sb_items[5] = r_funcs->Draw_PicFromWad ("sb_quad"); - - sb_sigil[0] = r_funcs->Draw_PicFromWad ("sb_sigil1"); - sb_sigil[1] = r_funcs->Draw_PicFromWad ("sb_sigil2"); - sb_sigil[2] = r_funcs->Draw_PicFromWad ("sb_sigil3"); - sb_sigil[3] = r_funcs->Draw_PicFromWad ("sb_sigil4"); - - sb_faces[4][0] = r_funcs->Draw_PicFromWad ("face1"); - sb_faces[4][1] = r_funcs->Draw_PicFromWad ("face_p1"); - sb_faces[3][0] = r_funcs->Draw_PicFromWad ("face2"); - sb_faces[3][1] = r_funcs->Draw_PicFromWad ("face_p2"); - sb_faces[2][0] = r_funcs->Draw_PicFromWad ("face3"); - sb_faces[2][1] = r_funcs->Draw_PicFromWad ("face_p3"); - sb_faces[1][0] = r_funcs->Draw_PicFromWad ("face4"); - sb_faces[1][1] = r_funcs->Draw_PicFromWad ("face_p4"); - sb_faces[0][0] = r_funcs->Draw_PicFromWad ("face5"); - sb_faces[0][1] = r_funcs->Draw_PicFromWad ("face_p5"); - - sb_face_invis = r_funcs->Draw_PicFromWad ("face_invis"); - sb_face_invuln = r_funcs->Draw_PicFromWad ("face_invul2"); - sb_face_invis_invuln = r_funcs->Draw_PicFromWad ("face_inv2"); - sb_face_quad = r_funcs->Draw_PicFromWad ("face_quad"); - - Cmd_AddCommand ("+showscores", Sbar_ShowScores, - "Display information on everyone playing"); - Cmd_AddCommand ("-showscores", Sbar_DontShowScores, - "Stop displaying information on everyone playing"); - Cmd_AddCommand ("+showteamscores", Sbar_ShowTeamScores, - "Display information for your team"); - Cmd_AddCommand ("-showteamscores", Sbar_DontShowTeamScores, - "Stop displaying information for your team"); - - sb_sbar = r_funcs->Draw_PicFromWad ("sbar"); - sb_ibar = r_funcs->Draw_PicFromWad ("ibar"); - sb_scorebar = r_funcs->Draw_PicFromWad ("scorebar"); - - r_data->viewsize_callback = viewsize_f; - - hud_scoreboard_uid = Cvar_Get ("hud_scoreboard_uid", "0", CVAR_NONE, - Sbar_DMO_Init_f, "Set to 1 to show uid " - "instead of ping. Set to 2 to show both."); - fs_fraglog = Cvar_Get ("fs_fraglog", "qw-scores.log", CVAR_ARCHIVE, NULL, - "Filename of the automatic frag-log."); - cl_fraglog = Cvar_Get ("cl_fraglog", "0", CVAR_ARCHIVE, NULL, - "Automatic fraglogging, non-zero value will switch " - "it on."); - hud_sbar = Cvar_Get ("hud_sbar", "0", CVAR_ARCHIVE, hud_sbar_f, - "status bar mode: 0 = hud, 1 = oldstyle"); - hud_swap = Cvar_Get ("hud_swap", "0", CVAR_ARCHIVE, hud_swap_f, - "new HUD on left side?"); - hud_scoreboard_gravity = Cvar_Get ("hud_scoreboard_gravity", "center", - CVAR_ARCHIVE, hud_scoreboard_gravity_f, - "control placement of scoreboard " - "overlay: center, northwest, north, " - "northeast, west, east, southwest, " - "south, southeast"); - scr_centertime = Cvar_Get ("scr_centertime", "2", CVAR_NONE, NULL, "How " - "long in seconds screen hints are displayed"); - scr_printspeed = Cvar_Get ("scr_printspeed", "8", CVAR_NONE, NULL, - "How fast the text is displayed at the end of " - "the single player episodes"); - - // register GIB builtins - GIB_Builtin_Add ("print::center", Sbar_GIB_Print_Center_f); -} diff --git a/qw/source/sv_ccmds.c b/qw/source/sv_ccmds.c index aa359c576..5cefe76d7 100644 --- a/qw/source/sv_ccmds.c +++ b/qw/source/sv_ccmds.c @@ -49,27 +49,36 @@ #include "QF/sys.h" #include "QF/va.h" -#include "qw/bothdefs.h" #include "compat.h" -#include "server.h" -#include "sv_demo.h" -#include "sv_progs.h" -#include "sv_qtv.h" -#include "sv_recorder.h" -qboolean sv_allow_cheats; +#include "qw/bothdefs.h" +#include "qw/include/server.h" +#include "qw/include/sv_demo.h" +#include "qw/include/sv_progs.h" +#include "qw/include/sv_qtv.h" +#include "qw/include/sv_recorder.h" + +bool sv_allow_cheats; int fp_messages = 4, fp_persecond = 4, fp_secondsdead = 10; char fp_msg[255] = { 0 }; -cvar_t *sv_leetnickmatch; +int sv_leetnickmatch; +static cvar_t sv_leetnickmatch_cvar = { + .name = "sv_3133735_7h4n_7h0u", + .description = + "Match '1' as 'i' and such in nicks", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_leetnickmatch }, +}; -static qboolean +static bool match_char (char a, char b) { a = tolower ((byte) sys_char_map[(byte) a]); b = tolower ((byte) sys_char_map[(byte) b]); - if (a == b || (sv_leetnickmatch->int_val + if (a == b || (sv_leetnickmatch && ( (a == '1' && b == 'i') || (a == 'i' && b == '1') || (a == '1' && b == 'l') || (a == 'l' && b == '1') || (a == '3' && b == 'e') || (a == 'e' && b == '3') @@ -223,7 +232,7 @@ SV_Restart_f (void) net_message->message->data); } Sys_Printf ("Shutting down: server restart, shell must relaunch server\n"); - SV_Shutdown (); + SV_Shutdown (0); // Error code 2 on exit, indication shell must restart the server exit (2); } @@ -248,13 +257,12 @@ SV_Fraglogfile_f (void) return; } name = dstring_new (); - if (!QFS_NextFilename (name, - va ("%s/frag_", qfs_gamedir->dir.def), ".log")) { - SV_Printf ("Can't open any logfiles.\n"); - sv_fraglogfile = NULL; + if (!(sv_fraglogfile = QFS_NextFile (name, + va (0, "%s/frag_", + qfs_gamedir->dir.def), ".log"))) { + SV_Printf ("Can't open any logfiles.\n%s\n", name->str); } else { SV_Printf ("Logging frags to %s.\n", name->str); - sv_fraglogfile = QFS_WOpen (name->str, 0); } dstring_delete (name); } @@ -264,7 +272,7 @@ SV_Fraglogfile_f (void) Sets host_client and sv_player to the player with idnum Cmd_Argv (1) */ -static qboolean +static bool SV_SetPlayer (void) { client_t *cl; @@ -389,35 +397,35 @@ SV_Current_Map (void) } static const char * -nice_time (float time) +nice_time (double time) { int t = time + 0.5; #if 0 //FIXME ditch or cvar? if (t < 60) { - return va ("%ds", t); + return va (0, "%ds", t); } else if (t < 600) { - return va ("%dm%02ds", t / 60, t % 60); + return va (0, "%dm%02ds", t / 60, t % 60); } else if (t < 3600) { - return va ("%dm", t / 60); + return va (0, "%dm", t / 60); } else if (t < 36000) { t /= 60; - return va ("%dh%02dm", t / 60, t % 60); + return va (0, "%dh%02dm", t / 60, t % 60); } else if (t < 86400) { - return va ("%dh", t / 3600); + return va (0, "%dh", t / 3600); } else { t /= 3600; - return va ("%dd%02dh", t / 24, t % 24); + return va (0, "%dd%02dh", t / 24, t % 24); } #endif if (t < 60) { - return va ("%ds", t); + return va (0, "%ds", t); } else if (t < 3600) { - return va ("%dm%02ds", t / 60, t % 60); + return va (0, "%dm%02ds", t / 60, t % 60); } else if (t < 86400) { - return va ("%dh%02dm%02ds", t / 3600, (t / 60) % 60, t % 60); + return va (0, "%dh%02dm%02ds", t / 3600, (t / 60) % 60, t % 60); } else { - return va ("%dd%02dh%02dm%02ds", + return va (0, "%dd%02dh%02dm%02ds", t / 86400, (t / 3600) % 24, (t / 60) % 60, t % 60); } } @@ -528,7 +536,8 @@ SV_Status_f (void) pak = (float) svs.stats.latched_packets / STATFRAMES; SV_Printf ("net address : %s\n", NET_AdrToString (net_local_adr)); - SV_Printf ("uptime : %s\n", nice_time (Sys_DoubleTime ())); + SV_Printf ("uptime : %s\n", + nice_time (Sys_DoubleTime () - Sys_DoubleTimeBase ())); SV_Printf ("cpu utilization : %3i%% (%3i%%)\n", (int) cpu, (int)demo); SV_Printf ("avg response time: %i ms\n", (int) avg); SV_Printf ("packets/frame : %5.2f\n", pak); @@ -601,7 +610,7 @@ SV_Status_f (void) SV_Printf ("SERVER %d\n", cl->ping); continue; } - SV_Printf ("%4i %4i %3.1f %4i", + SV_Printf ("%4i %4i %4.1f%% %5i", (int) (1000 * cl->netchan.frame_rate), (int) SV_CalcPing (cl), 100.0 * cl->netchan.drop_count / @@ -622,7 +631,7 @@ SV_Punish (int mode) { int i; double mins = 0.5; - qboolean all = false, done = false; + bool all = false, done = false; client_t *cl = 0; dstring_t *text = dstring_new(); const char *cmd = 0; @@ -741,15 +750,15 @@ SV_Ban_f (void) if (argc > argr) { reason = Cmd_Args (argr); SV_BroadcastPrintf (PRINT_HIGH, "Admin Banned user %s %s: %s\n", - cl->name, mins ? va ("for %.1f minutes", mins) + cl->name, mins ? va (0, "for %.1f minutes", mins) : "permanently", reason); } else { SV_BroadcastPrintf (PRINT_HIGH, "Admin Banned user %s %s\n", - cl->name, mins ? va ("for %.1f minutes", mins) + cl->name, mins ? va (0, "for %.1f minutes", mins) : "permanently"); } SV_DropClient (cl); - Cmd_ExecuteString (va ("addip %s %f", + Cmd_ExecuteString (va (0, "addip %s %f", NET_BaseAdrToString (cl->netchan.remote_address), mins), src_command); } @@ -777,7 +786,7 @@ SV_ConSay (const char *prefix, client_t *client) if (Cmd_Argc () < 2) return; - p = Hunk_TempAlloc (strlen (Cmd_Args (1)) + 1); + p = Hunk_TempAlloc (0, strlen (Cmd_Args (1)) + 1); strcpy (p, Cmd_Args (1)); if (*p == '"') { p++; @@ -811,7 +820,7 @@ SV_ConSay (const char *prefix, client_t *client) dbuf = SVR_WriteBegin (dem_all, 0, strlen (text->str) + 7); MSG_WriteByte (dbuf, svc_print); MSG_WriteByte (dbuf, PRINT_HIGH); - MSG_WriteString (dbuf, va ("%s\n", text->str)); + MSG_WriteString (dbuf, va (0, "%s\n", text->str)); MSG_WriteByte (dbuf, svc_print); MSG_WriteByte (dbuf, PRINT_CHAT); MSG_WriteString (dbuf, ""); @@ -870,13 +879,12 @@ SV_SendServerInfoChange (const char *key, const char *value) } void -Cvar_Info (cvar_t *var) +Cvar_Info (void *data, const cvar_t *cvar) { - if (var->flags & CVAR_SERVERINFO) { - Info_SetValueForKey (svs.info, var->name, var->string, - (!sv_highchars || !sv_highchars->int_val)); - - SV_SendServerInfoChange (var->name, var->string); + if (cvar->flags & CVAR_SERVERINFO) { + const char *cvar_str = Cvar_VarString (cvar); + Info_SetValueForKey (svs.info, cvar->name, cvar_str, !sv_highchars); + SV_SendServerInfoChange (cvar->name, cvar_str); } } @@ -922,9 +930,9 @@ SV_Serverinfo_f (void) // if this is a cvar, change it too var = Cvar_FindVar (key); if (var && (var->flags & CVAR_SERVERINFO)) { - Cvar_Set (var, value); + Cvar_SetVar (var, value); } else { - Info_SetValueForKey (svs.info, key, value, !sv_highchars->int_val); + Info_SetValueForKey (svs.info, key, value, !sv_highchars); SV_SendServerInfoChange (key, value); } } @@ -938,7 +946,7 @@ SV_SetLocalinfo (const char *key, const char *value) oldvalue = strdup (Info_ValueForKey (localinfo, key)); if (*value) - Info_SetValueForKey (localinfo, key, value, !sv_highchars->int_val); + Info_SetValueForKey (localinfo, key, value, !sv_highchars); else Info_RemoveKey (localinfo, key); @@ -950,6 +958,7 @@ SV_SetLocalinfo (const char *key, const char *value) P_STRING (&sv_pr_state, 0) = PR_SetTempString (&sv_pr_state, key); P_STRING (&sv_pr_state, 1) = PR_SetTempString (&sv_pr_state, oldvalue); P_STRING (&sv_pr_state, 2) = PR_SetTempString (&sv_pr_state, value); + sv_pr_state.pr_argc = 3; PR_ExecuteProgram (&sv_pr_state, sv_funcs.LocalinfoChanged); PR_PopFrame (&sv_pr_state); } @@ -1047,7 +1056,7 @@ SV_Gamedir (void) } Info_SetValueForStarKey (svs.info, "*gamedir", dir, - !sv_highchars->int_val); + !sv_highchars); } /* @@ -1133,14 +1142,16 @@ SV_Snap (int uid) if (!cl->uploadfn) cl->uploadfn = dstring_new (); - if (!QFS_NextFilename (cl->uploadfn, - va ("%s/snap/%d-", qfs_gamedir->dir.def, uid), - ".pcx")) { - SV_Printf ("Snap: Couldn't create a file, clean some out.\n"); + if (!(cl->upload = QFS_NextFile (cl->uploadfn, + va (0, "%s/snap/%d-", + qfs_gamedir->dir.def, uid), ".pcx"))) { + SV_Printf ("Snap: Couldn't create a file, clean some out.\n%s\n", + cl->uploadfn->str); dstring_delete (cl->uploadfn); cl->uploadfn = 0; return; } + cl->upload_started = 0; memcpy (&cl->snap_from, &net_from, sizeof (net_from)); if (sv_redirected != RD_NONE) @@ -1300,6 +1311,5 @@ SV_InitOperatorCommands (void) "commands do, so you can check safely"); // poor description - sv_leetnickmatch = Cvar_Get ("sv_3133735_7h4n_7h0u", "1", CVAR_NONE, NULL, - "Match '1' as 'i' and such in nicks"); + Cvar_Register (&sv_leetnickmatch_cvar, 0, 0); } diff --git a/qw/source/sv_demo.c b/qw/source/sv_demo.c index 65b6d397c..2996c149a 100644 --- a/qw/source/sv_demo.c +++ b/qw/source/sv_demo.c @@ -47,15 +47,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "QF/va.h" #include "compat.h" + #include "qw/pmove.h" -#include "server.h" -#include "sv_demo.h" -#include "sv_progs.h" -#include "sv_recorder.h" +#include "qw/include/server.h" +#include "qw/include/sv_demo.h" +#include "qw/include/sv_progs.h" +#include "qw/include/sv_recorder.h" static QFile *demo_file; static byte *demo_mfile; -static qboolean demo_disk; +static bool demo_disk; static dstring_t *demo_name; // filename of mvd static dstring_t *demo_text; // filename of description file static void *demo_dest; @@ -65,26 +66,138 @@ static recorder_t *recorder; static int delta_sequence; #define MIN_DEMO_MEMORY 0x100000 -#define USECACHE (sv_demoUseCache->int_val && svs.demomemsize) +#define USECACHE (sv_demoUseCache && svs.demomemsize) #define DWRITE(a,b,d) dwrite((QFile *) d, a, b) static int demo_max_size; static int demo_size; -cvar_t *sv_demofps; -cvar_t *sv_demoPings; -cvar_t *sv_demoMaxSize; -static cvar_t *sv_demoUseCache; -static cvar_t *sv_demoCacheSize; -static cvar_t *sv_demoMaxDirSize; -static cvar_t *sv_demoDir; -static cvar_t *sv_demoNoVis; -static cvar_t *sv_demoPrefix; -static cvar_t *sv_demoSuffix; -static cvar_t *sv_onrecordfinish; -static cvar_t *sv_ondemoremove; -static cvar_t *sv_demotxt; -static cvar_t *serverdemo; +float sv_demofps; +static cvar_t sv_demofps_cvar = { + .name = "sv_demofps", + .description = + "Minimum frame rate of packets written to the demo", + .default_value = "20", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_demofps }, +}; +float sv_demoPings; +static cvar_t sv_demoPings_cvar = { + .name = "sv_demoPings", + .description = + "Time in seconds between player ping updates. 0 disables.", + .default_value = "3", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_demoPings }, +}; +int sv_demoMaxSize; +static cvar_t sv_demoMaxSize_cvar = { + .name = "sv_demoMaxSize", + .description = + "FIXME not used", + .default_value = "20480", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_demoMaxSize }, +}; +static int sv_demoUseCache; +static cvar_t sv_demoUseCache_cvar = { + .name = "sv_demoUseCache", + .description = + "FIXME not used", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_demoUseCache }, +}; +int sv_demoCacheSize; +static cvar_t sv_demoCacheSize_cvar = { + .name = "sv_demoCacheSize", + .description = + "FIXME not used", + .default_value = 0, + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &sv_demoCacheSize }, +}; +int sv_demoMaxDirSize; +static cvar_t sv_demoMaxDirSize_cvar = { + .name = "sv_demoMaxDirSize", + .description = + "FIXME not used", + .default_value = "102400", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_demoMaxDirSize }, +}; +static char *sv_demoDir; +static cvar_t sv_demoDir_cvar = { + .name = "sv_demoDir", + .description = + "Name of subdirectory in which demos will be saved", + .default_value = "demos", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &sv_demoDir }, +}; +int sv_demoNoVis; +static cvar_t sv_demoNoVis_cvar = { + .name = "sv_demoNoVis", + .description = + "FIXME not used", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_demoNoVis }, +}; +static char *sv_demoPrefix; +static cvar_t sv_demoPrefix_cvar = { + .name = "sv_demoPrefix", + .description = + "Prefix for demo file names", + .default_value = "", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &sv_demoPrefix }, +}; +static char *sv_demoSuffix; +static cvar_t sv_demoSuffix_cvar = { + .name = "sv_demoSuffix", + .description = + "Suffix for demo file names", + .default_value = "", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &sv_demoSuffix }, +}; +static char *sv_onrecordfinish; +static cvar_t sv_onrecordfinish_cvar = { + .name = "sv_onrecordfinish", + .description = + "Command to execute when recording has finished. FIXME not used", + .default_value = "", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &sv_onrecordfinish }, +}; +static char *sv_ondemoremove; +static cvar_t sv_ondemoremove_cvar = { + .name = "sv_ondemoremove", + .description = + "Command to execute when a demo is removed. FIXME not used", + .default_value = "", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &sv_ondemoremove }, +}; +static int sv_demotxt; +static cvar_t sv_demotxt_cvar = { + .name = "sv_demotxt", + .description = + "Write brief description of demo to text file", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_demotxt }, +}; +static char *serverdemo; +static cvar_t serverdemo_cvar = { + .name = "serverdemo", + .description = + "Cvar for passing server demo file name to progs", + .default_value = "", + .flags = CVAR_SERVERINFO, + .value = { .type = 0, .value = &serverdemo }, +}; static int (*dwrite) (QFile * file, const void *buf, int count); @@ -112,10 +225,10 @@ demo_frame (void *unused) { double min_fps; - if (!sv_demofps->value) + if (!sv_demofps) min_fps = 20.0; else - min_fps = sv_demofps->value; + min_fps = sv_demofps; min_fps = max (4, min_fps); if (sv.time - demo_time < 1.0 / min_fps) return 0; @@ -176,13 +289,13 @@ SV_Stop (int reason) break; } /* - if (sv_onrecordfinish->string[0]) { + if (sv_onrecordfinish[0]) { extern redirect_t sv_redirected; int old = sv_redirected; char path[MAX_OSPATH]; char *p; - if ((p = strstr (sv_onrecordfinish->string, " ")) != NULL) + if ((p = strstr (sv_onrecordfinish, " ")) != NULL) *p = 0; // strip parameters strcpy (path, demo_name->str); @@ -190,9 +303,9 @@ SV_Stop (int reason) sv_redirected = RD_NONE; // onrecord script is called always // from the console - Cmd_TokenizeString (va ("script %s \"%s\" \"%s\" \"%s\" %s", - sv_onrecordfinish->string, demo.path->str, - serverdemo->string, + Cmd_TokenizeString (va (0, "script %s \"%s\" \"%s\" \"%s\" %s", + sv_onrecordfinish, demo.path->str, + serverdemo, path, p != NULL ? p + 1 : "")); if (p) *p = ' '; @@ -201,7 +314,7 @@ SV_Stop (int reason) sv_redirected = old; } */ - Cvar_Set (serverdemo, ""); + Cvar_Set ("serverdemo", ""); } static void @@ -221,7 +334,7 @@ SV_Cancel_f (void) SV_Stop (2); } -static qboolean +static bool SV_InitRecord (void) { if (!USECACHE) { @@ -326,7 +439,7 @@ SV_PrintTeams (void) if (numcl == 2) { // duel dsprintf (buffer, "team1 %s\nteam2 %s\n", clients[0]->name, clients[1]->name); - } else if (!teamplay->int_val) { // ffa + } else if (!teamplay) { // ffa dsprintf (buffer, "players:\n"); for (i = 0; i < numcl; i++) dasprintf (buffer, " %s\n", clients[i]->name); @@ -365,12 +478,6 @@ SV_Record (char *name) client_t *player; const char *gamedir, *s; - demo_file = QFS_Open (name, "wb"); - if (!demo_file) { - Sys_Printf ("ERROR: couldn't open %s\n", name); - return; - } - SV_InitRecord (); dstring_copystr (demo_name, name); @@ -378,12 +485,12 @@ SV_Record (char *name) SV_BroadcastPrintf (PRINT_CHAT, "Server started recording (%s):\n%s\n", demo_disk ? "disk" : "memory", QFS_SkipPath (demo_name->str)); - Cvar_Set (serverdemo, demo_name->str); + Cvar_Set ("serverdemo", demo_name->str); dstring_copystr (demo_text, name); strcpy (demo_text->str + strlen (demo_text->str) - 3, "txt"); - if (sv_demotxt->int_val) { + if (sv_demotxt) { QFile *f; f = QFS_Open (demo_text->str, "w+t"); @@ -395,8 +502,8 @@ SV_Record (char *name) strftime (date, sizeof (date), "%Y-%m-%d-%H-%M", localtime (&tim)); Qprintf (f, "date %s\nmap %s\nteamplay %d\ndeathmatch %d\n" "timelimit %d\n%s", - date, sv.name, teamplay->int_val, - deathmatch->int_val, timelimit->int_val, + date, sv.name, teamplay, + deathmatch, timelimit, SV_PrintTeams ()); Qclose (f); } @@ -453,7 +560,7 @@ SV_Record (char *name) // send server info string MSG_WriteByte (&buf, svc_stufftext); - MSG_WriteString (&buf, va ("fullserverinfo \"%s\"\n", + MSG_WriteString (&buf, va (0, "fullserverinfo \"%s\"\n", Info_MakeString (svs.info, 0))); // flush packet @@ -523,7 +630,7 @@ SV_Record (char *name) } MSG_WriteByte (&buf, svc_stufftext); - MSG_WriteString (&buf, va ("cmd spawn %i 0\n", svs.spawncount)); + MSG_WriteString (&buf, va (0, "cmd spawn %i 0\n", svs.spawncount)); if (buf.cursize) { SV_WriteRecordDemoMessage (&buf); @@ -575,7 +682,7 @@ SV_Record (char *name) // get the client to check and download skins // when that is completed, a begin command will be issued MSG_WriteByte (&buf, svc_stufftext); - MSG_WriteString (&buf, va ("skins\n")); + MSG_WriteString (&buf, va (0, "skins\n")); SV_WriteRecordDemoMessage (&buf); @@ -634,14 +741,19 @@ SV_Record_f (void) if (recorder) SV_Stop (0); - dsprintf (name, "%s/%s/%s%s%s", qfs_gamedir->dir.def, sv_demoDir->string, - sv_demoPrefix->string, SV_CleanName (Cmd_Argv (1)), - sv_demoSuffix->string); + dsprintf (name, "%s/%s/%s%s%s", qfs_gamedir->dir.def, sv_demoDir, + sv_demoPrefix, SV_CleanName (Cmd_Argv (1)), + sv_demoSuffix); // open the demo file QFS_DefaultExtension (name, ".mvd"); - SV_Record (name->str); + demo_file = QFS_Open (name->str, "wb"); + if (!demo_file) { + Sys_Printf ("ERROR: couldn't open %s\n", name->str); + } else { + SV_Record (name->str); + } dstring_delete (name); } @@ -671,7 +783,7 @@ Dem_Team (int num) { int i; static const char *lastteam[2]; - qboolean first = true; + bool first = true; client_t *client; static int index = 0; const char *team; @@ -733,7 +845,7 @@ SV_EasyRecord_f (void) else { // guess game type and write demo name i = Dem_CountPlayers (); - if (teamplay->int_val && i > 2) { + if (teamplay && i > 2) { // Teamplay dsprintf (name, "team_%s_vs_%s_%s", Dem_Team (1), Dem_Team (2), sv.name); @@ -751,13 +863,14 @@ SV_EasyRecord_f (void) // Make sure the filename doesn't contain illegal characters dsprintf (name2, "%s/%s%s%s%s%s", - qfs_gamedir->dir.def, sv_demoDir->string, - sv_demoDir->string[0] ? "/" : "", - sv_demoPrefix->string, SV_CleanName (name->str), - sv_demoSuffix->string); + qfs_gamedir->dir.def, sv_demoDir, + sv_demoDir[0] ? "/" : "", + sv_demoPrefix, SV_CleanName (name->str), + sv_demoSuffix); - if (QFS_NextFilename (name, name2->str, ".mvd")) + if ((demo_file = QFS_NextFile (name, name2->str, ".mvd"))) { SV_Record (name->str); + } dstring_delete (name); dstring_delete (name2); @@ -773,7 +886,7 @@ Demo_Init (void) if (p < com_argc - 1) size = atoi (com_argv[p + 1]) * 1024; else - Sys_Error ("Memory_Init: you must specify a size in KB after " + Sys_Error ("Demo_Init: you must specify a size in KB after " "-democache"); } @@ -786,28 +899,24 @@ Demo_Init (void) demo_name = dstring_newstr (); demo_text = dstring_newstr (); - svs.demomem = Hunk_AllocName (size, "demo"); + svs.demomem = Hunk_AllocName (0, size, "demo"); svs.demomemsize = size; demo_max_size = size - 0x80000; - - serverdemo = Cvar_Get ("serverdemo", "", CVAR_SERVERINFO, Cvar_Info, - "FIXME"); - sv_demofps = Cvar_Get ("sv_demofps", "20", CVAR_NONE, 0, "FIXME"); - sv_demoPings = Cvar_Get ("sv_demoPings", "3", CVAR_NONE, 0, "FIXME"); - sv_demoNoVis = Cvar_Get ("sv_demoNoVis", "1", CVAR_NONE, 0, "FIXME"); - sv_demoUseCache = Cvar_Get ("sv_demoUseCache", "0", CVAR_NONE, 0, "FIXME"); - sv_demoCacheSize = Cvar_Get ("sv_demoCacheSize", va ("%d", size / 1024), - CVAR_ROM, 0, "FIXME"); - sv_demoMaxSize = Cvar_Get ("sv_demoMaxSize", "20480", CVAR_NONE, 0, - "FIXME"); - sv_demoMaxDirSize = Cvar_Get ("sv_demoMaxDirSize", "102400", CVAR_NONE, 0, - "FIXME"); - sv_demoDir = Cvar_Get ("sv_demoDir", "demos", CVAR_NONE, 0, "FIXME"); - sv_demoPrefix = Cvar_Get ("sv_demoPrefix", "", CVAR_NONE, 0, "FIXME"); - sv_demoSuffix = Cvar_Get ("sv_demoSuffix", "", CVAR_NONE, 0, "FIXME"); - sv_onrecordfinish = Cvar_Get ("sv_onrecordfinish", "", CVAR_NONE, 0, "FIXME"); - sv_ondemoremove = Cvar_Get ("sv_ondemoremove", "", CVAR_NONE, 0, "FIXME"); - sv_demotxt = Cvar_Get ("sv_demotxt", "1", CVAR_NONE, 0, "FIXME"); + sv_demoCacheSize_cvar.default_value = nva ("%d", size / 1024); + Cvar_Register (&serverdemo_cvar, Cvar_Info, &serverdemo); + Cvar_Register (&sv_demofps_cvar, 0, 0); + Cvar_Register (&sv_demoPings_cvar, 0, 0); + Cvar_Register (&sv_demoNoVis_cvar, 0, 0); + Cvar_Register (&sv_demoUseCache_cvar, 0, 0); + Cvar_Register (&sv_demoCacheSize_cvar, 0, 0); + Cvar_Register (&sv_demoMaxSize_cvar, 0, 0); + Cvar_Register (&sv_demoMaxDirSize_cvar, 0, 0); + Cvar_Register (&sv_demoDir_cvar, 0, 0); + Cvar_Register (&sv_demoPrefix_cvar, 0, 0); + Cvar_Register (&sv_demoSuffix_cvar, 0, 0); + Cvar_Register (&sv_onrecordfinish_cvar, 0, 0); + Cvar_Register (&sv_ondemoremove_cvar, 0, 0); + Cvar_Register (&sv_demotxt_cvar, 0, 0); Cmd_AddCommand ("record", SV_Record_f, "FIXME"); Cmd_AddCommand ("easyrecord", SV_EasyRecord_f, "FIXME"); diff --git a/qw/source/sv_ents.c b/qw/source/sv_ents.c index bb0b2b1b3..d52401298 100644 --- a/qw/source/sv_ents.c +++ b/qw/source/sv_ents.c @@ -37,13 +37,17 @@ #include "QF/cvar.h" #include "QF/msg.h" +#include "QF/set.h" #include "QF/sys.h" +#include "QF/simd/vec4f.h" + +#include "compat.h" + #include "qw/msg_ucmd.h" -#include "compat.h" -#include "server.h" -#include "sv_progs.h" +#include "qw/include/server.h" +#include "qw/include/sv_progs.h" /* @@ -53,38 +57,33 @@ when the bob crosses a waterline. */ -byte fatpvs[MAX_MAP_LEAFS / 8]; -int fatbytes; +static set_t *fatpvs; static void -SV_AddToFatPVS (vec3_t org, mnode_t *node) +SV_AddToFatPVS (vec4f_t org, int node_id) { - byte *pvs; - int i; - float d; - plane_t *plane; + float d; while (1) { // if this is a leaf, accumulate the pvs bits - if (node->contents < 0) { - if (node->contents != CONTENTS_SOLID) { - pvs = Mod_LeafPVS ((mleaf_t *) node, sv.worldmodel); - for (i = 0; i < fatbytes; i++) - fatpvs[i] |= pvs[i]; + if (node_id < 0) { + mleaf_t *leaf = sv.worldmodel->brush.leafs + ~node_id; + if (leaf->contents != CONTENTS_SOLID) { + set_union (fatpvs, Mod_LeafPVS (leaf, sv.worldmodel)); } return; } - plane = node->plane; - d = DotProduct (org, plane->normal) - plane->dist; + mnode_t *node = sv.worldmodel->brush.nodes + node_id; + d = dotf (org, node->plane)[0]; if (d > 8) - node = node->children[0]; + node_id = node->children[0]; else if (d < -8) - node = node->children[1]; + node_id = node->children[1]; else { // go down both SV_AddToFatPVS (org, node->children[0]); - node = node->children[1]; + node_id = node->children[1]; } } } @@ -95,12 +94,15 @@ SV_AddToFatPVS (vec3_t org, mnode_t *node) Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the given point. */ -static byte * -SV_FatPVS (vec3_t org) +static set_t * +SV_FatPVS (vec4f_t org) { - fatbytes = (sv.worldmodel->numleafs + 31) >> 3; - memset (fatpvs, 0, fatbytes); - SV_AddToFatPVS (org, sv.worldmodel->nodes); + if (!fatpvs) { + fatpvs = set_new_size (sv.worldmodel->brush.visleafs); + } + set_expand (fatpvs, sv.worldmodel->brush.visleafs); + set_empty (fatpvs); + SV_AddToFatPVS (org, 0); return fatpvs; } @@ -111,7 +113,7 @@ int numnails; int nailcount; -static qboolean +static bool SV_AddNailUpdate (edict_t *ent) { if (SVfloat (ent, modelindex) != sv_nailmodel @@ -125,7 +127,7 @@ SV_AddNailUpdate (edict_t *ent) } static void -SV_EmitNailUpdate (sizebuf_t *msg, qboolean recorder) +SV_EmitNailUpdate (sizebuf_t *msg, bool recorder) { byte *buf; // [48 bits] xyzpy 12 12 12 4 8 int n, p, x, y, z, yaw; @@ -173,7 +175,7 @@ SV_EmitNailUpdate (sizebuf_t *msg, qboolean recorder) */ static void SV_WriteDelta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg, - qboolean force, int stdver) + bool force, int stdver) { int bits, i; float miss; @@ -399,7 +401,7 @@ write_demoplayer (delta_t *delta, plent_state_t *from, plent_state_t *to, int flags; int j; - flags = to->flags >> 1; // convert PF_(GIB|DEAD) to DF_(GIB|DEAD) + flags = to->es.flags >> 1; // convert PF_(GIB|DEAD) to DF_(GIB|DEAD) flags &= DF_GIB | DF_DEAD; // PF_MSEC and PF_COMMAND aren't wanted if (full) { flags |= DF_ORIGIN | (DF_ORIGIN << 1) | (DF_ORIGIN << 2) @@ -407,55 +409,55 @@ write_demoplayer (delta_t *delta, plent_state_t *from, plent_state_t *to, | DF_EFFECTS | DF_SKINNUM | DF_WEAPONFRAME | DF_MODEL; } else { for (j = 0; j < 3; j++) - if (from->origin[j] != to->origin[j]) + if (from->es.origin[j] != to->es.origin[j]) flags |= DF_ORIGIN << j; for (j = 0; j < 3; j++) if (from->cmd.angles[j] != to->cmd.angles[j]) flags |= DF_ANGLES << j; - if (from->modelindex != to->modelindex) + if (from->es.modelindex != to->es.modelindex) flags |= DF_MODEL; - if ((from->effects & 0xff) != (to->effects & 0xff)) + if ((from->es.effects & 0xff) != (to->es.effects & 0xff)) flags |= DF_EFFECTS; - if (from->skinnum != to->skinnum) + if (from->es.skinnum != to->es.skinnum) flags |= DF_SKINNUM; - if (from->weaponframe != to->weaponframe) + if (from->es.weaponframe != to->es.weaponframe) flags |= DF_WEAPONFRAME; } MSG_WriteByte (msg, svc_playerinfo); - MSG_WriteByte (msg, to->number); + MSG_WriteByte (msg, to->es.number); MSG_WriteShort (msg, flags); - MSG_WriteByte (msg, to->frame); + MSG_WriteByte (msg, to->es.frame); for (j = 0; j < 3; j++) if (flags & (DF_ORIGIN << j)) - MSG_WriteCoord (msg, to->origin[j]); + MSG_WriteCoord (msg, to->es.origin[j]); for (j = 0; j < 3; j++) if (flags & (DF_ANGLES << j)) MSG_WriteAngle16 (msg, to->cmd.angles[j]); if (flags & DF_MODEL) - MSG_WriteByte (msg, to->modelindex); + MSG_WriteByte (msg, to->es.modelindex); if (flags & DF_SKINNUM) - MSG_WriteByte (msg, to->skinnum); + MSG_WriteByte (msg, to->es.skinnum); if (flags & DF_EFFECTS) - MSG_WriteByte (msg, to->effects); + MSG_WriteByte (msg, to->es.effects); if (flags & DF_WEAPONFRAME) - MSG_WriteByte (msg, to->weaponframe); + MSG_WriteByte (msg, to->es.weaponframe); } static void write_player (delta_t *delta, plent_state_t *from, plent_state_t *to, sizebuf_t *msg, int mask, int full) { - int flags = to->flags; + int flags = to->es.flags; int qf_bits = 0; int i; int ds = delta->delta_sequence & 0x7f; @@ -471,30 +473,30 @@ write_player (delta_t *delta, plent_state_t *from, plent_state_t *to, ds = -1; } else { for (i = 0; i < 3; i++) - if (from->velocity[i] != to->velocity[i]) + if (from->es.velocity[i] != to->es.velocity[i]) flags |= PF_VELOCITY1 << i; - if (from->modelindex != to->modelindex) + if (from->es.modelindex != to->es.modelindex) flags |= PF_MODEL; - if (from->skinnum != to->skinnum) + if (from->es.skinnum != to->es.skinnum) flags |= PF_SKINNUM; - if ((from->effects & 0xff) != (to->effects &0xff)) + if ((from->es.effects & 0xff) != (to->es.effects &0xff)) flags |= PF_EFFECTS; - if (from->weaponframe != to->weaponframe) + if (from->es.weaponframe != to->es.weaponframe) flags |= PF_WEAPONFRAME; - if (from->alpha != to->alpha) + if (from->es.alpha != to->es.alpha) qf_bits |= PF_ALPHA; - if (from->scale != to->scale) + if (from->es.scale != to->es.scale) qf_bits |= PF_SCALE; - if ((from->effects & 0xff00) != (to->effects & 0xff00)) + if ((from->es.effects & 0xff00) != (to->es.effects & 0xff00)) qf_bits |= PF_EFFECTS2; - if (from->glow_size != to->glow_size) + if (from->es.glow_size != to->es.glow_size) qf_bits |= PF_GLOWSIZE; - if (from->glow_color != to->glow_color) + if (from->es.glow_color != to->es.glow_color) qf_bits |= PF_GLOWCOLOR; - if (from->colormod != to->colormod) + if (from->es.colormod != to->es.colormod) qf_bits |= PF_COLORMOD; - if ((from->frame & 0xff00) != (to->frame & 0xff00)) + if ((from->es.frame & 0xff00) != (to->es.frame & 0xff00)) qf_bits |= PF_FRAME2; if (qf_bits) flags |= PF_QF; @@ -505,12 +507,12 @@ write_player (delta_t *delta, plent_state_t *from, plent_state_t *to, MSG_WriteByte (msg, svc_playerinfo); if (delta->type == dt_tp_qtv) MSG_WriteByte (msg, ds); - MSG_WriteByte (msg, to->number); + MSG_WriteByte (msg, to->es.number); MSG_WriteShort (msg, flags); - MSG_WriteCoordV (msg, to->origin); + MSG_WriteCoordV (msg, (vec_t*)&to->es.origin);//FIXME - MSG_WriteByte (msg, to->frame); + MSG_WriteByte (msg, to->es.frame); if (flags & PF_MSEC) MSG_WriteByte (msg, to->msec); @@ -518,37 +520,37 @@ write_player (delta_t *delta, plent_state_t *from, plent_state_t *to, MSG_WriteDeltaUsercmd (msg, &from->cmd, &to->cmd); for (i = 0; i < 3; i++) if (flags & (PF_VELOCITY1 << i)) - MSG_WriteShort (msg, to->velocity[i]); + MSG_WriteShort (msg, to->es.velocity[i]); if (flags & PF_MODEL) - MSG_WriteByte (msg, to->modelindex); + MSG_WriteByte (msg, to->es.modelindex); if (flags & PF_SKINNUM) - MSG_WriteByte (msg, to->skinnum); + MSG_WriteByte (msg, to->es.skinnum); if (flags & PF_EFFECTS) - MSG_WriteByte (msg, to->effects); + MSG_WriteByte (msg, to->es.effects); if (flags & PF_WEAPONFRAME) - MSG_WriteByte (msg, to->weaponframe); + MSG_WriteByte (msg, to->es.weaponframe); if (flags & PF_QF) { MSG_WriteByte (msg, qf_bits); if (qf_bits & PF_ALPHA) - MSG_WriteByte (msg, to->alpha); + MSG_WriteByte (msg, to->es.alpha); if (qf_bits & PF_SCALE) - MSG_WriteByte (msg, to->scale); + MSG_WriteByte (msg, to->es.scale); if (qf_bits & PF_EFFECTS2) - MSG_WriteByte (msg, to->effects >> 8); + MSG_WriteByte (msg, to->es.effects >> 8); if (qf_bits & PF_GLOWSIZE) - MSG_WriteByte (msg, to->glow_size); + MSG_WriteByte (msg, to->es.glow_size); if (qf_bits & PF_GLOWCOLOR) - MSG_WriteByte (msg, to->glow_color); + MSG_WriteByte (msg, to->es.glow_color); if (qf_bits & PF_COLORMOD) - MSG_WriteByte (msg, to->colormod); + MSG_WriteByte (msg, to->es.colormod); if (qf_bits & PF_FRAME2) - MSG_WriteByte (msg, to->frame); + MSG_WriteByte (msg, to->es.frame); } } static void -SV_WritePlayersToClient (delta_t *delta, byte *pvs, sizebuf_t *msg) +SV_WritePlayersToClient (delta_t *delta, set_t *pvs, sizebuf_t *msg) { int j, k; client_t *cl; @@ -565,20 +567,19 @@ SV_WritePlayersToClient (delta_t *delta, byte *pvs, sizebuf_t *msg) void (*write) (delta_t *, plent_state_t *, plent_state_t *, sizebuf_t *, int, int); - if (!null_player_state.alpha) { - null_player_state.alpha = 255; - null_player_state.scale = 16; - null_player_state.glow_size = 0; - null_player_state.glow_color = 254; - null_player_state.colormod = 255; + if (!null_player_state.es.alpha) { + null_player_state.es.alpha = 255; + null_player_state.es.scale = 16; + null_player_state.es.glow_size = 0; + null_player_state.es.glow_color = 254; + null_player_state.es.colormod = 255; } - null_player_state.modelindex = 0; + null_player_state.es.modelindex = 0; if (delta->client) { clent = delta->client->edict; spec_track = delta->client->spec_track; - stdver = delta->client->stdver; - null_player_state.modelindex = sv_playermodel; + null_player_state.es.modelindex = sv_playermodel; full = 0; // normal qw clients don't get real deltas on players } @@ -613,7 +614,7 @@ SV_WritePlayersToClient (delta_t *delta, byte *pvs, sizebuf_t *msg) if (pvs) { // ignore if not touching a PV leaf for (el = SVdata (ent)->leafs; el; el = el->next) { - if (pvs[el->leafnum >> 3] & (1 << (el->leafnum & 7))) + if (set_is_member (pvs, el->leafnum)) break; } if (!el) @@ -625,9 +626,9 @@ SV_WritePlayersToClient (delta_t *delta, byte *pvs, sizebuf_t *msg) state = &pack->players[pack->num_players]; pack->num_players++; - state->number = j; - state->flags = 0; - VectorCopy (SVvector (ent, origin), state->origin); + state->es.number = j; + state->es.flags = 0; + VectorCopy (SVvector (ent, origin), state->es.origin); state->msec = min (255, 1000 * (sv.time - cl->localtime)); @@ -639,44 +640,44 @@ SV_WritePlayersToClient (delta_t *delta, byte *pvs, sizebuf_t *msg) state->cmd.angles[0] = 0; } - VectorCopy (SVvector (ent, velocity), state->velocity); - state->modelindex = SVfloat (ent, modelindex); - state->frame = SVfloat (ent, frame); - state->skinnum = SVfloat (ent, skin); - state->effects = SVfloat (ent, effects); - state->weaponframe = SVfloat (ent, weaponframe); + VectorCopy (SVvector (ent, velocity), state->es.velocity); + state->es.modelindex = SVfloat (ent, modelindex); + state->es.frame = SVfloat (ent, frame); + state->es.skinnum = SVfloat (ent, skin); + state->es.effects = SVfloat (ent, effects); + state->es.weaponframe = SVfloat (ent, weaponframe); if (SVfloat (ent, health) <= 0) - state->flags |= PF_DEAD; + state->es.flags |= PF_DEAD; if (SVvector (ent, mins)[2] != -24) - state->flags |= PF_GIB; - state->flags |= PF_MSEC | PF_COMMAND; + state->es.flags |= PF_GIB; + state->es.flags |= PF_MSEC | PF_COMMAND; - state->alpha = 255; - state->scale = 16; - state->glow_size = 0; - state->glow_color = 254; - state->colormod = 255; + state->es.alpha = 255; + state->es.scale = 16; + state->es.glow_size = 0; + state->es.glow_color = 254; + state->es.colormod = 255; if (sv_fields.alpha != -1 && SVfloat (ent, alpha)) { float alpha = SVfloat (ent, alpha); - state->alpha = bound (0, alpha, 1) * 255.0; + state->es.alpha = bound (0, alpha, 1) * 255.0; } if (sv_fields.scale != -1 && SVfloat (ent, scale)) { float scale = SVfloat (ent, scale); - state->scale = bound (0, scale, 15.9375) * 16; + state->es.scale = bound (0, scale, 15.9375) * 16; } if (sv_fields.glow_size != -1 && SVfloat (ent, glow_size)) { int glow_size = SVfloat (ent, glow_size); - state->glow_size = bound (-1024, glow_size, 1016) >> 3; + state->es.glow_size = bound (-1024, glow_size, 1016) >> 3; } if (sv_fields.glow_color != -1 && SVfloat (ent, glow_color)) - state->glow_color = SVfloat (ent, glow_color); + state->es.glow_color = SVfloat (ent, glow_color); if (sv_fields.colormod != -1 && !VectorIsZero (SVvector (ent, colormod))) { float *colormod= SVvector (ent, colormod); - state->colormod = ((int) (bound (0, colormod[0], 1) * 7) << 5) | - ((int) (bound (0, colormod[1], 1) * 7) << 2) | - (int) (bound (0, colormod[2], 1) * 3); + state->es.colormod = ((int) (bound (0, colormod[0], 1) * 7) << 5) | + ((int) (bound (0, colormod[1], 1) * 7) << 2) | + (int) (bound (0, colormod[2], 1) * 3); } if (cl->spectator) { @@ -693,10 +694,10 @@ SV_WritePlayersToClient (delta_t *delta, byte *pvs, sizebuf_t *msg) if (from_pack && from_pack->players) { while (k < from_pack->num_players - && from_pack->players[k].number < state->number) + && from_pack->players[k].es.number < state->es.number) k++; if (k < from_pack->num_players - && from_pack->players[k].number == state->number) { + && from_pack->players[k].es.number == state->es.number) { write (delta, &from_pack->players[k], state, msg, mask, full); continue; } @@ -706,16 +707,17 @@ SV_WritePlayersToClient (delta_t *delta, byte *pvs, sizebuf_t *msg) } } -static inline byte * +static inline set_t * calc_pvs (delta_t *delta) { - byte *pvs = 0; - vec3_t org; + set_t *pvs = 0; + vec4f_t org = {}; // find the client's PVS if (delta->pvs == dt_pvs_normal) { edict_t *clent = delta->client->edict; VectorAdd (SVvector (clent, origin), SVvector (clent, view_ofs), org); + org[3] = 1; pvs = SV_FatPVS (org); } else if (delta->pvs == dt_pvs_fat) { // when recording a demo, send only entities that can be seen. Can help @@ -735,10 +737,11 @@ calc_pvs (delta_t *delta) VectorAdd (SVvector (cl->edict, origin), SVvector (cl->edict, view_ofs), org); + org[3] = 1; if (pvs == NULL) { pvs = SV_FatPVS (org); } else { - SV_AddToFatPVS (org, sv.worldmodel->nodes); + SV_AddToFatPVS (org, 0); } } } @@ -756,7 +759,7 @@ calc_pvs (delta_t *delta) void SV_WriteEntitiesToClient (delta_t *delta, sizebuf_t *msg) { - byte *pvs = 0; + set_t *pvs = 0; int e, num_edicts; int max_packet_entities = MAX_DEMO_PACKET_ENTITIES; int stdver = 1; @@ -801,7 +804,7 @@ SV_WriteEntitiesToClient (delta_t *delta, sizebuf_t *msg) if (pvs) { // ignore if not touching a PV leaf for (el = SVdata (ent)->leafs; el; el = el->next) { - if (pvs[el->leafnum >> 3] & (1 << (el->leafnum & 7))) + if (set_is_member (pvs, el->leafnum)) break; } if (!el) diff --git a/qw/source/sv_gib.c b/qw/source/sv_gib.c index 7600805d2..17a21af7e 100644 --- a/qw/source/sv_gib.c +++ b/qw/source/sv_gib.c @@ -35,12 +35,13 @@ # include #endif +#include "QF/cbuf.h" #include "QF/dstring.h" #include "QF/info.h" #include "QF/gib.h" -#include "server.h" -#include "client.h" +#include "qw/include/server.h" +#include "qw/include/client.h" gib_event_t *sv_chat_e; gib_event_t *sv_client_connect_e; diff --git a/qw/source/sv_init.c b/qw/source/sv_init.c index b9dbb0aec..26379b17c 100644 --- a/qw/source/sv_init.c +++ b/qw/source/sv_init.c @@ -35,21 +35,24 @@ # include #endif +#include "QF/cbuf.h" #include "QF/crc.h" #include "QF/cvar.h" #include "QF/info.h" #include "QF/msg.h" #include "QF/quakefs.h" +#include "QF/set.h" #include "QF/sys.h" #include "QF/va.h" #include "compat.h" -#include "crudefile.h" -#include "map_cfg.h" + +#include "qw/include/crudefile.h" +#include "qw/include/map_cfg.h" #include "qw/pmove.h" -#include "server.h" -#include "sv_progs.h" -#include "sv_gib.h" +#include "qw/include/server.h" +#include "qw/include/sv_progs.h" +#include "qw/include/sv_gib.h" #include "world.h" info_t *localinfo; // local game info @@ -120,11 +123,8 @@ SV_FlushSignon (void) static void SV_CreateBaseline (void) { - int entnum; - edict_t *svent; - - for (entnum = 0; entnum < sv.num_edicts; entnum++) { - svent = EDICT_NUM (&sv_pr_state, entnum); + for (unsigned entnum = 0; entnum < sv.num_edicts; entnum++) { + edict_t *svent = EDICT_NUM (&sv_pr_state, entnum); if (svent->free) continue; // create baselines for all player slots, @@ -166,7 +166,8 @@ SV_CreateBaseline (void) MSG_WriteByte (&sv.signon, SVdata (svent)->state.colormap); MSG_WriteByte (&sv.signon, SVdata (svent)->state.skinnum); - MSG_WriteCoordAngleV (&sv.signon, SVdata (svent)->state.origin, + MSG_WriteCoordAngleV (&sv.signon, + (vec_t*)&SVdata (svent)->state.origin,//FIXME SVdata (svent)->state.angles); } } @@ -212,6 +213,34 @@ SV_SaveSpawnparms (void) } } +static set_t * +sv_alloc_vis_array (unsigned numleafs) +{ + // the passed in numleafs is the true number of leafs in the map and thus + // does include leaf 0, but pvs bits do not include leaf 0 + unsigned size = SET_SIZE (numleafs - 1); + + if (size > SET_DEFMAP_SIZE * SET_BITS) { + set_t *sets = Hunk_Alloc (0, + numleafs * (sizeof (set_t) + size / 8)); + unsigned words = size / SET_BITS; + set_bits_t *bits = (set_bits_t *) (&sets[numleafs]); + for (unsigned i = 0; i < numleafs; i++) { + sets[i].size = size; + sets[i].map = bits; + bits += words; + } + return sets; + } else { + set_t *sets = Hunk_Alloc (0, numleafs * sizeof (set_t)); + for (unsigned i = 0; i < numleafs; i++) { + sets[i].size = size; + sets[i].map = sets[i].defmap; + } + return sets; + } +} + /* SV_CalcPHS @@ -221,65 +250,41 @@ SV_SaveSpawnparms (void) static void SV_CalcPHS (void) { - byte *scan; - int bitbyte, count, index, num, rowbytes, rowwords, vcount, i, j, - k, l; - unsigned int *dest, *src; + int64_t count, vcount; + int num, i; SV_Printf ("Building PHS...\n"); - num = sv.worldmodel->numleafs; - rowwords = (num + 31) >> 5; - rowbytes = rowwords * 4; - - sv.pvs = Hunk_Alloc (rowbytes * num); - scan = sv.pvs; + num = sv.worldmodel->brush.modleafs; + sv.pvs = sv_alloc_vis_array (num); vcount = 0; - for (i = 0; i < num; i++, scan += rowbytes) { - memcpy (scan, Mod_LeafPVS (sv.worldmodel->leafs + i, sv.worldmodel), - rowbytes); + for (i = 0; i < num; i++) { + Mod_LeafPVS_set (sv.worldmodel->brush.leafs + i, sv.worldmodel, 0xff, + &sv.pvs[i]); if (i == 0) continue; - for (j = 0; j < num; j++) { - if (scan[j >> 3] & (1 << (j & 7))) { - vcount++; - } - } + vcount += set_count (&sv.pvs[i]); } - sv.phs = Hunk_Alloc (rowbytes * num); + sv.phs = sv_alloc_vis_array (num); count = 0; - scan = sv.pvs; - dest = (unsigned int *) sv.phs; - for (i = 0; i < num; i++, dest += rowwords, scan += rowbytes) { - memcpy (dest, scan, rowbytes); - for (j = 0; j < rowbytes; j++) { - bitbyte = scan[j]; - if (!bitbyte) - continue; - for (k = 0; k < 8; k++) { - if (!(bitbyte & (1 << k))) - continue; - // or this pvs row into the phs - // +1 because pvs is 1 based - index = ((j << 3) + k + 1); - if (index >= num) - continue; - src = (unsigned int *) sv.pvs + index * rowwords; - for (l = 0; l < rowwords; l++) - dest[l] |= src[l]; - } + for (i = 0; i < num; i++) { + set_assign (&sv.phs[i], &sv.pvs[i]); + + for (set_iter_t *iter = set_first (&sv.pvs[i]); iter; + iter = set_next (iter)) { + // or this pvs row into the phs + // +1 because pvs is 1 based + set_union (&sv.phs[i], &sv.pvs[iter->element + 1]); } if (i == 0) continue; - for (j = 0; j < num; j++) - if (((byte *) dest)[j >> 3] & (1 << (j & 7))) - count++; + count += set_count (&sv.phs[i]); } SV_Printf ("Average leafs visible / hearable / total: %i / %i / %i\n", - vcount / num, count / num, num); + (int) (vcount / num), (int) (count / num), num); } static unsigned int @@ -314,14 +319,13 @@ SV_SpawnServer (const char *server) { byte *buf; edict_t *ent; - int i; void *so_buffers; int *so_sizes; int max_so; struct recorder_s *recorders; QFile *ent_file; - Sys_MaskPrintf (SYS_DEV, "SpawnServer: %s\n", server); + Sys_MaskPrintf (SYS_dev, "SpawnServer: %s\n", server); SV_SaveSpawnparms (); @@ -332,7 +336,7 @@ SV_SpawnServer (const char *server) sv_pr_state.null_bad = 0; Mod_ClearAll (); - Hunk_FreeToLowMark (host_hunklevel); + Hunk_FreeToLowMark (0, host_hunklevel); // wipe the entire per-level structure, but don't lose sv.recorders // or signon buffers (good thing we're not multi-threaded FIXME?) @@ -341,6 +345,9 @@ SV_SpawnServer (const char *server) so_buffers = sv.signon_buffers; so_sizes = sv.signon_buffer_size; + if (sv.name) { + free (sv.name); + } memset (&sv, 0, sizeof (sv)); sv.recorders = recorders; @@ -363,19 +370,19 @@ SV_SpawnServer (const char *server) SV_NextSignon (); - strcpy (sv.name, server); + sv.name = strdup(server); // load progs to get entity field count which determines how big each // edict is SV_LoadProgs (); SV_FreeAllEdictLeafs (); SV_SetupUserCommands (); - Info_SetValueForStarKey (svs.info, "*progs", va ("%i", sv_pr_state.crc), - !sv_highchars->int_val); + Info_SetValueForStarKey (svs.info, "*progs", va (0, "%i", sv_pr_state.crc), + !sv_highchars); // leave slots at start for only clients sv.num_edicts = MAX_CLIENTS + 1; - for (i = 0; i < MAX_CLIENTS; i++) { + for (int i = 0; i < MAX_CLIENTS; i++) { ent = EDICT_NUM (&sv_pr_state, i + 1); svs.clients[i].edict = ent; // ZOID - make sure we update frags right @@ -384,7 +391,6 @@ SV_SpawnServer (const char *server) sv.time = 1.0; - strncpy (sv.name, server, sizeof (sv.name)); snprintf (sv.modelname, sizeof (sv.modelname), "maps/%s.bsp", server); map_cfg (sv.modelname, 0); sv.worldmodel = Mod_ForName (sv.modelname, true); @@ -399,7 +405,7 @@ SV_SpawnServer (const char *server) sv.model_precache[0] = sv_pr_state.pr_strings; sv.model_precache[1] = sv.modelname; sv.models[1] = sv.worldmodel; - for (i = 1; i < sv.worldmodel->numsubmodels; i++) { + for (unsigned i = 1; i < sv.worldmodel->brush.numsubmodels; i++) { sv.model_precache[1 + i] = localmodels[i]; sv.models[i + 1] = Mod_ForName (localmodels[i], false); } @@ -417,7 +423,7 @@ SV_SpawnServer (const char *server) ent = EDICT_NUM (&sv_pr_state, 0); ent->free = false; - SVstring (ent, model) = PR_SetString (&sv_pr_state, sv.worldmodel->name); + SVstring (ent, model) = PR_SetString (&sv_pr_state, sv.worldmodel->path); SVfloat (ent, modelindex) = 1; // world model SVfloat (ent, solid) = SOLID_BSP; SVfloat (ent, movetype) = MOVETYPE_PUSH; @@ -434,13 +440,13 @@ SV_SpawnServer (const char *server) // load and spawn all other entities *sv_globals.time = sv.time; - ent_file = QFS_VOpenFile (va ("maps/%s.ent", server), 0, + ent_file = QFS_VOpenFile (va (0, "maps/%s.ent", server), 0, sv.worldmodel->vpath); if ((buf = QFS_LoadFile (ent_file, 0))) { ED_LoadFromFile (&sv_pr_state, (char *) buf); free (buf); } else { - ED_LoadFromFile (&sv_pr_state, sv.worldmodel->entities); + ED_LoadFromFile (&sv_pr_state, sv.worldmodel->brush.entities); } // look up some model indexes for specialized message compression @@ -463,8 +469,8 @@ SV_SpawnServer (const char *server) SV_CreateBaseline (); sv.signon_buffer_size[sv.num_signon_buffers - 1] = sv.signon.cursize; - Info_SetValueForKey (svs.info, "map", sv.name, !sv_highchars->int_val); - Sys_MaskPrintf (SYS_DEV, "Server spawned.\n"); + Info_SetValueForKey (svs.info, "map", sv.name, !sv_highchars); + Sys_MaskPrintf (SYS_dev, "Server spawned.\n"); if (sv_map_e->func) GIB_Event_Callback (sv_map_e, 1, server); } @@ -472,14 +478,14 @@ SV_SpawnServer (const char *server) void SV_SetMoveVars (void) { - movevars.gravity = sv_gravity->value; - movevars.stopspeed = sv_stopspeed->value; - movevars.maxspeed = sv_maxspeed->value; - movevars.spectatormaxspeed = sv_spectatormaxspeed->value; - movevars.accelerate = sv_accelerate->value; - movevars.airaccelerate = sv_airaccelerate->value; - movevars.wateraccelerate = sv_wateraccelerate->value; - movevars.friction = sv_friction->value; - movevars.waterfriction = sv_waterfriction->value; + movevars.gravity = sv_gravity; + movevars.stopspeed = sv_stopspeed; + movevars.maxspeed = sv_maxspeed; + movevars.spectatormaxspeed = sv_spectatormaxspeed; + movevars.accelerate = sv_accelerate; + movevars.airaccelerate = sv_airaccelerate; + movevars.wateraccelerate = sv_wateraccelerate; + movevars.friction = sv_friction; + movevars.waterfriction = sv_waterfriction; movevars.entgravity = 1.0; } diff --git a/qw/source/sv_main.c b/qw/source/sv_main.c index 42a313aa6..22af5c180 100644 --- a/qw/source/sv_main.c +++ b/qw/source/sv_main.c @@ -77,19 +77,20 @@ #include "QF/plugin/console.h" -#include "qw/bothdefs.h" #include "buildnum.h" #include "compat.h" -#include "crudefile.h" -#include "game.h" #include "netchan.h" + +#include "qw/bothdefs.h" +#include "qw/include/crudefile.h" +#include "qw/include/game.h" #include "qw/pmove.h" -#include "server.h" -#include "sv_demo.h" -#include "sv_progs.h" -#include "sv_gib.h" -#include "sv_qtv.h" -#include "sv_recorder.h" +#include "qw/include/server.h" +#include "qw/include/sv_demo.h" +#include "qw/include/sv_progs.h" +#include "qw/include/sv_gib.h" +#include "qw/include/sv_qtv.h" +#include "qw/include/sv_recorder.h" SERVER_PLUGIN_PROTOS static plugin_list_t server_plugin_list[] = { @@ -105,14 +106,14 @@ entity_state_t cl_entities[MAX_CLIENTS][UPDATE_BACKUP+1][MAX_PACKET_ENTITIES]; / double sv_frametime; double realtime; // without any filtering or bounding -int host_hunklevel; +size_t host_hunklevel; netadr_t master_adr[MAX_MASTERS]; // address of group servers quakeparms_t host_parms; -qboolean host_initialized; // true if into command execution -qboolean rcon_from_user; +bool host_initialized; // true if into command execution +bool rcon_from_user; // DoS protection // FLOOD_PING, FLOOD_LOG, FLOOD_CONNECT, FLOOD_STATUS, FLOOD_RCON, FLOOD_BAN @@ -121,74 +122,390 @@ qboolean rcon_from_user; double netdosexpire[DOSFLOODCMDS] = { 1, 1, 2, 0.9, 1, 5 }; double netdosvalues[DOSFLOODCMDS] = { 12, 1, 3, 1, 1, 1 }; -cvar_t *sv_mem_size; +float sv_mem_size; +static cvar_t sv_mem_size_cvar = { + .name = "sv_mem_size", + .description = + "Amount of memory (in MB) to allocate for the " + PACKAGE_NAME + " heap", + .default_value = "8", + .flags = CVAR_ROM, + .value = { .type = &cexpr_float, .value = &sv_mem_size }, +}; -cvar_t *sv_console_plugin; +char *sv_console_plugin; +static cvar_t sv_console_plugin_cvar = { + .name = "sv_console_plugin", + .description = + "Plugin used for the console", + .default_value = "server", + .flags = CVAR_ROM, + .value = { .type = 0, .value = &sv_console_plugin }, +}; -cvar_t *sv_allow_status; -cvar_t *sv_allow_log; -cvar_t *sv_allow_ping; +int sv_allow_status; +static cvar_t sv_allow_status_cvar = { + .name = "sv_allow_status", + .description = + "Allow remote status queries (qstat etc)", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_allow_status }, +}; +int sv_allow_log; +static cvar_t sv_allow_log_cvar = { + .name = "sv_allow_log", + .description = + "Allow remote logging", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_allow_log }, +}; +int sv_allow_ping; +static cvar_t sv_allow_ping_cvar = { + .name = "sv_allow_pings", + .description = + "Allow remote pings (qstat etc)", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_allow_ping }, +}; -cvar_t *sv_extensions; // Use the extended protocols +int sv_extensions; +static cvar_t sv_extensions_cvar = { + .name = "sv_extensions", + .description = + "Use protocol extensions for QuakeForge clients", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_extensions }, +}; -cvar_t *sv_mintic; // bound the size of the -cvar_t *sv_maxtic; // physics time tic +float sv_mintic; +static cvar_t sv_mintic_cvar = { + .name = "sv_mintic", + .description = + "The minimum amount of time the server will wait before sending " + "packets to a client. Set to .5 to make modem users happy", + .default_value = "0.03", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_mintic }, +}; +float sv_maxtic; +static cvar_t sv_maxtic_cvar = { + .name = "sv_maxtic", + .description = + "The maximum amount of time in seconds before a client a receives an " + "update from the server", + .default_value = "0.1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_maxtic }, +}; -cvar_t *sv_netdosprotect; // tone down DoS from quake servers +int sv_netdosprotect; +static cvar_t sv_netdosprotect_cvar = { + .name = "sv_netdosprotect", + .description = + "DoS flood attack protection", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_netdosprotect }, +}; -cvar_t *sv_timeout; // seconds without any message -cvar_t *zombietime; // seconds to sink messages after +float sv_timeout; +static cvar_t sv_timeout_cvar = { + .name = "timeout", + .description = + "Sets the amount of time in seconds before a client is considered " + "disconnected if the server does not receive a packet", + .default_value = "65", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_timeout }, +}; +float zombietime; +static cvar_t zombietime_cvar = { + .name = "zombietime", + .description = + "The number of seconds that the server will keep the character of a " + "player on the map who seems to have disconnected", + .default_value = "2", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &zombietime }, +}; // disconnect -cvar_t *rcon_password; // password for remote server -cvar_t *admin_password; // password for admin commands +char *rcon_password; +static cvar_t rcon_password_cvar = { + .name = "rcon_password", + .description = + "Set the password for rcon 'root' commands", + .default_value = "", + .flags = CVAR_NONE, + .value = { .type=0/*FIXME want secret strings*/, .value = &rcon_password }, +}; +char *admin_password; +static cvar_t admin_password_cvar = { + .name = "admin_password", + .description = + "Set the password for rcon admin commands", + .default_value = "", + .flags = CVAR_NONE, + .value = { .type = 0/* not used */, .value = &admin_password }, +}; -cvar_t *password; // password for entering the game -cvar_t *spectator_password; // password for entering as a +char *password; +static cvar_t password_cvar = { + .name = "password", + .description = + "Set the server password for players", + .default_value = "", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &password }, +}; +char *spectator_password; +static cvar_t spectator_password_cvar = { + .name = "spectator_password", + .description = + "Set the spectator password", + .default_value = "", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &spectator_password }, +}; // spectator -cvar_t *allow_download; -cvar_t *allow_download_skins; -cvar_t *allow_download_models; -cvar_t *allow_download_sounds; -cvar_t *allow_download_maps; -cvar_t *allow_download_demos; +int allow_download; +static cvar_t allow_download_cvar = { + .name = "allow_download", + .description = + "Toggle if clients can download game data from the server", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &allow_download }, +}; +int allow_download_skins; +static cvar_t allow_download_skins_cvar = { + .name = "allow_download_skins", + .description = + "Toggle if clients can download skins from the server", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &allow_download_skins }, +}; +int allow_download_models; +static cvar_t allow_download_models_cvar = { + .name = "allow_download_models", + .description = + "Toggle if clients can download models from the server", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &allow_download_models }, +}; +int allow_download_sounds; +static cvar_t allow_download_sounds_cvar = { + .name = "allow_download_sounds", + .description = + "Toggle if clients can download sounds from the server", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &allow_download_sounds }, +}; +int allow_download_maps; +static cvar_t allow_download_maps_cvar = { + .name = "allow_download_maps", + .description = + "Toggle if clients can download maps from the server", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &allow_download_maps }, +}; +int allow_download_demos; +static cvar_t allow_download_demos_cvar = { + .name = "allow_download_demos", + .description = + "Toggle if clients can download maps from the server", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &allow_download_demos }, +}; -cvar_t *sv_highchars; -cvar_t *sv_phs; +int sv_highchars; +static cvar_t sv_highchars_cvar = { + .name = "sv_highchars", + .description = + "Toggle the use of high character color names for players", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_highchars }, +}; +int sv_phs; +static cvar_t sv_phs_cvar = { + .name = "sv_phs", + .description = + "Possibly Hearable Set. If set to zero, the server calculates sound " + "hearability in realtime", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_phs }, +}; -cvar_t *pausable; +int pausable; +static cvar_t pausable_cvar = { + .name = "pausable", + .description = + "None", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &pausable }, +}; -cvar_t *sv_minqfversion; // Minimum QF version allowed to +char *sv_minqfversion; +static cvar_t sv_minqfversion_cvar = { + .name = "sv_minqfversion", + .description = + "Minimum QF version on client", + .default_value = "0", + .flags = CVAR_SERVERINFO, + .value = { .type = 0, .value = &sv_minqfversion }, +}; // connect -cvar_t *sv_maxrate; // Maximum allowable rate (silently - // capped) -cvar_t *sv_timestamps; -cvar_t *sv_timefmt; +int sv_timestamps; +static cvar_t sv_timestamps_cvar = { + .name = "sv_timestamps", + .description = + "Time/date stamps in log entries", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_timestamps }, +}; +char *sv_timefmt; +static cvar_t sv_timefmt_cvar = { + .name = "sv_timefmt", + .description = + "Time/date format to use", + .default_value = "[%b %e %X] ", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &sv_timefmt }, +}; // game rules mirrored in svs.info -cvar_t *fraglimit; -cvar_t *timelimit; -cvar_t *teamplay; -cvar_t *samelevel; -cvar_t *maxclients; -cvar_t *maxspectators; -cvar_t *deathmatch; // 0, 1, or 2 -cvar_t *coop; -cvar_t *skill; -cvar_t *spawn; -cvar_t *watervis; +float fraglimit; +static cvar_t fraglimit_cvar = { + .name = "fraglimit", + .description = + "None", + .default_value = "0", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_float, .value = &fraglimit }, +}; +int timelimit; +static cvar_t timelimit_cvar = { + .name = "timelimit", + .description = + "None", + .default_value = "0", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_int, .value = &timelimit }, +}; +int teamplay; +static cvar_t teamplay_cvar = { + .name = "teamplay", + .description = + "None", + .default_value = "0", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_int, .value = &teamplay }, +}; +float samelevel; +static cvar_t samelevel_cvar = { + .name = "samelevel", + .description = + "None", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &samelevel }, +}; +int maxclients; +static cvar_t maxclients_cvar = { + .name = "maxclients", + .description = + "Sets how many clients can connect to your server, this includes " + "spectators and players", + .default_value = "8", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_int, .value = &maxclients }, +}; +int maxspectators; +static cvar_t maxspectators_cvar = { + .name = "maxspectators", + .description = + "Sets how many spectators can connect to your server. The maxclients " + "value takes precedence over this value so this value should always be" + " equal-to or less-then the maxclients value", + .default_value = "8", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_int, .value = &maxspectators }, +}; +int deathmatch; +static cvar_t deathmatch_cvar = { + .name = "deathmatch", + .description = + "0, 1, or 2", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &deathmatch }, +}; +int coop; +static cvar_t coop_cvar = { + .name = "coop", + .description = + "0 or 1", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &coop }, +}; +int skill; +static cvar_t skill_cvar = { + .name = "skill", + .description = + "0 - 3", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &skill }, +}; +float spawn; +static cvar_t spawn_cvar = { + .name = "spawn", + .description = + "Spawn the player entity", + .default_value = "0", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_float, .value = &spawn }, +}; +int watervis; +static cvar_t watervis_cvar = { + .name = "watervis", + .description = + "Set nonzero to enable r_wateralpha on clients", + .default_value = "0", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_int, .value = &watervis }, +}; -cvar_t *hostname; +char *hostname; +static cvar_t hostname_cvar = { + .name = "hostname", + .description = + "None", + .default_value = "UNNAMED", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &hostname }, +}; QFile *sv_fraglogfile; -cvar_t *pr_gc; -cvar_t *pr_gc_interval; -int pr_gc_count = 0; - int sv_net_initialized; const char *client_info_filters[] = { // Info keys needed by client @@ -237,7 +554,7 @@ Master_Shutdown (void) Quake calls this before calling Sys_Quit or Sys_Error */ void -SV_Shutdown (void) +SV_Shutdown (void *data) { Master_Shutdown (); if (sv_fraglogfile) { @@ -246,9 +563,6 @@ SV_Shutdown (void) } if (sv.recording_demo) SV_Stop (0); - - NET_Shutdown (); - Con_Shutdown (); } /* @@ -257,10 +571,10 @@ SV_Shutdown (void) Sends a datagram to all the clients informing them of the server crash, then exits */ -static void +static __attribute__((format(PRINTF, 1, 0))) void SV_Error (const char *error, va_list argptr) { - static qboolean inerror = false; + static bool inerror = false; dstring_t *string; if (inerror) @@ -370,7 +684,7 @@ SV_DropClient (client_t *drop) // Trigger GIB event if (sv_client_disconnect_e->func) GIB_Event_Callback (sv_client_disconnect_e, 1, - va ("%u", drop->userid)); + va (0, "%u", drop->userid)); } int @@ -479,9 +793,9 @@ CheckForFlood (flood_enum_t cmdtype) static double lastmessagetime = 0; static flood_t floodstatus[DOSFLOODCMDS][DOSFLOODIP]; int oldest, i; - static qboolean firsttime = true; + static bool firsttime = true; - if (!sv_netdosprotect->int_val) + if (!sv_netdosprotect) return 0; oldestTime = 0x7fffffff; @@ -547,7 +861,7 @@ SVC_Status (void) client_t *cl; int ping, bottom, top, i; - if (!sv_allow_status->int_val) + if (!sv_allow_status) return; if (CheckForFlood (FLOOD_STATUS)) return; @@ -612,7 +926,7 @@ SVC_Log (void) char data[MAX_DATAGRAM + 64]; int seq; - if (!sv_allow_log->int_val) + if (!sv_allow_log) return; if (CheckForFlood (FLOOD_LOG)) return; @@ -629,7 +943,7 @@ SVC_Log (void) return; } - Sys_MaskPrintf (SYS_DEV, "sending log %i to %s\n", svs.logsequence - 1, + Sys_MaskPrintf (SYS_dev, "sending log %i to %s\n", svs.logsequence - 1, NET_AdrToString (net_from)); // snprintf (data, sizeof (data), "stdlog %i\n", svs.logsequence - 1); @@ -652,7 +966,7 @@ SVC_Ping (void) { char data; - if (!sv_allow_ping->int_val) + if (!sv_allow_ping) return; if (CheckForFlood (FLOOD_PING)) return; @@ -698,13 +1012,13 @@ SVC_GetChallenge (void) i = oldest; } - if (sv_extensions->int_val) { + if (sv_extensions) { extended = " QF qtv EXT"; } // send it to the client - Netchan_OutOfBandPrint (net_from, "%c%i%s", S2C_CHALLENGE, - svs.challenges[i].challenge, extended); + SV_OutOfBandPrint (net_from, "%c%i%s", S2C_CHALLENGE, + svs.challenges[i].challenge, extended); } client_t * @@ -732,8 +1046,8 @@ SV_AllocClient (int spectator, int server) // if at server limits, refuse connection if (!free || - (!server && ((spectator && spectators >= maxspectators->int_val) - || (!spectator && clients >= maxclients->int_val)))) { + (!server && ((spectator && spectators >= maxspectators) + || (!spectator && clients >= maxclients)))) { return 0; } // find a client slot @@ -766,7 +1080,7 @@ SVC_DirectConnect (void) int challenge, version, i, qtv = 0; int qport; netadr_t adr; - qboolean spectator; + bool spectator; client_frame_t *frames; if (CheckForFlood (FLOOD_CONNECT)) @@ -778,8 +1092,8 @@ SVC_DirectConnect (void) } else { version = atoi (s); if (version != PROTOCOL_VERSION) { - Netchan_OutOfBandPrint (net_from, "%c\nServer is version %s.\n", - A2C_PRINT, QW_VERSION); + SV_OutOfBandPrint (net_from, "%c\nServer is version %s.\n", + A2C_PRINT, QW_VERSION); SV_Printf ("* rejected connect from version %i\n", version); return; } @@ -791,12 +1105,12 @@ SVC_DirectConnect (void) if (strlen (Cmd_Argv (4)) < MAX_INFO_STRING) userinfo = Info_ParseString (Cmd_Argv (4), 1023, - !sv_highchars->int_val); + !sv_highchars); // Validate the userinfo string. if (!userinfo) { - Netchan_OutOfBandPrint (net_from, "%c\nInvalid userinfo string.\n", - A2C_PRINT); + SV_OutOfBandPrint (net_from, "%c\nInvalid userinfo string.\n", + A2C_PRINT); return; } @@ -805,14 +1119,13 @@ SVC_DirectConnect (void) if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr)) { if (challenge == svs.challenges[i].challenge) break; // good - Netchan_OutOfBandPrint (net_from, "%c\nBad challenge.\n", - A2C_PRINT); + SV_OutOfBandPrint (net_from, "%c\nBad challenge.\n", A2C_PRINT); return; } } if (i == MAX_CHALLENGES) { - Netchan_OutOfBandPrint (net_from, "%c\nNo challenge for address.\n", - A2C_PRINT); + SV_OutOfBandPrint (net_from, "%c\nNo challenge for address.\n", + A2C_PRINT); return; } @@ -822,45 +1135,45 @@ SVC_DirectConnect (void) } s = Info_ValueForKey (userinfo, "*qf_version"); - if ((!s[0]) || sv_minqfversion->string[0]) { // kick old clients? - if (ver_compare (s, sv_minqfversion->string) < 0) { + if ((!s[0]) || sv_minqfversion[0]) { // kick old clients? + if (ver_compare (s, sv_minqfversion) < 0) { SV_Printf ("%s: Version %s is less than minimum version %s.\n", NET_AdrToString (net_from), s, - sv_minqfversion->string); + sv_minqfversion); - Netchan_OutOfBandPrint (net_from, "%c\nserver requires QuakeForge " - "v%s or greater. Get it from " - "http://www.quakeforge.net/\n", A2C_PRINT, - sv_minqfversion->string); + SV_OutOfBandPrint (net_from, "%c\nserver requires QuakeForge " + "v%s or greater. Get it from " + "http://www.quakeforge.net/\n", A2C_PRINT, + sv_minqfversion); return; } } // check for password or spectator_password s = Info_ValueForKey (userinfo, "spectator"); if (s[0] && strcmp (s, "0")) { - if (spectator_password->string[0] && - !strcaseequal (spectator_password->string, "none") && - !strequal (spectator_password->string, s)) { // failed + if (spectator_password[0] && + !strcaseequal (spectator_password, "none") && + !strequal (spectator_password, s)) { // failed SV_Printf ("%s: spectator password failed\n", NET_AdrToString (net_from)); - Netchan_OutOfBandPrint (net_from, - "%c\nrequires a spectator password\n\n", - A2C_PRINT); + SV_OutOfBandPrint (net_from, + "%c\nrequires a spectator password\n\n", + A2C_PRINT); return; } Info_RemoveKey (userinfo, "spectator"); // remove passwd Info_SetValueForStarKey (userinfo, "*spectator", "1", - !sv_highchars->int_val); + !sv_highchars); spectator = true; } else { s = Info_ValueForKey (userinfo, "password"); - if (password->string[0] - && !strcaseequal (password->string, "none") - && !strequal (password->string, s)) { + if (password[0] + && !strcaseequal (password, "none") + && !strequal (password, s)) { SV_Printf ("%s:password failed\n", NET_AdrToString (net_from)); - Netchan_OutOfBandPrint (net_from, - "%c\nserver requires a password\n\n", - A2C_PRINT); + SV_OutOfBandPrint (net_from, + "%c\nserver requires a password\n\n", + A2C_PRINT); return; } spectator = false; @@ -888,7 +1201,7 @@ SVC_DirectConnect (void) } if (!(newcl = SV_AllocClient (spectator, 0))) { SV_Printf ("%s:full connect\n", NET_AdrToString (adr)); - Netchan_OutOfBandPrint (adr, "%c\nserver is full\n\n", A2C_PRINT); + SV_OutOfBandPrint (adr, "%c\nserver is full\n\n", A2C_PRINT); return; } newcl->userinfo = userinfo; @@ -903,7 +1216,7 @@ SVC_DirectConnect (void) } newcl->delta.client = newcl; - Netchan_OutOfBandPrint (adr, "%c", S2C_CONNECTION); + SV_OutOfBandPrint (adr, "%c", S2C_CONNECTION); Netchan_Setup (&newcl->netchan, adr, qport, NC_QPORT_READ); newcl->backbuf.netchan = &newcl->netchan; @@ -951,12 +1264,12 @@ SVC_DirectConnect (void) } static int -Rcon_Validate (cvar_t *pass) +Rcon_Validate (const char *pass) { - if (!strlen (pass->string)) + if (!strlen (pass)) return 0; - if (strcmp (Cmd_Argv (1), pass->string)) + if (strcmp (Cmd_Argv (1), pass)) return 0; return 1; @@ -992,8 +1305,8 @@ SVC_RemoteCommand (void) { const char *command; char *name; - qboolean admin_cmd = false; - qboolean do_cmd = false; + bool admin_cmd = false; + bool do_cmd = false; if (CheckForFlood (FLOOD_RCON)) return; @@ -1149,8 +1462,28 @@ typedef struct { #define MAX_IPFILTERS 1024 -cvar_t *filterban; -cvar_t *sv_filter_automask; +int filterban; +static cvar_t filterban_cvar = { + .name = "filterban", + .description = + "Determines the rules for the IP list 0 Only IP addresses on the Ban " + "list will be allowed onto the server, 1 Only IP addresses NOT on the " + "Ban list will be allowed onto the server", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &filterban }, +}; +int sv_filter_automask; +static cvar_t sv_filter_automask_cvar = { + .name = "sv_filter_automask", + .description = + "Automatically determine the mask length when it is not explicitely " + "given. e.g. \"addip 1.2.0.0\" would be the same as \"addip " + "1.2.0.0/16\"", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_filter_automask }, +}; int numipfilters; ipfilter_t ipfilters[MAX_IPFILTERS]; unsigned int ipmasks[33]; // network byte order @@ -1206,7 +1539,7 @@ SV_MaskIPTrim (byte *ip, int mask) } // assumes b has already been masked -static inline qboolean +static inline __attribute__((pure)) bool SV_MaskIPCompare (byte *a, byte *b, int mask) { int i; @@ -1229,7 +1562,7 @@ SV_MaskIPCompare (byte *a, byte *b, int mask) return true; } -static inline qboolean +static inline bool SV_IPCompare (byte *a, byte *b) { int i; @@ -1260,7 +1593,7 @@ SV_IPCopy (byte *dest, byte *src) ((unsigned int *)dest)[i] = ((unsigned int *)src)[i]; } -static qboolean +static bool SV_StringToFilter (const char *address, ipfilter_t *f) { #ifdef HAVE_IPV6 @@ -1317,7 +1650,7 @@ SV_StringToFilter (const char *address, ipfilter_t *f) // change trailing 0 segments to be a mask, eg 1.2.0.0 gives a /16 mask if (mask == -1) { - if (sv_filter_automask->int_val) { + if (sv_filter_automask) { mask = sizeof (b) * 8; i = sizeof (b); while (i > 0 && !b[i - 1]) { @@ -1430,7 +1763,7 @@ SV_AddIP_f (void) // FIXME: this should boot any matching clients for (i = 0; i < MAX_CLIENTS; i++) { client_t *cl = &svs.clients[i]; - char text[1024]; + const char *text; const char *typestr; char timestr[1024]; @@ -1461,9 +1794,9 @@ SV_AddIP_f (void) bantime / 60); else strncpy (timestr, "permanently", sizeof (timestr)); - snprintf (text, sizeof (text), "You are %s %s\n%s", - typestr, timestr, type == ft_ban ? "" : - "\nReconnecting won't help..."); + text = va (0, "You are %s %s\n%s", + typestr, timestr, type == ft_ban ? "" : + "\nReconnecting won't help..."); MSG_ReliableWrite_Begin (&cl->backbuf, svc_centerprint, strlen (text) + 2); MSG_ReliableWrite_String (&cl->backbuf, text); @@ -1566,7 +1899,7 @@ SV_netDoSexpire_f (void) for (i = 0; i < DOSFLOODCMDS; i++) SV_Printf ("%f ", netdosexpire[i]); SV_Printf ("\n"); - if (!sv_netdosprotect->int_val) + if (!sv_netdosprotect) SV_Printf ("(disabled)\n"); return; } @@ -1595,7 +1928,7 @@ SV_netDoSvalues_f (void) for (i = 0; i < DOSFLOODCMDS; i++) SV_Printf ("%f ", netdosvalues[i]); SV_Printf ("\n"); - if (!sv_netdosprotect->int_val) + if (!sv_netdosprotect) SV_Printf ("(disabled)\n"); return; } @@ -1614,33 +1947,6 @@ SV_netDoSvalues_f (void) return; } -static void -SV_MaxRate_f (cvar_t *var) -{ - client_t *cl; - int maxrate = var->int_val; - int i, rate = 2500; - const char *val; - - Cvar_Info (var); - for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { - if (!cl->userinfo) - continue; - val = Info_ValueForKey (cl->userinfo, "rate"); - if (strlen (val)) { - rate = atoi (val); - - if (maxrate) { - rate = bound (500, rate, maxrate); - } else { - rate = max (500, rate); - } - cl->netchan.rate = 1.0 / rate; - } - SV_ClientPrintf (1, cl, PRINT_HIGH, "Net rate set to %i\n", rate); - } -} - static void SV_SendBan (double till) { @@ -1670,7 +1976,7 @@ SV_SendBan (double till) // FIXME: this should send a disconnect to the client! } -static qboolean +static bool SV_FilterIP (byte *ip, double *until) { int i; @@ -1683,7 +1989,7 @@ SV_FilterIP (byte *ip, double *until) if (SV_MaskIPCompare (ip, ipfilters[i].ip, ipfilters[i].mask)) { if (!ipfilters[i].time) { // normal ban - return filterban->int_val; + return filterban; } else if (ipfilters[i].time > realtime) { *until = ipfilters[i].time; return true; // banned no matter what @@ -1694,7 +2000,7 @@ SV_FilterIP (byte *ip, double *until) } } } - return !filterban->int_val; + return !filterban; } void @@ -1746,6 +2052,32 @@ SV_RestorePenaltyFilter (client_t *cl, filtertype_t type) return 0.0; } +void +SV_OutOfBand (netadr_t adr, unsigned length, byte *data) +{ + if (net_packetlog) { + Log_Outgoing_Packet (data, length, 0); + } + Netchan_OutOfBand (adr, length, data); +} + +void +SV_OutOfBandPrint (netadr_t adr, const char *format, ...) +{ + static dstring_t *string; + va_list argptr; + + if (!string) { + string = dstring_new (); + } + + va_start (argptr, format); + dvsprintf (string, format, argptr); + va_end (argptr); + + SV_OutOfBand (adr, string->size - 1, (byte *) string->str); +} + static void SV_ReadPackets (void) { @@ -1756,9 +2088,9 @@ SV_ReadPackets (void) double until; while (NET_GetPacket ()) { - if (net_packetlog->int_val) + if (net_packetlog) Log_Incoming_Packet (net_message->message->data, - net_message->message->cursize, 1, 1); + net_message->message->cursize, 1); if (SV_FilterIP (net_from.ip, &until)) { SV_SendBan (until); // tell them we aren't listening... continue; @@ -1790,7 +2122,7 @@ SV_ReadPackets (void) if (cl->netchan.qport != qport) continue; if (cl->netchan.remote_address.port != net_from.port) { - Sys_MaskPrintf (SYS_DEV, "SV_ReadPackets: fixing up a " + Sys_MaskPrintf (SYS_dev, "SV_ReadPackets: fixing up a " "translated port\n"); cl->netchan.remote_address.port = net_from.port; } @@ -1834,7 +2166,7 @@ SV_CheckTimeouts (void) float droptime; int nclients, i; - droptime = realtime - sv_timeout->value; + droptime = realtime - sv_timeout; nclients = 0; for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { @@ -1849,7 +2181,7 @@ SV_CheckTimeouts (void) } } if (cl->state == cs_zombie && - realtime - cl->connection_started > zombietime->value) { + realtime - cl->connection_started > zombietime) { cl->state = cs_free; // can now be reused svs.num_clients--; } @@ -1870,10 +2202,10 @@ SV_CheckVars (void) static char const *pw, *spw; int v; - if (password->string == pw && spectator_password->string == spw) + if (password == pw && spectator_password == spw) return; - pw = password->string; - spw = spectator_password->string; + pw = password; + spw = spectator_password; v = 0; if (pw && pw[0] && strcmp (pw, "none")) @@ -1884,23 +2216,10 @@ SV_CheckVars (void) SV_Printf ("Updated needpass.\n"); if (!v) Info_SetValueForKey (svs.info, "needpass", "", - !sv_highchars->int_val); + !sv_highchars); else - Info_SetValueForKey (svs.info, "needpass", va ("%i", v), - !sv_highchars->int_val); -} - -/* - SV_GarbageCollect - - Run string GC on progs every pr_gc_interval frames - - //snax: run QFobject GC as well -*/ -static void -SV_GarbageCollect (void) -{ - //Object_Garbage_Collect (); + Info_SetValueForKey (svs.info, "needpass", va (0, "%i", v), + !sv_highchars); } void @@ -1936,12 +2255,12 @@ SV_Frame (float time) // don't bother running a frame if sys_ticrate seconds haven't passed sv_frametime = sv.time - old_time; if (sv_frametime < 0) { - old_time = sv.time - sv_mintic->value; - sv_frametime = sv_mintic->value; + old_time = sv.time - sv_mintic; + sv_frametime = sv_mintic; } - if (sv_frametime >= sv_mintic->value) { - if (sv_frametime > sv_maxtic->value) - sv_frametime = sv_maxtic->value; + if (sv_frametime >= sv_mintic) { + if (sv_frametime > sv_maxtic) + sv_frametime = sv_maxtic; old_time = sv.time; *sv_globals.frametime = sv_frametime; @@ -1978,8 +2297,6 @@ SV_Frame (float time) // send a heartbeat to the master if needed Master_Heartbeat (); - SV_GarbageCollect (); - // collect timing statistics end = Sys_DoubleTime (); svs.stats.active += end - start; @@ -1998,38 +2315,30 @@ SV_Frame (float time) } static void -maxspectators_f (cvar_t *var) +maxspectators_f (void *data, const cvar_t *cvar) { - if (var->int_val > MAX_CLIENTS) - Cvar_SetValue (var, maxclients->int_val); - else if (var->int_val + maxclients->int_val > MAX_CLIENTS) - Cvar_SetValue (var, MAX_CLIENTS - maxclients->int_val); - else if (var->int_val < 0) - Cvar_SetValue (var, 0); - else if (var->int_val != var->value) - Cvar_SetValue (var, var->int_val); - else - Cvar_Info (var); + if (maxspectators > MAX_CLIENTS) + maxspectators = maxclients; + else if (maxspectators + maxclients > MAX_CLIENTS) + maxspectators = MAX_CLIENTS - maxclients; + else if (maxspectators < 0) + maxspectators = 0; + Cvar_Info (data, cvar); } static void -maxclients_f (cvar_t *var) +maxclients_f (void *data, const cvar_t *cvar) { - if (var->int_val > MAX_CLIENTS) - Cvar_SetValue (var, MAX_CLIENTS); - else if (var->int_val < 1) - Cvar_SetValue (var, 1); - else if (var->int_val != var->value) - Cvar_SetValue (var, var->int_val); - else { - Cvar_Info (var); - if (maxspectators) - maxspectators_f (maxspectators); - } + if (maxclients > MAX_CLIENTS) + maxclients = MAX_CLIENTS; + else if (maxclients < 1) + maxclients = 1; + Cvar_Info (data, cvar); + maxspectators_f (0, &maxspectators_cvar); } static void -gamedir_f (int phase) +gamedir_f (int phase, void *data) { if (!phase) return; @@ -2046,173 +2355,47 @@ SV_InitLocal (void) int i; SV_UserInit (); + SV_Physics_Init_Cvars (); - rcon_password = Cvar_Get ("rcon_password", "", CVAR_NONE, NULL, "Set the " - "password for rcon 'root' commands"); - admin_password = Cvar_Get ("admin_password", "", CVAR_NONE, NULL, "Set " - "the password for rcon admin commands"); - password = Cvar_Get ("password", "", CVAR_NONE, NULL, "Set the server " - "password for players"); - spectator_password = Cvar_Get ("spectator_password", "", CVAR_NONE, NULL, - "Set the spectator password"); - sv_mintic = Cvar_Get ("sv_mintic", "0.03", CVAR_NONE, NULL, "The minimum " - "amount of time the server will wait before sending " - "packets to a client. Set to .5 to make modem users " - "happy"); - sv_maxtic = Cvar_Get ("sv_maxtic", "0.1", CVAR_NONE, NULL, "The maximum " - "amount of time in seconds before a client a " - "receives an update from the server"); - fraglimit = Cvar_Get ("fraglimit", "0", CVAR_SERVERINFO, Cvar_Info, - "Amount of frags a player must attain in order to " - "exit the level"); - timelimit = Cvar_Get ("timelimit", "0", CVAR_SERVERINFO, Cvar_Info, - "Sets the amount of time in minutes that is needed " - "before advancing to the next level"); - teamplay = Cvar_Get ("teamplay", "0", CVAR_SERVERINFO, Cvar_Info, - "Determines teamplay rules. 0 off, 1 You cannot hurt " - "yourself nor your teammates, 2 You can hurt " - "yourself, your teammates, and you will lose one " - "frag for killing a teammate, 3 You can hurt " - "yourself but you cannot hurt your teammates"); - samelevel = Cvar_Get ("samelevel", "0", CVAR_SERVERINFO, Cvar_Info, - "Determines the rules for level changing and " - "exiting. 0 Allows advancing to the next level," - "1 The same level will be played until someone " - "exits, 2 The same level will be played and the " - "exit will kill anybody that tries to exit, 3 The " - "same level will be played and the exit will kill " - "anybody that tries to exit, except on the Start " - "map."); - maxclients = Cvar_Get ("maxclients", "8", CVAR_SERVERINFO, maxclients_f, - "Sets how many clients can connect to your " - "server, this includes spectators and players"); - maxspectators = Cvar_Get ("maxspectators", "8", CVAR_SERVERINFO, - maxspectators_f, - "Sets how many spectators can connect to your " - "server. The maxclients value takes precedence " - "over this value so this value should always be " - "equal-to or less-then the maxclients value"); - hostname = Cvar_Get ("hostname", "unnamed", CVAR_SERVERINFO, Cvar_Info, - "Report or sets the server name"); - deathmatch = Cvar_Get ("deathmatch", "1", CVAR_SERVERINFO, Cvar_Info, - "Sets the rules for weapon and item respawning. " - "1 Does not leave weapons on the map. You can " - "pickup weapons and items and they will respawn, " - "2 Leaves weapons on the map. You can pick up a " - "weapon only once. Picked up items will not " - "respawn, 3 Leaves weapons on the map. You can " - "pick up a weapon only once. Picked up items will " - "respawn."); - coop = Cvar_Get ("coop", "0", CVAR_NONE, NULL, "co-op mode for progs that " - "support it"); - skill = Cvar_Get ("skill", "0", CVAR_NONE, NULL, "skill setting for progs " - "that support it"); - spawn = Cvar_Get ("spawn", "0", CVAR_SERVERINFO, Cvar_Info, - "Spawn the player entity"); - watervis = Cvar_Get ("watervis", "0", CVAR_SERVERINFO, Cvar_Info, - "Set nonzero to enable r_wateralpha on clients"); - sv_timeout = Cvar_Get ("timeout", "65", CVAR_NONE, NULL, "Sets the amount " - "of time in seconds before a client is considered " - "disconnected if the server does not receive a " - "packet"); - zombietime = Cvar_Get ("zombietime", "2", CVAR_NONE, NULL, "The number of " - "seconds that the server will keep the character " - "of a player on the map who seems to have " - "disconnected"); - sv_maxvelocity = Cvar_Get ("sv_maxvelocity", "2000", CVAR_NONE, NULL, - "Sets the maximum velocity an object can " - "travel"); - sv_extensions = Cvar_Get ("sv_extensions", "1", CVAR_NONE, NULL, - "Use protocol extensions for QuakeForge " - "clients"); - sv_gravity = Cvar_Get ("sv_gravity", "800", CVAR_NONE, NULL, - "Sets the global value for the amount of gravity"); - sv_jump_any = Cvar_Get ("sv_jump_any", "1", CVAR_NONE, NULL, "None"); - sv_stopspeed = Cvar_Get ("sv_stopspeed", "100", CVAR_NONE, NULL, - "Sets the value that determines how fast the " - "player should come to a complete stop"); - sv_maxspeed = Cvar_Get ("sv_maxspeed", "320", CVAR_NONE, NULL, - "Sets the maximum speed a player can move"); - sv_spectatormaxspeed = Cvar_Get ("sv_spectatormaxspeed", "500", CVAR_NONE, - NULL, "Sets the maximum speed a " - "spectator can move"); - sv_accelerate = Cvar_Get ("sv_accelerate", "10", CVAR_NONE, NULL, - "Sets the acceleration value for the players"); - sv_airaccelerate = Cvar_Get ("sv_airaccelerate", "0.7", CVAR_NONE, NULL, - "Sets how quickly the players accelerate in " - "air"); - sv_wateraccelerate = Cvar_Get ("sv_wateraccelerate", "10", CVAR_NONE, NULL, - "Sets the water acceleration value"); - sv_friction = Cvar_Get ("sv_friction", "4", CVAR_NONE, NULL, - "Sets the friction value for the players"); - sv_waterfriction = Cvar_Get ("sv_waterfriction", "4", CVAR_NONE, NULL, - "Sets the water friction value"); - sv_aim = Cvar_Get ("sv_aim", "2", CVAR_NONE, NULL, - "Sets the value for auto-aiming leniency"); - sv_minqfversion = Cvar_Get ("sv_minqfversion", "0", CVAR_SERVERINFO, - Cvar_Info, "Minimum QF version on client"); - sv_maxrate = Cvar_Get ("sv_maxrate", "10000", CVAR_SERVERINFO, SV_MaxRate_f, - "Maximum allowable rate"); - sv_allow_log = Cvar_Get ("sv_allow_log", "1", CVAR_NONE, NULL, - "Allow remote logging"); - sv_allow_status = Cvar_Get ("sv_allow_status", "1", CVAR_NONE, NULL, - "Allow remote status queries (qstat etc)"); - sv_allow_ping = Cvar_Get ("sv_allow_pings", "1", CVAR_NONE, NULL, - "Allow remote pings (qstat etc)"); - sv_netdosprotect = Cvar_Get ("sv_netdosprotect", "0", CVAR_NONE, NULL, - "DoS flood attack protection"); - sv_timestamps = Cvar_Get ("sv_timestamps", "0", CVAR_NONE, NULL, - "Time/date stamps in log entries"); - sv_timefmt = Cvar_Get ("sv_timefmt", "[%b %e %X] ", CVAR_NONE, NULL, - "Time/date format to use"); - filterban = Cvar_Get ("filterban", "1", CVAR_NONE, NULL, - "Determines the rules for the IP list " - "0 Only IP addresses on the Ban list will be " - "allowed onto the server, 1 Only IP addresses NOT " - "on the Ban list will be allowed onto the server"); - sv_filter_automask = Cvar_Get ("sv_filter_automask", "1", CVAR_NONE, NULL, - "Automatically determine the mask length " - "when it is not explicitely given. e.g. " - "\"addip 1.2.0.0\" would be the same as " - "\"addip 1.2.0.0/16\""); - allow_download = Cvar_Get ("allow_download", "1", CVAR_NONE, NULL, - "Toggle if clients can download game data from " - "the server"); - allow_download_skins = Cvar_Get ("allow_download_skins", "1", CVAR_NONE, - NULL, "Toggle if clients can download " - "skins from the server"); - allow_download_models = Cvar_Get ("allow_download_models", "1", CVAR_NONE, - NULL, "Toggle if clients can download " - "models from the server"); - allow_download_sounds = Cvar_Get ("allow_download_sounds", "1", CVAR_NONE, - NULL, "Toggle if clients can download " - "sounds from the server"); - allow_download_maps = Cvar_Get ("allow_download_maps", "1", CVAR_NONE, - NULL, "Toggle if clients can download " - "maps from the server"); - allow_download_demos = Cvar_Get ("allow_download_demos", "1", CVAR_NONE, - NULL, "Toggle if clients can download " - "maps from the server"); - sv_highchars = Cvar_Get ("sv_highchars", "1", CVAR_NONE, NULL, - "Toggle the use of high character color names " - "for players"); - sv_phs = Cvar_Get ("sv_phs", "1", CVAR_NONE, NULL, "Possibly Hearable " - "Set. If set to zero, the server calculates sound " - "hearability in realtime"); - pausable = Cvar_Get ("pausable", "1", CVAR_NONE, NULL, - "Toggle if server can be paused 1 is on, 0 is off"); - pr_gc = Cvar_Get ("pr_gc", "2", CVAR_NONE, NULL, "Enable/disable the " - "garbage collector. 0 is off, 1 is on, 2 is auto (on " - "for newer qfcc progs, off otherwise)"); - pr_gc_interval = Cvar_Get ("pr_gc_interval", "50", CVAR_NONE, NULL, - "Number of frames to wait before running " - "string garbage collector."); - pr_double_remove = Cvar_Get ("pr_double_remove", "1", CVAR_NONE, NULL, - "Handling of double entity remove. " - "0 is silently ignore, 1 prints a " - "traceback, and 2 gives an error.\n" - "works Only if debugging is available " - "and enabled"); + Cvar_Register (&rcon_password_cvar, 0, 0); + Cvar_Register (&admin_password_cvar, 0, 0); + Cvar_Register (&password_cvar, 0, 0); + Cvar_Register (&spectator_password_cvar, 0, 0); + Cvar_Register (&sv_mintic_cvar, 0, 0); + Cvar_Register (&sv_maxtic_cvar, 0, 0); + Cvar_Register (&fraglimit_cvar, Cvar_Info, &fraglimit); + Cvar_Register (&timelimit_cvar, Cvar_Info, &timelimit); + Cvar_Register (&teamplay_cvar, Cvar_Info, &teamplay); + Cvar_Register (&samelevel_cvar, 0, 0); + Cvar_Register (&maxclients_cvar, maxclients_f, 0); + Cvar_Register (&maxspectators_cvar, maxspectators_f, 0); + Cvar_Register (&hostname_cvar, 0, 0); + Cvar_Register (&deathmatch_cvar, 0, 0); + Cvar_Register (&coop_cvar, 0, 0); + Cvar_Register (&skill_cvar, 0, 0); + Cvar_Register (&spawn_cvar, Cvar_Info, &spawn); + Cvar_Register (&watervis_cvar, Cvar_Info, &watervis); + Cvar_Register (&sv_timeout_cvar, 0, 0); + Cvar_Register (&zombietime_cvar, 0, 0); + Cvar_Register (&sv_extensions_cvar, 0, 0); + Cvar_Register (&sv_minqfversion_cvar, Cvar_Info, &sv_minqfversion); + Cvar_Register (&sv_allow_log_cvar, 0, 0); + Cvar_Register (&sv_allow_status_cvar, 0, 0); + Cvar_Register (&sv_allow_ping_cvar, 0, 0); + Cvar_Register (&sv_netdosprotect_cvar, 0, 0); + Cvar_Register (&sv_timestamps_cvar, 0, 0); + Cvar_Register (&sv_timefmt_cvar, 0, 0); + Cvar_Register (&filterban_cvar, 0, 0); + Cvar_Register (&sv_filter_automask_cvar, 0, 0); + Cvar_Register (&allow_download_cvar, 0, 0); + Cvar_Register (&allow_download_skins_cvar, 0, 0); + Cvar_Register (&allow_download_models_cvar, 0, 0); + Cvar_Register (&allow_download_sounds_cvar, 0, 0); + Cvar_Register (&allow_download_maps_cvar, 0, 0); + Cvar_Register (&allow_download_demos_cvar, 0, 0); + Cvar_Register (&sv_highchars_cvar, 0, 0); + Cvar_Register (&sv_phs_cvar, 0, 0); + Cvar_Register (&pausable_cvar, 0, 0); // DoS protection Cmd_AddCommand ("netdosexpire", SV_netDoSexpire_f, "FIXME: part of DoS " "protection obviously, but I don't know what it does. No " @@ -2234,13 +2417,13 @@ SV_InitLocal (void) snprintf (localmodels[i], sizeof (localmodels[i]), "*%i", i); Info_SetValueForStarKey (svs.info, "*version", QW_VERSION, - !sv_highchars->int_val); + !sv_highchars); // Brand server as QF, with appropriate QSG standards version --KB Info_SetValueForStarKey (svs.info, "*qf_version", PACKAGE_VERSION, - !sv_highchars->int_val); + !sv_highchars); Info_SetValueForStarKey (svs.info, "*qsg_version", QW_QSG_VERSION, - !sv_highchars->int_val); + !sv_highchars); CF_Init (); @@ -2295,7 +2478,7 @@ Master_Heartbeat (void) } } -static inline qboolean +static inline bool iswhitespace (char c) { return c == ' ' || c == '\r' || c == '\n' || c == '\t'; @@ -2316,7 +2499,7 @@ SV_ExtractFromUserinfo (client_t *cl) char newname[MAX_NAME]; client_t *client; int i; - qboolean badname = false; + bool badname = false; // name from the info string val = Info_ValueForKey (cl->userinfo, "name"); @@ -2363,15 +2546,15 @@ SV_ExtractFromUserinfo (client_t *cl) // set the name if (strcmp (newname, val) || strcmp (cl->name, newname)) { Info_SetValueForKey (cl->userinfo, "name", newname, - !sv_highchars->int_val); + !sv_highchars); val = Info_ValueForKey (cl->userinfo, "name"); SVstring (cl->edict, netname) = PR_SetString (&sv_pr_state, newname); // If the new name was not set (due to the info string being too // long), drop the client. if (strcmp (val, newname)) { - Netchan_OutOfBandPrint (net_from, "%c\nPlease choose a " - "different name.\n", A2C_PRINT); + SV_OutOfBandPrint (net_from, "%c\nPlease choose a " + "different name.\n", A2C_PRINT); SV_ClientPrintf (1, cl, PRINT_HIGH, "Please choose a " "different name.\n"); SV_Printf ("Client %d kicked for having a invalid name\n", @@ -2407,8 +2590,8 @@ SV_ExtractFromUserinfo (client_t *cl) if (strlen (val)) { i = atoi (val); - if (sv_maxrate->int_val) { - i = bound (500, i, sv_maxrate->int_val); + if (sv_maxrate) { + i = bound (500, i, sv_maxrate); } else { i = max (500, i); } @@ -2442,42 +2625,39 @@ SV_InitNet (void) Netchan_Init (); net_realtime = &realtime; - Net_Log_Init (sv.sound_precache); + Net_Log_Init (sv.sound_precache, 1); svs.last_heartbeat = -99999; // send immediately sv_net_initialized = 1; } -static void +static memhunk_t * SV_Init_Memory (void) { int mem_parm = COM_CheckParm ("-mem"); - int mem_size; + size_t mem_size; void *mem_base; - sv_mem_size = Cvar_Get ("sv_mem_size", "8", CVAR_NONE, NULL, - "Amount of memory (in MB) to allocate for the " - PACKAGE_NAME " heap"); + Cvar_Register (&sv_mem_size_cvar, 0, 0); if (mem_parm) - Cvar_Set (sv_mem_size, com_argv[mem_parm + 1]); + Cvar_Set ("sv_mem_size", com_argv[mem_parm + 1]); if (COM_CheckParm ("-minmemory")) - Cvar_SetValue (sv_mem_size, MINIMUM_MEMORY / (1024 * 1024.0)); + sv_mem_size = MINIMUM_MEMORY / (1024 * 1024.0); - Cvar_SetFlags (sv_mem_size, sv_mem_size->flags | CVAR_ROM); - mem_size = (int) (sv_mem_size->value * 1024 * 1024); + mem_size = (size_t) (sv_mem_size * 1024 * 1024); if (mem_size < MINIMUM_MEMORY) Sys_Error ("Only %4.1f megs of memory reported, can't execute game", mem_size / (float) 0x100000); - mem_base = malloc (mem_size); + mem_base = Sys_Alloc (mem_size); if (!mem_base) - Sys_Error ("Can't allocate %d", mem_size); + Sys_Error ("Can't allocate %zd", mem_size); - Memory_Init (mem_base, mem_size); + return Memory_Init (mem_base, mem_size); } void @@ -2486,35 +2666,35 @@ SV_Init (void) sv_cbuf = Cbuf_New (&id_interp); sv_args = Cbuf_ArgsNew (); - Sys_RegisterShutdown (SV_Shutdown); + Sys_RegisterShutdown (SV_Shutdown, 0); Sys_Init (); GIB_Init (true); - COM_ParseConfig (); + COM_ParseConfig (sv_cbuf); - Cvar_Get ("cmd_warncmd", "1", CVAR_NONE, NULL, NULL); + cmd_warncmd = 1; // snax: Init experimental object system and run a test //Object_Init(); - SV_Init_Memory (); + memhunk_t *hunk = SV_Init_Memory (); - QFS_GamedirCallback (gamedir_f); + QFS_GamedirCallback (gamedir_f, 0); svs.maxclients = MAX_CLIENTS; svs.info = Info_ParseString ("", MAX_SERVERINFO_STRING, 0); localinfo = Info_ParseString ("", 0, 0); // unlimited SV_InitOperatorCommands (); SV_GIB_Init (); - QFS_Init ("qw"); + QFS_Init (hunk, "qw"); PI_Init (); - sv_console_plugin = Cvar_Get ("sv_console_plugin", "server", - CVAR_ROM, 0, "Plugin used for the console"); + Cvar_Register (&sv_console_plugin_cvar, 0, 0); PI_RegisterPlugins (server_plugin_list); - Con_Init (sv_console_plugin->string); + Con_Load (sv_console_plugin); if (con_module) con_module->data->console->cbuf = sv_cbuf; + Con_Init (); con_list_print = Sys_Printf; Sys_SetStdPrintf (SV_Print); Sys_SetErrPrintf (SV_Error); @@ -2523,8 +2703,6 @@ SV_Init (void) Mod_Init_Cvars (); Netchan_Init_Cvars (); Pmove_Init_Cvars (); - SV_Progs_Init_Cvars (); - PR_Init_Cvars (); // and now reprocess the cmdline's sets for overrides Cmd_StuffCmds (sv_cbuf); @@ -2534,7 +2712,6 @@ SV_Init (void) Game_Init (); - PR_Init (); SV_Progs_Init (); Mod_Init (); @@ -2546,15 +2723,15 @@ SV_Init (void) SVR_Init (); Demo_Init (); - Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); - host_hunklevel = Hunk_LowMark (); + Hunk_AllocName (0, 0, "-HOST_HUNKLEVEL-"); + host_hunklevel = Hunk_LowMark (0); Cbuf_InsertText (sv_cbuf, "exec server.cfg\n"); host_initialized = true; // SV_Printf ("Exe: "__TIME__" "__DATE__"\n"); - SV_Printf ("%4.1f megabyte heap\n", sv_mem_size->value); + SV_Printf ("%4.1f megabyte heap\n", sv_mem_size); SV_Printf ("\n"); SV_Printf ("%s server, Version %s (build %04d)\n", @@ -2565,7 +2742,7 @@ SV_Init (void) SV_Printf ("<==> %s initialized <==>\n", PACKAGE_NAME); // process command line arguments - Cmd_Exec_File (sv_cbuf, fs_usercfg->string, 0); + Cmd_Exec_File (sv_cbuf, fs_usercfg, 0); Cmd_StuffCmds (sv_cbuf); Cbuf_Execute_Stack (sv_cbuf); diff --git a/qw/source/sv_move.c b/qw/source/sv_move.c index 013e4d48e..376c8389b 100644 --- a/qw/source/sv_move.c +++ b/qw/source/sv_move.c @@ -28,8 +28,8 @@ # include "config.h" #endif -#include "server.h" -#include "sv_progs.h" +#include "qw/include/server.h" +#include "qw/include/sv_progs.h" #include "world.h" #define STEPSIZE 18 @@ -43,7 +43,7 @@ int c_yes, c_no; Returns false if any part of the bottom of the entity is off an edge that is not a staircase. */ -qboolean +bool SV_CheckBottom (edict_t *ent) { float mid, bottom; @@ -112,8 +112,8 @@ SV_CheckBottom (edict_t *ent) possible, no move is done, false is returned, and pr_global_struct->trace_normal is set to the normal of the blocking wall */ -qboolean -SV_movestep (edict_t *ent, const vec3_t move, qboolean relink) +bool +SV_movestep (edict_t *ent, const vec3_t move, bool relink) { edict_t *enemy; float dz; @@ -221,14 +221,14 @@ SV_movestep (edict_t *ent, const vec3_t move, qboolean relink) Turns to the movement direction, and walks the current distance if facing it. */ -static qboolean +static bool SV_StepDirection (edict_t *ent, float yaw, float dist) { float delta; vec3_t move, oldorigin; SVfloat (ent, ideal_yaw) = yaw; - PF_changeyaw (&sv_pr_state); + PF_changeyaw (&sv_pr_state, 0); yaw = yaw * M_PI * 2 / 360; move[0] = cos (yaw) * dist; @@ -293,7 +293,7 @@ SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist) return; } // try other directions - if (((rand () & 3) & 1) || abs (deltay) > abs (deltax)) { + if (((rand () & 3) & 1) || fabsf (deltay) > fabsf (deltax)) { tdir = d[1]; d[1] = d[2]; d[2] = tdir; @@ -330,7 +330,7 @@ SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist) SV_FixCheckBottom (actor); } -static qboolean +static bool SV_CloseEnough (edict_t *ent, edict_t *goal, float dist) { int i; @@ -345,7 +345,7 @@ SV_CloseEnough (edict_t *ent, edict_t *goal, float dist) } void -SV_MoveToGoal (progs_t *pr) +SV_MoveToGoal (progs_t *pr, void *data) { edict_t *ent, *goal; float dist; diff --git a/qw/source/sv_phys.c b/qw/source/sv_phys.c index e2d08e814..6b9d51bce 100644 --- a/qw/source/sv_phys.c +++ b/qw/source/sv_phys.c @@ -31,8 +31,8 @@ #include "QF/cvar.h" #include "QF/sys.h" -#include "server.h" -#include "sv_progs.h" +#include "qw/include/server.h" +#include "qw/include/sv_progs.h" #include "world.h" /* @@ -53,11 +53,52 @@ solid_edge items clip against only bsp models. */ -cvar_t *sv_friction; -cvar_t *sv_gravity; -cvar_t *sv_jump_any; -cvar_t *sv_maxvelocity; -cvar_t *sv_stopspeed; +float sv_friction; +static cvar_t sv_friction_cvar = { + .name = "sv_friction", + .description = + "Sets the friction value for the players", + .default_value = "4", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_float, .value = &sv_friction }, +}; +float sv_gravity; +static cvar_t sv_gravity_cvar = { + .name = "sv_gravity", + .description = + "Sets the global value for the amount of gravity", + .default_value = "800", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_float, .value = &sv_gravity }, +}; +int sv_jump_any; +static cvar_t sv_jump_any_cvar = { + .name = "sv_jump_any", + .description = + "None", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_jump_any }, +}; +float sv_maxvelocity; +static cvar_t sv_maxvelocity_cvar = { + .name = "sv_maxvelocity", + .description = + "Sets the maximum velocity an object can travel", + .default_value = "2000", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_maxvelocity }, +}; +float sv_stopspeed; +static cvar_t sv_stopspeed_cvar = { + .name = "sv_stopspeed", + .description = + "Sets the value that determines how fast the player should come to a " + "complete stop", + .default_value = "100", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_stopspeed }, +}; #define MOVE_EPSILON 0.01 #if 0 @@ -87,18 +128,17 @@ void SV_CheckVelocity (edict_t *ent) { float wishspeed; -// int i; // bound velocity #if 0 - for (i = 0; i < 3; i++) { - if (IS_NAN (SVvector (ent, velocity)[i])) { + for (int i = 0; i < 3; i++) { + if (isnan (SVvector (ent, velocity)[i])) { Sys_Printf ("Got a NaN velocity on %s\n", PR_GetString (&sv_pr_state, SVstring (ent, classname))); SVvector (ent, velocity)[i] = 0; } - if (IS_NAN (SVvector (ent, origin)[i])) { + if (isnan (SVvector (ent, origin)[i])) { Sys_Printf ("Got a NaN origin on %s\n", PR_GetString (&sv_pr_state, SVstring (ent, classname))); @@ -107,8 +147,8 @@ SV_CheckVelocity (edict_t *ent) } #endif wishspeed = VectorLength (SVvector (ent, velocity)); - if (wishspeed > sv_maxvelocity->value) { - VectorScale (SVvector (ent, velocity), sv_maxvelocity->value / + if (wishspeed > sv_maxvelocity) { + VectorScale (SVvector (ent, velocity), sv_maxvelocity / wishspeed, SVvector (ent, velocity)); } } @@ -121,7 +161,7 @@ SV_CheckVelocity (edict_t *ent) in a frame. Not used for pushmove objects, because they must be exact. Returns false if the entity removed itself. */ -qboolean +bool SV_RunThink (edict_t *ent) { float thinktime; @@ -208,7 +248,7 @@ SV_EntCanSupportJump (edict_t *ent) int solid = SVfloat (ent, solid); if (solid == SOLID_BSP) return 1; - if (!sv_jump_any->int_val) + if (!sv_jump_any) return 0; if (solid == SOLID_NOT || solid == SOLID_SLIDEBOX) return 0; @@ -352,7 +392,7 @@ SV_AddGravity (edict_t *ent) ent_grav = SVfloat (ent, gravity); else ent_grav = 1.0; - SVvector (ent, velocity)[2] -= ent_grav * sv_gravity->value * sv_frametime; + SVvector (ent, velocity)[2] -= ent_grav * sv_gravity * sv_frametime; SVdata (ent)->add_grav = true; } @@ -365,7 +405,7 @@ SV_FinishGravity (edict_t *ent, vec3_t move) ent_grav = SVfloat (ent, gravity); else ent_grav = 1.0; - ent_grav *= sv_gravity->value; + ent_grav *= sv_gravity; move[2] += ent_grav * sv_frametime * sv_frametime / 2; } @@ -413,11 +453,11 @@ SV_PushEntity (edict_t *ent, vec3_t push, unsigned traceflags) return trace; } -static qboolean +static bool SV_Push (edict_t *pusher, const vec3_t tmove, const vec3_t amove) { float solid_save; - int num_moved, i, e; + int num_moved, i; edict_t *check, *block; edict_t **moved_edict; vec3_t move, org, org2; @@ -426,7 +466,7 @@ SV_Push (edict_t *pusher, const vec3_t tmove, const vec3_t amove) vec3_t forward = {1, 0, 0}; vec3_t left = {0, 1, 0}; vec3_t up = {0, 0, 1}; - int mark; + size_t mark; int c_flags, c_movetype, c_groundentity, c_solid; vec_t *c_absmin, *c_absmax, *c_origin, *c_angles, *c_mins, *c_maxs; vec_t *p_origin, *p_angles; @@ -451,14 +491,14 @@ SV_Push (edict_t *pusher, const vec3_t tmove, const vec3_t amove) VectorAdd (p_angles, amove, p_angles); SV_LinkEdict (pusher, false); - mark = Hunk_LowMark (); - moved_edict = Hunk_Alloc (sv.num_edicts * sizeof (edict_t *)); - moved_from = Hunk_Alloc (sv.num_edicts * sizeof (vec_t)); + mark = Hunk_LowMark (0); + moved_edict = Hunk_Alloc (0, sv.num_edicts * sizeof (edict_t *)); + moved_from = Hunk_Alloc (0, sv.num_edicts * sizeof (vec_t)); // see if any solid entities are inside the final position num_moved = 0; check = NEXT_EDICT (&sv_pr_state, sv.edicts); - for (e = 1; e < sv.num_edicts; + for (unsigned e = 1; e < sv.num_edicts; e++, check = NEXT_EDICT (&sv_pr_state, check)) { if (check->free) continue; @@ -487,8 +527,8 @@ SV_Push (edict_t *pusher, const vec3_t tmove, const vec3_t amove) // entity? c_absmin = SVvector (check, absmin); c_absmax = SVvector (check, absmax); - if (VectorCompCompare (c_absmin, >=, maxs) - || VectorCompCompare (c_absmax, <=, mins)) + if (VectorCompCompareAll (c_absmin, >=, maxs) + || VectorCompCompareAll (c_absmax, <=, mins)) continue; if (!SV_TestEntityPosition (check)) @@ -553,10 +593,10 @@ SV_Push (edict_t *pusher, const vec3_t tmove, const vec3_t amove) VectorSubtract (m_angles, amove, m_angles); SV_LinkEdict (moved_edict[i], false); } - Hunk_FreeToLowMark (mark); + Hunk_FreeToLowMark (0, mark); return false; } - Hunk_FreeToLowMark (mark); + Hunk_FreeToLowMark (0, mark); return true; } @@ -727,7 +767,7 @@ SV_Physics_Toss (edict_t *ent) } fl = 0; - if (sv_antilag->int_val == 2) + if (sv_antilag == 2) fl |= MOVE_LAGGED; trace = SV_PushEntity (ent, move, fl); if (trace.fraction == 1) @@ -773,11 +813,11 @@ SV_Physics_Toss (edict_t *ent) static void SV_Physics_Step (edict_t *ent) { - qboolean hitsound; + bool hitsound; // freefall if not on ground if (!((int) SVfloat (ent, flags) & (FL_ONGROUND | FL_FLY | FL_SWIM))) { - if (SVvector (ent, velocity)[2] < sv_gravity->value * -0.1) + if (SVvector (ent, velocity)[2] < sv_gravity * -0.1) hitsound = true; else hitsound = false; @@ -862,14 +902,14 @@ void SV_Physics (void) { edict_t *ent; - int i; SV_ProgStartFrame (); // treat each object in turn // even the world gets a chance to think ent = sv.edicts; - for (i = 0; i < sv.num_edicts; i++, ent = NEXT_EDICT (&sv_pr_state, ent)) { + for (unsigned i = 0; i < sv.num_edicts; + i++, ent = NEXT_EDICT (&sv_pr_state, ent)) { if (ent->free) continue; @@ -898,3 +938,13 @@ SV_Physics (void) PR_ExecuteProgram (&sv_pr_state, sv_funcs.EndFrame); } } + +void +SV_Physics_Init_Cvars (void) +{ + Cvar_Register (&sv_friction_cvar, Cvar_Info, &sv_friction); + Cvar_Register (&sv_gravity_cvar, Cvar_Info, &sv_gravity); + Cvar_Register (&sv_jump_any_cvar, 0, 0); + Cvar_Register (&sv_maxvelocity_cvar, 0, 0); + Cvar_Register (&sv_stopspeed_cvar, 0, 0); +} diff --git a/qw/source/sv_pr_cmds.c b/qw/source/sv_pr_cmds.c index 4f832e5a2..7075bc6cf 100644 --- a/qw/source/sv_pr_cmds.c +++ b/qw/source/sv_pr_cmds.c @@ -42,15 +42,17 @@ #include "QF/cvar.h" #include "QF/msg.h" #include "QF/ruamoko.h" +#include "QF/set.h" #include "QF/sys.h" #include "QF/va.h" #include "compat.h" -#include "crudefile.h" -#include "server.h" -#include "sv_gib.h" -#include "sv_progs.h" -#include "sv_recorder.h" + +#include "qw/include/crudefile.h" +#include "qw/include/server.h" +#include "qw/include/sv_gib.h" +#include "qw/include/sv_progs.h" +#include "qw/include/sv_recorder.h" #include "world.h" /* BUILT-IN FUNCTIONS */ @@ -66,16 +68,16 @@ // void (string e) error */ static void -PF_error (progs_t *pr) +PF_error (progs_t *pr, void *data) { const char *s; edict_t *ed; - s = PF_VarString (pr, 0); + s = PF_VarString (pr, 0, 1); Sys_Printf ("======SERVER ERROR in %s:\n%s\n", - PR_GetString (pr, pr->pr_xfunction->descriptor->s_name), s); + PR_GetString (pr, pr->pr_xfunction->descriptor->name), s); ed = PROG_TO_EDICT (pr, *sv_globals.self); - ED_Print (pr, ed); + ED_Print (pr, ed, 0); Sys_Error ("Program error"); } @@ -90,16 +92,16 @@ PF_error (progs_t *pr) // void (string e) objerror */ static void -PF_objerror (progs_t *pr) +PF_objerror (progs_t *pr, void *data) { const char *s; edict_t *ed; - s = PF_VarString (pr, 0); + s = PF_VarString (pr, 0, 1); Sys_Printf ("======OBJECT ERROR in %s:\n%s\n", - PR_GetString (pr, pr->pr_xfunction->descriptor->s_name), s); + PR_GetString (pr, pr->pr_xfunction->descriptor->name), s); ed = PROG_TO_EDICT (pr, *sv_globals.self); - ED_Print (pr, ed); + ED_Print (pr, ed, 0); ED_Free (pr, ed); PR_RunError (pr, "object error"); @@ -112,7 +114,7 @@ PF_objerror (progs_t *pr) void (vector angles) makevectors */ static void -PF_makevectors (progs_t *pr) +PF_makevectors (progs_t *pr, void *data) { AngleVectors (P_VECTOR (pr, 0), *sv_globals.v_forward, *sv_globals.v_right, *sv_globals.v_up); @@ -131,7 +133,7 @@ PF_makevectors (progs_t *pr) // void (entity e, vector o) setorigin */ static void -PF_setorigin (progs_t *pr) +PF_setorigin (progs_t *pr, void *data) { edict_t *e; float *org; @@ -151,7 +153,7 @@ PF_setorigin (progs_t *pr) // void (entity e, vector min, vector max) setsize */ static void -PF_setsize (progs_t *pr) +PF_setsize (progs_t *pr, void *data) { edict_t *e; float *min, *max; @@ -173,7 +175,7 @@ PF_setsize (progs_t *pr) Also sets size, mins, and maxs for inline bmodels */ static void -PF_setmodel (progs_t *pr) +PF_setmodel (progs_t *pr, void *data) { edict_t *e; const char *m, **check; @@ -213,14 +215,14 @@ PF_setmodel (progs_t *pr) // void (string s) bprint */ static void -PF_bprint (progs_t *pr) +PF_bprint (progs_t *pr, void *data) { const char *s; int level; level = P_FLOAT (pr, 0); - s = PF_VarString (pr, 1); + s = PF_VarString (pr, 1, 2); SV_BroadcastPrintf (level, "%s", s); } @@ -233,7 +235,7 @@ PF_bprint (progs_t *pr) // void (entity client, string s) sprint */ static void -PF_sprint (progs_t *pr) +PF_sprint (progs_t *pr, void *data) { const char *s; client_t *client; @@ -252,7 +254,7 @@ PF_sprint (progs_t *pr) if (client->state == cs_server) //FIXME record to mvd? return; - s = PF_VarString (pr, 2); + s = PF_VarString (pr, 2, 3); SV_ClientPrintf (1, client, level, "%s", s); } @@ -266,7 +268,7 @@ PF_sprint (progs_t *pr) // void (...) centerprint */ static void -PF_centerprint (progs_t *pr) +PF_centerprint (progs_t *pr, void *data) { const char *s; client_t *cl; @@ -284,7 +286,7 @@ PF_centerprint (progs_t *pr) if (cl->state == cs_server) //FIXME record to mvd? return; - s = PF_VarString (pr, 1); + s = PF_VarString (pr, 1, 2); MSG_ReliableWrite_Begin (&cl->backbuf, svc_centerprint, 2 + strlen (s)); MSG_ReliableWrite_String (&cl->backbuf, s); @@ -302,7 +304,7 @@ PF_centerprint (progs_t *pr) // void (vector pos, string samp, float vol, float atten) ambientsound */ static void -PF_ambientsound (progs_t *pr) +PF_ambientsound (progs_t *pr, void *data) { const char **check; const char *samp; @@ -348,7 +350,7 @@ PF_ambientsound (progs_t *pr) // void (entity e, float chan, string samp) sound */ static void -PF_sound (progs_t *pr) +PF_sound (progs_t *pr, void *data) { const char *sample; edict_t *entity; @@ -375,7 +377,7 @@ PF_sound (progs_t *pr) // float (vector v1, vector v2, float tryents) traceline */ static void -PF_traceline (progs_t *pr) +PF_traceline (progs_t *pr, void *data) { float *v1, *v2; edict_t *ent; @@ -387,7 +389,7 @@ PF_traceline (progs_t *pr) nomonsters = P_FLOAT (pr, 2); ent = P_EDICT (pr, 3); - if (sv_antilag->int_val == 2) + if (sv_antilag == 2) nomonsters |= MOVE_LAGGED; trace = SV_Move (v1, vec3_origin, vec3_origin, v2, nomonsters, ent); @@ -416,7 +418,7 @@ PF_traceline (progs_t *pr) redundant. */ static void -PF_tracebox (progs_t *pr) +PF_tracebox (progs_t *pr, void *data) { edict_t *ent; float *start, *end, *mins, *maxs; @@ -455,20 +457,18 @@ PF_tracebox (progs_t *pr) scalar checkpos (entity, vector) */ static void __attribute__ ((used)) -PF_checkpos (progs_t *pr) +PF_checkpos (progs_t *pr, void *data) { } -byte checkpvs[MAX_MAP_LEAFS / 8]; +set_t *checkpvs; static int PF_newcheckclient (progs_t *pr, int check) { - byte *pvs; edict_t *ent; int i; mleaf_t *leaf; - vec3_t org; // cycle to the next one if (check < 1) @@ -502,10 +502,14 @@ PF_newcheckclient (progs_t *pr, int check) } // get the PVS for the entity + vec4f_t org; VectorAdd (SVvector (ent, origin), SVvector (ent, view_ofs), org); + org[3] = 1; leaf = Mod_PointInLeaf (org, sv.worldmodel); - pvs = Mod_LeafPVS (leaf, sv.worldmodel); - memcpy (checkpvs, pvs, (sv.worldmodel->numleafs + 7) >> 3); + if (!checkpvs) { + checkpvs = set_new_size (sv.worldmodel->brush.visleafs); + } + set_assign (checkpvs, Mod_LeafPVS (leaf, sv.worldmodel)); return i; } @@ -528,12 +532,11 @@ int c_invis, c_notvis; // entity () clientlist */ static void -PF_checkclient (progs_t *pr) +PF_checkclient (progs_t *pr, void *data) { edict_t *ent, *self; int l; mleaf_t *leaf; - vec3_t view; // find a new check if on a new frame if (sv.time - sv.lastchecktime >= 0.1) { @@ -548,10 +551,12 @@ PF_checkclient (progs_t *pr) } // if current entity can't possibly see the check entity, return 0 self = PROG_TO_EDICT (pr, *sv_globals.self); + vec4f_t view; VectorAdd (SVvector (self, origin), SVvector (self, view_ofs), view); + view[3] = 1; leaf = Mod_PointInLeaf (view, sv.worldmodel); - l = (leaf - sv.worldmodel->leafs) - 1; - if ((l < 0) || !(checkpvs[l >> 3] & (1 << (l & 7)))) { + l = (leaf - sv.worldmodel->brush.leafs) - 1; + if (!set_is_member (checkpvs, l)) { c_notvis++; RETURN_EDICT (pr, sv.edicts); return; @@ -570,7 +575,7 @@ PF_checkclient (progs_t *pr) // void (entity client, string s) stuffcmd */ static void -PF_stuffcmd (progs_t *pr) +PF_stuffcmd (progs_t *pr, void *data) { const char *str; char *buf, *p; @@ -628,7 +633,7 @@ PF_stuffcmd (progs_t *pr) // void (string s) localcmd */ static void -PF_localcmd (progs_t *pr) +PF_localcmd (progs_t *pr, void *data) { const char *str; @@ -645,12 +650,11 @@ PF_localcmd (progs_t *pr) // entity (vector org, float rad) findradius */ static void -PF_findradius (progs_t *pr) +PF_findradius (progs_t *pr, void *data) { edict_t *ent, *chain; float rsqr; vec_t *emins, *emaxs, *org; - int i, j; vec3_t eorg; chain = (edict_t *) sv.edicts; @@ -660,7 +664,7 @@ PF_findradius (progs_t *pr) rsqr *= rsqr; // Square early, sqrt never ent = NEXT_EDICT (pr, sv.edicts); - for (i = 1; i < sv.num_edicts; i++, ent = NEXT_EDICT (pr, ent)) { + for (unsigned i = 1; i < sv.num_edicts; i++, ent = NEXT_EDICT (pr, ent)) { if (ent->free) continue; if (SVfloat (ent, solid) == SOLID_NOT @@ -668,7 +672,7 @@ PF_findradius (progs_t *pr) continue; emins = SVvector (ent, absmin); emaxs = SVvector (ent, absmax); - for (j = 0; j < 3; j++) + for (int j = 0; j < 3; j++) eorg[j] = org[j] - 0.5 * (emins[j] + emaxs[j]); if (DotProduct (eorg, eorg) > rsqr) continue; @@ -682,7 +686,7 @@ PF_findradius (progs_t *pr) // entity () spawn static void -PF_Spawn (progs_t *pr) +PF_spawn (progs_t *pr, void *data) { edict_t *ed; @@ -690,24 +694,22 @@ PF_Spawn (progs_t *pr) RETURN_EDICT (pr, ed); } -cvar_t *pr_double_remove; - // void (entity e) remove static void -PF_Remove (progs_t *pr) +PF_remove (progs_t *pr, void *data) { edict_t *ed; ed = P_EDICT (pr, 0); if (NUM_FOR_EDICT (pr, ed) < *pr->reserved_edicts) { - if (pr_double_remove->int_val == 1) { + if (pr_double_remove == 1) { PR_DumpState (pr); Sys_Printf ("Reserved entity remove\n"); } else // == 2 PR_RunError (pr, "Reserved entity remove\n"); } - if (ed->free && pr_double_remove->int_val) { - if (pr_double_remove->int_val == 1) { + if (ed->free && pr_double_remove) { + if (pr_double_remove == 1) { PR_DumpState (pr); Sys_Printf ("Double entity remove\n"); } else // == 2 @@ -736,7 +738,7 @@ do_precache (progs_t *pr, const char **cache, int max, const char *name, PR_CheckEmptyString (pr, name); - s = Hunk_TempAlloc (strlen (name) + 1); + s = Hunk_TempAlloc (0, strlen (name) + 1); for (i = 0; *name; i++, name++) { int c = (byte) *name; s[i] = tolower (c); @@ -745,10 +747,10 @@ do_precache (progs_t *pr, const char **cache, int max, const char *name, for (i = 0; i < max; i++) { if (!cache[i]) { - char *c = Hunk_Alloc (strlen (s) + 1); + char *c = Hunk_Alloc (0, strlen (s) + 1); strcpy (c, s); cache[i] = c; // blah, const - Sys_MaskPrintf (SYS_DEV, "%s: %3d %s\n", func, i, s); + Sys_MaskPrintf (SYS_dev, "%s: %3d %s\n", func, i, s); return; } if (!strcmp (cache[i], s)) @@ -760,31 +762,34 @@ do_precache (progs_t *pr, const char **cache, int max, const char *name, // string (string s) precache_file // string (string s) precache_file2 static void -PF_precache_file (progs_t *pr) +PF_precache_file (progs_t *pr, void *data) { // precache_file is used only to copy files with qcc, it does nothing R_INT (pr) = P_INT (pr, 0); } +#define PF_precache_file2 PF_precache_file // void (string s) precache_sound // string (string s) precache_sound2 static void -PF_precache_sound (progs_t *pr) +PF_precache_sound (progs_t *pr, void *data) { do_precache (pr, sv.sound_precache, MAX_SOUNDS, P_GSTRING (pr, 0), "precache_sound"); R_INT (pr) = P_INT (pr, 0); } +#define PF_precache_sound2 PF_precache_sound // void (string s) precache_model // string (string s) precache_model2 static void -PF_precache_model (progs_t *pr) +PF_precache_model (progs_t *pr, void *data) { do_precache (pr, sv.model_precache, MAX_MODELS, P_GSTRING (pr, 0), "precache_model"); R_INT (pr) = P_INT (pr, 0); } +#define PF_precache_model2 PF_precache_model /* PF_walkmove @@ -793,7 +798,7 @@ PF_precache_model (progs_t *pr) // float (float yaw, float dist) walkmove */ static void -PF_walkmove (progs_t *pr) +PF_walkmove (progs_t *pr, void *data) { edict_t *ent; float yaw, dist; @@ -831,7 +836,7 @@ PF_walkmove (progs_t *pr) // float () droptofloor */ static void -PF_droptofloor (progs_t *pr) +PF_droptofloor (progs_t *pr, void *data) { edict_t *ent; trace_t trace; @@ -863,7 +868,7 @@ PF_droptofloor (progs_t *pr) // void (float style, string value) lightstyle */ static void -PF_lightstyle (progs_t *pr) +PF_lightstyle (progs_t *pr, void *data) { const char *val; client_t *cl; @@ -897,7 +902,7 @@ PF_lightstyle (progs_t *pr) // float (entity e) checkbottom static void -PF_checkbottom (progs_t *pr) +PF_checkbottom (progs_t *pr, void *data) { edict_t *ent; @@ -908,7 +913,7 @@ PF_checkbottom (progs_t *pr) // float (vector v) pointcontents static void -PF_pointcontents (progs_t *pr) +PF_pointcontents (progs_t *pr, void *data) { float *v; @@ -917,8 +922,6 @@ PF_pointcontents (progs_t *pr) R_FLOAT (pr) = SV_PointContents (v); } -cvar_t *sv_aim; - /* PF_aim @@ -927,17 +930,17 @@ cvar_t *sv_aim; // vector (entity e, float speed) aim */ static void -PF_aim (progs_t *pr) +PF_aim (progs_t *pr, void *data) { const char *noaim; edict_t *ent, *check, *bestent; float dist, bestdist, speed; float *mins, *maxs, *org; - int i, j; + unsigned i, j; trace_t tr; vec3_t start, dir, end, bestdir; - if (sv_aim->value >= 1.0) { + if (sv_aim >= 1.0) { VectorCopy (*sv_globals.v_forward, R_VECTOR (pr)); return; } @@ -964,7 +967,7 @@ PF_aim (progs_t *pr) VectorMultAdd (start, 2048, dir, end); tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent); if (tr.ent && SVfloat (tr.ent, takedamage) == DAMAGE_AIM - && (!teamplay->int_val || SVfloat (ent, team) <= 0 + && (!teamplay || SVfloat (ent, team) <= 0 || SVfloat (ent, team) != SVfloat (tr.ent, team))) { VectorCopy (*sv_globals.v_forward, R_VECTOR (pr)); return; @@ -972,7 +975,7 @@ PF_aim (progs_t *pr) // try all possible entities VectorCopy (dir, bestdir); - bestdist = sv_aim->value; + bestdist = sv_aim; bestent = NULL; check = NEXT_EDICT (pr, sv.edicts); @@ -981,7 +984,7 @@ PF_aim (progs_t *pr) continue; if (check == ent) continue; - if (teamplay->int_val && SVfloat (ent, team) > 0 + if (teamplay && SVfloat (ent, team) > 0 && SVfloat (ent, team) == SVfloat (check, team)) continue; // don't aim at teammate @@ -1022,7 +1025,7 @@ PF_aim (progs_t *pr) // void () ChangeYaw */ void -PF_changeyaw (progs_t *pr) +PF_changeyaw (progs_t *pr, void *data) { edict_t *ent; float ideal, current, move, speed; @@ -1061,7 +1064,7 @@ PF_changeyaw (progs_t *pr) #define MSG_INIT 3 // write to the init string #define MSG_MULTICAST 4 // for multicast () -static sizebuf_t * +static __attribute__((pure)) sizebuf_t * WriteDest (progs_t *pr) { int dest; @@ -1094,7 +1097,7 @@ WriteDest (progs_t *pr) return NULL; } -static client_t * +static __attribute__((pure)) client_t * Write_GetClient (progs_t *pr) { edict_t *ent; @@ -1109,14 +1112,27 @@ Write_GetClient (progs_t *pr) // void (float to, ...) WriteBytes static void -PF_WriteBytes (progs_t *pr) +PF_WriteBytes (progs_t *pr, void *data) { int i, p; - int count = pr->pr_argc - 1; - byte buf[MAX_PARMS]; + byte buf[PR_MAX_PARAMS]; + int argc = pr->pr_argc - 1; + pr_type_t **argv = pr->pr_params + 1; - for (i = 0; i < count; i++) { - p = P_FLOAT (pr, i + 1); + if (pr->progs->version == PROG_VERSION) { + __auto_type va_list = &P_PACKED (pr, pr_va_list_t, 1); + argc = va_list->count; + if (argc) { + argv = alloca (argc * sizeof (pr_type_t *)); + for (int i = 0; i < argc; i++) { + argv[i] = &pr->pr_globals[va_list->list + i * 4]; + } + } else { + argv = 0; + } + } + for (i = 0; i < argc; i++) { + p = PR_PTR (float, argv[i]); buf[i] = p; } @@ -1124,23 +1140,23 @@ PF_WriteBytes (progs_t *pr) client_t *cl = Write_GetClient (pr); if (cl->state != cs_server) { - MSG_ReliableCheckBlock (&cl->backbuf, count); - MSG_ReliableWrite_SZ (&cl->backbuf, buf, count); + MSG_ReliableCheckBlock (&cl->backbuf, argc); + MSG_ReliableWrite_SZ (&cl->backbuf, buf, argc); } if (sv.recorders) { sizebuf_t *dbuf; - dbuf = SVR_WriteBegin (dem_single, cl - svs.clients, count); - SZ_Write (dbuf, buf, count); + dbuf = SVR_WriteBegin (dem_single, cl - svs.clients, argc); + SZ_Write (dbuf, buf, argc); } } else { sizebuf_t *msg = WriteDest (pr); - SZ_Write (msg, buf, count); + SZ_Write (msg, buf, argc); } } // void (float to, float f) WriteByte static void -PF_WriteByte (progs_t *pr) +PF_WriteByte (progs_t *pr, void *data) { if (P_FLOAT (pr, 0) == MSG_ONE) { client_t *cl = Write_GetClient (pr); @@ -1160,7 +1176,7 @@ PF_WriteByte (progs_t *pr) // void (float to, float f) WriteChar static void -PF_WriteChar (progs_t *pr) +PF_WriteChar (progs_t *pr, void *data) { if (P_FLOAT (pr, 0) == MSG_ONE) { client_t *cl = Write_GetClient (pr); @@ -1180,7 +1196,7 @@ PF_WriteChar (progs_t *pr) // void (float to, float f) WriteShort static void -PF_WriteShort (progs_t *pr) +PF_WriteShort (progs_t *pr, void *data) { if (P_FLOAT (pr, 0) == MSG_ONE) { client_t *cl = Write_GetClient (pr); @@ -1200,7 +1216,7 @@ PF_WriteShort (progs_t *pr) // void (float to, float f) WriteLong static void -PF_WriteLong (progs_t *pr) +PF_WriteLong (progs_t *pr, void *data) { if (P_FLOAT (pr, 0) == MSG_ONE) { client_t *cl = Write_GetClient (pr); @@ -1220,7 +1236,7 @@ PF_WriteLong (progs_t *pr) // void (float to, float f) WriteAngle static void -PF_WriteAngle (progs_t *pr) +PF_WriteAngle (progs_t *pr, void *data) { if (P_FLOAT (pr, 0) == MSG_ONE) { client_t *cl = Write_GetClient (pr); @@ -1240,7 +1256,7 @@ PF_WriteAngle (progs_t *pr) // void (float to, float f) WriteCoord static void -PF_WriteCoord (progs_t *pr) +PF_WriteCoord (progs_t *pr, void *data) { if (P_FLOAT (pr, 0) == MSG_ONE) { client_t *cl = Write_GetClient (pr); @@ -1260,7 +1276,7 @@ PF_WriteCoord (progs_t *pr) // void (float to, vector v) WriteAngleV static void -PF_WriteAngleV (progs_t *pr) +PF_WriteAngleV (progs_t *pr, void *data) { float *ang = P_VECTOR (pr, 1); @@ -1282,7 +1298,7 @@ PF_WriteAngleV (progs_t *pr) // void (float to, vector v) WriteCoordV static void -PF_WriteCoordV (progs_t *pr) +PF_WriteCoordV (progs_t *pr, void *data) { float *coord = P_VECTOR (pr, 1); @@ -1304,7 +1320,7 @@ PF_WriteCoordV (progs_t *pr) // void (float to, string s) WriteString static void -PF_WriteString (progs_t *pr) +PF_WriteString (progs_t *pr, void *data) { const char *str = P_GSTRING (pr, 1); @@ -1326,7 +1342,7 @@ PF_WriteString (progs_t *pr) // void (float to, entity s) WriteEntity static void -PF_WriteEntity (progs_t *pr) +PF_WriteEntity (progs_t *pr, void *data) { int ent = P_EDICTNUM (pr, 1); @@ -1348,7 +1364,7 @@ PF_WriteEntity (progs_t *pr) // void (entity e) makestatic static void -PF_makestatic (progs_t *pr) +PF_makestatic (progs_t *pr, void *data) { const char *model; edict_t *ent; @@ -1376,7 +1392,7 @@ PF_makestatic (progs_t *pr) // void (entity e) setspawnparms static void -PF_setspawnparms (progs_t *pr) +PF_setspawnparms (progs_t *pr, void *data) { client_t *client; edict_t *ent; @@ -1399,7 +1415,7 @@ PF_setspawnparms (progs_t *pr) // void (string s) changelevel static void -PF_changelevel (progs_t *pr) +PF_changelevel (progs_t *pr, void *data) { const char *s; static int last_spawncount; @@ -1410,7 +1426,7 @@ PF_changelevel (progs_t *pr) last_spawncount = svs.spawncount; s = P_GSTRING (pr, 0); - Cbuf_AddText (sv_cbuf, va ("map %s\n", s)); + Cbuf_AddText (sv_cbuf, va (0, "map %s\n", s)); } /* @@ -1420,7 +1436,7 @@ PF_changelevel (progs_t *pr) // void (entity killer, entity killee) logfrag */ static void -PF_logfrag (progs_t *pr) +PF_logfrag (progs_t *pr, void *data) { const char *s; edict_t *ent1, *ent2; @@ -1459,13 +1475,13 @@ PF_logfrag (progs_t *pr) snprintf(buf, sizeof(buf), "%d", u2); - GIB_Event_Callback (sv_frag_e, 4, type1, va ("%d", u1), type2, buf); + GIB_Event_Callback (sv_frag_e, 4, type1, va (0, "%d", u1), type2, buf); } if (e1 < 1 || e1 > MAX_CLIENTS || e2 < 1 || e2 > MAX_CLIENTS) return; - s = va ("\\%s\\%s\\\n", svs.clients[e1 - 1].name, + s = va (0, "\\%s\\%s\\\n", svs.clients[e1 - 1].name, svs.clients[e2 - 1].name); SZ_Print (&svs.log[svs.logsequence & 1], s); @@ -1482,7 +1498,7 @@ PF_logfrag (progs_t *pr) // string (entity e, string key) infokey */ static void -PF_infokey (progs_t *pr) +PF_infokey (progs_t *pr, void *data) { const char *key, *value; edict_t *e; @@ -1492,7 +1508,7 @@ PF_infokey (progs_t *pr) e1 = NUM_FOR_EDICT (pr, e); key = P_GSTRING (pr, 1); - if (sv_hide_version_info->int_val + if (sv_hide_version_info && (strequal (key, "*qf_version") || strequal (key, "*qsg_version") || strequal (key, "no_pogo_stick"))) { @@ -1510,7 +1526,7 @@ PF_infokey (progs_t *pr) else if (!strcmp (key, "ping")) { int ping = SV_CalcPing (&svs.clients[e1 - 1]); - value = va ("%d", ping); + value = va (0, "%d", ping); } else value = Info_ValueForKey (svs.clients[e1 - 1].userinfo, key); } else @@ -1526,7 +1542,7 @@ PF_infokey (progs_t *pr) // void (vector where, float set) multicast */ static void -PF_multicast (progs_t *pr) +PF_multicast (progs_t *pr, void *data) { float *o; int to; @@ -1544,7 +1560,7 @@ PF_multicast (progs_t *pr) // float (string path, string mode) cfopen */ static void -PF_cfopen (progs_t *pr) +PF_cfopen (progs_t *pr, void *data) { R_FLOAT (pr) = CF_Open (P_GSTRING (pr, 0), P_GSTRING (pr, 1)); } @@ -1556,7 +1572,7 @@ PF_cfopen (progs_t *pr) // void (float desc) cfclose */ static void -PF_cfclose (progs_t *pr) +PF_cfclose (progs_t *pr, void *data) { CF_Close ((int) P_FLOAT (pr, 0)); } @@ -1568,7 +1584,7 @@ PF_cfclose (progs_t *pr) // string (float desc) cfread */ static void -PF_cfread (progs_t *pr) +PF_cfread (progs_t *pr, void *data) { RETURN_STRING (pr, CF_Read ((int) P_FLOAT (pr, 0))); } @@ -1580,7 +1596,7 @@ PF_cfread (progs_t *pr) // float (float desc, string buf) cfwrite */ static void -PF_cfwrite (progs_t *pr) +PF_cfwrite (progs_t *pr, void *data) { R_FLOAT (pr) = CF_Write ((int) P_FLOAT (pr, 0), P_GSTRING (pr, 1)); } @@ -1592,7 +1608,7 @@ PF_cfwrite (progs_t *pr) // float (float desc) cfeof */ static void -PF_cfeof (progs_t *pr) +PF_cfeof (progs_t *pr, void *data) { R_FLOAT (pr) = CF_EOF ((int) P_FLOAT (pr, 0)); } @@ -1604,14 +1620,14 @@ PF_cfeof (progs_t *pr) // float () cfquota */ static void -PF_cfquota (progs_t *pr) +PF_cfquota (progs_t *pr, void *data) { R_FLOAT (pr) = CF_Quota (); } // void (entity ent, string key, string value) setinfokey static void -PF_setinfokey (progs_t *pr) +PF_setinfokey (progs_t *pr, void *data) { edict_t *edict = P_EDICT (pr, 0); int e1 = NUM_FOR_EDICT (pr, edict); @@ -1627,7 +1643,7 @@ PF_setinfokey (progs_t *pr) // entity (entity ent) testentitypos static void -PF_testentitypos (progs_t *pr) +PF_testentitypos (progs_t *pr, void *data) { edict_t *ent = P_EDICT (pr, 0); ent = SV_TestEntityPosition (ent); @@ -1637,9 +1653,9 @@ PF_testentitypos (progs_t *pr) #define MAX_PF_HULLS 64 // FIXME make dynamic? clip_hull_t *pf_hull_list[MAX_PF_HULLS]; -// integer (entity ent, vector point) hullpointcontents +// int (entity ent, vector point) hullpointcontents static void -PF_hullpointcontents (progs_t *pr) +PF_hullpointcontents (progs_t *pr, void *data) { edict_t *edict = P_EDICT (pr, 0); float *mins = P_VECTOR (pr, 1); @@ -1653,9 +1669,9 @@ PF_hullpointcontents (progs_t *pr) R_INT (pr) = SV_HullPointContents (hull, 0, offset); } -// vector (integer hull, integer max) getboxbounds +// vector (int hull, int max) getboxbounds static void -PF_getboxbounds (progs_t *pr) +PF_getboxbounds (progs_t *pr, void *data) { clip_hull_t *ch; int h = P_INT (pr, 0) - 1; @@ -1670,9 +1686,9 @@ PF_getboxbounds (progs_t *pr) } } -// integer () getboxhull +// int () getboxhull static void -PF_getboxhull (progs_t *pr) +PF_getboxhull (progs_t *pr, void *data) { clip_hull_t *ch = 0; int i; @@ -1695,9 +1711,9 @@ PF_getboxhull (progs_t *pr) } } -// void (integer hull) freeboxhull +// void (int hull) freeboxhull static void -PF_freeboxhull (progs_t *pr) +PF_freeboxhull (progs_t *pr, void *data) { int h = P_INT (pr, 0) - 1; clip_hull_t *ch; @@ -1725,9 +1741,9 @@ calc_dist (vec3_t p, vec3_t n, vec3_t *offsets) return DotProduct (v, n); } -// void (integer hull, vector right, vector forward, vector up, vector mins, vector maxs) rotate_bbox +// void (int hull, vector right, vector forward, vector up, vector mins, vector maxs) rotate_bbox static void -PF_rotate_bbox (progs_t *pr) +PF_rotate_bbox (progs_t *pr, void *data) { clip_hull_t *ch; float l; @@ -1806,29 +1822,45 @@ PF_rotate_bbox (progs_t *pr) // float () checkextension static void -PF_checkextension (progs_t *pr) +PF_checkextension (progs_t *pr, void *data) { R_FLOAT (pr) = 0; // FIXME: make this function actually useful :P } static void -PF_sv_cvar (progs_t *pr) +PF_sv_cvar (progs_t *pr, void *data) { const char *str; str = P_GSTRING (pr, 0); - if (sv_hide_version_info->int_val + if (sv_hide_version_info && strequal (str, "sv_hide_version_info")) { R_FLOAT (pr) = 0; } else { - R_FLOAT (pr) = Cvar_VariableValue (str); + R_FLOAT (pr) = Cvar_Value (str); } } +// int (entity) SV_ClientNumber +// returns -1 if the entity is not a client (either not in the client range +// or the entity is not in use by a client) +static void +PF_SV_ClientNumber (progs_t *pr, void *data) +{ + int entnum = P_EDICTNUM (pr, 0); + client_t *cl = svs.clients + entnum - 1; + + if (entnum < 1 || entnum > MAX_CLIENTS + || cl->state == cs_free || cl->state == cs_zombie) { + entnum = 0; // nil entity + } + R_INT (pr) = entnum - 1; +} + // entity () SV_AllocClient static void -PF_SV_AllocClient (progs_t *pr) +PF_SV_AllocClient (progs_t *pr, void *data) { client_t *cl = SV_AllocClient (0, 1); @@ -1846,7 +1878,7 @@ PF_SV_AllocClient (progs_t *pr) // void (entity cl) SV_FreeClient static void -PF_SV_FreeClient (progs_t *pr) +PF_SV_FreeClient (progs_t *pr, void *data) { int entnum = P_EDICTNUM (pr, 0); client_t *cl = svs.clients + entnum - 1; @@ -1861,13 +1893,13 @@ PF_SV_FreeClient (progs_t *pr) cl->state = cs_free; //if (sv_client_disconnect_e->func) - // GIB_Event_Callback (sv_client_disconnect_e, 2, va ("%u", cl->userid), + // GIB_Event_Callback (sv_client_disconnect_e, 2, va (0, "%u", cl->userid), // "server"); } // void (entity cl, string userinfo) SV_SetUserinfo static void -PF_SV_SetUserinfo (progs_t *pr) +PF_SV_SetUserinfo (progs_t *pr, void *data) { int entnum = P_EDICTNUM (pr, 0); client_t *cl = svs.clients + entnum - 1; @@ -1876,14 +1908,14 @@ PF_SV_SetUserinfo (progs_t *pr) if (entnum < 1 || entnum > MAX_CLIENTS || cl->state != cs_server) PR_RunError (pr, "not a server client"); - cl->userinfo = Info_ParseString (str, 1023, !sv_highchars->int_val); + cl->userinfo = Info_ParseString (str, 1023, !sv_highchars); cl->sendinfo = true; SV_ExtractFromUserinfo (cl); } -// void (entity cl, integer ping) SV_SetPing +// void (entity cl, int ping) SV_SetPing static void -PR_SV_SetPing (progs_t *pr) +PF_SV_SetPing (progs_t *pr, void *data) { int entnum = P_EDICTNUM (pr, 0); client_t *cl = svs.clients + entnum - 1; @@ -1893,9 +1925,9 @@ PR_SV_SetPing (progs_t *pr) cl->ping = P_INT (pr, 1); } -// void (entity cl, float secs, vector angles, vector move, integer buttons, integer impulse) SV_UserCmd +// void (entity cl, float secs, vector angles, vector move, int buttons, int impulse) SV_UserCmd static void -PR_SV_UserCmd (progs_t *pr) +PF_SV_UserCmd (progs_t *pr, void *data) { usercmd_t ucmd; int entnum = P_EDICTNUM (pr, 0); @@ -1921,7 +1953,7 @@ PR_SV_UserCmd (progs_t *pr) // void (entity cl) SV_Spawn static void -PR_SV_Spawn (progs_t *pr) +PF_SV_Spawn (progs_t *pr, void *data) { int entnum = P_EDICTNUM (pr, 0); client_t *cl = svs.clients + entnum - 1; @@ -1934,95 +1966,106 @@ PR_SV_Spawn (progs_t *pr) #define QF (PR_RANGE_QF << PR_RANGE_SHIFT) | +#define bi(x,n,np,params...) {#x, PF_##x, n, np, {params}} +#define p(type) PR_PARAM(type) static builtin_t builtins[] = { - {"makevectors", PF_makevectors, 1}, - {"setorigin", PF_setorigin, 2}, - {"setmodel", PF_setmodel, 3}, - {"setsize", PF_setsize, 4}, + bi(makevectors, 1, 1, p(vector)), + bi(setorigin, 2, 2, p(entity), p(vector)), + bi(setmodel, 3, 2, p(entity), p(string)), + bi(setsize, 4, 3, p(entity), p(vector), p(vector)), - {"sound", PF_sound, 8}, + bi(sound, 8, 3, p(entity), p(float), p(string)), - {"error", PF_error, 10}, - {"objerror", PF_objerror, 11}, - {"spawn", PF_Spawn, 14}, - {"remove", PF_Remove, 15}, - {"traceline", PF_traceline, 16}, - {"checkclient", PF_checkclient, 17}, + bi(error, 10, -1), // (...) + bi(objerror, 11, -1), // (...) + bi(spawn, 14, 0), // (void) + bi(remove, 15, 1, p(entity)), + bi(traceline, 16, 3, p(vector), p(vector), p(float)), + bi(checkclient, 17, 0), // (void) - {"precache_sound", PF_precache_sound, 19}, - {"precache_model", PF_precache_model, 20}, - {"stuffcmd", PF_stuffcmd, 21}, - {"findradius", PF_findradius, 22}, - {"bprint", PF_bprint, 23}, - {"sprint", PF_sprint, 24}, + bi(precache_sound, 19, 1, p(string)), + bi(precache_model, 20, 1, p(string)), + bi(stuffcmd, 21, 2, p(entity), p(string)), + bi(findradius, 22, 2, p(vector), p(float)), + bi(bprint, 23, -1), // (...) + bi(sprint, 24, -2, p(entity)), // (entity, string...) - {"walkmove", PF_walkmove, 32}, + bi(walkmove, 32, 2, p(float), p(float)), - {"droptofloor", PF_droptofloor, 34}, - {"lightstyle", PF_lightstyle, 35}, + bi(droptofloor, 34, 0), // (void) + bi(lightstyle, 35, 2, p(float), p(string)), - {"checkbottom", PF_checkbottom, 40}, - {"pointcontents", PF_pointcontents, 41}, + bi(checkbottom, 40, 1, p(entity)), + bi(pointcontents, 41, 1, p(vector)), - {"aim", PF_aim, 44}, + bi(aim, 44, 2, p(entity), p(float)), - {"localcmd", PF_localcmd, 46}, + bi(localcmd, 46, 1, p(string)), - {"changeyaw", PF_changeyaw, 49}, +// bi(particle, 48, 4, p(vector), p(vector), p(float), p(float)), + bi(changeyaw, 49, 0), // (void) - {"writebyte", PF_WriteByte, 52}, - {"WriteBytes", PF_WriteBytes, -1}, - {"writechar", PF_WriteChar, 53}, - {"writeshort", PF_WriteShort, 54}, - {"writelong", PF_WriteLong, 55}, - {"writecoord", PF_WriteCoord, 56}, - {"writeangle", PF_WriteAngle, 57}, - {"WriteCoordV", PF_WriteCoordV, -1}, - {"WriteAngleV", PF_WriteAngleV, -1}, - {"writestring", PF_WriteString, 58}, - {"writeentity", PF_WriteEntity, 59}, + bi(WriteByte, 52, 2, p(float), p(float)), + bi(WriteBytes, -1, -2, p(float)), // (float, float...) + bi(WriteChar, 53, 2, p(float), p(float)), + bi(WriteShort, 54, 2, p(float), p(float)), + bi(WriteLong, 55, 2, p(float), p(float)), + bi(WriteCoord, 56, 2, p(float), p(float)), + bi(WriteAngle, 57, 2, p(float), p(float)), + bi(WriteCoordV, -1, 2, p(float), p(vector)), + bi(WriteAngleV, -1, 2, p(float), p(vector)), + bi(WriteString, 58, 2, p(float), p(string)), + bi(WriteEntity, 59, 2, p(float), p(entity)), +#define PF_movetogoal SV_MoveToGoal + bi(movetogoal, 67, 0), // (void) +#undef PF_movetogoal + bi(precache_file, 68, 1, p(string)), + bi(makestatic, 69, 1, p(entity)), + bi(changelevel, 70, 1, p(string)), - {"movetogoal", SV_MoveToGoal, 67}, - {"precache_file", PF_precache_file, 68}, - {"makestatic", PF_makestatic, 69}, - {"changelevel", PF_changelevel, 70}, + bi(centerprint, 73, -1), // (...) + bi(ambientsound, 74, 4, p(vector), p(string), p(float), p(float)), + bi(precache_model2, 75, 1, p(string)), + bi(precache_sound2, 76, 1, p(string)), + bi(precache_file2, 77, 1, p(string)), + bi(setspawnparms, 78, 1, p(entity)), - {"centerprint", PF_centerprint, 73}, - {"ambientsound", PF_ambientsound, 74}, - {"precache_model2", PF_precache_model, 75}, - {"precache_sound2", PF_precache_sound, 76}, - {"precache_file2", PF_precache_file, 77}, - {"setspawnparms", PF_setspawnparms, 78}, + bi(logfrag, 79, 2, p(entity), p(entity)), + bi(infokey, 80, 2, p(entity), p(string)), + bi(multicast, 82, 2, p(vector), p(float)), - {"logfrag", PF_logfrag, 79}, - {"infokey", PF_infokey, 80}, - {"multicast", PF_multicast, 82}, + bi(testentitypos, QF 92, 1, p(entity)), + bi(hullpointcontents, QF 93, 2, p(entity), p(vector)), + bi(getboxbounds, QF 94, 2, p(int), p(int)), + bi(getboxhull, QF 95, 0), // (void) + bi(freeboxhull, QF 96, 1, p(int)), + bi(rotate_bbox, QF 97, 6, p(int), p(vector), p(vector), p(vector), + p(vector), p(vector)), + bi(tracebox, QF 98, 6, p(vector), p(vector), p(vector), p(vector), + p(float), p(entity)), + bi(checkextension, QF 99, -1, {}), //FIXME correct params? - {"testentitypos", PF_testentitypos, QF 92}, - {"hullpointcontents", PF_hullpointcontents, QF 93}, - {"getboxbounds", PF_getboxbounds, QF 94}, - {"getboxhull", PF_getboxhull, QF 95}, - {"freeboxhull", PF_freeboxhull, QF 96}, - {"rotate_bbox", PF_rotate_bbox, QF 97}, - {"tracebox", PF_tracebox, QF 98}, - {"checkextension", PF_checkextension, QF 99}, - {"setinfokey", PF_setinfokey, QF 102}, - {"cfopen", PF_cfopen, QF 103}, - {"cfclose", PF_cfclose, QF 104}, - {"cfread", PF_cfread, QF 105}, - {"cfwrite", PF_cfwrite, QF 106}, - {"cfeof", PF_cfeof, QF 107}, - {"cfquota", PF_cfquota, QF 108}, + bi(setinfokey, QF 102, 3, p(entity), p(string), p(string)), + bi(cfopen, QF 103, 2, p(string), p(string)), + bi(cfclose, QF 104, 1, p(float)), + bi(cfread, QF 105, 1, p(float)), + bi(cfwrite, QF 106, 2, p(float), p(string)), + bi(cfeof, QF 107, 1, p(float)), + bi(cfquota, QF 108, 0), // (void) - {"SV_AllocClient", PF_SV_AllocClient, -1}, - {"SV_FreeClient", PF_SV_FreeClient, -1}, - {"SV_SetUserinfo", PF_SV_SetUserinfo, -1}, - {"SV_SetPing", PR_SV_SetPing, -1}, - {"SV_UserCmd", PR_SV_UserCmd, -1}, - {"SV_Spawn", PR_SV_Spawn, -1}, + bi(SV_ClientNumber, -1, 1, p(entity)), + bi(SV_AllocClient, -1, 0), // (void) + bi(SV_FreeClient, -1, 1, p(entity)), + bi(SV_SetUserinfo, -1, 2, p(entity), p(string)), + bi(SV_SetPing, -1, 2, p(entity), p(int)), + bi(SV_UserCmd, -1, 6, p(entity), p(float), p(vector), p(vector), + p(int), p(int)), + bi(SV_Spawn, -1, 1, p(entity)), - {"EntityParseFunction", ED_EntityParseFunction, -1}, +#define PF_EntityParseFunction ED_EntityParseFunction + bi(EntityParseFunction, -1, 1, p(func)), +#undef PF_EntityParseFunction {0} }; @@ -2039,5 +2082,5 @@ SV_PR_Cmds_Init () bi = PR_FindBuiltin (&sv_pr_state, "cvar"); bi->proc = PF_sv_cvar; - PR_RegisterBuiltins (&sv_pr_state, builtins); + PR_RegisterBuiltins (&sv_pr_state, builtins, 0); } diff --git a/qw/source/sv_pr_cpqw.c b/qw/source/sv_pr_cpqw.c index 594f66219..6c72d90cc 100644 --- a/qw/source/sv_pr_cpqw.c +++ b/qw/source/sv_pr_cpqw.c @@ -39,13 +39,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "QF/quakefs.h" #include "QF/sys.h" -#include "server.h" -#include "sv_pr_cpqw.h" -#include "sv_progs.h" +#include "qw/include/server.h" +#include "qw/include/sv_pr_cpqw.h" +#include "qw/include/sv_progs.h" #include "world.h" static struct { - func_t ClientCommand; + pr_func_t ClientCommand; } cpqw_funcs; /* @@ -54,7 +54,7 @@ static struct { float(entity client) getuid */ static void -PF_getuid (progs_t *pr) +PF_getuid (progs_t *pr, void *data) { edict_t *client_ent; int e_num; @@ -76,7 +76,7 @@ PF_getuid (progs_t *pr) string(string st1, string st2) strcat */ static void -PF_strcat (progs_t *pr) +PF_strcat (progs_t *pr, void *data) { const char *st1 = P_GSTRING (pr, 0); const char *st2 = P_GSTRING (pr, 1); @@ -90,7 +90,7 @@ PF_strcat (progs_t *pr) string(string st, float len) padstr */ static void -PF_padstr (progs_t *pr) +PF_padstr (progs_t *pr, void *data) { const char *st; size_t i, padlen, givenlen; @@ -133,7 +133,7 @@ PF_padstr (progs_t *pr) // non-original charsets) static void -PF_colstr (progs_t *pr) +PF_colstr (progs_t *pr, void *data) { const char *srcstr; unsigned char *result; @@ -226,7 +226,7 @@ PF_colstr (progs_t *pr) float(string st1, string st2) strcasecmp */ static void -PF_strcasecmp (progs_t *pr) +PF_strcasecmp (progs_t *pr, void *data) { const char *st1; const char *st2; @@ -244,7 +244,7 @@ PF_strcasecmp (progs_t *pr) */ static void -PF_strlen (progs_t *pr) +PF_strlen (progs_t *pr, void *data) { const char *st; @@ -313,7 +313,7 @@ KK_Match_Str2 (const char *substr) entity(string st) getclient */ static void -PF_getclient (progs_t *pr) +PF_getclient (progs_t *pr, void *data) { edict_t *ent; const char *st; @@ -353,7 +353,7 @@ float(entity client) mutedtime */ static void -PF_mutedtime (progs_t *pr) +PF_mutedtime (progs_t *pr, void *data) { edict_t *client_ent; int e_num; @@ -382,7 +382,7 @@ float(string st) validatefile */ static void -PF_validatefile (progs_t *pr) +PF_validatefile (progs_t *pr, void *data) { float retval; QFile *f; @@ -410,7 +410,7 @@ PF_validatefile (progs_t *pr) extern int fp_messages, fp_persecond, fp_secondsdead; static void -PF_putsaytime (progs_t *pr) +PF_putsaytime (progs_t *pr, void *data) { edict_t *client_ent; int e_num, tmp; @@ -444,9 +444,9 @@ PF_putsaytime (progs_t *pr) */ static void -PF_makestr (progs_t *pr) +PF_makestr (progs_t *pr, void *data) { - string_t res = PR_NewMutableString (pr); + pr_string_t res = PR_NewMutableString (pr); dstring_t *dst = PR_GetMutableString (pr, res); const char *src = P_GSTRING (pr, 0); @@ -461,7 +461,7 @@ PF_makestr (progs_t *pr) */ static void -PF_delstr (progs_t *pr) +PF_delstr (progs_t *pr, void *data) { PR_FreeString (pr, P_STRING (pr, 0)); } @@ -548,7 +548,7 @@ GetLinearWave (float inputnum) #define GWAVE_USESHAPE 8 static void -PF_getwave (progs_t *pr) +PF_getwave (progs_t *pr, void *data) { float retval, inputnum, minnum, maxnum, balance, offset, shape; float temp; @@ -625,7 +625,7 @@ PF_getwave (progs_t *pr) */ static void -PF_clientsound (progs_t *pr) +PF_clientsound (progs_t *pr, void *data) { const char *sample; int channel; @@ -700,7 +700,7 @@ PF_clientsound (progs_t *pr) */ static void -PF_touchworld (progs_t *pr) +PF_touchworld (progs_t *pr, void *data) { edict_t *self; pr_int_t oself; @@ -729,7 +729,7 @@ PF_touchworld (progs_t *pr) #define TL_EVERYTHING 4 // scan for anything static void -CPQW_traceline (progs_t *pr) +PF_traceline (progs_t *pr, void *data) { float *v1, *v2; edict_t *ent; @@ -752,7 +752,7 @@ CPQW_traceline (progs_t *pr) nomonsters = TL_ANY_SOLID; nomonsters = tl_to_move[nomonsters]; - if (sv_antilag->int_val == 2) + if (sv_antilag == 2) nomonsters |= MOVE_LAGGED; trace = SV_Move (v1, vec3_origin, vec3_origin, v2, nomonsters, ent); @@ -774,29 +774,33 @@ CPQW_traceline (progs_t *pr) #define CPQW (PR_RANGE_CPQW << PR_RANGE_SHIFT) | +#define bi(x,n,np,params...) {"CPCW:"#x, PF_##x, n, np, {params}} +#define p(type) PR_PARAM(type) +#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } static builtin_t builtins[] = { - {"CPCW:traceline", CPQW_traceline, CPQW 16}, - {"CPQW:getuid", PF_getuid, CPQW 83}, - {"CPQW:strcat", PF_strcat, CPQW 84}, - {"CPQW:padstr", PF_padstr, CPQW 85}, - {"CPQW:colstr", PF_colstr, CPQW 86}, - {"CPQW:strcasecmp", PF_strcasecmp, CPQW 87}, - {"CPQW:strlen", PF_strlen, CPQW 88}, - {"CPQW:getclient", PF_getclient, CPQW 89}, - {"CPQW:mutedtime", PF_mutedtime, CPQW 90}, - {"CPQW:validatefile", PF_validatefile, CPQW 91}, - {"CPQW:putsaytime", PF_putsaytime, CPQW 92}, - {"CPQW:makestr", PF_makestr, CPQW 93}, - {"CPQW:delstr", PF_delstr, CPQW 94}, - {"CPQW:getwave", PF_getwave, CPQW 95}, - {"CPQW:clientsound", PF_clientsound, CPQW 96}, - {"CPQW:touchworld", PF_touchworld, CPQW 97}, + bi(traceline, CPQW 16, 3, p(vector), p(vector), p(float)), + bi(getuid, CPQW 83, 1, p(entity)), + bi(strcat, CPQW 84, 2, p(string), p(string)), + bi(padstr, CPQW 85, 2, p(string), p(float)), + bi(colstr, CPQW 86, 2, p(string), p(float)), + bi(strcasecmp, CPQW 87, 2, p(string), p(string)), + bi(strlen, CPQW 88, 1, p(string)), + bi(getclient, CPQW 89, 1, p(string)), + bi(mutedtime, CPQW 90, 1, p(entity)), + bi(validatefile, CPQW 91, 1, p(string)), + bi(putsaytime, CPQW 92, 1, p(entity)), + bi(makestr, CPQW 93, 1, p(string)), + bi(delstr, CPQW 94, 1, p(string)), + bi(getwave, CPQW 95, 7, p(float), p(float), p(float), p(float), + p(float), p(float), p(float)), + bi(clientsound, CPQW 96, 1, p(entity)), + bi(touchworld, CPQW 97, 0), {0} }; static struct { const char *name; - func_t *field; + pr_func_t *field; } cpqw_func_list[] = { {"ClientCommand", &cpqw_funcs.ClientCommand}, {"UserInfoChanged", &sv_funcs.UserInfoChanged}, @@ -817,11 +821,13 @@ cpqw_user_cmd (void) *sv_globals.self = EDICT_TO_PROG (&sv_pr_state, sv_player); PR_PushFrame (pr); + PR_RESET_PARAMS (pr); P_FLOAT (pr, 0) = argc; - for (i = 1; i < argc; i++) + for (i = 1; i < argc + 1; i++) P_STRING (pr, i) = PR_SetTempString (pr, Cmd_Argv (i - 1)); - for (; i < 7; i++) + for (; i < 8; i++) P_STRING (pr, i) = 0; + pr->pr_argc = 8; PR_ExecuteProgram (pr, cpqw_funcs.ClientCommand); PR_PopFrame (pr); return (int) R_FLOAT (pr); @@ -840,7 +846,7 @@ cpqw_load (progs_t *pr) *cpqw_func_list[i].field = 0; if (f) - *cpqw_func_list[i].field = (func_t) (f - pr->pr_functions); + *cpqw_func_list[i].field = (pr_func_t) (f - pr->pr_functions); } ucmd_unknown = cpqw_user_cmd; return 1; @@ -849,6 +855,6 @@ cpqw_load (progs_t *pr) void SV_PR_CPQW_Init (progs_t *pr) { - PR_RegisterBuiltins (pr, builtins); + PR_RegisterBuiltins (pr, builtins, 0); PR_AddLoadFunc (pr, cpqw_load); } diff --git a/qw/source/sv_pr_qwe.c b/qw/source/sv_pr_qwe.c index 0c844abd8..329cea360 100644 --- a/qw/source/sv_pr_qwe.c +++ b/qw/source/sv_pr_qwe.c @@ -50,21 +50,21 @@ #include "QF/sys.h" #include "QF/va.h" -#include "server.h" -#include "sv_pr_qwe.h" -#include "sv_progs.h" -#include "sv_recorder.h" +#include "qw/include/server.h" +#include "qw/include/sv_pr_qwe.h" +#include "qw/include/sv_progs.h" +#include "qw/include/sv_recorder.h" typedef struct { - func_t timeofday; - func_t ConsoleCmd; - func_t UserCmd; + pr_func_t timeofday; + pr_func_t ConsoleCmd; + pr_func_t UserCmd; } qwe_funcs_t; static qwe_funcs_t qwe_funcs; static void -PF_executecmd (progs_t *pr) +PF_executecmd (progs_t *pr, void *data) { int old_other, old_self; // mod_consolecmd will be executed, so // we need to store these @@ -87,7 +87,7 @@ PF_executecmd (progs_t *pr) */ static void -PF_tokanize (progs_t *pr) +PF_tokanize (progs_t *pr, void *data) { const char *str; @@ -105,7 +105,7 @@ PF_tokanize (progs_t *pr) */ static void -PF_argc (progs_t *pr) +PF_argc (progs_t *pr, void *data) { R_FLOAT (pr) = (float) Cmd_Argc (); } @@ -119,7 +119,7 @@ PF_argc (progs_t *pr) */ static void -PF_argv (progs_t *pr) +PF_argv (progs_t *pr, void *data) { int num; @@ -140,7 +140,7 @@ PF_argv (progs_t *pr) */ static void -PF_teamfield (progs_t *pr) +PF_teamfield (progs_t *pr, void *data) { sv_fields.team_str = P_INT (pr, 0); } @@ -152,7 +152,7 @@ PF_teamfield (progs_t *pr) */ static void -PF_substr (progs_t *pr) +PF_substr (progs_t *pr, void *data) { const char *s; char *tmp; @@ -174,7 +174,7 @@ PF_substr (progs_t *pr) if (len > l) len = l; - tmp = Hunk_TempAlloc (len + 1); + tmp = Hunk_TempAlloc (0, len + 1); strncpy (tmp, s, len); tmp[len] = 0; @@ -188,9 +188,9 @@ PF_substr (progs_t *pr) */ static void -PF_strcat (progs_t *pr) +PF_strcat (progs_t *pr, void *data) { - RETURN_STRING (pr, PF_VarString (pr, 0)); + RETURN_STRING (pr, PF_VarString (pr, 0, pr->pr_argc)); } /* @@ -200,7 +200,7 @@ PF_strcat (progs_t *pr) */ static void -PF_strlen (progs_t *pr) +PF_strlen (progs_t *pr, void *data) { R_FLOAT (pr) = (float) strlen (P_GSTRING (pr, 0)); } @@ -212,7 +212,7 @@ PF_strlen (progs_t *pr) */ static void -PF_str2byte (progs_t *pr) +PF_str2byte (progs_t *pr, void *data) { R_FLOAT (pr) = (float) *P_GSTRING (pr, 0); } @@ -224,7 +224,7 @@ PF_str2byte (progs_t *pr) */ static void -PF_str2short (progs_t *pr) +PF_str2short (progs_t *pr, void *data) { const byte *str = (byte *) P_GSTRING (pr, 0); @@ -239,7 +239,7 @@ PF_str2short (progs_t *pr) The new string will be at least as big as size, if given. */ static void -PF_newstr (progs_t *pr) +PF_newstr (progs_t *pr, void *data) { const char *s; dstring_t *dstr; @@ -269,7 +269,7 @@ PF_newstr (progs_t *pr) */ static void -PF_freestr (progs_t *pr) +PF_freestr (progs_t *pr, void *data) { PR_FreeString (pr, P_STRING (pr, 0)); } @@ -281,7 +281,7 @@ PF_freestr (progs_t *pr) */ static void -PF_readcmd (progs_t *pr) +PF_readcmd (progs_t *pr, void *data) { const char *s; redirect_t old; @@ -311,7 +311,7 @@ PF_readcmd (progs_t *pr) */ static void -PF_redirectcmd (progs_t *pr) +PF_redirectcmd (progs_t *pr, void *data) { const char *s; int entnum; @@ -334,7 +334,7 @@ PF_redirectcmd (progs_t *pr) } static void -PF_calltimeofday (progs_t *pr) +PF_calltimeofday (progs_t *pr, void *data) { date_t date; dfunction_t *f; @@ -353,7 +353,8 @@ PF_calltimeofday (progs_t *pr) P_FLOAT (pr, 5) = (float) date.year; P_STRING (pr, 6) = PR_SetReturnString (pr, date.str); - PR_ExecuteProgram (pr, (func_t) (f - sv_pr_state.pr_functions)); + pr->pr_argc = 7; + PR_ExecuteProgram (pr, (pr_func_t) (f - sv_pr_state.pr_functions)); PR_PopFrame (&sv_pr_state); } } @@ -367,7 +368,7 @@ PF_calltimeofday (progs_t *pr) */ static void -PF_forcedemoframe (progs_t *pr) +PF_forcedemoframe (progs_t *pr, void *data) { SVR_ForceFrame (); if (P_FLOAT (pr, 0) == 1) @@ -382,7 +383,7 @@ PF_forcedemoframe (progs_t *pr) */ static void -PF_strcpy (progs_t *pr) +PF_strcpy (progs_t *pr, void *data) { dstring_copystr (P_DSTRING (pr, 0), P_GSTRING (pr, 1)); } @@ -394,7 +395,7 @@ PF_strcpy (progs_t *pr) */ static void -PF_strncpy (progs_t *pr) +PF_strncpy (progs_t *pr, void *data) { dstring_t *dst = P_DSTRING (pr, 0); const char *src = P_GSTRING (pr, 1); @@ -413,7 +414,7 @@ PF_strncpy (progs_t *pr) */ static void -PF_strstr (progs_t *pr) +PF_strstr (progs_t *pr, void *data) { const char *str, *sub, *p; @@ -444,16 +445,16 @@ clean_text (char *text) */ static void -PF_log (progs_t *pr) +PF_log (progs_t *pr, void *data) { const char *name; char *text; QFile *file; - name = va ("%s/%s.log", qfs_gamedir->dir.def, P_GSTRING (pr, 0)); + name = va (0, "%s/%s.log", qfs_gamedir->dir.def, P_GSTRING (pr, 0)); file = QFS_Open (name, "a"); - text = PF_VarString (pr, 2); + text = PF_VarString (pr, 2, pr->pr_argc); clean_text (text); if (P_FLOAT (pr, 1)) @@ -472,42 +473,45 @@ PF_log (progs_t *pr) PF_conprint */ static void -PF_conprint (progs_t *pr) +PF_conprint (progs_t *pr, void *data) { - Sys_Printf ("%s", PF_VarString (pr, 0)); + Sys_Printf ("%s", PF_VarString (pr, 0, pr->pr_argc)); } #define QWE (PR_RANGE_QWE << PR_RANGE_SHIFT) | +#define bi(x,n,np,params...) {"QWE:"#x, PF_##x, n, np, {params}} +#define p(type) PR_PARAM(type) +#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } static builtin_t builtins[] = { - {"QWE:executecmd", PF_executecmd, QWE 83}, - {"QWE:tokanize" /* sic */, PF_tokanize, QWE 84}, - {"QWE:argc", PF_argc, QWE 85}, - {"QWE:argv", PF_argv, QWE 86}, - {"QWE:teamfield", PF_teamfield, QWE 87}, - {"QWE:substr", PF_substr, QWE 88}, - {"QWE:strcat", PF_strcat, QWE 89}, - {"QWE:strlen", PF_strlen, QWE 90}, - {"QWE:str2byte", PF_str2byte, QWE 91}, - {"QWE:str2short", PF_str2short, QWE 92}, - {"QWE:newstr", PF_newstr, QWE 93}, - {"QWE:freestr", PF_freestr, QWE 94}, - {"QWE:conprint", PF_conprint, QWE 95}, - {"QWE:readcmd", PF_readcmd, QWE 96}, - {"QWE:strcpy", PF_strcpy, QWE 97}, - {"QWE:strstr", PF_strstr, QWE 98}, - {"QWE:strncpy", PF_strncpy, QWE 99}, - {"QWE:log", PF_log, QWE 100}, - {"QWE:redirectcmd", PF_redirectcmd, QWE 101}, - {"QWE:calltimeofday", PF_calltimeofday, QWE 102}, - {"QWE:forceddemoframe", PF_forcedemoframe, QWE 103}, + bi(executecmd, QWE 83, 0), + bi(tokanize, QWE 84, 1, p(string)), /* sic */ + bi(argc, QWE 85, 0), + bi(argv, QWE 86, 1, p(float)), + bi(teamfield, QWE 87, 1, p(field)), + bi(substr, QWE 88, 3, p(string), p(float), p(float)), + bi(strcat, QWE 89, -1), + bi(strlen, QWE 90, 1, p(string)), + bi(str2byte, QWE 91, 1, p(string)), + bi(str2short, QWE 92, 1, p(string)), + bi(newstr, QWE 93, 2, p(string), p(float)), + bi(freestr, QWE 94, 1, p(string)), + bi(conprint, QWE 95, -1), + bi(readcmd, QWE 96, 1, p(string)), + bi(strcpy, QWE 97, 2, p(string), p(string)), + bi(strstr, QWE 98, 2, p(string), p(string)), + bi(strncpy, QWE 99, 3, p(string), p(string), p(float)), + bi(log, QWE 100, 3, p(string), p(float), p(string)), + bi(redirectcmd, QWE 101, 2, p(entity), p(string)), + bi(calltimeofday, QWE 102, 0), + bi(forcedemoframe, QWE 103, 1, p(float)), {0} }; #define LAST_QWE_BUILTIN 103 static struct { const char *name; - func_t *field; + pr_func_t *field; } qwe_func_list[] = { {"timeofday", &qwe_funcs.timeofday}, {"ConsoleCmd", &qwe_funcs.ConsoleCmd}, @@ -551,7 +555,7 @@ static int qwe_load_finish (progs_t *pr) { edict_t *ent; - ddef_t *targetname; + pr_def_t *targetname; targetname = PR_FindField (pr, "targetname"); ent = EDICT_NUM (pr, 0); @@ -573,7 +577,7 @@ qwe_load (progs_t *pr) *qwe_func_list[i].field = 0; if (f) - *qwe_func_list[i].field = (func_t) (f - pr->pr_functions); + *qwe_func_list[i].field = (pr_func_t) (f - pr->pr_functions); } sv_cbuf->unknown_command = qwe_console_cmd; @@ -585,6 +589,6 @@ qwe_load (progs_t *pr) void SV_PR_QWE_Init (progs_t *pr) { - PR_RegisterBuiltins (pr, builtins); + PR_RegisterBuiltins (pr, builtins, 0); PR_AddLoadFunc (pr, qwe_load); } diff --git a/qw/source/sv_progs.c b/qw/source/sv_progs.c index e11805c17..23a9c4061 100644 --- a/qw/source/sv_progs.c +++ b/qw/source/sv_progs.c @@ -42,10 +42,11 @@ #include "QF/sys.h" #include "compat.h" -#include "server.h" -#include "sv_progs.h" -#include "sv_pr_cpqw.h" -#include "sv_pr_qwe.h" + +#include "qw/include/server.h" +#include "qw/include/sv_progs.h" +#include "qw/include/sv_pr_cpqw.h" +#include "qw/include/sv_pr_qwe.h" #include "world.h" progs_t sv_pr_state; @@ -53,17 +54,105 @@ sv_globals_t sv_globals; sv_funcs_t sv_funcs; sv_fields_t sv_fields; +edict_t sv_edicts[MAX_EDICTS]; sv_data_t sv_data[MAX_EDICTS]; -cvar_t *r_skyname; -cvar_t *sv_progs; -cvar_t *sv_progs_zone; -cvar_t *sv_progs_ext; -cvar_t *pr_checkextensions; -cvar_t *sv_old_entity_free; -cvar_t *sv_hide_version_info; +char *r_skyname; +static cvar_t r_skyname_cvar = { + .name = "r_skyname", + .description = + "name of the current skybox", + .default_value = "none", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &r_skyname }, +}; +char *sv_progs; +static cvar_t sv_progs_cvar = { + .name = "sv_progs", + .description = + "Override the default game progs.", + .default_value = "", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &sv_progs }, +}; +int sv_progs_zone; +static cvar_t sv_progs_zone_cvar = { + .name = "sv_progs_zone", + .description = + "size of the zone for progs in kb", + .default_value = "256", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_progs_zone }, +}; +int sv_progs_stack; +static cvar_t sv_progs_stack_cvar = { + .name = "sv_progs_stack", + .description = + "size of the stack for progs in kb", + .default_value = "256", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_progs_stack }, +}; +char *sv_progs_ext; +static cvar_t sv_progs_ext_cvar = { + .name = "sv_progs_ext", + .description = + "extention mapping to use: none, id, qf", + .default_value = "qf", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &sv_progs_ext }, +}; +float pr_checkextensions; +static cvar_t pr_checkextensions_cvar = { + .name = "pr_checkextensions", + .description = + "indicate the presence of the checkextentions qc function", + .default_value = "1", + .flags = CVAR_ROM, + .value = { .type = &cexpr_float, .value = &pr_checkextensions }, +}; +int sv_old_entity_free; +static cvar_t sv_old_entity_free_cvar = { + .name = "sv_old_entity_free", + .description = + "set this for buggy mods that rely on the old behaviour of entity " + "freeing (eg, *TF)", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_old_entity_free }, +}; +int sv_hide_version_info; +static cvar_t sv_hide_version_info_cvar = { + .name = "sv_hide_version_info", + .description = + "hide QuakeForge specific serverinfo strings from terminally stupid " + "progs (eg, braindead TF variants)", + .default_value = "0", + .flags = CVAR_ROM, + .value = { .type = &cexpr_int, .value = &sv_hide_version_info }, +}; +int pr_double_remove; +static cvar_t pr_double_remove_cvar = { + .name = "pr_double_remove", + .description = + "Handling of double entity remove. 0 is silently ignore, 1 prints a " + "traceback, and 2 gives an error.\nworks Only if debugging is " + "available and enabled", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &pr_double_remove }, +}; +float sv_aim; +static cvar_t sv_aim_cvar = { + .name = "sv_aim", + .description = + "None", + .default_value = "0.93", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_aim }, +}; -static int reserved_edicts = MAX_CLIENTS; +static pr_uint_t reserved_edicts = MAX_CLIENTS; static int sv_range; @@ -91,17 +180,17 @@ bi_map (progs_t *pr, unsigned binum) static void free_edict (progs_t *pr, edict_t *ent) { - if (sv_old_entity_free->int_val) { - ent->v[sv_fields.model].entity_var = 0; - ent->v[sv_fields.takedamage].float_var = 0; - ent->v[sv_fields.modelindex].float_var = 0; - ent->v[sv_fields.colormap].float_var = 0; - ent->v[sv_fields.skin].float_var = 0; - ent->v[sv_fields.frame].float_var = 0; - ent->v[sv_fields.nextthink].float_var = -1; - ent->v[sv_fields.solid].float_var = 0; - memset (ent->v[sv_fields.origin].vector_var, 0, 3*sizeof (float)); - memset (ent->v[sv_fields.angles].vector_var, 0, 3*sizeof (float)); + if (sv_old_entity_free) { + E_STRING (ent, sv_fields.model) = 0; + E_FLOAT (ent, sv_fields.takedamage) = 0; + E_FLOAT (ent, sv_fields.modelindex) = 0; + E_FLOAT (ent, sv_fields.colormap) = 0; + E_FLOAT (ent, sv_fields.skin) = 0; + E_FLOAT (ent, sv_fields.frame) = 0; + E_FLOAT (ent, sv_fields.nextthink) = -1; + E_FLOAT (ent, sv_fields.solid) = 0; + VectorZero (E_VECTOR (ent, sv_fields.origin)); + VectorZero (E_VECTOR (ent, sv_fields.angles)); } else { ED_ClearEdict (pr, ent, 0); } @@ -115,7 +204,7 @@ prune_edict (progs_t *pr, edict_t *ent) return 1; } else { // remove things from different skill levels or deathmatch - if (deathmatch->int_val) { + if (deathmatch) { if (((int) SVfloat (ent, spawnflags) & SPAWNFLAG_NOT_DEATHMATCH)) { return 1; } @@ -149,10 +238,17 @@ static void ED_PrintEdict_f (void) { int i; + const char *fieldname = 0; + if (Cmd_Argc () < 2) { + SV_Printf ("edict num [fieldname]\n"); + return; + } + if (Cmd_Argc () >= 3) { + fieldname = Cmd_Argv (2); + } i = atoi (Cmd_Argv (1)); - SV_Printf ("\n EDICT %i:\n", i); - ED_PrintNum (&sv_pr_state, i); + ED_PrintNum (&sv_pr_state, i, fieldname); } static void @@ -343,7 +439,7 @@ static sv_def_t qw_opt_funcs[] = { }; static sv_def_t qw_opt_fields[] = { - {ev_integer, 0, "rotated_bbox", &sv_fields.rotated_bbox}, + {ev_int, 0, "rotated_bbox", &sv_fields.rotated_bbox}, {ev_float, 0, "alpha", &sv_fields.alpha}, {ev_float, 0, "scale", &sv_fields.scale}, {ev_float, 0, "glow_size", &sv_fields.glow_size}, @@ -363,22 +459,30 @@ set_address (sv_def_t *def, void *address) switch (def->type) { case ev_void: case ev_short: + case ev_ushort: case ev_invalid: case ev_type_count: break; case ev_float: case ev_vector: - case ev_quat: + case ev_quaternion: *(float **)def->field = (float *) address; break; + case ev_double: + *(double **)def->field = (double *) address; + break; case ev_string: case ev_entity: case ev_field: case ev_func: - case ev_pointer: - case ev_integer: - case ev_uinteger: - *(int **)def->field = (int *) address; + case ev_ptr: + case ev_int: + case ev_uint: + *(pr_int_t **)def->field = (pr_int_t *) address; + break; + case ev_long: + case ev_ulong: + *(pr_long_t **)def->field = (pr_long_t *) address; break; } } @@ -386,7 +490,7 @@ set_address (sv_def_t *def, void *address) static int resolve_globals (progs_t *pr, sv_def_t *def, int mode) { - ddef_t *ddef; + pr_def_t *ddef; int ret = 1; if (mode == 2) { @@ -414,13 +518,13 @@ resolve_functions (progs_t *pr, sv_def_t *def, int mode) if (mode == 2) { for (; def->name; def++) - *(func_t *) def->field = G_FUNCTION (pr, def->offset); + *(pr_func_t *) def->field = G_FUNCTION (pr, def->offset); return 1; } for (; def->name; def++) { dfunc = PR_FindFunction (pr, def->name); if (dfunc) { - *(func_t *) def->field = dfunc - pr->pr_functions; + *(pr_func_t *) def->field = dfunc - pr->pr_functions; } else if (mode) { PR_Undefined (pr, "function", def->name); ret = 0; @@ -432,7 +536,7 @@ resolve_functions (progs_t *pr, sv_def_t *def, int mode) static int resolve_fields (progs_t *pr, sv_def_t *def, int mode) { - ddef_t *ddef; + pr_def_t *ddef; int ret = 1; if (mode == 2) { @@ -477,71 +581,87 @@ resolve (progs_t *pr) resolve_fields (pr, qw_opt_fields, 0); // progs engine needs these globals anyway sv_pr_state.globals.self = sv_globals.self; - sv_pr_state.globals.time = sv_globals.time; + sv_pr_state.globals.ftime = sv_globals.time;//FIXME double time return ret; } +static int +sv_init_edicts (progs_t *pr) +{ + int i; + + memset (sv_edicts, 0, sizeof (sv_edicts)); + memset (sv_data, 0, sizeof (sv_data)); + + // init the data field of the edicts + for (i = 0; i < MAX_EDICTS; i++) { + edict_t *ent = EDICT_NUM (&sv_pr_state, i); + ent->pr = &sv_pr_state; + ent->entnum = i; + ent->edict = EDICT_TO_PROG (&sv_pr_state, ent); + ent->edata = &sv_data[i]; + SVdata (ent)->edict = ent; + } + + return 1; +} + void SV_LoadProgs (void) { const char *progs_name = "qwprogs.dat"; const char *range; - int i; - if (strequal (sv_progs_ext->string, "qf")) { + if (strequal (sv_progs_ext, "qf")) { sv_range = PR_RANGE_QF; range = "QF"; - } else if (strequal (sv_progs_ext->string, "id")) { + } else if (strequal (sv_progs_ext, "id")) { sv_range = PR_RANGE_ID; range = "ID"; - } else if (strequal (sv_progs_ext->string, "qwe") - || strequal (sv_progs_ext->string, "ktpro")) { + } else if (strequal (sv_progs_ext, "qwe") + || strequal (sv_progs_ext, "ktpro")) { sv_range = PR_RANGE_QWE; range = "QWE/KTPro"; - } else if (strequal (sv_progs_ext->string, "cpqw")) { + } else if (strequal (sv_progs_ext, "cpqw")) { sv_range = PR_RANGE_CPQW; range = "CPQW"; } else { sv_range = PR_RANGE_NONE; range = "None"; } - Sys_MaskPrintf (SYS_DEV, "Using %s builtin extention mapping\n", range); + Sys_MaskPrintf (SYS_dev, "Using %s builtin extention mapping\n", range); memset (&sv_globals, 0, sizeof (sv_funcs)); memset (&sv_funcs, 0, sizeof (sv_funcs)); Info_RemoveKey (svs.info, "sky"); - if (*r_skyname->string) - Info_SetValueForKey (svs.info, "sky", r_skyname->string, 0); + if (*r_skyname) + Info_SetValueForKey (svs.info, "sky", r_skyname, 0); sv_cbuf->unknown_command = 0; ucmd_unknown = 0; if (qfs_gamedir->gamecode && *qfs_gamedir->gamecode) progs_name = qfs_gamedir->gamecode; - if (*sv_progs->string) - progs_name = sv_progs->string; + if (*sv_progs) + progs_name = sv_progs; - PR_LoadProgs (&sv_pr_state, progs_name, MAX_EDICTS, - sv_progs_zone->int_val * 1024); + sv_pr_state.max_edicts = MAX_EDICTS; + sv_pr_state.zone_size = sv_progs_zone * 1024; + sv_pr_state.stack_size = sv_progs_stack * 1024; + sv.edicts = sv_edicts; + + PR_LoadProgs (&sv_pr_state, progs_name); if (!sv_pr_state.progs) Sys_Error ("SV_LoadProgs: couldn't load %s", progs_name); - - memset (sv_data, 0, sizeof (sv_data)); - - // init the data field of the edicts - for (i = 0; i < MAX_EDICTS; i++) { - edict_t *ent = EDICT_NUM (&sv_pr_state, i); - ent->entnum = i; - ent->edata = &sv_data[i]; - SVdata (ent)->edict = ent; - } } void SV_Progs_Init (void) { + SV_Progs_Init_Cvars (); + pr_gametype = "quakeworld"; - sv_pr_state.edicts = &sv.edicts; + sv_pr_state.pr_edicts = &sv.edicts; sv_pr_state.num_edicts = &sv.num_edicts; sv_pr_state.reserved_edicts = &reserved_edicts; sv_pr_state.unlink = SV_UnlinkEdict; @@ -552,6 +672,9 @@ SV_Progs_Init (void) sv_pr_state.bi_map = bi_map; sv_pr_state.resolve = resolve; + PR_AddLoadFunc (&sv_pr_state, sv_init_edicts); + PR_Init (&sv_pr_state); + SV_PR_Cmds_Init (); SV_PR_QWE_Init (&sv_pr_state); SV_PR_CPQW_Init (&sv_pr_state); @@ -571,25 +694,16 @@ SV_Progs_Init (void) void SV_Progs_Init_Cvars (void) { - r_skyname = Cvar_Get ("r_skyname", "", CVAR_NONE, NULL, - "Default name of skybox if none given by map"); - sv_progs = Cvar_Get ("sv_progs", "", CVAR_NONE, NULL, - "Override the default game progs."); - sv_progs_zone = Cvar_Get ("sv_progs_zone", "256", CVAR_NONE, NULL, - "size of the zone for progs in kb"); - sv_progs_ext = Cvar_Get ("sv_progs_ext", "qf", CVAR_NONE, NULL, - "extention mapping to use: " - "none, id, qf, qwe, ktpro, cpqw"); - pr_checkextensions = Cvar_Get ("pr_checkextensions", "1", CVAR_ROM, NULL, - "indicate the presence of the " - "checkextentions qc function"); - sv_old_entity_free = Cvar_Get ("sv_old_entity_free", "1", CVAR_NONE, NULL, - "set this for buggy mods that rely on the" - " old behaviour of entity freeing (eg," - " *TF)"); - sv_hide_version_info = Cvar_Get ("sv_hide_version_info", "0", CVAR_ROM, - NULL, "hide QuakeForge specific " - "serverinfo strings from terminally " - "stupid progs (eg, braindead TF " - "variants)"); + PR_Init_Cvars (); + + Cvar_Register (&r_skyname_cvar, 0, 0); + Cvar_Register (&sv_progs_cvar, 0, 0); + Cvar_Register (&sv_progs_zone_cvar, 0, 0); + Cvar_Register (&sv_progs_stack_cvar, 0, 0); + Cvar_Register (&sv_progs_ext_cvar, 0, 0); + Cvar_Register (&pr_checkextensions_cvar, 0, 0); + Cvar_Register (&sv_old_entity_free_cvar, 0, 0); + Cvar_Register (&sv_hide_version_info_cvar, 0, 0); + Cvar_Register (&sv_aim_cvar, 0, 0); + Cvar_Register (&pr_double_remove_cvar, 0, 0); } diff --git a/qw/source/sv_qtv.c b/qw/source/sv_qtv.c index a36b1c3cc..95a1e5a04 100644 --- a/qw/source/sv_qtv.c +++ b/qw/source/sv_qtv.c @@ -48,16 +48,17 @@ #include "QF/va.h" #include "compat.h" -#include "server.h" -#include "sv_qtv.h" -#include "sv_recorder.h" + +#include "qw/include/server.h" +#include "qw/include/sv_qtv.h" +#include "qw/include/sv_recorder.h" typedef struct { netchan_t netchan; backbuf_t backbuf; info_t *info; info_key_t *name_key; - qboolean packet; + bool packet; recorder_t *recorder; sizebuf_t datagram; byte datagram_buf[MAX_DATAGRAM]; @@ -193,9 +194,9 @@ qtv_prespawn_f (sv_qtv_t *proxy) buf = 0; if (buf == sv.num_signon_buffers - 1) - command = va ("cmd spawn %i 0\n", svs.spawncount); + command = va (0, "cmd spawn %i 0\n", svs.spawncount); else - command = va ("cmd prespawn %i %i\n", svs.spawncount, buf + 1); + command = va (0, "cmd prespawn %i %i\n", svs.spawncount, buf + 1); size = (3 + sv.signon_buffer_size[buf]) + (1 + strlen (command) + 1); msg = MSG_ReliableCheckBlock (&proxy->backbuf, size); @@ -380,6 +381,7 @@ qtv_reliable_send (sv_qtv_t *proxy) byte *buf = 0; SV_Printf ("proxy->begun: %d\n", proxy->begun); + SZ_Clear (&proxy->netchan.message); if (!proxy->begun) { MSG_WriteByte (&proxy->netchan.message, qtv_packet); pos = proxy->netchan.message.cursize; @@ -403,7 +405,7 @@ SV_qtvConnect (int qport, info_t *info) if (!(proxy = alloc_proxy ())) { SV_Printf ("%s:full proxy connect\n", NET_AdrToString (net_from)); - Netchan_OutOfBandPrint (net_from, "%c\nserver is full\n\n", A2C_PRINT); + SV_OutOfBandPrint (net_from, "%c\nserver is full\n\n", A2C_PRINT); return; } proxy->info = info; @@ -417,7 +419,7 @@ SV_qtvConnect (int qport, info_t *info) proxy->datagram.maxsize = sizeof (proxy->datagram_buf); proxy->begun = 0; - Netchan_OutOfBandPrint (net_from, "%c", S2C_CONNECTION); + SV_OutOfBandPrint (net_from, "%c", S2C_CONNECTION); } int @@ -433,7 +435,7 @@ SV_qtvPacket (int qport) if (proxies[i].netchan.qport != qport) continue; if (proxies[i].netchan.remote_address.port != net_from.port) { - Sys_MaskPrintf (SYS_DEV, + Sys_MaskPrintf (SYS_dev, "SV_ReadPackets: fixing up a translated port\n"); proxies[i].netchan.remote_address.port = net_from.port; } @@ -454,7 +456,7 @@ SV_qtvCheckTimeouts (void) float droptime; sv_qtv_t *proxy; - droptime = realtime - sv_timeout->value; + droptime = realtime - sv_timeout; for (i = 0; i < MAX_PROXIES; i++) { proxy = proxies + i; @@ -533,7 +535,7 @@ SV_qtvChanging (void) int i, len; const char *msg; - msg = va ("%cchanging", qtv_stringcmd); + msg = va (0, "%cchanging", qtv_stringcmd); len = strlen (msg) + 1; for (i = 0; i < MAX_PROXIES; i++) { proxy = proxies + i; @@ -554,7 +556,7 @@ SV_qtvReconnect (void) int i, len; const char *msg; - msg = va ("%creconnect", qtv_stringcmd); + msg = va (0, "%creconnect", qtv_stringcmd); len = strlen (msg) + 1; for (i = 0; i < MAX_PROXIES; i++) { proxy = proxies + i; diff --git a/qw/source/sv_recorder.c b/qw/source/sv_recorder.c index 562fd94d3..927cd975f 100644 --- a/qw/source/sv_recorder.c +++ b/qw/source/sv_recorder.c @@ -49,15 +49,15 @@ #include "QF/sys.h" #include "qw/bothdefs.h" -#include "server.h" -#include "sv_demo.h" -#include "sv_progs.h" -#include "sv_recorder.h" +#include "qw/include/server.h" +#include "qw/include/sv_demo.h" +#include "qw/include/sv_progs.h" +#include "qw/include/sv_recorder.h" typedef struct dbuffer_s { byte *data; - int start, end, last; - int maxsize; + unsigned start, end, last; + unsigned maxsize; } dbuffer_t; typedef struct header_s { @@ -70,7 +70,7 @@ typedef struct header_s { typedef struct demobuf_s { sizebuf_t sz; - int bufsize; + unsigned bufsize; header_t *h; } demobuf_t; @@ -121,7 +121,7 @@ static byte datagram_data[MAX_DATAGRAM]; static byte msg_buffer[2][MAX_DATAGRAM]; #define MIN_DEMO_MEMORY 0x100000 -#define USECACHE (sv_demoUseCache->int_val && svs.demomemsize) +#define USECACHE (sv_demoUseCache && svs.demomemsize) #define MAXSIZE (rec.dbuffer.end < rec.dbuffer.last ? \ rec.dbuffer.start - rec.dbuffer.end : \ rec.dbuffer.maxsize - rec.dbuffer.end) @@ -200,9 +200,9 @@ write_msg (sizebuf_t *msg, int type, int to, float time, sizebuf_t *dst) static void write_to_msg (int type, int to, float time, sizebuf_t *dst) { - int pos = 0; + unsigned pos = 0; header_t *p; - int size; + unsigned size; sizebuf_t msg; p = (header_t *) rec.dbuf->sz.data; @@ -256,7 +256,7 @@ static void set_buf (byte type, int to) { header_t *p; - int pos = 0; + unsigned pos = 0; p = (header_t *) rec.dbuf->sz.data; @@ -464,10 +464,10 @@ write_packet (void) } sizebuf_t * -SVR_WriteBegin (byte type, int to, int size) +SVR_WriteBegin (byte type, int to, unsigned size) { byte *p; - qboolean move = false; + bool move = false; // will it fit? while (rec.dbuf->bufsize + size + HEADER > rec.dbuf->sz.maxsize) { @@ -559,7 +559,7 @@ SVR_SendMessages (void) int stats[MAX_CL_STATS]; recorder_t *r; - if (sv_demoPings->value && sv.time - rec.pingtime > sv_demoPings->value) { + if (sv_demoPings && sv.time - rec.pingtime > sv_demoPings) { demo_pings (); rec.pingtime = sv.time; } diff --git a/qw/source/sv_sbar.c b/qw/source/sv_sbar.c index a76729789..0989ee2b6 100644 --- a/qw/source/sv_sbar.c +++ b/qw/source/sv_sbar.c @@ -32,20 +32,24 @@ #endif #include "QF/console.h" -#include "QF/view.h" #include "QF/va.h" +#include "QF/ui/view.h" + #include "QF/plugin/console.h" -#include "server.h" #include "sv_console.h" -#include "sv_recorder.h" + +#include "qw/include/server.h" +#include "qw/include/sv_progs.h" +#include "qw/include/sv_recorder.h" static void -draw_cpu (view_t *view) +draw_cpu (view_t view) { - sv_view_t *sv_view = view->data; + sv_view_t *sv_view = Ent_GetComponent (view.id, server_view, view.reg); sv_sbar_t *sb = sv_view->obj; + view_pos_t rel = View_GetRel (view); double cpu; const char *cpu_str; const char *s; @@ -54,47 +58,88 @@ draw_cpu (view_t *view) cpu = (svs.stats.latched_active + svs.stats.latched_idle); cpu = 100 * svs.stats.latched_active / cpu; - cpu_str = va ("[CPU: %3d%%]", (int) cpu); - for (s = cpu_str, d = sb->text + view->xrel; *s; s++) + cpu_str = va (0, "[CPU: %3d%%]", (int) cpu); + for (s = cpu_str, d = sb->text + rel.x; *s; s++) *d++ = *s; if (cpu > 70.0) { int i; for (i = 6; i < 9; i++) - sb->text[view->xrel + i] |= 0x80; + sb->text[rel.x + i] |= 0x80; } } static void -draw_rec (view_t *view) +draw_rec (view_t view) { - sv_view_t *sv_view = view->data; + sv_view_t *sv_view = Ent_GetComponent (view.id, server_view, view.reg); sv_sbar_t *sb = sv_view->obj; + view_pos_t rel = View_GetRel (view); const char *str; const char *s; char *d; - str = va ("[REC: %d]", SVR_NumRecorders ()); - for (s = str, d = sb->text + view->xrel; *s; s++) + str = va (0, "[REC: %d]", SVR_NumRecorders ()); + for (s = str, d = sb->text + rel.x; *s; s++) *d++ = *s; } +static void +draw_mem (view_t view) +{ + sv_view_t *sv_view = Ent_GetComponent (view.id, server_view, view.reg); + sv_sbar_t *sb = sv_view->obj; + view_pos_t rel = View_GetRel (view); + const char *str; + const char *s; + char *d; + size_t used, size; + byte mask = 0; + + Z_MemInfo (sv_pr_state.zone, &used, &size); + str = va (0, "[mem: %4zd / %-4zd]", used / 1024, size / 1024); + if (used / (size / 256) >= 192) { + mask = 0x80; + } + for (s = str, d = sb->text + rel.x; *s; s++) + *d++ = *s | mask; +} + void SV_Sbar_Init (void) { - view_t *status; - view_t *view; + view_t status; + view_t view; if (!con_module || !con_module->data->console->status_view) return; - status = con_module->data->console->status_view; - view = view_new (0, 0, 11, 1, grav_northwest); - view->draw = draw_cpu; - view->data = status->data; - view_add (status, view); + status = *con_module->data->console->status_view; + void *comp = Ent_GetComponent (status.id, server_window, status.reg); + sv_view_t sv_view = *(sv_view_t *) comp; + sv_view.setgeometry = 0; - view = view_new (11, 0, 8, 1, grav_northwest); - view->draw = draw_rec; - view->data = status->data; - view_add (status, view); + ecs_system_t viewsys = { .reg = status.reg, + .base = status.comp - view_href }; + view = View_New (viewsys, status); + View_SetPos (view, 0, 0); + View_SetLen (view, 11, 1); + View_SetGravity (view, grav_northwest); + sv_view.draw = draw_cpu; + Ent_SetComponent (view.id, server_view, view.reg, &sv_view); + + view = View_New (viewsys, status); + View_SetPos (view, 11, 0); + View_SetLen (view, 8, 1); + View_SetGravity (view, grav_northwest); + sv_view.draw = draw_rec; + Ent_SetComponent (view.id, server_view, view.reg, &sv_view); + + view = View_New (viewsys, status); + View_SetPos (view, 19, 0); + View_SetLen (view, 18, 1); + View_SetGravity (view, grav_northwest); + sv_view.draw = draw_mem; + Ent_SetComponent (view.id, server_view, view.reg, &sv_view); + + View_UpdateHierarchy (status); } diff --git a/qw/source/sv_send.c b/qw/source/sv_send.c index 58fb00bef..b53e37899 100644 --- a/qw/source/sv_send.c +++ b/qw/source/sv_send.c @@ -43,14 +43,15 @@ #include "QF/console.h" #include "QF/dstring.h" #include "QF/msg.h" -#include "QF/sound.h" // FIXME: DEFAULT_SOUND_PACKET_* +#include "QF/set.h" #include "QF/sys.h" -#include "qw/bothdefs.h" #include "compat.h" -#include "server.h" -#include "sv_progs.h" -#include "sv_recorder.h" + +#include "qw/bothdefs.h" +#include "qw/include/server.h" +#include "qw/include/sv_progs.h" +#include "qw/include/sv_recorder.h" #define CHAN_AUTO 0 #define CHAN_WEAPON 1 @@ -148,6 +149,23 @@ SV_EndRedirect (void) } #define MAXPRINTMSG 4096 +static int +find_userid (const char *name) +{ + int i; + + for (i = 0; i < MAX_CLIENTS; i++) { + if (!svs.clients[i].state) + continue; + if (!strcmp (svs.clients[i].name, name)) { + return svs.clients[i].userid; + } + } + return 0; +} + +#define hstrftime ((size_t (*)(char *s, size_t, const char *, \ + const struct tm*))strftime) /* SV_Printf @@ -158,82 +176,74 @@ SV_EndRedirect (void) void SV_Print (const char *fmt, va_list args) { + static dstring_t *premsg; + static dstring_t *msg; + static dstring_t *msg2; static int pending = 0; // partial line being printed - char premsg[MAXPRINTMSG]; - unsigned char msg[MAXPRINTMSG]; - char msg2[MAXPRINTMSG]; + char msg3[MAXPRINTMSG]; time_t mytime = 0; struct tm *local = NULL; - qboolean timestamps = false; + bool timestamps = false; char *in; - unsigned char *out; - vsnprintf (premsg, sizeof (premsg), fmt, args); - in = premsg; - out = msg; + if (!premsg) { + premsg = dstring_newstr (); + msg = dstring_new (); + msg2 = dstring_new (); + } + dstring_clearstr (msg); - if (!*premsg) + dvsprintf (premsg, fmt, args); + in = premsg->str; + + if (!*premsg->str) return; // expand FFnickFF to nick do { - switch ((byte) *in) { - case 0xFF: { - char *end = strchr (in + 1, 0xFF); - int userid = 0; - int len; - int i; - - if (!end) - end = in + strlen (in); - *end = '\0'; - for (i = 0; i < MAX_CLIENTS; i++) { - if (!svs.clients[i].state) - continue; - if (!strcmp (svs.clients[i].name, in + 1)) { - userid = svs.clients[i].userid; - break; - } - } - len = snprintf ((char *) out, sizeof (msg) - (out - msg), - "%s <%d>", in + 1, userid); - out += len; - in = end + 1; - break; + char *beg = strchr (in, 0xFF); + if (beg) { + char *name = beg + 1; + char *end = strchr (name, 0xFF); + if (!end) { + end = beg + strlen (name); } - default: - *out++ = *in++; + *end = 0; + dstring_appendsubstr (msg, in, beg - in); + dasprintf (msg, "%s <%d>", name, find_userid (name)); + in = end + 1; + } else { + dstring_appendstr (msg, in); + break; } - } while (sizeof (msg) - (out - msg) > 0 && *in); - *out = '\0'; + } while (*in); if (sv_redirected) { // Add to redirected message - dstring_appendstr (&outputbuf, (char *) msg); + dstring_appendstr (&outputbuf, msg->str); } - if (*msg && !con_printf_no_log) { + if (*msg->str && !con_printf_no_log) { // We want to output to console and maybe logfile - if (sv_timestamps && sv_timefmt && sv_timefmt->string - && sv_timestamps->int_val && !pending) + if (sv_timefmt && sv_timestamps && !pending) timestamps = true; if (timestamps) { mytime = time (NULL); local = localtime (&mytime); - strftime (msg3, sizeof (msg3), sv_timefmt->string, local); + hstrftime (msg3, sizeof (msg3), sv_timefmt, local); - snprintf (msg2, sizeof (msg2), "%s%s", msg3, msg); + dsprintf (msg2, "%s%s", msg3, msg->str); } else { - snprintf (msg2, sizeof (msg2), "%s", msg); + dsprintf (msg2, "%s", msg->str); } - if (msg2[strlen (msg2) - 1] != '\n') { + if (msg2->str[strlen (msg2->str) - 1] != '\n') { pending = 1; } else { pending = 0; } - Con_Printf ("%s", msg2); // also echo to debugging console + Con_Printf ("%s", msg2->str); // also echo to debugging console } } @@ -284,17 +294,19 @@ SV_PrintToClient (client_t *cl, int level, const char *string) void SV_Multicast (const vec3_t origin, int to) { - byte *mask; + set_t *mask; client_t *client; int leafnum, j; mleaf_t *leaf; - qboolean reliable; + bool reliable; + mod_brush_t *brush = &sv.worldmodel->brush; - leaf = Mod_PointInLeaf (origin, sv.worldmodel); + vec4f_t org = { VectorExpand (origin), 1 }; + leaf = Mod_PointInLeaf (org, sv.worldmodel); if (!leaf) leafnum = 0; else - leafnum = leaf - sv.worldmodel->leafs; + leafnum = leaf - sv.worldmodel->brush.leafs; reliable = false; @@ -302,23 +314,22 @@ SV_Multicast (const vec3_t origin, int to) case MULTICAST_ALL_R: reliable = true; // intentional fallthrough case MULTICAST_ALL: - mask = sv.pvs; // leaf 0 is everything; + mask = &sv.pvs[0]; // leaf 0 is everything; break; case MULTICAST_PHS_R: reliable = true; // intentional fallthrough case MULTICAST_PHS: - mask = sv.phs + leafnum * 4 * ((sv.worldmodel->numleafs + 31) >> 5); + mask = &sv.phs[leafnum]; break; case MULTICAST_PVS_R: reliable = true; // intentional fallthrough case MULTICAST_PVS: - mask = sv.pvs + leafnum * 4 * ((sv.worldmodel->numleafs + 31) >> 5); + mask = &sv.pvs[leafnum]; break; default: - mask = NULL; Sys_Error ("SV_Multicast: bad to:%i", to); } @@ -335,12 +346,12 @@ SV_Multicast (const vec3_t origin, int to) goto inrange; } - leaf = Mod_PointInLeaf (SVvector (client->edict, origin), - sv.worldmodel); + org = (vec4f_t) {VectorExpand (SVvector (client->edict, origin)), 1}; + leaf = Mod_PointInLeaf (org, sv.worldmodel); if (leaf) { // -1 is because pvs rows are 1 based, not 0 based like leafs - leafnum = leaf - sv.worldmodel->leafs - 1; - if (!(mask[leafnum >> 3] & (1 << (leafnum & 7)))) { + leafnum = leaf - brush->leafs - 1; + if (!set_is_member (mask, leafnum)) { // SV_Printf ("supressed multicast\n"); continue; } @@ -385,8 +396,8 @@ SV_StartSound (edict_t *entity, int channel, const char *sample, int volume, float attenuation) { int ent, sound_num, i; - qboolean use_phs; - qboolean reliable = false; + bool use_phs; + bool reliable = false; vec3_t origin; if (volume < 0 || volume > 255) @@ -411,7 +422,7 @@ SV_StartSound (edict_t *entity, int channel, const char *sample, int volume, ent = NUM_FOR_EDICT (&sv_pr_state, entity); - if ((channel & 8) || !sv_phs->int_val) // no PHS flag + if ((channel & 8) || !sv_phs) // no PHS flag { if (channel & 8) reliable = true; // sounds that break the phs are @@ -602,7 +613,7 @@ SV_UpdateClientStats (client_t *client) } } -static qboolean +static bool SV_SendClientDatagram (client_t *client) { byte buf[MAX_DATAGRAM]; diff --git a/qw/source/sv_sys_unix.c b/qw/source/sv_sys_unix.c index 315fb02ff..96b0c67b9 100644 --- a/qw/source/sv_sys_unix.c +++ b/qw/source/sv_sys_unix.c @@ -43,7 +43,7 @@ #include "QF/qargs.h" #include "QF/sys.h" -#include "server.h" +#include "qw/include/server.h" #ifdef NeXT # include @@ -122,7 +122,7 @@ main (int argc, const char **argv) SV_Init (); - Sys_RegisterShutdown (Net_LogStop); + Sys_RegisterShutdown (Net_LogStop, 0); // run one frame immediately for first heartbeat SV_Frame (0.1); @@ -138,9 +138,8 @@ main (int argc, const char **argv) SV_Frame (time); - // extrasleep is just a way to generate a fucked up connection on - // purpose - if (sys_extrasleep->int_val) - usleep (sys_extrasleep->int_val); + // extrasleep is just a way to generate a bad connection on purpose + if (sys_extrasleep) + usleep (sys_extrasleep); } } diff --git a/qw/source/sv_sys_win.c b/qw/source/sv_sys_win.c index 4397f5d92..ed907dee9 100644 --- a/qw/source/sv_sys_win.c +++ b/qw/source/sv_sys_win.c @@ -36,9 +36,9 @@ #include "QF/qargs.h" #include "QF/sys.h" -#include "server.h" +#include "qw/include/server.h" -qboolean WinNT; +bool WinNT; server_static_t svs; static void @@ -81,7 +81,7 @@ main (int argc, const char **argv) SV_Init (); if (COM_CheckParm ("-nopriority")) { - Cvar_Set (sys_sleep, "0"); + Cvar_Set ("sys_sleep", "0"); } else { if (!SetPriorityClass (GetCurrentProcess (), HIGH_PRIORITY_CLASS)) SV_Printf ("SetPriorityClass() failed\n"); @@ -91,9 +91,9 @@ main (int argc, const char **argv) // sys_sleep > 0 seems to cause packet loss on WinNT (why?) if (WinNT) - Cvar_Set (sys_sleep, "0"); + Cvar_Set ("sys_sleep", "0"); - Sys_RegisterShutdown (Net_LogStop); + Sys_RegisterShutdown (Net_LogStop, 0); // run one frame immediately for first heartbeat SV_Frame (0.1); diff --git a/qw/source/sv_user.c b/qw/source/sv_user.c index 5398eff6c..26b58a6a7 100644 --- a/qw/source/sv_user.c +++ b/qw/source/sv_user.c @@ -53,16 +53,17 @@ #include "QF/sys.h" #include "QF/va.h" +#include "compat.h" + #include "qw/msg_ucmd.h" #include "qw/msg_ucmd.h" #include "qw/bothdefs.h" -#include "compat.h" #include "qw/pmove.h" -#include "server.h" -#include "sv_gib.h" -#include "sv_progs.h" -#include "sv_recorder.h" +#include "qw/include/server.h" +#include "qw/include/sv_gib.h" +#include "qw/include/sv_progs.h" +#include "qw/include/sv_recorder.h" #include "world.h" typedef struct ucmd_s { @@ -79,36 +80,217 @@ edict_t *sv_player; usercmd_t cmd; -cvar_t *sv_antilag; -cvar_t *sv_antilag_frac; +int sv_maxrate; +static cvar_t sv_maxrate_cvar = { + .name = "sv_maxrate", + .description = + "Maximum allowable rate", + .default_value = "10000", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_int, .value = &sv_maxrate }, +}; + // capped) +int sv_antilag; +static cvar_t sv_antilag_cvar = { + .name = "sv_antilag", + .description = + "Attempt to backdate impacts to compensate for lag. 0=completely off. " + "1=mod-controlled. 2=forced, which might break certain uses of " + "traceline.", + .default_value = "1", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_int, .value = &sv_antilag }, +}; +float sv_antilag_frac; +static cvar_t sv_antilag_frac_cvar = { + .name = "sv_antilag_frac", + .description = + "FIXME something to do with sv_antilag", + .default_value = "1", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_float, .value = &sv_antilag_frac }, +}; -cvar_t *sv_accelerate; -cvar_t *sv_airaccelerate; -cvar_t *sv_maxspeed; -cvar_t *sv_spectatormaxspeed; -cvar_t *sv_wateraccelerate; -cvar_t *sv_waterfriction; +float sv_accelerate; +static cvar_t sv_accelerate_cvar = { + .name = "sv_accelerate", + .description = + "None", + .default_value = "10", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_accelerate }, +}; +float sv_airaccelerate; +static cvar_t sv_airaccelerate_cvar = { + .name = "sv_airaccelerate", + .description = + "Sets how quickly the players accelerate in air", + .default_value = "0.7", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_airaccelerate }, +}; +float sv_maxspeed; +static cvar_t sv_maxspeed_cvar = { + .name = "sv_maxspeed", + .description = + "None", + .default_value = "320", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_float, .value = &sv_maxspeed }, +}; +float sv_spectatormaxspeed; +static cvar_t sv_spectatormaxspeed_cvar = { + .name = "sv_spectatormaxspeed", + .description = + "Sets the maximum speed a spectator can move", + .default_value = "500", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_spectatormaxspeed }, +}; +float sv_wateraccelerate; +static cvar_t sv_wateraccelerate_cvar = { + .name = "sv_wateraccelerate", + .description = + "Sets the water acceleration value", + .default_value = "10", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_wateraccelerate }, +}; +float sv_waterfriction; +static cvar_t sv_waterfriction_cvar = { + .name = "sv_waterfriction", + .description = + "Sets the water friction value", + .default_value = "4", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_waterfriction }, +}; -cvar_t *sv_allowfake; +int sv_allowfake; +static cvar_t sv_allowfake_cvar = { + .name = "sv_allowfake", + .description = + "Allow 'fake' messages (FuhQuake $\\). 1 = always, 2 = only say_team", + .default_value = "2", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_allowfake }, +}; -cvar_t *cl_rollspeed; -cvar_t *cl_rollangle; -cvar_t *sv_spectalk; +float cl_rollspeed; +static cvar_t cl_rollspeed_cvar = { + .name = "cl_rollspeed", + .description = + "How quickly you straighten out after strafing", + .default_value = "200", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &cl_rollspeed }, +}; +float cl_rollangle; +static cvar_t cl_rollangle_cvar = { + .name = "cl_rollangle", + .description = + "How much your screen tilts when strafing", + .default_value = "2.0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &cl_rollangle }, +}; +int sv_spectalk; +static cvar_t sv_spectalk_cvar = { + .name = "sv_spectalk", + .description = + "Toggles the ability of spectators to talk to players", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_spectalk }, +}; -cvar_t *sv_kickfake; +int sv_kickfake; +static cvar_t sv_kickfake_cvar = { + .name = "sv_kickfake", + .description = + "Kick users sending to send fake talk messages", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_kickfake }, +}; -cvar_t *sv_mapcheck; +int sv_mapcheck; +static cvar_t sv_mapcheck_cvar = { + .name = "sv_mapcheck", + .description = + "Toggle the use of map checksumming to check for players who edit maps" + " to cheat", + .default_value = "1", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_mapcheck }, +}; -cvar_t *sv_timecheck_mode; -cvar_t *sv_timekick; -cvar_t *sv_timekick_fuzz; -cvar_t *sv_timekick_interval; -cvar_t *sv_timecheck_fuzz; -cvar_t *sv_timecheck_decay; +int sv_timecheck_mode; +static cvar_t sv_timecheck_mode_cvar = { + .name = "sv_timecheck_mode", + .description = + "select between timekick (0, default) and timecheck (1)", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_timecheck_mode }, +}; +int sv_timekick; +static cvar_t sv_timekick_cvar = { + .name = "sv_timekick", + .description = + "Time cheat protection", + .default_value = "3", + .flags = CVAR_SERVERINFO, + .value = { .type = &cexpr_int, .value = &sv_timekick }, +}; +float sv_timekick_fuzz; +static cvar_t sv_timekick_fuzz_cvar = { + .name = "sv_timekick_fuzz", + .description = + "Time cheat \"fuzz factor\" in milliseconds", + .default_value = "30", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_timekick_fuzz }, +}; +float sv_timekick_interval; +static cvar_t sv_timekick_interval_cvar = { + .name = "sv_timekick_interval", + .description = + "Time cheat check interval in seconds", + .default_value = "30", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &sv_timekick_interval }, +}; +int sv_timecheck_fuzz; +static cvar_t sv_timecheck_fuzz_cvar = { + .name = "sv_timecheck_fuzz", + .description = + "Milliseconds of tolerance before time cheat throttling kicks in.", + .default_value = "250", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_timecheck_fuzz }, +}; +int sv_timecheck_decay; +static cvar_t sv_timecheck_decay_cvar = { + .name = "sv_timecheck_decay", + .description = + "Rate at which time inaccuracies are \"forgiven\".", + .default_value = "2", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &sv_timecheck_decay }, +}; -cvar_t *sv_http_url_base; +char *sv_http_url_base; +static cvar_t sv_http_url_base_cvar = { + .name = "sv_http_url_base", + .description = + "set to base url for http redirects of downloaded files", + .default_value = "", + .flags = CVAR_NONE, + .value = { .type = 0, .value = &sv_http_url_base }, +}; -static void OutofBandPrintf (netadr_t where, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); +static void OutofBandPrintf (netadr_t where, const char *fmt, ...) __attribute__ ((format (PRINTF, 2, 3))); // USER STRINGCMD EXECUTION host_client and sv_player will be valid. @@ -140,7 +322,7 @@ SV_WriteWorldVars (netchan_t *netchan) // send server info string MSG_WriteByte (&netchan->message, svc_stufftext); MSG_WriteString (&netchan->message, - va ("fullserverinfo \"%s\"\n", + va (0, "fullserverinfo \"%s\"\n", Info_MakeString (svs.info, 0))); } @@ -193,7 +375,7 @@ SV_New_f (void *unused) // Trigger GIB connection event if (sv_client_connect_e->func) GIB_Event_Callback (sv_client_connect_e, 1, - va ("%u", host_client->userid)); + va (0, "%u", host_client->userid)); } void @@ -308,9 +490,9 @@ SV_Modellist_f (void *unused) static void SV_PreSpawn_f (void *unused) { - char *command; - int buf, size; - unsigned int check; + const char *command; + int buf, size; + unsigned check; sizebuf_t *msg; if (host_client->state != cs_connected) { @@ -332,16 +514,17 @@ SV_PreSpawn_f (void *unused) // should be three numbers following containing checksums check = atoi (Cmd_Argv (3)); -// Sys_MaskPrintf (SYS_DEV, , "Client check = %d\n", check); +// Sys_MaskPrintf (SYS_dev, , "Client check = %d\n", check); - if (sv_mapcheck->int_val && check != sv.worldmodel->checksum && - check != sv.worldmodel->checksum2) { + if (sv_mapcheck && check != sv.worldmodel->brush.checksum && + check != sv.worldmodel->brush.checksum2) { SV_ClientPrintf (1, host_client, PRINT_HIGH, "Map model file does " "not match (%s), %i != %i/%i.\n" "You may need a new version of the map, or the " "proper install files.\n", - sv.modelname, check, sv.worldmodel->checksum, - sv.worldmodel->checksum2); + sv.modelname, check, + sv.worldmodel->brush.checksum, + sv.worldmodel->brush.checksum2); SV_DropClient (host_client); return; } @@ -350,9 +533,9 @@ SV_PreSpawn_f (void *unused) host_client->prespawned = true; if (buf == sv.num_signon_buffers - 1) - command = va ("cmd spawn %i 0\n", svs.spawncount); + command = va (0, "cmd spawn %i 0\n", svs.spawncount); else - command = va ("cmd prespawn %i %i\n", svs.spawncount, buf + 1); + command = va (0, "cmd prespawn %i %i\n", svs.spawncount, buf + 1); size = sv.signon_buffer_size[buf] + 1 + strlen (command) + 1; @@ -376,7 +559,7 @@ SV_Spawn (client_t *client) // set up the edict ent = client->edict; - memset (&ent->v, 0, sv_pr_state.progs->entityfields * 4); + memset (&E_fld (ent, 0), 0, sv_pr_state.progs->entityfields * 4); SVfloat (ent, colormap) = NUM_FOR_EDICT (&sv_pr_state, ent); SVfloat (ent, team) = 0; // FIXME SVstring (ent, netname) = PR_SetString (&sv_pr_state, client->name); @@ -384,9 +567,9 @@ SV_Spawn (client_t *client) client->entgravity = 1.0; if (sv_fields.gravity != -1) SVfloat (ent, gravity) = 1.0; - client->maxspeed = sv_maxspeed->value; + client->maxspeed = sv_maxspeed; if (sv_fields.maxspeed != -1) - SVfloat (ent, maxspeed) = sv_maxspeed->value; + SVfloat (ent, maxspeed) = sv_maxspeed; } void @@ -489,16 +672,13 @@ SV_Spawn_f (void *unused) static void SV_SpawnSpectator (void) { - int i; - edict_t *e; - VectorZero (SVvector (sv_player, origin)); VectorZero (SVvector (sv_player, view_ofs)); SVvector (sv_player, view_ofs)[2] = 22; // search for an info_playerstart to spawn the spectator at - for (i = MAX_CLIENTS - 1; i < sv.num_edicts; i++) { - e = EDICT_NUM (&sv_pr_state, i); + for (unsigned i = MAX_CLIENTS - 1; i < sv.num_edicts; i++) { + edict_t *e = EDICT_NUM (&sv_pr_state, i); if (!strcmp (PR_GetString (&sv_pr_state, SVstring (e, classname)), "info_player_start")) { VectorCopy (SVvector (e, origin), SVvector (sv_player, origin)); @@ -604,7 +784,7 @@ SV_Begin_f (void *unused) // Trigger GIB events if (sv_client_spawn_e->func) - GIB_Event_Callback (sv_client_spawn_e, 1, va ("%u", + GIB_Event_Callback (sv_client_spawn_e, 1, va (0, "%u", host_client->userid)); } @@ -648,7 +828,7 @@ SV_NextUpload (void) { int percent, size; - if (!host_client->uploadfn) { + if (!host_client->upload) { SV_ClientPrintf (1, host_client, PRINT_HIGH, "Upload denied\n"); MSG_ReliableWrite_Begin (&host_client->backbuf, svc_stufftext, 8); MSG_ReliableWrite_String (&host_client->backbuf, "stopul"); @@ -663,16 +843,8 @@ SV_NextUpload (void) size = MSG_ReadShort (net_message); percent = MSG_ReadByte (net_message); - if (!host_client->upload) { - host_client->upload = QFS_Open (host_client->uploadfn->str, "wb"); - if (!host_client->upload) { - SV_Printf ("Can't create %s\n", host_client->uploadfn->str); - MSG_ReliableWrite_Begin (&host_client->backbuf, svc_stufftext, 8); - MSG_ReliableWrite_String (&host_client->backbuf, "stopul"); - dstring_delete (host_client->uploadfn); - host_client->uploadfn = 0; - return; - } + if (!host_client->upload_started) { + host_client->upload_started = 1; SV_Printf ("Receiving %s from %d...\n", host_client->uploadfn->str, host_client->userid); if (host_client->remote_snap) @@ -685,7 +857,7 @@ SV_NextUpload (void) net_message->readcount, size); net_message->readcount += size; - Sys_MaskPrintf (SYS_DEV, "UPLOAD: %d received\n", size); + Sys_MaskPrintf (SYS_dev, "UPLOAD: %d received\n", size); if (percent != 100) { MSG_ReliableWrite_Begin (&host_client->backbuf, svc_stufftext, 8); @@ -709,6 +881,7 @@ SV_NextUpload (void) } dstring_delete (host_client->uploadfn); host_client->uploadfn = 0; + host_client->upload_started = 0; } } @@ -723,19 +896,19 @@ SV_BeginDownload_f (void *unused) name = Cmd_Argv (1); // hacked by zoid to allow more conrol over download // first off, no .. or global allow check - if (strstr (name, "..") || !allow_download->int_val + if (strstr (name, "..") || !allow_download // leading dot is no good || *name == '.' // next up, skin check - || (strncmp (name, "skins/", 6) == 0 && !allow_download_skins->int_val) + || (strncmp (name, "skins/", 6) == 0 && !allow_download_skins) // now models || (strncmp (name, "progs/", 6) == 0 && - !allow_download_models->int_val) + !allow_download_models) // now sounds || (strncmp (name, "sound/", 6) == 0 && - !allow_download_sounds->int_val) + !allow_download_sounds) // now maps (note special case for maps, must not be in pak) - || (strncmp (name, "maps/", 5) == 0 && !allow_download_maps->int_val) + || (strncmp (name, "maps/", 5) == 0 && !allow_download_maps) // MUST be in a subdirectory || !strstr (name, "/")) { // don't allow anything with .. path MSG_ReliableWrite_Begin (&host_client->backbuf, svc_download, 4); @@ -750,13 +923,13 @@ SV_BeginDownload_f (void *unused) } zip = strchr (Info_ValueForKey (host_client->userinfo, "*cap"), 'z') != 0; - http = sv_http_url_base->string[0] + http = sv_http_url_base[0] && strchr (Info_ValueForKey (host_client->userinfo, "*cap"), 'h'); file = _QFS_FOpenFile (name, !zip); host_client->download = file; - host_client->downloadsize = Qfilesize (file); + host_client->downloadsize = file ? Qfilesize (file) : 0; host_client->downloadcount = 0; if (!host_client->download @@ -778,15 +951,15 @@ SV_BeginDownload_f (void *unused) if (http) { int size; int ren = zip && strcmp (qfs_foundfile.realname, name); - SV_Printf ("http redirect: %s/%s\n", sv_http_url_base->string, + SV_Printf ("http redirect: %s/%s\n", sv_http_url_base, qfs_foundfile.realname); size = ren ? strlen (qfs_foundfile.realname) * 2 : strlen (name); - size += strlen (sv_http_url_base->string) + 7; + size += strlen (sv_http_url_base) + 7; MSG_ReliableWrite_Begin (&host_client->backbuf, svc_download, size); MSG_ReliableWrite_Short (&host_client->backbuf, DL_HTTP); MSG_ReliableWrite_Byte (&host_client->backbuf, 0); MSG_ReliableWrite_String (&host_client->backbuf, - va ("%s/%s", sv_http_url_base->string, + va (0, "%s/%s", sv_http_url_base, ren ? qfs_foundfile.realname : name)); MSG_ReliableWrite_String (&host_client->backbuf, ren ? qfs_foundfile.realname : ""); @@ -814,11 +987,11 @@ SV_BeginDownload_f (void *unused) //============================================================================= static void -SV_Say (qboolean team) +SV_Say (bool team) { char *i, *p; dstring_t *text; - const char *t1 = 0, *t2, *type, *fmt; + const char *t1 = 0, *t2, *type; client_t *client; int tmp, j, cls = 0; sizebuf_t *dbuf; @@ -860,7 +1033,7 @@ SV_Say (qboolean team) host_client->whensaid[host_client->whensaidhead] = realtime; } - p = Hunk_TempAlloc (strlen (Cmd_Args (1)) + 1); + p = Hunk_TempAlloc (0, strlen (Cmd_Args (1)) + 1); strcpy (p, Cmd_Args (1)); if (*p == '"') { @@ -868,10 +1041,10 @@ SV_Say (qboolean team) p[strlen (p) - 1] = 0; } - if (!sv_allowfake->int_val || (!team && sv_allowfake->int_val == 2)) { + if (!sv_allowfake || (!team && sv_allowfake == 2)) { for (i = p; *i; i++) { if (*i == 13) { // ^M - if (sv_kickfake->int_val) { + if (sv_kickfake) { SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked for " "attempting to fake messages\n", host_client->name); @@ -900,20 +1073,19 @@ SV_Say (qboolean team) } text = dstring_new (); - if (host_client->spectator && (!sv_spectalk->int_val || team)) { - fmt = "[SPEC] %s: "; + if (host_client->spectator && (!sv_spectalk || team)) { type = "2"; + dsprintf (text, "[SPEC] %s: ", host_client->name); } else if (team) { - fmt = "(%s): "; type = "1"; + dsprintf (text, "(%s): ", host_client->name); } else { - fmt = "%s: "; type = "0"; + dsprintf (text, "%s: ", host_client->name); } - dsprintf (text, fmt, host_client->name); if (sv_chat_e->func) - GIB_Event_Callback (sv_chat_e, 2, va ("%i", host_client->userid), p, + GIB_Event_Callback (sv_chat_e, 2, va (0, "%i", host_client->userid), p, type); dstring_appendstr (text, p); @@ -924,7 +1096,7 @@ SV_Say (qboolean team) for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { if (client->state < cs_connected) // Clients connecting can hear. //FIXME record to mvd? continue; - if (host_client->spectator && !sv_spectalk->int_val) + if (host_client->spectator && !sv_spectalk) if (!client->spectator) continue; @@ -949,7 +1121,7 @@ SV_Say (qboolean team) } // non-team messages should be seen allways, even if not tracking any // player - if (!team && ((host_client->spectator && sv_spectalk->value) + if (!team && ((host_client->spectator && sv_spectalk) || !host_client->spectator)) { dbuf = SVR_WriteBegin (dem_all, 0, strlen (text->str) + 3); } else { @@ -1054,7 +1226,7 @@ SV_Pause_f (void *unused) lastpausetime = currenttime; - if (!pausable->int_val) { + if (!pausable) { SV_ClientPrintf (1, host_client, PRINT_HIGH, "Pause not allowed.\n"); return; } @@ -1148,8 +1320,8 @@ SV_Rate_f (void *unused) } rate = atoi (Cmd_Argv (1)); - if (sv_maxrate->int_val) { - rate = bound (500, rate, sv_maxrate->int_val); + if (sv_maxrate) { + rate = bound (500, rate, sv_maxrate); } else { rate = max (500, rate); } @@ -1188,7 +1360,7 @@ SV_SetUserinfo (client_t *client, const char *key, const char *value) if (sv_setinfo_e->func || sv_funcs.UserInfoChanged) oldvalue = strdup (Info_ValueForKey (client->userinfo, key)); if (!Info_SetValueForKey (client->userinfo, key, value, - !sv_highchars->int_val)) { + !sv_highchars)) { // key hasn't changed if (oldvalue) free (oldvalue); @@ -1200,7 +1372,7 @@ SV_SetUserinfo (client_t *client, const char *key, const char *value) // trigger a GIB event if (sv_setinfo_e->func) - GIB_Event_Callback (sv_setinfo_e, 4, va("%d", client->userid), + GIB_Event_Callback (sv_setinfo_e, 4, va (0, "%d", client->userid), key, oldvalue, value); if (sv_funcs.UserInfoChanged) { @@ -1211,6 +1383,7 @@ SV_SetUserinfo (client_t *client, const char *key, const char *value) P_STRING (&sv_pr_state, 0) = PR_SetTempString (&sv_pr_state, key); P_STRING (&sv_pr_state, 1) = PR_SetTempString (&sv_pr_state, oldvalue); P_STRING (&sv_pr_state, 2) = PR_SetTempString (&sv_pr_state, value); + sv_pr_state.pr_argc = 3; PR_ExecuteProgram (&sv_pr_state, sv_funcs.UserInfoChanged); PR_PopFrame (&sv_pr_state); send_changes = !R_FLOAT (&sv_pr_state); @@ -1261,6 +1434,7 @@ SV_SetInfo_f (void *unused) PR_RESET_PARAMS (&sv_pr_state); P_STRING (&sv_pr_state, 0) = PR_SetTempString (&sv_pr_state, key); P_STRING (&sv_pr_state, 1) = PR_SetTempString (&sv_pr_state, value); + sv_pr_state.pr_argc = 2; PR_ExecuteProgram (&sv_pr_state, sv_funcs.UserInfoCallback); PR_PopFrame (&sv_pr_state); if (R_FLOAT (&sv_pr_state)) @@ -1332,7 +1506,7 @@ static void call_qc_hook (void *qc_hook) { *sv_globals.self = EDICT_TO_PROG (&sv_pr_state, sv_player); - PR_ExecuteProgram (&sv_pr_state, (func_t) (intptr_t) qc_hook); + PR_ExecuteProgram (&sv_pr_state, (pr_func_t) (intptr_t) qc_hook); } static const char * @@ -1422,7 +1596,7 @@ SV_RemoveUserCommand (void *cmd) } static void -PF_AddUserCommand (progs_t *pr) +PF_SV_AddUserCommand (progs_t *pr, void *data) { const char *name = P_GSTRING (pr, 0); ucmd_t *cmd; @@ -1494,10 +1668,10 @@ SV_CalcRoll (vec3_t angles, vec3_t velocity) sign = side < 0 ? -1 : 1; side = fabs (side); - value = cl_rollangle->value; + value = cl_rollangle; - if (side < cl_rollspeed->value) - side = side * value / cl_rollspeed->value; + if (side < cl_rollspeed) + side = side * value / cl_rollspeed; else side = value; @@ -1513,7 +1687,7 @@ static void AddLinksToPmove (areanode_t *node) { edict_t *check; - int pl, i; + pr_uint_t pl, i; link_t *l, *next; physent_t *pe; @@ -1548,8 +1722,8 @@ AddLinksToPmove (areanode_t *node) pe->info = NUM_FOR_EDICT (&sv_pr_state, check); if (sv_fields.rotated_bbox != -1 - && SVinteger (check, rotated_bbox)) { - int h = SVinteger (check, rotated_bbox) - 1; + && SVint (check, rotated_bbox)) { + int h = SVint (check, rotated_bbox) - 1; pe->hull = pf_hull_list[h]->hulls[1]; } else { @@ -1599,12 +1773,12 @@ adjust_usecs (usercmd_t *ucmd) passed = (int) ((realtime - host_client->last_check) * 1000.0); host_client->msecs += passed - ucmd->msec; if (host_client->msecs >= 0) { - host_client->msecs -= sv_timecheck_decay->int_val; + host_client->msecs -= sv_timecheck_decay; } else { - host_client->msecs += sv_timecheck_decay->int_val; + host_client->msecs += sv_timecheck_decay; } - if (abs (host_client->msecs) > sv_timecheck_fuzz->int_val) { - int fuzz = sv_timecheck_fuzz->int_val; + if (abs (host_client->msecs) > sv_timecheck_fuzz) { + int fuzz = sv_timecheck_fuzz; host_client->msecs = bound (-fuzz, host_client->msecs, fuzz); ucmd->msec = passed; } @@ -1623,18 +1797,18 @@ check_usecs (usercmd_t *ucmd) if (host_client->last_check == -1.0) return; tmp_time = realtime - host_client->last_check; - if (tmp_time < sv_timekick_interval->value) + if (tmp_time < sv_timekick_interval) return; host_client->last_check = realtime; - tmp_time1 = tmp_time * (1000 + sv_timekick_fuzz->value); + tmp_time1 = tmp_time * (1000 + sv_timekick_fuzz); if (host_client->msecs >= tmp_time1) { host_client->msec_cheating++; SV_BroadcastPrintf (PRINT_HIGH, "%s thinks there are %d ms " "in %d seconds (Strike %d/%d)\n", host_client->name, host_client->msecs, (int) tmp_time, host_client->msec_cheating, - sv_timekick->int_val); - if (host_client->msec_cheating >= sv_timekick->int_val) { + sv_timekick); + if (host_client->msec_cheating >= sv_timekick) { SV_BroadcastPrintf (PRINT_HIGH, "Strike %d for %s!!\n", host_client->msec_cheating, host_client->name); SV_BroadcastPrintf (PRINT_HIGH, "Please see " @@ -1649,13 +1823,13 @@ check_usecs (usercmd_t *ucmd) } void -SV_RunCmd (usercmd_t *ucmd, qboolean inside) +SV_RunCmd (usercmd_t *ucmd, bool inside) { int oldmsec, i, n; edict_t *ent; if (!inside) { - if (sv_timecheck_mode->int_val) { + if (sv_timecheck_mode) { adjust_usecs (ucmd); } else { check_usecs (ucmd); @@ -1850,7 +2024,7 @@ SV_ExecuteClientMessage (client_t *cl) client_frame_t *frame; int checksumIndex, seq_hash, c; usercmd_t oldest, oldcmd, newcmd; - qboolean move_issued = false; // allow only one move command + bool move_issued = false; // allow only one move command vec3_t o; // make sure the reply sequence number matches the incoming @@ -1870,7 +2044,7 @@ SV_ExecuteClientMessage (client_t *cl) frame->ping_time = realtime - frame->senttime; cl->laggedents_count = 0; - if (sv_antilag->int_val) { + if (sv_antilag) { int i; for (i = 0; i < MAX_CLIENTS; i++) { @@ -1881,7 +2055,7 @@ SV_ExecuteClientMessage (client_t *cl) } } cl->laggedents_count = MAX_CLIENTS; - cl->laggedents_frac = sv_antilag_frac->value; + cl->laggedents_frac = sv_antilag_frac; } // save time for ping calculations @@ -1948,7 +2122,7 @@ SV_ExecuteClientMessage (client_t *cl) checksumIndex - 1, seq_hash); if (calculatedChecksum != checksum) { - Sys_MaskPrintf (SYS_DEV, + Sys_MaskPrintf (SYS_dev, "Failed command checksum for %s(%d) " "(%d != %d)\n", cl->name, cl->netchan.incoming_sequence, @@ -1999,61 +2173,71 @@ SV_ExecuteClientMessage (client_t *cl) } } +#define bi(x,np,params...) {#x, PF_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) +#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } static builtin_t builtins[] = { - {"SV_AddUserCommand", PF_AddUserCommand, -1}, + bi(SV_AddUserCommand, 3, p(string), p(func), p(int)), {0} }; +static void +SV_MaxRate_f (void *data, const cvar_t *cvar) +{ + client_t *cl; + int maxrate = sv_maxrate; + int i, rate = 2500; + const char *val; + + Cvar_Info (data, cvar); + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { + if (!cl->userinfo) + continue; + val = Info_ValueForKey (cl->userinfo, "rate"); + if (strlen (val)) { + rate = atoi (val); + + if (maxrate) { + rate = bound (500, rate, maxrate); + } else { + rate = max (500, rate); + } + cl->netchan.rate = 1.0 / rate; + } + SV_ClientPrintf (1, cl, PRINT_HIGH, "Net rate set to %i\n", rate); + } +} + void SV_UserInit (void) { - ucmd_table = Hash_NewTable (251, ucmds_getkey, ucmds_free, 0); + ucmd_table = Hash_NewTable (251, ucmds_getkey, ucmds_free, 0, 0); Hash_SetHashCompare (ucmd_table, ucmd_get_hash, ucmd_compare); - PR_RegisterBuiltins (&sv_pr_state, builtins); - cl_rollspeed = Cvar_Get ("cl_rollspeed", "200", CVAR_NONE, NULL, - "How quickly a player straightens out after " - "strafing"); - cl_rollangle = Cvar_Get ("cl_rollangle", "2", CVAR_NONE, NULL, "How much " - "a player's screen tilts when strafing"); + PR_RegisterBuiltins (&sv_pr_state, builtins, 0); + Cvar_Register (&cl_rollspeed_cvar, 0, 0); + Cvar_Register (&cl_rollangle_cvar, 0, 0); - sv_antilag = Cvar_Get ("sv_antilag", "1", CVAR_SERVERINFO, Cvar_Info, - "Attempt to backdate impacts to compensate for " - "lag. 0=completely off. 1=mod-controlled. " - "2=forced, which might break certain uses of " - "traceline."); - sv_antilag_frac = Cvar_Get ("sv_antilag_frac", "1", CVAR_SERVERINFO, - Cvar_Info, - "FIXME something to do with sv_antilag"); + Cvar_Register (&sv_antilag_cvar, Cvar_Info, &sv_antilag); + Cvar_Register (&sv_antilag_frac_cvar, Cvar_Info, &sv_antilag_frac); - sv_allowfake = Cvar_Get ("sv_allowfake", "2", CVAR_NONE, NULL, - "Allow 'fake' messages (FuhQuake $\\). 1 = " - "always, 2 = only say_team"); - sv_spectalk = Cvar_Get ("sv_spectalk", "1", CVAR_NONE, NULL, "Toggles " - "the ability of spectators to talk to players"); - sv_mapcheck = Cvar_Get ("sv_mapcheck", "1", CVAR_NONE, NULL, "Toggle the " - "use of map checksumming to check for players who " - "edit maps to cheat"); - sv_timecheck_mode = Cvar_Get ("sv_timecheck_mode", "0", CVAR_NONE, NULL, - "select between timekick (0, default) and " - "timecheck (1)"); - sv_timekick = Cvar_Get ("sv_timekick", "3", CVAR_SERVERINFO, Cvar_Info, - "Time cheat protection"); - sv_timekick_fuzz = Cvar_Get ("sv_timekick_fuzz", "30", CVAR_NONE, NULL, - "Time cheat \"fuzz factor\" in milliseconds"); - sv_timekick_interval = Cvar_Get ("sv_timekick_interval", "30", CVAR_NONE, - NULL, "Time cheat check interval in " - "seconds"); - sv_timecheck_fuzz = Cvar_Get ("sv_timecheck_fuzz", "250", CVAR_NONE, NULL, - "Milliseconds of tolerance before time " - "cheat throttling kicks in."); - sv_timecheck_decay = Cvar_Get ("sv_timecheck_decay", "2", CVAR_NONE, - NULL, "Rate at which time inaccuracies are " - "\"forgiven\"."); - sv_kickfake = Cvar_Get ("sv_kickfake", "0", CVAR_NONE, NULL, - "Kick users sending to send fake talk messages"); - sv_http_url_base = Cvar_Get ("sv_http_url_base", "", CVAR_NONE, NULL, - "set to base url for http redirects of " - "downloaded files"); + Cvar_Register (&sv_allowfake_cvar, 0, 0); + Cvar_Register (&sv_spectalk_cvar, 0, 0); + Cvar_Register (&sv_mapcheck_cvar, 0, 0); + Cvar_Register (&sv_timecheck_mode_cvar, 0, 0); + Cvar_Register (&sv_timekick_cvar, Cvar_Info, &sv_timekick); + Cvar_Register (&sv_timekick_fuzz_cvar, 0, 0); + Cvar_Register (&sv_timekick_interval_cvar, 0, 0); + Cvar_Register (&sv_timecheck_fuzz_cvar, 0, 0); + Cvar_Register (&sv_timecheck_decay_cvar, 0, 0); + Cvar_Register (&sv_kickfake_cvar, 0, 0); + Cvar_Register (&sv_http_url_base_cvar, 0, 0); + Cvar_Register (&sv_maxrate_cvar, SV_MaxRate_f, 0); + Cvar_Register (&sv_maxspeed_cvar, Cvar_Info, &sv_maxspeed); + Cvar_Register (&sv_spectatormaxspeed_cvar, 0, 0); + Cvar_Register (&sv_accelerate_cvar, 0, 0); + Cvar_Register (&sv_airaccelerate_cvar, 0, 0); + Cvar_Register (&sv_wateraccelerate_cvar, 0, 0); + Cvar_Register (&sv_waterfriction_cvar, 0, 0); } static void diff --git a/qw/source/teamplay.c b/qw/source/teamplay.c index 513fdd4f7..386f30ddb 100644 --- a/qw/source/teamplay.c +++ b/qw/source/teamplay.c @@ -42,27 +42,75 @@ #include "QF/cbuf.h" #include "QF/cmd.h" #include "QF/cvar.h" +#include "QF/dstring.h" #include "QF/gib.h" -#include "QF/locs.h" #include "QF/model.h" #include "QF/va.h" #include "QF/skin.h" #include "QF/sys.h" #include "QF/teamplay.h" -#include "qw/bothdefs.h" -#include "cl_input.h" -#include "client.h" +#include "QF/scene/scene.h" + #include "compat.h" -static qboolean died = false, recorded_location = false; -static vec3_t death_location, last_recorded_location; +#include "client/locs.h" +#include "client/world.h" -cvar_t *cl_deadbodyfilter; -cvar_t *cl_gibfilter; -cvar_t *cl_parsesay; -cvar_t *cl_nofake; -cvar_t *cl_freply; +#include "qw/bothdefs.h" +#include "qw/include/cl_input.h" +#include "qw/include/client.h" + +static bool died = false, recorded_location = false; +static vec4f_t death_location, last_recorded_location; + +int cl_deadbodyfilter; +static cvar_t cl_deadbodyfilter_cvar = { + .name = "cl_deadbodyfilter", + .description = + "Hide dead player models", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_deadbodyfilter }, +}; +int cl_gibfilter; +static cvar_t cl_gibfilter_cvar = { + .name = "cl_gibfilter", + .description = + "Hide gibs", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_gibfilter }, +}; +int cl_parsesay; +static cvar_t cl_parsesay_cvar = { + .name = "cl_parsesay", + .description = + "Use .loc files to find your present location when you put %l in " + "messages", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_parsesay }, +}; +int cl_nofake; +static cvar_t cl_nofake_cvar = { + .name = "cl_nofake", + .description = + "Unhide fake messages", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_int, .value = &cl_nofake }, +}; +float cl_freply; +static cvar_t cl_freply_cvar = { + .name = "cl_freply", + .description = + "Delay between replies to f_*. 0 disables. Minimum suggested setting " + "is 20", + .default_value = "0", + .flags = CVAR_NONE, + .value = { .type = &cexpr_float, .value = &cl_freply }, +}; void @@ -117,17 +165,16 @@ Team_BestWeaponImpulse (void) in_impulse = best; } - -const char * -Team_ParseSay (const char *s) +//FIXME slow use of dstring +const char * +Team_ParseSay (dstring_t *buf, const char *s) { - char chr, t2[128], t3[128]; + char chr, t2[128], t3[2]; const char *t1; - static char buf[1024]; size_t i, bracket; static location_t *location = NULL; - if (!cl_parsesay->int_val || (cl.fpd & FPD_NO_MACROS)) + if (!cl_parsesay || (cl.fpd & FPD_NO_MACROS)) return s; i = 0; @@ -156,8 +203,7 @@ Team_ParseSay (const char *s) break; case 'S': bracket = 0; - t1 = skin->string; - t1 = "FIXME"; + t1 = skin; break; case 'd': bracket = 0; @@ -165,7 +211,7 @@ Team_ParseSay (const char *s) location = locs_find (death_location); if (location) { recorded_location = true; - VectorCopy (death_location, last_recorded_location); + last_recorded_location = death_location; t1 = location->name; break; } @@ -184,10 +230,10 @@ Team_ParseSay (const char *s) case 'l': location: bracket = 0; - location = locs_find (cl.simorg); + location = locs_find (cl.viewstate.player_origin); if (location) { recorded_location = true; - VectorCopy (cl.simorg, last_recorded_location); + last_recorded_location = cl.viewstate.player_origin; t1 = location->name; } else snprintf (t2, sizeof (t2), "Unknown!"); @@ -198,38 +244,39 @@ Team_ParseSay (const char *s) bracket = 0; if (cl.stats[STAT_ITEMS] & IT_ARMOR3) - t3[0] = 'R' | 0x80; + t3[0] = (char) ('R' | 0x80); else if (cl.stats[STAT_ITEMS] & IT_ARMOR2) - t3[0] = 'Y' | 0x80; + t3[0] = (char) ('Y' | 0x80); else if (cl.stats[STAT_ITEMS] & IT_ARMOR1) - t3[0] = 'G' | 0x80; + t3[0] = (char) ('G' | 0x80); else { - t2[0] = 'N' | 0x80; - t2[1] = 'O' | 0x80; - t2[2] = 'N' | 0x80; - t2[3] = 'E' | 0x80; - t2[4] = '!' | 0x80; + t2[0] = (char) ('N' | 0x80); + t2[1] = (char) ('O' | 0x80); + t2[2] = (char) ('N' | 0x80); + t2[3] = (char) ('E' | 0x80); + t2[4] = (char) ('!' | 0x80); } snprintf (t2, sizeof (t2), "%sa:%i", t3, cl.stats[STAT_ARMOR]); - } else + } else { snprintf (t2, sizeof (t2), "%i", cl.stats[STAT_ARMOR]); + } break; case 'A': bracket = 0; if (cl.stats[STAT_ITEMS] & IT_ARMOR3) - t2[0] = 'R' | 0x80; + t2[0] = (char) ('R' | 0x80); else if (cl.stats[STAT_ITEMS] & IT_ARMOR2) - t2[0] = 'Y' | 0x80; + t2[0] = (char) ('Y' | 0x80); else if (cl.stats[STAT_ITEMS] & IT_ARMOR1) - t2[0] = 'G' | 0x80; + t2[0] = (char) ('G' | 0x80); else { - t2[0] = 'N' | 0x80; - t2[1] = 'O' | 0x80; - t2[2] = 'N' | 0x80; - t2[3] = 'E' | 0x80; - t2[4] = '!' | 0x80; + t2[0] = (char) ('N' | 0x80); + t2[1] = (char) ('O' | 0x80); + t2[2] = (char) ('N' | 0x80); + t2[3] = (char) ('E' | 0x80); + t2[4] = (char) ('!' | 0x80); } break; case 'h': @@ -254,36 +301,31 @@ Team_ParseSay (const char *s) t1 = t2; } - if (bracket) - buf[i++] = 0x90; // '[' - - if (t1) { - int len; - - len = strlen (t1); - if (i + len >= sizeof (buf)) - continue; // No more space in buffer, icky. - strncpy (buf + i, t1, len); - i += len; + if (bracket) { + dstring_appendstr (buf, "\x90"); // '[' } - if (bracket) - buf[i++] = 0x91; // ']' + if (t1) { + dstring_appendstr (buf, t1); + } + + if (bracket) { + dstring_appendstr (buf, "\x91"); // ']' + } continue; } - buf[i++] = *s++; + dstring_appendsubstr (buf, s++, 1); } - buf[i] = 0; - return buf; + return buf->str; } void Team_Dead (void) { died = true; - VectorCopy (cl.simorg, death_location); + death_location = cl.viewstate.player_origin; } void @@ -293,8 +335,8 @@ Team_NewMap (void) died = false; recorded_location = false; - mapname = strdup (cl.worldmodel->name); - t2 = malloc (sizeof (cl.worldmodel->name)); + mapname = strdup (cl_world.scene->worldmodel->path); + t2 = malloc (sizeof (cl_world.scene->worldmodel->path)); if (!mapname || !t2) Sys_Error ("Can't duplicate mapname!"); map_to_loc (mapname,t2); @@ -311,18 +353,11 @@ Team_NewMap (void) void Team_Init_Cvars (void) { - cl_deadbodyfilter = Cvar_Get ("cl_deadbodyfilter", "0", CVAR_NONE, NULL, - "Hide dead player models"); - cl_gibfilter = Cvar_Get ("cl_gibfilter", "0", CVAR_NONE, NULL, - "Hide gibs"); - cl_parsesay = Cvar_Get ("cl_parsesay", "0", CVAR_NONE, NULL, - "Use .loc files to find your present location " - "when you put %l in messages"); - cl_nofake = Cvar_Get ("cl_nofake", "0", CVAR_NONE, NULL, - "Unhide fake messages"); - cl_freply = Cvar_Get ("cl_freply", "0", CVAR_NONE, NULL, - "Delay between replies to f_*. 0 disables. Minimum " - "suggested setting is 20"); + Cvar_Register (&cl_deadbodyfilter_cvar, 0, 0); + Cvar_Register (&cl_gibfilter_cvar, 0, 0); + Cvar_Register (&cl_parsesay_cvar, 0, 0); + Cvar_Register (&cl_nofake_cvar, 0, 0); + Cvar_Register (&cl_freply_cvar, 0, 0); } /* @@ -344,16 +379,16 @@ locs_loc (void) "parameter\n"); return; } - if (!cl.worldmodel) { + if (!cl_world.scene->worldmodel) { Sys_Printf ("No map loaded. Unable to work with location markers.\n"); return; } if (Cmd_Argc () >= 3) desc = Cmd_Args (2); - mapname = malloc (sizeof (cl.worldmodel->name)); + mapname = malloc (sizeof (cl_world.scene->worldmodel->path)); if (!mapname) Sys_Error ("Can't duplicate mapname!"); - map_to_loc (cl.worldmodel->name, mapname); + map_to_loc (cl_world.scene->worldmodel->path, mapname); snprintf (locfile, sizeof (locfile), "%s/%s", qfs_gamedir->dir.def, mapname); free (mapname); @@ -374,7 +409,7 @@ locs_loc (void) if (strcasecmp (Cmd_Argv (1), "add") == 0) { if (Cmd_Argc () >= 3) - locs_mark (cl.simorg, desc); + locs_mark (cl.viewstate.player_origin, desc); else Sys_Printf ("loc add :marks the current location " "with the description and records the information " @@ -383,7 +418,7 @@ locs_loc (void) if (strcasecmp (Cmd_Argv (1), "rename") == 0) { if (Cmd_Argc () >= 3) - locs_edit (cl.simorg, desc); + locs_edit (cl.viewstate.player_origin, desc); else Sys_Printf ("loc rename :changes the description of " "the nearest location marker\n"); @@ -391,14 +426,14 @@ locs_loc (void) if (strcasecmp (Cmd_Argv (1),"delete") == 0) { if (Cmd_Argc () == 2) - locs_del (cl.simorg); + locs_del (cl.viewstate.player_origin); else Sys_Printf ("loc delete :removes nearest location marker\n"); } if (strcasecmp (Cmd_Argv (1),"move") == 0) { if (Cmd_Argc () == 2) - locs_edit (cl.simorg, NULL); + locs_edit (cl.viewstate.player_origin, NULL); else Sys_Printf ("loc move :moves the nearest location marker to your " "current location\n"); @@ -413,7 +448,7 @@ Locs_Loc_Get (void) if (GIB_Argc () != 1) GIB_USAGE (""); else { - location = locs_find (cl.simorg); + location = locs_find (cl.viewstate.player_origin); GIB_Return (location ? location->name : "unknown"); } } @@ -429,7 +464,7 @@ Locs_Init (void) static const char * Team_F_Version (char *args) { - return va ("say %s", PACKAGE_STRING); + return va (0, "say %s", PACKAGE_STRING); } static const char * @@ -438,7 +473,7 @@ Team_F_Skins (char *args) int totalfb, l; float allfb = 0.0; - allfb = min (cl.fbskins, cl_fb_players->value); + allfb = min (cl.fbskins, cl_fb_players); if (allfb >= 1.0) { return "say Player models fullbright"; @@ -451,7 +486,7 @@ Team_F_Skins (char *args) if (l == 0) { //XXXtotalfb = Skin_FbPercent (0); totalfb = 0; - return va ("say Player models have %f%% brightness\n" + return va (0, "say Player models have %f%% brightness\n" "say Average percent fullbright for all loaded skins is " "%d.%d%%", allfb * 100, totalfb / 10, totalfb % 10); } @@ -460,8 +495,8 @@ Team_F_Skins (char *args) totalfb = 0; if (totalfb >= 0) - return va ("say \"Skin %s is %d.%d%% fullbright\"", args, totalfb / 10, - totalfb % 10); + return va (0, "say \"Skin %s is %d.%d%% fullbright\"", + args, totalfb / 10, totalfb % 10); else return ("say \"Skin not currently loaded.\""); } @@ -477,10 +512,9 @@ Team_ParseChat (const char *string) char *s; unsigned int i; - if (!cl_freply->value) + if (!cl_freply) return; - s = strchr (string, ':'); if (!(s = strchr (string, ':'))) return; s++; @@ -489,7 +523,7 @@ Team_ParseChat (const char *string) for (i = 0; i < sizeof (f_replies) / sizeof (f_replies[0]); i++) { if (!strncmp (f_replies[i].name, s, strlen (f_replies[i].name)) - && realtime - f_replies[i].lasttime >= cl_freply->value) { + && realtime - f_replies[i].lasttime >= cl_freply) { while (*s && !isspace ((byte) *s)) s++; Cbuf_AddText (cl_cbuf, f_replies[i].func (s)); @@ -505,6 +539,6 @@ Team_ResetTimers (void) unsigned int i; for (i = 0; i < sizeof (f_replies) / sizeof (f_replies[0]); i++) - f_replies[i].lasttime = realtime - cl_freply->value; + f_replies[i].lasttime = realtime - cl_freply; return; } diff --git a/qw/source/world.c b/qw/source/world.c index ac5dd641b..1d032d5ba 100644 --- a/qw/source/world.c +++ b/qw/source/world.c @@ -38,13 +38,13 @@ #include #include "QF/clip_hull.h" -#include "QF/console.h" #include "QF/crc.h" #include "QF/sys.h" #include "compat.h" -#include "server.h" -#include "sv_progs.h" + +#include "qw/include/server.h" +#include "qw/include/sv_progs.h" #include "world.h" #define always_inline inline __attribute__((__always_inline__)) @@ -134,7 +134,7 @@ typedef struct { /* HULL BOXES */ static hull_t box_hull; -static mclipnode_t box_clipnodes[6]; +static dclipnode_t box_clipnodes[6]; static plane_t box_planes[6]; @@ -145,7 +145,7 @@ static plane_t box_planes[6]; can just be stored out and get a proper hull_t structure. */ void -SV_InitHull (hull_t *hull, mclipnode_t *clipnodes, plane_t *planes) +SV_InitHull (hull_t *hull, dclipnode_t *clipnodes, plane_t *planes) { int side, i; @@ -214,7 +214,7 @@ SV_HullForEntity (edict_t *ent, const vec3_t mins, const vec3_t maxs, vec3_t hullmins, hullmaxs, size; if ((sv_fields.rotated_bbox != -1 - && SVinteger (ent, rotated_bbox)) + && SVint (ent, rotated_bbox)) || SVfloat (ent, solid) == SOLID_BSP) { VectorSubtract (maxs, mins, size); if (size[0] < 3) @@ -225,8 +225,8 @@ SV_HullForEntity (edict_t *ent, const vec3_t mins, const vec3_t maxs, hull_index = 2; } if (sv_fields.rotated_bbox != -1 - && SVinteger (ent, rotated_bbox)) { - int h = SVinteger (ent, rotated_bbox) - 1; + && SVint (ent, rotated_bbox)) { + int h = SVint (ent, rotated_bbox) - 1; hull_list = pf_hull_list[h]->hulls; } if (SVfloat (ent, solid) == SOLID_BSP) { // explicit hulls in the BSP model @@ -244,7 +244,7 @@ SV_HullForEntity (edict_t *ent, const vec3_t mins, const vec3_t maxs, PR_GetString (&sv_pr_state, SVstring (ent, classname))); - hull_list = model->hull_list; + hull_list = model->brush.hull_list; } if (hull_list) { // decide which clipping hull to use, based on the size @@ -409,29 +409,28 @@ SV_TouchLinks (edict_t *ent, areanode_t *node) } static void -SV_FindTouchedLeafs (edict_t *ent, mnode_t *node) +SV_FindTouchedLeafs (edict_t *ent, int node_id) { int sides; - mleaf_t *leaf; plane_t *splitplane; edict_leaf_t *edict_leaf; - if (node->contents == CONTENTS_SOLID) - return; - // add an efrag if the node is a leaf - if (node->contents < 0) { - leaf = (mleaf_t *) node; + if (node_id < 0) { + mleaf_t *leaf = sv.worldmodel->brush.leafs + ~node_id; + if (leaf->contents == CONTENTS_SOLID) + return; edict_leaf = alloc_edict_leaf (); - edict_leaf->leafnum = leaf - sv.worldmodel->leafs - 1; + edict_leaf->leafnum = ~node_id - 1; edict_leaf->next = SVdata (ent)->leafs; SVdata (ent)->leafs = edict_leaf; return; } + mnode_t *node = sv.worldmodel->brush.nodes + node_id; // NODE_MIXED - splitplane = node->plane; + splitplane = (plane_t *) &node->plane; sides = BOX_ON_PLANE_SIDE (SVvector (ent, absmin), SVvector (ent, absmax), splitplane); @@ -444,7 +443,7 @@ SV_FindTouchedLeafs (edict_t *ent, mnode_t *node) } void -SV_LinkEdict (edict_t *ent, qboolean touch_triggers) +SV_LinkEdict (edict_t *ent, bool touch_triggers) { areanode_t *node; @@ -496,7 +495,7 @@ SV_LinkEdict (edict_t *ent, qboolean touch_triggers) // link to PVS leafs free_edict_leafs (&SVdata (ent)->leafs); if (SVfloat (ent, modelindex)) - SV_FindTouchedLeafs (ent, sv.worldmodel->nodes); + SV_FindTouchedLeafs (ent, 0); if (SVfloat (ent, solid) == SOLID_NOT) return; @@ -531,7 +530,7 @@ int SV_HullPointContents (hull_t *hull, int num, const vec3_t p) { float d; - mclipnode_t *node; + dclipnode_t *node; plane_t *plane; while (num >= 0) { @@ -559,7 +558,7 @@ SV_PointContents (const vec3_t p) { int cont; - cont = SV_HullPointContents (&sv.worldmodel->hulls[0], 0, p); + cont = SV_HullPointContents (&sv.worldmodel->brush.hulls[0], 0, p); if (cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN) cont = CONTENTS_WATER; return cont; @@ -568,7 +567,7 @@ SV_PointContents (const vec3_t p) int SV_TruePointContents (const vec3_t p) { - return SV_HullPointContents (&sv.worldmodel->hulls[0], 0, p); + return SV_HullPointContents (&sv.worldmodel->brush.hulls[0], 0, p); } /* @@ -677,7 +676,7 @@ SV_ClipMoveToEntity (edict_t *touched, const vec3_t start, return trace; } -static always_inline int +static always_inline __attribute__((pure)) int ctl_pretest_everything (edict_t *touch, moveclip_t *clip) { if (touch->free) @@ -705,7 +704,7 @@ ctl_pretest_triggers (edict_t *touch, moveclip_t *clip) return 1; } -static always_inline int +static always_inline __attribute__((pure)) int ctl_pretest_other (edict_t *touch, moveclip_t *clip) { if (SVfloat (touch, solid) == SOLID_NOT) @@ -817,11 +816,10 @@ SV_ClipToLinks (areanode_t *node, moveclip_t *clip) edict_t *touch; link_t *l, *next; trace_t trace; - int i; if (clip->type & MOVE_EVERYTHING) { touch = NEXT_EDICT (&sv_pr_state, sv.edicts); - for (i = 1; i < sv.num_edicts; i++, + for (unsigned i = 1; i < sv.num_edicts; i++, touch = NEXT_EDICT (&sv_pr_state, touch)) { if (clip->trace.allsolid) return; @@ -984,13 +982,12 @@ SV_Move (const vec3_t start, const vec3_t mins, const vec3_t maxs, edict_t * SV_TestPlayerPosition (edict_t *ent, const vec3_t origin) { - int e; edict_t *check; hull_t *hull; vec3_t boxmins, boxmaxs, offset; // check world first - hull = &sv.worldmodel->hulls[1]; + hull = &sv.worldmodel->brush.hulls[1]; if (SV_HullPointContents (hull, hull->firstclipnode, origin) != CONTENTS_EMPTY) return sv.edicts; @@ -999,8 +996,8 @@ SV_TestPlayerPosition (edict_t *ent, const vec3_t origin) VectorAdd (origin, SVvector (ent, maxs), boxmaxs); check = NEXT_EDICT (&sv_pr_state, sv.edicts); - for (e = 1; e < sv.num_edicts; e++, check = NEXT_EDICT (&sv_pr_state, - check)) { + for (unsigned e = 1; e < sv.num_edicts; + e++, check = NEXT_EDICT (&sv_pr_state, check)) { if (check->free) continue; if (check == ent) diff --git a/ruamoko/Makefile.am b/ruamoko/Makefile.am deleted file mode 100644 index 779fc1439..000000000 --- a/ruamoko/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -SUBDIRS= include lib game gui cl_menu scheme - -doc: Doxyfile - doxygen - -clean-local: - -rm -fr doxygen diff --git a/ruamoko/Makemodule.am b/ruamoko/Makemodule.am new file mode 100644 index 000000000..c7f813b17 --- /dev/null +++ b/ruamoko/Makemodule.am @@ -0,0 +1,11 @@ +ruamoko_libdir = $(datarootdir)/qfcc/lib +ruamoko_lib_LIBRARIES = + +include ruamoko/include/Makemodule.am +include ruamoko/lib/Makemodule.am +include ruamoko/game/Makemodule.am +include ruamoko/gui/Makemodule.am +include ruamoko/cl_menu/Makemodule.am +include ruamoko/gatest/Makemodule.am +include ruamoko/scheme/Makemodule.am +include ruamoko/qwaq/Makemodule.am diff --git a/ruamoko/cl_menu/CrosshairView.r b/ruamoko/cl_menu/CrosshairView.r index 81152efee..0ca4e9532 100644 --- a/ruamoko/cl_menu/CrosshairView.r +++ b/ruamoko/cl_menu/CrosshairView.r @@ -32,7 +32,7 @@ { switch (key) { case QFK_RETURN: - case QFM_BUTTON1: + //case QFM_BUTTON1: [self next]; return 1; default: diff --git a/ruamoko/cl_menu/CvarObject.r b/ruamoko/cl_menu/CvarObject.r index fab57fe28..f7d1c1ce2 100644 --- a/ruamoko/cl_menu/CvarObject.r +++ b/ruamoko/cl_menu/CvarObject.r @@ -20,5 +20,6 @@ -(void)dealloc { str_free (name); + [super dealloc]; } @end diff --git a/ruamoko/cl_menu/CvarRangeView.r b/ruamoko/cl_menu/CvarRangeView.r index f32d37b6d..b9980ca0a 100644 --- a/ruamoko/cl_menu/CvarRangeView.r +++ b/ruamoko/cl_menu/CvarRangeView.r @@ -1,5 +1,6 @@ #include "key.h" #include "sound.h" +#include "legacy_string.h" #include "string.h" #include "gui/Text.h" diff --git a/ruamoko/cl_menu/CvarToggleView.r b/ruamoko/cl_menu/CvarToggleView.r index 2102e99ab..8c1e96840 100644 --- a/ruamoko/cl_menu/CvarToggleView.r +++ b/ruamoko/cl_menu/CvarToggleView.r @@ -47,7 +47,7 @@ { switch (key) { case QFK_RETURN: - case QFM_BUTTON1: + //case QFM_BUTTON1: [self toggle]; return 1; default: diff --git a/ruamoko/cl_menu/HUD.r b/ruamoko/cl_menu/HUD.r index 48bfc916f..b0ff525b2 100644 --- a/ruamoko/cl_menu/HUD.r +++ b/ruamoko/cl_menu/HUD.r @@ -166,7 +166,7 @@ int HUDHandleClass; if (looping) currentFrame = 0; else { - nextFrameTime = 0.0; + nextFrameTime = 0.0f; currentFrame = 0; return; } @@ -201,7 +201,7 @@ int HUDHandleClass; - (void) stop { - nextFrameTime = 0.0; + nextFrameTime = 0.0f; currentFrame = 0; } diff --git a/ruamoko/cl_menu/Makefile.am b/ruamoko/cl_menu/Makefile.am deleted file mode 100644 index 55c21a857..000000000 --- a/ruamoko/cl_menu/Makefile.am +++ /dev/null @@ -1,49 +0,0 @@ -## Process this file with automake to produce Makefile.in -AUTOMAKE_OPTIONS= foreign - -pkgdatadir=@sharepath@/QF - -QFCC_DEP=$(top_builddir)/tools/qfcc/source/qfcc$(EXEEXT) -QFCC=$(QFCC_DEP) -QCFLAGS=-qq -O -g -Wall -Werror -Wno-integer-divide --no-default-paths -QCPPFLAGS=-I. -I$(srcdir) -I$(top_builddir)/ruamoko/include -I$(top_srcdir)/ruamoko/include -I$(top_builddir)/include -I$(top_srcdir)/include -GZIP=if echo $@ | grep -q .gz; then gzip -f `basename $@ .gz`; if test -f `basename $@ .dat.gz`.sym; then gzip -f `basename $@ .dat.gz`.sym; fi; fi -GZ=@progs_gz@ -# BSD make can't handle $(shell foo) directives, and GNU make can't handle |= -# so we have to bite the bullet and pass this to the shell every time. -STRIP=`echo -n $(srcdir)/ | sed -e 's/[^/]//g' | wc -c` - -menu_data=menu.dat$(GZ) menu.sym$(GZ) menu.plist - -data=$(menu_data) - -pkgdata_DATA= $(data) -EXTRA_DATA= $(menu_data) - -menu_src= \ - client_menu.r controls_o.r options.r options_util.r servlist.r \ - Frame.r menu.r HUD.r plistmenu.r ../lib/debug.r \ - \ - CrosshairCvar.r CrosshairView.r CvarColor.r CvarColorView.r \ - CvarObject.r CvarRange.r CvarRangeView.r CvarString.r CvarStringView.r \ - CvarToggle.r CvarToggleView.r \ - MenuGroup.r MouseToggle.r ProxyView.r RunToggle.r SubMenu.r - -SUFFIXES=.qfo .r -.r.qfo: - $(QFCC) $(QCFLAGS) $(QCPPFLAGS) -p $(STRIP) -c -o $@ $< -menu_obj=$(menu_src:.r=.qfo) - -menu.dat$(GZ): $(menu_obj) $(QFCC_DEP) ../lib/libcsqc.a ../lib/libr.a ../gui/libgui.a - $(QFCC) $(QCFLAGS) -p $(STRIP) -o menu.dat $(menu_obj) ../gui/libgui.a ../lib/libcsqc.a ../lib/libr.a - $(GZIP) -menu.sym$(GZ): menu.dat$(GZ) - -EXTRA_DIST= $(menu_src) \ - CrosshairCvar.h CrosshairView.h CvarColor.h CvarColorView.h CvarObject.h \ - CvarRange.h CvarRangeView.h CvarString.h CvarStringView.h \ - CvarToggle.h CvarToggleView.h Frame.h HUD.h \ - MenuGroup.h MouseToggle.h ProxyView.h RunToggle.h SubMenu.h client_menu.h \ - controls_o.h menu.h options.h options_util.h plistmenu.h servlist.h \ - menu.plist -CLEANFILES= *.dat *.sym *.gz *.qfo diff --git a/ruamoko/cl_menu/Makemodule.am b/ruamoko/cl_menu/Makemodule.am new file mode 100644 index 000000000..90dcbb5ba --- /dev/null +++ b/ruamoko/cl_menu/Makemodule.am @@ -0,0 +1,77 @@ +ruamoko_cl_menu_libexec=ruamoko/cl_menu/menu.dat$(EXEEXT) +ruamoko_cl_menu_data=ruamoko/cl_menu/menu.plist ruamoko/cl_menu/menu.sym + +ruamoko_cl_menudir = @sharepath@/QF +ruamoko_cl_menu_DATA = $(ruamoko_cl_menu_data) +ruamoko_cl_menu_PROGRAMS = $(ruamoko_cl_menu_libexec) +EXTRA_PROGRAMS += $(ruamoko_cl_menu_libexec) + +ruamoko_menu_src= \ + ruamoko/cl_menu/client_menu.r \ + ruamoko/cl_menu/controls_o.r \ + ruamoko/cl_menu/options.r \ + ruamoko/cl_menu/options_util.r \ + ruamoko/cl_menu/servlist.r \ + ruamoko/cl_menu/Frame.r \ + ruamoko/cl_menu/HUD.r \ + ruamoko/cl_menu/menu.r \ + ruamoko/cl_menu/plistmenu.r \ + ruamoko/cl_menu/CrosshairCvar.r \ + ruamoko/cl_menu/CrosshairView.r \ + ruamoko/cl_menu/CvarColor.r \ + ruamoko/cl_menu/CvarColorView.r \ + ruamoko/cl_menu/CvarObject.r \ + ruamoko/cl_menu/CvarRange.r \ + ruamoko/cl_menu/CvarRangeView.r \ + ruamoko/cl_menu/CvarString.r \ + ruamoko/cl_menu/CvarStringView.r \ + ruamoko/cl_menu/CvarToggle.r \ + ruamoko/cl_menu/CvarToggleView.r \ + ruamoko/cl_menu/MenuGroup.r \ + ruamoko/cl_menu/MouseToggle.r \ + ruamoko/cl_menu/ProxyView.r \ + ruamoko/cl_menu/RunToggle.r \ + ruamoko/cl_menu/SubMenu.r + +ruamoko_cl_menu_menu_dat_SOURCES=$(ruamoko_menu_src) +ruamoko_menu_obj=$(ruamoko_cl_menu_menu_dat_SOURCES:.r=.o) +ruamoko_menu_dep=$(call qcautodep,$(ruamoko_cl_menu_menu_dat_SOURCES)) +ruamoko/cl_menu/menu.dat$(EXEEXT): $(ruamoko_menu_obj) $(QFCC_DEP) ruamoko/lib/libcsqc.a ruamoko/lib/libr.a ruamoko/gui/libgui.a + $(V_QFCCLD)$(QLINK) -o $@ $(ruamoko_menu_obj) -Lruamoko/gui -lgui -lcsqc -lr +include $(ruamoko_menu_dep) # am--include-marker +r_depfiles_remade += $(ruamoko_menu_dep) + +ruamoko/cl_menu/menu.sym: ruamoko/cl_menu/menu.dat$(EXEEXT) + +EXTRA_DIST += \ + ruamoko/cl_menu/CrosshairCvar.h \ + ruamoko/cl_menu/CrosshairView.h \ + ruamoko/cl_menu/CvarColor.h \ + ruamoko/cl_menu/CvarColorView.h \ + ruamoko/cl_menu/CvarObject.h \ + ruamoko/cl_menu/CvarRange.h \ + ruamoko/cl_menu/CvarRangeView.h \ + ruamoko/cl_menu/CvarString.h \ + ruamoko/cl_menu/CvarStringView.h \ + ruamoko/cl_menu/CvarToggle.h \ + ruamoko/cl_menu/CvarToggleView.h \ + ruamoko/cl_menu/Frame.h \ + ruamoko/cl_menu/HUD.h \ + ruamoko/cl_menu/MenuGroup.h \ + ruamoko/cl_menu/MouseToggle.h \ + ruamoko/cl_menu/ProxyView.h \ + ruamoko/cl_menu/RunToggle.h \ + ruamoko/cl_menu/SubMenu.h \ + ruamoko/cl_menu/client_menu.h \ + ruamoko/cl_menu/controls_o.h \ + ruamoko/cl_menu/menu.h \ + ruamoko/cl_menu/options.h \ + ruamoko/cl_menu/options_util.h \ + ruamoko/cl_menu/plistmenu.h \ + ruamoko/cl_menu/servlist.h \ + ruamoko/cl_menu/menu.plist + +CLEANFILES += \ + ruamoko/cl_menu/*.dat \ + ruamoko/cl_menu/*.sym +DISTCLEANFILES += $(ruamoko_menu_dep) diff --git a/ruamoko/cl_menu/MenuGroup.r b/ruamoko/cl_menu/MenuGroup.r index 5c89382c7..356d9d795 100644 --- a/ruamoko/cl_menu/MenuGroup.r +++ b/ruamoko/cl_menu/MenuGroup.r @@ -30,11 +30,11 @@ if (!ret) { switch (key) { case QFK_DOWN: - case QFM_WHEEL_DOWN: + //case QFM_WHEEL_DOWN: [self next]; return 1; case QFK_UP: - case QFM_WHEEL_UP: + //case QFM_WHEEL_UP: [self prev]; return 1; } diff --git a/ruamoko/cl_menu/client_menu.r b/ruamoko/cl_menu/client_menu.r index 769ad9cae..d0bef7345 100644 --- a/ruamoko/cl_menu/client_menu.r +++ b/ruamoko/cl_menu/client_menu.r @@ -1,6 +1,5 @@ #include "AutoreleasePool.h" #include "menu.h" -#include "file.h" #include "cmd.h" #include "gib.h" #include "draw.h" @@ -10,6 +9,7 @@ #include "options.h" #include "servlist.h" #include "system.h" +#include "qfs.h" #include "debug.h" #include "HUD.h" #include "client_menu.h" @@ -47,11 +47,11 @@ menu_key_sound = { switch (key) { case QFK_DOWN: - case QFM_WHEEL_DOWN: + //case QFM_WHEEL_DOWN: S_LocalSound ("misc/menu1.wav"); break; case QFK_UP: - case QFM_WHEEL_UP: + //case QFM_WHEEL_UP: S_LocalSound ("misc/menu1.wav"); break; } @@ -137,17 +137,18 @@ void (int quick) scan_saves = local QFile f; local string line; local int max = MAX_SAVEGAMES; - if (quick) + string basename = "s"; + if (quick) { max = MAX_QUICK; + basename = "quick"; + } for (i = 0; i < max; i++) { if (!filenames[i]) filenames[i] = str_new (); loadable[i] = 0; - if (quick) { - f = File_Open (sprintf ("quick%i.sav", i + 1), "rz"); - } else { - f = File_Open (sprintf ("s%i.sav", i), "rz"); - } + string path = sprintf ("%s%i.sav", basename, i); + //dprint(path + "\n"); + f = QFS_OpenFile (path); if (!f) { str_copy (filenames[i], "--- UNUSED SLOT ---"); continue; @@ -230,23 +231,23 @@ int (int key, int unicode, int down) load_quickbup_keyevent = { switch (key) { case QFK_DOWN: - case QFM_WHEEL_DOWN: + //case QFM_WHEEL_DOWN: S_LocalSound ("misc/menu1.wav"); load_cursor++; load_cursor %= MAX_QUICK; return 1; case QFK_UP: - case QFM_WHEEL_UP: + //case QFM_WHEEL_UP: S_LocalSound ("misc/menu1.wav"); load_cursor += MAX_QUICK - 1; load_cursor %= MAX_QUICK; return 1; case QFK_RETURN: - case QFM_BUTTON1: + //case QFM_BUTTON1: if (loadable[load_cursor]) { S_LocalSound ("misc/menu2.wav"); Menu_SelectMenu (nil); - Cbuf_AddText (sprintf ("load quick%i.sav\n", load_cursor)); + Cbuf_AddText (nil, sprintf ("load quick%i.sav\n", load_cursor)); load_cursor = MAX_SAVEGAMES; } return 1; @@ -258,19 +259,19 @@ int (int key, int unicode, int down) load_keyevent = { switch (key) { case QFK_DOWN: - case QFM_WHEEL_DOWN: + //case QFM_WHEEL_DOWN: S_LocalSound ("misc/menu1.wav"); load_cursor++; load_cursor %= MAX_SAVEGAMES + 1; return 1; case QFK_UP: - case QFM_WHEEL_UP: + //case QFM_WHEEL_UP: S_LocalSound ("misc/menu1.wav"); load_cursor += MAX_SAVEGAMES; load_cursor %= MAX_SAVEGAMES + 1; return 1; case QFK_RETURN: - case QFM_BUTTON1: + //case QFM_BUTTON1: if (load_cursor == MAX_SAVEGAMES) { load_cursor = 0; scan_saves (1); @@ -278,7 +279,7 @@ int (int key, int unicode, int down) load_keyevent = } else if (loadable[load_cursor]) { S_LocalSound ("misc/menu2.wav"); Menu_SelectMenu (nil); - Cbuf_AddText (sprintf ("load s%i.sav\n", load_cursor)); + Cbuf_AddText (nil, sprintf ("load s%i.sav\n", load_cursor)); } return 1; } @@ -289,21 +290,21 @@ int (int key, int unicode, int down) save_keyevent = { switch (key) { case QFK_DOWN: - case QFM_WHEEL_DOWN: + //case QFM_WHEEL_DOWN: S_LocalSound ("misc/menu1.wav"); save_cursor++; save_cursor %= MAX_SAVEGAMES; return 1; case QFK_UP: - case QFM_WHEEL_UP: + //case QFM_WHEEL_UP: S_LocalSound ("misc/menu1.wav"); save_cursor += MAX_SAVEGAMES - 1; save_cursor %= MAX_SAVEGAMES; return 1; case QFK_RETURN: - case QFM_BUTTON1: + //case QFM_BUTTON1: Menu_SelectMenu (nil); - Cbuf_AddText (sprintf ("save s%i.sav\n", save_cursor)); + Cbuf_AddText (nil, sprintf ("save s%i.sav\n", save_cursor)); return 1; } return 0; @@ -397,15 +398,15 @@ void () quit_menu = int (string text, int key) sp_start = { Menu_SelectMenu (nil); - Cbuf_AddText ("disconnect\n"); - Cbuf_AddText ("maxplayers 1\n"); - Cbuf_AddText ("coop 0\n"); - Cbuf_AddText ("deathmatch 0\n"); - Cbuf_AddText ("teamplay 0\n"); - Cbuf_AddText ("listen 0\n"); - Cbuf_AddText ("noexit 0\n"); - Cbuf_AddText ("samelevel 0\n"); - Cbuf_AddText ("map start\n"); + Cbuf_AddText (nil, "disconnect\n"); + Cbuf_AddText (nil, "maxplayers 1\n"); + Cbuf_AddText (nil, "coop 0\n"); + Cbuf_AddText (nil, "deathmatch 0\n"); + Cbuf_AddText (nil, "teamplay 0\n"); + Cbuf_AddText (nil, "listen 0\n"); + Cbuf_AddText (nil, "noexit 0\n"); + Cbuf_AddText (nil, "samelevel 0\n"); + Cbuf_AddText (nil, "map start\n"); return 0; }; @@ -493,7 +494,7 @@ int (int key, int unicode, int down) lanconfig_keyevent = [input_active processInput:(key >= 256 ? key : unicode)]; switch (key) { case QFK_DOWN: - case QFM_WHEEL_DOWN: + //case QFM_WHEEL_DOWN: if (!input_active) { S_LocalSound ("misc/menu2.wav"); lanConfig_cursor ++; @@ -501,7 +502,7 @@ int (int key, int unicode, int down) lanconfig_keyevent = } return 1; case QFK_UP: - case QFM_WHEEL_UP: + //case QFM_WHEEL_UP: if (!input_active) { S_LocalSound ("misc/menu2.wav"); lanConfig_cursor += NUM_LANCONFIG_CMDS - 1; @@ -597,12 +598,12 @@ void () main_menu = Menu_LeaveHook (menu_leave_sound); Menu_KeyEvent (menu_key_sound); Menu_FadeScreen (1); - Menu_Pic (16, 4, "gfx/qplaque.lmp"); Menu_CenterPic (160, 4, "gfx/ttl_main.lmp"); if (do_single_player) Menu_Pic (71,32, "gfx/mainmenu.lmp"); else Menu_SubPic (71,52, "gfx/mainmenu.lmp", 0, 20, 240, 92); + Menu_Pic (16, 4, "gfx/qplaque.lmp"); Menu_Cursor (spinner); if (do_single_player) single_player_menu (); diff --git a/ruamoko/cl_menu/controls_o.r b/ruamoko/cl_menu/controls_o.r index 5c389d88e..3ae1b60c2 100644 --- a/ruamoko/cl_menu/controls_o.r +++ b/ruamoko/cl_menu/controls_o.r @@ -30,6 +30,7 @@ #include "draw.h" #include "system.h" #include "debug.h" +#include "legacy_string.h" #include "string.h" #include "key.h" #include "options_util.h" diff --git a/ruamoko/cl_menu/menu.r b/ruamoko/cl_menu/menu.r index 748bd35ed..a47650a42 100644 --- a/ruamoko/cl_menu/menu.r +++ b/ruamoko/cl_menu/menu.r @@ -21,3 +21,4 @@ int () Menu_GetIndex = #0; void (void) Menu_Next = #0; void (void) Menu_Prev = #0; void (void) Menu_Enter = #0; +void (void) Menu_Leave = #0; diff --git a/ruamoko/cl_menu/options.r b/ruamoko/cl_menu/options.r index e0529c23e..c587c9ccd 100644 --- a/ruamoko/cl_menu/options.r +++ b/ruamoko/cl_menu/options.r @@ -130,7 +130,7 @@ MENU_video_options (PLItem *plist) if (plist) { ret = object_from_plist ([(PLDictionary*) plist getObjectForKey:"video_options"]); - video_options = ret.pointer_val; + video_options = (Group *) ret.pointer_val; } Menu_End (); @@ -177,7 +177,7 @@ MENU_audio_options (PLItem *plist) if (plist) { ret = object_from_plist ([(PLDictionary*) plist getObjectForKey:"audio_options"]); - audio_options = ret.pointer_val; + audio_options = (Group *) ret.pointer_val; } Menu_End (); @@ -224,7 +224,7 @@ MENU_control_options (PLItem *plist) if (plist) { ret = object_from_plist ([(PLDictionary*) plist getObjectForKey:"control_options"]); - control_options = ret.pointer_val; + control_options = (Group *) ret.pointer_val; } MENU_control_binding (); //FIXME how to hook in the bindings menu? @@ -275,7 +275,7 @@ MENU_feature_options (PLItem *plist) if (plist) { ret = object_from_plist ([(PLDictionary*) plist getObjectForKey:"feature_options"]); - feature_options = ret.pointer_val; + feature_options = (Group *) ret.pointer_val; } Menu_End (); @@ -361,7 +361,7 @@ MENU_player_options (PLItem *plist) if (plist) { ret = object_from_plist ([(PLDictionary*) plist getObjectForKey:"player_options"]); - player_options = ret.pointer_val; + player_options = (Group *) ret.pointer_val; } Menu_End (); @@ -426,7 +426,7 @@ MENU_network_options (PLItem *plist) if (plist) { ret = object_from_plist ([(PLDictionary*) plist getObjectForKey:"network_options"]); - network_options = ret.pointer_val; + network_options = (Group *) ret.pointer_val; } Menu_End (); @@ -436,7 +436,7 @@ int (string text, int key) op_goto_console = { Menu_SelectMenu (""); - Cbuf_AddText ("toggleconsole\n"); + Cbuf_AddText (nil, "toggleconsole\n"); return 0; }; diff --git a/ruamoko/cl_menu/plistmenu.r b/ruamoko/cl_menu/plistmenu.r index 04d4355b7..2694e48b5 100644 --- a/ruamoko/cl_menu/plistmenu.r +++ b/ruamoko/cl_menu/plistmenu.r @@ -25,6 +25,7 @@ */ #include "debug.h" +#include "legacy_string.h" #include "string.h" #include "qfs.h" @@ -58,6 +59,7 @@ class_from_plist (PLDictionary *pldict) return ret; } obj = [class alloc]; + params[0].pointer_val = obj; messages = (PLArray*) [pldict getObjectForKey:"Messages"]; message_count = [messages count]; @@ -65,9 +67,10 @@ class_from_plist (PLDictionary *pldict) msg = (PLArray*) [messages getObjectAtIndex:i]; selname = [(PLString*) [msg getObjectAtIndex:0] string]; sel = sel_get_uid (selname); - va_list.count = [msg count] - 1; - for (j = 0; j < va_list.count; j++) { - paramstr = [(PLString*) [msg getObjectAtIndex:j + 1] string]; + params[1].pointer_val = sel; + va_list.count = [msg count] + 1; + for (j = 2; j < va_list.count; j++) { + paramstr = [(PLString*) [msg getObjectAtIndex:j - 1] string]; switch (str_mid (paramstr, 0, 1)) { case "\"": va_list.list[j].string_val = str_mid (paramstr, 1, -1); @@ -78,10 +81,10 @@ class_from_plist (PLDictionary *pldict) break; case "0": case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9": - if (str_str (paramstr, ".")) + if (str_str (paramstr, ".") >= 0) va_list.list[j].float_val = stof (paramstr); else - va_list.list[j].integer_val = stoi (paramstr); + va_list.list[j].int_val = stoi (paramstr); break; } } @@ -103,7 +106,7 @@ array_from_plist (PLArray *plarray) count = [plarray count]; for (i = 0; i < count; i++) { ret = object_from_plist ([plarray getObjectAtIndex:i]); - [array addObject: ret.pointer_val]; + [array addObject: (id) ret.pointer_val]; } ret.pointer_val = array; return ret; @@ -142,7 +145,7 @@ string_from_plist (PLString *plstring) local @param ret; local string str = [plstring string]; - ret.quaternion_val = nil; //FIXME should be ret = nil; + ret = nil; if (str_mid (str, 0, 1) == "[") return rect_from_plist (plstring); diff --git a/ruamoko/game/Axe.r b/ruamoko/game/Axe.r index 06c4ca083..7e3c49745 100644 --- a/ruamoko/game/Axe.r +++ b/ruamoko/game/Axe.r @@ -14,7 +14,7 @@ - (id) init { [super init]; - damage = (deathmatch > 3) ? 75.0 : 20.0; + damage = (deathmatch > 3) ? 75.0f : 20.0f; return self; } @@ -32,7 +32,7 @@ source = s.origin + '0 0 16'; traceline (source, source + v_forward * 64, NO, s); - if (trace_fraction == 1.0) + if (trace_fraction == 1.0f) return; org = trace_endpos - v_forward * 4; diff --git a/ruamoko/game/Makefile.am b/ruamoko/game/Makefile.am deleted file mode 100644 index e1ff2fbf2..000000000 --- a/ruamoko/game/Makefile.am +++ /dev/null @@ -1,35 +0,0 @@ -## Process this file with automake to produce Makefile.in -AUTOMAKE_OPTIONS= foreign - -#FIXME should qf data be installed somewhere other than id1 that gets -#searched after everything else? -pkgdatadir=@sharepath@/id1 - -QFCC_DEP=$(top_builddir)/tools/qfcc/source/qfcc$(EXEEXT) -QFCC=$(QFCC_DEP) -QCFLAGS=-qq -O -g -Werror -Wall -Wno-integer-divide --no-default-paths -QCPPFLAGS=-I. -I$(srcdir) -I$(top_builddir)/ruamoko/include -I$(top_srcdir)/ruamoko/include -GZIP=if echo $@ | grep -q .gz; then gzip -f `basename $@ .gz`; if test -f `basename $@ .dat.gz`.sym; then gzip -f `basename $@ .dat.gz`.sym; fi; fi -GZ=@progs_gz@ -# BSD make can't handle $(shell foo) directives, and GNU make can't handle |= -# so we have to bite the bullet and pass this to the shell every time. -STRIP=`echo -n $(srcdir)/ | sed -e 's/[^/]//g' | wc -c` - -data=game.dat$(GZ) - -noinst_DATA= $(data) -EXTRA_DATA= game.dat - -game_src= Axe.r GameEntity.r World.r tempent.r - -SUFFIXES=.qfo .r -.r.qfo: - $(QFCC) $(QCFLAGS) $(QCPPFLAGS) -p $(STRIP) -c -o $@ $< - -game_obj=$(game_src:.r=.qfo) -game.dat$(GZ): $(game_obj) ../lib/libr.a ../lib/libqw.a - $(QFCC) $(QCFLAGS) -p $(STRIP) -o game.dat $(game_obj) ../lib/libr.a ../lib/libqw.a ../lib/libr.a - $(GZIP) - -EXTRA_DIST= $(game_src) Axe.h GameEntity.h tempent.h Weapon.h World.h -CLEANFILES= *.dat *.sym *.gz *.qfo diff --git a/ruamoko/game/Makemodule.am b/ruamoko/game/Makemodule.am new file mode 100644 index 000000000..576867c27 --- /dev/null +++ b/ruamoko/game/Makemodule.am @@ -0,0 +1,27 @@ +ruamoko_game_libexec=ruamoko/game/game.dat$(EXEEXT) + +noinst_PROGRAMS += $(ruamoko_game_libexec) +EXTRA_PROGRAMS += $(ruamoko_game_libexec) + +ruamoko_game_src= ruamoko/game/Axe.r ruamoko/game/GameEntity.r ruamoko/game/World.r ruamoko/game/tempent.r + +ruamoko_game_game_dat_SOURCES=$(ruamoko_game_src) +ruamoko_game_obj=$(ruamoko_game_game_dat_SOURCES:.r=.o) +ruamoko_game_dep=$(call qcautodep,$(ruamoko_game_game_dat_SOURCES)) +ruamoko/game/game.dat$(EXEEXT): $(ruamoko_game_obj) $(QFCC_DEP) ruamoko/lib/libr.a ruamoko/lib/libqw.a + $(V_QFCCLD)$(QLINK) -o $@ $(ruamoko_game_obj) -lqw -lr +include $(ruamoko_game_dep) # am--include-marker +r_depfiles_remade += $(ruamoko_game_dep) + +ruamoko/game/game.sym: ruamoko/game/game.dat + +EXTRA_DIST += $(ruamoko_game_src) \ + ruamoko/game/Axe.h \ + ruamoko/game/GameEntity.h \ + ruamoko/game/tempent.h \ + ruamoko/game/Weapon.h \ + ruamoko/game/World.h +CLEANFILES += \ + ruamoko/game/*.dat \ + ruamoko/game/*.sym +DISTCLEANFILES += $(ruamoko_game_dep) diff --git a/ruamoko/gatest/Makemodule.am b/ruamoko/gatest/Makemodule.am new file mode 100644 index 000000000..d1021be95 --- /dev/null +++ b/ruamoko/gatest/Makemodule.am @@ -0,0 +1,35 @@ +noinst_PROGRAMS += ruamoko/gatest/gatest.dat$(EXEEXT) + +gatest_dat_src= \ + ruamoko/gatest/algebra.r \ + ruamoko/gatest/basisblade.r \ + ruamoko/gatest/basisgroup.r \ + ruamoko/gatest/basislayout.r \ + ruamoko/gatest/main.r \ + ruamoko/gatest/metric.r \ + ruamoko/gatest/multivector.r \ + ruamoko/gatest/util.r + +ruamoko_gatest_gatest_dat_SOURCES=$(gatest_dat_src) +ruamoko_gatest_gatest_obj=$(ruamoko_gatest_gatest_dat_SOURCES:.r=.o) +ruamoko_gatest_gatest_dep=$(call qcautodep,$(ruamoko_gatest_gatest_dat_SOURCES:.o=.Qo)) +ruamoko/gatest/gatest.dat$(EXEEXT): $(ruamoko_gatest_gatest_obj) $(QFCC_DEP) $(libui) ruamoko/lib/libr.a + $(V_QFCCLD)$(QLINK) -o $@ $(ruamoko_gatest_gatest_obj) $(libui) -lr +include $(ruamoko_gatest_gatest_dep) # am--include-marker +r_depfiles_remade += $(ruamoko_gatest_gatest_dep) + +EXTRA_PROGRAMS += \ + ruamoko/gatest/gatest + +EXTRA_DIST += \ + $(gatest_dat_src) \ + ruamoko/gatest/algebra.h \ + ruamoko/gatest/basisblade.h \ + ruamoko/gatest/basisgroup.h \ + ruamoko/gatest/basislayout.h \ + ruamoko/gatest/metric.h \ + ruamoko/gatest/multivector.h \ + ruamoko/gatest/util.h +CLEANFILES += \ + ruamoko/gatest/*.dat \ + ruamoko/gatest/*.sym diff --git a/ruamoko/gatest/algebra.h b/ruamoko/gatest/algebra.h new file mode 100644 index 000000000..d6a773454 --- /dev/null +++ b/ruamoko/gatest/algebra.h @@ -0,0 +1,35 @@ +#ifndef __algebra_h +#define __algebra_h + +#include "Object.h" + +@class Metric; +@class BasisGroup; +@class BasisLayout; +@class MultiVector; + +@interface Algebra : Object +{ + Metric *metric; + BasisGroup **grades; + BasisLayout *layout; + int num_components; + int dimension; + int plus, minus, zero; +} ++(Algebra *) R:(int)p, int m, int z; ++(Algebra *) PGA:(int)n; +-(void) print; +-(BasisGroup *)grade:(int)k; +-(BasisLayout *)layout; +-(Metric *) metric; +-(int)count; +-(int)dimension; + +-(MultiVector *) group:(int)group; +-(MultiVector *) group:(int)group values:(double *)values; +-(MultiVector *) ofGrade:(int)grade; +-(MultiVector *) ofGrade:(int)grade values:(double *)values; +@end + +#endif//__algebra_h diff --git a/ruamoko/gatest/algebra.r b/ruamoko/gatest/algebra.r new file mode 100644 index 000000000..28ee11f8c --- /dev/null +++ b/ruamoko/gatest/algebra.r @@ -0,0 +1,171 @@ +#include +#include + +#include "algebra.h" +#include "metric.h" +#include "basisblade.h" +#include "basisgroup.h" +#include "basislayout.h" +#include "multivector.h" +#include "util.h" + +@implementation Algebra ++(Algebra *) R:(int)p, int m, int z +{ + Algebra *a = [[[Algebra alloc] init] autorelease]; + a.metric = [[Metric R:p, m, z] retain]; + + a.plus = p; + a.minus = m; + a.zero = z; + int d = p + m + z; + a.dimension = d; + a.num_components = 1 << d; + BasisBlade **blades = obj_malloc (a.num_components * sizeof (BasisBlade *)); + int *counts = binomial (d); + int *indices = obj_malloc ((d + 1) * sizeof (int)); + + indices[0] = 0; + for (int i = 0; i < d; i++) { + indices[i + 1] = counts[i]; + } + prefixsum (indices, d + 1); + for (int i = 0; i < a.num_components; i++) { + int grade = count_bits (i); + int ind = indices[grade]++; + unsigned mask = i; + if (!z) { + // e0 is best for the null vector, but this geometry has no null + // vectors, so skip it; + mask <<= 1; + } + blades[ind] = [BasisBlade basis:i]; + } + + a.grades = obj_malloc ((d + 1) * sizeof (BasisGroup *)); + for (int i = 0; i < d + 1; i++) { + int c = counts[i]; + int ind = indices[i]; + a.grades[i] = [[BasisGroup new:c basis:blades + ind - c] retain]; + } + + if (p == 3 && m == 0 && z == 1) { + // 3d PGA (w squares to 0, x y z square to +1): + // : x y z w + // : yz zx xy 1 + // : wx wy wz wxyz + // : wzy wxz wyx xyz + BasisBlade *pga_blades[16] = { + blades[2], blades[3], blades[4], blades[1], + blades[10], blades[9], blades[7], blades[0], + blades[5], blades[6], blades[8], blades[15], + blades[13], blades[12], blades[11], blades[14], + }; + BasisGroup *pga_groups[4] = { + [BasisGroup new:4 basis:pga_blades + 0], + [BasisGroup new:4 basis:pga_blades + 4], + [BasisGroup new:4 basis:pga_blades + 8], + [BasisGroup new:4 basis:pga_blades + 12], + }; + a.layout = [[BasisLayout new:4 groups: pga_groups] retain]; + } else if (p == 2 && m == 0 && z == 1) { + // 2d PGA (w squares to 0, x y square to +1): + // : 1 xy wx wy + // : x y w wxy + BasisBlade *pga_blades[8] = { + blades[0], blades[6], blades[4], blades[5], + blades[2], blades[3], blades[1], blades[7], + }; + BasisGroup *pga_groups[2] = { + [BasisGroup new:4 basis:pga_blades + 0], + [BasisGroup new:4 basis:pga_blades + 4], + }; + a.layout = [[BasisLayout new:2 groups: pga_groups] retain]; + } else { + // just use the grades as the default layout + a.layout = [[BasisLayout new:d + 1 groups: a.grades] retain]; + } + + obj_free (indices); + obj_free (counts); + obj_free (blades); + return a; +} + ++(Algebra *) PGA:(int)n +{ + return [Algebra R:n, 0, 1]; +} + +-(void)dealloc +{ + obj_free (grades); + [metric release]; + [layout release]; + [super dealloc]; +} + +-(BasisGroup *)grade:(int)k +{ + return grades[k]; +} + +-(BasisLayout *)layout +{ + return layout; +} + +-(Metric *) metric +{ + return metric; +} + +-(int)count +{ + return num_components; +} + +-(int)dimension +{ + return dimension; +} + +-(MultiVector *) group:(int)group +{ + return [MultiVector new:self group:[layout group:group]]; +} + +-(MultiVector *) group:(int)group values:(double *)values +{ + return [MultiVector new:self group:[layout group:group] values:values]; +} + +-(MultiVector *) ofGrade:(int)grade +{ + return [MultiVector new:self group:grades[grade]]; +} + +-(MultiVector *) ofGrade:(int)grade values:(double *)values +{ + return [MultiVector new:self group:grades[grade] values:values]; +} + +-(void) print +{ + int count = [layout count]; + for (int i = 0; i < count; i++) { + BasisGroup *g = [layout group:i]; + int c = [g count]; + printf ("%d %d %@\n", i, c, [g set]); + for (int j = 0; j < c; j++) { + printf (" %@\n", [g bladeAt:j]); + } + } +} + +-(string) describe +{ + return sprintf ("R(%d,%d,%d)", plus, minus, zero); +} + +@end diff --git a/ruamoko/gatest/basisblade.h b/ruamoko/gatest/basisblade.h new file mode 100644 index 000000000..115a81dee --- /dev/null +++ b/ruamoko/gatest/basisblade.h @@ -0,0 +1,26 @@ +#ifndef __basisblade_h +#define __basisblade_h +#include + +@class Metric; + +@interface BasisBlade : Object +{ + unsigned mask; + double scale; +} ++(BasisBlade *) scalar:(double) scale; ++(BasisBlade *) zero; ++(BasisBlade *) basis:(unsigned) mask; ++(BasisBlade *) basis:(unsigned) mask scale:(double) scale; +-(BasisBlade *) product:(BasisBlade *) b isOuter:(int)outer metric:(Metric *) m; +-(BasisBlade *) outerProduct:(BasisBlade *) b; +-(BasisBlade *) geometricProduct:(BasisBlade *) b metric:(Metric *) m; +-(BasisBlade *) geometricProduct:(BasisBlade *) b; +-(int) grade; +-(unsigned) mask; +-(double) scale; +-(string) name; +@end + +#endif//__basisblade_h diff --git a/ruamoko/gatest/basisblade.r b/ruamoko/gatest/basisblade.r new file mode 100644 index 000000000..a3ad43a54 --- /dev/null +++ b/ruamoko/gatest/basisblade.r @@ -0,0 +1,106 @@ +#include +#include "basisblade.h" +#include "metric.h" +#include "util.h" + +@implementation BasisBlade : Object +-(BasisBlade *) initWithMask:(unsigned) mask scale:(double) scale +{ + if (!(self = [super init])) { + return self; + } + self.mask = mask; + self.scale = scale; + return self; +} + ++(BasisBlade *) scalar:(double) scale +{ + return [[[BasisBlade alloc] initWithMask:0 scale:scale] autorelease]; +} + ++(BasisBlade *) zero +{ + return [[[BasisBlade alloc] initWithMask:0 scale:0] autorelease]; +} + ++(BasisBlade *) basis:(unsigned) mask +{ + return [[[BasisBlade alloc] initWithMask:mask scale:1] autorelease]; +} + ++(BasisBlade *) basis:(unsigned) mask scale:(double) scale +{ + return [[[BasisBlade alloc] initWithMask:mask scale:scale] autorelease]; +} + +-(BasisBlade *) product:(BasisBlade *) b isOuter:(int)outer metric:(Metric *)m +{ + if (outer && (mask & b.mask)) { + // the two blades share at least one basis vector + return [BasisBlade zero]; + } + if (!scale || !b.scale) { + return [BasisBlade zero]; + } + int sign = 1 - (-(count_flips (mask, b.mask) & 1) & 2); + double s = scale * b.scale * sign; + if (m) { + s *= [m apply: mask, b.mask]; + if (!s) { + return [BasisBlade zero]; + } + } + return [BasisBlade basis:(mask ^ b.mask) scale:s]; +} + +-(BasisBlade *) outerProduct:(BasisBlade *) b +{ + return [self product:b isOuter:1 metric:nil]; +} + +-(BasisBlade *) geometricProduct:(BasisBlade *) b metric:(Metric *) m +{ + return [self product:b isOuter:0 metric:m]; +} + +-(BasisBlade *) geometricProduct:(BasisBlade *) b +{ + return [self product:b isOuter:0 metric:nil]; +} + +-(int) grade +{ + return count_bits (mask); +} + +-(unsigned) mask +{ + return mask; +} + +-(double) scale +{ + return scale; +} + +-(string) name +{ + string basis = ""; + + for (int i = 0; i < 32; i++) { + if (mask & (1 << i)) { + basis += sprintf("%x", i); + } + } + if (basis) { + basis = "e" + basis; + } + return basis; +} + +-(string) describe +{ + return sprintf ("%g%s", scale, [self name]); +} +@end diff --git a/ruamoko/gatest/basisgroup.h b/ruamoko/gatest/basisgroup.h new file mode 100644 index 000000000..e1ee77d17 --- /dev/null +++ b/ruamoko/gatest/basisgroup.h @@ -0,0 +1,25 @@ +#ifndef __basisgroup_h +#define __basisgroup_h +#include + +@class Metric; +@class BasisBlade; +@class Set; + +@interface BasisGroup : Object +{ + int count; + uivec2 range; + BasisBlade **blades; + int *map; + Set *set; +} ++(BasisGroup *) new:(int) count basis:(BasisBlade **)blades; +-(int)count; +-(uivec2)blade_range; +-(BasisBlade *) bladeAt:(int) ind; +-(BasisBlade *) blade:(unsigned) mask; +-(Set *) set; +@end + +#endif//__basisgroup_h diff --git a/ruamoko/gatest/basisgroup.r b/ruamoko/gatest/basisgroup.r new file mode 100644 index 000000000..abe18f4e1 --- /dev/null +++ b/ruamoko/gatest/basisgroup.r @@ -0,0 +1,69 @@ +#include +#include "basisblade.h" +#include "basisgroup.h" + +@implementation BasisGroup ++(BasisGroup *) new:(int) count basis:(BasisBlade **)blades +{ + BasisGroup *group = [[[BasisGroup alloc] init] autorelease]; + group.blades = obj_malloc (count * sizeof (BasisBlade *)); + group.set = [[Set set] retain]; + group.range = { ~0u, 0 }; + for (int i = 0; i < count; i++) { + group.blades[i] = [blades[i] retain]; + unsigned m = [blades[i] mask]; + if (m < group.range[0]) { + group.range[0] = m; + } + if (m > group.range[1]) { + group.range[1] = m; + } + [group.set add:m]; + } + int num = group.range[1] - group.range[0] + 1; + group.map = obj_malloc (num * sizeof (int)); + for (int i = 0; i < count; i++) { + group.map[[blades[i] mask] - group.range[0]] = i; + } + group.count = count; + return group; +} + +-(void)dealloc +{ + [set release]; + for (int i = 0; i < count; i++) { + [blades[i] release]; + } + obj_free (blades); + [super dealloc]; +} + +-(int)count +{ + return count; +} + +-(uivec2)blade_range +{ + return range; +} + +-(BasisBlade *) bladeAt:(int) ind +{ + return blades[ind]; +} + +-(BasisBlade *) blade:(unsigned) mask +{ + if (![set is_member:mask]) { + return nil; + } + return blades[map[mask - range[0]]]; +} + +-(Set *) set +{ + return set; +} +@end diff --git a/ruamoko/gatest/basislayout.h b/ruamoko/gatest/basislayout.h new file mode 100644 index 000000000..471a46316 --- /dev/null +++ b/ruamoko/gatest/basislayout.h @@ -0,0 +1,28 @@ +#ifndef __basislayout_h +#define __basislayout_h +#include + +@class BasisGroup; +@class Set; + +@interface BasisLayout : Object +{ + int count; + uivec2 range; + BasisGroup **groups; + ivec3 *group_map; + int *mask_map; + int blade_count; + Set *set; +} ++(BasisLayout *) new:(int) count groups:(BasisGroup **)groups; +-(int)count; +-(int)num_components; +-(int)blade_count; +-(BasisGroup *) group:(int) ind; +-(BasisBlade *) bladeAt:(int) ind; +-(int) bladeIndex:(unsigned) mask; +-(Set *) set; +@end + +#endif//__basislayout_h diff --git a/ruamoko/gatest/basislayout.r b/ruamoko/gatest/basislayout.r new file mode 100644 index 000000000..5bd1d452f --- /dev/null +++ b/ruamoko/gatest/basislayout.r @@ -0,0 +1,111 @@ +#include +#include +#include "basisblade.h" +#include "basisgroup.h" +#include "basislayout.h" +#include "util.h" + +@implementation BasisLayout ++(BasisLayout *) new:(int) count groups:(BasisGroup **)groups +{ + BasisLayout *layout = [[[BasisLayout alloc] init] autorelease]; + layout.count = count; + layout.groups = obj_malloc (count * sizeof (BasisGroup *)); + layout.set = [[Set set] retain]; + layout.range = { ~0u, 0 }; + int *group_base = obj_malloc ((count + 1) * sizeof (int)); + group_base[0] = 0; + int num_blades = 0; + for (int i = 0; i < count; i++) { + layout.groups[i] = [groups[i] retain]; + [layout.set union:[groups[i] set]]; + group_base[i + 1] = [groups[i] count]; + num_blades += group_base[i + 1]; + + uivec2 r = [groups[i] blade_range]; + if (r[0] < layout.range[0]) { + layout.range[0] = r[0]; + } + if (r[1] > layout.range[1]) { + layout.range[1] = r[1]; + } + } + prefixsum (group_base, count); + layout.blade_count = num_blades; + layout.group_map = obj_malloc (num_blades * sizeof (ivec3)); + + int num = layout.range[1] - layout.range[0] + 1; + layout.mask_map = obj_malloc (num * sizeof (int)); + int *group_inds = obj_malloc ((count + 1) * sizeof (int)); + for (int i = 0; i < count; i++) { + BasisGroup *g = groups[i]; + group_inds[i] = 0; + for (int j = 0; j < [g count]; j++) { + BasisBlade *b = [g bladeAt:j]; + layout.mask_map[[b mask] - layout.range[0]] = group_inds[count]; + layout.group_map[group_inds[count]][0] = i; + layout.group_map[group_inds[count]][1] = group_inds[i]++; + layout.group_map[group_inds[count]][2] = group_base[i]; + group_inds[count]++; + } + } + obj_free (group_inds); + return layout; +} + +-(void)dealloc +{ + [set release]; + for (int i = 0; i < count; i++) { + [groups[i] release]; + } + obj_free (groups); + obj_free (group_map); + [super dealloc]; +} + +-(int)count +{ + return count; +} + +-(int)num_components +{ + int num_components = 0; + // assumes there is no overlap + for (int i = 0; i < count; i++) { + num_components += [groups[i] count]; + } + return num_components; +} + +-(int)blade_count +{ + return blade_count; +} + +-(BasisGroup *) group:(int) ind +{ + return groups[ind]; +} + +-(BasisBlade *) bladeAt:(int) ind +{ + ivec3 gm = group_map[ind]; + BasisGroup *g = groups[gm[0]]; + return [g bladeAt:ind - gm[2]]; +} + +-(int) bladeIndex:(unsigned) mask +{ + if (![set is_member:mask]) { + return 0; + } + return mask_map[mask - range[0]]; +} + +-(Set *) set +{ + return set; +} +@end diff --git a/ruamoko/gatest/main.r b/ruamoko/gatest/main.r new file mode 100644 index 000000000..5b30ec26d --- /dev/null +++ b/ruamoko/gatest/main.r @@ -0,0 +1,472 @@ +#include +#include +#include +#include +#include + +#include "algebra.h" +#include "basisblade.h" +#include "basisgroup.h" +#include "metric.h" +#include "multivector.h" +#include "util.h" + +@static AutoreleasePool *autorelease_pool; +@static void +arp_start (void) +{ + autorelease_pool = [[AutoreleasePool alloc] init]; +} + +@static void +arp_end (void) +{ + [autorelease_pool release]; + autorelease_pool = nil; +} + +static void +basic_test (void) +{ + arp_start (); + + BasisBlade *a = [[BasisBlade basis:1] retain]; + BasisBlade *b = [[BasisBlade basis:2] retain]; + BasisBlade *c = [[BasisBlade basis:4] retain]; + BasisBlade *d = [[BasisBlade basis:8] retain]; + BasisBlade *blades[] = {a, b, c, d}; + static string names[] = {"a", "b", "c", "d"}; + +// printf ("a: %@\n", a); +// printf ("b: %@\n", b); +// printf ("c: %@\n", c); +// printf ("d: %@\n", d); + + arp_end (); +#if 0 + arp_start (); + for (int i = 0; i < 4; i++) { + arp_end (); + arp_start (); + BasisBlade *vec = blades[i]; + printf ("%s: %@\n", names[i], vec); + for (int j = 0; j < 4; j++) { + BasisBlade *bvec = [vec outerProduct:blades[j]]; + if (![bvec scale]) { + continue; + } + printf ("%s^%s: %@\n", names[i], names[j], bvec); + for (int k = 0; k < 4; k++) { + BasisBlade *tvec = [bvec outerProduct:blades[k]]; + if (![tvec scale]) { + continue; + } + printf ("%s^%s^%s: %@\n", names[i], names[j], names[k], + tvec); + for (int l = 0; l < 4; l++) { + BasisBlade *qvec = [tvec outerProduct:blades[l]]; + if (![qvec scale]) { + continue; + } + printf ("%s^%s^%s^%s: %@\n", + names[i], names[j], names[k], names[l], + qvec); + } + } + } + } + arp_end (); +#endif + arp_start (); + + Metric *m = [Metric R:3,0,1]; + BasisBlade *ad = [a geometricProduct:d metric:m]; + BasisBlade *prod = [ad geometricProduct:ad metric:m]; + printf ("%s%s %s%s: %@\n", + names[0], names[3], names[0], names[3], prod); + + Algebra *alg = [Algebra R:3, 0, 1]; + double plane1_vals[4] = {1, 0, 0, 8}; + double plane2_vals[4] = {0, 1, 0, 8}; + double origin_vals[4] = {0, 0, 0, 1}; + MultiVector *plane1 = [alg group:0 values:plane1_vals]; + MultiVector *plane2 = [alg group:0 values:plane2_vals]; + MultiVector *origin = [alg group:3 values:origin_vals]; + + MultiVector *line = [plane1 wedge:plane2]; + MultiVector *point = [[line dot:origin] product:[line reverse]]; + printf ("plane1:%@\nplane2:%@\nline:%@\norigin:%@\n", plane1, plane2, line, origin); + printf ("point:%@\n", point); + + arp_end (); +} + +typedef struct var_s { + string name; + MultiVector *value; +} var_t; + +static hashtab_t *symtab; + +typedef enum { + EOF, SEMI, + VAR, EQUAL, + ID, VALUE, + OPENP, CLOSEP, + OPENB, CLOSEB, + OPENS, CLOSES, + MUL, DIV, PLUS, MINUS, + WEDGE, ANTIWEDGE, DOT, + REVERSE, DUAL, +} token_e; + +script_t script; +string token_str; +token_e lookahead = -1; + +typedef struct token_s { + token_e id; + union { + MultiVector *value; + string name; + }; +} token_t; + +static token_t +get_token () +{ + if (!Script_TokenAvailable (script, 1)) { + return {EOF, nil}; + } + Script_GetToken (script, 1); + switch (token_str) { + case "var": return {VAR, nil}; + case "=": return {EQUAL, nil}; + case ";": return {SEMI, nil}; + case "(": return {OPENP, nil}; + case ")": return {CLOSEP, nil}; + case "[": return {OPENB, nil}; + case "]": return {CLOSEB, nil}; + case "{": return {OPENS, nil}; + case "}": return {CLOSES, nil}; + case "*": return {MUL, nil}; + case "/": return {DIV, nil}; + case "+": return {PLUS, nil}; + case "-": return {MINUS, nil}; + case "^": return {WEDGE, nil}; + case "&": return {ANTIWEDGE, nil}; + case ".": return {DOT, nil}; + case "~": return {REVERSE, nil}; + case "!": return {DUAL, nil}; + } + return {ID, .name = token_str }; +} + +static void +syntax_error () +{ + obj_error (nil, 0, "syntax error before `%s': %d\n", token_str, + Script_GetLine (script)); +} + +static int +match (token_e token) +{ + if (lookahead == -1) { + lookahead = get_token ().id; + } + return token == lookahead; +} + +static void +advance () +{ + lookahead = get_token ().id; +} + +static Algebra *algebra; +static MultiVector *minus_one; +static MultiVector *expression (); + +static int +is_digit (string x) +{ + return (x == "0" || x == "1" || x == "2" || x == "3" || + x == "4" || x == "5" || x == "6" || x == "7" || + x == "8" || x == "9"); +} + +static int +is_number (string x) +{ + return x == "." || is_digit (x); +} + +static MultiVector * +factor () +{ + MultiVector *vec; + if (match (REVERSE)) { + advance (); + vec = [factor () reverse]; + } else if (match (DUAL)) { + advance (); + vec = [factor () dual]; + } else if (match (MINUS)) { + advance (); + vec = [minus_one product:factor ()]; + } else if (match (OPENP)) { + advance (); + vec = expression (); + if (!match (CLOSEP)) { + syntax_error (); + } + advance (); + } else if (match (ID)) { + if (is_digit (str_mid (token_str, 0, 1))) { + string num_str = nil; + string blade_str = nil; + int pos = 0; + while (is_number (str_mid (token_str, pos, pos + 1))) { + pos++; + } + num_str = str_mid (token_str, 0, pos); + if (str_mid (token_str, pos, pos + 1) == "e") { + blade_str = str_mid (token_str, ++pos); + } + BasisBlade *blade = [BasisBlade basis:0]; + pos = 0; + while (is_digit (str_mid (blade_str, pos, pos + 1))) { + int x = str_char (blade_str, pos++) - '0'; + BasisBlade *new = [BasisBlade basis:1 << x]; + blade = [blade outerProduct:new]; + } + + double num = strtod (num_str, nil) * [blade scale]; + vec = [algebra ofGrade:[blade grade]]; + *[vec componentFor:blade] = num; + } else { + var_t *var = Hash_Find (symtab, token_str); + if (!var) { + syntax_error (); + } + vec = var.value; + } + advance (); + } else { + syntax_error (); + vec = nil; + } + return vec; +} + +static MultiVector * +high_term () +{ + MultiVector *vec = nil; + SEL op = nil; + while (1) { + if (vec) { + vec = [vec performSelector:op withObject: factor()]; + } else { + vec = factor (); + } + if (match (WEDGE)) { + op = @selector(wedge:); + advance (); + } else if (match (DOT)) { + op = @selector(dot:); + advance (); + } else { + return vec; + } + } +} + +static MultiVector * +term () +{ + MultiVector *vec = nil; + SEL op = nil; + while (1) { + if (vec) { + vec = [vec performSelector:op withObject: high_term()]; + } else { + vec = high_term (); + } + if (match (REVERSE) || match (DUAL) || match (OPENP) || match (ID)) { + op = @selector(product:); + } else if (match (DIV)) { + op = @selector(divide:); + advance (); + } else { + return vec; + } + } +} + +static MultiVector * +expression () +{ + MultiVector *vec = nil; + SEL op = nil; + while (1) { + if (vec) { + vec = [vec performSelector:op withObject: term()]; + } else { + vec = term (); + } + if (match (PLUS)) { + op = @selector(plus:); + advance (); + } else if (match (MINUS)) { + op = @selector(minus:); + advance (); + } else { + return vec; + } + } +} + +static var_t * +assignment () +{ + if (!match (ID) || is_digit (str_mid (token_str, 0, 1))) { + printf ("%s\n", token_str); + syntax_error (); + } + var_t *var = Hash_Find (symtab, token_str); + if (!var) { + syntax_error (); + } + advance (); + if (!match (EQUAL)) { + syntax_error (); + } + advance (); + var.value = [expression () retain]; + return var; +} + +static var_t * +declaration () +{ + if (!match (ID) || is_digit (str_mid (token_str, 0, 1))) { + syntax_error (); + } + if (Hash_Find (symtab, token_str)) { + syntax_error (); + } + var_t *var = obj_malloc (sizeof (var_t)); + var.name = str_hold (str_unmutable (token_str)); + Hash_Add (symtab, var); + assignment (); + return var; +} + +static int +parse_script (string name, QFile file) +{ + script = Script_New (); + Script_SetSingle (script, "()[]{}/+-^&~=;!"); + token_str = Script_FromFile (script, name, file); + + while (!match (EOF)) { + arp_end (); + arp_start (); + var_t *var; + if (match (VAR)) { + advance (); + var = declaration (); + printf ("var %s = %@\n", var.name, var.value); + } else { + var = assignment (); + printf ("%s = %@\n", var.name, var.value); + } + advance (); + } + + Script_Delete (script); + return 1; +} + +static string +get_symtab_key (void *var, void *unused) +{ + return ((var_t *) var).name; +} + +static Algebra * +parse_algebra (string spec) +{ + ivec3 R = {}; + string s = spec; + if (is_digit (str_mid (spec, 0, 1))) { + for (int i = 0; i < 3; i++) { + int end = 0; + R[i] = strtol (s, &end, 0); + string e = str_mid (s, end, end + 1); + if (!e) { + break; + } + if (e != ",") { + goto bad_spec; + } + s = str_mid (s, end + 1); + } + if (!R[0] && !R[1] && !R[2]) { + goto bad_spec; + } + return [Algebra R:R[0], R[1], R[2]]; + } else { + } +bad_spec: + printf ("bad algebra spec: %s\n", spec); + return nil; +} + +int +main (int argc, string *argv) +{ + symtab = Hash_NewTable (127, get_symtab_key, nil, nil); + if (argc < 2) { + basic_test (); + } else { + arp_start (); + algebra = [[Algebra PGA:3] retain]; + arp_end (); + arp_start (); + double m1 = -1; + minus_one = [[algebra ofGrade:0 values:&m1] retain]; + arp_end (); + arp_start (); + for (int i = 1; i < argc; i++) { + if (argv[i] == "-a") { + Algebra *a = [parse_algebra (argv[++i]) retain]; + if (!a) { + return 1; + } + [algebra release]; + algebra = a; + [minus_one release]; + minus_one = [[algebra ofGrade:0 values:&m1] retain]; + continue; + } + QFile file = Qopen (argv[i], "rt"); + if (file) { + arp_end (); + arp_start (); + printf ("Using algebra %@\n", algebra); + int res = parse_script (argv[i], file); + Qclose (file); + if (!res) { + return 1; + } + } else { + printf ("%s: failed to open '%s'\n", argv[0], argv[i]); + return 1; + } + } + } + return 0; +} diff --git a/ruamoko/gatest/metric.h b/ruamoko/gatest/metric.h new file mode 100644 index 000000000..357ccab18 --- /dev/null +++ b/ruamoko/gatest/metric.h @@ -0,0 +1,16 @@ +#ifndef __metric_h +#define __metric_h + +#include + +@interface Metric : Object +{ + unsigned plus; // mask of elements that square to +1 + unsigned minus; // mask of elements that square to -1 + unsigned zero; // mask of elements that square to 0 +} ++(Metric *)R:(int)p, int m, int z; +-(double)apply:(unsigned) a, unsigned b; +@end + +#endif//__metric_h diff --git a/ruamoko/gatest/metric.r b/ruamoko/gatest/metric.r new file mode 100644 index 000000000..ac9fd305f --- /dev/null +++ b/ruamoko/gatest/metric.r @@ -0,0 +1,37 @@ +#include "metric.h" + +@implementation Metric ++(Metric *)R:(int)p, int m, int z +{ + Metric *metric = [[[Metric alloc] init] autorelease]; + metric.plus = ((1 << p) - 1) << z; + metric.minus = ((1 << m) - 1) << (z + p); + metric.zero = (1 << z) - 1; + return metric; +} + +static double +count_minus (unsigned minus) +{ + double s = 1; + while (minus) { + if (minus & 1) { + s = -s; + } + minus >>= 1; + } + return s; +} + +-(double)apply:(unsigned) a, unsigned b +{ + // find all the squared elements + unsigned c = a & b; + // any elements that square to 0 result in 0 + if (c & zero) { + return 0; + } + return count_minus (c & minus); +} + +@end diff --git a/ruamoko/gatest/multivector.h b/ruamoko/gatest/multivector.h new file mode 100644 index 000000000..a93fc2c90 --- /dev/null +++ b/ruamoko/gatest/multivector.h @@ -0,0 +1,38 @@ +#ifndef __multivector_h +#define __multivector_h +#include + +@class Algebra; +@class BasisLayout; +@class BasisBlase; + +@interface MultiVector : Object +{ + double *components; + Algebra *algebra; + BasisLayout *layout; + int num_components; +} ++(MultiVector *) new:(Algebra *) algebra; +// NOTE: values must have the same layout as algebra ++(MultiVector *) new:(Algebra *) algebra values:(double *) values; ++(MultiVector *) new:(Algebra *) algebra group:(BasisGroup *) group; +// NOTE: values must have the same layout as group ++(MultiVector *) new:(Algebra *) algebra group:(BasisGroup *) group values:(double *) values; ++(MultiVector *) copy:(MultiVector *) src; + +-(double *) components; +-(int)indexFor:(unsigned)mask; +-(double *) componentFor:(BasisBlade *) blade; + +-(MultiVector *) product:(MultiVector *) rhs; +-(MultiVector *) divide:(MultiVector *) rhs; +-(MultiVector *) wedge:(MultiVector *) rhs; +-(MultiVector *) dot:(MultiVector *) rhs; +-(MultiVector *) plus:(MultiVector *) rhs; +-(MultiVector *) minus:(MultiVector *) rhs; +-(MultiVector *) dual; +-(MultiVector *) reverse; +@end + +#endif//__multivector_h diff --git a/ruamoko/gatest/multivector.r b/ruamoko/gatest/multivector.r new file mode 100644 index 000000000..976879ca4 --- /dev/null +++ b/ruamoko/gatest/multivector.r @@ -0,0 +1,293 @@ +#include +#include "algebra.h" +#include "basisblade.h" +#include "basislayout.h" +#include "multivector.h" +#include "util.h" + +@implementation MultiVector +static MultiVector *new_mv (Algebra *algebra, BasisLayout *layout) +{ + MultiVector *mv = [[[MultiVector alloc] init] autorelease]; + mv.algebra = [algebra retain]; + layout = layout ? layout : [algebra layout]; + mv.layout = [layout retain]; + mv.num_components = [layout num_components]; + mv.components = obj_malloc (mv.num_components * sizeof (double)); + return mv; +} + ++(MultiVector *) new:(Algebra *) algebra +{ + MultiVector *mv = new_mv (algebra, nil); + for (int i = 0; i < mv.num_components; i++) { + mv.components[i] = 0; + } + return mv; +} + ++(MultiVector *) new:(Algebra *) algebra values:(double *) values +{ + MultiVector *mv = new_mv (algebra, nil); + for (int i = 0; i < mv.num_components; i++) { + mv.components[i] = values[i]; + } + return mv; +} + ++(MultiVector *) new:(Algebra *) algebra group:(BasisGroup *) group +{ + BasisLayout *layout = [BasisLayout new:1 groups:&group]; + MultiVector *mv = new_mv (algebra, layout); + for (int i = 0; i < mv.num_components; i++) { + mv.components[i] = 0; + } + return mv; +} + ++(MultiVector *) new:(Algebra *) algebra group:(BasisGroup *) group values:(double *) values +{ + BasisLayout *layout = [BasisLayout new:1 groups:&group]; + MultiVector *mv = new_mv (algebra, layout); + for (int i = 0; i < mv.num_components; i++) { + mv.components[i] = values[i]; + } + return mv; +} + ++(MultiVector *) copy:(MultiVector *) src +{ + MultiVector *mv = new_mv (src.algebra, src.layout); + for (int i = 0; i < mv.num_components; i++) { + mv.components[i] = src.components[i]; + } + return mv; +} + +-(void)dealloc +{ + [algebra release]; + [layout release]; + obj_free (components); + [super dealloc]; +} + +-(string)describe +{ + string str = nil; + + for (int i = 0; i < num_components; i++) { + if (!components[i]) { + continue; + } + BasisBlade *b = [layout bladeAt:i]; + str = sprintf ("%s%s%g%s", str, str ? " + " : "", components[i], [b name]); + } + if (!str) { + str = "0"; + } + return str; +} + +-(double *) components +{ + return components; +} + +-(int)indexFor:(unsigned)mask +{ + return [layout bladeIndex:mask]; +} + +-(double *) componentFor:(BasisBlade *) blade +{ + return &components[[layout bladeIndex:[blade mask]]]; +} + +-(MultiVector *) product:(MultiVector *) rhs +{ + MultiVector *prod = [MultiVector new:algebra]; + for (int i = 0; i < num_components; i++) { + if (!components[i]) { + continue; + } + double lc = components[i]; + BasisBlade *lb = [layout bladeAt:i]; + for (int j = 0; j < rhs.num_components; j++) { + if (!rhs.components[j]) { + continue; + } + double rc = rhs.components[j]; + BasisBlade *rb = [rhs.layout bladeAt:j]; + BasisBlade *b = [lb geometricProduct:rb metric:[algebra metric]]; + double s = [b scale]; + if (!s) { + continue; + } + int ind = [prod.layout bladeIndex:[b mask]]; + prod.components[ind] += s * lc * rc; + } + } + return prod; +} + +-(MultiVector *) divide:(MultiVector *) rhs +{ + MultiVector *sqr = [rhs product:rhs]; + double smag = sqr.components[[sqr indexFor:0]]; + MultiVector *div = [self product:rhs]; + for (int i = 0; i < div.num_components; i++) { + div.components[i] /= smag; + } + return div; +} + +-(MultiVector *) wedge:(MultiVector *) rhs +{ + MultiVector *prod = [MultiVector new:algebra]; + for (int i = 0; i < num_components; i++) { + if (!components[i]) { + continue; + } + double lc = components[i]; + BasisBlade *lb = [layout bladeAt:i]; + for (int j = 0; j < rhs.num_components; j++) { + if (!rhs.components[j]) { + continue; + } + double rc = rhs.components[j]; + BasisBlade *rb = [rhs.layout bladeAt:j]; + BasisBlade *b = [lb outerProduct:rb]; + double s = [b scale]; + if (!s) { + continue; + } + int ind = [prod.layout bladeIndex:[b mask]]; + prod.components[ind] += s * lc * rc; + } + } + return prod; +} + +-(MultiVector *) dot:(MultiVector *) rhs +{ + MultiVector *prod = [MultiVector new:algebra]; + for (int i = 0; i < num_components; i++) { + if (!components[i]) { + continue; + } + double lc = components[i]; + BasisBlade *lb = [layout bladeAt:i]; + int lg = [lb grade]; + for (int j = 0; j < rhs.num_components; j++) { + if (!rhs.components[j]) { + continue; + } + double rc = rhs.components[j]; + BasisBlade *rb = [rhs.layout bladeAt:j]; + int rg = [rb grade]; + BasisBlade *b = [lb geometricProduct:rb metric:[algebra metric]]; + int g = [b grade]; + if ((lg <= rg) && (g != rg - lg)) { + continue; + } + if ((lg > rg) && (g != lg - rg)) { + continue; + } + double s = [b scale]; + if (!s) { + continue; + } + int ind = [prod.layout bladeIndex:[b mask]]; + prod.components[ind] += s * lc * rc; + } + } + return prod; +} + +-(MultiVector *) plus:(MultiVector *) rhs +{ + MultiVector *plus = [MultiVector new:algebra]; + for (int i = 0; i < num_components; i++) { + double c = components[i]; + if (!c) { + continue; + } + BasisBlade *b = [layout bladeAt:i]; + int ind = [plus.layout bladeIndex:[b mask]]; + plus.components[ind] += c; + } + for (int i = 0; i < rhs.num_components; i++) { + double c = rhs.components[i]; + if (!c) { + continue; + } + BasisBlade *b = [rhs.layout bladeAt:i]; + int ind = [plus.layout bladeIndex:[b mask]]; + plus.components[ind] += c; + } + return plus; +} + +-(MultiVector *) minus:(MultiVector *) rhs +{ + MultiVector *minus = [MultiVector new:algebra]; + for (int i = 0; i < num_components; i++) { + double c = components[i]; + if (!c) { + continue; + } + BasisBlade *b = [layout bladeAt:i]; + int ind = [minus.layout bladeIndex:[b mask]]; + minus.components[ind] += c; + } + for (int i = 0; i < rhs.num_components; i++) { + double c = rhs.components[i]; + if (!c) { + continue; + } + BasisBlade *b = [rhs.layout bladeAt:i]; + int ind = [minus.layout bladeIndex:[b mask]]; + minus.components[ind] -= c; + } + return minus; +} + +-(MultiVector *) dual +{ + MultiVector *dual = [MultiVector new:algebra]; + unsigned dual_mask = (1 << [algebra dimension]) - 1; + for (int i = 0; i < num_components; i++) { + if (!components[i]) { + continue; + } + double lc = components[i]; + BasisBlade *lb = [layout bladeAt:i]; + unsigned lb_mask = [lb mask]; + unsigned mask = lb_mask ^ dual_mask; + double ls = [lb scale]; + double s = count_flips (lb_mask, mask) & 1 ? -1 : 1; + int ind = [dual.layout bladeIndex:mask]; + dual.components[ind] += s * lc; + } + return dual; +} + +-(MultiVector *) reverse +{ + MultiVector *reverse = [MultiVector new:algebra]; + for (int i = 0; i < num_components; i++) { + if (!components[i]) { + continue; + } + double c = components[i]; + BasisBlade *b = [layout bladeAt:i]; + int g = [b grade]; + unsigned mask = [b mask]; + double s = g & 2 ? -1 : 1;//FIXME do in BasisBlade? + int ind = [layout bladeIndex:mask]; + reverse.components[ind] += s * c; + } + return reverse; +} +@end diff --git a/ruamoko/gatest/util.h b/ruamoko/gatest/util.h new file mode 100644 index 000000000..b80ca64db --- /dev/null +++ b/ruamoko/gatest/util.h @@ -0,0 +1,11 @@ +#ifndef __util_h +#define __util_h + +void printf(string fmt, ...); +void traceon(void); +void traceoff(void); +int *binomial (int n); +int count_bits (unsigned v); +int count_flips (unsigned a, unsigned b); + +#endif//__util_h diff --git a/ruamoko/gatest/util.r b/ruamoko/gatest/util.r new file mode 100644 index 000000000..a4716429d --- /dev/null +++ b/ruamoko/gatest/util.r @@ -0,0 +1,40 @@ +#include +#include "util.h" + +void printf(string fmt, ...) = #0; +void traceon(void) = #0; +void traceoff(void) = #0; + +int * +binomial (int n) +{ + int *coef = obj_malloc ((n + 1) * sizeof (int)); + int c = 1; + for (int i = 0; i < n + 1; i++) { + coef[i] = c; + c = (c * (n - i)) / (i + 1); + } + return coef; +} + +int +count_bits (unsigned v) +{ + int c = 0; + for (; v; c++) { + v &= v - 1u; //XXX bug in qfcc: just 1 results in extra temp + } + return c; +} + +int +count_flips (unsigned a, unsigned b) +{ + int c = 0; + a >>= 1; + while (a) { + c += count_bits (a & b); + a >>= 1; + } + return c; +} diff --git a/ruamoko/gui/Group.r b/ruamoko/gui/Group.r index 4bcccadf1..56b7303c7 100644 --- a/ruamoko/gui/Group.r +++ b/ruamoko/gui/Group.r @@ -1,6 +1,6 @@ -#include "gui/Group.h" -#include "gui/Point.h" -#include "Array.h" +#include +#include +#include @implementation Group diff --git a/ruamoko/gui/InputLine.r b/ruamoko/gui/InputLine.r index 781e56b78..e4fb8df8a 100644 --- a/ruamoko/gui/InputLine.r +++ b/ruamoko/gui/InputLine.r @@ -1,6 +1,6 @@ -#include "draw.h" -#include "gui/InputLine.h" -#include "gui/Rect.h" +#include +#include +#include inputline_t InputLine_Create (int lines, int size, int prompt) = #0; void InputLine_SetPos (inputline_t il, int x, int y) = #0; diff --git a/ruamoko/gui/Makefile.am b/ruamoko/gui/Makefile.am deleted file mode 100644 index e4866e856..000000000 --- a/ruamoko/gui/Makefile.am +++ /dev/null @@ -1,27 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -pkglibdir=$(datarootdir)/qfcc/lib - -QFCC=$(top_builddir)/tools/qfcc/source/qfcc$(EXEEXT) -QCFLAGS=-qq -O -g -Werror -Wall -Wno-integer-divide --no-default-paths -QCPPFLAGS=$(AM_CPPFLAGS) -PAK=$(top_builddir)/tools/pak/pak$(EXEEXT) -RANLIB=touch - -AM_CPPFLAGS= -I$(top_srcdir)/ruamoko/include -I$(top_srcdir)/include - -gui_libs=libgui.a -libs=$(gui_libs) - -pkglib_LIBRARIES= $(libs) -EXTRA_LIBRARIES= $(gui_libs) - -SUFFIXES= .qfo .r .qc -.r.o: - $(QFCC) $(QCFLAGS) $(QCPPFLAGS) -c -o $@ $< - -libgui_a_SOURCES= \ - Group.r InputLine.r Pic.r Point.r Rect.r Size.r Slider.r Text.r View.r -libgui_a_AR= $(PAK) -cf - -CLEANFILES= *.qfo *.o diff --git a/ruamoko/gui/Makemodule.am b/ruamoko/gui/Makemodule.am new file mode 100644 index 000000000..4f18772ac --- /dev/null +++ b/ruamoko/gui/Makemodule.am @@ -0,0 +1,25 @@ +ruamoko_gui_gui_libs=ruamoko/gui/libgui.a + +ruamoko_lib_LIBRARIES += $(ruamoko_gui_gui_libs) +EXTRA_LIBRARIES += $(ruamoko_gui_gui_libs) + +ruamoko_gui_libgui_a_SOURCES= \ + ruamoko/gui/Group.r \ + ruamoko/gui/InputLine.r \ + ruamoko/gui/Pic.r \ + ruamoko/gui/Point.r \ + ruamoko/gui/Rect.r \ + ruamoko/gui/Size.r \ + ruamoko/gui/Slider.r \ + ruamoko/gui/Text.r \ + ruamoko/gui/View.r +ruamoko_gui_libgui_a_dep=$(call qcautodep,$(ruamoko_gui_libgui_a_SOURCES)) +ruamoko_gui_libgui_a_AR= $(PAK) -cf +EXTRA_ruamoko_gui_libgui_a_DEPENDENCIES=$(PAK) +include $(ruamoko_gui_libgui_a_dep) # am--include-marker +r_depfiles_remade += $(ruamoko_gui_libgui_a_dep) + +CLEANFILES += \ + ruamoko/gui/*.dat \ + ruamoko/gui/*.sym +DISTCLEANFILES += $(ruamoko_gui_libgui_a_dep) diff --git a/ruamoko/gui/Pic.r b/ruamoko/gui/Pic.r index 89cf93950..748393cc5 100644 --- a/ruamoko/gui/Pic.r +++ b/ruamoko/gui/Pic.r @@ -1,6 +1,6 @@ -#include "draw.h" -#include "string.h" -#include "gui/Pic.h" +#include +#include +#include @implementation Pic -(id)init diff --git a/ruamoko/gui/Point.r b/ruamoko/gui/Point.r index c25c72000..91163087b 100644 --- a/ruamoko/gui/Point.r +++ b/ruamoko/gui/Point.r @@ -1,4 +1,4 @@ -#include "gui/Point.h" +#include Point makePoint (int x, int y) { diff --git a/ruamoko/gui/Rect.r b/ruamoko/gui/Rect.r index be1c69384..a7280cea3 100644 --- a/ruamoko/gui/Rect.r +++ b/ruamoko/gui/Rect.r @@ -1,7 +1,7 @@ -#include "Object.h" -#include "gui/Point.h" -#include "gui/Size.h" -#include "gui/Rect.h" +#include +#include +#include +#include Rect makeRect (int x, int y, int w, int h) { diff --git a/ruamoko/gui/Size.r b/ruamoko/gui/Size.r index d4565a33f..da1bf04a9 100644 --- a/ruamoko/gui/Size.r +++ b/ruamoko/gui/Size.r @@ -1,4 +1,4 @@ -#include "gui/Size.h" +#include Size makeSize (int width, int height) { diff --git a/ruamoko/gui/Slider.r b/ruamoko/gui/Slider.r index d43cdcf21..275a64de4 100644 --- a/ruamoko/gui/Slider.r +++ b/ruamoko/gui/Slider.r @@ -1,7 +1,7 @@ -#include "draw.h" +#include -#include "gui/Slider.h" -#include "gui/Rect.h" +#include +#include @implementation Slider diff --git a/ruamoko/gui/Text.r b/ruamoko/gui/Text.r index 731f661ce..6011273d8 100644 --- a/ruamoko/gui/Text.r +++ b/ruamoko/gui/Text.r @@ -1,6 +1,6 @@ -#include "gui/Text.h" -#include "string.h" -#include "draw.h" +#include +#include +#include @implementation Text - (id) init diff --git a/ruamoko/gui/View.r b/ruamoko/gui/View.r index 2accc4fe7..a83ebeb33 100644 --- a/ruamoko/gui/View.r +++ b/ruamoko/gui/View.r @@ -1,7 +1,7 @@ -#include "gui/Size.h" -#include "gui/Point.h" -#include "gui/Rect.h" -#include "gui/View.h" +#include +#include +#include +#include @implementation View diff --git a/ruamoko/include/Array.h b/ruamoko/include/Array.h index 8a9f91f36..cfc95f595 100644 --- a/ruamoko/include/Array.h +++ b/ruamoko/include/Array.h @@ -1,8 +1,8 @@ #ifndef __ruamoko_Array_h #define __ruamoko_Array_h -#include "Object.h" -#include "runtime.h" +#include +#include /** The Array class is a general ordered collection class. @@ -271,7 +271,16 @@ Iteratively sends #performSelector:withObject: to each contained object. */ - (void) makeObjectsPerformSelector: (SEL)selector - withObject: (id)arg; + withObject: (void *)arg; +//\} + +/** + Iteratively sends #performSelector:withObject:withObject: to each + contained object. +*/ +- (void) makeObjectsPerformSelector: (SEL)selector + withObject: (void *)arg + withObject: (void *)arg2; //\} @end diff --git a/ruamoko/include/AutoreleasePool.h b/ruamoko/include/AutoreleasePool.h index a02600c9e..c8202e0f0 100644 --- a/ruamoko/include/AutoreleasePool.h +++ b/ruamoko/include/AutoreleasePool.h @@ -1,7 +1,7 @@ #ifndef __ruamoko_AutoreleasePool_h #define __ruamoko_AutoreleasePool_h -#include "Object.h" +#include @class Array; diff --git a/ruamoko/include/Entity.h b/ruamoko/include/Entity.h index 85f304efb..a870e987f 100644 --- a/ruamoko/include/Entity.h +++ b/ruamoko/include/Entity.h @@ -30,14 +30,16 @@ #ifndef __ruamoko_Entity_h #define __ruamoko_Entity_h -#include "Object.h" +#include @interface Entity: Object { + int own; @public - entity ent; - int own; + entity ent; } ++(Entity *) spawn; ++(Entity *) withEntity: (entity) e; - (id) init; - (id) initWithEntity: (entity) e; diff --git a/ruamoko/include/Makefile.am b/ruamoko/include/Makefile.am deleted file mode 100644 index d978b3a70..000000000 --- a/ruamoko/include/Makefile.am +++ /dev/null @@ -1,15 +0,0 @@ -AUTOMAKE_OPTIONS= foreign -pkgincludedir= $(datarootdir)/qfcc/include -nobase_pkginclude_HEADERS= \ - crudefile.h debug.h entities.h infokey.h math.h message.h nq_message.h \ - physics.h msgbuf.h qfile.h qfs.h qw_message.h qw_physics.h qw_sys.h \ - server.h sound.h script.h string.h sv_sound.h system.h \ - \ - draw.h key.h \ - \ - cbuf.h cmd.h cvar.h file.h gib.h hash.h plist.h runtime.h \ - Object.h Protocol.h \ - AutoreleasePool.h Array.h Entity.h PropertyList.h Set.h \ - \ - gui/Group.h gui/InputLine.h gui/Pic.h gui/Point.h gui/Rect.h gui/Size.h \ - gui/Slider.h gui/Text.h gui/View.h diff --git a/ruamoko/include/Makemodule.am b/ruamoko/include/Makemodule.am new file mode 100644 index 000000000..8d19564d0 --- /dev/null +++ b/ruamoko/include/Makemodule.am @@ -0,0 +1,62 @@ +ruamoko_include = \ + ruamoko/include/crudefile.h \ + ruamoko/include/debug.h \ + ruamoko/include/entities.h \ + ruamoko/include/infokey.h \ + ruamoko/include/input.h \ + ruamoko/include/math.h \ + ruamoko/include/mersenne.h \ + ruamoko/include/message.h \ + ruamoko/include/nq_message.h \ + ruamoko/include/physics.h \ + ruamoko/include/msgbuf.h \ + ruamoko/include/qfile.h \ + ruamoko/include/qfs.h \ + ruamoko/include/qw_message.h \ + ruamoko/include/qw_physics.h \ + ruamoko/include/qw_sys.h \ + ruamoko/include/server.h \ + ruamoko/include/scene.h \ + ruamoko/include/script.h \ + ruamoko/include/sound.h \ + ruamoko/include/stdlib.h \ + ruamoko/include/string.h \ + ruamoko/include/sv_sound.h \ + ruamoko/include/system.h \ + ruamoko/include/types.h \ + ruamoko/include/legacy_string.h \ + ruamoko/include/draw.h \ + ruamoko/include/cbuf.h \ + ruamoko/include/cmd.h \ + ruamoko/include/cvar.h \ + ruamoko/include/gib.h \ + ruamoko/include/hash.h \ + ruamoko/include/plist.h \ + ruamoko/include/runtime.h \ + ruamoko/include/Object.h \ + ruamoko/include/Protocol.h \ + ruamoko/include/AutoreleasePool.h \ + ruamoko/include/Array.h \ + ruamoko/include/Entity.h \ + ruamoko/include/PropertyList.h \ + ruamoko/include/Set.h + +ruamoko_gui_include = \ + ruamoko/include/gui/Group.h \ + ruamoko/include/gui/InputLine.h \ + ruamoko/include/gui/Pic.h \ + ruamoko/include/gui/Point.h \ + ruamoko/include/gui/Rect.h \ + ruamoko/include/gui/Size.h \ + ruamoko/include/gui/Slider.h \ + ruamoko/include/gui/Text.h \ + ruamoko/include/gui/View.h + +ruamoko_includedir = $(datarootdir)/qfcc/include +ruamoko_gui_includedir = $(datarootdir)/qfcc/include/gui + +ruamoko_include_HEADERS = $(ruamoko_include) +ruamoko_gui_include_HEADERS = $(ruamoko_gui_include) + +EXTRA_DIST += \ + ruamoko/include/key.h diff --git a/ruamoko/include/Object.h b/ruamoko/include/Object.h index 23623ef06..981977651 100644 --- a/ruamoko/include/Object.h +++ b/ruamoko/include/Object.h @@ -1,7 +1,7 @@ #ifndef __ruamoko_Object_h #define __ruamoko_Object_h -#include "runtime.h" +#include @class Protocol; @@ -20,10 +20,14 @@ - (id) performSelector: (SEL)aSelector; - (id) performSelector: (SEL)aSelector - withObject: (id)anObject; + withObject: (void *)anObject; - (id) performSelector: (SEL)aSelector - withObject: (id)anObject - withObject: (id)anotherObject; + withObject: (void *)anObject + withObject: (void *)anotherObject; +// void return does not touch the actual return value (effectively retval) +// so if the target returns a value, and the forwarding method simply returns +// (and is void), the vallue will get out to the caller +- (void) performv: (SEL) sel : (@va_list) args; - (BOOL) respondsToSelector: (SEL)aSelector; - (BOOL) conformsToProtocol: (Protocol *)aProtocol; diff --git a/ruamoko/include/PropertyList.h b/ruamoko/include/PropertyList.h index 0f255cdfe..a2cca1fc6 100644 --- a/ruamoko/include/PropertyList.h +++ b/ruamoko/include/PropertyList.h @@ -1,13 +1,35 @@ #ifndef __ruamoko_PropertyList_h #define __ruamoko_PropertyList_h -#include "plist.h" -#include "Object.h" +#include +#include -@interface PLItem: Object +@class PLItem; + +@protocol PLDictionary +- (int) count; +- (int) numKeys; +- (PLItem *) getObjectForKey:(string) key; +- (PLItem *) allKeys; +- (string) keyAtIndex:(int) index; +- addKey:(string) key value:(PLItem *) value; +@end + +@protocol PLArray +- (int) count; +- (int) numObjects; +- (PLItem *) getObjectAtIndex:(int) index; +- addObject:(PLItem *) object; +- insertObject:(PLItem *) object atIndex:(int) index; +@end + +@protocol PLString +- (string) string; +@end + +@interface PLItem: Object { - plitem_t item; - int own; + plitem_t *item; } + (PLItem *) newDictionary; + (PLItem *) newArray; @@ -16,40 +38,31 @@ + (PLItem *) fromString:(string) str; + (PLItem *) fromFile:(QFile) file; -- initWithItem:(plitem_t) item; -- initWithOwnItem:(plitem_t) item; +- initWithItem:(plitem_t *) item; +- (plitem_t *) item; - (string) write; - (pltype_t) type; +- (int) line; @end -@interface PLDictionary: PLItem +@interface PLDictionary: PLItem + (PLDictionary *) new; - -- (int) count; -- (int) numKeys; -- (PLItem *) getObjectForKey:(string) key; -- (PLItem *) allKeys; -- addKey:(string) key value:(PLItem *) value; ++ (PLItem *) fromString:(string) str; ++ (PLItem *) fromFile:(QFile) file; @end -@interface PLArray: PLItem +@interface PLArray: PLItem + (PLArray *) new; - -- (int) count; -- (int) numObjects; -- (PLItem *) getObjectAtIndex:(int) index; -- addObject:(PLItem *) object; -- insertObject:(PLItem *) object atIndex:(int) index; ++ (PLItem *) fromString:(string) str; ++ (PLItem *) fromFile:(QFile) file; @end @interface PLData: PLItem + (PLData *) new:(void*) data size:(int) len; @end -@interface PLString: PLItem +@interface PLString: PLItem + (PLString *) new:(string) str; - -- (string) string; @end #endif//__ruamoko_PropertyList_h diff --git a/ruamoko/include/Protocol.h b/ruamoko/include/Protocol.h index 2894ee4fa..3d377ab75 100644 --- a/ruamoko/include/Protocol.h +++ b/ruamoko/include/Protocol.h @@ -1,7 +1,7 @@ #ifndef __ruamoko_Protocol_h #define __ruamoko_Protocol_h -#include "Object.h" +#include struct obj_method_description { string name; diff --git a/ruamoko/include/Set.h b/ruamoko/include/Set.h index 24a772565..2e020755b 100644 --- a/ruamoko/include/Set.h +++ b/ruamoko/include/Set.h @@ -1,8 +1,8 @@ #ifndef __ruamoko_Set_h #define __ruamoko_Set_h -typedef struct set_s *set_t; -typedef struct set_iter_s *set_iter_t; +typedef struct set_s set_t; +typedef struct set_iter_s set_iter_t; @extern void set_del_iter (set_iter_t *set_iter); @extern unsigned set_iter_element (set_iter_t *set_iter); @@ -25,15 +25,18 @@ typedef struct set_iter_s *set_iter_t; @extern int set_is_equivalent (set_t *s1, set_t *s2); @extern int set_is_subset (set_t *set, set_t *sub); @extern int set_is_member (set_t *set, unsigned x); -@extern unsigned set_size (set_t *set); +@extern unsigned set_count (set_t *set); @extern set_iter_t *set_first (set_t *set); @extern set_iter_t *set_next (set_iter_t *set_iter); @extern string set_as_string (set_t *set); #include +@class Set; + @interface SetIterator: Object { + Set *set; set_iter_t *iter; } - (SetIterator *) next; diff --git a/ruamoko/include/cbuf.h b/ruamoko/include/cbuf.h index 1a2ef142f..fa46a5efe 100644 --- a/ruamoko/include/cbuf.h +++ b/ruamoko/include/cbuf.h @@ -1,9 +1,11 @@ #ifndef __ruamoko_cbuf_h #define __ruamoko_cbuf_h -@extern void Cbuf_AddText (string text); -@extern void Cbuf_InsertText (string text); -@extern void Cbuf_Execute (void); -@extern void Cbuf_Execute_Sets (void); +typedef @handle cbuf_h cbuf_t; + +@extern void Cbuf_AddText (cbuf_t cbuf, string text); +@extern void Cbuf_InsertText (cbuf_t cbuf, string text); +@extern void Cbuf_Execute (cbuf_t cbuf); +@extern void Cbuf_Execute_Sets (cbuf_t cbuf); #endif//__ruamoko_cbuf_h diff --git a/ruamoko/include/debug.h b/ruamoko/include/debug.h index 017ca1858..64e06eb5d 100644 --- a/ruamoko/include/debug.h +++ b/ruamoko/include/debug.h @@ -67,13 +67,13 @@ /** Abort (crash) the server. "str" is the message the server crashes with. */ -@extern void error (string str); +@extern void error (.../*string str*/); /** Prints info on the "self" ENTITY (not object), and error message "e". The entity is freed. */ -@extern void objerror (string e); +@extern void objerror (.../*string e*/); //\} #endif //__ruamoko_debug_h diff --git a/ruamoko/include/draw.h b/ruamoko/include/draw.h index df4a1470f..a5369375e 100644 --- a/ruamoko/include/draw.h +++ b/ruamoko/include/draw.h @@ -1,7 +1,7 @@ #ifndef __ruamoko_draw_h #define __ruamoko_draw_h -#include "Object.h" +#include struct _qpic_t { int width; @@ -10,6 +10,9 @@ struct _qpic_t { }; typedef struct _qpic_t *qpic_t; +@extern int Draw_Width (void); +@extern int Draw_Height (void); + @extern void Draw_FreePic (qpic_t pic); @extern qpic_t Draw_MakePic (int width, int heiight, string data); @extern qpic_t Draw_CachePic (string name, int alpha); @@ -24,6 +27,7 @@ typedef struct _qpic_t *qpic_t; @extern void Draw_nString (int x, int y, string text, int n); @extern void Draw_AltString (int x, int y, string text); @extern void Draw_Fill (int x, int y, int w, int h, int c); +@extern void Draw_Line (int x0, int y0, int x1, int y1, int c); @extern void Draw_Crosshair (int ch, int x, int y); @extern void text_box (int x, int y, int width, int lines); diff --git a/ruamoko/include/entities.h b/ruamoko/include/entities.h index 117f7d309..2bff9d984 100644 --- a/ruamoko/include/entities.h +++ b/ruamoko/include/entities.h @@ -101,7 +101,7 @@ This function must be called multiple times to get multiple results. Stupid, but functional. */ -@extern entity find (entity start, ...); +@extern @attribute(no_va_list) entity find (entity start, ...); #endif /** diff --git a/ruamoko/include/file.h b/ruamoko/include/file.h deleted file mode 100644 index f1c3a580b..000000000 --- a/ruamoko/include/file.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __ruamoko_file_h -#define __ruamoko_file_h - -#include "qfile.h" - -@extern QFile File_Open (string path, string mode); - -#endif//__ruamoko_file_h diff --git a/ruamoko/include/gui/Group.h b/ruamoko/include/gui/Group.h index b9ea99386..ccd4baa65 100644 --- a/ruamoko/include/gui/Group.h +++ b/ruamoko/include/gui/Group.h @@ -1,10 +1,10 @@ #ifndef __ruamoko_gui_Group_h #define __ruamoko_gui_Group_h -#include "View.h" +#include /** \addtogroup gui */ -//@{ +///@{ @class Array; @@ -41,6 +41,6 @@ - (id) addViews: (Array*)viewlist; @end -//@} +///@} #endif//__ruamoko_gui_Group_h diff --git a/ruamoko/include/gui/InputLine.h b/ruamoko/include/gui/InputLine.h index 692562a08..c0b14cfb4 100644 --- a/ruamoko/include/gui/InputLine.h +++ b/ruamoko/include/gui/InputLine.h @@ -1,14 +1,14 @@ #ifndef __ruamoko_gui_InputLine_h #define __ruamoko_gui_InputLine_h -#include "View.h" +#include /** \defgroup inputline Low level intputline interface. \ingroup gui Interface functions to the engine implementation. */ -//@{ +///@{ /** Opaque handle to an inputline. @@ -118,10 +118,10 @@ typedef void (il_enterfunc)(string, void*); \return The current text of the intputline. */ @extern string InputLine_GetText (inputline_t il); -//@} +///@} /** \addtogroup gui */ -//@{ +///@{ /** Class representation of the low-level inputline objects. */ @@ -252,6 +252,6 @@ typedef void (il_enterfunc)(string, void*); - (string) text; @end -//@} +///@} #endif //__ruamoko_gui_InputLine_h diff --git a/ruamoko/include/gui/Pic.h b/ruamoko/include/gui/Pic.h index f19e8b6e4..9bfe29fbc 100644 --- a/ruamoko/include/gui/Pic.h +++ b/ruamoko/include/gui/Pic.h @@ -1,10 +1,10 @@ #ifndef __ruamoko_gui_Pic_h #define __ruamoko_gui_Pic_h -#include "gui/View.h" +#include /** \addtogroup gui */ -//@{ +///@{ @interface Pic : View { @@ -18,6 +18,6 @@ -(void)draw; @end -//@} +///@} #endif//__ruamoko_gui_Pic_h diff --git a/ruamoko/include/gui/Point.h b/ruamoko/include/gui/Point.h index 4ac36391a..9de4d7f5e 100644 --- a/ruamoko/include/gui/Point.h +++ b/ruamoko/include/gui/Point.h @@ -2,7 +2,7 @@ #define __ruamoko_gui_Point_h /** \addtogroup gui */ -//@{ +///@{ struct Point { int x; @@ -15,6 +15,6 @@ typedef struct Point Point; @extern Point addPoint (Point a, Point b); @extern Point subtractPoint (Point a, Point b); -//@} +///@} #endif //__ruamoko_gui_Point_h diff --git a/ruamoko/include/gui/Rect.h b/ruamoko/include/gui/Rect.h index 145815d66..d1432f460 100644 --- a/ruamoko/include/gui/Rect.h +++ b/ruamoko/include/gui/Rect.h @@ -1,11 +1,11 @@ #ifndef __ruamoko_gui_Rect_h #define __ruamoko_gui_Rect_h -#include "gui/Point.h" -#include "gui/Size.h" +#include +#include /** \addtogroup gui */ -//@{ +///@{ struct Rect { Point origin; @@ -30,6 +30,6 @@ typedef struct Rect Rect; - (Rect) offsetBySize: (Size)aSize; #endif -//@} +///@} #endif //__ruamoko_gui_Rect_h diff --git a/ruamoko/include/gui/Size.h b/ruamoko/include/gui/Size.h index 0f6b387a3..dd48c6259 100644 --- a/ruamoko/include/gui/Size.h +++ b/ruamoko/include/gui/Size.h @@ -2,7 +2,7 @@ #define __ruamoko_gui_Size_h /** \addtogroup gui */ -//@{ +///@{ struct Size { int width; @@ -15,6 +15,6 @@ typedef struct Size Size; @extern Size addSize (Size a, Size b); @extern Size subtractSize (Size a, Size b); -//@} +///@} #endif //__ruamoko_gui_Size_h diff --git a/ruamoko/include/gui/Slider.h b/ruamoko/include/gui/Slider.h index 13a68f492..99840af7e 100644 --- a/ruamoko/include/gui/Slider.h +++ b/ruamoko/include/gui/Slider.h @@ -1,10 +1,10 @@ #ifndef __ruamoko_gui_Slider_h #define __ruamoko_gui_Slider_h -#include "View.h" +#include /** \addtogroup gui */ -//@{ +///@{ @interface Slider: View { @@ -19,6 +19,6 @@ @end -//@} +///@} #endif //__ruamoko_gui_Slider_h diff --git a/ruamoko/include/gui/Text.h b/ruamoko/include/gui/Text.h index d9c6a1311..ef389bf9c 100644 --- a/ruamoko/include/gui/Text.h +++ b/ruamoko/include/gui/Text.h @@ -1,10 +1,10 @@ #ifndef __ruamoko_gui_Text_h #define __ruamoko_gui_Text_h -#include "View.h" +#include /** \addtogroup gui */ -//@{ +///@{ @interface Text: View { @@ -18,6 +18,6 @@ - (void) draw; @end -//@} +///@} #endif //__ruamoko_gui_Text_h diff --git a/ruamoko/include/gui/View.h b/ruamoko/include/gui/View.h index a84f05e06..3cb51ee67 100644 --- a/ruamoko/include/gui/View.h +++ b/ruamoko/include/gui/View.h @@ -1,14 +1,14 @@ #ifndef __ruamoko_gui_View_h #define __ruamoko_gui_View_h -#include "Object.h" -#include "gui/Rect.h" +#include +#include /** \defgroup gui GUI goo for gooey chewing */ /** \addtogroup gui */ -//@{ +///@{ /** The View class. */ @@ -34,6 +34,6 @@ - (int) keyEvent:(int)key unicode:(int)unicode down:(int)down; @end -//@} +///@} #endif //__ruamoko_gui_View_h diff --git a/ruamoko/include/hash.h b/ruamoko/include/hash.h index 8c07169aa..2d0aa80fc 100644 --- a/ruamoko/include/hash.h +++ b/ruamoko/include/hash.h @@ -1,24 +1,24 @@ #ifndef __ruamoko_hash_h #define __ruamoko_hash_h -typedef struct _hashtab_t *hashtab_t; +typedef struct _hashtab_t hashtab_t; -@extern hashtab_t Hash_NewTable (int size, string gk (void *ele, void *data), void f (void *ele, void *data), void *ud); -@extern void Hash_SetHashCompare (hashtab_t tab, unsigned gh (void *ele, void *data), int cmp (void *ele1, void *ele2, void *data)); -@extern void Hash_DelTable (hashtab_t tab); -@extern void Hash_FlushTable (hashtab_t tab); -@extern int Hash_Add (hashtab_t tab, void *ele); -@extern int Hash_AddElement (hashtab_t tab, void *ele); -@extern void *Hash_Find (hashtab_t tab, string key); -@extern void *Hash_FindElement (hashtab_t tab, void *ele); -@extern void **Hash_FindList (hashtab_t tab, string key); -@extern void **Hash_FindElementList (hashtab_t tab, void *ele); -@extern void *Hash_Del (hashtab_t tab, string key); -@extern void *Hash_DelElement (hashtab_t tab, void *ele); -@extern void Hash_Free (hashtab_t tab, void *ele); +@extern hashtab_t *Hash_NewTable (int size, string gk (void *ele, void *data), void f (void *ele, void *data), void *ud); +@extern void Hash_SetHashCompare (hashtab_t *tab, unsigned gh (void *ele, void *data), int cmp (void *ele1, void *ele2, void *data)); +@extern void Hash_DelTable (hashtab_t *tab); +@extern void Hash_FlushTable (hashtab_t *tab); +@extern int Hash_Add (hashtab_t *tab, void *ele); +@extern int Hash_AddElement (hashtab_t *tab, void *ele); +@extern void *Hash_Find (hashtab_t *tab, string key); +@extern void *Hash_FindElement (hashtab_t *tab, void *ele); +@extern void **Hash_FindList (hashtab_t *tab, string key); +@extern void **Hash_FindElementList (hashtab_t *tab, void *ele); +@extern void *Hash_Del (hashtab_t *tab, string key); +@extern void *Hash_DelElement (hashtab_t *tab, void *ele); +@extern void Hash_Free (hashtab_t *tab, void *ele); @extern int Hash_String (string str); @extern int Hash_Buffer (void *buf, int len); -@extern void **Hash_GetList (hashtab_t tab); -@extern void Hash_Stats (hashtab_t tab); +@extern void **Hash_GetList (hashtab_t *tab); +@extern void Hash_Stats (hashtab_t *tab); #endif // __ruamoko_hash_h diff --git a/ruamoko/include/input.h b/ruamoko/include/input.h new file mode 100644 index 000000000..511d43e4e --- /dev/null +++ b/ruamoko/include/input.h @@ -0,0 +1,45 @@ +#ifndef __ruamoko_input_h +#define __ruamoko_input_h + +#include + +struct plitem_s; +void IN_LoadConfig (struct plitem_s *config); +in_button_t *IN_CreateButton (string name, string description); +in_axis_t *IN_CreateAxis (string name, string description); +int IN_FindDeviceId (string _id); +string IN_GetDeviceName (int devid); +string IN_GetDeviceId (int devid); +//IN_AxisInfo (); +//IN_ButtonInfo (); +string IN_GetAxisName (int devid, int axis); +string IN_GetButtonName (int devid, int button); +int IN_GetAxisNumber (int devid, string axis); +int IN_GetButtonNumber (int devid, string button); +void IN_ProcessEvents (void); +void IN_ClearStates (void); +int IN_GetAxisInfo (int devid, int axis, in_axisinfo_t *info); +int IN_GetButtonInfo (int devid, int button, in_buttoninfo_t *info); +typedef void (*button_listener_t) (void *data, in_button_t *button);//FIXME const +@overload void IN_ButtonAddListener (in_button_t *button, + button_listener_t listener, void *data); +@overload void IN_ButtonRemoveListener (in_button_t *button, + button_listener_t listener, + void *data); +typedef void (*axis_listener_t) (void *data, in_axis_t *axis);//FIXME const +@overload void IN_AxisAddListener (in_axis_t *axis, axis_listener_t listener, + void *data); +@overload void IN_AxisRemoveListener (in_axis_t *axis, + axis_listener_t listener, void *data); +@overload void IN_ButtonAddListener (in_button_t *button, IMP listener, + id obj); +@overload void IN_ButtonRemoveListener (in_button_t *button, IMP listener, + id obj); +@overload void IN_AxisAddListener (in_axis_t *axis, IMP listener, id obj); +@overload void IN_AxisRemoveListener (in_axis_t *axis, IMP listener, id obj); + +int IMT_CreateContext (string name); +int IMT_GetContext (void); +void IMT_SetContext (int ctx); + +#endif//__ruamoko_input_h diff --git a/ruamoko/include/key.h b/ruamoko/include/key.h index bc7d04801..f3355b3d2 100644 --- a/ruamoko/include/key.h +++ b/ruamoko/include/key.h @@ -1,7 +1,7 @@ #ifndef __ruamoko_key_h #define __ruamoko_key_h -#include "QF/keys.h" +#include @extern int Key_keydown (int keynum); @extern string Key_SetBinding (string imt, int keynum, string binding); diff --git a/ruamoko/include/legacy_string.h b/ruamoko/include/legacy_string.h new file mode 100644 index 000000000..34e47817a --- /dev/null +++ b/ruamoko/include/legacy_string.h @@ -0,0 +1,12 @@ +#ifndef __ruamoko_legacy_string_h +#define __ruamoko_legacy_string_h + +@extern string ftos (float f); +@extern string vtos (vector v); +@extern float stof (string s); +@extern float charcount (string goal, string s); +@extern string itos (int i); +@extern int stoi (string s); +@extern vector stov (string s); + +#endif//__ruamoko_legacy_string_h diff --git a/ruamoko/include/math.h b/ruamoko/include/math.h index fad67a9de..5c3978b1c 100644 --- a/ruamoko/include/math.h +++ b/ruamoko/include/math.h @@ -60,50 +60,65 @@ /** Returns \a f, rounded down to the next lower integer */ -@extern float floor (float f); +@extern @overload float floor (float f); +@extern @overload double floor (double f); /** Returns \a f, rounded up to the next highest integer */ -@extern float ceil (float f); +@extern @overload float ceil (float f); +@extern @overload double ceil (double f); /** Returns the absolute value of \a f */ -@extern float fabs (float f); +@extern @overload float fabs (float f); +@extern @overload double fabs (double f); //\} ///\name Exponentials and Logarithms //\{ +/** + Returns e to the power of \a x. +*/ +@extern @overload float exp (float x); +@extern @overload double exp (double x); + /** Returns the natural log of \a x. */ -@extern float log (float x); +@extern @overload float log (float x); +@extern @overload double log (double x); /** Returns the base-2 log of \a x. */ -@extern float log2 (float x); +@extern @overload float log2 (float x); +@extern @overload double log2 (double x); /** Returns the base-10 log of \a x. */ -@extern float log10 (float x); +@extern @overload float log10 (float x); +@extern @overload double log10 (double x); /** Returns \a x to the \a y power */ -@extern float pow (float x, float y); +@extern @overload float pow (float x, float y); +@extern @overload double pow (double x, double y); /** Returns the square root of \a x */ -@extern float sqrt (float x); +@extern @overload float sqrt (float x); +@extern @overload double sqrt (double x); /** Returns the cube root of \a x */ -@extern float cbrt (float x); +@extern @overload float cbrt (float x); +@extern @overload double cbrt (double x); //\} ///\name Trigonometric functions @@ -112,40 +127,52 @@ /** Returns the sine of \a x. */ -@extern float sin (float x); +@extern @overload float sin (float x); +@extern @overload double sin (double x); /** Returns the cosine of \a x. */ -@extern float cos (float x); +@extern @overload float cos (float x); +@extern @overload double cos (double x); /** Returns the tangent of \a x. */ -@extern float tan (float x); +@extern @overload float tan (float x); +@extern @overload double tan (double x); /** Returns the arcsine of \a x. */ -@extern float asin (float x); +@extern @overload float asin (float x); +@extern @overload double asin (double x); /** Returns the arccosine of \a x. */ -@extern float acos (float x); +@extern @overload float acos (float x); +@extern @overload double acos (double x); /** Returns the arctangent of \a x. */ -@extern float atan (float x); -@extern float atan2 (float y, float x); +@extern @overload float atan (float x); +@extern @overload double atan (double x); + +/** + Returns the arctangent of \a y / \a x preserving the quadrant. +*/ +@extern @overload float atan2 (float y, float x); +@extern @overload double atan2 (double y, double x); /** Returns the length of the hypotenuse of a right triangle with sides \a x and \a y. That is, this function returns sqrt (\a x*\a x + \a y*\a y). */ -@extern float hypot (float x, float y); +@extern @overload float hypot (float x, float y); +@extern @overload double hypot (double x, double y); //\} ///\name Hyperbolic functions @@ -153,32 +180,38 @@ /** Returns the hyperbolic sine of \a x */ -@extern float sinh (float x); +@extern @overload float sinh (float x); +@extern @overload double sinh (double x); /** Returns the hyperbolic cosine of \a x */ -@extern float cosh (float x); +@extern @overload float cosh (float x); +@extern @overload double cosh (double x); /** Returns the hyperbolic tangent of \a x */ -@extern float tanh (float x); +@extern @overload float tanh (float x); +@extern @overload double tanh (double x); /** Returns the area hyperbolic sine of \a x */ -@extern float asinh (float x); +@extern @overload float asinh (float x); +@extern @overload double asinh (double x); /** Returns the area hyperbolic cosine of \a x */ -@extern float acosh (float x); +@extern @overload float acosh (float x); +@extern @overload double acosh (double x); /** Returns the area hyperbolic tangent of \a x */ -@extern float atanh (float x); +@extern @overload float atanh (float x); +@extern @overload double atanh (double x); //\} ///\name Vector Functions diff --git a/ruamoko/include/mersenne.h b/ruamoko/include/mersenne.h new file mode 100644 index 000000000..6f77cd7f5 --- /dev/null +++ b/ruamoko/include/mersenne.h @@ -0,0 +1,15 @@ +#ifndef __ruamoko_mersenne_h +#define __ruamoko_mersenne_h + +typedef struct _mtwist_t mtwist_t; + +mtwist_t *mtwist_new (int seed); +void mtwist_delete (mtwist_t *state); +void mtwist_seed (mtwist_t *state, int seed); +int mtwist_rand (mtwist_t *state); +// includes 0, does not include 1 +float mtwist_rand_0_1 (mtwist_t *state); +// does not include either -1 or 1 +float mtwist_rand_m1_1 (mtwist_t *state); + +#endif//__ruamoko_mersenne_h diff --git a/ruamoko/include/message.h b/ruamoko/include/message.h index 7d37e91f5..15354838f 100644 --- a/ruamoko/include/message.h +++ b/ruamoko/include/message.h @@ -34,8 +34,8 @@ #define MULTICAST_PHS_R 4 #define MULTICAST_PVS_R 5 -@extern void bprint (...); -@extern void sprint (entity client, string s); +@extern void bprint (float level, string s); +@extern void sprint (entity client, float level, string s); @extern void WriteBytes (float to, ...); @extern void WriteByte (float to, float f); @extern void WriteChar (float to, float f); @@ -47,6 +47,6 @@ @extern void WriteAngleV (float to, vector v); @extern void WriteString (float to, string s); @extern void WriteEntity (float to, entity s); -@extern void centerprint (...); +@extern void centerprint (entity client, string s); #endif//__ruamoko_message_h diff --git a/ruamoko/include/msgbuf.h b/ruamoko/include/msgbuf.h index d26ba4de9..172d7338a 100644 --- a/ruamoko/include/msgbuf.h +++ b/ruamoko/include/msgbuf.h @@ -1,7 +1,7 @@ #ifndef __ruamoko_msgbuf_h #define __ruamoko_msgbuf_h -#include "qfile.h" +#include struct msgbuf_s; typedef struct msgbuf_s msgbuf_t; diff --git a/ruamoko/include/plist.h b/ruamoko/include/plist.h index 949419ea9..161a1a611 100644 --- a/ruamoko/include/plist.h +++ b/ruamoko/include/plist.h @@ -1,30 +1,37 @@ #ifndef __ruamoko_plist_h #define __ruamoko_plist_h -#include "qfile.h" +#include -typedef struct plitem_s *plitem_t; +typedef struct plitem_s plitem_t; typedef enum {QFDictionary, QFArray, QFBinary, QFString} pltype_t; // possible types -@extern plitem_t PL_GetFromFile (QFile file); -@extern plitem_t PL_GetPropertyList (string str); -@extern string PL_WritePropertyList (plitem_t pl); -@extern pltype_t PL_Type (plitem_t str); -@extern string PL_String (plitem_t str); -@extern plitem_t PL_ObjectForKey (plitem_t item, string key); -@extern plitem_t PL_RemoveObjectForKey (plitem_t item, string key); -@extern plitem_t PL_ObjectAtIndex (plitem_t item, int index); -@extern plitem_t PL_D_AllKeys (plitem_t item); -@extern int PL_D_NumKeys (plitem_t item); -@extern int PL_D_AddObject (plitem_t dict, string key, plitem_t value); -@extern int PL_A_AddObject (plitem_t array_item, plitem_t item); -@extern int PL_A_NumObjects (plitem_t item); -@extern int PL_A_InsertObjectAtIndex (plitem_t array_item, plitem_t item, int index); -@extern plitem_t PL_RemoveObjectAtIndex (plitem_t array_item, int index); -@extern plitem_t PL_NewDictionary (); -@extern plitem_t PL_NewArray (); -@extern plitem_t PL_NewData (void *data, int len); -@extern plitem_t PL_NewString (string str); -@extern void PL_Free (plitem_t pl); +@extern plitem_t *PL_GetFromFile (QFile file); +@extern plitem_t *PL_GetPropertyList (string str); +@extern plitem_t *PL_GetDictionaryFromFile (QFile file); +@extern plitem_t *PL_GetDictionary (string str); +@extern plitem_t *PL_GetArrayFromFile (QFile file); +@extern plitem_t *PL_GetArray (string str); +@extern string PL_WritePropertyList (plitem_t *pl); +@extern pltype_t PL_Type (plitem_t *str); +@extern int PL_Line (plitem_t *str); +@extern string PL_String (plitem_t *str); +@extern plitem_t *PL_ObjectForKey (plitem_t *item, string key); +@extern void PL_RemoveObjectForKey (plitem_t *item, string key); +@extern plitem_t *PL_ObjectAtIndex (plitem_t *item, int index); +@extern plitem_t *PL_D_AllKeys (plitem_t *item); +@extern string PL_KeyAtIndex (plitem_t *item, int index); +@extern int PL_D_NumKeys (plitem_t *item); +@extern int PL_D_AddObject (plitem_t *dict, string key, plitem_t *value); +@extern int PL_A_AddObject (plitem_t *array_item, plitem_t *item); +@extern int PL_A_NumObjects (plitem_t *item); +@extern int PL_A_InsertObjectAtIndex (plitem_t *array_item, plitem_t *item, int index); +@extern void PL_RemoveObjectAtIndex (plitem_t *array_item, int index); +@extern plitem_t *PL_NewDictionary (); +@extern plitem_t *PL_NewArray (); +@extern plitem_t *PL_NewData (void *data, int len); +@extern plitem_t *PL_NewString (string str); +@extern plitem_t *PL_Retain (plitem_t *pl); +@extern plitem_t *PL_Release (plitem_t *pl); #endif//__ruamoko_plist_h diff --git a/ruamoko/include/qfile.h b/ruamoko/include/qfile.h index 93fdbd9b3..8d3a9eddc 100644 --- a/ruamoko/include/qfile.h +++ b/ruamoko/include/qfile.h @@ -1,7 +1,7 @@ #ifndef __ruamoko_qfile_h #define __ruamoko_qfile_h -typedef struct _qfile_t *QFile; +typedef @handle _qfile_t QFile; @extern int Qrename (string old, string new); @extern int Qremove (string path); diff --git a/ruamoko/include/qfs.h b/ruamoko/include/qfs.h index 484a020c0..379e29ad2 100644 --- a/ruamoko/include/qfs.h +++ b/ruamoko/include/qfs.h @@ -1,7 +1,7 @@ #ifndef __ruamoko_qfs_h #define __ruamoko_qfs_h -#include "qfile.h" +#include struct _qfslist_t { int count; @@ -17,5 +17,6 @@ typedef struct _qfslist_t *QFSlist; @extern int QFS_WriteFile (string filename, void *buf, int count); @extern QFSlist QFS_Filelist (string path, string ext, int strip); @extern void QFS_FilelistFree (QFSlist list); +@extern string QFS_GetDirectory (void); #endif//__ruamoko_qfs_h diff --git a/ruamoko/include/qw_message.h b/ruamoko/include/qw_message.h index 5bd7efb0d..60c8587d1 100644 --- a/ruamoko/include/qw_message.h +++ b/ruamoko/include/qw_message.h @@ -1,7 +1,7 @@ #ifndef __ruamoko_qw_message_h #define __ruamoko_qw_message_h -#include "message.h" +#include @extern void multicast (vector where, float set); diff --git a/ruamoko/include/runtime.h b/ruamoko/include/runtime.h index bbcd793ed..849b9be1e 100644 --- a/ruamoko/include/runtime.h +++ b/ruamoko/include/runtime.h @@ -38,12 +38,14 @@ typedef enum { YES ///< a true value } BOOL; +@extern void __obj_forward(id obj, SEL sel, ...); +@extern BOOL __obj_responds_to(id obj, SEL sel); @extern void obj_error (id object, int code, string fmt, ...); @extern void obj_verror (id object, int code, string fmt, @va_list args); //obj_error_handler obj_set_error_handler (objc_error_handler func); @extern IMP obj_msg_lookup (id receiver, SEL op); @extern IMP obj_msg_lookup_super (Super class, SEL op); -@extern @param obj_msg_sendv (id receiver, SEL op, @va_list args); +@extern @attribute(void_return) void obj_msg_sendv (id receiver, SEL op, @va_list args); @extern BOOL obj_decrement_ref_was_zero (id object); @extern BOOL obj_increment_ref_was_zero (id object); @extern void *obj_malloc (int size); @@ -98,6 +100,10 @@ typedef enum { @extern void *PR_FindGlobal (string name); //FIXME where? +// copies the list in src to a temporary buffer that will be freed when the +// calling function returns, and places the pointer to the buffer into the +// returned va_list. The count is copies as-is. +@extern @va_list va_copy(@va_list src); #endif //__ruamoko_runtime_h_ /** \} diff --git a/ruamoko/include/scene.h b/ruamoko/include/scene.h new file mode 100644 index 000000000..e2907ae6a --- /dev/null +++ b/ruamoko/include/scene.h @@ -0,0 +1,69 @@ +#ifndef __ruamoko_scene_h +#define __ruamoko_scene_h + +//FIXME this should be a native type so it can be used in math +typedef struct { + vec4 col[4]; +} mat4x4; + +typedef struct light_s { + vec4 color; + vec4 position; + vec4 direction; + vec4 attenuation; +} light_t; + +//FIXME need a handle type +typedef struct { long handle; } scene_t; +typedef struct { long handle; } entity_t; +typedef struct { long handle; } transform_t; +typedef struct { long handle; } lightingdata_t; +typedef struct { int handle; } model_t; + +scene_t Scene_NewScene (void); +void Scene_DeleteScene (scene_t scene); +entity_t Scene_CreateEntity (scene_t scene); +void Scene_DestroyEntity (entity_t ent); +void Scene_SetLighting (scene_t scene, lightingdata_t ldata); + +transform_t Entity_GetTransform (entity_t ent); +void Entity_SetModel (entity_t ent, model_t model); + +unsigned Transform_ChildCount (transform_t transform); +transform_t Transform_GetChild (transform_t transform, + unsigned childIndex); +void Transform_SetParent (transform_t transform, transform_t parent); +transform_t Transform_GetParent (transform_t transform); +void Transform_SetTag (transform_t transform, unsigned tag); +unsigned Transform_GetTag (transform_t transform); +mat4x4 Transform_GetLocalMatrix (transform_t transform); +mat4x4 Transform_GetLocalInverse (transform_t transform); +mat4x4 Transform_GetWorldMatrix (transform_t transform); +mat4x4 Transform_GetWorldInverse (transform_t transform); +vec4 Transform_GetLocalPosition (transform_t transform); +void Transform_SetLocalPosition (transform_t transform, vec4 position); +vec4 Transform_GetLocalRotation (transform_t transform); +void Transform_SetLocalRotation (transform_t transform, vec4 rotation); +vec4 Transform_GetLocalScale (transform_t transform); +void Transform_SetLocalScale (transform_t transform, vec4 scale); +vec4 Transform_GetWorldPosition (transform_t transform); +void Transform_SetWorldPosition (transform_t transform, vec4 position); +vec4 Transform_GetWorldRotation (transform_t transform); +void Transform_SetWorldRotation (transform_t transform, vec4 rotation); +vec4 Transform_GetWorldScale (transform_t transform); +void Transform_SetLocalTransform (transform_t transform, vec4 scale, + vec4 rotation, vec4 position); +vec4 Transform_Forward (transform_t transform); +vec4 Transform_Right (transform_t transform); +vec4 Transform_Up (transform_t transform); + +lightingdata_t Light_CreateLightingData (scene_t scene); +void Light_DestroyLightingData (lightingdata_t ldata); +void Light_ClearLights (lightingdata_t ldata); +void Light_AddLight (lightingdata_t ldata, light_t light, int style); +void Light_EnableSun (lightingdata_t ldata); + +model_t Model_Load (string path); +void Model_Unload (model_t model); + +#endif//__ruamoko_scene_h diff --git a/ruamoko/include/script.h b/ruamoko/include/script.h index 2a394f058..b56f479ab 100644 --- a/ruamoko/include/script.h +++ b/ruamoko/include/script.h @@ -1,16 +1,19 @@ #ifndef __ruamoko_script_h #define __ruamoko_script_h -typedef struct rua_script *script_t; +typedef @handle rua_script script_t; @extern script_t Script_New (void); @extern void Script_Delete (script_t script); // returns the token string @extern string Script_Start (script_t script, string file, string data); +@extern string Script_FromFile (script_t script, string filename, @handle _qfile_t file); @extern int Script_TokenAvailable (script_t script, int crossline); @extern int Script_GetToken (script_t script, int crossline); @extern void Script_UngetToken (script_t script); @extern string Script_Error (script_t script); @extern int Script_NoQuoteLines (script_t script); +@extern void Script_SetSingle (script_t script, string single); +@extern int Script_GetLine (script_t script); #endif//__ruamoko_script_h diff --git a/ruamoko/include/server.h b/ruamoko/include/server.h index f2e8c2469..2053ca4d2 100644 --- a/ruamoko/include/server.h +++ b/ruamoko/include/server.h @@ -1,8 +1,8 @@ #ifndef __ruamoko_server_h #define __ruamoko_server_h -@extern void precache_sound (string s); -@extern void precache_model (string s); +@extern string precache_sound (string s); +@extern string precache_model (string s); @extern void stuffcmd (entity client, string s); @extern void localcmd (string s); @extern void changelevel (string s); diff --git a/ruamoko/include/stdlib.h b/ruamoko/include/stdlib.h new file mode 100644 index 000000000..d645955f4 --- /dev/null +++ b/ruamoko/include/stdlib.h @@ -0,0 +1,17 @@ +#ifndef __stdlib_h +#define __stdlib_h + +//FIXME add const to qfcc? +// If compare is nil, the value pointed to by key and the first member of the +// elements in the array are treated as ints, making for fast searches when the +// first member of the element is the key and is a simple int +void *bsearch (void *key, void *array, int nmemb, int size, + int (*compare) (void *a, void *b)); +void *fbsearch (void *key, void *array, int nmemb, int size, + int (*compare) (void *a, void *b)); +void *qsort (void *array, int nmemb, int size, + int (*compare) (void *a, void *b)); +@overload void prefixsum (int *array, int count); +@overload void prefixsum (float *array, int count); + +#endif//__stdlib_h diff --git a/ruamoko/include/string.h b/ruamoko/include/string.h index c4136e2fc..fab2daf6b 100644 --- a/ruamoko/include/string.h +++ b/ruamoko/include/string.h @@ -1,23 +1,28 @@ #ifndef __ruamoko_string_h #define __ruamoko_string_h -@extern string ftos (float f); -@extern string vtos (vector v); -@extern float stof (string s); -@extern float strlen (string s); -@extern float charcount (string goal, string s); +@extern int strlen (string s); @extern string sprintf (string fmt, ...); -@extern string itos (int i); -@extern int stoi (string s); -@extern vector stov (string s); - +@extern string vsprintf (string fmt, @va_list args); @extern string str_new (void); -@extern string str_free (string str); +@extern string str_unmutable (string str); +@extern void str_free (string str); +@extern string str_hold (string str); +@extern int str_valid (string str); +@extern int str_mutable (string str); @extern string str_copy (string dst, string src); @extern string str_cat (string dst, string src); @extern string str_clear (string str); @extern @overload string str_mid (string str, int start); @extern @overload string str_mid (string str, int start, int len); -@extern string str_str (string haystack, string needle); +int str_str (string haystack, string needle); +@extern int str_char (string str, int ind); +string str_quote (string str); +string str_lower (string str); +string str_upper (string str); +double strtod (string str, int *end); +float strtof (string str, int *end); +long strtol (string str, int *end, int base); +unsigned long strtoul (string str, int *end, int base); #endif//__ruamoko_string_h diff --git a/ruamoko/include/system.h b/ruamoko/include/system.h index 64056c8d9..1c99f0035 100644 --- a/ruamoko/include/system.h +++ b/ruamoko/include/system.h @@ -3,8 +3,8 @@ @extern float time; -@extern void precache_sound (string s); -@extern void precache_model (string s); +@extern string precache_sound (string s); +@extern string precache_model (string s); @extern void stuffcmd (entity client, string s); @extern void localcmd (string s); @extern void changelevel (string s); diff --git a/ruamoko/include/types.h b/ruamoko/include/types.h new file mode 100644 index 000000000..d1405d2a9 --- /dev/null +++ b/ruamoko/include/types.h @@ -0,0 +1,98 @@ +#ifndef __types_h +#define __types_h + +#define EV_TYPE(type) ev_##type, +typedef enum { +#include + ev_invalid, // invalid type. used for instruction checking + ev_type_count // not a type, gives number of types +} etype_t; + +typedef enum { + ty_basic, ///< VM type (float, int, pointer, field, etc) + ty_struct, + ty_union, + ty_enum, + ty_array, + ty_class, + ty_alias, + ty_handle, +} ty_meta_e; + +typedef struct qfot_alias_s { + etype_t type; + struct qfot_type_s *aux_type; + struct qfot_type_s *full_type; + string name; +} qfot_alias_t; + +typedef struct qfot_handle_s { + etype_t type; + string tag; +} qfot_handle_t; + +typedef struct qfot_fldptr_s { + etype_t type; + struct qfot_type_s *aux_type; +} qfot_fldptr_t; + +typedef struct qfot_basic_s { + etype_t type; + int width; +} qfot_basic_t; + +typedef struct qfot_func_s { + etype_t type; + struct qfot_type_s *return_type; + int num_params; + struct qfot_type_s *param_types[1]; +} qfot_func_t; + +typedef struct qfot_var_s { + struct qfot_type_s *type; + string name; + int offset; // value for enum, 0 for union +} qfot_var_t; + +typedef struct qfot_struct_s { + string tag; + int num_fields; + qfot_var_t fields[1]; +} qfot_struct_t; + +typedef struct qfot_array_s { + struct qfot_type_s *type; + int base; + int size; +} qfot_array_t; + +typedef struct qfot_type_s { + ty_meta_e meta; + int size; + string encoding; + union { + etype_t type; + qfot_basic_t basic; + qfot_fldptr_t fldptr; + qfot_func_t func; + qfot_struct_t strct; + qfot_array_t array; + string class; + qfot_alias_t alias; + qfot_handle_t handle; + }; +} qfot_type_t; + +// the minimum size of a type encoding +#define TYPESIZE 4 + +typedef struct qfot_type_encodings_s { + qfot_type_t *types; + int size; +} qfot_type_encodings_t; + +@extern string ty_meta_name[7]; +@extern string pr_type_name[ev_type_count]; +@extern int pr_type_size[ev_type_count]; + +#endif diff --git a/ruamoko/lib/Array+Private.r b/ruamoko/lib/Array+Private.r index 167404e5c..2cdea39cf 100644 --- a/ruamoko/lib/Array+Private.r +++ b/ruamoko/lib/Array+Private.r @@ -20,14 +20,14 @@ local unsigned i = count; local unsigned tmp; - do { - if (_objs[--i] == anObject) { + while (i-- > 0) { + if (_objs[i] == anObject) { for (tmp = i; tmp < count - 1; tmp++) { _objs[tmp] = _objs[tmp + 1]; } count--; } - } while (i); + } } @end diff --git a/ruamoko/lib/Array.r b/ruamoko/lib/Array.r index 4673c31d8..aa0f82082 100644 --- a/ruamoko/lib/Array.r +++ b/ruamoko/lib/Array.r @@ -1,7 +1,7 @@ -#include "math.h" +#include -#include "Array.h" -#include "runtime.h" +#include +#include #define STANDARD_CAPACITY 16 #define ARRAY_MAX_GRANULARITY 100 @@ -162,7 +162,7 @@ - (id) objectAtIndex: (unsigned)index { if (index >= count) // FIXME: need exceptions - [self error: "-replaceObjectAtIndex:withObject: index out of range"]; + [self error: "-objectAtIndex:withObject: index out of range"]; return _objs[index]; } @@ -261,7 +261,7 @@ { local unsigned i; - if (index >= count) // FIXME: need exceptions + if (index > count) // FIXME: need exceptions [self error: "-insertObject:atIndex: index out of range"]; if (count == capacity) { // at capacity, expand @@ -323,7 +323,7 @@ cause something else to happen). */ tmp = _objs[--count]; - _objs[i] = nil; + _objs[count] = nil; [tmp release]; } #else @@ -401,7 +401,7 @@ } - (void) makeObjectsPerformSelector: (SEL)selector - withObject: (id)anObject + withObject: (void *)anObject { local int i; @@ -410,6 +410,19 @@ } } +- (void) makeObjectsPerformSelector: (SEL)selector + withObject: (void *)anObject + withObject: (void *)anotherObject +{ + local int i; + + for (i = 0; i < [self count]; i++) { + [[self objectAtIndex: i] performSelector: selector + withObject: anObject + withObject: anotherObject]; + } +} + - (void) dealloc { local unsigned i; diff --git a/ruamoko/lib/AutoreleasePool.r b/ruamoko/lib/AutoreleasePool.r index 6d8077cfc..b4aadd36f 100644 --- a/ruamoko/lib/AutoreleasePool.r +++ b/ruamoko/lib/AutoreleasePool.r @@ -1,7 +1,6 @@ -#include "AutoreleasePool.h" -#include "Array+Private.h" +#include -#include "Array.h" +#include #include "Array+Private.h" @static Array *poolStack; diff --git a/ruamoko/lib/Entity.r b/ruamoko/lib/Entity.r index 5cc1f4f11..9816d1fb9 100644 --- a/ruamoko/lib/Entity.r +++ b/ruamoko/lib/Entity.r @@ -1,46 +1,60 @@ -#include "Entity.h" +#include -#include "debug.h" -#include "entities.h" -#include "plist.h" -#include "script.h" -#include "string.h" +#include +#include +#include +#include +#include typedef void () void_function; int PR_SetField (entity ent, string field, string value) = #0; -function PR_FindFunction (string func) = #0; +@function PR_FindFunction (string func) = #0; @static void ParseEntities (string ent_data); @implementation Entity -- (void) own -{ - own = 1; -} - - (id) init { - self = [self initWithEntity: spawn ()]; - [self own]; + if (!(self = [self initWithEntity: spawn ()])) { + return nil; + } + own = 1; return self; } - (id) initWithEntity: (entity)e { - self = [super init]; + if (!(self = [super init])) { + return nil; + } ent = e; ent.@this = self; return self; } -- (id) initWithEntity: (entity)e fromPlist: (plitem_t) dict +- (id) initWithEntity: (entity)e fromPlist: (plitem_t *) dict { self = [self initWithEntity: e]; return self; } ++(Entity *) spawn +{ + return [[[self alloc] init] autorelease]; +} + ++(Entity *) withEntity: (entity) e +{ + return [[[self alloc] initWithEntity: e] autorelease]; +} + ++(Entity *) withEntity: (entity) e fromPlist: (plitem_t *) dict +{ + return [[[self alloc] initWithEntity: e fromPlist: dict] autorelease]; +} + - (void) dealloc { if (own && ent) @@ -58,15 +72,15 @@ function PR_FindFunction (string func) = #0; //XXX EntityParseFunction (ParseEntities); } -+ createFromPlist:(plitem_t) dict ++ createFromPlist:(plitem_t *) dict { local string classname; local id class; local entity ent; local int count; local string field, value; - local plitem_t keys; - local function func; + local plitem_t *keys; + local @function func; local Entity *e; classname = PL_String (PL_ObjectForKey (dict, "classname")); @@ -85,7 +99,7 @@ function PR_FindFunction (string func) = #0; value = PL_String (PL_ObjectForKey (dict, field)); PR_SetField (ent, field, value); } - PL_Free (keys); + PL_Release (keys); if ((func = PR_FindFunction (classname))) { //dprint ("calling " + classname + "\n"); @self = ent; @@ -95,7 +109,7 @@ function PR_FindFunction (string func) = #0; } } if (ent) - [e own]; + e.own = 1; return e; } @@ -104,7 +118,7 @@ function PR_FindFunction (string func) = #0; @static void ParseEntities (string ent_data) { local script_t script; - local plitem_t plist, ent, key, value; + local plitem_t *plist, *ent, *key, *value; local string token; local int anglehack, i, count; @@ -143,7 +157,7 @@ function PR_FindFunction (string func) = #0; else value = PL_NewString (token); PL_D_AddObject (ent, PL_String (key), value); - PL_Free (key); + PL_Release (key); } PL_A_AddObject (plist, ent); } diff --git a/ruamoko/lib/Makefile.am b/ruamoko/lib/Makefile.am deleted file mode 100644 index 53a20875b..000000000 --- a/ruamoko/lib/Makefile.am +++ /dev/null @@ -1,50 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -pkglibdir=$(datarootdir)/qfcc/lib - -QFCC=$(top_builddir)/tools/qfcc/source/qfcc$(EXEEXT) -QCFLAGS=-qq -O -g -Wall -Wno-integer-divide -Werror --no-default-paths -QCPPFLAGS=$(AM_CPPFLAGS) -PAK=$(top_builddir)/tools/pak/pak$(EXEEXT) -RANLIB=touch - -AM_CPPFLAGS= -I$(top_srcdir)/ruamoko/include -I$(top_srcdir)/include - -noinst_HEADERS= \ - Array+Private.h - -ruamoko_libs=libr.a libqw.a libnq.a libcsqc.a -libs=$(ruamoko_libs) - -pkglib_LIBRARIES= $(libs) -EXTRA_LIBRARIES= $(ruamoko_libs) - -SUFFIXES= .o .r .qc -.r.o: - $(QFCC) $(QCFLAGS) $(QCPPFLAGS) -c -o $@ $< -.qc.o: - $(QFCC) $(QCFLAGS) $(QCPPFLAGS) -c -o $@ $< - -libr_a_SOURCES=\ - cbuf.r cmd.r cvar.r file.r hash.r msgbuf.r plist.r qfile.r qfs.r script.r \ - sound.r string.r math.r \ - Object.r Protocol.r \ - AutoreleasePool.r Array.r Array+Private.r Entity.r PropertyList.r Set.r -libr_a_AR=$(PAK) -cf - -libqw_a_SOURCES=\ - crudefile.r debug.r entities.r infokey.r math.r message.r \ - physics.r qw_message.r qw_physics.r qw_sys.r \ - server.r sv_sound.r system.r -libqw_a_AR=$(PAK) -cf - -libnq_a_SOURCES=\ - crudefile.r debug.r entities.r infokey.r math.r message.r \ - nq_message.r physics.r server.r sv_sound.r system.r -libnq_a_AR=$(PAK) -cf - -libcsqc_a_SOURCES= \ - debug.r draw.r gib.r key.r system.r -libcsqc_a_AR= $(PAK) -cf - -CLEANFILES= *.qfo *.o diff --git a/ruamoko/lib/Makemodule.am b/ruamoko/lib/Makemodule.am new file mode 100644 index 000000000..eb110b6d9 --- /dev/null +++ b/ruamoko/lib/Makemodule.am @@ -0,0 +1,119 @@ +ruamoko_libs=ruamoko/lib/libr.a ruamoko/lib/libqw.a ruamoko/lib/libnq.a ruamoko/lib/libcsqc.a + +ruamoko_lib_LIBRARIES += $(ruamoko_libs) +EXTRA_LIBRARIES += $(ruamoko_libs) + +noinst_HEADERS += \ + ruamoko/lib/Array+Private.h + +ruamoko_lib_libr_a_SOURCES=\ + ruamoko/lib/cbuf.r \ + ruamoko/lib/cmd.r \ + ruamoko/lib/cvar.r \ + ruamoko/lib/hash.r \ + ruamoko/lib/msgbuf.r \ + ruamoko/lib/plist.r \ + ruamoko/lib/qfile.r \ + ruamoko/lib/qfs.r \ + ruamoko/lib/script.r \ + ruamoko/lib/sound.r \ + ruamoko/lib/stdlib.r \ + ruamoko/lib/string.r \ + ruamoko/lib/math.r \ + ruamoko/lib/types.r \ + ruamoko/lib/va_list.r \ + ruamoko/lib/obj_forward.r \ + ruamoko/lib/Object.r \ + ruamoko/lib/Protocol.r \ + ruamoko/lib/AutoreleasePool.r \ + ruamoko/lib/Array.r \ + ruamoko/lib/Array+Private.r \ + ruamoko/lib/Entity.r \ + ruamoko/lib/PropertyList.r \ + ruamoko/lib/Set.r +ruamoko_lib_libr_a_dep=$(call qcautodep,$(ruamoko_lib_libr_a_SOURCES)) +ruamoko_lib_libr_a_AR=$(PAK) -cf +EXTRA_ruamoko_lib_libr_a_DEPENDENCIES=$(PAK) +include $(ruamoko_lib_libr_a_dep) # am--include-marker +r_depfiles_remade += $(ruamoko_lib_libr_a_dep) + +ruamoko_lib_common_src= \ + ruamoko/lib/debug.r \ + ruamoko/lib/system.r \ + ruamoko/lib/legacy_math.r \ + ruamoko/lib/legacy_string.r + +ruamoko_lib_server_src= \ + ruamoko/lib/crudefile.r \ + ruamoko/lib/entities.r \ + ruamoko/lib/infokey.r \ + ruamoko/lib/message.r \ + ruamoko/lib/physics.r \ + ruamoko/lib/server.r \ + ruamoko/lib/sv_sound.r + +ruamoko_lib_libqw_a_src= \ + ruamoko/lib/qw_message.r \ + ruamoko/lib/qw_physics.r \ + ruamoko/lib/qw_sys.r + +ruamoko_lib_libnq_a_src= \ + ruamoko/lib/nq_message.r + +ruamoko_lib_libcsqc_a_src= \ + ruamoko/lib/draw.r \ + ruamoko/lib/gib.r \ + ruamoko/lib/input.r \ + ruamoko/lib/mersenne.r \ + ruamoko/lib/scene.r \ + ruamoko/lib/key.r + +ruamoko_lib_common_dep=$(call qcautodep,$(ruamoko_lib_common_src)) +include $(ruamoko_lib_common_dep) # am--include-marker +r_depfiles_remade += $(ruamoko_lib_common_dep) + +ruamoko_lib_server_dep=$(call qcautodep,$(ruamoko_lib_server_src)) +include $(ruamoko_lib_server_dep) # am--include-marker +r_depfiles_remade += $(ruamoko_lib_server_dep) + +ruamoko_lib_libqw_a_SOURCES= \ + $(ruamoko_lib_libqw_a_src) \ + $(ruamoko_lib_common_src) \ + $(ruamoko_lib_server_src) \ + ruamoko/lib/math.r +ruamoko_lib_libqw_a_dep=$(call qcautodep,$(ruamoko_lib_libqw_a_src)) +ruamoko_lib_libqw_a_AR=$(PAK) -cf +EXTRA_ruamoko_lib_libqw_a_DEPENDENCIES=$(PAK) +include $(ruamoko_lib_libqw_a_dep) # am--include-marker +r_depfiles_remade += $(ruamoko_lib_libqw_a_dep) + +ruamoko_lib_libnq_a_SOURCES=\ + $(ruamoko_lib_libnq_a_src) \ + $(ruamoko_lib_common_src) \ + $(ruamoko_lib_server_src) \ + ruamoko/lib/math.r +ruamoko_lib_libnq_a_dep=$(call qcautodep,$(ruamoko_lib_libnq_a_src)) +ruamoko_lib_libnq_a_AR=$(PAK) -cf +EXTRA_ruamoko_lib_libnq_a_DEPENDENCIES=$(PAK) +include $(ruamoko_lib_libnq_a_dep) # am--include-marker +r_depfiles_remade += $(ruamoko_lib_libnq_a_dep) + +ruamoko_lib_libcsqc_a_SOURCES=\ + $(ruamoko_lib_libcsqc_a_src) \ + $(ruamoko_lib_common_src) +ruamoko_lib_libcsqc_a_dep=$(call qcautodep,$(ruamoko_lib_libcsqc_a_src)) +ruamoko_lib_libcsqc_a_AR= $(PAK) -cf +EXTRA_ruamoko_lib_libcsqc_a_DEPENDENCIES=$(PAK) +include $(ruamoko_lib_libcsqc_a_dep) # am--include-marker +r_depfiles_remade += $(ruamoko_lib_libcsqc_a_dep) + +CLEANFILES += \ + ruamoko/lib/*.dat \ + ruamoko/lib/*.sym +DISTCLEANFILES += \ + $(ruamoko_lib_libr_a_dep) \ + $(ruamoko_lib_common_dep) \ + $(ruamoko_lib_server_dep) \ + $(ruamoko_lib_libqw_a_dep) \ + $(ruamoko_lib_libnq_a_dep) \ + $(ruamoko_lib_libcsqc_a_dep) diff --git a/ruamoko/lib/Object.r b/ruamoko/lib/Object.r index a6b0e9d85..83ed07755 100644 --- a/ruamoko/lib/Object.r +++ b/ruamoko/lib/Object.r @@ -1,17 +1,24 @@ -#include "Object.h" -#include "AutoreleasePool.h" +#include +#include +#include + +static void link__obj_forward (void) +{ + __obj_forward (nil, nil); +} void *PR_FindGlobal (string name) = #0; //FIXME where? void __obj_exec_class (struct obj_module *msg) = #0; +BOOL __obj_responds_to(id obj, SEL sel) = #0; void (id object, int code, string fmt, ...) obj_error = #0; void (id object, int code, string fmt, @va_list args) obj_verror = #0; -//obj_error_handler (objc_error_handler func) obj_set_error_handler = #0; +//obj_error_handler obj_set_error_handler (objc_error_handler func) = #0; IMP (id receiver, SEL op) obj_msg_lookup = #0; IMP (Super class, SEL op) obj_msg_lookup_super = #0; id (id receiver, SEL op, ...) obj_msgSend = #0; id obj_msgSend_super (Super *class, SEL op, ...) = #0; -@param (id receiver, SEL op, @va_list args) obj_msg_sendv = #0; +@attribute(void_return) void obj_msg_sendv (id receiver, SEL op, @va_list args) = #0; int obj_decrement_retaincount (id object) = #0; int obj_increment_retaincount (id object) = #0; int obj_get_retaincount (id object) = #0; @@ -21,11 +28,11 @@ void *obj_valloc (int size) = #0; void *obj_realloc (void *mem, int size) = #0; void *obj_calloc (int nelem, int size) = #0; void obj_free (void *mem) = #0; -//(void *) (void) obj_get_uninstalled_dtable = #0; +void *obj_get_uninstalled_dtable (void) = #0; -Class (string name) obj_get_class = #0; -Class (string name) obj_lookup_class = #0; -//Class (void **enum_stage) obj_next_class = #0; +Class obj_get_class (string name) = #0; +Class obj_lookup_class (string name) = #0; +Class obj_next_class (void **enum_stage) = #0; string (SEL selector) sel_get_name = #0; string (SEL selector) sel_get_type = #0; @@ -71,10 +78,12 @@ BOOL (id object) object_is_meta_class = #0; @end +typedef void arIMP(id, SEL, id); + @static BOOL allocDebug; @static Class autoreleaseClass; @static SEL autoreleaseSelector; -@static IMP autoreleaseIMP; +@static arIMP autoreleaseIMP; @implementation Object @@ -83,7 +92,7 @@ BOOL (id object) object_is_meta_class = #0; //allocDebug = localinfo ("AllocDebug"); autoreleaseClass = [AutoreleasePool class]; autoreleaseSelector = @selector(addObject:); - autoreleaseIMP = [autoreleaseClass methodForSelector: autoreleaseSelector]; + autoreleaseIMP = (arIMP)[autoreleaseClass methodForSelector: autoreleaseSelector]; return; } @@ -247,7 +256,7 @@ BOOL (id object) object_is_meta_class = #0; return msg (self, aSelector); } -- (id) performSelector: (SEL)aSelector withObject: (id)anObject +- (id) performSelector: (SEL)aSelector withObject: (void *)anObject { local IMP msg = nil; // FIXME teach qfcc about noreturn @@ -260,8 +269,8 @@ BOOL (id object) object_is_meta_class = #0; } - (id) performSelector: (SEL)aSelector - withObject: (id)anObject - withObject: (id)anotherObject + withObject: (void *)anObject + withObject: (void *)anotherObject { local IMP msg; @@ -274,6 +283,11 @@ BOOL (id object) object_is_meta_class = #0; return msg (self, aSelector, anObject, anotherObject); } +- (void) performv: (SEL) sel : (@va_list) args +{ + obj_msg_sendv (self, sel, args); +} + - (void) doesNotRecognizeSelector: (SEL)aSelector { [self error: "%s does not recognize %s", @@ -354,4 +368,9 @@ BOOL (id object) object_is_meta_class = #0; return self; } +- (string) describe +{ + return sprintf ("[%s@%p]", object_get_class_name (self), self); +} + @end diff --git a/ruamoko/lib/PropertyList.r b/ruamoko/lib/PropertyList.r index 0bcfa9850..c165d7192 100644 --- a/ruamoko/lib/PropertyList.r +++ b/ruamoko/lib/PropertyList.r @@ -1,4 +1,4 @@ -#include "PropertyList.h" +#include @implementation PLItem @@ -22,7 +22,7 @@ return [PLString new:str]; } -+ itemClass:(plitem_t) item ++ itemClass:(plitem_t *) item { local string classname = nil; local id class; @@ -59,31 +59,26 @@ return [[PLItem itemClass: PL_GetFromFile (file)] autorelease]; } -- initWithItem:(plitem_t) item +- initWithItem:(plitem_t *) item { if (!(self = [super init])) return self; + PL_Retain (item); self.item = item; - own = 0; - return self; -} - --initWithOwnItem:(plitem_t) item -{ - if (!(self = [super init])) - return self; - self.item = item; - own = 1; return self; } - (void) dealloc { - if (own) - PL_Free (item); + PL_Release (item); [super dealloc]; } +- (plitem_t *) item +{ + return item; +} + - (string) write { return PL_WritePropertyList (item); @@ -94,6 +89,73 @@ return PL_Type (item); } +- (int) line +{ + return PL_Line (item); +} + +- (int) count +{ + if ([self class] == [PLDictionary class]) { + return PL_D_NumKeys (item); + } else { + return PL_A_NumObjects (item); + } +} + +- (int) numKeys +{ + return PL_D_NumKeys (item); +} + +- (PLItem *) getObjectForKey:(string) key +{ + return [[PLItem itemClass: PL_ObjectForKey (item, key)] autorelease]; +} + +- (PLItem *) allKeys +{ + return [[PLItem itemClass: PL_D_AllKeys (item)] autorelease]; +} + +- (string) keyAtIndex:(int) index +{ + return PL_KeyAtIndex (item, index); +} + +- addKey:(string) key value:(PLItem *) value +{ + PL_D_AddObject (item, key, value.item); + return self; +} + +- (int) numObjects +{ + return PL_A_NumObjects (item); +} + +- (PLItem *) getObjectAtIndex:(int) index +{ + return [[PLItem itemClass: PL_ObjectAtIndex (item, index)] autorelease]; +} + +- addObject:(PLItem *) object +{ + PL_A_AddObject (item, object.item); + return self; +} + +- insertObject:(PLItem *) object atIndex:(int) index +{ + PL_A_InsertObjectAtIndex (item, object.item, index); + return self; +} + +- (string) string +{ + return PL_String (item); +} + @end @@ -101,7 +163,17 @@ + (PLDictionary *) new { - return [[PLDictionary alloc] initWithOwnItem: PL_NewDictionary ()]; + return [[PLDictionary alloc] initWithItem: PL_NewDictionary ()]; +} + ++ (PLItem *) fromString:(string) str +{ + return [[PLItem itemClass: PL_GetDictionary (str)] autorelease]; +} + ++ (PLItem *) fromFile:(QFile) file +{ + return [[PLItem itemClass: PL_GetDictionaryFromFile (file)] autorelease]; } - (int) count @@ -126,13 +198,7 @@ - addKey:(string) key value:(PLItem *) value { - if (!value.own) { - obj_error (self, 0, "add of unowned key/value to PLDictionary"); - return self; - } PL_D_AddObject (item, key, value.item); - value.own = 0; - [value release]; return self; } @@ -142,7 +208,17 @@ + (PLArray *) new { - return [[PLArray alloc] initWithOwnItem: PL_NewArray ()]; + return [[PLArray alloc] initWithItem: PL_NewArray ()]; +} + ++ (PLItem *) fromString:(string) str +{ + return [[PLItem itemClass: PL_GetArray (str)] autorelease]; +} + ++ (PLItem *) fromFile:(QFile) file +{ + return [[PLItem itemClass: PL_GetArrayFromFile (file)] autorelease]; } - (int) count @@ -162,25 +238,13 @@ - addObject:(PLItem *) object { - if (!object.own) { - obj_error (self, 0, "add of unowned object to PLArray"); - return self; - } PL_A_AddObject (item, object.item); - object.own = 0; - [object release]; return self; } - insertObject:(PLItem *) object atIndex:(int) index { - if (!object.own) { - obj_error (self, 0, "add of unowned object to PLArray"); - return self; - } PL_A_InsertObjectAtIndex (item, object.item, index); - object.own = 0; - [object release]; return self; } @@ -190,7 +254,7 @@ + (PLData *) new:(void*) data size:(int) len { - return [[PLData alloc] initWithOwnItem: PL_NewData (data, len)]; + return [[PLData alloc] initWithItem: PL_NewData (data, len)]; } @end @@ -199,7 +263,7 @@ + (PLString *) new: (string) str { - return [[PLString alloc] initWithOwnItem: PL_NewString (str)]; + return [[PLString alloc] initWithItem: PL_NewString (str)]; } - (string) string diff --git a/ruamoko/lib/Protocol.r b/ruamoko/lib/Protocol.r index 715ca30a7..8893d13bb 100644 --- a/ruamoko/lib/Protocol.r +++ b/ruamoko/lib/Protocol.r @@ -1,4 +1,4 @@ -#include "Protocol.h" +#include struct obj_protocol_list { struct obj_protocol_list *next; diff --git a/ruamoko/lib/Set.r b/ruamoko/lib/Set.r index 10cf6397d..f3234290e 100644 --- a/ruamoko/lib/Set.r +++ b/ruamoko/lib/Set.r @@ -1,6 +1,7 @@ -#include "Set.h" +#include void set_del_iter (set_iter_t *set_iter) = #0; +unsigned set_iter_element (set_iter_t *set_iter) = #0; set_t *set_new (void) = #0; void set_delete (set_t *set) = #0; set_t *set_add (set_t *set, unsigned x) = #0; @@ -20,18 +21,39 @@ int set_is_intersecting (set_t *s1, set_t *s2) = #0; int set_is_equivalent (set_t *s1, set_t *s2) = #0; int set_is_subset (set_t *set, set_t *sub) = #0; int set_is_member (set_t *set, unsigned x) = #0; -unsigned set_size (set_t *set) = #0; +unsigned set_count (set_t *set) = #0; set_iter_t *set_first (set_t *set) = #0; set_iter_t *set_next (set_iter_t *set_iter) = #0; string set_as_string (set_t *set) = #0; @implementation SetIterator: Object + +- initWithSet: (Set *)set iterator:(set_iter_t *) iter +{ + if (!(self = [super init])) { + return nil; + } + self.set = [set retain]; + self.iter = iter; + return self; +} + ++ (id) withSet: (Set *)set iterator:(set_iter_t *) iter +{ + return [[[self alloc] initWithSet: set iterator: iter] autorelease]; +} + +- (void) dealloc +{ + [set release]; + [super dealloc]; +} + - (SetIterator *) next { if ((iter = set_next (iter))) return self; - [self dealloc]; return nil; } @@ -58,6 +80,11 @@ string set_as_string (set_t *set) = #0; [super dealloc]; } +- (string) describe +{ + return set_as_string (set); +} + - (Set *) add: (unsigned) x = #0; - (Set *) remove: (unsigned) x = #0; - (Set *) invert = #0; @@ -84,8 +111,7 @@ string set_as_string (set_t *set) = #0; if (!iter) return nil; - iterator = [[SetIterator alloc] init]; - iterator.iter = iter; + iterator = [SetIterator withSet: self iterator: iter]; return iterator; } diff --git a/ruamoko/lib/cbuf.r b/ruamoko/lib/cbuf.r index 496585fd5..df00ccc81 100644 --- a/ruamoko/lib/cbuf.r +++ b/ruamoko/lib/cbuf.r @@ -1,6 +1,6 @@ -#include "cbuf.h" +#include -void (string text) Cbuf_AddText = #0; -void (string text) Cbuf_InsertText = #0; -void () Cbuf_Execute = #0; -void () Cbuf_Execute_Sets = #0; +void Cbuf_AddText (cbuf_t cbuf, string text) = #0; +void Cbuf_InsertText (cbuf_t cbuf, string text) = #0; +void Cbuf_Execute (cbuf_t cbuf) = #0; +void Cbuf_Execute_Sets (cbuf_t cbuf) = #0; diff --git a/ruamoko/lib/cmd.r b/ruamoko/lib/cmd.r index 2ecb6d100..ba86632f8 100644 --- a/ruamoko/lib/cmd.r +++ b/ruamoko/lib/cmd.r @@ -1,4 +1,4 @@ -#include "cmd.h" +#include void (string name, void () func) Cmd_AddCommand = #0; int () Cmd_Argc = #0; diff --git a/ruamoko/lib/crudefile.r b/ruamoko/lib/crudefile.r index c8d4b9e27..2987af452 100644 --- a/ruamoko/lib/crudefile.r +++ b/ruamoko/lib/crudefile.r @@ -1,4 +1,4 @@ -#include "crudefile.h" +#include float (string path, string mode) cfopen = #0x000f0000 + 103; void (float desc) cfclose = #0x000f0000 + 104; diff --git a/ruamoko/lib/cvar.r b/ruamoko/lib/cvar.r index 168c38e18..be12b212e 100644 --- a/ruamoko/lib/cvar.r +++ b/ruamoko/lib/cvar.r @@ -1,4 +1,4 @@ -#include "cvar.h" +#include float (string s) cvar = #45; void (string var, string val) cvar_set = #72; diff --git a/ruamoko/lib/debug.r b/ruamoko/lib/debug.r index 844c88eff..36dcf43d2 100644 --- a/ruamoko/lib/debug.r +++ b/ruamoko/lib/debug.r @@ -1,10 +1,10 @@ -#include "debug.h" +#include void abort (void) = #6; void coredump (void) = #28; void traceon (void) = #29; void traceoff (void) = #30; void eprint (entity e) = #31; -void dprint (string str) = #25; -void error (string str) = #10; -void objerror (string e) = #11; +void dprint (string s) = #25; +void error (.../*string str*/) = #10; +void objerror (.../*string e*/) = #11; diff --git a/ruamoko/lib/draw.r b/ruamoko/lib/draw.r index 741857bf5..65311595f 100644 --- a/ruamoko/lib/draw.r +++ b/ruamoko/lib/draw.r @@ -1,4 +1,7 @@ -#include "draw.h" +#include + +int Draw_Width (void) = #0; +int Draw_Height (void) = #0; void Draw_FreePic (qpic_t pic) = #0; qpic_t Draw_MakePic (int width, int heiight, string data) = #0; @@ -14,6 +17,7 @@ void (int x, int y, string text) Draw_String = #0; void (int x, int y, string text, int n) Draw_nString = #0; void (int x, int y, string text) Draw_AltString = #0; void (int x, int y, int w, int h, int c) Draw_Fill = #0; +void (int x0, int y0, int x1, int y1, int c) Draw_Line = #0; void (int ch, int x, int y) Draw_Crosshair = #0; void (int x, int y, int width, int lines) text_box = diff --git a/ruamoko/lib/entities.r b/ruamoko/lib/entities.r index f55bd91a7..0af69f189 100644 --- a/ruamoko/lib/entities.r +++ b/ruamoko/lib/entities.r @@ -1,4 +1,4 @@ -#include "entities.h" +#include void setmodel (entity e, string m) = #3; void setorigin (entity e, vector o) = #2; @@ -8,7 +8,7 @@ void remove (entity e) = #15; #ifdef __VERSION6__ entity find (entity start, .string field, string match) = #18; #else -entity find (entity start, ...) = #18; +entity @attribute(no_va_list) find (entity start, ...) = #18; #endif entity findradius (vector origin, float radius) = #22; entity nextent (entity e) = #47; diff --git a/ruamoko/lib/file.r b/ruamoko/lib/file.r deleted file mode 100644 index 02494d317..000000000 --- a/ruamoko/lib/file.r +++ /dev/null @@ -1,3 +0,0 @@ -#include "file.h" - -QFile (string path, string mode) File_Open = #0; diff --git a/ruamoko/lib/gib.r b/ruamoko/lib/gib.r index c3fead7b1..26baacd2f 100644 --- a/ruamoko/lib/gib.r +++ b/ruamoko/lib/gib.r @@ -1,4 +1,4 @@ -#include "gib.h" +#include void GIB_Builtin_Add (string name, void func (int argc, string *argv)) = #0; int (string value) GIB_Return = #0; diff --git a/ruamoko/lib/hash.r b/ruamoko/lib/hash.r index 24844bfc2..120d140f9 100644 --- a/ruamoko/lib/hash.r +++ b/ruamoko/lib/hash.r @@ -1,19 +1,19 @@ -#include "hash.h" +#include -hashtab_t Hash_NewTable (int size, string gk (void *ele, void *data), void f (void *ele, void *data), void *ud) = #0; -void Hash_SetHashCompare (hashtab_t tab, unsigned gh (void *ele, void *data), int cmp (void *ele1, void *ele2, void *data)) = #0; -void Hash_DelTable (hashtab_t tab) = #0; -void Hash_FlushTable (hashtab_t tab) = #0; -int Hash_Add (hashtab_t tab, void *ele) = #0; -int Hash_AddElement (hashtab_t tab, void *ele) = #0; -void *Hash_Find (hashtab_t tab, string key) = #0; -void *Hash_FindElement (hashtab_t tab, void *ele) = #0; -void **Hash_FindList (hashtab_t tab, string key) = #0; -void **Hash_FindElementList (hashtab_t tab, void *ele) = #0; -void *Hash_Del (hashtab_t tab, string key) = #0; -void *Hash_DelElement (hashtab_t tab, void *ele) = #0; -void Hash_Free (hashtab_t tab, void *ele) = #0; +hashtab_t *Hash_NewTable (int size, string gk (void *ele, void *data), void f (void *ele, void *data), void *ud) = #0; +void Hash_SetHashCompare (hashtab_t *tab, unsigned gh (void *ele, void *data), int cmp (void *ele1, void *ele2, void *data)) = #0; +void Hash_DelTable (hashtab_t *tab) = #0; +void Hash_FlushTable (hashtab_t *tab) = #0; +int Hash_Add (hashtab_t *tab, void *ele) = #0; +int Hash_AddElement (hashtab_t *tab, void *ele) = #0; +void *Hash_Find (hashtab_t *tab, string key) = #0; +void *Hash_FindElement (hashtab_t *tab, void *ele) = #0; +void **Hash_FindList (hashtab_t *tab, string key) = #0; +void **Hash_FindElementList (hashtab_t *tab, void *ele) = #0; +void *Hash_Del (hashtab_t *tab, string key) = #0; +void *Hash_DelElement (hashtab_t *tab, void *ele) = #0; +void Hash_Free (hashtab_t *tab, void *ele) = #0; int Hash_String (string str) = #0; int Hash_Buffer (void *buf, int len) = #0; -void **Hash_GetList (hashtab_t tab) = #0; -void Hash_Stats (hashtab_t tab) = #0; +void **Hash_GetList (hashtab_t *tab) = #0; +void Hash_Stats (hashtab_t *tab) = #0; diff --git a/ruamoko/lib/infokey.r b/ruamoko/lib/infokey.r index d0049a4fd..a2913df18 100644 --- a/ruamoko/lib/infokey.r +++ b/ruamoko/lib/infokey.r @@ -1,4 +1,4 @@ -#include "infokey.h" +#include string (entity e, string key) infokey = #80; void (entity ent, string key, string value) setinfokey = #0x000f0000 + 102; diff --git a/ruamoko/lib/input.r b/ruamoko/lib/input.r new file mode 100644 index 000000000..fa91c6b1f --- /dev/null +++ b/ruamoko/lib/input.r @@ -0,0 +1,42 @@ +#include "input.h" + +void IN_LoadConfig (struct plitem_s *config) = #0; +in_button_t *IN_CreateButton (string name, string description) = #0; +in_axis_t *IN_CreateAxis (string name, string description) = #0; +int IN_FindDeviceId (string _id) = #0; +string IN_GetDeviceName (int devid) = #0; +string IN_GetDeviceId (int devid) = #0; +//IN_AxisInfo () = #0; +//IN_ButtonInfo () = #0; +string IN_GetAxisName (int devid, int axis) = #0; +string IN_GetButtonName (int devid, int button) = #0; +int IN_GetAxisNumber (int devid, string axis) = #0; +int IN_GetButtonNumber (int devid, string button) = #0; +void IN_ProcessEvents (void) = #0; +void IN_ClearStates (void) = #0; +int IN_GetAxisInfo (int devid, int axis, in_axisinfo_t *info) = #0; +int IN_GetButtonInfo (int devid, int button, in_buttoninfo_t *info) = #0; +@overload +void IN_ButtonAddListener (in_button_t *button, button_listener_t listener, + void *data) = #0; +@overload +void IN_ButtonRemoveListener (in_button_t *button, button_listener_t listener, + void *data) = #0; +@overload +void IN_AxisAddListener (in_axis_t *axis, axis_listener_t listener, + void *data) = #0; +@overload +void IN_AxisRemoveListener (in_axis_t *axis, axis_listener_t listener, + void *data) = #0; +@overload +void IN_ButtonAddListener (in_button_t *button, IMP listener, id obj) = #0; +@overload +void IN_ButtonRemoveListener (in_button_t *button, IMP listener, id obj) = #0; +@overload +void IN_AxisAddListener (in_axis_t *axis, IMP listener, id obj) = #0; +@overload +void IN_AxisRemoveListener (in_axis_t *axis, IMP listener, id obj) = #0; + +int IMT_CreateContext (string name) = #0; +int IMT_GetContext (void) = #0; +void IMT_SetContext (int ctx) = #0; diff --git a/ruamoko/lib/key.r b/ruamoko/lib/key.r index 19662f138..812800128 100644 --- a/ruamoko/lib/key.r +++ b/ruamoko/lib/key.r @@ -1,7 +1,8 @@ -#include "key.h" +#include int Key_keydown (int keynum) = #0; string (string imt, int keynum, string binding) Key_SetBinding = #0; int (string imt, int bindnum, string binding) Key_LookupBinding = #0; int (string imt, string binding) Key_CountBinding = #0; string (int keynum) Key_KeynumToString = #0; +int (string keyname) Key_StringToKeynum = #0; diff --git a/ruamoko/lib/legacy_math.r b/ruamoko/lib/legacy_math.r new file mode 100644 index 000000000..41457de83 --- /dev/null +++ b/ruamoko/lib/legacy_math.r @@ -0,0 +1,15 @@ +#include + +vector v_forward, v_up, v_right; + +float () random = #7; +int (float f) ftoi = #0x000f0000 + 110; +float (int i) itof = #0x000f0000 + 111; +vector (vector v) normalize = #9; +float (vector v) vlen = #12; +float (vector v) vectoyaw = #13; +float (float v) rint = #36; +float (float v) floor = #37; +float (float v) ceil = #38; +float (float f) fabs = #43; +vector (vector v) vectoangles = #51; diff --git a/ruamoko/lib/legacy_string.r b/ruamoko/lib/legacy_string.r new file mode 100644 index 000000000..258a33ade --- /dev/null +++ b/ruamoko/lib/legacy_string.r @@ -0,0 +1,9 @@ +#include + +string (float f) ftos = #26; +string (vector v) vtos = #27; +float (string s) stof = #81; +float (string goal, string s) charcount = #0x000f0000 + 101; +string (int i) itos = #0x000f0000 + 112; +int (string s) stoi = #0x000f0000 + 113; +vector (string s) stov = #0x000f0000 + 114; diff --git a/ruamoko/lib/math.r b/ruamoko/lib/math.r index 8d0c51342..e6743781c 100644 --- a/ruamoko/lib/math.r +++ b/ruamoko/lib/math.r @@ -1,18 +1,4 @@ -#include "math.h" - -vector v_forward, v_up, v_right; - -float () random = #7; -int (float f) ftoi = #0x000f0000 + 110; -float (int i) itof = #0x000f0000 + 111; -vector (vector v) normalize = #9; -float (vector v) vlen = #12; -float (vector v) vectoyaw = #13; -float (float v) rint = #36; -float (float v) floor = #37; -float (float v) ceil = #38; -float (float f) fabs = #43; -vector (vector v) vectoangles = #51; +#include float (float x) sin = #0; float (float x) cos = #0; @@ -21,6 +7,7 @@ float (float x) asin = #0; float (float x) acos = #0; float (float x) atan = #0; float (float y, float x) atan2 = #0; +float (float x) exp = #0; float (float x) log = #0; float (float x) log2 = #0; float (float x) log10 = #0; @@ -34,3 +21,28 @@ float (float x) atanh = #0; float (float x) sqrt = #0; float (float x) cbrt = #0; float (float x, float y) hypot = #0; + +double (double v) floor = #0; +double (double v) ceil = #0; +double (double f) fabs = #0; +double (double x) sin = #0; +double (double x) cos = #0; +double (double x) tan = #0; +double (double x) asin = #0; +double (double x) acos = #0; +double (double x) atan = #0; +double (double y, double x) atan2 = #0; +double (double x) exp = #0; +double (double x) log = #0; +double (double x) log2 = #0; +double (double x) log10 = #0; +double (double x, double y) pow = #0; +double (double x) sinh = #0; +double (double x) cosh = #0; +double (double x) tanh = #0; +double (double x) asinh = #0; +double (double x) acosh = #0; +double (double x) atanh = #0; +double (double x) sqrt = #0; +double (double x) cbrt = #0; +double (double x, double y) hypot = #0; diff --git a/ruamoko/lib/mersenne.r b/ruamoko/lib/mersenne.r new file mode 100644 index 000000000..26c217f9b --- /dev/null +++ b/ruamoko/lib/mersenne.r @@ -0,0 +1,8 @@ +#include "mersenne.h" + +mtwist_t *mtwist_new (int seed) = #0; +void mtwist_delete (mtwist_t *state) = #0; +void mtwist_seed (mtwist_t *state, int seed) = #0; +int mtwist_rand (mtwist_t *state) = #0; +float mtwist_rand_0_1 (mtwist_t *state) = #0; +float mtwist_rand_m1_1 (mtwist_t *state) = #0; diff --git a/ruamoko/lib/message.r b/ruamoko/lib/message.r index 3136cd678..d592335c1 100644 --- a/ruamoko/lib/message.r +++ b/ruamoko/lib/message.r @@ -1,7 +1,7 @@ -#include "message.h" +#include -void (...) bprint = #23; -void (entity client, string s) sprint = #24; +void (float level, string s) bprint = #23; +void (entity client, float level, string s) sprint = #24; void (float to, float f) WriteByte = #52; void (float to, float f) WriteChar = #53; void (float to, float f) WriteShort = #54; @@ -13,4 +13,4 @@ void (float to, entity s) WriteEntity = #59; void (float to, ...) WriteBytes = #0; void (float to, vector v) WriteCoordV = #0; void (float to, vector v) WriteAngleV = #0; -void (...) centerprint = #73; +void (entity client, string s) centerprint = #73; diff --git a/ruamoko/lib/msgbuf.r b/ruamoko/lib/msgbuf.r index c1352a43e..1bd4e09b1 100644 --- a/ruamoko/lib/msgbuf.r +++ b/ruamoko/lib/msgbuf.r @@ -1,4 +1,4 @@ -#include "msgbuf.h" +#include msgbuf_t *MsgBuf_New (int size) = #0; void MsgBuf_Delete (msgbuf_t *msgbuf) = #0; diff --git a/ruamoko/lib/nq_message.r b/ruamoko/lib/nq_message.r index 14c06827a..223d1c604 100644 --- a/ruamoko/lib/nq_message.r +++ b/ruamoko/lib/nq_message.r @@ -1,3 +1,3 @@ -#include "nq_message.h" +#include void (vector o, vector d, float color, float count) particle = #48; diff --git a/ruamoko/lib/obj_forward.r b/ruamoko/lib/obj_forward.r new file mode 100644 index 000000000..60ad819b5 --- /dev/null +++ b/ruamoko/lib/obj_forward.r @@ -0,0 +1,3 @@ +// __obj_foward is the only thing in this file so it can be readily replaced +// at link time +void __obj_forward(id object, SEL sel, ...) = #0; diff --git a/ruamoko/lib/physics.r b/ruamoko/lib/physics.r index ca19e052f..b42585d00 100644 --- a/ruamoko/lib/physics.r +++ b/ruamoko/lib/physics.r @@ -1,4 +1,4 @@ -#include "physics.h" +#include float trace_allsolid; float trace_startsolid; diff --git a/ruamoko/lib/plist.r b/ruamoko/lib/plist.r index 4b01c653b..724eac34c 100644 --- a/ruamoko/lib/plist.r +++ b/ruamoko/lib/plist.r @@ -1,22 +1,29 @@ -#include "plist.h" +#include -plitem_t PL_GetFromFile (QFile file) = #0; -plitem_t PL_GetPropertyList (string str) = #0; -string PL_WritePropertyList (plitem_t pl) = #0; -pltype_t PL_Type (plitem_t str) = #0; -string PL_String (plitem_t str) = #0; -plitem_t PL_ObjectForKey (plitem_t item, string key) = #0; -plitem_t PL_RemoveObjectForKey (plitem_t item, string key) = #0; -plitem_t PL_ObjectAtIndex (plitem_t item, int index) = #0; -plitem_t PL_D_AllKeys (plitem_t item) = #0; -int PL_D_NumKeys (plitem_t item) = #0; -int PL_D_AddObject (plitem_t dict, string key, plitem_t value) = #0; -int PL_A_AddObject (plitem_t array_item, plitem_t item) = #0; -int PL_A_NumObjects (plitem_t item) = #0; -int PL_A_InsertObjectAtIndex (plitem_t array_item, plitem_t item, int index) = #0; -plitem_t PL_RemoveObjectAtIndex (plitem_t array_item, int index) = #0; -plitem_t PL_NewDictionary (void) = #0; -plitem_t PL_NewArray (void) = #0; -plitem_t PL_NewData (void *data, int len) = #0; -plitem_t PL_NewString (string str) = #0; -void PL_Free (plitem_t pl) = #0; +plitem_t *PL_GetFromFile (QFile file) = #0; +plitem_t *PL_GetPropertyList (string str) = #0; +plitem_t *PL_GetDictionaryFromFile (QFile file) = #0; +plitem_t *PL_GetDictionary (string str) = #0; +plitem_t *PL_GetArrayFromFile (QFile file) = #0; +plitem_t *PL_GetArray (string str) = #0; +string PL_WritePropertyList (plitem_t *pl) = #0; +pltype_t PL_Type (plitem_t *str) = #0; +int PL_Line (plitem_t *str) = #0; +string PL_String (plitem_t *str) = #0; +plitem_t *PL_ObjectForKey (plitem_t *item, string key) = #0; +void PL_RemoveObjectForKey (plitem_t *item, string key) = #0; +plitem_t *PL_ObjectAtIndex (plitem_t *item, int index) = #0; +plitem_t *PL_D_AllKeys (plitem_t *item) = #0; +string PL_KeyAtIndex (plitem_t *item, int index) = #0; +int PL_D_NumKeys (plitem_t *item) = #0; +int PL_D_AddObject (plitem_t *dict, string key, plitem_t *value) = #0; +int PL_A_AddObject (plitem_t *array_item, plitem_t *item) = #0; +int PL_A_NumObjects (plitem_t *item) = #0; +int PL_A_InsertObjectAtIndex (plitem_t *array_item, plitem_t *item, int index) = #0; +void PL_RemoveObjectAtIndex (plitem_t *array_item, int index) = #0; +plitem_t *PL_NewDictionary (void) = #0; +plitem_t *PL_NewArray (void) = #0; +plitem_t *PL_NewData (void *data, int len) = #0; +plitem_t *PL_NewString (string str) = #0; +plitem_t *PL_Retain (plitem_t *pl) = #0; +plitem_t *PL_Release (plitem_t *pl) = #0; diff --git a/ruamoko/lib/qfile.r b/ruamoko/lib/qfile.r index 072d72a1c..487beb5c5 100644 --- a/ruamoko/lib/qfile.r +++ b/ruamoko/lib/qfile.r @@ -1,4 +1,4 @@ -#include "qfile.h" +#include int Qrename (string old, string new) = #0; int Qremove (string path) = #0; diff --git a/ruamoko/lib/qfs.r b/ruamoko/lib/qfs.r index e1d374fc4..d467b09ce 100644 --- a/ruamoko/lib/qfs.r +++ b/ruamoko/lib/qfs.r @@ -1,4 +1,4 @@ -#include "qfs.h" +#include QFile QFS_Open (string path, string mode) = #0; QFile QFS_WOpen (string path, int zip) = #0; @@ -8,3 +8,4 @@ QFile QFS_OpenFile (string filename) = #0; int QFS_WriteFile (string filename, void *buf, int count) = #0; QFSlist QFS_Filelist (string path, string ext, int strip) = #0; void QFS_FilelistFree (QFSlist list) = #0; +string QFS_GetDirectory (void) = #0; diff --git a/ruamoko/lib/qw_message.r b/ruamoko/lib/qw_message.r index fcc714c37..218db52cd 100644 --- a/ruamoko/lib/qw_message.r +++ b/ruamoko/lib/qw_message.r @@ -1,3 +1,3 @@ -#include "qw_message.h" +#include void (vector where, float set) multicast = #82; diff --git a/ruamoko/lib/qw_physics.r b/ruamoko/lib/qw_physics.r index 431e97e63..0ff633569 100644 --- a/ruamoko/lib/qw_physics.r +++ b/ruamoko/lib/qw_physics.r @@ -1,4 +1,4 @@ -#include "qw_physics.h" +#include entity (entity ent) testentitypos = #0x000f0000 + 92; void (vector start, vector mins, vector maxs, vector end, float type, entity passent) checkmove = #0x000f0000 + 98; diff --git a/ruamoko/lib/qw_sys.r b/ruamoko/lib/qw_sys.r index 8d8643576..529f42947 100644 --- a/ruamoko/lib/qw_sys.r +++ b/ruamoko/lib/qw_sys.r @@ -1,3 +1,3 @@ -#include "qw_sys.h" +#include void (entity killer, entity killee) logfrag = #79; diff --git a/ruamoko/lib/scene.r b/ruamoko/lib/scene.r new file mode 100644 index 000000000..a4ec96db6 --- /dev/null +++ b/ruamoko/lib/scene.r @@ -0,0 +1,47 @@ +#include + +scene_t Scene_NewScene (void) = #0; +void Scene_DeleteScene (scene_t scene) = #0; +entity_t Scene_CreateEntity (scene_t scene) = #0; +void Scene_DestroyEntity (entity_t ent) = #0; +void Scene_SetLighting (scene_t scene, lightingdata_t ldata) = #0; + +transform_t Entity_GetTransform (entity_t ent) = #0; +void Entity_SetModel (entity_t ent, model_t model) = #0; + +unsigned Transform_ChildCount (transform_t transform) = #0; +transform_t Transform_GetChild (transform_t transform, + unsigned childIndex) = #0; +void Transform_SetParent (transform_t transform, transform_t parent) = #0; +transform_t Transform_GetParent (transform_t transform) = #0; +void Transform_SetTag (transform_t transform, unsigned tag) = #0; +unsigned Transform_GetTag (transform_t transform) = #0; +mat4x4 Transform_GetLocalMatrix (transform_t transform) = #0; +mat4x4 Transform_GetLocalInverse (transform_t transform) = #0; +mat4x4 Transform_GetWorldMatrix (transform_t transform) = #0; +mat4x4 Transform_GetWorldInverse (transform_t transform) = #0; +vec4 Transform_GetLocalPosition (transform_t transform) = #0; +void Transform_SetLocalPosition (transform_t transform, vec4 position) = #0; +vec4 Transform_GetLocalRotation (transform_t transform) = #0; +void Transform_SetLocalRotation (transform_t transform, vec4 rotation) = #0; +vec4 Transform_GetLocalScale (transform_t transform) = #0; +void Transform_SetLocalScale (transform_t transform, vec4 scale) = #0; +vec4 Transform_GetWorldPosition (transform_t transform) = #0; +void Transform_SetWorldPosition (transform_t transform, vec4 position) = #0; +vec4 Transform_GetWorldRotation (transform_t transform) = #0; +void Transform_SetWorldRotation (transform_t transform, vec4 rotation) = #0; +vec4 Transform_GetWorldScale (transform_t transform) = #0; +void Transform_SetLocalTransform (transform_t transform, vec4 scale, + vec4 rotation, vec4 position) = #0; +vec4 Transform_Forward (transform_t transform) = #0; +vec4 Transform_Right (transform_t transform) = #0; +vec4 Transform_Up (transform_t transform) = #0; + +lightingdata_t Light_CreateLightingData (scene_t scene) = #0; +void Light_DestroyLightingData (lightingdata_t ldata) = #0; +void Light_ClearLights (lightingdata_t ldata) = #0; +void Light_AddLight (lightingdata_t ldata, light_t light, int style) = #0; +void Light_EnableSun (lightingdata_t ldata) = #0; + +model_t Model_Load (string path) = #0; +void Model_Unload (model_t model) = #0; diff --git a/ruamoko/lib/script.r b/ruamoko/lib/script.r index 74c120075..086dcdfb7 100644 --- a/ruamoko/lib/script.r +++ b/ruamoko/lib/script.r @@ -1,11 +1,15 @@ -#include "script.h" +#include +#include script_t Script_New (void) = #0; void Script_Delete (script_t script) = #0; // returns the token string string Script_Start (script_t script, string file, string data) = #0; +string Script_FromFile (script_t script, string filename, QFile file) = #0; int Script_TokenAvailable (script_t script, int crossline) = #0; int Script_GetToken (script_t script, int crossline) = #0; void Script_UngetToken (script_t script) = #0; string Script_Error (script_t script) = #0; int Script_NoQuoteLines (script_t script) = #0; +void Script_SetSingle (script_t script, string single) = #0; +int Script_GetLine (script_t script) = #0; diff --git a/ruamoko/lib/server.r b/ruamoko/lib/server.r index 42bd99eb5..ab9ef0e6e 100644 --- a/ruamoko/lib/server.r +++ b/ruamoko/lib/server.r @@ -1,7 +1,7 @@ -#include "server.h" +#include -void (string s) precache_sound = #19; -void (string s) precache_model = #20; +string (string s) precache_sound = #19; +string (string s) precache_model = #20; void (entity client, string s) stuffcmd = #21; void (string s) localcmd = #46; void (string s) changelevel = #70; diff --git a/ruamoko/lib/sound.r b/ruamoko/lib/sound.r index 863800c81..000ef7047 100644 --- a/ruamoko/lib/sound.r +++ b/ruamoko/lib/sound.r @@ -1,3 +1,3 @@ -#include "sound.h" +#include void (string sound) S_LocalSound = #0; diff --git a/ruamoko/lib/stdlib.r b/ruamoko/lib/stdlib.r new file mode 100644 index 000000000..e1145e913 --- /dev/null +++ b/ruamoko/lib/stdlib.r @@ -0,0 +1,11 @@ +#include + +void *bsearch (void *key, void *array, int nmemb, int size, + int (*compare) (void *a, void *b)) = #0; +void *fbsearch (void *key, void *array, int nmemb, int size, + int (*compare) (void *a, void *b)) = #0; +void *qsort (void *array, int nmemb, int size, + int (*compare) (void *a, void *b)) = #0; + +void prefixsum (int *array, int count) = #0; +void prefixsum (float *array, int count) = #0; diff --git a/ruamoko/lib/string.r b/ruamoko/lib/string.r index f7eda6b48..0e0cb6b3b 100644 --- a/ruamoko/lib/string.r +++ b/ruamoko/lib/string.r @@ -1,20 +1,25 @@ -#include "string.h" - -string (float f) ftos = #26; -string (vector v) vtos = #27; -float (string s) stof = #81; -float (string s) strlen = #0x000f0000 + 100; -float (string goal, string s) charcount = #0x000f0000 + 101; -string (string fmt, ...) sprintf = #0x000f0000 + 109; -string (int i) itos = #0x000f0000 + 112; -int (string s) stoi = #0x000f0000 + 113; -vector (string s) stov = #0x000f0000 + 114; +#include +int (string s) strlen = #0; +string (string fmt, ...) sprintf = #0; +string vsprintf (string fmt, @va_list args) = #0; string (void) str_new = #0; -string (string str) str_free = #0; +string str_unmutable (string str) = #0; +void (string str) str_free = #0; +string str_hold (string str) = #0; +int str_valid (string str) = #0; +int str_mutable (string str) = #0; string (string dst, string src) str_copy = #0; string (string dst, string src) str_cat = #0; string (string str) str_clear = #0; string (string str, int start) str_mid = #0; string (string str, int start, int len) str_mid = #0; -string (string haystack, string needle) str_str = #0; +int str_str (string haystack, string needle) = #0; +int str_char (string str, int ind) = #0; +string str_quote (string str) = #0; +string str_lower (string str) = #0; +string str_upper (string str) = #0; +double strtod (string str, int *end) = #0; +float strtof (string str, int *end) = #0; +long strtol (string str, int *end, int base) = #0; +unsigned long strtoul (string str, int *end, int base) = #0; diff --git a/ruamoko/lib/sv_sound.r b/ruamoko/lib/sv_sound.r index 7880c6b1b..cd4b4f43e 100644 --- a/ruamoko/lib/sv_sound.r +++ b/ruamoko/lib/sv_sound.r @@ -1,4 +1,4 @@ -#include "sound.h" +#include void (entity e, float chan, string samp, float vol, float atten) sound = #8; void (vector pos, string samp, float vol, float atten) ambientsound = #74; diff --git a/ruamoko/lib/system.r b/ruamoko/lib/system.r index 1a387f890..753a41d99 100644 --- a/ruamoko/lib/system.r +++ b/ruamoko/lib/system.r @@ -1,4 +1,4 @@ -#include "system.h" +#include float time; diff --git a/ruamoko/lib/types.r b/ruamoko/lib/types.r new file mode 100644 index 000000000..7c27914ff --- /dev/null +++ b/ruamoko/lib/types.r @@ -0,0 +1,30 @@ +#include +#include + +string ty_meta_name[7] = { + "basic", + "struct", + "union", + "enum", + "array", + "class", + "alias", +}; + +//FIXME use pr_type_names.h, but need to fix unsigned, and add missing types +#define field .int +#define func void()(void) +#define ptr void * +#define uint unsigned +#define ulong unsigned long +#define ushort unsigned short +#define EV_TYPE(type) sizeof(type), +int pr_type_size[ev_type_count] = { +#include +}; + +#define EV_TYPE(type) #type, +string pr_type_name[ev_type_count] = { +#include + "invalid", +}; diff --git a/ruamoko/lib/va_list.r b/ruamoko/lib/va_list.r new file mode 100644 index 000000000..399e05933 --- /dev/null +++ b/ruamoko/lib/va_list.r @@ -0,0 +1,3 @@ +#include + +@va_list va_copy(@va_list src) = #0; diff --git a/tools/qwaq/.gdbinit b/ruamoko/qwaq/.gdbinit similarity index 100% rename from tools/qwaq/.gdbinit rename to ruamoko/qwaq/.gdbinit diff --git a/ruamoko/qwaq/Makemodule.am b/ruamoko/qwaq/Makemodule.am new file mode 100644 index 000000000..0ab3926a4 --- /dev/null +++ b/ruamoko/qwaq/Makemodule.am @@ -0,0 +1,221 @@ +QWAQ_LIBS=@QWAQ_LIBS@ +QWAQ_DEPS=@QWAQ_DEPS@ +QWAQ_INCS=@QWAQ_INCS@ + +noinst_PROGRAMS += @QWAQ_TARGETS@ ruamoko/qwaq/qwaq-app.dat$(EXEEXT) ruamoko/qwaq/gcd.dat$(EXEEXT) ruamoko/qwaq/input-app.dat$(EXEEXT) ruamoko/qwaq/z-transform.dat$(EXEEXT) + +libui=ruamoko/qwaq/libui.a +noinst_LIBRARIES += $(libui) + +ruamoko_qwaq_libui_a_SOURCES= \ + ruamoko/qwaq/ui/button.r \ + ruamoko/qwaq/ui/draw.r \ + ruamoko/qwaq/ui/garray.r \ + ruamoko/qwaq/ui/group.r \ + ruamoko/qwaq/ui/listener.r \ + ruamoko/qwaq/ui/proxyview.r \ + ruamoko/qwaq/ui/rect.r \ + ruamoko/qwaq/ui/scrollbar.r \ + ruamoko/qwaq/ui/stringview.r \ + ruamoko/qwaq/ui/tableview.r \ + ruamoko/qwaq/ui/textcontext.r \ + ruamoko/qwaq/ui/titlebar.r \ + ruamoko/qwaq/ui/view.r \ + ruamoko/qwaq/ui/window.r +ruamoko_qwaq_libui_a_dep=$(call qcautodep,$(ruamoko_qwaq_libui_a_SOURCES)) +ruamoko_qwaq_libui_a_AR=$(PAK) -cf +EXTRA_ruamoko_qwaq_libui_a_DEPENDENCIES=$(PAK) +include $(ruamoko_qwaq_libui_a_dep) # am--include-marker +r_depfiles_remade += $(ruamoko_qwaq_libui_a_dep) + +qwaq_app_dat_src= \ + ruamoko/qwaq/qwaq-app.r \ + ruamoko/qwaq/debugger/views/arrayview.r \ + ruamoko/qwaq/debugger/views/basicview.r \ + ruamoko/qwaq/debugger/views/defview.r \ + ruamoko/qwaq/debugger/views/doubleview.r \ + ruamoko/qwaq/debugger/views/entityview.r \ + ruamoko/qwaq/debugger/views/fieldview.r \ + ruamoko/qwaq/debugger/views/floatview.r \ + ruamoko/qwaq/debugger/views/funcview.r \ + ruamoko/qwaq/debugger/views/indexview.r \ + ruamoko/qwaq/debugger/views/intview.r \ + ruamoko/qwaq/debugger/views/nameview.r \ + ruamoko/qwaq/debugger/views/pointerview.r\ + ruamoko/qwaq/debugger/views/quatview.r \ + ruamoko/qwaq/debugger/views/stringview.r \ + ruamoko/qwaq/debugger/views/structview.r \ + ruamoko/qwaq/debugger/views/uintview.r \ + ruamoko/qwaq/debugger/views/vectorview.r \ + ruamoko/qwaq/debugger/views/voidview.r \ + ruamoko/qwaq/debugger/debug.r \ + ruamoko/qwaq/debugger/debugger.r \ + ruamoko/qwaq/debugger/localsdata.r \ + ruamoko/qwaq/debugger/typeencodings.r \ + ruamoko/qwaq/editor/editbuffer.r \ + ruamoko/qwaq/editor/editor.r \ + ruamoko/qwaq/editor/status.r + +qwaq_input_app_dat_src= \ + ruamoko/qwaq/device/axisdata.r \ + ruamoko/qwaq/device/axisview.r \ + ruamoko/qwaq/device/nameview.r \ + ruamoko/qwaq/device/device.r \ + ruamoko/qwaq/qwaq-input.r \ + ruamoko/qwaq/input-app.r + +qwaq_curses_libs= \ + libs/ui/libQFui.la \ + libs/input/libQFinput.la + +ruamoko_qwaq_qwaq_curses_SOURCES= \ + ruamoko/qwaq/builtins/main.c \ + ruamoko/qwaq/builtins/curses.c \ + ruamoko/qwaq/builtins/debug.c \ + ruamoko/qwaq/builtins/editbuffer.c \ + ruamoko/qwaq/builtins/qwaq-curses.c \ + ruamoko/qwaq/builtins/term-input.c \ + ruamoko/qwaq/builtins/threading.c + +ruamoko_qwaq_qwaq_curses_LDADD= $(qwaq_curses_libs) $(QWAQ_LIBS) \ + $(PANEL_LIBS) $(NCURSES_LIBS) $(PTHREAD_LDFLAGS) $(DL_LIBS) +ruamoko_qwaq_qwaq_curses_LDFLAGS= +ruamoko_qwaq_qwaq_curses_DEPENDENCIES= $(qwaq_curses_libs) $(QWAQ_DEPS) + +qwaq_cmd_libs= + +ruamoko_qwaq_qwaq_cmd_SOURCES= \ + ruamoko/qwaq/builtins/main.c \ + ruamoko/qwaq/builtins/qwaq-cmd.c + +ruamoko_qwaq_qwaq_cmd_LDADD= $(qwaq_cmd_libs) $(QWAQ_LIBS) \ + $(PTHREAD_LDFLAGS) $(DL_LIBS) +ruamoko_qwaq_qwaq_cmd_LDFLAGS= +ruamoko_qwaq_qwaq_cmd_DEPENDENCIES= $(qwaq_cmd_libs) $(QWAQ_DEPS) + +qwaq_cl_plugin_libs= \ + @client_static_plugin_libs@ + +qwaq_client_libs= \ + $(top_builddir)/libs/scene/libQFscene.la \ + $(top_builddir)/libs/console/libQFconsole.la \ + $(top_builddir)/libs/ui/libQFui.la \ + $(top_builddir)/libs/ecs/libQFecs.la \ + $(top_builddir)/libs/input/libQFinput.la \ + $(top_builddir)/libs/audio/libQFcd.la \ + $(top_builddir)/libs/audio/libQFsound.la \ + $(top_builddir)/libs/image/libQFimage.la + +qwaq_x11_libs= \ + $(qwaq_cl_plugin_libs) \ + ${top_builddir}/libs/gib/libQFgib.la \ + ${top_builddir}/libs/ruamoko/libQFruamoko_client.la \ + $(top_builddir)/libs/ui/libQFgui.la \ + $(top_builddir)/libs/video/renderer/libQFrenderer.la \ + $(top_builddir)/libs/models/libQFmodels.la \ + $(top_builddir)/libs/video/targets/libQFx11.la \ + $(qwaq_client_libs) +ruamoko_qwaq_qwaq_x11_SOURCES= \ + ruamoko/qwaq/builtins/main.c \ + ruamoko/qwaq/builtins/qwaq-graphics.c \ + ruamoko/qwaq/builtins/graphics.c +ruamoko_qwaq_qwaq_x11_LDADD= $(qwaq_x11_libs) $(QWAQ_LIBS) \ + $(VIDMODE_LIBS) $(DGA_LIBS) ${XFIXES_LIBS} $(XI2_LIBS) $(X_LIBS) \ + -lX11 $(X_EXTRA_LIBS) $(X_SHM_LIB) $(PTHREAD_LDFLAGS) $(DL_LIBS) +ruamoko_qwaq_qwaq_x11_LDFLAGS= +ruamoko_qwaq_qwaq_x11_DEPENDENCIES= $(qwaq_x11_libs) $(QWAQ_DEPS) + +ruamoko_qwaq_qwaq_app_dat_SOURCES=$(qwaq_app_dat_src) +ruamoko_qwaq_qwaq_app_obj=$(ruamoko_qwaq_qwaq_app_dat_SOURCES:.r=.o) +ruamoko_qwaq_qwaq_app_dep=$(call qcautodep,$(ruamoko_qwaq_qwaq_app_dat_SOURCES:.o=.Qo)) +ruamoko/qwaq/qwaq-app.dat$(EXEEXT): $(ruamoko_qwaq_qwaq_app_obj) $(QFCC_DEP) $(libui) ruamoko/lib/libr.a + $(V_QFCCLD)$(QLINK) -o $@ $(ruamoko_qwaq_qwaq_app_obj) $(libui) -lr +include $(ruamoko_qwaq_qwaq_app_dep) # am--include-marker +r_depfiles_remade += $(ruamoko_qwaq_qwaq_app_dep) + +ruamoko_qwaq_input_app_dat_SOURCES=$(qwaq_input_app_dat_src) +ruamoko_qwaq_input_app_obj=$(ruamoko_qwaq_input_app_dat_SOURCES:.r=.o) +ruamoko_qwaq_input_app_dep=$(call qcautodep,$(ruamoko_qwaq_input_app_dat_SOURCES:.o=.Qo)) +ruamoko/qwaq/input-app.dat$(EXEEXT): $(ruamoko_qwaq_input_app_obj) $(QFCC_DEP) $(libui) ruamoko/lib/libr.a + $(V_QFCCLD)$(QLINK) -o $@ $(ruamoko_qwaq_input_app_obj) $(libui) -lr +include $(ruamoko_qwaq_input_app_dep) # am--include-marker +r_depfiles_remade += $(ruamoko_qwaq_input_app_dep) + +ruamoko_qwaq_gcd_dat_SOURCES=ruamoko/qwaq/gcd.r +ruamoko_qwaq_gcd_obj=$(ruamoko_qwaq_gcd_dat_SOURCES:.r=.o) +ruamoko_qwaq_gcd_dep=$(call qcautodep,$(ruamoko_qwaq_gcd_dat_SOURCES:.o=.Qo)) +ruamoko/qwaq/gcd.dat$(EXEEXT): $(ruamoko_qwaq_gcd_obj) $(QFCC_DEP) ruamoko/lib/libr.a + $(V_QFCCLD)$(QLINK) -o $@ $(ruamoko_qwaq_gcd_obj) -lr +include $(ruamoko_qwaq_gcd_dep) # am--include-marker +r_depfiles_remade += $(ruamoko_qwaq_gcd_dep) + +ruamoko_qwaq_z_transform_dat_SOURCES=ruamoko/qwaq/z-transform.r +ruamoko_qwaq_z_transform_obj=$(ruamoko_qwaq_z_transform_dat_SOURCES:.r=.o) +ruamoko_qwaq_z_transform_dep=$(call qcautodep,$(ruamoko_qwaq_z_transform_dat_SOURCES:.o=.Qo)) +ruamoko/qwaq/z-transform.dat$(EXEEXT): $(ruamoko_qwaq_z_transform_obj) $(QFCC_DEP) ruamoko/lib/libr.a + $(V_QFCCLD)$(QLINK) -o $@ $(ruamoko_qwaq_z_transform_obj) -lr +include $(ruamoko_qwaq_z_transform_dep) # am--include-marker +r_depfiles_remade += $(ruamoko_qwaq_z_transform_dep) + +EXTRA_PROGRAMS += \ + ruamoko/qwaq/qwaq-cmd \ + ruamoko/qwaq/qwaq-curses \ + ruamoko/qwaq/qwaq-x11 + +EXTRA_DIST += \ + $(qwaq_dat_src) \ + ruamoko/qwaq/debugger/debug.h \ + ruamoko/qwaq/debugger/debugger.h \ + ruamoko/qwaq/debugger/localsdata.h \ + ruamoko/qwaq/debugger/typeencodings.h \ + ruamoko/qwaq/debugger/views/arrayview.h \ + ruamoko/qwaq/debugger/views/basicview.h \ + ruamoko/qwaq/debugger/views/defview.h \ + ruamoko/qwaq/debugger/views/doubleview.h \ + ruamoko/qwaq/debugger/views/entityview.h \ + ruamoko/qwaq/debugger/views/fieldview.h \ + ruamoko/qwaq/debugger/views/floatview.h \ + ruamoko/qwaq/debugger/views/funcview.h \ + ruamoko/qwaq/debugger/views/indexview.h \ + ruamoko/qwaq/debugger/views/intview.h \ + ruamoko/qwaq/debugger/views/nameview.h \ + ruamoko/qwaq/debugger/views/pointerview.h \ + ruamoko/qwaq/debugger/views/quatview.h \ + ruamoko/qwaq/debugger/views/stringview.h \ + ruamoko/qwaq/debugger/views/structview.h \ + ruamoko/qwaq/debugger/views/uintview.h \ + ruamoko/qwaq/debugger/views/vectorview.h \ + ruamoko/qwaq/debugger/views/voidview.h \ + ruamoko/qwaq/device/axisdata.h \ + ruamoko/qwaq/device/axisview.h \ + ruamoko/qwaq/device/nameview.h \ + ruamoko/qwaq/device/device.h \ + ruamoko/qwaq/editor/editbuffer.h \ + ruamoko/qwaq/editor/editor.h \ + ruamoko/qwaq/editor/status.h \ + ruamoko/qwaq/input-app.h \ + ruamoko/qwaq/qwaq-app.h \ + ruamoko/qwaq/qwaq-input.h \ + ruamoko/qwaq/qwaq.h \ + ruamoko/qwaq/threading.h \ + ruamoko/qwaq/ui/button.h \ + ruamoko/qwaq/ui/color.h \ + ruamoko/qwaq/ui/curses.h \ + ruamoko/qwaq/ui/draw.h \ + ruamoko/qwaq/ui/event.h \ + ruamoko/qwaq/ui/garray.h \ + ruamoko/qwaq/ui/group.h \ + ruamoko/qwaq/ui/listener.h \ + ruamoko/qwaq/ui/proxyview.h \ + ruamoko/qwaq/ui/rect.h \ + ruamoko/qwaq/ui/scrollbar.h \ + ruamoko/qwaq/ui/stringview.h \ + ruamoko/qwaq/ui/tableview.h \ + ruamoko/qwaq/ui/textcontext.h \ + ruamoko/qwaq/ui/titlebar.h \ + ruamoko/qwaq/ui/view.h \ + ruamoko/qwaq/ui/window.h +CLEANFILES += \ + ruamoko/qwaq/qwaq-curses.log \ + ruamoko/qwaq/*.dat \ + ruamoko/qwaq/*.sym diff --git a/ruamoko/qwaq/builtins/curses.c b/ruamoko/qwaq/builtins/curses.c new file mode 100644 index 000000000..e97e140db --- /dev/null +++ b/ruamoko/qwaq/builtins/curses.c @@ -0,0 +1,1976 @@ +/* + curses.c + + Ruamoko ncurses wrapper using threading + + Copyright (C) 2020 Bill Currie + + Author: Bill Currie + Date: 2020/03/01 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "QF/dstring.h" +#include "QF/progs.h" +#include "QF/ringbuffer.h" +#include "QF/sys.h" + +#include "rua_internal.h" + +#include "ruamoko/qwaq/qwaq.h" +#include "ruamoko/qwaq/ui/curses.h" +#include "ruamoko/qwaq/ui/rect.h" +#include "ruamoko/qwaq/ui/textcontext.h" + +#define always_inline inline __attribute__((__always_inline__)) +#define CMD_SIZE(x) sizeof(x)/sizeof(x[0]) + +typedef enum qwaq_commands_e { + qwaq_cmd_syncprint, + qwaq_cmd_newwin, + qwaq_cmd_delwin, + qwaq_cmd_getwrect, + qwaq_cmd_new_panel, + qwaq_cmd_del_panel, + qwaq_cmd_hide_panel, + qwaq_cmd_show_panel, + qwaq_cmd_top_panel, + qwaq_cmd_bottom_panel, + qwaq_cmd_move_panel, + qwaq_cmd_panel_window, + qwaq_cmd_replace_panel, + qwaq_cmd_update_panels, + qwaq_cmd_doupdate, + qwaq_cmd_mvwaddstr, + qwaq_cmd_waddstr, + qwaq_cmd_mvwaddch, + qwaq_cmd_waddch, + qwaq_cmd_wrefresh, + qwaq_cmd_init_pair, + qwaq_cmd_wbkgd, + qwaq_cmd_werase, + qwaq_cmd_scrollok, + qwaq_cmd_wmove, + qwaq_cmd_move, + qwaq_cmd_curs_set, + qwaq_cmd_wborder, + qwaq_cmd_mvwblit_line, + qwaq_cmd_wresize, + qwaq_cmd_resizeterm, + qwaq_cmd_mvwhline, + qwaq_cmd_mvwvline, +} qwaq_commands; + +static const char *qwaq_command_names[]= { + "syncprint", + "newwin", + "delwin", + "getwrect", + "new_panel", + "del_panel", + "hide_panel", + "show_panel", + "top_panel", + "bottom_panel", + "move_panel", + "panel_window", + "replace_panel", + "update_panels", + "doupdate", + "mvwaddstr", + "waddstr", + "mvwaddch", + "waddch", + "wrefresh", + "init_pair", + "wbkgd", + "werase", + "scrollok", + "wmove", + "move", + "curs_set", + "wborder", + "mvwblit_line", + "wresize", + "resizeterm", + "mvwhline", + "mvwvline", + + "send_connected_devices", + "get_device_info", +}; + +static window_t * +window_new (qwaq_resources_t *res) +{ + return PR_RESNEW (res->window_map); +} + +static void +window_free (qwaq_resources_t *res, window_t *win) +{ + PR_RESFREE (res->window_map, win); +} + +static void +window_reset (qwaq_resources_t *res) +{ + PR_RESRESET (res->window_map); +} + +static inline window_t * +window_get (qwaq_resources_t *res, unsigned index) +{ + return PR_RESGET(res->window_map, index); +} + +static inline int __attribute__((pure)) +window_index (qwaq_resources_t *res, window_t *win) +{ + return PR_RESINDEX (res->window_map, win); +} + +static always_inline window_t * __attribute__((pure)) +get_window (qwaq_resources_t *res, const char *name, int handle) +{ + if (handle == 1) { + return &res->stdscr; + } + + window_t *window = window_get (res, handle); + + if (!window || !window->win) { + PR_RunError (res->pr, "invalid window %d passed to %s", + handle, name + 5); + } + return window; +} + +static panel_t * +panel_new (qwaq_resources_t *res) +{ + return PR_RESNEW (res->panel_map); +} + +static void +panel_free (qwaq_resources_t *res, panel_t *win) +{ + PR_RESFREE (res->panel_map, win); +} + +static void +panel_reset (qwaq_resources_t *res) +{ + PR_RESRESET (res->panel_map); +} + +static inline panel_t * +panel_get (qwaq_resources_t *res, unsigned index) +{ + return PR_RESGET(res->panel_map, index); +} + +static inline int __attribute__((pure)) +panel_index (qwaq_resources_t *res, panel_t *win) +{ + return PR_RESINDEX (res->panel_map, win); +} + +static always_inline panel_t * __attribute__((pure)) +get_panel (qwaq_resources_t *res, const char *name, int handle) +{ + panel_t *panel = panel_get (res, handle); + + if (!panel || !panel->panel) { + PR_RunError (res->pr, "invalid panel passed to %s", name + 3); + } + return panel; +} + +static int +qwaq_cmd_peek (qwaq_resources_t *res, int ahead) +{ + return *RB_PEEK_DATA (res->commands.pipe, ahead); +} + +static dstring_t * +qwaq_cmd_string (qwaq_resources_t *res, int string_id) +{ + return res->commands.strings + string_id; +} + +static void +cmd_syncprint (qwaq_resources_t *res) +{ + int string_id = qwaq_cmd_peek (res, 2); + + Sys_Printf ("%s\n", qwaq_cmd_string (res, string_id)->str); + qwaq_pipe_release_string (&res->commands, string_id); +} + +static void +cmd_newwin (qwaq_resources_t *res) +{ + int xpos = qwaq_cmd_peek (res, 2); + int ypos = qwaq_cmd_peek (res, 3); + int xlen = qwaq_cmd_peek (res, 4); + int ylen = qwaq_cmd_peek (res, 5); + + window_t *window = window_new (res); + window->win = newwin (ylen, xlen, ypos, xpos); + keypad (window->win, FALSE); // do it ourselves + + int window_id = window_index (res, window); + int cmd_result[] = { qwaq_cmd_newwin, window_id }; + qwaq_pipe_submit (&res->results, cmd_result, CMD_SIZE (cmd_result)); +} + +static void +cmd_delwin (qwaq_resources_t *res) +{ + int window_id = qwaq_cmd_peek (res, 2); + + window_t *window = get_window (res, __FUNCTION__, window_id); + delwin (window->win); + window_free (res, window); +} + +static void +cmd_getwrect (qwaq_resources_t *res) +{ + int window_id = qwaq_cmd_peek (res, 2); + int xpos, ypos; + int xlen, ylen; + + window_t *window = get_window (res, __FUNCTION__, window_id); + getparyx (window->win, ypos, xpos); + if (xpos == -1 && ypos ==-1) { + getbegyx (window->win, ypos, xpos); + } + getmaxyx (window->win, ylen, xlen); + + int cmd_result[] = { qwaq_cmd_getwrect, xpos, ypos, xlen, ylen }; + qwaq_pipe_submit (&res->results, cmd_result, CMD_SIZE (cmd_result)); +} + +static void +cmd_new_panel (qwaq_resources_t *res) +{ + int window_id = qwaq_cmd_peek (res, 2); + + window_t *window = get_window (res, __FUNCTION__, window_id); + panel_t *panel = panel_new (res); + panel->panel = new_panel (window->win); + panel->window_id = window_id; + + int panel_id = panel_index (res, panel); + int cmd_result[] = { qwaq_cmd_new_panel, panel_id }; + qwaq_pipe_submit (&res->results, cmd_result, CMD_SIZE (cmd_result)); +} + +static void +cmd_del_panel (qwaq_resources_t *res) +{ + int panel_id = qwaq_cmd_peek (res, 2); + + panel_t *panel = get_panel (res, __FUNCTION__, panel_id); + del_panel (panel->panel); + panel_free (res, panel); +} + +static void +cmd_hide_panel (qwaq_resources_t *res) +{ + int panel_id = qwaq_cmd_peek (res, 2); + + panel_t *panel = get_panel (res, __FUNCTION__, panel_id); + hide_panel (panel->panel); +} + +static void +cmd_show_panel (qwaq_resources_t *res) +{ + int panel_id = qwaq_cmd_peek (res, 2); + + panel_t *panel = get_panel (res, __FUNCTION__, panel_id); + show_panel (panel->panel); +} + +static void +cmd_top_panel (qwaq_resources_t *res) +{ + int panel_id = qwaq_cmd_peek (res, 2); + + panel_t *panel = get_panel (res, __FUNCTION__, panel_id); + top_panel (panel->panel); +} + +static void +cmd_bottom_panel (qwaq_resources_t *res) +{ + int panel_id = qwaq_cmd_peek (res, 2); + + panel_t *panel = get_panel (res, __FUNCTION__, panel_id); + bottom_panel (panel->panel); +} + +static void +cmd_move_panel (qwaq_resources_t *res) +{ + int panel_id = qwaq_cmd_peek (res, 2); + int x = qwaq_cmd_peek (res, 3); + int y = qwaq_cmd_peek (res, 4); + + panel_t *panel = get_panel (res, __FUNCTION__, panel_id); + move_panel (panel->panel, y, x); +} + +static void +cmd_panel_window (qwaq_resources_t *res) +{ + int panel_id = qwaq_cmd_peek (res, 2); + + panel_t *panel = get_panel (res, __FUNCTION__, panel_id); + + int window_id = panel->window_id; + int cmd_result[] = { qwaq_cmd_panel_window, window_id, }; + qwaq_pipe_submit (&res->results, cmd_result, CMD_SIZE (cmd_result)); +} + +static void +cmd_replace_panel (qwaq_resources_t *res) +{ + int panel_id = qwaq_cmd_peek (res, 2); + int window_id = qwaq_cmd_peek (res, 3); + + panel_t *panel = get_panel (res, __FUNCTION__, panel_id); + window_t *window = get_window (res, __FUNCTION__, window_id); + + replace_panel (panel->panel, window->win); +} + +static void +cmd_update_panels (qwaq_resources_t *res) +{ + update_panels (); +} + +static void +cmd_doupdate (qwaq_resources_t *res) +{ + doupdate (); +} + +static void +cmd_mvwaddstr (qwaq_resources_t *res) +{ + int window_id = qwaq_cmd_peek (res, 2); + int x = qwaq_cmd_peek (res, 3); + int y = qwaq_cmd_peek (res, 4); + int string_id = qwaq_cmd_peek (res, 5); + + window_t *window = get_window (res, __FUNCTION__, window_id); + mvwaddstr (window->win, y, x, qwaq_cmd_string (res, string_id)->str); + qwaq_pipe_release_string (&res->commands, string_id); +} + +static void +cmd_waddstr (qwaq_resources_t *res) +{ + int window_id = qwaq_cmd_peek (res, 2); + int string_id = qwaq_cmd_peek (res, 3); + + window_t *window = get_window (res, __FUNCTION__, window_id); + waddstr (window->win, qwaq_cmd_string (res, string_id)->str); + qwaq_pipe_release_string (&res->commands, string_id); +} + +static void +cmd_mvwaddch (qwaq_resources_t *res) +{ + int window_id = qwaq_cmd_peek (res, 2); + int x = qwaq_cmd_peek (res, 3); + int y = qwaq_cmd_peek (res, 4); + int ch = qwaq_cmd_peek (res, 5); + + window_t *window = get_window (res, __FUNCTION__, window_id); + mvwaddch (window->win, y, x, ch); +} + +static void +cmd_waddch (qwaq_resources_t *res) +{ + int window_id = qwaq_cmd_peek (res, 2); + int ch = qwaq_cmd_peek (res, 3); + + window_t *window = get_window (res, __FUNCTION__, window_id); + waddch (window->win, ch); +} + +static void +cmd_wrefresh (qwaq_resources_t *res) +{ + int window_id = qwaq_cmd_peek (res, 2); + + window_t *window = get_window (res, __FUNCTION__, window_id); + wrefresh (window->win); +} + +static void +cmd_init_pair (qwaq_resources_t *res) +{ + int pair = qwaq_cmd_peek (res, 2); + int f = qwaq_cmd_peek (res, 3); + int b = qwaq_cmd_peek (res, 4); + + init_pair (pair, f, b); +} + +static void +cmd_wbkgd (qwaq_resources_t *res) +{ + int window_id = qwaq_cmd_peek (res, 2); + int ch = qwaq_cmd_peek (res, 3); + + window_t *window = get_window (res, __FUNCTION__, window_id); + wbkgd (window->win, ch); +} + +static void +cmd_werase (qwaq_resources_t *res) +{ + int window_id = qwaq_cmd_peek (res, 2); + + window_t *window = get_window (res, __FUNCTION__, window_id); + werase (window->win); +} + +static void +cmd_scrollok (qwaq_resources_t *res) +{ + int window_id = qwaq_cmd_peek (res, 2); + int flag = qwaq_cmd_peek (res, 3); + + window_t *window = get_window (res, __FUNCTION__, window_id); + scrollok (window->win, flag); +} + +static void +cmd_wmove (qwaq_resources_t *res) +{ + int window_id = qwaq_cmd_peek (res, 2); + int x = qwaq_cmd_peek (res, 3); + int y = qwaq_cmd_peek (res, 4); + + window_t *window = get_window (res, __FUNCTION__, window_id); + wmove (window->win, y, x); +} + +static void +cmd_move (qwaq_resources_t *res) +{ + int x = qwaq_cmd_peek (res, 2); + int y = qwaq_cmd_peek (res, 3); + + move (y, x); +} + +static void +cmd_curs_set (qwaq_resources_t *res) +{ + int visibility = qwaq_cmd_peek (res, 2); + + curs_set (visibility); +} + +static void +cmd_wborder (qwaq_resources_t *res) +{ + int window_id = qwaq_cmd_peek (res, 2); + int ls = qwaq_cmd_peek (res, 3); + int rs = qwaq_cmd_peek (res, 4); + int ts = qwaq_cmd_peek (res, 5); + int bs = qwaq_cmd_peek (res, 6); + int tl = qwaq_cmd_peek (res, 7); + int tr = qwaq_cmd_peek (res, 8); + int bl = qwaq_cmd_peek (res, 9); + int br = qwaq_cmd_peek (res, 10); + + window_t *window = get_window (res, __FUNCTION__, window_id); + wborder (window->win, ls, rs, ts, bs, tl, tr, bl, br); +} + +static void +cmd_mvwblit_line (qwaq_resources_t *res) +{ + int window_id = qwaq_cmd_peek (res, 2); + int x = qwaq_cmd_peek (res, 3); + int y = qwaq_cmd_peek (res, 4); + int chs_id = qwaq_cmd_peek (res, 5); + int len = qwaq_cmd_peek (res, 6); + int *chs = (int *) qwaq_cmd_string (res, chs_id)->str; + int save_x; + int save_y; + + window_t *window = get_window (res, __FUNCTION__, window_id); + getyx (window->win, save_y, save_x); + for (int i = 0; i < len; i++) { + if (chs[i] & 0xff) { + mvwaddch (window->win, y, x + i, chs[i]); + } + } + wmove (window->win, save_y, save_x); + qwaq_pipe_release_string (&res->commands, chs_id); +} + +static void +cmd_wresize (qwaq_resources_t *res) +{ + int window_id = qwaq_cmd_peek (res, 2); + int width = qwaq_cmd_peek (res, 3); + int height = qwaq_cmd_peek (res, 4); + + window_t *window = get_window (res, __FUNCTION__, window_id); + wresize (window->win, height, width); +} + +static void +cmd_resizeterm (qwaq_resources_t *res) +{ + int width = qwaq_cmd_peek (res, 2); + int height = qwaq_cmd_peek (res, 3); + + resizeterm (height, width); +} + +static void +cmd_mvwhline (qwaq_resources_t *res) +{ + int window_id = qwaq_cmd_peek (res, 2); + int x = qwaq_cmd_peek (res, 3); + int y = qwaq_cmd_peek (res, 4); + int ch = qwaq_cmd_peek (res, 5); + int n = qwaq_cmd_peek (res, 6); + + window_t *window = get_window (res, __FUNCTION__, window_id); + mvwhline (window->win, y, x, ch, n); +} + +static void +cmd_mvwvline (qwaq_resources_t *res) +{ + int window_id = qwaq_cmd_peek (res, 2); + int x = qwaq_cmd_peek (res, 3); + int y = qwaq_cmd_peek (res, 4); + int ch = qwaq_cmd_peek (res, 5); + int n = qwaq_cmd_peek (res, 6); + + window_t *window = get_window (res, __FUNCTION__, window_id); + mvwvline (window->win, y, x, ch, n); +} + +static void +dump_command (qwaq_resources_t *res, int len) +{ + if (0) { + qwaq_commands cmd = qwaq_cmd_peek (res, 0); + Sys_Printf ("%s[%d]", qwaq_command_names[cmd], len); + if (cmd == qwaq_cmd_syncprint) { + Sys_Printf (" "); + } else { + for (int i = 2; i < len; i++) { + Sys_Printf (" %d", qwaq_cmd_peek (res, i)); + } + Sys_Printf ("\n"); + } + } +} + +static void +process_commands (qwaq_resources_t *res) +{ + struct timespec timeout; + int avail; + int len; + int ret = 0; + + pthread_mutex_lock (&res->commands.pipe_cond.mut); + qwaq_init_timeout (&timeout, 20 * 1000000); + while (RB_DATA_AVAILABLE (res->commands.pipe) < 2 && ret == 0) { + ret = pthread_cond_timedwait (&res->commands.pipe_cond.rcond, + &res->commands.pipe_cond.mut, &timeout); + } + // The mutex is unlocked at the top of the loop and locked again + // (for broadcast) at the bottom, then finally unlocked after the loop. + // It should be only the data availability check that's not threadsafe + // as the mutex is not released until after the data has been written to + // the buffer. + while ((avail = RB_DATA_AVAILABLE (res->commands.pipe)) >= 2 + && avail >= (len = qwaq_cmd_peek (res, 1))) { + pthread_mutex_unlock (&res->commands.pipe_cond.mut); + dump_command (res, len); + qwaq_commands cmd = qwaq_cmd_peek (res, 0); + switch (cmd) { + case qwaq_cmd_syncprint: + cmd_syncprint (res); + break; + case qwaq_cmd_newwin: + cmd_newwin (res); + break; + case qwaq_cmd_delwin: + cmd_delwin (res); + break; + case qwaq_cmd_getwrect: + cmd_getwrect (res); + break; + case qwaq_cmd_new_panel: + cmd_new_panel (res); + break; + case qwaq_cmd_del_panel: + cmd_del_panel (res); + break; + case qwaq_cmd_hide_panel: + cmd_hide_panel (res); + break; + case qwaq_cmd_show_panel: + cmd_show_panel (res); + break; + case qwaq_cmd_top_panel: + cmd_top_panel (res); + break; + case qwaq_cmd_bottom_panel: + cmd_bottom_panel (res); + break; + case qwaq_cmd_move_panel: + cmd_move_panel (res); + break; + case qwaq_cmd_panel_window: + cmd_panel_window (res); + break; + case qwaq_cmd_replace_panel: + cmd_replace_panel (res); + break; + case qwaq_cmd_update_panels: + cmd_update_panels (res); + break; + case qwaq_cmd_doupdate: + cmd_doupdate (res); + break; + case qwaq_cmd_mvwaddstr: + cmd_mvwaddstr (res); + break; + case qwaq_cmd_waddstr: + cmd_waddstr (res); + break; + case qwaq_cmd_mvwaddch: + cmd_mvwaddch (res); + break; + case qwaq_cmd_waddch: + cmd_waddch (res); + break; + case qwaq_cmd_wrefresh: + cmd_wrefresh (res); + break; + case qwaq_cmd_init_pair: + cmd_init_pair (res); + break; + case qwaq_cmd_wbkgd: + cmd_wbkgd (res); + break; + case qwaq_cmd_werase: + cmd_werase (res); + break; + case qwaq_cmd_scrollok: + cmd_scrollok (res); + break; + case qwaq_cmd_wmove: + cmd_wmove (res); + break; + case qwaq_cmd_move: + cmd_move (res); + break; + case qwaq_cmd_curs_set: + cmd_curs_set (res); + break; + case qwaq_cmd_wborder: + cmd_wborder (res); + break; + case qwaq_cmd_mvwblit_line: + cmd_mvwblit_line (res); + break; + case qwaq_cmd_wresize: + cmd_wresize (res); + break; + case qwaq_cmd_resizeterm: + cmd_resizeterm (res); + break; + case qwaq_cmd_mvwhline: + cmd_mvwhline (res); + break; + case qwaq_cmd_mvwvline: + cmd_mvwvline (res); + break; + } + pthread_mutex_lock (&res->commands.pipe_cond.mut); + RB_RELEASE (res->commands.pipe, len); + pthread_cond_broadcast (&res->commands.pipe_cond.wcond); + // unlocked at top of top if there's more data, otherwise just after + // the loop + } + pthread_mutex_unlock (&res->commands.pipe_cond.mut); +} + +static int need_endwin; +static void +bi_shutdown (void *_pr) +{ + if (need_endwin) { + qwaq_input_disable_mouse (); + endwin (); + } +} + +static void +bi_syncprintf (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int string_id = qwaq_pipe_acquire_string (&res->commands); + dstring_t *print_buffer = qwaq_cmd_string (res, string_id); + int command[] = { qwaq_cmd_syncprint, 0, string_id }; + + command[1] = CMD_SIZE(command); + + dstring_clearstr (print_buffer); + RUA_Sprintf (pr, print_buffer, "syncprintf", 0); + + qwaq_pipe_submit (&res->commands, command, command[1]); +} + +static void +bi_create_window (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int xpos = P_INT (pr, 0); + int ypos = P_INT (pr, 1); + int xlen = P_INT (pr, 2); + int ylen = P_INT (pr, 3); + int command[] = { + qwaq_cmd_newwin, 0, + xpos, ypos, xlen, ylen, + }; + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); + + int cmd_result[2]; + qwaq_pipe_receive (&res->results, cmd_result, qwaq_cmd_newwin, + CMD_SIZE (cmd_result)); + R_INT (pr) = cmd_result[1]; +} + +static void +bi_destroy_window (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_INT (pr, 0); + + if (get_window (res, __FUNCTION__, window_id)) { + int command[] = { qwaq_cmd_delwin, 0, window_id, }; + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} + +static void +qwaq_getwrect (qwaq_resources_t *res, int window_id) +{ + progs_t *pr = res->pr; + + if (get_window (res, __FUNCTION__, window_id)) { + int command[] = { qwaq_cmd_getwrect, 0, window_id, }; + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); + + int cmd_result[5]; + qwaq_pipe_receive (&res->results, cmd_result, qwaq_cmd_getwrect, + CMD_SIZE (cmd_result)); + // return xpos, ypos, xlen, ylen + (&R_INT (pr))[0] = cmd_result[1]; + (&R_INT (pr))[1] = cmd_result[2]; + (&R_INT (pr))[2] = cmd_result[3]; + (&R_INT (pr))[3] = cmd_result[4]; + } +} +static void +bi_getwrect (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + qwaq_getwrect (res, P_INT (pr, 0)); +} + +static void +bi_create_panel (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_INT (pr, 0); + + if (get_window (res, __FUNCTION__, window_id)) { + int command[] = { qwaq_cmd_new_panel, 0, window_id, }; + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); + + int cmd_result[2]; + qwaq_pipe_receive (&res->results, cmd_result, qwaq_cmd_new_panel, + CMD_SIZE (cmd_result)); + R_INT (pr) = cmd_result[1]; + } +} + +static void +panel_command (progs_t *pr, qwaq_resources_t *res, qwaq_commands cmd) +{ + int panel_id = P_INT (pr, 0); + + if (get_panel (res, __FUNCTION__, panel_id)) { + int command[] = { cmd, 0, panel_id, }; + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} + +static void +bi_destroy_panel (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + panel_command (pr, res, qwaq_cmd_del_panel); +} + +static void +bi_hide_panel (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + panel_command (pr, res, qwaq_cmd_hide_panel); +} + +static void +bi_show_panel (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + panel_command (pr, res, qwaq_cmd_show_panel); +} + +static void +bi_top_panel (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + panel_command (pr, res, qwaq_cmd_top_panel); +} + +static void +bi_bottom_panel (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + panel_command (pr, res, qwaq_cmd_bottom_panel); +} + +static void +bi_move_panel (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int panel_id = P_INT (pr, 0); + int x = P_INT (pr, 1); + int y = P_INT (pr, 2); + + if (get_panel (res, __FUNCTION__, panel_id)) { + int command[] = { qwaq_cmd_move_panel, 0, panel_id, x, y, }; + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} + +static void +bi_panel_window (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int panel_id = P_INT (pr, 0); + + if (get_panel (res, __FUNCTION__, panel_id)) { + int command[] = { qwaq_cmd_panel_window, 0, panel_id, }; + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); + + int cmd_result[2]; + qwaq_pipe_receive (&res->results, cmd_result, qwaq_cmd_panel_window, + CMD_SIZE (cmd_result)); + (&R_INT (pr))[0] = cmd_result[1]; + } +} + +static void +bi_replace_panel (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int panel_id = P_INT (pr, 0); + int window_id = P_INT (pr, 1); + + if (get_panel (res, __FUNCTION__, panel_id) + && get_window (res, __FUNCTION__, window_id)) { + int command[] = { qwaq_cmd_replace_panel, 0, + panel_id, window_id}; + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} + +static void +qwaq_update_panels (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + + int command[] = { qwaq_cmd_update_panels, 0, }; + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); +} +static void +bi_update_panels (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + qwaq_update_panels (pr, res); +} + +static void +qwaq_doupdate (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + + int command[] = { qwaq_cmd_doupdate, 0, }; + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); +} +static void +bi_doupdate (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + qwaq_doupdate (pr, res); +} + +static void +qwaq_waddstr (qwaq_resources_t *res, int window_id, const char *str) +{ + if (get_window (res, __FUNCTION__, window_id)) { + int string_id = qwaq_pipe_acquire_string (&res->commands); + dstring_t *print_buffer = qwaq_cmd_string (res, string_id); + int command[] = { + qwaq_cmd_waddstr, 0, + window_id, string_id + }; + + command[1] = CMD_SIZE(command); + + dstring_copystr (print_buffer, str); + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} +static void +bi_waddstr (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_INT (pr, 0); + const char *str = P_GSTRING (pr, 1); + + qwaq_waddstr (res, window_id, str); +} + +static void +qwaq_wresize (qwaq_resources_t *res, int window_id, int width, int height) +{ + if (get_window (res, __FUNCTION__, window_id)) { + int command[] = { + qwaq_cmd_wresize, 0, + window_id, width, height + }; + + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} +static void +bi_wresize (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_STRUCT (pr, qwaq_textcontext_t, 0).window; + int width = P_INT (pr, 1); + int height = P_INT (pr, 2); + + qwaq_wresize (res, window_id, width, height); +} + +static void +qwaq_resizeterm (qwaq_resources_t *res, int width, int height) +{ + int command[] = { qwaq_cmd_resizeterm, 0, width, height }; + + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); +} +static void +bi_resizeterm (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int width = P_INT (pr, 0); + int height = P_INT (pr, 1); + + qwaq_resizeterm (res, width, height); +} + +static void +qwaq_mvwhline (qwaq_resources_t *res, int window_id, + int x, int y, int ch, int n) +{ + if (get_window (res, __FUNCTION__, window_id)) { + int command[] = { + qwaq_cmd_mvwhline, 0, + window_id, x, y, ch, n + }; + + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} +static void +bi_mvwhline (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_STRUCT (pr, qwaq_textcontext_t, 0).window; + int x = P_INT (pr, 1); + int y = P_INT (pr, 2); + int ch = P_INT (pr, 3); + int n = P_INT (pr, 4); + + qwaq_mvwhline (res, window_id, x, y, ch, n); +} + +static void +qwaq_mvwvline (qwaq_resources_t *res, int window_id, + int x, int y, int ch, int n) +{ + if (get_window (res, __FUNCTION__, window_id)) { + int command[] = { + qwaq_cmd_mvwvline, 0, + window_id, x, y, ch, n + }; + + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} +static void +bi_mvwvline (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_STRUCT (pr, qwaq_textcontext_t, 0).window; + int x = P_INT (pr, 1); + int y = P_INT (pr, 2); + int ch = P_INT (pr, 3); + int n = P_INT (pr, 4); + + qwaq_mvwvline (res, window_id, x, y, ch, n); +} + +static void +qwaq_mvwaddstr (qwaq_resources_t *res, int window_id, + int x, int y, const char *str) +{ + if (get_window (res, __FUNCTION__, window_id)) { + int string_id = qwaq_pipe_acquire_string (&res->commands); + dstring_t *print_buffer = qwaq_cmd_string (res, string_id); + int command[] = { + qwaq_cmd_mvwaddstr, 0, + window_id, x, y, string_id + }; + + command[1] = CMD_SIZE(command); + + dstring_copystr (print_buffer, str); + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} +static void +bi_mvwaddstr (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_INT (pr, 0); + int x = P_INT (pr, 1); + int y = P_INT (pr, 2); + const char *str = P_GSTRING (pr, 3); + + qwaq_mvwaddstr (res, window_id, x, y, str); +} + +static void +qwaq_mvwprintf (qwaq_resources_t *res, int window_id, + int x, int y, int fmt_arg) +{ + progs_t *pr = res->pr; + + if (get_window (res, __FUNCTION__, window_id)) { + int string_id = qwaq_pipe_acquire_string (&res->commands); + dstring_t *print_buffer = qwaq_cmd_string (res, string_id); + int command[] = { + qwaq_cmd_mvwaddstr, 0, + window_id, x, y, string_id + }; + + command[1] = CMD_SIZE(command); + + dstring_clearstr (print_buffer); + RUA_Sprintf (pr, print_buffer, "mvwaddstr", fmt_arg); + + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} +static void +bi_mvwprintf (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_INT (pr, 0); + int x = P_INT (pr, 1); + int y = P_INT (pr, 2); + + qwaq_mvwprintf (res, window_id, x, y, 3); +} + +static void +qwaq_wprintf (qwaq_resources_t *res, int window_id, int fmt_arg) +{ + progs_t *pr = res->pr; + + if (get_window (res, __FUNCTION__, window_id)) { + int string_id = qwaq_pipe_acquire_string (&res->commands); + dstring_t *print_buffer = qwaq_cmd_string (res, string_id); + int command[] = { + qwaq_cmd_waddstr, 0, + window_id, string_id + }; + + command[1] = CMD_SIZE(command); + + dstring_clearstr (print_buffer); + RUA_Sprintf (pr, print_buffer, "wprintf", fmt_arg); + + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} +static void +bi_wprintf (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_INT (pr, 0); + + qwaq_wprintf (res, window_id, 1); +} + +static void +qwaq_wvprintf (qwaq_resources_t *res, int window_id, + const char *fmt, pr_va_list_t *args) +{ + progs_t *pr = res->pr; + pr_type_t *list_start = PR_GetPointer (pr, args->list); + pr_type_t **list = alloca (args->count * sizeof (*list)); + + for (int i = 0; i < args->count; i++) { + list[i] = list_start + i * pr->pr_param_size; + } + + if (get_window (res, __FUNCTION__, window_id)) { + int string_id = qwaq_pipe_acquire_string (&res->commands); + dstring_t *print_buffer = qwaq_cmd_string (res, string_id); + int command[] = { + qwaq_cmd_waddstr, 0, + window_id, string_id + }; + + command[1] = CMD_SIZE(command); + + dstring_clearstr (print_buffer); + PR_Sprintf (pr, print_buffer, "wvprintf", fmt, args->count, list); + + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} +static void +bi_wvprintf (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_INT (pr, 0); + const char *fmt = P_GSTRING (pr, 1); + __auto_type args = (pr_va_list_t *) &P_POINTER (pr, 2); + + qwaq_wvprintf (res, window_id, fmt, args); +} + +static void +qwaq_waddch (qwaq_resources_t *res, int window_id, int ch) +{ + if (get_window (res, __FUNCTION__, window_id)) { + int command[] = { qwaq_cmd_waddch, 0, window_id, ch }; + + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} +static void +bi_waddch (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_INT (pr, 0); + int ch = P_INT (pr, 0); + + qwaq_waddch (res, window_id, ch); +} + +static void +qwaq_mvwvprintf (qwaq_resources_t *res, int window_id, int x, int y, + const char *fmt, pr_va_list_t *args) +{ + progs_t *pr = res->pr; + pr_type_t *list_start = PR_GetPointer (pr, args->list); + pr_type_t **list = alloca (args->count * sizeof (*list)); + + for (int i = 0; i < args->count; i++) { + list[i] = list_start + i * pr->pr_param_size; + } + + if (get_window (res, __FUNCTION__, window_id)) { + int string_id = qwaq_pipe_acquire_string (&res->commands); + dstring_t *print_buffer = qwaq_cmd_string (res, string_id); + int command[] = { + qwaq_cmd_mvwaddstr, 0, + window_id, x, y, string_id + }; + + command[1] = CMD_SIZE(command); + + dstring_clearstr (print_buffer); + PR_Sprintf (pr, print_buffer, "mvwvprintf", fmt, args->count, list); + + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} +static void +bi_mvwvprintf (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_INT (pr, 0); + int x = P_INT (pr, 1); + int y = P_INT (pr, 2); + const char *fmt = P_GSTRING (pr, 2); + __auto_type args = (pr_va_list_t *) &P_POINTER (pr, 3); + + qwaq_mvwvprintf (res, window_id, x, y, fmt, args); +} + +static void +qwaq_mvwaddch (qwaq_resources_t *res, int window_id, int x, int y, int ch) +{ + if (get_window (res, __FUNCTION__, window_id)) { + int command[] = { + qwaq_cmd_mvwaddch, 0, + window_id, x, y, ch + }; + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} +static void +bi_mvwaddch (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_INT (pr, 0); + int x = P_INT (pr, 1); + int y = P_INT (pr, 2); + int ch = P_INT (pr, 3); + + qwaq_mvwaddch (res, window_id, x, y, ch); +} + +static void +qwaq_wrefresh (qwaq_resources_t *res, int window_id) +{ + if (get_window (res, __FUNCTION__, window_id)) { + int command[] = { qwaq_cmd_wrefresh, 0, window_id, }; + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} +static void +bi_wrefresh (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_INT (pr, 0); + + qwaq_wrefresh (res, window_id); +} + +static void +bi_max_colors (progs_t *pr, void *_res) +{ + R_INT (pr) = COLORS; +} + +static void +bi_max_color_pairs (progs_t *pr, void *_res) +{ + R_INT (pr) = COLOR_PAIRS; +} + +static void +qwaq_init_pair (qwaq_resources_t *res, int pair, int f, int b) +{ + + int command[] = { qwaq_cmd_init_pair, 0, pair, f, b, }; + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); +} +static void +bi_init_pair (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int pair = P_INT (pr, 0); + int f = P_INT (pr, 1); + int b = P_INT (pr, 2); + + qwaq_init_pair (res, pair, f, b); +} + +static void +qwaq_wbkgd (qwaq_resources_t *res, int window_id, int ch) +{ + if (get_window (res, __FUNCTION__, window_id)) { + int command[] = { qwaq_cmd_wbkgd, 0, window_id, ch, }; + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} +static void +bi_wbkgd (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_INT (pr, 0); + int ch = P_INT (pr, 1); + + qwaq_wbkgd (res, window_id, ch); +} + +static void +qwaq_werase (qwaq_resources_t *res, int window_id, int ch) +{ + if (get_window (res, __FUNCTION__, window_id)) { + int command[] = { qwaq_cmd_werase, 0, window_id, }; + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} +static void +bi_werase (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_INT (pr, 0); + int ch = P_INT (pr, 1); + + qwaq_werase (res, window_id, ch); +} + +static void +qwaq_scrollok (qwaq_resources_t *res, int window_id, int flag) +{ + if (get_window (res, __FUNCTION__, window_id)) { + int command[] = { qwaq_cmd_scrollok, 0, window_id, flag, }; + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} +static void +bi_scrollok (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_INT (pr, 0); + int flag = P_INT (pr, 1); + + qwaq_scrollok (res, window_id, flag); +} + +static void +qwaq_wmove (qwaq_resources_t *res, int window_id, int x, int y) +{ + if (get_window (res, __FUNCTION__, window_id)) { + int command[] = { qwaq_cmd_wmove, 0, window_id, x, y, }; + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} +static void +bi_wmove (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_INT (pr, 0); + int x = P_INT (pr, 1); + int y = P_INT (pr, 2); + + qwaq_wmove (res, window_id, x, y); +} + +static const char qwaq_acs_char_map[] = "lmkjtuvwqxnos`afg~,+.-hi0pryz{|}"; +static void +qwaq_acs_char (progs_t *pr, unsigned acs) +{ + if (acs < 256) { + R_INT (pr) = NCURSES_ACS(acs); + } else if (acs - 256 < sizeof (qwaq_acs_char_map)) { + R_INT (pr) = NCURSES_ACS(qwaq_acs_char_map[acs - 256]); + } else { + R_INT (pr) = 0; + } +} +static void +bi_acs_char (progs_t *pr, void *_res) +{ + int acs = P_INT (pr, 0); + + qwaq_acs_char (pr, acs); +} + +static void +qwaq_move (qwaq_resources_t *res, int x, int y) +{ + int command[] = { qwaq_cmd_move, 0, x, y, }; + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); +} +static void +bi_move (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int x = P_INT (pr, 0); + int y = P_INT (pr, 1); + + qwaq_move (res, x, y); +} + +static void +qwaq_curs_set (qwaq_resources_t *res, int visibility) +{ + int command[] = { qwaq_cmd_curs_set, 0, visibility, }; + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); +} +static void +bi_curs_set (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int visibility = P_INT (pr, 0); + + qwaq_curs_set (res, visibility); +} + +static void +qwaq_wborder (qwaq_resources_t *res, int window_id, + box_sides_t sides, box_corners_t corns) +{ + if (get_window (res, __FUNCTION__, window_id)) { + int command[] = { qwaq_cmd_wborder, 0, window_id, + sides.ls, sides.rs, sides.ts, sides.bs, + corns.tl, corns.tr, corns.bl, corns.br, }; + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} +static void +bi_wborder (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_INT (pr, 0); + __auto_type sides = P_PACKED (pr, box_sides_t, 1); + __auto_type corns = P_PACKED (pr, box_corners_t, 2); + + qwaq_wborder (res, window_id, sides, corns); +} + +static void +qwaq__mvwblit_line (qwaq_resources_t *res, int window_id, int x, int y, + int *chs, int len) +{ + if (get_window (res, __FUNCTION__, window_id)) { + int chs_id = qwaq_pipe_acquire_string (&res->commands); + dstring_t *chs_buf = qwaq_cmd_string (res, chs_id); + int command[] = { qwaq_cmd_mvwblit_line, 0, window_id, + x, y, chs_id, len,}; + + command[1] = CMD_SIZE(command); + + chs_buf->size = len * sizeof (int); + dstring_adjust (chs_buf); + memcpy (chs_buf->str, chs, len * sizeof (int)); + + qwaq_pipe_submit (&res->commands, command, command[1]); + } +} +static void +bi_mvwblit_line (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_INT (pr, 0); + int x = P_INT (pr, 1); + int y = P_INT (pr, 2); + int *chs = (int *) P_GPOINTER (pr, 3); + int len = P_INT (pr, 4); + qwaq__mvwblit_line (res, window_id, x, y, chs, len); +} + +static void * +qwaq_curses_thread (qwaq_thread_t *thread) +{ + qwaq_resources_t *res = thread->data; + + while (1) { + process_commands (res); + } + thread->return_code = 0; + return thread; +} + +static void +bi_initialize (progs_t *pr, void *data) +{ + qwaq_resources_t *res = PR_Resources_Find (pr, "curses"); + + initscr (); + need_endwin = 1; + res->initialized = 1; + start_color (); + raw (); + keypad (stdscr, FALSE); // do it ourselves + noecho (); + nonl (); + nodelay (stdscr, TRUE); + mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, NULL); + refresh(); + + qwaq_input_enable_mouse (); + + res->stdscr.win = stdscr; + + create_thread (qwaq_curses_thread, res); +} + +static void +bi__c_TextContext__is_initialized (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + R_INT (pr) = res->initialized; +} + +static void +bi__c_TextContext__max_colors (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + bi_max_colors (pr, res); +} + +static void +bi__c_TextContext__max_color_pairs (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + bi_max_color_pairs (pr, res); +} + +static void +bi__c_TextContext__init_pair_ (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int pair = P_INT (pr, 2); + int f = P_INT (pr, 3); + int b = P_INT (pr, 4); + + qwaq_init_pair (res, pair, f, b); +} + +static void +bi__c_TextContext__acs_char_ (progs_t *pr, void *_res) +{ + int acs = P_INT (pr, 2); + + qwaq_acs_char (pr, acs); +} + +static void +bi__c_TextContext__move_ (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + Point *pos = &P_PACKED (pr, Point, 2); + + qwaq_move (res, pos->x, pos->y); +} + +static void +bi__c_TextContext__curs_set_ (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int visibility = P_INT (pr, 2); + + qwaq_curs_set (res, visibility); +} + +static void +bi__c_TextContext__doupdate (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + bi_doupdate (pr, res); +} + +static void +bi__i_TextContext__mvprintf_ (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_STRUCT (pr, qwaq_textcontext_t, 0).window; + Point *pos = &P_PACKED (pr, Point, 2); + + qwaq_mvwprintf (res, window_id, pos->x, pos->y, 3); +} + +static void +bi__i_TextContext__printf_ (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_STRUCT (pr, qwaq_textcontext_t, 0).window; + + qwaq_wprintf (res, window_id, 2); +} + +static void +bi__i_TextContext__vprintf_ (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_STRUCT (pr, qwaq_textcontext_t, 0).window; + const char *fmt = P_GSTRING (pr, 2); + __auto_type args = (pr_va_list_t *) &P_POINTER (pr, 3); + + qwaq_wvprintf (res, window_id, fmt, args); +} + +static void +bi__i_TextContext__addch_ (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_STRUCT (pr, qwaq_textcontext_t, 0).window; + int ch = P_INT (pr, 2); + + qwaq_waddch (res, window_id, ch); +} + +static void +bi__i_TextContext__addstr_ (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_STRUCT (pr, qwaq_textcontext_t, 0).window; + const char *str = P_GSTRING (pr, 2); + + qwaq_waddstr (res, window_id, str); +} + +static void +bi__i_TextContext__mvvprintf_ (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_STRUCT (pr, qwaq_textcontext_t, 0).window; + Point *pos = &P_PACKED (pr, Point, 2); + const char *fmt = P_GSTRING (pr, 3); + __auto_type args = &P_PACKED (pr, pr_va_list_t, 4); + + qwaq_mvwvprintf (res, window_id, pos->x, pos->y, fmt, args); +} + +static void +bi__i_TextContext__resizeTo_ (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + __auto_type self = &P_STRUCT (pr, qwaq_textcontext_t, 0); + int window_id = self->window; + + self->size = P_PACKED (pr, Extent, 2); + qwaq_wresize (res, window_id, self->size.width, self->size.height); + if (window_id == 1) { + qwaq_wbkgd (res, window_id, self->background); + } +} + +static void +bi__c_TextContext__refresh (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + qwaq_update_panels (pr, res); + qwaq_doupdate (pr, res); +} + +static void +bi__i_TextContext__refresh (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_STRUCT (pr, qwaq_textcontext_t, 0).window; + + qwaq_update_panels (pr, res); + if (window_id == 1) { + qwaq_wrefresh (res, window_id); + qwaq_doupdate (pr, res); + } +} + +static void +bi__i_TextContext__mvaddch_ (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_STRUCT (pr, qwaq_textcontext_t, 0).window; + Point *pos = &P_PACKED (pr, Point, 2); + int ch = P_INT (pr, 3); + + qwaq_mvwaddch (res, window_id, pos->x, pos->y, ch); +} + +static void +bi__i_TextContext__mvaddstr_ (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_STRUCT (pr, qwaq_textcontext_t, 0).window; + Point *pos = &P_PACKED (pr, Point, 2); + const char *str = P_GSTRING (pr, 3); + + qwaq_mvwaddstr (res, window_id, pos->x, pos->y, str); +} + +static void +bi__i_TextContext__bkgd_ (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + __auto_type self = &P_STRUCT (pr, qwaq_textcontext_t, 0); + int window_id = self->window; + int ch = P_INT (pr, 2); + + self->background = ch; + qwaq_wbkgd (res, window_id, ch); +} + +static void +bi__i_TextContext__clear (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + __auto_type self = &P_STRUCT (pr, qwaq_textcontext_t, 0); + int window_id = self->window; + int ch = self->background; + + qwaq_werase (res, window_id, ch); +} + +static void +bi__i_TextContext__scrollok_ (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_STRUCT (pr, qwaq_textcontext_t, 0).window; + int flag = P_INT (pr, 2); + + qwaq_scrollok (res, window_id, flag); +} + +static void +bi__i_TextContext__border_ (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + int window_id = P_STRUCT (pr, qwaq_textcontext_t, 0).window; + __auto_type sides = P_PACKED (pr, box_sides_t, 2); + __auto_type corns = P_PACKED (pr, box_corners_t, 3); + + qwaq_wborder (res, window_id, sides, corns); +} + +static void +bi__i_TextContext__mvhline_ (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + __auto_type self = &P_STRUCT (pr, qwaq_textcontext_t, 0); + int window_id = self->window; + __auto_type pos = &P_PACKED (pr, Point, 2); + int ch = P_INT (pr, 3); + int n = P_INT (pr, 4); + + qwaq_mvwhline (res, window_id, pos->x, pos->y, ch, n); +} + +static void +bi__i_TextContext__mvvline_ (progs_t *pr, void *_res) +{ + qwaq_resources_t *res = _res; + __auto_type self = &P_STRUCT (pr, qwaq_textcontext_t, 0); + int window_id = self->window; + __auto_type pos = &P_PACKED (pr, Point, 2); + int ch = P_INT (pr, 3); + int n = P_INT (pr, 4); + + qwaq_mvwvline (res, window_id, pos->x, pos->y, ch, n); +} + +static void +bi_curses_clear (progs_t *pr, void *_res) +{ + __auto_type res = (qwaq_resources_t *) _res; + + if (res->initialized) { + qwaq_input_disable_mouse (); + endwin (); + } + need_endwin = 0; + window_reset (res); + panel_reset (res); +} + +static void +bi_curses_destroy (progs_t *pr, void *_res) +{ + free (_res); +} + +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) +#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } +static builtin_t builtins[] = { + bi(initialize, 0), + bi(syncprintf, -2, p(string)), + bi(create_window, 4, p(int), p(int), p(int), p(int)), + bi(destroy_window, 1, p(ptr)), + bi(getwrect, 1, p(ptr)), + bi(create_panel, 1, p(ptr)), + bi(destroy_panel, 1, p(ptr)), + bi(hide_panel, 1, p(ptr)), + bi(show_panel, 1, p(ptr)), + bi(top_panel, 1, p(ptr)), + bi(bottom_panel, 1, p(ptr)), + bi(move_panel, 3, p(ptr), p(int), p(int)), + bi(panel_window, 1, p(ptr)), + bi(replace_panel, 2, p(ptr), p(ptr)), + bi(update_panels, 0), + bi(doupdate, 0), + bi(mvwprintf, -5, p(ptr), p(int), p(int), p(string)), + bi(wprintf, -3, p(ptr), p(string)), + bi(wvprintf, 3, p(ptr), p(string), P(1, 2)), + bi(mvwvprintf, 5, p(ptr), p(int), p(int), p(string), P(1, 2)), + bi(mvwaddch, 4, p(ptr), p(int), p(int), p(int)), + bi(waddch, 2, p(ptr), p(int)), + bi(mvwaddstr, 4, p(ptr), p(int), p(int), p(string)), + bi(waddstr, 2, p(ptr), p(string)), + bi(wrefresh, 1, p(ptr)), + bi(max_colors, 0), + bi(max_color_pairs, 0), + bi(init_pair, 3, p(int), p(int), p(int)), + bi(wbkgd, 2, p(ptr), p(int)), + bi(werase, 1, p(ptr)), + bi(scrollok, 2, p(ptr), p(int)), + bi(wmove, 3, p(ptr), p(int), p(int), p(int)), + bi(acs_char, 1, p(int)), + bi(move, 2, p(int), p(int)), + bi(curs_set, 1, p(int)), + bi(wborder, 3, p(ptr), P(1, 4), P(1, 4)), + bi(mvwblit_line, 5, p(ptr), p(int), p(int), p(ptr), p(int)), + bi(wresize, 3, p(ptr), p(int), p(int)), + bi(resizeterm, 2, p(int), p(int)), + bi(mvwhline, 5, p(ptr), p(int), p(int), p(int), p(int)), + bi(mvwvline, 5, p(ptr), p(int), p(int), p(int), p(int)), + + bi(_c_TextContext__is_initialized, 2, p(ptr), p(ptr)), + bi(_c_TextContext__max_colors, 2, p(ptr), p(ptr)), + bi(_c_TextContext__max_color_pairs, 2, p(ptr), p(ptr)), + bi(_c_TextContext__init_pair_, 5, p(ptr), p(ptr), + p(int), p(int), p(int)), + bi(_c_TextContext__acs_char_, 3, p(ptr), p(ptr), + p(int)), + bi(_c_TextContext__move_, 3, p(ptr), p(ptr), + P(1, 2)), + bi(_c_TextContext__curs_set_, 3, p(ptr), p(ptr), + p(int)), + bi(_c_TextContext__doupdate, 2, p(ptr), p(ptr)), + bi(_i_TextContext__mvprintf_, -5, p(ptr), p(ptr), + P(1, 2), p(string)), + bi(_i_TextContext__printf_, -4, p(ptr), p(ptr), + p(string)), + bi(_i_TextContext__vprintf_, 4, p(ptr), p(ptr), + p(string), P(1, 2)), + bi(_i_TextContext__addch_, 3, p(ptr), p(ptr), + p(int)), + bi(_i_TextContext__addstr_, 3, p(ptr), p(ptr), + p(string)), + bi(_i_TextContext__mvvprintf_, 5, p(ptr), p(ptr), + P(1, 2), p(string), P(1, 2)), + bi(_i_TextContext__resizeTo_, 3, p(ptr), p(ptr), + P(1, 2)), + bi(_c_TextContext__refresh, 2, p(ptr), p(ptr)), + bi(_i_TextContext__refresh, 2, p(ptr), p(ptr)), + bi(_i_TextContext__mvaddch_, 4, p(ptr), p(ptr), + P(1, 2), p(int)), + bi(_i_TextContext__mvaddstr_, 4, p(ptr), p(ptr), + P(1, 2), p(string)), + bi(_i_TextContext__bkgd_, 3, p(ptr), p(ptr), + p(int)), + bi(_i_TextContext__clear, 2, p(ptr), p(ptr)), + bi(_i_TextContext__scrollok_, 3, p(ptr), p(ptr), + p(int)), + bi(_i_TextContext__border_, 4, p(ptr), p(ptr), + P(1, 4), P(1, 4)), + bi(_i_TextContext__mvhline_, 5, p(ptr), p(ptr), + P(1, 2), p(int), p(int)), + bi(_i_TextContext__mvvline_, 5, p(ptr), p(ptr), + P(1, 2), p(int), p(int)), + + {0} +}; + +void +BI_Curses_Init (progs_t *pr) +{ + qwaq_resources_t *res = calloc (sizeof (*res), 1); + res->pr = pr; + + qwaq_init_pipe (&res->commands); + qwaq_init_pipe (&res->results); + + PR_Resources_Register (pr, "curses", res, bi_curses_clear, + bi_curses_destroy); + PR_RegisterBuiltins (pr, builtins, res); + Sys_RegisterShutdown (bi_shutdown, pr); +} diff --git a/ruamoko/qwaq/builtins/debug.c b/ruamoko/qwaq/builtins/debug.c new file mode 100644 index 000000000..6148076bc --- /dev/null +++ b/ruamoko/qwaq/builtins/debug.c @@ -0,0 +1,744 @@ +/* + debug.c + + Debugging support + + Copyright (C) 2020 Bill Currie + + Author: Bill Currie + Date: 2020/03/24 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "QF/dstring.h" +#include "QF/hash.h" +#include "QF/keys.h" +#include "QF/quakefs.h" +#include "QF/sys.h" + +#include "ruamoko/qwaq/qwaq.h" +#include "ruamoko/qwaq/ui/event.h" +#include "ruamoko/qwaq/ui/curses.h" +#include "ruamoko/qwaq/debugger/debug.h" + +typedef struct qwaq_target_s { + progs_t *pr; + struct qwaq_debug_s *debugger; + int handle; + prdebug_t event; + void *param; + rwcond_t run_cond; + int run_command; +} qwaq_target_t; + +typedef struct qwaq_debug_s { + progs_t *pr; + qwaq_input_resources_t *input; // to communicate with the debugger thread + PR_RESMAP (qwaq_target_t) targets; +} qwaq_debug_t; + +#define always_inline inline __attribute__((__always_inline__)) + +static qwaq_target_t * +target_new (qwaq_debug_t *debug) +{ + return PR_RESNEW (debug->targets); +} + +static void +target_free (qwaq_debug_t *debug, qwaq_target_t *target) +{ + PR_RESFREE (debug->targets, target); +} + +static void +target_reset (qwaq_debug_t *debug) +{ + PR_RESRESET (debug->targets); +} + +static inline qwaq_target_t * +target_get (qwaq_debug_t *debug, unsigned index) +{ + return PR_RESGET (debug->targets, index); +} + +static inline int __attribute__((pure)) +target_index (qwaq_debug_t *debug, qwaq_target_t *target) +{ + return PR_RESINDEX (debug->targets, target); +} + +static always_inline qwaq_target_t * __attribute__((pure)) +get_target (qwaq_debug_t *debug, const char *name, int handle) +{ + qwaq_target_t *target = target_get (debug, handle); + + if (!target || !target->debugger) { + PR_RunError (debug->pr, "invalid target passed to %s", name + 4); + } + return target; +} + +static void +qwaq_debug_handler (prdebug_t debug_event, void *param, void *data) +{ + __auto_type target = (qwaq_target_t *) data; + qwaq_debug_t *debug = target->debugger; + qwaq_event_t event = {}; + int ret; + + target->event = debug_event; + target->param = param; + event.what = qe_debug_event; + event.message.pointer_val = target->handle; + + while ((ret = qwaq_add_event (debug->input, &event)) == ETIMEDOUT) { + // spin + } + if (ret == EINVAL) { + Sys_Error ("event queue broke"); + } + pthread_mutex_lock (&target->run_cond.mut); + while (!target->run_command) { + pthread_cond_wait (&target->run_cond.rcond, &target->run_cond.mut); + } + target->run_command = 0; + pthread_mutex_unlock (&target->run_cond.mut); + if (debug_event == prd_runerror || debug_event == prd_error) { + pthread_exit (param); + } +} + +//FIXME need a better way to get this from one thread to the others +pthread_cond_t debug_data_cond = PTHREAD_COND_INITIALIZER; +pthread_mutex_t debug_data_mutex = PTHREAD_MUTEX_INITIALIZER; +static qwaq_debug_t *qwaq_debug_data; + +static int +qwaq_debug_load (progs_t *pr) +{ + __auto_type debug = (qwaq_debug_t *) PR_Resources_Find (pr, "qwaq-debug"); + + pthread_mutex_lock (&debug_data_mutex); + qwaq_debug_data = debug; // FIXME ? see decl + pthread_cond_broadcast (&debug_data_cond); + pthread_mutex_unlock (&debug_data_mutex); + + return 1; +} + +static void +qwaq_debug_clear (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + target_reset (debug); +} + +static void +qwaq_debug_destroy (progs_t *pr, void *_res) +{ + free (_res); +} + +static void +qwaq_target_clear (progs_t *pr, void *_res) +{ + qwaq_target_t *target = pr->debug_data; + if (target) { + target_free (target->debugger, target); + } +} + +static void +qwaq_target_destroy (progs_t *pr, void *_res) +{ + // no block to free +} + +static int +qwaq_target_load (progs_t *pr) +{ + pthread_mutex_lock (&debug_data_mutex); + while (!qwaq_debug_data) { + pthread_cond_wait (&debug_data_cond, &debug_data_mutex); + } + pthread_mutex_unlock (&debug_data_mutex); + + qwaq_target_t *target = target_new (qwaq_debug_data); + target->pr = pr; + target->debugger = qwaq_debug_data; + target->handle = target_index (qwaq_debug_data, target); + qwaq_init_cond (&target->run_cond); + target->run_command = 0; + + pr->debug_handler = qwaq_debug_handler; + pr->debug_data = target; + + // start tracing immediately so the debugger has a chance to start up + // before the target progs begin running + pr->pr_trace = 1; + pr->pr_trace_depth = -1; + + return 1; +} + +static void +qdb_set_trace (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + int state = P_INT (pr, 1); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + + tpr->pr_trace = state; +} + +static void +qdb_set_breakpoint (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + unsigned staddr = P_INT (pr, 1); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + + if (staddr >= tpr->progs->statements.count) { + R_INT (pr) = -1; + return; + } + int set = (tpr->pr_statements[staddr].op & OP_BREAK) != 0; + tpr->pr_statements[staddr].op |= OP_BREAK; + R_INT (pr) = set; +} + +static void +qdb_clear_breakpoint (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + unsigned staddr = P_UINT (pr, 1); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + + if (staddr >= tpr->progs->statements.count) { + R_INT (pr) = -1; + return; + } + tpr->pr_statements[staddr].op &= ~OP_BREAK; + R_INT (pr) = 0; +} + +static void +qdb_set_watchpoint (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + pr_ptr_t offset = P_UINT (pr, 1); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + + if (offset >= tpr->globals_size) { + R_INT (pr) = -1; + return; + } + tpr->watch = &tpr->pr_globals[offset]; + R_INT (pr) = 0; +} + +static void +qdb_clear_watchpoint (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + + tpr->watch = 0; + R_INT (pr) = 0; +} + +static void +qdb_continue (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + + pthread_mutex_lock (&target->run_cond.mut); + target->run_command = 1; + pthread_cond_signal (&target->run_cond.rcond); + pthread_mutex_unlock (&target->run_cond.mut); +} + +static void +qdb_get_state (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + pr_lineno_t *lineno; + pr_auxfunction_t *f; + pr_string_t file = 0; + unsigned line = 0; + unsigned staddr = tpr->pr_xstatement; + pr_func_t func = 0; + + if (tpr->pr_xfunction) { + func = tpr->pr_xfunction - tpr->function_table; + } + + lineno = PR_Find_Lineno (tpr, staddr); + if (lineno) { + f = PR_Get_Lineno_Func (tpr, lineno); + //FIXME file is a permanent string. dynamic would be better + //but they're not merged (and would need refcounting) + file = PR_SetString (pr, PR_Get_Source_File (tpr, lineno)); + func = f->function; + line = PR_Get_Lineno_Line (tpr, lineno); + line += f->source_line; + } + + qdb_state_t state = {}; + state.staddr = staddr; + state.func = func; + state.file = file; + state.line = line; + + R_PACKED (pr, qdb_state_t) = state; +} + +static void +qdb_get_stack_depth (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + + R_INT (pr) = tpr->pr_depth; +} + +static void +qdb_get_stack (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + int count = tpr->pr_depth; + + R_POINTER (pr) = 0; + if (count > 0) { + size_t size = count * sizeof (qdb_stack_t); + pr_string_t stack_block = PR_AllocTempBlock (pr, size); + __auto_type stack = (qdb_stack_t *) PR_GetString (pr, stack_block); + + for (int i = 0; i < count; i++) { + stack[i].staddr = tpr->pr_stack[i].staddr; + stack[i].func = tpr->pr_stack[i].func - tpr->pr_xfunction; + //XXX temp strings (need access somehow) + } + R_POINTER (pr) = PR_SetPointer (pr, stack); + } +} + +static void +qdb_get_event (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + __auto_type event = &P_STRUCT (pr, qdb_event_t, 1); + + memset (event, 0, sizeof (*event)); + event->what = target->event; + switch (event->what) { + case prd_subenter: + event->function = *(pr_func_t *) target->param; + break; + case prd_runerror: + case prd_error: + event->message = PR_SetReturnString (pr, (char *) target->param); + break; + case prd_begin: + event->function = *(pr_func_t *) target->param; + break; + case prd_terminate: + event->exit_code = *(int *) target->param; + break; + case prd_trace: + case prd_breakpoint: + case prd_watchpoint: + case prd_subexit: + case prd_none: + break; + } + R_INT (pr) = target->event != prd_none; +} + +static void +qdb_get_data (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + pr_ptr_t srcoff = P_POINTER (pr, 1); + unsigned length = P_UINT (pr, 2); + pr_ptr_t dstoff = P_POINTER(pr, 3); + + pr_type_t *src = PR_GetPointer(tpr, srcoff); + pr_type_t *dst = PR_GetPointer(pr, dstoff); + + if (srcoff >= tpr->globals_size || srcoff + length >= tpr->globals_size) { + R_INT (pr) = -1; + return; + } + if (dstoff >= pr->globals_size || dstoff + length >= pr->globals_size) { + R_INT (pr) = -1; + return; + } + memcpy (dst, src, length * sizeof (pr_type_t)); + R_INT (pr) = 0; +} + +static void +qdb_get_string (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + pr_string_t string = P_STRING (pr, 1); + + R_STRING (pr) = 0; + if (PR_StringValid (tpr, string)) { + RETURN_STRING (pr, PR_GetString (tpr, string)); + } +} + +static void +qdb_get_file_path (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + const char *file = P_GSTRING (pr, 1); + const char *basedir = PR_Debug_GetBaseDirectory (tpr, file); + + if (basedir) { + size_t baselen = strlen (basedir); + size_t filelen = strlen (file); + char *path = alloca (baselen + filelen + 2); + strcpy (path, basedir); + path[baselen] = '/'; + strcpy (path + baselen + 1, file); + path = QFS_CompressPath (path); + RETURN_STRING (pr, path); + free (path); + } else { + R_STRING (pr) = P_STRING (pr, 1); + } +} + +static void +qdb_find_string (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + const char *str = P_GSTRING (pr, 1); + + R_INT (pr) = PR_FindString (tpr, str); +} + +static void +qdb_find_global (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + const char *name = P_GSTRING (pr, 1); + pr_def_t *def = PR_FindGlobal (tpr, name); + + if (def) { + R_PACKED (pr, qdb_def_t).type_size = (def->size << 16) | def->type; + R_PACKED (pr, qdb_def_t).offset = def->ofs; + R_PACKED (pr, qdb_def_t).name = def->name; + R_PACKED (pr, qdb_def_t).type_encoding = def->type_encoding; + } else { + memset (&R_PACKED (pr, qdb_def_t), 0, sizeof (qdb_def_t)); + } +} + +static void +qdb_find_field (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + const char *name = P_GSTRING (pr, 1); + pr_def_t *def = PR_FindField (tpr, name); + + if (def) { + R_PACKED (pr, qdb_def_t).type_size = (def->size << 16) | def->type; + R_PACKED (pr, qdb_def_t).offset = def->ofs; + R_PACKED (pr, qdb_def_t).name = def->name; + R_PACKED (pr, qdb_def_t).type_encoding = def->type_encoding; + } else { + memset (&R_PACKED (pr, qdb_def_t), 0, sizeof (qdb_def_t)); + } +} + +static void +return_function (progs_t *pr, dfunction_t *func) +{ + R_POINTER (pr) = 0; + if (func) { + __auto_type f + = (qdb_function_t *) PR_Zone_Malloc (pr, sizeof (qdb_function_t)); + f->staddr = func->first_statement; + f->local_data = func->params_start; + f->local_size = func->locals; + f->profile = func->profile; + f->name = func->name; + f->file = func->file; + f->num_params = func->numparams; + R_POINTER (pr) = PR_SetPointer (pr, f); + } +} + +static void +qdb_find_function (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + const char *name = P_GSTRING (pr, 1); + dfunction_t *func = PR_FindFunction (tpr, name); + + return_function (pr, func); +} + +static void +qdb_get_function (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + pr_uint_t fnum = P_INT (pr, 1); + dfunction_t *func = tpr->pr_functions + fnum; + + if (fnum >= tpr->progs->functions.count) { + func = 0; + } + return_function (pr, func); +} + +static void +return_auxfunction (progs_t *pr, pr_auxfunction_t *auxfunc) +{ + R_POINTER (pr) = 0; + if (auxfunc) { + __auto_type f + = (qdb_auxfunction_t *) PR_Zone_Malloc (pr, + sizeof (qdb_auxfunction_t)); + f->function = auxfunc->function; + f->source_line = auxfunc->source_line; + f->line_info = auxfunc->line_info; + f->local_defs = auxfunc->local_defs; + f->num_locals = auxfunc->num_locals; + f->return_type = auxfunc->return_type; + R_POINTER (pr) = PR_SetPointer (pr, f); + } +} + +static void +qdb_find_auxfunction (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + const char *name = P_GSTRING (pr, 1); + dfunction_t *func = PR_FindFunction (tpr, name); + pr_uint_t fnum = func - tpr->pr_functions; + pr_auxfunction_t *auxfunc = PR_Debug_MappedAuxFunction (tpr, fnum); + + if (fnum >= tpr->progs->functions.count) { + func = 0; + } + return_auxfunction (pr, auxfunc); +} + +static void +qdb_get_auxfunction (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + pr_uint_t fnum = P_UINT (pr, 1); + pr_auxfunction_t *auxfunc = PR_Debug_MappedAuxFunction (tpr, fnum); + + return_auxfunction (pr, auxfunc); +} + +static void +qdb_get_local_defs (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + pr_uint_t fnum = P_UINT (pr, 1); + pr_auxfunction_t *auxfunc = PR_Debug_MappedAuxFunction (tpr, fnum); + + R_POINTER (pr) = 0; + if (auxfunc && auxfunc->num_locals) { + pr_def_t *defs = PR_Debug_LocalDefs (tpr, auxfunc); + __auto_type qdefs + = (qdb_def_t *) PR_Zone_Malloc (pr, + auxfunc->num_locals * sizeof (qdb_def_t)); + for (unsigned i = 0; i < auxfunc->num_locals; i++) { + qdefs[i].type_size = (defs[i].size << 16) | defs[i].type; + qdefs[i].offset = defs[i].ofs; + qdefs[i].name = defs[i].name; + qdefs[i].type_encoding = defs[i].type_encoding; + } + R_POINTER (pr) = PR_SetPointer (pr, qdefs); + } +} + +static void +qdb_get_source_line_addr (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + const char *file = P_GSTRING (pr, 1); + pr_uint_t line = P_UINT (pr, 2); + + R_UINT (pr) = PR_FindSourceLineAddr (tpr, file, line); +} + +static void +qdb_has_data_stack (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + + R_INT (pr) = tpr->progs->version == PROG_VERSION; +} + +static void +qdb_get_frame_addr (progs_t *pr, void *_res) +{ + __auto_type debug = (qwaq_debug_t *) _res; + pr_ptr_t handle = P_INT (pr, 0); + qwaq_target_t *target = get_target (debug, __FUNCTION__, handle); + progs_t *tpr = target->pr; + + R_UINT (pr) = 0; + + if (tpr->progs->version == PROG_VERSION) { + if (tpr->pr_depth) { + prstack_t *frame = tpr->pr_stack + tpr->pr_depth - 1; + R_UINT (pr) = frame->stack_ptr; + } + } +} + +#define bi(x,np,params...) {#x, x, -1, np, {params}} +#define p(type) PR_PARAM(type) +static builtin_t builtins[] = { + bi(qdb_set_trace, 2, p(int), p(int)), + bi(qdb_set_breakpoint, 2, p(int), p(uint)), + bi(qdb_clear_breakpoint, 2, p(int), p(uint)), + bi(qdb_set_watchpoint, 2, p(int), p(uint)), + bi(qdb_clear_watchpoint, 1, p(int)), + bi(qdb_continue, 1, p(int)), + bi(qdb_get_state, 1, p(int)), + bi(qdb_get_stack_depth, 1, p(int)), + bi(qdb_get_stack, 1, p(int)), + bi(qdb_get_event, 2, p(int), p(ptr)), + bi(qdb_get_data, 4, p(int), p(uint), p(uint), p(ptr)), + {"qdb_get_string|{tag qdb_target_s=}I", qdb_get_string, -1, 1, {p(uint)}}, + {"qdb_get_string|{tag qdb_target_s=}*", qdb_get_string, -1, 1, {p(string)}}, + bi(qdb_get_file_path, 2, p(int), p(string)), + bi(qdb_find_string, 2, p(int), p(string)), + bi(qdb_find_global, 2, p(int), p(string)), + bi(qdb_find_field, 2, p(int), p(string)), + bi(qdb_find_function, 2, p(int), p(string)), + bi(qdb_get_function, 2, p(int), p(uint)), + bi(qdb_find_auxfunction, 2, p(int), p(string)), + bi(qdb_get_auxfunction, 2, p(int), p(uint)), + bi(qdb_get_local_defs, 2, p(int), p(uint)), + bi(qdb_get_source_line_addr, 3, p(int), p(string), p(uint)), + bi(qdb_has_data_stack, 1, p(int)), + bi(qdb_get_frame_addr, 1, p(int)), + {} +}; + +void +QWAQ_Debug_Init (progs_t *pr) +{ + qwaq_debug_t *debug = calloc (sizeof (*debug), 1); + + debug->pr = pr; + debug->input = PR_Resources_Find (pr, "input"); + + PR_AddLoadFunc (pr, qwaq_debug_load); + PR_Resources_Register (pr, "qwaq-debug", debug, qwaq_debug_clear, + qwaq_debug_destroy); + PR_RegisterBuiltins (pr, builtins, debug); +} + +void +QWAQ_DebugTarget_Init (progs_t *pr) +{ + PR_AddLoadFunc (pr, qwaq_target_load); + PR_Resources_Register (pr, "qwaq-target", 0, qwaq_target_clear, + qwaq_target_destroy); +} diff --git a/ruamoko/qwaq/builtins/editbuffer.c b/ruamoko/qwaq/builtins/editbuffer.c new file mode 100644 index 000000000..0631d3c49 --- /dev/null +++ b/ruamoko/qwaq/builtins/editbuffer.c @@ -0,0 +1,1079 @@ +/* + editbuffer.c + + High level wrapper for TextBuffer_Destroy + + Copyright (C) 2020 Bill Currie + + Author: Bill Currie + Date: 2020/03/22 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "QF/progs.h" +#include "QF/ruamoko.h" +#include "QF/quakeio.h" + +#include "QF/ui/txtbuffer.h" + +#include "ruamoko/qwaq/qwaq.h" +#include "ruamoko/qwaq/editor/editbuffer.h" + +#define always_inline inline __attribute__((__always_inline__)) + +typedef struct editbuffer_s { + struct editbuffer_s *next; + struct editbuffer_s **prev; + txtbuffer_t *txtbuffer; + int modified; + int tabSize; +} editbuffer_t; + +typedef struct qwaq_ebresources_s { + progs_t *pr; + PR_RESMAP (editbuffer_t) buffers; + editbuffer_t *buffer_list; +} qwaq_ebresources_t; + +static editbuffer_t * +editbuffer_new (qwaq_ebresources_t *res) +{ + editbuffer_t *buffer = PR_RESNEW (res->buffers); + buffer->next = res->buffer_list; + buffer->prev = &res->buffer_list; + if (res->buffer_list) { + res->buffer_list->prev = &buffer->next; + } + res->buffer_list = buffer; + return buffer; +} + +static void +editbuffer_free (qwaq_ebresources_t *res, editbuffer_t *buffer) +{ + if (buffer->next) { + buffer->next->prev = buffer->prev; + } + *buffer->prev = buffer->next; + PR_RESFREE (res->buffers, buffer); +} + +static void +editbuffer_reset (qwaq_ebresources_t *res) +{ + PR_RESRESET (res->buffers); +} + +static inline editbuffer_t * +editbuffer_get (qwaq_ebresources_t *res, unsigned index) +{ + return PR_RESGET (res->buffers, index); +} + +static inline int __attribute__((pure)) +editbuffer_index (qwaq_ebresources_t *res, editbuffer_t *buffer) +{ + return PR_RESINDEX (res->buffers, buffer); +} + +static always_inline editbuffer_t * __attribute__((pure)) +get_editbuffer (qwaq_ebresources_t *res, const char *name, int handle) +{ + editbuffer_t *buffer = editbuffer_get (res, handle); + + if (!buffer || !buffer->txtbuffer) { + PR_RunError (res->pr, "invalid edit buffer passed to %s", name + 3); + } + return buffer; +} + +static always_inline int +isword (unsigned char c) +{ + return c >= 128 || c == '_' || isalnum(c); +} + +static always_inline unsigned +spanGap (txtbuffer_t *buffer, unsigned ptr) +{ + return ptr < buffer->gapOffset ? ptr : ptr + buffer->gapSize; +} + +static always_inline char +getChar (txtbuffer_t *buffer, unsigned ptr) +{ + return buffer->text[spanGap (buffer, ptr)]; +} + +static always_inline unsigned +nextChar (txtbuffer_t *buffer, unsigned ptr) +{ + if (ptr < buffer->textSize && getChar (buffer, ptr) != '\n') { + return ptr + 1; + } + return ptr; +} + +static always_inline unsigned +prevChar (txtbuffer_t *buffer, unsigned ptr) +{ + if (ptr > 0 && getChar (buffer, ptr - 1) != '\n') { + return ptr - 1; + } + return ptr; +} + +static always_inline unsigned __attribute__((pure)) +nextNonSpace (txtbuffer_t *buffer, unsigned ptr) +{ + while (ptr < buffer->textSize) { + char c = getChar (buffer, ptr); + if (!isspace (c) || c == '\n') { + break; + } + ptr++; + } + return ptr; +} + +static always_inline unsigned +prevNonSpace (txtbuffer_t *buffer, unsigned ptr) +{ + while (ptr > 0) { + char c = getChar (buffer, ptr - 1); + if (!isspace (c) || c == '\n') { + break; + } + ptr--; + } + return ptr; +} + +static always_inline unsigned __attribute__((pure)) +nextWord (txtbuffer_t *buffer, unsigned ptr) +{ + if (buffer->textSize && ptr < buffer->textSize - 1) { + while (ptr < buffer->textSize) { + char c = getChar (buffer, ptr); + if (!isword (c)) { + break; + } + ptr++; + } + while (ptr < buffer->textSize) { + char c = getChar (buffer, ptr); + if (c == '\n') { + return ptr; + } + if (isword (c)) { + break; + } + ptr++; + } + } + return ptr; +} + +static always_inline unsigned +prevWord (txtbuffer_t *buffer, unsigned ptr) +{ + if (ptr > 0) { + while (ptr > 0) { + char c = getChar (buffer, ptr - 1); + if (c == '\n') { + return ptr; + } + if (isword (c)) { + break; + } + ptr--; + } + while (ptr > 0) { + char c = getChar (buffer, ptr - 1); + if (c == '\n') { + return ptr; + } + if (!isword (c)) { + break; + } + ptr--; + } + } + return ptr; +} + +static always_inline unsigned __attribute__((pure)) +nextLine (txtbuffer_t *buffer, unsigned ptr) +{ + unsigned oldptr = ptr; + while (ptr < buffer->textSize && getChar (buffer, ptr++) != '\n') { + } + if (ptr == buffer->textSize && ptr > 0 + && getChar (buffer, ptr - 1) != '\n') { + return oldptr; + } + return ptr; +} + +static always_inline unsigned +prevLine (txtbuffer_t *buffer, unsigned ptr) +{ + if (ptr) { + ptr--; + while (ptr > 0 && getChar (buffer, ptr - 1) != '\n') { + ptr--; + } + } + return ptr; +} + +static always_inline unsigned +charPos (txtbuffer_t *buffer, unsigned ptr, unsigned target, int tabSize) +{ + unsigned pos = 0; + + while (ptr < target) { + if (getChar (buffer, ptr) == '\t') { + pos += tabSize - (pos % tabSize) - 1; // -1 for ++ + } + pos++; + ptr++; + } + return pos; +} + +static always_inline unsigned __attribute__((pure)) +charPtr (txtbuffer_t *buffer, unsigned ptr, unsigned target, int tabSize) +{ + unsigned pos = 0; + while (pos < target && ptr < buffer->textSize + && getChar (buffer, ptr) != '\n') { + if (getChar (buffer, ptr) == '\t') { + pos += tabSize - (pos % tabSize) - 1; // -1 for ++ + } + pos++; + ptr++; + } + if (pos > target) { + ptr--; + } + return ptr; +} + +static always_inline unsigned __attribute__((pure)) +getEOW (txtbuffer_t *buffer, unsigned ptr) +{ + while (ptr < buffer->textSize) { + char c = getChar (buffer, ptr); + if (!isword (c)) { + break; + } + ptr++; + } + return ptr; +} + +static always_inline unsigned +getBOW (txtbuffer_t *buffer, unsigned ptr) +{ + while (ptr > 0) { + char c = getChar (buffer, ptr - 1); + if (!isword (c)) { + break; + } + ptr--; + } + return ptr; +} + +static always_inline unsigned __attribute__((pure)) +getEOL (txtbuffer_t *buffer, unsigned ptr) +{ + while (ptr < buffer->textSize) { + char c = getChar (buffer, ptr); + if (c == '\n') { + break; + } + ptr++; + } + return ptr; +} + +static always_inline void +readString (txtbuffer_t *buffer, eb_sel_t *sel, char *str) +{ + unsigned start = sel->start; + unsigned length = sel->length; + unsigned end = sel->start + sel->length; + const char *ptr = buffer->text + spanGap (buffer, start); + if ((start < buffer->gapOffset && end <= buffer->gapOffset) + || start > buffer->gapOffset) { + memcpy (str, ptr, length); + } else { + length = buffer->gapOffset - start; + memcpy (str, ptr, length); + str += length; + ptr += length + buffer->gapOffset; + memcpy (str, ptr, sel->length - length); + } +} + +static always_inline unsigned +getBOL (txtbuffer_t *buffer, unsigned ptr) +{ + while (ptr > 0) { + char c = getChar (buffer, ptr - 1); + if (c == '\n') { + break; + } + ptr--; + } + return ptr; +} + +static always_inline unsigned +_countLines (txtbuffer_t *buffer, unsigned start, unsigned len) +{ + unsigned count = 0; + char *ptr = buffer->text + start; + + while (len-- > 0) { + count += *ptr++ == '\n'; + } + return count; +} + +static always_inline unsigned +countLines (txtbuffer_t *buffer, unsigned start, unsigned len) +{ + if (start < buffer->gapOffset) { + if (start + len <= buffer->gapOffset) { + return _countLines (buffer, start, len); + } else { + return _countLines (buffer, start, buffer->gapOffset - start) + + _countLines (buffer, buffer->gapOffset + buffer->gapSize, + len - (buffer->gapOffset - start)); + } + } else { + return _countLines (buffer, start + buffer->gapOffset, len); + } +} + +static const char * +_search (txtbuffer_t *buffer, const char *ptr, unsigned len, + const char *str, unsigned slen, int dir, + int (*cmp)(const char *, const char *, size_t)) +{ + while (len-- > 0) { + if (*ptr == *str) { + unsigned offset = ptr - buffer->text; + if (offset > buffer->gapOffset + || offset + slen < buffer->gapOffset) { + // search does not span gap + if (cmp (ptr, str, slen) == 0) { + return ptr; + } + } else { + // search spans gap + unsigned l = buffer->gapOffset - offset; + if (cmp (ptr, str, l) == 0 + && cmp (ptr + l + buffer->gapSize, + str + l, slen - l) == 0) { + return ptr; + } + } + } else { + ptr += dir; + } + } + return 0; +} + +static int +search (txtbuffer_t *buffer, const eb_sel_t *sel, const char *str, int dir, + eb_sel_t *find, int (*cmp)(const char *, const char *, size_t)) +{ + unsigned start = sel->start; + unsigned slen = strlen (str); + unsigned end = start + sel->length - slen; + const char *found = 0; + + find->start = 0; + find->length = 0; + + if (sel->length >= slen) { + if (dir < 0) { + const char *s = buffer->text + spanGap (buffer, end); + if ((start < buffer->gapOffset && end <= buffer->gapOffset) + || start > buffer->gapOffset) { + found = _search (buffer, s, end - start, str, slen, -1, cmp); + } else { + unsigned l = end - (buffer->gapOffset + buffer->gapSize); + found = _search (buffer, s, l, str, slen, 1, cmp); + if (!found) { + s -= l + buffer->gapSize; + l = buffer->gapOffset - start; + found = _search (buffer, s, l, str, slen, 1, cmp); + } + } + } else { + const char *s = buffer->text + spanGap (buffer, start); + if ((start < buffer->gapOffset && end <= buffer->gapOffset) + || start > buffer->gapOffset) { + found = _search (buffer, s, end - start, str, slen, 1, cmp); + } else { + unsigned l = buffer->gapOffset - start; + found = _search (buffer, s, l, str, slen, 1, cmp); + if (!found) { + s += l + buffer->gapSize; + l = end - start - l; + found = _search (buffer, s, l, str, slen, 1, cmp); + } + } + } + } + if (found) { + unsigned offset = found - buffer->text; + if (offset > buffer->gapOffset) { + offset -= buffer->gapSize; + } + find->start = offset; + find->length = slen; + return 1; + } + return 0; +} + +static int +saveFile (txtbuffer_t *buffer, const char *filename) +{ + QFile *file = Qopen (filename, "wt"); + + if (file) { + unsigned offset = buffer->gapOffset + buffer->gapSize; + Qwrite (file, buffer->text, buffer->gapOffset); + Qwrite (file, buffer->text + offset, buffer->textSize - offset); + Qclose (file); + return 1; + } + return 0; +} + +static int +loadFile (txtbuffer_t *buffer, const char *filename) +{ + QFile *file = Qopen (filename, "rtz"); + char *dst; + unsigned len; + + if (file) { + len = Qfilesize (file); + // ensure buffer is empty + TextBuffer_DeleteAt (buffer, 0, buffer->textSize); + dst = TextBuffer_OpenGap (buffer, 0, len); + Qread (file, dst, len); + + buffer->textSize += len; + buffer->gapOffset += len; + buffer->gapSize -= len; + + Qclose (file); + return 1; + } + return 0; +} + +static unsigned +formatLine (txtbuffer_t *buffer, unsigned linePtr, unsigned xpos, + int *dst, unsigned length, eb_sel_t *selection, eb_color_t *colors, + int tabSize) +{ + unsigned pos = 0; + unsigned ptr = linePtr; + unsigned sels = selection->start; + unsigned sele = selection->start + selection->length; + int coln = (colors->normal & ~0xff); + int cols = (colors->selected & ~0xff); + int col; + byte c = 0; + int count; + int *startdst = dst; + int startlen = length; + + while (pos < xpos && ptr < buffer->textSize) { + c = getChar (buffer, ptr); + if (c == '\n') { + break; + } + if (c == '\t') { + pos += tabSize - (pos % tabSize) - 1; // -1 for ++ + } + pos++; + ptr++; + } + col = ptr >= sels && ptr < sele ? cols : coln; + while (xpos < pos && length > 0) { + *dst++ = col | ' '; + length--; + xpos++; + } + while (length > 0 && ptr < buffer->textSize) { + col = ptr >= sels && ptr < sele ? cols : coln; + c = getChar (buffer, ptr++); + if (c == '\n') { + break; + } + count = 1; + if (c == '\t') { + c = ' '; + count = tabSize - (pos % tabSize); + } + while (length > 0 && count-- > 0) { + *dst++ = col | c; + pos++; + length--; + } + } + while (c != '\n' && ptr < buffer->textSize) { + c = getChar (buffer, ptr++); + } + while (length-- > 0) { + *dst++ = col | ' '; + } + if (dst - startdst > startlen) { + Sys_Error ("formatLine wrote too much: %zd %u %d", + dst - startdst, startlen, length); + } + return ptr; +} + +//=== + +static void +bi__i_EditBuffer__init (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + __auto_type self = &P_STRUCT (pr, qwaq_editbuffer_t, 0); + // [super init]; + RUA_obj_increment_retaincount (pr, pr->pr_objective_resources); + txtbuffer_t *txtbuffer = TextBuffer_Create (); + editbuffer_t *buffer = editbuffer_new (res); + + buffer->txtbuffer = txtbuffer; + buffer->tabSize = 4; // FIXME + + self->buffer = editbuffer_index (res, buffer); + R_INT (pr) = PR_SetPointer (pr, self); +} + +static void +bi__i_EditBuffer__initWithFile_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + __auto_type self = &P_STRUCT (pr, qwaq_editbuffer_t, 0); + // [super init]; + RUA_obj_increment_retaincount (pr, pr->pr_objective_resources); + const char *filename = P_GSTRING (pr, 2); + txtbuffer_t *txtbuffer = TextBuffer_Create (); + editbuffer_t *buffer = editbuffer_new (res); + + buffer->txtbuffer = txtbuffer; + buffer->tabSize = 4; // FIXME + + loadFile (buffer->txtbuffer, filename); + + self->buffer = editbuffer_index (res, buffer); + R_INT (pr) = PR_SetPointer (pr, self); +} + +static void +bi__i_EditBuffer__dealloc (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + __auto_type self = &P_STRUCT (pr, qwaq_editbuffer_t, 0); + int buffer_id = self->buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + + TextBuffer_Destroy (buffer->txtbuffer); + editbuffer_free (res, buffer); +} + +static void +bi__i_EditBuffer__nextChar_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + unsigned ptr = P_UINT (pr, 2); + + R_INT (pr) = nextChar (buffer->txtbuffer, ptr); +} + +static void +bi__i_EditBuffer__prevChar_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + unsigned ptr = P_UINT (pr, 2); + + R_INT (pr) = prevChar (buffer->txtbuffer, ptr); +} + +static void +bi__i_EditBuffer__nextNonSpace_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + unsigned ptr = P_UINT (pr, 2); + + R_INT (pr) = nextNonSpace (buffer->txtbuffer, ptr); +} + +static void +bi__i_EditBuffer__prevNonSpace_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + unsigned ptr = P_UINT (pr, 2); + + R_INT (pr) = prevNonSpace (buffer->txtbuffer, ptr); +} + +static void +bi__i_EditBuffer__isWord_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + unsigned ptr = P_UINT (pr, 2); + + R_INT (pr) = isword (getChar (buffer->txtbuffer, ptr)); +} + +static void +bi__i_EditBuffer__nextWord_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + unsigned ptr = P_UINT (pr, 2); + + R_INT (pr) = nextWord (buffer->txtbuffer, ptr); +} + +static void +bi__i_EditBuffer__prevWord_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + unsigned ptr = P_UINT (pr, 2); + + R_INT (pr) = prevWord (buffer->txtbuffer, ptr); +} + +static void +bi__i_EditBuffer__nextLine_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + unsigned ptr = P_UINT (pr, 2); + + R_INT (pr) = nextLine (buffer->txtbuffer, ptr); +} + +static void +bi__i_EditBuffer__prevLine_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + unsigned ptr = P_UINT (pr, 2); + + R_INT (pr) = prevLine (buffer->txtbuffer, ptr); +} + +static void +bi__i_EditBuffer__nextLine__ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + unsigned ptr = P_UINT (pr, 2); + unsigned count = P_UINT (pr, 3); + + while (count-- > 0) { + unsigned oldptr = ptr; + ptr = nextLine (buffer->txtbuffer, ptr); + if (ptr == buffer->txtbuffer->textSize && ptr > 0 + && getChar (buffer->txtbuffer, ptr - 1) != '\n') { + ptr = oldptr; + break; + } + } + R_INT (pr) = ptr; +} + +static void +bi__i_EditBuffer__prevLine__ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + unsigned ptr = P_UINT (pr, 2); + unsigned count = P_UINT (pr, 3); + + while (ptr && count-- > 0) { + ptr = prevLine (buffer->txtbuffer, ptr); + } + R_INT (pr) = ptr; +} + +static void +bi__i_EditBuffer__charPos_at_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + unsigned ptr = P_UINT (pr, 2); + unsigned target = P_UINT (pr, 3); + int tabSize = buffer->tabSize; + + R_INT (pr) = charPos (buffer->txtbuffer, ptr, target, tabSize); +} + +static void +bi__i_EditBuffer__charPtr_at_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + unsigned ptr = P_UINT (pr, 2); + unsigned target = P_UINT (pr, 3); + int tabSize = buffer->tabSize; + + R_INT (pr) = charPtr (buffer->txtbuffer, ptr, target, tabSize); +} + +static void +bi__i_EditBuffer__getWord_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + unsigned ptr = P_UINT (pr, 2); + unsigned s = getBOW (buffer->txtbuffer, ptr); + unsigned e = getEOW (buffer->txtbuffer, ptr); + + R_PACKED (pr, eb_sel_t).start = s; + R_PACKED (pr, eb_sel_t).length = e - s; +} + +static void +bi__i_EditBuffer__getLine_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + unsigned ptr = P_UINT (pr, 2); + unsigned s = getBOL (buffer->txtbuffer, ptr); + unsigned e = getEOL (buffer->txtbuffer, ptr); + + R_PACKED (pr, eb_sel_t).start = s; + R_PACKED (pr, eb_sel_t).length = e - s; +} + +static void +bi__i_EditBuffer__getBOL_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + unsigned ptr = P_UINT (pr, 2); + + R_INT (pr) = getBOL (buffer->txtbuffer, ptr); +} + +static void +bi__i_EditBuffer__getEOL_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + unsigned ptr = P_UINT (pr, 2); + + R_INT (pr) = getEOL (buffer->txtbuffer, ptr); +} + +static void +bi__i_EditBuffer__getBOT (progs_t *pr, void *_res) +{ + //qwaq_ebresources_t *res = _res; + //int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + //editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + + R_INT (pr) = 0; +} + +static void +bi__i_EditBuffer__getEOT (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + + R_INT (pr) = buffer->txtbuffer->textSize; +} + +static void +bi__i_EditBuffer__readString_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + __auto_type selection = &P_PACKED (pr, eb_sel_t, 2); + + char *str = alloca (selection->length + 1); + str[selection->length] = 0; + readString (buffer->txtbuffer, selection, str); + + RETURN_STRING (pr, str); +} + +static void +bi__i_EditBuffer__getChar_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + unsigned ptr = P_UINT (pr, 2); + + if (ptr >= buffer->txtbuffer->textSize) { + PR_RunError (pr, "EditBuffer: character index out of bounds\n"); + } + R_INT (pr) = (byte) getChar (buffer->txtbuffer, ptr); +} + +static void +bi__i_EditBuffer__putChar_at_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + int chr = P_UINT (pr, 2); + unsigned ptr = P_UINT (pr, 3); + + if (ptr >= buffer->txtbuffer->textSize) { + PR_RunError (pr, "EditBuffer: character index out of bounds\n"); + } + buffer->txtbuffer->text[spanGap (buffer->txtbuffer, ptr)] = chr; +} + +static void +bi__i_EditBuffer__insertChar_at_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + char chr = P_UINT (pr, 2); + unsigned ptr = P_UINT (pr, 3); + + if (ptr > buffer->txtbuffer->textSize) { + PR_RunError (pr, "EditBuffer: character index out of bounds\n"); + } + TextBuffer_InsertAt (buffer->txtbuffer, ptr, &chr, 1); +} + +static void +bi__i_EditBuffer__countLines_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + __auto_type selection = &P_PACKED (pr, eb_sel_t, 2); + + R_INT (pr) = countLines (buffer->txtbuffer, + selection->start, selection->length); +} + +static void +bi__i_EditBuffer__search_for_direction_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + __auto_type selection = &P_PACKED (pr, eb_sel_t, 2); + const char *str = P_GSTRING (pr, 3); + int dir = P_INT (pr, 4); + + search (buffer->txtbuffer, selection, str, dir, &R_PACKED (pr, eb_sel_t), + strncmp); +} + +static void +bi__i_EditBuffer__isearch_for_direction_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + __auto_type selection = &P_PACKED (pr, eb_sel_t, 2); + const char *str = P_GSTRING (pr, 3); + int dir = P_INT (pr, 4); + + search (buffer->txtbuffer, selection, str, dir, &R_PACKED (pr, eb_sel_t), + strncasecmp); +} + +static void +bi__i_EditBuffer__formatLine_from_into_width_highlight_colors_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + unsigned linePtr = P_UINT (pr, 2); + unsigned xpos = P_UINT (pr, 3); + int *dst = (int *) P_GPOINTER (pr, 4); + unsigned length = P_UINT (pr, 5); + __auto_type selection = &P_PACKED (pr, eb_sel_t, 6); + __auto_type colors = &P_PACKED (pr, eb_color_t, 7); + int tabSize = buffer->tabSize; + unsigned ptr; + + ptr = formatLine (buffer->txtbuffer, linePtr, xpos, dst, length, selection, + colors, tabSize); + R_INT (pr) = ptr; +} + +static void +bi__i_EditBuffer__modified (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + R_INT (pr) = buffer->modified; +} + +static void +bi__i_EditBuffer__textSize (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + R_INT (pr) = buffer->txtbuffer->textSize; +} + +static void +bi__i_EditBuffer__saveFile_ (progs_t *pr, void *_res) +{ + qwaq_ebresources_t *res = _res; + int buffer_id = P_STRUCT (pr, qwaq_editbuffer_t, 0).buffer; + editbuffer_t *buffer = get_editbuffer (res, __FUNCTION__, buffer_id); + const char *filename = P_GSTRING (pr, 2); + + if (saveFile (buffer->txtbuffer, filename)) { + buffer->modified = 0; + } +} + +static void +qwaq_ebresources_clear (progs_t *pr, void *_res) +{ + __auto_type res = (qwaq_ebresources_t *) _res; + + for (editbuffer_t *buffer = res->buffer_list; buffer; + buffer = buffer->next) { + TextBuffer_Destroy (buffer->txtbuffer); + buffer->txtbuffer = 0; + } + editbuffer_reset (res); +} + +static void +qwaq_ebresources_destroy (progs_t *pr, void *_res) +{ + free (_res); +} + +#define bi(x,n,np,params...) {#x, bi_##x, n, np, {params}} +#define p(type) PR_PARAM(type) +#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), } +static builtin_t builtins[] = { + bi(_i_EditBuffer__init, -1, 2, p(ptr), p(ptr)), + bi(_i_EditBuffer__initWithFile_, -1, 3, p(ptr), p(ptr), p(string)), + bi(_i_EditBuffer__dealloc, -1, 2, p(ptr), p(ptr)), + bi(_i_EditBuffer__nextChar_, -1, 3, p(ptr), p(ptr), p(uint)), + bi(_i_EditBuffer__prevChar_, -1, 3, p(ptr), p(ptr), p(uint)), + bi(_i_EditBuffer__nextNonSpace_, -1, 3, p(ptr), p(ptr), p(uint)), + bi(_i_EditBuffer__prevNonSpace_, -1, 3, p(ptr), p(ptr), p(uint)), + bi(_i_EditBuffer__isWord_, -1, 3, p(ptr), p(ptr), p(uint)), + bi(_i_EditBuffer__nextWord_, -1, 3, p(ptr), p(ptr), p(uint)), + bi(_i_EditBuffer__prevWord_, -1, 3, p(ptr), p(ptr), p(uint)), + bi(_i_EditBuffer__nextLine_, -1, 3, p(ptr), p(ptr), p(uint)), + bi(_i_EditBuffer__prevLine_, -1, 3, p(ptr), p(ptr), p(uint)), + bi(_i_EditBuffer__nextLine__, -1, 4, p(ptr), p(ptr), p(uint), p(uint)), + bi(_i_EditBuffer__prevLine__, -1, 4, p(ptr), p(ptr), p(uint), p(uint)), + bi(_i_EditBuffer__charPos_at_, -1, 4, p(ptr), p(ptr), p(uint), p(uint)), + bi(_i_EditBuffer__charPtr_at_, -1, 4, p(ptr), p(ptr), p(uint), p(uint)), + bi(_i_EditBuffer__getWord_, -1, 3, p(ptr), p(ptr), p(uint)), + bi(_i_EditBuffer__getLine_, -1, 3, p(ptr), p(ptr), p(uint)), + bi(_i_EditBuffer__getBOL_, -1, 3, p(ptr), p(ptr), p(uint)), + bi(_i_EditBuffer__getEOL_, -1, 3, p(ptr), p(ptr), p(uint)), + bi(_i_EditBuffer__getBOT, -1, 2, p(ptr), p(ptr)), + bi(_i_EditBuffer__getEOT, -1, 2, p(ptr), p(ptr)), + bi(_i_EditBuffer__readString_, -1, 3, p(ptr), p(ptr), P(1, 2)), + bi(_i_EditBuffer__getChar_, -1, 3, p(ptr), p(ptr), p(uint)), + bi(_i_EditBuffer__putChar_at_, -1, 4, p(ptr), p(ptr), p(int), p(uint)), + bi(_i_EditBuffer__insertChar_at_, -1, 4, p(ptr), p(ptr), p(int), p(uint)), + bi(_i_EditBuffer__countLines_, -1, 3, p(ptr), p(ptr), P(1, 2)), + bi(_i_EditBuffer__search_for_direction_, -1, + 5, p(ptr), p(ptr), P(1, 2), p(string), p(int)), + bi(_i_EditBuffer__isearch_for_direction_,-1, + 5, p(ptr), p(ptr), P(1, 2), p(string), p(int)), + bi(_i_EditBuffer__formatLine_from_into_width_highlight_colors_, -1, + 8, p(ptr), p(ptr), + p(uint), p(uint), p(ptr), p(uint), P(1, 2), P(1, 2)), + bi(_i_EditBuffer__modified, -1, 2, p(ptr), p(ptr)), + bi(_i_EditBuffer__textSize, -1, 2, p(ptr), p(ptr)), + bi(_i_EditBuffer__saveFile_, -1, 3, p(ptr), p(ptr), p(string)), + {} +}; + +void +QWAQ_EditBuffer_Init (progs_t *pr) +{ + qwaq_ebresources_t *res = calloc (sizeof (*res), 1); + res->pr = pr; + + PR_Resources_Register (pr, "qwaq-editbuffer", res, qwaq_ebresources_clear, + qwaq_ebresources_destroy); + PR_RegisterBuiltins (pr, builtins, res); +} diff --git a/ruamoko/qwaq/builtins/graphics.c b/ruamoko/qwaq/builtins/graphics.c new file mode 100644 index 000000000..3a9fb24cd --- /dev/null +++ b/ruamoko/qwaq/builtins/graphics.c @@ -0,0 +1,410 @@ +/* + graphics.c + + Basic game engine builtins + + Copyright (C) 2012 Bill Currie + + Author: Bill Currie + Date: 2012/7/24 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static __attribute__ ((used)) const char rcsid[] = "$Id$"; + +#include +#include +#include +#include +#include +#include + +#include "QF/cbuf.h" +#include "QF/draw.h" +#include "QF/image.h" +#include "QF/input.h" +#include "QF/keys.h" +#include "QF/progs.h" +#include "QF/quakefs.h" +#include "QF/render.h" +#include "QF/ruamoko.h" +#include "QF/screen.h" +#include "QF/sound.h" + +#include "QF/input/event.h" +#include "QF/math/bitop.h" + +#include "QF/plugin/console.h" +#include "QF/plugin/vid_render.h" +#include "QF/ui/canvas.h" +#include "QF/ui/font.h" +#include "QF/ui/text.h" + +#include "rua_internal.h" + +#include "ruamoko/qwaq/qwaq.h" + +CLIENT_PLUGIN_PROTOS +static plugin_list_t client_plugin_list[] = { + CLIENT_PLUGIN_LIST +}; + +double con_frametime; +double con_realtime, basetime; +double old_conrealtime; + + +static void +quit_f (void) +{ + if (!con_module) + Sys_Printf ("I hope you wanted to quit\n"); + Sys_Quit (); +} + +static progs_t *bi_rprogs; +static pr_func_t qc2d; +static int event_handler_id; +static canvas_system_t canvas_sys; +static view_t screen_view; +static uint32_t canvas; + +static void +bi_2d (void) +{ + if (qc2d) + PR_ExecuteProgram (bi_rprogs, qc2d); + Con_DrawConsole (); + Canvas_Draw (canvas_sys); +} + +static SCR_Func bi_2dfuncs[] = { + bi_2d, + 0, +}; + +static void +bi_newscene (progs_t *pr, void *_res) +{ + pr_ulong_t scene_id = P_ULONG (pr, 0); + SCR_NewScene (Scene_GetScene (pr, scene_id)); +} + +static void +bi_refresh (progs_t *pr, void *_res) +{ + con_realtime = Sys_DoubleTime () - basetime; + con_frametime = con_realtime - old_conrealtime; + old_conrealtime = con_realtime; + bi_rprogs = pr; + IN_ProcessEvents (); + //GIB_Thread_Execute (); + Cbuf_Execute_Stack (qwaq_cbuf); + SCR_UpdateScreen (nulltransform, con_realtime, bi_2dfuncs); + R_FLOAT (pr) = con_frametime; +} + +static void +bi_refresh_2d (progs_t *pr, void *_res) +{ + qc2d = P_FUNCTION (pr, 0); +} + +static void +bi_setpalette (progs_t *pr, void *_res) +{ + byte *palette = (byte *) P_GPOINTER (pr, 0); + byte *colormap = (byte *) P_GPOINTER (pr, 1); + VID_SetPalette (palette, colormap); +} + +static void +bi_shutdown (progs_t *pr, void *_res) +{ + Sys_Shutdown (); +} + +#define bi(x,n,np,params...) {#x, bi_##x, n, np, {params}} +#define p(type) PR_PARAM(type) +static builtin_t builtins[] = { + bi(newscene, -1, 1, p(long)), + bi(refresh, -1, 0), + bi(refresh_2d, -1, 1, p(func)), + bi(setpalette, -1, 2, p(ptr), p(ptr)), + bi(shutdown, -1, 0), + {0} +}; + +static int +event_handler (const IE_event_t *ie_event, void *_pr) +{ + // FIXME rethink event handling for qwaq + if (ie_event->type == ie_key && ie_event->key.code == QFK_ESCAPE) { + Con_SetState (con_active); + return 1; + } + return IN_Binding_HandleEvent (ie_event); +} + +static void +BI_shutdown (void *data) +{ + ECS_DelRegistry (canvas_sys.reg); + ColorCache_Shutdown (); +} + +static byte default_palette[256][3]; + +static byte * +write_raw_rgb (byte *dst, byte r, byte g, byte b) +{ + *dst++ = r; + *dst++ = g; + *dst++ = b; + return dst; +} + +static byte * +write_rgb (byte *dst, byte r, byte g, byte b) +{ +#define shift(x) (((x) << 2) | (((x) & 0x3f) >> 4)) + *dst++ = shift(r); + *dst++ = shift(g); + *dst++ = shift(b); + return dst; +#undef shift +} + +static byte * +write_grey (byte *dst, byte grey) +{ + return write_rgb (dst, grey, grey, grey); +} + +static byte * +genererate_irgb (byte *dst, byte lo, byte melo, byte mehi, byte hi) +{ + for (int i = 0; i < 16; i++) { + byte l = i & 8 ? melo : lo; + byte h = i & 8 ? hi : mehi; + byte r = i & 4 ? h : l; + byte g = i & 2 ? h : l; + byte b = i & 1 ? h : l; + if (i == 6) { // make dim yellow brown + g = melo; + } + dst = write_rgb (dst, r, g, b); + } + return dst; +} + +static byte * +write_run (byte *dst, byte *hue, byte ch, const byte *levels) +{ + byte rgb[3] = { + *hue & 4 ? levels[4] : levels[0], + *hue & 2 ? levels[4] : levels[0], + *hue & 1 ? levels[4] : levels[0], + }; + byte chan = 2 - BITOP_LOG2 (ch); + int ind = *hue & ch ? 4 : 0; + int dir = *hue & ch ? -1 : 1; + + for (int i = 0; i < 4; i++, ind += dir) { + rgb[chan] = levels[ind]; + dst = write_rgb (dst, rgb[0], rgb[1], rgb[2]); + } + + *hue ^= ch; + return dst; +} + +static byte * +write_cycle (byte *dst, byte lo, byte melo, byte me, byte mehi, byte hi) +{ + const byte levels[] = { lo, melo, me, mehi, hi }; + byte hue = 1; + dst = write_run (dst, &hue, 4, levels); + dst = write_run (dst, &hue, 1, levels); + dst = write_run (dst, &hue, 2, levels); + + dst = write_run (dst, &hue, 4, levels); + dst = write_run (dst, &hue, 1, levels); + dst = write_run (dst, &hue, 2, levels); + return dst; +} + +static void +generate_palette (void) +{ + const byte grey[] = { + 0, 5, 8, 11, 14, 17, 20, 24, + 28, 32, 36, 40, 45, 50, 56, 63, + }; + byte *dst = default_palette[0]; + + dst = genererate_irgb (dst, 0, 21, 42, 63); + + for (int i = 0; i < 16; i++) { + dst = write_grey (dst, grey[i]); + } + + dst = write_cycle (dst, 11, 12, 13, 15, 16); + dst = write_cycle (dst, 8, 10, 12, 14, 16); + dst = write_cycle (dst, 0, 4, 8, 12, 16); + + dst = write_cycle (dst, 20, 22, 24, 26, 28); + dst = write_cycle (dst, 14, 17, 21, 24, 28); + dst = write_cycle (dst, 0, 7, 14, 21, 28); + + dst = write_cycle (dst, 45, 49, 54, 58, 63); + dst = write_cycle (dst, 31, 39, 47, 55, 63); + dst = write_cycle (dst, 0, 16, 31, 47, 63); + + // std is black, but want vis trans and some phosphors + dst = write_raw_rgb (dst, 40, 40, 40); + dst = write_raw_rgb (dst, 40, 127, 40); + dst = write_raw_rgb (dst, 51, 255, 51); + dst = write_raw_rgb (dst, 102, 255, 102); + dst = write_raw_rgb (dst, 127, 102, 0); + dst = write_raw_rgb (dst, 255, 176, 0); + dst = write_raw_rgb (dst, 255, 204, 0); + + dst = write_raw_rgb (dst, 176, 160, 176); +} + +static byte default_colormap[64 * 256 + 1] = { [64 * 256] = 32 }; + +static void +generate_colormap (void) +{ + byte colors[64][256][3]; + tex_t tex = { + .width = 256, + .height = 64, + .format = tex_rgb, + .data = colors[0][0], + }; + + // baseline colors + for (int i = 0; i < 256; i++) { + VectorCopy (default_palette[i], colors[31][i]); + VectorCopy (default_palette[i], colors[32][i]); + } + for (int i = 0; i < 64; i++) { + // main colors + if (i < 31 || i > 32) { + float scale = i < 32 ? 2 - i / 31.0 : 1 - (i - 32) / 31.0; + for (int j = 0; j < 224; j++) { + for (int k = 0; k < 3; k++) { + colors[i][j][k] = bound (0, scale * colors[31][j][k], 255); + } + } + } + // fullbrights, but avoid copying the source row to itself + if (i != 31) { + memcpy (colors[i][224], colors[31][224], 32 * 3); + } + } + tex_t *cmap = ConvertImage (&tex, default_palette[0]); + // the colormap has an extra byte indicating the number of fullbright + // entries, but that byte is not in the image, so don't try to copy it, + // thus the - 1 + memcpy (default_colormap, cmap->data, sizeof (default_colormap) - 1); + free (cmap); +} + +static void +vidsize_listener (void *data, const viddef_t *vdef) +{ + Canvas_SetLen (canvas_sys, (view_pos_t) { vdef->width, vdef->height }); +} + +void +BI_Graphics_Init (progs_t *pr) +{ + qwaq_thread_t *thread = PR_Resources_Find (pr, "qwaq_thread"); + + PR_RegisterBuiltins (pr, builtins, 0); + + QFS_Init (thread->hunk, "nq"); + PI_Init (); + PI_RegisterPlugins (client_plugin_list); + + Sys_RegisterShutdown (BI_shutdown, pr); + + VID_Init_Cvars (); + IN_Init_Cvars (); + Mod_Init_Cvars (); + S_Init_Cvars (); + + generate_palette (); + generate_colormap (); + + VID_Init (default_palette[0], default_colormap); + IN_Init (); + Mod_Init (); + R_Init (); + Font_Init (); + + R_Progs_Init (pr); + RUA_Game_Init (pr, thread->rua_security); + S_Progs_Init (pr); + + event_handler_id = IE_Add_Handler (event_handler, pr); + IE_Set_Focus (event_handler_id); + + Con_Load ("client"); + __auto_type reg = ECS_NewRegistry (); + Canvas_InitSys (&canvas_sys, reg); + if (con_module) { + __auto_type cd = con_module->data->console; + cd->realtime = &con_realtime; + cd->frametime = &con_frametime; + cd->quit = quit_f; + cd->cbuf = qwaq_cbuf; + cd->component_base = ECS_RegisterComponents (reg, cd->components, + cd->num_components); + cd->canvas_sys = &canvas_sys; + } + ECS_CreateComponentPools (reg); + + canvas = Canvas_New (canvas_sys); + screen_view = Canvas_GetRootView (canvas_sys, canvas); + View_SetPos (screen_view, 0, 0); + View_SetLen (screen_view, viddef.width, viddef.height); + View_SetGravity (screen_view, grav_northwest); + View_SetVisible (screen_view, 1); + + VID_OnVidResize_AddListener (vidsize_listener, 0); + + //Key_SetKeyDest (key_game); + Con_Init (); + vidsize_listener (0, &viddef); //FIXME this probably shouldn't be needed + + S_Init (0, &con_frametime); + //CDAudio_Init (); + Con_NewMap (); + basetime = Sys_DoubleTime (); +} diff --git a/ruamoko/qwaq/builtins/main.c b/ruamoko/qwaq/builtins/main.c new file mode 100644 index 000000000..b71e5bef8 --- /dev/null +++ b/ruamoko/qwaq/builtins/main.c @@ -0,0 +1,490 @@ +/* + main.c + + Qwaq + + Copyright (C) 2001 Bill Currie + + Author: Bill Currie + Date: 2001/06/01 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "QF/cbuf.h" +#include "QF/cmd.h" +#include "QF/cvar.h" +#include "QF/gib.h" +#include "QF/hash.h" +#include "QF/idparse.h" +#include "QF/keys.h" +#include "QF/progs.h" +#include "QF/qargs.h" +#include "QF/quakefs.h" +#include "QF/ruamoko.h" +#include "QF/sys.h" +#include "QF/va.h" +#include "QF/zone.h" + +#include "compat.h" +#include "rua_internal.h" + +#include "ruamoko/qwaq/qwaq.h" +#include "ruamoko/qwaq/debugger/debug.h" + +#define MAX_EDICTS 1024 + +cbuf_t *qwaq_cbuf; + +const char *this_program; + +enum { + start_opts = 255, + OPT_QARGS, +}; + +static struct option const long_options[] = { + {"qargs", no_argument, 0, OPT_QARGS}, + {NULL, 0, NULL, 0}, +}; + +static const char *short_options = + "-" // magic option parsing mode doohicky (must come first) + ; + +qwaq_thread_set_t thread_data; + +static QFile * +open_file (const char *path, int *len) +{ + QFile *file = Qopen (path, "rbz"); + char errbuff[1024]; + + if (!file) { + strerror_r(errno, errbuff, sizeof (errbuff)); + Sys_Printf ("%s\n", errbuff); + *len = 0; + return 0; + } + *len = Qfilesize (file); + return file; +} + +static void * +load_file (progs_t *pr, const char *name, off_t *_size) +{ + QFile *file; + int size; + char *sym; + + file = open_file (name, &size); + if (!file) { + file = open_file (va (0, "%s.gz", name), &size); + if (!file) { + return 0; + } + } + sym = malloc (size + 1); + sym[size] = 0; + Qread (file, sym, size); + Qclose (file); + *_size = size; + return sym; +} + +static void * +allocate_progs_mem (progs_t *pr, int size) +{ +#ifdef _WIN32 + return _aligned_malloc (size, 64); +#else + return aligned_alloc (64, size); +#endif +} + +static void +free_progs_mem (progs_t *pr, void *mem) +{ +#ifdef _WIN32 + _aligned_free (mem); +#else + free (mem); +#endif +} + +static void +init_qf (void) +{ + qwaq_cbuf = Cbuf_New (&id_interp); + + Sys_Init (); + COM_ParseConfig (qwaq_cbuf); + + //Cvar_Set (developer, "1"); + + Memory_Init (Sys_Alloc (8 * 1024 * 1024), 8 * 1024 * 1024); + PR_Init_Cvars (); +} + +static void +bi_printf (progs_t *pr, void *_res) +{ + dstring_t *dstr = dstring_new (); + + RUA_Sprintf (pr, dstr, "printf", 0); + if (dstr->str) { + Sys_Printf ("%s", dstr->str); + } + dstring_delete (dstr); +} + +static void +bi_traceon (progs_t *pr, void *_res) +{ + pr->pr_trace = true; + pr->pr_trace_depth = pr->pr_depth; +} + +static void +bi_traceoff (progs_t *pr, void *_res) +{ + pr->pr_trace = false; +} + +#define bi(x,n,np,params...) {#x, bi_##x, n, np, {params}} +#define p(type) PR_PARAM(type) +static builtin_t common_builtins[] = { + bi(printf, -1, -2, p(string)), + bi(traceon, -1, 0), + bi(traceoff, -1, 0), + {}, +}; + +static void +common_builtins_init (progs_t *pr) +{ + PR_RegisterBuiltins (pr, common_builtins, 0); +} + +static void +qwaq_thread_clear (progs_t *pr, void *_thread) +{ +} + +static void +qwaq_thread_destroy (progs_t *pr, void *_res) +{ + // resource block is the thread data: don't own it +} + +static progs_t * +create_progs (qwaq_thread_t *thread) +{ + progs_t *pr = calloc (1, sizeof (*pr)); + progsinit_f *funcs = thread->progsinit; + + pr->load_file = load_file; + pr->allocate_progs_mem = allocate_progs_mem; + pr->free_progs_mem = free_progs_mem; + pr->no_exec_limit = 1; + pr->hashctx = &thread->hashctx; + + pr_debug = 2; + pr_boundscheck = 0; + PR_Init (pr); + PR_Resources_Register (pr, "qwaq_thread", thread, qwaq_thread_clear, + qwaq_thread_destroy); + RUA_Init (pr, thread->rua_security); + common_builtins_init (pr); + while (*funcs) { + (*funcs++) (pr); + } + + return pr; +} + +static int +load_progs (progs_t *pr, const char *name) +{ + QFile *file; + int size; + + file = open_file (name, &size); + if (!file) { + return 0; + } + pr->progs_name = name; + pr->max_edicts = 1; + pr->zone_size = 2*1024*1024; + pr->stack_size = 64*1024; + PR_LoadProgsFile (pr, file, size); + Qclose (file); + if (!PR_RunLoadFuncs (pr)) + PR_Error (pr, "unable to load %s", pr->progs_name); + return 1; +} + +static void +spawn_progs (qwaq_thread_t *thread) +{ + dfunction_t *dfunc; + const char *name = 0; + pr_string_t *pr_argv; + int pr_argc = 1, i; + progs_t *pr; + + thread->main_func = 0; + pr = thread->pr = create_progs (thread); + if (thread->args.size) { + name = thread->args.a[0]; + } + + if (!load_progs (pr, name)) { + Sys_Error ("couldn't load %s", name); + } + + if ((dfunc = PR_FindFunction (pr, ".main")) + || (dfunc = PR_FindFunction (pr, "main"))) { + thread->main_func = dfunc - pr->pr_functions; + } else { + PR_Undefined (pr, "function", "main"); + } + + if (thread->pr->debug_handler) { + thread->pr->debug_handler (prd_begin, &thread->main_func, + thread->pr->debug_data); + } + + if (!PR_RunPostLoadFuncs (pr)) { + PR_Error (pr, "unable to load %s", pr->progs_name); + } + + PR_PushFrame (pr); + if (thread->args.size) { + pr_argc = thread->args.size; + } + pr_argv = PR_Zone_Malloc (pr, (pr_argc + 1) * 4); + pr_argv[0] = PR_SetTempString (pr, name); + for (i = 1; i < pr_argc; i++) { + pr_argv[i] = PR_SetTempString (pr, thread->args.a[i]); + } + pr_argv[i] = 0; + + PR_RESET_PARAMS (pr); + P_INT (pr, 0) = pr_argc; + P_POINTER (pr, 1) = PR_SetPointer (pr, pr_argv); + pr->pr_argc = 2; +} + +static void * +run_progs (void *data) +{ + __auto_type thread = (qwaq_thread_t *) data; + + spawn_progs (thread); + //Sys_Printf ("starting thread for %s\n", thread->args.a[0]); + + PR_ExecuteProgram (thread->pr, thread->main_func); + PR_PopFrame (thread->pr); + thread->return_code = R_INT (thread->pr); + if (thread->pr->debug_handler) { + thread->pr->debug_handler (prd_terminate, &thread->return_code, + thread->pr->debug_data); + } + PR_Shutdown (thread->pr); + free (thread->pr); + thread->pr = 0; + Hash_DelContext (thread->hashctx); + thread->hashctx = 0; + Sys_Shutdown (); + return thread; +} + +static void +start_progs_thread (qwaq_thread_t *thread) +{ + pthread_create (&thread->thread_id, 0, run_progs, thread); +} + +qwaq_thread_t * +create_thread (void *(*thread_func) (qwaq_thread_t *), void *data) +{ + qwaq_thread_t *thread = calloc (1, sizeof (*thread)); + + thread->data = data; + DARRAY_APPEND (&thread_data, thread); + pthread_create (&thread->thread_id, 0, + (void*(*)(void*))thread_func, thread); + return thread; +} + +static void +usage (int status) +{ + printf ("%s - QuakeForge runtime\n", this_program); + printf ("sorry, no help yet\n"); + exit (status); +} + +static int +parse_argset (int argc, char **argv) +{ + qwaq_thread_t *thread = calloc (1, sizeof (*thread)); + DARRAY_INIT (&thread->args, 8); + + DARRAY_APPEND (&thread->args, 0); + while (optind < argc && strcmp (argv[optind], "--")) { + DARRAY_APPEND (&thread->args, argv[optind++]); + } + if (optind < argc) { + optind++; + } + DARRAY_APPEND (&thread_data, thread); + return thread_data.size - 1; +} + +static int +parse_args (int argc, char **argv) +{ + int c; + qwaq_thread_t *main_thread = calloc (1, sizeof (*main_thread)); + int qargs_ind = -1; + + DARRAY_INIT (&main_thread->args, 8); + + while ((c = getopt_long (argc, argv, + short_options, long_options, 0)) != -1) { + switch (c) { + case 1: + DARRAY_APPEND (&main_thread->args, argv[optind - 1]); + break; + case OPT_QARGS: + if (qargs_ind < 0) { + qargs_ind = parse_argset (argc, argv); + thread_data.a[qargs_ind]->args.a[0] = "--qargs"; + goto done; + } else { + printf ("more than one set of qargs given"); + exit (1); + } + break; + default: + usage (1); + } + } +done: + + free (thread_data.a[0]->args.a); + free (thread_data.a[0]); + thread_data.a[0] = main_thread; + + while (optind < argc) { + parse_argset (argc, argv); + } + return qargs_ind; +} + + +int +main (int argc, char **argv) +{ + int qargs_ind = -1; + int main_ind = -1; + int ret = 0; + size_t num_sys = 1; + + this_program = argv[0]; + + DARRAY_INIT (&thread_data, 4); + for (optind = 1; optind < argc; ) { + parse_argset (argc, argv); + } + if (thread_data.size) { + qwaq_thread_t *thread = thread_data.a[0]; + // the first arg is initialized to null, but this is for getopt, so + // set to main program name + thread->args.a[0] = this_program; + optind = 0; + qargs_ind = parse_args (thread->args.size, (char **) thread->args.a); + } else { + // create a blank main thread set + qwaq_thread_t *thread = calloc (1, sizeof (*thread)); + DARRAY_INIT (&thread->args, 4); + DARRAY_APPEND (&thread_data, thread); + } + + if (qargs_ind >= 0) { + qwaq_thread_t *qargs = thread_data.a[qargs_ind]; + // the first arg is initialized to --qargs, so + // set to main program name for now + qargs->args.a[0] = this_program; + COM_InitArgv (qargs->args.size, qargs->args.a); + num_sys++; + } else { + const char *args[] = { this_program, 0 }; + COM_InitArgv (1, args); + } + + init_qf (); + + if (thread_data.a[0]->args.size < 1) { + DARRAY_APPEND (&thread_data.a[0]->args, "qwaq-app.dat"); + } + + while (thread_data.size < thread_data.a[0]->args.size + num_sys) { + qwaq_thread_t *thread = calloc (1, sizeof (*thread)); + DARRAY_INIT (&thread->args, 4); + DARRAY_APPEND (&thread->args, 0); + DARRAY_APPEND (&thread_data, thread); + } + + main_ind = qwaq_init_threads (&thread_data); + if (main_ind >= 0) { + // threads might start new threads before the end is reached + size_t count = thread_data.size; + for (size_t i = 0; i < count; i++) { + if (thread_data.a[i]->progsinit) { + start_progs_thread (thread_data.a[i]); + } + } + pthread_join (thread_data.a[main_ind]->thread_id, 0); + ret = thread_data.a[main_ind]->return_code; + } + + for (size_t i = 0; i < thread_data.size; i++) { + qwaq_thread_t *thread = thread_data.a[i]; + DARRAY_CLEAR (&thread->args); + free (thread_data.a[i]); + } + DARRAY_CLEAR (&thread_data); + Cbuf_DeleteStackReverse (qwaq_cbuf); + Sys_Shutdown (); + return ret; +} diff --git a/ruamoko/qwaq/builtins/qwaq-cmd.c b/ruamoko/qwaq/builtins/qwaq-cmd.c new file mode 100644 index 000000000..c248d5ff7 --- /dev/null +++ b/ruamoko/qwaq/builtins/qwaq-cmd.c @@ -0,0 +1,91 @@ +/* + qwaq-cmd.c + + Qwaq command-line tool initialization + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/07/06 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "QF/cbuf.h" +#include "QF/cmd.h" +#include "QF/cvar.h" +#include "QF/gib.h" +#include "QF/idparse.h" +#include "QF/keys.h" +#include "QF/progs.h" +#include "QF/qargs.h" +#include "QF/quakefs.h" +#include "QF/ruamoko.h" +#include "QF/sys.h" +#include "QF/va.h" +#include "QF/zone.h" + +#include "compat.h" + +#include "ruamoko/qwaq/qwaq.h" +#include "ruamoko/qwaq/debugger/debug.h" + +static progsinit_f main_app[] = { + 0 +}; + +int +qwaq_init_threads (qwaq_thread_set_t *thread_data) +{ + int main_ind = -1; + + for (size_t i = 1, thread_ind = 0; i < thread_data->size; i++) { + qwaq_thread_t *thread = thread_data->a[i]; + progsinit_f *app_funcs = main_app; + + if (thread->args.size && thread->args.a[0] + && strcmp (thread->args.a[0], "--qargs")) { + // skip the args set that's passed to qargs + continue; + } + if (thread_ind < thread_data->a[0]->args.size) { + thread->args.a[0] = thread_data->a[0]->args.a[thread_ind++]; + } else { + printf ("ignoring extra arg sets\n"); + break; + } + if (main_ind < 0) { + main_ind = i; + app_funcs = main_app; + } + thread->progsinit = app_funcs; + } + return main_ind; +} diff --git a/ruamoko/qwaq/builtins/qwaq-curses.c b/ruamoko/qwaq/builtins/qwaq-curses.c new file mode 100644 index 000000000..0e4f2aaaa --- /dev/null +++ b/ruamoko/qwaq/builtins/qwaq-curses.c @@ -0,0 +1,116 @@ +/* + qwaq-curses.c + + Qwaq curses initialization + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/07/06 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "QF/cbuf.h" +#include "QF/cmd.h" +#include "QF/cvar.h" +#include "QF/gib.h" +#include "QF/idparse.h" +#include "QF/input.h" +#include "QF/keys.h" +#include "QF/progs.h" +#include "QF/qargs.h" +#include "QF/quakefs.h" +#include "QF/ruamoko.h" +#include "QF/sys.h" +#include "QF/va.h" +#include "QF/zone.h" + +#include "compat.h" + +#include "ruamoko/qwaq/qwaq.h" +#include "ruamoko/qwaq/debugger/debug.h" + +static progsinit_f main_app[] = { + BI_Curses_Init, + BI_TermInput_Init, + QWAQ_EditBuffer_Init, + QWAQ_Debug_Init, + 0 +}; + +static progsinit_f target_app[] = { + QWAQ_DebugTarget_Init, + 0 +}; + +static FILE *logfile; + +static __attribute__((format(PRINTF, 1, 0))) void +qwaq_print (const char *fmt, va_list args) +{ + vfprintf (logfile, fmt, args); + fflush (logfile); +} + +int +qwaq_init_threads (qwaq_thread_set_t *thread_data) +{ + int main_ind = -1; + + logfile = fopen ("qwaq-curses.log", "wt"); + Sys_SetStdPrintf (qwaq_print); + + IN_Init_Cvars (); + IN_Init (); + + for (size_t i = 1, thread_ind = 0; i < thread_data->size; i++) { + qwaq_thread_t *thread = thread_data->a[i]; + progsinit_f *app_funcs = target_app; + + if (thread->args.size && thread->args.a[0] + && strcmp (thread->args.a[0], "--qargs")) { + // skip the args set that's passed to qargs + continue; + } + if (thread_ind < thread_data->a[0]->args.size) { + thread->args.a[0] = thread_data->a[0]->args.a[thread_ind++]; + } else { + printf ("ignoring extra arg sets\n"); + break; + } + if (main_ind < 0) { + main_ind = i; + app_funcs = main_app; + } + thread->progsinit = app_funcs; + } + return main_ind; +} diff --git a/ruamoko/qwaq/builtins/qwaq-graphics.c b/ruamoko/qwaq/builtins/qwaq-graphics.c new file mode 100644 index 000000000..d66ecc3cd --- /dev/null +++ b/ruamoko/qwaq/builtins/qwaq-graphics.c @@ -0,0 +1,81 @@ +/* + qwaq-graphics.c + + Qwaq graphics initialization + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/12/20 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "QF/zone.h" + +#include "ruamoko/qwaq/qwaq.h" + +static progsinit_f main_app[] = { + BI_Graphics_Init, + 0 +}; +#if 0// FIXME no multi-thread support yet +static progsinit_f secondary_app[] = { + 0 +}; +#endif + +int +qwaq_init_threads (qwaq_thread_set_t *thread_data) +{ + int main_ind = -1; + size_t memsize = 8 * 1024 * 1024; + memhunk_t *hunk = Memory_Init (Sys_Alloc (memsize), memsize); + + for (size_t i = 1, thread_ind = 0; i < thread_data->size; i++) { + qwaq_thread_t *thread = thread_data->a[i]; + progsinit_f *app_funcs = 0;//secondary_app; + + if (thread->args.size && thread->args.a[0] + && strcmp (thread->args.a[0], "--qargs")) { + // skip the args set that's passed to qargs + continue; + } + if (thread_ind < thread_data->a[0]->args.size) { + thread->args.a[0] = thread_data->a[0]->args.a[thread_ind++]; + } else { + printf ("ignoring extra arg sets\n"); + break; + } + if (main_ind < 0) { + main_ind = i; + app_funcs = main_app; + } + thread->progsinit = app_funcs; + thread->rua_security = 1; + thread->hunk = hunk; //FIXME shared (but currently only one thread) + } + return main_ind; +} diff --git a/ruamoko/qwaq/builtins/term-input.c b/ruamoko/qwaq/builtins/term-input.c new file mode 100644 index 000000000..72de4fb74 --- /dev/null +++ b/ruamoko/qwaq/builtins/term-input.c @@ -0,0 +1,927 @@ +/* + term-input.c + + Input handling + + Copyright (C) 2020 Bill Currie + + Author: Bill Currie + Date: 2020/03/21 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SIGACTION // no sigaction, no window resize +#include +#include +#endif + +#include "QF/dstring.h" +#include "QF/hash.h" +#include "QF/input.h" +#include "QF/keys.h" +#include "QF/sys.h" + +#include "QF/input/event.h" + +#include "qfselect.h" + +#include "ruamoko/qwaq/qwaq.h" +#include "ruamoko/qwaq/qwaq-input.h" +#include "ruamoko/qwaq/ui/event.h" + +#define always_inline inline __attribute__((__always_inline__)) + +#define MOUSE_MOVES_ON "\033[?1003h" +#define MOUSE_MOVES_OFF "\033[?1003l" +#define SGR_ON "\033[?1006h" +#define SGR_OFF "\033[?1006l" +#define WHEEL_BUTTONS 0x78 // scroll up/down/left/right - always click + +typedef enum qwaq_input_commands_e { + qwaq_cmd_send_connected_devices, + qwaq_cmd_get_device_info, +} qwaq_input_commands; + +static const char *qwaq_input_command_names[]= { + "send_connected_devices", + "get_device_info", +}; + +typedef struct qwaq_key_s { + const char *sequence; + knum_t key; + unsigned shift; +} qwaq_key_t; + +static qwaq_key_t default_keys[] = { + { "\033OP", QFK_F1 }, + { "\033OQ", QFK_F2 }, + { "\033OR", QFK_F3 }, + { "\033OS", QFK_F4 }, + { "\033[15~", QFK_F5 }, + { "\033[17~", QFK_F6 }, + { "\033[18~", QFK_F7 }, + { "\033[19~", QFK_F8 }, + { "\033[20~", QFK_F9 }, + { "\033[21~", QFK_F10 }, + { "\033[23~", QFK_F11 }, + { "\033[24~", QFK_F12 }, + // shift F1-F12 + { "\033[1;2P", QFK_F13 }, + { "\033[1;2Q", QFK_F14 }, + { "\033[1;2R", QFK_F15 }, + { "\033[1;2S", QFK_F16 }, + { "\033[15;2~", QFK_F17 }, + { "\033[17;2~", QFK_F18 }, + { "\033[18;2~", QFK_F19 }, + { "\033[19;2~", QFK_F20 }, + { "\033[20;2~", QFK_F21 }, + { "\033[21;2~", QFK_F22 }, + { "\033[23;2~", QFK_F23 }, + { "\033[24;2~", QFK_F24 }, + // control F1-F12 + { "\033[1;5P", QFK_F25 }, + { "\033[1;5Q", QFK_F26 }, + { "\033[1;5R", QFK_F27 }, + { "\033[1;5S", QFK_F28 }, + { "\033[15;5~", QFK_F29 }, + { "\033[17;5~", QFK_F30 }, + { "\033[18;5~", QFK_F31 }, + { "\033[19;5~", QFK_F32 }, + { "\033[20;5~", QFK_F33 }, + { "\033[21;5~", QFK_F34 }, + { "\033[23;5~", QFK_F35 }, + { "\033[24;5~", QFK_F36 }, + // shift control F1-F12 + { "\033[1;6P", QFK_F37 }, + { "\033[1;6Q", QFK_F38 }, + { "\033[1;6R", QFK_F39 }, + { "\033[1;6S", QFK_F40 }, + { "\033[15;6~", QFK_F41 }, + { "\033[17;6~", QFK_F42 }, + { "\033[18;6~", QFK_F43 }, + { "\033[19;6~", QFK_F44 }, + { "\033[20;6~", QFK_F45 }, + { "\033[21;6~", QFK_F46 }, + { "\033[23;6~", QFK_F47 }, + { "\033[24;6~", QFK_F48 }, + + { "\033[2~", QFK_INSERT, 0 }, + { "\033[3~", QFK_DELETE, 0 }, + { "\033[H", QFK_HOME, 0 }, + { "\033[F", QFK_END, 0 }, + { "\033[5~", QFK_PAGEUP, 0 }, + { "\033[6~", QFK_PAGEDOWN, 0 }, + { "\033[A", QFK_UP, 0 }, + { "\033[B", QFK_DOWN, 0 }, + { "\033[C", QFK_RIGHT, 0 }, + { "\033[D", QFK_LEFT, 0 }, + // shift + // there may be a setting to tell xterm to NOT act on shift ins/pgup/pgdn + { "\033[2;2~", QFK_INSERT, 1 }, // xterm gobbles (and pastes) + { "\033[3;2~", QFK_DELETE, 1 }, + { "\033[1;2H", QFK_HOME, 1 }, + { "\033[1;2F", QFK_END, 1 }, + { "\033[5;2~", QFK_PAGEUP, 1 }, // xterm gobbles (scrolls term) + { "\033[6;2~", QFK_PAGEDOWN, 1 }, // xterm gobbles (scrolls term) + { "\033[1;2A", QFK_UP, 1 }, + { "\033[1;2B", QFK_DOWN, 1 }, + { "\033[1;2C", QFK_RIGHT, 1 }, + { "\033[1;2D", QFK_LEFT, 1 }, + { "\033[Z", QFK_TAB, 1 }, + // control + { "\033[2;5~", QFK_INSERT, 4 }, + { "\033[3;5~", QFK_DELETE, 4 }, + { "\033[1;5H", QFK_HOME, 4 }, + { "\033[1;5F", QFK_END, 4 }, + { "\033[5;5~", QFK_PAGEUP, 4 }, + { "\033[6;5~", QFK_PAGEDOWN, 4 }, + { "\033[1;5A", QFK_UP, 4 }, + { "\033[1;5B", QFK_DOWN, 4 }, + { "\033[1;5C", QFK_RIGHT, 4 }, + { "\033[1;5D", QFK_LEFT, 4 }, + // shift control + { "\033[2;6~", QFK_INSERT, 5 }, + { "\033[3;6~", QFK_DELETE, 5 }, + { "\033[1;6H", QFK_HOME, 5 }, + { "\033[1;6F", QFK_END, 5 }, + { "\033[5;6~", QFK_PAGEUP, 5 }, + { "\033[6;6~", QFK_PAGEDOWN, 5 }, + { "\033[1;6A", QFK_UP, 5 }, + { "\033[1;6B", QFK_DOWN, 5 }, + { "\033[1;6C", QFK_RIGHT, 5 }, + { "\033[1;6D", QFK_LEFT, 5 }, +}; + +#define CMD_SIZE(x) sizeof(x)/sizeof(x[0]) + +static int +qwaq_cmd_peek (qwaq_input_resources_t *res, int ahead) +{ + return *RB_PEEK_DATA (res->commands.pipe, ahead); +} +#if 0 +static dstring_t * +qwaq_cmd_string (qwaq_input_resources_t *res, int string_id) +{ + return res->commands.strings + string_id; +} +#endif +static dstring_t * +qwaq_res_string (qwaq_input_resources_t *res, int string_id) +{ + return res->results.strings + string_id; +} + +#ifdef HAVE_SIGACTION +static struct sigaction save_winch; +static sigset_t winch_mask; +static volatile sig_atomic_t winch_arrived; + +static void +handle_winch (int sig) +{ + winch_arrived = 1; +} +#endif + +int +qwaq_add_event (qwaq_input_resources_t *res, qwaq_event_t *event) +{ + struct timespec timeout; + int merged = 0; + int ret = 0; + + // merge motion events + pthread_mutex_lock (&res->events.cond.mut); + unsigned last = RB_DATA_AVAILABLE (res->events.queue); + if (event->what == qe_mousemove && last > 1 + && RB_PEEK_DATA(res->events.queue, last - 1)->what == qe_mousemove) { + RB_POKE_DATA(res->events.queue, last - 1, *event); + merged = 1; + pthread_cond_broadcast (&res->events.cond.rcond); + } + pthread_mutex_unlock (&res->events.cond.mut); + if (merged) { + return 0; + } + + pthread_mutex_lock (&res->events.cond.mut); + qwaq_init_timeout (&timeout, 5000 * (int64_t) 1000000); + while (RB_SPACE_AVAILABLE (res->events.queue) < 1 && ret == 0) { + ret = pthread_cond_timedwait (&res->events.cond.wcond, + &res->events.cond.mut, &timeout); + } + RB_WRITE_DATA (res->events.queue, event, 1); + pthread_cond_broadcast (&res->events.cond.rcond); + pthread_mutex_unlock (&res->events.cond.mut); + return ret; +} + +#ifdef HAVE_SIGACTION +static void +resize_event (qwaq_input_resources_t *res) +{ + qwaq_event_t event = {}; + struct winsize size; + if (ioctl (fileno (stdout), TIOCGWINSZ, &size) != 0) { + return; + } + event.what = qe_resize; + event.resize.width = size.ws_col; + event.resize.height = size.ws_row; + qwaq_add_event (res, &event); +} +#endif + +static void +key_event (qwaq_input_resources_t *res, int key, unsigned shift) +{ + qwaq_event_t event = {}; + event.what = qe_keydown; + event.when = Sys_DoubleTime (); + event.key.code = key; + event.key.shift = shift; + qwaq_add_event (res, &event); +} + +static void +mouse_event (qwaq_input_resources_t *res, int what, int x, int y) +{ + qwaq_event_t event = {}; + + event.what = what; + event.when = Sys_DoubleTime (); + event.mouse.x = x; + event.mouse.y = y; + event.mouse.buttons = res->button_state; + + if (event.what == qe_mousedown) { + if (event.when - res->lastClick.when <= 0.4 + && res->lastClick.mouse.buttons == event.mouse.buttons + && res->lastClick.mouse.click < 3) { + event.mouse.click = res->lastClick.mouse.click + 1; + event.what = qe_mouseclick; + } + res->lastClick = event; + } else if (event.what == qe_mouseclick) { + // scroll button event, so always single click + event.mouse.click = 1; + } + qwaq_add_event (res, &event); +} + +static void +parse_mouse (qwaq_input_resources_t *res, unsigned ctrl, int x, int y, int cmd) +{ + int button = ctrl & 3; + int ext = (ctrl >> 6); + int what = qe_none; + + if (ctrl & 0x20) { + // motion-only event + button = -1; + what = qe_mousemove; + } else { + // xterm doesn't send release events for buttons 4-7 + res->button_state &= ~(0x78); + if (!ext && button == 3 && !cmd) { + res->button_state = 0; // unknown which button was released + button = -1; + what = qe_mouseup; + } else { + if (ext) { + button += 4 * ext - 1; + } + if (cmd == 'm') { + res->button_state &= ~(1 << button); + what = qe_mouseup; + } else { + res->button_state |= 1 << button; + if (res->button_state & WHEEL_BUTTONS) { + what = qe_mouseclick; + } else { + what = qe_mousedown; + } + } + } + } + res->mouse_x = x; + res->mouse_y = y; + mouse_event (res, what, x, y); +} + +static void +parse_x10 (qwaq_input_resources_t *res) +{ + int x = (byte) res->escbuff.str[1] - '!'; // want 0-based + int y = (byte) res->escbuff.str[2] - '!'; // want 0-based + unsigned ctrl = (byte) res->escbuff.str[0] - ' '; + + parse_mouse (res, ctrl, x, y, 0); +} + +static void +parse_sgr (qwaq_input_resources_t *res, char cmd) +{ + unsigned ctrl, x, y; + + sscanf (res->escbuff.str, "%u;%u;%u", &ctrl, &x, &y); + // mouse coords are 1-based, but want 0-based + parse_mouse (res, ctrl, x - 1, y - 1, cmd); +} + +static void +parse_key (qwaq_input_resources_t *res) +{ + qwaq_key_t *key = Hash_Find (res->key_sequences, res->escbuff.str); + + //Sys_Printf ("parse_key: %s\n", res->escbuff.str + 1); + if (key) { + //Sys_Printf (" %d %03x %s\n", key->key, key->shift, + // Key_KeynumToString (key->key)); + key_event (res, key->key, key->shift); + } +} + +static void process_char (qwaq_input_resources_t *res, char ch) +{ + if (ch == 0x1b) { + // always reset if escape is seen, may be a desync + res->escstate = esc_escape; + } else { + switch (res->escstate) { + case esc_ground: + key_event (res, (byte) ch, 0); // shift state unknown + break; + case esc_escape: + if (ch == '[') { + res->escstate = esc_csi; + } else if (ch == 'O') { + // will wind up accepting P;P... but meh + res->escstate = esc_key; + dstring_clear (&res->escbuff); + // start the buffer with what got us here: eases key lookup + dstring_append (&res->escbuff, "\033O", 2); + } else { + res->escstate = esc_ground; + } + break; + case esc_csi: + if (ch == 'M') { + res->escstate = esc_mouse; + dstring_clear (&res->escbuff); + } else if (ch == '<') { + res->escstate = esc_sgr; + dstring_clear (&res->escbuff); + } else if (ch >= '0' && ch < 127) { + res->escstate = esc_key; + dstring_clear (&res->escbuff); + // start the buffer with what got us here: eases key lookup + dstring_append (&res->escbuff, "\033[", 2); + // the csi code might be short (eg, \e[H for home) so + // need to check for end of string right away + goto esc_key_jump; + } else { + res->escstate = esc_ground; + } + break; + case esc_mouse: + dstring_append (&res->escbuff, &ch, 1); + if (res->escbuff.size == 3) { + parse_x10 (res); + res->escstate = esc_ground; + } + break; + case esc_sgr: + if (isdigit (ch) || ch == ';') { + dstring_append (&res->escbuff, &ch, 1); + } else { + if (ch == 'm' || ch == 'M') { + // terminate the string + dstring_append (&res->escbuff, "", 1); + parse_sgr (res, ch); + } + res->escstate = esc_ground; + } + break; + case esc_key: +esc_key_jump: + dstring_append (&res->escbuff, &ch, 1); + if (!isdigit (ch) && ch != ';') { + // terminate the string + dstring_append (&res->escbuff, "", 1); + // parse_key will sort out whether it was a valid sequence + parse_key (res); + res->escstate = esc_ground; + } + break; + } + //printf("res->escstate %d\n", res->escstate); + } +} + +static const char * +key_sequence_getkey (const void *_seq, void *unused) +{ + __auto_type seq = (const qwaq_key_t *) _seq; + return seq->sequence; +} + +static void +term_init (void *_res) +{ +} + +static void +term_shutdown (void *_res) +{ +} + +static void +term_set_device_event_data (void *device, void *event_data, void *data) +{ +} + +static void * +term_get_device_event_data (void *device, void *data) +{ + return 0; +} + +#define FD 0 +static void +term_add_select (qf_fd_set *fdset, int *maxfd, void *_res) +{ + QF_FD_SET (FD, fdset); + if (*maxfd < FD) { + *maxfd = FD; + } +} + +static void +term_check_select (qf_fd_set *fdset, void *_res) +{ + qwaq_input_resources_t *res = _res; + char buf[256]; + int len; +#ifdef HAVE_SIGACTION + sigset_t save_set; + int saw_winch; + + pthread_sigmask (SIG_BLOCK, &winch_mask, &save_set); + saw_winch = winch_arrived; + winch_arrived = 0; + pthread_sigmask (SIG_SETMASK, &save_set, 0); + if (saw_winch) { + resize_event (res); + } +#endif + if (QF_FD_ISSET (FD, fdset)) { + len = read(0, buf, sizeof (buf)); + for (int i = 0; i < len; i++) { + process_char (res, buf[i]); + } + } +} + +static in_driver_t term_driver = { + .init = term_init, + .shutdown = term_shutdown, + .set_device_event_data = term_set_device_event_data, + .get_device_event_data = term_get_device_event_data, + .add_select = term_add_select, + .check_select = term_check_select, +}; +static int term_driver_handle; + +static void __attribute__((constructor)) +term_register_driver (void) +{ + term_driver_handle = IN_RegisterDriver (&term_driver, 0); +} + +static int +qwaq_input_event_handler (const IE_event_t *ie_event, void *_res) +{ + qwaq_input_resources_t *res = _res; + qwaq_event_t event = {}; + + event.when = ie_event->when * 1e-6 + Sys_DoubleTimeBase (); + + switch (ie_event->type) { + case ie_event_count: + case ie_none: + case ie_gain_focus: + case ie_lose_focus: + case ie_app_gain_focus: + case ie_app_lose_focus: + case ie_app_window: + return 0; + case ie_add_device: + event.what = qe_dev_add; + event.message.int_val = ie_event->device.devid; + break; + case ie_remove_device: + event.what = qe_dev_rem; + event.message.int_val = ie_event->device.devid; + break; + case ie_mouse: + case ie_key: + // these are handled directly for now + break; + case ie_axis: + event.what = qe_axis; + event.message.ivector_val[0] = ie_event->axis.devid; + event.message.ivector_val[1] = ie_event->axis.axis; + event.message.ivector_val[2] = ie_event->axis.value; + break; + case ie_button: + event.what = qe_button; + event.message.ivector_val[0] = ie_event->button.devid; + event.message.ivector_val[1] = ie_event->button.button; + event.message.ivector_val[2] = ie_event->button.state; + break; + } + qwaq_add_event (res, &event); + return 1; +} + +static void +qwaq_input_init (qwaq_input_resources_t *res) +{ + res->input_event_handler = IE_Add_Handler (qwaq_input_event_handler, res); + IE_Set_Focus (res->input_event_handler); + IN_DriverData (term_driver_handle, res); + + if (res->key_sequences) { + Hash_FlushTable (res->key_sequences); + } else { + res->key_sequences = Hash_NewTable (127, key_sequence_getkey, 0, 0, + res->pr->hashctx); + } + for (size_t i = 0; i < sizeof (default_keys) / sizeof (default_keys[0]); + i++) { + Hash_Add (res->key_sequences, &default_keys[i]); + } + +#ifdef HAVE_SIGACTION + sigemptyset (&winch_mask); + sigaddset (&winch_mask, SIGWINCH); + struct sigaction action = {}; + action.sa_handler = handle_winch; + sigaction (SIGWINCH, &action, &save_winch); +#endif +} + +void +qwaq_input_enable_mouse (void) +{ + // ncurses takes care of input mode for us, so need only tell xterm + // what we need + (void) !write(1, MOUSE_MOVES_ON, sizeof (MOUSE_MOVES_ON) - 1); + (void) !write(1, SGR_ON, sizeof (SGR_ON) - 1); +} + +void +qwaq_input_disable_mouse (void) +{ + // ncurses takes care of input mode for us, so need only tell xterm + // what we need + (void) !write(1, SGR_OFF, sizeof (SGR_OFF) - 1); + (void) !write(1, MOUSE_MOVES_OFF, sizeof (MOUSE_MOVES_OFF) - 1); +} + +static void +qwaq_process_input (qwaq_input_resources_t *res) +{ + IN_ProcessEvents (); +} + +static void +qwaq_input_shutdown (qwaq_input_resources_t *res) +{ + IE_Remove_Handler (res->input_event_handler); + IN_DriverData (term_driver_handle, 0); + +#ifdef HAVE_SIGACTION + sigaction (SIGWINCH, &save_winch, 0); +#endif +} + +static void +bi_send_connected_devices (progs_t *pr, void *_res) +{ + qwaq_input_resources_t *res = _res; + + int command[] = { + qwaq_cmd_send_connected_devices, 0, + }; + + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); +} + +static void +bi_get_device_info (progs_t *pr, void *_res) +{ + qwaq_input_resources_t *res = _res; + int devid = P_INT (pr, 0); + + int command[] = { + qwaq_cmd_get_device_info, 0, + devid, + }; + + command[1] = CMD_SIZE(command); + qwaq_pipe_submit (&res->commands, command, command[1]); + + int cmd_result[5]; + qwaq_pipe_receive (&res->results, cmd_result, qwaq_cmd_get_device_info, + CMD_SIZE (cmd_result)); + int name_id = cmd_result[1]; + int id_id = cmd_result[2]; + int axis_id = cmd_result[3]; + int button_id = cmd_result[4]; + + dstring_t *name_string = qwaq_res_string (res, name_id); + dstring_t *id_string = qwaq_res_string (res, id_id); + dstring_t *axis_buffer = qwaq_res_string (res, axis_id); + dstring_t *button_buffer = qwaq_res_string (res, button_id); + + qwaq_devinfo_t *devinfo = PR_Zone_Malloc (pr, sizeof (qwaq_devinfo_t)); + in_axisinfo_t *axes = PR_Zone_Malloc (pr, axis_buffer->size); + in_buttoninfo_t *buttons = PR_Zone_Malloc (pr, button_buffer->size); + + if (axes) { + memcpy (axes, axis_buffer->str, axis_buffer->size); + } + if (buttons) { + memcpy (buttons, button_buffer->str, button_buffer->size); + } + + devinfo->name = PR_SetDynamicString (pr, name_string->str); + devinfo->id = PR_SetDynamicString (pr, id_string->str); + devinfo->numaxes = axis_buffer->size / sizeof (in_axisinfo_t); + devinfo->axes = PR_SetPointer (pr, axes); + devinfo->numbuttons = button_buffer->size / sizeof (in_buttoninfo_t); + devinfo->buttons = PR_SetPointer (pr, buttons); + + qwaq_pipe_release_string (&res->results, name_id); + qwaq_pipe_release_string (&res->results, id_id); + qwaq_pipe_release_string (&res->results, axis_id); + qwaq_pipe_release_string (&res->results, button_id); + + RETURN_POINTER (pr, devinfo); +} + +static int +get_event (qwaq_input_resources_t *res, qwaq_event_t *event) +{ + struct timespec timeout; + int ret = 0; + int was_event = 0; + + pthread_mutex_lock (&res->events.cond.mut); + qwaq_init_timeout (&timeout, 20 * 1000000); + while (RB_DATA_AVAILABLE (res->events.queue) < 1 && ret == 0) { + ret = pthread_cond_timedwait (&res->events.cond.rcond, + &res->events.cond.mut, &timeout); + } + if (event) { + if (ret == 0) { + RB_READ_DATA (res->events.queue, event, 1); + was_event = 1; + } else if (res->button_state) { + event->what = qe_mouseauto; + event->when = Sys_DoubleTime (); + event->mouse.buttons = res->button_state; + event->mouse.x = res->mouse_x; + event->mouse.y = res->mouse_y; + } else { + event->what = qe_none; + } + } + pthread_cond_broadcast (&res->events.cond.wcond); + pthread_mutex_unlock (&res->events.cond.mut); + return was_event; +} + +static void +bi_get_event (progs_t *pr, void *_res) +{ + qwaq_input_resources_t *res = _res; + qwaq_event_t *event = &P_STRUCT (pr, qwaq_event_t, 0); + + R_INT (pr) = get_event (res, event); +} + +static void +cmd_send_connected_devices (qwaq_input_resources_t *res) +{ + IN_SendConnectedDevices (); +} + +static void +cmd_get_device_info (qwaq_input_resources_t *res) +{ + int devid = qwaq_cmd_peek (res, 2); + int name_id = qwaq_pipe_acquire_string (&res->results); + int id_id = qwaq_pipe_acquire_string (&res->results); + int axis_id = qwaq_pipe_acquire_string (&res->results); + int button_id = qwaq_pipe_acquire_string (&res->results); + + dstring_t *name_string = qwaq_res_string (res, name_id); + dstring_t *id_string = qwaq_res_string (res, id_id); + dstring_t *axis_buffer = qwaq_res_string (res, axis_id); + dstring_t *button_buffer = qwaq_res_string (res, button_id); + + dstring_copystr (name_string, IN_GetDeviceName (devid)); + dstring_copystr (id_string, IN_GetDeviceId (devid)); + + int num; + IN_AxisInfo (devid, 0, &num); + axis_buffer->size = num * sizeof (in_axisinfo_t); + dstring_adjust (axis_buffer); + IN_AxisInfo (devid, (in_axisinfo_t *) axis_buffer->str, &num); + + IN_ButtonInfo (devid, 0, &num); + button_buffer->size = num * sizeof (in_buttoninfo_t); + dstring_adjust (button_buffer); + IN_ButtonInfo (devid, (in_buttoninfo_t *) button_buffer->str, &num); + + int cmd_result[] = { + qwaq_cmd_get_device_info, + name_id, id_id, axis_id, button_id + }; + qwaq_pipe_submit (&res->results, cmd_result, CMD_SIZE (cmd_result)); +} + +static void +dump_command (qwaq_input_resources_t *res, int len) +{ + if (0) { + qwaq_input_commands cmd = qwaq_cmd_peek (res, 0); + Sys_Printf ("%s[%d]", qwaq_input_command_names[cmd], len); + for (int i = 2; i < len; i++) { + Sys_Printf (" %d", qwaq_cmd_peek (res, i)); + } + Sys_Printf ("\n"); + } +} + +static void +process_commands (qwaq_input_resources_t *res) +{ + struct timespec timeout; + int avail; + int len; + int ret = 0; + + pthread_mutex_lock (&res->commands.pipe_cond.mut); + qwaq_init_timeout (&timeout, 20 * 1000000); + while (RB_DATA_AVAILABLE (res->commands.pipe) < 2 && ret == 0) { + ret = pthread_cond_timedwait (&res->commands.pipe_cond.rcond, + &res->commands.pipe_cond.mut, &timeout); + } + // The mutex is unlocked at the top of the loop and locked again + // (for broadcast) at the bottom, then finally unlocked after the loop. + // It should be only the data availability check that's not threadsafe + // as the mutex is not released until after the data has been written to + // the buffer. + while ((avail = RB_DATA_AVAILABLE (res->commands.pipe)) >= 2 + && avail >= (len = qwaq_cmd_peek (res, 1))) { + pthread_mutex_unlock (&res->commands.pipe_cond.mut); + dump_command (res, len); + qwaq_input_commands cmd = qwaq_cmd_peek (res, 0); + switch (cmd) { + case qwaq_cmd_send_connected_devices: + cmd_send_connected_devices (res); + break; + case qwaq_cmd_get_device_info: + cmd_get_device_info (res); + break; + } + pthread_mutex_lock (&res->commands.pipe_cond.mut); + RB_RELEASE (res->commands.pipe, len); + pthread_cond_broadcast (&res->commands.pipe_cond.wcond); + // unlocked at top of top if there's more data, otherwise just after + // the loop + } + pthread_mutex_unlock (&res->commands.pipe_cond.mut); +} + +static void * +qwaq_input_thread (qwaq_thread_t *thread) +{ + qwaq_input_resources_t *res = thread->data; + + while (1) { + qwaq_process_input (res); + process_commands (res); + } + thread->return_code = 0; + return thread; +} + +static void +bi_init_input (progs_t *pr, void *_res) +{ + qwaq_input_resources_t *res = _res; + qwaq_input_init (res); + res->initialized = 1; + create_thread (qwaq_input_thread, res); + + IE_event_t event = { + .type = ie_app_gain_focus, + .when = Sys_LongTime (), + }; + IE_Send_Event (&event); +} + +#define bi(x,n,np,params...) {#x, bi_##x, n, np, {params}} +#define p(type) PR_PARAM(type) +static builtin_t builtins[] = { + bi(send_connected_devices, -1, 0), + bi(get_device_info, -1, 1, p(int)), + bi(get_event, -1, 1, p(ptr)), + bi(init_input, -1, 0), + + {0} +}; + +static void +bi_input_clear (progs_t *pr, void *_res) +{ + __auto_type res = (qwaq_input_resources_t *) _res; + + if (res->initialized) { + qwaq_input_shutdown (res); + } +} + +static void +bi_input_destroy (progs_t *pr, void *_res) +{ + free (_res); +} + +static void +bi_input_shutdown (void *_pr) +{ + __auto_type pr = (progs_t *) _pr; + qwaq_input_resources_t *res = PR_Resources_Find (pr, "input"); + + qwaq_input_shutdown (res); +} + +void +BI_TermInput_Init (progs_t *pr) +{ + qwaq_input_resources_t *res = calloc (sizeof (*res), 1); + res->pr = pr; + + qwaq_init_pipe (&res->commands); + qwaq_init_pipe (&res->results); + + res->escbuff.mem = &dstring_default_mem; + // ensure the backing buffer exists + dstring_clearstr (&res->escbuff); + + res->input_event_handler = -1; + + qwaq_init_cond (&res->events.cond); + + PR_Resources_Register (pr, "input", res, bi_input_clear, bi_input_destroy); + PR_RegisterBuiltins (pr, builtins, res); + Sys_RegisterShutdown (bi_input_shutdown, pr); +} diff --git a/ruamoko/qwaq/builtins/threading.c b/ruamoko/qwaq/builtins/threading.c new file mode 100644 index 000000000..020eed645 --- /dev/null +++ b/ruamoko/qwaq/builtins/threading.c @@ -0,0 +1,140 @@ +/* + threading.c + + Ruamoko threading support + + Copyright (C) 2020 Bill Currie + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2020/03/01 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include +#include +#include +#include +#include + +#include "QF/dstring.h" + +#include "ruamoko/qwaq/qwaq.h" +#include "ruamoko/qwaq/threading.h" + +#define always_inline inline __attribute__((__always_inline__)) + +int +qwaq_pipe_acquire_string (qwaq_pipe_t *pipe) +{ + int string_id = -1; + + pthread_mutex_lock (&pipe->string_id_cond.mut); + while (RB_DATA_AVAILABLE (pipe->string_ids) < 1) { + pthread_cond_wait (&pipe->string_id_cond.rcond, + &pipe->string_id_cond.mut); + } + RB_READ_DATA (pipe->string_ids, &string_id, 1); + pthread_cond_broadcast (&pipe->string_id_cond.wcond); + pthread_mutex_unlock (&pipe->string_id_cond.mut); + return string_id; +} + +void +qwaq_pipe_release_string (qwaq_pipe_t *pipe, int string_id) +{ + pthread_mutex_lock (&pipe->string_id_cond.mut); + while (RB_SPACE_AVAILABLE (pipe->string_ids) < 1) { + pthread_cond_wait (&pipe->string_id_cond.wcond, + &pipe->string_id_cond.mut); + } + RB_WRITE_DATA (pipe->string_ids, &string_id, 1); + pthread_cond_broadcast (&pipe->string_id_cond.rcond); + pthread_mutex_unlock (&pipe->string_id_cond.mut); +} + +void +qwaq_pipe_submit (qwaq_pipe_t *pipe, const int *cmd, unsigned len) +{ + pthread_mutex_lock (&pipe->pipe_cond.mut); + while (RB_SPACE_AVAILABLE (pipe->pipe) < len) { + pthread_cond_wait (&pipe->pipe_cond.wcond, + &pipe->pipe_cond.mut); + } + RB_WRITE_DATA (pipe->pipe, cmd, len); + pthread_cond_broadcast (&pipe->pipe_cond.rcond); + pthread_mutex_unlock (&pipe->pipe_cond.mut); +} + +void +qwaq_pipe_receive (qwaq_pipe_t *pipe, int *result, int cmd, unsigned len) +{ + //Sys_Printf ("qwaq_wait_result: %d %d\n", cmd, len); + pthread_mutex_lock (&pipe->pipe_cond.mut); + while (RB_DATA_AVAILABLE (pipe->pipe) < len + || *RB_PEEK_DATA (pipe->pipe, 0) != cmd) { + pthread_cond_wait (&pipe->pipe_cond.rcond, + &pipe->pipe_cond.mut); + } + RB_READ_DATA (pipe->pipe, result, len); + pthread_cond_broadcast (&pipe->pipe_cond.wcond); + pthread_mutex_unlock (&pipe->pipe_cond.mut); + //Sys_Printf ("qwaq_wait_result exit: %d %d\n", cmd, len); +} + +void +qwaq_init_timeout (struct timespec *timeout, int64_t time) +{ + #define SEC 1000000000L + struct timeval now; + gettimeofday(&now, 0); + timeout->tv_sec = now.tv_sec; + timeout->tv_nsec = now.tv_usec * 1000L + time; + if (timeout->tv_nsec >= SEC) { + timeout->tv_sec += timeout->tv_nsec / SEC; + timeout->tv_nsec %= SEC; + } +} + +void +qwaq_init_cond (rwcond_t *cond) +{ + pthread_cond_init (&cond->rcond, 0); + pthread_cond_init (&cond->wcond, 0); + pthread_mutex_init (&cond->mut, 0); +} + +void +qwaq_init_pipe (qwaq_pipe_t *pipe) +{ + qwaq_init_cond (&pipe->pipe_cond); + qwaq_init_cond (&pipe->string_id_cond); + + for (int i = 0; i < STRING_ID_QUEUE_SIZE - 1; i++) { + RB_WRITE_DATA (pipe->string_ids, &i, 1); + pipe->strings[i].mem = &dstring_default_mem; + } +} diff --git a/ruamoko/qwaq/debugger/debug.h b/ruamoko/qwaq/debugger/debug.h new file mode 100644 index 000000000..788c3aff7 --- /dev/null +++ b/ruamoko/qwaq/debugger/debug.h @@ -0,0 +1,113 @@ +#ifndef __qwaq_debugger_debug_h +#define __qwaq_debugger_debug_h + +#include "ruamoko/qwaq/ui/event.h" + +typedef enum { + qe_debug_event = 0x0100, +} qwaq_debug_messages; + +#ifdef __QFCC__ + +#include + +//FIXME finish unsigned in qfcc +#ifndef umax +#define umax 0x7fffffff +#endif + +typedef string pr_string_t; + +#endif + +typedef struct qdb_event_s { + prdebug_t what; + union { + pr_string_t message; + unsigned function; + int exit_code; + }; +} qdb_event_t; + +typedef struct qdb_state_s { + unsigned staddr; + unsigned func; + pr_string_t file; + unsigned line; +} qdb_state_t; + +typedef struct qdb_stack_s { + unsigned staddr; // return address + unsigned func; // calling function + // FIXME temp strings +} qdb_stack_t; + +typedef struct qdb_def_s { + unsigned type_size; // type in lower 16, size in upper 16 + unsigned offset; + unsigned name; // string + unsigned type_encoding; +} qdb_def_t; + +typedef struct qdb_function_s { + int staddr; + unsigned local_data; + unsigned local_size; + unsigned profile; + unsigned name; // string + unsigned file; // string + unsigned num_params; +} qdb_function_t; + +typedef struct qdb_auxfunction_s { + unsigned function; + unsigned source_line; + unsigned line_info; + unsigned local_defs; + unsigned num_locals; + unsigned return_type; +} qdb_auxfunction_t; + +#ifdef __QFCC__ + +typedef struct qdb_target_s { int handle; } qdb_target_t; + +void qdb_set_trace (qdb_target_t target, int state); +int qdb_set_breakpoint (qdb_target_t target, unsigned staddr); +int qdb_clear_breakpoint (qdb_target_t target, unsigned staddr); +int qdb_set_watchpoint (qdb_target_t target, unsigned offset); +int qdb_clear_watchpoint (qdb_target_t target); +int qdb_continue (qdb_target_t target); +qdb_state_t qdb_get_state (qdb_target_t target); +int qdb_get_stack_depth (qdb_target_t target); +qdb_stack_t *qdb_get_stack (qdb_target_t target); +int qdb_get_event (qdb_target_t target, qdb_event_t *event); +int qdb_get_data (qdb_target_t target, unsigned src, unsigned len, void *dst); +@overload string qdb_get_string (qdb_target_t target, unsigned str); +// note: str is likely not valid in the host progs, it's just a convinience to +// avoid cast shenanigans when getting type encoding strings +@overload string qdb_get_string (qdb_target_t target, string str); +string qdb_get_file_path (qdb_target_t target, string file); +int qdb_find_string (qdb_target_t target, string str); +qdb_def_t qdb_find_global (qdb_target_t target, string name); +qdb_def_t qdb_find_field (qdb_target_t target, string name); +qdb_function_t *qdb_find_function (qdb_target_t target, string name); +qdb_function_t *qdb_get_function (qdb_target_t target, unsigned fnum); +qdb_auxfunction_t *qdb_find_auxfunction (qdb_target_t target, string name); +qdb_auxfunction_t *qdb_get_auxfunction (qdb_target_t target, unsigned fnum); +qdb_def_t *qdb_get_local_defs (qdb_target_t target, unsigned fnum); +unsigned qdb_get_source_line_addr(qdb_target_t target, string file, + unsigned line); +int qdb_has_data_stack (qdb_target_t target); +unsigned qdb_get_frame_addr (qdb_target_t target); +void traceon(); +void traceoff(); + +#else//GCC + +void QWAQ_Debug_Init (progs_t *pr); +void QWAQ_DebugTarget_Init (progs_t *pr); + +#endif + +#endif//__qwaq_debugger_debug_h diff --git a/ruamoko/qwaq/debugger/debug.r b/ruamoko/qwaq/debugger/debug.r new file mode 100644 index 000000000..523476275 --- /dev/null +++ b/ruamoko/qwaq/debugger/debug.r @@ -0,0 +1,34 @@ +#include "ruamoko/qwaq/debugger/debug.h" + +void traceon() = #0; +void traceoff() = #0; + +void qdb_set_trace (qdb_target_t target, int state) = #0; +int qdb_set_breakpoint (qdb_target_t target, unsigned staddr) = #0; +int qdb_clear_breakpoint (qdb_target_t target, unsigned staddr) = #0; +int qdb_set_watchpoint (qdb_target_t target, unsigned offset) = #0; +int qdb_clear_watchpoint (qdb_target_t target) = #0; +int qdb_continue (qdb_target_t target) = #0; +qdb_state_t qdb_get_state (qdb_target_t target) = #0; +int qdb_get_stack_depth (qdb_target_t target) = #0; +qdb_stack_t *qdb_get_stack (qdb_target_t target) = #0; +int qdb_get_event (qdb_target_t target, qdb_event_t *event) = #0; +int qdb_get_data (qdb_target_t target, unsigned src, unsigned len, + void *dst) = #0; +string qdb_get_string (qdb_target_t target, unsigned str) = #0; +string qdb_get_string (qdb_target_t target, string str) = #0; +string qdb_get_file_path (qdb_target_t target, string file) = #0; +int qdb_find_string (qdb_target_t target, string str) = #0; +qdb_def_t qdb_find_global (qdb_target_t target, string name) = #0; +qdb_def_t qdb_find_field (qdb_target_t target, string name) = #0; +qdb_function_t *qdb_find_function (qdb_target_t target, string name) = #0; +qdb_function_t *qdb_get_function (qdb_target_t target, unsigned fnum) = #0; +qdb_auxfunction_t *qdb_find_auxfunction (qdb_target_t target, + string name) = #0; +qdb_auxfunction_t *qdb_get_auxfunction (qdb_target_t target, + unsigned fnum) = #0; +qdb_def_t *qdb_get_local_defs (qdb_target_t target, unsigned fnum) = #0; +unsigned qdb_get_source_line_addr(qdb_target_t target, string file, + unsigned line) = #0; +int qdb_has_data_stack (qdb_target_t target) = #0; +unsigned qdb_get_frame_addr (qdb_target_t target) = #0; diff --git a/ruamoko/qwaq/debugger/debugger.h b/ruamoko/qwaq/debugger/debugger.h new file mode 100644 index 000000000..64dce073c --- /dev/null +++ b/ruamoko/qwaq/debugger/debugger.h @@ -0,0 +1,52 @@ +#ifndef __qwaq_debugger_debugger_h +#define __qwaq_debugger_debugger_h + +#include +#include + +#include "ruamoko/qwaq/debugger/debug.h" +#include "ruamoko/qwaq/debugger/localsdata.h" + +@class ProxyView; +@class Editor; +@class EditStatus; +@class ScrollBar; +@class Window; +@class Array; +@class TableView; + +@interface Debugger : Object +{ + qdb_target_t target; + qdb_event_t event; + struct { + qdb_state_t state; + int depth; + int until_function; + } trace_cond; + struct { + int onEnter; + int onExit; + } sub_cond; + SEL traceHandler; + SEL breakHandler; + int running; + + Window *source_window; + ScrollBar *source_scrollbar; + EditStatus *source_status; + ProxyView *file_proxy; + Array *files; + Editor *current_file; + + Window *locals_window; + LocalsData *locals_data; + TableView *locals_view; +} ++(Debugger *)withTarget:(qdb_target_t)target; +-initWithTarget:(qdb_target_t) target; +-(qdb_target_t)target; +-handleDebugEvent; +@end + +#endif//__qwaq_debugger_debugger_h diff --git a/ruamoko/qwaq/debugger/debugger.r b/ruamoko/qwaq/debugger/debugger.r new file mode 100644 index 000000000..6afa14e84 --- /dev/null +++ b/ruamoko/qwaq/debugger/debugger.r @@ -0,0 +1,323 @@ +#include +#include +#include +#include + +#include "ruamoko/qwaq/ui/curses.h" +#include "ruamoko/qwaq/ui/listener.h" +#include "ruamoko/qwaq/ui/proxyview.h" +#include "ruamoko/qwaq/ui/scrollbar.h" +#include "ruamoko/qwaq/ui/tableview.h" +#include "ruamoko/qwaq/ui/window.h" +#include "ruamoko/qwaq/debugger/debugger.h" +#include "ruamoko/qwaq/debugger/typeencodings.h" +#include "ruamoko/qwaq/editor/editor.h" +#include "ruamoko/qwaq/qwaq-app.h" + +@implementation Debugger ++(Debugger *)withTarget:(qdb_target_t)target +{ + return [[[self alloc] initWithTarget:target] autorelease]; +} + +-(qdb_target_t)target +{ + return target; +} + +-initWithTarget:(qdb_target_t) target +{ + if (!(self = [super init])) { + return nil; + } + self.target = target; + + Extent s = [application size]; + files = [[Array array] retain]; + source_window = [Window withRect: {nil, s}]; + [application addView:source_window]; + + source_scrollbar = [ScrollBar vertical:s.height - 2 at:{s.width - 1, 1}]; + [source_window insert:source_scrollbar]; + + source_status = [EditStatus withRect:{{1, 0}, {2, 1}}]; + [source_window insert:source_status]; + + return self; +} + +-(void)dealloc +{ + [files release]; + [locals_data release]; + [super dealloc]; +} + +-(Editor *) find_file:(string) filename +{ + Editor *file; + string filepath = qdb_get_file_path (target, filename); + for (int i = [files count]; i-- > 0; ) { + file = [files objectAtIndex: i]; + if ([file filepath] == filepath) { + return file; + } + } + Rect rect = {{1, 1}, [source_window size]}; + rect.extent.width -= 2; + rect.extent.height -= 2; + file = [Editor withRect:rect file:filename path:filepath]; + [files addObject: file]; + return file; +} + +-(void) setup +{ + qdb_state_t state = qdb_get_state (target); + + current_file = [self find_file: state.file]; + file_proxy = [ProxyView withView: current_file]; + [[current_file gotoLine:state.line - 1] highlightLine]; + [[current_file onEvent] addListener: self :@selector(proxy_event::)]; + [current_file setVerticalScrollBar:source_scrollbar]; + [current_file setStatusView:source_status]; + //FIXME id? + [source_window insertSelected: (View *) file_proxy]; + [source_window setTitle:[current_file filename]]; + [source_window redraw]; + + locals_window = [Window withRect:{{0, 0}, {40, 10}}]; + [locals_window setBackground: color_palette[064]]; + [locals_window setTitle: "Locals"]; + locals_data = [[LocalsData withTarget:target] retain]; + locals_view = [TableView withRect:{{1, 1}, {38, 8}}]; + [locals_view addColumn:[TableViewColumn named:"name" width:12]]; + [locals_view addColumn:[[TableViewColumn named:"value" width:26] + setGrowMode:gfGrowHiX]]; + ScrollBar *sb = [ScrollBar vertical:8 at:{39, 1}]; + [locals_view setVerticalScrollBar:sb]; + [locals_view setDataSource:locals_data]; + [locals_window insertSelected: locals_view]; + [locals_window insert: sb]; + [application addView: locals_window]; + + [[locals_view onEvent] addListener:self :@selector(proxy_event::)]; +} + +-(void) show_line +{ + qdb_state_t state = qdb_get_state (target); + Editor *file = [self find_file: state.file]; + + if (current_file != file) { + [current_file setVerticalScrollBar:nil]; + [[current_file onEvent] removeListener:self :@selector(proxy_event::)]; + [file_proxy setView:file]; + [[file onEvent] addListener:self :@selector(proxy_event::)]; + [file setVerticalScrollBar:source_scrollbar]; + [file setStatusView:source_status]; + [source_window setTitle:[file filename]]; + current_file = file; + } + [[current_file gotoLine:state.line - 1] highlightLine]; + [source_window redraw]; +} + +-(void)update_watchvars +{ + qdb_state_t state = qdb_get_state (target); + [locals_data setFunction:state.func]; + [locals_data fetchData]; + [locals_view redraw]; +} + +static int +proxy_event_stopped (Debugger *self, id proxy, qwaq_event_t *event) +{ + if (event.what == qe_mouseclick && !(event.mouse.buttons & 0x78)) { + if (proxy == self.current_file) { + printf ("%s\n", [proxy getWordAt: {event.mouse.x, event.mouse.y}]); + [self.source_window redraw]; + return 1; + } + } else if (event.what == qe_keydown) { + switch (event.key.code) { + case QFK_F7: + case 's': + self.traceHandler = @selector(traceStep); + qdb_set_trace (self.target, 1); + self.trace_cond.state = qdb_get_state (self.target); + self.running = 1; + qdb_continue (self.target); + return 1; + case QFK_F8: + case 'n': + self.traceHandler = @selector(traceNext); + qdb_set_trace (self.target, 1); + self.trace_cond.state = qdb_get_state (self.target); + self.trace_cond.depth = qdb_get_stack_depth (self.target); + self.running = 1; + qdb_continue (self.target); + return 1; + case QFK_F4: + string file = [self.current_file filename]; + unsigned line = [self.current_file cursor].y + 1; + unsigned addr = qdb_get_source_line_addr (self.target, + file, line); + int set = -1; + if (addr) { + set = qdb_set_breakpoint (self.target, addr); + } + if (set >= 0) { + qdb_set_trace (self.target, 0); + if (set) { + self.breakHandler = @selector(breakKeep); + } else { + self.breakHandler = @selector(breakClear); + } + self.running = 1; + qdb_continue (self.target); + } else { + } + return 1; + } + } + return 0; +} + +static int +proxy_event_running (Debugger *self, id proxy, qwaq_event_t *event) +{ + return 0; +} + +-(void)proxy_event:(id)proxy :(qwaq_event_t *)event +{ + if (running) { + if (proxy_event_running (self, proxy, event)) { + event.what = qe_none; + } + } else { + if (proxy_event_stopped (self, proxy, event)) { + event.what = qe_none; + } + } +} + +-stop:(prdebug_t)reason +{ + running = 0; + if (!file_proxy) { + [self setup]; + } + [self show_line]; + [self update_watchvars]; + return self; +} + +// stop only if the progs have not advanced (may be a broken jump) +// or the progs have advanced to a different source line +static int +is_new_line (qdb_state_t last_state, qdb_state_t state) +{ + return !(last_state.staddr != state.staddr + && last_state.func == state.func + && last_state.file == state.file + && last_state.line == state.line); +} + +-traceStep +{ + qdb_state_t state = qdb_get_state (target); + + if (trace_cond.until_function && trace_cond.until_function == state.func) { + trace_cond.until_function = 0; + [self stop:prd_trace]; + return self; + } + if (is_new_line(trace_cond.state, state)) { + [self stop:prd_trace]; + return self; + } + trace_cond.state = state; + qdb_continue (self.target); + return self; +} + +-traceNext +{ + qdb_state_t state = qdb_get_state (target); + if (trace_cond.until_function && trace_cond.until_function == state.func) { + trace_cond.until_function = 0; + [self stop:prd_trace]; + return self; + } + if (is_new_line(trace_cond.state, state) + && qdb_get_stack_depth (target) <= trace_cond.depth) { + [self stop:prd_trace]; + return self; + } + trace_cond.state = state; + qdb_continue (self.target); + return self; +} + +-breakKeep +{ + return self; +} + +-breakClear +{ + qdb_state_t state = qdb_get_state (target); + qdb_clear_breakpoint (target, state.staddr); + return self; +} + +-handleDebugEvent +{ + if (qdb_get_event (target, &event)) { + switch (event.what) { + case prd_none: + break; // shouldn't happen + case prd_trace: + [self performSelector:traceHandler]; + break; + case prd_breakpoint: + case prd_watchpoint: + [self performSelector:breakHandler]; + [self stop:event.what]; + break; + case prd_subenter: + if (sub_cond.onEnter) { + [self stop:event.what]; + } else { + qdb_continue (self.target); + } + break; + case prd_subexit: + if (sub_cond.onExit) { + [self stop:event.what]; + } else { + qdb_continue (self.target); + } + break; + case prd_begin: + trace_cond.until_function = event.function; + [self stop:event.what]; + break; + case prd_terminate: + wprintf(stdscr, "Program ended: %d\n", event.exit_code); + [self stop:event.what]; + break; + case prd_runerror: + case prd_error: + wprintf(stdscr, "%s\n", event.message); + [self stop:event.what]; + break; + } + } + return self; +} + +@end diff --git a/ruamoko/qwaq/debugger/localsdata.h b/ruamoko/qwaq/debugger/localsdata.h new file mode 100644 index 000000000..3d4080998 --- /dev/null +++ b/ruamoko/qwaq/debugger/localsdata.h @@ -0,0 +1,31 @@ +#ifndef __qwaq_debugger_localsview_h +#define __qwaq_debugger_localsview_h + +#include +#include "ruamoko/qwaq/ui/tableview.h" +#include "ruamoko/qwaq/debugger/debug.h" + +@class DefView; +@class ListenerGroup; + +@interface LocalsData : Object +{ + ListenerGroup *onRowCountChanged; + qdb_target_t target; + int has_stack; + qfot_type_encodings_t target_encodings; + unsigned current_fnum; + qdb_function_t *func; + qdb_auxfunction_t *aux_func; + qdb_def_t *defs; + int num_user_defs; + DefView **def_views; + int *def_rows; + void *data; +} ++(LocalsData *)withTarget:(qdb_target_t)target; +-setFunction:(unsigned)fnum; +-fetchData; +@end + +#endif diff --git a/ruamoko/qwaq/debugger/localsdata.r b/ruamoko/qwaq/debugger/localsdata.r new file mode 100644 index 000000000..73b0f51cc --- /dev/null +++ b/ruamoko/qwaq/debugger/localsdata.r @@ -0,0 +1,170 @@ +#include +#include +#include +#include "ruamoko/qwaq/debugger/views/defview.h" +#include "ruamoko/qwaq/debugger/views/nameview.h" +#include "ruamoko/qwaq/debugger/localsdata.h" +#include "ruamoko/qwaq/ui/listener.h" + +@implementation LocalsData + +-initWithTarget:(qdb_target_t) target +{ + if (!(self = [super init])) { + return nil; + } + self.target = target; + + qdb_def_t encodings_def = qdb_find_global (target, ".type_encodings"); + qdb_get_data (target, encodings_def.offset, sizeof(target_encodings), + &target_encodings); + + self.has_stack = qdb_has_data_stack (target); + + self.onRowCountChanged = [[ListenerGroup listener] retain]; + return self; +} + ++(LocalsData *)withTarget:(qdb_target_t)target +{ + return [[[self alloc] initWithTarget:target] autorelease]; +} + +static void +free_defs (LocalsData *self) +{ + obj_free (self.defs); + self.defs = nil; + for (int i = 0; i < self.num_user_defs; i++) { + [self.def_views[i] release]; + } + obj_free (self.def_views); + self.def_views = nil; + obj_free (self.def_rows); + self.def_rows = nil; +} + +-(void)dealloc +{ + [onRowCountChanged release]; + if (defs) { + free_defs (self); + } + if (data) { + obj_free (data); + data = nil; + } + [super dealloc]; +} + +-setFunction:(unsigned) fnum +{ + if (current_fnum == fnum) { + return self; + } + current_fnum = fnum; + + if (defs) { + free_defs (self); + } + if (data) { + obj_free (data); + data = nil; + } + func = qdb_get_function (target, fnum); + if (func && func.local_size) { + data = obj_malloc (func.local_size); + } + aux_func = qdb_get_auxfunction (target, fnum); + num_user_defs = 0; + if (aux_func) { + defs = qdb_get_local_defs (target, fnum); + for (int i = 0; i < aux_func.num_locals; i++) { + string def_name = qdb_get_string (target, defs[i].name); + if (str_mid (def_name, 0, 1) != ".") { + num_user_defs++; + } + } + def_views = obj_malloc (num_user_defs); + def_rows = obj_malloc (num_user_defs + 1); + def_rows[0] = 0; + for (int i = 0, j = 0; i < aux_func.num_locals; i++) { + string def_name = qdb_get_string (target, defs[i].name); + if (str_mid (def_name, 0, 1) == ".") { + continue; + } + def_views[j] = [[DefView withDef:defs[i] in:data target:target] + retain]; + def_rows[j + 1] = [def_views[j] rows]; + j++; + } + prefixsum (def_rows, num_user_defs + 1); + } + [onRowCountChanged respond:self]; + return self; +} + +-fetchData +{ + if (data && func.local_size) { + unsigned local_data = func.local_data; + if (has_stack) { + unsigned stack_ptr = qdb_get_frame_addr (target); + local_data = 0; + if (stack_ptr) { + local_data = stack_ptr - func.local_data; + } + } + if (local_data) { + qdb_get_data (target, local_data, func.local_size, data); + } + } + int rowCount = def_rows[num_user_defs]; + if (aux_func) { + def_rows[0] = 0; + for (int i = 0; i < num_user_defs; i++) { + [def_views[i] fetchData]; + def_rows[i + 1] = [def_views[i] rows]; + } + prefixsum (def_rows, num_user_defs + 1); + } + if (rowCount != def_rows[num_user_defs]) { + [onRowCountChanged respond:self]; + } + return self; +} + +-(ListenerGroup *)onRowCountChanged +{ + return onRowCountChanged; +} + +-(int)numberOfRows:(TableView *)tableview +{ + if (aux_func) { + if (!num_user_defs) { + return 0; + } + return def_rows[num_user_defs]; + } else if (func) { + return (func.local_size + 3) / 4; + } + return 0; +} + +-(View *)tableView:(TableView *)tableview + forColumn:(TableViewColumn *)column + row:(int)row +{ + View *view = nil; + int *index = fbsearch (&row, def_rows, num_user_defs, 1, nil); + + if (index) { + DefView *dv = def_views[index - def_rows]; + int r = row - *index; + view = [dv viewAtRow: r forColumn:column]; + } + return view; +} + +@end diff --git a/ruamoko/qwaq/debugger/typeencodings.h b/ruamoko/qwaq/debugger/typeencodings.h new file mode 100644 index 000000000..0ed0f83d7 --- /dev/null +++ b/ruamoko/qwaq/debugger/typeencodings.h @@ -0,0 +1,14 @@ +#ifndef __qwaq_debugger_typeencodings_h +#define __qwaq_debugger_typeencodings_h + +#include +#include + +#include "ruamoko/qwaq/debugger/debug.h" + +@interface TypeEncodings : Object ++(qfot_type_t *)getType:(unsigned)typeAddr fromTarget:(qdb_target_t)target; ++(int)typeSize:(qfot_type_t *)type; +@end + +#endif//__qwaq_debugger_typeencodings_h diff --git a/ruamoko/qwaq/debugger/typeencodings.r b/ruamoko/qwaq/debugger/typeencodings.r new file mode 100644 index 000000000..a3053e69d --- /dev/null +++ b/ruamoko/qwaq/debugger/typeencodings.r @@ -0,0 +1,247 @@ +#include +#include +#include + +#include "ruamoko/qwaq/ui/curses.h" // printf FIXME +#include "ruamoko/qwaq/debugger/typeencodings.h" + +@implementation TypeEncodings + +// these are encodings that we already have so don't need to copy them from +// the target +static hashtab_t *static_encodings; +// these are codings that had to be copied from the target +static hashtab_t *dynamic_encodings; + +static qfot_type_t * +next_type (qfot_type_t *type) +{ + int size = type.size; + + if (!size) { // null type + size = TYPESIZE; + } + return (qfot_type_t *) ((int *) type + size); +} + +static string +type_get_key (void *t, void *unusded) +{ + qfot_type_t *type = (qfot_type_t *) t; + return type.encoding; +} + +static void type_free (void *t, void *unused) +{ + qfot_type_t *type = (qfot_type_t *) t; + str_free (type.encoding); + switch (type.meta) { + case ty_basic: + case ty_array: + break; + case ty_struct: + case ty_union: + case ty_enum: + str_free (type.strct.tag); + for (int i = 0; i < type.strct.num_fields; i++) { + str_free (type.strct.fields[i].name); + } + break; + case ty_class: + str_free (type.class); + break; + case ty_alias: + str_free (type.alias.name); + break; + case ty_handle: + str_free (type.handle.tag); + break; + } + obj_free (t); +} + ++initialize +{ + qfot_type_encodings_t *encodings; + qfot_type_t *type; + + static_encodings = Hash_NewTable (1023, type_get_key, nil, nil); + dynamic_encodings = Hash_NewTable (1023, type_get_key, type_free, nil); + + encodings = PR_FindGlobal (".type_encodings"); + for (type = encodings.types; + ((int *)type - (int *) encodings.types) < encodings.size; + type = next_type (type)) { + Hash_Add (static_encodings, type); + } + return self; +} + ++(qfot_type_t *) getType:(unsigned)typeAddr fromTarget:(qdb_target_t)target +{ + qfot_type_t buffer = {}; + string encoding; + qfot_type_t *type; + qfot_type_t *t; + + if (qdb_get_data (target, typeAddr, TYPESIZE, &buffer) < 0) { + return nil; + } + if (!buffer.size) { + return nil; + } + encoding = qdb_get_string (target, buffer.encoding); + if (!encoding) { + return nil; + } + if ((type = Hash_Find (static_encodings, encoding))) { + return type; + } + if ((type = Hash_Find (dynamic_encodings, encoding))) { + return type; + } + type = obj_calloc (1, buffer.size); + if (qdb_get_data (target, typeAddr, buffer.size, type) < 0) { + goto error; + } + if (!(type.encoding = qdb_get_string (target, type.encoding))) { + goto error; + } + switch (type.meta) { + case ty_basic: + if (type.type == ev_ptr || type.type == ev_field) { + t = [TypeEncodings getType:(unsigned)type.fldptr.aux_type + fromTarget:target]; + if (!t) { + goto error; + } + type.fldptr.aux_type = t; + } else if (type.type == ev_func) { + t = [TypeEncodings getType:(unsigned)type.func.return_type + fromTarget:target]; + if (!t) { + goto error; + } + type.func.return_type = t; + for (int i = 0; i < type.func.num_params; i++) { + t = type.func.param_types[i]; + t = [TypeEncodings getType:(unsigned)t fromTarget:target]; + if (!t) { + goto error; + } + type.func.param_types[i] = t; + } + } + goto hash_type; + case ty_array: + t = [TypeEncodings getType:(unsigned)type.array.type + fromTarget:target]; + if (!t) { + goto error; + } + type.array.type = t; + goto hash_type; + case ty_struct: + case ty_union: + case ty_enum: + if (!(type.strct.tag = qdb_get_string (target, type.strct.tag))) { + goto error; + } + for (int i = 0; i < type.strct.num_fields; i++) { + qfot_var_t *var = &type.strct.fields[i]; + if (!(var.name = qdb_get_string (target, var.name))) { + goto error; + } + t = [TypeEncodings getType:(unsigned)var.type + fromTarget:target]; + if (!t) { + goto error; + } + var.type = t; + } + goto hash_type; + case ty_class: + if (!(type.class = qdb_get_string (target, type.class))) { + goto error; + } + goto hash_type; + case ty_alias: + if (!(type.alias.name = qdb_get_string (target, type.alias.name))) { + goto error; + } + t = [TypeEncodings getType:(unsigned)type.alias.aux_type + fromTarget:target]; + if (!t) { + goto error; + } + type.alias.aux_type = t; + t = [TypeEncodings getType:(unsigned)type.alias.full_type + fromTarget:target]; + if (!t) { + goto error; + } + type.alias.full_type = t; + goto hash_type; + case ty_handle: + if (!(type.handle.tag = qdb_get_string (target, type.handle.tag))) { + goto error; + } + goto hash_type; + } + goto error; +hash_type: + printf ("fetched type %s\n", type.encoding); + Hash_Add (dynamic_encodings, type); + return type; + // not a valid type +error: + type_free (type, nil); + return nil; +} + ++(int)typeSize:(qfot_type_t *)type +{ + qfot_type_t *aux_type; + int size = 0; + + switch (type.meta) { + case ty_handle: + case ty_basic: + size = pr_type_size[type.type]; + break; + case ty_array: + aux_type = type.array.type; + size = type.array.size * [TypeEncodings typeSize:aux_type]; + break; + case ty_struct: + for (int i = 0; i < type.strct.num_fields; i++) { + aux_type = type.strct.fields[i].type; + size += [TypeEncodings typeSize:aux_type]; + } + break; + case ty_union: + for (int i = 0; i < type.strct.num_fields; i++) { + aux_type = type.strct.fields[i].type; + int s = [TypeEncodings typeSize:aux_type]; + if (s > size) { + size = s; + } + } + break; + case ty_enum: + // enums are ints + size = pr_type_size[ev_int]; + break; + case ty_class: + //FIXME + size = 1; + break; + case ty_alias: + aux_type = type.alias.aux_type; + size = [TypeEncodings typeSize:aux_type]; + break; + } + return size; +} + +@end diff --git a/ruamoko/qwaq/debugger/views/arrayview.h b/ruamoko/qwaq/debugger/views/arrayview.h new file mode 100644 index 000000000..22b45a107 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/arrayview.h @@ -0,0 +1,15 @@ +#ifndef __qwaq_debugger_arrayructview_h +#define __qwaq_debugger_arrayructview_h + +#include "ruamoko/qwaq/debugger/views/defview.h" + +@interface ArrayView : DefView +{ + unsigned *data; + DefView **element_views; + int *element_rows; +} ++(ArrayView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type; +@end + +#endif//__qwaq_debugger_arrayructview_h diff --git a/ruamoko/qwaq/debugger/views/arrayview.r b/ruamoko/qwaq/debugger/views/arrayview.r new file mode 100644 index 000000000..0faf2ab1b --- /dev/null +++ b/ruamoko/qwaq/debugger/views/arrayview.r @@ -0,0 +1,116 @@ +#include +#include +#include "ruamoko/qwaq/debugger/typeencodings.h" +#include "ruamoko/qwaq/debugger/views/indexview.h" +#include "ruamoko/qwaq/debugger/views/nameview.h" +#include "ruamoko/qwaq/debugger/views/arrayview.h" +#include "ruamoko/qwaq/ui/tableview.h" + +@implementation ArrayView + +-initWithDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + if (!(self = [super initWithDef:def type:type])) { + return nil; + } + self.data = (unsigned *)(data + def.offset); + element_views = obj_malloc (type.array.size); + element_rows = obj_malloc (type.array.size); + element_rows[0] = 0; + qfot_type_t *element_type = type.array.type; + int element_size = [TypeEncodings typeSize:element_type]; + for (int i = 0; i < type.array.size; i++) { + qdb_def_t def = { + 0, // XXX type/size not needed at this stage + i * element_size, + 0, // filled in by setTarget + (unsigned)type.fldptr.aux_type + }; + element_views[i] = [[DefView withDef:def + type:element_type + in:data + target:target] retain]; + element_rows[i + 1] = [element_views[i] rows]; + } + prefixsum (element_rows, type.array.size + 1); + return self; +} + ++(ArrayView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + return [[[self alloc] initWithDef:def in:data type:type] autorelease]; +} + +-setTarget:(qdb_target_t)target +{ + [super setTarget:target]; + for (int i = 0; i < type.array.size; i++) { + [element_views[i] setTarget:target]; + } + return self; +} + +-(void)dealloc +{ + for (int i = 0; i < type.array.size; i++) { + [element_views[i] release]; + } + obj_free (element_views); + obj_free (element_rows); + [super dealloc]; +} + +-draw +{ + [super draw]; + string val = sprintf ("%s[%d..%d]", type.array.type.encoding, + type.array.base, + type.array.base + type.array.size - 1); + [self mvprintf:{0, 0}, "%*.*s", xlen, xlen, val]; + return self; +} + +-fetchData +{ + [super fetchData]; + element_rows[0] = 0; + for (int i = 0; i < type.array.size; i++) { + [element_views[i] fetchData]; + element_rows[i + 1] = [element_views[i] rows]; + } + prefixsum (element_rows, type.array.size + 1); + return self; +} + +-(int) rows +{ + return 1 + element_rows[type.array.size]; +} + +-(View *) viewAtRow:(int)row forColumn:(TableViewColumn *)column +{ + if (row == 0) { + if ([column name] == "name") { + return [NameView withName:qdb_get_string (target, def.name)]; + } + return self; + } + + row -= 1; + + View *view = nil; + int *index = fbsearch (&row, element_rows, type.array.size, 1, nil); + + if ([column name] == "name") { + return [IndexView withIndex:index - element_rows + type.array.base]; + } + + if (index) { + DefView *dv = element_views[index - element_rows]; + int r = row - *index; + view = [dv viewAtRow: r forColumn:column]; + } + return view; +} + +@end diff --git a/ruamoko/qwaq/debugger/views/basicview.h b/ruamoko/qwaq/debugger/views/basicview.h new file mode 100644 index 000000000..8df776c60 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/basicview.h @@ -0,0 +1,10 @@ +#ifndef __qwaq_debugger_basicview_h +#define __qwaq_debugger_basicview_h + +#include "ruamoko/qwaq/debugger/views/defview.h" + +@interface BasicView : DefView ++(DefView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type; +@end + +#endif//__qwaq_debugger_basicview_h diff --git a/ruamoko/qwaq/debugger/views/basicview.r b/ruamoko/qwaq/debugger/views/basicview.r new file mode 100644 index 000000000..8b6b20d35 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/basicview.r @@ -0,0 +1,48 @@ +#include +#include "ruamoko/qwaq/debugger/views/basicview.h" +#include "ruamoko/qwaq/debugger/views/nameview.h" + +static string type_views[] = { + "VoidView", + "StringView", + "FloatView", + "VectorView", + "EntityView", + "FieldView", + "FuncView", + "PointerView", + "QuatView", + "IntView", + "UIntView", // uinteger + "IntView", // short + "DoubleView", +}; + +@implementation BasicView + +-init +{ + if (!(self = [super init])) { + return nil; + } + return self; +} + ++(DefView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + string typename = nil; + if (type.type == ty_alias) { + type = type.alias.aux_type; + } + if (type.type >= 0 + && type.type < sizeof(type_views) / sizeof (type_views[0])) { + typename = type_views[type.type]; + } + id class = obj_lookup_class (typename); + if (class) { + return [class withDef:def in:data type:type]; + } + return [NameView withName:"Invalid Type"]; +} + +@end diff --git a/ruamoko/qwaq/debugger/views/defview.h b/ruamoko/qwaq/debugger/views/defview.h new file mode 100644 index 000000000..f6e8e9cda --- /dev/null +++ b/ruamoko/qwaq/debugger/views/defview.h @@ -0,0 +1,26 @@ +#ifndef __qwaq_debugger_defview_h +#define __qwaq_debugger_defview_h + +#include +#include "ruamoko/qwaq/ui/view.h" +#include "ruamoko/qwaq/debugger/debug.h" + +@class TableViewColumn; + +@interface DefView : View +{ + qdb_def_t def; + qfot_type_t *type; + qdb_target_t target; +} ++(DefView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type; ++(DefView *)withDef:(qdb_def_t)def in:(void *)data target:(qdb_target_t)target; ++(DefView *)withDef:(qdb_def_t)def type:(qfot_type_t *)type in:(void *)data target:(qdb_target_t)target; +-initWithDef:(qdb_def_t)def type:(qfot_type_t *)type; +-setTarget:(qdb_target_t)target; +-fetchData; +-(int) rows; +-(View *) viewAtRow:(int) row forColumn:(TableViewColumn *)column; +@end + +#endif//__qwaq_debugger_defview_h diff --git a/ruamoko/qwaq/debugger/views/defview.r b/ruamoko/qwaq/debugger/views/defview.r new file mode 100644 index 000000000..8be59474a --- /dev/null +++ b/ruamoko/qwaq/debugger/views/defview.r @@ -0,0 +1,99 @@ +#include +#include "ruamoko/qwaq/debugger/typeencodings.h" +#include "ruamoko/qwaq/debugger/views/defview.h" +#include "ruamoko/qwaq/debugger/views/nameview.h" +#include "ruamoko/qwaq/ui/tableview.h" + +static string meta_views[] = { + "BasicView", + "StructView", + "StructView", + "EnumView", + "ArrayView", + "ClassView", + "AliasView", // shouldn't happen, but... +}; + +@implementation DefView + +-init +{ + if (!(self = [super init])) { + return nil; + } + return self; +} + +-initWithDef:(qdb_def_t)def type:(qfot_type_t *)type +{ + if (!(self = [super init])) { + return nil; + } + self.def = def; + self.type = type; + return self; +} + +-setTarget:(qdb_target_t)target +{ + self.target = target; + return self; +} + ++(DefView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + return [self withDef:def in:data target:nil]; +} + ++(DefView *)withDef:(qdb_def_t)def + type:(qfot_type_t *)type + in:(void *)data + target:(qdb_target_t)target +{ + string metaname = nil; + if (type.meta == ty_alias) { + type = type.alias.aux_type; + } + if (type.meta >= 0 + && type.meta < sizeof(meta_views) / sizeof (meta_views[0])) { + metaname = meta_views[type.meta]; + } + id class = obj_lookup_class (metaname); + if (class) { + return [[class withDef:def in:data type:type] setTarget:target]; + } + return [NameView withName:"Invalid Meta"]; +} + ++(DefView *)withDef:(qdb_def_t)def + in:(void *)data + target:(qdb_target_t)target +{ + qfot_type_t *type = [TypeEncodings getType:def.type_encoding + fromTarget:target]; + return [[DefView withDef:def + type:type + in:data + target:target] retain]; +} + +-fetchData +{ + // most def views do not need to update themselves + return self; +} + +-(int) rows +{ + return 1; +} + +-(View *) viewAtRow:(int) row forColumn:(TableViewColumn *)column +{ + if ([column name] == "name") { + return [NameView withName:qdb_get_string (target, def.name)]; + } + return self; +} + +@end diff --git a/ruamoko/qwaq/debugger/views/doubleview.h b/ruamoko/qwaq/debugger/views/doubleview.h new file mode 100644 index 000000000..738199d36 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/doubleview.h @@ -0,0 +1,13 @@ +#ifndef __qwaq_debugger_doubleview_h +#define __qwaq_debugger_doubleview_h + +#include "ruamoko/qwaq/debugger/views/defview.h" + +@interface DoubleView : DefView +{ + double *data; +} ++(DoubleView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type; +@end + +#endif//__qwaq_debugger_doubleview_h diff --git a/ruamoko/qwaq/debugger/views/doubleview.r b/ruamoko/qwaq/debugger/views/doubleview.r new file mode 100644 index 000000000..16d4653a1 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/doubleview.r @@ -0,0 +1,28 @@ +#include +#include "ruamoko/qwaq/debugger/views/doubleview.h" + +@implementation DoubleView + +-initWithDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + if (!(self = [super initWithDef:def type:type])) { + return nil; + } + self.data = (double *)(data + def.offset); + return self; +} + ++(DoubleView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + return [[[self alloc] initWithDef:def in:data type:type] autorelease]; +} + +-draw +{ + [super draw]; + string val = sprintf ("%.17g", data[0]); + [self mvprintf:{0, 0}, "%*.*s", xlen, xlen, val]; + return self; +} + +@end diff --git a/ruamoko/qwaq/debugger/views/entityview.h b/ruamoko/qwaq/debugger/views/entityview.h new file mode 100644 index 000000000..18453489a --- /dev/null +++ b/ruamoko/qwaq/debugger/views/entityview.h @@ -0,0 +1,13 @@ +#ifndef __qwaq_debugger_entityview_h +#define __qwaq_debugger_entityview_h + +#include "ruamoko/qwaq/debugger/views/defview.h" + +@interface EntityView : DefView +{ + entity *data; +} ++(EntityView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type; +@end + +#endif//__qwaq_debugger_entityview_h diff --git a/ruamoko/qwaq/debugger/views/entityview.r b/ruamoko/qwaq/debugger/views/entityview.r new file mode 100644 index 000000000..f1200a9c6 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/entityview.r @@ -0,0 +1,28 @@ +#include +#include "ruamoko/qwaq/debugger/views/entityview.h" + +@implementation EntityView + +-initWithDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + if (!(self = [super initWithDef:def type:type])) { + return nil; + } + self.data = (entity *)(data + def.offset); + return self; +} + ++(EntityView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + return [[[self alloc] initWithDef:def in:data type:type] autorelease]; +} + +-draw +{ + [super draw]; + string val = sprintf ("FIXME [%x]", data[0]); + [self mvprintf:{0, 0}, "%*.*s", xlen, xlen, val]; + return self; +} + +@end diff --git a/ruamoko/qwaq/debugger/views/fieldview.h b/ruamoko/qwaq/debugger/views/fieldview.h new file mode 100644 index 000000000..c97560e2c --- /dev/null +++ b/ruamoko/qwaq/debugger/views/fieldview.h @@ -0,0 +1,13 @@ +#ifndef __qwaq_debugger_fieldview_h +#define __qwaq_debugger_fieldview_h + +#include "ruamoko/qwaq/debugger/views/defview.h" + +@interface FieldView : DefView +{ + unsigned *data; +} ++(FieldView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type; +@end + +#endif//__qwaq_debugger_fieldview_h diff --git a/ruamoko/qwaq/debugger/views/fieldview.r b/ruamoko/qwaq/debugger/views/fieldview.r new file mode 100644 index 000000000..f09839bf2 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/fieldview.r @@ -0,0 +1,28 @@ +#include +#include "ruamoko/qwaq/debugger/views/fieldview.h" + +@implementation FieldView + +-initWithDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + if (!(self = [super initWithDef:def type:type])) { + return nil; + } + self.data = (unsigned *)(data + def.offset); + return self; +} + ++(FieldView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + return [[[self alloc] initWithDef:def in:data type:type] autorelease]; +} + +-draw +{ + [super draw]; + string val = sprintf ("FIXME [%x]", data[0]); + [self mvprintf:{0, 0}, "%*.*s", xlen, xlen, val]; + return self; +} + +@end diff --git a/ruamoko/qwaq/debugger/views/floatview.h b/ruamoko/qwaq/debugger/views/floatview.h new file mode 100644 index 000000000..db73ab864 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/floatview.h @@ -0,0 +1,13 @@ +#ifndef __qwaq_debugger_floatview_h +#define __qwaq_debugger_floatview_h + +#include "ruamoko/qwaq/debugger/views/defview.h" + +@interface FloatView : DefView +{ + float *data; +} ++(FloatView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type; +@end + +#endif//__qwaq_debugger_floatview_h diff --git a/ruamoko/qwaq/debugger/views/floatview.r b/ruamoko/qwaq/debugger/views/floatview.r new file mode 100644 index 000000000..516670815 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/floatview.r @@ -0,0 +1,28 @@ +#include +#include "ruamoko/qwaq/debugger/views/floatview.h" + +@implementation FloatView + +-initWithDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + if (!(self = [super initWithDef:def type:type])) { + return nil; + } + self.data = (float *)(data + def.offset); + return self; +} + ++(FloatView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + return [[[self alloc] initWithDef:def in:data type:type] autorelease]; +} + +-draw +{ + [super draw]; + string val = sprintf ("%.9g", data[0]); + [self mvprintf:{0, 0}, "%*.*s", xlen, xlen, val]; + return self; +} + +@end diff --git a/ruamoko/qwaq/debugger/views/funcview.h b/ruamoko/qwaq/debugger/views/funcview.h new file mode 100644 index 000000000..8b8adeee1 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/funcview.h @@ -0,0 +1,13 @@ +#ifndef __qwaq_debugger_funcview_h +#define __qwaq_debugger_funcview_h + +#include "ruamoko/qwaq/debugger/views/defview.h" + +@interface FuncView : DefView +{ + unsigned *data; +} ++(FuncView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type; +@end + +#endif//__qwaq_debugger_funcview_h diff --git a/ruamoko/qwaq/debugger/views/funcview.r b/ruamoko/qwaq/debugger/views/funcview.r new file mode 100644 index 000000000..81025da3a --- /dev/null +++ b/ruamoko/qwaq/debugger/views/funcview.r @@ -0,0 +1,28 @@ +#include +#include "ruamoko/qwaq/debugger/views/funcview.h" + +@implementation FuncView + +-initWithDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + if (!(self = [super initWithDef:def type:type])) { + return nil; + } + self.data = (unsigned *)(data + def.offset); + return self; +} + ++(FuncView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + return [[[self alloc] initWithDef:def in:data type:type] autorelease]; +} + +-draw +{ + [super draw]; + string val = sprintf ("FIXME [%x]", data[0]); + [self mvprintf:{0, 0}, "%*.*s", xlen, xlen, val]; + return self; +} + +@end diff --git a/ruamoko/qwaq/debugger/views/indexview.h b/ruamoko/qwaq/debugger/views/indexview.h new file mode 100644 index 000000000..0683e0fc1 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/indexview.h @@ -0,0 +1,13 @@ +#ifndef __qwaq_debugger_indexview_h +#define __qwaq_debugger_indexview_h + +#include "ruamoko/qwaq/debugger/views/defview.h" + +@interface IndexView : DefView +{ + int index; +} ++(IndexView *)withIndex:(int)index; +@end + +#endif//__qwaq_debugger_indexview_h diff --git a/ruamoko/qwaq/debugger/views/indexview.r b/ruamoko/qwaq/debugger/views/indexview.r new file mode 100644 index 000000000..59689792e --- /dev/null +++ b/ruamoko/qwaq/debugger/views/indexview.r @@ -0,0 +1,33 @@ +#include +#include "ruamoko/qwaq/debugger/views/indexview.h" + +@implementation IndexView + +-initWithIndex:(int)index +{ + if (!(self = [super initWithDef:def type:nil])) { + return nil; + } + self.index = index; + return self; +} + ++(IndexView *)withIndex:(int)index +{ + return [[[self alloc] initWithIndex:index] autorelease]; +} + +-draw +{ + [super draw]; + string val = sprintf ("[%d]", index); + [self mvprintf:{0, 0}, "%*.*s", xlen, xlen, val]; + return self; +} + +-(View *) viewAtRow:(int) row forColumn:(TableViewColumn *)column +{ + return self; +} + +@end diff --git a/ruamoko/qwaq/debugger/views/intview.h b/ruamoko/qwaq/debugger/views/intview.h new file mode 100644 index 000000000..e5fe8f659 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/intview.h @@ -0,0 +1,13 @@ +#ifndef __qwaq_debugger_intview_h +#define __qwaq_debugger_intview_h + +#include "ruamoko/qwaq/debugger/views/defview.h" + +@interface IntView : DefView +{ + int *data; +} ++(IntView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type; +@end + +#endif//__qwaq_debugger_intview_h diff --git a/ruamoko/qwaq/debugger/views/intview.r b/ruamoko/qwaq/debugger/views/intview.r new file mode 100644 index 000000000..f8115bff3 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/intview.r @@ -0,0 +1,28 @@ +#include +#include "ruamoko/qwaq/debugger/views/intview.h" + +@implementation IntView + +-initWithDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + if (!(self = [super initWithDef:def type:type])) { + return nil; + } + self.data = (int *)(data + def.offset); + return self; +} + ++(IntView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + return [[[self alloc] initWithDef:def in:data type:type] autorelease]; +} + +-draw +{ + [super draw]; + string val = sprintf ("%d", data[0]); + [self mvprintf:{0, 0}, "%*.*s", xlen, xlen, val]; + return self; +} + +@end diff --git a/ruamoko/qwaq/debugger/views/nameview.h b/ruamoko/qwaq/debugger/views/nameview.h new file mode 100644 index 000000000..ee5622062 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/nameview.h @@ -0,0 +1,13 @@ +#ifndef __qwaq_debugger_nameview_h +#define __qwaq_debugger_nameview_h + +#include "ruamoko/qwaq/debugger/views/defview.h" + +@interface NameView : DefView +{ + string name; +} ++(NameView *)withName:(string)name; +@end + +#endif//__qwaq_debugger_nameview_h diff --git a/ruamoko/qwaq/debugger/views/nameview.r b/ruamoko/qwaq/debugger/views/nameview.r new file mode 100644 index 000000000..d3002aac7 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/nameview.r @@ -0,0 +1,38 @@ +#include +#include "ruamoko/qwaq/debugger/views/nameview.h" + +@implementation NameView + +-initWithName:(string)name +{ + if (!(self = [super initWithDef:def type:nil])) { + return nil; + } + self.name = name; + return self; +} + +-(void)dealloc +{ + str_free (name); + [super dealloc]; +} + ++(NameView *)withName:(string)name +{ + return [[[self alloc] initWithName:name] autorelease]; +} + +-draw +{ + [super draw]; + [self mvaddstr:{0, 0}, str_mid (name, 0, xlen)]; + return self; +} + +-(View *) viewAtRow:(int) row forColumn:(TableViewColumn *)column +{ + return self; +} + +@end diff --git a/ruamoko/qwaq/debugger/views/pointerview.h b/ruamoko/qwaq/debugger/views/pointerview.h new file mode 100644 index 000000000..2e314a735 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/pointerview.h @@ -0,0 +1,19 @@ +#ifndef __qwaq_debugger_pointerview_h +#define __qwaq_debugger_pointerview_h + +#include "ruamoko/qwaq/debugger/views/defview.h" + +@interface PointerView : DefView +{ + unsigned *data; + int invalid; + unsigned ptr; + qfot_type_t *ptr_type; + int ptr_size; + void *ptr_data; + DefView *ptr_view; +} ++(PointerView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type; +@end + +#endif//__qwaq_debugger_pointerview_h diff --git a/ruamoko/qwaq/debugger/views/pointerview.r b/ruamoko/qwaq/debugger/views/pointerview.r new file mode 100644 index 000000000..f82c099e3 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/pointerview.r @@ -0,0 +1,84 @@ +#include +#include "ruamoko/qwaq/debugger/typeencodings.h" +#include "ruamoko/qwaq/debugger/views/nameview.h" +#include "ruamoko/qwaq/debugger/views/pointerview.h" +#include "ruamoko/qwaq/ui/tableview.h" + +@implementation PointerView + +-initWithDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + if (!(self = [super initWithDef:def type:type])) { + return nil; + } + self.data = (unsigned *)(data + def.offset); + return self; +} + ++(PointerView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + return [[[self alloc] initWithDef:def in:data type:type] autorelease]; +} + +-(void)dealloc +{ + if (ptr_data) { + obj_free (ptr_data); + } + [ptr_view release]; + [super dealloc]; +} + +-draw +{ + [super draw]; + string val = sprintf ("%s [0x%x]", type.encoding, data[0]); + [self mvprintf:{0, 0}, "%*.*s", xlen, xlen, val]; + return self; +} + +-fetchData +{ + if (!ptr_type) { + qdb_def_t def = { 0, 0, 0, (unsigned)type.fldptr.aux_type }; + ptr_type = type.fldptr.aux_type; + ptr_size = [TypeEncodings typeSize:ptr_type]; + ptr_data = obj_malloc (ptr_size); + ptr_view = [[DefView withDef:def + type:ptr_type + in:ptr_data + target:target] retain]; + } + invalid = 1; + if (!ptr_view || ptr != (unsigned) data[0]) { + invalid = qdb_get_data (target, data[0], ptr_size, ptr_data) < 0; + } + return self; +} + +-(int) rows +{ + if (invalid) { + return 2; + } + return 1 + [ptr_view rows]; +} + +-(View *) viewAtRow:(int) row forColumn:(TableViewColumn *)column +{ + if (row == 0) { + if ([column name] == "name") { + return [NameView withName:qdb_get_string (target, def.name)]; + } + return self; + } + if (invalid) { + if ([column name] == "name") { + return nil; + } + return [NameView withName:"Invalid pointer"]; + } + return [ptr_view viewAtRow:row - 1 forColumn:column]; +} + +@end diff --git a/ruamoko/qwaq/debugger/views/quatview.h b/ruamoko/qwaq/debugger/views/quatview.h new file mode 100644 index 000000000..30de36d8f --- /dev/null +++ b/ruamoko/qwaq/debugger/views/quatview.h @@ -0,0 +1,13 @@ +#ifndef __qwaq_debugger_quatview_h +#define __qwaq_debugger_quatview_h + +#include "ruamoko/qwaq/debugger/views/defview.h" + +@interface QuatView : DefView +{ + quaternion *data; +} ++(QuatView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type; +@end + +#endif//__qwaq_debugger_quatview_h diff --git a/ruamoko/qwaq/debugger/views/quatview.r b/ruamoko/qwaq/debugger/views/quatview.r new file mode 100644 index 000000000..202968415 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/quatview.r @@ -0,0 +1,28 @@ +#include +#include "ruamoko/qwaq/debugger/views/quatview.h" + +@implementation QuatView + +-initWithDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + if (!(self = [super initWithDef:def type:type])) { + return nil; + } + self.data = (quaternion *)(data + def.offset); + return self; +} + ++(QuatView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + return [[[self alloc] initWithDef:def in:data type:type] autorelease]; +} + +-draw +{ + [super draw]; + string val = sprintf ("%.9q", data[0]); + [self mvprintf:{0, 0}, "%*.*s", xlen, xlen, val]; + return self; +} + +@end diff --git a/ruamoko/qwaq/debugger/views/stringview.h b/ruamoko/qwaq/debugger/views/stringview.h new file mode 100644 index 000000000..badbef28c --- /dev/null +++ b/ruamoko/qwaq/debugger/views/stringview.h @@ -0,0 +1,13 @@ +#ifndef __qwaq_debugger_stringview_h +#define __qwaq_debugger_stringview_h + +#include "ruamoko/qwaq/debugger/views/defview.h" + +@interface StringView : DefView +{ + int *data; +} ++(StringView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type; +@end + +#endif//__qwaq_debugger_stringview_h diff --git a/ruamoko/qwaq/debugger/views/stringview.r b/ruamoko/qwaq/debugger/views/stringview.r new file mode 100644 index 000000000..d1c4808d3 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/stringview.r @@ -0,0 +1,29 @@ +#include +#include "ruamoko/qwaq/debugger/views/stringview.h" + +@implementation StringView + +-initWithDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + if (!(self = [super initWithDef:def type:type])) { + return nil; + } + self.data = (int *)(data + def.offset); + return self; +} + ++(StringView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + return [[[self alloc] initWithDef:def in:data type:type] autorelease]; +} + +-draw +{ + [super draw]; + string val = sprintf ("\"%s\"", + str_quote (qdb_get_string (target, data[0]))); + [self mvprintf:{0, 0}, "%*.*s", xlen, xlen, val]; + return self; +} + +@end diff --git a/ruamoko/qwaq/debugger/views/structview.h b/ruamoko/qwaq/debugger/views/structview.h new file mode 100644 index 000000000..85ecccdd4 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/structview.h @@ -0,0 +1,15 @@ +#ifndef __qwaq_debugger_structview_h +#define __qwaq_debugger_structview_h + +#include "ruamoko/qwaq/debugger/views/defview.h" + +@interface StructView : DefView +{ + unsigned *data; + DefView **field_views; + int *field_rows; +} ++(StructView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type; +@end + +#endif//__qwaq_debugger_structview_h diff --git a/ruamoko/qwaq/debugger/views/structview.r b/ruamoko/qwaq/debugger/views/structview.r new file mode 100644 index 000000000..1db4f5fe9 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/structview.r @@ -0,0 +1,120 @@ +#include +#include +#include "ruamoko/qwaq/debugger/typeencodings.h" +#include "ruamoko/qwaq/debugger/views/nameview.h" +#include "ruamoko/qwaq/debugger/views/structview.h" +#include "ruamoko/qwaq/ui/tableview.h" + +@implementation StructView + +-initWithDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + if (!(self = [super initWithDef:def type:type])) { + return nil; + } + self.data = (unsigned *)(data + def.offset); + field_views = obj_malloc (type.strct.num_fields); + field_rows = obj_malloc (type.strct.num_fields); + field_rows[0] = 0; + for (int i = 0; i < type.strct.num_fields; i++) { + qfot_type_t *field_type = type.strct.fields[i].type; + qdb_def_t def = { + 0, // XXX type/size not needed at this stage + type.strct.fields[i].offset, + 0, // filled in by setTarget + (unsigned)type.fldptr.aux_type + }; + field_views[i] = [[DefView withDef:def + type:field_type + in:data + target:target] retain]; + field_rows[i + 1] = 0; + if (str_mid (type.strct.fields[i].name, 0, 11) != ".anonymous.") { + field_rows[i + 1] = [field_views[i] rows]; + } + } + prefixsum (field_rows, type.strct.num_fields + 1); + return self; +} + ++(StructView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + return [[[self alloc] initWithDef:def in:data type:type] autorelease]; +} + +-setTarget:(qdb_target_t)target +{ + [super setTarget:target]; + for (int i = 0; i < type.strct.num_fields; i++) { + if (target.handle) { + // the field name is already in local space because the same type + // may be defined in both progs + string name = type.strct.fields[i].name; + field_views[i].def.name = qdb_find_string (target, name); + } + [field_views[i] setTarget:target]; + } + return self; +} + +-(void)dealloc +{ + for (int i = 0; i < type.strct.num_fields; i++) { + [field_views[i] release]; + } + obj_free (field_views); + obj_free (field_rows); + [super dealloc]; +} + +-draw +{ + [super draw]; + string val = sprintf ("%s", type.strct.tag); + [self mvprintf:{0, 0}, "%*.*s", xlen, xlen, val]; + return self; +} + +-fetchData +{ + [super fetchData]; + field_rows[0] = 0; + for (int i = 0; i < type.strct.num_fields; i++) { + [field_views[i] fetchData]; + field_rows[i + 1] = 0; + if (str_mid (type.strct.fields[i].name, 0, 11) != ".anonymous.") { + field_rows[i + 1] = [field_views[i] rows]; + } + } + prefixsum (field_rows, type.strct.num_fields + 1); + return self; +} + +-(int) rows +{ + return 1 + field_rows[type.strct.num_fields]; +} + +-(View *) viewAtRow:(int) row forColumn:(TableViewColumn *)column +{ + if (row == 0) { + if ([column name] == "name") { + return [NameView withName:qdb_get_string (target, def.name)]; + } + return self; + } + + row -= 1; + + View *view = nil; + int *index = fbsearch (&row, field_rows, type.strct.num_fields, 1, nil); + + if (index) { + DefView *dv = field_views[index - field_rows]; + int r = row - *index; + view = [dv viewAtRow: r forColumn:column]; + } + return view; +} + +@end diff --git a/ruamoko/qwaq/debugger/views/uintview.h b/ruamoko/qwaq/debugger/views/uintview.h new file mode 100644 index 000000000..417f9d080 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/uintview.h @@ -0,0 +1,13 @@ +#ifndef __qwaq_debugger_uintview_h +#define __qwaq_debugger_uintview_h + +#include "ruamoko/qwaq/debugger/views/defview.h" + +@interface UIntView : DefView +{ + unsigned *data; +} ++(UIntView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type; +@end + +#endif//__qwaq_debugger_uintview_h diff --git a/ruamoko/qwaq/debugger/views/uintview.r b/ruamoko/qwaq/debugger/views/uintview.r new file mode 100644 index 000000000..8a0f17e03 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/uintview.r @@ -0,0 +1,28 @@ +#include +#include "ruamoko/qwaq/debugger/views/uintview.h" + +@implementation UIntView + +-initWithDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + if (!(self = [super initWithDef:def type:type])) { + return nil; + } + self.data = (unsigned *)(data + def.offset); + return self; +} + ++(UIntView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + return [[[self alloc] initWithDef:def in:data type:type] autorelease]; +} + +-draw +{ + [super draw]; + string val = sprintf ("%u", data[0]); + [self mvprintf:{0, 0}, "%*.*s", xlen, xlen, val]; + return self; +} + +@end diff --git a/ruamoko/qwaq/debugger/views/vectorview.h b/ruamoko/qwaq/debugger/views/vectorview.h new file mode 100644 index 000000000..b1595ce98 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/vectorview.h @@ -0,0 +1,13 @@ +#ifndef __qwaq_debugger_vectorview_h +#define __qwaq_debugger_vectorview_h + +#include "ruamoko/qwaq/debugger/views/defview.h" + +@interface VectorView : DefView +{ + vector *data; +} ++(VectorView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type; +@end + +#endif//__qwaq_debugger_vectorview_h diff --git a/ruamoko/qwaq/debugger/views/vectorview.r b/ruamoko/qwaq/debugger/views/vectorview.r new file mode 100644 index 000000000..f7e510742 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/vectorview.r @@ -0,0 +1,28 @@ +#include +#include "ruamoko/qwaq/debugger/views/vectorview.h" + +@implementation VectorView + +-initWithDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + if (!(self = [super initWithDef:def type:type])) { + return nil; + } + self.data = (vector *)(data + def.offset); + return self; +} + ++(VectorView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + return [[[self alloc] initWithDef:def in:data type:type] autorelease]; +} + +-draw +{ + [super draw]; + string val = sprintf ("%.9v", data[0]); + [self mvprintf:{0, 0}, "%*.*s", xlen, xlen, val]; + return self; +} + +@end diff --git a/ruamoko/qwaq/debugger/views/voidview.h b/ruamoko/qwaq/debugger/views/voidview.h new file mode 100644 index 000000000..7a2b6a144 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/voidview.h @@ -0,0 +1,13 @@ +#ifndef __qwaq_debugger_voidview_h +#define __qwaq_debugger_voidview_h + +#include "ruamoko/qwaq/debugger/views/defview.h" + +@interface VoidView : DefView +{ + unsigned *data; +} ++(VoidView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type; +@end + +#endif//__qwaq_debugger_voidview_h diff --git a/ruamoko/qwaq/debugger/views/voidview.r b/ruamoko/qwaq/debugger/views/voidview.r new file mode 100644 index 000000000..665d28064 --- /dev/null +++ b/ruamoko/qwaq/debugger/views/voidview.r @@ -0,0 +1,29 @@ +#include +#include "ruamoko/qwaq/debugger/views/voidview.h" + +@implementation VoidView + +-initWithDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + if (!(self = [super initWithDef:def type:type])) { + return nil; + } + self.data = (unsigned *) (data + def.offset); + return self; +} + ++(VoidView *)withDef:(qdb_def_t)def in:(void *)data type:(qfot_type_t *)type +{ + return [[[self alloc] initWithDef:def in:data type:type] autorelease]; +} + +-draw +{ + [super draw]; + string val = sprintf ("%08x %08x %08x %08x", + data[0], data[1], data[2], data[3]); + [self mvprintf:{0, 0}, "%*.*s", xlen, xlen, val]; + return self; +} + +@end diff --git a/ruamoko/qwaq/device/axisdata.h b/ruamoko/qwaq/device/axisdata.h new file mode 100644 index 000000000..c743bad3d --- /dev/null +++ b/ruamoko/qwaq/device/axisdata.h @@ -0,0 +1,19 @@ +#ifndef __qwaq_device_axisdata_h +#define __qwaq_device_axisdata_h + +#include "ruamoko/qwaq/qwaq-input.h" +#include "ruamoko/qwaq/ui/tableview.h" + +@class AxisView; + +@interface AxisData : Object +{ + int numaxes; + in_axisinfo_t *axes; + AxisView **axis_views; +} ++withDevice:(qwaq_devinfo_t *)device; +-updateAxis:(int)axis value:(int)value; +@end + +#endif//__qwaq_device_axisdata_h diff --git a/ruamoko/qwaq/device/axisdata.r b/ruamoko/qwaq/device/axisdata.r new file mode 100644 index 000000000..5fa92c270 --- /dev/null +++ b/ruamoko/qwaq/device/axisdata.r @@ -0,0 +1,58 @@ +#include "ruamoko/qwaq/device/axisdata.h" +#include "ruamoko/qwaq/device/axisview.h" + +@implementation AxisData + +-initWithDevice:(qwaq_devinfo_t *)device +{ + if (!(self = [super init])) { + return nil; + } + numaxes = device.numaxes; + axes = device.axes; + axis_views = obj_malloc (numaxes); + for (int i = 0; i < numaxes; i++) { + axis_views[i] = [[AxisView withAxis:&axes[i]] retain]; + } + return self; +} + ++withDevice:(qwaq_devinfo_t *)device +{ + return [[[self alloc] initWithDevice:device] autorelease]; +} + +-(void)dalloc +{ +} + +-updateAxis:(int)axis value:(int)value +{ + axes[axis].value = value; + return self; +} + +-(ListenerGroup *)onRowCountChanged +{ + return nil; +} + +-(int)numberOfRows:(TableView *)tableview +{ + return numaxes; +} + +-(View *)tableView:(TableView *)tableview + forColumn:(TableViewColumn *)column + row:(int)row +{ + View *view = nil; + + if (row >= 0 && row < numaxes) { + AxisView *av = axis_views[row]; + view = [av viewAtRow:0 forColumn:column]; + } + return view; +} + +@end diff --git a/ruamoko/qwaq/device/axisview.h b/ruamoko/qwaq/device/axisview.h new file mode 100644 index 000000000..4ea867b9d --- /dev/null +++ b/ruamoko/qwaq/device/axisview.h @@ -0,0 +1,18 @@ +#ifndef __qwaq_device_axisview_h +#define __qwaq_device_axisview_h + +#include "ruamoko/qwaq/qwaq-input.h" +#include "ruamoko/qwaq/ui/view.h" + +@class TableViewColumn; + +@interface AxisView : View +{ + in_axisinfo_t *axis; +} ++withAxis:(in_axisinfo_t *)axis; +-(int)rows; +-(View *)viewAtRow:(int)row forColumn:(TableViewColumn *)column; +@end + +#endif//__qwaq_device_axisview_h diff --git a/ruamoko/qwaq/device/axisview.r b/ruamoko/qwaq/device/axisview.r new file mode 100644 index 000000000..8509634cf --- /dev/null +++ b/ruamoko/qwaq/device/axisview.r @@ -0,0 +1,42 @@ +#include +#include "ruamoko/qwaq/ui/tableview.h" +#include "ruamoko/qwaq/device/axisview.h" +#include "ruamoko/qwaq/device/nameview.h" + +@implementation AxisView + +-initWithAxis:(in_axisinfo_t *)axis +{ + if (!(self = [super init])) { + return nil; + } + self.axis = axis; + return self; +} + ++withAxis:(in_axisinfo_t *)axis +{ + return [[[self alloc] initWithAxis:axis] retain]; +} + +-draw +{ + [super draw]; + [self mvprintf:{0, 0}, "%*d", xlen, axis.value]; + return self; +} + +-(int)rows +{ + return 1; +} + +-(View *)viewAtRow:(int)row forColumn:(TableViewColumn *)column +{ + if ([column name] == "axis") { + return [NameView withName:sprintf("%d", axis.axis)]; + } + return self; +} + +@end diff --git a/ruamoko/qwaq/device/device.h b/ruamoko/qwaq/device/device.h new file mode 100644 index 000000000..1e5864706 --- /dev/null +++ b/ruamoko/qwaq/device/device.h @@ -0,0 +1,28 @@ +#ifndef __qwaq_device_device_h +#define __qwaq_device_device_h + +#include "ruamoko/qwaq/qwaq-input.h" + +@class Window; +@class TableView; +@class AxisData; + +@interface Device : Object +{ + int devid; + qwaq_devinfo_t *device; + + Window *window; + TableView *axis_view; + AxisData *axis_data; +} ++withDevice:(qwaq_devinfo_t *)device id:(int)devid; +-updateAxis:(int)axis value:(int)value; +-updateButton:(int)button state:(int)state; +-(int)devid; +-(string)name; +-(string)id; +-redraw; +@end + +#endif//__qwaq_device_device_h diff --git a/ruamoko/qwaq/device/device.r b/ruamoko/qwaq/device/device.r new file mode 100644 index 000000000..3a8c1e7a0 --- /dev/null +++ b/ruamoko/qwaq/device/device.r @@ -0,0 +1,89 @@ +#include +#include "ruamoko/qwaq/input-app.h" +#include "ruamoko/qwaq/ui/scrollbar.h" +#include "ruamoko/qwaq/ui/stringview.h" +#include "ruamoko/qwaq/ui/tableview.h" +#include "ruamoko/qwaq/ui/window.h" +#include "ruamoko/qwaq/device/axisdata.h" +#include "ruamoko/qwaq/device/device.h" + +@implementation Device + +-initWithDevice:(qwaq_devinfo_t *)device id:(int)devid +{ + if (!(self = [super init])) { + return nil; + } + self.device = device; + self.devid = devid; + + window = [Window withRect:{{0, 0}, {40, 10}}]; + [window setBackground: color_palette[064]]; + [window setTitle: device.id]; + + [window insert:[StringView withRect:{{1, 1}, {38, 1}} string:device.name]]; + axis_data = [[AxisData withDevice:device] retain]; + axis_view = [TableView withRect:{{1, 2}, {38, 7}}]; + [axis_view addColumn:[TableViewColumn named:"axis" width:3]]; + [axis_view addColumn:[TableViewColumn named:"value" width:6]]; + ScrollBar *sb = [ScrollBar vertical:7 at:{39, 2}]; + [axis_view setVerticalScrollBar:sb]; + [axis_view setDataSource:axis_data]; + [window insertSelected: axis_view]; + [window insert: sb]; + [application addView: window]; + + return self; +} + ++withDevice:(qwaq_devinfo_t *)device id:(int)devid +{ + return [[[self alloc] initWithDevice:device id:devid] autorelease]; +} + +-(void)dealloc +{ + [application removeView:window]; + [axis_data release]; + + obj_free (device.axes); + obj_free (device.buttons); + str_free (device.name); + str_free (device.id); + obj_free (device); + [super dealloc]; +} + +-updateAxis:(int)axis value:(int)value +{ + [axis_data updateAxis:axis value:value]; + return self; +} + +-updateButton:(int)button state:(int)state +{ + return self; +} + +-(int)devid +{ + return devid; +} + +-(string)name +{ + return device.name; +} + +-(string)id +{ + return device.id; +} + +-redraw +{ + [axis_view redraw]; + return self; +} + +@end diff --git a/ruamoko/qwaq/device/nameview.h b/ruamoko/qwaq/device/nameview.h new file mode 100644 index 000000000..8ba0a8df8 --- /dev/null +++ b/ruamoko/qwaq/device/nameview.h @@ -0,0 +1,13 @@ +#ifndef __qwaq_debugger_nameview_h +#define __qwaq_debugger_nameview_h + +#include "ruamoko/qwaq/debugger/views/defview.h" + +@interface NameView : View +{ + string name; +} ++(NameView *)withName:(string)name; +@end + +#endif//__qwaq_debugger_nameview_h diff --git a/ruamoko/qwaq/device/nameview.r b/ruamoko/qwaq/device/nameview.r new file mode 100644 index 000000000..5568870e6 --- /dev/null +++ b/ruamoko/qwaq/device/nameview.r @@ -0,0 +1,33 @@ +#include +#include "ruamoko/qwaq/device/nameview.h" + +@implementation NameView + +-initWithName:(string)name +{ + if (!(self = [super init])) { + return nil; + } + self.name = name; + return self; +} + +-(void)dealloc +{ + str_free (name); + [super dealloc]; +} + ++(NameView *)withName:(string)name +{ + return [[[self alloc] initWithName:name] autorelease]; +} + +-draw +{ + [super draw]; + [self mvaddstr:{0, 0}, str_mid (name, 0, xlen)]; + return self; +} + +@end diff --git a/ruamoko/qwaq/editor/editbuffer.h b/ruamoko/qwaq/editor/editbuffer.h new file mode 100644 index 000000000..08a13d213 --- /dev/null +++ b/ruamoko/qwaq/editor/editbuffer.h @@ -0,0 +1,89 @@ +#ifndef __qwaq_editor_editbuffer_h +#define __qwaq_editor_editbuffer_h + +#ifdef __QFCC__ +#include + +//FIXME finish unsigned in qfcc +#ifndef umax +#define umax 0x7fffffff +#endif + +#endif//__QFCC__ + +typedef struct eb_sel_s { + unsigned start; + unsigned length; +} eb_sel_t; + +typedef struct eb_color_s { + int normal; + int selected; +} eb_color_t; + +#ifdef __QFCC__ +@interface EditBuffer : Object +{ + struct edit_buffer_s *buffer; +} ++(EditBuffer *)buffer; ++(EditBuffer *)withFile:(string)filename; +-init; +-initWithFile: (string) filename; +- (unsigned) nextChar: (unsigned) charPtr; +- (unsigned) prevChar: (unsigned) charPtr; +- (unsigned) nextNonSpace: (unsigned) charPtr; +- (unsigned) prevNonSpace: (unsigned) charPtr; +- (int) isWord: (unsigned) charPtr; +- (unsigned) nextWord: (unsigned) wordPtr; +- (unsigned) prevWord: (unsigned) wordPtr; +- (unsigned) nextLine: (unsigned) linePtr; +- (unsigned) prevLine: (unsigned) linePtr; +- (unsigned) nextLine: (unsigned) linePtr : (unsigned) count; +- (unsigned) prevLine: (unsigned) linePtr : (unsigned) count; + +- (unsigned) charPos: (unsigned) linePtr at: (unsigned) target; +- (unsigned) charPtr: (unsigned) linePtr at: (unsigned) target; + +- (eb_sel_t) getWord: (unsigned) wordPtr; +- (eb_sel_t) getLine: (unsigned) linePtr; +- (unsigned) getBOL: (unsigned) linePtr; +- (unsigned) getEOL: (unsigned) linePtr; +- (unsigned) getBOT; +- (unsigned) getEOT; + +- (string) readString: (eb_sel_t) selection; +- (int) getChar: (unsigned) charPtr; +- (void) putChar: (int) char at:(unsigned) charPtr; +- (void) insertChar: (int) char at:(unsigned) charPtr; + +- (unsigned) countLines: (eb_sel_t) selection; +- (eb_sel_t) search: (eb_sel_t) selection + for: (string) str + direction: (int) dir; +- (eb_sel_t) isearch: (eb_sel_t) selection + for: (string) str + direction: (int) dir; +- (unsigned) formatLine: (unsigned) linePtr + from: (unsigned) xpos + into: (int *) buf + width: (unsigned) length + highlight: (eb_sel_t) selection + colors: (eb_color_t) colors; + +- (BOOL) modified; +- (unsigned) textSize; +- (int) saveFile: (string) fileName; +@end +#else//__QFCC__ + +#include "QF/progs/pr_obj.h" + +typedef struct qwaq_editbuffer_s { + pr_id_t isa; + pr_ptr_t buffer; +} qwaq_editbuffer_t; + +#endif//!__QFCC__ + +#endif//__qwaq_editor_editbuffer_h diff --git a/ruamoko/qwaq/editor/editbuffer.r b/ruamoko/qwaq/editor/editbuffer.r new file mode 100644 index 000000000..779e22077 --- /dev/null +++ b/ruamoko/qwaq/editor/editbuffer.r @@ -0,0 +1,63 @@ +#include "ruamoko/qwaq/editor/editbuffer.h" + +@implementation EditBuffer ++(EditBuffer *)buffer +{ + return [[[self alloc] init] autorelease]; +} + ++(EditBuffer *)withFile:(string)filename +{ + return [[[self alloc] initWithFile:filename] autorelease]; +} + +- init = #0; +- initWithFile: (string) filename = #0; +- (void) dealloc = #0; +- (unsigned) nextChar: (unsigned) charPtr = #0; +- (unsigned) prevChar: (unsigned) charPtr = #0; +- (unsigned) nextNonSpace: (unsigned) charPtr = #0; +- (unsigned) prevNonSpace: (unsigned) charPtr = #0; +- (int) isWord: (unsigned) charPtr = #0; +- (unsigned) nextWord: (unsigned) wordPtr = #0; +- (unsigned) prevWord: (unsigned) wordPtr = #0; +- (unsigned) nextLine: (unsigned) linePtr = #0; +- (unsigned) prevLine: (unsigned) linePtr = #0; +- (unsigned) nextLine: (unsigned) linePtr : (unsigned) count = #0; +- (unsigned) prevLine: (unsigned) linePtr : (unsigned) count = #0; + +- (unsigned) charPos: (unsigned) linePtr + at: (unsigned) target = #0; +- (unsigned) charPtr: (unsigned) linePtr + at: (unsigned) target = #0; + +- (eb_sel_t) getWord: (unsigned) wordPtr = #0; +- (eb_sel_t) getLine: (unsigned) linePtr = #0; +- (unsigned) getBOL: (unsigned) linePtr = #0; +- (unsigned) getEOL: (unsigned) linePtr = #0; +- (unsigned) getBOT = #0; +- (unsigned) getEOT = #0; + +- (string) readString: (eb_sel_t) selection = #0; +- (int) getChar: (unsigned) charPtr = #0; +- (void) putChar: (int) char at:(unsigned) charPtr = #0; +- (void) insertChar: (int) char at:(unsigned) charPtr = #0; + +- (unsigned) countLines: (eb_sel_t) selection = #0; +- (eb_sel_t) search: (eb_sel_t) selection + for: (string) str + direction: (int) dir = #0; +- (eb_sel_t) isearch: (eb_sel_t) selection + for: (string) str + direction: (int) dir = #0; +- (unsigned) formatLine: (unsigned) linePtr + from: (unsigned) xpos + into: (int *) buf + width: (unsigned) length + highlight: (eb_sel_t) selection + colors: (eb_color_t) colors = #0; + +- (BOOL) modified = #0; +- (unsigned) textSize = #0; +- (int) saveFile: (string) fileName = #0; +@end diff --git a/ruamoko/qwaq/editor/editor.h b/ruamoko/qwaq/editor/editor.h new file mode 100644 index 000000000..8fda028c6 --- /dev/null +++ b/ruamoko/qwaq/editor/editor.h @@ -0,0 +1,65 @@ +#ifndef __qwaq_editor_editor_h +#define __qwaq_editor_editor_h + +#include "ruamoko/qwaq/editor/editbuffer.h" +#include "ruamoko/qwaq/editor/status.h" +#include "ruamoko/qwaq/ui/view.h" + +@class Editor; +@class EditBuffer; +@class ListenerGroup; +@class EditStatus; + +@interface Editor : View +{ + EditBuffer *buffer; + EditStatus *status; + DrawBuffer *linebuffer; + eb_sel_t selection; + unsigned base_index; // top left corner + unsigned line_index; // current line + unsigned char_index; // current character + unsigned old_cind; // previous character + Point base; + Point cursor; + unsigned line_count; + string filename; + string filepath; + int modified; + CursorMode cursorMode; + + int virtualInsert; + int cursorThroughTabs; +} ++(Editor *)withRect:(Rect)rect file:(string)filename; ++(Editor *)withRect:(Rect)rect file:(string)filename path:(string)filepath; +-(string)filename; +-(string)filepath; +-(Point)cursor; +-setStatusView:(EditStatus *)status; +-scrollLeft:(unsigned) count; +-scrollRight:(unsigned) count; +-pageUp; +-pageDown; +-linesUp; +-linesDown; +-charUp; +-charDown; +-charLeft; +-charRight; +-wordLeft; +-wordRight; +-moveBOT; +-moveEOT; +-moveBOS; +-moveEOS; +-moveBOL; +-moveEOL; + +-recenter:(int) force; +-gotoLine:(unsigned) line; +-highlightLine; +-(string)getWordAt:(Point) pos; // view relative coordinates +@end + +#endif//__qwaq_editor_editor_h diff --git a/ruamoko/qwaq/editor/editor.r b/ruamoko/qwaq/editor/editor.r new file mode 100644 index 000000000..317d5ae21 --- /dev/null +++ b/ruamoko/qwaq/editor/editor.r @@ -0,0 +1,640 @@ +#include +#include +#include "ruamoko/qwaq/qwaq-app.h" +#include "ruamoko/qwaq/editor/editor.h" +#include "ruamoko/qwaq/ui/listener.h" +#include "ruamoko/qwaq/ui/scrollbar.h" + +@implementation Editor + +static int +center (unsigned v, int len) +{ + return v > len / 2 ? v / 2 : 0; +} + +static void +trackCursor (Editor *self) +{ + unsigned cx = [self.buffer charPos:self.line_index at:self.char_index]; + if (self.cursor.x != cx) { + if (self.char_index < [self.buffer getEOT]) { + int c = [self.buffer getChar:self.char_index]; + } + } +} + +-initWithRect:(Rect) rect file:(string) filename path:(string) filepath +{ + if (!(self = [super initWithRect: rect])) { + return nil; + } + self.filename = str_hold (filename); + if (filepath != filename) { + self.filepath = str_hold (filepath); + } else { + self.filepath = filename; + } + buffer = [[EditBuffer withFile:filepath] retain]; + line_count = [buffer countLines: {0, [buffer textSize]}]; + linebuffer = [[DrawBuffer buffer: { xlen, 1 }] retain]; + growMode = gfGrowHi; + options = ofCanFocus | ofRelativeEvents; + [onViewScrolled addListener:self :@selector(onScroll:)]; + [self setCursorVisible:1]; + + return self; +} + ++(Editor *)withRect:(Rect)rect file:(string)filename +{ + return [[[self alloc] initWithRect:rect + file:filename + path:filename] autorelease]; +} + ++(Editor *)withRect:(Rect)rect file:(string)filename path:(string)filepath +{ + return [[[self alloc] initWithRect:rect + file:filename + path:filepath] autorelease]; +} + +-(void)dealloc +{ + if (filepath != filename) { + str_free (filepath); + } + str_free (filename); + [vScrollBar release]; + [buffer release]; + [linebuffer release]; + [super dealloc]; +} + +-(string)filename +{ + return filename; +} + +-(string)filepath +{ + return filepath; +} + +-(Point)cursor +{ + return cursor; +} + +-setStatusView:(EditStatus *)status +{ + self.status = status; + [status setModified:modified]; + [status setCursorMode:cursorMode]; + [status redraw]; + return self; +} + +-trackCursor:(int)fwd +{ + unsigned tx = [buffer charPos:line_index at:char_index]; + + cursorMode &= ~cmVInsert; + if (tx != cursor.x) { + if (char_index < [buffer getEOT]) { + int c = [buffer getChar:char_index]; + + if (virtualInsert) { + if (c != '\t' || cursorThroughTabs) { + cursorMode |= cmVInsert; + goto done; + } + } + if (c == '\t' && fwd) { + tx = [buffer charPos:line_index at:++char_index]; + } + } else if (virtualInsert) { + cursorMode |= cmVInsert; + goto done; + } + cursor.x = tx; + } +done: + [status setCursorMode:cursorMode]; + [status redraw]; + return self; +} + +-draw +{ + [super draw]; + unsigned lind = base_index; + int *lbuf = [linebuffer buffer]; + for (int y = 0; y < ylen; y++) { + lind = [buffer formatLine:lind from:base.x into:lbuf width:xlen + highlight:selection colors: {color_palette[047], + color_palette[007]}]; + [textContext blitFromBuffer: linebuffer to: {xpos, ypos + y} + from: [linebuffer rect]]; + } + return self; +} + +-resize: (Extent) delta +{ + [super resize: delta]; + [linebuffer resizeTo: {xlen, 1}]; + return self; +} + +static int +handleEvent (Editor *self, qwaq_event_t *event) +{ + if (event.what & qe_mouse) { + if (event.what == qe_mouseclick) { + if (event.mouse.buttons & (1 << 3)) { + [self.vScrollBar page:1 dir:0]; + return 1; + } + if (event.mouse.buttons & (1 << 4)) { + [self.vScrollBar page:1 dir:1]; + return 1; + } + if (event.mouse.buttons & (1 << 5)) { + [self scrollLeft: 1]; + return 1; + } + if (event.mouse.buttons & (1 << 6)) { + [self scrollRight: 1]; + return 1; + } + } + } else if (event.what == qe_keydown) { + switch (event.key.code) { + case QFK_PAGEUP: + if (event.key.shift & qe_control) { + [self moveBOT]; + } else { + [self pageUp]; + } + return 1; + case QFK_PAGEDOWN: + if (event.key.shift & qe_control) { + [self moveEOT]; + } else { + [self pageDown]; + } + return 1; + case QFK_UP: + if (event.key.shift & qe_control) { + [self linesUp]; + } else { + [self charUp]; + } + return 1; + case QFK_DOWN: + if (event.key.shift & qe_control) { + [self linesDown]; + } else { + [self charDown]; + } + return 1; + case QFK_LEFT: + if (event.key.shift & qe_control) { + [self wordLeft]; + } else { + [self charLeft]; + } + return 1; + case QFK_RIGHT: + if (event.key.shift & qe_control) { + [self wordRight]; + } else { + [self charRight]; + } + return 1; + case QFK_HOME: + if (event.key.shift & qe_control) { + [self moveBOS]; + } else { + [self moveBOL]; + } + return 1; + case QFK_END: + if (event.key.shift & qe_control) { + [self moveEOS]; + } else { + [self moveEOL]; + } + return 1; + } + } + return 0; +} + +-handleEvent:(qwaq_event_t *) event +{ + [super handleEvent: event]; + + if (handleEvent (self, event)) { + event.what = qe_none; + } + return self; +} + +-scrollLeft:(unsigned) count +{ + if (base.x > count) { + base.x -= count; + } else { + base.x = 0; + } + [self redraw]; + [self moveCursor: {cursor.x - base.x, cursor.y - base.y}]; + return self; +} + +-scrollRight:(unsigned) count +{ + if (1024 - base.x > count) { + base.x += count; + } else { + base.x = 1024; + } + [self redraw]; + [self moveCursor: {cursor.x - base.x, cursor.y - base.y}]; + return self; +} + +-scrollTo:(unsigned)target +{ + if (target > base.y) { + base_index = [buffer nextLine:base_index :target - base.y]; + } else if (target < base.y) { + base_index = [buffer prevLine:base_index :base.y - target]; + } + base.y = target; + [vScrollBar setIndex:base.y]; + [self moveCursor: {cursor.x - base.x, cursor.y - base.y}]; + return self; +} + +-pageUp +{ + [self recenter:0]; + unsigned count = cursor.y; + + if (count > ylen) { + count = ylen; + } + if (count) { + cursor.y -= count; + [vScrollBar setIndex:[vScrollBar index] - count]; + line_index = [buffer prevLine:line_index :count]; + char_index = [buffer charPtr:line_index at:cursor.x]; + [self trackCursor:1]; + + [self moveCursor: {cursor.x - base.x, cursor.y - base.y}]; + } + return self; +} + +-pageDown +{ + [self recenter:0]; + unsigned count = line_count - cursor.y; + + if (count > ylen) { + count = ylen; + } + if (count) { + cursor.y += count; + [vScrollBar setIndex:[vScrollBar index] + count]; + line_index = [buffer nextLine:line_index :count]; + char_index = [buffer charPtr:line_index at:cursor.x]; + [self trackCursor:1]; + + [self moveCursor: {cursor.x - base.x, cursor.y - base.y}]; + } + return self; +} + +-linesUp +{ + while (line_index > 0) { + line_index = [buffer prevLine: line_index]; + cursor.y--; + unsigned p = [buffer nextNonSpace: line_index]; + if ([buffer getChar:p] == '\n') { + continue; + } + int x = [buffer charPos:line_index at:p]; + if (x <= cursor.x) { + break; + } + } + char_index = [buffer charPtr:line_index at:cursor.x]; + + [self recenter:0]; + [self trackCursor:1]; + [self moveCursor: {cursor.x - base.x, cursor.y - base.y}]; + + return self; +} + +-linesDown +{ + unsigned end = [buffer getEOT]; + unsigned last = [buffer getBOL:end]; + while (line_index < last) { + line_index = [buffer nextLine: line_index]; + cursor.y++; + unsigned p = [buffer nextNonSpace: line_index]; + if (p >= end || [buffer getChar:p] == '\n') { + continue; + } + int x = [buffer charPos:line_index at:p]; + if (x <= cursor.x) { + break; + } + } + char_index = [buffer charPtr:line_index at:cursor.x]; + + [self recenter:0]; + [self trackCursor:1]; + [self moveCursor: {cursor.x - base.x, cursor.y - base.y}]; + + return self; +} + +-charUp +{ + [self recenter:0]; + if (cursor.y < 1) { + return self; + } + cursor.y--; + line_index = [buffer prevLine:line_index :1]; + char_index = [buffer charPtr:line_index at:cursor.x]; + [self trackCursor:1]; + + if (base.y > cursor.y) { + [vScrollBar setIndex:cursor.y]; + } + [self moveCursor: {cursor.x - base.x, cursor.y - base.y}]; + return self; +} + +-charDown +{ + [self recenter:0]; + if (cursor.y >= line_count) { + return self; + } + cursor.y++; + line_index = [buffer nextLine:line_index :1]; + char_index = [buffer charPtr:line_index at:cursor.x]; + [self trackCursor:1]; + + if (base.y + ylen - 1 < cursor.y) { + [vScrollBar setIndex:cursor.y + 1 - ylen]; + } + [self moveCursor: {cursor.x - base.x, cursor.y - base.y}]; + return self; +} + +-charLeft +{ + [self recenter:0]; + if (cursor.x < 1) { + return self; + } + cursor.x--; + char_index = [buffer charPtr:line_index at:cursor.x]; + [self trackCursor:0]; + + if (base.x > cursor.x) { + [hScrollBar setIndex:cursor.x]; + } + [self moveCursor: {cursor.x - base.x, cursor.y - base.y}]; + return self; +} + +-charRight +{ + [self recenter:0]; + // FIXME handle horizontal scrolling and how to deal with max scroll + cursor.x++; + char_index = [buffer charPtr:line_index at:cursor.x]; + [self trackCursor:1]; + + if (base.x + xlen - 1 < cursor.x) { + [hScrollBar setIndex:cursor.x + 1 - xlen]; + } + if (base.x + xlen - 1 < cursor.x) { + cursor.x = base.x + xlen - 1; + } + [self moveCursor: {cursor.x - base.x, cursor.y - base.y}]; + return self; +} + +-wordLeft +{ + [self recenter:0]; + //unsigned oc = char_index; + Point b = base; + + do { + if (char_index && char_index == line_index) { + line_index = [buffer prevLine: line_index]; + char_index = [buffer getEOL: line_index]; + if (--cursor.y < b.y) { + b.y = cursor.y; + } + } + char_index = [buffer prevWord:char_index]; + } while (char_index && char_index < [buffer getEOT] + && ![buffer isWord:char_index]); + + cursor.x = [buffer charPos:line_index at:char_index]; + if (cursor.x < b.x) { + b.x = cursor.x; + } else if (cursor.x >= b.x) { + b.x = center (b.x, xlen); + } + base.x = b.x; + [vScrollBar setIndex:b.y]; + trackCursor (self); + [self moveCursor: {cursor.x - base.x, cursor.y - base.y}]; + + return self; +} + +-wordRight +{ + [self recenter:0]; + //unsigned oc = char_index; + Point b = base; + + if (char_index >= [buffer getEOT]) { + return self; + } + if ([buffer getChar:char_index] == '\n') { + while ([buffer getChar:char_index] == '\n') { + char_index = line_index = [buffer nextLine:line_index]; + if (++cursor.y >= b.y + ylen) { + b.y = cursor.y + 1 - ylen; + } + if (char_index >= [buffer getEOT]) { + break; + } + if (![buffer isWord:char_index]) { + char_index = [buffer nextWord: char_index]; + } + } + } else { + char_index = [buffer nextWord: char_index]; + } + + cursor.x = [buffer charPos:line_index at:char_index]; + if (cursor.x < b.x) { + b.x = cursor.x; + } else if (cursor.x >= b.x) { + b.x = center (b.x, xlen); + } + base.x = b.x; + [vScrollBar setIndex:b.y]; + trackCursor (self); + [self moveCursor: {cursor.x - base.x, cursor.y - base.y}]; + + return self; +} + +-moveBOT +{ + line_index = base_index = char_index = 0;; + cursor = nil; + base.x = 0; + [vScrollBar setIndex:0]; + [self trackCursor:1]; + [self moveCursor: {cursor.x - base.x, cursor.y - base.y}]; + return self; +} + +-moveEOT +{ + char_index = [buffer getEOT]; + line_index = [buffer getBOL:char_index]; + cursor.x = [buffer charPos:line_index at:char_index]; + cursor.y = line_count; + [self recenter:0]; + [self trackCursor:1]; + [self moveCursor: {cursor.x - base.x, cursor.y - base.y}]; + return self; +} + +-moveBOS +{ + line_index = base_index; + cursor.y = base.y; + char_index = [buffer charPtr:line_index at:cursor.x]; + [self trackCursor:1]; + [self moveCursor: {cursor.x - base.x, cursor.y - base.y}]; + return self; +} + +-moveEOS +{ + unsigned count = line_count - base.y; + if (count > ylen - 1) { + count = ylen - 1; + } + line_index = [buffer nextLine:base_index :count]; + cursor.y = base.y + count; + char_index = [buffer charPtr:line_index at:cursor.x]; + [self trackCursor:1]; + [self moveCursor: {cursor.x - base.x, cursor.y - base.y}]; + return self; +} + +-moveBOL +{ + char_index = line_index; + cursor.x = 0; + base.x = 0; + [self recenter:0]; + [self moveCursor: {cursor.x - base.x, cursor.y - base.y}]; + return self; +} + +-moveEOL +{ + char_index = [buffer getEOL:line_index]; + cursor.x = [buffer charPos:line_index at:char_index]; + [self recenter:0]; + [self moveCursor: {cursor.x - base.x, cursor.y - base.y}]; + return self; +} + +-(void)onScroll:(id)sender +{ + base.x = scroll.x; + if (base.y != scroll.y) { + [self scrollTo:scroll.y]; + } +} + +-setVerticalScrollBar:(ScrollBar *)scrollbar +{ + [super setVerticalScrollBar:scrollbar]; + [vScrollBar setRange:line_count]; + [vScrollBar setPageStep: ylen]; + return self; +} + +-recenter:(int) force +{ + if (force || cursor.y < base.y || cursor.y - base.y >= ylen) { + [self scrollTo: center (cursor.y, ylen)]; + } + return self; +} + +-gotoLine:(unsigned) line +{ + if (line > cursor.y) { + line_index = [buffer nextLine:line_index :line - cursor.y]; + } else if (line < cursor.y) { + line_index = [buffer prevLine:line_index :cursor.y - line]; + } + cursor.y = line; + char_index = [buffer charPtr:line_index at:cursor.x]; + [self recenter:0]; + [self moveCursor: {cursor.x - base.x, cursor.y - base.y}]; + return self; +} + +-highlightLine +{ + selection.start = line_index; + selection.length = [buffer nextLine: line_index] - line_index; + if (!selection.length) { + selection.length = [buffer getEOL: line_index] - line_index; + } + return self; +} + +-(string)getWordAt:(Point) pos +{ + if (pos.x < 0 || pos.y < 0 || pos.x >= xlen || pos.y >= ylen) { + return nil; + } + pos.x += base.x; + unsigned lind = [buffer nextLine:base_index :pos.y]; + unsigned cind = [buffer charPtr:lind at:pos.x]; + eb_sel_t word = [buffer getWord: cind]; + return [buffer readString:word]; +} + +@end diff --git a/ruamoko/qwaq/editor/status.h b/ruamoko/qwaq/editor/status.h new file mode 100644 index 000000000..0c8dec1eb --- /dev/null +++ b/ruamoko/qwaq/editor/status.h @@ -0,0 +1,23 @@ +#ifndef __qwaq_editor_status_h +#define __qwaq_editor_status_h + +#include "ruamoko/qwaq/ui/view.h" + +typedef enum { + cmInsert, + cmTypeOver, + cmVInsert, + cmVTypeOver, +} CursorMode; + +@interface EditStatus : View +{ + CursorMode cursorMode; + int modified; +} ++(EditStatus *)withRect:(Rect)rect; +-setCursorMode:(CursorMode)mode; +-setModified:(int)modified; +@end + +#endif//__qwaq_editor_status_h diff --git a/ruamoko/qwaq/editor/status.r b/ruamoko/qwaq/editor/status.r new file mode 100644 index 000000000..1f9d1f09d --- /dev/null +++ b/ruamoko/qwaq/editor/status.r @@ -0,0 +1,43 @@ +#include "ruamoko/qwaq/editor/status.h" + +static int cursor_modes[] = { 'I', 'O', 'i', 'o' }; + +@implementation EditStatus + +-initWithRect:(Rect)rect +{ + if (!(self = [super initWithRect: rect])) { + return nil; + } + growMode = gfGrowNone; + return self; +} + ++(EditStatus *)withRect:(Rect)rect +{ + return [[self alloc] initWithRect:rect]; +} + +-setCursorMode:(CursorMode)mode +{ + cursorMode = mode; + return self; +} + +-setModified:(int)modified +{ + self.modified = modified; + return self; +} + +-draw +{ + [super draw]; + if (modified) { + [self mvaddch:{0, 0}, '*']; + } + [self mvaddch:{1, 0}, cursor_modes[cursorMode]]; + return self; +} + +@end diff --git a/tools/qfcc/test/old/gcd.r b/ruamoko/qwaq/gcd.r similarity index 95% rename from tools/qfcc/test/old/gcd.r rename to ruamoko/qwaq/gcd.r index bfa3a5e84..a01cce830 100644 --- a/tools/qfcc/test/old/gcd.r +++ b/ruamoko/qwaq/gcd.r @@ -13,7 +13,6 @@ gcd (int a, int b) int main (int argc, string *argv) { - traceon (); x = 130; y = 120; printf ("%d\n", gcd (x, y)); diff --git a/ruamoko/qwaq/input-app.h b/ruamoko/qwaq/input-app.h new file mode 100644 index 000000000..7947b13bb --- /dev/null +++ b/ruamoko/qwaq/input-app.h @@ -0,0 +1,38 @@ +#ifndef __qwaq_app_h +#define __qwaq_app_h + +#include + +#include "ruamoko/qwaq/ui/event.h" +#include "ruamoko/qwaq/ui/rect.h" + +@class Array; +@class Group; +@class TextContext; +@class View; + +extern int color_palette[64]; + +@interface InputApplication: Object +{ + qwaq_event_t event; + qwaq_command endState; + + Group *objects; + + TextContext *screen; + Extent screenSize; + int autocount; + + Array *devices; +} +-(Extent)size; +-(TextContext *)screen; +-addView:(View *)view; +-removeView:(View *)view; +-run; +@end + +extern InputApplication *application; + +#endif//__qwaq_app_h diff --git a/ruamoko/qwaq/input-app.r b/ruamoko/qwaq/input-app.r new file mode 100644 index 000000000..3aa3b111b --- /dev/null +++ b/ruamoko/qwaq/input-app.r @@ -0,0 +1,206 @@ +int fence; + +#include +#include +#include + +#include "ruamoko/qwaq/ui/color.h" +#include "ruamoko/qwaq/ui/curses.h" +#include "ruamoko/qwaq/ui/group.h" +#include "ruamoko/qwaq/ui/view.h" +#include "ruamoko/qwaq/device/device.h" +#include "ruamoko/qwaq/qwaq-input.h" +#include "ruamoko/qwaq/input-app.h" + +string graph_up = " ⢀⢠⢰⢸⡀⣀⣠⣰⣸⡄⣄⣤⣴⣼⡆⣆⣦⣶⣾⡇⣇⣧⣷⣿"; +string graph_down = " ⠈⠘⠸⢸â â ‰â ™â ¹â¢¹â ƒâ ‹â ›â »â¢»â ‡â â Ÿâ ¿â¢¿â¡‡â¡â¡Ÿâ¡¿â£¿"; + +int color_palette[64]; + +static AutoreleasePool *autorelease_pool; +static void +arp_start (void) +{ + autorelease_pool = [[AutoreleasePool alloc] init]; +} + +static void +arp_end (void) +{ + [autorelease_pool release]; + autorelease_pool = nil; +} + +@implementation InputApplication ++app +{ + return [[[self alloc] init] autorelease]; +} + +-init +{ + if (!(self = [super init])) { + return nil; + } + + init_input (); + initialize (); + for (int i = 1; i < 64; i++) { + init_pair (i, i & 0x7, i >> 3); + color_palette[i] = COLOR_PAIR (i); + } + + screen = [TextContext screen]; + screenSize = [screen size]; + objects = [[Group withContext: screen owner: nil] retain]; + + [screen bkgd: color_palette[047]]; + [screen scrollok: 1]; + [screen clear]; + wrefresh (stdscr);//FIXME + + devices = [[Array array] retain]; + + send_connected_devices (); + + return self; +} + +-(Extent)size +{ + return screenSize; +} + +-(TextContext *)screen +{ + return screen; +} + +-draw +{ + [objects draw]; + [screen refresh]; + return self; +} + +-handleEvent: (qwaq_event_t *) event +{ + switch (event.what) { + case qe_resize: + Extent delta; + delta.width = event.resize.width - screenSize.width; + delta.height = event.resize.height - screenSize.height; + + resizeterm (event.resize.width, event.resize.height); + [screen resizeTo: {event.resize.width, event.resize.height}]; + screenSize = [screen size]; + [objects resize: delta]; + [screen refresh]; + event.what = qe_none; + break; + case qe_key: + if (event.key.code == '\x18' || event.key.code == '\x11') { + endState = event.message.int_val; + event.what = qe_none; + } + break; + case qe_dev_add: + { + int devid = event.message.int_val; + qwaq_devinfo_t *dev = get_device_info (devid); + Device *device = [Device withDevice:dev id:devid]; + [devices addObject:device]; + } + event.what = qe_none; + break; + case qe_dev_rem: + for (int i = [devices count]; i-- > 0; ) { + Device *device = [devices objectAtIndex:i]; + if ([device devid] == event.message.ivector_val[0]) { + [devices removeObjectAtIndex:i]; + break; + } + } + event.what = qe_none; + break; + case qe_axis: + for (int i = [devices count]; i-- > 0; ) { + Device *device = [devices objectAtIndex:i]; + if ([device devid] == event.message.ivector_val[0]) { + [device updateAxis:event.message.ivector_val[1] + value:event.message.ivector_val[2]]; + [device redraw]; + break; + } + } + event.what = qe_none; + break; + case qe_button: + for (int i = [devices count]; i-- > 0; ) { + Device *device = [devices objectAtIndex:i]; + if ([device devid] == event.message.ivector_val[0]) { + [device updateButton:event.message.ivector_val[1] + state:event.message.ivector_val[2]]; + [device redraw]; + break; + } + } + event.what = qe_none; + break; + } + if (event.what != qe_none) { + [objects handleEvent: event]; + } + return self; +} + +-run +{ + [objects takeFocus]; + [self draw]; + do { + arp_start (); + + get_event (&event); + if (event.what != qe_none) { + [self handleEvent: &event]; + } + + arp_end (); + } while (!endState); + return self; +} + +-addView:(View *)view +{ + [objects insertSelected: view]; + [screen refresh]; + return self; +} + +-removeView:(View *)view +{ + [objects remove: view]; + [screen refresh]; + return self; +} +@end + +InputApplication *application; + +int main (int argc, string *argv) +{ + fence = 0; + //while (!fence) {} + + arp_start (); + application = [[InputApplication app] retain]; + arp_end (); + + [application run]; + [application release]; + qwaq_event_t event; + get_event (&event); // XXX need a "wait for queue idle" + + return 0; +} diff --git a/ruamoko/qwaq/qdb b/ruamoko/qwaq/qdb new file mode 100755 index 000000000..7f1ed27e2 --- /dev/null +++ b/ruamoko/qwaq/qdb @@ -0,0 +1,3 @@ +#! /bin/bash -x + +./qwaq-curses qwaq-app.dat $1 diff --git a/ruamoko/qwaq/qwaq-app.h b/ruamoko/qwaq/qwaq-app.h new file mode 100644 index 000000000..a35cee787 --- /dev/null +++ b/ruamoko/qwaq/qwaq-app.h @@ -0,0 +1,37 @@ +#ifndef __qwaq_app_h +#define __qwaq_app_h + +#include + +#include "ruamoko/qwaq/ui/event.h" +#include "ruamoko/qwaq/ui/rect.h" + +@class Array; +@class Group; +@class TextContext; +@class View; + +extern int color_palette[64]; + +@interface QwaqApplication: Object +{ + qwaq_event_t event; + qwaq_command endState; + + Group *objects; + + TextContext *screen; + Extent screenSize; + int autocount; + + Array *debuggers; +} +-(Extent)size; +-(TextContext *)screen; +-addView:(View *)view; +-run; +@end + +extern QwaqApplication *application; + +#endif//__qwaq_app_h diff --git a/ruamoko/qwaq/qwaq-app.r b/ruamoko/qwaq/qwaq-app.r new file mode 100644 index 000000000..bf3876687 --- /dev/null +++ b/ruamoko/qwaq/qwaq-app.r @@ -0,0 +1,170 @@ +int fence; + +#include +#include + +#include "ruamoko/qwaq/ui/color.h" +#include "ruamoko/qwaq/ui/curses.h" +#include "ruamoko/qwaq/ui/group.h" +#include "ruamoko/qwaq/ui/view.h" +#include "ruamoko/qwaq/debugger/debugger.h" +#include "ruamoko/qwaq/qwaq-app.h" +#include "ruamoko/qwaq/qwaq-input.h" + +void init_input (void) = #0;//FIXME + +int color_palette[64]; + +static AutoreleasePool *autorelease_pool; +static void +arp_start (void) +{ + autorelease_pool = [[AutoreleasePool alloc] init]; +} + +static void +arp_end (void) +{ + [autorelease_pool release]; + autorelease_pool = nil; +} + +@implementation QwaqApplication ++app +{ + return [[[self alloc] init] autorelease]; +} + +-init +{ + if (!(self = [super init])) { + return nil; + } + + init_input (); + initialize (); + for (int i = 1; i < 64; i++) { + init_pair (i, i & 0x7, i >> 3); + color_palette[i] = COLOR_PAIR (i); + } + + screen = [TextContext screen]; + screenSize = [screen size]; + objects = [[Group withContext: screen owner: nil] retain]; + + [screen bkgd: color_palette[047]]; + [screen scrollok: 1]; + [screen clear]; + wrefresh (stdscr);//FIXME + + debuggers = [[Array array] retain]; + return self; +} + +-(Extent)size +{ + return screenSize; +} + +-(TextContext *)screen +{ + return screen; +} + +-draw +{ + [objects draw]; + [screen refresh]; + return self; +} + +-(Debugger *)find_debugger:(qdb_target_t) target +{ + Debugger *debugger; + + for (int i = [debuggers count]; i-- > 0; ) { + debugger = [debuggers objectAtIndex: i]; + if ([debugger target].handle == target.handle) { + return debugger; + } + } + debugger = [Debugger withTarget: target]; + [debuggers addObject: debugger]; + return debugger; +} + +-handleEvent: (qwaq_event_t *) event +{ + switch (event.what) { + case qe_resize: + Extent delta; + delta.width = event.resize.width - screenSize.width; + delta.height = event.resize.height - screenSize.height; + + resizeterm (event.resize.width, event.resize.height); + [screen resizeTo: {event.resize.width, event.resize.height}]; + screenSize = [screen size]; + [objects resize: delta]; + [screen refresh]; + event.what = qe_none; + break; + case qe_key: + if (event.key.code == '\x18' || event.key.code == '\x11') { + endState = event.message.int_val; + event.what = qe_none; + } + break; + case qe_debug_event: + [[self find_debugger:{event.message.int_val}] handleDebugEvent]; + event.what = qe_none; + break; + } + if (event.what != qe_none) { + [objects handleEvent: event]; + } + return self; +} + +-run +{ + [objects takeFocus]; + [self draw]; + do { + arp_start (); + + get_event (&event); + if (event.what != qe_none) { + [self handleEvent: &event]; + } + + arp_end (); + } while (!endState); + return self; +} + +-addView:(View *)view +{ + [objects insertSelected: view]; + [screen refresh]; + return self; +} +@end + +QwaqApplication *application; + +int main (int argc, string *argv) +{ + fence = 0; + //while (!fence) {} + + arp_start (); + application = [[QwaqApplication app] retain]; + arp_end (); + + [application run]; + [application release]; + qwaq_event_t event; + get_event (&event); // XXX need a "wait for queue idle" + + return 0; +} diff --git a/ruamoko/qwaq/qwaq-input.h b/ruamoko/qwaq/qwaq-input.h new file mode 100644 index 000000000..959015a33 --- /dev/null +++ b/ruamoko/qwaq/qwaq-input.h @@ -0,0 +1,53 @@ +#ifndef __qwaq_input_h +#define __qwaq_input_h + +#include "QF/input.h" + +typedef struct qwaq_devinfo_s { + int devid; +#ifdef __QFCC__ + string name; + string id; +#else + pr_string_t name; + pr_string_t id; +#endif + int numaxes; +#ifdef __QFCC__ + in_axisinfo_t *axes; +#else + pr_ptr_t axes; +#endif + int numbuttons; +#ifdef __QFCC__ + in_axisinfo_t *buttons; +#else + pr_ptr_t buttons; +#endif +} qwaq_devinfo_t; + +#ifdef __QFCC__ + +#include + +#include "ruamoko/qwaq/ui/event.h" +#include "ruamoko/qwaq/ui/rect.h" + + +@class Array; +@class Group; +@class TextContext; +@class View; + +@interface QwaqInput: Object +{ +} +@end + +void init_input (void); +void send_connected_devices (void); +qwaq_devinfo_t *get_device_info (int devid); + +#endif + +#endif//__qwaq_input_h diff --git a/ruamoko/qwaq/qwaq-input.r b/ruamoko/qwaq/qwaq-input.r new file mode 100644 index 000000000..abea04cf5 --- /dev/null +++ b/ruamoko/qwaq/qwaq-input.r @@ -0,0 +1,5 @@ +#include "ruamoko/qwaq/qwaq-input.h" + +void init_input (void) = #0; +void send_connected_devices (void) = #0; +qwaq_devinfo_t *get_device_info (int devid) = #0; diff --git a/ruamoko/qwaq/qwaq.h b/ruamoko/qwaq/qwaq.h new file mode 100644 index 000000000..1399bb6f8 --- /dev/null +++ b/ruamoko/qwaq/qwaq.h @@ -0,0 +1,37 @@ +#ifndef __qwaq_h +#define __qwaq_h + +#include + +#include "QF/darray.h" +#include "QF/progs.h" +#include "QF/sys.h" + +typedef void (*progsinit_f) (progs_t *pr); + +typedef struct qwaq_thread_s { + pthread_t thread_id; + int return_code; + struct DARRAY_TYPE (const char *) args; + sys_printf_t sys_printf; + progsinit_f*progsinit; + progs_t *pr; + int rua_security; + struct memhunk_s *hunk; + struct hashctx_s *hashctx; + pr_func_t main_func; + void *data; +} qwaq_thread_t; + +typedef struct qwaq_thread_set_s DARRAY_TYPE(qwaq_thread_t *) qwaq_thread_set_t; + +void BI_Graphics_Init (progs_t *pr); +void BI_Curses_Init (progs_t *pr); +void BI_TermInput_Init (progs_t *pr); +void QWAQ_EditBuffer_Init (progs_t *pr); +extern struct cbuf_s *qwaq_cbuf; +qwaq_thread_t *create_thread (void *(*thread_func) (qwaq_thread_t *), void *); + +int qwaq_init_threads (qwaq_thread_set_t *thread_data); + +#endif//__qwaq_h diff --git a/ruamoko/qwaq/threading.h b/ruamoko/qwaq/threading.h new file mode 100644 index 000000000..867ca3fc1 --- /dev/null +++ b/ruamoko/qwaq/threading.h @@ -0,0 +1,39 @@ +#ifndef __qwaq_threading_h +#define __qwaq_threading_h + +#ifndef __QFCC__ + +#include "QF/dstring.h" +#include "QF/ringbuffer.h" + +#define QUEUE_SIZE 16 +#define STRING_ID_QUEUE_SIZE 8 +#define COMMAND_QUEUE_SIZE 1280 + +typedef struct rwcond_s { + pthread_cond_t rcond; + pthread_cond_t wcond; + pthread_mutex_t mut; +} rwcond_t; + +typedef struct qwaq_pipe_s { + rwcond_t pipe_cond; + RING_BUFFER (int, COMMAND_QUEUE_SIZE) pipe; + + rwcond_t string_id_cond; + RING_BUFFER (int, STRING_ID_QUEUE_SIZE + 1) string_ids; + dstring_t strings[STRING_ID_QUEUE_SIZE]; +} qwaq_pipe_t; + +void qwaq_pipe_submit (qwaq_pipe_t *pipe, const int *data, unsigned len); +void qwaq_pipe_receive (qwaq_pipe_t *pipe, int *data, int id, unsigned len); +int qwaq_pipe_acquire_string (qwaq_pipe_t *pipe); +void qwaq_pipe_release_string (qwaq_pipe_t *pipe, int string_id); + +void qwaq_init_pipe (qwaq_pipe_t *pipe); +void qwaq_init_timeout (struct timespec *timeout, int64_t time); +void qwaq_init_cond (rwcond_t *cond); + +#endif + +#endif//__qwaq_threading_h diff --git a/ruamoko/qwaq/ui/button.h b/ruamoko/qwaq/ui/button.h new file mode 100644 index 000000000..7fc2e9716 --- /dev/null +++ b/ruamoko/qwaq/ui/button.h @@ -0,0 +1,40 @@ +#ifndef __qwaq_ui_button_h +#define __qwaq_ui_button_h + +#include "ruamoko/qwaq/ui/draw.h" +#include "ruamoko/qwaq/ui/view.h" + +@class ListenerGroup; + +@interface Button : View +{ + DrawBuffer *icon[2]; + int pressed; + int click; + Point dragBase; + Point dragPos; + ListenerGroup *onPress; + ListenerGroup *onRelease; + ListenerGroup *onClick; + ListenerGroup *onDrag; + ListenerGroup *onAuto; + ListenerGroup *onHover; +} ++(Button *)withPos: (Point) pos releasedIcon: (DrawBuffer *) released + pressedIcon: (DrawBuffer *) pressed; ++(Button *)withRect: (Rect) rect; // invisible button +-initWithPos: (Point) pos releasedIcon: (DrawBuffer *) released + pressedIcon: (DrawBuffer *) pressed; +-initWithRect: (Rect) rect; // invisible button +-(ListenerGroup *) onPress; +-(ListenerGroup *) onRelease; +-(ListenerGroup *) onClick; +-(ListenerGroup *) onDrag; +-(ListenerGroup *) onAuto; +-(ListenerGroup *) onHover; + +- (int) click; +- (Point) delta; +@end + +#endif//__qwaq_ui_button_h diff --git a/ruamoko/qwaq/ui/button.r b/ruamoko/qwaq/ui/button.r new file mode 100644 index 000000000..7f42fb3e4 --- /dev/null +++ b/ruamoko/qwaq/ui/button.r @@ -0,0 +1,180 @@ +#include "ruamoko/qwaq/ui/button.h" +#include "ruamoko/qwaq/ui/listener.h" + +@implementation Button + ++(Button *)withPos:(Point)pos releasedIcon:(DrawBuffer *)released + pressedIcon:(DrawBuffer *)pressed +{ + return [[[self alloc] initWithPos:pos + releasedIcon:released + pressedIcon:pressed] autorelease]; +} + ++(Button *)withRect:(Rect)rect +{ + return [[[self alloc] initWithRect:rect] autorelease]; +} + +-initWithPos: (Point) pos releasedIcon: (DrawBuffer *) released + pressedIcon: (DrawBuffer *) pressed +{ + Extent size = mergeExtents ([released size], [pressed size]); + + if (!(self = [super initWithRect: {pos, size}])) { + return nil; + } + icon[0] = [released retain]; + icon[1] = [pressed retain]; + onPress = [[ListenerGroup listener] retain]; + onRelease = [[ListenerGroup listener] retain]; + onClick = [[ListenerGroup listener] retain]; + onDrag = [[ListenerGroup listener] retain]; + onAuto = [[ListenerGroup listener] retain]; + onHover = [[ListenerGroup listener] retain]; + return self; +} + +-initWithRect: (Rect) rect +{ + if (!(self = [super initWithRect: rect])) { + return nil; + } + icon[0] = nil; + icon[1] = nil; + onPress = [[ListenerGroup listener] retain]; + onRelease = [[ListenerGroup listener] retain]; + onClick = [[ListenerGroup listener] retain]; + onDrag = [[ListenerGroup listener] retain]; + onAuto = [[ListenerGroup listener] retain]; + onHover = [[ListenerGroup listener] retain]; + return self; +} + +-(void)dealloc +{ + [icon[0] release]; + [icon[1] release]; + [onPress release]; + [onRelease release]; + [onClick release]; + [onDrag release]; + [onAuto release]; + [onHover release]; + [super dealloc]; +} + +-draw +{ + [super draw]; + if (icon[pressed]) { + [textContext blitFromBuffer: icon[pressed] + to: pos + from: [icon[pressed] rect]]; + } + return self; +} + +-handleEvent: (qwaq_event_t *) event +{ + ListenerGroup *action = nil; + + [super handleEvent: event]; + + if (event.what & qe_mouse) { + switch ((qwaq_mouse_event) (event.what & qe_mouse)) { + case qe_mousedown: + pressed = 1; + click = 0; + dragBase = {event.mouse.x, event.mouse.y}; + action = onPress; + [self grabMouse]; + [self redraw]; + break; + case qe_mouseup: + pressed = 0; + click = 0; + action = onRelease; + [self releaseMouse]; + if ([self containsPoint: {event.mouse.x, event.mouse.y}]) { + [onClick respond: self]; + } + [self redraw]; + break; + case qe_mouseclick: + action = onClick; + click = event.mouse.click; + break; + case qe_mousemove: + click = 0; + if (pressed) { + dragPos = {event.mouse.x, event.mouse.y}; + action = onDrag; + } + break; + case qe_mouseauto: + if (pressed + && [self containsPoint: {event.mouse.x, event.mouse.y}]) { + click = 0; + action = onAuto; + } + break; + } + if (action) { + [action respond: self]; + } + event.what = qe_none; + } + return self; +} + +-(ListenerGroup *) onPress +{ + return onPress; +} + +-(ListenerGroup *) onRelease +{ + return onRelease; +} + +-(ListenerGroup *) onClick +{ + return onClick; +} + +-(ListenerGroup *) onDrag +{ + return onDrag; +} + +-(ListenerGroup *) onAuto +{ + return onAuto; +} + +-(ListenerGroup *) onHover +{ + return onHover; +} + +- (int) click +{ + return click; +} + +- (Point) delta +{ + return {dragPos.x - dragBase.x, dragPos.y - dragBase.y}; +} + +-move:(Point) delta +{ + Point pos = self.pos; + [super move:delta]; + dragBase.x += self.pos.x - pos.x; + dragBase.y += self.pos.y - pos.y; + return self; +} + +@end diff --git a/ruamoko/qwaq/ui/color.h b/ruamoko/qwaq/ui/color.h new file mode 100644 index 000000000..3b5ce2b2a --- /dev/null +++ b/ruamoko/qwaq/ui/color.h @@ -0,0 +1,21 @@ +#ifndef __qwaq_ui_color_h +#define __qwaq_ui_color_h + +#ifndef COLOR_PAIR +// double protection in case this header is included in a C file + +#define COLOR_PAIR(cp) ((cp) << 8) + +// taken from ncurses.h +#define COLOR_BLACK 0 +#define COLOR_RED 1 +#define COLOR_GREEN 2 +#define COLOR_YELLOW 3 +#define COLOR_BLUE 4 +#define COLOR_MAGENTA 5 +#define COLOR_CYAN 6 +#define COLOR_WHITE 7 + +#endif + +#endif//__qwaq_ui_color_h diff --git a/ruamoko/qwaq/ui/curses.h b/ruamoko/qwaq/ui/curses.h new file mode 100644 index 000000000..4268915a1 --- /dev/null +++ b/ruamoko/qwaq/ui/curses.h @@ -0,0 +1,176 @@ +#ifndef __qwaq_ui_curses_h +#define __qwaq_ui_curses_h + +#include "ruamoko/qwaq/ui/event.h" + +typedef struct box_sides_s { + int ls; + int rs; + int ts; + int bs; +} box_sides_t; + +typedef struct box_corners_s { + int tl; + int tr; + int bl; + int br; +} box_corners_t; + +#ifdef __QFCC__ +#include "ruamoko/qwaq/ui/rect.h" + +// names, order and comments lifted from ncurses.h +typedef enum { +/* VT100 symbols begin here */ + ACS_ULCORNER = 256, /* upper left corner */ + ACS_LLCORNER, /* lower left corner */ + ACS_URCORNER, /* upper right corner */ + ACS_LRCORNER, /* lower right corner */ + ACS_LTEE, /* tee pointing right */ + ACS_RTEE, /* tee pointing left */ + ACS_BTEE, /* tee pointing up */ + ACS_TTEE, /* tee pointing down */ + ACS_HLINE, /* horizontal line */ + ACS_VLINE, /* vertical line */ + ACS_PLUS, /* large plus or crossover */ + ACS_S1, /* scan line 1 */ + ACS_S9, /* scan line 9 */ + ACS_DIAMOND, /* diamond */ + ACS_CKBOARD, /* checker board (stipple) */ + ACS_DEGREE, /* degree symbol */ + ACS_PLMINUS, /* plus/minus */ + ACS_BULLET, /* bullet */ +/* Teletype 5410v1 symbols begin here */ + ACS_LARROW, /* arrow pointing left */ + ACS_RARROW, /* arrow pointing right */ + ACS_DARROW, /* arrow pointing down */ + ACS_UARROW, /* arrow pointing up */ + ACS_BOARD, /* board of squares */ + ACS_LANTERN, /* lantern symbol */ + ACS_BLOCK, /* solid square block */ +/* + * These aren't documented, but a lot of System Vs have them anyway + * (you can spot pprryyzz{{||}} in a lot of AT&T terminfo strings). + * The ACS_names may not match AT&T's, our source didn't know them. + */ + ACS_S3, /* scan line 3 */ + ACS_S7, /* scan line 7 */ + ACS_LEQUAL, /* less/equal */ + ACS_GEQUAL, /* greater/equal */ + ACS_PI, /* Pi */ + ACS_NEQUAL, /* not equal */ + ACS_STERLING, /* UK pound sign */ + +/* + * Line drawing ACS names are of the form ACS_trbl, where t is the top, r + * is the right, b is the bottom, and l is the left. t, r, b, and l might + * be B (blank), S (single), D (double), or T (thick). The subset defined + * here only uses B and S. + */ + ACS_BSSB = ACS_ULCORNER, + ACS_SSBB = ACS_LLCORNER, + ACS_BBSS = ACS_URCORNER, + ACS_SBBS = ACS_LRCORNER, + ACS_SBSS = ACS_RTEE, + ACS_SSSB = ACS_LTEE, + ACS_SSBS = ACS_BTEE, + ACS_BSSS = ACS_TTEE, + ACS_BSBS = ACS_HLINE, + ACS_SBSB = ACS_VLINE, + ACS_SSSS = ACS_PLUS, +} qwaq_acs_chars; + +typedef struct window_s *window_t; +typedef struct panel_s *panel_t; + +extern window_t stdscr; + +void initialize (void); +void syncprintf (string fnt, ...); +window_t create_window (int xpos, int ypos, int xlen, int ylen); +void destroy_window (window_t win); +void mvwprintf (window_t win, int x, int y, string fmt, ...); +void wprintf (window_t win, string fmt, ...); +void wvprintf (window_t win, string fmt, @va_list args); +void mvwvprintf (window_t win, int x, int y, string fmt, @va_list args); +void wrefresh (window_t win); +void mvwaddch (window_t win, int x, int y, int ch); +void waddch (window_t win, int ch); +void mvwaddstr (window_t win, int x, int y, string str); +void waddstr (window_t win, string str); +void mvwhline (window_t win, int x, int y, int ch, int n); + +panel_t create_panel (window_t window); +void destroy_panel (panel_t panel); +void hide_panel (panel_t panel); +void show_panel (panel_t panel); +void top_panel (panel_t panel); +void bottom_panel (panel_t panel); +void move_panel (panel_t panel, int x, int y); +window_t panel_window (panel_t panel); +void update_panels (void); +void replace_panel (panel_t panel, window_t window); +void doupdate (void); + +int get_event (qwaq_event_t *event); +int max_colors (void); +int max_color_pairs (void); +int init_pair (int pair, int f, int b); +void wbkgd (window_t win, int ch); +void werase (window_t win); +void scrollok (window_t win, int flag); +int wmove (window_t win, int x, int y); + +int acs_char (int acs); +int curs_set (int visibility); +int move (int x, int y); + +void wborder (window_t window, box_sides_t sides, box_corners_t corners); +void mvwblit_line (window_t window, int x, int y, int *wch, int len); +void wresize (window_t window, int width, int height); +void resizeterm (int width, int height); + +Rect getwrect (struct window_s *window); + +void printf(string fmt, ...); +// qfcc stuff +#else +// gcc stuff + +#include +#include + +#include "QF/dstring.h" +#include "QF/progs.h" +#include "QF/ringbuffer.h" + +#include "ruamoko/qwaq/threading.h" + +#define QUEUE_SIZE 16 +#define STRING_ID_QUEUE_SIZE 8 // must be > 1 +#define COMMAND_QUEUE_SIZE 1280 + +typedef struct window_s { + WINDOW *win; +} window_t; + +typedef struct panel_s { + PANEL *panel; + int window_id; +} panel_t; + +typedef struct qwaq_resources_s { + progs_t *pr; + int initialized; + window_t stdscr; + PR_RESMAP (window_t) window_map; + PR_RESMAP (panel_t) panel_map; + + qwaq_pipe_t commands; + qwaq_pipe_t results; +} qwaq_resources_t; +// gcc stuff +#endif + +#endif//__qwaq_ui_curses_h diff --git a/ruamoko/qwaq/ui/draw.h b/ruamoko/qwaq/ui/draw.h new file mode 100644 index 000000000..a271d3c45 --- /dev/null +++ b/ruamoko/qwaq/ui/draw.h @@ -0,0 +1,49 @@ +#ifndef __qwaq_ui_draw_h +#define __qwaq_ui_draw_h + +#include + +#include "ruamoko/qwaq/ui/rect.h" + +@class DrawBuffer; + +@protocol DrawBuffer +- blitFromBuffer: (DrawBuffer *) srcBuffer to: (Point) pos from: (Rect) rect; +- (Rect) rect; +- (Extent) size; +- (int *) buffer; +@end + +@protocol TextContext +- (void) release; +- blitFromBuffer: (DrawBuffer *) srcBuffer to: (Point) pos from: (Rect) rect; +- clearReact: (Rect) rect; +- (Extent) size; +- (void) resizeTo: (Extent) newSize; // absolute size + +- (void) bkgd: (int) ch; +- (void) clear; +- (void) printf: (string) fmt, ...; +- (void) vprintf: (string) fmt, @va_list args; +- (void) addch: (int) ch; +- (void) addstr: (string) str; +- (void) mvprintf: (Point) pos, string fmt, ...; +- (void) mvvprintf: (Point) pos, string fmt, @va_list args; +- (void) mvaddch: (Point) pos, int ch; +- (void) mvaddstr: (Point) pos, string str; +- (void) mvhline:(Point)pos, int ch, int len; +- (void) mvvline:(Point)pos, int ch, int len; +@end + +@interface DrawBuffer : Object +{ + int *buffer; + Extent size; + Point cursor; + int background; +} ++(DrawBuffer *)buffer:(Extent)size; +-initWithSize:(Extent)size; +@end + +#endif//__qwaq_ui_draw_h diff --git a/ruamoko/qwaq/ui/draw.r b/ruamoko/qwaq/ui/draw.r new file mode 100644 index 000000000..aeaa00475 --- /dev/null +++ b/ruamoko/qwaq/ui/draw.r @@ -0,0 +1,267 @@ +#include + +#include "ruamoko/qwaq/ui/curses.h" +#include "ruamoko/qwaq/ui/draw.h" + +@implementation DrawBuffer + ++(DrawBuffer *)buffer:(Extent)size +{ + return [[[self alloc] initWithSize: size] autorelease]; +} + +- initWithSize: (Extent) size +{ + if (!(self = [super init])) { + return nil; + } + buffer = obj_malloc (size.width * size.height); + self.size = size; + return self; +} + +-(void)dealloc +{ + obj_free (buffer); + [super dealloc]; +} + +- (Extent) size +{ + return size; +} + +- (void) resizeTo: (Extent) newSize +{ + size = newSize; + buffer = obj_realloc (buffer, size.width * size.height); +} + +- (int *) buffer +{ + return buffer; +} +- (Rect) rect +{ + Rect rect = { nil, size }; + return rect; +} + +- blitFromBuffer: (DrawBuffer *) srcBuffer to: (Point) pos from: (Rect) rect +{ + Extent srcSize = srcBuffer.size; + Rect r = { {}, size }; + Rect t = { pos, rect.extent }; + + //wprintf (stdscr, "src: %p\n", srcBuffer); + //wprintf (stdscr, "srcSize: %d %d\n", srcSize.width, srcSize.height); + + t = clipRect (r, t); + if (t.extent.width < 0 || t.extent.height < 0) { + return self; + } + + rect.offset.x += t.offset.x - pos.x; + rect.offset.y += t.offset.y - pos.y; + rect.extent = t.extent; + pos = t.offset; + + r.offset = nil; + r.extent = size; + + rect = clipRect (r, rect); + if (rect.extent.width < 0 || rect.extent.height < 0) { + return self; + } + + int *dst = buffer + pos.y * size.width + pos.x; + int *src = srcBuffer.buffer + + rect.offset.y * srcSize.width + rect.offset.x; + for (int y = 0; y < rect.extent.height; y++) { + int *d = dst; + int *s = src; + dst += size.width; + src += srcSize.width; + for (int x = 0; x < rect.extent.width; x++) { + // FIXME 1) need memcpy/memmove + // 2) the generated code could be better + // github issue #3 + *d++ = *s++; + } + } + return self; +} + +- clearReact: (Rect) rect +{ + Point pos = rect.offset; + int len = rect.extent.width; + int count = rect.extent.height; + + if (pos.x + len > size.width) { + len = size.width - pos.x; + } + if (pos.x < 0) { + len += pos.x; + pos.x = 0; + } + if (len < 1) { + return self; + } + if (pos.y + count > size.height) { + count = size.height - pos.y; + } + if (pos.y < 0) { + count += pos.y; + pos.y = 0; + } + if (count < 1) { + return self; + } + int width = size.width; + int ch = background; + if (!(ch & 0xff)) { + ch |= ' '; + } + while (count-- > 0) { + int *p = buffer + pos.y * width + pos.x; + for (int i = 0; i < len; i++) { + *p++ = ch; + } + pos.y++; + } + return self; +} + +- (void) bkgd: (int) ch +{ + background = ch; +} + +- (void) clear +{ + int ch = background; + int *dst = buffer; + int *end = buffer + size.width * size.height; + + if (ch && !(ch & 0xff)) { + ch |= ' '; + } + while (dst < end) { + *dst++ = ch; + } + cursor = {0, 0}; +} + +- (void) printf: (string) fmt, ... +{ + string str = vsprintf (fmt, @args); + [self addstr: str]; +} + +- (void) vprintf: (string) fmt, @va_list args +{ + string str = vsprintf (fmt, args); + [self addstr: str]; +} + +- (void) addch: (int) ch +{ + if (cursor.x < 0) { + cursor.x = 0; + } + if (cursor.y < 0) { + cursor.y = 0; + } + if (cursor.x >= size.width && cursor.y < size.height) { + cursor.x = 0; + cursor.y++; + } + if (cursor.y >= size.height) { + return; + } + if (ch == '\n') { + cursor.x = 0; + cursor.y++; + } else if (ch == '\r') { + cursor.x = 0; + } else { + if (!(ch & ~0xff)) { + ch |= background & ~0xff; + } + buffer[cursor.y * size.width + cursor.x++] = ch; + } +} + +- (void) addstr: (string) str +{ + int ind = 0; + int ch; + + if (cursor.y >= size.height) { + return; + } + while (cursor.x < size.width && (ch = str_char (str, ind++))) { + [self addch: ch]; + } +} + +- (void) mvprintf: (Point) pos, string fmt, ... +{ + if (pos.x < 0 || pos.x >= size.width + || pos.y < 0 || pos.y >= size.height) { + return; + } + cursor = pos; + string str = vsprintf (fmt, @args); + [self addstr: str]; +} + +- (void) mvvprintf: (Point) pos, string fmt, @va_list args +{ + if (pos.x < 0 || pos.x >= size.width + || pos.y < 0 || pos.y >= size.height) { + return; + } + cursor = pos; + string str = vsprintf (fmt, args); + [self addstr: str]; +} + +- (void) mvaddch: (Point) pos, int ch +{ + if (pos.x < 0 || pos.x >= size.width + || pos.y < 0 || pos.y >= size.height) { + return; + } + cursor = pos; + [self addch: ch]; +} + +- (void) mvaddstr: (Point) pos, string str +{ + if (pos.x < 0 || pos.x >= size.width + || pos.y < 0 || pos.y >= size.height) { + return; + } + cursor = pos; + [self addstr: str]; +} + +- (void) mvhline:(Point)pos, int ch, int len +{ + while (len-- > 0) { + [self mvaddch:pos, ch]; + pos.x++; + } +} + +- (void) mvvline:(Point)pos, int ch, int len +{ + while (len-- > 0) { + [self mvaddch:pos, ch]; + pos.y++; + } +} + +@end diff --git a/ruamoko/qwaq/ui/event.h b/ruamoko/qwaq/ui/event.h new file mode 100644 index 000000000..571e256f6 --- /dev/null +++ b/ruamoko/qwaq/ui/event.h @@ -0,0 +1,135 @@ +#ifndef __qwaq_ui_event_h +#define __qwaq_ui_event_h + +#include "ruamoko/qwaq/threading.h" + +typedef enum { + qe_mousedown = 0x0001, + qe_mouseup = 0x0002, + qe_mouseclick= 0x0004, + qe_mousemove = 0x0008, + qe_mouseauto = 0x0010, +} qwaq_mouse_event; + +typedef enum { + qe_keydown = 0x0020, +} qwaq_key_event; + +typedef enum { + qe_shift = 1, + qe_control = 4, +} qwaq_key_shift; + +typedef enum { + qe_command = 0x0200, // application level command + qe_broadcast = 0x0400, + qe_resize = 0x0800, // screen resized + + qe_dev_add = 0x1000, + qe_dev_rem = 0x2000, + qe_axis = 0x4000, + qe_button = 0x8000, +} qwaq_message_event; + +typedef enum { + qe_none = 0x0000, + qe_mouse = 0x001f, + qe_key = 0x0020, + qe_system = 0x01c0, //FIXME this isn't very manageable + qe_message = 0xfe00, + + qe_focused = qe_key | qe_command, + qe_positional = qe_mouse, +} qwaq_event_mask; + +typedef enum { + qc_valid, + qc_exit, + qc_error, +} qwaq_command; + +typedef struct qwaq_mevent_s { + int x, y; + int buttons; // current button state + int click; +} qwaq_mevent_t; + +typedef struct qwaq_kevent_s { + int code; + int shift; +} qwaq_kevent_t; + +typedef struct qwaq_resize_s { + int width; + int height; +} qwaq_resize_t; + +typedef union qwaq_message_s { + int int_val; + float float_val; + float vector_val[4]; // vector and quaternion + int ivector_val[4]; + double double_val; +#ifdef __QFCC__ + void *pointer_val; + string string_val; +#else + pr_ptr_t pointer_val; + pr_string_t string_val; +#endif +} qwaq_message_t; + +typedef struct qwaq_event_s { + int what; + int __pad; + double when; // NOTE: 1<<32 based + union { + qwaq_kevent_t key; + qwaq_mevent_t mouse; + qwaq_message_t message; + qwaq_resize_t resize; + }; +} qwaq_event_t; + +#ifndef __QFCC__ +typedef struct qwaq_event_queue_s { + rwcond_t cond; + RING_BUFFER (qwaq_event_t, QUEUE_SIZE) queue; +} qwaq_event_queue_t; + +typedef enum { + esc_ground, + esc_escape, + esc_csi, + esc_mouse, + esc_sgr, + esc_key, +} esc_state_t; + +typedef struct qwaq_input_resources_s { + progs_t *pr; + int initialized; + + qwaq_event_queue_t events; + + qwaq_pipe_t commands; + qwaq_pipe_t results; + + dstring_t escbuff; + esc_state_t escstate; + unsigned button_state; + int mouse_x; + int mouse_y; + qwaq_event_t lastClick; + struct hashtab_s *key_sequences; + + int input_event_handler; +} qwaq_input_resources_t; + +int qwaq_add_event (qwaq_input_resources_t *res, qwaq_event_t *event); +void qwaq_input_enable_mouse (void); +void qwaq_input_disable_mouse (void); + +#endif + +#endif//__qwaq_ui_event_h diff --git a/ruamoko/qwaq/ui/garray.h b/ruamoko/qwaq/ui/garray.h new file mode 100644 index 000000000..3ba1a0ef2 --- /dev/null +++ b/ruamoko/qwaq/ui/garray.h @@ -0,0 +1,29 @@ +#ifndef __qwaq_ui_garray_h +#define __qwaq_ui_garray_h + +#include + +typedef BOOL condition_func (id object, void *data); +typedef BOOL condition_func2 (id object, void *anObject, void *data); + +@interface Array (Group) +- (void) makeObjectsPerformSelector: (SEL)selector + if: (condition_func)condition + with: (void *)data; +- (void) makeObjectsPerformSelector: (SEL)selector + withObject: (void *)anObject + if: (condition_func2)condition + with: (void *)data; +- (void) makeReversedObjectsPerformSelector: (SEL)selector; +- (void) makeReversedObjectsPerformSelector: (SEL)selector + withObject: (void *)anObject; +- (void) makeReversedObjectsPerformSelector: (SEL)selector + if: (condition_func)condition + with: (void *)data; +- (void) makeReversedObjectsPerformSelector: (SEL)selector + withObject: (void *)anObject + if: (condition_func2)condition + with: (void *)data; +@end + +#endif//__qwaq_ui_garray_h diff --git a/ruamoko/qwaq/ui/garray.r b/ruamoko/qwaq/ui/garray.r new file mode 100644 index 000000000..55aa35e88 --- /dev/null +++ b/ruamoko/qwaq/ui/garray.r @@ -0,0 +1,66 @@ +#include +#include "ruamoko/qwaq/ui/event.h" +#include "ruamoko/qwaq/ui/garray.h" + +@implementation Array (Group) +- (void) makeObjectsPerformSelector: (SEL)selector + if: (condition_func)condition + with: (void *)data +{ + for (int i = 0; i < [self count]; i++) { + if (condition (_objs[i], data)) { + [_objs[i] performSelector: selector]; + } + } +} + +- (void) makeObjectsPerformSelector: (SEL)selector + withObject: (void *)anObject + if: (condition_func2)condition + with: (void *)data +{ + for (int i = 0; i < [self count]; i++) { + if (condition (_objs[i], anObject, data)) { + [_objs[i] performSelector: selector withObject: anObject]; + } + } +} + +- (void) makeReversedObjectsPerformSelector: (SEL)selector +{ + for (int i = [self count]; i-->0; ) { + [_objs[i] performSelector: selector]; + } +} + +- (void) makeReversedObjectsPerformSelector: (SEL)selector + withObject: (void *)anObject +{ + for (int i = [self count]; i-->0; ) { + [_objs[i] performSelector: selector withObject: anObject]; + } +} + +- (void) makeReversedObjectsPerformSelector: (SEL)selector + if: (condition_func)condition + with: (void *)data +{ + for (int i = [self count]; i-->0; ) { + if (condition (_objs[i], data)) { + [_objs[i] performSelector: selector]; + } + } +} + +- (void) makeReversedObjectsPerformSelector: (SEL)selector + withObject: (void *)anObject + if: (condition_func2)condition + with: (void *)data +{ + for (int i = [self count]; i-->0; ) { + if (condition (_objs[i], anObject, data)) { + [_objs[i] performSelector: selector withObject: anObject]; + } + } +} +@end diff --git a/ruamoko/qwaq/ui/group.h b/ruamoko/qwaq/ui/group.h new file mode 100644 index 000000000..52ee2af23 --- /dev/null +++ b/ruamoko/qwaq/ui/group.h @@ -0,0 +1,47 @@ +#ifndef __qwaq_ui_group_h +#define __qwaq_ui_group_h + +#include + +#include "ruamoko/qwaq/ui/event.h" +#include "ruamoko/qwaq/ui/draw.h" + +@class View; + +@interface Group : Object +{ + View *owner; + Array *views; + View *mouse_grabbed; + View *mouse_within; + int focused; + id context; +} ++(Group *)withContext:(id)context owner:(View *)owner; +-initWithContext:(id)context owner:(View *)owner; +-(id)context; +-setContext: (id) context; +-insert: (View *) view; +-insertDrawn: (View *) view; +-insertSelected: (View *) view; +-remove: (View *) view; +-(View *) owner; +-(Rect) rect; +-(Rect) absRect; +-(Point) origin; +-(Extent) size; +-draw; +-redraw; +-updateAbsPos: (Point) absPos; +-resize: (Extent) delta; +-handleEvent: (qwaq_event_t *) event; +-takeFocus; +-loseFocus; +-selectNext; +-selectPrev; +-selectView: (View *) view; +-(void) grabMouse; +-(void) releaseMouse; +@end + +#endif//__qwaq_ui_group_h diff --git a/ruamoko/qwaq/ui/group.r b/ruamoko/qwaq/ui/group.r new file mode 100644 index 000000000..6ba7926ac --- /dev/null +++ b/ruamoko/qwaq/ui/group.r @@ -0,0 +1,342 @@ +#include +#include "ruamoko/qwaq/ui/event.h" +#include "ruamoko/qwaq/ui/draw.h" +#include "ruamoko/qwaq/ui/garray.h" +#include "ruamoko/qwaq/ui/group.h" +#include "ruamoko/qwaq/ui/view.h" + +@reference Array (Group); + +@implementation Group + ++(Group *)withContext:(id)context owner:(View *)owner +{ + return [[[self alloc] initWithContext:context owner:owner] autorelease]; +} + +-initWithContext: (id) context owner: (View *) owner +{ + if (!(self = [super init])) { + return nil; + } + self.owner = owner; + self.context = context; + focused = -1; + views = [[Array array] retain]; + return self; +} + +-(void)dealloc +{ + [views release]; + [super dealloc]; +} + +-(id)context +{ + return context; +} + +-setContext: (id) context +{ + self.context = context; + [views makeObjectsPerformSelector:@selector(setContext:) + withObject:context]; + return self; +} + +-insert: (View *) view +{ + [views addObject: view]; + [view setOwner: self]; + [view setContext: context]; + return self; +} + +-insertDrawn: (View *) view +{ + [self insert: view]; + [view draw]; + return self; +} + +-insertSelected: (View *) view +{ + [self insertDrawn: view]; + [self selectView: view]; + return self; +} + +-remove: (View *) view +{ + int index = [views indexOfObject: view]; + if (index != NotFound) { + [views removeObjectAtIndex: index]; + if (focused == index) { + focused--; + [self selectPrev]; + if (focused == index) { + focused = -1; + } + } else if (focused > index) { + focused--; + } + if (mouse_within == view) { + mouse_within = nil; + } + if (mouse_grabbed == view) { + [self releaseMouse]; + } + } + return self; +} + +static int +makeFirst (Group *self, int viewIndex) +{ + View *view = [self.views objectAtIndex: viewIndex]; + + // add before remove to avoid freeing view + [self.views addObject: view]; + [self.views removeObjectAtIndex: viewIndex]; + [view raise]; + return [self.views count] - 1; +} + +static int +trySetFocus (Group *self, int viewIndex) +{ + View *view = [self.views objectAtIndex:viewIndex]; + if (([view state] & (sfDrawn | sfDisabled)) == sfDrawn + && [view options] & ofCanFocus) { + if (!self.owner || ([self.owner state] & sfInFocus)) { + if (self.focused >= 0) { + [[self.views objectAtIndex: self.focused] loseFocus]; + } + self.focused = viewIndex; + if ([view options] & ofMakeFirst) { + self.focused = makeFirst (self, viewIndex); + } + [view takeFocus]; + } else { + self.focused = viewIndex; + } + return 1; + } + return 0; +} + +-takeFocus +{ + if (focused >= 0) { + [[views objectAtIndex:focused] takeFocus]; + } + return self; +} + +-loseFocus +{ + if (focused >= 0) { + [[views objectAtIndex:focused] loseFocus]; + } + return self; +} + +-selectNext +{ + for (int i = focused + 1; i < [views count]; i++) { + if (trySetFocus (self, i)) { + return self; + } + } + // hit end, start again at the beginning + for (int i = 0; i < focused; i++) { + if (trySetFocus (self, i)) { + return self; + } + } + // did not find another view that can be focused + return self; +} + +-selectPrev +{ + for (int i = focused - 1; i >= 0; i--) { + if (trySetFocus (self, i)) { + return self; + } + } + // hit end, start again at the beginning + for (int i = [views count] - 1; i > focused; i++) { + if (trySetFocus (self, i)) { + return self; + } + } + // did not find another view that can be focused + return self; +} + +-selectView:(View *)view +{ + int index = [views indexOfObject: view]; + if (index != NotFound) { + trySetFocus (self, index); + } + return self; +} + +-(View *) owner +{ + return owner; +} + +-(Rect) rect +{ + if (owner) { + return [owner rect]; + } + return {[self origin], [self size]}; +} + +-(Rect) absRect +{ + if (owner) { + return [owner absRect]; + } + return {[self origin], [self size]}; +} + +-(Point) origin +{ + if (owner) { + return [owner origin]; + } + return {0, 0}; +} + +-(Extent) size +{ + if (owner) { + return [owner size]; + } + return [context size]; +} + +static BOOL +not_dont_draw (id aView, void *aGroup) +{ + View *view = aView; + Group *group = (Group *) aGroup; + + return !([view options] & ofDontDraw); +} + +-draw +{ + [views makeObjectsPerformSelector: @selector(draw) + if: not_dont_draw + with: self]; + return self; +} + +-redraw +{ + if (owner) { + [owner redraw]; + } else { + [self draw]; + if (__obj_responds_to (context, @selector(refresh))) { + [(id)context refresh]; + } + } + return self; +} + +-updateAbsPos: (Point) absPos +{ + for (int i = [views count]; i-- > 0; ) { + [[views objectAtIndex: i] updateAbsPos: absPos]; + } + return self; +} + +-resize: (Extent) delta +{ + for (int i = [views count]; i-- > 0; ) { + [[views objectAtIndex: i] grow: delta]; + } + return self; +} + +static View * +find_mouse_view(Group *group, Point pos) +{ + for (int i = [group.views count]; i--; ) { + View *v = [group.views objectAtIndex: i]; + if ([v containsPoint: pos]) { + return v; + } + } + return nil; +} + +static void +handlePositionalEvent (qwaq_event_t *event, View *view) +{ + Point pos = [view origin]; + int options = [view options]; + + if (options & ofRelativeEvents) { + event.mouse.x -= pos.x; + event.mouse.y -= pos.y; + } + + [view handleEvent: event]; + + if (options & ofRelativeEvents) { + event.mouse.x += pos.x; + event.mouse.y += pos.y; + } +} + +-handleEvent: (qwaq_event_t *) event +{ + if (event.what & qe_focused) { + if (focused >= 0) { + [[views objectAtIndex:focused] handleEvent: event]; + } + } else if (event.what & qe_positional) { + if (mouse_grabbed) { + handlePositionalEvent (event, mouse_grabbed); + } else { + Point pos = {event.mouse.x, event.mouse.y}; + View *mouse_view = find_mouse_view (self, pos); + if (mouse_within != mouse_view) { + [mouse_within onMouseLeave: pos]; + [mouse_view onMouseEnter: pos]; + mouse_within = mouse_view; + } + if (mouse_within) { + handlePositionalEvent (event, mouse_within); + } + } + } else { + // broadcast + [views makeObjectsPerformSelector: @selector(draw) withObject: event]; + } + return self; +} + +-(void) grabMouse +{ + mouse_grabbed = mouse_within; + [owner grabMouse]; +} + +-(void) releaseMouse +{ + mouse_grabbed = nil; + [owner releaseMouse]; +} + +@end diff --git a/ruamoko/qwaq/ui/listener.h b/ruamoko/qwaq/ui/listener.h new file mode 100644 index 000000000..8df0c949c --- /dev/null +++ b/ruamoko/qwaq/ui/listener.h @@ -0,0 +1,33 @@ +#ifndef __qwaq_ui_listener_h +#define __qwaq_ui_listener_h + +#include + +@class Array; + +@interface Listener : Object +{ + id responder; + SEL message; + IMP imp; +} ++(Listener *)listenerWithResponder:(id)responder :(SEL)message; +-initWithResponder: (id) responder :(SEL)message; +-(void) respond: (void *) caller_data; +-(void) respond: (void *) caller_data withObject:(void *)anObject; +-(BOOL) matchResponder: (id) responder :(SEL)message; +@end + +@interface ListenerGroup : Object +{ + Array *listeners; +} ++(ListenerGroup *)listener; +-init; +-addListener: (id) responder :(SEL)message; +-removeListener: (id) responder :(SEL)message; +-(void) respond: (void *) caller_data; +-(void) respond: (void *) caller_data withObject:(void *)anObject; +@end + +#endif//__qwaq_ui_listener_h diff --git a/ruamoko/qwaq/ui/listener.r b/ruamoko/qwaq/ui/listener.r new file mode 100644 index 000000000..1776e9ffe --- /dev/null +++ b/ruamoko/qwaq/ui/listener.r @@ -0,0 +1,97 @@ +#include + +#include "ruamoko/qwaq/ui/listener.h" +#include "ruamoko/qwaq/ui/curses.h" + +@class Array; + +@implementation Listener ++(Listener *)listenerWithResponder:(id)responder :(SEL)message +{ + return [[[self alloc] initWithResponder:responder :message] autorelease]; +} + +-initWithResponder: (id) responder :(SEL)message +{ + if (!(self = [super init])) { + return nil; + } + self.responder = responder; + self.message = message; + imp = [responder methodForSelector: message]; + if (!imp) { + [self error:"method not found: %s", sel_get_name (message)]; + } + return self; +} + +-(void)respond: (void *) caller_data +{ + imp (responder, message, caller_data); +} + +-(void)respond: (void *) caller_data withObject:(void *)anObject +{ + imp (responder, message, caller_data, anObject); +} + +-(BOOL) matchResponder: (id) responder :(SEL)message +{ + return self.responder == responder && self.message == message; +} +@end + +@implementation ListenerGroup : Object ++(ListenerGroup *)listener +{ + return [[[self alloc] init] autorelease]; +} + +-init +{ + if (!(self = [super init])) { + return nil; + } + listeners = [[Array array] retain]; + return self; +} + +-(void)dealloc +{ + [listeners release]; + [super dealloc]; +} + +-addListener: (id) responder :(SEL)message +{ + Listener *listener = [Listener listenerWithResponder:responder :message]; + if (listener) { + [listeners addObject:listener]; + } + return self; +} + +-removeListener: (id) responder :(SEL)message +{ + for (int i = [listeners count]; i-- > 0; ) { + Listener *l = [listeners objectAtIndex: i]; + if ([l matchResponder: responder : message]) { + [listeners removeObjectAtIndex: i]; + } + } + return self; +} + +-(void)respond: (void *) caller_data +{ + [listeners makeObjectsPerformSelector: @selector (respond:) + withObject: caller_data]; +} + +-(void)respond: (void *) caller_data withObject:(void *)anObject +{ + [listeners makeObjectsPerformSelector: @selector (respond:withObject:) + withObject: caller_data + withObject: anObject]; +} +@end diff --git a/ruamoko/qwaq/ui/proxyview.h b/ruamoko/qwaq/ui/proxyview.h new file mode 100644 index 000000000..c7d062cdc --- /dev/null +++ b/ruamoko/qwaq/ui/proxyview.h @@ -0,0 +1,21 @@ +#ifndef __qwaq_ui_proxyview_h +#define __qwaq_ui_proxyview_h + +#include "ruamoko/qwaq/ui/view.h" + +@class Group; + +@interface ProxyView : Object +{ + View *view; + Group *owner; +} ++(ProxyView *)withView:(View *)view; +-initWithView:(View *)view; +-setView:(View *)view; +@end + +@interface ProxyView (View) +@end + +#endif//__qwaq_ui_proxyview_h diff --git a/ruamoko/qwaq/ui/proxyview.r b/ruamoko/qwaq/ui/proxyview.r new file mode 100644 index 000000000..c39358092 --- /dev/null +++ b/ruamoko/qwaq/ui/proxyview.r @@ -0,0 +1,58 @@ +#include "ruamoko/qwaq/ui/group.h" +#include "ruamoko/qwaq/ui/proxyview.h" + +@implementation ProxyView ++(ProxyView *)withView:(View *)view +{ + return [[[self alloc] initWithView:view] autorelease]; +} + +- (void) forward: (SEL) sel : (@va_list) args +{ + if (!view) { + @return nil; + } + @return obj_msg_sendv (view, sel, args); +} + +-initWithView:(View *) view +{ + if (!(self = [super init])) { + return nil; + } + self.view = [view retain]; + return self; +} + +-setOwner:(Group *)owner +{ + self.owner = owner; + return [view setOwner:owner]; +} + +-setView:(View *) view +{ + int state = [self.view state]; + + if (state & sfInFocus) { + [self.view loseFocus]; + } + [self.view hide]; + [self.view setContext:nil]; + [self.view setOwner:nil]; + + [view retain]; + [self.view release]; + self.view = view; + + [view setOwner:owner]; + [view setContext:[owner context]]; + if (state & sfDrawn) { + [view draw]; + } + if (state & sfInFocus) { + [view takeFocus]; + } + return self; +} +@end diff --git a/ruamoko/qwaq/ui/rect.h b/ruamoko/qwaq/ui/rect.h new file mode 100644 index 000000000..007dcb9e6 --- /dev/null +++ b/ruamoko/qwaq/ui/rect.h @@ -0,0 +1,28 @@ +#ifndef __qwaq_ui_rect_h +#define __qwaq_ui_rect_h + +typedef struct Point_s { + int x; + int y; +} Point; + +typedef struct Extent_s { + int width; + int height; +} Extent; + +typedef struct Rect_s { + Point offset; + Extent extent; +} Rect; + +#ifdef __QFCC__ +Rect makeRect (int xpos, int ypos, int xlen, int ylen); +Point makePoint (int x, int y); +Extent makeExtent (int width, int height); +Extent mergeExtents (Extent a, Extent b); +int rectContainsPoint (Rect rect, Point point); +Rect clipRect (Rect clipRect, Rect rect); +#endif + +#endif//__qwaq_ui_rect_h diff --git a/ruamoko/qwaq/ui/rect.r b/ruamoko/qwaq/ui/rect.r new file mode 100644 index 000000000..905ee0385 --- /dev/null +++ b/ruamoko/qwaq/ui/rect.r @@ -0,0 +1,55 @@ +#include "ruamoko/qwaq/ui/rect.h" + +Rect +clipRect (Rect clipRect, Rect rect) +{ + if (rect.offset.x < clipRect.offset.x) { + int dist = clipRect.offset.x - rect.offset.x; + rect.offset.x += dist; + rect.extent.width -= dist; + } + if (rect.offset.y < clipRect.offset.y) { + int dist = clipRect.offset.y - rect.offset.y; + rect.offset.y += dist; + rect.extent.height -= dist; + } + if (rect.offset.x + rect.extent.width > clipRect.extent.width) { + rect.extent.width = clipRect.extent.width - rect.offset.x; + } + if (rect.offset.y + rect.extent.height > clipRect.extent.height) { + rect.extent.height = clipRect.extent.height - rect.offset.y; + } + return rect; +} + +Rect +makeRect (int xpos, int ypos, int xlen, int ylen) +{ + Rect rect = {{xpos, ypos}, {xlen, ylen}}; + return rect; +} + +Point makePoint (int x, int y) +{ + return {x, y}; +} + +Extent makeExtent (int width, int height) +{ + return {width, height}; +} + +Extent mergeExtents (Extent a, Extent b) +{ + return { a.width < b.width ? b.width : a.width, + a.height < b.height ? b.height : a.height }; +} + +int +rectContainsPoint (Rect rect, Point point) +{ + return ((point.x >= rect.offset.x + && point.x < rect.offset.x + rect.extent.width) + && (point.y >= rect.offset.y + && point.y < rect.offset.y + rect.extent.height)); +} diff --git a/ruamoko/qwaq/ui/scrollbar.h b/ruamoko/qwaq/ui/scrollbar.h new file mode 100644 index 000000000..c1ed0d47a --- /dev/null +++ b/ruamoko/qwaq/ui/scrollbar.h @@ -0,0 +1,41 @@ +#ifndef __qwaq_ui_scrollbar_h +#define __qwaq_ui_scrollbar_h + +#include "ruamoko/qwaq/ui/view.h" + +@class Button; +@class DrawBuffer; +@class Group; +@class ListenerGroup; + +@interface ScrollBar : View +{ + int vertical; + int bgchar; + double mouseTime; + Point mouseStart; + Point tabStart; + DrawBuffer *buffer; + Button *backButton; + Button *forwardButton; + Button *thumbTab; + Group *objects; + ListenerGroup *onScrollBarModified; + + unsigned pageStep; + unsigned singleStep; + unsigned range; + unsigned index; +} ++(ScrollBar *)horizontal:(unsigned)len at:(Point)pos; ++(ScrollBar *)vertical:(unsigned)len at:(Point)pos; +-(ListenerGroup *)onScrollBarModified; +-setRange:(unsigned)range; +-setPageStep:(unsigned)pageStep; +-setSingleStep:(unsigned)singleStep; +-setIndex:(unsigned)index; +-(unsigned)index; +-page:(unsigned)step dir:(unsigned) dir; +@end + +#endif//__qwaq_ui_scrollbar_h diff --git a/ruamoko/qwaq/ui/scrollbar.r b/ruamoko/qwaq/ui/scrollbar.r new file mode 100644 index 000000000..392bd3555 --- /dev/null +++ b/ruamoko/qwaq/ui/scrollbar.r @@ -0,0 +1,266 @@ +#include "ruamoko/qwaq/ui/button.h" +#include "ruamoko/qwaq/ui/group.h" +#include "ruamoko/qwaq/ui/listener.h" +#include "ruamoko/qwaq/ui/scrollbar.h" + +@implementation ScrollBar + +-initWithRect:(Rect)rect +{ + if (!(self = [super initWithRect:rect])) { + return nil; + } + options = ofRelativeEvents; + buffer = [[DrawBuffer buffer:size] retain]; + objects = [[Group withContext:buffer owner:self] retain]; + onScrollBarModified = [[ListenerGroup listener] retain]; + vertical = xlen == 1; + DrawBuffer *icons[3] = { + [DrawBuffer buffer:{1, 1}], + [DrawBuffer buffer:{1, 1}], + [DrawBuffer buffer:{1, 1}], + }; + [icons[2] addch:acs_char (ACS_DIAMOND)]; + Point thumbPos; + SEL thumbSel; + growMode = gfGrowAll; + if (vertical) { + [icons[0] addch:acs_char (ACS_UARROW)]; + [icons[1] addch:acs_char (ACS_DARROW)]; + thumbPos = {0, 1}; + thumbSel = @selector(verticalSlide:); + growMode &= ~gfGrowLoY; + } else { + [icons[0] addch:acs_char (ACS_LARROW)]; + [icons[1] addch:acs_char (ACS_RARROW)]; + thumbPos = {1, 0}; + thumbSel = @selector(horizontalSlide:); + growMode &= ~gfGrowLoX; + } + bgchar = acs_char (ACS_CKBOARD); + backButton = [Button withPos:{0, 0} + releasedIcon:icons[0] pressedIcon:icons[0]]; + forwardButton = [Button withPos:{xlen - 1, ylen - 1} + releasedIcon:icons[1] pressedIcon:icons[1]]; + thumbTab = [Button withPos:thumbPos + releasedIcon:icons[2] pressedIcon:icons[2]]; + + [forwardButton setGrowMode:gfGrowAll]; + + [[backButton onClick] addListener:self :@selector(scrollBack:)]; + [[forwardButton onClick] addListener:self :@selector(scrollForward:)]; + [[thumbTab onPress] addListener:self :thumbSel]; + + singleStep = 1; + + [objects insert:backButton]; + [objects insert:forwardButton]; + [objects insert:thumbTab]; + return self; +} + +-(void)dealloc +{ + [objects release]; + [buffer release]; + [onScrollBarModified release]; + [super dealloc]; +} + ++(ScrollBar *)horizontal:(unsigned)len at:(Point)pos +{ + if (len == 1) { + [self error:"can't make scrollbar of length 1"]; + } + return [[[self alloc] initWithRect:{pos, {len, 1}}] autorelease]; +} + ++(ScrollBar *)vertical:(unsigned)len at:(Point)pos +{ + if (len == 1) { + [self error:"can't make scrollbar of length 1"]; + } + return [[[self alloc] initWithRect:{pos, {1, len}}] autorelease]; +} + +-(ListenerGroup *)onScrollBarModified +{ + return onScrollBarModified; +} + +-updateAbsPos: (Point) absPos +{ + [super updateAbsPos: absPos]; + [objects updateAbsPos: absRect.offset]; + return self; +} + +-draw +{ + [super draw]; + if (vertical) { + [buffer mvvline:{0,0}, bgchar, ylen]; + } else { + [buffer mvhline:{0,0}, bgchar, xlen]; + } + [objects draw]; + [textContext blitFromBuffer:buffer to:pos from:[buffer rect]]; + return self; +} + +static void +position_tab (ScrollBar *self) +{ + Point p = {0, 0}; + if (self.range > 0) { + if (self.vertical) { + p.y = 1 + self.index * (self.ylen - 3) / self.range; + } else { + p.x = 1 + self.index * (self.xlen - 3) / self.range; + } + } else { + if (self.vertical) { + p.y = 1; + } else { + p.x = 1; + } + } + [self.thumbTab moveTo:p]; + [self redraw]; +} + +-resize:(Extent)delta +{ + Extent size = self.size; + [super resize:delta]; + delta = {self.size.width - size.width, self.size.height - size.height}; + [objects resize:delta]; + [buffer resizeTo:self.size]; + position_tab (self); + return self; +} + +-page:(unsigned)step dir:(unsigned) dir +{ + unsigned oind = index; + + if (dir) { + if (range - index < step) { + step = range - index; + } + index += step; + } else { + if (index < step) { + step = index; + } + index -= step; + } + + if (index != oind) { + [onScrollBarModified respond:self]; + position_tab (self); + } + return self; +} + +static void +page (ScrollBar *self, Point pos, Point thumb) +{ + unsigned pageDir = 0; + if (self.vertical) { + if (pos.y < thumb.y) { + pageDir = 0; + } else { + pageDir = 1; + } + } else { + if (pos.x < thumb.x) { + pageDir = 0; + } else { + pageDir = 1; + } + } + [self page:self.pageStep dir:pageDir]; +} + +-(void)scrollBack:(id)sender +{ + [self page:singleStep dir:0]; +} + +-(void)scrollForward:(id)sender +{ + [self page:singleStep dir:1]; +} + +-(void)horizontalSlide:(id)sender +{ +} + +-(void)verticalSlide:(id)sender +{ +} + +-handleEvent:(qwaq_event_t *)event +{ + [super handleEvent: event]; + [objects handleEvent: event]; + if (event.what == qe_mousedown) { + [self grabMouse]; + mouseTime = event.when; + mouseStart = {event.mouse.x, event.mouse.y}; + tabStart = [thumbTab origin]; + page(self, mouseStart, tabStart); + event.what = qe_none; + } else if (event.what==qe_mouseauto) { + if (event.when - mouseTime > 0.1) { + mouseTime = event.when; + page(self, mouseStart, tabStart); + } + event.what = qe_none; + } else if (event.what == qe_mouseup) { + [self releaseMouse]; + event.what = qe_none; + } + return self; +} + +-setRange:(unsigned)range +{ + self.range = range; + if (index > range) { + index = range; + [onScrollBarModified respond:self]; + position_tab (self); + } + return self; +} + +-setPageStep:(unsigned)pageStep +{ + self.pageStep = pageStep; + return self; +} + +-setSingleStep:(unsigned)singleStep +{ + self.singleStep = singleStep; + return self; +} + +-setIndex:(unsigned)index +{ + if (index > self.index) { + [self page:index - self.index dir:1]; + } else { + [self page:self.index - index dir:0]; + } + return self; +} + +-(unsigned)index +{ + return index; +} + +@end diff --git a/ruamoko/qwaq/ui/stringview.h b/ruamoko/qwaq/ui/stringview.h new file mode 100644 index 000000000..b497a8486 --- /dev/null +++ b/ruamoko/qwaq/ui/stringview.h @@ -0,0 +1,15 @@ +#ifndef __qwaq_ui_stringview_h +#define __qwaq_ui_stringview_h + +#include "ruamoko/qwaq/ui/view.h" + +@interface StringView : View +{ + string str; +} ++(StringView *)withRect:(Rect)rect; ++(StringView *)withRect:(Rect)rect string:(string)str; +-setString:(string)newString; +@end + +#endif//__qwaq_ui_stringview_h diff --git a/ruamoko/qwaq/ui/stringview.r b/ruamoko/qwaq/ui/stringview.r new file mode 100644 index 000000000..109a0ac52 --- /dev/null +++ b/ruamoko/qwaq/ui/stringview.r @@ -0,0 +1,42 @@ +#include + +#include "ruamoko/qwaq/ui/group.h" +#include "ruamoko/qwaq/ui/stringview.h" + +@implementation StringView + +-initWithRect:(Rect)rect string:(string)str +{ + if (!(self = [super initWithRect:rect])) { + return nil; + } + self.str = str; + growMode = gfGrowHiX; + return self; +} + ++(StringView *)withRect:(Rect)rect +{ + return [[[self alloc] initWithRect:rect string:nil] autorelease]; +} + ++(StringView *)withRect:(Rect)rect string:(string)str +{ + return [[[self alloc] initWithRect:rect string:str] autorelease]; +} + +-setString:(string) newTitle +{ + str = newTitle; + [self redraw]; + return self; +} + +-draw +{ + [super draw]; + [self mvaddstr: { 0, 0}, str]; + return self; +} + +@end diff --git a/ruamoko/qwaq/ui/tableview.h b/ruamoko/qwaq/ui/tableview.h new file mode 100644 index 000000000..630b2bf79 --- /dev/null +++ b/ruamoko/qwaq/ui/tableview.h @@ -0,0 +1,51 @@ +#ifndef __qwaq_ui_tableview_h +#define __qwaq_ui_tableview_h + +#include "ruamoko/qwaq/ui/view.h" + +@class DrawBuffer; +@class TableView; +@class TableViewColumn; +@class Array; +@class ListenerGroup; + +@protocol TableViewDataSource +-(ListenerGroup *)onRowCountChanged; +-(int)numberOfRows:(TableView *)tableview; +-(View *)tableView:(TableView *)tableView + forColumn:(TableViewColumn *)column + row:(int)row; +-retain; +-release; +@end + +@interface TableViewColumn : Object +{ + string name; + int width; + int growMode; // Y flags ignored +} ++(TableViewColumn *)named:(string)name; ++(TableViewColumn *)named:(string)name width:(int)width; + +-setGrowMode: (int) mode; +-(int)growMode; + +-(string)name; +-(int)width; +@end + +@interface TableView : View +{ + Array *columns; + DrawBuffer *buffer; + int columns_dirty; + id dataSource; + Point base; +} ++(TableView *)withRect:(Rect)rect; +-addColumn:(TableViewColumn *)column; +-setDataSource:(id)dataSource; +@end + +#endif//__qwaq_ui_tableview_h diff --git a/ruamoko/qwaq/ui/tableview.r b/ruamoko/qwaq/ui/tableview.r new file mode 100644 index 000000000..91137c718 --- /dev/null +++ b/ruamoko/qwaq/ui/tableview.r @@ -0,0 +1,257 @@ +#include +#include "ruamoko/qwaq/ui/listener.h" +#include "ruamoko/qwaq/ui/scrollbar.h" +#include "ruamoko/qwaq/ui/tableview.h" + +@implementation TableViewColumn +-initWithName:(string)name width:(int)width +{ + if (!(self = [super init])) { + return nil; + } + self.name = name; + self.width = width; + return self; +} + ++(TableViewColumn *)named:(string)name +{ + return [[[self alloc] initWithName:name width:-1] autorelease]; +} + ++(TableViewColumn *)named:(string)name width:(int)width +{ + return [[[self alloc] initWithName:name width:width] autorelease]; +} + +-setGrowMode: (int) mode +{ + growMode = mode; + return self; +} + +-(int)growMode +{ + return growMode; +} + +-(string)name +{ + return name; +} + +-(int)width +{ + return width; +} + +-setWidth:(int)width +{ + self.width = width; + return self; +} + +-grow:(Extent)delta +{ + if (growMode & gfGrowHiX) { + width += delta.width; + } + return self; +} +@end + +@implementation TableView +-initWithRect:(Rect)rect +{ + if (!(self = [super initWithRect:rect])) { + return nil; + } + options = ofCanFocus | ofRelativeEvents; + columns = [[Array array] retain]; + buffer = [[DrawBuffer buffer:size] retain]; + [buffer bkgd:' ']; + [onViewScrolled addListener:self :@selector(onScroll:)]; + growMode = gfGrowHi; + return self; +} + +-(void)dealloc +{ + [columns release]; + [buffer release]; + [dataSource release]; + [super dealloc]; +} + ++(TableView *)withRect:(Rect)rect +{ + return [[[self alloc] initWithRect:rect] autorelease]; +} + +-addColumn:(TableViewColumn *)column +{ + [columns addObject:column]; + columns_dirty = 1; + return self; +} + +-setDataSource:(id)dataSource +{ + self.dataSource = [dataSource retain]; + [[dataSource onRowCountChanged] addListener:self + :@selector(onRowCountChanged:)]; + [vScrollBar setRange:[dataSource numberOfRows:self]]; + return self; +} + +-(void)onRowCountChanged:(id)sender +{ + [vScrollBar setRange:[sender numberOfRows:self]]; +} + +-resize:(Extent)delta +{ + Extent size = self.size; + [super resize:delta]; + [buffer resizeTo:self.size]; + for (int i = [columns count]; i-- > 0; ) { + [[columns objectAtIndex: i] grow: delta]; + } + return self; +} + +-draw +{ + View *cell; + TableViewColumn *col; + [super draw]; + int numCols = [columns count]; + int numRows = [dataSource numberOfRows:self]; + [buffer clear]; + for (int y = 0; y < ylen; y++) { + for (int i = 0, x = 0; i < numCols; i++) { + int row = base.y + y; + if (row >= numRows) { + break; + } + col = [columns objectAtIndex:i]; + cell = [dataSource tableView:self forColumn:col row:row]; + [[[[cell setContext:buffer] + moveTo:{x, y}] + resizeTo:{[col width], 1}] + draw]; + x += [col width]; + } + } + [textContext blitFromBuffer:buffer to:pos from:[buffer rect]]; + return self; +} + +-(void)onScroll:(id)sender +{ + if (base.y != scroll.y) { + base.y = scroll.y; + [self redraw]; + } +} + +static int +handleEvent (TableView *self, qwaq_event_t *event) +{ + if (event.what & qe_mouse) { + if (event.what == qe_mouseclick) { + if (event.mouse.buttons & (1 << 3)) { + [self.vScrollBar page:1 dir:0]; + return 1; + } + if (event.mouse.buttons & (1 << 4)) { + [self.vScrollBar page:1 dir:1]; + return 1; + } +#if 0 + if (event.mouse.buttons & (1 << 5)) { + [self scrollLeft: 1]; + return 1; + } + if (event.mouse.buttons & (1 << 6)) { + [self scrollRight: 1]; + return 1; + } +#endif + } + } else if (event.what == qe_keydown) { +#if 0 + switch (event.key.code) { + case QFK_PAGEUP: + if (event.key.shift & qe_control) { + [self moveBOT]; + } else { + [self pageUp]; + } + return 1; + case QFK_PAGEDOWN: + if (event.key.shift & qe_control) { + [self moveEOT]; + } else { + [self pageDown]; + } + return 1; + case QFK_UP: + if (event.key.shift & qe_control) { + [self linesUp]; + } else { + [self charUp]; + } + return 1; + case QFK_DOWN: + if (event.key.shift & qe_control) { + [self linesDown]; + } else { + [self charDown]; + } + return 1; + case QFK_LEFT: + if (event.key.shift & qe_control) { + [self wordLeft]; + } else { + [self charLeft]; + } + return 1; + case QFK_RIGHT: + if (event.key.shift & qe_control) { + [self wordRight]; + } else { + [self charRight]; + } + return 1; + case QFK_HOME: + if (event.key.shift & qe_control) { + [self moveBOS]; + } else { + [self moveBOL]; + } + return 1; + case QFK_END: + if (event.key.shift & qe_control) { + [self moveEOS]; + } else { + [self moveEOL]; + } + return 1; + } +#endif + } + return 0; +} + +-handleEvent:(qwaq_event_t *) event +{ + [super handleEvent: event]; + + if (handleEvent (self, event)) { + event.what = qe_none; + } + return self; +} + +@end diff --git a/ruamoko/qwaq/ui/textcontext.h b/ruamoko/qwaq/ui/textcontext.h new file mode 100644 index 000000000..168a7b9c0 --- /dev/null +++ b/ruamoko/qwaq/ui/textcontext.h @@ -0,0 +1,87 @@ +#ifndef __qwaq_ui_textcontect_h +#define __qwaq_ui_textcontect_h + +#ifdef __QFCC__ +#include +#include "ruamoko/qwaq/ui/curses.h" +#include "ruamoko/qwaq/ui/draw.h" +#include "ruamoko/qwaq/ui/rect.h" + +@class DrawBuffer; + +@interface TextContext : Object +{ + window_t window; + union { + Rect rect; + struct { + Point offset; + Extent size; + }; + struct { + int xpos; + int ypos; + int xlen; + int ylen; + }; + }; + int background; +} ++ (int) max_colors; ++ (int) max_color_pairs; ++ (void) init_pair: (int) pair, int fg, int bg; ++ (int) acs_char: (int) acs; ++ (void) move: (Point) pos; ++ (void) curs_set: (int) visibility; ++ (void) doupdate; ++ (TextContext *) screen; + ++(TextContext *)textContext; ++(TextContext *)withRect:(Rect)rect; ++(TextContext *)withWindow:(window_t)window; + +-init; +-initWithRect: (Rect) rect; +-initWithWindow: (window_t) window; + +- (window_t) window; +- (Extent) size; + +- blitFromBuffer: (DrawBuffer *) srcBuffer to: (Point) pos from: (Rect) rect; + +- (void) refresh; ++ (void) refresh; +- (void) bkgd: (int) ch; +- (void) scrollok: (int) flag; +- (void) border: (box_sides_t) sides, box_corners_t corners; +- (void) mvhline: (Point) pos, int ch, int n; +- (void) mvvline: (Point) pos, int ch, int n; +-clearReact: (Rect) rect; +@end + +#else + +#include "QF/progs/pr_obj.h" + +typedef struct qwaq_textcontext_s { + pr_id_t isa; + pr_ptr_t window; + union { + Rect rect; + struct { + Point offset; + Extent size; + }; + struct { + int xpos; + int ypos; + int xlen; + int ylen; + }; + }; + int background; +} qwaq_textcontext_t; + +#endif + +#endif//__qwaq_ui_textcontect_h diff --git a/ruamoko/qwaq/ui/textcontext.r b/ruamoko/qwaq/ui/textcontext.r new file mode 100644 index 000000000..791f5b693 --- /dev/null +++ b/ruamoko/qwaq/ui/textcontext.r @@ -0,0 +1,233 @@ +#include "ruamoko/qwaq/ui/draw.h" +#include "ruamoko/qwaq/ui/textcontext.h" + +@implementation TextContext ++ (int) is_initialized = #0; ++ (void) initialize +{ + if (![self is_initialized]) { + initialize (); + } +} + ++ (int) max_colors = #0; ++ (int) max_color_pairs = #0; ++ (void) init_pair: (int) pair, int fg, int bg = #0; ++ (int) acs_char: (int) acs = #0; ++ (void) move: (Point) pos = #0; ++ (void) curs_set: (int) visibility = #0; ++ (void) doupdate = #0; + +static TextContext *screen; ++ (TextContext *) screen +{ + if (!screen) { + screen = [[TextContext textContext] retain]; + } + return screen; +} + ++(TextContext *)textContext +{ + return [[[self alloc] init] autorelease]; +} + ++(TextContext *)withRect:(Rect)rect +{ + return [[[self alloc] initWithRect:rect] autorelease]; +} + ++(TextContext *)withWindow:(window_t)window +{ + return [[[self alloc] initWithWindow:window] autorelease]; +} + +- init +{ + if (!(self = [super init])) { + return nil; + } + window = stdscr; + rect = getwrect (window); + return self; +} + +- initWithRect: (Rect) rect +{ + if (!(self = [super init])) { + return nil; + } + window = create_window (rect.offset.x, rect.offset.y, + rect.extent.width, rect.extent.height); + offset = {}; + size = rect.extent; + return self; +} + +- initWithWindow: (window_t) window +{ + if (!(self = [super init])) { + return nil; + } + self.window = window; + return self; +} + +-(void)delloc +{ + if (window != stdscr) { + destroy_window (window); + } +} + +-(window_t) window +{ + return window; +} + +-(Extent) size +{ + return size; +} + +- blitFromBuffer: (DrawBuffer *) srcBuffer to: (Point) pos from: (Rect) rect +{ + Extent srcSize = [srcBuffer size]; + Rect r = { {}, size }; + Rect t = { pos, rect.extent }; + + t = clipRect (r, t); + if (t.extent.width < 0 || t.extent.height < 0) { + return self; + } + + rect.offset.x += t.offset.x - pos.x; + rect.offset.y += t.offset.y - pos.y; + rect.extent = t.extent; + pos = t.offset; + + r.offset = nil; + r.extent = size; + + rect = clipRect (r, rect); + if (rect.extent.width < 0 || rect.extent.height < 0) { + return self; + } + + int *src = [srcBuffer buffer] + + rect.offset.y * srcSize.width + rect.offset.x; + for (int y = 0; y < rect.extent.height; y++) { + mvwblit_line (window, pos.x, y + pos.y, src, rect.extent.width); + src += srcSize.width; + } + return self; +} + +-clearReact: (Rect) rect +{ + Point pos = rect.offset; + int len = rect.extent.width; + int count = rect.extent.height; + + if (pos.x + len > xlen) { + len = xlen - pos.x; + } + if (pos.x < 0) { + len += pos.x; + pos.x = 0; + } + if (len < 1) { + return self; + } + if (pos.y + count > ylen) { + count = ylen - pos.y; + } + if (pos.y < 0) { + count += pos.y; + pos.y = 0; + } + if (count < 1) { + return self; + } + int ch = background; + if (!(ch & 0xff)) { + ch |= ' '; + } + while (count-- > 0) { + [self mvhline:pos, ch, len]; + pos.y++; + } + return self; +} + +- (void) printf: (string) fmt, ... = #0; +- (void) vprintf: (string) mft, @va_list args = #0; +- (void) addch: (int) ch = #0; +- (void) addstr: (string) str = #0; + +- (void) mvprintf: (Point) pos, string fmt, ... = #0; +- (void) mvvprintf: (Point) pos, string mft, @va_list args = #0; +- (void) mvaddch: (Point) pos, int ch = #0; +- (void) mvaddstr: (Point) pos, string str = #0; +- (void) mvhline: (Point) pos, int ch, int n = #0; +- (void) mvvline: (Point) pos, int ch, int n = #0; + +- (void) resizeTo: (Extent) newSize = #0; // absolute size +- (void) refresh = #0; ++ (void) refresh = #0; + +- (void) bkgd: (int) ch = #0; +- (void) clear = #0; +- (void) scrollok: (int) flag = #0; +- (void) border: (box_sides_t) sides, box_corners_t corners = #0; + +@end + +window_t stdscr = (window_t) 1; + +void initialize (void) = #0; +void syncprintf (string fnt, ...) = #0; +window_t create_window (int xpos, int ypos, int xlen, int ylen) = #0; +void destroy_window (window_t win) = #0; +void mvwprintf (window_t win, int x, int y, string fmt, ...) = #0; +void wprintf (window_t win, string fmt, ...) = #0; +void wvprintf (window_t win, string fmt, @va_list args) = #0; +void mvwvprintf (window_t win, int x, int y, string fmt, @va_list args) = #0; +void wrefresh (window_t win) = #0; +void mvwaddch (window_t win, int x, int y, int ch) = #0; +void waddch (window_t win, int ch) = #0; +void mvwaddstr (window_t win, int x, int y, string str) = #0; +void waddstr (window_t win, string str) = #0; +int get_event (qwaq_event_t *event) = #0; +int max_colors (void) = #0; +int max_color_pairs (void) = #0; +int init_pair (int pair, int f, int b) = #0; +void wbkgd (window_t win, int ch) = #0; +void werase (window_t win) = #0; +void scrollok (window_t win, int flag) = #0; +int wmove (window_t win, int x, int y) = #0; +int acs_char (int acs) = #0; + +panel_t create_panel (window_t window) = #0; +void destroy_panel (panel_t panel) = #0; +void hide_panel (panel_t panel) = #0; +void show_panel (panel_t panel) = #0; +void top_panel (panel_t panel) = #0; +void bottom_panel (panel_t panel) = #0; +void move_panel (panel_t panel, int x, int y) = #0; +window_t panel_window (panel_t panel) = #0; +void replace_panel (panel_t panel, window_t window) = #0; +void update_panels (void) = #0; + +void doupdate (void) = #0; +int curs_set (int visibility) = #0; +int move (int x, int y) = #0; +void wborder (window_t window, box_sides_t sides, box_corners_t corners) = #0; +void mvwblit_line (window_t window, int x, int y, int *wch, int len) = #0; +void wresize (window_t window, int width, int height) = #0; +void resizeterm (int width, int height) = #0; +Rect getwrect (window_t window) = #0; +void mvwhline (window_t win, int x, int y, int ch, int n) = #0; +void mvwvline (window_t win, int x, int y, int ch, int n) = #0; + +void printf(string fmt, ...) = #0; diff --git a/ruamoko/qwaq/ui/titlebar.h b/ruamoko/qwaq/ui/titlebar.h new file mode 100644 index 000000000..0ad8f9f10 --- /dev/null +++ b/ruamoko/qwaq/ui/titlebar.h @@ -0,0 +1,18 @@ +#ifndef __qwaq_ui_titlebar_h +#define __qwaq_ui_titlebar_h + +#include "ruamoko/qwaq/ui/view.h" + +@interface TitleBar : View +{ + string title; + int length; +} +// title always centered at top of owner ++(TitleBar *)withTitle:(string)title; +-initWithTitle:(string)title; +-setTitle:(string)newTitle; +-(string)title; +@end + +#endif//__qwaq_ui_titlebar_h diff --git a/ruamoko/qwaq/ui/titlebar.r b/ruamoko/qwaq/ui/titlebar.r new file mode 100644 index 000000000..e005fa4cf --- /dev/null +++ b/ruamoko/qwaq/ui/titlebar.r @@ -0,0 +1,51 @@ +#include + +#include "ruamoko/qwaq/ui/group.h" +#include "ruamoko/qwaq/ui/titlebar.h" + +@implementation TitleBar + ++(TitleBar *)withTitle:(string)title +{ + return [[[self alloc] initWithTitle:title] autorelease]; +} + +-initWithTitle:(string) title +{ + if (!(self = [super init])) { + return nil; + } + self.title = title; + length = strlen (title); + growMode = gfGrowHiX; + return self; +} + +-setTitle:(string) newTitle +{ + title = newTitle; + length = strlen (title); + [self redraw]; + return self; +} + +-(string)title +{ + return title; +} + +-setOwner: (Group *) owner +{ + [super setOwner: owner]; + size = [owner size]; + return self; +} + +-draw +{ + [super draw]; + [self mvaddstr: { (xlen - length) / 2, 0}, title]; + return self; +} + +@end diff --git a/ruamoko/qwaq/ui/view.h b/ruamoko/qwaq/ui/view.h new file mode 100644 index 000000000..f05b94326 --- /dev/null +++ b/ruamoko/qwaq/ui/view.h @@ -0,0 +1,143 @@ +#ifndef __qwaq_ui_view_h +#define __qwaq_ui_view_h + +#include +#include + +#include "ruamoko/qwaq/ui/draw.h" +#include "ruamoko/qwaq/ui/rect.h" +#include "ruamoko/qwaq/ui/textcontext.h" + +@class Group; +@class ListenerGroup; +@class ScrollBar; + +enum { + ofCanFocus = 0x0001, + ofFirstClick = 0x0002, + ofMakeFirst = 0x0004, + ofDontDraw = 0x0008, + ofRelativeEvents= 0x0010, + + ofTileable = 0x0020, + ofCentered = 0x0040, +}; + +enum { + sfDrawn = 0x0001, + sfDisabled = 0x0002, + sfInFocus = 0x0004, + sfModal = 0x0008, + sfLocked = 0x0010, +}; + +enum { + gfGrowNone = 0x0000, + gfGrowLoX = 0x0001, + gfGrowLoY = 0x0002, + gfGrowHiX = 0x0004, + gfGrowHiY = 0x0008, + gfGrowRel = 0x0010, + gfGrowLo = gfGrowLoX | gfGrowLoY, + gfGrowHi = gfGrowHiX | gfGrowHiY, + gfGrowX = gfGrowLoX | gfGrowHiX, + gfGrowY = gfGrowLoY | gfGrowHiY, + gfGrowAll = gfGrowX | gfGrowY, +}; + +@protocol View +-setOwner: (Group *) owner; +-setGrowMode: (int) mode; + +-(Rect)rect; +-(Rect)absRect; +-(Point)origin; +-(Extent)size; + +-(int) containsPoint: (Point) point; +-(void) grabMouse; +-(void) releaseMouse; + +-(int) options; +-(int) state; + +-setVerticalScrollBar:(ScrollBar *)scrollbar; +-setHorizontalScrollBar:(ScrollBar *)scrollbar; +-(ListenerGroup *) onViewScrolled; + +-(id)context; +-setContext: (id) context; +-draw; +-hide; +-redraw; +-move: (Point) delta; +-updateAbsPos: (Point) absPos; +-resize: (Extent) delta; +-move:(Point)dpos andResize:(Extent)dsize; +-moveTo:(Point)pos; // does not redraw +-resizeTo:(Extent)size; // does not redraw +-grow: (Extent) delta; +-(ListenerGroup *)onEvent; +-handleEvent: (qwaq_event_t *) event; +-takeFocus; +-loseFocus; +-(ListenerGroup *) onReceiveFocus; +-(ListenerGroup *) onReleaseFocus; +-raise; +-hideCursor; +-showCursor; +-setCursorVisible:(int) visible; +-moveCursor:(Point) pos; + +- (void) onMouseEnter: (Point) pos; +- (void) onMouseLeave: (Point) pos; + +- (void) refresh; +- (void) mvprintf: (Point) pos, string fmt, ...; +- (void) mvvprintf: (Point) pos, string fmt, @va_list args; +- (void) mvaddch: (Point) pos, int ch; +-clear; +@end + +@interface View: Object +{ + union { + Rect rect; + struct { + int xpos; + int ypos; + int xlen; + int ylen; + }; + struct { + Point pos; + Extent size; + }; + }; + Rect absRect; + Group *owner; + id textContext; + int state; + int options; + int growMode; + int cursorState; + Point cursorPos; + ListenerGroup *onReceiveFocus; + ListenerGroup *onReleaseFocus; + ListenerGroup *onEvent; + ListenerGroup *onViewScrolled; + ScrollBar *hScrollBar; + ScrollBar *vScrollBar; + Point scroll; +} ++(View *)viewWithRect:(Rect)rect; ++(View *)viewWithRect:(Rect)rect options:(int)options; + +-initWithRect:(Rect)rect; +-initWithRect:(Rect)rect options:(int)options; +@end + +@interface View (TextContext) +@end + +#endif//__qwaq_ui_view_h diff --git a/ruamoko/qwaq/ui/view.r b/ruamoko/qwaq/ui/view.r new file mode 100644 index 000000000..1b7fb4c06 --- /dev/null +++ b/ruamoko/qwaq/ui/view.r @@ -0,0 +1,472 @@ +#include "ruamoko/qwaq/ui/curses.h" +#include "ruamoko/qwaq/ui/listener.h" +#include "ruamoko/qwaq/ui/group.h" +#include "ruamoko/qwaq/ui/scrollbar.h" +#include "ruamoko/qwaq/ui/view.h" +#include "ruamoko/qwaq/debugger/debug.h" + +@implementation View + ++(View *)viewWithRect:(Rect)rect +{ + return [[[self alloc] initWithRect:rect] autorelease]; +} + ++(View *)viewWithRect:(Rect)rect options:(int)options +{ + return [[[self alloc] initWithRect:rect options:options] autorelease]; +} + +static void view_init(View *self) +{ + self.onReceiveFocus = [[ListenerGroup listener] retain]; + self.onReleaseFocus = [[ListenerGroup listener] retain]; + self.onEvent = [[ListenerGroup listener] retain]; + self.onViewScrolled = [[ListenerGroup listener] retain]; +} + +-init +{ + if (!(self = [super init])) { + return nil; + } + view_init (self); + return self; +} + +-initWithRect: (Rect) rect +{ + if (!(self = [super init])) { + return nil; + } + view_init (self); + + self.rect = rect; + self.absRect = rect; + return self; +} + +-initWithRect: (Rect) rect options:(int)options +{ + if (!(self = [super init])) { + return nil; + } + view_init (self); + + self.rect = rect; + self.absRect = rect; + self.options = options; + return self; +} + +- (void) dealloc +{ + [onReceiveFocus release]; + [onReleaseFocus release]; + [onEvent release]; + [onViewScrolled release]; + [super dealloc]; +} + +-(id)context +{ + return textContext; +} + +-setContext: (id) context +{ + textContext = context; + return self; +} + +- (int) options +{ + return options; +} + +- (int) state +{ + return state; +} + +-(void)onScrollBarModified:(id)sender +{ + if (sender == vScrollBar) { + scroll.y = [sender index]; + } else if (sender == hScrollBar) { + scroll.x = [sender index]; + } + [onViewScrolled respond:self]; +} + +static void +setScrollBar (View *self, ScrollBar **sb, ScrollBar *scrollbar) +{ + SEL sel = @selector(onScrollBarModified:); + [scrollbar retain]; + [[*sb onScrollBarModified] removeListener:self :sel]; + [*sb release]; + + *sb = scrollbar; + [[*sb onScrollBarModified] addListener:self :sel]; +} + +-setVerticalScrollBar:(ScrollBar *)scrollbar +{ + setScrollBar (self, &vScrollBar, scrollbar); + return self; +} + +-setHorizontalScrollBar:(ScrollBar *)scrollbar +{ + setScrollBar (self, &hScrollBar, scrollbar); + return self; +} + +-(window_t) window +{ + return nil; +} + +static void updateScreenCursor (View *view); + +-updateScreenCursor +{ + updateScreenCursor (self); + return self; +} + +static void +updateScreenCursor (View *view) +{ + if (view.state & sfInFocus) { + if (view.owner) { + View *owner = [view.owner owner]; + if (view.cursorPos.x >= 0 && view.cursorPos.x < view.xlen + && view.cursorPos.y >= 0 && view.cursorPos.y < view.ylen) { + owner.cursorPos.x = view.cursorPos.x + view.xpos; + owner.cursorPos.y = view.cursorPos.y + view.ypos; + owner.cursorState = view.cursorState; + } else { + owner.cursorState = 0; + } + [owner updateScreenCursor]; + } else { + } + } +/* + curs_set (cursorState); + wmove (get_window (view), cursorPos.x, cursorPos.y); +*/ +} + +-hideCursor +{ + return [self setCursorVisible: 0]; +} + +-showCursor +{ + return [self setCursorVisible: 1]; +} + +-setCursorVisible: (int) visible +{ + cursorState = visible; + if ((state & (sfInFocus | sfDrawn)) == (sfInFocus | sfDrawn)) { + [self updateScreenCursor]; + } + return self; +} + +-moveCursor: (Point) pos +{ + cursorPos = pos; + if ((state & (sfInFocus | sfDrawn)) == (sfInFocus | sfDrawn)) { + [self updateScreenCursor]; + } + return self; +} + +-draw +{ + state |= sfDrawn; + [self updateScreenCursor]; + return self; +} + +-hide +{ + if (state & sfDrawn) { + state &= ~sfDrawn; + [self updateScreenCursor]; + } + return self; +} + +-redraw +{ + if ((state & sfDrawn) && !(options & ofDontDraw)) { + [self draw]; + [owner redraw]; + } + return self; +} + +-setOwner: (Group *) owner +{ + self.owner = owner; + return self; +} + +-setGrowMode: (int) mode +{ + growMode = mode; + return self; +} + +- (Rect) rect +{ + return rect; +} + +- (Rect) absRect +{ + return rect; +} + +-(Point)origin +{ + return pos; +} + +-(Extent)size +{ + return size; +} + + +-(int) containsPoint: (Point) point +{ + return rectContainsPoint (rect, point); +} + +-(void) grabMouse +{ + [owner grabMouse]; +} + +-(void) releaseMouse +{ + [owner releaseMouse]; +} + +- (void) forward: (SEL) sel : (@va_list) args +{ + if (!textContext) { + @return nil; + } + if (!__obj_responds_to (textContext, sel)) { + [self error: "no implementation for %s", sel_get_name (sel)]; + } + @return obj_msg_sendv (textContext, sel, args); +} + +- (void) refresh +{ + if (__obj_responds_to (textContext, @selector(refresh))) { + [(id)textContext refresh]; + } +} + +- (void) mvprintf: (Point) pos, string fmt, ... +{ + pos.x += xpos; + pos.y += ypos; + [textContext mvvprintf: pos, fmt, va_copy (@args)]; +} + +- (void) mvvprintf: (Point) pos, string fmt, @va_list args +{ + pos.x += xpos; + pos.y += ypos; + [textContext mvvprintf: pos, fmt, args]; +} + +- (void) mvaddch: (Point) pos, int ch +{ + pos.x += xpos; + pos.y += ypos; + [textContext mvaddch: pos, ch]; +} + +- (void) mvaddstr: (Point) pos, string str +{ + pos.x += xpos; + pos.y += ypos; + [textContext mvaddstr: pos, str]; +} + +-clear +{ + [textContext clearReact:rect]; + return self; +} + +-move: (Point) delta +{ + xpos += delta.x; + ypos += delta.y; + if (xpos + xlen < 1) { + xpos = 1 - xlen; + } + if (ypos < 0) { + ypos = 0; + } + if (owner) { + Rect r = [owner absRect]; + Extent s = [owner size]; + if (xpos > r.extent.width - 1) { + xpos = r.extent.width - 1; + } + if (ypos > r.extent.height - 1) { + ypos = r.extent.height - 1; + } + [self updateAbsPos: r.offset]; + } else { + [self updateAbsPos: nil]; + } + return self; +} + +-updateAbsPos: (Point) absPos +{ + absRect.offset.x = absPos.x + xpos; + absRect.offset.y = absPos.y + ypos; + return self; +} + +-resize: (Extent) delta +{ + xlen += delta.width; + ylen += delta.height; + if (xlen < 1) { + xlen = 1; + } + if (ylen < 1) { + ylen = 1; + } + return self; +} + +-move:(Point)dpos andResize:(Extent)dsize +{ + [self move: dpos]; + [self resize: dsize]; + return self; +} + +-moveTo:(Point)pos +{ + self.pos = pos; + return self; +} + +-resizeTo:(Extent)size +{ + self.size = size; + return self; +} + +-grow: (Extent) delta +{ + Point dpos = {}; + Extent dsize = {}; + + if (growMode & gfGrowLoX) { + dpos.x += delta.width; + dsize.width -= delta.width; + } + if (growMode & gfGrowHiX) { + dsize.width += delta.width; + } + if (growMode & gfGrowLoY) { + dpos.y += delta.height; + dsize.height -= delta.height; + } + if (growMode & gfGrowHiY) { + dsize.height += delta.height; + } + int save_state = state; + state &= ~sfDrawn; + [self move: dpos andResize: dsize]; + state = save_state; + [self redraw]; + return self; +} + +-(ListenerGroup *) onReceiveFocus +{ + return onReceiveFocus; +} + +-(ListenerGroup *) onReleaseFocus +{ + return onReleaseFocus; +} + +-(ListenerGroup *)onEvent +{ + return onEvent; +} + +-(ListenerGroup *) onViewScrolled +{ + return onViewScrolled; +} + +-handleEvent: (qwaq_event_t *) event +{ + // give any listeners a chance to override or extend event handling + [onEvent respond:self withObject:event]; + if (event.what & (qe_mousedown | qe_mouseclick) + && options & ofCanFocus && !(state & (sfDisabled | sfInFocus))) { + [owner selectView: self]; + if (!(options & ofFirstClick)) { + event.what = qe_none; + } + } + return self; +} + +-takeFocus +{ + state |= sfInFocus; + [self updateScreenCursor]; + [onReceiveFocus respond:self]; + return self; +} + +-loseFocus +{ + state &= ~sfInFocus; + [self updateScreenCursor]; + [onReleaseFocus respond:self]; + return self; +} + +-raise +{ + return self; +} + +- (void) onMouseEnter: (Point) pos +{ +} + +- (void) onMouseLeave: (Point) pos +{ +} + + +@end diff --git a/ruamoko/qwaq/ui/window.h b/ruamoko/qwaq/ui/window.h new file mode 100644 index 000000000..8464a68ee --- /dev/null +++ b/ruamoko/qwaq/ui/window.h @@ -0,0 +1,39 @@ +#ifndef __qwaq_ui_window_h +#define __qwaq_ui_window_h + +#include "Object.h" + +@class Group; +@class Button; +@class TitleBar; + +#include "ruamoko/qwaq/ui/draw.h" +#include "ruamoko/qwaq/ui/rect.h" +#include "ruamoko/qwaq/ui/view.h" + +@interface Window: View +{ + struct panel_s *panel; + Group *objects; + Point point; // FIXME can't be local :( + DrawBuffer *buf; + + Button *topDrag; // move-only + Button *topLeftDrag; + Button *topRightDrag; + Button *leftDrag; + Button *rightDrag; + Button *bottomLeftDrag; + Button *bottomRightDrag; + Button *bottomDrag; + TitleBar *titleBar; +} ++(Window *)withRect: (Rect) rect; +-setTitle:(string) title; +-setBackground: (int) ch; +-insert: (View *) view; +-insertDrawn: (View *) view; +-insertSelected: (View *) view; +@end + +#endif//__qwaq_ui_window_h diff --git a/ruamoko/qwaq/ui/window.r b/ruamoko/qwaq/ui/window.r new file mode 100644 index 000000000..d12ac974b --- /dev/null +++ b/ruamoko/qwaq/ui/window.r @@ -0,0 +1,312 @@ +#include +#include + +#include "ruamoko/qwaq/ui/event.h" +#include "ruamoko/qwaq/ui/button.h" +#include "ruamoko/qwaq/ui/curses.h" +#include "ruamoko/qwaq/ui/group.h" +#include "ruamoko/qwaq/ui/listener.h" +#include "ruamoko/qwaq/ui/titlebar.h" +#include "ruamoko/qwaq/ui/window.h" +#include "ruamoko/qwaq/ui/view.h" + +@implementation Window + ++(Window *)withRect: (Rect) rect +{ + return [[[self alloc] initWithRect: rect] autorelease]; +} + +-initWithRect: (Rect) rect +{ + return [self initWithRect: rect options:ofCanFocus | ofMakeFirst]; +} + +-initWithRect: (Rect) rect options:(int)options +{ + if (!(self = [super init])) { + return nil; + } + self.rect = rect; + textContext = [[TextContext withRect: rect] retain]; + panel = create_panel ([(id)textContext window]); + + objects = [[Group withContext:textContext owner:self] retain]; + + [self insert:titleBar = [TitleBar withTitle:""]]; + + topDrag = [Button withRect:{{2, 0}, {xlen - 4, 1}}]; + topLeftDrag = [Button withRect:{{0, 0}, {2, 2}}]; + topRightDrag = [Button withRect:{{xlen - 2, 0}, {2, 2}}]; + leftDrag = [Button withRect:{{0, 2}, {1, ylen - 4}}]; + rightDrag = [Button withRect:{{xlen - 1, 2}, {1, ylen - 4}}]; + bottomLeftDrag = [Button withRect:{{0, ylen - 2} , {2, 2}}]; + bottomRightDrag = [Button withRect:{{xlen - 2, ylen - 2}, {2, 2}}]; + bottomDrag = [Button withRect:{{2, ylen - 1}, {xlen - 4, 1}}]; + [self insert: [topDrag setGrowMode: gfGrowHiX]]; + [self insert: [topLeftDrag setGrowMode: gfGrowNone]]; + [self insert: [topRightDrag setGrowMode: gfGrowX]]; + [self insert: [leftDrag setGrowMode: gfGrowHiY]]; + [self insert: [rightDrag setGrowMode: gfGrowX | gfGrowHiY]]; + [self insert: [bottomLeftDrag setGrowMode: gfGrowY]]; + [self insert: [bottomRightDrag setGrowMode: gfGrowAll]]; + [self insert: [bottomDrag setGrowMode: gfGrowHiX | gfGrowY]]; + + [[topDrag onDrag] addListener: self : @selector(dragWindow:)]; + [[topLeftDrag onDrag] addListener: self : @selector(dragWindow:)]; + [[topRightDrag onDrag] addListener: self : @selector(dragWindow:)]; + [[leftDrag onDrag] addListener: self : @selector(dragWindow:)]; + [[rightDrag onDrag] addListener: self : @selector(dragWindow:)]; + [[bottomLeftDrag onDrag] addListener: self : @selector(dragWindow:)]; + [[bottomRightDrag onDrag] addListener: self : @selector(dragWindow:)]; + [[bottomDrag onDrag] addListener: self : @selector(dragWindow:)]; + + buf = [DrawBuffer buffer: {3, 3}]; + [buf mvaddstr: {0, 0}, "XOX"]; + [buf mvaddstr: {0, 1}, "OXO"]; + [buf mvaddstr: {0, 2}, "XOX"]; + + growMode = gfGrowHi; + self.options = options; + return self; +} + +-(void)dealloc +{ + destroy_panel (panel); + [textContext release]; + [objects release]; + [super dealloc]; +} + +-updateScreenCursor +{ + window_t window = [(id)textContext window]; + curs_set (cursorState); + wmove (window, cursorPos.x, cursorPos.y); + [[TextContext screen] refresh]; + return self; +} + +-setTitle:(string) title +{ + [titleBar setTitle:title]; + return self; +} + +#ifndef max +# define max(a,b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef min +# define min(a,b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef bound +# define bound(a,b,c) (max(a, min(b, c))) +#endif + +- (void) dragWindow: (Button *) sender +{ + Point delta = [sender delta]; + Point dp = nil; + Point ds = nil; + Extent b = [owner size]; + + if (sender == topDrag) { + dp.x = bound (0, xpos + delta.x, b.width - xlen) - xpos; + dp.y = bound (0, ypos + delta.y, b.height - ylen) - ypos; + } else if (sender == topLeftDrag) { + dp.x = bound (0, xpos + delta.x, xpos + xlen - delta.x - 1) - xpos; + dp.y = bound (0, ypos + delta.y, ypos + ylen - delta.y - 1) - ypos; + ds.x = bound (1, xlen - delta.x, b.width - xpos - delta.x) - xlen; + ds.y = bound (1, ylen - delta.y, b.height - ypos - delta.y) - ylen; + } else if (sender == topRightDrag) { + dp.y = bound (0, ypos + delta.y, ypos + ylen - delta.y - 1) - ypos; + ds.x = bound (1, xlen + delta.x, b.width - xpos) - xlen; + ds.y = bound (1, ylen - delta.y, b.height - ypos - delta.y) - ylen; + } else if (sender == leftDrag) { + dp.x = bound (0, xpos + delta.x, xpos + xlen - delta.x - 1) - xpos; + ds.x = bound (1, xlen - delta.x, b.width - xpos - delta.x) - xlen; + } else if (sender == rightDrag) { + ds.x = bound (1, xlen + delta.x, b.width - xpos) - xlen; + } else if (sender == bottomLeftDrag) { + dp.x = bound (0, xpos + delta.x, xpos + xlen - delta.x - 1) - xpos; + ds.x = bound (1, xlen - delta.x, b.width - xpos - delta.x) - xlen; + ds.y = bound (1, ylen + delta.y, b.height - ypos) - ylen; + } else if (sender == bottomRightDrag) { + ds.x = bound (1, xlen + delta.x, b.width - xpos) - xlen; + ds.y = bound (1, ylen + delta.y, b.height - ypos) - ylen; + } else if (sender == bottomDrag) { + ds.y = bound (1, ylen + delta.y, b.height - ypos) - ylen; + } + int save_state = state; + state &= ~sfDrawn; + [self move:dp andResize:{ds.x, ds.y}]; + state = save_state; + [self redraw]; +} + +-setContext: (id) context +{ + return self; +} + +-move:(Point)dpos andResize:(Extent)dsize +{ + int save_state = state; + state &= ~sfDrawn; + + Point pos = self.pos; + Extent size = self.size; + [super resize: dsize]; + [super move: dpos]; + // need to move the panel both before and after the resize to avoid + // HoM effects or window/panel possition errors + move_panel (panel, xpos, ypos); + [(id)textContext resizeTo: self.size]; + replace_panel (panel, [(id)textContext window]); + move_panel (panel, xpos, ypos); + + dsize = {self.size.width - size.width, self.size.height - size.height}; + [objects resize:dsize]; + + state = save_state; + [self redraw]; + return self; +} + +-move: (Point) delta +{ + int save_state = state; + state &= ~sfDrawn; + [super move: delta]; + move_panel (panel, xpos, ypos); + state = save_state; + [self redraw]; + return self; +} + +-resize: (Extent) delta +{ + Extent size = self.size; + [super resize:delta]; + delta = {self.size.width - size.width, self.size.height - size.height}; + [(id)textContext resizeTo: self.size]; + replace_panel (panel, [(id)textContext window]); + [objects resize:delta]; + return self; +} + +-handleEvent: (qwaq_event_t *) event +{ + [super handleEvent: event]; + + int offset = event.what & qe_positional; + if (offset) { + event.mouse.x -= xpos; + event.mouse.y -= ypos; + } + [objects handleEvent: event]; + if (offset) { + event.mouse.x += xpos; + event.mouse.y += ypos; + } + return self; +} + +-takeFocus +{ + [super takeFocus]; + [objects takeFocus]; + return self; +} + +-loseFocus +{ + [super loseFocus]; + [objects loseFocus]; + return self; +} + +-raise +{ + top_panel (panel); + [self redraw]; + return self; +} + +-insert: (View *) view +{ + [objects insert: view]; + return self; +} + +-insertDrawn: (View *) view +{ + [objects insertDrawn: view]; + return self; +} + +-insertSelected: (View *) view +{ + [objects insertSelected: view]; + return self; +} + +-setBackground: (int) ch +{ + [(id)textContext bkgd: ch]; + return self; +} + +-updateAbsPos: (Point) absPos +{ + [super updateAbsPos: absPos]; + [objects updateAbsPos: absRect.offset]; + return self; +} + +-draw +{ + static box_sides_t box_sides = { + ACS_VLINE, ACS_VLINE, + ACS_HLINE, ACS_HLINE, + }; + static box_corners_t box_corners = { + ACS_ULCORNER, ACS_URCORNER, + ACS_LLCORNER, ACS_LRCORNER, + }; + if (box_sides.ls == ACS_VLINE) { + int *foo = &box_sides.ls; + for (int i = 0; i < 8; i++) { + foo[i] = acs_char (foo[i]); + } + } + [super draw]; + [(id)textContext border: box_sides, box_corners]; + [objects draw]; + return self; +} + +-redraw +{ + if (state & sfDrawn) { + [owner redraw]; + } + return self; +} + +- (void) mvprintf: (Point) pos, string fmt, ... +{ + [textContext mvvprintf: pos, fmt, @args]; +} + +- (void) mvvprintf: (Point) pos, string fmt, @va_list args +{ + [textContext mvvprintf: pos, fmt, args]; +} + +- (void) mvaddch: (Point) pos, int ch +{ + [textContext mvaddch: pos, ch]; +} +@end diff --git a/ruamoko/qwaq/z-transform.r b/ruamoko/qwaq/z-transform.r new file mode 100644 index 000000000..328e8fd3c --- /dev/null +++ b/ruamoko/qwaq/z-transform.r @@ -0,0 +1,50 @@ +#include + +void printf (string fnt, ...) = #0; + +#define SAMPLES 4 +float output[SAMPLES]; +float input[SAMPLES]; + +float T=0.1; +float w=2; +float a=0.5; + +float A[3], B[3]; + +void +z_transform (float *y, float *x, float *A, float *B, int n, int zind) +{ + zind %= SAMPLES; + float c = x[zind] * A[0]; + for (int i = 1; i < n; i++) { + int z = (SAMPLES + zind - i) % SAMPLES; + c += x[z] * A[i] - y[z] * B[i]; + } + y[zind] = c / B[0]; +} + +int +main () +{ + float e = exp (-a*T); + float c = cos (w*T); + float s = sin (w*T); + + B[0] = 1; + B[1] = -2 * e * c; + B[2] = e * e; + + A[0] = 0; + A[1] = 1 - e*(c + (a/w)*s); + A[2] = e*e + e*((a/w)*s - c); + + for (int i = 0; i < 200; i++) { + int ind = i % SAMPLES; + input[ind] = 1;//i ? 0 : 1; + z_transform (output, input, A, B, 3, i); + printf ("%2d %7.4f %7.4f\n", i, input[ind], output[ind]); + } + + return 0; +} diff --git a/ruamoko/scheme/Lexer.r b/ruamoko/scheme/Lexer.r index 20b026599..32d2958a5 100644 --- a/ruamoko/scheme/Lexer.r +++ b/ruamoko/scheme/Lexer.r @@ -1,5 +1,6 @@ #include "Lexer.h" #include "Number.h" +#include "legacy_string.h" #include "string.h" #include "Boolean.h" #include "Error.h" diff --git a/ruamoko/scheme/Machine.h b/ruamoko/scheme/Machine.h index 7c99a2433..a7da59f61 100644 --- a/ruamoko/scheme/Machine.h +++ b/ruamoko/scheme/Machine.h @@ -11,7 +11,7 @@ { state_t state; SchemeObject *value; - hashtab_t globals; + hashtab_t *globals; SchemeObject *all_globals; } - (void) loadCode: (CompiledCode *) code; diff --git a/ruamoko/scheme/Makefile.am b/ruamoko/scheme/Makefile.am deleted file mode 100644 index 75f784ef5..000000000 --- a/ruamoko/scheme/Makefile.am +++ /dev/null @@ -1,63 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -pkglibdir=$(datarootdir)/qfcc/lib - -QFCC_DEP=$(top_builddir)/tools/qfcc/source/qfcc$(EXEEXT) -QFCC=$(QFCC_DEP) -QCFLAGS=-qq -O -g -Werror -Wall -Wno-integer-divide --no-default-paths -QCPPFLAGS=$(AM_CPPFLAGS) -PAK=$(top_builddir)/tools/pak/pak$(EXEEXT) -GZIP=if echo $@ | grep -q .gz; then gzip -f `basename $@ .gz`; if test -f `basename $@ .dat.gz`.sym; then gzip -f `basename $@ .dat.gz`.sym; fi; fi -GZ=@progs_gz@ -# BSD make can't handle $(shell foo) directives, and GNU make can't handle |= -# so we have to bite the bullet and pass this to the shell every time. -STRIP=`echo -n $(srcdir)/ | sed -e 's/[^/]//g' | wc -c` - -RANLIB=touch - -AM_CPPFLAGS= -I$(top_srcdir)/ruamoko/include -I$(top_srcdir)/include - -scheme_libs=libscheme.a -libs=$(scheme_libs) -data=$(scheme_data) - -pkglib_LIBRARIES= $(libs) -EXTRA_LIBRARIES= $(scheme_libs) -#pkgdata_DATA= $(data) -EXTRA_DATA = $(scheme_data) - -EXTRA_DIST = \ - BaseContinuation.h Boolean.h CompiledCode.h Compiler.h Cons.h \ - Continuation.h Error.h Frame.h Instruction.h Lambda.h Lexer.h \ - Machine.h Nil.h Number.h Parser.h Primitive.h Procedure.h \ - SchemeObject.h SchemeString.h Scope.h Symbol.h Void.h builtins.h \ - debug.h defs.h state.h \ - \ - main.r defs.r - -SUFFIXES=.qc .qfo .r -.r.qfo: - $(QFCC) $(QCFLAGS) $(QCPPFLAGS) -p $(STRIP) -c -o $@ $< -.r.o: - $(QFCC) $(QCFLAGS) $(QCPPFLAGS) -p $(STRIP) -c -o $@ $< - -libscheme_a_SOURCES=\ - SchemeObject.r Cons.r Number.r SchemeString.r Symbol.r Lexer.r Parser.r \ - Nil.r Procedure.r Primitive.r Lambda.r Scope.r Instruction.r builtins.r \ - Frame.r CompiledCode.r Compiler.r Continuation.r Machine.r Void.r \ - Error.r Boolean.r BaseContinuation.r -libscheme_a_AR=$(PAK) -cf - -scheme_data=\ - main.dat$(GZ) - -scheme_src=\ - main.r defs.r - -scheme_obj=$(scheme_src:.qc=.o) - -main.dat$(GZ): $(scheme_obj) $(QFCC_DEP) ../lib/libcsqc.a ../lib/libr.a libscheme.a - $(QFCC) $(QCFLAGS) -p $(STRIP) -o main.dat $(scheme_obj) libscheme.a ../lib/libcsqc.a ../lib/libr.a - $(GZIP) - -CLEANFILES= *.dat *.sym *.gz *.qfo *.o diff --git a/ruamoko/scheme/Makemodule.am b/ruamoko/scheme/Makemodule.am new file mode 100644 index 000000000..19b23cae4 --- /dev/null +++ b/ruamoko/scheme/Makemodule.am @@ -0,0 +1,88 @@ +ruamoko_scheme_libs=ruamoko/scheme/libscheme.a +ruamoko_scheme_libexec=ruamoko/scheme/main.dat$(EXEEXT) + +ruamoko_lib_LIBRARIES += $(ruamoko_scheme_libs) +EXTRA_LIBRARIES += $(ruamoko_scheme_libs) + +noinst_PROGRAMS += $(ruamoko_scheme_libexec) +EXTRA_PROGRAMS += $(ruamoko_scheme_libexec) + +ruamoko_scheme_libscheme_a_SOURCES=\ + ruamoko/scheme/SchemeObject.r \ + ruamoko/scheme/Cons.r \ + ruamoko/scheme/Number.r \ + ruamoko/scheme/SchemeString.r \ + ruamoko/scheme/Symbol.r \ + ruamoko/scheme/Lexer.r \ + ruamoko/scheme/Parser.r \ + ruamoko/scheme/Nil.r \ + ruamoko/scheme/Procedure.r \ + ruamoko/scheme/Primitive.r \ + ruamoko/scheme/Lambda.r \ + ruamoko/scheme/Scope.r \ + ruamoko/scheme/Instruction.r \ + ruamoko/scheme/builtins.r \ + ruamoko/scheme/Frame.r \ + ruamoko/scheme/CompiledCode.r \ + ruamoko/scheme/Compiler.r \ + ruamoko/scheme/Continuation.r \ + ruamoko/scheme/Machine.r \ + ruamoko/scheme/Void.r \ + ruamoko/scheme/Error.r \ + ruamoko/scheme/Boolean.r \ + ruamoko/scheme/BaseContinuation.r +ruamoko_scheme_libscheme_a_dep=$(call qcautodep,$(ruamoko_scheme_libscheme_a_SOURCES)) +ruamoko_scheme_libscheme_a_AR=$(PAK) -cf +EXTRA_ruamoko_scheme_libscheme_a_DEPENDENCIES=$(PAK) +include $(ruamoko_scheme_libscheme_a_dep) # am--include-marker +r_depfiles_remade += $(ruamoko_scheme_libscheme_a_dep) + +ruamoko_scheme_src=\ + ruamoko/scheme/main.r \ + ruamoko/scheme/defs.r + +ruamoko_scheme_main_dat_SOURCES=$(ruamoko_scheme_src) +ruamoko_scheme_main_obj=$(ruamoko_scheme_main_dat_SOURCES:.r=.o) +ruamoko_scheme_main_dep=$(call qcautodep,$(ruamoko_scheme_main_dat_SOURCES)) +ruamoko/scheme/main.dat$(EXEEXT): $(ruamoko_scheme_main_obj) $(QFCC_DEP) ruamoko/scheme/libscheme.a ruamoko/lib/libcsqc.a ruamoko/lib/libr.a + $(V_QFCCLD)$(QLINK) -o $@ $(ruamoko_scheme_main_obj) -Lruamoko/scheme -lscheme -lcsqc -lr +include $(ruamoko_scheme_main_dep) # am--include-marker +r_depfiles_remade += $(ruamoko_scheme_main_dep) + +ruamoko/scheme/main.sym: ruamoko/scheme/main.dat$(EXEEXT) + +EXTRA_DIST += \ + ruamoko/scheme/BaseContinuation.h \ + ruamoko/scheme/Boolean.h \ + ruamoko/scheme/CompiledCode.h \ + ruamoko/scheme/Compiler.h \ + ruamoko/scheme/Cons.h \ + ruamoko/scheme/Continuation.h \ + ruamoko/scheme/Error.h \ + ruamoko/scheme/Frame.h \ + ruamoko/scheme/Instruction.h \ + ruamoko/scheme/Lambda.h \ + ruamoko/scheme/Lexer.h \ + ruamoko/scheme/Machine.h \ + ruamoko/scheme/Nil.h \ + ruamoko/scheme/Number.h \ + ruamoko/scheme/Parser.h \ + ruamoko/scheme/Primitive.h \ + ruamoko/scheme/Procedure.h \ + ruamoko/scheme/SchemeObject.h \ + ruamoko/scheme/SchemeString.h \ + ruamoko/scheme/Scope.h \ + ruamoko/scheme/Symbol.h \ + ruamoko/scheme/Void.h \ + ruamoko/scheme/builtins.h \ + ruamoko/scheme/debug.h \ + ruamoko/scheme/defs.h \ + ruamoko/scheme/state.h \ + ruamoko/scheme/main.r \ + ruamoko/scheme/defs.r +CLEANFILES += \ + ruamoko/scheme/*.dat \ + ruamoko/scheme/*.sym +DISTCLEANFILES += \ + $(ruamoko_scheme_libscheme_a_dep) \ + $(ruamoko_scheme_main_dep) diff --git a/ruamoko/scheme/Number.r b/ruamoko/scheme/Number.r index 53e7f6975..530bcd46a 100644 --- a/ruamoko/scheme/Number.r +++ b/ruamoko/scheme/Number.r @@ -1,4 +1,5 @@ #include "Number.h" +#include "legacy_string.h" #include "string.h" @implementation Number diff --git a/ruamoko/scheme/SchemeObject.h b/ruamoko/scheme/SchemeObject.h index 262c700f4..e831b50bd 100644 --- a/ruamoko/scheme/SchemeObject.h +++ b/ruamoko/scheme/SchemeObject.h @@ -14,6 +14,7 @@ int line; string source; } ++ (void) finishCollecting; + (void) collectCheckPoint; - (void) mark; - (void) markReachable; diff --git a/ruamoko/scheme/Symbol.r b/ruamoko/scheme/Symbol.r index 0704b3a50..2adfc6a25 100644 --- a/ruamoko/scheme/Symbol.r +++ b/ruamoko/scheme/Symbol.r @@ -16,7 +16,7 @@ void SymbolFree (void *ele, void *data) [s release]; } -hashtab_t symbols; +hashtab_t *symbols; Symbol *lparen; Symbol *rparen; Symbol *quote; @@ -45,7 +45,7 @@ Symbol *symbol (string str) { local Symbol *res; - if ((res = Hash_Find (symbols, s))) { + if ((res = (Symbol *) Hash_Find (symbols, s))) { return res; } else { res = (Symbol*) [self newFromString: s]; diff --git a/ruamoko/scheme/defs.h b/ruamoko/scheme/defs.h index a1d691684..7f1e4289c 100644 --- a/ruamoko/scheme/defs.h +++ b/ruamoko/scheme/defs.h @@ -1,13 +1,13 @@ -@extern void (string str) print = #0; -@extern int () errno = #0; -@extern string (int err) strerror = #0; -@extern int (...) open = #0; // string path, float flags[, float mode] -@extern int (int handle) close = #0; -@extern string read (int handle, int count, int *result) = #0; -@extern int (int handle, string buffer, int count) write = #0; -@extern int (int handle, int pos, int whence) seek = #0; +@extern void (string str) print; +@extern int () errno; +@extern string (int err) strerror; +@extern int (...) open; // string path, float flags[, float mode] +@extern int (int handle) close; +@extern string read (int handle, int count, int *result); +@extern int (int handle, string buffer, int count) write; +@extern int (int handle, int pos, int whence) seek; -@extern void() traceon = #0; // turns statment trace on -@extern void() traceoff = #0; +@extern void() traceon; // turns statment trace on +@extern void() traceoff; -@extern void (...) printf = #0; +@extern void (...) printf; diff --git a/ruamoko/scheme/defs.r b/ruamoko/scheme/defs.r index 943ef9173..1c638232d 100644 --- a/ruamoko/scheme/defs.r +++ b/ruamoko/scheme/defs.r @@ -3,7 +3,7 @@ int () errno = #0; string (int err) strerror = #0; int (...) open = #0; // string path, float flags[, float mode] int (int handle) close = #0; -string (int handle, int count, int []result) read = #0; +string read (int handle, int count, int *result) = #0; int (int handle, string buffer, int count) write = #0; int (int handle, int pos, int whence) seek = #0; diff --git a/ruamoko/scheme/main.r b/ruamoko/scheme/main.r index fb1fa3e33..be7d8bca6 100644 --- a/ruamoko/scheme/main.r +++ b/ruamoko/scheme/main.r @@ -22,23 +22,23 @@ string readfile (string filename) str_copy(res, acc); return res; } - -int main (int argc, string []argv) +int main (int argc, string *argv) { - local Parser parser; - local CompiledCode code; - local Compiler comp; - local Machine vm; - local Lambda lm; - local SchemeObject stuff, res; + local Parser *parser; + local CompiledCode *code; + local Compiler *comp; + local Machine *vm; + local Lambda *lm; + local SchemeObject *stuff, *res; + local Error *err; if (argc < 1) { return -1; } //traceon(); - + parser = [Parser newFromSource: readfile(argv[1]) file: argv[1]]; vm = [Machine new]; [vm makeRootCell]; @@ -46,26 +46,29 @@ int main (int argc, string []argv) builtin_addtomachine (vm); while ((stuff = [parser read])) { if ([stuff isError]) { - printf(">> %s: %i\n", [stuff source], [stuff line]); - printf(">> Error (%s): %s\n", [stuff type], [stuff message]); + err = (Error *) stuff; + printf(">> %s: %i\n", [err source], [err line]); + printf(">> Error (%s): %s\n", [err type], [err message]); return -1; } comp = [Compiler newWithLambda: cons ([Symbol forString: "lambda"], cons ([Nil nil], cons(stuff, [Nil nil]))) scope: nil]; - code = (CompiledCode) [comp compile]; + code = (CompiledCode *) [comp compile]; if ([code isError]) { - printf(">> %s: %i\n", [code source], [code line]); - printf(">> Error (%s): %s\n", [code type], [code message]); + err = (Error *) code; + printf(">> %s: %i\n", [err source], [err line]); + printf(">> Error (%s): %s\n", [err type], [err message]); return -1; } lm = [Lambda newWithCode: code environment: nil]; [lm invokeOnMachine: vm]; res = [vm run]; if ([res isError]) { - printf(">> %s: %i\n", [res source], [res line]); - printf(">> Error (%s): %s\n", [res type], [res message]); + err = (Error *) res; + printf(">> %s: %i\n", [err source], [err line]); + printf(">> Error (%s): %s\n", [err type], [err message]); return -1; } [vm reset]; diff --git a/tools/Makefile.am b/tools/Makefile.am deleted file mode 100644 index 008638b4a..000000000 --- a/tools/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -SUBDIRS=@tools_dirs@ -DIST_SUBDIRS=bsp2img carne pak qfbsp qfcc qflight qflmp qfmodelgen qfspritegen qfvis qwaq wad wav -EXTRA_DIST= \ - io_mesh_qfmdl/export_mdl.py io_mesh_qfmdl/import_mdl.py \ - io_mesh_qfmdl/__init__.py io_mesh_qfmdl/mdl.py io_mesh_qfmdl/qfplist.py \ - io_mesh_qfmdl/quakepal.py diff --git a/tools/Makemodule.am b/tools/Makemodule.am new file mode 100644 index 000000000..7a3805526 --- /dev/null +++ b/tools/Makemodule.am @@ -0,0 +1,20 @@ +include tools/bsp2img/Makemodule.am +include tools/carne/Makemodule.am +include tools/pak/Makemodule.am +include tools/qfbsp/Makemodule.am +include tools/qfcc/Makemodule.am +include tools/qflight/Makemodule.am +include tools/qflmp/Makemodule.am +include tools/qfmodelgen/Makemodule.am +include tools/qfspritegen/Makemodule.am +include tools/qfvis/Makemodule.am +include tools/wad/Makemodule.am +include tools/wav/Makemodule.am + +EXTRA_DIST += \ + tools/io_mesh_qfmdl/export_mdl.py \ + tools/io_mesh_qfmdl/import_mdl.py \ + tools/io_mesh_qfmdl/__init__.py \ + tools/io_mesh_qfmdl/mdl.py \ + tools/io_mesh_qfmdl/qfplist.py \ + tools/io_mesh_qfmdl/quakepal.py diff --git a/tools/bsp2img/Makefile.am b/tools/bsp2img/Makemodule.am similarity index 60% rename from tools/bsp2img/Makefile.am rename to tools/bsp2img/Makemodule.am index ceaeca266..24b6594c2 100644 --- a/tools/bsp2img/Makefile.am +++ b/tools/bsp2img/Makemodule.am @@ -2,10 +2,9 @@ BSP2IMG_LIBS=@BSP2IMG_LIBS@ BSP2IMG_DEPS=@BSP2IMG_DEPS@ BSP2IMG_INCS=@BSP2IMG_INCS@ -AM_CPPFLAGS= -I$(top_srcdir)/include $(BSP2IMG_INCS) +EXTRA_PROGRAMS += bsp2img +bin_PROGRAMS += @BSP2IMG_TARGETS@ -bin_PROGRAMS=bsp2img - -bsp2img_SOURCES= bsp2img.c +bsp2img_SOURCES= tools/bsp2img/bsp2img.c bsp2img_LDADD= $(BSP2IMG_LIBS) bsp2img_DEPENDENCIES= $(BSP2IMG_DEPS) diff --git a/tools/bsp2img/bsp2img.c b/tools/bsp2img/bsp2img.c index 649fe1427..1742034a8 100644 --- a/tools/bsp2img/bsp2img.c +++ b/tools/bsp2img/bsp2img.c @@ -35,6 +35,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "QF/bspfile.h" #include "QF/cmd.h" #include "QF/cvar.h" +#include "QF/image.h" #include "QF/mathlib.h" #include "QF/pcx.h" #include "QF/png.h" @@ -73,8 +74,8 @@ Thanks fly to Id for a hackable game! :) /* MW */ typedef struct edge_extra_t { - long num_face_ref; - long ref_faces[MAX_REF_FACES]; // which faces are referenced + uint32_t num_face_ref; + uint32_t ref_faces[MAX_REF_FACES]; // which faces are referenced dvertex_t ref_faces_normal[MAX_REF_FACES]; // normal of referenced // faces int ref_faces_area[MAX_REF_FACES]; // area of the referenced faces @@ -531,7 +532,7 @@ create_image (long width, long height) static image_t * render_map (bsp_t *bsp) { - long i = 0, j = 0, k = 0, x = 0; + uint32_t j = 0, k = 0, x = 0; dvertex_t *vertexlist, *vert1, *vert2; dedge_t *edgelist; @@ -563,18 +564,18 @@ render_map (bsp_t *bsp) edge_extra = malloc (sizeof (struct edge_extra_t) * bsp->numedges); if (edge_extra == NULL) { fprintf (stderr, "Error allocating %ld bytes for extra edge info.", - (long) sizeof (struct edge_extra_t) * bsp->numedges); + (long) (sizeof (struct edge_extra_t) * bsp->numedges)); exit (2); } /* initialize the array */ - for (i = 0; i < bsp->numedges; i++) { + for (unsigned i = 0; i < bsp->numedges; i++) { edge_extra[i].num_face_ref = 0; - for (j = 0; j < MAX_REF_FACES; j++) { + for (int j = 0; j < MAX_REF_FACES; j++) { edge_extra[i].ref_faces[j] = -1; } } - for (i = 0; i < bsp->numfaces; i++) { + for (unsigned i = 0; i < bsp->numfaces; i++) { /* calculate the normal (cross product) */ /* starting edge: edgelist[ledges[facelist[i].firstedge]] */ /* number of edges: facelist[i].numedges; */ @@ -647,7 +648,7 @@ render_map (bsp_t *bsp) printf ("Collecting min/max\n"); /* Collect min and max */ - for (i = 0; i < bsp->numvertexes; i++) { + for (unsigned i = 0; i < bsp->numvertexes; i++) { /* Ugly hack - flip stuff around for different camera angles */ switch (options.camera_axis) { @@ -672,14 +673,14 @@ render_map (bsp_t *bsp) vertexlist[i].X = -vertexlist[i].X; tempf = vertexlist[i].Z; vertexlist[i].Z = vertexlist[i].Y; - vertexlist[i].Y = tempf;; + vertexlist[i].Y = tempf; break; case 2: /* +Y -- (-x <--> +x; +y out of screen, +z up) */ tempf = vertexlist[i].Z; vertexlist[i].Z = -vertexlist[i].Y; - vertexlist[i].Y = tempf;; + vertexlist[i].Y = tempf; break; case -3: /* -Z -- negate X and Z (ie. 180 rotate @@ -788,7 +789,7 @@ render_map (bsp_t *bsp) fprintf (stderr, "Plotting edges..."); k = 0; drawcol = (options.edgeremove) ? 64 : 32; - for (i = 0; i < bsp->numedges; i++) { + for (unsigned i = 0; i < bsp->numedges; i++) { /* Do a check on this line ... see if we keep this line or not */ /* run through all referenced faces */ @@ -820,7 +821,7 @@ render_map (bsp_t *bsp) vert1 = &vertexlist[edgelist[i].v[0]]; vert2 = &vertexlist[edgelist[i].v[1]]; SUB (*vert1, *vert2, vect); - if (abs (tempf) < options.flat_threshold + if (fabsf (tempf) < options.flat_threshold && usearea > options.area_threshold && sqrt (DOT (vect, vect)) > options.linelen_threshold) { float offs0, offs1; @@ -840,15 +841,15 @@ render_map (bsp_t *bsp) } } - printf ("%d edges plotted", bsp->numedges); + printf ("%zd edges plotted", bsp->numedges); if (options.edgeremove) { - printf (" (%ld edges removed)\n", k); + printf (" (%u edges removed)\n", k); } else { printf ("\n"); } /* Little gradient */ - for (i = 0; i <= 255; i++) { + for (unsigned i = 0; i <= 255; i++) { // across from top left plotpoint (image, i, 0, 255 - i); // down from top left @@ -872,8 +873,8 @@ render_map (bsp_t *bsp) /* Negate image if necessary */ if (options.negative_image) { - for (i = 0; i < image->height; i++) { - for (j = 0; j < image->width; j++) { + for (int i = 0; i < image->height; i++) { + for (int j = 0; j < image->width; j++) { image->image[i * image->width + j] = 255 - image->image[i * image->width + j]; } @@ -888,17 +889,29 @@ render_map (bsp_t *bsp) static void write_png (image_t *image) { - byte *data, *in, *out, b; + byte *in, *out, b; int size = image->width * image->height; + tex_t tex = { + .width = image->width, + .height = image->height, + .format = tex_rgb, + .loaded = 1, + .data = malloc (size * 3), + }; - out = data = malloc (size * 3); + out = tex.data; for (in = image->image; in - image->image < size; in++) { b = *in; *out++ = b; *out++ = b; *out++ = b; } - WritePNG (options.outf_name, data, image->width, image->height); + QFile *file = Qopen (options.outf_name, "wb"); + if (file) { + WritePNG (file, &tex); + Qclose (file); + } + free (tex.data); } static void @@ -925,7 +938,7 @@ write_pcx (image_t *image) Sys_Init (); - Memory_Init (malloc (MEMSIZE), MEMSIZE); + Memory_Init (Sys_Alloc (MEMSIZE), MEMSIZE); pcx = EncodePCX (image->image, image->width, image->height, image->width, palette, false, &pcx_len); if (Qwrite (outfile, pcx, pcx_len) != pcx_len) { diff --git a/tools/carne/Makefile.am b/tools/carne/Makefile.am deleted file mode 100644 index bec57857d..000000000 --- a/tools/carne/Makefile.am +++ /dev/null @@ -1,13 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -CARNE_LIBS=@CARNE_LIBS@ -CARNE_DEPS=@CARNE_DEPS@ -PAK_INCS=@CARNE_INCS@ - -AM_CPPFLAGS= -I$(top_srcdir)/include $(CARNE_INCS) - -noinst_PROGRAMS= carne - -carne_SOURCES= main.c -carne_LDADD= $(CARNE_LIBS) -carne_DEPENDENCIES= $(CARNE_DEPS) diff --git a/tools/carne/Makemodule.am b/tools/carne/Makemodule.am new file mode 100644 index 000000000..fe5862c0e --- /dev/null +++ b/tools/carne/Makemodule.am @@ -0,0 +1,9 @@ +CARNE_LIBS=@CARNE_LIBS@ +CARNE_DEPS=@CARNE_DEPS@ + +EXTRA_PROGRAMS += carne +noinst_PROGRAMS += @CARNE_TARGETS@ + +carne_SOURCES= tools/carne/main.c +carne_LDADD= $(CARNE_LIBS) +carne_DEPENDENCIES= $(CARNE_DEPS) diff --git a/tools/carne/main.c b/tools/carne/main.c index 8ecc723cd..36fc30e7b 100644 --- a/tools/carne/main.c +++ b/tools/carne/main.c @@ -1,3 +1,7 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + #include #include #include @@ -17,7 +21,7 @@ #include "gib_thread.h" #include "gib_parse.h" -static qboolean carne_done = false; +static bool carne_done = false; static int carne_exitcode = 0; static void diff --git a/tools/cross/mingw/cross-configure.sh b/tools/cross/mingw/cross-configure.sh index 67287e4f1..869ec4851 100755 --- a/tools/cross/mingw/cross-configure.sh +++ b/tools/cross/mingw/cross-configure.sh @@ -1,19 +1,21 @@ #!/bin/sh -x set -e -mkdir -p native i686-pc-mingw32 +mkdir -p native i686-w64-mingw32.static cd native ../../configure \ + --enable-silent-rules \ --disable-shared \ --without-clients \ --without-servers \ - --with-tools=qfcc,pak -cd ../i686-pc-mingw32 + --with-tools=qfcc,pak,qwaq +cd ../i686-w64-mingw32.static export MINGW=/opt/mxe -export MINGW_USR=$MINGW/usr/i686-pc-mingw32 +export MINGW_USR=$MINGW/usr/i686-w64-mingw32.static export PKG_CONFIG_LIBDIR=$MINGW_USR/lib/pkgconfig export PKG_CONFIG_PATH=$MINGW_USR/local/lib/pkgconfig export PATH=$MINGW/usr/bin:$PATH +export QCSYSPREFIX=$MINGW_USR ../../configure \ - --host=i686-pc-mingw32 \ + --host=i686-w64-mingw32.static \ --disable-shared \ $* diff --git a/tools/cross/mingw/cross-make.sh b/tools/cross/mingw/cross-make.sh index 61d0fe7f4..6d405a09e 100755 --- a/tools/cross/mingw/cross-make.sh +++ b/tools/cross/mingw/cross-make.sh @@ -3,14 +3,15 @@ set -e if test -d native; then cd native make $* - cd ../i686-pc-mingw32 - ln -fs ../native/tools/qfcc/source/qfcc . - ln -fs ../native/tools/pak/pak . + cd ../i686-w64-mingw32.static + ln -fs ../native/qfcc . + ln -fs ../native/pak . + ln -fs ../native/ruamoko/qwaq/qwaq-cmd . fi export MINGW=/opt/mxe -export MINGW_USR=$MINGW/usr/i686-pc-mingw32 +export MINGW_USR=$MINGW/usr/i686-w64-mingw32.static export PKG_CONFIG_LIBDIR=$MINGW_USR/lib/pkgconfig export PKG_CONFIG_PATH=$MINGW_USR/local/lib/pkgconfig export PATH=$MINGW/usr/bin:$PATH -make PAK='$(top_builddir)/pak' QFCC='$(top_builddir)/qfcc' $* +make PAK='$(top_builddir)/pak' QFCC='$(top_builddir)/qfcc' QWAQ='$(top_builddir)/qwaq-cmd' VKGENUSRINC=$MINGW_USR/include $* diff --git a/tools/cross/mingw64/cross-configure.sh b/tools/cross/mingw64/cross-configure.sh index 73f76c677..f1945d685 100755 --- a/tools/cross/mingw64/cross-configure.sh +++ b/tools/cross/mingw64/cross-configure.sh @@ -1,19 +1,22 @@ #!/bin/sh -x set -e -mkdir -p native x86_64-w64-mingw32 +mkdir -p native x86_64-w64-mingw32.static cd native ../../configure \ + --enable-silent-rules \ --disable-shared \ + --disable-optimize \ --without-clients \ --without-servers \ - --with-tools=qfcc,pak -cd ../x86_64-w64-mingw32 + --with-tools=qfcc,pak,qwaq +cd ../x86_64-w64-mingw32.static export MINGW=/opt/mxe -export MINGW_USR=$MINGW/usr/x86_64-w64-mingw32 +export MINGW_USR=$MINGW/usr/x86_64-w64-mingw32.static export PKG_CONFIG_LIBDIR=$MINGW_USR/lib/pkgconfig export PKG_CONFIG_PATH=$MINGW_USR/local/lib/pkgconfig export PATH=$MINGW/usr/bin:$PATH +export QCSYSPREFIX=$MINGW_USR ../../configure \ - --host=x86_64-w64-mingw32 \ + --host=x86_64-w64-mingw32.static \ --disable-shared \ $* diff --git a/tools/cross/mingw64/cross-make.sh b/tools/cross/mingw64/cross-make.sh index 9ecef230e..0f3481592 100755 --- a/tools/cross/mingw64/cross-make.sh +++ b/tools/cross/mingw64/cross-make.sh @@ -3,14 +3,15 @@ set -e if test -d native; then cd native make $* - cd ../x86_64-w64-mingw32 - ln -fs ../native/tools/qfcc/source/qfcc . - ln -fs ../native/tools/pak/pak . + cd ../x86_64-w64-mingw32.static + ln -fs ../native/qfcc . + ln -fs ../native/pak . + ln -fs ../native/ruamoko/qwaq/qwaq-cmd . fi export MINGW=/opt/mxe -export MINGW_USR=$MINGW/usr/x86_64-w64-mingw32 +export MINGW_USR=$MINGW/usr/x86_64-w64-mingw32.static export PKG_CONFIG_LIBDIR=$MINGW_USR/lib/pkgconfig export PKG_CONFIG_PATH=$MINGW_USR/local/lib/pkgconfig export PATH=$MINGW/usr/bin:$PATH -make PAK='$(top_builddir)/pak' QFCC='$(top_builddir)/qfcc' $* +make PAK='$(top_builddir)/pak' QFCC='$(top_builddir)/qfcc' QWAQ='$(top_builddir)/qwaq-cmd' VKGENUSRINC=$MINGW_USR/include $* diff --git a/tools/gl_stub/build b/tools/gl_stub/build index 113879f94..9b31d562e 100755 --- a/tools/gl_stub/build +++ b/tools/gl_stub/build @@ -1,4 +1,4 @@ #! /bin/sh -gcc -o GLstub.so -g -fPIC -shared -O3 -ffast-math -funroll-loops -fomit-frame-pointer -fexpensive-optimizations gl_stub.c gls_norm.c gls_trace.c +gcc -o GLstub.so -I../../include -g -fPIC -shared -O3 -ffast-math -funroll-loops -fomit-frame-pointer -fexpensive-optimizations gl_stub.c gls_norm.c gls_trace.c -Wall -Werror #gcc -o GLstub.so -g -fPIC -shared gl_stub.c gls_norm.c gls_trace.c diff --git a/tools/gl_stub/gl_stub.c b/tools/gl_stub/gl_stub.c index 24f123708..97ccfd191 100644 --- a/tools/gl_stub/gl_stub.c +++ b/tools/gl_stub/gl_stub.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -8,11 +9,13 @@ #include "QF/GL/defines.h" #include "QF/GL/extensions.h" #include "QF/GL/types.h" +#include "QF/GLSL/types.h" #include "QF/hash.h" typedef XID GLXDrawable; typedef struct __GLXcontextRec *GLXContext; +typedef struct __GLXFBConfigRec *GLXFBConfig; #define TRACE do { \ puts (__FUNCTION__);\ @@ -28,6 +31,7 @@ ret GLAPIENTRY norm_##name args; #define QFGL_NEED(ret, name, args) \ ret GLAPIENTRY norm_##name args; #include "QF/GL/qf_funcs_list.h" +#include "QF/GLSL/qf_funcs_list.h" #undef QFGL_NEED #undef QFGL_WANT @@ -36,6 +40,7 @@ ret GLAPIENTRY trace_##name args; #define QFGL_NEED(ret, name, args) \ ret GLAPIENTRY trace_##name args; #include "QF/GL/qf_funcs_list.h" +#include "QF/GLSL/qf_funcs_list.h" #undef QFGL_NEED #undef QFGL_WANT @@ -53,6 +58,7 @@ static gl_stub_t gl_stub_funcs[] = { #define QFGL_NEED(ret, name, args) \ {#name, norm_##name, trace_##name}, #include "QF/GL/qf_funcs_list.h" +#include "QF/GLSL/qf_funcs_list.h" #undef QFGL_NEED #undef QFGL_WANT }; @@ -144,3 +150,48 @@ glXGetConfig (Display *dpy, XVisualInfo *visual, int attrib, int *value ) TRACE; return 0; } + +GLXFBConfig * +glXChooseFBConfig (Display *dpy, int screen, int *attribList, int *count) +{ + if (trace) + TRACE; + GLXFBConfig *cfg = calloc (1, sizeof (GLXFBConfig)); + *cfg = (GLXFBConfig) (intptr_t) screen; + return cfg; +} + +XVisualInfo * +glXGetVisualFromFBConfig (Display *dpy, GLXFBConfig config) +{ + XVisualInfo template; + int num_visuals; + int template_mask; + int screen; + + if (trace) + TRACE; + + screen = (intptr_t) config; + template_mask = 0; + template.visualid = + XVisualIDFromVisual (XDefaultVisual (dpy, screen)); + template_mask = VisualIDMask; + return XGetVisualInfo (dpy, template_mask, &template, &num_visuals); +} + +int +glXGetFBConfigAttrib (Display *dpy, GLXFBConfig config, int attribute, int *value) +{ + if (trace) + TRACE; + return 0; +} + +GLXContext +glXCreateContextAttribsARB (Display *dpy, GLXFBConfig cfg, GLXContext ctx, Bool direct, const int *attribs) +{ + if (trace) + TRACE; + return (GLXContext)1; +} diff --git a/tools/gl_stub/gls_norm.c b/tools/gl_stub/gls_norm.c index 5942ffe9a..75bf94ba8 100644 --- a/tools/gl_stub/gls_norm.c +++ b/tools/gl_stub/gls_norm.c @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -5,15 +7,35 @@ #include "QF/GL/defines.h" #include "QF/GL/types.h" +#include "QF/GLSL/types.h" typedef struct __GLXcontextRec *GLXContext; typedef XID GLXDrawable; +#define QFGL_DONT_NEED(ret, func, params) QFGL_NEED(ret, func, params) + +#undef QFGL_WANT +#undef QFGL_NEED + +#define QFGL_WANT(ret, name, args) \ +ret GLAPIENTRY norm_##name args; +#define QFGL_NEED(ret, name, args) \ +ret GLAPIENTRY norm_##name args; +#include "QF/GL/qf_funcs_list.h" +#include "QF/GLSL/qf_funcs_list.h" +#undef QFGL_NEED +#undef QFGL_WANT + void norm_glAccum (GLenum op, GLfloat value) { } +void +norm_glActiveTexture (GLenum texture) +{ +} + void norm_glAlphaFunc (GLenum func, GLclampf ref) { @@ -31,11 +53,36 @@ norm_glArrayElement (GLint i) { } +void +norm_glAttachShader (GLuint program, GLuint shader) +{ +} + void norm_glBegin (GLenum mode) { } +void +norm_glBindAttribLocation (GLuint program, GLuint index, const GLchar* name) +{ +} + +void +norm_glBindBuffer (GLenum target, GLuint buffer) +{ +} + +void +norm_glBindFramebuffer (GLenum target, GLuint framebuffer) +{ +} + +void +norm_glBindRenderbuffer (GLenum target, GLuint renderbuffer) +{ +} + void norm_glBindTexture (GLenum target, GLuint texture) { @@ -57,11 +104,31 @@ norm_glBlendEquation (GLenum mode) { } +void +norm_glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha) +{ +} + void norm_glBlendFunc (GLenum sfactor, GLenum dfactor) { } +void +norm_glBlendFuncSeparate (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) +{ +} + +void +norm_glBufferData (GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) +{ +} + +void +norm_glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data) +{ +} + void norm_glCallList (GLuint list) { @@ -72,6 +139,449 @@ norm_glCallLists (GLsizei n, GLenum type, const GLvoid * lists) { } +GLenum +norm_glCheckFramebufferStatus (GLenum target) +{ + return 0x8CD5; +} + +void +norm_glClearDepthf (GLclampf depth) +{ +} + +void +norm_glCompileShader (GLuint shader) +{ +} + +void +norm_glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid* data) +{ +} + +void +norm_glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid* data) +{ +} + +GLuint +norm_glCreateProgram (void) +{ + static int program; + return ++program; +} + +GLuint +norm_glCreateShader (GLenum type) +{ + static int shader; + return ++shader; +} + +void +norm_glDeleteBuffers (GLsizei n, const GLuint* buffers) +{ +} + +void +norm_glDeleteFramebuffers (GLsizei n, const GLuint* framebuffers) +{ +} + +void +norm_glDeleteProgram (GLuint program) +{ +} + +void +norm_glDeleteRenderbuffers (GLsizei n, const GLuint* renderbuffers) +{ +} + +void +norm_glDeleteShader (GLuint shader) +{ +} + +void +norm_glDepthRangef (GLclampf zNear, GLclampf zFar) +{ +} + +void +norm_glDetachShader (GLuint program, GLuint shader) +{ +} + +void +norm_glDisableVertexAttribArray (GLuint index) +{ +} + +void +norm_glEnableVertexAttribArray (GLuint index) +{ +} + +void +norm_glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) +{ +} + +void +norm_glGenBuffers (GLsizei n, GLuint* buffers) +{ + memset (buffers, 0, n * sizeof (GLuint)); +} + +void +norm_glGenFramebuffers (GLsizei n, GLuint* framebuffers) +{ + memset (framebuffers, 0, n * sizeof (GLuint)); +} + +void +norm_glGenRenderbuffers (GLsizei n, GLuint* renderbuffers) +{ + memset (renderbuffers, 0, n * sizeof (GLuint)); +} + +void +norm_glGenerateMipmap (GLenum target) +{ +} + +void +norm_glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) +{ +} + +void +norm_glGetActiveUniform (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) +{ +} + +void +norm_glGetAttachedShaders (GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) +{ +} + +int +norm_glGetAttribLocation (GLuint program, const GLchar* name) +{ + return 0; +} + +void +norm_glGetBufferParameteriv (GLenum target, GLenum pname, GLint* params) +{ +} + +void +norm_glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint* params) +{ +} + +void +norm_glGetProgramInfoLog (GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog) +{ + if (bufsize > 0) { + *infolog = 0; + } +} + +void +norm_glGetProgramiv (GLuint program, GLenum pname, GLint* params) +{ + *params = 0; +} + +void +norm_glGetRenderbufferParameteriv (GLuint shader, GLenum pname, GLint* params) +{ +} + +void +norm_glGetShaderInfoLog (GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog) +{ + if (bufsize > 0) { + *infolog = 0; + } +} + +void +norm_glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) +{ +} + +void +norm_glGetShaderSource (GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source) +{ +} + +void +norm_glGetShaderiv (GLuint shader, GLenum pname, GLint* params) +{ + *params = 0; +} + +int +norm_glGetUniformLocation (GLuint program, const GLchar* name) +{ + return 0; +} + +void +norm_glGetUniformfv (GLuint program, GLint location, GLfloat* params) +{ +} + +void +norm_glGetUniformiv (GLuint program, GLint location, GLint* params) +{ +} + +void +norm_glGetVertexAttribPointerv (GLuint index, GLenum pname, GLvoid** pointer) +{ +} + +void +norm_glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat* params) +{ +} + +void +norm_glGetVertexAttribiv (GLuint index, GLenum pname, GLint* params) +{ +} + +GLboolean +norm_glIsBuffer (GLuint buffer) +{ + return 0; +} + +GLboolean +norm_glIsFramebuffer (GLuint framebuffer) +{ + return 0; +} + +GLboolean +norm_glIsProgram (GLuint program) +{ + return 0; +} + +GLboolean +norm_glIsRenderbuffer (GLuint renderbuffer) +{ + return 0; +} + +GLboolean +norm_glIsShader (GLuint shader) +{ + return 0; +} + +void +norm_glLinkProgram (GLuint program) +{ +} + +void +norm_glReleaseShaderCompiler (void) +{ +} + +void +norm_glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height) +{ +} + +void +norm_glSampleCoverage (GLclampf value, GLboolean invert) +{ +} + +void +norm_glShaderBinary (GLsizei n, const GLuint* shaders, GLenum binaryformat, const GLvoid* binary, GLsizei length) +{ +} + +void +norm_glShaderSource (GLuint shader, GLsizei count, const GLchar** string, const GLint* length) +{ +} + +void +norm_glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask) +{ +} + +void +norm_glStencilMaskSeparate (GLenum face, GLuint mask) +{ +} + +void +norm_glStencilOpSeparate (GLenum face, GLenum fail, GLenum zfail, GLenum zpass) +{ +} + +void +norm_glUniform1f (GLint location, GLfloat x) +{ +} + +void +norm_glUniform1fv (GLint location, GLsizei count, const GLfloat* v) +{ +} + +void +norm_glUniform1i (GLint location, GLint x) +{ +} + +void +norm_glUniform1iv (GLint location, GLsizei count, const GLint* v) +{ +} + +void +norm_glUniform2f (GLint location, GLfloat x, GLfloat y) +{ +} + +void +norm_glUniform2fv (GLint location, GLsizei count, const GLfloat* v) +{ +} + +void +norm_glUniform2i (GLint location, GLint x, GLint y) +{ +} + +void +norm_glUniform2iv (GLint location, GLsizei count, const GLint* v) +{ +} + +void +norm_glUniform3f (GLint location, GLfloat x, GLfloat y, GLfloat z) +{ +} + +void +norm_glUniform3fv (GLint location, GLsizei count, const GLfloat* v) +{ +} + +void +norm_glUniform3i (GLint location, GLint x, GLint y, GLint z) +{ +} + +void +norm_glUniform3iv (GLint location, GLsizei count, const GLint* v) +{ +} + +void +norm_glUniform4f (GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ +} + +void +norm_glUniform4fv (GLint location, GLsizei count, const GLfloat* v) +{ +} + +void +norm_glUniform4i (GLint location, GLint x, GLint y, GLint z, GLint w) +{ +} + +void +norm_glUniform4iv (GLint location, GLsizei count, const GLint* v) +{ +} + +void +norm_glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +{ +} + +void +norm_glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +{ +} + +void +norm_glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +{ +} + +void +norm_glUseProgram (GLuint program) +{ +} + +void +norm_glValidateProgram (GLuint program) +{ +} + +void +norm_glVertexAttrib1f (GLuint indx, GLfloat x) +{ +} + +void +norm_glVertexAttrib1fv (GLuint indx, const GLfloat* values) +{ +} + +void +norm_glVertexAttrib2f (GLuint indx, GLfloat x, GLfloat y) +{ +} + +void +norm_glVertexAttrib2fv (GLuint indx, const GLfloat* values) +{ +} + +void +norm_glVertexAttrib3f (GLuint indx, GLfloat x, GLfloat y, GLfloat z) +{ +} + +void +norm_glVertexAttrib3fv (GLuint indx, const GLfloat* values) +{ +} + +void +norm_glVertexAttrib4f (GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ +} + +void +norm_glVertexAttrib4fv (GLuint indx, const GLfloat* values) +{ +} + +void +norm_glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr) +{ +} + void norm_glClear (GLbitfield mask) { @@ -614,6 +1124,7 @@ norm_glGenLists (GLsizei range) void norm_glGenTextures (GLsizei n, GLuint * textures) { + memset (textures, 0, n * sizeof (GLuint)); } void @@ -694,7 +1205,7 @@ norm_glGetIntegerv (GLenum pname, GLint * params) { switch (pname) { case GL_MAX_TEXTURE_SIZE: - *params = 512; + *params = 2048; break; case GL_UNPACK_ALIGNMENT: case GL_PACK_ALIGNMENT: @@ -1964,3 +2475,38 @@ void norm_glPNTrianglesiATI (GLint x, GLint y) { } + +void +norm_glFramebufferTexture1D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) +{ +} + +void +norm_glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) +{ +} + +void +norm_glFramebufferTexture3D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint layer) +{ +} + +void +norm_glFramebufferTexture (GLenum target, GLenum attachment, GLuint texture, GLint level) +{ +} + +void +norm_glNamedFramebufferTexture (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level) +{ +} + +void +norm_glCreateVertexArrays (GLsizei n, GLuint *arrays) +{ +} + +void +norm_glBindVertexArray (GLuint array) +{ +} diff --git a/tools/gl_stub/gls_trace.c b/tools/gl_stub/gls_trace.c index 6ca666362..e2d904a56 100644 --- a/tools/gl_stub/gls_trace.c +++ b/tools/gl_stub/gls_trace.c @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -5,6 +7,7 @@ #include "QF/GL/defines.h" #include "QF/GL/types.h" +#include "QF/GLSL/types.h" typedef struct __GLXcontextRec *GLXContext; typedef XID GLXDrawable; @@ -19,6 +22,12 @@ trace_glAccum (GLenum op, GLfloat value) TRACE; } +void +trace_glActiveTexture (GLenum texture) +{ + TRACE; +} + void trace_glAlphaFunc (GLenum func, GLclampf ref) { @@ -39,12 +48,42 @@ trace_glArrayElement (GLint i) TRACE; } +void +trace_glAttachShader (GLuint program, GLuint shader) +{ + TRACE; +} + void trace_glBegin (GLenum mode) { TRACE; } +void +trace_glBindAttribLocation (GLuint program, GLuint index, const GLchar* name) +{ + TRACE; +} + +void +trace_glBindBuffer (GLenum target, GLuint buffer) +{ + TRACE; +} + +void +trace_glBindFramebuffer (GLenum target, GLuint framebuffer) +{ + TRACE; +} + +void +trace_glBindRenderbuffer (GLenum target, GLuint renderbuffer) +{ + TRACE; +} + void trace_glBindTexture (GLenum target, GLuint texture) { @@ -70,12 +109,36 @@ trace_glBlendEquation (GLenum mode) TRACE; } +void +trace_glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha) +{ + TRACE; +} + void trace_glBlendFunc (GLenum sfactor, GLenum dfactor) { TRACE; } +void +trace_glBlendFuncSeparate (GLenum sfactor, GLenum dfactor) +{ + TRACE; +} + +void +trace_glBufferData (GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) +{ + TRACE; +} + +void +trace_glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data) +{ + TRACE; +} + void trace_glCallList (GLuint list) { @@ -88,6 +151,533 @@ trace_glCallLists (GLsizei n, GLenum type, const GLvoid * lists) TRACE; } +GLenum +trace_glCheckFramebufferStatus (GLenum target) +{ + TRACE; + return 0x8CD5; +} + +void +trace_glClearDepthf (GLclampf depth) +{ + TRACE; +} + +void +trace_glCompileShader (GLuint shader) +{ + TRACE; +} + +void +trace_glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid* data) +{ + TRACE; +} + +void +trace_glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid* data) +{ + TRACE; +} + +GLuint +trace_glCreateProgram (void) +{ + static int program; + TRACE; + return ++program; +} + +GLuint +trace_glCreateShader (GLenum type) +{ + static int shader; + TRACE; + return ++shader; +} + +void +trace_glDeleteBuffers (GLsizei n, const GLuint* buffers) +{ + TRACE; +} + +void +trace_glDeleteFramebuffers (GLsizei n, const GLuint* framebuffers) +{ + TRACE; +} + +void +trace_glDeleteProgram (GLuint program) +{ + TRACE; +} + +void +trace_glDeleteRenderbuffers (GLsizei n, const GLuint* renderbuffers) +{ + TRACE; +} + +void +trace_glDeleteShader (GLuint shader) +{ + TRACE; +} + +void +trace_glDepthRangef (GLclampf zNear, GLclampf zFar) +{ + TRACE; +} + +void +trace_glDetachShader (GLuint program, GLuint shader) +{ + TRACE; +} + +void +trace_glDisableVertexAttribArray (GLuint index) +{ + TRACE; +} + +void +trace_glEnableVertexAttribArray (GLuint index) +{ + TRACE; +} + +void +trace_glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) +{ + TRACE; +} + +void +trace_glGenBuffers (GLsizei n, GLuint* buffers) +{ + TRACE; + memset (buffers, 0, n * sizeof (GLuint)); +} + +void +trace_glGenFramebuffers (GLsizei n, GLuint* framebuffers) +{ + TRACE; + memset (framebuffers, 0, n * sizeof (GLuint)); +} + +void +trace_glGenRenderbuffers (GLsizei n, GLuint* renderbuffers) +{ + TRACE; + memset (renderbuffers, 0, n * sizeof (GLuint)); +} + +void +trace_glGenerateMipmap (GLenum target) +{ + TRACE; +} + +void +trace_glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) +{ + TRACE; +} + +void +trace_glGetActiveUniform (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) +{ + TRACE; +} + +void +trace_glGetAttachedShaders (GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) +{ + TRACE; +} + +int +trace_glGetAttribLocation (GLuint program, const GLchar* name) +{ + TRACE; + return 0; +} + +void +trace_glGetBufferParameteriv (GLenum target, GLenum pname, GLint* params) +{ + TRACE; +} + +void +trace_glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint* params) +{ + TRACE; +} + +void +trace_glGetProgramInfoLog (GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog) +{ + TRACE; + if (bufsize > 0) { + *infolog = 0; + } +} + +void +trace_glGetProgramiv (GLuint program, GLenum pname, GLint* params) +{ + TRACE; + *params = 0; +} + +void +trace_glGetRenderbufferParameteriv (GLuint shader, GLenum pname, GLint* params) +{ + TRACE; +} + +void +trace_glGetShaderInfoLog (GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog) +{ + TRACE; + if (bufsize > 0) { + *infolog = 0; + } +} + +void +trace_glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) +{ + TRACE; +} + +void +trace_glGetShaderSource (GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source) +{ + TRACE; +} + +void +trace_glGetShaderiv (GLuint shader, GLenum pname, GLint* params) +{ + TRACE; + *params = 0; +} + +int +trace_glGetUniformLocation (GLuint program, const GLchar* name) +{ + TRACE; + return 0; +} + +void +trace_glGetUniformfv (GLuint program, GLint location, GLfloat* params) +{ + TRACE; +} + +void +trace_glGetUniformiv (GLuint program, GLint location, GLint* params) +{ + TRACE; +} + +void +trace_glGetVertexAttribPointerv (GLuint index, GLenum pname, GLvoid** pointer) +{ + TRACE; +} + +void +trace_glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat* params) +{ + TRACE; +} + +void +trace_glGetVertexAttribiv (GLuint index, GLenum pname, GLint* params) +{ + TRACE; +} + +GLboolean +trace_glIsBuffer (GLuint buffer) +{ + TRACE; + return 0; +} + +GLboolean +trace_glIsFramebuffer (GLuint framebuffer) +{ + TRACE; + return 0; +} + +GLboolean +trace_glIsProgram (GLuint program) +{ + TRACE; + return 0; +} + +GLboolean +trace_glIsRenderbuffer (GLuint renderbuffer) +{ + TRACE; + return 0; +} + +GLboolean +trace_glIsShader (GLuint shader) +{ + TRACE; + return 0; +} + +void +trace_glLinkProgram (GLuint program) +{ + TRACE; +} + +void +trace_glReleaseShaderCompiler (void) +{ + TRACE; +} + +void +trace_glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height) +{ + TRACE; +} + +void +trace_glSampleCoverage (GLclampf value, GLboolean invert) +{ + TRACE; +} + +void +trace_glShaderBinary (GLsizei n, const GLuint* shaders, GLenum binaryformat, const GLvoid* binary, GLsizei length) +{ + TRACE; +} + +void +trace_glShaderSource (GLuint shader, GLsizei count, const GLchar** string, const GLint* length) +{ + TRACE; +} + +void +trace_glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask) +{ + TRACE; +} + +void +trace_glStencilMaskSeparate (GLenum face, GLuint mask) +{ + TRACE; +} + +void +trace_glStencilOpSeparate (GLenum face, GLenum fail, GLenum zfail, GLenum zpass) +{ + TRACE; +} + +void +trace_glUniform1f (GLint location, GLfloat x) +{ + TRACE; +} + +void +trace_glUniform1fv (GLint location, GLsizei count, const GLfloat* v) +{ + TRACE; +} + +void +trace_glUniform1i (GLint location, GLint x) +{ + TRACE; +} + +void +trace_glUniform1iv (GLint location, GLsizei count, const GLint* v) +{ + TRACE; +} + +void +trace_glUniform2f (GLint location, GLfloat x, GLfloat y) +{ + TRACE; +} + +void +trace_glUniform2fv (GLint location, GLsizei count, const GLfloat* v) +{ + TRACE; +} + +void +trace_glUniform2i (GLint location, GLint x, GLint y) +{ + TRACE; +} + +void +trace_glUniform2iv (GLint location, GLsizei count, const GLint* v) +{ + TRACE; +} + +void +trace_glUniform3f (GLint location, GLfloat x, GLfloat y, GLfloat z) +{ + TRACE; +} + +void +trace_glUniform3fv (GLint location, GLsizei count, const GLfloat* v) +{ + TRACE; +} + +void +trace_glUniform3i (GLint location, GLint x, GLint y, GLint z) +{ + TRACE; +} + +void +trace_glUniform3iv (GLint location, GLsizei count, const GLint* v) +{ + TRACE; +} + +void +trace_glUniform4f (GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + TRACE; +} + +void +trace_glUniform4fv (GLint location, GLsizei count, const GLfloat* v) +{ + TRACE; +} + +void +trace_glUniform4i (GLint location, GLint x, GLint y, GLint z, GLint w) +{ + TRACE; +} + +void +trace_glUniform4iv (GLint location, GLsizei count, const GLint* v) +{ + TRACE; +} + +void +trace_glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +{ + TRACE; +} + +void +trace_glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +{ + TRACE; +} + +void +trace_glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +{ + TRACE; +} + +void +trace_glUseProgram (GLuint program) +{ + TRACE; +} + +void +trace_glValidateProgram (GLuint program) +{ + TRACE; +} + +void +trace_glVertexAttrib1f (GLuint indx, GLfloat x) +{ + TRACE; +} + +void +trace_glVertexAttrib1fv (GLuint indx, const GLfloat* values) +{ + TRACE; +} + +void +trace_glVertexAttrib2f (GLuint indx, GLfloat x, GLfloat y) +{ + TRACE; +} + +void +trace_glVertexAttrib2fv (GLuint indx, const GLfloat* values) +{ + TRACE; +} + +void +trace_glVertexAttrib3f (GLuint indx, GLfloat x, GLfloat y, GLfloat z) +{ + TRACE; +} + +void +trace_glVertexAttrib3fv (GLuint indx, const GLfloat* values) +{ + TRACE; +} + +void +trace_glVertexAttrib4f (GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + TRACE; +} + +void +trace_glVertexAttrib4fv (GLuint indx, const GLfloat* values) +{ + TRACE; +} + +void +trace_glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr) +{ + TRACE; +} + void trace_glClear (GLbitfield mask) { @@ -735,6 +1325,7 @@ void trace_glGenTextures (GLsizei n, GLuint * textures) { TRACE; + memset (textures, 0, n * sizeof (GLuint)); } void @@ -830,7 +1421,7 @@ trace_glGetIntegerv (GLenum pname, GLint * params) TRACE; switch (pname) { case GL_MAX_TEXTURE_SIZE: - *params = 512; + *params = 2048; break; case GL_UNPACK_ALIGNMENT: case GL_PACK_ALIGNMENT: @@ -2340,5 +2931,41 @@ trace_glViewport (GLint x, GLint y, GLsizei width, GLsizei height) void trace_glPNTrianglesiATI (GLint x, GLint y) +{ + TRACE; +} + +void +trace_glFramebufferTexture1D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) +{ +} + +void +trace_glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) +{ +} + +void +trace_glFramebufferTexture3D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint layer) +{ +} + +void +trace_glFramebufferTexture (GLenum target, GLenum attachment, GLuint texture, GLint level) +{ +} + +void +trace_glNamedFramebufferTexture (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level) +{ +} + +void +trace_glCreateVertexArrays (GLsizei n, GLuint *arrays) +{ +} + +void +trace_glBindVertexArray (GLuint array) { } diff --git a/tools/io_mesh_qfmdl/__init__.py b/tools/io_mesh_qfmdl/__init__.py index 5ee7375ef..3d9be7b20 100644 --- a/tools/io_mesh_qfmdl/__init__.py +++ b/tools/io_mesh_qfmdl/__init__.py @@ -24,14 +24,14 @@ bl_info = { "name": "Quake MDL format", "author": "Bill Currie", - "blender": (2, 6, 3), + "blender": (2, 80, 0), "api": 35622, "location": "File > Import-Export", "description": "Import-Export Quake MDL (version 6) files. (.mdl)", - "warning": "not even alpha", + "warning": "still work in progress", "wiki_url": "", "tracker_url": "", -# "support": 'OFFICIAL', +# "support": 'OFFICIAL', "category": "Import-Export"} # To support reload properly, try to access a package var, if it's there, @@ -66,28 +66,33 @@ EFFECTS=( ) class QFMDLSettings(bpy.types.PropertyGroup): - eyeposition = FloatVectorProperty( + eyeposition : FloatVectorProperty( name="Eye Position", description="View possion relative to object origin") - synctype = EnumProperty( + synctype : EnumProperty( items=SYNCTYPE, name="Sync Type", description="Add random time offset for automatic animations") - rotate = BoolProperty( + rotate : BoolProperty( name="Rotate", description="Rotate automatically (for pickup items)") - effects = EnumProperty( + effects : EnumProperty( items=EFFECTS, name="Effects", description="Particle trail effects") - #doesn't work :( - #script = PointerProperty( - # type=bpy.types.Object, - # name="Script", - # description="Script for animating frames and skins") - script = StringProperty( + + script : PointerProperty( + type=bpy.types.Text, name="Script", description="Script for animating frames and skins") + + xform : BoolProperty( + name="Auto transform", + description="Auto-apply location/rotation/scale when exporting", + default=True) + md16 : BoolProperty( + name="16-bit", + description="16 bit vertex coordinates: QuakeForge only") xform = BoolProperty( name="Auto transform", description="Auto-apply location/rotation/scale when exporting", @@ -102,7 +107,7 @@ class ImportMDL6(bpy.types.Operator, ImportHelper): bl_label = "Import MDL" filename_ext = ".mdl" - filter_glob = StringProperty(default="*.mdl", options={'HIDDEN'}) + filter_glob : StringProperty(default="*.mdl", options={'HIDDEN'}) def execute(self, context): from . import import_mdl @@ -116,7 +121,7 @@ class ExportMDL6(bpy.types.Operator, ExportHelper): bl_label = "Export MDL" filename_ext = ".mdl" - filter_glob = StringProperty(default="*.mdl", options={'HIDDEN'}) + filter_glob : StringProperty(default="*.mdl", options={'HIDDEN'}) @classmethod def poll(cls, context): @@ -128,11 +133,12 @@ class ExportMDL6(bpy.types.Operator, ExportHelper): keywords = self.as_keywords (ignore=("check_existing", "filter_glob")) return export_mdl.export_mdl(self, context, **keywords) -class MDLPanel(bpy.types.Panel): +class OBJECT_PT_MDLPanel(bpy.types.Panel): + bl_label = "MDL Properties" bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = 'object' - bl_label = 'QF MDL' + bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): @@ -157,21 +163,28 @@ def menu_func_import(self, context): def menu_func_export(self, context): self.layout.operator(ExportMDL6.bl_idname, text="Quake MDL (.mdl)") +classes = ( + QFMDLSettings, + OBJECT_PT_MDLPanel, + ImportMDL6, + ExportMDL6 +) def register(): - bpy.utils.register_module(__name__) + for cls in classes: + bpy.utils.register_class(cls) bpy.types.Object.qfmdl = PointerProperty(type=QFMDLSettings) - bpy.types.INFO_MT_file_import.append(menu_func_import) - bpy.types.INFO_MT_file_export.append(menu_func_export) - + bpy.types.TOPBAR_MT_file_import.append(menu_func_import) + bpy.types.TOPBAR_MT_file_export.append(menu_func_export) def unregister(): - bpy.utils.unregister_module(__name__) + for cls in classes: + bpy.utils.unregister_class(cls) - bpy.types.INFO_MT_file_import.remove(menu_func_import) - bpy.types.INFO_MT_file_export.remove(menu_func_export) + bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) + bpy.types.TOPBAR_MT_file_export.remove(menu_func_export) if __name__ == "__main__": register() diff --git a/tools/io_mesh_qfmdl/export_mdl.py b/tools/io_mesh_qfmdl/export_mdl.py index 9b98ee20a..2590bace6 100644 --- a/tools/io_mesh_qfmdl/export_mdl.py +++ b/tools/io_mesh_qfmdl/export_mdl.py @@ -27,6 +27,7 @@ from .qfplist import pldata, PListError from .quakepal import palette from .quakenorm import map_normal from .mdl import MDL +from .__init__ import SYNCTYPE, EFFECTS def check_faces(mesh): #Check that all faces are tris because mdl does not support anything else. @@ -35,17 +36,17 @@ def check_faces(mesh): faces_ok = True save_select = [] for f in mesh.polygons: - save_select.append(f.select) - f.select = False + save_select.append(f.select_get()) + f.select_set('DESELECT') if len(f.vertices) > 3: - f.select = True + f.select_set('SELECT') faces_ok = False if not faces_ok: mesh.update() return False #reset selection to what it was before the check. for f, s in map(lambda x, y: (x, y), mesh.polygons, save_select): - f.select = s + f.select_set('SELECT' if s else 'DESELECT') mesh.update() return True @@ -85,7 +86,7 @@ def null_skin(size): return skin def active_uv(mesh): - for uvt in mesh.uv_textures: + for uvt in mesh.uv_layers: if uvt.active: return uvt return None @@ -94,6 +95,40 @@ def make_skin(operator, mdl, mesh): uvt = active_uv(mesh) mdl.skinwidth, mdl.skinheight = (4, 4) skin = null_skin((mdl.skinwidth, mdl.skinheight)) + + materials = mesh.materials + + if len(materials) > 0: + for mat in materials: + allTextureNodes = list(filter(lambda node: node.type == "TEX_IMAGE", mat.node_tree.nodes)) + if len(allTextureNodes) > 1: #=== skingroup + skingroup = MDL.Skin() + skingroup.type = 1 + skingroup.skins = [] + skingroup.times = [] + sortedNodes = list(allTextureNodes) + sortedNodes.sort(key=lambda x: x.location[1], reverse=True) + for node in sortedNodes: + if node.type == "TEX_IMAGE": + image = node.image + mdl.skinwidth, mdl.skinheight = image.size + skin = convert_image(image) + skingroup.skins.append(skin) + skingroup.times.append(0.1) # hardcoded at the moment + mdl.skins.append(skingroup) + elif len(allTextureNodes) == 1: #=== single skin + for node in allTextureNodes: + if node.type == "TEX_IMAGE": + image = node.image + mdl.skinwidth, mdl.skinheight = image.size + skin = convert_image(image) + mdl.skins.append(skin) + else: + mdl.skins.append(skin) # add empty skin - no texture nodes + else: + mdl.skins.append(skin) # add empty skin - no materials + + ''' if (uvt and uvt.data and uvt.data[0].image): image = uvt.data[0].image if (uvt.data[0].image.size[0] and uvt.data[0].image.size[1]): @@ -102,7 +137,9 @@ def make_skin(operator, mdl, mesh): else: operator.report({'WARNING'}, "Texture '%s' invalid (missing?)." % image.name) + mdl.skins.append(skin) + ''' def build_tris(mesh): # mdl files have a 1:1 relationship between stverts and 3d verts. @@ -182,7 +219,7 @@ def calc_average_area(mdl): a = Vector(verts[0].r) - Vector(verts[1].r) b = Vector(verts[2].r) - Vector(verts[1].r) c = a.cross(b) - totalarea += (c * c) ** 0.5 / 2.0 + totalarea += (c @ c) ** 0.5 / 2.0 return totalarea / len(mdl.tris) def get_properties(operator, mdl, obj): @@ -192,16 +229,11 @@ def get_properties(operator, mdl, obj): | MDL.EFFECTS[obj.qfmdl.effects]) if obj.qfmdl.md16: mdl.ident = "MD16" + script = obj.qfmdl.script mdl.script = None if script: - try: - script = bpy.data.texts[script].as_string() - except KeyError: - operator.report({'ERROR'}, - "Script '%s' not found." % script) - return False - pl = pldata(script) + pl = pldata(script.as_string()) try: mdl.script = pl.parse() except PListError as err: @@ -268,17 +300,29 @@ def process_frame(mdl, scene, frame, vertmap, ingroup = False, return fr mdl.frames += fr.frames[:-1] return fr.frames[-1] - scene.frame_set(int(frameno), frameno - int(frameno)) - mesh = mdl.obj.to_mesh(scene, True, 'PREVIEW') #wysiwyg? + scene.frame_set(int(frameno), subframe = frameno - int(frameno)) + depsgraph = bpy.context.evaluated_depsgraph_get() + mesh = mdl.obj.evaluated_get(depsgraph).to_mesh() #wysiwyg? if mdl.obj.qfmdl.xform: mesh.transform(mdl.obj.matrix_world) fr = make_frame(mesh, vertmap) fr.name = name return fr +def get_frame_name(mesh, idx): + name = "frame" + str(idx) + if mesh.shape_keys: + shape_keys_amount = len(mesh.shape_keys.key_blocks) + if shape_keys_amount > idx: + name = mesh.shape_keys.key_blocks[idx].name + return name + def export_mdl(operator, context, filepath): obj = context.active_object - mesh = obj.to_mesh(context.scene, True, 'PREVIEW') #wysiwyg? + obj.update_from_editmode() + depsgraph = context.evaluated_depsgraph_get() + ob_eval = obj.evaluated_get(depsgraph) + mesh = ob_eval.to_mesh() #if not check_faces(mesh): # operator.report({'ERROR'}, # "Mesh has faces with more than 3 vertices.") @@ -287,6 +331,7 @@ def export_mdl(operator, context, filepath): mdl.obj = obj if not get_properties(operator, mdl, obj): return {'CANCELLED'} + mdl.tris, mdl.stverts, vertmap = build_tris(mesh) if mdl.script: if 'skins' in mdl.script: @@ -299,13 +344,19 @@ def export_mdl(operator, context, filepath): if not mdl.skins: make_skin(operator, mdl, mesh) if not mdl.frames: - curframe = context.scene.frame_current - for fno in range(1, curframe + 1): + scene = context.scene + for fno in range(scene.frame_start, scene.frame_end + 1): context.scene.frame_set(fno) - mesh = obj.to_mesh(context.scene, True, 'PREVIEW') #wysiwyg? - if mdl.obj.qfmdl.xform: + obj.update_from_editmode() + depsgraph = context.evaluated_depsgraph_get() + ob_eval = obj.evaluated_get(depsgraph) + mesh = ob_eval.to_mesh() + if obj.qfmdl.xform: mesh.transform(mdl.obj.matrix_world) - mdl.frames.append(make_frame(mesh, vertmap)) + frame = make_frame(mesh, vertmap) + frame.name = get_frame_name(obj.data, fno) + mdl.frames.append(frame) + convert_stverts(mdl, mdl.stverts) mdl.size = calc_average_area(mdl) scale_verts(mdl) diff --git a/tools/io_mesh_qfmdl/import_mdl.py b/tools/io_mesh_qfmdl/import_mdl.py index feca0fa08..76f393424 100644 --- a/tools/io_mesh_qfmdl/import_mdl.py +++ b/tools/io_mesh_qfmdl/import_mdl.py @@ -31,15 +31,13 @@ def make_verts(mdl, framenum, subframenum=0): frame = mdl.frames[framenum] if frame.type: frame = frame.frames[subframenum] - verts = [] s = Vector(mdl.scale) o = Vector(mdl.scale_origin) m = Matrix(((s.x, 0, 0,o.x), ( 0,s.y, 0,o.y), ( 0, 0,s.z,o.z), ( 0, 0, 0, 1))) - for v in frame.verts: - verts.append(m * Vector(v.r)) + verts = [m @ Vector(v.r) for v in frame.verts] return verts def make_faces(mdl): @@ -87,7 +85,7 @@ def load_skins(mdl): p[l + 2] = c[2] / 255.0 p[l + 3] = 1.0 img.pixels[:] = p[:] - img.pack(True) + img.pack() img.use_fake_user = True mdl.images=[] @@ -98,30 +96,80 @@ def load_skins(mdl): else: load_skin(skin, "%s_%d" % (mdl.name, i)) +def setup_main_material(mdl): + mat = bpy.data.materials.new(mdl.name) + mat.blend_method = 'OPAQUE' + mat.diffuse_color = (1, 1, 1, 1) + mat.metallic = 1 + mat.roughness = 1 + mat.specular_intensity = 0 + mat.use_nodes = True + return mat + def setup_skins(mdl, uvs): load_skins(mdl) - img = mdl.images[0] # use the first skin for now - uvlay = mdl.mesh.uv_textures.new(mdl.name) - uvloop = mdl.mesh.uv_layers[0] - for i, texpoly in enumerate(uvlay.data): +# img = mdl.images[0] # use the first skin for now +# uvlay = mdl.mesh.uv_textures.new(mdl.name) +# uvloop = mdl.mesh.uv_layers[0] +# for i, texpoly in enumerate(uvlay.data): + uvloop = mdl.mesh.uv_layers.new(name = mdl.name) + for i in range(len(mdl.mesh.polygons)): poly = mdl.mesh.polygons[i] mdl_uv = uvs[i] - texpoly.image = img +# texpoly.image = img # TODO: commented out by jazz for j,k in enumerate(poly.loop_indices): uvloop.data[k].uv = mdl_uv[j] - mat = bpy.data.materials.new(mdl.name) - mat.diffuse_color = (1,1,1) - mat.use_raytrace = False - tex = bpy.data.textures.new(mdl.name, 'IMAGE') - tex.extension = 'CLIP' - tex.use_preview_alpha = True - tex.image = img - mat.texture_slots.add() - ts = mat.texture_slots[0] - ts.texture = tex - ts.use_map_alpha = True - ts.texture_coords = 'UV' - mdl.mesh.materials.append(mat) + + #Load all skins + img_counter = 0 + for i, skin in enumerate(mdl.skins): + if skin.type: + mat = setup_main_material(mdl) + emissionNode = mat.node_tree.nodes.new("ShaderNodeEmission") + shaderOut = mat.node_tree.nodes["Material Output"] + mat.node_tree.nodes.remove(mat.node_tree.nodes["Principled BSDF"]) + + emissionNode.location = (0, 0) + shaderOut.location = (200, 0) + + yPos = 0 + + for j, subskin in enumerate(skin.skins): + tex_node = mat.node_tree.nodes.new("ShaderNodeTexImage") + tex_node.image = mdl.images[img_counter] + img_counter += 1 + tex_node.interpolation = "Closest" + + tex_node.location = (-300, yPos) + yPos -= 280 + + if j == 0: + # connect only first texture (we'll need something smarter in the future) + mat.node_tree.links.new(tex_node.outputs[0], emissionNode.inputs[0]) + + mat.node_tree.links.new(emissionNode.outputs[0], shaderOut.inputs[0]) + mdl.mesh.materials.append(mat) + + else: + mat = setup_main_material(mdl) + + # TODO: turn transform to True and position it properly in editor + emissionNode = mat.node_tree.nodes.new("ShaderNodeEmission") + shaderOut = mat.node_tree.nodes["Material Output"] + mat.node_tree.nodes.remove(mat.node_tree.nodes["Principled BSDF"]) + + tex_node = mat.node_tree.nodes.new("ShaderNodeTexImage") + tex_node.image = mdl.images[img_counter] + img_counter += 1 + tex_node.interpolation = "Closest" + + emissionNode.location = (0, 0) + shaderOut.location = (200, 0) + tex_node.location = (-300, 0) + + mat.node_tree.links.new(tex_node.outputs[0], emissionNode.inputs[0]) + mat.node_tree.links.new(emissionNode.outputs[0], shaderOut.inputs[0]) + mdl.mesh.materials.append(mat) def make_shape_key(mdl, framenum, subframenum=0): frame = mdl.frames[framenum] @@ -133,7 +181,7 @@ def make_shape_key(mdl, framenum, subframenum=0): name = frame.name else: frame.name = name - frame.key = mdl.obj.shape_key_add(name) + frame.key = mdl.obj.shape_key_add(name=name) frame.key.value = 0.0 mdl.keys.append(frame.key) s = Vector(mdl.scale) @@ -143,20 +191,25 @@ def make_shape_key(mdl, framenum, subframenum=0): ( 0, 0,s.z,o.z), ( 0, 0, 0, 1))) for i, v in enumerate(frame.verts): - frame.key.data[i].co = m * Vector(v.r) + frame.key.data[i].co = m @ Vector(v.r) def build_shape_keys(mdl): mdl.keys = [] - mdl.obj.shape_key_add("Basis") + mdl.obj.shape_key_add(name="Basis",from_mix=False) mdl.mesh.shape_keys.name = mdl.name mdl.obj.active_shape_key_index = 0 + bpy.context.scene.frame_end = 0 for i, frame in enumerate(mdl.frames): frame = mdl.frames[i] if frame.type: for j in range(len(frame.frames)): make_shape_key(mdl, i, j) + bpy.context.scene.frame_end += 1 else: make_shape_key(mdl, i) + bpy.context.scene.frame_end += 1 + + bpy.context.scene.frame_start = 1 def set_keys(act, data): for d in data: @@ -173,7 +226,7 @@ def build_actions(mdl): ad = sk.animation_data_create() track = ad.nla_tracks.new(); track.name = mdl.name - start_frame = 1.0 + start_frame = 1 for frame in mdl.frames: act = bpy.data.actions.new(frame.name) data = [] @@ -207,7 +260,7 @@ def build_actions(mdl): data.append((k, co)) set_keys(act, data) track.strips.new(act.name, start_frame, act) - start_frame += act.frame_range[1] + start_frame += int(act.frame_range[1]) def merge_frames(mdl): def get_base(name): @@ -241,8 +294,8 @@ def write_text(mdl): /* This script represents the animation data within the model file. It is generated automatically on import, and is optional when exporting. If no script is used when exporting, frames will be exported one per - blender frame from frame 1 to the current frame (inclusive), and only - one skin will be exported. + blender frame from the scene start frame to the scene end frame + (inclusive), and one skin per teximage node will be exported. The fundamental format of the script is documented at http://quakeforge.net/doxygen/property-list.html @@ -335,14 +388,14 @@ def set_properties(mdl): mdl.obj.qfmdl.synctype = 'ST_SYNC' mdl.obj.qfmdl.rotate = (mdl.flags & MDL.EF_ROTATE) and True or False mdl.obj.qfmdl.effects = parse_flags(mdl.flags) - mdl.obj.qfmdl.script = mdl.text.name #FIXME really want the text object + mdl.obj.qfmdl.script = mdl.text mdl.obj.qfmdl.md16 = (mdl.ident == "MD16") -def import_mdl(operator, context, filepath): - bpy.context.user_preferences.edit.use_global_undo = False +def import_mdl(operator, context, filepath, **opts): + bpy.context.preferences.edit.use_global_undo = False - for obj in bpy.context.scene.objects: - obj.select = False + for obj in bpy.context.scene.collection.objects: + obj.select_set(False) mdl = MDL() if not mdl.read(filepath): @@ -354,10 +407,14 @@ def import_mdl(operator, context, filepath): mdl.mesh = bpy.data.meshes.new(mdl.name) mdl.mesh.from_pydata(verts, [], faces) mdl.obj = bpy.data.objects.new(mdl.name, mdl.mesh) - bpy.context.scene.objects.link(mdl.obj) - bpy.context.scene.objects.active = mdl.obj - mdl.obj.select = True + + bpy.context.scene.collection.objects.link(mdl.obj) + mdl.obj.select_set(True) + bpy.context.view_layer.objects.active = mdl.obj setup_skins(mdl, uvs) + + bpy.context.scene.frame_start = 1 + bpy.context.scene.frame_end = 1 if len(mdl.frames) > 1 or mdl.frames[0].type: build_shape_keys(mdl) merge_frames(mdl) @@ -367,5 +424,5 @@ def import_mdl(operator, context, filepath): mdl.mesh.update() - bpy.context.user_preferences.edit.use_global_undo = True + bpy.context.preferences.edit.use_global_undo = True return {'FINISHED'} diff --git a/tools/io_mesh_qfmdl/qfplist.py b/tools/io_mesh_qfmdl/qfplist.py index 4f16a2c34..b1852dab7 100644 --- a/tools/io_mesh_qfmdl/qfplist.py +++ b/tools/io_mesh_qfmdl/qfplist.py @@ -20,7 +20,7 @@ # quotables = ("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" - + "abcdefghijklmnopqrstuvwxyz!#$%&*+-./:?@|~_^") + + "`abcdefghijklmnopqrstuvwxyz!#$%&*+-./:?@|~_^") class PListError(Exception): def __init__(self, line, message): @@ -173,9 +173,11 @@ class pldata: "Unexpected character (expected '=')") self.pos += 1 value = self.parse() + self.skip_space() if self.src[self.pos] == ';': self.pos += 1 elif self.src[self.pos] != '}': + print(self.src[self.pos]) raise PListError(self.line, "Unexpected character (wanted ';' or '}')") item[key] = value @@ -251,7 +253,7 @@ class pldata: elif type(item) in [int, float]: self.write_string(str(item)) else: - raise PListError(0, "unsupported type") + raise PListError(0, f"unsupported type {type(item)}") def write(self, item): self.data = [] self.write_item(item, 0) diff --git a/tools/io_mesh_qfmdl/quakepal.py b/tools/io_mesh_qfmdl/quakepal.py index da7e4adec..7c2a6678f 100644 --- a/tools/io_mesh_qfmdl/quakepal.py +++ b/tools/io_mesh_qfmdl/quakepal.py @@ -1,4 +1,27 @@ -palette = ( +# vim:ts=4:et +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# + +from struct import unpack + +default_palette = ( (0x00, 0x00, 0x00), (0x0f, 0x0f, 0x0f), (0x1f, 0x1f, 0x1f), @@ -256,3 +279,23 @@ palette = ( (0xff, 0xff, 0xff), (0x9f, 0x5b, 0x53), ) + +palette = default_palette + +def load_palette(filepath): + if not filepath: + palette = default_palette + else: + try: + f = open(filepath, "rb") + pal = f.read(768) + if len(pal) < 768: + pal = pal + b'\0' * (768 - len(pal)) + palette = [] + for i in range(256): + palette.append(unpack("<3B", pal[i * 3 : i * 3 + 3])) + palette = tuple(palette) + except IOError: + palette = default_palette + return None + return palette diff --git a/tools/io_qfmap/__init__.py b/tools/io_qfmap/__init__.py index c67158aff..2cabf517f 100644 --- a/tools/io_qfmap/__init__.py +++ b/tools/io_qfmap/__init__.py @@ -24,7 +24,7 @@ bl_info = { "name": "Quake map format", "author": "Bill Currie", - "blender": (2, 6, 3), + "blender": (2, 80, 0), "api": 35622, "location": "File > Import-Export", "description": "Import-Export Quake maps", @@ -34,286 +34,83 @@ bl_info = { # "support": 'OFFICIAL', "category": "Import-Export"} -# To support reload properly, try to access a package var, if it's there, -# reload everything -if "bpy" in locals(): - import imp - if "import_map" in locals(): - imp.reload(import_map) - if "export_map" in locals(): - imp.reload(export_map) - -from pprint import pprint +submodule_names = ( + "entity", + "entityclass", + "export_map", + "import_map", + "init", + "map", + "qfplist", + "quakechr", + "quakepal", + "wad", +) import bpy -from bpy.props import BoolProperty, FloatProperty, StringProperty, EnumProperty -from bpy.props import FloatVectorProperty, PointerProperty -from bpy_extras.io_utils import ExportHelper, ImportHelper, path_reference_mode, axis_conversion -from bpy.app.handlers import persistent +from bpy.props import PointerProperty +from bpy.utils import register_class, unregister_class -from .entityclass import EntityClassDict, EntityClassError -from . import entity -from . import import_map -from . import export_map +import importlib +import sys -def ecm_draw(self, context): - layout = self.layout - for item in self.menu_items: - if type(item[1]) is str: - ec = context.scene.qfmap.entity_classes[item[1]] - if ec.size: - icon = 'OBJECT_DATA' - else: - icon = 'MESH_DATA' - op = layout.operator("object.add_entity", text=item[0], icon=icon) - op.entclass=item[1] - if ec.comment: - pass - else: - layout.menu(item[1].bl_idname) +registered_submodules = [] -class EntityClassMenu: - @classmethod - def clear(cls): - while cls.menu_items: - if type(cls.menu_item[0][1]) is not str: - bpy.utils.unregister_class(cls.menu_items[0][1]) - cls.menu_items[0][1].clear() - del cls.menu_items[0] - @classmethod - def build(cls, menudict, name="INFO_MT_entity_add", label="entity"): - items = list(menudict.items()) - items.sort() - menu_items = [] - for i in items: - i = list(i) - if type(i[1]) is dict: - if i[0]: - nm = "_".join((name, i[0])) - else: - nm = name - i[1] = cls.build(i[1], nm, i[0]) - menu_items.append(i) - attrs = {} - attrs["menu_items"] = menu_items - attrs["draw"] = ecm_draw - attrs["bl_idname"] = name - attrs["bl_label"] = label - attrs["clear"] = cls.clear - menu = type(name, (bpy.types.Menu,), attrs) - bpy.utils.register_class(menu) - return menu +# When the addon is reloaded, this module gets reloaded, however none +# of the other modules from this addon get reloaded. As a result, they +# don't call register_submodules (only run when the module is loaded) and +# thus they don't end up registering everything. +# +# This is set before any loading starts (in register), to a set of all the +# names of the modules loaded as of when loading starts. While doing the +# module loading, check if a module is present in this list. If so, reload +# it and remove it from the set (to prevent it from getting reloaded twice). +preloaded_modules = None -@persistent -def scene_load_handler(dummy): - for scene in bpy.data.scenes: - if hasattr(scene, "qfmap"): - scene.qfmap.script_update(bpy.context) - -class MapeditMessage(bpy.types.Operator): - bl_idname = "qfmapedit.message" - bl_label = "Message" - type = StringProperty() - message = StringProperty() - - def execute(self, context): - self.report({'INFO'}, message) - return {'FINISHED'} - def invoke(self, context, event): - wm = context.window_manager - return wm.invoke_popup(self, width=400, height=200) - def draw(self, context): - self.layout.label(self.type, icon='ERROR') - self.layout.label(self.message) - -def scan_entity_classes(context): - qfmap = context.scene.qfmap - if not qfmap.dirpath: - return - qfmap.entity_classes.from_source_tree(qfmap.dirpath) - name = context.scene.name + '-EntityClasses' - if name in bpy.data.texts: - txt = bpy.data.texts[name] - else: - txt = bpy.data.texts.new(name) - txt.from_string(qfmap.entity_classes.to_plist()) - qfmap.script = name - -def parse_entity_classes(context): - context.scene.qfmap.script_update(context) - -def ec_dir_update(self, context): - try: - scan_entity_classes(context) - except EntityClassError as err: - self.dirpath = "" - bpy.ops.qfmapedit.message('INVOKE_DEFAULT', type="Error", - message="Entity Class Error: %s" % err) - -def ec_script_update(self, context): - self.script_update(context) - -class AddEntity(bpy.types.Operator): - '''Add an entity''' - bl_idname = "object.add_entity" - bl_label = "Entity" - entclass = StringProperty(name = "entclass") - - def execute(self, context): - keywords = self.as_keywords() - return entity.add_entity(self, context, **keywords) - -class QFEntityClassScan(bpy.types.Operator): - '''Rescan the specified QuakeC source tree''' - bl_idname = "scene.scan_entity_classes" - bl_label = "RELOAD" - - def execute(self, context): - scan_entity_classes(context) - return {'FINISHED'} - -class QFEntityClassParse(bpy.types.Operator): - '''Reparse the specified entity class script''' - bl_idname = "scene.parse_entity_classes" - bl_label = "RELOAD" - - def execute(self, context): - parse_entity_classes(context) - return {'FINISHED'} - -class QFEntityClasses(bpy.types.PropertyGroup): - wadpath = StringProperty( - name="wadpath", - description="Path to search for wad files", - subtype='DIR_PATH') - dirpath = StringProperty( - name="dirpath", - description="Path to qc source tree", - subtype='DIR_PATH', - update=ec_dir_update) - script = StringProperty( - name="script", - description="entity class storage", - update=ec_script_update) - entity_classes = EntityClassDict() - ecm = EntityClassMenu.build({}) - entity_targets = {} - target_entities = [] - - def script_update(self, context): - if self.script in bpy.data.texts: - script = bpy.data.texts[self.script].as_string() - self.entity_classes.from_plist(script) - menudict = {} - entclasses = self.entity_classes.keys() - for ec in entclasses: - ecsub = ec.split("_") - d = menudict - for sub in ecsub[:-1]: - if sub not in d: - d[sub] = {} - elif type(d[sub]) is str: - d[sub] = {"":d[sub]} - d = d[sub] - sub = ecsub[-1] - if sub in d: - d[sub][""] = ec - else: - d[sub] = ec - self.__class__.ecm = EntityClassMenu.build(menudict) - -class QFECPanel(bpy.types.Panel): - bl_space_type = 'PROPERTIES' - bl_region_type = 'WINDOW' - bl_context = 'scene' - bl_label = 'QF Entity Classes' - - @classmethod - def poll(cls, context): - return True - - def draw(self, context): - layout = self.layout - scene = context.scene - row = layout.row() - layout.prop(scene.qfmap, "wadpath") - row = layout.row() - row.prop(scene.qfmap, "dirpath") - row.operator("scene.scan_entity_classes", text="", icon="FILE_REFRESH") - row = layout.row() - row.prop(scene.qfmap, "script") - row.operator("scene.parse_entity_classes", text="", icon="FILE_REFRESH") - -class ImportPoints(bpy.types.Operator, ImportHelper): - '''Load a Quake points File''' - bl_idname = "import_mesh.quake_points" - bl_label = "Import points" - - filename_ext = ".pts" - filter_glob = StringProperty(default="*.pts", options={'HIDDEN'}) - - def execute(self, context): - keywords = self.as_keywords (ignore=("filter_glob",)) - return import_map.import_pts(self, context, **keywords) - -class ImportMap(bpy.types.Operator, ImportHelper): - '''Load a Quake map File''' - bl_idname = "import_mesh.quake_map" - bl_label = "Import map" - - filename_ext = ".map" - filter_glob = StringProperty(default="*.map", options={'HIDDEN'}) - - def execute(self, context): - keywords = self.as_keywords (ignore=("filter_glob",)) - return import_map.import_map(self, context, **keywords) - -class ExportMap(bpy.types.Operator, ExportHelper): - '''Save a Quake map File''' - - bl_idname = "export_mesh.quake_map" - bl_label = "Export map" - - filename_ext = ".map" - filter_glob = StringProperty(default="*.map", options={'HIDDEN'}) - - @classmethod - def poll(cls, context): - return True - - def execute(self, context): - keywords = self.as_keywords (ignore=("check_existing", "filter_glob")) - return export_map.export_map(self, context, **keywords) - -def menu_func_import(self, context): - self.layout.operator(ImportMap.bl_idname, text="Quake map (.map)") - self.layout.operator(ImportPoints.bl_idname, text="Quake points (.pts)") - -def menu_func_export(self, context): - self.layout.operator(ExportMap.bl_idname, text="Quake map (.map)") - -def menu_func_add(self, context): - self.layout.menu(context.scene.qfmap.ecm.bl_idname, icon='PLUGIN') +def register_submodules(name, submodule_names): + global preloaded_modules + module = __import__(name=name, fromlist=submodule_names) + submodules = [getattr(module, name) for name in submodule_names] + for mod in submodules: + # Look through the modules present when register was called. If this + # module was already loaded, then reload it. + if mod.__name__ in preloaded_modules: + mod = importlib.reload(mod) + # Prevent the module from getting reloaded more than once + preloaded_modules.remove(mod.__name__) + m = [(),(),()] + if hasattr(mod, "classes_to_register"): + m[0] = mod.classes_to_register + for cls in mod.classes_to_register: + register_class(cls) + if hasattr(mod, "menus_to_register"): + m[1] = mod.menus_to_register + for menu in mod.menus_to_register: + menu[0].append(menu[1]) + if hasattr(mod, "custom_properties_to_register"): + for prop in mod.custom_properties_to_register: + setattr(prop[0], prop[1], PointerProperty(type=prop[2])) + if hasattr(mod, "handlers_to_register"): + m[2] = mod.handlers_to_register + for handler in mod.handlers_to_register: + getattr(bpy.app.handlers, handler[0]).append(handler[1]) + if m[0] or m[1] or m[2]: + registered_submodules.append(m) def register(): - bpy.utils.register_module(__name__) - - bpy.types.Scene.qfmap = PointerProperty(type=QFEntityClasses) - - bpy.types.INFO_MT_file_import.append(menu_func_import) - bpy.types.INFO_MT_file_export.append(menu_func_export) - bpy.types.INFO_MT_add.append(menu_func_add) - - bpy.app.handlers.load_post.append(scene_load_handler) - entity.register() - + global preloaded_modules + preloaded_modules = set(sys.modules.keys()) + register_submodules(__name__, submodule_names) + preloaded_modules = None def unregister(): - bpy.utils.unregister_module(__name__) - - bpy.types.INFO_MT_file_import.remove(menu_func_import) - bpy.types.INFO_MT_file_export.remove(menu_func_export) - bpy.types.INFO_MT_add.remove(menu_func_add) + for mod in reversed(registered_submodules): + for handler in reversed(mod[2]): + getattr(bpy.app.handlers, handler[0]).remove(handler[1]) + for menu in reversed(mod[1]): + menu[0].remove(menu[1]) + for cls in reversed(mod[0]): + unregister_class(cls) if __name__ == "__main__": register() diff --git a/tools/io_qfmap/entity.py b/tools/io_qfmap/entity.py index ecf9180c8..58c6d8953 100644 --- a/tools/io_qfmap/entity.py +++ b/tools/io_qfmap/entity.py @@ -19,15 +19,20 @@ # -import bpy, bgl +import bpy, bgl, gpu +from gpu_extras.batch import batch_for_shader from bpy.props import BoolProperty, FloatProperty, StringProperty, EnumProperty from bpy.props import BoolVectorProperty, CollectionProperty, PointerProperty from bpy.props import FloatVectorProperty, IntProperty from mathutils import Vector +from math import ceil + +from textwrap import TextWrapper from .entityclass import EntityClass -def draw_callback(self, context): + +def build_batch(qfmap): def obj_location(obj): ec = None if obj.qfentity.classname in entity_classes: @@ -38,13 +43,13 @@ def draw_callback(self, context): for i in range(8): loc += Vector(obj.bound_box[i]) return obj.location + loc/8.0 - qfmap = context.scene.qfmap entity_classes = qfmap.entity_classes entity_targets = qfmap.entity_targets target_entities = qfmap.target_entities - bgl.glLineWidth(3) ents = 0 targs = 0 + verts = [] + colors = [] for obj in target_entities: #obj = bpy.data.objects[objname] qfentity = obj.qfentity @@ -54,30 +59,40 @@ def draw_callback(self, context): ec = entity_classes[qfentity.classname] target = None killtarget = None - for field in qfentity.fields: - if field.name == "target" and field.value: - target = field.value - if field.name == "killtarget" and field.value: - killtarget = field.value + if "target" in qfentity.fields: + target = qfentity.fields["target"].value + if "killtarget" in qfentity.fields: + killtarget = qfentity.fields["killtarget"].value targetlist = [target, killtarget] if target == killtarget: del targetlist[1] for tname in targetlist: if tname and tname in entity_targets: targets = entity_targets[tname] - bgl.glColor4f(ec.color[0], ec.color[1], ec.color[2], 1) + color = (ec.color[0], ec.color[1], ec.color[2], 1) for ton in targets: targs += 1 to = bpy.data.objects[ton] - bgl.glBegin(bgl.GL_LINE_STRIP) - loc = obj_location(obj) - bgl.glVertex3f(loc.x, loc.y, loc.z) - loc = obj_location(to) - bgl.glVertex3f(loc.x, loc.y, loc.z) - bgl.glEnd() + start = obj_location(obj) + end = obj_location(to) + + verts.append(start) + colors.append(color) + verts.append(end) + colors.append(color) + return {"pos": verts, "color": colors} + +def draw_callback(self, context): + #FIXME horribly inefficient + qfmap = context.scene.qfmap + content = build_batch(qfmap) + shader = gpu.shader.from_builtin('3D_SMOOTH_COLOR') + batch = batch_for_shader(shader, 'LINES', content) + bgl.glLineWidth(3) + batch.draw(shader) bgl.glLineWidth(1) -class QFEntityRelations(bpy.types.Panel): +class VIEW3D_PT_QFEntityRelations(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_label = "Register callback" @@ -100,30 +115,30 @@ class QFEntityRelations(bpy.types.Panel): def draw(self, context): pass -class EntityField_list(bpy.types.UIList): +class OBJECT_UL_EntityField_list(bpy.types.UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): - layout.label(item.name) - layout.label(item.value) + layout.label(text=item.name) + layout.label(text=item.value) def qfentity_items(self, context): qfmap = context.scene.qfmap entclasses = qfmap.entity_classes eclist = list(entclasses.keys()) eclist.sort() - enum = (('', "--", ""),) + enum = (('.', "--", ""),) enum += tuple(map(lambda ec: (ec, ec, ""), eclist)) return enum class QFEntityProp(bpy.types.PropertyGroup): - value = StringProperty(name="") - template_list_controls = StringProperty(default="value", options={'HIDDEN'}) + value: StringProperty(name="") + template_list_controls: StringProperty(default="value", options={'HIDDEN'}) class QFEntity(bpy.types.PropertyGroup): - classname = EnumProperty(items = qfentity_items, name = "Entity Class") - flags = BoolVectorProperty(size=12) - fields = CollectionProperty(type=QFEntityProp, name="Fields") - field_idx = IntProperty() + classname: EnumProperty(items = qfentity_items, name = "Entity Class") + flags: BoolVectorProperty(size=12) + fields: CollectionProperty(type=QFEntityProp, name="Fields") + field_idx: IntProperty() class QFEntpropAdd(bpy.types.Operator): '''Add an entity field/value pair''' @@ -146,24 +161,7 @@ class QFEntpropRemove(bpy.types.Operator): qfentity.fields.remove(qfentity.field_idx) return {'FINISHED'} -def reflow_text(text, max_width): - lines = [] - for text_line in text.split("\n"): - if not text_line: - continue - words = text_line.split(" ") - flowed_line = "" - while words: - if len(flowed_line) + len(words[0]) > max_width: - lines.append(flowed_line) - flowed_line = "" - flowed_line += (" " if flowed_line else "") + words[0] - del words[0] - if flowed_line: - lines.append(flowed_line) - return lines - -class EntityPanel(bpy.types.Panel): +class OBJECT_PT_EntityPanel(bpy.types.Panel): bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = 'object' @@ -186,9 +184,22 @@ class EntityPanel(bpy.types.Panel): row = layout.row() row.prop(qfentity, "classname") box=layout.box() - lines = reflow_text(ec.comment, 40) - for l in lines: - box.label(l) + width = int(ceil(context.region.width / 6)) + mainwrap = TextWrapper(width = width) + subwrap = TextWrapper(width = width - 8) + for c in ec.comment: + clines = mainwrap.wrap(c) + for l in clines: + box.label(text=l) + for f in ec.fields.values(): + print(f.name) + flines = subwrap.wrap(f.comment) + box.label(text=f"{f.name}") + for l in flines: + box.label(text=f" {l}") + if hasattr(f, "sounds"): + for s in f.sounds: + box.label(text=f" {s[0]} {s[1]}") row = layout.row() for c in range(3): col = row.column() @@ -198,11 +209,11 @@ class EntityPanel(bpy.types.Panel): sub.prop(qfentity, "flags", text=flags[idx], index=idx) row = layout.row() col = row.column() - col.template_list("EntityField_list", "", qfentity, "fields", + col.template_list("OBJECT_UL_EntityField_list", "", qfentity, "fields", qfentity, "field_idx", rows=3) col = row.column(align=True) - col.operator("object.entprop_add", icon='ZOOMIN', text="") - col.operator("object.entprop_remove", icon='ZOOMOUT', text="") + col.operator("object.entprop_add", icon='ADD', text="") + col.operator("object.entprop_remove", icon='REMOVE', text="") if len(qfentity.fields) > qfentity.field_idx >= 0: row = layout.row() field = qfentity.fields[qfentity.field_idx] @@ -246,8 +257,7 @@ def entity_box(entityclass): mesh = bpy.data.meshes.new(name) mesh.from_pydata(verts, [], faces) mat = bpy.data.materials.new(name) - mat.diffuse_color = color - mat.use_raytrace = False + mat.diffuse_color = color + (1,) mesh.materials.append(mat) return mesh @@ -258,7 +268,7 @@ def set_entity_props(obj, ent): qfe.classname = ent.d["classname"] except TypeError: #FIXME hmm, maybe an enum wasn't the most brilliant idea? - qfe.classname + qfe.classname = '.' if "spawnflags" in ent.d: flags = int(float(ent.d["spawnflags"])) for i in range(12): @@ -296,5 +306,17 @@ def add_entity(self, context, entclass): context.user_preferences.edit.use_global_undo = True return {'FINISHED'} -def register(): - bpy.types.Object.qfentity = PointerProperty(type=QFEntity) +classes_to_register = ( + VIEW3D_PT_QFEntityRelations, + OBJECT_UL_EntityField_list, + QFEntityProp, + QFEntity, + QFEntpropAdd, + QFEntpropRemove, + OBJECT_PT_EntityPanel, +) +menus_to_register = ( +) +custom_properties_to_register = ( + (bpy.types.Object, "qfentity", QFEntity), +) diff --git a/tools/io_qfmap/entityclass.py b/tools/io_qfmap/entityclass.py index f659c5d53..43e0b6604 100644 --- a/tools/io_qfmap/entityclass.py +++ b/tools/io_qfmap/entityclass.py @@ -20,9 +20,14 @@ # import os -from .script import Script -from .qfplist import pldata -from . import quakechr +try: + from .script import Script + from .qfplist import pldata + from . import quakechr +except ImportError: + from script import Script + from qfplist import pldata + import quakechr MAX_FLAGS = 8 @@ -34,16 +39,46 @@ class EntityClassError(Exception): def entclass_error(self, msg): raise EntityClassError(self.filename, self.line, msg) +class EntityField: + def __init__(self, name, default, comment): + self.name = name + self.default = default + self.comment = comment + def to_dictionary(self): + d = {} + if self.default != None: + d["default"] = self.default + if self.comment: + d["comment"] = self.comment + if hasattr(self, "sounds"): + d["sounds"] = self.sounds + return d + @classmethod + def from_dictionary(cls, name, d): + if "default" in d: + default = d["default"] + else: + default = None + if "comment" in d: + comment = d["comment"] + else: + comment = "" + field = cls(name, default, comment) + if "sounds" in d: + field.sounds = d["sounds"] + return field + class EntityClass: - def __init__(self, name, color, size, flagnames, comment): + def __init__(self, name, color, size, flagnames, comment, fields): self.name = name self.color = color self.size = size self.flagnames = flagnames self.comment = comment + self.fields = fields @classmethod def null(cls): - return cls('', (1, 1, 1), None, (), "") + return cls('', (1, 1, 1), None, (), "", {}) @classmethod def from_quaked(cls, text, filename, line = 0): script = Script(filename, text) @@ -59,8 +94,49 @@ class EntityClass: else: size = None flagnames = () - comment = cls.extract_comment(script) - return cls(name, color, size, flagnames, comment) + comment = [] + fields = {} + script.quotes = False + script.single = "" + while script.tokenAvailable(True): + line = [] + while script.tokenAvailable(): + script.getToken() + if script.token[-2:] == "*/": + break; + line.append(script.token) + if line: + if ((line[0][0] == '"' and line[0][-1] == '"') + or (len(line) > 1 and line[1] == '=')): + if line[0][0] == '"': + fname = line[0][1:-1] + line = line[1:] + else: + fname = line[0] + line = line[2:] + default = None + for i, t in enumerate(line[:-1]): + if t[0] == '(' and line[i + 1] == "default)": + default = t[1:] + break + line = " ".join(line) + fields[fname] = EntityField(fname, default, line) + line = None + elif "sounds" in fields: + sounds = fields["sounds"] + if not hasattr(sounds, "sounds"): + sounds.sounds = [] + if line[0][-1] == ')': + line[0] = line[0][:-1] + sounds.sounds.append((line[0], " ".join(line[1:]))) + line = None + else: + line = " ".join(line) + if line: + comment.append(line) + if script.token[-2:] == "*/": + break; + return cls(name, color, size, flagnames, comment, fields) @classmethod def from_dictionary(cls, name, d): if "color" in d: @@ -81,11 +157,21 @@ class EntityClass: if "comment" in d: comment = d["comment"] else: - comment = "" - return cls(name, color, size, flagnames, comment) + comment = [] + if "fields" in d: + field_dict = d["fields"] + fields = {} + for f in field_dict: + fields[f] = EntityField.from_dictionary(f, field_dict[f]) + else: + fields = {} + return cls(name, color, size, flagnames, comment, fields) def to_dictionary(self): + fields = {} + for f in self.fields: + fields[f] = self.fields[f].to_dictionary() d = {"color":self.color, "flagnames":self.flagnames, - "comment":self.comment} + "comment":self.comment, "fields":fields} if self.size: d["size"] = self.size return d @@ -93,8 +179,11 @@ class EntityClass: def parse_vector(cls, script): if script.getToken() != "(": script.error("Missing (") - v = (float(script.getToken()), float(script.getToken()), - float(script.getToken())) + s = script.getToken(), script.getToken(), script.getToken() + try: + v = (float(s[0]), float(s[1]), float(s[2])) + except ValueError: + v = s if script.getToken() != ")": script.error("Missing )") return v @@ -118,7 +207,6 @@ class EntityClass: if len(flagnames) < MAX_FLAGS: flagnames.append(script.token) return tuple(flagnames) - @classmethod def extract_comment(cls, script): if not script.tokenAvailable(True): return "" @@ -140,6 +228,8 @@ class EntityClassDict: def __len__(self): return self.entity_classes.__len__() def __getitem__(self, key): + if key == '.': + return EntityClass.null() return self.entity_classes.__getitem__(key) def __iter__(self): return self.entity_classes.__iter__() @@ -201,3 +291,35 @@ class EntityClassDict: self.entity_classes = {} for k in ec.keys(): self.entity_classes[k] = EntityClass.from_dictionary(k, ec[k]) + +if __name__ == "__main__": + import sys + from pprint import pprint + from textwrap import TextWrapper + + mainwrap = TextWrapper(width = 70) + fieldwrap = TextWrapper(width = 50) + + ecd = EntityClassDict() + for fname in sys.argv[1:]: + ecd.scan_source(fname) + text = ecd.to_plist() + print(text) + ecd.from_plist(text) + for ec in ecd.entity_classes.values(): + print(f"{ec.name}: {ec.color} {ec.size} {ec.flagnames}") + for c in ec.comment: + mlines = mainwrap.wrap(c) + for m in mlines: + print(f" {m}") + print() + for f in ec.fields.values(): + print(f" {f.name}: {f.default}") + flines = fieldwrap.wrap(f.comment) + for l in flines: + print(f" {l}") + if f.name == "sounds": + for s in f.sounds: + print(f" {s[0]} {s[1]}") + print() + print() diff --git a/tools/io_qfmap/import_map.py b/tools/io_qfmap/import_map.py index 7d22912a0..e814cf380 100644 --- a/tools/io_qfmap/import_map.py +++ b/tools/io_qfmap/import_map.py @@ -61,9 +61,8 @@ def load_material(tx): if tx.name in bpy.data.materials: return bpy.data.materials[tx.name] mat = bpy.data.materials.new(tx.name) - mat.diffuse_color = (1, 1, 1) + mat.diffuse_color = (1, 1, 1, 1) mat.specular_intensity = 0 - mat.use_raytrace = False if tx.image: tex = bpy.data.textures.new(tx.name, 'IMAGE') tex.extension = 'REPEAT' @@ -77,19 +76,23 @@ def load_material(tx): return mat def load_textures(texdefs, wads): + class MT: + def __init__(self, x, y): + self.width = x + self.height = y for tx in texdefs: if hasattr(tx, "miptex"): continue - try: - tx.miptex = wads[0].getData(tx.name) - tx.image = load_image(tx) - except KeyError: - class MT: - def __init__(self, x, y): - self.width = x - self.height = y + if not wads or not wads[0]: tx.miptex = MT(64,64) tx.image = None + else: + try: + tx.miptex = wads[0].getData(tx.name) + tx.image = load_image(tx) + except KeyError: + tx.miptex = MT(64,64) + tx.image = None tx.material = load_material(tx) def build_uvs(verts, faces, texdefs): @@ -110,7 +113,7 @@ def process_entity(ent, wads): classname = ent.d["classname"] name = classname if "classname" in ent.d and ent.d["classname"][:5] == "light": - light = bpy.data.lamps.new("light", 'POINT') + light = bpy.data.lights.new("light", 'POINT') if "light" in ent.d: light.distance = float(ent.d["light"]) elif "_light" in ent.d: @@ -150,7 +153,7 @@ def process_entity(ent, wads): tx.matindex = len(mesh.materials) mesh.materials.append(tx.material) mesh.from_pydata(verts, [], faces) - uvlay = mesh.uv_textures.new(name) + """uvlay = mesh.uv_textures.new(name) uvloop = mesh.uv_layers[0] for i, texpoly in enumerate(uvlay.data): poly = mesh.polygons[i] @@ -159,7 +162,7 @@ def process_entity(ent, wads): texpoly.image = tx.image poly.material_index = tx.matindex for j, k in enumerate(poly.loop_indices): - uvloop.data[k].uv = uv[j] + uvloop.data[k].uv = uv[j]"""#FIXME mesh.update() obj = bpy.data.objects.new(name, mesh) else: @@ -172,8 +175,8 @@ def process_entity(ent, wads): obj = bpy.data.objects.new(name, mesh) else: obj = bpy.data.objects.new(name, None) - obj.empty_draw_type = 'CUBE' - obj.empty_draw_size = 8 + obj.empty_display_type = 'CUBE' + obj.empty_display_size = 8 obj.show_name = True if "origin" in ent.d: obj.location = parse_vector (ent.d["origin"]) @@ -189,50 +192,52 @@ def process_entity(ent, wads): del ent.d["angles"] obj.rotation_mode = 'XZY' obj.rotation_euler = angles * pi / 180 - bpy.context.scene.objects.link(obj) - bpy.context.scene.objects.active=obj - obj.select = True + bpy.context.layer_collection.collection.objects.link(obj) + bpy.context.view_layer.objects.active = obj + obj.select_set(True) set_entity_props(obj, ent) def import_map(operator, context, filepath): - bpy.context.user_preferences.edit.use_global_undo = False - - for obj in bpy.context.scene.objects: - obj.select = False + undo = bpy.context.preferences.edit.use_global_undo + bpy.context.preferences.edit.use_global_undo = False try: + for obj in bpy.context.scene.objects: + obj.select_set(False) entities = parse_map (filepath) except MapError as err: operator.report({'ERROR'}, repr(err)) return {'CANCELLED'} - wads=[] - if entities: - if "_wad" in entities[0].d: - wads = entities[0].d["_wad"].split(";") - elif "wad" in entities[0].d: - wads = entities[0].d["wad"].split(";") - wadpath = bpy.context.scene.qfmap.wadpath - for i in range(len(wads)): - try: - wads[i] = WadFile.load(os.path.join(wadpath, wads[i])) - except IOError: + else: + wads=[] + if entities: + if "_wad" in entities[0].d: + wads = entities[0].d["_wad"].split(";") + elif "wad" in entities[0].d: + wads = entities[0].d["wad"].split(";") + wadpath = bpy.context.scene.qfmap.wadpath + for i in range(len(wads)): try: - wads[i] = WadFile.load(os.path.join(wadpath, - os.path.basename(wads[i]))) + wads[i] = WadFile.load(os.path.join(wadpath, wads[i])) except IOError: - #give up - operator.report({'INFO'}, "Cant't find %s" % wads[i]) - wads[i] = None - for ent in entities: - process_entity(ent, wads) - bpy.context.user_preferences.edit.use_global_undo = True + try: + wads[i] = WadFile.load(os.path.join(wadpath, + os.path.basename(wads[i]))) + except IOError: + #give up + operator.report({'INFO'}, "Cant't find %s" % wads[i]) + wads[i] = None + for ent in entities: + process_entity(ent, wads) + finally: + bpy.context.preferences.edit.use_global_undo = undo return {'FINISHED'} def import_pts(operator, context, filepath): bpy.context.user_preferences.edit.use_global_undo = False for obj in bpy.context.scene.objects: - obj.select = False + obj.select_set(False) lines = open(filepath, "rt").readlines() verts = [None] * len(lines) @@ -248,6 +253,6 @@ def import_pts(operator, context, filepath): obj = bpy.data.objects.new("leak points", mesh) bpy.context.scene.objects.link(obj) bpy.context.scene.objects.active=obj - obj.select = True + obj.select_set(True) bpy.context.user_preferences.edit.use_global_undo = True return {'FINISHED'} diff --git a/tools/io_qfmap/init.py b/tools/io_qfmap/init.py new file mode 100644 index 000000000..199bb71fa --- /dev/null +++ b/tools/io_qfmap/init.py @@ -0,0 +1,294 @@ +# vim:ts=4:et +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# + +import bpy +from bpy.props import BoolProperty, FloatProperty, StringProperty, EnumProperty +from bpy.props import FloatVectorProperty, PointerProperty +from bpy_extras.io_utils import ExportHelper, ImportHelper, path_reference_mode, axis_conversion +from bpy.app.handlers import persistent + +from .entityclass import EntityClassDict, EntityClassError +from . import entity +from . import import_map +from . import export_map + +def ecm_draw(self, context): + layout = self.layout + for item in self.menu_items: + if type(item[1]) is str: + ec = context.scene.qfmap.entity_classes[item[1]] + if ec.size: + icon = 'OBJECT_DATA' + else: + icon = 'MESH_DATA' + op = layout.operator("object.add_entity", text=item[0], icon=icon) + op.entclass=item[1] + if ec.comment: + pass + else: + layout.menu(item[1].bl_idname) + +class EntityClassMenu: + @classmethod + def clear(cls): + while cls.menu_items: + if type(cls.menu_item[0][1]) is not str: + bpy.utils.unregister_class(cls.menu_items[0][1]) + cls.menu_items[0][1].clear() + del cls.menu_items[0] + @classmethod + def build(cls, menudict, name="INFO_MT_entity_add", label="entity"): + items = list(menudict.items()) + items.sort() + menu_items = [] + for i in items: + i = list(i) + if type(i[1]) is dict: + if i[0]: + nm = "_".join((name, i[0])) + else: + nm = name + i[1] = cls.build(i[1], nm, i[0]) + menu_items.append(i) + attrs = {} + attrs["menu_items"] = menu_items + attrs["draw"] = ecm_draw + attrs["bl_idname"] = name + attrs["bl_label"] = label + attrs["clear"] = cls.clear + menu = type(name, (bpy.types.Menu,), attrs) + bpy.utils.register_class(menu) + return menu + +@persistent +def scene_load_handler(dummy): + for scene in bpy.data.scenes: + if hasattr(scene, "qfmap"): + scene.qfmap.script_update(bpy.context) + +class MapeditMessage(bpy.types.Operator): + bl_idname = "qfmapedit.message" + bl_label = "Message" + type: StringProperty() + message: StringProperty() + + def execute(self, context): + self.report({'INFO'}, message) + return {'FINISHED'} + def invoke(self, context, event): + wm = context.window_manager + return wm.invoke_popup(self, width=400, height=200) + def draw(self, context): + self.layout.label(self.type, icon='ERROR') + self.layout.label(self.message) + +def scan_entity_classes(context): + qfmap = context.scene.qfmap + if not qfmap.dirpath: + return + qfmap.entity_classes.from_source_tree(qfmap.dirpath) + name = context.scene.name + '-EntityClasses' + if name in bpy.data.texts: + txt = bpy.data.texts[name] + else: + txt = bpy.data.texts.new(name) + txt.from_string(qfmap.entity_classes.to_plist()) + qfmap.script = name + +def parse_entity_classes(context): + context.scene.qfmap.script_update(context) + +def ec_dir_update(self, context): + try: + scan_entity_classes(context) + except EntityClassError as err: + self.dirpath = "" + bpy.ops.qfmapedit.message('INVOKE_DEFAULT', type="Error", + message="Entity Class Error: %s" % err) + +def ec_script_update(self, context): + self.script_update(context) + +class AddEntity(bpy.types.Operator): + '''Add an entity''' + bl_idname = "object.add_entity" + bl_label = "Entity" + entclass: StringProperty(name = "entclass") + + def execute(self, context): + keywords = self.as_keywords() + return entity.add_entity(self, context, **keywords) + +class QFEntityClassScan(bpy.types.Operator): + '''Rescan the specified QuakeC source tree''' + bl_idname = "scene.scan_entity_classes" + bl_label = "RELOAD" + + def execute(self, context): + scan_entity_classes(context) + return {'FINISHED'} + +class QFEntityClassParse(bpy.types.Operator): + '''Reparse the specified entity class script''' + bl_idname = "scene.parse_entity_classes" + bl_label = "RELOAD" + + def execute(self, context): + parse_entity_classes(context) + return {'FINISHED'} + +class QFEntityClasses(bpy.types.PropertyGroup): + wadpath: StringProperty( + name="wadpath", + description="Path to search for wad files", + subtype='DIR_PATH') + dirpath: StringProperty( + name="dirpath", + description="Path to qc source tree", + subtype='DIR_PATH', + update=ec_dir_update) + script: StringProperty( + name="script", + description="entity class storage", + update=ec_script_update) + entity_classes = EntityClassDict() + ecm = EntityClassMenu.build({}) + entity_targets = {} + target_entities = [] + + def script_update(self, context): + if self.script in bpy.data.texts: + script = bpy.data.texts[self.script].as_string() + self.entity_classes.from_plist(script) + menudict = {} + entclasses = self.entity_classes.keys() + for ec in entclasses: + ecsub = ec.split("_") + d = menudict + for sub in ecsub[:-1]: + if sub not in d: + d[sub] = {} + elif type(d[sub]) is str: + d[sub] = {"":d[sub]} + d = d[sub] + sub = ecsub[-1] + if sub in d: + d[sub][""] = ec + else: + d[sub] = ec + self.__class__.ecm = EntityClassMenu.build(menudict) + +class OBJECT_PT_QFECPanel(bpy.types.Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + #bl_context = 'scene' + bl_category = "View" + bl_label = 'QF Entity Classes' + + @classmethod + def poll(cls, context): + return True + + def draw(self, context): + layout = self.layout + scene = context.scene + row = layout.row() + layout.prop(scene.qfmap, "wadpath") + row = layout.row() + row.prop(scene.qfmap, "dirpath") + row.operator("scene.scan_entity_classes", text="", icon="FILE_REFRESH") + row = layout.row() + row.prop(scene.qfmap, "script") + row.operator("scene.parse_entity_classes", text="", icon="FILE_REFRESH") + +class ImportPoints(bpy.types.Operator, ImportHelper): + '''Load a Quake points File''' + bl_idname = "import_mesh.quake_points" + bl_label = "Import points" + + filename_ext = ".pts" + filter_glob: StringProperty(default="*.pts", options={'HIDDEN'}) + + def execute(self, context): + keywords = self.as_keywords (ignore=("filter_glob",)) + return import_map.import_pts(self, context, **keywords) + +class ImportMap(bpy.types.Operator, ImportHelper): + '''Load a Quake map File''' + bl_idname = "import_mesh.quake_map" + bl_label = "Import map" + + filename_ext = ".map" + filter_glob: StringProperty(default="*.map", options={'HIDDEN'}) + + def execute(self, context): + keywords = self.as_keywords (ignore=("filter_glob",)) + return import_map.import_map(self, context, **keywords) + +class ExportMap(bpy.types.Operator, ExportHelper): + '''Save a Quake map File''' + + bl_idname = "export_mesh.quake_map" + bl_label = "Export map" + + filename_ext = ".map" + filter_glob: StringProperty(default="*.map", options={'HIDDEN'}) + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + keywords = self.as_keywords (ignore=("check_existing", "filter_glob")) + return export_map.export_map(self, context, **keywords) + +def menu_func_import(self, context): + self.layout.operator(ImportMap.bl_idname, text="Quake map (.map)") + self.layout.operator(ImportPoints.bl_idname, text="Quake points (.pts)") + +def menu_func_export(self, context): + self.layout.operator(ExportMap.bl_idname, text="Quake map (.map)") + +def menu_func_add(self, context): + self.layout.menu(context.scene.qfmap.ecm.bl_idname, icon='PLUGIN') + +classes_to_register = ( + MapeditMessage, + AddEntity, + QFEntityClassScan, + QFEntityClassParse, + QFEntityClasses, + OBJECT_PT_QFECPanel, + ImportPoints, + ImportMap, + ExportMap, +) +menus_to_register = ( + (bpy.types.TOPBAR_MT_file_import, menu_func_import), + (bpy.types.TOPBAR_MT_file_export, menu_func_export), + (bpy.types.VIEW3D_MT_add, menu_func_add), +) +custom_properties_to_register = ( + (bpy.types.Scene, "qfmap", QFEntityClasses), +) +handlers_to_register = ( + ("load_post", scene_load_handler), +) diff --git a/tools/io_qfmap/map.py b/tools/io_qfmap/map.py index b0820de69..e47b7563a 100644 --- a/tools/io_qfmap/map.py +++ b/tools/io_qfmap/map.py @@ -38,8 +38,8 @@ class Texinfo: norm = s_vec.cross(t_vec) q = Quaternion(norm, rotate * pi / 180) self.vecs = [None] * 2 - self.vecs[0] = (q * s_vec / scale[0], s_offs) - self.vecs[1] = (q * t_vec / scale[1], t_offs) + self.vecs[0] = (q @ s_vec / scale[0], s_offs) + self.vecs[1] = (q @ t_vec / scale[1], t_offs) def __cmp__(self, other): return self.name == other.name and self.vecs == other.vecs @classmethod diff --git a/tools/io_qfmap/script.py b/tools/io_qfmap/script.py index 9e34934f9..873384052 100644 --- a/tools/io_qfmap/script.py +++ b/tools/io_qfmap/script.py @@ -25,13 +25,20 @@ class ScriptError(Exception): self.line = line class Script: - def __init__(self, filename, text, single="{}()':"): - self.filename = filename - self.text = text - self.single = single - self.pos = 0 - self.line = 1 + def __init__(self, filename, text, single="{}()':", quotes=True): + if text[0:3] == "\xef\xbb\xbf": + text = text[3:] + elif text[0] == u"\ufeff": + text = text[1:] + self.token = "" self.unget = False + self.text = text + self.pos = 0 + self.filename = filename + self.line = 1 + self.no_quote_lines = False + self.single = single + self.quotes = quotes def error(self, msg): raise ScriptError(self.filename, self.line, msg) def tokenAvailable(self, crossline=False): @@ -87,7 +94,7 @@ class Script: if not crossline: self.error("line is incomplete") return None - if self.text[self.pos] == "\"": + if self.quotes and self.text[self.pos] == "\"": self.pos += 1 start = self.pos if self.text[self.pos] == len(self.text): @@ -97,6 +104,9 @@ class Script: self.error("EOF inside quoted string") return None if self.text[self.pos] == "\n": + if self.no_quote_lines: + self.error("EOL inside quoted string") + return None self.line += 1 self.pos += 1 self.token = self.text[start:self.pos] diff --git a/tools/misc/cvar2.py b/tools/misc/cvar2.py new file mode 100644 index 000000000..338a25904 --- /dev/null +++ b/tools/misc/cvar2.py @@ -0,0 +1,335 @@ +import re +import sys +from pprint import pprint +from io import StringIO +from textwrap import TextWrapper +import os + +STRING = r'\s*"((\\.|[^"\\])*)"\s*' +QSTRING = r'\s*("(\\.|[^"\\])*")\s*' +ID = r'([a-zA-Z_][a-zA-Z_0-9]*)' +STORE = r'(VISIBLE\s+|static\s+|extern\s+|)' +FIELD = r'(string|value|int_val|vec)\b' +FLAGS = r'\s*(' + ID + r'\s*(\|\s*' + ID + r')*)\s*' +TYPE = r'(cvar_t|struct cvar_s)\s*\*' +STUFF = r'\s*(.*)\s*' +cvar_decl_re = re.compile(r'^' + STORE + TYPE + ID + r';.*') +cvar_get_re = re.compile(r'.*\bCvar_Get\s*\(\s*"') +cvar_get_join_prev_re = re.compile(r'^\s*Cvar_Get\s*\(\s*".*') +cvar_get_assign_re = re.compile(r'\s*' + ID + r'\s*=\s*$') +cvar_get_incomplete_re = re.compile(r'.*Cvar_Get\s*\(\s*"[^;]*$') +cvar_get_complete_re = re.compile(r'.*Cvar_Get\s*\(\s*".*\);.*$') +cvar_create_re = re.compile(r'\s*(\w+)\s*=\s*Cvar_Get\s*\(' + + STRING + + ',(' + STRING + '|\s*(.*)\s*),' + FLAGS + r',\s*([^,]+),\s*' + STUFF + r'\);.*') +cvar_use_re = re.compile(ID + r'\s*\->\s*' + FIELD) +cvar_listener_re = re.compile(ID + r'\s*\(\s*cvar_t\s*\*' + ID + '\s*\)\s*') +string_or_id_re = re.compile(f'({ID}|{QSTRING})') +cvar_setrom_re = re.compile(r'\s*Cvar_SetFlags\s*\(\s*' + ID + '.*CVAR_ROM\);.*') +id_re = re.compile(f'\\b{ID}\\b') + +#cvar_decl_re = re.compile(r'^cvar_t\s+(\w+)\s*=\s*\{\s*("[^"]*")\s*,\s*("[^"]*")(\s*,\s*(\w+)(\s*,\s*(\w+))?)?\s*\}\s*;(\s*//\s*(.*))?\n') +cvar_set_re = re.compile(r'^(\s+)(Cvar_Set|Cvar_SetValue)\s*\(' + ID + ',\s*(.*)\);(.*)') + +wrapper = TextWrapper(width = 69, drop_whitespace = False, + break_long_words = False) + +class cvar: + def __init__(self, c_name, name, default, flags, callback, description): + self.c_name = c_name + self.name = name + self.default = default + self.flags = flags + if callback in ['NULL', '0']: + callback = 0 + self.callback = callback + if description in ['NULL', '0', '']: + description = 'FIXME no description' + self.desc = description + if self.desc==None: + self.desc = 'None' + self.data = 0 + self.files = [] + self.type = None + self.c_type = None + self.used_field = None + self.uses = [] + def guess_type(self): + if self.used_field == "value": + self.type = "&cexpr_float" + self.c_type = "float " + elif self.used_field == "int_val": + self.type = "&cexpr_int" + self.c_type = "int " + elif self.used_field == "vec": + self.type = "&cexpr_vector" + self.c_type = "vec4f_t " + elif self.used_field == "string": + self.type = 0 + self.c_type = "char *" + else: + self.type = "0/* not used */" + self.c_type = "char *" + def add_file(self, file, line): + self.files.append((file, line)) + def add_use(self, field, file, line): + if self.used_field and self.used_field != field: + print(f"{file}:{line}: cvar {self.name} used inconsistently") + self.used_field = field + self.uses.append((field, file, line)) + def __repr__(self): + return f"cvar(({self.c_name}, {self.name}, {self.default}, {self.flags}, {self.callback}, {self.desc})" + def __str__(self): + return self.__repr__() + def struct(self): + A = [ + f'static cvar_t {self.c_name}_cvar = {{\n', + f'\t.name = "{self.name}",\n', + f'\t.description =\n', + ] + B = [ + f'\t.default_value = "{self.default}",\n', + f'\t.flags = {self.flags},\n', + f'\t.value = {{ .type = {self.type}, .value = &{self.c_name} }},\n', + f'}};\n', + ] + desc = [] + dstrings = [m[0].strip() for m in string_or_id_re.findall(self.desc)] + def wrap(string, comma = ""): + if string[0] == '"' and string != '""': + string = string[1:-1] + dlines = wrapper.wrap(string) + desc.extend([f'\t\t"{l}"\n' for l in dlines[:-1]]) + desc.append(f'\t\t"{dlines[-1]}"{comma}\n') + else: + desc.append(f'\t\t{string}{comma}\n') + for dstring in dstrings[:-1]: + wrap (dstring) + wrap(dstrings[-1], ",") + return A + desc + B + def var(self, storage): + if storage: + storage += " " + return [f'{storage}{self.c_type}{self.c_name};\n'] + def set_rom(self): + if self.flags == 'CVAR_NONE': + self.flags = 'CVAR_ROM' + else: + self.flags = '|'.join([self.flags, 'CVAR_ROM']) + def register(self): + return [f'\tCvar_Register (&{self.c_name}_cvar, {self.callback}, {self.data});\n'] + + +cvar_decls = {} +cvar_dict = {} +cvar_sets = [] +cvar_rom = set() +cvar_listeners = {} +cvar_listeners_done = set() +cvar_listeners_multi = set() +files = {} +modified = set() + +cvar_struct = [ +"\tconst char *name;\n", +"\tconst char *description;\n", +"\tconst char *default_value;\n", +"\tunsigned flags;\n", +"\texprval_t value;\n", +"\tint (*validator) (const struct cvar_s *var);\n" +"\tstruct cvar_listener_set_s *listeners;\n" +"\tstruct cvar_s *next;\n" +] +cvar_reg = "void Cvar_Register (cvar_t *var, cvar_listener_t listener, void *data);\n" +cvar_cexpr = '#include "QF/cexpr.h"\n' +cvar_set = 'void Cvar_Set (const char *var, const char *value);\n' +cvar_setvar = 'void Cvar_SetVar (cvar_t *var, const char *value);\n' +cvar_info = 'struct cvar_s;\nvoid Cvar_Info (void *data, const struct cvar_s *cvar);\n' +nq_cl_cvar_info = 'nq/include/client.h' +nq_sv_cvar_info = 'nq/include/server.h' +qw_cl_cvar_info = 'qw/include/client.h' +qw_sv_cvar_info = 'qw/include/server.h' + +cvar_file = "include/QF/cvar.h" +line_substitutions = [ + (cvar_file, (40, 60), cvar_struct), + (cvar_file, (96, 100), cvar_reg), + (cvar_file, (35, 35), cvar_cexpr), + (cvar_file, (109, 110), cvar_set), + (cvar_file, (110, 111), cvar_setvar), + (nq_cl_cvar_info, (294,295), cvar_info), + (nq_sv_cvar_info, (300,301), cvar_info), + (qw_cl_cvar_info, (296,297), cvar_info), + (qw_sv_cvar_info, (634,635), cvar_info), +] + +def get_cvar_defs(fname): + fi = open(fname,'rt') + f = files[fname] = fi.readlines() + i = 0 + while i < len(f): + cr = cvar_setrom_re.match(f[i]) + if cr: + cvar_rom.add(cr[1]) + del(f[i]) + continue + if cvar_get_join_prev_re.match(f[i]): + if cvar_get_assign_re.match(f[i - 1]): + f[i - 1] = f"{f[i - 1].rstrip()} {f[i].lstrip()}" + del(f[i]) + i -= 1 + if cvar_get_incomplete_re.match(f[i]): + while not cvar_get_complete_re.match(f[i]): + f[i + 1] = f[i + 1].lstrip() + f[i] = f[i].rstrip() + if f[i][-1] == '"' and f[i + 1][0] == '"': + #string concatentation + f[i] = f[i][:-1] + f[i + 1][1:] + else: + f[i] = f"{f[i]} {f[i + 1]}" + del(f[i + 1]) + cd = cvar_decl_re.match(f[i]) + if cd: + if cd[3] not in cvar_decls: + cvar_decls[cd[3]] = [] + cvar_decls[cd[3]].append((fname, i)) + cl = cvar_listener_re.match(f[i]) + if cl: + if cl[1] not in cvar_listeners: + cvar_listeners[cl[1]] = [] + cvar_listeners[cl[1]].append((fname, i, cl[2])) + cc = cvar_create_re.match(f[i]) + if cc: + if cc.group(7): + default = 7 + else: + default = 5 + cc = cc.group(1, 2, default, 8, 12, 13) + if cc[0] not in cvar_dict: + cvar_dict[cc[0]] = cvar(*cc) + cvar_dict[cc[0]].add_file(fname, i) + cs = cvar_set_re.match(f[i]) + if cs: + cvar_sets.append((fname, i)) + i += 1 + fi.close() + +for f in sys.argv[1:]: + get_cvar_defs(f) +#for cv in cvar_decls.keys(): +# if cv not in cvar_dict: +# print(f"cvar {cv} declared but never created") +for file in files.items(): + fname,f = file + for i in range(len(f)): + for use in cvar_use_re.finditer(f[i]): + if use[1] in cvar_dict and cvar_dict[use[1]]: + cvar_dict[use[1]].add_use(use[2], fname, i) +keys = list(cvar_dict.keys()) +for cv in keys: + var = cvar_dict[cv] + if cv not in cvar_decls or var.callback not in cvar_listeners: + continue + if var.callback in cvar_listeners: + if var.callback in cvar_listeners_done: + if not var.callback in cvar_listeners_multi: + print(f"WARNING: {var.callback} has multiple uses") + cvar_listeners_multi.add (var.callback) + else: + cvar_listeners_done.add (var.callback); +cvar_listeners_done.clear() +for cv in keys: + var = cvar_dict[cv] + var.guess_type() + if var.c_name in cvar_rom: + print(var.c_name, 'rom') + var.set_rom() + if cv in cvar_decls: + if var.callback in cvar_listeners_multi: + var.data = f'&{var.c_name}' + for d in cvar_decls[cv]: + decl = cvar_decl_re.match(files[d[0]][d[1]]) + storage = decl[1].strip() + if storage == "extern": + subst = var.var(storage) + else: + subst = var.var(storage)+var.struct() + line_substitutions.append((d[0], (d[1], d[1] + 1), subst)) + for c in var.files: + line_substitutions.append((c[0], (c[1], c[1] + 1), var.register())) + for u in var.uses: + # use substitutions are done immediately because they never + # change the number of lines + field, fname, line = u + file = files[fname] + places = [] + for use in cvar_use_re.finditer(file[line]): + cv, field = use.group(1, 2) + if cv == var.c_name: + places.append((use.start(), use.end())) + places.reverse() + for p in places: + file[line] = file[line][:p[0]] + var.c_name + file[line][p[1]:] + modified.add(fname) + else: + #for f in cvar_dict[cv].files: + # print(f"{f[0]}:{f[1]}: {cv} created but not kept") + pass +for cv in keys: + var = cvar_dict[cv] + if cv not in cvar_decls or var.callback not in cvar_listeners: + continue + if var.callback in cvar_listeners_done: + continue + for listener in cvar_listeners[var.callback]: + fname, line, c_name = listener + file = files[fname] + file[line] = f"{var.callback} (void *data," " const cvar_t *cvar)\n" + modified.add(fname) + cvar_listeners_done.add (var.callback); + multi = var.callback in cvar_listeners_multi + line += 1 + while line < len(file) and file[line] != '}\n': + line += 1 + places = [] + for use in cvar_use_re.finditer(file[line]): + cv, field = use.group(1, 2) + if cv == c_name: + subst = f'*({var.c_type}*)data' if multi else var.c_name + places.append((use.start(), use.end(), subst)) + places.reverse() + for p in places: + file[line] = file[line][:p[0]] + p[2] + file[line][p[1]:] + modified.add(fname) + places = [] + for v in id_re.finditer(file[line]): + if v[1] == c_name: + places.append((v.start(), v.end(), "cvar")) + for p in places: + file[line] = file[line][:p[0]] + p[2] + file[line][p[1]:] + modified.add(fname) +for s in cvar_sets: + cs = cvar_set_re.match(files[s[0]][s[1]]) + subst = None + val = cs[4] + if cs[2] == "Cvar_SetValue" and cs[3] in cvar_dict: + if val[0] == '(': + #strip off a cast + val = val[val.index(')') + 1:].strip() + subst = f'{cs[1]}{cs[3]} = {val};{cs[5]}\n' + elif cs[2] == "Cvar_Set" and cs[3] in cvar_dict: + subst = f'{cs[1]}{cs[2]} ("{cvar_dict[cs[3]].name}", {val});{cs[5]}\n' + elif cs[2] == "Cvar_Set": + subst = f'{cs[1]}Cvar_SetVar ({cs[3]}, {val});{cs[5]}\n' + if subst: + line_substitutions.append((s[0], (s[1], s[1] + 1), subst)) +line_substitutions.sort(reverse=True, key=lambda s: s[1][1]) +for substitution in line_substitutions: + file, lines, subst = substitution + modified.add(file) + files[file][lines[0]:lines[1]] = subst +for f in files.keys(): + if f in modified: + file = open(f, "wt") + file.writelines(files[f]) + file.close () diff --git a/tools/misc/mdl.py b/tools/misc/mdl.py index 00c788316..2aba831ba 100644 --- a/tools/misc/mdl.py +++ b/tools/misc/mdl.py @@ -3,6 +3,7 @@ from pprint import * import sys model = open(sys.argv[1],"rb").read() +file_bytes = len(model) m = unpack ("4s i 3f 3f f 3f i i i i i i i", model[:76]) model = model[76:] m = m[0:2] + (m[2:5],) + (m[5:8],) + m[8:9] + (m[9:12],) + m[12:20] @@ -11,6 +12,7 @@ if m[1] == 6: model = model[8:] pprint (m) +print(f"skins @ {file_bytes - len(model)}") skins = [] s = m[7] * m[8] for i in range(m[6]): @@ -22,15 +24,16 @@ for i in range(m[6]): else: n = unpack ("i", model[:4])[0] model = model [4:] - print n - k = (n, unpack (`n`+"f", model[:n*4]), []) + print (n) + k = (n, unpack (("%df" % n), model[:n*4]), []) model = model [n*4:] for j in range (n): k[2].append (model[:s]) model = model[s:] skins.append (k) -pprint (skins) +#pprint (skins) +print(f"stverts @ {file_bytes - len(model)}") stverts = [] for i in range(m[9]): x = unpack ("i i i", model[:12]) @@ -38,6 +41,7 @@ for i in range(m[9]): model = model [12:] pprint (stverts) +print(f"tris @ {file_bytes - len(model)}") tris = [] for i in range(m[10]): tris.append (unpack ("i i i i", model[:16])) @@ -45,13 +49,18 @@ for i in range(m[10]): model = model [16:] pprint (tris) +print(f"frames @ {file_bytes - len(model)}") frames = [] for i in range (m[11]): t = unpack ("i", model[:4])[0] model = model[4:] if t==0: if m[1] == 6: - x = unpack ("3B B 3B B 16s", model[:24]) + try: + x = unpack ("3B B 3B B 16s", model[:24]) + except: + print(len(model)) + raise f = (t, ((x[:3], x[3]), (x[4:7], x[7]), x[8]), []) model = model[24:] else: @@ -67,7 +76,7 @@ for i in range (m[11]): g = (t, unpack ("i 3B B 3B B", model[:12])) model = model[12:] n = g[1][0] - g = g + (unpack (`n`+"f", model[:n*4]), []) + g = g + (unpack (("%df" % n), model[:n*4]), []) model = model[n*4:] for k in range (g[1][0]): f = (unpack ("3B B 3B B 16s", model[:24]), []) diff --git a/tools/misc/sprite.py b/tools/misc/sprite.py new file mode 100644 index 000000000..56eda578b --- /dev/null +++ b/tools/misc/sprite.py @@ -0,0 +1,51 @@ +from struct import * +from pprint import * +import sys + +sprite_types = [ + "vp parallel upright", + "facing upright", + "vp parallel", + "oriented", + "vp parallel oriented", +] + +def read_frame(frame, sprite, pref=""): + x,y,w,h = unpack("4i", sprite[:4*4]) + sprite = sprite[4*4:] + print(f"{pref}[{x},{y},{w},{h}]"); + return sprite[w*h:] + +def read_group(frame, sprite): + numFrames = unpack("points->numpoints < 3) Sys_Error ("CheckFace: %i points", f->points->numpoints); - VectorCopy (planes[f->planenum].normal, facenormal); + VectorCopy (planes.a[f->planenum].normal, facenormal); if (f->planeside) VectorNegate (facenormal, facenormal); @@ -92,14 +91,14 @@ CheckFace (const face_t *f) j = i + 1 == f->points->numpoints ? 0 : i + 1; // check the point is on the face plane - d = PlaneDiff (p1, &planes[f->planenum]); + d = PlaneDiff (p1, &planes.a[f->planenum]); // point off plane autofix if (d < -ON_EPSILON || d > ON_EPSILON) if (options.verbosity > 1) printf ("CheckFace: point off plane: %g @ (%g %g %g)\n", d, p1[0], p1[1], p1[2]); - VectorMultSub (p1, d, planes[f->planenum].normal, p1); + VectorMultSub (p1, d, planes.a[f->planenum].normal, p1); // check the edge isn't degenerate p2 = f->points->points[j]; @@ -257,7 +256,6 @@ NormalizePlane (plane_t *dp) int FindPlane (const plane_t *dplane, int *side) { - int i; plane_t *dp, pl; vec_t dot; @@ -272,8 +270,8 @@ FindPlane (const plane_t *dplane, int *side) else *side = 1; - dp = planes; - for (i = 0; i < numbrushplanes; i++, dp++) { + dp = planes.a; + for (size_t i = 0; i < planes.size; i++, dp++) { dot = DotProduct (dp->normal, pl.normal); if (dot > 1.0 - ANGLEEPSILON && fabs(dp->dist - pl.dist) < DISTEPSILON) { // regular match @@ -281,14 +279,9 @@ FindPlane (const plane_t *dplane, int *side) } } - if (numbrushplanes == MAX_MAP_PLANES) - Sys_Error ("numbrushplanes == MAX_MAP_PLANES"); + DARRAY_APPEND (&planes, pl); - planes[numbrushplanes] = pl; - - numbrushplanes++; - - return numbrushplanes - 1; + return planes.size - 1; } /* @@ -347,7 +340,7 @@ CreateBrushFaces (void) GetVectorForKey (FoundEntity, "origin", offset); SetKeyValue (CurrentEntity, "origin", - va ("%g %g %g", VectorExpand (offset))); + va (0, "%g %g %g", VectorExpand (offset))); } for (i = 0; i < numbrushfaces; i++) { @@ -736,7 +729,7 @@ LoadBrush (const mbrush_t *mb, int hullnum) CreateBrushFaces (); } else if (mb->detail) { face_t *f; - for (f = brush_faces; f; f = f->next); + for (f = brush_faces; f; f = f->next) f->detail = 1; } diff --git a/tools/qfbsp/source/csg4.c b/tools/qfbsp/source/csg4.c index 444627334..150a39244 100644 --- a/tools/qfbsp/source/csg4.c +++ b/tools/qfbsp/source/csg4.c @@ -27,13 +27,13 @@ #include "QF/sys.h" -#include "brush.h" -#include "bsp5.h" -#include "csg4.h" -#include "draw.h" -#include "merge.h" -#include "solidbsp.h" -#include "surfaces.h" +#include "tools/qfbsp/include/brush.h" +#include "tools/qfbsp/include/bsp5.h" +#include "tools/qfbsp/include/csg4.h" +#include "tools/qfbsp/include/draw.h" +#include "tools/qfbsp/include/merge.h" +#include "tools/qfbsp/include/solidbsp.h" +#include "tools/qfbsp/include/surfaces.h" /** \addtogroup qfbsp_csg4 */ @@ -46,7 +46,7 @@ tjunction */ -face_t *validfaces[MAX_MAP_PLANES]; +visfacetset_t validfaces = DARRAY_STATIC_INIT (1024); face_t *inside, *outside; int brushfaces; int csgfaces; @@ -128,13 +128,13 @@ SplitFace (face_t *in, plane_t *split, face_t **front, face_t **back) \param precedence XXX */ static void -ClipInside (int splitplane, int frontside, qboolean precedence) +ClipInside (int splitplane, int frontside, bool precedence) { face_t *insidelist, *next, *f; face_t *frags[2]; plane_t *split; - split = &planes[splitplane]; + split = &planes.a[splitplane]; insidelist = NULL; for (f = inside; f; f = next) { @@ -176,7 +176,7 @@ ClipInside (int splitplane, int frontside, qboolean precedence) \param mirror If true, add extra faces that face the opposite direction. */ static void -SaveOutside (qboolean mirror) +SaveOutside (bool mirror) { face_t *f, *next, *newf; int planenum; @@ -195,12 +195,12 @@ SaveOutside (qboolean mirror) newf->contents[0] = f->contents[1]; newf->contents[1] = f->contents[0]; - validfaces[planenum] = MergeFaceToList (newf, - validfaces[planenum]); + validfaces.a[planenum] = MergeFaceToList (newf, + validfaces.a[planenum]); } - validfaces[planenum] = MergeFaceToList (f, validfaces[planenum]); - validfaces[planenum] = FreeMergeListScraps (validfaces[planenum]); + validfaces.a[planenum] = MergeFaceToList (f, validfaces.a[planenum]); + validfaces.a[planenum] = FreeMergeListScraps (validfaces.a[planenum]); } } @@ -234,13 +234,12 @@ BuildSurfaces (void) { face_t *count; face_t **f; - int i; surface_t *surfhead, *s; surfhead = NULL; - f = validfaces; - for (i = 0; i < numbrushplanes; i++, f++) { + f = validfaces.a; + for (size_t i = 0; i < planes.size; i++, f++) { if (!*f) continue; // nothing left on this plane @@ -299,12 +298,13 @@ CSGFaces (brushset_t *bs) brush_t *b1, *b2; face_t *f; int i; - qboolean overwrite; + bool overwrite; surface_t *surfhead; qprintf ("---- CSGFaces ----\n"); - memset (validfaces, 0, sizeof (validfaces)); + DARRAY_RESIZE (&validfaces, planes.size); + memset (validfaces.a, 0, validfaces.size * sizeof (validfaces.a[0])); csgfaces = brushfaces = csgmergefaces = 0; diff --git a/tools/qfbsp/source/info.c b/tools/qfbsp/source/info.c new file mode 100644 index 000000000..5eeef52fd --- /dev/null +++ b/tools/qfbsp/source/info.c @@ -0,0 +1,162 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#include + +#include "QF/qendian.h" +#include "QF/sys.h" +#include "QF/va.h" + +#include "tools/qfbsp/include/brush.h" +#include "tools/qfbsp/include/bsp5.h" +#include "tools/qfbsp/include/draw.h" +#include "tools/qfbsp/include/options.h" + +typedef struct lumpinfo_s { + const char *name; + int data; + int size; + const char *(*extra) (const void *data); +} lumpinfo_t; + +static const char * +num_textures (const void *data) +{ + __auto_type d = (const dmiptexlump_t *)data; + return va (0, " %7d", LittleLong (d->nummiptex)); +} + +#define O(f) field_offset (bsp_t, f) +static lumpinfo_t lump_info[] = { + { "entities", O(entdata), O(entdatasize) }, + { "planes", O(planes), O(numplanes) }, + { "textures", O(texdata), O(texdatasize), num_textures }, + { "vertices", O(vertexes), O(numvertexes) }, + { "visibility", O(visdata), O(visdatasize) }, + { "nodes", O(nodes), O(numnodes) }, + { "texinfo", O(texinfo), O(numtexinfo) }, + { "faces", O(faces), O(numfaces) }, + { "lighting", O(lightdata), O(lightdatasize) }, + { "clipnodes", O(clipnodes), O(numclipnodes) }, + { "leafs", O(leafs), O(numleafs) }, + { "marksurfaces", O(marksurfaces), O(nummarksurfaces) }, + { "edges", O(edges), O(numedges) }, + { "surfedges", O(surfedges), O(numsurfedges) }, + { "models", O(models), O(nummodels) }, +}; + +typedef struct { + int count; + int depth; + uint32_t node_faces; + uint32_t leaf_faces; +} nodedata_t; + +static void +get_nodedata (int node, nodedata_t *data, int depth) +{ + if (node < 0) { + // hit a leaf, no surfaces + if (depth > data->depth) { + data->depth = depth; + } + dleaf_t *leaf = bsp->leafs + ~node; + if (leaf->nummarksurfaces > data->leaf_faces) { + data->leaf_faces = leaf->nummarksurfaces; + } + return; + } + data->count++; + dnode_t *dnode = &bsp->nodes[node]; + if (dnode->numfaces > data->node_faces) { + data->node_faces = dnode->numfaces; + } + get_nodedata (dnode->children[0], data, depth + 1); + get_nodedata (dnode->children[1], data, depth + 1); +} + +static void +get_clipdata (int node, nodedata_t *data, int depth) +{ + if (node < 0 || (uint32_t) node >= bsp->numclipnodes) { + // hit contents + if (depth > data->depth) { + data->depth = depth; + } + return; + } + data->count++; + dclipnode_t *cnode = &bsp->clipnodes[node]; + get_clipdata (cnode->children[0], data, depth + 1); + get_clipdata (cnode->children[1], data, depth + 1); +} + +void +bspinfo () +{ + printf ("version: %x\n", bsp->header->version); + for (int i = 0; i < HEADER_LUMPS; i++) { + lump_t *lump = &bsp->header->lumps[i]; + lumpinfo_t *info = &lump_info[i]; + const void *data = *(void **)((byte *) bsp + info->data); + size_t *size = (size_t *)((byte *) bsp + info->size); + const char *extra = ""; + + if (info->extra) { + extra = info->extra (data); + } + + printf (" %-12s: %7d %7d %7zd%s\n", info->name, + lump->fileofs, lump->filelen, *size, extra); + + } + for (unsigned i = 0; i < bsp->nummodels; i++) { + dmodel_t *model = &bsp->models[i]; + printf ("model: *%d\n", i); + printf (" mins : [%g, %g, %g]\n", VectorExpand (model->mins)); + printf (" maxs : [%g, %g, %g]\n", VectorExpand (model->maxs)); + printf (" origin : [%g, %g, %g]\n", VectorExpand (model->origin)); + printf (" headnode:"); + nodedata_t nodedata[MAX_MAP_HULLS] = {}; + for (int j = 0; j < MAX_MAP_HULLS; j++) { + int k; + for (k = 1; k < j; k++) { + if (model->headnode[j] == model->headnode[k]) { + nodedata[j] = nodedata[k]; + break; + } + } + if (k >= j) { + if (j) { + get_clipdata (model->headnode[j], &nodedata[j], 0); + } else { + get_nodedata (model->headnode[j], &nodedata[j], 0); + } + } + printf (" %5d", model->headnode[j]); + } + printf ("\n nodes:"); + for (int j = 0; j < MAX_MAP_HULLS; j++) { + if (model->headnode[j] < model->headnode[0]) { + continue; + } + printf (" %5d", nodedata[j].count); + } + printf ("\n depth:"); + for (int j = 0; j < MAX_MAP_HULLS; j++) { + if (model->headnode[j] < model->headnode[0]) { + continue; + } + printf (" %5d", nodedata[j].depth); + } + printf ("\n"); + printf (" max faces: n: %d l:%d\n", + nodedata[0].node_faces, nodedata[0].leaf_faces); + printf (" visleafs: %d\n", model->visleafs); + printf (" faces : %7d %7d\n", model->firstface, model->numfaces); + } +} diff --git a/tools/qfbsp/source/map.c b/tools/qfbsp/source/map.c index f2038e44d..b8a16916b 100644 --- a/tools/qfbsp/source/map.c +++ b/tools/qfbsp/source/map.c @@ -40,7 +40,7 @@ #include "compat.h" -#include "map.h" +#include "tools/qfbsp/include/map.h" /** \addtogroup qfbsp_map */ @@ -64,7 +64,7 @@ int numdetailbrushes; script_t *map_script; -static void __attribute__ ((format (printf, 1, 2), noreturn)) +static void __attribute__ ((format (PRINTF, 1, 2), noreturn)) map_error (const char *fmt, ...) { va_list args; @@ -97,7 +97,7 @@ FindMiptex (const char *name) if (strcmp (name, "skip") == 0) return TEX_SKIP; if (!miptex_hash) - miptex_hash = Hash_NewTable (1023, miptex_getkey, 0, 0); + miptex_hash = Hash_NewTable (1023, miptex_getkey, 0, 0, 0); if (miptexnames) { index = (intptr_t) Hash_Find (miptex_hash, mpname); if (index) @@ -122,10 +122,11 @@ FindMiptex (const char *name) static int FindTexinfo (texinfo_t *t) { - int i, j; + size_t i; + int j; texinfo_t *tex; - if (t->miptex < 0) + if (t->miptex == ~0u) return t->miptex; // it's HINT or SKIP // set the special flag @@ -448,7 +449,7 @@ ParseBrush (void) free (verts); } -static qboolean +static bool ParseEntity (void) { if (!Script_GetToken (map_script, true)) @@ -545,7 +546,7 @@ LoadMapFile (const char *filename) qprintf ("%5i brushes (%i detail)\n", nummapbrushes, numdetailbrushes); qprintf ("%5i entities\n", num_entities); qprintf ("%5i textures\n", nummiptexnames); - qprintf ("%5i texinfo\n", bsp->numtexinfo); + qprintf ("%5zd texinfo\n", bsp->numtexinfo); } void @@ -620,7 +621,7 @@ WriteEntitiesToString (void) dstring_appendstr (buf, "{\n"); for (ep = entities[i].epairs; ep; ep = ep->next) { - dstring_appendstr (buf, va ("\"%s\" \"%s\"\n", + dstring_appendstr (buf, va (0, "\"%s\" \"%s\"\n", ep->key, ep->value)); } dstring_appendstr (buf, "}\n"); diff --git a/tools/qfbsp/source/merge.c b/tools/qfbsp/source/merge.c index 8551336df..e30addd79 100644 --- a/tools/qfbsp/source/merge.c +++ b/tools/qfbsp/source/merge.c @@ -23,12 +23,12 @@ #include "QF/sys.h" -#include "brush.h" -#include "bsp5.h" -#include "csg4.h" -#include "draw.h" -#include "merge.h" -#include "surfaces.h" +#include "tools/qfbsp/include/brush.h" +#include "tools/qfbsp/include/bsp5.h" +#include "tools/qfbsp/include/csg4.h" +#include "tools/qfbsp/include/draw.h" +#include "tools/qfbsp/include/merge.h" +#include "tools/qfbsp/include/surfaces.h" /** \addtogroup qfbsp_merge */ @@ -56,7 +56,7 @@ TryMerge (const face_t *f1, const face_t *f2) face_t *newf; int i, j, k, l; plane_t *plane; - qboolean keep1, keep2; + bool keep1, keep2; vec3_t normal, delta, planenormal; vec_t dot; vec_t *p1, *p2, *p3, *p4, *back; @@ -74,7 +74,6 @@ TryMerge (const face_t *f1, const face_t *f2) return NULL; p1 = p2 = NULL; - j = 0; // find a common edge for (i = 0; i < f1p->numpoints; i++) { @@ -97,7 +96,7 @@ TryMerge (const face_t *f1, const face_t *f2) found_edge: // check slope of connected lines // if the slopes are colinear, the point can be removed - plane = &planes[f1->planenum]; + plane = &planes.a[f1->planenum]; VectorCopy (plane->normal, planenormal); if (f1->planeside) VectorNegate (planenormal, planenormal); @@ -152,7 +151,7 @@ found_edge: return newf; } -qboolean mergedebug; +bool mergedebug; face_t * MergeFaceToList (face_t *face, face_t *list) { diff --git a/tools/qfbsp/source/nodraw.c b/tools/qfbsp/source/nodraw.c index c333313c5..b7db6dc94 100644 --- a/tools/qfbsp/source/nodraw.c +++ b/tools/qfbsp/source/nodraw.c @@ -21,7 +21,7 @@ # include "config.h" #endif -#include "draw.h" +#include "tools/qfbsp/include/draw.h" /** \addtogroup qfbsp_draw */ diff --git a/tools/qfbsp/source/options.c b/tools/qfbsp/source/options.c index 214065f9b..8b5995dce 100644 --- a/tools/qfbsp/source/options.c +++ b/tools/qfbsp/source/options.c @@ -40,7 +40,7 @@ #include "QF/quakefs.h" -#include "options.h" +#include "tools/qfbsp/include/options.h" /** \addtogroup qfbsp_options */ @@ -59,6 +59,7 @@ static struct option const long_options[] = { {"noclip", no_argument, 0, 'c'}, {"onlyents", no_argument, 0, 'e'}, {"portal", no_argument, 0, 'p'}, + {"info", no_argument, 0, 'i'}, {"extract-textures", no_argument, 0, 256}, {"extract-entities", no_argument, 0, 257}, {"extract-hull", no_argument, 0, 258}, @@ -81,6 +82,7 @@ static const char *short_options = "f" // nofill "c" // noclip "e" // onlyents + "i" // info "o:" // outputfile "p" // portal "u" // usehulls @@ -102,6 +104,7 @@ usage (int status) " -v, --verbose Display more output than usual\n" " -h, --help Display this help and exit\n" " -V, --version Output version information and exit\n" + " -i, --info Display info about the bsp\n" " -d, --draw\n" " -t, --notjunc\n" " -c, --noclip\n" @@ -163,6 +166,10 @@ DecodeArgs (int argc, char **argv) case 'o': options.output_file = strdup (optarg); break; + case 'i': + options.extract = true; + options.info = true; + break; case 'p': // portal options.extract = true; options.portal = true; diff --git a/tools/qfbsp/source/outside.c b/tools/qfbsp/source/outside.c index 0907d4dee..80e3419a1 100644 --- a/tools/qfbsp/source/outside.c +++ b/tools/qfbsp/source/outside.c @@ -22,12 +22,12 @@ #include "QF/sys.h" -#include "brush.h" -#include "bsp5.h" -#include "draw.h" -#include "options.h" -#include "portals.h" -#include "outside.h" +#include "tools/qfbsp/include/brush.h" +#include "tools/qfbsp/include/bsp5.h" +#include "tools/qfbsp/include/draw.h" +#include "tools/qfbsp/include/options.h" +#include "tools/qfbsp/include/portals.h" +#include "tools/qfbsp/include/outside.h" /** \addtogroup qfbsp_outside */ @@ -41,14 +41,14 @@ int outleafs; \param point The point's location. \return The leaf node in which the point is. */ -static node_t * +static __attribute__((pure)) node_t * PointInLeaf (node_t *node, const vec3_t point) { vec_t d; while (!node->contents) { - d = DotProduct (planes[node->planenum].normal, point); - node = node->children[d <= planes[node->planenum].dist]; + d = DotProduct (planes.a[node->planenum].normal, point); + node = node->children[d <= planes.a[node->planenum].dist]; } return node; } @@ -93,7 +93,7 @@ FloodEntDist_r (node_t *n, int dist) \param headnode The root of the map's bsp tree. \return true if the entity could be placed, false otherwise. */ -static qboolean +static bool PlaceOccupant (int num, const vec3_t point, node_t *headnode) { node_t *n; @@ -180,6 +180,8 @@ MarkLeakTrail2 (void) vec3_t wc, pwc; const vec_t *v; + VectorZero (wc); + leakfile = fopen (options.pointfile, "w"); if (!leakfile) Sys_Error ("Couldn't open %s\n", options.pointfile); @@ -231,12 +233,12 @@ int hit_occupied; \return \c true if an occupied leaf is reached, otherwise \c false. */ -static qboolean -RecursiveFillOutside (node_t *l, qboolean fill) +static bool +RecursiveFillOutside (node_t *l, bool fill) { portal_t *p; int s; - qboolean res = false; + bool res = false; if (l->contents == CONTENTS_SOLID || l->contents == CONTENTS_SKY) return false; @@ -306,11 +308,11 @@ ClearOutFaces (node_t *node) node->faces = NULL; } -qboolean +bool FillOutside (node_t *node) { int i, s; - qboolean inside; + bool inside; vec_t *v; qprintf ("----- FillOutside ----\n"); diff --git a/tools/qfbsp/source/portals.c b/tools/qfbsp/source/portals.c index edad5724a..56c665e3f 100644 --- a/tools/qfbsp/source/portals.c +++ b/tools/qfbsp/source/portals.c @@ -28,11 +28,11 @@ #include "QF/sys.h" -#include "brush.h" -#include "bsp5.h" -#include "draw.h" -#include "options.h" -#include "portals.h" +#include "tools/qfbsp/include/brush.h" +#include "tools/qfbsp/include/bsp5.h" +#include "tools/qfbsp/include/draw.h" +#include "tools/qfbsp/include/options.h" +#include "tools/qfbsp/include/portals.h" /** \addtogroup qfbsp_portals */ @@ -271,7 +271,7 @@ CutNodePortals_r (node_t *node) return; } - plane = &planes[node->planenum]; + plane = &planes.a[node->planenum]; f = node->children[0]; b = node->children[1]; @@ -280,9 +280,8 @@ CutNodePortals_r (node_t *node) /// cutting plane and clipping it by all of the planes from the other /// portals on the node. w = BaseWindingForPlane (plane); - side = 0; for (p = node->portals; p; p = p->next[side]) { - clipplane = planes[p->planenum]; // copy the plane + clipplane = planes.a[p->planenum]; // copy the plane if (p->nodes[0] == node) side = 0; else if (p->nodes[1] == node) { @@ -423,7 +422,7 @@ int num_realleafs; \param cont The contents for which to check. \return 1 if the node has the specified contents, otherwise 0. */ -static int +static __attribute__((pure)) int HasContents (const node_t *n, int cont) { if (n->contents == cont) @@ -440,7 +439,7 @@ HasContents (const node_t *n, int cont) \param n1 The first node to check. \param n2 The second node to check. */ -static int +static __attribute__((pure)) int ShareContents (const node_t *n1, const node_t *n2) { if (n1->contents) { @@ -462,7 +461,7 @@ ShareContents (const node_t *n1, const node_t *n2) \param n1 The first node to check. \param n2 The second node to check. */ -static int +static __attribute__((pure)) int SameContents (const node_t *n1, const node_t *n2) { if (n1->contents == CONTENTS_SOLID || n2->contents == CONTENTS_SOLID) @@ -472,7 +471,7 @@ SameContents (const node_t *n1, const node_t *n2) if (options.watervis) //FIXME be more picky? return 1; if (n1->detail && n2->detail) - ShareContents (n1, n2); + return ShareContents (n1, n2); if (n1->detail) return HasContents (n1, n2->contents); if (n2->detail) @@ -512,7 +511,7 @@ WritePortalFile_r (const node_t *node) // sometimes planes get turned around when they are very near the // changeover point between different axis. interpret the plane // the same way vis will, and flip the side orders if needed - pl = &planes[p->planenum]; + pl = &planes.a[p->planenum]; PlaneFromWinding (w, &plane2); if (DotProduct (pl->normal, plane2.normal) < 0.99) { // backwards.. fprintf (pf, "%i %i %i ", w->numpoints, diff --git a/tools/qfbsp/source/qfbsp.c b/tools/qfbsp/source/qfbsp.c index e3948acb8..d23f83f2c 100644 --- a/tools/qfbsp/source/qfbsp.c +++ b/tools/qfbsp/source/qfbsp.c @@ -43,18 +43,18 @@ #include "QF/quakefs.h" #include "QF/sys.h" -#include "csg4.h" -#include "brush.h" -#include "bsp5.h" -#include "merge.h" -#include "options.h" -#include "outside.h" -#include "portals.h" -#include "readbsp.h" -#include "solidbsp.h" -#include "surfaces.h" -#include "writebsp.h" -#include "tjunc.h" +#include "tools/qfbsp/include/csg4.h" +#include "tools/qfbsp/include/brush.h" +#include "tools/qfbsp/include/bsp5.h" +#include "tools/qfbsp/include/merge.h" +#include "tools/qfbsp/include/options.h" +#include "tools/qfbsp/include/outside.h" +#include "tools/qfbsp/include/portals.h" +#include "tools/qfbsp/include/readbsp.h" +#include "tools/qfbsp/include/solidbsp.h" +#include "tools/qfbsp/include/surfaces.h" +#include "tools/qfbsp/include/writebsp.h" +#include "tools/qfbsp/include/tjunc.h" /** \addtogroup qfbsp */ @@ -70,7 +70,7 @@ int valid; char *argv0; // changed after fork(); -qboolean worldmodel; +bool worldmodel; int hullnum; @@ -103,7 +103,7 @@ ProcessEntity (int entnum) worldmodel = false; if (entnum == 1) qprintf ("--- Internal Entities ---\n"); - sprintf (mod, "*%i", bsp->nummodels); + sprintf (mod, "*%zd", bsp->nummodels); if (options.verbosity) PrintEntity (ent); @@ -225,7 +225,6 @@ WriteClipHull (void) FILE *f; dclipnode_t *d; dplane_t *p; - int i; options.hullfile[strlen (options.hullfile) - 1] = '0' + hullnum; @@ -236,18 +235,18 @@ WriteClipHull (void) if (!f) Sys_Error ("Couldn't open %s", options.hullfile); - fprintf (f, "%i\n", bsp->nummodels); + fprintf (f, "%zd\n", bsp->nummodels); - for (i = 0; i < bsp->nummodels; i++) + for (size_t i = 0; i < bsp->nummodels; i++) fprintf (f, "%i\n", bsp->models[i].headnode[hullnum]); - fprintf (f, "\n%i\n", bsp->numclipnodes); + fprintf (f, "\n%zd\n", bsp->numclipnodes); - for (i = 0; i < bsp->numclipnodes; i++) { + for (size_t i = 0; i < bsp->numclipnodes; i++) { d = &bsp->clipnodes[i]; p = &bsp->planes[d->planenum]; // the node number is written out only for human readability - fprintf (f, "%5i : %f %f %f %f : %5i %5i\n", i, p->normal[0], + fprintf (f, "%5zd : %f %f %f %f : %5i %5i\n", i, p->normal[0], p->normal[1], p->normal[2], p->dist, d->children[0], d->children[1]); } @@ -268,7 +267,8 @@ ReadClipHull (int hullnum) plane_t p; dplane_t dp; float f1, f2, f3, f4; - int firstclipnode, junk, c1, c2, i, j, n; + int firstclipnode, junk, c1, c2; + size_t i, j, n; int flip; options.hullfile[strlen (options.hullfile) - 1] = '0' + hullnum; @@ -277,27 +277,25 @@ ReadClipHull (int hullnum) if (!f) Sys_Error ("Couldn't open %s", options.hullfile); - if (fscanf (f, "%d\n", &n) != 1) + if (fscanf (f, "%zd\n", &n) != 1) Sys_Error ("Error parsing %s", options.hullfile); if (n != bsp->nummodels) - Sys_Error ("ReadClipHull: hull had %i models, base had %i", n, + Sys_Error ("ReadClipHull: hull had %zd models, base had %zd", n, bsp->nummodels); for (i = 0; i < n; i++) { - if (fscanf (f, "%d\n", &j) != 1) + if (fscanf (f, "%zd\n", &j) != 1) Sys_Error ("Error parsing %s", options.hullfile); bsp->models[i].headnode[hullnum] = bsp->numclipnodes + j; } - if (fscanf (f, "\n%d\n", &n) != 1) + if (fscanf (f, "\n%zd\n", &n) != 1) Sys_Error ("Error parsing %s", options.hullfile); firstclipnode = bsp->numclipnodes; for (i = 0; i < n; i++) { - if (bsp->numclipnodes == MAX_MAP_CLIPNODES) - Sys_Error ("ReadClipHull: MAX_MAP_CLIPNODES"); if (fscanf (f, "%d : %f %f %f %f : %d %d\n", &junk, &f1, &f2, &f3, &f4, &c1, &c2) != 7) Sys_Error ("Error parsing %s", options.hullfile); @@ -404,10 +402,10 @@ CreateHulls (void) static void ProcessFile (void) { - bsp = BSP_New (); - if (options.extract) { LoadBSP (); + if (options.info) + bspinfo (); if (options.portal) bsp2prt (); if (options.extract_textures) @@ -420,6 +418,7 @@ ProcessFile (void) return; } + bsp = BSP_New (); // load brushes and entities LoadMapFile (options.mapfile); diff --git a/tools/qfbsp/source/readbsp.c b/tools/qfbsp/source/readbsp.c index 4168a2ae5..fce4e4e84 100644 --- a/tools/qfbsp/source/readbsp.c +++ b/tools/qfbsp/source/readbsp.c @@ -48,11 +48,11 @@ #include "QF/va.h" #include "QF/wad.h" -#include "brush.h" -#include "bsp5.h" -#include "options.h" -#include "portals.h" -#include "readbsp.h" +#include "tools/qfbsp/include/brush.h" +#include "tools/qfbsp/include/bsp5.h" +#include "tools/qfbsp/include/options.h" +#include "tools/qfbsp/include/portals.h" +#include "tools/qfbsp/include/readbsp.h" /** \addtogroup qfbsp_readbsp */ @@ -101,16 +101,16 @@ static void load_planes (void) { const dplane_t *p; - int i; + plane_t tp = { }; - memset (planes, 0, sizeof (planes)); - for (i = 0; i < bsp->numplanes; i++) { + planes.size = 0; + for (size_t i = 0; i < bsp->numplanes; i++) { p = bsp->planes + i; - VectorCopy (p->normal, planes[i].normal); - planes[i].dist = p->dist; - planes[i].type = p->type; + VectorCopy (p->normal, tp.normal); + tp.dist = p->dist; + tp.type = p->type; + DARRAY_APPEND (&planes, tp); } - numbrushplanes = bsp->numplanes; } static void @@ -123,11 +123,10 @@ static void load_faces (void) { const dface_t *f; - int i, j; winding_t *points; mfaces = calloc (bsp->numfaces, sizeof (face_t)); - for (i = 0; i < bsp->numfaces; i++) { + for (size_t i = 0; i < bsp->numfaces; i++) { f = bsp->faces + i; mfaces[i].planenum = f->planenum; mfaces[i].planeside = f->side; @@ -137,7 +136,7 @@ load_faces (void) points = mfaces[i].points; points->numpoints = f->numedges; - for (j = 0; j < points->numpoints; j++) { + for (int j = 0; j < points->numpoints; j++) { int e = mfaces[i].edges[j]; int v; @@ -161,11 +160,9 @@ static void load_leafs (void) { const dleaf_t *l; - int i; - unsigned j; leafs = calloc (bsp->numleafs, sizeof (node_t)); - for (i = 0; i < bsp->numleafs; i++) { + for (size_t i = 0; i < bsp->numleafs; i++) { l = bsp->leafs + i; leafs[i].planenum = -1; leafs[i].contents = l->contents; @@ -173,7 +170,7 @@ load_leafs (void) VectorCopy (l->maxs, leafs[i].maxs); leafs[i].markfaces = calloc (l->nummarksurfaces + 1, sizeof (face_t *)); - for (j = 0; j < l->nummarksurfaces; j++) { + for (uint32_t j = 0; j < l->nummarksurfaces; j++) { unsigned short ms = l->firstmarksurface + j; leafs[i].markfaces[j] = mfaces + marksurfaces[ms]; } @@ -184,19 +181,16 @@ static void load_nodes (void) { const dnode_t *n; - face_t *f; - int i; - unsigned j; nodes = calloc (bsp->numnodes, sizeof (node_t)); - for (i = 0; i < bsp->numnodes; i++) { + for (size_t i = 0; i < bsp->numnodes; i++) { n = bsp->nodes + i; VectorCopy (n->mins, nodes[i].mins); VectorCopy (n->maxs, nodes[i].maxs); nodes[i].planenum = n->planenum; nodes[i].firstface = n->firstface; nodes[i].numfaces = n->numfaces; - for (j = 0; j < 2; j++) { + for (int j = 0; j < 2; j++) { if (n->children[j] < 0) { nodes[i].children[j] = leafs - n->children[j] - 1; } else { @@ -205,7 +199,8 @@ load_nodes (void) } if (nodes[i].numfaces) { nodes[i].faces = mfaces + nodes[i].firstface; - for (j = 0, f = nodes[i].faces; j < n->numfaces - 1; j++, f++) { + for (uint32_t j = 0; j < n->numfaces - 1; j++) { + face_t *f = nodes[i].faces + j; f->next = f + 1; } } @@ -359,13 +354,13 @@ unique_name (wad_t *wad, const char *name) do { strncpy (uname, name, MIPTEXNAME); uname[(MIPTEXNAME - 1)] = 0; - tag = va ("~%x", i++); + tag = va (0, "~%x", i++); if (strlen (uname) + strlen (tag) <= (MIPTEXNAME - 1)) strcat (uname, tag); else strcpy (uname + (MIPTEXNAME - 1) - strlen (tag), tag); } while (wad_find_lump (wad, uname)); - return va ("%s", uname); // just to make a safe returnable that doesn't + return va (0, "%s", uname); // just to make a safe returnable that doesn't // need to be freed } @@ -374,7 +369,7 @@ extract_textures (void) { const dmiptexlump_t *miptexlump = (dmiptexlump_t *) bsp->texdata; miptex_t *miptex; - int i, mtsize, pixels; + int mtsize, pixels; const char *wadfile; wad_t *wad; const char *uname; @@ -386,15 +381,15 @@ extract_textures (void) wad_add_data (wad, "PALETTE", TYP_PALETTE, default_palette, sizeof (default_palette)); - for (i = 0; i < miptexlump->nummiptex; i++) { - if (miptexlump->dataofs[i] == -1) + for (size_t i = 0; i < miptexlump->nummiptex; i++) { + if (miptexlump->dataofs[i] == ~0u) continue; miptex = (miptex_t *)(bsp->texdata + miptexlump->dataofs[i]); pixels = miptex->width * miptex->height / 64 * 85; mtsize = sizeof (miptex_t) + pixels; uname = unique_name (wad, miptex->name); #if 1 - printf ("%3d %6d ", i, miptexlump->dataofs[i]); + printf ("%3zd %6d ", i, miptexlump->dataofs[i]); printf ("%16.16s %16.16s %3dx%-3d %d %d %d %d %d %d\n", miptex->name, uname, miptex->width, miptex->height, miptex->offsets[0], miptex->offsets[1], @@ -431,7 +426,6 @@ extract_hull (void) { // hullfile = output_file (".c"); const char *hullfile; - int i, j; QFile *hf; hullfile = output_file (".c"); @@ -440,14 +434,14 @@ extract_hull (void) else hf = Qopen (hullfile, "wt"); - printf ("%d\n", bsp->nummodels); - for (i = 0; i < bsp->nummodels; i++) { + printf ("%zd\n", bsp->nummodels); + for (size_t i = 0; i < bsp->nummodels; i++) { dmodel_t *m = bsp->models + i; printf ("mins: (%g, %g, %g)\n", m->mins[0], m->mins[1], m->mins[2]); printf ("maxs: (%g, %g, %g)\n", m->maxs[0], m->maxs[1], m->maxs[2]); printf ("origin: (%g, %g, %g)\n", m->origin[0], m->origin[1], m->origin[2]); - for (j = 0; j < MAX_MAP_HULLS; j++) + for (int j = 0; j < MAX_MAP_HULLS; j++) printf ("headnodes[%d]: %d\n", j, m->headnode[j]); printf ("visleafs: %d\n", m->visleafs); printf ("firstface: %d\n", m->firstface); @@ -455,7 +449,7 @@ extract_hull (void) printf ("\n"); } Qprintf (hf, "dclipnode_t clipnodes[] = {\n"); - for (i = 0; i < bsp->numnodes; i++) { + for (size_t i = 0; i < bsp->numnodes; i++) { int c0, c1; c0 = bsp->nodes[i].children[0]; c1 = bsp->nodes[i].children[1]; @@ -463,13 +457,13 @@ extract_hull (void) c0 = bsp->leafs[-1 - c0].contents; if (c1 < 0) c1 = bsp->leafs[-1 - c1].contents; - Qprintf (hf, "\t{%d, {%d, %d}},\t// %d\n", bsp->nodes[i].planenum, + Qprintf (hf, "\t{%d, {%d, %d}},\t// %zd\n", bsp->nodes[i].planenum, c0, c1, i); } Qprintf (hf, "};\n"); Qprintf (hf, "mplane_t planes[] = {\n"); - for (i = 0; i < bsp->numplanes; i++) { - Qprintf (hf, "\t{{%g, %g, %g}, %g, %d, 0, {0, 0}},\t// %d\n", + for (size_t i = 0; i < bsp->numplanes; i++) { + Qprintf (hf, "\t{{%g, %g, %g}, %g, %d, 0, {0, 0}},\t// %zd\n", bsp->planes[i].normal[0], bsp->planes[i].normal[1], bsp->planes[i].normal[2], bsp->planes[i].dist, bsp->planes[i].type, diff --git a/tools/qfbsp/source/region.c b/tools/qfbsp/source/region.c index b7c875a22..2db9e5173 100644 --- a/tools/qfbsp/source/region.c +++ b/tools/qfbsp/source/region.c @@ -29,9 +29,9 @@ #include "compat.h" -#include "bsp5.h" -#include "region.h" -#include "surfaces.h" +#include "tools/qfbsp/include/bsp5.h" +#include "tools/qfbsp/include/region.h" +#include "tools/qfbsp/include/surfaces.h" /** \addtogroup qfbsp_region */ @@ -81,7 +81,7 @@ AddFaceToRegionSize (face_t *f) AddPointToRegion (f->points->points[i]); } -static qboolean +static bool CanJoinFaces (face_t *f, face_t *f2) { int i; @@ -160,31 +160,22 @@ RecursiveGrowRegion (dface_t *r, face_t *f) } */ -typedef struct { - int numedges; - int edges[2]; -} checkpoint_t; - -checkpoint_t checkpoints[MAX_MAP_VERTS]; - static void CountRealNumbers (void) { - int c, i; + qprintf ("%5zd regions\n", bsp->numfaces - firstmodelface); - qprintf ("%5i regions\n", bsp->numfaces - firstmodelface); - - c = 0; - for (i = firstmodelface; i < bsp->numfaces; i++) + size_t c = 0; + for (size_t i = firstmodelface; i < bsp->numfaces; i++) c += bsp->faces[i].numedges; - qprintf ("%5i real marksurfaces\n", c); + qprintf ("%5zd real marksurfaces\n", c); c = 0; - for (i = firstmodeledge; i < bsp->numedges; i++) + for (size_t i = firstmodeledge; i < bsp->numedges; i++) if (edgefaces[i].f[0]) c++; // not removed - qprintf ("%5i real edges\n", c); + qprintf ("%5zd real edges\n", c); } static void @@ -207,8 +198,6 @@ GrowNodeRegion_r (node_t * node) // continue; // allready grown into an earlier region // emit a region - if (bsp->numfaces == MAX_MAP_FACES) - Sys_Error ("MAX_MAP_FACES"); f->outputnumber = bsp->numfaces; r.planenum = node->outputplanenum; diff --git a/tools/qfbsp/source/solidbsp.c b/tools/qfbsp/source/solidbsp.c index 5e04f3c32..830c35ce8 100644 --- a/tools/qfbsp/source/solidbsp.c +++ b/tools/qfbsp/source/solidbsp.c @@ -28,12 +28,12 @@ #include "QF/sys.h" -#include "brush.h" -#include "csg4.h" -#include "bsp5.h" -#include "draw.h" -#include "solidbsp.h" -#include "surfaces.h" +#include "tools/qfbsp/include/brush.h" +#include "tools/qfbsp/include/csg4.h" +#include "tools/qfbsp/include/bsp5.h" +#include "tools/qfbsp/include/draw.h" +#include "tools/qfbsp/include/solidbsp.h" +#include "tools/qfbsp/include/surfaces.h" /** \addtogroup qfbsp_solidbsp */ @@ -45,7 +45,7 @@ int splitnodes; int c_solid, c_empty, c_water; -qboolean usemidsplit; +bool usemidsplit; /** Determine on which side of the plane a face is. @@ -110,7 +110,7 @@ FaceSide (const face_t *in, const plane_t *split) return SIDE_ON; } -/** Chose the best plane for dividing the bsp. +/** Choose the best plane for dividing the bsp. The clipping hull BSP doesn't worry about avoiding splits, so this function tries to find the plane that gives the most even split of the @@ -121,7 +121,7 @@ FaceSide (const face_t *in, const plane_t *split) \param maxs The maximum coordinate of the boundiing box. \return The chosen surface. */ -static surface_t * +static __attribute__((pure)) surface_t * ChooseMidPlaneFromList (surface_t *surfaces, const vec3_t mins, const vec3_t maxs) { @@ -138,7 +138,7 @@ ChooseMidPlaneFromList (surface_t *surfaces, if (p->onnode) continue; - plane = &planes[p->planenum]; + plane = &planes.a[p->planenum]; // check for axis aligned surfaces l = plane->type; @@ -186,9 +186,9 @@ ChooseMidPlaneFromList (surface_t *surfaces, \return The chosen surface, or NULL if a suitable surface could not be found. */ -static surface_t * +static __attribute__((pure)) surface_t * ChoosePlaneFromList (surface_t *surfaces, const vec3_t mins, const vec3_t maxs, - qboolean usefloors, qboolean usedetail) + bool usefloors, bool usedetail) { face_t *f; int j, k, l, ishint; @@ -215,7 +215,7 @@ ChoosePlaneFromList (surface_t *surfaces, const vec3_t mins, const vec3_t maxs, if (!p->has_struct && !usedetail) continue; - plane = &planes[p->planenum]; + plane = &planes.a[p->planenum]; k = 0; if (!usefloors && plane->normal[2] == 1) @@ -393,7 +393,7 @@ DividePlane (surface_t *in, plane_t *split, surface_t **front, int have[2][2]; // [front|back][detail|struct] - inplane = &planes[in->planenum]; + inplane = &planes.a[in->planenum]; // parallel case is easy if (_VectorCompare (inplane->normal, split->normal)) { @@ -638,7 +638,7 @@ PartitionSurfaces (surface_t *surfaces, node_t *node) node->children[1] = AllocNode (); node->planenum = split->planenum; - splitplane = &planes[split->planenum]; + splitplane = &planes.a[split->planenum]; // multiple surfaces, so split all the polysurfaces into front and back // lists @@ -674,7 +674,7 @@ PartitionSurfaces (surface_t *surfaces, node_t *node) } node_t * -SolidBSP (surface_t *surfhead, qboolean midsplit) +SolidBSP (surface_t *surfhead, bool midsplit) { int i; node_t *headnode; diff --git a/tools/qfbsp/source/surfaces.c b/tools/qfbsp/source/surfaces.c index 9c467c0e1..2d421eec3 100644 --- a/tools/qfbsp/source/surfaces.c +++ b/tools/qfbsp/source/surfaces.c @@ -26,13 +26,15 @@ #endif #include +#include "QF/progs.h" // for PR_RESMAP #include "QF/sys.h" -#include "bsp5.h" -#include "csg4.h" -#include "options.h" -#include "region.h" -#include "surfaces.h" +#include "tools/qfbsp/include/bsp5.h" +#include "tools/qfbsp/include/brush.h" +#include "tools/qfbsp/include/csg4.h" +#include "tools/qfbsp/include/options.h" +#include "tools/qfbsp/include/region.h" +#include "tools/qfbsp/include/surfaces.h" /** \addtogroup qfbsp_surface */ @@ -162,8 +164,8 @@ GatherNodeFaces_r (node_t *node) if (!f->points) { // face was removed outside FreeFace (f); } else { - f->next = validfaces[f->planenum]; - validfaces[f->planenum] = f; + f->next = validfaces.a[f->planenum]; + validfaces.a[f->planenum] = f; } } @@ -180,7 +182,8 @@ GatherNodeFaces_r (node_t *node) surface_t * GatherNodeFaces (node_t *headnode) { - memset (validfaces, 0, sizeof (validfaces)); + DARRAY_RESIZE (&validfaces, planes.size); + memset (validfaces.a, 0, validfaces.size * sizeof (validfaces.a[0])); GatherNodeFaces_r (headnode); return BuildSurfaces (); } @@ -198,11 +201,10 @@ typedef struct hashvert_s { int c_cornerverts; -hashvert_t hvertex[MAX_MAP_VERTS]; -hashvert_t *hvert_p; +static PR_RESMAP (hashvert_t) hvertex; #define EDGEFACE_CHUNK 4096 -int numedgefaces = 0; +size_t numedgefaces = 0; edgeface_t *edgefaces = 0; int firstmodeledge = 1; int firstmodelface; @@ -241,7 +243,7 @@ InitHash (void) hash_scale[1] = newsize[1] / size[1]; hash_scale[2] = newsize[1]; - hvert_p = hvertex; + PR_RESRESET (hvertex); } /** Calulate the hash value of a vector. @@ -302,7 +304,7 @@ GetVertex (const vec3_t in, int planenum) } } - hv = hvert_p; + hv = PR_RESNEW (hvertex); hv->numedges = 1; hv->numplanes = 1; hv->planenums[0] = planenum; @@ -310,14 +312,8 @@ GetVertex (const vec3_t in, int planenum) hashverts[h] = hv; VectorCopy (vert, hv->point); hv->num = bsp->numvertexes; - if (hv->num == MAX_MAP_VERTS) - Sys_Error ("GetVertex: MAX_MAP_VERTS"); - hvert_p++; // emit a vertex - if (bsp->numvertexes == MAX_MAP_VERTS) - Sys_Error ("numvertexes == MAX_MAP_VERTS"); - v.point[0] = vert[0]; v.point[1] = vert[1]; v.point[2] = vert[2]; @@ -340,12 +336,12 @@ int c_tryedges; \return The edge number. For a re-used edge, the edge number will be negative, indicating the ends of the edge are reversed. */ -static int +static size_t GetEdge (const vec3_t p1, const vec3_t p2, face_t *f) { dedge_t edge; unsigned v1, v2; - int i; + size_t i; if (!f->contents[0]) Sys_Error ("GetEdge: 0 contents"); diff --git a/tools/qfbsp/source/tjunc.c b/tools/qfbsp/source/tjunc.c index 50882df87..e18ec33da 100644 --- a/tools/qfbsp/source/tjunc.c +++ b/tools/qfbsp/source/tjunc.c @@ -29,10 +29,10 @@ #include "compat.h" -#include "brush.h" -#include "bsp5.h" -#include "options.h" -#include "tjunc.h" +#include "tools/qfbsp/include/brush.h" +#include "tools/qfbsp/include/bsp5.h" +#include "tools/qfbsp/include/options.h" +#include "tools/qfbsp/include/tjunc.h" /** \addtogroup qfbsp_tjunc */ @@ -121,7 +121,7 @@ HashVec (const vec3_t vec) \param vec The vector to make canonical. \return false is the vector is (0, 0, 0), otherwise true. */ -static qboolean +static bool CanonicalVector (vec3_t vec) { vec_t len = _VectorNormalize (vec); diff --git a/tools/qfbsp/source/writebsp.c b/tools/qfbsp/source/writebsp.c index fa95b4d57..ea535beef 100644 --- a/tools/qfbsp/source/writebsp.c +++ b/tools/qfbsp/source/writebsp.c @@ -37,10 +37,10 @@ #include "QF/va.h" #include "QF/wad.h" -#include "brush.h" -#include "bsp5.h" -#include "options.h" -#include "writebsp.h" +#include "tools/qfbsp/include/brush.h" +#include "tools/qfbsp/include/bsp5.h" +#include "tools/qfbsp/include/options.h" +#include "tools/qfbsp/include/writebsp.h" /** \addtogroup qfbsp_writebsp */ @@ -53,10 +53,8 @@ int firstface; int FindFinalPlane (const dplane_t *p) { - const dplane_t *dplane; - int i; - - for (i = 0, dplane = bsp->planes; i < bsp->numplanes; i++, dplane++) { + for (size_t i = 0; i < bsp->numplanes; i++) { + const dplane_t *dplane = bsp->planes + i; if (p->type != dplane->type) continue; if (p->dist != dplane->dist) @@ -67,14 +65,12 @@ FindFinalPlane (const dplane_t *p) } // new plane - if (bsp->numplanes == MAX_MAP_PLANES) - Sys_Error ("numplanes == MAX_MAP_PLANES"); BSP_AddPlane (bsp, p); return bsp->numplanes - 1; } -int planemapping[MAX_MAP_PLANES]; +static struct DARRAY_TYPE(int) planemapping = DARRAY_STATIC_INIT (1024); /** Recursively write the nodes' planes to the bsp file. @@ -91,10 +87,10 @@ WriteNodePlanes_r (node_t *node) if (node->planenum == -1) return; - if (planemapping[node->planenum] == -1) { // a new plane - planemapping[node->planenum] = bsp->numplanes; + if (planemapping.a[node->planenum] == -1) { // a new plane + planemapping.a[node->planenum] = bsp->numplanes; - plane = &planes[node->planenum]; + plane = &planes.a[node->planenum]; VectorCopy (plane->normal, dplane.normal); dplane.dist = plane->dist; @@ -102,7 +98,7 @@ WriteNodePlanes_r (node_t *node) BSP_AddPlane (bsp, &dplane); } - node->outputplanenum = planemapping[node->planenum]; + node->outputplanenum = planemapping.a[node->planenum]; WriteNodePlanes_r (node->children[0]); WriteNodePlanes_r (node->children[1]); @@ -111,7 +107,8 @@ WriteNodePlanes_r (node_t *node) void WriteNodePlanes (node_t *nodes) { - memset (planemapping, -1, sizeof (planemapping)); + DARRAY_RESIZE (&planemapping, planes.size); + memset (planemapping.a, -1, planemapping.size * sizeof (planemapping.a[0])); WriteNodePlanes_r (nodes); } @@ -124,7 +121,7 @@ WriteNodePlanes (node_t *nodes) static int WriteClipNodes_r (node_t *node) { - dclipnode_t cn; + dclipnode_t cn = { }; int num, c, i; // FIXME: free more stuff? @@ -176,8 +173,6 @@ WriteLeaf (const node_t *node) for (fp = node->markfaces; *fp; fp++) { // emit a marksurface - if (bsp->nummarksurfaces == MAX_MAP_MARKSURFACES) - Sys_Error ("nummarksurfaces == MAX_MAP_MARKSURFACES"); f = *fp; if (f->texturenum < 0) continue; @@ -206,8 +201,6 @@ WriteDrawNodes_r (const node_t *node) int nodenum = bsp->numnodes; // emit a node - if (bsp->numnodes == MAX_MAP_NODES) - Sys_Error ("numnodes == MAX_MAP_NODES"); BSP_AddNode (bsp, &dummy); n = &bsp->nodes[nodenum]; @@ -301,7 +294,7 @@ TEX_InitFromWad (const char *path) wad = wad_open (path); #ifdef HAVE_ZLIB if (!wad) - wad = wad_open (path = va ("%s.gz", path)); + wad = wad_open (path = va (0, "%s.gz", path)); #endif if (!wad) return -1; diff --git a/tools/qfcc/Makefile.am b/tools/qfcc/Makefile.am deleted file mode 100644 index b9050ce13..000000000 --- a/tools/qfcc/Makefile.am +++ /dev/null @@ -1,48 +0,0 @@ -# Makefile.am -# -# Automake-using build system for QuakeForge -# -# Copyright (C) 2000 Jeff Teunissen -# -# This Makefile is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# -# See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to: -# -# Free Software Foundation, Inc. -# 59 Temple Place - Suite 330 -# Boston, MA 02111-1307, USA -# -# $Id$ -# -AUTOMAKE_OPTIONS= foreign - -SUBDIRS= include source doc test - -dist-zip: distdir - -chmod -R a+r $(distdir) - ZIP="-r9q" zip $(distdir).zip $(NOCONV_DIST) - ZIP="-r9ql" zip $(distdir).zip $(distdir) -x $(NOCONV_DIST) - -rm -rf $(distdir) - -dist-bz2: distdir - -chmod -R a+r $(distdir) - BZIP2="-9" $(TAR) Ichof $(distdir).tar.bz2 $(distdir) - -rm -rf $(distdir) - -dist-all-local: distdir - -chmod -R a+r $(distdir) - GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir) - BZIP2="-9" $(TAR) Ichof $(distdir).tar.bz2 $(distdir) - ZIP="-r9q" zip $(distdir).zip $(NOCONV_DIST) - ZIP="-r9ql" zip $(distdir).zip $(distdir) -x $(NOCONV_DIST) - -rm -rf $(distdir) diff --git a/tools/qfcc/Makemodule.am b/tools/qfcc/Makemodule.am new file mode 100644 index 000000000..e4159fbf3 --- /dev/null +++ b/tools/qfcc/Makemodule.am @@ -0,0 +1,4 @@ +include tools/qfcc/include/Makemodule.am +include tools/qfcc/source/Makemodule.am +include tools/qfcc/doc/Makemodule.am +include tools/qfcc/test/Makemodule.am diff --git a/tools/qfcc/TODO b/tools/qfcc/TODO index ed68de57c..8180ed95d 100644 --- a/tools/qfcc/TODO +++ b/tools/qfcc/TODO @@ -12,11 +12,12 @@ M unnamed function parameters for prototypes/typdefs etc. I optimizations (esp CSE) I fix used/uninitialized warnings o id id; -o vec = [x, y, z]; expressions (nice feature in fteqcc) +M vec = [x, y, z]; expressions (nice feature in fteqcc) M finish -Fifstring o isset() intrinsic for more consistent string handling. o arrays in entities o optional arguments for functions (alternative to overloading) vector(vector fwd, optional vector up) vectoangles = #51; +o rewrite type system to be const-correct (hard!) ? try to reduce memory consumption ?? embedded nul characters in strings (why?) diff --git a/tools/qfcc/bootstrap b/tools/qfcc/bootstrap deleted file mode 100755 index 071d87a77..000000000 --- a/tools/qfcc/bootstrap +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -cd `dirname $0` -aclocal && autoheader && automake --add-missing && autoconf diff --git a/tools/qfcc/configure.in b/tools/qfcc/configure.in deleted file mode 100644 index 21934d623..000000000 --- a/tools/qfcc/configure.in +++ /dev/null @@ -1,144 +0,0 @@ -dnl Process this file with autoconf to produce a configure script. - -AC_PREREQ(2.13) -AC_INIT(source/qfcc.c) -AC_REVISION($Revision$) dnl -AM_CONFIG_HEADER(include/config.h) -AC_CANONICAL_SYSTEM - -dnl Every other copy of the package version number gets its value from here -AM_INIT_AUTOMAKE(qfcc, 0.1.0) - -AC_SUBST(VERSION) - -ISODATE=`date +%Y-%m-%d` -AC_SUBST(ISODATE) - -AC_LANG_C - -dnl Checks for programs. -AC_PROG_INSTALL -AC_PROG_CC -AC_PROG_CPP -AC_PROG_YACC -AM_PROG_LEX - -AC_ARG_WITH(cpp, -[ --with-cpp=CPP how qfcc should invoke cpp], - cpp_name="$withval", cpp_name=auto -) -if test "x$cpp_name" != xauto; then - CPP_NAME="$cpp_name" -else - CPP_NAME="cpp %d -o %o %i" - case "$target_os" in - *bsd*) - touch conftest.c - CPP_NAME="`(f=\`$CC -v -E -Dfoo conftest.c -o conftest.i 2>&1 | grep -e -Dfoo\`; set $f; echo "$1")` %d %i %o" - rm -f conftest.[ci] - ;; - esac -fi -AC_DEFINE_UNQUOTED(CPP_NAME, "$CPP_NAME", [Define this to the command line for the C preprocessor]) - -dnl We want warnings, lots of warnings... -if test "x$GCC" = xyes; then - CFLAGS="$CFLAGS -Wall -Werror" - # CFLAGS="$CFLAGS -Wall -pedantic" -fi - -dnl Checks for libraries. -AC_CHECK_LIB(z, gztell,, -) - -dnl Checks for header files. -AC_HEADER_STDC -AC_CHECK_HEADERS(process.h string.h strings.h fcntl.h sys/stat.h sys/types.h sys/wait.h unistd.h) - -dnl Checks for typedefs, structures, and compiler characteristics. -AC_ARG_ENABLE(profile, - [ --enable-profile compile with profiling (for development)], - profile=$enable_profile -) -if test "x$profile" = xyes; then - BUILD_TYPE="$BUILD_TYPE Profile" - if test "x$GCC" = xyes; then - CFLAGS="`echo $CFLAGS | sed -e 's/-fomit-frame-pointer//g'` -pg" - LDFLAGS="$LDFLAGS -pg" - else - CFLAGS="$CFLAGS -p" - fi -fi - -AC_DEFINE_UNQUOTED(PATH_SEPARATOR, '/', - [Define this to your operating system's path separator character]) - -dnl Checks for library functions. - -AC_CHECK_FUNCS(snprintf _snprintf vsnprintf _vsnprintf) - -AC_MSG_CHECKING(for timeGetTime in -lwinmm) -save_LIBS="$LIBS" -LIBS="$LIBS -lwinmm" -AC_TRY_COMPILE( - [#include ], - [timeGetTime ();], - AC_MSG_RESULT(yes), - LIBS="$save_LIBS" - AC_MSG_RESULT(no) -) - -AC_ARG_WITH(qf, -[ --with-qf=DIR location of QF libs and headers (prefix)], - if test "x$withval" != xyes ; then - LDFLAGS="$LDFLAGS -L${withval}/lib" - CFLAGS="$CFLAGS -I${withval}/include" - fi - , - HAVE_QF=auto -) -AC_MSG_CHECKING(for QF/qtypes.h) -AC_TRY_COMPILE( - [#include "QF/qtypes.h"], - [qboolean foo = false; - foo = true;], - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) - HAVE_QF=no -) -if test "x$HAVE_QF" != xno; then - AC_CHECK_LIB(QFutil, Hash_NewTable, - :, HAVE_QF=no, - [] - ) -fi -if test "x$HAVE_QF" != xno; then - AC_CHECK_LIB(QFgamecode, PR_Opcode, - :, HAVE_QF=no, - [-lQFutil] - ) -fi - -QFCC_LIBS="-lQFgamecode -lQFutil" -QFCC_DEPS="" -QFCC_INCS="" - -if test "x$HAVE_QF" = xno; then - echo '***' - echo '*** You seem to not have the QuakeForge libs & headers installed' - echo '***' - exit 1 -fi - -AC_SUBST(QFCC_LIBS) -AC_SUBST(QFCC_DEPS) -AC_SUBST(QFCC_INCS) - -AC_OUTPUT( - doc/Makefile - doc/man/Makefile - include/Makefile - source/Makefile - Makefile - qfcc.lsm -) diff --git a/tools/qfcc/doc/Makefile.am b/tools/qfcc/doc/Makefile.am deleted file mode 100644 index 8d0026abb..000000000 --- a/tools/qfcc/doc/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -SUBDIRS= man - -EXTRA_DIST= expressions.txt \ - dot/vector-alias.dot diff --git a/tools/qfcc/doc/Makemodule.am b/tools/qfcc/doc/Makemodule.am new file mode 100644 index 000000000..121e06e82 --- /dev/null +++ b/tools/qfcc/doc/Makemodule.am @@ -0,0 +1,5 @@ +man_MANS += tools/qfcc/doc/man/qfcc.1 + +EXTRA_DIST += \ + tools/qfcc/doc/expressions.txt \ + tools/qfcc/doc/dot/vector-alias.dot diff --git a/tools/qfcc/doc/man/Makefile.am b/tools/qfcc/doc/man/Makefile.am deleted file mode 100644 index cca3e62a5..000000000 --- a/tools/qfcc/doc/man/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -man_MANS=qfcc.1 -EXTRA_DIST=qfcc.1 diff --git a/tools/qfcc/doc/man/qfcc.1 b/tools/qfcc/doc/man/qfcc.1 index 2857ffedd..0541458dd 100644 --- a/tools/qfcc/doc/man/qfcc.1 +++ b/tools/qfcc/doc/man/qfcc.1 @@ -31,49 +31,70 @@ .ds qfcc \fBqfcc\fP .ds cpp \fBcpp\fP .ds progs.src \fIprogs.src\fP + .TH QFCC 1 "28 April, 2004" QuakeForge "QuakeForge Developer's Manual" .\" Please update the above date whenever this man page is modified. + + .SH NAME qfcc \- The QuakeForge Code Compiler + + .SH SYNOPSIS .B qfcc .RI [ options ] .RI [ files ] + + .SH DESCRIPTION \*[qfcc] compiles Ruamoko source into a form that the QuakeForge engine can understand. + + .SH OPTIONS \*[qfcc] takes the following arguments: + .TP .B \-\-advanced -Use advanced Ruamoko features. -This is the default when using separate compilation. +Use advanced Ruamoko language features limited to the v6p ISA. This +provides access to structs, arrays, pointers, integers, quaternions, +doubles and object-oriented features mostly compatible with Objective-C +(ie, Objective-QuakeC). For access to SIMD vector types, see --ruamoko. + .TP .B \-C, \-\-code OPTION,... Set code generation options. See \fBCODE GENERATION OPTIONS\fP for details. + .TP .B \-c Only compile, do not link. Can be used in either \fBprogs.src\fP or separate compilation modes. + .TP .B \-\-cpp CPPSPEC \*[cpp] execution command line. See \fBCPP NAME\fP for details. + .TP .B \-D, \-\-define SYMBOL[=VAL] Define a symbol for the preprocessor, if it is in use. + .TP .B \-E Only preprocess. No compilation or linking is done. + .TP .B \-\-extended -Allow extended keywords in traditional mode. +Allow extended keywords in traditional mode. Otherwise, it has \fIall\fP +the implications of \fB\-\-traditional\fP. + .TP .B \-F, \-\-files Generate \fIfiles.dat\fP. This list is created by checking the parameters to the precache_* functions. + .TP .B \-\-frames Generate \fI.frame\fP files. @@ -81,134 +102,172 @@ For each source file (listed either on the command line, or in \fBprogs.src\fP, write a file whose name is the base name of the source file with an extension of \fB.frame\fP, and contains a list of frame macro names with their associated frame numbers. Eg, \fBplayer.qc\fP will produce -\fBplayer.frame\fPa. Note that files that do not create frame macros will +\fBplayer.frame\fP. Note that files that do not create frame macros will not generate a frame file. At this time, the file is always written to the current directory. + .TP .B \-g Generate debugging info. Synonym for \fB\-\-code debug\fP. + .TP .B \-h, \-\-help Show summary of options. + .TP .B \-I DIR Add DIR to the list of directories for the preprocessor to search when looking for include files. + .TP .B \-\-include FILE Process FILE as if \fB#include "FILE"\fP appeared as the first line of the primary source file. See the \*[cpp] man page (\fB\-include\fP) for details. + .TP .B \-L DIR Add DIR to the search path used for finding libraries specified with \fB-l\fP. + .TP .B \-l LIB Add libLIB.a to the list of libraries to be used for resolving undefined symbols. \*[qfcc] expects libraries to be \fBpak\fP files of \*[qfcc] object files built using the \fBpak\fP utility. + .TP .B \-M, \-MD, \-MMD Generate dependency info. Dependent on \*[cpp] version, so check \*[cpp]'s documentation. + .TP .B \-\-no\-default\-paths Do not use default paths for include files or libraries. + .TP .B \-N, \-\-notice OPTION,... Set notice options. See \fBNOTICE OPTIONS\fP for details. + .TP .B \-o, \-\-output\-file FILE Specify output file name. In \fBprogs.src\fP mode, this overrides the output file in \*[progs.src]. + .TP .B \-\-progdefs Generate \fIprogdefs.h\fP. Forces \fB\-\-code crc\fP. + .TP .B \-P, \-\-progs\-src FILE File to use instead of \*[progs.src]. No effect in separate compilation mode. + .TP .B \-\-qccx\-escapes Use QCCX escape sequences instead of standard C/QuakeForge sequences in strings. See \fBESCAPE SEQUENCES\fP for details. -.TP -.B \-p, \-\-strip\-path NUM -Strip NUM leading path elements from file names. -eg. -p 3 will strip the -.I ../../../ -from -.I ../../../src/foo.r -when embedding the source file name in the output code. + .TP .B \-q, \-\-quiet Inhibit some of \*[qfcc]'s normal output. Specifying this option multiple times further inhibits \*[qfcc]'s output. Counteracts the effects of \fB-v\fP. + .TP .B \-r, \-\-relocatable Incremental linking. Generate a larger object file from other object files and libraries. + +.TP +.B \-\-raumoko, \-\-ruamoko +In addition to the Ruamoko language features added by --advanced, this +option provides access to SIMD vector types (and instructions), a data +stack for local variables and parameters (allowing pointers to local +variables to be passed to other functions!), and other features still +under development. +This is the default when using separate compilation. + .TP .B \-S, \-\-save\-temps Do not delete temporary files. + .TP .B \-s, \-\-source DIR Look for \*[progs.src] in \fBDIR\fP instead of the current directory. + .TP .B \-\-traditional Use traditional QuakeC syntax, semantics and \*(lqbugs\*(rq. -Also implies the \fBv6only\fP, \fBno-short-circuit\fP and -\fBno-local-merging\fP code generation options (see -\fBCODE GENERATION OPTIONS\fP). +Also implies the \fBtarget=v6\fP, \fBno-short-circuit\fP, +\fBconst-initializers\fP and \fBno-local-merging\fP code generation options +(see \fBCODE GENERATION OPTIONS\fP). This is the default when using \fBprogs.src\fP mode. + .TP .B \-U, \-\-undefine SYMBOL Undefine a preprocessor symbol, if the preprocessor is in use. + .TP .B \-V, \-\-version Show the version of \*[qfcc]. + .TP .B \-v, \-\-verbose Display more output than usual. Specifying this option multiple times further increases \*[qfcc]'s output. Counteracts the effects of \fB-q\fP. + .TP .B \-W, \-\-warn OPTION,... Set warning options. See \fBWARNING OPTIONS\fP for details. + .TP .B \-z Compress object files when writing them. This is especially useful when creating libraries, especially if using the object oriented features, but can be quite slow. This has no effect when creating \fBprogs.dat\fP. + + .SH "CODE GENERATION OPTIONS" Code generation options are processed in the order of their appearance on the command line. Unsupported options are ignored. The following options are supported by \*[qfcc]'s \fB\-\-code\fP argument: + +.TP +.B const-initializers +Treat initialized globals as constants. +This option is implied by \fB\-\-traditional\fP and \fB\-\-extended\fP, and is +turned off by \fB\-\-advanced\fP. + .TP .B cow Allow assignment to initialized globals. -In Quake-C and Ruamoko, a global that has been initialized to a value is not -a variable, but a named constant. +When initialized globals are treated as constants (traditional Quake-C, or +when const-initializers is activated), a global that has been initialized to a +value is not a variable, but a named constant. However, \fBqcc\fP never really enforced this. The \fBcow\fP option allows \*[qfcc] to gracefully cope with QuakeC source that assigns values to initialized globals in this manner. -(also known as \*(lqcopy on write\*(rq\(emnever mind the bovine connotations) +(also known as \*(lqcopy on write\*(rq\(emlo and behold the bovine +connotations) + .TP .B cpp Preprocess all input files with \*[cpp]. This includes the \*[progs.src] file when used. + .TP .B crc Write the CRC of \fBprogdefs.h\fP to \*(lqprogs.dat\*(rq. Default for v6 progs, otherwise defaults to off. However, \fB\-\-progdefs\fP has the effect of forcing this option. + .TP .B debug Generate debug code for QuakeForge engines. @@ -218,12 +277,14 @@ This option tells \*[qfcc] to generate this information. It is written to a secondary file with the extension \*(lqsym\*(rq\(emif your output file is \*(lqprogs.dat\*(rq, the symbol file will be \*(lqprogs.sym\*(rq. + .TP .B fast\-float Use float values directly in \*(lqif\*(rq statements. Defaults to on. -This option is always enabled when using version 6 progs (\fBv6only\fP is in -effect). +This option is always enabled when targeting v6 progs (\fBtarget=v6\fP is +in effect). + .TP .B local-merging Clump the local variables from all functions into one block of data the size @@ -235,6 +296,14 @@ data. This can be a problem because instructions can access addresses up to 32767 in older servers or 65535 in most modern servers. Defaults to off for traditional mode, and on for advanced mode. + +.TP +.B promote\-float +Promote float when passed to a function that takes a variable number of +arguements. Defaults to enabled for advanced code (v6p or ruamoko), is forced +off for traditional or v6 code (mostly because such code does not have +doubles). + .TP .B short\-circuit Generate short circuit code for logical operators (\fB&&\fP and \fB||\fP). @@ -243,6 +312,7 @@ the code for \fBB\fP will not be executed. Similar for \fBA || B\fP, but if \fBA\fP is true, the expression is known to be true and the code for \fBB\fP will not be executed. Defaults to off for traditional mode, and on for advanced mode. + .TP .B single-cpp In \fBprogs.src\fP mode, when \*[cpp] is used, produce an intermediate file @@ -255,6 +325,7 @@ Without this option, each source file is independent with respect to the preprocessor. Has no effect in separate compilation mode. Defaults to on. + .TP .B vector\-calls When a function is passed a constant vector, this causes the vector to be @@ -263,81 +334,114 @@ instruction. This can save a good number of pr_globals where those vectors contain many duplicate coordinates but do not match entirely. However, this will generate slower code for such calls. + .TP .B vector\-components Create extra symbols for accessing the components of a vector variable or field. For example, \fBvector vel\fP will also create \fBvel_x\fP, \fBvel_y\fP, and \fBvel_z\fP. Defaults to on for traditional code and off for advanced. +.PP +Any of the above can be prefixed with \fBno\-\fP to negate its meaning. + .TP -.B v6only -Restrict the compiler to only version 6 progs (original Quake/QuakeWorld) -features. +.B target=v6|v6p|ruamoko +Specify the target for which the compiler is to produce code. +.RS +.TP +.B v6 +Standard Quake VM (qcc compatible). This means that the compiled data file should be able to run on older servers, as long as you have not used any QuakeForge-specific built-in functions. Also disables compiler features (such as integers and string manipulation support) that require extensions. -Defaults to on for traditional mode and off for advanced mode. -.PP -Any of the above can be prefixed with \fBno\-\fP to negate its meaning. +.TP +.B v6p +QuakeForge extended v6 instructions. +This is not compatible with older servers, nor with most (any?) other Quake +engines. It adds a variety of instructions and types, including integers, +quaternions, pointers, doubles, structs, arrays and Objective-C style classes. +.TP +.B ruamoko +QuakeForge SIMD and stack instructions. +.RE +Defaults to v6 for traditional mode, v6p for advanced mode, and ruamoko +for Ruamoko mode (actually, trying to take Ruamoko's power after +granting it may not end well). + + .SH "WARNING OPTIONS" Warning options are processed in the order of their appearance on the command line. Unsupported options are ignored. The following options are supported by \*[qfcc]'s \fB\-\-warn\fP argument: + .TP .B cow Emit a warning when the source assigns a value to a named constant. See the description of the \fBcow\fP code generation option above for a description of what this means. + .TP .B error Promote warnings to errors. + .TP .B executable Emit a warning when non-executable statements (eg, \fB==\fP used for assignment) are encountered. + .TP .B initializer Emit a warning when too many structure/array initializer elements are given. + .TP .B integer-divide Emit a warning when both constants in a division operation are integers. + .TP .B interface\-check Emit a warning when a method is declared in an implementation but not in the interface for a class. + .TP .B precedence Emit a warning when potentially ambiguous logic is used without parentheses. + .TP .B redeclared Emit a warning when a local variable is redeclared. + .TP .B switch Emit a warning when an enum value is not handled in a switch statement that tests an enum. Using a default label will cause all otherwise unhandled enum values to be handled (for good or evil). -.TP + .TP .B traditional Emit a warning when code that should be an error is allowed by traditional \fBqcc\fP. Has effect only in traditional mode. + .TP .B undef\-function Emit a warning when a function is called, but has not yet been defined. + .TP .B unimplemented Emit a warning when a class method has not been implemented. + .TP .B unused Emit a warning for unused local variables. + .TP .B uninited\-var Emit a warning when a variable is read from that has not been initialized to a value. + .TP .B vararg\-integer Emit a warning when a function that takes a variable number of arguments is @@ -345,26 +449,34 @@ passed a constant of an integer type. .PP Any of the above can be prefixed with \fBno\-\fP to negate its meaning. There are also two special options: + .TP .B all Turns on all warning options except \fBerror\fP. + .TP .B none Turns off all warning options except \fBerror\fP. + + .SH "NOTICE OPTIONS" Notices are used to flag code constructs that may have changed semantics but shouldn't be treated as warnings. They are also used for internal debugging purposes, so if you see any cryptic notices, please report them as a bug (normal notices should be fairly self-explanatory). + .TP .B none Silences all notice messages. + .TP .B warn Promote notices to warnings. If warnings are being treated as errors, so will notices. Disabling warnings has no effect on this option. + + .SH "CPP NAME" When preprocessing source files, \*[qfcc] calls \*[cpp] (the C preprocessor) with a configurable command line. @@ -379,19 +491,24 @@ This spec is similar in concept to a \fBprintf\fP string. The name of the program may be either absolute (eg \fB/lib/cpp\fP) or relative as the \fBPATH\fP will be searched. Available substitutions: + .TP .B %d Mainly for defines (\-D, \-U and \-I) but \fB%d\fP will be replaced by all \*[cpp] options that \*[qfcc] passes to \*[cpp] + .TP .B %o This will be replaced by the output file path. Could be either absolute or relative, depending on whether \*[qfcc] is deleting temporary files or not. + .TP .B %i This will be replaced by the input file path. Generally as given to \*[qfcc]. + + .SH "COMPILATION MODES" \*[qfcc] has two, mutually exclusive, modes of operation: \fBprogs.src\fP mode and \*(lqseparate compilation\*(rq mode. @@ -407,7 +524,7 @@ are really just white-space separated strings (use double quotes around files with spaces, though using files with spaces is a gibbing offence). \fB//\fP is used to denote a comment. The comment extends to the end of the current line. -The first file name in the file specified the output file name. +The first file name in the file specifies the output file name. This may be overridden using the \fB-o\fP option. All subsequent file names specify QuakeC source files. .P @@ -466,106 +583,160 @@ overridden using \fB--traditional\fP. When using \*[cpp], each source file is passed through the preprocessor individually. Each file is truly independent of any other file on the command line. + + .SH "ESCAPE SEQUENCES" \*[qfcc] supports a variety of string escape sequences. This includes those of \fBqcc\fP (which are a subset of those in standard C), standard C and \fBqccx\fP. There are some conflicts between the escape sequences, but \fB\-\-qccx\-escapes\fP selects which set to use. -.TP -.B \(rs\(rs -Backslash. -.TP -.B \(rsn -Line feed. -.TP -.B \(rs" -Double quote. -.TP -.B \(rs\' -Single quote. -.TP -.B \(rs0-7 -Octal character code, up to three digits. This conflicts with \fBqccx\fP. In -\fBqccx\fP, this produces gold digits. Use \fB\-\-qccx\-escapes\fP to select -\fBqccx\fP behaviour. -.TP -.B \(rs8-9 -Produce gold digits. -.TP -.B \(rsx0-9A-Fa-f -Hexadecimal character code, any number of digits, but only the least -significant byte will be used. +.SS Standard escape sequences: +These are the supported escape sequences from standard C, with the addition of +\(rse (escape), which would be nice if it was in standard C. + .TP .B \(rsa Bell character (not in quake engines). Equivalent to \(rsx07. + .TP .B \(rsb Backspace character (not in quake engines). Equivalent to \(rsx08. This conflicts with \fBqccx\fP. In \fBqccx\fP, this toggles bronze characters. Use \fB\-\-qccx\-escapes\fP to select \fBqccx\fP behaviour. + .TP .B \(rse -Escape character (not in quake engines). Equivalent to \(rsx1b (dull 9). +Escape character (not in quake engines). Equivalent to \(rsx1b. Not actually +standard, but it should be. + .TP .B \(rsf Formfeed character (not in quake engines). Equivalent to \(rsx0c. + +.TP +.B \(rsn +Line feed. + .TP .B \(rsr Carriage return. Equivalent to \(rsx0d. + .TP -.B \(rss -Toggle "bold" characters (add 0x80). .B \(rst Tab character. Equivalent to \(rsx09. + .TP .B \(rsv Vertical tab. Equivalent to \(rsx0b. + .TP -.B \(rs^ -Make the next character "bold" (add 0x80). +.B \(rs\(rs +Backslash. + +.TP +.B \(rs\' +Single quote. + +.TP +.B \(rs" +Double quote. + +.TP +.B \(rs? +Question mark. Avoids trigraphs in standard C, but supported for compatibility. + +.TP +.B \(rs0-7 +Octal character code, up to three digits. This conflicts with \fBqccx\fP. In +\fBqccx\fP, this produces gold digits. Use \fB\-\-qccx\-escapes\fP to select +\fBqccx\fP behaviour. + +.TP +.B \(rs8-9 +Produce gold digits. + +.TP +.B \(rsx0-9A-Fa-f +Hexadecimal character code, any number of digits, but only the least +significant byte will be used. + + +.SS Quake character set extension escape sequences: + +.TP +.B \(rsb +Toggle bronze characters. Requires \fB\-\-qccx\-escapes\fP. + +.TP +.B \(rss +Toggle "bold" characters (add 0x80). + .TP .B \(rs[ Gold [ character. Equivalent to \(rsx90. + .TP .B \(rs] Gold ] character. Equivalent to \(rsx91. + .TP .B \(rs. Center dot. Equivalent to \(rsx1c. + .TP .B \(rs< Turn on "bold" characters (add 0x80). This conflicts with \fBqccx\fP. In -\fBqccx\fP, this produces the brown left end. Equivalent to \(rsx1d. Use +\fBqccx\fP, this produces the separator left end. Equivalent to \(rsx1d. Use \fB\-\-qccx\-escapes\fP to select \fBqccx\fP behaviour. + .TP .B \(rs\- -Brown center bit. Equivalent to \(rsx1e. +Separator center. Equivalent to \(rsx1e. + .TP .B \(rs> Turn off "bold" characters (add 0x80). This conflicts with \fBqccx\fP. In -\fBqccx\fP, this produces the brown right end. Equivalent to \(rsx1f. Use +\fBqccx\fP, this produces the separator right end. Equivalent to \(rsx1f. Use \fB\-\-qccx\-escapes\fP to select \fBqccx\fP behaviour. + +.TP +.B \(rs^ +Make the next character "bold" (add 0x80). + +.TP +.B \(rs0-9 +Produce gold digits. Requires \fB\-\-qccx\-escapes\fP (except \(rs8 and \(rs9: +they are always available). + .TP .B \(rs( -Left slider end. Equivalent to \(rsx80. +Slider left end. Equivalent to \(rsx80. + .TP .B \(rs= Slider center. Equivalent to \(rsx81. + .TP .B \(rs) -Right slider end. Equivalent to \(rsx82. +Slider right end. Equivalent to \(rsx82. + .TP .B \(rs{0-255} -Decimal character code. +Decimal character code. Quake specific as qccx added this to allow specifying +the character code directly as \(rs0-\(rs9 were already used for specifying +gold digits. + .P \fB\-\-qccx\-escapes\fP has no effect on sequences that do not conflict. -.SH TRADITIONAL VS ADVANCED -Compared to \fBqcc\fP, \*[qfcc] has many advanced features and is much stricter -about type checking. + + +.SH TRADITIONAL VS ADVANCED VS RUAMOKO +Compared to \fBqcc\fP, \*[qfcc] has many advanced features and is much +stricter about type checking. \*[qfcc] also uses the same operator semantics and precedence rules as standard \fBC\fP. -Unfortunately, this means that most older QuakeC code will not compile, or even -worse, will compile incorrectly. +Unfortunately, this means that most older QuakeC code will not compile, +or even worse, will compile incorrectly. .P To address this situation, \*[qfcc] has a \*(lqtraditional\*(rq mode for compiling old progs. @@ -573,26 +744,47 @@ This mode, enabled with \fB--traditional\fP or by default in \fBprogs.src\fP mode, removes the new keywords required by \*[qfcc]'s advanced features, converts new errors to warnings, some warnings to notices and inverts precedence order where required (eg, (!var & flag)). -Traditional mode also affects several code generation options (as always, this -can be overridden): +Traditional mode also affects several code generation options (as +always, this can be overridden): .IP \(bu 4 code output is restricted to version 6 progs instructions .IP \(bu 4 short circuit boolean logic is disabled .IP \(bu 4 -each function has a private area of data for its local variables (this wastes -a lot of data space). +each function has a private area of data for its local variables (this +wastes a lot of data space). .P -Advanced mode is simply \*[qfcc] in its natural state. -Using \fB--advanced\fP, \*[qfcc] can be put in to advanced mode while using the -\fBprogs.src\fP compilation mode. +Advanced mode is \*[qfcc] in what was its natural state until the +introduction of Ruamoko mode. Advanced mode adds several data types and +Objective-C object oriented programming. +Using \fB--advanced\fP, \*[qfcc] can be put in to advanced mode while +using the \fBprogs.src\fP compilation mode. +.P +Ruamoko mode is \*[qfcc] in its natural state. On top of the features +added by Advanced mode, Ruamoko mode adds SIMD types and instructions, +and a data stack for locals and parameters. + +.SH RUAMOKO PROGRAMMING LANGUAGE +Ruamoko evolved from the original QuakeC language, gaining standard C +syntax and most features (the char type is not supported, and function +pointers are a little weird (design bug?)), Objective-C object oriented +extensions, and (with the Ruamoko ISA) SIMD vectors and the ability to +pass pointers to local variables to other functions. + .SH "FAQ" + .TP .B Where did the name Ruamoko come from? In Maori mythology, Ruamoko is the youngest child of Ranginui, the Sky-father, and Papatuanuku, the Earth-mother. Ruamoko is the god of volcanoes and earthquakes. For more information, see the Web site at <\fBhttp://maori.com/kmst1.htm\fP>. + +.TP +.B Why both Ruamoko and Raumoko? +They are alternative spellings and pronunciations. Use whichever one you +prefer. + .TP .B qfcc hangs This is almost always caused by qfcc incorrectly invoking \*[cpp]. @@ -600,11 +792,13 @@ Using the \fB--cpp\fP option (refer to the \fBCPP NAME\fP section above), the correct method for invoking \*[cpp] can be specified. Once you have found this, please send the correct \*[cpp] command line, preferably along with the output of \fBconfig.guess\fP, to the team. + .TP .B qfcc is singing a bad 80s rap song to me. What's going on? \*(lqice ice baby\*(rq is QuakeForge-speak for \*(lqInternal Compiler Error\*(rq. It usually means there's a bug in \*[qfcc], so please report it to the team. + .TP .B qfcc is mooing at me. What's wrong with you people? The compiler doesn't like being treated like a slab of beef. @@ -615,11 +809,17 @@ while, but you told it not to do that by passing the \fBcow\fP option to \fB\-\-code\fP, so it has its revenge by mooing out a warning. Or something like that. To disable the warning, pass \fBno-cow\fP to \fB\-\-warn\fP. + + .SH "FILES" .I progs.src + + .SH "SEE ALSO" .BR quakeforge (1), .BR pak (1) + + .SH AUTHORS The original \fBqcc\fP program, for compiling the QuakeC language, was written by Id Software, Inc. diff --git a/tools/qfcc/include/Makefile.am b/tools/qfcc/include/Makefile.am deleted file mode 100644 index 140cae90a..000000000 --- a/tools/qfcc/include/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -EXTRA_DIST= class.h codespace.h cpp.h dags.h debug.h def.h defspace.h \ - diagnostic.h dot.h emit.h expr.h flow.h function.h grab.h idstuff.h \ - linker.h method.h obj_file.h obj_type.h opcodes.h options.h pragma.h \ - qfcc.h qfprogs.h reloc.h shared.h statements.h strpool.h struct.h \ - switch.h symtab.h type.h value.h diff --git a/tools/qfcc/include/Makemodule.am b/tools/qfcc/include/Makemodule.am new file mode 100644 index 000000000..6609ddfc4 --- /dev/null +++ b/tools/qfcc/include/Makemodule.am @@ -0,0 +1,37 @@ +EXTRA_DIST += \ + tools/qfcc/include/attribute.h \ + tools/qfcc/include/class.h \ + tools/qfcc/include/codespace.h \ + tools/qfcc/include/cpp.h \ + tools/qfcc/include/dags.h \ + tools/qfcc/include/debug.h \ + tools/qfcc/include/def.h \ + tools/qfcc/include/defspace.h \ + tools/qfcc/include/diagnostic.h \ + tools/qfcc/include/dot.h \ + tools/qfcc/include/emit.h \ + tools/qfcc/include/expr.h \ + tools/qfcc/include/expr_names.h \ + tools/qfcc/include/flow.h \ + tools/qfcc/include/function.h \ + tools/qfcc/include/grab.h \ + tools/qfcc/include/idstuff.h \ + tools/qfcc/include/linker.h \ + tools/qfcc/include/method.h \ + tools/qfcc/include/obj_file.h \ + tools/qfcc/include/obj_type.h \ + tools/qfcc/include/opcodes.h \ + tools/qfcc/include/options.h \ + tools/qfcc/include/pragma.h \ + tools/qfcc/include/qfcc.h \ + tools/qfcc/include/qfprogs.h \ + tools/qfcc/include/reloc.h \ + tools/qfcc/include/shared.h \ + tools/qfcc/include/statements.h \ + tools/qfcc/include/strpool.h \ + tools/qfcc/include/struct.h \ + tools/qfcc/include/switch.h \ + tools/qfcc/include/symtab.h \ + tools/qfcc/include/type.h \ + tools/qfcc/include/value.h \ + tools/qfcc/include/vec_types.h diff --git a/tools/qfcc/include/attribute.h b/tools/qfcc/include/attribute.h new file mode 100644 index 000000000..2f47642e6 --- /dev/null +++ b/tools/qfcc/include/attribute.h @@ -0,0 +1,43 @@ +/* + attribute.h + + Attribute list handling + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/02/02 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef attribute_h +#define attribute_h + +typedef struct attribute_s { + struct attribute_s *next; + const char *name; + struct ex_value_s *value; +} attribute_t; + +struct expr_s; +attribute_t *new_attribute(const char *name, struct expr_s *value); + +#endif//attribute_h diff --git a/tools/qfcc/include/class.h b/tools/qfcc/include/class.h index a2cf55182..217227a73 100644 --- a/tools/qfcc/include/class.h +++ b/tools/qfcc/include/class.h @@ -48,6 +48,7 @@ typedef struct class_type_s { typedef struct class_s { int defined; + int interface_declared; const char *name; struct class_s *super_class; struct category_s *categories; @@ -74,6 +75,7 @@ typedef struct protocol_s { const char *name; struct methodlist_s *methods; struct protocollist_s *protocols; + struct def_s *def; class_type_t class_type; } protocol_t; @@ -82,19 +84,25 @@ typedef struct protocollist_s { protocol_t **list; } protocollist_t; +typedef struct static_instance_s { + const char *class; + struct def_s *instance; +} static_instance_t; + extern struct type_s type_id; -extern struct type_s type_obj_object; -extern struct type_s type_obj_class; +extern struct type_s type_object; +extern struct type_s type_class; extern struct type_s type_Class; -extern struct type_s type_obj_protocol; +extern struct type_s type_protocol; +extern struct type_s type_selector; extern struct type_s type_SEL; extern struct type_s type_IMP; extern struct type_s type_supermsg; -extern struct type_s type_obj_exec_class; -extern struct type_s type_obj_method; -extern struct type_s type_obj_super; -extern struct type_s type_obj_method_description; -extern struct type_s type_obj_category; +extern struct type_s type_exec_class; +extern struct type_s type_method; +extern struct type_s type_super; +extern struct type_s type_method_description; +extern struct type_s type_category; extern struct type_s type_ivar; extern struct type_s type_module; @@ -106,14 +114,19 @@ struct dstring_s; struct expr_s; struct method_s; struct symbol_s; +struct selector_s; -int obj_is_id (const struct type_s *type); -int obj_is_class (const struct type_s *type); -int obj_is_Class (const struct type_s *type); -int obj_is_classptr (const struct type_s *type); +int is_id (const struct type_s *type) __attribute__((pure)); +int is_class (const struct type_s *type) __attribute__((pure)); +int is_Class (const struct type_s *type) __attribute__((const)); +int is_classptr (const struct type_s *type) __attribute__((pure)); +int is_SEL (const struct type_s *type) __attribute__((const)); +int is_object (const struct type_s *type) __attribute__((const)); +int is_method (const struct type_s *type) __attribute__((const)); +int is_method_description (const struct type_s *type) __attribute__((const)); int obj_types_assignable (const struct type_s *dst, const struct type_s *src); -class_t *extract_class (class_type_t *class_type); +class_t *extract_class (class_type_t *class_type) __attribute__((pure)); const char *get_class_name (class_type_t *class_type, int pretty); struct symbol_s *class_symbol (class_type_t *class_type, int external); void class_init (void); @@ -125,7 +138,7 @@ void class_add_ivars (class_t *class, struct symtab_s *ivars); void class_check_ivars (class_t *class, struct symtab_s *ivars); void class_begin (class_type_t *class_type); void class_finish (class_type_t *class_type); -int class_access (class_type_t *current_class, class_t *class); +int class_access (class_type_t *current_class, class_t *class) __attribute__((pure)); struct symbol_s *class_find_ivar (class_t *class, int vis, const char *name); struct symtab_s *class_ivar_scope (class_type_t *class_type, struct symtab_s *parent); @@ -134,7 +147,7 @@ void class_finish_ivar_scope (class_type_t *class_type, struct symtab_s *param_scope); struct method_s *class_find_method (class_type_t *class_type, struct method_s *method); -struct method_s *class_message_response (class_t *class, int class_msg, +struct method_s *class_message_response (struct type_s *clstype, int class_msg, struct expr_s *sel); struct symbol_s *class_pointer_symbol (class_t *class_type); category_t *get_category (struct symbol_s *class_name, @@ -153,8 +166,13 @@ void protocol_add_protocols (protocol_t *protocol, protocollist_t *protocols); struct def_s *protocol_def (protocol_t *protocol); protocollist_t *new_protocol_list (void); protocollist_t *add_protocol (protocollist_t *protocollist, const char *name); -int procollist_find_protocol (protocollist_t *protocollist, protocol_t *proto); -int compare_protocols (protocollist_t *protos1, protocollist_t *protos2); +int procollist_find_protocol (protocollist_t *protocollist, protocol_t *proto) __attribute__((pure)); +struct method_s *protocollist_find_method (protocollist_t *protocollist, + struct selector_s *selector, + int nstance) + __attribute__((pure)); + +int compare_protocols (protocollist_t *protos1, protocollist_t *protos2) __attribute__((pure)); void print_protocollist (struct dstring_s *dstr, protocollist_t *protocollist); struct def_s *emit_protocol (protocol_t *protocol); struct def_s *emit_protocol_list (protocollist_t *protocols, const char *name); diff --git a/tools/qfcc/include/cpp.h b/tools/qfcc/include/cpp.h index 6b599b439..81274c353 100644 --- a/tools/qfcc/include/cpp.h +++ b/tools/qfcc/include/cpp.h @@ -34,6 +34,8 @@ struct dstring_s; void parse_cpp_name (void); +void add_cpp_sysinc (const char *arg); +void add_cpp_undef (const char *arg); void add_cpp_def (const char *arg); void intermediate_file (struct dstring_s *ifile, const char *filename, const char *ext, int local); diff --git a/tools/qfcc/include/dags.h b/tools/qfcc/include/dags.h index 21ac8c943..66fbece40 100644 --- a/tools/qfcc/include/dags.h +++ b/tools/qfcc/include/dags.h @@ -33,9 +33,9 @@ /** \defgroup qfcc_dags DAG building \ingroup qfcc */ -//@{ +///@{ -#include "QF/pr_comp.h" +#include "QF/progs/pr_comp.h" #include "statements.h" @@ -46,7 +46,7 @@ typedef struct daglabel_s { struct daglabel_s *next; struct daglabel_s *daglabel_chain; ///< all labels created for a dag int number; ///< index into array of labels in dag_t - unsigned live:1; ///< accessed via an alias + unsigned live:1; ///< accessed via an alias FIXME redundant? const char *opcode; ///< not if op struct operand_s *op; ///< not if opcode; struct dagnode_s *dagnode; ///< node with which this label is associated @@ -59,10 +59,10 @@ typedef struct dagnode_s { int topo; ///< topological sort order struct set_s *parents; ///< empty if root node int cost; ///< cost of this node in temp vars - unsigned killed:1; ///< node is unavailable for cse - st_type_t type; ///< type of node (st_node = leaf) + struct dagnode_s *killed; ///< node is unavailable for cse (by node) + st_type_t type; ///< type of node (st_none = leaf) daglabel_t *label; ///< ident/const if leaf node, or operator - etype_t tl; + struct type_s *tl; struct operand_s *value; ///< operand holding the value of this node /// \name child nodes /// if \a children[0] is null, the rest must be null as well. Similar for @@ -75,10 +75,13 @@ typedef struct dagnode_s { /// topological sort of the DAG. //@{ struct dagnode_s *children[3]; - etype_t types[3]; ///< desired type of each operand (to alias) + struct type_s *types[3]; ///< desired type of each operand (to alias) struct set_s *edges; ///< includes nodes pointed to by \a children //@} struct set_s *identifiers; ///< set of identifiers attached to this node + struct set_s *reachable; ///< set of nodes reachable via edges (not + ///< parents) for ensuring cycles are not + ///< created } dagnode_t; typedef struct dag_s { @@ -89,7 +92,7 @@ typedef struct dag_s { int num_topo; ///< number of nodes in topo (may be < ///< num_nodes after dead node removal) daglabel_t **labels; ///< array of all daglabels in this dag - int num_labels;; + int num_labels; struct set_s *roots; ///< set of root nodes struct flownode_s *flownode;///< flow node this dag represents } dag_t; @@ -110,6 +113,6 @@ dag_t *dag_create (struct flownode_s *flownode); void dag_remove_dead_nodes (dag_t *dag); void dag_generate (dag_t *dag, sblock_t *block); -//@} +///@} #endif//dags_h diff --git a/tools/qfcc/include/debug.h b/tools/qfcc/include/debug.h index a5d5b5a1f..6e7d6f477 100644 --- a/tools/qfcc/include/debug.h +++ b/tools/qfcc/include/debug.h @@ -31,10 +31,12 @@ #ifndef __debug_h #define __debug_h -#include "QF/pr_debug.h" +#include "QF/progs/pr_debug.h" void line_info (char *text); pr_lineno_t *new_lineno (void); +void add_source_file (const char *file); +void debug_finish_module (const char *modname); extern int lineno_base; diff --git a/tools/qfcc/include/def.h b/tools/qfcc/include/def.h index 7e24506b9..a00758bdf 100644 --- a/tools/qfcc/include/def.h +++ b/tools/qfcc/include/def.h @@ -31,15 +31,16 @@ #ifndef __def_h #define __def_h -#include "QF/pr_comp.h" -#include "QF/pr_debug.h" +#include "QF/progs/pr_comp.h" +#include "QF/progs/pr_debug.h" /** \defgroup qfcc_def Def handling \ingroup qfcc */ -//@{ +///@{ struct symbol_s; +struct symtab_s; struct expr_s; /** Represent a memory location that holds a QuakeC/Ruamoko object. @@ -60,6 +61,7 @@ typedef struct def_s { const char *name; ///< the def's name struct defspace_s *space; ///< defspace to which this def belongs int offset; ///< address of this def in its defspace + int reg; ///< base register index to access def /** \name Def aliasing. Aliasing a def provides a different view of the def providing access @@ -71,7 +73,7 @@ typedef struct def_s { def they alias, including relocation records. However, they do keep track of the source file and line that first created the alias. - The relations between a def an any of its aliases are maintained by + The relations between a def and any of its aliases are maintained by a linked list headed by def_t::alias_defs and connected by def_t::next. def_t::alias is used to find the main def via one if its aliases. The order of the aliases in the list is arbitrary: it is the @@ -95,10 +97,11 @@ typedef struct def_s { unsigned external:1; ///< externally declared def unsigned local:1; ///< function local def unsigned param:1; ///< function param def + unsigned argument:1; ///< function argument def unsigned system:1; ///< system def unsigned nosave:1; ///< don't set DEF_SAVEGLOBAL - string_t file; ///< declaring/defining source file + pr_string_t file; ///< declaring/defining source file int line; ///< declaring/defining source line int qfo_def; ///< index to def in qfo defs @@ -110,12 +113,13 @@ typedef struct def_s { /** Specify the storage class of a def. */ typedef enum storage_class_e { - sc_global, ///< def is globally visibil across units + sc_global, ///< def is globally visible across units sc_system, ///< def may be redefined once sc_extern, ///< def is externally allocated sc_static, ///< def is private to the current unit sc_param, ///< def is an incoming function parameter - sc_local ///< def is local to the current function + sc_local, ///< def is local to the current function + sc_argument, ///< def is a function argument } storage_class_t; /** Create a new def. @@ -180,7 +184,7 @@ void free_def (def_t *def); Temporary defs are bound to the current function (::current_func must be valid). They are always allocated from the funciont's local defspace. */ -//@{ +///@{ /** Get a temporary def. If the current function has a free temp def of the same size as \a size, @@ -190,14 +194,13 @@ void free_def (def_t *def); \note ::current_func must be valid. - \param type The low-level type of the temporary variable. - \param size The amount of space to allocate to the temp. + \param type The type of the temporary variable. \return The def for the temparary variable. - \bug \a size is not checked for validity (must be 1-4). + \bug size of type must be 1 to 4. \todo support arbitrary sizes */ -def_t *temp_def (etype_t type, int size); +def_t *temp_def (struct type_s *type); /** Free a tempary def so it may be recycled. @@ -208,7 +211,7 @@ def_t *temp_def (etype_t type, int size); \param temp The temp def to be recycled. */ void free_temp_def (def_t *temp); -//@} +///@} /** Initialize a vm def from a qfcc def. @@ -220,9 +223,14 @@ void free_temp_def (def_t *temp); */ void def_to_ddef (def_t *def, ddef_t *ddef, int aux); +void init_vector_components (struct symbol_s *vector_sym, int is_field, + struct symtab_s *symtab); + /** Initialize a def referenced by the given symbol. - The symbol is checked for redefinition. (FIXME check rules) + The symbol is checked for redefinition. A symbol is considered to be + redefined if the previous definition is in the same symbol table and + of a different type or already initialized. If \a type is null, then the def will be given the default type (as specified by ::type_default). @@ -237,15 +245,14 @@ void def_to_ddef (def_t *def, ddef_t *ddef, int aux); For \a space and \a storage, see new_def(). \param sym The symbol for which to create and initialize a def. - \param type The type of the def. sym_t::type is set to this. If null, - the default type is used. \param init If not null, the expressions to use to initialize the def. \param space The space from which to allocate space for the def. \param storage The storage class of the def. + \param symtab The symbol table into which the def will be placed. */ -void initialize_def (struct symbol_s *sym, struct type_s *type, +void initialize_def (struct symbol_s *sym, struct expr_s *init, struct defspace_s *space, - storage_class_t storage); + storage_class_t storage, struct symtab_s *symtab); /** Determine if two defs overlap. @@ -254,7 +261,7 @@ void initialize_def (struct symbol_s *sym, struct type_s *type, \return 1 if the defs overlap, 2 if \a d1 fully overlaps \a d2, otherwise 0. */ -int def_overlap (def_t *d1, def_t *d2); +int def_overlap (def_t *d1, def_t *d2) __attribute__((pure)); /** Convenience function for obtaining a def's actual offset. @@ -263,7 +270,7 @@ int def_overlap (def_t *d1, def_t *d2); \param def The def of which to obtain the offset. May be an alias def. \return The actual offset of the def in the def's defspace. */ -int def_offset (def_t *def); +int def_offset (def_t *def) __attribute__((pure)); /** Convenience function for obtaining a def's size. @@ -272,7 +279,7 @@ int def_offset (def_t *def); \param def The def of which to obtain the size. \return The size of the def. */ -int def_size (def_t *def); +int def_size (def_t *def) __attribute__((pure)); /** Visit all defs that alias the given def, including itself. @@ -307,6 +314,6 @@ int def_size (def_t *def); */ int def_visit_all (def_t *def, int overlap, int (*visit) (def_t *, void *), void *data); -//@} +///@} #endif//__def_h diff --git a/tools/qfcc/include/defspace.h b/tools/qfcc/include/defspace.h index 371af492e..1152bf5a5 100644 --- a/tools/qfcc/include/defspace.h +++ b/tools/qfcc/include/defspace.h @@ -31,19 +31,20 @@ #ifndef __defspace_h #define __defspace_h -#include "QF/pr_comp.h" -#include "QF/pr_debug.h" +#include "QF/progs/pr_comp.h" +#include "QF/progs/pr_debug.h" /** \defgroup qfcc_defspace Defspace handling \ingroup qfcc */ -//@{ +///@{ typedef enum { ds_backed, ///< data space is globally addressable (near/far/type) and ///< has backing store ds_virtual, ///< data space has no backing store (local vars, entity - ///< fields) + ///< fields) defspace_t::max_size reflects the highest + ///< address allocated } ds_type_t; /** Represent a block of memory in the progs data space. @@ -55,8 +56,10 @@ typedef struct defspace_s { struct def_s *defs; ///< list of defs using this space struct def_s **def_tail; ///< for appending to \a defs pr_type_t *data; ///< backing memory for this space + int alignment; ///< minimum alignment of the whole space int size; ///< current high-water mark for alloced data - int max_size; ///< size of backing memory + int max_size; ///< size of backing memory, or highwater mark + ///< for ds_virtual /** Grow the backing memory of the defspace. This function is called when more memory is needed for the space. @@ -90,6 +93,8 @@ typedef struct defspace_s { */ defspace_t *defspace_new (ds_type_t type); +void defspace_delete (defspace_t *defspace); + /** Allocate space from the defspace's backing memory. If the memory is fragmented, then the first available location at least @@ -109,20 +114,41 @@ defspace_t *defspace_new (ds_type_t type); */ int defspace_alloc_loc (defspace_t *space, int size); +/** Allocate space from the defspace's backing memory. + + If the memory is fragmented, then the first available location at least + as large as \a size plus padding for alignment is returned. This means + that freeing a location then allocating the same amount of space may + return a different location. + + If memory cannot be allocated (there is no free space in the currently + available memory and defspace_t::grow is null), then an internal error + will be generated. + + \param space The space from which to allocate data. + \param size The amount of pr_type_t words to allocated. int and float + need 1 word, vector 3 words, and quaternion 4. + \param alignment The alignment of the allocated space. + \return The offset of the first word of the freshly allocated + space. May be 0 if the allocated space is at the beginning + of the defspace. +*/ +int defspace_alloc_aligned_loc (defspace_t *space, int size, int alignment); + /** Free a block of contiguous words, returning them to the defspace. The block to be freed is specified by \a ofs indicating the offset of the first word of the block and \a size indicating the number of words in the block. - If the block to be freed has 0 words, or if the is partly or fully outside - the defspace (as defined by defspace_t::size), or if the block overlaps - any unallocated space in the defspace, then an internal error will be - generated. However, it is perfectly valid to allocate a large block and - subsequently free a small block from anywhere within the larger block. - This is because when memory is not fragmented, there is no difference - between allocating one large block and allocating several smaller blocks - when allocating the same amount of memory. + If the block to be freed has 0 words, or if the block is partly or fully + outside the defspace (as defined by defspace_t::size), or if the block + overlaps any unallocated space in the defspace, then an internal error + will be generated. However, it is perfectly valid to allocate a large + block and subsequently free a small block from anywhere within the larger + block. This is because when memory is not fragmented, there is no + difference between allocating one large block and allocating several + smaller blocks when allocating the same amount of memory. \param space The space to which the freed block will be returned. \param ofs The first word of the block to be freed. @@ -136,7 +162,7 @@ void defspace_free_loc (defspace_t *space, int ofs, int size); defspace_alloc_loc(). If \a data is null, then the copying stage is skipped and this function - because a synonym for defspace_alloc_loc(). + becomes a synonym for defspace_alloc_loc(). \param space The space to which the data will be added. \param data The data to be copied into the space. @@ -145,6 +171,57 @@ void defspace_free_loc (defspace_t *space, int ofs, int size); */ int defspace_add_data (defspace_t *space, pr_type_t *data, int size); -//@} +/** Allocate a block of data from the end of the defspace. + + If memory cannot be allocated (there is no free space in the currently + available memory and defspace_t::grow is null), then an internal error + will be generated. + + \param space The space from which to allocate data. + \param size The amount of pr_type_t words to allocated. int and float + need 1 word, vector 3 words, and quaternion 4. + \return The offset of the first word of the freshly allocated + space. May be 0 if the allocated space is at the beginning + of the defspace. +*/ +int defspace_alloc_highwater (defspace_t *space, int size); + +/** Allocate an aligned block of data from the end of the defspace. + + Any unallocated holes in the defspace are ignored, even if the hole is + at the end of the defspace. However, any holes created by the padding + required for aligning the block will be available to defspace_alloc_loc() + and defspace_alloc_aligned_loc(). + + If memory cannot be allocated (there is no free space in the currently + available memory and defspace_t::grow is null), then an internal error + will be generated. + + \param space The space from which to allocate data. + \param size The amount of pr_type_t words to allocated. int and float + need 1 word, vector 3 words, and quaternion 4. + \param alignment The alignment of the allocated space. + \return The offset of the first word of the freshly allocated + space. May be 0 if the allocated space is at the beginning + of the defspace. +*/ +int defspace_alloc_aligned_highwater (defspace_t *space, int size, + int alignment); +/** Reset a defspace, freeing all allocated memory. + + defspace_t::max_size is not affected. This allows the defspace to be used + to find the larged block of memory required for a set of operations (eg, + the largest parameter block required in a function for all its calls + allowing the stack to remain constant instead of using many push/pop + operations. Note that this works best with ds_virtual defspaces. + + If the defspace has backing memory (ds_backed), the memory is not freed, + but it is zeroed so any new allocations will contain zeroed memory. + + \param space The space to be reset. +*/ +void defspace_reset (defspace_t *space); + +///@} #endif//__defspace_h diff --git a/tools/qfcc/include/diagnostic.h b/tools/qfcc/include/diagnostic.h index 51eea07a5..526745f56 100644 --- a/tools/qfcc/include/diagnostic.h +++ b/tools/qfcc/include/diagnostic.h @@ -31,27 +31,48 @@ #ifndef __diagnostic_h #define __diagnostic_h -#include "QF/pr_comp.h" - /** \defgroup qfcc_diagnostic Diagnostic Messages \ingroup qfcc */ -//@{ +///@{ -struct expr_s *error (struct expr_s *e, const char *fmt, ...) - __attribute__ ((format (printf, 2, 3))); -void -internal_error (struct expr_s *e, const char *fmt, ...) - __attribute__ ((format (printf, 2, 3), noreturn)); -struct expr_s *warning (struct expr_s *e, const char *fmt, ...) - __attribute__ ((format (printf, 2, 3))); -struct expr_s *notice (struct expr_s *e, const char *fmt, ...) - __attribute__ ((format (printf, 2, 3))); -void debug (struct expr_s *e, const char *fmt, ...) - __attribute__ ((format (printf, 2, 3))); -void bug (struct expr_s *e, const char *fmt, ...) - __attribute__ ((format (printf, 2, 3))); +typedef void (*diagnostic_hook)(const char *message); +extern diagnostic_hook bug_hook; +extern diagnostic_hook error_hook; +extern diagnostic_hook warning_hook; +extern diagnostic_hook notice_hook; -//@} +struct expr_s *_error (struct expr_s *e, const char *file, int line, + const char *fmt, ...) + __attribute__ ((format (PRINTF, 4, 5))); +#define error(e, fmt...) _error(e, __FILE__, __LINE__, fmt) + +void _internal_error (const struct expr_s *e, const char *file, int line, + const char *fmt, ...) + __attribute__ ((format (PRINTF, 4, 5), noreturn)); +#define internal_error(e, fmt...) _internal_error(e, __FILE__, __LINE__, fmt) + +struct expr_s *_warning (struct expr_s *e, const char *file, int line, + const char *fmt, ...) + __attribute__ ((format (PRINTF, 4, 5))); +#define warning(e, fmt...) _warning(e, __FILE__, __LINE__, fmt) + +struct expr_s *_notice (struct expr_s *e, const char *file, int line, + const char *fmt, ...) + __attribute__ ((format (PRINTF, 4, 5))); +#define notice(e, fmt...) _notice(e, __FILE__, __LINE__, fmt) + +void _debug (struct expr_s *e, const char *file, int line, + const char *fmt, ...) + __attribute__ ((format (PRINTF, 4, 5))); +#define debug(e, fmt...) _debug(e, __FILE__, __LINE__, fmt) + +void _bug (struct expr_s *e, const char *file, int line, const char *fmt, ...) + __attribute__ ((format (PRINTF, 4, 5))); +#define bug(e, fmt...) _bug(e, __FILE__, __LINE__, fmt) + +void print_srcline (int rep, const struct expr_s *e); + +///@} #endif//__diagnostic_h diff --git a/tools/qfcc/include/expr.h b/tools/qfcc/include/expr.h index 5b1c6d1e7..19aa8060f 100644 --- a/tools/qfcc/include/expr.h +++ b/tools/qfcc/include/expr.h @@ -31,31 +31,21 @@ #ifndef __expr_h #define __expr_h -#include "QF/pr_comp.h" +#include "QF/progs/pr_comp.h" /** \defgroup qfcc_expr Expressions \ingroup qfcc */ -//@{ +///@{ /** Type of the exression node in an expression tree. */ +#define EX_EXPR(expr) ex_##expr, typedef enum { - ex_error, ///< error expression. used to signal an error - ex_state, ///< state expression (::ex_state_t) - ex_bool, ///< short circuit boolean logic expression (::ex_bool_t) - ex_label, ///< goto/branch label (::ex_label_t) - ex_labelref, ///< label reference (::ex_labelref_t) - ex_block, ///< statement block expression (::ex_block_t) - ex_expr, ///< binary expression (::ex_expr_t) - ex_uexpr, ///< unary expression (::ex_expr_t) - ex_symbol, ///< non-temporary variable (::symbol_t) - ex_temp, ///< temporary variable (::ex_temp_t) - ex_vector, ///< "vector" expression (::ex_vector_t) - - ex_nil, ///< umm, nil, null. nuff said (0 of any type) - ex_value, ///< constant value (::ex_value_t) +#include "tools/qfcc/include/expr_names.h" + ex_count, ///< number of valid expression types } expr_type; +#undef EX_EXPR /** Binary and unary expressions. @@ -75,6 +65,7 @@ typedef struct ex_label_s { struct reloc_s *refs; ///< relocations associated with this label struct sblock_s *dest; ///< the location of this label if known const char *name; ///< the name of this label + struct symbol_s *symbol; ///< symbol used to define this label (maybe 0) int used; ///< label is used as a target struct daglabel_s *daglabel; } ex_label_t; @@ -83,15 +74,34 @@ typedef struct { ex_label_t *label; } ex_labelref_t; +typedef struct designator_s { + struct designator_s *next; + struct expr_s *field; + struct expr_s *index; +} designator_t; + +typedef struct element_s { + struct element_s *next; ///< next in chain + int offset; + struct type_s *type; + struct expr_s *expr; ///< initializer expression + designator_t *designator; ///< for labeled initializers +} element_t; + +typedef struct element_chain_s { + element_t *head; + element_t **tail; +} element_chain_t; + typedef struct { struct expr_s *head; ///< the first expression in the block struct expr_s **tail; ///< last expression in the block, for appending struct expr_s *result; ///< the result of this block if non-void int is_call; ///< this block exprssion forms a function call + void *return_addr;///< who allocated this } ex_block_t; typedef struct { - struct expr_s *expr; struct operand_s *op; ///< The operand for the temporary variable, if ///< allocated struct type_s *type; ///< The type of the temporary variable. @@ -102,6 +112,11 @@ typedef struct { struct expr_s *list; ///< Linked list of element expressions. } ex_vector_t; +typedef struct { + struct expr_s *sel_ref; ///< Reference to selector in selector table + struct selector_s *sel; ///< selector +} ex_selector_t; + /** Pointer constant expression. Represent a pointer to an absolute address in data space. @@ -110,6 +125,7 @@ typedef struct ex_pointer_s { int val; struct type_s *type; struct def_s *def; + struct operand_s *tempop; } ex_pointer_t; typedef struct ex_func_s { @@ -128,6 +144,12 @@ typedef struct { struct expr_s *e; } ex_bool_t; +typedef struct ex_memset_s { + struct expr_s *dst; + struct expr_s *val; + struct expr_s *count; +} ex_memset_t; + /** State expression used for think function state-machines. State expressions are of the form [framenum, nextthink] @@ -171,46 +193,134 @@ typedef struct { typedef struct ex_value_s { struct ex_value_s *next; struct daglabel_s *daglabel;///< dag label for this value - etype_t type; + struct type_s *type; + etype_t lltype; union { const char *string_val; ///< string constant + double double_val; ///< double constant + int64_t long_val; ///< signed 64-bit constant + uint64_t ulong_val; ///< unsigned 64-bit constant float float_val; ///< float constant float vector_val[3]; ///< vector constant int entity_val; ///< entity constant ex_func_t func_val; ///< function constant ex_pointer_t pointer; ///< pointer constant float quaternion_val[4]; ///< quaternion constant - int integer_val; ///< integer constant - unsigned uinteger_val; ///< unsigned integer constant - short short_val; ///< short constant + int int_val; ///< int constant + unsigned uint_val; ///< unsigned int constant + int16_t short_val; ///< short constant + uint16_t ushort_val; ///< unsigned short constant +#define VEC_TYPE(type_name, base_type) pr_##type_name##_t type_name##_val; +#include "tools/qfcc/include/vec_types.h" } v; } ex_value_t; +typedef struct { + struct type_s *type; ///< type to view the expression + struct expr_s *expr; ///< the expression to alias + struct expr_s *offset; ///< offset for alias +} ex_alias_t; + +typedef struct { + struct type_s *type; ///< pointer type + struct expr_s *lvalue; ///< the lvalue being addressed + struct expr_s *offset; ///< offset from the address +} ex_address_t; + +typedef struct { + struct expr_s *dst; ///< destination of assignment + struct expr_s *src; ///< source of assignment +} ex_assign_t; + +typedef struct { + pr_branch_e type; ///< type of branch + struct expr_s *target; ///< destination of branch + struct expr_s *index; ///< index for indirect branches + struct expr_s *test; ///< test expression (null for jump/call) + struct expr_s *args; ///< only for call + struct type_s *ret_type; ///< void for non-call +} ex_branch_t; + +typedef struct { + struct expr_s *ret_val; + int at_return; ///< return void_return call through void +} ex_return_t; + +typedef struct { + short mode; ///< currently must be 0 + short offset; ///< amount by which stack will be adjusted +} ex_adjstk_t; + +typedef struct { + short mode; + short reg; ///< base register to load + struct expr_s *with; ///< value to load +} ex_with_t; + +typedef struct { + int op; ///< operation to perform + struct expr_s *vec; ///< vector expression on which to operate + struct type_s *type; ///< result type +} ex_horizontal_t; + +//NOTE always operates on vec4 or dvec4, so needs a suitable destination and +//care must be taken when working with smaller source operands (check aligmnet +//and adjust swizzle operation as needed) +typedef struct { + struct expr_s *src; ///< source expression + unsigned source[4]; ///< src component indices + unsigned neg; ///< bitmask of dst components to negate + unsigned zero; ///< bitmask of dst components to 0 + struct type_s *type; ///< result type +} ex_swizzle_t; + +typedef struct { + struct expr_s *src; ///< source expression + int extend; ///< extend mode 0: 0, 1: 1, 2: copy/0 3:-1 + struct type_s *type; ///< result type; +} ex_extend_t; + #define POINTER_VAL(p) (((p).def ? (p).def->offset : 0) + (p).val) typedef struct expr_s { struct expr_s *next; ///< the next expression in a block expression - expr_type type; ///< the type of the result of this expression - int line; ///< source line that generated this expression - string_t file; ///< source file that generated this expression + expr_type type; ///< the type of the result of this expression + int line; ///< source line that generated this expression + pr_string_t file; ///< source file that generated this expression int printid; ///< avoid duplicate output when printing - unsigned paren:1; ///< the expression is enclosed in () - unsigned rvalue:1; ///< the expression is on the right side of = + unsigned paren:1; ///< the expression is enclosed in () + unsigned rvalue:1; ///< the expression is on the right side of = + unsigned implicit:1; ///< don't warn for implicit casts union { ex_label_t label; ///< label expression ex_labelref_t labelref; ///< label reference expression (&) ex_state_t state; ///< state expression - ex_bool_t bool; ///< boolean logic expression + ex_bool_t boolean; ///< boolean logic expression ex_block_t block; ///< statement block expression ex_expr_t expr; ///< binary or unary expression + struct def_s *def; ///< def reference expression struct symbol_s *symbol; ///< symbol reference expression ex_temp_t temp; ///< temporary variable expression ex_vector_t vector; ///< vector expression list + ex_selector_t selector; ///< selector ref and name ex_value_t *value; ///< constant value + element_chain_t compound; ///< compound initializer + ex_memset_t memset; ///< memset expr params + ex_alias_t alias; ///< alias expr params + ex_address_t address; ///< alias expr params + ex_assign_t assign; ///< assignment expr params + ex_branch_t branch; ///< branch expr params + ex_return_t retrn; ///< return expr params + ex_adjstk_t adjstk; ///< stack adjust param + ex_with_t with; ///< with expr param + struct type_s *nil; ///< type for nil if known + ex_horizontal_t hop; ///< horizontal vector operation + ex_swizzle_t swizzle; ///< vector swizzle operation + ex_extend_t extend; ///< vector extend operation } e; } expr_t; -extern struct type_s *ev_types[]; +extern const char *expr_names[]; /** Report a type mismatch error. @@ -225,7 +335,6 @@ expr_t *type_mismatch (expr_t *e1, expr_t *e2, int op); expr_t *param_mismatch (expr_t *e, int param, const char *fn, struct type_s *t1, struct type_s *t2); -expr_t *cast_error (expr_t *e, struct type_s *t1, struct type_s *t2); expr_t *test_error (expr_t *e, struct type_s *t); extern expr_t *local_expr; @@ -262,11 +371,19 @@ expr_t *new_expr (void); */ expr_t *copy_expr (expr_t *e); +/** Copy source expression's file and line to the destination expression + + \param dst The expression to receive the file and line + \param src The expression from which the file and line will be taken + \return \a dst +*/ +expr_t *expr_file_line (expr_t *dst, const expr_t *src); + /** Create a new label name. - The label name is guaranteed to to the compilation. It is made up of the - name of the current function plus an incrementing number. The number is - not reset between functions. + The label name is guaranteed to be unique to the compilation. It is made + up of the name of the current function plus an incrementing number. The + number is not reset between functions. \return The string representing the label name. */ @@ -280,6 +397,19 @@ const char *new_label_name (void); */ expr_t *new_label_expr (void); +/** Create a named label expression node. + + The label name is set using new_label_name(), but the symbol is used to add + the label to the function's label scope symbol table. If the label already + exists in the function's label scope, then the existing label is returned, + allowing for forward label declarations. + + \param label The name symbol to use for adding the label to the function + label scope. + \return The new label expression (::ex_label_t) node. +*/ +expr_t *named_label_expr (struct symbol_s *label); + /** Create a new label reference expression node. Used for taking the address of a label (eg. jump tables). @@ -313,7 +443,30 @@ expr_t *new_bool_expr (ex_list_t *true_list, ex_list_t *false_list, expr_t *e); */ expr_t *new_block_expr (void); -/** Create a new binary expression node node. +/** Create a new statement block expression node from an expression list + + The returned block holds the expression list in reverse order. This makes + it easy to build the list in a parser. + + \param expr_list The expression list to convert to an expression block. + Note that the evaluation order will be reversed. + \return The new block expression (::ex_block_t) node. +*/ +expr_t *build_block_expr (expr_t *expr_list); + +designator_t *new_designator (expr_t *field, expr_t *index); +element_t *new_element (expr_t *expr, designator_t *designator); +expr_t *new_compound_init (void); +expr_t *append_element (expr_t *compound, element_t *element); +expr_t *initialized_temp_expr (const struct type_s *type, expr_t *compound); +void assign_elements (expr_t *local_expr, expr_t *ptr, + element_chain_t *element_chain); +void build_element_chain (element_chain_t *element_chain, + const struct type_s *type, + expr_t *eles, int base_offset); +void free_element_chain (element_chain_t *element_chain); + +/** Create a new binary expression node. If either \a e1 or \a e2 are error expressions, then that expression will be returned instead of a new binary expression. @@ -327,10 +480,10 @@ expr_t *new_block_expr (void); */ expr_t *new_binary_expr (int op, expr_t *e1, expr_t *e2); -/** Create a new unary expression node node. +/** Create a new unary expression node. If \a e1 is an error expression, then it will be returned instead of a - new binary expression. + new unary expression. \param op The op-code of the unary expression. \param e1 The "right" side of the expression. @@ -339,6 +492,29 @@ expr_t *new_binary_expr (int op, expr_t *e1, expr_t *e2); */ expr_t *new_unary_expr (int op, expr_t *e1); +/** Create a new horizontal vector operantion node. + + If \a vec is an error expression, then it will be returned instead of a + new unary expression. + + \param op The op-code of the horizontal operation. + \param vec The expression (must be a vector type) on which to operate. + \param type The result type (must be scalar type) + \return The new unary expression node (::ex_expr_t) if \a e1 + is not an error expression, otherwise \a e1. +*/ +expr_t *new_horizontal_expr (int op, expr_t *vec, struct type_s *type); + +expr_t *new_swizzle_expr (expr_t *src, const char *swizzle); + +expr_t *new_extend_expr (expr_t *src, struct type_s *type, int ext); + +/** Create a new def reference (non-temporary variable) expression node. + + \return The new def reference expression node (::def_t). +*/ +expr_t *new_def_expr (struct def_s *def); + /** Create a new symbol reference (non-temporary variable) expression node. \return The new symbol reference expression node (::symbol_t). @@ -353,7 +529,7 @@ expr_t *new_symbol_expr (struct symbol_s *symbol); \param type The type of the temporary variable. \return The new temporary variable expression node (ex_temp_t). */ -expr_t *new_temp_def_expr (struct type_s *type); +expr_t *new_temp_def_expr (const struct type_s *type); /** Create a new nil expression node. @@ -363,6 +539,14 @@ expr_t *new_temp_def_expr (struct type_s *type); */ expr_t *new_nil_expr (void); +/** Create a new args expression node + + Marker between real parameters and those passed through ... + + \return The new args expression node. +*/ +expr_t *new_args_expr (void); + /** Create a new value expression node. \param value The value to put in the expression node. @@ -384,7 +568,16 @@ expr_t *new_name_expr (const char *name); (expr_t::e::string_val). */ expr_t *new_string_expr (const char *string_val); -const char *expr_string (expr_t *e); +const char *expr_string (expr_t *e) __attribute__((pure)); + +/** Create a new double constant expression node. + + \param double_val The double constant being represented. + \return The new double constant expression node + (expr_t::e::double_val). +*/ +expr_t *new_double_expr (double double_val); +double expr_double (expr_t *e) __attribute__((pure)); /** Create a new float constant expression node. @@ -393,7 +586,7 @@ const char *expr_string (expr_t *e); (expr_t::e::float_val). */ expr_t *new_float_expr (float float_val); -float expr_float (expr_t *e); +float expr_float (expr_t *e) __attribute__((pure)); /** Create a new vector constant expression node. @@ -402,7 +595,7 @@ float expr_float (expr_t *e); (expr_t::e::vector_val). */ expr_t *new_vector_expr (const float *vector_val); -const float *expr_vector (expr_t *e); +const float *expr_vector (expr_t *e) __attribute__((pure)); expr_t *new_vector_list (expr_t *e); /** Create a new entity constant expression node. @@ -415,7 +608,7 @@ expr_t *new_entity_expr (int entity_val); /** Create a new field constant expression node. - \param field_val XXX + \param field_val offset? XXX \param type The type of the field. \param def \return The new field constant expression node @@ -449,25 +642,28 @@ expr_t *new_pointer_expr (int val, struct type_s *type, struct def_s *def); (expr_t::e::quaternion_val). */ expr_t *new_quaternion_expr (const float *quaternion_val); -const float *expr_quaternion (expr_t *e); +const float *expr_quaternion (expr_t *e) __attribute__((pure)); -/** Create a new integer constant expression node. +/** Create a new itn constant expression node. - \param integer_val The integer constant being represented. - \return The new integer constant expression node - (expr_t::e::integer_val). + \param int_val The int constant being represented. + \return The new int constant expression node + (expr_t::e::int_val). */ -expr_t *new_integer_expr (int integer_val); -int expr_integer (expr_t *e); +expr_t *new_int_expr (int int_val); +int expr_int (expr_t *e) __attribute__((pure)); -/** Create a new integer constant expression node. +/** Create a new int constant expression node. - \param uinteger_val The integer constant being represented. - \return The new integer constant expression node - (expr_t::e::integer_val). + \param uint_val The int constant being represented. + \return The new int constant expression node + (expr_t::e::int_val). */ -expr_t *new_uinteger_expr (unsigned uinteger_val); -unsigned expr_uinteger (expr_t *e); +expr_t *new_uint_expr (unsigned uint_val); +unsigned expr_uint (expr_t *e) __attribute__((pure)); + +expr_t *new_long_expr (pr_long_t long_val); +expr_t *new_ulong_expr (pr_ulong_t ulong_val); /** Create a new short constant expression node. @@ -476,14 +672,32 @@ unsigned expr_uinteger (expr_t *e); (expr_t::e::short_val). */ expr_t *new_short_expr (short short_val); -short expr_short (expr_t *e); +short expr_short (expr_t *e) __attribute__((pure)); +unsigned short expr_ushort (expr_t *e) __attribute__((pure)); -/** Check of the expression refers to a constant value. +int expr_integral (expr_t *e) __attribute__((pure)); + +/** Check if the expression refers to a constant value. \param e The expression to check. \return True if the expression is constant. */ -int is_constant (expr_t *e); +int is_constant (expr_t *e) __attribute__((pure)); + +/** Check if the expression refers to a variable. + + \param e The expression to check. + \return True if the expression refers to a variable (def + expression, var symbol expression, or temp expression). +*/ +int is_variable (expr_t *e) __attribute__((pure)); + +/** Check if the expression refers to a selector + + \param e The expression to check. + \return True if the expression is a selector. +*/ +int is_selector (expr_t *e) __attribute__((pure)); /** Return a value expression representing the constant stored in \a e. @@ -500,30 +714,35 @@ expr_t *constant_expr (expr_t *e); \param op The op-code to check. \return True if the op-code is a comparison operator. */ -int is_compare (int op); +int is_compare (int op) __attribute__((const)); /** Check if the op-code is a math operator. \param op The op-code to check. \return True if the op-code is a math operator. */ -int is_math_op (int op); +int is_math_op (int op) __attribute__((const)); /** Check if the op-code is a logic operator. \param op The op-code to check. \return True if the op-code is a logic operator. */ -int is_logic (int op); +int is_logic (int op) __attribute__((const)); -int has_function_call (expr_t *e); +int has_function_call (expr_t *e) __attribute__((pure)); +int is_function_call (expr_t *e) __attribute__((pure)); -int is_string_val (expr_t *e); -int is_float_val (expr_t *e); -int is_vector_val (expr_t *e); -int is_quaternion_val (expr_t *e); -int is_integer_val (expr_t *e); -int is_short_val (expr_t *e); +int is_nil (expr_t *e) __attribute__((pure)); +int is_string_val (expr_t *e) __attribute__((pure)); +int is_float_val (expr_t *e) __attribute__((pure)); +int is_vector_val (expr_t *e) __attribute__((pure)); +int is_quaternion_val (expr_t *e) __attribute__((pure)); +int is_int_val (expr_t *e) __attribute__((pure)); +int is_uint_val (expr_t *e) __attribute__((pure)); +int is_short_val (expr_t *e) __attribute__((pure)); +int is_integral_val (expr_t *e) __attribute__((pure)); +int is_pointer_val (expr_t *e) __attribute__((pure)); /** Create a reference to the global .self entity variable. @@ -547,6 +766,14 @@ expr_t *new_this_expr (void); expr_t *new_ret_expr (struct type_s *type); expr_t *new_alias_expr (struct type_s *type, expr_t *expr); +expr_t *new_offset_alias_expr (struct type_s *type, expr_t *expr, int offset); + +expr_t *new_address_expr (struct type_s *lvtype, expr_t *lvalue, + expr_t *offset); +expr_t *new_assign_expr (expr_t *dst, expr_t *src); +expr_t *new_return_expr (expr_t *ret_val); +expr_t *new_adjstk_expr (int mode, int offset); +expr_t *new_with_expr (int mode, int reg, expr_t *val); /** Create an expression of the correct type that references the specified parameter slot. @@ -557,18 +784,7 @@ expr_t *new_alias_expr (struct type_s *type, expr_t *expr); */ expr_t *new_param_expr (struct type_s *type, int num); -/** Create an expression representing a block copy. - - This is used for structure assignments. - - \param e1 Destination of move. - \param e2 Source of move. - \param type type giving size of move. - \param indirect Move uses dereferenced pointers. - \return A new expression representing the move. -*/ -expr_t *new_move_expr (expr_t *e1, expr_t *e2, struct type_s *type, - int indirect); +expr_t *new_memset_expr (expr_t *dst, expr_t *val, expr_t *count); /** Convert a name to an expression of the appropriate type. @@ -579,18 +795,14 @@ expr_t *new_move_expr (expr_t *e1, expr_t *e2, struct type_s *type, */ void convert_name (expr_t *e); -expr_t *convert_vector (expr_t *e); - expr_t *append_expr (expr_t *block, expr_t *e); +expr_t *prepend_expr (expr_t *block, expr_t *e); expr_t *reverse_expr_list (expr_t *e); void print_expr (expr_t *e); void dump_dot_expr (void *e, const char *filename); -void convert_int (expr_t *e); -void convert_short (expr_t *e); -void convert_short_int (expr_t *e); -void convert_nil (expr_t *e, struct type_s *t); +expr_t *convert_nil (expr_t *e, struct type_s *t); expr_t *test_expr (expr_t *e); void backpatch (ex_list_t *list, expr_t *label); @@ -601,18 +813,22 @@ expr_t *binary_expr (int op, expr_t *e1, expr_t *e2); expr_t *field_expr (expr_t *e1, expr_t *e2); expr_t *asx_expr (int op, expr_t *e1, expr_t *e2); expr_t *unary_expr (int op, expr_t *e); -expr_t *build_function_call (expr_t *fexpr, struct type_s *ftype, +expr_t *build_function_call (expr_t *fexpr, const struct type_s *ftype, expr_t *params); expr_t *function_expr (expr_t *e1, expr_t *e2); struct function_s; expr_t *branch_expr (int op, expr_t *test, expr_t *label); expr_t *goto_expr (expr_t *label); +expr_t *jump_table_expr (expr_t *table, expr_t *index); +expr_t *call_expr (expr_t *func, expr_t *args, struct type_s *ret_type); expr_t *return_expr (struct function_s *f, expr_t *e); +expr_t *at_return_expr (struct function_s *f, expr_t *e); expr_t *conditional_expr (expr_t *cond, expr_t *e1, expr_t *e2); expr_t *incop_expr (int op, expr_t *e, int postop); expr_t *array_expr (expr_t *array, expr_t *index); -expr_t *pointer_expr (expr_t *pointer); -expr_t *address_expr (expr_t *e1, expr_t *e2, struct type_s *t); +expr_t *deref_pointer_expr (expr_t *pointer); +expr_t *offset_pointer_expr (expr_t *pointer, expr_t *offset); +expr_t *address_expr (expr_t *e1, struct type_s *t); expr_t *build_if_statement (int not, expr_t *test, expr_t *s1, expr_t *els, expr_t *s2); expr_t *build_while_statement (int not, expr_t *test, expr_t *statement, @@ -624,10 +840,11 @@ expr_t *build_for_statement (expr_t *init, expr_t *test, expr_t *next, expr_t *break_label, expr_t *continue_label); expr_t *build_state_expr (expr_t *e); expr_t *think_expr (struct symbol_s *think_sym); -expr_t *assign_expr (expr_t *e1, expr_t *e2); +int is_lvalue (const expr_t *expr) __attribute__((pure)); +expr_t *assign_expr (expr_t *dst, expr_t *src); expr_t *cast_expr (struct type_s *t, expr_t *e); -const char *get_op_string (int op); +const char *get_op_string (int op) __attribute__((const)); struct keywordarg_s; struct class_type_s; @@ -640,6 +857,6 @@ expr_t *sizeof_expr (expr_t *expr, struct type_s *type); expr_t *fold_constants (expr_t *e); -//@} +///@} #endif//__expr_h diff --git a/tools/qfcc/include/expr_names.h b/tools/qfcc/include/expr_names.h new file mode 100644 index 000000000..978684073 --- /dev/null +++ b/tools/qfcc/include/expr_names.h @@ -0,0 +1,69 @@ +/* + expr_names.h + + expression construction and manipulations + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/01/7 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +/** \defgroup qfcc_expr Expressions + \ingroup qfcc +*/ +///@{ + +#ifndef EX_EXPR +#define EX_EXPR(expr) +#endif + +EX_EXPR(error) ///< error expression. used to signal an error +EX_EXPR(state) ///< state expression (::ex_state_t) +EX_EXPR(bool) ///< short circuit boolean logic expression (::ex_bool_t) +EX_EXPR(label) ///< goto/branch label (::ex_label_t) +EX_EXPR(labelref) ///< label reference (::ex_labelref_t) +EX_EXPR(block) ///< statement block expression (::ex_block_t) +EX_EXPR(expr) ///< binary expression (::ex_expr_t) +EX_EXPR(uexpr) ///< unary expression (::ex_expr_t) +EX_EXPR(def) ///< non-temporary variable (::def_t) +EX_EXPR(symbol) ///< non-temporary variable (::symbol_t) +EX_EXPR(temp) ///< temporary variable (::ex_temp_t) +EX_EXPR(vector) ///< "vector" expression (::ex_vector_t) +EX_EXPR(selector) ///< selector expression (::ex_selector_t) +EX_EXPR(nil) ///< umm, nil, null. nuff said (0 of any type) +EX_EXPR(value) ///< constant value (::ex_value_t) +EX_EXPR(compound) ///< compound initializer +EX_EXPR(memset) ///< memset needs three params... +EX_EXPR(alias) ///< view expression as different type (::ex_alias_t) +EX_EXPR(address) ///< address of an lvalue expression (::ex_address_t) +EX_EXPR(assign) ///< assignment of src expr to dst expr (::ex_assing_t) +EX_EXPR(branch) ///< branch expression (::ex_branch_t) +EX_EXPR(return) ///< return expression (::ex_return_t) +EX_EXPR(adjstk) ///< stack adjust expression (::ex_adjstk_t) +EX_EXPR(with) ///< with expression (::ex_with_t) +EX_EXPR(args) ///< @args marker in parameter list. no data +EX_EXPR(horizontal) ///< horizontal vector operation (::ex_horzontal_t) +EX_EXPR(swizzle) ///< vector swizzle operation (::ex_swizzle_t) +EX_EXPR(extend) ///< vector extend operation (::ex_extend_t) + +///@} diff --git a/tools/qfcc/include/flow.h b/tools/qfcc/include/flow.h index c605b1d22..eb2d4ba20 100644 --- a/tools/qfcc/include/flow.h +++ b/tools/qfcc/include/flow.h @@ -33,7 +33,7 @@ /** \defgroup qfcc_flow Flow graph analysis \ingroup qfcc */ -//@{ +///@{ struct function_s; struct sblock_s; @@ -44,10 +44,19 @@ typedef struct flowvar_s { struct flowvar_s *next; ///< for ALLOC struct set_s *use; ///< set of statements that use this var struct set_s *define; ///< set of statements that define this var + struct set_s *udchains; ///< set of ud-chains for this var + struct set_s *duchains; ///< set of du-chains for this var struct operand_s *op; ///< an operand using this var int number; ///< number of variable in func's ref list + int flowaddr; ///< psuedo address for local and temp vars } flowvar_t; +typedef struct udchain_s { + int var; + int usest; + int defst; +} udchain_t; + typedef struct flowloop_s { struct flowloop_s *next; unsigned head; @@ -83,12 +92,6 @@ typedef struct flownode_s { struct set_s *in; struct set_s *out; } live_vars; - struct { - struct set_s *use; - struct set_s *def; - struct set_s *in; - struct set_s *out; - } init_vars; struct sblock_s *sblock; ///< original statement block struct dag_s *dag; ///< dag for this node } flownode_t; @@ -101,15 +104,16 @@ typedef struct flowgraph_s { flowedge_t *edges; ///< array of all edges in the graph int num_edges; struct set_s *dfst; ///< edges in the depth-first search tree - int *dfo; ///< depth-first order of nodes + int *depth_first; ///< depth-first order of nodes flowloop_t *loops; ///< linked list of natural loops } flowgraph_t; flowvar_t *flow_get_var (struct operand_s *op); +#define FLOW_OPERANDS 5 void flow_analyze_statement (struct statement_s *s, struct set_s *use, struct set_s *def, struct set_s *kill, - struct operand_s *operands[4]); + struct operand_s *operands[FLOW_OPERANDS]); void flow_data_flow (struct function_s *func); @@ -119,6 +123,6 @@ void dump_dot_flow_live (void *g, const char *filename); void dump_dot_flow_reaching (void *g, const char *filename); void dump_dot_flow_statements (void *g, const char *filename); -//@} +///@} #endif//flow_h diff --git a/tools/qfcc/include/function.h b/tools/qfcc/include/function.h index a1a5c19bf..a6f6d9dca 100644 --- a/tools/qfcc/include/function.h +++ b/tools/qfcc/include/function.h @@ -34,13 +34,16 @@ /** \defgroup qfcc_function Internal function structures. \ingroup qfcc */ -//@{ +///@{ -#include "QF/pr_comp.h" -#include "QF/pr_debug.h" +#include "QF/progs/pr_comp.h" +#include "QF/progs/pr_debug.h" #include "def.h" +// The maximum size of a temp def, return value, or parameter value +#define MAX_DEF_SIZE 32 + /** Represent an overloading of a function. Every function, whether overloaded or not, has an entry in the overloaded @@ -51,9 +54,9 @@ typedef struct overloaded_function_s { const char *name; ///< source level name of function const char *full_name; ///< progs name of function, with type ///< encoding - struct type_s *type; ///< type of this function + const struct type_s *type; ///< type of this function int overloaded; ///< is this function overloaded - string_t file; ///< source file of the function + pr_string_t file; ///< source file of the function int line; ///< source line of this function } overloaded_function_t; @@ -65,20 +68,42 @@ typedef struct function_s { int code; ///< first statement int function_num; int line_info; - int local_defs; - string_t s_file; ///< source file with definition - string_t s_name; ///< name of function in output + int params_start;///< relative to locals space. 0 for v6p + pr_string_t s_file; ///< source file with definition + pr_string_t s_name; ///< name of function in output + const struct type_s *type; ///< function's type without aliases + int temp_reg; ///< base register to use for temp defs int temp_num; ///< number for next temp var - struct def_s *temp_defs[4]; ///< freed temp vars (by size) + struct def_s *temp_defs[MAX_DEF_SIZE];///< freed temp vars (by size) struct def_s *def; ///< output def holding function number struct symbol_s *sym; ///< internal symbol for this function - /** Root scope symbol table of the function. + /** \name Local data space - Sub-scope symbol tables are not directly accessible, but all defs - created in the function's local data space are recorded in the root - scope symbol table's defspace. + The function parameters form the root scope for the function. Its + defspace is separate from the locals defspace so that it can be moved + to the beginning of locals space for v6 progs, and too the end (just + above the stack pointer on entry to the function) for Ruamoko progs. + + The locals scope is a direct child of the parameters scope, and any + sub-scope symbol tables are not directly accessible, but all defs + other than function call arugments created in the function's local + data space are recorded in the root local scope symbol table's + defspace. + + The arguments defspace is not used for v6 progs. It is used as a + highwater allocator for the arguments to all calls made by the + funciton, with the arguments to separate functions overlapping each + other. + + Afther the function has been emitted, locals, arguments and possibly + parameters will be merged into the one defspace. */ - struct symtab_s *symtab; + ///@{ + struct symtab_s *parameters; ///< Root scope symbol table + struct symtab_s *locals; ///< Actual local variables + struct defspace_s *arguments; ///< Space for called function arguments + ///@} + struct symtab_s *label_scope; struct reloc_s *refs; ///< relocation targets for this function struct expr_s *var_init; const char *name; ///< nice name for __PRETTY_FUNCTION__ @@ -92,8 +117,15 @@ typedef struct function_s { struct flowvar_s **vars; int num_vars; ///< total number of variables referenced struct set_s *global_vars;///< set indicating which vars are global + struct set_s *param_vars; ///< set indicating which vars are params + struct set_s *real_statements;///< actual statements for ud-chaining struct statement_s **statements; int num_statements; + int num_ud_chains; + struct udchain_s *ud_chains; + struct udchain_s *du_chains; + int pseudo_addr;///< pseudo address space for flow analysis + struct pseudoop_s *pseudo_ops;///< pseudo operands used by this function } function_t; extern function_t *current_func; @@ -105,9 +137,9 @@ extern function_t *current_func; typedef struct param_s { struct param_s *next; const char *selector; - struct type_s *type; //FIXME redundant - const char *name; //FIXME redundant - struct symbol_s *symbol; + struct type_s *type; + const char *name; + struct symbol_s *symbol; //FIXME what is this for? } param_t; struct expr_s; @@ -118,14 +150,16 @@ param_t *new_param (const char *selector, struct type_s *type, const char *name); param_t *param_append_identifiers (param_t *params, struct symbol_s *idents, struct type_s *type); -param_t *_reverse_params (param_t *params, param_t *next); param_t *reverse_params (param_t *params); +param_t *append_params (param_t *params, param_t *more_params); param_t *copy_params (param_t *params); -struct type_s *parse_params (struct type_s *type, param_t *params); +struct type_s *parse_params (struct type_s *return_type, param_t *params); + param_t *check_params (param_t *params); enum storage_class_e; struct defspace_s; +int value_too_large (struct type_s *val_type) __attribute__((pure)); void make_function (struct symbol_s *sym, const char *nice_name, struct defspace_s *space, enum storage_class_e storage); struct symbol_s *function_symbol (struct symbol_s *sym, @@ -134,18 +168,18 @@ struct expr_s *find_function (struct expr_s *fexpr, struct expr_s *params); function_t *new_function (const char *name, const char *nice_name); void add_function (function_t *f); function_t *begin_function (struct symbol_s *sym, const char *nicename, - struct symtab_s *parent, int far); + struct symtab_s *parent, int far, + enum storage_class_e storage); function_t *build_code_function (struct symbol_s *fsym, struct expr_s *state_expr, struct expr_s *statements); function_t *build_builtin_function (struct symbol_s *sym, - struct expr_s *bi_val, int far); -void build_function (function_t *f); -void finish_function (function_t *f); + struct expr_s *bi_val, int far, + enum storage_class_e storage); void emit_function (function_t *f, struct expr_s *e); int function_parms (function_t *f, byte *parm_size); void clear_functions (void); -//@} +///@} #endif//__function_h diff --git a/tools/qfcc/include/idstuff.h b/tools/qfcc/include/idstuff.h index 82ef617f8..2eadb691b 100644 --- a/tools/qfcc/include/idstuff.h +++ b/tools/qfcc/include/idstuff.h @@ -31,7 +31,7 @@ #ifndef __idstuff_h #define __idstuff_h -#include "QF/pr_comp.h" +#include "QF/progs/pr_comp.h" //XXX eww :/ void PrecacheSound (const char *, int ch); diff --git a/tools/qfcc/include/method.h b/tools/qfcc/include/method.h index ef51e1579..598e151ee 100644 --- a/tools/qfcc/include/method.h +++ b/tools/qfcc/include/method.h @@ -58,6 +58,10 @@ typedef struct methodlist_s { int instance; ///< used only for emitting } methodlist_t; +typedef struct methodset_s { + struct hashtab_s *tab; +} methodset_t; + typedef struct keywordarg_s { // the first two fields match the first two fields of param_t in // functionl.h @@ -80,7 +84,12 @@ struct symbol_s *method_symbol (struct class_type_s *class_type, void method_set_param_names (method_t *dst, method_t *src); methodlist_t *new_methodlist (void); -void copy_methods (methodlist_t *dst, methodlist_t *src); +methodset_t *new_methodset (void); +void methodset_add_methods (methodset_t *methodset, methodlist_t *methods); +int methodset_contains_method (methodset_t *methodset, method_t *method); +//NOTE frees the source list and any methods not copied +void merge_method_lists (methodlist_t *dst, methodlist_t *src); +void copy_methods (methodlist_t *dst, methodlist_t *src, methodset_t *except); int method_compare (method_t *m1, method_t *m2); keywordarg_t *new_keywordarg (const char *selector, struct expr_s *expr); @@ -89,6 +98,9 @@ keywordarg_t *copy_keywordargs (const keywordarg_t *kwargs); struct expr_s *send_message (int super); method_t *find_method (const char *sel_name); +method_t *methodlist_find_method (methodlist_t *methodlist, + selector_t *selector, int instance) + __attribute__((pure)); void selector_name (struct dstring_s *sel_id, keywordarg_t *selector); void method_types (struct dstring_s *sel_types, method_t *method); diff --git a/tools/qfcc/include/obj_file.h b/tools/qfcc/include/obj_file.h index cd9cd45f6..4c4eee75d 100644 --- a/tools/qfcc/include/obj_file.h +++ b/tools/qfcc/include/obj_file.h @@ -34,10 +34,10 @@ /** \defgroup qfcc_qfo Object file functions \ingroup qfcc */ -//@{ +///@{ -#include "QF/pr_comp.h" -#include "QF/pr_debug.h" +#include "QF/progs/pr_comp.h" +#include "QF/progs/pr_debug.h" #include "QF/quakeio.h" /** Identifier string for qfo object files (includes terminating nul) @@ -49,7 +49,7 @@ \hideinitializer */ -#define QFO_VERSION 0x00001006 +#define QFO_VERSION 0x00001007 /** Header block of QFO object files. The sections of the object file come immediately after the header, and are always in the order given by @@ -60,14 +60,16 @@ */ typedef struct qfo_header_s { int8_t qfo[4]; ///< identifier string (includes nul) (#QFO) - pr_int_t version; ///< QFO format version (#QFO_VERSION) - pr_int_t num_spaces; - pr_int_t num_relocs; ///< number of relocation records - pr_int_t num_defs; ///< number of def records - pr_int_t num_funcs; ///< number of function records - pr_int_t num_lines; ///< number of line records - pr_int_t num_loose_relocs; ///< number of loose relocation records + pr_uint_t version; ///< QFO format version (#QFO_VERSION) + pr_uint_t num_spaces; + pr_uint_t num_relocs; ///< number of relocation records + pr_uint_t num_defs; ///< number of def records + pr_uint_t num_funcs; ///< number of function records + pr_uint_t num_lines; ///< number of line records + pr_uint_t num_loose_relocs; ///< number of loose relocation records ///< (included in num_relocs) + pr_uint_t progs_version; ///< version of compatible VM + pr_uint_t reserved[3]; } qfo_header_t; typedef enum qfos_type_e { @@ -77,41 +79,43 @@ typedef enum qfos_type_e { qfos_string, ///< strings. char data qfos_entity, ///< entity field defs. no data qfos_type, ///< type encodings + qfos_debug, ///< debug data } qfos_type_t; /** Representation of a space in the object file. */ typedef struct qfo_space_s { pr_int_t type; ///< code, string, data, entity... - pr_int_t defs; ///< index of first def - pr_int_t num_defs; ///< zero for code or string spaces - pr_int_t data; ///< byte offset in qfo + pr_uint_t defs; ///< index of first def + pr_uint_t num_defs; ///< zero for code or string spaces + pr_uint_t data; ///< byte offset in qfo pr_uint_t data_size; ///< in elements. zero for entity spaces - pr_int_t id; - pr_int_t reserved[2]; + pr_uint_t id; + pr_uint_t alignment; ///< min alignment for space (1<spaces[s].d.data[o].t##_var) +#define QFO_var(q, s, t, o) (*(pr_##t##_t *) &(q)->spaces[s].data[o]) + +/** Access a double variable in the object file. Can be assigned to. + + \par QC type: + \c double + \param q pointer to ::qfo_t struct + \param s space index + \param o offset into object file data space + \return double lvalue + + \hideinitializer +*/ +#define QFO_DOUBLE(q, s, o) (*(double *) ((q)->spaces[s].data + o)) /** Access a float variable in the object file. Can be assigned to. @@ -318,10 +338,10 @@ enum { */ #define QFO_FLOAT(q, s, o) QFO_var (q, s, float, o) -/** Access a integer variable in the object file. Can be assigned to. +/** Access a int variable in the object file. Can be assigned to. \par QC type: - \c integer + \c int \param q pointer to ::qfo_t struct \param s space index \param o offset into object file data space @@ -329,7 +349,7 @@ enum { \hideinitializer */ -#define QFO_INT(q, s, o) QFO_var (q, s, integer, o) +#define QFO_INT(q, s, o) QFO_var (q, s, int, o) /** Access a vector variable in the object file. Can be assigned to. @@ -351,7 +371,7 @@ enum { \param q pointer to ::qfo_t struct \param s space index \param o offset into object file data space - \return string_t lvalue + \return pr_string_t lvalue \hideinitializer */ @@ -361,15 +381,14 @@ enum { \param q pointer to ::qfo_t struct \param s space index - \param s offset into object file string space \return (char *) \hideinitializer */ -#define QFO_GETSTR(q, s) ((q)->spaces[qfo_strings_space].d.strings + (s)) +#define QFO_GETSTR(q, s) ((q)->spaces[qfo_strings_space].strings + (s)) #define QFO_TYPE(q, t) ((qfot_type_t *) (char *) \ - ((q)->spaces[qfo_type_space].d.data + (t))) + ((q)->spaces[qfo_type_space].data + (t))) /** Retrieve a type string from the object file, converting it to a C string. @@ -385,7 +404,7 @@ enum { #define QFO_TYPEMETA(q, t) QFO_INT (q, qfo_type_space, (t) + 0) #define QFO_TYPETYPE(q, t) QFO_INT (q, qfo_type_space, (t) + 3) -#define QFO_STATEMENT(q, s) ((q)->spaces[qfo_code_space].d.code + (s)) +#define QFO_STATEMENT(q, s) ((q)->spaces[qfo_code_space].code + (s)) /** Access a string global, converting it to a C string. @@ -405,7 +424,7 @@ enum { \param q pointer to ::qfo_t struct \param s space index \param o offset into object file data space - \return func_t lvalue + \return pr_func_t lvalue \hideinitializer */ @@ -423,7 +442,7 @@ enum { \hideinitializer */ -#define QFO_POINTER(q, s, t, o) ((t *)(char *)&QFO_var (q, s, integer, o)) +#define QFO_POINTER(q, s, t, o) ((t *)(char *)&QFO_var (q, s, int, o)) /** Access a structure variable in the object file. Can be assigned to. @@ -439,11 +458,11 @@ enum { */ #define QFO_STRUCT(q, s, t, o) (*QFO_POINTER (q, s, t, o)) -//@} +///@} /** \addtogroup qfcc_qfo */ -//@{ +///@{ struct pr_info_s; @@ -472,7 +491,7 @@ qfo_t *qfo_read (QFile *file); */ qfo_t *qfo_open (const char *filename); -dprograms_t *qfo_to_progs (qfo_t *qfo, int *size); +dprograms_t *qfo_to_progs (qfo_t *in_qfo, int *size); pr_debug_header_t *qfo_to_sym (qfo_t *qfo, int *size); /** Create a new ::qfo_t struct @@ -485,6 +504,8 @@ qfo_t *qfo_new (void); */ void qfo_delete (qfo_t *qfo); -//@} +__attribute__((const)) int qfo_log2 (pr_uint_t x); + +///@} #endif//__obj_file_h diff --git a/tools/qfcc/include/obj_type.h b/tools/qfcc/include/obj_type.h index 1e559f45e..c3d9cb6e7 100644 --- a/tools/qfcc/include/obj_type.h +++ b/tools/qfcc/include/obj_type.h @@ -31,82 +31,12 @@ #ifndef __obj_type_h #define __obj_type_h -/** \defgroup qfcc_qfo_type Object file type encoding - \ingroup qfcc_qfo - - All \c pointer_t \c type fields are pointers within the type qfo_space. -*/ -//@{ - -#include "QF/pr_comp.h" +#include "QF/progs/pr_type.h" #include "type.h" -typedef struct qfot_fldptr_s { - pr_int_t type; ///< ev_field or ev_pointer - pointer_t aux_type; ///< referenced type -} qfot_fldptr_t; - -typedef struct qfot_func_s { - pr_int_t type; ///< always ev_func - pointer_t return_type; ///< return type of the function - pr_int_t num_params; ///< ones compliment count of the - ///< parameters. -ve values indicate the - ///< number of real parameters before the - ///< ellipsis - pointer_t param_types[1]; ///< variable length list of parameter - ///< types -} qfot_func_t; - -typedef struct qfot_var_s { - pointer_t type; ///< type of field or self reference for - ///< enum - string_t name; ///< name of field/enumerator - pr_int_t offset; ///< value for enum, 0 for union -} qfot_var_t; - -typedef struct qfot_struct_s { - string_t tag; ///< struct/union/enum tag - pr_int_t num_fields; ///< number of fields/enumerators - qfot_var_t fields[1]; ///< variable length list of - ///< fields/enumerators -} qfot_struct_t; - -typedef struct qfot_array_s { - pointer_t type; ///< element type - pr_int_t base; ///< start index of array - pr_int_t size; ///< number of elements in array -} qfot_array_t; - -/** QFO type encoding. - - \note As this holds a union of all type representations, and those - representations may contain variable arrays, sizeof() will return only - one, rather useless, value. It is also not suitable for direct use in - arrays. -*/ -typedef struct qfot_type_s { - pr_int_t ty; ///< meta type: ty_meta_e - pr_int_t size; ///< total word size of this encoding - string_t encoding; ///< Objective-QC encoding - union { - pr_int_t type; ///< basic type: etype_t - qfot_fldptr_t fldptr; ///< ty_none, ev_pointer/ev_field - qfot_func_t func; ///< ty_none, ev_func - qfot_struct_t strct; ///< ty_struct/ty_union/ty_enum - qfot_array_t array; ///< ty_array - pointer_t class; ///< ty_class - } t; -} qfot_type_t; - -typedef struct qfot_type_encodings_s { - pointer_t types; - pr_int_t size; -} qfot_type_encodings_t; - +struct defspace_s; struct type_s; -struct def_s *qfo_encode_type (struct type_s *type); - -//@} +struct def_s *qfo_encode_type (struct type_s *type, struct defspace_s *space); #endif//__obj_type_h diff --git a/tools/qfcc/include/opcodes.h b/tools/qfcc/include/opcodes.h index 779370340..055066f4b 100644 --- a/tools/qfcc/include/opcodes.h +++ b/tools/qfcc/include/opcodes.h @@ -31,25 +31,15 @@ #ifndef __opcodes_h #define __opcodes_h -extern struct opcode_s *op_done; -extern struct opcode_s *op_return; -extern struct opcode_s *op_return_v; -extern struct opcode_s *op_if; -extern struct opcode_s *op_ifnot; -extern struct opcode_s *op_ifbe; -extern struct opcode_s *op_ifb; -extern struct opcode_s *op_ifae; -extern struct opcode_s *op_ifa; -extern struct opcode_s *op_state; -extern struct opcode_s *op_state_f; -extern struct opcode_s *op_goto; -extern struct opcode_s *op_jump; -extern struct opcode_s *op_jumpb; +#include "QF/progs/pr_comp.h" + +typedef struct instruction_s instruction_t; struct operand_s; - -struct opcode_s *opcode_find (const char *name, struct operand_s *op_a, - struct operand_s *op_b, struct operand_s *op_c); +pr_ushort_t opcode_get (instruction_t *inst) __attribute__((pure)); +instruction_t *opcode_find (const char *name, struct operand_s *op_a, + struct operand_s *op_b, struct operand_s *op_c); void opcode_init (void); +void opcode_print_statement (pr_uint_t addr, dstatement_t *st); #endif//__opcodes_h diff --git a/tools/qfcc/include/options.h b/tools/qfcc/include/options.h index 4377dab97..2dc5413a5 100644 --- a/tools/qfcc/include/options.h +++ b/tools/qfcc/include/options.h @@ -34,81 +34,93 @@ #include "QF/qtypes.h" typedef struct { - qboolean cow; // Turn constants into variables if written to - qboolean crc; // Write progsdef.h crc to progs.dat - qboolean debug; // Generate debug info for the engine - qboolean short_circuit; // short circuit logic for && and || - qboolean optimize; // perform optimizations - qboolean fast_float; // use floats directly in ifs - qboolean vector_calls; // use floats instead of vectors for constant function args - qboolean local_merging; // merge function locals into one block + bool cow; // Turn constants into variables if written to + bool crc; // Write progsdef.h crc to progs.dat + bool debug; // Generate debug info for the engine + bool short_circuit; // short circuit logic for && and || + bool optimize; // perform optimizations + bool fast_float; // use floats directly in ifs + bool vector_calls; // use floats instead of vectors for constant function args + bool local_merging; // merge function locals into one block unsigned progsversion; // Progs version to generate code for - qboolean vector_components; // add *_[xyz] symbols for vectors - qboolean ifstring; // expand if (str) to if (str != "") + bool vector_components; // add *_[xyz] symbols for vectors + bool ifstring; // expand if (str) to if (str != "") + bool const_initializers; // initialied globals are constant + bool promote_float; // promote float through ... } code_options_t; typedef struct { - qboolean promote; // Promote warnings to errors - qboolean cow; // Warn on copy-on-write detection - qboolean undefined_function; // Warn on undefined function use - qboolean uninited_variable; // Warn on use of uninitialized vars - qboolean vararg_integer; // Warn on passing an integer to vararg func - qboolean integer_divide; // Warn on integer constant division - qboolean interface_check; // Warn for methods not in interface - qboolean unused; // Warn on unused local variables - qboolean executable; // Warn on expressions with no effect - qboolean traditional; // Warn on bogus constructs allowed by qcc - qboolean precedence; // Warn on precedence issues - qboolean initializer; // Warn on excessive initializer elements - qboolean unimplemented; // Warn on unimplemented class methods - qboolean redeclared; // Warn on redeclared local variables - qboolean enum_switch; // Warn on unhandled enum values in switch + int vector_mult; // operation for vector * vector +} math_options_t; + +typedef struct { + bool promote; // Promote warnings to errors + bool cow; // Warn on copy-on-write detection + bool undefined_function; // Warn on undefined function use + bool uninited_variable; // Warn on use of uninitialized vars + bool vararg_integer; // Warn on passing an integer to vararg func + bool integer_divide; // Warn on integer constant division + bool interface_check; // Warn for methods not in interface + bool unused; // Warn on unused local variables + bool executable; // Warn on expressions with no effect + bool traditional; // Warn on bogus constructs allowed by qcc + bool precedence; // Warn on precedence issues + bool initializer; // Warn on excessive initializer elements + bool unimplemented; // Warn on unimplemented class methods + bool redeclared; // Warn on redeclared local variables + bool enum_switch; // Warn on unhandled enum values in switch } warn_options_t; typedef struct { - qboolean promote; // Promote notices to warnings - qboolean silent; // don't even bother (overrides promote) + bool promote; // Promote notices to warnings + bool silent; // don't even bother (overrides promote) } notice_options_t; typedef struct { - qboolean initial; - qboolean thread; - qboolean dead; - qboolean final; - qboolean dags; - qboolean expr; - qboolean reaching; - qboolean live; - qboolean flow; - qboolean post; + bool promote; // Promote bugs to internal errors + bool silent; // don't even bother (overrides promote) +} bug_options_t; + +typedef struct { + bool initial; + bool thread; + bool dead; + bool final; + bool dags; + bool expr; + bool statements; + bool reaching; + bool live; + bool flow; + bool post; } blockdot_options_t; typedef struct { - code_options_t code; // Code generation options - warn_options_t warnings; // Warning options + code_options_t code; // Code generation options + math_options_t math; // Various math options + warn_options_t warnings; // Warning options notice_options_t notices; // Notice options + bug_options_t bug; // Bug options blockdot_options_t block_dot; // Statement block flow diagrams - int verbosity; // 0=silent, goes up to 2 currently - qboolean single_cpp; // process progs.src into a series of + int verbosity; // 0=silent, goes up to 2 currently + bool single_cpp; // process progs.src into a series of // #include directives and then compile // that - qboolean no_default_paths; // no default -I or -L - qboolean save_temps; // save temporary files - qboolean files_dat; // generate files.dat - qboolean frames_files; // generate .frame files - qboolean progdefs_h; // generate progdefs.h - qboolean qccx_escapes; // use qccx escapes instead of standard C - int traditional; // behave more like qcc - qboolean advanced; // behold the power of Ruamoko - qboolean compile; // serparate compilation mode - qboolean partial_link; // partial linking - qboolean preprocess_only;// run only cpp, don't compile - qboolean gzip; // compress qfo files when writing - int strip_path; // number of leading path elements to strip - // from source file names - const char *output_file; - const char *debug_file; + bool no_default_paths; // no default -I or -L + bool save_temps; // save temporary files + bool files_dat; // generate files.dat + bool frames_files; // generate .frame files + bool progdefs_h; // generate progdefs.h + bool qccx_escapes; // use qccx escapes instead of standard C + int traditional; // behave more like qcc + int advanced; // behold the power of Ruamoko + bool compile; // serparate compilation mode + bool partial_link; // partial linking + bool preprocess_only; // run only cpp, don't compile + bool gzip; // compress qfo files when writing + const char *output_file; + const char *debug_file; } options_t; extern options_t options; diff --git a/tools/qfcc/include/pragma.h b/tools/qfcc/include/pragma.h index 1f6726f28..2fad5a357 100644 --- a/tools/qfcc/include/pragma.h +++ b/tools/qfcc/include/pragma.h @@ -33,10 +33,11 @@ /** \defgroup qfcc_pragma pragma handling \ingroup qfcc */ -//@{ +///@{ -void pragma (const char *id); +void pragma_process (void); +void pragma_add_arg (const char *id); -//@} +///@} #endif//pragma_h diff --git a/tools/qfcc/include/qfcc.h b/tools/qfcc/include/qfcc.h index 3aa464185..01263eea6 100644 --- a/tools/qfcc/include/qfcc.h +++ b/tools/qfcc/include/qfcc.h @@ -34,17 +34,18 @@ */ #include -#include "QF/pr_comp.h" +#include "QF/darray.h" +#include "QF/progs/pr_comp.h" /** \defgroup qfcc_general General functions \ingroup qfcc */ -//@{ +///@{ typedef struct srcline_s srcline_t; struct srcline_s { srcline_t *next; - string_t source_file; + pr_string_t source_file; int source_line; }; @@ -68,12 +69,17 @@ typedef struct pr_info_s { struct defspace_s *entity_data; ///< entity field address space. no ///< data is stored in the progs file struct defspace_s *type_data; ///< encoded type information. + struct defspace_s *debug_data; ///< additional debug data. + struct strpool_s *comp_file_set; + struct DARRAY_TYPE (const char *) comp_files; + const char *comp_dir; + const char *unit_name; struct symtab_s *symtab; struct symtab_s *entity_fields; srcline_t *srcline_stack; - string_t source_file; + pr_string_t source_file; int source_line; int error_count; @@ -91,23 +97,25 @@ typedef struct pr_info_s { extern pr_info_t pr; #define GETSTR(s) (pr.strings->strings + (s)) -#define D_var(t, d) ((d)->space->data[(d)->offset].t##_var) +#define D_PACKED(t,d) (*(t *) &(d)->space->data[(d)->offset]) +#define D_var(t, d) D_PACKED (pr_##t##_t, d) +#define D_DOUBLE(d) D_var (double, d) #define D_FLOAT(d) D_var (float, d) -#define D_INT(d) D_var (integer, d) -#define D_VECTOR(d) D_var (vector, d) -#define D_QUAT(d) D_var (quat, d) +#define D_INT(d) D_var (int, d) +#define D_VECTOR(d) (&D_var (float, d)) +#define D_QUAT(d) (&D_var (float, d)) #define D_STRING(d) D_var (string, d) #define D_GETSTR(d) GETSTR (D_STRING (d)) #define D_FUNCTION(d) D_var (func, d) -#define D_POINTER(t,d) ((t *)((d)->space->data + (d)->offset)) +#define D_POINTER(t,d) (&D_PACKED (t, d)) #define D_STRUCT(t,d) (*D_POINTER (t, d)) -#define G_POINTER(s,t,o) ((t *)((s)->data + o)) -#define G_STRUCT(s,t,o) (*G_POINTER (s, t, o)) +#define Q_POINTER(s,t,o) ((t *)((s)->data + o)) +#define Q_STRUCT(s,t,o) (*Q_POINTER (s, t, o)) #define POINTER_OFS(s,p) ((pr_type_t *) (p) - (s)->data) -const char *strip_path (const char *filename); +const char *file_basename (const char *filename, int keepdot) __attribute__((pure)); extern FILE *qc_yyin; extern FILE *qp_yyin; @@ -132,6 +140,6 @@ char *fix_backslash (char *path); */ #define RUP(x,a) (((x) + ((a) - 1)) & ~((a) - 1)) -//@} +///@} #endif//__qfcc_h diff --git a/tools/qfcc/include/qfprogs.h b/tools/qfcc/include/qfprogs.h index 09fade09d..731a61194 100644 --- a/tools/qfcc/include/qfprogs.h +++ b/tools/qfcc/include/qfprogs.h @@ -55,8 +55,11 @@ struct dfunction_s *func_find (int st_num); void dump_strings (struct progs_s *pr); void qfo_globals (struct qfo_s *qfo); +void qfo_fields (struct qfo_s *qfo); void qfo_functions (struct qfo_s *qfo); +void qfo_lines (struct qfo_s *qfo); void qfo_relocs (struct qfo_s *qfo); +void qfo_strings (struct qfo_s *qfo); void qfo_types (struct qfo_s *qfo); #endif//__qfprogs_h diff --git a/tools/qfcc/include/reloc.h b/tools/qfcc/include/reloc.h index 2272dab5d..08e00f8d1 100644 --- a/tools/qfcc/include/reloc.h +++ b/tools/qfcc/include/reloc.h @@ -68,7 +68,7 @@ typedef enum { */ typedef struct reloc_s { struct reloc_s *next; ///< next reloc in reloc chain - struct ex_label_s *label; ///< instruction label for *_op relocs + const struct ex_label_s *label; ///< instruction label for *_op relocs struct defspace_s *space; ///< the space containing the location in ///< need of adjustment for def_* relocations ///< (op_* relocations always use the code @@ -77,8 +77,8 @@ typedef struct reloc_s { ///< adjustment reloc_type type; ///< type type of relocation to perform int line; ///< current source line when creating reloc - string_t file; ///< current source file when creating reloc - void *return_address; ///< for debugging + pr_string_t file; ///< current source file when creating reloc + const void *return_address; ///< for debugging } reloc_t; struct statement_s; @@ -147,7 +147,7 @@ void reloc_op_def_ofs (struct def_s *def, int offset, int field); adjusted. As the def's space and offset will be copied into the relocation record, a dummy def may be used. */ -void reloc_def_def (struct def_s *def, struct def_s *location); +void reloc_def_def (struct def_s *def, const struct def_s *location); /** Create a relocation record for a data location referencing a def. @@ -162,7 +162,7 @@ void reloc_def_def (struct def_s *def, struct def_s *location); adjusted. As the def's space and offset will be copied into the relocation record, a dummy def may be used. */ -void reloc_def_def_ofs (struct def_s *def, struct def_s *location); +void reloc_def_def_ofs (struct def_s *def, const struct def_s *location); /** Create a relocation record for a data location referencing a function. @@ -177,7 +177,7 @@ void reloc_def_def_ofs (struct def_s *def, struct def_s *location); adjusted. As the def's space and offset will be copied into the relocation record, a dummy def may be used. */ -void reloc_def_func (struct function_s *func, struct def_s *location); +void reloc_def_func (struct function_s *func, const struct def_s *location); /** Create a relocation record for a data location referencing a string. @@ -191,7 +191,7 @@ void reloc_def_func (struct function_s *func, struct def_s *location); adjusted. As the def's space and offset will be copied into the relocation record, a dummy def may be used. */ -void reloc_def_string (struct def_s *location); +void reloc_def_string (const struct def_s *location); /** Create a relocation record for a data location referencing a field. @@ -206,7 +206,7 @@ void reloc_def_string (struct def_s *location); adjusted. As the def's space and offset will be copied into the relocation record, a dummy def may be used. */ -void reloc_def_field (struct def_s *def, struct def_s *location); +void reloc_def_field (struct def_s *def, const struct def_s *location); /** Create a relocation record for a data location referencing a field. @@ -221,7 +221,7 @@ void reloc_def_field (struct def_s *def, struct def_s *location); adjusted. As the def's space and offset will be copied into the relocation record, a dummy def may be used. */ -void reloc_def_field_ofs (struct def_s *def, struct def_s *location); +void reloc_def_field_ofs (struct def_s *def, const struct def_s *location); /** Create a relocation record for a data location referencing an instruction. @@ -237,10 +237,13 @@ void reloc_def_field_ofs (struct def_s *def, struct def_s *location); adjusted. As the def's space and offset will be copied into the relocation record, a dummy def may be used. */ -void reloc_def_op (struct ex_label_s *label, struct def_s *location); +void reloc_def_op (const struct ex_label_s *label, + const struct def_s *location); void reloc_attach_relocs (reloc_t *relocs, reloc_t **location); +extern const char * const reloc_name[]; + ///@} #endif//__reloc_h diff --git a/tools/qfcc/include/statements.h b/tools/qfcc/include/statements.h index 75be49d79..fab423f0c 100644 --- a/tools/qfcc/include/statements.h +++ b/tools/qfcc/include/statements.h @@ -30,7 +30,7 @@ #ifndef statement_h #define statement_h -#include "QF/pr_comp.h" +#include "QF/progs/pr_comp.h" typedef enum { op_def, @@ -38,61 +38,89 @@ typedef enum { op_label, op_temp, op_alias, + op_nil, + op_pseudo, } op_type_e; -typedef struct { +struct expr_s; + +typedef struct tempop_s { struct def_s *def; + int offset; struct type_s *type; struct flowvar_s *flowvar; struct daglabel_s *daglabel; struct operand_s *alias; struct operand_s *alias_ops; int users; + int flowaddr; ///< "address" of temp in flow analysis, != 0 } tempop_t; +typedef struct pseudoop_s { + struct pseudoop_s *next; + const char *name; + struct flowvar_s *flowvar; + void (*uninitialized) (struct expr_s *expr, struct pseudoop_s *op); +} pseudoop_t; + typedef struct operand_s { struct operand_s *next; op_type_e op_type; - etype_t type; ///< possibly override def's type + struct type_s *type; ///< possibly override def's/nil's type int size; ///< for structures + int width; ///< for SIMD selection + struct expr_s *expr; ///< expression generating this operand + void *return_addr; ///< who created this operand union { struct def_s *def; struct ex_value_s *value; struct ex_label_s *label; tempop_t tempop; struct operand_s *alias; - } o; + pseudoop_t *pseudoop; + }; } operand_t; /** Overall type of statement. Statement types are broken down into expressions (binary and unary, includes address and pointer dereferencing (read)), assignment, pointer - assignment (write to dereference pointer), move (special case of pointer - assignment), state, function related (call, rcall, return and done), and - flow control (conditional branches, goto, jump (single pointer and jump - table)). + assignment (write to dereference pointer), move (special case of + assignment), pointer move (special case of pointer assignment), state, + function related (call, rcall, return and done), and flow control + (conditional branches, goto, jump (single pointer and jump table)). */ typedef enum { st_none, ///< not a (valid) statement. Used in dags. st_expr, ///< c = a op b; or c = op a; st_assign, ///< b = a st_ptrassign, ///< *b = a; or *(b + c) = a; - st_move, ///< memcpy (c, a, b); + st_move, ///< memcpy (c, a, b); c and a are direct def references + st_ptrmove, ///< memcpy (c, a, b); c and a are pointers + st_memset, ///< memset (c, a, b); c is direct def reference + st_ptrmemset, ///< memset (c, a, b); c is pointer st_state, ///< state (a, b); or state (a, b, c) st_func, ///< call, rcall or return/done st_flow, ///< if/ifa/ifae/ifb/ifbe/ifnot or goto or jump/jumpb + st_address, ///< lea } st_type_t; typedef struct statement_s { struct statement_s *next; st_type_t type; + int number; ///< number of this statement in function const char *opcode; operand_t *opa; operand_t *opb; operand_t *opc; struct expr_s *expr; ///< source expression for this statement - int number; ///< number of this statement in function + operand_t *use; ///< list of auxiliary operands used + operand_t *def; ///< list of auxiliary operands defined + operand_t *kill; ///< list of auxiliary operands killed + int first_use; + int num_use; + int first_def; + int num_def; } statement_t; typedef struct sblock_s { @@ -111,28 +139,40 @@ struct expr_s; struct type_s; struct dstring_s; -const char *optype_str (op_type_e type); +extern const char * const op_type_names[]; +extern const char * const st_type_names[]; -operand_t *def_operand (struct def_s *def, struct type_s *type); -operand_t *value_operand (struct ex_value_s *value); -operand_t *temp_operand (struct type_s *type); -operand_t *alias_operand (etype_t type, operand_t *op); +const char *optype_str (op_type_e type) __attribute__((const)); + +operand_t *nil_operand (struct type_s *type, struct expr_s *expr); +operand_t *def_operand (struct def_s *def, struct type_s *type, + struct expr_s *expr); +operand_t *return_operand (struct type_s *type, struct expr_s *expr); +operand_t *value_operand (struct ex_value_s *value, struct expr_s *expr); +int tempop_overlap (tempop_t *t1, tempop_t *t2) __attribute__((pure)); +operand_t *temp_operand (struct type_s *type, struct expr_s *expr); +int tempop_visit_all (tempop_t *tempop, int overlap, + int (*visit) (tempop_t *, void *), void *data); +operand_t *alias_operand (struct type_s *type, operand_t *op, + struct expr_s *expr); +operand_t *label_operand (struct expr_s *label); void free_operand (operand_t *op); sblock_t *new_sblock (void); statement_t *new_statement (st_type_t type, const char *opcode, struct expr_s *expr); -int statement_is_cond (statement_t *s); -int statement_is_goto (statement_t *s); -int statement_is_jumpb (statement_t *s); -int statement_is_call (statement_t *s); -int statement_is_return (statement_t *s); -sblock_t *statement_get_target (statement_t *s); +int statement_is_cond (statement_t *s) __attribute__((pure)); +int statement_is_goto (statement_t *s) __attribute__((pure)); +int statement_is_jumpb (statement_t *s) __attribute__((pure)); +int statement_is_call (statement_t *s) __attribute__((pure)); +int statement_is_return (statement_t *s) __attribute__((pure)); +sblock_t *statement_get_target (statement_t *s) __attribute__((pure)); sblock_t **statement_get_targetlist (statement_t *s); void sblock_add_statement (sblock_t *sblock, statement_t *statement); sblock_t *make_statements (struct expr_s *expr); void statements_count_temps (sblock_t *sblock); +void print_operand (operand_t *op); void print_statement (statement_t *s); void dump_dot_sblock (void *data, const char *fname); void dot_sblock (struct dstring_s *dstr, sblock_t *sblock, int blockno); diff --git a/tools/qfcc/include/strpool.h b/tools/qfcc/include/strpool.h index e6e065cb8..fbb4bc049 100644 --- a/tools/qfcc/include/strpool.h +++ b/tools/qfcc/include/strpool.h @@ -42,6 +42,7 @@ strpool_t *strpool_new (void); strpool_t *strpool_build (const char *strings, int size); void strpool_delete (strpool_t *strpool); int strpool_addstr (strpool_t *strpool, const char *str); +int strpool_findstr (strpool_t *strpool, const char *str); /** Smart strdup. @@ -53,6 +54,8 @@ int strpool_addstr (strpool_t *strpool, const char *str); */ const char *save_string (const char *str); +const char *save_cwd (void); + const char *make_string (char *token, char **end); const char *html_string (const char *str); diff --git a/tools/qfcc/include/struct.h b/tools/qfcc/include/struct.h index 8f50845cf..3b1d14edb 100644 --- a/tools/qfcc/include/struct.h +++ b/tools/qfcc/include/struct.h @@ -43,10 +43,15 @@ typedef struct { void (*emit) (struct def_s *def, void *data, int index); } struct_def_t; +struct symbol_s *find_handle (struct symbol_s *tag, struct type_s *type); + +struct symtab_s *start_struct (int *su, struct symbol_s *tag, + struct symtab_s *parent); struct symbol_s *find_struct (int su, struct symbol_s *tag, struct type_s *type); struct symbol_s *build_struct (int su, struct symbol_s *tag, - struct symtab_s *symtab, struct type_s *type); + struct symtab_s *symtab, struct type_s *type, + int base); struct symbol_s *find_enum (struct symbol_s *tag); struct symtab_s *start_enum (struct symbol_s *enm); struct symbol_s *finish_enum (struct symbol_s *sym); @@ -59,6 +64,7 @@ struct symbol_s *make_structure (const char *name, int su, struct_def_t *defs, struct type_s *type); struct def_s * emit_structure (const char *name, int su, struct_def_t *defs, struct type_s *type, void *data, + struct defspace_s *space, enum storage_class_e storage); #endif//__struct_h diff --git a/tools/qfcc/include/symtab.h b/tools/qfcc/include/symtab.h index 0a89a39e7..8d8943ef7 100644 --- a/tools/qfcc/include/symtab.h +++ b/tools/qfcc/include/symtab.h @@ -39,23 +39,31 @@ enum storage_class_e; /** \defgroup qfcc_symtab Symbol Table Management \ingroup qfcc */ -//@{ +///@{ typedef enum vis_e { vis_public, vis_protected, vis_private, + vis_anonymous, } vis_t; typedef enum { + sy_name, ///< just a name (referent tbd) sy_var, ///< symbol refers to a variable sy_const, ///< symbol refers to a constant sy_type, ///< symbol refers to a type sy_expr, ///< symbol refers to an expression sy_func, ///< symbol refers to a function sy_class, ///< symbol refers to a class + sy_convert, ///< symbol refers to a conversion function } sy_type_e; +typedef struct symconv_s { + struct expr_s *(*conv) (struct symbol_s *symbol, void *data); + void *data; +} symconv_t; + typedef struct symbol_s { struct symbol_s *next; ///< chain of symbols in symbol table struct symtab_s *table; ///< symbol table that owns this symbol @@ -64,21 +72,26 @@ typedef struct symbol_s { sy_type_e sy_type; ///< symbol type struct type_s *type; ///< type of object to which symbol refers struct param_s *params; ///< the parameters if a function + unsigned no_auto_init; ///< skip for non-designated initializers union { int offset; ///< sy_var (in a struct/union) struct def_s *def; ///< sy_var struct ex_value_s *value; ///< sy_const struct expr_s *expr; ///< sy_expr struct function_s *func; ///< sy_func + symconv_t convert; ///< sy_convert } s; } symbol_t; typedef enum { stab_global, ///< global (many symbols) + stab_param, ///< local (few symbols: func) stab_local, ///< local (few symbols: func) + stab_ivars, stab_struct, stab_union, stab_enum, + stab_label, } stab_type_e; typedef struct symtab_s { @@ -90,9 +103,10 @@ typedef struct symtab_s { symbol_t *symbols; ///< chain of symbols in this table symbol_t **symtail; ///< keep chain in declaration order struct defspace_s *space; ///< storage for vars in scope symtabs + struct class_s *class; ///< owning class if ivar scope } symtab_t; -const char *symtype_str (sy_type_e type); +const char *symtype_str (sy_type_e type) __attribute__((const)); /** Create a new, empty named symbol. @@ -121,7 +135,7 @@ symbol_t *new_symbol_type (const char *name, struct type_s *type); supports both code block scoping and ivar inheritance. \param parent Pointer to parent scope symbol table. - \param type The type of symbol table. Currently governs expected size. + \param type The type of symbol table. \return The new, empty symbol table. */ symtab_t *new_symtab (symtab_t *parent, stab_type_e type); @@ -184,6 +198,7 @@ symbol_t *copy_symbol (symbol_t *symbol); \param symtab The symbol table chain to be copied. \param parent The parent symbol table of the new symbol table, or null. + \param type The type of symbol table. \return The new symbol table. \dot @@ -211,7 +226,8 @@ symbol_t *copy_symbol (symbol_t *symbol); } \enddot */ -symtab_t *symtab_flat_copy (symtab_t *symtab, symtab_t *parent); +symtab_t *symtab_flat_copy (symtab_t *symtab, symtab_t *parent, + stab_type_e type); /** Create a global symbol and allocate space for a variable. @@ -234,6 +250,11 @@ symtab_t *symtab_flat_copy (symtab_t *symtab, symtab_t *parent); symbol_t *make_symbol (const char *name, struct type_s *type, struct defspace_s *space, enum storage_class_e storage); -//@} +struct specifier_s; +symbol_t *declare_symbol (struct specifier_s spec, struct expr_s *init, + symtab_t *symtab); +symbol_t *declare_field (struct specifier_s spec, symtab_t *symtab); + +///@} #endif//__symtab_h diff --git a/tools/qfcc/include/type.h b/tools/qfcc/include/type.h index 2020e8a95..0ddca9623 100644 --- a/tools/qfcc/include/type.h +++ b/tools/qfcc/include/type.h @@ -31,14 +31,21 @@ #ifndef __type_h #define __type_h -#include "QF/pr_comp.h" +#include "QF/progs/pr_type.h" #include "def.h" typedef struct ty_func_s { struct type_s *type; int num_params; - struct type_s *param_types[MAX_PARMS]; + struct type_s **param_types; + union { + struct { + unsigned no_va_list:1;///< don't inject va_list for ... function + unsigned void_return:1;///< special handling for return value + }; + unsigned attribute_bits; + }; } ty_func_t; typedef struct ty_fldptr_s { @@ -51,87 +58,98 @@ typedef struct ty_array_s { int size; } ty_array_t; -typedef enum { - ty_none, ///< func/field/pointer or not used - ty_struct, - ty_union, - ty_enum, - ty_array, - ty_class, -} ty_meta_e; +typedef struct ty_alias_s { + struct type_s *aux_type; ///< other aliases stripped + struct type_s *full_type; ///< full alias chain +} ty_alias_t; typedef struct type_s { etype_t type; ///< ev_invalid means structure/array etc const char *name; + int alignment; ///< required alignment for instances + int width; ///< components in vector types, otherwise 1 + ///< vector and quaternion are 1 (separate from + ///< vec3 and vec4) /// function/pointer/array/struct types are more complex ty_meta_e meta; union { + // no data for ty_basic when not a func, field or pointer ty_func_t func; ty_fldptr_t fldptr; ty_array_t array; struct symtab_s *symtab; struct class_s *class; + ty_alias_t alias; } t; struct type_s *next; int freeable; int allocated; + int printid; ///< for dot output struct protocollist_s *protos; const char *encoding; ///< Objective-QC encoding struct def_s *type_def; ///< offset of qfo encodoing } type_t; -typedef struct { +typedef struct specifier_s { type_t *type; struct param_s *params; + struct symbol_s *sym; storage_class_t storage; - unsigned multi_type:1; - unsigned multi_store:1; - unsigned is_signed:1; - unsigned is_unsigned:1; - unsigned is_short:1; - unsigned is_long:1; - unsigned is_typedef:1; - unsigned is_overload:1; - unsigned nosave:1; + union { + struct { + unsigned multi_type:1; + unsigned multi_store:1; + unsigned is_signed:1; + unsigned is_unsigned:1; + unsigned is_short:1; + unsigned is_long:1; + unsigned is_typedef:1; + unsigned is_overload:1; + unsigned is_function:1;//FIXME do proper void(*)() -> ev_func + unsigned nosave:1; + unsigned no_va_list:1; + unsigned void_return:1; + }; + unsigned spec_bits; + }; } specifier_t; +#define EV_TYPE(type) extern type_t type_##type; +#include "QF/progs/pr_type_names.h" + +#define VEC_TYPE(type_name, base_type) extern type_t type_##type_name; +#include "tools/qfcc/include/vec_types.h" + extern type_t type_invalid; -extern type_t type_void; -extern type_t type_string; -extern type_t type_float; -extern type_t type_vector; -extern type_t type_entity; -extern type_t type_field; -extern type_t type_function; -extern type_t type_pointer; extern type_t type_floatfield; -extern type_t type_quaternion; -extern type_t type_integer; -extern type_t type_uinteger; -extern type_t type_short; extern type_t *type_nil; // for passing nil into ... extern type_t *type_default; // default type (float or int) +extern type_t *type_long_int; // supported type for long +extern type_t *type_ulong_uint;// supported type for ulong extern type_t type_va_list; extern type_t type_param; extern type_t type_zero; extern type_t type_type_encodings; +extern type_t type_xdef; +extern type_t type_xdef_pointer; +extern type_t type_xdefs; extern struct symtab_s *vector_struct; extern struct symtab_s *quaternion_struct; struct dstring_s; -etype_t low_level_type (type_t *type); +etype_t low_level_type (type_t *type) __attribute__((pure)); type_t *new_type (void); void free_type (type_t *type); void chain_type (type_t *type); /** Append a type to the end of a type chain. - The type chain must be made up of only field, pointer, function and array - types, as other types do not have auxiliary type fields. + The type chain must be made up of only field, pointer, function, and + array types, as other types do not have auxiliary type fields. \param type The type chain to which the type will be appended. \param new The type to be appended. May be any type. @@ -139,32 +157,84 @@ void chain_type (type_t *type); level. */ type_t *append_type (type_t *type, type_t *new); +void set_func_type_attrs (type_t *func, specifier_t spec); +specifier_t default_type (specifier_t spec, struct symbol_s *sym); type_t *find_type (type_t *new); void new_typedef (const char *name, type_t *type); type_t *field_type (type_t *aux); type_t *pointer_type (type_t *aux); +type_t *vector_type (const type_t *ele_type, int width) __attribute__((pure)); +type_t *base_type (const type_t *vec_type) __attribute__((pure)); + +/** Return an integral type of same size as the provided type. + + Any 32-bit type will produce type_int (or one of ivec2, ivec3 or ivec4). + Any 64-bit type will produce type_long (lor one of lvec2, lvec3, or lvec4). + + Both type_width() and type_size() of the returned type will match the + provided type. + + \param base Type on which the return type will be based. + \return Matching integral type (int, long, or a vector form), or + null if no such match can be made. +*/ +type_t *int_type (const type_t *base) __attribute__((pure)); + +/** Return a floating point type of same size as the provided type. + + Any 32-bit type will produce type_float (or one of vec2, vec3 or vec4). + Any 64-bit type will produce type_double (lor one of dvec2, dvec3, or + dvec4). + + Both type_width() and type_size() of the returned type will match the + provided type. + + \param base Type on which the return type will be based. + \return Matching floating point type (float, double, or a vector + form), or null if no such match can be made. +*/ +type_t *float_type (const type_t *base) __attribute__((pure)); + type_t *array_type (type_t *aux, int size); type_t *based_array_type (type_t *aux, int base, int top); +type_t *alias_type (type_t *type, type_t *alias_chain, const char *name); +const type_t *unalias_type (const type_t *type) __attribute__((pure)); +const type_t *dereference_type (const type_t *type) __attribute__((pure)); void print_type_str (struct dstring_s *str, const type_t *type); +const char *get_type_string (const type_t *type); void print_type (const type_t *type); +void dump_dot_type (void *t, const char *filename); const char *encode_params (const type_t *type); void encode_type (struct dstring_s *encoding, const type_t *type); const char *type_get_encoding (const type_t *type); -int is_void (const type_t *type); -int is_enum (const type_t *type); -int is_integral (const type_t *type); -int is_float (const type_t *type); -int is_scalar (const type_t *type); -int is_math (const type_t *type); -int is_pointer (const type_t *type); -int is_struct (const type_t *type); -int is_array (const type_t *type); + +#define EV_TYPE(t) int is_##t (const type_t *type) __attribute__((pure)); +#include "QF/progs/pr_type_names.h" + +int is_enum (const type_t *type) __attribute__((pure)); +int is_integral (const type_t *type) __attribute__((pure)); +int is_real (const type_t *type) __attribute__((pure)); +int is_scalar (const type_t *type) __attribute__((pure)); +int is_nonscalar (const type_t *type) __attribute__((pure)); +int is_math (const type_t *type) __attribute__((pure)); +int is_struct (const type_t *type) __attribute__((pure)); +int is_union (const type_t *type) __attribute__((pure)); +int is_array (const type_t *type) __attribute__((pure)); +int is_structural (const type_t *type) __attribute__((pure)); +int type_compatible (const type_t *dst, const type_t *src) __attribute__((pure)); int type_assignable (const type_t *dst, const type_t *src); -int type_size (const type_t *type); +int type_promotes (const type_t *dst, const type_t *src) __attribute__((pure)); +int type_same (const type_t *dst, const type_t *src) __attribute__((pure)); +int type_size (const type_t *type) __attribute__((pure)); +int type_width (const type_t *type) __attribute__((pure)); void init_types (void); void chain_initial_types (void); void clear_typedefs (void); +extern type_t *ev_types[]; +extern int type_cast_map[]; +#define TYPE_CAST_CODE(from, to, width) (((width) << 6) | ((from) << 3) | (to)) + #endif//__type_h diff --git a/tools/qfcc/include/value.h b/tools/qfcc/include/value.h index 11bee26e6..e8eb5a3a7 100644 --- a/tools/qfcc/include/value.h +++ b/tools/qfcc/include/value.h @@ -34,12 +34,16 @@ /** \defgroup qfcc_value Constant values. \ingroup qfcc_expr */ -//@{ +///@{ +struct def_s; struct ex_value_s; +struct tempop_s; struct type_s; +struct pr_type_s; struct ex_value_s *new_string_val (const char *string_val); +struct ex_value_s *new_double_val (double double_val); struct ex_value_s *new_float_val (float float_val); struct ex_value_s *new_vector_val (const float *vector_val); struct ex_value_s *new_entity_val (int entity_val); @@ -47,12 +51,20 @@ struct ex_value_s *new_field_val (int field_val, struct type_s *type, struct def_s *def); struct ex_value_s *new_func_val (int func_val, struct type_s *type); struct ex_value_s *new_pointer_val (int val, struct type_s *type, - struct def_s *def); + struct def_s *def, + struct operand_s *tempop); struct ex_value_s *new_quaternion_val (const float *quaternion_val); -struct ex_value_s *new_integer_val (int integer_val); -struct ex_value_s *new_uinteger_val (int uinteger_val); +struct ex_value_s *new_int_val (int int_val); +struct ex_value_s *new_uint_val (int uint_val); +struct ex_value_s *new_long_val (pr_long_t long_val); +struct ex_value_s *new_ulong_val (pr_ulong_t ulong_val); struct ex_value_s *new_short_val (short short_val); struct ex_value_s *new_nil_val (struct type_s *type); +struct ex_value_s *new_type_value (const struct type_s *type, + const struct pr_type_s *data); +void value_store (pr_type_t *dst, const struct type_s *dstType, + const struct expr_s *src); +const char *get_value_string (const struct ex_value_s *value); struct ex_value_s *convert_value (struct ex_value_s *value, struct type_s *type); @@ -63,6 +75,6 @@ int ReuseString (const char *str); void clear_immediates (void); -//@} +///@} #endif//__value_h diff --git a/tools/qfcc/include/vec_types.h b/tools/qfcc/include/vec_types.h new file mode 100644 index 000000000..4931c8d09 --- /dev/null +++ b/tools/qfcc/include/vec_types.h @@ -0,0 +1,24 @@ +#ifndef VEC_TYPE +#define VEC_TYPE(type_name, base_type) +#endif + +VEC_TYPE(ivec2, int) +VEC_TYPE(ivec3, int) +VEC_TYPE(ivec4, int) +VEC_TYPE(vec2, float) +VEC_TYPE(vec3, float) +VEC_TYPE(vec4, float) +VEC_TYPE(lvec2, long) +VEC_TYPE(lvec3, long) +VEC_TYPE(lvec4, long) +VEC_TYPE(dvec2, double) +VEC_TYPE(dvec3, double) +VEC_TYPE(dvec4, double) +VEC_TYPE(uivec2, uint) +VEC_TYPE(uivec3, uint) +VEC_TYPE(uivec4, uint) +VEC_TYPE(ulvec2, ulong) +VEC_TYPE(ulvec3, ulong) +VEC_TYPE(ulvec4, ulong) + +#undef VEC_TYPE diff --git a/tools/qfcc/source/Makefile.am b/tools/qfcc/source/Makefile.am deleted file mode 100644 index 58011ea08..000000000 --- a/tools/qfcc/source/Makefile.am +++ /dev/null @@ -1,71 +0,0 @@ -## Process this file with automake to produce Makefile.in -# -# Makefile.am -# -# Automake-using build system for QuakeForge -# -# Copyright (C) 2000 Jeff Teunissen -# -# This Makefile is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# -# See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to: -# -# Free Software Foundation, Inc. -# 59 Temple Place - Suite 330 -# Boston, MA 02111-1307, USA -# -# $Id$ -# -AUTOMAKE_OPTIONS= foreign - -QFCC_LIBS=@QFCC_LIBS@ -QFCC_DEPS=@QFCC_DEPS@ -QFCC_INCS=@QFCC_INCS@ - -AM_CPPFLAGS= -I$(top_srcdir)/include $(QFCC_INCS) -YFLAGS = -v -d - -bin_PROGRAMS= qfcc qfprogs -bin_SCRIPTS= qfpreqcc - -common_src=\ - class.c codespace.c constfold.c cpp.c dags.c debug.c def.c defspace.c \ - diagnostic.c dot.c dot_dag.c dot_expr.c dot_flow.c dot_sblock.c emit.c \ - expr.c expr_binary.c flow.c function.c grab.c idstuff.c linker.c method.c \ - obj_file.c \ - obj_type.c opcodes.c options.c pragma.c qfcc.c reloc.c shared.c \ - statements.c strpool.c struct.c switch.c symtab.c type.c value.c - -qfcc_SOURCES= qc-lex.l qc-parse.y qp-lex.l qp-parse.y $(common_src) -qfcc_LDADD= $(QFCC_LIBS) -qfcc_DEPENDENCIES= $(QFCC_DEPS) - -qfprogs_SOURCES= \ - disassemble.c dump_globals.c dump_lines.c dump_modules.c dump_strings.c \ - obj_file.c qfprogs.c strpool.c stub.c -qfprogs_LDADD= $(QFCC_LIBS) -qfprogs_DEPENDENCIES= $(QFCC_DEPS) - -BUILT_SOURCES=qc-parse.c qc-parse.h qc-lex.c qp-parse.c qp-parse.h qp-lex.c - -qc-parse.c: qc-parse.y - $(YACC) $(YFLAGS) -Dapi.prefix=qc_yy $< -o $@ -qc-lex.c: qc-lex.l - $(LEX) $(LFLAGS) $(AM_LFLAGS) -Pqc_yy -o$@ $< -qp-parse.c: qp-parse.y - $(YACC) $(YFLAGS) -Dapi.prefix=qp_yy $< -o $@ -qp-lex.c: qp-lex.l - $(LEX) $(LFLAGS) $(AM_LFLAGS) -Pqp_yy -o$@ $< - -EXTRA_DIST=qc-lex.c qc-parse.c qc-parse.h qfpreqcc \ - qp-parse.c qp-parse.h qp-lex.c diff --git a/tools/qfcc/source/Makemodule.am b/tools/qfcc/source/Makemodule.am new file mode 100644 index 000000000..4bb24a6f3 --- /dev/null +++ b/tools/qfcc/source/Makemodule.am @@ -0,0 +1,96 @@ +QFCC_LIBS=@QFCC_LIBS@ +QFCC_DEPS=@QFCC_DEPS@ +QFCC_INCS=@QFCC_INCS@ + +EXTRA_PROGRAMS += qfcc$(EXEEXT) qfprogs$(EXEEXT) +bin_PROGRAMS += @QFCC_TARGETS@ +bin_SCRIPTS += tools/qfcc/source/qfpreqcc + +qfcc_SOURCES = \ + tools/qfcc/source/attribute.c \ + tools/qfcc/source/class.c \ + tools/qfcc/source/codespace.c \ + tools/qfcc/source/constfold.c \ + tools/qfcc/source/cpp.c \ + tools/qfcc/source/dags.c \ + tools/qfcc/source/debug.c \ + tools/qfcc/source/def.c \ + tools/qfcc/source/defspace.c \ + tools/qfcc/source/diagnostic.c \ + tools/qfcc/source/dot.c \ + tools/qfcc/source/dot_dag.c \ + tools/qfcc/source/dot_expr.c \ + tools/qfcc/source/dot_flow.c \ + tools/qfcc/source/dot_sblock.c \ + tools/qfcc/source/dot_type.c \ + tools/qfcc/source/emit.c \ + tools/qfcc/source/expr.c \ + tools/qfcc/source/expr_assign.c \ + tools/qfcc/source/expr_binary.c \ + tools/qfcc/source/expr_bool.c \ + tools/qfcc/source/expr_cast.c \ + tools/qfcc/source/expr_compound.c \ + tools/qfcc/source/expr_obj.c \ + tools/qfcc/source/expr_vector.c \ + tools/qfcc/source/flow.c \ + tools/qfcc/source/function.c \ + tools/qfcc/source/grab.c \ + tools/qfcc/source/idstuff.c \ + tools/qfcc/source/linker.c \ + tools/qfcc/source/method.c \ + tools/qfcc/source/obj_file.c \ + tools/qfcc/source/obj_type.c \ + tools/qfcc/source/opcodes.c \ + tools/qfcc/source/options.c \ + tools/qfcc/source/pragma.c \ + tools/qfcc/source/qc-lex.l \ + tools/qfcc/source/qc-parse.y \ + tools/qfcc/source/qfcc.c \ + tools/qfcc/source/qp-lex.l \ + tools/qfcc/source/qp-parse.y \ + tools/qfcc/source/reloc.c \ + tools/qfcc/source/shared.c \ + tools/qfcc/source/statements.c \ + tools/qfcc/source/strpool.c \ + tools/qfcc/source/struct.c \ + tools/qfcc/source/switch.c \ + tools/qfcc/source/symtab.c \ + tools/qfcc/source/type.c \ + tools/qfcc/source/value.c + +qfcc_LDADD= $(QFCC_LIBS) +qfcc_DEPENDENCIES= $(QFCC_DEPS) + +qfprogs_SOURCES= \ + tools/qfcc/source/disassemble.c \ + tools/qfcc/source/dump_globals.c \ + tools/qfcc/source/dump_lines.c \ + tools/qfcc/source/dump_modules.c \ + tools/qfcc/source/dump_strings.c \ + tools/qfcc/source/obj_file.c \ + tools/qfcc/source/qfprogs.c \ + tools/qfcc/source/strpool.c \ + tools/qfcc/source/stub.c \ + tools/qfcc/source/type.c +qfprogs_LDADD= $(QFCC_LIBS) +qfprogs_DEPENDENCIES= $(QFCC_DEPS) + +BUILT_SOURCES += \ + tools/qfcc/source/qc-parse.c \ + tools/qfcc/source/qc-parse.h \ + tools/qfcc/source/qc-lex.c \ + tools/qfcc/source/qp-parse.c \ + tools/qfcc/source/qp-parse.h \ + tools/qfcc/source/qp-lex.c + +tools/qfcc/source/qc-parse.c: tools/qfcc/source/qc-parse.y + $(AM_V_YACC)$(YACCCOMPILE) -Dapi.prefix={qc_yy} $< -o $@ +tools/qfcc/source/qc-lex.c: tools/qfcc/source/qc-lex.l + $(AM_V_LEX)$(LEXCOMPILE) -Pqc_yy -o$@ $< +tools/qfcc/source/qp-parse.c: tools/qfcc/source/qp-parse.y + $(AM_V_YACC)$(YACCCOMPILE) -Dapi.prefix={qp_yy} $< -o $@ +tools/qfcc/source/qp-lex.c: tools/qfcc/source/qp-lex.l + $(AM_V_LEX)$(LEXCOMPILE) -Pqp_yy -o$@ $< + +EXTRA_DIST += \ + tools/qfcc/source/qfpreqcc diff --git a/tools/qfcc/source/attribute.c b/tools/qfcc/source/attribute.c new file mode 100644 index 000000000..a1cbe443d --- /dev/null +++ b/tools/qfcc/source/attribute.c @@ -0,0 +1,55 @@ +/* + attribute.c + + Attribute list handling + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/02/02 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/alloc.h" + +#include "tools/qfcc/include/attribute.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/strpool.h" + +ALLOC_STATE (attribute_t, attributes); + +attribute_t *new_attribute(const char *name, expr_t *value) +{ + if (value && value->type != ex_value) { + error (value, "not a literal constant"); + return 0; + } + + attribute_t *attr; + ALLOC (16384, attribute_t, attributes, attr); + attr->name = save_string (name); + attr->value = value ? value->e.value : 0; + return attr; +} diff --git a/tools/qfcc/source/class.c b/tools/qfcc/source/class.c index a7898e5db..deb17d471 100644 --- a/tools/qfcc/source/class.c +++ b/tools/qfcc/source/class.c @@ -41,55 +41,144 @@ #include "QF/dstring.h" #include "QF/hash.h" -#include "QF/pr_obj.h" #include "QF/va.h" -#include "qfcc.h" +#include "QF/progs/pr_obj.h" -#include "codespace.h" -#include "class.h" -#include "def.h" -#include "defspace.h" -#include "diagnostic.h" -#include "emit.h" -#include "expr.h" -#include "method.h" -#include "options.h" -#include "reloc.h" -#include "shared.h" -#include "strpool.h" -#include "struct.h" -#include "symtab.h" -#include "type.h" -#include "value.h" +#include "tools/qfcc/include/qfcc.h" + +#include "tools/qfcc/include/codespace.h" +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/emit.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/method.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/shared.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/struct.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" static hashtab_t *class_hash; static hashtab_t *category_hash; static hashtab_t *protocol_hash; +static hashtab_t *static_instances; +static hashtab_t *static_instance_classes; // these will be built up further -type_t type_obj_selector = { ev_invalid, 0, ty_struct}; -type_t type_SEL = { ev_pointer, "SEL", ty_none, {{&type_obj_selector}}}; -type_t type_IMP = { ev_func, "IMP", ty_none, - {{&type_id, -3, {&type_id, &type_SEL}}}}; -type_t type_obj_super = { ev_invalid, 0 }; -type_t type_SuperPtr = { ev_pointer, 0, ty_none, {{&type_obj_super}}}; -type_t type_supermsg = { ev_func, ".supermsg", ty_none, - {{&type_id, -3, {&type_SuperPtr, &type_SEL}}}}; -type_t type_obj_method = { ev_invalid, 0, ty_struct }; -type_t type_obj_method_description = { ev_invalid, 0, ty_struct }; -type_t type_obj_category = { ev_invalid, 0, ty_struct}; -type_t type_obj_ivar = { ev_invalid, 0, ty_struct}; -type_t type_obj_module = { ev_invalid, 0, ty_struct}; -type_t type_moduleptr = { ev_pointer, 0, ty_none, {{&type_obj_module}}}; -type_t type_obj_exec_class = { ev_func, 0, ty_none, - {{&type_void, 1, { &type_moduleptr }}}}; - -type_t type_obj_object = {ev_invalid, 0, ty_struct}; -type_t type_id = { ev_pointer, "id", ty_none, {{&type_obj_object}}}; -type_t type_obj_class = { ev_invalid, 0, ty_struct}; -type_t type_Class = { ev_pointer, 0, ty_none, {{&type_obj_class}}}; -type_t type_obj_protocol = { ev_invalid, 0, ty_struct}; +type_t type_selector = { + .type = ev_invalid, + .meta = ty_struct, +}; +type_t type_SEL = { + .type = ev_ptr, + .name = "SEL", + .alignment = 1, + .width = 1, + .meta = ty_basic, + {{&type_selector}}, +}; +type_t *IMP_params[] = { &type_id, &type_SEL }; +type_t type_IMP = { + .type = ev_func, + .name = "IMP", + .alignment = 1, + .width = 1, + .meta = ty_basic, + {{&type_id, -3, IMP_params, .no_va_list = 1}}, +}; +type_t type_super = { + .type = ev_invalid, +}; +type_t type_SuperPtr = { + .type = ev_ptr, + .alignment = 1, + .width = 1, + .meta = ty_basic, + {{&type_super}}, +}; +type_t *supermsg_params[] = { &type_SuperPtr, &type_SEL }; +type_t type_supermsg = { + .type = ev_func, + .name = ".supermsg", + .alignment = 1, + .width = 1, + .meta = ty_basic, + {{&type_id, -3, supermsg_params}}, +}; +type_t type_method = { + .type = ev_invalid, + .meta = ty_struct, +}; +type_t type_method_description = { + .type = ev_invalid, + .meta = ty_struct, +}; +type_t type_category = { + .type = ev_invalid, + .meta = ty_struct, +}; +type_t type_ivar = { + .type = ev_invalid, + .meta = ty_struct, +}; +type_t type_module = { + .type = ev_invalid, + .meta = ty_struct, +}; +type_t type_moduleptr = { + .type = ev_ptr, + .alignment = 1, + .width = 1, + .meta = ty_basic, + {{&type_module}}, +}; +type_t *obj_exec_class_params[] = { + &type_moduleptr, +}; +type_t type_exec_class = { + .type = ev_func, + .alignment = 1, + .width = 1, + .meta = ty_basic, + {{&type_void, 1, obj_exec_class_params}}, +}; +// the cast of 1 in the init is to ensure pointers to incomplete types +// are never misidentified as id. It will be set to the correct value +// when the obj system is initialized. +type_t type_object = { + .type = ev_invalid, + .meta = ty_struct, + {{(type_t *)1}}, +}; +type_t type_id = { + .type = ev_ptr, + .name = "id", + .alignment = 1, + .width = 1, + .meta = ty_basic, + {{&type_object}}, +}; +type_t type_class = { + .type = ev_invalid, + .meta = ty_struct, +}; +type_t type_Class = { + .type = ev_ptr, + .alignment = 1, + .width = 1, + .meta = ty_basic, + {{&type_class}}, +}; +type_t type_protocol = { + .type = ev_invalid, + .meta = ty_struct, +}; int obj_initialized = 0; @@ -115,16 +204,16 @@ static struct_def_t method_desc_struct[] = { static struct_def_t category_struct[] = { {"category_name", &type_string}, {"class_name", &type_string}, - {"instance_methods", &type_pointer}, - {"class_methods", &type_pointer}, - {"protocols", &type_pointer}, + {"instance_methods", &type_ptr}, + {"class_methods", &type_ptr}, + {"protocols", &type_ptr}, {0, 0} }; static struct_def_t ivar_struct[] = { {"ivar_name", &type_string}, {"ivar_type", &type_string}, - {"ivar_offset", &type_integer}, + {"ivar_offset", &type_int}, {0, 0} }; @@ -135,10 +224,10 @@ static struct_def_t super_struct[] = { }; static struct_def_t module_struct[] = { - {"version", &type_integer}, - {"size", &type_integer}, + {"version", &type_int}, + {"size", &type_int}, {"name", &type_string}, - {"symtab", &type_pointer}, + {"symtab", &type_ptr}, {0, 0} }; @@ -146,25 +235,25 @@ static struct_def_t class_struct[] = { {"class_pointer", &type_Class}, {"super_class", &type_Class}, {"name", &type_string}, - {"version", &type_integer}, - {"info", &type_integer}, - {"instance_size", &type_integer}, - {"ivars", &type_pointer}, - {"methods", &type_pointer}, - {"dtable", &type_pointer}, - {"subclass_list", &type_pointer}, - {"sibling_class", &type_pointer}, - {"protocols", &type_pointer}, - {"gc_object_type", &type_pointer}, + {"version", &type_int}, + {"info", &type_int}, + {"instance_size", &type_int}, + {"ivars", &type_ptr}, + {"methods", &type_ptr}, + {"dtable", &type_ptr}, + {"subclass_list", &type_ptr}, + {"sibling_class", &type_ptr}, + {"protocols", &type_ptr}, + {"gc_object_type", &type_ptr}, {0, 0} }; static struct_def_t protocol_struct[] = { {"class_pointer", &type_Class}, {"protocol_name", &type_string}, - {"protocol_list", &type_pointer}, - {"instance_methods", &type_pointer}, - {"class_methods", &type_pointer}, + {"protocol_list", &type_ptr}, + {"instance_methods", &type_ptr}, + {"class_methods", &type_ptr}, {0, 0} }; @@ -173,25 +262,161 @@ static struct_def_t object_struct[] = { {0, 0} }; +static const char * +static_instance_get_key (const void *instance, void *unused) +{ + return ((static_instance_t *) instance)->class; +} + +static void +add_static_instance (const char *class, def_t *instance_def) +{ + static_instance_t *instance = malloc (sizeof (*instance)); + + if (!static_instances) { + static_instances = Hash_NewTable (1021, static_instance_get_key, + 0, 0, 0); + static_instance_classes = Hash_NewTable (1021, static_instance_get_key, + 0, 0, 0); + } + + instance->class = save_string (class); + instance->instance = instance_def; + Hash_Add (static_instances, instance); + + // uniqued set of class names for all static instances + if (!Hash_Find (static_instance_classes, class)) { + Hash_Add (static_instance_classes, instance); + } +} +typedef struct { + const char *class_name; + int num_instances; + static_instance_t **instances; +} obj_static_instances_data_t; + +static void +emit_instance_classname (def_t *def, void *data, int index) +{ + obj_static_instances_data_t *da = (obj_static_instances_data_t *)data; + + if (!is_string(def->type)) + internal_error (0, "%s: expected string def", __FUNCTION__); + EMIT_STRING (def->space, D_STRING (def), da->class_name); +} + +static void +emit_instance_defs (def_t *def, void *data, int index) +{ + obj_static_instances_data_t *da = (obj_static_instances_data_t *)data; + + if (!is_array (def->type) || def->type->t.array.type->type != ev_ptr) + internal_error (0, "%s: expected array of pointers def", __FUNCTION__); + if (index < 0 || index >= da->num_instances + 1) + internal_error (0, "%s: out of bounds index: %d %d", + __FUNCTION__, index, da->num_instances + 1); + D_INT (def) = 0; + if (index < da->num_instances) { + EMIT_DEF (def->space, D_INT (def), da->instances[index]->instance); + } +} + +static def_t * +emit_static_instances (const char *classname) +{ + static struct_def_t instances_struct[] = { + {"class_name", &type_string, emit_instance_classname}, + {"instances", 0, emit_instance_defs}, + {0, 0} + }; + obj_static_instances_data_t data = {}; + def_t *instances_def; + + data.class_name = classname; + data.instances = (static_instance_t **) Hash_FindList (static_instances, + classname); + for (static_instance_t **inst = data.instances; *inst; inst++) { + data.num_instances++; + } + instances_struct[1].type = array_type (&type_ptr, data.num_instances + 1); + instances_def = emit_structure (va (0, "_OBJ_STATIC_INSTANCES_%s", + classname), + 's', instances_struct, 0, &data, + 0, sc_static); + free (data.instances); + return instances_def; +} + +static def_t * +emit_static_instances_list (void) +{ + static_instance_t **classes; + int num_classes = 0; + def_t **instance_lists; + type_t *instance_lists_type; + symbol_t *instance_lists_sym; + def_t *instance_lists_def; + pr_ptr_t *list; + defspace_t *space; + + if (!static_instance_classes || !static_instances) { + return 0; + } + + classes = (static_instance_t **) Hash_GetList (static_instance_classes); + for (static_instance_t **c = classes; *c; c++) { + num_classes++; + } + if (!num_classes) { + free (classes); + return 0; + } + instance_lists = alloca (num_classes * sizeof (*instance_lists)); + for (int i = 0; i < num_classes; i++) { + instance_lists[i] = emit_static_instances (classes[i]->class); + } + free (classes); + + // +1 for terminating null + instance_lists_type = array_type (&type_ptr, num_classes + 1); + instance_lists_sym = make_symbol ("_OBJ_STATIC_INSTANCES", + instance_lists_type, + pr.far_data, sc_static); + if (!instance_lists_sym->table) { + symtab_addsymbol (pr.symtab, instance_lists_sym); + } + instance_lists_def = instance_lists_sym->s.def; + instance_lists_def->initialized = instance_lists_def->constant = 1; + instance_lists_def->nosave = 1; + + list = D_POINTER (pr_ptr_t, instance_lists_def); + space = instance_lists_def->space; + for (int i = 0; i < num_classes; i++, list++) { + EMIT_DEF (space, *list, instance_lists[i]); + } + *list = 0; + return instance_lists_def; +} + int -obj_is_id (const type_t *type) +is_id (const type_t *type) { if (type == &type_id) return 1; // type may be a qualified id, in which case it will be a pointer to // a qualified obj_object struct - if (type->type != ev_pointer) + if (type->type != ev_ptr) return 0; if (!is_struct (type->t.fldptr.type)) return 0; // if the the symtabs match, then type is id in disguise - if (type->t.fldptr.type->t.symtab == type_obj_object.t.symtab) + if (type->t.fldptr.type->t.symtab == type_object.t.symtab) return 1; return 0; } int -obj_is_class (const type_t *type) +is_class (const type_t *type) { if (type->type == ev_invalid && type->meta == ty_class) return 1; @@ -199,42 +424,57 @@ obj_is_class (const type_t *type) } int -obj_is_Class (const type_t *type) +is_Class (const type_t *type) { if (type == &type_Class) return 1; - // type may be a qualified Class, in which case it will be a pointer to - // a qualified obj_class struct - if (type->type != ev_pointer) - return 0; - if (!is_struct (type->t.fldptr.type)) - return 0; - // if the the symtabs match, then type is Class in disguise - if (type->t.fldptr.type->t.symtab == type_obj_class.t.symtab) - return 1; return 0; } int -obj_is_classptr (const type_t *type) +is_classptr (const type_t *type) { // easy cases first :) - if (obj_is_id (type) || obj_is_Class (type)) + if (is_id (type) || is_Class (type)) return 1; - if (type->type != ev_pointer) + if (type->type != ev_ptr) return 0; type = type->t.fldptr.type; - if (obj_is_class (type)) + if (is_class (type)) return 1; return 0; } +int +is_SEL (const type_t *type) +{ + return type == &type_SEL; +} + +int +is_object (const type_t *type) +{ + return type == &type_object; +} + +int +is_method (const type_t *type) +{ + return type == &type_method; +} + +int +is_method_description (const type_t *type) +{ + return type == &type_method_description; +} + static protocollist_t * obj_get_class_protos (const type_t *type) { - if (is_pointer (type)) + if (is_ptr (type)) type = type->t.fldptr.type; - if (obj_is_class (type)) + if (is_class (type)) return type->t.class->protocols; return 0; } @@ -242,7 +482,7 @@ obj_get_class_protos (const type_t *type) static protocollist_t * obj_get_protos (const type_t *type) { - if (is_pointer (type)) + if (is_ptr (type)) type = type->t.fldptr.type; return type->protos; } @@ -250,9 +490,9 @@ obj_get_protos (const type_t *type) static category_t * obj_get_categories (const type_t *type) { - if (is_pointer (type)) + if (is_ptr (type)) type = type->t.fldptr.type; - if (obj_is_class (type)) + if (is_class (type)) return type->t.class->categories; return 0; } @@ -266,14 +506,14 @@ obj_classname (const type_t *type) if (!str) str = dstring_new (); dstring_clearstr (str); - if (obj_is_id (type)) { + if (is_id (type)) { dstring_copystr (str, "id"); - } else if (obj_is_Class (type)) { + } else if (is_Class (type)) { dstring_copystr (str, "Class"); } else { - if (is_pointer (type)) + if (is_ptr (type)) type = type->t.fldptr.type; - if (obj_is_class (type)) + if (is_class (type)) dstring_copystr (str, type->t.class->name); } if ((protos = obj_get_protos (type))) @@ -281,7 +521,7 @@ obj_classname (const type_t *type) return str->str; } -static int +static __attribute__((pure)) int category_implements (category_t *cat, protocol_t *protocol) { for (; cat; cat = cat->next) { @@ -296,18 +536,24 @@ category_implements (category_t *cat, protocol_t *protocol) int obj_types_assignable (const type_t *dst, const type_t *src) { - class_t *dst_class, *src_class; + class_t *dst_class, *src_class = 0; category_t *cat; int dst_is_proto, src_is_proto; protocollist_t *dst_protos = 0, *src_protos = 0; int i; //puts ("%$$\"$#%"); - if (!obj_is_classptr (dst) || !obj_is_classptr (src)) + if (!is_classptr (src)) { + // if dst is a class pointer, then the types are not compatible, + // otherwise unknown + return is_classptr (dst) - 1; + } + if (!is_classptr (dst)) { return -1; + } - dst_is_proto = obj_is_id (dst) && (dst_protos = obj_get_protos (dst)); - src_is_proto = obj_is_id (src) && (src_protos = obj_get_protos (src)); + dst_is_proto = is_id (dst) && (dst_protos = obj_get_protos (dst)); + src_is_proto = is_id (src) && (src_protos = obj_get_protos (src)); if (dst_is_proto) { if (src_is_proto) { @@ -320,7 +566,7 @@ obj_types_assignable (const type_t *dst, const type_t *src) return 1; } } - } else if (!obj_is_id (src)) { + } else if (!is_id (src)) { src_protos = obj_get_class_protos (src); for (i = 0; i < dst_protos->count; i++) { if (procollist_find_protocol (src_protos, dst_protos->list[i])) @@ -337,12 +583,14 @@ obj_types_assignable (const type_t *dst, const type_t *src) } else if (src_is_proto) { } else { } - if (obj_is_id (dst) || obj_is_id (src)) + if (is_id (dst) || is_id (src)) return 1; // check dst is a base class of src dst_class = dst->t.fldptr.type->t.class; - src_class = src->t.fldptr.type->t.class; + if (src->t.fldptr.type->meta == ty_class) { + src_class = src->t.fldptr.type->t.class; + } //printf ("%s %s\n", dst_class->name, src_class->name); while (dst_class != src_class && src_class) { src_class = src_class->super_class; @@ -374,16 +622,16 @@ get_class_name (class_type_t *class_type, int pretty) if (pretty) return class_type->c.class->name; else - return va ("%s_", class_type->c.class->name); + return va (0, "%s_", class_type->c.class->name); case ct_category: if (pretty) - return va ("%s (%s)", class_type->c.category->class->name, + return va (0, "%s (%s)", class_type->c.category->class->name, class_type->c.category->name); else - return va ("%s_%s", class_type->c.category->class->name, + return va (0, "%s_%s", class_type->c.category->class->name, class_type->c.category->name); case ct_protocol: - return va ("<%s>", class_type->c.protocol->name); + return va (0, "<%s>", class_type->c.protocol->name); } return "???"; } @@ -397,14 +645,14 @@ class_symbol (class_type_t *class_type, int external) switch (class_type->type) { case ct_category: - name = va ("_OBJ_CATEGORY_%s_%s", + name = va (0, "_OBJ_CATEGORY_%s_%s", class_type->c.category->class->name, class_type->c.category->name); - type = &type_obj_category; + type = &type_category; break; case ct_class: - name = va ("_OBJ_CLASS_%s", class_type->c.class->name); - type = &type_obj_class; + name = va (0, "_OBJ_CLASS_%s", class_type->c.class->name); + type = &type_class; break; case ct_protocol: return 0; // probably in error recovery @@ -422,7 +670,7 @@ _get_class (symbol_t *sym, int create) class_t *c; if (!class_hash) - class_hash = Hash_NewTable (1021, class_get_key, 0, 0); + class_hash = Hash_NewTable (1021, class_get_key, 0, 0, 0); if (sym) { c = Hash_Find (class_hash, sym->name); if (c || !create) @@ -489,9 +737,7 @@ class_add_methods (class_t *class, methodlist_t *methods) if (!methods) return; - *class->methods->tail = methods->head; - class->methods->tail = methods->tail; - free (methods); + merge_method_lists (class->methods, methods); methods_set_self_type (class, class->methods); } @@ -502,15 +748,26 @@ class_add_protocols (class_t *class, protocollist_t *protocols) int i; protocol_t *p; methodlist_t *methods; + methodset_t *except; + class_t *super; if (!protocols) return; methods = class->methods; + except = new_methodset (); + for (super = class->super_class; super; super = super->super_class) { + methodset_add_methods (except, super->methods); + } + for (i = 0; i < protocols->count; i++) { p = protocols->list[i]; - copy_methods (methods, p->methods); + if (p->methods) { + copy_methods (methods, p->methods, except); + } else { + warning (0, "definition of protocol `%s' not found", p->name); + } if (p->protocols) class_add_protocols (class, p->protocols); } @@ -537,7 +794,8 @@ begin_category (category_t *category) EMIT_STRING (space, pr_category->class_name, class->name); EMIT_DEF (space, pr_category->protocols, emit_protocol_list (category->protocols, - va ("%s_%s", class->name, category->name))); + va (0, "%s_%s", class->name, + category->name))); } typedef struct { @@ -551,8 +809,8 @@ emit_ivar_count (def_t *def, void *data, int index) { ivar_data_t *ivar_data = (ivar_data_t *) data; - if (def->type != &type_integer) - internal_error (0, "%s: expected integer def", __FUNCTION__); + if (!is_int(def->type)) + internal_error (0, "%s: expected int def", __FUNCTION__); D_INT (def) = ivar_data->count; } @@ -565,7 +823,7 @@ emit_ivar_list_item (def_t *def, void *data, int index) defspace_t *space; #if 0 - //FIXME the type is dynamic, so need a way to pass it before it cn be + //FIXME the type is dynamic, so need a way to pass it before it can be //checked if (def->type != &XXX) internal_error (0, "%s: expected XXX def", @@ -596,8 +854,8 @@ static def_t * emit_ivars (symtab_t *ivars, const char *name) { static struct_def_t ivar_list_struct[] = { - {"ivar_count", &type_integer, emit_ivar_count}, - {"ivar_list", 0, emit_ivar_list_item}, + {"ivar_count", &type_int, emit_ivar_count}, + {"ivar_list", 0, emit_ivar_list_item}, {0, 0} }; ivar_data_t ivar_data = {0, 0, 0}; @@ -611,10 +869,10 @@ emit_ivars (symtab_t *ivars, const char *name) if (s->sy_type == sy_var) ivar_data.count++; } - ivar_list_struct[1].type = array_type (&type_obj_ivar, ivar_data.count); + ivar_list_struct[1].type = array_type (&type_ivar, ivar_data.count); - def = emit_structure (va ("_OBJ_INSTANCE_VARIABLES_%s", name), 's', - ivar_list_struct, 0, &ivar_data, sc_static); + def = emit_structure (va (0, "_OBJ_INSTANCE_VARIABLES_%s", name), 's', + ivar_list_struct, 0, &ivar_data, 0, sc_static); dstring_delete (ivar_data.encoding); return def; @@ -630,8 +888,8 @@ begin_class (class_t *class) def_t *def; defspace_t *space; - sym = make_symbol (va ("_OBJ_METACLASS_%s", class->name), - &type_obj_class, pr.far_data, sc_static); + sym = make_symbol (va (0, "_OBJ_METACLASS_%s", class->name), + &type_class, pr.far_data, sc_static); meta_def = sym->s.def; meta_def->initialized = meta_def->constant = meta_def->nosave = 1; space = meta_def->space; @@ -641,13 +899,13 @@ begin_class (class_t *class) EMIT_STRING (space, meta->super_class, class->super_class->name); EMIT_STRING (space, meta->name, class->name); meta->info = _PR_CLS_META; - meta->instance_size = type_size (&type_obj_class); + meta->instance_size = type_size (&type_class); if (!class->super_class) { // The ivars list for the meta class struct get emitted only for the // root class of the hierachy. - // NOTE: type_obj_class is not actually a class + // NOTE: type_class is not actually a class EMIT_DEF (space, meta->ivars, - emit_ivars (type_obj_class.t.symtab, "Class")); + emit_ivars (type_class.t.symtab, "Class")); } else { meta->ivars = 0; } @@ -699,16 +957,16 @@ emit_class_ref (const char *class_name) def_t *ref_def; def_t *name_def; - ref_sym = make_symbol (va (".obj_class_ref_%s", class_name), &type_pointer, - pr.far_data, sc_static); + ref_sym = make_symbol (va (0, ".obj_class_ref_%s", class_name), + &type_ptr, pr.far_data, sc_static); if (!ref_sym->table) symtab_addsymbol (pr.symtab, ref_sym); ref_def = ref_sym->s.def; if (ref_def->initialized) return; ref_def->initialized = ref_def->constant = ref_def->nosave = 1; - name_sym = make_symbol (va (".obj_class_name_%s", class_name), - &type_pointer, pr.far_data, sc_extern); + name_sym = make_symbol (va (0, ".obj_class_name_%s", class_name), + &type_ptr, pr.far_data, sc_extern); if (!name_sym->table) symtab_addsymbol (pr.symtab, name_sym); name_def = name_sym->s.def; @@ -723,8 +981,8 @@ emit_class_name (const char *class_name) symbol_t *name_sym; def_t *name_def; - name_sym = make_symbol (va (".obj_class_name_%s", class_name), - &type_pointer, pr.far_data, sc_global); + name_sym = make_symbol (va (0, ".obj_class_name_%s", class_name), + &type_ptr, pr.far_data, sc_global); if (!name_sym->table) symtab_addsymbol (pr.symtab, name_sym); name_def = name_sym->s.def; @@ -743,9 +1001,9 @@ emit_category_ref (const char *class_name, const char *category_name) def_t *ref_def; def_t *name_def; - ref_sym = make_symbol (va (".obj_category_ref_%s_%s", - class_name, category_name), - &type_pointer, pr.far_data, sc_static); + ref_sym = make_symbol (va (0, ".obj_category_ref_%s_%s", + class_name, category_name), + &type_ptr, pr.far_data, sc_static); if (!ref_sym->table) symtab_addsymbol (pr.symtab, ref_sym); ref_def = ref_sym->s.def; @@ -753,9 +1011,9 @@ emit_category_ref (const char *class_name, const char *category_name) return; ref_def->initialized = ref_def->constant = 1; ref_def->nosave = 1; - name_sym = make_symbol (va (".obj_category_name_%s_%s", - class_name, category_name), - &type_pointer, pr.far_data, sc_extern); + name_sym = make_symbol (va (0, ".obj_category_name_%s_%s", + class_name, category_name), + &type_ptr, pr.far_data, sc_extern); if (!name_sym->table) symtab_addsymbol (pr.symtab, name_sym); name_def = name_sym->s.def; @@ -770,9 +1028,9 @@ emit_category_name (const char *class_name, const char *category_name) symbol_t *name_sym; def_t *name_def; - name_sym = make_symbol (va (".obj_category_name_%s_%s", + name_sym = make_symbol (va (0, ".obj_category_name_%s_%s", class_name, category_name), - &type_pointer, pr.far_data, sc_global); + &type_ptr, pr.far_data, sc_global); if (!name_sym->table) symtab_addsymbol (pr.symtab, name_sym); name_def = name_sym->s.def; @@ -817,7 +1075,7 @@ finish_class (class_t *class) space = class->def->space; cls = &D_STRUCT (pr_class_t, class->def); - meta = &G_STRUCT (space, pr_class_t, cls->class_pointer); + meta = &Q_STRUCT (space, pr_class_t, cls->class_pointer); EMIT_DEF (space, meta->methods, emit_methods (class->methods, class->name, 0)); @@ -884,10 +1142,20 @@ class_find_ivar (class_t *class, int vis, const char *name) { symbol_t *ivar; + if (!class->ivars) { + if (!class->interface_declared) { + class->interface_declared = 1; + error (0, "accessing incomplete type %s", class->name); + } + return 0; + } ivar = symtab_lookup (class->ivars, name); if (ivar) { - if (ivar->visibility > (vis_t) vis) + if (ivar->visibility > (vis_t) vis + || (ivar->table->class != class + && ivar->visibility > vis_protected)) { goto access_error; + } return ivar; } error (0, "%s.%s does not exist", class->name, name); @@ -920,7 +1188,7 @@ class_find_method (class_type_t *class_type, method_t *method) start_methods = methods; start_class = class; while (class) { - for (m = methods->head; m; m = m->next) + for (m = methods->head; m; m = m->next) { if (method_compare (method, m)) { if (m->type != method->type) error (0, "method type mismatch"); @@ -932,6 +1200,7 @@ class_find_method (class_type_t *class_type, method_t *method) method_set_param_names (m, method); return m; } + } if (class->methods == methods) class = class->super_class; else @@ -945,48 +1214,86 @@ class_find_method (class_type_t *class_type, method_t *method) return method; } +static method_t * +cls_find_method (methodlist_t *methodlist, selector_t *selector, + int class_msg, int is_root) +{ + method_t *m = 0; + m = methodlist_find_method (methodlist, selector, !class_msg); + if (!m && is_root && class_msg + && (m = methodlist_find_method (methodlist, selector, 1))) { + return m; + } + return m; +} + method_t * -class_message_response (class_t *class, int class_msg, expr_t *sel) +class_message_response (type_t *clstype, int class_msg, expr_t *sel) { selector_t *selector; method_t *m; - class_t *c = class; + class_t *c; + class_t *class = 0; category_t *cat; + dstring_t *dstr; selector = get_selector (sel); if (!selector) return 0; - if (class && class->type != &type_obj_object) { - while (c) { - for (cat = c->categories; cat; cat = cat->next) { - for (m = cat->methods->head; m; m = m->next) { - if (((!c->super_class && class_msg) - || class_msg != m->instance) - && strcmp (selector->name, m->name) == 0) - return m; - } + + if (!is_classptr (clstype) && !is_class (clstype)) { + error (0, "neither class nor object"); + return 0; + } + if (is_id (clstype)) { + protocollist_t *protos = clstype->t.fldptr.type->protos; + if (protos) { + if ((m = protocollist_find_method (protos, selector, !class_msg))) { + return m; } - for (m = c->methods->head; m; m = m->next) { - if (((!c->super_class && class_msg) - || class_msg != m->instance) - && strcmp (selector->name, m->name) == 0) - return m; - } - c = c->super_class; + dstr = dstring_new (); + print_protocollist (dstr, protos); + warning (sel, "id%s may not respond to %c%s", dstr->str, + class_msg ? '+' : '-', selector->name); + dstring_delete (dstr); } - //FIXME right option? - if (options.warnings.interface_check) + } else { + if (is_class (clstype)) { + class = clstype->t.class; + } else if (is_class (clstype->t.fldptr.type)) { + class = clstype->t.fldptr.type->t.class; + } + if (class && !is_object(class->type)) { + if (!class->interface_declared) { + class->interface_declared = 1; + warning (0, "cannot find interface declaration for `%s'", + class->name); + } + c = class; + while (c) { + for (cat = c->categories; cat; cat = cat->next) { + if ((m = cls_find_method (cat->methods, selector, + class_msg, + !c->super_class))) { + return m; + } + } + if ((m = cls_find_method (c->methods, selector, class_msg, + !c->super_class))) { + return m; + } + c = c->super_class; + } warning (sel, "%s may not respond to %c%s", class->name, class_msg ? '+' : '-', selector->name); + } } m = find_method (selector->name); - if (m) - return m; - //FIXME right option? - if (options.warnings.interface_check) + if (!m && (!class || is_object(class->type))) { warning (sel, "could not find method for %c%s", class_msg ? '+' : '-', selector->name); - return 0; + } + return m; } static uintptr_t @@ -1013,7 +1320,8 @@ class_new_ivars (class_t *class) if (class->super_class) super_ivars = class->super_class->ivars; - ivars = new_symtab (super_ivars, stab_local); + ivars = new_symtab (super_ivars, stab_ivars); + ivars->class = class; return ivars; } @@ -1074,7 +1382,7 @@ get_category (symbol_t *class_name, const char *category_name, int create) class_t *class; if (!category_hash) { - category_hash = Hash_NewTable (1021, 0, 0, 0); + category_hash = Hash_NewTable (1021, 0, 0, 0, 0); Hash_SetHashCompare (category_hash, category_get_hash, category_compare); } @@ -1109,9 +1417,7 @@ category_add_methods (category_t *category, methodlist_t *methods) { if (!methods) return; - *category->methods->tail = methods->head; - category->methods->tail = methods->tail; - free (methods); + merge_method_lists (category->methods, methods); methods_set_self_type (category->class, category->methods); } @@ -1122,15 +1428,22 @@ category_add_protocols (category_t *category, protocollist_t *protocols) int i; protocol_t *p; methodlist_t *methods; + methodset_t *except; + class_t *class; if (!protocols) return; methods = category->methods; + except = new_methodset (); + for (class = category->class; class; class = class->super_class) { + methodset_add_methods (except, class->methods); + } + for (i = 0; i < protocols->count; i++) { p = protocols->list[i]; - copy_methods (methods, p->methods); + copy_methods (methods, p->methods, except); if (p->protocols) category_add_protocols (category, p->protocols); } @@ -1146,9 +1459,8 @@ class_pointer_symbol (class_t *class) class_type.c.class = class; - sym = make_symbol (va ("_OBJ_CLASS_POINTER_%s", class->name), - &type_Class, - pr.near_data, sc_static); + sym = make_symbol (va (0, "_OBJ_CLASS_POINTER_%s", class->name), + &type_Class, pr.near_data, sc_static); if (!sym->table) symtab_addsymbol (pr.symtab, sym); def = sym->s.def; @@ -1171,6 +1483,7 @@ typedef struct { int cls_def_cnt; category_t **categories; int cat_def_cnt; + def_t *instances_list; } obj_symtab_data_t; static void @@ -1178,8 +1491,8 @@ emit_symtab_ref_cnt (def_t *def, void *data, int index) { obj_symtab_data_t *da = (obj_symtab_data_t *)data; - if (def->type != &type_integer) - internal_error (0, "%s: expected integer def", __FUNCTION__); + if (!is_int(def->type)) + internal_error (0, "%s: expected int def", __FUNCTION__); D_INT (def) = 0; if (da->refs) D_INT (def) = da->refs->type->t.array.size; @@ -1190,7 +1503,7 @@ emit_symtab_refs (def_t *def, void *data, int index) { obj_symtab_data_t *da = (obj_symtab_data_t *)data; - if (def->type != &type_SEL) + if (!is_SEL(def->type)) internal_error (0, "%s: expected SEL def", __FUNCTION__); D_INT (def) = 0; if (da->refs) @@ -1202,8 +1515,8 @@ emit_symtab_cls_def_cnt (def_t *def, void *data, int index) { obj_symtab_data_t *da = (obj_symtab_data_t *)data; - if (def->type != &type_integer) - internal_error (0, "%s: expected integer def", __FUNCTION__); + if (!is_int(def->type)) + internal_error (0, "%s: expected int def", __FUNCTION__); D_INT (def) = da->cls_def_cnt; } @@ -1212,8 +1525,8 @@ emit_symtab_cat_def_cnt (def_t *def, void *data, int index) { obj_symtab_data_t *da = (obj_symtab_data_t *)data; - if (def->type != &type_integer) - internal_error (0, "%s: expected integer def", __FUNCTION__); + if (!is_int(def->type)) + internal_error (0, "%s: expected int def", __FUNCTION__); D_INT (def) = da->cat_def_cnt; } @@ -1222,12 +1535,12 @@ emit_symtab_defs (def_t *def, void *data, int index) { obj_symtab_data_t *da = (obj_symtab_data_t *)data; - if (!is_array (def->type) || def->type->t.array.type->type != ev_pointer) + if (!is_array (def->type) || def->type->t.array.type->type != ev_ptr) internal_error (0, "%s: expected array of pointers def", __FUNCTION__); - if (index < 0 || index >= da->cls_def_cnt + da->cat_def_cnt) + if (index < 0 || index >= da->cls_def_cnt + da->cat_def_cnt + 1) internal_error (0, "%s: out of bounds index: %d %d", __FUNCTION__, index, - da->cls_def_cnt + da->cat_def_cnt); + da->cls_def_cnt + da->cat_def_cnt + 1); if (index < da->cls_def_cnt) { class_t **cl; @@ -1236,7 +1549,7 @@ emit_symtab_defs (def_t *def, void *data, int index) if (!index--) break; EMIT_DEF (def->space, D_INT (def), (*cl)->def); - } else { + } else if (index < da->cls_def_cnt + da->cat_def_cnt) { category_t **ca; index -= da->cls_def_cnt; for (ca = da->categories; *ca; ca++) @@ -1244,6 +1557,11 @@ emit_symtab_defs (def_t *def, void *data, int index) if (!index--) break; EMIT_DEF (def->space, D_INT (def), (*ca)->def); + } else { + D_INT (def) = 0; + if (da->instances_list) { + EMIT_DEF (def->space, D_INT (def), da->instances_list); + } } } @@ -1251,25 +1569,25 @@ void class_finish_module (void) { static struct_def_t symtab_struct[] = { - {"sel_ref_cnt", &type_integer, emit_symtab_ref_cnt}, - {"refs", &type_SEL, emit_symtab_refs}, - {"cls_def_cnt", &type_integer, emit_symtab_cls_def_cnt}, - {"cat_def_cnt", &type_integer, emit_symtab_cat_def_cnt}, - {"defs", 0, emit_symtab_defs}, + {"sel_ref_cnt", &type_int, emit_symtab_ref_cnt}, + {"refs", &type_SEL, emit_symtab_refs}, + {"cls_def_cnt", &type_int, emit_symtab_cls_def_cnt}, + {"cat_def_cnt", &type_int, emit_symtab_cat_def_cnt}, + {"defs", 0, emit_symtab_defs}, {0, 0} }; - obj_symtab_data_t data = {0, 0, 0, 0, 0}; + obj_symtab_data_t data = {}; class_t **cl; category_t **ca; def_t *symtab_def; symbol_t *module_sym; + expr_t *module_expr; pr_module_t *module; symbol_t *exec_class_sym; symbol_t *init_sym; expr_t *init_expr; - storage_class_t save_storage; data.refs = emit_selectors (); if (class_hash) { @@ -1284,20 +1602,24 @@ class_finish_module (void) if ((*ca)->def && !(*ca)->def->external) data.cat_def_cnt++; } - if (!data.refs && !data.cls_def_cnt && !data.cat_def_cnt) + data.instances_list = emit_static_instances_list (); + if (!data.refs && !data.cls_def_cnt && !data.cat_def_cnt + && !data.instances_list) return; - symtab_struct[4].type = array_type (&type_pointer, - data.cls_def_cnt + data.cat_def_cnt); + symtab_struct[4].type = array_type (&type_ptr, + data.cls_def_cnt + + data.cat_def_cnt + + 1); symtab_def = emit_structure ("_OBJ_SYMTAB", 's', symtab_struct, 0, &data, - sc_static); + 0, sc_static); free (data.classes); free (data.categories); - module_sym = make_symbol ("_OBJ_MODULE", &type_obj_module, pr.far_data, + module_sym = make_symbol ("_OBJ_MODULE", &type_module, pr.far_data, sc_static); symtab_addsymbol (current_symtab, module_sym); module = &D_STRUCT (pr_module_t, module_sym->s.def); - module->size = type_size (&type_obj_module); + module->size = type_size (&type_module); EMIT_STRING (module_sym->s.def->space, module->name, GETSTR (pr.source_file)); EMIT_DEF (module_sym->s.def->space, module->symtab, symtab_def); @@ -1305,28 +1627,24 @@ class_finish_module (void) exec_class_sym = symtab_lookup (pr.symtab, "__obj_exec_class"); if (!exec_class_sym) { exec_class_sym = new_symbol_type ("__obj_exec_class", - &type_obj_exec_class); + &type_exec_class); exec_class_sym = function_symbol (exec_class_sym, 0, 1); make_function (exec_class_sym, 0, exec_class_sym->table->space, sc_extern); } - init_sym = new_symbol_type (".ctor", &type_function); + init_sym = new_symbol_type (".ctor", &type_func); init_sym = function_symbol (init_sym, 0, 1); + module_expr = address_expr (new_symbol_expr (module_sym), 0); + init_expr = new_block_expr (); append_expr (init_expr, - build_function_call (new_symbol_expr (exec_class_sym), - exec_class_sym->type, - address_expr (new_symbol_expr (module_sym), - 0, 0))); + build_function_call (new_symbol_expr (exec_class_sym), + exec_class_sym->type, module_expr)); - save_storage = current_storage; - current_storage = sc_static; - current_func = begin_function (init_sym, 0, current_symtab, 1); - build_code_function (init_sym, 0, init_expr);; - current_func = 0; - current_storage = save_storage; + current_func = begin_function (init_sym, 0, current_symtab, 1, sc_static); + build_code_function (init_sym, 0, init_expr); } protocol_t * @@ -1335,7 +1653,7 @@ get_protocol (const char *name, int create) protocol_t *p; if (!protocol_hash) - protocol_hash = Hash_NewTable (1021, protocol_get_key, 0, 0); + protocol_hash = Hash_NewTable (1021, protocol_get_key, 0, 0, 0); if (name) { p = Hash_Find (protocol_hash, name); @@ -1345,7 +1663,7 @@ get_protocol (const char *name, int create) p = calloc (sizeof (protocol_t), 1); p->name = name; - p->methods = new_methodlist (); + p->methods = 0; p->class_type.type = ct_protocol; p->class_type.c.protocol = p; if (name) @@ -1358,9 +1676,7 @@ protocol_add_methods (protocol_t *protocol, methodlist_t *methods) { if (!methods) return; - *protocol->methods->tail = methods->head; - protocol->methods->tail = methods->tail; - free (methods); + merge_method_lists (protocol->methods, methods); } void @@ -1372,8 +1688,11 @@ protocol_add_protocols (protocol_t *protocol, protocollist_t *protocols) def_t * protocol_def (protocol_t *protocol) { - return make_symbol (protocol->name, &type_obj_protocol, - pr.far_data, sc_static)->s.def; + if (!protocol->def) { + protocol->def = emit_protocol (protocol); + add_static_instance ("Protocol", protocol->def); + } + return protocol->def; } protocollist_t * @@ -1397,7 +1716,7 @@ add_protocol (protocollist_t *protocollist, const char *name) } protocollist->count++; protocollist->list = realloc (protocollist->list, - sizeof (protocol_t) * protocollist->count); + sizeof (protocol_t *) * protocollist->count); protocollist->list[protocollist->count - 1] = protocol; return protocollist; } @@ -1414,6 +1733,34 @@ procollist_find_protocol (protocollist_t *protocollist, protocol_t *proto) return 0; } +static method_t * +protocol_find_method (protocol_t *protocol, selector_t *selector, int instance) +{ + method_t *m = 0; + if (protocol->methods) { + m = methodlist_find_method (protocol->methods, selector, instance); + } + if (!m && protocol->protocols) { + return protocollist_find_method (protocol->protocols, selector, + instance); + } + return m; +} + +method_t * +protocollist_find_method (protocollist_t *protocollist, selector_t *selector, + int instance) +{ + method_t *m; + for (int i = 0; i < protocollist->count; i++) { + if ((m = protocol_find_method (protocollist->list[i], selector, + instance))) { + return m; + } + } + return 0; +} + int compare_protocols (protocollist_t *protos1, protocollist_t *protos2) { @@ -1448,8 +1795,8 @@ emit_protocol (protocol_t *protocol) pr_protocol_t *proto; defspace_t *space; - proto_def = make_symbol (va ("_OBJ_PROTOCOL_%s", protocol->name), - &type_obj_protocol, pr.far_data, sc_static)->s.def; + proto_def = make_symbol (va (0, "_OBJ_PROTOCOL_%s", protocol->name), + &type_protocol, pr.far_data, sc_static)->s.def; if (proto_def->initialized) return proto_def; proto_def->initialized = proto_def->constant = 1; @@ -1460,7 +1807,7 @@ emit_protocol (protocol_t *protocol) EMIT_STRING (space, proto->protocol_name, protocol->name); EMIT_DEF (space, proto->protocol_list, emit_protocol_list (protocol->protocols, - va ("PROTOCOL_%s", protocol->name))); + va (0, "PROTOCOL_%s", protocol->name))); EMIT_DEF (space, proto->instance_methods, emit_method_descriptions (protocol->methods, protocol->name, 1)); EMIT_DEF (space, proto->class_methods, @@ -1469,39 +1816,57 @@ emit_protocol (protocol_t *protocol) return proto_def; } +static void +emit_protocol_next (def_t *def, void *data, int index) +{ + if (!is_ptr(def->type)) { + internal_error (0, "%s: expected pointer def", __FUNCTION__); + } + D_INT (def) = 0; +} + +static void +emit_protocol_count (def_t *def, void *data, int index) +{ + protocollist_t *protocols = (protocollist_t *) data; + + if (!is_int(def->type)) { + internal_error (0, "%s: expected int def", __FUNCTION__); + } + D_INT (def) = protocols->count; +} + +static void +emit_protocol_list_item (def_t *def, void *data, int index) +{ + protocollist_t *protocols = (protocollist_t *) data; + protocol_t *protocol = protocols->list[index]; + + if (!is_array (def->type) || !is_ptr(def->type->t.array.type)) { + internal_error (0, "%s: expected array of pointer def", __FUNCTION__); + } + if (index < 0 || index >= protocols->count) { + internal_error (0, "%s: out of bounds index: %d %d", + __FUNCTION__, index, protocols->count); + } + EMIT_DEF (def->space, D_INT(def), protocol_def (protocol)); +} + def_t * emit_protocol_list (protocollist_t *protocols, const char *name) { - //FIXME use emit_struct static struct_def_t proto_list_struct[] = { - {"next", &type_pointer}, - {"count", &type_integer}, - {"list", 0}, // type will be filled in at run time + {"next", &type_ptr, emit_protocol_next}, + {"count", &type_int, emit_protocol_count}, + {"list", 0, emit_protocol_list_item}, {0, 0}, }; - type_t *proto_list_type; - def_t *proto_list_def; - defspace_t *space; - pr_protocol_list_t *proto_list; - int i; if (!protocols) return 0; - proto_list_struct[2].type = array_type (&type_pointer, protocols->count); - proto_list_type = make_structure (0, 's', proto_list_struct, 0)->type; - proto_list_def = make_symbol (va ("_OBJ_PROTOCOLS_%s", name), - proto_list_type, - pr.far_data, sc_static)->s.def; - proto_list_def->initialized = proto_list_def->constant = 1; - proto_list_def->nosave = 1; - space = proto_list_def->space; - proto_list = &D_STRUCT (pr_protocol_list_t, proto_list_def); - proto_list->next = 0; - proto_list->count = protocols->count; - for (i = 0; i < protocols->count; i++) - EMIT_DEF (space, proto_list->list[i], - emit_protocol (protocols->list[i])); - return proto_list_def; + proto_list_struct[2].type = array_type (&type_ptr, protocols->count); + return emit_structure (va (0, "_OBJ_PROTOCOLS_%s", name), 's', + proto_list_struct, 0, protocols, 0, sc_static); } void @@ -1513,6 +1878,10 @@ clear_classes (void) Hash_FlushTable (protocol_hash); if (category_hash) Hash_FlushTable (category_hash); + if (static_instances) + Hash_FlushTable (static_instances); + if (static_instance_classes) + Hash_FlushTable (static_instance_classes); obj_initialized = 0; } @@ -1534,7 +1903,7 @@ class_to_struct (class_t *class, symtab_t *symtab) // connect the ivars symbol table chain to the struct symbol table ancestor->parent = symtab; // create a new struct symbol table from the ivars symbol table chain - symtab = symtab_flat_copy (ivars, 0); + symtab = symtab_flat_copy (ivars, 0, stab_struct); // disconnect the ivars symbol table chain ancestor->parent = 0; // connect the new struct symbol table to the scope @@ -1548,7 +1917,16 @@ class_ivar_scope (class_type_t *class_type, symtab_t *parent) class_t *class = extract_class (class_type); if (!class->ivars) return 0; - return symtab_flat_copy (class->ivars, parent); + return symtab_flat_copy (class->ivars, parent, stab_ivars); +} + +static expr_t * +class_dereference_ivar (symbol_t *sym, void *_self) +{ + expr_t *self = (expr_t *) _self; + + return field_expr (copy_expr (self), + new_symbol_expr (new_symbol (sym->name))); } void @@ -1564,8 +1942,9 @@ class_finish_ivar_scope (class_type_t *class_type, symtab_t *ivar_scope, if (!ivar_scope) return; self = symtab_lookup (param_scope, "self"); - if (!self) + if (!self) { internal_error (0, "I've lost my self!"); + } self_expr = new_symbol_expr (self); if (self->type != class_ptr) { debug (0, "class method scope"); @@ -1575,35 +1954,35 @@ class_finish_ivar_scope (class_type_t *class_type, symtab_t *ivar_scope, for (sym = ivar_scope->symbols; sym; sym = sym->next) { if (sym->sy_type != sy_var) continue; - sym->sy_type = sy_expr; - sym->s.expr = field_expr (copy_expr (self_expr), - new_symbol_expr (new_symbol (sym->name))); + sym->sy_type = sy_convert; + sym->s.convert.conv = class_dereference_ivar; + sym->s.convert.data = self_expr; } } static void init_objective_structs (void) { - make_structure ("obj_selector", 's', sel_struct, &type_obj_selector); - chain_type (&type_obj_selector); + make_structure ("obj_selector", 's', sel_struct, &type_selector); + chain_type (&type_selector); chain_type (&type_SEL); chain_type (&type_IMP); - make_structure ("obj_method", 's', method_struct, &type_obj_method); - chain_type (&type_obj_method); + make_structure ("obj_method", 's', method_struct, &type_method); + chain_type (&type_method); make_structure ("obj_method_description", 's', method_desc_struct, - &type_obj_method_description); - chain_type (&type_obj_method_description); + &type_method_description); + chain_type (&type_method_description); - make_structure ("obj_category", 's', category_struct, &type_obj_category); - chain_type (&type_obj_category); + make_structure ("obj_category", 's', category_struct, &type_category); + chain_type (&type_category); - make_structure ("obj_ivar", 's', ivar_struct, &type_obj_ivar); - chain_type (&type_obj_ivar); + make_structure ("obj_ivar", 's', ivar_struct, &type_ivar); + chain_type (&type_ivar); - make_structure ("obj_super", 's', super_struct, &type_obj_super); - chain_type (&type_obj_super); + make_structure ("obj_super", 's', super_struct, &type_super); + chain_type (&type_super); chain_type (&type_SuperPtr); chain_type (&type_supermsg); @@ -1613,24 +1992,24 @@ init_objective_structs (void) static void init_classes (void) { - make_structure ("obj_class", 's', class_struct, &type_obj_class); - chain_type (&type_obj_class); + make_structure ("obj_class", 's', class_struct, &type_class); + chain_type (&type_class); chain_type (&type_Class); - make_structure ("obj_object", 's', object_struct, &type_obj_object); - chain_type (&type_obj_object); + make_structure ("obj_object", 's', object_struct, &type_object); + chain_type (&type_object); chain_type (&type_id); - make_structure ("obj_protocol", 's', protocol_struct, &type_obj_protocol); - chain_type (&type_obj_protocol); + make_structure ("obj_protocol", 's', protocol_struct, &type_protocol); + chain_type (&type_protocol); } static void class_init_obj_module (void) { - make_structure ("obj_module", 's', module_struct, &type_obj_module); + make_structure ("obj_module", 's', module_struct, &type_module); - chain_type (&type_obj_module); + chain_type (&type_module); chain_type (&type_moduleptr); - chain_type (&type_obj_exec_class); + chain_type (&type_exec_class); } void diff --git a/tools/qfcc/source/codespace.c b/tools/qfcc/source/codespace.c index 797bc5707..5d6aee439 100644 --- a/tools/qfcc/source/codespace.c +++ b/tools/qfcc/source/codespace.c @@ -39,9 +39,9 @@ #endif #include -#include "QF/pr_comp.h" +#include "QF/progs/pr_comp.h" -#include "codespace.h" +#include "tools/qfcc/include/codespace.h" codespace_t * codespace_new (void) diff --git a/tools/qfcc/source/constfold.c b/tools/qfcc/source/constfold.c index 799b2ff0f..18c3e1218 100644 --- a/tools/qfcc/source/constfold.c +++ b/tools/qfcc/source/constfold.c @@ -42,25 +42,20 @@ #include #include -#include "diagnostic.h" -#include "expr.h" -#include "options.h" -#include "qfcc.h" -#include "strpool.h" -#include "type.h" -#include "qc-parse.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" + +#include "tools/qfcc/source/qc-parse.h" typedef expr_t *(*operation_t) (int op, expr_t *e, expr_t *e1, expr_t *e2); typedef expr_t *(*unaryop_t) (int op, expr_t *e, expr_t *e1); -static expr_t * -cf_cast_expr (type_t *type, expr_t *e) -{ - e = cast_expr (type, e); - return e; -} - -static int +static __attribute__((pure)) int valid_op (int op, int *valid_ops) { while (*valid_ops && op != *valid_ops) @@ -68,26 +63,48 @@ valid_op (int op, int *valid_ops) return *valid_ops == op; } +static expr_t * +cmp_result_expr (int result) +{ + if (is_float (type_default)) { + return new_float_expr (result); + } else { + return new_int_expr(result); + } +} + +static int +is_addsub (int op) +{ + return op == '+' || op == '-'; +} + +static int +inv_addsub (int op) +{ + return op == '+' ? '-' : '+'; +} + static expr_t * do_op_string (int op, expr_t *e, expr_t *e1, expr_t *e2) { const char *s1, *s2; static dstring_t *temp_str; - static int valid[] = {'=', '+', LT, GT, LE, GE, EQ, NE, 0}; + static int valid[] = {'+', LT, GT, LE, GE, EQ, NE, 0}; if (!valid_op (op, valid)) return error (e1, "invalid operand for string"); if (is_compare (op) || is_logic (op)) { if (options.code.progsversion > PROG_ID_VERSION) - e->e.expr.type = &type_integer; + e->e.expr.type = &type_int; else e->e.expr.type = &type_float; } else { e->e.expr.type = &type_string; } - if (op == '=' || !is_constant (e1) || !is_constant (e2)) + if (!is_constant (e1) || !is_constant (e2)) return e; s1 = expr_string (e1); @@ -107,22 +124,22 @@ do_op_string (int op, expr_t *e, expr_t *e1, expr_t *e2) e = new_string_expr (save_string (temp_str->str)); break; case LT: - e = new_integer_expr (strcmp (s1, s2) < 0); + e = cmp_result_expr (strcmp (s1, s2) < 0); break; case GT: - e = new_integer_expr (strcmp (s1, s2) > 0); + e = cmp_result_expr (strcmp (s1, s2) > 0); break; case LE: - e = new_integer_expr (strcmp (s1, s2) <= 0); + e = cmp_result_expr (strcmp (s1, s2) <= 0); break; case GE: - e = new_integer_expr (strcmp (s1, s2) >= 0); + e = cmp_result_expr (strcmp (s1, s2) >= 0); break; case EQ: - e = new_integer_expr (strcmp (s1, s2) == 0); + e = cmp_result_expr (strcmp (s1, s2) == 0); break; case NE: - e = new_integer_expr (strcmp (s1, s2)); + e = cmp_result_expr (strcmp (s1, s2)); break; default: internal_error (e1, 0); @@ -135,32 +152,25 @@ do_op_string (int op, expr_t *e, expr_t *e1, expr_t *e2) static expr_t * convert_to_float (expr_t *e) { - if (get_type (e) == &type_float) + if (is_float(get_type (e))) return e; - switch (e->type) { - case ex_value: - switch (e->e.value->type) { - case ev_integer: - convert_int (e); - return e; - case ev_short: - convert_short (e); - return e; - default: - internal_error (e, 0); - } - break; - case ex_symbol: - case ex_expr: - case ex_uexpr: - case ex_temp: - case ex_block: - e = cf_cast_expr (&type_float, e); - return e; - default: - internal_error (e, 0); - } + expr_t *n = cast_expr (&type_float, e); + n->file = e->file; + n->line = e->line; + return n; +} + +static expr_t * +convert_to_double (expr_t *e) +{ + if (is_double(get_type (e))) + return e; + + expr_t *n = cast_expr (&type_double, e); + n->file = e->file; + n->line = e->line; + return n; } static expr_t * @@ -170,31 +180,22 @@ do_op_float (int op, expr_t *e, expr_t *e1, expr_t *e2) expr_t *conv; type_t *type = &type_float; static int valid[] = { - '=', '+', '-', '*', '/', '&', '|', '^', '%', + '+', '-', '*', '/', '&', '|', '^', '%', SHL, SHR, AND, OR, LT, GT, LE, GE, EQ, NE, 0 }; if (!valid_op (op, valid)) return error (e1, "invalid operator for float"); - if (op == '=' || op == PAS) { - if ((type = get_type (e1)) != &type_float) { - //FIXME optimize casting a constant - e->e.expr.e2 = e2 = cf_cast_expr (type, e2); - } else if ((conv = convert_to_float (e2)) != e2) { - e->e.expr.e2 = e2 = conv; - } - } else { - if ((conv = convert_to_float (e1)) != e1) { - e->e.expr.e1 = e1 = conv; - } - if ((conv = convert_to_float (e2)) != e2) { - e->e.expr.e2 = e2 = conv; - } + if ((conv = convert_to_float (e1)) != e1) { + e->e.expr.e1 = e1 = conv; + } + if ((conv = convert_to_float (e2)) != e2) { + e->e.expr.e2 = e2 = conv; } if (is_compare (op) || is_logic (op)) { if (options.code.progsversion > PROG_ID_VERSION) - type = &type_integer; + type = &type_int; else type = &type_float; } @@ -221,7 +222,7 @@ do_op_float (int op, expr_t *e, expr_t *e1, expr_t *e2) if (op == '-' && is_constant (e2) && expr_float (e2) == 0) return e1; - if (op == '=' || !is_constant (e1) || !is_constant (e2)) + if (!is_constant (e1) || !is_constant (e2)) return e; f1 = expr_float (e1); @@ -261,28 +262,124 @@ do_op_float (int op, expr_t *e, expr_t *e1, expr_t *e2) e = new_float_expr ((int)f1 >> (int)f2); break; case AND: - e = new_integer_expr (f1 && f2); + e = cmp_result_expr (f1 && f2); break; case OR: - e = new_integer_expr (f1 || f2); + e = cmp_result_expr (f1 || f2); break; case LT: - e = new_integer_expr (f1 < f2); + e = cmp_result_expr (f1 < f2); break; case GT: - e = new_integer_expr (f1 > f2); + e = cmp_result_expr (f1 > f2); break; case LE: - e = new_integer_expr (f1 <= f2); + e = cmp_result_expr (f1 <= f2); break; case GE: - e = new_integer_expr (f1 >= f2); + e = cmp_result_expr (f1 >= f2); break; case EQ: - e = new_integer_expr (f1 == f2); + e = cmp_result_expr (f1 == f2); break; case NE: - e = new_integer_expr (f1 != f2); + e = cmp_result_expr (f1 != f2); + break; + default: + internal_error (e1, 0); + } + e->file = e1->file; + e->line = e1->line; + return e; +} + +static expr_t * +do_op_double (int op, expr_t *e, expr_t *e1, expr_t *e2) +{ + double d1, d2; + expr_t *conv; + type_t *type = &type_double; + static int valid[] = { + '+', '-', '*', '/', '%', + LT, GT, LE, GE, EQ, NE, 0 + }; + + if (!valid_op (op, valid)) + return error (e1, "invalid operator for double"); + + if ((conv = convert_to_double (e1)) != e1) { + e->e.expr.e1 = e1 = conv; + } + if ((conv = convert_to_double (e2)) != e2) { + e->e.expr.e2 = e2 = conv; + } + if (is_compare (op) || is_logic (op)) { + type = &type_int; + } + e->e.expr.type = type; + + if (op == '*' && is_constant (e1) && expr_double (e1) == 1) + return e2; + if (op == '*' && is_constant (e2) && expr_double (e2) == 1) + return e1; + if (op == '*' && is_constant (e1) && expr_double (e1) == 0) + return e1; + if (op == '*' && is_constant (e2) && expr_double (e2) == 0) + return e2; + if (op == '/' && is_constant (e2) && expr_double (e2) == 1) + return e1; + if (op == '/' && is_constant (e2) && expr_double (e2) == 0) + return error (e, "division by zero"); + if (op == '/' && is_constant (e1) && expr_double (e1) == 0) + return e1; + if (op == '+' && is_constant (e1) && expr_double (e1) == 0) + return e2; + if (op == '+' && is_constant (e2) && expr_double (e2) == 0) + return e1; + if (op == '-' && is_constant (e2) && expr_double (e2) == 0) + return e1; + + if (!is_constant (e1) || !is_constant (e2)) + return e; + + d1 = expr_double (e1); + d2 = expr_double (e2); + + switch (op) { + case '+': + e = new_double_expr (d1 + d2); + break; + case '-': + e = new_double_expr (d1 - d2); + break; + case '*': + e = new_double_expr (d1 * d2); + break; + case '/': + if (!d2) + return error (e1, "divide by zero"); + e = new_double_expr (d1 / d2); + break; + case '%': + e = new_double_expr ((int)d1 % (int)d2); + break; + case LT: + e = cmp_result_expr (d1 < d2); + break; + case GT: + e = cmp_result_expr (d1 > d2); + break; + case LE: + e = cmp_result_expr (d1 <= d2); + break; + case GE: + e = cmp_result_expr (d1 >= d2); + break; + case EQ: + e = cmp_result_expr (d1 == d2); + break; + case NE: + e = cmp_result_expr (d1 != d2); break; default: internal_error (e1, 0); @@ -297,32 +394,34 @@ do_op_vector (int op, expr_t *e, expr_t *e1, expr_t *e2) { const float *v1, *v2; vec3_t v, float_vec; - static int valid[] = {'=', '+', '-', '*', EQ, NE, 0}; + static int vv_valid[] = {'+', '-', DOT, HADAMARD, EQ, NE, 0}; + static int vs_valid[] = {SCALE, 0}; + static int sv_valid[] = {SCALE, '/', 0}; expr_t *t; - if (get_type (e1) != &type_vector) { + if (!is_vector(get_type (e1))) { - if (op != '*') - return error (e1, "invalid operator for vector"); + if (!valid_op (op, sv_valid)) + return error (e1, "invalid operator for scalar-vector"); t = e1; e->e.expr.e1 = e1 = e2; e2 = t; } - if (get_type (e2) != &type_vector) { + if (!is_vector(get_type (e2))) { e->e.expr.e2 = e2 = convert_to_float (e2); - if (op != '*' && op != '/') + if (!valid_op (op, vs_valid)) return error (e1, "invalid operator for vector"); } else { - if (!valid_op (op, valid)) + if (!valid_op (op, vv_valid)) return error (e1, "invalid operator for vector"); } if (is_compare (op) || is_logic (op)) { if (options.code.progsversion > PROG_ID_VERSION) - e->e.expr.type = &type_integer; + e->e.expr.type = &type_int; else e->e.expr.type = &type_float; - } else if (op == '*' && get_type (e2) == &type_vector) { + } else if (op == DOT && is_vector(get_type (e2))) { e->e.expr.type = &type_float; } else if (op == '/' && !is_constant (e1)) { e2 = fold_constants (binary_expr ('/', new_float_expr (1), e2)); @@ -331,9 +430,9 @@ do_op_vector (int op, expr_t *e, expr_t *e1, expr_t *e2) e->e.expr.type = &type_vector; } - if (op == '*' && is_float_val (e2) && expr_float (e2) == 1) + if (op == SCALE && is_float_val (e2) && expr_float (e2) == 1) return e1; - if (op == '*' && is_float_val (e2) && expr_float (e2) == 0) + if (op == SCALE && is_float_val (e2) && expr_float (e2) == 0) return new_vector_expr (vec3_origin); if (op == '/' && is_float_val (e2) && expr_float (e2) == 1) return e1; @@ -355,7 +454,7 @@ do_op_vector (int op, expr_t *e, expr_t *e1, expr_t *e2) if (op == '-' && is_constant (e2) && VectorIsZero (expr_vector (e2))) return e1; - if (op == '=' || !is_constant (e1) || !is_constant (e2)) + if (!is_constant (e1) || !is_constant (e2)) return e; if (is_float_val (e1)) { @@ -386,19 +485,18 @@ do_op_vector (int op, expr_t *e, expr_t *e1, expr_t *e2) VectorScale (v1, 1 / v2[0], v); e = new_vector_expr (v); break; - case '*': - if (get_type (e2) == &type_vector) { - e = new_float_expr (DotProduct (v1, v2)); - } else { - VectorScale (v1, v2[0], v); - e = new_vector_expr (v); - } + case DOT: + e = new_float_expr (DotProduct (v1, v2)); + break; + case SCALE: + VectorScale (v1, v2[0], v); + e = new_vector_expr (v); break; case EQ: - e = new_integer_expr (VectorCompare (v1, v2)); + e = cmp_result_expr (VectorCompare (v1, v2)); break; case NE: - e = new_integer_expr (!VectorCompare (v1, v2)); + e = cmp_result_expr (!VectorCompare (v1, v2)); break; default: internal_error (e1, 0); @@ -413,17 +511,17 @@ do_op_entity (int op, expr_t *e, expr_t *e1, expr_t *e2) { type_t *type = get_type (e2); - if ((op == '.' || op == '&') && type->type == ev_field) { + if (op == '.' && type->type == ev_field) { return e; } if (op == EQ || op == NE) { if (options.code.progsversion > PROG_ID_VERSION) - e->e.expr.type = &type_integer; + e->e.expr.type = &type_int; else e->e.expr.type = &type_float; return e; } - if (op != '=' || type != &type_entity) + if (!is_entity(type)) return error (e1, "invalid operator for entity"); e->e.expr.type = &type_entity; return e; @@ -432,37 +530,27 @@ do_op_entity (int op, expr_t *e, expr_t *e1, expr_t *e2) static expr_t * do_op_field (int op, expr_t *e, expr_t *e1, expr_t *e2) { - if (op != '=') - return error (e1, "invalid operator for field"); - e->e.expr.type = &type_field; - return e; + return error (e1, "invalid operator for field"); } static expr_t * do_op_func (int op, expr_t *e, expr_t *e1, expr_t *e2) { - if (op == 'c') { - e->e.expr.type = get_type (e1)->t.func.type; - return e; - } if (op == EQ || op == NE) { if (options.code.progsversion > PROG_ID_VERSION) - e->e.expr.type = &type_integer; + e->e.expr.type = &type_int; else e->e.expr.type = &type_float; return e; } - if (op != '=') - return error (e1, "invalid operator for func"); - e->e.expr.type = &type_function; - return e; + return error (e1, "invalid operator for func"); } static expr_t * do_op_pointer (int op, expr_t *e, expr_t *e1, expr_t *e2) { type_t *type; - static int valid[] = {'=', PAS, '-', '&', 'M', '.', EQ, NE, 0}; + static int valid[] = {'-', '.', EQ, NE, 0}; if (is_integral (type = get_type (e2)) && (op == '-' || op == '+')) { // pointer arithmetic @@ -479,37 +567,23 @@ do_op_pointer (int op, expr_t *e, expr_t *e1, expr_t *e2) type = get_type (e1); if (type != get_type (e2)) return error (e2, "invalid operands to binary -"); - e1 = new_alias_expr (&type_integer, e1); - e2 = new_alias_expr (&type_integer, e2); + e1 = new_alias_expr (&type_int, e1); + e2 = new_alias_expr (&type_int, e2); e = binary_expr ('-', e1, e2); if (type_size (type) != 1) - e = binary_expr ('/', e, new_integer_expr (type_size (type))); + e = binary_expr ('/', e, new_int_expr (type_size (type))); return e; } - if (op == PAS && (type = get_type (e1)->t.fldptr.type) != get_type (e2)) { - // make sure auto-convertions happen - expr_t *tmp = new_temp_def_expr (type); - expr_t *ass = new_binary_expr ('=', tmp, e2); - - tmp->file = e1->file; - ass->line = e2->line; - ass->file = e2->file; - ass = fold_constants (ass); - if (e->e.expr.e2 == tmp) - internal_error (e2, 0); - e->e.expr.e2 = ass->e.expr.e2; - } if (op == EQ || op == NE) { if (options.code.progsversion > PROG_ID_VERSION) - e->e.expr.type = &type_integer; + e->e.expr.type = &type_int; else e->e.expr.type = &type_float; } - if (op != PAS && op != '.' && op != '&' && op != 'M' - && extract_type (e1) != extract_type (e2)) + if (op != '.' && extract_type (e1) != extract_type (e2)) return type_mismatch (e1, e2, op); - if ((op == '.' || op == '&') && get_type (e2) == &type_uinteger) - e->e.expr.e2 = cf_cast_expr (&type_integer, e2); + if (op == '.' && is_uint(get_type (e2))) + e->e.expr.e2 = cast_expr (&type_int, e2); return e; } @@ -540,10 +614,10 @@ do_op_quaternion (int op, expr_t *e, expr_t *e1, expr_t *e2) { const float *q1, *q2; quat_t q, float_quat; - static int valid[] = {'=', '+', '-', '*', EQ, NE, 0}; + static int valid[] = {'+', '-', '*', EQ, NE, 0}; expr_t *t; - if (get_type (e1) != &type_quaternion) { + if (!is_quaternion(get_type (e1))) { if (op != '*' && op != '/') return error (e1, "invalid operator for quaternion"); @@ -554,7 +628,7 @@ do_op_quaternion (int op, expr_t *e, expr_t *e1, expr_t *e2) e2 = t; } } - if (get_type (e2) != &type_quaternion) { + if (!is_quaternion(get_type (e2))) { e->e.expr.e2 = e2 = convert_to_float (e2); if (op != '*' && op != '/') return error (e1, "invalid operator for quaternion"); @@ -564,7 +638,7 @@ do_op_quaternion (int op, expr_t *e, expr_t *e1, expr_t *e2) } if (is_compare (op) || is_logic (op)) { if (options.code.progsversion > PROG_ID_VERSION) - e->e.expr.type = &type_integer; + e->e.expr.type = &type_int; else e->e.expr.type = &type_float; } else if (op == '/' && !is_constant (e1)) { @@ -589,15 +663,15 @@ do_op_quaternion (int op, expr_t *e, expr_t *e1, expr_t *e2) if (op == '-' && is_constant (e2) && QuatIsZero (expr_quaternion (e2))) return e1; - if (op == '=' || !is_constant (e1) || !is_constant (e2)) + if (!is_constant (e1) || !is_constant (e2)) return e; if (is_float_val (e1)) { - float_quat[0] = expr_float (e1); + QuatSet (0, 0, 0, expr_float (e1), float_quat); q2 = float_quat; q1 = expr_quaternion (e2); } else if (is_float_val (e2)) { - float_quat[0] = expr_float (e2); + QuatSet (0, 0, 0, expr_float (e2), float_quat); q2 = float_quat; q1 = expr_quaternion (e1); } else { @@ -624,18 +698,18 @@ do_op_quaternion (int op, expr_t *e, expr_t *e1, expr_t *e2) e = new_quaternion_expr (q); break; case '*': - if (get_type (e2) == &type_quaternion) { + if (is_quaternion(get_type (e2))) { QuatMult (q1, q2, q); } else { - QuatScale (q1, q2[0], q); + QuatScale (q1, q2[3], q); } e = new_quaternion_expr (q); break; case EQ: - e = new_integer_expr (QuatCompare (q1, q2)); + e = cmp_result_expr (QuatCompare (q1, q2)); break; case NE: - e = new_integer_expr (!QuatCompare (q1, q2)); + e = cmp_result_expr (!QuatCompare (q1, q2)); break; default: internal_error (e1, 0); @@ -646,115 +720,179 @@ do_op_quaternion (int op, expr_t *e, expr_t *e1, expr_t *e2) } static expr_t * -do_op_integer (int op, expr_t *e, expr_t *e1, expr_t *e2) +do_op_int (int op, expr_t *e, expr_t *e1, expr_t *e2) { - int i1, i2; + int isval1 = 0, isval2 = 0; + int val1 = 0, val2 = 0; static int valid[] = { - '=', '+', '-', '*', '/', '&', '|', '^', '%', + '+', '-', '*', '/', '&', '|', '^', '%', SHL, SHR, AND, OR, LT, GT, LE, GE, EQ, NE, 0 }; if (!valid_op (op, valid)) - return error (e1, "invalid operator for integer"); + return error (e1, "invalid operator for int"); - if (is_short_val (e1)) - convert_short_int (e1); + if (is_short_val (e1)) { + isval1 = 1; + val1 = expr_short (e1); + } + if (is_int_val (e1)) { + isval1 = 1; + val1 = expr_int (e1); + } - if (is_short_val (e2)) - convert_short_int (e2); + if (is_short_val (e2)) { + isval2 = 1; + val2 = expr_short (e2); + } + if (is_int_val (e2)) { + isval2 = 1; + val2 = expr_int (e2); + } if (is_compare (op) || is_logic (op)) { if (options.code.progsversion > PROG_ID_VERSION) - e->e.expr.type = &type_integer; + e->e.expr.type = &type_int; else e->e.expr.type = &type_float; } else { - e->e.expr.type = &type_integer; + e->e.expr.type = &type_int; } - if (op == '*' && is_constant (e1) && expr_integer (e1) == 1) + if (op == '*' && isval1 && val1 == 1) return e2; - if (op == '*' && is_constant (e2) && expr_integer (e2) == 1) + if (op == '*' && isval2 && val2 == 1) return e1; - if (op == '*' && is_constant (e1) && expr_integer (e1) == 0) + if (op == '*' && isval1 && val1 == 0) return e1; - if (op == '*' && is_constant (e2) && expr_integer (e2) == 0) + if (op == '*' && isval2 && val2 == 0) return e2; - if (op == '/' && is_constant (e2) && expr_integer (e2) == 1) + if (op == '/' && isval2 && val2 == 1) return e1; - if (op == '/' && is_constant (e2) && expr_integer (e2) == 0) + if (op == '/' && isval2 && val2 == 0) return error (e, "division by zero"); - if (op == '/' && is_constant (e1) && expr_integer (e1) == 0) + if (op == '/' && isval1 && val1 == 0) return e1; - if (op == '+' && is_constant (e1) && expr_integer (e1) == 0) + if (op == '+' && isval1 && val1 == 0) return e2; - if (op == '+' && is_constant (e2) && expr_integer (e2) == 0) + if (op == '+' && isval2 && val2 == 0) return e1; - if (op == '-' && is_constant (e2) && expr_integer (e2) == 0) + if (op == '-' && isval2 && val2 == 0) return e1; - if (op == '=' || !is_constant (e1) || !is_constant (e2)) + if (!isval1) { + if (e1->type == ex_expr) { + // at most one of the two sub-expressions is constant otherwise + // e1 would be a constant + if (is_constant (e1->e.expr.e1)) { + if ((op == '*' && e1->e.expr.op == '*') + || (is_addsub (op) && is_addsub (e1->e.expr.op))) { + expr_t *c = binary_expr (op, e1->e.expr.e1, e2); + e = binary_expr (e1->e.expr.op, c, e1->e.expr.e2); + } + } else if (is_constant (e1->e.expr.e2)) { + if ((op == '*' && e1->e.expr.op == '*') + || (is_addsub (op) && e1->e.expr.op == '+')) { + expr_t *c = binary_expr (op, e1->e.expr.e2, e2); + e = binary_expr (e1->e.expr.op, e1->e.expr.e1, c); + } else if (is_addsub (op) && e1->e.expr.op == '-') { + // must ivert op + expr_t *c = binary_expr (inv_addsub (op), + e1->e.expr.e2, e2); + e = binary_expr (e1->e.expr.op, e1->e.expr.e1, c); + } + } + } + return e; + } else if (!isval2) { + if (e2->type == ex_expr) { + // at most one of the two sub-expressions is constant otherwise + // e2 would be a constant + if (is_constant (e2->e.expr.e1)) { + if ((op == '*' && e2->e.expr.op == '*') + || (op == '+' && is_addsub (e2->e.expr.op))) { + expr_t *c = binary_expr (op, e1, e2->e.expr.e1); + e = binary_expr (e2->e.expr.op, c, e2->e.expr.e2); + } else if (op == '-' && is_addsub (e2->e.expr.op)) { + expr_t *c = binary_expr (op, e1, e2->e.expr.e1); + c = fold_constants (c); + e = binary_expr (inv_addsub (e2->e.expr.op), + c, e2->e.expr.e2); + } + } else if (is_constant (e2->e.expr.e2)) { + if ((op == '*' && e2->e.expr.op == '*') + || (op == '+' && is_addsub (e2->e.expr.op))) { + expr_t *c = binary_expr (e2->e.expr.op, + e1, e2->e.expr.e2); + e = binary_expr (op, c, e2->e.expr.e1); + } else if (op == '-' && is_addsub (e2->e.expr.op)) { + expr_t *c = binary_expr (inv_addsub (e2->e.expr.op), + e1, e2->e.expr.e2); + e = binary_expr (op, c, e2->e.expr.e1); + } + } + } + return e; + } + if (!isval1 || !isval2) return e; - - i1 = expr_integer (e1); - i2 = expr_integer (e2); switch (op) { case '+': - e = new_integer_expr (i1 + i2); + e = new_int_expr (val1 + val2); break; case '-': - e = new_integer_expr (i1 - i2); + e = new_int_expr (val1 - val2); break; case '*': - e = new_integer_expr (i1 * i2); + e = new_int_expr (val1 * val2); break; case '/': if (options.warnings.integer_divide) - warning (e2, "%d / %d == %d", i1, i2, i1 / i2); - e = new_integer_expr (i1 / i2); + warning (e2, "%d / %d == %d", val1, val2, val1 / val2); + e = new_int_expr (val1 / val2); break; case '&': - e = new_integer_expr (i1 & i2); + e = new_int_expr (val1 & val2); break; case '|': - e = new_integer_expr (i1 | i2); + e = new_int_expr (val1 | val2); break; case '^': - e = new_integer_expr (i1 ^ i2); + e = new_int_expr (val1 ^ val2); break; case '%': - e = new_integer_expr (i1 % i2); + e = new_int_expr (val1 % val2); break; case SHL: - e = new_integer_expr (i1 << i2); + e = new_int_expr (val1 << val2); break; case SHR: - e = new_integer_expr (i1 >> i2); + e = new_int_expr (val1 >> val2); break; case AND: - e = new_integer_expr (i1 && i2); + e = cmp_result_expr (val1 && val2); break; case OR: - e = new_integer_expr (i1 || i2); + e = cmp_result_expr (val1 || val2); break; case LT: - e = new_integer_expr (i1 < i2); + e = cmp_result_expr (val1 < val2); break; case GT: - e = new_integer_expr (i1 > i2); + e = cmp_result_expr (val1 > val2); break; case LE: - e = new_integer_expr (i1 <= i2); + e = cmp_result_expr (val1 <= val2); break; case GE: - e = new_integer_expr (i1 >= i2); + e = cmp_result_expr (val1 >= val2); break; case EQ: - e = new_integer_expr (i1 == i2); + e = cmp_result_expr (val1 == val2); break; case NE: - e = new_integer_expr (i1 != i2); + e = cmp_result_expr (val1 != val2); break; default: internal_error (e1, 0); @@ -765,7 +903,7 @@ do_op_integer (int op, expr_t *e, expr_t *e1, expr_t *e2) } static expr_t * -do_op_uinteger (int op, expr_t *e, expr_t *e1, expr_t *e2) +do_op_uint (int op, expr_t *e, expr_t *e1, expr_t *e2) { return e; } @@ -775,7 +913,7 @@ do_op_short (int op, expr_t *e, expr_t *e1, expr_t *e2) { short i1, i2; static int valid[] = { - '=', '+', '-', '*', '/', '&', '|', '^', '%', + '+', '-', '*', '/', '&', '|', '^', '%', SHL, SHR, AND, OR, LT, GT, LE, GE, EQ, NE, 0 }; @@ -784,14 +922,14 @@ do_op_short (int op, expr_t *e, expr_t *e1, expr_t *e2) if (is_compare (op) || is_logic (op)) { if (options.code.progsversion > PROG_ID_VERSION) - e->e.expr.type = &type_integer; + e->e.expr.type = &type_int; else e->e.expr.type = &type_float; } else { e->e.expr.type = &type_short; } - if (op == '=' || !is_constant (e1) || !is_constant (e2)) + if (!is_constant (e1) || !is_constant (e2)) return e; i1 = expr_short (e1); @@ -837,22 +975,22 @@ do_op_short (int op, expr_t *e, expr_t *e1, expr_t *e2) e = new_short_expr (i1 || i2); break; case LT: - e = new_integer_expr (i1 < i2); + e = cmp_result_expr (i1 < i2); break; case GT: - e = new_integer_expr (i1 > i2); + e = cmp_result_expr (i1 > i2); break; case LE: - e = new_integer_expr (i1 <= i2); + e = cmp_result_expr (i1 <= i2); break; case GE: - e = new_integer_expr (i1 >= i2); + e = cmp_result_expr (i1 >= i2); break; case EQ: - e = new_integer_expr (i1 == i2); + e = cmp_result_expr (i1 == i2); break; case NE: - e = new_integer_expr (i1 != i2); + e = cmp_result_expr (i1 != i2); break; default: internal_error (e1, 0); @@ -865,14 +1003,7 @@ do_op_short (int op, expr_t *e, expr_t *e1, expr_t *e2) static expr_t * do_op_struct (int op, expr_t *e, expr_t *e1, expr_t *e2) { - type_t *type; - - if (op != '=' && op != 'm') - return error (e1, "invalid operator for struct"); - if ((type = get_type (e1)) != get_type (e2)) - return type_mismatch (e1, e2, op); - e->e.expr.type = type; - return e; + return error (e1, "invalid operator for struct"); } static expr_t * @@ -882,16 +1013,22 @@ do_op_compound (int op, expr_t *e, expr_t *e1, expr_t *e2) type_t *t2 = get_type (e2); if (is_struct (t1) && is_struct (t2)) return do_op_struct (op, e, e1, e2); + if (is_union (t1) && is_union (t2)) + return do_op_struct (op, e, e1, e2); if (is_scalar (t1) && is_scalar (t2)) { if (is_enum (t1)) { - if (t2->type == ev_float) + if (t2->type == ev_double) return do_op_float (op, e, e1, e2); - return do_op_integer (op, e, e1, e2); + if (t2->type == ev_double) + return do_op_float (op, e, e1, e2); + return do_op_int (op, e, e1, e2); } if (is_enum (t2)) { + if (t1->type == ev_double) + return do_op_double (op, e, e1, e2); if (t1->type == ev_float) return do_op_float (op, e, e1, e2); - return do_op_integer (op, e, e1, e2); + return do_op_int (op, e, e1, e2); } } return error (e1, "invalid operator for compound"); @@ -904,8 +1041,6 @@ do_op_invalid (int op, expr_t *e, expr_t *e1, expr_t *e2) type_t *t1 = get_type (e1); type_t *t2 = get_type (e2); - if (e->e.expr.op == 'm') - return e; // assume the rest of the compiler got it right if (is_scalar (t1) && is_scalar (t2)) { // one or both expressions are an enum, and the other is one of // int, float or short. Treat the enum as the other type, or as @@ -935,227 +1070,288 @@ do_op_invalid (int op, expr_t *e, expr_t *e1, expr_t *e2) } static operation_t op_void[ev_type_count] = { - do_op_invalid, // ev_void - do_op_invalid, // ev_string - do_op_invalid, // ev_float - do_op_invalid, // ev_vector - do_op_invalid, // ev_entity - do_op_invalid, // ev_field - do_op_invalid, // ev_func - do_op_invalid, // ev_pointer - do_op_invalid, // ev_quaternion - do_op_invalid, // ev_integer - do_op_invalid, // ev_uinteger - do_op_invalid, // ev_short - do_op_invalid, // ev_invalid + [ev_void] = do_op_invalid, + [ev_string] = do_op_invalid, + [ev_float] = do_op_invalid, + [ev_vector] = do_op_invalid, + [ev_entity] = do_op_invalid, + [ev_field] = do_op_invalid, + [ev_func] = do_op_invalid, + [ev_ptr] = do_op_invalid, + [ev_quaternion] = do_op_invalid, + [ev_int] = do_op_invalid, + [ev_uint] = do_op_invalid, + [ev_short] = do_op_invalid, + [ev_double] = do_op_invalid, + [ev_long] = 0, + [ev_ulong] = 0, + [ev_invalid] = do_op_invalid, }; static operation_t op_string[ev_type_count] = { - do_op_invalid, // ev_void - do_op_string, // ev_string - do_op_invalid, // ev_float - do_op_invalid, // ev_vector - do_op_invalid, // ev_entity - do_op_invalid, // ev_field - do_op_invalid, // ev_func - do_op_invalid, // ev_pointer - do_op_invalid, // ev_quaternion - do_op_invalid, // ev_integer - do_op_invalid, // ev_uinteger - do_op_invalid, // ev_short - do_op_invalid, // ev_invalid + [ev_void] = do_op_invalid, + [ev_string] = do_op_string, + [ev_float] = do_op_invalid, + [ev_vector] = do_op_invalid, + [ev_entity] = do_op_invalid, + [ev_field] = do_op_invalid, + [ev_func] = do_op_invalid, + [ev_ptr] = do_op_invalid, + [ev_quaternion] = do_op_invalid, + [ev_int] = do_op_invalid, + [ev_uint] = do_op_invalid, + [ev_short] = do_op_invalid, + [ev_double] = do_op_invalid, + [ev_long] = 0, + [ev_ulong] = 0, + [ev_invalid] = do_op_invalid, }; static operation_t op_float[ev_type_count] = { - do_op_invalid, // ev_void - do_op_invalid, // ev_string - do_op_float, // ev_float - do_op_vector, // ev_vector - do_op_invalid, // ev_entity - do_op_invalid, // ev_field - do_op_invalid, // ev_func - do_op_invalid, // ev_pointer - do_op_quaternion, // ev_quaternion - do_op_float, // ev_integer - do_op_float, // ev_uinteger - do_op_float, // ev_short - do_op_invalid, // ev_invalid + [ev_void] = do_op_invalid, + [ev_string] = do_op_invalid, + [ev_float] = do_op_float, + [ev_vector] = do_op_vector, + [ev_entity] = do_op_invalid, + [ev_field] = do_op_invalid, + [ev_func] = do_op_invalid, + [ev_ptr] = do_op_invalid, + [ev_quaternion] = do_op_quaternion, + [ev_int] = do_op_float, + [ev_uint] = do_op_float, + [ev_short] = do_op_float, + [ev_double] = do_op_double, + [ev_long] = 0, + [ev_ulong] = 0, + [ev_invalid] = do_op_invalid, }; static operation_t op_vector[ev_type_count] = { - do_op_invalid, // ev_void - do_op_invalid, // ev_string - do_op_vector, // ev_float - do_op_vector, // ev_vector - do_op_invalid, // ev_entity - do_op_invalid, // ev_field - do_op_invalid, // ev_func - do_op_invalid, // ev_pointer - do_op_invalid, // ev_quaternion - do_op_vector, // ev_integer - do_op_vector, // ev_uinteger - do_op_vector, // ev_short - do_op_invalid, // ev_invalid + [ev_void] = do_op_invalid, + [ev_string] = do_op_invalid, + [ev_float] = do_op_vector, + [ev_vector] = do_op_vector, + [ev_entity] = do_op_invalid, + [ev_field] = do_op_invalid, + [ev_func] = do_op_invalid, + [ev_ptr] = do_op_invalid, + [ev_quaternion] = do_op_invalid, + [ev_int] = do_op_vector, + [ev_uint] = do_op_vector, + [ev_short] = do_op_vector, + [ev_double] = do_op_vector, + [ev_long] = 0, + [ev_ulong] = 0, + [ev_invalid] = do_op_invalid, }; static operation_t op_entity[ev_type_count] = { - do_op_invalid, // ev_void - do_op_invalid, // ev_string - do_op_invalid, // ev_float - do_op_invalid, // ev_vector - do_op_entity, // ev_entity - do_op_entity, // ev_field - do_op_invalid, // ev_func - do_op_invalid, // ev_pointer - do_op_invalid, // ev_quaternion - do_op_invalid, // ev_integer - do_op_invalid, // ev_uinteger - do_op_invalid, // ev_short - do_op_invalid, // ev_invalid + [ev_void] = do_op_invalid, + [ev_string] = do_op_invalid, + [ev_float] = do_op_invalid, + [ev_vector] = do_op_invalid, + [ev_entity] = do_op_entity, + [ev_field] = do_op_entity, + [ev_func] = do_op_invalid, + [ev_ptr] = do_op_invalid, + [ev_quaternion] = do_op_invalid, + [ev_int] = do_op_invalid, + [ev_uint] = do_op_invalid, + [ev_short] = do_op_invalid, + [ev_double] = do_op_invalid, + [ev_long] = 0, + [ev_ulong] = 0, + [ev_invalid] = do_op_invalid, }; static operation_t op_field[ev_type_count] = { - do_op_invalid, // ev_void - do_op_invalid, // ev_string - do_op_invalid, // ev_float - do_op_invalid, // ev_vector - do_op_invalid, // ev_entity - do_op_field, // ev_field - do_op_invalid, // ev_func - do_op_invalid, // ev_pointer - do_op_invalid, // ev_quaternion - do_op_invalid, // ev_integer - do_op_invalid, // ev_uinteger - do_op_invalid, // ev_short - do_op_invalid, // ev_invalid + [ev_void] = do_op_invalid, + [ev_string] = do_op_invalid, + [ev_float] = do_op_invalid, + [ev_vector] = do_op_invalid, + [ev_entity] = do_op_invalid, + [ev_field] = do_op_field, + [ev_func] = do_op_invalid, + [ev_ptr] = do_op_invalid, + [ev_quaternion] = do_op_invalid, + [ev_int] = do_op_invalid, + [ev_uint] = do_op_invalid, + [ev_short] = do_op_invalid, + [ev_double] = do_op_invalid, + [ev_long] = 0, + [ev_ulong] = 0, + [ev_invalid] = do_op_invalid, }; static operation_t op_func[ev_type_count] = { - do_op_func, // ev_void - do_op_func, // ev_string - do_op_func, // ev_float - do_op_func, // ev_vector - do_op_func, // ev_entity - do_op_func, // ev_field - do_op_func, // ev_func - do_op_func, // ev_pointer - do_op_func, // ev_quaternion - do_op_func, // ev_integer - do_op_func, // ev_uinteger - do_op_func, // ev_short - do_op_func, // ev_invalid + [ev_void] = do_op_func, + [ev_string] = do_op_func, + [ev_float] = do_op_func, + [ev_vector] = do_op_func, + [ev_entity] = do_op_func, + [ev_field] = do_op_func, + [ev_func] = do_op_func, + [ev_ptr] = do_op_func, + [ev_quaternion] = do_op_func, + [ev_int] = do_op_func, + [ev_uint] = do_op_func, + [ev_short] = do_op_func, + [ev_double] = do_op_func, + [ev_long] = 0, + [ev_ulong] = 0, + [ev_invalid] = do_op_func, }; static operation_t op_pointer[ev_type_count] = { - do_op_pointer, // ev_void - do_op_pointer, // ev_string - do_op_pointer, // ev_float - do_op_pointer, // ev_vector - do_op_pointer, // ev_entity - do_op_pointer, // ev_field - do_op_pointer, // ev_func - do_op_pointer, // ev_pointer - do_op_pointer, // ev_quaternion - do_op_pointer, // ev_integer - do_op_pointer, // ev_uinteger - do_op_pointer, // ev_short - do_op_pointer, // ev_invalid + [ev_void] = do_op_pointer, + [ev_string] = do_op_pointer, + [ev_float] = do_op_pointer, + [ev_vector] = do_op_pointer, + [ev_entity] = do_op_pointer, + [ev_field] = do_op_pointer, + [ev_func] = do_op_pointer, + [ev_ptr] = do_op_pointer, + [ev_quaternion] = do_op_pointer, + [ev_int] = do_op_pointer, + [ev_uint] = do_op_pointer, + [ev_short] = do_op_pointer, + [ev_double] = do_op_pointer, + [ev_long] = 0, + [ev_ulong] = 0, + [ev_invalid] = do_op_pointer, }; static operation_t op_quaternion[ev_type_count] = { - do_op_invalid, // ev_void - do_op_invalid, // ev_string - do_op_quaternion, // ev_float - do_op_quatvect, // ev_vector - do_op_invalid, // ev_entity - do_op_invalid, // ev_field - do_op_invalid, // ev_func - do_op_invalid, // ev_pointer - do_op_quaternion, // ev_quaternion - do_op_quaternion, // ev_integer - do_op_quaternion, // ev_uinteger - do_op_quaternion, // ev_short - do_op_invalid, // ev_invalid + [ev_void] = do_op_invalid, + [ev_string] = do_op_invalid, + [ev_float] = do_op_quaternion, + [ev_vector] = do_op_quatvect, + [ev_entity] = do_op_invalid, + [ev_field] = do_op_invalid, + [ev_func] = do_op_invalid, + [ev_ptr] = do_op_invalid, + [ev_quaternion] = do_op_quaternion, + [ev_int] = do_op_quaternion, + [ev_uint] = do_op_quaternion, + [ev_short] = do_op_quaternion, + [ev_double] = do_op_quaternion, + [ev_long] = 0, + [ev_ulong] = 0, + [ev_invalid] = do_op_invalid, }; -static operation_t op_integer[ev_type_count] = { - do_op_invalid, // ev_void - do_op_invalid, // ev_string - do_op_float, // ev_float - do_op_vector, // ev_vector - do_op_invalid, // ev_entity - do_op_invalid, // ev_field - do_op_invalid, // ev_func - do_op_invalid, // ev_pointer - do_op_quaternion, // ev_quaternion - do_op_integer, // ev_integer - do_op_uinteger, // ev_uinteger - do_op_integer, // ev_short - do_op_invalid, // ev_invalid +static operation_t op_int[ev_type_count] = { + [ev_void] = do_op_invalid, + [ev_string] = do_op_invalid, + [ev_float] = do_op_float, + [ev_vector] = do_op_vector, + [ev_entity] = do_op_invalid, + [ev_field] = do_op_invalid, + [ev_func] = do_op_invalid, + [ev_ptr] = do_op_invalid, + [ev_quaternion] = do_op_quaternion, + [ev_int] = do_op_int, + [ev_uint] = do_op_uint, + [ev_short] = do_op_int, + [ev_double] = do_op_double, + [ev_long] = 0, + [ev_ulong] = 0, + [ev_invalid] = do_op_invalid, }; -static operation_t op_uinteger[ev_type_count] = { - do_op_invalid, // ev_void - do_op_invalid, // ev_string - do_op_float, // ev_float - do_op_vector, // ev_vector - do_op_invalid, // ev_entity - do_op_invalid, // ev_field - do_op_invalid, // ev_func - do_op_invalid, // ev_pointer - do_op_quaternion, // ev_quaternion - do_op_uinteger, // ev_integer - do_op_uinteger, // ev_uinteger - do_op_uinteger, // ev_short - do_op_invalid, // ev_invalid +static operation_t op_uint[ev_type_count] = { + [ev_void] = do_op_invalid, + [ev_string] = do_op_invalid, + [ev_float] = do_op_float, + [ev_vector] = do_op_vector, + [ev_entity] = do_op_invalid, + [ev_field] = do_op_invalid, + [ev_func] = do_op_invalid, + [ev_ptr] = do_op_invalid, + [ev_quaternion] = do_op_quaternion, + [ev_int] = do_op_uint, + [ev_uint] = do_op_uint, + [ev_short] = do_op_uint, + [ev_double] = do_op_double, + [ev_long] = 0, + [ev_ulong] = 0, + [ev_invalid] = do_op_invalid, }; static operation_t op_short[ev_type_count] = { - do_op_invalid, // ev_void - do_op_invalid, // ev_string - do_op_float, // ev_float - do_op_vector, // ev_vector - do_op_invalid, // ev_entity - do_op_invalid, // ev_field - do_op_invalid, // ev_func - do_op_invalid, // ev_pointer - do_op_quaternion, // ev_quaternion - do_op_integer, // ev_integer - do_op_uinteger, // ev_uinteger - do_op_short, // ev_short - do_op_invalid, // ev_invalid + [ev_void] = do_op_invalid, + [ev_string] = do_op_invalid, + [ev_float] = do_op_float, + [ev_vector] = do_op_vector, + [ev_entity] = do_op_invalid, + [ev_field] = do_op_invalid, + [ev_func] = do_op_invalid, + [ev_ptr] = do_op_invalid, + [ev_quaternion] = do_op_quaternion, + [ev_int] = do_op_int, + [ev_uint] = do_op_uint, + [ev_short] = do_op_short, + [ev_double] = do_op_double, + [ev_long] = 0, + [ev_ulong] = 0, + [ev_invalid] = do_op_invalid, +}; + +static operation_t op_double[ev_type_count] = { + [ev_void] = do_op_invalid, + [ev_string] = do_op_invalid, + [ev_float] = do_op_float, + [ev_vector] = do_op_vector, + [ev_entity] = do_op_invalid, + [ev_field] = do_op_invalid, + [ev_func] = do_op_invalid, + [ev_ptr] = do_op_invalid, + [ev_quaternion] = do_op_quaternion, + [ev_int] = do_op_int, + [ev_uint] = do_op_uint, + [ev_short] = do_op_short, + [ev_double] = do_op_double, + [ev_long] = 0, + [ev_ulong] = 0, + [ev_invalid] = do_op_invalid, }; static operation_t op_compound[ev_type_count] = { - do_op_invalid, // ev_void - do_op_invalid, // ev_string - do_op_compound, // ev_float - do_op_invalid, // ev_vector - do_op_invalid, // ev_entity - do_op_invalid, // ev_field - do_op_invalid, // ev_func - do_op_invalid, // ev_pointer - do_op_invalid, // ev_quaternion - do_op_compound, // ev_integer - do_op_compound, // ev_uinteger - do_op_compound, // ev_short - do_op_compound, // ev_invalid + [ev_void] = do_op_invalid, + [ev_string] = do_op_invalid, + [ev_float] = do_op_compound, + [ev_vector] = do_op_invalid, + [ev_entity] = do_op_invalid, + [ev_field] = do_op_invalid, + [ev_func] = do_op_invalid, + [ev_ptr] = do_op_invalid, + [ev_quaternion] = do_op_invalid, + [ev_int] = do_op_compound, + [ev_uint] = do_op_compound, + [ev_short] = do_op_compound, + [ev_double] = do_op_compound, + [ev_long] = do_op_compound, + [ev_ulong] = do_op_compound, + [ev_invalid] = do_op_compound, }; static operation_t *do_op[ev_type_count] = { - op_void, // ev_void - op_string, // ev_string - op_float, // ev_float - op_vector, // ev_vector - op_entity, // ev_entity - op_field, // ev_field - op_func, // ev_func - op_pointer, // ev_pointer - op_quaternion, // ev_quaternion - op_integer, // ev_integer - op_uinteger, // ev_uinteger - op_short, // ev_short - op_compound, // ev_invalid + [ev_void] = op_void, + [ev_string] = op_string, + [ev_float] = op_float, + [ev_vector] = op_vector, + [ev_entity] = op_entity, + [ev_field] = op_field, + [ev_func] = op_func, + [ev_ptr] = op_pointer, + [ev_quaternion] = op_quaternion, + [ev_int] = op_int, + [ev_uint] = op_uint, + [ev_short] = op_short, + [ev_double] = op_double, + [ev_long] = 0, + [ev_ulong] = 0, + [ev_invalid] = op_compound, }; static unaryop_t do_unary_op[ev_type_count]; @@ -1196,20 +1392,22 @@ uop_string (int op, expr_t *e, expr_t *e1) return e; s = expr_string (e1); - return new_integer_expr (!s || !s[0]); + return cmp_result_expr (!s || !s[0]); } static expr_t * uop_float (int op, expr_t *e, expr_t *e1) { static int valid[] = { '+', '-', '!', '~', 'C', 0 }; + type_t *type; if (!valid_op (op, valid)) return error (e1, "invalid unary operator for float: %s", get_op_string (op)); if (op == '+') return e1; - if (op == 'C' && get_type (e) != &type_integer) + type = get_type (e); + if (op == 'C' && !is_int(type) && !is_double(type)) return error (e1, "invalid cast of float"); if (!is_constant (e1)) return e; @@ -1218,11 +1416,15 @@ uop_float (int op, expr_t *e, expr_t *e1) return new_float_expr (-expr_float (e1)); case '!': print_type (get_type (e)); - return new_integer_expr (!expr_float (e1)); + return cmp_result_expr (!expr_float (e1)); case '~': return new_float_expr (~(int) expr_float (e1)); case 'C': - return new_integer_expr (expr_float (e1)); + if (is_int(type)) { + return new_int_expr (expr_float (e1)); + } else { + return new_double_expr (expr_float (e1)); + } } internal_error (e, "float unary op blew up"); } @@ -1245,7 +1447,7 @@ uop_vector (int op, expr_t *e, expr_t *e1) VectorNegate (expr_vector (e), v); return new_vector_expr (v); case '!': - return new_integer_expr (!VectorIsZero (expr_vector (e1))); + return cmp_result_expr (!VectorIsZero (expr_vector (e1))); } internal_error (e, "vector unary op blew up"); } @@ -1339,7 +1541,7 @@ uop_quaternion (int op, expr_t *e, expr_t *e1) QuatNegate (expr_vector (e), q); return new_quaternion_expr (q); case '!': - return new_integer_expr (!QuatIsZero (expr_quaternion (e1))); + return cmp_result_expr (!QuatIsZero (expr_quaternion (e1))); case '~': QuatConj (expr_vector (e), q); return new_quaternion_expr (q); @@ -1348,7 +1550,7 @@ uop_quaternion (int op, expr_t *e, expr_t *e1) } static expr_t * -uop_integer (int op, expr_t *e, expr_t *e1) +uop_int (int op, expr_t *e, expr_t *e1) { static int valid[] = { '+', '-', '!', '~', 'C', 0 }; @@ -1357,25 +1559,25 @@ uop_integer (int op, expr_t *e, expr_t *e1) get_op_string (op)); if (op == '+') return e1; - if (op == 'C' && get_type (e) != &type_float) + if (op == 'C' && !is_float(get_type (e))) return error (e1, "invalid cast of int"); if (!is_constant (e1)) return e; switch (op) { case '-': - return new_integer_expr (-expr_integer (e1)); + return new_int_expr (-expr_int (e1)); case '!': - return new_integer_expr (!expr_integer (e1)); + return cmp_result_expr (!expr_int (e1)); case '~': - return new_integer_expr (~expr_integer (e1)); + return new_int_expr (~expr_int (e1)); case 'C': - return new_float_expr (expr_integer (e1)); + return new_float_expr (expr_int (e1)); } - internal_error (e, "integer unary op blew up"); + internal_error (e, "int unary op blew up"); } static expr_t * -uop_uinteger (int op, expr_t *e, expr_t *e1) +uop_uint (int op, expr_t *e, expr_t *e1) { static int valid[] = { '+', '-', '!', '~', 0 }; @@ -1388,13 +1590,13 @@ uop_uinteger (int op, expr_t *e, expr_t *e1) return e; switch (op) { case '-': - return new_uinteger_expr (-expr_uinteger (e1)); + return new_uint_expr (-expr_uint (e1)); case '!': - return new_integer_expr (!expr_uinteger (e1)); + return cmp_result_expr (!expr_uint (e1)); case '~': - return new_uinteger_expr (~expr_uinteger (e1)); + return new_uint_expr (~expr_uint (e1)); } - internal_error (e, "uinteger unary op blew up"); + internal_error (e, "uint unary op blew up"); } static expr_t * @@ -1413,13 +1615,45 @@ uop_short (int op, expr_t *e, expr_t *e1) case '-': return new_short_expr (-expr_short (e1)); case '!': - return new_integer_expr (!expr_short (e1)); + return cmp_result_expr (!expr_short (e1)); case '~': return new_short_expr (~expr_short (e1)); } internal_error (e, "short unary op blew up"); } +static expr_t * +uop_double (int op, expr_t *e, expr_t *e1) +{ + static int valid[] = { '+', '-', '!', 'C', 0 }; + type_t *type; + + if (!valid_op (op, valid)) + return error (e1, "invalid unary operator for double: %s", + get_op_string (op)); + if (op == '+') + return e1; + type = get_type (e); + if (op == 'C' && !is_int(type) && !is_float(type)) + return error (e1, "invalid cast of double"); + if (!is_constant (e1)) + return e; + switch (op) { + case '-': + return new_double_expr (-expr_double (e1)); + case '!': + print_type (get_type (e)); + return cmp_result_expr (!expr_double (e1)); + case 'C': + if (is_int(type)) { + return new_int_expr (expr_double (e1)); + } else { + return new_float_expr (expr_double (e1)); + } + } + internal_error (e, "float unary op blew up"); +} + static expr_t * uop_compound (int op, expr_t *e, expr_t *e1) { @@ -1427,7 +1661,7 @@ uop_compound (int op, expr_t *e, expr_t *e1) if (is_scalar (t1)) { if (is_enum (t1)) { - return uop_integer (op, e, e1); + return uop_int (op, e, e1); } } return error (e1, "invalid operand for unary %s", get_op_string (op)); @@ -1441,11 +1675,12 @@ static unaryop_t do_unary_op[ev_type_count] = { uop_entity, // ev_entity uop_field, // ev_field uop_func, // ev_func - uop_pointer, // ev_pointer + uop_pointer, // ev_ptr uop_quaternion, // ev_quaternion - uop_integer, // ev_integer - uop_uinteger, // ev_uinteger + uop_int, // ev_int + uop_uint, // ev_uint uop_short, // ev_short + uop_double, // ev_double uop_compound, // ev_invalid }; @@ -1456,71 +1691,34 @@ fold_constants (expr_t *e) expr_t *e1, *e2; etype_t t1, t2; - if (e->type == ex_block) { - expr_t *block = new_block_expr (); - expr_t *next; - - block->e.block.result = e->e.block.result; - block->line = e->line; - block->file = e->file; - - for (e = e->e.block.head; e; e = next) { - next = e->next; - e = fold_constants (e); - e->next = 0; - append_expr (block, e); - } - - return block; - } - if (e->type == ex_bool) { - e->e.bool.e = fold_constants (e->e.bool.e); - return e; - } if (e->type == ex_uexpr) { - if (!e->e.expr.e1) + e1 = e->e.expr.e1; + if (!e1) { return e; + } op = e->e.expr.op; - e->e.expr.e1 = e1 = fold_constants (e->e.expr.e1); - if (e1->type == ex_error) - return e1; - if (op == 'A' || op == 'g' || op == 'r') - return e; t1 = extract_type (e1); if (t1 >= ev_type_count || !do_unary_op[t1]) { print_expr (e); internal_error (e, "invalid type: %d", t1); } return do_unary_op[t1] (op, e, e1); + } else if (e->type == ex_expr) { + e1 = e->e.expr.e1; + e2 = e->e.expr.e2; + if (!is_constant (e1) && !is_constant (e2)) { + return e; + } + + op = e->e.expr.op; + + t1 = extract_type (e1); + t2 = extract_type (e2); + + if (t1 >= ev_type_count || t2 >= ev_type_count + || !do_op[t1] || !do_op[t1][t2]) + internal_error (e, "invalid type %d %d", t1, t2); + return do_op[t1][t2] (op, e, e1, e2); } - - if (e->type != ex_expr) - return e; - - op = e->e.expr.op; - - e->e.expr.e1 = e1 = fold_constants (e->e.expr.e1); - if (e1->type == ex_error) - return e1; - t1 = extract_type (e1); - - if (op == 'i' || op == 'n' || op == 'c') - return e; - - e->e.expr.e2 = e2 = fold_constants (e->e.expr.e2); - if (e2->type == ex_error) - return e2; - - if (e2->type == ex_label || e2->type == ex_labelref) - return e; - - t2 = extract_type (e2); - - if (op == 's') - return e; - - if (t1 >= ev_type_count || t2 >= ev_type_count - || !do_op[t1] || !do_op[t1][t2]) - internal_error (e, "invalid type"); - return do_op[t1][t2] (op, e, e1, e2); + return e; } diff --git a/tools/qfcc/source/cpp.c b/tools/qfcc/source/cpp.c index cfc5de81d..db2d95d55 100644 --- a/tools/qfcc/source/cpp.c +++ b/tools/qfcc/source/cpp.c @@ -54,8 +54,9 @@ #include "QF/dstring.h" -#include "cpp.h" -#include "options.h" +#include "tools/qfcc/include/cpp.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/options.h" typedef struct cpp_arg_s { struct cpp_arg_s *next; @@ -66,11 +67,25 @@ cpp_arg_t *cpp_arg_list; cpp_arg_t **cpp_arg_tail = &cpp_arg_list; cpp_arg_t *cpp_def_list; cpp_arg_t **cpp_def_tail = &cpp_def_list; +cpp_arg_t *cpp_undef_list; +cpp_arg_t **cpp_undef_tail = &cpp_undef_list; +cpp_arg_t *cpp_sysinc_list; +cpp_arg_t **cpp_sysinc_tail = &cpp_sysinc_list; const char **cpp_argv; const char *cpp_name = CPP_NAME; static int cpp_argc = 0; dstring_t *tempname; +static const char ** +append_cpp_args (const char **arg, cpp_arg_t *arg_list) +{ + cpp_arg_t *cpp_arg; + + for (cpp_arg = arg_list; cpp_arg; cpp_arg = cpp_arg->next) + *arg++ = cpp_arg->arg; + return arg; +} + static void add_cpp_arg (const char *arg) { @@ -82,6 +97,28 @@ add_cpp_arg (const char *arg) cpp_argc++; } +void +add_cpp_sysinc (const char *arg) +{ + cpp_arg_t *cpp_arg = malloc (sizeof (cpp_arg_t)); + cpp_arg->next = 0; + cpp_arg->arg = arg; + *cpp_sysinc_tail = cpp_arg; + cpp_sysinc_tail = &(*cpp_sysinc_tail)->next; + cpp_argc++; +} + +void +add_cpp_undef (const char *arg) +{ + cpp_arg_t *cpp_arg = malloc (sizeof (cpp_arg_t)); + cpp_arg->next = 0; + cpp_arg->arg = arg; + *cpp_undef_tail = cpp_arg; + cpp_undef_tail = &(*cpp_undef_tail)->next; + cpp_argc++; +} + void add_cpp_def (const char *arg) { @@ -116,18 +153,20 @@ static void build_cpp_args (const char *in_name, const char *out_name) { cpp_arg_t *cpp_arg; - cpp_arg_t *cpp_def; const char **arg; if (cpp_argv) free (cpp_argv); - cpp_argv = (const char **)malloc ((cpp_argc + 1) * sizeof (char**)); + cpp_argv = (const char **)malloc ((cpp_argc + 1) * sizeof (char *)); for (arg = cpp_argv, cpp_arg = cpp_arg_list; cpp_arg; cpp_arg = cpp_arg->next) { - if (!strcmp (cpp_arg->arg, "%d")) { - for (cpp_def = cpp_def_list; cpp_def; cpp_def = cpp_def->next) - *arg++ = cpp_def->arg; + if (!strcmp (cpp_arg->arg, "%u")) { + arg = append_cpp_args (arg, cpp_undef_list); + } else if (!strcmp (cpp_arg->arg, "%s")) { + arg = append_cpp_args (arg, cpp_sysinc_list); + } else if (!strcmp (cpp_arg->arg, "%d")) { + arg = append_cpp_args (arg, cpp_def_list); } else if (!strcmp (cpp_arg->arg, "%i")) { *arg++ = in_name; } else if (!strcmp (cpp_arg->arg, "%o")) { @@ -140,9 +179,6 @@ build_cpp_args (const char *in_name, const char *out_name) } } *arg = 0; - //for (arg = cpp_argv; *arg; arg++) - // printf ("%s ", *arg); - //puts (""); } //============================================================================ @@ -205,6 +241,9 @@ preprocess_file (const char *filename, const char *ext) if (cpp_name) { intermediate_file (tempname, filename, ext ? ext : "p", 0); build_cpp_args (filename, tempname->str); + if (!cpp_argv[0]) { + internal_error(0, "cpp_argv[0] is null"); + } #ifdef _WIN32 if (!options.save_temps && !options.preprocess_only) @@ -230,7 +269,7 @@ preprocess_file (const char *filename, const char *ext) puts(""); } -#ifdef _WIN64 +#if defined(_WIN64) || defined(_WIN32) status = spawnvp (_P_WAIT, cpp_argv[0], (char **) cpp_argv); #else status = spawnvp (_P_WAIT, cpp_argv[0], cpp_argv); diff --git a/tools/qfcc/source/dags.c b/tools/qfcc/source/dags.c index 95c99d10a..71b564144 100644 --- a/tools/qfcc/source/dags.c +++ b/tools/qfcc/source/dags.c @@ -47,23 +47,27 @@ #include "QF/mathlib.h" #include "QF/set.h" -#include "dags.h" -#include "diagnostic.h" -#include "flow.h" -#include "function.h" -#include "qfcc.h" -#include "statements.h" -#include "strpool.h" -#include "symtab.h" -#include "type.h" -#include "value.h" +#include "tools/qfcc/include/dags.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/dot.h" +#include "tools/qfcc/include/flow.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/statements.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" -static daglabel_t *labels_freelist; -static dagnode_t *nodes_freelist; -static dag_t *dags_freelist; +ALLOC_STATE (daglabel_t, labels); +ALLOC_STATE (dagnode_t, nodes); +ALLOC_STATE (dag_t, dags); static daglabel_t *daglabel_chain; +static void dag_live_aliases(operand_t *op); + static void flush_daglabels (void) { @@ -72,15 +76,15 @@ flush_daglabels (void) if ((op = daglabel_chain->op)) { if (op->op_type == op_def) - op->o.def->daglabel = 0; + op->def->daglabel = 0; else if (op->op_type == op_temp) - op->o.tempop.daglabel = 0; + op->tempop.daglabel = 0; else if (op->op_type == op_value) - op->o.value->daglabel = 0; + op->value->daglabel = 0; else if (op->op_type == op_label) - op->o.label->daglabel = 0; + op->label->daglabel = 0; else - internal_error (0, "unexpected operand type"); + internal_error (op->expr, "unexpected operand type"); } daglabel_chain = daglabel_chain->daglabel_chain; } @@ -114,6 +118,7 @@ new_node (dag_t *dag) node->parents = set_new (); node->edges = set_new (); node->identifiers = set_new (); + node->reachable = set_new (); node->number = dag->num_nodes; set_add (dag->roots, node->number); // nodes start out as root nodes dag->nodes[dag->num_nodes++] = node; @@ -133,6 +138,11 @@ daglabel_string (daglabel_t *label) // operand_string might use quote_string, which returns a pointer to // a static variable. dstring_copystr (str, operand_string (label->op)); +#if 0 + if (label->op->type) { + dstring_appendstr (str, label->op->type->encoding); + } +#endif return quote_string (str->str); } @@ -158,35 +168,35 @@ operand_label (dag_t *dag, operand_t *op) return 0; if (op->op_type == op_temp) { - while (op->o.tempop.alias) - op = op->o.tempop.alias; - if (op->o.tempop.daglabel) - return op->o.tempop.daglabel; + //while (op->tempop.alias) + // op = op->tempop.alias; + if (op->tempop.daglabel) + return op->tempop.daglabel; label = new_label (dag); label->op = op; - op->o.tempop.daglabel = label; + op->tempop.daglabel = label; } else if (op->op_type == op_def) { - def = op->o.def; + def = op->def; if (def->daglabel) return def->daglabel; label = new_label (dag); label->op = op; def->daglabel = label; } else if (op->op_type == op_value) { - val = op->o.value; + val = op->value; if (val->daglabel) return val->daglabel; label = new_label (dag); label->op = op; val->daglabel = label; } else if (op->op_type == op_label) { - if (op->o.label->daglabel) - return op->o.label->daglabel; + if (op->label->daglabel) + return op->label->daglabel; label = new_label (dag); label->op = op; - op->o.label->daglabel = label; + op->label->daglabel = label; } else { - internal_error (0, "unexpected operand type: %d", op->op_type); + internal_error (op->expr, "unexpected operand type: %s", op_type_names[op->op_type]); } return label; } @@ -208,7 +218,7 @@ leaf_node (dag_t *dag, operand_t *op, expr_t *expr) return node; } -static dagnode_t * +static __attribute__((pure)) dagnode_t * dag_node (operand_t *op) { def_t *def; @@ -217,36 +227,85 @@ dag_node (operand_t *op) if (!op) return 0; if (op->op_type == op_def) { - def = op->o.def; + def = op->def; if (def->daglabel) node = def->daglabel->dagnode; } else if (op->op_type == op_temp) { - while (op->o.tempop.alias) - op = op->o.tempop.alias; - if (op->o.tempop.daglabel) - node = op->o.tempop.daglabel->dagnode; + if (op->tempop.daglabel) + node = op->tempop.daglabel->dagnode; } else if (op->op_type == op_value) { - if (op->o.value->daglabel) - node = op->o.value->daglabel->dagnode; + if (op->value->daglabel) + node = op->value->daglabel->dagnode; } else if (op->op_type == op_label) { - if (op->o.label->daglabel) - node = op->o.label->daglabel->dagnode; + if (op->label->daglabel) + node = op->label->daglabel->dagnode; } - if (node && node->killed) - node = 0; return node; } static void -dag_make_children (dag_t *dag, statement_t *s, operand_t *operands[4], +dag_make_leafs (dag_t *dag, statement_t *s, operand_t *operands[FLOW_OPERANDS]) +{ + int i; + + flow_analyze_statement (s, 0, 0, 0, operands); + for (i = 1; i < FLOW_OPERANDS; i++) { + if (!dag_node (operands[i])) { + leaf_node (dag, operands[i], s->expr); + } + } +} + +static void +dagnode_set_reachable (dag_t *dag, dagnode_t *node) +{ + for (set_iter_t *edge_iter = set_first (node->edges); edge_iter; + edge_iter = set_next (edge_iter)) { + dagnode_t *r = dag->nodes[edge_iter->element]; + // The other node is directly reachable + set_add (node->reachable, r->number); + // All nodes reachable by the other node are indirectly reachable + // from this node. + set_union (node->reachable, r->reachable); + } +} + +static void +dag_make_children (dag_t *dag, statement_t *s, + operand_t *operands[FLOW_OPERANDS], dagnode_t *children[3]) { int i; flow_analyze_statement (s, 0, 0, 0, operands); for (i = 0; i < 3; i++) { - if (!(children[i] = dag_node (operands[i + 1]))) - children[i] = leaf_node (dag, operands[i + 1], s->expr); + dagnode_t *node = dag_node (operands[i + 1]); + dagnode_t *killer = 0; + + if (node && (node->killed || s->type == st_address)) { + // If the node has been killed, then a new node is needed + // taking the address of a variable effectively kills the node it's + // attached to. FIXME should this be for only when the variable is + // in the attached identifiers list and is not the node's label? + killer = node->killed; + node = 0; + } + + if (!node) { + // No valid node found (either first reference to the value, + // or the value's node was killed). + node = leaf_node (dag, operands[i + 1], s->expr); + } + if (killer) { + // When an operand refers to a killed node, it must be + // evaluated AFTER the killing node has been evaluated. + set_add (node->edges, killer->number); + // If killer is set, then node is guaranteed to be a new node + // and thus does not have any parents, so no need to worry about + // updating the reachable sets of any parent nodes. + dagnode_set_reachable (dag, node); + } + children[i] = node; } } @@ -271,8 +330,8 @@ dagnode_match (const dagnode_t *n, const daglabel_t *op, if (n->killed) return 0; - if (!strcmp (op->opcode, ".") - && n->label->opcode && !strcmp (n->label->opcode, ".=")) + if (!strcmp (op->opcode, "load") + && n->label->opcode && !strcmp (n->label->opcode, "store")) return dagnode_deref_match (n, op, children); if (n->label->opcode != op->opcode) return 0; @@ -311,7 +370,21 @@ dagnode_add_children (dag_t *dag, dagnode_t *n, operand_t *operands[4], } static int -dagnode_set_edges_visit (def_t *def, void *_node) +dagnode_tempop_set_edges_visit (tempop_t *tempop, void *_node) +{ + dagnode_t *node = (dagnode_t *) _node; + daglabel_t *label; + + label = tempop->daglabel; + if (label && label->dagnode) { + set_add (node->edges, label->dagnode->number); + label->live = 1; + } + return 0; +} + +static int +dagnode_def_set_edges_visit (def_t *def, void *_node) { dagnode_t *node = (dagnode_t *) _node; daglabel_t *label; @@ -336,7 +409,7 @@ dag_find_node (def_t *def, void *_daglabel) } static void -dagnode_set_edges (dag_t *dag, dagnode_t *n) +dagnode_set_edges (dag_t *dag, dagnode_t *n, statement_t *s) { int i; @@ -353,32 +426,53 @@ dagnode_set_edges (dag_t *dag, dagnode_t *n) if (child->label->op) { dagnode_t *node = child->label->dagnode; operand_t *op = child->label->op; - if (node != child && node != n) + if (node != child && node != n) { set_add (node->edges, n->number); + } if (op->op_type == op_value - && op->o.value->type == ev_pointer - && op->o.value->v.pointer.def) - def_visit_all (op->o.value->v.pointer.def, 1, - dagnode_set_edges_visit, n); + && op->value->lltype == ev_ptr + && op->value->v.pointer.def) { + def_visit_all (op->value->v.pointer.def, 1, + dagnode_def_set_edges_visit, n); + } if (op->op_type == op_def - && (op->o.def->alias || op->o.def->alias_defs)) - def_visit_all (op->o.def, 1, dagnode_set_edges_visit, n); + && (op->def->alias || op->def->alias_defs)) { + def_visit_all (op->def, 1, + dagnode_def_set_edges_visit, n); + } + if (op->op_type == op_temp + && (op->tempop.alias || op->tempop.alias_ops)) { + tempop_visit_all (&op->tempop, 1, + dagnode_tempop_set_edges_visit, n); + } } if (n != child) set_add (n->edges, child->number); } } + for (operand_t *use = s->use; use; use = use->next) { + if (use->op_type == op_pseudo) { + continue; + } + daglabel_t *label = operand_label (dag, use); + label->live = 1; + dag_live_aliases (use); + set_add (n->edges, label->dagnode->number); + } if (n->type == st_func) { const char *num_params = 0; int first_param = 0; - flowvar_t **flowvars = dag->flownode->graph->func->vars; + function_t *func = dag->flownode->graph->func; + flowvar_t **flowvars = func->vars; - if (!strncmp (n->label->opcode, "label->opcode, "call")) { + // nothing to do + } else if (!strncmp (n->label->opcode, "rcall", 5)) { num_params = n->label->opcode + 6; first_param = 2; - } else if (!strncmp (n->label->opcode, "label->opcode, "call", 4)) { num_params = n->label->opcode + 5; - } else if (!strcmp (n->label->opcode, "")) { + } else if (!strcmp (n->label->opcode, "return") && n->children[0]) { daglabel_t *label = n->children[0]->label; if (!label->op) { set_iter_t *lab_i; @@ -389,10 +483,32 @@ dagnode_set_edges (dag_t *dag, dagnode_t *n) } label->live = 1; } - if (num_params && isdigit (*num_params)) { + for (int i = 0; i < s->num_use; i++) { + udchain_t ud = func->ud_chains[s->first_use + i]; + flowvar_t *var = func->vars[ud.var]; + if (var->op->op_type == op_pseudo) { + continue; + } + daglabel_t *l = operand_label (dag, var->op); + if (l) { + l->live = 1; + } + } + // ensure all operations on global variables are completed before + // the st_func statement executes + for (set_iter_t *g = set_first (func->global_vars); g; + g = set_next (g)) { + flowvar_t *var = flowvars[g->element]; + dagnode_t *gn = dag_node (var->op); + if (gn) { + set_add (n->edges, gn->number); + set_remove (gn->edges, n->number); + } + } + if (num_params && isdigit ((byte) *num_params)) { for (i = first_param; i < *num_params - '0'; i++) { flowvar_t *var = flowvars[i + 1]; - def_t *param_def = var->op->o.def; + def_t *param_def = var->op->def; daglabel_t *daglabel; int param_node; @@ -400,7 +516,8 @@ dagnode_set_edges (dag_t *dag, dagnode_t *n) param_node = def_visit_all (param_def, 0, dag_find_node, &daglabel); if (!param_node) { - bug (0, ".param_%d not set for %s", i, n->label->opcode); + bug (n->label->expr, ".param_%d not set for %s", i, + n->label->opcode); continue; } daglabel->live = 1; @@ -413,6 +530,8 @@ dagnode_set_edges (dag_t *dag, dagnode_t *n) static int op_is_identifier (operand_t *op) { + if (!op) + return 0; if (op->op_type == op_label) return 0; if (op->op_type == op_value) @@ -425,19 +544,59 @@ op_is_identifier (operand_t *op) } static int -dag_kill_aliases_visit (def_t *def, void *_l) +op_is_constant (operand_t *op) +{ + if (!op) + return 0; + if (op->op_type == op_label) + return 1; + if (op->op_type == op_value) + return 1; + if (op->op_type == op_label) + return op->def->constant; + return 0; +} + +static int +op_is_temp (operand_t *op) +{ + if (!op) + return 0; + return op->op_type == op_temp; +} + +static int +dag_tempop_kill_aliases_visit (tempop_t *tempop, void *_l) { daglabel_t *l = (daglabel_t *) _l; - dagnode_t *node = l->dagnode;; + dagnode_t *node = l->dagnode; daglabel_t *label; - if (def == l->op->o.def) + if (tempop == &l->op->tempop) return 0; - label = def->daglabel; - if (label && label->dagnode) { + label = tempop->daglabel; + if (label && label->dagnode && !label->dagnode->killed) { set_add (node->edges, label->dagnode->number); set_remove (node->edges, node->number); - label->dagnode->killed = 1; + label->dagnode->killed = node; + } + return 0; +} + +static int +dag_def_kill_aliases_visit (def_t *def, void *_l) +{ + daglabel_t *l = (daglabel_t *) _l; + dagnode_t *node = l->dagnode; + daglabel_t *label; + + if (def == l->op->def) + return 0; + label = def->daglabel; + if (label && label->dagnode && !label->dagnode->killed) { + set_add (node->edges, label->dagnode->number); + set_remove (node->edges, node->number); + label->dagnode->killed = node; } return 0; } @@ -448,16 +607,30 @@ dag_kill_aliases (daglabel_t *l) operand_t *op = l->op; if (op->op_type == op_temp) { + if (op->tempop.alias || op->tempop.alias_ops) { + tempop_visit_all (&op->tempop, 1, + dag_tempop_kill_aliases_visit, l); + } } else if (op->op_type == op_def) { - if (op->o.def->alias || op->o.def->alias_defs) - def_visit_all (op->o.def, 1, dag_kill_aliases_visit, l); + if (op->def->alias || op->def->alias_defs) { + def_visit_all (op->def, 1, dag_def_kill_aliases_visit, l); + } } else { - internal_error (0, "rvalue assignment?"); + internal_error (op->expr, "rvalue assignment?"); } } static int -dag_live_aliases (def_t *def, void *_d) +dag_tempop_live_aliases (tempop_t *tempop, void *_t) +{ + + if (tempop != _t && tempop->daglabel) + tempop->daglabel->live = 1; + return 0; +} + +static int +dag_def_live_aliases (def_t *def, void *_d) { if (def != _d && def->daglabel) @@ -466,37 +639,90 @@ dag_live_aliases (def_t *def, void *_d) } static void -dagnode_attach_label (dagnode_t *n, daglabel_t *l) +dag_live_aliases(operand_t *op) +{ + // FIXME it would be better to propogate the aliasing + if (op->op_type == op_temp + && (op->tempop.alias || op->tempop.alias_ops)) { + tempop_visit_all (&op->tempop, 1, dag_tempop_live_aliases, + &op->tempop); + } + if (op->op_type == op_def + && (op->def->alias || op->def->alias_defs)) { + def_visit_all (op->def, 1, dag_def_live_aliases, op->def); + } +} + +static void +dag_make_var_live (set_t *live_vars, operand_t *op) +{ + flowvar_t *var = 0; + + if (op) { + dag_live_aliases (op); + var = flow_get_var (op); + } + if (var) + set_add (live_vars, var->number); +} + +static int +dagnode_attach_label (dag_t *dag, dagnode_t *n, daglabel_t *l) { if (!l->op) internal_error (0, "attempt to attach operator label to dagnode " "identifiers"); if (!op_is_identifier (l->op)) - internal_error (0, "attempt to attach non-identifer label to dagnode " + internal_error (l->op->expr, + "attempt to attach non-identifer label to dagnode " "identifiers"); if (l->dagnode) { dagnode_t *node = l->dagnode; - set_union (n->edges, node->parents); - set_remove (n->edges, n->number); set_remove (node->identifiers, l->number); + + // If the target node (n) is reachable by the label's node or its + // parents, then attaching the label's node to the target node would + // cause the label's node to be written before it used. + set_t *reachable = set_new (); + set_assign (reachable, node->reachable); + for (set_iter_t *node_iter = set_first (node->parents); node_iter; + node_iter = set_next (node_iter)) { + dagnode_t *p = dag->nodes[node_iter->element]; + set_union (reachable, p->reachable); + } + int is_reachable = set_is_member (reachable, n->number); + set_delete (reachable); + if (is_reachable) { + return 0; + } + + // this assignment to the variable must come after any previous uses, + // which includes itself and its parents + set_add (n->edges, node->number); + set_union (n->edges, node->parents); + dagnode_set_reachable (dag, n); } l->live = 0; // remove live forcing on assignment l->dagnode = n; set_add (n->identifiers, l->number); dag_kill_aliases (l); if (n->label->op) { - // FIXME temps - // FIXME it would be better to propogate the aliasing - if (n->label->op->op_type == op_def - && (n->label->op->o.def->alias - || n->label->op->o.def->alias_defs)) - def_visit_all (n->label->op->o.def, 1, dag_live_aliases, - n->label->op->o.def); + dag_live_aliases (n->label->op); } + return 1; } static int -dag_alias_live (def_t *def, void *_live_vars) +dag_tempop_alias_live (tempop_t *tempop, void *_live_vars) +{ + set_t *live_vars = (set_t *) _live_vars; + if (!tempop->flowvar) + return 0; + return set_is_member (live_vars, tempop->flowvar->number); +} + +static int +dag_def_alias_live (def_t *def, void *_live_vars) { set_t *live_vars = (set_t *) _live_vars; if (!def->flowvar) @@ -523,7 +749,11 @@ dag_remove_dead_vars (dag_t *dag, set_t *live_vars) if (set_is_member (dag->flownode->global_vars, var->number)) continue; if (l->op->op_type == op_def - && def_visit_all (l->op->o.def, 1, dag_alias_live, live_vars)) + && def_visit_all (l->op->def, 1, dag_def_alias_live, live_vars)) + continue; + if (l->op->op_type == op_temp + && tempop_visit_all (&l->op->tempop, 1, dag_tempop_alias_live, + live_vars)) continue; if (!set_is_member (live_vars, var->number)) set_remove (l->dagnode->identifiers, l->number); @@ -568,17 +798,6 @@ dag_sort_nodes (dag_t *dag) dag->num_topo = topo; } -static void -dag_make_var_live (set_t *live_vars, operand_t *op) -{ - flowvar_t *var = 0; - - if (op) - var = flow_get_var (op); - if (var) - set_add (live_vars, var->number); -} - static void dag_kill_nodes (dag_t *dag, dagnode_t *n) { @@ -587,20 +806,29 @@ dag_kill_nodes (dag_t *dag, dagnode_t *n) for (i = 0; i < dag->num_nodes; i++) { node = dag->nodes[i]; + if (node->killed) { + //the node is already killed + continue; + } if (node == n->children[1]) { // assume the pointer does not point to itself. This should be // reasonable because without casting, only a void pointer can // point to itself (the required type is recursive). continue; } - if (node->label->op && !op_is_identifier (node->label->op)) { + if (op_is_constant (node->label->op)) { // While constants in the Quake VM can be changed via a pointer, // doing so would cause much more fun than a simple // mis-optimization would, so consider them safe from pointer // operations. continue; } - node->killed = 1; + if (op_is_temp (node->label->op)) { + // Assume that the pointer cannot point to a temporary variable. + // This is reasonable as there is no programmer access to temps. + continue; + } + node->killed = n; } n->killed = 0; } @@ -614,53 +842,103 @@ dag_create (flownode_t *flownode) dagnode_t **nodes; daglabel_t **labels; int num_statements = 0; + int num_use = 0; + int num_nodes; + int num_lables; set_t *live_vars = set_new (); flush_daglabels (); // count the number of statements so the number of nodes and labels can be // guessed - for (s = block->statements; s; s = s->next) + for (s = block->statements; s; s = s->next) { num_statements++; + for (operand_t *use = s->use; use; use = use->next) { + if (use->op_type == op_pseudo) { + continue; + } + num_use++; + } + } set_assign (live_vars, flownode->live_vars.out); dag = new_dag (); dag->flownode = flownode; - // at most 4 per statement - dag->nodes = alloca (num_statements * 4 * sizeof (dagnode_t)); - // at most 4 per statement, + return + params - dag->labels = alloca (num_statements * (4 + 1 + 8) * sizeof (daglabel_t)); + // at most FLOW_OPERANDS per statement + use + num_nodes = num_statements * FLOW_OPERANDS + num_use; + dag->nodes = alloca (num_nodes * sizeof (dagnode_t)); + // at most FLOW_OPERANDS per statement, + return + params + use + num_lables = num_statements * (FLOW_OPERANDS + 1 + 8) + num_use; + dag->labels = alloca (num_lables * sizeof (daglabel_t)); dag->roots = set_new (); + // do a first pass to ensure all operands have an "x_0" leaf node + // prior do actual dag creation for (s = block->statements; s; s = s->next) { - operand_t *operands[4]; + operand_t *operands[FLOW_OPERANDS]; + dag_make_leafs (dag, s, operands); + // make sure any auxiliary operands are given nodes, too + for (operand_t *use = s->use; use; use = use->next) { + if (use->op_type == op_pseudo) { + continue; + } + if (!dag_node (use)) { + leaf_node (dag, use, s->expr); + } + } + } + // actual dag creation + for (s = block->statements; s; s = s->next) { + operand_t *operands[FLOW_OPERANDS]; dagnode_t *n = 0, *children[3] = {0, 0, 0}; daglabel_t *op, *lx; int i; dag_make_children (dag, s, operands, children); - if (s->type == st_flow) - for (i = 0; i < 3; i++) - if (children[i]) - dag_make_var_live (live_vars, operands[i]); + if (s->type == st_flow || s->type == st_func) { + for (i = 0; i < 3; i++) { + if (children[i]) { + dag_make_var_live (live_vars, operands[i + 1]); + } + } + } + if (operands[4]) { + // a movep instruction knew what it was reading, so mark that + // as live + dag_make_var_live (live_vars, operands[4]); + } op = opcode_label (dag, s->opcode, s->expr); n = children[0]; - if (s->type != st_assign - && !(n = dagnode_search (dag, op, children))) { - n = new_node (dag); - n->type = s->type; - n->label = op; - dagnode_add_children (dag, n, operands, children); - dagnode_set_edges (dag, n); + if (s->type != st_assign) { + if (!(n = dagnode_search (dag, op, children))) { + n = new_node (dag); + n->type = s->type; + n->label = op; + dagnode_add_children (dag, n, operands, children); + dagnode_set_edges (dag, n, s); + dagnode_set_reachable (dag, n); + } } lx = operand_label (dag, operands[0]); if (lx && lx->dagnode != n) { lx->expr = s->expr; - dagnode_attach_label (n, lx); + if (!dagnode_attach_label (dag, n, lx)) { + // attempting to attach the label to the node would create + // a dependency cycle in the dag, so a new node needs to be + // created for the source operand + if (s->type == st_assign) { + n = leaf_node (dag, operands[1], s->expr); + dagnode_attach_label (dag, n, lx); + } else { + internal_error (s->expr, "unexpected failure to attach" + " label to node"); + } + } } - if (n->type == st_ptrassign) + if (n->type == st_ptrassign) { dag_kill_nodes (dag, n); + } } nodes = malloc (dag->num_nodes * sizeof (dagnode_t *)); @@ -669,7 +947,12 @@ dag_create (flownode_t *flownode) labels = malloc (dag->num_labels * sizeof (daglabel_t *)); memcpy (labels, dag->labels, dag->num_labels * sizeof (daglabel_t *)); dag->labels = labels; - +#if 0 + if (options.block_dot.dags) { + flownode->dag = dag; + dump_dot ("raw-dags", flownode->graph, dump_dot_flow_dags); + } +#endif dag_remove_dead_vars (dag, live_vars); dag_sort_nodes (dag); set_delete (live_vars); @@ -686,11 +969,11 @@ build_statement (const char *opcode, operand_t **operands, expr_t *expr) for (i = 0; i < 3; i++) { if ((op = operands[i])) { while (op->op_type == op_alias) - op = op->o.alias; + op = op->alias; if (op->op_type == op_temp) { - while (op->o.tempop.alias) - op = op->o.tempop.alias; - op->o.tempop.users++; + while (op->tempop.alias) + op = op->tempop.alias; + op->tempop.users++; } } } @@ -734,10 +1017,10 @@ dag_calc_node_costs (dagnode_t *dagnode) } #endif static operand_t * -fix_op_type (operand_t *op, etype_t type) +fix_op_type (operand_t *op, type_t *type) { if (op && op->op_type != op_label && op->type != type) - op = alias_operand (type, op); + op = alias_operand (type, op, op->expr); return op; } @@ -768,7 +1051,7 @@ generate_moves (dag_t *dag, sblock_t *block, dagnode_t *dagnode) var = dag->labels[var_iter->element]; operands[2] = var->op; dst = operands[2]; - st = build_statement ("", operands, var->expr); + st = build_statement ("move", operands, var->expr); sblock_add_statement (block, st); } return dst; @@ -782,15 +1065,134 @@ generate_moveps (dag_t *dag, sblock_t *block, dagnode_t *dagnode) operand_t *operands[3] = {0, 0, 0}; statement_t *st; operand_t *dst = 0; + type_t *type; + int offset = 0; + def_t *dstDef; operands[0] = make_operand (dag, block, dagnode, 0); operands[1] = make_operand (dag, block, dagnode, 1); + if (dagnode->children[2]) { + operands[2] = make_operand (dag, block, dagnode, 2); + st = build_statement ("movep", operands, dagnode->label->expr); + sblock_add_statement (block, st); + if ((var_iter = set_first (dagnode->identifiers))) { + var = dag->labels[var_iter->element]; + dst = var->op; + set_del_iter (var_iter); + } + } else { + for (var_iter = set_first (dagnode->identifiers); var_iter; + var_iter = set_next (var_iter)) { + var = dag->labels[var_iter->element]; + dst = var->op; + type = dst->def->type; + dstDef = dst->def; + if (dstDef->alias) { + offset = dstDef->offset; + dstDef = dstDef->alias; + } + operands[2] = value_operand (new_pointer_val (offset, type, dstDef, 0), + operands[1]->expr); + st = build_statement ("movep", operands, var->expr); + sblock_add_statement (block, st); + } + } + return dst; +} + +static operand_t * +generate_memsets (dag_t *dag, sblock_t *block, dagnode_t *dagnode) +{ + set_iter_t *var_iter; + daglabel_t *var; + operand_t *operands[3] = {0, 0, 0}; + statement_t *st; + operand_t *dst; + + operands[0] = make_operand (dag, block, dagnode, 0); + operands[1] = make_operand (dag, block, dagnode, 1); + dst = operands[0]; for (var_iter = set_first (dagnode->identifiers); var_iter; var_iter = set_next (var_iter)) { var = dag->labels[var_iter->element]; - dst = var->op; - operands[2] = value_operand (new_pointer_val (0, 0, dst->o.def)); - st = build_statement ("", operands, var->expr); + operands[2] = var->op; + dst = operands[2]; + st = build_statement ("memset", operands, var->expr); + sblock_add_statement (block, st); + } + return dst; +} + +static operand_t * +generate_memsetps (dag_t *dag, sblock_t *block, dagnode_t *dagnode) +{ + set_iter_t *var_iter; + daglabel_t *var; + operand_t *operands[3] = {0, 0, 0}; + statement_t *st; + operand_t *dst = 0; + type_t *type; + int offset = 0; + def_t *dstDef; + + operands[0] = make_operand (dag, block, dagnode, 0); + operands[1] = make_operand (dag, block, dagnode, 1); + if (dagnode->children[2]) { + operands[2] = make_operand (dag, block, dagnode, 2); + st = build_statement ("memsetp", operands, dagnode->label->expr); + sblock_add_statement (block, st); + } else { + for (var_iter = set_first (dagnode->identifiers); var_iter; + var_iter = set_next (var_iter)) { + var = dag->labels[var_iter->element]; + dst = var->op; + type = dst->def->type; + dstDef = dst->def; + if (dstDef->alias) { + offset = dstDef->offset; + dstDef = dstDef->alias; + } + operands[2] = value_operand (new_pointer_val (offset, type, dstDef, 0), + operands[1]->expr); + st = build_statement ("memsetp", operands, var->expr); + sblock_add_statement (block, st); + } + } + return dst; +} + +static operand_t * +generate_call (dag_t *dag, sblock_t *block, dagnode_t *dagnode) +{ + set_iter_t *var_iter; + daglabel_t *var = 0; + operand_t *operands[3] = {0, 0, 0}; + statement_t *st; + operand_t *dst; + + operands[0] = make_operand (dag, block, dagnode, 0); + if (dagnode->children[1]) { + operands[1] = make_operand (dag, block, dagnode, 1); + } + dst = operands[0]; + for (var_iter = set_first (dagnode->identifiers); var_iter; + var_iter = set_next (var_iter)) { + if (var) { + internal_error (var->expr, "more than one return value for call"); + } + var = dag->labels[var_iter->element]; + operands[2] = var->op; + dst = operands[2]; + st = build_statement ("call", operands, var->expr); + sblock_add_statement (block, st); + } + if (var_iter) { + set_del_iter (var_iter); + } + if (!var) { + // void call or return value ignored, still have to call + operands[2] = make_operand (dag, block, dagnode, 2); + st = build_statement ("call", operands, dagnode->label->expr); sblock_add_statement (block, st); } return dst; @@ -798,21 +1200,21 @@ generate_moveps (dag_t *dag, sblock_t *block, dagnode_t *dagnode) static operand_t * generate_assignments (dag_t *dag, sblock_t *block, operand_t *src, - set_iter_t *var_iter, etype_t type) + set_iter_t *var_iter, type_t *type) { statement_t *st; operand_t *dst = 0; operand_t *operands[3] = {0, 0, 0}; daglabel_t *var; - operands[0] = fix_op_type (src, type); + operands[2] = fix_op_type (src, type); for ( ; var_iter; var_iter = set_next (var_iter)) { var = dag->labels[var_iter->element]; - operands[1] = fix_op_type (var->op, type); + operands[0] = fix_op_type (var->op, type); if (!dst) - dst = operands[1]; + dst = operands[0]; - st = build_statement ("=", operands, var->expr); + st = build_statement ("assign", operands, var->expr); sblock_add_statement (block, st); } return dst; @@ -826,7 +1228,7 @@ dag_gencode (dag_t *dag, sblock_t *block, dagnode_t *dagnode) statement_t *st; set_iter_t *var_iter; int i; - etype_t type; + type_t *type; switch (dagnode->type) { case st_none: @@ -838,13 +1240,15 @@ dag_gencode (dag_t *dag, sblock_t *block, dagnode_t *dagnode) dst = generate_assignments (dag, block, dst, var_iter, type); } break; + case st_address: case st_expr: operands[0] = make_operand (dag, block, dagnode, 0); if (dagnode->children[1]) operands[1] = make_operand (dag, block, dagnode, 1); - type = low_level_type (get_type (dagnode->label->expr)); + type = get_type (dagnode->label->expr); if (!(var_iter = set_first (dagnode->identifiers))) { - operands[2] = temp_operand (get_type (dagnode->label->expr)); + operands[2] = temp_operand (get_type (dagnode->label->expr), + dagnode->label->expr); } else { daglabel_t *var = dag->labels[var_iter->element]; @@ -860,29 +1264,35 @@ dag_gencode (dag_t *dag, sblock_t *block, dagnode_t *dagnode) case st_assign: internal_error (0, "unexpected assignment node"); case st_ptrassign: - operands[0] = make_operand (dag, block, dagnode, 0); - operands[1] = make_operand (dag, block, dagnode, 1); - if (dagnode->children[2]) - operands[2] = make_operand (dag, block, dagnode, 2); + operands[2] = make_operand (dag, block, dagnode, 0); + operands[0] = make_operand (dag, block, dagnode, 2); + if (dagnode->children[1]) + operands[1] = make_operand (dag, block, dagnode, 1); st = build_statement (dagnode->label->opcode, operands, dagnode->label->expr); sblock_add_statement (block, st); // the source location is suitable for use in other nodes - dst = operands[0]; + dst = operands[2]; break; case st_move: - if (!strcmp (dagnode->label->opcode, "")) { - dst = generate_moves (dag, block, dagnode); - break; - } - if (!strcmp (dagnode->label->opcode, "") - && !dagnode->children[2]) { - dst = generate_moveps (dag, block, dagnode); - break; - } - //fall through - case st_state: + dst = generate_moves (dag, block, dagnode); + break; + case st_ptrmove: + dst = generate_moveps (dag, block, dagnode); + break; + case st_memset: + dst = generate_memsets (dag, block, dagnode); + break; + case st_ptrmemset: + dst = generate_memsetps (dag, block, dagnode); + break; case st_func: + if (!strcmp (dagnode->label->opcode, "call")) { + dst = generate_call (dag, block, dagnode); + break; + } + // fallthrough + case st_state: for (i = 0; i < 3; i++) if (dagnode->children[i]) operands[i] = make_operand (dag, block, dagnode, i); @@ -894,6 +1304,8 @@ dag_gencode (dag_t *dag, sblock_t *block, dagnode_t *dagnode) operands[0] = make_operand (dag, block, dagnode, 0); if (dagnode->children[1]) operands[1] = make_operand (dag, block, dagnode, 1); + if (dagnode->children[2]) + operands[2] = make_operand (dag, block, dagnode, 2); st = build_statement (dagnode->label->opcode, operands, dagnode->label->expr); sblock_add_statement (block, st); @@ -938,6 +1350,19 @@ dag_remove_dead_nodes (dag_t *dag) } } } while (added_root); + + // clean up any stray edges that point to removed nodes + for (int i = 0; i < dag->num_nodes; i++) { + node = dag->nodes[i]; + for (child_i = set_first (node->edges); child_i; + child_i = set_next (child_i)) { + child = dag->nodes[child_i->element]; + if (!set_is_member (dag->roots, child->number) + && set_is_empty (child->parents)) { + set_remove (node->edges, child->number); + } + } + } dag_sort_nodes (dag); } diff --git a/tools/qfcc/source/debug.c b/tools/qfcc/source/debug.c index 5d6f1f5f5..f38952315 100644 --- a/tools/qfcc/source/debug.c +++ b/tools/qfcc/source/debug.c @@ -41,18 +41,24 @@ #include #include "QF/alloc.h" -#include "QF/pr_comp.h" +#include "QF/progs/pr_comp.h" -#include "debug.h" -#include "diagnostic.h" -#include "expr.h" -#include "qfcc.h" -#include "strpool.h" -#include "value.h" +#include "tools/qfcc/include/debug.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/emit.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/struct.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" int lineno_base; -static srcline_t *srclines_freelist; +ALLOC_STATE (srcline_t, srclines); static void push_source_file (void) @@ -79,6 +85,17 @@ pop_source_file (void) FREE (srclines, tmp); } +#define Sys_Error(fmt...) internal_error(0, fmt) +void +add_source_file (const char *file) +{ + pr.source_file = ReuseString (file); + if (!strpool_findstr (pr.comp_file_set, file)) { + strpool_addstr (pr.comp_file_set, file); + DARRAY_APPEND (&pr.comp_files, save_string (file)); + } +} + void line_info (char *text) { @@ -111,7 +128,7 @@ line_info (char *text) while (*p && *p != '\n') // ignore rest p++; pr.source_line = line - 1; - pr.source_file = ReuseString (strip_path (str)); + add_source_file (str); } pr_lineno_t * @@ -125,3 +142,67 @@ new_lineno (void) memset (&pr.linenos[pr.num_linenos], 0, sizeof (pr_lineno_t)); return &pr.linenos[pr.num_linenos++]; } + +static void +emit_unit_name (def_t *def, void *data, int index) +{ + if (!is_string (def->type)) { + internal_error (0, "%s: expected string def", __FUNCTION__); + } + EMIT_STRING (def->space, D_STRING (def), pr.unit_name); +} + +static void +emit_basedir (def_t *def, void *data, int index) +{ + if (!is_string (def->type)) { + internal_error (0, "%s: expected string def", __FUNCTION__); + } + EMIT_STRING (def->space, D_STRING (def), pr.comp_dir); +} + +static void +emit_num_files (def_t *def, void *data, int index) +{ + if (!is_int (def->type)) { + internal_error (0, "%s: expected int def", __FUNCTION__); + } + D_INT (def) = pr.comp_files.size; +} + +static void +emit_files_item (def_t *def, void *data, int index) +{ + if (!is_array (def->type) || !is_string (def->type->t.array.type)) { + internal_error (0, "%s: expected array of string def", __FUNCTION__); + } + if ((unsigned) index >= pr.comp_files.size) { + internal_error (0, "%s: out of bounds index: %d %zd", + __FUNCTION__, index, pr.comp_files.size); + } + EMIT_STRING (def->space, D_STRING (def), pr.comp_files.a[index]); +} + +static def_t * +emit_compunit (const char *modname) +{ + static struct_def_t compunit_struct[] = { + {"unit_name", &type_string, emit_unit_name}, + {"basedir", &type_string, emit_basedir}, + {"num_files", &type_int, emit_num_files}, + {"files", 0, emit_files_item}, + {0, 0} + }; + int count = pr.comp_files.size; + + pr.unit_name = modname; + compunit_struct[3].type = array_type (&type_string, count); + return emit_structure (".compile_unit", 's', compunit_struct, 0, &pr, + pr.debug_data, sc_static); +} + +void +debug_finish_module (const char *modname) +{ + emit_compunit (modname); +} diff --git a/tools/qfcc/source/def.c b/tools/qfcc/source/def.c index c16aa9912..b314d20c1 100644 --- a/tools/qfcc/source/def.c +++ b/tools/qfcc/source/def.c @@ -45,24 +45,24 @@ #include "QF/sys.h" #include "QF/va.h" -#include "qfcc.h" -#include "class.h" -#include "def.h" -#include "defspace.h" -#include "diagnostic.h" -#include "emit.h" -#include "expr.h" -#include "function.h" -#include "options.h" -#include "reloc.h" -#include "shared.h" -#include "strpool.h" -#include "struct.h" -#include "symtab.h" -#include "type.h" -#include "value.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/emit.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/shared.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/struct.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" -static def_t *defs_freelist; +ALLOC_STATE (def_t, defs); static void set_storage_bits (def_t *def, storage_class_t storage) @@ -76,30 +76,42 @@ set_storage_bits (def_t *def, storage_class_t storage) def->external = 0; def->local = 0; def->param = 0; + def->argument = 0; break; case sc_extern: def->global = 1; def->external = 1; def->local = 0; def->param = 0; + def->argument = 0; break; case sc_static: def->external = 0; def->global = 0; def->local = 0; def->param = 0; + def->argument = 0; break; case sc_local: def->external = 0; def->global = 0; def->local = 1; def->param = 0; + def->argument = 0; break; case sc_param: def->external = 0; def->global = 0; def->local = 1; def->param = 1; + def->argument = 0; + break; + case sc_argument: + def->external = 0; + def->global = 0; + def->local = 1; + def->param = 0; + def->argument = 1; break; } def->initialized = 0; @@ -140,7 +152,7 @@ new_def (const char *name, type_t *type, defspace_t *space, if (!space && storage != sc_extern) internal_error (0, "non-external def with no storage space"); - if (obj_is_class (type)) { + if (is_class (type) || (is_array (type) && is_class(type->t.array.type))) { error (0, "statically allocated instance of class %s", type->t.class->name); return def; @@ -148,11 +160,18 @@ new_def (const char *name, type_t *type, defspace_t *space, if (storage != sc_extern) { int size = type_size (type); + int alignment = type->alignment; + if (!size) { error (0, "%s has incomplete type", name); size = 1; + alignment = 1; } - def->offset = defspace_alloc_loc (space, size); + if (alignment < 1) { + print_type (type); + internal_error (0, "type has no alignment"); + } + def->offset = defspace_alloc_aligned_loc (space, size, alignment); } return def; @@ -173,14 +192,12 @@ alias_def (def_t *def, type_t *type, int offset) internal_error (0, "aliasing a def to a larger type"); if (offset < 0 || offset + type_size (type) > type_size (def->type)) internal_error (0, "invalid alias offset"); - if (type == def->type) - return def; for (alias = def->alias_defs; alias; alias = alias->next) { if (alias->type == type && alias->offset == offset) return alias; } ALLOC (16384, def_t, defs, alias); - alias->name = save_string (va ("[%s:%d]", def->name, offset)); + alias->name = save_string (va (0, "[%s:%d]", def->name, offset)); alias->return_addr = __builtin_return_address (0); alias->offset = offset; alias->offset_reloc = 1; @@ -189,32 +206,42 @@ alias_def (def_t *def, type_t *type, int offset) alias->line = pr.source_line; alias->file = pr.source_file; alias->next = def->alias_defs; + alias->reg = def->reg; def->alias_defs = alias; return alias; } def_t * -temp_def (etype_t type, int size) +temp_def (type_t *type) { def_t *temp; - defspace_t *space = current_func->symtab->space; + defspace_t *space = current_func->locals->space; + int size = type_size (type); + int alignment = type->alignment; + if (size < 1 || size > MAX_DEF_SIZE) { + internal_error (0, "%d invalid size for temp def", size); + } + if (alignment < 1) { + internal_error (0, "temp type has no alignment"); + } if ((temp = current_func->temp_defs[size - 1])) { current_func->temp_defs[size - 1] = temp->temp_next; temp->temp_next = 0; } else { ALLOC (16384, def_t, defs, temp); - temp->offset = defspace_alloc_loc (space, size); + temp->offset = defspace_alloc_aligned_loc (space, size, alignment); *space->def_tail = temp; space->def_tail = &temp->next; - temp->name = save_string (va (".tmp%d", current_func->temp_num++)); + temp->name = save_string (va (0, ".tmp%d", current_func->temp_num++)); } temp->return_addr = __builtin_return_address (0); - temp->type = ev_types[type]; + temp->type = type; temp->file = pr.source_file; temp->line = pr.source_line; set_storage_bits (temp, sc_local); temp->space = space; + temp->reg = current_func->temp_reg; return temp; } @@ -263,128 +290,148 @@ free_def (def_t *def) void def_to_ddef (def_t *def, ddef_t *ddef, int aux) { - type_t *type = def->type; + const type_t *type = unalias_type (def->type); if (aux) type = type->t.fldptr.type; // aux is true only for fields ddef->type = type->type; ddef->ofs = def->offset; - ddef->s_name = ReuseString (def->name); + ddef->name = ReuseString (def->name); +} + +static int +zero_memory (expr_t *local_expr, def_t *def, type_t *zero_type, + int init_size, int init_offset) +{ + int zero_size = type_size (zero_type); + expr_t *zero = convert_nil (new_nil_expr (), zero_type); + expr_t *dst; + + for (; init_offset < init_size + 1 - zero_size; init_offset += zero_size) { + dst = new_def_expr (def); + dst = new_offset_alias_expr (zero_type, dst, init_offset); + append_expr (local_expr, assign_expr (dst, zero)); + } + return init_offset; +} + +static void +init_elements_nil (def_t *def) +{ + if (def->local && local_expr) { + // memset to 0 + int init_size = type_size (def->type); + int init_offset = 0; + + if (options.code.progsversion != PROG_ID_VERSION) { + init_offset = zero_memory (local_expr, def, &type_zero, + init_size, init_offset); + } + // probably won't happen any time soon, but who knows... + if (options.code.progsversion != PROG_ID_VERSION + && init_size - init_offset >= type_size (&type_quaternion)) { + init_offset = zero_memory (local_expr, def, &type_quaternion, + init_size, init_offset); + } + if (init_size - init_offset >= type_size (&type_vector)) { + init_offset = zero_memory (local_expr, def, &type_vector, + init_size, init_offset); + } + if (options.code.progsversion != PROG_ID_VERSION + && init_size - init_offset >= type_size (&type_double)) { + init_offset = zero_memory (local_expr, def, &type_double, + init_size, init_offset); + } + if (init_size - init_offset >= type_size (type_default)) { + zero_memory (local_expr, def, type_default, + init_size, init_offset); + } + } + // it's a global, so already initialized to 0 } static void init_elements (struct def_s *def, expr_t *eles) { - expr_t *e, *c; - int count, i, num_elements, base_offset; + expr_t *c; pr_type_t *g; - def_t *elements; + element_chain_t element_chain; + element_t *element; - base_offset = def->offset; - if (def->local && local_expr) - base_offset = 0; - if (is_array (def->type)) { - type_t *array_type = def->type->t.array.type; - int array_size = def->type->t.array.size; - elements = calloc (array_size, sizeof (def_t)); - for (i = 0; i < array_size; i++) { - elements[i].type = array_type; - elements[i].space = def->space; - elements[i].offset = base_offset + i * type_size (array_type); - } - num_elements = i; - } else if (is_struct (def->type) - || def->type == &type_vector - || def->type == &type_quaternion) { - symtab_t *symtab = def->type->t.symtab; - symbol_t *field; - - for (i = 0, field = symtab->symbols; field; field = field->next) { - if (field->sy_type != sy_var) - continue; - i++; - } - elements = calloc (i, sizeof (def_t)); - for (i = 0, field = symtab->symbols; field; field = field->next) { - if (field->sy_type != sy_var) - continue; - elements[i].type = field->type; - elements[i].space = def->space; - elements[i].offset = base_offset + field->s.offset; - i++; - } - num_elements = i; - } else { - error (eles, "invalid initializer"); + if (eles->type == ex_nil) { + init_elements_nil (def); return; } - for (count = 0, e = eles->e.block.head; e; count++, e = e->next) { - convert_name (e); - if (e->type == ex_nil && count < num_elements) - convert_nil (e, elements[count].type); - if (e->type == ex_error) { - free (elements); - return; - } - } - if (count > num_elements) { - if (options.warnings.initializer) - warning (eles, "excessive elements in initializer"); - count = num_elements; - } - for (i = 0, e = eles->e.block.head; i < count; i++, e = e->next) { - g = D_POINTER (pr_type_t, &elements[i]); - c = constant_expr (e); - if (c->type == ex_block) { - if (!is_array (elements[i].type) - && !is_struct (elements[i].type)) { - error (e, "type mismatch in initializer"); - continue; - } - init_elements (&elements[i], c); - continue; - } else if (c->type == ex_labelref) { - def_t loc; - loc.space = elements[i].space; - loc.offset = elements[i].offset; - reloc_def_op (c->e.labelref.label, &loc); - continue; - } else if (c->type == ex_value) { - if (c->e.value->type == ev_integer - && elements[i].type->type == ev_float) - convert_int (c); - if (get_type (c) != elements[i].type) { - error (e, "type mismatch in initializer"); - continue; - } - } else { - if (!def->local || !local_expr) { - error (e, "non-constant initializer"); - continue; - } - } - if (def->local && local_expr) { - int offset = elements[i].offset; - type_t *type = elements[i].type; - expr_t *ptr = new_pointer_expr (offset, type, def); - append_expr (local_expr, assign_expr (unary_expr ('.', ptr), c)); - } else { - if (c->type != ex_value) + element_chain.head = 0; + element_chain.tail = &element_chain.head; + build_element_chain (&element_chain, def->type, eles, 0); + + if (def->local && local_expr) { + expr_t *dst = new_def_expr (def); + assign_elements (local_expr, dst, &element_chain); + } else { + def_t dummy = *def; + for (element = element_chain.head; element; element = element->next) { + if (!element->expr + || ((c = constant_expr (element->expr))->type == ex_nil)) { + // nil is type agnostic 0 and defspaces are initialized to + // 0 on creation + continue; + } + if (c->type == ex_nil) { + c = convert_nil (c, element->type); + } + dummy.offset = def->offset + element->offset; + g = D_POINTER (pr_type_t, &dummy); + if (c->type == ex_labelref) { + // reloc_def_* use only the def's offset and space, so dummy + // is ok + reloc_def_op (c->e.labelref.label, &dummy); + continue; + } else if (c->type == ex_value) { + type_t *ctype = get_type (c); + if (ctype != element->type + && type_assignable (element->type, ctype)) { + if (!c->implicit + && !type_promotes (element->type, ctype)) { + warning (c, "initialization of %s with %s" + " (use a cast)\n)", + get_type_string (element->type), + get_type_string (ctype)); + } + expr_t *n = cast_expr (element->type, c); + n->line = c->line; + n->file = c->line; + c = n; + } + if (get_type (c) != element->type) { + error (c, "type mismatch in initializer"); + continue; + } + } else { + if (!def->local || !local_expr) { + error (c, "non-constant initializer"); + continue; + } + } + if (c->type != ex_value) { internal_error (c, "bogus expression type in init_elements()"); - if (c->e.value->type == ev_string) { - EMIT_STRING (def->space, g->string_var, + } + if (c->e.value->lltype == ev_string) { + EMIT_STRING (def->space, *(pr_string_t *) g, c->e.value->v.string_val); } else { memcpy (g, &c->e.value->v, type_size (get_type (c)) * 4); } } } - free (elements); + + free_element_chain (&element_chain); } -static void -init_vector_components (symbol_t *vector_sym, int is_field) +void +init_vector_components (symbol_t *vector_sym, int is_field, symtab_t *symtab) { expr_t *vector_expr; int i; @@ -396,10 +443,10 @@ init_vector_components (symbol_t *vector_sym, int is_field) symbol_t *sym; const char *name; - name = va ("%s_%s", vector_sym->name, fields[i]); - sym = symtab_lookup (current_symtab, name); + name = va (0, "%s_%s", vector_sym->name, fields[i]); + sym = symtab_lookup (symtab, name); if (sym) { - if (sym->table == current_symtab) { + if (sym->table == symtab) { if (sym->sy_type != sy_expr) { error (0, "%s redefined", name); sym = 0; @@ -407,7 +454,7 @@ init_vector_components (symbol_t *vector_sym, int is_field) expr = sym->s.expr; if (is_field) { if (expr->type != ex_value - || expr->e.value->type != ev_field) { + || expr->e.value->lltype != ev_field) { error (0, "%s redefined", name); sym = 0; } else { @@ -433,14 +480,15 @@ init_vector_components (symbol_t *vector_sym, int is_field) sym->sy_type = sy_expr; sym->s.expr = expr; if (!sym->table) - symtab_addsymbol (current_symtab, sym); + symtab_addsymbol (symtab, sym); } } static void -init_field_def (def_t *def, expr_t *init, storage_class_t storage) +init_field_def (def_t *def, expr_t *init, storage_class_t storage, + symtab_t *symtab) { - type_t *type = def->type->t.fldptr.type; + type_t *type = (type_t *) dereference_type (def->type);//FIXME cast def_t *field_def; symbol_t *field_sym; reloc_t *relocs = 0; @@ -470,8 +518,8 @@ init_field_def (def_t *def, expr_t *init, storage_class_t storage) def->nosave = 1; } // no support for initialized field vector componets (yet?) - if (type == &type_vector && options.code.vector_components) - init_vector_components (field_sym, 1); + if (is_vector(type) && options.code.vector_components) + init_vector_components (field_sym, 1, symtab); } else if (init->type == ex_symbol) { symbol_t *sym = init->e.symbol; symbol_t *field = symtab_lookup (pr.entity_fields, sym->name); @@ -483,20 +531,29 @@ init_field_def (def_t *def, expr_t *init, storage_class_t storage) } } -void -initialize_def (symbol_t *sym, type_t *type, expr_t *init, defspace_t *space, - storage_class_t storage) +static int +num_elements (expr_t *e) { - symbol_t *check = symtab_lookup (current_symtab, sym->name); + int count = 0; + for (e = e->e.block.head; e; e = e->next) { + count++; + } + return count; +} + +void +initialize_def (symbol_t *sym, expr_t *init, defspace_t *space, + storage_class_t storage, symtab_t *symtab) +{ + symbol_t *check = symtab_lookup (symtab, sym->name); reloc_t *relocs = 0; - if (!type) { - warning (0, "type for %s defaults to %s", sym->name, - type_default->name); - type = type_default; + if (check && symtab->parent && check->table == symtab->parent->parent + && symtab->parent->parent->type == stab_param) { + error (0, "%s shadows a parameter", sym->name); } - if (check && check->table == current_symtab) { - if (check->sy_type != sy_var || check->type != type) { + if (check && check->table == symtab) { + if (check->sy_type != sy_var || !type_same (check->type, sym->type)) { error (0, "%s redefined", sym->name); } else { // is var and same type @@ -504,7 +561,7 @@ initialize_def (symbol_t *sym, type_t *type, expr_t *init, defspace_t *space, internal_error (0, "half defined var"); if (storage == sc_extern) { if (init) - warning (0, "initializing external variable"); + error (0, "initializing external variable"); return; } if (init && check->s.def->initialized) { @@ -514,20 +571,10 @@ initialize_def (symbol_t *sym, type_t *type, expr_t *init, defspace_t *space, sym = check; } } - sym->type = type; + sym->sy_type = sy_var; if (!sym->table) - symtab_addsymbol (current_symtab, sym); -// if (storage == sc_global && init && is_scalar (type)) { -// sym->sy_type = sy_const; -// memset (&sym->s.value, 0, sizeof (&sym->s.value)); -// if (init->type != ex_value) { //FIXME arrays/structs -// error (0, "non-constant initializier"); -// } else { -// sym->s.value = init->e.value; -// convert_value (&sym->s.value, sym->type); -// } -// return; -// } + symtab_addsymbol (symtab, sym); + if (sym->s.def && sym->s.def->external) { //FIXME this really is not the right way relocs = sym->s.def->relocs; @@ -535,16 +582,22 @@ initialize_def (symbol_t *sym, type_t *type, expr_t *init, defspace_t *space, sym->s.def = 0; } if (!sym->s.def) { - sym->s.def = new_def (sym->name, type, space, storage); + if (is_array (sym->type) && !type_size (sym->type) + && init && init->type == ex_compound) { + sym->type = array_type (sym->type->t.array.type, + num_elements (init)); + } + sym->s.def = new_def (sym->name, sym->type, space, storage); reloc_attach_relocs (relocs, &sym->s.def->relocs); } - if (type == &type_vector && options.code.vector_components) - init_vector_components (sym, 0); - if (type->type == ev_field && storage != sc_local && storage != sc_param) - init_field_def (sym->s.def, init, storage); + if (is_vector(sym->type) && options.code.vector_components) + init_vector_components (sym, 0, symtab); + if (sym->type->type == ev_field && storage != sc_local + && storage != sc_param) + init_field_def (sym->s.def, init, storage, symtab); if (storage == sc_extern) { if (init) - warning (0, "initializing external variable"); + error (0, "initializing external variable"); return; } if (!init) @@ -552,48 +605,67 @@ initialize_def (symbol_t *sym, type_t *type, expr_t *init, defspace_t *space, convert_name (init); if (init->type == ex_error) return; - if (init->type == ex_nil) - convert_nil (init, type); - if ((is_array (type) || is_struct (type) - || type == &type_vector || type == &type_quaternion) - && init->type == ex_block && !init->e.block.result) { + if ((is_structural (sym->type) || is_nonscalar (sym->type)) + && (init->type == ex_compound || init->type == ex_nil)) { init_elements (sym->s.def, init); sym->s.def->initialized = 1; } else { - if (!type_assignable (type, get_type (init))) { - error (init, "type mismatch in initializer"); + type_t *init_type; + if (init->type == ex_nil) { + convert_nil (init, sym->type); + } + init_type = get_type (init); + if (!type_assignable (sym->type, init_type)) { + error (init, "type mismatch in initializer: %s = %s", + get_type_string (sym->type), get_type_string (init_type)); return; } - if (local_expr) { + if (storage == sc_local && local_expr) { sym->s.def->initialized = 1; init = assign_expr (new_symbol_expr (sym), init); // fold_constants takes care of int/float conversions append_expr (local_expr, fold_constants (init)); } else { - if (init->type != ex_value) { //FIXME enum etc - error (0, "non-constant initializier"); + if (!is_constant (init)) { + error (init, "non-constant initializier"); return; } - if (init->e.value->type == ev_pointer - || init->e.value->type == ev_field) { + while (init->type == ex_alias) { + init = init->e.alias.expr; + } + if (init->type != ex_value) { //FIXME enum etc + internal_error (0, "initializier not a value"); + return; + } + if (init->e.value->lltype == ev_ptr + || init->e.value->lltype == ev_field) { // FIXME offset pointers D_INT (sym->s.def) = init->e.value->v.pointer.val; if (init->e.value->v.pointer.def) reloc_def_field (init->e.value->v.pointer.def, sym->s.def); } else { ex_value_t *v = init->e.value; - if (is_scalar (sym->type)) + if (!init->implicit + && is_double (init_type) + && (is_integral (sym->type) || is_float (sym->type))) { + warning (init, "assigning double to %s in initializer " + "(use a cast)", sym->type->name); + } + if (!type_same (sym->type, init_type)) v = convert_value (v, sym->type); - if (v->type == ev_string) { + if (v->lltype == ev_string) { EMIT_STRING (sym->s.def->space, D_STRING (sym->s.def), v->v.string_val); } else { - memcpy (D_POINTER (void, sym->s.def), &v->v, - type_size (type) * sizeof (pr_type_t)); + memcpy (D_POINTER (pr_type_t, sym->s.def), &v->v, + type_size (sym->type) * sizeof (pr_type_t)); } } - sym->s.def->initialized = sym->s.def->constant = 1; - sym->s.def->nosave = 1; + sym->s.def->initialized = 1; + if (options.code.const_initializers) { + sym->s.def->constant = 1; + sym->s.def->nosave = 1; + } } } sym->s.def->initializer = init; @@ -658,8 +730,11 @@ def_visit_all (def_t *def, int overlap, return ret; if (def->alias) { def = def->alias; - if ((ret = visit (def, data))) + if (!(overlap & 4) && (ret = visit (def, data))) return ret; + overlap &= ~4; + } else { + overlap = 0; } for (def = def->alias_defs; def; def = def->next) { if (def == start_def) diff --git a/tools/qfcc/source/defspace.c b/tools/qfcc/source/defspace.c index f227209d3..97333dae6 100644 --- a/tools/qfcc/source/defspace.c +++ b/tools/qfcc/source/defspace.c @@ -44,15 +44,9 @@ #include "QF/sys.h" #include "QF/va.h" -#include "qfcc.h" -#include "defspace.h" -#include "diagnostic.h" -#include "expr.h" -#include "options.h" -#include "reloc.h" -#include "strpool.h" -#include "struct.h" -#include "type.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" typedef struct locref_s { struct locref_s *next; @@ -60,8 +54,35 @@ typedef struct locref_s { int size; } locref_t; -static defspace_t *spaces_freelist; -static locref_t *locrefs_freelist; +ALLOC_STATE (defspace_t, spaces); +ALLOC_STATE (locref_t, locrefs); + +static locref_t * +new_locref (int ofs, int size, locref_t *next) +{ + locref_t *loc; + + ALLOC (1024, locref_t, locrefs, loc); + loc->ofs = ofs; + loc->size = size; + loc->next = next; + return loc; +} + +static void +del_locref (locref_t *loc) +{ + FREE (locrefs, loc); +} + +static defspace_t * +new_defspace (void) +{ + defspace_t *space; + + ALLOC (1024, defspace_t, spaces, space); + return space; +} #define GROW 1024 @@ -84,23 +105,17 @@ grow_space_global (defspace_t *space) static int grow_space_virtual (defspace_t *space) { - int size; - - if (space->size <= space->max_size) - return 1; - - size = space->size + GROW; - size -= size % GROW; - space->max_size = size; + if (space->size > space->max_size) { + space->max_size = space->size; + } return 1; } defspace_t * defspace_new (ds_type_t type) { - defspace_t *space; + defspace_t *space = new_defspace (); - ALLOC (1024, defspace_t, spaces, space); space->def_tail = &space->defs; space->type = type; if (type == ds_backed) { @@ -113,36 +128,89 @@ defspace_new (ds_type_t type) return space; } +void +defspace_delete (defspace_t *space) +{ + locref_t **lr; + + for (lr = &space->free_locs; *lr; lr = &(*lr)->next) { + } + *lr = locrefs_freelist; + locrefs_freelist = space->free_locs; + + if (space->data) { + free (space->data); + } + + while (space->defs) { + def_t *def = space->defs; + space->defs = def->next; + def->space = 0; + free_def (def); + } +} + int defspace_alloc_loc (defspace_t *space, int size) { - int ofs; + return defspace_alloc_aligned_loc (space, size, 1); +} + +int +defspace_alloc_aligned_loc (defspace_t *space, int size, int alignment) +{ + int ofs, pad; locref_t *loc; locref_t **l = &space->free_locs; if (size <= 0) internal_error (0, "invalid number of words requested: %d", size); - while (*l && (*l)->size < size) - l = &(*l)->next; - if ((loc = *l)) { - ofs = (*l)->ofs; - if ((*l)->size == size) { - loc = *l; - *l = (*l)->next; - FREE (locrefs, loc); - } else { - (*l)->ofs += size; - (*l)->size -= size; + if (alignment <= 0) + internal_error (0, "invalid alignment requested: %d", alignment); + while ((loc = *l)) { + ofs = loc->ofs; + pad = alignment * ((ofs + alignment - 1) / alignment) - ofs; + // exact fit, so just shrink the block or remove it if there is no + // padding (any padding remains free) + if (size + pad == loc->size) { + loc->size -= size; + if (!loc->size) { + *l = loc->next; + del_locref (loc); + } + return ofs + pad; } - return ofs; + // there's excess space in the block. If there's no padding, then + // just shrink it, otherwise split it into two, one on either side + // of the allocated block, such that the padding remains free + if (size + pad < loc->size) { + if (!pad) { + loc->ofs += size; + loc->size -= size; + } else { + loc->next = new_locref (ofs + pad + size, + loc->size - ofs - pad, loc->next); + loc->size = pad; + } + return ofs + pad; + } + l = &(*l)->next; } ofs = space->size; - space->size += size; + pad = alignment * ((ofs + alignment - 1) / alignment) - ofs; + if (alignment > space->alignment) { + space->alignment = alignment; + } + space->size += size + pad; if (space->size > space->max_size) { if (!space->grow || !space->grow (space)) internal_error (0, "unable to allocate %d words", size); } - return ofs; + if (pad) { + // mark the padding as free + *l = new_locref (ofs, pad, 0); + } + return ofs + pad; } void @@ -184,17 +252,13 @@ defspace_free_loc (defspace_t *space, int ofs, int size) loc->size += loc->next->size; loc = loc->next; *l = loc->next; - FREE (locrefs, loc); + del_locref (loc); } return; } } // insert a new free block for the location to be freed - ALLOC (1024, locref_t, locrefs, loc); - loc->ofs = ofs; - loc->size = size; - loc->next = *l; - *l = loc; + *l = new_locref (ofs, size, *l); } int @@ -202,8 +266,54 @@ defspace_add_data (defspace_t *space, pr_type_t *data, int size) { int loc; - loc = defspace_alloc_loc (space, size); + loc = defspace_alloc_highwater (space, size); if (data) memcpy (space->data + loc, data, size * sizeof (pr_type_t)); return loc; } + +int +defspace_alloc_highwater (defspace_t *space, int size) +{ + return defspace_alloc_aligned_highwater (space, size, 1); +} + +int +defspace_alloc_aligned_highwater (defspace_t *space, int size, int alignment) +{ + if (size < 0) + internal_error (0, "invalid number of words requested: %d", size); + if (alignment <= 0) + internal_error (0, "invalid alignment requested: %d", alignment); + + int ofs = space->size; + int pad = alignment * ((ofs + alignment - 1) / alignment) - ofs; + if (alignment > space->alignment) { + space->alignment = alignment; + } + space->size += size + pad; + if (space->size > space->max_size) { + if (!space->grow || !space->grow (space)) + internal_error (0, "unable to allocate %d words", size); + } + locref_t **l = &space->free_locs; + if (pad) { + // mark the padding as free + *l = new_locref (ofs, pad, 0); + } + return ofs + pad; +} + +void +defspace_reset (defspace_t *space) +{ + space->size = 0; + while (space->free_locs) { + locref_t *l = space->free_locs; + space->free_locs = l->next; + del_locref (l); + } + if (space->data) { + memset (space->data, 0, space->max_size * sizeof (pr_type_t)); + } +} diff --git a/tools/qfcc/source/diagnostic.c b/tools/qfcc/source/diagnostic.c index 2b2347cc2..1c5d6451a 100644 --- a/tools/qfcc/source/diagnostic.c +++ b/tools/qfcc/source/diagnostic.c @@ -33,20 +33,25 @@ #include -#include "qfcc.h" -#include "class.h" -#include "diagnostic.h" -#include "expr.h" -#include "function.h" -#include "options.h" -#include "strpool.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/strpool.h" + +diagnostic_hook bug_hook; +diagnostic_hook error_hook; +diagnostic_hook warning_hook; +diagnostic_hook notice_hook; static void -report_function (expr_t *e) +report_function (const expr_t *e) { static function_t *last_func = (function_t *)-1L; - static string_t last_file; - string_t file = pr.source_file; + static pr_string_t last_file; + pr_string_t file = pr.source_file; srcline_t *srcline; if (e) @@ -72,75 +77,137 @@ report_function (expr_t *e) last_func = current_func; } -static void -_warning (expr_t *e, const char *fmt, va_list args) +void +print_srcline (int rep, const expr_t *e) { - string_t file = pr.source_file; + pr_string_t file = pr.source_file; int line = pr.source_line; - - report_function (e); - if (options.warnings.promote) { - options.warnings.promote = 0; // want to do this only once - fprintf (stderr, "%s: warnings treated as errors\n", "qfcc"); - pr.error_count++; + if (e) { + file = e->file; + line = e->line; } + if (rep) { + report_function (e); + } + printf ("%s:%d\n", GETSTR (file), line); +} + +static __attribute__((format(PRINTF, 4, 0))) void +format_message (dstring_t *message, const char *msg_type, const expr_t *e, + const char *fmt, va_list args) +{ + pr_string_t file = pr.source_file; + int line = pr.source_line; + const char *colon = fmt ? ": " : ""; if (e) { file = e->file; line = e->line; } - fprintf (stderr, "%s:%d: warning: ", GETSTR (file), line); - vfprintf (stderr, fmt, args); - fputs ("\n", stderr); + dsprintf (message, "%s:%d: %s%s", GETSTR (file), line, msg_type, colon); + if (fmt) { + davsprintf (message, fmt, args); + } +} + +static __attribute__((format(PRINTF, 4, 0))) void +__warning (expr_t *e, const char *file, int line, + const char *fmt, va_list args) +{ + static int promoted = 0; + dstring_t *message = dstring_new (); + + report_function (e); + if (options.warnings.promote) { + if (!promoted) { + promoted = 1; // want to do this only once + fprintf (stderr, "%s: warnings treated as errors\n", "qfcc"); + } + pr.error_count++; + format_message (message, "error", e, fmt, args); + } else { + format_message (message, "warning", e, fmt, args); + } + + if (options.verbosity > 0) { + dasprintf (message, " (%s:%d)", file, line); + } + if (warning_hook) { + warning_hook (message->str); + } else { + fprintf (stderr, "%s\n", message->str); + } + dstring_delete (message); } void -debug (expr_t *e, const char *fmt, ...) +_debug (expr_t *e, const char *file, int line, const char *fmt, ...) { va_list args; - if (options.verbosity < 1) + if (options.verbosity < 2) return; + report_function (e); va_start (args, fmt); { - string_t file = pr.source_file; - int line = pr.source_line; + dstring_t *message = dstring_new (); - report_function (e); - if (e) { - file = e->file; - line = e->line; - } - fprintf (stderr, "%s:%d: debug: ", GETSTR (file), line); - vfprintf (stderr, fmt, args); - fputs ("\n", stderr); + format_message (message, "debug", e, fmt, args); + dasprintf (message, " (%s:%d)", file, line); + fprintf (stderr, "%s\n", message->str); + dstring_delete (message); } va_end (args); } -void -bug (expr_t *e, const char *fmt, ...) +static __attribute__((noreturn, format(PRINTF, 4, 0))) void +__internal_error (const expr_t *e, const char *file, int line, + const char *fmt, va_list args) { - va_list args; - string_t file = pr.source_file; - int line = pr.source_line; - - va_start (args, fmt); + dstring_t *message = dstring_new (); report_function (e); - if (e) { - file = e->file; - line = e->line; + + format_message (message, "internal error", e, fmt, args); + dasprintf (message, " (%s:%d)", file, line); + fprintf (stderr, "%s\n", message->str); + dstring_delete (message); + abort (); +} + +void +_bug (expr_t *e, const char *file, int line, const char *fmt, ...) +{ + va_list args; + + if (options.bug.silent) + return; + + va_start (args, fmt); + if (options.bug.promote) { + __internal_error (e, file, line, fmt, args); + } + + { + dstring_t *message = dstring_new (); + + report_function (e); + + format_message (message, "BUG", e, fmt, args); + dasprintf (message, " (%s:%d)", file, line); + if (bug_hook) { + bug_hook (message->str); + } else { + fprintf (stderr, "%s\n", message->str); + } + dstring_delete (message); } - fprintf (stderr, "%s:%d: BUG: ", GETSTR (file), line); - vfprintf (stderr, fmt, args); - fputs ("\n", stderr); va_end (args); } expr_t * -notice (expr_t *e, const char *fmt, ...) +_notice (expr_t *e, const char *file, int line, const char *fmt, ...) { va_list args; @@ -149,73 +216,73 @@ notice (expr_t *e, const char *fmt, ...) va_start (args, fmt); if (options.notices.promote) { - _warning (e, fmt, args); + __warning (e, file, line, fmt, args); } else { - string_t file = pr.source_file; - int line = pr.source_line; + dstring_t *message = dstring_new (); report_function (e); - if (e) { - file = e->file; - line = e->line; + + format_message (message, "notice", e, fmt, args); + if (options.verbosity > 0) { + dasprintf (message, " (%s:%d)", file, line); } - fprintf (stderr, "%s:%d: notice: ", GETSTR (file), line); - vfprintf (stderr, fmt, args); - fputs ("\n", stderr); + if (notice_hook) { + notice_hook (message->str); + } else { + fprintf (stderr, "%s\n", message->str); + } + dstring_delete (message); } va_end (args); return e; } expr_t * -warning (expr_t *e, const char *fmt, ...) +_warning (expr_t *e, const char *file, int line, const char *fmt, ...) { va_list args; va_start (args, fmt); - _warning (e, fmt, args); + __warning (e, file, line, fmt, args); va_end (args); return e; } -static void -_error (expr_t *e, const char *err, const char *fmt, va_list args) -{ - string_t file = pr.source_file; - int line = pr.source_line; - - report_function (e); - - if (e) { - file = e->file; - line = e->line; - } - fprintf (stderr, "%s:%d: %s%s", GETSTR (file), line, err, - fmt ? ": " : ""); - if (fmt) - vfprintf (stderr, fmt, args); - fputs ("\n", stderr); - pr.error_count++; -} - void -internal_error (expr_t *e, const char *fmt, ...) +_internal_error (const expr_t *e, const char *file, int line, + const char *fmt, ...) { va_list args; va_start (args, fmt); - _error (e, "internal error", fmt, args); + __internal_error (e, file, line, fmt, args); va_end (args); - abort (); } expr_t * -error (expr_t *e, const char *fmt, ...) +_error (expr_t *e, const char *file, int line, const char *fmt, ...) { va_list args; + pr.error_count++; + + report_function (e); + va_start (args, fmt); - _error (e, "error", fmt, args); + { + dstring_t *message = dstring_new (); + + format_message (message, "error", e, fmt, args); + if (options.verbosity > 0) { + dasprintf (message, " (%s:%d)", file, line); + } + if (error_hook) { + error_hook (message->str); + } else { + fprintf (stderr, "%s\n", message->str); + } + dstring_delete (message); + } va_end (args); if (!e) diff --git a/tools/qfcc/source/disassemble.c b/tools/qfcc/source/disassemble.c index 06624013c..5b8ec9735 100644 --- a/tools/qfcc/source/disassemble.c +++ b/tools/qfcc/source/disassemble.c @@ -56,27 +56,28 @@ #include "QF/progs.h" #include "QF/sys.h" -#include "qfprogs.h" +#include "tools/qfcc/include/qfprogs.h" void disassemble_progs (progs_t *pr) { unsigned int i; - for (i = 0; i < pr->progs->numstatements; i++) { + for (i = 0; i < pr->progs->statements.count; i++) { dfunction_t *desc = func_find (i); if (desc) { bfunction_t func; func.first_statement = desc->first_statement; - func.parm_start = desc->parm_start; + func.params_start = desc->params_start; func.locals = desc->locals; - func.numparms = desc->numparms; - memcpy (func.parm_size, desc->parm_size, sizeof (func.parm_size)); + func.numparams = desc->numparams; + memcpy (func.param_size, desc->param_size, + sizeof (func.param_size)); func.descriptor = desc; - Sys_Printf ("%s:\n", PR_GetString (pr, desc->s_name)); + Sys_Printf ("%s:\n", PR_GetString (pr, desc->name)); pr->pr_xfunction = &func; } PR_PrintStatement (pr, &pr->pr_statements[i], 2 | (verbosity > 1)); diff --git a/tools/qfcc/source/dot.c b/tools/qfcc/source/dot.c index 1844a1fe9..333104dad 100644 --- a/tools/qfcc/source/dot.c +++ b/tools/qfcc/source/dot.c @@ -35,10 +35,13 @@ #include "QF/va.h" -#include "dot.h" -#include "function.h" -#include "qfcc.h" -#include "strpool.h" +#include "tools/qfcc/include/dot.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/strpool.h" + +static function_t *last_func; +static int dot_index; void dump_dot (const char *stage, void *data, @@ -46,8 +49,18 @@ dump_dot (const char *stage, void *data, { char *fname; - fname = nva ("%s.%s.%s.dot", GETSTR (pr.source_file), current_func->name, - stage); + if (last_func != current_func) { + last_func = current_func; + dot_index = 0; + } else { + dot_index++; + } + if (current_func) { + fname = nva ("%s.%s.%03d.%s.dot", options.output_file, + current_func->name, dot_index, stage); + } else { + fname = nva ("%s.%03d.%s.dot", options.output_file, dot_index, stage); + } dump_func (data, fname); free (fname); } diff --git a/tools/qfcc/source/dot_dag.c b/tools/qfcc/source/dot_dag.c index bf0e26c63..9d6165a11 100644 --- a/tools/qfcc/source/dot_dag.c +++ b/tools/qfcc/source/dot_dag.c @@ -44,11 +44,11 @@ #include "QF/set.h" #include "QF/va.h" -#include "dags.h" -#include "statements.h" -#include "strpool.h" -#include "symtab.h" -#include "type.h" +#include "tools/qfcc/include/dags.h" +#include "tools/qfcc/include/statements.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" static void print_node_def (dstring_t *dstr, dag_t *dag, dagnode_t *node) @@ -124,6 +124,14 @@ print_node (dstring_t *dstr, dag_t *dag, dagnode_t *node) node, dag->nodes[edge_iter->element]); } set_delete (edges); + if (0) { + for (edge_iter = set_first (node->reachable); edge_iter; + edge_iter = set_next (edge_iter)) { + dasprintf (dstr, + " \"dagnode_%p\" -> \"dagnode_%p\" [style=dotted];\n", + node, dag->nodes[edge_iter->element]); + } + } if (0 && !set_is_empty (node->identifiers)) { set_iter_t *id_iter; daglabel_t *id; @@ -176,6 +184,7 @@ dot_dump_dag (void *_dag, const char *filename) dstring_t *dstr = dstring_newstr(); dasprintf (dstr, "digraph dag_%p {\n", dag); + dasprintf (dstr, " graph [label=\"%s\"];\n", quote_string (filename)); dasprintf (dstr, " layout=dot;\n"); dasprintf (dstr, " clusterrank=local;\n"); dasprintf (dstr, " rankdir=TB;\n"); diff --git a/tools/qfcc/source/dot_expr.c b/tools/qfcc/source/dot_expr.c index accd8485a..5b8b5b0c2 100644 --- a/tools/qfcc/source/dot_expr.c +++ b/tools/qfcc/source/dot_expr.c @@ -38,6 +38,7 @@ # include #endif #include +#include #include #include @@ -46,34 +47,27 @@ #include "qfalloca.h" -#include "expr.h" -#include "symtab.h" -#include "type.h" -#include "qc-parse.h" -#include "strpool.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/method.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" -static const char *expr_names[] = +#include "tools/qfcc/source/qc-parse.h" + +#define EX_EXPR(expr) #expr, +const char *expr_names[] = { - "error", - "state", - "bool", - "label", - "labelref", - "block", - "expr", - "uexpr", - "symbol", - "temp", - "vector", - "nil", - "value", +#include "tools/qfcc/include/expr_names.h" + 0 }; +#undef EX_EXPR const char * get_op_string (int op) { switch (op) { - case PAS: return ".="; case OR: return "||"; case AND: return "&&"; case EQ: return "=="; @@ -88,6 +82,7 @@ get_op_string (int op) case '*': return "*"; case '/': return "/"; case '%': return "%"; + case MOD: return "%%"; case '&': return "&"; case '|': return "|"; case '^': return "^"; @@ -96,20 +91,11 @@ get_op_string (int op) case SHL: return "<<"; case SHR: return ">>"; case '.': return "."; - case 'i': return ""; - case 'n': return ""; - case IFBE: return ""; - case IFB: return ""; - case IFAE: return ""; - case IFA: return ""; - case 'g': return ""; - case 'r': return ""; - case 's': return ""; - case 'c': return ""; - case 'A': return ""; case 'C': return ""; - case 'M': return ""; - case 'm': return ""; + case CROSS: return "@cross"; + case DOT: return "@dot"; + case HADAMARD: return "@hadamard"; + case SCALE: return "@scale"; default: return "unknown"; } @@ -155,20 +141,20 @@ print_bool (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) int indent = level * 2 + 2; int i, count; int tl_count = 0, fl_count = 0; - ex_bool_t *bool = &e->e.bool; + ex_bool_t *boolean = &e->e.boolean; dasprintf (dstr, "%*se_%p [shape=none,label=<\n", indent, "", e); dasprintf (dstr, "%*s\n", indent + 2, ""); - dasprintf (dstr, "%*s\n", + dasprintf (dstr, "%*s\n", indent + 4, "", e->line); dasprintf (dstr, "%*s\n", indent + 4, ""); - if (bool->true_list) - tl_count = bool->true_list->size; - if (bool->false_list) - fl_count = bool->false_list->size; + if (boolean->true_list) + tl_count = boolean->true_list->size; + if (boolean->false_list) + fl_count = boolean->false_list->size; count = min (tl_count, fl_count); for (i = 0; i < count; i++) dasprintf (dstr, "%*s" @@ -176,28 +162,28 @@ print_bool (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) for ( ; i < tl_count; i++) dasprintf (dstr, "%*s%s\n", indent, "", i, - i == count ? va ("", - bool->true_list->size - count) + i == count ? va (0, "", + boolean->true_list->size - count) : ""); for ( ; i < fl_count; i++) dasprintf (dstr, "%*s%s\n", indent, "", - i == count ? va ("", - bool->false_list->size - count) + i == count ? va (0, "", + boolean->false_list->size - count) : "", i); dasprintf (dstr, "%*s
<bool>(%d)
<boolean>(%d)
truefalse
t
t
f
\n", indent + 2, ""); dasprintf (dstr, "%*s>];\n", indent, ""); if (e->next) next = e->next; - _print_expr (dstr, e->e.bool.e, level, id, next); + _print_expr (dstr, e->e.boolean.e, level, id, next); for (i = 0; i < tl_count; i++) dasprintf (dstr, "%*se_%p:t%d -> e_%p;\n", indent, "", e, i, - bool->true_list->e[i]); + boolean->true_list->e[i]); for (i = 0; i < fl_count; i++) dasprintf (dstr, "%*se_%p:f%d -> e_%p;\n", indent, "", e, i, - bool->false_list->e[i]); - dasprintf (dstr, "%*se_%p -> e_%p;\n", indent, "", e, e->e.bool.e); + boolean->false_list->e[i]); + dasprintf (dstr, "%*se_%p -> e_%p;\n", indent, "", e, e->e.boolean.e); } static void @@ -246,7 +232,7 @@ print_block (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) "\n", indent + 4, ""); for (se = e->e.block.head, i = 0; se; se = se->next, i++) dasprintf (dstr, "%*s%d%s\n", - indent + 4, "", i, i, expr_names[se->type]); + indent + 4, "", se->line, i, expr_names[se->type]); dasprintf (dstr, "%*s\n", indent + 2, ""); dasprintf (dstr, "%*s>];\n", indent, ""); @@ -264,6 +250,115 @@ print_block (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) } } +static void +print_subexpr (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) +{ + int indent = level * 2 + 2; + + _print_expr (dstr, e->e.expr.e1, level, id, next); + _print_expr (dstr, e->e.expr.e2, level, id, next); + dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"l\"];\n", indent, "", e, + e->e.expr.e1); + dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"r\"];\n", indent, "", e, + e->e.expr.e2); + dasprintf (dstr, "%*se_%p [label=\"%s\\n%d\"];\n", indent, "", e, + get_op_string (e->e.expr.op), e->line); +} + +static void +print_alias (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) +{ + int indent = level * 2 + 2; + + _print_expr (dstr, e->e.alias.expr, level, id, next); + dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"a\"];\n", indent, "", e, + e->e.alias.expr); + if (e->e.alias.offset) { + _print_expr (dstr, e->e.alias.offset, level, id, next); + dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"o\"];\n", indent, "", e, + e->e.alias.offset); + } + + dstring_t *typestr = dstring_newstr(); + print_type_str (typestr, e->e.alias.type); + dasprintf (dstr, "%*se_%p [label=\"%s (%s)\\n%d\"];\n", indent, "", e, + "", typestr->str, e->line); + dstring_delete (typestr); +} + +static void +print_address (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) +{ + int indent = level * 2 + 2; + + _print_expr (dstr, e->e.address.lvalue, level, id, next); + dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"&\"];\n", indent, "", e, + e->e.address.lvalue); + if (e->e.address.offset) { + _print_expr (dstr, e->e.address.offset, level, id, next); + dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"+\"];\n", indent, "", e, + e->e.address.offset); + } + + dstring_t *typestr = dstring_newstr(); + print_type_str (typestr, e->e.address.type); + dasprintf (dstr, "%*se_%p [label=\"%s (%s)\\n%d\"];\n", indent, "", e, + "&", typestr->str, e->line); + dstring_delete (typestr); +} + +static void +print_assign (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) +{ + int indent = level * 2 + 2; + + _print_expr (dstr, e->e.assign.dst, level, id, next); + dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"lval\"];\n", indent, "", e, + e->e.assign.dst); + _print_expr (dstr, e->e.assign.src, level, id, next); + dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"rval\"];\n", indent, "", e, + e->e.assign.src); + + dasprintf (dstr, "%*se_%p [label=\"%s\\n%d\"];\n", indent, "", e, + "=", e->line); +} + +static void +print_conditional (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) +{ + int indent = level * 2 + 2; + static const char *condition[] = { + "ifz", "ifb", "ifa", 0, "ifnz", "ifnb", "ifna", 0 + }; + + _print_expr (dstr, e->e.branch.test, level, id, next); + dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"t\"];\n", indent, "", e, + e->e.branch.test); + dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"g\"];\n", indent, "", e, + e->e.branch.target); + if (e->next) { + next = e->next; + } + if (next) { + dasprintf (dstr, "%*se_%p -> e_%p [constraint=true," + "style=dashed];\n", indent, "", e, next); + } + dasprintf (dstr, "%*se_%p [label=\"%s\\n%d\"];\n", indent, "", e, + condition [e->e.branch.type], e->line); +} + +static void +print_jump (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) +{ + int indent = level * 2 + 2; + + _print_expr (dstr, e->e.branch.target, level, id, next); + dasprintf (dstr, "%*se_%p [label=\"%s\\n%d\"];\n", indent, "", e, + "jump", e->line); + dasprintf (dstr, "%*se_%p -> \"e_%p\";\n", indent, "", e, + e->e.branch.target); +} + static void print_call (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) { @@ -272,13 +367,13 @@ print_call (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) int i, count; expr_t **args; - for (count = 0, p = e->e.expr.e2; p; p = p->next) + for (count = 0, p = e->e.branch.args; p; p = p->next) count++; args = alloca (count * sizeof (expr_t *)); - for (i = 0, p = e->e.expr.e2; p; p = p->next, i++) + for (i = 0, p = e->e.branch.args; p; p = p->next, i++) args[count - 1 - i] = p; - _print_expr (dstr, e->e.expr.e1, level, id, next); + _print_expr (dstr, e->e.branch.target, level, id, next); dasprintf (dstr, "%*se_%p [label=\"call", indent, "", e); for (i = 0; i < count; i++) dasprintf (dstr, "|p%d", i, i); @@ -288,60 +383,63 @@ print_call (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) dasprintf (dstr, "%*se_%p:p%d -> e_%p;\n", indent + 2, "", e, i, args[i]); } - dasprintf (dstr, "%*se_%p:c -> e_%p;\n", indent, "", e, e->e.expr.e1); + dasprintf (dstr, "%*se_%p:c -> e_%p;\n", indent, "", e, + e->e.branch.target); } static void -print_subexpr (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) +print_branch (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) +{ + switch (e->e.branch.type) { + case pr_branch_eq: + case pr_branch_lt: + case pr_branch_gt: + case pr_branch_ne: + case pr_branch_ge: + case pr_branch_le: + print_conditional (dstr, e, level, id, next); + break; + case pr_branch_jump: + print_jump (dstr, e, level, id, next); + break; + case pr_branch_call: + print_call (dstr, e, level, id, next); + break; + } +} + +static void +print_return (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) { int indent = level * 2 + 2; - if (e->e.expr.op == 'c') { - print_call (dstr, e, level, id, next); - return; - } else if (e->e.expr.op == 'i' || e->e.expr.op == 'n' - || e->e.expr.op == IFB || e->e.expr.op ==IFBE - || e->e.expr.op == IFA || e->e.expr.op ==IFAE) { - _print_expr (dstr, e->e.expr.e1, level, id, next); - dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"t\"];\n", indent, "", e, - e->e.expr.e1); - dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"g\"];\n", indent, "", e, - e->e.expr.e2); - if (e->next) - next = e->next; - if (next) - dasprintf (dstr, "%*se_%p -> e_%p [constraint=true," - "style=dashed];\n", indent, "", e, next); - } else { - _print_expr (dstr, e->e.expr.e1, level, id, next); - _print_expr (dstr, e->e.expr.e2, level, id, next); - dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"l\"];\n", indent, "", e, - e->e.expr.e1); - dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"r\"];\n", indent, "", e, - e->e.expr.e2); + if (e->e.retrn.ret_val) { + _print_expr (dstr, e->e.retrn.ret_val, level, id, next); + dasprintf (dstr, "%*se_%p -> \"e_%p\";\n", indent, "", e, + e->e.retrn.ret_val); } dasprintf (dstr, "%*se_%p [label=\"%s\\n%d\"];\n", indent, "", e, - get_op_string (e->e.expr.op), e->line); + "return", e->line); } static void print_uexpr (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) { int indent = level * 2 + 2; - dstring_t *typestr = dstring_newstr(); - if (e->e.expr.op != 'g' && e->e.expr.e1) - _print_expr (dstr, e->e.expr.e1, level, id, next); - if (e->e.expr.op == 'A') { - dstring_copystr (typestr, "\\n"); - print_type_str (typestr, e->e.expr.type); - } - if (e->e.expr.op != 'r' || e->e.expr.e1) - dasprintf (dstr, "%*se_%p -> \"e_%p\";\n", indent, "", e, - e->e.expr.e1); - dasprintf (dstr, "%*se_%p [label=\"%s%s\\n%d\"];\n", indent, "", e, - get_op_string (e->e.expr.op), typestr->str, e->line); - dstring_delete (typestr); + _print_expr (dstr, e->e.expr.e1, level, id, next); + dasprintf (dstr, "%*se_%p -> \"e_%p\";\n", indent, "", e, e->e.expr.e1); + dasprintf (dstr, "%*se_%p [label=\"%s\\n%d\"];\n", indent, "", e, + get_op_string (e->e.expr.op), e->line); +} + +static void +print_def (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) +{ + int indent = level * 2 + 2; + + dasprintf (dstr, "%*se_%p [label=\"d %s\\n%d\"];\n", indent, "", e, + e->e.def->name, e->line); } static void @@ -367,7 +465,21 @@ print_vector (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) { int indent = level * 2 + 2; - dasprintf (dstr, "%*se_%p [label=\"vector FIXME\"];\n", indent, "", e); + for (expr_t *ele = e->e.vector.list; ele; ele = ele->next) { + _print_expr (dstr, ele, level, id, next); + dasprintf (dstr, "%*se_%p -> \"e_%p\";\n", indent, "", e, ele); + } + dasprintf (dstr, "%*se_%p [label=\"vector %d\"];\n", indent, "", e, + e->line); +} + +static void +print_selector (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) +{ + int indent = level * 2 + 2; + + dasprintf (dstr, "%*se_%p [label=\"%s\"];\n", indent, "", e, + e->e.selector.sel->name); } static void @@ -383,90 +495,148 @@ static void print_value (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) { int indent = level * 2 + 2; - type_t *type; const char *label = "?!?"; - switch (e->e.value->type) { - case ev_string: - label = va ("\\\"%s\\\"", quote_string (e->e.value->v.string_val)); - break; - case ev_float: - label = va ("f %g", e->e.value->v.float_val); - break; - case ev_vector: - label = va ("'%g %g %g'", - e->e.value->v.vector_val[0], - e->e.value->v.vector_val[1], - e->e.value->v.vector_val[2]); - break; - case ev_quat: - label = va ("'%g %g %g %g'", - e->e.value->v.quaternion_val[0], - e->e.value->v.quaternion_val[1], - e->e.value->v.quaternion_val[2], - e->e.value->v.quaternion_val[3]); - break; - case ev_pointer: - type = e->e.value->v.pointer.type; - if (e->e.value->v.pointer.def) - label = va ("(%s)[%d]<%s>", - type ? pr_type_name[type->type] : "???", - e->e.value->v.pointer.val, - e->e.value->v.pointer.def->name); - else - label = va ("(%s)[%d]", - type ? pr_type_name[type->type] : "???", - e->e.value->v.pointer.val); - break; - case ev_field: - label = va ("field %d", e->e.value->v.pointer.val); - break; - case ev_entity: - label = va ("ent %d", e->e.value->v.integer_val); - break; - case ev_func: - label = va ("func %d", e->e.value->v.integer_val); - break; - case ev_integer: - label = va ("i %d", e->e.value->v.integer_val); - break; - case ev_uinteger: - label = va ("u %u", e->e.value->v.uinteger_val); - break; - case ev_short: - label = va ("s %d", e->e.value->v.short_val); - break; - case ev_void: - label = ""; - break; - case ev_invalid: - label = ""; - break; - case ev_type_count: - label = ""; - break; + label = get_value_string (e->e.value); + if (is_string (e->e.value->type)) { + label = quote_string (html_string (label)); } dasprintf (dstr, "%*se_%p [label=\"%s\\n%d\"];\n", indent, "", e, label, e->line); } +static void +print_compound (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) +{ + int indent = level * 2 + 2; + dasprintf (dstr, "%*se_%p [label=\"compound init\"];\n", indent, "", e); +} + +static void +print_memset (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) +{ + int indent = level * 2 + 2; + expr_t *dst = e->e.memset.dst; + expr_t *val = e->e.memset.val; + expr_t *count = e->e.memset.count; + _print_expr (dstr, dst, level, id, next); + _print_expr (dstr, val, level, id, next); + _print_expr (dstr, count, level, id, next); + dasprintf (dstr, "%*se_%p -> \"e_%p\";\n", indent, "", e, dst); + dasprintf (dstr, "%*se_%p -> \"e_%p\";\n", indent, "", e, val); + dasprintf (dstr, "%*se_%p -> \"e_%p\";\n", indent, "", e, count); + dasprintf (dstr, "%*se_%p [label=\"memset\"];\n", indent, "", e); +} + +static void +print_adjstk (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) +{ + int indent = level * 2 + 2; + + dasprintf (dstr, "%*se_%p [label=\"adjstk %d:%d\\n%d\"];\n", indent, "", e, + e->e.adjstk.mode, e->e.adjstk.offset, e->line); +} + +static void +print_with (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) +{ + int indent = level * 2 + 2; + expr_t *with = e->e.with.with; + + _print_expr (dstr, with, level, id, next); + dasprintf (dstr, "%*se_%p -> \"e_%p\";\n", indent, "", e, with); + dasprintf (dstr, "%*se_%p [label=\"with %d:%d\\n%d\"];\n", indent, "", e, + e->e.with.mode, e->e.with.reg, e->line); +} + +static void +print_args (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) +{ + int indent = level * 2 + 2; + + dasprintf (dstr, "%*se_%p [label=\"...\\n%d\"];\n", indent, "", e, + e->line); +} + +static void +print_horizontal (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) +{ + int indent = level * 2 + 2; + + _print_expr (dstr, e->e.hop.vec, level, id, next); + dasprintf (dstr, "%*se_%p -> \"e_%p\";\n", indent, "", e, e->e.hop.vec); + dasprintf (dstr, "%*se_%p [label=\"hop %s\\n%d\"];\n", indent, "", e, + get_op_string (e->e.hop.op), e->line); +} + +static void +print_swizzle (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) +{ + static char swizzle_components[] = "xyzw"; + int indent = level * 2 + 2; + ex_swizzle_t swiz = e->e.swizzle; + const char *swizzle = ""; + + for (int i = 0; i < 4; i++) { + if (swiz.zero & (1 << i)) { + swizzle = va (0, "%s0", swizzle); + } else { + swizzle = va (0, "%s%s%c", swizzle, + swiz.neg & (1 << i) ? "-" : "", + swizzle_components[swiz.source[i]]); + } + } + + _print_expr (dstr, swiz.src, level, id, next); + dasprintf (dstr, "%*se_%p -> \"e_%p\";\n", indent, "", e, swiz.src); + dasprintf (dstr, "%*se_%p [label=\"swizzle %s\\n%d\"];\n", indent, "", e, + swizzle, e->line); +} + +static void +print_extend (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) +{ + int indent = level * 2 + 2; + ex_extend_t extend = e->e.extend; + + _print_expr (dstr, extend.src, level, id, next); + dasprintf (dstr, "%*se_%p -> \"e_%p\";\n", indent, "", e, extend.src); + dasprintf (dstr, "%*se_%p [label=\"extend %d\\n%d\"];\n", indent, "", e, + extend.extend, e->line); +} + static void _print_expr (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) { - static print_f print_funcs[] = { - print_error, - print_state, - print_bool, - print_label, - print_labelref, - print_block, - print_subexpr, - print_uexpr, - print_symbol, - print_temp, - print_vector, - print_nil, - print_value, + static print_f print_funcs[ex_count] = { + [ex_error] = print_error, + [ex_state] = print_state, + [ex_bool] = print_bool, + [ex_label] = print_label, + [ex_labelref] = print_labelref, + [ex_block] = print_block, + [ex_expr] = print_subexpr, + [ex_uexpr] = print_uexpr, + [ex_def] = print_def, + [ex_symbol] = print_symbol, + [ex_temp] = print_temp, + [ex_vector] = print_vector, + [ex_selector] = print_selector, + [ex_nil] = print_nil, + [ex_value] = print_value, + [ex_compound] = print_compound, + [ex_memset] = print_memset, + [ex_alias] = print_alias, + [ex_address] = print_address, + [ex_assign] = print_assign, + [ex_branch] = print_branch, + [ex_return] = print_return, + [ex_adjstk] = print_adjstk, + [ex_with] = print_with, + [ex_args] = print_args, + [ex_horizontal] = print_horizontal, + [ex_swizzle] = print_swizzle, + [ex_extend] = print_extend, }; int indent = level * 2 + 2; @@ -478,12 +648,16 @@ _print_expr (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) return; e->printid = id; - if ((int) e->type < 0 || e->type > ex_value) { - dasprintf (dstr, "%*se_%p [label=\"(bad expr type)\\n%d\"];\n", - indent, "", e, e->line); + if ((int) e->type < 0 || e->type >= ex_count || !print_funcs[e->type]) { + const char *type = va (0, "%d", e->type); + if ((unsigned) e->type < ex_count) { + type = expr_names[e->type]; + } + dasprintf (dstr, "%*se_%p [label=\"(bad expr type: %s)\\n%d\"];\n", + indent, "", e, type, e->line); return; } - print_funcs [e->type] (dstr, e, level, id, next); + print_funcs[e->type] (dstr, e, level, id, next); } @@ -495,6 +669,7 @@ dump_dot_expr (void *_e, const char *filename) expr_t *e = (expr_t *) _e; dasprintf (dstr, "digraph expr_%p {\n", e); + dasprintf (dstr, " graph [label=\"%s\"];\n", quote_string (filename)); dasprintf (dstr, " layout=dot; rankdir=TB; compound=true;\n"); _print_expr (dstr, e, 0, ++id, 0); dasprintf (dstr, "}\n"); diff --git a/tools/qfcc/source/dot_flow.c b/tools/qfcc/source/dot_flow.c index 7c8357611..4a37156b4 100644 --- a/tools/qfcc/source/dot_flow.c +++ b/tools/qfcc/source/dot_flow.c @@ -44,18 +44,22 @@ #include "QF/set.h" #include "QF/va.h" -#include "dags.h" -#include "flow.h" -#include "function.h" -#include "expr.h" -#include "statements.h" -#include "strpool.h" +#include "tools/qfcc/include/dags.h" +#include "tools/qfcc/include/flow.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/statements.h" +#include "tools/qfcc/include/strpool.h" + +typedef struct { + void (*node) (dstring_t *, flowgraph_t *, flownode_t *, int); + void (*edge) (dstring_t *, flowgraph_t *, flowedge_t *, int); + void (*extra) (dstring_t *, flowgraph_t *, int); +} flow_print_t; typedef struct { const char *type; - void (*print_node) (dstring_t *, flowgraph_t *, flownode_t *, int); - void (*print_edge) (dstring_t *, flowgraph_t *, flowedge_t *, int); - void (*print_extra) (dstring_t *, flowgraph_t *, int); + flow_print_t *print; } flow_dot_t; static void @@ -110,7 +114,7 @@ print_flow_node_dag (dstring_t *dstr, flowgraph_t *graph, flownode_t *node, int level) { if (node->dag) - print_dag (dstr, node->dag, va ("%d (%d)", node->id, node->dfn)); + print_dag (dstr, node->dag, va (0, "%d (%d)", node->id, node->dfn)); else print_flow_node (dstr, graph, node, level); } @@ -177,27 +181,21 @@ print_flow_node_live (dstring_t *dstr, flowgraph_t *graph, flownode_t *node, int level) { int indent = level * 2 + 2; - int live; set_t *use = node->live_vars.use; set_t *def = node->live_vars.def; set_t *in = node->live_vars.in; set_t *out = node->live_vars.out; - live = node->live_vars.out && !set_is_empty (node->live_vars.out); - - if (live) { - dasprintf (dstr, "%*sfn_%p [label=\"", indent, "", node); - dasprintf (dstr, "use: %s\\n", set_as_string (use)); - dasprintf (dstr, "def: %s\\n", set_as_string (def)); - dasprintf (dstr, "in: %s\\n", set_as_string (in)); - dasprintf (dstr, "out: %s\"];\n", set_as_string (out)); - } else { - print_flow_node (dstr, graph, node, level); - } + dasprintf (dstr, "%*sfn_%p [label=\"", indent, "", node); + dasprintf (dstr, "use: %s\\n", set_as_string (use)); + dasprintf (dstr, "def: %s\\n", set_as_string (def)); + dasprintf (dstr, "in: %s\\n", set_as_string (in)); + dasprintf (dstr, "out: %s", set_as_string (out)); + dasprintf (dstr, "\"];\n"); } static void -print_extra_live (dstring_t *dstr, flowgraph_t *graph, int level) +print_flow_vars (dstring_t *dstr, flowgraph_t *graph, int level) { int indent = level * 2 + 2; int i; @@ -206,10 +204,54 @@ print_extra_live (dstring_t *dstr, flowgraph_t *graph, int level) dasprintf (dstr, "%*sfv_%p [shape=none,label=<\n", indent, "", graph); dasprintf (dstr, "%*s\n", indent + 2, ""); + dasprintf (dstr, "%*s\n", + indent + 4, ""); + dasprintf (dstr, "%*s" + "" + "\n", indent + 4, ""); for (i = 0; i < graph->func->num_vars; i++) { var = graph->func->vars[i]; - dasprintf (dstr, "%*s\n", indent + 4, "", - var->number, html_string(operand_string (var->op))); + dasprintf (dstr, "%*s", + indent + 4, "", + var->number, html_string(operand_string (var->op)), + var->flowaddr, + set_as_string (var->define)); + dasprintf (dstr, "", set_as_string (var->use)); + dasprintf (dstr, "", set_as_string (var->udchains)); + dasprintf (dstr, "", set_as_string (var->duchains)); + dasprintf (dstr, "\n"); + } + dasprintf (dstr, "%*s
flow vars
#nameaddrdefineuseuddu
(%d) %s
%d%s%d%s%s%s%s
>];\n", indent + 2, ""); + + dasprintf (dstr, "%*sud_%p [shape=none,label=<\n", indent, "", graph); + dasprintf (dstr, "%*s\n", indent + 2, ""); + dasprintf (dstr, "%*s\n", + indent + 4, ""); + dasprintf (dstr, "%*s" + "\n", + indent + 4, ""); + for (i = 0; i < graph->func->num_ud_chains; i++) { + udchain_t ud = graph->func->ud_chains[i]; + dasprintf (dstr, "%*s" + "", + indent + 4, "", i, ud.var, ud.usest, ud.defst); + } + dasprintf (dstr, "%*s
ud chains
#varusedef
%d%d%d%d
>];\n", indent + 2, ""); + + dasprintf (dstr, "%*sdu_%p [shape=none,label=<\n", indent, "", graph); + dasprintf (dstr, "%*s\n", indent + 2, ""); + dasprintf (dstr, "%*s\n", + indent + 4, ""); + dasprintf (dstr, "%*s" + "\n", + indent + 4, ""); + for (i = 0; i < graph->func->num_ud_chains; i++) { + udchain_t du = graph->func->du_chains[i]; + dasprintf (dstr, "%*s" + "", + indent + 4, "", i, du.var, du.defst, du.usest); } dasprintf (dstr, "%*s
du chains
#vardefuse
%d%d%d%d
>];\n", indent + 2, ""); } @@ -296,12 +338,35 @@ print_flow_edge_statements (dstring_t *dstr, flowgraph_t *graph, dasprintf (dstr, "];\n"); } +static flow_print_t null_print[] = { + { print_flow_node, print_flow_edge }, + { 0 } +}; +static flow_print_t dag_print[] = { + { print_flow_node_dag, print_flow_edge_dag}, + { 0 } +}; +static flow_print_t live_print[] = { + { print_flow_node_live, print_flow_edge, print_flow_vars}, + { print_flow_node_statements, print_flow_edge_statements}, + { 0 } +}; +static flow_print_t reaching_print[] = { + { print_flow_node_reaching, print_flow_edge, print_flow_vars}, + { print_flow_node_statements, print_flow_edge_statements}, + { 0 } +}; +static flow_print_t statements_print[] = { + { print_flow_node_statements, print_flow_edge_statements}, + { 0 } +}; + static flow_dot_t flow_dot_methods[] = { - {"", print_flow_node, print_flow_edge}, - {"dag", print_flow_node_dag, print_flow_edge_dag}, - {"live", print_flow_node_live, print_flow_edge, print_extra_live}, - {"reaching", print_flow_node_reaching, print_flow_edge}, - {"statements", print_flow_node_statements, print_flow_edge_statements}, + {"", null_print}, + {"dag", dag_print}, + {"live", live_print}, + {"reaching", reaching_print}, + {"statements", statements_print}, }; static void @@ -311,21 +376,24 @@ print_flowgraph (flow_dot_t *method, flowgraph_t *graph, const char *filename) dstring_t *dstr = dstring_newstr(); dasprintf (dstr, "digraph flowgraph_%s_%p {\n", method->type, graph); + dasprintf (dstr, " graph [label=\"%s\"];\n", quote_string (filename)); dasprintf (dstr, " layout=dot;\n"); dasprintf (dstr, " clusterrank=local;\n"); dasprintf (dstr, " rankdir=TB;\n"); dasprintf (dstr, " compound=true;\n"); - for (i = 0; i < graph->num_nodes; i++) { - method->print_node (dstr, graph, graph->nodes[i], 0); + for (flow_print_t *print = method->print; print->node; print++) { + for (i = 0; i < graph->num_nodes; i++) { + print->node (dstr, graph, graph->nodes[i], 0); + } + for (i = 0; i < graph->num_edges; i++) { + if ((int) graph->edges[i].head >= graph->num_nodes + || (int) graph->edges[i].tail >= graph->num_nodes) + continue; // dummy node + print->edge (dstr, graph, &graph->edges[i], 0); + } + if (print->extra) + print->extra (dstr, graph, 0); } - for (i = 0; i < graph->num_edges; i++) { - if ((int) graph->edges[i].head >= graph->num_nodes - || (int) graph->edges[i].tail >= graph->num_nodes) - continue; // dummy node - method->print_edge (dstr, graph, &graph->edges[i], 0); - } - if (method->print_extra) - method->print_extra (dstr, graph, 0); dasprintf (dstr, "}\n"); if (filename) { diff --git a/tools/qfcc/source/dot_sblock.c b/tools/qfcc/source/dot_sblock.c index 72b245efe..eaca92300 100644 --- a/tools/qfcc/source/dot_sblock.c +++ b/tools/qfcc/source/dot_sblock.c @@ -41,27 +41,54 @@ #include #include +#include #include -#include "dags.h" -#include "flow.h" -#include "expr.h" -#include "qfcc.h" -#include "function.h" -#include "statements.h" -#include "strpool.h" -#include "symtab.h" -#include "type.h" +#include "tools/qfcc/include/dags.h" +#include "tools/qfcc/include/flow.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/statements.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" + +//#define SHOW_SETS static void flow_statement (dstring_t *dstr, statement_t *s) { dasprintf (dstr, " "); - dasprintf (dstr, "%d", s->number); + dasprintf (dstr, "%d:%d", s->number, s->expr ? s->expr->line : -1); dasprintf (dstr, "%s", html_string(quote_string (s->opcode))); dasprintf (dstr, "%s", html_string(operand_string (s->opa))); dasprintf (dstr, "%s", html_string(operand_string (s->opb))); dasprintf (dstr, "%s", html_string(operand_string (s->opc))); +#ifdef SHOW_SETS + if (s->number >= 0) { + set_t *use = set_new (); + set_t *def = set_new (); + set_t *kill = set_new (); + set_t *ops = set_new (); + operand_t *operands[FLOW_OPERANDS]; + + flow_analyze_statement (s, use, def, kill, operands); + for (int i = 0; i < FLOW_OPERANDS; i++) { + if (operands[i]) { + set_add (ops, i); + } + } + dasprintf (dstr, "%s", html_string(set_as_string (use))); + dasprintf (dstr, "%s", html_string(set_as_string (def))); + dasprintf (dstr, "%s", html_string(set_as_string (kill))); + dasprintf (dstr, "%s", html_string(set_as_string (ops))); + + set_delete (use); + set_delete (def); + set_delete (kill); + } +#endif dasprintf (dstr, "\n"); } @@ -77,11 +104,16 @@ dot_sblock (dstring_t *dstr, sblock_t *sblock, int blockno) dasprintf (dstr, " \n"); dasprintf (dstr, " %p(%d)\n", sblock, blockno); - dasprintf (dstr, " \n"); + dasprintf (dstr, " \n"); for (l = sblock->labels; l; l = l->next) dasprintf (dstr, " %s(%d)\n", l->name, l->used); dasprintf (dstr, " \n"); - dasprintf (dstr, " \n"); +#ifdef SHOW_SETS + dasprintf (dstr, " use\n"); + dasprintf (dstr, " def\n"); + dasprintf (dstr, " kill\n"); + dasprintf (dstr, " ops\n"); +#endif dasprintf (dstr, " \n"); for (s = sblock->statements; s; s = s->next) flow_statement (dstr, s); @@ -128,6 +160,7 @@ print_sblock (sblock_t *sblock, const char *filename) dstring_t *dstr = dstring_newstr(); dasprintf (dstr, "digraph sblock_%p {\n", sblock); + dasprintf (dstr, " graph [label=\"%s\"];\n", quote_string (filename)); dasprintf (dstr, " layout=dot; rankdir=TB;\n"); for (i = 0; sblock; sblock = sblock->next, i++) flow_sblock (dstr, sblock, i); diff --git a/tools/qfcc/source/dot_type.c b/tools/qfcc/source/dot_type.c new file mode 100644 index 000000000..5f743dae7 --- /dev/null +++ b/tools/qfcc/source/dot_type.c @@ -0,0 +1,282 @@ +/* + dot_type.c + + "emit" types to dot (graphvis). + + Copyright (C) 2020 Bill Currie + + Author: Bill Currie + Date: 2020/03/28 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include + +#include +#include +#include +#include + +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/strpool.h" + +typedef void (*print_f) (dstring_t *dstr, type_t *, int, int); +static void dot_print_type (dstring_t *dstr, type_t *t, int level, int id); + +static void +print_pointer (dstring_t *dstr, type_t *t, int level, int id) +{ + int indent = level * 2 + 2; + type_t *aux = t->t.fldptr.type; + + dot_print_type (dstr, aux, level, id); + dasprintf (dstr, "%*st_%p -> \"t_%p\";\n", indent, "", t, aux); + dasprintf (dstr, "%*st_%p [label=\"%c\"];\n", indent, "", t, + t->type == ev_ptr ? '*' : '.'); +} + +static void +print_ellipsis (dstring_t *dstr, int level, int id) +{ + static int ellipsis_id; + int indent = level * 2 + 2; + + if (ellipsis_id == id) { + return; + } + ellipsis_id = id; + dasprintf (dstr, "%*st_ellipsis [label=\"...\"];\n", indent, ""); +} + +static void +print_function (dstring_t *dstr, type_t *t, int level, int id) +{ + int indent = level * 2 + 2; + const ty_func_t *func = &t->t.func; + type_t *ret = func->type; + type_t *param; + + dot_print_type (dstr, ret, level + 1, id); + if (func->num_params < 0) { + for (int i = 0; i < ~func->num_params; i++) { + param = func->param_types[i]; + dot_print_type (dstr, param, level + 1, id); + } + print_ellipsis (dstr, level, id); + } else { + for (int i = 0; i < func->num_params; i++) { + param = func->param_types[i]; + dot_print_type (dstr, param, level + 1, id); + } + } + dasprintf (dstr, "%*st_%p -> \"t_%p\" [label=\"r\"];\n", indent, "", + t, ret); + if (func->num_params < 0) { + for (int i = 0; i < ~func->num_params; i++) { + param = func->param_types[i]; + dasprintf (dstr, "%*st_%p -> \"t_%p\";\n", indent, "", t, param); + } + dasprintf (dstr, "%*st_%p -> \"t_ellipsis\";\n", indent, "", t); + } else { + for (int i = 0; i < func->num_params; i++) { + param = func->param_types[i]; + dasprintf (dstr, "%*st_%p -> \"t_%p\";\n", indent, "", t, param); + } + } + dasprintf (dstr, "%*st_%p [label=\"( )\"];\n", indent, "", t); +} + +static void +print_basic (dstring_t *dstr, type_t *t, int level, int id) +{ + if (t->type == ev_ptr || t->type == ev_field) { + print_pointer (dstr, t, level, id); + } else if (t->type == ev_func) { + print_function (dstr, t, level, id); + } else { + int indent = level * 2 + 2; + dasprintf (dstr, "%*st_%p [label=\"%s\"];\n", indent, "", t, t->name); + } +} + +static void +print_struct (dstring_t *dstr, type_t *t, int level, int id) +{ + int indent = level * 2 + 2; + const symtab_t *symtab = t->t.symtab; + const symbol_t *sym; + int pnum; + static const char *struct_type_names[3] = {"struct", "union", "enum"}; + const char *struct_type = struct_type_names[t->meta - ty_struct]; + + if (!symtab) { + dasprintf (dstr, "%*st_%p [label=\"%s %s\"];\n", indent, "", t, + struct_type, quote_string (t->name)); + return; + } + if (t->meta != ty_enum) { + for (sym = symtab->symbols; sym; sym = sym->next) { + if (sym->sy_type != sy_var) { + continue; + } + dot_print_type (dstr, sym->type, level, id); + } + for (pnum = 0, sym = symtab->symbols; sym; sym = sym->next) { + if (sym->sy_type != sy_var) { + continue; + } + dasprintf (dstr, "%*st_%p:f%d -> \"t_%p\";\n", indent, "", + t, pnum++, sym->type); + } + } + dasprintf (dstr, "%*st_%p [shape=none,label=<\n", indent, "", t); + dasprintf (dstr, "%*s\n", + indent + 2, ""); + dasprintf (dstr, "%*s\n", + indent + 4, "", + struct_type, quote_string (t->name)); + for (pnum = 0, sym = symtab->symbols; sym; sym = sym->next) { + int val; + const char *port = ""; + if (sym->sy_type == sy_const) { + val = sym->s.value->v.int_val; + } else { + if (sym->sy_type != sy_var) { + continue; + } + val = sym->s.offset; + port = va (0, " port=\"f%d\"", pnum++); + } + dasprintf (dstr, "%*s%d\n", + indent + 4, "", + quote_string (sym->name), port, val); + } + dasprintf (dstr, "%*s
%s %s
%s
\n", indent + 2, ""); + dasprintf (dstr, "%*s>];\n", indent, ""); +} + +static void +print_array (dstring_t *dstr, type_t *t, int level, int id) +{ + int indent = level * 2 + 2; + type_t *type = t->t.array.type; + + dot_print_type (dstr, type, level, id); + dasprintf (dstr, "%*st_%p -> \"t_%p\";\n", indent, "", t, type); + if (t->t.array.base) { + dasprintf (dstr, "%*st_%p [label=\"[%d..%d]\"];\n", indent, "", t, + t->t.array.base, + t->t.array.base + t->t.array.size - 1); + } else { + dasprintf (dstr, "%*st_%p [label=\"[%d]\"];\n", indent, "", t, + t->t.array.size); + } +} + +static void +print_class (dstring_t *dstr, type_t *t, int level, int id) +{ + int indent = level * 2 + 2; + dasprintf (dstr, "%*st_%p [label=\"class '%s'\"];\n", indent, "", t, + t->t.class->name); +} + +static void +print_alias (dstring_t *dstr, type_t *t, int level, int id) +{ + int indent = level * 2 + 2; + type_t *aux = t->t.alias.aux_type; + type_t *full = t->t.alias.full_type; + + dot_print_type (dstr, aux, level, id); + dot_print_type (dstr, full, level, id); + dasprintf (dstr, "%*st_%p -> \"t_%p\";\n", indent, "", t, aux); + dasprintf (dstr, "%*st_%p -> \"t_%p\";\n", indent, "", t, full); + dasprintf (dstr, "%*st_%p [label=\"alias '%s'\"];\n", indent, "", t, + t->name); +} + +static void +dot_print_type (dstring_t *dstr, type_t *t, int level, int id) +{ + static print_f print_funcs[] = { + print_basic, + print_struct, + print_struct, + print_struct, + print_array, + print_class, + print_alias, + }; + int indent = level * 2 + 2; + + if (!t) { + dasprintf (dstr, "%*s\"e_%p\" [label=\"(null)\"];\n", indent, "", t); + return; + } + if (t->printid == id) // already printed this type + return; + t->printid = id; + + if ((unsigned) t->meta >= sizeof (print_funcs) / sizeof (print_funcs[0])) { + dasprintf (dstr, "%*se_%p [label=\"(bad type meta)\\n%d\"];\n", + indent, "", t, t->meta); + return; + } + print_funcs [t->meta] (dstr, t, level, id); +} + +void +dump_dot_type (void *_t, const char *filename) +{ + static int id = 0; + dstring_t *dstr = dstring_newstr (); + type_t *t = (type_t *) _t; + + dasprintf (dstr, "digraph type_%p {\n", t); + dasprintf (dstr, " graph [label=\"%s\"];\n", quote_string (filename)); + dasprintf (dstr, " layout=dot; rankdir=TB; compound=true;\n"); + dot_print_type (dstr, t, 0, ++id); + dasprintf (dstr, "}\n"); + + if (filename) { + QFile *file; + + file = Qopen (filename, "wt"); + Qwrite (file, dstr->str, dstr->size - 1); + Qclose (file); + } else { + fputs (dstr->str, stdout); + } + dstring_delete (dstr); +} diff --git a/tools/qfcc/source/dump_globals.c b/tools/qfcc/source/dump_globals.c index dce3bd265..895d95ce6 100644 --- a/tools/qfcc/source/dump_globals.c +++ b/tools/qfcc/source/dump_globals.c @@ -43,126 +43,68 @@ #include "QF/progs.h" #include "QF/va.h" -#include "obj_file.h" -#include "obj_type.h" -#include "qfprogs.h" -#include "reloc.h" -#include "strpool.h" +#include "tools/qfcc/include/obj_file.h" +#include "tools/qfcc/include/obj_type.h" +#include "tools/qfcc/include/qfprogs.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/strpool.h" static int cmp (const void *_a, const void *_b) { - const ddef_t *a = (const ddef_t *)_a; - const ddef_t *b = (const ddef_t *)_b; + const pr_def_t *a = (const pr_def_t *)_a; + const pr_def_t *b = (const pr_def_t *)_b; return a->ofs - b->ofs; } static void -dump_def (progs_t *pr, ddef_t *def, int indent) +dump_def (progs_t *pr, pr_def_t *def, int indent) { const char *name; const char *type; pr_uint_t offset; const char *comment; - string_t string; - const char *str; int saveglobal; - if (!def->type && !def->ofs && !def->s_name) + if (!def->type && !def->ofs && !def->name) return; - name = PR_GetString (pr, def->s_name); + name = PR_GetString (pr, def->name); type = pr_type_name[def->type & ~DEF_SAVEGLOBAL]; saveglobal = (def->type & DEF_SAVEGLOBAL) != 0; offset = def->ofs; comment = " invalid offset"; - if (offset < pr->progs->numglobals) { - comment = ""; - switch (def->type & ~DEF_SAVEGLOBAL) { - case ev_void: - break; - case ev_string: - string = G_INT (pr, offset); - if (string < 0 || string >= pr->progs->numstrings) { - str = "invalid string offset"; - comment = va (" %d %s", string, str); - } else { - str = quote_string (pr->pr_strings + G_INT (pr, offset)); - comment = va (" %d \"%s\"", string, str); - } - break; - case ev_float: - comment = va (" %g", G_FLOAT (pr, offset)); - break; - case ev_vector: - comment = va (" '%g %g %g'", - G_VECTOR (pr, offset)[0], - G_VECTOR (pr, offset)[1], - G_VECTOR (pr, offset)[2]); - break; - case ev_entity: - break; - case ev_field: - comment = va (" %x", G_INT (pr, offset)); - break; - case ev_func: - { - func_t func = G_FUNCTION (pr, offset); - int start; - if (func >= 0 && func < pr->progs->numfunctions) { - start = pr->pr_functions[func].first_statement; - if (start > 0) - comment = va (" %d @ %x", func, start); - else - comment = va (" %d = #%d", func, -start); - } else { - comment = va (" %d = illegal function", func); - } - } - break; - case ev_pointer: - comment = va (" %x", G_INT (pr, offset)); - break; - case ev_quat: - comment = va (" '%g %g %g %g'", - G_QUAT (pr, offset)[0], - G_QUAT (pr, offset)[1], - G_QUAT (pr, offset)[2], - G_QUAT (pr, offset)[3]); - break; - case ev_integer: - comment = va (" %d", G_INT (pr, offset)); - break; - case ev_short: - break; - case ev_invalid: - comment = " struct?"; - break; - case ev_type_count: - break; + if (offset < pr->progs->globals.count) { + static dstring_t *value_dstr; + if (!value_dstr) { + value_dstr = dstring_newstr (); } + dstring_clearstr (value_dstr); + qfot_type_t *ty = &G_STRUCT (pr, qfot_type_t, def->type_encoding); + comment = PR_Debug_ValueString (pr, offset, ty, value_dstr); } - printf ("%*s %x %d %s %s%s\n", indent * 12, "", - offset, saveglobal, name, type, comment); + printf ("%*s %x:%d %d %s %s:%x %s\n", indent * 12, "", + offset, def->size, saveglobal, name, type, def->type_encoding, + comment); } void dump_globals (progs_t *pr) { unsigned int i; - ddef_t *global_defs = pr->pr_globaldefs; + pr_def_t *global_defs = pr->pr_globaldefs; if (sorted) { - global_defs = malloc (pr->progs->numglobaldefs * sizeof (ddef_t)); + global_defs = malloc (pr->progs->globaldefs.count * sizeof (ddef_t)); memcpy (global_defs, pr->pr_globaldefs, - pr->progs->numglobaldefs * sizeof (ddef_t)); - qsort (global_defs, pr->progs->numglobaldefs, sizeof (ddef_t), cmp); + pr->progs->globaldefs.count * sizeof (ddef_t)); + qsort (global_defs, pr->progs->globaldefs.count, sizeof (ddef_t), cmp); } - for (i = 0; i < pr->progs->numglobaldefs; i++) { - ddef_t *def = &global_defs[i]; + for (i = 0; i < pr->progs->globaldefs.count; i++) { + pr_def_t *def = &global_defs[i]; dump_def (pr, def, 0); } } @@ -176,10 +118,10 @@ dump_fields (progs_t *pr) int offset; const char *comment; - for (i = 0; i < pr->progs->numfielddefs; i++) { - ddef_t *def = &pr->pr_fielddefs[i]; + for (i = 0; i < pr->progs->fielddefs.count; i++) { + pr_def_t *def = &pr->pr_fielddefs[i]; - name = PR_GetString (pr, def->s_name); + name = PR_GetString (pr, def->name); type = pr_type_name[def->type & ~DEF_SAVEGLOBAL]; offset = def->ofs; @@ -190,46 +132,93 @@ dump_fields (progs_t *pr) } void -dump_functions (progs_t *pr) +qfo_fields (qfo_t *qfo) { - int i, j; + unsigned int i; const char *name; - int start, count; + const char *typestr; + qfot_type_t *type; + int offset; const char *comment; + qfo_mspace_t *space = &qfo->spaces[qfo_entity_space]; - for (i = 0; i < pr->progs->numfunctions; i++) { - dfunction_t *func = &pr->pr_functions[i]; + if (qfo_entity_space >= qfo->num_spaces) { + printf ("no entity space\n"); + return; + } + if (!space->num_defs) { + printf ("no fields\n"); + return; + } - name = PR_GetString (pr, func->s_name); + for (i = 0; i < space->num_defs; i++) { + qfo_def_t *def = space->defs + i; + + name = QFO_GETSTR (qfo, def->name); + //FIXME check type + type = QFO_POINTER (qfo, qfo_type_space, qfot_type_t, def->type); + typestr = QFO_GETSTR (qfo, type->encoding); + offset = def->offset; comment = ""; + printf ("%d %s %s%s\n", offset, name, typestr, comment); + } +} + +void +dump_functions (progs_t *pr) +{ + pr_uint_t i, j, count; + const char *name; + int start; + const char *comment; + pr_def_t *encodings_def; + pr_ptr_t type_encodings = 0; + + encodings_def = PR_FindGlobal (pr, ".type_encodings"); + if (encodings_def) { + type_encodings = encodings_def->ofs; + } + + for (i = 0; i < pr->progs->functions.count; i++) { + dfunction_t *func = &pr->pr_functions[i]; + + name = PR_GetString (pr, func->name); + start = func->first_statement; if (start > 0) - comment = va (" @ %x", start); + comment = va (0, " @ %x", start); else - comment = va (" = #%d", -start); + comment = va (0, " = #%d", -start); - printf ("%-5d %s%s: %d (", i, name, comment, func->numparms); - if (func->numparms < 0) - count = -func->numparms - 1; + printf ("%-5d %s%s: %d (", i, name, comment, func->numparams); + if (func->numparams < 0) + count = -func->numparams - 1; else - count = func->numparms; + count = func->numparams; for (j = 0; j < count; j++) - printf (" %d", func->parm_size[j]); - printf (") %d @ %x", func->locals, func->parm_start); + printf (" %d:%d", func->param_size[j].alignment, + func->param_size[j].size); + printf (") %d @ %x", func->locals, func->params_start); puts (""); - if (pr->debug) { - pr_auxfunction_t *aux = pr->auxfunction_map[i]; - if (!aux) + if (type_encodings) { + pr_auxfunction_t *aux = PR_Debug_MappedAuxFunction (pr, i); + if (!aux) { continue; + } printf (" %d %s:%d %d %d %d %x\n", aux->function, - PR_GetString (pr, func->s_file), aux->source_line, + PR_GetString (pr, func->file), aux->source_line, aux->line_info, aux->local_defs, aux->num_locals, aux->return_type); - for (j = 0; j < (int)aux->num_locals; j++) - dump_def (pr, pr->local_defs + aux->local_defs + j, 1); + pr_def_t *local_defs = PR_Debug_LocalDefs (pr, aux); + if (!local_defs) { + continue; + } + for (j = 0; j < aux->num_locals; j++) { + dump_def (pr, local_defs + j, 1); + } } } } @@ -257,20 +246,21 @@ void qfo_globals (qfo_t *qfo) { qfo_def_t *def; - int i; - int space; + unsigned i; + unsigned space; int count = 0; for (space = 0; space < qfo->num_spaces; space++) { for (i = 0; i < qfo->spaces[space].num_defs; i++, count++) { def = &qfo->spaces[space].defs[i]; - printf ("%-5d %2d:%-5x %s %s %s", count, space, def->offset, + printf ("%-5d %2d:%-5x %s %s %x %s", count, space, def->offset, flags_string (def->flags), QFO_GETSTR (qfo, def->name), + def->type, QFO_TYPESTR (qfo, def->type)); - if (!(def->flags & QFOD_EXTERNAL) && qfo->spaces[space].d.data) + if (!(def->flags & QFOD_EXTERNAL) && qfo->spaces[space].data) printf (" %d", - qfo->spaces[space].d.data[def->offset].integer_var); + qfo->spaces[space].data[def->offset].value); puts (""); } } @@ -282,9 +272,14 @@ qfo_relocs (qfo_t *qfo) qfo_reloc_t *reloc; qfo_def_t *def; qfo_func_t *func; - int i; + int opind; + dstatement_t *statement; + unsigned i; for (i = 0; i < qfo->num_relocs; i++) { + if (i == qfo->num_relocs - qfo->num_loose_relocs) { + printf ("---- unbound relocs ----\n"); + } reloc = qfo->relocs + i; if ((unsigned) reloc->type > rel_def_field_ofs) { printf ("%d unknown reloc: %d\n", i, reloc->type); @@ -344,10 +339,13 @@ qfo_relocs (qfo_t *qfo) case rel_op_b_def_ofs: case rel_op_c_def_ofs: def = qfo->defs + reloc->target; - printf (" op.%c@%x def#%d %s", - reloc->type - rel_op_a_def_ofs + 'a', + opind = reloc->type - rel_op_a_def_ofs; + statement = QFO_STATEMENT (qfo, reloc->offset); + printf (" op.%c@%x def#%d %s+%d", + opind + 'a', reloc->offset, reloc->target, - QFO_GETSTR (qfo, def->name)); + QFO_GETSTR (qfo, def->name), + ((pr_ushort_t *)statement)[opind + 1]); break; case rel_def_def_ofs: def = qfo->defs + reloc->target; @@ -385,8 +383,9 @@ qfo_functions (qfo_t *qfo) { qfo_def_t *def; qfo_func_t *func; - int i, d; - int space; + unsigned i, j, d; + unsigned space; + qfo_mspace_t *locals; for (i = 0; i < qfo->num_funcs; i++) { func = &qfo->funcs[i]; @@ -410,24 +409,55 @@ qfo_functions (qfo_t *qfo) printf (" @ %x", func->code); else printf (" = #%d", -func->code); - puts (""); + printf (" loc: %d params: %d\n", + func->locals_space, func->params_start); + if (func->locals_space) { + locals = &qfo->spaces[func->locals_space]; + printf ("%*s%d %p %d %p %d %d\n", 16, "", locals->type, + locals->defs, locals->num_defs, + locals->data, locals->data_size, locals->id); + for (j = 0; j < locals->num_defs; j++) { + qfo_def_t *def = locals->defs + j; + int offset; + const char *typestr; + const char *name; + qfot_type_t *type; + + name = QFO_GETSTR (qfo, def->name); + //FIXME check type + type = QFO_POINTER (qfo, qfo_type_space, qfot_type_t, + def->type); + typestr = QFO_GETSTR (qfo, type->encoding); + offset = def->offset; + + printf ("%*s%d %s %s\n", 20, "", offset, name, typestr); + } + } } } static const char *ty_meta_names[] = { - "ty_none", + "ty_basic", "ty_struct", "ty_union", "ty_enum", "ty_array", "ty_class", + "ty_alias", + "ty_handle", }; #define NUM_META ((int)(sizeof (ty_meta_names) / sizeof (ty_meta_names[0]))) +const int vector_types = (1 << ev_float) + | (1 << ev_int) + | (1 << ev_uint) + | (1 << ev_double) + | (1 << ev_long) + | (1 << ev_ulong); static void dump_qfo_types (qfo_t *qfo, int base_address) { - pointer_t type_ptr; + pr_ptr_t type_ptr; qfot_type_t *type; const char *meta; int i, count; @@ -447,52 +477,63 @@ dump_qfo_types (qfo_t *qfo, int base_address) qfo->spaces[qfo_type_space].data_size); continue; } - if (type->ty < 0 || type->ty >= NUM_META) - meta = va ("invalid meta: %d", type->ty); + if ((unsigned) type->meta >= NUM_META) + meta = va (0, "invalid meta: %d", type->meta); else - meta = ty_meta_names[type->ty]; + meta = ty_meta_names[type->meta]; printf ("%-5x %-9s %-20s", type_ptr + base_address, meta, QFO_TYPESTR (qfo, type_ptr)); - if (type->ty < 0 || type->ty >= NUM_META) { + if ((unsigned) type->meta >= NUM_META) { puts (""); break; } - switch ((ty_meta_e) type->ty) { - case ty_none: - printf (" %-10s", (type->t.type < 0 - || type->t.type >= ev_type_count) + switch ((ty_meta_e) type->meta) { + case ty_basic: + printf (" %-10s", ((unsigned) type->type >= ev_type_count) ? "invalid type" - : pr_type_name[type->t.type]); - if (type->t.type == ev_func) { - printf (" %4x %d", type->t.func.return_type, - count = type->t.func.num_params); + : pr_type_name[type->type]); + if (type->type == ev_func) { + printf (" %4x %d", type->func.return_type, + count = type->func.num_params); if (count < 0) count = ~count; //ones complement for (i = 0; i < count; i++) - printf (" %x", type->t.func.param_types[i]); - } else if (type->t.type == ev_pointer - || type->t.type == ev_field) { - printf (" %4x", type->t.fldptr.aux_type); + printf (" %x", type->func.param_types[i]); + } else if (type->type == ev_ptr + || type->type == ev_field) { + printf (" %4x", type->fldptr.aux_type); + } else if ((1 << type->type) & vector_types + && type->basic.width > 1) { + printf ("[%d]", type->basic.width); } printf ("\n"); break; case ty_struct: case ty_union: case ty_enum: - printf (" %s\n", QFO_GETSTR (qfo, type->t.strct.tag)); - for (i = 0; i < type->t.strct.num_fields; i++) { + printf (" %s\n", QFO_GETSTR (qfo, type->strct.tag)); + for (i = 0; i < type->strct.num_fields; i++) { printf (" %-5x %4x %s\n", - type->t.strct.fields[i].type, - type->t.strct.fields[i].offset, - QFO_GETSTR (qfo, type->t.strct.fields[i].name)); + type->strct.fields[i].type, + type->strct.fields[i].offset, + QFO_GETSTR (qfo, type->strct.fields[i].name)); } break; case ty_array: - printf (" %-5x %d %d\n", type->t.array.type, - type->t.array.base, type->t.array.size); + printf (" %-5x %d %d\n", type->array.type, + type->array.base, type->array.size); break; case ty_class: - printf (" %-5x\n", type->t.class); + printf (" %-5x\n", type->class); + break; + case ty_alias: + printf (" %s %d %5x %5x\n", + QFO_GETSTR (qfo, type->alias.name), + type->alias.type, type->alias.aux_type, + type->alias.full_type); + break; + case ty_handle: + printf (" %-5x\n", type->handle.tag); break; } } @@ -509,7 +550,7 @@ dump_types (progs_t *pr) { qfo_mspace_t spaces[qfo_num_spaces]; qfo_t qfo; - ddef_t *encodings_def; + pr_def_t *encodings_def; qfot_type_encodings_t *encodings; encodings_def = PR_FindGlobal (pr, ".type_encodings"); @@ -532,10 +573,10 @@ dump_types (progs_t *pr) } memset (spaces, 0, sizeof (spaces)); spaces[qfo_strings_space].type = qfos_string; - spaces[qfo_strings_space].d.strings = pr->pr_strings; + spaces[qfo_strings_space].strings = pr->pr_strings; spaces[qfo_strings_space].data_size = pr->pr_stringsize; spaces[qfo_type_space].type = qfos_type; - spaces[qfo_type_space].d.data = pr->pr_globals + encodings->types; + spaces[qfo_type_space].data = pr->pr_globals + encodings->types; spaces[qfo_type_space].data_size = encodings->size; memset (&qfo, 0, sizeof (qfo)); qfo.spaces = spaces; diff --git a/tools/qfcc/source/dump_lines.c b/tools/qfcc/source/dump_lines.c index 847bdb5c2..4b95b3ad3 100644 --- a/tools/qfcc/source/dump_lines.c +++ b/tools/qfcc/source/dump_lines.c @@ -42,48 +42,153 @@ #include "QF/progs.h" -#include "qfprogs.h" +#include "QF/progs/pr_type.h" -void -dump_lines (progs_t *pr) +#include "tools/qfcc/include/obj_file.h" +#include "tools/qfcc/include/qfprogs.h" + +typedef struct { + const char *source_name; + const char *source_file; + pr_uint_t source_line; + pr_int_t first_statement; + pr_ptr_t return_type; + pr_uint_t local_defs; + pr_uint_t num_locals; + pr_uint_t line_info; + pr_uint_t function; +} func_data_t; + +typedef func_data_t *(*get_func_data_t)(unsigned func, void *data); + +static func_data_t * +progs_get_func_data (unsigned func_index, void *data) { - unsigned int i, line, addr; - pr_lineno_t *lineno; - pr_auxfunction_t *aux_func = 0; - dfunction_t *func = 0; + static func_data_t func_data; + progs_t *pr = (progs_t *) data; + pr_auxfunction_t *aux_func; + dfunction_t *func; - if (!pr->debug) - return; - for (i = 0; i < pr->debug->num_linenos; i++) { - lineno = &pr->linenos[i]; + memset (&func_data, 0, sizeof (func_data)); + aux_func = PR_Debug_AuxFunction (pr, func_index); + if (aux_func) { + func_data.source_line = aux_func->source_line; + func_data.return_type = aux_func->return_type; + func_data.num_locals = aux_func->num_locals; + func_data.local_defs = aux_func->local_defs; + func_data.line_info = aux_func->line_info; + func_data.function = aux_func->function; + if (aux_func->function < (unsigned int) pr->progs->functions.count) { + func = pr->pr_functions + aux_func->function; + func_data.source_file = pr->pr_strings + func->file; + func_data.source_name = pr->pr_strings + func->name; + func_data.first_statement = func->first_statement; + } + return &func_data; + } + return 0; +} +static void +dump_line_set (pr_lineno_t *lineno, unsigned count, + get_func_data_t get_func_data, void *data) +{ + unsigned int line, addr; + func_data_t *func_data = 0; + + for (; count-- > 0; lineno++) { if (!lineno->line) { - aux_func = 0; - func = 0; - if (lineno->fa.func < pr->debug->num_auxfunctions) - aux_func = pr->auxfunctions + lineno->fa.func; - if (aux_func - && aux_func->function < (unsigned int) pr->progs->numfunctions) - func = pr->pr_functions + aux_func->function; + func_data = get_func_data(lineno->fa.func, data); } printf ("%5u %5u", lineno->fa.addr, lineno->line); line = addr = -1; - if (aux_func) - line = aux_func->source_line + lineno->line; - if (func) - addr = lineno->line ? lineno->fa.addr - : (unsigned int) func->first_statement; - if (aux_func && func) - printf (" %05x %s:%u %s+%u %d", addr, pr->pr_strings + func->s_file, - line, pr->pr_strings + func->s_name, - addr - func->first_statement, aux_func->return_type); - else if (aux_func) - printf ("%u %u %u %u %u %d", aux_func->function, line, - aux_func->line_info, aux_func->local_defs, - aux_func->num_locals, aux_func->return_type); - else if (lineno->line) + if (func_data) { + line = func_data->source_line + lineno->line; + if (func_data->source_name) { + addr = lineno->line ? (pr_int_t) lineno->fa.addr + : func_data->first_statement; + printf (" %05x %s:%u %s+%u %d", addr, func_data->source_file, + line, func_data->source_name, + addr - func_data->first_statement, + func_data->return_type); + } else { + printf ("%u %u %u %u %u %d", func_data->function, line, + func_data->line_info, func_data->local_defs, + func_data->num_locals, func_data->return_type); + } + } else if (lineno->line) { printf ("%5x", lineno->fa.addr); + } printf ("\n"); } } + +void +dump_lines (progs_t *pr) +{ + pr_lineno_t *linenos; + pr_uint_t num_linenos; + if (!(linenos = PR_Debug_Linenos (pr, 0, &num_linenos))) + return; + dump_line_set (linenos, num_linenos, progs_get_func_data, pr); +} + +static func_data_t * +qfo_get_func_data (unsigned func_index, void *data) +{ + return (func_data_t *) data; +} + +static void +qfo_set_func_data (qfo_t *qfo, qfo_func_t *func, func_data_t *func_data) +{ + qfot_type_t *type; + + func_data->source_line = func->line; + //FIXME check type + type = QFO_POINTER (qfo, qfo_type_space, qfot_type_t, func->type); + func_data->return_type = type->func.return_type; + func_data->num_locals = -1; + if (func->locals_space < qfo->num_spaces) { + func_data->num_locals = qfo->spaces[func->locals_space].num_defs; + } + func_data->local_defs = func->locals_space; + func_data->line_info = func->line_info; + func_data->function = func - qfo->funcs; + func_data->source_file = QFO_GETSTR (qfo, func->file); + func_data->source_name = QFO_GETSTR (qfo, func->name); + func_data->first_statement = func->code; +} + +void +qfo_lines (qfo_t *qfo) +{ + static func_data_t func_data; + pr_lineno_t *start_lineno = 0; + pr_lineno_t *lineno; + qfo_func_t *func = 0; + + for (func = qfo->funcs; + (size_t) (func - qfo->funcs) < qfo->num_funcs; func++) { + if (!func->line_info) { + // builtin + continue; + } + if (func->line_info >= qfo->num_lines) { + printf ("%s: bad line info: %u >= %u\n", + QFO_GETSTR (qfo, func->name), + func->line_info, qfo->num_lines); + continue; + } + qfo_set_func_data(qfo, func, &func_data); + start_lineno = qfo->lines + func->line_info; + for (lineno = start_lineno + 1; + (size_t) (lineno - qfo->lines) < qfo->num_lines && lineno->line; + lineno++) + { + } + dump_line_set (start_lineno, lineno-start_lineno, + qfo_get_func_data, &func_data); + } +} diff --git a/tools/qfcc/source/dump_modules.c b/tools/qfcc/source/dump_modules.c index d55195aaa..70dfe35c0 100644 --- a/tools/qfcc/source/dump_modules.c +++ b/tools/qfcc/source/dump_modules.c @@ -40,11 +40,35 @@ #include -#include "QF/pr_obj.h" #include "QF/progs.h" #include "QF/va.h" -#include "qfprogs.h" +#include "QF/progs/pr_obj.h" + +#include "tools/qfcc/include/qfprogs.h" + +static void +dump_ivars (progs_t *pr, pr_ivar_list_t *ivars) +{ + if (!ivars || !ivars->ivar_count) { + puts (" { }"); + return; + } + puts (" {"); + for (int i = 0; i < ivars->ivar_count; i++) { + const char *name = ""; + const char *type = ""; + if (PR_StringValid (pr, ivars->ivar_list[i].ivar_name)) { + name = PR_GetString (pr, ivars->ivar_list[i].ivar_name); + } + if (PR_StringValid (pr, ivars->ivar_list[i].ivar_type)) { + type = PR_GetString (pr, ivars->ivar_list[i].ivar_type); + } + printf (" %d %s %s\n", ivars->ivar_list[i].ivar_offset, + name, type); + } + puts (" }"); +} static void dump_methods (progs_t *pr, pr_method_list_t *methods, int class) @@ -89,22 +113,41 @@ dump_selector (progs_t *pr, pr_sel_t *sel) printf (" %s\n", sel_types); } +static void +dump_method_description_list (progs_t *pr, char c, + pr_method_description_list_t *list) +{ + if (list) { + for (int i = 0; i < list->count; i++) { + printf ("%*s%s\n", 20, "", PR_GetString (pr, list->list[i].name)); + printf ("%*s%s\n", 20, "", PR_GetString (pr, list->list[i].types)); + } + } +} + static void dump_protocol (progs_t *pr, pr_protocol_t *proto) { const char *protocol_name = ""; - printf (" %d ", proto->class_pointer); + printf (" %x %x ", + (pr_ptr_t) ((pr_int_t *) proto - (pr_int_t *) pr->pr_globals), + proto->class_pointer); if (PR_StringValid (pr, proto->protocol_name)) protocol_name = PR_GetString (pr, proto->protocol_name); printf ("<%s>\n", protocol_name); + dump_method_description_list (pr, '-', + &G_STRUCT (pr, pr_method_description_list_t, + proto->instance_methods)); + dump_method_description_list (pr, '-', + &G_STRUCT (pr, pr_method_description_list_t, + proto->class_methods)); } static void dump_protocol_list (progs_t *pr, pr_protocol_list_t *list) { int i; - printf (" %d\n", list->next); - printf (" %d\n", list->count); + printf (" %x %d\n", list->next, list->count); for (i = 0; i < list->count; i++) { if (list->list[i] <= 0 || list->list[i] >= pr->globals_size) { printf ("invalid pointer\n"); @@ -139,6 +182,7 @@ dump_class (progs_t *pr, pr_class_t *class) printf (" meta:%x verion:%d info:%u size:%d\n", class->class_pointer, class->version, class->info, class->instance_size); + dump_ivars (pr, &G_STRUCT (pr, pr_ivar_list_t, class->ivars)); dump_methods (pr, &G_STRUCT (pr, pr_method_list_t, class->methods), 0); dump_methods (pr, &G_STRUCT (pr, pr_method_list_t, meta->methods), 1); printf (" %x\n", class->protocols); @@ -171,11 +215,37 @@ dump_category (progs_t *pr, pr_category_t *category) category->protocols)); } +static void +dump_static_instance_lists (progs_t *pr, pr_ptr_t instance_lists) +{ + pr_ptr_t *ptr = &G_STRUCT (pr, pr_ptr_t, instance_lists); + + printf (" static instance lists @ %x\n", instance_lists); + while (*ptr) { + __auto_type list = &G_STRUCT (pr, pr_static_instances_t, *ptr); + const char *class_name = "*** INVALID ***"; + + if (PR_StringValid (pr, list->class_name)) { + class_name = PR_GetString (pr, list->class_name); + } + printf (" %x %s\n", *ptr, class_name); + for (int i = 0; list->instances[i]; i++) { + if (!strcmp (class_name, "Protocol")) { + dump_protocol (pr, &G_STRUCT (pr, pr_protocol_t, + list->instances[i])); + } else { + printf (" %x\n", list->instances[i]); + } + } + ptr++; + } +} + static void dump_module (progs_t *pr, pr_module_t *module) { pr_symtab_t *symtab = &G_STRUCT (pr, pr_symtab_t, module->symtab); - pointer_t *ptr = symtab->defs; + pr_ptr_t *ptr = symtab->defs; pr_sel_t *sel = &G_STRUCT (pr, pr_sel_t, symtab->refs); int i; const char *module_name = ""; @@ -197,6 +267,9 @@ dump_module (progs_t *pr, pr_module_t *module) dump_class (pr, &G_STRUCT (pr, pr_class_t, *ptr++)); for (i = 0; i < symtab->cat_def_cnt; i++) dump_category (pr, &G_STRUCT (pr, pr_category_t, *ptr++)); + if (*ptr) { + dump_static_instance_lists (pr, *ptr); + } } void @@ -204,12 +277,12 @@ dump_modules (progs_t *pr) { unsigned int i; - for (i = 0; i < pr->progs->numglobaldefs; i++) { - ddef_t *def = &pr->pr_globaldefs[i]; + for (i = 0; i < pr->progs->globaldefs.count; i++) { + pr_def_t *def = &pr->pr_globaldefs[i]; const char *name = ""; - if (PR_StringValid (pr, def->s_name)) - name = PR_GetString (pr, def->s_name); + if (PR_StringValid (pr, def->name)) + name = PR_GetString (pr, def->name); if (strcmp (name, "_OBJ_MODULE") == 0) { printf ("module @ %x\n", def->ofs); dump_module (pr, &G_STRUCT (pr, pr_module_t, def->ofs)); diff --git a/tools/qfcc/source/dump_strings.c b/tools/qfcc/source/dump_strings.c index 91d6ca2ba..e09fd36e0 100644 --- a/tools/qfcc/source/dump_strings.c +++ b/tools/qfcc/source/dump_strings.c @@ -36,21 +36,22 @@ #include "QF/progs.h" #include "QF/sys.h" -#include "qfprogs.h" +#include "tools/qfcc/include/obj_file.h" +#include "tools/qfcc/include/qfprogs.h" -void -dump_strings (progs_t *pr) +static void +dump_string_block (const char *strblock, size_t size) { - int i = 0; - char *s = pr->pr_strings; + const char *s = strblock; - printf ("%d ", 0); - while (i++ < pr->progs->numstrings) { - switch (*s) { + printf ("%x \"", 0); + while ((size_t) (s - strblock) < size) { + byte c = *s++; + switch (c) { case 0: - fputs ("\n", stdout); - if (i < pr->progs->numstrings) - printf ("%d ", i); + fputs ("\"\n", stdout); + if ((size_t) (s - strblock) < size) + printf ("%zx \"", s - strblock); break; case 9: fputs ("\\t", stdout); @@ -61,10 +62,42 @@ dump_strings (progs_t *pr) case 13: fputs ("\\r", stdout); break; + case '\"': + fputs ("\\\"", stdout); + break; + case '\\': + fputs ("\\\\", stdout); + break; default: - fputc (sys_char_map[(unsigned char)*s], stdout); + if (c < 32 || c > 126) { + printf ("\\x%02x", c); + } else { + fputc (c, stdout); + } + //fputc (sys_char_map[c], stdout); break; } - s++; } } + +void +dump_strings (progs_t *pr) +{ + dump_string_block (pr->pr_strings, pr->progs->strings.count); +} + +void +qfo_strings (qfo_t *qfo) +{ + qfo_mspace_t *space = &qfo->spaces[qfo_strings_space]; + + if (qfo_strings_space >= qfo->num_spaces) { + printf ("no strings space\n"); + return; + } + if (!space->data_size) { + printf ("no strings\n"); + return; + } + dump_string_block (space->strings, space->data_size); +} diff --git a/tools/qfcc/source/emit.c b/tools/qfcc/source/emit.c index 34a5de3f2..d5615a794 100644 --- a/tools/qfcc/source/emit.c +++ b/tools/qfcc/source/emit.c @@ -42,37 +42,61 @@ #include #include -#include "codespace.h" -#include "def.h" -#include "defspace.h" -#include "debug.h" -#include "diagnostic.h" -#include "emit.h" -#include "function.h" -#include "opcodes.h" -#include "options.h" -#include "qfcc.h" -#include "reloc.h" -#include "statements.h" -#include "symtab.h" -#include "type.h" -#include "value.h" +#include "tools/qfcc/include/codespace.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/debug.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/emit.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/opcodes.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/statements.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" static def_t zero_def; +static def_t *get_operand_def (expr_t *expr, operand_t *op); + static def_t * -get_value_def (ex_value_t *value, etype_t type) +get_tempop_def (expr_t *expr, operand_t *tmpop, type_t *type) +{ + tempop_t *tempop = &tmpop->tempop; + if (tempop->def) { + return tempop->def; + } + if (tempop->alias) { + def_t *tdef = get_operand_def (expr, tempop->alias); + int offset = tempop->offset; + tempop->def = alias_def (tdef, type, offset); + } + if (!tempop->def) { + tempop->def = temp_def (type); + } + return tempop->def; +} + +static def_t * +get_value_def (expr_t *expr, ex_value_t *value, type_t *type) { def_t *def; - if (type == ev_short) { + if (is_short (type)) { def = new_def (0, &type_short, 0, sc_extern); def->offset = value->v.short_val; return def; } + if (is_ptr (type) && value->v.pointer.tempop && !value->v.pointer.def) { + value->v.pointer.def = get_tempop_def (expr, value->v.pointer.tempop, + type->t.fldptr.type); + } def = emit_value (value, 0); - if (type != def->type->type) - return alias_def (def, ev_types[type], 0); + if (type != def->type) + return alias_def (def, type, 0); return def; } @@ -83,22 +107,23 @@ get_operand_def (expr_t *expr, operand_t *op) return 0; switch (op->op_type) { case op_def: - return op->o.def; + return op->def; case op_value: - return get_value_def (op->o.value, op->type); + return get_value_def (expr, op->value, op->type); case op_label: - op->type = ev_short; + op->type = &type_short; zero_def.type = &type_short; return &zero_def; //FIXME case op_temp: - while (op->o.tempop.alias) - op = op->o.tempop.alias; - if (!op->o.tempop.def) - op->o.tempop.def = temp_def (op->type, op->size); - return op->o.tempop.def; + return get_tempop_def (expr, op, op->type); case op_alias: - return get_operand_def (expr, op->o.alias); + return get_operand_def (expr, op->alias); + case op_nil: + internal_error (expr, "unexpected nil operand"); + case op_pseudo: + internal_error (expr, "unexpected pseudo operand"); } + internal_error (expr, "unexpected operand"); return 0; } @@ -136,8 +161,8 @@ add_statement_op_ref (operand_t *op, dstatement_t *st, int field) int st_ofs = st - pr.code->code; reloc_t *reloc = new_reloc (0, st_ofs, rel_op_a_op + field); - reloc->next = op->o.label->dest->relocs; - op->o.label->dest->relocs = reloc; + reloc->next = op->label->dest->relocs; + op->label->dest->relocs = reloc; } } @@ -146,32 +171,132 @@ use_tempop (operand_t *op, expr_t *expr) { if (!op || op->op_type != op_temp) return; - while (op->o.tempop.alias) - op = op->o.tempop.alias; - if (--op->o.tempop.users == 0) - free_temp_def (op->o.tempop.def); - if (op->o.tempop.users <= -1) + while (op->tempop.alias) + op = op->tempop.alias; + if (--op->tempop.users == 0) + free_temp_def (op->tempop.def); + if (op->tempop.users <= -1) bug (expr, "temp users went negative: %s", operand_string (op)); } +static def_t * +cover_def_32 (def_t *def, int *adj) +{ + int offset = def->offset; + def_t *cover = def; + + if (def->alias) { + offset += def->alias->offset; + } + *adj = offset & 3; + if (offset & 3) { + if (def->alias) { + cover = alias_def (def->alias, def->type, def->offset); + } else { + cover = alias_def (def, def->type, 0); + } + cover->offset -= offset & 3; + } + return cover; +} + +static def_t * +cover_def_64 (def_t *def, int *adj) +{ + int offset = def->offset; + def_t *cover = def; + + if (def->alias) { + offset += def->alias->offset; + } + if (offset & 1) { + internal_error (0, "misaligned 64-bit swizzle source"); + } + *adj = (offset & 6) >> 1; + if (offset & 6) { + if (def->alias) { + cover = alias_def (def->alias, def->type, def->offset); + } else { + cover = alias_def (def, def->type, 0); + } + cover->offset -= offset & 6; + } + return cover; +} + +static def_t * +cover_def (def_t *def, int *adj) +{ + if (type_size (base_type (def->type)) == 1) { + return cover_def_32 (def, adj); + } else { + return cover_def_64 (def, adj); + } +} + +static void +adjust_swizzle (def_t *def, int adj) +{ + pr_ushort_t swiz = def->offset; + for (int i = 0; i < 8; i += 2) { + pr_ushort_t mask = 3 << i; + pr_ushort_t ind = swiz & mask; + swiz &= ~mask; + swiz |= (ind + (adj << i)) & mask; + } + def->offset = swiz; +} + static void emit_statement (statement_t *statement) { const char *opcode = statement->opcode; + operand_t *op_a, *op_b, *op_c; def_t *def_a, *def_b, *def_c; - opcode_t *op; + instruction_t *inst; dstatement_t *s; - def_a = get_operand_def (statement->expr, statement->opa); - use_tempop (statement->opa, statement->expr); - def_b = get_operand_def (statement->expr, statement->opb); - use_tempop (statement->opb, statement->expr); - def_c = get_operand_def (statement->expr, statement->opc); - use_tempop (statement->opc, statement->expr); - op = opcode_find (opcode, statement->opa, statement->opb, statement->opc); + if (options.code.progsversion < PROG_VERSION + && (strcmp (statement->opcode, "store") == 0 + || strcmp (statement->opcode, "assign") == 0 + || statement_is_cond (statement))) { + // the operands for assign, store and branch instructions are rotated + // when comparing v6/v6p and ruamoko + op_a = statement->opc; + op_b = statement->opa; + op_c = statement->opb; + } else { + op_a = statement->opa; + op_b = statement->opb; + op_c = statement->opc; + } - if (!op) { + def_a = get_operand_def (statement->expr, op_a); + use_tempop (op_a, statement->expr); + def_b = get_operand_def (statement->expr, op_b); + use_tempop (op_b, statement->expr); + def_c = get_operand_def (statement->expr, op_c); + use_tempop (op_c, statement->expr); + + if (strcmp (opcode, "swizzle") == 0) { + op_c->type = float_type (op_c->type); + op_a->type = float_type (op_a->type); + if (!op_c->type || !op_a->type) { + internal_error (statement->expr, "invalid types in swizzle"); + } + if (op_a->width < 4) { + int adj; + def_a = cover_def (def_a, &adj); + adjust_swizzle (def_b, adj); + op_a->width = 4; + } + } + + inst = opcode_find (opcode, op_a, op_b, op_c); + + if (!inst) { print_expr (statement->expr); + printf ("%d ", pr.code->size); print_statement (statement); internal_error (statement->expr, "ice ice baby"); } @@ -187,18 +312,32 @@ emit_statement (statement_t *statement) } } s = codespace_newstatement (pr.code); - s->op = op->opcode; - s->a = def_a ? def_a->offset : 0; - s->b = def_b ? def_b->offset : 0; - s->c = def_c ? def_c->offset : 0; + memset (s, 0, sizeof (*s)); + s->op = opcode_get (inst); + if (def_a) { + s->a = def_a->offset; + s->op |= ((def_a->reg) << OP_A_SHIFT) & OP_A_BASE; + } + if (def_b) { + s->b = def_b->offset; + s->op |= ((def_b->reg) << OP_B_SHIFT) & OP_B_BASE; + } + if (def_c) { + s->c = def_c->offset; + s->op |= ((def_c->reg) << OP_C_SHIFT) & OP_C_BASE; + } + + if (options.verbosity >= 2) { + opcode_print_statement (pr.code->size - 1, s); + } add_statement_def_ref (def_a, s, 0); add_statement_def_ref (def_b, s, 1); add_statement_def_ref (def_c, s, 2); - add_statement_op_ref (statement->opa, s, 0); - add_statement_op_ref (statement->opb, s, 1); - add_statement_op_ref (statement->opc, s, 2); + add_statement_op_ref (op_a, s, 0); + add_statement_op_ref (op_b, s, 1); + add_statement_op_ref (op_c, s, 2); } void diff --git a/tools/qfcc/source/expr.c b/tools/qfcc/source/expr.c index 36627517f..ca30d3074 100644 --- a/tools/qfcc/source/expr.c +++ b/tools/qfcc/source/expr.c @@ -42,46 +42,30 @@ #include "QF/alloc.h" #include "QF/dstring.h" #include "QF/mathlib.h" -#include "QF/sys.h" #include "QF/va.h" -#include "qfcc.h" -#include "class.h" -#include "def.h" -#include "defspace.h" -#include "diagnostic.h" -#include "emit.h" -#include "expr.h" -#include "function.h" -#include "idstuff.h" -#include "method.h" -#include "options.h" -#include "reloc.h" -#include "shared.h" -#include "strpool.h" -#include "struct.h" -#include "symtab.h" -#include "type.h" -#include "value.h" -#include "qc-parse.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/emit.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/idstuff.h" +#include "tools/qfcc/include/method.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/shared.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/struct.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" -static expr_t *exprs_freelist; +#include "tools/qfcc/source/qc-parse.h" -type_t *ev_types[ev_type_count] = { - &type_void, - &type_string, - &type_float, - &type_vector, - &type_entity, - &type_field, - &type_function, - &type_pointer, - &type_quaternion, - &type_integer, - &type_uinteger, - &type_short, - &type_invalid, -}; +ALLOC_STATE (expr_t, exprs); void convert_name (expr_t *e) @@ -106,7 +90,7 @@ convert_name (expr_t *e) } if (!strcmp (sym->name, "__LINE__") && current_func) { - new = new_integer_expr (e->line); + new = new_int_expr (e->line); goto convert; } if (!strcmp (sym->name, "__INFINITY__") @@ -125,6 +109,10 @@ convert_name (expr_t *e) //FIXME need a def return; } + if (sym->sy_type == sy_convert) { + new = sym->s.convert.conv (sym, sym->s.convert.data); + goto convert; + } if (sym->sy_type == sy_expr) { new = copy_expr (sym->s.expr); goto convert; @@ -138,80 +126,37 @@ convert: e->e = new->e; } -expr_t * -convert_vector (expr_t *e) -{ - float val[4]; - - if (e->type != ex_vector) - return e; - if (e->e.vector.type == &type_vector) { - // guaranteed to have three elements - expr_t *x = e->e.vector.list; - expr_t *y = x->next; - expr_t *z = y->next; - x = fold_constants (cast_expr (&type_float, x)); - y = fold_constants (cast_expr (&type_float, y)); - z = fold_constants (cast_expr (&type_float, z)); - if (is_constant (x) && is_constant (y) && is_constant (z)) { - val[0] = expr_float(x); - val[1] = expr_float(y); - val[2] = expr_float(z); - return new_vector_expr (val); - } - } - if (e->e.vector.type == &type_quaternion) { - // guaranteed to have two or four elements - if (e->e.vector.list->next->next) { - // four vals: w, x, y, z - expr_t *w = e->e.vector.list; - expr_t *x = w->next; - expr_t *y = x->next; - expr_t *z = y->next; - w = fold_constants (cast_expr (&type_float, w)); - x = fold_constants (cast_expr (&type_float, x)); - y = fold_constants (cast_expr (&type_float, y)); - z = fold_constants (cast_expr (&type_float, z)); - if (is_constant (w) && is_constant (x) && is_constant (y) - && is_constant (z)) { - val[0] = expr_float(w); - val[1] = expr_float(x); - val[2] = expr_float(y); - val[3] = expr_float(z); - return new_quaternion_expr (val); - } - } else { - // s, v - expr_t *s = e->e.vector.list; - expr_t *v = s->next; - - s = fold_constants (cast_expr (&type_float, s)); - v = convert_vector (v); - if (is_constant (s) && is_constant (v)) { - val[0] = expr_float (s); - memcpy (val + 1, expr_vector (v), 3 * sizeof (float)); - return new_quaternion_expr (val); - } - } - } - internal_error (e, "bogus vector expression"); -} - type_t * get_type (expr_t *e) { + const type_t *type = 0; convert_name (e); switch (e->type) { + case ex_branch: + type = e->e.branch.ret_type; + break; case ex_labelref: + case ex_adjstk: + case ex_with: return &type_void; - case ex_label: + case ex_memset: + return 0; case ex_error: - return 0; // something went very wrong + return 0; + case ex_return: + internal_error (e, "unexpected expression type"); + case ex_label: + case ex_compound: + return 0; case ex_bool: if (options.code.progsversion == PROG_ID_VERSION) return &type_float; - return &type_integer; + return &type_int; case ex_nil: + if (e->e.nil) { + return e->e.nil; + } + // fall through case ex_state: return &type_void; case ex_block: @@ -220,27 +165,44 @@ get_type (expr_t *e) return &type_void; case ex_expr: case ex_uexpr: - return e->e.expr.type; + type = e->e.expr.type; + break; + case ex_def: + type = e->e.def->type; + break; case ex_symbol: - return e->e.symbol->type; + type = e->e.symbol->type; + break; case ex_temp: - return e->e.temp.type; + type = e->e.temp.type; + break; case ex_value: - if (e->e.value->type == ev_func) - return e->e.value->v.func_val.type; - if (e->e.value->type == ev_pointer) - return pointer_type (e->e.value->v.pointer.type); - if (e->e.value->type == ev_field) - return field_type (e->e.value->v.pointer.type); - if (e->e.value->type == ev_integer - && options.code.progsversion == PROG_ID_VERSION) { - convert_int (e); - } - return ev_types[e->e.value->type]; + type = e->e.value->type; + break; case ex_vector: return e->e.vector.type; + case ex_selector: + return &type_SEL; + case ex_alias: + type = e->e.alias.type; + break; + case ex_address: + type = e->e.address.type; + break; + case ex_assign: + return get_type (e->e.assign.dst); + case ex_args: + return &type_va_list; + case ex_horizontal: + return e->e.hop.type; + case ex_swizzle: + return e->e.swizzle.type; + case ex_extend: + return e->e.extend.type; + case ex_count: + internal_error (e, "invalid expression"); } - return 0; + return (type_t *) unalias_type (type);//FIXME cast } etype_t @@ -256,47 +218,17 @@ extract_type (expr_t *e) expr_t * type_mismatch (expr_t *e1, expr_t *e2, int op) { - dstring_t *t1 = dstring_newstr (); - dstring_t *t2 = dstring_newstr (); - - print_type_str (t1, get_type (e1)); - print_type_str (t2, get_type (e2)); - e1 = error (e1, "type mismatch: %s %s %s", - t1->str, get_op_string (op), t2->str); - dstring_delete (t1); - dstring_delete (t2); + get_type_string (get_type (e1)), get_op_string (op), + get_type_string (get_type (e2))); return e1; } expr_t * param_mismatch (expr_t *e, int param, const char *fn, type_t *t1, type_t *t2) { - dstring_t *s1 = dstring_newstr (); - dstring_t *s2 = dstring_newstr (); - - print_type_str (s1, t1); - print_type_str (s2, t2); - e = error (e, "type mismatch for parameter %d of %s: expected %s, got %s", - param, fn, s1->str, s2->str); - dstring_delete (s1); - dstring_delete (s2); - return e; -} - -expr_t * -cast_error (expr_t *e, type_t *t1, type_t *t2) -{ - dstring_t *s1 = dstring_newstr (); - dstring_t *s2 = dstring_newstr (); - - print_type_str (s1, t1); - print_type_str (s2, t2); - - e = error (e, "can not cast from %s to %s", s1->str, s2->str); - dstring_delete (s1); - dstring_delete (s2); + param, fn, get_type_string (t1), get_type_string (t2)); return e; } @@ -334,6 +266,7 @@ copy_expr (expr_t *e) return 0; switch (e->type) { case ex_error: + case ex_def: case ex_symbol: case ex_nil: case ex_value: @@ -352,23 +285,23 @@ copy_expr (expr_t *e) *n = *e; n->line = pr.source_line; n->file = pr.source_file; - if (e->e.bool.true_list) { - int count = e->e.bool.true_list->size; + if (e->e.boolean.true_list) { + int count = e->e.boolean.true_list->size; size_t size = (size_t)&((ex_list_t *) 0)->e[count]; - n->e.bool.true_list = malloc (size); + n->e.boolean.true_list = malloc (size); while (count--) - n->e.bool.true_list->e[count] = - copy_expr (e->e.bool.true_list->e[count]); + n->e.boolean.true_list->e[count] = + copy_expr (e->e.boolean.true_list->e[count]); } - if (e->e.bool.false_list) { - int count = e->e.bool.false_list->size; + if (e->e.boolean.false_list) { + int count = e->e.boolean.false_list->size; size_t size = (size_t)&((ex_list_t *) 0)->e[count]; - n->e.bool.false_list = malloc (size); + n->e.boolean.false_list = malloc (size); while (count--) - n->e.bool.false_list->e[count] = - copy_expr (e->e.bool.false_list->e[count]); + n->e.boolean.false_list->e[count] = + copy_expr (e->e.boolean.false_list->e[count]); } - n->e.bool.e = copy_expr (e->e.bool.e); + n->e.boolean.e = copy_expr (e->e.boolean.e); return n; case ex_label: /// Create a fresh label @@ -393,7 +326,7 @@ copy_expr (expr_t *e) } if (e->e.block.result && !n->e.block.result) internal_error (e, "bogus block result?"); - break; + return n; case ex_expr: n = new_expr (); *n = *e; @@ -414,34 +347,121 @@ copy_expr (expr_t *e) *n = *e; n->line = pr.source_line; n->file = pr.source_file; - n->e.temp.expr = copy_expr (e->e.temp.expr); return n; case ex_vector: n = new_expr (); + *n = *e; n->e.vector.type = e->e.vector.type; n->e.vector.list = copy_expr (e->e.vector.list); - n = n->e.vector.list; t = e->e.vector.list; + e = n->e.vector.list; while (t->next) { - n->next = copy_expr (t->next); - n = n->next; + e->next = copy_expr (t->next); + e = e->next; t = t->next; } return n; + case ex_selector: + n = new_expr (); + *n = *e; + n->e.selector.sel_ref = copy_expr (e->e.selector.sel_ref); + return n; + case ex_compound: + n = new_expr (); + *n = *e; + for (element_t *i = e->e.compound.head; i; i = i->next) { + append_element (n, new_element (i->expr, i->designator)); + } + return n; + case ex_memset: + n = new_expr (); + *n = *e; + n->e.memset.dst = copy_expr (e->e.memset.dst); + n->e.memset.val = copy_expr (e->e.memset.val); + n->e.memset.count = copy_expr (e->e.memset.count); + return n; + case ex_alias: + n = new_expr (); + *n = *e; + n->e.alias.expr = copy_expr (e->e.alias.expr); + n->e.alias.offset = copy_expr (e->e.alias.offset); + return n; + case ex_address: + n = new_expr (); + *n = *e; + n->e.address.lvalue = copy_expr (e->e.address.lvalue); + n->e.address.offset = copy_expr (e->e.address.offset); + return n; + case ex_assign: + n = new_expr (); + *n = *e; + n->e.assign.dst = copy_expr (e->e.assign.dst); + n->e.assign.src = copy_expr (e->e.assign.src); + return n; + case ex_branch: + n = new_expr (); + *n = *e; + n->e.branch.target = copy_expr (e->e.branch.target); + n->e.branch.index = copy_expr (e->e.branch.index); + n->e.branch.test = copy_expr (e->e.branch.test); + n->e.branch.args = copy_expr (e->e.branch.args); + return n; + case ex_return: + n = new_expr (); + *n = *e; + n->e.retrn.ret_val = copy_expr (e->e.retrn.ret_val); + return n; + case ex_adjstk: + n = new_expr (); + *n = *e; + return n; + case ex_with: + n = new_expr (); + *n = *e; + n->e.with.with = copy_expr (e->e.with.with); + return n; + case ex_args: + n = new_expr (); + *n = *e; + return n; + case ex_horizontal: + n = new_expr (); + *n = *e; + e->e.hop.vec = copy_expr (e->e.hop.vec); + return n; + case ex_swizzle: + n = new_expr (); + *n = *e; + e->e.swizzle.src = copy_expr (e->e.swizzle.src); + return n; + case ex_extend: + n = new_expr (); + *n = *e; + e->e.extend.src = copy_expr (e->e.extend.src); + return n; + case ex_count: + break; } internal_error (e, "invalid expression"); } +expr_t * +expr_file_line (expr_t *dst, const expr_t *src) +{ + dst->file = src->file; + dst->line = src->line; + return dst; +} + const char * new_label_name (void) { static int label = 0; int lnum = ++label; const char *fname = current_func->sym->name; - char *lname; + const char *lname; - lname = nva ("$%s_%d", fname, lnum); - SYS_CHECKMEM (lname); + lname = save_string (va (0, "$%s_%d", fname, lnum)); return lname; } @@ -471,9 +491,9 @@ new_bool_expr (ex_list_t *true_list, ex_list_t *false_list, expr_t *e) expr_t *b = new_expr (); b->type = ex_bool; - b->e.bool.true_list = true_list; - b->e.bool.false_list = false_list; - b->e.bool.e = e; + b->e.boolean.true_list = true_list; + b->e.boolean.false_list = false_list; + b->e.boolean.e = e; return b; } @@ -488,6 +508,32 @@ new_label_expr (void) return l; } +expr_t * +named_label_expr (symbol_t *label) +{ + symbol_t *sym; + expr_t *l; + + if (!current_func) { + // XXX this might be only an error + internal_error (0, "label defined outside of function scope"); + } + + sym = symtab_lookup (current_func->label_scope, label->name); + + if (sym) { + return sym->s.expr; + } + l = new_label_expr (); + l->e.label.name = save_string (va (0, "%s_%s", l->e.label.name, + label->name)); + l->e.label.symbol = label; + label->sy_type = sy_expr; + label->s.expr = l; + symtab_addsymbol (current_func->label_scope, label); + return label->s.expr; +} + expr_t * new_label_ref (ex_label_t *label) { @@ -508,6 +554,7 @@ new_block_expr (void) b->type = ex_block; b->e.block.head = 0; b->e.block.tail = &b->e.block.head; + b->e.block.return_addr = __builtin_return_address (0); return b; } @@ -528,6 +575,20 @@ new_binary_expr (int op, expr_t *e1, expr_t *e2) return e; } +expr_t * +build_block_expr (expr_t *expr_list) +{ + expr_t *b = new_block_expr (); + + while (expr_list) { + expr_t *e = expr_list; + expr_list = e->next; + e->next = 0; + append_expr (b, e); + } + return b; +} + expr_t * new_unary_expr (int op, expr_t *e1) { @@ -542,6 +603,116 @@ new_unary_expr (int op, expr_t *e1) return e; } +expr_t * +new_horizontal_expr (int op, expr_t *vec, type_t *type) +{ + type_t *vec_type = get_type (vec); + if (!vec_type) { + return vec; + } + if (!is_math (vec_type) || is_scalar (vec_type)) { + internal_error (vec, "horizontal operand not a vector type"); + } + if (!is_scalar (type)) { + internal_error (vec, "horizontal result not a scalar type"); + } + + expr_t *e = new_expr (); + e->type = ex_horizontal; + e->e.hop.op = op; + e->e.hop.vec = vec; + e->e.hop.type = type; + return e; +} + +expr_t * +new_swizzle_expr (expr_t *src, const char *swizzle) +{ + type_t *src_type = get_type (src); + if (!src_type) { + return src; + } + int src_width = type_width (src_type); + // swizzle always generates a *vec4 + ex_swizzle_t swiz = {}; + +#define m(x) (1 << ((x) - 'a')) +#define v(x, mask) (((x) & 0x60) == 0x60 && (m(x) & (mask))) +#define vind(x) ((x) & 3) +#define cind(x) (-(((x) >> 3) ^ (x)) & 3) +#define tind(x) ((((~(x+1)>>2)&1) + x + 1) & 3) + const int color = m('r') | m('g') | m('b') | m('a'); + const int vector = m('x') | m('y') | m('z') | m('w'); + const int texture = m('s') | m('t') | m('p') | m('q'); + + int type_mask = 0; + int comp_count = 0; + + for (const char *s = swizzle; *s; s++) { + if (comp_count >= 4) { + return error (src, "too many components in swizzle"); + } + if (*s == '0') { + swiz.zero |= 1 << comp_count; + comp_count++; + } else if (*s == '-') { + swiz.neg |= 1 << comp_count; + } else { + int ind = 0; + int mask = 0; + if (v (*s, vector)) { + ind = vind (*s); + mask = 1; + } else if (v (*s, color)) { + ind = cind (*s); + mask = 2; + } else if (v (*s, texture)) { + ind = tind (*s); + mask = 4; + } + if (!mask) { + return error (src, "invalid component in swizzle"); + } + if (type_mask & ~mask) { + return error (src, "mixed components in swizzle"); + } + if (ind >= src_width) { + return error (src, "swizzle component out of bounds"); + } + type_mask |= mask; + swiz.source[comp_count++] = ind; + } + } + swiz.zero |= (0xf << comp_count) & 0xf; + swiz.src = new_alias_expr (vector_type (&type_float, src_width), src); + swiz.type = vector_type (base_type (src_type), 4); + + expr_t *expr = new_expr (); + expr->type = ex_swizzle; + expr->e.swizzle = swiz; + return expr; +} + +expr_t * +new_extend_expr (expr_t *src, type_t *type, int ext) +{ + expr_t *expr = new_expr (); + expr->type = ex_extend; + expr->e.extend.src = src; + expr->e.extend.extend = ext; + expr->e.extend.type = type; + return expr; +} + +expr_t * +new_def_expr (def_t *def) +{ + expr_t *e = new_expr (); + e->type = ex_def; + e->e.def = def; + return e; +} + expr_t * new_symbol_expr (symbol_t *symbol) { @@ -552,12 +723,12 @@ new_symbol_expr (symbol_t *symbol) } expr_t * -new_temp_def_expr (type_t *type) +new_temp_def_expr (const type_t *type) { expr_t *e = new_expr (); e->type = ex_temp; - e->e.temp.type = type; + e->e.temp.type = (type_t *) unalias_type (type); // FIXME cast return e; } @@ -569,6 +740,14 @@ new_nil_expr (void) return e; } +expr_t * +new_args_expr (void) +{ + expr_t *e = new_expr (); + e->type = ex_args; + return e; +} + expr_t * new_value_expr (ex_value_t *value) { @@ -595,149 +774,93 @@ new_name_expr (const char *name) expr_t * new_string_expr (const char *string_val) { - expr_t *e = new_expr (); - e->type = ex_value; - e->e.value = new_string_val (string_val); - return e; + return new_value_expr (new_string_val (string_val)); +} + +expr_t * +new_double_expr (double double_val) +{ + return new_value_expr (new_double_val (double_val)); } expr_t * new_float_expr (float float_val) { - expr_t *e = new_expr (); - e->type = ex_value; - e->e.value = new_float_val (float_val); - return e; + return new_value_expr (new_float_val (float_val)); } expr_t * new_vector_expr (const float *vector_val) { - expr_t *e = new_expr (); - e->type = ex_value; - e->e.value = new_vector_val (vector_val); - return e; -} - -expr_t * -new_vector_list (expr_t *e) -{ - expr_t *t; - int count; - type_t *type = &type_vector; - expr_t *vec; - - e = reverse_expr_list (e); // put the elements in the right order - for (t = e, count = 0; t; t = t->next) - count++; - switch (count) { - case 4: - type = &type_quaternion; - case 3: - // quaternion or vector. all expressions must be compatible with - // a float - for (t = e; t; t = t->next) - if (!type_assignable (&type_float, get_type (t))) - return error (t, "invalid type for vector element"); - vec = new_expr (); - vec->type = ex_vector; - vec->e.vector.type = type; - vec->e.vector.list = e; - break; - case 2: - // quaternion. first expression must be compatible with a float, - // the other must be a vector - if (!type_assignable (&type_float, get_type (e)) - || !type_assignable (&type_vector, get_type(e->next))) { - return error (t, "invalid types for vector elements"); - } - // s, v - vec = new_expr (); - vec->type = ex_vector; - vec->e.vector.type = &type_quaternion; - vec->e.vector.list = e; - break; - default: - return error (e, "invalid number of elements in vector exprssion"); - } - return vec; + return new_value_expr (new_vector_val (vector_val)); } expr_t * new_entity_expr (int entity_val) { - expr_t *e = new_expr (); - e->type = ex_value; - e->e.value = new_entity_val (entity_val); - return e; + return new_value_expr (new_entity_val (entity_val)); } expr_t * new_field_expr (int field_val, type_t *type, def_t *def) { - expr_t *e = new_expr (); - e->type = ex_value; - e->e.value = new_field_val (field_val, type, def); - return e; + return new_value_expr (new_field_val (field_val, type, def)); } expr_t * new_func_expr (int func_val, type_t *type) { - expr_t *e = new_expr (); - e->type = ex_value; - e->e.value = new_func_val (func_val, type); - return e; + return new_value_expr (new_func_val (func_val, type)); } expr_t * new_pointer_expr (int val, type_t *type, def_t *def) { - expr_t *e = new_expr (); - e->type = ex_value; - e->e.value = new_pointer_val (val, type, def); - return e; + return new_value_expr (new_pointer_val (val, type, def, 0)); } expr_t * new_quaternion_expr (const float *quaternion_val) { - expr_t *e = new_expr (); - e->type = ex_value; - e->e.value = new_quaternion_val (quaternion_val); - return e; + return new_value_expr (new_quaternion_val (quaternion_val)); } expr_t * -new_integer_expr (int integer_val) +new_int_expr (int int_val) { - expr_t *e = new_expr (); - e->type = ex_value; - e->e.value = new_integer_val (integer_val); - return e; + return new_value_expr (new_int_val (int_val)); } expr_t * -new_uinteger_expr (unsigned uinteger_val) +new_uint_expr (unsigned uint_val) { - expr_t *e = new_expr (); - e->type = ex_value; - e->e.value = new_uinteger_val (uinteger_val); - return e; + return new_value_expr (new_uint_val (uint_val)); +} + +expr_t * +new_long_expr (pr_long_t long_val) +{ + return new_value_expr (new_long_val (long_val)); +} + +expr_t * +new_ulong_expr (pr_ulong_t ulong_val) +{ + return new_value_expr (new_ulong_val (ulong_val)); } expr_t * new_short_expr (short short_val) { - expr_t *e = new_expr (); - e->type = ex_value; - e->e.value = new_short_val (short_val); - return e; + return new_value_expr (new_short_val (short_val)); } int is_constant (expr_t *e) { + while (e->type == ex_alias) { + e = e->e.alias.expr; + } if (e->type == ex_nil || e->type == ex_value || e->type == ex_labelref || (e->type == ex_symbol && e->e.symbol->sy_type == sy_const) || (e->type == ex_symbol && e->e.symbol->sy_type == sy_var @@ -746,6 +869,26 @@ is_constant (expr_t *e) return 0; } +int +is_variable (expr_t *e) +{ + while (e->type == ex_alias) { + e = e->e.alias.expr; + } + if (e->type == ex_def + || (e->type == ex_symbol && e->e.symbol->sy_type == sy_var) + || e->type == ex_temp) { + return 1; + } + return 0; +} + +int +is_selector (expr_t *e) +{ + return e->type == ex_selector; +} + expr_t * constant_expr (expr_t *e) { @@ -771,20 +914,24 @@ constant_expr (expr_t *e) } else { return e; } - new = new_expr (); - new->type = ex_value; + new = new_value_expr (value); new->line = e->line; new->file = e->file; - new->e.value = value; return new; } +int +is_nil (expr_t *e) +{ + return e->type == ex_nil; +} + int is_string_val (expr_t *e) { if (e->type == ex_nil) return 1; - if (e->type == ex_value && e->e.value->type == ev_string) + if (e->type == ex_value && e->e.value->lltype == ev_string) return 1; if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const && e->e.symbol->type->type == ev_string) @@ -797,7 +944,7 @@ expr_string (expr_t *e) { if (e->type == ex_nil) return 0; - if (e->type == ex_value && e->e.value->type == ev_string) + if (e->type == ex_value && e->e.value->lltype == ev_string) return e->e.value->v.string_val; internal_error (e, "not a string constant"); } @@ -807,7 +954,7 @@ is_float_val (expr_t *e) { if (e->type == ex_nil) return 1; - if (e->type == ex_value && e->e.value->type == ev_float) + if (e->type == ex_value && e->e.value->lltype == ev_float) return 1; if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const && e->e.symbol->type->type == ev_float) @@ -815,12 +962,29 @@ is_float_val (expr_t *e) return 0; } +double +expr_double (expr_t *e) +{ + if (e->type == ex_nil) + return 0; + if (e->type == ex_value && e->e.value->lltype == ev_double) + return e->e.value->v.double_val; + if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const + && e->e.symbol->type->type == ev_double) + return e->e.symbol->s.value->v.double_val; + if (e->type == ex_symbol && e->e.symbol->sy_type == sy_var + && e->e.symbol->s.def->constant + && is_double (e->e.symbol->s.def->type)) + return D_DOUBLE (e->e.symbol->s.def); + internal_error (e, "not a double constant"); +} + float expr_float (expr_t *e) { if (e->type == ex_nil) return 0; - if (e->type == ex_value && e->e.value->type == ev_float) + if (e->type == ex_value && e->e.value->lltype == ev_float) return e->e.value->v.float_val; if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const && e->e.symbol->type->type == ev_float) @@ -837,7 +1001,7 @@ is_vector_val (expr_t *e) { if (e->type == ex_nil) return 1; - if (e->type == ex_value && e->e.value->type == ev_vector) + if (e->type == ex_value && e->e.value->lltype == ev_vector) return 1; if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const && e->e.symbol->type->type == ev_vector) @@ -850,7 +1014,7 @@ expr_vector (expr_t *e) { if (e->type == ex_nil) return vec3_origin; - if (e->type == ex_value && e->e.value->type == ev_vector) + if (e->type == ex_value && e->e.value->lltype == ev_vector) return e->e.value->v.vector_val; if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const && e->e.symbol->type->type == ev_vector) @@ -867,10 +1031,10 @@ is_quaternion_val (expr_t *e) { if (e->type == ex_nil) return 1; - if (e->type == ex_value && e->e.value->type == ev_quat) + if (e->type == ex_value && e->e.value->lltype == ev_quaternion) return 1; if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const - && e->e.symbol->type->type == ev_quat) + && e->e.symbol->type->type == ev_quaternion) return 1; return 0; } @@ -880,131 +1044,311 @@ expr_quaternion (expr_t *e) { if (e->type == ex_nil) return quat_origin; - if (e->type == ex_value && e->e.value->type == ev_quat) + if (e->type == ex_value && e->e.value->lltype == ev_quaternion) return e->e.value->v.quaternion_val; if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const - && e->e.symbol->type->type == ev_quat) + && e->e.symbol->type->type == ev_quaternion) return e->e.symbol->s.value->v.quaternion_val; if (e->type == ex_symbol && e->e.symbol->sy_type == sy_var && e->e.symbol->s.def->constant - && e->e.symbol->s.def->type->type == ev_quat) + && e->e.symbol->s.def->type->type == ev_quaternion) return D_QUAT (e->e.symbol->s.def); internal_error (e, "not a quaternion constant"); } int -is_integer_val (expr_t *e) +is_int_val (expr_t *e) { - if (e->type == ex_nil) + if (e->type == ex_nil) { return 1; - if (e->type == ex_value && e->e.value->type == ev_integer) + } + if (e->type == ex_value && e->e.value->lltype == ev_int) { return 1; + } if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const - && (e->e.symbol->type->type == ev_integer - || is_enum (e->e.symbol->type))) + && is_integral (e->e.symbol->type)) { return 1; + } + if (e->type == ex_def && e->e.def->constant + && is_integral (e->e.def->type)) { + return 1; + } return 0; } int -expr_integer (expr_t *e) +expr_int (expr_t *e) { - if (e->type == ex_nil) + if (e->type == ex_nil) { return 0; - if (e->type == ex_value && e->e.value->type == ev_integer) - return e->e.value->v.integer_val; + } + if (e->type == ex_value && e->e.value->lltype == ev_int) { + return e->e.value->v.int_val; + } + if (e->type == ex_value && e->e.value->lltype == ev_short) { + return e->e.value->v.short_val; + } if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const - && (e->e.symbol->type->type == ev_integer - || is_enum (e->e.symbol->type))) - return e->e.symbol->s.value->v.integer_val; + && (e->e.symbol->type->type == ev_int + || is_enum (e->e.symbol->type))) { + return e->e.symbol->s.value->v.int_val; + } if (e->type == ex_symbol && e->e.symbol->sy_type == sy_var && e->e.symbol->s.def->constant - && is_integral (e->e.symbol->s.def->type)) + && is_integral (e->e.symbol->s.def->type)) { return D_INT (e->e.symbol->s.def); - internal_error (e, "not an integer constant"); + } + if (e->type == ex_def && e->e.def->constant + && is_integral (e->e.def->type)) { + return D_INT (e->e.def); + } + internal_error (e, "not an int constant"); +} + +int +is_uint_val (expr_t *e) +{ + if (e->type == ex_nil) { + return 1; + } + if (e->type == ex_value && e->e.value->lltype == ev_uint) { + return 1; + } + if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const + && is_integral (e->e.symbol->type)) { + return 1; + } + if (e->type == ex_def && e->e.def->constant + && is_integral (e->e.def->type)) { + return 1; + } + return 0; } unsigned -expr_uinteger (expr_t *e) +expr_uint (expr_t *e) { - if (e->type == ex_nil) + if (e->type == ex_nil) { return 0; - if (e->type == ex_value && e->e.value->type == ev_uinteger) - return e->e.value->v.uinteger_val; + } + if (e->type == ex_value && e->e.value->lltype == ev_uint) { + return e->e.value->v.uint_val; + } if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const - && e->e.symbol->type->type == ev_uinteger) - return e->e.symbol->s.value->v.uinteger_val; + && e->e.symbol->type->type == ev_uint) { + return e->e.symbol->s.value->v.uint_val; + } if (e->type == ex_symbol && e->e.symbol->sy_type == sy_var && e->e.symbol->s.def->constant - && is_integral (e->e.symbol->s.def->type)) + && is_integral (e->e.symbol->s.def->type)) { return D_INT (e->e.symbol->s.def); + } + if (e->type == ex_def && e->e.def->constant + && is_integral (e->e.def->type)) { + return D_INT (e->e.def); + } internal_error (e, "not an unsigned constant"); } int is_short_val (expr_t *e) { - if (e->type == ex_nil) + if (e->type == ex_nil) { return 1; - if (e->type == ex_value && e->e.value->type == ev_short) + } + if (e->type == ex_value && e->e.value->lltype == ev_short) { return 1; + } if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const - && e->e.symbol->type->type == ev_short) + && e->e.symbol->type->type == ev_short) { return 1; + } return 0; } short expr_short (expr_t *e) { - if (e->type == ex_nil) + if (e->type == ex_nil) { return 0; - if (e->type == ex_value && e->e.value->type == ev_short) + } + if (e->type == ex_value && e->e.value->lltype == ev_short) { return e->e.value->v.short_val; + } if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const - && e->e.symbol->type->type == ev_short) + && e->e.symbol->type->type == ev_short) { return e->e.symbol->s.value->v.short_val; + } internal_error (e, "not a short constant"); } -expr_t * -new_self_expr (void) +unsigned short +expr_ushort (expr_t *e) { - symbol_t *sym; - - sym = make_symbol (".self", &type_entity, pr.near_data, sc_extern); - if (!sym->table) - symtab_addsymbol (pr.symtab, sym); - return new_symbol_expr (sym); + if (e->type == ex_nil) { + return 0; + } + if (e->type == ex_value && e->e.value->lltype == ev_ushort) { + return e->e.value->v.ushort_val; + } + if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const + && e->e.symbol->type->type == ev_ushort) { + return e->e.symbol->s.value->v.ushort_val; + } + internal_error (e, "not a ushort constant"); } -expr_t * -new_this_expr (void) +int +is_integral_val (expr_t *e) { - symbol_t *sym; + if (is_constant (e)) { + if (is_int_val (e)) { + return 1; + } + if (is_uint_val (e)) { + return 1; + } + if (is_short_val (e)) { + return 1; + } + } + return 0; +} - sym = make_symbol (".this", field_type (&type_id), pr.near_data, sc_extern); - if (!sym->table) - symtab_addsymbol (pr.symtab, sym); - return new_symbol_expr (sym); +int +expr_integral (expr_t *e) +{ + if (is_constant (e)) { + if (is_int_val (e)) { + return expr_int (e); + } + if (is_uint_val (e)) { + return expr_uint (e); + } + if (is_short_val (e)) { + return expr_short (e); + } + } + internal_error (e, "not an integral constant"); +} + +int +is_pointer_val (expr_t *e) +{ + if (e->type == ex_value && e->e.value->lltype == ev_ptr) { + return 1; + } + return 0; } expr_t * new_alias_expr (type_t *type, expr_t *expr) { - expr_t *alias; + if (is_ptr (type) && expr->type == ex_address) { + // avoid aliasing a pointer to a pointer (redundant) + expr = copy_expr (expr); + expr->e.address.type = type; + return expr; + } + if (expr->type == ex_alias) { + if (expr->e.alias.offset) { + return new_offset_alias_expr (type, expr, 0); + } + expr = expr->e.alias.expr; + } + // this can happen when constant folding an offset pointer results in + // a noop due to the offset being 0 and thus casting back to the original + // type + if (type == get_type (expr)) { + return expr; + } - if (expr->type == ex_value) - return new_value_expr (alias_value (expr->e.value, type)); - alias = new_unary_expr ('A', expr); - alias->e.expr.type = type; - //if (expr->type == ex_uexpr && expr->e.expr.op == 'A') - // bug (alias, "aliasing an alias expression"); + expr_t *alias = new_expr (); + alias->type = ex_alias; + alias->e.alias.type = type; + alias->e.alias.expr = expr; alias->file = expr->file; alias->line = expr->line; return alias; } +expr_t * +new_offset_alias_expr (type_t *type, expr_t *expr, int offset) +{ + if (expr->type == ex_alias && expr->e.alias.offset) { + expr_t *ofs_expr = expr->e.alias.offset; + if (!is_constant (ofs_expr)) { + internal_error (ofs_expr, "non-constant offset for alias expr"); + } + offset += expr_int (ofs_expr); + + if (expr->e.alias.expr->type == ex_alias) { + internal_error (expr, "alias expr of alias expr"); + } + expr = expr->e.alias.expr; + } + + expr_t *alias = new_expr (); + alias->type = ex_alias; + alias->e.alias.type = type; + alias->e.alias.expr = expr; + alias->e.alias.offset = new_int_expr (offset); + alias->file = expr->file; + alias->line = expr->line; + return alias; +} + +expr_t * +new_address_expr (type_t *lvtype, expr_t *lvalue, expr_t *offset) +{ + expr_t *addr = new_expr (); + addr->type = ex_address; + addr->e.address.type = pointer_type (lvtype); + addr->e.address.lvalue = lvalue; + addr->e.address.offset = offset; + return addr; +} + +expr_t * +new_assign_expr (expr_t *dst, expr_t *src) +{ + expr_t *addr = new_expr (); + addr->type = ex_assign; + addr->e.assign.dst = dst; + addr->e.assign.src = src; + return addr; +} + +expr_t * +new_return_expr (expr_t *ret_val) +{ + expr_t *retrn = new_expr (); + retrn->type = ex_return; + retrn->e.retrn.ret_val = ret_val; + return retrn; +} + +expr_t * +new_adjstk_expr (int mode, int offset) +{ + expr_t *adj = new_expr (); + adj->type = ex_adjstk; + adj->e.adjstk.mode = mode; + adj->e.adjstk.offset = offset; + return adj; +} + +expr_t * +new_with_expr (int mode, int reg, expr_t *val) +{ + expr_t *with = new_expr (); + with->type = ex_with; + with->e.with.mode = mode; + with->e.with.reg = reg; + with->e.with.with = val; + return with; +} + static expr_t * param_expr (const char *name, type_t *type) { @@ -1027,14 +1371,17 @@ new_ret_expr (type_t *type) expr_t * new_param_expr (type_t *type, int num) { - return param_expr (va (".param_%d", num), type); + return param_expr (va (0, ".param_%d", num), type); } expr_t * -new_move_expr (expr_t *e1, expr_t *e2, type_t *type, int indirect) +new_memset_expr (expr_t *dst, expr_t *val, expr_t *count) { - expr_t *e = new_binary_expr (indirect ? 'M' : 'm', e1, e2); - e->e.expr.type = type; + expr_t *e = new_expr (); + e->type = ex_memset; + e->e.memset.dst = dst; + e->e.memset.val = val; + e->e.memset.count = count; return e; } @@ -1056,8 +1403,30 @@ append_expr (expr_t *block, expr_t *e) return block; } +expr_t * +prepend_expr (expr_t *block, expr_t *e) +{ + if (block->type != ex_block) + internal_error (block, "not a block expression"); + + if (!e || e->type == ex_error) + return block; + + if (e->next) + internal_error (e, "append_expr: expr loop detected"); + + e->next = block->e.block.head; + block->e.block.head = e; + + if (block->e.block.tail == &block->e.block.head) { + block->e.block.tail = &e->next; + } + + return block; +} + static symbol_t * -get_struct_field (type_t *t1, expr_t *e1, expr_t *e2) +get_struct_field (const type_t *t1, expr_t *e1, expr_t *e2) { symtab_t *strct = t1->t.symtab; symbol_t *sym = e2->e.symbol;//FIXME need to check @@ -1068,8 +1437,12 @@ get_struct_field (type_t *t1, expr_t *e1, expr_t *e2) return 0; } field = symtab_lookup (strct, sym->name); - if (!field && t1 != &type_entity) { - error (e2, "'%s' has no member named '%s'", t1->name + 4, sym->name); + if (!field && !is_entity(t1)) { + const char *name = t1->name; + if (!strncmp (name, "tag ", 4)) { + name += 4; + } + error (e2, "'%s' has no member named '%s'", name, sym->name); e1->type = ex_error; } return field; @@ -1078,13 +1451,13 @@ get_struct_field (type_t *t1, expr_t *e1, expr_t *e2) expr_t * field_expr (expr_t *e1, expr_t *e2) { - type_t *t1, *t2; + const type_t *t1, *t2; expr_t *e; + t1 = get_type (e1); if (e1->type == ex_error) return e1; - t1 = get_type (e1); - if (t1->type == ev_entity) { + if (is_entity (t1)) { symbol_t *field = 0; if (e2->type == ex_symbol) @@ -1104,35 +1477,33 @@ field_expr (expr_t *e1, expr_t *e2) return e; } } - } else if (t1->type == ev_pointer) { - if (is_struct (t1->t.fldptr.type)) { + } else if (is_ptr (t1)) { + if (is_struct (t1->t.fldptr.type) || is_union (t1->t.fldptr.type)) { symbol_t *field; field = get_struct_field (t1->t.fldptr.type, e1, e2); if (!field) return e1; - e2->type = ex_value; - e2->e.value = new_short_val (field->s.offset); - e = new_binary_expr ('&', e1, e2); - e->e.expr.type = pointer_type (field->type); - return unary_expr ('.', e); - } else if (obj_is_class (t1->t.fldptr.type)) { + expr_t *offset = new_short_expr (field->s.offset); + e1 = offset_pointer_expr (e1, offset); + e1 = cast_expr (pointer_type (field->type), e1); + return unary_expr ('.', e1); + } else if (is_class (t1->t.fldptr.type)) { class_t *class = t1->t.fldptr.type->t.class; symbol_t *sym = e2->e.symbol;//FIXME need to check symbol_t *ivar; + int protected = class_access (current_class, class); - ivar = class_find_ivar (class, vis_protected, sym->name); + ivar = class_find_ivar (class, protected, sym->name); if (!ivar) return new_error_expr (); - e2->type = ex_value; - e2->e.value = new_short_val (ivar->s.offset); - e = new_binary_expr ('&', e1, e2); - e->e.expr.type = pointer_type (ivar->type); - return unary_expr ('.', e); + expr_t *offset = new_short_expr (ivar->s.offset); + e1 = offset_pointer_expr (e1, offset); + e1 = cast_expr (pointer_type (ivar->type), e1); + return unary_expr ('.', e1); } - } else if (t1->type == ev_vector || t1->type == ev_quat - || is_struct (t1)) { + } else if (is_nonscalar (t1) || is_struct (t1) || is_union (t1)) { symbol_t *field; field = get_struct_field (t1, e1, e2); @@ -1140,7 +1511,7 @@ field_expr (expr_t *e1, expr_t *e2) return e1; if (e1->type == ex_expr && e1->e.expr.op == '.' - && get_type (e1->e.expr.e1) == &type_entity) { + && is_entity(get_type (e1->e.expr.e1))) { // undo the . expression e2 = e1->e.expr.e2; e1 = e1->e.expr.e1; @@ -1155,19 +1526,24 @@ field_expr (expr_t *e1, expr_t *e2) } def = sym->s.def; e2 = new_field_expr (0, field->type, def); - } else if (e2->type != ex_value || e2->e.value->type != ev_field) { + } else if (e2->type != ex_value + || e2->e.value->lltype != ev_field) { internal_error (e2, "unexpected field exression"); } e2->e.value = new_field_val (e2->e.value->v.pointer.val + field->s.offset, field->type, e2->e.value->v.pointer.def); // create a new . expression return field_expr (e1, e2); } else { - e2->type = ex_value; - e2->e.value = new_short_val (field->s.offset); - e = address_expr (e1, e2, field->type); - return unary_expr ('.', e); + if (e1->type == ex_uexpr && e1->e.expr.op == '.') { + expr_t *offset = new_short_expr (field->s.offset); + e1 = offset_pointer_expr (e1->e.expr.e1, offset); + e1 = cast_expr (pointer_type (field->type), e1); + return unary_expr ('.', e1); + } else { + return new_offset_alias_expr (field->type, e1, field->s.offset); + } } - } else if (obj_is_class (t1)) { + } else if (is_class (t1)) { //Class instance variables aren't allowed and thus declaring one //is treated as an error, so this is a follow-on error. return error (e1, "class instance access"); @@ -1175,194 +1551,6 @@ field_expr (expr_t *e1, expr_t *e2) return type_mismatch (e1, e2, '.'); } -expr_t * -test_expr (expr_t *e) -{ - static float zero[4] = {0, 0, 0, 0}; - expr_t *new = 0; - type_t *type; - - if (e->type == ex_error) - return e; - - type = get_type (e); - if (e->type == ex_error) - return e; - switch (type->type) { - case ev_type_count: - internal_error (e, 0); - case ev_void: - if (options.traditional) { - if (options.warnings.traditional) - warning (e, "void has no value"); - return e; - } - return error (e, "void has no value"); - case ev_string: - if (!options.code.ifstring) - return new_alias_expr (type_default, e); - new = new_string_expr (0); - break; - case ev_uinteger: - case ev_integer: - case ev_short: - if (type_default != &type_integer) - return new_alias_expr (type_default, e); - return e; - case ev_float: - if (options.code.fast_float - || options.code.progsversion == PROG_ID_VERSION) { - if (type_default != &type_float) - return new_alias_expr (type_default, e); - return e; - } - new = new_float_expr (0); - break; - case ev_vector: - new = new_vector_expr (zero); - break; - case ev_entity: - return new_alias_expr (type_default, e); - case ev_field: - return new_alias_expr (type_default, e); - case ev_func: - return new_alias_expr (type_default, e); - case ev_pointer: - return new_alias_expr (type_default, e); - case ev_quat: - new = new_quaternion_expr (zero); - break; - case ev_invalid: - if (is_enum (type)) { - new = new_nil_expr (); - break; - } - return test_error (e, get_type (e)); - } - new->line = e->line; - new->file = e->file; - new = binary_expr (NE, e, new); - new->line = e->line; - new->file = e->file; - return fold_constants (new); -} - -void -backpatch (ex_list_t *list, expr_t *label) -{ - int i; - expr_t *e; - - if (!list) - return; - if (!label || label->type != ex_label) - internal_error (label, "not a label"); - - for (i = 0; i < list->size; i++) { - e = list->e[i]; - if (e->type == ex_uexpr && e->e.expr.op == 'g') - e->e.expr.e1 = label; - else if (e->type == ex_expr && (e->e.expr.op == 'i' - || e->e.expr.op == 'n')) - e->e.expr.e2 = label; - else { - internal_error (e, 0); - } - label->e.label.used++; - } -} - -static ex_list_t * -merge (ex_list_t *l1, ex_list_t *l2) -{ - ex_list_t *m; - - if (!l1 && !l2) - internal_error (0, 0); - if (!l2) - return l1; - if (!l1) - return l2; - m = malloc ((size_t)&((ex_list_t *)0)->e[l1->size + l2->size]); - m->size = l1->size + l2->size; - memcpy (m->e, l1->e, l1->size * sizeof (expr_t *)); - memcpy (m->e + l1->size, l2->e, l2->size * sizeof (expr_t *)); - return m; -} - -static ex_list_t * -make_list (expr_t *e) -{ - ex_list_t *m; - - m = malloc ((size_t)&((ex_list_t *) 0)->e[1]); - m->size = 1; - m->e[0] = e; - return m; -} - -expr_t * -convert_bool (expr_t *e, int block) -{ - expr_t *b; - - if (e->type == ex_expr && (e->e.expr.op == '=' || e->e.expr.op == PAS)) { - expr_t *src; - if (!e->paren && options.warnings.precedence) - warning (e, "suggest parentheses around assignment " - "used as truth value"); - src = e->e.expr.e2; - if (src->type == ex_block) { - src = new_temp_def_expr (get_type (src)); - e = new_binary_expr (e->e.expr.op, e->e.expr.e1, - assign_expr (src, e->e.expr.e2)); - } - b = convert_bool (src, 1); - if (b->type == ex_error) - return b; - // insert the assignment into the bool's block - e->next = b->e.bool.e->e.block.head; - b->e.bool.e->e.block.head = e; - if (b->e.bool.e->e.block.tail == &b->e.bool.e->e.block.head) { - // shouldn't happen, but just in case - b->e.bool.e->e.block.tail = &e->next; - } - return b; - } - - if (e->type == ex_uexpr && e->e.expr.op == '!' - && get_type (e->e.expr.e1) != &type_string) { - e = convert_bool (e->e.expr.e1, 0); - if (e->type == ex_error) - return e; - e = unary_expr ('!', e); - } - if (e->type != ex_bool) { - e = test_expr (e); - if (e->type == ex_error) - return e; - if (is_integer_val (e)) { - b = goto_expr (0); - if (expr_integer (e)) - e = new_bool_expr (make_list (b), 0, b); - else - e = new_bool_expr (0, make_list (b), b); - } else { - b = new_block_expr (); - append_expr (b, branch_expr ('i', e, 0)); - append_expr (b, goto_expr (0)); - e = new_bool_expr (make_list (b->e.block.head), - make_list (b->e.block.head->next), b); - } - } - if (block && e->e.bool.e->type != ex_block) { - expr_t *block = new_block_expr (); - append_expr (block, e->e.bool.e); - e->e.bool.e = block; - } - return e; -} - expr_t * convert_from_bool (expr_t *e, type_t *type) { @@ -1370,19 +1558,19 @@ convert_from_bool (expr_t *e, type_t *type) expr_t *one; expr_t *cond; - if (type == &type_float) { + if (is_float (type)) { one = new_float_expr (1); zero = new_float_expr (0); - } else if (type == &type_integer) { - one = new_integer_expr (1); - zero = new_integer_expr (0); + } else if (is_int (type)) { + one = new_int_expr (1); + zero = new_int_expr (0); } else if (is_enum (type) && enum_as_bool (type, &zero, &one)) { // don't need to do anything - } else if (type == &type_uinteger) { - one = new_uinteger_expr (1); - zero = new_uinteger_expr (0); + } else if (is_uint (type)) { + one = new_uint_expr (1); + zero = new_uint_expr (0); } else { - return error (e, "can't convert from bool value"); + return error (e, "can't convert from boolean value"); } cond = new_expr (); *cond = *e; @@ -1395,72 +1583,10 @@ convert_from_bool (expr_t *e, type_t *type) } expr_t * -bool_expr (int op, expr_t *label, expr_t *e1, expr_t *e2) -{ - expr_t *block; - - if (!options.code.short_circuit) - return binary_expr (op, e1, e2); - - e1 = convert_bool (e1, 0); - if (e1->type == ex_error) - return e1; - - e2 = convert_bool (e2, 0); - if (e2->type == ex_error) - return e2; - - block = new_block_expr (); - append_expr (block, e1); - append_expr (block, label); - append_expr (block, e2); - - switch (op) { - case OR: - backpatch (e1->e.bool.false_list, label); - return new_bool_expr (merge (e1->e.bool.true_list, - e2->e.bool.true_list), - e2->e.bool.false_list, block); - break; - case AND: - backpatch (e1->e.bool.true_list, label); - return new_bool_expr (e2->e.bool.true_list, - merge (e1->e.bool.false_list, - e2->e.bool.false_list), block); - break; - } - internal_error (e1, 0); -} - -void -convert_int (expr_t *e) -{ - float float_val = expr_integer (e); - e->type = ex_value; - e->e.value = new_float_val (float_val); -} - -void -convert_short (expr_t *e) -{ - float float_val = expr_short (e); - e->type = ex_value; - e->e.value = new_float_val (float_val); -} - -void -convert_short_int (expr_t *e) -{ - float integer_val = expr_short (e); - e->type = ex_value; - e->e.value = new_integer_val (integer_val); -} - -void convert_nil (expr_t *e, type_t *t) { - e->type = ex_value; - e->e.value = new_nil_val (t); + e->e.nil = t; + return e; } int @@ -1493,7 +1619,7 @@ has_function_call (expr_t *e) { switch (e->type) { case ex_bool: - return has_function_call (e->e.bool.e); + return has_function_call (e->e.boolean.e); case ex_block: if (e->e.block.is_call) return 1; @@ -1502,16 +1628,64 @@ has_function_call (expr_t *e) return 1; return 0; case ex_expr: - if (e->e.expr.op == 'c') - return 1; return (has_function_call (e->e.expr.e1) || has_function_call (e->e.expr.e2)); case ex_uexpr: - if (e->e.expr.op != 'g') - return has_function_call (e->e.expr.e1); - default: + return has_function_call (e->e.expr.e1); + case ex_alias: + return has_function_call (e->e.alias.expr); + case ex_address: + return has_function_call (e->e.address.lvalue); + case ex_assign: + return (has_function_call (e->e.assign.dst) + || has_function_call (e->e.assign.src)); + case ex_branch: + if (e->e.branch.type == pr_branch_call) { + return 1; + } + if (e->e.branch.type == pr_branch_jump) { + return 0; + } + return has_function_call (e->e.branch.test); + case ex_return: + return has_function_call (e->e.retrn.ret_val); + case ex_horizontal: + return has_function_call (e->e.hop.vec); + case ex_swizzle: + return has_function_call (e->e.swizzle.src); + case ex_extend: + return has_function_call (e->e.extend.src); + case ex_error: + case ex_state: + case ex_label: + case ex_labelref: + case ex_def: + case ex_symbol: + case ex_temp: + case ex_vector: + case ex_selector: + case ex_nil: + case ex_value: + case ex_compound: + case ex_memset: + case ex_adjstk: + case ex_with: + case ex_args: return 0; + case ex_count: + break; } + internal_error (e, "invalid expression type"); +} + +int +is_function_call (expr_t *e) +{ + if (e->type != ex_block || !e->e.block.is_call) { + return 0; + } + e = e->e.block.result; + return e->type == ex_branch && e->e.branch.type == pr_branch_call; } expr_t * @@ -1536,6 +1710,8 @@ unary_expr (int op, expr_t *e) vec3_t v; quat_t q; const char *s; + expr_t *new; + type_t *t; convert_name (e); if (e->type == ex_error) @@ -1550,20 +1726,28 @@ unary_expr (int op, expr_t *e) case ev_entity: case ev_field: case ev_func: - case ev_pointer: + case ev_ptr: internal_error (e, "type check failed!"); + case ev_double: + new = new_double_expr (-expr_double (e)); + new->implicit = e->implicit; + return new; case ev_float: return new_float_expr (-expr_float (e)); case ev_vector: VectorNegate (expr_vector (e), v); return new_vector_expr (v); - case ev_quat: + case ev_quaternion: QuatNegate (expr_vector (e), q); return new_vector_expr (q); - case ev_integer: - return new_integer_expr (-expr_integer (e)); - case ev_uinteger: - return new_uinteger_expr (-expr_uinteger (e)); + case ev_long: + case ev_ulong: + case ev_ushort: + internal_error (e, "long not implemented"); + case ev_int: + return new_int_expr (-expr_int (e)); + case ev_uint: + return new_uint_expr (-expr_uint (e)); case ev_short: return new_short_expr (-expr_short (e)); case ev_invalid: @@ -1579,21 +1763,56 @@ unary_expr (int op, expr_t *e) case ex_label: case ex_labelref: case ex_state: - internal_error (e, 0); + case ex_compound: + case ex_memset: + case ex_selector: + case ex_return: + case ex_adjstk: + case ex_with: + case ex_args: + internal_error (e, "unexpected expression type"); case ex_uexpr: - if (e->e.expr.op == '-') + if (e->e.expr.op == '-') { return e->e.expr.e1; + } + { + expr_t *n = new_unary_expr (op, e); + + n->e.expr.type = get_type (e); + return n; + } case ex_block: - if (!e->e.block.result) + if (!e->e.block.result) { return error (e, "invalid type for unary -"); + } + { + expr_t *n = new_unary_expr (op, e); + + n->e.expr.type = get_type (e); + return n; + } + case ex_branch: + return error (e, "invalid type for unary -"); case ex_expr: case ex_bool: case ex_temp: case ex_vector: + case ex_alias: + case ex_assign: + case ex_horizontal: + case ex_swizzle: + case ex_extend: { expr_t *n = new_unary_expr (op, e); - n->e.expr.type = e->e.expr.type; + n->e.expr.type = get_type (e); + return n; + } + case ex_def: + { + expr_t *n = new_unary_expr (op, e); + + n->e.expr.type = e->e.def->type; return n; } case ex_symbol: @@ -1604,7 +1823,10 @@ unary_expr (int op, expr_t *e) return n; } case ex_nil: + case ex_address: return error (e, "invalid type for unary -"); + case ex_count: + internal_error (e, "invalid expression"); } break; case '!': @@ -1613,21 +1835,27 @@ unary_expr (int op, expr_t *e) case ev_entity: case ev_field: case ev_func: - case ev_pointer: + case ev_ptr: internal_error (e, 0); case ev_string: s = expr_string (e); - return new_integer_expr (!s || !s[0]); + return new_int_expr (!s || !s[0]); + case ev_double: + return new_int_expr (!expr_double (e)); case ev_float: - return new_integer_expr (!expr_float (e)); + return new_int_expr (!expr_float (e)); case ev_vector: - return new_integer_expr (!VectorIsZero (expr_vector (e))); - case ev_quat: - return new_integer_expr (!QuatIsZero (expr_quaternion (e))); - case ev_integer: - return new_integer_expr (!expr_integer (e)); - case ev_uinteger: - return new_uinteger_expr (!expr_uinteger (e)); + return new_int_expr (!VectorIsZero (expr_vector (e))); + case ev_quaternion: + return new_int_expr (!QuatIsZero (expr_quaternion (e))); + case ev_long: + case ev_ulong: + case ev_ushort: + internal_error (e, "long not implemented"); + case ev_int: + return new_int_expr (!expr_int (e)); + case ev_uint: + return new_uint_expr (!expr_uint (e)); case ev_short: return new_short_expr (!expr_short (e)); case ev_invalid: @@ -1643,29 +1871,48 @@ unary_expr (int op, expr_t *e) case ex_label: case ex_labelref: case ex_state: - internal_error (e, 0); + case ex_compound: + case ex_memset: + case ex_selector: + case ex_return: + case ex_adjstk: + case ex_with: + case ex_args: + internal_error (e, "unexpected expression type"); case ex_bool: - return new_bool_expr (e->e.bool.false_list, - e->e.bool.true_list, e); + return new_bool_expr (e->e.boolean.false_list, + e->e.boolean.true_list, e); case ex_block: if (!e->e.block.result) return error (e, "invalid type for unary !"); case ex_uexpr: case ex_expr: + case ex_def: case ex_symbol: case ex_temp: case ex_vector: - { + case ex_alias: + case ex_address: + case ex_assign: + case ex_horizontal: + case ex_swizzle: + case ex_extend: + if (options.code.progsversion == PROG_VERSION) { + return binary_expr (EQ, e, new_nil_expr ()); + } else { expr_t *n = new_unary_expr (op, e); if (options.code.progsversion > PROG_ID_VERSION) - n->e.expr.type = &type_integer; + n->e.expr.type = &type_int; else n->e.expr.type = &type_float; return n; } + case ex_branch: case ex_nil: return error (e, "invalid type for unary !"); + case ex_count: + internal_error (e, "invalid expression"); } break; case '~': @@ -1675,21 +1922,31 @@ unary_expr (int op, expr_t *e) case ev_entity: case ev_field: case ev_func: - case ev_pointer: + case ev_ptr: case ev_vector: + case ev_double: return error (e, "invalid type for unary ~"); case ev_float: return new_float_expr (~(int) expr_float (e)); - case ev_quat: + case ev_quaternion: QuatConj (expr_vector (e), q); return new_vector_expr (q); - case ev_integer: - return new_integer_expr (~expr_integer (e)); - case ev_uinteger: - return new_uinteger_expr (~expr_uinteger (e)); + case ev_long: + case ev_ulong: + case ev_ushort: + internal_error (e, "long not implemented"); + case ev_int: + return new_int_expr (~expr_int (e)); + case ev_uint: + return new_uint_expr (~expr_uint (e)); case ev_short: return new_short_expr (~expr_short (e)); case ev_invalid: + t = get_type (e); + if (t->meta == ty_enum) { + return new_int_expr (~expr_int (e)); + } + break; case ev_type_count: case ev_void: break; @@ -1702,7 +1959,14 @@ unary_expr (int op, expr_t *e) case ex_label: case ex_labelref: case ex_state: - internal_error (e, 0); + case ex_compound: + case ex_memset: + case ex_selector: + case ex_return: + case ex_adjstk: + case ex_with: + case ex_args: + internal_error (e, "unexpected expression type"); case ex_uexpr: if (e->e.expr.op == '~') return e->e.expr.e1; @@ -1711,31 +1975,42 @@ unary_expr (int op, expr_t *e) if (!e->e.block.result) return error (e, "invalid type for unary ~"); goto bitnot_expr; + case ex_branch: + return error (e, "invalid type for unary ~"); case ex_expr: case ex_bool: + case ex_def: case ex_symbol: case ex_temp: case ex_vector: + case ex_alias: + case ex_assign: + case ex_horizontal: + case ex_swizzle: + case ex_extend: bitnot_expr: if (options.code.progsversion == PROG_ID_VERSION) { - expr_t *n1 = new_integer_expr (-1); + expr_t *n1 = new_int_expr (-1); return binary_expr ('-', n1, e); } else { expr_t *n = new_unary_expr (op, e); type_t *t = get_type (e); - if (t != &type_integer && t != &type_float - && t != &type_quaternion) + if (!is_int(t) && !is_float(t) + && !is_quaternion(t)) return error (e, "invalid type for unary ~"); n->e.expr.type = t; return n; } case ex_nil: + case ex_address: return error (e, "invalid type for unary ~"); + case ex_count: + internal_error (e, "invalid expression"); } break; case '.': - if (extract_type (e) != ev_pointer) + if (extract_type (e) != ev_ptr) return error (e, "invalid type for unary ."); e = new_unary_expr ('.', e); e->e.expr.type = get_type (e->e.expr.e1)->t.fldptr.type; @@ -1749,15 +2024,18 @@ bitnot_expr: } expr_t * -build_function_call (expr_t *fexpr, type_t *ftype, expr_t *params) +build_function_call (expr_t *fexpr, const type_t *ftype, expr_t *params) { expr_t *e; - int arg_count = 0, parm_count = 0; + expr_t *p; + int arg_count = 0, param_count = 0; int i; expr_t *args = 0, **a = &args; - type_t *arg_types[MAX_PARMS]; - expr_t *arg_exprs[MAX_PARMS][2]; + type_t *arg_types[PR_MAX_PARAMS]; + expr_t *arg_exprs[PR_MAX_PARAMS][2]; int arg_expr_count = 0; + int emit_args = 0; + expr_t *assign; expr_t *call; expr_t *err = 0; @@ -1767,8 +2045,8 @@ build_function_call (expr_t *fexpr, type_t *ftype, expr_t *params) arg_count++; } - if (arg_count > MAX_PARMS) { - return error (fexpr, "more than %d parameters", MAX_PARMS); + if (arg_count > PR_MAX_PARAMS) { + return error (fexpr, "more than %d parameters", PR_MAX_PARAMS); } if (ftype->t.func.num_params < -1) { if (-arg_count > ftype->t.func.num_params + 1) { @@ -1777,7 +2055,7 @@ build_function_call (expr_t *fexpr, type_t *ftype, expr_t *params) if (options.warnings.traditional) warning (fexpr, "too few arguments"); } - parm_count = -ftype->t.func.num_params - 1; + param_count = -ftype->t.func.num_params - 1; } else if (ftype->t.func.num_params >= 0) { if (arg_count > ftype->t.func.num_params) { return error (fexpr, "too many arguments"); @@ -1787,23 +2065,38 @@ build_function_call (expr_t *fexpr, type_t *ftype, expr_t *params) if (options.warnings.traditional) warning (fexpr, "too few arguments"); } - parm_count = ftype->t.func.num_params; + param_count = ftype->t.func.num_params; } + if (ftype->t.func.num_params < 0) { + emit_args = !ftype->t.func.no_va_list; + } + // params is reversed (a, b, c) -> c, b, a for (i = arg_count - 1, e = params; i >= 0; i--, e = e->next) { - type_t *t = get_type (e); + type_t *t; - if (!type_size (t)) + if (e->type == ex_compound) { + if (i < param_count) { + t = ftype->t.func.param_types[i]; + } else { + return error (e, "cannot pass compound initializer " + "through ..."); + } + } else { + t = get_type (e); + } + if (!t) { + return e; + } + + if (!type_size (t)) { err = error (e, "type of formal parameter %d is incomplete", i + 1); - if (type_size (t) > type_size (&type_param)) + } + if (!is_array (t) && value_too_large (t)) { err = error (e, "formal parameter %d is too large to be passed by" " value", i + 1); - if (ftype->t.func.param_types[i] == &type_float - && is_integer_val (e)) { - convert_int (e); - t = &type_float; } - if (i < parm_count) { + if (i < param_count) { if (e->type == ex_nil) convert_nil (e, t = ftype->t.func.param_types[i]); if (e->type == ex_bool) @@ -1820,46 +2113,77 @@ build_function_call (expr_t *fexpr, type_t *ftype, expr_t *params) convert_nil (e, t = type_nil); if (e->type == ex_bool) convert_from_bool (e, get_type (e)); - if (is_integer_val (e) + if (is_int_val (e) && options.code.progsversion == PROG_ID_VERSION) - convert_int (e); - if (is_integer_val (e) && options.warnings.vararg_integer) - warning (e, "passing integer constant into ... function"); + e = cast_expr (&type_float, e); + if (options.code.promote_float) { + if (is_scalar (get_type (e)) && is_float (get_type (e))) { + t = &type_double; + } + } else { + if (is_scalar (get_type (e)) && is_double (get_type (e))) { + if (!e->implicit) { + warning (e, "passing double into ... function"); + } + if (is_constant (e)) { + // don't auto-demote non-constant doubles + t = &type_float; + } + } + } + if (is_int_val (e) && options.warnings.vararg_integer) + warning (e, "passing int constant into ... function"); } arg_types[arg_count - 1 - i] = t; } if (err) return err; - call = new_block_expr (); + call = expr_file_line (new_block_expr (), fexpr); call->e.block.is_call = 1; - for (e = params, i = 0; e; e = e->next, i++) { + // args is built in reverse order so it matches params + for (p = params, i = 0; p; p = p->next, i++) { + if (emit_args && arg_count - i == param_count) { + emit_args = 0; + *a = new_args_expr (); + a = &(*a)->next; + } + expr_t *e = p; + if (e->type == ex_compound) { + e = expr_file_line (initialized_temp_expr (arg_types[i], e), e); + } + // FIXME this is target-specific info and should not be in the + // expression tree + // That, or always use a temp, since it should get optimized out if (has_function_call (e)) { - *a = new_temp_def_expr (arg_types[i]); - arg_exprs[arg_expr_count][0] = cast_expr (arg_types[i], convert_vector (e)); + expr_t *cast = cast_expr (arg_types[i], e); + expr_t *tmp = new_temp_def_expr (arg_types[i]); + *a = expr_file_line (tmp, e); + arg_exprs[arg_expr_count][0] = expr_file_line (cast, e); arg_exprs[arg_expr_count][1] = *a; arg_expr_count++; } else { - *a = cast_expr (arg_types[i], convert_vector (e)); + *a = expr_file_line (cast_expr (arg_types[i], e), e); } a = &(*a)->next; } + if (emit_args) { + emit_args = 0; + *a = new_args_expr (); + a = &(*a)->next; + } for (i = 0; i < arg_expr_count - 1; i++) { - append_expr (call, assign_expr (arg_exprs[i][1], arg_exprs[i][0])); + assign = assign_expr (arg_exprs[i][1], arg_exprs[i][0]); + append_expr (call, expr_file_line (assign, arg_exprs[i][0])); } if (arg_expr_count) { e = assign_expr (arg_exprs[arg_expr_count - 1][1], arg_exprs[arg_expr_count - 1][0]); + e = expr_file_line (e, arg_exprs[arg_expr_count - 1][0]); append_expr (call, e); } - e = new_binary_expr ('c', fexpr, args); - e->e.expr.type = ftype->t.func.type; - append_expr (call, e); - if (ftype->t.func.type != &type_void) { - call->e.block.result = new_ret_expr (ftype->t.func.type); - } else if (options.traditional) { - call->e.block.result = new_ret_expr (&type_float); - } + e = expr_file_line (call_expr (fexpr, args, ftype->t.func.type), fexpr); + call->e.block.result = e; return call; } @@ -1898,65 +2222,123 @@ function_expr (expr_t *fexpr, expr_t *params) expr_t * branch_expr (int op, expr_t *test, expr_t *label) { - if (label && label->type != ex_label) + // need to translated op due to precedence rules dictating the layout + // of the token ids + static pr_branch_e branch_type [] = { + pr_branch_eq, + pr_branch_ne, + pr_branch_lt, + pr_branch_gt, + pr_branch_le, + pr_branch_ge, + }; + if (op < EQ || op > LE) { + internal_error (label, "invalid op: %d", op); + } + if (label && label->type != ex_label) { internal_error (label, "not a label"); - if (label) + } + if (label) { label->e.label.used++; - return new_binary_expr (op, test, label); + } + expr_t *branch = new_expr (); + branch->type = ex_branch; + branch->e.branch.type = branch_type[op - EQ]; + branch->e.branch.target = label; + branch->e.branch.test = test; + return branch; } expr_t * goto_expr (expr_t *label) { - if (label && label->type != ex_label) + if (label && label->type != ex_label) { internal_error (label, "not a label"); - if (label) + } + if (label) { label->e.label.used++; - return new_unary_expr ('g', label); + } + expr_t *branch = new_expr (); + branch->type = ex_branch; + branch->e.branch.type = pr_branch_jump; + branch->e.branch.target = label; + return branch; +} + +expr_t * +jump_table_expr (expr_t *table, expr_t *index) +{ + expr_t *branch = new_expr (); + branch->type = ex_branch; + branch->e.branch.type = pr_branch_jump; + branch->e.branch.target = table;//FIXME separate? all branch types can + branch->e.branch.index = index; + return branch; +} + +expr_t * +call_expr (expr_t *func, expr_t *args, type_t *ret_type) +{ + expr_t *branch = new_expr (); + branch->type = ex_branch; + branch->e.branch.type = pr_branch_call; + branch->e.branch.target = func; + branch->e.branch.args = args; + branch->e.branch.ret_type = ret_type; + return branch; } expr_t * return_expr (function_t *f, expr_t *e) { - type_t *t; + const type_t *t; + const type_t *ret_type = unalias_type (f->type->t.func.type); if (!e) { - if (f->sym->type->t.func.type != &type_void) { + if (!is_void(ret_type)) { if (options.traditional) { if (options.warnings.traditional) warning (e, "return from non-void function without a value"); + // force a nil return value in case qf code is being generated e = new_nil_expr (); } else { e = error (e, "return from non-void function without a value"); return e; } } - return new_unary_expr ('r', 0); + // the traditional check above may have set e + if (!e) { + return new_return_expr (0); + } + } + + if (e->type == ex_compound) { + e = expr_file_line (initialized_temp_expr (ret_type, e), e); } t = get_type (e); - if (e->type == ex_error) + if (!t) { return e; - if (f->sym->type->t.func.type == &type_void) { + } + if (is_void(ret_type)) { if (!options.traditional) return error (e, "returning a value for a void function"); if (options.warnings.traditional) warning (e, "returning a value for a void function"); } - if (e->type == ex_bool) - e = convert_from_bool (e, f->sym->type->t.func.type); - if (f->sym->type->t.func.type == &type_float && is_integer_val (e)) { - convert_int (e); + if (e->type == ex_bool) { + e = convert_from_bool (e, (type_t *) ret_type); //FIXME cast + } + if (is_float(ret_type) && is_int_val (e)) { + e = cast_expr (&type_float, e); t = &type_float; } - if (t == &type_void) { + if (is_void(t)) { if (e->type == ex_nil) { - t = f->sym->type->t.func.type; - convert_nil (e, t); - if (e->type == ex_nil) - return error (e, "invalid return type for NIL"); + t = ret_type; + convert_nil (e, (type_t *) t);//FIXME cast } else { if (!options.traditional) return error (e, "void value not ignored as it ought to be"); @@ -1965,18 +2347,51 @@ return_expr (function_t *f, expr_t *e) //FIXME does anything need to be done here? } } - if (!type_assignable (f->sym->type->t.func.type, t)) { + if (!type_assignable (ret_type, t)) { if (!options.traditional) - return error (e, "type mismatch for return value of %s", - f->sym->name); + return error (e, "type mismatch for return value of %s: %s -> %s", + f->sym->name, get_type_string (t), + get_type_string (ret_type)); if (options.warnings.traditional) warning (e, "type mismatch for return value of %s", f->sym->name); } else { - if (f->sym->type->t.func.type != t) - e = cast_expr (f->sym->type->t.func.type, e); + if (ret_type != t) { + e = cast_expr ((type_t *) ret_type, e);//FIXME cast + t = f->sym->type->t.func.type; + } } - return new_unary_expr ('r', e); + if (e->type == ex_vector) { + e = assign_expr (new_temp_def_expr (t), e); + } + if (e->type == ex_block) { + e->e.block.result->rvalue = 1; + } + return new_return_expr (e); +} + +expr_t * +at_return_expr (function_t *f, expr_t *e) +{ + const type_t *ret_type = unalias_type (f->type->t.func.type); + + if (!is_void(ret_type)) { + return error (e, "use of @return in non-void function"); + } + if (is_nil (e)) { + // int or pointer 0 seems reasonable + return new_return_expr (new_int_expr (0)); + } else if (!is_function_call (e)) { + return error (e, "@return value not a function"); + } + expr_t *call_expr = e->e.block.result->e.branch.target; + const type_t *call_type = get_type (call_expr); + if (!is_func (call_type) && !call_type->t.func.void_return) { + return error (e, "@return function not void_return"); + } + expr_t *ret_expr = new_return_expr (e); + ret_expr->e.retrn.at_return = 1; + return ret_expr; } expr_t * @@ -2000,12 +2415,12 @@ conditional_expr (expr_t *cond, expr_t *e1, expr_t *e2) if (cond->type == ex_error) return cond; - backpatch (cond->e.bool.true_list, tlabel); - backpatch (cond->e.bool.false_list, flabel); + backpatch (cond->e.boolean.true_list, tlabel); + backpatch (cond->e.boolean.false_list, flabel); block->e.block.result = (type1 == type2) ? new_temp_def_expr (type1) : 0; append_expr (block, cond); - append_expr (cond->e.bool.e, flabel); + append_expr (cond->e.boolean.e, flabel); if (block->e.block.result) append_expr (block, assign_expr (block->e.block.result, e2)); else @@ -2028,7 +2443,7 @@ incop_expr (int op, expr_t *e, int postop) if (e->type == ex_error) return e; - one = new_integer_expr (1); // integer constants get auto-cast to float + one = new_int_expr (1); // int constants get auto-cast to float if (postop) { expr_t *t1, *t2; type_t *type = get_type (e); @@ -2043,7 +2458,7 @@ incop_expr (int op, expr_t *e, int postop) append_expr (block, assign_expr (t2, binary_expr (op, t1, one))); res = copy_expr (e); if (res->type == ex_uexpr && res->e.expr.op == '.') - res = pointer_expr (address_expr (res, 0, 0)); + res = deref_pointer_expr (address_expr (res, 0)); append_expr (block, assign_expr (res, t2)); block->e.block.result = t1; return block; @@ -2057,7 +2472,11 @@ array_expr (expr_t *array, expr_t *index) { type_t *array_type = get_type (array); type_t *index_type = get_type (index); + type_t *ele_type; expr_t *scale; + expr_t *offset; + expr_t *base; + expr_t *ptr; expr_t *e; int ind = 0; @@ -2066,61 +2485,111 @@ array_expr (expr_t *array, expr_t *index) if (index->type == ex_error) return index; - if (array_type->type != ev_pointer && !is_array (array_type)) + if (!is_ptr (array_type) && !is_array (array_type) + && !is_nonscalar (array_type)) return error (array, "not an array"); if (!is_integral (index_type)) return error (index, "invalid array index type"); if (is_short_val (index)) ind = expr_short (index); - if (is_integer_val (index)) - ind = expr_integer (index); - if (array_type->t.func.num_params + if (is_int_val (index)) + ind = expr_int (index); + if (is_array (array_type) + && array_type->t.array.size && is_constant (index) && (ind < array_type->t.array.base - || ind - array_type->t.array.base >= array_type->t.array.size)) - return error (index, "array index out of bounds"); - scale = new_integer_expr (type_size (array_type->t.array.type)); + || ind - array_type->t.array.base >= array_type->t.array.size)) { + return error (index, "array index out of bounds"); + } + if (is_nonscalar (array_type) + && is_constant (index) + && (ind < 0 || ind >= array_type->width)) { + return error (index, "array index out of bounds"); + } + if (is_array (array_type)) { + ele_type = array_type->t.array.type; + base = new_int_expr (array_type->t.array.base); + } else if (is_ptr (array_type)) { + ele_type = array_type->t.fldptr.type; + base = new_int_expr (0); + } else { + ele_type = ev_types[array_type->type]; + if (array->type == ex_uexpr && array->e.expr.op == '.') { + expr_t *vec = offset_pointer_expr (array->e.expr.e1, index); + vec = cast_expr (pointer_type (ele_type), vec); + return unary_expr ('.', vec); + } + base = new_int_expr (0); + } + scale = new_int_expr (type_size (ele_type)); index = binary_expr ('*', index, scale); - index = binary_expr ('-', index, - binary_expr ('*', - new_integer_expr (array_type->t.array.base), - scale)); - index = fold_constants (index); + offset = binary_expr ('*', base, scale); + index = binary_expr ('-', index, offset); if (is_short_val (index)) ind = expr_short (index); - if (is_integer_val (index)) - ind = expr_integer (index); - if ((is_constant (index) && ind < 32768 && ind >= -32768)) - index = new_short_expr (ind); + if (is_int_val (index)) + ind = expr_int (index); if (is_array (array_type)) { - e = address_expr (array, index, array_type->t.array.type); - } else { - if (!is_short_val (index) || expr_short (index)) { - e = new_binary_expr ('&', array, index); - //e->e.expr.type = array_type->aux_type; - e->e.expr.type = array_type; + if (array->type == ex_uexpr && array->e.expr.op == '.') { + ptr = array->e.expr.e1; } else { - e = array; + expr_t *alias = new_offset_alias_expr (ele_type, array, 0); + ptr = new_address_expr (ele_type, alias, 0); } + } else if (is_nonscalar (array_type)) { + expr_t *alias = new_offset_alias_expr (ele_type, array, 0); + ptr = new_address_expr (ele_type, alias, 0); + } else { + ptr = array; } - e = unary_expr ('.', e); + ptr = offset_pointer_expr (ptr, index); + ptr = cast_expr (pointer_type (ele_type), ptr); + + e = unary_expr ('.', ptr); return e; } expr_t * -pointer_expr (expr_t *pointer) +deref_pointer_expr (expr_t *pointer) { type_t *pointer_type = get_type (pointer); if (pointer->type == ex_error) return pointer; - if (pointer_type->type != ev_pointer) + if (pointer_type->type != ev_ptr) return error (pointer, "not a pointer"); - return array_expr (pointer, new_integer_expr (0)); + return unary_expr ('.', pointer); } expr_t * -address_expr (expr_t *e1, expr_t *e2, type_t *t) +offset_pointer_expr (expr_t *pointer, expr_t *offset) +{ + type_t *ptr_type = get_type (pointer); + if (!is_ptr (ptr_type)) { + internal_error (pointer, "not a pointer"); + } + if (!is_integral (get_type (offset))) { + internal_error (offset, "pointer offset is not an integer type"); + } + expr_t *ptr; + if (pointer->type == ex_alias && !pointer->e.alias.offset + && is_integral (get_type (pointer->e.alias.expr))) { + ptr = pointer->e.alias.expr; + } else if (pointer->type == ex_address && is_constant (offset)) { + if (pointer->e.address.offset) { + offset = binary_expr ('+', pointer->e.address.offset, offset); + } + pointer->e.address.offset = offset; + return pointer; + } else { + ptr = cast_expr (&type_int, pointer); + } + ptr = binary_expr ('+', ptr, offset); + return cast_expr (ptr_type, ptr); +} + +expr_t * +address_expr (expr_t *e1, type_t *t) { expr_t *e; @@ -2131,15 +2600,44 @@ address_expr (expr_t *e1, expr_t *e2, type_t *t) t = get_type (e1); switch (e1->type) { + case ex_def: + { + def_t *def = e1->e.def; + type_t *type = def->type; + + //FIXME this test should be in statements.c + if (options.code.progsversion == PROG_VERSION + && (def->local || def->param)) { + e = new_address_expr (t, e1, 0); + return e; + } + if (is_array (type)) { + e = e1; + e->type = ex_value; + e->e.value = new_pointer_val (0, t, def, 0); + } else { + e = new_pointer_expr (0, t, def); + e->line = e1->line; + e->file = e1->file; + } + } + break; case ex_symbol: if (e1->e.symbol->sy_type == sy_var) { def_t *def = e1->e.symbol->s.def; type_t *type = def->type; + //FIXME this test should be in statements.c + if (options.code.progsversion == PROG_VERSION + && (def->local || def->param)) { + e = new_address_expr (t, e1, 0); + return e; + } + if (is_array (type)) { e = e1; e->type = ex_value; - e->e.value = new_pointer_val (0, t, def); + e->e.value = new_pointer_val (0, t, def, 0); } else { e = new_pointer_expr (0, t, def); e->line = e1->line; @@ -2150,19 +2648,8 @@ address_expr (expr_t *e1, expr_t *e2, type_t *t) return error (e1, "invalid type for unary &"); case ex_expr: if (e1->e.expr.op == '.') { - e = e1; - e->e.expr.op = '&'; - e->e.expr.type = pointer_type (e->e.expr.type); - break; - } - if (e1->e.expr.op == 'm') { - // direct move, so obtain the address of the source - e = address_expr (e1->e.expr.e2, 0, t); - break; - } - if (e1->e.expr.op == 'M') { - // indirect move, so we already have the address of the source - e = e1->e.expr.e2; + e = new_address_expr (e1->e.expr.type, + e1->e.expr.e1, e1->e.expr.e2); break; } return error (e1, "invalid type for unary &"); @@ -2170,46 +2657,24 @@ address_expr (expr_t *e1, expr_t *e2, type_t *t) if (e1->e.expr.op == '.') { e = e1->e.expr.e1; if (e->type == ex_expr && e->e.expr.op == '.') { - e->e.expr.type = pointer_type (e->e.expr.type); - e->e.expr.op = '&'; + e = new_address_expr (e->e.expr.type, e->e.expr.e1, e->e.expr.e2); } break; } - if (e1->e.expr.op == 'A') { - if (!t) - t = e1->e.expr.type; - return address_expr (e1->e.expr.e1, e2, t); - } return error (e1, "invalid type for unary &"); - case ex_block: - if (!e1->e.block.result) - return error (e1, "invalid type for unary &"); - e1->e.block.result = address_expr (e1->e.block.result, e2, t); - return e1; case ex_label: return new_label_ref (&e1->e.label); + case ex_temp: + e = new_address_expr (t, e1, 0); + break; + case ex_alias: + if (!t) { + t = e1->e.alias.type; + } + return new_address_expr (t, e1, 0); default: return error (e1, "invalid type for unary &"); } - if (e2) { - if (e2->type == ex_error) - return e2; - if (e->type == ex_value && e->e.value->type == ev_pointer - && is_short_val (e2)) { - e->e.value = new_pointer_val (e->e.value->v.pointer.val + expr_short (e2), t, e->e.value->v.pointer.def); - } else { - if (!is_short_val (e2) || expr_short (e2)) { - if (e->type == ex_expr && e->e.expr.op == '&') { - e = new_binary_expr ('&', e->e.expr.e1, - binary_expr ('+', e->e.expr.e2, e2)); - } else { - e = new_binary_expr ('&', e, e2); - } - } - if (e->type == ex_expr || e->type == ex_uexpr) - e->e.expr.type = pointer_type (t); - } - } return e; } @@ -2217,7 +2682,7 @@ expr_t * build_if_statement (int not, expr_t *test, expr_t *s1, expr_t *els, expr_t *s2) { int line = pr.source_line; - string_t file = pr.source_file; + pr_string_t file = pr.source_file; expr_t *if_expr; expr_t *tl = new_label_expr (); expr_t *fl = new_label_expr (); @@ -2238,13 +2703,13 @@ build_if_statement (int not, expr_t *test, expr_t *s1, expr_t *els, expr_t *s2) test = convert_bool (test, 1); if (test->type != ex_error) { if (not) { - backpatch (test->e.bool.true_list, fl); - backpatch (test->e.bool.false_list, tl); + backpatch (test->e.boolean.true_list, fl); + backpatch (test->e.boolean.false_list, tl); } else { - backpatch (test->e.bool.true_list, tl); - backpatch (test->e.bool.false_list, fl); + backpatch (test->e.boolean.true_list, tl); + backpatch (test->e.boolean.false_list, fl); } - append_expr (test->e.bool.e, tl); + append_expr (test->e.boolean.e, tl); append_expr (if_expr, test); } append_expr (if_expr, s1); @@ -2276,7 +2741,7 @@ build_while_statement (int not, expr_t *test, expr_t *statement, expr_t *break_label, expr_t *continue_label) { int line = pr.source_line; - string_t file = pr.source_file; + pr_string_t file = pr.source_file; expr_t *l1 = new_label_expr (); expr_t *l2 = break_label; expr_t *while_expr; @@ -2294,13 +2759,13 @@ build_while_statement (int not, expr_t *test, expr_t *statement, test = convert_bool (test, 1); if (test->type != ex_error) { if (not) { - backpatch (test->e.bool.true_list, l2); - backpatch (test->e.bool.false_list, l1); + backpatch (test->e.boolean.true_list, l2); + backpatch (test->e.boolean.false_list, l1); } else { - backpatch (test->e.bool.true_list, l1); - backpatch (test->e.bool.false_list, l2); + backpatch (test->e.boolean.true_list, l1); + backpatch (test->e.boolean.false_list, l2); } - append_expr (test->e.bool.e, l2); + append_expr (test->e.boolean.e, l2); append_expr (while_expr, test); } @@ -2316,7 +2781,7 @@ build_do_while_statement (expr_t *statement, int not, expr_t *test, { expr_t *l1 = new_label_expr (); int line = pr.source_line; - string_t file = pr.source_file; + pr_string_t file = pr.source_file; expr_t *do_while_expr; if (!statement) { @@ -2336,13 +2801,13 @@ build_do_while_statement (expr_t *statement, int not, expr_t *test, test = convert_bool (test, 1); if (test->type != ex_error) { if (not) { - backpatch (test->e.bool.true_list, break_label); - backpatch (test->e.bool.false_list, l1); + backpatch (test->e.boolean.true_list, break_label); + backpatch (test->e.boolean.false_list, l1); } else { - backpatch (test->e.bool.true_list, l1); - backpatch (test->e.bool.false_list, break_label); + backpatch (test->e.boolean.true_list, l1); + backpatch (test->e.boolean.false_list, break_label); } - append_expr (test->e.bool.e, break_label); + append_expr (test->e.boolean.e, break_label); append_expr (do_while_expr, test); } @@ -2362,7 +2827,7 @@ build_for_statement (expr_t *init, expr_t *test, expr_t *next, expr_t *l1 = 0; expr_t *t; int line = pr.source_line; - string_t file = pr.source_file; + pr_string_t file = pr.source_file; expr_t *for_expr; if (next) @@ -2391,9 +2856,9 @@ build_for_statement (expr_t *init, expr_t *test, expr_t *next, append_expr (for_expr, l1); test = convert_bool (test, 1); if (test->type != ex_error) { - backpatch (test->e.bool.true_list, tl); - backpatch (test->e.bool.false_list, fl); - append_expr (test->e.bool.e, fl); + backpatch (test->e.boolean.true_list, tl); + backpatch (test->e.boolean.false_list, fl); + append_expr (test->e.boolean.e, fl); append_expr (for_expr, test); } } else { @@ -2420,8 +2885,8 @@ build_state_expr (expr_t *e) step = think->next; if (think->type == ex_symbol) think = think_expr (think->e.symbol); - if (is_integer_val (frame)) - convert_int (frame); + if (is_int_val (frame)) + frame = cast_expr (&type_float, frame); if (!type_assignable (&type_float, get_type (frame))) return error (frame, "invalid type for frame number"); if (extract_type (think) != ev_func) @@ -2429,8 +2894,8 @@ build_state_expr (expr_t *e) if (step) { if (step->next) return error (step->next, "too many state arguments"); - if (is_integer_val (step)) - convert_int (step); + if (is_int_val (step)) + step = cast_expr (&type_float, step); if (!type_assignable (&type_float, get_type (step))) return error (step, "invalid type for step"); } @@ -2451,300 +2916,13 @@ think_expr (symbol_t *think_sym) && sym->type->t.fldptr.type->type == ev_func) { think_sym->type = sym->type->t.fldptr.type; } else { - think_sym->type = &type_function; + think_sym->type = &type_func; } think_sym = function_symbol (think_sym, 0, 1); make_function (think_sym, 0, current_symtab->space, current_storage); return new_symbol_expr (think_sym); } -static int -is_indirect (expr_t *e) -{ - if (e->type == ex_block && e->e.block.result) - return is_indirect (e->e.block.result); - if (e->type == ex_expr && e->e.expr.op == '.') - return 1; - if (!(e->type == ex_uexpr && e->e.expr.op == '.')) - return 0; - e = e->e.expr.e1; - if (e->type != ex_value || e->e.value->type != ev_pointer - || !(POINTER_VAL (e->e.value->v.pointer) >= 0 - && POINTER_VAL (e->e.value->v.pointer) < 65536)) { - return 1; - } - return 0; -} - -static inline int -is_lvalue (expr_t *e) -{ - if (e->type == ex_symbol) { - switch (e->e.symbol->sy_type) { - case sy_var: - return 1; - case sy_const: - return 0; - case sy_type: - return 0; - case sy_expr: - return 0; - case sy_func: - return 0; - case sy_class: - return 0; - } - } - if (e->type == ex_temp) - return 1; - if (e->type == ex_expr && e->e.expr.op == '.') - return 1; - if (e->type == ex_uexpr && e->e.expr.op == '.') - return 1; - if (e->type == ex_uexpr && e->e.expr.op == 'A') - return is_lvalue (e->e.expr.e1); - return 0; -} - -expr_t * -assign_expr (expr_t *e1, expr_t *e2) -{ - int op = '='; - type_t *t1, *t2, *type; - expr_t *e; - - convert_name (e1); - convert_name (e2); - e2 = convert_vector (e2); - - if (e1->type == ex_error) - return e1; - if (e2->type == ex_error) - return e2; - - e1 = fold_constants (e1); - e2 = fold_constants (e2); - - if (options.traditional) { - if (e2->type == ex_expr && !e2->paren - && (e2->e.expr.op == AND || e2->e.expr.op == OR)) { - notice (e2, "precedence of `%s' and `%s' inverted for " - "traditional code", get_op_string (op), - get_op_string (e2->e.expr.op)); - e1 = assign_expr (e1, e2->e.expr.e1); - e1->paren = 1; - return binary_expr (e2->e.expr.op, e1, e2->e.expr.e2); - } - } - - if (!is_lvalue (e1)) { - if (options.traditional) - warning (e1, "invalid lvalue in assignment"); - else - return error (e1, "invalid lvalue in assignment"); - } - t1 = get_type (e1); - t2 = get_type (e2); - if (!t1 || !t2) - internal_error (e1, 0); - //XXX func = func ??? - if (t1->type == ev_pointer && is_array (t2)) { - e2 = address_expr (e2, 0, t2->t.fldptr.type); - t2 = get_type (e2); - } - if (e2->type == ex_bool) - e2 = convert_from_bool (e2, t1); - - if (t1->type != ev_void && e2->type == ex_nil) { - t2 = t1; - convert_nil (e2, t2); - } - - e2->rvalue = 1; - - if (!type_assignable (t1, t2)) { - if (options.traditional) { - if (t1->type == ev_func && t2->type == ev_func) { - warning (e1, "assignment between disparate function types"); - } else if (t1->type == ev_float && t2->type == ev_vector) { - warning (e1, "assignment of vector to float"); - e2 = field_expr (e2, new_name_expr ("x")); - } else if (t1->type == ev_vector && t2->type == ev_float) { - warning (e1, "assignment of float to vector"); - e1 = field_expr (e1, new_name_expr ("x")); - } else { - return type_mismatch (e1, e2, op); - } - } else { - return type_mismatch (e1, e2, op); - } - } - type = t1; - if (is_indirect (e1) && is_indirect (e2)) { - if (is_struct (get_type (e2))) { - e1 = address_expr (e1, 0, 0); - e2 = address_expr (e2, 0, 0); - e = new_move_expr (e1, e2, t2, 1); - } else { - expr_t *temp = new_temp_def_expr (t1); - - e = new_block_expr (); - append_expr (e, assign_expr (temp, e2)); - append_expr (e, assign_expr (e1, temp)); - e->e.block.result = temp; - } - return e; - } else if (is_indirect (e1)) { - if (is_struct (get_type (e1))) { - e1 = address_expr (e1, 0, 0); - e2 = address_expr (e2, 0, 0); - return new_move_expr (e1, e2, t1, 1); - } - if (e1->type == ex_expr) { - if (get_type (e1->e.expr.e1) == &type_entity) { - type = e1->e.expr.type; - e1->e.expr.type = pointer_type (type); - e1->e.expr.op = '&'; - } - op = PAS; - } else { - e = e1->e.expr.e1; - if ((e->type != ex_value || e->e.value->type != ev_pointer) - || !(POINTER_VAL (e->e.value->v.pointer) > 0 - && POINTER_VAL (e->e.value->v.pointer) < 65536)) { - e1 = e; - op = PAS; - } - } - } else if (is_indirect (e2)) { - if (is_struct (get_type (e1))) { - e1 = address_expr (e1, 0, 0); - e2 = address_expr (e2, 0, 0); - e2->rvalue = 1; - return new_move_expr (e1, e2, t2, 1); - } - if (e2->type == ex_uexpr) { - e = e2->e.expr.e1; - if ((e->type != ex_value || e->e.value->type != ev_pointer) - || !(POINTER_VAL (e->e.value->v.pointer) > 0 - && POINTER_VAL (e->e.value->v.pointer) < 65536)) { - if (e->type == ex_expr && e->e.expr.op == '&' - && e->e.expr.type->type == ev_pointer - && !is_constant (e)) { - e2 = e; - e2->e.expr.op = '.'; - e2->e.expr.type = t2; - e2->rvalue = 1; - } - } - } - } - if (is_struct (get_type (e1))) { - return new_move_expr (e1, e2, get_type (e1), 0); - } - if (!type) - internal_error (e1, 0); - - e = new_binary_expr (op, e1, e2); - e->e.expr.type = type; - return e; -} - -expr_t * -cast_expr (type_t *type, expr_t *e) -{ - expr_t *c; - type_t *e_type; - - convert_name (e); - - if (e->type == ex_error) - return e; - - e_type = get_type (e); - - if (type == e_type) - return e; - - if ((type == type_default && is_enum (e_type)) - || (is_enum (type) && e_type == type_default)) - return e; - if (!(type->type == ev_pointer - && (e_type->type == ev_pointer || is_integral (e_type) - || is_array (e_type))) - && !(is_integral (type) && e_type->type == ev_pointer) - && !(type->type == ev_func && e_type->type == ev_func) - && !(is_scalar (type) && is_scalar (e_type))) { - return cast_error (e, e_type, type); - } - if (is_array (e_type)) - return address_expr (e, 0, 0); - if (is_constant (e) && is_scalar (type) && is_scalar (e_type)) { - ex_value_t *val = 0; - if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const) { - val = e->e.symbol->s.value; - } else if (e->type == ex_value) { - val = e->e.value; - } else if (e->type == ex_nil) { - convert_nil (e, type); - return e; - } - if (!val) - internal_error (e, "unexpected constant expression type"); - e->e.value = convert_value (val, type); - e->type = ex_value; - c = e; - } else if ((is_float (type) && is_integral (e_type)) - || (is_integral (type) && is_float (e_type))) { - c = new_unary_expr ('C', e); - c->e.expr.type = type; - } else if (e->type == ex_uexpr && e->e.expr.op == '.') { - e->e.expr.type = type; - c = e; - } else { - c = new_alias_expr (type, e); - } - return c; -} - -expr_t * -selector_expr (keywordarg_t *selector) -{ - dstring_t *sel_id = dstring_newstr (); - expr_t *sel; - symbol_t *sel_sym; - symbol_t *sel_table; - int index; - - selector = copy_keywordargs (selector); - selector = (keywordarg_t *) reverse_params ((param_t *) selector); - selector_name (sel_id, selector); - index = selector_index (sel_id->str); - index *= type_size (type_SEL.t.fldptr.type); - sel_sym = make_symbol ("_OBJ_SELECTOR_TABLE_PTR", &type_SEL, - pr.near_data, sc_static); - if (!sel_sym->table) { - symtab_addsymbol (pr.symtab, sel_sym); - sel_table = make_symbol ("_OBJ_SELECTOR_TABLE", - array_type (type_SEL.t.fldptr.type, 0), - pr.far_data, sc_extern); - if (!sel_table->table) - symtab_addsymbol (pr.symtab, sel_table); - reloc_def_def (sel_table->s.def, sel_sym->s.def); - } - sel = new_symbol_expr (sel_sym); - dstring_delete (sel_id); - sel = new_binary_expr ('&', sel, new_short_expr (index)); - sel->e.expr.type = &type_SEL; - return sel; -} - -expr_t * -protocol_expr (const char *protocol) -{ - return error (0, "not implemented"); -} - expr_t * encode_expr (type_t *type) { @@ -2757,139 +2935,6 @@ encode_expr (type_t *type) return e; } -expr_t * -super_expr (class_type_t *class_type) -{ - symbol_t *sym; - expr_t *super; - expr_t *e; - expr_t *super_block; - class_t *class; - - if (!class_type) - return error (0, "`super' used outside of class implementation"); - - class = extract_class (class_type); - - if (!class->super_class) - return error (0, "%s has no super class", class->name); - - sym = symtab_lookup (current_symtab, ".super"); - if (!sym || sym->table != current_symtab) { - sym = new_symbol (".super"); - initialize_def (sym, &type_obj_super, 0, current_symtab->space, - sc_local); - } - super = new_symbol_expr (sym); - - super_block = new_block_expr (); - - e = assign_expr (field_expr (super, new_name_expr ("self")), - new_name_expr ("self")); - append_expr (super_block, e); - - e = new_symbol_expr (class_pointer_symbol (class)); - e = assign_expr (field_expr (super, new_name_expr ("class")), - field_expr (e, new_name_expr ("super_class"))); - append_expr (super_block, e); - - e = address_expr (super, 0, 0); - super_block->e.block.result = e; - return super_block; -} - -expr_t * -message_expr (expr_t *receiver, keywordarg_t *message) -{ - expr_t *args = 0, **a = &args; - expr_t *selector = selector_expr (message); - expr_t *call; - keywordarg_t *m; - int self = 0, super = 0, class_msg = 0; - type_t *rec_type; - type_t *return_type; - type_t *method_type = &type_IMP; - class_t *class = 0; - method_t *method; - expr_t *send_msg; - - if (receiver->type == ex_symbol - && strcmp (receiver->e.symbol->name, "super") == 0) { - super = 1; - - receiver = super_expr (current_class); - - if (receiver->type == ex_error) - return receiver; - receiver = cast_expr (&type_id, receiver); //FIXME better way? - class = extract_class (current_class); - rec_type = class->type; - } else { - if (receiver->type == ex_symbol) { - if (strcmp (receiver->e.symbol->name, "self") == 0) - self = 1; - if (receiver->e.symbol->sy_type == sy_class) { - class = receiver->e.symbol->type->t.class; - class_msg = 1; - receiver = new_symbol_expr (class_pointer_symbol (class)); - } - } else if (receiver->type == ex_nil) { - convert_nil (receiver, &type_id); - } - rec_type = get_type (receiver); - - if (receiver->type == ex_error) - return receiver; - - if (rec_type == &type_id || rec_type == &type_Class) { - } else { - if (rec_type->type == ev_pointer) - rec_type = rec_type->t.fldptr.type; - if (!obj_is_class (rec_type)) - return error (receiver, "not a class/object"); - - if (self) { - if (!class) - class = extract_class (current_class); - if (rec_type == &type_obj_class) - class_msg = 1; - } else { - if (!class) - class = rec_type->t.class; - } - } - } - - return_type = &type_id; - method = class_message_response (class, class_msg, selector); - if (method) - return_type = method->type->t.func.type; - - for (m = message; m; m = m->next) { - *a = m->expr; - while ((*a)) - a = &(*a)->next; - } - *a = selector; - a = &(*a)->next; - *a = receiver; - - send_msg = send_message (super); - if (method) { - expr_t *err; - if ((err = method_check_params (method, args))) - return err; - method_type = method->type; - } - call = build_function_call (send_msg, method_type, args); - - if (call->type == ex_error) - return receiver; - - call->e.block.result = new_ret_expr (return_type); - return call; -} - expr_t * sizeof_expr (expr_t *expr, struct type_s *type) { @@ -2897,7 +2942,9 @@ sizeof_expr (expr_t *expr, struct type_s *type) internal_error (0, 0); if (!type) type = get_type (expr); - expr = new_integer_expr (type_size (type)); + if (type) { + expr = new_int_expr (type_size (type)); + } return expr; } diff --git a/tools/qfcc/source/expr_assign.c b/tools/qfcc/source/expr_assign.c new file mode 100644 index 000000000..ad9fa86e5 --- /dev/null +++ b/tools/qfcc/source/expr_assign.c @@ -0,0 +1,379 @@ +/* + expr_assign.c + + assignment expression construction and manipulations + + Copyright (C) 2001 Bill Currie + + Author: Bill Currie + Date: 2001/06/15 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include + +#include "QF/alloc.h" +#include "QF/dstring.h" +#include "QF/mathlib.h" +#include "QF/sys.h" +#include "QF/va.h" + +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/emit.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/idstuff.h" +#include "tools/qfcc/include/method.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/shared.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/struct.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" + +static expr_t * +check_assign_logic_precedence (expr_t *dst, expr_t *src) +{ + if (src->type == ex_expr && !src->paren && is_logic (src->e.expr.op)) { + // traditional QuakeC gives = higher precedence than && and || + expr_t *assignment; + notice (src, "precedence of `=' and `%s' inverted for " + "traditional code", get_op_string (src->e.expr.op)); + // change {a = (b logic c)} to {(a = b) logic c} + assignment = assign_expr (dst, src->e.expr.e1); + assignment->paren = 1; // protect assignment from binary_expr + return binary_expr (src->e.expr.op, assignment, src->e.expr.e2); + } + return 0; +} + +int +is_lvalue (const expr_t *expr) +{ + switch (expr->type) { + case ex_def: + return !expr->e.def->constant; + case ex_symbol: + switch (expr->e.symbol->sy_type) { + case sy_name: + break; + case sy_var: + return 1; + case sy_const: + break; + case sy_type: + break; + case sy_expr: + break; + case sy_func: + break; + case sy_class: + break; + case sy_convert: + break; + } + break; + case ex_temp: + return 1; + case ex_expr: + if (expr->e.expr.op == '.') { + return 1; + } + break; + case ex_alias: + return is_lvalue (expr->e.alias.expr); + case ex_address: + return 0; + case ex_assign: + return 0; + case ex_uexpr: + if (expr->e.expr.op == '.') { + return 1; + } + break; + case ex_branch: + case ex_memset: + case ex_compound: + case ex_state: + case ex_bool: + case ex_label: + case ex_labelref: + case ex_block: + case ex_vector: + case ex_nil: + case ex_value: + case ex_error: + case ex_selector: + case ex_return: + case ex_adjstk: + case ex_with: + case ex_args: + case ex_horizontal: + case ex_swizzle: + case ex_extend: + break; + case ex_count: + internal_error (expr, "invalid expression"); + } + return 0; +} + +static expr_t * +check_valid_lvalue (expr_t *expr) +{ + if (!is_lvalue (expr)) { + if (options.traditional) { + warning (expr, "invalid lvalue in assignment"); + return 0; + } + return error (expr, "invalid lvalue in assignment"); + } + return 0; +} + +static expr_t * +check_types_compatible (expr_t *dst, expr_t *src) +{ + type_t *dst_type = get_type (dst); + type_t *src_type = get_type (src); + + if (dst_type == src_type) { + return 0; + } + + if (type_assignable (dst_type, src_type)) { + debug (dst, "casting %s to %s", src_type->name, dst_type->name); + if (!src->implicit && !type_promotes (dst_type, src_type)) { + if (is_double (src_type)) { + warning (dst, "assignment of %s to %s (use a cast)\n", + src_type->name, dst_type->name); + } + } + // the types are different but cast-compatible + expr_t *new = cast_expr (dst_type, src); + // the cast was a no-op, so the types are compatible at the + // low level (very true for default type <-> enum) + if (new != src) { + return assign_expr (dst, new); + } + return 0; + } + // traditional qcc is a little sloppy + if (!options.traditional) { + return type_mismatch (dst, src, '='); + } + if (is_func (dst_type) && is_func (src_type)) { + warning (dst, "assignment between disparate function types"); + return 0; + } + if (is_float (dst_type) && is_vector (src_type)) { + warning (dst, "assignment of vector to float"); + src = field_expr (src, new_name_expr ("x")); + return assign_expr (dst, src); + } + if (is_vector (dst_type) && is_float (src_type)) { + warning (dst, "assignment of float to vector"); + dst = field_expr (dst, new_name_expr ("x")); + return assign_expr (dst, src); + } + return type_mismatch (dst, src, '='); +} + +static void +copy_qv_elements (expr_t *block, expr_t *dst, expr_t *src) +{ + expr_t *dx, *sx; + expr_t *dy, *sy; + expr_t *dz, *sz; + expr_t *dw, *sw; + expr_t *ds, *ss; + expr_t *dv, *sv; + + if (is_vector (src->e.vector.type)) { + // guaranteed to have three elements + sx = src->e.vector.list; + sy = sx->next; + sz = sy->next; + dx = field_expr (dst, new_name_expr ("x")); + dy = field_expr (dst, new_name_expr ("y")); + dz = field_expr (dst, new_name_expr ("z")); + append_expr (block, assign_expr (dx, sx)); + append_expr (block, assign_expr (dy, sy)); + append_expr (block, assign_expr (dz, sz)); + } else { + // guaranteed to have two or four elements + if (src->e.vector.list->next->next) { + // four vals: x, y, z, w + sx = src->e.vector.list; + sy = sx->next; + sz = sy->next; + sw = sz->next; + dx = field_expr (dst, new_name_expr ("x")); + dy = field_expr (dst, new_name_expr ("y")); + dz = field_expr (dst, new_name_expr ("z")); + dw = field_expr (dst, new_name_expr ("w")); + append_expr (block, assign_expr (dx, sx)); + append_expr (block, assign_expr (dy, sy)); + append_expr (block, assign_expr (dz, sz)); + append_expr (block, assign_expr (dw, sw)); + } else { + // v, s + sv = src->e.vector.list; + ss = sv->next; + dv = field_expr (dst, new_name_expr ("v")); + ds = field_expr (dst, new_name_expr ("s")); + append_expr (block, assign_expr (dv, sv)); + append_expr (block, assign_expr (ds, ss)); + } + } +} + +static int +copy_elements (expr_t *block, expr_t *dst, expr_t *src, int base) +{ + int index = 0; + for (expr_t *e = src->e.vector.list; e; e = e->next) { + if (e->type == ex_vector) { + index += copy_elements (block, dst, e, index + base); + } else { + expr_t *dst_ele = array_expr (dst, new_int_expr (index + base)); + append_expr (block, assign_expr (dst_ele, e)); + index += type_width (get_type (e)); + } + } + return index; +} + +static expr_t * +assign_vector_expr (expr_t *dst, expr_t *src) +{ + if (src->type == ex_vector && dst->type != ex_vector) { + expr_t *block = new_block_expr (); + + if (options.code.progsversion <= PROG_VERSION) { + copy_qv_elements (block, dst, src); + } else { + copy_elements (block, dst, src, 0); + } + block->e.block.result = dst; + return block; + } + return 0; +} + +static __attribute__((pure)) int +is_memset (expr_t *e) +{ + return e->type == ex_memset; +} + +expr_t * +assign_expr (expr_t *dst, expr_t *src) +{ + expr_t *expr; + type_t *dst_type, *src_type; + + convert_name (dst); + if (dst->type == ex_error) { + return dst; + } + if ((expr = check_valid_lvalue (dst))) { + return expr; + } + dst_type = get_type (dst); + if (!dst_type) { + internal_error (dst, "dst_type broke in assign_expr"); + } + + if (src && !is_memset (src)) { + convert_name (src); + if (src->type == ex_error) { + return src; + } + + if (options.traditional + && (expr = check_assign_logic_precedence (dst, src))) { + return expr; + } + } else { + if (!src && is_scalar (dst_type)) { + return error (dst, "empty scalar initializer"); + } + src = new_nil_expr (); + } + if (src->type == ex_compound) { + src = initialized_temp_expr (dst_type, src); + if (src->type == ex_error) { + return src; + } + } + src_type = get_type (src); + if (!src_type) { + internal_error (src, "src_type broke in assign_expr"); + } + + if (is_ptr (dst_type) && is_array (src_type)) { + // assigning an array to a pointer is the same as taking the address of + // the array but using the type of the array elements + src = address_expr (src, src_type->t.fldptr.type); + src_type = get_type (src); + } + if (src->type == ex_bool) { + // boolean expressions are chains of tests, so extract the result + // of the tests + src = convert_from_bool (src, dst_type); + if (src->type == ex_error) { + return src; + } + src_type = get_type (src); + } + + if (!is_nil (src)) { + if ((expr = check_types_compatible (dst, src))) { + // expr might be a valid expression, but if so, + // check_types_compatible will take care of everything + return expr; + } + if ((expr = assign_vector_expr (dst, src))) { + return expr; + } + } else { + convert_nil (src, dst_type); + } + + expr = new_assign_expr (dst, src); + return expr; +} diff --git a/tools/qfcc/source/expr_binary.c b/tools/qfcc/source/expr_binary.c index 2aa963b3a..c55182667 100644 --- a/tools/qfcc/source/expr_binary.c +++ b/tools/qfcc/source/expr_binary.c @@ -31,27 +31,39 @@ # include "config.h" #endif -#include "diagnostic.h" -#include "expr.h" -#include "options.h" -#include "type.h" -#include "qc-parse.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/type.h" + +#include "tools/qfcc/source/qc-parse.h" typedef struct { int op; - type_t *type; + type_t *result_type; type_t *a_cast; type_t *b_cast; + expr_t *(*process)(int op, expr_t *e1, expr_t *e2); } expr_type_t; +static expr_t *pointer_arithmetic (int op, expr_t *e1, expr_t *e2); +static expr_t *pointer_compare (int op, expr_t *e1, expr_t *e2); +static expr_t *func_compare (int op, expr_t *e1, expr_t *e2); +static expr_t *inverse_multiply (int op, expr_t *e1, expr_t *e2); +static expr_t *double_compare (int op, expr_t *e1, expr_t *e2); +static expr_t *vector_compare (int op, expr_t *e1, expr_t *e2); +static expr_t *vector_multiply (int op, expr_t *e1, expr_t *e2); +static expr_t *vector_scale (int op, expr_t *e1, expr_t *e2); +static expr_t *entity_compare (int op, expr_t *e1, expr_t *e2); + static expr_type_t string_string[] = { {'+', &type_string}, - {EQ, &type_integer}, - {NE, &type_integer}, - {LE, &type_integer}, - {GE, &type_integer}, - {LT, &type_integer}, - {GT, &type_integer}, + {EQ, &type_int}, + {NE, &type_int}, + {LE, &type_int}, + {GE, &type_int}, + {LT, &type_int}, + {GT, &type_int}, {0, 0} }; @@ -64,29 +76,31 @@ static expr_type_t float_float[] = { {'|', &type_float}, {'^', &type_float}, {'%', &type_float}, + {MOD, &type_float}, {SHL, &type_float}, {SHR, &type_float}, - {EQ, &type_integer}, - {NE, &type_integer}, - {LE, &type_integer}, - {GE, &type_integer}, - {LT, &type_integer}, - {GT, &type_integer}, + {AND, &type_int}, + {OR, &type_int}, + {EQ, &type_int}, + {NE, &type_int}, + {LE, &type_int}, + {GE, &type_int}, + {LT, &type_int}, + {GT, &type_int}, {0, 0} }; static expr_type_t float_vector[] = { - {'*', &type_vector}, + {'*', .process = vector_scale }, {0, 0} }; static expr_type_t float_quat[] = { {'*', &type_quaternion}, - {'/', &type_quaternion}, {0, 0} }; -static expr_type_t float_integer[] = { +static expr_type_t float_int[] = { {'+', &type_float, 0, &type_float}, {'-', &type_float, 0, &type_float}, {'*', &type_float, 0, &type_float}, @@ -95,78 +109,104 @@ static expr_type_t float_integer[] = { {'|', &type_float, 0, &type_float}, {'^', &type_float, 0, &type_float}, {'%', &type_float, 0, &type_float}, + {MOD, &type_float, 0, &type_float}, {SHL, &type_float, 0, &type_float}, {SHR, &type_float, 0, &type_float}, - {EQ, &type_integer, 0, &type_float}, - {NE, &type_integer, 0, &type_float}, - {LE, &type_integer, 0, &type_float}, - {GE, &type_integer, 0, &type_float}, - {LT, &type_integer, 0, &type_float}, - {GT, &type_integer, 0, &type_float}, + {EQ, &type_int, 0, &type_float}, + {NE, &type_int, 0, &type_float}, + {LE, &type_int, 0, &type_float}, + {GE, &type_int, 0, &type_float}, + {LT, &type_int, 0, &type_float}, + {GT, &type_int, 0, &type_float}, + {0, 0} +}; +#define float_uint float_int +#define float_short float_int + +static expr_type_t float_double[] = { + {'+', &type_double, &type_double, 0}, + {'-', &type_double, &type_double, 0}, + {'*', &type_double, &type_double, 0}, + {'/', &type_double, &type_double, 0}, + {'%', &type_double, &type_double, 0}, + {MOD, &type_double, &type_double, 0}, + {EQ, 0, 0, 0, double_compare}, + {NE, 0, 0, 0, double_compare}, + {LE, 0, 0, 0, double_compare}, + {GE, 0, 0, 0, double_compare}, + {LT, 0, 0, 0, double_compare}, + {GT, 0, 0, 0, double_compare}, {0, 0} }; -#define float_uinteger float_integer -#define float_short float_integer static expr_type_t vector_float[] = { - {'*', &type_vector}, - {'/', &type_vector}, + {'*', .process = vector_scale }, + {'/', 0, 0, 0, inverse_multiply}, {0, 0} }; static expr_type_t vector_vector[] = { {'+', &type_vector}, {'-', &type_vector}, - {'*', &type_float}, - {EQ, &type_integer}, - {NE, &type_integer}, + {DOT, &type_vector}, + {CROSS, &type_vector}, + {HADAMARD, &type_vector}, + {'*', 0, 0, 0, vector_multiply}, + {EQ, 0, 0, 0, vector_compare}, + {NE, 0, 0, 0, vector_compare}, {0, 0} }; -#define vector_integer vector_float -#define vector_uinteger vector_float +#define vector_int vector_float +#define vector_uint vector_float #define vector_short vector_float +static expr_type_t vector_double[] = { + {'*', &type_vector, 0, &type_float, vector_scale}, + {'/', 0, 0, 0, inverse_multiply}, + {0, 0} +}; + static expr_type_t entity_entity[] = { - {EQ, &type_integer}, - {NE, &type_integer}, + {EQ, &type_int, 0, 0, entity_compare}, + {NE, &type_int, 0, 0, entity_compare}, {0, 0} }; static expr_type_t field_field[] = { - {EQ, &type_integer}, - {NE, &type_integer}, + {EQ, &type_int}, + {NE, &type_int}, {0, 0} }; static expr_type_t func_func[] = { - {EQ, &type_integer}, - {NE, &type_integer}, + {EQ, 0, 0, 0, func_compare}, + {NE, 0, 0, 0, func_compare}, {0, 0} }; static expr_type_t pointer_pointer[] = { - {'-', &type_integer, &type_integer, &type_integer}, - {EQ, &type_integer}, - {NE, &type_integer}, - {LE, &type_integer}, - {GE, &type_integer}, - {LT, &type_integer}, - {GT, &type_integer}, + {'-', 0, 0, 0, pointer_arithmetic}, + {EQ, 0, 0, 0, pointer_compare}, + {NE, 0, 0, 0, pointer_compare}, + {LE, 0, 0, 0, pointer_compare}, + {GE, 0, 0, 0, pointer_compare}, + {LT, 0, 0, 0, pointer_compare}, + {GT, 0, 0, 0, pointer_compare}, {0, 0} }; -static expr_type_t pointer_integer[] = { - {'+', &type_pointer}, - {'-', &type_pointer}, +static expr_type_t pointer_int[] = { + {'+', 0, 0, 0, pointer_arithmetic}, + {'-', 0, 0, 0, pointer_arithmetic}, {0, 0} }; -#define pointer_uinteger pointer_integer -#define pointer_short pointer_integer +#define pointer_uint pointer_int +#define pointer_short pointer_int static expr_type_t quat_float[] = { {'*', &type_quaternion}, - {'/', &type_quaternion}, + {'/', 0, 0, 0, inverse_multiply}, {0, 0} }; @@ -179,20 +219,26 @@ static expr_type_t quat_quat[] = { {'+', &type_quaternion}, {'-', &type_quaternion}, {'*', &type_quaternion}, - {EQ, &type_integer}, - {NE, &type_integer}, + {EQ, &type_int}, + {NE, &type_int}, {0, 0} }; -static expr_type_t quat_integer[] = { +static expr_type_t quat_int[] = { {'*', &type_quaternion, 0, &type_float}, - {'/', &type_quaternion, 0, &type_float}, + {'/', 0, 0, 0, inverse_multiply}, {0, 0} }; -#define quat_uinteger quat_integer -#define quat_short quat_integer +#define quat_uint quat_int +#define quat_short quat_int -static expr_type_t integer_float[] = { +static expr_type_t quat_double[] = { + {'*', &type_quaternion}, + {'/', 0, 0, 0, inverse_multiply}, + {0, 0} +}; + +static expr_type_t int_float[] = { {'+', &type_float, &type_float, 0}, {'-', &type_float, &type_float, 0}, {'*', &type_float, &type_float, 0}, @@ -201,181 +247,207 @@ static expr_type_t integer_float[] = { {'|', &type_float, &type_float, 0}, {'^', &type_float, &type_float, 0}, {'%', &type_float, &type_float, 0}, - {SHL, &type_integer, 0, &type_integer}, //FIXME? - {SHR, &type_integer, 0, &type_integer}, //FIXME? - {EQ, &type_integer, &type_float, 0}, - {NE, &type_integer, &type_float, 0}, - {LE, &type_integer, &type_float, 0}, - {GE, &type_integer, &type_float, 0}, - {LT, &type_integer, &type_float, 0}, - {GT, &type_integer, &type_float, 0}, + {MOD, &type_float, &type_float, 0}, + {SHL, &type_int, 0, &type_int}, //FIXME? + {SHR, &type_int, 0, &type_int}, //FIXME? + {EQ, &type_int, &type_float, 0}, + {NE, &type_int, &type_float, 0}, + {LE, &type_int, &type_float, 0}, + {GE, &type_int, &type_float, 0}, + {LT, &type_int, &type_float, 0}, + {GT, &type_int, &type_float, 0}, {0, 0} }; -static expr_type_t integer_vector[] = { - {'*', &type_vector, &type_float, 0}, +static expr_type_t int_vector[] = { + {'*', &type_vector, &type_float, 0, vector_scale}, {0, 0} }; -static expr_type_t integer_pointer[] = { - {'+', &type_pointer}, +static expr_type_t int_pointer[] = { + {'+', 0, 0, 0, pointer_arithmetic}, {0, 0} }; -static expr_type_t integer_quat[] = { +static expr_type_t int_quat[] = { {'*', &type_quaternion, &type_float, 0}, - {'/', &type_quaternion, &type_float, 0}, {0, 0} }; -static expr_type_t integer_integer[] = { - {'+', &type_integer}, - {'-', &type_integer}, - {'*', &type_integer}, - {'/', &type_integer}, - {'&', &type_integer}, - {'|', &type_integer}, - {'^', &type_integer}, - {'%', &type_integer}, - {SHL, &type_integer}, - {SHR, &type_integer}, - {EQ, &type_integer}, - {NE, &type_integer}, - {LE, &type_integer}, - {GE, &type_integer}, - {LT, &type_integer}, - {GT, &type_integer}, +static expr_type_t int_int[] = { + {'+', &type_int}, + {'-', &type_int}, + {'*', &type_int}, + {'/', &type_int}, + {'&', &type_int}, + {'|', &type_int}, + {'^', &type_int}, + {'%', &type_int}, + {MOD, &type_int}, + {SHL, &type_int}, + {SHR, &type_int}, + {AND, &type_int}, + {OR, &type_int}, + {EQ, &type_int}, + {NE, &type_int}, + {LE, &type_int}, + {GE, &type_int}, + {LT, &type_int}, + {GT, &type_int}, {0, 0} }; -static expr_type_t integer_uinteger[] = { - {'+', &type_integer}, - {'-', &type_integer}, - {'*', &type_integer}, - {'/', &type_integer}, - {'&', &type_integer}, - {'|', &type_integer}, - {'^', &type_integer}, - {'%', &type_integer}, - {SHL, &type_integer}, - {SHR, &type_integer}, - {EQ, &type_integer}, - {NE, &type_integer}, - {LE, &type_integer}, - {GE, &type_integer}, - {LT, &type_integer}, - {GT, &type_integer}, +static expr_type_t int_uint[] = { + {'+', &type_int, 0, &type_int}, + {'-', &type_int, 0, &type_int}, + {'*', &type_int, 0, &type_int}, + {'/', &type_int, 0, &type_int}, + {'&', &type_int, 0, &type_int}, + {'|', &type_int, 0, &type_int}, + {'^', &type_int, 0, &type_int}, + {'%', &type_int, 0, &type_int}, + {MOD, &type_int, 0, &type_int}, + {SHL, &type_int, 0, &type_int}, + {SHR, &type_int, 0, &type_int}, + {EQ, &type_int, 0, &type_int}, + {NE, &type_int, 0, &type_int}, + {LE, &type_int, 0, &type_int}, + {GE, &type_int, 0, &type_int}, + {LT, &type_int, 0, &type_int}, + {GT, &type_int, 0, &type_int}, {0, 0} }; -static expr_type_t integer_short[] = { - {'+', &type_integer}, - {'-', &type_integer}, - {'*', &type_integer}, - {'/', &type_integer}, - {'&', &type_integer}, - {'|', &type_integer}, - {'^', &type_integer}, - {'%', &type_integer}, - {SHL, &type_integer}, - {SHR, &type_integer}, - {EQ, &type_integer}, - {NE, &type_integer}, - {LE, &type_integer}, - {GE, &type_integer}, - {LT, &type_integer}, - {GT, &type_integer}, +static expr_type_t int_short[] = { + {'+', &type_int, 0, &type_int}, + {'-', &type_int, 0, &type_int}, + {'*', &type_int, 0, &type_int}, + {'/', &type_int, 0, &type_int}, + {'&', &type_int, 0, &type_int}, + {'|', &type_int, 0, &type_int}, + {'^', &type_int, 0, &type_int}, + {'%', &type_int, 0, &type_int}, + {MOD, &type_int, 0, &type_int}, + {SHL, &type_int, 0, &type_int}, + {SHR, &type_int, 0, &type_int}, + {EQ, &type_int, 0, &type_int}, + {NE, &type_int, 0, &type_int}, + {LE, &type_int, 0, &type_int}, + {GE, &type_int, 0, &type_int}, + {LT, &type_int, 0, &type_int}, + {GT, &type_int, 0, &type_int}, {0, 0} }; -#define uinteger_float integer_float -#define uinteger_vector integer_vector -#define uinteger_pointer integer_pointer -#define uinteger_quat integer_quat - -static expr_type_t uinteger_integer[] = { - {'+', &type_integer}, - {'-', &type_integer}, - {'*', &type_integer}, - {'/', &type_integer}, - {'&', &type_integer}, - {'|', &type_integer}, - {'^', &type_integer}, - {'%', &type_integer}, - {SHL, &type_uinteger}, - {SHR, &type_uinteger}, - {EQ, &type_integer}, - {NE, &type_integer}, - {LE, &type_integer}, - {GE, &type_integer}, - {LT, &type_integer}, - {GT, &type_integer}, +static expr_type_t int_double[] = { + {'+', &type_double, &type_double, 0}, + {'-', &type_double, &type_double, 0}, + {'*', &type_double, &type_double, 0}, + {'/', &type_double, &type_double, 0}, + {'%', &type_double, &type_double, 0}, + {MOD, &type_double, &type_double, 0}, + {EQ, &type_int, &type_double, 0}, + {NE, &type_int, &type_double, 0}, + {LE, &type_int, &type_double, 0}, + {GE, &type_int, &type_double, 0}, + {LT, &type_int, &type_double, 0}, + {GT, &type_int, &type_double, 0}, {0, 0} }; -static expr_type_t uinteger_uinteger[] = { - {'+', &type_uinteger}, - {'-', &type_uinteger}, - {'*', &type_uinteger}, - {'/', &type_uinteger}, - {'&', &type_uinteger}, - {'|', &type_uinteger}, - {'^', &type_uinteger}, - {'%', &type_uinteger}, - {SHL, &type_uinteger}, - {SHR, &type_uinteger}, - {EQ, &type_integer}, - {NE, &type_integer}, - {LE, &type_integer}, - {GE, &type_integer}, - {LT, &type_integer}, - {GT, &type_integer}, +#define uint_float int_float +#define uint_vector int_vector +#define uint_pointer int_pointer +#define uint_quat int_quat + +static expr_type_t uint_int[] = { + {'+', &type_int, &type_int, &type_int }, + {'-', &type_int, &type_int, &type_int }, + {'*', &type_int, &type_int, &type_int }, + {'/', &type_int, &type_int, &type_int }, + {'&', &type_int, &type_int, &type_int }, + {'|', &type_int, &type_int, &type_int }, + {'^', &type_int, &type_int, &type_int }, + {'%', &type_int, &type_int, &type_int }, + {MOD, &type_int, &type_int, &type_int }, + {SHL, &type_uint, &type_int, &type_int }, + {SHR, &type_uint, 0, &type_int }, + {EQ, &type_int, &type_int, &type_int }, + {NE, &type_int, &type_int, &type_int }, + {LE, &type_int, &type_int, &type_int }, + {GE, &type_int, &type_int, &type_int }, + {LT, &type_int, &type_int, &type_int }, + {GT, &type_int, &type_int, &type_int }, {0, 0} }; -#define uinteger_short uinteger_integer -#define short_float integer_float -#define short_vector integer_vector -#define short_pointer integer_pointer -#define short_quat integer_quat +static expr_type_t uint_uint[] = { + {'+', &type_uint}, + {'-', &type_uint}, + {'*', &type_uint}, + {'/', &type_uint}, + {'&', &type_uint}, + {'|', &type_uint}, + {'^', &type_uint}, + {'%', &type_uint}, + {MOD, &type_uint}, + {SHL, &type_uint}, + {SHR, &type_uint}, + {EQ, &type_int, &type_int, &type_int}, + {NE, &type_int, &type_int, &type_int}, + {LE, &type_int}, + {GE, &type_int}, + {LT, &type_int}, + {GT, &type_int}, + {0, 0} +}; +#define uint_short uint_int +#define uint_double int_double -static expr_type_t short_integer[] = { - {'+', &type_integer}, - {'-', &type_integer}, - {'*', &type_integer}, - {'/', &type_integer}, - {'&', &type_integer}, - {'|', &type_integer}, - {'^', &type_integer}, - {'%', &type_integer}, +#define short_float int_float +#define short_vector int_vector +#define short_pointer int_pointer +#define short_quat int_quat + +static expr_type_t short_int[] = { + {'+', &type_int, &type_int, 0}, + {'-', &type_int, &type_int, 0}, + {'*', &type_int, &type_int, 0}, + {'/', &type_int, &type_int, 0}, + {'&', &type_int, &type_int, 0}, + {'|', &type_int, &type_int, 0}, + {'^', &type_int, &type_int, 0}, + {'%', &type_int, &type_int, 0}, + {MOD, &type_int, &type_int, 0}, {SHL, &type_short}, {SHR, &type_short}, - {EQ, &type_integer}, - {NE, &type_integer}, - {LE, &type_integer}, - {GE, &type_integer}, - {LT, &type_integer}, - {GT, &type_integer}, + {EQ, &type_int, &type_int, 0}, + {NE, &type_int, &type_int, 0}, + {LE, &type_int, &type_int, 0}, + {GE, &type_int, &type_int, 0}, + {LT, &type_int, &type_int, 0}, + {GT, &type_int, &type_int, 0}, {0, 0} }; -static expr_type_t short_uinteger[] = { - {'+', &type_uinteger}, - {'-', &type_uinteger}, - {'*', &type_uinteger}, - {'/', &type_uinteger}, - {'&', &type_uinteger}, - {'|', &type_uinteger}, - {'^', &type_uinteger}, - {'%', &type_uinteger}, +static expr_type_t short_uint[] = { + {'+', &type_uint, &type_uint, 0}, + {'-', &type_uint, &type_uint, 0}, + {'*', &type_uint, &type_uint, 0}, + {'/', &type_uint, &type_uint, 0}, + {'&', &type_uint, &type_uint, 0}, + {'|', &type_uint, &type_uint, 0}, + {'^', &type_uint, &type_uint, 0}, + {'%', &type_uint, &type_uint, 0}, + {MOD, &type_uint, &type_uint, 0}, {SHL, &type_short}, {SHR, &type_short}, - {EQ, &type_integer}, - {NE, &type_integer}, - {LE, &type_integer}, - {GE, &type_integer}, - {LT, &type_integer}, - {GT, &type_integer}, + {EQ, &type_int, &type_uint, 0}, + {NE, &type_int, &type_uint, 0}, + {LE, &type_int, &type_uint, 0}, + {GE, &type_int, &type_uint, 0}, + {LT, &type_int, &type_uint, 0}, + {GT, &type_int, &type_uint, 0}, {0, 0} }; @@ -388,205 +460,473 @@ static expr_type_t short_short[] = { {'|', &type_short}, {'^', &type_short}, {'%', &type_short}, + {MOD, &type_short}, {SHL, &type_short}, {SHR, &type_short}, - {EQ, &type_integer}, - {NE, &type_integer}, - {LE, &type_integer}, - {GE, &type_integer}, - {LT, &type_integer}, - {GT, &type_integer}, + {EQ, &type_int}, + {NE, &type_int}, + {LE, &type_int}, + {GE, &type_int}, + {LT, &type_int}, + {GT, &type_int}, + {0, 0} +}; +#define short_double int_double + +static expr_type_t double_float[] = { + {'+', &type_double, 0, &type_double}, + {'-', &type_double, 0, &type_double}, + {'*', &type_double, 0, &type_double}, + {'/', &type_double, 0, &type_double}, + {'%', &type_double, 0, &type_double}, + {MOD, &type_double, 0, &type_double}, + {EQ, 0, 0, 0, double_compare}, + {NE, 0, 0, 0, double_compare}, + {LE, 0, 0, 0, double_compare}, + {GE, 0, 0, 0, double_compare}, + {LT, 0, 0, 0, double_compare}, + {GT, 0, 0, 0, double_compare}, {0, 0} }; -static expr_type_t *string_x[] = { - 0, // ev_void - string_string, - 0, // ev_float - 0, // ev_vector - 0, // ev_entity - 0, // ev_field - 0, // ev_func - 0, // ev_pointer - 0, // ev_quat - 0, // ev_integer - 0, // ev_uinteger - 0, // ev_short +static expr_type_t double_vector[] = { + {'*', &type_vector, &type_float, 0, vector_scale}, + {0, 0} }; -static expr_type_t *float_x[] = { - 0, // ev_void - 0, // ev_string - float_float, - float_vector, - 0, // ev_entity - 0, // ev_field - 0, // ev_func - 0, // ev_pointer - float_quat, - float_integer, - float_uinteger, - float_short, +static expr_type_t double_quat[] = { + {'*', &type_quaternion}, + {0, 0} }; -static expr_type_t *vector_x[] = { - 0, // ev_void - 0, // ev_string - vector_float, - vector_vector, - 0, // ev_entity - 0, // ev_field - 0, // ev_func - 0, // ev_pointer - 0, // ev_quaternion - vector_integer, - vector_uinteger, - vector_short, +static expr_type_t double_int[] = { + {'+', &type_double, 0, &type_double}, + {'-', &type_double, 0, &type_double}, + {'*', &type_double, 0, &type_double}, + {'/', &type_double, 0, &type_double}, + {'%', &type_double, 0, &type_double}, + {MOD, &type_double, 0, &type_double}, + {EQ, 0, 0, 0, double_compare}, + {NE, 0, 0, 0, double_compare}, + {LE, 0, 0, 0, double_compare}, + {GE, 0, 0, 0, double_compare}, + {LT, 0, 0, 0, double_compare}, + {GT, 0, 0, 0, double_compare}, + {0, 0} +}; +#define double_uint double_int +#define double_short double_int + +static expr_type_t double_double[] = { + {'+', &type_double}, + {'-', &type_double}, + {'*', &type_double}, + {'/', &type_double}, + {'%', &type_double}, + {MOD, &type_double}, + {EQ, &type_int}, + {NE, &type_int}, + {LE, &type_int}, + {GE, &type_int}, + {LT, &type_int}, + {GT, &type_int}, + {0, 0} }; -static expr_type_t *entity_x[] = { - 0, // ev_void - 0, // ev_string - 0, // ev_float - 0, // ev_vector - entity_entity, // ev_entity - 0, // ev_field - 0, // ev_func - 0, // ev_pointer - 0, // ev_quaternion - 0, // ev_integer - 0, // ev_uinteger - 0, // ev_short +static expr_type_t long_long[] = { + {'+', &type_long}, + {'-', &type_long}, + {'*', &type_long}, + {'/', &type_long}, + {'&', &type_long}, + {'|', &type_long}, + {'^', &type_long}, + {'%', &type_long}, + {MOD, &type_long}, + {SHL, &type_long}, + {SHR, &type_long}, + {EQ, &type_int}, + {NE, &type_int}, + {LE, &type_int}, + {GE, &type_int}, + {LT, &type_int}, + {GT, &type_int}, + {0, 0} }; -static expr_type_t *field_x[] = { - 0, // ev_void - 0, // ev_string - 0, // ev_float - 0, // ev_vector - 0, // ev_entity - field_field, // ev_field - 0, // ev_func - 0, // ev_pointer - 0, // ev_quaternion - 0, // ev_integer - 0, // ev_uinteger - 0, // ev_short +static expr_type_t ulong_ulong[] = { + {'+', &type_long}, + {'-', &type_long}, + {'*', &type_long}, + {'/', &type_long}, + {'&', &type_long}, + {'|', &type_long}, + {'^', &type_long}, + {'%', &type_long}, + {MOD, &type_long}, + {SHL, &type_long}, + {SHR, &type_long}, + {EQ, &type_int}, + {NE, &type_int}, + {LE, &type_int}, + {GE, &type_int}, + {LT, &type_int}, + {GT, &type_int}, + {0, 0} }; -static expr_type_t *funcx[] = { - 0, // ev_void - 0, // ev_string - 0, // ev_float - 0, // ev_vector - 0, // ev_entity - 0, // ev_field - func_func, // ev_func - 0, // ev_pointer - 0, // ev_quaternion - 0, // ev_integer - 0, // ev_uinteger - 0, // ev_short +static expr_type_t *string_x[ev_type_count] = { + [ev_string] = string_string, }; -static expr_type_t *pointer_x[] = { - 0, // ev_void - 0, // ev_string - 0, // ev_float - 0, // ev_vector - 0, // ev_entity - 0, // ev_field - 0, // ev_func - pointer_pointer, - 0, // ev_quat - pointer_integer, - pointer_uinteger, - pointer_short, +static expr_type_t *float_x[ev_type_count] = { + [ev_float] = float_float, + [ev_vector] = float_vector, + [ev_quaternion] = float_quat, + [ev_int] = float_int, + [ev_uint] = float_uint, + [ev_short] = float_short, + [ev_double] = float_double, }; -static expr_type_t *quat_x[] = { - 0, // ev_void - 0, // ev_string - quat_float, - quat_vector, - 0, // ev_entity - 0, // ev_field - 0, // ev_func - 0, // ev_pointer - quat_quat, - quat_integer, - quat_uinteger, - quat_short, +static expr_type_t *vector_x[ev_type_count] = { + [ev_float] = vector_float, + [ev_vector] = vector_vector, + [ev_int] = vector_int, + [ev_uint] = vector_uint, + [ev_short] = vector_short, + [ev_double] = vector_double, }; -static expr_type_t *integer_x[] = { - 0, // ev_void - 0, // ev_string - integer_float, - integer_vector, - 0, // ev_entity - 0, // ev_field - 0, // ev_func - integer_pointer, - integer_quat, - integer_integer, - integer_uinteger, - integer_short, +static expr_type_t *entity_x[ev_type_count] = { + [ev_entity] = entity_entity, }; -static expr_type_t *uinteger_x[] = { - 0, // ev_void - 0, // ev_string - uinteger_float, - uinteger_vector, - 0, // ev_entity - 0, // ev_field - 0, // ev_func - uinteger_pointer, - uinteger_quat, - uinteger_integer, - uinteger_uinteger, - uinteger_short, +static expr_type_t *field_x[ev_type_count] = { + [ev_field] = field_field, }; -static expr_type_t *short_x[] = { - 0, // ev_void - 0, // ev_string - short_float, - short_vector, - 0, // ev_entity - 0, // ev_field - 0, // ev_func - short_pointer, - short_quat, - short_integer, - short_uinteger, - short_short, +static expr_type_t *func_x[ev_type_count] = { + [ev_func] = func_func, }; -static expr_type_t **binary_expr_types[] = { - 0, // ev_void - string_x, - float_x, - vector_x, - entity_x, - field_x, - funcx, - pointer_x, - quat_x, - integer_x, - uinteger_x, - short_x, +static expr_type_t *pointer_x[ev_type_count] = { + [ev_ptr] = pointer_pointer, + [ev_int] = pointer_int, + [ev_uint] = pointer_uint, + [ev_short] = pointer_short, }; +static expr_type_t *quat_x[ev_type_count] = { + [ev_float] = quat_float, + [ev_vector] = quat_vector, + [ev_quaternion] = quat_quat, + [ev_int] = quat_int, + [ev_uint] = quat_uint, + [ev_short] = quat_short, + [ev_double] = quat_double, +}; + +static expr_type_t *int_x[ev_type_count] = { + [ev_float] = int_float, + [ev_vector] = int_vector, + [ev_ptr] = int_pointer, + [ev_quaternion] = int_quat, + [ev_int] = int_int, + [ev_uint] = int_uint, + [ev_short] = int_short, + [ev_double] = int_double, +}; + +static expr_type_t *uint_x[ev_type_count] = { + [ev_float] = uint_float, + [ev_vector] = uint_vector, + [ev_ptr] = uint_pointer, + [ev_quaternion] = uint_quat, + [ev_int] = uint_int, + [ev_uint] = uint_uint, + [ev_short] = uint_short, + [ev_double] = uint_double, +}; + +static expr_type_t *short_x[ev_type_count] = { + [ev_float] = short_float, + [ev_vector] = short_vector, + [ev_ptr] = short_pointer, + [ev_quaternion] = short_quat, + [ev_int] = short_int, + [ev_uint] = short_uint, + [ev_short] = short_short, + [ev_double] = short_double, +}; + +static expr_type_t *double_x[ev_type_count] = { + [ev_float] = double_float, + [ev_vector] = double_vector, + [ev_quaternion] = double_quat, + [ev_int] = double_int, + [ev_uint] = double_uint, + [ev_short] = double_short, + [ev_double] = double_double, +}; + +static expr_type_t *long_x[ev_type_count] = { + [ev_long] = long_long, +}; + +static expr_type_t *ulong_x[ev_type_count] = { + [ev_ulong] = ulong_ulong, +}; + +static expr_type_t **binary_expr_types[ev_type_count] = { + [ev_string] = string_x, + [ev_float] = float_x, + [ev_vector] = vector_x, + [ev_entity] = entity_x, + [ev_field] = field_x, + [ev_func] = func_x, + [ev_ptr] = pointer_x, + [ev_quaternion] = quat_x, + [ev_int] = int_x, + [ev_uint] = uint_x, + [ev_short] = short_x, + [ev_double] = double_x, + [ev_long] = long_x, + [ev_ulong] = ulong_x, +}; + +// supported operators for scalar-vector expressions +static int scalar_vec_ops[] = { '*', '/', '%', MOD, 0 }; +static expr_t * +convert_scalar (expr_t *scalar, int op, expr_t *vec) +{ + int *s_op = scalar_vec_ops; + while (*s_op && *s_op != op) { + s_op++; + } + if (!*s_op) { + return 0; + } + + // expand the scalar to a vector of the same width as vec + type_t *vec_type = get_type (vec); + + if (is_constant (scalar)) { + for (int i = 1; i < type_width (get_type (vec)); i++) { + expr_t *s = copy_expr (scalar); + s->next = scalar; + scalar = s; + } + return new_vector_list (scalar); + } + + return new_extend_expr (scalar, vec_type, 2);//2 = copy +} + static expr_t * -invalid_binary_expr (int op, expr_t *e1, expr_t *e2) +pointer_arithmetic (int op, expr_t *e1, expr_t *e2) { - etype_t t1, t2; - t1 = extract_type (e1); - t2 = extract_type (e2); - return error (e1, "invalid binary expression: %s %s %s", - pr_type_name[t1], get_op_string (op), pr_type_name[t2]); + type_t *t1 = get_type (e1); + type_t *t2 = get_type (e2); + expr_t *ptr = 0; + expr_t *offset = 0; + expr_t *psize; + type_t *ptype = 0; + + if (!is_ptr (t1) && !is_ptr (t2)) { + internal_error (e1, "pointer arithmetic on non-pointers"); + } + if (is_ptr (t1) && is_ptr (t2)) { + if (op != '-') { + return error (e2, "invalid pointer operation"); + } + if (t1 != t2) { + return error (e2, "cannot use %c on pointers of different types", + op); + } + e1 = cast_expr (&type_int, e1); + e2 = cast_expr (&type_int, e2); + psize = new_int_expr (type_size (t1->t.fldptr.type)); + return binary_expr ('/', binary_expr ('-', e1, e2), psize); + } else if (is_ptr (t1)) { + offset = cast_expr (&type_int, e2); + ptr = e1; + ptype = t1; + } else if (is_ptr (t2)) { + offset = cast_expr (&type_int, e1); + ptr = e2; + ptype = t2; + } + // op is known to be + or - + psize = new_int_expr (type_size (ptype->t.fldptr.type)); + offset = unary_expr (op, binary_expr ('*', offset, psize)); + return offset_pointer_expr (ptr, offset); +} + +static expr_t * +pointer_compare (int op, expr_t *e1, expr_t *e2) +{ + type_t *t1 = get_type (e1); + type_t *t2 = get_type (e2); + expr_t *e; + + if (!type_assignable (t1, t2)) { + return error (e2, "cannot use %s on pointers of different types", + get_op_string (op)); + } + if (options.code.progsversion < PROG_VERSION) { + e = new_binary_expr (op, e1, e2); + } else { + e = new_binary_expr (op, cast_expr (&type_int, e1), + cast_expr (&type_int, e2)); + } + e->e.expr.type = &type_int; + return e; +} + +static expr_t * +func_compare (int op, expr_t *e1, expr_t *e2) +{ + expr_t *e; + + if (options.code.progsversion < PROG_VERSION) { + e = new_binary_expr (op, e1, e2); + } else { + e = new_binary_expr (op, new_alias_expr (&type_int, e1), + new_alias_expr (&type_int, e2)); + } + e->e.expr.type = &type_int; + if (options.code.progsversion == PROG_ID_VERSION) { + e->e.expr.type = &type_float; + } + return e; +} + +static expr_t * +inverse_multiply (int op, expr_t *e1, expr_t *e2) +{ + // There is no vector/float or quaternion/float instruction and adding + // one would mean the engine would have to do 1/f every time + expr_t *one = new_float_expr (1); + return binary_expr ('*', e1, binary_expr ('/', one, e2)); +} + +static expr_t * +vector_compare (int op, expr_t *e1, expr_t *e2) +{ + if (options.code.progsversion < PROG_VERSION) { + expr_t *e = new_binary_expr (op, e1, e2); + e->e.expr.type = &type_int; + if (options.code.progsversion == PROG_ID_VERSION) { + e->e.expr.type = &type_float; + } + return e; + } + int hop = op == EQ ? '&' : '|'; + e1 = new_alias_expr (&type_vec3, e1); + e2 = new_alias_expr (&type_vec3, e2); + expr_t *e = new_binary_expr (op, e1, e2); + e->e.expr.type = &type_ivec3; + return new_horizontal_expr (hop, e, &type_int); +} + +static expr_t *vector_multiply (int op, expr_t *e1, expr_t *e2) +{ + expr_t *e = new_binary_expr ('*', e1, e2); + if (options.math.vector_mult == DOT) { + // vector * vector is dot product in v6 progs (ick) + e->e.expr.op = DOT; + if (options.code.progsversion == PROG_VERSION) { + e->e.expr.type = &type_vector; + e = new_alias_expr (&type_float, e); + } else { + e->e.expr.type = &type_float; + } + } else { + // component-wise multiplication + e->e.expr.type = &type_vector; + } + + return e; +} + +static expr_t *vector_scale (int op, expr_t *e1, expr_t *e2) +{ + // Ensure the expression is always vector * scalar. The operation is + // always commutative, and the Ruamoko ISA supports only vector * scalar + // (though v6 does support scalar * vector, one less if). + if (is_scalar (get_type (e1))) { + expr_t *t = e1; + e1 = e2; + e2 = t; + } + expr_t *e = new_binary_expr (SCALE, e1, e2); + e->e.expr.type = get_type (e1); + return e; +} + +static expr_t * +double_compare (int op, expr_t *e1, expr_t *e2) +{ + type_t *t1 = get_type (e1); + type_t *t2 = get_type (e2); + expr_t *e; + + if (is_constant (e1) && e1->implicit && is_double (t1) && is_float (t2)) { + t1 = &type_float; + e1 = cast_expr (t1, e1); + } + if (is_float (t1) && is_constant (e2) && e2->implicit && is_double (t2)) { + t2 = &type_float; + e2 = cast_expr (t2, e2); + } + if (is_double (t1)) { + if (is_float (t2)) { + warning (e2, "comparison between double and float"); + } else if (!is_constant (e2)) { + warning (e2, "comparison between double and int"); + } + e2 = cast_expr (&type_double, e2); + } else if (is_double (t2)) { + if (is_float (t1)) { + warning (e1, "comparison between float and double"); + } else if (!is_constant (e1)) { + warning (e1, "comparison between int and double"); + } + e1 = cast_expr (&type_double, e1); + } + e = new_binary_expr (op, e1, e2); + e->e.expr.type = &type_int; + return e; +} + +static expr_t * +entity_compare (int op, expr_t *e1, expr_t *e2) +{ + if (options.code.progsversion == PROG_VERSION) { + e1 = new_alias_expr (&type_int, e1); + e2 = new_alias_expr (&type_int, e2); + } + expr_t *e = new_binary_expr (op, e1, e2); + e->e.expr.type = &type_int; + if (options.code.progsversion == PROG_ID_VERSION) { + e->e.expr.type = &type_float; + } + return e; +} + +#define invalid_binary_expr(_op, _e1, _e2) \ + _invalid_binary_expr(_op, _e1, _e2, __FILE__, __LINE__) +static expr_t * +_invalid_binary_expr (int op, expr_t *e1, expr_t *e2, + const char *file, int line) +{ + type_t *t1, *t2; + t1 = get_type (e1); + t2 = get_type (e2); + return _error (e1, file, line, "invalid binary expression: %s %s %s", + get_type_string (t1), get_op_string (op), + get_type_string (t2)); } static expr_t * @@ -598,32 +938,14 @@ reimplement_binary_expr (int op, expr_t *e1, expr_t *e2) switch (op) { case '%': { - expr_t *tmp1, *tmp2, *tmp3, *tmp4, *t1, *t2; + expr_t *tmp1, *tmp2; e = new_block_expr (); - t1 = new_temp_def_expr (&type_float); - t2 = new_temp_def_expr (&type_float); tmp1 = new_temp_def_expr (&type_float); tmp2 = new_temp_def_expr (&type_float); - tmp3 = new_temp_def_expr (&type_float); - tmp4 = new_temp_def_expr (&type_float); - append_expr (e, assign_expr (t1, e1)); - e1 = binary_expr ('&', t1, t1); - append_expr (e, assign_expr (tmp1, e1)); - - append_expr (e, assign_expr (t2, e2)); - e2 = binary_expr ('&', t2, t2); - append_expr (e, assign_expr (tmp2, e2)); - - e1 = binary_expr ('/', tmp1, tmp2); - append_expr (e, assign_expr (tmp3, e1)); - - e2 = binary_expr ('&', tmp3, tmp3); - append_expr (e, assign_expr (tmp4, e2)); - - e1 = binary_expr ('*', tmp2, tmp4); - e2 = binary_expr ('-', tmp1, e1); - e->e.block.result = e2; + append_expr (e, assign_expr (tmp1, binary_expr ('/', e1, e2))); + append_expr (e, assign_expr (tmp2, binary_expr ('&', tmp1, tmp1))); + e->e.block.result = binary_expr ('-', e1, binary_expr ('*', e2, tmp2)); return e; } break; @@ -655,7 +977,7 @@ check_precedence (int op, expr_t *e1, expr_t *e2) if (((op == '&' || op == '|') && (is_math_op (e2->e.expr.op) || is_compare (e2->e.expr.op))) || (op == '=' - && (e2->e.expr.op == OR || e2->e.expr.op == AND))) { + &&(e2->e.expr.op == OR || e2->e.expr.op == AND))) { notice (e1, "precedence of `%s' and `%s' inverted for " "traditional code", get_op_string (op), get_op_string (e2->e.expr.op)); @@ -677,7 +999,7 @@ check_precedence (int op, expr_t *e1, expr_t *e2) if (((op == '&' || op == '|') && (is_math_op (e1->e.expr.op) || is_compare (e1->e.expr.op))) || (op == '=' - && (e1->e.expr.op == OR || e1->e.expr.op == AND))) { + &&(e2->e.expr.op == OR || e2->e.expr.op == AND))) { notice (e1, "precedence of `%s' and `%s' inverted for " "traditional code", get_op_string (op), get_op_string (e1->e.expr.op)); @@ -717,6 +1039,20 @@ check_precedence (int op, expr_t *e1, expr_t *e2) return 0; } +static int is_call (expr_t *e) +{ + return e->type == ex_block && e->e.block.is_call; +} + +static type_t * +promote_type (type_t *dst, type_t *src) +{ + if (is_vector (dst) || is_quaternion (dst)) { + return dst; + } + return vector_type (base_type (dst), type_width (src)); +} + expr_t * binary_expr (int op, expr_t *e1, expr_t *e2) { @@ -726,10 +1062,24 @@ binary_expr (int op, expr_t *e1, expr_t *e2) expr_type_t *expr_type; convert_name (e1); + // FIXME this is target-specific info and should not be in the + // expression tree + if (e1->type == ex_alias && is_call (e1->e.alias.expr)) { + // move the alias expression inside the block so the following check + // can detect the call and move the temp assignment into the block + expr_t *block = e1->e.alias.expr; + e1->e.alias.expr = block->e.block.result; + block->e.block.result = e1; + e1 = block; + } if (e1->type == ex_block && e1->e.block.is_call && has_function_call (e2) && e1->e.block.result) { - e = new_temp_def_expr (get_type (e1->e.block.result)); - e1 = assign_expr (e, e1); + // the temp assignment needs to be insided the block so assignment + // code generation doesn't see it when applying right-associativity + expr_t *tmp = new_temp_def_expr (get_type (e1->e.block.result)); + e = assign_expr (tmp, e1->e.block.result); + append_expr (e1, e); + e1->e.block.result = tmp; } if (e1->type == ex_error) return e1; @@ -761,13 +1111,105 @@ binary_expr (int op, expr_t *e1, expr_t *e2) } } + if (is_constant (e1) && is_double (t1) && e1->implicit && is_float (t2)) { + t1 = &type_float; + e1 = cast_expr (t1, e1); + } + if (is_constant (e2) && is_double (t2) && e2->implicit && is_float (t1)) { + t2 = &type_float; + e2 = cast_expr (t2, e2); + } + if (is_array (t1) && (is_ptr (t2) || is_integral (t2))) { + t1 = pointer_type (t1->t.array.type); + e1 = cast_expr (t1, e1); + } + if (is_array (t2) && (is_ptr (t1) || is_integral (t1))) { + t2 = pointer_type (t2->t.array.type); + e2 = cast_expr (t2, e2); + } + et1 = low_level_type (t1); et2 = low_level_type (t2); - if (et1 > ev_short || !binary_expr_types[et1]) + if (et1 >= ev_type_count || !binary_expr_types[et1]) return invalid_binary_expr(op, e1, e2); - if (et2 > ev_short || !binary_expr_types[et1][et2]) + if (et2 >= ev_type_count || !binary_expr_types[et1][et2]) return invalid_binary_expr(op, e1, e2); + + if ((t1->width > 1 || t2->width > 1)) { + // vector/quaternion and scalar won't get here as vector and quaternion + // are distict types with type.width == 1, but vector and vec3 WILL get + // here because of vec3 being float{3} + if (t1 != t2) { + type_t *pt1 = t1; + type_t *pt2 = t2; + if (is_float (base_type (t1)) && is_double (base_type (t2)) + && e2->implicit) { + pt2 = promote_type (t1, t2); + } else if (is_double (base_type (t1)) && is_float (base_type (t2)) + && e1->implicit) { + pt1 = promote_type (t2, t1); + } else if (type_promotes (base_type (t1), base_type (t2))) { + pt2 = promote_type (t1, t2); + } else if (type_promotes (base_type (t2), base_type (t1))) { + pt1 = promote_type (t2, t1); + } else if (base_type (t1) == base_type (t2)) { + if (is_vector (t1) || is_quaternion (t1)) { + pt2 = t1; + } else if (is_vector (t2) || is_quaternion (t2)) { + pt1 = t2; + } + } else { + debug (e1, "%d %d\n", e1->implicit, e2->implicit); + return invalid_binary_expr (op, e1, e2); + } + if (pt1 != t1) { + e1 = cast_expr (pt1, e1); + t1 = pt1; + } + if (pt2 != t2) { + e2 = cast_expr (pt2, e2); + t2 = pt2; + } + } + int scalar_op = 0; + if (type_width (t1) == 1) { + // scalar op vec + if (!(e = convert_scalar (e1, op, e2))) { + return invalid_binary_expr (op, e1, e2); + } + scalar_op = 1; + e1 = e; + t1 = get_type (e1); + } + if (type_width (t2) == 1) { + // vec op scalar + if (!(e = convert_scalar (e2, op, e1))) { + return invalid_binary_expr (op, e1, e2); + } + scalar_op = 1; + e2 = e; + t2 = get_type (e2); + } + if (scalar_op && op == '*') { + op = HADAMARD; + } + if (type_width (t1) != type_width (t2)) { + // vec op vec of different widths + return invalid_binary_expr (op, e1, e2); + } + t1 = get_type (e1); + t2 = get_type (e2); + et1 = low_level_type (t1); + et2 = low_level_type (t2); + // both widths are the same at this point + if (t1->width > 1) { + e = new_binary_expr (op, e1, e2); + e->e.expr.type = t1; + return e; + } + } + expr_type = binary_expr_types[et1][et2]; while (expr_type->op && expr_type->op != op) expr_type++; @@ -778,15 +1220,19 @@ binary_expr (int op, expr_t *e1, expr_t *e2) e1 = cast_expr (expr_type->a_cast, e1); if (expr_type->b_cast) e2 = cast_expr (expr_type->b_cast, e2); + if (expr_type->process) { + return fold_constants (expr_type->process (op, e1, e2)); + } if ((e = reimplement_binary_expr (op, e1, e2))) - return e; + return fold_constants (e); e = new_binary_expr (op, e1, e2); - e->e.expr.type = expr_type->type; + e->e.expr.type = expr_type->result_type; if (is_compare (op) || is_logic (op)) { - if (options.code.progsversion == PROG_ID_VERSION) + if (options.code.progsversion == PROG_ID_VERSION) { e->e.expr.type = &type_float; + } } - return e; + return fold_constants (e); } diff --git a/tools/qfcc/source/expr_bool.c b/tools/qfcc/source/expr_bool.c new file mode 100644 index 000000000..52d377b75 --- /dev/null +++ b/tools/qfcc/source/expr_bool.c @@ -0,0 +1,324 @@ +/* + expr_bool.c + + short-circuit boolean expressions + + Copyright (C) 2001 Bill Currie + + Author: Bill Currie + Date: 2001/06/15 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include + +#include "QF/alloc.h" +#include "QF/dstring.h" +#include "QF/mathlib.h" +#include "QF/sys.h" +#include "QF/va.h" + +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/emit.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/idstuff.h" +#include "tools/qfcc/include/method.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/shared.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/struct.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" + +#include "tools/qfcc/source/qc-parse.h" + +expr_t * +test_expr (expr_t *e) +{ + static float zero[4] = {0, 0, 0, 0}; + expr_t *new = 0; + type_t *type; + + if (e->type == ex_error) + return e; + + type = get_type (e); + if (e->type == ex_error) + return e; + switch (type->type) { + case ev_type_count: + internal_error (e, 0); + case ev_void: + if (options.traditional) { + if (options.warnings.traditional) + warning (e, "void has no value"); + return e; + } + return error (e, "void has no value"); + case ev_string: + if (!options.code.ifstring) + return new_alias_expr (type_default, e); + new = new_string_expr (0); + break; + case ev_long: + case ev_ulong: + case ev_ushort: + internal_error (e, "long not implemented"); + case ev_uint: + case ev_int: + case ev_short: + if (!is_int(type_default)) { + if (is_constant (e)) { + return cast_expr (type_default, e); + } + return new_alias_expr (type_default, e); + } + return e; + case ev_float: + if (options.code.progsversion < PROG_VERSION + && (options.code.fast_float + || options.code.progsversion == PROG_ID_VERSION)) { + if (!is_float(type_default)) { + if (is_constant (e)) { + return cast_expr (type_default, e); + } + return new_alias_expr (type_default, e); + } + return e; + } + new = new_float_expr (0); + break; + case ev_double: + new = new_double_expr (0); + break; + case ev_vector: + new = new_vector_expr (zero); + break; + case ev_entity: + return new_alias_expr (type_default, e); + case ev_field: + return new_alias_expr (type_default, e); + case ev_func: + return new_alias_expr (type_default, e); + case ev_ptr: + return new_alias_expr (type_default, e); + case ev_quaternion: + new = new_quaternion_expr (zero); + break; + case ev_invalid: + if (is_enum (type)) { + new = new_nil_expr (); + break; + } + return test_error (e, get_type (e)); + } + new->line = e->line; + new->file = e->file; + new = binary_expr (NE, e, new); + new->line = e->line; + new->file = e->file; + return new; +} + +void +backpatch (ex_list_t *list, expr_t *label) +{ + int i; + expr_t *e; + + if (!list) + return; + if (!label || label->type != ex_label) + internal_error (label, "not a label"); + + for (i = 0; i < list->size; i++) { + e = list->e[i]; + if (e->type == ex_branch && e->e.branch.type < pr_branch_call) { + e->e.branch.target = label; + } else { + internal_error (e, 0); + } + label->e.label.used++; + } +} + +static ex_list_t * +merge (ex_list_t *l1, ex_list_t *l2) +{ + ex_list_t *m; + + if (!l1 && !l2) + internal_error (0, 0); + if (!l2) + return l1; + if (!l1) + return l2; + m = malloc ((size_t)&((ex_list_t *)0)->e[l1->size + l2->size]); + m->size = l1->size + l2->size; + memcpy (m->e, l1->e, l1->size * sizeof (expr_t *)); + memcpy (m->e + l1->size, l2->e, l2->size * sizeof (expr_t *)); + return m; +} + +static ex_list_t * +make_list (expr_t *e) +{ + ex_list_t *m; + + m = malloc ((size_t)&((ex_list_t *) 0)->e[1]); + m->size = 1; + m->e[0] = e; + return m; +} + +expr_t * +bool_expr (int op, expr_t *label, expr_t *e1, expr_t *e2) +{ + expr_t *block; + + if (!options.code.short_circuit) + return binary_expr (op, e1, e2); + + e1 = convert_bool (e1, 0); + if (e1->type == ex_error) + return e1; + + e2 = convert_bool (e2, 0); + if (e2->type == ex_error) + return e2; + + block = new_block_expr (); + append_expr (block, e1); + append_expr (block, label); + append_expr (block, e2); + + switch (op) { + case OR: + backpatch (e1->e.boolean.false_list, label); + return new_bool_expr (merge (e1->e.boolean.true_list, + e2->e.boolean.true_list), + e2->e.boolean.false_list, block); + break; + case AND: + backpatch (e1->e.boolean.true_list, label); + return new_bool_expr (e2->e.boolean.true_list, + merge (e1->e.boolean.false_list, + e2->e.boolean.false_list), block); + break; + } + internal_error (e1, 0); +} + +static int __attribute__((pure)) +has_block_expr (expr_t *e) +{ + while (e->type == ex_alias) { + e = e->e.alias.expr; + } + return e->type == ex_block; +} + +expr_t * +convert_bool (expr_t *e, int block) +{ + expr_t *b; + + if (e->type == ex_assign) { + expr_t *tst; + if (!e->paren && options.warnings.precedence) + warning (e, "suggest parentheses around assignment " + "used as truth value"); + tst = e->e.assign.src; + if (has_block_expr (tst) && has_block_expr (e->e.assign.dst)) { + tst = new_temp_def_expr (get_type (tst)); + e = new_assign_expr (e->e.assign.dst, + assign_expr (tst, e->e.assign.src)); + } else if (has_block_expr (tst)) { + tst = e->e.assign.dst; + } + b = convert_bool (tst, 1); + if (b->type == ex_error) + return b; + // insert the assignment into the boolean's block + e->next = b->e.boolean.e->e.block.head; + b->e.boolean.e->e.block.head = e; + if (b->e.boolean.e->e.block.tail == &b->e.boolean.e->e.block.head) { + // shouldn't happen, but just in case + b->e.boolean.e->e.block.tail = &e->next; + } + return b; + } + + if (e->type == ex_uexpr && e->e.expr.op == '!' + && !is_string(get_type (e->e.expr.e1))) { + e = convert_bool (e->e.expr.e1, 0); + if (e->type == ex_error) + return e; + e = unary_expr ('!', e); + } + if (e->type != ex_bool) { + e = test_expr (e); + if (e->type == ex_error) + return e; + if (is_constant (e)) { + int val; + + b = goto_expr (0); + if (is_int_val (e)) { + val = expr_int (e); + } else { + val = expr_float (e) != 0; + } + if (val) + e = new_bool_expr (make_list (b), 0, b); + else + e = new_bool_expr (0, make_list (b), b); + } else { + b = new_block_expr (); + append_expr (b, branch_expr (NE, e, 0)); + append_expr (b, goto_expr (0)); + e = new_bool_expr (make_list (b->e.block.head), + make_list (b->e.block.head->next), b); + } + } + if (block && e->e.boolean.e->type != ex_block) { + expr_t *block = new_block_expr (); + append_expr (block, e->e.boolean.e); + e->e.boolean.e = block; + } + return e; +} diff --git a/tools/qfcc/source/expr_cast.c b/tools/qfcc/source/expr_cast.c new file mode 100644 index 000000000..a02024c0c --- /dev/null +++ b/tools/qfcc/source/expr_cast.c @@ -0,0 +1,145 @@ +/* + expr_cast.c + + expression casting + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/04/27 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "QF/mathlib.h" + +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" + +static expr_t * +cast_error (expr_t *e, type_t *t1, type_t *t2) +{ + e = error (e, "cannot cast from %s to %s", get_type_string (t1), + get_type_string (t2)); + return e; +} + +static void +do_conversion (pr_type_t *dst_value, type_t *dstType, + pr_type_t *src_value, type_t *srcType, expr_t *expr) +{ + int from = type_cast_map[base_type (srcType)->type]; + int to = type_cast_map[base_type (dstType)->type]; + int width = type_width (srcType) - 1; + int conversion = TYPE_CAST_CODE (from, to, width); +#define OPA(type) (*((pr_##type##_t *) (src_value))) +#define OPC(type) (*((pr_##type##_t *) (dst_value))) + switch (conversion) { +#include "libs/gamecode/pr_convert.cinc" + default: + internal_error (expr, "invalid conversion code: %04o", conversion); + } +} + +static expr_t * +cast_math (type_t *dstType, type_t *srcType, expr_t *expr) +{ + pr_type_t src_value[type_size (srcType)]; + pr_type_t dst_value[type_size (dstType)]; + + value_store (src_value, srcType, expr); + + do_conversion (dst_value, dstType, src_value, srcType, expr); + + return new_value_expr (new_type_value (dstType, dst_value)); +} + +expr_t * +cast_expr (type_t *dstType, expr_t *e) +{ + expr_t *c; + type_t *srcType; + + convert_name (e); + + if (e->type == ex_error) + return e; + + dstType = (type_t *) unalias_type (dstType); //FIXME cast + srcType = get_type (e); + + if (dstType == srcType) + return e; + + if ((dstType == type_default && is_enum (srcType)) + || (is_enum (dstType) && srcType == type_default)) + return e; + if ((is_ptr (dstType) && is_string (srcType)) + || (is_string (dstType) && is_ptr (srcType))) { + c = new_alias_expr (dstType, e); + return c; + } + if (!(is_ptr (dstType) && (is_ptr (srcType) || is_integral (srcType) + || is_array (srcType))) + && !(is_integral (dstType) && is_ptr (srcType)) + && !(is_func (dstType) && is_func (srcType)) + && !(is_math (dstType) && is_math (srcType) + && type_width (dstType) == type_width (srcType)) + && !((is_int (dstType) || is_uint (dstType)) + && (is_short (srcType) || is_ushort (srcType)) + // [u]short is always width 0 + && type_width (dstType) == 1)) { + return cast_error (e, srcType, dstType); + } + if (is_array (srcType)) { + return address_expr (e, dstType->t.fldptr.type); + } + if (is_short (srcType)) { + e = new_int_expr (expr_short (e)); + srcType = &type_int; + } else if (is_ushort (srcType)) { + e = new_int_expr (expr_ushort (e)); + srcType = &type_int; + } + if (is_constant (e) && is_math (dstType) && is_math (srcType)) { + return cast_math (dstType, srcType, e); + } else if (is_integral (dstType) && is_integral (srcType)) { + c = new_alias_expr (dstType, e); + } else if (is_scalar (dstType) && is_scalar (srcType)) { + c = new_unary_expr ('C', e); + c->e.expr.type = dstType; + } else if (e->type == ex_uexpr && e->e.expr.op == '.') { + e->e.expr.type = dstType; + c = e; + } else { + c = new_alias_expr (dstType, e); + } + return c; +} diff --git a/tools/qfcc/source/expr_compound.c b/tools/qfcc/source/expr_compound.c new file mode 100644 index 000000000..df382809f --- /dev/null +++ b/tools/qfcc/source/expr_compound.c @@ -0,0 +1,351 @@ +/* + expr_compound.c + + compound intializer expression construction and manipulations + + Copyright (C) 2020 Bill Currie + + Author: Bill Currie + Date: 2020/03/11 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include + +#include "QF/alloc.h" +#include "QF/dstring.h" +#include "QF/mathlib.h" +#include "QF/set.h" +#include "QF/sys.h" +#include "QF/va.h" + +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" + +ALLOC_STATE (element_t, elements); +ALLOC_STATE (designator_t, designators); + +designator_t * +new_designator (expr_t *field, expr_t *index) +{ + if ((!field && !index) || (field && index)) { + internal_error (0, "exactly one of field or index is required"); + } + if (field && field->type != ex_symbol) { + internal_error (field, "invalid field designator"); + } + designator_t *des; + ALLOC (256, designator_t, designators, des); + des->field = field; + des->index = index; + return des; +} + +element_t * +new_element (expr_t *expr, designator_t *designator) +{ + element_t *element; + ALLOC (256, element_t, elements, element); + element->expr = expr; + element->designator = designator; + return element; +} + +static element_t * +append_init_element (element_chain_t *element_chain, element_t *element) +{ + element->next = 0; + *element_chain->tail = element; + element_chain->tail = &element->next; + return element; +} + +expr_t * +new_compound_init (void) +{ + expr_t *c = new_expr (); + c->type = ex_compound; + c->e.compound.head = 0; + c->e.compound.tail = &c->e.compound.head; + return c; +} + +static symbol_t * +designator_field (const designator_t *des, const type_t *type) +{ + if (des->index) { + error (des->index, "designator index in non-array"); + return 0; + } + symtab_t *symtab = type->t.symtab; + symbol_t *sym = des->field->e.symbol; + symbol_t *field = symtab_lookup (symtab, sym->name);; + if (!field) { + const char *name = type->name; + if (!strncmp (name, "tag ", 4)) { + name += 4; + } + error (des->field, "'%s' has no member named '%s'", name, sym->name); + return 0; + } + return field; +} + +static int +designator_index (const designator_t *des, int ele_size, int array_size) +{ + if (des->field) { + error (des->field, "field designator in array initializer"); + return -1; + } else if (!is_constant (des->index)) { + error (des->index, "non-constant designator index"); + return -1; + } else if (!is_integral (get_type (des->index))) { + error (des->index, "invalid designator index type"); + return -1; + } + int index = expr_integral (des->index); + if (index <= 0 || index >= array_size) { + error (des->index, "designator index out of bounds"); + return -1; + } + return index * ele_size; +} + +typedef struct { + type_t *type; + symbol_t *field; + int offset; +} initstate_t; + +static initstate_t +get_designated_offset (const type_t *type, const designator_t *des) +{ + int offset = -1; + type_t *ele_type = 0; + symbol_t *field = 0; + + if (is_struct (type) || is_union (type)) { + field = designator_field (des, type); + offset = field->s.offset; + ele_type = field->type; + } else if (is_array (type)) { + int array_size = type->t.array.size; + ele_type = type->t.array.type; + offset = designator_index (des, type_size (ele_type), array_size); + } else if (is_nonscalar (type)) { + ele_type = ev_types[type->type]; + if (type->t.symtab && des->field) { + field = designator_field (des, type); + offset = field->s.offset; + } else { + int vec_width = type_width (type); + offset = designator_index (des, type_size (ele_type), vec_width); + } + } else { + error (0, "invalid initializer"); + } + if (ele_type && des->next) { + __auto_type state = get_designated_offset (ele_type, des->next); + ele_type = state.type; + offset += state.offset; + } + return (initstate_t) { .type = ele_type, .field = field, .offset = offset}; +} + +static int +skip_field (symbol_t *field) +{ + if (field->sy_type != sy_var) { + return 1; + } + if (field->no_auto_init) { + return 1; + } + return 0; +} + +void +build_element_chain (element_chain_t *element_chain, const type_t *type, + expr_t *eles, int base_offset) +{ + element_t *ele = eles->e.compound.head; + + type = unalias_type (type); + + initstate_t state = {}; + if (is_struct (type) || is_union (type) + || (is_nonscalar (type) && type->t.symtab)) { + state.field = type->t.symtab->symbols; + while (skip_field (state.field)) { + state.field = state.field->next; + } + state.type = state.field->type; + state.offset = state.field->s.offset; + } else if (is_array (type)) { + state.type = type->t.array.type; + } else { + internal_error (eles, "invalid initialization"); + } + while (ele) { + if (ele->designator) { + state = get_designated_offset (type, ele->designator); + } + if (!state.type) { + break; + } + + if (state.offset >= type_size (type)) { + if (options.warnings.initializer) { + warning (eles, "excessive elements in initializer"); + } + break; + } + + if (ele->expr && ele->expr->type == ex_compound) { + build_element_chain (element_chain, state.type, ele->expr, + state.offset); + } else { + element_t *element = new_element (0, 0); + element->type = state.type; + element->offset = base_offset + state.offset; + element->expr = ele->expr; // null -> nil + append_init_element (element_chain, element); + } + + state.offset += type_size (state.type); + if (state.field) { + state.field = state.field->next; + while (state.field && skip_field (state.field)) { + state.field = state.field->next; + } + if (state.field) { + state.type = state.field->type; + state.offset = state.field->s.offset; + } + } + + ele = ele->next; + } +} + +void +free_element_chain (element_chain_t *element_chain) +{ + *element_chain->tail = elements_freelist; + elements_freelist = element_chain->head; + element_chain->head = 0; + element_chain->tail = &element_chain->head; +} + +expr_t * +append_element (expr_t *compound, element_t *element) +{ + if (compound->type != ex_compound) { + internal_error (compound, "not a compound expression"); + } + + if (!element || (element->expr && element->expr->type == ex_error)) { + return compound; + } + + if (element->next) { + internal_error (compound, "append_element: element loop detected"); + } + append_init_element (&compound->e.compound, element); + return compound; +} + +void +assign_elements (expr_t *local_expr, expr_t *init, + element_chain_t *element_chain) +{ + element_t *element; + type_t *init_type = get_type (init); + set_t *initialized = set_new_size (type_size (init_type)); + + for (element = element_chain->head; element; element = element->next) { + int offset = element->offset; + type_t *type = element->type; + expr_t *alias = new_offset_alias_expr (type, init, offset); + + expr_t *c; + + if (type_size (type) == 0) + internal_error (init, "wtf"); + if (element->expr) { + c = constant_expr (element->expr); + } else { + c = new_nil_expr (); + } + if (c->type == ex_nil) { + c = convert_nil (c, type); + } + append_expr (local_expr, assign_expr (alias, c)); + set_add_range (initialized, offset, type_size (type)); + } + + unsigned start = 0; + for (set_iter_t *in = set_first (initialized); in; in = set_next (in)) { + unsigned end = in->element; + if (end > start) { + expr_t *dst = new_offset_alias_expr (&type_int, init, start); + expr_t *zero = new_int_expr (0); + expr_t *count = new_int_expr (end - start); + append_expr (local_expr, new_memset_expr (dst, zero, count)); + } + // skip over all the initialized locations + in = set_while (in); + if (in) { + start = in->element; + } + } + set_delete (initialized); +} + +expr_t * +initialized_temp_expr (const type_t *type, expr_t *compound) +{ + type = unalias_type (type); + element_chain_t element_chain; + expr_t *temp = new_temp_def_expr (type); + expr_t *block = new_block_expr (); + + element_chain.head = 0; + element_chain.tail = &element_chain.head; + build_element_chain (&element_chain, type, compound, 0); + assign_elements (block, temp, &element_chain); + block->e.block.result = temp; + free_element_chain (&element_chain); + return block; +} diff --git a/tools/qfcc/source/expr_obj.c b/tools/qfcc/source/expr_obj.c new file mode 100644 index 000000000..b2463e4d1 --- /dev/null +++ b/tools/qfcc/source/expr_obj.c @@ -0,0 +1,257 @@ +/* + expr_obj.c + + Objective-QuakeC expression construction and manipulations + + Copyright (C) 2001 Bill Currie + + Author: Bill Currie + Date: 2001/06/15 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include + +#include "QF/alloc.h" +#include "QF/dstring.h" +#include "QF/mathlib.h" +#include "QF/sys.h" +#include "QF/va.h" + +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/emit.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/idstuff.h" +#include "tools/qfcc/include/method.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/shared.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/struct.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" + +expr_t * +new_self_expr (void) +{ + symbol_t *sym; + + sym = make_symbol (".self", &type_entity, pr.near_data, sc_extern); + if (!sym->table) + symtab_addsymbol (pr.symtab, sym); + return new_symbol_expr (sym); +} + +expr_t * +new_this_expr (void) +{ + symbol_t *sym; + + sym = make_symbol (".this", field_type (&type_id), pr.near_data, sc_extern); + if (!sym->table) + symtab_addsymbol (pr.symtab, sym); + return new_symbol_expr (sym); +} + +expr_t * +selector_expr (keywordarg_t *selector) +{ + dstring_t *sel_id = dstring_newstr (); + expr_t *sel_ref; + symbol_t *sel_sym; + symbol_t *sel_table; + int index; + + selector = copy_keywordargs (selector); + selector = (keywordarg_t *) reverse_params ((param_t *) selector); + selector_name (sel_id, selector); + index = selector_index (sel_id->str); + index *= type_size (type_SEL.t.fldptr.type); + sel_sym = make_symbol ("_OBJ_SELECTOR_TABLE_PTR", &type_SEL, + pr.near_data, sc_static); + if (!sel_sym->table) { + symtab_addsymbol (pr.symtab, sel_sym); + sel_table = make_symbol ("_OBJ_SELECTOR_TABLE", + array_type (type_SEL.t.fldptr.type, 0), + pr.far_data, sc_extern); + if (!sel_table->table) + symtab_addsymbol (pr.symtab, sel_table); + reloc_def_def (sel_table->s.def, sel_sym->s.def); + } + sel_ref = new_symbol_expr (sel_sym); + sel_ref = new_address_expr (&type_selector, sel_ref, + new_short_expr (index)); + + expr_t *sel = new_expr (); + sel->type = ex_selector; + sel->e.selector.sel_ref = sel_ref; + sel->e.selector.sel = get_selector (sel_ref); + dstring_delete (sel_id); + return sel; +} + +expr_t * +protocol_expr (const char *protocol_name) +{ + protocol_t *protocol = get_protocol (protocol_name, 0); + + if (!protocol) { + return error (0, "cannot find protocol declaration for `%s'", + protocol_name); + } + class_t *proto_class = get_class (new_symbol ("Protocol"), 1); + return new_pointer_expr (0, proto_class->type, protocol_def (protocol)); +} + +expr_t * +super_expr (class_type_t *class_type) +{ + symbol_t *sym; + expr_t *super; + expr_t *e; + expr_t *super_block; + class_t *class; + + if (!class_type) + return error (0, "`super' used outside of class implementation"); + + class = extract_class (class_type); + + if (!class->super_class) + return error (0, "%s has no super class", class->name); + + sym = symtab_lookup (current_symtab, ".super"); + if (!sym || sym->table != current_symtab) { + sym = new_symbol_type (".super", &type_super); + initialize_def (sym, 0, current_symtab->space, sc_local, + current_symtab); + } + super = new_symbol_expr (sym); + + super_block = new_block_expr (); + + e = assign_expr (field_expr (super, new_name_expr ("self")), + new_name_expr ("self")); + append_expr (super_block, e); + + e = new_symbol_expr (class_pointer_symbol (class)); + e = assign_expr (field_expr (super, new_name_expr ("class")), + field_expr (e, new_name_expr ("super_class"))); + append_expr (super_block, e); + + e = address_expr (super, 0); + super_block->e.block.result = e; + return super_block; +} + +expr_t * +message_expr (expr_t *receiver, keywordarg_t *message) +{ + expr_t *args = 0, **a = &args; + expr_t *selector = selector_expr (message); + expr_t *call; + keywordarg_t *m; + int super = 0, class_msg = 0; + type_t *rec_type = 0; + type_t *return_type; + type_t *method_type = &type_IMP; + method_t *method; + expr_t *send_msg; + + if (receiver->type == ex_nil) { + rec_type = &type_id; + convert_nil (receiver, rec_type); + } else if (receiver->type == ex_symbol) { + if (strcmp (receiver->e.symbol->name, "self") == 0) { + rec_type = get_type (receiver); + } else if (strcmp (receiver->e.symbol->name, "super") == 0) { + super = 1; + + receiver = super_expr (current_class); + + if (receiver->type == ex_error) + return receiver; + receiver = cast_expr (&type_id, receiver); //FIXME better way? + rec_type = extract_class (current_class)->type; + } else if (receiver->e.symbol->sy_type == sy_class) { + class_t *class; + rec_type = receiver->e.symbol->type; + class = rec_type->t.class; + class_msg = 1; + receiver = new_symbol_expr (class_pointer_symbol (class)); + } + } + if (!rec_type) { + rec_type = get_type (receiver); + } + + if (receiver->type == ex_error) + return receiver; + + return_type = &type_id; + method = class_message_response (rec_type, class_msg, selector); + if (method) + return_type = method->type->t.func.type; + + for (m = message; m; m = m->next) { + *a = m->expr; + while ((*a)) { + expr_file_line (selector, *a); + a = &(*a)->next; + } + } + *a = selector; + a = &(*a)->next; + *a = receiver; + + send_msg = expr_file_line (send_message (super), receiver); + if (method) { + expr_t *err; + if ((err = method_check_params (method, args))) + return err; + method_type = method->type; + } + call = build_function_call (send_msg, method_type, args); + + if (call->type == ex_error) + return receiver; + + if (!is_function_call (call)) { + internal_error (call, "unexpected call expression type"); + } + call->e.block.result->e.branch.ret_type = return_type; + return call; +} diff --git a/tools/qfcc/source/expr_vector.c b/tools/qfcc/source/expr_vector.c new file mode 100644 index 000000000..0efc2b063 --- /dev/null +++ b/tools/qfcc/source/expr_vector.c @@ -0,0 +1,172 @@ +/* + expr_vector.c + + vector expressions + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/04/27 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "QF/alloc.h" +#include "QF/dstring.h" +#include "QF/mathlib.h" +#include "QF/sys.h" +#include "QF/va.h" + +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/emit.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/idstuff.h" +#include "tools/qfcc/include/method.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/shared.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/struct.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" + +#include "tools/qfcc/source/qc-parse.h" + +expr_t * +new_vector_list (expr_t *expr_list) +{ + type_t *ele_type = type_default; + + // lists are built in reverse order + expr_list = reverse_expr_list (expr_list); + + int width = 0; + int count = 0; + for (expr_t *e = expr_list; e; e = e->next) { + count++; + type_t *t = get_type (e); + if (!t) { + return e; + } + if (!is_math (t)) { + return error (e, "invalid type for vector element"); + } + width += type_width (t); + if (is_nonscalar (t)) { + t = base_type (t); + } + if (type_promotes (t, ele_type)) { + ele_type = t; + } + } + if (width < 2) { + return error (expr_list, "not a vector"); + } + if (width > 4) { + return error (expr_list, "resulting vector is too large: %d elements", + width); + } + + int all_constant = 1; + int all_implicit = 1; + expr_t *elements[count + 1]; + elements[count] = 0; + count = 0; + for (expr_t *e = expr_list; e; e = e->next) { + int cast_width = type_width (get_type (e)); + type_t *cast_type = vector_type (ele_type, cast_width); + all_implicit = all_implicit && e->implicit; + elements[count] = cast_expr (cast_type, fold_constants (e)); + all_constant = all_constant && is_constant (elements[count]); + count++; + } + + switch (count) { + case 4: + // all scalars (otherwise width would be too large) + break; + case 3: + // shuffle any vectors to the beginning of the list (there should + // be only one, but futhre...) + for (int i = 1; i < count; i++) { + if (is_nonscalar (get_type (elements[i]))) { + expr_t *t = elements[i]; + int j = i; + for (; j > 0 && is_scalar (get_type (elements[j])); j--) { + elements[j] = elements[j - 1]; + } + elements[j] = t; + } + } + break; + case 2: + if (is_scalar (get_type (elements[0])) + && is_nonscalar (get_type (elements[1]))) { + // swap s, v to be v, s (ie, vector always comes before scalar) + expr_t *t = elements[0]; + elements[0] = elements[1]; + elements[1] = t; + } + break; + case 1: + if (is_scalar (get_type (elements[0]))) { + internal_error (expr_list, "confused about vectors"); + } + // it's already a vector + return elements[0]; + } + + if (all_constant) { + type_t *vec_type = vector_type (ele_type, width); + pr_type_t value[type_size (vec_type)]; + + for (int i = 0, offs = 0; i < count; i++) { + type_t *src_type = get_type (elements[i]); + value_store (value + offs, src_type, elements[i]); + offs += type_size (src_type); + } + + expr_t *vec = new_value_expr (new_type_value (vec_type, value)); + vec->implicit = all_implicit; + return vec; + } + + for (int i = 0; i < count; i++) { + elements[i]->next = elements[i + 1]; + } + + expr_t *vec = new_expr (); + vec->type = ex_vector; + vec->e.vector.type = vector_type (ele_type, width); + vec->e.vector.list = elements[0]; + return vec; +} diff --git a/tools/qfcc/source/flow.c b/tools/qfcc/source/flow.c index 2c6692f1d..e0d33346c 100644 --- a/tools/qfcc/source/flow.c +++ b/tools/qfcc/source/flow.c @@ -40,28 +40,28 @@ #include #include "QF/alloc.h" -#include "QF/dstring.h" +#include "QF/heapsort.h" #include "QF/set.h" #include "QF/va.h" -#include "dags.h" -#include "def.h" -#include "defspace.h" -#include "diagnostic.h" -#include "dot.h" -#include "flow.h" -#include "function.h" -#include "options.h" -#include "qfcc.h" -#include "statements.h" -#include "symtab.h" -#include "type.h" +#include "tools/qfcc/include/dags.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/dot.h" +#include "tools/qfcc/include/flow.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/statements.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" -static flowvar_t *vars_freelist; -static flowloop_t *loops_freelist; -static flownode_t *nodes_freelist; -static flowgraph_t *graphs_freelist; +/// \addtogroup qfcc_flow +///@{ +/** Static operand definitions for the ever present return and parameter slots. + */ static struct { const char *name; operand_t op; @@ -78,6 +78,17 @@ static struct { }; static const int num_flow_params = sizeof(flow_params)/sizeof(flow_params[0]); +/** \name Flow analysis memory management */ +///@{ +ALLOC_STATE (flowvar_t, vars); ///< flowvar pool +ALLOC_STATE (flowloop_t, loops); ///< flow loop pool +ALLOC_STATE (flownode_t, nodes); ///< flow node pool +ALLOC_STATE (flowgraph_t, graphs); ///< flow graph pool + +/** Allocate a new flow var. + * + * The var's use, define, udchain and duchain sets are initialized to empty. + */ static flowvar_t * new_flowvar (void) { @@ -85,9 +96,27 @@ new_flowvar (void) ALLOC (256, flowvar_t, vars, var); var->use = set_new (); var->define = set_new (); + var->udchains = set_new (); + var->duchains = set_new (); return var; } +/** Delete a flow var + */ +static void +delete_flowvar (flowvar_t *var) +{ + set_delete (var->use); + set_delete (var->define); + set_delete (var->udchains); + set_delete (var->duchains); + FREE (vars, var); +} + +/** Allocate a new flow loop. + * + * The loop's nodes set is initialized to the empty set. + */ static flowloop_t * new_loop (void) { @@ -97,6 +126,8 @@ new_loop (void) return loop; } +/** Free a flow loop and its nodes set. + */ static void delete_loop (flowloop_t *loop) { @@ -104,6 +135,10 @@ delete_loop (flowloop_t *loop) FREE (loops, loop); } +/** Allocate a new flow node. + * + * The node is completely empty. + */ static flownode_t * new_node (void) { @@ -112,6 +147,10 @@ new_node (void) return node; } +/** Free a flow node and its resources. + * + * \bug not global_vars or the vars and defs sets? + */ static void delete_node (flownode_t *node) { @@ -126,6 +165,10 @@ delete_node (flownode_t *node) FREE (nodes, node); } +/** Allocate a new flow graph. + * + * The graph is completely empty. + */ static flowgraph_t * new_graph (void) { @@ -134,6 +177,10 @@ new_graph (void) return graph; } +/** Return a flow graph and its resources to the pools. + * + * \bug except loops? + */ static void __attribute__((unused)) delete_graph (flowgraph_t *graph) { @@ -148,31 +195,23 @@ delete_graph (flowgraph_t *graph) free (graph->edges); if (graph->dfst) set_delete (graph->dfst); - if (graph->dfo) - free (graph->dfo); + if (graph->depth_first) + free (graph->depth_first); FREE (graphs, graph); } +///@} -static def_t * -flowvar_get_def (flowvar_t *var) -{ - operand_t *op = var->op; - - switch (op->op_type) { - case op_def: - return op->o.def; - case op_value: - case op_label: - return 0; - case op_temp: - return op->o.tempop.def; - case op_alias: - internal_error (0, "unexpected alias operand"); - } - internal_error (0, "oops, blue pill"); - return 0; -} - +/** \name Flowvar classification */ +///@{ +/** Check if the flowvar refers to a global variable. + * + * For the flowvar to refer to a global variable, the flowvar's operand + * must be a def operand (but the def itself may be an alias of the real def) + * and the rel def must not have its def_t::local flag set. This means that + * function-scope static variables are not considered local (ie, only + * non-static function-scope variables and function parameters are considered + * local (temp vars are local too, but are not represented by \a op_def)). + */ static int flowvar_is_global (flowvar_t *var) { @@ -180,7 +219,7 @@ flowvar_is_global (flowvar_t *var) if (var->op->op_type != op_def) return 0; - def = var->op->o.def; + def = var->op->def; if (def->alias) def = def->alias; if (def->local) @@ -188,6 +227,15 @@ flowvar_is_global (flowvar_t *var) return 1; } +/** Check if the flowvar refers to a function parameter. + * + * For the flowvar to refer to a function parameter, the flowvar's operand + * must be a def operand (but the def itself may be an alias of the real def) + * and the real def must have both its def_t::local and def_t::param flags + * set. + * + * Temp vars are are not represented by op_def, so no mistake can be made. + */ static int flowvar_is_param (flowvar_t *var) { @@ -195,7 +243,7 @@ flowvar_is_param (flowvar_t *var) if (var->op->op_type != op_def) return 0; - def = var->op->o.def; + def = var->op->def; if (def->alias) def = def->alias; if (!def->local) @@ -204,18 +252,83 @@ flowvar_is_param (flowvar_t *var) return 0; return 1; } -#if 0 + +/** Check if the flowvar refers to a function argument. + * + * For the flowvar to refer to a function argument, the flowvar's operand + * must be a def operand (but the def itself may be an alias of the real def) + * and the real def must have both its def_t::local and def_t::argument flags + * set. + * + * Temp vars are are not represented by op_def, so no mistake can be made. + */ static int -flowvar_is_initialized (flowvar_t *var) +flowvar_is_argument (flowvar_t *var) { def_t *def; if (var->op->op_type != op_def) return 0; - def = var->op->o.def; - return def->initialized; + def = var->op->def; + if (def->alias) + def = def->alias; + if (!def->local) + return 0; + if (!def->argument) + return 0; + return 1; } -#endif + +/** Check if the flowvar refers to a local variable. + * + * As this is simply "neither global nor pamam nor argument", all other + * flowvars are considered local, in particular actual non-static function + * scope variables and temp vars. + */ +static int +flowvar_is_local (flowvar_t *var) +{ + return !(flowvar_is_global (var) || flowvar_is_param (var) + || flowvar_is_argument (var)); +} +///@} + +/** Extract the def from a def or temp flowvar. + * + * It is an error for the operand referenced by the flowvar to be anything + * other than a real def or temp. + */ +static __attribute__((pure)) def_t * +flowvar_get_def (flowvar_t *var) +{ + operand_t *op = var->op; + + switch (op->op_type) { + case op_def: + return op->def; + case op_value: + case op_label: + return 0; + case op_temp: + return op->tempop.def; + case op_alias: + internal_error (op->expr, "unexpected alias operand"); + case op_nil: + internal_error (op->expr, "unexpected nil operand"); + case op_pseudo: + internal_error (op->expr, "unexpected pseudo operand"); + } + internal_error (op->expr, "oops, blue pill"); + return 0; +} + +/** Get a def or temp var operand's flowvar. + * + * Other operand types never have a flowvar. + * + * If the operand does not yet have a flowvar, one is created and assigned + * to the operand. + */ flowvar_t * flow_get_var (operand_t *op) { @@ -223,18 +336,29 @@ flow_get_var (operand_t *op) return 0; if (op->op_type == op_temp) { - if (!op->o.tempop.flowvar) - op->o.tempop.flowvar = new_flowvar (); - return op->o.tempop.flowvar; + if (!op->tempop.flowvar) + op->tempop.flowvar = new_flowvar (); + return op->tempop.flowvar; } if (op->op_type == op_def) { - if (!op->o.def->flowvar) - op->o.def->flowvar = new_flowvar (); - return op->o.def->flowvar; + if (!op->def->flowvar) + op->def->flowvar = new_flowvar (); + return op->def->flowvar; + } + if (op->op_type == op_pseudo) { + if (!op->pseudoop->flowvar) + op->pseudoop->flowvar = new_flowvar (); + return op->pseudoop->flowvar; } return 0; } +/** Indicate whether the operand should be counted. + * + * If the operand is a def or temp var operand, and it has not already been + * counted, then it is counted, otherwise it is not. + * \return 1 if the operand should be counted, 0 if not + */ static int count_operand (operand_t *op) { @@ -246,14 +370,15 @@ count_operand (operand_t *op) return 0; var = flow_get_var (op); - // flowvars are initialized with number == 0, and any global flowvar - // used by a function will always have a number >= 0 after flow analysis, - // and local flowvars will always be 0 before flow analysis, so use -1 - // to indicate the variable has been counted. - // - // Also, since this is the beginning of flow analysis for this function, - // ensure the define/use sets for global vars are empty. However, as - // checking if a var is global is too much trouble, just clear them all. + /** Flowvars are initialized with number == 0, and any global flowvar + * used by a function will always have a number >= 0 after flow analysis, + * and local flowvars will always be 0 before flow analysis, so use -1 + * to indicate the variable has been counted. + * + * Also, since this is the beginning of flow analysis for this function, + * ensure the define/use sets for global vars are empty. However, since + * checking if a var is global is too much trouble, just clear them all. + */ if (var && var->number != -1) { set_empty (var->use); set_empty (var->define); @@ -263,6 +388,65 @@ count_operand (operand_t *op) return 0; } +static int +count_operand_chain (operand_t *op) +{ + int count = 0; + while (op) { + count += count_operand (op); + op = op->next; + } + return count; +} + +/** Allocate flow analysis pseudo address space. + */ +static int +get_pseudo_address (function_t *func, int size) +{ + int addr = func->pseudo_addr; + func->pseudo_addr += size; + return addr; +} + +/** Allocate flow analysis pseudo address space to a temporary variable. + * + * If the operand already has an address allocated (flowvar_t::flowaddr is + * not 0), then the already allocated address is returned. + * + * If the operand refers to an alias, the alias chain is followed to the + * actual temp var operand and the real temp var is allocated space if it + * has not allready been alloced. + * + * The operand is given the address of the real temp var operand plus whatever + * offset the operand has. + * + * Real temp var operands must have a zero offset. + * + * The operand address is set in \a op and returned. + */ +static int +get_temp_address (function_t *func, operand_t *op) +{ + operand_t *top = op; + if (op->tempop.flowaddr) { + return op->tempop.flowaddr; + } + while (top->tempop.alias) { + top = top->tempop.alias; + } + if (!top->tempop.flowaddr) { + top->tempop.flowaddr = get_pseudo_address (func, top->size); + } + if (top->tempop.offset) { + internal_error (top->expr, "real tempop with a non-zero offset"); + } + op->tempop.flowaddr = top->tempop.flowaddr + op->tempop.offset; + return op->tempop.flowaddr; +} + +/** Add an operand's flowvar to the function's list of variables. + */ static void add_operand (function_t *func, operand_t *op) { @@ -274,15 +458,47 @@ add_operand (function_t *func, operand_t *op) return; var = flow_get_var (op); - // If the flowvar number is still -1, then the flowvar has not yet been - // added to the list of variables referenced by the function. + /** If the flowvar number is still -1, then the flowvar has not yet been + * added to the list of variables referenced by the function. + * + * The flowvar's flowvar_t::number is set to its index in the function's + * list of flowvars. + * + * Also, temp and local flowvars are assigned addresses from the flow + * analysys pseudo address space so partial accesses can be analyzed. + */ if (var && var->number == -1) { var->number = func->num_vars++; var->op = op; func->vars[var->number] = var; + if (op->op_type == op_pseudo) { + var->flowaddr = get_pseudo_address (func, 1); + } else if (op->op_type == op_temp) { + var->flowaddr = get_temp_address (func, op); + } else if (flowvar_is_param (var)) { + var->flowaddr = func->num_statements + def_offset (var->op->def); + var->flowaddr += func->locals->space->size; + } else if (flowvar_is_argument (var)) { + var->flowaddr = func->num_statements + def_offset (var->op->def); + var->flowaddr += func->locals->space->size; + var->flowaddr += func->parameters->space->size; + } else if (flowvar_is_local (var)) { + var->flowaddr = func->num_statements + def_offset (var->op->def); + } } } +static void +add_operand_chain (function_t *func, operand_t *op) +{ + while (op) { + add_operand (func, op); + op = op->next; + } +} + +/** Create symbols and defs for params/return if not already available. + */ static symbol_t * param_symbol (const char *name) { @@ -293,6 +509,15 @@ param_symbol (const char *name) return sym; } +/** Build an array of all the statements in a function. + + The array exists so statements can be referenced by number and thus used + in sets. + + The statement references in the array (function_t::statements) are in the + same order as they are within the statement blocks (function_t::sblock) + and with the blocks in the same order as the linked list of blocks. +*/ static void flow_build_statements (function_t *func) { @@ -309,17 +534,96 @@ flow_build_statements (function_t *func) func->statements = malloc (num_statements * sizeof (statement_t *)); func->num_statements = num_statements; + func->real_statements = set_new (); for (sblock = func->sblock; sblock; sblock = sblock->next) { - for (s = sblock->statements; s; s = s->next) + for (s = sblock->statements; s; s = s->next) { func->statements[s->number] = s; + set_add (func->real_statements, s->number); + } + } +} + +static int flow_def_clear_flowvars (def_t *def, void *data) +{ + if (def->flowvar) { + delete_flowvar (def->flowvar); + } + def->flowvar = 0; + return 0; +} + +static void +clear_operand (operand_t *op) +{ + if (op && op->op_type == op_def) { + def_visit_all (op->def, 0, flow_def_clear_flowvars, 0); } } +static void +clear_operand_chain (operand_t *op) +{ + while (op) { + clear_operand (op); + op = op->next; + } +} + +static void +add_var_addrs (set_t *set, flowvar_t *var) +{ + for (int i = 0; i < var->op->size; i++) { + set_add (set, var->flowaddr + i); + } +} + +/** Build an array of all the variables used by a function + * + * The array exists so variables can be referenced by number and thus used + * in sets. However, because larger variables may be aliased by smaller types, + * their representation is more complicated. + * + * # Local variable representation + * Defined local vars add their address in local space to the number of + * statements in the function. Thus their flow analysis address is in the + * range: + * + * ([num_statements ... num_statements+localsize]) + * + * with a set element in flowvar_t::define for each word used by the var. + * That is, single word types (int, float, pointer, etc) have one element, + * doubles have two adjacant elements, and vectors and quaternions have + * three and four elements respectively (also adjacant). Structural types + * (struct, union, array) have as many adjacant elements as their size + * dictates. + * + * Temporary vars are pseudo allocated and their addresses are added as + * for normal local vars. + * + * Note, however, that flowvar_t::define also includes real function + * statements that assign to the variable. + * + * # Pseudo Address Space + * Temporary variables are _effectively_ local variables and thus will + * be treated as such by the analyzer in that their addresses and sizes + * will be used to determine which and how many set elements to use. + * + * However, at this stage, temporary variables do not have any address + * space assigned to them because their lifetimes are generally limited + * to a few statements and the memory used for the temp vars may be + * recycled. Thus, give temp vars a pseudo address space just past the + * address space used for source-defined local variables. As each temp + * var is added to the analyzer, get_temp_address() assigns the temp var + * an address using function_t::pseudo_addr as a starting point. + * + * add_operand() takes care of setting flowvar_t::flowaddr for both locals + * and temps. + */ static void flow_build_vars (function_t *func) { statement_t *s; - operand_t *operands[4]; + operand_t *operands[FLOW_OPERANDS]; int num_vars = 0; int i, j; set_t *stuse; @@ -327,9 +631,27 @@ flow_build_vars (function_t *func) set_iter_t *var_i; flowvar_t *var; - // first, count .return and .param_[0-7] as they are always needed + // First, run through the statements making sure any accessed variables + // have their flowvars reset. Local variables will be fine, but global + // variables may have had flowvars added in a previous function, and it's + // easier to just clear them all. + // This is done before .return and .param so they won't get reset just + // after being counted + for (i = 0; i < func->num_statements; i++) { + s = func->statements[i]; + flow_analyze_statement (s, 0, 0, 0, operands); + for (j = 0; j < FLOW_OPERANDS; j++) { + clear_operand (operands[j]); + } + clear_operand_chain (s->use); + clear_operand_chain (s->def); + clear_operand_chain (s->kill); + } + // count .return and .param_[0-7] as they are always needed for (i = 0; i < num_flow_params; i++) { - flow_params[i].op.o.def = param_symbol (flow_params[i].name)->s.def; + def_t *def = param_symbol (flow_params[i].name)->s.def; + def_visit_all (def, 0, flow_def_clear_flowvars, 0); + flow_params[i].op.def = def; num_vars += count_operand (&flow_params[i].op); } // then run through the statements in the function looking for accessed @@ -339,15 +661,28 @@ flow_build_vars (function_t *func) flow_analyze_statement (s, 0, 0, 0, operands); for (j = 0; j < 4; j++) num_vars += count_operand (operands[j]); + // count any pseudo operands referenced by the statement + num_vars += count_operand_chain (s->use); + num_vars += count_operand_chain (s->def); + num_vars += count_operand_chain (s->kill); } if (!num_vars) return; - func->vars = malloc (num_vars * sizeof (daglabel_t *)); + func->vars = malloc (num_vars * sizeof (flowvar_t *)); stuse = set_new (); stdef = set_new (); + // set up the pseudo address space for temp vars so accessing tmp vars + // though aliases analyses correctly + func->pseudo_addr = func->num_statements; + func->pseudo_addr += func->locals->space->size; + func->pseudo_addr += func->parameters->space->size; + if (func->arguments) { + func->pseudo_addr += func->arguments->size; + } + func->num_vars = 0; // incremented by add_operand // first, add .return and .param_[0-7] as they are always needed for (i = 0; i < num_flow_params; i++) @@ -359,7 +694,15 @@ flow_build_vars (function_t *func) flow_analyze_statement (s, 0, 0, 0, operands); for (j = 0; j < 4; j++) add_operand (func, operands[j]); - + add_operand_chain (func, s->use); + add_operand_chain (func, s->def); + add_operand_chain (func, s->kill); + } + // and set the use/def sets for the vars (has to be a separate pass + // because the alias handling reqruires the flow address to be valid + // (ie, not -1) + for (i = 0; i < func->num_statements; i++) { + s = func->statements[i]; flow_analyze_statement (s, stuse, stdef, 0, 0); for (var_i = set_first (stdef); var_i; var_i = set_next (var_i)) { var = func->vars[var_i->element]; @@ -371,36 +714,47 @@ flow_build_vars (function_t *func) } } func->global_vars = set_new (); - // mark all global vars (except .return and .param_N) + func->param_vars = set_new (); + // mark all global vars (except .return and .param_N), and param vars for (i = num_flow_params; i < func->num_vars; i++) { - if (flowvar_is_global (func->vars[i])) + if (flowvar_is_global (func->vars[i])) { set_add (func->global_vars, i); - } - // create dummy defs for local vars - for (i = 0; i < func->num_vars; i++) { - int offset, size; - int j; - - var = func->vars[i]; - if (flowvar_is_global (var) || flowvar_is_param (var)) - continue; - if (var->op->op_type == op_temp) { - j = func->symtab->space->size + var->number; - set_add (var->define, func->num_statements + j); - } else { - offset = def_offset (var->op->o.def); - size = def_size (var->op->o.def); - for (j = offset; j < offset + size; j++) - set_add (var->define, func->num_statements + j); } + if (flowvar_is_param (func->vars[i])) { + add_var_addrs (func->param_vars, func->vars[i]); + } + } + // Put the local varibals in their place (set var->defined to the addresses + // spanned by the var) + for (i = 0; i < func->num_vars; i++) { + var = func->vars[i]; + if (flowvar_is_global (var)) {// || flowvar_is_param (var)) { + continue; + } + add_var_addrs (var->define, var); } set_delete (stuse); set_delete (stdef); } +/** Add the tempop's spanned addresses to the kill set + */ static int -flow_kill_aliases_visit (def_t *def, void *_kill) +flow_tempop_kill_aliases (tempop_t *tempop, void *_kill) +{ + set_t *kill = (set_t *) _kill; + flowvar_t *var; + var = tempop->flowvar; + if (var) + set_union (kill, var->define); + return 0; +} + +/** Add the def's spanned addresses to the kill set + */ +static int +flow_def_kill_aliases (def_t *def, void *_kill) { set_t *kill = (set_t *) _kill; flowvar_t *var; @@ -410,6 +764,13 @@ flow_kill_aliases_visit (def_t *def, void *_kill) return 0; } +/** Add the flowvar's spanned addresses to the kill set + * + * If the flowvar refers to an alias, then the real def/tempop and any + * overlapping aliases are aslo killed. + * + * However, other aliases cannot kill anything in the uninitialized set. + */ static void flow_kill_aliases (set_t *kill, flowvar_t *var, const set_t *uninit) { @@ -419,27 +780,190 @@ flow_kill_aliases (set_t *kill, flowvar_t *var, const set_t *uninit) set_union (kill, var->define); op = var->op; tmp = set_new (); + // collect the kill sets from any aliases if (op->op_type == op_temp) { - if (op->o.tempop.alias) { - op = op->o.tempop.alias; - var = op->o.tempop.flowvar; - if (var) - set_union (tmp, var->define); - } - for (op = op->o.tempop.alias_ops; op; op = op->next) { - var = op->o.tempop.flowvar; - if (var) - set_union (tmp, var->define); - } + tempop_visit_all (&op->tempop, 1, flow_tempop_kill_aliases, tmp); } else if (op->op_type == op_def) { - def_visit_all (op->o.def, 1, flow_kill_aliases_visit, tmp); - // don't allow aliases to kill definitions in the entry dummy block + def_visit_all (op->def, 4 | 1, flow_def_kill_aliases, tmp); + } + // don't allow aliases to kill definitions in the entry dummy block + if (uninit) { set_difference (tmp, uninit); } // merge the alias kills with the current def's kills set_union (kill, tmp); + set_delete (tmp); } +static int +flow_tempop_add_aliases (tempop_t *tempop, void *_set) +{ + set_t *set = (set_t *) _set; + flowvar_t *var; + var = tempop->flowvar; + if (var) + set_add (set, var->number); + return 0; +} + +static int +flow_def_add_aliases (def_t *def, void *_set) +{ + set_t *set = (set_t *) _set; + flowvar_t *var; + var = def->flowvar; + if (var) + set_add (set, var->number); + return 0; +} + +static void +flow_add_op_var (set_t *set, operand_t *op, int ol) +{ + flowvar_t *var; + + if (!set) + return; + if (!(var = flow_get_var (op))) + return; + set_add (set, var->number); + + if (op->op_type == op_temp) { + tempop_visit_all (&op->tempop, ol, flow_tempop_add_aliases, set); + } else if (op->op_type == op_def) { + def_visit_all (op->def, ol, flow_def_add_aliases, set); + } +} + +static int +flowvar_def_add_use (def_t *def, void *data) +{ + statement_t *st = data; + flowvar_t *var = def->flowvar; + if (var) { + set_add (var->use, st->number); + } + return 0; +} + +static void +flowvar_add_use (flowvar_t *var, statement_t *st) +{ + set_add (var->use, st->number); + + if (var->op->op_type != op_def) { + return; + } + def_t *def = var->op->def->alias; + if (def && is_array (def->type)) { + def_visit_all (def, 0, flowvar_def_add_use, st); + } +} + +static void +follow_ud_chain (udchain_t ud, function_t *func, set_t *ptr, set_t *visited) +{ + statement_t *st = func->statements[ud.defst]; + if (set_is_member (visited, st->number)) { + return; + } + set_add (visited, st->number); + if (st->type == st_address) { + flowvar_t *var = flow_get_var (st->opa); + set_add (ptr, var->number); + return; + } + for (int j = 0; j < st->num_use; j++) { + udchain_t c = func->ud_chains[j + st->first_use]; + if (c.defst < func->num_statements) { + operand_t *op = func->vars[c.var]->op; + if (is_ptr (op->type)) { + follow_ud_chain (c, func, ptr, visited); + } else { + } + } + } +} + +static void +flow_check_params (statement_t *st, set_t *use, set_t *def, function_t *func) +{ + if (!func->ud_chains) { + return; + } + set_t *use_ptr = set_new (); + set_t *def_ptr = set_new (); + set_t *ptr = set_new (); + set_t *visited = set_new (); + + int have_use = 0; + for (operand_t *op = st->use; op; op = op->next) { + if (op->op_type == op_def && is_ptr (op->type)) { + flowvar_t *var = flow_get_var (op); + set_add (use_ptr, var->number); + have_use = 1; + const char *name = op->def->name; + if (!strncmp (name,".arg", 4) || !strncmp (name, ".param_", 7)) { + set_add (def_ptr, var->number); + } + } + } + if (have_use) { + for (int i = 0; i < st->num_use; i++) { + udchain_t ud = func->ud_chains[i + st->first_use]; + set_empty (visited); + set_add (visited, st->number); + if (set_is_member (use_ptr, ud.var)) { + set_empty (ptr); + follow_ud_chain (ud, func, ptr, visited); + for (set_iter_t *p = set_first (ptr); p; p = set_next (p)) { + flowvar_t *var = func->vars[p->element]; + flow_add_op_var (use, var->op, 0); + flowvar_add_use (var, st); + } + } + } + } + + set_delete (visited); + set_delete (use_ptr); + set_delete (def_ptr); + set_delete (ptr); +} + +typedef struct { + set_t *gen; + set_t *kill; + set_t *stgen; + set_t *stkill; + set_t *stdef; + set_t *uninit; + flowvar_t **vars; +} reachint_t; + +static void +flow_statement_reaching (statement_t *st, reachint_t *r) +{ + set_empty (r->stgen); + set_empty (r->stkill); + + set_iter_t *var_i; + for (var_i = set_first (r->stdef); var_i; var_i = set_next (var_i)) { + flowvar_t *var = r->vars[var_i->element]; + flow_kill_aliases (r->stkill, var, r->uninit); + set_remove (r->stkill, st->number); + set_add (r->stgen, st->number); + } + + set_difference (r->gen, r->stkill); + set_union (r->gen, r->stgen); + + set_difference (r->kill, r->stgen); + set_union (r->kill, r->stkill); +} + +/** Compute reaching defs + */ static void flow_reaching_defs (flowgraph_t *graph) { @@ -447,25 +971,45 @@ flow_reaching_defs (flowgraph_t *graph) int changed; flownode_t *node; statement_t *st; - set_t *stdef = set_new (); - set_t *stgen = set_new (); - set_t *stkill = set_new (); - set_t *oldout = set_new (); - set_t *gen, *kill, *in, *out, *uninit; - set_iter_t *var_i; + reachint_t reach = { + .stgen = set_new (), + .stkill = set_new (), + .stdef = set_new (), + .vars = graph->func->vars, + }; + set_t *in, *out, *uninit; set_iter_t *pred_i; flowvar_t *var; // First, create out for the entry dummy node using fake statement numbers. - kill = set_new (); - for (i = 0; i < graph->func->num_statements; i++) - set_add (kill, i); + //\f[ \bigcup\limits_{i=1}^{\infty} F_{i} \f] + //\f[ \bigcap\limits_{i=1}^{\infty} F_{i} \f] + + /** The dummy entry node reaching defs \a out set is initialized to: + * \f[ out_{reaching}=[\bigcup\limits_{v \in \{locals\}} define_{v}] + * \setminus \{statements\} \f] + * where {\a locals} is the set of local def and tempop flowvars (does + * not include parameters), \a define is the set of addresses spanned + * by the flowvar (see flow_build_vars()) (XXX along with statement + * gens), and {\a statements} is the set of all statements in the + * function (ensures the \a out set does not include any initializers in + * the code nodes). + * + * All other entry node sets are initialized to empty. + */ + // uninit uninit = set_new (); for (i = 0; i < graph->func->num_vars; i++) { var = graph->func->vars[i]; set_union (uninit, var->define);// do not want alias handling here - set_difference (uninit, kill); // remove any gens from the function } + /** Any possible gens from the function code are removed from the + * \a uninit set (which becomes the \a out set of the entry node's + * reaching defs) in order to prevent them leaking into the real nodes. + */ + // remove any gens from the function + set_difference (uninit, graph->func->real_statements); + // initialize the reaching defs sets in the entry node graph->nodes[graph->num_nodes]->reaching_defs.out = uninit; graph->nodes[graph->num_nodes]->reaching_defs.in = set_new (); graph->nodes[graph->num_nodes]->reaching_defs.gen = set_new (); @@ -474,41 +1018,28 @@ flow_reaching_defs (flowgraph_t *graph) // Calculate gen and kill for each block, and initialize in and out for (i = 0; i < graph->num_nodes; i++) { node = graph->nodes[i]; - gen = set_new (); - kill = set_new (); + reach.gen = set_new (); + reach.kill = set_new (); for (st = node->sblock->statements; st; st = st->next) { - flow_analyze_statement (st, 0, stdef, 0, 0); - set_empty (stgen); - set_empty (stkill); - for (var_i = set_first (stdef); var_i; var_i = set_next (var_i)) { - var = graph->func->vars[var_i->element]; - flow_kill_aliases (stkill, var, uninit); - set_remove (stkill, st->number); - set_add (stgen, st->number); - } - - set_difference (gen, stkill); - set_union (gen, stgen); - - set_difference (kill, stgen); - set_union (kill, stkill); + flow_analyze_statement (st, 0, reach.stdef, 0, 0); + flow_statement_reaching (st, &reach); } - node->reaching_defs.gen = gen; - node->reaching_defs.kill = kill; + node->reaching_defs.gen = reach.gen; + node->reaching_defs.kill = reach.kill; node->reaching_defs.in = set_new (); node->reaching_defs.out = set_new (); + reach.gen = reach.kill = 0; } changed = 1; + set_t *oldout = set_new (); while (changed) { changed = 0; // flow down the graph for (i = 0; i < graph->num_nodes; i++) { - node = graph->nodes[graph->dfo[i]]; + node = graph->nodes[graph->depth_first[i]]; in = node->reaching_defs.in; out = node->reaching_defs.out; - gen = node->reaching_defs.gen; - kill = node->reaching_defs.kill; for (pred_i = set_first (node->predecessors); pred_i; pred_i = set_next (pred_i)) { flownode_t *pred = graph->nodes[pred_i->element]; @@ -516,18 +1047,21 @@ flow_reaching_defs (flowgraph_t *graph) } set_assign (oldout, out); set_assign (out, in); - set_difference (out, kill); - set_union (out, gen); + set_difference (out, node->reaching_defs.kill); + set_union (out, node->reaching_defs.gen); if (!set_is_equivalent (out, oldout)) changed = 1; } } set_delete (oldout); - set_delete (stdef); - set_delete (stgen); - set_delete (stkill); + + set_delete (reach.stdef); + set_delete (reach.stgen); + set_delete (reach.stkill); } +/** Update the node's \a use set from the statement's \a use set + */ static void live_set_use (set_t *stuse, set_t *use, set_t *def) { @@ -536,6 +1070,8 @@ live_set_use (set_t *stuse, set_t *use, set_t *def) set_union (use, stuse); } +/** Update the node's \a def set from the statement's \a def set + */ static void live_set_def (set_t *stdef, set_t *use, set_t *def) { @@ -544,6 +1080,147 @@ live_set_def (set_t *stdef, set_t *use, set_t *def) set_union (def, stdef); } +static int +duchain_cmp (const void *_a, const void *_b) +{ + const udchain_t *a = _a; + const udchain_t *b = _b; + return a->defst - b->defst; +} + +static void +flow_build_chains (flowgraph_t *graph) +{ + reachint_t reach = { + .stgen = set_new (), + .stkill = set_new (), + .stdef = set_new (), + .vars = graph->func->vars, + }; + statement_t *st; + + reach.gen = set_new (); + reach.kill = set_new (); + set_t *stuse = set_new (); + set_t *tmp = set_new (); + set_t *st_update = set_new (); + set_t *udchains[graph->func->num_vars]; + int first_use[graph->func->num_statements]; + int num_use[graph->func->num_statements]; + for (int i = 0; i < graph->func->num_vars; i++) { + udchains[i] = set_new (); + } + int num_ud_chains; + while (1) { + udchain_t *ud_chains = 0; + num_ud_chains = 0; + + // count use-def chain elements + for (int i = 0; i < graph->num_nodes; i++) { + flownode_t *node = graph->nodes[i]; + set_empty (reach.kill); + set_assign (reach.gen, node->reaching_defs.in); + for (st = node->sblock->statements; st; st = st->next) { + flow_analyze_statement (st, stuse, reach.stdef, 0, 0); + if (st->type == st_func && statement_is_call (st)) { + // set def later? + flow_check_params (st, stuse, 0, graph->func); + } + set_empty (tmp); + for (set_iter_t *vi = set_first (stuse); vi; + vi = set_next (vi)) { + flowvar_t *var = reach.vars[vi->element]; + set_assign (tmp, var->define); + set_intersection (tmp, reach.gen); + num_ud_chains += set_count (tmp); + } + flow_statement_reaching (st, &reach); + } + } + if (num_ud_chains == graph->func->num_ud_chains) { + break; + } + ud_chains = malloc (num_ud_chains * sizeof (udchain_t)); + num_ud_chains = 0; + for (int i = 0; i < graph->func->num_vars; i++) { + set_empty (udchains[i]); + } + set_empty (st_update); + for (int i = 0; i < graph->num_nodes; i++) { + flownode_t *node = graph->nodes[i]; + set_empty (reach.kill); + set_assign (reach.gen, node->reaching_defs.in); + for (st = node->sblock->statements; st; st = st->next) { + flow_analyze_statement (st, stuse, reach.stdef, 0, 0); + if (st->type == st_func && statement_is_call (st)) { + // set def later? + flow_check_params (st, stuse, 0, graph->func); + } + set_empty (tmp); + first_use[st->number] = num_ud_chains; + num_use[st->number] = 0; + set_add (st_update, st->number); + for (set_iter_t *vi = set_first (stuse); vi; + vi = set_next (vi)) { + flowvar_t *var = reach.vars[vi->element]; + set_assign (tmp, var->define); + set_intersection (tmp, reach.gen); + for (set_iter_t *ud = set_first (tmp); ud; + ud = set_next (ud)) { + set_add (udchains[vi->element], num_ud_chains); + udchain_t *udc = &ud_chains[num_ud_chains++]; + udc->var = vi->element; + udc->usest = st->number; + udc->defst = ud->element; + } + } + num_use[st->number] = num_ud_chains - first_use[st->number]; + flow_statement_reaching (st, &reach); + } + } + for (set_iter_t *si = set_first (st_update); si; si = set_next (si)) { + st = graph->func->statements[si->element]; + st->first_use = first_use[si->element]; + st->num_use = num_use[si->element]; + } + for (int i = 0; i < graph->func->num_vars; i++) { + flowvar_t *var = reach.vars[i]; + set_assign (var->udchains, udchains[i]); + } + free (graph->func->ud_chains); + graph->func->ud_chains = ud_chains; + graph->func->num_ud_chains = num_ud_chains; + } + + for (int i = 0; i < graph->func->num_vars; i++) { + set_delete (udchains[i]); + } + set_delete (stuse); + set_delete (tmp); + set_delete (reach.gen); + set_delete (reach.kill); + set_delete (reach.stdef); + + graph->func->du_chains = malloc (num_ud_chains * sizeof (udchain_t)); + memcpy (graph->func->du_chains, graph->func->ud_chains, + num_ud_chains * sizeof (udchain_t)); + heapsort (graph->func->du_chains, num_ud_chains, sizeof (udchain_t), + duchain_cmp); + for (int i = 0; i < num_ud_chains; i++) { + udchain_t du = graph->func->du_chains[i]; + + flowvar_t *var = graph->func->vars[du.var]; + set_add (var->duchains, i); + + if (du.defst < graph->func->num_statements) { + statement_t *st = graph->func->statements[du.defst]; + if (!st->num_def++) { + st->first_def = i; + } + } + } +} + static void flow_live_vars (flowgraph_t *graph) { @@ -560,12 +1237,30 @@ flow_live_vars (flowgraph_t *graph) // first, calculate use and def for each block, and initialize the in and // out sets. + set_t *node_statements = set_new (); for (i = 0; i < graph->num_nodes; i++) { node = graph->nodes[i]; use = set_new (); def = set_new (); + set_empty (node_statements); for (st = node->sblock->statements; st; st = st->next) { - flow_analyze_statement (st, stuse, stdef, 0, 0); + set_add (node_statements, st->number); + } + for (st = node->sblock->statements; st; st = st->next) { + set_empty (stuse); + set_empty (stdef); + for (int i = 0; i < st->num_use; i++) { + udchain_t ud = graph->func->ud_chains[st->first_use + i]; + if (!set_is_member (node_statements, ud.defst)) { + set_add (stuse, ud.var); + } + } + for (int i = 0; i < st->num_def; i++) { + udchain_t du = graph->func->du_chains[st->first_def + i]; + if (!set_is_member (node_statements, du.usest)) { + set_add (stdef, du.var); + } + } live_set_use (stuse, use, def); live_set_def (stdef, use, def); } @@ -574,6 +1269,7 @@ flow_live_vars (flowgraph_t *graph) node->live_vars.in = set_new (); node->live_vars.out = set_new (); } + set_delete (node_statements); // create in for the exit dummy block using the global vars used by the // function use = set_new (); @@ -589,7 +1285,7 @@ flow_live_vars (flowgraph_t *graph) // flow UP the graph because live variable analysis uses information // from a node's successors rather than its predecessors. for (j = graph->num_nodes - 1; j >= 0; j--) { - node = graph->nodes[graph->dfo[j]]; + node = graph->nodes[graph->depth_first[j]]; set_empty (tmp); for (succ = set_first (node->successors); succ; succ = set_next (succ)) @@ -628,15 +1324,24 @@ flow_uninit_scan_statements (flownode_t *node, set_t *defs, set_t *uninit) for (var_i = set_first (stuse); var_i; var_i = set_next (var_i)) { var = node->graph->func->vars[var_i->element]; if (set_is_intersecting (defs, var->define)) { - def_t *def = flowvar_get_def (var); - if (def) { - if (options.warnings.uninited_variable) { - warning (st->expr, "%s may be used uninitialized", - def->name); + if (var->op->op_type == op_pseudo) { + pseudoop_t *op = var->op->pseudoop; + if (op->uninitialized) { + op->uninitialized (st->expr, op); + } else { + internal_error (0, "pseudoop uninitialized not set"); } } else { - bug (st->expr, "st %d, uninitialized temp %s", - st->number, operand_string (var->op)); + def_t *def = flowvar_get_def (var); + if (def) { + if (options.warnings.uninited_variable) { + warning (st->expr, "%s may be used uninitialized", + def->name); + } + } else { + bug (st->expr, "st %d, uninitialized temp %s", + st->number, operand_string (var->op)); + } } } // avoid repeat warnings in this node @@ -648,13 +1353,13 @@ flow_uninit_scan_statements (flownode_t *node, set_t *defs, set_t *uninit) set_difference (defs, var->define); if (var->op->op_type == op_temp) { op = var->op; - if (op->o.tempop.alias) { - var = op->o.tempop.alias->o.tempop.flowvar; + if (op->tempop.alias) { + var = op->tempop.alias->tempop.flowvar; if (var) set_difference (defs, var->define); } - for (op = op->o.tempop.alias_ops; op; op = op->next) { - var = op->o.tempop.flowvar; + for (op = op->tempop.alias_ops; op; op = op->next) { + var = op->tempop.flowvar; if (var) set_difference (defs, var->define); } @@ -678,10 +1383,12 @@ flow_uninitialized (flowgraph_t *graph) uninitialized = set_new (); node = graph->nodes[graph->num_nodes]; set_assign (uninitialized, node->reaching_defs.out); + // parameters are, by definition, initialized + set_difference (uninitialized, graph->func->param_vars); defs = set_new (); for (i = 0; i < graph->num_nodes; i++) { - node = graph->nodes[graph->dfo[i]]; + node = graph->nodes[graph->depth_first[i]]; set_empty (defs); // collect definitions of all variables "used" in this node. use from // the live vars analysis is perfect for the job @@ -700,6 +1407,7 @@ flow_uninitialized (flowgraph_t *graph) flow_uninit_scan_statements (node, defs, uninitialized); } set_delete (defs); + set_delete (uninitialized); } static void @@ -759,43 +1467,82 @@ flow_generate (flowgraph_t *graph) return code; } -static void -flow_add_op_var (set_t *set, operand_t *op) +static operand_t * +flow_analyze_pointer_operand (operand_t *ptrop, set_t *def) { - flowvar_t *var; + operand_t *op = 0; - if (!set) - return; - if (!(var = flow_get_var (op))) - return; - set_add (set, var->number); + if (ptrop->op_type == op_value && ptrop->value->lltype == ev_ptr) { + ex_pointer_t *ptr = &ptrop->value->v.pointer; + if (ptrop->value->v.pointer.def) { + def_t *alias; + alias = alias_def (ptr->def, ptr->type, ptr->val); + op = def_operand (alias, ptr->type, ptrop->expr); + } + if (ptrop->value->v.pointer.tempop) { + op = ptrop->value->v.pointer.tempop; + } + if (op) { + flow_add_op_var (def, op, 6); + } + } + return op; } void flow_analyze_statement (statement_t *s, set_t *use, set_t *def, set_t *kill, - operand_t *operands[4]) + operand_t *operands[FLOW_OPERANDS]) { - int i, start, calln = -1; + int i, calln = -1; + operand_t *src_op = 0; + operand_t *res_op = 0; + operand_t *aux_op1 = 0; + operand_t *aux_op2 = 0; + operand_t *aux_op3 = 0; - if (use) + if (use) { set_empty (use); - if (def) + for (operand_t *op = s->use; op; op = op->next) { + flow_add_op_var (use, op, 1); + } + } + if (def) { set_empty (def); - if (kill) + for (operand_t *op = s->def; op; op = op->next) { + flow_add_op_var (def, op, 6); + } + } + if (kill) { set_empty (kill); + for (operand_t *op = s->kill; op; op = op->next) { + flow_add_op_var (kill, op, 6); + } + } if (operands) { - for (i = 0; i < 4; i++) + for (i = 0; i < FLOW_OPERANDS; i++) operands[i] = 0; } switch (s->type) { case st_none: internal_error (s->expr, "not a statement"); + case st_address: + if (s->opb) { + flow_add_op_var (use, s->opa, 1); + flow_add_op_var (use, s->opb, 1); + } + flow_add_op_var (def, s->opc, 6); + if (operands) { + operands[0] = s->opc; + operands[1] = s->opa; + operands[2] = s->opb; + } + break; case st_expr: - flow_add_op_var (def, s->opc); - flow_add_op_var (use, s->opa); + flow_add_op_var (def, s->opc, 6); + flow_add_op_var (use, s->opa, 1); if (s->opb) - flow_add_op_var (use, s->opb); + flow_add_op_var (use, s->opb, 1); if (operands) { operands[0] = s->opc; operands[1] = s->opa; @@ -803,57 +1550,62 @@ flow_analyze_statement (statement_t *s, set_t *use, set_t *def, set_t *kill, } break; case st_assign: - flow_add_op_var (def, s->opb); - flow_add_op_var (use, s->opa); + flow_add_op_var (def, s->opa, 6); + flow_add_op_var (use, s->opc, 1); if (operands) { - operands[0] = s->opb; - operands[1] = s->opa; + operands[0] = s->opa; + operands[1] = s->opc; } break; case st_ptrassign: case st_move: - flow_add_op_var (use, s->opa); - flow_add_op_var (use, s->opb); - if (!strcmp (s->opcode, "")) { - flow_add_op_var (def, s->opc); - } else if (!strcmp (s->opcode, "")) { - flow_add_op_var (use, s->opc); - if (s->opc->op_type == op_value - && s->opc->o.value->type == ev_pointer - && s->opc->o.value->v.pointer.def) { - operand_t *op; - ex_pointer_t *ptr = &s->opc->o.value->v.pointer; - op = def_operand (ptr->def, ptr->type); - flow_add_op_var (def, op); - if (operands) - operands[0] = op; - else - free_operand (op); - } else { - if (operands) - operands[3] = s->opc; - } + case st_ptrmove: + case st_memset: + case st_ptrmemset: + flow_add_op_var (use, s->opa, 1); + flow_add_op_var (use, s->opb, 1); + aux_op1 = s->opb; + if (!strcmp (s->opcode, "move") + || !strcmp (s->opcode, "memset")) { + flow_add_op_var (def, s->opc, 6); + src_op = s->opa; + res_op = s->opc; + } else if (!strcmp (s->opcode, "movep")) { + flow_add_op_var (use, s->opc, 6); + aux_op3 = flow_analyze_pointer_operand (s->opa, use); + res_op = flow_analyze_pointer_operand (s->opc, def); + src_op = s->opa; + aux_op2 = s->opc; + } else if (!strcmp (s->opcode, "memsetp")) { + flow_add_op_var (use, s->opc, 6); + res_op = flow_analyze_pointer_operand (s->opc, def); + src_op = s->opa; + aux_op2 = s->opc; + } else if (!strcmp (s->opcode, "store")) { + flow_add_op_var (use, s->opc, 1); + res_op = flow_analyze_pointer_operand (s->opa, def); + src_op = s->opc; + aux_op2 = s->opa; } else { - if (s->opc) - flow_add_op_var (use, s->opc); + internal_error (s->expr, "unexpected opcode '%s' for %d", + s->opcode, s->type); } if (kill) { - //FIXME set of everything + set_everything (kill); } if (operands) { - if (!strcmp (s->opcode, "")) - operands[0] = s->opc; - operands[1] = s->opa; - operands[2] = s->opb; - if (strncmp (s->opcode, "opc; + operands[0] = res_op; + operands[1] = src_op; + operands[2] = aux_op1; + operands[3] = aux_op2; + operands[4] = aux_op3; } break; case st_state: - flow_add_op_var (use, s->opa); - flow_add_op_var (use, s->opb); + flow_add_op_var (use, s->opa, 1); + flow_add_op_var (use, s->opb, 1); if (s->opc) - flow_add_op_var (use, s->opc); + flow_add_op_var (use, s->opc, 1); //FIXME entity members if (operands) { operands[1] = s->opa; @@ -862,32 +1614,56 @@ flow_analyze_statement (statement_t *s, set_t *use, set_t *def, set_t *kill, } break; case st_func: - if (strcmp (s->opcode, "") == 0 - || strcmp (s->opcode, "") == 0) { - flow_add_op_var (use, s->opa); - } else if (strcmp (s->opcode, "") == 0) { - if (use) - set_add (use, 0); //FIXME assumes .return location + if (statement_is_return (s)) { + if (s->opc) { + // ruamoko + // opc always short + short ret_mode = s->opc->value->v.short_val; + // -1 is void + // FIXME size and addressing + if (ret_mode >= 0) { + flow_add_op_var (use, s->opa, 1); + } + } else { + // v6/v6p + if (s->opa) { + flow_add_op_var (use, s->opa, 1); + } + if (use) { + flow_add_op_var (use, &flow_params[0].op, 1); + } + } } - if (strncmp (s->opcode, "opcode, "call") == 0) { + // call uses opc to specify the destination of the return value + // parameter usage is taken care of by the statement's use + // list + flow_add_op_var (def, s->opc, 6); + // don't want old argument processing + calln = -1; + if (operands && s->opc->op_type != op_value) { + operands[0] = s->opc; + } + } else if (strncmp (s->opcode, "call", 4) == 0) { + calln = s->opcode[4] - '0'; + flow_add_op_var (use, s->opa, 1); + } else if (strncmp (s->opcode, "rcall", 5) == 0) { calln = s->opcode[5] - '0'; - flow_add_op_var (use, s->opa); - } else if (strncmp (s->opcode, "opcode[6] - '0'; - flow_add_op_var (use, s->opa); - flow_add_op_var (use, s->opb); + flow_add_op_var (use, s->opa, 1); + flow_add_op_var (use, s->opb, 1); if (s->opc) - flow_add_op_var (use, s->opc); + flow_add_op_var (use, s->opc, 1); } if (calln >= 0) { - if (use) { - for (i = start; i < calln; i++) - set_add (use, i + 1);//FIXME assumes .param_N locations + if (def) { + flow_add_op_var (def, &flow_params[0].op, 6); + } + if (kill) { + for (i = 1; i < num_flow_params; i++) { + flowvar_t *var = flow_get_var (&flow_params[i].op); + flow_kill_aliases (kill, var, 0); + } } - if (kill) - set_add (kill, 0); //FIXME assumes .return location } if (operands) { operands[1] = s->opa; @@ -896,14 +1672,21 @@ flow_analyze_statement (statement_t *s, set_t *use, set_t *def, set_t *kill, } break; case st_flow: - if (strcmp (s->opcode, "") != 0) { - flow_add_op_var (use, s->opa); - if (strcmp (s->opcode, "") == 0) - flow_add_op_var (use, s->opb); + if (statement_is_goto (s)) { + // opa is just a label + } else if (statement_is_jumpb (s)) { + flow_add_op_var (use, s->opa, 1); + flow_add_op_var (use, s->opb, 1); + } else if (statement_is_cond (s)) { + flow_add_op_var (use, s->opc, 1); + } else { + internal_error (s->expr, "unexpected flow statement: %s", + s->opcode); } if (operands) { operands[1] = s->opa; operands[2] = s->opb; + operands[3] = s->opc; } break; } @@ -960,18 +1743,18 @@ flow_find_successors (flowgraph_t *graph) if (sb->next) { set_add (node->successors, sb->next->flownode->id); } else { - bug (0, "code drops off the end of the function"); + bug (st->expr, "code drops off the end of the function"); // this shouldn't happen // however, make the exit dummy block the node's successor set_add (node->successors, graph->num_nodes + 1); } } - graph->num_edges += set_size (node->successors); + graph->num_edges += set_count (node->successors); } // set the successor for the entry dummy node to the real entry node node = graph->nodes[graph->num_nodes]; set_add (node->successors, 0); - graph->num_edges += set_size (node->successors); + graph->num_edges += set_count (node->successors); } static void @@ -981,7 +1764,7 @@ flow_make_edges (flowgraph_t *graph) flownode_t *node; set_iter_t *succ; - if (graph->edges); + if (graph->edges) free (graph->edges); graph->edges = malloc (graph->num_edges * sizeof (flowedge_t)); for (j = 0, i = 0; i < graph->num_nodes + 2; i++) { @@ -1039,7 +1822,6 @@ flow_find_dominators (flowgraph_t *graph) changed = 0; for (i = 1; i < graph->num_nodes; i++) { node = graph->nodes[i]; - pred = set_first (node->predecessors); set_empty (work); for (pred = set_first (node->predecessors); pred; pred = set_next (pred)) @@ -1139,7 +1921,7 @@ df_search (flowgraph_t *graph, set_t *visited, int *i, int n) } } node->dfn = --*i; - graph->dfo[node->dfn] = n; + graph->depth_first[node->dfn] = n; } static void @@ -1152,11 +1934,11 @@ flow_build_dfst (flowgraph_t *graph) set_add (visited, graph->num_nodes); set_add (visited, graph->num_nodes + 1); - if (graph->dfo) - free (graph->dfo); + if (graph->depth_first) + free (graph->depth_first); if (graph->dfst) set_delete (graph->dfst); - graph->dfo = calloc (graph->num_nodes, sizeof (int)); + graph->depth_first = calloc (graph->num_nodes, sizeof (int)); graph->dfst = set_new (); i = graph->num_nodes; df_search (graph, visited, &i, 0); @@ -1212,6 +1994,35 @@ flow_make_node (sblock_t *sblock, int id, function_t *func) return node; } +/** Build the flow graph for the function. + * + * In addition to the nodes created by the statement blocks, there are two + * dummy blocks: + * + * \dot + * digraph flow_build_graph { + * layout = dot; rankdir = TB; compound =true; nodesp = 1.0; + * dummy_entry [shape=box,label="entry"]; + * sblock0 [label="code"]; sblock1 [label="code"]; + * sblock2 [label="code"]; sblock3 [label="code"]; + * dummy_exit [shape=box,label="exit"]; + * dummy_entry -> sblock0; sblock0 -> sblock1; + * sblock1 -> sblock2; sblock2 -> sblock1; + * sblock2 -> dummy_exit; sblock1 -> sblock3; + * sblock3 -> dummy_exit; + * } + * \enddot + * + * The entry block is used for detecting use of uninitialized local variables + * and the exit block is used for ensuring global variables are treated as + * live at function exit. + * + * The exit block, which also is empty of statements, has its live vars + * \a use set initilized to the set of global defs, which are simply numbered + * by their index in the function's list of flowvars. All other exit node sets + * are initialized to empty. + * \f[ use_{live}=globals \f] + */ static flowgraph_t * flow_build_graph (function_t *func) { @@ -1245,7 +2056,7 @@ flow_build_graph (function_t *func) flow_make_edges (graph); flow_build_dfst (graph); if (options.block_dot.flow) - dump_dot (va ("flow-%d", pass), graph, dump_dot_flow); + dump_dot (va (0, "flow-%d", pass), graph, dump_dot_flow); pass++; } while (flow_remove_unreachable_nodes (graph)); flow_find_predecessors (graph); @@ -1262,7 +2073,10 @@ flow_data_flow (function_t *func) flow_build_statements (func); flow_build_vars (func); graph = flow_build_graph (func); + if (options.block_dot.statements) + dump_dot ("statements", graph, dump_dot_flow_statements); flow_reaching_defs (graph); + flow_build_chains (graph); if (options.block_dot.reaching) dump_dot ("reaching", graph, dump_dot_flow_reaching); flow_live_vars (graph); @@ -1273,3 +2087,5 @@ flow_data_flow (function_t *func) flow_cleanup_dags (graph); func->sblock = flow_generate (graph); } + +///@} diff --git a/tools/qfcc/source/function.c b/tools/qfcc/source/function.c index 873f734b6..b02dc30c4 100644 --- a/tools/qfcc/source/function.c +++ b/tools/qfcc/source/function.c @@ -44,32 +44,40 @@ #include "QF/hash.h" #include "QF/va.h" -#include "qfcc.h" +#include "tools/qfcc/include/qfcc.h" -#include "codespace.h" -#include "debug.h" -#include "def.h" -#include "defspace.h" -#include "diagnostic.h" -#include "emit.h" -#include "expr.h" -#include "flow.h" -#include "function.h" -#include "opcodes.h" -#include "options.h" -#include "reloc.h" -#include "shared.h" -#include "statements.h" -#include "strpool.h" -#include "symtab.h" -#include "type.h" -#include "value.h" +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/codespace.h" +#include "tools/qfcc/include/debug.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/emit.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/flow.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/opcodes.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/shared.h" +#include "tools/qfcc/include/statements.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" -static param_t *params_freelist; -static function_t *functions_freelist; +ALLOC_STATE (param_t, params); +ALLOC_STATE (function_t, functions); static hashtab_t *overloaded_functions; static hashtab_t *function_map; +// standardized base register to use for all locals (arguments, local defs, +// params) +#define LOCALS_REG 1 +// keep the stack aligned to 8 words (32 bytes) so lvec etc can be used without +// having to do shenanigans with mixed-alignment stack frames +#define STACK_ALIGN 8 + static const char * ol_func_get_key (const void *_f, void *unused) { @@ -92,7 +100,7 @@ new_param (const char *selector, type_t *type, const char *name) ALLOC (4096, param_t, params, param); param->next = 0; param->selector = selector; - param->type = type; + param->type = find_type (type); param->name = name; return param; @@ -119,7 +127,7 @@ param_append_identifiers (param_t *params, symbol_t *idents, type_t *type) return params; } -param_t * +static param_t * _reverse_params (param_t *params, param_t *next) { param_t *p = params; @@ -137,6 +145,20 @@ reverse_params (param_t *params) return _reverse_params (params, 0); } +param_t * +append_params (param_t *params, param_t *more_params) +{ + if (params) { + param_t *p; + for (p = params; p->next; ) { + p = p->next; + } + p->next = more_params; + return params; + } + return more_params; +} + param_t * copy_params (param_t *params) { @@ -151,27 +173,45 @@ copy_params (param_t *params) } type_t * -parse_params (type_t *type, param_t *parms) +parse_params (type_t *return_type, param_t *parms) { param_t *p; type_t *new; + type_t *ptype; + int count = 0; + + if (return_type && is_class (return_type)) { + error (0, "cannot return an object (forgot *?)"); + return_type = &type_id; + } new = new_type (); new->type = ev_func; - new->t.func.type = type; + new->alignment = 1; + new->width = 1; + new->t.func.type = return_type; new->t.func.num_params = 0; for (p = parms; p; p = p->next) { - if (new->t.func.num_params > MAX_PARMS) { - error (0, "too many params"); - return type; + if (p->type) { + count++; } + } + if (count) { + new->t.func.param_types = malloc (count * sizeof (type_t)); + } + for (p = parms; p; p = p->next) { if (!p->selector && !p->type && !p->name) { if (p->next) internal_error (0, 0); new->t.func.num_params = -(new->t.func.num_params + 1); } else if (p->type) { - new->t.func.param_types[new->t.func.num_params] = p->type; + if (is_class (p->type)) { + error (0, "cannot use an object as a parameter (forgot *?)"); + p->type = &type_id; + } + ptype = (type_t *) unalias_type (p->type); //FIXME cast + new->t.func.param_types[new->t.func.num_params] = ptype; new->t.func.num_params++; } } @@ -186,7 +226,7 @@ check_params (param_t *params) if (!params) return 0; while (p) { - if (p->type == &type_void) { + if (p->type && is_void(p->type)) { if (p->name) { error (0, "parameter %d ('%s') has incomplete type", num, p->name); @@ -205,19 +245,19 @@ check_params (param_t *params) } static overloaded_function_t * -get_function (const char *name, type_t *type, int overload, int create) +get_function (const char *name, const type_t *type, int overload, int create) { const char *full_name; overloaded_function_t *func; if (!overloaded_functions) { - overloaded_functions = Hash_NewTable (1021, ol_func_get_key, 0, 0); - function_map = Hash_NewTable (1021, func_map_get_key, 0, 0); + overloaded_functions = Hash_NewTable (1021, ol_func_get_key, 0, 0, 0); + function_map = Hash_NewTable (1021, func_map_get_key, 0, 0, 0); } name = save_string (name); - full_name = save_string (va ("%s|%s", name, encode_params (type))); + full_name = save_string (va (0, "%s|%s", name, encode_params (type))); func = Hash_Find (overloaded_functions, full_name); if (func) { @@ -264,7 +304,7 @@ function_symbol (symbol_t *sym, int overload, int create) overloaded_function_t *func; symbol_t *s; - func = get_function (name, sym->type, overload, create); + func = get_function (name, unalias_type (sym->type), overload, create); if (func && func->overloaded) name = func->full_name; @@ -272,7 +312,7 @@ function_symbol (symbol_t *sym, int overload, int create) if ((!s || s->table != current_symtab) && create) { s = new_symbol (name); s->sy_type = sy_func; - s->type = sym->type; + s->type = (type_t *) unalias_type (sym->type); // FIXME cast s->params = sym->params; s->s.func = 0; // function not yet defined symtab_addsymbol (current_symtab, s); @@ -286,8 +326,8 @@ func_compare (const void *a, const void *b) { overloaded_function_t *fa = *(overloaded_function_t **) a; overloaded_function_t *fb = *(overloaded_function_t **) b; - type_t *ta = fa->type; - type_t *tb = fb->type; + const type_t *ta = fa->type; + const type_t *tb = fb->type; int na = ta->t.func.num_params; int nb = tb->t.func.num_params; int ret, i; @@ -326,8 +366,9 @@ find_function (expr_t *fexpr, expr_t *params) return e; type.t.func.num_params++; } - if (type.t.func.num_params > MAX_PARMS) - return fexpr; + i = type.t.func.num_params * sizeof (type_t); + type.t.func.param_types = alloca(i); + memset (type.t.func.param_types, 0, i); for (i = 0, e = params; e; i++, e = e->next) { type.t.func.param_types[type.t.func.num_params - 1 - i] = get_type (e); if (e->type == ex_error) @@ -339,14 +380,17 @@ find_function (expr_t *fexpr, expr_t *params) for (func_count = 0; funcs[func_count]; func_count++) ; if (func_count < 2) { - free (funcs); - return fexpr; + f = (overloaded_function_t *) funcs[0]; + if (func_count && !f->overloaded) { + free (funcs); + return fexpr; + } } type.t.func.type = ((overloaded_function_t *) funcs[0])->type->t.func.type; dummy.type = find_type (&type); qsort (funcs, func_count, sizeof (void *), func_compare); - dummy.full_name = save_string (va ("%s|%s", fexpr->e.symbol->name, + dummy.full_name = save_string (va (0, "%s|%s", fexpr->e.symbol->name, encode_params (&type))); dummy_p = bsearch (&dummy_p, funcs, func_count, sizeof (void *), func_compare); @@ -415,6 +459,18 @@ find_function (expr_t *fexpr, expr_t *params) return fexpr; } +int +value_too_large (type_t *val_type) +{ + if ((options.code.progsversion < PROG_VERSION + && type_size (val_type) > type_size (&type_param)) + || (options.code.progsversion == PROG_VERSION + && type_size (val_type) > MAX_DEF_SIZE)) { + return 1; + } + return 0; +} + static void check_function (symbol_t *fsym) { @@ -426,8 +482,9 @@ check_function (symbol_t *fsym) error (0, "return type is an incomplete type"); fsym->type->t.func.type = &type_void;//FIXME better type? } - if (type_size (fsym->type->t.func.type) > type_size (&type_param)) { - error (0, "return value too large to be passed by value"); + if (value_too_large (fsym->type->t.func.type)) { + error (0, "return value too large to be passed by value (%d)", + type_size (&type_param)); fsym->type->t.func.type = &type_void;//FIXME better type? } for (p = params, i = 0; p; p = p->next, i++) { @@ -435,35 +492,30 @@ check_function (symbol_t *fsym) continue; // ellipsis marker if (!p->type) continue; // non-param selector - if (!type_size (p->type)) + if (!type_size (p->type)) { error (0, "parameter %d (‘%s’) has incomplete type", i + 1, p->name); - if (type_size (p->type) > type_size (&type_param)) + } + if (value_too_large (p->type)) { error (0, "param %d (‘%s’) is too large to be passed by value", i + 1, p->name); + } } } static void -build_scope (symbol_t *fsym, symtab_t *parent) +build_v6p_scope (symbol_t *fsym) { int i; param_t *p; symbol_t *args = 0; symbol_t *param; - symtab_t *symtab; - symtab_t *cs = current_symtab; + symtab_t *parameters = fsym->s.func->parameters; + symtab_t *locals = fsym->s.func->locals; - check_function (fsym); - - symtab = new_symtab (parent, stab_local); - fsym->s.func->symtab = symtab; - symtab->space = defspace_new (ds_virtual); - current_symtab = symtab; - - if (fsym->type->t.func.num_params < 0) { + if (fsym->s.func->type->t.func.num_params < 0) { args = new_symbol_type (".args", &type_va_list); - initialize_def (args, args->type, 0, symtab->space, sc_param); + initialize_def (args, 0, parameters->space, sc_param, locals); } for (p = fsym->params, i = 0; p; p = p->next) { @@ -476,18 +528,91 @@ build_scope (symbol_t *fsym, symtab_t *parent) p->name = save_string (""); } param = new_symbol_type (p->name, p->type); - initialize_def (param, param->type, 0, symtab->space, sc_param); + initialize_def (param, 0, parameters->space, sc_param, locals); i++; } if (args) { - while (i < MAX_PARMS) { - param = new_symbol_type (va (".par%d", i), &type_param); - initialize_def (param, &type_param, 0, symtab->space, sc_param); + while (i < PR_MAX_PARAMS) { + param = new_symbol_type (va (0, ".par%d", i), &type_param); + initialize_def (param, 0, parameters->space, sc_param, locals); i++; } } - current_symtab = cs; +} + +static void +create_param (symtab_t *parameters, symbol_t *param) +{ + defspace_t *space = parameters->space; + def_t *def = new_def (param->name, 0, space, sc_param); + int size = type_size (param->type); + int alignment = param->type->alignment; + if (alignment < 4) { + alignment = 4; + } + def->offset = defspace_alloc_aligned_highwater (space, size, alignment); + def->type = param->type; + param->s.def = def; + param->sy_type = sy_var; + symtab_addsymbol (parameters, param); + if (is_vector(param->type) && options.code.vector_components) + init_vector_components (param, 0, parameters); +} + +static void +build_rua_scope (symbol_t *fsym) +{ + for (param_t *p = fsym->params; p; p = p->next) { + symbol_t *param; + if (!p->selector && !p->type && !p->name) { + // ellipsis marker + param = new_symbol_type (".args", &type_va_list); + } else { + if (!p->type) { + continue; // non-param selector + } + if (!p->name) { + error (0, "parameter name omitted"); + p->name = save_string (""); + } + param = new_symbol_type (p->name, p->type); + } + create_param (fsym->s.func->parameters, param); + param->s.def->reg = fsym->s.func->temp_reg;; + } +} + +static void +build_scope (symbol_t *fsym, symtab_t *parent) +{ + symtab_t *parameters; + symtab_t *locals; + + if (!fsym->s.func) { + internal_error (0, "function %s not defined", fsym->name); + } + if (!is_func (fsym->s.func->type)) { + internal_error (0, "function type %s not a funciton", fsym->name); + } + + check_function (fsym); + + fsym->s.func->label_scope = new_symtab (0, stab_label); + + parameters = new_symtab (parent, stab_param); + parameters->space = defspace_new (ds_virtual); + fsym->s.func->parameters = parameters; + + locals = new_symtab (parameters, stab_local); + locals->space = defspace_new (ds_virtual); + fsym->s.func->locals = locals; + + if (options.code.progsversion == PROG_VERSION) { + build_rua_scope (fsym); + } else { + build_v6p_scope (fsym); + } } function_t * @@ -515,6 +640,7 @@ make_function (symbol_t *sym, const char *nice_name, defspace_t *space, if (!sym->s.func) { sym->s.func = new_function (sym->name, nice_name); sym->s.func->sym = sym; + sym->s.func->type = unalias_type (sym->type); } if (sym->s.func->def && sym->s.func->def->external && storage != sc_extern) { @@ -539,13 +665,13 @@ add_function (function_t *f) function_t * begin_function (symbol_t *sym, const char *nicename, symtab_t *parent, - int far) + int far, storage_class_t storage) { defspace_t *space; if (sym->sy_type != sy_func) { error (0, "%s is not a function", sym->name); - sym = new_symbol_type (sym->name, &type_function); + sym = new_symbol_type (sym->name, &type_func); sym = function_symbol (sym, 1, 1); } if (sym->s.func && sym->s.func->def && sym->s.func->def->initialized) { @@ -556,13 +682,16 @@ begin_function (symbol_t *sym, const char *nicename, symtab_t *parent, space = sym->table->space; if (far) space = pr.far_data; - make_function (sym, nicename, space, current_storage); + make_function (sym, nicename, space, storage); if (!sym->s.func->def->external) { sym->s.func->def->initialized = 1; sym->s.func->def->constant = 1; sym->s.func->def->nosave = 1; add_function (sym->s.func); reloc_def_func (sym->s.func, sym->s.func->def); + + sym->s.func->def->file = pr.source_file; + sym->s.func->def->line = pr.source_line; } sym->s.func->code = pr.code->size; @@ -576,23 +705,141 @@ begin_function (symbol_t *sym, const char *nicename, symtab_t *parent, return sym->s.func; } +static void +build_function (symbol_t *fsym) +{ + const type_t *func_type = fsym->s.func->type; + if (func_type->t.func.num_params > PR_MAX_PARAMS) { + error (0, "too many params"); + } +} + +static void +merge_spaces (defspace_t *dst, defspace_t *src, int alignment) +{ + int offset; + + for (def_t *def = src->defs; def; def = def->next) { + if (def->type->alignment > alignment) { + alignment = def->type->alignment; + } + } + offset = defspace_alloc_aligned_highwater (dst, src->size, alignment); + for (def_t *def = src->defs; def; def = def->next) { + def->offset += offset; + def->space = dst; + } + + if (src->defs) { + *dst->def_tail = src->defs; + dst->def_tail = src->def_tail; + src->def_tail = &src->defs; + *src->def_tail = 0; + } + + defspace_delete (src); +} + function_t * build_code_function (symbol_t *fsym, expr_t *state_expr, expr_t *statements) { if (fsym->sy_type != sy_func) // probably in error recovery return 0; - build_function (fsym->s.func); + build_function (fsym); if (state_expr) { - state_expr->next = statements; - statements = state_expr; + prepend_expr (statements, state_expr); + } + function_t *func = fsym->s.func; + if (options.code.progsversion == PROG_VERSION) { + /* Create a function entry block to set up the stack frame and add the + * actual function code to that block. This ensure that the adjstk and + * with statements always come first, regardless of what ideas the + * optimizer gets. + */ + expr_t *e; + expr_t *entry = new_block_expr (); + entry->file = func->def->file; + entry->line = func->def->line; + + e = new_adjstk_expr (0, 0); + e->file = func->def->file; + e->line = func->def->line; + append_expr (entry, e); + + e = new_with_expr (2, LOCALS_REG, new_short_expr (0)); + e->file = func->def->file; + e->line = func->def->line; + append_expr (entry, e); + + append_expr (entry, statements); + statements = entry; + + /* Mark all local defs as using the base register used for stack + * references. + */ + func->temp_reg = LOCALS_REG; + for (def_t *def = func->locals->space->defs; def; def = def->next) { + if (def->local || def->param) { + def->reg = LOCALS_REG; + } + } + for (def_t *def = func->parameters->space->defs; def; def = def->next) { + if (def->local || def->param) { + def->reg = LOCALS_REG; + } + } + } + emit_function (func, statements); + if (options.code.progsversion < PROG_VERSION) { + // stitch parameter and locals data together with parameters coming + // first + defspace_t *space = defspace_new (ds_virtual); + + func->params_start = 0; + + merge_spaces (space, func->parameters->space, 1); + func->parameters->space = space; + + merge_spaces (space, func->locals->space, 1); + func->locals->space = space; + } else { + defspace_t *space = defspace_new (ds_virtual); + + if (func->arguments) { + func->arguments->size = func->arguments->max_size; + merge_spaces (space, func->arguments, STACK_ALIGN); + func->arguments = 0; + } + + merge_spaces (space, func->locals->space, STACK_ALIGN); + func->locals->space = space; + + // allocate 0 words to force alignment and get the address + func->params_start = defspace_alloc_aligned_highwater (space, 0, + STACK_ALIGN); + + dstatement_t *st = &pr.code->code[func->code]; + if (st->op == OP_ADJSTK) { + if (func->params_start) { + st->b = -func->params_start; + } else { + // skip over adjstk so a zero adjustment doesn't get executed + func->code += 1; + } + } + merge_spaces (space, func->parameters->space, STACK_ALIGN); + func->parameters->space = space; + + // force the alignment again so the full stack slot is counted when + // the final parameter is smaller than STACK_ALIGN words + defspace_alloc_aligned_highwater (space, 0, STACK_ALIGN); } - emit_function (fsym->s.func, statements); - finish_function (fsym->s.func); return fsym->s.func; } function_t * -build_builtin_function (symbol_t *sym, expr_t *bi_val, int far) +build_builtin_function (symbol_t *sym, expr_t *bi_val, int far, + storage_class_t storage) { int bi; defspace_t *space; @@ -605,49 +852,41 @@ build_builtin_function (symbol_t *sym, expr_t *bi_val, int far) error (bi_val, "%s redefined", sym->name); return 0; } - if (!is_integer_val (bi_val) && !is_float_val (bi_val)) { + if (!is_int_val (bi_val) && !is_float_val (bi_val)) { error (bi_val, "invalid constant for = #"); return 0; } space = sym->table->space; if (far) space = pr.far_data; - make_function (sym, 0, space, current_storage); + make_function (sym, 0, space, storage); if (sym->s.func->def->external) return 0; + sym->s.func->def->initialized = 1; + sym->s.func->def->constant = 1; + sym->s.func->def->nosave = 1; add_function (sym->s.func); - if (is_integer_val (bi_val)) - bi = expr_integer (bi_val); + if (is_int_val (bi_val)) + bi = expr_int (bi_val); else bi = expr_float (bi_val); + if (bi < 0) { + error (bi_val, "builtin functions must be positive or 0"); + return 0; + } sym->s.func->builtin = bi; reloc_def_func (sym->s.func, sym->s.func->def); - build_function (sym->s.func); - finish_function (sym->s.func); + build_function (sym); // for debug info build_scope (sym, current_symtab); - sym->s.func->symtab->space->size = 0; + sym->s.func->parameters->space->size = 0; + sym->s.func->locals->space = sym->s.func->parameters->space; return sym->s.func; } -void -build_function (function_t *f) -{ - // FIXME -// f->def->constant = 1; -// f->def->nosave = 1; -// f->def->initialized = 1; -// G_FUNCTION (f->def->ofs) = f->function_num; -} - -void -finish_function (function_t *f) -{ -} - void emit_function (function_t *f, expr_t *e) { diff --git a/tools/qfcc/source/grab.c b/tools/qfcc/source/grab.c index 03edaa41a..7cad9cc62 100644 --- a/tools/qfcc/source/grab.c +++ b/tools/qfcc/source/grab.c @@ -44,12 +44,12 @@ #include "QF/hash.h" #include "QF/quakeio.h" -#include "diagnostic.h" -#include "expr.h" -#include "grab.h" -#include "options.h" -#include "qfcc.h" -#include "strpool.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/grab.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/strpool.h" int grab_frame; int grab_other; @@ -64,17 +64,22 @@ typedef struct frame_s { int num; } frame_t; -static frame_t *frames_freelist; +ALLOC_STATE (frame_t, frames); static frame_t *frame_list; static frame_t **frame_tail = &frame_list; static frame_t grab_list[] = { - {0, "cd", 0}, - {0, "origin", 0}, - {0, "base", 0}, - {0, "flags", 0}, - {0, "scale", 0}, - {0, "skin", 0}, + {0, "modelname", 0}, + {0, "base", 0}, + {0, "cd", 0}, + {0, "sync", 0}, + {0, "origin", 0}, + {0, "eyeposition", 0}, + {0, "scale", 0}, + {0, "flags", 0}, + {0, "skin", 0}, + {0, "framegroupstart", 0}, + {0, "skingroupstart", 0}, }; static const char * @@ -100,8 +105,8 @@ do_grab (const char *token) size_t i; initialized = 1; - frame_tab = Hash_NewTable (1021, frame_get_key, frame_free, 0); - grab_tab = Hash_NewTable (1021, frame_get_key, 0, 0); + frame_tab = Hash_NewTable (1021, frame_get_key, frame_free, 0, 0); + grab_tab = Hash_NewTable (1021, frame_get_key, 0, 0, 0); for (i = 0; i < sizeof (grab_list) / sizeof (grab_list[0]); i++) Hash_Add (grab_tab, &grab_list[i]); } diff --git a/tools/qfcc/source/idstuff.c b/tools/qfcc/source/idstuff.c index 629862003..34e9c4d7b 100644 --- a/tools/qfcc/source/idstuff.c +++ b/tools/qfcc/source/idstuff.c @@ -43,16 +43,16 @@ #include #include -#include "def.h" -#include "defspace.h" -#include "diagnostic.h" -#include "qfcc.h" -#include "expr.h" -#include "idstuff.h" -#include "options.h" -#include "strpool.h" -#include "symtab.h" -#include "type.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/idstuff.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" #define MAX_SOUNDS 1024 #define MAX_MODELS 1024 @@ -212,10 +212,10 @@ WriteProgdefs (dprograms_t *progs, const char *filename) "\n\ntypedef struct\n{\tint\tpad[%i];\n", RESERVED_OFS); - strings = (char *) progs + progs->ofs_strings; - for (i = 0; i < progs->numglobaldefs; i++) { - def = (ddef_t *) ((char *) progs + progs->ofs_globaldefs) + i; - name = strings + def->s_name; + strings = (char *) progs + progs->strings.offset; + for (i = 0; i < progs->globaldefs.count; i++) { + def = (ddef_t *) ((char *) progs + progs->globaldefs.offset) + i; + name = strings + def->name; if (!strcmp (name, "end_sys_globals")) break; if (!def->ofs) @@ -230,7 +230,7 @@ WriteProgdefs (dprograms_t *progs, const char *filename) case ev_vector: dasprintf (dstr, "\tvec3_t\t%s;\n", name); break; - case ev_quat: + case ev_quaternion: dasprintf (dstr, "\tquat_t\t%s;\n", name); break; case ev_string: @@ -251,9 +251,9 @@ WriteProgdefs (dprograms_t *progs, const char *filename) // print all fields dasprintf (dstr, "typedef struct\n{\n"); - for (i = 0, j = 0; i < progs->numglobaldefs; i++) { - def = (ddef_t *) ((char *) progs + progs->ofs_globaldefs) + i; - name = strings + def->s_name; + for (i = 0, j = 0; i < progs->globaldefs.count; i++) { + def = (ddef_t *) ((char *) progs + progs->globaldefs.offset) + i; + name = strings + def->name; if (!strcmp (name, "end_sys_fields")) break; @@ -264,8 +264,8 @@ WriteProgdefs (dprograms_t *progs, const char *filename) if (!strcmp (name, ".imm")) continue; - fdef = (ddef_t *) ((char *) progs + progs->ofs_fielddefs) + j++; - if (fdef->s_name != def->s_name) + fdef = (ddef_t *) ((char *) progs + progs->fielddefs.offset) + j++; + if (fdef->name != def->name) internal_error (0, "def and field order messup"); switch (fdef->type) { @@ -295,7 +295,7 @@ WriteProgdefs (dprograms_t *progs, const char *filename) crc = CRC_Block ((byte *) dstr->str, dstr->size - 1); dasprintf (dstr, "#define PROGHEADER_CRC %u\n", crc); - dstring_insertstr (dstr, 0, "/* Actually, generated by qfcc, be one must " + dstring_insertstr (dstr, 0, "/* Actually, generated by qfcc, but one must " "maintain formalities */"); if (filename) { diff --git a/tools/qfcc/source/linker.c b/tools/qfcc/source/linker.c index 772f3fd35..8d04f4ef5 100644 --- a/tools/qfcc/source/linker.c +++ b/tools/qfcc/source/linker.c @@ -58,32 +58,32 @@ #include "QF/pakfile.h" #include "QF/va.h" -#include "class.h" -#include "codespace.h" -#include "def.h" -#include "defspace.h" -#include "diagnostic.h" -#include "emit.h" -#include "expr.h" -#include "linker.h" -#include "obj_file.h" -#include "obj_type.h" -#include "options.h" -#include "qfcc.h" -#include "reloc.h" -#include "strpool.h" -#include "type.h" +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/codespace.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/emit.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/linker.h" +#include "tools/qfcc/include/obj_file.h" +#include "tools/qfcc/include/obj_type.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/type.h" static void linker_internal_error (const char *fmt, ...) - __attribute__ ((format (printf, 1, 2), noreturn)); + __attribute__ ((format (PRINTF, 1, 2), noreturn)); static void linker_error (const char *fmt, ...) - __attribute__ ((format (printf, 1, 2))); + __attribute__ ((format (PRINTF, 1, 2))); static void linker_warning (const char *fmt, ...) - __attribute__ ((format (printf, 1, 2))); + __attribute__ ((format (PRINTF, 1, 2))); static void def_error (qfo_def_t *def, const char *fmt, ...) - __attribute__ ((format (printf, 2, 3))); + __attribute__ ((format (PRINTF, 2, 3))); static void def_warning (qfo_def_t *def, const char *fmt, ...) - __attribute__ ((used, format (printf, 2, 3))); + __attribute__ ((used, format (PRINTF, 2, 3))); /** Safe handling of defs in hash tables and other containers. @@ -128,10 +128,10 @@ static builtin_sym_t builtin_symbols[] __attribute__ ((used)) = { {".param_6", &type_param, QFOD_NOSAVE | QFOD_GLOBAL}, {".param_7", &type_param, QFOD_NOSAVE | QFOD_GLOBAL}, }; -static const int num_builtins = sizeof (builtin_symbols) - / sizeof (builtin_symbols[0]); +static const unsigned num_builtins = sizeof (builtin_symbols) + / sizeof (builtin_symbols[0]); -static defref_t *defrefs_freelist; +ALLOC_STATE (defref_t, defrefs); static hashtab_t *extern_data_defs; static hashtab_t *defined_data_defs; @@ -147,13 +147,14 @@ static qfo_t *work; static int work_base[qfo_num_spaces]; static int work_func_base; static defref_t **work_defrefs; -static int num_work_defrefs; +static unsigned num_work_defrefs; static strpool_t *work_strings; static codespace_t *work_code; static defspace_t *work_near_data; static defspace_t *work_far_data; static defspace_t *work_entity_data; static defspace_t *work_type_data; +static defspace_t *work_debug_data; static qfo_reloc_t *work_loose_relocs; static int work_num_loose_relocs; @@ -163,6 +164,7 @@ static defspace_t **work_spaces[qfo_num_spaces] = { &work_far_data, &work_entity_data, &work_type_data, + &work_debug_data, }; static dstring_t *linker_current_file; @@ -170,7 +172,7 @@ static dstring_t *linker_current_file; #define QFOSTR(q,s) QFO_GETSTR (q, s) #define WORKSTR(s) QFOSTR (work, s) #define QFOTYPE(t) ((qfot_type_t *) (char *) \ - (qfo_type_defs->d.data + (t))) + (qfo_type_defs->data + (t))) #define WORKTYPE(t) ((qfot_type_t *) (char *) \ (work_type_data->data + (t))) @@ -243,9 +245,9 @@ defs_get_key (const void *_r, void *unused) int linker_add_string (const char *str) { - string_t new; + pr_string_t new; new = strpool_addstr (work_strings, str); - work->spaces[qfo_strings_space].d.strings = work_strings->strings; + work->spaces[qfo_strings_space].strings = work_strings->strings; work->spaces[qfo_strings_space].data_size = work_strings->size; return new; } @@ -266,7 +268,7 @@ alloc_data (int space, int size) if (size <= 0) linker_internal_error ("bad size for alloc_data (): %d", space); offset = defspace_alloc_loc (*work_spaces[space], size); - work->spaces[space].d.data = (*work_spaces[space])->data; + work->spaces[space].data = (*work_spaces[space])->data; work->spaces[space].data_size = (*work_spaces[space])->size; return offset; } @@ -282,6 +284,8 @@ alloc_data (int space, int size) static void resolve_external_def (defref_t *ext, defref_t *def) { + qfot_type_t *ext_type; + qfot_type_t *def_type; if (!(REF (ext)->flags & QFOD_EXTERNAL)) { def_error (REF (ext), "%s %x", WORKSTR (REF (ext)->name), REF (ext)->flags); @@ -292,7 +296,15 @@ resolve_external_def (defref_t *ext, defref_t *def) REF (def)->flags); linker_internal_error ("def is an external or local def"); } - if (REF (ext)->type != REF (def)->type) { + ext_type = WORKTYPE (REF(ext)->type); + if (ext_type->meta == ty_alias) { + ext_type = WORKTYPE (ext_type->alias.aux_type); + } + def_type = WORKTYPE (REF (def)->type); + if (def_type->meta == ty_alias) { + def_type = WORKTYPE (def_type->alias.aux_type); + } + if (ext_type != def_type) { linker_type_mismatch (REF (ext), REF (def)); return; } @@ -408,14 +420,17 @@ process_type_def (defref_t *ref, qfo_mspace_t *space, qfo_def_t *old) memcpy (new_type, old_type, old_type->size * sizeof (pr_type_t)); define_def (ref, extern_type_defs, defined_type_defs); } - // save the new address in the old def's type field so relocation - // records can be updated. + // Save the new address in the old def's type field so relocation + // records can be updated. Type encoding defs start with no type + // (type = 0). The old def's offset is not modified because it is used + // for finding the def when adjusting relocation records that point + // to fields inside the type encoding. old->type = REF (ref)->offset; - // mark the old type encoding as having been transfered, and save the + // Mark the old type encoding as having been transfered, and save the // new address in the encoding's class field so def and function types // can be updated easily. - old_type->ty = -1; - old_type->t.class = REF (ref)->offset; + old_type->meta = -1; + old_type->class = REF (ref)->offset; } } @@ -436,7 +451,7 @@ adjust_reloc_offset (qfo_reloc_t *reloc) static int add_relocs (qfo_t *qfo, int start, int count, int target) { - int size; + unsigned size; qfo_reloc_t *ireloc; qfo_reloc_t *oreloc; @@ -446,9 +461,11 @@ add_relocs (qfo_t *qfo, int start, int count, int target) oreloc = work->relocs + work->num_relocs; for ( ; work->num_relocs < size; ireloc++, oreloc++) { *oreloc = *ireloc; + // Mark the reloc as having been copied and record the new reloc record + // number in the old reloc's offset ireloc->type = -1; ireloc->offset = work->num_relocs++; - if (oreloc->space < 0 || oreloc->space >= qfo->num_spaces) { + if (oreloc->space >= qfo->num_spaces) { linker_error ("bad reloc space: %d (%d)", oreloc->space, qfo->num_spaces); oreloc->type = rel_none; @@ -485,8 +502,20 @@ add_defs (qfo_t *qfo, qfo_mspace_t *space, qfo_mspace_t *dest_space, odef->file = linker_add_string (QFOSTR (qfo, idef->file)); idef->file = -1; // mark def as copied idef->line = num_work_defrefs; // so def can be found + // In the first passs, process_type_def sets the type meta to -1 and + // class to the offset of the copied type, but the null type encodiing + // is not modified. Other defs are processed in the second pass. type = QFOTYPE(idef->type); - odef->type = type->t.class; // pointer to type in work + if (idef->type && (int) type->meta != -1) { + linker_internal_error ("reference to type that has not been " + "relocated"); + } + // Type encodings have no type (type = 0) so setting the type + // to the idef type class has no effect. + odef->type = type->class; // pointer to type in work + // don't add unused (no attached relocs) external defs to the work + // defref list so they will not cause unused object files to be + // pulled in from libraries if (odef->flags & QFOD_EXTERNAL && !odef->num_relocs) continue; ref = get_defref (odef, dest_space); @@ -507,11 +536,11 @@ add_defs (qfo_t *qfo, qfo_mspace_t *space, qfo_mspace_t *dest_space, static void add_qfo_strings (qfo_mspace_t *strings) { - const char *str = strings->d.strings; + const char *str = strings->strings; - while ((pr_uint_t) (str - strings->d.strings) < strings->data_size) { + while ((pr_uint_t) (str - strings->strings) < strings->data_size) { linker_add_string (str); - while ((pr_uint_t) (str - strings->d.strings) < strings->data_size + while ((pr_uint_t) (str - strings->strings) < strings->data_size && *str) str++; str++; // advance past the terminating nul @@ -525,11 +554,22 @@ add_qfo_strings (qfo_mspace_t *strings) static void add_code (qfo_mspace_t *code) { - codespace_addcode (work_code, code->d.code, code->data_size); - work->spaces[qfo_code_space].d.code = work_code->code; + codespace_addcode (work_code, code->code, code->data_size); + work->spaces[qfo_code_space].code = work_code->code; work->spaces[qfo_code_space].data_size = work_code->size; } +static void +align_data (int space, qfo_mspace_t *data) +{ + if (space < 0 || space >= qfo_num_spaces || !work_spaces[space]) + linker_internal_error ("bad space for align_data (): %d", space); + defspace_alloc_aligned_highwater (*work_spaces[space], 0, + 1 << data->alignment); + work->spaces[space].data = (*work_spaces[space])->data; + work->spaces[space].data_size = (*work_spaces[space])->size; +} + /** Add the data in a data space to the working qfo. \param space The space to which the data will be added. @@ -541,8 +581,8 @@ add_data (int space, qfo_mspace_t *data) if (space < 0 || space >= qfo_num_spaces || !work_spaces[space]) linker_internal_error ("bad space for add_data (): %d", space); if (data->data_size) - defspace_add_data (*work_spaces[space], data->d.data, data->data_size); - work->spaces[space].d.data = (*work_spaces[space])->data; + defspace_add_data (*work_spaces[space], data->data, data->data_size); + work->spaces[space].data = (*work_spaces[space])->data; work->spaces[space].data_size = (*work_spaces[space])->size; } @@ -567,13 +607,14 @@ add_data_space (qfo_t *qfo, qfo_mspace_t *space) ws->type = space->type; if (space->num_defs) add_defs (qfo, space, ws, process_data_def); - if (space->d.data) { + if (space->data) { int size = space->data_size * sizeof (pr_type_t); - ws->d.data = malloc (size); - memcpy (ws->d.data, space->d.data, size); + ws->data = malloc (size); + memcpy (ws->data, space->data, size); } ws->data_size = space->data_size; ws->id = space->id; + ws->alignment = space->alignment; } static defref_t * @@ -601,7 +642,7 @@ make_def (int s, const char *name, type_t *type, unsigned flags, void *val) if (val) memcpy (&def_space->data[def->offset], val, type_size (type) * sizeof (pr_type_t)); - space->d.data = def_space->data; + space->data = def_space->data; space->data_size = def_space->size; ref = get_defref (def, space); @@ -638,18 +679,18 @@ linker_find_def (const char *name) void linker_begin (void) { - int i; + unsigned i; linker_current_file = dstring_newstr (); - extern_data_defs = Hash_NewTable (16381, defs_get_key, 0, 0); - defined_data_defs = Hash_NewTable (16381, defs_get_key, 0, 0); + extern_data_defs = Hash_NewTable (16381, defs_get_key, 0, 0, 0); + defined_data_defs = Hash_NewTable (16381, defs_get_key, 0, 0, 0); - extern_field_defs = Hash_NewTable (16381, defs_get_key, 0, 0); - defined_field_defs = Hash_NewTable (16381, defs_get_key, 0, 0); + extern_field_defs = Hash_NewTable (16381, defs_get_key, 0, 0, 0); + defined_field_defs = Hash_NewTable (16381, defs_get_key, 0, 0, 0); - extern_type_defs = Hash_NewTable (16381, defs_get_key, 0, 0); - defined_type_defs = Hash_NewTable (16381, defs_get_key, 0, 0); + extern_type_defs = Hash_NewTable (16381, defs_get_key, 0, 0, 0); + defined_type_defs = Hash_NewTable (16381, defs_get_key, 0, 0, 0); work_strings = strpool_new (); work_code = codespace_new (); @@ -657,6 +698,7 @@ linker_begin (void) work_far_data = defspace_new (ds_backed); work_entity_data = defspace_new (ds_virtual); work_type_data = defspace_new (ds_backed); + work_debug_data = defspace_new (ds_backed); pr.strings = work_strings; @@ -665,23 +707,26 @@ linker_begin (void) work->num_spaces = qfo_num_spaces; work->spaces[qfo_null_space].type = qfos_null; work->spaces[qfo_strings_space].type = qfos_string; - work->spaces[qfo_strings_space].d.strings = work_strings->strings; + work->spaces[qfo_strings_space].strings = work_strings->strings; work->spaces[qfo_strings_space].data_size = work_strings->size; work->spaces[qfo_code_space].type = qfos_code; - work->spaces[qfo_code_space].d.code = work_code->code; + work->spaces[qfo_code_space].code = work_code->code; work->spaces[qfo_code_space].data_size = work_code->size; work->spaces[qfo_near_data_space].type = qfos_data; - work->spaces[qfo_near_data_space].d.data = work_near_data->data; + work->spaces[qfo_near_data_space].data = work_near_data->data; work->spaces[qfo_near_data_space].data_size = work_near_data->size; work->spaces[qfo_far_data_space].type = qfos_data; - work->spaces[qfo_far_data_space].d.data = work_far_data->data; + work->spaces[qfo_far_data_space].data = work_far_data->data; work->spaces[qfo_far_data_space].data_size = work_far_data->size; work->spaces[qfo_entity_space].type = qfos_entity; - work->spaces[qfo_entity_space].d.data = work_entity_data->data; + work->spaces[qfo_entity_space].data = work_entity_data->data; work->spaces[qfo_entity_space].data_size = work_entity_data->size; work->spaces[qfo_type_space].type = qfos_type; - work->spaces[qfo_type_space].d.data = work_type_data->data; + work->spaces[qfo_type_space].data = work_type_data->data; work->spaces[qfo_type_space].data_size = work_type_data->size; + work->spaces[qfo_debug_space].type = qfos_debug; + work->spaces[qfo_debug_space].data = work_type_data->data; + work->spaces[qfo_debug_space].data_size = work_type_data->size; for (i = 0; i < qfo_num_spaces; i++) work->spaces[i].id = i; @@ -706,7 +751,7 @@ process_null_space (qfo_t *qfo, qfo_mspace_t *space, int pass) { if (pass != 0) return 0; - if (space->defs || space->num_defs || space->d.data || space->data_size + if (space->defs || space->num_defs || space->data || space->data_size || space->id) { linker_error ("non-null null space"); return 1; @@ -732,18 +777,25 @@ process_code_space (qfo_t *qfo, qfo_mspace_t *space, int pass) static int process_data_space (qfo_t *qfo, qfo_mspace_t *space, int pass) { - if (pass != 1) - return 0; - if (space->id == qfo_near_data_space) { - add_defs (qfo, space, work->spaces + qfo_near_data_space, - process_data_def); - add_data (qfo_near_data_space, space); - } else if (space->id == qfo_far_data_space) { - add_defs (qfo, space, work->spaces + qfo_far_data_space, - process_data_def); - add_data (qfo_far_data_space, space); - } else { - add_data_space (qfo, space); + if (pass == 0) { + if (space->id == qfo_near_data_space) { + align_data (qfo_near_data_space, space); + } else if (space->id == qfo_far_data_space) { + align_data (qfo_far_data_space, space); + } else { + } + } else if (pass == 1) { + if (space->id == qfo_near_data_space) { + add_defs (qfo, space, work->spaces + qfo_near_data_space, + process_data_def); + add_data (qfo_near_data_space, space); + } else if (space->id == qfo_far_data_space) { + add_defs (qfo, space, work->spaces + qfo_far_data_space, + process_data_def); + add_data (qfo_far_data_space, space); + } else { + add_data_space (qfo, space); + } } return 0; } @@ -764,10 +816,12 @@ process_strings_space (qfo_t *qfo, qfo_mspace_t *space, int pass) static int process_entity_space (qfo_t *qfo, qfo_mspace_t *space, int pass) { - if (pass != 1) - return 0; - add_defs (qfo, space, work->spaces + qfo_entity_space, process_field_def); - add_data (qfo_entity_space, space); + if (pass == 0) { + align_data (qfo_entity_space, space); + } else if (pass == 1) { + add_defs (qfo, space, work->spaces + qfo_entity_space, process_field_def); + add_data (qfo_entity_space, space); + } return 0; } @@ -792,21 +846,28 @@ update_type_space_reloc (qfo_mspace_t *space, qfo_reloc_t *reloc) qfo_def_t dummy; qfo_def_t *def; - if (reloc->type == -1) + if (reloc->type == -1) { + // The reloc has been copied, and the record number of the new reloc + // is in the old reloc's offset. reloc = work->relocs + reloc->offset; + } dummy.offset = reloc->offset; def = (qfo_def_t *) bsearch (&dummy, space->defs, space->num_defs, sizeof (qfo_def_t), type_def_compare); - if (!def) + if (!def) { linker_internal_error ("relocation record with invalid address. " "corrupt object file?"); - reloc->offset += def->type - def->offset; + } + // The new offset of the type encoding is stored in the def's type field. + // The old offset is in the def's offset field. The reloc's offset points + // to somewhere within the type encoding. + reloc->offset = def->type + (reloc->offset - def->offset); } static int process_type_space (qfo_t *qfo, qfo_mspace_t *space, int pass) { - int i; + unsigned i; if (pass != 0) return 0; @@ -816,14 +877,15 @@ process_type_space (qfo_t *qfo, qfo_mspace_t *space, int pass) } qfo_type_defs = space; add_defs (qfo, space, work->spaces + qfo_type_space, process_type_def); - // the defs in qfo are no longer needed by the rest of the linker, so - // we're free to mess around with them + // The defs in qfo are no longer needed by the rest of the linker, so + // we're free to mess around with them. - // sort the defs by addres. Unfortunately, they will usually be in order, - // so qsort will likely be pessimistic, but oh well. + // Sort the defs by addres so they can found using bsearch when adjusting + // the targets of type encoding relocs. Unfortunately, they will usually + // be in order, so qsort will likely be pessimistic, but oh well. qsort (space->defs, space->num_defs, sizeof (qfo_def_t), type_def_compare); - // update the offsets of all relocation records that point into the type + // Update the offsets of all relocation records that point into the type // encoding space. for (i = 0; i < qfo->num_relocs; i++) { qfo_reloc_t *reloc = qfo->relocs + i; @@ -831,12 +893,26 @@ process_type_space (qfo_t *qfo, qfo_mspace_t *space, int pass) if (reloc->space != space->id) continue; update_type_space_reloc (space, reloc); - // while we're at it, update the strings so the type space strings - // are always correct. + // while we're at it, relocate all references in the type encoding + // space so the type encodings are always correct. if (reloc->type == rel_def_string) { - string_t str; + pr_string_t str; str = linker_add_string (QFOSTR (qfo, reloc->target)); QFO_STRING (work, reloc->space, reloc->offset) = str; + } else if (reloc->type == rel_def_def || reloc->type == -1) { + qfo_def_t *def; + if (reloc->type == -1) { + // The reloc has been copied, and the record number of the new + // reloc is in the old reloc's offset. + reloc = work->relocs + reloc->offset; + } + if (reloc->target >= work->spaces[reloc->space].num_defs) { + linker_error ("Invalid reloc target def %d / %d.\n", + reloc->target, qfo->num_defs); + continue; + } + def = work->spaces[reloc->space].defs + reloc->target; + QFO_INT (work, reloc->space, reloc->offset) = def->offset; } } for (i = 0; i < num_builtins; i++) { @@ -857,10 +933,26 @@ process_type_space (qfo_t *qfo, qfo_mspace_t *space, int pass) return 0; } +static int +process_debug_space (qfo_t *qfo, qfo_mspace_t *space, int pass) +{ + if (space->type != qfos_debug) { + linker_internal_error ("bad space type for process_debug_space (): %d", + space->type); + } + if (pass == 0) { + align_data (qfo_debug_space, space); + } else if (pass == 1) { + add_defs (qfo, space, work->spaces + qfo_debug_space, process_data_def); + add_data (qfo_debug_space, space); + } + return 0; +} + static void process_funcs (qfo_t *qfo) { - int size; + unsigned size; qfo_func_t *func; qfot_type_t *type; @@ -871,7 +963,7 @@ process_funcs (qfo_t *qfo) while (work->num_funcs < size) { func = work->funcs + work->num_funcs++; type = QFOTYPE(func->type); - func->type = type->t.class; + func->type = type->class; func->name = linker_add_string (QFOSTR (qfo, func->name)); func->file = linker_add_string (QFOSTR (qfo, func->file)); if (func->code > 0) @@ -888,7 +980,7 @@ process_funcs (qfo_t *qfo) static void process_lines (qfo_t *qfo) { - int size; + unsigned size; pr_lineno_t *line; if (!qfo->num_lines) @@ -920,7 +1012,7 @@ process_loose_relocs (qfo_t *qfo) qfo->num_loose_relocs * sizeof (qfo_reloc_t)); while (work_num_loose_relocs < size) { reloc = work_loose_relocs + work_num_loose_relocs++; - if (reloc->space < 0 || reloc->space >= qfo->num_spaces) { + if (reloc->space >= qfo->num_spaces) { linker_error ("bad reloc space"); reloc->type = rel_none; continue; @@ -940,6 +1032,9 @@ process_loose_relocs (qfo_t *qfo) if (reloc->type == rel_def_op) reloc->target += work_base[qfo_code_space]; adjust_reloc_offset (reloc); + if (reloc->type == rel_def_string) { + QFO_STRING (work, reloc->space, reloc->offset) = reloc->target; + } } } @@ -953,25 +1048,28 @@ linker_add_qfo (qfo_t *qfo) process_strings_space, process_entity_space, process_type_space, + process_debug_space, }; - int i; + unsigned i; int pass; qfo_mspace_t *space; qfo_type_defs = 0; - for (i = 0; i < qfo_num_spaces; i++) { - work_base[i] = work->spaces[i].data_size; - } work_func_base = work->num_funcs; for (pass = 0; pass < 2; pass++) { for (i = 0, space = qfo->spaces; i < qfo->num_spaces; i++, space++) { - if ((int) space->type < 0 || space->type > qfos_type) { + if ((int) space->type < 0 || space->type > qfos_debug) { linker_error ("bad space type"); return 1; } if (funcs[space->type] (qfo, space, pass)) return 1; } + for (i = 0; i < qfo_num_spaces; i++) { + if (pass == 0) { + work_base[i] = work->spaces[i].data_size; + } + } } process_funcs (qfo); process_lines (qfo); @@ -1027,12 +1125,13 @@ linker_add_lib (const char *libname) path_t start = {path_head, "."}; path_t *path = &start; const char *path_name = 0; - int i, j; + int i; + unsigned j; int did_something; if (strncmp (libname, "-l", 2) == 0) { while (path) { - path_name = va ("%s/lib%s.a", path->path, libname + 2); + path_name = va (0, "%s/lib%s.a", path->path, libname + 2); pack = pack_open (path_name); if (pack) break; @@ -1074,6 +1173,10 @@ linker_add_lib (const char *libname) linker_error ("error opening"); return 1; } + if (qfo->progs_version != options.code.progsversion) { + linker_error ("qfo progs version does not match target"); + return 1; + } for (j = 0; j < qfo->num_defs; j++) { qfo_def_t *def = qfo->defs + j; @@ -1101,7 +1204,7 @@ static __attribute__ ((used)) void undefined_def (qfo_def_t *def) { qfo_def_t line_def; - pr_int_t i; + pr_uint_t i; qfo_reloc_t *reloc = work->relocs + def->relocs; for (i = 0; i < def->num_relocs; i++, reloc++) { @@ -1113,14 +1216,14 @@ undefined_def (qfo_def_t *def) || reloc->type == rel_op_c_def_ofs) && work->lines) { qfo_func_t *func = work->funcs; - qfo_func_t *best = func; - pr_int_t best_dist = reloc->offset - func->code; + qfo_func_t *best = 0; + pr_uint_t best_dist; pr_lineno_t *line; - while (best_dist && func - work->funcs < work->num_funcs) { - if (func->code <= reloc->offset) { - if (best_dist < 0 - || reloc->offset - func->code < best_dist) { + while (func - work->funcs < (ptrdiff_t) work->num_funcs) { + if (func->code >= 0 + && (pr_uint_t) func->code <= reloc->offset) { + if (!best || reloc->offset - func->code < best_dist) { best = func; best_dist = reloc->offset - func->code; } @@ -1132,7 +1235,7 @@ undefined_def (qfo_def_t *def) line_def.line = best->line; if (!line->line && line->fa.func == (pr_uint_t) (best - work->funcs)) { - while (line - work->lines < work->num_lines - 1 + while (line - work->lines < (ptrdiff_t) work->num_lines - 1 && line[1].line && line[1].fa.addr <= (pr_uint_t) reloc->offset) line++; @@ -1160,7 +1263,7 @@ check_defs (void) defref_t *_d = Hash_Find (defined_data_defs, "self"); if (_d) { qfo_def_t *d = REF (_d); - if (QFO_TYPEMETA (work, d->type) == ty_none + if (QFO_TYPEMETA (work, d->type) == ty_basic && QFO_TYPETYPE (work, d->type) == ev_entity) def_warning (d, "@self and self used together"); } @@ -1195,7 +1298,7 @@ build_qfo (void) { qfo_t *qfo; int size; - int i, j; + unsigned i, j; qfo_reloc_t *reloc; qfo_def_t **defs; @@ -1205,7 +1308,7 @@ build_qfo (void) for (i = 0; i < work->num_spaces; i++) { qfo->spaces[i].type = work->spaces[i].type; qfo->spaces[i].id = work->spaces[i].id; - qfo->spaces[i].d = work->spaces[i].d; + qfo->spaces[i].data = work->spaces[i].data; qfo->spaces[i].data_size = work->spaces[i].data_size; } // allocate space for all relocs and copy in the loose relocs. bound @@ -1280,17 +1383,6 @@ build_qfo (void) reloc++; } } - for (i = 0; i < qfo->num_relocs; i++) { - qfo_def_t *def; - - reloc = qfo->relocs + i; - if (reloc->space != qfo_type_space) - continue; - if (reloc->type != rel_def_def) - continue; - def = qfo->defs + reloc->target; - QFO_INT (qfo, reloc->space, reloc->offset) = def->offset; - } return qfo; } diff --git a/tools/qfcc/source/method.c b/tools/qfcc/source/method.c index ab1c873a8..1034ab337 100644 --- a/tools/qfcc/source/method.c +++ b/tools/qfcc/source/method.c @@ -41,26 +41,27 @@ #include "QF/dstring.h" #include "QF/hash.h" -#include "QF/pr_obj.h" #include "QF/va.h" -#include "qfcc.h" +#include "QF/progs/pr_obj.h" -#include "expr.h" -#include "class.h" -#include "def.h" -#include "defspace.h" -#include "diagnostic.h" -#include "emit.h" -#include "method.h" -#include "options.h" -#include "reloc.h" -#include "shared.h" -#include "strpool.h" -#include "struct.h" -#include "symtab.h" -#include "type.h" -#include "value.h" +#include "tools/qfcc/include/qfcc.h" + +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/emit.h" +#include "tools/qfcc/include/method.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/shared.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/struct.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" static hashtab_t *known_methods; @@ -89,8 +90,12 @@ new_method (type_t *ret_type, param_t *selector, param_t *opt_params) dstring_t *name = dstring_newstr (); dstring_t *types = dstring_newstr (); - opt_params = reverse_params (opt_params); - selector = _reverse_params (selector, opt_params); + if (!ret_type) { + ret_type = &type_id; + } + + selector = reverse_params (selector); + selector = append_params (selector, opt_params); cmd->next = selector; self->next = cmd; @@ -109,11 +114,12 @@ new_method (type_t *ret_type, param_t *selector, param_t *opt_params) free (name); free (types); - //print_type (meth->type); puts (""); + //print_type (meth->type); meth->def = 0; if (!known_methods) - known_methods = Hash_NewTable (1021, method_get_key, method_free, 0); + known_methods = Hash_NewTable (1021, method_get_key, + method_free, 0, 0); Hash_Add (known_methods, meth); return meth; @@ -147,6 +153,12 @@ add_method (methodlist_t *methodlist, method_t *method) if (method->next) internal_error (0, "add_method: method loop detected"); + for (method_t *m = methodlist->head; m; m = m->next) { + if (method_compare (m, method)) { + debug (0, "dropping duplicate method: %s", method->name); + return; + } + } *methodlist->tail = method; methodlist->tail = &method->next; } @@ -199,20 +211,117 @@ new_methodlist (void) return l; } -void -copy_methods (methodlist_t *dst, methodlist_t *src) +static uintptr_t +methodset_get_hash (const void *_method, void *unused) { - method_t *s, *d; + method_t *method = (method_t *) _method; + uintptr_t hash; - for (s = src->head; s; s = s->next) { - d = malloc (sizeof (method_t)); - *d = *s; - d->next = 0; - add_method (dst, d); + hash = Hash_String (method->name); + return hash ^ (method->instance << 3); +} + +static int +methodset_compare (const void *_m1, const void *_m2, void *unused) +{ + method_t *m1 = (method_t *) _m1; + method_t *m2 = (method_t *) _m2; + int cmp; + + cmp = strcmp (m1->name, m2->name) == 0; + return cmp && m1->instance == m2->instance; +} + +methodset_t * +new_methodset (void) +{ + methodset_t *s = malloc (sizeof (*s)); + s->tab = Hash_NewTable (31, 0, 0, 0, 0); + Hash_SetHashCompare (s->tab, methodset_get_hash, methodset_compare); + return s; +} + +void +methodset_add_methods (methodset_t *methodset, methodlist_t *methods) +{ + method_t *m; + + for (m = methods->head; m; m = m->next) { + Hash_AddElement (methodset->tab, m); } } int +methodset_contains_method (methodset_t *methodset, method_t *method) +{ + return Hash_FindElement (methodset->tab, method) != 0; +} + +static int __attribute__((pure)) +method_in_list (methodlist_t *method_list, method_t *method) +{ + method_t *m; + + for (m = method_list->head; m; m = m->next) { + if (method_compare (m, method)) { + return 1; + } + } + return 0; +} + +void +merge_method_lists (methodlist_t *dst, methodlist_t *src) +{ + while (src->head) { + method_t *s = src->head; + src->head = s->next; + s->next = 0; + if (method_in_list (dst, s)) { + debug (0, "dropping duplicate method: %s", s->name); + //FIXME this free is currently erroneous as it remains in + //known_methods, but it may also be a leak, thus only + //commented out for now. + //free (s); + } else { + // add_method does the duplicate check + *dst->tail = s; + dst->tail = &s->next; + } + } + free (src); +} + +void +copy_methods (methodlist_t *dst, methodlist_t *src, methodset_t *except) +{ + method_t *s, *d; + param_t *self; + + for (s = src->head; s; s = s->next) { + if (methodset_contains_method (except, s) || method_in_list (dst, s)) { + debug (0, "skipping duplicate method: %s", s->name); + continue; + } + d = malloc (sizeof (method_t)); + *d = *s; + // The above is only a shallow copy and thus even though the methods + // are not shared between the source and destination lists, the + // parameters are. Thus, duplicate the self (first) parameter so + // changing its type to match the class into which it is inserted does + // not affect the source list. The rest of the parameters do not need + // to be copied as they will not be altered. + self = malloc (sizeof (param_t)); + *self = *d->params; + d->params = self; + d->next = 0; + // add_method does the duplicate check + *dst->tail = d; + dst->tail = &d->next; + } +} + +__attribute__((pure)) int method_compare (method_t *m1, method_t *m2) { if (m1->instance != m2->instance) @@ -275,6 +384,20 @@ find_method (const char *sel_name) return Hash_Find (known_methods, sel_name); } +method_t * +methodlist_find_method (methodlist_t *methodlist, selector_t *selector, + int instance) +{ + method_t *m; + + for (m = methodlist->head; m; m = m->next) { + if (m->instance == instance && strcmp (selector->name, m->name) == 0) { + return m; + } + } + return 0; +} + void selector_name (dstring_t *sel_id, keywordarg_t *selector) { @@ -341,9 +464,9 @@ selector_index (const char *sel_id) selector_t *sel = &_sel; if (!sel_hash) { - sel_hash = Hash_NewTable (1021, 0, 0, 0); + sel_hash = Hash_NewTable (1021, 0, 0, 0, 0); Hash_SetHashCompare (sel_hash, sel_get_hash, sel_compare); - sel_index_hash = Hash_NewTable (1021, 0, 0, 0); + sel_index_hash = Hash_NewTable (1021, 0, 0, 0, 0); Hash_SetHashCompare (sel_index_hash, sel_index_get_hash, sel_index_compare); } @@ -364,12 +487,15 @@ get_selector (expr_t *sel) { selector_t _sel = {0, 0, 0}; - if (sel->type != ex_expr && sel->e.expr.op != '&' - && sel->e.expr.type != &type_SEL) { + if (sel->type == ex_selector) { + return sel->e.selector.sel; + } + if (sel->type != ex_address && !sel->e.address.offset + && !is_SEL(sel->e.address.type)) { error (sel, "not a selector"); return 0; } - _sel.index = expr_short (sel->e.expr.e2); + _sel.index = expr_short (sel->e.address.offset); _sel.index /= type_size (type_SEL.t.fldptr.type); return (selector_t *) Hash_FindElement (sel_index_hash, &_sel); } @@ -410,7 +536,7 @@ emit_selectors (void) static void emit_methods_next (def_t *def, void *data, int index) { - if (def->type != &type_pointer) + if (!is_ptr(def->type)) internal_error (0, "%s: expected pointer def", __FUNCTION__); D_INT (def) = 0; } @@ -420,8 +546,8 @@ emit_methods_count (def_t *def, void *data, int index) { methodlist_t *methods = (methodlist_t *) data; - if (def->type != &type_integer) - internal_error (0, "%s: expected integer def", __FUNCTION__); + if (!is_int(def->type)) + internal_error (0, "%s: expected int def", __FUNCTION__); D_INT (def) = methods->count; } @@ -432,7 +558,7 @@ emit_methods_list_item (def_t *def, void *data, int index) method_t *m; pr_method_t *meth; - if (!is_array (def->type) || def->type->t.array.type != &type_obj_method) + if (!is_array (def->type) || !is_method(def->type->t.array.type)) internal_error (0, "%s: expected array of method def", __FUNCTION__); if (index < 0 || index >= methods->count) @@ -462,9 +588,9 @@ def_t * emit_methods (methodlist_t *methods, const char *name, int instance) { static struct_def_t methods_struct[] = { - {"method_next", &type_pointer, emit_methods_next}, - {"method_count", &type_integer, emit_methods_count}, - {"method_list", 0, emit_methods_list_item}, + {"method_next", &type_ptr, emit_methods_next}, + {"method_count", &type_int, emit_methods_count}, + {"method_list", 0, emit_methods_list_item}, {0, 0} }; const char *type = instance ? "INSTANCE" : "CLASS"; @@ -474,7 +600,7 @@ emit_methods (methodlist_t *methods, const char *name, int instance) if (!methods) return 0; - for (count = 0, m = methods->head; m; m = m->next) + for (count = 0, m = methods->head; m; m = m->next) { if (!m->instance == !instance) { if (!m->def && options.warnings.unimplemented) { warning (0, "Method `%c%s' not implemented", @@ -483,14 +609,15 @@ emit_methods (methodlist_t *methods, const char *name, int instance) if (m->def) count++; } + } if (!count) return 0; methods->count = count; methods->instance = instance; - methods_struct[2].type = array_type (&type_obj_method, count); - return emit_structure (va ("_OBJ_%s_METHODS_%s", type, name), 's', - methods_struct, 0, methods, sc_static); + methods_struct[2].type = array_type (&type_method, count); + return emit_structure (va (0, "_OBJ_%s_METHODS_%s", type, name), 's', + methods_struct, 0, methods, 0, sc_static); } static void @@ -498,8 +625,8 @@ emit_method_list_count (def_t *def, void *data, int index) { methodlist_t *methods = (methodlist_t *) data; - if (def->type != &type_integer) - internal_error (0, "%s: expected integer def", __FUNCTION__); + if (!is_int(def->type)) + internal_error (0, "%s: expected int def", __FUNCTION__); D_INT (def) = methods->count; } @@ -510,9 +637,11 @@ emit_method_list_item (def_t *def, void *data, int index) method_t *m; pr_method_description_t *desc; - if (def->type != &type_obj_method_description) - internal_error (0, "%s: expected method_descripting def", + if (!is_array (def->type) + || !is_method_description(def->type->t.array.type)) { + internal_error (0, "%s: expected array of method_description def", __FUNCTION__); + } if (index < 0 || index >= methods->count) internal_error (0, "%s: out of bounds index: %d %d", __FUNCTION__, index, methods->count); @@ -520,7 +649,7 @@ emit_method_list_item (def_t *def, void *data, int index) desc = D_POINTER (pr_method_description_t, def); for (m = methods->head; m; m = m->next) { - if (!m->instance != !methods->instance || !m->def) + if (!m->instance != !methods->instance) continue; if (!index--) break; @@ -534,7 +663,7 @@ emit_method_descriptions (methodlist_t *methods, const char *name, int instance) { static struct_def_t method_list_struct[] = { - {"count", &type_integer, emit_method_list_count}, + {"count", &type_int, emit_method_list_count}, {"method_list", 0, emit_method_list_item}, {0, 0} }; @@ -546,7 +675,7 @@ emit_method_descriptions (methodlist_t *methods, const char *name, return 0; for (count = 0, m = methods->head; m; m = m->next) - if (!m->instance == !instance && m->def) + if (!m->instance == !instance) count++; if (!count) return 0; @@ -554,10 +683,9 @@ emit_method_descriptions (methodlist_t *methods, const char *name, methods->count = count; methods->instance = instance; - method_list_struct[1].type = array_type (&type_obj_method_description, - count); - return emit_structure (va ("_OBJ_%s_METHODS_%s", type, name), 's', - method_list_struct, 0, methods, sc_static); + method_list_struct[1].type = array_type (&type_method_description, count); + return emit_structure (va (0, "_OBJ_%s_METHODS_%s", type, name), 's', + method_list_struct, 0, methods, 0, sc_static); } void @@ -585,8 +713,8 @@ method_check_params (method_t *method, expr_t *args) for (count = 0, a = args; a; a = a->next) count++; - if (count > MAX_PARMS) - return error (args, "more than %d parameters", MAX_PARMS); + if (count > PR_MAX_PARAMS) + return error (args, "more than %d parameters", PR_MAX_PARAMS); if (mtype->t.func.num_params >= 0) param_count = mtype->t.func.num_params; @@ -603,20 +731,27 @@ method_check_params (method_t *method, expr_t *args) arg_list[i--] = a; for (i = 2; i < count; i++) { expr_t *e = arg_list[i]; - type_t *t = get_type (e); + type_t *arg_type = mtype->t.func.param_types[i]; + type_t *t; - if (!t) + if (e->type == ex_compound) { + e = expr_file_line (initialized_temp_expr (arg_type, e), e); + } + t = get_type (e); + if (!t) { return e; + } if (i < param_count) { - if (e->type != ex_nil) - if (!type_assignable (mtype->t.func.param_types[i], t)) { - err = param_mismatch (e, i - 1, method->name, - mtype->t.func.param_types[i], t); + if (e->type != ex_nil) { + if (!type_assignable (arg_type, t)) { + err = param_mismatch (e, i - 1, method->name, arg_type, t); } + } } else { - if (is_integer_val (e) && options.warnings.vararg_integer) - warning (e, "passing integer consant into ... function"); + if (is_int_val (e) && options.warnings.vararg_integer) { + warning (e, "passing int consant into ... function"); + } } } free (arg_list); diff --git a/tools/qfcc/source/obj_file.c b/tools/qfcc/source/obj_file.c index 1c81381f1..3dc11fd7d 100644 --- a/tools/qfcc/source/obj_file.c +++ b/tools/qfcc/source/obj_file.c @@ -44,23 +44,25 @@ #include "QF/quakeio.h" #include "QF/va.h" -#include "codespace.h" -#include "debug.h" -#include "def.h" -#include "defspace.h" -#include "emit.h" -#include "expr.h" -#include "function.h" -#include "obj_file.h" -#include "obj_type.h" -#include "options.h" -#include "qfcc.h" -#include "reloc.h" -#include "statements.h" -#include "strpool.h" -#include "symtab.h" -#include "type.h" -#include "value.h" +#include "compat.h" + +#include "tools/qfcc/include/codespace.h" +#include "tools/qfcc/include/debug.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/emit.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/obj_file.h" +#include "tools/qfcc/include/obj_type.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/statements.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" static int count_relocs (reloc_t *r) @@ -167,9 +169,9 @@ qfo_count_function_stuff (qfo_t *qfo, function_t *functions) for (func = functions; func; func = func->next) { qfo->num_funcs++; qfo->num_relocs += count_relocs (func->refs); - if (func->symtab && func->symtab->space) { + if (func->locals && func->locals->space) { qfo->num_spaces++; - qfo_count_space_stuff (qfo, func->symtab->space); + qfo_count_space_stuff (qfo, func->locals->space); } } } @@ -181,6 +183,7 @@ qfo_count_stuff (qfo_t *qfo, pr_info_t *pr) qfo_count_space_stuff (qfo, pr->far_data); qfo_count_space_stuff (qfo, pr->entity_data); qfo_count_space_stuff (qfo, pr->type_data); + qfo_count_space_stuff (qfo, pr->debug_data); qfo_count_function_stuff (qfo, pr->func_head); qfo->num_relocs += count_relocs (pr->relocs); } @@ -188,11 +191,16 @@ qfo_count_stuff (qfo_t *qfo, pr_info_t *pr) static void qfo_init_string_space (qfo_t *qfo, qfo_mspace_t *space, strpool_t *strings) { + size_t size = strings->size * sizeof (*strings->strings); strings->qfo_space = space - qfo->spaces; space->type = qfos_string; space->num_defs = 0; space->defs = 0; - space->d.strings = strings->strings; + space->strings = 0; + if (strings->strings) { + space->strings = malloc (size); + memcpy (space->strings, strings->strings, size); + } space->data_size = strings->size; space->id = qfo_strings_space; } @@ -200,11 +208,16 @@ qfo_init_string_space (qfo_t *qfo, qfo_mspace_t *space, strpool_t *strings) static void qfo_init_code_space (qfo_t *qfo, qfo_mspace_t *space, codespace_t *code) { + size_t size = code->size * sizeof (*code->code); code->qfo_space = space - qfo->spaces; space->type = qfos_code; space->num_defs = 0; space->defs = 0; - space->d.code = code->code; + space->code = 0; + if (code->code) { + space->code = malloc (size); + memcpy (space->code, code->code, size); + } space->data_size = code->size; space->id = qfo_code_space; } @@ -213,12 +226,18 @@ static void qfo_init_data_space (qfo_t *qfo, qfo_def_t **defs, qfo_reloc_t **relocs, qfo_mspace_t *space, defspace_t *data) { + size_t size = data->size * sizeof (*data->data); data->qfo_space = space - qfo->spaces; space->type = qfos_data; space->defs = *defs; space->num_defs = qfo_encode_defs (qfo, data->defs, defs, relocs); - space->d.data = data->data; + space->data = 0; + if (data->data) { + space->data = malloc (size); + memcpy (space->data, data->data, size); + } space->data_size = data->size; + space->alignment = qfo_log2 (data->alignment); } static void @@ -229,24 +248,48 @@ qfo_init_entity_space (qfo_t *qfo, qfo_def_t **defs, qfo_reloc_t **relocs, space->type = qfos_entity; space->defs = *defs; space->num_defs = qfo_encode_defs (qfo, data->defs, defs, relocs); - space->d.data = 0; + space->data = 0; space->data_size = data->size; space->id = qfo_entity_space; + space->alignment = qfo_log2 (data->alignment); } static void qfo_init_type_space (qfo_t *qfo, qfo_def_t **defs, qfo_reloc_t **relocs, qfo_mspace_t *space, defspace_t *data) { + size_t size = data->size * sizeof (*data->data); data->qfo_space = space - qfo->spaces; space->type = qfos_type; space->defs = *defs; space->num_defs = qfo_encode_defs (qfo, data->defs, defs, relocs); - space->d.data = data->data; + space->data = 0; + if (data->data) { + space->data = malloc (size); + memcpy (space->data, data->data, size); + } space->data_size = data->size; space->id = qfo_type_space; } +static void +qfo_init_debug_space (qfo_t *qfo, qfo_def_t **defs, qfo_reloc_t **relocs, + qfo_mspace_t *space, defspace_t *data) +{ + size_t size = data->size * sizeof (*data->data); + data->qfo_space = space - qfo->spaces; + space->type = qfos_debug; + space->defs = *defs; + space->num_defs = qfo_encode_defs (qfo, data->defs, defs, relocs); + space->data = 0; + if (data->data) { + space->data = malloc (size); + memcpy (space->data, data->data, size); + } + space->data_size = data->size; + space->id = qfo_debug_space; +} + static void qfo_encode_functions (qfo_t *qfo, qfo_def_t **defs, qfo_reloc_t **relocs, qfo_mspace_t *space, function_t *functions) @@ -263,14 +306,15 @@ qfo_encode_functions (qfo_t *qfo, qfo_def_t **defs, qfo_reloc_t **relocs, if (f->builtin) // FIXME redundant q->code = -f->builtin; q->def = f->def->qfo_def; - if (f->symtab && f->symtab->space) { - space->id = f->symtab->space->qfo_space; - qfo_init_data_space (qfo, defs, relocs, space++, f->symtab->space); - q->locals_space = f->symtab->space->qfo_space; + if (f->locals && f->locals->space) { + space->id = f->locals->space->qfo_space; + qfo_init_data_space (qfo, defs, relocs, space++, f->locals->space); + q->locals_space = f->locals->space->qfo_space; } q->line_info = f->line_info; q->relocs = *relocs - qfo->relocs; q->num_relocs = qfo_encode_relocs (f->refs, relocs, q - qfo->funcs); + q->params_start = f->params_start; } } @@ -311,6 +355,8 @@ qfo_from_progs (pr_info_t *pr) pr->entity_data); qfo_init_type_space (qfo, &def, &reloc, &qfo->spaces[qfo_type_space], pr->type_data); + qfo_init_debug_space (qfo, &def, &reloc, &qfo->spaces[qfo_debug_space], + pr->debug_data); qfo_encode_functions (qfo, &def, &reloc, qfo->spaces + qfo_num_spaces, pr->func_head); @@ -348,18 +394,20 @@ qfo_space_size (qfo_mspace_t *space) case qfos_null: return 0; case qfos_code: - return space->data_size * sizeof (*space->d.code); + return space->data_size * sizeof (*space->code); case qfos_data: - // data size > 0 but d.data == null -> all data is zero - if (!space->d.data) + // data size > 0 but data == null -> all data is zero + if (!space->data) return 0; - return space->data_size * sizeof (*space->d.data); + return space->data_size * sizeof (*space->data); case qfos_string: - return space->data_size * sizeof (*space->d.strings); + return space->data_size * sizeof (*space->strings); case qfos_entity: return 0; case qfos_type: - return space->data_size * sizeof (*space->d.data); + return space->data_size * sizeof (*space->data); + case qfos_debug: + return space->data_size * sizeof (*space->data); } return 0; } @@ -385,8 +433,9 @@ qfo_byteswap_space (void *space, int size, qfos_type_t type) case qfos_data: case qfos_entity: case qfos_type: + case qfos_debug: for (val = (pr_type_t *) space, c = 0; c < size; c++, val++) - val->integer_var = LittleLong (val->integer_var); + val->value = LittleLong (val->value); break; } } @@ -394,9 +443,9 @@ qfo_byteswap_space (void *space, int size, qfos_type_t type) int qfo_write (qfo_t *qfo, const char *filename) { - int size; + unsigned size; int space_offset; - int i; + unsigned i; byte *data; qfo_header_t *header; qfo_space_t *spaces; @@ -408,8 +457,10 @@ qfo_write (qfo_t *qfo, const char *filename) QFile *file; file = Qopen (filename, options.gzip ? "wbz9" : "wb"); - if (!file) + if (!file) { + perror (va (0, "failed to open %s for writing", filename)); return -1; + } size = sizeof (qfo_header_t); size += sizeof (qfo_space_t) * qfo->num_spaces; @@ -430,6 +481,7 @@ qfo_write (qfo_t *qfo, const char *filename) header->num_funcs = LittleLong (qfo->num_funcs); header->num_lines = LittleLong (qfo->num_lines); header->num_loose_relocs = LittleLong (qfo->num_loose_relocs); + header->progs_version = LittleLong (options.code.progsversion); spaces = (qfo_space_t *) &header[1]; relocs = (qfo_reloc_t *) &spaces[qfo->num_spaces]; defs = (qfo_def_t *) &relocs[qfo->num_relocs]; @@ -441,16 +493,17 @@ qfo_write (qfo_t *qfo, const char *filename) if (qfo->spaces[i].defs) spaces[i].defs = LittleLong (qfo->spaces[i].defs - qfo->defs); spaces[i].num_defs = LittleLong (qfo->spaces[i].num_defs); - if (qfo->spaces[i].d.data) { + if (qfo->spaces[i].data) { int space_size = qfo_space_size (qfo->spaces + i); spaces[i].data = LittleLong (space_data - data); - memcpy (space_data, qfo->spaces[i].d.data, space_size); + memcpy (space_data, qfo->spaces[i].data, space_size); qfo_byteswap_space (space_data, qfo->spaces[i].data_size, qfo->spaces[i].type); space_data += RUP (space_size, 16); } spaces[i].data_size = LittleLong (qfo->spaces[i].data_size); spaces[i].id = LittleLong (qfo->spaces[i].id); + spaces[i].alignment = LittleLong (qfo->spaces[i].alignment); } for (i = 0; i < qfo->num_relocs; i++) { relocs[i].space = LittleLong (qfo->relocs[i].space); @@ -479,6 +532,7 @@ qfo_write (qfo_t *qfo, const char *filename) funcs[i].line_info = LittleLong (qfo->funcs[i].line_info); funcs[i].relocs = LittleLong (qfo->funcs[i].relocs); funcs[i].num_relocs = LittleLong (qfo->funcs[i].num_relocs); + funcs[i].params_start = LittleLong (qfo->funcs[i].params_start); } for (i = 0; i < qfo->num_lines; i++) { lines[i].fa.addr = LittleLong (qfo->lines[i].fa.addr); @@ -499,7 +553,7 @@ qfo_read (QFile *file) qfo_header_t *header; qfo_space_t *spaces; qfo_t *qfo; - int i; + unsigned i; size = Qfilesize (file); data = malloc (size); @@ -512,6 +566,21 @@ qfo_read (QFile *file) free (data); return 0; } + header->progs_version = LittleLong (header->progs_version); + if (header->progs_version != PROG_ID_VERSION + && header->progs_version != PROG_V6P_VERSION + && header->progs_version != PROG_VERSION) { + fprintf (stderr, "not a compatible qfo file\n"); + free (data); + return 0; + } + // qfprogs leaves progsversion as 0 + if (options.code.progsversion + && header->progs_version != options.code.progsversion) { + fprintf (stderr, "qfo file target VM does not match current target\n"); + free (data); + return 0; + } qfo = calloc (1, sizeof (qfo_t)); qfo->num_spaces = LittleLong (header->num_spaces); @@ -520,6 +589,7 @@ qfo_read (QFile *file) qfo->num_funcs = LittleLong (header->num_funcs); qfo->num_lines = LittleLong (header->num_lines); qfo->num_loose_relocs = LittleLong (header->num_loose_relocs); + qfo->progs_version = header->progs_version; //already swapped spaces = (qfo_space_t *) &header[1]; qfo->data = data; @@ -536,11 +606,12 @@ qfo_read (QFile *file) qfo->spaces[i].defs = qfo->defs + LittleLong (spaces[i].defs); qfo->spaces[i].data_size = LittleLong (spaces[i].data_size); if (spaces[i].data) { - qfo->spaces[i].d.strings = data + LittleLong (spaces[i].data); - qfo_byteswap_space (qfo->spaces[i].d.data, + qfo->spaces[i].strings = data + LittleLong (spaces[i].data); + qfo_byteswap_space (qfo->spaces[i].data, qfo->spaces[i].data_size, qfo->spaces[i].type); } qfo->spaces[i].id = LittleLong (spaces[i].id); + qfo->spaces[i].alignment = LittleLong (spaces[i].alignment); } for (i = 0; i < qfo->num_relocs; i++) { qfo->relocs[i].space = LittleLong (qfo->relocs[i].space); @@ -605,9 +676,9 @@ qfo_delete (qfo_t *qfo) if (qfo->data) { free (qfo->data); } else { - int i; + unsigned i; for (i = 0; i < qfo->num_spaces; i++) - free (qfo->spaces->d.data); + free (qfo->spaces[i].data); free (qfo->relocs); free (qfo->defs); free (qfo->funcs); @@ -618,24 +689,26 @@ qfo_delete (qfo_t *qfo) } static etype_t -get_def_type (qfo_t *qfo, pointer_t type) +get_def_type (qfo_t *qfo, pr_ptr_t type) { qfot_type_t *type_def; if (type >= qfo->spaces[qfo_type_space].data_size) return ev_void; type_def = QFO_POINTER (qfo, qfo_type_space, qfot_type_t, type); - switch ((ty_meta_e)type_def->ty) { - case ty_none: + switch ((ty_meta_e)type_def->meta) { + case ty_alias: //XXX + case ty_basic: + case ty_handle: //XXX // field, pointer and function types store their basic type in // the same location. - return type_def->t.type; + return type_def->type; case ty_struct: case ty_union: return ev_invalid; case ty_enum: if (options.code.progsversion == PROG_ID_VERSION) return ev_float; - return ev_integer; + return ev_int; case ty_array: case ty_class: return ev_invalid; @@ -643,42 +716,104 @@ get_def_type (qfo_t *qfo, pointer_t type) return ev_invalid; } -static etype_t -get_type_size (qfo_t *qfo, pointer_t type) +static __attribute__((pure)) int +get_type_size (qfo_t *qfo, pr_ptr_t type) { qfot_type_t *type_def; int i, size; if (type >= qfo->spaces[qfo_type_space].data_size) return 1; type_def = QFO_POINTER (qfo, qfo_type_space, qfot_type_t, type); - switch ((ty_meta_e)type_def->ty) { - case ty_none: + switch ((ty_meta_e)type_def->meta) { + case ty_alias: + return get_type_size (qfo, type_def->alias.aux_type); + case ty_handle: //XXX + case ty_basic: // field, pointer and function types store their basic type in // the same location. - return pr_type_size[type_def->t.type]; + return pr_type_size[type_def->type]; case ty_struct: - for (i = size = 0; i < type_def->t.strct.num_fields; i++) - size += get_type_size (qfo, type_def->t.strct.fields[i].type); + for (i = size = 0; i < type_def->strct.num_fields; i++) + size += get_type_size (qfo, type_def->strct.fields[i].type); return size; case ty_union: - for (i = size = 0; i < type_def->t.strct.num_fields; i++) { + for (i = size = 0; i < type_def->strct.num_fields; i++) { int s; - s = get_type_size (qfo, type_def->t.strct.fields[i].type); + s = get_type_size (qfo, type_def->strct.fields[i].type); if (s > size) size = s; } return size; case ty_enum: - return pr_type_size[ev_integer]; + return pr_type_size[ev_int]; case ty_array: - return type_def->t.array.size - * get_type_size (qfo, type_def->t.array.type); + return type_def->array.size + * get_type_size (qfo, type_def->array.type); case ty_class: return 0; // FIXME } return 0; } +int +qfo_log2 (unsigned x) +{ + int log2 = 0; + + while (x > 1) { + x >>= 1; + log2++; + } + return log2; +} + +static __attribute__((pure)) int +get_type_alignment_log (qfo_t *qfo, pr_ptr_t type) +{ + qfot_type_t *type_def; + int i, alignment; + if (type >= qfo->spaces[qfo_type_space].data_size) + return 0; + type_def = QFO_POINTER (qfo, qfo_type_space, qfot_type_t, type); + switch ((ty_meta_e)type_def->meta) { + case ty_alias: + return get_type_alignment_log (qfo, type_def->alias.aux_type); + case ty_handle: //XXX + case ty_basic: + // field, pointer and function types store their basic type in + // the same location. + return qfo_log2 (ev_types[type_def->type]->alignment); + case ty_struct: + case ty_union: + for (i = alignment = 0; i < type_def->strct.num_fields; i++) { + qfot_var_t *field = type_def->strct.fields + i; + int a; + a = get_type_alignment_log (qfo, field->type); + if (a > alignment) { + alignment = a; + } + } + return alignment; + case ty_enum: + return qfo_log2 (ev_types[ev_int]->alignment); + case ty_array: + return get_type_alignment_log (qfo, type_def->array.type); + case ty_class: + return 0; // FIXME + } + return 0; +} + +static __attribute__((pure)) dparmsize_t +get_paramsize (qfo_t *qfo, pr_ptr_t type) +{ + dparmsize_t paramsize = { + get_type_size (qfo, type), + get_type_alignment_log (qfo, type), + }; + return paramsize; +} + static void function_params (qfo_t *qfo, qfo_func_t *func, dfunction_t *df) { @@ -689,21 +824,26 @@ function_params (qfo_t *qfo, qfo_func_t *func, dfunction_t *df) if (func->type >= qfo->spaces[qfo_type_space].data_size) return; type = QFO_POINTER (qfo, qfo_type_space, qfot_type_t, func->type); - if (type->ty != ty_none && type->t.type != ev_func) + if (type->meta == ty_alias) { + type = QFO_POINTER (qfo, qfo_type_space, qfot_type_t, + type->alias.aux_type); + } + if (type->meta != ty_basic || type->type != ev_func) return; - df->numparms = num_params = type->t.func.num_params; + df->numparams = num_params = type->func.num_params; if (num_params < 0) num_params = ~num_params; - for (i = 0; i < num_params; i++) - df->parm_size[i] = get_type_size (qfo, type->t.func.param_types[i]); + for (i = 0; i < num_params; i++) { + df->param_size[i] = get_paramsize (qfo, type->func.param_types[i]); + } } static void -convert_def (qfo_t *qfo, const qfo_def_t *def, ddef_t *ddef) +qfo_def_to_ddef (qfo_t *qfo, const qfo_def_t *def, ddef_t *ddef) { ddef->type = get_def_type (qfo, def->type); ddef->ofs = def->offset; - ddef->s_name = def->name; + ddef->name = def->name; if (!(def->flags & QFOD_NOSAVE) && !(def->flags & QFOD_CONSTANT) && (def->flags & QFOD_GLOBAL) @@ -712,10 +852,20 @@ convert_def (qfo_t *qfo, const qfo_def_t *def, ddef_t *ddef) ddef->type |= DEF_SAVEGLOBAL; } +static void +qfo_def_to_prdef (qfo_t *qfo, const qfo_def_t *def, pr_def_t *prdef) +{ + prdef->type = get_def_type (qfo, def->type); + prdef->size = get_type_size (qfo, def->type); + prdef->ofs = def->offset; + prdef->name = def->name; + prdef->type_encoding = def->type; +} + static void qfo_relocate_refs (qfo_t *qfo) { - int i; + unsigned i; qfo_reloc_t *reloc; for (i = 0, reloc = qfo->relocs; i < qfo->num_relocs; i++, reloc++) { @@ -773,10 +923,87 @@ qfo_relocate_refs (qfo_t *qfo) } } -dprograms_t * -qfo_to_progs (qfo_t *qfo, int *size) +static unsigned +align_globals_size (unsigned size) +{ + if (options.code.progsversion == PROG_ID_VERSION) + return size; + return RUP (size, 16 / sizeof (pr_type_t)); +} + +static pr_uint_t +qfo_count_locals (const qfo_t *qfo, pr_uint_t *big_locals) +{ + if (options.code.progsversion == PROG_VERSION) { + // Ruamoko progs keep their locals on the stack rather than a + // "protected" area in the globals + *big_locals = 0; + return 0; + } + + pr_uint_t locals_size = 0; + for (pr_uint_t i = qfo_num_spaces; i < qfo->num_spaces; i++) { + if (options.code.local_merging) { + if (locals_size < qfo->spaces[i].data_size) { + locals_size = qfo->spaces[i].data_size; + *big_locals = i; + } + } else { + locals_size += align_globals_size (qfo->spaces[i].data_size); + } + } + return locals_size; +} + +typedef struct globals_info_s { + pr_uint_t locals_start; + pr_uint_t locals_size; + pr_uint_t big_locals; + pr_uint_t near_data_size; + pr_uint_t type_encodings_start; + pr_uint_t xdefs_start; + pr_uint_t xdefs_size; + pr_uint_t globals_size; +} globals_info_t; + + +static globals_info_t __attribute__((pure)) +qfo_count_globals (qfo_t *qfo, dprograms_t *progs, int word_align) +{ + globals_info_t info = {}; + info.globals_size = qfo->spaces[qfo_near_data_space].data_size; + info.globals_size = RUP (info.globals_size, word_align); + + info.locals_start = info.globals_size; + info.locals_size = qfo_count_locals (qfo, &info.big_locals); + info.globals_size += info.locals_size; + info.near_data_size = info.globals_size; + + info.globals_size = RUP (info.globals_size, word_align); + info.globals_size += qfo->spaces[qfo_far_data_space].data_size; + + info.type_encodings_start = info.globals_size; + info.globals_size += qfo->spaces[qfo_type_space].data_size; + info.globals_size = RUP (info.globals_size, type_xdef.alignment); + + info.xdefs_start = info.globals_size; + info.xdefs_size = progs->globaldefs.count + progs->fielddefs.count; + info.xdefs_size *= type_size (&type_xdef); + info.globals_size += info.xdefs_size; + + return info; +} + +static pr_uint_t +qfo_next_offset (pr_chunk_t chunk, pr_uint_t size, pr_uint_t align) +{ + size *= chunk.count; + return RUP (chunk.offset + size, align); +} + +dprograms_t * +qfo_to_progs (qfo_t *in_qfo, int *size) { - byte *data; char *strings; dstatement_t *statements; dfunction_t *functions; @@ -786,87 +1013,103 @@ qfo_to_progs (qfo_t *qfo, int *size) pr_type_t *locals; pr_type_t *far_data; pr_type_t *type_data; + pr_type_t *xdef_data; dprograms_t *progs; qfo_def_t *types_def = 0; - int i, j; - unsigned locals_size = 0; - int locals_start; - int big_locals = 0; + qfo_def_t *xdefs_def = 0; + unsigned i, j; int big_func = 0; + pr_xdefs_t *xdefs = 0; + xdef_t *xdef; + + // id progs were aligned to only 4 bytes, but 8 is much more reasonable + pr_uint_t byte_align = 8; + if (options.code.progsversion == PROG_VERSION) { + byte_align = __alignof__ (pr_lvec4_t); + } else if (options.code.progsversion == PROG_V6P_VERSION) { + byte_align = 16; + } + pr_uint_t word_align = byte_align / sizeof (pr_int_t); + + + qfo_t *qfo = alloca (sizeof (qfo_t) + + in_qfo->num_spaces * sizeof (qfo_mspace_t)); + *qfo = *in_qfo; + qfo->spaces = (qfo_mspace_t *) (qfo + 1); + for (i = 0; i < qfo->num_spaces; i++) { + qfo->spaces[i] = in_qfo->spaces[i]; + } *size = RUP (sizeof (dprograms_t), 16); progs = calloc (1, *size); progs->version = options.code.progsversion; - progs->numstatements = qfo->spaces[qfo_code_space].data_size; - progs->numglobaldefs = qfo->spaces[qfo_near_data_space].num_defs; - //FIXME ddef offsets are 16 bits - progs->numglobaldefs += qfo->spaces[qfo_far_data_space].num_defs; - progs->numfielddefs = qfo->spaces[qfo_entity_space].num_defs; - progs->numfunctions = qfo->num_funcs + 1; - progs->numstrings = qfo->spaces[qfo_strings_space].data_size; - progs->numglobals = qfo->spaces[qfo_near_data_space].data_size; - progs->numglobals += qfo->spaces[qfo_far_data_space].data_size; - progs->numglobals += qfo->spaces[qfo_type_space].data_size; - locals_start = qfo->spaces[qfo_near_data_space].data_size; - for (i = qfo_num_spaces; i < qfo->num_spaces; i++) { - if (options.code.local_merging) { - if (locals_size < qfo->spaces[i].data_size) { - locals_size = qfo->spaces[i].data_size; - big_locals = i; - } - } else { - locals_size += qfo->spaces[i].data_size; - } - } - progs->numglobals += locals_size; - progs->numglobals = RUP (progs->numglobals, 16 / sizeof (pr_type_t)); + // crc is set in qfcc.c if enabled + + // these are in order in which they usually appear in the file rather + // than the order they appear in the struct, though with the offsets + // it doesn't matter too much. However, as people expect a certain + // layout, it does matter enough to preserve the traditional file order. + progs->strings.offset = *size; + progs->strings.count = qfo->spaces[qfo_strings_space].data_size; + + progs->statements.offset = qfo_next_offset (progs->strings, + sizeof (char), + byte_align); + progs->statements.count = qfo->spaces[qfo_code_space].data_size; + + progs->functions.offset = qfo_next_offset (progs->statements, + sizeof (dstatement_t), + byte_align); + progs->functions.count = qfo->num_funcs + 1; + + progs->globaldefs.offset = qfo_next_offset (progs->functions, + sizeof (dfunction_t), + byte_align); + progs->globaldefs.count = qfo->spaces[qfo_near_data_space].num_defs; + //ddef offsets are 16 bits so the ddef ofs will likely be invalid + //thus it will be forced invalid and the true offset written to the + //.xdefs array in the progs file + progs->globaldefs.count += qfo->spaces[qfo_far_data_space].num_defs; + + progs->fielddefs.offset = qfo_next_offset (progs->globaldefs, + sizeof (ddef_t), + byte_align); + progs->fielddefs.count = qfo->spaces[qfo_entity_space].num_defs; + + progs->globals.offset = qfo_next_offset (progs->fielddefs, + sizeof (ddef_t), + byte_align); + globals_info_t globals_info = qfo_count_globals (qfo, progs, word_align); + progs->globals.count = globals_info.globals_size; + progs->entityfields = qfo->spaces[qfo_entity_space].data_size; - *size += progs->numstatements * sizeof (dstatement_t); - *size += progs->numglobaldefs * sizeof (ddef_t); - *size += progs->numfielddefs * sizeof (ddef_t); - *size += progs->numfunctions * sizeof (dfunction_t); - *size += RUP (progs->numstrings * sizeof (char), 16); - *size += progs->numglobals * sizeof (pr_type_t); + // qfo_debug_space does not go in the progs file + + *size = qfo_next_offset (progs->globals, sizeof (pr_type_t), 1); progs = realloc (progs, *size); - data = (byte *) progs; memset (progs + 1, 0, *size - sizeof (dprograms_t)); - data += RUP (sizeof (dprograms_t), 16); - progs->ofs_strings = data - (byte *) progs; - strings = (char *) data; - data += RUP (progs->numstrings * sizeof (char), 16); - - progs->ofs_statements = data - (byte *) progs; - statements = (dstatement_t *) data; - data += progs->numstatements * sizeof (dstatement_t); - - progs->ofs_functions = data - (byte *) progs; - functions = (dfunction_t *) data; +#define qfo_block(t,b) (t *) ((byte *) progs + progs->b.offset) + strings = qfo_block (char, strings); + statements = qfo_block (dstatement_t, statements); + functions = qfo_block (dfunction_t, functions); functions++; // skip over null function - data += progs->numfunctions * sizeof (dfunction_t); + globaldefs = qfo_block (ddef_t, globaldefs); + fielddefs = qfo_block (ddef_t, fielddefs); + globals = qfo_block (pr_type_t, globals); + locals = globals + globals_info.locals_start; + far_data = globals + globals_info.near_data_size; + type_data = globals + globals_info.type_encodings_start; + xdef_data = globals + globals_info.xdefs_start; - progs->ofs_globaldefs = data - (byte *) progs; - globaldefs = (ddef_t *) data; - data += progs->numglobaldefs * sizeof (ddef_t); - - progs->ofs_fielddefs = data - (byte *) progs; - fielddefs = (ddef_t *) (globaldefs + progs->numglobaldefs); - data += progs->numfielddefs * sizeof (ddef_t); - - progs->ofs_globals = data - (byte *) progs; - globals = (pr_type_t*) data; - locals = globals + qfo->spaces[qfo_near_data_space].data_size; - far_data = locals + locals_size; - type_data = far_data + qfo->spaces[qfo_far_data_space].data_size; - - memcpy (strings, qfo->spaces[qfo_strings_space].d.strings, + memcpy (strings, qfo->spaces[qfo_strings_space].strings, qfo->spaces[qfo_strings_space].data_size * sizeof (char)); - qfo->spaces[qfo_strings_space].d.strings = strings; + qfo->spaces[qfo_strings_space].strings = strings; - memcpy (statements, qfo->spaces[qfo_code_space].d.code, + memcpy (statements, qfo->spaces[qfo_code_space].code, qfo->spaces[qfo_code_space].data_size * sizeof (dstatement_t)); - qfo->spaces[qfo_code_space].d.code = statements; + qfo->spaces[qfo_code_space].code = statements; for (i = 0; i < qfo->num_funcs; i++) { dfunction_t *df = functions + i; @@ -874,64 +1117,98 @@ qfo_to_progs (qfo_t *qfo, int *size) qfo_mspace_t *space = qfo->spaces + qf->locals_space; df->first_statement = qf->code; - df->parm_start = locals_start; df->locals = space->data_size; - // finalize the offsets of the local defs - for (j = 0; j < space->num_defs; j++) - space->defs[j].offset += locals_start; - if (!options.code.local_merging) - locals_start += df->locals; + if (options.code.progsversion < PROG_VERSION) { + df->params_start = globals_info.locals_start; + // finalize the offsets of the local defs + for (j = 0; j < space->num_defs; j++) + space->defs[j].offset += globals_info.locals_start; + if (!options.code.local_merging) + globals_info.locals_start += align_globals_size (df->locals); + } else { + // relative to start of locals for Ruamoko progs + df->params_start = qf->params_start; + } df->profile = 0; - df->s_name = qf->name; - df->s_file = qf->file; + df->name = qf->name; + df->file = qf->file; function_params (qfo, qf, df); } for (i = 0; i < qfo->spaces[qfo_near_data_space].num_defs; i++) { qfo_def_t *def = qfo->spaces[qfo_near_data_space].defs + i; - if (!strcmp (QFO_GETSTR (qfo, def->name), ".type_encodings")) + const char *defname = QFO_GETSTR (qfo, def->name); + if (!strcmp (defname, ".type_encodings")) types_def = def; - convert_def (qfo, def, globaldefs++); + if (!strcmp (defname, ".xdefs")) + xdefs_def = def; + qfo_def_to_ddef (qfo, def, globaldefs++); } - //FIXME ddef offsets are 16 bits for (i = 0; i < qfo->spaces[qfo_far_data_space].num_defs; i++) { qfo_def_t *def = qfo->spaces[qfo_far_data_space].defs + i; def->offset += far_data - globals; - convert_def (qfo, def, globaldefs++); + qfo_def_to_ddef (qfo, def, globaldefs); + // the correct offset will be written to the far data space + globaldefs->ofs = -1; + globaldefs++; } for (i = 0; i < qfo->spaces[qfo_type_space].num_defs; i++) { - qfo->spaces[qfo_type_space].defs[i].offset += type_data - globals; + qfo->spaces[qfo_type_space].defs[i].offset += globals_info.type_encodings_start; } for (i = 0; i < qfo->spaces[qfo_entity_space].num_defs; i++) { - convert_def (qfo, qfo->spaces[qfo_entity_space].defs + i, - fielddefs + i); + qfo_def_to_ddef (qfo, qfo->spaces[qfo_entity_space].defs + i, + fielddefs + i); } // copy near data - memcpy (globals, qfo->spaces[qfo_near_data_space].d.data, + memcpy (globals, qfo->spaces[qfo_near_data_space].data, qfo->spaces[qfo_near_data_space].data_size * sizeof (pr_type_t)); - qfo->spaces[qfo_near_data_space].d.data = globals; - // lcear locals data - memset (locals, 0, locals_size * sizeof (pr_type_t)); + qfo->spaces[qfo_near_data_space].data = globals; + // clear locals data + memset (locals, 0, globals_info.locals_size * sizeof (pr_type_t)); // copy far data - memcpy (far_data, qfo->spaces[qfo_far_data_space].d.data, + memcpy (far_data, qfo->spaces[qfo_far_data_space].data, qfo->spaces[qfo_far_data_space].data_size * sizeof (pr_type_t)); - qfo->spaces[qfo_far_data_space].d.data = far_data; + qfo->spaces[qfo_far_data_space].data = far_data; // copy type data - memcpy (type_data, qfo->spaces[qfo_type_space].d.data, + memcpy (type_data, qfo->spaces[qfo_type_space].data, qfo->spaces[qfo_type_space].data_size * sizeof (pr_type_t)); - qfo->spaces[qfo_type_space].d.data = type_data; + qfo->spaces[qfo_type_space].data = type_data; qfo_relocate_refs (qfo); if (types_def) { qfot_type_encodings_t *encodings; encodings = (qfot_type_encodings_t *) &globals[types_def->offset]; - encodings->types = type_data - globals; + encodings->types = globals_info.type_encodings_start; encodings->size = qfo->spaces[qfo_type_space].data_size; } + if (xdefs_def) { + xdefs = (pr_xdefs_t *) &globals[xdefs_def->offset]; + xdef = (xdef_t *) xdef_data; + xdefs->xdefs = globals_info.xdefs_start; + xdefs->num_xdefs = progs->globaldefs.count + progs->fielddefs.count; + for (i = 0; i < qfo->spaces[qfo_near_data_space].num_defs; + i++, xdef++) { + qfo_def_t *def = qfo->spaces[qfo_near_data_space].defs + i; + xdef->type = def->type + globals_info.type_encodings_start; + xdef->ofs = def->offset; + } + for (i = 0; i < qfo->spaces[qfo_far_data_space].num_defs; + i++, xdef++) { + qfo_def_t *def = qfo->spaces[qfo_far_data_space].defs + i; + xdef->type = def->type + globals_info.type_encodings_start; + xdef->ofs = def->offset; + } + for (i = 0; i < qfo->spaces[qfo_entity_space].num_defs; + i++, xdef++) { + qfo_def_t *def = qfo->spaces[qfo_entity_space].defs + i; + xdef->type = def->type + globals_info.type_encodings_start; + xdef->ofs = def->offset; + } + } // undo the relocation of the offsets of local defs so the local defs have // the correct offset in the debug info @@ -940,29 +1217,33 @@ qfo_to_progs (qfo_t *qfo, int *size) qfo_func_t *qf = qfo->funcs + i; qfo_mspace_t *space = qfo->spaces + qf->locals_space; - if (qf->locals_space == big_locals) + if (qf->locals_space == globals_info.big_locals) big_func = i; for (j = 0; j < space->num_defs; j++) - space->defs[j].offset -= df->parm_start; + space->defs[j].offset -= df->params_start; } if (options.verbosity >= 0) { const char *big_function = ""; if (big_func) - big_function = va (" (%s)", strings + qfo->funcs[big_func].name); - printf ("%6i strofs\n", progs->numstrings); - printf ("%6i statements\n", progs->numstatements); - printf ("%6i functions\n", progs->numfunctions); - printf ("%6i global defs\n", progs->numglobaldefs); - printf ("%6i fielddefs\n", progs->numfielddefs); - printf ("%6i globals\n", progs->numglobals); - printf (" %6i near globals\n", - qfo->spaces[qfo_near_data_space].data_size + locals_size); - printf (" %6i locals size%s\n", locals_size, big_function); + big_function = va (0, " (%s)", strings + qfo->funcs[big_func].name); + printf ("%6i strofs\n", progs->strings.count); + printf ("%6i statements\n", progs->statements.count); + printf ("%6i functions\n", progs->functions.count); + printf ("%6i global defs\n", progs->globaldefs.count); + printf ("%6i field defs\n", progs->fielddefs.count); + printf ("%6i globals\n", progs->globals.count); + printf (" %6i near globals\n", globals_info.near_data_size); + printf (" %6i locals size%s\n", + globals_info.locals_size, big_function); printf (" %6i far globals\n", qfo->spaces[qfo_far_data_space].data_size); printf (" %6i type globals\n", qfo->spaces[qfo_type_space].data_size); + if (xdefs) { + printf (" %6i extended defs\n", + xdefs->num_xdefs * type_size (&type_xdef)); + } printf ("%6i entity fields\n", progs->entityfields); } @@ -976,11 +1257,12 @@ pr_debug_header_t * qfo_to_sym (qfo_t *qfo, int *size) { pr_debug_header_t *sym; - int i, j; + unsigned i, j; pr_auxfunction_t *auxfuncs; pr_auxfunction_t *aux; pr_lineno_t *linenos; - ddef_t *locals, *ld; + pr_def_t *locals, *ld, *debug_defs; + pr_type_t *debug_data; *size = sizeof (pr_debug_header_t); sym = calloc (1, *size); @@ -988,7 +1270,7 @@ qfo_to_sym (qfo_t *qfo, int *size) sym->version = PROG_DEBUG_VERSION; for (i = 0; i < qfo->num_funcs; i++) { qfo_func_t *func = qfo->funcs + i; - int num_locals = 0; + unsigned num_locals = 0; if (func->locals_space) num_locals = qfo->spaces[func->locals_space].num_defs; @@ -998,26 +1280,34 @@ qfo_to_sym (qfo_t *qfo, int *size) sym->num_locals += num_locals; } sym->num_linenos = qfo->num_lines; + sym->num_debug_defs = qfo->spaces[qfo_debug_space].num_defs; + sym->debug_data_size = qfo->spaces[qfo_debug_space].data_size; *size += sym->num_auxfunctions * sizeof (pr_auxfunction_t); *size += sym->num_linenos * sizeof (pr_lineno_t); - *size += sym->num_locals * sizeof (ddef_t); + *size += sym->num_locals * sizeof (pr_def_t); + *size += sym->num_debug_defs * sizeof (pr_def_t); + *size += sym->debug_data_size * sizeof (pr_type_t); sym = realloc (sym, *size); auxfuncs = (pr_auxfunction_t *)(sym + 1); linenos = (pr_lineno_t *)(auxfuncs + sym->num_auxfunctions); - locals = (ddef_t *)(linenos + sym->num_linenos); + locals = (pr_def_t *)(linenos + sym->num_linenos); + debug_defs = locals + sym->num_locals; + debug_data = (pr_type_t *)(debug_defs + sym->num_debug_defs); sym->auxfunctions = (char *) auxfuncs - (char *) sym; sym->linenos = (char *) linenos - (char *) sym; sym->locals = (char *) locals - (char *) sym; + sym->debug_defs = (char *) debug_defs - (char *) sym; + sym->debug_data = (char *) debug_data - (char *) sym; ld = locals; for (i = 0, aux = auxfuncs; i < qfo->num_funcs; i++) { qfo_func_t *func = qfo->funcs + i; qfo_def_t *def = 0; - int num_locals = 0; + unsigned num_locals = 0; qfot_type_t *type; if (func->locals_space) { @@ -1034,15 +1324,27 @@ qfo_to_sym (qfo_t *qfo, int *size) qfo->lines[func->line_info].fa.func = aux - auxfuncs; if (num_locals) { aux->local_defs = ld - locals; - for (j = 0; j < num_locals; j++) - convert_def (qfo, def++, ld++); + for (j = 0; j < num_locals; j++, def++, ld++) { + qfo_def_to_prdef (qfo, def, ld); + } } aux->num_locals = num_locals; //FIXME check type type = QFO_POINTER (qfo, qfo_type_space, qfot_type_t, func->type); - aux->return_type = type->t.func.return_type; + if (type->meta == ty_alias) { + type = QFO_POINTER (qfo, qfo_type_space, qfot_type_t, + type->alias.aux_type); + } + aux->return_type = type->func.return_type; aux++; } memcpy (linenos, qfo->lines, qfo->num_lines * sizeof (pr_lineno_t)); + for (i = 0; i < sym->num_debug_defs; i++) { + qfo_def_t *def = &qfo->spaces[qfo_debug_space].defs[i]; + pr_def_t *prdef = &debug_defs[i]; + qfo_def_to_prdef (qfo, def, prdef); + } + memcpy (debug_data, qfo->spaces[qfo_debug_space].data, + sym->debug_data_size * sizeof (*debug_data)); return sym; } diff --git a/tools/qfcc/source/obj_type.c b/tools/qfcc/source/obj_type.c index 2b77a864f..8e896b750 100644 --- a/tools/qfcc/source/obj_type.c +++ b/tools/qfcc/source/obj_type.c @@ -42,16 +42,16 @@ #include "compat.h" -#include "class.h" -#include "def.h" -#include "defspace.h" -#include "diagnostic.h" -#include "emit.h" -#include "obj_type.h" -#include "qfcc.h" -#include "reloc.h" -#include "symtab.h" -#include "value.h" +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/emit.h" +#include "tools/qfcc/include/obj_type.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/value.h" #define ENC_DEF(dest,def) EMIT_DEF (pr.type_data, dest, def) #define ENC_STR(dest,str) \ @@ -63,9 +63,9 @@ reloc_def_string (&loc); \ } while (0) -typedef def_t *(*encode_f) (type_t *type); +typedef def_t *(*encode_f) (type_t *type, defspace_t *space); -static string_t +static pr_string_t encoding_string (const char *string) { int str; @@ -75,26 +75,26 @@ encoding_string (const char *string) } static def_t * -qfo_new_encoding (type_t *type, int size) +qfo_new_encoding (type_t *type, int size, defspace_t *space) { qfot_type_t *enc; def_t *def; - size += sizeof (qfot_type_t) - sizeof (enc->t); + size += field_offset (qfot_type_t, type); size /= sizeof (pr_type_t); - def = new_def (type->encoding, 0, pr.type_data, sc_static); + def = new_def (type->encoding, 0, space, sc_static); def->offset = defspace_alloc_loc (pr.type_data, size); enc = D_POINTER (qfot_type_t, def); - enc->ty = type->meta; + enc->meta = type->meta; enc->size = size; ENC_STR (enc->encoding, type->encoding); return def; } static def_t * -qfo_encode_func (type_t *type) +qfo_encode_func (type_t *type, defspace_t *space) { int param_count; int size; @@ -109,14 +109,15 @@ qfo_encode_func (type_t *type) if (param_count < 0) param_count = ~param_count; param_type_defs = alloca (param_count * sizeof (def_t *)); - return_type_def = qfo_encode_type (type->t.func.type); + return_type_def = qfo_encode_type (type->t.func.type, space); for (i = 0; i < param_count; i++) - param_type_defs[i] = qfo_encode_type (type->t.func.param_types[i]); + param_type_defs[i] = qfo_encode_type (type->t.func.param_types[i], + space); size = field_offset (qfot_func_t, param_types[param_count]); - def = qfo_new_encoding (type, size); + def = qfo_new_encoding (type, size, space); enc = D_POINTER (qfot_type_t, def); - func = &enc->t.func; + func = &enc->func; func->type = ev_func; ENC_DEF (func->return_type, return_type_def); func->num_params = type->t.func.num_params; @@ -126,39 +127,40 @@ qfo_encode_func (type_t *type) } static def_t * -qfo_encode_fldptr (type_t *type) +qfo_encode_fldptr (type_t *type, defspace_t *space) { qfot_type_t *enc; def_t *def; def_t *type_def; - type_def = qfo_encode_type (type->t.fldptr.type); - def = qfo_new_encoding (type, sizeof (enc->t.fldptr)); + type_def = qfo_encode_type (type->t.fldptr.type, space); + def = qfo_new_encoding (type, sizeof (enc->fldptr), space); enc = D_POINTER (qfot_type_t, def); - enc->t.fldptr.type = type->type; - ENC_DEF (enc->t.fldptr.aux_type, type_def); + enc->fldptr.type = type->type; + ENC_DEF (enc->fldptr.aux_type, type_def); return def; } static def_t * -qfo_encode_none (type_t *type) +qfo_encode_basic (type_t *type, defspace_t *space) { qfot_type_t *enc; def_t *def; if (type->type == ev_func) - return qfo_encode_func (type); - else if (type->type == ev_pointer || type->type == ev_field) - return qfo_encode_fldptr (type); + return qfo_encode_func (type, space); + else if (type->type == ev_ptr || type->type == ev_field) + return qfo_encode_fldptr (type, space); - def = qfo_new_encoding (type, sizeof (enc->t.type)); + def = qfo_new_encoding (type, sizeof (enc->basic), space); enc = D_POINTER (qfot_type_t, def); - enc->t.type = type->type; + enc->basic.type = type->type; + enc->basic.width = type->width; return def; } static def_t * -qfo_encode_struct (type_t *type) +qfo_encode_struct (type_t *type, defspace_t *space) { sy_type_e sy; int num_fields; @@ -185,9 +187,9 @@ qfo_encode_struct (type_t *type) } size = field_offset (qfot_struct_t, fields[num_fields]); - def = qfo_new_encoding (type, size); + def = qfo_new_encoding (type, size, space); enc = D_POINTER (qfot_type_t, def); - strct = &enc->t.strct; + strct = &enc->strct; ENC_STR (strct->tag, type->name); strct->num_fields = num_fields; @@ -200,7 +202,7 @@ qfo_encode_struct (type_t *type) if (i == num_fields) internal_error (0, "whoa, what happened?"); if (type->meta != ty_enum) { - field_types[i] = qfo_encode_type (sym->type); + field_types[i] = qfo_encode_type (sym->type, space); } else { field_types[i] = type_default->type_def; } @@ -213,7 +215,7 @@ qfo_encode_struct (type_t *type) if (i == num_fields) internal_error (0, "whoa, what happened?"); if (sym->sy_type == sy_const) - offset = sym->s.value->v.integer_val; + offset = sym->s.value->v.int_val; else offset = sym->s.offset; ENC_DEF (strct->fields[i].type, field_types[i]); @@ -225,46 +227,83 @@ qfo_encode_struct (type_t *type) } static def_t * -qfo_encode_array (type_t *type) +qfo_encode_array (type_t *type, defspace_t *space) { qfot_type_t *enc; def_t *def; def_t *array_type_def; - array_type_def = qfo_encode_type (type->t.array.type); + array_type_def = qfo_encode_type (type->t.array.type, space); - def = qfo_new_encoding (type, sizeof (enc->t.array)); + def = qfo_new_encoding (type, sizeof (enc->array), space); enc = D_POINTER (qfot_type_t, def); - ENC_DEF (enc->t.array.type, array_type_def); - enc->t.array.base = type->t.array.base; - enc->t.array.size = type->t.array.size; + ENC_DEF (enc->array.type, array_type_def); + enc->array.base = type->t.array.base; + enc->array.size = type->t.array.size; return def; } static def_t * -qfo_encode_class (type_t *type) +qfo_encode_class (type_t *type, defspace_t *space) { qfot_type_t *enc; def_t *def; - def = qfo_new_encoding (type, sizeof (enc->t.class)); + def = qfo_new_encoding (type, sizeof (enc->class), space); enc = D_POINTER (qfot_type_t, def); - ENC_STR (enc->t.class, type->t.class->name); + ENC_STR (enc->class, type->t.class->name); + return def; +} + +static def_t * +qfo_encode_alias (type_t *type, defspace_t *space) +{ + qfot_type_t *enc; + def_t *def; + def_t *type_def; + def_t *full_def; + + type_def = qfo_encode_type (type->t.alias.aux_type, space); + full_def = qfo_encode_type (type->t.alias.full_type, space); + + def = qfo_new_encoding (type, sizeof (enc->alias), space); + enc = D_POINTER (qfot_type_t, def); + enc->alias.type = type->type; + ENC_DEF (enc->alias.aux_type, type_def); + ENC_DEF (enc->alias.full_type, full_def); + if (type->name) { + ENC_STR (enc->alias.name, type->name); + } + return def; +} + +static def_t * +qfo_encode_handle (type_t *type, defspace_t *space) +{ + qfot_type_t *enc; + def_t *def; + + def = qfo_new_encoding (type, sizeof (enc->handle), space); + enc = D_POINTER (qfot_type_t, def); + enc->handle.type = type->type; + ENC_STR (enc->handle.tag, type->name); return def; } def_t * -qfo_encode_type (type_t *type) +qfo_encode_type (type_t *type, defspace_t *space) { reloc_t *relocs = 0; static encode_f funcs[] = { - qfo_encode_none, // ty_none + qfo_encode_basic, // ty_basic qfo_encode_struct, // ty_struct qfo_encode_struct, // ty_union qfo_encode_struct, // ty_enum qfo_encode_array, // ty_array qfo_encode_class, // ty_class + qfo_encode_alias, // ty_alias + qfo_encode_handle, // ty_handle }; if (type->type_def && type->type_def->external) { @@ -274,11 +313,11 @@ qfo_encode_type (type_t *type) } if (type->type_def) return type->type_def; - if (type->meta > ty_class) + if (type->meta >= sizeof (funcs) / (sizeof (funcs[0]))) internal_error (0, "bad type meta type"); if (!type->encoding) type->encoding = type_get_encoding (type); - type->type_def = funcs[type->meta] (type); + type->type_def = funcs[type->meta] (type, space); reloc_attach_relocs (relocs, &type->type_def->relocs); return type->type_def; } diff --git a/tools/qfcc/source/opcodes.c b/tools/qfcc/source/opcodes.c index 4d8bb4678..23da7490b 100644 --- a/tools/qfcc/source/opcodes.c +++ b/tools/qfcc/source/opcodes.c @@ -41,21 +41,56 @@ #include -#include "opcodes.h" -#include "options.h" -#include "qfcc.h" -#include "statements.h" -#include "type.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/opcodes.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/statements.h" +#include "tools/qfcc/include/type.h" -hashtab_t *opcode_type_table; -hashtab_t *opcode_void_table; +typedef struct v6p_uint_opcode_s { + pr_opcode_v6p_e op; + v6p_opcode_t opcode; +} v6p_uint_opcode_t; + +static v6p_uint_opcode_t v6p_uint_opcodes[] = { + {OP_LOAD_I_v6p, {"load", "load.i", ev_entity, ev_field, ev_uint }}, + {OP_LOADBI_I_v6p, {"load", "loadbi.i", ev_ptr, ev_short, ev_uint }}, + {OP_ADDRESS_I_v6p, {"lea", "address.i",ev_uint, ev_invalid, ev_ptr }}, + {OP_STORE_I_v6p, {"assign", "store.i", ev_uint, ev_uint, ev_invalid }}, + {OP_STOREP_I_v6p, {"store", "storep.i", ev_uint, ev_ptr, ev_invalid }}, + {OP_STOREB_I_v6p, {"store", "storeb.i", ev_uint, ev_ptr, ev_int }}, + {OP_STOREBI_I_v6p, {"store", "storebi.i",ev_uint, ev_ptr, ev_short }}, + {OP_IF_v6p, {"ifnz", "if", ev_uint, ev_short, ev_invalid }}, + {OP_IFNOT_v6p, {"ifz", "ifnot", ev_uint, ev_short, ev_invalid }}, + {OP_ADD_I_v6p, {"add", "add.i", ev_uint, ev_uint, ev_uint }}, + {OP_SUB_I_v6p, {"sub", "sub.i", ev_uint, ev_uint, ev_uint }}, + {OP_MUL_I_v6p, {"mul", "mul.i", ev_uint, ev_uint, ev_uint }}, + {OP_DIV_I_v6p, {"div", "div.i", ev_uint, ev_uint, ev_uint }}, + {OP_BITAND_I_v6p, {"bitand", "bitand.i", ev_uint, ev_uint, ev_uint }}, + {OP_BITOR_I_v6p, {"bitor", "bitor.i", ev_uint, ev_uint, ev_uint }}, + {OP_BITXOR_I_v6p, {"bitxor", "bitxor.i", ev_uint, ev_uint, ev_uint }}, + {OP_REM_I_v6p, {"rem", "rem.i", ev_uint, ev_uint, ev_uint }}, + {OP_MOD_I_v6p, {"mod", "mod.i", ev_uint, ev_uint, ev_uint }}, + {OP_SHL_I_v6p, {"shl", "shl.i", ev_uint, ev_uint, ev_uint }}, + {OP_BITNOT_I_v6p, {"bitnot", "bitnot.i", ev_uint, ev_invalid, ev_int }}, + {} +}; + +static hashtab_t *v6p_opcode_type_table; +static hashtab_t *v6p_opcode_void_table; +static hashtab_t *v6p_opcode_uint_table; +static v6p_opcode_t *v6p_opcode_map; + +static hashtab_t *rua_opcode_type_table; +static hashtab_t *rua_opcode_void_table; #define ROTL(x,n) ((((unsigned)(x))<<(n))|((unsigned)(x))>>(32-n)) static uintptr_t -get_hash (const void *_op, void *_tab) +v6p_get_hash (const void *_op, void *_tab) { - opcode_t *op = (opcode_t *) _op; + v6p_opcode_t *op = (v6p_opcode_t *) _op; uintptr_t hash; hash = ROTL (~op->type_a, 8) + ROTL (~op->type_b, 16) @@ -64,10 +99,10 @@ get_hash (const void *_op, void *_tab) } static int -compare (const void *_opa, const void *_opb, void *unused) +v6p_compare (const void *_opa, const void *_opb, void *unused) { - opcode_t *opa = (opcode_t *) _opa; - opcode_t *opb = (opcode_t *) _opb; + v6p_opcode_t *opa = (v6p_opcode_t *) _opa; + v6p_opcode_t *opb = (v6p_opcode_t *) _opb; int cmp; cmp = (opa->type_a == opb->type_a) @@ -77,9 +112,59 @@ compare (const void *_opa, const void *_opb, void *unused) } static const char * -get_key (const void *op, void *unused) +v6p_get_key (const void *op, void *unused) { - return ((opcode_t *) op)->name; + return ((v6p_opcode_t *) op)->name; +} + +static uintptr_t +v6p_uint_get_hash (const void *_op, void *_tab) +{ + __auto_type uint_op = (v6p_uint_opcode_t *) _op; + return v6p_get_hash (&uint_op->opcode, _tab); +} + +static int +v6p_uint_compare (const void *_opa, const void *_opb, void *data) +{ + __auto_type uint_opa = (v6p_uint_opcode_t *) _opa; + __auto_type uint_opb = (v6p_uint_opcode_t *) _opb; + return v6p_compare (&uint_opa->opcode, &uint_opb->opcode, data); +} + +static uintptr_t +rua_get_hash (const void *_op, void *_tab) +{ + opcode_t *op = (opcode_t *) _op; + uintptr_t hash; + + hash = ROTL (~op->types[0], 8) + ROTL (~op->types[1], 16) + + ROTL (~op->types[2], 24); + hash += ROTL (~op->widths[0], 12) + ROTL (~op->widths[1], 20) + + ROTL (~op->widths[2], 28); + return hash + Hash_String (op->opname); +} + +static int +rua_compare (const void *_opa, const void *_opb, void *unused) +{ + opcode_t *opa = (opcode_t *) _opa; + opcode_t *opb = (opcode_t *) _opb; + int cmp; + + cmp = (opa->types[0] == opb->types[0]) + && (opa->types[1] == opb->types[1]) + && (opa->types[2] == opb->types[2]); + cmp &= (opa->widths[0] == opb->widths[0]) + && (opa->widths[1] == opb->widths[1]) + && (opa->widths[2] == opb->widths[2]); + return cmp && !strcmp (opa->opname, opb->opname); +} + +static const char * +rua_get_key (const void *op, void *unused) +{ + return ((opcode_t *) op)->opname; } static int @@ -91,77 +176,360 @@ check_operand_type (etype_t ot1, etype_t ot2) return 0; } -opcode_t * -opcode_find (const char *name, operand_t *op_a, operand_t *op_b, - operand_t *op_c) +static int +check_operand_width (int ow1, int ow2) { - opcode_t search_op; - opcode_t *op; - opcode_t *sop; + return (ow1 == -1 || ow1 == ow2); +} + +pr_ushort_t +opcode_get (instruction_t *op) +{ + if (options.code.progsversion < PROG_VERSION) { + return (v6p_opcode_t *) op - v6p_opcode_map; + } else { + return (opcode_t *) op - pr_opcodes; + } +} + +static v6p_opcode_t * +v6p_opcode_find (const char *name, operand_t *op_a, operand_t *op_b, + operand_t *op_c) +{ + v6p_uint_opcode_t search_op = { + .opcode = { + .name = name, + .type_a = op_a ? low_level_type (op_a->type) : ev_invalid, + .type_b = op_b ? low_level_type (op_b->type) : ev_invalid, + .type_c = op_c ? low_level_type (op_c->type) : ev_invalid, + }, + }; + v6p_uint_opcode_t *uint_op; + v6p_opcode_t *op; + v6p_opcode_t *sop; void **op_list; int i; - search_op.name = name; - search_op.type_a = op_a ? op_a->type : ev_invalid; - search_op.type_b = op_b ? op_b->type : ev_invalid; - search_op.type_c = op_c ? op_c->type : ev_invalid; - op = Hash_FindElement (opcode_type_table, &search_op); + uint_op = Hash_FindElement (v6p_opcode_uint_table, &search_op); + if (uint_op) { + return v6p_opcode_map + uint_op->op; + } + op = Hash_FindElement (v6p_opcode_type_table, &search_op.opcode); if (op) return op; - op_list = Hash_FindList (opcode_void_table, name); + op_list = Hash_FindList (v6p_opcode_void_table, name); if (!op_list) return op; for (i = 0; !op && op_list[i]; i++) { sop = op_list[i]; - if (check_operand_type (sop->type_a, search_op.type_a) - && check_operand_type (sop->type_b, search_op.type_b) - && check_operand_type (sop->type_c, search_op.type_c)) + if (check_operand_type (sop->type_a, search_op.opcode.type_a) + && check_operand_type (sop->type_b, search_op.opcode.type_b) + && check_operand_type (sop->type_c, search_op.opcode.type_c)) op = sop; } free (op_list); return op; } -static void -opcode_free (void *_op, void *unused) +static const char *unsigned_demote_ops[] = { + "add", + "bitand", + "bitnot", + "bitor", + "bitxor", + "eq", + "ifnz", + "ifz", + "mul", + "ne", + "sub", +}; + +static int +ud_compare (const void *_a, const void *_b) { - free (_op); + const char *a = _a; + const char *b = *(const char **)_b; + return strcmp (a, b); } -void -opcode_init (void) +static etype_t __attribute__((pure)) +operand_type (const operand_t *op, const char *name) { - opcode_t *op, *mop; - - if (opcode_type_table) { - Hash_FlushTable (opcode_void_table); - Hash_FlushTable (opcode_type_table); - } else { - PR_Opcode_Init (); - opcode_type_table = Hash_NewTable (1021, 0, opcode_free, 0); - Hash_SetHashCompare (opcode_type_table, get_hash, compare); - opcode_void_table = Hash_NewTable (1021, get_key, 0, 0); + if (!op) { + return ev_invalid; } - for (op = pr_opcodes; op->name; op++) { + etype_t type = low_level_type (op->type); + if (type == ev_vector || type == ev_quaternion) { + return ev_float; + } + if (type == ev_uint || type == ev_ulong) { + if (bsearch (name, unsigned_demote_ops, + sizeof (unsigned_demote_ops) + / sizeof (unsigned_demote_ops[0]), + sizeof (unsigned_demote_ops[0]), + ud_compare)) { + if (type == ev_uint) { + type = ev_int; + } + if (type == ev_ulong) { + type = ev_long; + } + } + } + return type; +} + +static int +operand_width (const char *opname, operand_t *op) +{ + if (!op) { + return 0; + } + etype_t type = low_level_type (op->type); + if (type == ev_vector) { + return 3; + } + if (type == ev_quaternion) { + return 4; + } + // FIXME see FIXME in rua_opcode_find + if ((type == ev_long || type == ev_ulong || type == ev_double) + && (!strcmp (opname, "load") || !strcmp (opname, "store") + || !strcmp (opname, "assign"))) { + if (op->width < 3) { + return op->width * 2; + } + } + return op->width; +} +#if 0 + if (!strcmp (name, "swizzle")) { + adjust_swizzle_op (&search_op, 0); + adjust_swizzle_op (&search_op, 2); + } +static void +adjust_swizzle_op (opcode_t *op, int opind) +{ + // swizzle instructions require both operands to be 4 components (4 or 8 + // words) in size with the same alignment. + op->widths[opind] = 4; + if (pr_type_size[op->types[opind]] == 1) { + op->types[opind] = ev_float; + } else if (pr_type_size[op->types[opind]] == 2) { + op->types[opind] = ev_double; + } else { + internal_error (0, "unexpected swizzle op size"); + } +} +#endif +static opcode_t * +rua_opcode_find (const char *name, operand_t *op_a, operand_t *op_b, + operand_t *op_c) +{ + // FIXME this is a bit of an ugly hack to map 64-bit load and store/assign + // instructions: 1 and 2 component instructions become 2 and 4 components + // using the 32-bit instructions, while 3 and 4 remain unchanged but use + // the 64-bit versions of the instructs (of which there are only 3 and 4 + // component versions). That bit of fun can't be helped without wasting a + // lot of instructions, but this mapping scheme leaves a lot to be desired. + const char *opname_a = ""; + const char *opname_c = ""; + etype_t type; + if ((!strcmp (name, "load") || !strcmp (name, "store") + || !strcmp (name, "assign")) + && ((type = low_level_type (op_c->type)) == ev_long + || type == ev_ulong || type == ev_double)) { + opname_c = name; + if (!strcmp (name, "assign")) { + opname_a = name; + if (op_c->width > 2) { + name = "assign64"; + } + } + if (!strcmp (name, "load")) { + if (op_c->width > 2) { + name = "load64"; + } + } + if (!strcmp (name, "store")) { + if (op_c->width > 2) { + name = "store64"; + } + } + } + + opcode_t search_op = { + .opname = name, + .types = { + operand_type (op_a, name), + operand_type (op_b, name), + operand_type (op_c, name), + }, + .widths = { + operand_width (opname_a, op_a), + operand_width ("", op_b), + operand_width (opname_c, op_c), + }, + }; + opcode_t *op; + opcode_t *sop; + void **op_list; + int i; + +#if 0 + printf ("%s [%s %d] [%s %d] [%s %d]\n", search_op.opname, + pr_type_name[search_op.types[0]], search_op.widths[0], + pr_type_name[search_op.types[1]], search_op.widths[1], + pr_type_name[search_op.types[2]], search_op.widths[2]); +#endif + op = Hash_FindElement (rua_opcode_type_table, &search_op); + if (op) + return op; + op_list = Hash_FindList (rua_opcode_void_table, name); + if (!op_list) + return op; + for (i = 0; !op && op_list[i]; i++) { + sop = op_list[i]; + if (!(check_operand_type (sop->types[0], search_op.types[0]) + && check_operand_type (sop->types[1], search_op.types[1]) + && check_operand_type (sop->types[2], search_op.types[2]))) { + continue; + } + if (!(check_operand_width (sop->widths[0], search_op.widths[0]) + && check_operand_width (sop->widths[1], search_op.widths[1]) + && check_operand_width (sop->widths[2], search_op.widths[2]))) { +#if 0 + printf ("%s [%s %d] [%s %d] [%s %d]\n", sop->opname, + pr_type_name[sop->types[0]], sop->widths[0], + pr_type_name[sop->types[1]], sop->widths[1], + pr_type_name[sop->types[2]], sop->widths[2]); +#endif + continue; + } + op = sop; + } + free (op_list); + return op; +} + +instruction_t * +opcode_find (const char *name, operand_t *op_a, operand_t *op_b, + operand_t *op_c) +{ + if (options.code.progsversion < PROG_VERSION) { + return (instruction_t *) v6p_opcode_find (name, op_a, op_b, op_c); + } else { + return (instruction_t *) rua_opcode_find (name, op_a, op_b, op_c); + } +} + +static void +v6p_opcode_init (void) +{ + const v6p_opcode_t *op; + v6p_opcode_t *mop; + + if (v6p_opcode_type_table) { + Hash_FlushTable (v6p_opcode_void_table); + Hash_FlushTable (v6p_opcode_type_table); + Hash_FlushTable (v6p_opcode_uint_table); + } else { + v6p_opcode_type_table = Hash_NewTable (1021, 0, 0, 0, 0); + Hash_SetHashCompare (v6p_opcode_type_table, v6p_get_hash, v6p_compare); + v6p_opcode_void_table = Hash_NewTable (1021, v6p_get_key, 0, 0, 0); + v6p_opcode_uint_table = Hash_NewTable (1021, 0, 0, 0, 0); + Hash_SetHashCompare (v6p_opcode_uint_table, + v6p_uint_get_hash, v6p_uint_compare); + } + + int num_opcodes = 0; + for (op = pr_v6p_opcodes; op->name; op++) { + num_opcodes++; + } + if (!v6p_opcode_map) { + v6p_opcode_map = calloc (num_opcodes, sizeof (v6p_opcode_t)); + } + for (int i = 0; i < num_opcodes; i++) { + op = pr_v6p_opcodes + i; if (op->min_version > options.code.progsversion) continue; - mop = malloc (sizeof (opcode_t)); + mop = v6p_opcode_map + i; *mop = *op; if (options.code.progsversion == PROG_ID_VERSION) { // v6 progs have no concept of integer, but the QF engine // treats the operands of certain operands as integers // irrespective the progs version, so convert the engine's // view of the operands to the prog's view. - if (mop->type_a == ev_integer) + if (mop->type_a == ev_int) mop->type_a = ev_float; - if (mop->type_b == ev_integer) + if (mop->type_b == ev_int) mop->type_b = ev_float; - if (mop->type_c == ev_integer) + if (mop->type_c == ev_int) mop->type_c = ev_float; } - Hash_AddElement (opcode_type_table, mop); + Hash_AddElement (v6p_opcode_type_table, mop); if (mop->type_a == ev_void || mop->type_b == ev_void || mop->type_c == ev_void) - Hash_Add (opcode_void_table, mop); + Hash_Add (v6p_opcode_void_table, mop); + } + if (options.code.progsversion != PROG_ID_VERSION) { + for (__auto_type uiop = &v6p_uint_opcodes[0]; uiop->op; uiop++) { + Hash_AddElement (v6p_opcode_uint_table, uiop); + } } } + +static void +rua_opcode_init (void) +{ + if (rua_opcode_type_table) { + return; + } + + rua_opcode_type_table = Hash_NewTable (1021, 0, 0, 0, 0); + Hash_SetHashCompare (rua_opcode_type_table, rua_get_hash, rua_compare); + rua_opcode_void_table = Hash_NewTable (1021, rua_get_key, 0, 0, 0); + + int num_opcodes = sizeof (pr_opcodes) / sizeof (pr_opcodes[0]); + for (int i = 0; i < num_opcodes; i++) { + const opcode_t *op = pr_opcodes + i; + if (!op->opname) { + continue; + } + Hash_AddElement (rua_opcode_type_table, (opcode_t *) op); + if (op->types[0] == ev_void || op->types[1] == ev_void + || op->types[2] == ev_void) { + Hash_Add (rua_opcode_void_table, (opcode_t *) op); + } + } +} + +void +opcode_init (void) +{ + if (options.code.progsversion < PROG_VERSION) { + v6p_opcode_init (); + } else { + rua_opcode_init (); + } +} + +void +opcode_print_statement (pr_uint_t addr, dstatement_t *st) +{ + const char *mnemonic; + // this is ok because v6p has < 300 instructions + int st_op = st->op & 0x1ff; + + if (options.code.progsversion < PROG_VERSION) { + mnemonic = v6p_opcode_map[st_op].opname; + } else { + mnemonic = pr_opcodes[st_op].mnemonic; + } + printf ("%04x (%03x)%-8s %d:%04x %d:%04x %d:%04x\n", + addr, st_op & 0x1ff, mnemonic, + (st->op & OP_A_BASE) >> OP_A_SHIFT, st->a, + (st->op & OP_B_BASE) >> OP_B_SHIFT, st->b, + (st->op & OP_C_BASE) >> OP_C_SHIFT, st->c); +} diff --git a/tools/qfcc/source/options.c b/tools/qfcc/source/options.c index 4edb03623..fff369987 100644 --- a/tools/qfcc/source/options.c +++ b/tools/qfcc/source/options.c @@ -42,14 +42,18 @@ #include -#include "QF/pr_comp.h" #include "QF/va.h" -#include "cpp.h" -#include "linker.h" -#include "options.h" -#include "qfcc.h" -#include "strpool.h" +#include "QF/progs/pr_comp.h" + +#include "tools/qfcc/include/cpp.h" +#include "tools/qfcc/include/linker.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/type.h" + +#include "tools/qfcc/source/qc-parse.h" const char *this_program; const char **source_files; @@ -68,12 +72,15 @@ enum { OPT_NO_DEFAULT_PATHS, OPT_PROGDEFS, OPT_QCCX_ESCAPES, + OPT_RUAMOKO, OPT_TRADITIONAL, + OPT_BUG, }; static struct option const long_options[] = { {"advanced", no_argument, 0, OPT_ADVANCED}, {"block-dot", optional_argument, 0, OPT_BLOCK_DOT}, + {"bug", required_argument, 0, OPT_BUG}, {"code", required_argument, 0, 'C'}, {"cpp", required_argument, 0, OPT_CPP}, {"define", required_argument, 0, 'D'}, @@ -89,10 +96,11 @@ static struct option const long_options[] = { {"progs-src", required_argument, 0, 'P'}, {"qccx-escapes", no_argument, 0, OPT_QCCX_ESCAPES}, {"quiet", no_argument, 0, 'q'}, + {"raumoko", no_argument, 0, OPT_RUAMOKO}, {"relocatable", no_argument, 0, 'r'}, + {"ruamoko", no_argument, 0, OPT_RUAMOKO}, {"save-temps", no_argument, 0, 'S'}, {"source", required_argument, 0, 's'}, - {"strip-path", required_argument, 0, 'p'}, {"traditional", no_argument, 0, OPT_TRADITIONAL}, {"undefine", required_argument, 0, 'U'}, {"verbose", no_argument, 0, 'v'}, @@ -118,7 +126,6 @@ static const char *short_options = "o:" // output file "O" // optimize "P:" // progs.src name - "p:" // strip path "q" // quiet "r" // partial linking "S" // save temps @@ -138,7 +145,7 @@ usage (int status) printf ( "Options:\n" " --advanced Advanced Ruamoko mode\n" -" default for separate compilation mode\n" +" --bug OPTION,... Set bug options\n" " -C, --code OPTION,... Set code generation options\n" " -c Only compile, don't link\n" " --cpp CPPSPEC cpp execution command line\n" @@ -163,12 +170,14 @@ usage (int status) " -o, --output-file FILE Specify output file name\n" " --progdefs Genderate progdefs.h\n" " -P, --progs-src FILE File to use instead of progs.src\n" -" -p, --strip-path NUM Strip NUM leading path elements from file\n" -" names\n" " --qccx-escapes Use QCCX escape sequences instead of standard\n" " C/QuakeForge sequences.\n" " -q, --quiet Inhibit usual output\n" " -r, --relocatable Incremental linking\n" +" --raumoko Use both Ruamoko language features and the\n" +" --ruamoko Ruamoko ISA, providing access to SIMD types\n" +" and a stack for locals.\n" +" default for separate compilation mode\n" " -S, --save-temps Do not delete temporary files\n" " -s, --source DIR Look for progs.src in DIR instead of \".\"\n" " --traditional Traditional QuakeC mode: implies v6only\n" @@ -191,14 +200,16 @@ code_usage (void) printf ("%s - QuakeForge Code Compiler\n", this_program); printf ("Code generation options\n"); printf ( +" [no-]const-initializers Treat initialized globals as constants.\n" " [no-]cow Allow assignment to initialized globals.\n" " [no-]cpp Preprocess all input files with cpp.\n" " [no-]crc Write progdefs.h crc to progs.dat.\n" " [no-]debug Generate debug information.\n" " [no-]fast-float Use float values directly in \"if\" statements.\n" -" help Display his text.\n" +" help Display this text.\n" " [no-]local-merging Merge the local variable blocks into one.\n" " [no-]optimize Perform various optimizations on the code.\n" +" [no-]promote-float Promote float when passed through ...\n" " [no-]short-circuit Generate short circuit code for logical\n" " operators.\n" " [no-]single-cpp Convert progs.src to cpp input file.\n" @@ -206,8 +217,10 @@ code_usage (void) " passing\n" " vectors to functiosn.\n" " [no-]vector-components Create *_[xyz] symbols for vector variables.\n" -" [no-]v6only Restrict output code to version 6 progs\n" -" features.\n" +" target=v6|v6p|ruamoko Generate code for the specified target VM\n" +" v6 Standard Quake VM (qcc compatible)\n" +" v6p *QuakeForge extended v6 instructions\n" +" ruamoko QuakeForge SIMD instructions\n" "\n" "For details, see the qfcc(1) manual page\n" ); @@ -264,6 +277,21 @@ notice_usage (void) exit (0); } +static void +bug_usage (void) +{ + printf ("%s - QuakeForge Code Compiler\n", this_program); + printf ("Bug options\n"); + printf ( +" help Display his text.\n" +" none Turn off all bugs (don't we wish: messages).\n" +" die Change bugs to internal errors.\n" +"\n" +"This is a developer feature and thus not in the manual page\n" + ); + exit (0); +} + static void add_file (const char *file) { @@ -281,6 +309,8 @@ DecodeArgs (int argc, char **argv) int c; int saw_E = 0, saw_MD = 0; + add_cpp_undef ("-undef"); + add_cpp_undef ("-nostdinc"); add_cpp_def ("-D__QFCC__=1"); add_cpp_def ("-D__QUAKEC__=1"); @@ -289,6 +319,7 @@ DecodeArgs (int argc, char **argv) options.code.vector_components = -1; options.code.crc = -1; options.code.fast_float = true; + options.code.promote_float = true; options.warnings.uninited_variable = true; options.warnings.unused = true; options.warnings.executable = true; @@ -302,7 +333,6 @@ DecodeArgs (int argc, char **argv) options.single_cpp = true; options.save_temps = false; options.verbosity = 0; - options.strip_path = 0; sourcedir = ""; progs_src = "progs.src"; @@ -323,7 +353,7 @@ DecodeArgs (int argc, char **argv) } break; case 'l': // lib file - add_file (va ("-l%s", NORMALIZE (optarg))); + add_file (va (0, "-l%s", NORMALIZE (optarg))); break; case 'L': linker_add_path (NORMALIZE (optarg)); @@ -341,9 +371,6 @@ DecodeArgs (int argc, char **argv) case 'P': // progs-src progs_src = save_string (NORMALIZE (optarg)); break; - case 'p': - options.strip_path = atoi (optarg); - break; case 'F': options.files_dat = true; break; @@ -372,16 +399,25 @@ DecodeArgs (int argc, char **argv) options.traditional = 1; options.advanced = false; options.code.progsversion = PROG_ID_VERSION; + options.code.const_initializers = true; break; case OPT_TRADITIONAL: options.traditional = 2; - options.advanced = false; + options.advanced = 0; options.code.progsversion = PROG_ID_VERSION; + options.code.const_initializers = true; break; case OPT_ADVANCED: options.traditional = 0; - options.advanced = true; + options.advanced = 1; + options.code.progsversion = PROG_V6P_VERSION; + options.code.const_initializers = false; + break; + case OPT_RUAMOKO: + options.traditional = 0; + options.advanced = 2; options.code.progsversion = PROG_VERSION; + options.code.const_initializers = false; break; case OPT_BLOCK_DOT: if (optarg) { @@ -389,7 +425,7 @@ DecodeArgs (int argc, char **argv) char *temp = strtok (opts, ","); while (temp) { - qboolean flag = true; + bool flag = true; if (!strncasecmp (temp, "no-", 3)) { flag = false; @@ -411,6 +447,8 @@ DecodeArgs (int argc, char **argv) options.block_dot.flow = flag; } else if (!(strcasecmp (temp, "reaching"))) { options.block_dot.reaching = flag; + } else if (!(strcasecmp (temp, "statements"))) { + options.block_dot.statements = flag; } else if (!(strcasecmp (temp, "live"))) { options.block_dot.live = flag; } else if (!(strcasecmp (temp, "post"))) { @@ -428,6 +466,7 @@ DecodeArgs (int argc, char **argv) options.block_dot.expr = true; options.block_dot.flow = true; options.block_dot.reaching = true; + options.block_dot.statements = true; options.block_dot.live = true; options.block_dot.post = true; } @@ -446,8 +485,21 @@ DecodeArgs (int argc, char **argv) char *temp = strtok (opts, ","); while (temp) { - qboolean flag = true; + bool flag = true; + if (!(strncasecmp (temp, "target=", 7))) { + const char *tgt = temp + 7; + if (!strcasecmp (tgt, "v6")) { + options.code.progsversion = PROG_ID_VERSION; + } else if (!strcasecmp (tgt, "v6p")) { + options.code.progsversion = PROG_V6P_VERSION; + } else if (!strcasecmp (tgt, "ruamoko")) { + options.code.progsversion = PROG_VERSION; + } else { + fprintf (stderr, "unknown target: %s\n", tgt); + exit (1); + } + } if (!strncasecmp (temp, "no-", 3)) { flag = false; temp += 3; @@ -462,6 +514,8 @@ DecodeArgs (int argc, char **argv) options.code.debug = flag; } else if (!(strcasecmp (temp, "fast-float"))) { options.code.fast_float = flag; + } else if (!(strcasecmp (temp, "promote-float"))) { + options.code.promote_float = flag; } else if (!strcasecmp (temp, "help")) { code_usage (); } else if (!(strcasecmp (temp, "local-merging"))) { @@ -478,11 +532,8 @@ DecodeArgs (int argc, char **argv) options.code.vector_calls = flag; } else if (!(strcasecmp (temp, "vector-components"))) { options.code.vector_components = flag; - } else if (!(strcasecmp (temp, "v6only"))) { - if (flag) - options.code.progsversion = PROG_ID_VERSION; - else - options.code.progsversion = PROG_VERSION; + } else if (!(strcasecmp (temp, "const-initializers"))) { + options.code.const_initializers = flag; } temp = strtok (NULL, ","); } @@ -525,7 +576,7 @@ DecodeArgs (int argc, char **argv) options.warnings.redeclared = false; options.warnings.enum_switch = false; } else { - qboolean flag = true; + bool flag = true; if (!strncasecmp (temp, "no-", 3)) { flag = false; @@ -587,6 +638,23 @@ DecodeArgs (int argc, char **argv) free (opts); } break; + case OPT_BUG:{ + char *opts = strdup (optarg); + char *temp = strtok (opts, ","); + + while (temp) { + if (!strcasecmp (temp, "help")) { + bug_usage (); + } else if (!(strcasecmp (temp, "none"))) { + options.bug.silent = true; + } else if (!(strcasecmp (temp, "die"))) { + options.bug.promote = true; + } + temp = strtok (NULL, ","); + } + free (opts); + } + break; case OPT_CPP: // --cpp= cpp_name = save_string (optarg); break; @@ -640,49 +708,65 @@ DecodeArgs (int argc, char **argv) if (saw_MD) options.preprocess_only = 0; if (!source_files && !options.advanced) { - // progs.src mode without --advanced implies --traditional + // progs.src mode without --advanced or --ruamoko implies --traditional // but --extended overrides if (!options.traditional) options.traditional = 2; options.advanced = false; if (!options.code.progsversion) options.code.progsversion = PROG_ID_VERSION; - if (options.code.ifstring == (qboolean) -1) + if (options.code.ifstring == (bool) -1) options.code.ifstring = false; - if (options.code.short_circuit == (qboolean) -1) + if (options.code.short_circuit == (bool) -1) options.code.short_circuit = false; - if (options.code.local_merging == (qboolean) -1) + if (options.code.local_merging == (bool) -1) options.code.local_merging = false; - if (options.code.vector_components == (qboolean) -1) + if (options.code.vector_components == (bool) -1) options.code.vector_components = true; + if (options.math.vector_mult == 0) + options.math.vector_mult = DOT; } if (!options.code.progsversion) options.code.progsversion = PROG_VERSION; if (!options.traditional) { - options.advanced = true; - add_cpp_def ("-D__RUAMOKO__=1"); - add_cpp_def ("-D__RAUMOKO__=1"); - if (options.code.ifstring == (qboolean) -1) + // avanced=2 requires the Ruamoko ISA + options.advanced = 2 - (options.code.progsversion < PROG_VERSION); + const char *ruamoko = va (0, "-D__RUAMOKO__=%d", options.advanced); + add_cpp_def (save_string (ruamoko)); + if (options.code.ifstring == (bool) -1) options.code.ifstring = false; - if (options.code.short_circuit == (qboolean) -1) + if (options.code.short_circuit == (bool) -1) options.code.short_circuit = true; - if (options.code.local_merging == (qboolean) -1) + if (options.code.local_merging == (bool) -1) options.code.local_merging = true; - if (options.code.vector_components == (qboolean) -1) + if (options.code.vector_components == (bool) -1) options.code.vector_components = false; + if (options.math.vector_mult == 0) + options.math.vector_mult = options.advanced == 1 ? DOT : HADAMARD; + } else { + options.code.promote_float = 0; } if (options.code.progsversion == PROG_ID_VERSION) { + options.code.promote_float = 0; add_cpp_def ("-D__VERSION6__=1"); - if (options.code.crc == (qboolean) -1) + if (options.code.crc == (bool) -1) options.code.crc = true; } else { - if (options.code.crc == (qboolean) -1) + if (options.code.crc == (bool) -1) options.code.crc = false; } + if (options.traditional && options.advanced) { + fprintf (stderr, + "%s: internal error: traditional and advanced twisted\n", + this_program); + abort (); + } + // add the default paths if (!options.no_default_paths) { - add_cpp_def (nva ("-I%s", QFCC_INCLUDE_PATH)); + add_cpp_sysinc ("-isystem"); + add_cpp_sysinc (QFCC_INCLUDE_PATH); linker_add_path (QFCC_LIB_PATH); } diff --git a/tools/qfcc/source/pragma.c b/tools/qfcc/source/pragma.c index c5e8e816d..af6897c00 100644 --- a/tools/qfcc/source/pragma.c +++ b/tools/qfcc/source/pragma.c @@ -39,33 +39,57 @@ #endif #include -#include "QF/pr_comp.h" +#include "QF/alloc.h" -#include "diagnostic.h" -#include "opcodes.h" -#include "options.h" -#include "pragma.h" -#include "type.h" +#include "QF/progs/pr_comp.h" + +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/opcodes.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/pragma.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/type.h" + +#include "tools/qfcc/source/qc-parse.h" + +typedef struct pragma_arg_s { + struct pragma_arg_s *next; + const char *arg; +} pragma_arg_t; + +ALLOC_STATE (pragma_arg_t, pragma_args); +static pragma_arg_t *pragma_args; +static pragma_arg_t **pragma_args_tail = &pragma_args; static void set_traditional (int traditional) { switch (traditional) { + case -1: + options.traditional = 0; + options.advanced = 2; + options.code.progsversion = PROG_VERSION; + type_default = &type_int; + type_long_int = &type_long; + type_ulong_uint = &type_ulong; + break; case 0: options.traditional = 0; - options.advanced = true; - options.code.progsversion = PROG_VERSION; - type_default = &type_integer; + options.advanced = 1; + options.code.progsversion = PROG_V6P_VERSION; + type_default = &type_int; + type_long_int = &type_int; + type_ulong_uint = &type_uint; break; case 1: options.traditional = 1; - options.advanced = false; + options.advanced = 0; options.code.progsversion = PROG_ID_VERSION; type_default = &type_float; break; case 2: options.traditional = 2; - options.advanced = false; + options.advanced = 0; options.code.progsversion = PROG_ID_VERSION; type_default = &type_float; break; @@ -73,20 +97,133 @@ set_traditional (int traditional) opcode_init (); // reset the opcode table } -void -pragma (const char *id) +static void +set_bug (pragma_arg_t *args) { + if (!args) { + warning (0, "missing bug flag"); + return; + } + const char *flag = args->arg; + if (!strcmp (flag, "none")) { + options.bug.silent = true; + } else if (!strcmp (flag, "!none")) { + options.bug.silent = false; + } else if (!strcmp (flag, "die")) { + options.bug.promote = true; + } else if (!strcmp (flag, "!die")) { + options.bug.promote = false; + } + if (args->next) { + warning (0, "pragma bug: ignoring extra arguments"); + } +} + +static void +set_warn (pragma_arg_t *args) +{ + if (!args) { + warning (0, "missing warn flag"); + return; + } + const char *flag = args->arg; + if (!strcmp (flag, "error")) { + options.warnings.promote = true; + } else if (!strcmp (flag, "!error")) { + options.warnings.promote = false; + } + if (args->next) { + warning (0, "pragma warn: ignoring extra arguments"); + } +} + +static void +set_optimize (pragma_arg_t *args) +{ + if (!args) { + warning (0, "missing optimize flag"); + return; + } + const char *flag = args->arg; + if (!strcmp (flag, "on") || !strcmp (flag, "!off")) { + options.code.optimize = true; + } else if (!strcmp (flag, "!on") || !strcmp (flag, "off")) { + options.code.optimize = false; + } + if (args->next) { + warning (0, "pragma optimize: ignoring extra arguments"); + } +} + +static pragma_arg_t * +set_vector_mult (pragma_arg_t *args) +{ + if (!args) { + warning (0, "missing vector_mult arg"); + return args; + } + const char *op = args->arg; + if (!strcmp (op, "@dot")) { + options.math.vector_mult = DOT; + } else { + warning (0, "unknown vector_mult arg: %s", op); + } + return args->next; +} + +static void +set_math (pragma_arg_t *args) +{ + if (!args) { + warning (0, "missing math arg"); + return; + } + while (args) { + const char *a = args->arg; + if (!strcmp (a, "vector_mult")) { + args = set_vector_mult (args->next); + } + } +} + +void +pragma_process () +{ + if (!pragma_args) { + warning (0, "empty pragma"); + return; + } + const char *id = pragma_args->arg; if (!strcmp (id, "traditional")) { set_traditional (2); - return; - } - if (!strcmp (id, "extended")) { + } else if (!strcmp (id, "extended")) { set_traditional (1); - return; - } - if (!strcmp (id, "advanced")) { + } else if (!strcmp (id, "advanced")) { set_traditional (0); - return; + } else if (!strcmp (id, "ruamoko") || !strcmp (id, "raumoko")) { + set_traditional (-1); + } else if (!strcmp (id, "bug")) { + set_bug (pragma_args->next); + } else if (!strcmp (id, "warn")) { + set_warn (pragma_args->next); + } else if (!strcmp (id, "optimize")) { + set_optimize (pragma_args->next); + } else if (!strcmp (id, "math")) { + set_math (pragma_args->next); + } else { + warning (0, "unknown pragma: '%s'", id); } - warning (0, "unknown pragma: %s", id); + *pragma_args_tail = pragma_args_freelist; + pragma_args_tail = &pragma_args; + pragma_args = 0; +} + +void +pragma_add_arg (const char *id) +{ + pragma_arg_t *arg; + ALLOC (16, pragma_arg_t, pragma_args, arg); + arg->arg = save_string (id); + *pragma_args_tail = arg; + pragma_args_tail = &arg->next; } diff --git a/tools/qfcc/source/qc-lex.l b/tools/qfcc/source/qc-lex.l index 7d190cb48..d5fbcb71e 100644 --- a/tools/qfcc/source/qc-lex.l +++ b/tools/qfcc/source/qc-lex.l @@ -45,22 +45,22 @@ #include #include -#include "class.h" -#include "debug.h" -#include "diagnostic.h" -#include "expr.h" -#include "grab.h" -#include "options.h" -#include "pragma.h" -#include "qfcc.h" -#include "shared.h" -#include "strpool.h" -#include "struct.h" -#include "symtab.h" -#include "type.h" -#include "value.h" +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/debug.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/grab.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/pragma.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/shared.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/struct.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" -#include "qc-parse.h" +#include "tools/qfcc/source/qc-parse.h" #ifndef YY_PROTO # define YY_PROTO(x) x @@ -72,8 +72,17 @@ #define YY_DECL int yylex YY_PROTO(( void )) YY_DECL; +int yyget_lineno (void) __attribute__((pure)); +int yyget_leng (void) __attribute__((pure)); +int yywrap (void) __attribute__((const)); +char *yyget_text (void) __attribute__((pure)); +int yyget_debug (void) __attribute__((pure)); +FILE *yyget_in (void) __attribute__((pure)); +FILE *yyget_out (void) __attribute__((pure)); -static int keyword_or_id (char *token); +static int keyword_or_id (const char *token); +static expr_t *parse_float_vector (const char *token, int width); +static expr_t *parse_int_vector (const char *token, int width); extern QC_YYSTYPE qc_yylval; @@ -86,7 +95,14 @@ B [01] X [0-9a-fA-F] ID [a-zA-Z_][a-zA-Z_0-9]* FLOAT ({D}+|{D}*\.{D}+|{D}+\.{D}*)([eE]{m}?{D}+)? +FLOATf {FLOAT}[fF] +FLOATd {FLOAT}[dD] +FCOMP {m}?{FLOAT} +FD [fFdD] INT ({D}+|0[xX]{X}+|0[bB]{B}) +ICOMP {m}?{INT} +UL ([uU]?([lL][lL]?)?) +ULFD ({UL}|{FD}) RANGE \.\. ELLIPSIS \.\.\. FRAMEID {ID}(\.{ID})* @@ -113,26 +129,62 @@ STRING \"(\\.|[^"\\])*\" ^{s}*#{s}*pragma{s}+ { BEGIN (PRAGMA); } -{INT}+[uU]? { +{INT}+{UL}? { const char *c = yytext + yyleng - 1; - int i; + pr_long_t i; if (yytext[0] == '0' && tolower (yytext[1] == 'b')) i = strtol (yytext + 2, 0, 2); else i = strtol (yytext, 0, 0); - if (*c == 'u' || *c == 'U') - qc_yylval.expr = new_integer_expr (i);//FIXME - else - qc_yylval.expr = new_integer_expr (i); + if (tolower (*c) == 'u') { + if (tolower (c[1]) == 'l') { + qc_yylval.expr = new_ulong_expr (i); + } else { + qc_yylval.expr = new_uint_expr (i); + } + } else { + if (tolower (c[1]) == 'l') { + qc_yylval.expr = new_long_expr (i); + } else { + qc_yylval.expr = new_int_expr (i); + qc_yylval.expr->implicit = 1; + } + } return VALUE; } {FLOAT} { + // advanced code defaults to double, but traditional + // and extended code defaults to float + if (options.traditional < 1) { + double d = strtod (yytext, 0); + qc_yylval.expr = new_double_expr (d); + qc_yylval.expr->implicit = 1; + } else { + float f = strtof (yytext, 0); + qc_yylval.expr = new_float_expr (f); + } + return VALUE; + } +{FLOATf} { float f = strtof (yytext, 0); qc_yylval.expr = new_float_expr (f); return VALUE; } +{FLOATd} { + // advanced code defaults to double, but traditional + // and extended code defaults to float + if (options.traditional < 1) { + double d = strtod (yytext, 0); + qc_yylval.expr = new_double_expr (d); + } else { + float f = strtof (yytext, 0); + qc_yylval.expr = new_float_expr (f); + warning (0, "truncating double constant to float"); + } + return VALUE; + } {ID} { int tok = keyword_or_id(yytext); @@ -152,19 +204,33 @@ STRING \"(\\.|[^"\\])*\" } @ return '@'; -'{s}*{m}?{FLOAT}{s}+{m}?{FLOAT}{s}+{m}?{FLOAT}{s}*' { - vec3_t v; - sscanf (yytext, "' %f %f %f '", - &v[0], &v[1], &v[2]); - qc_yylval.expr = new_vector_expr (v); +'{s}*{ICOMP}{s}+{ICOMP}{s}*'{ULFD}? { + qc_yylval.expr = parse_int_vector (yytext, 2); return VALUE; } -'{s}*{m}?{FLOAT}{s}+{m}?{FLOAT}{s}+{m}?{FLOAT}{s}+{m}?{FLOAT}{s}*' { - quat_t q; - sscanf (yytext, "' %f %f %f %f'", - &q[0], &q[1], &q[2], &q[3]); - qc_yylval.expr = new_quaternion_expr (q); +'{s}*{ICOMP}{s}+{ICOMP}{s}+{ICOMP}{s}*'{ULFD}? { + qc_yylval.expr = parse_int_vector (yytext, 3); + return VALUE; + } + +'{s}*{ICOMP}{s}+{ICOMP}{s}+{ICOMP}{s}+{ICOMP}{s}*'{ULFD}? { + qc_yylval.expr = parse_int_vector (yytext, 4); + return VALUE; + } + +'{s}*{FCOMP}{s}+{FCOMP}{s}*'{FD}? { + qc_yylval.expr = parse_float_vector (yytext, 2); + return VALUE; + } + +'{s}*{FCOMP}{s}+{FCOMP}{s}+{FCOMP}{s}*'{FD}? { + qc_yylval.expr = parse_float_vector (yytext, 3); + return VALUE; + } + +'{s}*{FCOMP}{s}+{FCOMP}{s}+{FCOMP}{s}+{FCOMP}{s}*'{FD}? { + qc_yylval.expr = parse_float_vector (yytext, 4); return VALUE; } @@ -173,7 +239,7 @@ STRING \"(\\.|[^"\\])*\" if (str[1]) warning (0, "multibyte char constant"); - qc_yylval.expr = new_integer_expr (*str); + qc_yylval.expr = new_int_expr (*str); return VALUE; } @@ -182,6 +248,11 @@ STRING \"(\\.|[^"\\])*\" return ASX; } +"%%=" { + qc_yylval.op = MOD; + return ASX; + } + "<<=" { qc_yylval.op = SHL; return ASX; @@ -197,6 +268,11 @@ STRING \"(\\.|[^"\\])*\" return yytext[0]; } +"%%" { + qc_yylval.pointer = 0; // ensure pointer vals are null + return MOD; + } + {ELLIPSIS} return ELLIPSIS; "<<" return SHL; @@ -224,7 +300,7 @@ STRING \"(\\.|[^"\\])*\" "$"{s}*{FRAMEID} { int ret = do_grab (yytext); if (ret >= 0) { - qc_yylval.expr = new_integer_expr (ret); + qc_yylval.expr = new_int_expr (ret); return VALUE; } else { BEGIN (-ret); @@ -238,9 +314,13 @@ STRING \"(\\.|[^"\\])*\" write_frame_macros (s); BEGIN (GRAB_OTHER); // ignore rest of line } -{ID} { pragma (yytext); } +{ID} { pragma_add_arg (yytext); } +@{ID} { pragma_add_arg (yytext); } <*>\r*\n { + if (YY_START == PRAGMA) { + pragma_process (); + } pr.source_line++; BEGIN (INITIAL); } @@ -260,21 +340,29 @@ yywrap (void) typedef struct { const char *name; int value; - type_t *type; + specifier_t spec; } keyword_t; +// These keywords are part of the Ruamoko language and require the QuakeForge +// Ruamoko VM. +static keyword_t rua_keywords[] = { +#define VEC_TYPE(type_name, base_type) \ + { #type_name, TYPE_SPEC, .spec = { .type = &type_##type_name } }, +#include "tools/qfcc/include/vec_types.h" +}; + // These keywords are all part of the Ruamoko (Objective-QC) language. // The first time any one of them is encountered, the class system will be // initialized. // If not compiling for the QuakeForge VM, or if Ruamoko has been disabled, // then they will be unavailable as keywords. static keyword_t obj_keywords[] = { - {"id", OBJECT, &type_id }, - {"Class", TYPE, &type_Class }, - {"Method", TYPE, &type_obj_method}, - {"Super", TYPE, &type_obj_super }, - {"SEL", TYPE, &type_SEL }, - {"IMP", TYPE, &type_IMP }, + {"id", OBJECT_NAME, .spec = { .type = &type_id } }, + {"Class", TYPE_SPEC, .spec = { .type = &type_Class } }, + {"Method", TYPE_SPEC, .spec = { .type = &type_method } }, + {"Super", TYPE_SPEC, .spec = { .type = &type_super } }, + {"SEL", TYPE_SPEC, .spec = { .type = &type_SEL } }, + {"IMP", TYPE_SPEC, .spec = { .type = &type_IMP } }, {"@class", CLASS }, {"@defs", DEFS }, @@ -305,6 +393,7 @@ static keyword_t obj_keywords[] = { // make the language features available to traditional code. static keyword_t at_keywords[] = { {"for", FOR }, + {"goto", GOTO }, {"break", BREAK }, {"continue", CONTINUE}, {"switch", SWITCH }, @@ -318,39 +407,49 @@ static keyword_t at_keywords[] = { {"extern", EXTERN }, {"static", STATIC }, {"sizeof", SIZEOF }, - {"nosave", NOSAVE }, {"not", NOT }, }; // These keywords require the QuakeForge VM to be of any use. ie, they cannot // be supported (sanely) by v6 progs. static keyword_t qf_keywords[] = { - {"quaternion", TYPE, &type_quaternion}, - {"int", TYPE, &type_integer }, - {"unsigned", TYPE, &type_integer },//FIXME - {"function", TYPE, &type_function }, + {"quaternion", TYPE_SPEC, .spec = { .type = &type_quaternion } }, + {"double", TYPE_SPEC, .spec = { .type = &type_double } }, + {"int", TYPE_SPEC, .spec = { .type = &type_int } }, + {"unsigned", TYPE_SPEC, .spec = { .is_unsigned = 1 } }, + {"signed", TYPE_SPEC, .spec = { .is_signed = 1 } }, + {"long", TYPE_SPEC, .spec = { .is_long = 1 } }, + {"short", TYPE_SPEC, .spec = { .is_short = 1 } }, - {"@args", ARGS, 0 }, - {"@va_list", TYPE, &type_va_list }, - {"@param", TYPE, &type_param }, + {"@function", TYPE_SPEC, .spec = { .type = &type_func } }, + {"@args", ARGS, }, + {"@va_list", TYPE_SPEC, .spec = { .type = &type_va_list } }, + {"@param", TYPE_SPEC, .spec = { .type = &type_param } }, + {"@return", AT_RETURN, }, + + {"@cross", CROSS, }, + {"@dot", DOT, }, + {"@hadamard", HADAMARD, }, }; -// These keywors are always available. Other than @system and @overload, they +// These keywors are always available. Other than the @ keywords, they // form traditional QuakeC. static keyword_t keywords[] = { - {"void", TYPE, &type_void }, - {"float", TYPE, &type_float }, - {"string", TYPE, &type_string}, - {"vector", TYPE, &type_vector}, - {"entity", TYPE, &type_entity}, - {"local", LOCAL, 0 }, - {"return", RETURN, 0 }, - {"while", WHILE, 0 }, - {"do", DO, 0 }, - {"if", IF, 0 }, - {"else", ELSE, 0 }, - {"@system", SYSTEM, 0 }, - {"@overload", OVERLOAD, 0 }, + {"void", TYPE_SPEC, .spec = { .type = &type_void } }, + {"float", TYPE_SPEC, .spec = { .type = &type_float } }, + {"string", TYPE_SPEC, .spec = { .type = &type_string } }, + {"vector", TYPE_SPEC, .spec = { .type = &type_vector } }, + {"entity", TYPE_SPEC, .spec = { .type = &type_entity } }, + {"local", LOCAL, }, + {"return", RETURN, }, + {"while", WHILE, }, + {"do", DO, }, + {"if", IF, }, + {"else", ELSE, }, + {"@system", SYSTEM, }, + {"@overload", OVERLOAD, }, + {"@attribute", ATTRIBUTE, }, + {"@handle", HANDLE, }, }; static const char * @@ -364,24 +463,45 @@ process_keyword (keyword_t *keyword, const char *token) { if (keyword->value == STRUCT) { qc_yylval.op = token[0]; - } else if (keyword->value == OBJECT) { + } else if (keyword->value == OBJECT_NAME) { symbol_t *sym; sym = symtab_lookup (current_symtab, token); qc_yylval.symbol = sym; + // the global id symbol is always just a name so attempts to redefine + // it globally can be caught and treated as an error, but it needs to + // be redefinable when in an enclosing scope. + if (sym->sy_type == sy_name) { + // this is the global id (object) + qc_yylval.spec = (specifier_t) { + .type = sym->type, + .sym = sym, + }; + return OBJECT_NAME; + } else if (sym->sy_type == sy_type) { + // id has been redeclared via a typedef + qc_yylval.spec = (specifier_t) { + .type = sym->type, + .sym = sym, + }; + return TYPE_NAME; + } + // id has been redelcared as a variable (hopefully) + return NAME; } else { - qc_yylval.type = keyword->type; + qc_yylval.spec = keyword->spec; } return keyword->value; } static int -keyword_or_id (char *token) +keyword_or_id (const char *token) { static hashtab_t *keyword_tab; static hashtab_t *qf_keyword_tab; static hashtab_t *at_keyword_tab; static hashtab_t *obj_keyword_tab; + static hashtab_t *rua_keyword_tab; keyword_t *keyword = 0; symbol_t *sym; @@ -389,10 +509,11 @@ keyword_or_id (char *token) if (!keyword_tab) { size_t i; - keyword_tab = Hash_NewTable (253, keyword_get_key, 0, 0); - qf_keyword_tab = Hash_NewTable (253, keyword_get_key, 0, 0); - at_keyword_tab = Hash_NewTable (253, keyword_get_key, 0, 0); - obj_keyword_tab = Hash_NewTable (253, keyword_get_key, 0, 0); + keyword_tab = Hash_NewTable (253, keyword_get_key, 0, 0, 0); + qf_keyword_tab = Hash_NewTable (253, keyword_get_key, 0, 0, 0); + at_keyword_tab = Hash_NewTable (253, keyword_get_key, 0, 0, 0); + obj_keyword_tab = Hash_NewTable (253, keyword_get_key, 0, 0, 0); + rua_keyword_tab = Hash_NewTable (253, keyword_get_key, 0, 0, 0); #define NUMKEYS(_k) (sizeof (_k) / sizeof (_k[0])) @@ -404,12 +525,19 @@ keyword_or_id (char *token) Hash_Add (at_keyword_tab, &at_keywords[i]); for (i = 0; i < NUMKEYS(obj_keywords); i++) Hash_Add (obj_keyword_tab, &obj_keywords[i]); + for (i = 0; i < NUMKEYS(rua_keywords); i++) + Hash_Add (rua_keyword_tab, &rua_keywords[i]); } if (options.traditional < 1) { - keyword = Hash_Find (obj_keyword_tab, token); - if (keyword) { - if (!obj_initialized) - class_init (); + if (options.code.progsversion == PROG_VERSION) { + keyword = Hash_Find (rua_keyword_tab, token); + } + if (!keyword) { + keyword = Hash_Find (obj_keyword_tab, token); + if (keyword) { + if (!obj_initialized) + class_init (); + } } if (!keyword) keyword = Hash_Find (qf_keyword_tab, token); @@ -432,16 +560,204 @@ keyword_or_id (char *token) if (!sym) sym = new_symbol (token); qc_yylval.symbol = sym; - if (sym->sy_type == sy_type) + if (sym->sy_type == sy_type) { + qc_yylval.spec = (specifier_t) { + .type = sym->type, + .sym = sym, + }; return TYPE_NAME; + } if (sym->sy_type == sy_class) return CLASS_NAME; return NAME; } +static expr_t * +parse_int_vector (const char *token, int width) +{ + char t1 = 0, t2 = 0; + type_t *type = 0; + + union { + pr_long_t l[4]; + pr_type_t t[PR_SIZEOF (dvec4)]; + } long_data = {}; + pr_type_t *data = __builtin_choose_expr ( + sizeof (pr_long_t) == sizeof (int64_t), long_data.t, (void) 0); + + switch (width) { + case 4: + sscanf (token, "' %"SCNi64" %"SCNi64" %"SCNi64" %"SCNi64" '%c%c", + &long_data.l[0], &long_data.l[1], + &long_data.l[2], &long_data.l[3], &t1, &t2); + break; + case 3: + sscanf (token, "' %"SCNi64" %"SCNi64" %"SCNi64" '%c%c", + &long_data.l[0], &long_data.l[1], + &long_data.l[2], &t1, &t2); + break; + case 2: + sscanf (token, "' %"SCNi64" %"SCNi64" '%c%c", + &long_data.l[0], &long_data.l[1], &t1, &t2); + break; + } + t1 = tolower (t1); + t2 = tolower (t2); + if (options.code.progsversion < PROG_VERSION) { + if (!t1) { + t1 = 'f'; + } + } + expr_t *expr = 0; + switch (t1) { + case 'u': + if (t2 == 'l') { + type = &type_ulong; + type = vector_type (type, width); + expr = new_value_expr (new_type_value (type, data)); + } else { + type = &type_uint; + union { + pr_uint_t u[4]; + pr_type_t t[PR_SIZEOF (ivec4)]; + } uint_data = { + .u = { + long_data.l[0], + long_data.l[1], + long_data.l[2], + long_data.l[3], + } + }; + data = uint_data.t; + type = vector_type (type, width); + expr = new_value_expr (new_type_value (type, data)); + } + break; + case 'l': + type = &type_long; + type = vector_type (type, width); + expr = new_value_expr (new_type_value (type, data)); + break; + case 'f': + type = &type_float; + union { + pr_float_t f[4]; + pr_type_t t[PR_SIZEOF (vec4)]; + } float_data = { + .f = { + long_data.l[0], + long_data.l[1], + long_data.l[2], + long_data.l[3], + } + }; + data = float_data.t; + type = vector_type (type, width); + expr = new_value_expr (new_type_value (type, data)); + break; + case 'd': + type = &type_double; + union { + pr_double_t d[4]; + pr_type_t t[PR_SIZEOF (dvec4)]; + } double_data = { + .d = { + long_data.l[0], + long_data.l[1], + long_data.l[2], + long_data.l[3], + } + }; + data = double_data.t; + type = vector_type (type, width); + expr = new_value_expr (new_type_value (type, data)); + break; + case 0: + type = &type_int; + union { + pr_int_t i[4]; + pr_type_t t[PR_SIZEOF (ivec4)]; + } int_data = { + .i = { + long_data.l[0], + long_data.l[1], + long_data.l[2], + long_data.l[3], + } + }; + data = int_data.t; + type = vector_type (type, width); + expr = new_value_expr (new_type_value (type, data)); + break; + } + expr->implicit = !t1; + return expr; +} + +static expr_t * +parse_float_vector (const char *token, int width) +{ + char t = 0; + type_t *type = 0; + + union { + pr_double_t d[4]; + pr_type_t t[PR_SIZEOF (dvec4)]; + } double_data = {}; + pr_type_t *data = __builtin_choose_expr ( + sizeof (pr_double_t) == sizeof (double), double_data.t, (void) 0); + + switch (width) { + case 4: + sscanf (token, "' %lf %lf %lf %lf '%c", + &double_data.d[0], &double_data.d[1], + &double_data.d[2], &double_data.d[3], &t); + break; + case 3: + sscanf (token, "' %lf %lf %lf '%c", + &double_data.d[0], &double_data.d[1], + &double_data.d[1], &t); + type = (t == 'f' || t == 'F') ? &type_vec3 : &type_dvec3; + break; + case 2: + sscanf (token, "' %lf %lf '%c", + &double_data.d[0], &double_data.d[1], &t); + type = (t == 'f' || t == 'F') ? &type_vec2 : &type_dvec2; + break; + } + if (options.code.progsversion < PROG_VERSION) { + if (!t) { + t = 'f'; + } + } + if (t == 'f' || t == 'F') { + volatile union { + pr_float_t f[4]; + pr_type_t t[PR_SIZEOF (vec4)]; + } float_data = { + .f = { + double_data.d[0], + double_data.d[1], + double_data.d[2], + double_data.d[3], + } + }; + data = (pr_type_t *) float_data.t; + type = &type_float; + } else { + type = &type_double; + } + type = vector_type (type, width); + expr_t *expr = new_value_expr (new_type_value (type, data)); + expr->implicit = !t; + return expr; +} + #ifdef YY_FLEX_REALLOC_HACK static __attribute__ ((used)) void *(*const yy_flex_realloc_hack)(void *,yy_size_t) = yy_flex_realloc; #else +#ifdef yyunput static __attribute__ ((used)) void (*yyunput_hack)(int, char*) = yyunput; +#endif static __attribute__ ((used)) int (*input_hack)(void) = input; #endif diff --git a/tools/qfcc/source/qc-parse.y b/tools/qfcc/source/qc-parse.y index 2ab9d4144..26e053730 100644 --- a/tools/qfcc/source/qc-parse.y +++ b/tools/qfcc/source/qc-parse.y @@ -42,25 +42,27 @@ #include #include +#include -#include "class.h" -#include "debug.h" -#include "def.h" -#include "diagnostic.h" -#include "emit.h" -#include "expr.h" -#include "function.h" -#include "method.h" -#include "options.h" -#include "qfcc.h" -#include "reloc.h" -#include "shared.h" -#include "strpool.h" -#include "struct.h" -#include "switch.h" -#include "symtab.h" -#include "type.h" -#include "value.h" +#include "tools/qfcc/include/attribute.h" +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/debug.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/emit.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/method.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/shared.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/struct.h" +#include "tools/qfcc/include/switch.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" #define YYDEBUG 1 #define YYERROR_VERBOSE 1 @@ -92,11 +94,12 @@ int yylex (void); %union { int op; - int size; + unsigned size; specifier_t spec; void *pointer; // for ensuring pointer values are null struct type_s *type; struct expr_s *expr; + struct element_s *element; struct function_s *function; struct switch_block_s *switch_block; struct param_s *param; @@ -110,6 +113,8 @@ int yylex (void); struct methodlist_s *methodlist; struct symbol_s *symbol; struct symtab_s *symtab; + struct attribute_s *attribute; + struct designator_s *designator; } // these tokens are common between qc and qp @@ -122,7 +127,7 @@ int yylex (void); %nonassoc STORAGEX %left COMMA -%right '=' ASX PAS /* pointer assign */ +%right '=' ASX %right '?' ':' %left OR %left AND @@ -130,12 +135,14 @@ int yylex (void); %left '^' %left '&' %left EQ NE -%left LE GE LT GT +%left LT GT GE LE +%token NAND NOR XNOR // end of tokens common between qc and qp %left SHL SHR %left '+' '-' -%left '*' '/' '%' +%left '*' '/' '%' MOD SCALE +%left CROSS DOT HADAMARD %right SIZEOF UNARY INCOP %left HYPERUNARY %left '.' '(' '[' @@ -143,55 +150,63 @@ int yylex (void); %token CLASS_NAME NAME %token VALUE STRING -%token LOCAL RETURN WHILE DO IF ELSE FOR BREAK CONTINUE ELLIPSIS -%token NIL IFBE IFB IFAE IFA SWITCH CASE DEFAULT ENUM TYPEDEF -%token ARGS EXTERN STATIC SYSTEM NOSAVE OVERLOAD NOT -%token STRUCT -%token TYPE -%token OBJECT TYPE_NAME +%token LOCAL WHILE DO IF ELSE FOR BREAK CONTINUE +%token RETURN AT_RETURN ELLIPSIS +%token NIL GOTO SWITCH CASE DEFAULT ENUM +%token ARGS TYPEDEF EXTERN STATIC SYSTEM OVERLOAD NOT ATTRIBUTE +%token STRUCT HANDLE +%token TYPE_SPEC TYPE_NAME TYPE_QUAL +%token OBJECT_NAME %token CLASS DEFS ENCODE END IMPLEMENTATION INTERFACE PRIVATE %token PROTECTED PROTOCOL PUBLIC SELECTOR REFERENCE SELF THIS -%type optional_specifiers specifiers local_specifiers %type storage_class save_storage -%type type_specifier type_specifier_or_storage_class -%type type +%type typespec typespec_reserved typespec_nonreserved +%type declspecs declspecs_nosc declspecs_nots +%type declspecs_ts +%type declspecs_nosc_ts declspecs_nosc_nots +%type declspecs_sc_ts declspecs_sc_nots defspecs +%type declarator notype_declarator after_type_declarator +%type param_declarator param_declarator_starttypename +%type param_declarator_nostarttypename +%type absdecl absdecl1 direct_absdecl typename ptr_spec copy_spec +%type qc_comma -%type function_params var_list param_declaration -%type qc_func_params qc_var_list qc_param_decl -%type new_name -%type var_decl function_decl -%type abstract_decl abs_decl +%type attribute_list attribute -%type tag optional_tag -%type struct_specifier struct_def struct_decl +%type function_params +%type qc_func_params qc_param_list qc_first_param qc_param + +%type tag +%type struct_specifier struct_list %type enum_specifier %type optional_enum_list enum_list enumerator_list enumerator %type enum_init %type array_decl -%type func_def_list comma - %type const string -%type ivar_decl decl decl_list +%type ivar_decl +%type decl %type ivars -%type param param_list +%type param_list parameter_list parameter %type methoddef -%type opt_initializer var_initializer local_def +%type var_initializer local_def -%type opt_expr fexpr expr element_list element vector_expr -%type optional_state_expr texpr +%type opt_init_semi opt_expr comma_expr expr +%type compound_init element_list +%type designator designator_spec +%type element +%type ose optional_state_expr texpr vector_expr %type statement statements compound_statement -%type else label break_label continue_label -%type unary_expr cast_expr opt_arg_list arg_list +%type else bool_label break_label continue_label +%type unary_expr ident_expr cast_expr expr_list +%type opt_arg_list arg_list arg_expr %type switch_block -%type identifier - -%type overloaded_identifier +%type identifier label %type identifier_list -%type selector reserved_word +%type protocol_name_list selector reserved_word %type optional_param_list unaryselector keyworddecl %type keywordselector %type methodproto methoddecl @@ -229,32 +244,269 @@ make_spec (type_t *type, storage_class_t storage, int is_typedef, return spec; } +static specifier_t +parse_attributes (attribute_t *attr_list) +{ + specifier_t spec = {}; + for (attribute_t *attr = attr_list; attr; attr = attr->next) { + if (!strcmp (attr->name, "no_va_list")) { + spec.no_va_list = 1; + } else if (!strcmp (attr->name, "nosave")) { + spec.nosave = 1; + } else if (!strcmp (attr->name, "void_return")) { + spec.void_return = 1; + } else { + warning (0, "skipping unknown attribute '%s'", attr->name); + } + } + return spec; +} + +static int +storage_auto (specifier_t spec) +{ + return spec.storage == sc_global || spec.storage == sc_local; +} + static specifier_t spec_merge (specifier_t spec, specifier_t new) { if (new.type) { - if (spec.type && !spec.multi_type) { + // deal with "type " + if (!spec.type || new.sym) { + spec.sym = new.sym; + if (!spec.type) { + spec.type = new.type; + } + } else if (!spec.multi_type) { error (0, "two or more data types in declaration specifiers"); spec.multi_type = 1; } - spec.type = new.type; } - if (new.is_typedef || new.storage) { - if ((spec.is_typedef || spec.storage) && !spec.multi_store) { + if (new.is_typedef || !storage_auto (new)) { + if ((spec.is_typedef || !storage_auto (spec)) && !spec.multi_store) { error (0, "multiple storage classes in declaration specifiers"); spec.multi_store = 1; } spec.storage = new.storage; spec.is_typedef = new.is_typedef; } - spec.is_overload |= new.is_overload; - spec.nosave |= new.nosave; + if ((new.is_unsigned && spec.is_signed) + || (new.is_signed && spec.is_unsigned)) { + if (!spec.multi_type) { + error (0, "both signed and unsigned in declaration specifiers"); + spec.multi_type = 1; + } + } + if ((new.is_long && spec.is_short) || (new.is_short && spec.is_long)) { + if (!spec.multi_store) { + error (0, "both long and short in declaration specifiers"); + spec.multi_store = 1; + } + } + spec.sym = new.sym; + spec.spec_bits |= new.spec_bits; return spec; } +static specifier_t +typename_spec (specifier_t spec) +{ + spec = default_type (spec, 0); + spec.sym->type = find_type (append_type (spec.sym->type, spec.type)); + spec.type = spec.sym->type; + return spec; +} + +static specifier_t +function_spec (specifier_t spec, param_t *params) +{ + // empty param list in an abstract decle does not create a symbol + if (!spec.sym) { + spec.sym = new_symbol (0); + } + spec = default_type (spec, spec.sym); + spec.sym->params = params; + spec.sym->type = append_type (spec.sym->type, parse_params (0, params)); + spec.is_function = 1; //FIXME do proper void(*)() -> ev_func + return spec; +} + +static specifier_t +array_spec (specifier_t spec, unsigned size) +{ + spec = default_type (spec, spec.sym); + spec.sym->type = append_type (spec.sym->type, array_type (0, size)); + return spec; +} + +static specifier_t +pointer_spec (specifier_t quals, specifier_t spec) +{ + spec.sym->type = append_type (spec.sym->type, pointer_type (0)); + return spec; +} + +static specifier_t +parse_qc_params (specifier_t spec, param_t *params) +{ + type_t **type; + // .float () foo; is a field holding a function variable rather + // than a function that returns a float field. + for (type = &spec.type; *type && is_field (*type); + type = &(*type)->t.fldptr.type) { + } + type_t *ret_type = *type; + *type = 0; + + spec.sym = new_symbol (0); + spec.sym->type = spec.type; + spec.type = ret_type; + + spec = function_spec (spec, params); + return spec; +} + +static symbol_t * +funtion_sym_type (specifier_t spec, symbol_t *sym) +{ + sym->type = append_type (spec.sym->type, spec.type); + set_func_type_attrs (sym->type, spec); + sym->type = find_type (sym->type); + return sym; +} + +static symbol_t * +qc_nocode_symbol (specifier_t spec, symbol_t *sym) +{ + sym->params = spec.sym->params; + sym = funtion_sym_type (spec, sym); + return sym; +} + +static symbol_t * +qc_function_symbol (specifier_t spec, symbol_t *sym) +{ + sym = qc_nocode_symbol (spec, sym); + sym = function_symbol (sym, spec.is_overload, 1); + return sym; +} + +static param_t * +make_ellipsis (void) +{ + return new_param (0, 0, 0); +} + +static param_t * +make_param (specifier_t spec) +{ + spec = default_type (spec, spec.sym); + spec.type = find_type (append_type (spec.sym->type, spec.type)); + param_t *param = new_param (0, spec.type, spec.sym->name); + return param; +} + +static param_t * +make_selector (const char *selector, struct type_s *type, const char *name) +{ + param_t *param = new_param (selector, type, name); + return param; +} + +static param_t * +make_qc_param (specifier_t spec, symbol_t *sym) +{ + spec = default_type (spec, sym); + sym->type = spec.type; + param_t *param = new_param (0, sym->type, sym->name); + return param; +} + +static param_t * +make_qc_func_param (specifier_t spec, param_t *params, symbol_t *sym) +{ + spec = parse_qc_params (spec, params); + sym->type = append_type (spec.sym->type, spec.type); + param_t *param = new_param (0, sym->type, sym->name); + return param; +} + +static int +is_anonymous_struct (specifier_t spec) +{ + if (spec.sym) { + return 0; + } + if (!is_struct (spec.type) && !is_union (spec.type)) { + return 0; + } + if (!spec.type->t.symtab || spec.type->t.symtab->parent) { + return 0; + } + // struct and union type names always begin with "tag ". Untagged s/u + // are "tag ..". + if (spec.type->name[4] != '.') { + return 0; + } + return 1; +} + +static int +is_null_spec (specifier_t spec) +{ + static specifier_t null_spec; + return memcmp (&spec, &null_spec, sizeof (spec)) == 0; +} + +static int +use_type_name (specifier_t spec) +{ + spec.sym = new_symbol (spec.sym->name); + spec.sym->type = spec.type; + spec.sym->sy_type = sy_var; + symbol_t *s = symtab_addsymbol (current_symtab, spec.sym); + // a different symbol being returned means that this is a redefinition + // of that symbol in the same scope. However, typedefs to the same type + // are allowed. + if (s != spec.sym && spec.is_typedef && s->sy_type == sy_type + && type_same (s->type, spec.type)) { + spec.sym = s; + } + return !!spec.sym->table; +} + +static void __attribute__((used)) +check_specifiers (specifier_t spec) +{ + if (!is_null_spec (spec)) { + if (!spec.type && !spec.sym) { + warning (0, "useless specifiers"); + } else if (spec.type && !spec.sym) { + if (is_anonymous_struct (spec)){ + warning (0, "unnamed struct/union that defines " + "no instances"); + } else if (!is_enum (spec.type) + && !is_struct (spec.type) && !is_union (spec.type)) { + warning (0, "useless type name in empty declaration"); + } + } else if (!spec.type && spec.sym) { + bug (0, "wha? %p %p", spec.type, spec.sym); + } else { + // a type name (id, typedef, etc) was used as a variable name. + // this is allowed in C, so long as it's in a different scope, + // or the types are the same + if (!use_type_name (spec)) { + error (0, "%s redeclared as different kind of symbol", + spec.sym->name); + } + } + } +} + %} -%expect 0 +%expect 2 %% @@ -275,51 +527,11 @@ external_def_list } | external_def_list external_def | external_def_list obj_def - | error END - { - yyerrok; - current_class = 0; - current_symtab = pr.symtab; - current_storage = sc_global; - } - | error ';' - { - yyerrok; - current_class = 0; - current_symtab = pr.symtab; - current_storage = sc_global; - } - | error '}' - { - yyerrok; - current_class = 0; - current_symtab = pr.symtab; - current_storage = sc_global; - } ; external_def - : optional_specifiers external_decl_list ';' { } - | optional_specifiers ';' { } - | optional_specifiers qc_func_params - { - type_t **type; - $$ = $1; // copy spec bits and storage - // .float () foo; is a field holding a function variable rather - // than a function that returns a float field. - for (type = &$$.type; *type && (*type)->type == ev_field; - type = &(*type)->t.fldptr.type) - ; - *type = parse_params (*type, $2); - $$.type = find_type ($$.type); - if ($$.type->type != ev_field) - $$.params = $2; - } - function_def_list - { - (void) ($3); - } - | optional_specifiers function_decl function_body + : fndef + | datadef | storage_class '{' save_storage { current_storage = $1.storage; @@ -330,169 +542,373 @@ external_def } ; -save_storage - : /* emtpy */ +fndef + : declspecs_ts declarator function_body + | declspecs_nots notype_declarator function_body + | defspecs notype_declarator function_body + ; + +datadef + : defspecs notype_initdecls ';' + | declspecs_nots notype_initdecls ';' + | declspecs_ts initdecls ';' + | declspecs_ts qc_func_params { - $$.storage = current_storage; + $$ = parse_qc_params ($1, $2); + } + qc_func_decls + | declspecs ';' + | error ';' + | error '}' + | ';' + ; + +qc_func_params + : '(' copy_spec qc_param_list ')' { $$ = check_params ($3); } + | '(' copy_spec typespec_reserved ')' + { + if (!is_void ($3.type)) { + PARSE_ERROR; + } + $$ = 0; + } + | '(' copy_spec ')' { $$ = 0; } + ; + +qc_param_list + : qc_first_param + | qc_param_list ',' qc_param + { + $$ = append_params ($1, $3); + } + ; +// quakec function parameters cannot use a typedef as the return type +// in the first parameter (really, they should't use any as standard quakec +// doesn't support typedef, but that seems overly restrictive), howevery, +// they can stil use a typedef first parameter. This is due to grammar issues +qc_first_param + : typespec identifier + { + $$ = make_qc_param ($1, $2); + } + | typespec_reserved qc_func_params identifier + { + $$ = make_qc_func_param ($1, $2, $3); + } + | ELLIPSIS { $$ = make_ellipsis (); } + ; + +qc_param + : typespec identifier + { + $$ = make_qc_param ($1, $2); + } + | typespec qc_func_params identifier + { + $$ = make_qc_func_param ($1, $2, $3); + } + | ELLIPSIS { $$ = make_ellipsis (); } + ; + +/* This rule is used only to get an action before both qc_func_decl and + qc_func_decl_term so that the function spec can be inherited. +*/ +qc_comma + : ',' { $$ = $0; } + ; + +qc_func_decls + : qc_func_decl_list qc_comma qc_func_decl_term + | qc_func_decl_term + ; + +qc_func_decl_term + : qc_nocode_func ';' + | qc_code_func ';' + | qc_code_func %prec IFX + ; + +qc_func_decl_list + : qc_func_decl_list qc_comma qc_func_decl + | qc_func_decl + ; + +qc_func_decl + : qc_nocode_func + | qc_code_func + ; + +qc_nocode_func + : identifier '=' '#' expr + { + specifier_t spec = $0; + symbol_t *sym = $1; + expr_t *expr = $4; + sym = qc_function_symbol (spec, sym); + build_builtin_function (sym, expr, 0, spec.storage); + } + | identifier '=' expr + { + specifier_t spec = $0; + spec.sym = $1; + spec.sym->params = spec.params; + spec.sym->type = find_type (spec.type); + spec.is_function = 0; + declare_symbol (spec, $3, current_symtab); + } + | identifier + { + specifier_t spec = $0; + symbol_t *sym = $1; + if (!local_expr && !is_field (spec.sym->type)) { + sym = qc_function_symbol (spec, sym); + } else { + sym = qc_nocode_symbol (spec, sym); + } + if (!local_expr && !is_field (sym->type)) { + // things might be a confused mess from earlier errors + if (sym->sy_type == sy_func) + make_function (sym, 0, sym->table->space, spec.storage); + } else { + initialize_def (sym, 0, current_symtab->space, spec.storage, + current_symtab); + if (sym->s.def) + sym->s.def->nosave |= spec.nosave; + } } ; -function_body - : optional_state_expr - { - symbol_t *sym = $0; - - if (!$-1.type) - $-1.type = type_default; - sym->type = find_type (append_type (sym->type, $-1.type)); - $$ = function_symbol (sym, $-1.is_overload, 1); - } +qc_code_func + : identifier '=' optional_state_expr save_storage { $$ = current_symtab; - current_func = begin_function ($2, 0, current_symtab, 0); - current_symtab = current_func->symtab; + specifier_t spec = $0; + symbol_t *sym = $1; + sym = qc_function_symbol (spec, sym); + current_func = begin_function (sym, 0, current_symtab, 0, + spec.storage); + current_symtab = current_func->locals; current_storage = sc_local; + $1 = sym; } compound_statement { - build_code_function ($2, $1, $5); - current_symtab = $4; - current_storage = $3.storage; + build_code_function ($1, $3, $6); + current_symtab = $5; + current_storage = $4.storage; current_func = 0; } - | '=' '#' expr ';' - { - symbol_t *sym = $0; + ; - if (!$-1.type) - $-1.type = type_default; - sym->type = find_type (append_type (sym->type, $-1.type)); - sym = function_symbol (sym, $-1.is_overload, 1); - build_builtin_function (sym, $3, 0); +declarator + : after_type_declarator + | notype_declarator + ; + +after_type_declarator + : '(' copy_spec after_type_declarator ')' { $$ = $3; } + | after_type_declarator function_params + { + $$ = function_spec ($1, $2); + } + | after_type_declarator array_decl + { + $$ = array_spec ($1, $2); + } + | '*' ptr_spec after_type_declarator + { + $$ = pointer_spec ($2, $3); + } + | TYPE_NAME + { + $$ = $0; + $$.sym = new_symbol ($1.sym->name); + } + | OBJECT_NAME + { + $$ = $0; + $$.sym = new_symbol ($1.sym->name); } ; -external_decl_list - : external_decl - | external_decl_list ',' { $$ = $0; } - external_decl { (void) ($3); } - ; - -external_decl - : var_decl +copy_spec + : /* empty */ { - specifier_t spec = $0; - type_t *type; - - if (!spec.type) - spec.type = type_default; - type = find_type (append_type ($1->type, spec.type)); - if (spec.is_typedef) { - $1->type = type; - $1->sy_type = sy_type; - symtab_addsymbol (current_symtab, $1); - } else { - initialize_def ($1, type, 0, current_symtab->space, - spec.storage); - if ($1->s.def) - $1->s.def->nosave |= spec.nosave; - } - } - | var_decl var_initializer - { - specifier_t spec = $0; - type_t *type; - - if (!spec.type) - spec.type = type_default; - type = find_type (append_type ($1->type, spec.type)); - if (spec.is_typedef) { - error (0, "typedef %s is initialized", $1->name); - $1->type = type; - $1->sy_type = sy_type; - symtab_addsymbol (current_symtab, $1); - } else { - initialize_def ($1, type, $2, current_symtab->space, - spec.storage); - if ($1->s.def) - $1->s.def->nosave |= spec.nosave; - } - } - | function_decl - { - specifier_t spec = $0; - if (!spec.type) - spec.type = type_default; - $1->type = find_type (append_type ($1->type, spec.type)); - if (spec.is_typedef) { - $1->sy_type = sy_type; - symtab_addsymbol (current_symtab, $1); - } else { - $1 = function_symbol ($1, spec.is_overload, 1); - // things might be a confused mess from earlier errors - if ($1->sy_type == sy_func) - make_function ($1, 0, $1->table->space, spec.storage); - } + $$ = $-1; } ; -storage_class - : EXTERN { $$ = make_spec (0, sc_extern, 0, 0); } - | STATIC { $$ = make_spec (0, sc_static, 0, 0); } - | SYSTEM { $$ = make_spec (0, sc_system, 0, 0); } - | TYPEDEF { $$ = make_spec (0, sc_global, 1, 0); } - | OVERLOAD { $$ = make_spec (0, current_storage, 0, 1); } - | NOSAVE +ptr_spec + : copy_spec // for when no qualifiers are present + ; + +notype_declarator + : '(' copy_spec notype_declarator ')' { $$ = $3; } + | notype_declarator function_params { - $$ = make_spec (0, current_storage, 0, 0); - $$.nosave = 1; + $$ = function_spec ($1, $2); + } + | notype_declarator array_decl + { + $$ = array_spec ($1, $2); + } + | '*' ptr_spec notype_declarator + { + $$ = pointer_spec ($2, $3); + } + | NAME + { + $$ = $0; + $$.sym = new_symbol ($1->name); } ; -optional_specifiers - : specifiers - { - $$ = $1; - if (!$$.storage) - $$.storage = current_storage; - } - | /* empty */ - { - $$ = make_spec (0, current_storage, 0, 0); - } +initdecls + : initdecl + | initdecls ',' { $$ = $0; } initdecl ; -specifiers - : type_specifier_or_storage_class - | specifiers type_specifier_or_storage_class +initdecl + : declarator '=' var_initializer + { declare_symbol ($1, $3, current_symtab); } + | declarator + { declare_symbol ($1, 0, current_symtab); } + ; + +notype_initdecls + : notype_initdecl + | notype_initdecls ',' { $$ = $0; } notype_initdecl + ; + +notype_initdecl + : notype_declarator '=' var_initializer + { declare_symbol ($1, $3, current_symtab); } + | notype_declarator + { declare_symbol ($1, 0, current_symtab); } + ; + +/* various lists of type specifiers, storage class etc */ +declspecs_ts + : declspecs_nosc_ts + | declspecs_sc_ts + ; + +declspecs_nots + : declspecs_nosc_nots + | declspecs_sc_nots + ; + +declspecs_nosc + : declspecs_nosc_nots + | declspecs_nosc_ts + ; + +declspecs + : declspecs_nosc_nots + | declspecs_nosc_ts + | declspecs_sc_nots + | declspecs_sc_ts + ; + +declspecs_nosc_nots + : TYPE_QUAL + | declspecs_nosc_nots TYPE_QUAL { $$ = spec_merge ($1, $2); } ; -type - : type_specifier - | type type_specifier - ; - -type_specifier_or_storage_class - : type_specifier - | storage_class - ; - -type_specifier - : TYPE +declspecs_nosc_ts + : typespec + | declspecs_nosc_ts TYPE_QUAL { - $$ = make_spec ($1, 0, 0, 0); + $$ = spec_merge ($1, $2); } + | declspecs_nosc_ts typespec_reserved + { + $$ = spec_merge ($1, $2); + } + | declspecs_nosc_nots typespec + { + $$ = spec_merge ($1, $2); + } + ; + +declspecs_sc_nots + : storage_class + | declspecs_sc_nots TYPE_QUAL + { + $$ = spec_merge ($1, $2); + } + | declspecs_nosc_nots storage_class + { + $$ = spec_merge ($1, $2); + } + | declspecs_sc_nots storage_class + { + $$ = spec_merge ($1, $2); + } + ; + +declspecs_sc_ts + : declspecs_sc_ts TYPE_QUAL + { + $$ = spec_merge ($1, $2); + } + | declspecs_sc_ts typespec_reserved + { + $$ = spec_merge ($1, $2); + } + | declspecs_sc_nots typespec + { + $$ = spec_merge ($1, $2); + } + | declspecs_nosc_ts storage_class + { + $$ = spec_merge ($1, $2); + } + | declspecs_sc_ts storage_class + { + $$ = spec_merge ($1, $2); + } + ; + +typespec + : typespec_reserved + { + $$ = $1; + if (!$$.storage) { + $$.storage = current_storage; + } + } + | typespec_nonreserved + { + $$ = $1; + if (!$$.storage) { + $$.storage = current_storage; + } + } + ; + +typespec_reserved + : TYPE_SPEC | enum_specifier | struct_specifier - | TYPE_NAME + // NOTE: fields don't parse the way they should. This is not a problem + // for basic types, but functions need special treatment + | '.' typespec_reserved { - $$ = make_spec ($1->type, 0, 0, 0); + // avoid find_type() + $$ = make_spec (field_type (0), 0, 0, 0); + $$.type = append_type ($$.type, $2.type); } - | OBJECT protocolrefs + ; + + +typespec_nonreserved + : TYPE_NAME + | OBJECT_NAME protocolrefs { if ($2) { type_t type = *type_id.t.fldptr.type; @@ -502,6 +918,7 @@ type_specifier } else { $$ = make_spec (&type_id, 0, 0, 0); } + $$.sym = $1.sym; } | CLASS_NAME protocolrefs { @@ -513,10 +930,11 @@ type_specifier } else { $$ = make_spec ($1->type, 0, 0, 0); } + $$.sym = $1; } // NOTE: fields don't parse the way they should. This is not a problem // for basic types, but functions need special treatment - | '.' type_specifier + | '.' typespec_nonreserved { // avoid find_type() $$ = make_spec (field_type (0), 0, 0, 0); @@ -524,9 +942,79 @@ type_specifier } ; -optional_tag - : tag - | /* empty */ { $$ = 0; } +defspecs + : /* empty */ + { + $$ = (specifier_t) {}; + } + ; + +save_storage + : /* emtpy */ + { + $$.storage = current_storage; + } + ; + +function_body + : ose + { + specifier_t spec = default_type ($0, $0.sym); + symbol_t *sym = funtion_sym_type (spec, spec.sym); + $$ = function_symbol (sym, spec.is_overload, 1); + } + save_storage + { + $$ = current_symtab; + current_func = begin_function ($2, 0, current_symtab, 0, + $-1.storage); + current_symtab = current_func->locals; + current_storage = sc_local; + } + compound_statement + { + build_code_function ($2, $1, $5); + current_symtab = $4; + current_storage = $3.storage; + current_func = 0; + } + | '=' '#' expr ';' + { + specifier_t spec = default_type ($0, $0.sym); + symbol_t *sym = funtion_sym_type (spec, spec.sym); + sym = function_symbol (sym, spec.is_overload, 1); + build_builtin_function (sym, $3, 0, spec.storage); + } + ; + +storage_class + : EXTERN { $$ = make_spec (0, sc_extern, 0, 0); } + | STATIC { $$ = make_spec (0, sc_static, 0, 0); } + | SYSTEM { $$ = make_spec (0, sc_system, 0, 0); } + | TYPEDEF { $$ = make_spec (0, sc_global, 1, 0); } + | OVERLOAD { $$ = make_spec (0, current_storage, 0, 1); } + | ATTRIBUTE '(' attribute_list ')' + { + $$ = parse_attributes ($3); + } + ; + +attribute_list + : attribute + | attribute_list ',' attribute + { + if ($3) { + $3->next = $1; + $$ = $3; + } else { + $$ = $1; + } + } + ; + +attribute + : NAME { $$ = new_attribute ($1->name, 0); } + | NAME '(' expr_list ')' { $$ = new_attribute ($1->name, $3); } ; tag : NAME ; @@ -554,6 +1042,7 @@ optional_enum_list enum_list : '{' enum_init enumerator_list optional_comma '}' { + current_symtab = current_symtab->parent; $$ = finish_enum ($3); } ; @@ -563,6 +1052,7 @@ enum_init { $$ = find_enum ($-1); start_enum ($$); + current_symtab = $$->type->t.symtab; } ; @@ -572,23 +1062,55 @@ enumerator_list enumerator { $$ = $0; - (void) ($3); } ; enumerator : identifier { add_enum ($0, $1, 0); } - | identifier '=' fexpr { add_enum ($0, $1, $3); } + | identifier '=' expr { add_enum ($0, $1, $3); } ; struct_specifier - : STRUCT optional_tag '{' + : STRUCT tag struct_list { $$ = $3; } + | STRUCT {$$ = 0;} struct_list { $$ = $3; } + | STRUCT tag { symbol_t *sym; + sym = find_struct ($1, $2, 0); - if (!sym->table) - symtab_addsymbol (current_symtab, sym); - current_symtab = new_symtab (current_symtab, stab_local); + sym->type = find_type (sym->type); + $$ = make_spec (sym->type, 0, 0, 0); + if (!sym->table) { + symtab_t *tab = current_symtab; + while (tab->parent && tab->type == stab_struct) { + tab = tab->parent; + } + symtab_addsymbol (tab, sym); + } + } + | HANDLE tag + { + symbol_t *sym = find_handle ($2, 0); + sym->type = find_type (sym->type); + $$ = make_spec (sym->type, 0, 0, 0); + if (!sym->table) { + symtab_t *tab = current_symtab; + while (tab->parent && tab->type == stab_struct) { + tab = tab->parent; + } + symtab_addsymbol (tab, sym); + } + } + ; + +struct_list + : '{' + { + int op = $-1; + symbol_t *sym = $0; + current_symtab = start_struct (&op, sym, current_symtab); + $1 = op; + $$ = sym; } struct_defs '}' { @@ -596,28 +1118,22 @@ struct_specifier symtab_t *symtab = current_symtab; current_symtab = symtab->parent; - sym = build_struct ($1, $2, symtab, 0); - $$ = make_spec (sym->type, 0, 0, 0); - if (!sym->table) - symtab_addsymbol (current_symtab, sym); - } - | STRUCT tag - { - symbol_t *sym; - - sym = find_struct ($1, $2, 0); - $$ = make_spec (sym->type, 0, 0, 0); - if (!sym->table) - symtab_addsymbol (current_symtab, sym); + if ($1) { + sym = $2; + sym = build_struct ($1, sym, symtab, 0, 0); + $$ = make_spec (sym->type, 0, 0, 0); + if (!sym->table) + symtab_addsymbol (current_symtab, sym); + } } ; struct_defs - : struct_def_list ';' + : component_decl_list | DEFS '(' identifier ')' { $3 = check_undefined ($3); - if (!$3->type || !obj_is_class ($3->type)) { + if (!$3->type || !is_class ($3->type)) { error (0, "`%s' is not a class", $3->name); } else { // replace the struct symbol table with one built from @@ -629,399 +1145,251 @@ struct_defs } ; -struct_def_list - : struct_def - | struct_def_list ';' struct_def - ; - -struct_def - : type struct_decl_list - | type - ; - -struct_decl_list - : struct_decl_list ',' { $$ = $0; } - struct_decl { (void) ($3); } - | struct_decl - ; - -struct_decl - : function_decl +component_decl_list + : component_decl_list2 + | component_decl_list2 component_decl { - if (!$0.type) - $0.type = type_default; - $1->type = append_type ($1->type, $0.type); - $1->type = find_type ($1->type); - symtab_addsymbol (current_symtab, $1); - } - | var_decl - { - if (!$0.type) - $0.type = type_default; - $1->type = append_type ($1->type, $0.type); - $1->type = find_type ($1->type); - symtab_addsymbol (current_symtab, $1); - } - | var_decl ':' expr %prec COMMA {} - | ':' expr %prec COMMA {} - ; - -new_name - : NAME %prec COMMA - { - $$ = $1; - // due to the way declarations work, we need a new symbol at all - // times. redelcarations will be checked later - if ($$->table) - $$ = new_symbol ($1->name); + warning (0, "no semicolon at end of struct or union"); } ; -var_decl - : new_name - { - $$ = $1; - $$->type = 0; - } - | var_decl function_params - { - $$->type = append_type ($$->type, parse_params (0, $2)); - } - | var_decl array_decl - { - $$->type = append_type ($$->type, array_type (0, $2)); - } - | '*' var_decl %prec UNARY - { - $$ = $2; - $$->type = append_type ($$->type, pointer_type (0)); - } - | '(' var_decl ')' { $$ = $2; } +component_decl_list2 + : /* empty */ + | component_decl_list2 component_decl ';' + | component_decl_list2 ';' ; -function_decl - : '*' function_decl +component_decl + : declspecs_nosc_ts components + | declspecs_nosc_ts { - $$ = $2; - $$->type = append_type ($$->type, pointer_type (0)); + if (is_anonymous_struct ($1)) { + // type->name always begins with "tag " + $1.sym = new_symbol (va (0, ".anonymous.%s", + $1.type->name + 4)); + $1.sym->type = $1.type; + $1.sym->sy_type = sy_var; + $1.sym->visibility = vis_anonymous; + symtab_addsymbol (current_symtab, $1.sym); + if (!$1.sym->table) { + error (0, "duplicate field `%s'", $1.sym->name); + } + } } - | function_decl array_decl + | declspecs_nosc_nots components_notype + | declspecs_nosc_nots { internal_error (0, "not implemented"); } + ; + +components + : component_declarator + | components ',' { $$ = $0; } component_declarator + ; + +component_declarator + : declarator { - $$ = $1; - $$->type = append_type ($$->type, array_type (0, $2)); - } - | '(' function_decl ')' { $$ = $2; } - | function_decl function_params - { - $$ = $1; - $$->params = $2; - $$->type = append_type ($$->type, parse_params (0, $2)); - } - | NAME function_params - { - $$ = $1; - $$->params = $2; - $$->type = parse_params (0, $2); + declare_field ($1, current_symtab); } + | declarator ':' expr + | ':' expr + ; + +components_notype + : component_notype_declarator + | components_notype ',' component_notype_declarator + ; + +component_notype_declarator + : notype_declarator { internal_error (0, "not implemented"); } + | notype_declarator ':' expr + | ':' expr ; function_params - : '(' ')' { $$ = 0; } - | '(' ps var_list ')' { $$ = check_params ($3); } + : '(' copy_spec param_list ')' { $$ = check_params ($3); } ; -qc_func_params - : '(' ')' { $$ = 0; } - | '(' ps qc_var_list ')' { $$ = check_params ($3); } - | '(' ps TYPE ')' +param_list + : /* empty */ { $$ = 0; } + | parameter_list + | parameter_list ',' ELLIPSIS { - if ($3 != &type_void) - PARSE_ERROR; - $$ = 0; + $$ = param_append_identifiers ($1, 0, 0); + } + | ELLIPSIS + { + $$ = make_ellipsis (); } ; -ps : ; - -var_list - : param_declaration - | var_list ',' param_declaration +parameter_list + : parameter + | parameter_list ',' parameter { - param_t *p; - - for (p = $1; p->next; p = p->next) - ; - p->next = $3; - $$ = $1; + $$ = append_params ($1, $3); } ; -qc_var_list - : qc_param_decl - | qc_var_list ',' qc_param_decl +parameter + : declspecs_ts param_declarator { - param_t *p; - - for (p = $1; p->next; p = p->next) - ; - p->next = $3; - $$ = $1; + $$ = make_param ($2); + } + | declspecs_ts notype_declarator + { + $$ = make_param ($2); + } + | declspecs_ts absdecl + { + $$ = make_param ($2); + } + | declspecs_nosc_nots notype_declarator + { + $$ = make_param ($2); + } + | declspecs_nosc_nots absdecl + { + $$ = make_param ($2); } ; -param_declaration - : type var_decl +absdecl + : /* empty */ { - if (!$1.type) - $1.type = type_default; - $2->type = find_type (append_type ($2->type, $1.type)); - $$ = new_param (0, $2->type, $2->name); + $$ = $0; + $$.sym = new_symbol (0); } - | abstract_decl { $$ = new_param (0, $1->type, 0); } - | ELLIPSIS { $$ = new_param (0, 0, 0); } + | absdecl1 ; -abstract_decl - : type abs_decl +absdecl1 + : direct_absdecl + | '*' ptr_spec absdecl { - $$ = $2; - if (!$1.type) - $1.type = type_default; - $$->type = find_type (append_type ($$->type, $1.type)); + $$ = pointer_spec ($2, $3); } ; -qc_param_decl - : type new_name +direct_absdecl + : '(' copy_spec absdecl1 ')' { $$ = $3; } + | direct_absdecl function_params { - $2->type = find_type ($1.type); - $$ = new_param (0, $2->type, $2->name); + $$ = function_spec ($1, $2); } - | type qc_func_params new_name + | direct_absdecl array_decl { - type_t **type; - // .float () foo; is a field holding a function variable rather - // than a function that returns a float field. - for (type = &$1.type; *type && (*type)->type == ev_field; - type = &(*type)->t.fldptr.type) - ; - *type = parse_params (*type, $2); - $3->type = find_type ($1.type); - if ($3->type->type != ev_field) - $3->params = $2; - $$ = new_param (0, $3->type, $3->name); + $$ = array_spec ($1, $2); + } + | function_params + { + $$ = function_spec ($0, $1); + } + | array_decl + { + $$ = array_spec ($0, $1); } - | ELLIPSIS { $$ = new_param (0, 0, 0); } ; -abs_decl - : /* empty */ { $$ = new_symbol (""); } - | '(' abs_decl ')' function_params +param_declarator + : param_declarator_starttypename + | param_declarator_nostarttypename + ; + +param_declarator_starttypename + : param_declarator_starttypename function_params + { $$ = $1; internal_error (0, "not implemented"); } + | param_declarator_starttypename array_decl + { $$ = $1; internal_error (0, "not implemented"); } + | TYPE_NAME + { $$ = $1; internal_error (0, "not implemented"); } + | OBJECT_NAME + { $$ = $1; internal_error (0, "not implemented"); } + ; + +param_declarator_nostarttypename + : param_declarator_nostarttypename function_params + { $$ = $1; internal_error (0, "not implemented"); } + | param_declarator_nostarttypename array_decl + { $$ = $1; internal_error (0, "not implemented"); } + | '*' ptr_spec param_declarator_starttypename + { $$ = $3; internal_error (0, "not implemented"); } + | '*' ptr_spec param_declarator_nostarttypename + { $$ = $3; internal_error (0, "not implemented"); } + | '(' copy_spec param_declarator_nostarttypename ')' { $$ = $3; } + ; + +typename + : declspecs_nosc absdecl { - $$ = $2; - $$->type = append_type ($$->type, parse_params (0, $4)); - } - | '*' abs_decl - { - $$ = $2; - $$->type = append_type ($$->type, pointer_type (0)); - } - | abs_decl array_decl - { - $$ = $1; - $$->type = append_type ($$->type, array_type (0, $2)); - } - | '(' abs_decl ')' - { - $$ = $2; + $$ = typename_spec ($2); } ; array_decl - : '[' fexpr ']' + : '[' expr ']' { - $2 = fold_constants ($2); - if (!is_integer_val ($2) || expr_integer ($2) < 1) { + if (is_int_val ($2) && expr_int ($2) > 0) { + $$ = expr_int ($2); + } else if (is_uint_val ($2) && expr_uint ($2) > 0) { + $$ = expr_uint ($2); + } else { error (0, "invalid array size"); $$ = 0; - } else { - $$ = expr_integer ($2); } } | '[' ']' { $$ = 0; } ; -local_specifiers - : LOCAL specifiers { $$ = $2; } - | specifiers { $$ = $1; } - ; - -param_list - : param { $$ = $1; } - | param_list ',' { $$ = $1; } param - { - $$ = $4; - (void) ($3); - } - ; - -param - : type identifier - { - $2 = check_redefined ($2); - $$ = param_append_identifiers ($0, $2, $1.type); - } - ; - -local_decl_list - : decl_list - | qc_func_params - { - type_t **type; - specifier_t spec = $0; // copy spec bits and storage - // .float () foo; is a field holding a function variable rather - // than a function that returns a float field. - for (type = &spec.type; *type && (*type)->type == ev_field; - type = &(*type)->t.fldptr.type) - ; - *type = parse_params (*type, $1); - spec.type = find_type (spec.type); - $$ = spec; - } - func_def_list - { - (void) ($2); - } - ; - -decl_list - : decl_list ',' { $$ = $0; } decl { (void) ($3); } - | decl - ; - decl - : function_decl {} - | var_decl opt_initializer + : declspecs_ts local_expr initdecls ';' { - specifier_t spec = $0; - type_t *type; - storage_class_t sc = $0.storage; - struct defspace_s *space = current_symtab->space; - - if (!spec.type) - spec.type = type_default; - if (sc == sc_static) - space = pr.near_data; - type = find_type (append_type ($1->type, spec.type)); - initialize_def ($1, type, $2, space, sc); - if ($1->s.def) - $1->s.def->nosave |= $0.nosave; + $$ = local_expr; + local_expr = 0; } - ; - -function_def_list - : func_def_list comma func_def_list_term - | func_def_list_term - ; - -func_def_list - : func_def_list comma func_def { $$ = $2; } - | func_def { $$ = $0; } - ; - -/* This rule is used only to get an action before both func_def and - func_def_list_term so that the function spec can be inherited. -*/ -comma: ',' { $$ = $0; } - -func_def_list_term - : non_code_funcion ';' - | code_function ';' - | code_function %prec IFX - ; - -func_def - : non_code_funcion - | code_function - ; - -code_function - : overloaded_identifier code_func - ; - -non_code_funcion - : overloaded_identifier non_code_func - ; - -overloaded_identifier - : identifier + | declspecs_nots local_expr notype_initdecls ';' { - $$ = $1; - $$->type = $0.type; - if (!local_expr && $$->type->type != ev_field) { - $$ = function_symbol ($$, $0.is_overload, 1); - $$->params = $0.params; - } + $$ = local_expr; + local_expr = 0; } + | declspecs_ts local_expr qc_func_params + { + $$ = parse_qc_params ($1, $3); + } + qc_func_decls + { + $$ = local_expr; + local_expr = 0; + } + | declspecs ';' { $$ = 0; } ; -non_code_func - : '=' '#' fexpr +local_expr + : /* emtpy */ { - build_builtin_function ($0, $3, 0); + $$ = $0; + local_expr = new_block_expr (); } - | '=' fexpr - { - symbol_t *sym = $0; - specifier_t spec = $-1; - initialize_def (sym, sym->type, $2, current_symtab->space, - spec.storage); - if (sym->s.def) - sym->s.def->nosave |= spec.nosave; - } - | /* emtpy */ - { - symbol_t *sym = $0; - specifier_t spec = $-1; - if (!local_expr && sym->type->type != ev_field) { - // things might be a confused mess from earlier errors - if (sym->sy_type == sy_func) - make_function (sym, 0, sym->table->space, spec.storage); - } else { - initialize_def (sym, sym->type, 0, current_symtab->space, - spec.storage); - if (sym->s.def) - sym->s.def->nosave |= spec.nosave; - } - } - ; - -code_func - : '=' optional_state_expr - save_storage - { - $$ = current_symtab; - current_func = begin_function ($0, 0, current_symtab, 0); - current_symtab = current_func->symtab; - current_storage = sc_local; - } - compound_statement - { - build_code_function ($0, $2, $5); - current_symtab = $4; - current_storage = $3.storage; - current_func = 0; - } - ; - -opt_initializer - : /*empty*/ { $$ = 0; } - | var_initializer { $$ = $1; } ; var_initializer - : '=' fexpr { $$ = $2; } - | '=' '{' element_list optional_comma '}' { $$ = $3; } + : expr { $$ = $1; } + | compound_init + { + if (!$1 && is_scalar ($-1.type)) { + error (0, "empty scalar initializer"); + } + $$ = $1 ? $1 : new_nil_expr (); + } + ; + +compound_init + : '{' element_list optional_comma '}' { $$ = $2; } + | '{' '}' { $$ = 0; } + ; + +ose + : /* emtpy */ { $$ = 0; } + | SHR vector_expr { $$ = build_state_expr ($2); } ; optional_state_expr @@ -1032,18 +1400,39 @@ optional_state_expr element_list : element { - $$ = new_block_expr (); - append_expr ($$, $1); + $$ = new_compound_init (); + append_element ($$, $1); } - | element_list ',' element + | element_list ',' element { - append_expr ($$, $3); + append_element ($$, $3); } ; element - : '{' element_list optional_comma '}' { $$ = $2; } - | fexpr { $$ = $1; } + : designator '=' compound_init { $$ = new_element ($3, $1); } + | designator '=' expr { $$ = new_element ($3, $1); } + | compound_init { $$ = new_element ($1, 0); } + | expr { $$ = new_element ($1, 0); } + ; + +designator + : designator_spec + | designator designator_spec + { + designator_t *des = $1; + while (des->next) { + des = des->next; + } + des->next = $2; + $$ = $1; + } + ; + +designator_spec + : '.' ident_expr { $$ = new_designator ($2, 0); } + | '.' NAME { $$ = new_designator (new_symbol_expr ($2), 0); } + | '[' expr ']' { $$ = new_designator (0, $2); } ; optional_comma @@ -1051,22 +1440,28 @@ optional_comma | ',' ; -compound_statement - : '{' +push_scope + : /* empty */ { if (!options.traditional) { current_symtab = new_symtab (current_symtab, stab_local); current_symtab->space = current_symtab->parent->space; } } - statements '}' + ; + +pop_scope + : /* empty */ { if (!options.traditional) current_symtab = current_symtab->parent; - $$ = $3; } ; +compound_statement + : '{' push_scope statements '}' pop_scope { $$ = $3; } + ; + statements : /*empty*/ { @@ -1079,19 +1474,8 @@ statements ; local_def - : local_specifiers - { - if (!$1.storage) - $1.storage = sc_local; - $$ = $1; - local_expr = new_block_expr (); - } - local_decl_list ';' - { - $$ = local_expr; - local_expr = 0; - (void) ($2); - } + : LOCAL decl { $$ = $2; } + | decl ; statement @@ -1099,10 +1483,9 @@ statement | error ';' { $$ = 0; yyerrok; } | compound_statement { $$ = $1; } | local_def { $$ = $1; } - | RETURN opt_expr ';' - { - $$ = return_expr (current_func, $2); - } + | RETURN opt_expr ';' { $$ = return_expr (current_func, $2); } + | RETURN compound_init ';' { $$ = return_expr (current_func, $2); } + | AT_RETURN expr ';' { $$ = at_return_expr (current_func, $2); } | BREAK ';' { $$ = 0; @@ -1119,7 +1502,11 @@ statement else error (0, "continue outside of loop"); } - | CASE fexpr ':' + | label + { + $$ = named_label_expr ($1); + } + | CASE expr ':' { $$ = case_label_expr (switch_block, $2); } @@ -1127,12 +1514,17 @@ statement { $$ = case_label_expr (switch_block, 0); } - | SWITCH break_label '(' fexpr switch_block ')' compound_statement + | SWITCH break_label '(' expr switch_block ')' compound_statement { $$ = switch_expr (switch_block, break_label, $7); switch_block = $5; break_label = $2; } + | GOTO NAME + { + expr_t *label = named_label_expr ($2); + $$ = goto_expr (label); + } | IF not '(' texpr ')' statement %prec IFX { $$ = build_if_statement ($2, $4, $6, 0, 0); @@ -1141,13 +1533,16 @@ statement { $$ = build_if_statement ($2, $4, $6, $7, $8); } - | FOR break_label continue_label - '(' opt_expr ';' opt_expr ';' opt_expr ')' statement + | FOR push_scope break_label continue_label + '(' opt_init_semi opt_expr ';' opt_expr ')' statement pop_scope { - $$ = build_for_statement ($5, $7, $9, $11, + if ($6) { + $6 = build_block_expr ($6); + } + $$ = build_for_statement ($6, $7, $9, $11, break_label, continue_label); - break_label = $2; - continue_label = $3; + break_label = $3; + continue_label = $4; } | WHILE break_label continue_label not '(' texpr ')' statement { @@ -1163,7 +1558,7 @@ statement break_label = $2; continue_label = $3; } - | fexpr ';' + | comma_expr ';' { $$ = $1; } @@ -1183,6 +1578,10 @@ else ; label + : NAME ':' + ; + +bool_label : /* empty */ { $$ = new_label_expr (); @@ -1214,8 +1613,17 @@ switch_block } ; +opt_init_semi + : comma_expr ';' + | decl /* contains ; */ + | ';' + { + $$ = 0; + } + ; + opt_expr - : fexpr + : comma_expr | /* empty */ { $$ = 0; @@ -1231,6 +1639,7 @@ unary_expr | '(' expr ')' { $$ = $2; $$->paren = 1; } | unary_expr '(' opt_arg_list ')' { $$ = function_expr ($1, $3); } | unary_expr '[' expr ']' { $$ = array_expr ($1, $3); } + | unary_expr '.' ident_expr { $$ = field_expr ($1, $3); } | unary_expr '.' unary_expr { $$ = field_expr ($1, $3); } | INCOP unary_expr { $$ = incop_expr ($1, $2, 0); } | unary_expr INCOP { $$ = incop_expr ($2, $1, 1); } @@ -1238,19 +1647,25 @@ unary_expr | '-' cast_expr %prec UNARY { $$ = unary_expr ('-', $2); } | '!' cast_expr %prec UNARY { $$ = unary_expr ('!', $2); } | '~' cast_expr %prec UNARY { $$ = unary_expr ('~', $2); } - | '&' cast_expr %prec UNARY { $$ = address_expr ($2, 0, 0); } - | '*' cast_expr %prec UNARY { $$ = pointer_expr ($2); } + | '&' cast_expr %prec UNARY { $$ = address_expr ($2, 0); } + | '*' cast_expr %prec UNARY { $$ = deref_pointer_expr ($2); } | SIZEOF unary_expr %prec UNARY { $$ = sizeof_expr ($2, 0); } - | SIZEOF '(' abstract_decl ')' %prec HYPERUNARY + | SIZEOF '(' typename ')' %prec HYPERUNARY { - $$ = sizeof_expr (0, $3->type); + $$ = sizeof_expr (0, $3.type); } | vector_expr { $$ = new_vector_list ($1); } | obj_expr { $$ = $1; } ; +ident_expr + : OBJECT_NAME { $$ = new_symbol_expr ($1.sym); } + | CLASS_NAME { $$ = new_symbol_expr ($1); } + | TYPE_NAME { $$ = new_symbol_expr ($1.sym); } + ; + vector_expr - : '[' fexpr ',' arg_list ']' + : '[' expr ',' expr_list ']' { expr_t *t = $4; while (t->next) @@ -1261,9 +1676,9 @@ vector_expr ; cast_expr - : '(' abstract_decl ')' cast_expr + : '(' typename ')' cast_expr { - $$ = cast_expr ($2->type, $4); + $$ = cast_expr (find_type ($2.type), $4); } | unary_expr %prec LOW ; @@ -1271,10 +1686,11 @@ cast_expr expr : cast_expr | expr '=' expr { $$ = assign_expr ($1, $3); } + | expr '=' compound_init { $$ = assign_expr ($1, $3); } | expr ASX expr { $$ = asx_expr ($2, $1, $3); } | expr '?' expr ':' expr { $$ = conditional_expr ($1, $3, $5); } - | expr AND label expr { $$ = bool_expr (AND, $3, $1, $4); } - | expr OR label expr { $$ = bool_expr (OR, $3, $1, $4); } + | expr AND bool_label expr { $$ = bool_expr (AND, $3, $1, $4); } + | expr OR bool_label expr { $$ = bool_expr (OR, $3, $1, $4); } | expr EQ expr { $$ = binary_expr (EQ, $1, $3); } | expr NE expr { $$ = binary_expr (NE, $1, $3); } | expr LE expr { $$ = binary_expr (LE, $1, $3); } @@ -1291,14 +1707,35 @@ expr | expr '|' expr { $$ = binary_expr ('|', $1, $3); } | expr '^' expr { $$ = binary_expr ('^', $1, $3); } | expr '%' expr { $$ = binary_expr ('%', $1, $3); } - ; - -fexpr - : expr { $$ = fold_constants ($1); } + | expr MOD expr { $$ = binary_expr (MOD, $1, $3); } + | expr CROSS expr { $$ = binary_expr (CROSS, $1, $3); } + | expr DOT expr { $$ = binary_expr (DOT, $1, $3); } + | expr HADAMARD expr { $$ = binary_expr (HADAMARD, $1, $3); } ; texpr - : fexpr { $$ = convert_bool ($1, 1); } + : expr { $$ = convert_bool ($1, 1); } + ; + +comma_expr + : expr_list + { + if ($1->next) { + expr_t *res = $1; + $1 = build_block_expr ($1); + $1->e.block.result = res; + } + $$ = $1; + } + ; + +expr_list + : expr + | expr_list ',' expr + { + $3->next = $1; + $$ = $3; + } ; opt_arg_list @@ -1307,14 +1744,19 @@ opt_arg_list ; arg_list - : fexpr - | arg_list ',' fexpr + : arg_expr + | arg_list ',' arg_expr { $3->next = $1; $$ = $3; } ; +arg_expr + : expr + | compound_init + ; + const : VALUE | NIL { $$ = new_nil_expr (); } @@ -1328,9 +1770,9 @@ string identifier : NAME - | OBJECT + | OBJECT_NAME { $$ = $1.sym; } | CLASS_NAME - | TYPE_NAME + | TYPE_NAME { $$ = $1.sym; } ; // Objective-QC stuff @@ -1338,6 +1780,7 @@ identifier obj_def : classdef { } | classdecl + | protocoldecl | protocoldef | { if (!current_class) PARSE_ERROR; } methoddef | END @@ -1376,9 +1819,13 @@ classdecl class_name : identifier %prec CLASS_NOT_CATEGORY { - $1 = check_undefined ($1); - if (!$1->type || !obj_is_class ($1->type)) { - error (0, "`%s' is not a class %p", $1->name, $1->type); + if (!$1->type) { + $$ = get_class ($1, 1); + if (!$1->table) { + symtab_addsymbol (current_symtab, $1); + } + } else if (!is_class ($1->type)) { + error (0, "`%s' is not a class", $1->name); $$ = get_class (0, 1); } else { $$ = $1->type->t.class; @@ -1399,6 +1846,7 @@ new_class_name $1 = check_redefined ($1); $$ = get_class ($1, 1); } + $$->interface_declared = 1; current_class = &$$->class_type; if (!$1->table) symtab_addsymbol (current_symtab, $1); @@ -1417,6 +1865,12 @@ class_with_super new_class_with_super : new_class_name ':' class_name { + if (!$3->interface_declared) { + $3->interface_declared = 1; + error (0, "cannot find interface declaration for `%s', " + "superclass of `%s'", $3->name, $1->name); + } + $1->interface_declared = 1; $1->super_class = $3; $$ = $1; } @@ -1465,17 +1919,18 @@ category_reference } ; - protocol_name : identifier { $$ = get_protocol ($1->name, 0); - if ($$) { - error (0, "redefinition of %s", $1->name); + if ($$ && $$->methods) { + error (0, "redefinition of protocol %s", $1->name); $$ = get_protocol (0, 1); - } else { + } + if (!$$) { $$ = get_protocol ($1->name, 1); } + $$->methods = new_methodlist (); current_class = &$$->class_type; } ; @@ -1493,8 +1948,6 @@ classdef END { current_class = 0; - (void) ($6); - (void) ($9); } | INTERFACE new_class_name protocolrefs { class_add_protocols ($2, $3); } @@ -1506,7 +1959,6 @@ classdef END { current_class = 0; - (void) ($5); } | INTERFACE new_class_with_super protocolrefs { class_add_protocols ($2, $3);} @@ -1520,8 +1972,6 @@ classdef END { current_class = 0; - (void) ($6); - (void) ($9); } | INTERFACE new_class_with_super protocolrefs { class_add_protocols ($2, $3); } @@ -1533,7 +1983,6 @@ classdef END { current_class = 0; - (void) ($5); } | INTERFACE new_category_name protocolrefs @@ -1545,14 +1994,12 @@ classdef END { current_class = 0; - (void) ($4); } | IMPLEMENTATION class_name { class_begin (&$2->class_type); } '{' { $$ = $2; } ivar_decl_list '}' { class_check_ivars ($2, $6); - (void) ($5); } | IMPLEMENTATION class_name { class_begin (&$2->class_type); } | IMPLEMENTATION class_with_super { class_begin (&$2->class_type); } @@ -1560,7 +2007,6 @@ classdef ivar_decl_list '}' { class_check_ivars ($2, $6); - (void) ($5); } | IMPLEMENTATION class_with_super { class_begin (&$2->class_type); } | IMPLEMENTATION category_name { class_begin (&$2->class_type); } @@ -1568,22 +2014,36 @@ classdef | REFERENCE category_reference ';' { } ; +protocoldecl + : protocol + protocol_name_list ';' + { + while ($2) { + get_protocol ($2->name, 1); + $2 = $2->next; + } + } + ; + protocoldef - : PROTOCOL { $$ = current_class; } + : protocol protocol_name - protocolrefs { protocol_add_protocols ($3, $4); $$ = 0; } - methodprotolist { protocol_add_methods ($3, $6); } + protocolrefs { protocol_add_protocols ($2, $3); $$ = 0; } + methodprotolist { protocol_add_methods ($2, $5); } END { - current_class = $2; - (void) ($5); + current_class = $1; } ; +protocol + : PROTOCOL { $$ = current_class; } + ; + protocolrefs : /* emtpy */ { $$ = 0; } | LT { $$ = new_protocol_list (); } - protocol_list GT { $$ = $3; (void) ($2); } + protocol_list GT { $$ = $3; } ; protocol_list @@ -1620,8 +2080,13 @@ ivar_decl_list tab->parent = 0; tab = $$->parent; // preserve the ivars inheritance chain - build_struct ('s', 0, $$, 0); + int base = 0; + if ($0->super_class) { + base = type_size ($0->super_class->type); + } + build_struct ('s', 0, $$, 0, base); $$->parent = tab; + current_visibility = vis_public; } ; @@ -1642,16 +2107,53 @@ ivar_decls ; ivar_decl - : type ivars + : declspecs_nosc_ts ivars + | declspecs_nosc_ts + { + if (is_anonymous_struct ($1)) { + // type->name always begins with "tag " + $1.sym = new_symbol (va (0, ".anonymous.%s", + $1.type->name + 4)); + $1.sym->type = $1.type; + $1.sym->sy_type = sy_var; + $1.sym->visibility = vis_anonymous; + symtab_addsymbol (current_symtab, $1.sym); + if (!$1.sym->table) { + error (0, "duplicate field `%s'", $1.sym->name); + } + } + } + | declspecs_nosc_nots notype_ivars ; ivars - : struct_decl - | ivars ',' { $$ = $0; } struct_decl { (void) ($3); } + : ivar_declarator { } + | ivars ',' { $$ = $0; } ivar_declarator + ; + +notype_ivars + : notype_ivar_declarator { } + | notype_ivars ',' { $$ = $0; } + notype_ivar_declarator + ; + +ivar_declarator + : declarator + { + declare_field ($1, current_symtab); + } + | declarator ':' expr + | ':' expr + ; + +notype_ivar_declarator + : notype_declarator { internal_error (0, "not implemented"); } + | notype_declarator ':' expr + | ':' expr ; methoddef - : ci methoddecl optional_state_expr + : ci methoddecl ose { method_t *method = $2; @@ -1669,12 +2171,13 @@ methoddef $$ = current_symtab; ivar_scope = class_ivar_scope (current_class, current_symtab); - current_func = begin_function (sym, nicename, ivar_scope, 1); + current_func = begin_function (sym, nicename, ivar_scope, 1, + sc_static); class_finish_ivar_scope (current_class, ivar_scope, - current_func->symtab); + current_func->locals); method->func = sym->s.func; method->def = sym->s.func->def; - current_symtab = current_func->symtab; + current_symtab = current_func->locals; current_storage = sc_local; } compound_statement @@ -1692,7 +2195,7 @@ methoddef method->instance = $1; method = class_find_method (current_class, method); sym = method_symbol (current_class, method); - build_builtin_function (sym, $5, 1); + build_builtin_function (sym, $5, 1, sc_static); method->func = sym->s.func; method->def = sym->s.func->def; } @@ -1727,6 +2230,10 @@ methodproto $2->instance = 0; $$ = $2; } + | '-' error ';' + { $$ = new_method (&type_id, make_selector ("", 0, 0), 0); } + | '+' error ';' + { $$ = new_method (&type_id, make_selector ("", 0, 0), 0); } | '-' methoddecl ';' { $2->instance = 1; @@ -1735,29 +2242,23 @@ methodproto ; methoddecl - : '(' abstract_decl ')' unaryselector - { $$ = new_method ($2->type, $4, 0); } + : '(' typename ')' unaryselector + { $$ = new_method ($2.type, $4, 0); } | unaryselector { $$ = new_method (&type_id, $1, 0); } - | '(' abstract_decl ')' keywordselector optional_param_list - { $$ = new_method ($2->type, $4, $5); } + | '(' typename ')' keywordselector optional_param_list + { $$ = new_method ($2.type, $4, $5); } | keywordselector optional_param_list { $$ = new_method (&type_id, $1, $2); } ; optional_param_list : /* empty */ { $$ = 0; } - | ',' ELLIPSIS { $$ = new_param (0, 0, 0); } | ',' param_list { $$ = $2; } - | ',' param_list ',' ELLIPSIS - { - $$ = new_param (0, 0, 0); - $$->next = $2; - } ; unaryselector - : selector { $$ = new_param ($1->name, 0, 0); } + : selector { $$ = make_selector ($1->name, 0, 0); } ; keywordselector @@ -1765,12 +2266,16 @@ keywordselector | keywordselector keyworddecl { $2->next = $1; $$ = $2; } ; +protocol_name_list + : identifier + | protocol_name_list ',' identifier { $3->next = $1; $$ = $3; } + selector : NAME { $$ = $1; } | CLASS_NAME { $$ = $1; } - | OBJECT { $$ = new_symbol (qc_yytext); } - | TYPE { $$ = new_symbol (qc_yytext); } - | TYPE_NAME { $$ = $1; } + | OBJECT_NAME { $$ = new_symbol (qc_yytext); } + | TYPE_SPEC { $$ = new_symbol (qc_yytext); } + | TYPE_NAME { $$ = $1.sym; } | reserved_word ; @@ -1794,21 +2299,21 @@ reserved_word ; keyworddecl - : selector ':' '(' abstract_decl ')' identifier - { $$ = new_param ($1->name, $4->type, $6->name); } + : selector ':' '(' typename ')' identifier + { $$ = make_selector ($1->name, $4.type, $6->name); } | selector ':' identifier - { $$ = new_param ($1->name, &type_id, $3->name); } - | ':' '(' abstract_decl ')' identifier - { $$ = new_param ("", $3->type, $5->name); } + { $$ = make_selector ($1->name, &type_id, $3->name); } + | ':' '(' typename ')' identifier + { $$ = make_selector ("", $3.type, $5->name); } | ':' identifier - { $$ = new_param ("", &type_id, $2->name); } + { $$ = make_selector ("", &type_id, $2->name); } ; obj_expr : obj_messageexpr | SELECTOR '(' selectorarg ')' { $$ = selector_expr ($3); } | PROTOCOL '(' identifier ')' { $$ = protocol_expr ($3->name); } - | ENCODE '(' abstract_decl ')' { $$ = encode_expr ($3->type); } + | ENCODE '(' typename ')' { $$ = encode_expr ($3.type); } | obj_string /* FIXME string object? */ ; @@ -1817,7 +2322,7 @@ obj_messageexpr ; receiver - : fexpr + : expr | CLASS_NAME { $$ = new_symbol_expr ($1); diff --git a/tools/qfcc/source/qfcc.c b/tools/qfcc/source/qfcc.c index fdf455b5d..e0b15f04c 100644 --- a/tools/qfcc/source/qfcc.c +++ b/tools/qfcc/source/qfcc.c @@ -65,31 +65,31 @@ #include #include -#include "qfcc.h" -#include "class.h" -#include "codespace.h" -#include "cpp.h" -#include "debug.h" -#include "def.h" -#include "defspace.h" -#include "diagnostic.h" -#include "emit.h" -#include "expr.h" -#include "function.h" -#include "grab.h" -#include "idstuff.h" -#include "linker.h" -#include "method.h" -#include "obj_file.h" -#include "opcodes.h" -#include "options.h" -#include "reloc.h" -#include "shared.h" -#include "strpool.h" -#include "struct.h" -#include "symtab.h" -#include "type.h" -#include "value.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/codespace.h" +#include "tools/qfcc/include/cpp.h" +#include "tools/qfcc/include/debug.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/emit.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/grab.h" +#include "tools/qfcc/include/idstuff.h" +#include "tools/qfcc/include/linker.h" +#include "tools/qfcc/include/method.h" +#include "tools/qfcc/include/obj_file.h" +#include "tools/qfcc/include/opcodes.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/shared.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/struct.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" options_t options; @@ -137,10 +137,21 @@ InitData (void) if (pr.code) { codespace_delete (pr.code); strpool_delete (pr.strings); + defspace_delete (pr.near_data); + defspace_delete (pr.far_data); + defspace_delete (pr.entity_data); + defspace_delete (pr.type_data); + defspace_delete (pr.debug_data); + strpool_delete (pr.comp_file_set); } - if (pr.linenos) + if (pr.comp_files.a) { + DARRAY_CLEAR (&pr.comp_files); + } + + if (pr.linenos) { free (pr.linenos); + } memset (&pr, 0, sizeof (pr)); pr.source_line = 1; @@ -148,6 +159,9 @@ InitData (void) pr.code = codespace_new (); memset (codespace_newstatement (pr.code), 0, sizeof (dstatement_t)); pr.strings = strpool_new (); + if (options.code.promote_float) { + ReuseString ("@float_promoted@"); + } pr.num_functions = 1; pr.num_linenos = 0; @@ -165,20 +179,23 @@ InitData (void) pr.type_data = defspace_new (ds_backed); defspace_alloc_loc (pr.type_data, 4);// reserve space for a null descriptor + pr.debug_data = defspace_new (ds_backed); + pr.comp_file_set = strpool_new (); + DARRAY_INIT (&pr.comp_files, 16); + pr.symtab = new_symtab (0, stab_global); pr.symtab->space = pr.near_data; current_symtab = pr.symtab; pr.entity_data = defspace_new (ds_virtual); pr.entity_fields = new_symtab (0, stab_global); - pr.entity_fields->space = pr.entity_data;; + pr.entity_fields->space = pr.entity_data; clear_functions (); clear_frame_macros (); clear_classes (); clear_immediates (); clear_selectors (); - chain_initial_types (); } static int @@ -195,41 +212,41 @@ WriteProgs (dprograms_t *progs, int size) pr_type_t *globals; #define P(t,o) ((t *)((char *)progs + progs->o)) - statements = P (dstatement_t, ofs_statements); - functions = P (dfunction_t, ofs_functions); - globaldefs = P (ddef_t, ofs_globaldefs); - fielddefs = P (ddef_t, ofs_fielddefs); - globals = P (pr_type_t, ofs_globals); + statements = P (dstatement_t, statements.offset); + functions = P (dfunction_t, functions.offset); + globaldefs = P (ddef_t, globaldefs.offset); + fielddefs = P (ddef_t, fielddefs.offset); + globals = P (pr_type_t, globals.offset); #undef P - for (i = 0; i < progs->numstatements; i++) { + for (i = 0; i < progs->statements.count; i++) { statements[i].op = LittleShort (statements[i].op); statements[i].a = LittleShort (statements[i].a); statements[i].b = LittleShort (statements[i].b); statements[i].c = LittleShort (statements[i].c); } - for (i = 0; i < (unsigned) progs->numfunctions; i++) { + for (i = 0; i < (unsigned) progs->functions.count; i++) { dfunction_t *func = functions + i; func->first_statement = LittleLong (func->first_statement); - func->parm_start = LittleLong (func->parm_start); + func->params_start = LittleLong (func->params_start); func->locals = LittleLong (func->locals); func->profile = LittleLong (func->profile); - func->s_name = LittleLong (func->s_name); - func->s_file = LittleLong (func->s_file); - func->numparms = LittleLong (func->numparms); + func->name = LittleLong (func->name); + func->file = LittleLong (func->file); + func->numparams = LittleLong (func->numparams); } - for (i = 0; i < progs->numglobaldefs; i++) { + for (i = 0; i < progs->globaldefs.count; i++) { globaldefs[i].type = LittleShort (globaldefs[i].type); globaldefs[i].ofs = LittleShort (globaldefs[i].ofs); - globaldefs[i].s_name = LittleLong (globaldefs[i].s_name); + globaldefs[i].name = LittleLong (globaldefs[i].name); } - for (i = 0; i < progs->numfielddefs; i++) { + for (i = 0; i < progs->fielddefs.count; i++) { fielddefs[i].type = LittleShort (fielddefs[i].type); fielddefs[i].ofs = LittleShort (fielddefs[i].ofs); - fielddefs[i].s_name = LittleLong (fielddefs[i].s_name); + fielddefs[i].name = LittleLong (fielddefs[i].name); } - for (i = 0; i < progs->numglobals; i++) - globals[i].integer_var = LittleLong (globals[i].integer_var); + for (i = 0; i < progs->globals.count; i++) + globals[i].value = LittleLong (globals[i].value); if (!(h = Qopen (options.output_file, "wb"))) Sys_Error ("%s: %s\n", options.output_file, strerror(errno)); @@ -249,12 +266,16 @@ WriteSym (pr_debug_header_t *sym, int size) pr_auxfunction_t *auxfunctions; pr_lineno_t *linenos; - ddef_t *locals; + pr_def_t *locals; + pr_def_t *debug_defs; + pr_type_t *debug_data; #define P(t,o) ((t *)((char *)sym + sym->o)) auxfunctions = P (pr_auxfunction_t, auxfunctions); linenos = P (pr_lineno_t, linenos); - locals = P (ddef_t, locals); + locals = P (pr_def_t, locals); + debug_defs = P (pr_def_t, debug_defs); + debug_data = P (pr_type_t, debug_data); #undef P for (i = 0; i < sym->num_auxfunctions; i++) { @@ -273,8 +294,20 @@ WriteSym (pr_debug_header_t *sym, int size) } for (i = 0; i < sym->num_locals; i++) { locals[i].type = LittleShort (locals[i].type); - locals[i].ofs = LittleShort (locals[i].ofs); - locals[i].s_name = LittleLong (locals[i].s_name); + locals[i].size = LittleShort (locals[i].size); + locals[i].ofs = LittleLong (locals[i].ofs); + locals[i].name = LittleLong (locals[i].name); + locals[i].type_encoding = LittleLong (locals[i].type_encoding); + } + for (i = 0; i < sym->num_debug_defs; i++) { + debug_defs[i].type = LittleShort (debug_defs[i].type); + debug_defs[i].size = LittleShort (debug_defs[i].size); + debug_defs[i].ofs = LittleLong (debug_defs[i].ofs); + debug_defs[i].name = LittleLong (debug_defs[i].name); + debug_defs[i].type_encoding = LittleLong (debug_defs[i].type_encoding); + } + for (i = 0; i < sym->debug_data_size; i++) { + debug_data[i].value = LittleLong (debug_data[i].value); } if (!(h = Qopen (options.debug_file, "wb"))) @@ -295,23 +328,7 @@ begin_compilation (void) } const char * -strip_path (const char *filename) -{ - const char *p = filename; - int i = options.strip_path; - - while (i-- > 0) { - while (*p && *p != '/') - p++; - if (!*p) - break; - filename = ++p; - } - return filename; -} - -static const char * -file_basename (const char *filename) +file_basename (const char *filename, int keepdot) { const char *p; const char *dot; @@ -322,7 +339,7 @@ file_basename (const char *filename) for (dot = p = filename + strlen (filename); p > filename; p--) { if (p[-1] == '/' || p[-1] == '\\') break; - if (p[0] == '.') + if (!keepdot && p[0] == '.') dot = p; } dstring_copysubstr (base, p, dot - p); @@ -377,12 +394,14 @@ compile_to_obj (const char *file, const char *obj, lang_t lang) } *yyin = preprocess_file (file, 0); - if (!*yyin) + if (options.preprocess_only || !*yyin) return !options.preprocess_only; InitData (); + chain_initial_types (); begin_compilation (); - pr.source_file = ReuseString (strip_path (file)); + pr.comp_dir = save_cwd (); + add_source_file (file); err = yyparse () || pr.error_count; fclose (*yyin); if (cpp_name && !options.save_temps) { @@ -391,14 +410,20 @@ compile_to_obj (const char *file, const char *obj, lang_t lang) exit (1); } } - write_frame_macros (va ("%s.frame", file_basename (file))); + if (options.frames_files) { + write_frame_macros (va (0, "%s.frame", file_basename (file, 0))); + } if (!err) { qfo_t *qfo; class_finish_module (); - qfo = qfo_from_progs (&pr); - err = qfo_write (qfo, obj); - qfo_delete (qfo); + err = pr.error_count; + if (!err) { + debug_finish_module (obj); + qfo = qfo_from_progs (&pr); + err = qfo_write (qfo, obj); + qfo_delete (qfo); + } } return err; } @@ -415,8 +440,16 @@ finish_link (void) flags = (QFOD_GLOBAL | QFOD_CONSTANT | QFOD_INITIALIZED | QFOD_NOSAVE); if (options.code.progsversion != PROG_ID_VERSION) { pr_int_t param_size = type_size (&type_param); - linker_add_def (".param_size", &type_integer, flags, + pr_int_t param_alignment = qfo_log2 (type_param.alignment); + linker_add_def (".param_size", &type_int, flags, ¶m_size); + linker_add_def (".param_alignment", &type_int, flags, + ¶m_alignment); + linker_add_def (".xdefs", &type_xdefs, flags, 0); + } + if (options.code.progsversion == PROG_VERSION) { + int stk = (QFOD_GLOBAL | QFOD_INITIALIZED | QFOD_NOSAVE); + linker_add_def (".stack", &type_uint, stk, 0); } if (options.code.debug) { @@ -436,7 +469,12 @@ finish_link (void) } else { int size; dprograms_t *progs; + pr_debug_header_t *sym = 0; + int sym_size = 0; + if (options.code.debug) { + sym = qfo_to_sym (qfo, &sym_size); + } progs = qfo_to_progs (qfo, &size); //finish_compilation (); @@ -450,9 +488,6 @@ finish_link (void) WriteProgs (progs, size); if (options.code.debug) { - pr_debug_header_t *sym; - int sym_size = 0; - sym = qfo_to_sym (qfo, &sym_size); sym->crc = CRC_Block ((byte *) progs, size); WriteSym (sym, sym_size); } @@ -460,7 +495,7 @@ finish_link (void) return 0; } -static lang_t +static __attribute__((pure)) lang_t file_language (const char *file, const char *ext) { static ext_lang_t ext_lang[] = { @@ -533,8 +568,9 @@ separate_compile (void) } dstring_delete (output_file); dstring_delete (extension); - if (!err && !options.compile) { + if (!err && !options.compile && !options.preprocess_only) { InitData (); + chain_initial_types (); linker_begin (); for (file = source_files; *file; file++) { if (strncmp (*file, "-l", 2)) { @@ -589,6 +625,14 @@ load_file (const char *fname) return src; } +/** Parse a cpp line number directive. + + Parses a cpp line directive of the form "# 1 file", setting the line and + file fields of \a script. "#line 1 file" is supported, too. + + \param script the script being parsed + \param filename storage for the parsed filename +*/ static void parse_cpp_line (script_t *script, dstring_t *filename) { @@ -615,10 +659,10 @@ compile_file (const char *filename) int (*yyparse) (void) = qc_yyparse; *yyin = preprocess_file (filename, 0); - if (!*yyin) + if (options.preprocess_only || !*yyin) return !options.preprocess_only; - pr.source_file = ReuseString (strip_path (filename)); + add_source_file (filename); pr.source_line = 1; clear_frame_macros (); err = yyparse () || pr.error_count; @@ -673,6 +717,9 @@ progs_src_compile (void) exit (1); } } + if (options.preprocess_only) { + return 0; + } src = load_file (filename->str); if (!src) { @@ -704,6 +751,7 @@ progs_src_compile (void) setup_sym_file (options.output_file); InitData (); + chain_initial_types (); begin_compilation (); @@ -731,12 +779,15 @@ progs_src_compile (void) fprintf (single, "#include \"%s\"\n", qc_filename->str); if (options.frames_files) fprintf (single, "$frame_write \"%s.frame\"\n", - file_basename (qc_filename->str)); + file_basename (qc_filename->str, 0)); } else { if (compile_file (qc_filename->str)) return 1; - write_frame_macros (va ("%s.frame", - file_basename (qc_filename->str))); + if (options.frames_files) { + write_frame_macros (va (0, "%s.frame", + file_basename (qc_filename->str, + 0))); + } } if (!Script_TokenAvailable (script, 0)) break; @@ -758,6 +809,7 @@ progs_src_compile (void) } class_finish_module (); + debug_finish_module (options.output_file); qfo = qfo_from_progs (&pr); if (options.compile) { qfo_write (qfo, options.output_file); @@ -805,6 +857,7 @@ main (int argc, char **argv) parse_cpp_name (); opcode_init (); + InitData (); init_types (); clear_immediates (); diff --git a/tools/qfcc/source/qfprogs.c b/tools/qfcc/source/qfprogs.c index d6bf84927..3b8a62581 100644 --- a/tools/qfcc/source/qfprogs.c +++ b/tools/qfcc/source/qfprogs.c @@ -56,17 +56,18 @@ #include "QF/cvar.h" #include "QF/hash.h" #include "QF/mathlib.h" -#include "QF/pr_comp.h" #include "QF/progs.h" #include "QF/quakeio.h" #include "QF/sys.h" #include "QF/va.h" #include "QF/zone.h" -#include "obj_file.h" -#include "obj_type.h" -#include "qfprogs.h" -#include "reloc.h" +#include "QF/progs/pr_comp.h" + +#include "tools/qfcc/include/obj_file.h" +#include "tools/qfcc/include/obj_type.h" +#include "tools/qfcc/include/qfprogs.h" +#include "tools/qfcc/include/reloc.h" const char *reloc_names[] = { "none", @@ -125,10 +126,9 @@ static const char *short_options = ; static edict_t *edicts; -static int num_edicts; -static int reserved_edicts = 1; +static pr_uint_t num_edicts; +static pr_uint_t reserved_edicts = 1; static progs_t pr; -static int need_progs; static qfo_t *qfo; static const char *source_path = ""; @@ -176,7 +176,7 @@ file_error (progs_t *pr, const char *name) } static void * -load_file (progs_t *pr, const char *name) +load_file (progs_t *pr, const char *name, off_t *_size) { QFile *file; int size; @@ -184,13 +184,14 @@ load_file (progs_t *pr, const char *name) file = open_file (name, &size); if (!file) { - file = open_file (va ("%s.gz", name), &size); + file = open_file (va (0, "%s.gz", name), &size); if (!file) return 0; } sym = malloc (size + 1); sym[size] = 0; Qread (file, sym, size); + *_size = size; return sym; } @@ -233,12 +234,12 @@ init_qf (void) { Sys_Init (); - Cvar_Get ("pr_debug", va ("%d", verbosity), 0, 0, ""); - Cvar_Get ("pr_source_path", source_path, 0, 0, ""); PR_Init_Cvars (); - PR_Init (); - pr.edicts = &edicts; + pr_debug = 1 + verbosity; + Cvar_Set ("pr_source_path", source_path); + + pr.pr_edicts = &edicts; pr.num_edicts = &num_edicts; pr.reserved_edicts = &reserved_edicts; pr.file_error = file_error; @@ -246,58 +247,18 @@ init_qf (void) pr.allocate_progs_mem = allocate_progs_mem; pr.free_progs_mem = free_progs_mem; - func_tab = Hash_NewTable (1021, 0, 0, 0); + PR_Init (&pr); + + func_tab = Hash_NewTable (1021, 0, 0, 0, 0); Hash_SetHashCompare (func_tab, func_hash, func_compare); } -static void -convert_qfo (void) -{ - int size; - int i; - - pr.progs = qfo_to_progs (qfo, &size); - -#define P(t,o) ((t *)((char *)pr.progs + pr.progs->o)) - pr.pr_statements = P (dstatement_t, ofs_statements); - pr.pr_strings = P (char, ofs_strings); - pr.pr_stringsize = pr.progs->numstrings; - pr.pr_functions = P (dfunction_t, ofs_functions); - pr.pr_globaldefs = P (ddef_t, ofs_globaldefs); - pr.pr_fielddefs = P (ddef_t, ofs_fielddefs); - pr.pr_globals = P (pr_type_t, ofs_globals); - pr.globals_size = pr.progs->numglobals; - pr.pr_edict_size = max (1, pr.progs->entityfields) * 4; - pr.pr_edictareasize = 1 * pr.pr_edict_size; -#undef P - - if (verbosity) { - pr.debug = qfo_to_sym (qfo, &size); -#define P(t,o) ((t *)((char *)pr.debug + pr.debug->o)) - pr.auxfunctions = P (pr_auxfunction_t, auxfunctions); - pr.linenos = P (pr_lineno_t, linenos); - pr.local_defs = P (ddef_t, locals); -#undef P - - pr.local_defs = calloc (qfo->num_defs, sizeof (ddef_t)); - - pr.auxfunction_map = calloc (pr.progs->numfunctions, - sizeof (pr_auxfunction_t *)); - for (i = 0; (int) i < pr.progs->numfunctions; i++) //FIXME (cast) - pr.auxfunction_map[i] = 0; - - for (i = 0; i < (int) pr.debug->num_auxfunctions; i++) { - pr_auxfunction_t *aux = pr.auxfunctions + i; - pr.auxfunction_map[aux->function] = aux; - } - } -} - static int load_progs (const char *name) { QFile *file; - int i, size; + int size; + pr_uint_t i; char buff[5]; Hash_FlushTable (func_tab); @@ -310,6 +271,7 @@ load_progs (const char *name) Qread (file, buff, 4); buff[4] = 0; Qseek (file, 0, SEEK_SET); + qfo = 0; if (!strcmp (buff, QFO)) { qfo = qfo_read (file); Qclose (file); @@ -317,22 +279,22 @@ load_progs (const char *name) if (!qfo) return 0; - if (!need_progs) - return 1; - convert_qfo (); + return 1; } else { pr.progs_name = name; - PR_LoadProgsFile (&pr, file, size, 1, 0); + pr.max_edicts = 1; + pr.zone_size = 0; + PR_LoadProgsFile (&pr, file, size); Qclose (file); if (!pr.progs) return 0; - PR_LoadStrings (&pr); PR_ResolveGlobals (&pr); + PR_LoadStrings (&pr); PR_LoadDebug (&pr); } - for (i = 0; i < pr.progs->numfunctions; i++) { + for (i = 0; i < pr.progs->functions.count; i++) { // don't bother with builtins if (pr.pr_functions[i].first_statement > 0) Hash_AddElement (func_tab, &pr.pr_functions[i]); @@ -348,10 +310,10 @@ typedef struct { operation_t operations[] = { {disassemble_progs, 0}, // disassemble {dump_globals, qfo_globals}, // globals - {dump_strings, 0}, // strings - {dump_fields, 0}, // fields + {dump_strings, qfo_strings}, // strings + {dump_fields, qfo_fields}, // fields {dump_functions, qfo_functions}, // functions - {dump_lines, 0}, // lines + {dump_lines, qfo_lines}, // lines {dump_modules, 0}, // modules {0, qfo_relocs}, // relocs {dump_types, qfo_types}, // types @@ -410,12 +372,11 @@ main (int argc, char **argv) } init_qf (); while (optind < argc) { - need_progs = !func->qfo; if (!load_progs (argv[optind++])) return 1; if (qfo && func->qfo) func->qfo (qfo); - else if (func->progs) + else if (!qfo && func->progs) func->progs (&pr); else fprintf (stderr, "can't process %s\n", argv[optind - 1]); diff --git a/tools/qfcc/source/qp-lex.l b/tools/qfcc/source/qp-lex.l index e98b6511b..7bb6d160b 100644 --- a/tools/qfcc/source/qp-lex.l +++ b/tools/qfcc/source/qp-lex.l @@ -35,17 +35,17 @@ #include "QF/hash.h" -#include "debug.h" -#include "diagnostic.h" -#include "expr.h" -#include "grab.h" -#include "qfcc.h" -#include "shared.h" -#include "strpool.h" -#include "symtab.h" -#include "type.h" +#include "tools/qfcc/include/debug.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/grab.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/shared.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" -#include "qp-parse.h" +#include "tools/qfcc/source/qp-parse.h" #ifndef YY_PROTO # define YY_PROTO(x) x @@ -57,9 +57,16 @@ #define YY_DECL int yylex YY_PROTO(( void )) YY_DECL; +int yyget_lineno (void) __attribute__((pure)); +int yyget_leng (void) __attribute__((pure)); +int yywrap (void) __attribute__((const)); +char *yyget_text (void) __attribute__((pure)); +int yyget_debug (void) __attribute__((pure)); +FILE *yyget_in (void) __attribute__((pure)); +FILE *yyget_out (void) __attribute__((pure)); static int keyword_or_id (const char *token); -static int convert_relop (const char *relop); +static int convert_relop (const char *relop) __attribute__((pure)); %} @@ -106,7 +113,7 @@ FRAMEID {ID}(\.{ID})* i = strtol (yytext + 2, 0, 2); else i = strtol (yytext, 0, 0); - qp_yylval.expr = new_integer_expr (i); + qp_yylval.expr = new_int_expr (i); return VALUE; } @@ -163,7 +170,7 @@ FRAMEID {ID}(\.{ID})* "$"{s}*{FRAMEID} { int ret = do_grab (yytext); if (ret >= 0) { - qp_yylval.expr = new_integer_expr (ret); + qp_yylval.expr = new_int_expr (ret); return VALUE; } else { BEGIN (-ret); @@ -202,7 +209,7 @@ static keyword_t keywords[] = { {"vector", TYPE, &type_vector}, {"entity", TYPE, &type_entity}, {"quaternion", TYPE, &type_quaternion}, - {"integer", TYPE, &type_integer}, + {"integer", TYPE, &type_int}, {"program", PROGRAM, 0}, {"var", VAR, 0}, @@ -241,7 +248,7 @@ keyword_or_id (const char *token) if (!keyword_tab) { size_t i; - keyword_tab = Hash_NewTable (1021, keyword_get_key, 0, 0); + keyword_tab = Hash_NewTable (1021, keyword_get_key, 0, 0, 0); for (i = 0; i < sizeof (keywords) / sizeof (keywords[0]); i++) Hash_Add (keyword_tab, &keywords[i]); } @@ -296,7 +303,9 @@ convert_relop (const char *relop) #ifdef YY_FLEX_REALLOC_HACK static __attribute__ ((used)) void *(*const yy_flex_realloc_hack)(void *,yy_size_t) = yy_flex_realloc; #else +#ifdef yyunput static __attribute__ ((used)) void (*yyunput_hack)(int, char*) = yyunput; +#endif static __attribute__ ((used)) int (*input_hack)(void) = input; #endif diff --git a/tools/qfcc/source/qp-parse.y b/tools/qfcc/source/qp-parse.y index 9731402a5..8a809bacb 100644 --- a/tools/qfcc/source/qp-parse.y +++ b/tools/qfcc/source/qp-parse.y @@ -41,15 +41,15 @@ #include "QF/dstring.h" -#include "codespace.h" -#include "diagnostic.h" -#include "expr.h" -#include "function.h" -#include "qfcc.h" -#include "reloc.h" -#include "shared.h" -#include "symtab.h" -#include "type.h" +#include "tools/qfcc/include/codespace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/shared.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" #define YYDEBUG 1 #define YYERROR_VERBOSE 1 @@ -105,7 +105,7 @@ int yylex (void); %nonassoc STORAGEX %left COMMA -%right '=' ASX PAS /* pointer assign */ +%right '=' ASX %right '?' ':' %left OR %left AND @@ -113,7 +113,8 @@ int yylex (void); %left '^' %left '&' %left EQ NE -%left LE GE LT GT +%left LT GT GE LE +%token NAND NOR XNOR // end of tokens common between qc and qp %left RELOP @@ -139,6 +140,56 @@ int yylex (void); %type sign %{ + +static void +build_dotmain (symbol_t *program) +{ + symbol_t *dotmain = new_symbol (".main"); + expr_t *code; + expr_t *exitcode; + + dotmain->params = 0; + dotmain->type = parse_params (&type_int, 0); + dotmain->type = find_type (dotmain->type); + dotmain = function_symbol (dotmain, 0, 1); + + exitcode = new_symbol_expr (symtab_lookup (current_symtab, "ExitCode")); + + current_func = begin_function (dotmain, 0, current_symtab, 0, + current_storage); + current_symtab = current_func->locals; + code = new_block_expr (); + append_expr (code, function_expr (new_symbol_expr (program), 0)); + append_expr (code, return_expr (current_func, exitcode)); + build_code_function (dotmain, 0, code); +} + +static symbol_t * +function_value (function_t *func) +{ + symbol_t *ret = 0; + if (func->type->t.func.type) { + ret = symtab_lookup (func->locals, ".ret"); + if (!ret || ret->table != func->locals) { + ret = new_symbol_type (".ret", func->type->t.func.type); + initialize_def (ret, 0, func->locals->space, sc_local, + func->locals); + } + } + return ret; +} + +static expr_t * +function_return (function_t *func) +{ + symbol_t *ret = function_value (func); + expr_t *ret_val = 0; + if (ret) { + ret_val = new_symbol_expr (ret); + } + return ret_val; +} + %} %% @@ -154,20 +205,13 @@ program symtab_removesymbol (current_symtab, $1); symtab_addsymbol (current_symtab, $1); - current_func = begin_function ($1, 0, current_symtab, 0); - current_symtab = current_func->symtab; + current_func = begin_function ($1, 0, current_symtab, 0, + current_storage); + current_symtab = current_func->locals; build_code_function ($1, 0, $4); current_symtab = st; - $4 = function_expr (new_symbol_expr ($1), 0); - $1 = new_symbol (".main"); - $1->params = 0; - $1->type = parse_params (&type_void, 0); - $1->type = find_type ($1->type); - $1 = function_symbol ($1, 0, 1); - current_func = begin_function ($1, 0, current_symtab, 0); - current_symtab = current_func->symtab; - build_code_function ($1, 0, $4); + build_dotmain ($1); current_symtab = st; } ; @@ -178,6 +222,16 @@ program_head { $$ = $3; + // FIXME need units and standard units + { + symbol_t *sym = new_symbol ("ExitCode"); + sym->type = &type_int; + initialize_def (sym, 0, current_symtab->space, sc_global, + current_symtab); + if (sym->s.def) { + sym->s.def->nosave = 1; + } + } $$->type = parse_params (&type_void, 0); $$->type = find_type ($$->type); $$ = function_symbol ($$, 0, 1); @@ -207,8 +261,9 @@ declarations { while ($3) { symbol_t *next = $3->next; - initialize_def ($3, $5, 0, current_symtab->space, - current_storage); + $3->type = $5; + initialize_def ($3, 0, current_symtab->space, current_storage, + current_symtab); $3 = next; } } @@ -219,7 +274,7 @@ type : standard_type | ARRAY '[' VALUE RANGE VALUE ']' OF standard_type { - $$ = based_array_type ($8, expr_integer ($3), expr_integer ($5)); + $$ = based_array_type ($8, expr_int ($3), expr_int ($5)); } ; @@ -237,20 +292,22 @@ subprogram_declaration : subprogram_head ';' { $$ = current_storage; - current_func = begin_function ($1, 0, current_symtab, 0); - current_symtab = current_func->symtab; + current_func = begin_function ($1, 0, current_symtab, 0, + current_storage); + current_symtab = current_func->locals; current_storage = sc_local; + function_value (current_func); } declarations compound_statement ';' { - append_expr ($5, new_unary_expr ('r', 0)); + append_expr ($5, new_return_expr (function_return (current_func))); build_code_function ($1, 0, $5); - current_symtab = current_symtab->parent; + current_symtab = current_func->parameters->parent; current_storage = $3; } | subprogram_head ASSIGNOP '#' VALUE ';' { - build_builtin_function ($1, $4, 0); + build_builtin_function ($1, $4, 0, current_storage); } ; @@ -346,8 +403,18 @@ statement : variable ASSIGNOP expression { $$ = $1; - if ($$->type == ex_symbol && extract_type ($$) == ev_func) - $$ = new_ret_expr ($$->e.symbol->type->t.func.type); + if ($$->type == ex_symbol && $$->e.symbol->sy_type == sy_func) { + if ($$->e.symbol->s.func != current_func) { + $$ = error ($$, "cannot assign to other function"); + } else { + symbol_t *ret = function_value (current_func); + if (!ret) { + $$ = error ($$, "cannot assign to procedure"); + } else { + $$ = new_symbol_expr (ret); + } + } + } $$ = assign_expr ($$, $3); } | procedure_statement @@ -368,13 +435,14 @@ statement } | RETURN { - $$ = return_expr (current_func, 0); + $$ = return_expr (current_func, function_return (current_func)); } ; else : ELSE { + // this is only to get the the file and line number info $$ = new_nil_expr (); } ; @@ -452,7 +520,7 @@ name { if (!$1->table) { error (0, "%s undefined", $1->name); - $1->type = &type_integer; + $1->type = &type_int; symtab_addsymbol (current_symtab, $1); } $$ = new_symbol_expr ($1); diff --git a/tools/qfcc/source/reloc.c b/tools/qfcc/source/reloc.c index 29f1663d8..eec081153 100644 --- a/tools/qfcc/source/reloc.c +++ b/tools/qfcc/source/reloc.c @@ -41,19 +41,19 @@ #include "QF/alloc.h" -#include "codespace.h" -#include "def.h" -#include "defspace.h" -#include "diagnostic.h" -#include "emit.h" -#include "expr.h" -#include "function.h" -#include "qfcc.h" -#include "reloc.h" +#include "tools/qfcc/include/codespace.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/emit.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/reloc.h" -static reloc_t *refs_freelist; +ALLOC_STATE (reloc_t, refs); -static const char *reloc_name[] = { +const char * const reloc_name[] = { "rel_none", "rel_op_a_def", "rel_op_b_def", @@ -73,7 +73,7 @@ static const char *reloc_name[] = { "rel_def_field_ofs", }; -#define RELOC(r) (r)->space->data[(r)->offset].integer_var +#define RELOC(r) (r)->space->data[(r)->offset].value void relocate_refs (reloc_t *reloc, int offset) @@ -168,8 +168,8 @@ relocate_refs (reloc_t *reloc, int offset) break; case rel_def_field_ofs: //FIXME what is correct here? - //RELOC (reloc) += pr.data->data[offset].integer_var; - RELOC (reloc) += pr.near_data->data[offset].integer_var; + //RELOC (reloc) += pr.data->data[offset].int_var; + RELOC (reloc) += PR_PTR (int, &pr.near_data->data[offset]); break; } reloc = reloc->next; @@ -210,7 +210,7 @@ reloc_op_def_ofs (def_t *def, int offset, int field) } void -reloc_def_def (def_t *def, def_t *location) +reloc_def_def (def_t *def, const def_t *location) { reloc_t *ref; @@ -221,7 +221,7 @@ reloc_def_def (def_t *def, def_t *location) } void -reloc_def_def_ofs (def_t *def, def_t *location) +reloc_def_def_ofs (def_t *def, const def_t *location) { reloc_t *ref; @@ -232,7 +232,7 @@ reloc_def_def_ofs (def_t *def, def_t *location) } void -reloc_def_func (function_t *func, def_t *location) +reloc_def_func (function_t *func, const def_t *location) { reloc_t *ref; @@ -243,7 +243,7 @@ reloc_def_func (function_t *func, def_t *location) } void -reloc_def_string (def_t *location) +reloc_def_string (const def_t *location) { reloc_t *ref; @@ -254,7 +254,7 @@ reloc_def_string (def_t *location) } void -reloc_def_field (def_t *def, def_t *location) +reloc_def_field (def_t *def, const def_t *location) { reloc_t *ref; @@ -265,7 +265,7 @@ reloc_def_field (def_t *def, def_t *location) } void -reloc_def_field_ofs (def_t *def, def_t *location) +reloc_def_field_ofs (def_t *def, const def_t *location) { reloc_t *ref; @@ -276,7 +276,7 @@ reloc_def_field_ofs (def_t *def, def_t *location) } void -reloc_def_op (ex_label_t *label, def_t *location) +reloc_def_op (const ex_label_t *label, const def_t *location) { reloc_t *ref; diff --git a/tools/qfcc/source/shared.c b/tools/qfcc/source/shared.c index 25e0ac979..acdbf149f 100644 --- a/tools/qfcc/source/shared.c +++ b/tools/qfcc/source/shared.c @@ -31,14 +31,14 @@ # include "config.h" #endif -#include "class.h" -#include "diagnostic.h" -#include "expr.h" -#include "function.h" -#include "options.h" -#include "shared.h" -#include "symtab.h" -#include "type.h" +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/shared.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" function_t *current_func; class_type_t *current_class; @@ -72,7 +72,7 @@ check_undefined (symbol_t *sym) if (options.code.progsversion == PROG_ID_VERSION) sym->type = &type_float; else - sym->type = &type_integer; + sym->type = &type_int; } return sym; } diff --git a/tools/qfcc/source/statements.c b/tools/qfcc/source/statements.c index f331a39f0..774afb2ca 100644 --- a/tools/qfcc/source/statements.c +++ b/tools/qfcc/source/statements.c @@ -38,32 +38,56 @@ #endif #include +#include #include "qfalloca.h" #include "QF/alloc.h" +#include "QF/mathlib.h" #include "QF/va.h" -#include "dags.h" -#include "diagnostic.h" -#include "dot.h" -#include "expr.h" -#include "function.h" -#include "options.h" -#include "qfcc.h" -#include "reloc.h" -#include "statements.h" -#include "strpool.h" -#include "symtab.h" -#include "type.h" -#include "value.h" -#include "qc-parse.h" +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/dags.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/dot.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/method.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/statements.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" -static const char *op_type_names[] = { +#include "tools/qfcc/source/qc-parse.h" + +const char * const op_type_names[] = { "op_def", "op_value", "op_label", "op_temp", + "op_alias", + "op_nil", + "op_pseudo", +}; + +const char * const st_type_names[] = { + "st_none", + "st_expr", + "st_assign", + "st_ptrassign", + "st_move", + "st_ptrmove", + "st_memset", + "st_ptrmemset", + "st_state", + "st_func", + "st_flow", + "st_address", }; const char * @@ -74,6 +98,22 @@ optype_str (op_type_e type) return op_type_names[type]; } +static const char * +tempop_string (operand_t *tmpop) +{ + tempop_t *tempop = &tmpop->tempop; + if (tempop->alias) { + return va (0, "", + pr_type_name[tempop->type->type], + tmpop, tempop->users, + tempop->alias, + tempop->offset, + tempop->alias->tempop.users); + } + return va (0, "", pr_type_name[tempop->type->type], + tmpop, tempop->users); +} + const char * operand_string (operand_t *op) { @@ -81,134 +121,82 @@ operand_string (operand_t *op) return ""; switch (op->op_type) { case op_def: - return op->o.def->name; + return op->def->name; case op_value: - switch (op->o.value->type) { - case ev_string: - return va ("\"%s\"", - quote_string (op->o.value->v.string_val)); - case ev_float: - return va ("%g", op->o.value->v.float_val); - case ev_vector: - return va ("'%g %g %g'", - op->o.value->v.vector_val[0], - op->o.value->v.vector_val[1], - op->o.value->v.vector_val[2]); - case ev_quat: - return va ("'%g %g %g %g'", - op->o.value->v.quaternion_val[0], - op->o.value->v.quaternion_val[1], - op->o.value->v.quaternion_val[2], - op->o.value->v.quaternion_val[3]); - case ev_pointer: - return va ("ptr %d", op->o.value->v.pointer.val); - case ev_field: - return va ("field %d", op->o.value->v.pointer.val); - case ev_entity: - return va ("ent %d", op->o.value->v.integer_val); - case ev_func: - return va ("func %d", op->o.value->v.integer_val); - case ev_integer: - return va ("int %d", op->o.value->v.integer_val); - case ev_uinteger: - return va ("uint %u", op->o.value->v.uinteger_val); - case ev_short: - return va ("short %d", op->o.value->v.short_val); - case ev_void: - return "(void)"; - case ev_invalid: - return "(invalid)"; - case ev_type_count: - return "(type_count)"; - } - break; + return get_value_string (op->value); case op_label: - return op->o.label->name; + return op->label->name; case op_temp: - if (op->o.tempop.alias) - return va ("", - pr_type_name[op->type], - op, op->o.tempop.users, - op->o.tempop.alias, - op->o.tempop.alias->o.tempop.users); - return va ("", pr_type_name[op->o.tempop.type->type], - op, op->o.tempop.users); + return tempop_string (op); case op_alias: { - const char *alias = operand_string (op->o.alias); + const char *alias = operand_string (op->alias); char *buf = alloca (strlen (alias) + 1); strcpy (buf, alias); - return va ("alias(%s,%s)", pr_type_name[op->type], buf); + return va (0, "alias(%s,%s)", pr_type_name[op->type->type], + buf); } + case op_nil: + return "nil"; + case op_pseudo: + return va (0, "pseudo: %s", op->pseudoop->name); } return ("??"); } static void -print_operand (operand_t *op) +_print_operand (operand_t *op) { switch (op->op_type) { case op_def: - printf ("(%s) ", pr_type_name[op->type]); - printf ("%s", op->o.def->name); + printf ("(%s) ", get_type_string (op->type)); + printf ("%s", op->def->name); break; case op_value: - printf ("(%s) ", pr_type_name[op->type]); - switch (op->o.value->type) { - case ev_string: - printf ("\"%s\"", op->o.value->v.string_val); - break; - case ev_float: - printf ("%g", op->o.value->v.float_val); - break; - case ev_vector: - printf ("'%g", op->o.value->v.vector_val[0]); - printf (" %g", op->o.value->v.vector_val[1]); - printf (" %g'", op->o.value->v.vector_val[2]); - break; - case ev_quat: - printf ("'%g", op->o.value->v.quaternion_val[0]); - printf (" %g", op->o.value->v.quaternion_val[1]); - printf (" %g", op->o.value->v.quaternion_val[2]); - printf (" %g'", op->o.value->v.quaternion_val[3]); - break; - case ev_pointer: - printf ("(%s)[%d]", - pr_type_name[op->o.value->v.pointer.type->type], - op->o.value->v.pointer.val); - break; - case ev_field: - printf ("%d", op->o.value->v.pointer.val); - break; - case ev_entity: - case ev_func: - case ev_integer: - printf ("%d", op->o.value->v.integer_val); - break; - case ev_uinteger: - printf ("%u", op->o.value->v.uinteger_val); - break; - case ev_short: - printf ("%d", op->o.value->v.short_val); - break; - case ev_void: - case ev_invalid: - case ev_type_count: - internal_error (0, "weird value type"); - } + printf ("(%s) %s", pr_type_name[op->type->type], + get_value_string (op->value)); break; case op_label: - printf ("block %p", op->o.label->dest); + printf ("block %p", op->label->dest); break; case op_temp: - printf ("tmp (%s) %p", pr_type_name[op->type], op); - if (op->o.tempop.def) - printf (" %s", op->o.tempop.def->name); + printf ("tmp (%s) %p", get_type_string (op->type), op); + if (op->tempop.def) + printf (" %s:%04x", op->tempop.def->name, + op->tempop.def->offset); break; case op_alias: - printf ("alias(%s,", pr_type_name[op->type]); - print_operand (op->o.alias); + printf ("alias(%s,", get_type_string (op->type)); + _print_operand (op->alias); printf (")"); + break; + case op_nil: + printf ("nil"); + break; + case op_pseudo: + printf ("pseudo: %s", op->pseudoop->name); + break; + } +} + +void +print_operand (operand_t *op) +{ + _print_operand (op); + puts (""); +} + +static void +print_operand_chain (const char *name, operand_t *op) +{ + if (op) { + printf (" %s:", name); + while (op) { + printf (" "); + _print_operand (op); + op = op->next; + } + printf ("\n"); } } @@ -217,19 +205,23 @@ print_statement (statement_t *s) { printf ("(%s, ", s->opcode); if (s->opa) - print_operand (s->opa); + _print_operand (s->opa); printf (", "); if (s->opb) - print_operand (s->opb); + _print_operand (s->opb); printf (", "); if (s->opc) - print_operand (s->opc); + _print_operand (s->opc); printf (")\n"); + print_operand_chain ("use", s->use); + print_operand_chain ("def", s->def); + print_operand_chain ("kill", s->kill); } -static sblock_t *sblocks_freelist; -static statement_t *statements_freelist; -static operand_t *operands_freelist; +ALLOC_STATE (pseudoop_t, pseudoops); +ALLOC_STATE (sblock_t, sblocks); +ALLOC_STATE (statement_t, statements); +ALLOC_STATE (operand_t, operands); sblock_t * new_sblock (void) @@ -257,18 +249,70 @@ new_statement (st_type_t type, const char *opcode, expr_t *expr) statement->type = type; statement->opcode = save_string (opcode); statement->expr = expr; + statement->number = -1; // indicates flow analysis not done yet return statement; } +static void +statement_add_use (statement_t *s, operand_t *use) +{ + if (use) { + use->next = s->use; + s->use = use; + } +} + +static void +statement_add_def (statement_t *s, operand_t *def) +{ + if (def) { + def->next = s->def; + s->def = def; + } +} + +static void +statement_add_kill (statement_t *s, operand_t *kill) +{ + if (kill) { + kill->next = s->kill; + s->kill = kill; + } +} + +static pseudoop_t * +new_pseudoop (const char *name) +{ + pseudoop_t *pseudoop; + ALLOC (256, pseudoop_t, pseudoops, pseudoop); + pseudoop->name = save_string (name); + return pseudoop; + +} + static operand_t * -new_operand (op_type_e op) +new_operand (op_type_e op, expr_t *expr, void *return_addr) { operand_t *operand; ALLOC (256, operand_t, operands, operand); operand->op_type = op; + operand->expr = expr; + operand->return_addr = return_addr; return operand; } +static operand_t * +copy_operand (operand_t *src) +{ + if (!src) { + return 0; + } + operand_t *cpy = new_operand (src->op_type, src->expr, 0); + *cpy = *src; + cpy->return_addr = __builtin_return_address (0); + return cpy; +} + void free_operand (operand_t *op) { @@ -298,96 +342,205 @@ free_sblock (sblock_t *sblock) FREE (sblocks, sblock); } +static operand_t * +pseudo_operand (pseudoop_t *pseudoop, expr_t *expr) +{ + operand_t *op; + op = new_operand (op_pseudo, expr, __builtin_return_address (0)); + op->pseudoop = pseudoop; + op->size = 1; + return op; +} + operand_t * -def_operand (def_t *def, type_t *type) +nil_operand (type_t *type, expr_t *expr) +{ + operand_t *op; + op = new_operand (op_nil, expr, __builtin_return_address (0)); + op->type = type; + op->size = type_size (type); + op->width = type_width (type); + return op; +} + +operand_t * +def_operand (def_t *def, type_t *type, expr_t *expr) { operand_t *op; if (!type) type = def->type; - op = new_operand (op_def); - op->type = low_level_type (type); - op->o.def = def; + op = new_operand (op_def, expr, __builtin_return_address (0)); + op->type = type; + op->size = type_size (type); + op->width = type_width (type); + op->def = def; return op; } operand_t * -value_operand (ex_value_t *value) +return_operand (type_t *type, expr_t *expr) +{ + symbol_t *return_symbol; + return_symbol = make_symbol (".return", &type_param, pr.symtab->space, + sc_extern); + if (!return_symbol->table) { + symtab_addsymbol (pr.symtab, return_symbol); + } + def_t *return_def = return_symbol->s.def; + return def_operand (alias_def (return_def, type, 0), 0, expr); +} + +operand_t * +value_operand (ex_value_t *value, expr_t *expr) { operand_t *op; - op = new_operand (op_value); + op = new_operand (op_value, expr, __builtin_return_address (0)); op->type = value->type; - op->o.value = value; + op->size = type_size (value->type); + op->width = type_width (value->type); + op->value = value; return op; } operand_t * -temp_operand (type_t *type) +temp_operand (type_t *type, expr_t *expr) { - operand_t *op = new_operand (op_temp); + operand_t *op = new_operand (op_temp, expr, __builtin_return_address (0)); - op->o.tempop.type = type; - op->type = low_level_type (type); + op->tempop.type = type; + op->type = type; op->size = type_size (type); + op->width = type_width (type); return op; } +int +tempop_overlap (tempop_t *t1, tempop_t *t2) +{ + int offs1 = t1->offset; + int offs2 = t2->offset; + int size1 = type_size (t1->type); + int size2 = type_size (t2->type); + + if (t1->alias) { + offs1 += t1->alias->tempop.offset; + } + if (t2->alias) { + offs2 += t2->alias->tempop.offset; + } + if (offs1 <= offs2 && offs1 + size1 >= offs2 + size2) + return 2; // t1 fully overlaps t2 + if (offs1 < offs2 + size2 && offs2 < offs1 + size1) + return 1; // t1 and t2 at least partially overlap + return 0; +} + +int +tempop_visit_all (tempop_t *tempop, int overlap, + int (*visit) (tempop_t *, void *), void *data) +{ + tempop_t *start_tempop = tempop; + operand_t *top; + int ret; + + if ((ret = visit (tempop, data))) + return ret; + if (tempop->alias) { + top = tempop->alias; + if (top->op_type != op_temp) { + internal_error (top->expr, "temp alias of non-temp operand"); + } + tempop = &top->tempop; + if (!(overlap & 4) && (ret = visit (tempop, data))) + return ret; + overlap &= ~4; + } else { + overlap = 0; + } + for (top = tempop->alias_ops; top; top = top->next) { + if (top->op_type != op_temp) { + internal_error (top->expr, "temp alias of non-temp operand"); + } + tempop = &top->tempop; + if (tempop == start_tempop) + continue; + if (overlap && tempop_overlap (tempop, start_tempop) < overlap) + continue; + if ((ret = visit (tempop, data))) + return ret; + } + return 0; +} + operand_t * -alias_operand (etype_t type, operand_t *op) +alias_operand (type_t *type, operand_t *op, expr_t *expr) { operand_t *aop; - if (pr_type_size[type] != pr_type_size[op->type]) - internal_error (0, "aliasing operand with type of diffent size"); - aop = new_operand (op_alias); - aop->o.alias = op; + if (type_size (type) != type_size (op->type)) { + internal_error (op->expr, + "aliasing operand with type of different size: %d, %d", + type_size (type), type_size (op->type)); + } + aop = new_operand (op_alias, expr, __builtin_return_address (0)); + aop->alias = op; aop->type = type; - aop->size = pr_type_size[type]; + aop->size = type_size (type); + aop->width = type_width (type); return aop; } +operand_t * +label_operand (expr_t *label) +{ + operand_t *lop; + + if (label->type != ex_label) { + internal_error (label, "not a label expression"); + } + lop = new_operand (op_label, label, __builtin_return_address (0)); + lop->label = &label->e.label; + return lop; +} + static operand_t * -short_operand (short short_val) +short_operand (short short_val, expr_t *expr) { ex_value_t *val = new_short_val (short_val); - return value_operand (val); + return value_operand (val, expr); } static const char * convert_op (int op) { switch (op) { - case PAS: return ".="; - case OR: return "||"; - case AND: return "&&"; - case EQ: return "=="; - case NE: return "!="; - case LE: return "<="; - case GE: return ">="; - case LT: return "<"; - case GT: return ">"; - case '=': return "="; - case '+': return "+"; - case '-': return "-"; - case '*': return "*"; - case '/': return "/"; - case '%': return "%"; - case '&': return "&"; - case '|': return "|"; - case '^': return "^"; - case '~': return "~"; - case '!': return "!"; - case SHL: return "<<"; - case SHR: return ">>"; - case '.': return "."; - case 'i': return ""; - case 'n': return ""; - case IFBE: return ""; - case IFB: return ""; - case IFAE: return ""; - case IFA: return ""; - case 'm': return ""; - case 'M': return ""; + case OR: return "or"; + case AND: return "and"; + case EQ: return "eq"; + case NE: return "ne"; + case LE: return "le"; + case GE: return "ge"; + case LT: return "lt"; + case GT: return "gt"; + case '+': return "add"; + case '-': return "sub"; + case '*': return "mul"; + case '/': return "div"; + case '%': return "rem"; + case MOD: return "mod"; + case '&': return "bitand"; + case '|': return "bitor"; + case '^': return "bitxor"; + case '~': return "bitnot"; + case '!': return "not"; + case SHL: return "shl"; + case SHR: return "shr"; + case '.': return "load"; + case CROSS: return "cross"; + case DOT: return "dot"; + case HADAMARD: return "mul"; + case SCALE: return "scale"; default: return 0; } @@ -398,7 +551,7 @@ statement_is_cond (statement_t *s) { if (!s) return 0; - return !strncmp (s->opcode, "opcode, "if", 2); } int @@ -406,7 +559,7 @@ statement_is_goto (statement_t *s) { if (!s) return 0; - return !strcmp (s->opcode, ""); + return !strcmp (s->opcode, "jump") && !s->opb; } int @@ -414,7 +567,7 @@ statement_is_jumpb (statement_t *s) { if (!s) return 0; - return !strcmp (s->opcode, ""); + return !strcmp (s->opcode, "jump") && s->opb; } int @@ -422,9 +575,9 @@ statement_is_call (statement_t *s) { if (!s) return 0; - if (!strncmp (s->opcode, "opcode, "call", 4)) return 1; - if (!strncmp (s->opcode, "opcode, "rcall", 5)) return 2; return 0; } @@ -434,17 +587,25 @@ statement_is_return (statement_t *s) { if (!s) return 0; - return !strncmp (s->opcode, "opcode, "return", 6); +} + +static ex_label_t ** +statement_get_labelref (statement_t *s) +{ + if (statement_is_cond (s) + || statement_is_goto (s) + || statement_is_jumpb (s)) { + return &s->opa->label; + } + return 0; } sblock_t * statement_get_target (statement_t *s) { - if (statement_is_cond (s)) - return s->opb->o.label->dest; - if (statement_is_goto (s)) - return s->opa->o.label->dest; - return 0; + ex_label_t **label = statement_get_labelref (s); + return label ? (*label)->dest : 0; } sblock_t ** @@ -453,14 +614,14 @@ statement_get_targetlist (statement_t *s) sblock_t **target_list; int count = 0, i; def_t *table = 0; - expr_t *e; + element_t *e; if (statement_is_cond (s)) { count = 1; } else if (statement_is_goto (s)) { count = 1; } else if (statement_is_jumpb (s)) { - table = s->opa->o.def; + table = s->opa->def; count = table->type->t.array.size; } target_list = malloc ((count + 1) * sizeof (sblock_t *)); @@ -471,10 +632,10 @@ statement_get_targetlist (statement_t *s) target_list[0] = statement_get_target (s); } else if (statement_is_jumpb (s)) { if (table->alias) - internal_error (0, "aliased jump table"); - e = table->initializer->e.block.head; //FIXME check!!! + internal_error (s->opa->expr, "aliased jump table"); + e = table->initializer->e.compound.head; //FIXME check!!! for (i = 0; i < count; e = e->next, i++) - target_list[i] = e->e.labelref.label->dest; + target_list[i] = e->expr->e.labelref.label->dest; } return target_list; } @@ -482,18 +643,18 @@ statement_get_targetlist (statement_t *s) static void invert_conditional (statement_t *s) { - if (!strcmp (s->opcode, "")) - s->opcode = ""; - else if (!strcmp (s->opcode, "")) - s->opcode = ""; - else if (!strcmp (s->opcode, "")) - s->opcode = ""; - else if (!strcmp (s->opcode, "")) - s->opcode = ""; - else if (!strcmp (s->opcode, "")) - s->opcode = ""; - else if (!strcmp (s->opcode, "")) - s->opcode = ""; + if (!strcmp (s->opcode, "ifnz")) + s->opcode = "ifz"; + else if (!strcmp (s->opcode, "ifz")) + s->opcode = "ifnz"; + else if (!strcmp (s->opcode, "ifbe")) + s->opcode = "ifa"; + else if (!strcmp (s->opcode, "ifb")) + s->opcode = "ifae"; + else if (!strcmp (s->opcode, "ifae")) + s->opcode = "ifb"; + else if (!strcmp (s->opcode, "ifa")) + s->opcode = "ifbe"; } typedef sblock_t *(*statement_f) (sblock_t *, expr_t *); @@ -502,116 +663,323 @@ typedef sblock_t *(*expr_f) (sblock_t *, expr_t *, operand_t **); static sblock_t *statement_subexpr (sblock_t *sblock, expr_t *e, operand_t **op); static sblock_t *statement_slist (sblock_t *sblock, expr_t *e); +static sblock_t *expr_symbol (sblock_t *sblock, expr_t *e, operand_t **op); +static sblock_t *expr_def (sblock_t *sblock, expr_t *e, operand_t **op); static sblock_t * -statement_branch (sblock_t *sblock, expr_t *e) +expr_address (sblock_t *sblock, expr_t *e, operand_t **op) { - statement_t *s = 0; - const char *opcode; + statement_t *s; + expr_t *lvalue = e->e.address.lvalue; + expr_t *offset = e->e.address.offset; - if (e->type == ex_uexpr && e->e.expr.op == 'g') { - s = new_statement (st_flow, "", e); - s->opa = new_operand (op_label); - s->opa->o.label = &e->e.expr.e1->e.label; - } else { - if (e->e.expr.op == 'g') { - s = new_statement (st_flow, "", e); - sblock = statement_subexpr (sblock, e->e.expr.e1, &s->opa); - sblock = statement_subexpr (sblock, e->e.expr.e2, &s->opb); - } else { - opcode = convert_op (e->e.expr.op); - s = new_statement (st_flow, opcode, e); - sblock = statement_subexpr (sblock, e->e.expr.e1, &s->opa); - s->opb = new_operand (op_label); - s->opb->o.label = &e->e.expr.e2->e.label; + if (lvalue->type == ex_alias && offset && is_constant (offset)) { + lvalue = new_offset_alias_expr (lvalue->e.alias.type, + lvalue->e.alias.expr, + expr_int (offset)); + offset = 0; + } else if (offset && is_constant (offset)) { + int o = expr_int (offset); + if (o < 32768 && o >= -32768) { + offset = expr_file_line (new_short_expr (o), offset); } } + s = new_statement (st_address, "lea", e); + sblock = statement_subexpr (sblock, lvalue, &s->opa); + if (offset) { + sblock = statement_subexpr (sblock, offset, &s->opb); + } + s->opc = temp_operand (e->e.address.type, e); sblock_add_statement (sblock, s); - sblock->next = new_sblock (); - return sblock->next; + *(op) = s->opc; + return sblock; +} + +static operand_t * +operand_address (operand_t *reference, expr_t *e) +{ + def_t *def; + type_t *type; + int offset = 0; + + type = reference->type; + switch (reference->op_type) { + case op_def: + // assumes aliasing is only one level deep which should be the + // case + def = reference->def; + if (def->alias) { + offset = def->offset; + def = def->alias; + } + return value_operand (new_pointer_val (offset, type, def, 0), e); + case op_temp: + // assumes aliasing is only one level deep which should be the + // case + if (reference->tempop.alias) { + offset = reference->tempop.offset; + reference = reference->tempop.alias; + } + return value_operand (new_pointer_val (offset, type, 0, + reference), e); + case op_alias: + //op_alias comes only from alias_operand and that is called + // by dags, so not expected + case op_value: + case op_label: + case op_nil: + case op_pseudo: + break; + } + internal_error (e, "invalid operand type for operand address: %s", + op_type_names[reference->op_type]); +} + +static __attribute__((pure)) int +is_indirect (expr_t *e) +{ + if ((e->type == ex_expr || e->type == ex_uexpr) + && e->e.expr.op == '.') { + return 1; + } + return 0; +} + +static sblock_t *addressing_mode (sblock_t *sblock, expr_t *ref, + operand_t **base, operand_t **offset, + pr_ushort_t *mode, operand_t **target); +static statement_t *lea_statement (operand_t *pointer, operand_t *offset, + expr_t *e); + +static statement_t * +assign_statement (operand_t *dst, operand_t *src, expr_t *e) +{ + statement_t *s = new_statement (st_assign, "assign", e); + s->opa = dst; + s->opc = src; + return s; +} + +static sblock_t * +expr_assign_copy (sblock_t *sblock, expr_t *e, operand_t **op, operand_t *src) +{ + statement_t *s; + expr_t *dst_expr = e->e.assign.dst; + expr_t *src_expr = e->e.assign.src; + type_t *dst_type = get_type (dst_expr); + type_t *src_type = get_type (src_expr); + unsigned count; + expr_t *count_expr; + operand_t *dst = 0; + operand_t *size = 0; + static const char *opcode_sets[][2] = { + {"move", "movep"}, + {"memset", "memsetp"}, + }; + const unsigned max_count = 1 << 16; + const char **opcode_set = opcode_sets[0]; + const char *opcode; + int need_ptr = 0; + st_type_t type = st_move; + operand_t *use = 0; + operand_t *def = 0; + operand_t *kill = 0; + + if ((src && src->op_type == op_nil) || src_expr->type == ex_nil) { + // switch to memset because nil is type agnostic 0 and structures + // can be any size + src_expr = new_int_expr (0); + sblock = statement_subexpr (sblock, src_expr, &src); + opcode_set = opcode_sets[1]; + if (op) { + *op = nil_operand (dst_type, src_expr); + } + type = st_memset; + if (is_indirect (dst_expr)) { + need_ptr = 1; + } + } else { + if (is_indirect (src_expr)) { + src_expr = expr_file_line (address_expr (src_expr, 0), e); + need_ptr = 1; + } + if (!src) { + // This is the very right-hand node of a non-nil assignment chain + // (there may be more chains somewhere within src_expr, but they + // are not part of this chain as they are separated by another + // expression). + sblock = statement_subexpr (sblock, src_expr, &src); + } + // send the source operand back up through the assignment chain + // before modifying src if its address is needed + if (op) { + *op = src; + } + if (is_indirect (dst_expr)) { + if (is_variable (src_expr)) { + // FIXME this probably needs to be more agressive + // shouldn't emit code... + sblock = statement_subexpr (sblock, src_expr, &use); + } + if (options.code.progsversion == PROG_VERSION) { + // FIXME it was probably a mistake extracting the operand + // type from the statement expression in dags. Also, can't + // use address_expr() because src_expr may be a function call + // and unary_expr doesn't like that + src_expr = expr_file_line ( + new_address_expr (get_type (src_expr), src_expr, 0), + src_expr); + s = lea_statement (src, 0, src_expr); + sblock_add_statement (sblock, s); + src = s->opc; + } else { + src = operand_address (src, src_expr); + } + need_ptr = 1; + } + } + if (need_ptr) { + // dst_expr and/or src_expr are dereferenced pointers, so need to + // un-dereference dst_expr to get the pointer and switch to movep + // or memsetp instructions. + if (is_variable (dst_expr)) { + // FIXME this probably needs to be more agressive + // shouldn't emit code... + sblock = statement_subexpr (sblock, dst_expr, &def); + //FIXME is this even necessary? if it is, should use copy_operand + sblock = statement_subexpr (sblock, dst_expr, &kill); + } + dst_expr = expr_file_line (address_expr (dst_expr, 0), e); + need_ptr = 1; + } + sblock = statement_subexpr (sblock, dst_expr, &dst); + + if (type_size (dst_type) != type_size (src_type)) { + bug (e, "dst and src sizes differ in expr_assign_copy: %d %d", + type_size (dst_type), type_size (src_type)); + } + count = min (type_size (dst_type), type_size (src_type)); + if (count < (1 << 16)) { + count_expr = expr_file_line (new_short_expr (count), e); + } else { + count_expr = expr_file_line (new_int_expr (count), e); + } + sblock = statement_subexpr (sblock, count_expr, &size); + + if (count < max_count && !need_ptr) { + opcode = opcode_set[0]; + } else { + opcode = opcode_set[1]; + type++; // from st_move/st_memset to st_ptrmove/st_ptrmemset + } + + s = new_statement (type, opcode, e); + s->opa = src; + s->opb = size; + s->opc = dst; + s->use = use; + s->def = def; + s->kill = kill; + sblock_add_statement (sblock, s); + return sblock; } static sblock_t * expr_assign (sblock_t *sblock, expr_t *e, operand_t **op) { statement_t *s; - expr_t *src_expr = e->e.expr.e2; - expr_t *dst_expr = e->e.expr.e1; + expr_t *src_expr = e->e.assign.src; + expr_t *dst_expr = e->e.assign.dst; + type_t *dst_type = get_type (dst_expr); operand_t *src = 0; operand_t *dst = 0; operand_t *ofs = 0; - const char *opcode = convert_op (e->e.expr.op); - st_type_t type; + operand_t *target = 0; + pr_ushort_t mode = 0; // assign + const char *opcode = "assign"; + st_type_t type = st_assign; - if (e->e.expr.op == '=') { - sblock = statement_subexpr (sblock, dst_expr, &dst); - src = dst; + if (src_expr->type == ex_assign) { sblock = statement_subexpr (sblock, src_expr, &src); - ofs = 0; - if (op) - *op = dst; - if (src == dst) - return sblock; - type = st_assign; - } else { - //FIXME this sucks. find a better way to handle both pointer - //dereferences and pointer assignements - sblock = statement_subexpr (sblock, src_expr, &src); - if (dst_expr->type == ex_expr - && extract_type (dst_expr->e.expr.e1) == ev_pointer - && !is_constant (dst_expr->e.expr.e1)) { - sblock = statement_subexpr (sblock, dst_expr->e.expr.e1, &dst); - sblock = statement_subexpr (sblock, dst_expr->e.expr.e2, &ofs); + if (is_structural (dst_type)) { + return expr_assign_copy (sblock, e, op, src); + } + if (is_indirect (dst_expr)) { + goto dereference_dst; } else { - if (dst_expr->type == ex_uexpr - && dst_expr->e.expr.op == '&') { - opcode = "="; - dst_expr = unary_expr ('.', dst_expr); - } sblock = statement_subexpr (sblock, dst_expr, &dst); + } + } else { + if (is_structural (dst_type)) { + return expr_assign_copy (sblock, e, op, src); + } + + if (is_indirect (dst_expr)) { + // If both dst_expr and src_expr are indirect, then a staging temp + // is needed, but emitting src_expr first generates that temp + // because src is null. If src_expr is not indirect and is a simple + // variable reference, then just the ref will be generated and thus + // will be assigned to the dereferenced destination. If src_expr + // is not simple, then a temp will be generated, so all good. + sblock = statement_subexpr (sblock, src_expr, &src); + goto dereference_dst; + } else { + // dst_expr is direct and known to be an l-value, so emitting + // its expression will simply generate a reference to that l-value + // which will be used as the default location to store src_expr's + // result + sblock = statement_subexpr (sblock, dst_expr, &dst); + src = dst; + sblock = statement_subexpr (sblock, src_expr, &src); + } + } + + if (0) { +dereference_dst: + // dst_expr is a dereferenced pointer, so need to get its addressing + // parameters (base and offset) and switch to store instructions. + sblock = addressing_mode (sblock, dst_expr, &dst, &ofs, &mode, &target); + if (mode != 0) { + opcode = "store"; + type = st_ptrassign; + } else { ofs = 0; } - if (op) - *op = src; - type = st_ptrassign; + } + if (op) { + *op = src; + } + if (src == dst) { + return sblock; + } + + if (options.code.progsversion < PROG_VERSION + && is_entity (dst->type) && ofs && is_field (ofs->type)) { + // need to get a pointer type, entity.field expressions do not provide + // one directly. FIXME it was probably a mistake extracting the operand + // type from the statement expression in dags + dst_expr = expr_file_line (address_expr (dst_expr, 0), dst_expr); + s = new_statement (st_address, "lea", dst_expr); + s->opa = dst; + s->opb = ofs; + s->opc = temp_operand (&type_ptr, dst_expr); + sblock_add_statement (sblock, s); + dst = s->opc; + ofs = 0; } s = new_statement (type, opcode, e); - s->opa = src; - s->opb = dst; - s->opc = ofs; + s->opa = dst; + s->opb = ofs; + s->opc = src; + if (type != st_ptrassign) { + statement_add_def (s, copy_operand (target)); + statement_add_kill (s, target); + } sblock_add_statement (sblock, s); return sblock; } -static sblock_t * -expr_move (sblock_t *sblock, expr_t *e, operand_t **op) -{ - statement_t *s; - type_t *type = e->e.expr.type; - expr_t *dst_expr = e->e.expr.e1; - expr_t *src_expr = e->e.expr.e2; - expr_t *size_expr; - operand_t *dst = 0; - operand_t *src = 0; - operand_t *size = 0; - - if (!op) - op = &dst; - size_expr = new_short_expr (type_size (type)); - sblock = statement_subexpr (sblock, dst_expr, op); - dst = *op; - sblock = statement_subexpr (sblock, src_expr, &src); - sblock = statement_subexpr (sblock, size_expr, &size); - s = new_statement (st_move, convert_op (e->e.expr.op), e); - s->opa = src; - s->opb = size; - s->opc = dst; - sblock_add_statement (sblock, s); - - return sblock; -} - static sblock_t * vector_call (sblock_t *sblock, expr_t *earg, expr_t *param, int ind, operand_t **op) @@ -634,12 +1002,11 @@ vector_call (sblock_t *sblock, expr_t *earg, expr_t *param, int ind, return sblock; } - static sblock_t * -expr_call (sblock_t *sblock, expr_t *call, operand_t **op) +expr_call_v6p (sblock_t *sblock, expr_t *call, operand_t **op) { - expr_t *func = call->e.expr.e1; - expr_t *args = call->e.expr.e2; + expr_t *func = call->e.branch.target; + expr_t *args = call->e.branch.args; expr_t *a; expr_t *param; operand_t *arguments[2] = {0, 0}; @@ -648,149 +1015,558 @@ expr_call (sblock_t *sblock, expr_t *call, operand_t **op) const char *opcode; const char *pref = ""; statement_t *s; + operand_t *use = 0; - for (a = args; a; a = a->next) + // function arguments are in reverse order + for (a = args; a; a = a->next) { + if (a->type == ex_args) { + // v6p uses callN and pr_argc + continue; + } count++; + } ind = count; for (a = args; a; a = a->next) { + if (a->type == ex_args) { + // v6p uses callN and pr_argc + continue; + } ind--; param = new_param_expr (get_type (a), ind); if (count && options.code.progsversion != PROG_ID_VERSION && ind < 2) { - pref = "R"; + pref = "r"; sblock = statement_subexpr (sblock, param, &arguments[ind]); if (options.code.vector_calls && a->type == ex_value - && a->e.value->type == ev_vector) + && a->e.value->lltype == ev_vector) sblock = vector_call (sblock, a, param, ind, &arguments[ind]); else sblock = statement_subexpr (sblock, a, &arguments[ind]); + operand_t *p; + sblock = statement_subexpr (sblock, param, &p); + p->next = use; + use = p; continue; } - if (is_struct (get_type (param))) { - //FIXME this should be done in the expression tree + if (is_struct (get_type (param)) || is_union (get_type (param))) { expr_t *mov = assign_expr (param, a); mov->line = a->line; mov->file = a->file; sblock = statement_slist (sblock, mov); } else { + operand_t *p = 0; + sblock = statement_subexpr (sblock, param, &p); if (options.code.vector_calls && a->type == ex_value - && a->e.value->type == ev_vector) { + && a->e.value->lltype == ev_vector) { sblock = vector_call (sblock, a, param, ind, 0); } else { - operand_t *p = 0; - operand_t *arg; - sblock = statement_subexpr (sblock, param, &p); + operand_t *arg = p; arg = p; sblock = statement_subexpr (sblock, a, &arg); if (arg != p) { - s = new_statement (st_assign, "=", a); - s->opa = arg; - s->opb = p; + s = assign_statement (p, arg, a); sblock_add_statement (sblock, s); } } + p->next = use; + use = p; } } - opcode = va ("<%sCALL%d>", pref, count); + opcode = va (0, "%scall%d", pref, count); s = new_statement (st_func, opcode, call); sblock = statement_subexpr (sblock, func, &s->opa); s->opb = arguments[0]; s->opc = arguments[1]; + s->use = use; + if (op) { + *op = return_operand (call->e.branch.ret_type, call); + } sblock_add_statement (sblock, s); sblock->next = new_sblock (); return sblock->next; } static sblock_t * -expr_address (sblock_t *sblock, expr_t *e, operand_t **op) +expr_call (sblock_t *sblock, expr_t *call, operand_t **op) { - if (e->type == ex_uexpr) { - sblock = statement_subexpr (sblock, e->e.expr.e1, op); - (*op)->type = ev_pointer; + if (options.code.progsversion < PROG_VERSION) { + return expr_call_v6p (sblock, call, op); } + if (!current_func->arguments) { + current_func->arguments = defspace_new (ds_virtual); + } + defspace_t *arg_space = current_func->arguments; + expr_t *func = call->e.branch.target; + expr_t **args = 0; + expr_t *args_va_list = 0; // .args (...) parameter + expr_t *args_params = 0; // first arg in ... + operand_t *use = 0; + operand_t *kill = 0; + int num_params = 0; + + defspace_reset (arg_space); + + int num_args = 0; + for (expr_t *a = call->e.branch.args; a; a = a->next) { + num_args++; + } + if (num_args) { + int i = num_args; + args = alloca (num_args * sizeof (expr_t *)); + for (expr_t *a = call->e.branch.args; a; a = a->next) { + args[--i] = a; + } + } + int arg_num = 0; + for (int i = 0; i < num_args; i++) { + expr_t *a = args[i]; + const char *arg_name = va (0, ".arg%d", arg_num++); + def_t *def = new_def (arg_name, 0, current_func->arguments, + sc_argument); + type_t *arg_type = get_type (a); + int size = type_size (arg_type); + int alignment = arg_type->alignment; + if (alignment < 4) { + alignment = 4; + } + def->offset = defspace_alloc_aligned_highwater (arg_space, size, + alignment); + def->type = arg_type; + def->reg = current_func->temp_reg; + expr_t *def_expr = expr_file_line (new_def_expr (def), call); + if (a->type == ex_args) { + args_va_list = def_expr; + } else { + if (args_va_list && !args_params) { + args_params = def_expr; + } + if (args_va_list) { + num_params++; + } + expr_t *assign = assign_expr (def_expr, a); + expr_file_line (assign, call); + sblock = statement_slist (sblock, assign); + } + + // The call both uses and kills the arguments: use is obvious, but kill + // is because the callee has direct access to them and might modify + // them + // need two ops for the one def because there's two lists + operand_t *u = def_operand (def, arg_type, call); + operand_t *k = def_operand (def, arg_type, call); + u->next = use; + use = u; + k->next = kill; + kill = k; + } + if (args_va_list) { + expr_t *assign; + expr_t *count; + expr_t *list; + expr_t *args_count = field_expr (args_va_list, + new_name_expr ("count")); + expr_t *args_list = field_expr (args_va_list, + new_name_expr ("list")); + expr_file_line (args_count, call); + expr_file_line (args_list, call); + + count = new_short_expr (num_params); + assign = assign_expr (args_count, count); + expr_file_line (assign, call); + sblock = statement_slist (sblock, assign); + + if (args_params) { + list = address_expr (args_params, &type_param); + } else { + list = new_nil_expr (); + } + expr_file_line (list, call); + assign = assign_expr (args_list, list); + expr_file_line (assign, call); + sblock = statement_slist (sblock, assign); + } + statement_t *s = new_statement (st_func, "call", call); + sblock = statement_subexpr (sblock, func, &s->opa); + if (!op) { + s->opc = short_operand (0, call); + } else { + if (!*op) { + *op = temp_operand (call->e.branch.ret_type, call); + } + s->opc = *op; + } + s->use = use; + s->kill = kill; + sblock_add_statement (sblock, s); + sblock->next = new_sblock (); + return sblock->next; +} + +static sblock_t * +expr_branch (sblock_t *sblock, expr_t *e, operand_t **op) +{ + if (e->e.branch.type != pr_branch_call) { + internal_error (e, "unexpected branch type in expression: %d", + e->e.branch.type); + } + return expr_call (sblock, e, op); +} + +static sblock_t * +statement_branch (sblock_t *sblock, expr_t *e) +{ + static const char *opcodes[] = { + "ifz", + "ifb", + "ifa", + 0, // special handling + "ifnz", + "ifae", + "ifbe", + 0, // not used here + }; + statement_t *s = 0; + const char *opcode; + + if (e->e.branch.type == pr_branch_call) { + return expr_call (sblock, e, 0); + } + if (e->e.branch.type == pr_branch_jump) { + if (e->e.branch.index) { + s = new_statement (st_flow, "jump", e); + sblock = statement_subexpr (sblock, e->e.branch.target, &s->opa); + sblock = statement_subexpr (sblock, e->e.branch.index, &s->opb); + } else { + s = new_statement (st_flow, "jump", e); + s->opa = label_operand (e->e.branch.target); + } + } else { + opcode = opcodes [e->e.branch.type]; + s = new_statement (st_flow, opcode, e); + sblock = statement_subexpr (sblock, e->e.branch.test, &s->opc); + s->opa = label_operand (e->e.branch.target); + } + + sblock_add_statement (sblock, s); + sblock->next = new_sblock (); + return sblock->next; +} + +static operand_t * +find_def_operand (expr_t *ref) +{ + operand_t *op = 0; + if (ref->type == ex_alias) { + return find_def_operand (ref->e.alias.expr); + } else if (ref->type == ex_address) { + return find_def_operand (ref->e.address.lvalue); + } else if (ref->type == ex_symbol) { + expr_symbol (0, ref, &op); + } else if (ref->type == ex_def) { + expr_def (0, ref, &op); + } else if (ref->type == ex_expr) { + debug (ref, "unexpected expr type: %s", expr_names[ref->type]); + } else { + internal_error (ref, "unexpected expr type: %s", + expr_names[ref->type]); + } + return op; +} + +static sblock_t * +ptr_addressing_mode (sblock_t *sblock, expr_t *ref, + operand_t **base, operand_t **offset, pr_ushort_t *mode, + operand_t **target) +{ + type_t *type = get_type (ref); + if (!is_ptr (type)) { + internal_error (ref, "expected pointer in ref"); + } + if (ref->type == ex_address + && (!ref->e.address.offset || is_constant (ref->e.address.offset)) + && ref->e.address.lvalue->type == ex_alias + && (!ref->e.address.lvalue->e.alias.offset + || is_constant (ref->e.address.lvalue->e.alias.offset))) { + expr_t *lvalue = ref->e.address.lvalue; + expr_t *offs = ref->e.address.offset; + expr_t *alias; + if (lvalue->e.alias.offset) { + if (offs) { + offs = binary_expr ('+', offs, lvalue->e.alias.offset); + } else { + offs = lvalue->e.alias.offset; + } + } + type = type->t.fldptr.type; + if (offs) { + expr_t *lv = lvalue->e.alias.expr; + type_t *lvtype = get_type (lv); + int o = expr_int (offs); + if (o < 0 || o + type_size (type) > type_size (lvtype)) { + // not a valid offset for the type, which technically should + // be an error, but there may be legitimate reasons for doing + // such pointer shenanigans + goto just_a_pointer; + } + alias = new_offset_alias_expr (type, lv, expr_int (offs)); + } else { + alias = new_alias_expr (type, lvalue->e.alias.expr); + } + expr_file_line (alias, ref); + return addressing_mode (sblock, alias, base, offset, mode, target); + } else if (ref->type != ex_alias || ref->e.alias.offset) { + // probably just a pointer +just_a_pointer: + sblock = statement_subexpr (sblock, ref, base); + *offset = short_operand (0, ref); + *mode = 2; // mode C: ptr + constant index + } else if (is_ptr (get_type (ref->e.alias.expr))) { + // cast of one pointer type to another + return ptr_addressing_mode (sblock, ref->e.alias.expr, base, offset, + mode, target); + } else { + // alias with no offset + if (!is_integral (get_type (ref->e.alias.expr))) { + internal_error (ref, "expected integer expr in ref"); + } + expr_t *intptr = ref->e.alias.expr; + if (intptr->type != ex_expr + || (intptr->e.expr.op != '+' + && intptr->e.expr.op != '-')) { + // treat ref as simple pointer + sblock = statement_subexpr (sblock, ref, base); + *offset = short_operand (0, ref); + *mode = 2; // mode C: ptr + constant index + } else { + expr_t *ptr = intptr->e.expr.e1; + expr_t *offs = intptr->e.expr.e2; + int const_offs; + if (target) { + if (ptr->type == ex_alias + && is_ptr (get_type (ptr->e.alias.expr))) { + *target = find_def_operand (ptr->e.alias.expr); + } + } + // move the +/- to the offset + offs = unary_expr (intptr->e.expr.op, offs); + // make the base a pointer again + ptr = new_alias_expr (ref->e.alias.type, ptr); + sblock = statement_subexpr (sblock, ptr, base); + if (is_constant (offs) + && (const_offs = expr_int (offs)) < 32768 + && const_offs >= -32768) { + *mode = 2; + *offset = short_operand (const_offs, ref); + } else { + *mode = 3; + sblock = statement_subexpr (sblock, offs, offset); + } + } + } + return sblock; +} + +static sblock_t * +addressing_mode (sblock_t *sblock, expr_t *ref, + operand_t **base, operand_t **offset, pr_ushort_t *mode, + operand_t **target) +{ + if (is_indirect (ref)) { + // ref is known to be either ex_expr or ex_uexpr, with '.' for + // the operator + if (ref->type == ex_expr) { + expr_t *ent_expr = ref->e.expr.e1; + expr_t *fld_expr = ref->e.expr.e2; + if (!is_entity (get_type (ent_expr)) + || !is_field (get_type (fld_expr))) { + print_expr (ref); + internal_error (ref, "expected entity.field"); + } + sblock = statement_subexpr (sblock, ent_expr, base); + sblock = statement_subexpr (sblock, fld_expr, offset); + *mode = 1;//entity.field + } else if (ref->type == ex_uexpr) { + sblock = ptr_addressing_mode (sblock, ref->e.expr.e1, base, offset, + mode, target); + } else { + internal_error (ref, "unexpected expression type for indirect: %s", + expr_names[ref->type]); + } + } else { + sblock = statement_subexpr (sblock, ref, base); + *offset = short_operand (0, ref); + *mode = 0; + *target = 0; + } + return sblock; +} + +static sblock_t * +statement_return (sblock_t *sblock, expr_t *e) +{ + const char *opcode; + statement_t *s; + + debug (e, "RETURN"); + opcode = "return"; + if (!e->e.retrn.ret_val) { + if (options.code.progsversion == PROG_ID_VERSION) { + e->e.retrn.ret_val = new_float_expr (0); + } + } + s = new_statement (st_func, opcode, e); + if (options.code.progsversion < PROG_VERSION) { + if (e->e.retrn.ret_val) { + expr_t *ret_val = e->e.retrn.ret_val; + type_t *ret_type = get_type (ret_val); + + // at_return is used for passing the result of a void_return + // function through void. v6 progs always use .return for the + // return value, so don't need to do anything special: just call + // the function and do a normal void return + if (!e->e.retrn.at_return) { + s->opa = return_operand (ret_type, e); + } + sblock = statement_subexpr (sblock, ret_val, &s->opa); + } + } else { + if (!e->e.retrn.at_return && e->e.retrn.ret_val) { + expr_t *ret_val = e->e.retrn.ret_val; + type_t *ret_type = get_type (ret_val); + operand_t *target = 0; + pr_ushort_t ret_crtl = type_size (ret_type) - 1; + pr_ushort_t mode = 0; + sblock = addressing_mode (sblock, ret_val, &s->opa, &s->opb, &mode, + &target); + ret_crtl |= mode << 5; + s->opc = short_operand (ret_crtl, e); + statement_add_use (s, target); + } else { + if (e->e.retrn.at_return) { + expr_t *call = e->e.retrn.ret_val; + if (!call || !is_function_call (call)) { + internal_error (e, "@return with no call"); + } + // FIXME hard-coded reg, and assumes 3 is free + #define REG 3 + expr_t *with = new_with_expr (11, REG, new_short_expr (0)); + def_t *ret_ptr = new_def (0, 0, 0, sc_local); + operand_t *ret_op = def_operand (ret_ptr, &type_void, e); + ret_ptr->reg = REG; + expr_file_line (with, e); + sblock = statement_slist (sblock, with); + sblock = statement_subexpr (sblock, call, &ret_op); + } + s->opa = short_operand (0, e); + s->opb = short_operand (0, e); + s->opc = short_operand (-1, e); // void return + } + } + sblock_add_statement (sblock, s); + sblock->next = new_sblock (); + sblock = sblock->next; + + return sblock; +} + +static sblock_t * +statement_adjstk (sblock_t *sblock, expr_t *e) +{ + statement_t *s = new_statement (st_func, "adjstk", e); + s->opa = short_operand (e->e.adjstk.mode, e); + s->opb = short_operand (e->e.adjstk.offset, e); + + sblock_add_statement (sblock, s); + return sblock; +} + +static sblock_t * +statement_with (sblock_t *sblock, expr_t *e) +{ + statement_t *s = new_statement (st_func, "with", e); + s->opa = short_operand (e->e.with.mode, e); + s->opc = short_operand (e->e.with.reg, e); + sblock = statement_subexpr (sblock, e->e.with.with, &s->opb); + sblock_add_statement (sblock, s); return sblock; } static statement_t * lea_statement (operand_t *pointer, operand_t *offset, expr_t *e) { - statement_t *s = new_statement (st_expr, "&", e); + statement_t *s = new_statement (st_address, "lea", e); s->opa = pointer; s->opb = offset; - s->opc = temp_operand (&type_pointer); + s->opc = temp_operand (&type_ptr, e); return s; } static statement_t * -address_statement (operand_t *value, expr_t *e) +movep_statement (operand_t *dst, operand_t *src, type_t *type, expr_t *e) { - statement_t *s = new_statement (st_expr, "&", e); - s->opa = value; - s->opc = temp_operand (&type_pointer); + operand_t *dst_addr = operand_address (dst, e); + statement_t *s = new_statement (st_ptrmove, "movep", e); + s->opa = src; + //FIXME large types + s->opb = short_operand (type_size (type), e); + s->opc = dst_addr; + return s; +} + +static statement_t * +load_statement (operand_t *ptr, operand_t *offs, operand_t *op, expr_t *e) +{ + statement_t *s = new_statement (st_expr, "load", e); + s->opa = ptr; + s->opb = offs; + s->opc = op; return s; } static sblock_t * expr_deref (sblock_t *sblock, expr_t *deref, operand_t **op) { - type_t *type = deref->e.expr.type; - expr_t *e; + type_t *load_type = deref->e.expr.type; + expr_t *ptr_expr = deref->e.expr.e1; + operand_t *base = 0; + operand_t *offset = 0; + operand_t *target = 0; + pr_ushort_t mode; + statement_t *s; - e = deref->e.expr.e1; - if (e->type == ex_uexpr && e->e.expr.op == '&' - && e->e.expr.e1->type == ex_symbol) { - if (e->e.expr.e1->e.symbol->sy_type != sy_var) - internal_error (e, "address of non-var"); - *op = def_operand (e->e.expr.e1->e.symbol->s.def, type); - } else if (e->type == ex_expr && e->e.expr.op == '&') { - statement_t *s; - operand_t *ptr = 0; - operand_t *offs = 0; - sblock = statement_subexpr (sblock, e->e.expr.e1, &ptr); - sblock = statement_subexpr (sblock, e->e.expr.e2, &offs); - if (!*op) - *op = temp_operand (type); - if (low_level_type (type) == ev_void) { - operand_t *src_addr; - operand_t *dst_addr; + sblock = addressing_mode (sblock, deref, &base, &offset, &mode, &target); - s = lea_statement (ptr, offs, e); - src_addr = s->opc; - sblock_add_statement (sblock, s); + if (!*op) { + *op = temp_operand (load_type, deref); + } - //FIXME an address immediate would be nice. - s = address_statement (*op, e); - dst_addr = s->opc; + switch (mode) { + case 0://direct def access + // FIXME should check that offset is 0, but currently, + // addressing_mode always sets offset to 0 for mode 0 + s = assign_statement (*op, base, deref); sblock_add_statement (sblock, s); + statement_add_use (s, target); + return sblock; + case 1://entity.field + case 2://const indexed pointer + case 3://var indexed pointer + break; + default: + internal_error (deref, "unexpected addressing mode: %d", mode); + } - s = new_statement (st_move, "", deref); - s->opa = src_addr; - s->opb = short_operand (type_size (type)); - s->opc = dst_addr; - sblock_add_statement (sblock, s); - } else { - s = new_statement (st_expr, ".", deref); - s->opa = ptr; - s->opb = offs; - s->opc = *op; - sblock_add_statement (sblock, s); - } - } else if (e->type == ex_value && e->e.value->type == ev_pointer) { - ex_pointer_t *ptr = &e->e.value->v.pointer; - *op = def_operand (alias_def (ptr->def, ptr->type, ptr->val), - ptr->type); + if (low_level_type (load_type) == ev_void) { + s = lea_statement (base, offset, ptr_expr); + sblock_add_statement (sblock, s); + + s = movep_statement (*op, s->opc, load_type, deref); + sblock_add_statement (sblock, s); } else { - statement_t *s; - operand_t *ptr = 0; - - sblock = statement_subexpr (sblock, e, &ptr); - if (!*op) - *op = temp_operand (type); - s = new_statement (st_expr, ".", deref); - s->opa = ptr; - s->opb = short_operand (0); - s->opc = *op; + s = load_statement (base, offset, *op, deref); sblock_add_statement (sblock, s); } + statement_add_use (s, target); + return sblock; } @@ -805,79 +1581,95 @@ expr_block (sblock_t *sblock, expr_t *e, operand_t **op) } static sblock_t * -expr_expr (sblock_t *sblock, expr_t *e, operand_t **op) +expr_alias (sblock_t *sblock, expr_t *e, operand_t **op) { - const char *opcode; - statement_t *s; + operand_t *aop = 0; + operand_t *top; + type_t *type; + def_t *def; + int offset = 0; - switch (e->e.expr.op) { - case 'c': - sblock = expr_call (sblock, e, op); - break; - case '=': - case PAS: - sblock = expr_assign (sblock, e, op); - break; - case 'm': - case 'M': - sblock = expr_move (sblock, e, op); - break; - default: - opcode = convert_op (e->e.expr.op); - if (!opcode) - internal_error (e, "ice ice baby"); - s = new_statement (st_expr, opcode, e); - sblock = statement_subexpr (sblock, e->e.expr.e1, &s->opa); - sblock = statement_subexpr (sblock, e->e.expr.e2, &s->opb); - if (!*op) - *op = temp_operand (e->e.expr.type); - s->opc = *op; - sblock_add_statement (sblock, s); - break; + if (e->e.alias.offset) { + offset = expr_int (e->e.alias.offset); + } + type = e->e.alias.type; + sblock = statement_subexpr (sblock, e->e.alias.expr, &aop); + if (type_compatible (aop->type, type)) { + if (offset) { + //For types to be compatible, they must be the same size, thus this + //seemingly mismatched error + internal_error (e, "offset alias of same size type"); + } + *op = aop; + return sblock; + } + if (aop->op_type == op_temp) { + while (aop->tempop.alias) { + aop = aop->tempop.alias; + if (aop->op_type != op_temp) + internal_error (e, "temp alias of non-temp var"); + if (aop->tempop.alias) + bug (e, "aliased temp alias"); + } + for (top = aop->tempop.alias_ops; top; top = top->next) { + if (top->type == type && top->tempop.offset == offset) { + break; + } + } + if (!top) { + top = temp_operand (type, e); + top->tempop.alias = aop; + top->tempop.offset = offset; + top->next = aop->tempop.alias_ops; + aop->tempop.alias_ops = top; + } + *op = top; + } else if (aop->op_type == op_def) { + def = aop->def; + while (def->alias) + def = def->alias; + *op = def_operand (alias_def (def, type, offset), 0, e); + } else if (aop->op_type == op_value) { + *op = value_operand (aop->value, e); + (*op)->type = type; + } else { + internal_error (e, "invalid alias target: %s: %s", + optype_str (aop->op_type), operand_string (aop)); } return sblock; } static sblock_t * -expr_alias (sblock_t *sblock, expr_t *e, operand_t **op) +expr_expr (sblock_t *sblock, expr_t *e, operand_t **op) { - operand_t *aop = 0; - operand_t *top; - etype_t type; - def_t *def; + const char *opcode; + statement_t *s; - type = low_level_type (e->e.expr.type); - sblock = statement_subexpr (sblock, e->e.expr.e1, &aop); - if (aop->type == type) { - *op = aop; - return sblock; + opcode = convert_op (e->e.expr.op); + if (!opcode) + internal_error (e, "ice ice baby"); + if (strcmp (opcode, "ne") == 0 && is_string (get_type (e->e.expr.e1))) { + opcode = "cmp"; } - if (aop->op_type == op_temp) { - while (aop->o.tempop.alias) { - aop = aop->o.tempop.alias; - if (aop->op_type != op_temp) - internal_error (e, "temp alias of non-temp var"); + if (strcmp (opcode, "dot") == 0) { + if (type_width (get_type (e->e.expr.e1)) == 2) { + opcode = "cdot"; } - for (top = aop->o.tempop.alias_ops; top; top = top->next) - if (top->type == type) - break; - if (!top) { - top = new_operand (op_temp); - top->type = type; - top->o.tempop.alias = aop; - top->next = aop->o.tempop.alias_ops; - aop->o.tempop.alias_ops = top; + if (type_width (get_type (e->e.expr.e1)) == 3) { + opcode = "vdot"; + } + if (type_width (get_type (e->e.expr.e1)) == 4) { + opcode = "qdot"; } - *op = top; - } else if (aop->op_type == op_def) { - def = aop->o.def; - while (def->alias) - def = def->alias; - *op = def_operand (alias_def (def, ev_types[type], 0), 0); - } else { - internal_error (e, "invalid alias target: %s: %s", - optype_str (aop->op_type), operand_string (aop)); } + s = new_statement (st_expr, opcode, e); + sblock = statement_subexpr (sblock, e->e.expr.e1, &s->opa); + sblock = statement_subexpr (sblock, e->e.expr.e2, &s->opb); + if (!*op) + *op = temp_operand (e->e.expr.type, e); + s->opc = *op; + sblock_add_statement (sblock, s); + return sblock; } @@ -889,13 +1681,19 @@ expr_cast (sblock_t *sblock, expr_t *e, operand_t **op) statement_t *s; src_type = get_type (e->e.expr.e1); - if ((src_type->type == ev_integer && type->type == ev_float) - || (src_type->type == ev_float && type->type == ev_integer)) { + if (is_scalar (src_type) && is_scalar (type)) { operand_t *src = 0; sblock = statement_subexpr (sblock, e->e.expr.e1, &src); - *op = temp_operand (e->e.expr.type); - s = new_statement (st_expr, "", e); + *op = temp_operand (e->e.expr.type, e); + s = new_statement (st_expr, "conv", e); s->opa = src; + if (options.code.progsversion == PROG_VERSION) { + int from = type_cast_map[src_type->type]; + int to = type_cast_map[type->type]; + int width = type_width (src_type) - 1; + int conv = TYPE_CAST_CODE (from, to, width); + s->opb = short_operand (conv, e); + } s->opc = *op; sblock_add_statement (sblock, s); } else { @@ -927,15 +1725,9 @@ expr_uexpr (sblock_t *sblock, expr_t *e, operand_t **op) statement_t *s; switch (e->e.expr.op) { - case '&': - sblock = expr_address (sblock, e, op); - break; case '.': sblock = expr_deref (sblock, e, op); break; - case 'A': - sblock = expr_alias (sblock, e, op); - break; case 'C': sblock = expr_cast (sblock, e, op); break; @@ -950,27 +1742,151 @@ expr_uexpr (sblock_t *sblock, expr_t *e, operand_t **op) s = new_statement (st_expr, opcode, e); sblock = statement_subexpr (sblock, e->e.expr.e1, &s->opa); if (!*op) - *op = temp_operand (e->e.expr.type); + *op = temp_operand (e->e.expr.type, e); s->opc = *op; sblock_add_statement (sblock, s); } return sblock; } +static sblock_t * +expr_horizontal (sblock_t *sblock, expr_t *e, operand_t **op) +{ + const char *opcode = "hops"; + statement_t *s; + int hop; + type_t *res_type = e->e.hop.type; + type_t *vec_type = get_type (e->e.hop.vec); + + switch (e->e.hop.op) { + case '&': + hop = 0; + break; + case '|': + hop = 1; + break; + case '^': + hop = 2; + break; + case '+': + if (is_integral (vec_type)) { + hop = 3; + } else { + hop = 7; + } + break; + case NAND: + hop = 4; + break; + case NOR: + hop = 5; + break; + case XNOR: + hop = 6; + break; + default: + internal_error (e, "invalid horizontal op"); + } + hop |= (type_width (vec_type) - 1) << 3; + hop |= (pr_type_size[vec_type->type] - 1) << 5; + + s = new_statement (st_expr, opcode, e); + sblock = statement_subexpr (sblock, e->e.hop.vec, &s->opa); + s->opb = short_operand (hop, e); + if (!*op) { + *op = temp_operand (res_type, e); + } + s->opc = *op; + sblock_add_statement (sblock, s); + + return sblock; +} + +static sblock_t * +expr_swizzle (sblock_t *sblock, expr_t *e, operand_t **op) +{ + const char *opcode = "swizzle"; + statement_t *s; + int swiz = 0; + type_t *res_type = e->e.swizzle.type; + + for (int i = 0; i < 4; i++) { + swiz |= e->e.swizzle.source[i] & 3; + } + swiz |= (e->e.swizzle.neg & 0xf) << 8; + swiz |= (e->e.swizzle.zero & 0xf) << 12; + + s = new_statement (st_expr, opcode, e); + sblock = statement_subexpr (sblock, e->e.swizzle.src, &s->opa); + s->opb = short_operand (swiz, e); + if (!*op) { + *op = temp_operand (res_type, e); + } + s->opc = *op; + sblock_add_statement (sblock, s); + + return sblock; +} + +static sblock_t * +expr_extend (sblock_t *sblock, expr_t *e, operand_t **op) +{ + type_t *src_type = get_type (e->e.extend.src); + type_t *res_type = e->e.extend.type; + int src_width = type_width (src_type); + int res_width = type_width (res_type); + type_t *src_base = base_type (src_type); + type_t *res_base = base_type (res_type); + static int mode[4][4] = { + {-1, 0, 1, 2}, + {-1,-1, 3, 4}, + {-1,-1,-1, 5}, + {-1,-1,-1,-1}, + }; + int ext = mode[src_width - 1][res_width - 1]; + ext |= (e->e.extend.extend & 3) << 3; + ext |= (pr_type_size[res_base->type] - 1) << 5; + if (ext < 0 || res_base != src_base) { + internal_error (e, "invalid type combination for extend %d %d %d", + ext, src_width, res_width); + } + + statement_t *s = new_statement (st_expr, "extend", e); + sblock = statement_subexpr (sblock, e->e.extend.src, &s->opa); + s->opb = short_operand (ext, e); + if (!*op) { + *op = temp_operand (res_type, e); + } + s->opc = *op; + sblock_add_statement (sblock, s); + + return sblock; +} + +static sblock_t * +expr_def (sblock_t *sblock, expr_t *e, operand_t **op) +{ + *op = def_operand (e->e.def, e->e.def->type, e); + return sblock; +} + static sblock_t * expr_symbol (sblock_t *sblock, expr_t *e, operand_t **op) { symbol_t *sym = e->e.symbol; if (sym->sy_type == sy_var) { - *op = def_operand (sym->s.def, sym->type); + *op = def_operand (sym->s.def, sym->type, e); } else if (sym->sy_type == sy_const) { - *op = value_operand (sym->s.value); + *op = value_operand (sym->s.value, e); } else if (sym->sy_type == sy_func) { - *op = def_operand (sym->s.func->def, 0); + if (!sym->s.func) { + make_function (sym, 0, pr.symtab->space, sc_extern); + } + *op = def_operand (sym->s.func->def, 0, e); } else { - internal_error (e, "unexpected symbol type: %s", - symtype_str(sym->sy_type)); + internal_error (e, "unexpected symbol type: %s for %s", + symtype_str (sym->sy_type), sym->name); } return sblock; } @@ -979,45 +1895,130 @@ static sblock_t * expr_temp (sblock_t *sblock, expr_t *e, operand_t **op) { if (!e->e.temp.op) - e->e.temp.op = temp_operand (e->e.temp.type); + e->e.temp.op = temp_operand (e->e.temp.type, e); *op = e->e.temp.op; return sblock; } +static int +statement_copy_elements (sblock_t **sblock, expr_t *dst, expr_t *src, int base) +{ + int index = 0; + for (expr_t *e = src->e.vector.list; e; e = e->next) { + if (e->type == ex_vector) { + index += statement_copy_elements (sblock, dst, e, index + base); + } else { + int size = type_size (base_type (get_type (dst))); + type_t *src_type = get_type (e); + expr_t *dst_ele = new_offset_alias_expr (src_type, dst, + size * (index + base)); + index += type_width (src_type); + *sblock = statement_slist (*sblock, assign_expr (dst_ele, e)); + } + } + return index; +} + +static sblock_t * +expr_vector_e (sblock_t *sblock, expr_t *e, operand_t **op) +{ + expr_t *tmp; + type_t *vec_type = get_type (e); + int file = pr.source_file; + int line = pr.source_line; + + pr.source_file = e->file; + pr.source_line = e->line; + + tmp = new_temp_def_expr (vec_type); + statement_copy_elements (&sblock, tmp, e, 0); + + pr.source_file = file; + pr.source_line = line; + sblock = statement_subexpr (sblock, tmp, op); + return sblock; +} + +static sblock_t * +expr_nil (sblock_t *sblock, expr_t *e, operand_t **op) +{ + type_t *nil = e->e.nil; + expr_t *size_expr; + size_t nil_size; + operand_t *zero; + operand_t *size; + statement_t *s; + + if (!is_structural (nil)) { + *op = value_operand (new_nil_val (nil), e); + return sblock; + } + if (!*op) { + *op = temp_operand (nil, e); + } + nil_size = type_size (nil); + if (nil_size < 0x10000) { + size_expr = new_short_expr (nil_size); + } else { + size_expr = new_int_expr (nil_size); + } + sblock = statement_subexpr (sblock, new_int_expr(0), &zero); + sblock = statement_subexpr (sblock, size_expr, &size); + + s = new_statement (st_memset, "memset", e); + s->opa = zero; + s->opb = size; + s->opc = *op; + sblock_add_statement (sblock, s); + + return sblock; +} + static sblock_t * expr_value (sblock_t *sblock, expr_t *e, operand_t **op) { - *op = value_operand (e->e.value); + *op = value_operand (e->e.value, e); return sblock; } +static sblock_t * +expr_selector (sblock_t *sblock, expr_t *e, operand_t **op) +{ + return statement_subexpr (sblock, e->e.selector.sel_ref, op); +} + static sblock_t * statement_subexpr (sblock_t *sblock, expr_t *e, operand_t **op) { - static expr_f sfuncs[] = { - 0, // ex_error - 0, // ex_state - 0, // ex_bool - 0, // ex_label - 0, // ex_labelref - expr_block, // ex_block - expr_expr, - expr_uexpr, - expr_symbol, - expr_temp, - 0, // ex_vector - 0, // ex_nil - expr_value, + static expr_f sfuncs[ex_count] = { + [ex_block] = expr_block, + [ex_expr] = expr_expr, + [ex_uexpr] = expr_uexpr, + [ex_horizontal] = expr_horizontal, + [ex_swizzle] = expr_swizzle, + [ex_def] = expr_def, + [ex_symbol] = expr_symbol, + [ex_temp] = expr_temp, + [ex_vector] = expr_vector_e, + [ex_nil] = expr_nil, + [ex_value] = expr_value, + [ex_selector] = expr_selector, + [ex_alias] = expr_alias, + [ex_address] = expr_address, + [ex_assign] = expr_assign, + [ex_branch] = expr_branch, + [ex_extend] = expr_extend, }; if (!e) { *op = 0; return sblock; } - if (e->type > ex_value) - internal_error (e, "bad expression type"); + if (e->type >= ex_count) + internal_error (e, "bad sub-expression type: %d", e->type); if (!sfuncs[e->type]) - internal_error (e, "unexpected expression type"); + internal_error (e, "unexpected sub-expression type: %s", + expr_names[e->type]); sblock = sfuncs[e->type] (sblock, e, op); return sblock; @@ -1034,7 +2035,7 @@ statement_state (sblock_t *sblock, expr_t *e) { statement_t *s; - s = new_statement (st_state, "", e); + s = new_statement (st_state, "state", e); sblock = statement_subexpr (sblock, e->e.state.frame, &s->opa); sblock = statement_subexpr (sblock, e->e.state.think, &s->opb); sblock = statement_subexpr (sblock, e->e.state.step, &s->opc); @@ -1047,33 +2048,30 @@ build_bool_block (expr_t *block, expr_t *e) { switch (e->type) { case ex_bool: - build_bool_block (block, e->e.bool.e); + build_bool_block (block, e->e.boolean.e); return; case ex_label: e->next = 0; append_expr (block, e); return; + case ex_assign: + e->next = 0; + append_expr (block, e); + return; + case ex_branch: + e->next = 0; + append_expr (block, e); + return; case ex_expr: if (e->e.expr.op == OR || e->e.expr.op == AND) { build_bool_block (block, e->e.expr.e1); build_bool_block (block, e->e.expr.e2); - } else if (e->e.expr.op == 'i') { - e->next = 0; - append_expr (block, e); - } else if (e->e.expr.op == 'n') { - e->next = 0; - append_expr (block, e); } else { e->next = 0; append_expr (block, e); } return; case ex_uexpr: - if (e->e.expr.op == 'g') { - e->next = 0; - append_expr (block, e); - return; - } break; case ex_block: if (!e->e.block.result) { @@ -1094,19 +2092,20 @@ build_bool_block (expr_t *block, expr_t *e) static int is_goto_expr (expr_t *e) { - return e && e->type == ex_uexpr && e->e.expr.op == 'g'; + return e && e->type == ex_branch && e->e.branch.type == pr_branch_jump + && !e->e.branch.index; } static int is_if_expr (expr_t *e) { - return e && e->type == ex_expr && e->e.expr.op == 'i'; + return e && e->type == ex_branch && e->e.branch.type == pr_branch_ne; } static int is_ifnot_expr (expr_t *e) { - return e && e->type == ex_expr && e->e.expr.op == 'n'; + return e && e->type == ex_branch && e->e.branch.type == pr_branch_eq; } static sblock_t * @@ -1121,33 +2120,33 @@ statement_bool (sblock_t *sblock, expr_t *e) s = &block->e.block.head; while (*s) { if (is_if_expr (*s) && is_goto_expr ((*s)->next)) { - l = (*s)->e.expr.e2; + l = (*s)->e.branch.target; for (e = (*s)->next->next; e && e->type == ex_label; e = e->next) { if (e == l) { l->e.label.used--; e = *s; - e->e.expr.op = 'n'; - e->e.expr.e2 = e->next->e.expr.e1; + e->e.branch.type = pr_branch_eq; + e->e.branch.target = e->next->e.branch.target; e->next = e->next->next; break; } } s = &(*s)->next; } else if (is_ifnot_expr (*s) && is_goto_expr ((*s)->next)) { - l = (*s)->e.expr.e2; + l = (*s)->e.branch.target; for (e = (*s)->next->next; e && e->type == ex_label; e = e->next) { if (e == l) { l->e.label.used--; e = *s; - e->e.expr.op = 'i'; - e->e.expr.e2 = e->next->e.expr.e1; + e->e.branch.type = pr_branch_ne; + e->e.branch.target = e->next->e.branch.target; e->next = e->next->next; break; } } s = &(*s)->next; } else if (is_goto_expr (*s)) { - l = (*s)->e.expr.e1; + l = (*s)->e.branch.target; for (e = (*s)->next; e && e->type == ex_label; e = e->next) { if (e == l) { l->e.label.used--; @@ -1178,7 +2177,11 @@ statement_label (sblock_t *sblock, expr_t *e) e->e.label.next = sblock->labels; sblock->labels = &e->e.label; } else { - debug (e, "dropping unused label %s", e->e.label.name); + if (e->e.label.symbol) { + warning (e, "unused label %s", e->e.label.symbol->name); + } else { + debug (e, "dropping unused label %s", e->e.label.name); + } } return sblock; } @@ -1191,85 +2194,71 @@ statement_block (sblock_t *sblock, expr_t *e) sblock = sblock->next; } sblock = statement_slist (sblock, e->e.block.head); + if (e->e.block.is_call) { + // for a fuction call, the call expresion is in only the result, not + // the actual block + sblock = statement_slist (sblock, e->e.block.result); + } return sblock; } static sblock_t * statement_expr (sblock_t *sblock, expr_t *e) { - switch (e->e.expr.op) { - case 'c': - sblock = expr_call (sblock, e, 0); - break; - case 'g': - case 'i': - case 'n': - case IFBE: - case IFB: - case IFAE: - case IFA: - sblock = statement_branch (sblock, e); - break; - case '=': - case PAS: - sblock = expr_assign (sblock, e, 0); - break; - case 'm': - case 'M': - sblock = expr_move (sblock, e, 0); - break; - default: - if (e->e.expr.op < 256) - debug (e, "e %c", e->e.expr.op); - else - debug (e, "e %d", e->e.expr.op); - if (options.warnings.executable) - warning (e, "Non-executable statement;" - " executing programmer instead."); - } + if (e->e.expr.op < 256) + debug (e, "e %c", e->e.expr.op); + else + debug (e, "e %d", e->e.expr.op); + if (options.warnings.executable) + warning (e, "Non-executable statement; executing programmer instead."); return sblock; } static sblock_t * statement_uexpr (sblock_t *sblock, expr_t *e) { - const char *opcode; + debug (e, "e ue %d", e->e.expr.op); + if (options.warnings.executable) + warning (e, "Non-executable statement; executing programmer instead."); + return sblock; +} + +static sblock_t * +statement_memset (sblock_t *sblock, expr_t *e) +{ + expr_t *dst = e->e.memset.dst; + expr_t *val = e->e.memset.val; + expr_t *count = e->e.memset.count; + const char *opcode = "memset"; statement_t *s; - switch (e->e.expr.op) { - case 'r': - debug (e, "RETURN"); - opcode = ""; - if (!e->e.expr.e1) { - if (options.code.progsversion != PROG_ID_VERSION) { - opcode = ""; - } else { - e->e.expr.e1 = new_float_expr (0); - } - } - s = new_statement (st_func, opcode, e); - if (e->e.expr.e1) - sblock = statement_subexpr (sblock, e->e.expr.e1, &s->opa); - sblock_add_statement (sblock, s); - sblock->next = new_sblock (); - sblock = sblock->next; - break; - case 'g': - sblock = statement_branch (sblock, e); - break; - default: - debug (e, "e ue %d", e->e.expr.op); - if (options.warnings.executable) - warning (e, "Non-executable statement;" - " executing programmer instead."); + if (is_constant (count)) { + if (is_int (get_type (count)) + && (unsigned) expr_int (count) < 0x10000) { + count = new_short_expr (expr_int (count)); + } + if (is_uint (get_type (count)) && expr_int (count) < 0x10000) { + count = new_short_expr (expr_uint (count)); + } } + s = new_statement (st_move, opcode, e); + sblock = statement_subexpr (sblock, dst, &s->opc); + sblock = statement_subexpr (sblock, count, &s->opb); + sblock = statement_subexpr (sblock, val, &s->opa); + sblock_add_statement (sblock, s); return sblock; } +static sblock_t * +statement_assign (sblock_t *sblock, expr_t *e) +{ + return expr_assign (sblock, e, 0); +} + static sblock_t * statement_nonexec (sblock_t *sblock, expr_t *e) { - if (options.warnings.executable) + if (!e->rvalue && options.warnings.executable) warning (e, "Non-executable statement; executing programmer instead."); return sblock; } @@ -1277,24 +2266,30 @@ statement_nonexec (sblock_t *sblock, expr_t *e) static sblock_t * statement_slist (sblock_t *sblock, expr_t *e) { - static statement_f sfuncs[] = { - statement_ignore, // ex_error - statement_state, - statement_bool, - statement_label, - 0, // ex_labelref - statement_block, - statement_expr, - statement_uexpr, - statement_nonexec, // ex_symbol - statement_nonexec, // ex_temp - statement_nonexec, // ex_vector - statement_nonexec, // ex_nil - statement_nonexec, // ex_value + static statement_f sfuncs[ex_count] = { + [ex_error] = statement_ignore, + [ex_state] = statement_state, + [ex_bool] = statement_bool, + [ex_label] = statement_label, + [ex_block] = statement_block, + [ex_expr] = statement_expr, + [ex_uexpr] = statement_uexpr, + [ex_def] = statement_nonexec, + [ex_symbol] = statement_nonexec, + [ex_temp] = statement_nonexec, + [ex_vector] = statement_nonexec, + [ex_nil] = statement_nonexec, + [ex_value] = statement_nonexec, + [ex_memset] = statement_memset, + [ex_assign] = statement_assign, + [ex_branch] = statement_branch, + [ex_return] = statement_return, + [ex_adjstk] = statement_adjstk, + [ex_with] = statement_with, }; for (/**/; e; e = e->next) { - if (e->type > ex_value) + if (e->type >= ex_count || !sfuncs[e->type]) internal_error (e, "bad expression type"); sblock = sfuncs[e->type] (sblock, e); } @@ -1383,7 +2378,7 @@ remove_label_from_dest (ex_label_t *label) sblock_t *sblock; ex_label_t **l; - if (!label) + if (!label || !label->dest) return; debug (0, "dropping deceased label %s", label->name); @@ -1420,15 +2415,23 @@ thread_jumps (sblock_t *blocks) if (!sblock->statements) continue; s = (statement_t *) sblock->tail; - if (statement_is_goto (s)) - label = &s->opa->o.label; - else if (statement_is_cond (s)) - label = &s->opb->o.label; - else + if (statement_is_goto (s)) { + label = &s->opa->label; + if (!(*label)->dest && (*label)->symbol) { + error (s->opa->expr, "undefined label `%s'", + (*label)->symbol->name); + (*label)->symbol = 0; + } + } else if (statement_is_cond (s)) { + label = &s->opa->label; + } else { continue; + } for (l = *label; - l->dest->statements && statement_is_goto (l->dest->statements); - l = l->dest->statements->opa->o.label) { + l->dest && l->dest->statements + && statement_is_goto (l->dest->statements); + l = *statement_get_labelref (l->dest->statements)) { + // empty loop } if (l != *label) { unuse_label (*label); @@ -1481,11 +2484,12 @@ remove_dead_blocks (sblock_t *blocks) s = (statement_t *) sblock->tail; if (statement_is_cond (s) && sb->statements && statement_is_goto (sb->statements) - && s->opb->o.label->dest == sb->next) { + && statement_get_target (s) == sb->next) { debug (0, "merging if/goto %p %p", sblock, sb); - unuse_label (s->opb->o.label); - s->opb->o.label = sb->statements->opa->o.label; - s->opb->o.label->used++; + ex_label_t **labelref = statement_get_labelref(s); + unuse_label (*labelref); + *labelref = *statement_get_labelref (sb->statements); + (*labelref)->used++; invert_conditional (s); sb->reachable = 0; for (sb = sb->next; sb; sb = sb->next) @@ -1507,10 +2511,8 @@ remove_dead_blocks (sblock_t *blocks) if (sb->statements) { s = (statement_t *) sb->tail; - if (statement_is_goto (s)) - label = s->opa->o.label; - else if (statement_is_cond (s)) - label = s->opb->o.label; + ex_label_t **labelref = statement_get_labelref (s); + label = labelref ? *labelref : 0; } unuse_label (label); did_something = 1; @@ -1524,14 +2526,66 @@ remove_dead_blocks (sblock_t *blocks) return did_anything; } +static void +super_dealloc_warning (expr_t *expr, pseudoop_t *op) +{ + warning (expr, + "control may reach end of derived -dealloc without invoking %s", + op->name); +} + +static void +search_for_super_dealloc (sblock_t *sblock) +{ + operand_t *op; + pseudoop_t *super_dealloc = new_pseudoop ("[super dealloc]"); + int super_dealloc_found = 0; + super_dealloc->next = current_func->pseudo_ops; + current_func->pseudo_ops = super_dealloc; + super_dealloc->uninitialized = super_dealloc_warning; + while (sblock) { + for (statement_t *st = sblock->statements; st; st = st->next) { + if (statement_is_return (st)) { + op = pseudo_operand (super_dealloc, st->expr); + statement_add_use (st, op); + continue; + } + if (!statement_is_call (st)) { + continue; + } + // effectively checks target + if (st->opa->op_type != op_def + || strcmp (st->opa->def->name, "obj_msgSend_super") != 0) { + continue; + } + // function arguments are in reverse order, and the selector + // is the second argument (or second last in the list) + expr_t *arg; + for (arg = st->expr->e.branch.args; + arg && arg->next && arg->next->next; arg = arg->next) { + } + if (arg && arg->next && is_selector (arg)) { + selector_t *sel = get_selector (st->expr->e.branch.args); + if (sel && strcmp (sel->name, "dealloc") == 0) { + op = pseudo_operand (super_dealloc, st->expr); + statement_add_def (st, op); + super_dealloc_found++; + } + } + } + sblock = sblock->next; + } + // warn only when NOT optimizing because flow analysis will catch the + // missed invokation + if (!super_dealloc_found && !options.code.optimize) { + warning (0, "Derived class -dealloc does not call [super dealloc]"); + } +} + static void check_final_block (sblock_t *sblock) { statement_t *s = 0; - symbol_t *return_symbol = 0; - def_t *return_def = 0; - operand_t *return_operand = 0; - const char *return_opcode = ""; if (!sblock) return; @@ -1544,7 +2598,7 @@ check_final_block (sblock_t *sblock) if (statement_is_return (s)) return; } - if (current_func->sym->type->t.func.type != &type_void) + if (!is_void(current_func->sym->type->t.func.type)) warning (0, "control reaches end of non-void function"); if (s && s->type >= st_func) { // func and flow end blocks, so we need to add a new block to take the @@ -1552,16 +2606,17 @@ check_final_block (sblock_t *sblock) sblock->next = new_sblock (); sblock = sblock->next; } - if (options.traditional || options.code.progsversion == PROG_ID_VERSION) { - return_symbol = make_symbol (".return", &type_param, pr.symtab->space, - sc_extern); - return_def = return_symbol->s.def; - return_opcode = ""; + s = new_statement (st_func, "return", 0); + if (options.code.progsversion == PROG_VERSION) { + s->opa = short_operand (0, 0); + s->opb = short_operand (0, 0); + s->opc = short_operand (-1, 0); + } else { + if (options.traditional + || options.code.progsversion == PROG_ID_VERSION) { + s->opa = return_operand (&type_void, 0); + } } - if (return_symbol) - return_operand = def_operand (return_def, &type_void); - s = new_statement (st_func, return_opcode, 0); - s->opa = return_operand; sblock_add_statement (sblock, s); } @@ -1577,6 +2632,7 @@ make_statements (expr_t *e) sblock_t *sblock = new_sblock (); int did_something; int pass = 0; + class_t *class; if (options.block_dot.expr) dump_dot ("expr", e, dump_dot_expr); @@ -1586,14 +2642,24 @@ make_statements (expr_t *e) do { did_something = thread_jumps (sblock); if (options.block_dot.thread) - dump_dot (va ("thread-%d", pass), sblock, dump_dot_sblock); + dump_dot (va (0, "thread-%d", pass), sblock, dump_dot_sblock); did_something |= remove_dead_blocks (sblock); sblock = merge_blocks (sblock); if (options.block_dot.dead) - dump_dot (va ("dead-%d", pass), sblock, dump_dot_sblock); + dump_dot (va (0, "dead-%d", pass), sblock, dump_dot_sblock); pass++; } while (did_something); check_final_block (sblock); + if (current_class && (class = extract_class (current_class))) { + // If the class is a root class, then it is not possible for there + // to be a call to [super dealloc] so do not check. However, any + // derived class implementeing -dealloc must call [super dealloc] + // (or some other deallocator (FIXME: later)) + // FIXME better check for dealloc? + if (class->super_class && !strcmp (current_func->name, "-dealloc")) { + search_for_super_dealloc (sblock); + } + } if (options.block_dot.final) dump_dot ("final", sblock, dump_dot_sblock); @@ -1606,9 +2672,9 @@ count_temp (operand_t *op) if (!op) return; if (op->op_type == op_temp) { - while (op->o.tempop.alias) - op = op->o.tempop.alias; - op->o.tempop.users++; + while (op->tempop.alias) + op = op->tempop.alias; + op->tempop.users++; } } diff --git a/tools/qfcc/source/strpool.c b/tools/qfcc/source/strpool.c index 8b32ae71e..42d7ba6d6 100644 --- a/tools/qfcc/source/strpool.c +++ b/tools/qfcc/source/strpool.c @@ -39,13 +39,15 @@ #endif #include #include +#include #include "QF/dstring.h" #include "QF/hash.h" +#include "QF/msg.h" -#include "diagnostic.h" -#include "options.h" -#include "strpool.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/strpool.h" static hashtab_t *saved_strings; @@ -63,7 +65,7 @@ strpool_new (void) { strpool_t *strpool = calloc (1, sizeof (strpool_t)); - strpool->str_tab = Hash_NewTable (16381, strpool_get_key, 0, strpool); + strpool->str_tab = Hash_NewTable (16381, strpool_get_key, 0, strpool, 0); strpool->size = 1; strpool->max_size = 16384; strpool->strings = malloc (strpool->max_size); @@ -77,7 +79,7 @@ strpool_build (const char *strings, int size) intptr_t s; strpool_t *strpool = malloc (sizeof (strpool_t)); - strpool->str_tab = Hash_NewTable (16381, strpool_get_key, 0, strpool); + strpool->str_tab = Hash_NewTable (16381, strpool_get_key, 0, strpool, 0); strpool->size = size + (*strings != 0); strpool->max_size = (strpool->size + 16383) & ~16383; strpool->strings = malloc (strpool->max_size); @@ -120,6 +122,14 @@ strpool_addstr (strpool_t *strpool, const char *str) return s; } +int +strpool_findstr (strpool_t *strpool, const char *str) +{ + if (!str) + return 0; + return (intptr_t) Hash_Find (strpool->str_tab, str); +} + static const char * ss_get_key (const void *s, void *unused) { @@ -131,7 +141,7 @@ save_string (const char *str) { char *s; if (!saved_strings) - saved_strings = Hash_NewTable (16381, ss_get_key, 0, 0); + saved_strings = Hash_NewTable (16381, ss_get_key, 0, 0, 0); s = Hash_Find (saved_strings, str); if (s) return s; @@ -140,14 +150,28 @@ save_string (const char *str) return s; } +const char * +save_cwd (void) +{ + char *cwd = getcwd (0, 0); + const char *str = save_string (cwd); + free (cwd); + return str; +} + const char * make_string (char *token, char **end) { - char s[2]; + char s[7]; // utf8 needs 6 + nul + sizebuf_t utf8str = { + .maxsize = sizeof (s), + .data = (byte *) s, + }; int c; int i; int mask; int boldnext; + int unicount; int quote; static dstring_t *str; @@ -159,6 +183,7 @@ make_string (char *token, char **end) mask = 0x00; boldnext = 0; + unicount = 0; quote = *token++; do { @@ -188,6 +213,10 @@ make_string (char *token, char **end) boldnext = 0; c = '\'' ^ mask; break; + case '?': + boldnext = 0; + c = '?' ^ mask; + break; case '0': case '1': case '2': @@ -230,7 +259,33 @@ make_string (char *token, char **end) } if (!*token) error (0, "EOF inside quote"); - c ^= mask; + c ^= mask; // cancel mask below + break; + case 'U': + unicount += 4; + case 'u': + unicount += 4; + boldnext = 0; + c = 0; + while (unicount && *token + && isxdigit ((unsigned char)*token)) { + c *= 16; + if (*token <= '9') + c += *token - '0'; + else if (*token <= 'F') + c += *token - 'A' + 10; + else + c += *token - 'a' + 10; + token++; + --unicount; + } + if (!*token) { + error (0, "EOF inside quote"); + } else if (unicount) { + error (0, "incomplete unicode sequence: %x %d", c, unicount); + } + unicount = 1; // signal need to encode to utf8 + c ^= mask; // cancel mask below break; case 'a': boldnext = 0; @@ -349,7 +404,15 @@ make_string (char *token, char **end) c = c ^ 0x80; boldnext = 0; c = c ^ mask; - s[0] = c; + if (unicount) { + SZ_Clear (&utf8str); + MSG_WriteUTF8 (&utf8str, c); + MSG_WriteByte (&utf8str, 0); // nul-terminate string + unicount = 0; + } else { + s[0] = c; + s[1] = 0; + } dstring_appendstr (str, s); } while (1); diff --git a/tools/qfcc/source/struct.c b/tools/qfcc/source/struct.c index 4cb81fa74..80c14fb01 100644 --- a/tools/qfcc/source/struct.c +++ b/tools/qfcc/source/struct.c @@ -43,24 +43,26 @@ #include #include -#include #include #include -#include "def.h" -#include "defspace.h" -#include "diagnostic.h" -#include "emit.h" -#include "expr.h" -#include "obj_type.h" -#include "qfcc.h" -#include "reloc.h" -#include "shared.h" -#include "strpool.h" -#include "struct.h" -#include "symtab.h" -#include "type.h" -#include "value.h" +#include + +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/emit.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/obj_type.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/shared.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/struct.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" static symbol_t * find_tag (ty_meta_e meta, symbol_t *tag, type_t *type) @@ -69,13 +71,13 @@ find_tag (ty_meta_e meta, symbol_t *tag, type_t *type) symbol_t *sym; if (tag) { - tag_name = va ("tag %s", tag->name); + tag_name = va (0, "tag %s", tag->name); } else { const char *path = GETSTR (pr.source_file); const char *file = strrchr (path, '/'); if (!file++) file = path; - tag_name = va ("tag .%s.%d", file, pr.source_line); + tag_name = va (0, "tag .%s.%d", file, pr.source_line); } sym = symtab_lookup (current_symtab, tag_name); if (sym) { @@ -96,6 +98,45 @@ find_tag (ty_meta_e meta, symbol_t *tag, type_t *type) return sym; } +symtab_t * +start_struct (int *su, symbol_t *tag, symtab_t *parent) +{ + symbol_t *sym; + sym = find_struct (*su, tag, 0); + if (!sym->table) { + symtab_addsymbol (parent, sym); + } else { + if (!sym->type) { + internal_error (0, "broken structure symbol?"); + } + if (tag) { + tag->type = sym->type; + } + if (sym->type->meta == ty_enum + || (sym->type->meta == ty_struct && sym->type->t.symtab)) { + error (0, "%s %s redefined", + *su == 's' ? "struct" : "union", tag->name); + *su = 0; + } else if (sym->type->meta != ty_struct) { + internal_error (0, "%s is not a struct or union", + tag->name); + } + } + return new_symtab (parent, stab_struct); +} + +symbol_t * +find_handle (symbol_t *tag, type_t *type) +{ + symbol_t *sym = find_tag (ty_handle, tag, type); + if (sym->type->type == ev_invalid) { + sym->type->type = ev_func; + sym->type->width = 1; + sym->type->alignment = 1; + } + return sym; +} + symbol_t * find_struct (int su, symbol_t *tag, type_t *type) { @@ -108,10 +149,12 @@ find_struct (int su, symbol_t *tag, type_t *type) } symbol_t * -build_struct (int su, symbol_t *tag, symtab_t *symtab, type_t *type) +build_struct (int su, symbol_t *tag, symtab_t *symtab, type_t *type, int base) { symbol_t *sym = find_struct (su, tag, type); symbol_t *s; + int alignment = 1; + symbol_t *as; symtab->parent = 0; // disconnect struct's symtab from parent scope @@ -122,7 +165,12 @@ build_struct (int su, symbol_t *tag, symtab_t *symtab, type_t *type) for (s = symtab->symbols; s; s = s->next) { if (s->sy_type != sy_var) continue; + if (is_class (s->type)) { + error (0, "statically allocated instance of class %s", + s->type->t.class->name); + } if (su == 's') { + symtab->size = RUP (symtab->size + base, s->type->alignment) - base; s->s.offset = symtab->size; symtab->size += type_size (s->type); } else { @@ -130,12 +178,45 @@ build_struct (int su, symbol_t *tag, symtab_t *symtab, type_t *type) if (size > symtab->size) symtab->size = size; } + if (s->type->alignment > alignment) { + alignment = s->type->alignment; + } + if (s->visibility == vis_anonymous) { + symtab_t *anonymous; + symbol_t *t = s->next; + int offset = s->s.offset; + + if (!is_struct (s->type) && !is_union (s->type)) { + internal_error (0, "non-struct/union anonymous field"); + } + anonymous = s->type->t.symtab; + for (as = anonymous->symbols; as; as = as->next) { + if (as->visibility == vis_anonymous || as->sy_type!= sy_var) { + continue; + } + if (Hash_Find (symtab->tab, as->name)) { + error (0, "ambiguous field `%s' in anonymous %s", + as->name, su == 's' ? "struct" : "union"); + } else { + s->next = copy_symbol (as); + s = s->next; + s->s.offset += offset; + s->table = symtab; + s->no_auto_init = 1; + Hash_Add (symtab->tab, s); + } + } + s->next = t; + } } if (!type) sym->type = find_type (sym->type); // checks the tag, not the symtab sym->type->t.symtab = symtab; + if (alignment > sym->type->alignment) { + sym->type->alignment = alignment; + } if (!type && sym->type->type_def->external) //FIXME should not be necessary - sym->type->type_def = qfo_encode_type (sym->type); + sym->type->type_def = qfo_encode_type (sym->type, pr.type_data); return sym; } @@ -152,49 +233,61 @@ start_enum (symbol_t *sym) error (0, "%s defined as wrong kind of tag", sym->name); sym = find_enum (0); } - sym->type->t.symtab = new_symtab (current_symtab, stab_local); + sym->type->t.symtab = new_symtab (current_symtab, stab_enum); + sym->type->alignment = 1; + sym->type->width = 1; return sym->type->t.symtab; } symbol_t * finish_enum (symbol_t *sym) { - sym->type = find_type (sym->type); + symbol_t *enum_sym; + symbol_t *name; + type_t *enum_type; + symtab_t *enum_tab; + + enum_type = sym->type = find_type (sym->type); + enum_tab = enum_type->t.symtab; + + for (name = enum_tab->symbols; name; name = name->next) { + name->type = sym->type; + + enum_sym = new_symbol_type (name->name, enum_type); + enum_sym->sy_type = sy_const; + enum_sym->s.value = name->s.value; + symtab_addsymbol (enum_tab->parent, enum_sym); + } return sym; } void add_enum (symbol_t *enm, symbol_t *name, expr_t *val) { - symbol_t *sym; type_t *enum_type = enm->type; - symtab_t *enum_tab; + symtab_t *enum_tab = enum_type->t.symtab; int value; - if (name->table == current_symtab) + if (name->table == current_symtab || name->table == enum_tab) error (0, "%s redefined", name->name); if (name->table) name = new_symbol (name->name); name->sy_type = sy_const; name->type = enum_type; - enum_tab = enum_type->t.symtab; value = 0; if (enum_tab->symbols) - value = ((symbol_t *)(enum_tab->symtail))->s.value->v.integer_val + 1; + value = ((symbol_t *)(enum_tab->symtail))->s.value->v.int_val + 1; if (val) { + convert_name (val); if (!is_constant (val)) error (val, "non-constant initializer"); - else if (!is_integer_val (val)) + else if (!is_int_val (val)) error (val, "invalid initializer type"); else - value = expr_integer (val); + value = expr_int (val); } - name->s.value = new_integer_val (value); + name->s.value = new_int_val (value); symtab_addsymbol (enum_tab, name); - sym = new_symbol_type (name->name, name->type); - sym->sy_type = sy_const; - sym->s.value = name->s.value; - symtab_addsymbol (enum_tab->parent, sym); } int @@ -211,12 +304,12 @@ enum_as_bool (type_t *enm, expr_t **zero, expr_t **one) for (sym = symtab->symbols; sym; sym = sym->next) { if (sym->sy_type != sy_const) continue; - val = sym->s.value->v.integer_val; + val = sym->s.value->v.int_val; if (!val) { zero_sym = sym; } else { if (one_sym) { - v = one_sym->s.value->v.integer_val; + v = one_sym->s.value->v.int_val; if (val * val > v * v) continue; } @@ -246,17 +339,18 @@ make_structure (const char *name, int su, struct_def_t *defs, type_t *type) strct = new_symtab (0, stab_struct); while (defs->name) { field = new_symbol_type (defs->name, defs->type); + field->sy_type = sy_var; if (!symtab_addsymbol (strct, field)) internal_error (0, "duplicate symbol: %s", defs->name); defs++; } - sym = build_struct (su, sym, strct, type); + sym = build_struct (su, sym, strct, type, 0); return sym; } def_t * emit_structure (const char *name, int su, struct_def_t *defs, type_t *type, - void *data, storage_class_t storage) + void *data, defspace_t *space, storage_class_t storage) { int i, j; int saw_null = 0; @@ -269,8 +363,7 @@ emit_structure (const char *name, int su, struct_def_t *defs, type_t *type, name = save_string (name); if (!type) type = make_structure (0, su, defs, 0)->type; - if (!is_struct (type) || (su == 's' && type->meta != ty_struct) - || (su == 'u' && type->meta != ty_union)) + if ((su == 's' && !is_struct (type)) || (su == 'u' && !is_union (type))) internal_error (0, "structure %s type mismatch", name); for (i = 0, field_sym = type->t.symtab->symbols; field_sym; i++, field_sym = field_sym->next) { @@ -291,7 +384,10 @@ emit_structure (const char *name, int su, struct_def_t *defs, type_t *type, if (storage != sc_global && storage != sc_static) internal_error (0, "structure %s must be global or static", name); - struct_sym = make_symbol (name, type, pr.far_data, storage); + if (!space) { + space = pr.far_data; + } + struct_sym = make_symbol (name, type, space, storage); struct_def = struct_sym->s.def; if (struct_def->initialized) @@ -302,13 +398,13 @@ emit_structure (const char *name, int su, struct_def_t *defs, type_t *type, for (i = 0, field_sym = type->t.symtab->symbols; field_sym; i++, field_sym = field_sym->next) { field_def.type = field_sym->type; - field_def.name = save_string (va ("%s.%s", name, field_sym->name)); + field_def.name = save_string (va (0, "%s.%s", name, field_sym->name)); field_def.space = struct_def->space; field_def.offset = struct_def->offset + field_sym->s.offset; if (!defs[i].emit) { //FIXME relocs? arrays? structs? pr_type_t *val = (pr_type_t *) data; - memcpy (D_POINTER (void, &field_def), val, + memcpy (D_POINTER (pr_type_t, &field_def), val, type_size (field_def.type) * sizeof (pr_type_t)); data = &val[type_size (field_def.type)]; } else { diff --git a/tools/qfcc/source/stub.c b/tools/qfcc/source/stub.c index af6e70131..ac57d9b23 100644 --- a/tools/qfcc/source/stub.c +++ b/tools/qfcc/source/stub.c @@ -2,32 +2,56 @@ # include "config.h" #endif -#include "class.h" -#include "codespace.h" -#include "diagnostic.h" -#include "debug.h" -#include "def.h" -#include "defspace.h" -#include "emit.h" -#include "expr.h" -#include "function.h" -#include "obj_file.h" -#include "options.h" -#include "qfcc.h" -#include "strpool.h" -#include "type.h" -#include "value.h" +#include + +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/codespace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/dot.h" +#include "tools/qfcc/include/debug.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/emit.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/obj_file.h" +#include "tools/qfcc/include/obj_type.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/struct.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" struct dstring_s; options_t options; int num_linenos; pr_lineno_t *linenos; pr_info_t pr; -string_t ReuseString (const char *str) {return 0;} -void encode_type (struct dstring_s *str, const type_t *type) {} -codespace_t *codespace_new (void) {return 0;} +type_t type_Class; +type_t type_SEL; +type_t type_id; +__attribute__((const)) pr_string_t ReuseString (const char *str) {return 0;} +__attribute__((const)) codespace_t *codespace_new (void) {return 0;} void codespace_addcode (codespace_t *codespace, struct dstatement_s *code, int size) {} -int function_parms (function_t *f, byte *parm_size) {return 0;} +__attribute__((const)) int function_parms (function_t *f, byte *parm_size) {return 0;} void def_to_ddef (def_t *def, ddef_t *ddef, int aux) {} -expr_t *warning (expr_t *e, const char *fmt, ...) {return 0;} -expr_t *error (expr_t *e, const char *fmt, ...) {return 0;} +__attribute__((noreturn)) void _internal_error (const expr_t *e, const char *file, int line, const char *fmt, ...) {abort();} +__attribute__((const)) expr_t *_warning (expr_t *e, const char *file, int line, const char *fmt, ...) {return 0;} +__attribute__((const)) expr_t *_error (expr_t *e, const char *file, int line, const char *fmt, ...) {return 0;} +__attribute__((const)) symbol_t *make_structure (const char *name, int su, struct_def_t *defs, type_t *type) {return 0;} +__attribute__((const)) symbol_t *symtab_addsymbol (symtab_t *symtab, symbol_t *symbol) {return 0;} +__attribute__((const)) symbol_t *new_symbol_type (const char *name, type_t *type) {return 0;} +__attribute__((const)) def_t *qfo_encode_type (type_t *type, defspace_t *space) {return 0;} +__attribute__((const)) int obj_types_assignable (const type_t *dst, const type_t *src) {return 0;} +void print_protocollist (struct dstring_s *dstr, protocollist_t *protocollist) {} +int is_id (const type_t *type){return type->type;} +int is_SEL (const type_t *type){return 0;} +int is_Class (const type_t *type){return 0;} +int compare_protocols (protocollist_t *protos1, protocollist_t *protos2){return protos1->count - protos2->count;} +void dump_dot (const char *stage, void *data, + void (*dump_func) (void *data, const char *fname)){} +void dump_dot_type (void *_t, const char *filename){} +char *fubar; +const char *file_basename(const char *p, int keepdot) { return fubar;} diff --git a/tools/qfcc/source/switch.c b/tools/qfcc/source/switch.c index 80321a9ab..f275af673 100644 --- a/tools/qfcc/source/switch.c +++ b/tools/qfcc/source/switch.c @@ -42,18 +42,19 @@ #include #include -#include "def.h" -#include "diagnostic.h" -#include "expr.h" -#include "opcodes.h" -#include "options.h" -#include "qfcc.h" -#include "reloc.h" -#include "switch.h" -#include "symtab.h" -#include "type.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/opcodes.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/shared.h" +#include "tools/qfcc/include/switch.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" -#include "qc-parse.h" +#include "tools/qfcc/source/qc-parse.h" typedef struct case_node_s { expr_t *low; @@ -63,7 +64,7 @@ typedef struct case_node_s { struct case_node_s *left, *right; } case_node_t; -static ex_value_t * +static __attribute__((pure)) ex_value_t * get_value (expr_t *e) { if (e->type == ex_symbol) @@ -73,7 +74,7 @@ get_value (expr_t *e) return e->e.value; } -static uintptr_t +static __attribute__((pure)) uintptr_t get_hash (const void *_cl, void *unused) { case_label_t *cl = (case_label_t *) _cl; @@ -82,7 +83,7 @@ get_hash (const void *_cl, void *unused) if (!cl->value) return 0; val = get_value (cl->value); - return Hash_Buffer (&val->v, sizeof (val->v)) + val->type; + return Hash_Buffer (&val->v, sizeof (val->v)) + val->lltype; } static int @@ -136,14 +137,14 @@ case_label_expr (switch_block_t *switch_block, expr_t *value) if (!type_assignable (type, get_type (value))) return error (value, "type mismatch in case label"); if (is_integral (type) && is_integral (val_type)) { - value = new_integer_expr (expr_integer (value)); + value = new_int_expr (expr_int (value)); debug (value, "integeral label used in integral switch"); } else if (is_integral (type) && is_float (val_type)) { warning (value, "float label used in integral switch"); - value = new_integer_expr (expr_float (value)); + value = new_int_expr (expr_float (value)); } else if (is_float (type) && is_integral (val_type)) { debug (value, "integeral label used in float switch"); - value = new_float_expr (expr_integer (value)); + value = new_float_expr (expr_int (value)); } else if (is_float (type) && is_float (val_type)) { value = new_float_expr (expr_float (value)); debug (value, "float label used in float switch"); @@ -166,13 +167,13 @@ new_switch_block (void) switch_block_t *switch_block = malloc (sizeof (switch_block_t)); SYS_CHECKMEM (switch_block); - switch_block->labels = Hash_NewTable (127, 0, 0, 0); + switch_block->labels = Hash_NewTable (127, 0, 0, 0, 0); Hash_SetHashCompare (switch_block->labels, get_hash, compare); switch_block->test = 0; return switch_block; } -static int +static __attribute__((pure)) int label_compare (const void *_a, const void *_b) { const case_label_t **a = (const case_label_t **) _a; @@ -189,8 +190,8 @@ label_compare (const void *_a, const void *_b) return strcmp (s1, s2); } else if (is_float_val ((*a)->value)) { return expr_float ((*a)->value) - expr_float ((*b)->value); - } else if (is_integer_val ((*a)->value)) { - return expr_integer ((*a)->value) - expr_integer ((*b)->value); + } else if (is_int_val ((*a)->value)) { + return expr_int ((*a)->value) - expr_int ((*b)->value); } internal_error (0, "in switch"); } @@ -210,10 +211,10 @@ new_case_node (expr_t *low, expr_t *high) } else { int size; - if (!is_integer_val (low)) + if (!is_int_val (low)) internal_error (low, "switch"); - size = expr_integer (high) - expr_integer (low) + 1; - node->labels = calloc (size, sizeof (case_node_t *)); + size = expr_int (high) - expr_int (low) + 1; + node->labels = calloc (size, sizeof (expr_t *)); } node->left = node->right = 0; return node; @@ -253,18 +254,18 @@ build_case_tree (case_label_t **labels, int count, int range) if (!nodes) Sys_Error ("out of memory"); - if (range && is_integer_val (labels[0]->value)) { + if (range && is_int_val (labels[0]->value)) { for (i = 0; i < count - 1; i = j, num_nodes++) { for (j = i + 1; j < count; j++) { - if (expr_integer (labels[j]->value) - - expr_integer (labels[j - 1]->value) > 1) + if (expr_int (labels[j]->value) + - expr_int (labels[j - 1]->value) > 1) break; } nodes[num_nodes] = new_case_node (labels[i]->value, labels[j - 1]->value); for (k = i; k < j; k++) - nodes[num_nodes]->labels[expr_integer (labels[k]->value) - - expr_integer (labels[i]->value)] + nodes[num_nodes]->labels[expr_int (labels[k]->value) + - expr_int (labels[i]->value)] = labels[k]->label; } if (i < count) { @@ -311,12 +312,12 @@ build_switch (expr_t *sw, case_node_t *tree, int op, expr_t *sw_val, append_expr (sw, test); if (tree->low == tree->high) { - branch = branch_expr ('n', new_alias_expr (&type_integer, temp), + branch = branch_expr (EQ, new_alias_expr (&type_int, temp), tree->labels[0]); append_expr (sw, branch); if (tree->left) { - branch = branch_expr (IFA, new_alias_expr (&type_integer, temp), + branch = branch_expr (GT, new_alias_expr (&type_int, temp), high_label); append_expr (sw, branch); @@ -329,36 +330,38 @@ build_switch (expr_t *sw, case_node_t *tree, int op, expr_t *sw_val, build_switch (sw, tree->right, op, sw_val, temp, default_label); } } else { - int low = expr_integer (tree->low); - int high = expr_integer (tree->high); + int low = expr_int (tree->low); + int high = expr_int (tree->high); symbol_t *table_sym; expr_t *table_expr; expr_t *table_init; const char *table_name = new_label_name (); int i; expr_t *range = binary_expr ('-', tree->high, tree->low); + expr_t *label; - range = fold_constants (range); - - table_init = new_block_expr (); + table_init = new_compound_init (); for (i = 0; i <= high - low; i++) { tree->labels[i]->e.label.used++; - append_expr (table_init, address_expr (tree->labels[i], 0, 0)); + label = address_expr (tree->labels[i], 0); + append_element (table_init, new_element (label, 0)); } - table_sym = new_symbol (table_name); - initialize_def (table_sym, array_type (&type_integer, high - low + 1), - table_init, pr.near_data, sc_static); + table_sym = new_symbol_type (table_name, + array_type (&type_int, + high - low + 1)); + initialize_def (table_sym, table_init, pr.near_data, sc_static, + current_symtab); table_expr = new_symbol_expr (table_sym); if (tree->left) { - branch = branch_expr (IFB, temp, low_label); + branch = branch_expr (LT, temp, low_label); append_expr (sw, branch); } - test = binary_expr (GT, cast_expr (&type_uinteger, temp), - cast_expr (&type_uinteger, range)); - branch = branch_expr ('i', test, high_label); + test = binary_expr (GT, cast_expr (&type_uint, temp), + cast_expr (&type_uint, range)); + branch = branch_expr (NE, test, high_label); append_expr (sw, branch); - branch = new_binary_expr ('g', table_expr, temp); + branch = jump_table_expr (table_expr, temp); append_expr (sw, branch); debug (sw, "switch using jump table"); if (tree->left) { @@ -381,7 +384,7 @@ check_enum_switch (switch_block_t *switch_block) for (enum_val = type->t.symtab->symbols; enum_val; enum_val = enum_val->next) { - cl.value = new_integer_expr (enum_val->s.value->v.integer_val); + cl.value = new_int_expr (enum_val->s.value->v.int_val); if (!Hash_FindElement (switch_block->labels, &cl)) { warning (switch_block->test, "enumeration value `%s' not handled in switch", @@ -394,6 +397,10 @@ struct expr_s * switch_expr (switch_block_t *switch_block, expr_t *break_label, expr_t *statements) { + if (switch_block->test->type == ex_error) { + return switch_block->test; + } + case_label_t **labels, **l; case_label_t _default_label; case_label_t *default_label = &_default_label; @@ -403,11 +410,7 @@ switch_expr (switch_block_t *switch_block, expr_t *break_label, expr_t *default_expr; int num_labels = 0; int saved_line = pr.source_line; - string_t saved_file = pr.source_file; - - if (switch_block->test->type == ex_error) { - return switch_block->test; - } + pr_string_t saved_file = pr.source_file; pr.source_line = sw_val->line = switch_block->test->line; pr.source_file = sw_val->file = switch_block->test->file; @@ -428,12 +431,11 @@ switch_expr (switch_block_t *switch_block, expr_t *break_label, for (l = labels; *l; l++) num_labels++; if (options.code.progsversion == PROG_ID_VERSION - || (type != &type_string - && type != &type_float && !is_integral (type)) + || (!is_string(type) && !is_float(type) && !is_integral (type)) || num_labels < 8) { for (l = labels; *l; l++) { expr_t *cmp = binary_expr (EQ, sw_val, (*l)->value); - expr_t *test = branch_expr ('i', test_expr (cmp), + expr_t *test = branch_expr (NE, test_expr (cmp), (*l)->label); append_expr (sw, test); @@ -445,8 +447,8 @@ switch_expr (switch_block_t *switch_block, expr_t *break_label, int op; case_node_t *case_tree; - if (type == &type_string) - temp = new_temp_def_expr (&type_integer); + if (is_string(type)) + temp = new_temp_def_expr (&type_int); else temp = new_temp_def_expr (type); case_tree = build_case_tree (labels, num_labels, is_integral (type)); diff --git a/tools/qfcc/source/symtab.c b/tools/qfcc/source/symtab.c index 3e18cf615..47746710f 100644 --- a/tools/qfcc/source/symtab.c +++ b/tools/qfcc/source/symtab.c @@ -37,27 +37,30 @@ #include "QF/alloc.h" #include "QF/hash.h" -#include "class.h" -#include "def.h" -#include "defspace.h" -#include "diagnostic.h" -#include "function.h" -#include "qfcc.h" -#include "reloc.h" -#include "strpool.h" -#include "symtab.h" -#include "type.h" +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/shared.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" -static symtab_t *symtabs_freelist; -static symbol_t *symbols_freelist; +ALLOC_STATE (symtab_t, symtabs); +ALLOC_STATE (symbol_t, symbols); -static const char *sy_type_names[] = { +static const char * const sy_type_names[] = { + "sy_name", "sy_var", "sy_const", "sy_type", "sy_expr", "sy_func", "sy_class", + "sy_convert", }; const char * @@ -73,7 +76,9 @@ new_symbol (const char *name) { symbol_t *symbol; ALLOC (256, symbol_t, symbols, symbol); - symbol->name = save_string (name); + if (name) { + symbol->name = save_string (name); + } return symbol; } @@ -104,7 +109,7 @@ new_symtab (symtab_t *parent, stab_type_e type) symtab->type = type; if (symtab->type == stab_global) tabsize = 1023; - symtab->tab = Hash_NewTable (tabsize, sym_getkey, 0, 0); + symtab->tab = Hash_NewTable (tabsize, sym_getkey, 0, 0, 0); symtab->symtail = &symtab->symbols; return symtab; } @@ -151,7 +156,7 @@ symtab_removesymbol (symtab_t *symtab, symbol_t *symbol) for (s = &symtab->symbols; *s && *s != symbol; s = & (*s)->next) ; if (!*s) - internal_error (0, "symtab_removesymbol"); + internal_error (0, "attempt to remove symbol not in symtab"); *s = (*s)->next; if (symtab->symtail == &symbol->next) symtab->symtail = s; @@ -164,6 +169,7 @@ symbol_t * copy_symbol (symbol_t *symbol) { symbol_t *sym = new_symbol (symbol->name); + sym->visibility = symbol->visibility; sym->type = symbol->type; sym->params = copy_params (symbol->params); sym->sy_type = symbol->sy_type; @@ -172,16 +178,17 @@ copy_symbol (symbol_t *symbol) } symtab_t * -symtab_flat_copy (symtab_t *symtab, symtab_t *parent) +symtab_flat_copy (symtab_t *symtab, symtab_t *parent, stab_type_e type) { symtab_t *newtab; symbol_t *newsym; symbol_t *symbol; - newtab = new_symtab (parent, stab_local); + newtab = new_symtab (parent, type); do { for (symbol = symtab->symbols; symbol; symbol = symbol->next) { - if (Hash_Find (newtab->tab, symbol->name)) + if (symbol->visibility == vis_anonymous + || Hash_Find (newtab->tab, symbol->name)) continue; newsym = copy_symbol (symbol); symtab_addsymbol (newtab, newsym); @@ -234,5 +241,74 @@ make_symbol (const char *name, type_t *type, defspace_t *space, sym->s.def = new_def (name, type, space, storage); reloc_attach_relocs (relocs, &sym->s.def->relocs); } + sym->sy_type = sy_var; return sym; } + +symbol_t * +declare_symbol (specifier_t spec, expr_t *init, symtab_t *symtab) +{ + symbol_t *s = spec.sym; + defspace_t *space = symtab->space; + + if (s->table) { + // due to the way declarations work, we need a new symbol at all times. + // redelcarations will be checked later + s = new_symbol (s->name); + } + + spec = default_type (spec, s); + if (!spec.storage) { + spec.storage = current_storage; + } + if (spec.storage == sc_static) { + space = pr.near_data; + } + + //FIXME is_function is bad (this whole implementation of handling + //function prototypes is bad) + s->type = append_type (spec.sym->type, spec.type); + if (spec.is_function && is_func (s->type)) { + set_func_type_attrs (s->type, spec); + } + + if (spec.is_typedef) { + if (init) { + error (0, "typedef %s is initialized", s->name); + } + s->sy_type = sy_type; + s->type = find_type (s->type); + s->type = find_type (alias_type (s->type, s->type, s->name)); + symtab_addsymbol (symtab, s); + } else { + if (spec.is_function && is_func (s->type)) { + if (init) { + error (0, "function %s is initialized", s->name); + } + s->type = find_type (s->type); + s = function_symbol (s, spec.is_overload, 1); + } else { + s->type = find_type (s->type); + initialize_def (s, init, space, spec.storage, symtab); + if (s->s.def) { + s->s.def->nosave |= spec.nosave; + } + } + } + return s; +} + +symbol_t * +declare_field (specifier_t spec, symtab_t *symtab) +{ + symbol_t *s = spec.sym; + spec = default_type (spec, s); + s->type = find_type (append_type (s->type, spec.type)); + s->sy_type = sy_var; + s->visibility = current_visibility; + symtab_addsymbol (current_symtab, s); + if (!s->table) { + error (0, "duplicate field `%s'", s->name); + } + return s; +} diff --git a/tools/qfcc/source/type.c b/tools/qfcc/source/type.c index 586a7b1a0..06cdf68df 100644 --- a/tools/qfcc/source/type.c +++ b/tools/qfcc/source/type.c @@ -47,53 +47,126 @@ #include "QF/sys.h" #include "QF/va.h" -#include "class.h" -#include "def.h" -#include "diagnostic.h" -#include "expr.h" -#include "function.h" -#include "obj_type.h" -#include "options.h" -#include "qfcc.h" -#include "strpool.h" -#include "struct.h" -#include "symtab.h" -#include "type.h" +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/obj_type.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/struct.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" -// simple types. function types are dynamically allocated -type_t type_invalid = { ev_invalid, "invalid" }; -type_t type_void = { ev_void, "void" }; -type_t type_string = { ev_string, "string" }; -type_t type_float = { ev_float, "float" }; -type_t type_vector = { ev_vector, "vector" }; -type_t type_entity = { ev_entity, "entity" }; -type_t type_field = {ev_field, "field", ty_none, {{&type_void}} }; +#define EV_TYPE(t) \ + type_t type_##t = { \ + .type = ev_##t, \ + .name = #t, \ + .alignment = PR_ALIGNOF(t), \ + .width = __builtin_choose_expr (ev_##t == ev_short \ + || ev_##t == ev_ushort, 0, 1), \ + .meta = ty_basic, \ + {{ __builtin_choose_expr (ev_##t == ev_field \ + || ev_##t == ev_func \ + || ev_##t == ev_ptr, &type_void, 0 ) }}, \ + }; +#include "QF/progs/pr_type_names.h" -// type_function is a void() function used for state defs -type_t type_function = { ev_func, "function", ty_none, {{&type_void}} }; -type_t type_pointer = { ev_pointer, "pointer", ty_none, {{&type_void}} }; -type_t type_quaternion = { ev_quat, "quaternion" }; -type_t type_integer = { ev_integer, "integer" }; -type_t type_uinteger = { ev_uinteger, "uinteger" }; -type_t type_short = { ev_short, "short" }; +type_t type_invalid = { + .type = ev_invalid, + .name = "invalid", +}; +#define VEC_TYPE(type_name, base_type) \ + type_t type_##type_name = { \ + .type = ev_##base_type, \ + .name = #type_name, \ + .alignment = PR_ALIGNOF(type_name), \ + .width = PR_SIZEOF(type_name) / PR_SIZEOF (base_type), \ + .meta = ty_basic, \ + }; +#include "tools/qfcc/include/vec_types.h" + +#define VEC_TYPE(type_name, base_type) &type_##type_name, +static type_t *vec_types[] = { +#include "tools/qfcc/include/vec_types.h" + 0 +}; type_t *type_nil; type_t *type_default; +type_t *type_long_int; +type_t *type_ulong_uint; // these will be built up further -type_t type_va_list = { ev_invalid, 0, ty_struct }; -type_t type_param = { ev_invalid, 0, ty_struct }; -type_t type_zero = { ev_invalid, 0, ty_struct }; -type_t type_type_encodings = { ev_invalid, "@type_encodings", ty_struct }; +type_t type_va_list = { + .type = ev_invalid, + .meta = ty_struct, +}; +type_t type_param = { + .type = ev_invalid, + .meta = ty_struct, +}; +type_t type_zero = { + .type = ev_invalid, + .meta = ty_struct, +}; +type_t type_type_encodings = { + .type = ev_invalid, + .name = "@type_encodings", + .meta = ty_struct, +}; +type_t type_xdef = { + .type = ev_invalid, + .name = "@xdef", + .meta = ty_struct, +}; +type_t type_xdef_pointer = { + .type = ev_ptr, + .alignment = 1, + .width = 1, + .meta = ty_basic, + {{&type_xdef}}, +}; +type_t type_xdefs = { + .type = ev_invalid, + .name = "@xdefs", + .meta = ty_struct, +}; -type_t type_floatfield = { ev_field, ".float", ty_none, {{&type_float}} }; +type_t type_floatfield = { + .type = ev_field, + .name = ".float", + .alignment = 1, + .width = 1, + .meta = ty_basic, + {{&type_float}}, +}; -static type_t *types_freelist; +#define EV_TYPE(type) &type_##type, +type_t *ev_types[ev_type_count] = { +#include "QF/progs/pr_type_names.h" + &type_invalid, +}; + +int type_cast_map[ev_type_count] = { + [ev_int] = 0, + [ev_float] = 1, + [ev_long] = 2, + [ev_double] = 3, + [ev_uint] = 4, + //[ev_bool32] = 5, + [ev_ulong] = 6, + //[ev_bool64] = 7, +}; + +ALLOC_STATE (type_t, types); etype_t low_level_type (type_t *type) { - if (type->type >= ev_type_count) + if (type->type > ev_type_count) internal_error (0, "invalid type"); if (type->type == ev_type_count) internal_error (0, "found 'type count' type"); @@ -101,9 +174,7 @@ low_level_type (type_t *type) return type->type; if (is_enum (type)) return type_default->type; - if (is_struct (type)) - return ev_void; - if (is_array (type)) + if (is_structural (type)) return ev_void; internal_error (0, "invalid complex type"); } @@ -131,7 +202,7 @@ chain_type (type_t *type) if (!type->encoding) type->encoding = type_get_encoding (type); if (!type->type_def) - type->type_def = qfo_encode_type (type); + type->type_def = qfo_encode_type (type, pr.type_data); } type_t * @@ -160,13 +231,17 @@ free_type (type_t *type) case ev_vector: case ev_entity: case ev_type_count: - case ev_quat: - case ev_integer: - case ev_uinteger: + case ev_quaternion: + case ev_int: + case ev_uint: + case ev_long: + case ev_ulong: case ev_short: + case ev_ushort: + case ev_double: break; case ev_field: - case ev_pointer: + case ev_ptr: free_type (type->t.fldptr.type); break; case ev_func: @@ -181,44 +256,129 @@ free_type (type_t *type) FREE (types, type); } +static type_t * +copy_chain (type_t *type, type_t *append) +{ + type_t *new = 0; + type_t **n = &new; + + while (type) { + *n = new_type (); + **n = *type; + switch (type->meta) { + case ty_basic: + switch (type->type) { + case ev_void: + case ev_string: + case ev_float: + case ev_vector: + case ev_entity: + case ev_type_count: + case ev_quaternion: + case ev_int: + case ev_uint: + case ev_long: + case ev_ulong: + case ev_short: + case ev_ushort: + case ev_double: + internal_error (0, "copy basic type"); + case ev_field: + case ev_ptr: + n = &(*n)->t.fldptr.type; + type = type->t.fldptr.type; + break; + case ev_func: + n = &(*n)->t.func.type; + type = type->t.func.type; + break; + case ev_invalid: + internal_error (0, "invalid basic type"); + break; + } + break; + case ty_array: + n = &(*n)->t.array.type; + type = type->t.array.type; + break; + case ty_struct: + case ty_union: + case ty_enum: + case ty_class: + case ty_alias: //XXX is this correct? + case ty_handle: + internal_error (0, "copy object type %d", type->meta); + } + } + *n = append; + return new; +} + type_t * append_type (type_t *type, type_t *new) { type_t **t = &type; while (*t) { - switch ((*t)->type) { - case ev_void: - case ev_string: - case ev_float: - case ev_vector: - case ev_entity: - case ev_type_count: - case ev_quat: - case ev_integer: - case ev_uinteger: - case ev_short: - internal_error (0, "append to basic type"); - case ev_field: - case ev_pointer: - t = &(*t)->t.fldptr.type; + switch ((*t)->meta) { + case ty_basic: + switch ((*t)->type) { + case ev_void: + case ev_string: + case ev_float: + case ev_vector: + case ev_entity: + case ev_type_count: + case ev_quaternion: + case ev_int: + case ev_uint: + case ev_long: + case ev_ulong: + case ev_short: + case ev_ushort: + case ev_double: + internal_error (0, "append to basic type"); + case ev_field: + case ev_ptr: + t = &(*t)->t.fldptr.type; + type->alignment = 1; + type->width = 1; + break; + case ev_func: + t = &(*t)->t.func.type; + type->alignment = 1; + type->width = 1; + break; + case ev_invalid: + internal_error (0, "invalid basic type"); + break; + } break; - case ev_func: - t = &(*t)->t.func.type; - break; - case ev_invalid: - if ((*t)->meta == ty_array) - t = &(*t)->t.array.type; - else - internal_error (0, "append to object type"); + case ty_array: + t = &(*t)->t.array.type; + type->alignment = new->alignment; + type->width = new->width; break; + case ty_struct: + case ty_union: + case ty_enum: + case ty_class: + case ty_alias: //XXX is this correct? + case ty_handle: + internal_error (0, "append to object type"); } } - *t = new; + if (type && new->meta == ty_alias) { + type_t *chain = find_type (copy_chain (type, new)); + *t = new->t.alias.aux_type; + type = alias_type (type, chain, 0); + } else { + *t = new; + } return type; } -static int +static __attribute__((pure)) int types_same (type_t *a, type_t *b) { int i, count; @@ -226,15 +386,16 @@ types_same (type_t *a, type_t *b) if (a->type != b->type || a->meta != b->meta) return 0; switch (a->meta) { - case ty_none: + case ty_basic: switch (a->type) { case ev_field: - case ev_pointer: + case ev_ptr: if (a->t.fldptr.type != b->t.fldptr.type) return 0; case ev_func: if (a->t.func.type != b->t.func.type - || a->t.func.num_params != b->t.func.num_params) + || a->t.func.num_params != b->t.func.num_params + || a->t.func.attribute_bits != b->t.func.attribute_bits) return 0; count = a->t.func.num_params; if (count < 0) @@ -245,7 +406,7 @@ types_same (type_t *a, type_t *b) return 0; return 1; default: // other types don't have aux data - return 1; + return a->width == b->width; } break; case ty_struct: @@ -266,10 +427,94 @@ types_same (type_t *a, type_t *b) if (a->t.class != b->t.class) return 0; return compare_protocols (a->protos, b->protos); + case ty_alias: + // names have gone through save_string + return (a->name == b->name + && a->t.alias.aux_type == b->t.alias.aux_type + && a->t.alias.full_type == b->t.alias.full_type); + case ty_handle: + // names have gone through save_string + return a->name == b->name; } internal_error (0, "we be broke"); } +void +set_func_type_attrs (type_t *func, specifier_t spec) +{ + func->t.func.no_va_list = spec.no_va_list; + func->t.func.void_return = spec.void_return; +} + +specifier_t +default_type (specifier_t spec, symbol_t *sym) +{ + if (spec.type) { + if (is_float (spec.type) && !spec.multi_type) { + // no modifieres allowed + if (spec.is_unsigned) { + spec.multi_type = 1; + error (0, "both unsigned and float in declaration specifiers"); + } else if (spec.is_signed) { + spec.multi_type = 1; + error (0, "both signed and float in declaration specifiers"); + } else if (spec.is_short) { + spec.multi_type = 1; + error (0, "both short and float in declaration specifiers"); + } else if (spec.is_long) { + spec.multi_type = 1; + error (0, "both long and float in declaration specifiers"); + } + } + if (is_double (spec.type)) { + // long is allowed but ignored + if (spec.is_unsigned) { + spec.multi_type = 1; + error (0, "both unsigned and double in declaration specifiers"); + } else if (spec.is_signed) { + spec.multi_type = 1; + error (0, "both signed and double in declaration specifiers"); + } else if (spec.is_short) { + spec.multi_type = 1; + error (0, "both short and double in declaration specifiers"); + } + } + if (is_int (spec.type)) { + // signed and short are ignored + if (spec.is_unsigned && spec.is_long) { + spec.type = &type_ulong; + } else if (spec.is_long) { + spec.type = &type_long; + } + } + } else { + if (spec.is_long) { + if (spec.is_unsigned) { + spec.type = type_ulong_uint; + } else { + spec.type = type_long_int; + } + } else { + if (spec.is_unsigned) { + spec.type = &type_uint; + } else if (spec.is_signed || spec.is_short) { + spec.type = &type_int; + } + } + } + if (!spec.type) { + spec.type = type_default; + if (sym) { + warning (0, "type defaults to '%s' in declaration of '%s'", + type_default->name, sym->name); + } else { + warning (0, "type defaults to '%s' in abstract declaration", + type_default->name); + } + } + return spec; +} + /* find_type @@ -287,10 +532,10 @@ find_type (type_t *type) if (type->freeable) { switch (type->meta) { - case ty_none: + case ty_basic: switch (type->type) { case ev_field: - case ev_pointer: + case ev_ptr: type->t.fldptr.type = find_type (type->t.fldptr.type); break; case ev_func: @@ -315,6 +560,11 @@ find_type (type_t *type) break; case ty_class: break; + case ty_alias: + type->t.alias.aux_type = find_type (type->t.alias.aux_type); + break; + case ty_handle: + break; } } @@ -326,6 +576,20 @@ find_type (type_t *type) // allocate a new one check = new_type (); *check = *type; + if (is_func (type)) { + check->t.func.param_types = 0; + const type_t *t = unalias_type (type); + int num_params = t->t.func.num_params; + if (num_params < 0) { + num_params = ~num_params; + } + if (num_params) { + check->t.func.param_types = malloc (sizeof (type_t *) * num_params); + for (int i = 0; i < num_params; i++) { + check->t.func.param_types[i] = t->t.func.param_types[i]; + } + } + } check->freeable = 0; chain_type (check); @@ -344,9 +608,11 @@ field_type (type_t *aux) else new = new_type (); new->type = ev_field; - new->t.fldptr.type = aux; - if (aux) - new = find_type (new); + new->alignment = 1; + new->width = 1; + if (aux) { + new = find_type (append_type (new, aux)); + } return new; } @@ -360,13 +626,87 @@ pointer_type (type_t *aux) memset (&_new, 0, sizeof (_new)); else new = new_type (); - new->type = ev_pointer; - new->t.fldptr.type = aux; - if (aux) - new = find_type (new); + new->type = ev_ptr; + new->alignment = 1; + new->width = 1; + if (aux) { + new = find_type (append_type (new, aux)); + } return new; } +type_t * +vector_type (const type_t *ele_type, int width) +{ + if (width == 1) { + for (type_t **t = ev_types; t - ev_types < ev_type_count; t++) { + if ((*t)->type == ele_type->type && (*t)->width == 1) { + return *t; + } + } + } + for (type_t **vtype = vec_types; *vtype; vtype++) { + if ((*vtype)->type == ele_type->type + && (*vtype)->width == width) { + if (options.code.progsversion <= PROG_VERSION) { + if (*vtype == &type_vec3) { + return &type_vector; + } + if (*vtype == &type_vec4) { + return &type_quaternion; + } + } + return *vtype; + } + } + return 0; +} + +type_t * +base_type (const type_t *vec_type) +{ + if (!is_math (vec_type)) { + return 0; + } + // vec_type->type for quaternion and vector points back to itself + if (is_quaternion (vec_type) || is_vector (vec_type)) { + return &type_float; + } + return ev_types[vec_type->type]; +} + +type_t * +int_type (const type_t *base) +{ + int width = type_width (base); + base = base_type (base); + if (!base) { + return 0; + } + if (type_size (base) == 1) { + base = &type_int; + } else if (type_size (base) == 2) { + base = &type_long; + } + return vector_type (base, width); +} + +type_t * +float_type (const type_t *base) +{ + int width = type_width (base); + base = base_type (base); + if (!base) { + return 0; + } + if (type_size (base) == 1) { + base = &type_float; + } else if (type_size (base) == 2) { + base = &type_double; + } + return vector_type (base, width); +} + type_t * array_type (type_t *aux, int size) { @@ -377,12 +717,16 @@ array_type (type_t *aux, int size) memset (&_new, 0, sizeof (_new)); else new = new_type (); - new->type = ev_invalid; new->meta = ty_array; - new->t.array.type = aux; + new->type = ev_invalid; + if (aux) { + new->alignment = aux->alignment; + new->width = aux->width; + } new->t.array.size = size; - if (aux) - new = find_type (new); + if (aux) { + new = find_type (append_type (new, aux)); + } return new; } @@ -397,6 +741,10 @@ based_array_type (type_t *aux, int base, int top) else new = new_type (); new->type = ev_invalid; + if (aux) { + new->alignment = aux->alignment; + new->width = aux->width; + } new->meta = ty_array; new->t.array.type = aux; new->t.array.base = base; @@ -406,6 +754,56 @@ based_array_type (type_t *aux, int base, int top) return new; } +type_t * +alias_type (type_t *type, type_t *alias_chain, const char *name) +{ + type_t *alias = new_type (); + alias->meta = ty_alias; + alias->type = type->type; + alias->alignment = type->alignment; + alias->width = type->width; + if (type == alias_chain && type->meta == ty_alias) { + // typedef of a type that contains a typedef somewhere + // grab the alias-free branch for type + type = alias_chain->t.alias.aux_type; + if (!alias_chain->name) { + // the other typedef is further inside, so replace the unnamed + // alias node with the typedef + alias_chain = alias_chain->t.alias.full_type; + } + } + alias->t.alias.aux_type = type; + alias->t.alias.full_type = alias_chain; + if (name) { + alias->name = save_string (name); + } + return alias; +} + +const type_t * +unalias_type (const type_t *type) +{ + if (type->meta == ty_alias) { + type = type->t.alias.aux_type; + if (type->meta == ty_alias) { + internal_error (0, "alias type node in alias-free chain"); + } + } + return type; +} + +const type_t * +dereference_type (const type_t *type) +{ + if (!is_ptr (type) && !is_field (type)) { + internal_error (0, "dereference non pointer/field type"); + } + if (type->meta == ty_alias) { + type = type->t.alias.full_type; + } + return type->t.fldptr.type; +} + void print_type_str (dstring_t *str, const type_t *type) { @@ -413,79 +811,125 @@ print_type_str (dstring_t *str, const type_t *type) dasprintf (str, " (null)"); return; } - switch (type->type) { - case ev_field: - dasprintf (str, ".("); - print_type_str (str, type->t.fldptr.type); - dasprintf (str, ")"); - break; - case ev_func: - print_type_str (str, type->t.func.type); - if (type->t.func.num_params == -1) { - dasprintf (str, "(...)"); + switch (type->meta) { + case ty_handle: + dasprintf (str, " handle %s", type->name); + return; + case ty_alias: + dasprintf (str, "({%s=", type->name); + print_type_str (str, type->t.alias.aux_type); + dstring_appendstr (str, "})"); + return; + case ty_class: + dasprintf (str, " %s", type->t.class->name); + if (type->protos) + print_protocollist (str, type->protos); + return; + case ty_enum: + dasprintf (str, " enum %s", type->name); + return; + case ty_struct: + dasprintf (str, " struct %s", type->name); + return; + case ty_union: + dasprintf (str, " union %s", type->name); + return; + case ty_array: + print_type_str (str, type->t.array.type); + if (type->t.array.base) { + dasprintf (str, "[%d..%d]", type->t.array.base, + type->t.array.base + type->t.array.size - 1); } else { - int c, i; - dasprintf (str, "("); - if ((c = type->t.func.num_params) < 0) - c = ~c; // num_params is one's compliment - for (i = 0; i < c; i++) { - if (i) - dasprintf (str, ", "); - print_type_str (str, type->t.func.param_types[i]); - } - if (type->t.func.num_params < 0) - dasprintf (str, ", ..."); - dasprintf (str, ")"); + dasprintf (str, "[%d]", type->t.array.size); } - break; - case ev_pointer: - if (obj_is_id (type)) { - dasprintf (str, "id"); - if (type->t.fldptr.type->protos) - print_protocollist (str, type->t.fldptr.type->protos); - break; - } - if (type == &type_SEL) { - dasprintf (str, "SEL"); - break; - } - dasprintf (str, "(*"); - print_type_str (str, type->t.fldptr.type); - dasprintf (str, ")"); - break; - case ev_invalid: - switch (type->meta) { - case ty_class: - dasprintf (str, " %s", type->t.class->name); - if (type->protos) - print_protocollist (str, type->protos); - break; - case ty_enum: - dasprintf (str, " enum %s", type->name); - break; - case ty_struct: - dasprintf (str, " struct %s", type->name); - break; - case ty_union: - dasprintf (str, " union %s", type->name); - break; - case ty_array: - print_type_str (str, type->t.array.type); - if (type->t.array.base) { - dasprintf (str, "[%d..%d]", type->t.array.base, - type->t.array.base + type->t.array.size - 1); + return; + case ty_basic: + switch (type->type) { + case ev_field: + dasprintf (str, ".("); + print_type_str (str, type->t.fldptr.type); + dasprintf (str, ")"); + return; + case ev_func: + print_type_str (str, type->t.func.type); + if (type->t.func.num_params == -1) { + dasprintf (str, "(...)"); } else { - dasprintf (str, "[%d]", type->t.array.size); + int c, i; + dasprintf (str, "("); + if ((c = type->t.func.num_params) < 0) + c = ~c; // num_params is one's compliment + for (i = 0; i < c; i++) { + if (i) + dasprintf (str, ", "); + print_type_str (str, type->t.func.param_types[i]); + } + if (type->t.func.num_params < 0) + dasprintf (str, ", ..."); + dasprintf (str, ")"); } - break; - case ty_none: + return; + case ev_ptr: + if (type->t.fldptr.type) { + if (is_id (type)) { + __auto_type ptr = type->t.fldptr.type; + dasprintf (str, "id"); + if (ptr->protos) + print_protocollist (str, ptr->protos); + return; + } + if (is_SEL(type)) { + dasprintf (str, "SEL"); + return; + } + } + dasprintf (str, "(*"); + print_type_str (str, type->t.fldptr.type); + dasprintf (str, ")"); + return; + case ev_void: + case ev_string: + case ev_float: + case ev_vector: + case ev_entity: + case ev_quaternion: + case ev_int: + case ev_uint: + case ev_long: + case ev_ulong: + case ev_short: + case ev_ushort: + case ev_double: + dasprintf (str, " %s%s", pr_type_name[type->type], + type->width > 1 ? va (0, "{%d}", type->width) + : ""); + return; + case ev_invalid: + case ev_type_count: break; } break; - default: - dasprintf (str, " %s", pr_type_name[type->type]); - break; } + internal_error (0, "bad type meta:type %d:%d", type->meta, type->type); +} + +const char * +get_type_string (const type_t *type) +{ + static dstring_t *type_str[8]; + static int str_index; + + if (!type_str[str_index]) { + type_str[str_index] = dstring_newstr (); + } + dstring_clearstr (type_str[str_index]); + print_type_str (type_str[str_index], type); + const char *str = type_str[str_index++]->str; + str_index %= sizeof (type_str) / sizeof (type_str[0]); + while (*str == ' ') { + str++; + } + return str; } void @@ -509,7 +953,7 @@ encode_params (const type_t *type) else count = type->t.func.num_params; for (i = 0; i < count; i++) - encode_type (encoding, type->t.func.param_types[i]); + encode_type (encoding, unalias_type (type->t.func.param_types[i])); if (type->t.func.num_params < 0) dasprintf (encoding, "."); @@ -559,101 +1003,146 @@ encode_type (dstring_t *encoding, const type_t *type) { if (!type) return; - switch (type->type) { - case ev_void: - dasprintf (encoding, "v"); - break; - case ev_string: - dasprintf (encoding, "*"); - break; - case ev_float: - dasprintf (encoding, "f"); - break; - case ev_vector: - dasprintf (encoding, "V"); - break; - case ev_entity: - dasprintf (encoding, "E"); - break; - case ev_field: - dasprintf (encoding, "F"); - encode_type (encoding, type->t.fldptr.type); - break; - case ev_func: - dasprintf (encoding, "("); - encode_type (encoding, type->t.func.type); - dasprintf (encoding, "%s)", encode_params (type)); - break; - case ev_pointer: - if (type == &type_id) { - dasprintf (encoding, "@"); + switch (type->meta) { + case ty_handle: + dasprintf (encoding, "{%s$}", type->name); + return; + case ty_alias: + dasprintf (encoding, "{%s>", type->name ? type->name : ""); + encode_type (encoding, type->t.alias.aux_type); + dasprintf (encoding, "}"); + return; + case ty_class: + encode_class (encoding, type); + return; + case ty_enum: + encode_enum (encoding, type); + return; + case ty_struct: + case ty_union: + encode_struct (encoding, type); + return; + case ty_array: + dasprintf (encoding, "["); + dasprintf (encoding, "%d", type->t.array.size); + if (type->t.array.base) + dasprintf (encoding, ":%d", type->t.array.base); + dasprintf (encoding, "="); + encode_type (encoding, type->t.array.type); + dasprintf (encoding, "]"); + return; + case ty_basic: + switch (type->type) { + case ev_void: + dasprintf (encoding, "v"); + return; + case ev_string: + dasprintf (encoding, "*"); + return; + case ev_double: + if (type->width > 1) { + dasprintf (encoding, "d%d", type->width); + } else { + dasprintf (encoding, "d"); + } + return; + case ev_float: + if (type->width > 1) { + dasprintf (encoding, "f%d", type->width); + } else { + dasprintf (encoding, "f"); + } + return; + case ev_vector: + dasprintf (encoding, "V"); + return; + case ev_entity: + dasprintf (encoding, "E"); + return; + case ev_field: + dasprintf (encoding, "F"); + encode_type (encoding, type->t.fldptr.type); + return; + case ev_func: + dasprintf (encoding, "("); + encode_type (encoding, type->t.func.type); + dasprintf (encoding, "%s)", encode_params (type)); + return; + case ev_ptr: + if (is_id(type)) { + dasprintf (encoding, "@"); + return; + } + if (is_SEL(type)) { + dasprintf (encoding, ":"); + return; + } + if (is_Class(type)) { + dasprintf (encoding, "#"); + return; + } + type = type->t.fldptr.type; + dasprintf (encoding, "^"); + encode_type (encoding, type); + return; + case ev_quaternion: + dasprintf (encoding, "Q"); + return; + case ev_int: + if (type->width > 1) { + dasprintf (encoding, "i%d", type->width); + } else { + dasprintf (encoding, "i"); + } + return; + case ev_uint: + if (type->width > 1) { + dasprintf (encoding, "I%d", type->width); + } else { + dasprintf (encoding, "I"); + } + return; + case ev_long: + if (type->width > 1) { + dasprintf (encoding, "l%d", type->width); + } else { + dasprintf (encoding, "l"); + } + return; + case ev_ulong: + if (type->width > 1) { + dasprintf (encoding, "L%d", type->width); + } else { + dasprintf (encoding, "L"); + } + return; + case ev_short: + dasprintf (encoding, "s"); + return; + case ev_ushort: + dasprintf (encoding, "S"); + return; + case ev_invalid: + case ev_type_count: break; } - if (type == &type_SEL) { - dasprintf (encoding, ":"); - break; - } - if (type == &type_Class) { - dasprintf (encoding, "#"); - break; - } - type = type->t.fldptr.type; - dasprintf (encoding, "^"); - encode_type (encoding, type); - break; - case ev_quat: - dasprintf (encoding, "Q"); - break; - case ev_integer: - dasprintf (encoding, "i"); - break; - case ev_uinteger: - dasprintf (encoding, "I"); - break; - case ev_short: - dasprintf (encoding, "s"); - break; - case ev_invalid: - switch (type->meta) { - case ty_class: - encode_class (encoding, type); - break; - case ty_enum: - encode_enum (encoding, type); - break; - case ty_struct: - case ty_union: - encode_struct (encoding, type); - break; - case ty_array: - dasprintf (encoding, "["); - dasprintf (encoding, "%d", type->t.array.size); - if (type->t.array.base) - dasprintf (encoding, ":%d", type->t.array.base); - dasprintf (encoding, "="); - encode_type (encoding, type->t.array.type); - dasprintf (encoding, "]"); - break; - case ty_none: - dasprintf (encoding, "?"); - break; - } - break; - case ev_type_count: - dasprintf (encoding, "?"); break; } + internal_error (0, "bad type meta:type %d:%d", type->meta, type->type); } -int -is_void (const type_t *type) -{ - return type->type == ev_void; +#define EV_TYPE(t) \ +int is_##t (const type_t *type) \ +{ \ + type = unalias_type (type); \ + return type->type == ev_##t; \ } +#include "QF/progs/pr_type_names.h" int is_enum (const type_t *type) { + type = unalias_type (type); if (type->type == ev_invalid && type->meta == ty_enum) return 1; return 0; @@ -662,46 +1151,73 @@ is_enum (const type_t *type) int is_integral (const type_t *type) { - etype_t t = type->type; - - if (t == ev_integer || t == ev_uinteger || t == ev_short) + type = unalias_type (type); + if (is_int (type) || is_uint (type) || is_short (type)) + return 1; + if (is_long (type) || is_ulong (type) || is_ushort (type)) return 1; return is_enum (type); } int -is_float (const type_t *type) +is_real (const type_t *type) { - return type->type == ev_float; + type = unalias_type (type); + return is_float (type) || is_double (type); } int is_scalar (const type_t *type) { - return is_float (type) || is_integral (type); + type = unalias_type (type); + if (is_short (type) || is_ushort (type)) { + // shorts have width 0 + return 1; + } + if (type->width != 1) { + return 0; + } + return is_real (type) || is_integral (type); +} + +int +is_nonscalar (const type_t *type) +{ + type = unalias_type (type); + if (is_vector (type) || is_quaternion (type)) { + return 1; + } + if (type->width < 2) { + return 0; + } + return is_real (type) || is_integral (type); } int is_math (const type_t *type) { - etype_t t = type->type; + type = unalias_type (type); - return t == ev_vector || t == ev_quat || is_scalar (type); + if (is_vector (type) || is_quaternion (type)) { + return 1; + } + return is_scalar (type) || is_nonscalar (type); } int is_struct (const type_t *type) { - if (type->type == ev_invalid - && (type->meta == ty_struct || type->meta == ty_union)) + type = unalias_type (type); + if (type->type == ev_invalid && type->meta == ty_struct) return 1; return 0; } int -is_pointer (const type_t *type) +is_union (const type_t *type) { - if (type->type == ev_pointer) + type = unalias_type (type); + if (type->type == ev_invalid && type->meta == ty_union) return 1; return 0; } @@ -709,16 +1225,45 @@ is_pointer (const type_t *type) int is_array (const type_t *type) { + type = unalias_type (type); if (type->type == ev_invalid && type->meta == ty_array) return 1; return 0; } +int +is_structural (const type_t *type) +{ + type = unalias_type (type); + return is_struct (type) || is_union (type) || is_array (type); +} + +int +type_compatible (const type_t *dst, const type_t *src) +{ + // same type + if (dst == src) { + return 1; + } + if (is_field (dst) && is_field (src)) { + return 1; + } + if (is_func (dst) && is_func (src)) { + return 1; + } + if (is_ptr (dst) && is_ptr (src)) { + return 1; + } + return 0; +} + int type_assignable (const type_t *dst, const type_t *src) { int ret; + dst = unalias_type (dst); + src = unalias_type (src); // same type if (dst == src) return 1; @@ -726,13 +1271,22 @@ type_assignable (const type_t *dst, const type_t *src) if (dst->type == ev_field && src->type == ev_field) return 1; // pointer = array - if (is_pointer (dst) && is_array (src)) { - if (dst->t.fldptr.type == src->t.array.type) + if (is_ptr (dst) && is_array (src)) { + if (is_void (dst->t.fldptr.type) + || dst->t.fldptr.type == src->t.array.type) return 1; return 0; } - if (!is_pointer (dst) || !is_pointer (src)) - return is_scalar (dst) && is_scalar (src); + if (!is_ptr (dst) || !is_ptr (src)) { + if (is_scalar (dst) && is_scalar (src)) { + return 1; + } + if (is_nonscalar (dst) && is_nonscalar (src) + && type_width (dst) == type_width (src)) { + return 1; + } + return 0; + } // pointer = pointer // give the object system first shot because the pointee types might have @@ -744,6 +1298,9 @@ type_assignable (const type_t *dst, const type_t *src) dst = dst->t.fldptr.type; src = src->t.fldptr.type; + if (dst == src) { + return 1; + } if (is_void (dst)) return 1; if (is_void (src)) @@ -752,57 +1309,188 @@ type_assignable (const type_t *dst, const type_t *src) } int -type_size (const type_t *type) +type_promotes (const type_t *dst, const type_t *src) { - if (!type) + dst = unalias_type (dst); + src = unalias_type (src); + // nothing promotes to int + if (is_int (dst)) { return 0; - switch (type->type) { - case ev_void: - case ev_string: - case ev_float: - case ev_vector: - case ev_entity: - case ev_field: - case ev_func: - case ev_pointer: - case ev_quat: - case ev_integer: - case ev_uinteger: - case ev_short: - case ev_type_count: - return pr_type_size[type->type]; - case ev_invalid: - switch (type->meta) { - case ty_enum: - if (!type->t.symtab) - return 0; - return type_size (&type_integer); - case ty_struct: - case ty_union: - if (!type->t.symtab) - return 0; - return type->t.symtab->size; - case ty_class: - { - class_t *class = type->t.class; - int size; - if (!class->ivars) - return 0; - size = class->ivars->size; - if (class->super_class) - size += type_size (class->super_class->type); - return size; - } - case ty_array: - return type->t.array.size * type_size (type->t.array.type); - case ty_none: - return 0; - } - break; + } + if (is_uint (dst) && is_int (src)) { + return 1; + } + if (is_long (dst) && (is_int (src) || is_uint (src))) { + return 1; + } + if (is_ulong (dst) && (is_int (src) || is_uint (src) || is_long (src))) { + return 1; + } + if (is_float (dst) && (is_int (src) || is_uint (src))) { + return 1; + } + //XXX what to do with (u)long<->float? + // everything promotes to double + if (is_double (dst)) { + return 1; } return 0; } +int +type_same (const type_t *dst, const type_t *src) +{ + dst = unalias_type (dst); + src = unalias_type (src); + + return dst == src; +} + +int +type_size (const type_t *type) +{ + switch (type->meta) { + case ty_handle: + case ty_basic: + return pr_type_size[type->type] * type->width; + case ty_struct: + case ty_union: + if (!type->t.symtab) + return 0; + return type->t.symtab->size; + case ty_enum: + if (!type->t.symtab) + return 0; + return type_size (&type_int); + case ty_array: + return type->t.array.size * type_size (type->t.array.type); + case ty_class: + { + class_t *class = type->t.class; + int size; + if (!class->ivars) + return 0; + size = class->ivars->size; + if (class->super_class) + size += type_size (class->super_class->type); + return size; + } + case ty_alias: + return type_size (type->t.alias.aux_type); + } + internal_error (0, "invalid type meta: %d", type->meta); +} + +int +type_width (const type_t *type) +{ + switch (type->meta) { + case ty_basic: + if (type->type == ev_vector) { + return 3; + } + if (type->type == ev_quaternion) { + return 4; + } + return type->width; + case ty_handle: + case ty_struct: + case ty_union: + return 1; + case ty_enum: + if (!type->t.symtab) + return 0; + return type_width (&type_int); + case ty_array: + return type_width (type->t.array.type); + case ty_class: + return 1; + case ty_alias: + return type_width (type->t.alias.aux_type); + } + internal_error (0, "invalid type meta: %d", type->meta); +} + +static void +chain_basic_types (void) +{ + type_entity.t.symtab = pr.entity_fields; + if (options.code.progsversion == PROG_VERSION) { + type_quaternion.alignment = 4; + } + + chain_type (&type_void); + chain_type (&type_string); + chain_type (&type_float); + chain_type (&type_vector); + chain_type (&type_entity); + chain_type (&type_field); + chain_type (&type_func); + chain_type (&type_ptr); + chain_type (&type_floatfield); + if (!options.traditional) { + chain_type (&type_quaternion); + chain_type (&type_int); + chain_type (&type_uint); + chain_type (&type_short); + chain_type (&type_double); + + if (options.code.progsversion == PROG_VERSION) { + chain_type (&type_long); + chain_type (&type_ulong); + chain_type (&type_ushort); +#define VEC_TYPE(name, type) chain_type (&type_##name); +#include "tools/qfcc/include/vec_types.h" + } + } +} + +static void +chain_structural_types (void) +{ + chain_type (&type_param); + chain_type (&type_zero); + chain_type (&type_type_encodings); + chain_type (&type_xdef); + chain_type (&type_xdef_pointer); + chain_type (&type_xdefs); + chain_type (&type_va_list); +} + +void +chain_initial_types (void) +{ + chain_basic_types (); + chain_structural_types (); +} + +static const char *vector_field_names[] = { "x", "y", "z", "w" }; +//static const char *color_field_names[] = { "r", "g", "b", "a" }; +//static const char *texture_field_names[] = { "s", "t", "p", "q" }; + +static void +build_vector_struct (type_t *type) +{ + ty_meta_e meta = type->meta; + etype_t etype = type->type; + type_t *ele_type = base_type (type); + int width = type_width (type); + + if (!ele_type || width < 2) { + internal_error (0, "%s not a vector type: %p %d", type->name, ele_type, width); + } + + struct_def_t fields[width + 1]; + for (int i = 0; i < width; i++) { + fields[i] = (struct_def_t) { vector_field_names[i], ele_type }; + } + fields[width] = (struct_def_t) {}; + + make_structure (va (0, "@%s", type->name), 's', fields, type); + type->type = etype; + type->meta = meta; +} + void init_types (void) { @@ -811,14 +1499,15 @@ init_types (void) {"float_val", &type_float}, {"entity_val", &type_entity}, {"field_val", &type_field}, - {"func_val", &type_function}, - {"pointer_val", &type_pointer}, + {"func_val", &type_func}, + {"pointer_val", &type_ptr}, {"vector_val", &type_vector}, - {"int_val", &type_integer}, - {"uint_val", &type_uinteger}, - {"integer_val", &type_integer}, - {"uinteger_val", &type_uinteger}, + {"int_val", &type_int}, + {"uint_val", &type_uint}, + {"int_val", &type_int}, + {"uint_val", &type_uint}, {"quaternion_val", &type_quaternion}, + {"double_val", &type_double}, {0, 0} }; static struct_def_t param_struct[] = { @@ -827,34 +1516,47 @@ init_types (void) {"vector_val", &type_vector}, {"entity_val", &type_entity}, {"field_val", &type_field}, - {"func_val", &type_function}, - {"pointer_val", &type_pointer}, - {"int_val", &type_integer}, - {"uint_val", &type_uinteger}, - {"integer_val", &type_integer}, - {"uinteger_val", &type_uinteger}, + {"func_val", &type_func}, + {"pointer_val", &type_ptr}, + {"int_val", &type_int}, + {"uint_val", &type_uint}, {"quaternion_val", &type_quaternion}, - {0, 0} - }; - static struct_def_t vector_struct[] = { - {"x", &type_float}, - {"y", &type_float}, - {"z", &type_float}, - {0, 0} - }; - static struct_def_t quaternion_struct[] = { - {"s", &type_float}, - {"v", &type_vector}, + {"double_val", &type_double}, {0, 0} }; static struct_def_t type_encoding_struct[] = { - {"types", &type_pointer}, - {"size", &type_integer}, + {"types", &type_ptr}, + {"size", &type_uint}, + {0, 0} + }; + static struct_def_t xdef_struct[] = { + {"types", &type_ptr}, + {"offset", &type_ptr}, + {0, 0} + }; + static struct_def_t xdefs_struct[] = { + {"xdefs", &type_xdef_pointer}, + {"num_xdefs", &type_uint}, + {0, 0} + }; + static struct_def_t va_list_struct[] = { + {"count", &type_int}, + {"list", 0}, // type will be filled in at runtime {0, 0} }; + chain_basic_types (); + type_nil = &type_quaternion; - type_default = &type_integer; + type_default = &type_int; + type_long_int = &type_long; + type_ulong_uint = &type_ulong; + if (options.code.progsversion < PROG_VERSION) { + // even v6p doesn't support long, and I don't feel like adding it + // use ruamoko :P + type_long_int = &type_int; + type_ulong_uint = &type_uint; + } if (options.code.progsversion == PROG_ID_VERSION) { // vector can't be part of .zero for v6 progs because for v6 progs, // .zero is only one word wide. @@ -867,67 +1569,30 @@ init_types (void) make_structure ("@zero", 'u', zero_struct, &type_zero); make_structure ("@param", 'u', param_struct, &type_param); - make_structure ("@vector", 's', vector_struct, &type_vector); - type_vector.type = ev_vector; - type_vector.meta = ty_none; + build_vector_struct (&type_vector); make_structure ("@type_encodings", 's', type_encoding_struct, &type_type_encodings); - - if (options.traditional) - return; - - make_structure ("@quaternion", 's', quaternion_struct, &type_quaternion); - type_quaternion.type = ev_quat; - type_quaternion.meta = ty_none; - { - symbol_t *sym; - sym = new_symbol_type ("w", &type_float); - sym->s.offset = 0; - symtab_addsymbol (type_quaternion.t.symtab, sym); - sym = new_symbol_type ("x", &type_float); - sym->s.offset = 1; - symtab_addsymbol (type_quaternion.t.symtab, sym); - sym = new_symbol_type ("y", &type_float); - sym->s.offset = 2; - symtab_addsymbol (type_quaternion.t.symtab, sym); - sym = new_symbol_type ("z", &type_float); - sym->s.offset = 3; - symtab_addsymbol (type_quaternion.t.symtab, sym); - } -} - -void -chain_initial_types (void) -{ - static struct_def_t va_list_struct[] = { - {"count", &type_integer}, - {"list", 0}, // type will be filled in at runtime - {0, 0} - }; - - chain_type (&type_void); - chain_type (&type_string); - chain_type (&type_float); - chain_type (&type_vector); - type_entity.t.symtab = pr.entity_fields; - chain_type (&type_entity); - chain_type (&type_field); - chain_type (&type_function); - chain_type (&type_pointer); - chain_type (&type_floatfield); - if (!options.traditional) { - chain_type (&type_quaternion); - chain_type (&type_integer); - chain_type (&type_uinteger); - chain_type (&type_short); - } - - chain_type (&type_param); - chain_type (&type_zero); - chain_type (&type_type_encodings); + make_structure ("@xdef", 's', xdef_struct, &type_xdef); + make_structure ("@xdefs", 's', xdefs_struct, &type_xdefs); va_list_struct[1].type = pointer_type (&type_param); make_structure ("@va_list", 's', va_list_struct, &type_va_list); - chain_type (&type_va_list); + + build_vector_struct (&type_quaternion); + { + symbol_t *sym; + + sym = new_symbol_type ("v", &type_vector); + sym->s.offset = 0; + symtab_addsymbol (type_quaternion.t.symtab, sym); + + sym = new_symbol_type ("s", &type_float); + sym->s.offset = 3; + symtab_addsymbol (type_quaternion.t.symtab, sym); + } +#define VEC_TYPE(type_name, base_type) build_vector_struct (&type_##type_name); +#include "tools/qfcc/include/vec_types.h" + + chain_structural_types (); } diff --git a/tools/qfcc/source/value.c b/tools/qfcc/source/value.c index 1eab5ec68..14e891827 100644 --- a/tools/qfcc/source/value.c +++ b/tools/qfcc/source/value.c @@ -43,43 +43,132 @@ #include "QF/dstring.h" #include "QF/hash.h" #include "QF/mathlib.h" +#include "QF/progs.h" #include "QF/va.h" -#include "qfcc.h" -#include "def.h" -#include "defspace.h" -#include "diagnostic.h" -#include "emit.h" -#include "expr.h" -#include "reloc.h" -#include "strpool.h" -#include "symtab.h" -#include "type.h" -#include "value.h" +#include "QF/simd/types.h" + +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/emit.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/reloc.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" + +//FIXME I don't like this being here (or aligned_alloc being necessary in +//the first place, but not at all sure what to do about that) +#ifdef _WIN32 +#define aligned_alloc(al, sz) _aligned_malloc(sz, al) +#define free(x) _aligned_free(x) +#endif typedef struct { def_t *def; union { - string_t string_val; - float float_val; - float vector_val[3]; - int entity_val; - int field_val; - int func_val; +#define EV_TYPE(type) pr_##type##_t type##_val; +#include "QF/progs/pr_type_names.h" +#define VEC_TYPE(type_name, base_type) pr_##type_name##_t type_name##_val; +#include "tools/qfcc/include/vec_types.h" ex_pointer_t pointer; - float quaternion_val[4]; - int integer_val; } i; } immediate_t; static hashtab_t *value_table; -static ex_value_t *values_freelist; +ALLOC_STATE (ex_value_t, values); + +//FIXME this (to setup_value_progs) should be in its own file and more +//general (good for constant folding, too, and maybe some others). +static void +value_debug_handler (prdebug_t event, void *param, void *data) +{ + progs_t *pr = data; + switch (event) { + case prd_trace: + dstatement_t *st = pr->pr_statements + pr->pr_xstatement; + PR_PrintStatement (pr, st, 0); + break; + case prd_breakpoint: + case prd_subenter: + case prd_subexit: + case prd_runerror: + case prd_watchpoint: + case prd_begin: + case prd_terminate: + case prd_error: + case prd_none: + break; + } +} + +enum { + vf_null, + vf_convert, +}; + +#define BASE(b, base) (((base) & 3) << OP_##b##_SHIFT) +#define OP(a, b, c, op) ((op) | BASE(A, a) | BASE(B, b) | BASE(C, c)) + +static bfunction_t value_functions[] = { + {}, // null function + [vf_convert] = { .first_statement = vf_convert * 16 }, +}; + +static __attribute__((aligned(64))) +dstatement_t value_statements[] = { + [vf_convert * 16 - 1] = {}, + { OP_CONV, 0, 07777, 16 }, + { OP_RETURN, 16, 0, 0 }, +}; + +#define num_globals 16384 +#define stack_size 8192 +static __attribute__((aligned(64))) +pr_type_t value_globals[num_globals + 128] = { + [num_globals - stack_size] = { .uint_value = num_globals }, +}; + +static dprograms_t value_progs = { + .version = PROG_VERSION, + .statements = { + .count = sizeof (value_statements) / sizeof (value_statements[0]), + }, +}; +static progs_t value_pr = { + .progs = &value_progs, + .debug_handler = value_debug_handler, + .debug_data = &value_pr, + .pr_trace = 1, + .pr_trace_depth = -1, + .function_table = value_functions, + .pr_statements = value_statements, + .globals_size = num_globals, + .pr_globals = value_globals, + .stack_bottom = num_globals - stack_size + 4, + .pr_return_buffer = value_globals + num_globals, + .pr_return = value_globals + num_globals, + .globals = { + .stack = (pr_ptr_t *) (value_globals + num_globals - stack_size), + } +}; + +static void +setup_value_progs (void) +{ + PR_Init (&value_pr); + PR_Debug_Init (&value_pr); +} static uintptr_t value_get_hash (const void *_val, void *unused) { const ex_value_t *val = (const ex_value_t *) _val; - return Hash_Buffer (&val->v, sizeof (val->v)) + val->type; + return Hash_Buffer (&val->v, sizeof (val->v)) ^ (uintptr_t) val->type; } static int @@ -96,10 +185,19 @@ static ex_value_t * new_value (void) { ex_value_t *value; +#define malloc(x) aligned_alloc (__alignof__(ex_value_t), x) ALLOC (256, ex_value_t, values, value); +#undef malloc return value; } +static void +set_val_type (ex_value_t *val, type_t *type) +{ + val->type = type; + val->lltype = low_level_type (type); +} + static ex_value_t * find_value (const ex_value_t *val) { @@ -119,18 +217,28 @@ new_string_val (const char *string_val) { ex_value_t val; memset (&val, 0, sizeof (val)); - val.type = ev_string; + set_val_type (&val, &type_string); if (string_val) val.v.string_val = save_string (string_val); return find_value (&val); } +ex_value_t * +new_double_val (double double_val) +{ + ex_value_t val; + memset (&val, 0, sizeof (val)); + set_val_type (&val, &type_double); + val.v.double_val = double_val; + return find_value (&val); +} + ex_value_t * new_float_val (float float_val) { ex_value_t val; memset (&val, 0, sizeof (val)); - val.type = ev_float; + set_val_type (&val, &type_float); val.v.float_val = float_val; return find_value (&val); } @@ -140,7 +248,7 @@ new_vector_val (const float *vector_val) { ex_value_t val; memset (&val, 0, sizeof (val)); - val.type = ev_vector; + set_val_type (&val, &type_vector); VectorCopy (vector_val, val.v.vector_val); return find_value (&val); } @@ -150,7 +258,7 @@ new_entity_val (int entity_val) { ex_value_t val; memset (&val, 0, sizeof (val)); - val.type = ev_entity; + set_val_type (&val, &type_entity); val.v.entity_val = entity_val; return find_value (&val); } @@ -160,7 +268,7 @@ new_field_val (int field_val, type_t *type, def_t *def) { ex_value_t val; memset (&val, 0, sizeof (val)); - val.type = ev_field; + set_val_type (&val, field_type (type)); val.v.pointer.val = field_val; val.v.pointer.type = type; val.v.pointer.def = def; @@ -172,21 +280,26 @@ new_func_val (int func_val, type_t *type) { ex_value_t val; memset (&val, 0, sizeof (val)); - val.type = ev_func; + set_val_type (&val, type); val.v.func_val.val = func_val; val.v.func_val.type = type; return find_value (&val); } ex_value_t * -new_pointer_val (int pointer_val, type_t *type, def_t *def) +new_pointer_val (int pointer_val, type_t *type, def_t *def, + struct operand_s *tempop) { ex_value_t val; + if (!type) { + internal_error (0, "pointer value with no type"); + } memset (&val, 0, sizeof (val)); - val.type = ev_pointer; + set_val_type (&val, pointer_type (type)); val.v.pointer.val = pointer_val; val.v.pointer.type = type; val.v.pointer.def = def; + val.v.pointer.tempop = tempop; return find_value (&val); } @@ -195,28 +308,44 @@ new_quaternion_val (const float *quaternion_val) { ex_value_t val; memset (&val, 0, sizeof (val)); - val.type = ev_quat; + set_val_type (&val, &type_quaternion); QuatCopy (quaternion_val, val.v.quaternion_val); return find_value (&val); } ex_value_t * -new_integer_val (int integer_val) +new_int_val (int int_val) { ex_value_t val; memset (&val, 0, sizeof (val)); - val.type = ev_integer; - val.v.integer_val = integer_val; + set_val_type (&val, &type_int); + val.v.int_val = int_val; return find_value (&val); } ex_value_t * -new_uinteger_val (int uinteger_val) +new_uint_val (int uint_val) { ex_value_t val; memset (&val, 0, sizeof (val)); - val.type = ev_uinteger; - val.v.uinteger_val = uinteger_val; + set_val_type (&val, &type_uint); + val.v.uint_val = uint_val; + return find_value (&val); +} + +ex_value_t * +new_long_val (pr_long_t long_val) +{ + ex_value_t val = { .v = { .long_val = long_val } }; + set_val_type (&val, &type_long); + return find_value (&val); +} + +ex_value_t * +new_ulong_val (pr_ulong_t ulong_val) +{ + ex_value_t val = { .v = { .ulong_val = ulong_val } }; + set_val_type (&val, &type_ulong); return find_value (&val); } @@ -225,7 +354,7 @@ new_short_val (short short_val) { ex_value_t val; memset (&val, 0, sizeof (val)); - val.type = ev_short; + set_val_type (&val, &type_short); val.v.short_val = short_val; return find_value (&val); } @@ -235,23 +364,208 @@ new_nil_val (type_t *type) { ex_value_t val; memset (&val, 0, sizeof (val)); - val.type = low_level_type (type); - if (val.type == ev_pointer|| val.type == ev_field ) + set_val_type (&val, type); + if (val.lltype == ev_void) { + val.lltype = type_nil->type; + } + if (val.lltype == ev_ptr || val.lltype == ev_field ) val.v.pointer.type = type->t.fldptr.type; - if (val.type == ev_func) + if (val.lltype == ev_func) val.v.func_val.type = type; return find_value (&val); } +ex_value_t * +new_type_value (const type_t *type, const pr_type_t *data) +{ + size_t typeSize = type_size (type) * sizeof (pr_type_t); + ex_value_t val = {}; + set_val_type (&val, (type_t *) type);//FIXME cast + memcpy (&val.v, data, typeSize); + return find_value (&val); +} + +void +value_store (pr_type_t *dst, const type_t *dstType, const expr_t *src) +{ + size_t dstSize = type_size (dstType) * sizeof (pr_type_t); + + if (src->type == ex_nil) { + memset (dst, 0, dstSize); + return; + } + if (src->type == ex_symbol && src->e.symbol->sy_type == sy_var) { + // initialized global def treated as a constant + // from the tests in cast_expr, the def is known to be constant + def_t *def = src->e.symbol->s.def; + memcpy (dst, &D_PACKED (pr_type_t, def), dstSize); + return; + } + ex_value_t *val = 0; + if (src->type == ex_value) { + val = src->e.value; + } + if (src->type == ex_symbol && src->e.symbol->sy_type == sy_const) { + val = src->e.symbol->s.value; + } + if (!val) { + internal_error (src, "unexpected constant expression type"); + } + memcpy (dst, &val->v, dstSize); +} + +const char * +get_value_string (const ex_value_t *value) +{ + const type_t *type = value->type; + const char *str = ""; + switch (type->type) { + case ev_string: + return va (0, "\"%s\"", quote_string (value->v.string_val)); + case ev_vector: + case ev_quaternion: + case ev_float: + switch (type_width (type)) { + case 1: + str = va (0, "%.9g", value->v.float_val); + break; + case 2: + str = va (0, VEC2F_FMT, VEC2_EXP (value->v.vec2_val)); + break; + case 3: + str = va (0, "[%.9g, %.9g, %.9g]", + VectorExpand (value->v.vec3_val)); + break; + case 4: + str = va (0, VEC4F_FMT, VEC4_EXP (value->v.vec4_val)); + break; + } + return va (0, "%s %s", type->name, str); + case ev_entity: + case ev_func: + return va (0, "%s %d", type->name, value->v.int_val); + case ev_field: + if (value->v.pointer.def) { + int offset = value->v.pointer.val; + offset += value->v.pointer.def->offset; + return va (0, "field %d", offset); + } else { + return va (0, "field %d", value->v.pointer.val); + } + case ev_ptr: + if (value->v.pointer.def) { + str = va (0, "<%s>", value->v.pointer.def->name); + } + return va (0, "(* %s)[%d]%s", + value->v.pointer.type + ? get_type_string (value->v.pointer.type) : "???", + value->v.pointer.val, str); + case ev_int: + switch (type_width (type)) { + case 1: + str = va (0, "%"PRIi32, value->v.int_val); + break; + case 2: + str = va (0, VEC2I_FMT, VEC2_EXP (value->v.ivec2_val)); + break; + case 3: + str = va (0, "[%"PRIi32", %"PRIi32", %"PRIi32"]", + VectorExpand (value->v.ivec3_val)); + break; + case 4: + str = va (0, VEC4I_FMT, VEC4_EXP (value->v.ivec4_val)); + break; + } + return va (0, "%s %s", type->name, str); + case ev_uint: + switch (type_width (type)) { + case 1: + str = va (0, "%"PRIu32, value->v.uint_val); + break; + case 2: + str = va (0, "[%"PRIu32", %"PRIi32"]", + VEC2_EXP (value->v.uivec2_val)); + break; + case 3: + str = va (0, "[%"PRIu32", %"PRIi32", %"PRIi32"]", + VectorExpand (value->v.uivec3_val)); + break; + case 4: + str = va (0, "[%"PRIu32", %"PRIi32", %"PRIi32", %"PRIi32"]", + VEC4_EXP (value->v.uivec4_val)); + break; + } + return va (0, "%s %s", type->name, str); + case ev_short: + return va (0, "%s %"PRIi16, type->name, value->v.short_val); + case ev_ushort: + return va (0, "%s %"PRIu16, type->name, value->v.ushort_val); + case ev_double: + switch (type_width (type)) { + case 1: + str = va (0, "%.17g", value->v.double_val); + break; + case 2: + str = va (0, VEC2D_FMT, VEC2_EXP (value->v.dvec2_val)); + break; + case 3: + str = va (0, "[%.17g, %.17g, %.17g]", + VectorExpand (value->v.dvec3_val)); + break; + case 4: + str = va (0, VEC4D_FMT, VEC4_EXP (value->v.dvec4_val)); + break; + } + return va (0, "%s %s", type->name, str); + case ev_long: + switch (type_width (type)) { + case 1: + str = va (0, "%"PRIi64, value->v.long_val); + break; + case 2: + str = va (0, VEC2L_FMT, VEC2_EXP (value->v.lvec2_val)); + break; + case 3: + str = va (0, "[%"PRIi64", %"PRIi64", %"PRIi64"]", + VectorExpand (value->v.lvec3_val)); + break; + case 4: + str = va (0, VEC4L_FMT, VEC4_EXP (value->v.lvec4_val)); + break; + } + return va (0, "%s %s", type->name, str); + case ev_ulong: + switch (type_width (type)) { + case 1: + str = va (0, "%"PRIu64, value->v.ulong_val); + break; + case 2: + str = va (0, "[%"PRIu64", %"PRIi64"]", + VEC2_EXP (value->v.ulvec2_val)); + break; + case 3: + str = va (0, "[%"PRIu64", %"PRIi64", %"PRIi64"]", + VectorExpand (value->v.ulvec3_val)); + break; + case 4: + str = va (0, "[%"PRIu64", %"PRIi64", %"PRIi64", %"PRIi64"]", + VEC4_EXP (value->v.ulvec4_val)); + break; + } + return va (0, "%s %s", type->name, str); + case ev_void: + return ""; + case ev_invalid: + return ""; + case ev_type_count: + return ""; + } + return "invalid type"; +} + static hashtab_t *string_imm_defs; -static hashtab_t *float_imm_defs; -static hashtab_t *vector_imm_defs; -static hashtab_t *entity_imm_defs; -static hashtab_t *field_imm_defs; -static hashtab_t *func_imm_defs; -static hashtab_t *pointer_imm_defs; -static hashtab_t *quaternion_imm_defs; -static hashtab_t *integer_imm_defs; +static hashtab_t *fldptr_imm_defs; +static hashtab_t *value_imm_defs; static void imm_free (void *_imm, void *unused) @@ -259,7 +573,7 @@ imm_free (void *_imm, void *unused) free (_imm); } -static uintptr_t +static __attribute__((pure)) uintptr_t imm_get_hash (const void *_imm, void *_tab) { immediate_t *imm = (immediate_t *) _imm; @@ -268,29 +582,17 @@ imm_get_hash (const void *_imm, void *_tab) if (tab == &string_imm_defs) { const char *str = pr.strings->strings + imm->i.string_val; return str ? Hash_String (str) : 0; - } else if (tab == &float_imm_defs) { - return imm->i.integer_val; - } else if (tab == &vector_imm_defs) { - return Hash_Buffer (&imm->i.vector_val, sizeof (&imm->i.vector_val)); - } else if (tab == &entity_imm_defs) { - return imm->i.integer_val; - } else if (tab == &field_imm_defs) { + } else if (tab == &fldptr_imm_defs) { return Hash_Buffer (&imm->i.pointer, sizeof (&imm->i.pointer)); - } else if (tab == &func_imm_defs) { - return imm->i.integer_val; - } else if (tab == &pointer_imm_defs) { - return Hash_Buffer (&imm->i.pointer, sizeof (&imm->i.pointer)); - } else if (tab == &quaternion_imm_defs) { - return Hash_Buffer (&imm->i.quaternion_val, - sizeof (&imm->i.quaternion_val)); - } else if (tab == &integer_imm_defs) { - return imm->i.integer_val; + } else if (tab == &value_imm_defs) { + size_t size = type_size (imm->def->type) * sizeof (pr_type_t); + return Hash_Buffer (&imm->i, size) ^ (uintptr_t) imm->def->type; } else { - internal_error (0, 0); + internal_error (0, "invalid immediate hash table"); } } -static int +static __attribute__((pure)) int imm_compare (const void *_imm1, const void *_imm2, void *_tab) { immediate_t *imm1 = (immediate_t *) _imm1; @@ -301,28 +603,14 @@ imm_compare (const void *_imm1, const void *_imm2, void *_tab) const char *str1 = pr.strings->strings + imm1->i.string_val; const char *str2 = pr.strings->strings + imm2->i.string_val; return (str1 == str2 || (str1 && str2 && !strcmp (str1, str2))); - } else if (tab == &float_imm_defs) { - return imm1->i.float_val == imm2->i.float_val; - } else if (tab == &vector_imm_defs) { - return VectorCompare (imm1->i.vector_val, imm2->i.vector_val); - } else if (tab == &entity_imm_defs) { - return imm1->i.entity_val == imm2->i.entity_val; - } else if (tab == &field_imm_defs) { + } else if (tab == &fldptr_imm_defs) { return !memcmp (&imm1->i.pointer, &imm2->i.pointer, sizeof (imm1->i.pointer)); - } else if (tab == &func_imm_defs) { - return imm1->i.func_val == imm2->i.func_val; - } else if (tab == &pointer_imm_defs) { - return !memcmp (&imm1->i.pointer, &imm2->i.pointer, - sizeof (imm1->i.pointer)); - } else if (tab == &quaternion_imm_defs) { - return (VectorCompare (imm1->i.quaternion_val, - imm2->i.quaternion_val) - && imm1->i.quaternion_val[3] == imm2->i.quaternion_val[3]); - } else if (tab == &integer_imm_defs) { - return imm1->i.integer_val == imm2->i.integer_val; + } else if (tab == &value_imm_defs) { + size_t size = type_size (imm1->def->type) * sizeof (pr_type_t); + return !memcmp (&imm1->i, &imm2->i, size); } else { - internal_error (0, 0); + internal_error (0, "invalid immediate hash table"); } } @@ -332,69 +620,29 @@ ReuseString (const char *str) return strpool_addstr (pr.strings, str); } -static float -value_as_float (ex_value_t *value) -{ - if (value->type == ev_uinteger) - return value->v.uinteger_val; - if (value->type == ev_integer) - return value->v.integer_val; - if (value->type == ev_short) - return value->v.short_val; - if (value->type == ev_float) - return value->v.float_val; - return 0; -} - -static int -value_as_int (ex_value_t *value) -{ - if (value->type == ev_uinteger) - return value->v.uinteger_val; - if (value->type == ev_integer) - return value->v.integer_val; - if (value->type == ev_short) - return value->v.short_val; - if (value->type == ev_float) - return value->v.float_val; - return 0; -} - -static unsigned -value_as_uint (ex_value_t *value) -{ - if (value->type == ev_uinteger) - return value->v.uinteger_val; - if (value->type == ev_integer) - return value->v.integer_val; - if (value->type == ev_short) - return value->v.short_val; - if (value->type == ev_float) - return value->v.float_val; - return 0; -} - ex_value_t * convert_value (ex_value_t *value, type_t *type) { - if (!is_scalar (type) || !is_scalar (ev_types[value->type])) { - error (0, "unable to convert non-scalar value"); + if (!is_math (type) || !is_math (value->type)) { + error (0, "unable to convert non-math value"); return value; } - if (is_float (type)) { - float val = value_as_float (value); - return new_float_val (val); - } else if (type->type == ev_short) { - int val = value_as_int (value); - return new_short_val (val); - } else if (type->type == ev_uinteger) { - unsigned val = value_as_uint (value); - return new_uinteger_val (val); - } else { - //FIXME handle enums separately? - int val = value_as_int (value); - return new_integer_val (val); + if (type_width (type) != type_width (value->type)) { + error (0, "unable to convert between values of different widths"); + return value; } + int from = type_cast_map[base_type (value->type)->type]; + int to = type_cast_map[base_type (type)->type]; + int width = type_width (value->type) - 1; + int conv = TYPE_CAST_CODE (from, to, width); + int addr = value_functions[vf_convert].first_statement; + value_statements[addr + 0].b = conv; + value_statements[addr + 1].c = type_size (type) - 1; + memcpy (value_globals, &value->v, + type_size (value->type) * sizeof (pr_type_t)); + value_pr.pr_trace = options.verbosity > 1; + PR_ExecuteProgram (&value_pr, vf_convert); + return new_type_value (type, value_pr.pr_return_buffer); } ex_value_t * @@ -402,12 +650,12 @@ alias_value (ex_value_t *value, type_t *type) { ex_value_t new; - if (type_size (type) != type_size (ev_types[value->type])) { + if (type_size (type) != type_size (ev_types[value->lltype])) { error (0, "unable to alias different sized values"); return value; } new = *value; - new.type = type->type; + set_val_type (&new, type); return find_value (&new); } @@ -416,7 +664,8 @@ make_def_imm (def_t *def, hashtab_t *tab, ex_value_t *val) { immediate_t *imm; - imm = calloc (1, sizeof (immediate_t)); + imm = aligned_alloc (__alignof__(immediate_t), sizeof (immediate_t)); + memset (imm, 0, sizeof (immediate_t)); imm->def = def; memcpy (&imm->i, &val->v, sizeof (imm->i)); @@ -432,62 +681,47 @@ emit_value (ex_value_t *value, def_t *def) hashtab_t *tab = 0; type_t *type; ex_value_t val = *value; - immediate_t *imm, search; if (!string_imm_defs) { clear_immediates (); } cn = 0; - if (val.type == ev_void) - val.type = type_nil->type; - switch (val.type) { +// if (val.type == ev_void) +// val.type = type_nil->type; + switch (val.lltype) { case ev_entity: - tab = entity_imm_defs; - type = &type_entity; + case ev_func: + case ev_int: + case ev_uint: + case ev_float: + case ev_vector: + case ev_quaternion: + case ev_double: + case ev_long: + case ev_ulong: + tab = value_imm_defs; + type = val.type; break; case ev_field: - tab = field_imm_defs; - type = &type_field; - break; - case ev_func: - tab = func_imm_defs; - type = &type_function; - break; - case ev_pointer: - tab = pointer_imm_defs; - type = &type_pointer; - break; - case ev_integer: - case ev_uinteger: - if (!def || def->type != &type_float) { - tab = integer_imm_defs; - type = &type_integer; - break; - } - val.v.float_val = val.v.integer_val; - val.type = ev_float; - case ev_float: - tab = float_imm_defs; - type = &type_float; + case ev_ptr: + tab = fldptr_imm_defs; + type = ev_types[val.lltype]; break; case ev_string: - val.v.integer_val = ReuseString (val.v.string_val); + val.v.int_val = ReuseString (val.v.string_val); tab = string_imm_defs; type = &type_string; break; - case ev_vector: - tab = vector_imm_defs; - type = &type_vector; - break; - case ev_quat: - tab = quaternion_imm_defs; - type = &type_quaternion; - break; default: - internal_error (0, 0); + internal_error (0, "unexpected value type: %s", + val.type->type < ev_type_count + ? pr_type_name[val.lltype] + : va (0, "%d", val.lltype)); } + def_t search_def = { .type = type }; + immediate_t search = { .def = &search_def }; memcpy (&search.i, &val.v, sizeof (search.i)); - imm = (immediate_t *) Hash_FindElement (tab, &search); + immediate_t *imm = Hash_FindElement (tab, &search); if (imm && strcmp (imm->def->name, ".zero") == 0) { if (def) { imm = 0; //FIXME do full def aliasing @@ -530,7 +764,7 @@ emit_value (ex_value_t *value, def_t *def) cn->initialized = cn->constant = 1; cn->nosave = 1; // copy the immediate to the global area - switch (val.type) { + switch (val.lltype) { case ev_string: reloc_def_string (cn); break; @@ -546,7 +780,7 @@ emit_value (ex_value_t *value, def_t *def) if (val.v.pointer.def) reloc_def_field_ofs (val.v.pointer.def, cn); break; - case ev_pointer: + case ev_ptr: if (val.v.pointer.def) { EMIT_DEF_OFS (pr.near_data, D_INT (cn), val.v.pointer.def); @@ -556,9 +790,9 @@ emit_value (ex_value_t *value, def_t *def) break; } - memcpy (D_POINTER (void, cn), &val.v, 4 * type_size (type)); + memcpy (D_POINTER (pr_type_t, cn), &val.v, 4 * type_size (type)); - imm = make_def_imm (cn, tab, &val); + make_def_imm (cn, tab, &val); return cn; } @@ -572,55 +806,31 @@ clear_immediates (void) if (value_table) { Hash_FlushTable (value_table); Hash_FlushTable (string_imm_defs); - Hash_FlushTable (float_imm_defs); - Hash_FlushTable (vector_imm_defs); - Hash_FlushTable (entity_imm_defs); - Hash_FlushTable (field_imm_defs); - Hash_FlushTable (func_imm_defs); - Hash_FlushTable (pointer_imm_defs); - Hash_FlushTable (quaternion_imm_defs); - Hash_FlushTable (integer_imm_defs); + Hash_FlushTable (fldptr_imm_defs); + Hash_FlushTable (value_imm_defs); } else { - value_table = Hash_NewTable (16381, 0, 0, 0); + setup_value_progs (); + + value_table = Hash_NewTable (16381, 0, 0, 0, 0); Hash_SetHashCompare (value_table, value_get_hash, value_compare); - string_imm_defs = Hash_NewTable (16381, 0, imm_free, &string_imm_defs); + string_imm_defs = Hash_NewTable (16381, 0, imm_free, + &string_imm_defs, 0); Hash_SetHashCompare (string_imm_defs, imm_get_hash, imm_compare); - float_imm_defs = Hash_NewTable (16381, 0, imm_free, &float_imm_defs); - Hash_SetHashCompare (float_imm_defs, imm_get_hash, imm_compare); + fldptr_imm_defs = Hash_NewTable (16381, 0, imm_free, + &fldptr_imm_defs, 0); + Hash_SetHashCompare (fldptr_imm_defs, imm_get_hash, imm_compare); - vector_imm_defs = Hash_NewTable (16381, 0, imm_free, &vector_imm_defs); - Hash_SetHashCompare (vector_imm_defs, imm_get_hash, imm_compare); - - entity_imm_defs = Hash_NewTable (16381, 0, imm_free, &entity_imm_defs); - Hash_SetHashCompare (entity_imm_defs, imm_get_hash, imm_compare); - - field_imm_defs = Hash_NewTable (16381, 0, imm_free, &field_imm_defs); - Hash_SetHashCompare (field_imm_defs, imm_get_hash, imm_compare); - - func_imm_defs = Hash_NewTable (16381, 0, imm_free, &func_imm_defs); - Hash_SetHashCompare (func_imm_defs, imm_get_hash, imm_compare); - - pointer_imm_defs = - Hash_NewTable (16381, 0, imm_free, &pointer_imm_defs); - Hash_SetHashCompare (pointer_imm_defs, imm_get_hash, imm_compare); - - quaternion_imm_defs = - Hash_NewTable (16381, 0, imm_free, &quaternion_imm_defs); - Hash_SetHashCompare (quaternion_imm_defs, imm_get_hash, imm_compare); - - integer_imm_defs = - Hash_NewTable (16381, 0, imm_free, &integer_imm_defs); - Hash_SetHashCompare (integer_imm_defs, imm_get_hash, imm_compare); + value_imm_defs = Hash_NewTable (16381, 0, imm_free, + &value_imm_defs, 0); + Hash_SetHashCompare (value_imm_defs, imm_get_hash, imm_compare); } def = make_symbol (".zero", &type_zero, 0, sc_extern)->s.def; memset (&zero_val, 0, sizeof (zero_val)); make_def_imm (def, string_imm_defs, &zero_val); - make_def_imm (def, float_imm_defs, &zero_val); - make_def_imm (def, entity_imm_defs, &zero_val); - make_def_imm (def, pointer_imm_defs, &zero_val); - make_def_imm (def, integer_imm_defs, &zero_val); + make_def_imm (def, fldptr_imm_defs, &zero_val); + make_def_imm (def, value_imm_defs, &zero_val); } diff --git a/tools/qfcc/test/Makefile.am b/tools/qfcc/test/Makefile.am deleted file mode 100644 index c4302f32f..000000000 --- a/tools/qfcc/test/Makefile.am +++ /dev/null @@ -1,166 +0,0 @@ -AUTOMAKE_OPTIONS= foreign -AM_CPPFLAGS= -I$(top_srcdir)/include $(QFCC_INCS) - -QFCC_DEP=$(builddir)/../source/qfcc$(EXEEXT) -QFCC=$(QFCC_DEP) - -QCFLAGS=-qq -O -g --no-default-paths -Werror -QCPPFLAGS= -QCOMPILE=$(QFCC) $(QCFLAGS) $(QCPPFLAGS) - -SUFFIXES=.qfo .r -.r.qfo: - $(QCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tqo -c -o $@ $< - sed -i -e '1s@:@: $(QFCC_DEP)@' $(DEPDIR)/$*.Tqo - $(am__mv) $(DEPDIR)/$*.Tqo $(DEPDIR)/$*.Qo - -QFCC_TEST_LIBS=@QFCC_TEST_LIBS@ -QFCC_TEST_DEPS=@QFCC_TEST_DEPS@ -QFCC_TEST_INCS=@QFCC_TEST_INCS@ - -test_progs_dat=\ - chewed-alias.dat \ - chewed-return.dat \ - func-expr.dat \ - func-static.dat \ - deadbool.dat \ - infloop.dat \ - modulo.dat \ - paramret.dat \ - return-ivar.dat \ - sendv.dat \ - state.dat \ - structlive.dat \ - structptr.dat \ - vecinit.dat \ - while.dat \ - voidfor.dat - -fail_progs_dat=\ - $E - -TESTS=$(test_progs_dat:.dat=.run) -XFAIL_TESTS=$(fail_progs_dat:.dat=.run) - -check_PROGRAMS=test-harness $(test_progs_dat) - -test_harness_SOURCES= test-bi.c test-harness.c -test_harness_LDADD= $(QFCC_TEST_LIBS) -test_harness_DEPENDENCIES= $(QFCC_TEST_DEPS) - -chewed_alias_dat_SOURCES=chewed-alias.r -chewed_alias_obj=$(chewed_alias_dat_SOURCES:.r=.qfo) -chewed-alias.dat$(EXEEXT): $(chewed_alias_obj) $(QFCC_DEP) - $(QFCC) $(QCFLAGS) -o $@ $(chewed_alias_obj) -chewed-alias.run: Makefile build-run - $(srcdir)/build-run $@ - -chewed_return_dat_SOURCES=chewed-return.r -chewed_return_obj=$(chewed_return_dat_SOURCES:.r=.qfo) -chewed-return.dat$(EXEEXT): $(chewed_return_obj) $(QFCC_DEP) - $(QFCC) $(QCFLAGS) -o $@ $(chewed_return_obj) -chewed-return.run: Makefile build-run - TEST_HARNESS_OPTS=--float $(srcdir)/build-run $@ - -func_expr_dat_SOURCES=func-expr.r -func_expr_obj=$(func_expr_dat_SOURCES:.r=.qfo) -func-expr.dat$(EXEEXT): $(func_expr_obj) $(QFCC_DEP) - $(QFCC) $(QCFLAGS) -o $@ $(func_expr_obj) -func-expr.run: Makefile build-run - $(srcdir)/build-run $@ - -func_static_dat_SOURCES=func-static.r -func_static_obj=$(func_static_dat_SOURCES:.r=.qfo) -func-static.dat$(EXEEXT): $(func_static_obj) $(QFCC_DEP) - $(QFCC) $(QCFLAGS) -o $@ $(func_static_obj) -func-static.run: Makefile build-run - $(srcdir)/build-run $@ - -deadbool_dat_SOURCES=deadbool.r -deadbool_obj=$(deadbool_dat_SOURCES:.r=.qfo) -deadbool.dat$(EXEEXT): $(deadbool_obj) $(QFCC_DEP) - $(QFCC) $(QCFLAGS) -o $@ $(deadbool_obj) -deadbool.run: Makefile build-run - $(srcdir)/build-run $@ - -infloop_dat_SOURCES=infloop.r -infloop_obj=$(infloop_dat_SOURCES:.r=.qfo) -infloop.dat$(EXEEXT): $(infloop_obj) $(QFCC_DEP) - $(QFCC) $(QCFLAGS) -o $@ $(infloop_obj) -infloop.run: Makefile build-run - $(srcdir)/build-run $@ - -modulo_dat_SOURCES=modulo.r -modulo_obj=$(modulo_dat_SOURCES:.r=.qfo) -modulo.dat$(EXEEXT): $(modulo_obj) $(QFCC_DEP) - $(QFCC) $(QCFLAGS) -o $@ $(modulo_obj) -modulo.run: Makefile build-run - TEST_HARNESS_OPTS=--float $(srcdir)/build-run $@ - -paramret_dat_SOURCES=paramret.r -paramret_obj=$(paramret_dat_SOURCES:.r=.qfo) -paramret.dat$(EXEEXT): $(paramret_obj) $(QFCC_DEP) - $(QFCC) $(QCFLAGS) -o $@ $(paramret_obj) -paramret.run: Makefile build-run - TEST_HARNESS_OPTS=--float $(srcdir)/build-run $@ - -return_ivar_dat_SOURCES=return-ivar.r -return_ivar_obj=$(return_ivar_dat_SOURCES:.r=.qfo) -return-ivar.dat$(EXEEXT): $(return_ivar_obj) $(QFCC_DEP) - $(QFCC) $(QCFLAGS) -o $@ $(return_ivar_obj) -return-ivar.run: Makefile build-run - $(srcdir)/build-run $@ - -sendv_dat_SOURCES=sendv.r -sendv_obj=$(sendv_dat_SOURCES:.r=.qfo) -sendv.dat$(EXEEXT): $(sendv_obj) $(QFCC_DEP) - $(QFCC) $(QCFLAGS) -o $@ $(sendv_obj) -sendv.run: Makefile build-run - $(srcdir)/build-run $@ - -state_dat_SOURCES=state.r -state_obj=$(state_dat_SOURCES:.r=.qfo) -state.dat$(EXEEXT): $(state_obj) $(QFCC_DEP) - $(QFCC) $(QCFLAGS) -o $@ $(state_obj) -state.run: Makefile build-run - $(srcdir)/build-run $@ - -structlive_dat_SOURCES=structlive.r -structlive_obj=$(structlive_dat_SOURCES:.r=.qfo) -structlive.dat$(EXEEXT): $(structlive_obj) $(QFCC_DEP) - $(QFCC) $(QCFLAGS) -o $@ $(structlive_obj) -structlive.run: Makefile build-run - $(srcdir)/build-run $@ - -structptr_dat_SOURCES=structptr.r -structptr_obj=$(structptr_dat_SOURCES:.r=.qfo) -structptr.dat$(EXEEXT): $(structptr_obj) $(QFCC_DEP) - $(QFCC) $(QCFLAGS) -o $@ $(structptr_obj) -structptr.run: Makefile build-run - $(srcdir)/build-run $@ - -vecinit_dat_SOURCES=vecinit.r -vecinit_obj=$(vecinit_dat_SOURCES:.r=.qfo) -vecinit.dat$(EXEEXT): $(vecinit_obj) $(QFCC_DEP) - $(QFCC) $(QCFLAGS) -o $@ $(vecinit_obj) -vecinit.run: Makefile build-run - $(srcdir)/build-run $@ - -while_dat_SOURCES=while.r -while_obj=$(while_dat_SOURCES:.r=.qfo) -while.dat$(EXEEXT): $(while_obj) $(QFCC_DEP) - $(QFCC) $(QCFLAGS) -o $@ $(while_obj) -while.run: Makefile build-run - $(srcdir)/build-run $@ - -voidfor_dat_SOURCES=voidfor.r -voidfor_obj=$(voidfor_dat_SOURCES:.r=.qfo) -voidfor.dat$(EXEEXT): $(voidfor_obj) $(QFCC_DEP) - $(QFCC) $(QCFLAGS) -o $@ $(voidfor_obj) -voidfor.run: Makefile build-run - $(srcdir)/build-run $@ - -include ./$(DEPDIR)/*.Qo - -EXTRA_DIST= test-bi.h build-run -CLEANFILES= *.dat *.sym *.qfo *.run diff --git a/tools/qfcc/test/Makemodule.am b/tools/qfcc/test/Makemodule.am new file mode 100644 index 000000000..daaacdcda --- /dev/null +++ b/tools/qfcc/test/Makemodule.am @@ -0,0 +1,840 @@ +QFCC_TEST_LIBS=@QFCC_TEST_LIBS@ +QFCC_TEST_DEPS=@QFCC_TEST_DEPS@ +QFCC_TEST_INCS=@QFCC_TEST_INCS@ + +test_bins=\ + tools/qfcc/test/test-defspace + +fail_bins= + +test_progs_dat=\ + tools/qfcc/test/address-cast.dat \ + tools/qfcc/test/alignment.dat \ + tools/qfcc/test/arraylife.dat \ + tools/qfcc/test/arraylife2.dat \ + tools/qfcc/test/assignchain.dat \ + tools/qfcc/test/anonstruct.dat \ + tools/qfcc/test/chewed-alias.dat \ + tools/qfcc/test/chewed-return.dat \ + tools/qfcc/test/comma-expr.dat \ + tools/qfcc/test/compound.dat \ + tools/qfcc/test/const-fold-int.dat \ + tools/qfcc/test/deadbool.dat \ + tools/qfcc/test/dealloc-nowarn.dat \ + tools/qfcc/test/dealloc-nowarn2.dat \ + tools/qfcc/test/double.dat \ + tools/qfcc/test/double-alias.dat \ + tools/qfcc/test/enum.dat \ + tools/qfcc/test/entity-struct.dat \ + tools/qfcc/test/fordecl.dat \ + tools/qfcc/test/func-expr.dat \ + tools/qfcc/test/func-expr2.dat \ + tools/qfcc/test/func-static.dat \ + tools/qfcc/test/gcd.dat \ + tools/qfcc/test/ifsuper.dat \ + tools/qfcc/test/infloop.dat \ + tools/qfcc/test/iterfunc.dat \ + tools/qfcc/test/ivar-struct-return.dat \ + tools/qfcc/test/link_order.dat \ + tools/qfcc/test/lost-use.dat \ + tools/qfcc/test/methodparams.dat \ + tools/qfcc/test/modulo.dat \ + tools/qfcc/test/nilparamret.dat \ + tools/qfcc/test/overload.dat \ + tools/qfcc/test/paramret.dat \ + tools/qfcc/test/postop.dat \ + tools/qfcc/test/ptraliasenc.dat \ + tools/qfcc/test/ptrfunc.dat \ + tools/qfcc/test/ptrstructcast.dat \ + tools/qfcc/test/quaternion.dat \ + tools/qfcc/test/return-ivar.dat \ + tools/qfcc/test/return-postop.dat \ + tools/qfcc/test/sendv.dat \ + tools/qfcc/test/state.dat \ + tools/qfcc/test/static-init.dat \ + tools/qfcc/test/struct-init-param.dat \ + tools/qfcc/test/struct-nil-init.dat \ + tools/qfcc/test/structarray.dat \ + tools/qfcc/test/structlive.dat \ + tools/qfcc/test/structptr.dat \ + tools/qfcc/test/structstruct.dat \ + tools/qfcc/test/swap.dat \ + tools/qfcc/test/temp-component.dat \ + tools/qfcc/test/triangle.dat \ + tools/qfcc/test/twice-called.dat \ + tools/qfcc/test/typedef.dat \ + tools/qfcc/test/typelinker.dat \ + tools/qfcc/test/unaryminus.dat \ + tools/qfcc/test/vecaddr.dat \ + tools/qfcc/test/vecexpr.dat \ + tools/qfcc/test/vecconst.dat \ + tools/qfcc/test/vecinit.dat \ + tools/qfcc/test/voidfor.dat \ + tools/qfcc/test/while.dat \ + tools/qfcc/test/zerolinker.dat + +fail_progs_dat= + +test_build_passes=\ + tools/qfcc/test/typeredef1.r \ + tools/qfcc/test/typeredef2.r + +test_build_errors=\ + tools/qfcc/test/classarray.r \ + tools/qfcc/test/dealloc-warn.r \ + tools/qfcc/test/dealloc-warn2.r \ + tools/qfcc/test/dealloc-warn3.r \ + tools/qfcc/test/dealloc-warn4.r \ + tools/qfcc/test/double-demote-float.r \ + tools/qfcc/test/double-demote-float-ainit.r \ + tools/qfcc/test/double-demote-float-ginit.r \ + tools/qfcc/test/double-demote-float-linit.r \ + tools/qfcc/test/double-demote-int.r \ + tools/qfcc/test/double-demote-int-ainit.r \ + tools/qfcc/test/double-demote-int-ginit.r \ + tools/qfcc/test/double-demote-int-linit.r \ + tools/qfcc/test/double-int-compare.r \ + tools/qfcc/test/double-float-compare.r + +fail_build_errors= + +test_defspace_src=\ + tools/qfcc/test/tw-defspace.c tools/qfcc/test/tw-diagnostic.c tools/qfcc/test/tw-strpool.c + +TESTS += \ + $(test_bins) \ + $(test_progs_dat:.dat=.run) \ + $(test_build_passes:.r=.run) \ + $(test_build_errors:.r=.run) +XFAIL_TESTS += \ + $(fail_bins) \ + $(fail_progs_dat:.dat=.run) \ + $(fail_build_errors:.r=.run) + +check_PROGRAMS += \ + tools/qfcc/test/test-harness \ + $(test_progs_dat) \ + $(test_bins) + +qfcc-tests: tools/qfcc/test/test-harness $(test_progs_dat) $(test_bins) + +tools_qfcc_test_test_defspace_SOURCES= tools/qfcc/test/test-defspace.c $(test_defspace_src) +tools_qfcc_test_test_defspace_LDADD= $(QFCC_LIBS) +tools_qfcc_test_test_defspace_DEPENDENCIES= $(QFCC_DEPS) + +tools_qfcc_test_test_harness_SOURCES= tools/qfcc/test/test-bi.c tools/qfcc/test/test-harness.c +tools_qfcc_test_test_harness_LDADD= $(QFCC_TEST_LIBS) +tools_qfcc_test_test_harness_DEPENDENCIES= $(QFCC_TEST_DEPS) + +qfcc_test_run_deps = Makefile tools/qfcc/test/build-run +qfcc_comp_run_deps = Makefile tools/qfcc/test/build-compile-pass-run +qfcc_fail_run_deps = Makefile tools/qfcc/test/build-compile-fail-run +tools_qfcc_test_address_cast_dat_SOURCES=tools/qfcc/test/address-cast.r +address_cast_obj=$(tools_qfcc_test_address_cast_dat_SOURCES:.r=.o) +address_cast_dep=$(call qcautodep,$(tools_qfcc_test_address_cast_dat_SOURCES)) +tools/qfcc/test/address-cast.dat$(EXEEXT): $(address_cast_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(address_cast_obj) +tools/qfcc/test/address-cast.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(address_cast_dep) # am--include-marker +r_depfiles_remade += $(address_cast_dep) + +tools_qfcc_test_alignment_dat_SOURCES=tools/qfcc/test/alignment.r +alignment_obj=$(tools_qfcc_test_alignment_dat_SOURCES:.r=.o) +alignment_dep=$(call qcautodep,$(tools_qfcc_test_alignment_dat_SOURCES)) +tools/qfcc/test/alignment.dat$(EXEEXT): $(alignment_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(alignment_obj) +tools/qfcc/test/alignment.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(alignment_dep) # am--include-marker +r_depfiles_remade += $(alignment_dep) + +tools_qfcc_test_anonstruct_dat_SOURCES=tools/qfcc/test/anonstruct.r +anonstruct_obj=$(tools_qfcc_test_anonstruct_dat_SOURCES:.r=.o) +anonstruct_dep=$(call qcautodep,$(tools_qfcc_test_anonstruct_dat_SOURCES)) +tools/qfcc/test/anonstruct.dat$(EXEEXT): $(anonstruct_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(anonstruct_obj) +tools/qfcc/test/anonstruct.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(anonstruct_dep) # am--include-marker +r_depfiles_remade += $(anonstruct_dep) + +tools_qfcc_test_arraylife_dat_SOURCES=tools/qfcc/test/arraylife.r +arraylife_obj=$(tools_qfcc_test_arraylife_dat_SOURCES:.r=.o) +arraylife_dep=$(call qcautodep,$(tools_qfcc_test_arraylife_dat_SOURCES)) +tools/qfcc/test/arraylife.dat$(EXEEXT): $(arraylife_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(arraylife_obj) +tools/qfcc/test/arraylife.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(arraylife_dep) # am--include-marker +r_depfiles_remade += $(arraylife_dep) + +tools_qfcc_test_arraylife2_dat_SOURCES=tools/qfcc/test/arraylife2.r +arraylife2_obj=$(tools_qfcc_test_arraylife2_dat_SOURCES:.r=.o) +arraylife2_dep=$(call qcautodep,$(tools_qfcc_test_arraylife2_dat_SOURCES)) +tools/qfcc/test/arraylife2.dat$(EXEEXT): $(arraylife2_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(arraylife2_obj) +tools/qfcc/test/arraylife2.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(arraylife2_dep) # am--include-marker +r_depfiles_remade += $(arraylife2_dep) + +tools_qfcc_test_assignchain_dat_SOURCES=tools/qfcc/test/assignchain.r +assignchain_obj=$(tools_qfcc_test_assignchain_dat_SOURCES:.r=.o) +assignchain_dep=$(call qcautodep,$(tools_qfcc_test_assignchain_dat_SOURCES)) +tools/qfcc/test/assignchain.dat$(EXEEXT): $(assignchain_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(assignchain_obj) +tools/qfcc/test/assignchain.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(assignchain_dep) # am--include-marker +r_depfiles_remade += $(assignchain_dep) + +tools_qfcc_test_chewed_alias_dat_SOURCES=tools/qfcc/test/chewed-alias.r +chewed_alias_obj=$(tools_qfcc_test_chewed_alias_dat_SOURCES:.r=.o) +chewed_alias_dep=$(call qcautodep,$(tools_qfcc_test_chewed_alias_dat_SOURCES)) +tools/qfcc/test/chewed-alias.dat$(EXEEXT): $(chewed_alias_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(chewed_alias_obj) +tools/qfcc/test/chewed-alias.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(chewed_alias_dep) # am--include-marker +r_depfiles_remade += $(chewed_alias_dep) + +tools_qfcc_test_chewed_return_dat_SOURCES=tools/qfcc/test/chewed-return.r +chewed_return_obj=$(tools_qfcc_test_chewed_return_dat_SOURCES:.r=.o) +chewed_return_dep=$(call qcautodep,$(tools_qfcc_test_chewed_return_dat_SOURCES)) +tools/qfcc/test/chewed-return.dat$(EXEEXT): $(chewed_return_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -Ctarget=v6 -o $@ $(chewed_return_obj) +tools/qfcc/test/chewed-return.run: $(qfcc_test_run_deps) + @TEST_HARNESS_OPTS=--float $(top_srcdir)/tools/qfcc/test/build-run $@ +include $(chewed_return_dep) # am--include-marker +r_depfiles_remade += $(chewed_return_dep) + +tools_qfcc_test_comma_expr_dat_SOURCES=tools/qfcc/test/comma-expr.r +comma_expr_obj=$(tools_qfcc_test_comma_expr_dat_SOURCES:.r=.o) +comma_expr_dep=$(call qcautodep,$(tools_qfcc_test_comma_expr_dat_SOURCES)) +tools/qfcc/test/comma-expr.dat$(EXEEXT): $(comma_expr_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(comma_expr_obj) +tools/qfcc/test/comma-expr.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(comma_expr_dep) # am--include-marker +r_depfiles_remade += $(comma_expr_dep) + +tools_qfcc_test_compound_dat_SOURCES=tools/qfcc/test/compound.r +compound_obj=$(tools_qfcc_test_compound_dat_SOURCES:.r=.o) +compound_dep=$(call qcautodep,$(tools_qfcc_test_compound_dat_SOURCES)) +tools/qfcc/test/compound.dat$(EXEEXT): $(compound_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(compound_obj) +tools/qfcc/test/compound.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(compound_dep) # am--include-marker +r_depfiles_remade += $(compound_dep) + +tools_qfcc_test_const_fold_int_dat_SOURCES=tools/qfcc/test/const-fold-int.r +const_fold_int_obj=$(tools_qfcc_test_const_fold_int_dat_SOURCES:.r=.o) +const_fold_int_dep=$(call qcautodep,$(tools_qfcc_test_const_fold_int_dat_SOURCES)) +tools/qfcc/test/const-fold-int.dat$(EXEEXT): $(const_fold_int_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(const_fold_int_obj) +tools/qfcc/test/const-fold-int.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(const_fold_int_dep) # am--include-marker +r_depfiles_remade += $(const_fold_int_dep) + +tools_qfcc_test_deadbool_dat_SOURCES=tools/qfcc/test/deadbool.r +deadbool_obj=$(tools_qfcc_test_deadbool_dat_SOURCES:.r=.o) +deadbool_dep=$(call qcautodep,$(tools_qfcc_test_deadbool_dat_SOURCES)) +tools/qfcc/test/deadbool.dat$(EXEEXT): $(deadbool_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(deadbool_obj) +tools/qfcc/test/deadbool.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(deadbool_dep) # am--include-marker +r_depfiles_remade += $(deadbool_dep) + +tools_qfcc_test_dealloc_nowarn_dat_SOURCES=tools/qfcc/test/dealloc-nowarn.r +dealloc_nowarn_obj=$(tools_qfcc_test_dealloc_nowarn_dat_SOURCES:.r=.o) +dealloc_nowarn_dep=$(call qcautodep,$(tools_qfcc_test_dealloc_nowarn_dat_SOURCES)) +tools/qfcc/test/dealloc-nowarn.dat$(EXEEXT): $(dealloc_nowarn_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(dealloc_nowarn_obj) +tools/qfcc/test/dealloc-nowarn.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(dealloc_nowarn_dep) # am--include-marker +r_depfiles_remade += $(dealloc_nowarn_dep) + +tools_qfcc_test_dealloc_nowarn2_dat_SOURCES=tools/qfcc/test/dealloc-nowarn2.r +dealloc_nowarn2_obj=$(tools_qfcc_test_dealloc_nowarn2_dat_SOURCES:.r=.o) +dealloc_nowarn2_dep=$(call qcautodep,$(tools_qfcc_test_dealloc_nowarn2_dat_SOURCES)) +tools/qfcc/test/dealloc-nowarn2.dat$(EXEEXT): $(dealloc_nowarn2_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(dealloc_nowarn2_obj) +tools/qfcc/test/dealloc-nowarn2.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(dealloc_nowarn2_dep) # am--include-marker +r_depfiles_remade += $(dealloc_nowarn2_dep) + +tools_qfcc_test_double_dat_SOURCES=tools/qfcc/test/double.r +double_obj=$(tools_qfcc_test_double_dat_SOURCES:.r=.o) +double_dep=$(call qcautodep,$(tools_qfcc_test_double_dat_SOURCES)) +tools/qfcc/test/double.dat$(EXEEXT): $(double_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(double_obj) +tools/qfcc/test/double.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(double_dep) # am--include-marker +r_depfiles_remade += $(double_dep) + +tools_qfcc_test_double_alias_dat_SOURCES=tools/qfcc/test/double-alias.r +double_alias_obj=$(tools_qfcc_test_double_alias_dat_SOURCES:.r=.o) +double_alias_dep=$(call qcautodep,$(tools_qfcc_test_double_alias_dat_SOURCES)) +tools/qfcc/test/double-alias.dat$(EXEEXT): $(double_alias_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(double_alias_obj) +tools/qfcc/test/double-alias.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(double_alias_dep) # am--include-marker +r_depfiles_remade += $(double_alias_dep) + +tools/qfcc/test/classarray.run$(EXEEXT): tools/qfcc/test/classarray.r $(qfcc_fail_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-compile-fail-run $@ $(QFCC) $(QCFLAGS) $< + +tools/qfcc/test/dealloc-warn.run$(EXEEXT): tools/qfcc/test/dealloc-warn.r $(qfcc_fail_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-compile-fail-run $@ $(QFCC) $(QCFLAGS) $< + +tools/qfcc/test/dealloc-warn2.run$(EXEEXT): tools/qfcc/test/dealloc-warn2.r $(qfcc_fail_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-compile-fail-run $@ $(QFCC) $(QCFLAGS) $< + +tools/qfcc/test/dealloc-warn3.run$(EXEEXT): tools/qfcc/test/dealloc-warn3.r $(qfcc_fail_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-compile-fail-run $@ $(QFCC) $(QCFLAGS) $< + +tools/qfcc/test/dealloc-warn4.run$(EXEEXT): tools/qfcc/test/dealloc-warn4.r $(qfcc_fail_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-compile-fail-run $@ $(QFCC) $(QCFLAGS) $< + +tools/qfcc/test/double-demote-int.run$(EXEEXT): tools/qfcc/test/double-demote-int.r $(qfcc_fail_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-compile-fail-run $@ $(QFCC) $(QCFLAGS) $< + +tools/qfcc/test/double-demote-float.run$(EXEEXT): tools/qfcc/test/double-demote-float.r $(qfcc_fail_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-compile-fail-run $@ $(QFCC) $(QCFLAGS) $< + +tools/qfcc/test/double-demote-int-ainit.run$(EXEEXT): tools/qfcc/test/double-demote-int-ainit.r $(qfcc_fail_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-compile-fail-run $@ $(QFCC) $(QCFLAGS) $< + +tools/qfcc/test/double-demote-float-ainit.run$(EXEEXT): tools/qfcc/test/double-demote-float-ainit.r $(qfcc_fail_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-compile-fail-run $@ $(QFCC) $(QCFLAGS) $< + +tools/qfcc/test/double-demote-int-ginit.run$(EXEEXT): tools/qfcc/test/double-demote-int-ginit.r $(qfcc_fail_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-compile-fail-run $@ $(QFCC) $(QCFLAGS) $< + +tools/qfcc/test/double-demote-float-ginit.run$(EXEEXT): tools/qfcc/test/double-demote-float-ginit.r $(qfcc_fail_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-compile-fail-run $@ $(QFCC) $(QCFLAGS) $< + +tools/qfcc/test/double-demote-int-linit.run$(EXEEXT): tools/qfcc/test/double-demote-int-linit.r $(qfcc_fail_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-compile-fail-run $@ $(QFCC) $(QCFLAGS) $< + +tools/qfcc/test/double-demote-float-linit.run$(EXEEXT): tools/qfcc/test/double-demote-float-linit.r $(qfcc_fail_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-compile-fail-run $@ $(QFCC) $(QCFLAGS) $< + +tools/qfcc/test/double-int-compare.run$(EXEEXT): tools/qfcc/test/double-int-compare.r $(qfcc_fail_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-compile-fail-run $@ $(QFCC) $(QCFLAGS) $< + +tools/qfcc/test/double-float-compare.run$(EXEEXT): tools/qfcc/test/double-float-compare.r $(qfcc_fail_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-compile-fail-run $@ $(QFCC) $(QCFLAGS) $< + +tools/qfcc/test/typeredef1.run$(EXEEXT): tools/qfcc/test/typeredef1.r $(qfcc_comp_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-compile-pass-run $@ $(QFCC) $(QCFLAGS) $< + +tools/qfcc/test/typeredef2.run$(EXEEXT): tools/qfcc/test/typeredef2.r $(qfcc_comp_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-compile-pass-run $@ $(QFCC) $(QCFLAGS) $< + +tools_qfcc_test_enum_dat_SOURCES=tools/qfcc/test/enum.r +enum_obj=$(tools_qfcc_test_enum_dat_SOURCES:.r=.o) +enum_dep=$(call qcautodep,$(tools_qfcc_test_enum_dat_SOURCES)) +tools/qfcc/test/enum.dat$(EXEEXT): $(enum_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(enum_obj) +tools/qfcc/test/enum.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(enum_dep) # am--include-marker +r_depfiles_remade += $(enum_dep) + +tools_qfcc_test_entity_struct_dat_SOURCES=tools/qfcc/test/entity-struct.r +entity_struct_obj=$(tools_qfcc_test_entity_struct_dat_SOURCES:.r=.o) +entity_struct_dep=$(call qcautodep,$(tools_qfcc_test_entity_struct_dat_SOURCES)) +tools/qfcc/test/entity-struct.dat$(EXEEXT): $(entity_struct_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(entity_struct_obj) +tools/qfcc/test/entity-struct.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(entity_struct_dep) # am--include-marker +r_depfiles_remade += $(entity_struct_dep) + +tools_qfcc_test_fordecl_dat_SOURCES=tools/qfcc/test/fordecl.r +fordecl_obj=$(tools_qfcc_test_fordecl_dat_SOURCES:.r=.o) +fordecl_dep=$(call qcautodep,$(tools_qfcc_test_fordecl_dat_SOURCES)) +tools/qfcc/test/fordecl.dat$(EXEEXT): $(fordecl_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(fordecl_obj) +tools/qfcc/test/fordecl.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(fordecl_dep) # am--include-marker +r_depfiles_remade += $(fordecl_dep) + +tools_qfcc_test_func_expr_dat_SOURCES=tools/qfcc/test/func-expr.r +func_expr_obj=$(tools_qfcc_test_func_expr_dat_SOURCES:.r=.o) +func_expr_dep=$(call qcautodep,$(tools_qfcc_test_func_expr_dat_SOURCES)) +tools/qfcc/test/func-expr.dat$(EXEEXT): $(func_expr_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(func_expr_obj) +tools/qfcc/test/func-expr.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(func_expr_dep) # am--include-marker +r_depfiles_remade += $(func_expr_dep) + +tools_qfcc_test_func_expr2_dat_SOURCES=tools/qfcc/test/func-expr2.r +func_expr2_obj=$(tools_qfcc_test_func_expr2_dat_SOURCES:.r=.o) +func_expr2_dep=$(call qcautodep,$(tools_qfcc_test_func_expr2_dat_SOURCES)) +tools/qfcc/test/func-expr2.dat$(EXEEXT): $(func_expr2_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(func_expr2_obj) +tools/qfcc/test/func-expr2.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(func_expr2_dep) # am--include-marker +r_depfiles_remade += $(func_expr2_dep) + +tools_qfcc_test_func_static_dat_SOURCES=tools/qfcc/test/func-static.r +func_static_obj=$(tools_qfcc_test_func_static_dat_SOURCES:.r=.o) +func_static_dep=$(call qcautodep,$(tools_qfcc_test_func_static_dat_SOURCES)) +tools/qfcc/test/func-static.dat$(EXEEXT): $(func_static_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(func_static_obj) +tools/qfcc/test/func-static.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(func_static_dep) # am--include-marker +r_depfiles_remade += $(func_static_dep) + +tools_qfcc_test_gcd_dat_SOURCES=tools/qfcc/test/gcd.pas +gcd_obj=$(tools_qfcc_test_gcd_dat_SOURCES:.pas=.o) +gcd_dep=$(call qcautodep,$(tools_qfcc_test_gcd_dat_SOURCES)) +tools/qfcc/test/gcd.dat$(EXEEXT): $(gcd_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(gcd_obj) +tools/qfcc/test/gcd.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(gcd_dep) # am--include-marker +pas_depfiles_remade += $(gcd_dep) + +tools_qfcc_test_ifsuper_dat_SOURCES=tools/qfcc/test/ifsuper.r +ifsuper_obj=$(tools_qfcc_test_ifsuper_dat_SOURCES:.r=.o) +ifsuper_dep=$(call qcautodep,$(tools_qfcc_test_ifsuper_dat_SOURCES)) +tools/qfcc/test/ifsuper.dat$(EXEEXT): $(ifsuper_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) --advanced -o $@ $(ifsuper_obj) +tools/qfcc/test/ifsuper.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(ifsuper_dep) # am--include-marker +r_depfiles_remade += $(ifsuper_dep) + +tools_qfcc_test_infloop_dat_SOURCES=tools/qfcc/test/infloop.r +infloop_obj=$(tools_qfcc_test_infloop_dat_SOURCES:.r=.o) +infloop_dep=$(call qcautodep,$(tools_qfcc_test_infloop_dat_SOURCES)) +tools/qfcc/test/infloop.dat$(EXEEXT): $(infloop_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(infloop_obj) +tools/qfcc/test/infloop.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(infloop_dep) # am--include-marker +r_depfiles_remade += $(infloop_dep) + +tools_qfcc_test_iterfunc_dat_SOURCES=tools/qfcc/test/iterfunc.r +iterfunc_obj=$(tools_qfcc_test_iterfunc_dat_SOURCES:.r=.o) +iterfunc_dep=$(call qcautodep,$(tools_qfcc_test_iterfunc_dat_SOURCES)) +tools/qfcc/test/iterfunc.dat$(EXEEXT): $(iterfunc_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(iterfunc_obj) +tools/qfcc/test/iterfunc.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(iterfunc_dep) # am--include-marker +r_depfiles_remade += $(iterfunc_dep) + +tools_qfcc_test_ivar_struct_return_dat_SOURCES=tools/qfcc/test/ivar-struct-return.r +ivar_struct_return_obj=$(tools_qfcc_test_ivar_struct_return_dat_SOURCES:.r=.o) +ivar_struct_return_dep=$(call qcautodep,$(tools_qfcc_test_ivar_struct_return_dat_SOURCES)) +tools/qfcc/test/ivar-struct-return.dat$(EXEEXT): $(ivar_struct_return_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(ivar_struct_return_obj) +tools/qfcc/test/ivar-struct-return.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(ivar_struct_return_dep) # am--include-marker +r_depfiles_remade += $(ivar_struct_return_dep) + +tools_qfcc_test_link_order_dat_SOURCES=tools/qfcc/test/link_order.r +link_order_obj=$(tools_qfcc_test_link_order_dat_SOURCES:.r=.o) +link_order_dep=$(call qcautodep,$(tools_qfcc_test_link_order_dat_SOURCES)) +tools/qfcc/test/link_order.dat$(EXEEXT): $(link_order_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(link_order_obj) +tools/qfcc/test/link_order.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(link_order_dep) # am--include-marker +r_depfiles_remade += $(link_order_dep) + +tools_qfcc_test_lost_use_dat_SOURCES=tools/qfcc/test/lost-use.r +lost_use_obj=$(tools_qfcc_test_lost_use_dat_SOURCES:.r=.o) +lost_use_dep=$(call qcautodep,$(tools_qfcc_test_lost_use_dat_SOURCES)) +tools/qfcc/test/lost-use.dat$(EXEEXT): $(lost_use_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(lost_use_obj) +tools/qfcc/test/lost-use.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(lost_use_dep) # am--include-marker +r_depfiles_remade += $(lost_use_dep) + +tools_qfcc_test_methodparams_dat_SOURCES=tools/qfcc/test/methodparams.r +methodparams_obj=$(tools_qfcc_test_methodparams_dat_SOURCES:.r=.o) +methodparams_dep=$(call qcautodep,$(tools_qfcc_test_methodparams_dat_SOURCES)) +tools/qfcc/test/methodparams.dat$(EXEEXT): $(methodparams_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(methodparams_obj) +tools/qfcc/test/methodparams.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(methodparams_dep) # am--include-marker +r_depfiles_remade += $(methodparams_dep) + +tools_qfcc_test_modulo_dat_SOURCES=tools/qfcc/test/modulo.r +modulo_obj=$(tools_qfcc_test_modulo_dat_SOURCES:.r=.o) +modulo_dep=$(call qcautodep,$(tools_qfcc_test_modulo_dat_SOURCES)) +tools/qfcc/test/modulo.dat$(EXEEXT): $(modulo_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(modulo_obj) +tools/qfcc/test/modulo.run: $(qfcc_test_run_deps) + @TEST_HARNESS_OPTS=--float $(top_srcdir)/tools/qfcc/test/build-run $@ +include $(modulo_dep) # am--include-marker +r_depfiles_remade += $(modulo_dep) + +tools_qfcc_test_nilparamret_dat_SOURCES=tools/qfcc/test/nilparamret.r +nilparamret_obj=$(tools_qfcc_test_nilparamret_dat_SOURCES:.r=.o) +nilparamret_dep=$(call qcautodep,$(tools_qfcc_test_nilparamret_dat_SOURCES)) +tools/qfcc/test/nilparamret.dat$(EXEEXT): $(nilparamret_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(nilparamret_obj) +tools/qfcc/test/nilparamret.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(nilparamret_dep) # am--include-marker +r_depfiles_remade += $(nilparamret_dep) + +tools_qfcc_test_overload_dat_SOURCES=tools/qfcc/test/overload.r +overload_obj=$(tools_qfcc_test_overload_dat_SOURCES:.r=.o) +overload_dep=$(call qcautodep,$(tools_qfcc_test_overload_dat_SOURCES)) +tools/qfcc/test/overload.dat$(EXEEXT): $(overload_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(overload_obj) +tools/qfcc/test/overload.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(overload_dep) # am--include-marker +r_depfiles_remade += $(overload_dep) + +tools_qfcc_test_paramret_dat_SOURCES=tools/qfcc/test/paramret.r +paramret_obj=$(tools_qfcc_test_paramret_dat_SOURCES:.r=.o) +paramret_dep=$(call qcautodep,$(tools_qfcc_test_paramret_dat_SOURCES)) +tools/qfcc/test/paramret.dat$(EXEEXT): $(paramret_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(paramret_obj) +tools/qfcc/test/paramret.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(paramret_dep) # am--include-marker +r_depfiles_remade += $(paramret_dep) + +tools_qfcc_test_postop_dat_SOURCES=tools/qfcc/test/postop.r +postop_obj=$(tools_qfcc_test_postop_dat_SOURCES:.r=.o) +postop_dep=$(call qcautodep,$(tools_qfcc_test_postop_dat_SOURCES)) +tools/qfcc/test/postop.dat$(EXEEXT): $(postop_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(postop_obj) +tools/qfcc/test/postop.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(postop_dep) # am--include-marker +r_depfiles_remade += $(postop_dep) + +tools_qfcc_test_ptraliasenc_dat_SOURCES=tools/qfcc/test/ptraliasenc.r +ptraliasenc_obj=$(tools_qfcc_test_ptraliasenc_dat_SOURCES:.r=.o) +ptraliasenc_dep=$(call qcautodep,$(tools_qfcc_test_ptraliasenc_dat_SOURCES)) +tools/qfcc/test/ptraliasenc.dat$(EXEEXT): $(ptraliasenc_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(ptraliasenc_obj) +tools/qfcc/test/ptraliasenc.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(ptraliasenc_dep) # am--include-marker +r_depfiles_remade += $(ptraliasenc_dep) + +tools_qfcc_test_ptrfunc_dat_SOURCES=tools/qfcc/test/ptrfunc.r +ptrfunc_obj=$(tools_qfcc_test_ptrfunc_dat_SOURCES:.r=.o) +ptrfunc_dep=$(call qcautodep,$(tools_qfcc_test_ptrfunc_dat_SOURCES)) +tools/qfcc/test/ptrfunc.dat$(EXEEXT): $(ptrfunc_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(ptrfunc_obj) +tools/qfcc/test/ptrfunc.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(ptrfunc_dep) # am--include-marker +r_depfiles_remade += $(ptrfunc_dep) + +tools_qfcc_test_ptrstructcast_dat_SOURCES=tools/qfcc/test/ptrstructcast.r +ptrstructcast_obj=$(tools_qfcc_test_ptrstructcast_dat_SOURCES:.r=.o) +ptrstructcast_dep=$(call qcautodep,$(tools_qfcc_test_ptrstructcast_dat_SOURCES)) +tools/qfcc/test/ptrstructcast.dat$(EXEEXT): $(ptrstructcast_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(ptrstructcast_obj) +tools/qfcc/test/ptrstructcast.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(ptrstructcast_dep) # am--include-marker +r_depfiles_remade += $(ptrstructcast_dep) + +tools_qfcc_test_quaternion_dat_SOURCES=tools/qfcc/test/quaternion.r +quaternion_obj=$(tools_qfcc_test_quaternion_dat_SOURCES:.r=.o) +quaternion_dep=$(call qcautodep,$(tools_qfcc_test_quaternion_dat_SOURCES)) +tools/qfcc/test/quaternion.dat$(EXEEXT): $(quaternion_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(quaternion_obj) +tools/qfcc/test/quaternion.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(quaternion_dep) # am--include-marker +r_depfiles_remade += $(quaternion_dep) + +tools_qfcc_test_return_ivar_dat_SOURCES=tools/qfcc/test/return-ivar.r +return_ivar_obj=$(tools_qfcc_test_return_ivar_dat_SOURCES:.r=.o) +return_ivar_dep=$(call qcautodep,$(tools_qfcc_test_return_ivar_dat_SOURCES)) +tools/qfcc/test/return-ivar.dat$(EXEEXT): $(return_ivar_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(return_ivar_obj) +tools/qfcc/test/return-ivar.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(return_ivar_dep) # am--include-marker +r_depfiles_remade += $(return_ivar_dep) + +tools_qfcc_test_return_postop_dat_SOURCES=tools/qfcc/test/return-postop.r +return_postop_obj=$(tools_qfcc_test_return_postop_dat_SOURCES:.r=.o) +return_postop_dep=$(call qcautodep,$(tools_qfcc_test_return_postop_dat_SOURCES)) +tools/qfcc/test/return-postop.dat$(EXEEXT): $(return_postop_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(return_postop_obj) +tools/qfcc/test/return-postop.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(return_postop_dep) # am--include-marker +r_depfiles_remade += $(return_postop_dep) + +tools_qfcc_test_sendv_dat_SOURCES=tools/qfcc/test/sendv.r +sendv_obj=$(tools_qfcc_test_sendv_dat_SOURCES:.r=.o) +sendv_dep=$(call qcautodep,$(tools_qfcc_test_sendv_dat_SOURCES)) +tools/qfcc/test/sendv.dat$(EXEEXT): $(sendv_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(sendv_obj) +tools/qfcc/test/sendv.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(sendv_dep) # am--include-marker +r_depfiles_remade += $(sendv_dep) + +tools_qfcc_test_state_dat_SOURCES=tools/qfcc/test/state.r +state_obj=$(tools_qfcc_test_state_dat_SOURCES:.r=.o) +state_dep=$(call qcautodep,$(tools_qfcc_test_state_dat_SOURCES)) +tools/qfcc/test/state.dat$(EXEEXT): $(state_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(state_obj) +tools/qfcc/test/state.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(state_dep) # am--include-marker +r_depfiles_remade += $(state_dep) + +tools_qfcc_test_static_init_dat_SOURCES=tools/qfcc/test/static-init.r +static_init_obj=$(tools_qfcc_test_static_init_dat_SOURCES:.r=.o) +static_init_dep=$(call qcautodep,$(tools_qfcc_test_static_init_dat_SOURCES)) +tools/qfcc/test/static-init.dat$(EXEEXT): $(static_init_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(static_init_obj) +tools/qfcc/test/static-init.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(static_init_dep) # am--include-marker +r_depfiles_remade += $(static_init_dep) + +tools_qfcc_test_struct_init_param_dat_SOURCES=tools/qfcc/test/struct-init-param.r +struct_init_param_obj=$(tools_qfcc_test_struct_init_param_dat_SOURCES:.r=.o) +struct_init_param_dep=$(call qcautodep,$(tools_qfcc_test_struct_init_param_dat_SOURCES)) +tools/qfcc/test/struct-init-param.dat$(EXEEXT): $(struct_init_param_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(struct_init_param_obj) +tools/qfcc/test/struct-init-param.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(struct_init_param_dep) # am--include-marker +r_depfiles_remade += $(struct_init_param_dep) + +tools_qfcc_test_struct_nil_init_dat_SOURCES=tools/qfcc/test/struct-nil-init.r +struct_nil_init_obj=$(tools_qfcc_test_struct_nil_init_dat_SOURCES:.r=.o) +struct_nil_init_dep=$(call qcautodep,$(tools_qfcc_test_struct_nil_init_dat_SOURCES)) +tools/qfcc/test/struct-nil-init.dat$(EXEEXT): $(struct_nil_init_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(struct_nil_init_obj) +tools/qfcc/test/struct-nil-init.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(struct_nil_init_dep) # am--include-marker +r_depfiles_remade += $(struct_nil_init_dep) + +tools_qfcc_test_structarray_dat_SOURCES=tools/qfcc/test/structarray.r +structarray_obj=$(tools_qfcc_test_structarray_dat_SOURCES:.r=.o) +structarray_dep=$(call qcautodep,$(tools_qfcc_test_structarray_dat_SOURCES)) +tools/qfcc/test/structarray.dat$(EXEEXT): $(structarray_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(structarray_obj) +tools/qfcc/test/structarray.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(structarray_dep) # am--include-marker +r_depfiles_remade += $(structarray_dep) + +tools_qfcc_test_structlive_dat_SOURCES=tools/qfcc/test/structlive.r +structlive_obj=$(tools_qfcc_test_structlive_dat_SOURCES:.r=.o) +structlive_dep=$(call qcautodep,$(tools_qfcc_test_structlive_dat_SOURCES)) +tools/qfcc/test/structlive.dat$(EXEEXT): $(structlive_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(structlive_obj) +tools/qfcc/test/structlive.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(structlive_dep) # am--include-marker +r_depfiles_remade += $(structlive_dep) + +tools_qfcc_test_structptr_dat_SOURCES=tools/qfcc/test/structptr.r +structptr_obj=$(tools_qfcc_test_structptr_dat_SOURCES:.r=.o) +structptr_dep=$(call qcautodep,$(tools_qfcc_test_structptr_dat_SOURCES)) +tools/qfcc/test/structptr.dat$(EXEEXT): $(structptr_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(structptr_obj) +tools/qfcc/test/structptr.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(structptr_dep) # am--include-marker +r_depfiles_remade += $(structptr_dep) + +tools_qfcc_test_structstruct_dat_SOURCES=tools/qfcc/test/structstruct.r +structstruct_obj=$(tools_qfcc_test_structstruct_dat_SOURCES:.r=.o) +structstruct_dep=$(call qcautodep,$(tools_qfcc_test_structstruct_dat_SOURCES)) +tools/qfcc/test/structstruct.dat$(EXEEXT): $(structstruct_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(structstruct_obj) +tools/qfcc/test/structstruct.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(structstruct_dep) # am--include-marker +r_depfiles_remade += $(structstruct_dep) + +tools_qfcc_test_swap_dat_SOURCES=tools/qfcc/test/swap.r +swap_obj=$(tools_qfcc_test_swap_dat_SOURCES:.r=.o) +swap_dep=$(call qcautodep,$(tools_qfcc_test_swap_dat_SOURCES)) +tools/qfcc/test/swap.dat$(EXEEXT): $(swap_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(swap_obj) +tools/qfcc/test/swap.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(swap_dep) # am--include-marker +r_depfiles_remade += $(swap_dep) + +tools_qfcc_test_temp_component_dat_SOURCES=tools/qfcc/test/temp-component.r +temp_component_obj=$(tools_qfcc_test_temp_component_dat_SOURCES:.r=.o) +temp_component_dep=$(call qcautodep,$(tools_qfcc_test_temp_component_dat_SOURCES)) +tools/qfcc/test/temp-component.dat$(EXEEXT): $(temp_component_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(temp_component_obj) +tools/qfcc/test/temp-component.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(temp_component_dep) # am--include-marker +r_depfiles_remade += $(temp_component_dep) + +tools_qfcc_test_triangle_dat_SOURCES=tools/qfcc/test/triangle.r +triangle_obj=$(tools_qfcc_test_triangle_dat_SOURCES:.r=.o) +triangle_dep=$(call qcautodep,$(tools_qfcc_test_triangle_dat_SOURCES)) +tools/qfcc/test/triangle.dat$(EXEEXT): $(triangle_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(triangle_obj) +tools/qfcc/test/triangle.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ 100000 100000 1.00005 50002.4961 +include $(triangle_dep) # am--include-marker +r_depfiles_remade += $(triangle_dep) + +tools_qfcc_test_twice_called_dat_SOURCES=tools/qfcc/test/twice-called.r +twice_called_obj=$(tools_qfcc_test_twice_called_dat_SOURCES:.r=.o) +twice_called_dep=$(call qcautodep,$(tools_qfcc_test_twice_called_dat_SOURCES)) +tools/qfcc/test/twice-called.dat$(EXEEXT): $(twice_called_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(twice_called_obj) +tools/qfcc/test/twice-called.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ 100000 100000 1.00005 50002.4961 +include $(twice_called_dep) # am--include-marker +r_depfiles_remade += $(twice_called_dep) + +tools_qfcc_test_typedef_dat_SOURCES=tools/qfcc/test/typedef.r +typedef_obj=$(tools_qfcc_test_typedef_dat_SOURCES:.r=.o) +typedef_dep=$(call qcautodep,$(tools_qfcc_test_typedef_dat_SOURCES)) +tools/qfcc/test/typedef.dat$(EXEEXT): $(typedef_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(typedef_obj) +tools/qfcc/test/typedef.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(typedef_dep) # am--include-marker +r_depfiles_remade += $(typedef_dep) + +tools_qfcc_test_typelinker_dat_SOURCES=tools/qfcc/test/typelinker_a.r tools/qfcc/test/typelinker_b.r +typelinker_obj=$(tools_qfcc_test_typelinker_dat_SOURCES:.r=.o) +typelinker_dep=$(call qcautodep,$(tools_qfcc_test_typelinker_dat_SOURCES)) +tools/qfcc/test/typelinker.dat$(EXEEXT): $(typelinker_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(typelinker_obj) +tools/qfcc/test/typelinker.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(typelinker_dep) # am--include-marker +r_depfiles_remade += $(typelinker_dep) + +tools_qfcc_test_unaryminus_dat_SOURCES=tools/qfcc/test/unaryminus.r +unaryminus_obj=$(tools_qfcc_test_unaryminus_dat_SOURCES:.r=.o) +unaryminus_dep=$(call qcautodep,$(tools_qfcc_test_unaryminus_dat_SOURCES)) +tools/qfcc/test/unaryminus.dat$(EXEEXT): $(unaryminus_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(unaryminus_obj) +tools/qfcc/test/unaryminus.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(unaryminus_dep) # am--include-marker +r_depfiles_remade += $(unaryminus_dep) + +tools_qfcc_test_vecaddr_dat_SOURCES=tools/qfcc/test/vecaddr.r +vecaddr_obj=$(tools_qfcc_test_vecaddr_dat_SOURCES:.r=.o) +vecaddr_dep=$(call qcautodep,$(tools_qfcc_test_vecaddr_dat_SOURCES)) +tools/qfcc/test/vecaddr.dat$(EXEEXT): $(vecaddr_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(vecaddr_obj) +tools/qfcc/test/vecaddr.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(vecaddr_dep) # am--include-marker +r_depfiles_remade += $(vecaddr_dep) + +tools_qfcc_test_vecexpr_dat_SOURCES=tools/qfcc/test/vecexpr.r +vecexpr_obj=$(tools_qfcc_test_vecexpr_dat_SOURCES:.r=.o) +vecexpr_dep=$(call qcautodep,$(tools_qfcc_test_vecexpr_dat_SOURCES)) +tools/qfcc/test/vecexpr.dat$(EXEEXT): $(vecexpr_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(vecexpr_obj) +tools/qfcc/test/vecexpr.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(vecexpr_dep) # am--include-marker +r_depfiles_remade += $(vecexpr_dep) + +tools_qfcc_test_vecconst_dat_SOURCES=tools/qfcc/test/vecconst.r +vecconst_obj=$(tools_qfcc_test_vecconst_dat_SOURCES:.r=.o) +vecconst_dep=$(call qcautodep,$(tools_qfcc_test_vecconst_dat_SOURCES)) +tools/qfcc/test/vecconst.dat$(EXEEXT): $(vecconst_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(vecconst_obj) +tools/qfcc/test/vecconst.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(vecconst_dep) # am--include-marker +r_depfiles_remade += $(vecconst_dep) + +tools_qfcc_test_vecinit_dat_SOURCES=tools/qfcc/test/vecinit.r +vecinit_obj=$(tools_qfcc_test_vecinit_dat_SOURCES:.r=.o) +vecinit_dep=$(call qcautodep,$(tools_qfcc_test_vecinit_dat_SOURCES)) +tools/qfcc/test/vecinit.dat$(EXEEXT): $(vecinit_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(vecinit_obj) +tools/qfcc/test/vecinit.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(vecinit_dep) # am--include-marker +r_depfiles_remade += $(vecinit_dep) + +tools_qfcc_test_voidfor_dat_SOURCES=tools/qfcc/test/voidfor.r +voidfor_obj=$(tools_qfcc_test_voidfor_dat_SOURCES:.r=.o) +voidfor_dep=$(call qcautodep,$(tools_qfcc_test_voidfor_dat_SOURCES)) +tools/qfcc/test/voidfor.dat$(EXEEXT): $(voidfor_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(voidfor_obj) +tools/qfcc/test/voidfor.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(voidfor_dep) # am--include-marker +r_depfiles_remade += $(voidfor_dep) + +tools_qfcc_test_while_dat_SOURCES=tools/qfcc/test/while.r +while_obj=$(tools_qfcc_test_while_dat_SOURCES:.r=.o) +while_dep=$(call qcautodep,$(tools_qfcc_test_while_dat_SOURCES)) +tools/qfcc/test/while.dat$(EXEEXT): $(while_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(while_obj) +tools/qfcc/test/while.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(while_dep) # am--include-marker +r_depfiles_remade += $(while_dep) + +tools_qfcc_test_zerolinker_dat_SOURCES=tools/qfcc/test/zerolinker.r +zerolinker_obj=$(tools_qfcc_test_zerolinker_dat_SOURCES:.r=.o) +zerolinker_dep=$(call qcautodep,$(tools_qfcc_test_zerolinker_dat_SOURCES)) +tools/qfcc/test/zerolinker.dat$(EXEEXT): $(zerolinker_obj) $(QFCC_DEP) + $(V_QFCCLD)$(QLINK) -o $@ $(zerolinker_obj) +tools/qfcc/test/zerolinker.run: $(qfcc_test_run_deps) + @$(top_srcdir)/tools/qfcc/test/build-run $@ +include $(zerolinker_dep) # am--include-marker +r_depfiles_remade += $(zerolinker_dep) + +EXTRA_DIST += \ + $(test_build_errors) \ + $(test_build_passes) \ + tools/qfcc/test/build-compile-pass-run \ + tools/qfcc/test/build-compile-fail-run \ + tools/qfcc/test/test-bi.h \ + tools/qfcc/test/build-run \ + tools/qfcc/test/test-defspace.h \ + tools/qfcc/test/test-harness.h \ + tools/qfcc/test/typelinker.h + +CLEANFILES += \ + tools/qfcc/test/*.dat \ + tools/qfcc/test/*.sym \ + tools/qfcc/test/*.run \ + tools/qfcc/test/*.frame \ + tools/qfcc/test/*.log \ + tools/qfcc/test/*.trs diff --git a/tools/qfcc/test/address-cast.r b/tools/qfcc/test/address-cast.r new file mode 100644 index 000000000..ed853f9e5 --- /dev/null +++ b/tools/qfcc/test/address-cast.r @@ -0,0 +1,21 @@ +int a; +double b; +int c = (int) &b; +double d; +void printf (string fmt, ...) = #0; + +int main() +{ + int di = (int) &d; + int fail = 0; + + if (!c) { + printf ("global init address cast fail: %d\n", c); + fail |= 1; + } + if (!di) { + printf ("local init address cast fail: %d\n", di); + fail |= 1; + } + return fail; +} diff --git a/tools/qfcc/test/alignment.r b/tools/qfcc/test/alignment.r new file mode 100644 index 000000000..9b867b2d6 --- /dev/null +++ b/tools/qfcc/test/alignment.r @@ -0,0 +1,36 @@ +int a; +double b; +int c; +double d; +void printf (string fmt, ...) = #0; + +int main() +{ + int fail = 0; + void *ap = &a; + void *bp = &b; + void *cp = &c; + void *dp = &d; + int aa = (int) ap; + int ba = (int) bp; + int ca = (int) cp; + int da = (int) dp; + + if (ba & 1) { + printf ("double b is not aligned: %d\n", ba); + fail |= 1; + } + if (da & 1) { + printf ("double d is not aligned: %d\n", da); + fail |= 1; + } + if (ca - aa != 1) { + printf ("int c (%d) is not adjacant to int a (%d)\n", ca, aa); + fail |= 1; + } + if (ba <= ca) { + printf ("double b does not come after int c: %d %d\n", ba, ca); + fail |= 1; + } + return fail; +} diff --git a/tools/qfcc/test/anonstruct.r b/tools/qfcc/test/anonstruct.r new file mode 100644 index 000000000..3c025bb38 --- /dev/null +++ b/tools/qfcc/test/anonstruct.r @@ -0,0 +1,52 @@ +void printf (string fmt, ...) = #0; + +typedef struct xyzzy_s { + int magic; +} xyzzy_t; + +typedef struct anon_s { + int foo; + int id; + struct { + int bar; + int baz; + }; + union { + int snafu; + float fizzle; + }; +} anon_t; + +int foo (float f) +{ + anon_t anon; + anon.fizzle = f; + return anon.snafu; +} + +int main() +{ + anon_t anon; + int ret = 0; + if ((int)&anon.snafu != (int)&anon.fizzle) { + printf ("anon union broken: %p %p\n", + &anon.snafu, &anon.fizzle); + ret |= 1; + } + if ((int)&anon.snafu - (int)&anon.baz != 1) { + printf ("snafu and baz not adjacant: snafu:%p baz:%p\n", + &anon.snafu, &anon.baz); + ret |= 1; + } + if ((int)&anon.baz - (int)&anon.bar != 1) { + printf ("baz and bar not adjacant: baz:%p bar:%p\n", + &anon.baz, &anon.bar); + ret |= 1; + } + if ((int)&anon.bar - (int)&anon.id != 1) { + printf ("bar not after id: bar:%p id:%p\n", + &anon.bar, &anon.id); + ret |= 1; + } + return ret; +} diff --git a/tools/qfcc/test/arraylife.r b/tools/qfcc/test/arraylife.r new file mode 100644 index 000000000..7df0ea8a8 --- /dev/null +++ b/tools/qfcc/test/arraylife.r @@ -0,0 +1,20 @@ +vector +bar (float *v) +{ + return [v[0], v[1], v[2]]; +} + +vector +foo (float x, float y, float z) +{ + float v[3] = { x, y, z }; + return bar (&v[0]); +} + +int +main () +{ + int ret = 0; + ret |= foo(1,2,3) != [1, 2, 3]; + return ret; +} diff --git a/tools/qfcc/test/arraylife2.r b/tools/qfcc/test/arraylife2.r new file mode 100644 index 000000000..bd783aecd --- /dev/null +++ b/tools/qfcc/test/arraylife2.r @@ -0,0 +1,30 @@ +vector bar (float *v); +vector snafu (float *v); + +vector +foo (float x, float y, float z) +{ + float v[3] = { x, y, z }; + float w[3] = { x, y, z }; + snafu (v); + return bar (w); +} + +vector snafu (float *v) +{ + return nil; +} + +vector +bar (float *v) +{ + return [v[0], v[1], v[2]]; +} + +int +main () +{ + int ret = 0; + ret |= foo(1,2,3) != [1, 2, 3]; + return ret; +} diff --git a/tools/qfcc/test/assignchain.r b/tools/qfcc/test/assignchain.r new file mode 100644 index 000000000..7f08c0494 --- /dev/null +++ b/tools/qfcc/test/assignchain.r @@ -0,0 +1,92 @@ +#include "test-harness.h" + +typedef struct foo { + int x; + float y; +} foo; + +int x, y; +int z = 42; +foo bar, baz; +foo foo_init = { 5, 6.25 }; + +int test_simple_global (void) +{ + int ret = 0; + x = y = z; + if (x != z || y != z) { + printf ("test_simple_global: x=%d y=%d z=%d\n", x, y, z); + ret |= 1; + } + return ret; +} + +int test_struct_global (void) +{ + int ret = 0; + bar = baz = foo_init; + if (bar.x != foo_init.x || bar.y != foo_init.y) { + printf ("test_struct_global: bar={%d %g} foo_init={%d %g}\n", + bar.x, bar.y, foo_init.x, foo_init.y); + ret |= 1; + } + if (baz.x != foo_init.x || baz.y != foo_init.y) { + printf ("test_struct_global: baz={%d %g} foo_init={%d %g}\n", + baz.x, baz.y, foo_init.x, foo_init.y); + ret |= 1; + } + bar = baz = nil; + if (bar.x || baz.x || bar.y || baz.y) { + printf ("test_struct_global: bar={%d %g} baz={%d %g}\n", + bar.x, bar.y, baz.x, baz.y); + ret |= 1; + } + return ret; +} + +int test_simple_pointer (int *x, int *y) +{ + int ret = 0; + *x = *y = z; + if (*x != z || *y != z) { + printf ("test_simple_pointer: *x=%d *y=%d z=%d\n", *x, *y, z); + ret |= 1; + } + return ret; +} + +int test_struct_pointer (foo *bar, foo *baz) +{ + int ret = 0; + *bar = *baz = foo_init; + if (bar.x != foo_init.x || bar.y != foo_init.y) { + printf ("test_struct_pointer: bar={%d %g} foo_init={%d %g}\n", + bar.x, bar.y, foo_init.x, foo_init.y); + ret |= 1; + } + if (baz.x != foo_init.x || baz.y != foo_init.y) { + printf ("test_struct_pointer: baz={%d %g} foo_init={%d %g}\n", + baz.x, baz.y, foo_init.x, foo_init.y); + ret |= 1; + } + *bar = foo_init; + *baz = foo_init; + *bar = *baz = nil; + if (bar.x || baz.x || bar.y || baz.y) { + printf ("test_struct: bar={%d %g} baz={%d %g}\n", + bar.x, bar.y, baz.x, baz.y); + ret |= 1; + } + return ret; +} + +int main () +{ + int ret = 0; + ret |= test_simple_global (); + ret |= test_struct_global (); + x = 0; y = 0; + ret |= test_simple_pointer (&x, &y); + ret |= test_struct_pointer (&bar, &baz); + return ret; +} diff --git a/tools/qfcc/test/build-compile-fail-run b/tools/qfcc/test/build-compile-fail-run new file mode 100755 index 000000000..415e894e7 --- /dev/null +++ b/tools/qfcc/test/build-compile-fail-run @@ -0,0 +1,15 @@ +#! /bin/sh + +script=$1 +shift + +cat > $script < $script < $script < 0; ) { + i += 2; + j += 3; + } + if (i == 13 && j == 20) { + fail = 0; + } + + return fail; +} + +int +test_comma () +{ + int fail = 1; + if (return_comma() == 5) { + if (a == 3) { + fail = 0; + } + } + return fail; +} + +int +main () +{ + int fail = 0; + fail |= test_comma (); + fail |= test_for_comma (); + return fail; +} diff --git a/tools/qfcc/test/compound.r b/tools/qfcc/test/compound.r new file mode 100644 index 000000000..19a72d3fe --- /dev/null +++ b/tools/qfcc/test/compound.r @@ -0,0 +1,72 @@ +#include "test-harness.h" + +typedef struct Point { + int x; + int y; +} Point; + +typedef struct Size { + int width; + int height; +} Size; + +typedef struct Rect { + Point origin; + Size size; +} Rect; + +int test_simple_param (Point p, int x, int y) +{ + int ret = !(p.x == x && p.y == y); + if (ret) { + printf ("simple param: {%d, %d} != {%d, %d}\n", p.x, p.y, x, y); + } + return ret; +} + +int test_nested_param (Rect r, int x, int y, int w, int h) +{ + int ret = !(r.origin.x == x && r.origin.y == y + && r.size.width == w && r.size.height == h); + if (ret) { + printf ("nested param: {{%d, %d}, {%d, %d}} != ", + r.origin.x, r.origin.y, r.size.width, r.size.height); + printf ("{{%d, %d}, {%d, %d}}\n", x, y, w, h); + } + return ret; +} + +int test_simple_assign (int x, int y) +{ + Point p; + p = { x, y }; + int ret = !(p.x == x && p.y == y); + if (ret) { + printf ("simple assign: {%d, %d} != {%d, %d}\n", p.x, p.y, x, y); + } + return ret; +} + +int test_nested_assign (int x, int y, int w, int h) +{ + Rect r; + r = {{x, y}, {w, h}}; + int ret = !(r.origin.x == x && r.origin.y == y + && r.size.width == w && r.size.height == h); + if (ret) { + printf ("nested assign: {{%d, %d}, {%d, %d}} != ", + r.origin.x, r.origin.y, r.size.width, r.size.height); + printf ("{{%d, %d}, {%d, %d}}\n", x, y, w, h); + } + return ret; +} + +int main (void) +{ + int ret = 0; + ret |= test_simple_param ({1, 2}, 1, 2); + ret |= test_nested_param ({{1, 2}, {3, 4}}, 1, 2, 3, 4); + ret |= test_simple_assign (1, 2); + ret |= test_nested_assign (1, 2, 3, 4); + return ret; +} diff --git a/tools/qfcc/test/const-fold-int.r b/tools/qfcc/test/const-fold-int.r new file mode 100644 index 000000000..d7fa31920 --- /dev/null +++ b/tools/qfcc/test/const-fold-int.r @@ -0,0 +1,76 @@ +int mul_e_c_t_c (int x) { return x * 6 * 7; } +int mul_c_e_t_c (int x) { return 6 * x * 7; } +int mul_c_c_t_e (int x) { return 6 * 7 * x; } +int mul_e_t_c_c (int x) { return x * (6 * 7); } +int mul_c_t_e_c (int x) { return 6 * (x * 7); } +int mul_c_t_c_e (int x) { return 6 * (7 * x); } + +int addadd_e_c_t_c (int x) { return x + 6 + 7; } +int addadd_c_e_t_c (int x) { return 6 + x + 7; } +int addadd_c_c_t_e (int x) { return 6 + 7 + x; } +int addadd_e_t_c_c (int x) { return x + (6 + 7); } +int addadd_c_t_e_c (int x) { return 6 + (x + 7); } +int addadd_c_t_c_e (int x) { return 6 + (7 + x); } + +int addsub_e_c_t_c (int x) { return x + 6 - 7; } +int addsub_c_e_t_c (int x) { return 6 + x - 7; } +int addsub_c_c_t_e (int x) { return 6 + 7 - x; } +int addsub_e_t_c_c (int x) { return x + (6 - 7); } +int addsub_c_t_e_c (int x) { return 6 + (x - 7); } +int addsub_c_t_c_e (int x) { return 6 + (7 - x); } + +int subadd_e_c_t_c (int x) { return x - 6 + 7; } +int subadd_c_e_t_c (int x) { return 6 - x + 7; } +int subadd_c_c_t_e (int x) { return 6 - 7 + x; } +int subadd_e_t_c_c (int x) { return x - (6 + 7); } +int subadd_c_t_e_c (int x) { return 6 - (x + 7); } +int subadd_c_t_c_e (int x) { return 6 - (7 + x); } + +int subsub_e_c_t_c (int x) { return x - 6 - 7; } +int subsub_c_e_t_c (int x) { return 6 - x - 7; } +int subsub_c_c_t_e (int x) { return 6 - 7 - x; } +int subsub_e_t_c_c (int x) { return x - (6 - 7); } +int subsub_c_t_e_c (int x) { return 6 - (x - 7); } +int subsub_c_t_c_e (int x) { return 6 - (7 - x); } + +int +main () +{ + int fail = 0; + + fail |= mul_e_c_t_c (10) != 420; + fail |= mul_c_e_t_c (10) != 420; + fail |= mul_c_c_t_e (10) != 420; + fail |= mul_e_t_c_c (10) != 420; + fail |= mul_c_t_e_c (10) != 420; + fail |= mul_c_t_c_e (10) != 420; + + fail |= addadd_e_c_t_c (29) != 42; + fail |= addadd_c_e_t_c (29) != 42; + fail |= addadd_c_c_t_e (29) != 42; + fail |= addadd_e_t_c_c (29) != 42; + fail |= addadd_c_t_e_c (29) != 42; + fail |= addadd_c_t_c_e (29) != 42; + + fail |= addsub_e_c_t_c (43) != 42; + fail |= addsub_c_e_t_c (43) != 42; + fail |= addsub_c_c_t_e (-29) != 42; + fail |= addsub_e_t_c_c (43) != 42; + fail |= addsub_c_t_e_c (43) != 42; + fail |= addsub_c_t_c_e (-29) != 42; + + fail |= subadd_e_c_t_c (41) != 42; + fail |= subadd_c_e_t_c (-29) != 42; + fail |= subadd_c_c_t_e (43) != 42; + fail |= subadd_e_t_c_c (55) != 42; + fail |= subadd_c_t_e_c (-43) != 42; + fail |= subadd_c_t_c_e (-43) != 42; + + fail |= subsub_e_c_t_c (55) != 42; + fail |= subsub_c_e_t_c (-43) != 42; + fail |= subsub_c_c_t_e (-43) != 42; + fail |= subsub_e_t_c_c (41) != 42; + fail |= subsub_c_t_e_c (-29) != 42; + fail |= subsub_c_t_c_e (43) != 42; + return fail; +} diff --git a/tools/qfcc/test/deadbool.r b/tools/qfcc/test/deadbool.r index c4869b293..c56aa0f10 100644 --- a/tools/qfcc/test/deadbool.r +++ b/tools/qfcc/test/deadbool.r @@ -9,7 +9,7 @@ void foo (void) return; if (!time) { ent = spawn (); - ent.f = time + 0.1; + ent.f = time + 0.1f; } } diff --git a/tools/qfcc/test/dealloc-nowarn.r b/tools/qfcc/test/dealloc-nowarn.r new file mode 100644 index 000000000..f4bc3bcc4 --- /dev/null +++ b/tools/qfcc/test/dealloc-nowarn.r @@ -0,0 +1,36 @@ +@interface Object +{ + Class isa; +} +-(void)dealloc; +@end + +@interface derived : Object +@end + +@implementation Object +-(void) dealloc +{ + // this is the root of the hierarchy, so no super to call, thus + // must not check for [super dealloc] +} +@end + +void __obj_exec_class (struct obj_module *msg) = #0; +id obj_msgSend_super (Super *class, SEL op, ...) = #0; + +@implementation derived +-(void) dealloc +{ + // as this is a derived class, failure to call [super dealloc] will + // result in a memory leak (yes, there could be special allocators + // involved, in which case something will be needed to inform the + // compiler) + [super dealloc]; +} +@end + +int main () +{ + return 0; // test passes if compile succeeds (with -Werror) +} diff --git a/tools/qfcc/test/dealloc-nowarn2.r b/tools/qfcc/test/dealloc-nowarn2.r new file mode 100644 index 000000000..1443be8e2 --- /dev/null +++ b/tools/qfcc/test/dealloc-nowarn2.r @@ -0,0 +1,43 @@ +@interface Object +{ + Class isa; +} +-(void)dealloc; +@end + +@interface derived : Object +{ + int free; +} +@end + +@implementation Object +-(void) dealloc +{ + // this is the root of the hierarchy, so no super to call, thus + // must not check for [super dealloc] +} +@end + +void __obj_exec_class (struct obj_module *msg) = #0; +id obj_msgSend_super (Super *class, SEL op, ...) = #0; + +@implementation derived +-(void) dealloc +{ + // as this is a derived class, failure to call [super dealloc] will + // result in a memory leak (yes, there could be special allocators + // involved, in which case something will be needed to inform the + // compiler) + if (free) { + [super dealloc]; + } else { + [super dealloc]; + } +} +@end + +int main () +{ + return 0; // test passes if compile succeeds (with -Werror) +} diff --git a/tools/qfcc/test/dealloc-warn.r b/tools/qfcc/test/dealloc-warn.r new file mode 100644 index 000000000..86dd53a75 --- /dev/null +++ b/tools/qfcc/test/dealloc-warn.r @@ -0,0 +1,37 @@ +#pragma warn error + +@interface Object +{ + Class isa; +} +-(void)dealloc; +@end + +@interface derived : Object +@end + +@implementation Object +-(void) dealloc +{ + // this is the root of the hierarchy, so no super to call, thus + // must not check for [super dealloc] +} +@end + +@implementation derived +-(void) dealloc +{ + // as this is a derived class, failure to call [super dealloc] will + // result in a memory leak (yes, there could be special allocators + // involved, in which case something will be needed to inform the + // compiler) +} +@end + +void __obj_exec_class (struct obj_module *msg) = #0; +id obj_msgSend_super (Super *class, SEL op, ...) = #0; + +int main () +{ + return 1; // test fails if compile succeeds (with -Werror) +} diff --git a/tools/qfcc/test/dealloc-warn2.r b/tools/qfcc/test/dealloc-warn2.r new file mode 100644 index 000000000..533913da5 --- /dev/null +++ b/tools/qfcc/test/dealloc-warn2.r @@ -0,0 +1,47 @@ +#pragma warn error + +void __obj_exec_class (struct obj_module *msg) = #0; +id obj_msgSend_super (Super *class, SEL op, ...) = #0; +id obj_msgSend (Super *class, SEL op, ...) = #0; + +@interface Object +{ + Class isa; +} +-(void)dealloc; +-(void)release; +@end + +@interface derived : Object +{ + id something; +} +@end + +@implementation Object +-(void) dealloc +{ + // this is the root of the hierarchy, so no super to call, thus + // must not check for [super dealloc] +} +-(void) release +{ +} +@end + +@implementation derived +-(void) dealloc +{ + // as this is a derived class, failure to call [super dealloc] will + // result in a memory leak (yes, there could be special allocators + // involved, in which case something will be needed to inform the + // compiler) + [super release]; + [something dealloc]; +} +@end + +int main () +{ + return 1; // test fails if compile succeeds (with -Werror) +} diff --git a/tools/qfcc/test/dealloc-warn3.r b/tools/qfcc/test/dealloc-warn3.r new file mode 100644 index 000000000..a8dd2d2b4 --- /dev/null +++ b/tools/qfcc/test/dealloc-warn3.r @@ -0,0 +1,47 @@ +#pragma warn error +#pragma optimize on + +@interface Object +{ + Class isa; +} +-(void)dealloc; +@end + +@interface derived : Object +{ + int free; +} +@end + +@implementation Object +-(void) dealloc +{ + // this is the root of the hierarchy, so no super to call, thus + // must not check for [super dealloc] +} +-(void) release +{ +} +@end + +@implementation derived +-(void) dealloc +{ + // as this is a derived class, failure to call [super dealloc] will + // result in a memory leak (yes, there could be special allocators + // involved, in which case something will be needed to inform the + // compiler) + if (free) { + [super dealloc]; + } +} +@end + +void __obj_exec_class (struct obj_module *msg) = #0; +id obj_msgSend_super (Super *class, SEL op, ...) = #0; + +int main () +{ + return 1; // test fails if compile succeeds (with -Werror) +} diff --git a/tools/qfcc/test/dealloc-warn4.r b/tools/qfcc/test/dealloc-warn4.r new file mode 100644 index 000000000..9962e19ca --- /dev/null +++ b/tools/qfcc/test/dealloc-warn4.r @@ -0,0 +1,51 @@ +#pragma warn error +#pragma optimize on + +void str_free(string str) +{ +} + +@interface Object +{ + Class isa; +} +-(void)dealloc; +@end + +@interface derived : Object +{ + int free; + string foo; + id bar; +} +@end + +@implementation Object +-(void) dealloc +{ + // this is the root of the hierarchy, so no super to call, thus + // must not check for [super dealloc] +} +-(void) release +{ +} +@end + +@implementation derived +-(void) dealloc +{ + // as this is a derived class, failure to call [super dealloc] will + // result in a memory leak (yes, there could be special allocators + // involved, in which case something will be needed to inform the + // compiler) + str_free (foo); +} +@end + +void __obj_exec_class (struct obj_module *msg) = #0; +id obj_msgSend_super (Super *class, SEL op, ...) = #0; + +int main () +{ + return 1; // test fails if compile succeeds (with -Werror) +} diff --git a/tools/qfcc/test/double-alias.r b/tools/qfcc/test/double-alias.r new file mode 100644 index 000000000..5df0d3398 --- /dev/null +++ b/tools/qfcc/test/double-alias.r @@ -0,0 +1,41 @@ +void printf (string fmt, ...) = #0; +# define M_PI 3.14159265358979323846 + +union { + double d; + int i[2]; +} type_pun; + +int alias_printf (string fmt, ...); + +int +test_alias () +{ + int fail = 0; + type_pun.d = M_PI; + fail = alias_printf ("%g %08x%08x\n", type_pun.d, + type_pun.i[1], type_pun.i[0]); + return fail; +} + +int +alias_printf (string fmt, ...) +{ + int fail = 0; + // this will fail on big-endian systems + fail = (@args.list[2].int_val != 0x54442d18 + || @args.list[1].int_val != 0x400921fb); + printf ("%.17g %08x%08x\n", + @args.list[0].double_val, + @args.list[2].int_val, + @args.list[1].int_val); + return fail; +} + +int +main () +{ + int fail = 0; + fail |= test_alias (); + return fail; +} diff --git a/tools/qfcc/test/double-demote-float-ainit.r b/tools/qfcc/test/double-demote-float-ainit.r new file mode 100644 index 000000000..7ebbb1467 --- /dev/null +++ b/tools/qfcc/test/double-demote-float-ainit.r @@ -0,0 +1,8 @@ +#pragma warn error + +double a; +int b[] = {1.0d}; +int main () +{ + return 1; // test fails if compile succeeds +} diff --git a/tools/qfcc/test/double-demote-float-ginit.r b/tools/qfcc/test/double-demote-float-ginit.r new file mode 100644 index 000000000..a23f98939 --- /dev/null +++ b/tools/qfcc/test/double-demote-float-ginit.r @@ -0,0 +1,8 @@ +#pragma warn error + +double a; +float b = 1.0d; +int main () +{ + return 1; // test fails if compile succeeds +} diff --git a/tools/qfcc/test/double-demote-float-linit.r b/tools/qfcc/test/double-demote-float-linit.r new file mode 100644 index 000000000..2eba60432 --- /dev/null +++ b/tools/qfcc/test/double-demote-float-linit.r @@ -0,0 +1,8 @@ +#pragma warn error + +double a; +int main () +{ + float b = a; + return 1; // test fails if compile succeeds +} diff --git a/tools/qfcc/test/double-demote-float.r b/tools/qfcc/test/double-demote-float.r new file mode 100644 index 000000000..f38db3a96 --- /dev/null +++ b/tools/qfcc/test/double-demote-float.r @@ -0,0 +1,9 @@ +#pragma warn error + +double a; +float b; +int main () +{ + b = a; + return 1; // test fails if compile succeeds +} diff --git a/tools/qfcc/test/double-demote-int-ainit.r b/tools/qfcc/test/double-demote-int-ainit.r new file mode 100644 index 000000000..7ebbb1467 --- /dev/null +++ b/tools/qfcc/test/double-demote-int-ainit.r @@ -0,0 +1,8 @@ +#pragma warn error + +double a; +int b[] = {1.0d}; +int main () +{ + return 1; // test fails if compile succeeds +} diff --git a/tools/qfcc/test/double-demote-int-ginit.r b/tools/qfcc/test/double-demote-int-ginit.r new file mode 100644 index 000000000..80fa7857a --- /dev/null +++ b/tools/qfcc/test/double-demote-int-ginit.r @@ -0,0 +1,8 @@ +#pragma warn error + +double a; +int b = 1.0d; +int main () +{ + return 1; // test fails if compile succeeds +} diff --git a/tools/qfcc/test/double-demote-int-linit.r b/tools/qfcc/test/double-demote-int-linit.r new file mode 100644 index 000000000..523cca503 --- /dev/null +++ b/tools/qfcc/test/double-demote-int-linit.r @@ -0,0 +1,8 @@ +#pragma warn error + +double a; +int main () +{ + int b = a; + return 1; // test fails if compile succeeds +} diff --git a/tools/qfcc/test/double-demote-int.r b/tools/qfcc/test/double-demote-int.r new file mode 100644 index 000000000..dd921215f --- /dev/null +++ b/tools/qfcc/test/double-demote-int.r @@ -0,0 +1,9 @@ +#pragma warn error + +double a; +int b; +int main () +{ + b = a; + return 1; // test fails if compile succeeds +} diff --git a/tools/qfcc/test/double-float-compare.r b/tools/qfcc/test/double-float-compare.r new file mode 100644 index 000000000..37a4bfb03 --- /dev/null +++ b/tools/qfcc/test/double-float-compare.r @@ -0,0 +1,9 @@ +#pragma warn error + +double a; +float b; +int main () +{ + int x = a == b; + return 1; // test fails if compile succeeds +} diff --git a/tools/qfcc/test/double-int-compare.r b/tools/qfcc/test/double-int-compare.r new file mode 100644 index 000000000..374b32edc --- /dev/null +++ b/tools/qfcc/test/double-int-compare.r @@ -0,0 +1,9 @@ +#pragma warn error + +double a; +int b; +int main () +{ + int x = a == b; + return 1; // test fails if compile succeeds +} diff --git a/tools/qfcc/test/double.r b/tools/qfcc/test/double.r new file mode 100644 index 000000000..de0649b19 --- /dev/null +++ b/tools/qfcc/test/double.r @@ -0,0 +1,106 @@ +void printf (string fmt, ...) = #0; +# define M_PI 3.14159265358979323846 + +union { + double d; + int i[2]; +} type_pun; + +int +test_format () +{ + int fail = 0; + type_pun.d = M_PI; + printf ("%.17g %08x%08x\n", type_pun.d, type_pun.i[1], type_pun.i[0]); + // this will fail on big-endian systems + fail = type_pun.i[0] != 0x54442d18 || type_pun.i[1] != 0x400921fb; + return fail; +} + +int +test_constant () +{ + int fail = 0; + double a, b, c, d, e; + a = 1; + b = 2.0; + c = 3.2f; + d = 3.2d; + e = 3.2; + printf ("%.17g %.17g %.17g %.17g %.17g\n", a, b, c, d, e); + // this will fail on big-endian systems + fail |= c == d; // 3.2 is not exactly representable, so must be different + fail |= c == e; // 3.2 is not exactly representable, so must be different + fail |= d != e; // 3.2d and 3.2 are both double, so must be the same + return fail; +} + +double less = 3; +double greater_equal = 3; +double less_equal = 5; +double greater = 5; + +int +test_copare () +{ + int fail = 0; + + fail |= !(less < greater); + fail |= (less > greater); + fail |= !(less != greater); + fail |= (less == greater); + fail |= !(less <= greater); + fail |= (less >= greater); + + fail |= (less_equal < greater); + fail |= (less_equal > greater); + fail |= !(less_equal == greater); + fail |= (less_equal != greater); + fail |= !(less_equal <= greater); + fail |= !(less_equal >= greater); + + fail |= (greater < less); + fail |= !(greater > less); + fail |= !(greater != less); + fail |= (greater == less); + fail |= (greater <= less); + fail |= !(greater >= less); + + fail |= (greater_equal < less); + fail |= (greater_equal > less); + fail |= !(greater_equal == less); + fail |= (greater_equal != less); + fail |= !(greater_equal <= less); + fail |= !(greater_equal >= less); + return fail; +} + +int +test_ops () +{ + int fail = 0; + double a = 6.25, b = 2.375; + double c; + + c = a + b; + fail |= c != 8.625; + c = a - b; + fail |= c != 3.875; + c = a * b; + fail |= c != 14.84375; + c = a / b; + fail |= c != 50d/19d; + c = a % b; + fail |= c != 1.5; + return fail; +} + +int +main () +{ + int fail = 0; + fail |= test_format (); + fail |= test_constant (); + fail |= test_ops (); + return fail; +} diff --git a/tools/qfcc/test/entity-struct.r b/tools/qfcc/test/entity-struct.r new file mode 100644 index 000000000..e342e5b62 --- /dev/null +++ b/tools/qfcc/test/entity-struct.r @@ -0,0 +1,26 @@ +typedef struct foo_s { + int a, b; +} foo_t; + +entity self; +foo_t a, b; +vector x, y; +int i, j; + +.vector v; +.foo_t foo; + +void bar (entity other) +{ + self.v = x; + self.foo.b = i; + j = self.foo.b; + self.foo.a = self.foo.b; + self.foo.b = self.foo.a; +} + +int +main (void) +{ + return 0; // to survive and prevail :) +} diff --git a/tools/qfcc/test/enum.r b/tools/qfcc/test/enum.r new file mode 100644 index 000000000..f1565cdf0 --- /dev/null +++ b/tools/qfcc/test/enum.r @@ -0,0 +1,13 @@ +typedef enum { + NO = 0, + YES +} BOOL; + +int +main() +{ + BOOL b; + b = 0; + b = YES; + return !b; +} diff --git a/tools/qfcc/test/fordecl.r b/tools/qfcc/test/fordecl.r new file mode 100644 index 000000000..70d57690a --- /dev/null +++ b/tools/qfcc/test/fordecl.r @@ -0,0 +1,29 @@ +void printf (string fmt, ...) = #0; + +int +test_fordecl () +{ + int fail = 1; + int count = 5; + int ti = -1, tj = -1; + + for (int i = 3, j = 5; count-- > 0; ) { + i += 2; + j += 3; + ti = i; + tj = j; + } + if (ti == 13 && tj == 20) { + fail = 0; + } + + return fail; +} + +int +main () +{ + int fail = 0; + fail |= test_fordecl (); + return fail; +} diff --git a/tools/qfcc/test/func-expr2.r b/tools/qfcc/test/func-expr2.r new file mode 100644 index 000000000..32dad106d --- /dev/null +++ b/tools/qfcc/test/func-expr2.r @@ -0,0 +1,23 @@ +typedef struct Extent_s { + int width; + int height; +} Extent; + +extern Extent get_size (void); +extern int getlen(void); + +int main (void) +{ + int x = get_size().width - getlen() - 1; + return x != 29; +} + +Extent get_size (void) +{ + return {38, 8}; +} + +int getlen (void) +{ + return 8; +} diff --git a/tools/qfcc/test/old/gcd.pas b/tools/qfcc/test/gcd.pas similarity index 73% rename from tools/qfcc/test/old/gcd.pas rename to tools/qfcc/test/gcd.pas index 04e193b26..3879a3d3d 100644 --- a/tools/qfcc/test/old/gcd.pas +++ b/tools/qfcc/test/gcd.pas @@ -1,5 +1,5 @@ program example (input, output); -var x, y: integer; +var x, y, g: integer; procedure printf (format:string; ...) := #0; function gcd (a, b: integer): integer; var c: quaternion; @@ -11,5 +11,7 @@ end; begin x := 130; y := 120; - printf ("%d\n", gcd (x, y)) + g := gcd (x, y); + printf ("%d\n", g); + ExitCode := g <> 10; end. diff --git a/tools/qfcc/test/ifsuper.r b/tools/qfcc/test/ifsuper.r new file mode 100644 index 000000000..58e916102 --- /dev/null +++ b/tools/qfcc/test/ifsuper.r @@ -0,0 +1,56 @@ +#pragma advanced +#include "test-harness.h" + +int obj_increment_retaincount (id object) = #0; +int obj_get_retaincount (id object) = #0; +id (Class class) class_create_instance = #0; +id obj_msgSend_super (Super *class, SEL op, ...) = #0; + +@interface Object +{ + Class isa; +} ++(id) alloc; +-(id) init; +@end + +@interface Foo : Object +-(id) init; +@end + +@implementation Object ++(id) alloc +{ + return class_create_instance (self); +} +-(id) init +{ + obj_increment_retaincount (self); + return self; +} +@end + +@implementation Foo +-(id) init +{ + if (!(self = [super init])) { + return nil; + } + return self; +} +@end + +int main () +{ + Foo *foo = [[Foo alloc] init]; + if (!foo) { + printf ("foo is nil\n"); + return 1; + } + int retain = obj_get_retaincount (foo); + if (retain != 1) { + printf ("retain count != 1: %d\n", retain); + return 1; + } + return 0; +} diff --git a/tools/qfcc/test/iterfunc.r b/tools/qfcc/test/iterfunc.r new file mode 100644 index 000000000..803b6a6f6 --- /dev/null +++ b/tools/qfcc/test/iterfunc.r @@ -0,0 +1,33 @@ +#include + +// can't link against libr.a (may not be built) +void *PR_FindGlobal (string name) = #0; +void printf (string fmt, ...) = #0; + +qfot_type_encodings_t *encodings; +qfot_type_t *next_type (qfot_type_t *type); + +int +main (void) +{ + int found_param = 0; + int found_zero = 0; + qfot_type_t *type; + + encodings = PR_FindGlobal (".type_encodings"); + + for (type = encodings.types; + ((int *)type - (int *) encodings.types) < encodings.size; + type = next_type (type)) { + } + return 0; +} + +qfot_type_t * +next_type (qfot_type_t *type) +{ + int size = type.size; + if (!size) + size = 4; + return (qfot_type_t *) ((int *) type + size); +} diff --git a/tools/qfcc/test/ivar-struct-return.r b/tools/qfcc/test/ivar-struct-return.r new file mode 100644 index 000000000..8351293c8 --- /dev/null +++ b/tools/qfcc/test/ivar-struct-return.r @@ -0,0 +1,29 @@ +#pragma bug die +#include "test-harness.h" + +struct Point { + int x; + int y; +}; +typedef struct Point Point; + +@interface Object +{ + int foo; + Point origin; +} ++(Point) origin; +@end + +@implementation Object ++(Point) origin +{ + origin = {1, 2}; + return origin; +} +@end +int main() +{ + Point p = [Object origin]; + return !(p.x == 1 && p.y == 2); +} diff --git a/tools/qfcc/test/link_order.r b/tools/qfcc/test/link_order.r new file mode 100644 index 000000000..9b0cdc73b --- /dev/null +++ b/tools/qfcc/test/link_order.r @@ -0,0 +1,44 @@ +#pragma bug die +#include "test-harness.h" + +typedef struct link_s { + int something; + struct link_s *next; +} link_t; + +link_t * +link_objs(link_t **array, int count) +{ + link_t *obj = nil, *o; + while (count-- > 0) { + o = array[count]; + o.next = obj; + obj = o; + } + return obj; +} + +link_t link_a; +link_t link_b; +link_t *links[2]; + +int +main () +{ + links[0] = &link_a; + links[1] = &link_b; + link_t *chain = link_objs (links, 2); + if (chain != &link_a) { + printf ("chain doesn't point to link_a\n"); + return 1; + } + if (chain.next != &link_b) { + printf ("chain.next doesn't point to link_b\n"); + return 1; + } + if (chain.next.next != nil) { + printf ("chain.next.next isn't nil\n"); + return 1; + } + return 0; +} diff --git a/tools/qfcc/test/lost-use.r b/tools/qfcc/test/lost-use.r new file mode 100644 index 000000000..dc9d79142 --- /dev/null +++ b/tools/qfcc/test/lost-use.r @@ -0,0 +1,29 @@ +void printf (string fmt, ...) = #0; +int getval(void) +{ + return 42; +} + +void magic (void) +{ +} + +void storeval (int *p) +{ + int x = getval (); + magic (); + *p = x; +} + +int val; + +int +main(void) +{ + storeval (&val); + if (val != 42) { + printf ("val is dead: %d\n", val); + return 1; + } + return 0; +} diff --git a/tools/qfcc/test/methodparams.r b/tools/qfcc/test/methodparams.r new file mode 100644 index 000000000..d2e71591b --- /dev/null +++ b/tools/qfcc/test/methodparams.r @@ -0,0 +1,29 @@ +typedef struct { int x, y; } Point; +@interface TextContext +- (void) mvvprintf: (Point) pos, string mft, @va_list args; +@end +@interface View +{ + TextContext *textContext; +} +- (void) mvprintf: (Point) pos, string mft, ...; +@end + +@implementation View +- (void) mvprintf: (Point) pos, string fmt, ... +{ + [textContext mvvprintf: pos, fmt, @args]; +} +@end +@attribute(no_va_list) id obj_msgSend (id receiver, SEL op, ...) = #0; +void __obj_exec_class (struct obj_module *msg) = #0; +@interface Object +@end +@implementation Object +@end + +int +main (void) +{ + return 0; // to survive and prevail :) +} diff --git a/tools/qfcc/test/modulo.r b/tools/qfcc/test/modulo.r index 4d59fdf87..6fb69490c 100644 --- a/tools/qfcc/test/modulo.r +++ b/tools/qfcc/test/modulo.r @@ -1,11 +1,39 @@ -#pragma traditional +#if !defined(__RUAMOKO__) || __RUAMOKO__ < 2 +# define test_traditional +#endif -void (...) printf = #0; +void printf (string ftm, ...) = #0; + +float snafu (float a, float b) +{ + float c = a % b; + return c; +} + +int imodulo (int a, int b) +{ + return a %% b; +} + +float fmodulo (float a, float b) +{ + return a %% b; +} + +double dmodulo (double a, double b) +{ + return a %% b; +} + +#ifdef test_traditional +#pragma traditional float foo (float a, float b) { float c = a % b; return c; } +#pragma advanced + float bar (float a, float b) { float c; @@ -15,47 +43,141 @@ float bar (float a, float b) return c; } +#pragma traditional float baz (float a, float b) { float c = (a + b) % (a - b); return c; } +#pragma advanced +#endif -float test (string name, float (func)(float a, float b), - float a, float b, float c) +@overload int +test (string name, string op, int (func)(int a, int b), int a, int b, int c) { - float ret; + int ret; ret = func (a, b); if (ret != c) { +#ifdef test_traditional if (func == baz) - printf ("%s: (%g + %g) %% (%g - %g): %g != %g\n", + printf ("%s: (%d + %d) %% (%d - %d): %d != %d\n", name, a, b, a, b, ret, c); else - printf ("%s: %g %% %g: %g != %g\n", - name, a, b, ret, c); +#endif + printf ("%s: %d %s %d: %d != %d\n", + name, a, op, b, ret, c); return 1; } return 0; } -float main (void) +@overload int +test (string name, string op, float (func)(float a, float b), + float a, float b, float c) { - float res = 0; - res |= test ("foo", foo, 5, 3, 2); - res |= test ("bar", bar, 5, 3, 2); - res |= test ("baz", baz, 5, 3, 0); + float ret; - res |= test ("foo", foo, -5, 3, -2); - res |= test ("bar", bar, -5, 3, -2); - res |= test ("baz", baz, -5, 3, -2); + ret = func (a, b); + if (ret != c) { +#ifdef test_traditional + if (func == baz) + printf ("%s: (%g + %g) %% (%g - %g): %g != %g\n", + name, a, b, a, b, ret, c); + else +#endif + printf ("%s: %g %s %g: %g != %g\n", + name, a, op, b, ret, c); + return 1; + } + return 0; +} - res |= test ("foo", foo, 5, -3, 2); - res |= test ("bar", bar, 5, -3, 2); - res |= test ("baz", baz, 5, -3, 2); +@overload int +test (string name, string op, double (func)(double a, double b), + double a, double b, double c) +{ + double ret; - res |= test ("foo", foo, -5, -3, -2); - res |= test ("bar", bar, -5, -3, -2); - res |= test ("baz", baz, -5, -3, 0); + ret = func (a, b); + if (ret != c) { +#ifdef test_traditional + if (func == baz) + printf ("%s: (%g + %g) %% (%g - %g): %g != %g\n", + name, a, b, a, b, ret, c); + else +#endif + printf ("%s: %g %s %g: %g != %g\n", + name, a, op, b, ret, c); + return 1; + } + return 0; +} +#ifdef test_traditional +typedef float restype; +#else +typedef int restype; +#endif + +restype main (void) +{ + restype res = 0; +#ifdef test_traditional + res |= test ("foo", "%", foo, 5, 3, 2); + res |= test ("bar", "%", bar, 5, 3, 2); + res |= test ("baz", "%", baz, 5, 3, 0); +#endif + res |= test ("snafu", "%", snafu, 5, 3, 2); +#ifdef test_traditional + res |= test ("foo", "%", foo, -5, 3, -2); + res |= test ("bar", "%", bar, -5, 3, -2); + res |= test ("baz", "%", baz, -5, 3, -2); +#endif + res |= test ("snafu", "%", snafu, -5, 3, -2); +#ifdef test_traditional + res |= test ("foo", "%", foo, 5, -3, 2); + res |= test ("bar", "%", bar, 5, -3, 2); + res |= test ("baz", "%", baz, 5, -3, 2); +#endif + res |= test ("snafu", "%", snafu, 5, -3, 2); +#ifdef test_traditional + res |= test ("foo", "%", foo, -5, -3, -2); + res |= test ("bar", "%", bar, -5, -3, -2); + res |= test ("baz", "%", baz, -5, -3, 0); +#endif + res |= test ("snafu", "%", snafu, -5, -3, -2); +#ifdef test_traditional + res |= test ("foo", "%", foo, 5, 3.5, 1.5); + res |= test ("foo", "%", foo, -5, 3.5, -1.5); +#endif + res |= test ("snafu", "%", snafu, 5, 3.5, 1.5); + res |= test ("snafu", "%", snafu, -5, 3.5, -1.5); + + res |= test ("int modulo", "%%", imodulo, 5, 3, 2); + res |= test ("int modulo", "%%", imodulo, -5, 3, 1); + res |= test ("int modulo", "%%", imodulo, 5, -3, -1); + res |= test ("int modulo", "%%", imodulo, -5, -3, -2); + res |= test ("int modulo", "%%", imodulo, 6, 3, 0); + res |= test ("int modulo", "%%", imodulo, -6, 3, 0); + res |= test ("int modulo", "%%", imodulo, 6, -3, 0); + res |= test ("int modulo", "%%", imodulo, -6, -3, 0); + + res |= test ("float modulo", "%%", fmodulo, 5, 3, 2); + res |= test ("float modulo", "%%", fmodulo, -5, 3, 1); + res |= test ("float modulo", "%%", fmodulo, 5, -3, -1); + res |= test ("float modulo", "%%", fmodulo, -5, -3, -2); + res |= test ("float modulo", "%%", fmodulo, 6, 3, 0); + res |= test ("float modulo", "%%", fmodulo, -6, 3, 0); + res |= test ("float modulo", "%%", fmodulo, 6, -3, 0); + res |= test ("float modulo", "%%", fmodulo, -6, -3, 0); + + res |= test ("double modulo", "%%", dmodulo, 5, 3, 2); + res |= test ("double modulo", "%%", dmodulo, -5, 3, 1); + res |= test ("double modulo", "%%", dmodulo, 5, -3, -1); + res |= test ("double modulo", "%%", dmodulo, -5, -3, -2); + res |= test ("double modulo", "%%", dmodulo, 6, 3, 0); + res |= test ("double modulo", "%%", dmodulo, -6, 3, 0); + res |= test ("double modulo", "%%", dmodulo, 6, -3, 0); + res |= test ("double modulo", "%%", dmodulo, -6, -3, 0); return res; } diff --git a/tools/qfcc/test/nilparamret.r b/tools/qfcc/test/nilparamret.r new file mode 100644 index 000000000..fbe0b7dc5 --- /dev/null +++ b/tools/qfcc/test/nilparamret.r @@ -0,0 +1,26 @@ +typedef struct bar { + float x, y, z, w; +} bar; + +bar set_return(); + +@param foo() +{ + set_return(); + return nil; +} + +bar set_return() +{ + return {1, 2, 3, 4}; +} +void printf (string fmt, ...) = #0; +int main() +{ + @param r = foo(); + printf("%q\n", r); + return !(r.quaternion_val.x == 0 + && r.quaternion_val.y == 0 + && r.quaternion_val.z == 0 + && r.quaternion_val.w == 0); +} diff --git a/tools/qfcc/test/old/linkdef2.r b/tools/qfcc/test/old/linkdef2.r index 133235f69..20a72fa3a 100644 --- a/tools/qfcc/test/old/linkdef2.r +++ b/tools/qfcc/test/old/linkdef2.r @@ -5,5 +5,5 @@ id obj; integer foo (void) { [obj message]; - return bar () + baz ();; + return bar () + baz (); } diff --git a/tools/qfcc/test/old/overload.r b/tools/qfcc/test/old/overload.r deleted file mode 100644 index 60bd5cf46..000000000 --- a/tools/qfcc/test/old/overload.r +++ /dev/null @@ -1,30 +0,0 @@ -//integer foo; -//typedef integer foo; -//integer (void) foo = #0; -//integer (bag); -//.integer (sack); -//.integer (float sack)x; -//integer *bar, baz; -//integer snafu(void) -//{ -//} -//integer [8]blah; - -@overload void func (integer a, integer b, integer c); -@overload void func (integer a, integer b); -@overload void func (integer a, ...); -//@overload void func (integer a, integer b, ...); -@overload void func (string y); -//@overload void func (...); -void func (integer x) -{ -// func (""); -} -void func (string y) -{ -// func (0.0); -// func (0, 0.0); -// func (0, 0, 0.0); - func (0.0, ""); -// func (0, 0, 0, 0, 0.0); -} diff --git a/tools/qfcc/test/overload.r b/tools/qfcc/test/overload.r new file mode 100644 index 000000000..97c77bbdf --- /dev/null +++ b/tools/qfcc/test/overload.r @@ -0,0 +1,23 @@ +typedef enum { + NO = 0, + YES +} BOOL; + +@extern BOOL sel_is_mapped (SEL aSel); +BOOL (SEL aSel) sel_is_mapped = #0; + +@overload int foo(int x) +{ + return 1; +} + +@overload int foo(float x) +{ + return 2; +} + +int main() +{ + //FIXME fails on implicit cast of double to float + return !(foo(5) == 1 && foo (5.0f) == 2); +} diff --git a/tools/qfcc/test/postop.r b/tools/qfcc/test/postop.r new file mode 100644 index 000000000..4b1091ac5 --- /dev/null +++ b/tools/qfcc/test/postop.r @@ -0,0 +1,19 @@ +int array[3] = { -1, -1, -1 }; + +int array_index (void) +{ + int i = 0; + + for (int c = 0; c < 3; c++) { + array[i++] = 0; + } + return i == 3 && !(array[0] | array[1] | array[2]); +} + +int +main () +{ + int ret = 0; + ret |= !array_index (); + return ret; +} diff --git a/tools/qfcc/test/ptraliasenc.r b/tools/qfcc/test/ptraliasenc.r new file mode 100644 index 000000000..8e8a58e9c --- /dev/null +++ b/tools/qfcc/test/ptraliasenc.r @@ -0,0 +1,31 @@ +#include + +#include "test-harness.h" + +typedef int int32_t; +int32_t *int32_ptr; + +typedef struct xdef_s { + qfot_type_t *type; ///< pointer to type definition + void *ofs; ///< 32-bit version of ddef_t.ofs +} xdef_t; + +typedef struct xdefs_s { + xdef_t *xdefs; + unsigned num_xdefs; +} xdefs_t; + +void *PR_FindGlobal (string name) = #0; + +int +main (void) +{ + //FIXME need a simple way to get at a def's meta-data + xdefs_t *xdefs = PR_FindGlobal (".xdefs"); + xdef_t *xdef = xdefs.xdefs; + while (xdef - xdefs.xdefs < xdefs.num_xdefs && xdef.ofs != &int32_ptr) { + xdef++; + } + printf ("int32_ptr: %s\n", xdef.type.encoding); + return xdef.type.encoding != "{>^i}"; +} diff --git a/tools/qfcc/test/ptrfunc.r b/tools/qfcc/test/ptrfunc.r new file mode 100644 index 000000000..12d4a77c7 --- /dev/null +++ b/tools/qfcc/test/ptrfunc.r @@ -0,0 +1,72 @@ +int func(string x) { return 1; } +int (*funcptr)(string); +int *(*ptrfuncptr)(string); +int array[7]; +int *ptrarray[7]; +int (*arrayptr)[7]; +int *(*ptrarrayptr)[7]; + +#include + +#include "test-harness.h" + +typedef struct xdef_s { + qfot_type_t *type; ///< pointer to type definition + void *ofs; ///< 32-bit version of ddef_t.ofs +} xdef_t; + +typedef struct xdefs_s { + xdef_t *xdefs; + unsigned num_xdefs; +} xdefs_t; + +void *PR_FindGlobal (string name) = #0; + +xdef_t * +find_xdef (void *varaddr) +{ + //FIXME need a simple way to get at a def's meta-data + xdefs_t *xdefs = PR_FindGlobal (".xdefs"); + xdef_t *xdef = xdefs.xdefs; + while (xdef - xdefs.xdefs < xdefs.num_xdefs && xdef.ofs != varaddr) { + xdef++; + } + if (xdef.ofs != varaddr) { + return nil; + } + return xdef; +} + +int +check_var (string name, string encoding) +{ + void *var = PR_FindGlobal (name); + if (!var) { + printf ("could not find var: %s\n", name); + return 0; + } + xdef_t *xdef = find_xdef (var); + if (!var) { + printf ("could not find xdef var: %s @ %p\n", name, var); + return 0; + } + int ok = xdef.type.encoding == encoding; + printf ("%s: %p %s %s %s\n", name, var, xdef.type.encoding, encoding, + ok ? "pass" : "fail"); + return ok; +} + +int +main (void) +{ + int ok = 1; + ok &= check_var ("PR_FindGlobal", "(^v*)"); + ok &= check_var ("func", "(i*)"); + ok &= check_var ("funcptr", "^(i*)"); + ok &= check_var ("ptrfuncptr", "^(^i*)"); + ok &= check_var ("array", "[7=i]"); + ok &= check_var ("ptrarray", "[7=^i]"); + ok &= check_var ("arrayptr", "^[7=i]"); + ok &= check_var ("ptrarrayptr", "^[7=^i]"); + return 1 & (!ok); +} diff --git a/tools/qfcc/test/ptrstructcast.r b/tools/qfcc/test/ptrstructcast.r new file mode 100644 index 000000000..36e560d08 --- /dev/null +++ b/tools/qfcc/test/ptrstructcast.r @@ -0,0 +1,22 @@ +typedef struct Point_s { + int x; + int y; +} Point; + +typedef struct Extent_s { + int width; + int height; +} Extent; + +int +foo (Point p) +{ + Extent e = *(Extent *)&p; + return e.width * e.height; +} + +int +main (void) +{ + return foo ({ 6, 7 }) != 42; +} diff --git a/tools/qfcc/test/quaternion.r b/tools/qfcc/test/quaternion.r new file mode 100644 index 000000000..6ac67911a --- /dev/null +++ b/tools/qfcc/test/quaternion.r @@ -0,0 +1,73 @@ +void printf (string fmt, ...) = #0; + +int +test_format () +{ + int fail = 0; + quaternion q = '1 2 3 4'; + vector v = '-1 -2 -3'; + float s = -4; + + if (q.x != 1 || q.y != 2 || q.z != 3 || q.w != 4) { + printf ("q = '1 2 3 4' -> %q\n", q); + fail = 1; + } + if (q.v != '1 2 3' || q.s != 4) { + printf ("q = '1 2 3 4' -> %v, %g\n", q.v, q.s); + fail = 1; + } + q = nil; + if (q.x != 0 || q.y != 0 || q.z != 0 || q.w != 0) { + printf ("q = nil -> %q\n", q); + fail = 1; + } + if (q.v != '0 0 0' || q.s != 0) { + printf ("q = nil -> %v, %g\n", q.v, q.s); + fail = 1; + } + q = [1, [2, 3, 4]]; + if (q.x != 2 || q.y != 3 || q.z != 4 || q.w != 1) { + printf ("q = [1, [2, 3, 4]] -> %q\n", q); + fail = 1; + } + if (q.v != '2 3 4' || q.s != 1) { + printf ("q = [1, [2, 3, 4]] -> %v, %g\n", q.v, q.s); + fail = 1; + } + q = [[5, 6, 7], 8]; + if (q.x != 5 || q.y != 6 || q.z != 7 || q.w != 8) { + printf ("q = [[5, 6, 7], 8] -> %q\n", q); + fail = 1; + } + if (q.v != '5 6 7' || q.s != 8) { + printf ("q = [[5, 6, 7], 8] -> %v, %g\n", q.v, q.s); + fail = 1; + } + q = [s, v]; + if (q.x != v.x || q.y != v.y || q.z != v.z || q.w != s) { + printf ("q = [s, v] -> %q (%v)\n", q, v); + fail = 1; + } + if (q.v != v || q.s != s) { + printf ("q = [s, v] -> %v, %g (%v)\n", q.v, q.s, v); + fail = 1; + } + q = [v, s]; + if (q.x != v.x || q.y != v.y || q.z != v.z || q.w != s) { + printf ("q = [v, s] -> %q (%v %s)\n", q, v, s); + fail = 1; + } + if (q.v != v || q.s != s) { + printf ("q = [v, s] -> %v, %g (%v %s)\n", q.v, q.s, v, s); + fail = 1; + } + return fail; +} + +int +main () +{ + int fail = 0; + fail |= test_format (); + return fail; +} diff --git a/tools/qfcc/test/return-postop.r b/tools/qfcc/test/return-postop.r new file mode 100644 index 000000000..bdf244f98 --- /dev/null +++ b/tools/qfcc/test/return-postop.r @@ -0,0 +1,23 @@ +#pragma optimize on +#include "test-harness.h" + +int counter; + +int +function (void) +{ + return counter++; +} + +int +main (void) +{ + int ret = 0; + counter = 0; + function (); + if (counter != 1) { + printf ("counter != 1: %d\n", counter); + ret = 1; + } + return ret; +} diff --git a/tools/qfcc/test/sendv.r b/tools/qfcc/test/sendv.r index 979a72c43..aaf7e19cd 100644 --- a/tools/qfcc/test/sendv.r +++ b/tools/qfcc/test/sendv.r @@ -8,13 +8,14 @@ int obj_increment_retaincount (id object) = #0; void send (id obj, string cmd, string str) { - @static @param params[1]; - @va_list va_list = {1, params}; - SEL sel; + @static @param params[3]; + @va_list va_list = {3, params}; + SEL sel = sel_get_uid (cmd); - params[0].string_val = str; - sel = sel_get_uid (cmd); - obj_msg_sendv (obj, sel, va_list); + params[0].pointer_val = obj; + params[1].pointer_val = sel; + params[2].string_val = str; + @return obj_msg_sendv (obj, sel, va_list); } @interface Object //just so the runtime doesn't complain @@ -53,5 +54,5 @@ main () } @end -id (id receiver, SEL op, ...) obj_msgSend = #0; +@attribute(no_va_list) id (id receiver, SEL op, ...) obj_msgSend = #0; void __obj_exec_class (struct obj_module *msg) = #0; diff --git a/tools/qfcc/test/state.r b/tools/qfcc/test/state.r index 7fec3c2d0..9eca8b7eb 100644 --- a/tools/qfcc/test/state.r +++ b/tools/qfcc/test/state.r @@ -10,10 +10,10 @@ $frame frame0 frame1 frame2 frame3 void state0 (void) -[$frame1, state1] +>> [$frame1, state1] { if (self.frame != $frame1 || self.think != state1 - || self.nextthink != 0.1) { + || self.nextthink != 0.1f) { printf ("state0: %g %x %g\n", self.frame, self.think, self.nextthink); exit (1); } @@ -21,22 +21,22 @@ state0 (void) void state1 (void) -[$frame2, state2, 0.2] +>> [$frame2, state2, 0.2f] { if (self.frame != $frame2 || self.think != state2 - || self.nextthink != 0.2) { - printf ("state0: %g %x %g\n", self.frame, self.think, self.nextthink); + || self.nextthink != 0.2f) { + printf ("state1: %g %x %g\n", self.frame, self.think, self.nextthink); exit (1); } } void state2 (void) -[$frame0, state0, 0.5] +>> [$frame0, state0, 0.5f] { if (self.frame != $frame0 || self.think != state0 - || self.nextthink != 0.5) { - printf ("state0: %g %x %g\n", self.frame, self.think, self.nextthink); + || self.nextthink != 0.5f) { + printf ("state2: %g %x %g\n", self.frame, self.think, self.nextthink); exit (1); } } diff --git a/tools/qfcc/test/static-init.r b/tools/qfcc/test/static-init.r new file mode 100644 index 000000000..bbbe3f9a7 --- /dev/null +++ b/tools/qfcc/test/static-init.r @@ -0,0 +1,19 @@ +#include "test-harness.h" + +int count_down () +{ + static int count = 2; + count--; + return count > 0; +} + +int main() +{ + int ret = 0; + count_down (); + if (count_down ()) { + printf ("did not reach 0\n"); + ret |= 1; + } + return ret; +} diff --git a/tools/qfcc/test/struct-init-param.r b/tools/qfcc/test/struct-init-param.r new file mode 100644 index 000000000..c3dd5927b --- /dev/null +++ b/tools/qfcc/test/struct-init-param.r @@ -0,0 +1,59 @@ +void printf (string fmt, ...) = #0; +typedef struct { + int x; + int y; +} Point; + +typedef struct { + int width; + int height; +} Extent; + +typedef struct Rect_s { + Point offset; + Extent extent; +} Rect; + +void *foo (Rect *obj, void *cmd, Rect *o, Point pos, Rect r); + +void *baz (Rect *obj, void *cmd, Rect *o, Point pos) +{ + Rect rect = { {1, 2}, {3, 4} }; + return foo (obj, cmd, o, pos, rect); +} + +void *bar (Rect *obj, void *cmd, Rect *o, Point pos) +{ + Rect rect = { {}, obj.extent }; + return foo (obj, cmd, o, pos, rect); +} + +void *foo (Rect *obj, void *cmd, Rect *o, Point pos, Rect r) +{ + *o = r; + return obj; +} + +Rect obj = { { 1, 2}, { 3, 4} }; +Rect o = { { 5, 6}, {7, 8} }; + +int main (void) +{ + int ret = 0; + int ok; + + bar(&obj, nil, &o, obj.offset); + ok = (o.offset.x == 0 && o.offset.y == 0 + && o.extent.width == 3 && o.extent.height == 4); + ret |= !ok; + printf ("%d %d %d %d %d\n", o.offset.x, o.offset.y, + o.extent.width, o.extent.height, ok); + + baz(&obj, nil, &o, obj.offset); + ok = (o.offset.x == 1 && o.offset.y == 2 + && o.extent.width == 3 && o.extent.height == 4); + ret |= !ok; + printf ("%d %d %d %d %d\n", o.offset.x, o.offset.y, + o.extent.width, o.extent.height, ok); + return ret; +} diff --git a/tools/qfcc/test/struct-nil-init.r b/tools/qfcc/test/struct-nil-init.r new file mode 100644 index 000000000..065a43b34 --- /dev/null +++ b/tools/qfcc/test/struct-nil-init.r @@ -0,0 +1,12 @@ +#pragma bug die + +struct foo { + quaternion x; + double y; +}; + +int main() +{ + struct foo bar = { }; + return bar.y; // to survive and prevail +} diff --git a/tools/qfcc/test/structarray.r b/tools/qfcc/test/structarray.r new file mode 100644 index 000000000..74d64214e --- /dev/null +++ b/tools/qfcc/test/structarray.r @@ -0,0 +1,23 @@ +void printf (string fmt, ...) = #0; +void *obj_malloc (int size) = #0; + +typedef struct { + int val; + int cap; + int ofs[]; +} valstruct_t; + +int foo[] = {1, 2, 3, 4, 5}; +valstruct_t *vs; +int dst; + +int +main () +{ + vs = (valstruct_t *)foo; + for (int i = 0; i < 2; i++) { + dst = vs.ofs[i]; + } + printf("dst = %d\n", dst); + return sizeof(foo) != 5 || dst != 4; +} diff --git a/tools/qfcc/test/structstruct.r b/tools/qfcc/test/structstruct.r new file mode 100644 index 000000000..c8ef25fad --- /dev/null +++ b/tools/qfcc/test/structstruct.r @@ -0,0 +1,61 @@ +#include "test-harness.h" + +typedef struct Point { + int x; + int y; +} Point; + +typedef struct Size { + int width; + int height; +} Size; + +typedef struct Rect { + Point origin; + Size size; +} Rect; + +Rect rect = {{1, 2}, {3, 4}}; +Point origin = {5, 6}; +Size size = {7, 8}; + +int +test_struct_1(Rect rect) +{ + return rect.origin.x; +} + +int +test_struct_2(Rect rect) +{ + return rect.origin.y; +} + +int +test_struct_3(Rect rect) +{ + return rect.size.width; +} + +int +test_struct_4(Rect rect) +{ + return rect.size.height; +} + +int +main() +{ + int ret = 0; + ret |= test_struct_1(rect) != 1; + ret |= test_struct_2(rect) != 2; + ret |= test_struct_3(rect) != 3; + ret |= test_struct_4(rect) != 4; + rect.origin = origin; + rect.size = size; + ret |= test_struct_1(rect) != 5; + ret |= test_struct_2(rect) != 6; + ret |= test_struct_3(rect) != 7; + ret |= test_struct_4(rect) != 8; + return ret; +} diff --git a/tools/qfcc/test/swap.r b/tools/qfcc/test/swap.r new file mode 100644 index 000000000..8a338be15 --- /dev/null +++ b/tools/qfcc/test/swap.r @@ -0,0 +1,15 @@ +void printf (string fmt, ...) = #0; + +float +swap (float a, float b) +{ + float t; + if (a < b) { t = a; a = b; b = t; } + return a - b; +} + +int +main () +{ + return swap (1, 2) > 0 ? 0 : 1; +} diff --git a/tools/qfcc/test/temp-component.r b/tools/qfcc/test/temp-component.r new file mode 100644 index 000000000..2db18a401 --- /dev/null +++ b/tools/qfcc/test/temp-component.r @@ -0,0 +1,13 @@ +float +gety(vector v, vector z) +{ + return (v + z).y; +} + +int +main () +{ + vector a = [1, 2, 3]; + vector b = [1, 2, 6]; + return gety (a, b) != 4; +} diff --git a/tools/qfcc/test/test-bi.c b/tools/qfcc/test/test-bi.c index bec2b247e..a13cf9ee7 100644 --- a/tools/qfcc/test/test-bi.c +++ b/tools/qfcc/test/test-bi.c @@ -40,47 +40,48 @@ #include "QF/dstring.h" #include "QF/progs.h" +#include "rua_internal.h" #include "test-bi.h" static void -bi_printf (progs_t *pr) +bi_printf (progs_t *pr, void *data) { - const char *fmt = P_GSTRING (pr, 0); - int count = pr->pr_argc - 1; - pr_type_t **args = pr->pr_params + 1; static dstring_t *dstr; - if (!dstr) + if (!dstr) { dstr = dstring_new (); - else + } else { dstring_clear (dstr); + } - PR_Sprintf (pr, dstr, "bi_printf", fmt, count, args); - if (dstr->str) + RUA_Sprintf (pr, dstr, "printf", 0); + + if (dstr->str) { fputs (dstr->str, stdout); + } } static void -bi_errno (progs_t *pr) +bi_errno (progs_t *pr, void *data) { R_INT (pr) = errno; } static void -bi_strerror (progs_t *pr) +bi_strerror (progs_t *pr, void *data) { int err = P_INT (pr, 0); RETURN_STRING (pr, strerror (err)); } static void -bi_exit (progs_t *pr) +bi_exit (progs_t *pr, void *data) { exit (P_INT (pr, 0)); } static void -bi_spawn (progs_t *pr) +bi_spawn (progs_t *pr, void *data) { edict_t *ed; ed = ED_Alloc (pr); @@ -88,25 +89,27 @@ bi_spawn (progs_t *pr) } static void -bi_remove (progs_t *pr) +bi_remove (progs_t *pr, void *data) { edict_t *ed; ed = P_EDICT (pr, 0); ED_Free (pr, ed); } +#define bi(x,np,params...) {#x, bi_##x, -1, np, {params}} +#define p(type) PR_PARAM(type) static builtin_t builtins[] = { - {"printf", bi_printf, -1}, - {"errno", bi_errno, -1}, - {"strerror", bi_strerror, -1}, - {"exit", bi_exit, -1}, - {"spawn", bi_spawn, -1}, - {"remove", bi_remove, -1}, + bi(printf, -2, p(string)), + bi(errno, 0), + bi(strerror, 1, p(int)), + bi(exit, 1, p(int)), + bi(spawn, 0), + bi(remove, 1, p(entity)), {0} }; void BI_Init (progs_t *pr) { - PR_RegisterBuiltins (pr, builtins); + PR_RegisterBuiltins (pr, builtins, 0); } diff --git a/tools/qfcc/test/test-defspace.c b/tools/qfcc/test/test-defspace.c new file mode 100644 index 000000000..cbb80d623 --- /dev/null +++ b/tools/qfcc/test/test-defspace.c @@ -0,0 +1,151 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include + +#include "tools/qfcc/include/class.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/options.h" +#include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/qfcc.h" + +#include "tools/qfcc/test/test-defspace.h" + +options_t options; +pr_info_t pr; +function_t *current_func; +class_type_t *current_class; + +void +free_def (def_t *def) +{ + if (0) free (def); +} + +__attribute__((const))const char * +get_class_name (class_type_t *class_type, int prett) +{ + return 0; +} + +expr_t * +new_expr (void) +{ + return calloc(1, sizeof (expr_t)); +} + +static int +check_init_state (const defspace_t *space, const char *name) +{ + int pass = 1; + + if (space->free_locs) { + printf ("%s free_locs not null\n", name); + pass = 0; + } + if (space->defs) { + printf ("%s defs not null\n", name); + pass = 0; + } + if (space->def_tail != &space->defs) { + printf ("%s def_tail not pointing to defs\n", name); + pass = 0; + } + if (space->data) { + printf ("%s data not null\n", name); + pass = 0; + } + if (space->size) { + printf ("%s size not 0\n", name); + pass = 0; + } + if (space->max_size) { + printf ("%s max_size not 0\n", name); + pass = 0; + } + if (!space->grow) { + printf ("%s grow is null\n", name); + pass = 0; + } + if (space->qfo_space) { + printf ("%s qfo_space not 0\n", name); + pass = 0; + } + return pass; +} + +static int +test_init (void) +{ + int pass = 1; + defspace_t *backed = defspace_new (ds_backed); + defspace_t *virtual = defspace_new (ds_virtual); + + if (backed->grow == virtual->grow) { + printf ("expected different grow functions for backed and virtual\n"); + pass = 0; + } + + if (backed->type != ds_backed) { + printf ("backed ds has wrong type\n"); + pass = 0; + } + + if (virtual->type != ds_virtual) { + printf ("virtual ds has wrong type\n"); + pass = 0; + } + + pass &= check_init_state (backed, "backed"); + pass &= check_init_state (virtual, "virtual"); + + return pass; +} + +static int +test_aligned_alloc (void) +{ + defspace_t *space = defspace_new (ds_virtual); + struct { + int size, align; + } allocations[6] = { + { 2, 2 }, + { 2, 2 }, + { 1, 1 }, + { 4, 4 }, + { 2, 2 }, + { 2, 2 }, + }; + int offsets[6]; + for (int i = 0; i < 6; i++) { + offsets[i] = defspace_alloc_aligned_loc (space, allocations[i].size, + allocations[i].align); + } + for (int i = 0; i < 5; i++) { + for (int j = i + 1; j < 6; j++) { + if (offsets[i] == offsets[j]) { + printf ("duplicate offset in allocations"); + printf ("%d %d %d %d %d %d\n", + offsets[0], offsets[1], offsets[2], + offsets[3], offsets[4], offsets[5]); + return 0; + } + } + } + return 1; +} + +int +main (int argc, const char **argv) +{ + pr.strings = strpool_new (); + + int pass = 1; + + pass &= test_init (); + pass &= test_aligned_alloc (); + + return !pass; +} diff --git a/tools/qfcc/test/test-defspace.h b/tools/qfcc/test/test-defspace.h new file mode 100644 index 000000000..ab6fcd1e8 --- /dev/null +++ b/tools/qfcc/test/test-defspace.h @@ -0,0 +1,6 @@ +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/defspace.h" + +int def_list_is_empty (const defspace_t *space); +int def_list_is_valid (const defspace_t *space); +int free_locs_is_valid (const defspace_t *space); diff --git a/tools/qfcc/test/test-harness.c b/tools/qfcc/test/test-harness.c index 87cd26c55..b277e99f1 100644 --- a/tools/qfcc/test/test-harness.c +++ b/tools/qfcc/test/test-harness.c @@ -31,6 +31,7 @@ # include "config.h" #endif +#include #include #include @@ -47,6 +48,7 @@ #define MAX_EDICTS 64 // 64 edicts should be enough for testing #define MAX_HEAP 1024*1024 // 1MB should be enough for testing +#define MAX_STACK 64*1024 // 64kB should be enough for testing // keep me sane when adding long options :P enum { @@ -70,10 +72,12 @@ static const char *short_options = "V" // version ; +static edict_t test_edicts[MAX_EDICTS]; + static edict_t *edicts; -static int num_edicts; -static int reserved_edicts; -static progs_t pr; +static pr_uint_t num_edicts; +static pr_uint_t reserved_edicts; +static progs_t test_pr; static const char *this_program; static struct { @@ -96,7 +100,7 @@ open_file (const char *path, int *len) } static void * -load_file (progs_t *pr, const char *name) +load_file (progs_t *pr, const char *name, off_t *_size) { QFile *file; int size; @@ -104,7 +108,7 @@ load_file (progs_t *pr, const char *name) file = open_file (name, &size); if (!file) { - file = open_file (va ("%s.gz", name), &size); + file = open_file (va (0, "%s.gz", name), &size); if (!file) { return 0; } @@ -112,13 +116,18 @@ load_file (progs_t *pr, const char *name) sym = malloc (size + 1); sym[size] = 0; Qread (file, sym, size); + *_size = size; return sym; } +#define ALIGN 32 + static void * allocate_progs_mem (progs_t *pr, int size) { - return malloc (size); + intptr_t mem = (intptr_t) malloc (size + ALIGN); + mem = (mem + ALIGN - 1) & ~(ALIGN - 1); + return (void *) mem; } static void @@ -127,31 +136,49 @@ free_progs_mem (progs_t *pr, void *mem) free (mem); } +static int +init_edicts (progs_t *pr) +{ + memset (test_edicts, 0, sizeof (test_edicts)); + + // init the data field of the edicts + for (int i = 0; i < MAX_EDICTS; i++) { + edict_t *ent = EDICT_NUM (&test_pr, i); + ent->pr = &test_pr; + ent->entnum = i; + ent->edict = EDICT_TO_PROG (&test_pr, ent); + } + return 1; +} + static void init_qf (void) { Sys_Init (); - Cvar_Get ("developer", va ("%d", options.developer), 0, 0, 0); + developer = options.developer; - Memory_Init (malloc (1024 * 1024), 1024 * 1024); + Memory_Init (Sys_Alloc (1024 * 1024), 1024 * 1024); - Cvar_Get ("pr_debug", "2", 0, 0, 0); - Cvar_Get ("pr_boundscheck", "2", 0, 0, 0); - - pr.edicts = &edicts; - pr.num_edicts = &num_edicts; - pr.reserved_edicts = &reserved_edicts; - pr.load_file = load_file; - pr.allocate_progs_mem = allocate_progs_mem; - pr.free_progs_mem = free_progs_mem; - pr.no_exec_limit = 0; // absolutely want a limit! - pr.pr_trace = options.trace; + test_pr.pr_edicts = &edicts; + test_pr.num_edicts = &num_edicts; + test_pr.reserved_edicts = &reserved_edicts; + test_pr.load_file = load_file; + test_pr.allocate_progs_mem = allocate_progs_mem; + test_pr.free_progs_mem = free_progs_mem; + test_pr.no_exec_limit = 0; // absolutely want a limit! PR_Init_Cvars (); - PR_Init (); - RUA_Init (&pr, 0); - PR_Cmds_Init(&pr); - BI_Init (&pr); + + pr_debug = options.trace > 1 ? 4 : 2; + pr_boundscheck = 2; + pr_deadbeef_locals = 1; + + PR_AddLoadFunc (&test_pr, init_edicts); + PR_Init (&test_pr); + + RUA_Init (&test_pr, 0); + PR_Cmds_Init(&test_pr); + BI_Init (&test_pr); } static int @@ -164,11 +191,20 @@ load_progs (const char *name) if (!file) { return 0; } - pr.progs_name = name; - PR_LoadProgsFile (&pr, file, size, 16, 1024 * 1024); + test_pr.progs_name = name; + test_pr.max_edicts = MAX_EDICTS; + test_pr.zone_size = MAX_HEAP; + test_pr.stack_size = MAX_STACK; + edicts = test_edicts; + + PR_LoadProgsFile (&test_pr, file, size); Qclose (file); - if (!PR_RunLoadFuncs (&pr)) - PR_Error (&pr, "unable to load %s", pr.progs_name); + if (!PR_RunLoadFuncs (&test_pr)) + PR_Error (&test_pr, "unable to load %s", test_pr.progs_name); + if (!PR_RunPostLoadFuncs (&test_pr)) + PR_Error (&test_pr, "unable to load %s", test_pr.progs_name); + test_pr.pr_trace_depth = -1; + test_pr.pr_trace = options.trace; return 1; } @@ -204,7 +240,7 @@ parse_options (int argc, char **argv) options.flote = 1; break; case 't': - options.trace = 1; + options.trace++; break; case 'h': usage (0); @@ -223,9 +259,9 @@ int main (int argc, char **argv) { dfunction_t *dfunc; - func_t main_func = 0; + pr_func_t main_func = 0; const char *name = "progs.dat"; - string_t *pr_argv; + pr_string_t *pr_argv; int pr_argc = 1, i; i = parse_options (argc, argv); @@ -240,26 +276,32 @@ main (int argc, char **argv) if (!load_progs (name)) Sys_Error ("couldn't load %s", name); - PR_PushFrame (&pr); - if (argc > 2) - pr_argc = argc - 1; - pr_argv = PR_Zone_Malloc (&pr, (pr_argc + 1) * 4); - pr_argv[0] = PR_SetTempString (&pr, name); - for (i = 1; i < pr_argc; i++) - pr_argv[i] = PR_SetTempString (&pr, argv[1 + i]); + if ((dfunc = PR_FindFunction (&test_pr, ".main")) + || (dfunc = PR_FindFunction (&test_pr, "main"))) { + main_func = dfunc - test_pr.pr_functions; + } else { + PR_Undefined (&test_pr, "function", "main"); + } + + PR_PushFrame (&test_pr); + if (argc) { + pr_argc = argc; + } + pr_argv = PR_Zone_Malloc (&test_pr, (pr_argc + 1) * 4); + pr_argv[0] = PR_SetTempString (&test_pr, name); + for (i = 1; i < pr_argc; i++) { + pr_argv[i] = PR_SetTempString (&test_pr, argv[i]); + } pr_argv[i] = 0; - if ((dfunc = PR_FindFunction (&pr, ".main")) - || (dfunc = PR_FindFunction (&pr, "main"))) - main_func = dfunc - pr.pr_functions; - else - PR_Undefined (&pr, "function", "main"); - PR_RESET_PARAMS (&pr); - P_INT (&pr, 0) = pr_argc; - P_POINTER (&pr, 1) = PR_SetPointer (&pr, pr_argv); - PR_ExecuteProgram (&pr, main_func); - PR_PopFrame (&pr); + PR_RESET_PARAMS (&test_pr); + P_INT (&test_pr, 0) = pr_argc; + P_POINTER (&test_pr, 1) = PR_SetPointer (&test_pr, pr_argv); + test_pr.pr_argc = 2; + + PR_ExecuteProgram (&test_pr, main_func); + PR_PopFrame (&test_pr); if (options.flote) - return R_FLOAT (&pr); - return R_INT (&pr); + return R_FLOAT (&test_pr); + return R_INT (&test_pr); } diff --git a/tools/qfcc/test/test-harness.h b/tools/qfcc/test/test-harness.h index 959910534..51c5bde12 100644 --- a/tools/qfcc/test/test-harness.h +++ b/tools/qfcc/test/test-harness.h @@ -1,6 +1,11 @@ +void *obj_malloc (int size) = #0; void printf (string fmt, ...) = #0; int errno (void) = #0; string strerror (int err) = #0; void exit (int code) = #0; +void traceon (void) = #0; +void traceoff (void) = #0; entity spawn (void) = #0; void remove (entity e) = #0; +id obj_msgSend (id receiver, SEL op, ...) = #0; +void __obj_exec_class (struct obj_module *msg) = #0; diff --git a/tools/qfcc/test/triangle.r b/tools/qfcc/test/triangle.r new file mode 100644 index 000000000..cec1f6884 --- /dev/null +++ b/tools/qfcc/test/triangle.r @@ -0,0 +1,33 @@ +void printf (string fmt, ...) = #0; +float (string s) stof = #0; +@overload float (float x) sqrt = #0; + +float +heron (float a, float b, float c) +{ + float s = (a + b + c) / 2; + return sqrt (s*(s-a)*(s-b)*(s-c)); +} + +float +kahan (float a, float b, float c) +{ + float t; + if (a < b) { t = a; a = b; b = t; } + if (b < c) { t = b; b = c; c = t; } + if (a < b) { t = a; a = b; b = t; } + return sqrt ((a + (b + c))*(c - (a - b))*(c + (a - b))*(a + (b - c)))/4; +} + +int +main (int argc, string *argv) +{ + float a = stof (argv[1]); + float b = stof (argv[2]); + float c = stof (argv[3]); + float expt = stof (argv[4]); + float h = heron (a, b, c); + float k = kahan (a, b, c); + printf ("%.9g %.9g %d\n", h, k, k == expt); + return k != expt; +} diff --git a/tools/qfcc/test/tw-defspace.c b/tools/qfcc/test/tw-defspace.c new file mode 100644 index 000000000..c1f599810 --- /dev/null +++ b/tools/qfcc/test/tw-defspace.c @@ -0,0 +1,55 @@ +#include "../source/defspace.c" +#include "test-defspace.h" + +__attribute__((pure)) int +def_list_is_empty (const defspace_t *space) +{ + return (!space->defs && space->def_tail == &space->defs); +} + +__attribute__((pure)) int +def_list_is_valid (const defspace_t *space) +{ + def_t *const *d = &space->defs; + + while (*d) { + d = &(*d)->next; + } + return d == space->def_tail; +} + +int +free_locs_is_valid (const defspace_t *space) +{ + int free_space = 0; + const locref_t *loc; + + for (loc = space->free_locs; loc; loc = loc->next) { + if (loc->ofs < 0) { + printf ("negative offset in free_locs\n"); + return 0; + } + if (loc->size <= 0) { + printf ("zero or negative size in free_locs\n"); + return 0; + } + if (loc->next && loc->ofs > loc->next->ofs) { + printf ("free_locs not in ascending order\n"); + return 0; + } + if (loc->next && loc->ofs + loc->size > loc->next->ofs) { + printf ("overlap in free_locs\n"); + return 0; + } + if (loc->next && loc->ofs + loc->size == loc->next->ofs) { + printf ("adjoining nodes in free_locs\n"); + return 0; + } + free_space += loc->size; + } + if (free_space > space->size) { + printf ("free_locs describes too much free space\n"); + return 0; + } + return 1; +} diff --git a/tools/qfcc/test/tw-diagnostic.c b/tools/qfcc/test/tw-diagnostic.c new file mode 100644 index 000000000..d84f8ad33 --- /dev/null +++ b/tools/qfcc/test/tw-diagnostic.c @@ -0,0 +1 @@ +#include "../source/diagnostic.c" diff --git a/tools/qfcc/test/tw-strpool.c b/tools/qfcc/test/tw-strpool.c new file mode 100644 index 000000000..74fbb8d75 --- /dev/null +++ b/tools/qfcc/test/tw-strpool.c @@ -0,0 +1 @@ +#include "../source/strpool.c" diff --git a/tools/qfcc/test/twice-called.r b/tools/qfcc/test/twice-called.r new file mode 100644 index 000000000..9542af0f6 --- /dev/null +++ b/tools/qfcc/test/twice-called.r @@ -0,0 +1,28 @@ +#include "test-harness.h" + +int counter; + +int +function (void) +{ + return ++counter; +} + +int +main (void) +{ + int ret = 0; + counter = 0; + //function (); + //if (counter != 1) { + //printf ("discarded return not called only once\n"); + // ret = 1; + //} + counter = 0; + printf ("function: %d\n", function ()); + if (counter != 1) { + //printf ("used return not called only once\n"); + ret = 1; + } + return ret; +} diff --git a/tools/qfcc/test/typedef.r b/tools/qfcc/test/typedef.r new file mode 100644 index 000000000..61df8f9b6 --- /dev/null +++ b/tools/qfcc/test/typedef.r @@ -0,0 +1,66 @@ +#include + +// can't link against libr.a (may not be built) +void *PR_FindGlobal (string name) = #0; +void printf (string fmt, ...) = #0; + +qfot_type_encodings_t *encodings; + +typedef int *foo; +typedef int *bar; + +foo baz; +bar snafu; + +qfot_type_t * +next_type (qfot_type_t *type) +{ + int size = type.size; + if (!size) + size = 4; + return (qfot_type_t *) ((int *) type + size); +} + +int +check_alias (string name, qfot_type_t *alias) +{ + if (alias.meta != ty_basic || alias.type != ev_ptr + || alias.fldptr.aux_type.meta != ty_basic + || alias.fldptr.aux_type.type != ev_int) { + printf ("%s is not a *int alias\n", name); + return 0; + } + return 1; +} + +int +main (void) +{ + baz = snafu; + + int found_foo = 0; + int found_bar = 0; + qfot_type_t *type; + + encodings = PR_FindGlobal (".type_encodings"); + + for (type = encodings.types; + ((int *)type - (int *) encodings.types) < encodings.size; + type = next_type (type)) { + if (type.meta == ty_alias) { + if (type.alias.name == "foo") { + found_foo = check_alias (type.alias.name, + type.alias.aux_type); + } + if (type.alias.name == "bar") { + found_bar = check_alias (type.alias.name, + type.alias.aux_type); + } + } + } + if (!(found_bar && found_foo)) { + printf ("missing typedef: foo: %d bar:%d\n", found_foo, found_bar); + return 1; + } + return 0; +} diff --git a/tools/qfcc/test/typelinker.h b/tools/qfcc/test/typelinker.h new file mode 100644 index 000000000..1968a52cc --- /dev/null +++ b/tools/qfcc/test/typelinker.h @@ -0,0 +1,3 @@ +typedef struct plitem_s *plitem_t; + +@extern plitem_t PL_GetPropertyList (string str); diff --git a/tools/qfcc/test/typelinker_a.r b/tools/qfcc/test/typelinker_a.r new file mode 100644 index 000000000..57c98ca76 --- /dev/null +++ b/tools/qfcc/test/typelinker_a.r @@ -0,0 +1,7 @@ +#include "typelinker.h" + +int main () +{ + PL_GetPropertyList ("{}"); + return 0; +} diff --git a/tools/qfcc/test/typelinker_b.r b/tools/qfcc/test/typelinker_b.r new file mode 100644 index 000000000..54336eae3 --- /dev/null +++ b/tools/qfcc/test/typelinker_b.r @@ -0,0 +1,3 @@ +#include "typelinker.h" + +plitem_t PL_GetPropertyList (string str) = #0; diff --git a/tools/qfcc/test/typeredef1.r b/tools/qfcc/test/typeredef1.r new file mode 100644 index 000000000..6eb9ffaa4 --- /dev/null +++ b/tools/qfcc/test/typeredef1.r @@ -0,0 +1,2 @@ +typedef int foo; +typedef int foo; diff --git a/tools/qfcc/test/typeredef2.r b/tools/qfcc/test/typeredef2.r new file mode 100644 index 000000000..22028d74b --- /dev/null +++ b/tools/qfcc/test/typeredef2.r @@ -0,0 +1,2 @@ +typedef int foo(void); +typedef int foo(void); diff --git a/tools/qfcc/test/unaryminus.r b/tools/qfcc/test/unaryminus.r new file mode 100644 index 000000000..5149a224a --- /dev/null +++ b/tools/qfcc/test/unaryminus.r @@ -0,0 +1,13 @@ +int strlen (string s) = #0; + +int foo (string bar) +{ + int len; + len = -strlen (bar); + return len; +} + +int main (void) +{ + return 0; +} diff --git a/tools/qfcc/test/vecaddr.r b/tools/qfcc/test/vecaddr.r new file mode 100644 index 000000000..965fbf716 --- /dev/null +++ b/tools/qfcc/test/vecaddr.r @@ -0,0 +1,34 @@ +void printf (string fmt, ...) = #0; + +#if __RUAMOKO__ > 1 +#define dot @dot +#define X .y +#else +#define dot * +#define X +#endif + +void forcelive (float z) +{ +} + +float foo (vector _v, float _z) +{ + vector v = _v; + float z = _z; + _v = nil; + _z = 0; + forcelive (_z); + forcelive (z); + return (v dot *(vector*)(&v.y))X; +} + +int +main (int argc, string *argv) +{ + vector v = [1, 2, 3]; + vector w = [2, 3, 4]; + float f; + printf ("%v %g %g %g\n", v, v dot v, v dot w, f=foo (v, 4)); + return f != (v dot w)X; +} diff --git a/tools/qfcc/test/vecconst.r b/tools/qfcc/test/vecconst.r new file mode 100644 index 000000000..6beaff9c2 --- /dev/null +++ b/tools/qfcc/test/vecconst.r @@ -0,0 +1,60 @@ +vector mins = '-16 -16 -24'; +vector maxs = '16 16 32'; +ivec4 i4 = '1 2 -3 4'; +vec4 v4 = '1 2 -3 4'; +lvec4 l4 = '1 2 -3 4'; +uivec4 ui4 = '1 2 -3 4'; +dvec4 d4 = '1 2 -3 4'; +ulvec4 ul4 = '1 2 -3 4'; + +int +check_v (vector v, float x, float y, float z) +{ + return v.x != x || v.y != y || v.z != z; +} + +int check_ivec4 (ivec4 v, int x, int y, int z, int w) +{ + return v.x != x || v.y != y || v.z != z || v.w != w; +} + +int check_vec4 (vec4 v, float x, float y, float z, float w) +{ + return v.x != x || v.y != y || v.z != z || v.w != w; +} + +int check_lvec4 (lvec4 v, long x, long y, long z, long w) +{ + return v.x != x || v.y != y || v.z != z || v.w != w; +} + +int check_uivec4 (uivec4 v, unsigned x, unsigned y, unsigned z, unsigned w) +{ + return v.x != x || v.y != y || v.z != z || v.w != w; +} + +int check_dvec4 (dvec4 v, double x, double y, double z, double w) +{ + return v.x != x || v.y != y || v.z != z || v.w != w; +} + +int check_ulvec4 (ulvec4 v, unsigned long x, unsigned long y, unsigned long z, + unsigned long w) +{ + return v.x != x || v.y != y || v.z != z || v.w != w; +} + +int +main () +{ + int ret = 0; + ret |= check_v (mins, -16, -16, -24); + ret |= check_v (maxs, 16, 16, 32); + ret |= check_ivec4 (i4, 1, 2, -3, 4); + ret |= check_vec4 (v4, 1, 2, -3, 4); + ret |= check_lvec4 (l4, 1, 2, -3, 4); + ret |= check_uivec4 (ui4, 1, 2, -3, 4); + ret |= check_dvec4 (d4, 1, 2, -3, 4); + ret |= check_ulvec4 (ul4, 1, 2, -3, 4); + return ret; +} diff --git a/tools/qfcc/test/vecexpr.r b/tools/qfcc/test/vecexpr.r new file mode 100644 index 000000000..e4ca4489b --- /dev/null +++ b/tools/qfcc/test/vecexpr.r @@ -0,0 +1,49 @@ +#include "test-harness.h" + +vector t1(); +vector t2(float x); + +vector +t3(float x) +{ + return [x, t2(9).z, x] * 2; +} + +vector +t1() +{ + return [1, 2, 3]; +} + +vector +t2(float x) +{ + return [x, x, x]; +} + +int +main () +{ + int ret = 0; + float x = 4; + float y = 5; + vector v; + + v = t2(5); + if (v != [5, 5, 5]) { + printf("t2(5) = %v\n", v); + ret |= 1; + } + v = t3 (5); + if (v != [10, 18, 10]) { + printf("t3(5) = %v\n", v); + ret |= 1; + } + v = [x, y, 0] / 2; + if (v != [2, 2.5, 0]) { + printf("v = %v\n", v); + ret |= 1; + } + + return ret; +} diff --git a/tools/qfcc/test/vecinit.r b/tools/qfcc/test/vecinit.r index 6503b390d..653f9e25d 100644 --- a/tools/qfcc/test/vecinit.r +++ b/tools/qfcc/test/vecinit.r @@ -8,8 +8,33 @@ foo (float x, float y, float z) return v; } +float w = 2; +float h = 4; + +vector +bar (void) +{ + vector pos; + + pos.x = w; + pos.y = h; + pos.z = 0; + return pos; +} + +vector +baz (float w, float h) +{ + vector p = [w, h, 0] / 2; + return p; +} + int main () { - return 0; + int ret = 0; + ret |= foo(1,2,3) != [1, 2, 3]; + ret |= bar() != [2, 4, 0]; + ret |= baz(5, 6) != [2.5, 3, 0]; + return ret; } diff --git a/tools/qfcc/test/zerolinker.r b/tools/qfcc/test/zerolinker.r new file mode 100644 index 000000000..7b14f4a6e --- /dev/null +++ b/tools/qfcc/test/zerolinker.r @@ -0,0 +1,45 @@ +#include + +// can't link against libr.a (may not be built) +void *PR_FindGlobal (string name) = #0; +void printf (string fmt, ...) = #0; + +qfot_type_encodings_t *encodings; + +qfot_type_t * +next_type (qfot_type_t *type) +{ + int size = type.size; + if (!size) + size = 4; + return (qfot_type_t *) ((int *) type + size); +} + +int +main (void) +{ + int found_param = 0; + int found_zero = 0; + qfot_type_t *type; + + encodings = PR_FindGlobal (".type_encodings"); + + for (type = encodings.types; + ((int *)type - (int *) encodings.types) < encodings.size; + type = next_type (type)) { + if (type.meta == ty_union) { + if (type.strct.tag == "tag @param") { + found_param = 1; + } + if (type.strct.tag == "tag @zero") { + found_zero = 1; + } + } + } + if (!(found_param && found_zero)) { + printf ("missing struct: param: %d zero:%d\n", + found_param, found_zero); + return 1; + } + return 0; +} diff --git a/tools/qflight/Makefile.am b/tools/qflight/Makefile.am deleted file mode 100644 index e4102a7e9..000000000 --- a/tools/qflight/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -SUBDIRS= include source - -man_MANS= qflight.1 - -EXTRA_DIST= qflight.1 diff --git a/tools/qflight/Makemodule.am b/tools/qflight/Makemodule.am new file mode 100644 index 000000000..cd4d971c6 --- /dev/null +++ b/tools/qflight/Makemodule.am @@ -0,0 +1,4 @@ +include tools/qflight/include/Makemodule.am +include tools/qflight/source/Makemodule.am + +man_MANS += tools/qflight/qflight.1 diff --git a/tools/qflight/include/Makefile.am b/tools/qflight/include/Makefile.am deleted file mode 100644 index 3d7e10861..000000000 --- a/tools/qflight/include/Makefile.am +++ /dev/null @@ -1,3 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -EXTRA_DIST= entities.h light.h noise.h options.h properties.h threads.h diff --git a/tools/qflight/include/Makemodule.am b/tools/qflight/include/Makemodule.am new file mode 100644 index 000000000..747bdca73 --- /dev/null +++ b/tools/qflight/include/Makemodule.am @@ -0,0 +1,7 @@ +EXTRA_DIST += \ + tools/qflight/include/entities.h \ + tools/qflight/include/light.h \ + tools/qflight/include/noise.h \ + tools/qflight/include/options.h \ + tools/qflight/include/properties.h \ + tools/qflight/include/threads.h diff --git a/tools/qflight/include/entities.h b/tools/qflight/include/entities.h index 82bf378cc..0873369d7 100644 --- a/tools/qflight/include/entities.h +++ b/tools/qflight/include/entities.h @@ -34,7 +34,7 @@ /** \defgroup qflight_entities Light entity data. \ingroup qflight */ -//@{ +///@{ #define DEFAULTLIGHTLEVEL 300 #define DEFAULTFALLOFF 1.0f @@ -56,7 +56,7 @@ typedef struct entity_s { const char *classname; vec3_t origin; vec_t angle; - int light; + vec_t light; int sun_light[2]; vec3_t sun_color[2]; @@ -106,6 +106,6 @@ void GetVectorForKey (entity_t *ent, const char *key, vec3_t vec); void LoadEntities (void); void WriteEntitiesToString (void); -//@} +///@} #endif// __entities_h diff --git a/tools/qflight/include/light.h b/tools/qflight/include/light.h index 0899bc1b8..b5de5e1c5 100644 --- a/tools/qflight/include/light.h +++ b/tools/qflight/include/light.h @@ -37,7 +37,7 @@ /** \defgroup qflight_general General functions \ingroup qflight */ -//@{ +///@{ #define ON_EPSILON 0.1 #define MAXLIGHTS 1024 @@ -95,13 +95,11 @@ extern vec3_t bsp_origin; extern vec3_t bsp_xvector; extern vec3_t bsp_yvector; -extern qboolean extrasamples; - -extern float minlights[MAX_MAP_FACES]; +extern bool extrasamples; void LoadNodes (const char *file); -qboolean TestLine (lightinfo_t *l, const vec3_t start, const vec3_t stop); -qboolean TestSky (lightinfo_t *l, const vec3_t start, const vec3_t stop); +bool TestLine (lightinfo_t *l, const vec3_t start, const vec3_t stop); +bool TestSky (lightinfo_t *l, const vec3_t start, const vec3_t stop); void LightFace (lightinfo_t *l, int surfnum); void LightLeaf (dleaf_t *leaf); @@ -128,8 +126,8 @@ extern vec3_t *surfaceorgs; extern struct entity_s **novislights; extern int num_novislights; -const char *get_tex_name (int texindex); +const char *get_tex_name (int texindex) __attribute__((pure)); -//@} +///@} #endif// __light_h diff --git a/tools/qflight/include/noise.h b/tools/qflight/include/noise.h index 0017ab107..0b118d17a 100644 --- a/tools/qflight/include/noise.h +++ b/tools/qflight/include/noise.h @@ -31,12 +31,12 @@ /** \defgroup qflight_noise Light noise functions. \ingroup qflight */ -//@{ +///@{ -float noise3d (vec3_t v, int num); -float noiseXYZ (float x, float y, float z, int num); -float noise_scaled (vec3_t v, float s, int num); -float noise_perlin (vec3_t v, float p, int num); +float noise3d (vec3_t v, int num) __attribute__((pure)); +float noiseXYZ (float x, float y, float z, int num) __attribute__((const)); +float noise_scaled (vec3_t v, float s, int num) __attribute__((pure)); +float noise_perlin (vec3_t v, float p, int num) __attribute__((pure)); void snap_vector (vec3_t v_old, vec3_t v_new, float scale); -//@} +///@} diff --git a/tools/qflight/include/options.h b/tools/qflight/include/options.h index 22443ccae..32c6d6755 100644 --- a/tools/qflight/include/options.h +++ b/tools/qflight/include/options.h @@ -33,7 +33,7 @@ /** \defgroup qflight_options Light command line options. \ingroup qflight */ -//@{ +///@{ typedef struct { int verbosity; // 0=silent @@ -57,8 +57,8 @@ extern struct dstring_s *bspfile; extern const char *this_program; int DecodeArgs (int argc, char **argv); -void usage (int status); +void usage (int status) __attribute__((noreturn)); -//@} +///@} #endif//__options_h diff --git a/tools/qflight/include/properties.h b/tools/qflight/include/properties.h index a6b724d19..8d39361b7 100644 --- a/tools/qflight/include/properties.h +++ b/tools/qflight/include/properties.h @@ -33,7 +33,7 @@ /** \defgroup qflight_properties Lighting properties \ingroup qflight */ -//@{ +///@{ struct plitem_s; @@ -76,7 +76,7 @@ void parse_color (const char *str, vec3_t color); \arg HalfLife "R G B i" where R G & B are 0-255 and i is the same as for normal id lights. The RGB values will be scaled by 1/255 such that 255 becomes 1.0. - \arg RGB "R B B" where R G & B are left as-is and the intensity + \arg RGB "R G B" where R G & B are left as-is and the intensity is set to 1.0. \arg id Standard quake single value light intensity. The color is set to white ([1.0 1.0 1.0]). @@ -124,7 +124,7 @@ int parse_noise (const char *arg); The database is loaded via LoadProperties(). - If a set of properties named "worldspawn" is in the database, the it + If a set of properties named "worldspawn" is in the database, then it will be used for default values, otherwise the database will be ignored. @@ -163,7 +163,7 @@ void set_sun_properties (entity_t *ent, struct plitem_s *dict); Supported properties: \arg \c light see \ref parse_light \arg \c style light style: 0-254 - \arg \c angle spotlight con angle in degress. defaults to 20 + \arg \c angle spotlight cone angle in degress. defaults to 20 \arg \c wait light "falloff". defaults to 1.0 \arg \c lightradius size of light. interacts with falloff for distance clipping (?). defaults to 0 @@ -199,6 +199,6 @@ void set_properties (entity_t *ent, struct plitem_s *dict); */ void LoadProperties (const char *filename); -//@} +///@} #endif//__properties_h diff --git a/tools/qflight/include/threads.h b/tools/qflight/include/threads.h index 30b9cc59f..0fd5c00af 100644 --- a/tools/qflight/include/threads.h +++ b/tools/qflight/include/threads.h @@ -32,7 +32,7 @@ /** \defgroup qflight_threads Light thread handling. \ingroup qflight */ -//@{ +///@{ #if defined (HAVE_PTHREAD_H) && defined (HAVE_PTHREAD) @@ -66,6 +66,6 @@ typedef void *(threadfunc_t) (void *); void InitThreads (void); void RunThreadsOn (threadfunc_t func); -//@} +///@} #endif// __threads_h diff --git a/tools/qflight/source/Makefile.am b/tools/qflight/source/Makefile.am deleted file mode 100644 index cdf5005fb..000000000 --- a/tools/qflight/source/Makefile.am +++ /dev/null @@ -1,20 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -QFLIGHT_LIBS=@QFLIGHT_LIBS@ -QFLIGHT_DEPS=@QFLIGHT_DEPS@ -QFLIGHT_INCS=@QFLIGHT_INCS@ -PTHREAD_LDFLAGS=@PTHREAD_LDFLAGS@ -PTHREAD_CFLAGS=@PTHREAD_CFLAGS@ - -AM_CPPFLAGS= -I$(top_srcdir)/include $(QFLIGHT_INCS) $(PTHREAD_CFLAGS) - -bin_PROGRAMS= qflight - -qflight_SOURCES=\ - entities.c ltface.c noise.c options.c properties.c qflight.c \ - threads.c trace.c vis.c - - -qflight_LDFLAGS= $(PTHREAD_LDFLAGS) -qflight_LDADD= $(QFLIGHT_LIBS) -qflight_DEPENDENCIES= $(QFLIGHT_DEPS) diff --git a/tools/qflight/source/Makemodule.am b/tools/qflight/source/Makemodule.am new file mode 100644 index 000000000..8990cea7a --- /dev/null +++ b/tools/qflight/source/Makemodule.am @@ -0,0 +1,21 @@ +QFLIGHT_LIBS=@QFLIGHT_LIBS@ +QFLIGHT_DEPS=@QFLIGHT_DEPS@ +QFLIGHT_INCS=@QFLIGHT_INCS@ + +EXTRA_PROGRAMS += qflight +bin_PROGRAMS += @QFLIGHT_TARGETS@ + +qflight_SOURCES=\ + tools/qflight/source/entities.c \ + tools/qflight/source/ltface.c \ + tools/qflight/source/noise.c \ + tools/qflight/source/options.c \ + tools/qflight/source/properties.c \ + tools/qflight/source/qflight.c \ + tools/qflight/source/threads.c \ + tools/qflight/source/trace.c \ + tools/qflight/source/vis.c + +qflight_LDFLAGS= $(PTHREAD_LDFLAGS) +qflight_LDADD= $(QFLIGHT_LIBS) +qflight_DEPENDENCIES= $(QFLIGHT_DEPS) diff --git a/tools/qflight/source/entities.c b/tools/qflight/source/entities.c index 01c36077c..edc40a284 100644 --- a/tools/qflight/source/entities.c +++ b/tools/qflight/source/entities.c @@ -47,19 +47,19 @@ #include "QF/bspfile.h" #include "QF/dstring.h" #include "QF/mathlib.h" +#include "QF/plist.h" #include "QF/progs.h" -#include "QF/qfplist.h" #include "QF/qtypes.h" #include "QF/quakefs.h" #include "QF/script.h" #include "QF/sys.h" #include "QF/va.h" -#include "light.h" -#include "threads.h" -#include "entities.h" -#include "options.h" -#include "properties.h" +#include "tools/qflight/include/light.h" +#include "tools/qflight/include/threads.h" +#include "tools/qflight/include/entities.h" +#include "tools/qflight/include/options.h" +#include "tools/qflight/include/properties.h" entity_t *entities; int num_entities; @@ -76,7 +76,7 @@ const char *lighttargets[32]; static int -LightStyleForTargetname (const char *targetname, qboolean alloc) +LightStyleForTargetname (const char *targetname, bool alloc) { int i; @@ -125,7 +125,8 @@ MatchTargets (void) // set the style on the source ent for switchable lights if (entities[j].style) { entities[i].style = entities[j].style; - SetKeyValue (&entities[i], "style", va ("%i", entities[i].style)); + SetKeyValue (&entities[i], "style", va (0, "%d", + entities[i].style)); } if (entities[i].spotcone >= 0) { @@ -178,12 +179,12 @@ LoadEntities (void) script = Script_New (); Script_Start (script, "ent data", bsp->entdata); - entity_list = ED_ConvertToPlist (script, 1); + entity_list = ED_ConvertToPlist (script, 1, 0); Script_Delete (script); // start parsing num_entities = PL_A_NumObjects (entity_list); - entities = malloc (num_entities * sizeof (entity_t));; + entities = malloc (num_entities * sizeof (entity_t)); // go through all the entities for (i = 0; i < num_entities; i++) { @@ -207,7 +208,7 @@ LoadEntities (void) } entity->dict = PL_ObjectAtIndex (entity_list, i); - dict = PL_NewDictionary (); + dict = PL_NewDictionary (0); // go through all the keys in this entity keys = PL_D_AllKeys (entity->dict); @@ -240,7 +241,7 @@ LoadEntities (void) } if (options.verbosity > 1 && entity->targetname) - printf ("%s %d %d\n", entity->targetname, entity->light, + printf ("%s %g %d\n", entity->targetname, entity->light, entity->style); // all fields have been parsed @@ -273,7 +274,7 @@ LoadEntities (void) entity->persistence = 1; } } - PL_Free (dict); + PL_Release (dict); if (entity->light) { // convert to subtraction to the brightness for the whole light, @@ -333,12 +334,9 @@ LoadEntities (void) if (entity->classname && !strcmp (entity->classname, "light")) { if (entity->targetname && entity->targetname[0] && !entity->style) { - char s[16]; - entity->style = LightStyleForTargetname (entity->targetname, true); - sprintf (s, "%i", entity->style); - SetKeyValue (entity, "style", s); + SetKeyValue (entity, "style", va (0, "%d", entity->style)); } } } diff --git a/tools/qflight/source/ltface.c b/tools/qflight/source/ltface.c index acb157c36..64ea9d800 100644 --- a/tools/qflight/source/ltface.c +++ b/tools/qflight/source/ltface.c @@ -52,11 +52,11 @@ #include "compat.h" -#include "light.h" -#include "entities.h" -#include "noise.h" -#include "options.h" -#include "threads.h" +#include "tools/qflight/include/light.h" +#include "tools/qflight/include/entities.h" +#include "tools/qflight/include/noise.h" +#include "tools/qflight/include/options.h" +#include "tools/qflight/include/threads.h" int c_bad; int c_culldistplane, c_proper; @@ -71,7 +71,7 @@ get_tex_name (int texindex) if (bsp->texdatasize) { mtl = (dmiptexlump_t *) bsp->texdata; miptex = bsp->texinfo[texindex].miptex; - if (mtl->dataofs[miptex] != -1) { + if (mtl->dataofs[miptex] != ~0u) { mt = (miptex_t *) (bsp->texdata + mtl->dataofs[miptex]); return mt->name; } @@ -172,7 +172,6 @@ CalcFaceVectors (lightinfo_t *l, vec3_t faceorg) static void CalcFaceExtents (lightinfo_t *l) { - int i, j, e; vec_t mins[2], maxs[2], val; dface_t *s; dvertex_t *v; @@ -185,14 +184,14 @@ CalcFaceExtents (lightinfo_t *l) tex = &bsp->texinfo[s->texinfo]; - for (i = 0; i < s->numedges; i++) { - e = bsp->surfedges[s->firstedge + i]; + for (uint32_t i = 0; i < s->numedges; i++) { + int e = bsp->surfedges[s->firstedge + i]; if (e >= 0) v = bsp->vertexes + bsp->edges[e].v[0]; else v = bsp->vertexes + bsp->edges[-e].v[1]; - for (j = 0; j < 2; j++) { + for (int j = 0; j < 2; j++) { val = DotProduct (v->point, tex->vecs[j]) + tex->vecs[j][3]; if (val < mins[j]) mins[j] = val; @@ -201,7 +200,7 @@ CalcFaceExtents (lightinfo_t *l) } } - for (i = 0; i < 2; i++) { + for (int i = 0; i < 2; i++) { l->exactmins[i] = mins[i]; l->exactmaxs[i] = maxs[i]; @@ -289,7 +288,7 @@ static void SingleLightFace (entity_t *light, lightinfo_t *l) { int mapnum, i; - qboolean hit; + bool hit; vec3_t incoming, spotvec; vec_t angle, dist, idist, lightfalloff, lightsubtract, spotcone; vec_t add = 0.0; @@ -488,7 +487,6 @@ SkyLightFace (entity_t *ent, int sun, lightinfo_t *l) // Check each point... VectorCopy (sun_dir, incoming); VectorNormalize (incoming); - angle = DotProduct (incoming, l->facenormal); //anglesense = 0.5; //FIXME // FIXME global @@ -499,11 +497,9 @@ SkyLightFace (entity_t *ent, int sun, lightinfo_t *l) if (!TestSky (l, point->v, sun_dir)) continue; + add = sun_light; - continue; - add *= angle; - add *= options.extrascale; sample = &l->sample[mapnum][point->samplepos]; @@ -511,38 +507,6 @@ SkyLightFace (entity_t *ent, int sun, lightinfo_t *l) } } -#if 0 -static void -FixMinlight (lightinfo_t *l) -{ - float minlight; - int i, j; - - minlight = minlights[l->surfnum]; - - // if minlight is set, there must be a style 0 light map - if (!minlight) - return; - - for (i = 0; i < l->numlightstyles; i++) { - if (l->lightstyles[i] == 0) - break; - } - if (i == l->numlightstyles) { - if (l->numlightstyles == MAXLIGHTMAPS) - return; // oh well.. - for (j = 0; j < l->numsurfpt; j++) - l->lightmaps[i][j] = minlight; - l->lightstyles[i] = 0; - l->numlightstyles++; - } else { - for (j = 0; j < l->numsurfpt; j++) - if (l->lightmaps[i][j] < minlight) - l->lightmaps[i][j] = minlight; - } -} -#endif - void LightFace (lightinfo_t *l, int surfnum) { @@ -593,8 +557,6 @@ LightFace (lightinfo_t *l, int surfnum) SkyLightFace (world_entity, i, l); } -// FixMinlight (&l); - for (i = 0; i < MAXLIGHTMAPS; i++) if (l->lightstyles[i] == 255) break; diff --git a/tools/qflight/source/noise.c b/tools/qflight/source/noise.c index 8d8a87758..b34e31ee1 100644 --- a/tools/qflight/source/noise.c +++ b/tools/qflight/source/noise.c @@ -35,7 +35,7 @@ #include "QF/qtypes.h" -#include "noise.h" +#include "tools/qflight/include/noise.h" // returns the 3D noise value for a point in space float diff --git a/tools/qflight/source/options.c b/tools/qflight/source/options.c index 88a3584b9..b50753169 100644 --- a/tools/qflight/source/options.c +++ b/tools/qflight/source/options.c @@ -44,9 +44,9 @@ #include "compat.h" -#include "entities.h" -#include "options.h" -#include "properties.h" +#include "tools/qflight/include/entities.h" +#include "tools/qflight/include/options.h" +#include "tools/qflight/include/properties.h" const char *this_program; diff --git a/tools/qflight/source/properties.c b/tools/qflight/source/properties.c index d9286e268..87229d05d 100644 --- a/tools/qflight/source/properties.c +++ b/tools/qflight/source/properties.c @@ -41,15 +41,15 @@ #include #include "QF/mathlib.h" -#include "QF/qfplist.h" +#include "QF/plist.h" #include "QF/quakeio.h" #include "compat.h" -#include "entities.h" -#include "light.h" -#include "options.h" -#include "properties.h" +#include "tools/qflight/include/entities.h" +#include "tools/qflight/include/light.h" +#include "tools/qflight/include/options.h" +#include "tools/qflight/include/properties.h" static plitem_t *properties; @@ -237,14 +237,11 @@ set_properties (entity_t *ent, plitem_t *dict) const char *str; if (properties) { - prop = PL_ObjectForKey (properties, ent->classname); if ((p = get_item ("light_name", dict, 0)) && (str = PL_String (p))) prop = PL_ObjectForKey (properties, str); if (!prop) prop = PL_ObjectForKey (properties, ent->classname); - - prop = PL_ObjectForKey (properties, ent->classname); if (!prop) prop = PL_ObjectForKey (properties, "default"); } @@ -337,6 +334,6 @@ LoadProperties (const char *filename) Qread (f, buf, len); Qclose (f); buf[len] = 0; - properties = PL_GetPropertyList (buf); + properties = PL_GetPropertyList (buf, 0); free (buf); } diff --git a/tools/qflight/source/qflight.c b/tools/qflight/source/qflight.c index 62eda9af1..677dd439c 100644 --- a/tools/qflight/source/qflight.c +++ b/tools/qflight/source/qflight.c @@ -53,11 +53,11 @@ #include "QF/sys.h" #include "QF/va.h" -#include "light.h" -#include "threads.h" -#include "entities.h" -#include "options.h" -#include "properties.h" +#include "tools/qflight/include/light.h" +#include "tools/qflight/include/threads.h" +#include "tools/qflight/include/entities.h" +#include "tools/qflight/include/options.h" +#include "tools/qflight/include/properties.h" options_t options; bsp_t *bsp; @@ -71,15 +71,12 @@ dstring_t *lightdata; dstring_t *rgblightdata; dmodel_t *bspmodel; -int bspfileface; // next surface to dispatch +size_t bspfileface; // next surface to dispatch int bspfileent; // next entity to dispatch vec3_t bsp_origin; -qboolean extrasamples; - -float minlights[MAX_MAP_FACES]; - +bool extrasamples; int GetFileSpace (int size) @@ -122,13 +119,13 @@ VisThread (void *junk) static void * LightThread (void *l) { - int i; + size_t i; while (1) { LOCK; i = bspfileface++; if (i < bsp->numfaces) { - printf ("%5d / %d\r", i, bsp->numfaces); + printf ("%5zd / %zd\r", i, bsp->numfaces); fflush (stdout); } UNLOCK; @@ -142,22 +139,21 @@ LightThread (void *l) static void FindFaceOffsets (void) { - int i, j; entity_t *ent; vec3_t org; const char *name; surfaceorgs = (vec3_t *) calloc (bsp->numfaces, sizeof (vec3_t)); - for (i = 1; i < bsp->nummodels; i++) { - ent = FindEntityWithKeyPair ("model", name = va ("*%d", i)); + for (size_t i = 1; i < bsp->nummodels; i++) { + ent = FindEntityWithKeyPair ("model", name = va (0, "*%zd", i)); VectorZero (org); if (!ent) Sys_Error ("FindFaceOffsets: Couldn't find entity for model %s.\n", name); if (!strncmp (ValueForKey (ent, "classname"), "rotate_", 7)) GetVectorForKey (ent, "origin", org); - for (j = 0; j < bsp->models[i].numfaces; j++) + for (uint32_t j = 0; j < bsp->models[i].numfaces; j++) VectorCopy (org, surfaceorgs[bsp->models[i].firstface]); } } diff --git a/tools/qflight/source/threads.c b/tools/qflight/source/threads.c index 50e2709a0..8438f7b8e 100644 --- a/tools/qflight/source/threads.c +++ b/tools/qflight/source/threads.c @@ -46,9 +46,9 @@ #include "QF/qtypes.h" #include "QF/qendian.h" -#include "light.h" -#include "options.h" -#include "threads.h" +#include "tools/qflight/include/light.h" +#include "tools/qflight/include/options.h" +#include "tools/qflight/include/threads.h" #if defined (HAVE_PTHREAD_H) && defined (HAVE_PTHREAD) pthread_mutex_t *my_mutex; diff --git a/tools/qflight/source/trace.c b/tools/qflight/source/trace.c index 0f9df661e..9b77e2849 100644 --- a/tools/qflight/source/trace.c +++ b/tools/qflight/source/trace.c @@ -50,8 +50,8 @@ #include "QF/quakefs.h" #include "QF/sys.h" -#include "light.h" -#include "options.h" +#include "tools/qflight/include/light.h" +#include "tools/qflight/include/options.h" typedef struct { int type; @@ -142,9 +142,9 @@ MakeTnodes (dmodel_t *bm) #define TESTLINESTATE_EMPTY 1 #define TESTLINESTATE_SOLID 2 -static qboolean +static bool TestLineOrSky (lightinfo_t *l, const vec3_t start, const vec3_t end, - qboolean sky_test) + bool sky_test) { vec_t front, back; vec3_t frontpt, backpt; @@ -223,13 +223,13 @@ TestLineOrSky (lightinfo_t *l, const vec3_t start, const vec3_t end, } } -qboolean +bool TestLine (lightinfo_t *l, const vec3_t start, const vec3_t stop) { return TestLineOrSky (l, start, stop, false); } -qboolean +bool TestSky (lightinfo_t *l, const vec3_t start, const vec3_t dir) { vec3_t stop; diff --git a/tools/qflight/source/vis.c b/tools/qflight/source/vis.c index 44ed4ecca..42eec6631 100644 --- a/tools/qflight/source/vis.c +++ b/tools/qflight/source/vis.c @@ -47,10 +47,10 @@ #include "QF/bspfile.h" #include "QF/mathlib.h" -#include "entities.h" -#include "light.h" -#include "options.h" -#include "threads.h" +#include "tools/qflight/include/entities.h" +#include "tools/qflight/include/light.h" +#include "tools/qflight/include/options.h" +#include "tools/qflight/include/threads.h" static struct { int empty; @@ -73,7 +73,7 @@ int num_alllights; entity_t **novislights; int num_novislights; -static dleaf_t * +static __attribute__((pure)) dleaf_t * Light_PointInLeaf (vec3_t point) { int num = 0, side; @@ -145,9 +145,6 @@ VisEntity (int ent_index) entity_t *entity = entities + ent_index; dleaf_t *leaf; int ignorevis = false; - int i; - unsigned j; - uint32_t *mark; byte *vis, *surfacehit; int vis_size; @@ -193,19 +190,19 @@ VisEntity (int ent_index) memset (surfacehit, 0, (bsp->numfaces + 7) / 8); DecompressVis (bsp->visdata + leaf->visofs, vis, vis_size); - for (i = 0, leaf = bsp->leafs + 1; i < bsp->models[0].visleafs; - i++, leaf++) { + for (uint32_t i = 0; i < bsp->models[0].visleafs; i++) { + leaf = bsp->leafs + 1 + i; if (!leaf->nummarksurfaces) continue; if (vis[i >> 3] & (1 << (i & 7))) { - for (j = 0, mark = bsp->marksurfaces + leaf->firstmarksurface; - j < leaf->nummarksurfaces; j++, mark++) { - mark_face (*mark, surfacehit, entity); + uint32_t *mark = bsp->marksurfaces + leaf->firstmarksurface; + for (size_t j = 0; j < leaf->nummarksurfaces; j++) { + mark_face (*mark++, surfacehit, entity); } } } - for (i = 1; i < bsp->nummodels; i++) { - for (j = 0; (int) j < bsp->models[i].numfaces; j++) { + for (size_t i = 1; i < bsp->nummodels; i++) { + for (uint32_t j = 0; j < bsp->models[i].numfaces; j++) { //FIXME vis mark_face (bsp->models[i].firstface + j, surfacehit, entity); } @@ -216,8 +213,6 @@ VisEntity (int ent_index) void VisStats (void) { - int i, count; - printf ("%4i lights\n", counts.lights); printf ("%4i air\n", counts.empty); printf ("%4i solid\n", counts.solid); @@ -227,10 +222,11 @@ VisStats (void) printf ("%4i sky\n", counts.sky); printf ("%4i unknown\n", counts.misc); - for (i = count = 0; i < bsp->numfaces; i++) + size_t count = 0; + for (size_t i = 0; i < bsp->numfaces; i++) if (surfacelightchain[i]) count++; - printf ("%i faces, %i (%i%%) may receive light\n", bsp->numfaces, + printf ("%zd faces, %zd (%zd%%) may receive light\n", bsp->numfaces, count, count * 100 / bsp->numfaces); if (counts.solid || counts.sky) printf ("warning: %i lights of %i lights (%i%%) were found in sky\n" @@ -238,6 +234,6 @@ VisStats (void) "out of the solid or sky to accelerate compiling\n", counts.solid + counts.sky, counts.lights, (counts.solid + counts.sky) * 100 / counts.lights); - printf ("%i lights will be cast onto %i surfaces, %i casts will " + printf ("%i lights will be cast onto %zd surfaces, %i casts will " "be performed\n", counts.lights, bsp->numfaces, counts.cast); } diff --git a/tools/qflmp/Makefile.am b/tools/qflmp/Makefile.am deleted file mode 100644 index b9436172c..000000000 --- a/tools/qflmp/Makefile.am +++ /dev/null @@ -1,18 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -QFLMP_LIBS=@QFLMP_LIBS@ -QFLMP_DEPS=@QFLMP_DEPS@ -QFLMP_INCS=@QFLMP_INCS@ - -AM_CPPFLAGS= -I$(top_srcdir)/include $(QFLMP_INCS) - -bin_PROGRAMS= qflmp - -#mans=qflmp.1 -man_MANS= $(mans) - -qflmp_SOURCES= lmp.c -qflmp_LDADD= $(QFLMP_LIBS) -qflmp_DEPENDENCIES= $(QFLMP_DEPS) - -EXTRA_DIST= lmp.h #lmp.1 diff --git a/tools/qflmp/Makemodule.am b/tools/qflmp/Makemodule.am new file mode 100644 index 000000000..f1a07221a --- /dev/null +++ b/tools/qflmp/Makemodule.am @@ -0,0 +1,15 @@ +QFLMP_LIBS=@QFLMP_LIBS@ +QFLMP_DEPS=@QFLMP_DEPS@ +QFLMP_INCS=@QFLMP_INCS@ + +EXTRA_PROGRAMS += qflmp +bin_PROGRAMS += @QFLMP_TARGETS@ + +#mans=qflmp.1 +man_MANS += $(mans) + +qflmp_SOURCES= tools/qflmp/lmp.c +qflmp_LDADD= $(QFLMP_LIBS) +qflmp_DEPENDENCIES= $(QFLMP_DEPS) + +EXTRA_DIST += tools/qflmp/lmp.h #lmp.1 diff --git a/tools/qflmp/lmp.c b/tools/qflmp/lmp.c index b8021166c..88001cb2f 100644 --- a/tools/qflmp/lmp.c +++ b/tools/qflmp/lmp.c @@ -124,7 +124,7 @@ usage (int status) } -static qboolean +static bool exportFile (const char *inpath) { char *outpath = replaceExtension (inpath, "pcx"); @@ -136,7 +136,7 @@ exportFile (const char *inpath) int32_t width; int32_t height; void *data; - qboolean ret = false; + bool ret = false; if (options.verbosity > 1) Sys_Printf ("file size: %d\n", fsize); @@ -200,7 +200,7 @@ die: } -static qboolean +static bool importFile (const char *inpath) { char *outpath = replaceExtension (inpath, "lmp"); @@ -208,12 +208,12 @@ importFile (const char *inpath) QFile *outfile = Qopen (outpath, "wb"); int fsize = Qfilesize (infile); tex_t *lmp; - qboolean ret = false; + bool ret = false; if (options.verbosity > 1) Sys_Printf ("PCX file size: %d\n", fsize); - lmp = LoadPCX (infile, false, NULL); + lmp = LoadPCX (infile, false, NULL, 1); if (!lmp) { Sys_Printf ("%s: Failed to load %s as texture.\n", @@ -334,7 +334,7 @@ main (int argc, char **argv) this_program = argv[0]; Sys_Init (); - Memory_Init (malloc (MEMSIZE), MEMSIZE); + Memory_Init (Sys_Alloc (MEMSIZE), MEMSIZE); decode_args (argc, argv); diff --git a/tools/qflmp/lmp.h b/tools/qflmp/lmp.h index 4428da4c5..7098370b6 100644 --- a/tools/qflmp/lmp.h +++ b/tools/qflmp/lmp.h @@ -44,7 +44,7 @@ typedef struct { lmpmode_t mode; // see above int verbosity; // 0=silent char *palette; // palette file to read - qboolean raw; // don't read / write header + bool raw; // don't read / write header short width; // width of images short height; // height " " } options_t; diff --git a/tools/qfmodelgen/Makefile.am b/tools/qfmodelgen/Makefile.am deleted file mode 100644 index e5b93df89..000000000 --- a/tools/qfmodelgen/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -SUBDIRS= include source - -#man_MANS= qfmodelgen.1 - -#EXTRA_DIST= $(man_MANS) diff --git a/tools/qfmodelgen/Makemodule.am b/tools/qfmodelgen/Makemodule.am new file mode 100644 index 000000000..d397b4f91 --- /dev/null +++ b/tools/qfmodelgen/Makemodule.am @@ -0,0 +1,2 @@ +include tools/qfmodelgen/include/Makemodule.am +include tools/qfmodelgen/source/Makemodule.am diff --git a/tools/qfmodelgen/include/Makefile.am b/tools/qfmodelgen/include/Makefile.am deleted file mode 100644 index c4cc81268..000000000 --- a/tools/qfmodelgen/include/Makefile.am +++ /dev/null @@ -1,3 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -EXTRA_DIST= lbmlib.h trilib.h diff --git a/tools/qfmodelgen/include/Makemodule.am b/tools/qfmodelgen/include/Makemodule.am new file mode 100644 index 000000000..0a5f0306d --- /dev/null +++ b/tools/qfmodelgen/include/Makemodule.am @@ -0,0 +1,3 @@ +EXTRA_DIST += \ + tools/qfmodelgen/include/lbmlib.h \ + tools/qfmodelgen/include/trilib.h diff --git a/tools/qfmodelgen/source/Makefile.am b/tools/qfmodelgen/source/Makefile.am deleted file mode 100644 index 4bc3a0bda..000000000 --- a/tools/qfmodelgen/source/Makefile.am +++ /dev/null @@ -1,14 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -QFMODELGEN_LIBS=@QFMODELGEN_LIBS@ -QFMODELGEN_DEPS=@QFMODELGEN_DEPS@ -QFMODELGEN_INCS=@QFMODELGEN_INCS@ - -AM_CPPFLAGS= -I$(top_srcdir)/include $(QFMODELGEN_INCS) - -bin_PROGRAMS= qfmodelgen - -qfmodelgen_SOURCES= lbmlib.c modelgen.c trilib.c - -qfmodelgen_LDADD= $(QFMODELGEN_LIBS) -qfmodelgen_DEPENDENCIES= $(QFMODELGEN_DEPS) diff --git a/tools/qfmodelgen/source/Makemodule.am b/tools/qfmodelgen/source/Makemodule.am new file mode 100644 index 000000000..86d40702e --- /dev/null +++ b/tools/qfmodelgen/source/Makemodule.am @@ -0,0 +1,14 @@ +QFMODELGEN_LIBS=@QFMODELGEN_LIBS@ +QFMODELGEN_DEPS=@QFMODELGEN_DEPS@ +QFMODELGEN_INCS=@QFMODELGEN_INCS@ + +EXTRA_PROGRAMS += qfmodelgen +bin_PROGRAMS += @QFMODELGEN_TARGETS@ + +qfmodelgen_SOURCES = \ + tools/qfmodelgen/source/lbmlib.c \ + tools/qfmodelgen/source/modelgen.c \ + tools/qfmodelgen/source/trilib.c + +qfmodelgen_LDADD= $(QFMODELGEN_LIBS) +qfmodelgen_DEPENDENCIES= $(QFMODELGEN_DEPS) diff --git a/tools/qfmodelgen/source/lbmlib.c b/tools/qfmodelgen/source/lbmlib.c index 77af86e92..1650bda86 100644 --- a/tools/qfmodelgen/source/lbmlib.c +++ b/tools/qfmodelgen/source/lbmlib.c @@ -30,7 +30,7 @@ #include "QF/quakeio.h" #include "QF/sys.h" -#include "lbmlib.h" +#include "tools/qfmodelgen/include/lbmlib.h" static int LoadFile (const char *fname, byte **buf) diff --git a/tools/qfmodelgen/source/modelgen.c b/tools/qfmodelgen/source/modelgen.c index 4905ad041..b48934576 100644 --- a/tools/qfmodelgen/source/modelgen.c +++ b/tools/qfmodelgen/source/modelgen.c @@ -45,10 +45,11 @@ #include "QF/script.h" #include "QF/sys.h" -#include "lbmlib.h" -#include "trilib.h" #include "compat.h" +#include "tools/qfmodelgen/include/lbmlib.h" +#include "tools/qfmodelgen/include/trilib.h" + #define MAXVERTS 2048 #define MAXFRAMES 256 #define MAXSKINS 100 @@ -108,7 +109,7 @@ char cdpartial[256]; char cddir[256]; int framecount, skincount; -qboolean cdset; +bool cdset; int degeneratetris; int firstframe = 1; float totsize, averagesize; @@ -177,15 +178,17 @@ SetQdirFromPath (char *path) static const char * ExpandPath (const char *path) { - static char full[1024]; + static dstring_t *full; - //FIXME buffer overflow central + if (!full) { + full = dstring_new(); + } //if (!qdir) // Sys_Error ("ExpandPath called without qdir set"); if (path[0] == '/' || path[0] == '\\' || path[1] == ':') return path; - sprintf (full, "%s%s", qdir, path); - return full; + dsprintf (full, "%s%s", qdir, path); + return full->str; } static void diff --git a/tools/qfmodelgen/source/trilib.c b/tools/qfmodelgen/source/trilib.c index 41f6ffd42..da3ad6ea3 100644 --- a/tools/qfmodelgen/source/trilib.c +++ b/tools/qfmodelgen/source/trilib.c @@ -31,7 +31,7 @@ #include "QF/quakeio.h" #include "QF/sys.h" -#include "trilib.h" +#include "tools/qfmodelgen/include/trilib.h" // on disk representation of a face @@ -74,16 +74,17 @@ LoadTriangleList (char *filename, triangle_t **pptri, int *numtriangles) { QFile *input; char name[256], tex[256]; - float start, exitpattern, t; + float start, exitpattern; int count, iLevel, magic, i; tf_triangle tri; triangle_t *ptri; - t = -FLOAT_START; - *((unsigned char *) &exitpattern + 0) = *((unsigned char *) &t + 3); - *((unsigned char *) &exitpattern + 1) = *((unsigned char *) &t + 2); - *((unsigned char *) &exitpattern + 2) = *((unsigned char *) &t + 1); - *((unsigned char *) &exitpattern + 3) = *((unsigned char *) &t + 0); + // FIXME not sure if this should be BigFloat or FloatSwap + exitpattern = BigFloat (FLOAT_END); + //*((unsigned char *) &exitpattern + 0) = *((unsigned char *) &t + 3); + //*((unsigned char *) &exitpattern + 1) = *((unsigned char *) &t + 2); + //*((unsigned char *) &exitpattern + 2) = *((unsigned char *) &t + 1); + //*((unsigned char *) &exitpattern + 3) = *((unsigned char *) &t + 0); if ((input = Qopen(filename, "rb")) == 0) { fprintf (stderr,"reader: could not open file '%s'\n", filename); diff --git a/tools/qfspritegen/Makefile.am b/tools/qfspritegen/Makemodule.am similarity index 57% rename from tools/qfspritegen/Makefile.am rename to tools/qfspritegen/Makemodule.am index 05f321022..ae36299e5 100644 --- a/tools/qfspritegen/Makefile.am +++ b/tools/qfspritegen/Makemodule.am @@ -1,14 +1,11 @@ -AUTOMAKE_OPTIONS= foreign - QFSPRITEGEN_LIBS=@QFSPRITEGEN_LIBS@ QFSPRITEGEN_DEPS=@QFSPRITEGEN_DEPS@ QFSPRITEGEN_INCS=@QFSPRITEGEN_INCS@ -AM_CPPFLAGS= -I$(top_srcdir)/include $(QFSPRITEGEN_INCS) +EXTRA_PROGRAMS += qfspritegen +bin_PROGRAMS += @QFSPRITEGEN_TARGETS@ -bin_PROGRAMS= qfspritegen - -qfspritegen_SOURCES= spritegen.c +qfspritegen_SOURCES= tools/qfspritegen/spritegen.c qfspritegen_LDADD= $(QFSPRITEGEN_LIBS) qfspritegen_DEPENDENCIES= $(QFSPRITEGEN_DEPS) diff --git a/tools/qfspritegen/spritegen.c b/tools/qfspritegen/spritegen.c index b209650d4..e1af9e487 100644 --- a/tools/qfspritegen/spritegen.c +++ b/tools/qfspritegen/spritegen.c @@ -22,11 +22,16 @@ // Result is stored in /raid/quake/id1/sprites/.spr. // +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + #include #include #include #include +#include "QF/dstring.h" #include "QF/image.h" #include "QF/pcx.h" #include "QF/qendian.h" @@ -42,8 +47,8 @@ tex_t *image; dsprite_t sprite; byte *lumpbuffer, *plump; -char spritedir[1024]; -char spriteoutname[1024]; +dstring_t *spritedir; +dstring_t *spriteoutname; int framesmaxs[2]; int framecount; @@ -189,7 +194,7 @@ LoadScreen (const char *name) file = Qopen (name, "rb"); if (!file) Sys_Error ("could not open"); - image = LoadPCX (file, false, 0); + image = LoadPCX (file, false, 0, 1); } @@ -444,7 +449,7 @@ Cmd_Spritename (void) FinishSprite (); Script_GetToken (&scr, false); - sprintf (spriteoutname, "%s%s.spr", spritedir, Script_Token (&scr)); + dsprintf (spriteoutname, "%s%s.spr", spritedir->str, Script_Token (&scr)); memset (&sprite, 0, sizeof(sprite)); framecount = 0; @@ -472,14 +477,14 @@ FinishSprite (void) if (sprite.numframes == 0) Sys_Error ("no frames\n"); - if (!strlen(spriteoutname)) + if (!spriteoutname->str) Sys_Error ("Didn't name sprite file"); if ((plump - lumpbuffer) > MAX_BUFFER_SIZE) Sys_Error ("Sprite package too big; increase MAX_BUFFER_SIZE"); - spriteouthandle = Qopen (spriteoutname, "wb"); - printf ("saving in %s\n", spriteoutname); + spriteouthandle = Qopen (spriteoutname->str, "wb"); + printf ("saving in %s\n", spriteoutname->str); WriteSprite (spriteouthandle); Qclose (spriteouthandle); @@ -487,7 +492,7 @@ FinishSprite (void) printf ("%d frame(s)\n", sprite.numframes); printf ("%d ungrouped frame(s), including group headers\n", framecount); - spriteoutname[0] = 0; // clear for a new sprite + dstring_clearstr (spriteoutname); // clear for a new sprite } /* @@ -505,6 +510,9 @@ int main (int argc, char **argv) if (argc != 2) Sys_Error ("usage: spritegen file.qc"); + spritedir = dstring_newstr (); + spriteoutname = dstring_newstr (); + i = 1; //SetQdirFromPath (argv[i]); diff --git a/tools/qfvis/Makefile.am b/tools/qfvis/Makefile.am deleted file mode 100644 index 52a798c6e..000000000 --- a/tools/qfvis/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -SUBDIRS= include source - -mans=qfvis.1 -man_MANS= $(mans) -EXTRA_DIST= $(mans) diff --git a/tools/qfvis/Makemodule.am b/tools/qfvis/Makemodule.am new file mode 100644 index 000000000..5840ea1f6 --- /dev/null +++ b/tools/qfvis/Makemodule.am @@ -0,0 +1,4 @@ +include tools/qfvis/include/Makemodule.am +include tools/qfvis/source/Makemodule.am + +man_MANS += tools/qfvis/qfvis.1 diff --git a/tools/qfvis/include/Makefile.am b/tools/qfvis/include/Makefile.am deleted file mode 100644 index 0bbb6480b..000000000 --- a/tools/qfvis/include/Makefile.am +++ /dev/null @@ -1,3 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -EXTRA_DIST= options.h vis.h diff --git a/tools/qfvis/include/Makemodule.am b/tools/qfvis/include/Makemodule.am new file mode 100644 index 000000000..eec0af5b0 --- /dev/null +++ b/tools/qfvis/include/Makemodule.am @@ -0,0 +1,3 @@ +EXTRA_DIST += \ + tools/qfvis/include/options.h \ + tools/qfvis/include/vis.h diff --git a/tools/qfvis/include/options.h b/tools/qfvis/include/options.h index 0af22f520..a75d0da0e 100644 --- a/tools/qfvis/include/options.h +++ b/tools/qfvis/include/options.h @@ -33,14 +33,18 @@ typedef struct { int verbosity; // 0=silent int threads; - qboolean minimal; + bool minimal; + bool no_auto_pvs; + bool fat_pvs; + bool utf8; int level; + size_t portal_limit; struct dstring_s *bspfile; } options_t; extern options_t options; int DecodeArgs (int argc, char **argv); -void usage (int status); +void usage (int status) __attribute__((noreturn)); extern const char *this_program; #endif//__options_h diff --git a/tools/qfvis/include/vis.h b/tools/qfvis/include/vis.h index a0863d4be..9106dc67c 100644 --- a/tools/qfvis/include/vis.h +++ b/tools/qfvis/include/vis.h @@ -41,6 +41,7 @@ #include extern pthread_rwlock_t *global_lock; extern pthread_rwlock_t *portal_locks; +extern pthread_rwlock_t *stats_lock; #define WRLOCK(l) \ do { \ @@ -65,24 +66,31 @@ extern pthread_rwlock_t *portal_locks; #define UNLOCK_PORTAL(p) UNLOCK (&portal_locks[p - portals]) #else -#define LOCK -#define UNLOCK +#define LOCK(l) +#define UNLOCK(l) +#define WRLOCK_PORTAL(p) +#define RDLOCK_PORTAL(p) +#define UNLOCK_PORTAL(p) #endif +#include "QF/cmem.h" +#include "QF/dstring.h" #include "QF/set.h" +#include "QF/zone.h" +#include "QF/simd/vec4f.h" -#define MAX_PORTALS 32768 #define PORTALFILE "PRT1" #define PORTALFILE_AM "PRT1-AM" #define PORTALFILE2 "PRT2" #define ON_EPSILON 0.1 -#define MAX_POINTS_ON_WINDING 64 -#define MAX_PORTALS_ON_CLUSTER 128 -typedef struct { - qboolean original; // don't free, it's part of the portal - int numpoints; - vec3_t points[8]; // variable sized +typedef struct winding_s { + struct winding_s *next; + bool original; // don't free, it's part of the portal + unsigned numpoints; + int id; + int thread; + vec4f_t points[1]; // variable sized } winding_t; typedef enum { @@ -93,9 +101,9 @@ typedef enum { } vstatus_t; typedef struct { - plane_t plane; // normal pointing into neighbor + vec4f_t plane; // normal pointing into neighbor + vspheref_t sphere; // bounding sphere int cluster; // neighbor - sphere_t sphere; // bounding sphere winding_t *winding; vstatus_t status; set_t *visbits; @@ -105,8 +113,8 @@ typedef struct { } portal_t; typedef struct seperating_plane_s { + vec4f_t plane; // from portal is on positive side struct seperating_plane_s *next; - plane_t plane; // from portal is on positive side } sep_t; typedef struct passage_s { @@ -117,9 +125,10 @@ typedef struct passage_s { typedef struct cluster_s { int numportals; - passage_t *passages; - portal_t *portals[MAX_PORTALS_ON_CLUSTER]; int visofs; + vspheref_t sphere; + passage_t *passages; + portal_t *portals; } cluster_t; typedef struct pstack_s { @@ -127,26 +136,33 @@ typedef struct pstack_s { cluster_t *cluster; ///< the cluster being sub-vised winding_t *source_winding; ///< clipped source portal winding portal_t *pass_portal; ///< the portal exiting from the cluster + vec4f_t pass_plane; ///< plane of the pass portal winding_t *pass_winding; ///< clipped pass portal winding - plane_t pass_plane; ///< plane of the pass portal set_t *mightsee; sep_t *separators[2]; } pstack_t; typedef struct { - int portaltest; ///< number of portals tested via separators - int portalpass; ///< number of portals through which vis passes - int portalcheck; ///< number of portal checks - int targettested; ///< number of times target portal tested - int targettrimmed; ///< number of times target portal trimmed - int targetclipped; ///< number of times target portal clipped away - int sourcetested; ///< number of times source portal tested - int sourcetrimmed; ///< number of times source portal trimmed - int sourceclipped; ///< number of times source portal clipped away - int chains; ///< number of visits to clusters - int mighttest; ///< amount mightsee is used for masked tests - int vistest; ///< amount visbits is used for masked tests - int mightseeupdate; ///< amount of updates to waiting portals + unsigned long portaltest; ///< number of portals tested via separators + unsigned long portalpass; ///< number of portals through which vis passes + unsigned long portalcheck; ///< number of portal checks + unsigned long targettested; ///< number of times target portal tested + unsigned long targettrimmed;///< number of times target portal trimmed + unsigned long targetclipped;///< number of times target portal clipped away + unsigned long sourcetested; ///< number of times source portal tested + unsigned long sourcetrimmed;///< number of times source portal trimmed + unsigned long sourceclipped;///< number of times source portal clipped away + unsigned long chains; ///< number of visits to clusters + unsigned long mighttest; ///< amount mightsee is used for masked tests + unsigned long vistest; ///< amount visbits is used for masked tests + unsigned long mightseeupdate;///< amount of updates to waiting portals + unsigned sep_alloc; ///< how many separators were allocated + unsigned sep_free; ///< how many separators were freed + unsigned sep_highwater; ///< most separators in flight + unsigned sep_maxbulk; ///< most separators freed at once + size_t winding_mark; ///< most memory allocated to windings + unsigned stack_alloc; ///< how many stack blocks were allocated + unsigned stack_free; ///< how many stack blocks were freed } visstat_t; typedef struct threaddata_s { @@ -155,17 +171,32 @@ typedef struct threaddata_s { portal_t *base; ///< portal for which this thread is being run pstack_t pstack_head; sep_t *sep_freelist; ///< per-thread list of free separators + memsuper_t *memsuper; ///< per-thread memory pool + memhunk_t *hunk; + dstring_t *str; set_pool_t set_pool; + int id; + int winding_id; } threaddata_t; typedef struct { set_t *portalsee; - int clustersee; + unsigned long selfcull; ///< number of protals culled by self + unsigned long clustercull; ///< number of portals culled by cluster sphere + unsigned long clustertest; ///< number of portals tested by cluster + unsigned long spheretest; ///< number of portal sphere tests done + unsigned long spherecull; ///< number of portals culled by sphere tests + unsigned long spherepass; ///< number of portals passed by sphere tests + unsigned long windingtest; ///< number of portal pairs tested by winding + unsigned long windingcull; ///< number of portals culled by winding tests + unsigned long windingpass; ///< number of portals passed by winding tests + unsigned long clustersee; + int id; } basethread_t; -extern int numportals; -extern int portalclusters; -extern int numrealleafs; +extern unsigned numportals; +extern unsigned portalclusters; +extern unsigned numrealleafs; extern int bitbytes; extern int bitbytes_l; extern int bitlongs; @@ -173,17 +204,31 @@ extern struct bsp_s *bsp; extern portal_t *portals; extern cluster_t *clusters; -extern int *leafcluster; +extern uint32_t *leafcluster; extern byte *uncompressed; -void FreeWinding (winding_t *w); -winding_t *NewWinding (int points); -winding_t *ClipWinding (winding_t *in, const plane_t *split, qboolean keepon); -winding_t *CopyWinding (const winding_t *w); +winding_t *NewWinding (threaddata_t *thread, int points); +winding_t *ClipWinding (threaddata_t *thread, winding_t *in, vec4f_t split, + bool keepon); +winding_t *CopyWinding (threaddata_t *thread, const winding_t *w); void ClusterFlow (int clusternum); void PortalBase (basethread_t *thread, portal_t *portal); void PortalFlow (threaddata_t *data, portal_t *portal); void CalcAmbientSounds (void); +struct sizebuf_s; +int CompressRow (struct sizebuf_s *dest, const byte *vis, unsigned num_leafs, + int utf8); + +void CalcFatPVS (void); + +void RunThreads (const char *heading, void *(*thread_func) (void *), + int (*calc_progress)(void)); + +extern const char spinner[]; +extern const char progress[]; +extern int *working; +extern int progress_tick; + #endif// __vis_h diff --git a/tools/qfvis/source/Makefile.am b/tools/qfvis/source/Makefile.am deleted file mode 100644 index e0a725ca0..000000000 --- a/tools/qfvis/source/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -QFVIS_LIBS=@QFVIS_LIBS@ -QFVIS_DEPS=@QFVIS_DEPS@ -QFVIS_INCS=@QFVIS_INCS@ -PTHREAD_LDFLAGS=@PTHREAD_LDFLAGS@ -PTHREAD_CFLAGS=@PTHREAD_CFLAGS@ - -AM_CPPFLAGS= -I$(top_srcdir)/include $(QFVIS_INCS) $(PTHREAD_CFLAGS) - -bin_PROGRAMS= qfvis - -qfvis_SOURCES= base-vis.c flow.c options.c qfvis.c soundphs.c - -qfvis_LDFLAGS= $(PTHREAD_LDFLAGS) -qfvis_LDADD= $(QFVIS_LIBS) -qfvis_DEPENDENCIES= $(QFVIS_DEPS) diff --git a/tools/qfvis/source/Makemodule.am b/tools/qfvis/source/Makemodule.am new file mode 100644 index 000000000..7efb7bbe2 --- /dev/null +++ b/tools/qfvis/source/Makemodule.am @@ -0,0 +1,18 @@ +QFVIS_LIBS=@QFVIS_LIBS@ +QFVIS_DEPS=@QFVIS_DEPS@ +QFVIS_INCS=@QFVIS_INCS@ + +EXTRA_PROGRAMS += qfvis +bin_PROGRAMS += @QFVIS_TARGETS@ + +qfvis_SOURCES = \ + tools/qfvis/source/base-vis.c \ + tools/qfvis/source/fatpvs.c \ + tools/qfvis/source/flow.c \ + tools/qfvis/source/options.c \ + tools/qfvis/source/qfvis.c \ + tools/qfvis/source/soundphs.c + +qfvis_LDFLAGS= $(PTHREAD_LDFLAGS) +qfvis_LDADD= $(QFVIS_LIBS) +qfvis_DEPENDENCIES= $(QFVIS_DEPS) diff --git a/tools/qfvis/source/base-vis.c b/tools/qfvis/source/base-vis.c index ca4d912e0..2242409dd 100644 --- a/tools/qfvis/source/base-vis.c +++ b/tools/qfvis/source/base-vis.c @@ -54,8 +54,8 @@ #include "QF/quakefs.h" #include "QF/sys.h" -#include "vis.h" -#include "options.h" +#include "tools/qfvis/include/vis.h" +#include "tools/qfvis/include/options.h" /* This is a rough first-order aproximation that is used to trivially reject @@ -76,7 +76,7 @@ SimpleFlood (basethread_t *thread, portal_t *srcportal, int clusternum) cluster = &clusters[clusternum]; for (i = 0; i < cluster->numportals; i++) { - portal = cluster->portals[i]; + portal = &cluster->portals[i]; if (!set_is_member (thread->portalsee, portal - portals)) continue; SimpleFlood (thread, srcportal, portal->cluster); @@ -84,82 +84,168 @@ SimpleFlood (basethread_t *thread, portal_t *srcportal, int clusternum) } static inline int -test_sphere (sphere_t *sphere, plane_t *plane) +test_sphere (const vspheref_t *sphere, vec4f_t plane) { - float d; - int front, back; +#ifdef __SSE3__ + const vec4f_t zero = {}; + float r = sphere->radius; + vec4f_t eps = { r, r, r, r }; + vec4f_t d = _mm_addsub_ps (zero, dotf (sphere->center, plane)); + vec4i_t c = (d - eps) >= 0; - d = DotProduct (sphere->center, plane->normal) - plane->dist; - front = (d >= sphere->radius); - back = (d <= -sphere->radius); + c = (vec4i_t) _mm_hsub_epi32 ((__m128i) c, (__m128i) c); + return c[0]; +#else + float d = DotProduct (sphere->center, plane) + plane[3]; + int front = (d >= sphere->radius); + int back = (d <= -sphere->radius); return front - back; +#endif +} + +static int __attribute__ ((pure)) +test_winding_front (const winding_t *winding, vec4f_t plane) +{ + for (unsigned i = 0; i < winding->numpoints; i++) { + vec4f_t d = dotf (winding->points[i], plane); + if (d[0] > ON_EPSILON) + return 1; + } + return 0; +} + +static int __attribute__ ((pure)) +test_winding_back (const winding_t *winding, vec4f_t plane) +{ + for (unsigned i = 0; i < winding->numpoints; i++) { + vec4f_t d = dotf (winding->points[i], plane); + if (d[0] < -ON_EPSILON) + return 1; + } + return 0; } void PortalBase (basethread_t *thread, portal_t *portal) { - int i, j, k; - float d; - portal_t *tp; - winding_t *winding; + portal_t *tp; + cluster_t *cluster; int tp_side, portal_side; - i = portal - portals; + for (cluster = clusters; cluster < clusters + portalclusters; cluster++) { + int side = test_sphere (&cluster->sphere, portal->plane); - for (j = 0, tp = portals; j < numportals * 2; j++, tp++) { - if (j == i) - continue; - - // If the target portal is behind the portals's plane, then it - // can't possibly be seen by the portal. - // - // If the portal is in front of the target's plane, then the target - // is of no interest as it is facing counter to the flow of - // visibility. - - // First check using the bounding spheres of the two portals. - tp_side = test_sphere (&tp->sphere, &portal->plane); - if (tp_side < 0) { - // The test portal definitely is entirely behind the portal's - // plane. - continue; // entirely behind - } - portal_side = test_sphere (&portal->sphere, &tp->plane); - if (portal_side > 0) { - // The portal definitely is entirely in front of the test - // portal's plane. - continue; // entirely in front - } - - if (tp_side == 0) { - // The test portal's sphere touches the portal's plane, so - // do a more refined check. - winding = tp->winding; - for (k = 0; k < winding->numpoints; k++) { - d = DotProduct (winding->points[k], - portal->plane.normal) - portal->plane.dist; - if (d > ON_EPSILON) - break; + if (side < 0) { + thread->clustertest += cluster->numportals; + // The cluster is entirely behind the portal's plane, thus every + // portal in the cluster is also behind the portal's plane and + // cannot be seen at all. + thread->clustercull += cluster->numportals; + } else if (side > 0) { + // The cluster is entirely in front of the portal's plane, thus + // every portal in the cluster is also in front of the portal's + // plane and may be seen. However, as portals are one-way (ie, + // can see out the portal along its plane normal, but not into + // the portal against its plane normal), the current portal + // must be behind the cluster's portal, or straddle its plane, + // for a cluster's portal to be considered visible. + thread->clustertest += cluster->numportals; + for (int i = 0; i < cluster->numportals; i++) { + tp = cluster->portals + i; + if (tp == portal) { + thread->selfcull++; + continue; + } + thread->spheretest++; + portal_side = test_sphere (&portal->sphere, tp->plane); + if (portal_side > 0) { + // The portal definitely is entirely in front of the test + // portal's plane. (cannot see into a portal from its + // front side) + thread->spherecull++; + } else if (portal_side < 0) { + // The portal's sphere is behind the test portal's plane, + // so the portal itself is entirely behind the plane + // thus the test portal is potentially visible. + thread->spherepass++; + set_add (thread->portalsee, tp - portals); + } else { + // The portal's sphere straddle's the test portal's + // plane, so need to do a more refined check. + thread->windingtest++; + if (test_winding_back (portal->winding, tp->plane)) { + // The portal is at least partially behind the test + // portal's plane, so the test portal is potentially + // visible. + thread->windingpass++; + set_add (thread->portalsee, tp - portals); + } else { + thread->windingcull++; + } + } } - if (k == winding->numpoints) - continue; // no points on front - } + } else { + thread->clustertest += cluster->numportals; + // The cluster's sphere straddle's the portal's plane, thus each + // portal in the cluster must be tested individually. + for (int i = 0; i < cluster->numportals; i++) { + tp = cluster->portals + i; + if (tp == portal) { + thread->selfcull++; + continue; + } + thread->spheretest++; + // If the target portal is behind the portals's plane, then + // it can't possibly be seen by the portal. + // + // If the portal is in front of the target's plane, then the + // target is of no interest as it is facing counter to the + // flow of visibility. - if (portal_side == 0) { - // The portal's sphere touches the test portal's plane, so - // do a more refined check. - winding = portal->winding; - for (k = 0; k < winding->numpoints; k++) { - d = DotProduct (winding->points[k], - tp->plane.normal) - tp->plane.dist; - if (d < -ON_EPSILON) - break; + // First check using the bounding spheres of the two portals. + tp_side = test_sphere (&tp->sphere, portal->plane); + if (tp_side < 0) { + // The test portal definitely is entirely behind the + // portal's plane. + thread->spherecull++; + continue; // entirely behind + } + portal_side = test_sphere (&portal->sphere, tp->plane); + if (portal_side > 0) { + // The portal definitely is entirely in front of the + // test portal's plane. (cannot see into a portal from + // its front side) + thread->spherecull++; + continue; // entirely in front + } + if (tp_side > 0 && portal_side < 0) { + thread->spherepass++; + set_add (thread->portalsee, tp - portals); + continue; + } + + thread->windingtest++; + if (tp_side == 0) { + // The test portal's sphere touches the portal's plane, + // so do a more refined check. + if (!test_winding_front (tp->winding, portal->plane)) { + thread->windingcull++; + continue; + } + } + if (portal_side == 0) { + // The portal's sphere touches the test portal's plane, + // so do a more refined check. + if (!test_winding_back (portal->winding, tp->plane)) { + thread->windingcull++; + continue; + } + } + + thread->windingpass++; + set_add (thread->portalsee, tp - portals); } - if (k == winding->numpoints) - continue; // no points on front } - - set_add (thread->portalsee, j); } SimpleFlood (thread, portal, portal->cluster); diff --git a/tools/qfvis/source/fatpvs.c b/tools/qfvis/source/fatpvs.c new file mode 100644 index 000000000..724e0d758 --- /dev/null +++ b/tools/qfvis/source/fatpvs.c @@ -0,0 +1,385 @@ +/* + fatpvs.c + + PVS PHS generator tool + + Copyright (C) 2021 Bil Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "QF/bspfile.h" +#include "QF/heapsort.h" +#include "QF/pvsfile.h" +#include "QF/quakefs.h" +#include "QF/set.h" +#include "QF/sizebuf.h" +#include "QF/sys.h" + +#include "tools/qfvis/include/options.h" +#include "tools/qfvis/include/vis.h" + +typedef struct { + uint32_t visoffs; + uint32_t leafnum; +} leafvis_t; + +typedef struct { + uint32_t first_leaf; + uint32_t num_leafs; +} leafmap_t; + +static set_pool_t *set_pool; +static set_t *base_pvs; +static set_t *fat_pvs; +static leafvis_t *leafvis; +static leafmap_t *leafmap; +static sizebuf_t *cmp_pvs; + +static uint32_t num_leafs; +static uint32_t num_clusters; +static uint32_t work_cluster; + +typedef struct { + long pvs_visible; + long fat_visible; + long fat_bytes; +} fatstats_t; + +fatstats_t fatstats; + +static void +update_stats (fatstats_t *stats) +{ + WRLOCK (stats_lock); + fatstats.pvs_visible += stats->pvs_visible; + fatstats.fat_visible += stats->fat_visible; + fatstats.fat_bytes += stats->fat_bytes; + UNLOCK (stats_lock); +} + +static int +cluster_progress (void) +{ + return work_cluster * 100 / num_clusters; +} + +static unsigned +next_cluster (void) +{ + unsigned leaf = ~0; + WRLOCK (global_lock); + progress_tick++; + if (work_cluster < num_clusters) { + leaf = work_cluster++; + } + UNLOCK (global_lock); + return leaf; +} + +static inline void +decompress_vis (const byte *in, unsigned numleafs, set_t *pvs) +{ + byte *out = (byte *) pvs->map; + byte *start = out; + int row, c; + + row = (numleafs + 7) >> 3; + + if (!in) { // no vis info, so make all visible + while (row) { + *out++ = 0xff; + row--; + } + } else { + do { + if (*in) { + *out++ = *in++; + continue; + } + c = in[1]; + in += 2; + while (c) { + *out++ = 0; + c--; + } + } while (out - start < row); + } + + while ((out - start) & (sizeof (set_bits_t) - 1)) { + *out++ = 0; + } +} + +static void * +decompress_thread (void *d) +{ + fatstats_t stats = { }; + int thread = (intptr_t) d; + set_t vis = { }; + + if (num_leafs != num_clusters) { + vis = (set_t) SET_STATIC_INIT (num_leafs - 1, alloca); + } + while (1) { + unsigned cluster_num = next_cluster (); + + if (working) + working[thread] = cluster_num; + if (cluster_num == ~0u) { + break; + } + byte *visdata = 0; + dleaf_t *leaf = &bsp->leafs[leafmap[cluster_num].first_leaf + 1]; + if (leaf->visofs >= 0) { + visdata = bsp->visdata + leaf->visofs; + } + if (num_leafs == num_clusters) { + decompress_vis (visdata, num_leafs, &base_pvs[cluster_num]); + } else { + decompress_vis (visdata, num_leafs, &vis); + set_empty (&base_pvs[cluster_num]); + for (set_iter_t *iter = set_first_r (&set_pool[thread], &vis); + iter; iter = set_next_r (&set_pool[thread], iter)) { + set_add (&base_pvs[cluster_num], leafcluster[iter->element]); + } + } + stats.pvs_visible += set_count (&base_pvs[cluster_num]); + } + update_stats (&stats); + return 0; +} + +static void * +fatten_thread (void *d) +{ + fatstats_t stats = { }; + int thread = (intptr_t) d; + + while (1) { + unsigned cluster_num = next_cluster (); + + if (working) + working[thread] = cluster_num; + if (cluster_num == ~0u) { + break; + } + + set_assign (&fat_pvs[cluster_num], &base_pvs[cluster_num]); + for (set_iter_t *iter = set_first_r (&set_pool[thread], + &base_pvs[cluster_num]); + iter; + iter = set_next_r (&set_pool[thread], iter)) { + + set_union (&fat_pvs[cluster_num], &base_pvs[iter->element]); + } + stats.fat_visible += set_count (&fat_pvs[cluster_num]); + set_difference (&fat_pvs[cluster_num], &base_pvs[cluster_num]); + } + update_stats (&stats); + return 0; +} + +static void * +compress_thread (void *d) +{ + fatstats_t stats = { }; + int thread = (intptr_t) d; + bool rle = options.utf8; + set_t vis = { }; + + if (num_leafs != num_clusters) { + vis = (set_t) SET_STATIC_INIT (num_leafs - 1, alloca); + } + + while (1) { + unsigned cluster_num = next_cluster (); + + if (working) + working[thread] = cluster_num; + if (cluster_num == ~0u) { + break; + } + + sizebuf_t *compressed = &cmp_pvs[cluster_num]; + const byte *fat_bytes = (const byte *) fat_pvs[cluster_num].map; + if (num_leafs != num_clusters) { + fat_bytes = (const byte *) vis.map; + set_empty (&vis); + for (set_iter_t *iter = set_first_r (&set_pool[thread], + &fat_pvs[cluster_num]); + iter; iter = set_next_r (&set_pool[thread], iter)) { + for (uint32_t j = 0; + j < leafmap[iter->element].num_leafs; j++) { + uint32_t l = leafmap[iter->element].first_leaf + j; + set_add (&vis, leafvis[l].leafnum); + } + } + } + stats.fat_bytes += CompressRow (compressed, fat_bytes, num_leafs, rle); + } + update_stats (&stats); + return 0; +} + +static int +leaf_compare (const void *_la, const void *_lb) +{ + const leafvis_t *la = _la; + const leafvis_t *lb = _lb; + if (la->visoffs == lb->visoffs) { + return la->leafnum - lb->leafnum; + } + return la->visoffs - lb->visoffs; +} + +static void +reconstruct_clusters (void) +{ + leafvis = malloc (num_leafs * sizeof (leafvis_t)); + int sorted = 1; + num_clusters = 1; + for (unsigned i = 0; i < num_leafs; i++) { + leafvis[i].visoffs = bsp->leafs[i + 1].visofs; + leafvis[i].leafnum = i; + if (i > 0) { + num_clusters += leafvis[i].visoffs != leafvis[i - 1].visoffs; + if (leafvis[i].visoffs < leafvis[i - 1].visoffs) { + sorted = 0; + } + } + } + if (!sorted) { + heapsort (leafvis, num_leafs, sizeof (leafvis_t), leaf_compare); + num_clusters = 1; + for (unsigned i = 1; i < num_leafs; i++) { + num_clusters += leafvis[i].visoffs != leafvis[i - 1].visoffs; + } + } + + leafcluster = malloc (num_leafs * sizeof (uint32_t)); + leafmap = calloc (num_clusters, sizeof (leafmap_t)); + leafmap_t *lm = leafmap; + uint32_t offs = leafvis[0].visoffs; + for (unsigned i = 0; i < num_leafs; i++) { + if (leafvis[i].visoffs != offs) { + lm++; + lm->first_leaf = i; + offs = leafvis[i].visoffs; + } + leafcluster[leafvis[i].leafnum] = lm - leafmap; + lm->num_leafs++; + } + + printf ("leafs : %u\n", num_leafs); + printf ("clusters: %u\n", num_clusters); +} + +static void +allocate_data (void) +{ + set_pool = calloc (options.threads, sizeof (set_pool_t)); + + base_pvs = malloc (num_clusters * sizeof (set_t)); + fat_pvs = malloc (num_clusters * sizeof (set_t)); + cmp_pvs = malloc (num_clusters * sizeof (sizebuf_t)); + + uint32_t visbytes = (num_leafs + 7) / 8; + // Worst case, RLE can add 50% to the bytes required (alternating zero + // and non-zero bytes: 0 x 0 y -> 0 1 x 0 1 y ...). Also, if the map is + // very tiny (8 leafs or fewer), there will be only one byte for vis, but + // if that byte is 0, an extra byte for the count is required. + visbytes = (visbytes * 3) / 2 + 1; + for (unsigned i = 0; i < num_clusters; i++) { + base_pvs[i] = (set_t) SET_STATIC_INIT (num_clusters - 1, malloc); + fat_pvs[i] = (set_t) SET_STATIC_INIT (num_clusters - 1, malloc); + cmp_pvs[i] = (sizebuf_t) { + .data = malloc (visbytes), + .maxsize = visbytes, + }; + } +} + +static void +write_pvs_file (void) +{ + uint32_t offset = sizeof (pvsfile_t) + num_leafs * sizeof (uint32_t); + pvsfile_t *pvsfile = malloc (offset + fatstats.fat_bytes); + + strncpy (pvsfile->magic, PVS_MAGIC, sizeof (pvsfile->magic)); + pvsfile->version = PVS_VERSION; + pvsfile->md4_offset = 0; //FIXME add + pvsfile->flags = PVS_IS_FATPVS; + if (options.utf8) { + pvsfile->flags |= PVS_UTF8_RLE; + } + pvsfile->visleafs = num_leafs; + for (uint32_t i = 0; i < num_clusters; i++) { + unsigned size = cmp_pvs[i].cursize; + for (uint32_t j = 0; j < leafmap[i].num_leafs; j++) { + uint32_t l = leafmap[i].first_leaf + j; + pvsfile->visoffsets[leafvis[l].leafnum] = offset; + } + memcpy ((byte *) pvsfile + offset, cmp_pvs[i].data, size); + offset += size; + } + + dstring_t *pvsname = dstring_new (); + dstring_copystr (pvsname, options.bspfile->str); + QFS_SetExtension (pvsname, ".pvs"); + + QFile *f = Qopen (pvsname->str, "wb"); + if (!f) { + Sys_Error ("couldn't open %s for writing.", pvsname->str); + } + Qwrite (f, pvsfile, offset); + Qclose (f); +} + +void +CalcFatPVS (void) +{ + num_leafs = bsp->models[0].visleafs; + + reconstruct_clusters (); + allocate_data (); + + work_cluster = 0; + RunThreads ("Decompress", decompress_thread, cluster_progress); + + work_cluster = 0; + RunThreads ("Fatten", fatten_thread, cluster_progress); + + work_cluster = 0; + RunThreads ("Compress", compress_thread, cluster_progress); + + printf ("Average clusters visible / fat visible / total: %d / %d / %d\n", + (int) (fatstats.pvs_visible / num_clusters), + (int) (fatstats.fat_visible / num_clusters), num_clusters); + printf ("Compressed fat vis size: %ld\n", fatstats.fat_bytes); + + write_pvs_file (); +} diff --git a/tools/qfvis/source/flow.c b/tools/qfvis/source/flow.c index 15af6524f..5bc36edf4 100644 --- a/tools/qfvis/source/flow.c +++ b/tools/qfvis/source/flow.c @@ -48,15 +48,15 @@ #include #include -#include "QF/alloc.h" #include "QF/bspfile.h" +#include "QF/cmem.h" #include "QF/cmd.h" #include "QF/mathlib.h" #include "QF/quakefs.h" #include "QF/sys.h" -#include "vis.h" -#include "options.h" +#include "tools/qfvis/include/vis.h" +#include "tools/qfvis/include/options.h" static int CheckStack (cluster_t *cluster, threaddata_t *thread) @@ -79,9 +79,10 @@ new_stack (threaddata_t *td) { pstack_t *stack; - stack = malloc (sizeof (pstack_t)); + stack = cmemalloc (td->memsuper, sizeof (pstack_t)); stack->next = 0; stack->mightsee = set_new_size_r (&td->set_pool, portalclusters); + td->stats.stack_alloc++; return stack; } @@ -90,23 +91,34 @@ new_separator (threaddata_t *thread) { sep_t *sep; - ALLOC (128, sep_t, thread->sep, sep); + sep = CMEMALLOC (32, sep_t, thread->sep, thread->memsuper); + thread->stats.sep_alloc++; return sep; } static void delete_separator (threaddata_t *thread, sep_t *sep) { - FREE (thread->sep, sep); + thread->stats.sep_free++; + CMEMFREE (thread->sep, sep); } static void free_separators (threaddata_t *thread, sep_t *sep_list) { + unsigned count = thread->stats.sep_alloc - thread->stats.sep_free; + if (count > thread->stats.sep_highwater) { + thread->stats.sep_highwater = count; + } + count = 0; while (sep_list) { sep_t *sep = sep_list; sep_list = sep->next; delete_separator (thread, sep); + count++; + } + if (count > thread->stats.sep_maxbulk) { + thread->stats.sep_maxbulk = count; } } @@ -121,48 +133,41 @@ test_zero (float d) } static int -calc_plane (const vec3_t v1, const vec3_t v2, int flip, const vec3_t p, - plane_t *plane) +calc_plane (vec4f_t v1, vec4f_t v2, int flip, vec4f_t p, vec4f_t *plane) { - vec_t length; + vec4f_t length; if (flip < 0) { //CrossProduct (v2, v1, plane.normal); - plane->normal[0] = v2[1] * v1[2] - v2[2] * v1[1]; - plane->normal[1] = v2[2] * v1[0] - v2[0] * v1[2]; - plane->normal[2] = v2[0] * v1[1] - v2[1] * v1[0]; + *plane = crossf (v2, v1); } else { //CrossProduct (v1, v2, plane.normal); - plane->normal[0] = v1[1] * v2[2] - v1[2] * v2[1]; - plane->normal[1] = v1[2] * v2[0] - v1[0] * v2[2]; - plane->normal[2] = v1[0] * v2[1] - v1[1] * v2[0]; + *plane = crossf (v1, v2); } - length = DotProduct (plane->normal, plane->normal); + length = dotf (*plane, *plane); // if points don't make a valid plane, skip it - if (length < ON_EPSILON) + if (length[0] < ON_EPSILON) return 0; - length = 1 / sqrt (length); - VectorScale (plane->normal, length, plane->normal); - plane->dist = DotProduct (p, plane->normal); + (*plane)[3] = -dotf (p, *plane)[0]; return 1; } static inline int -test_plane (const plane_t *plane, const winding_t *pass, int index) +test_plane (vec4f_t plane, const winding_t *pass, unsigned index) { int s1, s2; int k; - vec_t d; + vec4f_t d; - k = (index + 1) % pass->numpoints; - d = DotProduct (pass->points[k], plane->normal) - plane->dist; - s1 = test_zero (d); - k = (index + pass->numpoints - 1) % pass->numpoints; - d = DotProduct (pass->points[k], plane->normal) - plane->dist; - s2 = test_zero (d); + k = index + 1 == pass->numpoints ? 0 : index + 1; + d = dotf (pass->points[k], plane); + s1 = test_zero (d[0]); + k = (index == 0 ? pass->numpoints : index) - 1; + d = dotf (pass->points[k], plane); + s2 = test_zero (d[0]); if (s1 == 0 && s2 == 0) return 0; if (s1 < 0 || s2 < 0) @@ -171,32 +176,30 @@ test_plane (const plane_t *plane, const winding_t *pass, int index) } static inline sep_t * -create_separator (threaddata_t *thread, const plane_t *src_pl, - const vec3_t p1, const vec3_t v1, +create_separator (threaddata_t *thread, vec4f_t src_pl, vec4f_t p1, vec4f_t v1, const winding_t *pass, int index, int flip) { int fliptest; - vec_t d; - vec3_t v2; - plane_t plane; + vec4f_t d; + vec4f_t v2; + vec4f_t plane; sep_t *sep; - d = DotProduct (pass->points[index], src_pl->normal) - src_pl->dist; - if ((fliptest = test_zero (d)) == 0) + d = dotf (pass->points[index], src_pl); + if ((fliptest = test_zero (d[0])) == 0) return 0; // The point lies in the source plane - VectorSubtract (pass->points[index], p1, v2); + v2 = pass->points[index] - p1; if (!calc_plane (v1, v2, fliptest, pass->points[index], &plane)) return 0; // point does not form a valid plane - if (!test_plane (&plane, pass, index)) + if (!test_plane (plane, pass, index)) return 0; // not the right point sep = new_separator (thread); // flip the normal if we want the back side if (flip) { - VectorNegate (plane.normal, sep->plane.normal); - sep->plane.dist = -plane.dist; + sep->plane = -plane; } else { sep->plane = plane; } @@ -221,20 +224,20 @@ create_separator (threaddata_t *thread, const plane_t *src_pl, */ static sep_t * FindSeparators (threaddata_t *thread, - const winding_t *source, const plane_t src_pl, + const winding_t *source, vec4f_t src_pl, const winding_t *pass, int flip) { - int i, j, l; - vec3_t v1; + unsigned i, j, l; + vec4f_t v1; sep_t *separators = 0, *sep; for (i = 0; i < source->numpoints; i++) { - l = (i + 1) % source->numpoints; - VectorSubtract (source->points[l], source->points[i], v1); + l = (i + 1) == source->numpoints ? 0 : i + 1; + v1 = source->points[l] - source->points[i]; for (j = 0; j < pass->numpoints; j++) { - sep = create_separator (thread, &src_pl, source->points[i], v1, + sep = create_separator (thread, src_pl, source->points[i], v1, pass, j, flip); if (sep) { sep->next = separators; @@ -247,12 +250,13 @@ FindSeparators (threaddata_t *thread, } static winding_t * -ClipToSeparators (const sep_t *separators, winding_t *target) +ClipToSeparators (threaddata_t *thread, const sep_t *separators, + winding_t *target) { const sep_t *sep; for (sep = separators; target && sep; sep = sep->next) { - target = ClipWinding (target, &sep->plane, false); + target = ClipWinding (thread, target, sep->plane, false); } return target; } @@ -288,11 +292,18 @@ mightsee_more (set_t *might, const set_t *prev_might, const set_t *test, return more != 0; } +static void +free_winding_memory (threaddata_t *thread, size_t winding_mark) +{ + thread->stats.winding_mark = max (thread->stats.winding_mark, + Hunk_LowMark (thread->hunk)); + Hunk_RawFreeToLowMark (thread->hunk, winding_mark); +} + /* RecursiveClusterFlow Flood fill through the clusters - If src_portal is NULL, this is the originating cluster */ static void RecursiveClusterFlow (int clusternum, threaddata_t *thread, pstack_t *prevstack) @@ -303,8 +314,8 @@ RecursiveClusterFlow (int clusternum, threaddata_t *thread, pstack_t *prevstack) cluster_t *cluster; pstack_t *stack; portal_t *target_portal; - plane_t backplane; - const plane_t *source_plane, *pass_plane; + vec4f_t backplane; + vec4f_t source_plane, pass_plane; const winding_t *pass_winding; winding_t *source_winding, *target_winding; @@ -332,14 +343,15 @@ RecursiveClusterFlow (int clusternum, threaddata_t *thread, pstack_t *prevstack) might = stack->mightsee; vis = thread->clustervis; - source_plane = &thread->pstack_head.pass_plane; + source_plane = thread->pstack_head.pass_plane; pass_winding = prevstack->pass_winding; - pass_plane = &prevstack->pass_plane; + pass_plane = prevstack->pass_plane; + + size_t winding_mark = Hunk_LowMark (thread->hunk); // check all portals for flowing into other clusters for (i = 0; i < cluster->numportals; i++) { - target_portal = cluster->portals[i]; - + target_portal = &cluster->portals[i]; if (!set_is_member (prevstack->mightsee, target_portal->cluster)) continue; // can't possibly see it @@ -351,16 +363,20 @@ RecursiveClusterFlow (int clusternum, threaddata_t *thread, pstack_t *prevstack) } // get plane of target_portal, point normal into the neighbor cluster - VectorNegate (target_portal->plane.normal, backplane.normal); - backplane.dist = -target_portal->plane.dist; + backplane = -target_portal->plane; - if (_VectorCompare (pass_plane->normal, backplane.normal)) + vec4f_t diff = vabs4f (pass_plane - backplane); + vec4i_t cmp = diff > (vec4f_t) {0.001, 0.001, 0.001, 0.001}; + if (!(cmp[0] || cmp[1] || cmp[2])) { // dist isn't interesting continue; // can't go out a coplanar face + } + + free_winding_memory (thread, winding_mark); thread->stats.portalcheck++; target_winding = target_portal->winding; - target_winding = ClipWinding (target_winding, source_plane, false); + target_winding = ClipWinding (thread, target_winding, source_plane, false); if (!target_winding) continue; @@ -374,21 +390,19 @@ RecursiveClusterFlow (int clusternum, threaddata_t *thread, pstack_t *prevstack) stack->pass_portal = target_portal; RecursiveClusterFlow (target_portal->cluster, thread, stack); - FreeWinding (target_winding); continue; } - target_winding = ClipWinding (target_winding, pass_plane, false); + target_winding = ClipWinding (thread, target_winding, pass_plane, false); if (!target_winding) continue; // copy source_winding because it likely is already a copy and thus // if it gets clipped away, earlier stack levels will get corrupted - source_winding = CopyWinding (prevstack->source_winding); + source_winding = CopyWinding (thread, prevstack->source_winding); - source_winding = ClipWinding (source_winding, &backplane, false); + source_winding = ClipWinding (thread, source_winding, backplane, false); if (!source_winding) { - FreeWinding (target_winding); continue; } @@ -400,13 +414,13 @@ RecursiveClusterFlow (int clusternum, threaddata_t *thread, pstack_t *prevstack) if (!stack->separators[0]) stack->separators[0] = FindSeparators (thread, source_winding, - *source_plane, + source_plane, pass_winding, 0); - target_winding = ClipToSeparators (stack->separators[0], + target_winding = ClipToSeparators (thread, + stack->separators[0], target_winding); if (!target_winding) { thread->stats.targetclipped++; - FreeWinding (source_winding); continue; } if (target_winding != old) @@ -418,13 +432,13 @@ RecursiveClusterFlow (int clusternum, threaddata_t *thread, pstack_t *prevstack) if (!stack->separators[1]) stack->separators[1] = FindSeparators (thread, pass_winding, - *pass_plane, + pass_plane, source_winding, 1); - target_winding = ClipToSeparators (stack->separators[1], + target_winding = ClipToSeparators (thread, + stack->separators[1], target_winding); if (!target_winding) { thread->stats.targetclipped++; - FreeWinding (source_winding); continue; } if (target_winding != old) @@ -438,11 +452,10 @@ RecursiveClusterFlow (int clusternum, threaddata_t *thread, pstack_t *prevstack) sep = FindSeparators (thread, target_winding, target_portal->plane, pass_winding, 0); - source_winding = ClipToSeparators (sep, source_winding); + source_winding = ClipToSeparators (thread, sep, source_winding); free_separators (thread, sep); if (!source_winding) { thread->stats.sourceclipped++; - FreeWinding (target_winding); continue; } if (source_winding != old) @@ -452,13 +465,12 @@ RecursiveClusterFlow (int clusternum, threaddata_t *thread, pstack_t *prevstack) if (options.level > 3) { winding_t *old = source_winding; sep_t *sep; - sep = FindSeparators (thread, pass_winding, *pass_plane, + sep = FindSeparators (thread, pass_winding, pass_plane, target_winding, 1); - source_winding = ClipToSeparators (sep, source_winding); + source_winding = ClipToSeparators (thread, sep, source_winding); free_separators (thread, sep); if (!source_winding) { thread->stats.sourceclipped++; - FreeWinding (target_winding); continue; } if (source_winding != old) @@ -475,10 +487,8 @@ RecursiveClusterFlow (int clusternum, threaddata_t *thread, pstack_t *prevstack) // flow through it for real RecursiveClusterFlow (target_portal->cluster, thread, stack); - - FreeWinding (source_winding); - FreeWinding (target_winding); } + free_winding_memory (thread, winding_mark); free_separators (thread, stack->separators[1]); free_separators (thread, stack->separators[0]); } diff --git a/tools/qfvis/source/notes b/tools/qfvis/source/notes deleted file mode 100644 index 2bc29508b..000000000 --- a/tools/qfvis/source/notes +++ /dev/null @@ -1,29 +0,0 @@ -non-threaded access only ---- -bsp -originalvismapsize -portalleafs -totalvis -uncompressed -visdata - -threaded read-only ---- -bitbytes -bitlongs -numportals -options - -threaded read/write ---- -portals -leafs - -threaded read/write stats-only ---- -c_chains -c_mighttest -c_portaltest -c_portalpass -c_portalcheck -c_vistest diff --git a/tools/qfvis/source/options.c b/tools/qfvis/source/options.c index 048f604b0..a0a8ff221 100644 --- a/tools/qfvis/source/options.c +++ b/tools/qfvis/source/options.c @@ -40,13 +40,25 @@ #include #include "QF/dstring.h" +#include "QF/sys.h" -#include "options.h" -#include "vis.h" +#include "tools/qfvis/include/options.h" +#include "tools/qfvis/include/vis.h" const char *this_program; +enum { + start_opts = 255, // not used, starts the enum. + OPT_PORTAL_LIMIT, + OPT_FAT_PVS, + OPT_FULL_PVS, + OPT_UTF8, +}; + static struct option const long_options[] = { + {"fat-pvs", no_argument, 0, OPT_FAT_PVS}, + {"full-pvs", no_argument, 0, OPT_FULL_PVS}, + {"utf8", no_argument, 0, OPT_UTF8}, {"quiet", no_argument, 0, 'q'}, {"verbose", no_argument, 0, 'v'}, {"help", no_argument, 0, 'h'}, @@ -55,6 +67,7 @@ static struct option const long_options[] = { {"minimal", no_argument, 0, 'm'}, {"level", required_argument, 0, 'l'}, {"file", required_argument, 0, 'f'}, + {"portal-limit", required_argument, 0, OPT_PORTAL_LIMIT}, {NULL, 0, NULL, 0} }; @@ -93,11 +106,7 @@ default_threads (void) { int threads = 1; #ifdef USE_PTHREADS -# ifdef _SC_NPROCESSORS_ONLN - threads = sysconf(_SC_NPROCESSORS_ONLN); - if (threads < 1) - threads = 1; -# endif + threads = Sys_ProcessorCount (); #endif return threads; } @@ -131,6 +140,19 @@ DecodeArgs (int argc, char **argv) case 't': // threads options.threads = atoi (optarg); break; + case OPT_PORTAL_LIMIT: + options.portal_limit = atoi (optarg); + break; + case OPT_FAT_PVS: + options.fat_pvs = true; + options.no_auto_pvs = true; + break; + case OPT_FULL_PVS: + options.fat_pvs = true; + break; + case OPT_UTF8: + options.utf8 = true; + break; case 'm': // minimal vis options.minimal = true; break; diff --git a/tools/qfvis/source/qfvis.c b/tools/qfvis/source/qfvis.c index 7ec7e776c..0aa29f973 100644 --- a/tools/qfvis/source/qfvis.c +++ b/tools/qfvis/source/qfvis.c @@ -51,14 +51,16 @@ #include "QF/bspfile.h" #include "QF/cmd.h" +#include "QF/cmem.h" #include "QF/dstring.h" #include "QF/mathlib.h" -#include "QF/qtypes.h" +#include "QF/msg.h" #include "QF/quakefs.h" +#include "QF/sizebuf.h" #include "QF/sys.h" -#include "vis.h" -#include "options.h" +#include "tools/qfvis/include/vis.h" +#include "tools/qfvis/include/options.h" #ifdef USE_PTHREADS pthread_attr_t threads_attrib; @@ -71,14 +73,24 @@ bsp_t *bsp; options_t options; -static visstat_t stats; -int base_mightsee; +static threaddata_t main_thread; +static visstat_t global_stats; +unsigned long base_mightsee; +unsigned long base_selfcull; +unsigned long base_clustercull; +unsigned long base_clustertest; +unsigned long base_spherecull; +unsigned long base_spheretest; +unsigned long base_spherepass; +unsigned long base_windingcull; +unsigned long base_windingtest; +unsigned long base_windingpass; -int portal_count; -int numportals; -int portalclusters; -int numrealleafs; -size_t originalvismapsize; +static unsigned portal_count; +unsigned numportals; +unsigned portalclusters; +unsigned numrealleafs; +unsigned originalvismapsize; int totalvis; int count_sep; int bitbytes; // (portalleafs + 63)>>3 @@ -91,9 +103,10 @@ portal_t *portals; cluster_t *clusters; dstring_t *visdata; byte *uncompressed; // [bitbytes * portalleafs] -int *leafcluster; // leaf to cluster mappings as read from .prt file +uint32_t *leafcluster; // leaf to cluster mappings as read from .prt file int *working; // per thread current portal # +int progress_tick; static void InitThreads (void) @@ -111,6 +124,9 @@ InitThreads (void) stats_lock = malloc (sizeof (pthread_rwlock_t)); if (pthread_rwlock_init (stats_lock, 0)) Sys_Error ("pthread_rwlock_init failed"); +#else + // Unable to run multi-threaded, so force threadcount to 1 + options.threads = 1; #endif } @@ -127,67 +143,175 @@ EndThreads (void) #endif } -static void -PlaneFromWinding (winding_t *winding, plane_t *plane) +static vec4f_t +PlaneFromWinding (winding_t *winding) { - vec3_t v1, v2; + vec4f_t plane; + vec4f_t v1, v2; // calc plane using CW winding - VectorSubtract (winding->points[2], winding->points[1], v1); - VectorSubtract (winding->points[0], winding->points[1], v2); - CrossProduct (v2, v1, plane->normal); - _VectorNormalize (plane->normal); - plane->dist = DotProduct (winding->points[0], plane->normal); + v1 = winding->points[2] - winding->points[1]; + v2 = winding->points[0] - winding->points[1]; + plane = normalf (crossf (v2, v1)); + // negative so dot(point, plane) includes -dist (point[3] = 1) + plane[3] = -dotf (winding->points[0], plane)[0]; + return plane; } winding_t * -NewWinding (int points) +NewWinding (threaddata_t *thread, int points) { winding_t *winding; - size_t size; + unsigned size; - if (points > MAX_POINTS_ON_WINDING) - Sys_Error ("NewWinding: %i points", points); - - size = (size_t)(uintptr_t) ((winding_t *) 0)->points[points]; - winding = calloc (1, size); + size = field_offset (winding_t, points[points]); + winding = Hunk_RawAlloc (thread->hunk, size); + memset (winding, 0, size); + winding->id = thread->winding_id++; + winding->thread = thread->id; return winding; } -void -FreeWinding (winding_t *w) -{ - if (!w->original) - free (w); -} - winding_t * -CopyWinding (const winding_t *w) +CopyWinding (threaddata_t *thread, const winding_t *w) { - size_t size; + unsigned size; winding_t *copy; - size = (size_t) (uintptr_t) ((winding_t *) 0)->points[w->numpoints]; - copy = malloc (size); + size = field_offset (winding_t, points[w->numpoints]); + copy = Hunk_RawAlloc (thread->hunk, size); memcpy (copy, w, size); copy->original = false; + copy->id = thread->winding_id++; + copy->thread = thread->id; return copy; } static winding_t * -NewFlippedWinding (const winding_t *w) +NewFlippedWinding (threaddata_t *thread, const winding_t *w) { winding_t *flipped; - int i; + unsigned i; - flipped = NewWinding (w->numpoints); - for (i = 0; i < w->numpoints; i++) - VectorCopy (w->points[i], flipped->points[w->numpoints - 1 - i]); + flipped = NewWinding (thread, w->numpoints); + for (i = 0; i < w->numpoints; i++) { + flipped->points[w->numpoints - 1 - i] = w->points[i]; + } flipped->numpoints = w->numpoints; return flipped; } +static vec4i_t +signeps (vec4f_t dist) +{ +#ifdef __SSE3__ + const vec4f_t zero = {}; + const vec4f_t eps = { ON_EPSILON, ON_EPSILON, ON_EPSILON, ON_EPSILON }; + vec4f_t d = _mm_addsub_ps (zero, dist); + vec4i_t c = (d - eps) > 0; + c = (vec4i_t) _mm_hsub_epi32 ((__m128i) c, (__m128i) c); + return c; +#else + float d = dist[0]; + int front = (d >= ON_EPSILON); + int back = (d <= -ON_EPSILON); + int i = front - back; + return (vec4i_t) { i, i, i, i }; +#endif +} + +static vec4f_t +split_edge (const vec4f_t *points, const vec4f_t *dists, + int ind1, int ind2, vec4f_t split) +{ + vec4f_t p1 = points[ind1]; + vec4f_t p2 = points[ind2]; + vec4f_t d1 = dists[ind1]; + vec4f_t d2 = dists[ind2]; + // avoid nan/inf in w: d1's w is never 0 (would not be here if it was) + // so the multiply ensures d1.w - d2.w cannot be 0 and thus d1.w/diff + // will not result in division by 0 + static const vec4f_t one = { 1, 1, 1, 0 }; + vec4f_t d = d1 / (d1 - d2 * one); + vec4f_t mid = p1 + d * (p2 - p1); + + // avoid roundoff error when possible by forcing the appropriate + // component to the split-plane's distance when the split-plane's + // normal is signed-canonical. + // "nan" because 0x7fffffff is nan when viewed as a float + static const vec4i_t onenan = {0x3f800000,0x3f800000,0x3f800000,~0u >> 1}; + static const vec4i_t nan = { ~0u >> 1, ~0u >> 1, ~0u >> 1, ~0u >> 1}; + vec4i_t x = ((vec4i_t) split & nan) == onenan; + // plane vector has -dist in w + vec4f_t y = (vec4f_t) ((vec4i_t) split & x) * -split[3]; +#ifdef __SSE3__ + mid = _mm_blendv_ps (mid, y, (__m128) x); +#else + mid = (vec4f_t) (((vec4i_t) y & x) | ((vec4i_t) mid & ~x)); +#endif +// if (isnan (mid[0])) *(int *) 0 = 0; + return mid; +} + +static inline unsigned __attribute__((const)) +is_not_on (unsigned x) +{ + return x & 1; +} + +static inline unsigned __attribute__((const)) +is_back (unsigned x) +{ + return x & 2; +} + +static inline unsigned __attribute__((const)) +is_not_back (unsigned x) +{ + return ~x & 2; +} + +static inline unsigned __attribute__((const)) +is_front (unsigned x) +{ + return is_not_on (x) & (is_not_back (x) >> 1); +} + +static inline unsigned __attribute__((const)) +is_back_front (unsigned x, unsigned y) +{ + return (is_back (x) >> 1) & is_front (y); +} + +static inline unsigned __attribute__((const)) +is_front_back (unsigned x, unsigned y) +{ + return is_front (x) & (is_back (y) >> 1); +} + +static inline unsigned __attribute__((const)) +is_transition (unsigned x, unsigned y) +{ + return is_back_front (x, y) | is_front_back (x, y); +} + +static inline void +test_point (vec4f_t split, const vec4f_t *points, int index, vec4f_t *dists, + int *sides, unsigned *counts) +{ + dists[index] = dotf (points[index], split); + sides[index] = signeps (dists[index])[0]; + counts[sides[index]]++; +} + +#undef SIDE_FRONT +#undef SIDE_BACK +#undef SIDE_ON +#define SIDE_FRONT 1 +#define SIDE_BACK -1 +#define SIDE_ON 0 + /* ClipWinding @@ -200,127 +324,138 @@ NewFlippedWinding (const winding_t *w) it will be clipped away. */ winding_t * -ClipWinding (winding_t *in, const plane_t *split, qboolean keepon) +ClipWinding (threaddata_t *thread, winding_t *in, vec4f_t split, + bool keepon) { - int maxpts, i, j; - int counts[3], sides[MAX_POINTS_ON_WINDING]; - vec_t dot; - vec_t dists[MAX_POINTS_ON_WINDING]; - vec_t *p1, *p2; - vec3_t mid; + unsigned maxpts = 0; + unsigned i; + unsigned _counts[3]; + unsigned *const counts = _counts + 1; + int *const sides = alloca ((in->numpoints + 1) * sizeof (int)); + vec4f_t *const dists = alloca ((in->numpoints + 1) * sizeof (vec4f_t)); winding_t *neww; - counts[0] = counts[1] = counts[2] = 0; + counts[SIDE_FRONT] = counts[SIDE_ON] = counts[SIDE_BACK] = 0; // determine sides for each point - for (i = 0; i < in->numpoints; i++) { - dot = DotProduct (in->points[i], split->normal); - dot -= split->dist; - dists[i] = dot; - if (dot > ON_EPSILON) - sides[i] = SIDE_FRONT; - else if (dot < -ON_EPSILON) - sides[i] = SIDE_BACK; - else { - sides[i] = SIDE_ON; - } - counts[sides[i]]++; + test_point (split, in->points, 0, dists, sides, counts); + for (i = 1; i < in->numpoints; i++) { + test_point (split, in->points, i, dists, sides, counts); + maxpts += is_transition (sides[i - 1], sides[i]); } sides[i] = sides[0]; dists[i] = dists[0]; + maxpts += is_transition (sides[i - 1], sides[i]); - if (keepon && !counts[0] && !counts[1]) + if (keepon && counts[SIDE_ON] == in->numpoints) { return in; - - if (!counts[0]) { - FreeWinding (in); + } + if (!counts[SIDE_FRONT]) { return NULL; } - if (!counts[1]) + if (!counts[SIDE_BACK]) { return in; - - maxpts = in->numpoints + 4; // can't use counts[0] + 2 because - // of fp grouping errors - neww = NewWinding (maxpts); - - for (i = 0; i < in->numpoints; i++) { - p1 = in->points[i]; - - if (sides[i] == SIDE_ON) { - VectorCopy (p1, neww->points[neww->numpoints]); - neww->numpoints++; - continue; - } - - if (sides[i] == SIDE_FRONT) { - VectorCopy (p1, neww->points[neww->numpoints]); - neww->numpoints++; - } - - if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i]) - continue; - - // generate a split point - p2 = in->points[(i + 1) % in->numpoints]; - - dot = dists[i] / (dists[i] - dists[i + 1]); - for (j = 0; j < 3; j++) { - // avoid round off error when possible - if (split->normal[j] == 1) - mid[j] = split->dist; - else if (split->normal[j] == -1) - mid[j] = -split->dist; - else - mid[j] = p1[j] + dot * (p2[j] - p1[j]); - } - - VectorCopy (mid, neww->points[neww->numpoints]); - neww->numpoints++; } - if (neww->numpoints > maxpts) - Sys_Error ("ClipWinding: points exceeded estimate"); - // free the original winding - FreeWinding (in); + maxpts += in->numpoints - counts[SIDE_BACK]; + neww = NewWinding (thread, maxpts); + + for (i = 0; i < in->numpoints; i++) { + if (sides[i] == SIDE_ON) { + neww->points[neww->numpoints++] = in->points[i]; + continue; + } + if (sides[i] == SIDE_FRONT) { + neww->points[neww->numpoints++] = in->points[i]; + } + if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i]) { + continue; + } + unsigned j = (i + 1) == in->numpoints ? 0 : i + 1; + vec4f_t mid = split_edge (in->points, dists, i, j, split); + neww->points[neww->numpoints++] = mid; + } + + if (neww->numpoints < maxpts) { + Sys_Error ("ClipWinding: not all points copied: n:%u m:%u i:%u %u %u %u", + neww->numpoints, maxpts, in->numpoints, + counts[SIDE_BACK], counts[SIDE_ON], counts[SIDE_FRONT]); + } + if (neww->numpoints > maxpts) { + Sys_Error ("ClipWinding: points exceeded estimate: n:%u m:%u", + neww->numpoints, maxpts); + } return neww; } static portal_t * -GetNextPortal (void) +GetNextPortal (int limit) { portal_t *p = 0; - WRLOCK (global_lock); - if (portal_count < 2 * numportals) { - p = portal_queue[portal_count++]; - p->status = stat_selected; + if (!(limit + && options.portal_limit > 0 + && portal_count >= options.portal_limit)) { + WRLOCK (global_lock); + progress_tick++; + if (portal_count < 2 * numportals) { + p = portal_queue[portal_count++]; + p->status = stat_selected; + } + UNLOCK (global_lock); } - UNLOCK (global_lock); return p; } static void -UpdateMightsee (cluster_t *source, cluster_t *dest) +UpdateMightsee (threaddata_t *thread, cluster_t *source, cluster_t *dest) { int i, clusternum; portal_t *portal; clusternum = dest - clusters; for (i = 0; i < source->numportals; i++) { - portal = source->portals[i]; + portal = &source->portals[i]; WRLOCK_PORTAL (portal); if (portal->status == stat_none) { if (set_is_member (portal->mightsee, clusternum)) { set_remove (portal->mightsee, clusternum); portal->nummightsee--; - stats.mightseeupdate++; + thread->stats.mightseeupdate++; } } UNLOCK_PORTAL (portal); } } +static void +UpdateStats (threaddata_t *thread) +{ + WRLOCK (stats_lock); + global_stats.portaltest += thread->stats.portaltest; + global_stats.portalpass += thread->stats.portalpass; + global_stats.portalcheck += thread->stats.portalcheck; + global_stats.targettested += thread->stats.targettested; + global_stats.targettrimmed += thread->stats.targettrimmed; + global_stats.targetclipped += thread->stats.targetclipped; + global_stats.sourcetested += thread->stats.sourcetested; + global_stats.sourcetrimmed += thread->stats.sourcetrimmed; + global_stats.sourceclipped += thread->stats.sourceclipped; + global_stats.chains += thread->stats.chains; + global_stats.mighttest += thread->stats.mighttest; + global_stats.vistest += thread->stats.vistest; + global_stats.mightseeupdate += thread->stats.mightseeupdate; + global_stats.sep_alloc += thread->stats.sep_alloc; + global_stats.sep_free += thread->stats.sep_free; + global_stats.winding_mark = max (global_stats.winding_mark, + thread->stats.winding_mark); + global_stats.stack_alloc += thread->stats.stack_alloc; + global_stats.stack_free += thread->stats.stack_free; + UNLOCK (stats_lock); + memset (&thread->stats, 0, sizeof (thread->stats)); +} + static void PortalCompleted (threaddata_t *thread, portal_t *completed) { @@ -331,45 +466,87 @@ PortalCompleted (threaddata_t *thread, portal_t *completed) int i, j; completed->status = stat_done; - WRLOCK (stats_lock); - stats.portaltest += thread->stats.portaltest; - stats.portalpass += thread->stats.portalpass; - stats.portalcheck += thread->stats.portalcheck; - stats.targettested += thread->stats.targettested; - stats.targettrimmed += thread->stats.targettrimmed; - stats.targetclipped += thread->stats.targetclipped; - stats.sourcetested += thread->stats.sourcetested; - stats.sourcetrimmed += thread->stats.sourcetrimmed; - stats.sourceclipped += thread->stats.sourceclipped; - stats.chains += thread->stats.chains; - stats.mighttest += thread->stats.mighttest; - stats.vistest += thread->stats.vistest; - stats.mightseeupdate += thread->stats.mightseeupdate; - UNLOCK (stats_lock); - memset (&thread->stats, 0, sizeof (thread->stats)); changed = set_new_size_r (&thread->set_pool, portalclusters); cluster = &clusters[completed->cluster]; for (i = 0; i < cluster->numportals; i++) { - portal = cluster->portals[i]; + portal = &cluster->portals[i]; if (portal->status != stat_done) continue; set_assign (changed, portal->mightsee); set_difference (changed, portal->visbits); +#if 0 + dstring_copystr (thread->str, "mightsee: "); + set_as_string_r (thread->str, portal->mightsee); + dstring_appendstr (thread->str, "\nvisbits: "); + set_as_string_r (thread->str, portal->visbits); + dstring_appendstr (thread->str, "\nchanged: "); + set_as_string_r (thread->str, changed); + dstring_appendstr (thread->str, "\n"); + write (0, thread->str->str, thread->str->size - 1); +#endif for (j = 0; j < cluster->numportals; j++) { if (j == i) continue; - if (cluster->portals[j]->status == stat_done) - set_difference (changed, cluster->portals[j]->visbits); + if (cluster->portals[j].status == stat_done) + set_difference (changed, cluster->portals[j].visbits); else - set_difference (changed, cluster->portals[j]->mightsee); + set_difference (changed, cluster->portals[j].mightsee); } for (ci = set_first_r (&thread->set_pool, changed); ci; ci = set_next_r (&thread->set_pool, ci)) { - UpdateMightsee (&clusters[ci->element], cluster); + UpdateMightsee (thread, &clusters[ci->element], cluster); } } set_delete_r (&thread->set_pool, changed); + + UpdateStats (thread); +} + +static void +dump_super_stats (int id, memsuper_t *super) +{ + size_t total_size = 0; + size_t total_allocated = 0; + size_t total_post_size = 0; + size_t total_post_allocated = 0; + size_t num_blocks = 0; + size_t free_counts[MAX_CACHE_LINES]; + + for (memblock_t *block = super->memblocks; block; block = block->next) { + num_blocks++; + total_size += block->size; + total_allocated += block->allocated; + total_post_size += block->post_size; + // post_free is a flag + total_post_allocated += !block->post_free * block->post_size; + } + for (int i = 0; i < MAX_CACHE_LINES; i++) { + free_counts[i] = 0; + for (memline_t *line = super->free_lines[i]; line; + line = line->free_next) { + free_counts[i]++; + } + } + + WRLOCK (global_lock); + printf ("cmem stats for thread %d\n", id); + printf (" blocks: %zd\n", num_blocks); + printf (" : s:%-8zd a:%-8zd f:%-8zd\n", total_size, + total_allocated, total_size - total_allocated); + printf (" post: s:%-8zd a:%-8zd f:%-8zd\n", total_post_size, + total_post_allocated, total_post_size - total_post_allocated); + printf (" "); + for (int i = 0; i < MAX_CACHE_LINES; i++) { + printf (" %5d", 64 << i); + } + printf ("\n"); + printf (" "); + for (int i = 0; i < MAX_CACHE_LINES; i++) { + printf (" %5zd", free_counts[i]); + } + printf ("\n"); + UNLOCK (global_lock); } static void * @@ -378,33 +555,61 @@ LeafThread (void *_thread) portal_t *portal; int thread = (int) (intptr_t) _thread; threaddata_t data; + int count = 0; + size_t thread_memsize = 1024 * 1024; memset (&data, 0, sizeof (data)); + data.hunk = Hunk_Init (Sys_Alloc (thread_memsize), thread_memsize); set_pool_init (&data.set_pool); + data.id = thread; + data.memsuper = new_memsuper (); + data.str = dstring_new (); do { - portal = GetNextPortal (); + portal = GetNextPortal (1); if (!portal) break; + if (options.verbosity >= 3 && (!count++ % 16)) { + dump_super_stats (thread, data.memsuper); + } if (working) working[thread] = (int) (portal - portals); PortalFlow (&data, portal); + int whm = data.stats.winding_mark; + int shw = data.stats.sep_highwater; + int smb = data.stats.sep_maxbulk; PortalCompleted (&data, portal); + data.stats.sep_highwater = shw; + data.stats.sep_maxbulk = smb; + data.stats.winding_mark = whm; - if (options.verbosity > 1) - printf ("portal:%5i mightsee:%5i cansee:%5i %5d/%d\n", + if (options.verbosity >= 4) + printf ("portal:%5i mightsee:%5i cansee:%5i %5u/%u\n", (int) (portal - portals), portal->nummightsee, portal->numcansee, portal_count, numportals * 2); } while (1); - if (options.verbosity > 0) + if (options.verbosity >= 2) { + printf ("thread %d winding mark: %zd\n", thread, + data.stats.winding_mark); + printf ("thread %d separator highwater: %d\n", thread, + data.stats.sep_highwater); + printf ("thread %d separator maxbulk: %d\n", thread, + data.stats.sep_maxbulk); + } + + if (options.verbosity >= 4) printf ("thread %d done\n", thread); if (working) working[thread] = -1; + delete_memsuper (data.memsuper); + dstring_delete (data.str); + + Sys_Free (data.hunk, thread_memsize); return NULL; } @@ -418,10 +623,11 @@ BaseVisThread (void *_thread) int num_mightsee = 0; memset (&data, 0, sizeof (data)); + data.id = thread; set_pool_init (&set_pool); data.portalsee = set_new_size_r (&set_pool, numportals * 2); do { - portal = GetNextPortal (); + portal = GetNextPortal (0); if (!portal) break; @@ -437,10 +643,19 @@ BaseVisThread (void *_thread) } while (1); WRLOCK (stats_lock); + base_selfcull += data.selfcull; + base_clustercull += data.clustercull; + base_clustertest += data.clustertest; + base_spherecull += data.spherecull; + base_spheretest += data.spheretest; + base_spherepass += data.spherepass; + base_windingcull += data.windingcull; + base_windingtest += data.windingtest; + base_windingpass += data.windingpass; base_mightsee += num_mightsee; UNLOCK (stats_lock); - if (options.verbosity > 0) + if (options.verbosity >= 4) printf ("thread %d done\n", thread); if (working) working[thread] = -1; @@ -458,18 +673,22 @@ print_thread_stats (const int *local_work, int thread, int spinner_ind) for (i = 0; i < thread; i++) printf ("%6d", local_work[i]); - printf (" %5d / %5d", portal_count, numportals * 2); + printf (" %5u / %5u", portal_count, numportals * 2); fflush (stdout); printf (" %c\r", spinner[spinner_ind % 4]); fflush (stdout); } static int -print_progress (int prev_prog, int spinner_ind) +portal_progress (void) { - int prog; + return portal_count * 100 / (numportals * 2) + 1; +} - prog = portal_count * 50 / (numportals * 2) + 1; +static int +print_progress (int prev_prog, int prog, int spinner_ind) +{ + prog /= 2; if (prog > prev_prog) printf ("%.*s", prog - prev_prog, progress + prev_prog); printf (" %c\b\b", spinner[spinner_ind % 4]); @@ -477,16 +696,22 @@ print_progress (int prev_prog, int spinner_ind) return prog; } +typedef struct { + int thread; + int (*calc_progress)(void); +} watch_data_t; + static void * -WatchThread (void *_thread) +WatchThread (void *_wd) { - int thread = (intptr_t) _thread; + watch_data_t *wd = _wd; + int thread = wd->thread; int *local_work = malloc (thread * sizeof (int)); int i; int spinner_ind = 0; int count = 0; int prev_prog = 0; - int prev_port = 0; + int prev_tick = 0; int stalled = 0; while (1) { @@ -496,50 +721,86 @@ WatchThread (void *_thread) break; if (i == thread) break; - if (count++ == 100) { + if (count++ == 50) { count = 0; for (i = 0; i < thread; i ++) local_work[i] = working[i]; - if (options.verbosity > 0) + if (options.verbosity >= 4) print_thread_stats (local_work, thread, spinner_ind); - else if (options.verbosity == 0) - prev_prog = print_progress (prev_prog, spinner_ind); - if (prev_port != portal_count || stalled++ == 10) { - prev_port = portal_count; + else if (options.verbosity >= 0) { + prev_prog = print_progress (prev_prog, wd->calc_progress (), + spinner_ind); + } + if (prev_tick != progress_tick || stalled++ == 20) { + prev_tick = progress_tick; stalled = 0; spinner_ind++; } } } - if (options.verbosity > 0) + if (options.verbosity >= 4) { printf ("watch thread done\n"); - else if (options.verbosity == 0) - printf ("\n"); + } else if (options.verbosity >= 0) { + prev_prog = print_progress (prev_prog, wd->calc_progress (), + spinner_ind); + } free (local_work); return NULL; } #endif +static double +thread_start (const char *msg) +{ + if (options.verbosity >= 0) { + printf ("%-10.10s: ", msg); + } + if (options.verbosity >= 4) { + printf ("\n"); + } + return Sys_DoubleTime (); +} + static void -RunThreads (void *(*thread_func) (void *)) +thread_end (double start) +{ + double end = Sys_DoubleTime (); + double duration = end - start; + + if (options.verbosity >= 0) { + if (duration >= 1.2) { + printf (" %5.1fs", duration); + } else { + printf (" %5.1fms", duration * 1000); + } + } + printf ("\n"); +} + +void +RunThreads (const char *heading, void *(*thread_func) (void *), + int (*calc_progress)(void)) { #ifdef USE_PTHREADS pthread_t *work_threads; void *status; int i; + double start; if (options.threads > 1) { work_threads = alloca ((options.threads + 1) * sizeof (pthread_t *)); working = calloc (options.threads, sizeof (int)); + start = thread_start (heading); for (i = 0; i < options.threads; i++) { if (pthread_create (&work_threads[i], &threads_attrib, thread_func, (void *) (intptr_t) i) == -1) Sys_Error ("pthread_create failed"); } + watch_data_t wd = { i, calc_progress }; if (pthread_create (&work_threads[i], &threads_attrib, - WatchThread, (void *) (intptr_t) i) == -1) + WatchThread, &wd) == -1) Sys_Error ("pthread_create failed"); for (i = 0; i < options.threads; i++) { @@ -548,24 +809,29 @@ RunThreads (void *(*thread_func) (void *)) } if (pthread_join (work_threads[i], &status) == -1) Sys_Error ("pthread_join failed"); + thread_end (start); free (working); } else { + start = thread_start (heading); thread_func (0); + thread_end (start); } #else - LeafThread (0); + thread_func (0); #endif } -static int -CompressRow (byte *vis, byte *dest) +int +CompressRow (sizebuf_t *dest, const byte *vis, unsigned num_leafs, int utf8) { int rep, visrow, j; byte *dest_p; + // if we ever need 2GB (32Gb) of vis data for a single leaf... + int maxrep = utf8 ? 0x7fffffff : 255; - dest_p = dest; - visrow = (numrealleafs + 7) >> 3; + dest_p = dest->data; + visrow = (num_leafs + 7) >> 3; for (j = 0; j < visrow; j++) { *dest_p++ = vis[j]; @@ -574,21 +840,27 @@ CompressRow (byte *vis, byte *dest) rep = 1; for (j++; j < visrow; j++) - if (vis[j] || rep == 255) + if (vis[j] || rep == maxrep) break; else rep++; - *dest_p++ = rep; + if (utf8) { + dest->cursize = dest_p - dest->data; + MSG_WriteUTF8 (dest, rep); + dest_p = dest->data + dest->cursize; + } else { + *dest_p++ = rep; + } j--; } - - return dest_p - dest; + dest->cursize = dest_p - dest->data; + return dest->cursize; } static void ClusterFlowExpand (const set_t *src, byte *dest) { - int i, j; + unsigned i, j; for (j = 1, i = 0; i < numrealleafs; i++) { if (set_is_member (src, leafcluster[i])) @@ -601,6 +873,8 @@ ClusterFlowExpand (const set_t *src, byte *dest) } } + +static sizebuf_t *compressed_vis; /* ClusterFlow @@ -609,8 +883,9 @@ ClusterFlowExpand (const set_t *src, byte *dest) void ClusterFlow (int clusternum) { - set_t *visclusters; - byte compressed[MAX_MAP_LEAFS / 8]; + set_t visclusters = SET_STATIC_INIT (portalclusters, alloca); + sizebuf_t *compressed = &compressed_vis[clusternum]; + byte *outbuffer; int numvis, i; cluster_t *cluster; @@ -619,37 +894,92 @@ ClusterFlow (int clusternum) outbuffer = uncompressed + clusternum * bitbytes_l; cluster = &clusters[clusternum]; - // flow through all portals, collecting visible bits + set_empty (&visclusters); - memset (compressed, 0, sizeof (compressed)); - visclusters = set_new (); + // flow through all portals, collecting visible bits for (i = 0; i < cluster->numportals; i++) { - portal = cluster->portals[i]; + portal = &cluster->portals[i]; if (portal->status != stat_done) Sys_Error ("portal not done"); - set_union (visclusters, portal->visbits); + if (set_is_member (portal->visbits, clusternum)) { + //printf ("cluster %d saw into self via portal %d\n", + // clusternum, (int) (portal - portals)); + } + set_union (&visclusters, portal->visbits); } - if (set_is_member (visclusters, clusternum)) - Sys_Error ("Cluster portals saw into cluster"); + set_add (&visclusters, clusternum); - set_add (visclusters, clusternum); - - numvis = set_size (visclusters); + numvis = set_count (&visclusters); // expand to cluster->leaf PVS - ClusterFlowExpand (visclusters, outbuffer); - - set_delete (visclusters); + ClusterFlowExpand (&visclusters, outbuffer); // compress the bit string - if (options.verbosity > 1) + if (options.verbosity >= 4) printf ("cluster %4i : %4i visible\n", clusternum, numvis); totalvis += numvis; - i = CompressRow (outbuffer, compressed); - cluster->visofs = visdata->size; - dstring_append (visdata, (char *) compressed, i); + CompressRow (compressed, outbuffer, numrealleafs, 0); +} + +static unsigned work_cluster; + +static int +flow_progress (void) +{ + return work_cluster * 100 / portalclusters; +} + +static unsigned +next_cluster (void) +{ + unsigned cluster = ~0; + WRLOCK (global_lock); + progress_tick++; + if (work_cluster < portalclusters) { + cluster = work_cluster++; + } + UNLOCK (global_lock); + return cluster; +} + +static void * +FlowClusters (void *d) +{ + int thread = (intptr_t) d; + + while (1) { + unsigned clusternum = next_cluster (); + + if (working) { + working[thread] = clusternum; + } + if (clusternum == ~0u) { + break; + } + ClusterFlow (clusternum); + } + return 0; +} + +static void +CompactPortalVis (void) +{ + compressed_vis = malloc (portalclusters * sizeof (sizebuf_t)); + for (unsigned i = 0; i < portalclusters; i++) { + compressed_vis[i] = (sizebuf_t) { + .maxsize = (bitbytes_l * 3) / 2, + .data = malloc ((bitbytes_l * 3) / 2) + }; + } + RunThreads ("Comp vis", FlowClusters, flow_progress); + + for (unsigned i = 0; i < portalclusters; i++) { + unsigned size = compressed_vis[i].cursize; + clusters[i].visofs = visdata->size; + dstring_append (visdata, (char *) compressed_vis[i].data, size); + } } static int @@ -665,27 +995,33 @@ BasePortalVis (void) { double start, end; - if (options.verbosity >= 0) - printf ("Base vis: "); - if (options.verbosity >= 1) - printf ("\n"); - start = Sys_DoubleTime (); - RunThreads (BaseVisThread); + RunThreads ("Base vis", BaseVisThread, portal_progress); end = Sys_DoubleTime (); - if (options.verbosity > 0) - printf ("base_mightsee: %d %gs\n", base_mightsee, end - start); + if (options.verbosity >= 1) { + unsigned long n = numportals; + printf ("base_mightsee: %lu %gs\n", base_mightsee, end - start); + printf ("\n"); + printf (" total tests: %lu\n", n * n * 4); + printf ("cluster test: %lu\n", base_clustertest); + printf ("cluster cull: %lu\n", base_clustercull); + printf (" self cull: %lu\n", base_selfcull); + printf (" sphere test: %lu\n", base_spheretest); + printf (" sphere cull: %lu\n", base_spherecull); + printf (" sphere pass: %lu\n", base_spherepass); + printf ("winding test: %lu\n", base_windingtest); + printf ("winding cull: %lu\n", base_windingcull); + printf ("winding pass: %lu\n", base_windingpass); + } } static void CalcPortalVis (void) { - long i; + unsigned i; double start, end; - portal_count = 0; - // fastvis just uses mightsee for a very loose bound if (options.minimal) { for (i = 0; i < numportals * 2; i++) { @@ -697,48 +1033,86 @@ CalcPortalVis (void) start = Sys_DoubleTime (); qsort (portal_queue, numportals * 2, sizeof (portal_t *), portalcmp); end = Sys_DoubleTime (); - if (options.verbosity > 0) + if (options.verbosity >= 1) printf ("qsort: %gs\n", end - start); - if (options.verbosity >= 0) - printf ("Full vis: "); - if (options.verbosity >= 1) - printf ("\n"); + portal_count = 0; - RunThreads (LeafThread); + RunThreads ("Full vis", LeafThread, portal_progress); - if (options.verbosity > 0) { - printf ("portalcheck: %i portaltest: %i portalpass: %i\n", - stats.portalcheck, stats.portaltest, stats.portalpass); - printf ("target trimmed: %d clipped: %d tested: %d\n", - stats.targettrimmed, stats.targetclipped, stats.targettested); - printf ("source trimmed: %d clipped: %d tested: %d\n", - stats.sourcetrimmed, stats.sourceclipped, stats.sourcetested); - printf ("vistest: %i mighttest: %i mightseeupdate: %i\n", - stats.vistest, stats.mighttest, stats.mightseeupdate); + if (options.verbosity >= 1) { + printf ("portalcheck: %ld portaltest: %ld portalpass: %ld\n", + global_stats.portalcheck, global_stats.portaltest, + global_stats.portalpass); + printf ("target trimmed: %ld clipped: %ld tested: %ld\n", + global_stats.targettrimmed, global_stats.targetclipped, + global_stats.targettested); + printf ("source trimmed: %ld clipped: %ld tested: %ld\n", + global_stats.sourcetrimmed, global_stats.sourceclipped, + global_stats.sourcetested); + printf ("vistest: %ld mighttest: %ld mightseeupdate: %ld\n", + global_stats.vistest, global_stats.mighttest, + global_stats.mightseeupdate); + if (options.verbosity >= 2) { + printf ("separators allocated: %u freed: %u %u\n", + global_stats.sep_alloc, global_stats.sep_free, + global_stats.sep_alloc - global_stats.sep_free); + printf ("max windings mark: %zd\n", global_stats.winding_mark); + printf ("stack blocks allocated: %u freed: %u %u\n", + global_stats.stack_alloc, global_stats.stack_free, + global_stats.stack_alloc - global_stats.stack_free); + } } } static void CalcVis (void) { - int i; + unsigned i; + printf ("Thread count: %d\n", options.threads); BasePortalVis (); + for (i = 0; i < 2 * numportals; i++) { + portals[i].status = stat_none; + } CalcPortalVis (); // assemble the leaf vis lists by oring and compressing the portal lists - for (i = 0; i < portalclusters; i++) - ClusterFlow (i); + CompactPortalVis (); for (i = 0; i < numrealleafs; i++) { bsp->leafs[i + 1].visofs = clusters[leafcluster[i]].visofs; } if (options.verbosity >= 0) - printf ("average clusters visible: %i\n", totalvis / portalclusters); + printf ("average clusters visible: %u\n", totalvis / portalclusters); } + +static void +CalcClusterSpheres (void) +{ + memhunk_t *hunk = main_thread.hunk; + + for (unsigned i = 0; i < portalclusters; i++) { + cluster_t *cluster = &clusters[i]; + int vertcount = 0; + for (int j = 0; j < cluster->numportals; j++) { + vertcount += cluster->portals[j].winding->numpoints; + } + size_t mark = Hunk_LowMark (hunk); + vec4f_t *verts = Hunk_RawAlloc (hunk, vertcount * sizeof (vec4f_t)); + vertcount = 0; + for (int j = 0; j < cluster->numportals; j++) { + memcpy (verts + vertcount, cluster->portals[j].winding->points, + cluster->portals[j].winding->numpoints * sizeof (vec4f_t)); + vertcount += cluster->portals[j].winding->numpoints; + } + cluster->sphere = SmallestEnclosingBall_vf (verts, vertcount); + Hunk_RawFreeToLowMark (hunk, mark); + } +} + #if 0 -static qboolean +static bool PlaneCompare (plane_t *p1, plane_t *p2) { int i; @@ -761,7 +1135,7 @@ FindPassages (winding_t *source, winding_t *pass) int i, j, k, l; int counts[3]; plane_t plane; - qboolean fliptest; + bool fliptest; sep_t *sep, *list; vec3_t v1, v2; @@ -912,20 +1286,58 @@ CalcPassages (void) } } #endif + +static winding_t * +parse_winding (const char *line, int numpoints) +{ + winding_t *winding = NewWinding (&main_thread, numpoints); + + winding->original = true; + winding->numpoints = numpoints; + + for (int i = 0; i < numpoints; i++) { + // (%ld %ld %ld) + while (isspace ((byte) *line)) + line++; + if (*line++ != '(') { + return 0; + } + + for (int j = 0; j < 3; j++) { + char *err; + + winding->points[i][j] = strtod (line, &err); + if (err == line) { + return 0; + } + line = err; + } + winding->points[i][3] = 1; + + while (isspace ((byte) *line)) + line++; + if (*line++ != ')') { + return 0; + } + } + return winding; +} + static void LoadPortals (char *name) { + typedef struct { + unsigned clusters[2]; + } clusterpair_t; + const char *line; char *err; - int numpoints, i, j, k; + unsigned numpoints; int read_leafs = 0; - int clusternums[2]; - cluster_t *cluster; - plane_t plane; - portal_t *portal; - winding_t *winding; - sphere_t sphere; + vec4f_t plane; + vspheref_t sphere; QFile *f; + clusterpair_t *clusternums; if (!strcmp (name, "-")) f = Qdopen (0, "rt"); // create a QFile of stdin @@ -943,34 +1355,34 @@ LoadPortals (char *name) if (line && (!strcmp (line, PORTALFILE "\n") || !strcmp (line, PORTALFILE "\r\n"))) { line = Qgetline (f); - if (!line || sscanf (line, "%i\n", &portalclusters) != 1) + if (!line || sscanf (line, "%u\n", &portalclusters) != 1) Sys_Error ("LoadPortals: failed to read header"); line = Qgetline (f); - if (!line || sscanf (line, "%i\n", &numportals) != 1) + if (!line || sscanf (line, "%u\n", &numportals) != 1) Sys_Error ("LoadPortals: failed to read header"); numrealleafs = portalclusters; } else if (line && (!strcmp (line, PORTALFILE_AM "\n") || !strcmp (line, PORTALFILE_AM "\r\n"))) { line = Qgetline (f); - if (!line || sscanf (line, "%i\n", &portalclusters) != 1) + if (!line || sscanf (line, "%u\n", &portalclusters) != 1) Sys_Error ("LoadPortals: failed to read header"); line = Qgetline (f); - if (!line || sscanf (line, "%i\n", &numportals) != 1) + if (!line || sscanf (line, "%u\n", &numportals) != 1) Sys_Error ("LoadPortals: failed to read header"); line = Qgetline (f); - if (!line || sscanf (line, "%i\n", &numrealleafs) != 1) + if (!line || sscanf (line, "%u\n", &numrealleafs) != 1) Sys_Error ("LoadPortals: failed to read header"); read_leafs = 1; } else if (line && (!strcmp (line, PORTALFILE2 "\n") || !strcmp (line, PORTALFILE2 "\r\n"))) { line = Qgetline (f); - if (!line || sscanf (line, "%i\n", &numrealleafs) != 1) + if (!line || sscanf (line, "%u\n", &numrealleafs) != 1) Sys_Error ("LoadPortals: failed to read header"); line = Qgetline (f); - if (!line || sscanf (line, "%i\n", &portalclusters) != 1) + if (!line || sscanf (line, "%u\n", &portalclusters) != 1) Sys_Error ("LoadPortals: failed to read header"); line = Qgetline (f); - if (!line || sscanf (line, "%i\n", &numportals) != 1) + if (!line || sscanf (line, "%u\n", &numportals) != 1) Sys_Error ("LoadPortals: failed to read header"); read_leafs = 1; } else { @@ -978,9 +1390,9 @@ LoadPortals (char *name) } if (options.verbosity >= 0) { - printf ("%4i portalclusters\n", portalclusters); - printf ("%4i numportals\n", numportals); - printf ("%4i numrealleafs\n", numrealleafs); + printf ("%4u portalclusters\n", portalclusters); + printf ("%4u numportals\n", numportals); + printf ("%4u numrealleafs\n", numrealleafs); } bitbytes = ((portalclusters + 63) & ~63) >> 3; @@ -992,126 +1404,167 @@ LoadPortals (char *name) // direction portals = calloc (2 * numportals, sizeof (portal_t)); portal_queue = malloc (2 * numportals * sizeof (portal_t *)); - for (i = 0; i < 2 * numportals; i++) { + for (unsigned i = 0; i < 2 * numportals; i++) { portal_queue[i] = &portals[i]; } #ifdef USE_PTHREADS portal_locks = calloc (2 * numportals, sizeof (pthread_rwlock_t)); - for (i = 0; i < 2 * numportals; i++) { + for (unsigned i = 0; i < 2 * numportals; i++) { if (pthread_rwlock_init (&portal_locks[i], 0)) Sys_Error ("pthread_rwlock_init failed"); } #endif - clusters = calloc (portalclusters, sizeof (cluster_t)); + clusters = calloc (portalclusters, sizeof (cluster_t)); originalvismapsize = numrealleafs * ((numrealleafs + 7) / 8); - for (i = 0, portal = portals; i < numportals; i++) { + clusternums = calloc (numportals, sizeof (clusterpair_t)); + winding_t **windings = malloc (numportals * sizeof (winding_t *)); + for (unsigned i = 0; i < numportals; i++) { line = Qgetline (f); if (!line) - Sys_Error ("LoadPortals: reading portal %i", i); + Sys_Error ("LoadPortals: reading portal %u", i); numpoints = strtol (line, &err, 10); if (err == line) - Sys_Error ("LoadPortals: reading portal %i", i); + Sys_Error ("LoadPortals: reading portal %u", i); line = err; - for (j = 0; j < 2; j++) { - clusternums[j] = strtol (line, &err, 10); + + for (int j = 0; j < 2; j++) { + clusternums[i].clusters[j] = strtoul (line, &err, 10); if (err == line) - Sys_Error ("LoadPortals: reading portal %i", i); + Sys_Error ("LoadPortals: reading portal %u", i); line = err; + + if (clusternums[i].clusters[j] > portalclusters) + Sys_Error ("LoadPortals: reading portal %u", i); } + clusters[clusternums[i].clusters[0]].numportals++; + clusters[clusternums[i].clusters[1]].numportals++; - if (numpoints > MAX_POINTS_ON_WINDING) - Sys_Error ("LoadPortals: portal %i has too many points", i); - if ((unsigned) clusternums[0] > (unsigned) portalclusters - || (unsigned) clusternums[1] > (unsigned) portalclusters) - Sys_Error ("LoadPortals: reading portal %i", i); - - winding = portal->winding = NewWinding (numpoints); - winding->original = true; - winding->numpoints = numpoints; - - for (j = 0; j < numpoints; j++) { - // (%ld %ld %ld) - while (isspace ((byte) *line)) - line++; - if (*line++ != '(') - Sys_Error ("LoadPortals: reading portal %i", i); - - for (k = 0; k < 3; k++) { - winding->points[j][k] = strtod (line, &err); - if (err == line) - Sys_Error ("LoadPortals: reading portal %i", i); - line = err; - } - - while (isspace ((byte) *line)) - line++; - if (*line++ != ')') - Sys_Error ("LoadPortals: reading portal %i", i); + if (!(windings[i] = parse_winding (line, numpoints))) { + Sys_Error ("LoadPortals: reading portal %u", i); } + } + + clusters[0].portals = portals; + for (unsigned i = 1; i < portalclusters; i++) { + portal_t *portal = clusters[i - 1].portals; + clusters[i].portals = portal + clusters[i - 1].numportals; + clusters[i - 1].numportals = 0; + } + clusters[portalclusters - 1].numportals = 0; + + for (unsigned i = 0; i < numportals; i++) { + winding_t *winding = windings[i]; + cluster_t *cluster; + portal_t *portal; + + sphere = SmallestEnclosingBall_vf(winding->points, winding->numpoints); + //printf (VEC4F_FMT" %.9g\n", VEC4_EXP (sphere.center), sphere.radius); // calc plane - PlaneFromWinding (winding, &plane); - sphere = SmallestEnclosingBall((const vec_t(*)[3])winding->points, - winding->numpoints); + plane = PlaneFromWinding (winding); // create forward portal - cluster = &clusters[clusternums[0]]; - if (cluster->numportals == MAX_PORTALS_ON_CLUSTER) - Sys_Error ("Cluster with too many portals"); - cluster->portals[cluster->numportals] = portal; - cluster->numportals++; - - portal->winding = winding; - VectorNegate (plane.normal, portal->plane.normal); - portal->plane.dist = -plane.dist; // plane is for CW, portal is CCW - portal->cluster = clusternums[1]; + cluster = &clusters[clusternums[i].clusters[0]]; + portal = &cluster->portals[cluster->numportals++]; + portal->plane = -plane; // plane is for CW, portal is CCW portal->sphere = sphere; - portal++; - - // create backwards portal - cluster = &clusters[clusternums[1]]; - if (cluster->numportals == MAX_PORTALS_ON_CLUSTER) - Sys_Error ("Cluster with too many portals"); - cluster->portals[cluster->numportals] = portal; - cluster->numportals++; + portal->cluster = clusternums[i].clusters[1]; + portal->winding = winding; // Use a flipped winding for the reverse portal so the winding // direction and plane normal match. - portal->winding = NewFlippedWinding (winding); - portal->winding->original = true; - portal->plane = plane; - portal->cluster = clusternums[0]; - portal->sphere = sphere; - portal++; - } + winding = NewFlippedWinding (&main_thread, winding); // ** + winding->original = true; - leafcluster = calloc (numrealleafs, sizeof (int)); + // create backwards portal + cluster = &clusters[clusternums[i].clusters[1]]; + portal = &cluster->portals[cluster->numportals++]; + portal->plane = plane; + portal->sphere = sphere; + portal->cluster = clusternums[i].clusters[0]; + portal->winding = winding; + } + free (clusternums); + free (windings); + + leafcluster = calloc (numrealleafs, sizeof (uint32_t)); if (read_leafs) { - for (i = 0; i < numrealleafs; i++) { + for (unsigned i = 0; i < numrealleafs; i++) { line = Qgetline (f); if (sscanf (line, "%i\n", &leafcluster[i]) != 1) Sys_Error ("LoadPortals: parse error in leaf->cluster " "mappings"); } } else { - for (i = 0; i < numrealleafs; i++) + for (unsigned i = 0; i < numrealleafs; i++) leafcluster[i] = i; } Qclose (f); } +static void +generate_pvs (void) +{ + QFile *f; + + dstring_t *portalfile = dstring_new (); + dstring_t *backupfile = dstring_new (); + + visdata = dstring_new (); + + dstring_copystr (portalfile, options.bspfile->str); + QFS_SetExtension (portalfile, ".prt"); + LoadPortals (portalfile->str); + + uncompressed = calloc (bitbytes_l, portalclusters); + + CalcClusterSpheres (); + CalcVis (); + + if (options.verbosity >= 1) + printf ("chains: %ld%s\n", global_stats.chains, + options.threads > 1 ? " (not reliable)" :""); + + BSP_AddVisibility (bsp, (byte *) visdata->str, visdata->size); + if (options.verbosity >= 0) + printf ("visdatasize:%ld compressed from %ld\n", + (long) bsp->visdatasize, (long) originalvismapsize); + + CalcAmbientSounds (); + + dstring_copystr (backupfile, options.bspfile->str); + QFS_SetExtension (backupfile, ".bsp~"); + + Qrename (options.bspfile->str, backupfile->str); + f = Qopen (options.bspfile->str, "wb"); + if (!f) + Sys_Error ("couldn't open %s for writing.", options.bspfile->str); + WriteBSPFile (bsp, f); + Qclose (f); + + dstring_delete (backupfile); + dstring_delete (portalfile); + dstring_delete (visdata); + dstring_delete (options.bspfile); + free (leafcluster); + free (uncompressed); + free (portals); + free (clusters); +} + int main (int argc, char **argv) { double start, stop; - dstring_t *portalfile = dstring_new (); QFile *f; + size_t main_memsize = 128 * 1024 * 1024; - InitThreads (); + main_thread.memsuper = new_memsuper (); + main_thread.hunk = Hunk_Init (Sys_Alloc (main_memsize), main_memsize); start = Sys_DoubleTime (); @@ -1119,6 +1572,8 @@ main (int argc, char **argv) DecodeArgs (argc, argv); + InitThreads (); + if (!options.bspfile) { usage (1); Sys_Error ("%s: no bsp file specified.", this_program); @@ -1132,48 +1587,21 @@ main (int argc, char **argv) bsp = LoadBSPFile (f, Qfilesize (f)); Qclose (f); - visdata = dstring_new (); + if (!options.no_auto_pvs) { + generate_pvs (); + } + if (options.fat_pvs) { + CalcFatPVS (); + } - dstring_copystr (portalfile, options.bspfile->str); - QFS_SetExtension (portalfile, ".prt"); - LoadPortals (portalfile->str); + BSP_Free (bsp); - uncompressed = calloc (bitbytes_l, portalclusters); - - CalcVis (); - - if (options.verbosity > 0) - printf ("chains: %i%s\n", stats.chains, - options.threads > 1 ? " (not reliable)" :""); - - BSP_AddVisibility (bsp, (byte *) visdata->str, visdata->size); - if (options.verbosity >= 0) - printf ("visdatasize:%ld compressed from %ld\n", - (long) bsp->visdatasize, (long) originalvismapsize); - - CalcAmbientSounds (); - - f = Qopen (options.bspfile->str, "wb"); - if (!f) - Sys_Error ("couldn't open %s for writing.", options.bspfile->str); - WriteBSPFile (bsp, f); - Qclose (f); + EndThreads (); stop = Sys_DoubleTime (); if (options.verbosity >= -1) printf ("%5.1f seconds elapsed\n", stop - start); - dstring_delete (portalfile); - dstring_delete (visdata); - dstring_delete (options.bspfile); - BSP_Free (bsp); - free (leafcluster); - free (uncompressed); - free (portals); - free (clusters); - - EndThreads (); - return 0; } diff --git a/tools/qfvis/source/soundphs.c b/tools/qfvis/source/soundphs.c index 086e453cc..52686ffda 100644 --- a/tools/qfvis/source/soundphs.c +++ b/tools/qfvis/source/soundphs.c @@ -54,7 +54,8 @@ #include "QF/quakefs.h" #include "QF/sys.h" -#include "vis.h" +#include "tools/qfvis/include/options.h" +#include "tools/qfvis/include/vis.h" /* Some textures (sky, water, slime, lava) are considered ambient sound @@ -64,24 +65,25 @@ leaf. */ +static byte *surf_ambients; + static void SurfaceBBox (dface_t *s, vec3_t mins, vec3_t maxs) { - int vi, e, i, j; - float *v; - mins[0] = mins[1] = mins[2] = 999999; maxs[0] = maxs[1] = maxs[2] = -99999; - for (i = 0; i < s->numedges; i++) { - e = bsp->surfedges[s->firstedge + i]; + for (uint32_t i = 0; i < s->numedges; i++) { + int e = bsp->surfedges[s->firstedge + i]; + int vi; + if (e >= 0) vi = bsp->edges[e].v[0]; else vi = bsp->edges[-e].v[1]; - v = bsp->vertexes[vi].point; + float *v = bsp->vertexes[vi].point; - for (j = 0; j < 3; j++) { + for (int j = 0; j < 3; j++) { if (v[j] < mins[j]) mins[j] = v[j]; if (v[j] > maxs[j]) @@ -90,87 +92,153 @@ SurfaceBBox (dface_t *s, vec3_t mins, vec3_t maxs) } } +static void +init_surf_ambients (void) +{ + surf_ambients = malloc (bsp->numfaces); + if (!bsp->texdata) { + memset (surf_ambients, -1, bsp->numfaces); + return; + } + + dmiptexlump_t *miptex = (dmiptexlump_t *) bsp->texdata; + + for (size_t i = 0; i < bsp->numfaces; i++) { + dface_t *surf = &bsp->faces[i]; + texinfo_t *info = &bsp->texinfo[surf->texinfo]; + int ofs = miptex->dataofs[info->miptex]; + miptex_t *miptex = (miptex_t *) &bsp->texdata[ofs]; + + if (!strncasecmp (miptex->name, "*water", 6)) + surf_ambients[i] = AMBIENT_WATER; + else if (!strncasecmp (miptex->name, "sky", 3)) + surf_ambients[i] = AMBIENT_SKY; + else if (!strncasecmp (miptex->name, "*slime", 6)) + surf_ambients[i] = AMBIENT_WATER; + else if (!strncasecmp (miptex->name, "*lava", 6)) + surf_ambients[i] = AMBIENT_LAVA; + else if (!strncasecmp (miptex->name, "*04water", 8)) + surf_ambients[i] = AMBIENT_WATER; + else + surf_ambients[i] = -1; + } +} + +static void +LeafAmbients (dleaf_t *leaf, set_t *vis) +{ + float dists[NUM_AMBIENTS]; + + // clear ambients + for (int j = 0; j < NUM_AMBIENTS; j++) { + dists[j] = 1020; + } + + for (unsigned j = 0; j < numrealleafs - 1; j++) { + if (!set_is_member (vis, j)) { + continue; + } + + if (!bsp->texdata) + continue; + + // check this leaf for sound textures + dleaf_t *hit = &bsp->leafs[j + 1]; + + for (uint32_t k = 0; k < hit->nummarksurfaces; k++) { + unsigned surf_index = bsp->marksurfaces[hit->firstmarksurface + k]; + dface_t *surf = &bsp->faces[surf_index]; + + byte ambient_type; + if ((ambient_type = surf_ambients[surf_index]) == 0xff) { + continue; + } + + // find distance from source leaf to polygon + vec3_t mins, maxs; + float maxd = 0; + SurfaceBBox (surf, mins, maxs); + for (int l = 0; l < 3; l++) { + float d = 0; + if (mins[l] > leaf->maxs[l]) + d = mins[l] - leaf->maxs[l]; + else if (maxs[l] < leaf->mins[l]) + d = leaf->mins[l] - mins[l]; + else + d = 0; + if (d > maxd) + maxd = d; + } + + maxd = 0.25; + if (maxd < dists[ambient_type]) + dists[ambient_type] = maxd; + } + } + + for (int j = 0; j < NUM_AMBIENTS; j++) { + float vol = 0; + + if (dists[j] < 100) + vol = 1.0; + else { + vol = 1.0 - dists[2] * 0.002; + if (vol < 0) + vol = 0; + } + leaf->ambient_level[j] = vol * 255; + } +} + +// vis is 1-based, but bsp leafs are 0-based +static unsigned work_leaf = 1; + +static int +ambient_progress (void) +{ + return (work_leaf - 1) * 100 / (numrealleafs - 1); +} + +static unsigned +next_leaf (void) +{ + unsigned leaf = ~0; + WRLOCK (global_lock); + progress_tick++; + if (work_leaf < numrealleafs) { + leaf = work_leaf++; + } + UNLOCK (global_lock); + return leaf; +} + +static void * +AmbientThread (void *d) +{ + int thread = (intptr_t) d; + set_t vis = {}; + + vis.size = SET_SIZE (numrealleafs - 1); + + while (1) { + unsigned leaf_num = next_leaf (); + + if (working) + working[thread] = leaf_num; + if (leaf_num == ~0u) { + break; + } + + unsigned cluster_num = leafcluster[leaf_num]; + vis.map = (set_bits_t *) &uncompressed[cluster_num * bitbytes_l]; + LeafAmbients (&bsp->leafs[leaf_num], &vis); + } + return 0; +} + void CalcAmbientSounds (void) { - byte *vis; - int ambient_type, ofs, i, j, l; - unsigned k; - float maxd, vol, d; - float dists[NUM_AMBIENTS]; - dface_t *surf; - dleaf_t *leaf, *hit; - vec3_t mins, maxs; - texinfo_t *info; - miptex_t *miptex; - - for (i = 0; i < numrealleafs - 1; i++) { - leaf = &bsp->leafs[i + 1]; - - // clear ambients - for (j = 0; j < NUM_AMBIENTS; j++) - dists[j] = 1020; - - vis = &uncompressed[leafcluster[i] * bitbytes_l]; - - for (j = 0; j < numrealleafs - 1; j++) { - if (!(vis[j >> 3] & (1 << (j & 7)))) - continue; - - if (!bsp->texdata) - continue; - - // check this leaf for sound textures - hit = &bsp->leafs[j + 1]; - - for (k = 0; k < hit->nummarksurfaces; k++) { - surf = &bsp->faces[bsp->marksurfaces[hit->firstmarksurface + k]]; - info = &bsp->texinfo[surf->texinfo]; - ofs = ((dmiptexlump_t *) bsp->texdata)->dataofs[info->miptex]; - miptex = (miptex_t *) (&bsp->texdata[ofs]); - - if (!strncasecmp (miptex->name, "*water", 6)) - ambient_type = AMBIENT_WATER; - else if (!strncasecmp (miptex->name, "sky", 3)) - ambient_type = AMBIENT_SKY; - else if (!strncasecmp (miptex->name, "*slime", 6)) - ambient_type = AMBIENT_WATER; - else if (!strncasecmp (miptex->name, "*lava", 6)) - ambient_type = AMBIENT_LAVA; - else if (!strncasecmp (miptex->name, "*04water", 8)) - ambient_type = AMBIENT_WATER; - else - continue; - - // find distance from source leaf to polygon - SurfaceBBox (surf, mins, maxs); - maxd = 0; - for (l = 0; l < 3; l++) { - if (mins[l] > leaf->maxs[l]) - d = mins[l] - leaf->maxs[l]; - else if (maxs[l] < leaf->mins[l]) - d = leaf->mins[l] - mins[l]; - else - d = 0; - if (d > maxd) - maxd = d; - } - - maxd = 0.25; - if (maxd < dists[ambient_type]) - dists[ambient_type] = maxd; - } - } - - for (j = 0; j < NUM_AMBIENTS; j++) { - if (dists[j] < 100) - vol = 1.0; - else { - vol = 1.0 - dists[2] * 0.002; - if (vol < 0) - vol = 0; - } - leaf->ambient_level[j] = vol * 255; - } - } + init_surf_ambients (); + RunThreads ("Ambients", AmbientThread, ambient_progress); } diff --git a/tools/qwaq/Makefile.am b/tools/qwaq/Makefile.am deleted file mode 100644 index dd34fd9c5..000000000 --- a/tools/qwaq/Makefile.am +++ /dev/null @@ -1,50 +0,0 @@ -QWAQ_LIBS=@QWAQ_LIBS@ -QWAQ_DEPS=@QWAQ_DEPS@ -QWAQ_INCS=@QWAQ_INCS@ - -AM_CPPFLAGS= -I$(top_srcdir)/include $(QWAQ_INCS) - -QFCC_DEP=$(top_builddir)/tools/qfcc/source/qfcc$(EXEEXT) -QFCC=$(top_builddir)/tools/qfcc/source/qfcc -QCFLAGS=-qq -O -g -Werror --advanced --no-default-paths - -noinst_PROGRAMS=qwaq @QWAQ_TARGETS@ -noinst_DATA=qwaq.dat - -qwaq_dat_src= \ - defs.qc main.qc test.r types.r - -qwaq_SOURCES= main.c builtins.c -qwaq_LDADD= $(QWAQ_LIBS) -qwaq_DEPENDENCIES= $(QWAQ_DEPS) - -cl_plugin_libs= \ - @client_static_plugin_libs@ - -client_libs= \ - $(top_builddir)/libs/console/libQFconsole.la \ - $(top_builddir)/libs/video/targets/libQFjs.la \ - $(top_builddir)/libs/audio/libQFcd.la \ - $(top_builddir)/libs/audio/libQFsound.la \ - $(top_builddir)/libs/image/libQFimage.la - -qwaq_x11_libs= \ - $(cl_plugin_libs) \ - $(top_builddir)/libs/video/renderer/libQFrenderer.la \ - $(top_builddir)/libs/models/libQFmodels.la \ - $(top_builddir)/libs/video/targets/libQFx11.la \ - $(client_libs) \ - $(top_builddir)/libs/gib/libQFgib.la -qwaq_x11_SOURCES=qwaq.c qwaq-bi.c -qwaq_x11_LDADD= $(qwaq_x11_libs) $(QWAQ_LIBS) \ - $(VIDMODE_LIBS) $(DGA_LIBS) $(X_LIBS) -lX11 \ - $(X_EXTRA_LIBS) $(X_SHM_LIB) $(DL_LIBS) -qwaq_x11_LDFLAGS= -qwaq_x11_DEPENDENCIES= $(qwaq_x11_libs) $(QWAQ_DEPS) - -qwaq.dat: progs.src $(qwaq_dat_src) $(QFCC_DEP) $(top_srcdir)/ruamoko/lib/Object.r - $(QFCC) $(QCFLAGS) -I$(top_srcdir)/ruamoko/include - -EXTRA_PROGRAMS=qwaq-x11 -EXTRA_DIST=$(qwaq_dat_src) qwaq.h -CLEANFILES= *.dat *.sym diff --git a/tools/qwaq/builtins.c b/tools/qwaq/builtins.c deleted file mode 100644 index 3976dc330..000000000 --- a/tools/qwaq/builtins.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - #FILENAME# - - #DESCRIPTION# - - Copyright (C) 2001 #AUTHOR# - - Author: #AUTHOR# - Date: #DATE# - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "qwaq.h" - -static void -bi_print (progs_t *pr) -{ - const char *str; - - str = P_GSTRING (pr, 0); - fprintf (stdout, "%s", str); -} - -static void -bi_errno (progs_t *pr) -{ - R_INT (pr) = errno; -} - -static void -bi_strerror (progs_t *pr) -{ - int err = P_INT (pr, 0); - RETURN_STRING (pr, strerror (err)); -} - -static void -bi_open (progs_t *pr) -{ - const char *path = P_GSTRING (pr, 0); - int flags = P_INT (pr, 1); - int mode = P_INT (pr, 2); - R_INT (pr) = open (path, flags, mode); -} - -static void -bi_close (progs_t *pr) -{ - int handle = P_INT (pr, 0); - R_INT (pr) = close (handle); -} - -static void -bi_read (progs_t *pr) -{ - int handle = P_INT (pr, 0); - int count = P_INT (pr, 1); - int *read_result = (int *)P_GPOINTER (pr, 2); - int res; - char *buffer; - - buffer = Hunk_TempAlloc (count); - if (!buffer) - PR_Error (pr, "%s: couldn't allocate %d bytes", "bi_read", count); - res = read (handle, buffer, count); - if (res != -1) - RETURN_STRING (pr, buffer); - *read_result = res; -} - -static void -bi_write (progs_t *pr) -{ - int handle = P_INT (pr, 0); - const char *buffer = P_GSTRING (pr, 1); - int count = P_INT (pr, 2); - - R_INT (pr) = write (handle, buffer, count); -} - -static void -bi_seek (progs_t *pr) -{ - int handle = P_INT (pr, 0); - int pos = P_INT (pr, 1); - int whence = P_INT (pr, 2); - - R_INT (pr) = lseek (handle, pos, whence); -} - -/* -static void -bi_traceon (progs_t *pr) -{ - pr->pr_trace = true; - pr->pr_trace_depth = pr->pr_depth; -} - -static void -bi_traceoff (progs_t *pr) -{ - pr->pr_trace = false; -} -*/ -static void -bi_printf (progs_t *pr) -{ - const char *fmt = P_GSTRING (pr, 0); - int count = pr->pr_argc - 1; - pr_type_t **args = pr->pr_params + 1; - dstring_t *dstr = dstring_new (); - - PR_Sprintf (pr, dstr, "bi_printf", fmt, count, args); - if (dstr->str) - fputs (dstr->str, stdout); - dstring_delete (dstr); -} - -static builtin_t builtins[] = { - {"print", bi_print, -1}, - {"errno", bi_errno, -1}, - {"strerror", bi_strerror, -1}, - {"open", bi_open, -1}, - {"close", bi_close, -1}, - {"read", bi_read, -1}, - {"write", bi_write, -1}, - {"seek", bi_seek, -1}, -// {"traceon", bi_traceon, -1}, -// {"traceoff", bi_traceoff, -1}, - {"printf", bi_printf, -1}, - {0} -}; - -void -BI_Init (progs_t *pr) -{ - PR_RegisterBuiltins (pr, builtins); -} diff --git a/tools/qwaq/defs.qc b/tools/qwaq/defs.qc deleted file mode 100644 index e872fb799..000000000 --- a/tools/qwaq/defs.qc +++ /dev/null @@ -1,22 +0,0 @@ -void (string str) print = #0; -int () errno = #0; -string (int err) strerror = #0; -int (...) open = #0; // string path, float flags[, float mode] -int (int handle) close = #0; -string read (int handle, int count, int *result) = #0; -int (int handle, string buffer, int count) write = #0; -int (int handle, int pos, int whence) seek = #0; - -//void() traceon = #0; // turns statment trace on -//void() traceoff = #0; - -void (...) printf = #0; - - -float time; -entity self; - -.float nextthink; -.void() think; -.float frame; -.vector origin; diff --git a/tools/qwaq/main.c b/tools/qwaq/main.c deleted file mode 100644 index edb7c0229..000000000 --- a/tools/qwaq/main.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - #FILENAME# - - #DESCRIPTION# - - Copyright (C) 2001 #AUTHOR# - - Author: #AUTHOR# - Date: #DATE# - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include - -#include -#include -#include -#include -#include "QF/ruamoko.h" -#include -#include "QF/va.h" -#include - -#include "qwaq.h" - -#define MAX_EDICTS 1024 - -static edict_t *edicts; -static int num_edicts; -static int reserved_edicts; -static progs_t pr; - -static QFile * -open_file (const char *path, int *len) -{ - QFile *file = Qopen (path, "rbz"); - - if (!file) { - perror (path); - return 0; - } - *len = Qfilesize (file); - return file; -} - -static void * -load_file (progs_t *pr, const char *name) -{ - QFile *file; - int size; - char *sym; - - file = open_file (name, &size); - if (!file) { - file = open_file (va ("%s.gz", name), &size); - if (!file) { - return 0; - } - } - sym = malloc (size + 1); - sym[size] = 0; - Qread (file, sym, size); - return sym; -} - -static void * -allocate_progs_mem (progs_t *pr, int size) -{ - return malloc (size); -} - -static void -free_progs_mem (progs_t *pr, void *mem) -{ - free (mem); -} - -static void -init_qf (void) -{ - Sys_Init (); - //Cvar_Get ("developer", "128", 0, 0, 0); - - Memory_Init (malloc (1024 * 1024), 1024 * 1024); - - Cvar_Get ("pr_debug", "2", 0, 0, 0); - Cvar_Get ("pr_boundscheck", "0", 0, 0, 0); - - pr.edicts = &edicts; - pr.num_edicts = &num_edicts; - pr.reserved_edicts = &reserved_edicts; - pr.load_file = load_file; - pr.allocate_progs_mem = allocate_progs_mem; - pr.free_progs_mem = free_progs_mem; - pr.no_exec_limit = 1; - - PR_Init_Cvars (); - PR_Init (); - RUA_Init (&pr, 0); - PR_Cmds_Init(&pr); - BI_Init (&pr); -} - -static int -load_progs (const char *name) -{ - QFile *file; - int size; - - file = open_file (name, &size); - if (!file) { - return 0; - } - pr.progs_name = name; - PR_LoadProgsFile (&pr, file, size, 1, 1024 * 1024); - Qclose (file); - if (!PR_RunLoadFuncs (&pr)) - PR_Error (&pr, "unable to load %s", pr.progs_name); - return 1; -} - -int -main (int argc, char **argv) -{ - dfunction_t *dfunc; - func_t main_func = 0; - const char *name = "qwaq.dat"; - string_t *pr_argv; - int pr_argc = 1, i; - - init_qf (); - - if (argc > 1) - name = argv[1]; - - if (!load_progs (name)) - Sys_Error ("couldn't load %s", "qwaq.dat"); - - PR_PushFrame (&pr); - if (argc > 2) - pr_argc = argc - 1; - pr_argv = PR_Zone_Malloc (&pr, (pr_argc + 1) * 4); - pr_argv[0] = PR_SetTempString (&pr, name); - for (i = 1; i < pr_argc; i++) - pr_argv[i] = PR_SetTempString (&pr, argv[1 + i]); - pr_argv[i] = 0; - - if ((dfunc = PR_FindFunction (&pr, ".main")) - || (dfunc = PR_FindFunction (&pr, "main"))) - main_func = dfunc - pr.pr_functions; - else - PR_Undefined (&pr, "function", "main"); - PR_RESET_PARAMS (&pr); - P_INT (&pr, 0) = pr_argc; - P_POINTER (&pr, 1) = PR_SetPointer (&pr, pr_argv); - PR_ExecuteProgram (&pr, main_func); - PR_PopFrame (&pr); - return R_INT (&pr); -} diff --git a/tools/qwaq/main.qc b/tools/qwaq/main.qc deleted file mode 100644 index 1014c1f4d..000000000 --- a/tools/qwaq/main.qc +++ /dev/null @@ -1,113 +0,0 @@ -string read_file (string filename) -{ - local QFile file; - local string data = nil, d; - file = Qopen (filename, "rtz"); - if (!file) { - printf ("Can't open %s for reading." - " Probably a bad hardcoded filename:)\n", filename); - return nil; - } - while ((d = Qgetline (file))) - data = data + d; - //FIXME can't read to a string, can't convert a pointer to a string ... - //Qread (file, data, 1023); - Qclose (file); - d = str_new (); - str_copy (d, data); - return d; -} - -void test_plist (void) -{ - local string data, l; - local plitem_t pl, item, i; - - data = read_file ("/home/bill/.quakeforge/gamedir.conf"); - pl = PL_GetPropertyList ("{" + data + "}"); - l = PL_WritePropertyList (pl); - printf ("%s", data); - printf ("%s", l); - i = PL_ObjectForKey (pl, "QF"); - item = PL_RemoveObjectForKey (pl, "QF"); - l = PL_WritePropertyList (item); - printf ("%s", l); - PL_Free (item); - //Because i and item both point to the same plitem, and item has been, - //freed, freeing i will generate an error - //l = PL_WritePropertyList (i); - PL_Free (pl); - str_free (data); -} - -void test_script (void) -{ - local script_t script; - local string token; - local string data; - - data = read_file ("progs.src"); - script = Script_New (); - token = Script_Start (script, "progs.src", data); - while (Script_GetToken (script, 1)) { - printf ("%s\n", token); - } - Script_Delete (script); - str_free (data); -} - -void () test_str = -{ - local string a,b,c,d; - a = "testing "; - b = "temp "; - c = "strings "; - d = "\n"; - print (a + b + c + d); - printf ("%i \"%.5s\" %3.4f %v\n", 14, "hi there", 3.1415926, '4 1 3'); -}; -void (...) dprint = #0; -int main (int argc, string *argv) -{ - local int i; - local SEL sel; - dprint ("foo", "bar\n"); - for (i = 0; i < argc; i++) { - print (argv[i]); - print ("\n"); - } - local id foo = [[Foo alloc] init]; - [foo run]; - sel = sel_get_uid ("run"); - if (sel) { - print ("found selector for `run'\n"); - if ([foo respondsToSelector:sel]) - print ("foo responds to `run'\n"); - else - print ("foo does not repond to `run'\n"); - } else - print ("did not find selector for `run'\n"); - sel = sel_get_uid ("alloc"); - if (sel) { - print ("found selector for `alloc'\n"); - if ([Object instancesRespondToSelector:sel]) - print ("Object instances respond to `alloc'\n"); - else - print ("Object instances do not repond to `alloc'\n"); - } else - print ("did not find selector for `alloc'\n"); - sel = sel_get_uid ("run:with:me:"); - if (sel) { - print ("found selector for `run:with:me:'\n"); - if ([Object instancesRespondToSelector:sel]) - print ("Object instances respond to `run:with:me:'\n"); - else - print ("Object instances do not repond to `run:with:me:'\n"); - } else - print ("did not find selector for `run:with:me:'\n"); - test_str (); - test_script (); - test_plist (); - test_types (); - return 0; -}; diff --git a/tools/qwaq/progs.src.in b/tools/qwaq/progs.src.in deleted file mode 100644 index c6d4f7070..000000000 --- a/tools/qwaq/progs.src.in +++ /dev/null @@ -1,16 +0,0 @@ -qwaq.dat - -@srcdir@/defs.qc - -@top_srcdir@/ruamoko/lib/plist.r -@top_srcdir@/ruamoko/lib/qfile.r -@top_srcdir@/ruamoko/lib/script.r -@top_srcdir@/ruamoko/lib/string.r -@top_srcdir@/ruamoko/lib/Object.r -@top_srcdir@/ruamoko/lib/Protocol.r -@top_srcdir@/ruamoko/lib/Array.r -@top_srcdir@/ruamoko/lib/Array+Private.r -@top_srcdir@/ruamoko/lib/AutoreleasePool.r -@srcdir@/test.r -@srcdir@/types.r -@srcdir@/main.qc diff --git a/tools/qwaq/qwaq-bi.c b/tools/qwaq/qwaq-bi.c deleted file mode 100644 index 3c5964df7..000000000 --- a/tools/qwaq/qwaq-bi.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - #FILENAME# - - #DESCRIPTION# - - Copyright (C) 2001 #AUTHOR# - - Author: #AUTHOR# - Date: #DATE# - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -static __attribute__ ((used)) const char rcsid[] = "$Id$"; - -#include -#include -#include -#include -#include -#include - -#include "QF/cbuf.h" -#include "QF/cdaudio.h" -#include "QF/console.h" -#include "QF/draw.h" -#include "QF/dstring.h" -#include "QF/input.h" -#include "QF/model.h" -#include "QF/plugin.h" -#include "QF/progs.h" -#include "QF/quakefs.h" -#include "QF/render.h" -#include "QF/screen.h" -#include "QF/sound.h" -#include "QF/sys.h" -#include "QF/vid.h" -#include "QF/zone.h" - -#include "QF/plugin/console.h" -#include "QF/plugin/vid_render.h" - -#include "qwaq.h" - -CLIENT_PLUGIN_PROTOS -static plugin_list_t client_plugin_list[] = { - CLIENT_PLUGIN_LIST -}; - -double con_frametime; -double con_realtime, basetime; -double old_conrealtime; - - -static void -quit_f (void) -{ - if (!con_module) - Sys_Printf ("I hope you wanted to quit\n"); - Sys_Quit (); -} - -static void -bi_printf (progs_t *pr) -{ - const char *fmt = P_GSTRING (pr, 0); - int count = pr->pr_argc - 1; - pr_type_t **args = pr->pr_params + 1; - dstring_t *dstr = dstring_new (); - - PR_Sprintf (pr, dstr, "bi_printf", fmt, count, args); - if (dstr->str) - Con_Printf (dstr->str, stdout); - dstring_delete (dstr); -} - -static progs_t *bi_rprogs; -static func_t qc3d, qc2d; - -static void -bi_3d (void) -{ - if (qc3d) - PR_ExecuteProgram (bi_rprogs, qc3d); -} - -static void -bi_2d (void) -{ - if (qc2d) - PR_ExecuteProgram (bi_rprogs, qc2d); -} - -static SCR_Func bi_2dfuncs[] = { - bi_2d, - Con_DrawConsole, - 0, -}; - -static void -bi_refresh (progs_t *pr) -{ - con_realtime = Sys_DoubleTime () - basetime; - con_frametime = con_realtime - old_conrealtime; - old_conrealtime = con_realtime; - bi_rprogs = pr; - IN_ProcessEvents (); - //GIB_Thread_Execute (); - Cbuf_Execute_Stack (qwaq_cbuf); - r_funcs->SCR_UpdateScreen (con_realtime, bi_3d, bi_2dfuncs); - R_FLOAT (pr) = con_frametime; -} - -static void -bi_refresh_2d (progs_t *pr) -{ - qc2d = P_FUNCTION (pr, 0); -} - -static void -bi_refresh_3d (progs_t *pr) -{ - qc3d = P_FUNCTION (pr, 0); -} - -static builtin_t builtins[] = { - {"printf", bi_printf, -1}, - {"refresh", bi_refresh, -1}, - {"refresh_2d", bi_refresh_2d, -1}, - {"refresh_3d", bi_refresh_3d, -1}, - {0} -}; - -static void -bi_shutdown (void) -{ - S_Shutdown (); - IN_Shutdown (); - VID_Shutdown (); -} - -void -BI_Init (progs_t *pr) -{ - byte *basepal, *colormap; - - PR_RegisterBuiltins (pr, builtins); - - QFS_Init ("nq"); - PI_Init (); - PI_RegisterPlugins (client_plugin_list); - - Sys_RegisterShutdown (bi_shutdown); - - VID_Init_Cvars (); - IN_Init_Cvars (); - Mod_Init_Cvars (); - S_Init_Cvars (); - - basepal = (byte *) QFS_LoadHunkFile (QFS_FOpenFile ("gfx/palette.lmp")); - if (!basepal) - Sys_Error ("Couldn't load gfx/palette.lmp"); - colormap = (byte *) QFS_LoadHunkFile (QFS_FOpenFile ("gfx/colormap.lmp")); - if (!colormap) - Sys_Error ("Couldn't load gfx/colormap.lmp"); - - W_LoadWadFile ("gfx.wad"); - VID_Init (basepal, colormap); - IN_Init (qwaq_cbuf); - Mod_Init (); - R_Init (); - R_Progs_Init (pr); - Key_Progs_Init (pr); - S_Progs_Init (pr); - - Con_Init ("client"); - if (con_module) { - con_module->data->console->realtime = &con_realtime; - con_module->data->console->frametime = &con_frametime; - con_module->data->console->quit = quit_f; - con_module->data->console->cbuf = qwaq_cbuf; - } - Key_SetKeyDest (key_game); - - S_Init (0, &con_frametime); - //CDAudio_Init (); - Con_NewMap (); - basetime = Sys_DoubleTime (); -} diff --git a/tools/qwaq/qwaq.c b/tools/qwaq/qwaq.c deleted file mode 100644 index f1197ccfb..000000000 --- a/tools/qwaq/qwaq.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - #FILENAME# - - #DESCRIPTION# - - Copyright (C) 2001 #AUTHOR# - - Author: #AUTHOR# - Date: #DATE# - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - - Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA - -*/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include - -#include "QF/cbuf.h" -#include "QF/cmd.h" -#include "QF/cvar.h" -#include "QF/gib.h" -#include "QF/idparse.h" -#include "QF/progs.h" -#include "QF/qargs.h" -#include "QF/quakefs.h" -#include "QF/ruamoko.h" -#include "QF/sys.h" -#include "QF/va.h" -#include "QF/zone.h" - -#include "qwaq.h" - -#define MAX_EDICTS 1024 - -static edict_t *edicts; -static int num_edicts; -static int reserved_edicts; -static progs_t pr; - -cbuf_t *qwaq_cbuf; - -static QFile * -open_file (const char *path, int *len) -{ - QFile *file = Qopen (path, "rbz"); - - if (!file) { - perror (path); - return 0; - } - *len = Qfilesize (file); - return file; -} - -static void * -load_file (progs_t *pr, const char *name) -{ - QFile *file; - int size; - char *sym; - - file = open_file (name, &size); - if (!file) { - file = open_file (va ("%s.gz", name), &size); - if (!file) { - return 0; - } - } - sym = malloc (size + 1); - sym[size] = 0; - Qread (file, sym, size); - return sym; -} - -static void * -allocate_progs_mem (progs_t *pr, int size) -{ - return malloc (size); -} - -static void -free_progs_mem (progs_t *pr, void *mem) -{ - free (mem); -} - -static void -init_qf (void) -{ - qwaq_cbuf = Cbuf_New (&id_interp); - - Sys_Init (); - GIB_Init (true); - COM_ParseConfig (); - - //Cvar_Set (developer, "1"); - - Memory_Init (malloc (8 * 1024 * 1024), 8 * 1024 * 1024); - - Cvar_Get ("pr_debug", "2", 0, 0, 0); - Cvar_Get ("pr_boundscheck", "0", 0, 0, 0); - - pr.edicts = &edicts; - pr.num_edicts = &num_edicts; - pr.reserved_edicts = &reserved_edicts; - pr.load_file = load_file; - pr.allocate_progs_mem = allocate_progs_mem; - pr.free_progs_mem = free_progs_mem; - pr.no_exec_limit = 1; - - PR_Init_Cvars (); - PR_Init (); - RUA_Init (&pr, 0); - PR_Cmds_Init (&pr); - BI_Init (&pr); -} - -static int -load_progs (const char *name) -{ - QFile *file; - int size; - - file = open_file (name, &size); - if (!file) { - return 0; - } - pr.progs_name = name; - PR_LoadProgsFile (&pr, file, size, 1, 1024 * 1024); - Qclose (file); - if (!PR_RunLoadFuncs (&pr)) - PR_Error (&pr, "unable to load %s", pr.progs_name); - return 1; -} - -int -main (int argc, const char **argv) -{ - dfunction_t *dfunc; - func_t main_func = 0; - const char *name = "qwaq.dat"; - string_t *pr_argv; - int pr_argc = 1, i; - - COM_InitArgv (argc, argv); - init_qf (); - - if (argc > 1) - name = argv[1]; - - if (!load_progs (name)) - Sys_Error ("couldn't load %s", "qwaq.dat"); - - PR_PushFrame (&pr); - if (argc > 2) - pr_argc = argc - 1; - pr_argv = PR_Zone_Malloc (&pr, (pr_argc + 1) * 4); - pr_argv[0] = PR_SetTempString (&pr, name); - for (i = 1; i < pr_argc; i++) - pr_argv[i] = PR_SetTempString (&pr, argv[1 + i]); - pr_argv[i] = 0; - - if ((dfunc = PR_FindFunction (&pr, ".main")) - || (dfunc = PR_FindFunction (&pr, "main"))) - main_func = dfunc - pr.pr_functions; - else - PR_Undefined (&pr, "function", "main"); - PR_RESET_PARAMS (&pr); - P_INT (&pr, 0) = pr_argc; - P_POINTER (&pr, 1) = PR_SetPointer (&pr, pr_argv); - PR_ExecuteProgram (&pr, main_func); - PR_PopFrame (&pr); - return R_INT (&pr); -} diff --git a/tools/qwaq/qwaq.h b/tools/qwaq/qwaq.h deleted file mode 100644 index 9dbb2c23a..000000000 --- a/tools/qwaq/qwaq.h +++ /dev/null @@ -1,2 +0,0 @@ -void BI_Init (progs_t *pr); -extern struct cbuf_s *qwaq_cbuf; diff --git a/tools/qwaq/test.r b/tools/qwaq/test.r deleted file mode 100644 index 6e0afd6f2..000000000 --- a/tools/qwaq/test.r +++ /dev/null @@ -1,42 +0,0 @@ -@interface Foo : Object -{ - int x; -} --run; -@end - - -@implementation Foo - -+load -{ - print ("+load\n"); - return self; -} - -+alloc -{ - print ("+alloc\n"); - return class_create_instance (self); -} - --init -{ - print ("-init\n"); - return [super init]; -} - -+ (void) initialize -{ - print ("+initialize\n"); -} - --run -{ - print ("Hello world\n"); - printf ("%i %p [%s %s]\n", self, &self.x, [self description], - __PRETTY_FUNCTION__); - return self; -} - -@end diff --git a/tools/qwaq/types.r b/tools/qwaq/types.r deleted file mode 100644 index 82665e20a..000000000 --- a/tools/qwaq/types.r +++ /dev/null @@ -1,174 +0,0 @@ -#include "runtime.h" //FIXME for PR_FindGlobal -typedef enum { - ev_void, - ev_string, - ev_float, - ev_vector, - ev_entity, - ev_field, - ev_func, - ev_pointer, // end of v6 types - ev_quat, - ev_integer, - ev_uinteger, - ev_short, // value is embedded in the opcode - - ev_invalid, // invalid type. used for instruction checking - ev_type_count // not a type, gives number of types -} etype_t; - -typedef enum { - ty_none, ///< func/field/pointer or not used - ty_struct, - ty_union, - ty_enum, - ty_array, - ty_class, -} ty_meta_e; - - -typedef struct qfot_fldptr_s { - etype_t type; - struct qfot_type_s *aux_type; -} qfot_fldptr_t; - -typedef struct qfot_func_s { - etype_t type; - struct qfot_type_s *return_type; - int num_params; - struct qfot_type_s *param_types[1]; -} qfot_func_t; - -typedef struct qfot_var_s { - struct qfot_type_s *type; - string name; - int offset; // value for enum, 0 for union -} qfot_var_t; - -typedef struct qfot_struct_s { - string tag; - int num_fields; - qfot_var_t fields[1]; -} qfot_struct_t; - -typedef struct qfot_array_s { - struct qfot_type_s *type; - int base; - int size; -} qfot_array_t; - -typedef struct qfot_type_s { - ty_meta_e meta; - int size; - string encoding; - union { - etype_t type; - qfot_fldptr_t fldptr; - qfot_func_t func; - qfot_struct_t strct; - qfot_array_t array; - string class; - } t; -} qfot_type_t; - -typedef struct qfot_type_encodings_s { - qfot_type_t *types; - int size; -} qfot_type_encodings_t; - -qfot_type_t * -next_type (qfot_type_t *type) -{ - int size = type.size; - if (!size) - size = 4; - return (qfot_type_t *) ((int *) type + size); -} - -string ty_meta_name[6] = { - "basic", - "struct", - "union", - "enum", - "array", - "class", -}; - -string pr_type_name[ev_type_count] = { - "void", - "string", - "float", - "vector", - "entity", - "field", - "function", - "pointer", - "quaternion", - "integer", - "uinteger", - "short", - "invalid", -}; - -void -test_types (void) -{ - qfot_type_encodings_t *encodings; - qfot_type_t *type; - int i; - - encodings = PR_FindGlobal (".type_encodings"); - if (!encodings) { - printf ("Can't find encodings\n"); - return; - } - for (type = encodings.types; - ((int *)type - (int *) encodings.types) < encodings.size; - type = next_type (type)) { - printf ("%p %-6s %-20s", type, ty_meta_name[type.meta], - type.encoding); - if (!type.size) { - printf ("\n"); - continue; - } - switch (type.meta) { - case ty_none: - printf (" %-10s", pr_type_name[type.t.type]); - if (type.t.type == ev_func) { - int count = type.t.func.num_params; - printf ("%p %d", type.t.func.return_type, count); - if (count < 0) - count = ~count; - for (i = 0; i < count; i++) - printf (" %p", type.t.func.param_types[i]); - } else if (type.t.type == ev_pointer) { - printf (" *%p", type.t.fldptr.aux_type); - } else if (type.t.type == ev_field) { - printf (" .%p", type.t.fldptr.aux_type); - } else { - printf (" %p", type.t.fldptr.aux_type); - } - printf ("\n"); - break; - case ty_struct: - case ty_union: - case ty_enum: - printf (" %s\n", type.t.strct.tag); - for (i = 0; i < type.t.strct.num_fields; i++) { - printf (" %p %4x %s\n", - type.t.strct.fields[i].type, - type.t.strct.fields[i].offset, - type.t.strct.fields[i].name); - } - break; - case ty_array: - printf (" %p %d %d\n", type.t.array.type, - type.t.array.base, type.t.array.size); - break; - case ty_class: - printf (" %s %p\n", type.t.class, - obj_lookup_class (type.t.class)); - break; - } - } -} diff --git a/tools/wad/Makefile.am b/tools/wad/Makefile.am deleted file mode 100644 index 7db82d273..000000000 --- a/tools/wad/Makefile.am +++ /dev/null @@ -1,19 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -WAD_LIBS=@WAD_LIBS@ -WAD_DEPS=@WAD_DEPS@ -WAD_INCS=@WAD_INCS@ - -AM_CPPFLAGS= -I$(top_srcdir)/include $(WAD_INCS) - - -bin_PROGRAMS= wad - -mans=wad.1 -man_MANS= $(mans) - -wad_SOURCES= grab.c script.c wad.c -wad_LDADD= $(WAD_LIBS) -wad_DEPENDENCIES= $(WAD_DEPS) - -EXTRA_DIST= wad.h $(mans) diff --git a/tools/wad/Makemodule.am b/tools/wad/Makemodule.am new file mode 100644 index 000000000..13fcf3a43 --- /dev/null +++ b/tools/wad/Makemodule.am @@ -0,0 +1,14 @@ +WAD_LIBS=@WAD_LIBS@ +WAD_DEPS=@WAD_DEPS@ +WAD_INCS=@WAD_INCS@ + +EXTRA_PROGRAMS += wad +bin_PROGRAMS += @WAD_TARGETS@ + +man_MANS += tools/wad/wad.1 + +wad_SOURCES= tools/wad/grab.c tools/wad/script.c tools/wad/wad.c +wad_LDADD= $(WAD_LIBS) +wad_DEPENDENCIES= $(WAD_DEPS) + +EXTRA_DIST += tools/wad/wad.h diff --git a/tools/wad/script.c b/tools/wad/script.c index eef6a90e9..3b98e4c1b 100644 --- a/tools/wad/script.c +++ b/tools/wad/script.c @@ -52,7 +52,7 @@ #include "wad.h" static dstring_t destfile = {&dstring_default_mem}; -static qboolean savesingle = false; +static bool savesingle = false; static wad_t *wadfile; dstring_t *lumpname; tex_t *image; @@ -92,7 +92,7 @@ load_image (const char *name) if (!(file = Qopen (name, "rb"))) Sys_Error ("couldn't open %s. %s", name, strerror(errno)); - if (!(tex = LoadPNG (file))) + if (!(tex = LoadPNG (file, 1))) Sys_Error ("couldn't read %s as PNG", name); pixels = tex->width * tex->height; @@ -104,7 +104,7 @@ load_image (const char *name) switch (tex->format) { case tex_palette: for (i = 0, s = tex->data, d = image->data; i < pixels; i++) { - byte *v = tex->palette + *s++ * 3; + const byte *v = tex->palette + *s++ * 3; *d++ = *v++; *d++ = *v++; *d++ = *v++; @@ -159,7 +159,7 @@ write_file (void) QFile *file; const char *name; - name = va ("%s/%s.lmp", destfile.str, lumpname->str); + name = va (0, "%s/%s.lmp", destfile.str, lumpname->str); if (!(file = Qopen (name, "wb"))) Sys_Error ("couldn't open %s. %s", name, strerror(errno)); Qwrite (file, lumpbuffer, lump_p - lumpbuffer); diff --git a/tools/wad/wad.c b/tools/wad/wad.c index a1caf6fc9..08abf6d81 100644 --- a/tools/wad/wad.c +++ b/tools/wad/wad.c @@ -396,7 +396,7 @@ main (int argc, char **argv) lumpinfo_t *pf; Sys_Init (); - Memory_Init (malloc (MEMSIZE), MEMSIZE); + Memory_Init (Sys_Alloc (MEMSIZE), MEMSIZE); this_program = argv[0]; diff --git a/tools/wad/wad.h b/tools/wad/wad.h index d5e3d32fc..af850e202 100644 --- a/tools/wad/wad.h +++ b/tools/wad/wad.h @@ -46,9 +46,9 @@ typedef enum { typedef struct { wadmode_t mode; // see above int verbosity; // 0=silent - qboolean compress; // for the future - qboolean pad; // pad area of files to 4-byte boundary - qboolean nomip; // exclude mipmaps from output textures. + bool compress; // for the future + bool pad; // pad area of files to 4-byte boundary + bool nomip; // exclude mipmaps from output textures. char *wadfile; // wad file to read/write/test } options_t; diff --git a/tools/wav/Makefile.am b/tools/wav/Makefile.am deleted file mode 100644 index 7e17fe376..000000000 --- a/tools/wav/Makefile.am +++ /dev/null @@ -1,18 +0,0 @@ -AUTOMAKE_OPTIONS= foreign - -WAV_LIBS=@WAV_LIBS@ -WAV_DEPS=@WAV_DEPS@ -WAV_INCS=@WAV_INCS@ - -AM_CPPFLAGS= -I$(top_srcdir)/include $(WAV_INCS) - -bin_PROGRAMS= qfwavinfo - -mans=qfwavinfo.1 -man_MANS= #$(mans) - -qfwavinfo_SOURCES= qfwavinfo.c -qfwavinfo_LDADD= $(WAV_LIBS) -qfwavinfo_DEPENDENCIES= $(WAV_DEPS) - -EXTRA_DIST=#$(mans) diff --git a/tools/wav/Makemodule.am b/tools/wav/Makemodule.am new file mode 100644 index 000000000..6eb727ad0 --- /dev/null +++ b/tools/wav/Makemodule.am @@ -0,0 +1,12 @@ +WAV_LIBS=@WAV_LIBS@ +WAV_DEPS=@WAV_DEPS@ +WAV_INCS=@WAV_INCS@ + +EXTRA_PROGRAMS += qfwavinfo +bin_PROGRAMS += @WAV_TARGETS@ + +#man_MANS += tools/wav/qfwavinfo.1 + +qfwavinfo_SOURCES= tools/wav/qfwavinfo.c +qfwavinfo_LDADD= $(WAV_LIBS) +qfwavinfo_DEPENDENCIES= $(WAV_DEPS) diff --git a/vc2005/Makefile.am b/vc2005/Makefile.am deleted file mode 100644 index 7f616ef19..000000000 --- a/vc2005/Makefile.am +++ /dev/null @@ -1,24 +0,0 @@ -## Process this file with automake to produce Makefile.in -AUTOMAKE_OPTIONS= foreign -EXTRA_DIST = \ - Makefile.am QuakeForge-VS8-Express.sln QuakeForge-VS8.sln \ - bsp2img/bsp2img.vcproj builtins/builtins.vcproj clean.ps1 \ - common/common.vcproj console/console.vcproj \ - console_client/console_client.vcproj console_server/console_server.vcproj \ - engine/engine.vcproj gib/gib.vcproj hw-master/hw-master.vcproj \ - image/image.vcproj include/config.h models-sw/models-sw.vcproj \ - models/models.vcproj modelsgl/modelsgl.vcproj net-main/net-main.vcproj \ - net/net.vcproj nq-common/nq-common.vcproj nq-sdl/nq-sdl.vcproj \ - nq-sdl32/nq-sdl32.vcproj nq-server/nq-server.vcproj nq-sgl/nq-sgl.vcproj \ - nq-wgl/nq-wgl.vcproj nq/nq.vcproj pak/pak.vcproj qfbsp/qfbsp.vcproj \ - qfcc/FlexBison.rules qfcc/qfcc.vcproj qfclient/qfclient.vcproj \ - qflight/qflight.vcproj qfmodelgen/qfmodelgen.vcproj \ - qfprogs/qfprogs.vcproj qfserver/qfserver.vcproj qfvis/qfvis.vcproj \ - qfwavinfo/qfwavinfo.vcproj qtv/qtv.vcproj \ - qw-client-sdl/qw-client-sdl.vcproj qw-client-sdl32/qw-client-sdl32.vcproj \ - qw-client-sgl/qw-client-sgl.vcproj qw-client-wgl/qw-client-wgl.vcproj \ - qw-master/qw-master.vcproj qw/qw.vcproj readme.txt ruamoko/ruamoko.vcproj \ - sound/sound.vcproj util/util.vcproj video-sdl/video-sdl.vcproj \ - video-sgl/video-sgl.vcproj video-sw/video-sw.vcproj \ - video-sw32/video-sw32.vcproj video-wgl/video-wgl.vcproj \ - video/video.vcproj videogl/videogl.vcproj wad/wad.vcproj diff --git a/vc2005/QuakeForge-VS8-Express.sln b/vc2005/QuakeForge-VS8-Express.sln deleted file mode 100644 index 404f35ff4..000000000 --- a/vc2005/QuakeForge-VS8-Express.sln +++ /dev/null @@ -1,935 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-client", "qfclient\qfclient.vcproj", "{9A942925-61E6-4975-935A-5D62E8248E64}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw", "qw\qw.vcproj", "{6ADA4322-693A-46BB-897B-17BB5BE9F08C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "net-chan", "net\net.vcproj", "{6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ruamoko", "ruamoko\ruamoko.vcproj", "{51028ACF-53D4-4478-8500-55E6B8A81375}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models-gl", "modelsgl\modelsgl.vcproj", "{1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "console", "console\console.vcproj", "{ED4AFBF5-C247-4352-966D-048B8998C6A1}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gib", "gib\gib.vcproj", "{01C3B138-9D45-4ED6-A763-893C067262C2}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "util", "util\util.vcproj", "{ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "engine", "engine\engine.vcproj", "{2626C0E1-6F5C-47D3-B80D-93942D766DD7}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "builtins", "builtins\builtins.vcproj", "{04FA9D77-E45F-4917-B972-B353BA6A6FA8}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sound", "sound\sound.vcproj", "{BF149D97-7520-4788-9CD1-3D99C5C8150F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "image", "image\image.vcproj", "{5203F034-0047-4EC0-B7E9-D037FAF5D536}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video", "video\video.vcproj", "{E7B3D07D-2FE8-481B-8DAB-6255A412A42F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-common", "common\common.vcproj", "{BC1F021A-1EEC-4A7A-B746-5AA6048E478C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-server", "qfserver\qfserver.vcproj", "{544D097C-9C24-4C57-A171-8C8029421185}" - ProjectSection(ProjectDependencies) = postProject - {6ADA4322-693A-46BB-897B-17BB5BE9F08C} = {6ADA4322-693A-46BB-897B-17BB5BE9F08C} - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} = {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A} = {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A} - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models", "models\models.vcproj", "{DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "console-client", "console_client\console_client.vcproj", "{226D42CE-5833-444E-94FA-84C1D51892A8}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "console-server", "console_server\console_server.vcproj", "{B37FE734-01F4-4799-86B2-D084820715BE}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qfbsp", "qfbsp\qfbsp.vcproj", "{B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qflight", "qflight\qflight.vcproj", "{5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qfvis", "qfvis\qfvis.vcproj", "{E5D842C5-669F-4FC7-A5E0-44B562F86435}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-master", "qw-master\qw-master.vcproj", "{445A2500-3BBC-449A-A929-C419C2A16051}" - ProjectSection(ProjectDependencies) = postProject - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qfmodelgen", "qfmodelgen\qfmodelgen.vcproj", "{052C34FE-C9E2-43ED-95DA-FB3F27B44E37}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qfprogs", "qfprogs\qfprogs.vcproj", "{5FA27C8E-51B1-445A-A375-FBE74F0984B1}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qfwavinfo", "qfwavinfo\qfwavinfo.vcproj", "{56BD559B-1590-4FC4-B441-AB1973BAC2BD}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wad", "wad\wad.vcproj", "{88422448-C5FB-46F3-A0B3-0811F90E537C}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pak", "pak\pak.vcproj", "{9EE8BD4B-47D3-4AD5-A8B9-831329792A05}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bsp2img", "bsp2img\bsp2img.vcproj", "{4605418D-2292-470A-AB18-C2119B016F71}" - ProjectSection(ProjectDependencies) = postProject - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qtv", "qtv\qtv.vcproj", "{05E68E3B-5901-43A9-981D-CF392C0F5C6C}" - ProjectSection(ProjectDependencies) = postProject - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {6ADA4322-693A-46BB-897B-17BB5BE9F08C} = {6ADA4322-693A-46BB-897B-17BB5BE9F08C} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq-common", "nq-common\nq-common.vcproj", "{6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq-wgl", "nq-wgl\nq-wgl.vcproj", "{1CD1A18B-95D5-4EA4-917C-34B10066BFCC}" - ProjectSection(ProjectDependencies) = postProject - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137} = {178D81A7-F2FB-41D7-B300-9D1A4DE5E137} - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} = {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} = {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} = {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} = {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} = {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "net-main", "net-main\net-main.vcproj", "{053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq-server", "nq-server\nq-server.vcproj", "{231C032C-DE16-459A-8E7D-6509C2EC3998}" - ProjectSection(ProjectDependencies) = postProject - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} = {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} = {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A} = {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hw-master", "hw-master\hw-master.vcproj", "{419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}" - ProjectSection(ProjectDependencies) = postProject - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qfcc", "qfcc\qfcc.vcproj", "{40639893-4D75-48CD-811F-4B363CC71FFA}" - ProjectSection(ProjectDependencies) = postProject - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-client-wgl", "qw-client-wgl\qw-client-wgl.vcproj", "{5FD733BF-B3C6-4A96-BED7-35E2484448E1}" - ProjectSection(ProjectDependencies) = postProject - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} = {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {9A942925-61E6-4975-935A-5D62E8248E64} = {9A942925-61E6-4975-935A-5D62E8248E64} - {6ADA4322-693A-46BB-897B-17BB5BE9F08C} = {6ADA4322-693A-46BB-897B-17BB5BE9F08C} - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} = {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} = {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137} = {178D81A7-F2FB-41D7-B300-9D1A4DE5E137} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-client-sgl", "qw-client-sgl\qw-client-sgl.vcproj", "{649C4168-1C65-4E41-818F-85A1C52C1B8F}" - ProjectSection(ProjectDependencies) = postProject - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} = {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} - {9A942925-61E6-4975-935A-5D62E8248E64} = {9A942925-61E6-4975-935A-5D62E8248E64} - {6ADA4322-693A-46BB-897B-17BB5BE9F08C} = {6ADA4322-693A-46BB-897B-17BB5BE9F08C} - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} = {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} = {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} - {44A18410-3AA8-4A64-935B-D20223AD6885} = {44A18410-3AA8-4A64-935B-D20223AD6885} - {7E30C3B1-AEE7-483D-B231-C672365CD2D7} = {7E30C3B1-AEE7-483D-B231-C672365CD2D7} - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video-gl", "videogl\videogl.vcproj", "{C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video-sdl", "video-sdl\video-sdl.vcproj", "{44A18410-3AA8-4A64-935B-D20223AD6885}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-client-sdl", "qw-client-sdl\qw-client-sdl.vcproj", "{B44CB3E0-F2FD-4260-A632-C01FB881613E}" - ProjectSection(ProjectDependencies) = postProject - {66D3A191-E4D5-45F3-86BD-EFBB249CD554} = {66D3A191-E4D5-45F3-86BD-EFBB249CD554} - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - {44A18410-3AA8-4A64-935B-D20223AD6885} = {44A18410-3AA8-4A64-935B-D20223AD6885} - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} = {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} - {6ADA4322-693A-46BB-897B-17BB5BE9F08C} = {6ADA4322-693A-46BB-897B-17BB5BE9F08C} - {9A942925-61E6-4975-935A-5D62E8248E64} = {9A942925-61E6-4975-935A-5D62E8248E64} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {F4A9881E-0EB0-44A1-9664-B6CBDE992861} = {F4A9881E-0EB0-44A1-9664-B6CBDE992861} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models-sw", "models-sw\models-sw.vcproj", "{F4A9881E-0EB0-44A1-9664-B6CBDE992861}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video-sw", "video-sw\video-sw.vcproj", "{66D3A191-E4D5-45F3-86BD-EFBB249CD554}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-client-sdl32", "qw-client-sdl32\qw-client-sdl32.vcproj", "{CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}" - ProjectSection(ProjectDependencies) = postProject - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - {44A18410-3AA8-4A64-935B-D20223AD6885} = {44A18410-3AA8-4A64-935B-D20223AD6885} - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} = {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} - {F4A9881E-0EB0-44A1-9664-B6CBDE992861} = {F4A9881E-0EB0-44A1-9664-B6CBDE992861} - {6ADA4322-693A-46BB-897B-17BB5BE9F08C} = {6ADA4322-693A-46BB-897B-17BB5BE9F08C} - {9A942925-61E6-4975-935A-5D62E8248E64} = {9A942925-61E6-4975-935A-5D62E8248E64} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {D5B9558F-EF25-4167-ACE4-723C7413B390} = {D5B9558F-EF25-4167-ACE4-723C7413B390} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video-sw32", "video-sw32\video-sw32.vcproj", "{D5B9558F-EF25-4167-ACE4-723C7413B390}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq", "nq\nq.vcproj", "{155112B9-A8A9-4E06-90F5-4AAAB32B2F70}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video-wgl", "video-wgl\video-wgl.vcproj", "{178D81A7-F2FB-41D7-B300-9D1A4DE5E137}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video-sgl", "video-sgl\video-sgl.vcproj", "{7E30C3B1-AEE7-483D-B231-C672365CD2D7}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq-sgl", "nq-sgl\nq-sgl.vcproj", "{2E26DB8B-9E37-4072-B397-8A7086E0ACD0}" - ProjectSection(ProjectDependencies) = postProject - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} = {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {7E30C3B1-AEE7-483D-B231-C672365CD2D7} = {7E30C3B1-AEE7-483D-B231-C672365CD2D7} - {44A18410-3AA8-4A64-935B-D20223AD6885} = {44A18410-3AA8-4A64-935B-D20223AD6885} - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} = {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} = {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} = {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} = {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq-sdl", "nq-sdl\nq-sdl.vcproj", "{2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}" - ProjectSection(ProjectDependencies) = postProject - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} = {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} = {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {66D3A191-E4D5-45F3-86BD-EFBB249CD554} = {66D3A191-E4D5-45F3-86BD-EFBB249CD554} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} = {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {F4A9881E-0EB0-44A1-9664-B6CBDE992861} = {F4A9881E-0EB0-44A1-9664-B6CBDE992861} - {44A18410-3AA8-4A64-935B-D20223AD6885} = {44A18410-3AA8-4A64-935B-D20223AD6885} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq-sdl32", "nq-sdl32\nq-sdl32.vcproj", "{2CEB1965-A89C-4422-A9AC-B30FCE7913C3}" - ProjectSection(ProjectDependencies) = postProject - {44A18410-3AA8-4A64-935B-D20223AD6885} = {44A18410-3AA8-4A64-935B-D20223AD6885} - {F4A9881E-0EB0-44A1-9664-B6CBDE992861} = {F4A9881E-0EB0-44A1-9664-B6CBDE992861} - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} = {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {D5B9558F-EF25-4167-ACE4-723C7413B390} = {D5B9558F-EF25-4167-ACE4-723C7413B390} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} = {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} = {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release (static)|Win32 = Release (static)|Win32 - Release (static)|x64 = Release (static)|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9A942925-61E6-4975-935A-5D62E8248E64}.Debug|Win32.ActiveCfg = Debug|Win32 - {9A942925-61E6-4975-935A-5D62E8248E64}.Debug|Win32.Build.0 = Debug|Win32 - {9A942925-61E6-4975-935A-5D62E8248E64}.Debug|x64.ActiveCfg = Debug|x64 - {9A942925-61E6-4975-935A-5D62E8248E64}.Debug|x64.Build.0 = Debug|x64 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release (static)|x64.Build.0 = Release (static)|x64 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release|Win32.ActiveCfg = Release|Win32 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release|Win32.Build.0 = Release|Win32 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release|x64.ActiveCfg = Release|x64 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release|x64.Build.0 = Release|x64 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Debug|Win32.ActiveCfg = Debug|Win32 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Debug|Win32.Build.0 = Debug|Win32 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Debug|x64.ActiveCfg = Debug|x64 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Debug|x64.Build.0 = Debug|x64 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release (static)|x64.Build.0 = Release (static)|x64 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release|Win32.ActiveCfg = Release|Win32 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release|Win32.Build.0 = Release|Win32 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release|x64.ActiveCfg = Release|x64 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release|x64.Build.0 = Release|x64 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Debug|Win32.ActiveCfg = Debug|Win32 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Debug|Win32.Build.0 = Debug|Win32 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Debug|x64.ActiveCfg = Debug|x64 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Debug|x64.Build.0 = Debug|x64 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release (static)|x64.Build.0 = Release (static)|x64 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release|Win32.ActiveCfg = Release|Win32 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release|Win32.Build.0 = Release|Win32 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release|x64.ActiveCfg = Release|x64 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release|x64.Build.0 = Release|x64 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Debug|Win32.ActiveCfg = Debug|Win32 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Debug|Win32.Build.0 = Debug|Win32 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Debug|x64.ActiveCfg = Debug|x64 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Debug|x64.Build.0 = Debug|x64 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release (static)|x64.Build.0 = Release (static)|x64 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release|Win32.ActiveCfg = Release|Win32 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release|Win32.Build.0 = Release|Win32 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release|x64.ActiveCfg = Release|x64 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release|x64.Build.0 = Release|x64 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Debug|Win32.ActiveCfg = Debug|Win32 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Debug|Win32.Build.0 = Debug|Win32 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Debug|x64.ActiveCfg = Debug|x64 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Debug|x64.Build.0 = Debug|x64 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release (static)|x64.Build.0 = Release (static)|x64 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release|Win32.ActiveCfg = Release|Win32 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release|Win32.Build.0 = Release|Win32 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release|x64.ActiveCfg = Release|x64 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release|x64.Build.0 = Release|x64 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Debug|Win32.ActiveCfg = Debug|Win32 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Debug|Win32.Build.0 = Debug|Win32 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Debug|x64.ActiveCfg = Debug|x64 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Debug|x64.Build.0 = Debug|x64 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release (static)|x64.Build.0 = Release (static)|x64 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release|Win32.ActiveCfg = Release|Win32 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release|Win32.Build.0 = Release|Win32 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release|x64.ActiveCfg = Release|x64 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release|x64.Build.0 = Release|x64 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Debug|Win32.ActiveCfg = Debug|Win32 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Debug|Win32.Build.0 = Debug|Win32 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Debug|x64.ActiveCfg = Debug|x64 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Debug|x64.Build.0 = Debug|x64 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release (static)|x64.Build.0 = Release (static)|x64 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release|Win32.ActiveCfg = Release|Win32 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release|Win32.Build.0 = Release|Win32 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release|x64.ActiveCfg = Release|x64 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release|x64.Build.0 = Release|x64 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Debug|Win32.ActiveCfg = Debug|Win32 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Debug|Win32.Build.0 = Debug|Win32 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Debug|x64.ActiveCfg = Debug|x64 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Debug|x64.Build.0 = Debug|x64 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release (static)|x64.Build.0 = Release (static)|x64 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release|Win32.ActiveCfg = Release|Win32 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release|Win32.Build.0 = Release|Win32 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release|x64.ActiveCfg = Release|x64 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release|x64.Build.0 = Release|x64 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Debug|Win32.ActiveCfg = Debug|Win32 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Debug|Win32.Build.0 = Debug|Win32 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Debug|x64.ActiveCfg = Debug|x64 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Debug|x64.Build.0 = Debug|x64 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release (static)|x64.Build.0 = Release (static)|x64 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release|Win32.ActiveCfg = Release|Win32 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release|Win32.Build.0 = Release|Win32 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release|x64.ActiveCfg = Release|x64 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release|x64.Build.0 = Release|x64 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Debug|Win32.ActiveCfg = Debug|Win32 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Debug|Win32.Build.0 = Debug|Win32 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Debug|x64.ActiveCfg = Debug|x64 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Debug|x64.Build.0 = Debug|x64 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release (static)|x64.Build.0 = Release (static)|x64 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release|Win32.ActiveCfg = Release|Win32 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release|Win32.Build.0 = Release|Win32 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release|x64.ActiveCfg = Release|x64 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release|x64.Build.0 = Release|x64 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Debug|Win32.ActiveCfg = Debug|Win32 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Debug|Win32.Build.0 = Debug|Win32 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Debug|x64.ActiveCfg = Debug|x64 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Debug|x64.Build.0 = Debug|x64 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release (static)|x64.Build.0 = Release (static)|x64 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release|Win32.ActiveCfg = Release|Win32 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release|Win32.Build.0 = Release|Win32 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release|x64.ActiveCfg = Release|x64 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release|x64.Build.0 = Release|x64 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Debug|Win32.ActiveCfg = Debug|Win32 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Debug|Win32.Build.0 = Debug|Win32 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Debug|x64.ActiveCfg = Debug|x64 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Debug|x64.Build.0 = Debug|x64 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release (static)|x64.Build.0 = Release (static)|x64 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release|Win32.ActiveCfg = Release|Win32 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release|Win32.Build.0 = Release|Win32 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release|x64.ActiveCfg = Release|x64 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release|x64.Build.0 = Release|x64 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Debug|Win32.ActiveCfg = Debug|Win32 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Debug|Win32.Build.0 = Debug|Win32 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Debug|x64.ActiveCfg = Debug|x64 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Debug|x64.Build.0 = Debug|x64 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release (static)|x64.Build.0 = Release (static)|x64 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release|Win32.ActiveCfg = Release|Win32 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release|Win32.Build.0 = Release|Win32 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release|x64.ActiveCfg = Release|x64 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release|x64.Build.0 = Release|x64 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Debug|Win32.ActiveCfg = Debug|Win32 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Debug|Win32.Build.0 = Debug|Win32 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Debug|x64.ActiveCfg = Debug|x64 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Debug|x64.Build.0 = Debug|x64 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release (static)|x64.Build.0 = Release (static)|x64 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release|Win32.ActiveCfg = Release|Win32 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release|Win32.Build.0 = Release|Win32 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release|x64.ActiveCfg = Release|x64 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release|x64.Build.0 = Release|x64 - {544D097C-9C24-4C57-A171-8C8029421185}.Debug|Win32.ActiveCfg = Debug|Win32 - {544D097C-9C24-4C57-A171-8C8029421185}.Debug|Win32.Build.0 = Debug|Win32 - {544D097C-9C24-4C57-A171-8C8029421185}.Debug|x64.ActiveCfg = Debug|x64 - {544D097C-9C24-4C57-A171-8C8029421185}.Debug|x64.Build.0 = Debug|x64 - {544D097C-9C24-4C57-A171-8C8029421185}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {544D097C-9C24-4C57-A171-8C8029421185}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {544D097C-9C24-4C57-A171-8C8029421185}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {544D097C-9C24-4C57-A171-8C8029421185}.Release (static)|x64.Build.0 = Release (static)|x64 - {544D097C-9C24-4C57-A171-8C8029421185}.Release|Win32.ActiveCfg = Release|Win32 - {544D097C-9C24-4C57-A171-8C8029421185}.Release|Win32.Build.0 = Release|Win32 - {544D097C-9C24-4C57-A171-8C8029421185}.Release|x64.ActiveCfg = Release|x64 - {544D097C-9C24-4C57-A171-8C8029421185}.Release|x64.Build.0 = Release|x64 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Debug|Win32.ActiveCfg = Debug|Win32 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Debug|Win32.Build.0 = Debug|Win32 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Debug|x64.ActiveCfg = Debug|x64 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Debug|x64.Build.0 = Debug|x64 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release (static)|x64.Build.0 = Release (static)|x64 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release|Win32.ActiveCfg = Release|Win32 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release|Win32.Build.0 = Release|Win32 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release|x64.ActiveCfg = Release|x64 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release|x64.Build.0 = Release|x64 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Debug|Win32.ActiveCfg = Debug|Win32 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Debug|Win32.Build.0 = Debug|Win32 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Debug|x64.ActiveCfg = Debug|x64 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Debug|x64.Build.0 = Debug|x64 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release (static)|x64.Build.0 = Release (static)|x64 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release|Win32.ActiveCfg = Release|Win32 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release|Win32.Build.0 = Release|Win32 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release|x64.ActiveCfg = Release|x64 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release|x64.Build.0 = Release|x64 - {B37FE734-01F4-4799-86B2-D084820715BE}.Debug|Win32.ActiveCfg = Debug|Win32 - {B37FE734-01F4-4799-86B2-D084820715BE}.Debug|Win32.Build.0 = Debug|Win32 - {B37FE734-01F4-4799-86B2-D084820715BE}.Debug|x64.ActiveCfg = Debug|x64 - {B37FE734-01F4-4799-86B2-D084820715BE}.Debug|x64.Build.0 = Debug|x64 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release (static)|x64.Build.0 = Release (static)|x64 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release|Win32.ActiveCfg = Release|Win32 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release|Win32.Build.0 = Release|Win32 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release|x64.ActiveCfg = Release|x64 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release|x64.Build.0 = Release|x64 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Debug|Win32.ActiveCfg = Debug|Win32 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Debug|Win32.Build.0 = Debug|Win32 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Debug|x64.ActiveCfg = Debug|x64 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Debug|x64.Build.0 = Debug|x64 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release (static)|x64.Build.0 = Release (static)|x64 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release|Win32.ActiveCfg = Release|Win32 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release|Win32.Build.0 = Release|Win32 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release|x64.ActiveCfg = Release|x64 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release|x64.Build.0 = Release|x64 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Debug|Win32.ActiveCfg = Debug|Win32 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Debug|Win32.Build.0 = Debug|Win32 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Debug|x64.ActiveCfg = Debug|x64 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Debug|x64.Build.0 = Debug|x64 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release (static)|x64.Build.0 = Release (static)|x64 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release|Win32.ActiveCfg = Release|Win32 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release|Win32.Build.0 = Release|Win32 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release|x64.ActiveCfg = Release|x64 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release|x64.Build.0 = Release|x64 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Debug|Win32.ActiveCfg = Debug|Win32 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Debug|Win32.Build.0 = Debug|Win32 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Debug|x64.ActiveCfg = Debug|x64 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Debug|x64.Build.0 = Debug|x64 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release (static)|x64.Build.0 = Release (static)|x64 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release|Win32.ActiveCfg = Release|Win32 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release|Win32.Build.0 = Release|Win32 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release|x64.ActiveCfg = Release|x64 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release|x64.Build.0 = Release|x64 - {445A2500-3BBC-449A-A929-C419C2A16051}.Debug|Win32.ActiveCfg = Debug|Win32 - {445A2500-3BBC-449A-A929-C419C2A16051}.Debug|Win32.Build.0 = Debug|Win32 - {445A2500-3BBC-449A-A929-C419C2A16051}.Debug|x64.ActiveCfg = Debug|x64 - {445A2500-3BBC-449A-A929-C419C2A16051}.Debug|x64.Build.0 = Debug|x64 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release (static)|x64.Build.0 = Release (static)|x64 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release|Win32.ActiveCfg = Release|Win32 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release|Win32.Build.0 = Release|Win32 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release|x64.ActiveCfg = Release|x64 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release|x64.Build.0 = Release|x64 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Debug|Win32.ActiveCfg = Debug|Win32 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Debug|Win32.Build.0 = Debug|Win32 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Debug|x64.ActiveCfg = Debug|x64 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Debug|x64.Build.0 = Debug|x64 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release (static)|x64.Build.0 = Release (static)|x64 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release|Win32.ActiveCfg = Release|Win32 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release|Win32.Build.0 = Release|Win32 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release|x64.ActiveCfg = Release|x64 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release|x64.Build.0 = Release|x64 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Debug|Win32.ActiveCfg = Debug|Win32 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Debug|Win32.Build.0 = Debug|Win32 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Debug|x64.ActiveCfg = Debug|x64 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Debug|x64.Build.0 = Debug|x64 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release (static)|x64.Build.0 = Release (static)|x64 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release|Win32.ActiveCfg = Release|Win32 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release|Win32.Build.0 = Release|Win32 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release|x64.ActiveCfg = Release|x64 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release|x64.Build.0 = Release|x64 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Debug|Win32.ActiveCfg = Debug|Win32 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Debug|Win32.Build.0 = Debug|Win32 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Debug|x64.ActiveCfg = Debug|x64 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Debug|x64.Build.0 = Debug|x64 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release (static)|x64.Build.0 = Release (static)|x64 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release|Win32.ActiveCfg = Release|Win32 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release|Win32.Build.0 = Release|Win32 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release|x64.ActiveCfg = Release|x64 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release|x64.Build.0 = Release|x64 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Debug|Win32.ActiveCfg = Debug|Win32 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Debug|Win32.Build.0 = Debug|Win32 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Debug|x64.ActiveCfg = Debug|x64 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Debug|x64.Build.0 = Debug|x64 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release (static)|x64.Build.0 = Release (static)|x64 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release|Win32.ActiveCfg = Release|Win32 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release|Win32.Build.0 = Release|Win32 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release|x64.ActiveCfg = Release|x64 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release|x64.Build.0 = Release|x64 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Debug|Win32.ActiveCfg = Debug|Win32 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Debug|Win32.Build.0 = Debug|Win32 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Debug|x64.ActiveCfg = Debug|x64 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Debug|x64.Build.0 = Debug|x64 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release (static)|x64.Build.0 = Release (static)|x64 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release|Win32.ActiveCfg = Release|Win32 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release|Win32.Build.0 = Release|Win32 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release|x64.ActiveCfg = Release|x64 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release|x64.Build.0 = Release|x64 - {4605418D-2292-470A-AB18-C2119B016F71}.Debug|Win32.ActiveCfg = Debug|Win32 - {4605418D-2292-470A-AB18-C2119B016F71}.Debug|Win32.Build.0 = Debug|Win32 - {4605418D-2292-470A-AB18-C2119B016F71}.Debug|x64.ActiveCfg = Debug|x64 - {4605418D-2292-470A-AB18-C2119B016F71}.Debug|x64.Build.0 = Debug|x64 - {4605418D-2292-470A-AB18-C2119B016F71}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {4605418D-2292-470A-AB18-C2119B016F71}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {4605418D-2292-470A-AB18-C2119B016F71}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {4605418D-2292-470A-AB18-C2119B016F71}.Release (static)|x64.Build.0 = Release (static)|x64 - {4605418D-2292-470A-AB18-C2119B016F71}.Release|Win32.ActiveCfg = Release|Win32 - {4605418D-2292-470A-AB18-C2119B016F71}.Release|Win32.Build.0 = Release|Win32 - {4605418D-2292-470A-AB18-C2119B016F71}.Release|x64.ActiveCfg = Release|x64 - {4605418D-2292-470A-AB18-C2119B016F71}.Release|x64.Build.0 = Release|x64 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Debug|Win32.ActiveCfg = Debug|Win32 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Debug|Win32.Build.0 = Debug|Win32 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Debug|x64.ActiveCfg = Debug|x64 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Debug|x64.Build.0 = Debug|x64 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release (static)|x64.Build.0 = Release (static)|x64 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release|Win32.ActiveCfg = Release|Win32 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release|Win32.Build.0 = Release|Win32 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release|x64.ActiveCfg = Release|x64 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release|x64.Build.0 = Release|x64 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Debug|Win32.ActiveCfg = Debug|Win32 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Debug|Win32.Build.0 = Debug|Win32 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Debug|x64.ActiveCfg = Debug|x64 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Debug|x64.Build.0 = Debug|x64 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release (static)|x64.Build.0 = Release (static)|x64 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release|Win32.ActiveCfg = Release|Win32 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release|Win32.Build.0 = Release|Win32 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release|x64.ActiveCfg = Release|x64 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release|x64.Build.0 = Release|x64 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Debug|Win32.ActiveCfg = Debug|Win32 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Debug|Win32.Build.0 = Debug|Win32 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Debug|x64.ActiveCfg = Debug|x64 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Debug|x64.Build.0 = Debug|x64 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release (static)|x64.Build.0 = Release (static)|x64 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release|Win32.ActiveCfg = Release|Win32 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release|Win32.Build.0 = Release|Win32 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release|x64.ActiveCfg = Release|x64 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release|x64.Build.0 = Release|x64 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Debug|Win32.ActiveCfg = Debug|Win32 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Debug|Win32.Build.0 = Debug|Win32 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Debug|x64.ActiveCfg = Debug|x64 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Debug|x64.Build.0 = Debug|x64 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release (static)|x64.Build.0 = Release (static)|x64 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release|Win32.ActiveCfg = Release|Win32 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release|Win32.Build.0 = Release|Win32 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release|x64.ActiveCfg = Release|x64 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release|x64.Build.0 = Release|x64 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Debug|Win32.ActiveCfg = Debug|Win32 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Debug|Win32.Build.0 = Debug|Win32 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Debug|x64.ActiveCfg = Debug|x64 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Debug|x64.Build.0 = Debug|x64 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release (static)|x64.Build.0 = Release (static)|x64 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release|Win32.ActiveCfg = Release|Win32 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release|Win32.Build.0 = Release|Win32 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release|x64.ActiveCfg = Release|x64 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release|x64.Build.0 = Release|x64 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Debug|Win32.ActiveCfg = Debug|Win32 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Debug|Win32.Build.0 = Debug|Win32 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Debug|x64.ActiveCfg = Debug|x64 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Debug|x64.Build.0 = Debug|x64 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release (static)|x64.Build.0 = Release (static)|x64 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release|Win32.ActiveCfg = Release|Win32 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release|Win32.Build.0 = Release|Win32 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release|x64.ActiveCfg = Release|x64 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release|x64.Build.0 = Release|x64 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Debug|Win32.ActiveCfg = Debug|Win32 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Debug|Win32.Build.0 = Debug|Win32 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Debug|x64.ActiveCfg = Debug|x64 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Debug|x64.Build.0 = Debug|x64 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release (static)|x64.Build.0 = Release (static)|x64 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release|Win32.ActiveCfg = Release|Win32 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release|Win32.Build.0 = Release|Win32 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release|x64.ActiveCfg = Release|x64 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release|x64.Build.0 = Release|x64 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Debug|Win32.ActiveCfg = Debug|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Debug|Win32.Build.0 = Debug|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Debug|x64.ActiveCfg = Debug|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release (static)|x64.Build.0 = Release (static)|x64 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release|Win32.ActiveCfg = Release|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release|Win32.Build.0 = Release|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release|x64.ActiveCfg = Release|x64 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release|x64.Build.0 = Release|x64 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Debug|Win32.ActiveCfg = Debug|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Debug|Win32.Build.0 = Debug|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Debug|x64.ActiveCfg = Debug|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release (static)|x64.Build.0 = Release (static)|x64 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release|Win32.ActiveCfg = Release|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release|Win32.Build.0 = Release|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release|x64.ActiveCfg = Release|x64 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release|x64.Build.0 = Release|x64 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Debug|Win32.ActiveCfg = Debug|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Debug|Win32.Build.0 = Debug|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Debug|x64.ActiveCfg = Debug|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release (static)|x64.Build.0 = Release (static)|x64 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release|Win32.ActiveCfg = Release|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release|Win32.Build.0 = Release|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release|x64.ActiveCfg = Release|x64 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release|x64.Build.0 = Release|x64 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Debug|Win32.ActiveCfg = Debug|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Debug|Win32.Build.0 = Debug|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Debug|x64.ActiveCfg = Debug|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release (static)|x64.Build.0 = Release (static)|x64 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release|Win32.ActiveCfg = Release|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release|Win32.Build.0 = Release|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release|x64.ActiveCfg = Release|x64 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release|x64.Build.0 = Release|x64 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Debug|Win32.ActiveCfg = Debug|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Debug|Win32.Build.0 = Debug|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Debug|x64.ActiveCfg = Debug|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release (static)|x64.Build.0 = Release (static)|x64 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release|Win32.ActiveCfg = Release|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release|Win32.Build.0 = Release|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release|x64.ActiveCfg = Release|x64 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release|x64.Build.0 = Release|x64 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Debug|Win32.ActiveCfg = Debug|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Debug|Win32.Build.0 = Debug|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Debug|x64.ActiveCfg = Debug|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release (static)|x64.Build.0 = Release (static)|x64 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release|Win32.ActiveCfg = Release|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release|Win32.Build.0 = Release|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release|x64.ActiveCfg = Release|x64 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release|x64.Build.0 = Release|x64 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Debug|Win32.ActiveCfg = Debug|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Debug|Win32.Build.0 = Debug|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Debug|x64.ActiveCfg = Debug|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release (static)|x64.Build.0 = Release (static)|x64 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release|Win32.ActiveCfg = Release|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release|Win32.Build.0 = Release|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release|x64.ActiveCfg = Release|x64 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release|x64.Build.0 = Release|x64 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Debug|Win32.ActiveCfg = Debug|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Debug|Win32.Build.0 = Debug|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Debug|x64.ActiveCfg = Debug|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release (static)|x64.Build.0 = Release (static)|x64 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release|Win32.ActiveCfg = Release|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release|Win32.Build.0 = Release|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release|x64.ActiveCfg = Release|x64 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release|x64.Build.0 = Release|x64 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Debug|Win32.ActiveCfg = Debug|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Debug|Win32.Build.0 = Debug|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Debug|x64.ActiveCfg = Debug|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release (static)|x64.Build.0 = Release (static)|x64 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release|Win32.ActiveCfg = Release|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release|Win32.Build.0 = Release|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release|x64.ActiveCfg = Release|x64 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release|x64.Build.0 = Release|x64 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Debug|Win32.ActiveCfg = Debug|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Debug|Win32.Build.0 = Debug|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Debug|x64.ActiveCfg = Debug|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release (static)|x64.Build.0 = Release (static)|x64 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release|Win32.ActiveCfg = Release|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release|Win32.Build.0 = Release|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release|x64.ActiveCfg = Release|x64 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release|x64.Build.0 = Release|x64 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Debug|Win32.ActiveCfg = Debug|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Debug|Win32.Build.0 = Debug|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Debug|x64.ActiveCfg = Debug|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release (static)|x64.Build.0 = Release (static)|x64 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release|Win32.ActiveCfg = Release|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release|Win32.Build.0 = Release|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release|x64.ActiveCfg = Release|x64 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release|x64.Build.0 = Release|x64 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Debug|Win32.ActiveCfg = Debug|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Debug|Win32.Build.0 = Debug|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Debug|x64.ActiveCfg = Debug|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release (static)|x64.Build.0 = Release (static)|x64 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release|Win32.ActiveCfg = Release|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release|Win32.Build.0 = Release|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release|x64.ActiveCfg = Release|x64 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release|x64.Build.0 = Release|x64 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Debug|Win32.ActiveCfg = Debug|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Debug|Win32.Build.0 = Debug|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Debug|x64.ActiveCfg = Debug|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release (static)|x64.Build.0 = Release (static)|x64 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release|Win32.ActiveCfg = Release|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release|Win32.Build.0 = Release|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release|x64.ActiveCfg = Release|x64 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release|x64.Build.0 = Release|x64 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Debug|Win32.ActiveCfg = Debug|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Debug|Win32.Build.0 = Debug|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Debug|x64.ActiveCfg = Debug|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release (static)|x64.Build.0 = Release (static)|x64 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release|Win32.ActiveCfg = Release|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release|Win32.Build.0 = Release|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release|x64.ActiveCfg = Release|x64 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release|x64.Build.0 = Release|x64 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Debug|Win32.ActiveCfg = Debug|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Debug|Win32.Build.0 = Debug|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Debug|x64.ActiveCfg = Debug|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release (static)|x64.Build.0 = Release (static)|x64 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release|Win32.ActiveCfg = Release|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release|Win32.Build.0 = Release|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release|x64.ActiveCfg = Release|x64 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/vc2005/QuakeForge-VS8.sln b/vc2005/QuakeForge-VS8.sln deleted file mode 100644 index de80d166d..000000000 --- a/vc2005/QuakeForge-VS8.sln +++ /dev/null @@ -1,966 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-client", "qfclient\qfclient.vcproj", "{9A942925-61E6-4975-935A-5D62E8248E64}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw", "qw\qw.vcproj", "{6ADA4322-693A-46BB-897B-17BB5BE9F08C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "net-chan", "net\net.vcproj", "{6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ruamoko", "ruamoko\ruamoko.vcproj", "{51028ACF-53D4-4478-8500-55E6B8A81375}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models-gl", "modelsgl\modelsgl.vcproj", "{1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "console", "console\console.vcproj", "{ED4AFBF5-C247-4352-966D-048B8998C6A1}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gib", "gib\gib.vcproj", "{01C3B138-9D45-4ED6-A763-893C067262C2}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "util", "util\util.vcproj", "{ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "engine", "engine\engine.vcproj", "{2626C0E1-6F5C-47D3-B80D-93942D766DD7}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "builtins", "builtins\builtins.vcproj", "{04FA9D77-E45F-4917-B972-B353BA6A6FA8}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sound", "sound\sound.vcproj", "{BF149D97-7520-4788-9CD1-3D99C5C8150F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "image", "image\image.vcproj", "{5203F034-0047-4EC0-B7E9-D037FAF5D536}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video", "video\video.vcproj", "{E7B3D07D-2FE8-481B-8DAB-6255A412A42F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-common", "common\common.vcproj", "{BC1F021A-1EEC-4A7A-B746-5AA6048E478C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-server", "qfserver\qfserver.vcproj", "{544D097C-9C24-4C57-A171-8C8029421185}" - ProjectSection(ProjectDependencies) = postProject - {6ADA4322-693A-46BB-897B-17BB5BE9F08C} = {6ADA4322-693A-46BB-897B-17BB5BE9F08C} - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} = {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A} = {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A} - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models", "models\models.vcproj", "{DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "console-client", "console_client\console_client.vcproj", "{226D42CE-5833-444E-94FA-84C1D51892A8}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "console-server", "console_server\console_server.vcproj", "{B37FE734-01F4-4799-86B2-D084820715BE}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qfbsp", "qfbsp\qfbsp.vcproj", "{B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qflight", "qflight\qflight.vcproj", "{5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qfvis", "qfvis\qfvis.vcproj", "{E5D842C5-669F-4FC7-A5E0-44B562F86435}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-master", "qw-master\qw-master.vcproj", "{445A2500-3BBC-449A-A929-C419C2A16051}" - ProjectSection(ProjectDependencies) = postProject - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qfmodelgen", "qfmodelgen\qfmodelgen.vcproj", "{052C34FE-C9E2-43ED-95DA-FB3F27B44E37}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qfprogs", "qfprogs\qfprogs.vcproj", "{5FA27C8E-51B1-445A-A375-FBE74F0984B1}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qfwavinfo", "qfwavinfo\qfwavinfo.vcproj", "{56BD559B-1590-4FC4-B441-AB1973BAC2BD}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wad", "wad\wad.vcproj", "{88422448-C5FB-46F3-A0B3-0811F90E537C}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pak", "pak\pak.vcproj", "{9EE8BD4B-47D3-4AD5-A8B9-831329792A05}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bsp2img", "bsp2img\bsp2img.vcproj", "{4605418D-2292-470A-AB18-C2119B016F71}" - ProjectSection(ProjectDependencies) = postProject - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qtv", "qtv\qtv.vcproj", "{05E68E3B-5901-43A9-981D-CF392C0F5C6C}" - ProjectSection(ProjectDependencies) = postProject - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {6ADA4322-693A-46BB-897B-17BB5BE9F08C} = {6ADA4322-693A-46BB-897B-17BB5BE9F08C} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq-common", "nq-common\nq-common.vcproj", "{6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq-wgl", "nq-wgl\nq-wgl.vcproj", "{1CD1A18B-95D5-4EA4-917C-34B10066BFCC}" - ProjectSection(ProjectDependencies) = postProject - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137} = {178D81A7-F2FB-41D7-B300-9D1A4DE5E137} - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} = {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} = {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} = {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} = {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} = {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "net-main", "net-main\net-main.vcproj", "{053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq-server", "nq-server\nq-server.vcproj", "{231C032C-DE16-459A-8E7D-6509C2EC3998}" - ProjectSection(ProjectDependencies) = postProject - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} = {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} = {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A} = {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hw-master", "hw-master\hw-master.vcproj", "{419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}" - ProjectSection(ProjectDependencies) = postProject - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{3CDA2798-7565-47C5-B972-F9E63DBEFFFA}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qfcc", "qfcc\qfcc.vcproj", "{40639893-4D75-48CD-811F-4B363CC71FFA}" - ProjectSection(ProjectDependencies) = postProject - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-client-wgl", "qw-client-wgl\qw-client-wgl.vcproj", "{5FD733BF-B3C6-4A96-BED7-35E2484448E1}" - ProjectSection(ProjectDependencies) = postProject - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} = {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {9A942925-61E6-4975-935A-5D62E8248E64} = {9A942925-61E6-4975-935A-5D62E8248E64} - {6ADA4322-693A-46BB-897B-17BB5BE9F08C} = {6ADA4322-693A-46BB-897B-17BB5BE9F08C} - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} = {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} = {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137} = {178D81A7-F2FB-41D7-B300-9D1A4DE5E137} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-client-sgl", "qw-client-sgl\qw-client-sgl.vcproj", "{649C4168-1C65-4E41-818F-85A1C52C1B8F}" - ProjectSection(ProjectDependencies) = postProject - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} = {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} - {9A942925-61E6-4975-935A-5D62E8248E64} = {9A942925-61E6-4975-935A-5D62E8248E64} - {6ADA4322-693A-46BB-897B-17BB5BE9F08C} = {6ADA4322-693A-46BB-897B-17BB5BE9F08C} - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} = {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} = {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} - {44A18410-3AA8-4A64-935B-D20223AD6885} = {44A18410-3AA8-4A64-935B-D20223AD6885} - {7E30C3B1-AEE7-483D-B231-C672365CD2D7} = {7E30C3B1-AEE7-483D-B231-C672365CD2D7} - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video-gl", "videogl\videogl.vcproj", "{C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video-sdl", "video-sdl\video-sdl.vcproj", "{44A18410-3AA8-4A64-935B-D20223AD6885}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-client-sdl", "qw-client-sdl\qw-client-sdl.vcproj", "{B44CB3E0-F2FD-4260-A632-C01FB881613E}" - ProjectSection(ProjectDependencies) = postProject - {66D3A191-E4D5-45F3-86BD-EFBB249CD554} = {66D3A191-E4D5-45F3-86BD-EFBB249CD554} - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - {44A18410-3AA8-4A64-935B-D20223AD6885} = {44A18410-3AA8-4A64-935B-D20223AD6885} - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} = {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} - {6ADA4322-693A-46BB-897B-17BB5BE9F08C} = {6ADA4322-693A-46BB-897B-17BB5BE9F08C} - {9A942925-61E6-4975-935A-5D62E8248E64} = {9A942925-61E6-4975-935A-5D62E8248E64} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {F4A9881E-0EB0-44A1-9664-B6CBDE992861} = {F4A9881E-0EB0-44A1-9664-B6CBDE992861} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models-sw", "models-sw\models-sw.vcproj", "{F4A9881E-0EB0-44A1-9664-B6CBDE992861}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video-sw", "video-sw\video-sw.vcproj", "{66D3A191-E4D5-45F3-86BD-EFBB249CD554}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-client-sdl32", "qw-client-sdl32\qw-client-sdl32.vcproj", "{CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}" - ProjectSection(ProjectDependencies) = postProject - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - {44A18410-3AA8-4A64-935B-D20223AD6885} = {44A18410-3AA8-4A64-935B-D20223AD6885} - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} = {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} - {F4A9881E-0EB0-44A1-9664-B6CBDE992861} = {F4A9881E-0EB0-44A1-9664-B6CBDE992861} - {6ADA4322-693A-46BB-897B-17BB5BE9F08C} = {6ADA4322-693A-46BB-897B-17BB5BE9F08C} - {9A942925-61E6-4975-935A-5D62E8248E64} = {9A942925-61E6-4975-935A-5D62E8248E64} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {D5B9558F-EF25-4167-ACE4-723C7413B390} = {D5B9558F-EF25-4167-ACE4-723C7413B390} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video-sw32", "video-sw32\video-sw32.vcproj", "{D5B9558F-EF25-4167-ACE4-723C7413B390}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq", "nq\nq.vcproj", "{155112B9-A8A9-4E06-90F5-4AAAB32B2F70}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video-wgl", "video-wgl\video-wgl.vcproj", "{178D81A7-F2FB-41D7-B300-9D1A4DE5E137}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video-sgl", "video-sgl\video-sgl.vcproj", "{7E30C3B1-AEE7-483D-B231-C672365CD2D7}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq-sgl", "nq-sgl\nq-sgl.vcproj", "{2E26DB8B-9E37-4072-B397-8A7086E0ACD0}" - ProjectSection(ProjectDependencies) = postProject - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} = {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {7E30C3B1-AEE7-483D-B231-C672365CD2D7} = {7E30C3B1-AEE7-483D-B231-C672365CD2D7} - {44A18410-3AA8-4A64-935B-D20223AD6885} = {44A18410-3AA8-4A64-935B-D20223AD6885} - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} = {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} = {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} = {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} = {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq-sdl", "nq-sdl\nq-sdl.vcproj", "{2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}" - ProjectSection(ProjectDependencies) = postProject - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} = {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} = {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {66D3A191-E4D5-45F3-86BD-EFBB249CD554} = {66D3A191-E4D5-45F3-86BD-EFBB249CD554} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} = {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {F4A9881E-0EB0-44A1-9664-B6CBDE992861} = {F4A9881E-0EB0-44A1-9664-B6CBDE992861} - {44A18410-3AA8-4A64-935B-D20223AD6885} = {44A18410-3AA8-4A64-935B-D20223AD6885} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq-sdl32", "nq-sdl32\nq-sdl32.vcproj", "{2CEB1965-A89C-4422-A9AC-B30FCE7913C3}" - ProjectSection(ProjectDependencies) = postProject - {44A18410-3AA8-4A64-935B-D20223AD6885} = {44A18410-3AA8-4A64-935B-D20223AD6885} - {F4A9881E-0EB0-44A1-9664-B6CBDE992861} = {F4A9881E-0EB0-44A1-9664-B6CBDE992861} - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} = {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {D5B9558F-EF25-4167-ACE4-723C7413B390} = {D5B9558F-EF25-4167-ACE4-723C7413B390} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} = {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} = {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release (static)|Win32 = Release (static)|Win32 - Release (static)|x64 = Release (static)|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9A942925-61E6-4975-935A-5D62E8248E64}.Debug|Win32.ActiveCfg = Debug|Win32 - {9A942925-61E6-4975-935A-5D62E8248E64}.Debug|Win32.Build.0 = Debug|Win32 - {9A942925-61E6-4975-935A-5D62E8248E64}.Debug|x64.ActiveCfg = Debug|x64 - {9A942925-61E6-4975-935A-5D62E8248E64}.Debug|x64.Build.0 = Debug|x64 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release (static)|x64.Build.0 = Release (static)|x64 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release|Win32.ActiveCfg = Release|Win32 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release|Win32.Build.0 = Release|Win32 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release|x64.ActiveCfg = Release|x64 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release|x64.Build.0 = Release|x64 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Debug|Win32.ActiveCfg = Debug|Win32 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Debug|Win32.Build.0 = Debug|Win32 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Debug|x64.ActiveCfg = Debug|x64 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Debug|x64.Build.0 = Debug|x64 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release (static)|x64.Build.0 = Release (static)|x64 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release|Win32.ActiveCfg = Release|Win32 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release|Win32.Build.0 = Release|Win32 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release|x64.ActiveCfg = Release|x64 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release|x64.Build.0 = Release|x64 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Debug|Win32.ActiveCfg = Debug|Win32 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Debug|Win32.Build.0 = Debug|Win32 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Debug|x64.ActiveCfg = Debug|x64 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Debug|x64.Build.0 = Debug|x64 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release (static)|x64.Build.0 = Release (static)|x64 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release|Win32.ActiveCfg = Release|Win32 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release|Win32.Build.0 = Release|Win32 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release|x64.ActiveCfg = Release|x64 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release|x64.Build.0 = Release|x64 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Debug|Win32.ActiveCfg = Debug|Win32 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Debug|Win32.Build.0 = Debug|Win32 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Debug|x64.ActiveCfg = Debug|x64 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Debug|x64.Build.0 = Debug|x64 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release (static)|x64.Build.0 = Release (static)|x64 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release|Win32.ActiveCfg = Release|Win32 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release|Win32.Build.0 = Release|Win32 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release|x64.ActiveCfg = Release|x64 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release|x64.Build.0 = Release|x64 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Debug|Win32.ActiveCfg = Debug|Win32 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Debug|Win32.Build.0 = Debug|Win32 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Debug|x64.ActiveCfg = Debug|x64 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Debug|x64.Build.0 = Debug|x64 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release (static)|x64.Build.0 = Release (static)|x64 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release|Win32.ActiveCfg = Release|Win32 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release|Win32.Build.0 = Release|Win32 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release|x64.ActiveCfg = Release|x64 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release|x64.Build.0 = Release|x64 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Debug|Win32.ActiveCfg = Debug|Win32 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Debug|Win32.Build.0 = Debug|Win32 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Debug|x64.ActiveCfg = Debug|x64 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Debug|x64.Build.0 = Debug|x64 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release (static)|x64.Build.0 = Release (static)|x64 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release|Win32.ActiveCfg = Release|Win32 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release|Win32.Build.0 = Release|Win32 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release|x64.ActiveCfg = Release|x64 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release|x64.Build.0 = Release|x64 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Debug|Win32.ActiveCfg = Debug|Win32 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Debug|Win32.Build.0 = Debug|Win32 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Debug|x64.ActiveCfg = Debug|x64 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Debug|x64.Build.0 = Debug|x64 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release (static)|x64.Build.0 = Release (static)|x64 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release|Win32.ActiveCfg = Release|Win32 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release|Win32.Build.0 = Release|Win32 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release|x64.ActiveCfg = Release|x64 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release|x64.Build.0 = Release|x64 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Debug|Win32.ActiveCfg = Debug|Win32 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Debug|Win32.Build.0 = Debug|Win32 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Debug|x64.ActiveCfg = Debug|x64 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Debug|x64.Build.0 = Debug|x64 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release (static)|x64.Build.0 = Release (static)|x64 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release|Win32.ActiveCfg = Release|Win32 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release|Win32.Build.0 = Release|Win32 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release|x64.ActiveCfg = Release|x64 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release|x64.Build.0 = Release|x64 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Debug|Win32.ActiveCfg = Debug|Win32 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Debug|Win32.Build.0 = Debug|Win32 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Debug|x64.ActiveCfg = Debug|x64 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Debug|x64.Build.0 = Debug|x64 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release (static)|x64.Build.0 = Release (static)|x64 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release|Win32.ActiveCfg = Release|Win32 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release|Win32.Build.0 = Release|Win32 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release|x64.ActiveCfg = Release|x64 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release|x64.Build.0 = Release|x64 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Debug|Win32.ActiveCfg = Debug|Win32 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Debug|Win32.Build.0 = Debug|Win32 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Debug|x64.ActiveCfg = Debug|x64 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Debug|x64.Build.0 = Debug|x64 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release (static)|x64.Build.0 = Release (static)|x64 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release|Win32.ActiveCfg = Release|Win32 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release|Win32.Build.0 = Release|Win32 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release|x64.ActiveCfg = Release|x64 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release|x64.Build.0 = Release|x64 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Debug|Win32.ActiveCfg = Debug|Win32 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Debug|Win32.Build.0 = Debug|Win32 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Debug|x64.ActiveCfg = Debug|x64 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Debug|x64.Build.0 = Debug|x64 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release (static)|x64.Build.0 = Release (static)|x64 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release|Win32.ActiveCfg = Release|Win32 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release|Win32.Build.0 = Release|Win32 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release|x64.ActiveCfg = Release|x64 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release|x64.Build.0 = Release|x64 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Debug|Win32.ActiveCfg = Debug|Win32 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Debug|Win32.Build.0 = Debug|Win32 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Debug|x64.ActiveCfg = Debug|x64 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Debug|x64.Build.0 = Debug|x64 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release (static)|x64.Build.0 = Release (static)|x64 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release|Win32.ActiveCfg = Release|Win32 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release|Win32.Build.0 = Release|Win32 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release|x64.ActiveCfg = Release|x64 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release|x64.Build.0 = Release|x64 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Debug|Win32.ActiveCfg = Debug|Win32 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Debug|Win32.Build.0 = Debug|Win32 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Debug|x64.ActiveCfg = Debug|x64 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Debug|x64.Build.0 = Debug|x64 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release (static)|x64.Build.0 = Release (static)|x64 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release|Win32.ActiveCfg = Release|Win32 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release|Win32.Build.0 = Release|Win32 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release|x64.ActiveCfg = Release|x64 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release|x64.Build.0 = Release|x64 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Debug|Win32.ActiveCfg = Debug|Win32 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Debug|Win32.Build.0 = Debug|Win32 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Debug|x64.ActiveCfg = Debug|x64 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Debug|x64.Build.0 = Debug|x64 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release (static)|x64.Build.0 = Release (static)|x64 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release|Win32.ActiveCfg = Release|Win32 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release|Win32.Build.0 = Release|Win32 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release|x64.ActiveCfg = Release|x64 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release|x64.Build.0 = Release|x64 - {544D097C-9C24-4C57-A171-8C8029421185}.Debug|Win32.ActiveCfg = Debug|Win32 - {544D097C-9C24-4C57-A171-8C8029421185}.Debug|Win32.Build.0 = Debug|Win32 - {544D097C-9C24-4C57-A171-8C8029421185}.Debug|x64.ActiveCfg = Debug|x64 - {544D097C-9C24-4C57-A171-8C8029421185}.Debug|x64.Build.0 = Debug|x64 - {544D097C-9C24-4C57-A171-8C8029421185}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {544D097C-9C24-4C57-A171-8C8029421185}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {544D097C-9C24-4C57-A171-8C8029421185}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {544D097C-9C24-4C57-A171-8C8029421185}.Release (static)|x64.Build.0 = Release (static)|x64 - {544D097C-9C24-4C57-A171-8C8029421185}.Release|Win32.ActiveCfg = Release|Win32 - {544D097C-9C24-4C57-A171-8C8029421185}.Release|Win32.Build.0 = Release|Win32 - {544D097C-9C24-4C57-A171-8C8029421185}.Release|x64.ActiveCfg = Release|x64 - {544D097C-9C24-4C57-A171-8C8029421185}.Release|x64.Build.0 = Release|x64 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Debug|Win32.ActiveCfg = Debug|Win32 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Debug|Win32.Build.0 = Debug|Win32 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Debug|x64.ActiveCfg = Debug|x64 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Debug|x64.Build.0 = Debug|x64 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release (static)|x64.Build.0 = Release (static)|x64 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release|Win32.ActiveCfg = Release|Win32 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release|Win32.Build.0 = Release|Win32 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release|x64.ActiveCfg = Release|x64 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release|x64.Build.0 = Release|x64 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Debug|Win32.ActiveCfg = Debug|Win32 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Debug|Win32.Build.0 = Debug|Win32 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Debug|x64.ActiveCfg = Debug|x64 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Debug|x64.Build.0 = Debug|x64 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release (static)|x64.Build.0 = Release (static)|x64 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release|Win32.ActiveCfg = Release|Win32 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release|Win32.Build.0 = Release|Win32 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release|x64.ActiveCfg = Release|x64 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release|x64.Build.0 = Release|x64 - {B37FE734-01F4-4799-86B2-D084820715BE}.Debug|Win32.ActiveCfg = Debug|Win32 - {B37FE734-01F4-4799-86B2-D084820715BE}.Debug|Win32.Build.0 = Debug|Win32 - {B37FE734-01F4-4799-86B2-D084820715BE}.Debug|x64.ActiveCfg = Debug|x64 - {B37FE734-01F4-4799-86B2-D084820715BE}.Debug|x64.Build.0 = Debug|x64 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release (static)|x64.Build.0 = Release (static)|x64 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release|Win32.ActiveCfg = Release|Win32 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release|Win32.Build.0 = Release|Win32 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release|x64.ActiveCfg = Release|x64 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release|x64.Build.0 = Release|x64 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Debug|Win32.ActiveCfg = Debug|Win32 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Debug|Win32.Build.0 = Debug|Win32 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Debug|x64.ActiveCfg = Debug|x64 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Debug|x64.Build.0 = Debug|x64 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release (static)|x64.Build.0 = Release (static)|x64 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release|Win32.ActiveCfg = Release|Win32 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release|Win32.Build.0 = Release|Win32 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release|x64.ActiveCfg = Release|x64 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release|x64.Build.0 = Release|x64 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Debug|Win32.ActiveCfg = Debug|Win32 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Debug|Win32.Build.0 = Debug|Win32 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Debug|x64.ActiveCfg = Debug|x64 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Debug|x64.Build.0 = Debug|x64 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release (static)|x64.Build.0 = Release (static)|x64 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release|Win32.ActiveCfg = Release|Win32 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release|Win32.Build.0 = Release|Win32 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release|x64.ActiveCfg = Release|x64 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release|x64.Build.0 = Release|x64 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Debug|Win32.ActiveCfg = Debug|Win32 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Debug|Win32.Build.0 = Debug|Win32 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Debug|x64.ActiveCfg = Debug|x64 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Debug|x64.Build.0 = Debug|x64 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release (static)|x64.Build.0 = Release (static)|x64 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release|Win32.ActiveCfg = Release|Win32 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release|Win32.Build.0 = Release|Win32 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release|x64.ActiveCfg = Release|x64 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release|x64.Build.0 = Release|x64 - {445A2500-3BBC-449A-A929-C419C2A16051}.Debug|Win32.ActiveCfg = Debug|Win32 - {445A2500-3BBC-449A-A929-C419C2A16051}.Debug|Win32.Build.0 = Debug|Win32 - {445A2500-3BBC-449A-A929-C419C2A16051}.Debug|x64.ActiveCfg = Debug|x64 - {445A2500-3BBC-449A-A929-C419C2A16051}.Debug|x64.Build.0 = Debug|x64 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release (static)|x64.Build.0 = Release (static)|x64 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release|Win32.ActiveCfg = Release|Win32 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release|Win32.Build.0 = Release|Win32 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release|x64.ActiveCfg = Release|x64 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release|x64.Build.0 = Release|x64 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Debug|Win32.ActiveCfg = Debug|Win32 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Debug|Win32.Build.0 = Debug|Win32 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Debug|x64.ActiveCfg = Debug|x64 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Debug|x64.Build.0 = Debug|x64 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release (static)|x64.Build.0 = Release (static)|x64 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release|Win32.ActiveCfg = Release|Win32 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release|Win32.Build.0 = Release|Win32 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release|x64.ActiveCfg = Release|x64 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release|x64.Build.0 = Release|x64 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Debug|Win32.ActiveCfg = Debug|Win32 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Debug|Win32.Build.0 = Debug|Win32 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Debug|x64.ActiveCfg = Debug|x64 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Debug|x64.Build.0 = Debug|x64 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release (static)|x64.Build.0 = Release (static)|x64 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release|Win32.ActiveCfg = Release|Win32 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release|Win32.Build.0 = Release|Win32 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release|x64.ActiveCfg = Release|x64 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release|x64.Build.0 = Release|x64 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Debug|Win32.ActiveCfg = Debug|Win32 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Debug|Win32.Build.0 = Debug|Win32 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Debug|x64.ActiveCfg = Debug|x64 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Debug|x64.Build.0 = Debug|x64 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release (static)|x64.Build.0 = Release (static)|x64 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release|Win32.ActiveCfg = Release|Win32 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release|Win32.Build.0 = Release|Win32 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release|x64.ActiveCfg = Release|x64 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release|x64.Build.0 = Release|x64 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Debug|Win32.ActiveCfg = Debug|Win32 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Debug|Win32.Build.0 = Debug|Win32 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Debug|x64.ActiveCfg = Debug|x64 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Debug|x64.Build.0 = Debug|x64 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release (static)|x64.Build.0 = Release (static)|x64 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release|Win32.ActiveCfg = Release|Win32 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release|Win32.Build.0 = Release|Win32 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release|x64.ActiveCfg = Release|x64 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release|x64.Build.0 = Release|x64 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Debug|Win32.ActiveCfg = Debug|Win32 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Debug|Win32.Build.0 = Debug|Win32 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Debug|x64.ActiveCfg = Debug|x64 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Debug|x64.Build.0 = Debug|x64 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release (static)|x64.Build.0 = Release (static)|x64 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release|Win32.ActiveCfg = Release|Win32 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release|Win32.Build.0 = Release|Win32 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release|x64.ActiveCfg = Release|x64 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release|x64.Build.0 = Release|x64 - {4605418D-2292-470A-AB18-C2119B016F71}.Debug|Win32.ActiveCfg = Debug|Win32 - {4605418D-2292-470A-AB18-C2119B016F71}.Debug|Win32.Build.0 = Debug|Win32 - {4605418D-2292-470A-AB18-C2119B016F71}.Debug|x64.ActiveCfg = Debug|x64 - {4605418D-2292-470A-AB18-C2119B016F71}.Debug|x64.Build.0 = Debug|x64 - {4605418D-2292-470A-AB18-C2119B016F71}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {4605418D-2292-470A-AB18-C2119B016F71}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {4605418D-2292-470A-AB18-C2119B016F71}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {4605418D-2292-470A-AB18-C2119B016F71}.Release (static)|x64.Build.0 = Release (static)|x64 - {4605418D-2292-470A-AB18-C2119B016F71}.Release|Win32.ActiveCfg = Release|Win32 - {4605418D-2292-470A-AB18-C2119B016F71}.Release|Win32.Build.0 = Release|Win32 - {4605418D-2292-470A-AB18-C2119B016F71}.Release|x64.ActiveCfg = Release|x64 - {4605418D-2292-470A-AB18-C2119B016F71}.Release|x64.Build.0 = Release|x64 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Debug|Win32.ActiveCfg = Debug|Win32 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Debug|Win32.Build.0 = Debug|Win32 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Debug|x64.ActiveCfg = Debug|x64 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Debug|x64.Build.0 = Debug|x64 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release (static)|x64.Build.0 = Release (static)|x64 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release|Win32.ActiveCfg = Release|Win32 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release|Win32.Build.0 = Release|Win32 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release|x64.ActiveCfg = Release|x64 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release|x64.Build.0 = Release|x64 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Debug|Win32.ActiveCfg = Debug|Win32 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Debug|Win32.Build.0 = Debug|Win32 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Debug|x64.ActiveCfg = Debug|x64 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Debug|x64.Build.0 = Debug|x64 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release (static)|x64.Build.0 = Release (static)|x64 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release|Win32.ActiveCfg = Release|Win32 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release|Win32.Build.0 = Release|Win32 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release|x64.ActiveCfg = Release|x64 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release|x64.Build.0 = Release|x64 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Debug|Win32.ActiveCfg = Debug|Win32 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Debug|Win32.Build.0 = Debug|Win32 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Debug|x64.ActiveCfg = Debug|x64 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Debug|x64.Build.0 = Debug|x64 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release (static)|x64.Build.0 = Release (static)|x64 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release|Win32.ActiveCfg = Release|Win32 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release|Win32.Build.0 = Release|Win32 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release|x64.ActiveCfg = Release|x64 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release|x64.Build.0 = Release|x64 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Debug|Win32.ActiveCfg = Debug|Win32 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Debug|Win32.Build.0 = Debug|Win32 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Debug|x64.ActiveCfg = Debug|x64 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Debug|x64.Build.0 = Debug|x64 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release (static)|x64.Build.0 = Release (static)|x64 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release|Win32.ActiveCfg = Release|Win32 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release|Win32.Build.0 = Release|Win32 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release|x64.ActiveCfg = Release|x64 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release|x64.Build.0 = Release|x64 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Debug|Win32.ActiveCfg = Debug|Win32 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Debug|Win32.Build.0 = Debug|Win32 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Debug|x64.ActiveCfg = Debug|x64 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Debug|x64.Build.0 = Debug|x64 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release (static)|x64.Build.0 = Release (static)|x64 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release|Win32.ActiveCfg = Release|Win32 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release|Win32.Build.0 = Release|Win32 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release|x64.ActiveCfg = Release|x64 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release|x64.Build.0 = Release|x64 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Debug|Win32.ActiveCfg = Debug|Win32 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Debug|Win32.Build.0 = Debug|Win32 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Debug|x64.ActiveCfg = Debug|x64 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Debug|x64.Build.0 = Debug|x64 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release (static)|x64.Build.0 = Release (static)|x64 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release|Win32.ActiveCfg = Release|Win32 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release|Win32.Build.0 = Release|Win32 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release|x64.ActiveCfg = Release|x64 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release|x64.Build.0 = Release|x64 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Debug|Win32.ActiveCfg = Debug|Win32 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Debug|Win32.Build.0 = Debug|Win32 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Debug|x64.ActiveCfg = Debug|x64 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Debug|x64.Build.0 = Debug|x64 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release (static)|x64.Build.0 = Release (static)|x64 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release|Win32.ActiveCfg = Release|Win32 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release|Win32.Build.0 = Release|Win32 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release|x64.ActiveCfg = Release|x64 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release|x64.Build.0 = Release|x64 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Debug|Win32.ActiveCfg = Debug|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Debug|Win32.Build.0 = Debug|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Debug|x64.ActiveCfg = Debug|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release (static)|x64.Build.0 = Release (static)|x64 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release|Win32.ActiveCfg = Release|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release|Win32.Build.0 = Release|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release|x64.ActiveCfg = Release|x64 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release|x64.Build.0 = Release|x64 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Debug|Win32.ActiveCfg = Debug|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Debug|Win32.Build.0 = Debug|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Debug|x64.ActiveCfg = Debug|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release (static)|x64.Build.0 = Release (static)|x64 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release|Win32.ActiveCfg = Release|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release|Win32.Build.0 = Release|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release|x64.ActiveCfg = Release|x64 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release|x64.Build.0 = Release|x64 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Debug|Win32.ActiveCfg = Debug|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Debug|Win32.Build.0 = Debug|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Debug|x64.ActiveCfg = Debug|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release (static)|x64.Build.0 = Release (static)|x64 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release|Win32.ActiveCfg = Release|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release|Win32.Build.0 = Release|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release|x64.ActiveCfg = Release|x64 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release|x64.Build.0 = Release|x64 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Debug|Win32.ActiveCfg = Debug|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Debug|Win32.Build.0 = Debug|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Debug|x64.ActiveCfg = Debug|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release (static)|x64.Build.0 = Release (static)|x64 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release|Win32.ActiveCfg = Release|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release|Win32.Build.0 = Release|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release|x64.ActiveCfg = Release|x64 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release|x64.Build.0 = Release|x64 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Debug|Win32.ActiveCfg = Debug|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Debug|Win32.Build.0 = Debug|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Debug|x64.ActiveCfg = Debug|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release (static)|x64.Build.0 = Release (static)|x64 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release|Win32.ActiveCfg = Release|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release|Win32.Build.0 = Release|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release|x64.ActiveCfg = Release|x64 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release|x64.Build.0 = Release|x64 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Debug|Win32.ActiveCfg = Debug|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Debug|Win32.Build.0 = Debug|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Debug|x64.ActiveCfg = Debug|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release (static)|x64.Build.0 = Release (static)|x64 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release|Win32.ActiveCfg = Release|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release|Win32.Build.0 = Release|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release|x64.ActiveCfg = Release|x64 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release|x64.Build.0 = Release|x64 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Debug|Win32.ActiveCfg = Debug|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Debug|Win32.Build.0 = Debug|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Debug|x64.ActiveCfg = Debug|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release (static)|x64.Build.0 = Release (static)|x64 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release|Win32.ActiveCfg = Release|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release|Win32.Build.0 = Release|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release|x64.ActiveCfg = Release|x64 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release|x64.Build.0 = Release|x64 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Debug|Win32.ActiveCfg = Debug|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Debug|Win32.Build.0 = Debug|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Debug|x64.ActiveCfg = Debug|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release (static)|x64.Build.0 = Release (static)|x64 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release|Win32.ActiveCfg = Release|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release|Win32.Build.0 = Release|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release|x64.ActiveCfg = Release|x64 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release|x64.Build.0 = Release|x64 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Debug|Win32.ActiveCfg = Debug|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Debug|Win32.Build.0 = Debug|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Debug|x64.ActiveCfg = Debug|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release (static)|x64.Build.0 = Release (static)|x64 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release|Win32.ActiveCfg = Release|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release|Win32.Build.0 = Release|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release|x64.ActiveCfg = Release|x64 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release|x64.Build.0 = Release|x64 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Debug|Win32.ActiveCfg = Debug|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Debug|Win32.Build.0 = Debug|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Debug|x64.ActiveCfg = Debug|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release (static)|x64.Build.0 = Release (static)|x64 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release|Win32.ActiveCfg = Release|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release|Win32.Build.0 = Release|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release|x64.ActiveCfg = Release|x64 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release|x64.Build.0 = Release|x64 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Debug|Win32.ActiveCfg = Debug|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Debug|Win32.Build.0 = Debug|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Debug|x64.ActiveCfg = Debug|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release (static)|x64.Build.0 = Release (static)|x64 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release|Win32.ActiveCfg = Release|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release|Win32.Build.0 = Release|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release|x64.ActiveCfg = Release|x64 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release|x64.Build.0 = Release|x64 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Debug|Win32.ActiveCfg = Debug|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Debug|Win32.Build.0 = Debug|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Debug|x64.ActiveCfg = Debug|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release (static)|x64.Build.0 = Release (static)|x64 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release|Win32.ActiveCfg = Release|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release|Win32.Build.0 = Release|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release|x64.ActiveCfg = Release|x64 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release|x64.Build.0 = Release|x64 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Debug|Win32.ActiveCfg = Debug|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Debug|Win32.Build.0 = Debug|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Debug|x64.ActiveCfg = Debug|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release (static)|x64.Build.0 = Release (static)|x64 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release|Win32.ActiveCfg = Release|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release|Win32.Build.0 = Release|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release|x64.ActiveCfg = Release|x64 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release|x64.Build.0 = Release|x64 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Debug|Win32.ActiveCfg = Debug|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Debug|Win32.Build.0 = Debug|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Debug|x64.ActiveCfg = Debug|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release (static)|x64.Build.0 = Release (static)|x64 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release|Win32.ActiveCfg = Release|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release|Win32.Build.0 = Release|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release|x64.ActiveCfg = Release|x64 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release|x64.Build.0 = Release|x64 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Debug|Win32.ActiveCfg = Debug|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Debug|Win32.Build.0 = Debug|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Debug|x64.ActiveCfg = Debug|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release (static)|x64.Build.0 = Release (static)|x64 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release|Win32.ActiveCfg = Release|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release|Win32.Build.0 = Release|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release|x64.ActiveCfg = Release|x64 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {6ADA4322-693A-46BB-897B-17BB5BE9F08C} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {B37FE734-01F4-4799-86B2-D084820715BE} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {9A942925-61E6-4975-935A-5D62E8248E64} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {44A18410-3AA8-4A64-935B-D20223AD6885} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {F4A9881E-0EB0-44A1-9664-B6CBDE992861} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {66D3A191-E4D5-45F3-86BD-EFBB249CD554} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {D5B9558F-EF25-4167-ACE4-723C7413B390} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {7E30C3B1-AEE7-483D-B231-C672365CD2D7} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - EndGlobalSection -EndGlobal diff --git a/vc2005/bsp2img/bsp2img.vcproj b/vc2005/bsp2img/bsp2img.vcproj deleted file mode 100644 index 49beb4a45..000000000 --- a/vc2005/bsp2img/bsp2img.vcproj +++ /dev/null @@ -1,567 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/builtins/builtins.vcproj b/vc2005/builtins/builtins.vcproj deleted file mode 100644 index 63e384c61..000000000 --- a/vc2005/builtins/builtins.vcproj +++ /dev/null @@ -1,473 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/clean.ps1 b/vc2005/clean.ps1 deleted file mode 100644 index b1e75f990..000000000 --- a/vc2005/clean.ps1 +++ /dev/null @@ -1,3 +0,0 @@ -ls -recurse -include 'win32','x64','*.user' | rm -recurse -if(test-path QuakeForge.suo) { rm QuakeForge.suo -force } -if(test-path QuakeForge.ncb) { rm QuakeForge.ncb } \ No newline at end of file diff --git a/vc2005/common/common.vcproj b/vc2005/common/common.vcproj deleted file mode 100644 index e473c7cce..000000000 --- a/vc2005/common/common.vcproj +++ /dev/null @@ -1,489 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/console/console.vcproj b/vc2005/console/console.vcproj deleted file mode 100644 index b71cf236e..000000000 --- a/vc2005/console/console.vcproj +++ /dev/null @@ -1,493 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/console_client/console_client.vcproj b/vc2005/console_client/console_client.vcproj deleted file mode 100644 index b6ae66820..000000000 --- a/vc2005/console_client/console_client.vcproj +++ /dev/null @@ -1,481 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/console_server/console_server.vcproj b/vc2005/console_server/console_server.vcproj deleted file mode 100644 index 4c3347b3a..000000000 --- a/vc2005/console_server/console_server.vcproj +++ /dev/null @@ -1,469 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/engine/engine.vcproj b/vc2005/engine/engine.vcproj deleted file mode 100644 index f3b7665ff..000000000 --- a/vc2005/engine/engine.vcproj +++ /dev/null @@ -1,509 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/gib/gib.vcproj b/vc2005/gib/gib.vcproj deleted file mode 100644 index 4039956cb..000000000 --- a/vc2005/gib/gib.vcproj +++ /dev/null @@ -1,537 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/hw-master/hw-master.vcproj b/vc2005/hw-master/hw-master.vcproj deleted file mode 100644 index 133d54777..000000000 --- a/vc2005/hw-master/hw-master.vcproj +++ /dev/null @@ -1,567 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/image/image.vcproj b/vc2005/image/image.vcproj deleted file mode 100644 index e32ffd958..000000000 --- a/vc2005/image/image.vcproj +++ /dev/null @@ -1,481 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/include/config.h b/vc2005/include/config.h deleted file mode 100644 index 1b787db16..000000000 --- a/vc2005/include/config.h +++ /dev/null @@ -1,740 +0,0 @@ -/* include/config.h. Generated from config.h.in by configure. */ -/* include/config.h.in. Generated from configure.ac by autoheader. */ - -/* list of cd plugins */ -#define CD_PLUGIN_LIST {"cd_win", cd_win_PluginInfo},{0, 0} - -/* list of cd prototypes */ -#define CD_PLUGIN_PROTOS extern plugin_t *cd_win_PluginInfo (void); - -/* list of client plugins */ -#define CLIENT_PLUGIN_LIST {"console_client", console_client_PluginInfo},{0, 0} - -/* list of client prototypes */ -#define CLIENT_PLUGIN_PROTOS extern plugin_t *console_client_PluginInfo (void); - -/* Define this to the command line for the C preprocessor */ -#define CPP_NAME "wave --c99 %d -o %o %i" - -/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP - systems. This function is required for `alloca.c' support on those systems. - */ -/* #undef CRAY_STACKSEG_END */ - -/* Define to 1 if using `alloca.c'. */ -/* #undef C_ALLOCA */ - -/* Define this to the location of the global config file */ -#define FS_GLOBALCFG "~/quakeforge.conf" - -/* Define this to the path from which to load plugins */ -#define FS_PLUGINPATH "/usr/local/lib/quakeforge" - -/* Define this to the shared game directory root */ -#define FS_SHAREPATH "." - -/* Define this to the location of the user config file */ -#define FS_USERCFG "~/quakeforgerc" - -/* Define this to the unshared game directory root */ -#define FS_USERPATH "." - -/* Define this to the default GL dynamic lib */ -#define GL_DRIVER "OPENGL32.DLL" - -/* Define to 1 if you have the `access' function. */ -#define HAVE_ACCESS 1 - -/* Define to 1 if you have `alloca', as a function or macro. */ -#define HAVE_ALLOCA 1 - -/* Define to 1 if you have and it should be used (not on Ultrix). - */ -/* #undef HAVE_ALLOCA_H */ - -/* Define this if alloca is prototyped */ -/* #undef HAVE_ALLOCA_PROTO */ -#ifndef HAVE_ALLOCA_PROTO -#ifndef QFASM -void *alloca (int size); -#endif -#endif - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_ALSA_ASOUNDLIB_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_ARPA_INET_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_ASM_IO_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_ASSERT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_CONIO_H 1 - -/* Define to 1 if you have the `connect' function. */ -#define HAVE_CONNECT 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_CTYPE_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_CURSES_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_DDRAW_H 1 - -/* Define if you have the XFree86 DGA extension */ -/* #undef HAVE_DGA */ - -/* Define to 1 if you have the header file. */ -#define HAVE_DINPUT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_DIRECT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_DIRENT_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_DLFCN_H */ - -/* Define if you have the dlopen function. */ -/* #undef HAVE_DLOPEN */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_DMEDIA_AUDIO_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_DMEDIA_CDAUDIO_H */ - -/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ -/* #undef HAVE_DOPRNT */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_DPMI_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_DSOUND_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_ERRNO_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_EXECINFO_H */ - -/* Define this if you have FB_AUX_VGA_PLANES_CFB4 */ -/* #undef HAVE_FB_AUX_VGA_PLANES_CFB4 */ - -/* Define this if you have FB_AUX_VGA_PLANES_CFB4 */ -/* #undef HAVE_FB_AUX_VGA_PLANES_CFB8 */ - -/* Define this if you have FB_AUX_VGA_PLANES_VGA4 */ -/* #undef HAVE_FB_AUX_VGA_PLANES_VGA4 */ - -/* Define to 1 if you have the `fcntl' function. */ -/* #undef HAVE_FCNTL */ - -/* Define to 1 if you have the header file. */ -#define HAVE_FCNTL_H 1 - -/* define this if you have flac libs */ -/* #undef HAVE_FLAC */ - -/* Define to 1 if you have the header file. */ -#define HAVE_FNMATCH_H 1 - -/* Define this if fnmatch is prototyped in fnmatch.h */ -/* #undef HAVE_FNMATCH_PROTO */ - -/* Define this if FPOS_T is a struct */ -/* #undef HAVE_FPOS_T_STRUCT */ - -/* Define to 1 if you have the `ftime' function. */ -#define HAVE_FTIME 1 - -/* Define to 1 if you have the `getaddrinfo' function. */ -#define HAVE_GETADDRINFO 1 - -/* Define to 1 if you have the `gethostbyname' function. */ -#define HAVE_GETHOSTBYNAME 1 - -/* Define to 1 if you have the `gethostname' function. */ -#define HAVE_GETHOSTNAME 1 - -/* Define to 1 if you have the `getnameinfo' function. */ -#define HAVE_GETNAMEINFO 1 - -/* Define to 1 if you have the `getpagesize' function. */ -/* #undef HAVE_GETPAGESIZE */ - -/* Define to 1 if you have the `gettimeofday' function. */ -/* #undef HAVE_GETTIMEOFDAY */ - -/* Define to 1 if you have the `getwd' function. */ -/* #undef HAVE_GETWD */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_INTTYPES_H */ - -/* Define this if your system has struct in_pktinfo */ -/* #undef HAVE_IN_PKTINFO */ - -/* Define to 1 if you have the header file. */ -#define HAVE_IO_H 1 - -/* Define this if you want IPv6 support */ -/* #undef HAVE_IPV6 */ - -/* Define if you have libjack */ -/* #undef HAVE_JACK */ - -/* Define to 1 if you have a functional curl library. */ -#define HAVE_LIBCURL 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_LIBC_H */ - -/* Define to 1 if you have the `db' library (-ldb). */ -/* #undef HAVE_LIBDB */ - -/* Define to 1 if you have the `efence' library (-lefence). */ -/* #undef HAVE_LIBEFENCE */ - -/* Define to 1 if you have the `m' library (-lm). */ -/* #undef HAVE_LIBM */ - -/* Define to 1 if you have the header file. */ -#define HAVE_LIMITS_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_LINUX_CDROM_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_LINUX_FB_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_LINUX_JOYSTICK_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_LINUX_SOUNDCARD_H */ - -/* Define to 1 if you support file names longer than 14 characters. */ -#define HAVE_LONG_FILE_NAMES 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_MACHINE_SOUNDCARD_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_MALLOC_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_MATH_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_MEMORY_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_MGRAPH_H */ - -/* Define to 1 if you have the `mkdir' function. */ -#define HAVE_MKDIR 1 - -/* Define to 1 if you have a working `mmap' system call. */ -/* #undef HAVE_MMAP */ - -/* Define to 1 if you have the `mprotect' function. */ -/* #undef HAVE_MPROTECT */ - -/* Define to 1 if you have the header file, and it defines `DIR'. */ -/* #undef HAVE_NDIR_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_NETDB_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_NETINET_IN_H */ - -/* Define if you have libpng */ -/* #undef HAVE_PNG */ - -/* Define to 1 if you have the header file. */ -#define HAVE_PROCESS_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_PTHREAD_H */ - -/* Define to 1 if you have the `putenv' function. */ -#define HAVE_PUTENV 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_PWD_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_RPC_TYPES_H */ - -/* Define this if you have sa_len member in struct sockaddr (BSD) */ -/* #undef HAVE_SA_LEN */ - -/* Define to 1 if you have the `select' function. */ -#define HAVE_SELECT 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SETJMP_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SIGNAL_H 1 - -/* Define this if you have sin6_len member in struct sockaddr_in6 (BSD) */ -/* #undef HAVE_SIN6_LEN */ - -/* Define this if your system has size_t */ -#define HAVE_SIZE_T 1 - -/* Define to 1 if you have the `snprintf' function. */ -#define HAVE_SNPRINTF 1 - -/* Define to 1 if you have the `socket' function. */ -#define HAVE_SOCKET 1 - -/* Define this if your system has socklen_t */ -/* #undef HAVE_SOCKLEN_T */ - -/* Define this if you have ss_len member in struct sockaddr_storage (BSD) */ -/* #undef HAVE_SS_LEN */ - -/* Define to 1 if you have the `stat' function. */ -#define HAVE_STAT 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDARG_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDIO_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the `strcasestr' function. */ -/* #undef HAVE_STRCASESTR */ - -/* Define this if strcasestr is prototyped in string.h */ -/* #undef HAVE_STRCASESTR_PROTO */ - -/* Define to 1 if you have the `strerror' function. */ -#define HAVE_STRERROR 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_STRINGS_H 1 */ - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the `strnlen' function. */ -#define HAVE_STRNLEN 1 - -/* Define this if strnlen is prototyped in string.h */ -#define HAVE_STRNLEN_PROTO 1 - -/* Define to 1 if you have the `strsep' function. */ -/* #undef HAVE_STRSEP */ - -/* Define to 1 if you have the `strstr' function. */ -#define HAVE_STRSTR 1 - -/* Define to 1 if `st_blksize' is member of `struct stat'. */ -/* #undef HAVE_STRUCT_STAT_ST_BLKSIZE */ - -/* Define to 1 if your `struct stat' has `st_blksize'. Deprecated, use - `HAVE_STRUCT_STAT_ST_BLKSIZE' instead. */ -/* #undef HAVE_ST_BLKSIZE */ - -/* Define this if C symbols are prefixed with an underscore */ -#define HAVE_SYM_PREFIX_UNDERSCORE 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_ASOUNDLIB_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_AUDIOIO_H */ - -/* Define to 1 if you have the header file, and it defines `DIR'. - */ -/* #undef HAVE_SYS_DIR_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_FILIO_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_IOCTL_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_IO_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_IPC_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_MMAN_H */ - -/* Define to 1 if you have the header file, and it defines `DIR'. - */ -/* #undef HAVE_SYS_NDIR_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_PARAM_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_POLL_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_SHM_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_SIGNAL_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_SOCKET_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_SOUNDCARD_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_STAT_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_TIME_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TYPES_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_UIO_H */ - -/* Define to 1 if you have that is POSIX.1 compatible. */ -/* #undef HAVE_SYS_WAIT_H */ - -/* Define this if you have tchar.h */ -#define HAVE_TCHAR_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_TERMIOS_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_TIME_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_UNISTD_H */ - -/* Define to 1 if you have the `usleep' function. */ -/* #undef HAVE_USLEEP */ - -/* Define if va_copy is available */ -/* #undef HAVE_VA_COPY */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_VGAKEYBOARD_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_VGAMOUSE_H */ - -/* Define if you have the XFree86 VIDMODE extension */ -/* #undef HAVE_VIDMODE */ - -/* define this if you have ogg/vorbis libs */ -/* #undef HAVE_VORBIS */ - -/* Define to 1 if you have the `vprintf' function. */ -#define HAVE_VPRINTF 1 - -/* Define to 1 if you have the `vsnprintf' function. */ -#define HAVE_VSNPRINTF 1 - -/* Define if you have WildMidi */ -/* #undef HAVE_WILDMIDI */ - -/* Define to 1 if you have the header file. */ -#define HAVE_WINDOWS_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_WINSOCK_H 1 - -/* Define if you have XMMS */ -/* #undef HAVE_XMMS */ - -/* Define if you have zlib */ -#define HAVE_ZLIB 1 - -/* Define to 1 if you have the `_access' function. */ -#define HAVE__ACCESS 1 - -/* Define to 1 if you have the `_ftime' function. */ -#define HAVE__FTIME 1 - -/* Define to 1 if you have the <_mingw.h> header file. */ -/* #undef HAVE__MINGW_H */ - -/* Define to 1 if you have the `_mkdir' function. */ -#define HAVE__MKDIR 1 - -/* Define this if you have _SC_PAGESIZE */ -/* #undef HAVE__SC_PAGESIZE */ - -/* Define to 1 if you have the `_snprintf' function. */ -#define HAVE__SNPRINTF 1 - -/* Define if __va_copy is available */ -/* #undef HAVE__VA_COPY */ - -/* Define to 1 if you have the `_vsnprintf' function. */ -#define HAVE__VSNPRINTF 1 - -/* Define this if the GCC __attribute__ keyword is available */ -/* #undef HAVE___ATTRIBUTE__ */ - -#ifndef HAVE___ATTRIBUTE__ -# define __attribute__(x) -#endif - -/* Define this if the GCC __attribute__ keyword is available */ -/* #undef HAVE___ATTRIBUTE__VISIBILITY */ - -#ifdef HAVE___ATTRIBUTE__VISIBILITY -# define VISIBLE __attribute__((visibility ("default"))) -#else -# define VISIBLE -#endif - -/* Define this if the GCC __builtin_expect keyword is available */ -/* #undef HAVE___BUILTIN_EXPECT */ - -#ifndef HAVE___BUILTIN_EXPECT -# define __builtin_expect(x,c) x -#endif - -/* Defined if libcurl supports AsynchDNS */ -/* #undef LIBCURL_FEATURE_ASYNCHDNS */ - -/* Defined if libcurl supports IDN */ -#define LIBCURL_FEATURE_IDN 1 - -/* Defined if libcurl supports IPv6 */ -#define LIBCURL_FEATURE_IPV6 1 - -/* Defined if libcurl supports KRB4 */ -/* #undef LIBCURL_FEATURE_KRB4 */ - -/* Defined if libcurl supports libz */ -#define LIBCURL_FEATURE_LIBZ 1 - -/* Defined if libcurl supports NTLM */ -#define LIBCURL_FEATURE_NTLM 1 - -/* Defined if libcurl supports SSL */ -/* #undef LIBCURL_FEATURE_SSL */ - -/* Defined if libcurl supports SSPI */ -#define LIBCURL_FEATURE_SSPI 1 - -/* Defined if libcurl supports DICT */ -/* #undef LIBCURL_PROTOCOL_DICT */ - -/* Defined if libcurl supports FILE */ -/* #undef LIBCURL_PROTOCOL_FILE */ - -/* Defined if libcurl supports FTP */ -#define LIBCURL_PROTOCOL_FTP 1 - -/* Defined if libcurl supports FTPS */ -/* #undef LIBCURL_PROTOCOL_FTPS */ - -/* Defined if libcurl supports HTTP */ -#define LIBCURL_PROTOCOL_HTTP 1 - -/* Defined if libcurl supports HTTPS */ -/* #undef LIBCURL_PROTOCOL_HTTPS */ - -/* Defined if libcurl supports LDAP */ -/* #undef LIBCURL_PROTOCOL_LDAP */ - -/* Defined if libcurl supports TELNET */ -/* #undef LIBCURL_PROTOCOL_TELNET */ - -/* Defined if libcurl supports TFTP */ -/* #undef LIBCURL_PROTOCOL_TFTP */ - -/* Define to 1 if `major', `minor', and `makedev' are declared in . - */ -/* #undef MAJOR_IN_MKDEV */ - -/* Define to 1 if `major', `minor', and `makedev' are declared in - . */ -/* #undef MAJOR_IN_SYSMACROS */ - -/* Define this to the QSG standard version you support in NetQuake */ -#define NQ_QSG_VERSION "1.0" - -/* Define this to the NetQuake standard version you support */ -#define NQ_VERSION "1.09" - -/* Name of package */ -#define PACKAGE "quakeforge" - -/* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "" - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "" - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "" - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "" - -/* Define this to your operating system's path separator character */ -#define PATH_SEPARATOR '/' - -/* "Proper" package name */ -#define PROGRAM "QuakeForge" - -/* Define this to where qfcc should look for header files */ -#define QFCC_INCLUDE_PATH "/usr/local/include/QF/ruamoko" - -/* Define this to where qfcc should look for lib files */ -#define QFCC_LIB_PATH "/usr/local/lib/ruamoko" - -/* Define this to the QSG standard version you support in QuakeWorld */ -#define QW_QSG_VERSION "2.0" - -/* Define this to the QuakeWorld standard version you support */ -#define QW_VERSION "2.40" - -/* Define as the return type of signal handlers (`int' or `void'). */ -#define RETSIGTYPE void - -/* list of server plugins */ -#define SERVER_PLUGIN_LIST {"console_server", console_server_PluginInfo},{0, 0} - -/* list of server prototypes */ -#define SERVER_PLUGIN_PROTOS extern plugin_t *console_server_PluginInfo (void); - -/* Define this to the default sound output driver. */ -#define SND_OUTPUT_DEFAULT "win" - -/* list of sound output plugins */ -#define SND_OUTPUT_LIST {"snd_output_win", snd_output_win_PluginInfo},{0, 0} - -/* list of sound output prototypes */ -#define SND_OUTPUT_PROTOS extern plugin_t *snd_output_win_PluginInfo (void); - -/* list of sound render plugins */ -#define SND_RENDER_LIST {"snd_render_default", snd_render_default_PluginInfo},{0, 0} - -/* list of sound render prototypes */ -#define SND_RENDER_PROTOS extern plugin_t *snd_render_default_PluginInfo (void); - -/* If using the C implementation of alloca, define if you know the - direction of stack growth for your system; otherwise it will be - automatically deduced at runtime. - STACK_DIRECTION > 0 => grows toward higher addresses - STACK_DIRECTION < 0 => grows toward lower addresses - STACK_DIRECTION = 0 => direction of growth unknown */ -/* #undef STACK_DIRECTION */ - -/* Define this if you are building static plugins */ -#define STATIC_PLUGINS 1 - -/* Define to 1 if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Define to 1 if you can safely include both and . */ -#define TIME_WITH_SYS_TIME 1 - -/* Define to 1 if your declares `struct tm'. */ -/* #undef TM_IN_SYS_TIME */ - -/* Define this if you want progs typechecking */ -/* #undef TYPECHECK_PROGS */ - -/* Define this if you want to use Intel assembly optimizations */ -/* #undef USE_INTEL_ASM */ - -/* Define if va_list is an array */ -/* #undef VA_LIST_IS_ARRAY */ - -/* Version number of package */ -#define VERSION "0.5.5-SVN" - -/* Define to 1 if your processor stores words with the most significant byte - first (like Motorola and SPARC, unlike Intel and VAX). */ -/* #undef WORDS_BIGENDIAN */ - -/* Define to 1 if the X Window System is missing or not being used. */ -/* #undef X_DISPLAY_MISSING */ - -/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a - `char[]'. */ -/* #undef YYTEXT_POINTER */ - -/* Define to empty if `const' does not conform to ANSI C. */ -/* #undef const */ - -/* Define curl_free() as free() if our version of curl lacks curl_free. */ -/* #undef curl_free */ - -/* Define to `__inline__' or `__inline' if that's what the C compiler - calls it, or to nothing if 'inline' is not supported under any name. */ -#ifndef __cplusplus -#define inline __inline -/* #undef inline */ -#endif - -/* Define to `unsigned int' if does not define. */ -/* #undef size_t */ - -/* Define strcasecmp as stricmp if you have one but not the other */ -#define strcasecmp stricmp - -/* new stuff, for VC2005 compatibility (phrosty) */ - -#define _CRT_SECURE_NO_WARNINGS -#define _CRT_NONSTDC_NO_WARNINGS - -/* used in qfcc. should #define to _ReturnAddress()? */ -#define __builtin_return_address(x) (0) - -#define _WIN32_WINNT 0x0500 -#define DIRECTINPUT_VERSION 0x0600 - -#define INITGUID - -#define snprintf _snprintf -#define strncasecmp strnicmp - -#define ssize_t ptrdiff_t -#define S_ISDIR(mode) (mode & _S_IFDIR) - -/* used in access() */ -#define R_OK 04 - -/* - disable silent conversion warnings for fixing later.. - - 4047: 'operator' : 'identifier1' differs in levels of indirection from 'identifier2' - 4244: 'argument' : conversion from 'type1' to 'type2', possible loss of data - 4267: 'var' : conversion from 'size_t' to 'type', possible loss of data (/Wp64 warning) - 4305: 'identifier' : truncation from 'type1' to 'type2' - 4311: 'variable' : pointer truncation from 'type' to 'type' (/Wp64 warning) - 4312: 'operation' : conversion from 'type1' to 'type2' of greater size (/Wp64 warning) -*/ -#pragma warning(disable:4047 4244 4267 4305 4311 4312) diff --git a/vc2005/models-sw/models-sw.vcproj b/vc2005/models-sw/models-sw.vcproj deleted file mode 100644 index 6eb490b83..000000000 --- a/vc2005/models-sw/models-sw.vcproj +++ /dev/null @@ -1,511 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/models/models.vcproj b/vc2005/models/models.vcproj deleted file mode 100644 index 06c14ed04..000000000 --- a/vc2005/models/models.vcproj +++ /dev/null @@ -1,489 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/modelsgl/modelsgl.vcproj b/vc2005/modelsgl/modelsgl.vcproj deleted file mode 100644 index 4f455edbe..000000000 --- a/vc2005/modelsgl/modelsgl.vcproj +++ /dev/null @@ -1,521 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/net-main/net-main.vcproj b/vc2005/net-main/net-main.vcproj deleted file mode 100644 index d986b743a..000000000 --- a/vc2005/net-main/net-main.vcproj +++ /dev/null @@ -1,487 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/net/net.vcproj b/vc2005/net/net.vcproj deleted file mode 100644 index 5b43da9e5..000000000 --- a/vc2005/net/net.vcproj +++ /dev/null @@ -1,473 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/nq-common/nq-common.vcproj b/vc2005/nq-common/nq-common.vcproj deleted file mode 100644 index 0dccdd9a4..000000000 --- a/vc2005/nq-common/nq-common.vcproj +++ /dev/null @@ -1,515 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/nq-sdl/nq-sdl.vcproj b/vc2005/nq-sdl/nq-sdl.vcproj deleted file mode 100644 index f4813f262..000000000 --- a/vc2005/nq-sdl/nq-sdl.vcproj +++ /dev/null @@ -1,561 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/nq-sdl32/nq-sdl32.vcproj b/vc2005/nq-sdl32/nq-sdl32.vcproj deleted file mode 100644 index 9c46d06f4..000000000 --- a/vc2005/nq-sdl32/nq-sdl32.vcproj +++ /dev/null @@ -1,567 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/nq-server/nq-server.vcproj b/vc2005/nq-server/nq-server.vcproj deleted file mode 100644 index 2b89d7bbe..000000000 --- a/vc2005/nq-server/nq-server.vcproj +++ /dev/null @@ -1,571 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/nq-sgl/nq-sgl.vcproj b/vc2005/nq-sgl/nq-sgl.vcproj deleted file mode 100644 index 3e61b1f10..000000000 --- a/vc2005/nq-sgl/nq-sgl.vcproj +++ /dev/null @@ -1,567 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/nq-wgl/nq-wgl.vcproj b/vc2005/nq-wgl/nq-wgl.vcproj deleted file mode 100644 index a789c0871..000000000 --- a/vc2005/nq-wgl/nq-wgl.vcproj +++ /dev/null @@ -1,567 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/nq/nq.vcproj b/vc2005/nq/nq.vcproj deleted file mode 100644 index 82cf3e55f..000000000 --- a/vc2005/nq/nq.vcproj +++ /dev/null @@ -1,543 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/pak/pak.vcproj b/vc2005/pak/pak.vcproj deleted file mode 100644 index 0daa6d627..000000000 --- a/vc2005/pak/pak.vcproj +++ /dev/null @@ -1,571 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/qfbsp/qfbsp.vcproj b/vc2005/qfbsp/qfbsp.vcproj deleted file mode 100644 index 461d6734b..000000000 --- a/vc2005/qfbsp/qfbsp.vcproj +++ /dev/null @@ -1,637 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/qfcc/FlexBison.rules b/vc2005/qfcc/FlexBison.rules deleted file mode 100644 index 0e4c1e3dc..000000000 --- a/vc2005/qfcc/FlexBison.rules +++ /dev/null @@ -1,246 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/qfcc/qfcc.vcproj b/vc2005/qfcc/qfcc.vcproj deleted file mode 100644 index 786974fe1..000000000 --- a/vc2005/qfcc/qfcc.vcproj +++ /dev/null @@ -1,970 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/qfclient/qfclient.vcproj b/vc2005/qfclient/qfclient.vcproj deleted file mode 100644 index 19d1fac4d..000000000 --- a/vc2005/qfclient/qfclient.vcproj +++ /dev/null @@ -1,625 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/qflight/qflight.vcproj b/vc2005/qflight/qflight.vcproj deleted file mode 100644 index 820cbc14a..000000000 --- a/vc2005/qflight/qflight.vcproj +++ /dev/null @@ -1,625 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/qfmodelgen/qfmodelgen.vcproj b/vc2005/qfmodelgen/qfmodelgen.vcproj deleted file mode 100644 index 24436bea5..000000000 --- a/vc2005/qfmodelgen/qfmodelgen.vcproj +++ /dev/null @@ -1,583 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/qfprogs/qfprogs.vcproj b/vc2005/qfprogs/qfprogs.vcproj deleted file mode 100644 index d77e43a2e..000000000 --- a/vc2005/qfprogs/qfprogs.vcproj +++ /dev/null @@ -1,595 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/qfserver/qfserver.vcproj b/vc2005/qfserver/qfserver.vcproj deleted file mode 100644 index 2852ccec3..000000000 --- a/vc2005/qfserver/qfserver.vcproj +++ /dev/null @@ -1,677 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/qfvis/qfvis.vcproj b/vc2005/qfvis/qfvis.vcproj deleted file mode 100644 index 4e34daab8..000000000 --- a/vc2005/qfvis/qfvis.vcproj +++ /dev/null @@ -1,593 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/qfwavinfo/qfwavinfo.vcproj b/vc2005/qfwavinfo/qfwavinfo.vcproj deleted file mode 100644 index 9e2ead0d5..000000000 --- a/vc2005/qfwavinfo/qfwavinfo.vcproj +++ /dev/null @@ -1,567 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/qtv/qtv.vcproj b/vc2005/qtv/qtv.vcproj deleted file mode 100644 index 127035507..000000000 --- a/vc2005/qtv/qtv.vcproj +++ /dev/null @@ -1,599 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/qw-client-sdl/qw-client-sdl.vcproj b/vc2005/qw-client-sdl/qw-client-sdl.vcproj deleted file mode 100644 index 188d11f88..000000000 --- a/vc2005/qw-client-sdl/qw-client-sdl.vcproj +++ /dev/null @@ -1,563 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/qw-client-sdl32/qw-client-sdl32.vcproj b/vc2005/qw-client-sdl32/qw-client-sdl32.vcproj deleted file mode 100644 index ccbdb9c89..000000000 --- a/vc2005/qw-client-sdl32/qw-client-sdl32.vcproj +++ /dev/null @@ -1,567 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/qw-client-sgl/qw-client-sgl.vcproj b/vc2005/qw-client-sgl/qw-client-sgl.vcproj deleted file mode 100644 index 22adf9217..000000000 --- a/vc2005/qw-client-sgl/qw-client-sgl.vcproj +++ /dev/null @@ -1,567 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/qw-client-wgl/qw-client-wgl.vcproj b/vc2005/qw-client-wgl/qw-client-wgl.vcproj deleted file mode 100644 index 96fb8c16a..000000000 --- a/vc2005/qw-client-wgl/qw-client-wgl.vcproj +++ /dev/null @@ -1,563 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/qw-master/qw-master.vcproj b/vc2005/qw-master/qw-master.vcproj deleted file mode 100644 index a8605e358..000000000 --- a/vc2005/qw-master/qw-master.vcproj +++ /dev/null @@ -1,567 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/qw/qw.vcproj b/vc2005/qw/qw.vcproj deleted file mode 100644 index d73566194..000000000 --- a/vc2005/qw/qw.vcproj +++ /dev/null @@ -1,473 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/readme.txt b/vc2005/readme.txt deleted file mode 100644 index 8c50717db..000000000 --- a/vc2005/readme.txt +++ /dev/null @@ -1,27 +0,0 @@ -Requirements -============= -- Visual C++ 2005 SP1 -- Windows Vista SDK (previous RC compilers won't recognize the - high-resolution Vista icon) -- DirectX SDK (for DirectInput) -- SDL (required for sdl, sdl32, and sgl builds) -- bison/flex (required for qfcc, needs to be in your compiler path) - -Optional -========= -- zlib (#undef HAVE_ZLIB from vc2005/include/config.h if you don't want - this). Expects zlib.lib and zlib.dll for Debug/Release builds, and - libzlib.lib for Release (static) build. -- libcurl (#undef HAVE_LIBCURL from vc2005/include/config.h if you don't - want this). Expects curl.lib and curl.dll for Debug/Release builds, - and libcurl.lib for Release (static) build. - -Notes -====== -By default, qfcc is configured to use the Boost Wave preprocessor. You -can get this from http://www.boost.org, or change the CPP_NAME #define in -vc2005/include/config.h to whatever preprocessor you want. If you have -GCC, you can simply replace "wave --c99" with "gcc" in the define and it -should work. - -clean.ps1 is a Windows Powershell script that cleans up any build files. diff --git a/vc2005/ruamoko/ruamoko.vcproj b/vc2005/ruamoko/ruamoko.vcproj deleted file mode 100644 index 93fd48673..000000000 --- a/vc2005/ruamoko/ruamoko.vcproj +++ /dev/null @@ -1,517 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/sound/sound.vcproj b/vc2005/sound/sound.vcproj deleted file mode 100644 index 50c25ce4c..000000000 --- a/vc2005/sound/sound.vcproj +++ /dev/null @@ -1,513 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/util/util.vcproj b/vc2005/util/util.vcproj deleted file mode 100644 index 021e3d655..000000000 --- a/vc2005/util/util.vcproj +++ /dev/null @@ -1,617 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/video-sdl/video-sdl.vcproj b/vc2005/video-sdl/video-sdl.vcproj deleted file mode 100644 index 5efd89c56..000000000 --- a/vc2005/video-sdl/video-sdl.vcproj +++ /dev/null @@ -1,471 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/video-sgl/video-sgl.vcproj b/vc2005/video-sgl/video-sgl.vcproj deleted file mode 100644 index 94be99a3a..000000000 --- a/vc2005/video-sgl/video-sgl.vcproj +++ /dev/null @@ -1,467 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/video-sw/video-sw.vcproj b/vc2005/video-sw/video-sw.vcproj deleted file mode 100644 index 9158369bf..000000000 --- a/vc2005/video-sw/video-sw.vcproj +++ /dev/null @@ -1,595 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/video-sw32/video-sw32.vcproj b/vc2005/video-sw32/video-sw32.vcproj deleted file mode 100644 index fd4ed7caa..000000000 --- a/vc2005/video-sw32/video-sw32.vcproj +++ /dev/null @@ -1,571 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/video-wgl/video-wgl.vcproj b/vc2005/video-wgl/video-wgl.vcproj deleted file mode 100644 index 0087c8b78..000000000 --- a/vc2005/video-wgl/video-wgl.vcproj +++ /dev/null @@ -1,471 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/video/video.vcproj b/vc2005/video/video.vcproj deleted file mode 100644 index 44213b2a4..000000000 --- a/vc2005/video/video.vcproj +++ /dev/null @@ -1,537 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/videogl/videogl.vcproj b/vc2005/videogl/videogl.vcproj deleted file mode 100644 index cfd456dc5..000000000 --- a/vc2005/videogl/videogl.vcproj +++ /dev/null @@ -1,555 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2005/wad/wad.vcproj b/vc2005/wad/wad.vcproj deleted file mode 100644 index 044bff20a..000000000 --- a/vc2005/wad/wad.vcproj +++ /dev/null @@ -1,579 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/Makefile.am b/vc2008/Makefile.am deleted file mode 100644 index 04c53f194..000000000 --- a/vc2008/Makefile.am +++ /dev/null @@ -1,24 +0,0 @@ -## Process this file with automake to produce Makefile.in -AUTOMAKE_OPTIONS= foreign -EXTRA_DIST = \ - Makefile.am QuakeForge.sln bsp2img/bsp2img.vcproj \ - builtins/builtins.vcproj clean.ps1 common/common.vcproj \ - console/console.vcproj console_client/console_client.vcproj \ - console_server/console_server.vcproj engine/engine.vcproj gib/gib.vcproj \ - hw-master/hw-master.vcproj image/image.vcproj include/config.h \ - models-sw/models-sw.vcproj models/models.vcproj modelsgl/modelsgl.vcproj \ - net-main/net-main.vcproj net/net.vcproj nq-common/nq-common.vcproj \ - nq-sdl/nq-sdl.vcproj nq-sdl32/nq-sdl32.vcproj nq-server/nq-server.vcproj \ - nq-sgl/nq-sgl.vcproj nq-wgl/nq-wgl.vcproj nq/nq.vcproj pak/pak.vcproj \ - qfbsp/qfbsp.vcproj qfcc/FlexBison.rules qfcc/qfcc.vcproj \ - qfclient/qfclient.vcproj qflight/qflight.vcproj \ - qfmodelgen/qfmodelgen.vcproj qfprogs/qfprogs.vcproj \ - qfserver/qfserver.vcproj qfvis/qfvis.vcproj qfwavinfo/qfwavinfo.vcproj \ - qtv/qtv.vcproj qw-client-sdl/qw-client-sdl.vcproj \ - qw-client-sdl32/qw-client-sdl32.vcproj qw-client-sgl/qw-client-sgl.vcproj \ - qw-client-wgl/qw-client-wgl.vcproj qw-master/qw-master.vcproj \ - qw/qw.vcproj readme.txt ruamoko/ruamoko.vcproj sound/sound.vcproj \ - util/util.vcproj video-sdl/video-sdl.vcproj video-sgl/video-sgl.vcproj \ - video-sw/video-sw.vcproj video-sw32/video-sw32.vcproj \ - video-wgl/video-wgl.vcproj video/video.vcproj videogl/videogl.vcproj \ - wad/wad.vcproj diff --git a/vc2008/QuakeForge.sln b/vc2008/QuakeForge.sln deleted file mode 100644 index 296484c89..000000000 --- a/vc2008/QuakeForge.sln +++ /dev/null @@ -1,966 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{3CDA2798-7565-47C5-B972-F9E63DBEFFFA}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-client", "qfclient\qfclient.vcproj", "{9A942925-61E6-4975-935A-5D62E8248E64}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw", "qw\qw.vcproj", "{6ADA4322-693A-46BB-897B-17BB5BE9F08C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "net-chan", "net\net.vcproj", "{6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ruamoko", "ruamoko\ruamoko.vcproj", "{51028ACF-53D4-4478-8500-55E6B8A81375}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models-gl", "modelsgl\modelsgl.vcproj", "{1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "console", "console\console.vcproj", "{ED4AFBF5-C247-4352-966D-048B8998C6A1}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gib", "gib\gib.vcproj", "{01C3B138-9D45-4ED6-A763-893C067262C2}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "util", "util\util.vcproj", "{ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "engine", "engine\engine.vcproj", "{2626C0E1-6F5C-47D3-B80D-93942D766DD7}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "builtins", "builtins\builtins.vcproj", "{04FA9D77-E45F-4917-B972-B353BA6A6FA8}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sound", "sound\sound.vcproj", "{BF149D97-7520-4788-9CD1-3D99C5C8150F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "image", "image\image.vcproj", "{5203F034-0047-4EC0-B7E9-D037FAF5D536}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video", "video\video.vcproj", "{E7B3D07D-2FE8-481B-8DAB-6255A412A42F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-common", "common\common.vcproj", "{BC1F021A-1EEC-4A7A-B746-5AA6048E478C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-server", "qfserver\qfserver.vcproj", "{544D097C-9C24-4C57-A171-8C8029421185}" - ProjectSection(ProjectDependencies) = postProject - {6ADA4322-693A-46BB-897B-17BB5BE9F08C} = {6ADA4322-693A-46BB-897B-17BB5BE9F08C} - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} = {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A} = {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A} - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models", "models\models.vcproj", "{DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "console-client", "console_client\console_client.vcproj", "{226D42CE-5833-444E-94FA-84C1D51892A8}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "console-server", "console_server\console_server.vcproj", "{B37FE734-01F4-4799-86B2-D084820715BE}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qfbsp", "qfbsp\qfbsp.vcproj", "{B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qflight", "qflight\qflight.vcproj", "{5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qfvis", "qfvis\qfvis.vcproj", "{E5D842C5-669F-4FC7-A5E0-44B562F86435}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-master", "qw-master\qw-master.vcproj", "{445A2500-3BBC-449A-A929-C419C2A16051}" - ProjectSection(ProjectDependencies) = postProject - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qfmodelgen", "qfmodelgen\qfmodelgen.vcproj", "{052C34FE-C9E2-43ED-95DA-FB3F27B44E37}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qfprogs", "qfprogs\qfprogs.vcproj", "{5FA27C8E-51B1-445A-A375-FBE74F0984B1}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qfwavinfo", "qfwavinfo\qfwavinfo.vcproj", "{56BD559B-1590-4FC4-B441-AB1973BAC2BD}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wad", "wad\wad.vcproj", "{88422448-C5FB-46F3-A0B3-0811F90E537C}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pak", "pak\pak.vcproj", "{9EE8BD4B-47D3-4AD5-A8B9-831329792A05}" - ProjectSection(ProjectDependencies) = postProject - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bsp2img", "bsp2img\bsp2img.vcproj", "{4605418D-2292-470A-AB18-C2119B016F71}" - ProjectSection(ProjectDependencies) = postProject - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qtv", "qtv\qtv.vcproj", "{05E68E3B-5901-43A9-981D-CF392C0F5C6C}" - ProjectSection(ProjectDependencies) = postProject - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {6ADA4322-693A-46BB-897B-17BB5BE9F08C} = {6ADA4322-693A-46BB-897B-17BB5BE9F08C} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq-common", "nq-common\nq-common.vcproj", "{6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq-wgl", "nq-wgl\nq-wgl.vcproj", "{1CD1A18B-95D5-4EA4-917C-34B10066BFCC}" - ProjectSection(ProjectDependencies) = postProject - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137} = {178D81A7-F2FB-41D7-B300-9D1A4DE5E137} - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} = {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} = {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} = {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} = {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} = {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "net-main", "net-main\net-main.vcproj", "{053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq-server", "nq-server\nq-server.vcproj", "{231C032C-DE16-459A-8E7D-6509C2EC3998}" - ProjectSection(ProjectDependencies) = postProject - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} = {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} = {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A} = {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hw-master", "hw-master\hw-master.vcproj", "{419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}" - ProjectSection(ProjectDependencies) = postProject - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qfcc", "qfcc\qfcc.vcproj", "{40639893-4D75-48CD-811F-4B363CC71FFA}" - ProjectSection(ProjectDependencies) = postProject - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-client-wgl", "qw-client-wgl\qw-client-wgl.vcproj", "{5FD733BF-B3C6-4A96-BED7-35E2484448E1}" - ProjectSection(ProjectDependencies) = postProject - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} = {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {9A942925-61E6-4975-935A-5D62E8248E64} = {9A942925-61E6-4975-935A-5D62E8248E64} - {6ADA4322-693A-46BB-897B-17BB5BE9F08C} = {6ADA4322-693A-46BB-897B-17BB5BE9F08C} - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} = {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} = {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137} = {178D81A7-F2FB-41D7-B300-9D1A4DE5E137} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-client-sgl", "qw-client-sgl\qw-client-sgl.vcproj", "{649C4168-1C65-4E41-818F-85A1C52C1B8F}" - ProjectSection(ProjectDependencies) = postProject - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} = {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} - {9A942925-61E6-4975-935A-5D62E8248E64} = {9A942925-61E6-4975-935A-5D62E8248E64} - {6ADA4322-693A-46BB-897B-17BB5BE9F08C} = {6ADA4322-693A-46BB-897B-17BB5BE9F08C} - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} = {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} = {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} - {44A18410-3AA8-4A64-935B-D20223AD6885} = {44A18410-3AA8-4A64-935B-D20223AD6885} - {7E30C3B1-AEE7-483D-B231-C672365CD2D7} = {7E30C3B1-AEE7-483D-B231-C672365CD2D7} - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video-gl", "videogl\videogl.vcproj", "{C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video-sdl", "video-sdl\video-sdl.vcproj", "{44A18410-3AA8-4A64-935B-D20223AD6885}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-client-sdl", "qw-client-sdl\qw-client-sdl.vcproj", "{B44CB3E0-F2FD-4260-A632-C01FB881613E}" - ProjectSection(ProjectDependencies) = postProject - {66D3A191-E4D5-45F3-86BD-EFBB249CD554} = {66D3A191-E4D5-45F3-86BD-EFBB249CD554} - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - {44A18410-3AA8-4A64-935B-D20223AD6885} = {44A18410-3AA8-4A64-935B-D20223AD6885} - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} = {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} - {6ADA4322-693A-46BB-897B-17BB5BE9F08C} = {6ADA4322-693A-46BB-897B-17BB5BE9F08C} - {9A942925-61E6-4975-935A-5D62E8248E64} = {9A942925-61E6-4975-935A-5D62E8248E64} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {F4A9881E-0EB0-44A1-9664-B6CBDE992861} = {F4A9881E-0EB0-44A1-9664-B6CBDE992861} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models-sw", "models-sw\models-sw.vcproj", "{F4A9881E-0EB0-44A1-9664-B6CBDE992861}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video-sw", "video-sw\video-sw.vcproj", "{66D3A191-E4D5-45F3-86BD-EFBB249CD554}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qw-client-sdl32", "qw-client-sdl32\qw-client-sdl32.vcproj", "{CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}" - ProjectSection(ProjectDependencies) = postProject - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} - {44A18410-3AA8-4A64-935B-D20223AD6885} = {44A18410-3AA8-4A64-935B-D20223AD6885} - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} = {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} - {F4A9881E-0EB0-44A1-9664-B6CBDE992861} = {F4A9881E-0EB0-44A1-9664-B6CBDE992861} - {6ADA4322-693A-46BB-897B-17BB5BE9F08C} = {6ADA4322-693A-46BB-897B-17BB5BE9F08C} - {9A942925-61E6-4975-935A-5D62E8248E64} = {9A942925-61E6-4975-935A-5D62E8248E64} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {D5B9558F-EF25-4167-ACE4-723C7413B390} = {D5B9558F-EF25-4167-ACE4-723C7413B390} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video-sw32", "video-sw32\video-sw32.vcproj", "{D5B9558F-EF25-4167-ACE4-723C7413B390}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq", "nq\nq.vcproj", "{155112B9-A8A9-4E06-90F5-4AAAB32B2F70}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video-wgl", "video-wgl\video-wgl.vcproj", "{178D81A7-F2FB-41D7-B300-9D1A4DE5E137}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "video-sgl", "video-sgl\video-sgl.vcproj", "{7E30C3B1-AEE7-483D-B231-C672365CD2D7}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq-sgl", "nq-sgl\nq-sgl.vcproj", "{2E26DB8B-9E37-4072-B397-8A7086E0ACD0}" - ProjectSection(ProjectDependencies) = postProject - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} = {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {7E30C3B1-AEE7-483D-B231-C672365CD2D7} = {7E30C3B1-AEE7-483D-B231-C672365CD2D7} - {44A18410-3AA8-4A64-935B-D20223AD6885} = {44A18410-3AA8-4A64-935B-D20223AD6885} - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} = {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} = {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} = {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} = {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq-sdl", "nq-sdl\nq-sdl.vcproj", "{2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}" - ProjectSection(ProjectDependencies) = postProject - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} = {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} = {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {66D3A191-E4D5-45F3-86BD-EFBB249CD554} = {66D3A191-E4D5-45F3-86BD-EFBB249CD554} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} = {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {F4A9881E-0EB0-44A1-9664-B6CBDE992861} = {F4A9881E-0EB0-44A1-9664-B6CBDE992861} - {44A18410-3AA8-4A64-935B-D20223AD6885} = {44A18410-3AA8-4A64-935B-D20223AD6885} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nq-sdl32", "nq-sdl32\nq-sdl32.vcproj", "{2CEB1965-A89C-4422-A9AC-B30FCE7913C3}" - ProjectSection(ProjectDependencies) = postProject - {44A18410-3AA8-4A64-935B-D20223AD6885} = {44A18410-3AA8-4A64-935B-D20223AD6885} - {F4A9881E-0EB0-44A1-9664-B6CBDE992861} = {F4A9881E-0EB0-44A1-9664-B6CBDE992861} - {B37FE734-01F4-4799-86B2-D084820715BE} = {B37FE734-01F4-4799-86B2-D084820715BE} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {5203F034-0047-4EC0-B7E9-D037FAF5D536} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {01C3B138-9D45-4ED6-A763-893C067262C2} - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} = {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {04FA9D77-E45F-4917-B972-B353BA6A6FA8} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} - {D5B9558F-EF25-4167-ACE4-723C7413B390} = {D5B9558F-EF25-4167-ACE4-723C7413B390} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {BF149D97-7520-4788-9CD1-3D99C5C8150F} - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} = {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {226D42CE-5833-444E-94FA-84C1D51892A8} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {51028ACF-53D4-4478-8500-55E6B8A81375} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {2626C0E1-6F5C-47D3-B80D-93942D766DD7} - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} = {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {ED4AFBF5-C247-4352-966D-048B8998C6A1} - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release (static)|Win32 = Release (static)|Win32 - Release (static)|x64 = Release (static)|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9A942925-61E6-4975-935A-5D62E8248E64}.Debug|Win32.ActiveCfg = Debug|Win32 - {9A942925-61E6-4975-935A-5D62E8248E64}.Debug|Win32.Build.0 = Debug|Win32 - {9A942925-61E6-4975-935A-5D62E8248E64}.Debug|x64.ActiveCfg = Debug|x64 - {9A942925-61E6-4975-935A-5D62E8248E64}.Debug|x64.Build.0 = Debug|x64 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release (static)|x64.Build.0 = Release (static)|x64 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release|Win32.ActiveCfg = Release|Win32 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release|Win32.Build.0 = Release|Win32 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release|x64.ActiveCfg = Release|x64 - {9A942925-61E6-4975-935A-5D62E8248E64}.Release|x64.Build.0 = Release|x64 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Debug|Win32.ActiveCfg = Debug|Win32 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Debug|Win32.Build.0 = Debug|Win32 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Debug|x64.ActiveCfg = Debug|x64 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Debug|x64.Build.0 = Debug|x64 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release (static)|x64.Build.0 = Release (static)|x64 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release|Win32.ActiveCfg = Release|Win32 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release|Win32.Build.0 = Release|Win32 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release|x64.ActiveCfg = Release|x64 - {6ADA4322-693A-46BB-897B-17BB5BE9F08C}.Release|x64.Build.0 = Release|x64 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Debug|Win32.ActiveCfg = Debug|Win32 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Debug|Win32.Build.0 = Debug|Win32 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Debug|x64.ActiveCfg = Debug|x64 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Debug|x64.Build.0 = Debug|x64 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release (static)|x64.Build.0 = Release (static)|x64 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release|Win32.ActiveCfg = Release|Win32 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release|Win32.Build.0 = Release|Win32 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release|x64.ActiveCfg = Release|x64 - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0}.Release|x64.Build.0 = Release|x64 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Debug|Win32.ActiveCfg = Debug|Win32 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Debug|Win32.Build.0 = Debug|Win32 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Debug|x64.ActiveCfg = Debug|x64 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Debug|x64.Build.0 = Debug|x64 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release (static)|x64.Build.0 = Release (static)|x64 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release|Win32.ActiveCfg = Release|Win32 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release|Win32.Build.0 = Release|Win32 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release|x64.ActiveCfg = Release|x64 - {51028ACF-53D4-4478-8500-55E6B8A81375}.Release|x64.Build.0 = Release|x64 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Debug|Win32.ActiveCfg = Debug|Win32 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Debug|Win32.Build.0 = Debug|Win32 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Debug|x64.ActiveCfg = Debug|x64 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Debug|x64.Build.0 = Debug|x64 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release (static)|x64.Build.0 = Release (static)|x64 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release|Win32.ActiveCfg = Release|Win32 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release|Win32.Build.0 = Release|Win32 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release|x64.ActiveCfg = Release|x64 - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7}.Release|x64.Build.0 = Release|x64 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Debug|Win32.ActiveCfg = Debug|Win32 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Debug|Win32.Build.0 = Debug|Win32 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Debug|x64.ActiveCfg = Debug|x64 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Debug|x64.Build.0 = Debug|x64 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release (static)|x64.Build.0 = Release (static)|x64 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release|Win32.ActiveCfg = Release|Win32 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release|Win32.Build.0 = Release|Win32 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release|x64.ActiveCfg = Release|x64 - {ED4AFBF5-C247-4352-966D-048B8998C6A1}.Release|x64.Build.0 = Release|x64 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Debug|Win32.ActiveCfg = Debug|Win32 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Debug|Win32.Build.0 = Debug|Win32 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Debug|x64.ActiveCfg = Debug|x64 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Debug|x64.Build.0 = Debug|x64 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release (static)|x64.Build.0 = Release (static)|x64 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release|Win32.ActiveCfg = Release|Win32 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release|Win32.Build.0 = Release|Win32 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release|x64.ActiveCfg = Release|x64 - {01C3B138-9D45-4ED6-A763-893C067262C2}.Release|x64.Build.0 = Release|x64 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Debug|Win32.ActiveCfg = Debug|Win32 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Debug|Win32.Build.0 = Debug|Win32 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Debug|x64.ActiveCfg = Debug|x64 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Debug|x64.Build.0 = Debug|x64 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release (static)|x64.Build.0 = Release (static)|x64 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release|Win32.ActiveCfg = Release|Win32 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release|Win32.Build.0 = Release|Win32 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release|x64.ActiveCfg = Release|x64 - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB}.Release|x64.Build.0 = Release|x64 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Debug|Win32.ActiveCfg = Debug|Win32 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Debug|Win32.Build.0 = Debug|Win32 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Debug|x64.ActiveCfg = Debug|x64 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Debug|x64.Build.0 = Debug|x64 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release (static)|x64.Build.0 = Release (static)|x64 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release|Win32.ActiveCfg = Release|Win32 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release|Win32.Build.0 = Release|Win32 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release|x64.ActiveCfg = Release|x64 - {2626C0E1-6F5C-47D3-B80D-93942D766DD7}.Release|x64.Build.0 = Release|x64 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Debug|Win32.ActiveCfg = Debug|Win32 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Debug|Win32.Build.0 = Debug|Win32 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Debug|x64.ActiveCfg = Debug|x64 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Debug|x64.Build.0 = Debug|x64 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release (static)|x64.Build.0 = Release (static)|x64 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release|Win32.ActiveCfg = Release|Win32 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release|Win32.Build.0 = Release|Win32 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release|x64.ActiveCfg = Release|x64 - {04FA9D77-E45F-4917-B972-B353BA6A6FA8}.Release|x64.Build.0 = Release|x64 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Debug|Win32.ActiveCfg = Debug|Win32 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Debug|Win32.Build.0 = Debug|Win32 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Debug|x64.ActiveCfg = Debug|x64 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Debug|x64.Build.0 = Debug|x64 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release (static)|x64.Build.0 = Release (static)|x64 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release|Win32.ActiveCfg = Release|Win32 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release|Win32.Build.0 = Release|Win32 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release|x64.ActiveCfg = Release|x64 - {BF149D97-7520-4788-9CD1-3D99C5C8150F}.Release|x64.Build.0 = Release|x64 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Debug|Win32.ActiveCfg = Debug|Win32 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Debug|Win32.Build.0 = Debug|Win32 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Debug|x64.ActiveCfg = Debug|x64 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Debug|x64.Build.0 = Debug|x64 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release (static)|x64.Build.0 = Release (static)|x64 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release|Win32.ActiveCfg = Release|Win32 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release|Win32.Build.0 = Release|Win32 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release|x64.ActiveCfg = Release|x64 - {5203F034-0047-4EC0-B7E9-D037FAF5D536}.Release|x64.Build.0 = Release|x64 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Debug|Win32.ActiveCfg = Debug|Win32 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Debug|Win32.Build.0 = Debug|Win32 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Debug|x64.ActiveCfg = Debug|x64 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Debug|x64.Build.0 = Debug|x64 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release (static)|x64.Build.0 = Release (static)|x64 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release|Win32.ActiveCfg = Release|Win32 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release|Win32.Build.0 = Release|Win32 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release|x64.ActiveCfg = Release|x64 - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F}.Release|x64.Build.0 = Release|x64 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Debug|Win32.ActiveCfg = Debug|Win32 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Debug|Win32.Build.0 = Debug|Win32 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Debug|x64.ActiveCfg = Debug|x64 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Debug|x64.Build.0 = Debug|x64 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release (static)|x64.Build.0 = Release (static)|x64 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release|Win32.ActiveCfg = Release|Win32 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release|Win32.Build.0 = Release|Win32 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release|x64.ActiveCfg = Release|x64 - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C}.Release|x64.Build.0 = Release|x64 - {544D097C-9C24-4C57-A171-8C8029421185}.Debug|Win32.ActiveCfg = Debug|Win32 - {544D097C-9C24-4C57-A171-8C8029421185}.Debug|Win32.Build.0 = Debug|Win32 - {544D097C-9C24-4C57-A171-8C8029421185}.Debug|x64.ActiveCfg = Debug|x64 - {544D097C-9C24-4C57-A171-8C8029421185}.Debug|x64.Build.0 = Debug|x64 - {544D097C-9C24-4C57-A171-8C8029421185}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {544D097C-9C24-4C57-A171-8C8029421185}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {544D097C-9C24-4C57-A171-8C8029421185}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {544D097C-9C24-4C57-A171-8C8029421185}.Release (static)|x64.Build.0 = Release (static)|x64 - {544D097C-9C24-4C57-A171-8C8029421185}.Release|Win32.ActiveCfg = Release|Win32 - {544D097C-9C24-4C57-A171-8C8029421185}.Release|Win32.Build.0 = Release|Win32 - {544D097C-9C24-4C57-A171-8C8029421185}.Release|x64.ActiveCfg = Release|x64 - {544D097C-9C24-4C57-A171-8C8029421185}.Release|x64.Build.0 = Release|x64 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Debug|Win32.ActiveCfg = Debug|Win32 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Debug|Win32.Build.0 = Debug|Win32 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Debug|x64.ActiveCfg = Debug|x64 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Debug|x64.Build.0 = Debug|x64 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release (static)|x64.Build.0 = Release (static)|x64 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release|Win32.ActiveCfg = Release|Win32 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release|Win32.Build.0 = Release|Win32 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release|x64.ActiveCfg = Release|x64 - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A}.Release|x64.Build.0 = Release|x64 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Debug|Win32.ActiveCfg = Debug|Win32 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Debug|Win32.Build.0 = Debug|Win32 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Debug|x64.ActiveCfg = Debug|x64 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Debug|x64.Build.0 = Debug|x64 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release (static)|x64.Build.0 = Release (static)|x64 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release|Win32.ActiveCfg = Release|Win32 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release|Win32.Build.0 = Release|Win32 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release|x64.ActiveCfg = Release|x64 - {226D42CE-5833-444E-94FA-84C1D51892A8}.Release|x64.Build.0 = Release|x64 - {B37FE734-01F4-4799-86B2-D084820715BE}.Debug|Win32.ActiveCfg = Debug|Win32 - {B37FE734-01F4-4799-86B2-D084820715BE}.Debug|Win32.Build.0 = Debug|Win32 - {B37FE734-01F4-4799-86B2-D084820715BE}.Debug|x64.ActiveCfg = Debug|x64 - {B37FE734-01F4-4799-86B2-D084820715BE}.Debug|x64.Build.0 = Debug|x64 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release (static)|x64.Build.0 = Release (static)|x64 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release|Win32.ActiveCfg = Release|Win32 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release|Win32.Build.0 = Release|Win32 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release|x64.ActiveCfg = Release|x64 - {B37FE734-01F4-4799-86B2-D084820715BE}.Release|x64.Build.0 = Release|x64 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Debug|Win32.ActiveCfg = Debug|Win32 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Debug|Win32.Build.0 = Debug|Win32 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Debug|x64.ActiveCfg = Debug|x64 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Debug|x64.Build.0 = Debug|x64 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release (static)|x64.Build.0 = Release (static)|x64 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release|Win32.ActiveCfg = Release|Win32 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release|Win32.Build.0 = Release|Win32 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release|x64.ActiveCfg = Release|x64 - {B00D4025-0437-4FF2-BD0E-D2AE6CF82AC2}.Release|x64.Build.0 = Release|x64 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Debug|Win32.ActiveCfg = Debug|Win32 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Debug|Win32.Build.0 = Debug|Win32 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Debug|x64.ActiveCfg = Debug|x64 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Debug|x64.Build.0 = Debug|x64 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release (static)|x64.Build.0 = Release (static)|x64 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release|Win32.ActiveCfg = Release|Win32 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release|Win32.Build.0 = Release|Win32 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release|x64.ActiveCfg = Release|x64 - {5E7E6110-89E8-4797-A8E3-EBEF0EF5CAF2}.Release|x64.Build.0 = Release|x64 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Debug|Win32.ActiveCfg = Debug|Win32 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Debug|Win32.Build.0 = Debug|Win32 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Debug|x64.ActiveCfg = Debug|x64 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Debug|x64.Build.0 = Debug|x64 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release (static)|x64.Build.0 = Release (static)|x64 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release|Win32.ActiveCfg = Release|Win32 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release|Win32.Build.0 = Release|Win32 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release|x64.ActiveCfg = Release|x64 - {E5D842C5-669F-4FC7-A5E0-44B562F86435}.Release|x64.Build.0 = Release|x64 - {445A2500-3BBC-449A-A929-C419C2A16051}.Debug|Win32.ActiveCfg = Debug|Win32 - {445A2500-3BBC-449A-A929-C419C2A16051}.Debug|Win32.Build.0 = Debug|Win32 - {445A2500-3BBC-449A-A929-C419C2A16051}.Debug|x64.ActiveCfg = Debug|x64 - {445A2500-3BBC-449A-A929-C419C2A16051}.Debug|x64.Build.0 = Debug|x64 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release (static)|x64.Build.0 = Release (static)|x64 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release|Win32.ActiveCfg = Release|Win32 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release|Win32.Build.0 = Release|Win32 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release|x64.ActiveCfg = Release|x64 - {445A2500-3BBC-449A-A929-C419C2A16051}.Release|x64.Build.0 = Release|x64 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Debug|Win32.ActiveCfg = Debug|Win32 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Debug|Win32.Build.0 = Debug|Win32 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Debug|x64.ActiveCfg = Debug|x64 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Debug|x64.Build.0 = Debug|x64 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release (static)|x64.Build.0 = Release (static)|x64 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release|Win32.ActiveCfg = Release|Win32 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release|Win32.Build.0 = Release|Win32 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release|x64.ActiveCfg = Release|x64 - {052C34FE-C9E2-43ED-95DA-FB3F27B44E37}.Release|x64.Build.0 = Release|x64 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Debug|Win32.ActiveCfg = Debug|Win32 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Debug|Win32.Build.0 = Debug|Win32 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Debug|x64.ActiveCfg = Debug|x64 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Debug|x64.Build.0 = Debug|x64 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release (static)|x64.Build.0 = Release (static)|x64 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release|Win32.ActiveCfg = Release|Win32 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release|Win32.Build.0 = Release|Win32 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release|x64.ActiveCfg = Release|x64 - {5FA27C8E-51B1-445A-A375-FBE74F0984B1}.Release|x64.Build.0 = Release|x64 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Debug|Win32.ActiveCfg = Debug|Win32 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Debug|Win32.Build.0 = Debug|Win32 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Debug|x64.ActiveCfg = Debug|x64 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Debug|x64.Build.0 = Debug|x64 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release (static)|x64.Build.0 = Release (static)|x64 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release|Win32.ActiveCfg = Release|Win32 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release|Win32.Build.0 = Release|Win32 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release|x64.ActiveCfg = Release|x64 - {56BD559B-1590-4FC4-B441-AB1973BAC2BD}.Release|x64.Build.0 = Release|x64 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Debug|Win32.ActiveCfg = Debug|Win32 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Debug|Win32.Build.0 = Debug|Win32 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Debug|x64.ActiveCfg = Debug|x64 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Debug|x64.Build.0 = Debug|x64 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release (static)|x64.Build.0 = Release (static)|x64 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release|Win32.ActiveCfg = Release|Win32 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release|Win32.Build.0 = Release|Win32 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release|x64.ActiveCfg = Release|x64 - {88422448-C5FB-46F3-A0B3-0811F90E537C}.Release|x64.Build.0 = Release|x64 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Debug|Win32.ActiveCfg = Debug|Win32 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Debug|Win32.Build.0 = Debug|Win32 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Debug|x64.ActiveCfg = Debug|x64 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Debug|x64.Build.0 = Debug|x64 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release (static)|x64.Build.0 = Release (static)|x64 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release|Win32.ActiveCfg = Release|Win32 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release|Win32.Build.0 = Release|Win32 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release|x64.ActiveCfg = Release|x64 - {9EE8BD4B-47D3-4AD5-A8B9-831329792A05}.Release|x64.Build.0 = Release|x64 - {4605418D-2292-470A-AB18-C2119B016F71}.Debug|Win32.ActiveCfg = Debug|Win32 - {4605418D-2292-470A-AB18-C2119B016F71}.Debug|Win32.Build.0 = Debug|Win32 - {4605418D-2292-470A-AB18-C2119B016F71}.Debug|x64.ActiveCfg = Debug|x64 - {4605418D-2292-470A-AB18-C2119B016F71}.Debug|x64.Build.0 = Debug|x64 - {4605418D-2292-470A-AB18-C2119B016F71}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {4605418D-2292-470A-AB18-C2119B016F71}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {4605418D-2292-470A-AB18-C2119B016F71}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {4605418D-2292-470A-AB18-C2119B016F71}.Release (static)|x64.Build.0 = Release (static)|x64 - {4605418D-2292-470A-AB18-C2119B016F71}.Release|Win32.ActiveCfg = Release|Win32 - {4605418D-2292-470A-AB18-C2119B016F71}.Release|Win32.Build.0 = Release|Win32 - {4605418D-2292-470A-AB18-C2119B016F71}.Release|x64.ActiveCfg = Release|x64 - {4605418D-2292-470A-AB18-C2119B016F71}.Release|x64.Build.0 = Release|x64 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Debug|Win32.ActiveCfg = Debug|Win32 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Debug|Win32.Build.0 = Debug|Win32 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Debug|x64.ActiveCfg = Debug|x64 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Debug|x64.Build.0 = Debug|x64 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release (static)|x64.Build.0 = Release (static)|x64 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release|Win32.ActiveCfg = Release|Win32 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release|Win32.Build.0 = Release|Win32 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release|x64.ActiveCfg = Release|x64 - {05E68E3B-5901-43A9-981D-CF392C0F5C6C}.Release|x64.Build.0 = Release|x64 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Debug|Win32.ActiveCfg = Debug|Win32 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Debug|Win32.Build.0 = Debug|Win32 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Debug|x64.ActiveCfg = Debug|x64 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Debug|x64.Build.0 = Debug|x64 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release (static)|x64.Build.0 = Release (static)|x64 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release|Win32.ActiveCfg = Release|Win32 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release|Win32.Build.0 = Release|Win32 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release|x64.ActiveCfg = Release|x64 - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80}.Release|x64.Build.0 = Release|x64 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Debug|Win32.ActiveCfg = Debug|Win32 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Debug|Win32.Build.0 = Debug|Win32 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Debug|x64.ActiveCfg = Debug|x64 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Debug|x64.Build.0 = Debug|x64 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release (static)|x64.Build.0 = Release (static)|x64 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release|Win32.ActiveCfg = Release|Win32 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release|Win32.Build.0 = Release|Win32 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release|x64.ActiveCfg = Release|x64 - {1CD1A18B-95D5-4EA4-917C-34B10066BFCC}.Release|x64.Build.0 = Release|x64 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Debug|Win32.ActiveCfg = Debug|Win32 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Debug|Win32.Build.0 = Debug|Win32 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Debug|x64.ActiveCfg = Debug|x64 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Debug|x64.Build.0 = Debug|x64 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release (static)|x64.Build.0 = Release (static)|x64 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release|Win32.ActiveCfg = Release|Win32 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release|Win32.Build.0 = Release|Win32 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release|x64.ActiveCfg = Release|x64 - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5}.Release|x64.Build.0 = Release|x64 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Debug|Win32.ActiveCfg = Debug|Win32 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Debug|Win32.Build.0 = Debug|Win32 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Debug|x64.ActiveCfg = Debug|x64 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Debug|x64.Build.0 = Debug|x64 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release (static)|x64.Build.0 = Release (static)|x64 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release|Win32.ActiveCfg = Release|Win32 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release|Win32.Build.0 = Release|Win32 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release|x64.ActiveCfg = Release|x64 - {231C032C-DE16-459A-8E7D-6509C2EC3998}.Release|x64.Build.0 = Release|x64 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Debug|Win32.ActiveCfg = Debug|Win32 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Debug|Win32.Build.0 = Debug|Win32 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Debug|x64.ActiveCfg = Debug|x64 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Debug|x64.Build.0 = Debug|x64 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release (static)|x64.Build.0 = Release (static)|x64 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release|Win32.ActiveCfg = Release|Win32 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release|Win32.Build.0 = Release|Win32 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release|x64.ActiveCfg = Release|x64 - {419FEFBE-D9D5-4149-974B-0FFFC8D0D93F}.Release|x64.Build.0 = Release|x64 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Debug|Win32.ActiveCfg = Debug|Win32 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Debug|Win32.Build.0 = Debug|Win32 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Debug|x64.ActiveCfg = Debug|x64 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Debug|x64.Build.0 = Debug|x64 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release (static)|x64.Build.0 = Release (static)|x64 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release|Win32.ActiveCfg = Release|Win32 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release|Win32.Build.0 = Release|Win32 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release|x64.ActiveCfg = Release|x64 - {40639893-4D75-48CD-811F-4B363CC71FFA}.Release|x64.Build.0 = Release|x64 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Debug|Win32.ActiveCfg = Debug|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Debug|Win32.Build.0 = Debug|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Debug|x64.ActiveCfg = Debug|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release (static)|x64.Build.0 = Release (static)|x64 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release|Win32.ActiveCfg = Release|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release|Win32.Build.0 = Release|Win32 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release|x64.ActiveCfg = Release|x64 - {5FD733BF-B3C6-4A96-BED7-35E2484448E1}.Release|x64.Build.0 = Release|x64 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Debug|Win32.ActiveCfg = Debug|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Debug|Win32.Build.0 = Debug|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Debug|x64.ActiveCfg = Debug|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release (static)|x64.Build.0 = Release (static)|x64 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release|Win32.ActiveCfg = Release|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release|Win32.Build.0 = Release|Win32 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release|x64.ActiveCfg = Release|x64 - {649C4168-1C65-4E41-818F-85A1C52C1B8F}.Release|x64.Build.0 = Release|x64 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Debug|Win32.ActiveCfg = Debug|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Debug|Win32.Build.0 = Debug|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Debug|x64.ActiveCfg = Debug|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release (static)|x64.Build.0 = Release (static)|x64 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release|Win32.ActiveCfg = Release|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release|Win32.Build.0 = Release|Win32 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release|x64.ActiveCfg = Release|x64 - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F}.Release|x64.Build.0 = Release|x64 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Debug|Win32.ActiveCfg = Debug|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Debug|Win32.Build.0 = Debug|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Debug|x64.ActiveCfg = Debug|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release (static)|x64.Build.0 = Release (static)|x64 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release|Win32.ActiveCfg = Release|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release|Win32.Build.0 = Release|Win32 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release|x64.ActiveCfg = Release|x64 - {44A18410-3AA8-4A64-935B-D20223AD6885}.Release|x64.Build.0 = Release|x64 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Debug|Win32.ActiveCfg = Debug|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Debug|Win32.Build.0 = Debug|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Debug|x64.ActiveCfg = Debug|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release (static)|x64.Build.0 = Release (static)|x64 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release|Win32.ActiveCfg = Release|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release|Win32.Build.0 = Release|Win32 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release|x64.ActiveCfg = Release|x64 - {B44CB3E0-F2FD-4260-A632-C01FB881613E}.Release|x64.Build.0 = Release|x64 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Debug|Win32.ActiveCfg = Debug|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Debug|Win32.Build.0 = Debug|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Debug|x64.ActiveCfg = Debug|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release (static)|x64.Build.0 = Release (static)|x64 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release|Win32.ActiveCfg = Release|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release|Win32.Build.0 = Release|Win32 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release|x64.ActiveCfg = Release|x64 - {F4A9881E-0EB0-44A1-9664-B6CBDE992861}.Release|x64.Build.0 = Release|x64 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Debug|Win32.ActiveCfg = Debug|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Debug|Win32.Build.0 = Debug|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Debug|x64.ActiveCfg = Debug|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release (static)|x64.Build.0 = Release (static)|x64 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release|Win32.ActiveCfg = Release|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release|Win32.Build.0 = Release|Win32 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release|x64.ActiveCfg = Release|x64 - {66D3A191-E4D5-45F3-86BD-EFBB249CD554}.Release|x64.Build.0 = Release|x64 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Debug|Win32.ActiveCfg = Debug|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Debug|Win32.Build.0 = Debug|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Debug|x64.ActiveCfg = Debug|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release (static)|x64.Build.0 = Release (static)|x64 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release|Win32.ActiveCfg = Release|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release|Win32.Build.0 = Release|Win32 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release|x64.ActiveCfg = Release|x64 - {CBD0DD82-4DC7-4398-AF32-FD7BFFA3C2D5}.Release|x64.Build.0 = Release|x64 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Debug|Win32.ActiveCfg = Debug|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Debug|Win32.Build.0 = Debug|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Debug|x64.ActiveCfg = Debug|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release (static)|x64.Build.0 = Release (static)|x64 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release|Win32.ActiveCfg = Release|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release|Win32.Build.0 = Release|Win32 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release|x64.ActiveCfg = Release|x64 - {D5B9558F-EF25-4167-ACE4-723C7413B390}.Release|x64.Build.0 = Release|x64 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Debug|Win32.ActiveCfg = Debug|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Debug|Win32.Build.0 = Debug|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Debug|x64.ActiveCfg = Debug|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release (static)|x64.Build.0 = Release (static)|x64 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release|Win32.ActiveCfg = Release|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release|Win32.Build.0 = Release|Win32 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release|x64.ActiveCfg = Release|x64 - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70}.Release|x64.Build.0 = Release|x64 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Debug|Win32.ActiveCfg = Debug|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Debug|Win32.Build.0 = Debug|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Debug|x64.ActiveCfg = Debug|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release (static)|x64.Build.0 = Release (static)|x64 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release|Win32.ActiveCfg = Release|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release|Win32.Build.0 = Release|Win32 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release|x64.ActiveCfg = Release|x64 - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137}.Release|x64.Build.0 = Release|x64 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Debug|Win32.ActiveCfg = Debug|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Debug|Win32.Build.0 = Debug|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Debug|x64.ActiveCfg = Debug|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release (static)|x64.Build.0 = Release (static)|x64 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release|Win32.ActiveCfg = Release|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release|Win32.Build.0 = Release|Win32 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release|x64.ActiveCfg = Release|x64 - {7E30C3B1-AEE7-483D-B231-C672365CD2D7}.Release|x64.Build.0 = Release|x64 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Debug|Win32.ActiveCfg = Debug|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Debug|Win32.Build.0 = Debug|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Debug|x64.ActiveCfg = Debug|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release (static)|x64.Build.0 = Release (static)|x64 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release|Win32.ActiveCfg = Release|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release|Win32.Build.0 = Release|Win32 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release|x64.ActiveCfg = Release|x64 - {2E26DB8B-9E37-4072-B397-8A7086E0ACD0}.Release|x64.Build.0 = Release|x64 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Debug|Win32.ActiveCfg = Debug|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Debug|Win32.Build.0 = Debug|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Debug|x64.ActiveCfg = Debug|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release (static)|x64.Build.0 = Release (static)|x64 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release|Win32.ActiveCfg = Release|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release|Win32.Build.0 = Release|Win32 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release|x64.ActiveCfg = Release|x64 - {2736D4F9-EA8B-4ADF-B2D9-B705FF288A8A}.Release|x64.Build.0 = Release|x64 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Debug|Win32.ActiveCfg = Debug|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Debug|Win32.Build.0 = Debug|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Debug|x64.ActiveCfg = Debug|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release (static)|Win32.ActiveCfg = Release (static)|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release (static)|Win32.Build.0 = Release (static)|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release (static)|x64.ActiveCfg = Release (static)|x64 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release (static)|x64.Build.0 = Release (static)|x64 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release|Win32.ActiveCfg = Release|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release|Win32.Build.0 = Release|Win32 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release|x64.ActiveCfg = Release|x64 - {2CEB1965-A89C-4422-A9AC-B30FCE7913C3}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {9A942925-61E6-4975-935A-5D62E8248E64} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {6ADA4322-693A-46BB-897B-17BB5BE9F08C} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {6540C00F-C5EF-4C8B-824D-F2B7B302F0E0} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {51028ACF-53D4-4478-8500-55E6B8A81375} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {1311DEDF-B04C-4E96-BFDC-5D9FA0B05AC7} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {ED4AFBF5-C247-4352-966D-048B8998C6A1} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {01C3B138-9D45-4ED6-A763-893C067262C2} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {ACCC6F49-7E06-4395-AAF4-3C03A68F49EB} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {2626C0E1-6F5C-47D3-B80D-93942D766DD7} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {04FA9D77-E45F-4917-B972-B353BA6A6FA8} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {BF149D97-7520-4788-9CD1-3D99C5C8150F} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {5203F034-0047-4EC0-B7E9-D037FAF5D536} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {E7B3D07D-2FE8-481B-8DAB-6255A412A42F} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {BC1F021A-1EEC-4A7A-B746-5AA6048E478C} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {DE7E8FF8-0F5D-4062-A5C0-CFA3502E7A5A} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {226D42CE-5833-444E-94FA-84C1D51892A8} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {B37FE734-01F4-4799-86B2-D084820715BE} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {6BA39648-5BD0-4B8D-A8DD-8202FBA1AE80} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {053E1EE2-75B4-4D9A-B8AE-89600BCB60A5} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {C55D5FBF-43A0-4F6C-BCF6-BAE5705C121F} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {44A18410-3AA8-4A64-935B-D20223AD6885} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {F4A9881E-0EB0-44A1-9664-B6CBDE992861} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {66D3A191-E4D5-45F3-86BD-EFBB249CD554} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {D5B9558F-EF25-4167-ACE4-723C7413B390} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {155112B9-A8A9-4E06-90F5-4AAAB32B2F70} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {178D81A7-F2FB-41D7-B300-9D1A4DE5E137} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - {7E30C3B1-AEE7-483D-B231-C672365CD2D7} = {3CDA2798-7565-47C5-B972-F9E63DBEFFFA} - EndGlobalSection -EndGlobal diff --git a/vc2008/bsp2img/bsp2img.vcproj b/vc2008/bsp2img/bsp2img.vcproj deleted file mode 100644 index b4608c799..000000000 --- a/vc2008/bsp2img/bsp2img.vcproj +++ /dev/null @@ -1,562 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/builtins/builtins.vcproj b/vc2008/builtins/builtins.vcproj deleted file mode 100644 index 43fa081df..000000000 --- a/vc2008/builtins/builtins.vcproj +++ /dev/null @@ -1,474 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/clean.ps1 b/vc2008/clean.ps1 deleted file mode 100644 index b1e75f990..000000000 --- a/vc2008/clean.ps1 +++ /dev/null @@ -1,3 +0,0 @@ -ls -recurse -include 'win32','x64','*.user' | rm -recurse -if(test-path QuakeForge.suo) { rm QuakeForge.suo -force } -if(test-path QuakeForge.ncb) { rm QuakeForge.ncb } \ No newline at end of file diff --git a/vc2008/common/common.vcproj b/vc2008/common/common.vcproj deleted file mode 100644 index 7c1404ac7..000000000 --- a/vc2008/common/common.vcproj +++ /dev/null @@ -1,490 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/console/console.vcproj b/vc2008/console/console.vcproj deleted file mode 100644 index f93f3975f..000000000 --- a/vc2008/console/console.vcproj +++ /dev/null @@ -1,494 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/console_client/console_client.vcproj b/vc2008/console_client/console_client.vcproj deleted file mode 100644 index 0e2a378b0..000000000 --- a/vc2008/console_client/console_client.vcproj +++ /dev/null @@ -1,482 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/console_server/console_server.vcproj b/vc2008/console_server/console_server.vcproj deleted file mode 100644 index 33a611997..000000000 --- a/vc2008/console_server/console_server.vcproj +++ /dev/null @@ -1,470 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/engine/engine.vcproj b/vc2008/engine/engine.vcproj deleted file mode 100644 index 203710bf7..000000000 --- a/vc2008/engine/engine.vcproj +++ /dev/null @@ -1,510 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/gib/gib.vcproj b/vc2008/gib/gib.vcproj deleted file mode 100644 index 3fbb039b6..000000000 --- a/vc2008/gib/gib.vcproj +++ /dev/null @@ -1,538 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/hw-master/hw-master.vcproj b/vc2008/hw-master/hw-master.vcproj deleted file mode 100644 index 0e7273e3b..000000000 --- a/vc2008/hw-master/hw-master.vcproj +++ /dev/null @@ -1,562 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/image/image.vcproj b/vc2008/image/image.vcproj deleted file mode 100644 index 73970fb11..000000000 --- a/vc2008/image/image.vcproj +++ /dev/null @@ -1,482 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/include/config.h b/vc2008/include/config.h deleted file mode 100644 index 1b787db16..000000000 --- a/vc2008/include/config.h +++ /dev/null @@ -1,740 +0,0 @@ -/* include/config.h. Generated from config.h.in by configure. */ -/* include/config.h.in. Generated from configure.ac by autoheader. */ - -/* list of cd plugins */ -#define CD_PLUGIN_LIST {"cd_win", cd_win_PluginInfo},{0, 0} - -/* list of cd prototypes */ -#define CD_PLUGIN_PROTOS extern plugin_t *cd_win_PluginInfo (void); - -/* list of client plugins */ -#define CLIENT_PLUGIN_LIST {"console_client", console_client_PluginInfo},{0, 0} - -/* list of client prototypes */ -#define CLIENT_PLUGIN_PROTOS extern plugin_t *console_client_PluginInfo (void); - -/* Define this to the command line for the C preprocessor */ -#define CPP_NAME "wave --c99 %d -o %o %i" - -/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP - systems. This function is required for `alloca.c' support on those systems. - */ -/* #undef CRAY_STACKSEG_END */ - -/* Define to 1 if using `alloca.c'. */ -/* #undef C_ALLOCA */ - -/* Define this to the location of the global config file */ -#define FS_GLOBALCFG "~/quakeforge.conf" - -/* Define this to the path from which to load plugins */ -#define FS_PLUGINPATH "/usr/local/lib/quakeforge" - -/* Define this to the shared game directory root */ -#define FS_SHAREPATH "." - -/* Define this to the location of the user config file */ -#define FS_USERCFG "~/quakeforgerc" - -/* Define this to the unshared game directory root */ -#define FS_USERPATH "." - -/* Define this to the default GL dynamic lib */ -#define GL_DRIVER "OPENGL32.DLL" - -/* Define to 1 if you have the `access' function. */ -#define HAVE_ACCESS 1 - -/* Define to 1 if you have `alloca', as a function or macro. */ -#define HAVE_ALLOCA 1 - -/* Define to 1 if you have and it should be used (not on Ultrix). - */ -/* #undef HAVE_ALLOCA_H */ - -/* Define this if alloca is prototyped */ -/* #undef HAVE_ALLOCA_PROTO */ -#ifndef HAVE_ALLOCA_PROTO -#ifndef QFASM -void *alloca (int size); -#endif -#endif - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_ALSA_ASOUNDLIB_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_ARPA_INET_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_ASM_IO_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_ASSERT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_CONIO_H 1 - -/* Define to 1 if you have the `connect' function. */ -#define HAVE_CONNECT 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_CTYPE_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_CURSES_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_DDRAW_H 1 - -/* Define if you have the XFree86 DGA extension */ -/* #undef HAVE_DGA */ - -/* Define to 1 if you have the header file. */ -#define HAVE_DINPUT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_DIRECT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_DIRENT_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_DLFCN_H */ - -/* Define if you have the dlopen function. */ -/* #undef HAVE_DLOPEN */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_DMEDIA_AUDIO_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_DMEDIA_CDAUDIO_H */ - -/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ -/* #undef HAVE_DOPRNT */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_DPMI_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_DSOUND_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_ERRNO_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_EXECINFO_H */ - -/* Define this if you have FB_AUX_VGA_PLANES_CFB4 */ -/* #undef HAVE_FB_AUX_VGA_PLANES_CFB4 */ - -/* Define this if you have FB_AUX_VGA_PLANES_CFB4 */ -/* #undef HAVE_FB_AUX_VGA_PLANES_CFB8 */ - -/* Define this if you have FB_AUX_VGA_PLANES_VGA4 */ -/* #undef HAVE_FB_AUX_VGA_PLANES_VGA4 */ - -/* Define to 1 if you have the `fcntl' function. */ -/* #undef HAVE_FCNTL */ - -/* Define to 1 if you have the header file. */ -#define HAVE_FCNTL_H 1 - -/* define this if you have flac libs */ -/* #undef HAVE_FLAC */ - -/* Define to 1 if you have the header file. */ -#define HAVE_FNMATCH_H 1 - -/* Define this if fnmatch is prototyped in fnmatch.h */ -/* #undef HAVE_FNMATCH_PROTO */ - -/* Define this if FPOS_T is a struct */ -/* #undef HAVE_FPOS_T_STRUCT */ - -/* Define to 1 if you have the `ftime' function. */ -#define HAVE_FTIME 1 - -/* Define to 1 if you have the `getaddrinfo' function. */ -#define HAVE_GETADDRINFO 1 - -/* Define to 1 if you have the `gethostbyname' function. */ -#define HAVE_GETHOSTBYNAME 1 - -/* Define to 1 if you have the `gethostname' function. */ -#define HAVE_GETHOSTNAME 1 - -/* Define to 1 if you have the `getnameinfo' function. */ -#define HAVE_GETNAMEINFO 1 - -/* Define to 1 if you have the `getpagesize' function. */ -/* #undef HAVE_GETPAGESIZE */ - -/* Define to 1 if you have the `gettimeofday' function. */ -/* #undef HAVE_GETTIMEOFDAY */ - -/* Define to 1 if you have the `getwd' function. */ -/* #undef HAVE_GETWD */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_INTTYPES_H */ - -/* Define this if your system has struct in_pktinfo */ -/* #undef HAVE_IN_PKTINFO */ - -/* Define to 1 if you have the header file. */ -#define HAVE_IO_H 1 - -/* Define this if you want IPv6 support */ -/* #undef HAVE_IPV6 */ - -/* Define if you have libjack */ -/* #undef HAVE_JACK */ - -/* Define to 1 if you have a functional curl library. */ -#define HAVE_LIBCURL 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_LIBC_H */ - -/* Define to 1 if you have the `db' library (-ldb). */ -/* #undef HAVE_LIBDB */ - -/* Define to 1 if you have the `efence' library (-lefence). */ -/* #undef HAVE_LIBEFENCE */ - -/* Define to 1 if you have the `m' library (-lm). */ -/* #undef HAVE_LIBM */ - -/* Define to 1 if you have the header file. */ -#define HAVE_LIMITS_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_LINUX_CDROM_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_LINUX_FB_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_LINUX_JOYSTICK_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_LINUX_SOUNDCARD_H */ - -/* Define to 1 if you support file names longer than 14 characters. */ -#define HAVE_LONG_FILE_NAMES 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_MACHINE_SOUNDCARD_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_MALLOC_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_MATH_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_MEMORY_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_MGRAPH_H */ - -/* Define to 1 if you have the `mkdir' function. */ -#define HAVE_MKDIR 1 - -/* Define to 1 if you have a working `mmap' system call. */ -/* #undef HAVE_MMAP */ - -/* Define to 1 if you have the `mprotect' function. */ -/* #undef HAVE_MPROTECT */ - -/* Define to 1 if you have the header file, and it defines `DIR'. */ -/* #undef HAVE_NDIR_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_NETDB_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_NETINET_IN_H */ - -/* Define if you have libpng */ -/* #undef HAVE_PNG */ - -/* Define to 1 if you have the header file. */ -#define HAVE_PROCESS_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_PTHREAD_H */ - -/* Define to 1 if you have the `putenv' function. */ -#define HAVE_PUTENV 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_PWD_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_RPC_TYPES_H */ - -/* Define this if you have sa_len member in struct sockaddr (BSD) */ -/* #undef HAVE_SA_LEN */ - -/* Define to 1 if you have the `select' function. */ -#define HAVE_SELECT 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SETJMP_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SIGNAL_H 1 - -/* Define this if you have sin6_len member in struct sockaddr_in6 (BSD) */ -/* #undef HAVE_SIN6_LEN */ - -/* Define this if your system has size_t */ -#define HAVE_SIZE_T 1 - -/* Define to 1 if you have the `snprintf' function. */ -#define HAVE_SNPRINTF 1 - -/* Define to 1 if you have the `socket' function. */ -#define HAVE_SOCKET 1 - -/* Define this if your system has socklen_t */ -/* #undef HAVE_SOCKLEN_T */ - -/* Define this if you have ss_len member in struct sockaddr_storage (BSD) */ -/* #undef HAVE_SS_LEN */ - -/* Define to 1 if you have the `stat' function. */ -#define HAVE_STAT 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDARG_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDIO_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the `strcasestr' function. */ -/* #undef HAVE_STRCASESTR */ - -/* Define this if strcasestr is prototyped in string.h */ -/* #undef HAVE_STRCASESTR_PROTO */ - -/* Define to 1 if you have the `strerror' function. */ -#define HAVE_STRERROR 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_STRINGS_H 1 */ - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the `strnlen' function. */ -#define HAVE_STRNLEN 1 - -/* Define this if strnlen is prototyped in string.h */ -#define HAVE_STRNLEN_PROTO 1 - -/* Define to 1 if you have the `strsep' function. */ -/* #undef HAVE_STRSEP */ - -/* Define to 1 if you have the `strstr' function. */ -#define HAVE_STRSTR 1 - -/* Define to 1 if `st_blksize' is member of `struct stat'. */ -/* #undef HAVE_STRUCT_STAT_ST_BLKSIZE */ - -/* Define to 1 if your `struct stat' has `st_blksize'. Deprecated, use - `HAVE_STRUCT_STAT_ST_BLKSIZE' instead. */ -/* #undef HAVE_ST_BLKSIZE */ - -/* Define this if C symbols are prefixed with an underscore */ -#define HAVE_SYM_PREFIX_UNDERSCORE 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_ASOUNDLIB_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_AUDIOIO_H */ - -/* Define to 1 if you have the header file, and it defines `DIR'. - */ -/* #undef HAVE_SYS_DIR_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_FILIO_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_IOCTL_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_IO_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_IPC_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_MMAN_H */ - -/* Define to 1 if you have the header file, and it defines `DIR'. - */ -/* #undef HAVE_SYS_NDIR_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_PARAM_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_POLL_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_SHM_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_SIGNAL_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_SOCKET_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_SOUNDCARD_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_STAT_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_TIME_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TYPES_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_UIO_H */ - -/* Define to 1 if you have that is POSIX.1 compatible. */ -/* #undef HAVE_SYS_WAIT_H */ - -/* Define this if you have tchar.h */ -#define HAVE_TCHAR_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_TERMIOS_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_TIME_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_UNISTD_H */ - -/* Define to 1 if you have the `usleep' function. */ -/* #undef HAVE_USLEEP */ - -/* Define if va_copy is available */ -/* #undef HAVE_VA_COPY */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_VGAKEYBOARD_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_VGAMOUSE_H */ - -/* Define if you have the XFree86 VIDMODE extension */ -/* #undef HAVE_VIDMODE */ - -/* define this if you have ogg/vorbis libs */ -/* #undef HAVE_VORBIS */ - -/* Define to 1 if you have the `vprintf' function. */ -#define HAVE_VPRINTF 1 - -/* Define to 1 if you have the `vsnprintf' function. */ -#define HAVE_VSNPRINTF 1 - -/* Define if you have WildMidi */ -/* #undef HAVE_WILDMIDI */ - -/* Define to 1 if you have the header file. */ -#define HAVE_WINDOWS_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_WINSOCK_H 1 - -/* Define if you have XMMS */ -/* #undef HAVE_XMMS */ - -/* Define if you have zlib */ -#define HAVE_ZLIB 1 - -/* Define to 1 if you have the `_access' function. */ -#define HAVE__ACCESS 1 - -/* Define to 1 if you have the `_ftime' function. */ -#define HAVE__FTIME 1 - -/* Define to 1 if you have the <_mingw.h> header file. */ -/* #undef HAVE__MINGW_H */ - -/* Define to 1 if you have the `_mkdir' function. */ -#define HAVE__MKDIR 1 - -/* Define this if you have _SC_PAGESIZE */ -/* #undef HAVE__SC_PAGESIZE */ - -/* Define to 1 if you have the `_snprintf' function. */ -#define HAVE__SNPRINTF 1 - -/* Define if __va_copy is available */ -/* #undef HAVE__VA_COPY */ - -/* Define to 1 if you have the `_vsnprintf' function. */ -#define HAVE__VSNPRINTF 1 - -/* Define this if the GCC __attribute__ keyword is available */ -/* #undef HAVE___ATTRIBUTE__ */ - -#ifndef HAVE___ATTRIBUTE__ -# define __attribute__(x) -#endif - -/* Define this if the GCC __attribute__ keyword is available */ -/* #undef HAVE___ATTRIBUTE__VISIBILITY */ - -#ifdef HAVE___ATTRIBUTE__VISIBILITY -# define VISIBLE __attribute__((visibility ("default"))) -#else -# define VISIBLE -#endif - -/* Define this if the GCC __builtin_expect keyword is available */ -/* #undef HAVE___BUILTIN_EXPECT */ - -#ifndef HAVE___BUILTIN_EXPECT -# define __builtin_expect(x,c) x -#endif - -/* Defined if libcurl supports AsynchDNS */ -/* #undef LIBCURL_FEATURE_ASYNCHDNS */ - -/* Defined if libcurl supports IDN */ -#define LIBCURL_FEATURE_IDN 1 - -/* Defined if libcurl supports IPv6 */ -#define LIBCURL_FEATURE_IPV6 1 - -/* Defined if libcurl supports KRB4 */ -/* #undef LIBCURL_FEATURE_KRB4 */ - -/* Defined if libcurl supports libz */ -#define LIBCURL_FEATURE_LIBZ 1 - -/* Defined if libcurl supports NTLM */ -#define LIBCURL_FEATURE_NTLM 1 - -/* Defined if libcurl supports SSL */ -/* #undef LIBCURL_FEATURE_SSL */ - -/* Defined if libcurl supports SSPI */ -#define LIBCURL_FEATURE_SSPI 1 - -/* Defined if libcurl supports DICT */ -/* #undef LIBCURL_PROTOCOL_DICT */ - -/* Defined if libcurl supports FILE */ -/* #undef LIBCURL_PROTOCOL_FILE */ - -/* Defined if libcurl supports FTP */ -#define LIBCURL_PROTOCOL_FTP 1 - -/* Defined if libcurl supports FTPS */ -/* #undef LIBCURL_PROTOCOL_FTPS */ - -/* Defined if libcurl supports HTTP */ -#define LIBCURL_PROTOCOL_HTTP 1 - -/* Defined if libcurl supports HTTPS */ -/* #undef LIBCURL_PROTOCOL_HTTPS */ - -/* Defined if libcurl supports LDAP */ -/* #undef LIBCURL_PROTOCOL_LDAP */ - -/* Defined if libcurl supports TELNET */ -/* #undef LIBCURL_PROTOCOL_TELNET */ - -/* Defined if libcurl supports TFTP */ -/* #undef LIBCURL_PROTOCOL_TFTP */ - -/* Define to 1 if `major', `minor', and `makedev' are declared in . - */ -/* #undef MAJOR_IN_MKDEV */ - -/* Define to 1 if `major', `minor', and `makedev' are declared in - . */ -/* #undef MAJOR_IN_SYSMACROS */ - -/* Define this to the QSG standard version you support in NetQuake */ -#define NQ_QSG_VERSION "1.0" - -/* Define this to the NetQuake standard version you support */ -#define NQ_VERSION "1.09" - -/* Name of package */ -#define PACKAGE "quakeforge" - -/* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "" - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "" - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "" - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "" - -/* Define this to your operating system's path separator character */ -#define PATH_SEPARATOR '/' - -/* "Proper" package name */ -#define PROGRAM "QuakeForge" - -/* Define this to where qfcc should look for header files */ -#define QFCC_INCLUDE_PATH "/usr/local/include/QF/ruamoko" - -/* Define this to where qfcc should look for lib files */ -#define QFCC_LIB_PATH "/usr/local/lib/ruamoko" - -/* Define this to the QSG standard version you support in QuakeWorld */ -#define QW_QSG_VERSION "2.0" - -/* Define this to the QuakeWorld standard version you support */ -#define QW_VERSION "2.40" - -/* Define as the return type of signal handlers (`int' or `void'). */ -#define RETSIGTYPE void - -/* list of server plugins */ -#define SERVER_PLUGIN_LIST {"console_server", console_server_PluginInfo},{0, 0} - -/* list of server prototypes */ -#define SERVER_PLUGIN_PROTOS extern plugin_t *console_server_PluginInfo (void); - -/* Define this to the default sound output driver. */ -#define SND_OUTPUT_DEFAULT "win" - -/* list of sound output plugins */ -#define SND_OUTPUT_LIST {"snd_output_win", snd_output_win_PluginInfo},{0, 0} - -/* list of sound output prototypes */ -#define SND_OUTPUT_PROTOS extern plugin_t *snd_output_win_PluginInfo (void); - -/* list of sound render plugins */ -#define SND_RENDER_LIST {"snd_render_default", snd_render_default_PluginInfo},{0, 0} - -/* list of sound render prototypes */ -#define SND_RENDER_PROTOS extern plugin_t *snd_render_default_PluginInfo (void); - -/* If using the C implementation of alloca, define if you know the - direction of stack growth for your system; otherwise it will be - automatically deduced at runtime. - STACK_DIRECTION > 0 => grows toward higher addresses - STACK_DIRECTION < 0 => grows toward lower addresses - STACK_DIRECTION = 0 => direction of growth unknown */ -/* #undef STACK_DIRECTION */ - -/* Define this if you are building static plugins */ -#define STATIC_PLUGINS 1 - -/* Define to 1 if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Define to 1 if you can safely include both and . */ -#define TIME_WITH_SYS_TIME 1 - -/* Define to 1 if your declares `struct tm'. */ -/* #undef TM_IN_SYS_TIME */ - -/* Define this if you want progs typechecking */ -/* #undef TYPECHECK_PROGS */ - -/* Define this if you want to use Intel assembly optimizations */ -/* #undef USE_INTEL_ASM */ - -/* Define if va_list is an array */ -/* #undef VA_LIST_IS_ARRAY */ - -/* Version number of package */ -#define VERSION "0.5.5-SVN" - -/* Define to 1 if your processor stores words with the most significant byte - first (like Motorola and SPARC, unlike Intel and VAX). */ -/* #undef WORDS_BIGENDIAN */ - -/* Define to 1 if the X Window System is missing or not being used. */ -/* #undef X_DISPLAY_MISSING */ - -/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a - `char[]'. */ -/* #undef YYTEXT_POINTER */ - -/* Define to empty if `const' does not conform to ANSI C. */ -/* #undef const */ - -/* Define curl_free() as free() if our version of curl lacks curl_free. */ -/* #undef curl_free */ - -/* Define to `__inline__' or `__inline' if that's what the C compiler - calls it, or to nothing if 'inline' is not supported under any name. */ -#ifndef __cplusplus -#define inline __inline -/* #undef inline */ -#endif - -/* Define to `unsigned int' if does not define. */ -/* #undef size_t */ - -/* Define strcasecmp as stricmp if you have one but not the other */ -#define strcasecmp stricmp - -/* new stuff, for VC2005 compatibility (phrosty) */ - -#define _CRT_SECURE_NO_WARNINGS -#define _CRT_NONSTDC_NO_WARNINGS - -/* used in qfcc. should #define to _ReturnAddress()? */ -#define __builtin_return_address(x) (0) - -#define _WIN32_WINNT 0x0500 -#define DIRECTINPUT_VERSION 0x0600 - -#define INITGUID - -#define snprintf _snprintf -#define strncasecmp strnicmp - -#define ssize_t ptrdiff_t -#define S_ISDIR(mode) (mode & _S_IFDIR) - -/* used in access() */ -#define R_OK 04 - -/* - disable silent conversion warnings for fixing later.. - - 4047: 'operator' : 'identifier1' differs in levels of indirection from 'identifier2' - 4244: 'argument' : conversion from 'type1' to 'type2', possible loss of data - 4267: 'var' : conversion from 'size_t' to 'type', possible loss of data (/Wp64 warning) - 4305: 'identifier' : truncation from 'type1' to 'type2' - 4311: 'variable' : pointer truncation from 'type' to 'type' (/Wp64 warning) - 4312: 'operation' : conversion from 'type1' to 'type2' of greater size (/Wp64 warning) -*/ -#pragma warning(disable:4047 4244 4267 4305 4311 4312) diff --git a/vc2008/models-sw/models-sw.vcproj b/vc2008/models-sw/models-sw.vcproj deleted file mode 100644 index e8aa3d541..000000000 --- a/vc2008/models-sw/models-sw.vcproj +++ /dev/null @@ -1,512 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/models/models.vcproj b/vc2008/models/models.vcproj deleted file mode 100644 index cacad0007..000000000 --- a/vc2008/models/models.vcproj +++ /dev/null @@ -1,490 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/modelsgl/modelsgl.vcproj b/vc2008/modelsgl/modelsgl.vcproj deleted file mode 100644 index 128903bd5..000000000 --- a/vc2008/modelsgl/modelsgl.vcproj +++ /dev/null @@ -1,522 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/net-main/net-main.vcproj b/vc2008/net-main/net-main.vcproj deleted file mode 100644 index 8f33ca46c..000000000 --- a/vc2008/net-main/net-main.vcproj +++ /dev/null @@ -1,488 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/net/net.vcproj b/vc2008/net/net.vcproj deleted file mode 100644 index b79e2718b..000000000 --- a/vc2008/net/net.vcproj +++ /dev/null @@ -1,474 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/nq-common/nq-common.vcproj b/vc2008/nq-common/nq-common.vcproj deleted file mode 100644 index f5e82ed15..000000000 --- a/vc2008/nq-common/nq-common.vcproj +++ /dev/null @@ -1,516 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/nq-sdl/nq-sdl.vcproj b/vc2008/nq-sdl/nq-sdl.vcproj deleted file mode 100644 index 73f38ada0..000000000 --- a/vc2008/nq-sdl/nq-sdl.vcproj +++ /dev/null @@ -1,556 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/nq-sdl32/nq-sdl32.vcproj b/vc2008/nq-sdl32/nq-sdl32.vcproj deleted file mode 100644 index 1e9ffcae2..000000000 --- a/vc2008/nq-sdl32/nq-sdl32.vcproj +++ /dev/null @@ -1,562 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/nq-server/nq-server.vcproj b/vc2008/nq-server/nq-server.vcproj deleted file mode 100644 index 202f9b6f2..000000000 --- a/vc2008/nq-server/nq-server.vcproj +++ /dev/null @@ -1,566 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/nq-sgl/nq-sgl.vcproj b/vc2008/nq-sgl/nq-sgl.vcproj deleted file mode 100644 index f6e9bc3b1..000000000 --- a/vc2008/nq-sgl/nq-sgl.vcproj +++ /dev/null @@ -1,562 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/nq-wgl/nq-wgl.vcproj b/vc2008/nq-wgl/nq-wgl.vcproj deleted file mode 100644 index 6453a12bd..000000000 --- a/vc2008/nq-wgl/nq-wgl.vcproj +++ /dev/null @@ -1,562 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/nq/nq.vcproj b/vc2008/nq/nq.vcproj deleted file mode 100644 index 220f0ff7a..000000000 --- a/vc2008/nq/nq.vcproj +++ /dev/null @@ -1,544 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/pak/pak.vcproj b/vc2008/pak/pak.vcproj deleted file mode 100644 index b5cd0bec8..000000000 --- a/vc2008/pak/pak.vcproj +++ /dev/null @@ -1,566 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/qfbsp/qfbsp.vcproj b/vc2008/qfbsp/qfbsp.vcproj deleted file mode 100644 index 97462b79e..000000000 --- a/vc2008/qfbsp/qfbsp.vcproj +++ /dev/null @@ -1,632 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/qfcc/FlexBison.rules b/vc2008/qfcc/FlexBison.rules deleted file mode 100644 index 0e4c1e3dc..000000000 --- a/vc2008/qfcc/FlexBison.rules +++ /dev/null @@ -1,246 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/qfcc/qfcc.vcproj b/vc2008/qfcc/qfcc.vcproj deleted file mode 100644 index 59bc03f84..000000000 --- a/vc2008/qfcc/qfcc.vcproj +++ /dev/null @@ -1,965 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/qfclient/qfclient.vcproj b/vc2008/qfclient/qfclient.vcproj deleted file mode 100644 index 8739a5098..000000000 --- a/vc2008/qfclient/qfclient.vcproj +++ /dev/null @@ -1,626 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/qflight/qflight.vcproj b/vc2008/qflight/qflight.vcproj deleted file mode 100644 index 3fc0ab1d6..000000000 --- a/vc2008/qflight/qflight.vcproj +++ /dev/null @@ -1,620 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/qfmodelgen/qfmodelgen.vcproj b/vc2008/qfmodelgen/qfmodelgen.vcproj deleted file mode 100644 index 893da110a..000000000 --- a/vc2008/qfmodelgen/qfmodelgen.vcproj +++ /dev/null @@ -1,578 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/qfprogs/qfprogs.vcproj b/vc2008/qfprogs/qfprogs.vcproj deleted file mode 100644 index 5141f8959..000000000 --- a/vc2008/qfprogs/qfprogs.vcproj +++ /dev/null @@ -1,590 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/qfserver/qfserver.vcproj b/vc2008/qfserver/qfserver.vcproj deleted file mode 100644 index 909324104..000000000 --- a/vc2008/qfserver/qfserver.vcproj +++ /dev/null @@ -1,672 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/qfvis/qfvis.vcproj b/vc2008/qfvis/qfvis.vcproj deleted file mode 100644 index 479049560..000000000 --- a/vc2008/qfvis/qfvis.vcproj +++ /dev/null @@ -1,588 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/qfwavinfo/qfwavinfo.vcproj b/vc2008/qfwavinfo/qfwavinfo.vcproj deleted file mode 100644 index 40df9c014..000000000 --- a/vc2008/qfwavinfo/qfwavinfo.vcproj +++ /dev/null @@ -1,562 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/qtv/qtv.vcproj b/vc2008/qtv/qtv.vcproj deleted file mode 100644 index 0e2bb2d4b..000000000 --- a/vc2008/qtv/qtv.vcproj +++ /dev/null @@ -1,594 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/qw-client-sdl/qw-client-sdl.vcproj b/vc2008/qw-client-sdl/qw-client-sdl.vcproj deleted file mode 100644 index 983be0451..000000000 --- a/vc2008/qw-client-sdl/qw-client-sdl.vcproj +++ /dev/null @@ -1,558 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/qw-client-sdl32/qw-client-sdl32.vcproj b/vc2008/qw-client-sdl32/qw-client-sdl32.vcproj deleted file mode 100644 index 33a31293d..000000000 --- a/vc2008/qw-client-sdl32/qw-client-sdl32.vcproj +++ /dev/null @@ -1,562 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/qw-client-sgl/qw-client-sgl.vcproj b/vc2008/qw-client-sgl/qw-client-sgl.vcproj deleted file mode 100644 index 2842872d1..000000000 --- a/vc2008/qw-client-sgl/qw-client-sgl.vcproj +++ /dev/null @@ -1,562 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/qw-client-wgl/qw-client-wgl.vcproj b/vc2008/qw-client-wgl/qw-client-wgl.vcproj deleted file mode 100644 index 0a2c37256..000000000 --- a/vc2008/qw-client-wgl/qw-client-wgl.vcproj +++ /dev/null @@ -1,558 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/qw-master/qw-master.vcproj b/vc2008/qw-master/qw-master.vcproj deleted file mode 100644 index 4d7961feb..000000000 --- a/vc2008/qw-master/qw-master.vcproj +++ /dev/null @@ -1,562 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/qw/qw.vcproj b/vc2008/qw/qw.vcproj deleted file mode 100644 index e5cb0be82..000000000 --- a/vc2008/qw/qw.vcproj +++ /dev/null @@ -1,474 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/readme.txt b/vc2008/readme.txt deleted file mode 100644 index b4ad78741..000000000 --- a/vc2008/readme.txt +++ /dev/null @@ -1,25 +0,0 @@ -Requirements -============= -- Visual C++ 2008 SP1 -- DirectX SDK (for DirectInput) -- SDL (required for sdl, sdl32, and sgl builds) -- bison/flex (required for qfcc, needs to be in your compiler path) - -Optional -========= -- zlib (#undef HAVE_ZLIB from vc2008/include/config.h if you don't want - this). Expects zlib.lib and zlib.dll for Debug/Release builds, and - libzlib.lib for Release (static) build. -- libcurl (#undef HAVE_LIBCURL from vc2008/include/config.h if you don't - want this). Expects curl.lib and curl.dll for Debug/Release builds, - and libcurl.lib for Release (static) build. - -Notes -====== -By default, qfcc is configured to use the Boost Wave preprocessor. You -can get this from http://www.boost.org, or change the CPP_NAME #define in -vc2008/include/config.h to whatever preprocessor you want. If you have -GCC, you can simply replace "wave --c99" with "gcc" in the define and it -should work. - -clean.ps1 is a Windows Powershell script that cleans up any build files. diff --git a/vc2008/ruamoko/ruamoko.vcproj b/vc2008/ruamoko/ruamoko.vcproj deleted file mode 100644 index 924c02f7c..000000000 --- a/vc2008/ruamoko/ruamoko.vcproj +++ /dev/null @@ -1,518 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/sound/sound.vcproj b/vc2008/sound/sound.vcproj deleted file mode 100644 index fe681171d..000000000 --- a/vc2008/sound/sound.vcproj +++ /dev/null @@ -1,514 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/util/util.vcproj b/vc2008/util/util.vcproj deleted file mode 100644 index e680f491d..000000000 --- a/vc2008/util/util.vcproj +++ /dev/null @@ -1,618 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/video-sdl/video-sdl.vcproj b/vc2008/video-sdl/video-sdl.vcproj deleted file mode 100644 index 2b03f2566..000000000 --- a/vc2008/video-sdl/video-sdl.vcproj +++ /dev/null @@ -1,472 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/video-sgl/video-sgl.vcproj b/vc2008/video-sgl/video-sgl.vcproj deleted file mode 100644 index 920a06e47..000000000 --- a/vc2008/video-sgl/video-sgl.vcproj +++ /dev/null @@ -1,468 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/video-sw/video-sw.vcproj b/vc2008/video-sw/video-sw.vcproj deleted file mode 100644 index 75f419ae0..000000000 --- a/vc2008/video-sw/video-sw.vcproj +++ /dev/null @@ -1,596 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/video-sw32/video-sw32.vcproj b/vc2008/video-sw32/video-sw32.vcproj deleted file mode 100644 index fc6bc6054..000000000 --- a/vc2008/video-sw32/video-sw32.vcproj +++ /dev/null @@ -1,572 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/video-wgl/video-wgl.vcproj b/vc2008/video-wgl/video-wgl.vcproj deleted file mode 100644 index a0b315c7c..000000000 --- a/vc2008/video-wgl/video-wgl.vcproj +++ /dev/null @@ -1,472 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/video/video.vcproj b/vc2008/video/video.vcproj deleted file mode 100644 index 737ecfe59..000000000 --- a/vc2008/video/video.vcproj +++ /dev/null @@ -1,538 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/videogl/videogl.vcproj b/vc2008/videogl/videogl.vcproj deleted file mode 100644 index 44d30c3fc..000000000 --- a/vc2008/videogl/videogl.vcproj +++ /dev/null @@ -1,556 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vc2008/wad/wad.vcproj b/vc2008/wad/wad.vcproj deleted file mode 100644 index 11fc5737d..000000000 --- a/vc2008/wad/wad.vcproj +++ /dev/null @@ -1,574 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -